Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-11-03 09:10:37 +00:00
parent bbc0882f57
commit 058bd6be52
36 changed files with 553 additions and 49 deletions

View File

@ -8,6 +8,7 @@ gl-emoji {
}
.user-status-emoji {
margin-left: $gl-padding-4;
margin-right: $gl-padding-4;
gl-emoji {

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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,

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,7 @@
# frozen_string_literal: true
module IncidentManagement
class TimelineEventTagPolicy < ::BasePolicy
delegate { @subject.project }
end
end

View File

@ -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?

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1 @@
d7ec9ab32c5f58805bec64bea9bd32aedbd80f678d6b8e8c6914aa26523dcc95

View File

@ -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);

View File

@ -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.

View File

@ -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 |

View File

@ -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 |

View File

@ -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)

View File

@ -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'

View File

@ -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|

View File

@ -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

View File

@ -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>

View File

@ -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'

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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 } }

View File

@ -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

View File

@ -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

View File

@ -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
{

View File

@ -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
]