Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
bbc0882f57
commit
058bd6be52
|
@ -8,6 +8,7 @@ gl-emoji {
|
|||
}
|
||||
|
||||
.user-status-emoji {
|
||||
margin-left: $gl-padding-4;
|
||||
margin-right: $gl-padding-4;
|
||||
|
||||
gl-emoji {
|
||||
|
|
|
@ -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
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module IncidentManagement
|
||||
class TimelineEventTagPolicy < ::BasePolicy
|
||||
delegate { @subject.project }
|
||||
end
|
||||
end
|
|
@ -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?
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -0,0 +1 @@
|
|||
d7ec9ab32c5f58805bec64bea9bd32aedbd80f678d6b8e8c6914aa26523dcc95
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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`
|
|||
| <a id="mutationtimelineeventcreateincidentid"></a>`incidentId` | [`IssueID!`](#issueid) | Incident ID of the timeline event. |
|
||||
| <a id="mutationtimelineeventcreatenote"></a>`note` | [`String!`](#string) | Text note of the timeline event. |
|
||||
| <a id="mutationtimelineeventcreateoccurredat"></a>`occurredAt` | [`Time!`](#time) | Timestamp of when the event occurred. |
|
||||
| <a id="mutationtimelineeventcreatetimelineeventtagnames"></a>`timelineEventTagNames` | [`[String!]`](#string) | Tags for the incident timeline event. |
|
||||
|
||||
#### Fields
|
||||
|
||||
|
@ -9320,6 +9325,29 @@ The edge type for [`TimeTrackingTimelogCategory`](#timetrackingtimelogcategory).
|
|||
| <a id="timetrackingtimelogcategoryedgecursor"></a>`cursor` | [`String!`](#string) | A cursor for use in pagination. |
|
||||
| <a id="timetrackingtimelogcategoryedgenode"></a>`node` | [`TimeTrackingTimelogCategory`](#timetrackingtimelogcategory) | The item at the end of the edge. |
|
||||
|
||||
#### `TimelineEventTagTypeConnection`
|
||||
|
||||
The connection type for [`TimelineEventTagType`](#timelineeventtagtype).
|
||||
|
||||
##### Fields
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="timelineeventtagtypeconnectionedges"></a>`edges` | [`[TimelineEventTagTypeEdge]`](#timelineeventtagtypeedge) | A list of edges. |
|
||||
| <a id="timelineeventtagtypeconnectionnodes"></a>`nodes` | [`[TimelineEventTagType]`](#timelineeventtagtype) | A list of nodes. |
|
||||
| <a id="timelineeventtagtypeconnectionpageinfo"></a>`pageInfo` | [`PageInfo!`](#pageinfo) | Information to aid in pagination. |
|
||||
|
||||
#### `TimelineEventTagTypeEdge`
|
||||
|
||||
The edge type for [`TimelineEventTagType`](#timelineeventtagtype).
|
||||
|
||||
##### Fields
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="timelineeventtagtypeedgecursor"></a>`cursor` | [`String!`](#string) | A cursor for use in pagination. |
|
||||
| <a id="timelineeventtagtypeedgenode"></a>`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.
|
|||
| <a id="timeboxreporterrorcode"></a>`code` | [`TimeboxReportErrorReason`](#timeboxreporterrorreason) | Machine readable code, categorizing the error. |
|
||||
| <a id="timeboxreporterrormessage"></a>`message` | [`String`](#string) | Human readable message explaining what happened. |
|
||||
|
||||
### `TimelineEventTagType`
|
||||
|
||||
Describes a tag on an incident management timeline event.
|
||||
|
||||
#### Fields
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="timelineeventtagtypeid"></a>`id` | [`IncidentManagementTimelineEventTagID!`](#incidentmanagementtimelineeventtagid) | ID of the timeline event tag. |
|
||||
| <a id="timelineeventtagtypename"></a>`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.
|
|||
| <a id="timelineeventtypenotehtml"></a>`noteHtml` | [`String`](#string) | HTML note of the timeline event. |
|
||||
| <a id="timelineeventtypeoccurredat"></a>`occurredAt` | [`Time!`](#time) | Timestamp when the event occurred. |
|
||||
| <a id="timelineeventtypepromotedfromnote"></a>`promotedFromNote` | [`Note`](#note) | Note from which the timeline event was created. |
|
||||
| <a id="timelineeventtypetimelineeventtags"></a>`timelineEventTags` | [`TimelineEventTagTypeConnection`](#timelineeventtagtypeconnection) | Tags for the incident timeline event. (see [Connections](#connections)) |
|
||||
| <a id="timelineeventtypeupdatedat"></a>`updatedAt` | [`Time!`](#time) | Timestamp when the event updated. |
|
||||
| <a id="timelineeventtypeupdatedbyuser"></a>`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.
|
||||
|
|
|
@ -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 |
|
||||
|
|
|
@ -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 |
|
||||
|
|
|
@ -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" (<abbr title="GitHub Flavored Markdown">GFM</abbr>)[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)
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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|
|
||||
|
|
|
@ -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
|
|
@ -1,4 +1,7 @@
|
|||
<repomd xmlns="http://gitlab.com/api/v4/projects/1/packages/rpm/repodata/repomd.xml" xmlns:rpm="http://gitlab.com/api/v4/projects/1/packages/rpm/repodata/repomd.xml">
|
||||
<repomd
|
||||
xmlns="http://gitlab.com/api/v4/projects/1/packages/rpm/repodata/364c77dd49e8f814d56e621d0b3306c4fd0696dcad506f527329b818eb0f5db3-repomd.xml"
|
||||
xmlns:rpm="http://gitlab.com/api/v4/projects/1/packages/rpm/repodata/364c77dd49e8f814d56e621d0b3306c4fd0696dcad506f527329b818eb0f5db3-repomd.xml"
|
||||
>
|
||||
<revision>1644602779</revision>
|
||||
<data type="filelists">
|
||||
<checksum type="sha256">6503673de76312406ff8ecb06d9733c32b546a65abae4d4170d9b51fb75bf253</checksum>
|
|
@ -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'
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 } }
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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
|
||||
]
|
||||
|
|
Loading…
Reference in New Issue