diff --git a/app/assets/stylesheets/framework/emojis.scss b/app/assets/stylesheets/framework/emojis.scss
index a31910e3090..68a3493670d 100644
--- a/app/assets/stylesheets/framework/emojis.scss
+++ b/app/assets/stylesheets/framework/emojis.scss
@@ -8,6 +8,7 @@ gl-emoji {
}
.user-status-emoji {
+ margin-left: $gl-padding-4;
margin-right: $gl-padding-4;
gl-emoji {
diff --git a/app/finders/incident_management/timeline_event_tags_finder.rb b/app/finders/incident_management/timeline_event_tags_finder.rb
new file mode 100644
index 00000000000..71820bf7dcb
--- /dev/null
+++ b/app/finders/incident_management/timeline_event_tags_finder.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+module IncidentManagement
+ class TimelineEventTagsFinder
+ def initialize(user, timeline_event, params = {})
+ @user = user
+ @timeline_event = timeline_event
+ @params = params
+ end
+
+ def execute
+ return ::IncidentManagement::TimelineEventTag.none unless allowed?
+
+ timeline_event.timeline_event_tags
+ end
+
+ private
+
+ attr_reader :user, :timeline_event, :params
+
+ def allowed?
+ Ability.allowed?(user, :read_incident_management_timeline_event_tag, timeline_event)
+ end
+ end
+end
diff --git a/app/graphql/mutations/incident_management/timeline_event/create.rb b/app/graphql/mutations/incident_management/timeline_event/create.rb
index 1907954cada..419b814dc8c 100644
--- a/app/graphql/mutations/incident_management/timeline_event/create.rb
+++ b/app/graphql/mutations/incident_management/timeline_event/create.rb
@@ -18,6 +18,10 @@ module Mutations
required: true,
description: 'Timestamp of when the event occurred.'
+ argument :timeline_event_tag_names, [GraphQL::Types::String],
+ required: false,
+ description: copy_field_description(Types::IncidentManagement::TimelineEventType, :timeline_event_tags)
+
def resolve(incident_id:, **args)
incident = authorized_find!(id: incident_id)
diff --git a/app/graphql/resolvers/incident_management/timeline_event_tags_resolver.rb b/app/graphql/resolvers/incident_management/timeline_event_tags_resolver.rb
new file mode 100644
index 00000000000..ac6577d119b
--- /dev/null
+++ b/app/graphql/resolvers/incident_management/timeline_event_tags_resolver.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+module Resolvers
+ module IncidentManagement
+ class TimelineEventTagsResolver < BaseResolver
+ include LooksAhead
+
+ type ::Types::IncidentManagement::TimelineEventTagType.connection_type, null: true
+
+ def resolve(**args)
+ apply_lookahead(::IncidentManagement::TimelineEventTagsFinder.new(current_user, object, args).execute)
+ end
+ end
+ end
+end
diff --git a/app/graphql/resolvers/incident_management/timeline_events_resolver.rb b/app/graphql/resolvers/incident_management/timeline_events_resolver.rb
index b9978259e6b..0d46b1387b0 100644
--- a/app/graphql/resolvers/incident_management/timeline_events_resolver.rb
+++ b/app/graphql/resolvers/incident_management/timeline_events_resolver.rb
@@ -22,11 +22,17 @@ module Resolvers
prepare: ->(id, ctx) { id.model_id }
end
- def resolve(**args)
+ def resolve_with_lookahead(**args)
incident = args[:incident_id].find
apply_lookahead(::IncidentManagement::TimelineEventsFinder.new(current_user, incident, args).execute)
end
+
+ def preloads
+ {
+ timeline_event_tags: [:timeline_event_tags]
+ }
+ end
end
end
end
diff --git a/app/graphql/types/incident_management/timeline_event_tag_type.rb b/app/graphql/types/incident_management/timeline_event_tag_type.rb
new file mode 100644
index 00000000000..452294d4797
--- /dev/null
+++ b/app/graphql/types/incident_management/timeline_event_tag_type.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+module Types
+ module IncidentManagement
+ class TimelineEventTagType < BaseObject
+ graphql_name 'TimelineEventTagType'
+
+ description 'Describes a tag on an incident management timeline event.'
+
+ authorize :read_incident_management_timeline_event_tag
+
+ field :id,
+ Types::GlobalIDType[::IncidentManagement::TimelineEventTag],
+ null: false,
+ description: 'ID of the timeline event tag.'
+
+ field :name,
+ GraphQL::Types::String,
+ null: false,
+ description: 'Name of the timeline event tag.'
+ end
+ end
+end
diff --git a/app/graphql/types/incident_management/timeline_event_type.rb b/app/graphql/types/incident_management/timeline_event_type.rb
index 690facc8732..939dd9f09e5 100644
--- a/app/graphql/types/incident_management/timeline_event_type.rb
+++ b/app/graphql/types/incident_management/timeline_event_type.rb
@@ -53,6 +53,13 @@ module Types
null: false,
description: 'Timestamp when the event occurred.'
+ field :timeline_event_tags,
+ ::Types::IncidentManagement::TimelineEventTagType.connection_type,
+ null: true,
+ description: 'Tags for the incident timeline event.',
+ extras: [:lookahead],
+ resolver: Resolvers::IncidentManagement::TimelineEventTagsResolver
+
field :created_at,
Types::TimeType,
null: false,
diff --git a/app/graphql/types/mutation_type.rb b/app/graphql/types/mutation_type.rb
index e4b705245ed..efaf6c9c816 100644
--- a/app/graphql/types/mutation_type.rb
+++ b/app/graphql/types/mutation_type.rb
@@ -47,7 +47,7 @@ module Types
mount_mutation Mutations::DependencyProxy::ImageTtlGroupPolicy::Update
mount_mutation Mutations::DependencyProxy::GroupSettings::Update
mount_mutation Mutations::Environments::CanaryIngress::Update
- mount_mutation Mutations::IncidentManagement::TimelineEvent::Create
+ mount_mutation Mutations::IncidentManagement::TimelineEvent::Create, alpha: { milestone: '15.6' }
mount_mutation Mutations::IncidentManagement::TimelineEvent::PromoteFromNote
mount_mutation Mutations::IncidentManagement::TimelineEvent::Update
mount_mutation Mutations::IncidentManagement::TimelineEvent::Destroy
diff --git a/app/models/incident_management/timeline_event_tag.rb b/app/models/incident_management/timeline_event_tag.rb
index ad1ec04e1e7..2064ccc8c5e 100644
--- a/app/models/incident_management/timeline_event_tag.rb
+++ b/app/models/incident_management/timeline_event_tag.rb
@@ -20,6 +20,8 @@ module IncidentManagement
validates :name, uniqueness: { scope: :project_id }
validates :name, length: { maximum: 255 }
+ scope :by_names, -> (tag_names) { where(name: tag_names) }
+
def self.pluck_names
pluck(:name)
end
diff --git a/app/policies/incident_management/timeline_event_tag_policy.rb b/app/policies/incident_management/timeline_event_tag_policy.rb
new file mode 100644
index 00000000000..e2268d917b4
--- /dev/null
+++ b/app/policies/incident_management/timeline_event_tag_policy.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+module IncidentManagement
+ class TimelineEventTagPolicy < ::BasePolicy
+ delegate { @subject.project }
+ end
+end
diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb
index c71b26987a0..b0818d1de6c 100644
--- a/app/policies/project_policy.rb
+++ b/app/policies/project_policy.rb
@@ -254,7 +254,6 @@ class ProjectPolicy < BasePolicy
enable :change_namespace
enable :change_visibility_level
- enable :rename_project
enable :remove_project
enable :archive_project
enable :remove_fork_project
@@ -497,6 +496,7 @@ class ProjectPolicy < BasePolicy
enable :push_to_delete_protected_branch
enable :update_snippet
enable :admin_snippet
+ enable :rename_project
enable :admin_project_member
enable :admin_note
enable :admin_wiki
@@ -846,6 +846,10 @@ class ProjectPolicy < BasePolicy
enable :view_package_registry_project_settings
end
+ rule { can?(:read_project) }.policy do
+ enable :read_incident_management_timeline_event_tag
+ end
+
private
def user_is_user?
diff --git a/app/services/incident_management/timeline_events/create_service.rb b/app/services/incident_management/timeline_events/create_service.rb
index 32b9d3eab7b..c625b0d15bd 100644
--- a/app/services/incident_management/timeline_events/create_service.rb
+++ b/app/services/incident_management/timeline_events/create_service.rb
@@ -99,6 +99,9 @@ module IncidentManagement
if timeline_event.save(context: validation_context)
add_system_note(timeline_event)
+
+ create_timeline_event_tag_links(timeline_event, params[:timeline_event_tag_names])
+
track_usage_event(:incident_management_timeline_event_created, user.id)
success(timeline_event)
@@ -126,6 +129,22 @@ module IncidentManagement
def validation_context
:user_input if !auto_created && params[:promoted_from_note].blank?
end
+
+ def create_timeline_event_tag_links(timeline_event, tag_names)
+ return unless params[:timeline_event_tag_names]
+
+ tags = project.incident_management_timeline_event_tags.by_names(tag_names)
+
+ tag_links = tags.select(:id).map do |tag|
+ {
+ timeline_event_id: timeline_event.id,
+ timeline_event_tag_id: tag.id,
+ created_at: DateTime.current
+ }
+ end
+
+ IncidentManagement::TimelineEventTagLink.insert_all(tag_links) if tag_links.any?
+ end
end
end
end
diff --git a/config/feature_flags/development/allow_audit_event_type_filtering.yml b/config/feature_flags/development/allow_audit_event_type_filtering.yml
new file mode 100644
index 00000000000..e5cbd2fddcf
--- /dev/null
+++ b/config/feature_flags/development/allow_audit_event_type_filtering.yml
@@ -0,0 +1,8 @@
+---
+name: allow_audit_event_type_filtering
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/102502
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/373833
+milestone: '15.6'
+type: development
+group: group::compliance
+default_enabled: false
diff --git a/db/migrate/20221018092552_add_file_name_index_to_packages_rpm_repository_files.rb b/db/migrate/20221018092552_add_file_name_index_to_packages_rpm_repository_files.rb
new file mode 100644
index 00000000000..fcec3a6800d
--- /dev/null
+++ b/db/migrate/20221018092552_add_file_name_index_to_packages_rpm_repository_files.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+class AddFileNameIndexToPackagesRpmRepositoryFiles < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ NEW_INDEX_NAME = 'index_packages_rpm_repository_files_on_project_id_and_file_name'
+ OLD_INDEX_NAME = 'index_packages_rpm_repository_files_on_project_id'
+
+ def up
+ add_concurrent_index :packages_rpm_repository_files, %i[project_id file_name], name: NEW_INDEX_NAME
+ remove_concurrent_index :packages_rpm_repository_files, :project_id, name: OLD_INDEX_NAME
+ end
+
+ def down
+ add_concurrent_index :packages_rpm_repository_files, :project_id, name: OLD_INDEX_NAME
+ remove_concurrent_index :packages_rpm_repository_files, %i[project_id file_name], name: NEW_INDEX_NAME
+ end
+end
diff --git a/db/schema_migrations/20221018092552 b/db/schema_migrations/20221018092552
new file mode 100644
index 00000000000..8416f7d72a3
--- /dev/null
+++ b/db/schema_migrations/20221018092552
@@ -0,0 +1 @@
+d7ec9ab32c5f58805bec64bea9bd32aedbd80f678d6b8e8c6914aa26523dcc95
\ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index 691a2e29922..c84c6ebc749 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -29943,7 +29943,7 @@ CREATE INDEX index_packages_project_id_name_partial_for_nuget ON packages_packag
CREATE INDEX index_packages_rpm_metadata_on_package_id ON packages_rpm_metadata USING btree (package_id);
-CREATE INDEX index_packages_rpm_repository_files_on_project_id ON packages_rpm_repository_files USING btree (project_id);
+CREATE INDEX index_packages_rpm_repository_files_on_project_id_and_file_name ON packages_rpm_repository_files USING btree (project_id, file_name);
CREATE INDEX index_packages_tags_on_package_id ON packages_tags USING btree (package_id);
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index efbddb2e774..a750e5e7b11 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -4882,6 +4882,10 @@ Input type: `TerraformStateUnlockInput`
### `Mutation.timelineEventCreate`
+WARNING:
+**Introduced** in 15.6.
+This feature is in Alpha. It can be changed or removed at any time.
+
Input type: `TimelineEventCreateInput`
#### Arguments
@@ -4892,6 +4896,7 @@ Input type: `TimelineEventCreateInput`
| `incidentId` | [`IssueID!`](#issueid) | Incident ID of the timeline event. |
| `note` | [`String!`](#string) | Text note of the timeline event. |
| `occurredAt` | [`Time!`](#time) | Timestamp of when the event occurred. |
+| `timelineEventTagNames` | [`[String!]`](#string) | Tags for the incident timeline event. |
#### Fields
@@ -9320,6 +9325,29 @@ The edge type for [`TimeTrackingTimelogCategory`](#timetrackingtimelogcategory).
| `cursor` | [`String!`](#string) | A cursor for use in pagination. |
| `node` | [`TimeTrackingTimelogCategory`](#timetrackingtimelogcategory) | The item at the end of the edge. |
+#### `TimelineEventTagTypeConnection`
+
+The connection type for [`TimelineEventTagType`](#timelineeventtagtype).
+
+##### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| `edges` | [`[TimelineEventTagTypeEdge]`](#timelineeventtagtypeedge) | A list of edges. |
+| `nodes` | [`[TimelineEventTagType]`](#timelineeventtagtype) | A list of nodes. |
+| `pageInfo` | [`PageInfo!`](#pageinfo) | Information to aid in pagination. |
+
+#### `TimelineEventTagTypeEdge`
+
+The edge type for [`TimelineEventTagType`](#timelineeventtagtype).
+
+##### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| `cursor` | [`String!`](#string) | A cursor for use in pagination. |
+| `node` | [`TimelineEventTagType`](#timelineeventtagtype) | The item at the end of the edge. |
+
#### `TimelineEventTypeConnection`
The connection type for [`TimelineEventType`](#timelineeventtype).
@@ -18924,6 +18952,17 @@ Explains why we could not generate a timebox report.
| `code` | [`TimeboxReportErrorReason`](#timeboxreporterrorreason) | Machine readable code, categorizing the error. |
| `message` | [`String`](#string) | Human readable message explaining what happened. |
+### `TimelineEventTagType`
+
+Describes a tag on an incident management timeline event.
+
+#### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| `id` | [`IncidentManagementTimelineEventTagID!`](#incidentmanagementtimelineeventtagid) | ID of the timeline event tag. |
+| `name` | [`String!`](#string) | Name of the timeline event tag. |
+
### `TimelineEventType`
Describes an incident management timeline event.
@@ -18942,6 +18981,7 @@ Describes an incident management timeline event.
| `noteHtml` | [`String`](#string) | HTML note of the timeline event. |
| `occurredAt` | [`Time!`](#time) | Timestamp when the event occurred. |
| `promotedFromNote` | [`Note`](#note) | Note from which the timeline event was created. |
+| `timelineEventTags` | [`TimelineEventTagTypeConnection`](#timelineeventtagtypeconnection) | Tags for the incident timeline event. (see [Connections](#connections)) |
| `updatedAt` | [`Time!`](#time) | Timestamp when the event updated. |
| `updatedByUser` | [`UserCore`](#usercore) | User that updated the timeline event. |
@@ -22533,6 +22573,12 @@ A `IncidentManagementTimelineEventID` is a global ID. It is encoded as a string.
An example `IncidentManagementTimelineEventID` is: `"gid://gitlab/IncidentManagement::TimelineEvent/1"`.
+### `IncidentManagementTimelineEventTagID`
+
+A `IncidentManagementTimelineEventTagID` is a global ID. It is encoded as a string.
+
+An example `IncidentManagementTimelineEventTagID` is: `"gid://gitlab/IncidentManagement::TimelineEventTag/1"`.
+
### `Int`
Represents non-fractional signed whole numeric values. Int can represent values between -(2^31) and 2^31 - 1.
diff --git a/doc/api/group_iterations.md b/doc/api/group_iterations.md
index 92333de701c..988986d8965 100644
--- a/doc/api/group_iterations.md
+++ b/doc/api/group_iterations.md
@@ -20,8 +20,8 @@ Returns a list of group iterations.
GET /groups/:id/iterations
GET /groups/:id/iterations?state=opened
GET /groups/:id/iterations?state=closed
-GET /groups/:id/iterations?title=1.0
GET /groups/:id/iterations?search=version
+GET /groups/:id/iterations?include_ancestors=false
```
| Attribute | Type | Required | Description |
diff --git a/doc/api/iterations.md b/doc/api/iterations.md
index 5704bcd3418..4997a917a5a 100644
--- a/doc/api/iterations.md
+++ b/doc/api/iterations.md
@@ -22,8 +22,8 @@ Returns a list of project iterations.
GET /projects/:id/iterations
GET /projects/:id/iterations?state=opened
GET /projects/:id/iterations?state=closed
-GET /projects/:id/iterations?title=1.0
GET /projects/:id/iterations?search=version
+GET /projects/:id/iterations?include_ancestors=false
```
| Attribute | Type | Required | Description |
diff --git a/doc/development/gitlab_flavored_markdown/specification_guide/index.md b/doc/development/gitlab_flavored_markdown/specification_guide/index.md
index e64c145b3a3..17afebcf6ee 100644
--- a/doc/development/gitlab_flavored_markdown/specification_guide/index.md
+++ b/doc/development/gitlab_flavored_markdown/specification_guide/index.md
@@ -1144,7 +1144,7 @@ The `output_example_snapshots` directory contains files which are generated by t
`glfm_specification/input` directory.
The `output-specification.rb` script generates
-`output_snapshot_examples/glfm_snapshot_spec.md` and `output_snapshot_examples/glfm_snapshot_spec.html`.
+`output_snapshot_examples/snapshot_spec.md` and `output_snapshot_examples/snapshot_spec.html`.
These files are Markdown specification files containing examples generated based on input files,
similar to the `output_spec/spec.txt` and `output_spec/spec.html`, with the following differences:
@@ -1166,9 +1166,9 @@ key in `glfm_specification/input/gitlab_flavored_markdown/glfm_example_status.ym
can be used to disable automatic generation of some examples. They can instead
be manually edited as necessary to help drive the implementations.
-##### `glfm_snapshot_spec.md`
+##### `snapshot_spec.md`
-[`glfm_specification/output_example_snapshots/glfm_snapshot_spec.md`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/glfm_specification/output_example_snapshots/glfm_snapshot_spec.md)
+[`glfm_specification/output_example_snapshots/snapshot_spec.md`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/glfm_specification/output_example_snapshots/snapshot_spec.md)
is a Markdown file, containing standard Markdown + canonical HTML examples like [`spec.txt`](#spectxt).
It is generated or updated by the `update-specification.rb` script, using the
@@ -1180,26 +1180,26 @@ scripts such as `update-example-snapshots.rb`.
It is similar to [`spec.txt`](#spectxt), with the following differences:
1. [`spec.txt`](#spectxt) contains only examples for GitLab Flavored Markdown, but
- `glfm_snapshot_spec.md` also contains the full superset of examples from the
+ `snapshot_spec.md` also contains the full superset of examples from the
"GitHub Flavored Markdown" (GFM)[specification](https://github.github.com/gfm/)
and the [CommonMark specification](https://spec.commonmark.org/0.30/) specifications.
1. [`spec.txt`](#spectxt) represents the full GLFM specification, including additional header sections
- containing only explanatory prose and no examples, but `glfm_snapshot_spec.md` consists of only
+ containing only explanatory prose and no examples, but `snapshot_spec.md` consists of only
header sections which contain examples. This is because its purpose is to serve as input for
the other [`output example snapshot files`](#output-example-snapshot-files) - it is not intended
to serve as an actual [specification file](#output-specification-files)
like [`spec.txt`](#spectxt) or [`spec.html`](#spechtml).
-##### `glfm_snapshot_spec.html`
+##### `snapshot_spec.html`
-[`glfm_specification/output_snapshot_examples/glfm_snapshot_spec.html`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/glfm_specification/output_snapshot_examples/glfm_snapshot_spec.html)
-is an HTML file, rendered based on `glfm_snapshot_spec.md`. It is
+[`glfm_specification/output_snapshot_examples/snapshot_spec.html`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/glfm_specification/output_snapshot_examples/snapshot_spec.html)
+is an HTML file, rendered based on `snapshot_spec.md`. It is
generated (or updated) by the `update-specification.rb` script at the same time as
-`glfm_snapshot_spec.md`.
+`snapshot_spec.md`.
NOTE:
The formatting of this HTML is currently not identical to the GFM and CommonMark
-HTML-rendered specification. It is only the raw output of running `glfm_snapshot_spec.md` through
+HTML-rendered specification. It is only the raw output of running `snapshot_spec.md` through
the GitLab Markdown renderer. Properly formatting the HTML will require
duplicating or reusing the Lua script and template from the CommonMark project:
[CommonMark Makefile](https://github.com/commonmark/commonmark-spec/blob/master/Makefile#L11)
diff --git a/lib/api/rpm_project_packages.rb b/lib/api/rpm_project_packages.rb
index 1eb7361d356..13bbdfc0e6c 100644
--- a/lib/api/rpm_project_packages.rb
+++ b/lib/api/rpm_project_packages.rb
@@ -30,7 +30,14 @@ module API
requires :file_name, type: String, desc: 'Repository metadata file name'
end
get 'repodata/*file_name', requirements: { file_name: API::NO_SLASH_URL_PART_REGEX } do
- not_found!
+ authorize_read_package!(authorized_user_project)
+
+ repository_file = Packages::Rpm::RepositoryFile.find_by_project_id_and_file_name!(
+ authorized_user_project.id,
+ "#{params['file_name']}.#{params['format']}"
+ )
+
+ present_carrierwave_file!(repository_file.file)
end
desc 'Download RPM package files'
diff --git a/spec/factories/packages/rpm/rpm_repository_files.rb b/spec/factories/packages/rpm/rpm_repository_files.rb
index 079d32b3995..00755f49d98 100644
--- a/spec/factories/packages/rpm/rpm_repository_files.rb
+++ b/spec/factories/packages/rpm/rpm_repository_files.rb
@@ -4,9 +4,10 @@ FactoryBot.define do
factory :rpm_repository_file, class: 'Packages::Rpm::RepositoryFile' do
project
- file_name { 'repomd.xml' }
+ file_name { '364c77dd49e8f814d56e621d0b3306c4fd0696dcad506f527329b818eb0f5db3-repomd.xml' }
file_sha1 { 'efae869b4e95d54796a46481f3a211d6a88d0323' }
file_md5 { 'ddf8a75330c896a8d7709e75f8b5982a' }
+ file_sha256 { '364c77dd49e8f814d56e621d0b3306c4fd0696dcad506f527329b818eb0f5db3' }
size { 3127.kilobytes }
status { :default }
@@ -15,7 +16,11 @@ FactoryBot.define do
end
transient do
- file_fixture { 'spec/fixtures/packages/rpm/repodata/repomd.xml' }
+ file_fixture do
+ # rubocop:disable Layout/LineLength
+ 'spec/fixtures/packages/rpm/repodata/364c77dd49e8f814d56e621d0b3306c4fd0696dcad506f527329b818eb0f5db3-repomd.xml'
+ # rubocop:enable Layout/LineLength
+ end
end
after(:build) do |package_file, evaluator|
diff --git a/spec/finders/incident_management/timeline_event_tags_finder_spec.rb b/spec/finders/incident_management/timeline_event_tags_finder_spec.rb
new file mode 100644
index 00000000000..5bdb356ff62
--- /dev/null
+++ b/spec/finders/incident_management/timeline_event_tags_finder_spec.rb
@@ -0,0 +1,58 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe IncidentManagement::TimelineEventTagsFinder do
+ let_it_be(:user) { create(:user) }
+ let_it_be(:project) { create(:project) }
+ let_it_be(:incident) { create(:incident, project: project) }
+ let_it_be(:timeline_event) do
+ create(:incident_management_timeline_event, project: project, incident: incident, occurred_at: Time.current)
+ end
+
+ let_it_be(:timeline_event_tag) do
+ create(:incident_management_timeline_event_tag, project: project)
+ end
+
+ let_it_be(:timeline_event_tag_link) do
+ create(:incident_management_timeline_event_tag_link,
+ timeline_event: timeline_event,
+ timeline_event_tag: timeline_event_tag)
+ end
+
+ let(:params) { {} }
+
+ describe '#execute' do
+ subject(:execute) { described_class.new(user, timeline_event, params).execute }
+
+ context 'when user has permissions' do
+ before do
+ project.add_guest(user)
+ end
+
+ it 'returns tags on the event' do
+ is_expected.to match_array([timeline_event_tag])
+ end
+
+ context 'when event does not have tags' do
+ let(:timeline_event) do
+ create(:incident_management_timeline_event, project: project, incident: incident, occurred_at: Time.current)
+ end
+
+ it 'returns empty result' do
+ is_expected.to match_array([])
+ end
+ end
+
+ context 'when timeline event is nil' do
+ let(:timeline_event) { nil }
+
+ it { is_expected.to eq(IncidentManagement::TimelineEventTag.none) }
+ end
+ end
+
+ context 'when user does not have permissions' do
+ it { is_expected.to eq(IncidentManagement::TimelineEventTag.none) }
+ end
+ end
+end
diff --git a/spec/fixtures/packages/rpm/repodata/repomd.xml b/spec/fixtures/packages/rpm/repodata/364c77dd49e8f814d56e621d0b3306c4fd0696dcad506f527329b818eb0f5db3-repomd.xml
similarity index 82%
rename from spec/fixtures/packages/rpm/repodata/repomd.xml
rename to spec/fixtures/packages/rpm/repodata/364c77dd49e8f814d56e621d0b3306c4fd0696dcad506f527329b818eb0f5db3-repomd.xml
index 4554ee9a6d0..177a9be4723 100644
--- a/spec/fixtures/packages/rpm/repodata/repomd.xml
+++ b/spec/fixtures/packages/rpm/repodata/364c77dd49e8f814d56e621d0b3306c4fd0696dcad506f527329b818eb0f5db3-repomd.xml
@@ -1,4 +1,7 @@
-
+
1644602779
6503673de76312406ff8ecb06d9733c32b546a65abae4d4170d9b51fb75bf253
diff --git a/spec/graphql/mutations/incident_management/timeline_event/create_spec.rb b/spec/graphql/mutations/incident_management/timeline_event/create_spec.rb
index 9254d84b29c..61fb0d9458b 100644
--- a/spec/graphql/mutations/incident_management/timeline_event/create_spec.rb
+++ b/spec/graphql/mutations/incident_management/timeline_event/create_spec.rb
@@ -6,6 +6,9 @@ RSpec.describe Mutations::IncidentManagement::TimelineEvent::Create do
let_it_be(:current_user) { create(:user) }
let_it_be(:project) { create(:project) }
let_it_be(:incident) { create(:incident, project: project) }
+ let_it_be(:timeline_event_tag) do
+ create(:incident_management_timeline_event_tag, project: project)
+ end
let(:args) { { note: 'note', occurred_at: Time.current } }
@@ -39,6 +42,18 @@ RSpec.describe Mutations::IncidentManagement::TimelineEvent::Create do
it_behaves_like 'responding with an incident timeline errors',
errors: ["Occurred at can't be blank and Timeline text can't be blank"]
end
+
+ context 'when timeline event tags are passed' do
+ let(:args) do
+ {
+ note: 'note',
+ occurred_at: Time.current,
+ timeline_event_tag_names: [timeline_event_tag.name.to_s]
+ }
+ end
+
+ it_behaves_like 'creating an incident timeline event'
+ end
end
it_behaves_like 'failing to create an incident timeline event'
diff --git a/spec/graphql/resolvers/incident_management/timeline_event_tags_resolver_spec.rb b/spec/graphql/resolvers/incident_management/timeline_event_tags_resolver_spec.rb
new file mode 100644
index 00000000000..8ab34e05e52
--- /dev/null
+++ b/spec/graphql/resolvers/incident_management/timeline_event_tags_resolver_spec.rb
@@ -0,0 +1,92 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Resolvers::IncidentManagement::TimelineEventTagsResolver do
+ include GraphqlHelpers
+
+ let_it_be(:current_user) { create(:user) }
+ let_it_be(:project) { create(:project) }
+ let_it_be(:incident) { create(:incident, project: project) }
+
+ let_it_be(:timeline_event) do
+ create(:incident_management_timeline_event, project: project, incident: incident)
+ end
+
+ let_it_be(:timeline_event_with_no_tags) do
+ create(:incident_management_timeline_event, project: project, incident: incident)
+ end
+
+ let_it_be(:timeline_event_tag) do
+ create(:incident_management_timeline_event_tag, project: project)
+ end
+
+ let_it_be(:timeline_event_tag2) do
+ create(:incident_management_timeline_event_tag, project: project, name: 'Test tag 2')
+ end
+
+ let_it_be(:timeline_event_tag_link) do
+ create(:incident_management_timeline_event_tag_link,
+ timeline_event: timeline_event,
+ timeline_event_tag: timeline_event_tag)
+ end
+
+ let(:resolver) { described_class }
+
+ subject(:resolved_timeline_event_tags) do
+ sync(resolve_timeline_event_tags(timeline_event, current_user: current_user).to_a)
+ end
+
+ before do
+ project.add_guest(current_user)
+ end
+
+ specify do
+ expect(resolver).to have_nullable_graphql_type(
+ Types::IncidentManagement::TimelineEventTagType.connection_type
+ )
+ end
+
+ it 'returns timeline event tags', :aggregate_failures do
+ expect(resolved_timeline_event_tags.length).to eq(1)
+ expect(resolved_timeline_event_tags.first).to be_a(::IncidentManagement::TimelineEventTag)
+ end
+
+ context 'when timeline event is nil' do
+ subject(:resolved_timeline_event_tags) do
+ sync(resolve_timeline_event_tags(nil, current_user: current_user).to_a)
+ end
+
+ it 'returns no timeline event tags' do
+ expect(resolved_timeline_event_tags).to be_empty
+ end
+ end
+
+ context 'when there is no timeline event tag link' do
+ subject(:resolved_timeline_event_tags) do
+ sync(resolve_timeline_event_tags(timeline_event_with_no_tags, current_user: current_user).to_a)
+ end
+
+ it 'returns no timeline event tags' do
+ expect(resolved_timeline_event_tags).to be_empty
+ end
+ end
+
+ context 'when user does not have permissions' do
+ let(:non_member) { create(:user) }
+
+ subject(:resolved_timeline_event_tags) do
+ sync(resolve_timeline_event_tags(timeline_event, current_user: non_member).to_a)
+ end
+
+ it 'returns no timeline event tags' do
+ expect(resolved_timeline_event_tags).to be_empty
+ end
+ end
+
+ private
+
+ def resolve_timeline_event_tags(obj, context = { current_user: current_user })
+ resolve(resolver, obj: obj, args: {}, ctx: context, arg_style: :internal_prepared)
+ end
+end
diff --git a/spec/graphql/types/incident_management/timeline_event_tag_type_spec.rb b/spec/graphql/types/incident_management/timeline_event_tag_type_spec.rb
new file mode 100644
index 00000000000..831a598ab66
--- /dev/null
+++ b/spec/graphql/types/incident_management/timeline_event_tag_type_spec.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe GitlabSchema.types['TimelineEventTagType'] do
+ specify { expect(described_class.graphql_name).to eq('TimelineEventTagType') }
+
+ specify { expect(described_class).to require_graphql_authorizations(:read_incident_management_timeline_event_tag) }
+
+ it 'exposes the expected fields' do
+ expected_fields = %i[
+ id
+ name
+ ]
+
+ expect(described_class).to have_graphql_fields(*expected_fields)
+ end
+end
diff --git a/spec/graphql/types/incident_management/timeline_event_type_spec.rb b/spec/graphql/types/incident_management/timeline_event_type_spec.rb
index 5a6bc461f20..6805e0cdc9b 100644
--- a/spec/graphql/types/incident_management/timeline_event_type_spec.rb
+++ b/spec/graphql/types/incident_management/timeline_event_type_spec.rb
@@ -21,6 +21,7 @@ RSpec.describe GitlabSchema.types['TimelineEventType'] do
occurred_at
created_at
updated_at
+ timeline_event_tags
]
expect(described_class).to have_graphql_fields(*expected_fields)
diff --git a/spec/models/incident_management/timeline_event_tag_spec.rb b/spec/models/incident_management/timeline_event_tag_spec.rb
index 9dbb78a6d82..66cc885d8b8 100644
--- a/spec/models/incident_management/timeline_event_tag_spec.rb
+++ b/spec/models/incident_management/timeline_event_tag_spec.rb
@@ -39,4 +39,21 @@ RSpec.describe IncidentManagement::TimelineEventTag do
it { expect(described_class::START_TIME_TAG_NAME).to eq('Start time') }
it { expect(described_class::END_TIME_TAG_NAME).to eq('End time') }
end
+
+ describe '#by_names scope' do
+ let_it_be(:project) { create(:project) }
+ let_it_be(:project2) { create(:project) }
+ let_it_be(:tag1) { create(:incident_management_timeline_event_tag, name: 'Test tag 1', project: project) }
+ let_it_be(:tag2) { create(:incident_management_timeline_event_tag, name: 'Test tag 2', project: project) }
+ let_it_be(:tag3) { create(:incident_management_timeline_event_tag, name: 'Test tag 3', project: project2) }
+
+ it 'returns two matching tags' do
+ expect(described_class.by_names(['Test tag 1', 'Test tag 2'])).to contain_exactly(tag1, tag2)
+ end
+
+ it 'returns tags on the project' do
+ expect(project2.incident_management_timeline_event_tags.by_names(['Test tag 1',
+ 'Test tag 3'])).to contain_exactly(tag3)
+ end
+ end
end
diff --git a/spec/policies/project_policy_spec.rb b/spec/policies/project_policy_spec.rb
index 1024d4f0b4a..0ee9c24ee9b 100644
--- a/spec/policies/project_policy_spec.rb
+++ b/spec/policies/project_policy_spec.rb
@@ -533,6 +533,24 @@ RSpec.describe ProjectPolicy do
end
end
+ context 'with timeline event tags' do
+ context 'when user is member of the project' do
+ it 'allows access to timeline event tags' do
+ expect(described_class.new(owner, project)).to be_allowed(:read_incident_management_timeline_event_tag)
+ expect(described_class.new(developer, project)).to be_allowed(:read_incident_management_timeline_event_tag)
+ expect(described_class.new(admin, project)).to be_allowed(:read_incident_management_timeline_event_tag)
+ end
+ end
+
+ context 'when user is not a member of the project' do
+ let(:project) { private_project }
+
+ it 'disallows access to the timeline event tags' do
+ expect(described_class.new(non_member, project)).to be_disallowed(:read_incident_management_timeline_event_tag)
+ end
+ end
+ end
+
context 'reading a project' do
it 'allows access when a user has read access to the repo' do
expect(described_class.new(owner, project)).to be_allowed(:read_project)
diff --git a/spec/requests/api/graphql/mutations/incident_management/timeline_event/create_spec.rb b/spec/requests/api/graphql/mutations/incident_management/timeline_event/create_spec.rb
index bac78149cf9..fc3b666dd3d 100644
--- a/spec/requests/api/graphql/mutations/incident_management/timeline_event/create_spec.rb
+++ b/spec/requests/api/graphql/mutations/incident_management/timeline_event/create_spec.rb
@@ -10,8 +10,16 @@ RSpec.describe 'Creating an incident timeline event' do
let_it_be(:incident) { create(:incident, project: project) }
let_it_be(:event_occurred_at) { Time.current }
let_it_be(:note) { 'demo note' }
+ let_it_be(:tag1) { create(:incident_management_timeline_event_tag, project: project, name: 'Tag 1') }
+ let_it_be(:tag2) { create(:incident_management_timeline_event_tag, project: project, name: 'Tag 2') }
+
+ let(:input) do
+ { incident_id: incident.to_global_id.to_s,
+ note: note,
+ occurred_at: event_occurred_at,
+ timeline_event_tag_names: [tag1.name] }
+ end
- let(:input) { { incident_id: incident.to_global_id.to_s, note: note, occurred_at: event_occurred_at } }
let(:mutation) do
graphql_mutation(:timeline_event_create, input) do
<<~QL
@@ -22,6 +30,7 @@ RSpec.describe 'Creating an incident timeline event' do
author { id username }
incident { id title }
note
+ timelineEventTags { nodes { name } }
editable
action
occurredAt
@@ -64,4 +73,18 @@ RSpec.describe 'Creating an incident timeline event' do
it_behaves_like 'timeline event mutation responds with validation error',
error_message: 'Timeline text is too long (maximum is 280 characters)'
end
+
+ context 'when timeline event tags are passed' do
+ it 'creates incident timeline event with tags', :aggregate_failures do
+ post_graphql_mutation(mutation, current_user: user)
+
+ timeline_event_response = mutation_response['timelineEvent']
+ tag_names = timeline_event_response['timelineEventTags']['nodes']
+
+ expect(response).to have_gitlab_http_status(:success)
+ expect(timeline_event_response).to include(
+ 'timelineEventTags' => { 'nodes' => tag_names }
+ )
+ end
+ end
end
diff --git a/spec/requests/api/graphql/project/incident_management/timeline_events_spec.rb b/spec/requests/api/graphql/project/incident_management/timeline_events_spec.rb
index bcbb1f11d43..544d2d7bd95 100644
--- a/spec/requests/api/graphql/project/incident_management/timeline_events_spec.rb
+++ b/spec/requests/api/graphql/project/incident_management/timeline_events_spec.rb
@@ -48,6 +48,7 @@ RSpec.describe 'getting incident timeline events' do
note
noteHtml
promotedFromNote { id body }
+ timelineEventTags { nodes { name } }
editable
action
occurredAt
@@ -100,6 +101,7 @@ RSpec.describe 'getting incident timeline events' do
'id' => promoted_from_note.to_global_id.to_s,
'body' => promoted_from_note.note
},
+ 'timelineEventTags' => { 'nodes' => [] },
'editable' => true,
'action' => timeline_event.action,
'occurredAt' => timeline_event.occurred_at.iso8601,
@@ -108,6 +110,47 @@ RSpec.describe 'getting incident timeline events' do
)
end
+ context 'when timelineEvent tags are linked' do
+ let_it_be(:tag1) { create(:incident_management_timeline_event_tag, project: project, name: 'Tag 1') }
+ let_it_be(:tag2) { create(:incident_management_timeline_event_tag, project: project, name: 'Tag 2') }
+ let_it_be(:timeline_event_tag_link) do
+ create(:incident_management_timeline_event_tag_link,
+ timeline_event: timeline_event,
+ timeline_event_tag: tag1)
+ end
+
+ it_behaves_like 'a working graphql query'
+
+ it 'returns the set tags' do
+ expect(timeline_events.first['timelineEventTags']['nodes'].first['name']).to eq(tag1.name)
+ end
+
+ context 'when different timeline events are loaded' do
+ it 'avoids N+1 queries' do
+ control = ActiveRecord::QueryRecorder.new do
+ post_graphql(query, current_user: current_user)
+ end
+
+ new_event = create(:incident_management_timeline_event,
+ incident: incident,
+ project: project,
+ updated_by_user: updated_by_user,
+ promoted_from_note: promoted_from_note,
+ note: "Referencing #{issue.to_reference(full: true)} - Full URL #{issue_url}"
+ )
+
+ create(:incident_management_timeline_event_tag_link,
+ timeline_event: new_event,
+ timeline_event_tag: tag2
+ )
+
+ expect(incident.incident_management_timeline_events.length).to eq(3)
+ expect(post_graphql(query, current_user: current_user)).not_to exceed_query_limit(control)
+ expect(timeline_events.count).to eq(3)
+ end
+ end
+ end
+
context 'when filtering by id' do
let(:params) { { incident_id: incident.to_global_id.to_s, id: timeline_event.to_global_id.to_s } }
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index a6b2ef5cd86..746fe83e1a4 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -3426,18 +3426,6 @@ RSpec.describe API::Projects do
end
context 'when authenticated as project owner' do
- it 'updates name' do
- project_param = { name: 'bar' }
-
- put api("/projects/#{project.id}", user), params: project_param
-
- expect(response).to have_gitlab_http_status(:ok)
-
- project_param.each_pair do |k, v|
- expect(json_response[k.to_s]).to eq(v)
- end
- end
-
it 'updates visibility_level' do
project_param = { visibility: 'public' }
@@ -3795,10 +3783,16 @@ RSpec.describe API::Projects do
expect(json_response['message']['path']).to eq(['has already been taken'])
end
- it 'does not update name' do
+ it 'updates name' do
project_param = { name: 'bar' }
- put api("/projects/#{project3.id}", user4), params: project_param
- expect(response).to have_gitlab_http_status(:forbidden)
+
+ put api("/projects/#{project.id}", user), params: project_param
+
+ expect(response).to have_gitlab_http_status(:ok)
+
+ project_param.each_pair do |k, v|
+ expect(json_response[k.to_s]).to eq(v)
+ end
end
it 'does not update visibility_level' do
diff --git a/spec/requests/api/rpm_project_packages_spec.rb b/spec/requests/api/rpm_project_packages_spec.rb
index dd029a3ccf0..68511795c94 100644
--- a/spec/requests/api/rpm_project_packages_spec.rb
+++ b/spec/requests/api/rpm_project_packages_spec.rb
@@ -37,7 +37,7 @@ RSpec.describe API::RpmProjectPackages do
it_behaves_like 'returning response status', status
end
- shared_examples 'a deploy token for RPM requests' do
+ shared_examples 'a deploy token for RPM requests' do |success_status = :not_found|
context 'with deploy token headers' do
before do
project.update_column(:visibility_level, Gitlab::VisibilityLevel::PRIVATE)
@@ -46,7 +46,7 @@ RSpec.describe API::RpmProjectPackages do
let(:headers) { basic_auth_header(deploy_token.username, deploy_token.token) }
context 'when token is valid' do
- it_behaves_like 'returning response status', :not_found
+ it_behaves_like 'returning response status', success_status
end
context 'when token is invalid' do
@@ -57,7 +57,7 @@ RSpec.describe API::RpmProjectPackages do
end
end
- shared_examples 'a job token for RPM requests' do
+ shared_examples 'a job token for RPM requests' do |success_status = :not_found|
context 'with job token headers' do
let(:headers) { basic_auth_header(::Gitlab::Auth::CI_JOB_USER, job.token) }
@@ -67,7 +67,7 @@ RSpec.describe API::RpmProjectPackages do
end
context 'with valid token' do
- it_behaves_like 'returning response status', :not_found
+ it_behaves_like 'returning response status', success_status
end
context 'with invalid token' do
@@ -84,10 +84,10 @@ RSpec.describe API::RpmProjectPackages do
end
end
- shared_examples 'a user token for RPM requests' do
+ shared_examples 'a user token for RPM requests' do |success_status = :not_found|
context 'with valid project' do
where(:visibility_level, :user_role, :member, :user_token, :shared_examples_name, :expected_status) do
- 'PUBLIC' | :developer | true | true | 'process rpm packages upload/download' | :not_found
+ 'PUBLIC' | :developer | true | true | 'process rpm packages upload/download' | success_status
'PUBLIC' | :guest | true | true | 'process rpm packages upload/download' | :forbidden
'PUBLIC' | :developer | true | false | 'rejects rpm packages access' | :unauthorized
'PUBLIC' | :guest | true | false | 'rejects rpm packages access' | :unauthorized
@@ -96,7 +96,7 @@ RSpec.describe API::RpmProjectPackages do
'PUBLIC' | :developer | false | false | 'rejects rpm packages access' | :unauthorized
'PUBLIC' | :guest | false | false | 'rejects rpm packages access' | :unauthorized
'PUBLIC' | :anonymous | false | true | 'process rpm packages upload/download' | :unauthorized
- 'PRIVATE' | :developer | true | true | 'process rpm packages upload/download' | :not_found
+ 'PRIVATE' | :developer | true | true | 'process rpm packages upload/download' | success_status
'PRIVATE' | :guest | true | true | 'rejects rpm packages access' | :forbidden
'PRIVATE' | :developer | true | false | 'rejects rpm packages access' | :unauthorized
'PRIVATE' | :guest | true | false | 'rejects rpm packages access' | :unauthorized
@@ -124,13 +124,15 @@ RSpec.describe API::RpmProjectPackages do
end
describe 'GET /api/v4/projects/:project_id/packages/rpm/repodata/:filename' do
- let(:url) { "/projects/#{project.id}/packages/rpm/repodata/#{package_name}" }
+ let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace, user: user } }
+ let(:repository_file) { create(:rpm_repository_file, project: project) }
+ let(:url) { "/projects/#{project.id}/packages/rpm/repodata/#{repository_file.file_name}" }
subject { get api(url), headers: headers }
- it_behaves_like 'a job token for RPM requests'
- it_behaves_like 'a deploy token for RPM requests'
- it_behaves_like 'a user token for RPM requests'
+ it_behaves_like 'a job token for RPM requests', :success
+ it_behaves_like 'a deploy token for RPM requests', :success
+ it_behaves_like 'a user token for RPM requests', :success
end
describe 'GET /api/v4/projects/:id/packages/rpm/:package_file_id/:filename' do
diff --git a/spec/services/incident_management/timeline_events/create_service_spec.rb b/spec/services/incident_management/timeline_events/create_service_spec.rb
index 6ab02ad0b0b..4e9cc4fa09c 100644
--- a/spec/services/incident_management/timeline_events/create_service_spec.rb
+++ b/spec/services/incident_management/timeline_events/create_service_spec.rb
@@ -8,6 +8,9 @@ RSpec.describe IncidentManagement::TimelineEvents::CreateService do
let_it_be(:project) { create(:project) }
let_it_be_with_refind(:incident) { create(:incident, project: project) }
let_it_be(:comment) { create(:note, project: project, noteable: incident) }
+ let_it_be(:timeline_event_tag) do
+ create(:incident_management_timeline_event_tag, name: 'Test tag 1', project: project)
+ end
let(:args) do
{
@@ -134,6 +137,25 @@ RSpec.describe IncidentManagement::TimelineEvents::CreateService do
end
end
+ context 'when timeline event tag names are passed' do
+ let(:args) do
+ {
+ note: 'note',
+ occurred_at: Time.current,
+ action: 'new comment',
+ promoted_from_note: comment,
+ timeline_event_tag_names: ['Test tag 1']
+ }
+ end
+
+ it_behaves_like 'success response'
+
+ it 'matches the tag name' do
+ result = execute.payload[:timeline_event]
+ expect(result.timeline_event_tags.first).to eq(timeline_event_tag)
+ end
+ end
+
context 'with editable param' do
let(:args) do
{
diff --git a/spec/support/shared_contexts/policies/project_policy_shared_context.rb b/spec/support/shared_contexts/policies/project_policy_shared_context.rb
index 9f99a66e2e1..6e2caa853f8 100644
--- a/spec/support/shared_contexts/policies/project_policy_shared_context.rb
+++ b/spec/support/shared_contexts/policies/project_policy_shared_context.rb
@@ -68,7 +68,7 @@ RSpec.shared_context 'ProjectPolicy context' do
admin_project admin_project_member admin_snippet admin_terraform_state
admin_wiki create_deploy_token destroy_deploy_token
push_to_delete_protected_branch read_deploy_token update_snippet
- destroy_upload admin_member_access_request
+ destroy_upload admin_member_access_request rename_project
]
end
@@ -83,7 +83,7 @@ RSpec.shared_context 'ProjectPolicy context' do
let(:base_owner_permissions) do
%i[
archive_project change_namespace change_visibility_level destroy_issue
- destroy_merge_request manage_owners remove_fork_project remove_project rename_project
+ destroy_merge_request manage_owners remove_fork_project remove_project
set_issue_created_at set_issue_iid set_issue_updated_at
set_note_created_at
]