Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
bfb305ef51
commit
14846d722e
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"))
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 |
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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` |  |
|
||||
| `bar` (time series, that is when `group_by` is used) |  |
|
||||
| `line` |  |
|
||||
| `stacked-bar` |  |
|
||||
|
||||
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` |  |
|
||||
| `bar` (time series, that is when `group_by` is used) |  |
|
||||
| `line` |  |
|
||||
| `stacked-bar` |  |
|
||||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -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 ""
|
||||
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
/* eslint-disable import/no-commonjs */
|
||||
const loader = require('graphql-tag/loader');
|
||||
|
||||
module.exports = {
|
||||
process(src) {
|
||||
return loader.call({ cacheable() {} }, src);
|
||||
},
|
||||
};
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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) }
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
Loading…
Reference in New Issue