diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-06-29 00:10:10 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-06-29 00:10:10 +0300 |
commit | 59223e71ada4330f2219e99e660cc8b6d470fc16 (patch) | |
tree | 2dc2429bb647c847cbeb58233c3b8370f0f363d2 | |
parent | d12d801795043280c3d726fae0abfec63266d156 (diff) |
Add latest changes from gitlab-org/gitlab@master
44 files changed, 1031 insertions, 192 deletions
diff --git a/app/assets/javascripts/pipeline_editor/components/header/pipeline_editor_mini_graph.vue b/app/assets/javascripts/pipeline_editor/components/header/pipeline_editor_mini_graph.vue index 7beabcfe403..3662100d526 100644 --- a/app/assets/javascripts/pipeline_editor/components/header/pipeline_editor_mini_graph.vue +++ b/app/assets/javascripts/pipeline_editor/components/header/pipeline_editor_mini_graph.vue @@ -10,8 +10,6 @@ export default { }, components: { PipelineMiniGraph, - LinkedPipelinesMiniList: () => - import('ee_component/vue_shared/components/linked_pipelines_mini_list.vue'), }, inject: ['projectFullPath'], props: { @@ -47,9 +45,6 @@ export default { downstreamPipelines() { return this.linkedPipelines?.downstream?.nodes || []; }, - hasDownstreamPipelines() { - return this.downstreamPipelines.length > 0; - }, hasPipelineStages() { return this.pipelineStages.length > 0; }, @@ -87,23 +82,11 @@ export default { </script> <template> - <div + <pipeline-mini-graph v-if="hasPipelineStages" - class="gl-align-items-center gl-display-inline-flex gl-flex-wrap stage-cell gl-mr-5" - > - <linked-pipelines-mini-list - v-if="upstreamPipeline" - :triggered-by="/* eslint-disable @gitlab/vue-no-new-non-primitive-in-template */ [ - upstreamPipeline, - ] /* eslint-enable @gitlab/vue-no-new-non-primitive-in-template */" - data-testid="pipeline-editor-mini-graph-upstream" - /> - <pipeline-mini-graph :stages="pipelineStages" /> - <linked-pipelines-mini-list - v-if="hasDownstreamPipelines" - :triggered="downstreamPipelines" - :pipeline-path="pipelinePath" - data-testid="pipeline-editor-mini-graph-downstream" - /> - </div> + :downstream-pipelines="downstreamPipelines" + :pipeline-path="pipelinePath" + :stages="pipelineStages" + :upstream-pipeline="upstreamPipeline" + /> </template> diff --git a/app/assets/javascripts/pipeline_editor/components/header/pipeline_status.vue b/app/assets/javascripts/pipeline_editor/components/header/pipeline_status.vue index 4b9c98135ec..137dfca68d6 100644 --- a/app/assets/javascripts/pipeline_editor/components/header/pipeline_status.vue +++ b/app/assets/javascripts/pipeline_editor/components/header/pipeline_status.vue @@ -174,7 +174,7 @@ export default { <div class="gl-display-flex gl-flex-wrap"> <pipeline-editor-mini-graph :pipeline="pipeline" v-on="$listeners" /> <gl-button - class="gl-mt-2 gl-md-mt-0" + class="gl-ml-3" category="secondary" variant="confirm" :href="status.detailsPath" diff --git a/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_mini_graph.vue b/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_mini_graph.vue index 05cb2ebb769..f1edc3d4be8 100644 --- a/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_mini_graph.vue +++ b/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_mini_graph.vue @@ -1,32 +1,60 @@ <script> -import PipelineStage from '~/pipelines/components/pipelines_list/pipeline_stage.vue'; +import { GlIcon } from '@gitlab/ui'; +import PipelineStages from '~/pipelines/components/pipelines_list/pipeline_stages.vue'; /** * Renders the pipeline mini graph. */ export default { components: { - PipelineStage, + GlIcon, + PipelineStages, + LinkedPipelinesMiniList: () => + import('ee_component/vue_shared/components/linked_pipelines_mini_list.vue'), }, + arrowStyles: [ + 'arrow-icon gl-display-inline-block gl-mx-1 gl-text-gray-500 gl-vertical-align-middle!', + ], props: { - stages: { + downstreamPipelines: { type: Array, - required: true, + required: false, + default: () => [], }, - updateDropdown: { + isMergeTrain: { type: Boolean, required: false, default: false, }, + pipelinePath: { + type: String, + required: false, + default: '', + }, + stages: { + type: Array, + required: true, + default: () => [], + }, stagesClass: { type: [Array, Object, String], required: false, default: '', }, - isMergeTrain: { + updateDropdown: { type: Boolean, required: false, default: false, }, + upstreamPipeline: { + type: Object, + required: false, + default: () => {}, + }, + }, + computed: { + hasDownstreamPipelines() { + return Boolean(this.downstreamPipelines.length); + }, }, methods: { onPipelineActionRequestComplete() { @@ -36,19 +64,39 @@ export default { }; </script> <template> - <div data-testid="pipeline-mini-graph" class="gl-display-inline gl-vertical-align-middle"> - <div - v-for="stage in stages" - :key="stage.name" - :class="stagesClass" - class="dropdown gl-display-inline-block gl-mr-2 gl-my-2 gl-vertical-align-middle stage-container" - > - <pipeline-stage - :stage="stage" - :update-dropdown="updateDropdown" - :is-merge-train="isMergeTrain" - @pipelineActionRequestComplete="onPipelineActionRequestComplete" - /> - </div> + <div class="stage-cell" data-testid="pipeline-mini-graph"> + <linked-pipelines-mini-list + v-if="upstreamPipeline" + :triggered-by="/* eslint-disable @gitlab/vue-no-new-non-primitive-in-template */ [ + upstreamPipeline, + ] /* eslint-enable @gitlab/vue-no-new-non-primitive-in-template */" + data-testid="pipeline-mini-graph-upstream" + /> + <gl-icon + v-if="upstreamPipeline" + :class="$options.arrowStyles" + name="long-arrow" + data-testid="upstream-arrow-icon" + /> + <pipeline-stages + :is-merge-train="isMergeTrain" + :stages="stages" + :update-dropdown="updateDropdown" + :stages-class="stagesClass" + data-testid="pipeline-stages" + @pipelineActionRequestComplete="onPipelineActionRequestComplete" + /> + <gl-icon + v-if="hasDownstreamPipelines" + :class="$options.arrowStyles" + name="long-arrow" + data-testid="downstream-arrow-icon" + /> + <linked-pipelines-mini-list + v-if="hasDownstreamPipelines" + :triggered="downstreamPipelines" + :pipeline-path="pipelinePath" + data-testid="pipeline-mini-graph-downstream" + /> </div> </template> diff --git a/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_stages.vue b/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_stages.vue new file mode 100644 index 00000000000..f1923e94a47 --- /dev/null +++ b/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_stages.vue @@ -0,0 +1,54 @@ +<script> +import PipelineStage from '~/pipelines/components/pipelines_list/pipeline_stage.vue'; +/** + * Renders the pipeline stages portion of the pipeline mini graph. + */ +export default { + components: { + PipelineStage, + }, + props: { + stages: { + type: Array, + required: true, + }, + updateDropdown: { + type: Boolean, + required: false, + default: false, + }, + stagesClass: { + type: [Array, Object, String], + required: false, + default: '', + }, + isMergeTrain: { + type: Boolean, + required: false, + default: false, + }, + }, + methods: { + onPipelineActionRequestComplete() { + this.$emit('pipelineActionRequestComplete'); + }, + }, +}; +</script> +<template> + <div data-testid="pipeline-stages" class="gl-display-inline gl-vertical-align-middle"> + <div + v-for="stage in stages" + :key="stage.name" + :class="stagesClass" + class="dropdown gl-display-inline-block gl-mr-2 gl-my-2 gl-vertical-align-middle stage-container" + > + <pipeline-stage + :stage="stage" + :update-dropdown="updateDropdown" + :is-merge-train="isMergeTrain" + @pipelineActionRequestComplete="onPipelineActionRequestComplete" + /> + </div> + </div> +</template> diff --git a/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_table.vue b/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_table.vue index 53da98434b0..4046ee69428 100644 --- a/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_table.vue +++ b/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_table.vue @@ -17,8 +17,6 @@ const DEFAULT_TH_CLASSES = export default { components: { GlTableLite, - LinkedPipelinesMiniList: () => - import('ee_component/vue_shared/components/linked_pipelines_mini_list.vue'), PipelineMiniGraph, PipelineOperations, PipelinesStatusBadge, @@ -169,29 +167,14 @@ export default { </template> <template #cell(stages)="{ item }"> - <div class="stage-cell"> - <!-- This empty div should be removed, see https://gitlab.com/gitlab-org/gitlab/-/issues/323488 --> - <div></div> - <linked-pipelines-mini-list - v-if="item.triggered_by" - :triggered-by="/* eslint-disable @gitlab/vue-no-new-non-primitive-in-template */ [ - item.triggered_by, - ] /* eslint-enable @gitlab/vue-no-new-non-primitive-in-template */" - data-testid="mini-graph-upstream" - /> - <pipeline-mini-graph - v-if="item.details && item.details.stages && item.details.stages.length > 0" - :stages="item.details.stages" - :update-dropdown="updateGraphDropdown" - @pipelineActionRequestComplete="onPipelineActionRequestComplete" - /> - <linked-pipelines-mini-list - v-if="item.triggered.length" - :triggered="item.triggered" - :pipeline-path="item.path" - data-testid="mini-graph-downstream" - /> - </div> + <pipeline-mini-graph + :downstream-pipelines="item.triggered" + :pipeline-path="item.path" + :stages="item.details.stages" + :update-dropdown="updateGraphDropdown" + :upstream-pipeline="item.triggered_by" + @pipelineActionRequestComplete="onPipelineActionRequestComplete" + /> </template> <template #cell(actions)="{ item }"> diff --git a/app/assets/javascripts/projects/commit_box/info/components/commit_box_pipeline_mini_graph.vue b/app/assets/javascripts/projects/commit_box/info/components/commit_box_pipeline_mini_graph.vue index 1cdf26b76b7..a4044106a53 100644 --- a/app/assets/javascripts/projects/commit_box/info/components/commit_box_pipeline_mini_graph.vue +++ b/app/assets/javascripts/projects/commit_box/info/components/commit_box_pipeline_mini_graph.vue @@ -2,11 +2,11 @@ import { GlLoadingIcon } from '@gitlab/ui'; import createFlash from '~/flash'; import { __ } from '~/locale'; -import PipelineMiniGraph from '~/pipelines/components/pipelines_list/pipeline_mini_graph.vue'; import { getQueryHeaders, toggleQueryPollingByVisibility, } from '~/pipelines/components/graph/utils'; +import PipelineMiniGraph from '~/pipelines/components/pipelines_list/pipeline_mini_graph.vue'; import { formatStages } from '../utils'; import getLinkedPipelinesQuery from '../graphql/queries/get_linked_pipelines.query.graphql'; import getPipelineStagesQuery from '../graphql/queries/get_pipeline_stages.query.graphql'; @@ -21,8 +21,6 @@ export default { components: { GlLoadingIcon, PipelineMiniGraph, - LinkedPipelinesMiniList: () => - import('ee_component/vue_shared/components/linked_pipelines_mini_list.vue'), }, inject: { fullPath: { @@ -92,12 +90,12 @@ export default { }; }, computed: { - hasDownstream() { - return this.pipeline?.downstream?.nodes.length > 0; - }, downstreamPipelines() { return this.pipeline?.downstream?.nodes; }, + pipelinePath() { + return this.pipeline?.path ?? ''; + }, upstreamPipeline() { return this.pipeline?.upstream; }, @@ -128,23 +126,13 @@ export default { <template> <div class="gl-pt-2"> <gl-loading-icon v-if="$apollo.queries.pipeline.loading" /> - <div v-else class="gl-align-items-center gl-display-flex"> - <linked-pipelines-mini-list - v-if="upstreamPipeline" - :triggered-by="/* eslint-disable @gitlab/vue-no-new-non-primitive-in-template */ [ - upstreamPipeline, - ] /* eslint-enable @gitlab/vue-no-new-non-primitive-in-template */" - data-testid="commit-box-mini-graph-upstream" - /> - - <pipeline-mini-graph :stages="formattedStages" data-testid="commit-box-mini-graph" /> - - <linked-pipelines-mini-list - v-if="hasDownstream" - :triggered="downstreamPipelines" - :pipeline-path="pipeline.path" - data-testid="commit-box-mini-graph-downstream" - /> - </div> + <pipeline-mini-graph + v-else + data-testid="commit-box-pipeline-mini-graph" + :downstream-pipelines="downstreamPipelines" + :pipeline-path="pipelinePath" + :stages="formattedStages" + :upstream-pipeline="upstreamPipeline" + /> </div> </template> diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.vue b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.vue index 1e1a2049414..5ecf49b51be 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.vue @@ -11,8 +11,8 @@ import { } from '@gitlab/ui'; import mrWidgetPipelineMixin from 'ee_else_ce/vue_merge_request_widget/mixins/mr_widget_pipeline'; import { s__, n__ } from '~/locale'; -import PipelineMiniGraph from '~/pipelines/components/pipelines_list/pipeline_mini_graph.vue'; import PipelineArtifacts from '~/pipelines/components/pipelines_list/pipelines_artifacts.vue'; +import PipelineMiniGraph from '~/pipelines/components/pipelines_list/pipeline_mini_graph.vue'; import CiIcon from '~/vue_shared/components/ci_icon.vue'; import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue'; import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate/tooltip_on_truncate.vue'; @@ -31,8 +31,6 @@ export default { PipelineMiniGraph, TimeAgoTooltip, TooltipOnTruncate, - LinkedPipelinesMiniList: () => - import('ee_component/vue_shared/components/linked_pipelines_mini_list.vue'), }, directives: { GlTooltip: GlTooltipDirective, @@ -276,17 +274,15 @@ export default { </div> </div> <div> - <span class="gl-align-items-center gl-display-inline-flex mr-widget-pipeline-graph"> - <span class="gl-align-items-center gl-display-inline-flex gl-flex-wrap stage-cell"> - <linked-pipelines-mini-list v-if="triggeredBy.length" :triggered-by="triggeredBy" /> - <pipeline-mini-graph - v-if="hasStages" - stages-class="mr-widget-pipeline-stages" - :stages="pipeline.details.stages" - :is-merge-train="isMergeTrain" - /> - </span> - <linked-pipelines-mini-list v-if="triggered.length" :triggered="triggered" /> + <span class="gl-align-items-center gl-display-inline-flex"> + <pipeline-mini-graph + v-if="pipeline.details.stages" + :downstream-pipelines="triggered" + :is-merge-train="isMergeTrain" + :stages="pipeline.details.stages" + :upstream-pipeline="triggeredBy[0]" + stages-class="mr-widget-pipeline-stages" + /> <pipeline-artifacts :pipeline-id="pipeline.id" :artifacts="artifacts" class="gl-ml-3" /> </span> </div> diff --git a/app/assets/stylesheets/page_bundles/merge_requests.scss b/app/assets/stylesheets/page_bundles/merge_requests.scss index 14873c54cd7..269afd01615 100644 --- a/app/assets/stylesheets/page_bundles/merge_requests.scss +++ b/app/assets/stylesheets/page_bundles/merge_requests.scss @@ -400,12 +400,6 @@ $tabs-holder-z-index: 250; display: block; } - .mr-widget-pipeline-graph { - .dropdown-menu { - z-index: $zindex-dropdown-menu; - } - } - .normal { flex: 1; flex-basis: auto; diff --git a/app/assets/stylesheets/pages/commits.scss b/app/assets/stylesheets/pages/commits.scss index c96d8ecc782..19318d87731 100644 --- a/app/assets/stylesheets/pages/commits.scss +++ b/app/assets/stylesheets/pages/commits.scss @@ -33,12 +33,6 @@ height: 22px; } } - - .mr-widget-pipeline-graph { - .dropdown-menu { - margin-top: 11px; - } - } } .branch-info .commit-icon { diff --git a/doc/administration/instance_limits.md b/doc/administration/instance_limits.md index d86414ae285..09f61e57b9e 100644 --- a/doc/administration/instance_limits.md +++ b/doc/administration/instance_limits.md @@ -793,19 +793,9 @@ Plan.default.actual_limits.update!(dotenv_size: 5.kilobytes) > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/17859) in GitLab 12.5. -You can limit the number of inbound alerts for [incidents](../operations/incident_management/incidents.md) -that can be created in a period of time. The inbound [incident management](../operations/incident_management/index.md) -alert limit can help prevent overloading your incident responders by reducing the -number of alerts or duplicate issues. - -To set inbound incident management alert limits: - -1. On the top bar, select **Menu > Admin**. -1. On the left sidebar, select **Settings > Network**. -1. Expand General **Incident Management Limits**. -1. Select the **Enable Incident Management inbound alert limit** checkbox. -1. Optional. Input a custom value for **Maximum requests per project per rate limit period**. Default is 3600. -1. Optional. Input a custom value for **Rate limit period**. Default is 3600 seconds. +This setting limits the number of inbound alert payloads over a period of time. + +Read more about [incident management rate limits](../user/admin_area/settings/rate_limit_on_pipelines_creation.md). ### Prometheus Alert JSON payloads diff --git a/doc/api/usage_data.md b/doc/api/usage_data.md index be816a0f864..6e50794a0ac 100644 --- a/doc/api/usage_data.md +++ b/doc/api/usage_data.md @@ -1,5 +1,5 @@ --- -stage: Growth +stage: Analytics group: Product Intelligence info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments type: reference, api diff --git a/doc/development/reusing_abstractions.md b/doc/development/reusing_abstractions.md index ccf82dc6c77..f3eb1ebcc0c 100644 --- a/doc/development/reusing_abstractions.md +++ b/doc/development/reusing_abstractions.md @@ -190,6 +190,10 @@ Everything in `app/finders`, typically used for retrieving data from a database. Finders can not reuse other finders in an attempt to better control the SQL queries they produce. +Finders' `execute` method should return `ActiveRecord::Relation`. Exceptions +can be added to `spec/support/finder_collection_allowlist.yml`. +See [`#298771`](https://gitlab.com/gitlab-org/gitlab/-/issues/298771) for more details. + ### Presenters Everything in `app/presenters`, used for exposing complex data to a Rails view, diff --git a/doc/development/service_ping/implement.md b/doc/development/service_ping/implement.md index 6948eb20e00..65a40ed7626 100644 --- a/doc/development/service_ping/implement.md +++ b/doc/development/service_ping/implement.md @@ -1,5 +1,5 @@ --- -stage: Growth +stage: Analytics group: Product Intelligence info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments --- diff --git a/doc/development/service_ping/index.md b/doc/development/service_ping/index.md index e776b78b710..cb6b0f87814 100644 --- a/doc/development/service_ping/index.md +++ b/doc/development/service_ping/index.md @@ -1,5 +1,5 @@ --- -stage: Growth +stage: Analytics group: Product Intelligence info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments --- diff --git a/doc/development/service_ping/metrics_dictionary.md b/doc/development/service_ping/metrics_dictionary.md index fee3bb571c2..dd201678e51 100644 --- a/doc/development/service_ping/metrics_dictionary.md +++ b/doc/development/service_ping/metrics_dictionary.md @@ -1,5 +1,5 @@ --- -stage: Growth +stage: Analytics group: Product Intelligence info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments --- diff --git a/doc/development/service_ping/metrics_instrumentation.md b/doc/development/service_ping/metrics_instrumentation.md index 4fd03eea84f..e1c51713f3c 100644 --- a/doc/development/service_ping/metrics_instrumentation.md +++ b/doc/development/service_ping/metrics_instrumentation.md @@ -1,5 +1,5 @@ --- -stage: Growth +stage: Analytics group: Product Intelligence info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments --- diff --git a/doc/development/service_ping/metrics_lifecycle.md b/doc/development/service_ping/metrics_lifecycle.md index c9cc9a4f2d2..28f77b6f587 100644 --- a/doc/development/service_ping/metrics_lifecycle.md +++ b/doc/development/service_ping/metrics_lifecycle.md @@ -1,5 +1,5 @@ --- -stage: Growth +stage: Analytics group: Product Intelligence info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments --- diff --git a/doc/development/service_ping/performance_indicator_metrics.md b/doc/development/service_ping/performance_indicator_metrics.md index 48c123171fa..bdd4c319d41 100644 --- a/doc/development/service_ping/performance_indicator_metrics.md +++ b/doc/development/service_ping/performance_indicator_metrics.md @@ -1,5 +1,5 @@ --- -stage: Growth +stage: Analytics group: Product Intelligence info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments --- diff --git a/doc/development/service_ping/review_guidelines.md b/doc/development/service_ping/review_guidelines.md index ee2d8f4f4a1..4ce5b2d577c 100644 --- a/doc/development/service_ping/review_guidelines.md +++ b/doc/development/service_ping/review_guidelines.md @@ -1,5 +1,5 @@ --- -stage: Growth +stage: Analytics group: Product Intelligence info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments --- diff --git a/doc/development/service_ping/troubleshooting.md b/doc/development/service_ping/troubleshooting.md index 2764ef41f98..99dab8fb21d 100644 --- a/doc/development/service_ping/troubleshooting.md +++ b/doc/development/service_ping/troubleshooting.md @@ -1,5 +1,5 @@ --- -stage: Growth +stage: Analytics group: Product Intelligence info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments --- diff --git a/doc/development/service_ping/usage_data.md b/doc/development/service_ping/usage_data.md index a25ad5f62be..a659bbf2265 100644 --- a/doc/development/service_ping/usage_data.md +++ b/doc/development/service_ping/usage_data.md @@ -1,5 +1,5 @@ --- -stage: Growth +stage: Analytics group: Product Intelligence info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments --- diff --git a/doc/development/snowplow/event_dictionary_guide.md b/doc/development/snowplow/event_dictionary_guide.md index 5ae81c3426d..7980395b1a9 100644 --- a/doc/development/snowplow/event_dictionary_guide.md +++ b/doc/development/snowplow/event_dictionary_guide.md @@ -1,5 +1,5 @@ --- -stage: Growth +stage: Analytics group: Product Intelligence info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments --- diff --git a/doc/development/snowplow/implementation.md b/doc/development/snowplow/implementation.md index 88fb1d5cfe4..e6b323c5f83 100644 --- a/doc/development/snowplow/implementation.md +++ b/doc/development/snowplow/implementation.md @@ -1,5 +1,5 @@ --- -stage: Growth +stage: Analytics group: Product Intelligence info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments --- diff --git a/doc/development/snowplow/index.md b/doc/development/snowplow/index.md index d6a7b900629..155ce87b8d9 100644 --- a/doc/development/snowplow/index.md +++ b/doc/development/snowplow/index.md @@ -1,5 +1,5 @@ --- -stage: Growth +stage: Analytics group: Product Intelligence info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments --- diff --git a/doc/development/snowplow/infrastructure.md b/doc/development/snowplow/infrastructure.md index 28541874e98..758c850e89f 100644 --- a/doc/development/snowplow/infrastructure.md +++ b/doc/development/snowplow/infrastructure.md @@ -1,5 +1,5 @@ --- -stage: Growth +stage: Analytics group: Product Intelligence info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments --- diff --git a/doc/development/snowplow/review_guidelines.md b/doc/development/snowplow/review_guidelines.md index 0359636380b..673166452b7 100644 --- a/doc/development/snowplow/review_guidelines.md +++ b/doc/development/snowplow/review_guidelines.md @@ -1,5 +1,5 @@ --- -stage: Growth +stage: Analytics group: Product Intelligence info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments --- diff --git a/doc/development/snowplow/schemas.md b/doc/development/snowplow/schemas.md index 4066151600d..799f8335000 100644 --- a/doc/development/snowplow/schemas.md +++ b/doc/development/snowplow/schemas.md @@ -1,5 +1,5 @@ --- -stage: Growth +stage: Analytics group: Product Intelligence info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments --- diff --git a/doc/development/snowplow/troubleshooting.md b/doc/development/snowplow/troubleshooting.md index 2a6db80a6f2..42a433e6a94 100644 --- a/doc/development/snowplow/troubleshooting.md +++ b/doc/development/snowplow/troubleshooting.md @@ -1,5 +1,5 @@ --- -stage: Growth +stage: Analytics group: Product Intelligence info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments --- diff --git a/doc/operations/product_analytics.md b/doc/operations/product_analytics.md index a55cbe906a0..98ba6a9203c 100644 --- a/doc/operations/product_analytics.md +++ b/doc/operations/product_analytics.md @@ -1,5 +1,5 @@ --- -stage: Growth +stage: Analytics group: Product Intelligence info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments --- diff --git a/doc/security/rate_limits.md b/doc/security/rate_limits.md index 695a0d52af6..e48a9999a06 100644 --- a/doc/security/rate_limits.md +++ b/doc/security/rate_limits.md @@ -43,6 +43,7 @@ You can set these rate limits in the Admin Area of your instance: - [Deprecated API rate limits](../user/admin_area/settings/deprecated_api_rate_limits.md) - [GitLab Pages rate limits](../administration/pages/index.md#rate-limits) - [Pipeline rate limits](../user/admin_area/settings/rate_limit_on_pipelines_creation.md) +- [Incident management rate limits](../user/admin_area/settings/incident_management_rate_limits.md) You can set these rate limits using the Rails console: diff --git a/doc/user/admin_area/settings/incident_management_rate_limits.md b/doc/user/admin_area/settings/incident_management_rate_limits.md new file mode 100644 index 00000000000..6226ec23be7 --- /dev/null +++ b/doc/user/admin_area/settings/incident_management_rate_limits.md @@ -0,0 +1,38 @@ +--- +type: reference +stage: Monitor +group: Respond +info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments +--- + +# Incident management rate limits **(ULTIMATE SELF)** + +> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/17859) in GitLab 12.5. + +You can limit the number of inbound alerts for [incidents](../../../operations/incident_management/incidents.md) +that can be created in a period of time. The inbound [incident management](../../../operations/incident_management/index.md) +alert limit can help prevent overloading your incident responders by reducing the +number of alerts or duplicate issues. + +As an example, if you set a limit of `10` requests every `60` seconds, +and `11` requests are sent to an [alert integration endpoint](../../../operations/incident_management/integrations.md) within one minute, +the eleventh request is blocked. Access to the endpoint is allowed again after one minute. + +This limit is: + +- Applied independently per project. +- Not applied per IP address. +- Disabled by default. + +Requests that exceed the limit are logged into `auth.log`. + +## Set a limit on inbound alerts + +To set inbound incident management alert limits: + +1. On the top bar, select **Menu > Admin**. +1. On the left sidebar, select **Settings > Network**. +1. Expand **Incident Management Limits**. +1. Select the **Enable Incident Management inbound alert limit** checkbox. +1. Optional. Input a custom value for **Maximum requests per project per rate limit period**. Default is 3600. +1. Optional. Input a custom value for **Rate limit period**. Default is 3600 seconds. diff --git a/doc/user/admin_area/settings/usage_statistics.md b/doc/user/admin_area/settings/usage_statistics.md index c74906c2762..65712a9a85c 100644 --- a/doc/user/admin_area/settings/usage_statistics.md +++ b/doc/user/admin_area/settings/usage_statistics.md @@ -1,5 +1,5 @@ --- -stage: Growth +stage: Analytics group: Product Intelligence info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments --- diff --git a/doc/user/gitlab_com/index.md b/doc/user/gitlab_com/index.md index 6dd84622362..085456f926a 100644 --- a/doc/user/gitlab_com/index.md +++ b/doc/user/gitlab_com/index.md @@ -344,6 +344,7 @@ after the limits change in January, 2021: | **GitLab Pages** requests (for a given **IP address**) | | **1000** requests per **50 seconds** | | **GitLab Pages** requests (for a given **GitLab Pages domain**) | | **5000** requests per **10 seconds** | | **Pipeline creation** requests (for a given **project, user, and commit**) | | **25** requests per minute | +| **Alert integration endpoint** requests (for a given **project**) | | **3600** requests per hour | More details are available on the rate limits for [protected paths](#protected-paths-throttle) and [raw diff --git a/spec/features/projects/commit/mini_pipeline_graph_spec.rb b/spec/features/projects/commit/mini_pipeline_graph_spec.rb index e472cff38ce..4740f6e19fe 100644 --- a/spec/features/projects/commit/mini_pipeline_graph_spec.rb +++ b/spec/features/projects/commit/mini_pipeline_graph_spec.rb @@ -27,7 +27,7 @@ RSpec.describe 'Mini Pipeline Graph in Commit View', :js do end it 'displays a mini pipeline graph' do - expect(page).to have_selector('[data-testid="commit-box-mini-graph"]') + expect(page).to have_selector('[data-testid="commit-box-pipeline-mini-graph"]') first('[data-testid="mini-pipeline-graph-dropdown"]').click diff --git a/spec/features/projects/settings/repository_settings_spec.rb b/spec/features/projects/settings/repository_settings_spec.rb index cfdd3d9224d..def7b1f5420 100644 --- a/spec/features/projects/settings/repository_settings_spec.rb +++ b/spec/features/projects/settings/repository_settings_spec.rb @@ -167,14 +167,14 @@ RSpec.describe 'Projects > Settings > Repository settings' do expect(project.remote_mirrors.first.only_protected_branches).to eq(false) end - it 'creates a push mirror that only mirrors protected branches', :js do + it 'creates a push mirror that only mirrors protected branches', :js, + quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/337394' do find('#only_protected_branches').click expect(find('.js-mirror-protected-hidden', visible: false).value).to eq('1') fill_in 'url', with: 'ssh://user@localhost/project.git' select 'SSH public key', from: 'Authentication method' - select_direction Sidekiq::Testing.fake! do diff --git a/spec/frontend/commit/commit_box_pipeline_mini_graph_spec.js b/spec/frontend/commit/commit_box_pipeline_mini_graph_spec.js index b1c8ba48475..ab5055de5e3 100644 --- a/spec/frontend/commit/commit_box_pipeline_mini_graph_spec.js +++ b/spec/frontend/commit/commit_box_pipeline_mini_graph_spec.js @@ -6,6 +6,7 @@ import { extendedWrapper } from 'helpers/vue_test_utils_helper'; import waitForPromises from 'helpers/wait_for_promises'; import createFlash from '~/flash'; import CommitBoxPipelineMiniGraph from '~/projects/commit_box/info/components/commit_box_pipeline_mini_graph.vue'; +import PipelineMiniGraph from '~/pipelines/components/pipelines_list/pipeline_mini_graph.vue'; import getLinkedPipelinesQuery from '~/projects/commit_box/info/graphql/queries/get_linked_pipelines.query.graphql'; import getPipelineStagesQuery from '~/projects/commit_box/info/graphql/queries/get_pipeline_stages.query.graphql'; import { mockPipelineStagesQueryResponse, mockStages } from './mock_data'; @@ -17,9 +18,7 @@ Vue.use(VueApollo); describe('Commit box pipeline mini graph', () => { let wrapper; - const findMiniGraph = () => wrapper.findByTestId('commit-box-mini-graph'); - const findUpstream = () => wrapper.findByTestId('commit-box-mini-graph-upstream'); - const findDownstream = () => wrapper.findByTestId('commit-box-mini-graph-downstream'); + const findPipelineMiniGraph = () => wrapper.findComponent(PipelineMiniGraph); const stagesHandler = jest.fn().mockResolvedValue(mockPipelineStagesQueryResponse); @@ -51,13 +50,16 @@ describe('Commit box pipeline mini graph', () => { await createComponent(); }); - it('should display the mini pipeine graph', () => { - expect(findMiniGraph().exists()).toBe(true); + it('should display the pipeline mini graph', () => { + expect(findPipelineMiniGraph().exists()).toBe(true); }); it('should not display linked pipelines', () => { - expect(findUpstream().exists()).toBe(false); - expect(findDownstream().exists()).toBe(false); + const downstreamPipelines = findPipelineMiniGraph().props('downstreamPipelines'); + const upstreamPipeline = findPipelineMiniGraph().props('upstreamPipeline'); + + expect(downstreamPipelines).toHaveLength(0); + expect(upstreamPipeline).toEqual(undefined); }); }); diff --git a/spec/frontend/pipeline_editor/components/header/pipeline_editor_mini_graph_spec.js b/spec/frontend/pipeline_editor/components/header/pipeline_editor_mini_graph_spec.js new file mode 100644 index 00000000000..93eb18c90cf --- /dev/null +++ b/spec/frontend/pipeline_editor/components/header/pipeline_editor_mini_graph_spec.js @@ -0,0 +1,109 @@ +import { shallowMount } from '@vue/test-utils'; +import Vue from 'vue'; +import VueApollo from 'vue-apollo'; +import createMockApollo from 'helpers/mock_apollo_helper'; +import waitForPromises from 'helpers/wait_for_promises'; +import PipelineEditorMiniGraph from '~/pipeline_editor/components/header/pipeline_editor_mini_graph.vue'; +import PipelineMiniGraph from '~/pipelines/components/pipelines_list/pipeline_mini_graph.vue'; +import getLinkedPipelinesQuery from '~/projects/commit_box/info/graphql/queries/get_linked_pipelines.query.graphql'; +import { PIPELINE_FAILURE } from '~/pipeline_editor/constants'; +import { mockLinkedPipelines, mockProjectFullPath, mockProjectPipeline } from '../../mock_data'; + +Vue.use(VueApollo); + +describe('Pipeline Status', () => { + let wrapper; + let mockApollo; + let mockLinkedPipelinesQuery; + + const createComponent = ({ hasStages = true, options } = {}) => { + wrapper = shallowMount(PipelineEditorMiniGraph, { + provide: { + dataMethod: 'graphql', + projectFullPath: mockProjectFullPath, + }, + propsData: { + pipeline: mockProjectPipeline({ hasStages }).pipeline, + }, + ...options, + }); + }; + + const createComponentWithApollo = (hasStages = true) => { + const handlers = [[getLinkedPipelinesQuery, mockLinkedPipelinesQuery]]; + mockApollo = createMockApollo(handlers); + + createComponent({ + hasStages, + options: { + apolloProvider: mockApollo, + }, + }); + }; + + const findPipelineMiniGraph = () => wrapper.findComponent(PipelineMiniGraph); + + beforeEach(() => { + mockLinkedPipelinesQuery = jest.fn(); + }); + + afterEach(() => { + mockLinkedPipelinesQuery.mockReset(); + wrapper.destroy(); + }); + + describe('when there are stages', () => { + beforeEach(() => { + createComponent(); + }); + + it('renders pipeline mini graph', () => { + expect(findPipelineMiniGraph().exists()).toBe(true); + }); + }); + + describe('when there are no stages', () => { + beforeEach(() => { + createComponent({ hasStages: false }); + }); + + it('does not render pipeline mini graph', () => { + expect(findPipelineMiniGraph().exists()).toBe(false); + }); + }); + + describe('when querying upstream and downstream pipelines', () => { + describe('when query succeeds', () => { + beforeEach(() => { + mockLinkedPipelinesQuery.mockResolvedValue(mockLinkedPipelines()); + createComponentWithApollo(); + }); + + it('should call the query with the correct variables', () => { + expect(mockLinkedPipelinesQuery).toHaveBeenCalledTimes(1); + expect(mockLinkedPipelinesQuery).toHaveBeenCalledWith({ + fullPath: mockProjectFullPath, + iid: mockProjectPipeline().pipeline.iid, + }); + }); + }); + + describe('when query fails', () => { + beforeEach(async () => { + mockLinkedPipelinesQuery.mockRejectedValue(new Error()); + createComponentWithApollo(); + await waitForPromises(); + }); + + it('should emit an error event when query fails', async () => { + expect(wrapper.emitted('showError')).toHaveLength(1); + expect(wrapper.emitted('showError')[0]).toEqual([ + { + type: PIPELINE_FAILURE, + reasons: [wrapper.vm.$options.i18n.linkedPipelinesFetchError], + }, + ]); + }); + }); + }); +}); diff --git a/spec/frontend/pipelines/components/pipelines_list/pipeline_mini_graph_spec.js b/spec/frontend/pipelines/components/pipelines_list/pipeline_stages_spec.js index 1cb43c199aa..1e31d8a62ff 100644 --- a/spec/frontend/pipelines/components/pipelines_list/pipeline_mini_graph_spec.js +++ b/spec/frontend/pipelines/components/pipelines_list/pipeline_stages_spec.js @@ -1,18 +1,18 @@ import { shallowMount } from '@vue/test-utils'; import { pipelines } from 'test_fixtures/pipelines/pipelines.json'; -import PipelineMiniGraph from '~/pipelines/components/pipelines_list/pipeline_mini_graph.vue'; import PipelineStage from '~/pipelines/components/pipelines_list/pipeline_stage.vue'; +import PipelineStages from '~/pipelines/components/pipelines_list/pipeline_stages.vue'; const mockStages = pipelines[0].details.stages; -describe('Pipeline Mini Graph', () => { +describe('Pipeline Stages', () => { let wrapper; const findPipelineStages = () => wrapper.findAll(PipelineStage); const findPipelineStagesAt = (i) => findPipelineStages().at(i); const createComponent = (props = {}) => { - wrapper = shallowMount(PipelineMiniGraph, { + wrapper = shallowMount(PipelineStages, { propsData: { stages: mockStages, ...props, diff --git a/spec/frontend/pipelines/linked_pipelines_mock_data.js b/spec/frontend/pipelines/linked_pipelines_mock_data.js new file mode 100644 index 00000000000..117c7f2ae52 --- /dev/null +++ b/spec/frontend/pipelines/linked_pipelines_mock_data.js @@ -0,0 +1,407 @@ +export default { + triggered_by: { + id: 129, + active: true, + path: '/gitlab-org/gitlab-foss/-/pipelines/129', + project: { + name: 'GitLabCE', + }, + details: { + status: { + icon: 'status_running', + text: 'running', + label: 'running', + group: 'running', + has_details: true, + details_path: '/gitlab-org/gitlab-foss/-/pipelines/129', + favicon: + '/assets/ci_favicons/dev/favicon_status_running-c3ad2fc53ea6079c174e5b6c1351ff349e99ec3af5a5622fb77b0fe53ea279c1.ico', + }, + }, + flags: { + latest: false, + triggered: false, + stuck: false, + yaml_errors: false, + retryable: true, + cancelable: true, + }, + ref: { + name: '7-5-stable', + path: '/gitlab-org/gitlab-foss/commits/7-5-stable', + tag: false, + branch: true, + }, + commit: { + id: '23433d4d8b20d7e45c103d0b6048faad38a130ab', + short_id: '23433d4d', + title: 'Version 7.5.0.rc1', + created_at: '2014-11-17T15:44:14.000+01:00', + parent_ids: ['30ac909f30f58d319b42ed1537664483894b18cd'], + message: 'Version 7.5.0.rc1\n', + author_name: 'Jacob Vosmaer', + author_email: 'contact@jacobvosmaer.nl', + authored_date: '2014-11-17T15:44:14.000+01:00', + committer_name: 'Jacob Vosmaer', + committer_email: 'contact@jacobvosmaer.nl', + committed_date: '2014-11-17T15:44:14.000+01:00', + author_gravatar_url: + 'http://www.gravatar.com/avatar/e66d11c0eedf8c07b3b18fca46599807?s=80&d=identicon', + commit_url: + 'http://localhost:3000/gitlab-org/gitlab-foss/commit/23433d4d8b20d7e45c103d0b6048faad38a130ab', + commit_path: '/gitlab-org/gitlab-foss/commit/23433d4d8b20d7e45c103d0b6048faad38a130ab', + }, + retry_path: '/gitlab-org/gitlab-foss/-/pipelines/129/retry', + cancel_path: '/gitlab-org/gitlab-foss/-/pipelines/129/cancel', + created_at: '2017-05-24T14:46:20.090Z', + updated_at: '2017-05-24T14:46:29.906Z', + }, + triggered: [ + { + id: 132, + active: true, + path: '/gitlab-org/gitlab-foss/-/pipelines/132', + project: { + name: 'GitLabCE', + }, + details: { + status: { + icon: 'status_running', + text: 'running', + label: 'running', + group: 'running', + has_details: true, + details_path: '/gitlab-org/gitlab-foss/-/pipelines/132', + favicon: + '/assets/ci_favicons/dev/favicon_status_running-c3ad2fc53ea6079c174e5b6c1351ff349e99ec3af5a5622fb77b0fe53ea279c1.ico', + }, + }, + flags: { + latest: false, + triggered: false, + stuck: false, + yaml_errors: false, + retryable: true, + cancelable: true, + }, + ref: { + name: 'crowd', + path: '/gitlab-org/gitlab-foss/commits/crowd', + tag: false, + branch: true, + }, + commit: { + id: 'b9d58c4cecd06be74c3cc32ccfb522b31544ab2e', + short_id: 'b9d58c4c', + title: 'getting user keys publically through http without any authentication, the github…', + created_at: '2013-10-03T12:50:33.000+05:30', + parent_ids: ['e219cf7246c6a0495e4507deaffeba11e79f13b8'], + message: + 'getting user keys publically through http without any authentication, the github way. E.g: http://github.com/devaroop.keys\n\nchangelog updated to include ssh key retrieval feature update\n', + author_name: 'devaroop', + author_email: 'devaroop123@yahoo.co.in', + authored_date: '2013-10-02T20:39:29.000+05:30', + committer_name: 'devaroop', + committer_email: 'devaroop123@yahoo.co.in', + committed_date: '2013-10-03T12:50:33.000+05:30', + author_gravatar_url: + 'http://www.gravatar.com/avatar/35df4b155ec66a3127d53459941cf8a2?s=80&d=identicon', + commit_url: + 'http://localhost:3000/gitlab-org/gitlab-foss/commit/b9d58c4cecd06be74c3cc32ccfb522b31544ab2e', + commit_path: '/gitlab-org/gitlab-foss/commit/b9d58c4cecd06be74c3cc32ccfb522b31544ab2e', + }, + retry_path: '/gitlab-org/gitlab-foss/-/pipelines/132/retry', + cancel_path: '/gitlab-org/gitlab-foss/-/pipelines/132/cancel', + created_at: '2017-05-24T14:46:24.644Z', + updated_at: '2017-05-24T14:48:55.226Z', + }, + { + id: 133, + active: true, + path: '/gitlab-org/gitlab-foss/-/pipelines/133', + project: { + name: 'GitLabCE', + }, + details: { + status: { + icon: 'status_running', + text: 'running', + label: 'running', + group: 'running', + has_details: true, + details_path: '/gitlab-org/gitlab-foss/-/pipelines/133', + favicon: + '/assets/ci_favicons/dev/favicon_status_running-c3ad2fc53ea6079c174e5b6c1351ff349e99ec3af5a5622fb77b0fe53ea279c1.ico', + }, + }, + flags: { + latest: false, + triggered: false, + stuck: false, + yaml_errors: false, + retryable: true, + cancelable: true, + }, + ref: { + name: 'crowd', + path: '/gitlab-org/gitlab-foss/commits/crowd', + tag: false, + branch: true, + }, + commit: { + id: 'b6bd4856a33df3d144be66c4ed1f1396009bb08b', + short_id: 'b6bd4856', + title: 'getting user keys publically through http without any authentication, the github…', + created_at: '2013-10-02T20:39:29.000+05:30', + parent_ids: ['e219cf7246c6a0495e4507deaffeba11e79f13b8'], + message: + 'getting user keys publically through http without any authentication, the github way. E.g: http://github.com/devaroop.keys\n', + author_name: 'devaroop', + author_email: 'devaroop123@yahoo.co.in', + authored_date: '2013-10-02T20:39:29.000+05:30', + committer_name: 'devaroop', + committer_email: 'devaroop123@yahoo.co.in', + committed_date: '2013-10-02T20:39:29.000+05:30', + author_gravatar_url: + 'http://www.gravatar.com/avatar/35df4b155ec66a3127d53459941cf8a2?s=80&d=identicon', + commit_url: + 'http://localhost:3000/gitlab-org/gitlab-foss/commit/b6bd4856a33df3d144be66c4ed1f1396009bb08b', + commit_path: '/gitlab-org/gitlab-foss/commit/b6bd4856a33df3d144be66c4ed1f1396009bb08b', + }, + retry_path: '/gitlab-org/gitlab-foss/-/pipelines/133/retry', + cancel_path: '/gitlab-org/gitlab-foss/-/pipelines/133/cancel', + created_at: '2017-05-24T14:46:24.648Z', + updated_at: '2017-05-24T14:48:59.673Z', + }, + { + id: 130, + active: true, + path: '/gitlab-org/gitlab-foss/-/pipelines/130', + project: { + name: 'GitLabCE', + }, + details: { + status: { + icon: 'status_running', + text: 'running', + label: 'running', + group: 'running', + has_details: true, + details_path: '/gitlab-org/gitlab-foss/-/pipelines/130', + favicon: + '/assets/ci_favicons/dev/favicon_status_running-c3ad2fc53ea6079c174e5b6c1351ff349e99ec3af5a5622fb77b0fe53ea279c1.ico', + }, + }, + flags: { + latest: false, + triggered: false, + stuck: false, + yaml_errors: false, + retryable: true, + cancelable: true, + }, + ref: { + name: 'crowd', + path: '/gitlab-org/gitlab-foss/commits/crowd', + tag: false, + branch: true, + }, + commit: { + id: '6d7ced4a2311eeff037c5575cca1868a6d3f586f', + short_id: '6d7ced4a', + title: 'Whitespace fixes to patch', + created_at: '2013-10-08T13:53:22.000-05:00', + parent_ids: ['1875141a963a4238bda29011d8f7105839485253'], + message: 'Whitespace fixes to patch\n', + author_name: 'Dale Hamel', + author_email: 'dale.hamel@srvthe.net', + authored_date: '2013-10-08T13:53:22.000-05:00', + committer_name: 'Dale Hamel', + committer_email: 'dale.hamel@invenia.ca', + committed_date: '2013-10-08T13:53:22.000-05:00', + author_gravatar_url: + 'http://www.gravatar.com/avatar/cd08930e69fa5ad1a669206e7bafe476?s=80&d=identicon', + commit_url: + 'http://localhost:3000/gitlab-org/gitlab-foss/commit/6d7ced4a2311eeff037c5575cca1868a6d3f586f', + commit_path: '/gitlab-org/gitlab-foss/commit/6d7ced4a2311eeff037c5575cca1868a6d3f586f', + }, + retry_path: '/gitlab-org/gitlab-foss/-/pipelines/130/retry', + cancel_path: '/gitlab-org/gitlab-foss/-/pipelines/130/cancel', + created_at: '2017-05-24T14:46:24.630Z', + updated_at: '2017-05-24T14:49:45.091Z', + }, + { + id: 131, + active: true, + path: '/gitlab-org/gitlab-foss/-/pipelines/132', + project: { + name: 'GitLabCE', + }, + details: { + status: { + icon: 'status_running', + text: 'running', + label: 'running', + group: 'running', + has_details: true, + details_path: '/gitlab-org/gitlab-foss/-/pipelines/132', + favicon: + '/assets/ci_favicons/dev/favicon_status_running-c3ad2fc53ea6079c174e5b6c1351ff349e99ec3af5a5622fb77b0fe53ea279c1.ico', + }, + }, + flags: { + latest: false, + triggered: false, + stuck: false, + yaml_errors: false, + retryable: true, + cancelable: true, + }, + ref: { + name: 'crowd', + path: '/gitlab-org/gitlab-foss/commits/crowd', + tag: false, + branch: true, + }, + commit: { + id: 'b9d58c4cecd06be74c3cc32ccfb522b31544ab2e', + short_id: 'b9d58c4c', + title: 'getting user keys publically through http without any authentication, the github…', + created_at: '2013-10-03T12:50:33.000+05:30', + parent_ids: ['e219cf7246c6a0495e4507deaffeba11e79f13b8'], + message: + 'getting user keys publically through http without any authentication, the github way. E.g: http://github.com/devaroop.keys\n\nchangelog updated to include ssh key retrieval feature update\n', + author_name: 'devaroop', + author_email: 'devaroop123@yahoo.co.in', + authored_date: '2013-10-02T20:39:29.000+05:30', + committer_name: 'devaroop', + committer_email: 'devaroop123@yahoo.co.in', + committed_date: '2013-10-03T12:50:33.000+05:30', + author_gravatar_url: + 'http://www.gravatar.com/avatar/35df4b155ec66a3127d53459941cf8a2?s=80&d=identicon', + commit_url: + 'http://localhost:3000/gitlab-org/gitlab-foss/commit/b9d58c4cecd06be74c3cc32ccfb522b31544ab2e', + commit_path: '/gitlab-org/gitlab-foss/commit/b9d58c4cecd06be74c3cc32ccfb522b31544ab2e', + }, + retry_path: '/gitlab-org/gitlab-foss/-/pipelines/132/retry', + cancel_path: '/gitlab-org/gitlab-foss/-/pipelines/132/cancel', + created_at: '2017-05-24T14:46:24.644Z', + updated_at: '2017-05-24T14:48:55.226Z', + }, + { + id: 134, + active: true, + path: '/gitlab-org/gitlab-foss/-/pipelines/133', + project: { + name: 'GitLabCE', + }, + details: { + status: { + icon: 'status_running', + text: 'running', + label: 'running', + group: 'running', + has_details: true, + details_path: '/gitlab-org/gitlab-foss/-/pipelines/133', + favicon: + '/assets/ci_favicons/dev/favicon_status_running-c3ad2fc53ea6079c174e5b6c1351ff349e99ec3af5a5622fb77b0fe53ea279c1.ico', + }, + }, + flags: { + latest: false, + triggered: false, + stuck: false, + yaml_errors: false, + retryable: true, + cancelable: true, + }, + ref: { + name: 'crowd', + path: '/gitlab-org/gitlab-foss/commits/crowd', + tag: false, + branch: true, + }, + commit: { + id: 'b6bd4856a33df3d144be66c4ed1f1396009bb08b', + short_id: 'b6bd4856', + title: 'getting user keys publically through http without any authentication, the github…', + created_at: '2013-10-02T20:39:29.000+05:30', + parent_ids: ['e219cf7246c6a0495e4507deaffeba11e79f13b8'], + message: + 'getting user keys publically through http without any authentication, the github way. E.g: http://github.com/devaroop.keys\n', + author_name: 'devaroop', + author_email: 'devaroop123@yahoo.co.in', + authored_date: '2013-10-02T20:39:29.000+05:30', + committer_name: 'devaroop', + committer_email: 'devaroop123@yahoo.co.in', + committed_date: '2013-10-02T20:39:29.000+05:30', + author_gravatar_url: + 'http://www.gravatar.com/avatar/35df4b155ec66a3127d53459941cf8a2?s=80&d=identicon', + commit_url: + 'http://localhost:3000/gitlab-org/gitlab-foss/commit/b6bd4856a33df3d144be66c4ed1f1396009bb08b', + commit_path: '/gitlab-org/gitlab-foss/commit/b6bd4856a33df3d144be66c4ed1f1396009bb08b', + }, + retry_path: '/gitlab-org/gitlab-foss/-/pipelines/133/retry', + cancel_path: '/gitlab-org/gitlab-foss/-/pipelines/133/cancel', + created_at: '2017-05-24T14:46:24.648Z', + updated_at: '2017-05-24T14:48:59.673Z', + }, + { + id: 135, + active: true, + path: '/gitlab-org/gitlab-foss/-/pipelines/130', + project: { + name: 'GitLabCE', + }, + details: { + status: { + icon: 'status_running', + text: 'running', + label: 'running', + group: 'running', + has_details: true, + details_path: '/gitlab-org/gitlab-foss/-/pipelines/130', + favicon: + '/assets/ci_favicons/dev/favicon_status_running-c3ad2fc53ea6079c174e5b6c1351ff349e99ec3af5a5622fb77b0fe53ea279c1.ico', + }, + }, + flags: { + latest: false, + triggered: false, + stuck: false, + yaml_errors: false, + retryable: true, + cancelable: true, + }, + ref: { + name: 'crowd', + path: '/gitlab-org/gitlab-foss/commits/crowd', + tag: false, + branch: true, + }, + commit: { + id: '6d7ced4a2311eeff037c5575cca1868a6d3f586f', + short_id: '6d7ced4a', + title: 'Whitespace fixes to patch', + created_at: '2013-10-08T13:53:22.000-05:00', + parent_ids: ['1875141a963a4238bda29011d8f7105839485253'], + message: 'Whitespace fixes to patch\n', + author_name: 'Dale Hamel', + author_email: 'dale.hamel@srvthe.net', + authored_date: '2013-10-08T13:53:22.000-05:00', + committer_name: 'Dale Hamel', + committer_email: 'dale.hamel@invenia.ca', + committed_date: '2013-10-08T13:53:22.000-05:00', + author_gravatar_url: + 'http://www.gravatar.com/avatar/cd08930e69fa5ad1a669206e7bafe476?s=80&d=identicon', + commit_url: + 'http://localhost:3000/gitlab-org/gitlab-foss/commit/6d7ced4a2311eeff037c5575cca1868a6d3f586f', + commit_path: '/gitlab-org/gitlab-foss/commit/6d7ced4a2311eeff037c5575cca1868a6d3f586f', + }, + retry_path: '/gitlab-org/gitlab-foss/-/pipelines/130/retry', + cancel_path: '/gitlab-org/gitlab-foss/-/pipelines/130/cancel', + created_at: '2017-05-24T14:46:24.630Z', + updated_at: '2017-05-24T14:49:45.091Z', + }, + ], +}; diff --git a/spec/frontend/pipelines/pipeline_mini_graph_spec.js b/spec/frontend/pipelines/pipeline_mini_graph_spec.js new file mode 100644 index 00000000000..81aa97ce13f --- /dev/null +++ b/spec/frontend/pipelines/pipeline_mini_graph_spec.js @@ -0,0 +1,149 @@ +import { mount } from '@vue/test-utils'; +import { pipelines } from 'test_fixtures/pipelines/pipelines.json'; +import PipelineMiniGraph from '~/pipelines/components/pipelines_list/pipeline_mini_graph.vue'; +import PipelineStages from '~/pipelines/components/pipelines_list/pipeline_stages.vue'; +import mockLinkedPipelines from './linked_pipelines_mock_data'; + +const mockStages = pipelines[0].details.stages; + +describe('Pipeline Mini Graph', () => { + let wrapper; + + const findPipelineMiniGraph = () => wrapper.findComponent(PipelineMiniGraph); + const findPipelineStages = () => wrapper.findComponent(PipelineStages); + + const findLinkedPipelineUpstream = () => + wrapper.findComponent('[data-testid="pipeline-mini-graph-upstream"]'); + const findLinkedPipelineDownstream = () => + wrapper.findComponent('[data-testid="pipeline-mini-graph-downstream"]'); + const findDownstreamArrowIcon = () => wrapper.find('[data-testid="downstream-arrow-icon"]'); + const findUpstreamArrowIcon = () => wrapper.find('[data-testid="upstream-arrow-icon"]'); + + const createComponent = (props = {}) => { + wrapper = mount(PipelineMiniGraph, { + propsData: { + stages: mockStages, + ...props, + }, + }); + }; + + describe('rendered state without upstream or downstream pipelines', () => { + beforeEach(() => { + createComponent(); + }); + + afterEach(() => { + wrapper.destroy(); + wrapper = null; + }); + + it('should render the pipeline stages', () => { + expect(findPipelineStages().exists()).toBe(true); + }); + + it('should have the correct props', () => { + expect(findPipelineMiniGraph().props()).toMatchObject({ + downstreamPipelines: [], + isMergeTrain: false, + pipelinePath: '', + stages: expect.any(Array), + stagesClass: '', + updateDropdown: false, + upstreamPipeline: undefined, + }); + }); + + it('should have no linked pipelines', () => { + expect(findLinkedPipelineDownstream().exists()).toBe(false); + expect(findLinkedPipelineUpstream().exists()).toBe(false); + }); + + it('should not render arrow icons', () => { + expect(findUpstreamArrowIcon().exists()).toBe(false); + expect(findDownstreamArrowIcon().exists()).toBe(false); + }); + + it('triggers events in "action request complete"', () => { + createComponent(); + + findPipelineMiniGraph(0).vm.$emit('pipelineActionRequestComplete'); + findPipelineMiniGraph(1).vm.$emit('pipelineActionRequestComplete'); + + expect(wrapper.emitted('pipelineActionRequestComplete')).toHaveLength(2); + }); + }); + + describe('rendered state with upstream pipeline', () => { + beforeEach(() => { + createComponent({ + upstreamPipeline: mockLinkedPipelines.triggered_by, + }); + }); + + afterEach(() => { + wrapper.destroy(); + wrapper = null; + }); + + it('should have the correct props', () => { + expect(findPipelineMiniGraph().props()).toMatchObject({ + downstreamPipelines: [], + isMergeTrain: false, + pipelinePath: '', + stages: expect.any(Array), + stagesClass: '', + updateDropdown: false, + upstreamPipeline: expect.any(Object), + }); + }); + + it('should render the upstream linked pipelines mini list only', () => { + expect(findLinkedPipelineUpstream().exists()).toBe(true); + expect(findLinkedPipelineDownstream().exists()).toBe(false); + }); + + it('should render an upstream arrow icon only', () => { + expect(findDownstreamArrowIcon().exists()).toBe(false); + expect(findUpstreamArrowIcon().exists()).toBe(true); + expect(findUpstreamArrowIcon().props('name')).toBe('long-arrow'); + }); + }); + + describe('rendered state with downstream pipelines', () => { + beforeEach(() => { + createComponent({ + downstreamPipelines: mockLinkedPipelines.triggered, + pipelinePath: 'my/pipeline/path', + }); + }); + + it('should have the correct props', () => { + expect(findPipelineMiniGraph().props()).toMatchObject({ + downstreamPipelines: expect.any(Array), + isMergeTrain: false, + pipelinePath: 'my/pipeline/path', + stages: expect.any(Array), + stagesClass: '', + updateDropdown: false, + upstreamPipeline: undefined, + }); + }); + + afterEach(() => { + wrapper.destroy(); + wrapper = null; + }); + + it('should render the downstream linked pipelines mini list only', () => { + expect(findLinkedPipelineDownstream().exists()).toBe(true); + expect(findLinkedPipelineUpstream().exists()).toBe(false); + }); + + it('should render a downstream arrow icon only', () => { + expect(findUpstreamArrowIcon().exists()).toBe(false); + expect(findDownstreamArrowIcon().exists()).toBe(true); + expect(findDownstreamArrowIcon().props('name')).toBe('long-arrow'); + }); + }); +}); diff --git a/spec/frontend/pipelines/pipelines_table_spec.js b/spec/frontend/pipelines/pipelines_table_spec.js index 7b49baa5a20..07818b9dadb 100644 --- a/spec/frontend/pipelines/pipelines_table_spec.js +++ b/spec/frontend/pipelines/pipelines_table_spec.js @@ -113,40 +113,28 @@ describe('Pipelines Table', () => { }); describe('stages cell', () => { - it('should render a pipeline mini graph', () => { + it('should render pipeline mini graph', () => { expect(findPipelineMiniGraph().exists()).toBe(true); }); it('should render the right number of stages', () => { const stagesLength = pipeline.details.stages.length; - expect( - findPipelineMiniGraph().findAll('[data-testid="mini-pipeline-graph-dropdown"]'), - ).toHaveLength(stagesLength); + expect(findPipelineMiniGraph().props('stages').length).toBe(stagesLength); }); describe('when pipeline does not have stages', () => { beforeEach(() => { pipeline = createMockPipeline(); - pipeline.details.stages = null; + pipeline.details.stages = []; createComponent({ pipelines: [pipeline] }); }); it('stages are not rendered', () => { - expect(findPipelineMiniGraph().exists()).toBe(false); + expect(findPipelineMiniGraph().props('stages')).toHaveLength(0); }); }); - it('should not update dropdown', () => { - expect(findPipelineMiniGraph().props('updateDropdown')).toBe(false); - }); - - it('when update graph dropdown is set, should update graph dropdown', () => { - createComponent({ pipelines: [pipeline], updateGraphDropdown: true }); - - expect(findPipelineMiniGraph().props('updateDropdown')).toBe(true); - }); - it('when action request is complete, should refresh table', () => { findPipelineMiniGraph().vm.$emit('pipelineActionRequestComplete'); diff --git a/spec/frontend/vue_mr_widget/components/mr_widget_pipeline_spec.js b/spec/frontend/vue_mr_widget/components/mr_widget_pipeline_spec.js index 6347e3c3be3..a32f61c4567 100644 --- a/spec/frontend/vue_mr_widget/components/mr_widget_pipeline_spec.js +++ b/spec/frontend/vue_mr_widget/components/mr_widget_pipeline_spec.js @@ -4,9 +4,8 @@ import axios from 'axios'; import MockAdapter from 'axios-mock-adapter'; import { trimText } from 'helpers/text_helper'; import { extendedWrapper } from 'helpers/vue_test_utils_helper'; +import MRWidgetPipelineComponent from '~/vue_merge_request_widget/components/mr_widget_pipeline.vue'; import PipelineMiniGraph from '~/pipelines/components/pipelines_list/pipeline_mini_graph.vue'; -import PipelineStage from '~/pipelines/components/pipelines_list/pipeline_stage.vue'; -import PipelineComponent from '~/vue_merge_request_widget/components/mr_widget_pipeline.vue'; import { SUCCESS } from '~/vue_merge_request_widget/constants'; import mockData from '../mock_data'; @@ -30,14 +29,13 @@ describe('MRWidgetPipeline', () => { const findPipelineInfoContainer = () => wrapper.findByTestId('pipeline-info-container'); const findCommitLink = () => wrapper.findByTestId('commit-link'); const findPipelineFinishedAt = () => wrapper.findByTestId('finished-at'); - const findPipelineMiniGraph = () => wrapper.findComponent(PipelineMiniGraph); - const findAllPipelineStages = () => wrapper.findAllComponents(PipelineStage); const findPipelineCoverage = () => wrapper.findByTestId('pipeline-coverage'); const findPipelineCoverageDelta = () => wrapper.findByTestId('pipeline-coverage-delta'); const findPipelineCoverageTooltipText = () => wrapper.findByTestId('pipeline-coverage-tooltip').text(); const findPipelineCoverageDeltaTooltipText = () => wrapper.findByTestId('pipeline-coverage-delta-tooltip').text(); + const findPipelineMiniGraph = () => wrapper.findComponent(PipelineMiniGraph); const findMonitoringPipelineMessage = () => wrapper.findByTestId('monitoring-pipeline-message'); const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon); @@ -45,7 +43,7 @@ describe('MRWidgetPipeline', () => { const createWrapper = (props = {}, mountFn = shallowMount) => { wrapper = extendedWrapper( - mountFn(PipelineComponent, { + mountFn(MRWidgetPipelineComponent, { propsData: { ...defaultProps, ...props, @@ -106,8 +104,10 @@ describe('MRWidgetPipeline', () => { }); it('should render pipeline graph', () => { + const stagesCount = mockData.pipeline.details.stages.length; + expect(findPipelineMiniGraph().exists()).toBe(true); - expect(findAllPipelineStages()).toHaveLength(mockData.pipeline.details.stages.length); + expect(findPipelineMiniGraph().props('stages')).toHaveLength(stagesCount); }); describe('should render pipeline coverage information', () => { @@ -176,15 +176,11 @@ describe('MRWidgetPipeline', () => { expect(findPipelineInfoContainer().text()).toMatch(mockData.pipeline.details.status.label); }); - it('should render pipeline graph with correct styles', () => { + it('should render pipeline graph', () => { const stagesCount = mockData.pipeline.details.stages.length; expect(findPipelineMiniGraph().exists()).toBe(true); - expect(findPipelineMiniGraph().findAll('.mr-widget-pipeline-stages')).toHaveLength( - stagesCount, - ); - - expect(findAllPipelineStages()).toHaveLength(stagesCount); + expect(findPipelineMiniGraph().props('stages')).toHaveLength(stagesCount); }); it('should render coverage information', () => { diff --git a/spec/support/finder_collection.rb b/spec/support/finder_collection.rb new file mode 100644 index 00000000000..494dd4bdca1 --- /dev/null +++ b/spec/support/finder_collection.rb @@ -0,0 +1,48 @@ +# frozen_string_literal: true + +require 'set' + +module Support + # Ensure that finders' `execute` method always returns + # `ActiveRecord::Relation`. + # + # See https://gitlab.com/gitlab-org/gitlab/-/issues/298771 + module FinderCollection + def self.install_check(finder_class) + return unless check?(finder_class) + + finder_class.prepend CheckResult + end + + ALLOWLIST_YAML = File.join(__dir__, 'finder_collection_allowlist.yml') + + def self.check?(finder_class) + @allowlist ||= YAML.load_file(ALLOWLIST_YAML).to_set + + @allowlist.exclude?(finder_class.name) + end + + module CheckResult + def execute(...) + result = super + + unless result.is_a?(ActiveRecord::Relation) + raise <<~MESSAGE + #{self.class}#execute returned `#{result.class}` instead of `ActiveRecord::Relation`. + All finder classes are expected to return `ActiveRecord::Relation`. + + Read more at https://docs.gitlab.com/ee/development/reusing_abstractions.html#finders + MESSAGE + end + + result + end + end + end +end + +RSpec.configure do |config| + config.before(:all, type: :finder) do + Support::FinderCollection.install_check(described_class) + end +end diff --git a/spec/support/finder_collection_allowlist.yml b/spec/support/finder_collection_allowlist.yml new file mode 100644 index 00000000000..8f09153afec --- /dev/null +++ b/spec/support/finder_collection_allowlist.yml @@ -0,0 +1,66 @@ +# Allow list for spec/support/finder_collection.rb + +# Permenant excludes +# For example: +# FooFinder # Reason: It uses a memory backend + +# Temporary excludes (aka TODOs) +# For example: +# BarFinder # See <ISSUE_URL> +- AccessRequestsFinder +- Admin::PlansFinder +- Analytics::CycleAnalytics::StageFinder +- ApplicationsFinder +- Autocomplete::GroupFinder +- Autocomplete::ProjectFinder +- Autocomplete::UsersFinder +- BilledUsersFinder +- Boards::BoardsFinder +- Boards::VisitsFinder +- BranchesFinder +- Ci::AuthJobFinder +- Ci::CommitStatusesFinder +- Ci::DailyBuildGroupReportResultsFinder +- ClusterAncestorsFinder +- Clusters::AgentAuthorizationsFinder +- Clusters::KubernetesNamespaceFinder +- ComplianceManagement::MergeRequests::ComplianceViolationsFinder +- ContainerRepositoriesFinder +- ContextCommitsFinder +- Environments::EnvironmentNamesFinder +- Environments::EnvironmentsByDeploymentsFinder +- EventsFinder +- GroupDescendantsFinder +- Groups::ProjectsRequiringAuthorizationsRefresh::OnDirectMembershipFinder +- Groups::ProjectsRequiringAuthorizationsRefresh::OnTransferFinder +- KeysFinder +- LfsPointersFinder +- LicenseTemplateFinder +- MergeRequests::OldestPerCommitFinder +- NotesFinder +- Packages::BuildInfosFinder +- Packages::Conan::PackageFileFinder +- Packages::Go::ModuleFinder +- Packages::Go::PackageFinder +- Packages::Go::VersionFinder +- Packages::PackageFileFinder +- Packages::PackageFinder +- Packages::Pypi::PackageFinder +- Projects::Integrations::Jira::ByIdsFinder +- Projects::Integrations::Jira::IssuesFinder +- Releases::EvidencePipelineFinder +- Repositories::BranchNamesFinder +- Repositories::ChangelogTagFinder +- Repositories::TreeFinder +- Security::FindingsFinder +- Security::PipelineVulnerabilitiesFinder +- Security::ScanExecutionPoliciesFinder +- Security::TrainingProviders::BaseUrlFinder +- Security::TrainingUrlsFinder +- SentryIssueFinder +- ServerlessDomainFinder +- TagsFinder +- TemplateFinder +- UploaderFinder +- UserGroupNotificationSettingsFinder +- UserGroupsCounter |