Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-09-30 09:09:43 +00:00
parent bfb305ef51
commit 14846d722e
30 changed files with 439 additions and 386 deletions

View File

@ -69,7 +69,6 @@ RSpec/DescribedClass:
- 'ee/spec/services/resource_access_tokens/create_service_spec.rb'
- 'ee/spec/services/security/ingestion/tasks/update_vulnerability_uuids_spec.rb'
- 'ee/spec/services/users/captcha_challenge_service_spec.rb'
- 'ee/spec/services/vulnerabilities/create_from_security_finding_service_spec.rb'
- 'ee/spec/workers/concerns/elastic/indexing_control_spec.rb'
- 'ee/spec/workers/elastic/migration_worker_spec.rb'
- 'ee/spec/workers/geo/secondary/registry_consistency_worker_spec.rb'

View File

@ -99,8 +99,6 @@ RSpec/PredicateMatcher:
- 'ee/spec/services/jira/requests/issues/list_service_spec.rb'
- 'ee/spec/services/milestones/promote_service_spec.rb'
- 'ee/spec/services/protected_environments/create_service_spec.rb'
- 'ee/spec/services/vulnerabilities/create_from_security_finding_service_spec.rb'
- 'ee/spec/services/vulnerabilities/findings/create_from_security_finding_service_spec.rb'
- 'ee/spec/services/vulnerabilities/manually_create_service_spec.rb'
- 'ee/spec/services/vulnerability_exports/export_service_spec.rb'
- 'ee/spec/support/shared_examples/graphql/mutations/dast_on_demand_scans_shared_examples.rb'

View File

@ -98,8 +98,6 @@ RSpec/ScatteredLet:
- 'ee/spec/services/personal_access_tokens/groups/update_lifetime_service_spec.rb'
- 'ee/spec/services/sbom/ingestion/occurrence_map_spec.rb'
- 'ee/spec/services/security/report_summary_service_spec.rb'
- 'ee/spec/services/vulnerabilities/create_from_security_finding_service_spec.rb'
- 'ee/spec/services/vulnerabilities/findings/create_from_security_finding_service_spec.rb'
- 'ee/spec/services/vulnerabilities/security_finding/create_issue_service_spec.rb'
- 'ee/spec/views/subscriptions/groups/edit.html.haml_spec.rb'
- 'ee/spec/workers/concerns/update_orchestration_policy_configuration_spec.rb'

View File

@ -265,7 +265,6 @@ Style/FormatString:
- 'ee/app/services/vulnerabilities/destroy_dismissal_feedback_service.rb'
- 'ee/app/services/vulnerabilities/dismiss_service.rb'
- 'ee/app/services/vulnerabilities/finding_dismiss_service.rb'
- 'ee/app/services/vulnerabilities/findings/create_from_security_finding_service.rb'
- 'ee/app/services/vulnerabilities/historical_statistics/adjustment_service.rb'
- 'ee/app/services/vulnerabilities/statistics/adjustment_service.rb'
- 'ee/app/services/vulnerability_external_issue_links/create_service.rb'

View File

@ -24,6 +24,14 @@ module GraphqlTriggers
def self.issuable_dates_updated(issuable)
GitlabSchema.subscriptions.trigger('issuableDatesUpdated', { issuable_id: issuable.to_gid }, issuable)
end
def self.merge_request_reviewers_updated(merge_request)
GitlabSchema.subscriptions.trigger(
'mergeRequestReviewersUpdated',
{ issuable_id: merge_request.to_gid },
merge_request
)
end
end
GraphqlTriggers.prepend_mod

View File

@ -3,13 +3,10 @@
module Timebox
extend ActiveSupport::Concern
include AtomicInternalId
include CacheMarkdownField
include Gitlab::SQL::Pattern
include IidRoutes
include Referable
include StripAttribute
include FromUnion
TimeboxStruct = Struct.new(:title, :name, :id, :class_name) do
# Ensure these models match the interface required for exporting
@ -42,39 +39,19 @@ module Timebox
alias_method :timebox_id, :id
validates :group, presence: true, unless: :project
validates :project, presence: true, unless: :group
validate :timebox_type_check
validate :start_date_should_be_less_than_due_date, if: proc { |m| m.start_date.present? && m.due_date.present? }
validate :dates_within_4_digits
cache_markdown_field :title, pipeline: :single_line
cache_markdown_field :description, issuable_reference_expansion_enabled: true
belongs_to :project
belongs_to :group
has_many :issues
has_many :labels, -> { distinct.reorder('labels.title') }, through: :issues
has_many :merge_requests
scope :of_projects, ->(ids) { where(project_id: ids) }
scope :of_groups, ->(ids) { where(group_id: ids) }
scope :closed, -> { with_state(:closed) }
scope :for_projects, -> { where(group: nil).includes(:project) }
scope :with_title, -> (title) { where(title: title) }
scope :for_projects_and_groups, -> (projects, groups) do
projects = projects.compact if projects.is_a? Array
projects = [] if projects.nil?
groups = groups.compact if groups.is_a? Array
groups = [] if groups.nil?
from_union([where(project_id: projects), where(group_id: groups)], remove_duplicates: false)
end
# A timebox is within the timeframe (start_date, end_date) if it overlaps
# with that timeframe:
#
@ -132,10 +109,6 @@ module Timebox
end
end
def count_by_state
reorder(nil).group(:state).count
end
def predefined_id?(id)
[Any.id, None.id, Upcoming.id, Started.id].include?(id)
end
@ -145,29 +118,8 @@ module Timebox
end
end
##
# Returns the String necessary to reference a Timebox in Markdown. Group
# timeboxes only support name references, and do not support cross-project
# references.
#
# format - Symbol format to use (default: :iid, optional: :name)
#
# Examples:
#
# Milestone.first.to_reference # => "%1"
# Iteration.first.to_reference(format: :name) # => "*iteration:\"goal\""
# Milestone.first.to_reference(cross_namespace_project) # => "gitlab-org/gitlab-foss%1"
# Iteration.first.to_reference(same_namespace_project) # => "gitlab-foss*iteration:1"
#
def to_reference(from = nil, format: :name, full: false)
format_reference = timebox_format_reference(format)
reference = "#{self.class.reference_prefix}#{format_reference}"
if project
"#{project.to_reference_base(from, full: full)}#{reference}"
else
reference
end
def to_reference
raise NotImplementedError
end
def reference_link_text(from = nil)
@ -182,20 +134,12 @@ module Timebox
model_name.singular
end
def group_timebox?
group_id.present?
end
def project_timebox?
project_id.present?
end
def safe_title
title.to_slug.normalize.to_s
end
def resource_parent
group || project
raise NotImplementedError
end
def to_ability_name
@ -203,13 +147,7 @@ module Timebox
end
def merge_requests_enabled?
if group_timebox?
# Assume that groups have at least one project with merge requests enabled.
# Otherwise, we would need to load all of the projects from the database.
true
elsif project_timebox?
project&.merge_requests_enabled?
end
raise NotImplementedError
end
def weight_available?
@ -218,28 +156,6 @@ module Timebox
private
def timebox_format_reference(format = :iid)
raise ArgumentError, _('Unknown format') unless [:iid, :name].include?(format)
if group_timebox? && format == :iid
raise ArgumentError, _('Cannot refer to a group %{timebox_type} by an internal id!') % { timebox_type: timebox_name }
end
if format == :name && !name.include?('"')
%("#{name}")
else
iid
end
end
# Timebox should be either a project timebox or a group timebox
def timebox_type_check
if group_id && project_id
field = project_id_changed? ? :project_id : :group_id
errors.add(field, _("%{timebox_name} should belong either to a project or a group.") % { timebox_name: timebox_name })
end
end
def start_date_should_be_less_than_due_date
if due_date <= start_date
errors.add(:due_date, _("must be greater than start date"))

View File

@ -2,6 +2,12 @@
# Placeholder class for model that is implemented in EE
class Iteration < ApplicationRecord
include IgnorableColumns
# TODO https://gitlab.com/gitlab-org/gitlab/-/issues/372125
# TODO https://gitlab.com/gitlab-org/gitlab/-/issues/372126
ignore_column :project_id, remove_with: '15.6', remove_after: '2022-09-17'
self.table_name = 'sprints'
def self.reference_prefix

View File

@ -1,11 +1,13 @@
# frozen_string_literal: true
class Milestone < ApplicationRecord
include AtomicInternalId
include Sortable
include Timebox
include Milestoneish
include FromUnion
include Importable
include IidRoutes
prepend_mod_with('Milestone') # rubocop: disable Cop/InjectEnterpriseEditionModule
@ -13,6 +15,9 @@ class Milestone < ApplicationRecord
ALL = [::Timebox::None, ::Timebox::Any, ::Timebox::Started, ::Timebox::Upcoming].freeze
end
belongs_to :project
belongs_to :group
has_many :milestone_releases
has_many :releases, through: :milestone_releases
@ -30,13 +35,28 @@ class Milestone < ApplicationRecord
.order(:project_id, :group_id, :due_date)
end
scope :of_projects, ->(ids) { where(project_id: ids) }
scope :for_projects, -> { where(group: nil).includes(:project) }
scope :for_projects_and_groups, -> (projects, groups) do
projects = projects.compact if projects.is_a? Array
projects = [] if projects.nil?
groups = groups.compact if groups.is_a? Array
groups = [] if groups.nil?
from_union([where(project_id: projects), where(group_id: groups)], remove_duplicates: false)
end
scope :order_by_name_asc, -> { order(Arel::Nodes::Ascending.new(arel_table[:title].lower)) }
scope :reorder_by_due_date_asc, -> { reorder(arel_table[:due_date].asc.nulls_last) }
scope :with_api_entity_associations, -> { preload(project: [:project_feature, :route, namespace: :route]) }
scope :order_by_dates_and_title, -> { order(due_date: :asc, start_date: :asc, title: :asc) }
validates :group, presence: true, unless: :project
validates :project, presence: true, unless: :group
validates :title, presence: true
validates_associated :milestone_releases, message: -> (_, obj) { obj[:value].map(&:errors).map(&:full_messages).join(",") }
validate :parent_type_check
validate :uniqueness_of_title, if: :title_changed?
state_machine :state, initial: :active do
@ -176,10 +196,18 @@ class Milestone < ApplicationRecord
# TODO: remove after all code paths use `timebox_id`
# https://gitlab.com/gitlab-org/gitlab/-/issues/215688
alias_method :milestoneish_id, :timebox_id
# TODO: remove after all code paths use (group|project)_timebox?
# https://gitlab.com/gitlab-org/gitlab/-/issues/215690
alias_method :group_milestone?, :group_timebox?
alias_method :project_milestone?, :project_timebox?
def group_milestone?
group_id.present?
end
def project_milestone?
project_id.present?
end
def resource_parent
group || project
end
def parent
if group_milestone?
@ -193,8 +221,63 @@ class Milestone < ApplicationRecord
group_milestone? && parent.subgroup?
end
def merge_requests_enabled?
if group_milestone?
# Assume that groups have at least one project with merge requests enabled.
# Otherwise, we would need to load all of the projects from the database.
true
elsif project_milestone?
project&.merge_requests_enabled?
end
end
##
# Returns the String necessary to reference a milestone in Markdown. Group
# milestones only support name references, and do not support cross-project
# references.
#
# format - Symbol format to use (default: :iid, optional: :name)
#
# Examples:
#
# Milestone.first.to_reference # => "%1"
# Milestone.first.to_reference(cross_namespace_project) # => "gitlab-org/gitlab-foss%1"
#
def to_reference(from = nil, format: :name, full: false)
format_reference = timebox_format_reference(format)
reference = "#{self.class.reference_prefix}#{format_reference}"
if project
"#{project.to_reference_base(from, full: full)}#{reference}"
else
reference
end
end
private
def timebox_format_reference(format = :iid)
raise ArgumentError, _('Unknown format') unless [:iid, :name].include?(format)
if group_milestone? && format == :iid
raise ArgumentError, _('Cannot refer to a group milestone by an internal id!')
end
if format == :name && !name.include?('"')
%("#{name}")
else
iid
end
end
# Milestone should be either a project milestone or a group milestone
def parent_type_check
return unless group_id && project_id
field = project_id_changed? ? :project_id : :group_id
errors.add(field, _("milestone should belong either to a project or a group.") % { timebox_name: timebox_name })
end
def issues_finder_params
{ project_id: project_id, group_id: group_id, include_subgroups: group_id.present? }.compact
end

View File

@ -263,7 +263,6 @@ class Project < ApplicationRecord
has_many :integrations
has_many :events
has_many :milestones
has_many :iterations
# Projects with a very large number of notes may time out destroying them
# through the foreign key. Additionally, the deprecated attachment uploader

View File

@ -2,9 +2,6 @@
class UserDetail < ApplicationRecord
extend ::Gitlab::Utils::Override
include IgnorableColumns
ignore_columns :other_role, remove_after: '2022-07-22', remove_with: '15.3'
REGISTRATION_OBJECTIVE_PAIRS = { basics: 0, move_repository: 1, code_storage: 2, exploring: 3, ci: 4, other: 5, joining_team: 6 }.freeze

View File

@ -58,6 +58,7 @@ module MergeRequests
new_reviewers = merge_request.reviewers - old_reviewers
merge_request_activity_counter.track_users_review_requested(users: new_reviewers)
merge_request_activity_counter.track_reviewers_changed_action(user: current_user)
GraphqlTriggers.merge_request_reviewers_updated(merge_request)
end
def cleanup_environments(merge_request)

View File

@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/365986
milestone: '15.2'
type: development
group: group::container security
default_enabled: false
default_enabled: true

View File

@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/359883
milestone: '15.2'
type: development
group: group::container security
default_enabled: false
default_enabled: true

View File

@ -125,13 +125,16 @@ GET /groups?custom_attributes[key]=value&custom_attributes[other_key]=other_valu
## List a group's subgroups
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/15142) in GitLab 10.3.
Get a list of visible direct subgroups in this group.
When accessed without authentication, only public groups are returned.
By default, this request returns 20 results at a time because the API results [are paginated](index.md#pagination).
If you request this list as:
- An unauthenticated user, the response returns only public groups.
- An authenticated user, the response returns only the groups you're
a member of and does not include public groups.
Parameters:
| Attribute | Type | Required | Description |

View File

@ -7,22 +7,26 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Generate test vulnerabilities
You can generate test vulnerabilities when you work on the [Vulnerability Report](../vulnerability_report/index.md).
You can generate test vulnerabilities for the [Vulnerability Report](../vulnerability_report/index.md) to test GitLab
vulnerability management features without running a pipeline.
1. Login in to GitLab.
1. Go to `/-/profile/personal_access_tokens` and generate a personal access token with `api` permissions.
1. Go to your project page and find the project ID. You can find the project ID below the project title.
1. Open a terminal and go to the `gitlab/qa` directory.
1. [Clone the GitLab repository](../../../gitlab-basics/start-using-git.md#clone-a-repository) to your local machine.
1. Open a terminal and go to `gitlab/qa` directory.
1. Run `bundle install`
1. Run the following command:
```shell
GITLAB_QA_ACCESS_TOKEN=<your_personal_access_token> GITLAB_URL="http://localhost:3000" bundle exec rake vulnerabilities:setup\[<your_project_id>,<vulnerability_count>\] --trace
GITLAB_QA_ACCESS_TOKEN=<your_personal_access_token> GITLAB_URL="<address:port>" bundle exec rake vulnerabilities:setup\[<your_project_id>,<vulnerability_count>\] --trace
```
Make sure you do the following:
- Replace `<your_personal_access_token>` with the token you generated in step one.
- Double check the `GITLAB_URL`. It should point to the running local instance.
- Replace `<your_project_id>` with the ID you obtained in step two.
- Double check the `GITLAB_URL`. It should point to address and port of your GitLab instance, for example `http://localhost:3000` if you are running GDK
- Replace `<your_project_id>` with the ID you obtained in step three above.
- Replace `<vulnerability_count>` with the number of vulnerabilities you'd like to generate.
The script creates the specified amount of vulnerabilities in the project.
The script creates the specified number of placeholder vulnerabilities in the project.

View File

@ -95,8 +95,9 @@ The following table lists the chart parameters:
### `title`
`title` is the title of the chart as it displays on the Insights page.
For example:
Use `title` to update the chart title. The title displays on the insights report.
**Example:**
```yaml
monthlyBugsCreated:
@ -105,8 +106,9 @@ monthlyBugsCreated:
### `description`
The `description` text is displayed above the chart, but below the title. It's used
to give extra details regarding the chart, for example:
Use `description` to add a description of the chart. The description displays above the chart, below the title.
**Example:**
```yaml
monthlyBugsCreated:
@ -116,9 +118,20 @@ monthlyBugsCreated:
### `type`
`type` is the chart type.
Use `type` to define the chart type.
For example:
**Supported values:**
| Name | Example: |
| ----- | ------- |
| `bar` | ![Insights example bar chart](img/insights_example_bar_chart.png) |
| `bar` (time series, that is when `group_by` is used) | ![Insights example bar time series chart](img/insights_example_bar_time_series_chart.png) |
| `line` | ![Insights example stacked bar chart](img/insights_example_line_chart.png) |
| `stacked-bar` | ![Insights example stacked bar chart](img/insights_example_stacked_bar_chart.png) |
The `dora` data source supports the `bar` and `line` [chart types](#type).
**Example:**
```yaml
monthlyBugsCreated:
@ -126,23 +139,11 @@ monthlyBugsCreated:
type: bar
```
Supported values are:
| Name | Example |
| ----- | ------- |
| `bar` | ![Insights example bar chart](img/insights_example_bar_chart.png) |
| `bar` (time series, that is when `group_by` is used) | ![Insights example bar time series chart](img/insights_example_bar_time_series_chart.png) |
| `line` | ![Insights example stacked bar chart](img/insights_example_line_chart.png) |
| `stacked-bar` | ![Insights example stacked bar chart](img/insights_example_stacked_bar_chart.png) |
NOTE:
The `dora` data source supports the `bar` and `line` chart types.
### `query`
`query` allows to define the data source and various filtering conditions for the chart.
Use `query` to define the data source and filtering conditions for the chart.
Example:
**Example:**
```yaml
monthlyBugsCreated:
@ -190,46 +191,46 @@ monthlyBugsCreated:
> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/725) in GitLab 15.3.
The `data_source` parameter was introduced to allow visualizing data from different data sources.
Use `data_source` to define the data source that exposes the data.
Supported values are:
**Supported values:**
- `issuables`: Exposes merge request or issue data.
- `dora`: Exposes DORA metrics data.
- `dora`: Exposes DORA metrics.
#### `Issuable` query parameters
#### `issuable` query parameters
##### `query.params.issuable_type`
Defines the type of "issuable" you want to create a chart for.
Use `query.params.issuable_type` to define the type of issuable to create a chart for.
Supported values are:
**Supported values:**
- `issue`: The chart displays issues' data.
- `merge_request`: The chart displays merge requests' data.
##### `query.params.issuable_state`
Filter by the current state of the queried "issuable".
Use `query.params.issuable_state` to filter by the current state of the queried issuable.
By default, the `opened` state filter is applied.
Supported values are:
**Supported values:**
- `opened`: Open issues / merge requests.
- `closed`: Closed Open issues / merge requests.
- `locked`: Issues / merge requests that have their discussion locked.
- `opened`: Open issues or merge requests.
- `closed`: Closed issues or merge requests.
- `locked`: Issues or merge requests that have their discussion locked.
- `merged`: Merged merge requests.
- `all`: Issues / merge requests in all states
- `all`: Issues or merge requests in all states.
##### `query.params.filter_labels`
Filter by labels currently applied to the queried "issuable".
Use `query.params.filter_labels` to filter by labels applied to the queried issuable.
By default, no labels filter is applied. All the defined labels must be
currently applied to the "issuable" in order for it to be selected.
By default, no label filter is applied. All defined labels must
be applied to the issuable for it to be selected.
Example:
**Example:**:
```yaml
monthlyBugsCreated:
@ -247,12 +248,13 @@ monthlyBugsCreated:
##### `query.params.collection_labels`
Group "issuable" by the configured labels.
Use `query.params.collection_labels` to group issuables by the configured labels.
Grouping is not applied by default.
By default, no grouping is done. When using this keyword, you need to
set `type` to either `line` or `stacked-bar`.
When using this parameter, you must
set `type` to `line` or `stacked-bar`.
Example:
**Example:**
```yaml
weeklyBugsBySeverity:
@ -274,9 +276,9 @@ weeklyBugsBySeverity:
##### `query.group_by`
Define the X-axis of your chart.
Use `query.group_by` to define the X-axis of the chart.
Supported values are:
**Supported values:**
- `day`: Group data per day.
- `week`: Group data per week.
@ -284,11 +286,10 @@ Supported values are:
##### `query.period_limit`
Define how far "issuables" are queried in the past (using the `query.period_field`).
Use `query.period_limit` to define how far back in time to query issuables (using the `query.period_field`).
The unit is related to the `query.group_by` you defined. For instance if you
defined `query.group_by: 'day'` then `query.period_limit: 365` would mean
"Gather and display data for the last 365 days".
The unit is related to the value defined in `query.group_by`. For example, if you
defined `query.group_by: 'day'`, and `query.period_limit: 365`, the chart displays data from the last 365 days.
By default, default values are applied depending on the `query.group_by`
you defined.
@ -301,9 +302,9 @@ you defined.
#### `query.period_field`
Define the timestamp field used to group "issuables".
Use `query.period_field` to define the timestamp field by which to group issuables.
Supported values are:
**Supported values:**
- `created_at` (default): Group data using the `created_at` field.
- `closed_at`: Group data using the `closed_at` field (for issues only).
@ -323,7 +324,9 @@ you may see `created_at` in place of `merged_at`. `created_at` is used instead.
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/367248) in GitLab 15.3.
An example DORA chart definition:
Use DORA-specific queries with the `dora` data source to create a DORA chart definition.
**Example:**
```yaml
dora:
@ -355,53 +358,59 @@ dora:
##### `query.metric`
Defines which DORA metric to query. The available values are:
Use `query.metric` to define the [DORA metrics](../../../api/dora/metrics.md#the-value-field) to query.
**Supported values:**
- `deployment_frequency` (default)
- `lead_time_for_changes`
- `time_to_restore_service`
- `change_failure_rate`
The metrics are described on the [DORA API](../../../api/dora/metrics.md#the-value-field) page.
##### `query.group_by`
Define the X-axis of your chart.
Use `query.group_by` to define the X-axis of your chart.
Supported values are:
**Supported values:**
- `day` (default): Group data per day.
- `month`: Group data per month.
##### `query.period_limit`
Define how far the metrics are queried in the past (default: 15). Maximum lookback period is 180 days or 6 months.
Use `query.period_limit` to define how far the metrics are queried in the past (default: 15). The maximum period is 180 days or 6 months.
##### `query.environment_tiers`
An array of environments to include into the calculation (default: production). Available options: `production`, `staging`, `testing`, `development`, `other`.
Use `query.environment_tiers` to define an array of environments to include the calculation.
**Supported values:**
- `production`(default)
- `staging`
- `testing`
- `development`
- `other`
### `projects`
You can limit where the "issuables" can be queried from:
Use `projects` to limit where issuables are queried from:
- If `.gitlab/insights.yml` is used for a [group's insights](../../group/insights/index.md#configure-your-insights), with `projects`, you can limit the projects to be queried. By default, all projects currently under the group are used.
- If `.gitlab/insights.yml` is used for a project's insights, specifying any other projects yields no results. By default, the project itself is used.
- If `.gitlab/insights.yml` is used for a [group's insights](../../group/insights/index.md#configure-your-insights), use `projects` to define the projects from which to query issuables. By default, all projects under the group are used.
- If `.gitlab/insights.yml` is used for a project's insights, specifying other projects does not yield results. By default, the project is used.
#### `projects.only`
The `projects.only` option specifies the projects which the "issuables"
should be queried from.
Use `projects.only` to specify the projects from which issuables
are queried.
Projects listed here are ignored when:
Projects listed in this parameter are ignored when:
- They don't exist.
- The current user doesn't have sufficient permissions to read them.
- They are outside of the group.
- They are outside the group.
In the following `insights.yml` example, we specify the projects
the queries are used on. This example is useful when setting
a group's insights:
**Example:**
```yaml
monthlyBugsCreated:
@ -423,7 +432,7 @@ monthlyBugsCreated:
- groupB/project # Projects outside the group will be ignored
```
## Complete example
## Complete insights configuration example
```yaml
.projectsOnly: &projectsOnly

View File

@ -174,7 +174,7 @@ module.exports = (path, options = {}) => {
restoreMocks: true,
slowTestThreshold: process.env.CI ? 6000 : 500,
transform: {
'^.+\\.(gql|graphql)$': 'jest-transform-graphql',
'^.+\\.(gql|graphql)$': './spec/frontend/__helpers__/graphql_transformer.js',
'^.+_worker\\.js$': './spec/frontend/__helpers__/web_worker_transformer.js',
'^.+\\.js$': 'babel-jest',
'^.+\\.vue$': '@vue/vue2-jest',

View File

@ -1101,9 +1101,6 @@ msgstr ""
msgid "%{text} is available"
msgstr ""
msgid "%{timebox_name} should belong either to a project or a group."
msgstr ""
msgid "%{timebox_type} does not support burnup charts"
msgstr ""
@ -7578,7 +7575,7 @@ msgstr ""
msgid "Cannot promote issue due to insufficient permissions."
msgstr ""
msgid "Cannot refer to a group %{timebox_type} by an internal id!"
msgid "Cannot refer to a group milestone by an internal id!"
msgstr ""
msgid "Cannot show preview. For previews on sketch files, they must have the file format introduced by Sketch version 43 and above."
@ -47595,9 +47592,6 @@ msgstr ""
msgid "is not allowed. Please use your regular email address."
msgstr ""
msgid "is not allowed. We do not currently support project-level iterations"
msgstr ""
msgid "is not in the group enforcing Group Managed Account"
msgstr ""
@ -47730,6 +47724,9 @@ msgstr ""
msgid "metric_id must be unique across a project"
msgstr ""
msgid "milestone should belong either to a project or a group."
msgstr ""
msgid "missing"
msgstr ""

View File

@ -236,7 +236,6 @@
"jest-jasmine2": "^27.5.1",
"jest-junit": "^12.0.0",
"jest-raw-loader": "^1.0.1",
"jest-transform-graphql": "^2.1.0",
"jest-util": "^27.5.1",
"jsonlint": "^1.6.3",
"markdownlint-cli": "0.32.2",

View File

@ -3,42 +3,41 @@
require 'spec_helper'
RSpec.describe 'Related issues', :js do
let(:user) { create(:user) }
let(:project) { create(:project_empty_repo, :public) }
let(:project_b) { create(:project_empty_repo, :public) }
let(:project_unauthorized) { create(:project_empty_repo, :public) }
let(:issue_a) { create(:issue, project: project) }
let(:issue_b) { create(:issue, project: project) }
let(:issue_c) { create(:issue, project: project) }
let(:issue_d) { create(:issue, project: project) }
let(:issue_project_b_a) { create(:issue, project: project_b) }
let(:issue_project_unauthorized_a) { create(:issue, project: project_unauthorized) }
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project_empty_repo, :public) }
let_it_be(:project_b) { create(:project_empty_repo, :public) }
let_it_be(:project_unauthorized) { create(:project_empty_repo, :public) }
let_it_be(:internal_project) { create(:project_empty_repo, :internal) }
let_it_be(:private_project) { create(:project_empty_repo, :private) }
let_it_be(:public_project) { create(:project_empty_repo, :public) }
let_it_be(:issue_a) { create(:issue, project: project) }
let_it_be(:issue_b) { create(:issue, project: project) }
let_it_be(:issue_c) { create(:issue, project: project) }
let_it_be(:issue_d) { create(:issue, project: project) }
let_it_be(:issue_project_b_a) { create(:issue, project: project_b) }
let_it_be(:issue_project_unauthorized_a) { create(:issue, project: project_unauthorized) }
let_it_be(:internal_issue) { create(:issue, project: internal_project) }
let_it_be(:private_issue) { create(:issue, project: private_project) }
let_it_be(:public_issue) { create(:issue, project: public_project) }
context 'widget visibility' do
context 'when not logged in' do
it 'does not show widget when internal project' do
project = create :project_empty_repo, :internal
issue = create :issue, project: project
visit project_issue_path(project, issue)
visit project_issue_path(internal_project, internal_issue)
expect(page).not_to have_css('.related-issues-block')
end
it 'does not show widget when private project' do
project = create :project_empty_repo, :private
issue = create :issue, project: project
visit project_issue_path(project, issue)
visit project_issue_path(private_project, private_issue)
expect(page).not_to have_css('.related-issues-block')
end
it 'shows widget when public project' do
project = create :project_empty_repo, :public
issue = create :issue, project: project
visit project_issue_path(project, issue)
visit project_issue_path(public_project, public_issue)
expect(page).to have_css('.related-issues-block')
expect(page).not_to have_button 'Add a related issue'
@ -51,39 +50,29 @@ RSpec.describe 'Related issues', :js do
end
it 'shows widget when internal project' do
project = create :project_empty_repo, :internal
issue = create :issue, project: project
visit project_issue_path(project, issue)
visit project_issue_path(internal_project, internal_issue)
expect(page).to have_css('.related-issues-block')
expect(page).not_to have_button 'Add a related issue'
end
it 'does not show widget when private project' do
project = create :project_empty_repo, :private
issue = create :issue, project: project
visit project_issue_path(project, issue)
visit project_issue_path(private_project, private_issue)
expect(page).not_to have_css('.related-issues-block')
end
it 'shows widget when public project' do
project = create :project_empty_repo, :public
issue = create :issue, project: project
visit project_issue_path(project, issue)
visit project_issue_path(public_project, public_issue)
expect(page).to have_css('.related-issues-block')
expect(page).not_to have_button 'Add a related issue'
end
it 'shows widget on their own public issue' do
project = create :project_empty_repo, :public
issue = create :issue, project: project, author: user
issue = create :issue, project: public_project, author: user
visit project_issue_path(project, issue)
visit project_issue_path(public_project, issue)
expect(page).to have_css('.related-issues-block')
expect(page).not_to have_button 'Add a related issue'
@ -96,33 +85,27 @@ RSpec.describe 'Related issues', :js do
end
it 'shows widget when internal project' do
project = create :project_empty_repo, :internal
issue = create :issue, project: project
project.add_guest(user)
internal_project.add_guest(user)
visit project_issue_path(project, issue)
visit project_issue_path(internal_project, internal_issue)
expect(page).to have_css('.related-issues-block')
expect(page).not_to have_button 'Add a related issue'
end
it 'shows widget when private project' do
project = create :project_empty_repo, :private
issue = create :issue, project: project
project.add_guest(user)
private_project.add_guest(user)
visit project_issue_path(project, issue)
visit project_issue_path(private_project, private_issue)
expect(page).to have_css('.related-issues-block')
expect(page).not_to have_button 'Add a related issue'
end
it 'shows widget when public project' do
project = create :project_empty_repo, :public
issue = create :issue, project: project
project.add_guest(user)
public_project.add_guest(user)
visit project_issue_path(project, issue)
visit project_issue_path(public_project, public_issue)
expect(page).to have_css('.related-issues-block')
expect(page).not_to have_button 'Add a related issue'
@ -135,44 +118,37 @@ RSpec.describe 'Related issues', :js do
end
it 'shows widget when internal project' do
project = create :project_empty_repo, :internal
issue = create :issue, project: project
project.add_reporter(user)
internal_project.add_reporter(user)
visit project_issue_path(project, issue)
visit project_issue_path(internal_project, internal_issue)
expect(page).to have_css('.related-issues-block')
expect(page).to have_button 'Add a related issue'
end
it 'shows widget when private project' do
project = create :project_empty_repo, :private
issue = create :issue, project: project
project.add_reporter(user)
private_project.add_reporter(user)
visit project_issue_path(project, issue)
visit project_issue_path(private_project, private_issue)
expect(page).to have_css('.related-issues-block')
expect(page).to have_button 'Add a related issue'
end
it 'shows widget when public project' do
project = create :project_empty_repo, :public
issue = create :issue, project: project
project.add_reporter(user)
public_project.add_reporter(user)
visit project_issue_path(project, issue)
visit project_issue_path(public_project, public_issue)
expect(page).to have_css('.related-issues-block')
expect(page).to have_button 'Add a related issue'
end
it 'shows widget on their own public issue' do
project = create :project_empty_repo, :public
issue = create :issue, project: project, author: user
project.add_reporter(user)
issue = create :issue, project: public_project, author: user
public_project.add_reporter(user)
visit project_issue_path(project, issue)
visit project_issue_path(public_project, issue)
expect(page).to have_css('.related-issues-block')
expect(page).to have_button 'Add a related issue'
@ -184,8 +160,11 @@ RSpec.describe 'Related issues', :js do
let!(:issue_link_b) { create :issue_link, source: issue_a, target: issue_b }
let!(:issue_link_c) { create :issue_link, source: issue_a, target: issue_c }
before do
before_all do
project.add_guest(user)
end
before do
sign_in(user)
end
@ -213,9 +192,12 @@ RSpec.describe 'Related issues', :js do
end
context 'when user has permission to manage related issues' do
before do
before_all do
project.add_maintainer(user)
project_b.add_maintainer(user)
end
before do
sign_in(user)
end

View File

@ -5,13 +5,16 @@ require 'spec_helper'
RSpec.describe 'CI Lint', :js do
include Spec::Support::Helpers::Features::SourceEditorSpecHelpers
let(:project) { create(:project, :repository) }
let(:user) { create(:user) }
let_it_be(:project) { create(:project, :repository) }
let_it_be(:user) { create(:user) }
let(:content_selector) { '.content .view-lines' }
before do
before_all do
project.add_developer(user)
end
before do
sign_in(user)
visit project_ci_lint_path(project)

View File

@ -0,0 +1,8 @@
/* eslint-disable import/no-commonjs */
const loader = require('graphql-tag/loader');
module.exports = {
process(src) {
return loader.call({ cacheable() {} }, src);
},
};

View File

@ -75,4 +75,18 @@ RSpec.describe GraphqlTriggers do
GraphqlTriggers.issuable_dates_updated(work_item)
end
end
describe '.merge_request_reviewers_updated' do
it 'triggers the mergeRequestReviewersUpdated subscription' do
merge_request = build_stubbed(:merge_request)
expect(GitlabSchema.subscriptions).to receive(:trigger).with(
'mergeRequestReviewersUpdated',
{ issuable_id: merge_request.to_gid },
merge_request
).and_call_original
GraphqlTriggers.merge_request_reviewers_updated(merge_request)
end
end
end

View File

@ -5,9 +5,35 @@ require 'spec_helper'
RSpec.describe Milestone do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project, :public) }
let_it_be(:group) { create(:group) }
let_it_be(:issue) { create(:issue, project: project) }
describe 'modules' do
context 'with a project' do
it_behaves_like 'AtomicInternalId' do
let(:internal_id_attribute) { :iid }
let(:instance) { build(:milestone, project: create(:project), group: nil) }
let(:scope) { :project }
let(:scope_attrs) { { project: instance.project } }
let(:usage) { :milestones }
end
end
context 'with a group' do
it_behaves_like 'AtomicInternalId' do
let(:internal_id_attribute) { :iid }
let(:instance) { build(:milestone, project: nil, group: create(:group)) }
let(:scope) { :group }
let(:scope_attrs) { { namespace: instance.group } }
let(:usage) { :milestones }
end
end
end
it_behaves_like 'a timebox', :milestone do
let(:project) { create(:project, :public) }
let(:timebox) { create(:milestone, project: project) }
describe "#uniqueness_of_title" do
context "per project" do
it "does not accept the same title in a project twice" do
@ -25,7 +51,7 @@ RSpec.describe Milestone do
end
context "per group" do
let(:timebox) { create(:milestone, *timebox_args, group: group) }
let(:timebox) { create(:milestone, group: group) }
before do
project.update!(group: group)
@ -96,9 +122,22 @@ RSpec.describe Milestone do
end
end
end
describe '#parent_type_check' do
let(:milestone) { build(:milestone, group: group) }
it 'is invalid if it has both project_id and group_id' do
milestone.project = project
expect(milestone).not_to be_valid
expect(milestone.errors[:project_id]).to include("milestone should belong either to a project or a group.")
end
end
end
describe "Associations" do
it { is_expected.to belong_to(:project) }
it { is_expected.to belong_to(:group) }
it { is_expected.to have_many(:releases) }
it { is_expected.to have_many(:milestone_releases) }
end
@ -562,6 +601,57 @@ RSpec.describe Milestone do
it { is_expected.not_to match("gitlab-org/gitlab-ce/milestones/123") }
end
describe '#merge_requests_enabled?' do
context "per project" do
it "is true for projects with MRs enabled" do
project = create(:project, :merge_requests_enabled)
milestone = build(:milestone, project: project)
expect(milestone.merge_requests_enabled?).to be_truthy
end
it "is false for projects with MRs disabled" do
project = create(:project, :repository_enabled, :merge_requests_disabled)
milestone = build(:milestone, project: project)
expect(milestone.merge_requests_enabled?).to be_falsey
end
it "is false for projects with repository disabled" do
project = create(:project, :repository_disabled)
milestone = build(:milestone, project: project)
expect(milestone.merge_requests_enabled?).to be_falsey
end
end
context "per group" do
let(:milestone) { build(:milestone, group: group) }
it "is always true for groups, for performance reasons" do
expect(milestone.merge_requests_enabled?).to be_truthy
end
end
end
describe '#resource_parent' do
context 'when group is present' do
let(:milestone) { build(:milestone, group: group) }
it 'returns the group' do
expect(milestone.resource_parent).to eq(group)
end
end
context 'when project is present' do
let(:milestone) { build(:milestone, project: project) }
it 'returns the project' do
expect(milestone.resource_parent).to eq(project)
end
end
end
describe '#parent' do
context 'with group' do
it 'returns the expected parent' do
@ -598,4 +688,40 @@ RSpec.describe Milestone do
end
end
end
describe '#project_milestone?' do
context 'when project_id is present' do
let(:milestone) { build(:milestone, project: project) }
it 'returns true' do
expect(milestone.project_milestone?).to be_truthy
end
end
context 'when project_id is not present' do
let(:milestone) { build(:milestone, group: group) }
it 'returns false' do
expect(milestone.project_milestone?).to be_falsey
end
end
end
describe '#group_milestone?' do
context 'when group_id is present' do
let(:milestone) { build(:milestone, group: group) }
it 'returns true' do
expect(milestone.group_milestone?).to be_truthy
end
end
context 'when group_id is not present' do
let(:milestone) { build(:milestone, project: project) }
it 'returns false' do
expect(milestone.group_milestone?).to be_falsey
end
end
end
end

View File

@ -28,7 +28,6 @@ RSpec.describe Project, factory_default: :keep do
it { is_expected.to have_many(:issues) }
it { is_expected.to have_many(:incident_management_issuable_escalation_statuses).through(:issues).inverse_of(:project).class_name('IncidentManagement::IssuableEscalationStatus') }
it { is_expected.to have_many(:milestones) }
it { is_expected.to have_many(:iterations) }
it { is_expected.to have_many(:project_members).dependent(:delete_all) }
it { is_expected.to have_many(:users).through(:project_members) }
it { is_expected.to have_many(:requesters).dependent(:delete_all) }

View File

@ -128,6 +128,12 @@ RSpec.describe MergeRequests::UpdateReviewersService do
set_reviewers
end
it 'triggers graphql subscription mergeRequestReviewersUpdated' do
expect(GraphqlTriggers).to receive(:merge_request_reviewers_updated).with(merge_request)
set_reviewers
end
it 'calls MergeRequest::ResolveTodosService#async_execute' do
expect_next_instance_of(MergeRequests::ResolveTodosService, merge_request, user) do |service|
expect(service).to receive(:async_execute)
@ -149,6 +155,16 @@ RSpec.describe MergeRequests::UpdateReviewersService do
set_reviewers
end
context 'when reviewers did not change' do
let(:opts) { { reviewer_ids: merge_request.reviewer_ids } }
it 'does not trigger graphql subscription mergeRequestReviewersUpdated' do
expect(GraphqlTriggers).not_to receive(:merge_request_reviewers_updated)
set_reviewers
end
end
it 'does not update the reviewers if they do not have access' do
opts[:reviewer_ids] = [create(:user).id]

View File

@ -711,6 +711,20 @@ RSpec.describe MergeRequests::UpdateService, :mailer do
expect(user2.review_requested_open_merge_requests_count).to eq(1)
expect(user3.review_requested_open_merge_requests_count).to eq(0)
end
it 'triggers graphql subscription mergeRequestReviewersUpdated' do
expect(GraphqlTriggers).to receive(:merge_request_reviewers_updated).with(merge_request)
update_merge_request({ reviewer_ids: [user2.id] })
end
end
context 'when reviewers did not change' do
it 'does not trigger graphql subscription mergeRequestReviewersUpdated' do
expect(GraphqlTriggers).not_to receive(:merge_request_reviewers_updated)
update_merge_request({ reviewer_ids: [merge_request.reviewer_ids] })
end
end
context 'when the milestone is removed' do

View File

@ -3263,12 +3263,10 @@
- './ee/spec/services/users_ops_dashboard_projects/destroy_service_spec.rb'
- './ee/spec/services/users/update_highest_member_role_service_spec.rb'
- './ee/spec/services/vulnerabilities/confirm_service_spec.rb'
- './ee/spec/services/vulnerabilities/create_from_security_finding_service_spec.rb'
- './ee/spec/services/vulnerabilities/create_service_spec.rb'
- './ee/spec/services/vulnerabilities/destroy_dismissal_feedback_service_spec.rb'
- './ee/spec/services/vulnerabilities/dismiss_service_spec.rb'
- './ee/spec/services/vulnerabilities/finding_dismiss_service_spec.rb'
- './ee/spec/services/vulnerabilities/findings/create_from_security_finding_service_spec.rb'
- './ee/spec/services/vulnerabilities/historical_statistics/adjustment_service_spec.rb'
- './ee/spec/services/vulnerabilities/historical_statistics/deletion_service_spec.rb'
- './ee/spec/services/vulnerabilities/manually_create_service_spec.rb'

View File

@ -1,10 +1,7 @@
# frozen_string_literal: true
RSpec.shared_examples 'a timebox' do |timebox_type|
let(:project) { create(:project, :public) }
let(:group) { create(:group) }
let(:timebox_args) { [] }
let(:timebox) { create(timebox_type, *timebox_args, project: project) }
let(:issue) { create(:issue, project: project) }
let(:user) { create(:user) }
let(:timebox_table_name) { timebox_type.to_s.pluralize.to_sym }
@ -14,28 +11,6 @@ RSpec.shared_examples 'a timebox' do |timebox_type|
let(:open_on_left) { nil }
let(:open_on_right) { nil }
describe 'modules' do
context 'with a project' do
it_behaves_like 'AtomicInternalId' do
let(:internal_id_attribute) { :iid }
let(:instance) { build(timebox_type, *timebox_args, project: create(:project), group: nil) }
let(:scope) { :project }
let(:scope_attrs) { { project: instance.project } }
let(:usage) { timebox_table_name }
end
end
context 'with a group' do
it_behaves_like 'AtomicInternalId' do
let(:internal_id_attribute) { :iid }
let(:instance) { build(timebox_type, *timebox_args, project: nil, group: create(:group)) }
let(:scope) { :group }
let(:scope_attrs) { { namespace: instance.group } }
let(:usage) { timebox_table_name }
end
end
end
describe "Validation" do
before do
allow(subject).to receive(:set_iid).and_return(false)
@ -65,21 +40,9 @@ RSpec.shared_examples 'a timebox' do |timebox_type|
expect(timebox.errors[:due_date]).to include("date must not be after 9999-12-31")
end
end
describe '#timebox_type_check' do
it 'is invalid if it has both project_id and group_id' do
timebox = build(timebox_type, *timebox_args, group: group)
timebox.project = project
expect(timebox).not_to be_valid
expect(timebox.errors[:project_id]).to include("#{timebox_type} should belong either to a project or a group.")
end
end
end
describe "Associations" do
it { is_expected.to belong_to(:project) }
it { is_expected.to belong_to(:group) }
it { is_expected.to have_many(:issues) }
it { is_expected.to have_many(:merge_requests) }
it { is_expected.to have_many(:labels).through(:issues) }
@ -91,38 +54,6 @@ RSpec.shared_examples 'a timebox' do |timebox_type|
end
end
describe '#project_timebox?' do
context 'when project_id is present' do
it 'returns true' do
expect(timebox.project_timebox?).to be_truthy
end
end
context 'when project_id is not present' do
let(:timebox) { build(timebox_type, *timebox_args, group: group) }
it 'returns false' do
expect(timebox.project_timebox?).to be_falsey
end
end
end
describe '#group_timebox?' do
context 'when group_id is present' do
let(:timebox) { build(timebox_type, *timebox_args, group: group) }
it 'returns true' do
expect(timebox.group_timebox?).to be_truthy
end
end
context 'when group_id is not present' do
it 'returns false' do
expect(timebox.group_timebox?).to be_falsey
end
end
end
describe '#safe_title' do
let(:timebox) { create(timebox_type, *timebox_args, title: "<b>foo & bar -> 2.2</b>") }
@ -131,22 +62,6 @@ RSpec.shared_examples 'a timebox' do |timebox_type|
end
end
describe '#resource_parent' do
context 'when group is present' do
let(:timebox) { build(timebox_type, *timebox_args, group: group) }
it 'returns the group' do
expect(timebox.resource_parent).to eq(group)
end
end
context 'when project is present' do
it 'returns the project' do
expect(timebox.resource_parent).to eq(project)
end
end
end
describe "#title" do
let(:timebox) { create(timebox_type, *timebox_args, title: "<b>foo & bar -> 2.2</b>") }
@ -155,39 +70,6 @@ RSpec.shared_examples 'a timebox' do |timebox_type|
end
end
describe '#merge_requests_enabled?' do
context "per project" do
it "is true for projects with MRs enabled" do
project = create(:project, :merge_requests_enabled)
timebox = create(timebox_type, *timebox_args, project: project)
expect(timebox.merge_requests_enabled?).to be_truthy
end
it "is false for projects with MRs disabled" do
project = create(:project, :repository_enabled, :merge_requests_disabled)
timebox = create(timebox_type, *timebox_args, project: project)
expect(timebox.merge_requests_enabled?).to be_falsey
end
it "is false for projects with repository disabled" do
project = create(:project, :repository_disabled)
timebox = create(timebox_type, *timebox_args, project: project)
expect(timebox.merge_requests_enabled?).to be_falsey
end
end
context "per group" do
let(:timebox) { create(timebox_type, *timebox_args, group: group) }
it "is always true for groups, for performance reasons" do
expect(timebox.merge_requests_enabled?).to be_truthy
end
end
end
describe '#to_ability_name' do
it 'returns timebox' do
timebox = build(timebox_type, *timebox_args)

View File

@ -7578,11 +7578,6 @@ jest-snapshot@^27.5.1:
pretty-format "^27.5.1"
semver "^7.3.2"
jest-transform-graphql@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/jest-transform-graphql/-/jest-transform-graphql-2.1.0.tgz#903cb66bb27bc2772fd3e5dd4f7e9b57230f5829"
integrity sha1-kDy2a7J7wncv0+XdT36bVyMPWCk=
jest-util@^27.5.1:
version "27.5.1"
resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-27.5.1.tgz#3ba9771e8e31a0b85da48fe0b0891fb86c01c2f9"