Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
1782886ed2
commit
0bc6d00165
|
@ -13,6 +13,7 @@
|
|||
/doc/development/ @marcia @mjang1
|
||||
/doc/development/documentation/ @mikelewis
|
||||
/doc/ci @marcel.amirault @sselhorn
|
||||
/doc/operations @aqualls @eread
|
||||
/doc/user/clusters @aqualls
|
||||
/doc/user/infrastructure @aqualls
|
||||
/doc/user/project/clusters @aqualls
|
||||
|
|
4
Gemfile
4
Gemfile
|
@ -244,7 +244,9 @@ gem 'slack-messenger', '~> 2.3.3'
|
|||
gem 'hangouts-chat', '~> 0.0.5'
|
||||
|
||||
# Asana integration
|
||||
gem 'asana', '~> 0.9'
|
||||
# asana 0.10.1 needs faraday 1.0
|
||||
# https://gitlab.com/gitlab-org/gitlab/-/issues/224296
|
||||
gem 'asana', '0.10.0'
|
||||
|
||||
# FogBugz integration
|
||||
gem 'ruby-fogbugz', '~> 0.2.1'
|
||||
|
|
|
@ -76,7 +76,7 @@ GEM
|
|||
apollo_upload_server (2.0.0.beta.3)
|
||||
graphql (>= 1.8)
|
||||
rails (>= 4.2)
|
||||
asana (0.9.3)
|
||||
asana (0.10.0)
|
||||
faraday (~> 0.9)
|
||||
faraday_middleware (~> 0.9)
|
||||
faraday_middleware-multi_json (~> 0.0)
|
||||
|
@ -304,7 +304,7 @@ GEM
|
|||
multipart-post (>= 1.2, < 3)
|
||||
faraday-http-cache (2.0.0)
|
||||
faraday (~> 0.8)
|
||||
faraday_middleware (0.12.2)
|
||||
faraday_middleware (0.14.0)
|
||||
faraday (>= 0.7.4, < 1.0)
|
||||
faraday_middleware-aws-signers-v4 (0.1.7)
|
||||
aws-sdk-resources (~> 2)
|
||||
|
@ -1175,7 +1175,7 @@ DEPENDENCIES
|
|||
addressable (~> 2.7)
|
||||
akismet (~> 3.0)
|
||||
apollo_upload_server (~> 2.0.0.beta3)
|
||||
asana (~> 0.9)
|
||||
asana (= 0.10.0)
|
||||
asciidoctor (~> 2.0.10)
|
||||
asciidoctor-include-ext (~> 0.3.1)
|
||||
asciidoctor-plantuml (~> 0.0.12)
|
||||
|
|
|
@ -375,7 +375,7 @@ export const fetchDashboardValidationWarnings = ({ state, dispatch }) => {
|
|||
})
|
||||
.then(resp => resp.data?.project?.environments?.nodes?.[0]?.metricsDashboard)
|
||||
.then(({ schemaValidationWarnings } = {}) => {
|
||||
const hasWarnings = schemaValidationWarnings?.length !== 0;
|
||||
const hasWarnings = schemaValidationWarnings && schemaValidationWarnings.length !== 0;
|
||||
/**
|
||||
* The payload of the dispatch is a boolean, because at the moment a standard
|
||||
* warning message is shown instead of the warnings the BE returns
|
||||
|
|
|
@ -55,13 +55,22 @@ export default {
|
|||
|
||||
<template>
|
||||
<div class="d-inline-block">
|
||||
<button v-gl-modal="modalId" type="button" class="btn btn-danger">{{ __('Delete') }}</button>
|
||||
<button
|
||||
v-gl-modal="modalId"
|
||||
type="button"
|
||||
class="btn btn-danger"
|
||||
data-qa-selector="delete_button"
|
||||
>
|
||||
{{ __('Delete') }}
|
||||
</button>
|
||||
<gl-modal
|
||||
:title="title"
|
||||
:ok-title="s__('WikiPageConfirmDelete|Delete page')"
|
||||
:action-primary="{
|
||||
text: s__('WikiPageConfirmDelete|Delete page'),
|
||||
attributes: { variant: 'danger', 'data-qa-selector': 'confirm_deletion_button' },
|
||||
}"
|
||||
:modal-id="modalId"
|
||||
title-tag="h4"
|
||||
ok-variant="danger"
|
||||
@ok="onSubmit"
|
||||
>
|
||||
{{ message }}
|
||||
|
|
|
@ -8,6 +8,7 @@ module SnippetsActions
|
|||
include PaginatedCollection
|
||||
include Gitlab::NoteableMetadata
|
||||
include Snippets::SendBlob
|
||||
include SnippetsSort
|
||||
|
||||
included do
|
||||
skip_before_action :verify_authenticity_token,
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module SnippetsSort
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
def sort_param
|
||||
params[:sort].presence || 'updated_desc'
|
||||
end
|
||||
end
|
|
@ -3,6 +3,7 @@
|
|||
class Dashboard::SnippetsController < Dashboard::ApplicationController
|
||||
include PaginatedCollection
|
||||
include Gitlab::NoteableMetadata
|
||||
include SnippetsSort
|
||||
|
||||
skip_cross_project_access_check :index
|
||||
|
||||
|
@ -11,7 +12,7 @@ class Dashboard::SnippetsController < Dashboard::ApplicationController
|
|||
.new(current_user, author: current_user)
|
||||
.execute
|
||||
|
||||
@snippets = SnippetsFinder.new(current_user, author: current_user, scope: params[:scope])
|
||||
@snippets = SnippetsFinder.new(current_user, author: current_user, scope: params[:scope], sort: sort_param)
|
||||
.execute
|
||||
.page(params[:page])
|
||||
.inc_author
|
||||
|
|
|
@ -19,7 +19,7 @@ class Projects::SnippetsController < Projects::Snippets::ApplicationController
|
|||
.new(current_user, project: @project)
|
||||
.execute
|
||||
|
||||
@snippets = SnippetsFinder.new(current_user, project: @project, scope: params[:scope])
|
||||
@snippets = SnippetsFinder.new(current_user, project: @project, scope: params[:scope], sort: sort_param)
|
||||
.execute
|
||||
.page(params[:page])
|
||||
.inc_author
|
||||
|
|
|
@ -21,7 +21,7 @@ class SnippetsController < Snippets::ApplicationController
|
|||
if params[:username].present?
|
||||
@user = UserFinder.new(params[:username]).find_by_username!
|
||||
|
||||
@snippets = SnippetsFinder.new(current_user, author: @user, scope: params[:scope])
|
||||
@snippets = SnippetsFinder.new(current_user, author: @user, scope: params[:scope], sort: sort_param)
|
||||
.execute
|
||||
.page(params[:page])
|
||||
.inc_author
|
||||
|
|
|
@ -43,7 +43,7 @@ class SnippetsFinder < UnionFinder
|
|||
include Gitlab::Utils::StrongMemoize
|
||||
|
||||
attr_accessor :current_user, :params
|
||||
delegate :explore, :only_personal, :only_project, :scope, to: :params
|
||||
delegate :explore, :only_personal, :only_project, :scope, :sort, to: :params
|
||||
|
||||
def initialize(current_user = nil, params = {})
|
||||
@current_user = current_user
|
||||
|
@ -69,7 +69,9 @@ class SnippetsFinder < UnionFinder
|
|||
|
||||
items = init_collection
|
||||
items = by_ids(items)
|
||||
items.with_optional_visibility(visibility_from_scope).fresh
|
||||
items = items.with_optional_visibility(visibility_from_scope)
|
||||
|
||||
items.order_by(sort_param)
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -202,6 +204,10 @@ class SnippetsFinder < UnionFinder
|
|||
params[:project].is_a?(Project) ? params[:project] : Project.find_by_id(params[:project])
|
||||
end
|
||||
end
|
||||
|
||||
def sort_param
|
||||
sort.presence || 'id_desc'
|
||||
end
|
||||
end
|
||||
|
||||
SnippetsFinder.prepend_if_ee('EE::SnippetsFinder')
|
||||
|
|
|
@ -10,6 +10,10 @@ class MergeRequestPolicy < IssuablePolicy
|
|||
# it would not be safe to prevent :create_note there, since
|
||||
# note permissions are shared, and this would apply too broadly.
|
||||
rule { ~can?(:read_merge_request) }.prevent :create_note
|
||||
|
||||
rule { can?(:update_merge_request) }.policy do
|
||||
enable :approve_merge_request
|
||||
end
|
||||
end
|
||||
|
||||
MergeRequestPolicy.prepend_if_ee('EE::MergeRequestPolicy')
|
||||
|
|
|
@ -2,8 +2,6 @@
|
|||
|
||||
module Members
|
||||
class DestroyService < Members::BaseService
|
||||
WAIT_FOR_DELETE = 1.hour
|
||||
|
||||
def execute(member, skip_authorization: false, skip_subresources: false, unassign_issuables: false)
|
||||
raise Gitlab::Access::AccessDeniedError unless skip_authorization || can_destroy_member?(member)
|
||||
|
||||
|
@ -72,7 +70,7 @@ module Members
|
|||
source_type = member.is_a?(GroupMember) ? 'Group' : 'Project'
|
||||
|
||||
member.run_after_commit do
|
||||
MembersDestroyer::UnassignIssuablesWorker.perform_in(WAIT_FOR_DELETE, member.user_id, member.source_id, source_type)
|
||||
MembersDestroyer::UnassignIssuablesWorker.perform_async(member.user_id, member.source_id, source_type)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,19 +3,27 @@
|
|||
module MergeRequests
|
||||
class ApprovalService < MergeRequests::BaseService
|
||||
def execute(merge_request)
|
||||
return unless can_be_approved?(merge_request)
|
||||
|
||||
approval = merge_request.approvals.new(user: current_user)
|
||||
|
||||
return unless save_approval(approval)
|
||||
return success unless save_approval(approval)
|
||||
|
||||
reset_approvals_cache(merge_request)
|
||||
create_event(merge_request)
|
||||
create_approval_note(merge_request)
|
||||
mark_pending_todos_as_done(merge_request)
|
||||
execute_approval_hooks(merge_request, current_user)
|
||||
|
||||
success
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def can_be_approved?(merge_request)
|
||||
current_user.can?(:approve_merge_request, merge_request)
|
||||
end
|
||||
|
||||
def reset_approvals_cache(merge_request)
|
||||
merge_request.approvals.reset
|
||||
end
|
||||
|
|
|
@ -4,6 +4,8 @@ module MergeRequests
|
|||
class RemoveApprovalService < MergeRequests::BaseService
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def execute(merge_request)
|
||||
return unless approved_by_user?(merge_request)
|
||||
|
||||
# paranoid protection against running wrong deletes
|
||||
return unless merge_request.id && current_user.id
|
||||
|
||||
|
@ -15,11 +17,17 @@ module MergeRequests
|
|||
reset_approvals_cache(merge_request)
|
||||
create_note(merge_request)
|
||||
end
|
||||
|
||||
success
|
||||
end
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
|
||||
private
|
||||
|
||||
def approved_by_user?(merge_request)
|
||||
merge_request.approved_by_users.include?(current_user)
|
||||
end
|
||||
|
||||
def reset_approvals_cache(merge_request)
|
||||
merge_request.approvals.reset
|
||||
end
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
#{sanitize_name(@updated_by_user.name)} pushed new commits to merge request #{merge_request_reference_link(@merge_request)}
|
||||
#{sanitize_name(@updated_by_user.name)} pushed new commits to merge request #{@merge_request.to_reference}
|
||||
|
||||
Merge Request URL: #{project_merge_request_url(@merge_request.target_project, @merge_request)}
|
||||
\
|
||||
- if @existing_commits.any?
|
||||
- count = @existing_commits.size
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Move plan stage usage activity to CE
|
||||
merge_request: 36087
|
||||
author:
|
||||
type: changed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix dashboard schema validation issue
|
||||
merge_request: 36577
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Reorder snippets in lists using `updated_at` column
|
||||
merge_request: 34393
|
||||
author: Dibyadarshi Dash @ddash2
|
||||
type: changed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Propagate DS_JAVA_VERSION for dependency scanning
|
||||
merge_request: 36448
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Remove HTML link from plain text mail
|
||||
merge_request: 36301
|
||||
author:
|
||||
type: fixed
|
|
@ -138,7 +138,7 @@ The following documentation relates to the DevOps **Plan** stage:
|
|||
Consolidate source code into a single [distributed version control system](https://en.wikipedia.org/wiki/Distributed_version_control)
|
||||
that’s easily managed and controlled without disrupting your workflow.
|
||||
|
||||
GitLab’s Git repositories come complete with branching tools and access
|
||||
GitLab repositories come complete with branching tools and access
|
||||
controls, providing a scalable, single source of truth for collaborating
|
||||
on projects and code.
|
||||
|
||||
|
|
|
@ -109,6 +109,24 @@ For example, to enable the [`:junit_pipeline_view`](../ci/junit_test_reports.md#
|
|||
Feature.enable(:junit_pipeline_view, Project.find(1234))
|
||||
```
|
||||
|
||||
`Feature.enable` and `Feature.disable` always return `nil`, this is not an indication that the command failed:
|
||||
|
||||
```ruby
|
||||
irb(main):001:0> Feature.enable(:release_evidence_collection)
|
||||
=> nil
|
||||
```
|
||||
|
||||
To check if a flag is enabled or disabled you can use `Feature.enabled?` or `Feature.disabled?`:
|
||||
|
||||
```ruby
|
||||
Feature.enable(:release_evidence_collection)
|
||||
=> nil
|
||||
Feature.enabled?(:release_evidence_collection)
|
||||
=> true
|
||||
Feature.disabled?(:release_evidence_collection)
|
||||
=> false
|
||||
```
|
||||
|
||||
When the feature is ready, GitLab will remove the feature flag, the option for
|
||||
enabling and disabling it will no longer exist, and the feature will become
|
||||
available in all instances.
|
||||
|
|
|
@ -927,7 +927,7 @@ larger images, or images that take longer than 5 minutes to push, users may
|
|||
encounter this error.
|
||||
|
||||
Administrators can increase the token duration in **Admin area > Settings >
|
||||
Container Registry > Authorization token duration (minutes)**.
|
||||
CI/CD > Container Registry > Authorization token duration (minutes)**.
|
||||
|
||||
### AWS S3 with the GitLab registry error when pushing large images
|
||||
|
||||
|
|
|
@ -4,6 +4,44 @@
|
|||
|
||||
We use ESLint to encapsulate and enforce frontend code standards. Our configuration may be found in the [`gitlab-eslint-config`](https://gitlab.com/gitlab-org/gitlab-eslint-config) project.
|
||||
|
||||
### Yarn Script
|
||||
|
||||
This section describes yarn scripts that are available to validate and apply automatic fixes to files using ESLint.
|
||||
|
||||
To check all currently staged files (based on `git diff`) with ESLint, run the following script:
|
||||
|
||||
```shell
|
||||
yarn eslint-staged
|
||||
```
|
||||
|
||||
_A list of problems found will be logged to the console._
|
||||
|
||||
To apply automatic ESLint fixes to all currently staged files (based on `git diff`), run the following script:
|
||||
|
||||
```shell
|
||||
yarn eslint-staged-fix
|
||||
```
|
||||
|
||||
_If manual changes are required, a list of changes will be sent to the console._
|
||||
|
||||
To check **all** files in the repository with ESLint, run the following script:
|
||||
|
||||
```shell
|
||||
yarn eslint
|
||||
```
|
||||
|
||||
_A list of problems found will be logged to the console._
|
||||
|
||||
To apply automatic ESLint fixes to **all** files in the repository, run the following script:
|
||||
|
||||
```shell
|
||||
yarn eslint-fix
|
||||
```
|
||||
|
||||
_If manual changes are required, a list of changes will be sent to the console._
|
||||
|
||||
**Caution:** Limit use to global rule updates. Otherwise, the changes can lead to huge Merge Requests.
|
||||
|
||||
### Disabling ESLint in new files
|
||||
|
||||
Do not disable ESLint when creating new files. Existing files may have existing rules
|
||||
|
|
|
@ -681,10 +681,19 @@ appear to be associated to any of the services running, since they all appear to
|
|||
| `ldap_group_sync_enabled` | `usage_activity_by_stage` | `manage` | | EE | |
|
||||
| `ldap_admin_sync_enabled` | `usage_activity_by_stage` | `manage` | | EE | |
|
||||
| `group_saml_enabled` | `usage_activity_by_stage` | `manage` | | EE | |
|
||||
| `projects_jira_dvcs_server_active` | `usage_activity_by_stage` | `plan` | | | |
|
||||
| `service_desk_enabled_projects` | `usage_activity_by_stage` | `plan` | | | |
|
||||
| `service_desk_issues` | `usage_activity_by_stage` | `plan` | | | |
|
||||
| `todos: 0` | `usage_activity_by_stage` | `plan` | | | |
|
||||
| `issues` | `usage_activity_by_stage` | `plan` | | CE+EE | |
|
||||
| `notes` | `usage_activity_by_stage` | `plan` | | CE+EE | |
|
||||
| `projects` | `usage_activity_by_stage` | `plan` | | CE+EE | |
|
||||
| `todos` | `usage_activity_by_stage` | `plan` | | CE+EE | |
|
||||
| `assignee_lists` | `usage_activity_by_stage` | `plan` | | EE | |
|
||||
| `epics` | `usage_activity_by_stage` | `plan` | | EE | |
|
||||
| `label_lists` | `usage_activity_by_stage` | `plan` | | EE | |
|
||||
| `milestone_lists` | `usage_activity_by_stage` | `plan` | | EE | |
|
||||
| `projects_jira_active` | `usage_activity_by_stage` | `plan` | | EE | |
|
||||
| `projects_jira_dvcs_server_active` | `usage_activity_by_stage` | `plan` | | EE | |
|
||||
| `projects_jira_dvcs_server_active` | `usage_activity_by_stage` | `plan` | | EE | |
|
||||
| `service_desk_enabled_projects` | `usage_activity_by_stage` | `plan` | | EE | |
|
||||
| `service_desk_issues` | `usage_activity_by_stage` | `plan` | | EE | |
|
||||
| `deployments` | `usage_activity_by_stage` | `release` | | CE+EE | Total deployments |
|
||||
| `failed_deployments` | `usage_activity_by_stage` | `release` | | CE+EE | Total failed deployments |
|
||||
| `projects_mirrored_with_pipelines_enabled` | `usage_activity_by_stage` | `release` | | EE | Projects with repository mirroring enabled |
|
||||
|
|
|
@ -11,6 +11,7 @@ your applications:
|
|||
|
||||
- Collect [Prometheus metrics](../user/project/integrations/prometheus_library/index.md).
|
||||
- Deploy to different [environments](../ci/environments/index.md).
|
||||
- Manage your [Alerts](../user/project/operations/alert_management.md) and [Incidents](../user/incident_management/index.md).
|
||||
- Connect your project to a [Kubernetes cluster](../user/project/clusters/index.md).
|
||||
- Manage your infrastructure with [Infrastructure as Code](../user/infrastructure/index.md) approaches.
|
||||
- Discover and view errors generated by your applications with [Error Tracking](../user/project/operations/error_tracking.md).
|
||||
|
|
|
@ -0,0 +1,261 @@
|
|||
---
|
||||
stage: Monitor
|
||||
group: APM
|
||||
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
|
||||
---
|
||||
|
||||
# Panel types for dashboards
|
||||
|
||||
The below panel types are supported in monitoring dashboards.
|
||||
|
||||
## Area or Line Chart
|
||||
|
||||
To add an area chart panel type to a dashboard, look at the following sample dashboard file:
|
||||
|
||||
```yaml
|
||||
dashboard: 'Dashboard Title'
|
||||
panel_groups:
|
||||
- group: 'Group Title'
|
||||
panels:
|
||||
- type: area-chart # or line-chart
|
||||
title: 'Area Chart Title'
|
||||
y_label: "Y-Axis"
|
||||
y_axis:
|
||||
format: number
|
||||
precision: 0
|
||||
metrics:
|
||||
- id: area_http_requests_total
|
||||
query_range: 'http_requests_total'
|
||||
label: "Instance: {{instance}}, Method: {{method}}"
|
||||
unit: "count"
|
||||
```
|
||||
|
||||
Note the following properties:
|
||||
|
||||
| Property | Type | Required | Description |
|
||||
| ------ | ------ | ------ | ------ |
|
||||
| type | string | no | Type of panel to be rendered. Optional for area panel types |
|
||||
| query_range | string | required | For area panel types, you must use a [range query](https://prometheus.io/docs/prometheus/latest/querying/api/#range-queries) |
|
||||
|
||||
![area panel chart](../../../user/project/integrations/img/prometheus_dashboard_area_panel_type_v12_8.png)
|
||||
|
||||
Starting in [version 12.8](https://gitlab.com/gitlab-org/gitlab/-/issues/202696), the y-axis values will automatically scale according to the data. Previously, it always started from 0.
|
||||
|
||||
## Anomaly chart
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/16530) in GitLab 12.5.
|
||||
|
||||
To add an anomaly chart panel type to a dashboard, add a panel with *exactly* 3 metrics.
|
||||
|
||||
The first metric represents the current state, and the second and third metrics represent the upper and lower limit respectively:
|
||||
|
||||
```yaml
|
||||
dashboard: 'Dashboard Title'
|
||||
panel_groups:
|
||||
- group: 'Group Title'
|
||||
panels:
|
||||
- type: anomaly-chart
|
||||
title: "Chart Title"
|
||||
y_label: "Y-Axis"
|
||||
metrics:
|
||||
- id: anomaly_requests_normal
|
||||
query_range: 'http_requests_total'
|
||||
label: "# of Requests"
|
||||
unit: "count"
|
||||
metrics:
|
||||
- id: anomaly_requests_upper_limit
|
||||
query_range: 10000
|
||||
label: "Max # of requests"
|
||||
unit: "count"
|
||||
metrics:
|
||||
- id: anomaly_requests_lower_limit
|
||||
query_range: 2000
|
||||
label: "Min # of requests"
|
||||
unit: "count"
|
||||
```
|
||||
|
||||
Note the following properties:
|
||||
|
||||
| Property | Type | Required | Description |
|
||||
| ------ | ------ | ------ | ------ |
|
||||
| type | string | required | Must be `anomaly-chart` for anomaly panel types |
|
||||
| query_range | yes | required | For anomaly panel types, you must use a [range query](https://prometheus.io/docs/prometheus/latest/querying/api/#range-queries) in every metric. |
|
||||
|
||||
![anomaly panel type](../../../user/project/integrations/img/prometheus_dashboard_anomaly_panel_type.png)
|
||||
|
||||
## Bar chart
|
||||
|
||||
To add a bar chart to a dashboard, look at the following sample dashboard file:
|
||||
|
||||
```yaml
|
||||
dashboard: 'Dashboard Title'
|
||||
panel_groups:
|
||||
- group: 'Group title'
|
||||
panels:
|
||||
- type: bar
|
||||
title: "Http Handlers"
|
||||
x_label: 'Response Size'
|
||||
y_axis:
|
||||
name: "Handlers"
|
||||
metrics:
|
||||
- id: prometheus_http_response_size_bytes_bucket
|
||||
query_range: "sum(increase(prometheus_http_response_size_bytes_bucket[1d])) by (handler)"
|
||||
unit: 'Bytes'
|
||||
```
|
||||
|
||||
Note the following properties:
|
||||
|
||||
| Property | Type | Required | Description |
|
||||
| ------ | ------ | ------ | ------ |
|
||||
| `type` | string | yes | Type of panel to be rendered. For bar chart types, set to `bar` |
|
||||
| `query_range` | yes | yes | For bar chart, you must use a [range query](https://prometheus.io/docs/prometheus/latest/querying/api/#range-queries)
|
||||
|
||||
![bar chart panel type](../../../user/project/integrations/img/prometheus_dashboard_bar_chart_panel_type_v12.10.png)
|
||||
|
||||
## Column chart
|
||||
|
||||
To add a column panel type to a dashboard, look at the following sample dashboard file:
|
||||
|
||||
```yaml
|
||||
dashboard: 'Dashboard Title'
|
||||
panel_groups:
|
||||
- group: 'Group title'
|
||||
panels:
|
||||
- title: "Column"
|
||||
type: "column"
|
||||
metrics:
|
||||
- id: 1024_memory
|
||||
query: 'avg(sum(container_memory_usage_bytes{container_name!="POD",pod_name=~"^%{ci_environment_slug}-([^c].*|c([^a]|a([^n]|n([^a]|a([^r]|r[^y])))).*|)-(.*)",namespace="%{kube_namespace}"}) by (job)) without (job) / count(avg(container_memory_usage_bytes{container_name!="POD",pod_name=~"^%{ci_environment_slug}-([^c].*|c([^a]|a([^n]|n([^a]|a([^r]|r[^y])))).*|)-(.*)",namespace="%{kube_namespace}"}) without (job)) /1024/1024'
|
||||
unit: MB
|
||||
label: "Memory Usage"
|
||||
```
|
||||
|
||||
Note the following properties:
|
||||
|
||||
| Property | Type | Required | Description |
|
||||
| ------ | ------ | ------ | ------ |
|
||||
| type | string | yes | Type of panel to be rendered. For column panel types, set to `column` |
|
||||
| query_range | yes | yes | For column panel types, you must use a [range query](https://prometheus.io/docs/prometheus/latest/querying/api/#range-queries) |
|
||||
|
||||
![anomaly panel type](../../../user/project/integrations/img/prometheus_dashboard_column_panel_type.png)
|
||||
|
||||
## Stacked column
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/30583) in GitLab 12.8.
|
||||
|
||||
To add a stacked column panel type to a dashboard, look at the following sample dashboard file:
|
||||
|
||||
```yaml
|
||||
dashboard: 'Dashboard title'
|
||||
priority: 1
|
||||
panel_groups:
|
||||
- group: 'Group Title'
|
||||
priority: 5
|
||||
panels:
|
||||
- type: 'stacked-column'
|
||||
title: "Stacked column"
|
||||
y_label: "y label"
|
||||
x_label: 'x label'
|
||||
metrics:
|
||||
- id: memory_1
|
||||
query_range: 'memory_query'
|
||||
label: "memory query 1"
|
||||
unit: "count"
|
||||
series_name: 'group 1'
|
||||
- id: memory_2
|
||||
query_range: 'memory_query_2'
|
||||
label: "memory query 2"
|
||||
unit: "count"
|
||||
series_name: 'group 2'
|
||||
```
|
||||
|
||||
![stacked column panel type](../../../user/project/integrations/img/prometheus_dashboard_stacked_column_panel_type_v12_8.png)
|
||||
|
||||
| Property | Type | Required | Description |
|
||||
| ------ | ------ | ------ | ------ |
|
||||
| `type` | string | yes | Type of panel to be rendered. For stacked column panel types, set to `stacked-column` |
|
||||
| `query_range` | yes | yes | For stacked column panel types, you must use a [range query](https://prometheus.io/docs/prometheus/latest/querying/api/#range-queries) |
|
||||
|
||||
## Single Stat
|
||||
|
||||
To add a single stat panel type to a dashboard, look at the following sample dashboard file:
|
||||
|
||||
```yaml
|
||||
dashboard: 'Dashboard Title'
|
||||
panel_groups:
|
||||
- group: 'Group Title'
|
||||
panels:
|
||||
- title: "Single Stat"
|
||||
type: "single-stat"
|
||||
metrics:
|
||||
- id: 10
|
||||
query: 'max(go_memstats_alloc_bytes{job="prometheus"})'
|
||||
unit: MB
|
||||
label: "Total"
|
||||
```
|
||||
|
||||
Note the following properties:
|
||||
|
||||
| Property | Type | Required | Description |
|
||||
| ------ | ------ | ------ | ------ |
|
||||
| type | string | yes | Type of panel to be rendered. For single stat panel types, set to `single-stat` |
|
||||
| query | string | yes | For single stat panel types, you must use an [instant query](https://prometheus.io/docs/prometheus/latest/querying/api/#instant-queries) |
|
||||
|
||||
![single stat panel type](../../../user/project/integrations/img/prometheus_dashboard_single_stat_panel_type.png)
|
||||
|
||||
## Percentile based results
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/201946) in GitLab 12.8.
|
||||
|
||||
Query results sometimes need to be represented as a percentage value out of 100. You can use the `max_value` property at the root of the panel definition:
|
||||
|
||||
```yaml
|
||||
dashboard: 'Dashboard Title'
|
||||
panel_groups:
|
||||
- group: 'Group Title'
|
||||
panels:
|
||||
- title: "Single Stat"
|
||||
type: "single-stat"
|
||||
max_value: 100
|
||||
metrics:
|
||||
- id: 10
|
||||
query: 'max(go_memstats_alloc_bytes{job="prometheus"})'
|
||||
unit: '%'
|
||||
label: "Total"
|
||||
```
|
||||
|
||||
For example, if you have a query value of `53.6`, adding `%` as the unit results in a single stat value of `53.6%`, but if the maximum expected value of the query is `120`, the value would be `44.6%`. Adding the `max_value` causes the correct percentage value to display.
|
||||
|
||||
## Heatmaps
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/30581) in GitLab 12.5.
|
||||
|
||||
To add a heatmap panel type to a dashboard, look at the following sample dashboard file:
|
||||
|
||||
```yaml
|
||||
dashboard: 'Dashboard Title'
|
||||
panel_groups:
|
||||
- group: 'Group Title'
|
||||
panels:
|
||||
- title: "Heatmap"
|
||||
type: "heatmap"
|
||||
metrics:
|
||||
- id: 10
|
||||
query: 'sum(rate(nginx_upstream_responses_total{upstream=~"%{kube_namespace}-%{ci_environment_slug}-.*"}[60m])) by (status_code)'
|
||||
unit: req/sec
|
||||
label: "Status code"
|
||||
```
|
||||
|
||||
Note the following properties:
|
||||
|
||||
| Property | Type | Required | Description |
|
||||
| ------ | ------ | ------ | ------ |
|
||||
| type | string | yes | Type of panel to be rendered. For heatmap panel types, set to `heatmap` |
|
||||
| query_range | yes | yes | For area panel types, you must use a [range query](https://prometheus.io/docs/prometheus/latest/querying/api/#range-queries) |
|
||||
|
||||
![heatmap panel type](../../../user/project/integrations/img/heatmap_panel_type.png)
|
||||
|
||||
CAUTION: **Warning:**
|
||||
When a query returns too many data points, the heatmap data bucket dimensions tend downwards to 0, making the chart's data invisible, as shown in the image below. To fix this problem, limit the amount of data returned by changing the time range filter on the metrics dashboard UI, or adding the **step** property to your dashboard's YAML file.
|
||||
|
||||
![heatmap chart_too_much_data](../../../user/project/integrations/img/heatmap_chart_too_much_data_v_13_2.png)
|
|
@ -59,7 +59,7 @@ Panels in a panel group are laid out in rows consisting of two panels per row. A
|
|||
| `title` | string | yes | Heading for the panel. |
|
||||
| `y_label` | string | no, but highly encouraged | Y-Axis label for the panel. |
|
||||
| `y_axis` | string | no | Y-Axis configuration for the panel. |
|
||||
| `max_value` | number | no | Denominator value used for calculating [percentile based results](../../../user/project/integrations/prometheus.md#percentile-based-results) |
|
||||
| `max_value` | number | no | Denominator value used for calculating [percentile based results](panel_types.md#percentile-based-results) |
|
||||
| `weight` | number | no, defaults to order in file | Order to appear within the grouping. Lower number means higher priority, which will be higher on the page. Numbers do not need to be consecutive. |
|
||||
| `metrics` | array | yes | The metrics which should be displayed in the panel. Any number of metrics can be displayed when `type` is `area-chart` or `line-chart`, whereas only 3 can be displayed when `type` is `anomaly-chart`. |
|
||||
| `links` | array | no | Add links to display on the chart's [context menu](index.md#chart-context-menu). |
|
||||
|
|
|
@ -120,6 +120,7 @@ The following table depicts the various user permission levels in a project.
|
|||
| Rewrite/remove Git tags | | | ✓ | ✓ | ✓ |
|
||||
| Manage Feature Flags **(PREMIUM)** | | | ✓ | ✓ | ✓ |
|
||||
| Create/edit/delete metrics dashboard annotations | | | ✓ | ✓ | ✓ |
|
||||
| Run CI/CD pipeline against a protected branch | | | ✓ (*5*) | ✓ | ✓ |
|
||||
| Use environment terminals | | | | ✓ | ✓ |
|
||||
| Run Web IDE's Interactive Web Terminals **(ULTIMATE ONLY)** | | | | ✓ | ✓ |
|
||||
| Add new team members | | | | ✓ | ✓ |
|
||||
|
|
|
@ -353,262 +353,6 @@ When **Metrics Dashboard YAML definition is invalid** at least one of the follow
|
|||
|
||||
Metrics Dashboard YAML definition validation information is also available as a [GraphQL API field](../../../api/graphql/reference/index.md#metricsdashboard)
|
||||
|
||||
#### Panel types for dashboards
|
||||
|
||||
The below panel types are supported in monitoring dashboards.
|
||||
|
||||
##### Area or Line Chart
|
||||
|
||||
To add an area chart panel type to a dashboard, look at the following sample dashboard file:
|
||||
|
||||
```yaml
|
||||
dashboard: 'Dashboard Title'
|
||||
panel_groups:
|
||||
- group: 'Group Title'
|
||||
panels:
|
||||
- type: area-chart # or line-chart
|
||||
title: 'Area Chart Title'
|
||||
y_label: "Y-Axis"
|
||||
y_axis:
|
||||
format: number
|
||||
precision: 0
|
||||
metrics:
|
||||
- id: area_http_requests_total
|
||||
query_range: 'http_requests_total'
|
||||
label: "Instance: {{instance}}, Method: {{method}}"
|
||||
unit: "count"
|
||||
```
|
||||
|
||||
Note the following properties:
|
||||
|
||||
| Property | Type | Required | Description |
|
||||
| ------ | ------ | ------ | ------ |
|
||||
| type | string | no | Type of panel to be rendered. Optional for area panel types |
|
||||
| query_range | string | required | For area panel types, you must use a [range query](https://prometheus.io/docs/prometheus/latest/querying/api/#range-queries) |
|
||||
|
||||
![area panel chart](img/prometheus_dashboard_area_panel_type_v12_8.png)
|
||||
|
||||
Starting in [version 12.8](https://gitlab.com/gitlab-org/gitlab/-/issues/202696), the y-axis values will automatically scale according to the data. Previously, it always started from 0.
|
||||
|
||||
##### Anomaly chart
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/16530) in GitLab 12.5.
|
||||
|
||||
To add an anomaly chart panel type to a dashboard, add a panel with *exactly* 3 metrics.
|
||||
|
||||
The first metric represents the current state, and the second and third metrics represent the upper and lower limit respectively:
|
||||
|
||||
```yaml
|
||||
dashboard: 'Dashboard Title'
|
||||
panel_groups:
|
||||
- group: 'Group Title'
|
||||
panels:
|
||||
- type: anomaly-chart
|
||||
title: "Chart Title"
|
||||
y_label: "Y-Axis"
|
||||
metrics:
|
||||
- id: anomaly_requests_normal
|
||||
query_range: 'http_requests_total'
|
||||
label: "# of Requests"
|
||||
unit: "count"
|
||||
metrics:
|
||||
- id: anomaly_requests_upper_limit
|
||||
query_range: 10000
|
||||
label: "Max # of requests"
|
||||
unit: "count"
|
||||
metrics:
|
||||
- id: anomaly_requests_lower_limit
|
||||
query_range: 2000
|
||||
label: "Min # of requests"
|
||||
unit: "count"
|
||||
```
|
||||
|
||||
Note the following properties:
|
||||
|
||||
| Property | Type | Required | Description |
|
||||
| ------ | ------ | ------ | ------ |
|
||||
| type | string | required | Must be `anomaly-chart` for anomaly panel types |
|
||||
| query_range | yes | required | For anomaly panel types, you must use a [range query](https://prometheus.io/docs/prometheus/latest/querying/api/#range-queries) in every metric. |
|
||||
|
||||
![anomaly panel type](img/prometheus_dashboard_anomaly_panel_type.png)
|
||||
|
||||
##### Bar chart
|
||||
|
||||
To add a bar chart to a dashboard, look at the following sample dashboard file:
|
||||
|
||||
```yaml
|
||||
dashboard: 'Dashboard Title'
|
||||
panel_groups:
|
||||
- group: 'Group title'
|
||||
panels:
|
||||
- type: bar
|
||||
title: "Http Handlers"
|
||||
x_label: 'Response Size'
|
||||
y_axis:
|
||||
name: "Handlers"
|
||||
metrics:
|
||||
- id: prometheus_http_response_size_bytes_bucket
|
||||
query_range: "sum(increase(prometheus_http_response_size_bytes_bucket[1d])) by (handler)"
|
||||
unit: 'Bytes'
|
||||
```
|
||||
|
||||
Note the following properties:
|
||||
|
||||
| Property | Type | Required | Description |
|
||||
| ------ | ------ | ------ | ------ |
|
||||
| `type` | string | yes | Type of panel to be rendered. For bar chart types, set to `bar` |
|
||||
| `query_range` | yes | yes | For bar chart, you must use a [range query](https://prometheus.io/docs/prometheus/latest/querying/api/#range-queries)
|
||||
|
||||
![bar chart panel type](img/prometheus_dashboard_bar_chart_panel_type_v12.10.png)
|
||||
|
||||
##### Column chart
|
||||
|
||||
To add a column panel type to a dashboard, look at the following sample dashboard file:
|
||||
|
||||
```yaml
|
||||
dashboard: 'Dashboard Title'
|
||||
panel_groups:
|
||||
- group: 'Group title'
|
||||
panels:
|
||||
- title: "Column"
|
||||
type: "column"
|
||||
metrics:
|
||||
- id: 1024_memory
|
||||
query: 'avg(sum(container_memory_usage_bytes{container_name!="POD",pod_name=~"^%{ci_environment_slug}-([^c].*|c([^a]|a([^n]|n([^a]|a([^r]|r[^y])))).*|)-(.*)",namespace="%{kube_namespace}"}) by (job)) without (job) / count(avg(container_memory_usage_bytes{container_name!="POD",pod_name=~"^%{ci_environment_slug}-([^c].*|c([^a]|a([^n]|n([^a]|a([^r]|r[^y])))).*|)-(.*)",namespace="%{kube_namespace}"}) without (job)) /1024/1024'
|
||||
unit: MB
|
||||
label: "Memory Usage"
|
||||
```
|
||||
|
||||
Note the following properties:
|
||||
|
||||
| Property | Type | Required | Description |
|
||||
| ------ | ------ | ------ | ------ |
|
||||
| type | string | yes | Type of panel to be rendered. For column panel types, set to `column` |
|
||||
| query_range | yes | yes | For column panel types, you must use a [range query](https://prometheus.io/docs/prometheus/latest/querying/api/#range-queries) |
|
||||
|
||||
![anomaly panel type](img/prometheus_dashboard_column_panel_type.png)
|
||||
|
||||
##### Stacked column
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/30583) in GitLab 12.8.
|
||||
|
||||
To add a stacked column panel type to a dashboard, look at the following sample dashboard file:
|
||||
|
||||
```yaml
|
||||
dashboard: 'Dashboard title'
|
||||
priority: 1
|
||||
panel_groups:
|
||||
- group: 'Group Title'
|
||||
priority: 5
|
||||
panels:
|
||||
- type: 'stacked-column'
|
||||
title: "Stacked column"
|
||||
y_label: "y label"
|
||||
x_label: 'x label'
|
||||
metrics:
|
||||
- id: memory_1
|
||||
query_range: 'memory_query'
|
||||
label: "memory query 1"
|
||||
unit: "count"
|
||||
series_name: 'group 1'
|
||||
- id: memory_2
|
||||
query_range: 'memory_query_2'
|
||||
label: "memory query 2"
|
||||
unit: "count"
|
||||
series_name: 'group 2'
|
||||
```
|
||||
|
||||
![stacked column panel type](img/prometheus_dashboard_stacked_column_panel_type_v12_8.png)
|
||||
|
||||
| Property | Type | Required | Description |
|
||||
| ------ | ------ | ------ | ------ |
|
||||
| `type` | string | yes | Type of panel to be rendered. For stacked column panel types, set to `stacked-column` |
|
||||
| `query_range` | yes | yes | For stacked column panel types, you must use a [range query](https://prometheus.io/docs/prometheus/latest/querying/api/#range-queries) |
|
||||
|
||||
##### Single Stat
|
||||
|
||||
To add a single stat panel type to a dashboard, look at the following sample dashboard file:
|
||||
|
||||
```yaml
|
||||
dashboard: 'Dashboard Title'
|
||||
panel_groups:
|
||||
- group: 'Group Title'
|
||||
panels:
|
||||
- title: "Single Stat"
|
||||
type: "single-stat"
|
||||
metrics:
|
||||
- id: 10
|
||||
query: 'max(go_memstats_alloc_bytes{job="prometheus"})'
|
||||
unit: MB
|
||||
label: "Total"
|
||||
```
|
||||
|
||||
Note the following properties:
|
||||
|
||||
| Property | Type | Required | Description |
|
||||
| ------ | ------ | ------ | ------ |
|
||||
| type | string | yes | Type of panel to be rendered. For single stat panel types, set to `single-stat` |
|
||||
| query | string | yes | For single stat panel types, you must use an [instant query](https://prometheus.io/docs/prometheus/latest/querying/api/#instant-queries) |
|
||||
|
||||
![single stat panel type](img/prometheus_dashboard_single_stat_panel_type.png)
|
||||
|
||||
###### Percentile based results
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/201946) in GitLab 12.8.
|
||||
|
||||
Query results sometimes need to be represented as a percentage value out of 100. You can use the `max_value` property at the root of the panel definition:
|
||||
|
||||
```yaml
|
||||
dashboard: 'Dashboard Title'
|
||||
panel_groups:
|
||||
- group: 'Group Title'
|
||||
panels:
|
||||
- title: "Single Stat"
|
||||
type: "single-stat"
|
||||
max_value: 100
|
||||
metrics:
|
||||
- id: 10
|
||||
query: 'max(go_memstats_alloc_bytes{job="prometheus"})'
|
||||
unit: '%'
|
||||
label: "Total"
|
||||
```
|
||||
|
||||
For example, if you have a query value of `53.6`, adding `%` as the unit results in a single stat value of `53.6%`, but if the maximum expected value of the query is `120`, the value would be `44.6%`. Adding the `max_value` causes the correct percentage value to display.
|
||||
|
||||
##### Heatmaps
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/30581) in GitLab 12.5.
|
||||
|
||||
To add a heatmap panel type to a dashboard, look at the following sample dashboard file:
|
||||
|
||||
```yaml
|
||||
dashboard: 'Dashboard Title'
|
||||
panel_groups:
|
||||
- group: 'Group Title'
|
||||
panels:
|
||||
- title: "Heatmap"
|
||||
type: "heatmap"
|
||||
metrics:
|
||||
- id: 10
|
||||
query: 'sum(rate(nginx_upstream_responses_total{upstream=~"%{kube_namespace}-%{ci_environment_slug}-.*"}[60m])) by (status_code)'
|
||||
unit: req/sec
|
||||
label: "Status code"
|
||||
```
|
||||
|
||||
Note the following properties:
|
||||
|
||||
| Property | Type | Required | Description |
|
||||
| ------ | ------ | ------ | ------ |
|
||||
| type | string | yes | Type of panel to be rendered. For heatmap panel types, set to `heatmap` |
|
||||
| query_range | yes | yes | For area panel types, you must use a [range query](https://prometheus.io/docs/prometheus/latest/querying/api/#range-queries) |
|
||||
|
||||
![heatmap panel type](img/heatmap_panel_type.png)
|
||||
|
||||
CAUTION: **Warning:**
|
||||
When a query returns too many data points, the heatmap data bucket dimensions tend downwards to 0, making the chart's data invisible, as shown in the image below. To fix this problem, limit the amount of data returned by changing the time range filter on the metrics dashboard UI, or adding the **step** property to your dashboard's YAML file.
|
||||
|
||||
![heatmap chart_too_much_data](img/heatmap_chart_too_much_data_v_13_2.png)
|
||||
|
||||
### Templating variables for metrics dashboards
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/214539) in GitLab 13.0.
|
||||
|
|
|
@ -52,6 +52,7 @@ dependency_scanning:
|
|||
DS_PYTHON_VERSION \
|
||||
DS_PIP_VERSION \
|
||||
DS_PIP_DEPENDENCY_PATH \
|
||||
DS_JAVA_VERSION \
|
||||
GEMNASIUM_DB_LOCAL_PATH \
|
||||
GEMNASIUM_DB_REMOTE_URL \
|
||||
GEMNASIUM_DB_REF_NAME \
|
||||
|
|
|
@ -525,9 +525,16 @@ module Gitlab
|
|||
# Omitted because no user, creator or author associated: `boards`, `labels`, `milestones`, `uploads`
|
||||
# Omitted because too expensive: `epics_deepest_relationship_level`
|
||||
# Omitted because of encrypted properties: `projects_jira_cloud_active`, `projects_jira_server_active`
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def usage_activity_by_stage_plan(time_period)
|
||||
{}
|
||||
{
|
||||
issues: distinct_count(::Issue.where(time_period), :author_id),
|
||||
notes: distinct_count(::Note.where(time_period), :author_id),
|
||||
projects: distinct_count(::Project.where(time_period), :creator_id),
|
||||
todos: distinct_count(::Todo.where(time_period), :author_id)
|
||||
}
|
||||
end
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
|
||||
# Omitted because no user, creator or author associated: `environments`, `feature_flags`, `in_review_folder`, `pages_domains`
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
"dev-server": "NODE_OPTIONS=\"--max-old-space-size=3584\" node scripts/frontend/webpack_dev_server.js",
|
||||
"eslint": "eslint --cache --max-warnings 0 --report-unused-disable-directives --ext .js,.vue .",
|
||||
"eslint-fix": "eslint --cache --max-warnings 0 --report-unused-disable-directives --ext .js,.vue --fix .",
|
||||
"eslint-staged": "git diff --cached --name-only | grep -E \"(.*)\\.(js|vue)$\" | xargs eslint --cache --max-warnings 0 --report-unused-disable-directives",
|
||||
"eslint-staged-fix": "git diff --cached --name-only | grep -E \"(.*)\\.(js|vue)$\" | xargs eslint --cache --max-warnings 0 --report-unused-disable-directives --fix",
|
||||
"eslint-report": "eslint --max-warnings 0 --ext .js,.vue --format html --output-file ./eslint-report.html --no-inline-config .",
|
||||
"file-coverage": "scripts/frontend/file_test_coverage.js",
|
||||
"prejest": "yarn check-dependencies",
|
||||
|
@ -41,7 +43,7 @@
|
|||
"@babel/preset-env": "^7.10.1",
|
||||
"@gitlab/at.js": "1.5.5",
|
||||
"@gitlab/svgs": "1.151.0",
|
||||
"@gitlab/ui": "17.19.1",
|
||||
"@gitlab/ui": "17.21.0",
|
||||
"@gitlab/visual-review-tools": "1.6.1",
|
||||
"@rails/actioncable": "^6.0.3-1",
|
||||
"@sentry/browser": "^5.10.2",
|
||||
|
|
4
qa/qa.rb
4
qa/qa.rb
|
@ -474,6 +474,10 @@ module QA
|
|||
autoload :Templates, 'qa/page/component/project/templates'
|
||||
end
|
||||
end
|
||||
|
||||
module Modal
|
||||
autoload :DeleteWiki, 'qa/page/modal/delete_wiki'
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module QA
|
||||
module Page
|
||||
module Modal
|
||||
class DeleteWiki < Base
|
||||
view 'app/assets/javascripts/pages/shared/wikis/components/delete_wiki_modal.vue' do
|
||||
element :confirm_deletion_button, required: true
|
||||
end
|
||||
|
||||
def confirm_deletion
|
||||
click_element :confirm_deletion_button
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -15,6 +15,10 @@ module QA
|
|||
element :create_page_button
|
||||
end
|
||||
|
||||
view 'app/assets/javascripts/pages/shared/wikis/components/delete_wiki_modal.vue' do
|
||||
element :delete_button
|
||||
end
|
||||
|
||||
def set_title(title)
|
||||
fill_element :wiki_title_textbox, title
|
||||
end
|
||||
|
@ -34,6 +38,11 @@ module QA
|
|||
def click_create_page
|
||||
click_element :create_page_button
|
||||
end
|
||||
|
||||
def delete_page
|
||||
click_element :delete_button, Page::Modal::DeleteWiki
|
||||
Page::Modal::DeleteWiki.perform(&:confirm_deletion)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -58,6 +58,10 @@ module QA
|
|||
def has_content?(content)
|
||||
has_element?(:wiki_page_content, content)
|
||||
end
|
||||
|
||||
def has_no_page?
|
||||
has_element? :create_first_page_link
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -22,6 +22,8 @@ module QA
|
|||
Page::Project::Menu.perform(&:click_issues)
|
||||
|
||||
Page::Project::Issue::Index.perform do |issues_page|
|
||||
expect(issues_page).to have_content("2 issues successfully imported")
|
||||
|
||||
issues_page.click_issue_link(jira_issue_title)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module QA
|
||||
RSpec.describe 'Create' do
|
||||
context 'Wiki' do
|
||||
let(:initial_wiki) { Resource::Wiki::ProjectPage.fabricate_via_api! }
|
||||
|
||||
before do
|
||||
Flow::Login.sign_in
|
||||
end
|
||||
|
||||
context 'Page deletion' do
|
||||
it 'has removed the deleted page correctly' do
|
||||
initial_wiki.visit!
|
||||
|
||||
Page::Project::Wiki::Show.perform(&:click_edit)
|
||||
Page::Project::Wiki::Edit.perform(&:delete_page)
|
||||
|
||||
Page::Project::Wiki::Show.perform do |wiki|
|
||||
expect(wiki).to have_no_page
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -48,7 +48,8 @@ echo
|
|||
if [ ${FIND_READMES} -ne $NUMBER_READMES ]
|
||||
then
|
||||
echo
|
||||
echo ' ✖ ERROR: New README.md file(s) detected, prefer index.md over README.md.' >&2
|
||||
echo ' ✖ ERROR: The number of README.md file(s) has changed. Use index.md instead of README.md.' >&2
|
||||
echo ' ✖ If removing a README.md file, update NUMBER_READMES in lint-doc.sh.' >&2
|
||||
echo ' https://docs.gitlab.com/ee/development/documentation/styleguide.html#work-with-directories-and-files'
|
||||
echo
|
||||
((ERRORCODE++))
|
||||
|
|
|
@ -46,7 +46,7 @@ RSpec.describe RendersCommits do
|
|||
it 'avoids N + 1' do
|
||||
stub_const("MergeRequestDiff::COMMITS_SAFE_SIZE", 5)
|
||||
|
||||
control_count = ActiveRecord::QueryRecorder.new(skip_cached: false) do
|
||||
control_count = ActiveRecord::QueryRecorder.new do
|
||||
go
|
||||
end.count
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Dashboard::SnippetsController do
|
||||
let(:user) { create(:user) }
|
||||
let_it_be(:user) { create(:user) }
|
||||
|
||||
before do
|
||||
sign_in(user)
|
||||
|
@ -26,5 +26,7 @@ RSpec.describe Dashboard::SnippetsController do
|
|||
|
||||
get :index
|
||||
end
|
||||
|
||||
it_behaves_like 'snippets sort order'
|
||||
end
|
||||
end
|
||||
|
|
|
@ -15,14 +15,18 @@ RSpec.describe Projects::SnippetsController do
|
|||
end
|
||||
|
||||
describe 'GET #index' do
|
||||
let(:base_params) do
|
||||
{
|
||||
namespace_id: project.namespace,
|
||||
project_id: project
|
||||
}
|
||||
end
|
||||
|
||||
subject { get :index, params: base_params }
|
||||
|
||||
it_behaves_like 'paginated collection' do
|
||||
let(:collection) { project.snippets }
|
||||
let(:params) do
|
||||
{
|
||||
namespace_id: project.namespace,
|
||||
project_id: project
|
||||
}
|
||||
end
|
||||
let(:params) { base_params }
|
||||
|
||||
before do
|
||||
create(:project_snippet, :public, project: project, author: user)
|
||||
|
@ -35,7 +39,11 @@ RSpec.describe Projects::SnippetsController do
|
|||
.to receive(:new).with(nil, project: project)
|
||||
.and_return(service)
|
||||
|
||||
get :index, params: { namespace_id: project.namespace, project_id: project }
|
||||
subject
|
||||
end
|
||||
|
||||
it_behaves_like 'snippets sort order' do
|
||||
let(:params) { base_params }
|
||||
end
|
||||
|
||||
context 'when the project snippet is private' do
|
||||
|
@ -43,7 +51,7 @@ RSpec.describe Projects::SnippetsController do
|
|||
|
||||
context 'when anonymous' do
|
||||
it 'does not include the private snippet' do
|
||||
get :index, params: { namespace_id: project.namespace, project_id: project }
|
||||
subject
|
||||
|
||||
expect(assigns(:snippets)).not_to include(project_snippet)
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
|
@ -56,7 +64,7 @@ RSpec.describe Projects::SnippetsController do
|
|||
end
|
||||
|
||||
it 'renders the snippet' do
|
||||
get :index, params: { namespace_id: project.namespace, project_id: project }
|
||||
subject
|
||||
|
||||
expect(assigns(:snippets)).to include(project_snippet)
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
|
@ -69,7 +77,7 @@ RSpec.describe Projects::SnippetsController do
|
|||
end
|
||||
|
||||
it 'renders the snippet' do
|
||||
get :index, params: { namespace_id: project.namespace, project_id: project }
|
||||
subject
|
||||
|
||||
expect(assigns(:snippets)).to include(project_snippet)
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
|
|
|
@ -6,6 +6,8 @@ RSpec.describe SnippetsController do
|
|||
let_it_be(:user) { create(:user) }
|
||||
|
||||
describe 'GET #index' do
|
||||
let(:base_params) { { username: user.username } }
|
||||
|
||||
context 'when username parameter is present' do
|
||||
it_behaves_like 'paginated collection' do
|
||||
let(:collection) { Snippet.all }
|
||||
|
@ -38,6 +40,10 @@ RSpec.describe SnippetsController do
|
|||
expect(response).to redirect_to(dashboard_snippets_path)
|
||||
end
|
||||
end
|
||||
|
||||
it_behaves_like 'snippets sort order' do
|
||||
let(:params) { base_params }
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET #new' do
|
||||
|
|
|
@ -295,6 +295,22 @@ RSpec.describe SnippetsFinder do
|
|||
expect(finder.execute).to be_empty
|
||||
end
|
||||
end
|
||||
|
||||
context 'no sort param is provided' do
|
||||
it 'returns snippets sorted by id' do
|
||||
snippets = described_class.new(admin).execute
|
||||
|
||||
expect(snippets.ids).to eq(Snippet.order_id_desc.ids)
|
||||
end
|
||||
end
|
||||
|
||||
context 'sort param is provided' do
|
||||
it 'returns snippets sorted by sort param' do
|
||||
snippets = described_class.new(admin, sort: 'updated_desc').execute
|
||||
|
||||
expect(snippets.ids).to eq(Snippet.order_updated_desc.ids)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it_behaves_like 'snippet visibility'
|
||||
|
|
|
@ -948,6 +948,25 @@ describe('Monitoring store actions', () => {
|
|||
);
|
||||
});
|
||||
|
||||
it('dispatches receiveDashboardValidationWarningsSuccess with false payload when the response is empty ', () => {
|
||||
mockMutate.mockResolvedValue({
|
||||
data: {
|
||||
project: null,
|
||||
},
|
||||
});
|
||||
|
||||
return testAction(
|
||||
fetchDashboardValidationWarnings,
|
||||
null,
|
||||
state,
|
||||
[],
|
||||
[{ type: 'receiveDashboardValidationWarningsSuccess', payload: false }],
|
||||
() => {
|
||||
expect(mockMutate).toHaveBeenCalledWith(mutationVariables);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it('dispatches receiveDashboardValidationWarningsFailure if the warnings API call fails', () => {
|
||||
mockMutate.mockRejectedValue({});
|
||||
|
||||
|
|
|
@ -235,6 +235,31 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do
|
|||
end
|
||||
end
|
||||
|
||||
context 'for plan' do
|
||||
it 'includes accurate usage_activity_by_stage data' do
|
||||
for_defined_days_back do
|
||||
user = create(:user)
|
||||
project = create(:project, creator: user)
|
||||
issue = create(:issue, project: project, author: user)
|
||||
create(:note, project: project, noteable: issue, author: user)
|
||||
create(:todo, project: project, target: issue, author: user)
|
||||
end
|
||||
|
||||
expect(described_class.uncached_data[:usage_activity_by_stage][:plan]).to include(
|
||||
issues: 2,
|
||||
notes: 2,
|
||||
projects: 2,
|
||||
todos: 2
|
||||
)
|
||||
expect(described_class.uncached_data[:usage_activity_by_stage_monthly][:plan]).to include(
|
||||
issues: 1,
|
||||
notes: 1,
|
||||
projects: 1,
|
||||
todos: 1
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context 'for release' do
|
||||
it 'includes accurate usage_activity_by_stage data' do
|
||||
for_defined_days_back do
|
||||
|
|
|
@ -24,6 +24,7 @@ RSpec.describe MergeRequestPolicy do
|
|||
mr_perms = %i[create_merge_request_in
|
||||
create_merge_request_from
|
||||
read_merge_request
|
||||
approve_merge_request
|
||||
create_note].freeze
|
||||
|
||||
shared_examples_for 'a denied user' do
|
||||
|
|
|
@ -11,6 +11,10 @@ RSpec.describe MergeRequests::ApprovalService do
|
|||
|
||||
subject(:service) { described_class.new(project, user) }
|
||||
|
||||
before do
|
||||
project.add_developer(user)
|
||||
end
|
||||
|
||||
context 'with invalid approval' do
|
||||
before do
|
||||
allow(merge_request.approvals).to receive(:new).and_return(double(save: false))
|
||||
|
@ -56,5 +60,15 @@ RSpec.describe MergeRequests::ApprovalService do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'user cannot update the merge request' do
|
||||
before do
|
||||
project.add_guest(user)
|
||||
end
|
||||
|
||||
it 'does not update approvals' do
|
||||
expect { service.execute(merge_request) }.not_to change { merge_request.approvals.size }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -33,5 +33,14 @@ RSpec.describe MergeRequests::RemoveApprovalService do
|
|||
execute!
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a user who has not approved' do
|
||||
it 'does not create an unapproval note and triggers web hook' do
|
||||
expect(service).not_to receive(:execute_hooks)
|
||||
expect(SystemNoteService).not_to receive(:unapprove_mr)
|
||||
|
||||
execute!
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.shared_examples 'snippets sort order' do
|
||||
let(:params) { {} }
|
||||
let(:sort_argument) { {} }
|
||||
let(:sort_params) { params.merge(sort_argument)}
|
||||
|
||||
before do
|
||||
sign_in(user)
|
||||
|
||||
stub_snippet_counter
|
||||
end
|
||||
|
||||
subject { get :index, params: sort_params }
|
||||
|
||||
context 'when no sort param is provided' do
|
||||
it 'calls SnippetsFinder with updated_at sort option' do
|
||||
expect(SnippetsFinder).to receive(:new).with(user,
|
||||
hash_including(sort: 'updated_desc')).and_call_original
|
||||
|
||||
subject
|
||||
end
|
||||
end
|
||||
|
||||
context 'when sort param is provided' do
|
||||
let(:order) { 'created_desc' }
|
||||
let(:sort_argument) { { sort: order } }
|
||||
|
||||
it 'calls SnippetsFinder with the given sort param' do
|
||||
expect(SnippetsFinder).to receive(:new).with(user,
|
||||
hash_including(sort: order)).and_call_original
|
||||
|
||||
subject
|
||||
end
|
||||
end
|
||||
|
||||
def stub_snippet_counter
|
||||
allow(Snippets::CountService)
|
||||
.to receive(:new).and_return(double(:count_service, execute: {}))
|
||||
end
|
||||
end
|
|
@ -0,0 +1,9 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.shared_examples 'renders plain text email correctly' do
|
||||
it 'renders the email without HTML links' do
|
||||
render
|
||||
|
||||
expect(rendered).to have_no_selector('a')
|
||||
end
|
||||
end
|
|
@ -0,0 +1,19 @@
|
|||
# frozen_string_literal: true
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe 'notify/push_to_merge_request_email.text.haml' do
|
||||
let(:user) { create(:user, developer_projects: [project]) }
|
||||
let(:project) { create(:project, :repository) }
|
||||
let(:merge_request) { create(:merge_request, :simple, source_project: project) }
|
||||
let(:new_commits) { project.repository.commits_between('be93687618e4b132087f430a4d8fc3a609c9b77c', '54fcc214b94e78d7a41a9a8fe6d87a5e59500e51') }
|
||||
|
||||
before do
|
||||
assign(:updated_by_user, user)
|
||||
assign(:project, project)
|
||||
assign(:merge_request, merge_request)
|
||||
assign(:existing_commits, [])
|
||||
assign(:new_commits, new_commits)
|
||||
end
|
||||
|
||||
it_behaves_like 'renders plain text email correctly'
|
||||
end
|
|
@ -848,10 +848,10 @@
|
|||
resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.151.0.tgz#099905295d33eb31033f4a48eb3652da2f686239"
|
||||
integrity sha512-2PTSM8CFhUjeTFKfcq6E/YwPpOVdSVWupf3NhKO/bz/cisSBS5P7aWxaXKIaxy28ySyBKEfKaAT6b4rXTwvVgg==
|
||||
|
||||
"@gitlab/ui@17.19.1":
|
||||
version "17.19.1"
|
||||
resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-17.19.1.tgz#48c6d542b19fd6a420d22017e7026190aba1fd31"
|
||||
integrity sha512-RA4QXzVWOjbK3gjX78Luhtmo1z6td1uOu8S01v+yu5Pc00HKIgN6pdDwPK8+WLCK2cnu368c457A901wSr82Gg==
|
||||
"@gitlab/ui@17.21.0":
|
||||
version "17.21.0"
|
||||
resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-17.21.0.tgz#e881bac4540e3db29ee32e1dfd452677a445cd10"
|
||||
integrity sha512-Ijh3QPlB3Y10Sk0f0eZ/rgRIKHGSzAWZLugw9sb+ppcn9OPbb+2vk0ZgCcdIrfkrX3G8tD8q0Ndl3K1nrz6a5g==
|
||||
dependencies:
|
||||
"@babel/standalone" "^7.0.0"
|
||||
"@gitlab/vue-toasted" "^1.3.0"
|
||||
|
|
Loading…
Reference in New Issue