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

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitlab/merge_request_templates/Security Release.md2
-rw-r--r--Gemfile2
-rw-r--r--Gemfile.lock4
-rw-r--r--app/assets/javascripts/boards/index.js2
-rw-r--r--app/assets/javascripts/build_artifacts.js18
-rw-r--r--app/assets/javascripts/clusters/components/crossplane_provider_stack.vue18
-rw-r--r--app/assets/javascripts/clusters/components/fluentd_output_settings.vue20
-rw-r--r--app/assets/javascripts/clusters/components/ingress_modsecurity_settings.vue20
-rw-r--r--app/assets/javascripts/clusters/components/knative_domain_editor.vue26
-rw-r--r--app/assets/javascripts/right_sidebar.js5
-rw-r--r--app/assets/javascripts/static_site_editor/pages/success.vue1
-rw-r--r--app/models/environment.rb1
-rw-r--r--app/models/prometheus_alert.rb2
-rw-r--r--app/models/prometheus_metric.rb2
-rw-r--r--app/services/git/branch_hooks_service.rb8
-rw-r--r--app/services/metrics/dashboard/custom_dashboard_service.rb6
-rw-r--r--app/workers/all_queues.yml8
-rw-r--r--app/workers/metrics/dashboard/sync_dashboards_worker.rb22
-rw-r--r--changelogs/unreleased/Replace-GlDeprecatedDropdown-with-GlDropdown-in-app-assets-javascripts-cl.yml5
-rw-r--r--config/feature_flags/development/sync_metrics_dashboards.yml7
-rw-r--r--config/sidekiq_queues.yml2
-rw-r--r--doc/administration/raketasks/check.md31
-rw-r--r--doc/api/epics.md6
-rw-r--r--doc/api/issues.md6
-rw-r--r--doc/api/merge_requests.md6
-rw-r--r--doc/api/openapi/openapi.yaml4
-rw-r--r--doc/api/todos.md22
-rw-r--r--doc/development/api_styleguide.md2
-rw-r--r--doc/development/contributing/issue_workflow.md5
-rw-r--r--doc/development/documentation/graphql_styleguide.md2
-rw-r--r--doc/development/documentation/restful_api_styleguide.md180
-rw-r--r--doc/development/documentation/styleguide.md300
-rw-r--r--doc/development/gotchas.md15
-rw-r--r--doc/operations/incident_management/alerts.md10
-rw-r--r--doc/user/project/description_templates.md4
-rw-r--r--doc/user/project/issues/design_management.md6
-rw-r--r--doc/user/project/issues/due_dates.md2
-rw-r--r--doc/user/project/issues/issue_data_and_actions.md2
-rw-r--r--doc/user/project/quick_actions.md4
-rw-r--r--doc/user/todos.md6
-rw-r--r--lib/gitlab/metrics/dashboard/importers/prometheus_metrics.rb49
-rw-r--r--lib/gitlab/metrics/dashboard/stages/custom_dashboard_metrics_inserter.rb24
-rw-r--r--lib/gitlab/metrics/dashboard/transformers/errors.rb6
-rw-r--r--spec/factories/prometheus_metrics.rb1
-rw-r--r--spec/frontend/clusters/components/fluentd_output_settings_spec.js4
-rw-r--r--spec/frontend/clusters/components/ingress_modsecurity_settings_spec.js4
-rw-r--r--spec/frontend/clusters/components/knative_domain_editor_spec.js4
-rw-r--r--spec/frontend/clusters/services/crossplane_provider_stack_spec.js4
-rw-r--r--spec/frontend/static_site_editor/pages/success_spec.js1
-rw-r--r--spec/lib/gitlab/metrics/dashboard/importers/prometheus_metrics_spec.rb82
-rw-r--r--spec/services/git/branch_hooks_service_spec.rb22
-rw-r--r--spec/services/metrics/dashboard/custom_dashboard_service_spec.rb17
-rw-r--r--spec/workers/metrics/dashboard/sync_dashboards_worker_spec.rb25
53 files changed, 724 insertions, 313 deletions
diff --git a/.gitlab/merge_request_templates/Security Release.md b/.gitlab/merge_request_templates/Security Release.md
index eda16747c13..fccfad18ef0 100644
--- a/.gitlab/merge_request_templates/Security Release.md
+++ b/.gitlab/merge_request_templates/Security Release.md
@@ -21,7 +21,7 @@ See [the general developer security release guidelines](https://gitlab.com/gitla
- [ ] Assign to a reviewer and maintainer, per our [Code Review process].
- [ ] Ensure it's approved according to our [Approval Guidelines].
- [ ] Ensure it's approved by an AppSec engineer.
- - If you're unsure who should approve, find the AppSec engineer associated to the issue in the [Canonical repository], or ask #sec-appsec on Slack.
+ - Please see the security release [Code reviews and Approvals](https://gitlab.com/gitlab-org/release/docs/blob/master/general/security/developer.md#code-reviews-and-approvals) documentation for details on which AppSec team member to ping for approval.
- Trigger the [`package-and-qa` build]. The docker image generated will be used by the AppSec engineer to validate the security vulnerability has been remediated.
- [ ] For a backport MR targeting a versioned stable branch (`X-Y-stable-ee`)
- [ ] Ensure it's approved by a maintainer.
diff --git a/Gemfile b/Gemfile
index a5a978e5999..57853f9f3b5 100644
--- a/Gemfile
+++ b/Gemfile
@@ -244,7 +244,7 @@ gem 'atlassian-jwt', '~> 0.2.0'
gem 'flowdock', '~> 0.7'
# Slack integration
-gem 'slack-messenger', '~> 2.3.3'
+gem 'slack-messenger', '~> 2.3.4'
# Hangouts Chat integration
gem 'hangouts-chat', '~> 0.0.5'
diff --git a/Gemfile.lock b/Gemfile.lock
index 19abf6651a4..142a606faa0 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -1096,7 +1096,7 @@ GEM
simplecov (~> 0.8)
simplecov-html (0.12.2)
sixarm_ruby_unaccent (1.2.0)
- slack-messenger (2.3.3)
+ slack-messenger (2.3.4)
snowplow-tracker (0.6.1)
contracts (~> 0.7, <= 0.11)
spring (2.0.2)
@@ -1471,7 +1471,7 @@ DEPENDENCIES
simple_po_parser (~> 1.1.2)
simplecov (~> 0.18.5)
simplecov-cobertura (~> 1.3.1)
- slack-messenger (~> 2.3.3)
+ slack-messenger (~> 2.3.4)
snowplow-tracker (~> 0.6.1)
spring (~> 2.0.0)
spring-commands-rspec (~> 1.0.4)
diff --git a/app/assets/javascripts/boards/index.js b/app/assets/javascripts/boards/index.js
index dd4d075a042..2af96e94d32 100644
--- a/app/assets/javascripts/boards/index.js
+++ b/app/assets/javascripts/boards/index.js
@@ -84,7 +84,7 @@ export default () => {
},
provide: {
boardId: $boardApp.dataset.boardId,
- groupId: Number($boardApp.dataset.groupId) || null,
+ groupId: Number($boardApp.dataset.groupId),
rootPath: $boardApp.dataset.rootPath,
canUpdate: $boardApp.dataset.canUpdate,
},
diff --git a/app/assets/javascripts/build_artifacts.js b/app/assets/javascripts/build_artifacts.js
index 2955f0f014b..8324c649538 100644
--- a/app/assets/javascripts/build_artifacts.js
+++ b/app/assets/javascripts/build_artifacts.js
@@ -3,6 +3,7 @@
import $ from 'jquery';
import { visitUrl } from './lib/utils/url_utility';
import { parseBoolean } from './lib/utils/common_utils';
+import { hide, initTooltips, show } from '~/tooltips';
export default class BuildArtifacts {
constructor() {
@@ -10,6 +11,7 @@ export default class BuildArtifacts {
this.setupEntryClick();
this.setupTooltips();
}
+
// eslint-disable-next-line class-methods-use-this
disablePropagation() {
$('.top-block').on('click', '.download', e => {
@@ -19,15 +21,17 @@ export default class BuildArtifacts {
e.stopImmediatePropagation();
});
}
+
// eslint-disable-next-line class-methods-use-this
setupEntryClick() {
return $('.tree-holder').on('click', 'tr[data-link]', function() {
visitUrl(this.dataset.link, parseBoolean(this.dataset.externalLink));
});
}
+
// eslint-disable-next-line class-methods-use-this
setupTooltips() {
- $('.js-artifact-tree-tooltip').tooltip({
+ initTooltips({
placement: 'bottom',
// Stop the tooltip from hiding when we stop hovering the element directly
// We handle all the showing/hiding below
@@ -38,14 +42,14 @@ export default class BuildArtifacts {
// But be placed below and in the middle of the file name
$('.js-artifact-tree-row')
.on('mouseenter', e => {
- $(e.currentTarget)
- .find('.js-artifact-tree-tooltip')
- .tooltip('show');
+ const $el = $(e.currentTarget).find('.js-artifact-tree-tooltip');
+
+ show($el);
})
.on('mouseleave', e => {
- $(e.currentTarget)
- .find('.js-artifact-tree-tooltip')
- .tooltip('hide');
+ const $el = $(e.currentTarget).find('.js-artifact-tree-tooltip');
+
+ hide($el);
});
}
}
diff --git a/app/assets/javascripts/clusters/components/crossplane_provider_stack.vue b/app/assets/javascripts/clusters/components/crossplane_provider_stack.vue
index c816fc56d7a..6b99bb09504 100644
--- a/app/assets/javascripts/clusters/components/crossplane_provider_stack.vue
+++ b/app/assets/javascripts/clusters/components/crossplane_provider_stack.vue
@@ -1,12 +1,12 @@
<script>
-import { GlDeprecatedDropdown, GlDeprecatedDropdownItem, GlIcon } from '@gitlab/ui';
+import { GlDropdown, GlDropdownItem, GlIcon } from '@gitlab/ui';
import { s__ } from '../../locale';
export default {
name: 'CrossplaneProviderStack',
components: {
- GlDeprecatedDropdown,
- GlDeprecatedDropdownItem,
+ GlDropdown,
+ GlDropdownItem,
GlIcon,
},
props: {
@@ -67,21 +67,17 @@ export default {
<label>
{{ s__('ClusterIntegration|Enabled stack') }}
</label>
- <gl-deprecated-dropdown
+ <gl-dropdown
:disabled="crossplane.installed"
:text="dropdownText"
toggle-class="dropdown-menu-toggle gl-field-error-outline"
class="w-100"
:class="{ 'gl-show-field-errors': validationError }"
>
- <gl-deprecated-dropdown-item
- v-for="stack in stacks"
- :key="stack.code"
- @click="selectStack(stack)"
- >
+ <gl-dropdown-item v-for="stack in stacks" :key="stack.code" @click="selectStack(stack)">
<span class="ml-1">{{ stack.name }}</span>
- </gl-deprecated-dropdown-item>
- </gl-deprecated-dropdown>
+ </gl-dropdown-item>
+ </gl-dropdown>
<span v-if="validationError" class="gl-field-error">{{ validationError }}</span>
<p class="form-text text-muted">
{{ s__(`You must select a stack for configuring your cloud provider. Learn more about`) }}
diff --git a/app/assets/javascripts/clusters/components/fluentd_output_settings.vue b/app/assets/javascripts/clusters/components/fluentd_output_settings.vue
index 7b55efaaccd..b37fc3894f8 100644
--- a/app/assets/javascripts/clusters/components/fluentd_output_settings.vue
+++ b/app/assets/javascripts/clusters/components/fluentd_output_settings.vue
@@ -1,11 +1,5 @@
<script>
-import {
- GlAlert,
- GlButton,
- GlDeprecatedDropdown,
- GlDeprecatedDropdownItem,
- GlFormCheckbox,
-} from '@gitlab/ui';
+import { GlAlert, GlButton, GlDropdown, GlDropdownItem, GlFormCheckbox } from '@gitlab/ui';
import { mapValues } from 'lodash';
import { __ } from '~/locale';
import { APPLICATION_STATUS, FLUENTD } from '~/clusters/constants';
@@ -17,8 +11,8 @@ export default {
components: {
GlAlert,
GlButton,
- GlDeprecatedDropdown,
- GlDeprecatedDropdownItem,
+ GlDropdown,
+ GlDropdownItem,
GlFormCheckbox,
},
props: {
@@ -203,15 +197,15 @@ export default {
<label for="fluentd-protocol">
<strong>{{ s__('ClusterIntegration|SIEM Protocol') }}</strong>
</label>
- <gl-deprecated-dropdown :text="protocolName" class="w-100">
- <gl-deprecated-dropdown-item
+ <gl-dropdown :text="protocolName" class="w-100">
+ <gl-dropdown-item
v-for="(value, index) in protocols"
:key="index"
@click="selectProtocol(value.toLowerCase())"
>
{{ value }}
- </gl-deprecated-dropdown-item>
- </gl-deprecated-dropdown>
+ </gl-dropdown-item>
+ </gl-dropdown>
</div>
<div class="form-group flex flex-wrap">
<gl-form-checkbox :checked="wafLogEnabled" @input="wafLogChanged">
diff --git a/app/assets/javascripts/clusters/components/ingress_modsecurity_settings.vue b/app/assets/javascripts/clusters/components/ingress_modsecurity_settings.vue
index ec252878e93..f05c8db5d56 100644
--- a/app/assets/javascripts/clusters/components/ingress_modsecurity_settings.vue
+++ b/app/assets/javascripts/clusters/components/ingress_modsecurity_settings.vue
@@ -6,8 +6,8 @@ import {
GlLink,
GlToggle,
GlButton,
- GlDeprecatedDropdown,
- GlDeprecatedDropdownItem,
+ GlDropdown,
+ GlDropdownItem,
GlIcon,
} from '@gitlab/ui';
import modSecurityLogo from 'images/cluster_app_logos/gitlab.png';
@@ -26,8 +26,8 @@ export default {
GlLink,
GlToggle,
GlButton,
- GlDeprecatedDropdown,
- GlDeprecatedDropdownItem,
+ GlDropdown,
+ GlDropdownItem,
GlIcon,
},
props: {
@@ -221,15 +221,11 @@ export default {
</strong>
</p>
</div>
- <gl-deprecated-dropdown :text="modSecurityModeName" :disabled="saveButtonDisabled">
- <gl-deprecated-dropdown-item
- v-for="(mode, key) in modes"
- :key="key"
- @click="selectMode(key)"
- >
+ <gl-dropdown :text="modSecurityModeName" :disabled="saveButtonDisabled">
+ <gl-dropdown-item v-for="(mode, key) in modes" :key="key" @click="selectMode(key)">
{{ mode.name }}
- </gl-deprecated-dropdown-item>
- </gl-deprecated-dropdown>
+ </gl-dropdown-item>
+ </gl-dropdown>
</div>
</div>
<div v-if="showButtons" class="gl-mt-5 gl-display-flex">
diff --git a/app/assets/javascripts/clusters/components/knative_domain_editor.vue b/app/assets/javascripts/clusters/components/knative_domain_editor.vue
index 2617ea0bdea..19ce3e36cd7 100644
--- a/app/assets/javascripts/clusters/components/knative_domain_editor.vue
+++ b/app/assets/javascripts/clusters/components/knative_domain_editor.vue
@@ -1,8 +1,8 @@
<script>
import {
- GlDeprecatedDropdown,
- GlDeprecatedDropdownDivider,
- GlDeprecatedDropdownItem,
+ GlDropdown,
+ GlDropdownDivider,
+ GlDropdownItem,
GlLoadingIcon,
GlSearchBoxByType,
GlSprintf,
@@ -20,9 +20,9 @@ export default {
GlButton,
ClipboardButton,
GlLoadingIcon,
- GlDeprecatedDropdown,
- GlDeprecatedDropdownDivider,
- GlDeprecatedDropdownItem,
+ GlDropdown,
+ GlDropdownDivider,
+ GlDropdownItem,
GlSearchBoxByType,
GlSprintf,
},
@@ -121,7 +121,7 @@ export default {
<strong>{{ s__('ClusterIntegration|Knative Domain Name:') }}</strong>
</label>
- <gl-deprecated-dropdown
+ <gl-dropdown
v-if="showDomainsDropdown"
:text="domainDropdownText"
toggle-class="dropdown-menu-toggle"
@@ -132,16 +132,16 @@ export default {
:placeholder="s__('ClusterIntegration|Search domains')"
class="gl-m-3"
/>
- <gl-deprecated-dropdown-item
+ <gl-dropdown-item
v-for="domain in filteredDomains"
:key="domain.id"
@click="selectDomain(domain)"
>
<span class="ml-1">{{ domain.domain }}</span>
- </gl-deprecated-dropdown-item>
+ </gl-dropdown-item>
<template v-if="searchQuery">
- <gl-deprecated-dropdown-divider />
- <gl-deprecated-dropdown-item key="custom-domain" @click="selectCustomDomain(searchQuery)">
+ <gl-dropdown-divider />
+ <gl-dropdown-item key="custom-domain" @click="selectCustomDomain(searchQuery)">
<span class="ml-1">
<gl-sprintf :message="s__('ClusterIntegration|Use %{query}')">
<template #query>
@@ -149,9 +149,9 @@ export default {
</template>
</gl-sprintf>
</span>
- </gl-deprecated-dropdown-item>
+ </gl-dropdown-item>
</template>
- </gl-deprecated-dropdown>
+ </gl-dropdown>
<input
v-else
diff --git a/app/assets/javascripts/right_sidebar.js b/app/assets/javascripts/right_sidebar.js
index 8bebd16ace7..5d60c6d5175 100644
--- a/app/assets/javascripts/right_sidebar.js
+++ b/app/assets/javascripts/right_sidebar.js
@@ -5,6 +5,7 @@ import Cookies from 'js-cookie';
import { deprecatedCreateFlash as flash } from './flash';
import axios from './lib/utils/axios_utils';
import { sprintf, s__, __ } from './locale';
+import { fixTitle, hide } from '~/tooltips';
function Sidebar() {
this.toggleTodo = this.toggleTodo.bind(this);
@@ -77,7 +78,7 @@ Sidebar.prototype.toggleTodo = function(e) {
const ajaxType = $this.data('deletePath') ? 'delete' : 'post';
const url = String($this.data('deletePath') || $this.data('createPath'));
- $this.tooltip('hide');
+ hide($this);
$('.js-issuable-todo')
.disable()
@@ -119,7 +120,7 @@ Sidebar.prototype.todoUpdateDone = function(data) {
.data('deletePath', deletePath);
if ($el.hasClass('has-tooltip')) {
- $el.tooltip('_fixTitle');
+ fixTitle($el);
}
if (typeof $el.data('isCollapsed') !== 'undefined') {
diff --git a/app/assets/javascripts/static_site_editor/pages/success.vue b/app/assets/javascripts/static_site_editor/pages/success.vue
index 6a3f40cc1db..5b013c27c35 100644
--- a/app/assets/javascripts/static_site_editor/pages/success.vue
+++ b/app/assets/javascripts/static_site_editor/pages/success.vue
@@ -85,6 +85,7 @@ export default {
:primary-button-text="savedContentMeta && $options.primaryButtonText"
:primary-button-link="savedContentMeta && savedContentMeta.mergeRequest.url"
:svg-path="mergeRequestsIllustrationPath"
+ :svg-height="146"
>
<template #description>
<div v-if="savedContentMeta">
diff --git a/app/models/environment.rb b/app/models/environment.rb
index cfdcb0499e6..f64776a6991 100644
--- a/app/models/environment.rb
+++ b/app/models/environment.rb
@@ -86,6 +86,7 @@ class Environment < ApplicationRecord
scope :with_rank, -> do
select('environments.*, rank() OVER (PARTITION BY project_id ORDER BY id DESC)')
end
+ scope :for_id, -> (id) { where(id: id) }
state_machine :state, initial: :available do
event :start do
diff --git a/app/models/prometheus_alert.rb b/app/models/prometheus_alert.rb
index f0441d4a3cb..684f50d5f58 100644
--- a/app/models/prometheus_alert.rb
+++ b/app/models/prometheus_alert.rb
@@ -4,6 +4,7 @@ class PrometheusAlert < ApplicationRecord
include Sortable
include UsageStatistics
include Presentable
+ include EachBatch
OPERATORS_MAP = {
lt: "<",
@@ -35,6 +36,7 @@ class PrometheusAlert < ApplicationRecord
scope :for_metric, -> (metric) { where(prometheus_metric: metric) }
scope :for_project, -> (project) { where(project_id: project) }
scope :for_environment, -> (environment) { where(environment_id: environment) }
+ scope :get_environment_id, -> { select(:environment_id).pluck(:environment_id) }
def self.distinct_projects
sub_query = self.group(:project_id).select(1)
diff --git a/app/models/prometheus_metric.rb b/app/models/prometheus_metric.rb
index 9ddf66cd388..590eda62c11 100644
--- a/app/models/prometheus_metric.rb
+++ b/app/models/prometheus_metric.rb
@@ -1,6 +1,8 @@
# frozen_string_literal: true
class PrometheusMetric < ApplicationRecord
+ include EachBatch
+
belongs_to :project, validate: true, inverse_of: :prometheus_metrics
has_many :prometheus_alerts, inverse_of: :prometheus_metric
diff --git a/app/services/git/branch_hooks_service.rb b/app/services/git/branch_hooks_service.rb
index dcb32b4c84b..93a0d139001 100644
--- a/app/services/git/branch_hooks_service.rb
+++ b/app/services/git/branch_hooks_service.rb
@@ -76,12 +76,20 @@ module Git
def branch_change_hooks
enqueue_process_commit_messages
enqueue_jira_connect_sync_messages
+ enqueue_metrics_dashboard_sync
end
def branch_remove_hooks
project.repository.after_remove_branch(expire_cache: false)
end
+ def enqueue_metrics_dashboard_sync
+ return unless Feature.enabled?(:sync_metrics_dashboards, project)
+ return unless default_branch?
+
+ ::Metrics::Dashboard::SyncDashboardsWorker.perform_async(project.id)
+ end
+
# Schedules processing of commit messages
def enqueue_process_commit_messages
referencing_commits = limited_commits.select(&:matches_cross_reference_regex?)
diff --git a/app/services/metrics/dashboard/custom_dashboard_service.rb b/app/services/metrics/dashboard/custom_dashboard_service.rb
index f0f19bf2ba3..bde8e86851a 100644
--- a/app/services/metrics/dashboard/custom_dashboard_service.rb
+++ b/app/services/metrics/dashboard/custom_dashboard_service.rb
@@ -42,6 +42,12 @@ module Metrics
def cache_key
"project_#{project.id}_metrics_dashboard_#{dashboard_path}"
end
+
+ def sequence
+ [
+ ::Gitlab::Metrics::Dashboard::Stages::CustomDashboardMetricsInserter
+ ] + super
+ end
end
end
end
diff --git a/app/workers/all_queues.yml b/app/workers/all_queues.yml
index 722a66d94c1..bdcb31b8d46 100644
--- a/app/workers/all_queues.yml
+++ b/app/workers/all_queues.yml
@@ -1556,6 +1556,14 @@
:weight: 1
:idempotent: true
:tags: []
+- :name: metrics_dashboard_sync_dashboards
+ :feature_category: :metrics
+ :has_external_dependencies:
+ :urgency: :low
+ :resource_boundary: :unknown
+ :weight: 1
+ :idempotent: true
+ :tags: []
- :name: migrate_external_diffs
:feature_category: :source_code_management
:has_external_dependencies:
diff --git a/app/workers/metrics/dashboard/sync_dashboards_worker.rb b/app/workers/metrics/dashboard/sync_dashboards_worker.rb
new file mode 100644
index 00000000000..7a124a33f9e
--- /dev/null
+++ b/app/workers/metrics/dashboard/sync_dashboards_worker.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+module Metrics
+ module Dashboard
+ class SyncDashboardsWorker
+ include ApplicationWorker
+
+ feature_category :metrics
+
+ idempotent!
+
+ def perform(project_id)
+ project = Project.find(project_id)
+ dashboard_paths = ::Gitlab::Metrics::Dashboard::RepoDashboardFinder.list_dashboards(project)
+
+ dashboard_paths.each do |dashboard_path|
+ ::Gitlab::Metrics::Dashboard::Importer.new(dashboard_path, project).execute!
+ end
+ end
+ end
+ end
+end
diff --git a/changelogs/unreleased/Replace-GlDeprecatedDropdown-with-GlDropdown-in-app-assets-javascripts-cl.yml b/changelogs/unreleased/Replace-GlDeprecatedDropdown-with-GlDropdown-in-app-assets-javascripts-cl.yml
new file mode 100644
index 00000000000..f3f2cb3ae58
--- /dev/null
+++ b/changelogs/unreleased/Replace-GlDeprecatedDropdown-with-GlDropdown-in-app-assets-javascripts-cl.yml
@@ -0,0 +1,5 @@
+---
+title: Replace deprecated cluster dropdowns with updated dropdowns
+merge_request: 41414
+author: nuwe1
+type: other
diff --git a/config/feature_flags/development/sync_metrics_dashboards.yml b/config/feature_flags/development/sync_metrics_dashboards.yml
new file mode 100644
index 00000000000..2dd8964121a
--- /dev/null
+++ b/config/feature_flags/development/sync_metrics_dashboards.yml
@@ -0,0 +1,7 @@
+---
+name: sync_metrics_dashboards
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/39658
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/241793
+group: group::apm
+type: development
+default_enabled: false
diff --git a/config/sidekiq_queues.yml b/config/sidekiq_queues.yml
index c994bda3389..a2cd2576d2b 100644
--- a/config/sidekiq_queues.yml
+++ b/config/sidekiq_queues.yml
@@ -168,6 +168,8 @@
- 1
- - metrics_dashboard_prune_old_annotations
- 1
+- - metrics_dashboard_sync_dashboards
+ - 1
- - migrate_external_diffs
- 1
- - namespaceless_project_destroy
diff --git a/doc/administration/raketasks/check.md b/doc/administration/raketasks/check.md
index 15014fffd01..d13e6328b2f 100644
--- a/doc/administration/raketasks/check.md
+++ b/doc/administration/raketasks/check.md
@@ -154,3 +154,34 @@ If the issue persists, try triggering `gc` via the
p = Project.find_by_path("project-name")
Projects::HousekeepingService.new(p, :gc).execute
```
+
+### Delete references to missing remote uploads
+
+`gitlab-rake gitlab:uploads:check VERBOSE=1` detects remote objects that do not exist because they were
+deleted externally but their references still exist in the GitLab database.
+
+Example output with error message:
+
+```shell
+$ sudo gitlab-rake gitlab:uploads:check VERBOSE=1
+Checking integrity of Uploads
+- 100..434: Failures: 2
+- Upload: 100: Remote object does not exist
+- Upload: 101: Remote object does not exist
+Done!
+```
+
+To delete these references to remote uploads that were deleted externally, open the [GitLab Rails Console](../troubleshooting/navigating_gitlab_via_rails_console.md#starting-a-rails-console-session)
+and run:
+[Rails Console](../troubleshooting/navigating_gitlab_via_rails_console.md#starting-a-rails-console-session):
+
+```ruby
+uploads_deleted=0
+Upload.find_each do |upload|
+ next if upload.retrieve_uploader.file.exists?
+ uploads_deleted=uploads_deleted + 1
+ p upload ### allow verification before destroy
+ # p upload.destroy! ### uncomment to actually destroy
+end
+p "#{uploads_deleted} remote objects were destroyed."
+```
diff --git a/doc/api/epics.md b/doc/api/epics.md
index e2a179b56dd..5c7366c8457 100644
--- a/doc/api/epics.md
+++ b/doc/api/epics.md
@@ -424,10 +424,10 @@ DELETE /groups/:id/epics/:epic_iid
curl --request DELETE --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/groups/1/epics/5"
```
-## Create a to-do
+## Create a to do
-Manually creates a to-do for the current user on an epic. If
-there already exists a to-do for the user on that epic, status code `304` is
+Manually creates a to do for the current user on an epic. If
+there already exists a to do for the user on that epic, status code `304` is
returned.
```plaintext
diff --git a/doc/api/issues.md b/doc/api/issues.md
index 334447c92cd..b50ea7b42be 100644
--- a/doc/api/issues.md
+++ b/doc/api/issues.md
@@ -1507,10 +1507,10 @@ Example response:
}
```
-## Create a to-do
+## Create a to do
-Manually creates a to-do for the current user on an issue. If
-there already exists a to-do for the user on that issue, status code `304` is
+Manually creates a to do for the current user on an issue. If
+there already exists a to do for the user on that issue, status code `304` is
returned.
```plaintext
diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md
index faefc445210..944a5530100 100644
--- a/doc/api/merge_requests.md
+++ b/doc/api/merge_requests.md
@@ -2085,10 +2085,10 @@ the `approvals_before_merge` parameter:
}
```
-## Create a to-do
+## Create a to do
-Manually creates a to-do for the current user on a merge request.
-If there already exists a to-do for the user on that merge request,
+Manually creates a to do for the current user on a merge request.
+If there already exists a to do for the user on that merge request,
status code `304` is returned.
```plaintext
diff --git a/doc/api/openapi/openapi.yaml b/doc/api/openapi/openapi.yaml
index 8aa4de62501..8c46804d86f 100644
--- a/doc/api/openapi/openapi.yaml
+++ b/doc/api/openapi/openapi.yaml
@@ -9,9 +9,9 @@ info:
When viewing this on gitlab.com, you can test API calls directly from the browser
against the `gitlab.com` instance, if you are logged in.
The feature uses the current [GitLab session cookie](https://docs.gitlab.com/ee/api/README.html#session-cookie),
- so each request is made using your account.
+ so each request is made using your account.
- Read more at <https://docs.gitlab.com/ee/development/documentation/styleguide.html#restful-api>.
+ Read more at <https://docs.gitlab.com/ee/development/documentation/restful_api_styleguide.html>.
version: "v4"
title: "GitLab API"
termsOfService: "https://about.gitlab.com/terms/"
diff --git a/doc/api/todos.md b/doc/api/todos.md
index ebe10ecbd49..ab36021d694 100644
--- a/doc/api/todos.md
+++ b/doc/api/todos.md
@@ -4,13 +4,13 @@ group: Project Management
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
---
-# To-dos API
+# To dos API
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/3188) in GitLab 8.10.
-## Get a list of to-dos
+## Get a list of to dos
-Returns a list of to-dos. When no filter is applied, it returns all pending to-dos
+Returns a list of to dos. When no filter is applied, it returns all pending to dos
for the current user. Different filters allow the user to precise the request.
```plaintext
@@ -25,8 +25,8 @@ Parameters:
| `author_id` | integer | no | The ID of an author |
| `project_id` | integer | no | The ID of a project |
| `group_id` | integer | no | The ID of a group |
-| `state` | string | no | The state of the to-do. Can be either `pending` or `done` |
-| `type` | string | no | The type of a to-do. Can be either `Issue`, `MergeRequest`, `DesignManagement::Design` or `AlertManagement::Alert` |
+| `state` | string | no | The state of the to do. Can be either `pending` or `done` |
+| `type` | string | no | The type of a to do. Can be either `Issue`, `MergeRequest`, `DesignManagement::Design` or `AlertManagement::Alert` |
```shell
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/todos"
@@ -187,10 +187,10 @@ Example Response:
]
```
-## Mark a to-do as done
+## Mark a to do as done
-Marks a single pending to-do given by its ID for the current user as done. The
-to-do marked as done is returned in the response.
+Marks a single pending to do given by its ID for the current user as done. The
+to do marked as done is returned in the response.
```plaintext
POST /todos/:id/mark_as_done
@@ -200,7 +200,7 @@ Parameters:
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer | yes | The ID of a to-do |
+| `id` | integer | yes | The ID of a to do |
```shell
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/todos/130/mark_as_done"
@@ -285,9 +285,9 @@ Example Response:
}
```
-## Mark all to-dos as done
+## Mark all to dos as done
-Marks all pending to-dos for the current user as done. It returns the HTTP status code `204` with an empty response.
+Marks all pending to dos for the current user as done. It returns the HTTP status code `204` with an empty response.
```plaintext
POST /todos/mark_as_done
diff --git a/doc/development/api_styleguide.md b/doc/development/api_styleguide.md
index 400752c69e9..47c06377d88 100644
--- a/doc/development/api_styleguide.md
+++ b/doc/development/api_styleguide.md
@@ -17,7 +17,7 @@ Each new or updated API endpoint must come with documentation, unless it is inte
The docs should be in the same merge request, or, if strictly necessary,
in a follow-up with the same milestone as the original merge request.
-See the [Documentation Style Guide RESTful API section](documentation/styleguide.md#restful-api) for details on documenting API resources in Markdown as well as in OpenAPI definition files.
+See the [Documentation Style Guide RESTful API page](documentation/restful_api_styleguide.md) for details on documenting API resources in Markdown as well as in OpenAPI definition files.
## Methods and parameters description
diff --git a/doc/development/contributing/issue_workflow.md b/doc/development/contributing/issue_workflow.md
index bb7b4713a5e..42c67b97a35 100644
--- a/doc/development/contributing/issue_workflow.md
+++ b/doc/development/contributing/issue_workflow.md
@@ -320,6 +320,11 @@ look for issues labeled `~"Accepting merge requests"` with a [weight of 1](https
More experienced contributors are very welcome to tackle
[any of them](https://gitlab.com/groups/gitlab-org/-/issues?state=opened&label_name[]=Accepting+merge+requests&assignee_id=None).
+For more complex features that have a weight of 2 or more and clear scope, we recommend looking at issues
+with the [label `~"Community Challenge"`](https://gitlab.com/gitlab-org/gitlab/-/issues?scope=all&utf8=%E2%9C%93&state=opened&label_name[]=Accepting%20merge%20requests&label_name[]=Community%20challenge).
+If your MR for the `~"Community Challenge"` issue gets merged, you will also have a chance to win a custom
+GitLab merchandise.
+
If you've decided that you would like to work on an issue, please @-mention
the [appropriate product manager](https://about.gitlab.com/handbook/product/#who-to-talk-to-for-what)
as soon as possible. The product manager will then pull in appropriate GitLab team
diff --git a/doc/development/documentation/graphql_styleguide.md b/doc/development/documentation/graphql_styleguide.md
index 417cdd69801..53a645a2936 100644
--- a/doc/development/documentation/graphql_styleguide.md
+++ b/doc/development/documentation/graphql_styleguide.md
@@ -8,7 +8,7 @@ description: "Writing styles, markup, formatting, and other standards for GraphQ
# GraphQL API
-GraphQL APIs are different from [RESTful APIs](styleguide.md#restful-api). Reference
+GraphQL APIs are different from [RESTful APIs](restful_api_styleguide.md). Reference
information is generated in our [GraphQL reference](../../api/graphql/reference/index.md).
However, it's helpful to include examples on how to use GraphQL for different
diff --git a/doc/development/documentation/restful_api_styleguide.md b/doc/development/documentation/restful_api_styleguide.md
new file mode 100644
index 00000000000..b12578b5d98
--- /dev/null
+++ b/doc/development/documentation/restful_api_styleguide.md
@@ -0,0 +1,180 @@
+---
+type: reference, dev
+stage: none
+group: Development
+info: "See the Technical Writers assigned to Development Guidelines: https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments-to-development-guidelines"
+description: "Writing styles, markup, formatting, and other standards for GitLab's RESTful APIs."
+---
+
+# RESTful API
+
+REST API resources are documented in Markdown under
+[`/doc/api`](https://gitlab.com/gitlab-org/gitlab/-/tree/master/doc/api). Each
+resource has its own Markdown file, which is linked from `api_resources.md`.
+
+When modifying the Markdown, also update the corresponding
+[OpenAPI definition](https://gitlab.com/gitlab-org/gitlab/-/tree/master/doc/api/openapi)
+if one exists for the resource. If not, consider creating one. Match the latest
+[OpenAPI 3.0.x specification](https://swagger.io/specification/). (For more
+information, see the discussion in this
+[issue](https://gitlab.com/gitlab-org/gitlab/-/issues/16023#note_370901810).)
+
+In the Markdown doc for a resource (AKA endpoint):
+
+- Every method must have the REST API request. For example:
+
+ ```plaintext
+ GET /projects/:id/repository/branches
+ ```
+
+- Every method must have a detailed [description of the parameters](#method-description).
+- Every method must have a cURL example.
+- Every method must have a response body (in JSON format).
+
+## API topic template
+
+The following can be used as a template to get started:
+
+````markdown
+## Descriptive title
+
+One or two sentence description of what endpoint does.
+
+```plaintext
+METHOD /endpoint
+```
+
+| Attribute | Type | Required | Description |
+|:------------|:---------|:---------|:----------------------|
+| `attribute` | datatype | yes/no | Detailed description. |
+| `attribute` | datatype | yes/no | Detailed description. |
+
+Example request:
+
+```shell
+curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/endpoint?parameters"
+```
+
+Example response:
+
+```json
+[
+ {
+ }
+]
+```
+````
+
+## Method description
+
+Use the following table headers to describe the methods. Attributes should
+always be in code blocks using backticks (`` ` ``).
+
+```markdown
+| Attribute | Type | Required | Description |
+|:----------|:-----|:---------|:------------|
+```
+
+Rendered example:
+
+| Attribute | Type | Required | Description |
+|:----------|:-------|:---------|:--------------------|
+| `user` | string | yes | The GitLab username. |
+
+## cURL commands
+
+- Use `https://gitlab.example.com/api/v4/` as an endpoint.
+- Wherever needed use this personal access token: `<your_access_token>`.
+- Always put the request first. `GET` is the default so you don't have to
+ include it.
+- Wrap the URL in double quotes (`"`).
+- Prefer to use examples using the personal access token and don't pass data of
+ username and password.
+
+| Methods | Description |
+|:------------------------------------------- |:------------------------------------------------------|
+| `--header "PRIVATE-TOKEN: <your_access_token>"` | Use this method as is, whenever authentication needed. |
+| `--request POST` | Use this method when creating new objects |
+| `--request PUT` | Use this method when updating existing objects |
+| `--request DELETE` | Use this method when removing existing objects |
+
+## cURL Examples
+
+The following sections include a set of [cURL](https://curl.haxx.se) examples
+you can use in the API documentation.
+
+CAUTION: **Caution:**
+Do not use information for real users, URLs, or tokens. For documentation, refer to our
+relevant style guide sections on [Fake user information](styleguide.md#fake-user-information),
+[Fake URLs](styleguide.md#fake-urls), and [Fake tokens](styleguide.md#fake-tokens).
+
+### Simple cURL command
+
+Get the details of a group:
+
+```shell
+curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/groups/gitlab-org"
+```
+
+### cURL example with parameters passed in the URL
+
+Create a new project under the authenticated user's namespace:
+
+```shell
+curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects?name=foo"
+```
+
+### Post data using cURL's `--data`
+
+Instead of using `--request POST` and appending the parameters to the URI, you
+can use cURL's `--data` option. The example below will create a new project
+`foo` under the authenticated user's namespace.
+
+```shell
+curl --data "name=foo" --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects"
+```
+
+### Post data using JSON content
+
+NOTE: **Note:**
+In this example we create a new group. Watch carefully the single and double
+quotes.
+
+```shell
+curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" --header "Content-Type: application/json" --data '{"path": "my-group", "name": "My group"}' "https://gitlab.example.com/api/v4/groups"
+```
+
+### Post data using form-data
+
+Instead of using JSON or urlencode you can use multipart/form-data which
+properly handles data encoding:
+
+```shell
+curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" --form "title=ssh-key" --form "key=ssh-rsa AAAAB3NzaC1yc2EA..." "https://gitlab.example.com/api/v4/users/25/keys"
+```
+
+The above example is run by and administrator and will add an SSH public key
+titled `ssh-key` to user's account which has an ID of 25.
+
+### Escape special characters
+
+Spaces or slashes (`/`) may sometimes result to errors, thus it is recommended
+to escape them when possible. In the example below we create a new issue which
+contains spaces in its title. Observe how spaces are escaped using the `%20`
+ASCII code.
+
+```shell
+curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/42/issues?title=Hello%20Dude"
+```
+
+Use `%2F` for slashes (`/`).
+
+### Pass arrays to API calls
+
+The GitLab API sometimes accepts arrays of strings or integers. For example, to
+exclude specific users when requesting a list of users for a project, you would
+do something like this:
+
+```shell
+curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" --data "skip_users[]=<user_id>" --data "skip_users[]=<user_id>" "https://gitlab.example.com/api/v4/projects/<project_id>/users"
+```
diff --git a/doc/development/documentation/styleguide.md b/doc/development/documentation/styleguide.md
index 19bb7857fd8..1345cf1eb05 100644
--- a/doc/development/documentation/styleguide.md
+++ b/doc/development/documentation/styleguide.md
@@ -480,6 +480,46 @@ more precise and functional, such as `primary` and `secondary`.
For more information see the following [Internet Draft specification](https://tools.ietf.org/html/draft-knodel-terminology-02).
+### Fake user information
+
+You may need to include user information in entries such as a REST call or user profile.
+**Do not** use real user information or email addresses in GitLab documentation. For email
+addresses and names, do use:
+
+- **Email addresses**: Use an email address ending in `example.com`.
+- **Names**: Use strings like `example_username`. Alternatively, use diverse or
+ non-gendered names with common surnames, such as `Sidney Jones`, `Zhang Wei`,
+ or `Maria Garcia`.
+
+### Fake URLs
+
+When including sample URLs in the documentation, use:
+
+- `example.com` when the domain name is generic.
+- `gitlab.example.com` when referring to self-managed instances of GitLab.
+
+### Fake tokens
+
+There may be times where a token is needed to demonstrate an API call using
+cURL or a variable used in CI. It is strongly advised not to use real tokens in
+documentation even if the probability of a token being exploited is low.
+
+You can use the following fake tokens as examples:
+
+| Token type | Token value |
+|:----------------------|:-------------------------------------------------------------------|
+| Private user token | `<your_access_token>` |
+| Personal access token | `n671WNGecHugsdEDPsyo` |
+| Application ID | `2fcb195768c39e9a94cec2c2e32c59c0aad7a3365c10892e8116b5d83d4096b6` |
+| Application secret | `04f294d1eaca42b8692017b426d53bbc8fe75f827734f0260710b83a556082df` |
+| CI/CD variable | `Li8j-mLUVA3eZYjPfd_H` |
+| Specific runner token | `yrnZW46BrtBFqM7xDzE7dddd` |
+| Shared runner token | `6Vk7ZsosqQyfreAxXTZr` |
+| Trigger token | `be20d8dcc028677c931e04f3871a9b` |
+| Webhook secret token | `6XhDroRcYPM5by_h-HLY` |
+| Health check token | `Tu7BgjR9qeZTEyRzGG2P` |
+| Request profile token | `7VgpS4Ax5utVD2esNstz` |
+
### Language to avoid
When creating documentation, limit or avoid the use of the following verb
@@ -1636,6 +1676,15 @@ heading level.
### Text for documentation requiring version text
+When a feature is new or updated, you can add version information as a bulleted
+item in the **Version history**, or as an inline reference with related text.
+
+#### Version text in the **Version History**
+
+If all content in a section is related, add version text in a bulleted list
+following the heading for the section. To render correctly, it must be on its
+own line and surrounded by blank lines.
+
- For features that need to declare the GitLab version that the feature was
introduced. Text similar to the following should be added immediately below
the heading as a blockquote:
@@ -1685,9 +1734,20 @@ heading level.
and replaced by [Feature name](link-to-feature-documentation).
```
-NOTE: **Note:**
-Version text must be on its own line and surrounded by blank lines to render
-correctly.
+#### Inline version text
+
+If you're adding content to an existing topic, you can add version information
+inline with the existing text.
+
+In this case, add `([introduced/deprecated](<link-to-issue>) in GitLab X.X)`.
+If applicable, include the paid tier: `([introduced/deprecated](<link-to-issue>) in [GitLab Premium](https://about.gitlab.com/pricing/) 12.4)`
+
+Including the issue link is encouraged, but isn't a requirement. For example:
+
+```markdown
+The voting strategy (introduced in GitLab 13.4) requires
+the primary and secondary voters to agree.
+```
### Versions in the past or future
@@ -1916,209 +1976,85 @@ steps aren't required, consider setting up a [table](#tables) with headers of
Learn how to [document features deployed behind flags](feature_flags.md). For
guidance on developing GitLab with feature flags, see [Feature flags in development of GitLab](../feature_flags/index.md).
-## RESTful API
-
-REST API resources are documented in Markdown under
-[`/doc/api`](https://gitlab.com/gitlab-org/gitlab/-/tree/master/doc/api). Each
-resource has its own Markdown file, which is linked from `api_resources.md`.
+## GraphQL API
-When modifying the Markdown, also update the corresponding
-[OpenAPI definition](https://gitlab.com/gitlab-org/gitlab/-/tree/master/doc/api/openapi)
-if one exists for the resource. If not, consider creating one. Match the latest
-[OpenAPI 3.0.x specification](https://swagger.io/specification/). (For more
-information, see the discussion in this
-[issue](https://gitlab.com/gitlab-org/gitlab/-/issues/16023#note_370901810).)
+GraphQL APIs are different from [RESTful APIs](restful_api_styleguide.md). Reference
+information is generated in our [GraphQL reference](../../api/graphql/reference/index.md).
-In the Markdown doc for a resource (AKA endpoint):
+However, it's helpful to include examples on how to use GraphQL for different
+*use cases*, with samples that readers can use directly in the
+[GraphiQL explorer](../api_graphql_styleguide.md#graphiql).
-- Every method must have the REST API request. For example:
+This section describes the steps required to add your GraphQL examples to
+GitLab documentation.
- ```plaintext
- GET /projects/:id/repository/branches
- ```
+### Add a dedicated GraphQL page
-- Every method must have a detailed [description of the parameters](#method-description).
-- Every method must have a cURL example.
-- Every method must have a response body (in JSON format).
+To create a dedicated GraphQL page, create a new `.md` file in the
+`doc/api/graphql/` directory. Give that file a functional name, such as
+`import_from_specific_location.md`.
-### API topic template
+### Start the page with an explanation
-The following can be used as a template to get started:
-
-````markdown
-## Descriptive title
-
-One or two sentence description of what endpoint does.
-
-```plaintext
-METHOD /endpoint
-```
-
-| Attribute | Type | Required | Description |
-|:------------|:---------|:---------|:----------------------|
-| `attribute` | datatype | yes/no | Detailed description. |
-| `attribute` | datatype | yes/no | Detailed description. |
-
-Example request:
-
-```shell
-curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/endpoint?parameters"
-```
-
-Example response:
-
-```json
-[
- {
- }
-]
-```
-````
-
-### Fake user information
-
-You may need to demonstrate an API call or a cURL command that includes the name
-and email address of a user. Don't use real user information in API calls:
-
-- **Email addresses**: Use an email address ending in `example.com`.
-- **Names**: Use strings like `Example Username`. Alternatively, use diverse or
- non-gendered names with common surnames, such as `Sidney Jones`, `Zhang Wei`,
- or `Maria Garcia`.
-
-### Fake URLs
-
-When including sample URLs in the documentation, use:
-
-- `example.com` when the domain name is generic.
-- `gitlab.example.com` when referring to self-managed instances of GitLab.
-
-### Fake tokens
-
-There may be times where a token is needed to demonstrate an API call using
-cURL or a variable used in CI. It is strongly advised not to use real tokens in
-documentation even if the probability of a token being exploited is low.
-
-You can use the following fake tokens as examples:
-
-| Token type | Token value |
-|:----------------------|:-------------------------------------------------------------------|
-| Private user token | `<your_access_token>` |
-| Personal access token | `n671WNGecHugsdEDPsyo` |
-| Application ID | `2fcb195768c39e9a94cec2c2e32c59c0aad7a3365c10892e8116b5d83d4096b6` |
-| Application secret | `04f294d1eaca42b8692017b426d53bbc8fe75f827734f0260710b83a556082df` |
-| CI/CD variable | `Li8j-mLUVA3eZYjPfd_H` |
-| Specific runner token | `yrnZW46BrtBFqM7xDzE7dddd` |
-| Shared runner token | `6Vk7ZsosqQyfreAxXTZr` |
-| Trigger token | `be20d8dcc028677c931e04f3871a9b` |
-| Webhook secret token | `6XhDroRcYPM5by_h-HLY` |
-| Health check token | `Tu7BgjR9qeZTEyRzGG2P` |
-| Request profile token | `7VgpS4Ax5utVD2esNstz` |
-
-### Method description
-
-Use the following table headers to describe the methods. Attributes should
-always be in code blocks using backticks (`` ` ``).
+Include a page title that describes the GraphQL functionality in a few words,
+such as:
```markdown
-| Attribute | Type | Required | Description |
-|:----------|:-----|:---------|:------------|
+# Search for [substitute kind of data]
```
-Rendered example:
-
-| Attribute | Type | Required | Description |
-|:----------|:-------|:---------|:--------------------|
-| `user` | string | yes | The GitLab username |
-
-### cURL commands
+Describe the search. One sentence may be all you need. More information may
+help readers learn how to use the example for their GitLab deployments.
-- Use `https://gitlab.example.com/api/v4/` as an endpoint.
-- Wherever needed use this personal access token: `<your_access_token>`.
-- Always put the request first. `GET` is the default so you don't have to
- include it.
-- Wrap the URL in double quotes (`"`).
-- Prefer to use examples using the personal access token and don't pass data of
- username and password.
+### Include a procedure using the GraphiQL explorer
-| Methods | Description |
-|:------------------------------------------- |:------------------------------------------------------|
-| `--header "PRIVATE-TOKEN: <your_access_token>"` | Use this method as is, whenever authentication needed |
-| `--request POST` | Use this method when creating new objects |
-| `--request PUT` | Use this method when updating existing objects |
-| `--request DELETE` | Use this method when removing existing objects |
+The GraphiQL explorer can help readers test queries with working deployments.
+Set up the section with the following:
-### cURL Examples
+- Use the following title:
-The following sections include a set of [cURL](https://curl.haxx.se) examples
-you can use in the API documentation.
-
-#### Simple cURL command
-
-Get the details of a group:
-
-```shell
-curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/groups/gitlab-org"
-```
-
-#### cURL example with parameters passed in the URL
-
-Create a new project under the authenticated user's namespace:
-
-```shell
-curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects?name=foo"
-```
-
-#### Post data using cURL's `--data`
-
-Instead of using `--request POST` and appending the parameters to the URI, you
-can use cURL's `--data` option. The example below will create a new project
-`foo` under the authenticated user's namespace.
-
-```shell
-curl --data "name=foo" --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects"
-```
-
-#### Post data using JSON content
+ ```markdown
+ ## Set up the GraphiQL explorer
+ ```
-NOTE: **Note:**
-In this example we create a new group. Watch carefully the single and double
-quotes.
+- Include a code block with the query that anyone can include in their
+ instance of the GraphiQL explorer:
-```shell
-curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" --header "Content-Type: application/json" --data '{"path": "my-group", "name": "My group"}' "https://gitlab.example.com/api/v4/groups"
-```
+ ````markdown
+ ```graphql
+ query {
+ <insert queries here>
+ }
+ ```
+ ````
-#### Post data using form-data
+- Tell the user what to do:
-Instead of using JSON or urlencode you can use multipart/form-data which
-properly handles data encoding:
+ ```markdown
+ 1. Open the GraphiQL explorer tool in the following URL: `https://gitlab.com/-/graphql-explorer`.
+ 1. Paste the `query` listed above into the left window of your GraphiQL explorer tool.
+ 1. Select Play to get the result shown here:
+ ```
-```shell
-curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" --form "title=ssh-key" --form "key=ssh-rsa AAAAB3NzaC1yc2EA..." "https://gitlab.example.com/api/v4/users/25/keys"
-```
+- Include a screenshot of the result in the GraphiQL explorer. Follow the naming
+ convention described in the [Save the image](#save-the-image) section.
+- Follow up with an example of what you can do with the output. Make sure the
+ example is something that readers can do on their own deployments.
+- Include a link to the [GraphQL API resources](../../api/graphql/reference/index.md).
-The above example is run by and administrator and will add an SSH public key
-titled `ssh-key` to user's account which has an ID of 25.
+### Add the GraphQL example to the Table of Contents
-#### Escape special characters
+You'll need to open a second MR, against the [GitLab documentation repository](https://gitlab.com/gitlab-org/gitlab-docs/).
-Spaces or slashes (`/`) may sometimes result to errors, thus it is recommended
-to escape them when possible. In the example below we create a new issue which
-contains spaces in its title. Observe how spaces are escaped using the `%20`
-ASCII code.
+We store our Table of Contents in the `default-nav.yaml` file, in the
+`content/_data` subdirectory. You can find the GraphQL section under the
+following line:
-```shell
-curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/42/issues?title=Hello%20Dude"
+```yaml
+- category_title: GraphQL
```
-Use `%2F` for slashes (`/`).
+Be aware that CI tests for that second MR will fail with a bad link until the
+main MR that adds the new GraphQL page is merged.
-#### Pass arrays to API calls
-
-The GitLab API sometimes accepts arrays of strings or integers. For example, to
-exclude specific users when requesting a list of users for a project, you would
-do something like this:
-
-```shell
-curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" --data "skip_users[]=<user_id>" --data "skip_users[]=<user_id>" "https://gitlab.example.com/api/v4/projects/<project_id>/users"
-```
+And that's all you need!
diff --git a/doc/development/gotchas.md b/doc/development/gotchas.md
index f7b44e74c17..1044b7edcb8 100644
--- a/doc/development/gotchas.md
+++ b/doc/development/gotchas.md
@@ -3,6 +3,21 @@
The purpose of this guide is to document potential "gotchas" that contributors
might encounter or should avoid during development of GitLab CE and EE.
+## Do not read files from app/assets directory
+
+Since GitLab 10.8, Omnibus has [dropped the `app/assets` directory](https://gitlab.com/gitlab-org/omnibus-gitlab/-/merge_requests/2456),
+after asset compilation. The `ee/app/assets`, `vendor/assets` directories are dropped as well.
+
+This means that reading files from that directory will fail in Omnibus-installed GitLab instances:
+
+```ruby
+file = Rails.root.join('app/assets/images/logo.svg')
+
+# This file does not exist, read will fail with:
+# Errno::ENOENT: No such file or directory @ rb_sysopen
+File.read(file)
+```
+
## Do not assert against the absolute value of a sequence-generated attribute
Consider the following factory:
diff --git a/doc/operations/incident_management/alerts.md b/doc/operations/incident_management/alerts.md
index 78936495706..bbfbb59c644 100644
--- a/doc/operations/incident_management/alerts.md
+++ b/doc/operations/incident_management/alerts.md
@@ -188,7 +188,7 @@ GitLab supports only a single assignee per alert.
**{angle-double-right}** **Expand sidebar** to expand it.
1. In the right sidebar, locate the **Assignee**, and then select **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.
+ GitLab creates a [to-do item](../../user/todos.md) for each user.
![Alert Details View Assignee(s)](./img/alert_todo_assignees_v13_1.png)
@@ -211,13 +211,13 @@ The following actions will result in a system note:
![Alert Details View System Notes](./img/alert_detail_system_notes_v13_1.png)
-#### Create a to-do from an alert
+#### 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:
+add a to do:
1. To display the list of current alerts, navigate to **Operations > Alerts**.
1. Select your desired alert to display its **Alert Management Details View**.
@@ -225,9 +225,9 @@ add a to-do:
![Alert Details Add A To Do](./img/alert_detail_add_todo_v13_1.png)
-Select the **To-Do** **{todo-done}** in the navigation bar to view your current to-do list.
+Select the **To-Do List** **{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)
+![Alert Details Added to do](./img/alert_detail_added_todo_v13_1.png)
#### View an alert's metrics data
diff --git a/doc/user/project/description_templates.md b/doc/user/project/description_templates.md
index dc1d6034813..e0c4097d1c5 100644
--- a/doc/user/project/description_templates.md
+++ b/doc/user/project/description_templates.md
@@ -49,8 +49,8 @@ To create a Markdown file:
1. Click the `+` button next to `master` and click **New file**.
1. Add the name of your issue template to the **File name** text field next to `master`.
- Make sure words are separated with underscores and that your file has the `.md` extension, for
- example `feature_request.md`.
+ Make sure that your file has the `.md` extension, for
+ example `feature_request.md` or `Feature Request.md`.
1. Commit and push to your default branch.
If you don't have a `.gitlab/issue_templates` directory in your repository, you'll need to create it.
diff --git a/doc/user/project/issues/design_management.md b/doc/user/project/issues/design_management.md
index d4738b8d692..b92d99fc4a8 100644
--- a/doc/user/project/issues/design_management.md
+++ b/doc/user/project/issues/design_management.md
@@ -224,7 +224,7 @@ Note that your resolved comment pins will disappear from the Design to free up s
However, if you need to revisit or find a resolved discussion, all of your resolved threads will be
available in the **Resolved Comment** area at the bottom of the right sidebar.
-## Add To-Do for Designs
+## Add to dos for designs
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/198439) in GitLab 13.4.
> - It's [deployed behind a feature flag](../../feature_flags.md), enabled by default.
@@ -235,11 +235,11 @@ available in the **Resolved Comment** area at the bottom of the right sidebar.
CAUTION: **Warning:**
This feature might not be available to you. Check the **version history** note above for details.
-Add a to-do for a design by clicking **Add a To-Do** on the design sidebar:
+Add a to do for a design by clicking **Add a To-Do** on the design sidebar:
![To-Do button](img/design_todo_button_v13_4.png)
-### Enable or disable the design To-Do button **(CORE ONLY)**
+### Enable or disable the design to-do button **(CORE ONLY)**
The **Add a To-Do** button for Designs is under development but ready for production use. It is
deployed behind a feature flag that is **enabled by default**.
diff --git a/doc/user/project/issues/due_dates.md b/doc/user/project/issues/due_dates.md
index 55b45bf9d3d..b3ebefadef0 100644
--- a/doc/user/project/issues/due_dates.md
+++ b/doc/user/project/issues/due_dates.md
@@ -46,7 +46,7 @@ the icon and the date colored red. You can sort issues by those that are
Due dates also appear in your [to-do list](../../todos.md).
-![Issues with due dates in the to-dos](img/due_dates_todos.png)
+![Issues with due dates in the to dos](img/due_dates_todos.png)
The day before an open issue is due, an email will be sent to all participants
of the issue. Like the due date, the "day before the due date" is determined by the
diff --git a/doc/user/project/issues/issue_data_and_actions.md b/doc/user/project/issues/issue_data_and_actions.md
index 5395b0c08a4..95d7f2edac5 100644
--- a/doc/user/project/issues/issue_data_and_actions.md
+++ b/doc/user/project/issues/issue_data_and_actions.md
@@ -242,7 +242,7 @@ and selecting either:
Also:
- You can mention a user or a group present in your GitLab instance with
- `@username` or `@groupname` and they will be notified via To-Do items
+ `@username` or `@groupname` and they will be notified via to-do items
and email, unless they have [disabled all notifications](#notifications)
in their profile settings.
- Mentions for yourself (the current logged in user), will be highlighted
diff --git a/doc/user/project/quick_actions.md b/doc/user/project/quick_actions.md
index 518cf472b50..14ddd9dad6e 100644
--- a/doc/user/project/quick_actions.md
+++ b/doc/user/project/quick_actions.md
@@ -39,7 +39,7 @@ The following quick actions are applicable to descriptions, discussions and thre
| `/copy_metadata <!merge_request>` | ✓ | ✓ | | Copy labels and milestone from another merge request in the project. |
| `/copy_metadata <#issue>` | ✓ | ✓ | | Copy labels and milestone from another issue in the project. |
| `/create_merge_request <branch name>` | ✓ | | | Create a new merge request starting from the current issue. |
-| `/done` | ✓ | ✓ | ✓ | Mark To-Do as done. |
+| `/done` | ✓ | ✓ | ✓ | Mark to do as done. |
| `/due <date>` | ✓ | | | Set due date. Examples of valid `<date>` include `in 2 days`, `this Friday` and `December 31st`. |
| `/duplicate <#issue>` | ✓ | | | Close this issue and mark as a duplicate of another issue. **(CORE)** Also, mark both as related. **(STARTER)** |
| `/epic <epic>` | ✓ | | | Add to epic `<epic>`. The `<epic>` value should be in the format of `&epic`, `group&epic`, or a URL to an epic. **(PREMIUM)** |
@@ -74,7 +74,7 @@ The following quick actions are applicable to descriptions, discussions and thre
| `/tableflip <comment>` | ✓ | ✓ | ✓ | Append the comment with `(╯°□°)╯︵ ┻━┻`. |
| `/target_branch <local branch name>` | | ✓ | | Set target branch. |
| `/title <new title>` | ✓ | ✓ | ✓ | Change title. |
-| `/todo` | ✓ | ✓ | ✓ | Add a To-Do. |
+| `/todo` | ✓ | ✓ | ✓ | Add a to do. |
| `/unassign @user1 @user2` | ✓ | ✓ | | Remove specific assignees. **(STARTER)** |
| `/unassign` | ✓ | ✓ | | Remove all assignees. |
| `/unlabel ~label1 ~label2` or `/remove_label ~label1 ~label2` | ✓ | ✓ | ✓ | Remove all or specific labels. |
diff --git a/doc/user/todos.md b/doc/user/todos.md
index 9575511f151..46f589c1b11 100644
--- a/doc/user/todos.md
+++ b/doc/user/todos.md
@@ -125,7 +125,7 @@ If no action is needed, you can manually mark the to do item as done by
clicking its corresponding **Done** button to have GitLab remove the item from
your To-Do List.
-![A to-do in the To-Do List](img/todos_todo_list_item.png)
+![A to do in the To-Do List](img/todos_todo_list_item.png)
You can also mark a to do item as done by clicking the **Mark as done** button
in the sidebar of an issue or merge request (or epic **(ULTIMATE)**).
@@ -143,9 +143,9 @@ You can use the following types of filters with your To-Do List:
| ------- | ---------------------------------------------------------------- |
| Project | Filter by project. |
| Group | Filter by group. |
-| Author | Filter by the author that triggered the To Do. |
+| Author | Filter by the author that triggered the to do. |
| Type | Filter by issue, merge request, design, or epic. **(ULTIMATE)** |
-| Action | Filter by the action that triggered the To Do. |
+| Action | Filter by the action that triggered the to do. |
You can also filter by more than one of these at the same time. The previously
described [triggering actions](#what-triggers-a-to-do) include:
diff --git a/lib/gitlab/metrics/dashboard/importers/prometheus_metrics.rb b/lib/gitlab/metrics/dashboard/importers/prometheus_metrics.rb
index d1490d5d9b6..8a176be30a2 100644
--- a/lib/gitlab/metrics/dashboard/importers/prometheus_metrics.rb
+++ b/lib/gitlab/metrics/dashboard/importers/prometheus_metrics.rb
@@ -13,11 +13,12 @@ module Gitlab
@dashboard_hash = dashboard_hash
@project = project
@dashboard_path = dashboard_path
+ @affected_environment_ids = []
end
def execute
import
- rescue ActiveRecord::RecordInvalid, ::Gitlab::Metrics::Dashboard::Transformers::TransformerError
+ rescue ActiveRecord::RecordInvalid, Dashboard::Transformers::Errors::BaseError
false
end
@@ -32,28 +33,51 @@ module Gitlab
def import
delete_stale_metrics
create_or_update_metrics
+ update_prometheus_environments
end
# rubocop: disable CodeReuse/ActiveRecord
def create_or_update_metrics
# TODO: use upsert and worker for callbacks?
+
+ affected_metric_ids = []
prometheus_metrics_attributes.each do |attributes|
- prometheus_metric = PrometheusMetric.find_or_initialize_by(attributes.slice(:identifier, :project))
+ prometheus_metric = PrometheusMetric.find_or_initialize_by(attributes.slice(:dashboard_path, :identifier, :project))
prometheus_metric.update!(attributes.slice(*ALLOWED_ATTRIBUTES))
+
+ affected_metric_ids << prometheus_metric.id
end
+
+ @affected_environment_ids += find_alerts(affected_metric_ids).get_environment_id
end
# rubocop: enable CodeReuse/ActiveRecord
def delete_stale_metrics
- identifiers = prometheus_metrics_attributes.map { |metric_attributes| metric_attributes[:identifier] }
+ identifiers_from_yml = prometheus_metrics_attributes.map { |metric_attributes| metric_attributes[:identifier] }
stale_metrics = PrometheusMetric.for_project(project)
.for_dashboard_path(dashboard_path)
.for_group(Enums::PrometheusMetric.groups[:custom])
- .not_identifier(identifiers)
+ .not_identifier(identifiers_from_yml)
+
+ return unless stale_metrics.exists?
+
+ delete_stale_alerts(stale_metrics)
+ stale_metrics.each_batch { |batch| batch.delete_all }
+ end
+
+ def delete_stale_alerts(stale_metrics)
+ stale_alerts = find_alerts(stale_metrics)
+
+ affected_environment_ids = stale_alerts.get_environment_id
+ return unless affected_environment_ids.present?
- # TODO: use destroy_all and worker for callbacks?
- stale_metrics.each(&:destroy)
+ @affected_environment_ids += affected_environment_ids
+ stale_alerts.each_batch { |batch| batch.delete_all }
+ end
+
+ def find_alerts(metrics)
+ Projects::Prometheus::AlertsFinder.new(project: project, metric: metrics).execute
end
def prometheus_metrics_attributes
@@ -65,6 +89,19 @@ module Gitlab
).execute
end
end
+
+ def update_prometheus_environments
+ affected_environments = ::Environment.for_id(@affected_environment_ids.flatten.uniq).for_project(project)
+
+ return unless affected_environments.exists?
+
+ affected_environments.each do |affected_environment|
+ ::Clusters::Applications::ScheduleUpdateService.new(
+ affected_environment.cluster_prometheus_adapter,
+ project
+ ).execute
+ end
+ end
end
end
end
diff --git a/lib/gitlab/metrics/dashboard/stages/custom_dashboard_metrics_inserter.rb b/lib/gitlab/metrics/dashboard/stages/custom_dashboard_metrics_inserter.rb
new file mode 100644
index 00000000000..5ed4466f440
--- /dev/null
+++ b/lib/gitlab/metrics/dashboard/stages/custom_dashboard_metrics_inserter.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Metrics
+ module Dashboard
+ module Stages
+ # Acts on metrics which have been ingested from source controlled dashboards
+ class CustomDashboardMetricsInserter < BaseStage
+ # For each metric in the dashboard config, attempts to
+ # find a corresponding database record. If found, includes
+ # the record's id in the dashboard config.
+ def transform!
+ database_metrics = ::PrometheusMetricsFinder.new(common: false, group: :custom, project: project).execute
+
+ for_metrics do |metric|
+ metric_record = database_metrics.find { |m| m.identifier == metric[:id] }
+ metric[:metric_id] = metric_record.id if metric_record
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/metrics/dashboard/transformers/errors.rb b/lib/gitlab/metrics/dashboard/transformers/errors.rb
index 4d94ab098ae..bc85dc4e131 100644
--- a/lib/gitlab/metrics/dashboard/transformers/errors.rb
+++ b/lib/gitlab/metrics/dashboard/transformers/errors.rb
@@ -4,10 +4,10 @@ module Gitlab
module Metrics
module Dashboard
module Transformers
- TransformerError = Class.new(StandardError)
-
module Errors
- class MissingAttribute < TransformerError
+ BaseError = Class.new(StandardError)
+
+ class MissingAttribute < BaseError
def initialize(attribute_name)
super("Missing attribute: '#{attribute_name}'")
end
diff --git a/spec/factories/prometheus_metrics.rb b/spec/factories/prometheus_metrics.rb
index 83e3845f1c3..503d392a524 100644
--- a/spec/factories/prometheus_metrics.rb
+++ b/spec/factories/prometheus_metrics.rb
@@ -9,6 +9,7 @@ FactoryBot.define do
group { :business }
project
legend { 'legend' }
+ dashboard_path { '.gitlab/dashboards/dashboard_path.yml'}
trait :common do
common { true }
diff --git a/spec/frontend/clusters/components/fluentd_output_settings_spec.js b/spec/frontend/clusters/components/fluentd_output_settings_spec.js
index c263679a45c..25db8785edc 100644
--- a/spec/frontend/clusters/components/fluentd_output_settings_spec.js
+++ b/spec/frontend/clusters/components/fluentd_output_settings_spec.js
@@ -1,5 +1,5 @@
import { shallowMount } from '@vue/test-utils';
-import { GlAlert, GlDeprecatedDropdown, GlFormCheckbox } from '@gitlab/ui';
+import { GlAlert, GlDropdown, GlFormCheckbox } from '@gitlab/ui';
import FluentdOutputSettings from '~/clusters/components/fluentd_output_settings.vue';
import { APPLICATION_STATUS, FLUENTD } from '~/clusters/constants';
import eventHub from '~/clusters/event_hub';
@@ -36,7 +36,7 @@ describe('FluentdOutputSettings', () => {
};
const findSaveButton = () => wrapper.find({ ref: 'saveBtn' });
const findCancelButton = () => wrapper.find({ ref: 'cancelBtn' });
- const findProtocolDropdown = () => wrapper.find(GlDeprecatedDropdown);
+ const findProtocolDropdown = () => wrapper.find(GlDropdown);
const findCheckbox = name =>
wrapper.findAll(GlFormCheckbox).wrappers.find(x => x.text() === name);
const findHost = () => wrapper.find('#fluentd-host');
diff --git a/spec/frontend/clusters/components/ingress_modsecurity_settings_spec.js b/spec/frontend/clusters/components/ingress_modsecurity_settings_spec.js
index 0e46693a4bf..1f07a0b7908 100644
--- a/spec/frontend/clusters/components/ingress_modsecurity_settings_spec.js
+++ b/spec/frontend/clusters/components/ingress_modsecurity_settings_spec.js
@@ -1,5 +1,5 @@
import { shallowMount } from '@vue/test-utils';
-import { GlAlert, GlToggle, GlDeprecatedDropdown } from '@gitlab/ui';
+import { GlAlert, GlToggle, GlDropdown } from '@gitlab/ui';
import IngressModsecuritySettings from '~/clusters/components/ingress_modsecurity_settings.vue';
import { APPLICATION_STATUS, INGRESS } from '~/clusters/constants';
import eventHub from '~/clusters/event_hub';
@@ -33,7 +33,7 @@ describe('IngressModsecuritySettings', () => {
const findCancelButton = () =>
wrapper.find('[data-qa-selector="cancel_ingress_modsecurity_settings"]');
const findModSecurityToggle = () => wrapper.find(GlToggle);
- const findModSecurityDropdown = () => wrapper.find(GlDeprecatedDropdown);
+ const findModSecurityDropdown = () => wrapper.find(GlDropdown);
describe('when ingress is installed', () => {
beforeEach(() => {
diff --git a/spec/frontend/clusters/components/knative_domain_editor_spec.js b/spec/frontend/clusters/components/knative_domain_editor_spec.js
index 11ebe1b5d61..b7f76211fd6 100644
--- a/spec/frontend/clusters/components/knative_domain_editor_spec.js
+++ b/spec/frontend/clusters/components/knative_domain_editor_spec.js
@@ -1,5 +1,5 @@
import { shallowMount } from '@vue/test-utils';
-import { GlDeprecatedDropdownItem, GlButton } from '@gitlab/ui';
+import { GlDropdownItem, GlButton } from '@gitlab/ui';
import KnativeDomainEditor from '~/clusters/components/knative_domain_editor.vue';
import { APPLICATION_STATUS } from '~/clusters/constants';
@@ -112,7 +112,7 @@ describe('KnativeDomainEditor', () => {
createComponent({ knative: { ...knative, availableDomains: [newDomain] } });
jest.spyOn(wrapper.vm, 'selectDomain');
- wrapper.find(GlDeprecatedDropdownItem).vm.$emit('click');
+ wrapper.find(GlDropdownItem).vm.$emit('click');
return wrapper.vm.$nextTick().then(() => {
expect(wrapper.vm.selectDomain).toHaveBeenCalledWith(newDomain);
diff --git a/spec/frontend/clusters/services/crossplane_provider_stack_spec.js b/spec/frontend/clusters/services/crossplane_provider_stack_spec.js
index 57c538d2650..3e5f8de8e7b 100644
--- a/spec/frontend/clusters/services/crossplane_provider_stack_spec.js
+++ b/spec/frontend/clusters/services/crossplane_provider_stack_spec.js
@@ -1,5 +1,5 @@
import { shallowMount } from '@vue/test-utils';
-import { GlDeprecatedDropdownItem, GlIcon } from '@gitlab/ui';
+import { GlDropdownItem, GlIcon } from '@gitlab/ui';
import CrossplaneProviderStack from '~/clusters/components/crossplane_provider_stack.vue';
describe('CrossplaneProviderStack component', () => {
@@ -37,7 +37,7 @@ describe('CrossplaneProviderStack component', () => {
createComponent({ crossplane });
});
- const findDropdownElements = () => wrapper.findAll(GlDeprecatedDropdownItem);
+ const findDropdownElements = () => wrapper.findAll(GlDropdownItem);
const findFirstDropdownElement = () => findDropdownElements().at(0);
afterEach(() => {
diff --git a/spec/frontend/static_site_editor/pages/success_spec.js b/spec/frontend/static_site_editor/pages/success_spec.js
index 712da4825c7..3fc69dc4586 100644
--- a/spec/frontend/static_site_editor/pages/success_spec.js
+++ b/spec/frontend/static_site_editor/pages/success_spec.js
@@ -66,6 +66,7 @@ describe('~/static_site_editor/pages/success.vue', () => {
primaryButtonLink: savedContentMeta.mergeRequest.url,
title: 'Your merge request has been created',
svgPath: mergeRequestsIllustrationPath,
+ svgHeight: 146,
});
});
diff --git a/spec/lib/gitlab/metrics/dashboard/importers/prometheus_metrics_spec.rb b/spec/lib/gitlab/metrics/dashboard/importers/prometheus_metrics_spec.rb
index 09d5e048f6a..ff8f5797f9d 100644
--- a/spec/lib/gitlab/metrics/dashboard/importers/prometheus_metrics_spec.rb
+++ b/spec/lib/gitlab/metrics/dashboard/importers/prometheus_metrics_spec.rb
@@ -8,9 +8,16 @@ RSpec.describe Gitlab::Metrics::Dashboard::Importers::PrometheusMetrics do
describe '#execute' do
let(:project) { create(:project) }
let(:dashboard_path) { 'path/to/dashboard.yml' }
+ let(:prometheus_adapter) { double('adapter', clear_prometheus_reactive_cache!: nil) }
subject { described_class.new(dashboard_hash, project: project, dashboard_path: dashboard_path) }
+ before do
+ allow_next_instance_of(::Clusters::Applications::ScheduleUpdateService) do |update_service|
+ allow(update_service).to receive(:execute)
+ end
+ end
+
context 'valid dashboard' do
let(:dashboard_hash) { load_sample_dashboard }
@@ -21,20 +28,32 @@ RSpec.describe Gitlab::Metrics::Dashboard::Importers::PrometheusMetrics do
end
context 'with existing metrics' do
+ let(:existing_metric_attributes) do
+ {
+ project: project,
+ identifier: 'metric_b',
+ title: 'overwrite',
+ y_label: 'overwrite',
+ query: 'overwrite',
+ unit: 'overwrite',
+ legend: 'overwrite',
+ dashboard_path: dashboard_path
+ }
+ end
+
let!(:existing_metric) do
- create(:prometheus_metric, {
- project: project,
- identifier: 'metric_b',
- title: 'overwrite',
- y_label: 'overwrite',
- query: 'overwrite',
- unit: 'overwrite',
- legend: 'overwrite'
- })
+ create(:prometheus_metric, existing_metric_attributes)
+ end
+
+ let!(:existing_alert) do
+ alert = create(:prometheus_alert, project: project, prometheus_metric: existing_metric)
+ existing_metric.prometheus_alerts << alert
+
+ alert
end
it 'updates existing PrometheusMetrics' do
- described_class.new(dashboard_hash, project: project, dashboard_path: dashboard_path).execute
+ subject.execute
expect(existing_metric.reload.attributes.with_indifferent_access).to include({
title: 'Super Chart B',
@@ -49,6 +68,15 @@ RSpec.describe Gitlab::Metrics::Dashboard::Importers::PrometheusMetrics do
expect { subject.execute }.to change { PrometheusMetric.count }.by(2)
end
+ it 'updates affected environments' do
+ expect(::Clusters::Applications::ScheduleUpdateService).to receive(:new).with(
+ existing_alert.environment.cluster_prometheus_adapter,
+ project
+ ).and_return(double('ScheduleUpdateService', execute: true))
+
+ subject.execute
+ end
+
context 'with stale metrics' do
let!(:stale_metric) do
create(:prometheus_metric,
@@ -59,11 +87,45 @@ RSpec.describe Gitlab::Metrics::Dashboard::Importers::PrometheusMetrics do
)
end
+ let!(:stale_alert) do
+ alert = create(:prometheus_alert, project: project, prometheus_metric: stale_metric)
+ stale_metric.prometheus_alerts << alert
+
+ alert
+ end
+
+ it 'updates existing PrometheusMetrics' do
+ subject.execute
+
+ expect(existing_metric.reload.attributes.with_indifferent_access).to include({
+ title: 'Super Chart B',
+ y_label: 'y_label',
+ query: 'query',
+ unit: 'unit',
+ legend: 'Legend Label'
+ })
+ end
+
it 'deletes stale metrics' do
subject.execute
expect { stale_metric.reload }.to raise_error(ActiveRecord::RecordNotFound)
end
+
+ it 'deletes stale alert' do
+ subject.execute
+
+ expect { stale_alert.reload }.to raise_error(ActiveRecord::RecordNotFound)
+ end
+
+ it 'updates affected environments' do
+ expect(::Clusters::Applications::ScheduleUpdateService).to receive(:new).with(
+ existing_alert.environment.cluster_prometheus_adapter,
+ project
+ ).and_return(double('ScheduleUpdateService', execute: true))
+
+ subject.execute
+ end
end
end
end
diff --git a/spec/services/git/branch_hooks_service_spec.rb b/spec/services/git/branch_hooks_service_spec.rb
index db25bb766c9..a5290f0be68 100644
--- a/spec/services/git/branch_hooks_service_spec.rb
+++ b/spec/services/git/branch_hooks_service_spec.rb
@@ -429,4 +429,26 @@ RSpec.describe Git::BranchHooksService do
end
end
end
+
+ describe 'Metrics dashboard sync' do
+ context 'with feature flag enabled' do
+ before do
+ Feature.enable(:metrics_dashboards_sync)
+ end
+
+ it 'imports metrics to database' do
+ expect(Metrics::Dashboard::SyncDashboardsWorker).to receive(:perform_async)
+
+ service.execute
+ end
+ end
+
+ context 'with feature flag disabled' do
+ it 'imports metrics to database' do
+ expect(Metrics::Dashboard::SyncDashboardsWorker).to receive(:perform_async)
+
+ service.execute
+ end
+ end
+ end
end
diff --git a/spec/services/metrics/dashboard/custom_dashboard_service_spec.rb b/spec/services/metrics/dashboard/custom_dashboard_service_spec.rb
index aea9c25d104..5dc30c156ac 100644
--- a/spec/services/metrics/dashboard/custom_dashboard_service_spec.rb
+++ b/spec/services/metrics/dashboard/custom_dashboard_service_spec.rb
@@ -67,6 +67,23 @@ RSpec.describe Metrics::Dashboard::CustomDashboardService, :use_clean_rails_memo
.at_least(:once)
end
+ context 'with metric in database' do
+ let!(:prometheus_metric) do
+ create(:prometheus_metric, project: project, identifier: 'metric_a1', group: 'custom')
+ end
+
+ it 'includes metric_id' do
+ dashboard = described_class.new(*service_params).get_dashboard
+
+ metric_id = dashboard[:dashboard][:panel_groups].find { |panel_group| panel_group[:group] == 'Group A' }
+ .fetch(:panels).find { |panel| panel[:title] == 'Super Chart A1' }
+ .fetch(:metrics).find { |metric| metric[:id] == 'metric_a1' }
+ .fetch(:metric_id)
+
+ expect(metric_id).to eq(prometheus_metric.id)
+ end
+ end
+
context 'and the dashboard is then deleted' do
it 'does not return the previously cached dashboard' do
described_class.new(*service_params).get_dashboard
diff --git a/spec/workers/metrics/dashboard/sync_dashboards_worker_spec.rb b/spec/workers/metrics/dashboard/sync_dashboards_worker_spec.rb
new file mode 100644
index 00000000000..19b79835825
--- /dev/null
+++ b/spec/workers/metrics/dashboard/sync_dashboards_worker_spec.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Metrics::Dashboard::SyncDashboardsWorker do
+ include MetricsDashboardHelpers
+ subject(:worker) { described_class.new }
+
+ let(:project) { project_with_dashboard(dashboard_path) }
+ let(:dashboard_path) { '.gitlab/dashboards/test.yml' }
+
+ describe ".perform" do
+ it 'imports metrics' do
+ expect { worker.perform(project.id) }.to change(PrometheusMetric, :count).by(3)
+ end
+
+ it 'is idempotent' do
+ 2.times do
+ worker.perform(project.id)
+ end
+
+ expect(PrometheusMetric.count).to eq(3)
+ end
+ end
+end