summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/graphql/resolvers/metrics/dashboard_resolver.rb21
-rw-r--r--app/graphql/types/environment_type.rb4
-rw-r--r--app/graphql/types/metrics/dashboard_type.rb15
-rw-r--r--app/models/performance_monitoring/prometheus_dashboard.rb34
-rw-r--r--app/models/prometheus_alert.rb3
-rw-r--r--changelogs/unreleased/204908-ensure-correct-prometheus-alert-found.yml5
-rw-r--r--changelogs/unreleased/georgekoltsov-remove_trial_ends_on_from_group_export.yml5
-rw-r--r--changelogs/unreleased/mwaw-211330-add-metrics-dashboard-graphql-resource.yml5
-rw-r--r--doc/administration/instance_limits.md12
-rw-r--r--doc/api/graphql/reference/gitlab_schema.graphql17
-rw-r--r--doc/api/graphql/reference/gitlab_schema.json54
-rw-r--r--doc/api/graphql/reference/index.md7
-rw-r--r--doc/development/understanding_explain_plans.md4
-rw-r--r--doc/user/project/integrations/prometheus.md1
-rw-r--r--lib/gitlab/alerting/alert.rb19
-rw-r--r--lib/gitlab/diff/highlight_cache.rb2
-rw-r--r--lib/gitlab/import_export/group/import_export.yml3
-rw-r--r--spec/fixtures/group.json1
-rw-r--r--spec/fixtures/group_export.tar.gzbin2795 -> 3546 bytes
-rw-r--r--spec/fixtures/group_export_invalid_subrelations.tar.gzbin3036 -> 3602 bytes
-rw-r--r--spec/fixtures/lib/gitlab/import_export/group_exports/complex/group.json9
-rw-r--r--spec/fixtures/lib/gitlab/import_export/group_exports/no_children/group.json3
-rw-r--r--spec/fixtures/lib/gitlab/import_export/group_exports/visibility_levels/internal/group.json12
-rw-r--r--spec/fixtures/lib/gitlab/import_export/group_exports/visibility_levels/private/group.json12
-rw-r--r--spec/fixtures/lib/gitlab/import_export/group_exports/visibility_levels/public/group.json12
-rw-r--r--spec/graphql/resolvers/metrics/dashboard_resolver_spec.rb44
-rw-r--r--spec/graphql/types/environment_type_spec.rb2
-rw-r--r--spec/graphql/types/metrics/dashboard_type_spec.rb15
-rw-r--r--spec/lib/gitlab/alerting/alert_spec.rb42
-rw-r--r--spec/models/prometheus_alert_spec.rb3
-rw-r--r--spec/requests/api/graphql/metrics/dashboard_query_spec.rb82
31 files changed, 379 insertions, 69 deletions
diff --git a/app/graphql/resolvers/metrics/dashboard_resolver.rb b/app/graphql/resolvers/metrics/dashboard_resolver.rb
new file mode 100644
index 00000000000..05d82ca0f46
--- /dev/null
+++ b/app/graphql/resolvers/metrics/dashboard_resolver.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+module Resolvers
+ module Metrics
+ class DashboardResolver < Resolvers::BaseResolver
+ argument :path, GraphQL::STRING_TYPE,
+ required: true,
+ description: "Path to a file which defines metrics dashboard eg: 'config/prometheus/common_metrics.yml'"
+
+ type Types::Metrics::DashboardType, null: true
+
+ alias_method :environment, :object
+
+ def resolve(**args)
+ return unless environment
+
+ ::PerformanceMonitoring::PrometheusDashboard.find_for(project: environment.project, user: context[:current_user], path: args[:path], options: { environment: environment })
+ end
+ end
+ end
+end
diff --git a/app/graphql/types/environment_type.rb b/app/graphql/types/environment_type.rb
index c165c0ddc61..34a90006d03 100644
--- a/app/graphql/types/environment_type.rb
+++ b/app/graphql/types/environment_type.rb
@@ -15,5 +15,9 @@ module Types
field :state, GraphQL::STRING_TYPE, null: false,
description: 'State of the environment, for example: available/stopped'
+
+ field :metrics_dashboard, Types::Metrics::DashboardType, null: true,
+ description: 'Metrics dashboard schema for the environment',
+ resolver: Resolvers::Metrics::DashboardResolver
end
end
diff --git a/app/graphql/types/metrics/dashboard_type.rb b/app/graphql/types/metrics/dashboard_type.rb
new file mode 100644
index 00000000000..11e834013ca
--- /dev/null
+++ b/app/graphql/types/metrics/dashboard_type.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+module Types
+ module Metrics
+ # rubocop: disable Graphql/AuthorizeTypes
+ # Authorization is performed at environment level
+ class DashboardType < ::Types::BaseObject
+ graphql_name 'MetricsDashboard'
+
+ field :path, GraphQL::STRING_TYPE, null: true,
+ description: 'Path to a file with the dashboard definition'
+ end
+ # rubocop: enable Graphql/AuthorizeTypes
+ end
+end
diff --git a/app/models/performance_monitoring/prometheus_dashboard.rb b/app/models/performance_monitoring/prometheus_dashboard.rb
index 5f2df444fd0..30fb1935a27 100644
--- a/app/models/performance_monitoring/prometheus_dashboard.rb
+++ b/app/models/performance_monitoring/prometheus_dashboard.rb
@@ -4,27 +4,41 @@ module PerformanceMonitoring
class PrometheusDashboard
include ActiveModel::Model
- attr_accessor :dashboard, :panel_groups
+ attr_accessor :dashboard, :panel_groups, :path, :environment, :priority
validates :dashboard, presence: true
validates :panel_groups, presence: true
- def self.from_json(json_content)
- dashboard = new(
- dashboard: json_content['dashboard'],
- panel_groups: json_content['panel_groups'].map { |group| PrometheusPanelGroup.from_json(group) }
- )
-
- dashboard.tap(&:validate!)
+ class << self
+ def from_json(json_content)
+ dashboard = new(
+ dashboard: json_content['dashboard'],
+ panel_groups: json_content['panel_groups'].map { |group| PrometheusPanelGroup.from_json(group) }
+ )
+
+ dashboard.tap(&:validate!)
+ end
+
+ def find_for(project:, user:, path:, options: {})
+ dashboard_response = Gitlab::Metrics::Dashboard::Finder.find(project, user, options.merge(dashboard_path: path))
+ return unless dashboard_response[:status] == :success
+
+ new(
+ {
+ path: path,
+ environment: options[:environment]
+ }.merge(dashboard_response[:dashboard])
+ )
+ end
end
def to_yaml
- self.as_json(only: valid_attributes).to_yaml
+ self.as_json(only: yaml_valid_attributes).to_yaml
end
private
- def valid_attributes
+ def yaml_valid_attributes
%w(panel_groups panels metrics group priority type title y_label weight id unit label query query_range dashboard)
end
end
diff --git a/app/models/prometheus_alert.rb b/app/models/prometheus_alert.rb
index a1303f59129..fbc0281296f 100644
--- a/app/models/prometheus_alert.rb
+++ b/app/models/prometheus_alert.rb
@@ -56,7 +56,8 @@ class PrometheusAlert < ApplicationRecord
"for" => "5m",
"labels" => {
"gitlab" => "hook",
- "gitlab_alert_id" => prometheus_metric_id
+ "gitlab_alert_id" => prometheus_metric_id,
+ "gitlab_prometheus_alert_id" => id
}
}
end
diff --git a/changelogs/unreleased/204908-ensure-correct-prometheus-alert-found.yml b/changelogs/unreleased/204908-ensure-correct-prometheus-alert-found.yml
new file mode 100644
index 00000000000..35f48fd884b
--- /dev/null
+++ b/changelogs/unreleased/204908-ensure-correct-prometheus-alert-found.yml
@@ -0,0 +1,5 @@
+---
+title: Prevent wrong environment being used when processing Prometheus alert
+merge_request: 29119
+author:
+type: fixed
diff --git a/changelogs/unreleased/georgekoltsov-remove_trial_ends_on_from_group_export.yml b/changelogs/unreleased/georgekoltsov-remove_trial_ends_on_from_group_export.yml
new file mode 100644
index 00000000000..ff61cf92cc9
--- /dev/null
+++ b/changelogs/unreleased/georgekoltsov-remove_trial_ends_on_from_group_export.yml
@@ -0,0 +1,5 @@
+---
+title: Exclude 'trial_ends_on', 'shared_runners_minutes_limit' & 'extra_shared_runners_minutes_limit' from list of exported Group attributes
+merge_request: 29259
+author:
+type: fixed
diff --git a/changelogs/unreleased/mwaw-211330-add-metrics-dashboard-graphql-resource.yml b/changelogs/unreleased/mwaw-211330-add-metrics-dashboard-graphql-resource.yml
new file mode 100644
index 00000000000..ab4801c4a48
--- /dev/null
+++ b/changelogs/unreleased/mwaw-211330-add-metrics-dashboard-graphql-resource.yml
@@ -0,0 +1,5 @@
+---
+title: Add graphQL interface to fetch metrics dashboard
+merge_request: 29112
+author:
+type: added
diff --git a/doc/administration/instance_limits.md b/doc/administration/instance_limits.md
index cf4cd9f4345..57acdec4ea2 100644
--- a/doc/administration/instance_limits.md
+++ b/doc/administration/instance_limits.md
@@ -178,6 +178,18 @@ Plan.default.limits.update!(ci_pipeline_schedules: 100)
## Instance monitoring and metrics
+### Incident Management inbound alert limits
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/14932) in GitLab 12.5.
+
+Limiting inbound alerts for an incident reduces the number of alerts (issues)
+that can be created within a period of time, which can help prevent overloading
+your incident responders with duplicate issues. You can reduce the volume of
+alerts in the following ways:
+
+- Max requests per period per project, 3600 seconds by default.
+- Rate limit period in seconds, 3600 seconds by default.
+
### Prometheus Alert JSON payloads
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/14929) in GitLab 12.6.
diff --git a/doc/api/graphql/reference/gitlab_schema.graphql b/doc/api/graphql/reference/gitlab_schema.graphql
index e79d52d4d10..22ca25e45d3 100644
--- a/doc/api/graphql/reference/gitlab_schema.graphql
+++ b/doc/api/graphql/reference/gitlab_schema.graphql
@@ -1883,6 +1883,16 @@ type Environment {
id: ID!
"""
+ Metrics dashboard schema for the environment
+ """
+ metricsDashboard(
+ """
+ Path to a file which defines metrics dashboard eg: 'config/prometheus/common_metrics.yml'
+ """
+ path: String!
+ ): MetricsDashboard
+
+ """
Human-readable name of the environment
"""
name: String!
@@ -5278,6 +5288,13 @@ type Metadata {
version: String!
}
+type MetricsDashboard {
+ """
+ Path to a file with the dashboard definition
+ """
+ path: String
+}
+
"""
Represents a milestone.
"""
diff --git a/doc/api/graphql/reference/gitlab_schema.json b/doc/api/graphql/reference/gitlab_schema.json
index 820f41755d0..40bd27062b3 100644
--- a/doc/api/graphql/reference/gitlab_schema.json
+++ b/doc/api/graphql/reference/gitlab_schema.json
@@ -5581,6 +5581,33 @@
"deprecationReason": null
},
{
+ "name": "metricsDashboard",
+ "description": "Metrics dashboard schema for the environment",
+ "args": [
+ {
+ "name": "path",
+ "description": "Path to a file which defines metrics dashboard eg: 'config/prometheus/common_metrics.yml'",
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ }
+ },
+ "defaultValue": null
+ }
+ ],
+ "type": {
+ "kind": "OBJECT",
+ "name": "MetricsDashboard",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
"name": "name",
"description": "Human-readable name of the environment",
"args": [
@@ -15119,6 +15146,33 @@
},
{
"kind": "OBJECT",
+ "name": "MetricsDashboard",
+ "description": null,
+ "fields": [
+ {
+ "name": "path",
+ "description": "Path to a file with the dashboard definition",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ }
+ ],
+ "inputFields": null,
+ "interfaces": [
+
+ ],
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
+ "kind": "OBJECT",
"name": "Milestone",
"description": "Represents a milestone.",
"fields": [
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index 6ed3c5a778e..e1375530bf4 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -324,6 +324,7 @@ Describes where code is deployed for a project
| Name | Type | Description |
| --- | ---- | ---------- |
| `id` | ID! | ID of the environment |
+| `metricsDashboard` | MetricsDashboard | Metrics dashboard schema for the environment |
| `name` | String! | Human-readable name of the environment |
| `state` | String! | State of the environment, for example: available/stopped |
@@ -815,6 +816,12 @@ Autogenerated return type of MergeRequestSetWip
| `revision` | String! | Revision |
| `version` | String! | Version |
+## MetricsDashboard
+
+| Name | Type | Description |
+| --- | ---- | ---------- |
+| `path` | String | Path to a file with the dashboard definition |
+
## Milestone
Represents a milestone.
diff --git a/doc/development/understanding_explain_plans.md b/doc/development/understanding_explain_plans.md
index 48791e201eb..8c71a27540d 100644
--- a/doc/development/understanding_explain_plans.md
+++ b/doc/development/understanding_explain_plans.md
@@ -721,7 +721,7 @@ For example, in order to test new index you can do the following:
Create the index:
```sql
-exec CREATE INDEX index_projects_marked_for_deletion ON projects (marked_for_deletion_at) WHERE marked_for_deletion_at IS NOT NULL
+exec CREATE INDEX index_projects_last_activity ON projects (last_activity_at) WHERE last_activity_at IS NOT NULL
```
Analyze the table to update its statistics:
@@ -733,7 +733,7 @@ exec ANALYZE projects
Get the query plan:
```sql
-explain SELECT * FROM projects WHERE marked_for_deletion_at < CURRENT_DATE
+explain SELECT * FROM projects WHERE last_activity_at < CURRENT_DATE
```
Once done you can rollback your changes:
diff --git a/doc/user/project/integrations/prometheus.md b/doc/user/project/integrations/prometheus.md
index 4b07ecd2743..88a9bd40f07 100644
--- a/doc/user/project/integrations/prometheus.md
+++ b/doc/user/project/integrations/prometheus.md
@@ -309,6 +309,7 @@ The following tables outline the details of expected properties.
| `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. |
+| `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
diff --git a/lib/gitlab/alerting/alert.rb b/lib/gitlab/alerting/alert.rb
index f6f52e392c6..7d97bd1bb52 100644
--- a/lib/gitlab/alerting/alert.rb
+++ b/lib/gitlab/alerting/alert.rb
@@ -21,6 +21,12 @@ module Gitlab
end
end
+ def gitlab_prometheus_alert_id
+ strong_memoize(:gitlab_prometheus_alert_id) do
+ payload&.dig('labels', 'gitlab_prometheus_alert_id')
+ end
+ end
+
def title
strong_memoize(:title) do
gitlab_alert&.title || parse_title_from_payload
@@ -120,12 +126,19 @@ module Gitlab
end
def parse_gitlab_alert_from_payload
- return unless metric_id
+ alerts_found = matching_gitlab_alerts
+
+ return if alerts_found.blank? || alerts_found.size > 1
+
+ alerts_found.first
+ end
+
+ def matching_gitlab_alerts
+ return unless metric_id || gitlab_prometheus_alert_id
Projects::Prometheus::AlertsFinder
- .new(project: project, metric: metric_id)
+ .new(project: project, metric: metric_id, id: gitlab_prometheus_alert_id)
.execute
- .first
end
def parse_title_from_payload
diff --git a/lib/gitlab/diff/highlight_cache.rb b/lib/gitlab/diff/highlight_cache.rb
index e79127108b4..055eae2c0fd 100644
--- a/lib/gitlab/diff/highlight_cache.rb
+++ b/lib/gitlab/diff/highlight_cache.rb
@@ -14,7 +14,7 @@ module Gitlab
define_histogram :gitlab_redis_diff_caching_memory_usage_bytes do
docstring 'Redis diff caching memory usage by key'
- buckets [100, 1000, 10000, 100000, 1000000, 10000000]
+ buckets [100, 1_000, 10_000, 100_000, 1_000_000, 10_000_000]
end
define_counter :gitlab_redis_diff_caching_hit do
diff --git a/lib/gitlab/import_export/group/import_export.yml b/lib/gitlab/import_export/group/import_export.yml
index 49b9e0f83d9..5008639077c 100644
--- a/lib/gitlab/import_export/group/import_export.yml
+++ b/lib/gitlab/import_export/group/import_export.yml
@@ -36,6 +36,9 @@ excluded_attributes:
- :runners_token_encrypted
- :saml_discovery_token
- :visibility_level
+ - :trial_ends_on
+ - :shared_runners_minute_limit
+ - :extra_shared_runners_minutes_limit
epics:
- :state_id
diff --git a/spec/fixtures/group.json b/spec/fixtures/group.json
new file mode 100644
index 00000000000..86de34e2f3b
--- /dev/null
+++ b/spec/fixtures/group.json
@@ -0,0 +1 @@
+{"name":"H5bp","path":"h5bp","owner_id":null,"created_at":"2020-01-09 12:08:57 UTC","updated_at":"2020-01-09 12:08:57 UTC","description":"A voluptate non sequi temporibus quam at.","avatar":{"url":null},"membership_lock":false,"share_with_group_lock":false,"visibility_level":20,"request_access_enabled":true,"ldap_sync_status":"ready","ldap_sync_error":null,"ldap_sync_last_update_at":null,"ldap_sync_last_successful_update_at":null,"ldap_sync_last_sync_at":null,"lfs_enabled":null,"parent_id":null,"repository_size_limit":null,"require_two_factor_authentication":false,"two_factor_grace_period":48,"plan_id":null,"project_creation_level":2,"file_template_project_id":null,"saml_discovery_token":"JZGRJbe3","custom_project_templates_group_id":null,"auto_devops_enabled":null,"last_ci_minutes_notification_at":null,"last_ci_minutes_usage_notification_level":null,"subgroup_creation_level":1,"emails_disabled":null,"max_pages_size":null,"max_artifacts_size":null,"mentions_disabled":null,"milestones":[],"badges":[],"labels":[{"id":69,"title":"Amfunc","color":"#2658d0","project_id":null,"created_at":"2020-01-09T12:08:57.693Z","updated_at":"2020-01-09T12:08:57.693Z","template":false,"description":null,"group_id":28,"type":"GroupLabel","priorities":[],"textColor":"#FFFFFF"},{"id":68,"title":"Aquacell","color":"#f658e0","project_id":null,"created_at":"2020-01-09T12:08:57.688Z","updated_at":"2020-01-09T12:08:57.688Z","template":false,"description":null,"group_id":28,"type":"GroupLabel","priorities":[],"textColor":"#333333"},{"id":62,"title":"Bryns","color":"#16f0b9","project_id":null,"created_at":"2020-01-09T12:08:57.672Z","updated_at":"2020-01-09T12:08:57.672Z","template":false,"description":null,"group_id":28,"type":"GroupLabel","priorities":[],"textColor":"#FFFFFF"},{"id":61,"title":"Pephfunc","color":"#556319","project_id":null,"created_at":"2020-01-09T12:08:57.669Z","updated_at":"2020-01-09T12:08:57.669Z","template":false,"description":null,"group_id":28,"type":"GroupLabel","priorities":[],"textColor":"#FFFFFF"},{"id":67,"title":"Sinesync","color":"#4c44e8","project_id":null,"created_at":"2020-01-09T12:08:57.685Z","updated_at":"2020-01-09T12:08:57.685Z","template":false,"description":null,"group_id":28,"type":"GroupLabel","priorities":[],"textColor":"#FFFFFF"},{"id":65,"title":"Traphwood","color":"#71b900","project_id":null,"created_at":"2020-01-09T12:08:57.679Z","updated_at":"2020-01-09T12:08:57.679Z","template":false,"description":null,"group_id":28,"type":"GroupLabel","priorities":[],"textColor":"#FFFFFF"},{"id":66,"title":"Tricell","color":"#179ad3","project_id":null,"created_at":"2020-01-09T12:08:57.683Z","updated_at":"2020-01-09T12:08:57.683Z","template":false,"description":null,"group_id":28,"type":"GroupLabel","priorities":[],"textColor":"#FFFFFF"},{"id":63,"title":"Trienceforge","color":"#cb1e0e","project_id":null,"created_at":"2020-01-09T12:08:57.674Z","updated_at":"2020-01-09T12:08:57.674Z","template":false,"description":null,"group_id":28,"type":"GroupLabel","priorities":[],"textColor":"#FFFFFF"},{"id":64,"title":"Trinix","color":"#66ca41","project_id":null,"created_at":"2020-01-09T12:08:57.677Z","updated_at":"2020-01-09T12:08:57.677Z","template":false,"description":null,"group_id":28,"type":"GroupLabel","priorities":[],"textColor":"#FFFFFF"}],"boards":[],"members":[{"id":8,"access_level":10,"source_id":28,"source_type":"Namespace","user_id":13,"notification_level":3,"created_at":"2020-01-09T12:08:57.884Z","updated_at":"2020-01-09T12:08:57.884Z","created_by_id":null,"invite_email":null,"invite_token":null,"invite_accepted_at":null,"requested_at":null,"expires_at":null,"ldap":false,"override":false,"user":{"id":13,"email":"lorraine@leannon.info","username":"elina.lakin"}},{"id":7,"access_level":30,"source_id":28,"source_type":"Namespace","user_id":1,"notification_level":3,"created_at":"2020-01-09T12:08:57.129Z","updated_at":"2020-01-09T12:08:57.898Z","created_by_id":null,"invite_email":null,"invite_token":null,"invite_accepted_at":null,"requested_at":null,"expires_at":null,"ldap":false,"override":false,"user":{"id":1,"email":"admin@example.com","username":"root"}},{"id":9,"access_level":20,"source_id":28,"source_type":"Namespace","user_id":8,"notification_level":3,"created_at":"2020-01-09T12:08:57.901Z","updated_at":"2020-01-09T12:08:57.901Z","created_by_id":null,"invite_email":null,"invite_token":null,"invite_accepted_at":null,"requested_at":null,"expires_at":null,"ldap":false,"override":false,"user":{"id":8,"email":"euna_schroeder@bernier.us","username":"loreen_medhurst"}},{"id":10,"access_level":40,"source_id":28,"source_type":"Namespace","user_id":10,"notification_level":3,"created_at":"2020-01-09T12:08:57.918Z","updated_at":"2020-01-09T12:08:57.918Z","created_by_id":null,"invite_email":null,"invite_token":null,"invite_accepted_at":null,"requested_at":null,"expires_at":null,"ldap":false,"override":false,"user":{"id":10,"email":"glady_green@prohaska.biz","username":"maia_kuhlman"}}],"epics":[{"id":31,"group_id":28,"author_id":13,"assignee_id":null,"iid":1,"updated_by_id":null,"last_edited_by_id":null,"lock_version":0,"start_date":null,"end_date":null,"last_edited_at":null,"created_at":"2020-01-09T12:11:12.006Z","updated_at":"2020-01-09T12:11:12.006Z","title":"Optio nihil consequatur et vel quaerat cupiditate.","description":"Vel molestias repellendus id voluptatem et non. Blanditiis natus veritatis adipisci qui illo. Sed est veritatis facilis dolore voluptas quibusdam aliquam omnis.\n\nFacere delectus quo architecto explicabo dolores. Quis odit nostrum nobis consequuntur cumque est officiis. Maiores ipsa nobis minus incidunt provident.\n\nVoluptate voluptatem debitis facilis architecto commodi vero. Sit quibusdam consectetur ad quis cupiditate. Exercitationem quasi inventore incidunt itaque optio autem accusantium.","start_date_sourcing_milestone_id":null,"due_date_sourcing_milestone_id":null,"start_date_fixed":null,"due_date_fixed":null,"start_date_is_fixed":null,"due_date_is_fixed":null,"closed_by_id":null,"closed_at":null,"parent_id":null,"relative_position":null,"state_id":"opened","start_date_sourcing_epic_id":null,"due_date_sourcing_epic_id":null,"notes":[]},{"id":32,"group_id":28,"author_id":1,"assignee_id":null,"iid":2,"updated_by_id":null,"last_edited_by_id":null,"lock_version":0,"start_date":null,"end_date":null,"last_edited_at":null,"created_at":"2020-01-09T12:11:12.021Z","updated_at":"2020-01-09T12:11:12.021Z","title":"Maxime cumque totam deleniti excepturi iusto qui sint.","description":"Nesciunt ut sit voluptas iusto rem. Sint doloribus ex et aliquam ea. Nihil necessitatibus distinctio similique incidunt. Consequatur voluptatibus excepturi qui quis quia. Quia optio quod non magnam dolorem id molestiae dignissimos.\n\nAt eligendi fuga veritatis accusamus quo dolores. Eos amet dolores nisi illo voluptatem consequuntur alias. Dolor veniam quas est sed. Ex corporis soluta sit ducimus facere et.\n\nEst odit asperiores vel quae quibusdam maiores quod. Debitis libero quo sed sunt voluptas praesentium. Ut nisi qui et culpa. Consequatur atque aut molestiae sint. Quaerat expedita animi distinctio repellat sed.","start_date_sourcing_milestone_id":null,"due_date_sourcing_milestone_id":null,"start_date_fixed":null,"due_date_fixed":null,"start_date_is_fixed":null,"due_date_is_fixed":null,"closed_by_id":null,"closed_at":null,"parent_id":null,"relative_position":null,"state_id":"opened","start_date_sourcing_epic_id":null,"due_date_sourcing_epic_id":null,"notes":[]},{"id":33,"group_id":28,"author_id":8,"assignee_id":null,"iid":3,"updated_by_id":null,"last_edited_by_id":null,"lock_version":0,"start_date":null,"end_date":null,"last_edited_at":null,"created_at":"2020-01-09T12:11:12.035Z","updated_at":"2020-01-09T12:11:12.035Z","title":"Corrupti perferendis labore omnis pariatur dolores aut repellendus quisquam id.","description":"Eligendi est asperiores minus nulla unde ex. Aspernatur repellat vel deleniti molestiae occaecati odio dicta. Ipsam excepturi quia modi ut autem sed. Omnis quae quam quas ut. Et corrupti aut omnis blanditiis.\n\nDolore dolorem accusamus tempore nihil dolores repellendus sed provident. Commodi consectetur blanditiis et consequatur laborum nulla eveniet facilis. Inventore aut officiis fuga est maiores rerum. Sint aspernatur et necessitatibus omnis qui et blanditiis.\n\nFugiat molestias nulla eos labore rerum perspiciatis non. Minus est et ipsum quis animi et. Id praesentium vitae in eum fugiat incidunt. Voluptas ut nemo ut cum saepe quasi dolores unde.","start_date_sourcing_milestone_id":null,"due_date_sourcing_milestone_id":null,"start_date_fixed":null,"due_date_fixed":null,"start_date_is_fixed":null,"due_date_is_fixed":null,"closed_by_id":null,"closed_at":null,"parent_id":null,"relative_position":null,"state_id":"opened","start_date_sourcing_epic_id":null,"due_date_sourcing_epic_id":null,"notes":[]},{"id":34,"group_id":28,"author_id":10,"assignee_id":null,"iid":4,"updated_by_id":null,"last_edited_by_id":null,"lock_version":0,"start_date":null,"end_date":null,"last_edited_at":null,"created_at":"2020-01-09T12:11:12.049Z","updated_at":"2020-01-09T12:11:12.049Z","title":"Architecto ipsa autem ducimus velit harum doloribus odio fugiat.","description":"Eos perferendis commodi labore id. Ipsa esse non voluptatem sit. Odit et et maiores quis adipisci sed earum.\n\nRerum asperiores consequuntur sed odit. Quis architecto nobis dolor sit consectetur. Inventore praesentium est magnam et reprehenderit impedit occaecati similique.\n\nTemporibus culpa ratione vel facilis perspiciatis rerum. Quisquam ut veniam quia sit totam. Quo ipsam quam dolorum quis. Totam porro nesciunt ut temporibus.","start_date_sourcing_milestone_id":null,"due_date_sourcing_milestone_id":null,"start_date_fixed":null,"due_date_fixed":null,"start_date_is_fixed":null,"due_date_is_fixed":null,"closed_by_id":null,"closed_at":null,"parent_id":null,"relative_position":null,"state_id":"opened","start_date_sourcing_epic_id":null,"due_date_sourcing_epic_id":null,"notes":[]},{"id":35,"group_id":28,"author_id":10,"assignee_id":null,"iid":5,"updated_by_id":null,"last_edited_by_id":null,"lock_version":0,"start_date":null,"end_date":null,"last_edited_at":null,"created_at":"2020-01-09T12:11:12.062Z","updated_at":"2020-01-09T12:11:12.062Z","title":"Accusantium illo a sint nobis qui explicabo eum iusto facere aut.","description":"Modi laudantium possimus beatae dolorem molestias ut neque. Voluptatem libero harum id cupiditate. Officia et enim quia dolores at adipisci.\n\nEx officia quia at et vel aut. Esse consequatur officiis magnam vel velit eius. Sit cumque quo qui laborum tempore rerum in.\n\nQuibusdam provident quis molestiae laborum odio commodi. Impedit iure voluptatem possimus necessitatibus et error non. Iure est et ipsa recusandae et.","start_date_sourcing_milestone_id":null,"due_date_sourcing_milestone_id":null,"start_date_fixed":null,"due_date_fixed":null,"start_date_is_fixed":null,"due_date_is_fixed":null,"closed_by_id":null,"closed_at":null,"parent_id":null,"relative_position":null,"state_id":"opened","start_date_sourcing_epic_id":null,"due_date_sourcing_epic_id":null,"notes":[]}]}
diff --git a/spec/fixtures/group_export.tar.gz b/spec/fixtures/group_export.tar.gz
index d76c6ddba25..5f5fd989f75 100644
--- a/spec/fixtures/group_export.tar.gz
+++ b/spec/fixtures/group_export.tar.gz
Binary files differ
diff --git a/spec/fixtures/group_export_invalid_subrelations.tar.gz b/spec/fixtures/group_export_invalid_subrelations.tar.gz
index 6a8f1453517..6844d166260 100644
--- a/spec/fixtures/group_export_invalid_subrelations.tar.gz
+++ b/spec/fixtures/group_export_invalid_subrelations.tar.gz
Binary files differ
diff --git a/spec/fixtures/lib/gitlab/import_export/group_exports/complex/group.json b/spec/fixtures/lib/gitlab/import_export/group_exports/complex/group.json
index 001bf7b43c1..a5a85dc661f 100644
--- a/spec/fixtures/lib/gitlab/import_export/group_exports/complex/group.json
+++ b/spec/fixtures/lib/gitlab/import_export/group_exports/complex/group.json
@@ -19,18 +19,15 @@
"ldap_sync_last_sync_at": null,
"lfs_enabled": null,
"parent_id": 7,
- "shared_runners_minutes_limit": null,
"repository_size_limit": null,
"require_two_factor_authentication": false,
"two_factor_grace_period": 48,
"plan_id": null,
"project_creation_level": 2,
- "trial_ends_on": null,
"file_template_project_id": null,
"saml_discovery_token": "rBKx3ioz",
"custom_project_templates_group_id": null,
"auto_devops_enabled": null,
- "extra_shared_runners_minutes_limit": null,
"last_ci_minutes_notification_at": null,
"last_ci_minutes_usage_notification_level": null,
"runners_token": "token",
@@ -1075,18 +1072,15 @@
"ldap_sync_last_sync_at": null,
"lfs_enabled": null,
"parent_id": 4351,
- "shared_runners_minutes_limit": null,
"repository_size_limit": null,
"require_two_factor_authentication": false,
"two_factor_grace_period": 48,
"plan_id": null,
"project_creation_level": 2,
- "trial_ends_on": null,
"file_template_project_id": null,
"saml_discovery_token": "ki3Xnjw3",
"custom_project_templates_group_id": null,
"auto_devops_enabled": null,
- "extra_shared_runners_minutes_limit": null,
"last_ci_minutes_notification_at": null,
"last_ci_minutes_usage_notification_level": null,
"subgroup_creation_level": 1,
@@ -1649,18 +1643,15 @@
"ldap_sync_last_sync_at": null,
"lfs_enabled": null,
"parent_id": 4351,
- "shared_runners_minutes_limit": null,
"repository_size_limit": null,
"require_two_factor_authentication": false,
"two_factor_grace_period": 48,
"plan_id": null,
"project_creation_level": 2,
- "trial_ends_on": null,
"file_template_project_id": null,
"saml_discovery_token": "m7cx4AZi",
"custom_project_templates_group_id": null,
"auto_devops_enabled": null,
- "extra_shared_runners_minutes_limit": null,
"last_ci_minutes_notification_at": null,
"last_ci_minutes_usage_notification_level": null,
"subgroup_creation_level": 1,
diff --git a/spec/fixtures/lib/gitlab/import_export/group_exports/no_children/group.json b/spec/fixtures/lib/gitlab/import_export/group_exports/no_children/group.json
index 3ffa899405e..158cd22ed2f 100644
--- a/spec/fixtures/lib/gitlab/import_export/group_exports/no_children/group.json
+++ b/spec/fixtures/lib/gitlab/import_export/group_exports/no_children/group.json
@@ -19,18 +19,15 @@
"ldap_sync_last_sync_at": null,
"lfs_enabled": null,
"parent_id": null,
- "shared_runners_minutes_limit": null,
"repository_size_limit": null,
"require_two_factor_authentication": false,
"two_factor_grace_period": 48,
"plan_id": null,
"project_creation_level": 2,
- "trial_ends_on": null,
"file_template_project_id": null,
"saml_discovery_token": "rBKx3ioz",
"custom_project_templates_group_id": null,
"auto_devops_enabled": null,
- "extra_shared_runners_minutes_limit": null,
"last_ci_minutes_notification_at": null,
"last_ci_minutes_usage_notification_level": null,
"subgroup_creation_level": 1,
diff --git a/spec/fixtures/lib/gitlab/import_export/group_exports/visibility_levels/internal/group.json b/spec/fixtures/lib/gitlab/import_export/group_exports/visibility_levels/internal/group.json
index f747088f87e..01dc44a28d5 100644
--- a/spec/fixtures/lib/gitlab/import_export/group_exports/visibility_levels/internal/group.json
+++ b/spec/fixtures/lib/gitlab/import_export/group_exports/visibility_levels/internal/group.json
@@ -20,17 +20,14 @@
"ldap_sync_last_sync_at": null,
"lfs_enabled": null,
"parent_id": null,
- "shared_runners_minutes_limit": null,
"repository_size_limit": null,
"require_two_factor_authentication": false,
"two_factor_grace_period": 48,
"plan_id": null,
"project_creation_level": 2,
- "trial_ends_on": null,
"file_template_project_id": null,
"custom_project_templates_group_id": null,
"auto_devops_enabled": null,
- "extra_shared_runners_minutes_limit": null,
"last_ci_minutes_notification_at": null,
"last_ci_minutes_usage_notification_level": null,
"subgroup_creation_level": 1,
@@ -61,17 +58,14 @@
"ldap_sync_last_sync_at": null,
"lfs_enabled": null,
"parent_id": 283,
- "shared_runners_minutes_limit": null,
"repository_size_limit": null,
"require_two_factor_authentication": false,
"two_factor_grace_period": 48,
"plan_id": null,
"project_creation_level": 2,
- "trial_ends_on": null,
"file_template_project_id": null,
"custom_project_templates_group_id": null,
"auto_devops_enabled": null,
- "extra_shared_runners_minutes_limit": null,
"last_ci_minutes_notification_at": null,
"last_ci_minutes_usage_notification_level": null,
"subgroup_creation_level": 1,
@@ -102,17 +96,14 @@
"ldap_sync_last_sync_at": null,
"lfs_enabled": null,
"parent_id": 283,
- "shared_runners_minutes_limit": null,
"repository_size_limit": null,
"require_two_factor_authentication": false,
"two_factor_grace_period": 48,
"plan_id": null,
"project_creation_level": 2,
- "trial_ends_on": null,
"file_template_project_id": null,
"custom_project_templates_group_id": null,
"auto_devops_enabled": null,
- "extra_shared_runners_minutes_limit": null,
"last_ci_minutes_notification_at": null,
"last_ci_minutes_usage_notification_level": null,
"subgroup_creation_level": 1,
@@ -143,17 +134,14 @@
"ldap_sync_last_sync_at": null,
"lfs_enabled": null,
"parent_id": 283,
- "shared_runners_minutes_limit": null,
"repository_size_limit": null,
"require_two_factor_authentication": false,
"two_factor_grace_period": 48,
"plan_id": null,
"project_creation_level": 2,
- "trial_ends_on": null,
"file_template_project_id": null,
"custom_project_templates_group_id": null,
"auto_devops_enabled": null,
- "extra_shared_runners_minutes_limit": null,
"last_ci_minutes_notification_at": null,
"last_ci_minutes_usage_notification_level": null,
"subgroup_creation_level": 1,
diff --git a/spec/fixtures/lib/gitlab/import_export/group_exports/visibility_levels/private/group.json b/spec/fixtures/lib/gitlab/import_export/group_exports/visibility_levels/private/group.json
index 1328e596fa5..c9323f27770 100644
--- a/spec/fixtures/lib/gitlab/import_export/group_exports/visibility_levels/private/group.json
+++ b/spec/fixtures/lib/gitlab/import_export/group_exports/visibility_levels/private/group.json
@@ -20,17 +20,14 @@
"ldap_sync_last_sync_at": null,
"lfs_enabled": null,
"parent_id": null,
- "shared_runners_minutes_limit": null,
"repository_size_limit": null,
"require_two_factor_authentication": false,
"two_factor_grace_period": 48,
"plan_id": null,
"project_creation_level": 2,
- "trial_ends_on": null,
"file_template_project_id": null,
"custom_project_templates_group_id": null,
"auto_devops_enabled": null,
- "extra_shared_runners_minutes_limit": null,
"last_ci_minutes_notification_at": null,
"last_ci_minutes_usage_notification_level": null,
"subgroup_creation_level": 1,
@@ -61,17 +58,14 @@
"ldap_sync_last_sync_at": null,
"lfs_enabled": null,
"parent_id": 283,
- "shared_runners_minutes_limit": null,
"repository_size_limit": null,
"require_two_factor_authentication": false,
"two_factor_grace_period": 48,
"plan_id": null,
"project_creation_level": 2,
- "trial_ends_on": null,
"file_template_project_id": null,
"custom_project_templates_group_id": null,
"auto_devops_enabled": null,
- "extra_shared_runners_minutes_limit": null,
"last_ci_minutes_notification_at": null,
"last_ci_minutes_usage_notification_level": null,
"subgroup_creation_level": 1,
@@ -102,17 +96,14 @@
"ldap_sync_last_sync_at": null,
"lfs_enabled": null,
"parent_id": 283,
- "shared_runners_minutes_limit": null,
"repository_size_limit": null,
"require_two_factor_authentication": false,
"two_factor_grace_period": 48,
"plan_id": null,
"project_creation_level": 2,
- "trial_ends_on": null,
"file_template_project_id": null,
"custom_project_templates_group_id": null,
"auto_devops_enabled": null,
- "extra_shared_runners_minutes_limit": null,
"last_ci_minutes_notification_at": null,
"last_ci_minutes_usage_notification_level": null,
"subgroup_creation_level": 1,
@@ -143,17 +134,14 @@
"ldap_sync_last_sync_at": null,
"lfs_enabled": null,
"parent_id": 283,
- "shared_runners_minutes_limit": null,
"repository_size_limit": null,
"require_two_factor_authentication": false,
"two_factor_grace_period": 48,
"plan_id": null,
"project_creation_level": 2,
- "trial_ends_on": null,
"file_template_project_id": null,
"custom_project_templates_group_id": null,
"auto_devops_enabled": null,
- "extra_shared_runners_minutes_limit": null,
"last_ci_minutes_notification_at": null,
"last_ci_minutes_usage_notification_level": null,
"subgroup_creation_level": 1,
diff --git a/spec/fixtures/lib/gitlab/import_export/group_exports/visibility_levels/public/group.json b/spec/fixtures/lib/gitlab/import_export/group_exports/visibility_levels/public/group.json
index 29020e92004..b4f746b28e2 100644
--- a/spec/fixtures/lib/gitlab/import_export/group_exports/visibility_levels/public/group.json
+++ b/spec/fixtures/lib/gitlab/import_export/group_exports/visibility_levels/public/group.json
@@ -20,17 +20,14 @@
"ldap_sync_last_sync_at": null,
"lfs_enabled": null,
"parent_id": null,
- "shared_runners_minutes_limit": null,
"repository_size_limit": null,
"require_two_factor_authentication": false,
"two_factor_grace_period": 48,
"plan_id": null,
"project_creation_level": 2,
- "trial_ends_on": null,
"file_template_project_id": null,
"custom_project_templates_group_id": null,
"auto_devops_enabled": null,
- "extra_shared_runners_minutes_limit": null,
"last_ci_minutes_notification_at": null,
"last_ci_minutes_usage_notification_level": null,
"subgroup_creation_level": 1,
@@ -61,17 +58,14 @@
"ldap_sync_last_sync_at": null,
"lfs_enabled": null,
"parent_id": 283,
- "shared_runners_minutes_limit": null,
"repository_size_limit": null,
"require_two_factor_authentication": false,
"two_factor_grace_period": 48,
"plan_id": null,
"project_creation_level": 2,
- "trial_ends_on": null,
"file_template_project_id": null,
"custom_project_templates_group_id": null,
"auto_devops_enabled": null,
- "extra_shared_runners_minutes_limit": null,
"last_ci_minutes_notification_at": null,
"last_ci_minutes_usage_notification_level": null,
"subgroup_creation_level": 1,
@@ -102,17 +96,14 @@
"ldap_sync_last_sync_at": null,
"lfs_enabled": null,
"parent_id": 283,
- "shared_runners_minutes_limit": null,
"repository_size_limit": null,
"require_two_factor_authentication": false,
"two_factor_grace_period": 48,
"plan_id": null,
"project_creation_level": 2,
- "trial_ends_on": null,
"file_template_project_id": null,
"custom_project_templates_group_id": null,
"auto_devops_enabled": null,
- "extra_shared_runners_minutes_limit": null,
"last_ci_minutes_notification_at": null,
"last_ci_minutes_usage_notification_level": null,
"subgroup_creation_level": 1,
@@ -143,17 +134,14 @@
"ldap_sync_last_sync_at": null,
"lfs_enabled": null,
"parent_id": 283,
- "shared_runners_minutes_limit": null,
"repository_size_limit": null,
"require_two_factor_authentication": false,
"two_factor_grace_period": 48,
"plan_id": null,
"project_creation_level": 2,
- "trial_ends_on": null,
"file_template_project_id": null,
"custom_project_templates_group_id": null,
"auto_devops_enabled": null,
- "extra_shared_runners_minutes_limit": null,
"last_ci_minutes_notification_at": null,
"last_ci_minutes_usage_notification_level": null,
"subgroup_creation_level": 1,
diff --git a/spec/graphql/resolvers/metrics/dashboard_resolver_spec.rb b/spec/graphql/resolvers/metrics/dashboard_resolver_spec.rb
new file mode 100644
index 00000000000..6a8eb8a65af
--- /dev/null
+++ b/spec/graphql/resolvers/metrics/dashboard_resolver_spec.rb
@@ -0,0 +1,44 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Resolvers::Metrics::DashboardResolver do
+ include GraphqlHelpers
+
+ let_it_be(:current_user) { create(:user) }
+
+ describe '#resolve' do
+ subject(:resolve_dashboard) { resolve(described_class, obj: parent_object, args: args, ctx: { current_user: current_user }) }
+
+ let(:args) do
+ {
+ path: 'config/prometheus/common_metrics.yml'
+ }
+ end
+
+ context 'for environment' do
+ let(:project) { create(:project) }
+ let(:parent_object) { create(:environment, project: project) }
+
+ before do
+ project.add_developer(current_user)
+ end
+
+ it 'use ActiveModel class to find matching dashboard', :aggregate_failures do
+ expected_arguments = { project: project, user: current_user, path: args[:path], options: { environment: parent_object } }
+
+ expect(PerformanceMonitoring::PrometheusDashboard).to receive(:find_for).with(expected_arguments).and_return(PerformanceMonitoring::PrometheusDashboard.new)
+ expect(resolve_dashboard).to be_instance_of PerformanceMonitoring::PrometheusDashboard
+ end
+
+ context 'without parent object' do
+ let(:parent_object) { nil }
+
+ it 'returns nil', :aggregate_failures do
+ expect(PerformanceMonitoring::PrometheusDashboard).not_to receive(:find_for)
+ expect(resolve_dashboard).to be_nil
+ end
+ end
+ end
+ end
+end
diff --git a/spec/graphql/types/environment_type_spec.rb b/spec/graphql/types/environment_type_spec.rb
index b3711fa2f5c..24a8bddfa6a 100644
--- a/spec/graphql/types/environment_type_spec.rb
+++ b/spec/graphql/types/environment_type_spec.rb
@@ -7,7 +7,7 @@ describe GitlabSchema.types['Environment'] do
it 'has the expected fields' do
expected_fields = %w[
- name id state
+ name id state metrics_dashboard
]
expect(described_class).to have_graphql_fields(*expected_fields)
diff --git a/spec/graphql/types/metrics/dashboard_type_spec.rb b/spec/graphql/types/metrics/dashboard_type_spec.rb
new file mode 100644
index 00000000000..4795fd77537
--- /dev/null
+++ b/spec/graphql/types/metrics/dashboard_type_spec.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe GitlabSchema.types['MetricsDashboard'] do
+ it { expect(described_class.graphql_name).to eq('MetricsDashboard') }
+
+ it 'has the expected fields' do
+ expected_fields = %w[
+ path
+ ]
+
+ expect(described_class).to have_graphql_fields(*expected_fields)
+ end
+end
diff --git a/spec/lib/gitlab/alerting/alert_spec.rb b/spec/lib/gitlab/alerting/alert_spec.rb
index 8b67cf949ce..6d97f08af91 100644
--- a/spec/lib/gitlab/alerting/alert_spec.rb
+++ b/spec/lib/gitlab/alerting/alert_spec.rb
@@ -9,11 +9,14 @@ describe Gitlab::Alerting::Alert do
let(:payload) { {} }
shared_context 'gitlab alert' do
- let(:gitlab_alert_id) { gitlab_alert.prometheus_metric_id.to_s }
let!(:gitlab_alert) { create(:prometheus_alert, project: project) }
+ let(:gitlab_alert_id) { gitlab_alert.id }
before do
- payload['labels'] = { 'gitlab_alert_id' => gitlab_alert_id }
+ payload['labels'] = {
+ 'gitlab_alert_id' => gitlab_alert.prometheus_metric_id.to_s,
+ 'gitlab_prometheus_alert_id' => gitlab_alert_id
+ }
end
end
@@ -68,6 +71,41 @@ describe Gitlab::Alerting::Alert do
it { is_expected.to be_nil }
end
+
+ context 'when two alerts with the same metric exist' do
+ include_context 'gitlab alert'
+
+ let!(:second_gitlab_alert) do
+ create(:prometheus_alert,
+ project: project,
+ prometheus_metric_id: gitlab_alert.prometheus_metric_id
+ )
+ end
+
+ context 'alert id given in params' do
+ before do
+ payload['labels'] = {
+ 'gitlab_alert_id' => gitlab_alert.prometheus_metric_id.to_s,
+ 'gitlab_prometheus_alert_id' => second_gitlab_alert.id
+ }
+ end
+
+ it { is_expected.to eq(second_gitlab_alert) }
+ end
+
+ context 'metric id given in params' do
+ # This tests the case when two alerts are found, as metric id
+ # is not unique.
+
+ # Note the metric id was incorrectly named as 'gitlab_alert_id'
+ # in PrometheusAlert#to_param.
+ before do
+ payload['labels'] = { 'gitlab_alert_id' => gitlab_alert.prometheus_metric_id }
+ end
+
+ it { is_expected.to be_nil }
+ end
+ end
end
describe '#title' do
diff --git a/spec/models/prometheus_alert_spec.rb b/spec/models/prometheus_alert_spec.rb
index cdcdb46a6c4..1409cf65fee 100644
--- a/spec/models/prometheus_alert_spec.rb
+++ b/spec/models/prometheus_alert_spec.rb
@@ -96,7 +96,8 @@ describe PrometheusAlert do
"for" => "5m",
"labels" => {
"gitlab" => "hook",
- "gitlab_alert_id" => metric.id
+ "gitlab_alert_id" => metric.id,
+ "gitlab_prometheus_alert_id" => subject.id
})
end
end
diff --git a/spec/requests/api/graphql/metrics/dashboard_query_spec.rb b/spec/requests/api/graphql/metrics/dashboard_query_spec.rb
new file mode 100644
index 00000000000..8b0965a815b
--- /dev/null
+++ b/spec/requests/api/graphql/metrics/dashboard_query_spec.rb
@@ -0,0 +1,82 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'Getting Metrics Dashboard' do
+ include GraphqlHelpers
+
+ let_it_be(:current_user) { create(:user) }
+ let(:project) { create(:project) }
+ let!(:environment) { create(:environment, project: project) }
+
+ let(:fields) do
+ <<~QUERY
+ #{all_graphql_fields_for('MetricsDashboard'.classify)}
+ QUERY
+ end
+
+ let(:query) do
+ %(
+ query {
+ project(fullPath:"#{project.full_path}") {
+ environments(name: "#{environment.name}") {
+ nodes {
+ metricsDashboard(path: "#{path}"){
+ #{fields}
+ }
+ }
+ }
+ }
+ }
+ )
+ end
+
+ context 'for anonymous user' do
+ before do
+ post_graphql(query, current_user: current_user)
+ end
+
+ context 'requested dashboard is available' do
+ let(:path) { 'config/prometheus/common_metrics.yml' }
+
+ it_behaves_like 'a working graphql query'
+
+ it 'returns nil' do
+ dashboard = graphql_data.dig('project', 'environments', 'nodes')
+
+ expect(dashboard).to be_nil
+ end
+ end
+ end
+
+ context 'for user with developer access' do
+ before do
+ project.add_developer(current_user)
+ post_graphql(query, current_user: current_user)
+ end
+
+ context 'requested dashboard is available' do
+ let(:path) { 'config/prometheus/common_metrics.yml' }
+
+ it_behaves_like 'a working graphql query'
+
+ it 'returns metrics dashboard' do
+ dashboard = graphql_data.dig('project', 'environments', 'nodes')[0]['metricsDashboard']
+
+ expect(dashboard).to eql("path" => path)
+ end
+ end
+
+ context 'requested dashboard can not be found' do
+ let(:path) { 'config/prometheus/i_am_not_here.yml' }
+
+ it_behaves_like 'a working graphql query'
+
+ it 'return snil' do
+ dashboard = graphql_data.dig('project', 'environments', 'nodes')[0]['metricsDashboard']
+
+ expect(dashboard).to be_nil
+ end
+ end
+ end
+end