diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-08-18 21:09:57 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-08-18 21:09:57 +0000 |
commit | bfce95a4c5e9d71ed523f48f3fb901d2b7af60f7 (patch) | |
tree | 6bada22ff3863edec03f928f8edcf19c6e7107f1 | |
parent | 85f7fa54f404f28b0f351c2be0f7a6e9d74fe65f (diff) | |
download | gitlab-ce-bfce95a4c5e9d71ed523f48f3fb901d2b7af60f7.tar.gz |
Add latest changes from gitlab-org/gitlab@master
107 files changed, 1834 insertions, 901 deletions
diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION index b0d59f532f4..3d4bf7f0ea0 100644 --- a/GITALY_SERVER_VERSION +++ b/GITALY_SERVER_VERSION @@ -1 +1 @@ -397a8aa41c8b1b159a667fb262aebc644719e074 +14370f86aaddaf173ff205148704eab118d8d181 diff --git a/app/assets/javascripts/badges/components/badge_settings.vue b/app/assets/javascripts/badges/components/badge_settings.vue index 531f84ad272..dc107cb44a1 100644 --- a/app/assets/javascripts/badges/components/badge_settings.vue +++ b/app/assets/javascripts/badges/components/badge_settings.vue @@ -6,6 +6,7 @@ import DeprecatedModal2 from '~/vue_shared/components/deprecated_modal_2.vue'; import Badge from './badge.vue'; import BadgeForm from './badge_form.vue'; import BadgeList from './badge_list.vue'; +import { GlSprintf } from '@gitlab/ui'; export default { name: 'BadgeSettings', @@ -14,14 +15,15 @@ export default { BadgeForm, BadgeList, GlModal: DeprecatedModal2, + GlSprintf, + }, + i18n: { + deleteModalText: s__( + 'Badges|You are going to delete this badge. Deleted badges %{strongStart}cannot%{strongEnd} be restored.', + ), }, computed: { ...mapState(['badgeInModal', 'isEditing']), - deleteModalText() { - return s__( - 'Badges|You are going to delete this badge. Deleted badges <strong>cannot</strong> be restored.', - ); - }, }, methods: { ...mapActions(['deleteBadge']), @@ -54,7 +56,13 @@ export default { :link-url="badgeInModal ? badgeInModal.renderedLinkUrl : ''" /> </div> - <p v-html="deleteModalText"></p> + <p> + <gl-sprintf :message="$options.i18n.deleteModalText"> + <template #strong="{ content }"> + <strong>{{ content }}</strong> + </template> + </gl-sprintf> + </p> </gl-modal> <badge-form v-show="isEditing" :is-editing="true" /> diff --git a/app/assets/javascripts/clusters/components/remove_cluster_confirmation.vue b/app/assets/javascripts/clusters/components/remove_cluster_confirmation.vue index 4c4a728591b..3e3b102f0aa 100644 --- a/app/assets/javascripts/clusters/components/remove_cluster_confirmation.vue +++ b/app/assets/javascripts/clusters/components/remove_cluster_confirmation.vue @@ -1,6 +1,6 @@ <script> import { escape } from 'lodash'; -import { GlModal, GlButton, GlDeprecatedButton, GlFormInput } from '@gitlab/ui'; +import { GlModal, GlButton, GlDeprecatedButton, GlFormInput, GlSprintf } from '@gitlab/ui'; import SplitButton from '~/vue_shared/components/split_button.vue'; import { s__, sprintf } from '~/locale'; import csrf from '~/lib/utils/csrf'; @@ -30,6 +30,7 @@ export default { GlButton, GlDeprecatedButton, GlFormInput, + GlSprintf, }, props: { clusterPath: { @@ -67,18 +68,6 @@ export default { ) : s__('ClusterIntegration|You are about to remove your cluster integration.'); }, - warningToBeRemoved() { - return s__(`ClusterIntegration| - This will permanently delete the following resources: - <ul> - <li>All installed applications and related resources</li> - <li>The <code>gitlab-managed-apps</code> namespace</li> - <li>Any project namespaces</li> - <li><code>clusterroles</code></li> - <li><code>clusterrolebindings</code></li> - </ul> - `); - }, confirmationTextLabel() { return sprintf( this.confirmCleanup @@ -144,7 +133,27 @@ export default { > <template> <p>{{ warningMessage }}</p> - <div v-if="confirmCleanup" v-html="warningToBeRemoved"></div> + <div v-if="confirmCleanup"> + {{ s__('ClusterIntegration|This will permanently delete the following resources:') }} + <ul> + <li> + {{ s__('ClusterIntegration|All installed applications and related resources') }} + </li> + <li> + <gl-sprintf :message="s__('ClusterIntegration|The %{gitlabNamespace} namespace')"> + <template #gitlabNamespace> + <!-- eslint-disable-next-line @gitlab/vue-require-i18n-strings --> + <code>{{ 'gitlab-managed-apps' }}</code> + </template> + </gl-sprintf> + </li> + <li>{{ s__('ClusterIntegration|Any project namespaces') }}</li> + <!-- eslint-disable @gitlab/vue-require-i18n-strings --> + <li><code>clusterroles</code></li> + <li><code>clusterrolebindings</code></li> + <!-- eslint-enable @gitlab/vue-require-i18n-strings --> + </ul> + </div> <strong v-html="confirmationTextLabel"></strong> <form ref="form" :action="clusterPath" method="post" class="gl-mb-5"> <input ref="method" type="hidden" name="_method" value="delete" /> diff --git a/app/assets/javascripts/create_cluster/gke_cluster/components/gke_project_id_dropdown.vue b/app/assets/javascripts/create_cluster/gke_cluster/components/gke_project_id_dropdown.vue index b0bec10f64d..88d04f9ed45 100644 --- a/app/assets/javascripts/create_cluster/gke_cluster/components/gke_project_id_dropdown.vue +++ b/app/assets/javascripts/create_cluster/gke_cluster/components/gke_project_id_dropdown.vue @@ -1,12 +1,16 @@ <script> -import { escape } from 'lodash'; import { mapState, mapGetters, mapActions } from 'vuex'; -import { s__, sprintf } from '~/locale'; +import { s__ } from '~/locale'; +import { GlSprintf, GlLink } from '@gitlab/ui'; import gkeDropdownMixin from './gke_dropdown_mixin'; export default { name: 'GkeProjectIdDropdown', + components: { + GlSprintf, + GlLink, + }, mixins: [gkeDropdownMixin], props: { docsUrl: { @@ -46,31 +50,23 @@ export default { return s__('ClusterIntegration|Select project'); }, helpText() { - let message; if (this.hasErrors) { return this.errorMessage; } if (!this.items) { - message = - 'ClusterIntegration|We were unable to fetch any projects. Ensure that you have a project on %{docsLinkStart}Google Cloud Platform%{docsLinkEnd}.'; + return s__( + 'ClusterIntegration|We were unable to fetch any projects. Ensure that you have a project on %{docsLinkStart}Google Cloud Platform%{docsLinkEnd}.', + ); } - message = - this.items && this.items.length - ? 'ClusterIntegration|To use a new project, first create one on %{docsLinkStart}Google Cloud Platform%{docsLinkEnd}.' - : 'ClusterIntegration|To create a cluster, first create a project on %{docsLinkStart}Google Cloud Platform%{docsLinkEnd}.'; - - return sprintf( - s__(message), - { - docsLinkEnd: ' <i class="fa fa-external-link" aria-hidden="true"></i></a>', - docsLinkStart: `<a href="${escape( - this.docsUrl, - )}" target="_blank" rel="noopener noreferrer">`, - }, - false, - ); + return this.items.length + ? s__( + 'ClusterIntegration|To use a new project, first create one on %{docsLinkStart}Google Cloud Platform%{docsLinkEnd}.', + ) + : s__( + 'ClusterIntegration|To create a cluster, first create a project on %{docsLinkStart}Google Cloud Platform%{docsLinkEnd}.', + ); }, errorMessage() { if (!this.projectHasBillingEnabled) { @@ -80,21 +76,13 @@ export default { ); } - return sprintf( - s__( - 'This project does not have billing enabled. To create a cluster, <a href=%{linkToBilling} target="_blank" rel="noopener noreferrer">enable billing <i class="fa fa-external-link" aria-hidden="true"></i></a> and try again.', - ), - { - linkToBilling: - 'https://console.cloud.google.com/freetrial?utm_campaign=2018_cpanel&utm_source=gitlab&utm_medium=referral', - }, - false, + return s__( + 'ClusterIntegration|This project does not have billing enabled. To create a cluster, %{linkToBillingStart}enable billing%{linkToBillingEnd} and try again.', ); } - return sprintf( - s__('ClusterIntegration|An error occurred while trying to fetch your projects: %{error}'), - { error: this.gapiError }, + return s__( + 'ClusterIntegration|An error occurred while trying to fetch your projects: %{error}', ); }, }, @@ -182,7 +170,28 @@ export default { 'text-muted': !hasErrors, }" class="form-text" - v-html="helpText" - ></span> + > + <gl-sprintf :message="helpText"> + <template #linkToBilling="{ content }"> + <gl-link + :href=" + 'https://console.cloud.google.com/freetrial?utm_campaign=2018_cpanel&utm_source=gitlab&utm_medium=referral' + " + target="_blank" + >{{ content }} <i class="fa fa-external-link" aria-hidden="true"></i + ></gl-link> + </template> + + <template #docsLink="{ content }"> + <gl-link :href="docsUrl" target="_blank" + >{{ content }} <i class="fa fa-external-link" aria-hidden="true"></i + ></gl-link> + </template> + + <template #error> + {{ gapiError }} + </template> + </gl-sprintf> + </span> </div> </template> diff --git a/app/assets/javascripts/ide/stores/modules/terminal/messages.js b/app/assets/javascripts/ide/stores/modules/terminal/messages.js index 38c5a8a28d8..bf35ce0f0bc 100644 --- a/app/assets/javascripts/ide/stores/modules/terminal/messages.js +++ b/app/assets/javascripts/ide/stores/modules/terminal/messages.js @@ -21,7 +21,7 @@ export const EMPTY_RUNNERS = __( 'Configure GitLab runners to start using the Web Terminal. %{helpStart}Learn more.%{helpEnd}', ); export const ERROR_CONFIG = __( - 'Configure a <code>.gitlab-webide.yml</code> file in the <code>.gitlab</code> directory to start using the Web Terminal. %{helpStart}Learn more.%{helpEnd}', + 'Configure a %{codeStart}.gitlab-webide.yml%{codeEnd} file in the %{codeStart}.gitlab%{codeEnd} directory to start using the Web Terminal. %{helpStart}Learn more.%{helpEnd}', ); export const ERROR_PERMISSION = __( 'You do not have permission to run the Web Terminal. Please contact a project administrator.', @@ -34,6 +34,8 @@ export const configCheckError = (status, helpUrl) => { { helpStart: `<a href="${escape(helpUrl)}" target="_blank">`, helpEnd: '</a>', + codeStart: '<code>', + codeEnd: '</code>', }, false, ); diff --git a/app/assets/javascripts/lib/utils/keys.js b/app/assets/javascripts/lib/utils/keys.js index b7ba6d3a071..2a8b1759e54 100644 --- a/app/assets/javascripts/lib/utils/keys.js +++ b/app/assets/javascripts/lib/utils/keys.js @@ -1,5 +1,2 @@ -/* eslint-disable @gitlab/require-i18n-strings */ - export const ESC_KEY = 'Escape'; -export const ESC_KEY_IE11 = 'Esc'; // https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key export const ENTER_KEY = 'Enter'; diff --git a/app/assets/javascripts/monitoring/components/alert_widget_form.vue b/app/assets/javascripts/monitoring/components/alert_widget_form.vue index f961e21984b..5fa0da53a04 100644 --- a/app/assets/javascripts/monitoring/components/alert_widget_form.vue +++ b/app/assets/javascripts/monitoring/components/alert_widget_form.vue @@ -7,8 +7,8 @@ import { GlButtonGroup, GlFormGroup, GlFormInput, - GlDeprecatedDropdown, - GlDeprecatedDropdownItem, + GlNewDropdown as GlDropdown, + GlNewDropdownItem as GlDropdownItem, GlModal, GlTooltipDirective, } from '@gitlab/ui'; @@ -40,8 +40,8 @@ export default { GlButtonGroup, GlFormGroup, GlFormInput, - GlDeprecatedDropdown, - GlDeprecatedDropdownItem, + GlDropdown, + GlDropdownItem, GlModal, GlLink, Icon, @@ -251,20 +251,20 @@ export default { </template> </gl-form-group> <gl-form-group v-else label-for="alert-query-dropdown" :label="$options.alertQueryText.label"> - <gl-deprecated-dropdown + <gl-dropdown id="alert-query-dropdown" :text="queryDropdownLabel" - toggle-class="dropdown-menu-toggle qa-alert-query-dropdown" + toggle-class="dropdown-menu-toggle gl-border-1! qa-alert-query-dropdown" > - <gl-deprecated-dropdown-item + <gl-dropdown-item v-for="query in relevantQueries" :key="query.metricId" data-qa-selector="alert_query_option" @click="selectQuery(query.metricId)" > {{ query.label }} - </gl-deprecated-dropdown-item> - </gl-deprecated-dropdown> + </gl-dropdown-item> + </gl-dropdown> </gl-form-group> <gl-button-group class="mb-3" :label="s__('PrometheusAlerts|Operator')"> <gl-deprecated-button diff --git a/app/assets/javascripts/monitoring/components/dashboard.vue b/app/assets/javascripts/monitoring/components/dashboard.vue index ecbce9471bf..a56470dd893 100644 --- a/app/assets/javascripts/monitoring/components/dashboard.vue +++ b/app/assets/javascripts/monitoring/components/dashboard.vue @@ -7,7 +7,7 @@ import DashboardHeader from './dashboard_header.vue'; import DashboardPanel from './dashboard_panel.vue'; import { s__ } from '~/locale'; import createFlash from '~/flash'; -import { ESC_KEY, ESC_KEY_IE11 } from '~/lib/utils/keys'; +import { ESC_KEY } from '~/lib/utils/keys'; import { mergeUrlParams, updateHistory } from '~/lib/utils/url_utility'; import invalidUrl from '~/lib/utils/invalid_url'; import Icon from '~/vue_shared/components/icon.vue'; @@ -311,7 +311,7 @@ export default { }, onKeyup(event) { const { key } = event; - if (key === ESC_KEY || key === ESC_KEY_IE11) { + if (key === ESC_KEY) { this.clearExpandedPanel(); } }, diff --git a/app/assets/javascripts/notes/components/note_header.vue b/app/assets/javascripts/notes/components/note_header.vue index 81812ee2279..9ded5ab648e 100644 --- a/app/assets/javascripts/notes/components/note_header.vue +++ b/app/assets/javascripts/notes/components/note_header.vue @@ -191,7 +191,7 @@ export default { name="eye-slash" :size="14" :title="s__('Notes|Private comments are accessible by internal staff only')" - class="gl-ml-1 gl-text-gray-800 align-middle" + class="gl-ml-1 gl-text-gray-700 align-middle" /> <slot name="extra-controls"></slot> <i diff --git a/app/assets/javascripts/onboarding_issues/index.js b/app/assets/javascripts/onboarding_issues/index.js index 5a6f952ffdf..27f2f7f0e9d 100644 --- a/app/assets/javascripts/onboarding_issues/index.js +++ b/app/assets/javascripts/onboarding_issues/index.js @@ -1,6 +1,6 @@ import $ from 'jquery'; import { parseBoolean, getCookie, setCookie, removeCookie } from '~/lib/utils/common_utils'; -import { __ } from '~/locale'; +import { __, sprintf } from '~/locale'; import Tracking from '~/tracking'; const COOKIE_NAME = 'onboarding_issues_settings'; @@ -94,8 +94,12 @@ export const showLearnGitLabProjectPopover = () => { if (!el) return; const options = { - content: __( - 'Go to <strong>Issues</strong> > <strong>Boards</strong> to access your personalized learning issue board.', + content: sprintf( + __( + 'Go to %{strongStart}Issues%{strongEnd} > %{strongStart}Boards%{strongEnd} to access your personalized learning issue board.', + ), + { strongStart: '<strong>', strongEnd: '</strong>' }, + false, ), }; @@ -111,8 +115,12 @@ export const showLearnGitLabIssuesPopover = () => { if (!el) return; const options = { - content: __( - 'Go to <strong>Issues</strong> > <strong>Boards</strong> to access your personalized learning issue board.', + content: sprintf( + __( + 'Go to %{strongStart}Issues%{strongEnd} > %{strongStart}Boards%{strongEnd} to access your personalized learning issue board.', + ), + { strongStart: '<strong>', strongEnd: '</strong>' }, + false, ), }; diff --git a/app/assets/javascripts/pages/dashboard/projects/index/components/customize_homepage_banner.vue b/app/assets/javascripts/pages/dashboard/projects/index/components/customize_homepage_banner.vue new file mode 100644 index 00000000000..6b907f31a37 --- /dev/null +++ b/app/assets/javascripts/pages/dashboard/projects/index/components/customize_homepage_banner.vue @@ -0,0 +1,66 @@ +<script> +import { GlBanner } from '@gitlab/ui'; +import { s__ } from '~/locale'; +import axios from '~/lib/utils/axios_utils'; + +export default { + components: { + GlBanner, + }, + inject: { + svgPath: { + default: '', + }, + preferencesBehaviorPath: { + default: '', + }, + calloutsPath: { + default: '', + }, + calloutsFeatureId: { + default: '', + }, + }, + i18n: { + title: s__('CustomizeHomepageBanner|Do you want to customize this page?'), + body: s__( + 'CustomizeHomepageBanner|This page shows a list of your projects by default but it can be changed to show projects\' activity, groups, your to-do list, assigned issues, assigned merge requests, and more. You can change this under "Homepage content" in your preferences', + ), + button_text: s__('CustomizeHomepageBanner|Go to preferences'), + }, + data() { + return { + visible: true, + }; + }, + methods: { + handleClose() { + axios + .post(this.calloutsPath, { + feature_name: this.calloutsFeatureId, + }) + .catch(e => { + // eslint-disable-next-line @gitlab/require-i18n-strings, no-console + console.error('Failed to dismiss banner.', e); + }); + + this.visible = false; + }, + }, +}; +</script> + +<template> + <gl-banner + v-if="visible" + :title="$options.i18n.title" + :button-text="$options.i18n.button_text" + :button-link="preferencesBehaviorPath" + :svg-path="svgPath" + @close="handleClose" + > + <p> + {{ $options.i18n.body }} + </p> + </gl-banner> +</template> diff --git a/app/assets/javascripts/pages/dashboard/projects/index.js b/app/assets/javascripts/pages/dashboard/projects/index/index.js index 01001d4f3ff..b3c95f4ac1f 100644 --- a/app/assets/javascripts/pages/dashboard/projects/index.js +++ b/app/assets/javascripts/pages/dashboard/projects/index/index.js @@ -1,5 +1,8 @@ import ProjectsList from '~/projects_list'; +import initCustomizeHomepageBanner from './init_customize_homepage_banner'; document.addEventListener('DOMContentLoaded', () => { new ProjectsList(); // eslint-disable-line no-new + + initCustomizeHomepageBanner(); }); diff --git a/app/assets/javascripts/pages/dashboard/projects/index/init_customize_homepage_banner.js b/app/assets/javascripts/pages/dashboard/projects/index/init_customize_homepage_banner.js new file mode 100644 index 00000000000..c0735dde1da --- /dev/null +++ b/app/assets/javascripts/pages/dashboard/projects/index/init_customize_homepage_banner.js @@ -0,0 +1,16 @@ +import Vue from 'vue'; +import CustomizeHomepageBanner from './components/customize_homepage_banner.vue'; + +export default () => { + const el = document.querySelector('.js-customize-homepage-banner'); + + if (!el) { + return false; + } + + return new Vue({ + el, + provide: { ...el.dataset }, + render: createElement => createElement(CustomizeHomepageBanner), + }); +}; diff --git a/app/assets/javascripts/pages/projects/labels/components/promote_label_modal.vue b/app/assets/javascripts/pages/projects/labels/components/promote_label_modal.vue index 08078fa6b62..385da528d89 100644 --- a/app/assets/javascripts/pages/projects/labels/components/promote_label_modal.vue +++ b/app/assets/javascripts/pages/projects/labels/components/promote_label_modal.vue @@ -1,15 +1,16 @@ <script> -import { escape } from 'lodash'; import axios from '~/lib/utils/axios_utils'; import createFlash from '~/flash'; import DeprecatedModal2 from '~/vue_shared/components/deprecated_modal_2.vue'; import { s__, sprintf } from '~/locale'; import { visitUrl } from '~/lib/utils/url_utility'; import eventHub from '../event_hub'; +import { GlSprintf } from '@gitlab/ui'; export default { components: { GlModal: DeprecatedModal2, + GlSprintf, }, props: { url: { @@ -45,20 +46,6 @@ export default { }, ); }, - title() { - const label = `<span - class="label color-label" - style="background-color: ${this.labelColor}; color: ${this.labelTextColor};" - >${escape(this.labelTitle)}</span>`; - - return sprintf( - s__('Labels|<span>Promote label</span> %{labelTitle} <span>to Group Label?</span>'), - { - labelTitle: label, - }, - false, - ); - }, }, methods: { onSubmit() { @@ -90,7 +77,27 @@ export default { footer-primary-button-variant="warning" @submit="onSubmit" > - <div slot="title" class="modal-title-with-label" v-html="title"></div> + <div slot="title" class="modal-title-with-label"> + <gl-sprintf + :message=" + s__( + 'Labels|%{spanStart}Promote label%{spanEnd} %{labelTitle} %{spanStart}to Group Label?%{spanEnd}', + ) + " + > + <template #labelTitle> + <span + class="label color-label" + :style="`background-color: ${labelColor}; color: ${labelTextColor};`" + > + {{ labelTitle }} + </span> + </template> + <template #span="{ content }" + ><span>{{ content }}</span></template + > + </gl-sprintf> + </div> {{ text }} </gl-modal> diff --git a/app/assets/javascripts/prometheus_metrics/prometheus_metrics.js b/app/assets/javascripts/prometheus_metrics/prometheus_metrics.js index 59d47ae4155..7fc1b18bf71 100644 --- a/app/assets/javascripts/prometheus_metrics/prometheus_metrics.js +++ b/app/assets/javascripts/prometheus_metrics/prometheus_metrics.js @@ -87,16 +87,15 @@ export default class PrometheusMetrics { if (totalMonitoredMetrics === 0) { const emptyCommonMetricsText = sprintf( - s__( - 'PrometheusService|<p class="text-tertiary">No <a href="%{docsUrl}">common metrics</a> were found</p>', - ), + s__('PrometheusService|No %{docsUrlStart}common metrics%{docsUrlEnd} were found'), { - docsUrl: this.helpMetricsPath, + docsUrlStart: `<a href="${this.helpMetricsPath}">`, + docsUrlEnd: '</a>', }, false, ); this.$monitoredMetricsEmpty.empty(); - this.$monitoredMetricsEmpty.append(emptyCommonMetricsText); + this.$monitoredMetricsEmpty.append(`<p class="text-tertiary">${emptyCommonMetricsText}</p>`); this.showMonitoringMetricsPanelState(PANEL_STATE.EMPTY); } else { const metricsCountText = sprintf( diff --git a/app/assets/javascripts/sidebar/components/confidential/edit_form.vue b/app/assets/javascripts/sidebar/components/confidential/edit_form.vue index 6dad68800f9..0a542f03fcb 100644 --- a/app/assets/javascripts/sidebar/components/confidential/edit_form.vue +++ b/app/assets/javascripts/sidebar/components/confidential/edit_form.vue @@ -1,10 +1,12 @@ <script> import editFormButtons from './edit_form_buttons.vue'; -import { __, sprintf } from '../../../locale'; +import { __ } from '../../../locale'; +import { GlSprintf } from '@gitlab/ui'; export default { components: { editFormButtons, + GlSprintf, }, props: { confidential: { @@ -22,25 +24,13 @@ export default { }, computed: { confidentialityOnWarning() { - const accessLevel = __('at least Reporter access'); - - return sprintf( - __( - 'You are going to turn on the confidentiality. This means that only team members with %{accessLevel} are able to see and leave comments on the %{issuableType}.', - ), - { issuableType: this.issuableType, accessLevel: `<strong>${accessLevel}</strong>` }, - false, + return __( + 'You are going to turn on the confidentiality. This means that only team members with %{strongStart}at least Reporter access%{strongEnd} are able to see and leave comments on the %{issuableType}.', ); }, confidentialityOffWarning() { - const accessLevel = __('everyone'); - - return sprintf( - __( - 'You are going to turn off the confidentiality. This means %{accessLevel} will be able to see and leave a comment on this %{issuableType}.', - ), - { issuableType: this.issuableType, accessLevel: `<strong>${accessLevel}</strong>` }, - false, + return __( + 'You are going to turn off the confidentiality. This means %{strongStart}everyone%{strongEnd} will be able to see and leave a comment on this %{issuableType}.', ); }, }, @@ -51,8 +41,22 @@ export default { <div class="dropdown show"> <div class="dropdown-menu sidebar-item-warning-message"> <div> - <p v-if="!confidential" v-html="confidentialityOnWarning"></p> - <p v-else v-html="confidentialityOffWarning"></p> + <p v-if="!confidential"> + <gl-sprintf :message="confidentialityOnWarning"> + <template #strong="{ content }"> + <strong>{{ content }}</strong> + </template> + <template #issuableType>{{ issuableType }}</template> + </gl-sprintf> + </p> + <p v-else> + <gl-sprintf :message="confidentialityOffWarning"> + <template #strong="{ content }"> + <strong>{{ content }}</strong> + </template> + <template #issuableType>{{ issuableType }}</template> + </gl-sprintf> + </p> <edit-form-buttons :full-path="fullPath" :confidential="confidential" /> </div> </div> diff --git a/app/assets/javascripts/sidebar/components/lock/edit_form.vue b/app/assets/javascripts/sidebar/components/lock/edit_form.vue index 9175bc13385..49bf28147be 100644 --- a/app/assets/javascripts/sidebar/components/lock/edit_form.vue +++ b/app/assets/javascripts/sidebar/components/lock/edit_form.vue @@ -1,10 +1,11 @@ <script> import editFormButtons from './edit_form_buttons.vue'; -import { __, sprintf } from '../../../locale'; +import { GlSprintf } from '@gitlab/ui'; export default { components: { editFormButtons, + GlSprintf, }, props: { isLocked: { @@ -16,33 +17,41 @@ export default { type: String, }, }, - computed: { - lockWarning() { - return sprintf( - __( - 'Lock this %{issuableDisplayName}? Only <strong>project members</strong> will be able to comment.', - ), - { issuableDisplayName: this.issuableDisplayName }, - ); - }, - unlockWarning() { - return sprintf( - __( - 'Unlock this %{issuableDisplayName}? <strong>Everyone</strong> will be able to comment.', - ), - { issuableDisplayName: this.issuableDisplayName }, - ); - }, - }, }; </script> <template> <div class="dropdown show"> <div class="dropdown-menu sidebar-item-warning-message" data-testid="warning-text"> - <p v-if="isLocked" class="text" v-html="unlockWarning"></p> + <p v-if="isLocked" class="text"> + <gl-sprintf + :message=" + __( + 'Unlock this %{issuableDisplayName}? %{strongStart}Everyone%{strongEnd} will be able to comment.', + ) + " + > + <template #issuableDisplayName>{{ issuableDisplayName }}</template> + <template #strong="{ content }" + ><strong>{{ content }}</strong></template + > + </gl-sprintf> + </p> - <p v-else class="text" v-html="lockWarning"></p> + <p v-else class="text"> + <gl-sprintf + :message=" + __( + 'Lock this %{issuableDisplayName}? Only %{strongStart}project members%{strongEnd} will be able to comment.', + ) + " + > + <template #issuableDisplayName>{{ issuableDisplayName }}</template> + <template #strong="{ content }" + ><strong>{{ content }}</strong></template + > + </gl-sprintf> + </p> <edit-form-buttons :is-locked="isLocked" :issuable-display-name="issuableDisplayName" /> </div> diff --git a/app/assets/javascripts/vue_merge_request_widget/components/source_branch_removal_status.vue b/app/assets/javascripts/vue_merge_request_widget/components/source_branch_removal_status.vue index a0e76b151f7..439baa5b6c5 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/source_branch_removal_status.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/source_branch_removal_status.vue @@ -1,22 +1,37 @@ <script> import tooltip from '../../vue_shared/directives/tooltip'; import { __ } from '../../locale'; +import { GlSprintf } from '@gitlab/ui'; export default { + i18n: { + removesBranchText: __('%{strongStart}Deletes%{strongEnd} source branch'), + tooltipTitle: __('A user with write access to the source branch selected this option'), + }, + components: { + GlSprintf, + }, directives: { tooltip, }, - created() { - this.removesBranchText = __('<strong>Deletes</strong> source branch'); - this.tooltipTitle = __('A user with write access to the source branch selected this option'); - }, }; </script> <template> <p v-once class="mr-info-list mr-links gl-mb-0"> - <span class="status-text" v-html="removesBranchText"> </span> - <i v-tooltip :title="tooltipTitle" :aria-label="tooltipTitle" class="fa fa-question-circle"> + <span class="status-text"> + <gl-sprintf :message="$options.i18n.removesBranchText"> + <template #strong="{ content }"> + <strong>{{ content }}</strong> + </template> + </gl-sprintf> + </span> + <i + v-tooltip + :title="$options.i18n.tooltipTitle" + :aria-label="$options.i18n.tooltipTitle" + class="fa fa-question-circle" + > </i> </p> </template> diff --git a/app/assets/stylesheets/components/avatar.scss b/app/assets/stylesheets/components/avatar.scss index 6bb7e9d215e..67213eedca8 100644 --- a/app/assets/stylesheets/components/avatar.scss +++ b/app/assets/stylesheets/components/avatar.scss @@ -125,7 +125,7 @@ $identicon-backgrounds: $identicon-red, $identicon-purple, $identicon-indigo, $i .identicon { text-align: center; vertical-align: top; - color: $gray-800; + color: $gray-700; background-color: $gray-darker; // Sizes diff --git a/app/assets/stylesheets/components/rich_content_editor.scss b/app/assets/stylesheets/components/rich_content_editor.scss index 425c67ee188..ade1bb2099d 100644 --- a/app/assets/stylesheets/components/rich_content_editor.scss +++ b/app/assets/stylesheets/components/rich_content_editor.scss @@ -28,7 +28,7 @@ } button { - @include gl-text-gray-800; + @include gl-text-gray-700; } } diff --git a/app/assets/stylesheets/framework/badges.scss b/app/assets/stylesheets/framework/badges.scss index 5b8a4bf964e..3f1d742ca14 100644 --- a/app/assets/stylesheets/framework/badges.scss +++ b/app/assets/stylesheets/framework/badges.scss @@ -1,7 +1,7 @@ .badge.badge-pill:not(.gl-badge) { font-weight: $gl-font-weight-normal; background-color: $badge-bg; - color: $gray-800; + color: $gray-700; vertical-align: baseline; // Do not use this! diff --git a/app/assets/stylesheets/framework/broadcast_messages.scss b/app/assets/stylesheets/framework/broadcast_messages.scss index f7836213e5c..c1647c16c77 100644 --- a/app/assets/stylesheets/framework/broadcast_messages.scss +++ b/app/assets/stylesheets/framework/broadcast_messages.scss @@ -38,7 +38,7 @@ } .broadcast-message-dismiss { - color: $gray-800; + color: $gray-700; } } diff --git a/app/assets/stylesheets/framework/gitlab_theme.scss b/app/assets/stylesheets/framework/gitlab_theme.scss index 7aaed1fa9b7..97bd6ca6fe2 100644 --- a/app/assets/stylesheets/framework/gitlab_theme.scss +++ b/app/assets/stylesheets/framework/gitlab_theme.scss @@ -297,7 +297,7 @@ body { $gray-200, $gray-300, $gray-500, - $gray-800, + $gray-700, $gray-900, $white ); @@ -306,7 +306,7 @@ body { &.ui-light { @include gitlab-theme( $gray-500, - $gray-800, + $gray-700, $gray-500, $gray-500, $gray-50, diff --git a/app/assets/stylesheets/framework/selects.scss b/app/assets/stylesheets/framework/selects.scss index c2ab6f5b8c5..e81ecfb43d5 100644 --- a/app/assets/stylesheets/framework/selects.scss +++ b/app/assets/stylesheets/framework/selects.scss @@ -285,7 +285,7 @@ .select2-highlighted { .group-result { .group-path { - color: $gray-800; + color: $gray-700; } } } diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss index bfb8d209868..62cf3079c20 100644 --- a/app/assets/stylesheets/framework/variables.scss +++ b/app/assets/stylesheets/framework/variables.scss @@ -170,7 +170,7 @@ $gray-400: #868686 !default; $gray-500: #666 !default; $gray-600: #5e5e5e !default; $gray-700: #525252 !default; -$gray-800: #4f4f4f !default; +$gray-800: #404040 !default; $gray-900: #303030 !default; $gray-950: #1f1f1f !default; diff --git a/app/assets/stylesheets/page_bundles/_ide_theme_overrides.scss b/app/assets/stylesheets/page_bundles/_ide_theme_overrides.scss index a8d10ea1a29..dfd7fd355a4 100644 --- a/app/assets/stylesheets/page_bundles/_ide_theme_overrides.scss +++ b/app/assets/stylesheets/page_bundles/_ide_theme_overrides.scss @@ -38,7 +38,7 @@ } .badge.badge-pill { - color: var(--ide-text-color, $gray-800); + color: var(--ide-text-color, $gray-700); background-color: var(--ide-background, $badge-bg); } diff --git a/app/assets/stylesheets/pages/graph.scss b/app/assets/stylesheets/pages/graph.scss index f6c045f1d97..c4b6cdd703d 100644 --- a/app/assets/stylesheets/pages/graph.scss +++ b/app/assets/stylesheets/pages/graph.scss @@ -49,7 +49,7 @@ text { font-weight: bold; font-size: 12px; - fill: $gray-800; + fill: $gray-700; } } } diff --git a/app/assets/stylesheets/pages/labels.scss b/app/assets/stylesheets/pages/labels.scss index 73d2c3ca2f8..e37b26187e7 100644 --- a/app/assets/stylesheets/pages/labels.scss +++ b/app/assets/stylesheets/pages/labels.scss @@ -336,11 +336,11 @@ } .label-action { - color: $gray-800; + color: $gray-700; cursor: pointer; svg { - fill: $gray-800; + fill: $gray-700; } &:hover { diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss index 9797800b782..d180e64b8dd 100644 --- a/app/assets/stylesheets/pages/pipelines.scss +++ b/app/assets/stylesheets/pages/pipelines.scss @@ -796,7 +796,7 @@ &.ci-status-icon-disabled, &.ci-status-icon-not-found, &.ci-status-icon-manual { - @include mini-pipeline-graph-color($white, $gray-500, $gray-800, $gray-900, $gray-950, $black); + @include mini-pipeline-graph-color($white, $gray-500, $gray-700, $gray-900, $gray-950, $black); } &.ci-status-icon-created, diff --git a/app/controllers/root_controller.rb b/app/controllers/root_controller.rb index 14469877e14..191134472c2 100644 --- a/app/controllers/root_controller.rb +++ b/app/controllers/root_controller.rb @@ -13,6 +13,7 @@ class RootController < Dashboard::ProjectsController before_action :redirect_unlogged_user, if: -> { current_user.nil? } before_action :redirect_logged_user, if: -> { current_user.present? } + before_action :customize_homepage, only: :index, if: -> { current_user.present? } # We only need to load the projects when the user is logged in but did not # configure a dashboard. In which case we render projects. We can do that straight # from the #index action. @@ -66,6 +67,10 @@ class RootController < Dashboard::ProjectsController root_urls.exclude?(home_page_url) end + + def customize_homepage + @customize_homepage = experiment_enabled?(:customize_homepage) + end end RootController.prepend_if_ee('EE::RootController') diff --git a/app/graphql/resolvers/metrics/dashboard_resolver.rb b/app/graphql/resolvers/metrics/dashboard_resolver.rb index 05d82ca0f46..18a654c7dc5 100644 --- a/app/graphql/resolvers/metrics/dashboard_resolver.rb +++ b/app/graphql/resolvers/metrics/dashboard_resolver.rb @@ -14,7 +14,8 @@ module Resolvers def resolve(**args) return unless environment - ::PerformanceMonitoring::PrometheusDashboard.find_for(project: environment.project, user: context[:current_user], path: args[:path], options: { environment: environment }) + ::PerformanceMonitoring::PrometheusDashboard + .find_for(project: environment.project, user: context[:current_user], path: args[:path], options: { environment: environment }) end end end diff --git a/app/graphql/types/ci/pipeline_type.rb b/app/graphql/types/ci/pipeline_type.rb index c9cbae2d828..82a9f8495ce 100644 --- a/app/graphql/types/ci/pipeline_type.rb +++ b/app/graphql/types/ci/pipeline_type.rb @@ -45,8 +45,9 @@ module Types description: 'Stages of the pipeline', extras: [:lookahead], resolver: Resolvers::Ci::PipelineStagesResolver - - # TODO: Add triggering user as a type + field :user, Types::UserType, null: true, + description: 'Pipeline user', + resolve: -> (pipeline, _args, _context) { Gitlab::Graphql::Loaders::BatchModelLoader.new(User, pipeline.user_id).find } end end end diff --git a/app/graphql/types/metrics/dashboard_type.rb b/app/graphql/types/metrics/dashboard_type.rb index bbcce2d9596..47502356773 100644 --- a/app/graphql/types/metrics/dashboard_type.rb +++ b/app/graphql/types/metrics/dashboard_type.rb @@ -16,6 +16,13 @@ module Types field :annotations, Types::Metrics::Dashboards::AnnotationType.connection_type, null: true, description: 'Annotations added to the dashboard', resolver: Resolvers::Metrics::Dashboards::AnnotationResolver + + # In order to maintain backward compatibility we need to return NULL when there are no warnings + # and dashboard validation returns an empty array when there are no issues. + def schema_validation_warnings + warnings = object.schema_validation_warnings + warnings unless warnings.empty? + end end # rubocop: enable Graphql/AuthorizeTypes end diff --git a/app/graphql/types/user_status_type.rb b/app/graphql/types/user_status_type.rb new file mode 100644 index 00000000000..ff277c1f8e8 --- /dev/null +++ b/app/graphql/types/user_status_type.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Types + # rubocop: disable Graphql/AuthorizeTypes + class UserStatusType < BaseObject + graphql_name 'UserStatus' + + markdown_field :message_html, null: true, + description: 'HTML of the user status message' + field :message, GraphQL::STRING_TYPE, null: true, + description: 'User status message' + field :emoji, GraphQL::STRING_TYPE, null: true, + description: 'String representation of emoji' + end +end diff --git a/app/graphql/types/user_type.rb b/app/graphql/types/user_type.rb index f636f1b02b2..cb3575b41d1 100644 --- a/app/graphql/types/user_type.rb +++ b/app/graphql/types/user_type.rb @@ -18,6 +18,8 @@ module Types description: 'Human-readable name of the user' field :state, Types::UserStateEnum, null: false, description: 'State of the user' + field :email, GraphQL::STRING_TYPE, null: true, + description: 'User email' field :avatar_url, GraphQL::STRING_TYPE, null: true, description: "URL of the user's avatar" field :web_url, GraphQL::STRING_TYPE, null: false, @@ -30,6 +32,8 @@ module Types field :group_memberships, Types::GroupMemberType.connection_type, null: true, description: 'Group memberships of the user', method: :group_members + field :status, Types::UserStatusType, null: true, + description: 'User status' field :project_memberships, Types::ProjectMemberType.connection_type, null: true, description: 'Project memberships of the user', method: :project_members diff --git a/app/helpers/user_callouts_helper.rb b/app/helpers/user_callouts_helper.rb index f1ecba6e68a..c37f86b6a92 100644 --- a/app/helpers/user_callouts_helper.rb +++ b/app/helpers/user_callouts_helper.rb @@ -7,6 +7,7 @@ module UserCalloutsHelper SUGGEST_POPOVER_DISMISSED = 'suggest_popover_dismissed' TABS_POSITION_HIGHLIGHT = 'tabs_position_highlight' WEBHOOKS_MOVED = 'webhooks_moved' + CUSTOMIZE_HOMEPAGE = 'customize_homepage' def show_admin_integrations_moved? !user_dismissed?(ADMIN_INTEGRATIONS_MOVED) @@ -44,6 +45,10 @@ module UserCalloutsHelper !user_dismissed?(WEBHOOKS_MOVED) end + def show_customize_homepage_banner?(customize_homepage) + customize_homepage && !user_dismissed?(CUSTOMIZE_HOMEPAGE) + end + private def user_dismissed?(feature_name, ignore_dismissal_earlier_than = nil) diff --git a/app/models/blob_viewer/metrics_dashboard_yml.rb b/app/models/blob_viewer/metrics_dashboard_yml.rb index c05fb5d88d6..1ee7cad5094 100644 --- a/app/models/blob_viewer/metrics_dashboard_yml.rb +++ b/app/models/blob_viewer/metrics_dashboard_yml.rb @@ -26,19 +26,10 @@ module BlobViewer def parse_blob_data yaml = ::Gitlab::Config::Loader::Yaml.new(blob.data).load_raw! - - ::PerformanceMonitoring::PrometheusDashboard.from_json(yaml) - nil + Gitlab::Metrics::Dashboard::Validator + .errors(yaml, dashboard_path: blob.path, project: project) rescue Gitlab::Config::Loader::FormatError => error - wrap_yml_syntax_error(error) - rescue ActiveModel::ValidationError => invalid - invalid.model.errors - end - - def wrap_yml_syntax_error(error) - ::PerformanceMonitoring::PrometheusDashboard.new.errors.tap do |errors| - errors.add(:'YAML syntax', error.message) - end + [error] end end end diff --git a/app/models/discussion.rb b/app/models/discussion.rb index e928bb0959a..adcb2217d85 100644 --- a/app/models/discussion.rb +++ b/app/models/discussion.rb @@ -23,6 +23,7 @@ class Discussion :resolved_by_id, :system_note_with_references_visible_for?, :resource_parent, + :save, to: :first_note diff --git a/app/models/individual_note_discussion.rb b/app/models/individual_note_discussion.rb index bdfa6dcc6bd..c3ccf44d27e 100644 --- a/app/models/individual_note_discussion.rb +++ b/app/models/individual_note_discussion.rb @@ -17,12 +17,8 @@ class IndividualNoteDiscussion < Discussion noteable.supports_replying_to_individual_notes? end - def convert_to_discussion!(save: false) - first_note.becomes!(Discussion.note_class).to_discussion.tap do - # Save needs to be called on first_note instead of the transformed note - # because of https://gitlab.com/gitlab-org/gitlab-foss/issues/57324 - first_note.save if save - end + def convert_to_discussion! + first_note.becomes!(Discussion.note_class).to_discussion end def reply_attributes diff --git a/app/models/performance_monitoring/prometheus_dashboard.rb b/app/models/performance_monitoring/prometheus_dashboard.rb index bf87d2c3916..0c9358aa5cb 100644 --- a/app/models/performance_monitoring/prometheus_dashboard.rb +++ b/app/models/performance_monitoring/prometheus_dashboard.rb @@ -53,14 +53,18 @@ module PerformanceMonitoring # This method is planned to be refactored as a part of https://gitlab.com/gitlab-org/gitlab/-/issues/219398 # implementation. For new existing logic was reused to faster deliver MVC def schema_validation_warnings - self.class.from_json(reload_schema) - nil - rescue ActiveModel::ValidationError => exception - exception.model.errors.map { |attr, error| "#{attr}: #{error}" } + run_custom_validation.map(&:message) end private + def run_custom_validation + Gitlab::Metrics::Dashboard::Validator + .errors(reload_schema, dashboard_path: path, project: environment&.project) + rescue Gitlab::Config::Loader::FormatError => error + [error.message] + end + # dashboard finder methods are somehow limited, #find includes checking if # user is authorised to view selected dashboard, but modifies schema, which in some cases may # cause false positives returned from validation, and #find_raw does not authorise users diff --git a/app/models/repository.rb b/app/models/repository.rb index 9d876321217..0b82c5f943e 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -220,6 +220,7 @@ class Repository prefix_regex.match?(ref) end end + cache_method :has_ambiguous_refs? def expand_ref(ref) if tag_exists?(ref) @@ -311,12 +312,12 @@ class Repository end def expire_tags_cache - expire_method_caches(%i(tag_names tag_count)) + expire_method_caches(%i(tag_names tag_count has_ambiguous_refs?)) @tags = nil end def expire_branches_cache - expire_method_caches(%i(branch_names merged_branch_names branch_count has_visible_content?)) + expire_method_caches(%i(branch_names merged_branch_names branch_count has_visible_content? has_ambiguous_refs?)) @local_branches = nil @branch_exists_memo = nil end diff --git a/app/models/user_callout_enums.rb b/app/models/user_callout_enums.rb index 6de573ca9e3..5b64befd284 100644 --- a/app/models/user_callout_enums.rb +++ b/app/models/user_callout_enums.rb @@ -19,7 +19,8 @@ module UserCalloutEnums webhooks_moved: 13, admin_integrations_moved: 15, personal_access_token_expiry: 21, # EE-only - suggest_pipeline: 22 + suggest_pipeline: 22, + customize_homepage: 23 } end end diff --git a/app/services/notes/create_service.rb b/app/services/notes/create_service.rb index 9c60c54353f..4f2329a42f2 100644 --- a/app/services/notes/create_service.rb +++ b/app/services/notes/create_service.rb @@ -54,7 +54,8 @@ module Notes def when_saved(note) if note.part_of_discussion? && note.discussion.can_convert_to_discussion? - note.discussion.convert_to_discussion!(save: true) + note.discussion.convert_to_discussion!.save + note.clear_memoization(:discussion) end todo_service.new_note(note, current_user) diff --git a/app/views/dashboard/projects/index.html.haml b/app/views/dashboard/projects/index.html.haml index 2e7eab87af3..54a5624c6dd 100644 --- a/app/views/dashboard/projects/index.html.haml +++ b/app/views/dashboard/projects/index.html.haml @@ -3,6 +3,14 @@ = content_for :meta_tags do = auto_discovery_link_tag(:atom, dashboard_projects_url(rss_url_options), title: "All activity") +- if show_customize_homepage_banner?(@customize_homepage) + = content_for :customize_homepage_banner do + .d-none.d-md-block{ class: "gl-pt-6! gl-pb-2! #{(container_class unless @no_container)} #{@content_class}" } + .js-customize-homepage-banner{ data: { svg_path: image_path('illustrations/monitoring/getting_started.svg'), + preferences_behavior_path: profile_preferences_path(anchor: 'behavior'), + callouts_path: user_callouts_path, + callouts_feature_id: UserCalloutsHelper::CUSTOMIZE_HOMEPAGE } } + = render_dashboard_gold_trial(current_user) - page_title _("Projects") diff --git a/app/views/layouts/_page.html.haml b/app/views/layouts/_page.html.haml index b2e545bd0cb..3a543fef292 100644 --- a/app/views/layouts/_page.html.haml +++ b/app/views/layouts/_page.html.haml @@ -16,6 +16,7 @@ = render_account_recovery_regular_check = render_if_exists "layouts/header/ee_subscribable_banner" = render_if_exists "shared/namespace_storage_limit_alert" + = yield :customize_homepage_banner - unless @hide_breadcrumbs = render "layouts/nav/breadcrumbs" .d-flex diff --git a/app/views/projects/blob/viewers/_metrics_dashboard_yml.html.haml b/app/views/projects/blob/viewers/_metrics_dashboard_yml.html.haml index 9ec1d7d0d67..de9c6c5320f 100644 --- a/app/views/projects/blob/viewers/_metrics_dashboard_yml.html.haml +++ b/app/views/projects/blob/viewers/_metrics_dashboard_yml.html.haml @@ -5,7 +5,7 @@ = icon('warning fw') = _('Metrics Dashboard YAML definition is invalid:') %ul - - viewer.errors.messages.each do |error| - %li= error.join(': ') + - viewer.errors.each do |error| + %li= error = link_to _('Learn more'), help_page_path('operations/metrics/dashboards/index.md') diff --git a/changelogs/unreleased/233386-fix-resolve-button-when-replying-to-comment.yml b/changelogs/unreleased/233386-fix-resolve-button-when-replying-to-comment.yml new file mode 100644 index 00000000000..74d320c073d --- /dev/null +++ b/changelogs/unreleased/233386-fix-resolve-button-when-replying-to-comment.yml @@ -0,0 +1,5 @@ +--- +title: Fix missing resolve button when replying to notes in MRs +merge_request: 39614 +author: +type: fixed diff --git a/changelogs/unreleased/234066-create-issuelink-for-vulnerabilities-that-do-not-have-them.yml b/changelogs/unreleased/234066-create-issuelink-for-vulnerabilities-that-do-not-have-them.yml new file mode 100644 index 00000000000..c577de4d079 --- /dev/null +++ b/changelogs/unreleased/234066-create-issuelink-for-vulnerabilities-that-do-not-have-them.yml @@ -0,0 +1,5 @@ +--- +title: Create IssueLink for Vulnerabilities that do not have them +merge_request: 39098 +author: +type: fixed diff --git a/changelogs/unreleased/lm-add-user-to-pipeline.yml b/changelogs/unreleased/lm-add-user-to-pipeline.yml new file mode 100644 index 00000000000..97fab787e2e --- /dev/null +++ b/changelogs/unreleased/lm-add-user-to-pipeline.yml @@ -0,0 +1,5 @@ +--- +title: 'GraphQL: Add user to pipeline + status and email to user + StatusType' +merge_request: 39402 +author: +type: added diff --git a/changelogs/unreleased/mo-add-default-value-to-pipeline-artifact.yml b/changelogs/unreleased/mo-add-default-value-to-pipeline-artifact.yml new file mode 100644 index 00000000000..ce42eacb277 --- /dev/null +++ b/changelogs/unreleased/mo-add-default-value-to-pipeline-artifact.yml @@ -0,0 +1,5 @@ +--- +title: Add default value for file_store to ci_pipeline_artifacts +merge_request: 39349 +author: +type: fixed diff --git a/changelogs/unreleased/mwaw-219398-metrics-extend-dashboard-yaml-definion-validation-when-viewin.yml b/changelogs/unreleased/mwaw-219398-metrics-extend-dashboard-yaml-definion-validation-when-viewin.yml new file mode 100644 index 00000000000..09450c54a05 --- /dev/null +++ b/changelogs/unreleased/mwaw-219398-metrics-extend-dashboard-yaml-definion-validation-when-viewin.yml @@ -0,0 +1,6 @@ +--- +title: Change metrics dashboard schema validation messages into exhaustive list of + all encountered errors. +merge_request: 38925 +author: +type: changed diff --git a/changelogs/unreleased/rpereira2-allow-numbers-in-dashboard-query-key.yml b/changelogs/unreleased/rpereira2-allow-numbers-in-dashboard-query-key.yml new file mode 100644 index 00000000000..eb865de6015 --- /dev/null +++ b/changelogs/unreleased/rpereira2-allow-numbers-in-dashboard-query-key.yml @@ -0,0 +1,5 @@ +--- +title: Allow query/query_range keys in metrics dashboard to contain numbers +merge_request: 39530 +author: +type: changed diff --git a/db/migrate/20200811194848_add_default_value_for_file_store_to_pipeline_artifact.rb b/db/migrate/20200811194848_add_default_value_for_file_store_to_pipeline_artifact.rb new file mode 100644 index 00000000000..f689a505809 --- /dev/null +++ b/db/migrate/20200811194848_add_default_value_for_file_store_to_pipeline_artifact.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +class AddDefaultValueForFileStoreToPipelineArtifact < ActiveRecord::Migration[6.0] + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + PIPELINE_ARTIFACT_LOCAL_FILE_STORE = 1 + + def up + with_lock_retries do + change_column_default :ci_pipeline_artifacts, :file_store, PIPELINE_ARTIFACT_LOCAL_FILE_STORE + end + end + + def down + with_lock_retries do + change_column_default :ci_pipeline_artifacts, :file_store, nil + end + end +end diff --git a/db/post_migrate/20200811130000_create_index_vulnerabilities_feedback_issue_id_not_null.rb b/db/post_migrate/20200811130000_create_index_vulnerabilities_feedback_issue_id_not_null.rb new file mode 100644 index 00000000000..118076eb254 --- /dev/null +++ b/db/post_migrate/20200811130000_create_index_vulnerabilities_feedback_issue_id_not_null.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +class CreateIndexVulnerabilitiesFeedbackIssueIdNotNull < ActiveRecord::Migration[6.0] + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + + disable_ddl_transaction! + + def up + add_concurrent_index :vulnerability_feedback, :id, where: 'issue_id IS NOT NULL', + name: "index_vulnerability_feedback_on_issue_id_not_null" + end + + def down + remove_concurrent_index_by_name :vulnerability_feedback, + :index_vulnerability_feedback_on_issue_id_not_null + end +end diff --git a/db/post_migrate/20200811130433_create_missing_vulnerabilities_issue_links.rb b/db/post_migrate/20200811130433_create_missing_vulnerabilities_issue_links.rb new file mode 100644 index 00000000000..7da8204deed --- /dev/null +++ b/db/post_migrate/20200811130433_create_missing_vulnerabilities_issue_links.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true + +class CreateMissingVulnerabilitiesIssueLinks < ActiveRecord::Migration[6.0] + class VulnerabilitiesFeedback < ActiveRecord::Base + include EachBatch + self.table_name = 'vulnerability_feedback' + end + + class VulnerabilitiesIssueLink < ActiveRecord::Base + self.table_name = 'vulnerability_issue_links' + LINK_TYPE_CREATED = 2 + end + + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + + disable_ddl_transaction! + + def up + # https://github.com/rails/rails/issues/35493 + VulnerabilitiesFeedback.where('issue_id IS NOT NULL').each_batch do |relation| + timestamp = Time.now + values = relation + .joins("JOIN vulnerability_occurrences vo ON vo.project_id = vulnerability_feedback.project_id AND vo.report_type = vulnerability_feedback.category AND encode(vo.project_fingerprint, 'hex') = vulnerability_feedback.project_fingerprint") + .pluck(:vulnerability_id, :issue_id) + .map do |v_id, i_id| + { + vulnerability_id: v_id, + issue_id: i_id, + link_type: VulnerabilitiesIssueLink::LINK_TYPE_CREATED, + created_at: timestamp, + updated_at: timestamp + } + end + + next if values.empty? + + VulnerabilitiesIssueLink.insert_all( + values, + returning: false, + unique_by: %i[vulnerability_id issue_id] + ) + end + end + + def down + end +end diff --git a/db/schema_migrations/20200811130000 b/db/schema_migrations/20200811130000 new file mode 100644 index 00000000000..a7df459c416 --- /dev/null +++ b/db/schema_migrations/20200811130000 @@ -0,0 +1 @@ +36d3db5618a56a0ea03272563fe254590d6af1f7d2610a1f01a5054b1cda1a7d
\ No newline at end of file diff --git a/db/schema_migrations/20200811130433 b/db/schema_migrations/20200811130433 new file mode 100644 index 00000000000..303468e8949 --- /dev/null +++ b/db/schema_migrations/20200811130433 @@ -0,0 +1 @@ +e8fc0809b5bd3248dc625602deeaaef16e2db6b33d8eaf51fdcc1c67dee49e17
\ No newline at end of file diff --git a/db/schema_migrations/20200811194848 b/db/schema_migrations/20200811194848 new file mode 100644 index 00000000000..e1e9ad01ee8 --- /dev/null +++ b/db/schema_migrations/20200811194848 @@ -0,0 +1 @@ +c45bbd5aa9143e039d41448f1cb4a2e8e0a7b2c165b50e89b1b829bbbe81f137
\ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index 78b1e88e38f..b423b9bdeac 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -10061,7 +10061,7 @@ CREATE TABLE public.ci_pipeline_artifacts ( pipeline_id bigint NOT NULL, project_id bigint NOT NULL, size integer NOT NULL, - file_store smallint NOT NULL, + file_store smallint DEFAULT 1 NOT NULL, file_type smallint NOT NULL, file_format smallint NOT NULL, file text, @@ -20971,6 +20971,8 @@ CREATE INDEX index_vulnerability_feedback_on_comment_author_id ON public.vulnera CREATE INDEX index_vulnerability_feedback_on_issue_id ON public.vulnerability_feedback USING btree (issue_id); +CREATE INDEX index_vulnerability_feedback_on_issue_id_not_null ON public.vulnerability_feedback USING btree (id) WHERE (issue_id IS NOT NULL); + CREATE INDEX index_vulnerability_feedback_on_merge_request_id ON public.vulnerability_feedback USING btree (merge_request_id); CREATE INDEX index_vulnerability_feedback_on_pipeline_id ON public.vulnerability_feedback USING btree (pipeline_id); diff --git a/doc/api/graphql/reference/gitlab_schema.graphql b/doc/api/graphql/reference/gitlab_schema.graphql index 43c50002779..a285ce96ae5 100644 --- a/doc/api/graphql/reference/gitlab_schema.graphql +++ b/doc/api/graphql/reference/gitlab_schema.graphql @@ -10423,6 +10423,11 @@ type Pipeline { updatedAt: Time! """ + Pipeline user + """ + user: User + + """ Permissions for the current user on the resource """ userPermissions: PipelinePermissions! @@ -16463,6 +16468,11 @@ type User { avatarUrl: String """ + User email + """ + email: String + + """ Group memberships of the user """ groupMemberships( @@ -16568,6 +16578,11 @@ type User { state: UserState! """ + User status + """ + status: UserStatus + + """ Todos of the user """ todos( @@ -16705,6 +16720,23 @@ enum UserState { deactivated } +type UserStatus { + """ + String representation of emoji + """ + emoji: String + + """ + User status message + """ + message: String + + """ + HTML of the user status message + """ + messageHtml: String +} + enum VisibilityLevelsEnum { internal private diff --git a/doc/api/graphql/reference/gitlab_schema.json b/doc/api/graphql/reference/gitlab_schema.json index f4282facd30..3733f33d308 100644 --- a/doc/api/graphql/reference/gitlab_schema.json +++ b/doc/api/graphql/reference/gitlab_schema.json @@ -31146,6 +31146,20 @@ "deprecationReason": null }, { + "name": "user", + "description": "Pipeline user", + "args": [ + + ], + "type": { + "kind": "OBJECT", + "name": "User", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { "name": "userPermissions", "description": "Permissions for the current user on the resource", "args": [ @@ -48276,6 +48290,20 @@ "deprecationReason": null }, { + "name": "email", + "description": "User email", + "args": [ + + ], + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { "name": "groupMemberships", "description": "Group memberships of the user", "args": [ @@ -48527,6 +48555,20 @@ "deprecationReason": null }, { + "name": "status", + "description": "User status", + "args": [ + + ], + "type": { + "kind": "OBJECT", + "name": "UserStatus", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { "name": "todos", "description": "Todos of the user", "args": [ @@ -48944,6 +48986,61 @@ "possibleTypes": null }, { + "kind": "OBJECT", + "name": "UserStatus", + "description": null, + "fields": [ + { + "name": "emoji", + "description": "String representation of emoji", + "args": [ + + ], + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "message", + "description": "User status message", + "args": [ + + ], + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "messageHtml", + "description": "HTML of the user status message", + "args": [ + + ], + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [ + + ], + "enumValues": null, + "possibleTypes": null + }, + { "kind": "ENUM", "name": "VisibilityLevelsEnum", "description": null, diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index b766007c7de..738258ac7b4 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -1604,6 +1604,7 @@ Information about pagination in a connection. | `startedAt` | Time | Timestamp when the pipeline was started | | `status` | PipelineStatusEnum! | Status of the pipeline (CREATED, WAITING_FOR_RESOURCE, PREPARING, PENDING, RUNNING, FAILED, SUCCESS, CANCELED, SKIPPED, MANUAL, SCHEDULED) | | `updatedAt` | Time! | Timestamp of the pipeline's last activity | +| `user` | User | Pipeline user | | `userPermissions` | PipelinePermissions! | Permissions for the current user on the resource | ## PipelinePermissions @@ -2434,9 +2435,11 @@ Autogenerated return type of UpdateSnippet | Name | Type | Description | | --- | ---- | ---------- | | `avatarUrl` | String | URL of the user's avatar | +| `email` | String | User email | | `id` | ID! | ID of the user | | `name` | String! | Human-readable name of the user | | `state` | UserState! | State of the user | +| `status` | UserStatus | User status | | `userPermissions` | UserPermissions! | Permissions for the current user on the resource | | `username` | String! | Username of the user. Unique within this instance of GitLab | | `webPath` | String! | Web path of the user | @@ -2448,6 +2451,14 @@ Autogenerated return type of UpdateSnippet | --- | ---- | ---------- | | `createSnippet` | Boolean! | Indicates the user can perform `create_snippet` on this resource | +## UserStatus + +| Name | Type | Description | +| --- | ---- | ---------- | +| `emoji` | String | String representation of emoji | +| `message` | String | User status message | +| `messageHtml` | String | HTML of the user status message | + ## VulnerabilitiesCountByDay Represents the count of vulnerabilities by severity on a particular day diff --git a/doc/development/telemetry/usage_ping.md b/doc/development/telemetry/usage_ping.md index afb800b85fc..ea5eb6c389f 100644 --- a/doc/development/telemetry/usage_ping.md +++ b/doc/development/telemetry/usage_ping.md @@ -131,7 +131,7 @@ There are four types of counters which are all found in `usage_data.rb`: - **Ordinary Batch Counters:** Simple count of a given ActiveRecord_Relation - **Distinct Batch Counters:** Distinct count of a given ActiveRecord_Relation on given column - **Alternative Counters:** Used for settings and configurations -- **Redis Counters:** Used for in-memory counts. This method is being deprecated due to data inaccuracies and will be replaced with a persistent method. +- **Redis Counters:** Used for in-memory counts. NOTE: **Note:** Only use the provided counter methods. Each counter method contains a built in fail safe to isolate each counter to avoid breaking the entire Usage Ping. @@ -256,8 +256,6 @@ redis_usage_data do ) ``` -Note that Redis counters are in the [process of being deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/216330) and you should instead try to use Snowplow events instead. We're in the process of building [self-managed event tracking](https://gitlab.com/gitlab-org/telemetry/-/issues/373) and once this is available, we will convert all Redis counters into Snowplow events. - ### Alternative Counters Handles `StandardError` and fallbacks into -1 this way not all measures fail if we encounter one exception. diff --git a/doc/operations/incident_management/alertdetails.md b/doc/operations/incident_management/alertdetails.md new file mode 100644 index 00000000000..774eaee286f --- /dev/null +++ b/doc/operations/incident_management/alertdetails.md @@ -0,0 +1,194 @@ +--- +stage: Monitor +group: Health +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/#designated-technical-writers +--- + +# Alert details page + +Navigate to the Alert details view by visiting the +[Alert list](alerts.md) and selecting an alert from the +list. You need least Developer [permissions](../../user/permissions.md) to access +alerts. + +Alerts provide **Overview** and **Alert details** tabs to give you the right +amount of information you need. + +## Alert overview tab + +The **Overview** tab provides basic information about the alert: + +![Alert Detail Overview](img/alert_detail_overview_v13_1.png) + +## Alert details tab + +![Alert Full Details](img/alert_detail_full_v13_1.png) + +### Update an Alert's status + +The Alert detail view enables you to update the Alert Status. +See [Create and manage alerts in GitLab](alerts.md) for more details. + +### Create an Issue from an Alert + +> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/217745) in GitLab 13.1. + +The Alert detail view enables you to create an issue with a +description automatically populated from an alert. To create the issue, +click the **Create Issue** button. You can then view the issue from the +alert by clicking the **View Issue** button. + +Closing a GitLab issue associated with an alert changes the alert's status to Resolved. +See [Create and manage alerts in GitLab](alerts.md) for more details about alert statuses. + +### Update an Alert's assignee + +> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/3066) in GitLab 13.1. + +The Alert detail view allows users to update the Alert assignee. + +In large teams, where there is shared ownership of an alert, it can be difficult +to track who is investigating and working on it. The Alert detail view +enables you to update the Alert assignee: + +NOTE: **Note:** +GitLab currently only supports a single assignee per alert. + +1. To display the list of current alerts, click + **{cloud-gear}** **Operations > Alerts**: + + ![Alert List View Assignee(s)](img/alert_list_assignees_v13_1.png) + +1. Select your desired alert to display its **Alert Details View**: + + ![Alert Details View Assignee(s)](img/alert_details_assignees_v13_1.png) + +1. If the right sidebar is not expanded, click + **{angle-double-right}** **Expand sidebar** to expand it. +1. In the right sidebar, locate the **Assignee** and click **Edit**. From the + dropdown menu, select each user you want to assign to the alert. GitLab creates + a [To-Do list item](../../user/todos.md) for each user. + + ![Alert Details View Assignee(s)](img/alert_todo_assignees_v13_1.png) + +To remove an assignee, click **Edit** next to the **Assignee** dropdown menu and +deselect the user from the list of assignees, or click **Unassigned**. + +### Alert system notes + +> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/3066) in GitLab 13.1. + +When you take action on an alert, this is logged as a system note, +which is visible in the Alert Details view. This gives you a linear +timeline of the alert's investigation and assignment history. + +The following actions will result in a system note: + +- [Updating the status of an alert](#update-an-alerts-status) +- [Creating an issue based on an alert](#create-an-issue-from-an-alert) +- [Assignment of an alert to a user](#update-an-alerts-assignee) + +![Alert Details View System Notes](img/alert_detail_system_notes_v13_1.png) + +### Create a To-Do from an Alert + +> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/3066) in GitLab 13.1. + +You can manually create [To-Do list items](../../user/todos.md) for yourself from the +Alert details screen, and view them later on your **To-Do List**. To add a To-Do: + +1. To display the list of current alerts, click + **{cloud-gear}** **Operations > Alerts**. +1. Select your desired alert to display its **Alert Management Details View**. +1. Click the **Add a To-Do** button in the right sidebar: + + ![Alert Details Add A To Do](img/alert_detail_add_todo_v13_1.png) + +Click the **To-Do** **{todo-done}** in the navigation bar to view your current To-Do list. + +![Alert Details Added to Do](img/alert_detail_added_todo_v13_1.png) + +### View an Alert's metrics data + +> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/217768) in GitLab 13.2. + +To view the metrics for an alert: + + 1. Sign in as a user with Developer or higher [permissions](../../user/permissions.md). + 1. Navigate to **{cloud-gear}** **Operations > Alerts**. + 1. Click the alert you want to view. + 1. Below the title of the alert, click the **Metrics** tab. + +![Alert Metrics View](img/alert_detail_metrics_v13_2.png) + +For GitLab-managed Prometheus instances, metrics data is automatically available +for the alert, making it easy to see surrounding behavior. See +[Managed Prometheus instances](../metrics/alerts.md#managed-prometheus-instances) +for information on setting up alerts. + +For externally-managed Prometheus instances, you can configure your alerting rules to +display a chart in the alert. See +[Embedding metrics based on alerts in incident issues](../metrics/embed.md#embedding-metrics-based-on-alerts-in-incident-issues) +for information on how to appropriately configure your alerting rules. See +[External Prometheus instances](../metrics/alerts.md#external-prometheus-instances) +for information on setting up alerts for your self-managed Prometheus instance. + +## Use cases for assigning alerts + +Consider a team formed by different sections of monitoring, collaborating on a +single application. After an alert surfaces, it's extremely important to +route the alert to the team members who can address and resolve the alert. + +Assigning Alerts eases collaboration and delegation. All +assignees are shown in your team's work-flows, and all assignees receive +notifications, simplifying communication and ownership of the alert. + +After completing their portion of investigating or fixing the alert, users can +unassign their account from the alert when their role is complete. +The alert status can be updated on the [Alert list](alerts.md) to +reflect if the alert has been resolved. + +## View an Alert's logs + +> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/217768) in GitLab 13.3. + +To view the logs for an alert: + + 1. Sign in as a user with Developer or higher [permissions](../../user/permissions.md). + 1. Navigate to **{cloud-gear}** **Operations > Alerts**. + 1. Click the alert you want to view. + 1. Below the title of the alert, click the **Metrics** tab. + 1. Click the [menu](../metrics/dashboards/index.md#chart-context-menu) of the metric chart to view options. + 1. Click **View logs**. + +Read [View logs from metrics panel](#view-logs-from-metrics-panel) for additional information. + +## Embed metrics in incidents and issues + +You can embed metrics anywhere [GitLab Markdown](../../user/markdown.md) is used, such as descriptions, +comments on issues, and merge requests. Embedding metrics helps you share them +when discussing incidents or performance issues. You can output the dashboard directly +into any issue, merge request, epic, or any other Markdown text field in GitLab +by [copying and pasting the link to the metrics dashboard](../metrics/embed.md#embedding-gitlab-managed-kubernetes-metrics). + +You can embed both +[GitLab-hosted metrics](../metrics/embed.md) and +[Grafana metrics](../metrics/embed_grafana.md) +in incidents and issue templates. + +### Context menu + +You can view more details about an embedded metrics panel from the context menu. +To access the context menu, click the **{ellipsis_v}** **More actions** dropdown box +above the upper right corner of the panel. For a list of options, see +[Chart context menu](../metrics/dashboards/index.md#chart-context-menu). + +#### View logs from metrics panel + +> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/201846) in GitLab Ultimate 12.8. +> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/25455) to [GitLab Core](https://about.gitlab.com/pricing/) 12.9. + +Viewing logs from a metrics panel can be useful if you're triaging an application +incident and need to [explore logs](../metrics/dashboards/index.md#chart-context-menu) +from across your application. These logs help you understand what is affecting +your application's performance and resolve any problems. diff --git a/doc/operations/incident_management/alerts.md b/doc/operations/incident_management/alerts.md new file mode 100644 index 00000000000..5a5fc59d5e3 --- /dev/null +++ b/doc/operations/incident_management/alerts.md @@ -0,0 +1,118 @@ +--- +stage: Monitor +group: Health +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/#designated-technical-writers +--- + +# Create and manage alerts in GitLab + +Users with at least Developer [permissions](../../user/permissions.md) can access +the Alert Management list at **{cloud-gear}** **Operations > Alerts** in your +project's sidebar. The Alert Management list displays alerts sorted by start time, +but you can change the sort order by clicking the headers in the Alert Management list. +([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/217745) in GitLab 13.1.) + +The alert list displays the following information: + +![Alert List](../../user/project/operations/img/alert_list_v13_1.png) + +- **Search** - The alert list supports a simple free text search on the title, + description, monitoring tool, and service fields. + ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/213884) in GitLab 13.1.) +- **Severity** - The current importance of a alert and how much attention it should + receive. For a listing of all statuses, read [Alert Management severity](#alert-severity). +- **Start time** - How long ago the alert fired. This field uses the standard + GitLab pattern of `X time ago`, but is supported by a granular date/time tooltip + depending on the user's locale. +- **Alert description** - The description of the alert, which attempts to capture the most meaningful data. +- **Event count** - The number of times that an alert has fired. +- **Issue** - A link to the incident issue that has been created for the alert. +- **Status** - The current status of the alert: + - **Triggered**: No one has begun investigation. + - **Acknowledged**: Someone is actively investigating the problem. + - **Resolved**: No further work is required. + +## Enable Alerts + +NOTE: **Note:** +You need at least Maintainer [permissions](../../user/permissions.md) to enable +the Alerts feature. + +There are several ways to accept alerts into your GitLab project. +Enabling any of these methods enables the Alert list. After configuring +alerts, visit **{cloud-gear}** **Operations > Alerts** in your project's sidebar +to view the list of alerts. + +### Enable GitLab-managed Prometheus alerts + +You can install the GitLab-managed Prometheus application on your Kubernetes +cluster. For more information, read +[Managed Prometheus on Kubernetes](../../user/project/integrations/prometheus.md#managed-prometheus-on-kubernetes). +When GitLab-managed Prometheus is installed, the [Alerts list](alerts.md) +is also enabled. + +To populate the alerts with data, read +[GitLab-Managed Prometheus instances](../metrics/alerts.md#managed-prometheus-instances). + +### Enable external Prometheus alerts + +You can configure an externally-managed Prometheus instance to send alerts +to GitLab. To set up this configuration, read the [configuring Prometheus](../metrics/alerts.md#external-prometheus-instances) documentation. Activating the external Prometheus +configuration also enables the [Alerts list](alerts.md). + +To populate the alerts with data, read +[External Prometheus instances](../metrics/alerts.md#external-prometheus-instances). + +### Enable a Generic Alerts endpoint + +GitLab provides the Generic Alerts endpoint so you can accept alerts from a third-party +alerts service. Read the +[instructions for toggling generic alerts](../../user/project/integrations/generic_alerts.md#setting-up-generic-alerts) +to add this option. After configuring the endpoint, the +[Alerts list](alerts.md) is enabled. + +To populate the alerts with data, read [Customizing the payload](../../user/project/integrations/generic_alerts.md#customizing-the-payload) for requests to the alerts endpoint. + +### Opsgenie integration **(PREMIUM)** + +> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/3066) in [GitLab Premium](https://about.gitlab.com/pricing/) 13.2. + +A new way of monitoring Alerts via a GitLab integration is with +[Opsgenie](https://www.atlassian.com/software/opsgenie). + +NOTE: **Note:** +If you enable the Opsgenie integration, you can't have other GitLab alert services, +such as [Generic Alerts](../../user/project/integrations/generic_alerts.md) or +Prometheus alerts, active at the same time. + +To enable Opsgenie integration: + +1. Sign in as a user with Maintainer or Owner [permissions](../../user/permissions.md). +1. Navigate to **{cloud-gear}** **Operations > Alerts**. +1. In the **Integrations** select box, select Opsgenie. +1. Click the **Active** toggle. +1. In the **API URL**, enter the base URL for your Opsgenie integration, such + as `https://app.opsgenie.com/alert/list`. +1. Click **Save changes**. + +After enabling the integration, navigate to the Alerts list page at +**{cloud-gear}** **Operations > Alerts**, and click **View alerts in Opsgenie**. + +## Alert severity + +Each level of alert contains a uniquely shaped and color-coded icon to help +you identify the severity of a particular alert. These severity icons help you +immediately identify which alerts you should prioritize investigating: + +![Alert Management Severity System](img/alert_management_severity_v13_0.png) + +Alerts contain one of the following icons: + +| Severity | Icon | Color (hexadecimal) | +|---|---|---| +| Critical | **{severity-critical}** | `#8b2615` | +| High | **{severity-high}** | `#c0341d` | +| Medium | **{severity-medium}** | `#fca429` | +| Low | **{severity-low}** | `#fdbc60` | +| Info | **{severity-info}** | `#418cd8` | +| Unknown | **{severity-unknown}** | `#bababa` | diff --git a/doc/operations/incident_management/incidents.md b/doc/operations/incident_management/incidents.md new file mode 100644 index 00000000000..0668dc72c22 --- /dev/null +++ b/doc/operations/incident_management/incidents.md @@ -0,0 +1,103 @@ +--- +stage: Monitor +group: Health +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/#designated-technical-writers +--- + +# Create and manage incidents in GitLab + +While no configuration is required to use the [manual features](#create-an-incident-manually) +of incident management, some simple [configuration](#configure-incidents) is needed to automate incident creation. + +For users with at least Developer [permissions](../../user/permissions.md), the +Incident Management list is available at **Operations > Incidents** +in your project's sidebar. The list contains the following metrics: + +![Incident List](img/incident_list_sort_v13_3.png) + +- **Status** - To filter incidents by their status, click **Open**, **Closed**, + or **All** above the incident list. +- **Search** - The Incident list supports a simple free text search, which filters + on the **Title** and **Incident** fields. +- **Incident** - The description of the incident, which attempts to capture the + most meaningful data. +- **Date created** - How long ago the incident was created. This field uses the + standard GitLab pattern of `X time ago`, but is supported by a granular date/time + tooltip depending on the user's locale. +- **Assignees** - The user assigned to the incident. +- **Published** - Displays a green check mark (**{check-circle}**) if the incident is published + to a [Status Page](status_page.md).. **(ULTIMATE)** + +The Incident list displays incidents sorted by incident created date. +([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/229534) to GitLab core in 13.3).) +To see if a column is sortable, point your mouse at the header. Sortable columns +display an arrow next to the column name. + +NOTE: **Note:** +Incidents share the [Issues API](../../user/project/issues/index.md). + +## Configure incidents + +> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/4925) in GitLab Ultimate 11.11. + +With Maintainer or higher [permissions](../../user/permissions.md), you can enable +or disable Incident Management features in the GitLab user interface +to create issues when alerts are triggered: + +1. Navigate to **Settings > Operations > Incidents** and expand + **Incidents**: + + ![Incident Management Settings](img/incident_management_settings_v13_3.png) + +1. For GitLab versions 11.11 and greater, you can select the **Create an issue** + checkbox to create an issue based on your own + [issue templates](../../user/project/description_templates.md#creating-issue-templates). + For more information, see + [Trigger actions from alerts](../metrics/alerts.md#trigger-actions-from-alerts-ultimate) **(ULTIMATE)**. +1. To create issues from alerts, select the template in the **Issue Template** + select box. +1. To send [separate email notifications](index.md#notify-developers-of-alerts) to users + with [Developer permissions](../../user/permissions.md), select + **Send a separate email notification to Developers**. +1. Click **Save changes**. + +Appropriately configured alerts include an +[embedded chart](../metrics/embed.md#embedding-metrics-based-on-alerts-in-incident-issues) +for the query corresponding to the alert. You can also configure GitLab to +[close issues](../metrics/alerts.md#trigger-actions-from-alerts-ultimate) +when you receive notification that the alert is resolved. + +## Create an incident manually + +> [Moved](https://gitlab.com/gitlab-org/monitor/health/-/issues/24) to GitLab core in 13.3. + +For users with at least Developer [permissions](../../user/permissions.md), to create a Incident you can take any of the following actions: + +- Navigate to **Operations > Incidents** and click **Create Incident**. +- Create a new issue using the `incident` template available when creating it. +- Create a new issue and assign the `incident` label to it. + +![Incident List Create](img/incident_list_create_v13_3.png) + +## Configure PagerDuty integration + +> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/119018) in GitLab 13.3. + +You can set up a webhook with PagerDuty to automatically create a GitLab issue +for each PagerDuty incident. This configuration requires you to make changes +in both PagerDuty and GitLab: + +1. Sign in as a user with Maintainer [permissions](../../user/permissions.md). +1. Navigate to **Settings > Operations > Incidents** and expand **Incidents**. +1. Select the **PagerDuty integration** tab: + + ![PagerDuty incidents integration](img/pagerduty_incidents_integration_v13_3.png) + +1. Activate the integration, and save the changes in GitLab. +1. Copy the value of **Webhook URL** for use in a later step. +1. Follow the steps described in the + [PagerDuty documentation](https://support.pagerduty.com/docs/webhooks) + to add the webhook URL to a PagerDuty webhook integration. + +To confirm the integration is successful, trigger a test incident from PagerDuty to +confirm that a GitLab issue is created from the incident. diff --git a/doc/operations/incident_management/index.md b/doc/operations/incident_management/index.md index 568b346ed8a..53a6b47ec4b 100644 --- a/doc/operations/incident_management/index.md +++ b/doc/operations/incident_management/index.md @@ -14,324 +14,9 @@ being developed, efficiency and awareness can be increased. GitLab offers solutions for handling incidents in your applications and services, such as [setting up Prometheus alerts](#configure-prometheus-alerts), -[displaying metrics](#embed-metrics-in-incidents-and-issues), and sending notifications. -While no configuration is required to use the [manual features](#create-an-incident-manually) -of incident management, some simple [configuration](#configure-incidents) is needed to automate incident creation. +[displaying metrics](alertdetails.md#embed-metrics-in-incidents-and-issues), and sending notifications. -For users with at least Developer [permissions](../../user/permissions.md), the -Incident Management list is available at **Operations > Incidents** -in your project's sidebar. The list contains the following metrics: - -![Incident Management List](img/incident_list_sort_v13_3.png) - -- **Incident** - The description of the incident, which attempts to capture the - most meaningful data. -- **Date created** - How long ago the incident was created. This field uses the - standard GitLab pattern of `X time ago`, but is supported by a granular date/time - tooltip depending on the user's locale. -- **Assignees** - The user assigned to the incident. -- **Published** - Whether or not an incident is published to the - [Status Page](./status_page.md). **(ULTIMATE)** - -The Incident Management list displays incidents sorted by incident created date. -([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/229534) to GitLab core in 13.3).) -To see if a column is sortable, point your mouse at the header. Sortable columns -display an arrow next to the column name. - -The Incident list supports a simple free text search, which filters on the -**Title** and **Incident** fields. - -To filter incidents by their status, click **Open**, **Closed**, or **All** -above the incident list. - -NOTE: **Note:** -Incidents share the [Issues API](../../user/project/issues/index.md). - -## Enable Alert Management - -NOTE: **Note:** -You will need at least Maintainer [permissions](../../user/permissions.md) to enable the Alert Management feature. - -There are several ways to accept alerts into your GitLab project, as outlined below. -Enabling any of these methods will allow the Alerts list to display. After configuring -alerts, visit **{cloud-gear}** **Operations > Alerts** in your project's sidebar -to [view the list](#alert-management-list) of alerts. - -### Opsgenie integration **(PREMIUM)** - -> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/3066) in [GitLab Premium](https://about.gitlab.com/pricing/) 13.2. - -A new way of monitoring Alerts via a GitLab integration is with -[Opsgenie](https://www.atlassian.com/software/opsgenie). - -NOTE: **Note:** -If you enable the Opsgenie integration, you cannot have other GitLab alert services, -such as [Generic Alerts](../../user/project/integrations/generic_alerts.md) or -Prometheus alerts, active at the same time. - -To enable Opsgenie integration: - -1. Sign in as a user with Maintainer or Owner [permissions](../../user/permissions.md). -1. Navigate to **{cloud-gear}** **Operations > Alerts**. -1. In the **Integrations** select box, select Opsgenie. -1. Click the **Active** toggle. -1. In the **API URL**, enter the base URL for your Opsgenie integration, such - as `https://app.opsgenie.com/alert/list`. -1. Click **Save changes**. - -After enabling the integration, navigate to the Alerts list page at **{cloud-gear}** **Operations > Alerts**, and click **View alerts in Opsgenie**. - -### Enable a Generic Alerts endpoint - -GitLab provides the Generic Alerts endpoint so you can accept alerts from a third-party -alerts service. See the -[instructions for toggling generic alerts](../../user/project/integrations/generic_alerts.md#setting-up-generic-alerts) -to add this option. After configuring the endpoint, the -[Alerts list](#alert-management-list) is enabled. - -To populate the alerts with data, see [Customizing the payload](../../user/project/integrations/generic_alerts.md#customizing-the-payload) for requests to the alerts endpoint. - -### Enable GitLab-managed Prometheus alerts - -You can install the GitLab-managed Prometheus application on your Kubernetes -cluster. For more information, see -[Managed Prometheus on Kubernetes](../../user/project/integrations/prometheus.md#managed-prometheus-on-kubernetes). -When GitLab-managed Prometheus is installed, the [Alerts list](#alert-management-list) -is also enabled. - -To populate the alerts with data, see -[GitLab-Managed Prometheus instances](../metrics/alerts.md#managed-prometheus-instances). - -### Enable external Prometheus alerts - -You can configure an externally-managed Prometheus instance to send alerts -to GitLab. To set up this configuration, see the [configuring Prometheus](../metrics/alerts.md#external-prometheus-instances) documentation. Activating the external Prometheus -configuration also enables the [Alerts list](#alert-management-list). - -To populate the alerts with data, see -[External Prometheus instances](../metrics/alerts.md#external-prometheus-instances). - -## Alert Management severity - -Each level of alert contains a uniquely shaped and color-coded icon to help -you identify the severity of a particular alert. These severity icons help you -immediately identify which alerts you should prioritize investigating: - -![Alert Management Severity System](img/alert_management_severity_v13_0.png) - -Alerts contain one of the following icons: - -| Severity | Icon | Color (hexadecimal) | -|---|---|---| -| Critical | **{severity-critical}** | `#8b2615` | -| High | **{severity-high}** | `#c0341d` | -| Medium | **{severity-medium}** | `#fca429` | -| Low | **{severity-low}** | `#fdbc60` | -| Info | **{severity-info}** | `#418cd8` | -| Unknown | **{severity-unknown}** | `#bababa` | - -## Alert Management list - -NOTE: **Note:** -You will need at least Developer [permissions](../../user/permissions.md) to view the Alert Management list. - -You can find the Alert Management list at **{cloud-gear}** **Operations > Alerts** in your project's sidebar. -Each alert contains the following metrics: - -![Alert Management List](../../user/project/operations/img/alert_list_v13_1.png) - -- **Severity** - The current importance of a alert and how much attention it should receive. -- **Start time** - How long ago the alert fired. This field uses the standard GitLab pattern of `X time ago`, but is supported by a granular date/time tooltip depending on the user's locale. -- **Alert description** - The description of the alert, which attempts to capture the most meaningful data. -- **Event count** - The number of times that an alert has fired. -- **Issue** - A link to the incident issue that has been created for the alert. -- **Status** - The [current status](#alert-management-statuses) of the alert. - -### Alert Management list sorting - -> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/217745) in GitLab 13.1. - -The Alert Management list displays alerts sorted by start time, but you can -change the sort order by clicking the headers in the Alert Management list. - -To see if a column is sortable, point your mouse at the header. Sortable columns -display an arrow next to the column name, as shown in this example: - -![Alert Management List Sorting](img/alert_list_sort_v13_1.png) - -### Searching alerts - -> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/213884) in GitLab 13.1. - -The alert list supports a simple free text search. - -![Alert List Search](img/alert_list_search_v13_1.png) - -This search filters on the following fields: - -- Title -- Description -- Monitoring tool -- Service - -### Alert Management statuses - -Each alert contains a status dropdown to indicate which alerts need investigation. -Standard alert statuses include `triggered`, `acknowledged`, and `resolved`: - -- **Triggered**: No one has begun investigation. -- **Acknowledged**: Someone is actively investigating the problem. -- **Resolved**: No further work is required. - -## Alert Management details - -NOTE: **Note:** -You will need at least Developer [permissions](../../user/permissions.md) to view Alert Management details. - -Navigate to the Alert Management detail view by visiting the [Alert Management list](#alert-management-list) and selecting an Alert from the list. - -![Alert Management Detail Overview](img/alert_detail_overview_v13_1.png) - -![Alert Management Full Details](img/alert_detail_full_v13_1.png) - -### Update an Alert's status - -The Alert Management detail view enables you to update the Alert Status. -See [Alert Management statuses](#alert-management-statuses) for more details. - -### Create an Issue from an Alert - -> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/217745) in GitLab 13.1. - -The Alert Management detail view enables you to create an issue with a -description automatically populated from an alert. To create the issue, -click the **Create Issue** button. You can then view the issue from the -alert by clicking the **View Issue** button. - -Closing a GitLab issue associated with an alert changes the alert's status to Resolved. -See [Alert Management statuses](#alert-management-statuses) for more details about statuses. - -### Update an Alert's assignee - -> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/3066) in GitLab 13.1. - -The Alert Management detail view allows users to update the Alert assignee. - -In large teams, where there is shared ownership of an alert, it can be difficult -to track who is investigating and working on it. The Alert Management detail view -enables you to update the Alert assignee: - -NOTE: **Note:** -GitLab currently only supports a single assignee per alert. - -1. To display the list of current alerts, click - **{cloud-gear}** **Operations > Alerts**: - - ![Alert Management List View Assignee(s)](img/alert_list_assignees_v13_1.png) - -1. Select your desired alert to display its **Alert Management Details View**: - - ![Alert Management Details View Assignee(s)](img/alert_details_assignees_v13_1.png) - -1. If the right sidebar is not expanded, click - **{angle-double-right}** **Expand sidebar** to expand it. -1. In the right sidebar, locate the **Assignee** and click **Edit**. From the - dropdown menu, select each user you want to assign to the alert. GitLab creates - a [To-Do list item](../../user/todos.md) for each user. - - ![Alert Management Details View Assignee(s)](img/alert_todo_assignees_v13_1.png) - -To remove an assignee, click **Edit** next to the **Assignee** dropdown menu and -deselect the user from the list of assignees, or click **Unassigned**. - -### Alert system notes - -> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/3066) in GitLab 13.1. - -When you take action on an alert, this is logged as a system note, -which is visible in the Alert Details view. This gives you a linear -timeline of the alert's investigation and assignment history. - -The following actions will result in a system note: - -- [Updating the status of an alert](#update-an-alerts-status) -- [Creating an issue based on an alert](#create-an-issue-from-an-alert) -- [Assignment of an alert to a user](#update-an-alerts-assignee) - -![Alert Management Details View System Notes](img/alert_detail_system_notes_v13_1.png) - -### Create a To-Do from an Alert - -> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/3066) in GitLab 13.1. - -You can manually create [To-Do list items](../../user/todos.md) for yourself from the -Alert details screen, and view them later on your **To-Do List**. To add a To-Do: - -1. To display the list of current alerts, click - **{cloud-gear}** **Operations > Alerts**. -1. Select your desired alert to display its **Alert Management Details View**. -1. Click the **Add a To-Do** button in the right sidebar: - - ![Alert Management Details Add A To Do](img/alert_detail_add_todo_v13_1.png) - -Click the **To-Do** **{todo-done}** in the navigation bar to view your current To-Do list. - -![Alert Management Details Added to Do](img/alert_detail_added_todo_v13_1.png) - -### View an Alert's metrics data - -> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/217768) in GitLab 13.2. - -To view the metrics for an alert: - - 1. Sign in as a user with Developer or higher [permissions](../../user/permissions.md). - 1. Navigate to **{cloud-gear}** **Operations > Alerts**. - 1. Click the alert you want to view. - 1. Below the title of the alert, click the **Metrics** tab. - -![Alert Management Metrics View](img/alert_detail_metrics_v13_2.png) - -For GitLab-managed Prometheus instances, metrics data is automatically available -for the alert, making it easy to see surrounding behavior. See -[Managed Prometheus instances](../metrics/alerts.md#managed-prometheus-instances) -for information on setting up alerts. - -For externally-managed Prometheus instances, you can configure your alerting rules to -display a chart in the alert. See -[Embedding metrics based on alerts in incident issues](../metrics/embed.md#embedding-metrics-based-on-alerts-in-incident-issues) -for information on how to appropriately configure your alerting rules. See -[External Prometheus instances](../metrics/alerts.md#external-prometheus-instances) -for information on setting up alerts for your self-managed Prometheus instance. - -### View an Alert's logs - -> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/217768) in GitLab 13.3. - -To view the logs for an alert: - - 1. Sign in as a user with Developer or higher [permissions](../../user/permissions.md). - 1. Navigate to **{cloud-gear}** **Operations > Alerts**. - 1. Click the alert you want to view. - 1. Below the title of the alert, click the **Metrics** tab. - 1. Click the [menu](../metrics/dashboards/index.md#chart-context-menu) of the metric chart to view options. - 1. Click **View logs**. - -Read [View logs from metrics panel](#view-logs-from-metrics-panel) for additional information. - -## Use cases for assigning alerts - -Consider a team formed by different sections of monitoring, collaborating on a -single application. After an alert surfaces, it's extremely important to -route the alert to the team members who can address and resolve the alert. - -Assigning Alerts to multiple assignees eases collaboration and delegation. All -assignees are shown in your team's work-flows, and all assignees receive -notifications, simplifying communication and ownership of the alert. - -After completing their portion of investigating or fixing the alert, users can -unassign their account from the alert when their role is complete. -The [alerts status](#alert-management-statuses) can be updated to -reflect if the alert has been resolved. +## Alert notifications ### Slack Notifications @@ -341,36 +26,6 @@ You can be alerted via a Slack message when a new alert has been received. See the [Slack Notifications Service docs](../../user/project/integrations/slack.md) for information on how to set this up. -## Configure incidents - -> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/4925) in GitLab Ultimate 11.11. - -With Maintainer or higher [permissions](../../user/permissions.md), you can enable or disable Incident Management features in the GitLab user interface -to create issues when alerts are triggered: - -1. Navigate to **Settings > Operations > Incidents** and expand - **Incidents**: - - ![Incident Management Settings](img/incident_management_settings_v13_3.png) - -1. For GitLab versions 11.11 and greater, you can select the **Create an issue** - checkbox to create an issue based on your own - [issue templates](../../user/project/description_templates.md#creating-issue-templates). - For more information, see - [Trigger actions from alerts](../metrics/alerts.md#trigger-actions-from-alerts-ultimate) **(ULTIMATE)**. -1. To create issues from alerts, select the template in the **Issue Template** - select box. -1. To send [separate email notifications](#notify-developers-of-alerts) to users - with [Developer permissions](../../user/permissions.md), select - **Send a separate email notification to Developers**. -1. Click **Save changes**. - -Appropriately configured alerts include an -[embedded chart](../metrics/embed.md#embedding-metrics-based-on-alerts-in-incident-issues) -for the query corresponding to the alert. You can also configure GitLab to -[close issues](../metrics/alerts.md#trigger-actions-from-alerts-ultimate) -when you receive notification that the alert is resolved. - ### Notify developers of alerts GitLab can react to the alerts triggered from your applications and services @@ -379,58 +34,8 @@ sends these emails to [owners and maintainers](../../user/permissions.md) of the These emails contain details of the alert, and a link for more information. To send separate email notifications to users with -[Developer permissions](../../user/permissions.md), see [Configure incidents](#configure-incidents). - -## Incident List - -Incidents in GitLab are aggregated in the Incident List, available at -**Operations > Incidents**. This list displays all incidents in GitLab, with tabs -to display open incidents, closed incidents, and all incidents: - -![Incident list](img/incident_list.png) - -The list displays the following attributes: - -- **Incident title** -- **Date created** - in 'time ago' format. -- **Assignees** - the avatar of the user assigned to the incident. -- **Published** - Displays a green check mark (**{check-circle}**) if the incident is published - to a [Status Page](status_page.md). - -## Create an incident manually - -> [Moved](https://gitlab.com/gitlab-org/monitor/health/-/issues/24) to GitLab core in 13.3. - -For users with at least Developer [permissions](../../user/permissions.md), to create a Incident you can take any of the following actions: - -- Navigate to **Operations > Incidents** and click **Create Incident**. -- Create a new issue using the `incident` template available when creating it. -- Create a new issue and assign the `incident` label to it. - -![Incident List Create](img/incident_list_create_v13_3.png) - -## Configure PagerDuty integration - -> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/119018) in GitLab 13.3. - -You can set up a webhook with PagerDuty to automatically create a GitLab issue -for each PagerDuty incident. This configuration requires you to make changes -in both PagerDuty and GitLab: - -1. Sign in as a user with Maintainer [permissions](../../user/permissions.md). -1. Navigate to **Settings > Operations > Incidents** and expand **Incidents**. -1. Select the **PagerDuty integration** tab: - - ![PagerDuty incidents integration](img/pagerduty_incidents_integration_v13_3.png) - -1. Activate the integration, and save the changes in GitLab. -1. Copy the value of **Webhook URL** for use in a later step. -1. Follow the steps described in the - [PagerDuty documentation](https://support.pagerduty.com/docs/webhooks) - to add the webhook URL to a PagerDuty webhook integration. - -To confirm the integration is successful, trigger a test incident from PagerDuty to -confirm that a GitLab issue is created from the incident. +[Developer permissions](../../user/permissions.md), see +[Configure incidents](incidents.md#configure-incidents). ## Configure Prometheus alerts @@ -448,36 +53,6 @@ GitLab can accept alerts from any source through a generic webhook receiver. Whe [configuring the generic alerts integration](../../user/project/integrations/generic_alerts.md), GitLab creates a unique endpoint which receives a JSON-formatted, customizable payload. -## Embed metrics in incidents and issues - -You can embed metrics anywhere [GitLab Markdown](../../user/markdown.md) is used, such as descriptions, -comments on issues, and merge requests. Embedding metrics helps you share them -when discussing incidents or performance issues. You can output the dashboard directly -into any issue, merge request, epic, or any other Markdown text field in GitLab -by [copying and pasting the link to the metrics dashboard](../metrics/embed.md#embedding-gitlab-managed-kubernetes-metrics). - -You can embed both -[GitLab-hosted metrics](../metrics/embed.md) and -[Grafana metrics](../metrics/embed_grafana.md) -in incidents and issue templates. - -### Context menu - -You can view more details about an embedded metrics panel from the context menu. -To access the context menu, click the **{ellipsis_v}** **More actions** dropdown box -above the upper right corner of the panel. For a list of options, see -[Chart context menu](../metrics/dashboards/index.md#chart-context-menu). - -#### View logs from metrics panel - -> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/201846) in GitLab Ultimate 12.8. -> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/25455) to [GitLab Core](https://about.gitlab.com/pricing/) 12.9. - -Viewing logs from a metrics panel can be useful if you're triaging an application -incident and need to [explore logs](../metrics/dashboards/index.md#chart-context-menu) -from across your application. These logs help you understand what is affecting -your application's performance and resolve any problems. - ## Integrate incidents with Slack Slack slash commands allow you to control GitLab and view GitLab content without leaving Slack. diff --git a/doc/operations/metrics/dashboards/yaml.md b/doc/operations/metrics/dashboards/yaml.md index 4d1eec1b4f9..1fd95892f2e 100644 --- a/doc/operations/metrics/dashboards/yaml.md +++ b/doc/operations/metrics/dashboards/yaml.md @@ -89,8 +89,8 @@ is no longer used. | `id` | string | no | Used for associating dashboard metrics with database records. Must be unique across dashboard configuration files. Required for [alerting](../alerts.md) (support not yet enabled, see [relevant issue](https://gitlab.com/gitlab-org/gitlab/-/issues/27980)). | | `unit` | string | yes | Defines the unit of the query's return data. | | `label` | string | no, but highly encouraged | Defines the legend-label for the query. Should be unique within the panel's metrics. Can contain time series labels as interpolated variables. | -| `query` | string | yes if `query_range` is not defined | Defines the Prometheus query to be used to populate the chart/panel. If defined, the `query` endpoint of the [Prometheus API](https://prometheus.io/docs/prometheus/latest/querying/api/) will be utilized. | -| `query_range` | string | yes if `query` is not defined | Defines the Prometheus query to be used to populate the chart/panel. If defined, the `query_range` endpoint of the [Prometheus API](https://prometheus.io/docs/prometheus/latest/querying/api/) will be utilized. | +| `query` | string/number | yes if `query_range` is not defined | Defines the Prometheus query to be used to populate the chart/panel. If defined, the `query` endpoint of the [Prometheus API](https://prometheus.io/docs/prometheus/latest/querying/api/) will be utilized. | +| `query_range` | string/number | yes if `query` is not defined | Defines the Prometheus query to be used to populate the chart/panel. If defined, the `query_range` endpoint of the [Prometheus API](https://prometheus.io/docs/prometheus/latest/querying/api/) will be utilized. | | `step` | number | no, value is calculated if not defined | Defines query resolution step width in float number of seconds. Metrics on the same panel should use the same `step` value. | ## Dynamic labels @@ -156,21 +156,46 @@ and files with invalid syntax display **Metrics Dashboard YAML definition is inv When **Metrics Dashboard YAML definition is invalid** at least one of the following messages is displayed: -1. `dashboard: can't be blank` [learn more](#dashboard-top-level-properties) -1. `panel_groups: should be an array of panel_groups objects` [learn more](#dashboard-top-level-properties) -1. `group: can't be blank` [learn more](#panel-group-panel_groups-properties) -1. `panels: should be an array of panels objects` [learn more](#panel-group-panel_groups-properties) -1. `title: can't be blank` [learn more](#panel-panels-properties) -1. `metrics: should be an array of metrics objects` [learn more](#panel-panels-properties) -1. `query: can't be blank` [learn more](#metrics-metrics-properties) -1. `query_range: can't be blank` [learn more](#metrics-metrics-properties) -1. `unit: can't be blank` [learn more](#metrics-metrics-properties) -1. `YAML syntax: The parsed YAML is too big` - - This is displayed when the YAML file is larger than 1 MB. - -1. `YAML syntax: Invalid configuration format` - - This is displayed when the YAML file is empty or does not contain valid YAML. +1. `[location] is missing required keys: [list of missing keys]` - The entry at + `[location]` is missing a key, or a key has been mistyped. This + example returns the error `root is missing required keys: panel_groups`: + + ```yaml + dashboard: Important metrics + group_panels: + - ... + ``` + +1. `[data] at [location] is not of type: [type]` - The entry at `[location]` contains + `[data]` which type does not adhere to required types. This example returns the + error `'123' at /panel_groups/0/group is not of type: string`: + + ```yaml + dashboard: Environment metrics + panel_groups: + - group: 123 + panels: + ... + ``` + +1. `[data] at [location] is not one of: [types]` - The entry at `[location]` contains + `[data]` which is not included in the list of required values. This example returns + the error `'scatterplot-chart' at /panel_groups/0/panels/0/type is not one of: ["area-chart", "line-chart", "anomaly-chart", "bar", "column", "stacked-column", "single-stat", "heatmap"]`: + + ```yaml + dashboard: Environment metrics + panel_groups: + - group: Network + panels: + - title: Throughput + type: scatterplot-chart + y_label: Requests / Sec + ... + ``` + +1. `metric_id must be unique across a project` - At least two metrics entries have + the same `id` attribute, which [must be unique](#metrics-metrics-properties). +1. `The parsed YAML is too big` - The YAML file is larger than 1 MB. +1. `Invalid configuration format` - The YAML file is empty or does not contain valid YAML. Metrics Dashboard YAML definition validation information is also available as a [GraphQL API field](../../../api/graphql/reference/index.md#metricsdashboard) diff --git a/doc/user/project/integrations/generic_alerts.md b/doc/user/project/integrations/generic_alerts.md index 3abe18381e4..dc6aa40ea82 100644 --- a/doc/user/project/integrations/generic_alerts.md +++ b/doc/user/project/integrations/generic_alerts.md @@ -116,7 +116,7 @@ In GitLab versions 13.2 and greater, GitLab groups alerts based on their payload When an incoming alert contains the same payload as another alert (excluding the `start_time` and `hosts` attributes), GitLab groups these alerts together and displays a counter on the -[Alert Management List](../../../operations/incident_management/index.md#alert-management-list) +[Alert Management List](../../../operations/incident_management/incidents.md) and details pages. If the existing alert is already `resolved`, then a new alert will be created instead. diff --git a/doc/user/project/integrations/img/project_integrations_v13_3.png b/doc/user/project/integrations/img/project_integrations_v13_3.png Binary files differnew file mode 100644 index 00000000000..9c925d32441 --- /dev/null +++ b/doc/user/project/integrations/img/project_integrations_v13_3.png diff --git a/doc/user/project/integrations/img/project_services.png b/doc/user/project/integrations/img/project_services.png Binary files differdeleted file mode 100644 index 5fed38a349c..00000000000 --- a/doc/user/project/integrations/img/project_services.png +++ /dev/null diff --git a/doc/user/project/integrations/overview.md b/doc/user/project/integrations/overview.md index 752c6a6d897..f179cd6b98e 100644 --- a/doc/user/project/integrations/overview.md +++ b/doc/user/project/integrations/overview.md @@ -18,7 +18,7 @@ You can find the available integrations under your project's There are more than 20 integrations to integrate with. Click on the one that you want to configure. -![Integrations list](img/project_services.png) +![Integrations list](img/project_integrations_v13_3.png) ## Integrations listing diff --git a/lib/gitlab/database/partitioning/partition_creator.rb b/lib/gitlab/database/partitioning/partition_creator.rb index ed3cdc4931c..4c1b13fe3b5 100644 --- a/lib/gitlab/database/partitioning/partition_creator.rb +++ b/lib/gitlab/database/partitioning/partition_creator.rb @@ -24,8 +24,6 @@ module Gitlab end def create_partitions - return unless Feature.enabled?(:postgres_dynamic_partition_creation, default_enabled: true) - Gitlab::AppLogger.info("Checking state of dynamic postgres partitions") models.each do |model| diff --git a/lib/gitlab/experimentation.rb b/lib/gitlab/experimentation.rb index 05a0c8f0bcf..9908369426a 100644 --- a/lib/gitlab/experimentation.rb +++ b/lib/gitlab/experimentation.rb @@ -59,6 +59,9 @@ module Gitlab }, contact_sales_btn_in_app: { tracking_category: 'Growth::Conversion::Experiment::ContactSalesInApp' + }, + customize_homepage: { + tracking_category: 'Growth::Expansion::Experiment::CustomizeHomepage' } }.freeze diff --git a/lib/gitlab/i18n/html_todo.yml b/lib/gitlab/i18n/html_todo.yml index 1ad036f79e2..bfd96ba8579 100644 --- a/lib/gitlab/i18n/html_todo.yml +++ b/lib/gitlab/i18n/html_todo.yml @@ -76,6 +76,102 @@ - "< 1 saat" - "< 1 Stunde" - "< 1시간" + +# +# Strings below are fixed in the source code but the translations are still present in CrowdIn so the +# locale files will fail the linter. They can be deleted after next CrowdIn sync, likely in: +# https://gitlab.com/gitlab-org/gitlab/-/issues/226008 +# + +"This commit was signed with an <strong>unverified</strong> signature.": + plural_id: + translations: + - "このコミットは<strong>検証されていない</strong> 署名でサインされています。" + - "Этот коммит был подписан <strong>непроверенной</strong> подписью." + - "此提交使用 <strong>未经验证的</strong> 签名进行签名。" + - "Цей коміт підписано <strong>неперевіреним</strong> підписом." + - "Esta commit fue firmado con una firma <strong>no verificada</strong>." +"This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user.": + plural_id: + translations: + - "このコミットは <strong>検証済み</strong> の署名でサインされており、このコミッターのメールは同じユーザーのものであることが検証されています。" + - "Это коммит был подписан <strong>верифицированной</strong> подписью и коммитер подтвердил, что адрес почты принадлежит ему." + - "此提交使用 <strong>已验证</strong> 的签名进行签名,并且已验证提交者的电子邮件属于同一用户。" + - "Цей коміт підписано <strong>перевіреним</strong> підписом і адреса електронної пошти комітера гарантовано належить тому самому користувачу." + - "Este commit fue firmado con una firma verificada, y <strong>se ha verificado</strong> que la dirección de correo electrónico del committer y la firma pertenecen al mismo usuario." +"Branch <strong>%{branch_name}</strong> was created. To set up auto deploy, choose a GitLab CI Yaml template and commit your changes. %{link_to_autodeploy_doc}": + plural_id: + translations: + - "分支 <strong>%{branch_name}</strong> 已創建。如需設置自動部署, 請選擇合適的 GitLab CI Yaml 模板併提交更改。%{link_to_autodeploy_doc}" + - "O branch <strong>%{branch_name}</strong> foi criado. Para configurar o deploy automático, selecione um modelo de Yaml do GitLab CI e commit suas mudanças. %{link_to_autodeploy_doc}" + - "<strong>%{branch_name}</strong> ブランチが作成されました。自動デプロイを設定するには、GitLab CI Yaml テンプレートを選択して、変更をコミットしてください。 %{link_to_autodeploy_doc}" + - "La branch <strong>%{branch_name}</strong> è stata creata. Per impostare un rilascio automatico scegli un template CI di Gitlab e committa le tue modifiche %{link_to_autodeploy_doc}" + - "O ramo <strong>%{branch_name}</strong> foi criado. Para configurar a implantação automática, seleciona um modelo de Yaml do GitLab CI e envia as tuas alterações. %{link_to_autodeploy_doc}" + - "Ветка <strong>%{branch_name}</strong> создана. Для настройки автоматического развертывания выберите YAML-шаблон для GitLab CI и зафиксируйте свои изменения. %{link_to_autodeploy_doc}" + - "已创建分支 <strong>%{branch_name}</strong> 。如需设置自动部署, 请选择合适的 GitLab CI Yaml 模板并提交更改。%{link_to_autodeploy_doc}" + - "Гілка <strong>%{branch_name}</strong> створена. Для настройки автоматичного розгортання виберіть GitLab CI Yaml-шаблон і закомітьте зміни. %{link_to_autodeploy_doc}" + - "Клонът <strong>%{branch_name}</strong> беше създаден. За да настроите автоматичното внедряване, изберете Yaml шаблон за GitLab CI и подайте промените си. %{link_to_autodeploy_doc}" + - "Branch <strong>%{branch_name}</strong> wurde erstellt. Um die automatische Bereitstellung einzurichten, wähle eine GitLab CI Yaml Vorlage und committe deine Änderungen. %{link_to_autodeploy_doc}" + - "<strong>%{branch_name}</strong> 브랜치가 생성되었습니다. 자동 배포를 설정하려면 GitLab CI Yaml 템플릿을 선택하고 변경 사항을 적용하십시오. %{link_to_autodeploy_doc}" + - "La branĉo <strong>%{branch_name}</strong> estis kreita. Por agordi aŭtomatan disponigadon, bonvolu elekti Yaml-ŝablonon por GitLab CI kaj enmeti viajn ŝanĝojn. %{link_to_autodeploy_doc}" + - "La branche <strong>%{branch_name}</strong> a été créée. Pour mettre en place le déploiement automatisé, sélectionnez un modèle de fichier YAML pour l’intégration continue (CI) de GitLab, et validez les modifications. %{link_to_autodeploy_doc}" + - "La rama <strong>%{branch_name}</strong> fue creada. Para configurar el auto despliegue, escoge una plantilla Yaml para GitLab CI y envía tus cambios. %{link_to_autodeploy_doc}" +"GitLabPages|GitLab Pages are disabled for this project. You can enable them on your project's %{strong_start}Settings > General > Visibility%{strong_end} page.": + plural_id: + translations: + - "GitLab Pagesはこのプロジェクトでは無効になっています。 プロジェクトの%{strong_start} 設定> 全般> 可視性%{strong_end}ページで有効にできます。" + - "GitLab Pages отключены для этого проекта. Вы можете включить в поле %{strong_start}Настройки > Общие > Видимость%{strong_end} вашего проекта." + - "此项目禁用GitLab Pages。您可以在您的项目的%{strong_start}设置 > 常规 > 可见性%{strong_end} 页面启用。" + - "GitLab Pages вимкнено для цього проєкту. Ви можете їх увімкнути перейшовши на сторінку проєкту %{strong_start}Налаштування > Загальні > Видимість%{strong_end}." + - "Las páginas de GitLab están deshabilitadas para este proyecto. Puede habilitarlas en los ajustes %{strong_start} de su proyecto > General > Visibilidad%{strong_end}." +"You can invite a new member to <strong>%{project_name}</strong> or invite another group.": + plural_id: + translations: + - "新しいメンバーを<strong>%{project_name} </strong>に招待するか、別のグループを招待することができます。" + - "Podes convidar um novo para <strong>%{project_name}</strong> ou convidar outro grupo." + - "邀请新成员或另一个群组加入<strong>%{project_name}</strong>。" + - "Puede invitar a un nuevo miembro a <strong>%{project_name}</strong> o invitar a otro grupo." + - "<strong>%{project_name}</strong> projesine yeni bir üye davet edebilir veya başka bir grubu davet edebilirsiniz." + - "Вы можете пригласить нового участника в <strong>%{project_name}</strong> или пригласить другую группу." + - "Ви можете запросити нового учасника до <strong>%{project_name}</strong> або запросити іншу групу." +"You can invite a new member to <strong>%{project_name}</strong>.": + plural_id: + translations: + - "新しいメンバーを<strong>%{project_name} </strong>に招待できます。" + - "Podes convidar um novo membro para <strong>%{project_name}</strong>." + - "邀请新成员加入<strong>%{project_name}</strong>。" + - "Puedes invitar a un nuevo miembro a <strong>%{project_name}</strong>." + - "<strong>%{project_name}</strong> projesine yeni bir üye davet edebilirsiniz." + - "Вы можете пригласить нового участника в <strong>%{project_name}</strong>." + - "Ви можете запросити нового учасника до <strong>%{project_name}</strong>." +"You can invite another group to <strong>%{project_name}</strong>.": + plural_id: + translations: + - "他のグループを<strong>%{project_name} </strong>に招待できます。" + - "Podes convidar outro grupo para <strong>%{project_name}</strong>." + - "您可以邀请另一个群组加入<strong>%{project_name}</strong>。" + - "Ви можете запросити нову групу до <strong>%{project_name}</strong>." + - "Puedes invitar a otro grupo a <strong>%{project_name}</strong>." +"Example: <code>192.168.0.0/24</code>. %{read_more_link}.": + plural_id: + translations: +"Note that PostgreSQL %{pg_version_upcoming} will become the minimum required version in GitLab %{gl_version_upcoming} (%{gl_version_upcoming_date}). Please consider upgrading your environment to a supported PostgreSQL version soon, see <a href=\\\"%{pg_version_upcoming_url}\\\">the related epic</a> for details.": + plural_id: + translations: +"Authorize <strong>%{user}</strong> to use your account?": + plural_id: + translations: +"DeployFreeze|Specify times when deployments are not allowed for an environment. The <code>gitlab-ci.yml</code> file must be updated to make deployment jobs aware of the %{freeze_period_link_start}freeze period%{freeze_period_link_end}.": + plural_id: + translations: +"<project name>": + translations: + - "<название проекта>" + - "<project name>" + - "<proje adı>" + - "<naziv projekta>" + - "<ім’я проєкту>" + - "<프로젝트 이름>" "<strong>Deletes</strong> source branch": plural_id: translations: @@ -217,99 +313,3 @@ - "Vous êtes sur le point de d’activer la confidentialité. Cela signifie que seuls les membres de l’équipe avec <strong>au moins un accès en tant que rapporteur</strong> seront en mesure de voir et de laisser des commentaires sur le ticket." - "Va a activar la confidencialidad. Esto significa que solo los miembros del equipo con como mínimo,<strong>acceso como Reporter</strong> podrán ver y dejar comentarios sobre la incidencia." - "あなたは非公開設定をオンにしようとしています。これは、最低でも<strong>報告権限</strong>を持ったチームメンバーのみが課題を表示したりコメントを残したりすることができるようになるということです。" - -# -# Strings below are fixed in the source code but the translations are still present in CrowdIn so the -# locale files will fail the linter. They can be deleted after next CrowdIn sync, likely in: -# https://gitlab.com/gitlab-org/gitlab/-/issues/226008 -# - -"This commit was signed with an <strong>unverified</strong> signature.": - plural_id: - translations: - - "このコミットは<strong>検証されていない</strong> 署名でサインされています。" - - "Этот коммит был подписан <strong>непроверенной</strong> подписью." - - "此提交使用 <strong>未经验证的</strong> 签名进行签名。" - - "Цей коміт підписано <strong>неперевіреним</strong> підписом." - - "Esta commit fue firmado con una firma <strong>no verificada</strong>." -"This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user.": - plural_id: - translations: - - "このコミットは <strong>検証済み</strong> の署名でサインされており、このコミッターのメールは同じユーザーのものであることが検証されています。" - - "Это коммит был подписан <strong>верифицированной</strong> подписью и коммитер подтвердил, что адрес почты принадлежит ему." - - "此提交使用 <strong>已验证</strong> 的签名进行签名,并且已验证提交者的电子邮件属于同一用户。" - - "Цей коміт підписано <strong>перевіреним</strong> підписом і адреса електронної пошти комітера гарантовано належить тому самому користувачу." - - "Este commit fue firmado con una firma verificada, y <strong>se ha verificado</strong> que la dirección de correo electrónico del committer y la firma pertenecen al mismo usuario." -"Branch <strong>%{branch_name}</strong> was created. To set up auto deploy, choose a GitLab CI Yaml template and commit your changes. %{link_to_autodeploy_doc}": - plural_id: - translations: - - "分支 <strong>%{branch_name}</strong> 已創建。如需設置自動部署, 請選擇合適的 GitLab CI Yaml 模板併提交更改。%{link_to_autodeploy_doc}" - - "O branch <strong>%{branch_name}</strong> foi criado. Para configurar o deploy automático, selecione um modelo de Yaml do GitLab CI e commit suas mudanças. %{link_to_autodeploy_doc}" - - "<strong>%{branch_name}</strong> ブランチが作成されました。自動デプロイを設定するには、GitLab CI Yaml テンプレートを選択して、変更をコミットしてください。 %{link_to_autodeploy_doc}" - - "La branch <strong>%{branch_name}</strong> è stata creata. Per impostare un rilascio automatico scegli un template CI di Gitlab e committa le tue modifiche %{link_to_autodeploy_doc}" - - "O ramo <strong>%{branch_name}</strong> foi criado. Para configurar a implantação automática, seleciona um modelo de Yaml do GitLab CI e envia as tuas alterações. %{link_to_autodeploy_doc}" - - "Ветка <strong>%{branch_name}</strong> создана. Для настройки автоматического развертывания выберите YAML-шаблон для GitLab CI и зафиксируйте свои изменения. %{link_to_autodeploy_doc}" - - "已创建分支 <strong>%{branch_name}</strong> 。如需设置自动部署, 请选择合适的 GitLab CI Yaml 模板并提交更改。%{link_to_autodeploy_doc}" - - "Гілка <strong>%{branch_name}</strong> створена. Для настройки автоматичного розгортання виберіть GitLab CI Yaml-шаблон і закомітьте зміни. %{link_to_autodeploy_doc}" - - "Клонът <strong>%{branch_name}</strong> беше създаден. За да настроите автоматичното внедряване, изберете Yaml шаблон за GitLab CI и подайте промените си. %{link_to_autodeploy_doc}" - - "Branch <strong>%{branch_name}</strong> wurde erstellt. Um die automatische Bereitstellung einzurichten, wähle eine GitLab CI Yaml Vorlage und committe deine Änderungen. %{link_to_autodeploy_doc}" - - "<strong>%{branch_name}</strong> 브랜치가 생성되었습니다. 자동 배포를 설정하려면 GitLab CI Yaml 템플릿을 선택하고 변경 사항을 적용하십시오. %{link_to_autodeploy_doc}" - - "La branĉo <strong>%{branch_name}</strong> estis kreita. Por agordi aŭtomatan disponigadon, bonvolu elekti Yaml-ŝablonon por GitLab CI kaj enmeti viajn ŝanĝojn. %{link_to_autodeploy_doc}" - - "La branche <strong>%{branch_name}</strong> a été créée. Pour mettre en place le déploiement automatisé, sélectionnez un modèle de fichier YAML pour l’intégration continue (CI) de GitLab, et validez les modifications. %{link_to_autodeploy_doc}" - - "La rama <strong>%{branch_name}</strong> fue creada. Para configurar el auto despliegue, escoge una plantilla Yaml para GitLab CI y envía tus cambios. %{link_to_autodeploy_doc}" -"GitLabPages|GitLab Pages are disabled for this project. You can enable them on your project's %{strong_start}Settings > General > Visibility%{strong_end} page.": - plural_id: - translations: - - "GitLab Pagesはこのプロジェクトでは無効になっています。 プロジェクトの%{strong_start} 設定> 全般> 可視性%{strong_end}ページで有効にできます。" - - "GitLab Pages отключены для этого проекта. Вы можете включить в поле %{strong_start}Настройки > Общие > Видимость%{strong_end} вашего проекта." - - "此项目禁用GitLab Pages。您可以在您的项目的%{strong_start}设置 > 常规 > 可见性%{strong_end} 页面启用。" - - "GitLab Pages вимкнено для цього проєкту. Ви можете їх увімкнути перейшовши на сторінку проєкту %{strong_start}Налаштування > Загальні > Видимість%{strong_end}." - - "Las páginas de GitLab están deshabilitadas para este proyecto. Puede habilitarlas en los ajustes %{strong_start} de su proyecto > General > Visibilidad%{strong_end}." -"You can invite a new member to <strong>%{project_name}</strong> or invite another group.": - plural_id: - translations: - - "新しいメンバーを<strong>%{project_name} </strong>に招待するか、別のグループを招待することができます。" - - "Podes convidar um novo para <strong>%{project_name}</strong> ou convidar outro grupo." - - "邀请新成员或另一个群组加入<strong>%{project_name}</strong>。" - - "Puede invitar a un nuevo miembro a <strong>%{project_name}</strong> o invitar a otro grupo." - - "<strong>%{project_name}</strong> projesine yeni bir üye davet edebilir veya başka bir grubu davet edebilirsiniz." - - "Вы можете пригласить нового участника в <strong>%{project_name}</strong> или пригласить другую группу." - - "Ви можете запросити нового учасника до <strong>%{project_name}</strong> або запросити іншу групу." -"You can invite a new member to <strong>%{project_name}</strong>.": - plural_id: - translations: - - "新しいメンバーを<strong>%{project_name} </strong>に招待できます。" - - "Podes convidar um novo membro para <strong>%{project_name}</strong>." - - "邀请新成员加入<strong>%{project_name}</strong>。" - - "Puedes invitar a un nuevo miembro a <strong>%{project_name}</strong>." - - "<strong>%{project_name}</strong> projesine yeni bir üye davet edebilirsiniz." - - "Вы можете пригласить нового участника в <strong>%{project_name}</strong>." - - "Ви можете запросити нового учасника до <strong>%{project_name}</strong>." -"You can invite another group to <strong>%{project_name}</strong>.": - plural_id: - translations: - - "他のグループを<strong>%{project_name} </strong>に招待できます。" - - "Podes convidar outro grupo para <strong>%{project_name}</strong>." - - "您可以邀请另一个群组加入<strong>%{project_name}</strong>。" - - "Ви можете запросити нову групу до <strong>%{project_name}</strong>." - - "Puedes invitar a otro grupo a <strong>%{project_name}</strong>." -"Example: <code>192.168.0.0/24</code>. %{read_more_link}.": - plural_id: - translations: -"Note that PostgreSQL %{pg_version_upcoming} will become the minimum required version in GitLab %{gl_version_upcoming} (%{gl_version_upcoming_date}). Please consider upgrading your environment to a supported PostgreSQL version soon, see <a href=\\\"%{pg_version_upcoming_url}\\\">the related epic</a> for details.": - plural_id: - translations: -"Authorize <strong>%{user}</strong> to use your account?": - plural_id: - translations: -"DeployFreeze|Specify times when deployments are not allowed for an environment. The <code>gitlab-ci.yml</code> file must be updated to make deployment jobs aware of the %{freeze_period_link_start}freeze period%{freeze_period_link_end}.": - plural_id: - translations: -"<project name>": - translations: - - "<название проекта>" - - "<project name>" - - "<proje adı>" - - "<naziv projekta>" - - "<ім’я проєкту>" - - "<프로젝트 이름>" diff --git a/lib/gitlab/metrics/dashboard/stages/metric_endpoint_inserter.rb b/lib/gitlab/metrics/dashboard/stages/metric_endpoint_inserter.rb index 944977a4bc8..dd85bd0beb1 100644 --- a/lib/gitlab/metrics/dashboard/stages/metric_endpoint_inserter.rb +++ b/lib/gitlab/metrics/dashboard/stages/metric_endpoint_inserter.rb @@ -47,7 +47,7 @@ module Gitlab # We need to remove any newlines since our UrlBlocker does not allow # multiline URLs. - query.squish + query.to_s.squish end end end diff --git a/lib/gitlab/metrics/dashboard/validator.rb b/lib/gitlab/metrics/dashboard/validator.rb index 8edd9c397f9..a2450c59886 100644 --- a/lib/gitlab/metrics/dashboard/validator.rb +++ b/lib/gitlab/metrics/dashboard/validator.rb @@ -8,20 +8,18 @@ module Gitlab class << self def validate(content, schema_path = DASHBOARD_SCHEMA_PATH, dashboard_path: nil, project: nil) - errors = _validate(content, schema_path, dashboard_path: dashboard_path, project: project) - errors.empty? + errors(content, schema_path, dashboard_path: dashboard_path, project: project).empty? end def validate!(content, schema_path = DASHBOARD_SCHEMA_PATH, dashboard_path: nil, project: nil) - errors = _validate(content, schema_path, dashboard_path: dashboard_path, project: project) + errors = errors(content, schema_path, dashboard_path: dashboard_path, project: project) errors.empty? || raise(errors.first) end - private - - def _validate(content, schema_path, dashboard_path: nil, project: nil) - client = Validator::Client.new(content, schema_path, dashboard_path: dashboard_path, project: project) - client.execute + def errors(content, schema_path = DASHBOARD_SCHEMA_PATH, dashboard_path: nil, project: nil) + Validator::Client + .new(content, schema_path, dashboard_path: dashboard_path, project: project) + .execute end end end diff --git a/lib/gitlab/metrics/dashboard/validator/client.rb b/lib/gitlab/metrics/dashboard/validator/client.rb index c63415abcfc..588c677ca28 100644 --- a/lib/gitlab/metrics/dashboard/validator/client.rb +++ b/lib/gitlab/metrics/dashboard/validator/client.rb @@ -46,7 +46,7 @@ module Gitlab def validate_against_schema schemer.validate(content).map do |error| - Errors::SchemaValidationError.new(error) + ::Gitlab::Metrics::Dashboard::Validator::Errors::SchemaValidationError.new(error) end end end diff --git a/lib/gitlab/metrics/dashboard/validator/schemas/metric.json b/lib/gitlab/metrics/dashboard/validator/schemas/metric.json index 5986ae7aa3b..13831b77e3e 100644 --- a/lib/gitlab/metrics/dashboard/validator/schemas/metric.json +++ b/lib/gitlab/metrics/dashboard/validator/schemas/metric.json @@ -9,8 +9,8 @@ }, "unit": { "type": "string" }, "label": { "type": "string" }, - "query": { "type": "string" }, - "query_range": { "type": "string" }, + "query": { "type": ["string", "number"] }, + "query_range": { "type": ["string", "number"] }, "step": { "type": "number" } } } diff --git a/lib/gitlab/metrics/dashboard/validator/schemas/panel.json b/lib/gitlab/metrics/dashboard/validator/schemas/panel.json index 011eef53e40..2ae9608036e 100644 --- a/lib/gitlab/metrics/dashboard/validator/schemas/panel.json +++ b/lib/gitlab/metrics/dashboard/validator/schemas/panel.json @@ -4,7 +4,7 @@ "properties": { "type": { "type": "string", - "enum": ["area-chart", "anomaly-chart", "bar", "column", "stacked-column", "single-stat", "heatmap"], + "enum": ["area-chart", "line-chart", "anomaly-chart", "bar", "column", "stacked-column", "single-stat", "heatmap", "gauge"], "default": "area-chart" }, "title": { "type": "string" }, diff --git a/locale/gitlab.pot b/locale/gitlab.pot index fa746ee8af8..64f705afa3d 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -697,6 +697,9 @@ msgstr "" msgid "%{state} epics" msgstr "" +msgid "%{strongStart}Deletes%{strongEnd} source branch" +msgstr "" + msgid "%{strongStart}Note:%{strongEnd} Once a custom stage has been added you can re-order stages by dragging them into the desired position." msgstr "" @@ -1075,9 +1078,6 @@ msgstr "" msgid "< 1 hour" msgstr "" -msgid "<strong>Deletes</strong> source branch" -msgstr "" - msgid "A 'Runner' is a process which runs a job. You can set up as many Runners as you need." msgstr "" @@ -3761,7 +3761,7 @@ msgstr "" msgid "Badges|This project has no badges" msgstr "" -msgid "Badges|You are going to delete this badge. Deleted badges <strong>cannot</strong> be restored." +msgid "Badges|You are going to delete this badge. Deleted badges %{strongStart}cannot%{strongEnd} be restored." msgstr "" msgid "Badges|Your badges" @@ -5117,9 +5117,6 @@ msgstr "" msgid "ClusterAgent|You have insufficient permissions to create a cluster agent for this project" msgstr "" -msgid "ClusterIntegration| This will permanently delete the following resources: <ul> <li>All installed applications and related resources</li> <li>The <code>gitlab-managed-apps</code> namespace</li> <li>Any project namespaces</li> <li><code>clusterroles</code></li> <li><code>clusterrolebindings</code></li> </ul>" -msgstr "" - msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster" msgstr "" @@ -5174,6 +5171,9 @@ msgstr "" msgid "ClusterIntegration|All data will be deleted and cannot be restored." msgstr "" +msgid "ClusterIntegration|All installed applications and related resources" +msgstr "" + msgid "ClusterIntegration|Allow GitLab to manage namespace and service accounts for this cluster." msgstr "" @@ -5198,6 +5198,9 @@ msgstr "" msgid "ClusterIntegration|An error occurred while trying to fetch zone machine types: %{error}" msgstr "" +msgid "ClusterIntegration|Any project namespaces" +msgstr "" + msgid "ClusterIntegration|Any running pipelines will be canceled." msgstr "" @@ -5870,6 +5873,9 @@ msgstr "" msgid "ClusterIntegration|Subnets" msgstr "" +msgid "ClusterIntegration|The %{gitlabNamespace} namespace" +msgstr "" + msgid "ClusterIntegration|The Amazon Resource Name (ARN) associated with your role. If you do not have a provision role, first create one on %{startAwsLink}Amazon Web Services %{externalLinkIcon}%{endLink} using the above account and external IDs. %{startMoreInfoLink}More information%{endLink}" msgstr "" @@ -5909,15 +5915,27 @@ msgstr "" msgid "ClusterIntegration|This option will allow you to install applications on RBAC clusters." msgstr "" +msgid "ClusterIntegration|This project does not have billing enabled. To create a cluster, %{linkToBillingStart}enable billing%{linkToBillingEnd} and try again." +msgstr "" + +msgid "ClusterIntegration|This will permanently delete the following resources:" +msgstr "" + msgid "ClusterIntegration|To access your application after deployment, point a wildcard DNS to the Knative Endpoint." msgstr "" +msgid "ClusterIntegration|To create a cluster, first create a project on %{docsLinkStart}Google Cloud Platform%{docsLinkEnd}." +msgstr "" + msgid "ClusterIntegration|To remove your integration and resources, type %{clusterName} to confirm:" msgstr "" msgid "ClusterIntegration|To remove your integration, type %{clusterName} to confirm:" msgstr "" +msgid "ClusterIntegration|To use a new project, first create one on %{docsLinkStart}Google Cloud Platform%{docsLinkEnd}." +msgstr "" + msgid "ClusterIntegration|Uninstall %{appTitle}" msgstr "" @@ -5942,6 +5960,9 @@ msgstr "" msgid "ClusterIntegration|We could not verify that one of your projects on GCP has billing enabled. Please try again." msgstr "" +msgid "ClusterIntegration|We were unable to fetch any projects. Ensure that you have a project on %{docsLinkStart}Google Cloud Platform%{docsLinkEnd}." +msgstr "" + msgid "ClusterIntegration|With a Kubernetes cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way." msgstr "" @@ -6373,7 +6394,7 @@ msgstr "" msgid "Configure Tracing" msgstr "" -msgid "Configure a <code>.gitlab-webide.yml</code> file in the <code>.gitlab</code> directory to start using the Web Terminal. %{helpStart}Learn more.%{helpEnd}" +msgid "Configure a %{codeStart}.gitlab-webide.yml%{codeEnd} file in the %{codeStart}.gitlab%{codeEnd} directory to start using the Web Terminal. %{helpStart}Learn more.%{helpEnd}" msgstr "" msgid "Configure automatic git checks and housekeeping on repositories." @@ -7429,6 +7450,15 @@ msgstr "" msgid "Customize your pipeline configuration." msgstr "" +msgid "CustomizeHomepageBanner|Do you want to customize this page?" +msgstr "" + +msgid "CustomizeHomepageBanner|Go to preferences" +msgstr "" + +msgid "CustomizeHomepageBanner|This page shows a list of your projects by default but it can be changed to show projects' activity, groups, your to-do list, assigned issues, assigned merge requests, and more. You can change this under \"Homepage content\" in your preferences" +msgstr "" + msgid "Cycle Time" msgstr "" @@ -8069,8 +8099,8 @@ msgid_plural "Depends on %d merge requests being merged" msgstr[0] "" msgstr[1] "" -msgid "Depends on <strong>%d closed</strong> merge request." -msgid_plural "Depends on <strong>%d closed</strong> merge requests." +msgid "Depends on %{strongStart}%{closedCount} closed%{strongEnd} merge request." +msgid_plural "Depends on %{strongStart}%{closedCount} closed%{strongEnd} merge requests." msgstr[0] "" msgstr[1] "" @@ -11644,7 +11674,7 @@ msgstr "" msgid "Go to %{link_to_google_takeout}." msgstr "" -msgid "Go to <strong>Issues</strong> > <strong>Boards</strong> to access your personalized learning issue board." +msgid "Go to %{strongStart}Issues%{strongEnd} > %{strongStart}Boards%{strongEnd} to access your personalized learning issue board." msgstr "" msgid "Go to Webhooks" @@ -13948,7 +13978,7 @@ msgstr "" msgid "Labels can be applied to issues and merge requests." msgstr "" -msgid "Labels|<span>Promote label</span> %{labelTitle} <span>to Group Label?</span>" +msgid "Labels|%{spanStart}Promote label%{spanEnd} %{labelTitle} %{spanStart}to Group Label?%{spanEnd}" msgstr "" msgid "Labels|Promote Label" @@ -14516,7 +14546,7 @@ msgstr "" msgid "Lock the discussion" msgstr "" -msgid "Lock this %{issuableDisplayName}? Only <strong>project members</strong> will be able to comment." +msgid "Lock this %{issuableDisplayName}? Only %{strongStart}project members%{strongEnd} will be able to comment." msgstr "" msgid "Lock to current projects" @@ -19534,9 +19564,6 @@ msgstr "" msgid "PrometheusService|%{exporters} with %{metrics} were found" msgstr "" -msgid "PrometheusService|<p class=\"text-tertiary\">No <a href=\"%{docsUrl}\">common metrics</a> were found</p>" -msgstr "" - msgid "PrometheusService|Active" msgstr "" @@ -19594,6 +19621,9 @@ msgstr "" msgid "PrometheusService|New metric" msgstr "" +msgid "PrometheusService|No %{docsUrlStart}common metrics%{docsUrlEnd} were found" +msgstr "" + msgid "PrometheusService|No custom metrics have been created. Create one using the button above" msgstr "" @@ -25193,9 +25223,6 @@ msgstr "" msgid "This project does not have a wiki homepage yet" msgstr "" -msgid "This project does not have billing enabled. To create a cluster, <a href=%{linkToBilling} target=\"_blank\" rel=\"noopener noreferrer\">enable billing <i class=\"fa fa-external-link\" aria-hidden=\"true\"></i></a> and try again." -msgstr "" - msgid "This project has no active access tokens." msgstr "" @@ -26229,7 +26256,7 @@ msgstr "" msgid "Unlock the discussion" msgstr "" -msgid "Unlock this %{issuableDisplayName}? <strong>Everyone</strong> will be able to comment." +msgid "Unlock this %{issuableDisplayName}? %{strongStart}Everyone%{strongEnd} will be able to comment." msgstr "" msgid "Unlocked" @@ -27849,10 +27876,10 @@ msgstr "" msgid "You are going to transfer %{project_full_name} to another owner. Are you ABSOLUTELY sure?" msgstr "" -msgid "You are going to turn off the confidentiality. This means %{accessLevel} will be able to see and leave a comment on this %{issuableType}." +msgid "You are going to turn off the confidentiality. This means %{strongStart}everyone%{strongEnd} will be able to see and leave a comment on this %{issuableType}." msgstr "" -msgid "You are going to turn on the confidentiality. This means that only team members with %{accessLevel} are able to see and leave comments on the %{issuableType}." +msgid "You are going to turn on the confidentiality. This means that only team members with %{strongStart}at least Reporter access%{strongEnd} are able to see and leave comments on the %{issuableType}." msgstr "" msgid "You are not allowed to push into this branch. Create another branch or open a merge request." @@ -28580,9 +28607,6 @@ msgstr "" msgid "assign yourself" msgstr "" -msgid "at least Reporter access" -msgstr "" - msgid "at risk" msgstr "" @@ -28973,9 +28997,6 @@ msgstr "" msgid "estimateCommand|%{slash_command} will update the estimated time with the latest command." msgstr "" -msgid "everyone" -msgstr "" - msgid "exceeds the limit of %{bytes} bytes" msgstr "" @@ -29940,6 +29961,9 @@ msgstr "" msgid "vulnerability|Add comment & dismiss" msgstr "" +msgid "vulnerability|Add comment and dismiss" +msgstr "" + msgid "vulnerability|Dismiss vulnerability" msgstr "" diff --git a/spec/controllers/root_controller_spec.rb b/spec/controllers/root_controller_spec.rb index 9eefbcb0835..1db99a09404 100644 --- a/spec/controllers/root_controller_spec.rb +++ b/spec/controllers/root_controller_spec.rb @@ -122,6 +122,30 @@ RSpec.describe RootController do expect(response).to render_template 'dashboard/projects/index' end + + context 'when experiment is enabled' do + before do + stub_experiment_for_user(customize_homepage: true) + end + + it 'renders the default dashboard' do + get :index + + expect(assigns[:customize_homepage]).to be true + end + end + + context 'when experiment not enabled' do + before do + stub_experiment(customize_homepage: false) + end + + it 'renders the default dashboard' do + get :index + + expect(assigns[:customize_homepage]).to be false + end + end end end end diff --git a/spec/features/merge_request/user_posts_notes_spec.rb b/spec/features/merge_request/user_posts_notes_spec.rb index e2d218ed752..3526f6f75a5 100644 --- a/spec/features/merge_request/user_posts_notes_spec.rb +++ b/spec/features/merge_request/user_posts_notes_spec.rb @@ -95,20 +95,32 @@ RSpec.describe 'Merge request > User posts notes', :js do end end - describe 'reply on a deleted conversation' do - before do - visit project_merge_request_path(project, merge_request) - end - - it 'shows an error message' do + describe 'replying to a comment' do + it 'makes the discussion resolvable' do find('.js-reply-button').click - note.delete page.within('.discussion-reply-holder') do fill_in 'note[note]', with: 'A reply' click_button 'Add comment now' - wait_for_requests - expect(page).to have_content('Your comment could not be submitted because discussion to reply to cannot be found') + + expect(page).to have_button('Resolve thread') + end + end + + context 'when comment is deleted' do + before do + note.delete + end + + it 'shows an error message' do + find('.js-reply-button').click + + page.within('.discussion-reply-holder') do + fill_in 'note[note]', with: 'A reply' + click_button 'Add comment now' + + expect(page).to have_content('Your comment could not be submitted because discussion to reply to cannot be found') + end end end end diff --git a/spec/features/projects/blobs/blob_show_spec.rb b/spec/features/projects/blobs/blob_show_spec.rb index 3032d115a00..257f931866f 100644 --- a/spec/features/projects/blobs/blob_show_spec.rb +++ b/spec/features/projects/blobs/blob_show_spec.rb @@ -589,7 +589,7 @@ RSpec.describe 'File blob', :js do aggregate_failures do # shows that dashboard yaml is invalid expect(page).to have_content('Metrics Dashboard YAML definition is invalid:') - expect(page).to have_content("panel_groups: should be an array of panel_groups objects") + expect(page).to have_content("root is missing required keys: panel_groups") # shows a learn more link expect(page).to have_link('Learn more') diff --git a/spec/fixtures/lib/gitlab/metrics/dashboard/sample_dashboard.yml b/spec/fixtures/lib/gitlab/metrics/dashboard/sample_dashboard.yml index 4c8b200b16f..17b9552763a 100644 --- a/spec/fixtures/lib/gitlab/metrics/dashboard/sample_dashboard.yml +++ b/spec/fixtures/lib/gitlab/metrics/dashboard/sample_dashboard.yml @@ -48,7 +48,7 @@ panel_groups: y_label: "y_label" metrics: - id: metric_a2 - query_range: 'query' + query_range: 2000 label: Legend Label unit: unit - title: "Super Chart A1" diff --git a/spec/fixtures/lib/gitlab/metrics/dashboard/schemas/metrics.json b/spec/fixtures/lib/gitlab/metrics/dashboard/schemas/metrics.json index 038f5ac5d4e..b23b0ea15d2 100644 --- a/spec/fixtures/lib/gitlab/metrics/dashboard/schemas/metrics.json +++ b/spec/fixtures/lib/gitlab/metrics/dashboard/schemas/metrics.json @@ -10,8 +10,8 @@ ], "properties": { "id": { "type": "string" }, - "query_range": { "type": "string" }, - "query": { "type": "string" }, + "query_range": { "type": ["string", "number"] }, + "query": { "type": ["string", "number"] }, "unit": { "type": "string" }, "label": { "type": "string" }, "track": { "type": "string" }, diff --git a/spec/frontend/ide/stores/modules/terminal/messages_spec.js b/spec/frontend/ide/stores/modules/terminal/messages_spec.js index 966158999da..1bb92a9dfa5 100644 --- a/spec/frontend/ide/stores/modules/terminal/messages_spec.js +++ b/spec/frontend/ide/stores/modules/terminal/messages_spec.js @@ -15,6 +15,8 @@ describe('IDE store terminal messages', () => { sprintf( messages.ERROR_CONFIG, { + codeStart: `<code>`, + codeEnd: `</code>`, helpStart: `<a href="${escape(TEST_HELP_URL)}" target="_blank">`, helpEnd: '</a>', }, diff --git a/spec/frontend/monitoring/components/dashboard_spec.js b/spec/frontend/monitoring/components/dashboard_spec.js index 2111890a960..809d4549fb6 100644 --- a/spec/frontend/monitoring/components/dashboard_spec.js +++ b/spec/frontend/monitoring/components/dashboard_spec.js @@ -2,7 +2,7 @@ import { shallowMount, mount } from '@vue/test-utils'; import VueDraggable from 'vuedraggable'; import MockAdapter from 'axios-mock-adapter'; import { TEST_HOST } from 'helpers/test_constants'; -import { ESC_KEY, ESC_KEY_IE11 } from '~/lib/utils/keys'; +import { ESC_KEY } from '~/lib/utils/keys'; import { objectToQuery } from '~/lib/utils/url_utility'; import axios from '~/lib/utils/axios_utils'; import { dashboardEmptyStates, metricStates } from '~/monitoring/constants'; @@ -566,15 +566,6 @@ describe('Dashboard', () => { undefined, ); }); - - it('restores dashboard from full screen by typing the Escape key on IE11', () => { - mockKeyup(ESC_KEY_IE11); - - expect(store.dispatch).toHaveBeenCalledWith( - `monitoringDashboard/clearExpandedPanel`, - undefined, - ); - }); }); }); diff --git a/spec/frontend/pages/dashboard/projects/index/components/customize_homepage_banner_spec.js b/spec/frontend/pages/dashboard/projects/index/components/customize_homepage_banner_spec.js new file mode 100644 index 00000000000..c21525442d2 --- /dev/null +++ b/spec/frontend/pages/dashboard/projects/index/components/customize_homepage_banner_spec.js @@ -0,0 +1,50 @@ +import { shallowMount } from '@vue/test-utils'; +import CustomizeHomepageBanner from '~/pages/dashboard/projects/index/components/customize_homepage_banner.vue'; +import { GlBanner } from '@gitlab/ui'; +import MockAdapter from 'axios-mock-adapter'; +import axios from '~/lib/utils/axios_utils'; + +const svgPath = '/illustrations/background'; +const provide = { + svgPath, + preferencesBehaviorPath: 'some/behavior/path', + calloutsPath: 'call/out/path', + calloutsFeatureId: 'some-feature-id', +}; + +const createComponent = () => { + return shallowMount(CustomizeHomepageBanner, { provide }); +}; + +describe('CustomizeHomepageBanner', () => { + let mockAxios; + let wrapper; + + beforeEach(() => { + mockAxios = new MockAdapter(axios); + wrapper = createComponent(); + }); + + afterEach(() => { + wrapper.destroy(); + wrapper = null; + mockAxios.restore(); + }); + + it('should render the banner when not dismissed', () => { + expect(wrapper.contains(GlBanner)).toBe(true); + }); + + it('should close the banner when dismiss is clicked', async () => { + mockAxios.onPost(provide.calloutsPath).replyOnce(200); + expect(wrapper.contains(GlBanner)).toBe(true); + wrapper.find(GlBanner).vm.$emit('close'); + + await wrapper.vm.$nextTick(); + expect(wrapper.contains(GlBanner)).toBe(false); + }); + + it('includes the body text from options', () => { + expect(wrapper.html()).toContain(wrapper.vm.$options.i18n.body); + }); +}); diff --git a/spec/frontend/sidebar/confidential/__snapshots__/edit_form_spec.js.snap b/spec/frontend/sidebar/confidential/__snapshots__/edit_form_spec.js.snap new file mode 100644 index 00000000000..d33f6c7f389 --- /dev/null +++ b/spec/frontend/sidebar/confidential/__snapshots__/edit_form_spec.js.snap @@ -0,0 +1,50 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Edit Form Dropdown when confidential renders on or off text based on confidentiality 1`] = ` +<div + class="dropdown show" + toggleform="function () {}" + updateconfidentialattribute="function () {}" +> + <div + class="dropdown-menu sidebar-item-warning-message" + > + <div> + <p> + <gl-sprintf-stub + message="You are going to turn off the confidentiality. This means %{strongStart}everyone%{strongEnd} will be able to see and leave a comment on this %{issuableType}." + /> + </p> + + <edit-form-buttons-stub + confidential="true" + fullpath="" + /> + </div> + </div> +</div> +`; + +exports[`Edit Form Dropdown when not confidential renders "You are going to turn on the confidentiality." in the 1`] = ` +<div + class="dropdown show" + toggleform="function () {}" + updateconfidentialattribute="function () {}" +> + <div + class="dropdown-menu sidebar-item-warning-message" + > + <div> + <p> + <gl-sprintf-stub + message="You are going to turn on the confidentiality. This means that only team members with %{strongStart}at least Reporter access%{strongEnd} are able to see and leave comments on the %{issuableType}." + /> + </p> + + <edit-form-buttons-stub + fullpath="" + /> + </div> + </div> +</div> +`; diff --git a/spec/frontend/sidebar/confidential/edit_form_spec.js b/spec/frontend/sidebar/confidential/edit_form_spec.js index 44b6f755048..56f163eecd1 100644 --- a/spec/frontend/sidebar/confidential/edit_form_spec.js +++ b/spec/frontend/sidebar/confidential/edit_form_spec.js @@ -23,14 +23,14 @@ describe('Edit Form Dropdown', () => { }); describe('when not confidential', () => { - it('renders "You are going to turn off the confidentiality." in the ', () => { + it('renders "You are going to turn on the confidentiality." in the ', () => { createComponent({ confidential: false, toggleForm, updateConfidentialAttribute, }); - expect(wrapper.find('p').text()).toContain('You are going to turn on the confidentiality.'); + expect(wrapper.element).toMatchSnapshot(); }); }); @@ -42,7 +42,7 @@ describe('Edit Form Dropdown', () => { updateConfidentialAttribute, }); - expect(wrapper.find('p').text()).toContain('You are going to turn off the confidentiality.'); + expect(wrapper.element).toMatchSnapshot(); }); }); }); diff --git a/spec/frontend/sidebar/lock/__snapshots__/edit_form_spec.js.snap b/spec/frontend/sidebar/lock/__snapshots__/edit_form_spec.js.snap new file mode 100644 index 00000000000..18d4df297df --- /dev/null +++ b/spec/frontend/sidebar/lock/__snapshots__/edit_form_spec.js.snap @@ -0,0 +1,79 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Edit Form Dropdown In issue page when locked the appropriate warning text is rendered 1`] = ` +<div + class="dropdown-menu sidebar-item-warning-message" + data-testid="warning-text" +> + <p + class="text" + > + <gl-sprintf-stub + message="Unlock this %{issuableDisplayName}? %{strongStart}Everyone%{strongEnd} will be able to comment." + /> + </p> + + <edit-form-buttons-stub + islocked="true" + issuabledisplayname="issue" + /> +</div> +`; + +exports[`Edit Form Dropdown In issue page when unlocked the appropriate warning text is rendered 1`] = ` +<div + class="dropdown-menu sidebar-item-warning-message" + data-testid="warning-text" +> + <p + class="text" + > + <gl-sprintf-stub + message="Lock this %{issuableDisplayName}? Only %{strongStart}project members%{strongEnd} will be able to comment." + /> + </p> + + <edit-form-buttons-stub + issuabledisplayname="issue" + /> +</div> +`; + +exports[`Edit Form Dropdown In merge request page when locked the appropriate warning text is rendered 1`] = ` +<div + class="dropdown-menu sidebar-item-warning-message" + data-testid="warning-text" +> + <p + class="text" + > + <gl-sprintf-stub + message="Unlock this %{issuableDisplayName}? %{strongStart}Everyone%{strongEnd} will be able to comment." + /> + </p> + + <edit-form-buttons-stub + islocked="true" + issuabledisplayname="merge request" + /> +</div> +`; + +exports[`Edit Form Dropdown In merge request page when unlocked the appropriate warning text is rendered 1`] = ` +<div + class="dropdown-menu sidebar-item-warning-message" + data-testid="warning-text" +> + <p + class="text" + > + <gl-sprintf-stub + message="Lock this %{issuableDisplayName}? Only %{strongStart}project members%{strongEnd} will be able to comment." + /> + </p> + + <edit-form-buttons-stub + issuabledisplayname="merge request" + /> +</div> +`; diff --git a/spec/frontend/sidebar/lock/edit_form_spec.js b/spec/frontend/sidebar/lock/edit_form_spec.js index 6a86ab14d90..b1c3bfe3ef5 100644 --- a/spec/frontend/sidebar/lock/edit_form_spec.js +++ b/spec/frontend/sidebar/lock/edit_form_spec.js @@ -38,18 +38,16 @@ describe('Edit Form Dropdown', () => { }); describe.each` - isLocked | lockStatusText | lockAction | warningText - ${false} | ${'unlocked'} | ${'Lock'} | ${'Only project members will be able to comment.'} - ${true} | ${'locked'} | ${'Unlock'} | ${'Everyone will be able to comment.'} - `('when $lockStatusText', ({ isLocked, lockAction, warningText }) => { + isLocked | lockStatusText + ${false} | ${'unlocked'} + ${true} | ${'locked'} + `('when $lockStatusText', ({ isLocked }) => { beforeEach(() => { createComponent({ props: { isLocked } }); }); it(`the appropriate warning text is rendered`, () => { - expect(findWarningText().text()).toContain( - `${lockAction} this ${issuableDisplayName}? ${warningText}`, - ); + expect(findWarningText().element).toMatchSnapshot(); }); }); }); diff --git a/spec/graphql/types/user_status_type_spec.rb b/spec/graphql/types/user_status_type_spec.rb new file mode 100644 index 00000000000..c4421a9cc10 --- /dev/null +++ b/spec/graphql/types/user_status_type_spec.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Types::UserStatusType do + specify { expect(described_class.graphql_name).to eq('UserStatus') } + + it 'exposes the expected fields' do + expected_fields = %i[ + emoji + message + message_html + ] + + expect(described_class).to have_graphql_fields(*expected_fields) + end +end diff --git a/spec/graphql/types/user_type_spec.rb b/spec/graphql/types/user_type_spec.rb index 641d43e7dfd..7710b8efefe 100644 --- a/spec/graphql/types/user_type_spec.rb +++ b/spec/graphql/types/user_type_spec.rb @@ -14,11 +14,13 @@ RSpec.describe GitlabSchema.types['User'] do snippets name username + email avatarUrl webUrl webPath todos state + status authoredMergeRequests assignedMergeRequests groupMemberships diff --git a/spec/helpers/user_callouts_helper_spec.rb b/spec/helpers/user_callouts_helper_spec.rb index dd5e04761af..6f1f358af83 100644 --- a/spec/helpers/user_callouts_helper_spec.rb +++ b/spec/helpers/user_callouts_helper_spec.rb @@ -81,6 +81,36 @@ RSpec.describe UserCalloutsHelper do end end + describe '.show_customize_homepage_banner?' do + let(:customize_homepage) { true } + + subject { helper.show_customize_homepage_banner?(customize_homepage) } + + context 'when user has not dismissed' do + before do + allow(helper).to receive(:user_dismissed?).with(described_class::CUSTOMIZE_HOMEPAGE) { false } + end + + context 'when customize_homepage is set' do + it { is_expected.to be true } + end + + context 'when customize_homepage is false' do + let(:customize_homepage) { false } + + it { is_expected.to be false } + end + end + + context 'when user dismissed' do + before do + allow(helper).to receive(:user_dismissed?).with(described_class::CUSTOMIZE_HOMEPAGE) { true } + end + + it { is_expected.to be false } + end + end + describe '.render_flash_user_callout' do it 'renders the flash_user_callout partial' do expect(helper).to receive(:render) diff --git a/spec/lib/gitlab/metrics/dashboard/stages/metric_endpoint_inserter_spec.rb b/spec/lib/gitlab/metrics/dashboard/stages/metric_endpoint_inserter_spec.rb index 61d7da77162..bb3c8626d32 100644 --- a/spec/lib/gitlab/metrics/dashboard/stages/metric_endpoint_inserter_spec.rb +++ b/spec/lib/gitlab/metrics/dashboard/stages/metric_endpoint_inserter_spec.rb @@ -27,9 +27,17 @@ RSpec.describe Gitlab::Metrics::Dashboard::Stages::MetricEndpointInserter do transform! expect(all_metrics).to satisfy_all do |metric| - metric[:prometheus_endpoint_path] == prometheus_path(metric[:query_range].squish) + metric[:prometheus_endpoint_path].present? && !metric[:prometheus_endpoint_path].include?("\n") end end + + it 'works when query/query_range is a number' do + query = 2000 + + transform! + + expect(all_metrics[1][:prometheus_endpoint_path]).to eq(prometheus_path(query)) + end end private diff --git a/spec/lib/gitlab/metrics/dashboard/validator/errors_spec.rb b/spec/lib/gitlab/metrics/dashboard/validator/errors_spec.rb index f0db1bd0d33..aa31980871d 100644 --- a/spec/lib/gitlab/metrics/dashboard/validator/errors_spec.rb +++ b/spec/lib/gitlab/metrics/dashboard/validator/errors_spec.rb @@ -34,6 +34,19 @@ RSpec.describe Gitlab::Metrics::Dashboard::Validator::Errors do it { is_expected.to eq 'root is missing required keys: one' } end + + context 'when there is type mismatch' do + %w(null string boolean integer number array object).each do |expected_type| + context "on type: #{expected_type}" do + let(:type) { expected_type } + let(:details) { nil } + + subject { described_class.new(error_hash).message } + + it { is_expected.to eq "'property_name' at root is not of type: #{expected_type}" } + end + end + end end context 'for nested object' do diff --git a/spec/lib/gitlab/metrics/dashboard/validator_spec.rb b/spec/lib/gitlab/metrics/dashboard/validator_spec.rb index c4cda271408..eb67ea2b7da 100644 --- a/spec/lib/gitlab/metrics/dashboard/validator_spec.rb +++ b/spec/lib/gitlab/metrics/dashboard/validator_spec.rb @@ -143,4 +143,56 @@ RSpec.describe Gitlab::Metrics::Dashboard::Validator do end end end + + describe '#errors' do + context 'valid dashboard schema' do + it 'returns no errors' do + expect(described_class.errors(valid_dashboard)).to eq [] + end + + context 'with duplicate metric_ids' do + it 'returns errors' do + expect(described_class.errors(duplicate_id_dashboard)).to eq [Gitlab::Metrics::Dashboard::Validator::Errors::DuplicateMetricIds.new] + end + end + + context 'with dashboard_path and project' do + subject { described_class.errors(valid_dashboard, dashboard_path: 'test/path.yml', project: project) } + + context 'with no conflicting metric identifiers in db' do + it { is_expected.to eq [] } + end + + context 'with metric identifier present in current dashboard' do + before do + create(:prometheus_metric, + identifier: 'metric_a1', + dashboard_path: 'test/path.yml', + project: project + ) + end + + it { is_expected.to eq [] } + end + + context 'with metric identifier present in another dashboard' do + before do + create(:prometheus_metric, + identifier: 'metric_a1', + dashboard_path: 'some/other/dashboard/path.yml', + project: project + ) + end + + it { is_expected.to eq [Gitlab::Metrics::Dashboard::Validator::Errors::DuplicateMetricIds.new] } + end + end + end + + context 'invalid dashboard schema' do + it 'returns collection of validation errors' do + expect(described_class.errors(invalid_dashboard)).to all be_kind_of(Gitlab::Metrics::Dashboard::Validator::Errors::SchemaValidationError) + end + end + end end diff --git a/spec/migrations/20200811130433_create_missing_vulnerabilities_issue_links_spec.rb b/spec/migrations/20200811130433_create_missing_vulnerabilities_issue_links_spec.rb new file mode 100644 index 00000000000..885f9c45072 --- /dev/null +++ b/spec/migrations/20200811130433_create_missing_vulnerabilities_issue_links_spec.rb @@ -0,0 +1,122 @@ +# frozen_string_literal: true +require 'spec_helper' +require Rails.root.join('db', 'post_migrate', '20200811130433_create_missing_vulnerabilities_issue_links.rb') + +RSpec.describe CreateMissingVulnerabilitiesIssueLinks, :migration do + let(:namespace) { table(:namespaces).create!(name: 'user', path: 'user') } + let(:users) { table(:users) } + let(:user) { create_user! } + let(:project) { table(:projects).create!(id: 123, namespace_id: namespace.id) } + let(:scanner) { table(:vulnerability_scanners).create!(project_id: project.id, external_id: 'test', name: 'test scanner') } + let(:issue) { table(:issues).create!(id: 123, project_id: project.id) } + let(:vulnerabilities) { table(:vulnerabilities) } + let(:vulnerabilities_findings) { table(:vulnerability_occurrences) } + let(:vulnerability_feedback) { table(:vulnerability_feedback) } + let(:vulnerability_issue_links) { table(:vulnerability_issue_links) } + let(:vulnerability_identifier) { table(:vulnerability_identifiers).create!(project_id: project.id, external_type: 'test', external_id: 'test', fingerprint: 'test', name: 'test') } + + let!(:vulnerability) do + create_vulnerability!( + project_id: project.id, + author_id: user.id + ) + end + + before do + create_finding!( + vulnerability_id: vulnerability.id, + project_id: project.id, + scanner_id: scanner.id, + primary_identifier_id: vulnerability_identifier.id + ) + create_feedback!( + issue_id: issue.id, + project_id: project.id, + author_id: user.id + ) + end + + context 'with no Vulnerabilities::IssueLinks present' do + it 'creates missing Vulnerabilities::IssueLinks' do + expect(vulnerability_issue_links.count).to eq(0) + + migrate! + + expect(vulnerability_issue_links.count).to eq(1) + end + end + + context 'when an Vulnerabilities::IssueLink already exists' do + before do + vulnerability_issue_links.create!(vulnerability_id: vulnerability.id, issue_id: issue.id) + end + + it 'creates no duplicates' do + expect(vulnerability_issue_links.count).to eq(1) + + migrate! + + expect(vulnerability_issue_links.count).to eq(1) + end + end + + private + + def create_vulnerability!(project_id:, author_id:, title: 'test', severity: 7, confidence: 7, report_type: 0) + vulnerabilities.create!( + project_id: project_id, + author_id: author_id, + title: title, + severity: severity, + confidence: confidence, + report_type: report_type + ) + end + + # rubocop:disable Metrics/ParameterLists + def create_finding!( + vulnerability_id:, project_id:, scanner_id:, primary_identifier_id:, + name: "test", severity: 7, confidence: 7, report_type: 0, + project_fingerprint: '123qweasdzxc', location_fingerprint: 'test', + metadata_version: 'test', raw_metadata: 'test', uuid: 'test') + vulnerabilities_findings.create!( + vulnerability_id: vulnerability_id, + project_id: project_id, + name: name, + severity: severity, + confidence: confidence, + report_type: report_type, + project_fingerprint: project_fingerprint, + scanner_id: scanner.id, + primary_identifier_id: vulnerability_identifier.id, + location_fingerprint: location_fingerprint, + metadata_version: metadata_version, + raw_metadata: raw_metadata, + uuid: uuid + ) + end + # rubocop:enable Metrics/ParameterLists + + # project_fingerprint on Vulnerabilities::Finding is a bytea and we need to match this + def create_feedback!(issue_id:, project_id:, author_id:, feedback_type: 1, category: 0, project_fingerprint: '3132337177656173647a7863') + vulnerability_feedback.create!( + feedback_type: feedback_type, + issue_id: issue.id, + category: category, + project_fingerprint: project_fingerprint, + project_id: project_id, + author_id: author_id + ) + end + + def create_user!(name: "Example User", email: "user@example.com", user_type: nil, created_at: Time.now, confirmed_at: Time.now) + users.create!( + name: name, + email: email, + username: name, + projects_limit: 0, + user_type: user_type, + confirmed_at: confirmed_at + ) + end +end diff --git a/spec/models/blob_viewer/metrics_dashboard_yml_spec.rb b/spec/models/blob_viewer/metrics_dashboard_yml_spec.rb index 057f0f32158..bc6ac88f3aa 100644 --- a/spec/models/blob_viewer/metrics_dashboard_yml_spec.rb +++ b/spec/models/blob_viewer/metrics_dashboard_yml_spec.rb @@ -13,11 +13,11 @@ RSpec.describe BlobViewer::MetricsDashboardYml do subject(:viewer) { described_class.new(blob) } context 'when the definition is valid' do - let(:data) { File.read(Rails.root.join('config/prometheus/common_metrics.yml')) } + let(:data) { fixture_file('lib/gitlab/metrics/dashboard/sample_dashboard.yml')} describe '#valid?' do it 'calls prepare! on the viewer' do - allow(PerformanceMonitoring::PrometheusDashboard).to receive(:from_json) + allow(Gitlab::Metrics::Dashboard::Validator).to receive(:errors) expect(viewer).to receive(:prepare!) @@ -30,46 +30,44 @@ RSpec.describe BlobViewer::MetricsDashboardYml do expect_next_instance_of(::Gitlab::Config::Loader::Yaml, data) do |loader| expect(loader).to receive(:load_raw!).and_call_original end - expect(PerformanceMonitoring::PrometheusDashboard) - .to receive(:from_json) - .with(yml) + expect(Gitlab::Metrics::Dashboard::Validator) + .to receive(:errors) + .with(yml, dashboard_path: '.gitlab/dashboards/custom-dashboard.yml', project: project) .and_call_original expect(viewer.valid?).to be_truthy end end describe '#errors' do - it 'returns nil' do - allow(PerformanceMonitoring::PrometheusDashboard).to receive(:from_json) + it 'returns empty array' do + allow(Gitlab::Metrics::Dashboard::Validator).to receive(:errors).and_return([]) - expect(viewer.errors).to be nil + expect(viewer.errors).to eq [] end end end context 'when definition is invalid' do - let(:error) { ActiveModel::ValidationError.new(PerformanceMonitoring::PrometheusDashboard.new.tap(&:validate)) } + let(:error) { ::Gitlab::Metrics::Dashboard::Validator::Errors::SchemaValidationError.new } let(:data) do <<~YAML dashboard: YAML end + before do + allow(Gitlab::Metrics::Dashboard::Validator).to receive(:errors).and_return([error]) + end + describe '#valid?' do it 'returns false' do - expect(PerformanceMonitoring::PrometheusDashboard) - .to receive(:from_json).and_raise(error) - - expect(viewer.valid?).to be_falsey + expect(viewer.valid?).to be false end end describe '#errors' do it 'returns validation errors' do - allow(PerformanceMonitoring::PrometheusDashboard) - .to receive(:from_json).and_raise(error) - - expect(viewer.errors).to be error.model.errors + expect(viewer.errors).to eq [error] end end end @@ -85,17 +83,14 @@ RSpec.describe BlobViewer::MetricsDashboardYml do describe '#valid?' do it 'returns false' do - expect(PerformanceMonitoring::PrometheusDashboard).not_to receive(:from_json) - expect(viewer.valid?).to be_falsey + expect(Gitlab::Metrics::Dashboard::Validator).not_to receive(:errors) + expect(viewer.valid?).to be false end end describe '#errors' do it 'returns validation errors' do - yaml_wrapped_errors = { 'YAML syntax': ["(<unknown>): did not find expected key while parsing a block mapping at line 1 column 1"] } - - expect(viewer.errors).to be_kind_of ActiveModel::Errors - expect(viewer.errors.messages).to eql(yaml_wrapped_errors) + expect(viewer.errors).to all be_kind_of Gitlab::Config::Loader::FormatError end end end @@ -113,15 +108,12 @@ RSpec.describe BlobViewer::MetricsDashboardYml do end it 'is invalid' do - expect(PerformanceMonitoring::PrometheusDashboard).not_to receive(:from_json) - expect(viewer.valid?).to be(false) + expect(Gitlab::Metrics::Dashboard::Validator).not_to receive(:errors) + expect(viewer.valid?).to be false end it 'returns validation errors' do - yaml_wrapped_errors = { 'YAML syntax': ["The parsed YAML is too big"] } - - expect(viewer.errors).to be_kind_of(ActiveModel::Errors) - expect(viewer.errors.messages).to eq(yaml_wrapped_errors) + expect(viewer.errors).to all be_kind_of Gitlab::Config::Loader::FormatError end end end diff --git a/spec/models/design_management/design_spec.rb b/spec/models/design_management/design_spec.rb index 0b53a5e4bb7..e228e67f4a2 100644 --- a/spec/models/design_management/design_spec.rb +++ b/spec/models/design_management/design_spec.rb @@ -26,7 +26,7 @@ RSpec.describe DesignManagement::Design do end describe 'validations' do - subject(:design) { build(:design) } + subject(:design) { build(:design, issue: issue) } it { is_expected.to be_valid } it { is_expected.to validate_presence_of(:project) } @@ -153,7 +153,7 @@ RSpec.describe DesignManagement::Design do end describe '.ordered' do - before do + before_all do design1.update!(relative_position: 2) design2.update!(relative_position: 1) design3.update!(relative_position: nil) @@ -219,7 +219,7 @@ RSpec.describe DesignManagement::Design do end describe '#visible_in?' do - let_it_be(:issue) { create(:issue) } + let_it_be(:issue) { create(:issue, project: issue.project) } # It is expensive to re-create complex histories, so we do it once, and then # assert that we can establish visibility at any given version. @@ -275,7 +275,7 @@ RSpec.describe DesignManagement::Design do describe '#status' do context 'the design is new' do - subject { build(:design) } + subject { build(:design, issue: issue) } it { is_expected.to have_attributes(status: :new) } end @@ -295,7 +295,7 @@ RSpec.describe DesignManagement::Design do describe '#deleted?' do context 'the design is new' do - let(:design) { build(:design) } + let(:design) { build(:design, issue: issue) } it 'is falsy' do expect(design).not_to be_deleted @@ -319,7 +319,7 @@ RSpec.describe DesignManagement::Design do end context 'the design has been deleted, but was then re-created' do - let(:design) { create(:design, :with_versions, versions_count: 1, deleted: true) } + let(:design) { create(:design, :with_versions, issue: issue, versions_count: 1, deleted: true) } it 'is falsy' do restore_designs(design) @@ -337,7 +337,7 @@ RSpec.describe DesignManagement::Design do end it "is true when there are no versions" do - expect(build(:design)).to be_new_design + expect(build(:design, issue: issue)).to be_new_design end it 'is false for deleted designs' do @@ -374,7 +374,7 @@ RSpec.describe DesignManagement::Design do describe "#full_path" do it "builds the full path for a design" do - design = build(:design, filename: "hello.jpg") + design = build(:design, issue: issue, filename: "hello.jpg") expected_path = "#{DesignManagement.designs_directory}/issue-#{design.issue.iid}/hello.jpg" expect(design.full_path).to eq(expected_path) @@ -397,15 +397,13 @@ RSpec.describe DesignManagement::Design do let(:versions_count) { 1 } it 'builds diff refs based on the empty tree if there was only one version' do - design = create(:design, :with_file, versions_count: 1) - expect(design.diff_refs.base_sha).to eq(Gitlab::Git::BLANK_SHA) expect(design.diff_refs.head_sha).to eq(design.diff_refs.head_sha) end end it 'has no diff ref if new' do - design = build(:design) + design = build(:design, issue: issue) expect(design.diff_refs).to be_nil end @@ -413,7 +411,7 @@ RSpec.describe DesignManagement::Design do describe '#repository' do it 'is a design repository' do - design = build(:design) + design = build(:design, issue: issue) expect(design.repository).to be_a(DesignManagement::Repository) end @@ -421,7 +419,7 @@ RSpec.describe DesignManagement::Design do describe '#note_etag_key' do it 'returns a correct etag key' do - design = create(:design) + design = design1 expect(design.note_etag_key).to eq( ::Gitlab::Routing.url_helpers.designs_project_issue_path(design.project, design.issue, { vueroute: design.filename }) @@ -430,47 +428,26 @@ RSpec.describe DesignManagement::Design do end describe '#user_notes_count', :use_clean_rails_memory_store_caching do - let_it_be(:design) { create(:design, :with_file) } - - subject { design.user_notes_count } - # Note: Cache invalidation tests are in `design_user_notes_count_service_spec.rb` - it 'returns a count of user-generated notes' do - create(:diff_note_on_design, noteable: design) - - is_expected.to eq(1) - end - - it 'does not count notes on other designs' do - second_design = create(:design, :with_file) - create(:diff_note_on_design, noteable: second_design) + common_attrs = { issue: issue, project: issue.project, author: issue.project.creator } + design, second_design = create_list(:design, 2, :with_file, issue: issue) + create(:diff_note_on_design, **common_attrs, noteable: design) + create(:diff_note_on_design, **common_attrs, system: true, noteable: design) + create(:diff_note_on_design, **common_attrs, noteable: second_design) - is_expected.to eq(0) - end - - it 'does not count system notes' do - create(:diff_note_on_design, system: true, noteable: design) - - is_expected.to eq(0) + expect(design.user_notes_count).to eq(1) end end describe '#after_note_changed' do - subject { build(:design) } - - it 'calls #delete_cache on DesignUserNotesCountService' do - expect_next_instance_of(DesignManagement::DesignUserNotesCountService) do |service| - expect(service).to receive(:delete_cache) - end - - subject.after_note_changed(build(:note)) - end + it 'calls #delete_cache on DesignUserNotesCountService for non-system notes' do + design = design1 - it 'does not call #delete_cache on DesignUserNotesCountService when passed a system note' do - expect(DesignManagement::DesignUserNotesCountService).not_to receive(:new) + expect(design.send(:user_notes_count_service)).to receive(:delete_cache).once - subject.after_note_changed(build(:note, :system)) + design.after_note_changed(build(:note, project: issue.project)) + design.after_note_changed(build(:note, :system, project: issue.project)) end end @@ -554,14 +531,14 @@ RSpec.describe DesignManagement::Design do with_them do let(:filename) { "my-file.#{ext}" } - let(:design) { build(:design, filename: filename) } + let(:design) { build(:design, issue: issue, filename: filename) } let(:url) { url_for_design(design) } let(:captures) { described_class.link_reference_pattern.match(url)&.named_captures } it 'matches the URL' do expect(captures).to include( 'url_filename' => filename, - 'issue' => design.issue.iid.to_s, + 'issue' => issue.iid.to_s, 'namespace' => design.project.namespace.to_param, 'project' => design.project.name ) diff --git a/spec/models/performance_monitoring/prometheus_dashboard_spec.rb b/spec/models/performance_monitoring/prometheus_dashboard_spec.rb index 61174a7d0c5..ca4572ac094 100644 --- a/spec/models/performance_monitoring/prometheus_dashboard_spec.rb +++ b/spec/models/performance_monitoring/prometheus_dashboard_spec.rb @@ -219,20 +219,31 @@ RSpec.describe PerformanceMonitoring::PrometheusDashboard do end describe '#schema_validation_warnings' do + let_it_be(:project) { create(:project) } + let_it_be(:environment) { create(:environment, project: project) } + let(:path) { '.gitlab/dashboards/test.yml' } + + subject(:schema_validation_warnings) { described_class.new(json_content.merge(path: path, environment: environment)).schema_validation_warnings } + + before do + allow(Gitlab::Metrics::Dashboard::Finder).to receive(:find_raw).with(project, dashboard_path: path).and_return(json_content) + end + context 'when schema is valid' do it 'returns nil' do - expect(described_class).to receive(:from_json) - expect(described_class.new.schema_validation_warnings).to be_nil + expect(Gitlab::Metrics::Dashboard::Validator).to receive(:errors).with(json_content, dashboard_path: path, project: project).and_return([]) + + expect(schema_validation_warnings).to eq [] end end context 'when schema is invalid' do it 'returns array with errors messages' do - instance = described_class.new - instance.errors.add(:test, 'test error') + error = ::Gitlab::Metrics::Dashboard::Validator::Errors::SchemaValidationError.new + + expect(Gitlab::Metrics::Dashboard::Validator).to receive(:errors).with(json_content, dashboard_path: path, project: project).and_return([error]) - expect(described_class).to receive(:from_json).and_raise(ActiveModel::ValidationError.new(instance)) - expect(described_class.new.schema_validation_warnings).to eq ['test: test error'] + expect(schema_validation_warnings).to eq [error.message] end end end diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index 48dee1e6481..a6b79e55f02 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -2103,7 +2103,7 @@ RSpec.describe Repository do describe '#expire_branches_cache' do it 'expires the cache' do expect(repository).to receive(:expire_method_caches) - .with(%i(branch_names merged_branch_names branch_count has_visible_content?)) + .with(%i(branch_names merged_branch_names branch_count has_visible_content? has_ambiguous_refs?)) .and_call_original repository.expire_branches_cache @@ -2113,7 +2113,7 @@ RSpec.describe Repository do describe '#expire_tags_cache' do it 'expires the cache' do expect(repository).to receive(:expire_method_caches) - .with(%i(tag_names tag_count)) + .with(%i(tag_names tag_count has_ambiguous_refs?)) .and_call_original repository.expire_tags_cache diff --git a/spec/requests/api/graphql/metrics/dashboard_query_spec.rb b/spec/requests/api/graphql/metrics/dashboard_query_spec.rb index 456b0a5dea1..79b9c27125a 100644 --- a/spec/requests/api/graphql/metrics/dashboard_query_spec.rb +++ b/spec/requests/api/graphql/metrics/dashboard_query_spec.rb @@ -7,7 +7,7 @@ RSpec.describe 'Getting Metrics Dashboard' do let_it_be(:current_user) { create(:user) } let(:project) { create(:project) } - let!(:environment) { create(:environment, project: project) } + let(:environment) { create(:environment, project: project) } let(:query) do graphql_query_for( @@ -67,7 +67,7 @@ RSpec.describe 'Getting Metrics Dashboard' do it 'returns metrics dashboard' do dashboard = graphql_data.dig('project', 'environments', 'nodes', 0, 'metricsDashboard') - expect(dashboard).to eql("path" => path, "schemaValidationWarnings" => ["panel_groups: should be an array of panel_groups objects"]) + expect(dashboard).to eql("path" => path, "schemaValidationWarnings" => ["root is missing required keys: panel_groups"]) end end @@ -78,7 +78,7 @@ RSpec.describe 'Getting Metrics Dashboard' do it 'returns metrics dashboard' do dashboard = graphql_data.dig('project', 'environments', 'nodes', 0, 'metricsDashboard') - expect(dashboard).to eql("path" => path, "schemaValidationWarnings" => ["dashboard: can't be blank", "panel_groups: should be an array of panel_groups objects"]) + expect(dashboard).to eql("path" => path, "schemaValidationWarnings" => ["root is missing required keys: dashboard, panel_groups"]) end end end diff --git a/spec/requests/api/graphql/user_query_spec.rb b/spec/requests/api/graphql/user_query_spec.rb index d8a2f96f5d8..2f4dc0a9160 100644 --- a/spec/requests/api/graphql/user_query_spec.rb +++ b/spec/requests/api/graphql/user_query_spec.rb @@ -75,7 +75,9 @@ RSpec.describe 'getting user information' do 'name' => presenter.name, 'username' => presenter.username, 'webUrl' => presenter.web_url, - 'avatarUrl' => presenter.avatar_url + 'avatarUrl' => presenter.avatar_url, + 'status' => presenter.status, + 'email' => presenter.email )) end diff --git a/spec/services/notes/create_service_spec.rb b/spec/services/notes/create_service_spec.rb index 15defd7e22f..f087f72ca46 100644 --- a/spec/services/notes/create_service_spec.rb +++ b/spec/services/notes/create_service_spec.rb @@ -434,6 +434,13 @@ RSpec.describe Notes::CreateService do .and change { existing_note.updated_at } end + it 'returns a DiscussionNote with its parent discussion refreshed correctly' do + discussion_notes = subject.discussion.notes + + expect(discussion_notes.size).to eq(2) + expect(discussion_notes.first).to be_a(DiscussionNote) + end + context 'discussion to reply cannot be found' do before do existing_note.delete |