diff options
Diffstat (limited to 'app')
16 files changed, 391 insertions, 48 deletions
diff --git a/app/assets/javascripts/ci/artifacts/components/job_artifacts_table.vue b/app/assets/javascripts/ci/artifacts/components/job_artifacts_table.vue index 3f6ea56382f..035e26d09e6 100644 --- a/app/assets/javascripts/ci/artifacts/components/job_artifacts_table.vue +++ b/app/assets/javascripts/ci/artifacts/components/job_artifacts_table.vue @@ -9,6 +9,7 @@ import { GlIcon, GlPagination, GlFormCheckbox, + GlTooltipDirective, } from '@gitlab/ui'; import { createAlert } from '~/alert'; import { getIdFromGraphQLId, convertToGraphQLId } from '~/graphql_shared/utils'; @@ -43,6 +44,7 @@ import { I18N_BULK_DELETE_PARTIAL_ERROR, I18N_BULK_DELETE_CONFIRMATION_TOAST, SELECTED_ARTIFACTS_MAX_COUNT, + I18N_BULK_DELETE_MAX_SELECTED, } from '../constants'; import JobCheckbox from './job_checkbox.vue'; import ArtifactsBulkDelete from './artifacts_bulk_delete.vue'; @@ -78,6 +80,9 @@ export default { ArtifactsTableRowDetails, FeedbackBanner, }, + directives: { + GlTooltip: GlTooltipDirective, + }, mixins: [glFeatureFlagsMixin()], inject: ['projectId', 'projectPath', 'canDestroyArtifacts'], apollo: { @@ -164,6 +169,25 @@ export default { artifactsToDelete() { return this.isDeletingArtifactsForJob ? this.jobArtifactsToDelete : this.selectedArtifacts; }, + isAnyVisibleArtifactSelected() { + return this.jobArtifacts.some((job) => + job.artifacts.nodes.some((artifactNode) => + this.selectedArtifacts.includes(artifactNode.id), + ), + ); + }, + areAllVisibleArtifactsSelected() { + return this.jobArtifacts.every((job) => + job.artifacts.nodes.every((artifactNode) => + this.selectedArtifacts.includes(artifactNode.id), + ), + ); + }, + selectAllTooltipText() { + return this.isSelectedArtifactsLimitReached && !this.isAnyVisibleArtifactSelected + ? I18N_BULK_DELETE_MAX_SELECTED + : ''; + }, }, methods: { refetchArtifacts() { @@ -205,11 +229,11 @@ export default { } }, selectArtifact(artifactNode, checked) { - if (checked) { - if (!this.isSelectedArtifactsLimitReached) { - this.selectedArtifacts.push(artifactNode.id); - } - } else { + const isSelected = this.selectedArtifacts.includes(artifactNode.id); + + if (checked && !isSelected && !this.isSelectedArtifactsLimitReached) { + this.selectedArtifacts.push(artifactNode.id); + } else if (isSelected) { this.selectedArtifacts.splice(this.selectedArtifacts.indexOf(artifactNode.id), 1); } }, @@ -274,6 +298,11 @@ export default { this.isBulkDeleteModalVisible = false; this.jobArtifactsToDelete = []; }, + handleSelectAllChecked(checked) { + this.jobArtifacts.map((job) => + job.artifacts.nodes.map((artifactNode) => this.selectArtifact(artifactNode, checked)), + ); + }, clearSelectedArtifacts() { this.selectedArtifacts = []; }, @@ -369,10 +398,12 @@ export default { </template> <template v-if="canBulkDestroyArtifacts" #head(checkbox)> <gl-form-checkbox - :disabled="!anyArtifactsSelected" - :checked="anyArtifactsSelected" - :indeterminate="anyArtifactsSelected" - @change="clearSelectedArtifacts" + v-gl-tooltip.right + :title="selectAllTooltipText" + :checked="isAnyVisibleArtifactSelected" + :indeterminate="isAnyVisibleArtifactSelected && !areAllVisibleArtifactsSelected" + :disabled="isSelectedArtifactsLimitReached && !isAnyVisibleArtifactSelected" + @change="handleSelectAllChecked" /> </template> <template diff --git a/app/assets/javascripts/ci/artifacts/components/job_checkbox.vue b/app/assets/javascripts/ci/artifacts/components/job_checkbox.vue index 91296bd507e..861278147e9 100644 --- a/app/assets/javascripts/ci/artifacts/components/job_checkbox.vue +++ b/app/assets/javascripts/ci/artifacts/components/job_checkbox.vue @@ -48,7 +48,7 @@ export default { }, }, methods: { - handleInput(checked) { + handleChange(checked) { if (checked) { this.unselectedArtifacts.forEach((node) => this.$emit('selectArtifact', node, true)); } else { @@ -65,6 +65,6 @@ export default { :disabled="disabled" :checked="checked" :indeterminate="indeterminate" - @input="handleInput" + @change="handleChange" /> </template> diff --git a/app/assets/javascripts/ml/experiment_tracking/routes/candidates/show/ml_candidates_show.vue b/app/assets/javascripts/ml/experiment_tracking/routes/candidates/show/ml_candidates_show.vue index 3ef73e7c874..b7a612a9688 100644 --- a/app/assets/javascripts/ml/experiment_tracking/routes/candidates/show/ml_candidates_show.vue +++ b/app/assets/javascripts/ml/experiment_tracking/routes/candidates/show/ml_candidates_show.vue @@ -17,6 +17,10 @@ import { DELETE_CANDIDATE_PRIMARY_ACTION_LABEL, DELETE_CANDIDATE_MODAL_TITLE, MLFLOW_ID_LABEL, + CI_SECTION_LABEL, + JOB_LABEL, + CI_USER_LABEL, + CI_MR_LABEL, } from './translations'; export default { @@ -43,11 +47,18 @@ export default { DELETE_CANDIDATE_PRIMARY_ACTION_LABEL, DELETE_CANDIDATE_MODAL_TITLE, MLFLOW_ID_LABEL, + CI_SECTION_LABEL, + JOB_LABEL, + CI_USER_LABEL, + CI_MR_LABEL, }, computed: { info() { return Object.freeze(this.candidate.info); }, + ciJob() { + return Object.freeze(this.info.ci_job); + }, sections() { return [ { @@ -106,6 +117,31 @@ export default { :text="$options.i18n.ARTIFACTS_LABEL" /> + <template v-if="ciJob"> + <tr class="divider"></tr> + + <detail-row + :label="$options.i18n.JOB_LABEL" + :text="ciJob.name" + :href="ciJob.path" + :section-label="$options.i18n.CI_SECTION_LABEL" + /> + + <detail-row + v-if="ciJob.user" + :label="$options.i18n.CI_USER_LABEL" + :href="ciJob.user.path" + :text="ciJob.user.username" + /> + + <detail-row + v-if="ciJob.merge_request" + :label="$options.i18n.CI_MR_LABEL" + :text="ciJob.merge_request.title" + :href="ciJob.merge_request.path" + /> + </template> + <template v-for="{ sectionName, sectionValues } in sections"> <tr v-if="sectionValues" :key="sectionName" class="divider"></tr> diff --git a/app/assets/javascripts/ml/experiment_tracking/routes/candidates/show/translations.js b/app/assets/javascripts/ml/experiment_tracking/routes/candidates/show/translations.js index 66ee84adb4e..fa9518f3e27 100644 --- a/app/assets/javascripts/ml/experiment_tracking/routes/candidates/show/translations.js +++ b/app/assets/javascripts/ml/experiment_tracking/routes/candidates/show/translations.js @@ -1,4 +1,4 @@ -import { s__ } from '~/locale'; +import { __, s__ } from '~/locale'; export const TITLE_LABEL = s__('MlExperimentTracking|Model candidate details'); export const INFO_LABEL = s__('MlExperimentTracking|Info'); @@ -15,3 +15,7 @@ export const DELETE_CANDIDATE_CONFIRMATION_MESSAGE = s__( ); export const DELETE_CANDIDATE_PRIMARY_ACTION_LABEL = s__('MlExperimentTracking|Delete candidate'); export const DELETE_CANDIDATE_MODAL_TITLE = s__('MLExperimentTracking|Delete candidate?'); +export const CI_SECTION_LABEL = __('CI'); +export const JOB_LABEL = __('Job'); +export const CI_USER_LABEL = s__('MlExperimentTracking|Triggered by'); +export const CI_MR_LABEL = __('Merge request'); diff --git a/app/assets/javascripts/projects/commit_box/info/components/commit_refs.vue b/app/assets/javascripts/projects/commit_box/info/components/commit_refs.vue new file mode 100644 index 00000000000..936938f3032 --- /dev/null +++ b/app/assets/javascripts/projects/commit_box/info/components/commit_refs.vue @@ -0,0 +1,121 @@ +<script> +import { createAlert } from '~/alert'; +import commitReferencesQuery from '../graphql/queries/commit_references.query.graphql'; +import containingBranchesQuery from '../graphql/queries/commit_containing_branches.query.graphql'; +import containingTagsQuery from '../graphql/queries/commit_containing_tags.query.graphql'; +import { + BRANCHES, + TAGS, + FETCH_CONTAINING_REFS_EVENT, + FETCH_COMMIT_REFERENCES_ERROR, +} from '../constants'; +import RefsList from './refs_list.vue'; + +export default { + name: 'CommitRefs', + components: { + RefsList, + }, + inject: ['fullPath', 'commitSha'], + apollo: { + project: { + query: commitReferencesQuery, + variables() { + return this.queryVariables; + }, + update({ + project: { + commitReferences: { tippingTags, tippingBranches, containingBranches, containingTags }, + }, + }) { + this.tippingTags = tippingTags.names; + this.tippingBranches = tippingBranches.names; + this.hasContainingBranches = Boolean(containingBranches.names.length); + this.hasContainingTags = Boolean(containingTags.names.length); + }, + error() { + createAlert({ + message: this.$options.i18n.errorMessage, + captureError: true, + }); + }, + }, + }, + data() { + return { + containingTags: [], + containingBranches: [], + tippingTags: [], + tippingBranches: [], + hasContainingBranches: false, + hasContainingTags: false, + }; + }, + computed: { + hasBranches() { + return this.tippingBranches.length || this.hasContainingBranches; + }, + hasTags() { + return this.tippingTags.length || this.hasContainingTags; + }, + queryVariables() { + return { + fullPath: this.fullPath, + commitSha: this.commitSha, + }; + }, + }, + methods: { + async fetchContainingRefs({ query, namespace }) { + try { + const { data } = await this.$apollo.query({ + query, + variables: this.queryVariables, + }); + this[namespace] = data.project.commitReferences[namespace].names; + return data.project.commitReferences[namespace].names; + } catch { + return createAlert({ + message: this.$options.i18n.errorMessage, + captureError: true, + }); + } + }, + fetchContainingBranches() { + this.fetchContainingRefs({ query: containingBranchesQuery, namespace: 'containingBranches' }); + }, + fetchContainingTags() { + this.fetchContainingRefs({ query: containingTagsQuery, namespace: 'containingTags' }); + }, + }, + i18n: { + branches: BRANCHES, + tags: TAGS, + errorMessage: FETCH_COMMIT_REFERENCES_ERROR, + }, + fetchContainingRefsEvent: FETCH_CONTAINING_REFS_EVENT, +}; +</script> + +<template> + <div class="gl-ml-7"> + <refs-list + v-if="hasBranches" + :has-containing-refs="hasContainingBranches" + :is-loading="$apollo.queries.project.loading" + :tipping-refs="tippingBranches" + :containing-refs="containingBranches" + :namespace="$options.i18n.branches" + @[$options.fetchContainingRefsEvent]="fetchContainingBranches" + /> + <refs-list + v-if="hasTags" + :has-containing-refs="hasContainingTags" + :is-loading="$apollo.queries.project.loading" + :tipping-refs="tippingTags" + :containing-refs="containingTags" + :namespace="$options.i18n.tags" + @[$options.fetchContainingRefsEvent]="fetchContainingTags" + /> + </div> +</template> diff --git a/app/assets/javascripts/projects/commit_box/info/components/refs_list.vue b/app/assets/javascripts/projects/commit_box/info/components/refs_list.vue new file mode 100644 index 00000000000..602fa26efa7 --- /dev/null +++ b/app/assets/javascripts/projects/commit_box/info/components/refs_list.vue @@ -0,0 +1,91 @@ +<script> +import { GlCollapse, GlBadge, GlButton, GlIcon, GlSkeletonLoader } from '@gitlab/ui'; +import { CONTAINING_COMMIT, FETCH_CONTAINING_REFS_EVENT } from '../constants'; + +export default { + name: 'RefsList', + components: { + GlCollapse, + GlSkeletonLoader, + GlBadge, + GlButton, + GlIcon, + }, + props: { + containingRefs: { + type: Array, + required: false, + default: () => [], + }, + tippingRefs: { + type: Array, + required: false, + default: () => [], + }, + namespace: { + type: String, + required: true, + }, + hasContainingRefs: { + type: Boolean, + required: true, + }, + isLoading: { + type: Boolean, + required: true, + }, + }, + data() { + return { + isContainingRefsVisible: false, + }; + }, + computed: { + collapseIcon() { + return this.isContainingRefsVisible ? 'chevron-down' : 'chevron-right'; + }, + isLoadingRefs() { + return this.isLoading && !this.containingRefs.length; + }, + }, + methods: { + toggleCollapse() { + this.isContainingRefsVisible = !this.isContainingRefsVisible; + }, + showRefs() { + this.toggleCollapse(); + this.$emit(FETCH_CONTAINING_REFS_EVENT); + }, + }, + i18n: { + containingCommit: CONTAINING_COMMIT, + }, +}; +</script> + +<template> + <div class="gl-pt-4"> + <span data-testid="title" class="gl-mr-2">{{ namespace }}</span> + <gl-badge v-for="ref in tippingRefs" :key="ref" class="gl-mt-2 gl-mr-2" size="sm">{{ + ref + }}</gl-badge> + <gl-button + v-if="hasContainingRefs" + class="gl-mr-2 gl-font-sm!" + variant="link" + size="small" + @click="showRefs" + > + <gl-icon :name="collapseIcon" :size="14" /> + {{ namespace }} {{ $options.i18n.containingCommit }} + </gl-button> + <gl-collapse :visible="isContainingRefsVisible"> + <gl-skeleton-loader v-if="isLoadingRefs" :lines="1" /> + <template v-else> + <gl-badge v-for="ref in containingRefs" :key="ref" class="gl-mt-3 gl-mr-2" size="sm">{{ + ref + }}</gl-badge> + </template> + </gl-collapse> + </div> +</template> diff --git a/app/assets/javascripts/projects/commit_box/info/constants.js b/app/assets/javascripts/projects/commit_box/info/constants.js index be0bf715314..f255d6c3877 100644 --- a/app/assets/javascripts/projects/commit_box/info/constants.js +++ b/app/assets/javascripts/projects/commit_box/info/constants.js @@ -1,7 +1,19 @@ -import { __ } from '~/locale'; +import { __, s__ } from '~/locale'; export const COMMIT_BOX_POLL_INTERVAL = 10000; export const PIPELINE_STATUS_FETCH_ERROR = __( 'There was a problem fetching the latest pipeline status.', ); + +export const BRANCHES = s__('Commit|Branches'); + +export const TAGS = s__('Commit|Tags'); + +export const CONTAINING_COMMIT = s__('Commit|containing commit'); + +export const FETCH_CONTAINING_REFS_EVENT = 'fetch-containing-refs'; + +export const FETCH_COMMIT_REFERENCES_ERROR = s__( + 'Commit|There was an error fetching the commit references. Please try again later.', +); diff --git a/app/assets/javascripts/projects/commit_box/info/graphql/queries/commit_containing_branches.query.graphql b/app/assets/javascripts/projects/commit_box/info/graphql/queries/commit_containing_branches.query.graphql new file mode 100644 index 00000000000..ea74efdbc46 --- /dev/null +++ b/app/assets/javascripts/projects/commit_box/info/graphql/queries/commit_containing_branches.query.graphql @@ -0,0 +1,10 @@ +query CommitContainingBranches($fullPath: ID!, $commitSha: String!) { + project(fullPath: $fullPath) { + id + commitReferences(commitSha: $commitSha) { + containingBranches(excludeTipped: true) { + names + } + } + } +} diff --git a/app/assets/javascripts/projects/commit_box/info/graphql/queries/commit_containing_tags.query.graphql b/app/assets/javascripts/projects/commit_box/info/graphql/queries/commit_containing_tags.query.graphql new file mode 100644 index 00000000000..d736dc3ab66 --- /dev/null +++ b/app/assets/javascripts/projects/commit_box/info/graphql/queries/commit_containing_tags.query.graphql @@ -0,0 +1,10 @@ +query CommitContainingTags($fullPath: ID!, $commitSha: String!) { + project(fullPath: $fullPath) { + id + commitReferences(commitSha: $commitSha) { + containingTags(excludeTipped: true) { + names + } + } + } +} diff --git a/app/assets/javascripts/projects/commit_box/info/graphql/queries/commit_references.query.graphql b/app/assets/javascripts/projects/commit_box/info/graphql/queries/commit_references.query.graphql new file mode 100644 index 00000000000..71d911c2acc --- /dev/null +++ b/app/assets/javascripts/projects/commit_box/info/graphql/queries/commit_references.query.graphql @@ -0,0 +1,19 @@ +query CommitReferences($fullPath: ID!, $commitSha: String!) { + project(fullPath: $fullPath) { + id + commitReferences(commitSha: $commitSha) { + containingBranches(excludeTipped: true, limit: 1) { + names + } + containingTags(excludeTipped: true, limit: 1) { + names + } + tippingBranches { + names + } + tippingTags { + names + } + } + } +} diff --git a/app/assets/javascripts/projects/commit_box/info/index.js b/app/assets/javascripts/projects/commit_box/info/index.js index 7c4b76fd62f..8f09c8e1e11 100644 --- a/app/assets/javascripts/projects/commit_box/info/index.js +++ b/app/assets/javascripts/projects/commit_box/info/index.js @@ -1,12 +1,10 @@ import { fetchCommitMergeRequests } from '~/commit_merge_requests'; import { initCommitPipelineMiniGraph } from './init_commit_pipeline_mini_graph'; -import { loadBranches } from './load_branches'; import initCommitPipelineStatus from './init_commit_pipeline_status'; +import initCommitReferences from './init_commit_references'; export const initCommitBoxInfo = () => { // Display commit related branches - loadBranches(); - // Related merge requests to this commit fetchCommitMergeRequests(); @@ -14,4 +12,6 @@ export const initCommitBoxInfo = () => { initCommitPipelineMiniGraph(); initCommitPipelineStatus(); + + initCommitReferences(); }; diff --git a/app/assets/javascripts/projects/commit_box/info/init_commit_references.js b/app/assets/javascripts/projects/commit_box/info/init_commit_references.js new file mode 100644 index 00000000000..c8497187211 --- /dev/null +++ b/app/assets/javascripts/projects/commit_box/info/init_commit_references.js @@ -0,0 +1,32 @@ +import Vue from 'vue'; +import VueApollo from 'vue-apollo'; +import createDefaultClient from '~/lib/graphql'; +import CommitBranches from './components/commit_refs.vue'; + +Vue.use(VueApollo); + +const apolloProvider = new VueApollo({ + defaultClient: createDefaultClient(), +}); + +export default (selector = 'js-commit-branches-and-tags') => { + const el = document.getElementById(selector); + + if (!el) { + return false; + } + + const { fullPath, commitSha } = el.dataset; + + return new Vue({ + el, + apolloProvider, + provide: { + fullPath, + commitSha, + }, + render(createElement) { + return createElement(CommitBranches); + }, + }); +}; diff --git a/app/assets/javascripts/projects/commit_box/info/load_branches.js b/app/assets/javascripts/projects/commit_box/info/load_branches.js deleted file mode 100644 index 8333e70b951..00000000000 --- a/app/assets/javascripts/projects/commit_box/info/load_branches.js +++ /dev/null @@ -1,24 +0,0 @@ -import axios from 'axios'; -import { sanitize } from '~/lib/dompurify'; -import { __ } from '~/locale'; -import { initDetailsButton } from './init_details_button'; - -export const loadBranches = (containerSelector = '.js-commit-box-info') => { - const containerEl = document.querySelector(containerSelector); - if (!containerEl) { - return; - } - - const { commitPath } = containerEl.dataset; - const branchesEl = containerEl.querySelector('.commit-info.branches'); - axios - .get(commitPath) - .then(({ data }) => { - branchesEl.innerHTML = sanitize(data); - - initDetailsButton(); - }) - .catch(() => { - branchesEl.textContent = __('Failed to load branches. Please try again.'); - }); -}; diff --git a/app/services/alert_management/process_prometheus_alert_service.rb b/app/services/alert_management/process_prometheus_alert_service.rb index e0594247975..556f04e8786 100644 --- a/app/services/alert_management/process_prometheus_alert_service.rb +++ b/app/services/alert_management/process_prometheus_alert_service.rb @@ -6,9 +6,10 @@ module AlertManagement include ::AlertManagement::AlertProcessing include ::AlertManagement::Responses - def initialize(project, payload) + def initialize(project, payload, integration: nil) @project = project @payload = payload + @integration = integration end def execute @@ -24,7 +25,7 @@ module AlertManagement private - attr_reader :project, :payload + attr_reader :project, :payload, :integration override :incoming_payload def incoming_payload @@ -32,6 +33,7 @@ module AlertManagement Gitlab::AlertManagement::Payload.parse( project, payload, + integration: integration, monitoring_tool: Gitlab::AlertManagement::Payload::MONITORING_TOOLS[:prometheus] ) end diff --git a/app/services/projects/prometheus/alerts/notify_service.rb b/app/services/projects/prometheus/alerts/notify_service.rb index 1d24a113e05..f1c093c89b7 100644 --- a/app/services/projects/prometheus/alerts/notify_service.rb +++ b/app/services/projects/prometheus/alerts/notify_service.rb @@ -36,7 +36,7 @@ module Projects truncate_alerts! if max_alerts_exceeded? - process_prometheus_alerts + process_prometheus_alerts(integration) created end @@ -151,10 +151,10 @@ module Projects ActiveSupport::SecurityUtils.secure_compare(expected, actual) end - def process_prometheus_alerts + def process_prometheus_alerts(integration) alerts.map do |alert| AlertManagement::ProcessPrometheusAlertService - .new(project, alert) + .new(project, alert, integration: integration) .execute end end diff --git a/app/views/projects/commit/_commit_box.html.haml b/app/views/projects/commit/_commit_box.html.haml index 079e24c6389..c161e1c9d2a 100644 --- a/app/views/projects/commit/_commit_box.html.haml +++ b/app/views/projects/commit/_commit_box.html.haml @@ -29,15 +29,14 @@ %pre.commit-description< = preserve(markdown_field(@commit, :description)) -.info-well.js-commit-box-info{ 'data-commit-path' => branches_project_commit_path(@project, @commit.id) } +.info-well .well-segment .icon-container.commit-icon = custom_icon("icon_commit") %span.cgray= n_('parent', 'parents', @commit.parents.count) - @commit.parents.each do |parent| = link_to parent.short_id, project_commit_path(@project, parent), class: "commit-sha" - .commit-info.branches - = gl_loading_icon(inline: true, css_class: 'gl-vertical-align-middle') + #js-commit-branches-and-tags{ data: { full_path: @project.full_path, commit_sha: @commit.short_id } } .well-segment.merge-request-info .icon-container |