Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2023-07-28 21:11:01 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2023-07-28 21:11:01 +0300
commit7c5f1bfac791045e54386b9c9bb56ee24afc68ca (patch)
treea11c8dff3994899c25acacb383c0a70522a24cd1 /app
parentd62fd6e04c272d48dccde4033529ca97c27502f6 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/clusters/forms/components/integration_form.vue3
-rw-r--r--app/assets/javascripts/clusters_list/components/clusters_actions.vue6
-rw-r--r--app/assets/javascripts/observability/client.js12
-rw-r--r--app/assets/javascripts/pages/projects/tracing/show/index.js4
-rw-r--r--app/assets/javascripts/tracing/components/tracing_details.vue90
-rw-r--r--app/assets/javascripts/tracing/components/tracing_empty_state.vue8
-rw-r--r--app/assets/javascripts/tracing/components/tracing_list.vue21
-rw-r--r--app/assets/javascripts/tracing/components/tracing_table_list.vue27
-rw-r--r--app/assets/javascripts/tracing/details_index.vue49
-rw-r--r--app/graphql/mutations/work_items/subscribe.rb41
-rw-r--r--app/graphql/types/mutation_type.rb1
-rw-r--r--app/models/application_record.rb2
-rw-r--r--app/models/todo.rb4
-rw-r--r--app/services/ci/pipeline_schedules/base_save_service.rb54
-rw-r--r--app/services/ci/pipeline_schedules/create_service.rb40
-rw-r--r--app/services/ci/pipeline_schedules/update_service.rb34
-rw-r--r--app/services/todos/destroy/group_private_service.rb5
-rw-r--r--app/views/clusters/clusters/user/_form.html.haml4
-rw-r--r--app/views/dashboard/todos/_todo.html.haml1
19 files changed, 316 insertions, 90 deletions
diff --git a/app/assets/javascripts/clusters/forms/components/integration_form.vue b/app/assets/javascripts/clusters/forms/components/integration_form.vue
index b2a8381f937..cd82465f2f0 100644
--- a/app/assets/javascripts/clusters/forms/components/integration_form.vue
+++ b/app/assets/javascripts/clusters/forms/components/integration_form.vue
@@ -74,7 +74,6 @@ export default {
v-gl-tooltip:tooltipcontainer
name="cluster[enabled]"
class="gl-mb-0 js-project-feature-toggle"
- data-qa-selector="integration_status_toggle"
aria-describedby="toggleCluster"
:disabled="!editable"
:label="$options.i18n.toggleLabel"
@@ -111,7 +110,6 @@ export default {
id="cluster_base_domain"
v-model="baseDomainField"
name="cluster[base_domain]"
- data-qa-selector="base_domain_field"
class="col-md-6"
type="text"
/>
@@ -144,7 +142,6 @@ export default {
type="submit"
:disabled="!canSubmit"
:aria-disabled="!canSubmit"
- data-qa-selector="save_changes_button"
>{{ s__('ClusterIntegration|Save changes') }}</gl-button
>
</div>
diff --git a/app/assets/javascripts/clusters_list/components/clusters_actions.vue b/app/assets/javascripts/clusters_list/components/clusters_actions.vue
index 7b97a5af373..c388d3fee71 100644
--- a/app/assets/javascripts/clusters_list/components/clusters_actions.vue
+++ b/app/assets/javascripts/clusters_list/components/clusters_actions.vue
@@ -92,11 +92,7 @@ export default {
<!--TODO: Replace button-group workaround once `split` option for new dropdowns is implemented.-->
<!-- See issue at https://gitlab.com/gitlab-org/gitlab-ui/-/issues/2263-->
- <gl-button-group
- ref="actions"
- data-qa-selector="clusters_actions_button"
- class="gl-w-full gl-mb-3 gl-md-w-auto gl-md-mb-0"
- >
+ <gl-button-group ref="actions" class="gl-w-full gl-mb-3 gl-md-w-auto gl-md-mb-0">
<gl-button
v-gl-modal-directive="shouldTriggerModal && $options.INSTALL_AGENT_MODAL_ID"
:href="defaultActionUrl"
diff --git a/app/assets/javascripts/observability/client.js b/app/assets/javascripts/observability/client.js
index 251c165e7dd..667bbc107dc 100644
--- a/app/assets/javascripts/observability/client.js
+++ b/app/assets/javascripts/observability/client.js
@@ -1,4 +1,5 @@
import axios from '~/lib/utils/axios_utils';
+import mockData from './mock_traces.json';
function enableTraces() {
// TODO remove mocks https://gitlab.com/gitlab-org/opstrace/opstrace/-/issues/2271
@@ -19,6 +20,16 @@ function isTracingEnabled() {
});
}
+async function fetchTrace(tracingUrl, traceId) {
+ const data = mockData;
+ const trace = data.traces.find((t) => t.trace_id === traceId);
+ const duration = trace.spans.reduce((acc, cur) => acc + cur.duration_nano, 0);
+ return {
+ ...trace,
+ duration: duration / 1000,
+ };
+}
+
async function fetchTraces(tracingUrl) {
const { data } = await axios.get(tracingUrl, { withCredentials: true });
if (!Array.isArray(data.traces)) {
@@ -39,5 +50,6 @@ export function buildClient({ provisioningUrl, tracingUrl }) {
enableTraces: () => enableTraces(provisioningUrl),
isTracingEnabled: () => isTracingEnabled(provisioningUrl),
fetchTraces: () => fetchTraces(tracingUrl),
+ fetchTrace: (traceId) => fetchTrace(tracingUrl, traceId),
};
}
diff --git a/app/assets/javascripts/pages/projects/tracing/show/index.js b/app/assets/javascripts/pages/projects/tracing/show/index.js
new file mode 100644
index 00000000000..107c004aa5f
--- /dev/null
+++ b/app/assets/javascripts/pages/projects/tracing/show/index.js
@@ -0,0 +1,4 @@
+import { initSimpleApp } from '~/helpers/init_simple_app_helper';
+import DetailsIndex from '~/tracing/details_index.vue';
+
+initSimpleApp('#js-tracing-details', DetailsIndex);
diff --git a/app/assets/javascripts/tracing/components/tracing_details.vue b/app/assets/javascripts/tracing/components/tracing_details.vue
new file mode 100644
index 00000000000..d8b2cbc9469
--- /dev/null
+++ b/app/assets/javascripts/tracing/components/tracing_details.vue
@@ -0,0 +1,90 @@
+<script>
+import { GlLoadingIcon } from '@gitlab/ui';
+import { s__ } from '~/locale';
+import { createAlert } from '~/alert';
+import { visitUrl, isSafeURL } from '~/lib/utils/url_utility';
+
+export default {
+ components: {
+ GlLoadingIcon,
+ },
+ i18n: {
+ error: s__('Tracing|Failed to load trace details.'),
+ },
+ props: {
+ observabilityClient: {
+ required: true,
+ type: Object,
+ },
+ traceId: {
+ required: true,
+ type: String,
+ },
+ tracingIndexUrl: {
+ required: true,
+ type: String,
+ validator: (val) => isSafeURL(val),
+ },
+ },
+ data() {
+ return {
+ trace: null,
+ loading: false,
+ };
+ },
+ created() {
+ this.validateAndFetch();
+ },
+ methods: {
+ async validateAndFetch() {
+ if (!this.traceId) {
+ createAlert({
+ message: this.$options.i18n.error,
+ });
+ }
+ this.loading = true;
+ try {
+ const enabled = await this.observabilityClient.isTracingEnabled();
+ if (enabled) {
+ await this.fetchTrace();
+ } else {
+ this.goToTracingIndex();
+ }
+ } catch (e) {
+ createAlert({
+ message: this.$options.i18n.error,
+ });
+ } finally {
+ this.loading = false;
+ }
+ },
+ async fetchTrace() {
+ this.loading = true;
+ try {
+ this.trace = await this.observabilityClient.fetchTrace(this.traceId);
+ } catch (e) {
+ createAlert({
+ message: this.$options.i18n.error,
+ });
+ } finally {
+ this.loading = false;
+ }
+ },
+ goToTracingIndex() {
+ visitUrl(this.tracingIndexUrl);
+ },
+ },
+};
+</script>
+
+<template>
+ <div v-if="loading" class="gl-py-5">
+ <gl-loading-icon size="lg" />
+ </div>
+
+ <!-- TODO Replace with actual trace-details component-->
+ <div v-else-if="trace" data-testid="trace-details">
+ <p>{{ tracingIndexUrl }}</p>
+ <p>{{ trace }}</p>
+ </div>
+</template>
diff --git a/app/assets/javascripts/tracing/components/tracing_empty_state.vue b/app/assets/javascripts/tracing/components/tracing_empty_state.vue
index 4cb3bd6d9f0..3cdb280ef9e 100644
--- a/app/assets/javascripts/tracing/components/tracing_empty_state.vue
+++ b/app/assets/javascripts/tracing/components/tracing_empty_state.vue
@@ -1,15 +1,15 @@
<script>
import EMPTY_TRACING_SVG from '@gitlab/svgs/dist/illustrations/monitoring/tracing.svg?url';
import { GlEmptyState, GlButton } from '@gitlab/ui';
-import { __ } from '~/locale';
+import { s__ } from '~/locale';
export default {
EMPTY_TRACING_SVG,
name: 'TracingEmptyState',
i18n: {
- title: __('Get started with Tracing'),
- description: __('Monitor your applications with GitLab Distributed Tracing.'),
- enableButtonText: __('Enable'),
+ title: s__('Tracing|Get started with Tracing'),
+ description: s__('Tracing|Monitor your applications with GitLab Distributed Tracing.'),
+ enableButtonText: s__('Tracing|Enable'),
},
components: {
GlEmptyState,
diff --git a/app/assets/javascripts/tracing/components/tracing_list.vue b/app/assets/javascripts/tracing/components/tracing_list.vue
index 294e520d7ac..d247e78bb0b 100644
--- a/app/assets/javascripts/tracing/components/tracing_list.vue
+++ b/app/assets/javascripts/tracing/components/tracing_list.vue
@@ -1,7 +1,8 @@
<script>
import { GlLoadingIcon } from '@gitlab/ui';
-import { __ } from '~/locale';
+import { s__ } from '~/locale';
import { createAlert } from '~/alert';
+import { visitUrl, joinPaths } from '~/lib/utils/url_utility';
import TracingEmptyState from './tracing_empty_state.vue';
import TracingTableList from './tracing_table_list.vue';
@@ -28,7 +29,7 @@ export default {
traces: [],
};
},
- async created() {
+ created() {
this.checkEnabled();
},
methods: {
@@ -41,7 +42,7 @@ export default {
}
} catch (e) {
createAlert({
- message: __('Failed to load page.'),
+ message: s__('Tracing|Failed to load page.'),
});
} finally {
this.loading = false;
@@ -55,7 +56,7 @@ export default {
await this.fetchTraces();
} catch (e) {
createAlert({
- message: __('Failed to enable tracing.'),
+ message: s__('Tracing|Failed to enable tracing.'),
});
} finally {
this.loading = false;
@@ -68,12 +69,15 @@ export default {
this.traces = traces;
} catch (e) {
createAlert({
- message: __('Failed to load traces.'),
+ message: s__('Tracing|Failed to load traces.'),
});
} finally {
this.loading = false;
}
},
+ selectTrace(trace) {
+ visitUrl(joinPaths(window.location.pathname, trace.trace_id));
+ },
},
};
</script>
@@ -87,7 +91,12 @@ export default {
<template v-else-if="tracingEnabled !== null">
<tracing-empty-state v-if="tracingEnabled === false" :enable-tracing="enableTracing" />
- <tracing-table-list v-else :traces="traces" @reload="fetchTraces" />
+ <tracing-table-list
+ v-else
+ :traces="traces"
+ @reload="fetchTraces"
+ @trace-selected="selectTrace"
+ />
</template>
</div>
</template>
diff --git a/app/assets/javascripts/tracing/components/tracing_table_list.vue b/app/assets/javascripts/tracing/components/tracing_table_list.vue
index bbed5520b40..59604890c86 100644
--- a/app/assets/javascripts/tracing/components/tracing_table_list.vue
+++ b/app/assets/javascripts/tracing/components/tracing_table_list.vue
@@ -1,37 +1,37 @@
<script>
import { GlTable, GlLink } from '@gitlab/ui';
-import { __ } from '~/locale';
+import { s__ } from '~/locale';
export const tableDataClass = 'gl-display-flex gl-md-display-table-cell gl-align-items-center';
export default {
name: 'TracingTableList',
i18n: {
- title: __('Traces'),
- emptyText: __('No traces to display.'),
- emptyLinkText: __('Check again'),
+ title: s__('Tracing|Traces'),
+ emptyText: s__('Tracing|No traces to display.'),
+ emptyLinkText: s__('Tracing|Check again'),
},
fields: [
{
key: 'timestamp',
- label: __('Date'),
+ label: s__('Tracing|Date'),
tdClass: tableDataClass,
sortable: true,
},
{
key: 'service_name',
- label: __('Service'),
+ label: s__('Tracing|Service'),
tdClass: tableDataClass,
sortable: true,
},
{
key: 'operation',
- label: __('Operation'),
+ label: s__('Tracing|Operation'),
tdClass: tableDataClass,
sortable: true,
},
{
key: 'duration',
- label: __('Duration'),
+ label: s__('Tracing|Duration'),
thClass: 'gl-w-15p',
tdClass: tableDataClass,
sortable: true,
@@ -47,6 +47,13 @@ export default {
type: Array,
},
},
+ methods: {
+ onSelect(items) {
+ if (items[0]) {
+ this.$emit('trace-selected', items[0]);
+ }
+ },
+ },
};
</script>
@@ -64,6 +71,10 @@ export default {
fixed
stacked="md"
tbody-tr-class="table-row"
+ selectable
+ select-mode="single"
+ selected-variant=""
+ @row-selected="onSelect"
>
<template #cell(timestamp)="data">
{{ data.item.timestamp }}
diff --git a/app/assets/javascripts/tracing/details_index.vue b/app/assets/javascripts/tracing/details_index.vue
new file mode 100644
index 00000000000..5702a88766c
--- /dev/null
+++ b/app/assets/javascripts/tracing/details_index.vue
@@ -0,0 +1,49 @@
+<script>
+import ObservabilityContainer from '~/observability/components/observability_container.vue';
+import TracingDetails from './components/tracing_details.vue';
+
+export default {
+ components: {
+ ObservabilityContainer,
+ TracingDetails,
+ },
+ props: {
+ traceId: {
+ type: String,
+ required: true,
+ },
+ oauthUrl: {
+ type: String,
+ required: true,
+ },
+ tracingUrl: {
+ type: String,
+ required: true,
+ },
+ provisioningUrl: {
+ type: String,
+ required: true,
+ },
+ tracingIndexUrl: {
+ required: true,
+ type: String,
+ },
+ },
+};
+</script>
+
+<template>
+ <observability-container
+ :oauth-url="oauthUrl"
+ :tracing-url="tracingUrl"
+ :provisioning-url="provisioningUrl"
+ >
+ <template #default="{ observabilityClient }">
+ <tracing-details
+ :trace-id="traceId"
+ :tracing-index-url="tracingIndexUrl"
+ :observability-client="observabilityClient"
+ />
+ </template>
+ </observability-container>
+</template>
diff --git a/app/graphql/mutations/work_items/subscribe.rb b/app/graphql/mutations/work_items/subscribe.rb
new file mode 100644
index 00000000000..a29c3416c3d
--- /dev/null
+++ b/app/graphql/mutations/work_items/subscribe.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+
+module Mutations
+ module WorkItems
+ class Subscribe < BaseMutation
+ graphql_name 'WorkItemSubscribe'
+
+ argument :id, ::Types::GlobalIDType[::WorkItem],
+ required: true,
+ description: 'Global ID of the work item.'
+
+ argument :subscribed,
+ GraphQL::Types::Boolean,
+ required: true,
+ description: 'Desired state of the subscription.'
+
+ field :work_item, Types::WorkItemType,
+ null: true,
+ description: 'Work item after mutation.'
+
+ authorize :update_subscription
+
+ def resolve(args)
+ work_item = authorized_find!(id: args[:id])
+
+ update_subscription(work_item, args[:subscribed])
+
+ {
+ work_item: work_item,
+ errors: []
+ }
+ end
+
+ private
+
+ def update_subscription(work_item, subscribed_state)
+ work_item.set_subscription(current_user, subscribed_state, work_item.project)
+ end
+ end
+ end
+end
diff --git a/app/graphql/types/mutation_type.rb b/app/graphql/types/mutation_type.rb
index 16c46d172f3..9a6b3861103 100644
--- a/app/graphql/types/mutation_type.rb
+++ b/app/graphql/types/mutation_type.rb
@@ -183,6 +183,7 @@ module Types
mount_mutation Mutations::SavedReplies::Destroy
mount_mutation Mutations::Uploads::Delete
mount_mutation Mutations::Users::SetNamespaceCommitEmail
+ mount_mutation Mutations::WorkItems::Subscribe, alpha: { milestone: '16.3' }
end
end
diff --git a/app/models/application_record.rb b/app/models/application_record.rb
index 291375f647c..8e5f3f030fb 100644
--- a/app/models/application_record.rb
+++ b/app/models/application_record.rb
@@ -95,7 +95,7 @@ class ApplicationRecord < ActiveRecord::Base
end
def self.underscore
- Gitlab::SafeRequestStore.fetch("model:#{self}:underscore") { to_s.underscore }
+ @underscore ||= to_s.underscore
end
def self.where_exists(query)
diff --git a/app/models/todo.rb b/app/models/todo.rb
index 2f2e731fc7e..d159b51a0eb 100644
--- a/app/models/todo.rb
+++ b/app/models/todo.rb
@@ -225,6 +225,10 @@ class Todo < ApplicationRecord
action == MEMBER_ACCESS_REQUESTED
end
+ def review_submitted?
+ action == REVIEW_SUBMITTED
+ end
+
def member_access_type
target.class.name.downcase
end
diff --git a/app/services/ci/pipeline_schedules/base_save_service.rb b/app/services/ci/pipeline_schedules/base_save_service.rb
new file mode 100644
index 00000000000..45d70e5a65d
--- /dev/null
+++ b/app/services/ci/pipeline_schedules/base_save_service.rb
@@ -0,0 +1,54 @@
+# frozen_string_literal: true
+
+module Ci
+ module PipelineSchedules
+ class BaseSaveService
+ include Gitlab::Utils::StrongMemoize
+
+ def execute
+ schedule.assign_attributes(params)
+
+ return forbidden_to_save unless allowed_to_save?
+ return forbidden_to_save_variables unless allowed_to_save_variables?
+
+ if schedule.save
+ ServiceResponse.success(payload: schedule)
+ else
+ ServiceResponse.error(payload: schedule, message: schedule.errors.full_messages)
+ end
+ end
+
+ private
+
+ attr_reader :project, :user, :params, :schedule
+
+ def allowed_to_save?
+ user.can?(self.class::AUTHORIZE, schedule)
+ end
+
+ def forbidden_to_save
+ # We add the error to the base object too
+ # because model errors are used in the API responses and the `form_errors` helper.
+ schedule.errors.add(:base, authorize_message)
+
+ ServiceResponse.error(payload: schedule, message: [authorize_message], reason: :forbidden)
+ end
+
+ def allowed_to_save_variables?
+ return true if params[:variables_attributes].blank?
+
+ user.can?(:set_pipeline_variables, project)
+ end
+
+ def forbidden_to_save_variables
+ message = _('The current user is not authorized to set pipeline schedule variables')
+
+ # We add the error to the base object too
+ # because model errors are used in the API responses and the `form_errors` helper.
+ schedule.errors.add(:base, message)
+
+ ServiceResponse.error(payload: schedule, message: [message], reason: :forbidden)
+ end
+ end
+ end
+end
diff --git a/app/services/ci/pipeline_schedules/create_service.rb b/app/services/ci/pipeline_schedules/create_service.rb
index c1825865bc0..23775e68399 100644
--- a/app/services/ci/pipeline_schedules/create_service.rb
+++ b/app/services/ci/pipeline_schedules/create_service.rb
@@ -2,46 +2,22 @@
module Ci
module PipelineSchedules
- class CreateService
- def initialize(project, user, params)
- @project = project
- @user = user
- @params = params
+ class CreateService < BaseSaveService
+ AUTHORIZE = :create_pipeline_schedule
+ def initialize(project, user, params)
@schedule = project.pipeline_schedules.new
- end
-
- def execute
- return forbidden unless allowed?
-
- schedule.assign_attributes(params.merge(owner: user))
-
- if schedule.save
- ServiceResponse.success(payload: schedule)
- else
- ServiceResponse.error(payload: schedule, message: schedule.errors.full_messages)
- end
+ @user = user
+ @project = project
+ @params = params.merge(owner: user)
end
private
- attr_reader :project, :user, :params, :schedule
-
- def allowed?
- user.can?(:create_pipeline_schedule, schedule)
- end
-
- def forbidden
- # We add the error to the base object too
- # because model errors are used in the API responses and the `form_errors` helper.
- schedule.errors.add(:base, forbidden_message)
-
- ServiceResponse.error(payload: schedule, message: [forbidden_message], reason: :forbidden)
- end
-
- def forbidden_message
+ def authorize_message
_('The current user is not authorized to create the pipeline schedule')
end
+ strong_memoize_attr :authorize_message
end
end
end
diff --git a/app/services/ci/pipeline_schedules/update_service.rb b/app/services/ci/pipeline_schedules/update_service.rb
index 28c22e0a868..2fd1173ecce 100644
--- a/app/services/ci/pipeline_schedules/update_service.rb
+++ b/app/services/ci/pipeline_schedules/update_service.rb
@@ -2,44 +2,22 @@
module Ci
module PipelineSchedules
- class UpdateService
+ class UpdateService < BaseSaveService
+ AUTHORIZE = :update_pipeline_schedule
+
def initialize(schedule, user, params)
@schedule = schedule
@user = user
+ @project = schedule.project
@params = params
end
- def execute
- return forbidden unless allowed?
-
- schedule.assign_attributes(params)
-
- if schedule.save
- ServiceResponse.success(payload: schedule)
- else
- ServiceResponse.error(message: schedule.errors.full_messages)
- end
- end
-
private
- attr_reader :schedule, :user, :params
-
- def allowed?
- user.can?(:update_pipeline_schedule, schedule)
- end
-
- def forbidden
- # We add the error to the base object too
- # because model errors are used in the API responses and the `form_errors` helper.
- schedule.errors.add(:base, forbidden_message)
-
- ServiceResponse.error(message: [forbidden_message], reason: :forbidden)
- end
-
- def forbidden_message
+ def authorize_message
_('The current user is not authorized to update the pipeline schedule')
end
+ strong_memoize_attr :authorize_message
end
end
end
diff --git a/app/services/todos/destroy/group_private_service.rb b/app/services/todos/destroy/group_private_service.rb
index d7ecbb952aa..60599ca9ca4 100644
--- a/app/services/todos/destroy/group_private_service.rb
+++ b/app/services/todos/destroy/group_private_service.rb
@@ -24,7 +24,10 @@ module Todos
override :authorized_users
def authorized_users
- group.direct_and_indirect_users.select(:id)
+ User.from_union([
+ group.project_users_with_descendants.select(:id),
+ group.members_with_parents.select(:user_id)
+ ], remove_duplicates: false)
end
override :todos_to_remove?
diff --git a/app/views/clusters/clusters/user/_form.html.haml b/app/views/clusters/clusters/user/_form.html.haml
index 893271ea982..4ecef4b76ce 100644
--- a/app/views/clusters/clusters/user/_form.html.haml
+++ b/app/views/clusters/clusters/user/_form.html.haml
@@ -47,7 +47,7 @@
.form-group
.form-check
- = platform_kubernetes_field.check_box :authorization_type, { data: { qa_selector: 'rbac_checkbox'}, inline: true, class: 'form-check-input' }, 'rbac', 'abac'
+ = platform_kubernetes_field.check_box :authorization_type, { inline: true, class: 'form-check-input' }, 'rbac', 'abac'
= platform_kubernetes_field.label :authorization_type, s_('ClusterIntegration|RBAC-enabled cluster'), class: 'form-check-label label-bold'
%small.form-text.text-muted
= '%{help_text} %{help_link}'.html_safe % { help_text: rbac_help_text, help_link: rbac_help_link }
@@ -73,4 +73,4 @@
= render('clusters/clusters/namespace', platform_field: platform_kubernetes_field)
.form-group
- = field.submit s_('ClusterIntegration|Add Kubernetes cluster'), pajamas_button: true, data: { qa_selector: 'add_kubernetes_cluster_button' }
+ = field.submit s_('ClusterIntegration|Add Kubernetes cluster'), pajamas_button: true
diff --git a/app/views/dashboard/todos/_todo.html.haml b/app/views/dashboard/todos/_todo.html.haml
index e20fccc218a..1cd8015934e 100644
--- a/app/views/dashboard/todos/_todo.html.haml
+++ b/app/views/dashboard/todos/_todo.html.haml
@@ -48,6 +48,7 @@
= first_line_in_markdown(todo, :body, 125, is_todo: true, project: todo.project, group: todo.group)
= render_if_exists "dashboard/todos/diff_summary", local_assigns: { todo: todo }
+ = render_if_exists "dashboard/todos/review_summary", local_assigns: { todo: todo }
.todo-timestamp.gl-white-space-nowrap.gl-sm-ml-3.gl-mt-2.gl-mb-2.gl-sm-my-0.gl-px-2.gl-sm-px-0
%span.todo-timestamp.gl-font-sm.gl-text-secondary