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 {
|
.user-status-emoji {
|
||||||
|
margin-left: $gl-padding-4;
|
||||||
margin-right: $gl-padding-4;
|
margin-right: $gl-padding-4;
|
||||||
|
|
||||||
gl-emoji {
|
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,
|
required: true,
|
||||||
description: 'Timestamp of when the event occurred.'
|
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)
|
def resolve(incident_id:, **args)
|
||||||
incident = authorized_find!(id: incident_id)
|
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 }
|
prepare: ->(id, ctx) { id.model_id }
|
||||||
end
|
end
|
||||||
|
|
||||||
def resolve(**args)
|
def resolve_with_lookahead(**args)
|
||||||
incident = args[:incident_id].find
|
incident = args[:incident_id].find
|
||||||
|
|
||||||
apply_lookahead(::IncidentManagement::TimelineEventsFinder.new(current_user, incident, args).execute)
|
apply_lookahead(::IncidentManagement::TimelineEventsFinder.new(current_user, incident, args).execute)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def preloads
|
||||||
|
{
|
||||||
|
timeline_event_tags: [:timeline_event_tags]
|
||||||
|
}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
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,
|
null: false,
|
||||||
description: 'Timestamp when the event occurred.'
|
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,
|
field :created_at,
|
||||||
Types::TimeType,
|
Types::TimeType,
|
||||||
null: false,
|
null: false,
|
||||||
|
|
|
@ -47,7 +47,7 @@ module Types
|
||||||
mount_mutation Mutations::DependencyProxy::ImageTtlGroupPolicy::Update
|
mount_mutation Mutations::DependencyProxy::ImageTtlGroupPolicy::Update
|
||||||
mount_mutation Mutations::DependencyProxy::GroupSettings::Update
|
mount_mutation Mutations::DependencyProxy::GroupSettings::Update
|
||||||
mount_mutation Mutations::Environments::CanaryIngress::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::PromoteFromNote
|
||||||
mount_mutation Mutations::IncidentManagement::TimelineEvent::Update
|
mount_mutation Mutations::IncidentManagement::TimelineEvent::Update
|
||||||
mount_mutation Mutations::IncidentManagement::TimelineEvent::Destroy
|
mount_mutation Mutations::IncidentManagement::TimelineEvent::Destroy
|
||||||
|
|
|
@ -20,6 +20,8 @@ module IncidentManagement
|
||||||
validates :name, uniqueness: { scope: :project_id }
|
validates :name, uniqueness: { scope: :project_id }
|
||||||
validates :name, length: { maximum: 255 }
|
validates :name, length: { maximum: 255 }
|
||||||
|
|
||||||
|
scope :by_names, -> (tag_names) { where(name: tag_names) }
|
||||||
|
|
||||||
def self.pluck_names
|
def self.pluck_names
|
||||||
pluck(:name)
|
pluck(:name)
|
||||||
end
|
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_namespace
|
||||||
enable :change_visibility_level
|
enable :change_visibility_level
|
||||||
enable :rename_project
|
|
||||||
enable :remove_project
|
enable :remove_project
|
||||||
enable :archive_project
|
enable :archive_project
|
||||||
enable :remove_fork_project
|
enable :remove_fork_project
|
||||||
|
@ -497,6 +496,7 @@ class ProjectPolicy < BasePolicy
|
||||||
enable :push_to_delete_protected_branch
|
enable :push_to_delete_protected_branch
|
||||||
enable :update_snippet
|
enable :update_snippet
|
||||||
enable :admin_snippet
|
enable :admin_snippet
|
||||||
|
enable :rename_project
|
||||||
enable :admin_project_member
|
enable :admin_project_member
|
||||||
enable :admin_note
|
enable :admin_note
|
||||||
enable :admin_wiki
|
enable :admin_wiki
|
||||||
|
@ -846,6 +846,10 @@ class ProjectPolicy < BasePolicy
|
||||||
enable :view_package_registry_project_settings
|
enable :view_package_registry_project_settings
|
||||||
end
|
end
|
||||||
|
|
||||||
|
rule { can?(:read_project) }.policy do
|
||||||
|
enable :read_incident_management_timeline_event_tag
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def user_is_user?
|
def user_is_user?
|
||||||
|
|
|
@ -99,6 +99,9 @@ module IncidentManagement
|
||||||
|
|
||||||
if timeline_event.save(context: validation_context)
|
if timeline_event.save(context: validation_context)
|
||||||
add_system_note(timeline_event)
|
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)
|
track_usage_event(:incident_management_timeline_event_created, user.id)
|
||||||
|
|
||||||
success(timeline_event)
|
success(timeline_event)
|
||||||
|
@ -126,6 +129,22 @@ module IncidentManagement
|
||||||
def validation_context
|
def validation_context
|
||||||
:user_input if !auto_created && params[:promoted_from_note].blank?
|
:user_input if !auto_created && params[:promoted_from_note].blank?
|
||||||
end
|
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
|
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_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);
|
CREATE INDEX index_packages_tags_on_package_id ON packages_tags USING btree (package_id);
|
||||||
|
|
||||||
|
|
|
@ -4882,6 +4882,10 @@ Input type: `TerraformStateUnlockInput`
|
||||||
|
|
||||||
### `Mutation.timelineEventCreate`
|
### `Mutation.timelineEventCreate`
|
||||||
|
|
||||||
|
WARNING:
|
||||||
|
**Introduced** in 15.6.
|
||||||
|
This feature is in Alpha. It can be changed or removed at any time.
|
||||||
|
|
||||||
Input type: `TimelineEventCreateInput`
|
Input type: `TimelineEventCreateInput`
|
||||||
|
|
||||||
#### Arguments
|
#### Arguments
|
||||||
|
@ -4892,6 +4896,7 @@ Input type: `TimelineEventCreateInput`
|
||||||
| <a id="mutationtimelineeventcreateincidentid"></a>`incidentId` | [`IssueID!`](#issueid) | Incident ID of the timeline event. |
|
| <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="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="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
|
#### 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="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. |
|
| <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`
|
#### `TimelineEventTypeConnection`
|
||||||
|
|
||||||
The connection type for [`TimelineEventType`](#timelineeventtype).
|
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="timeboxreporterrorcode"></a>`code` | [`TimeboxReportErrorReason`](#timeboxreporterrorreason) | Machine readable code, categorizing the error. |
|
||||||
| <a id="timeboxreporterrormessage"></a>`message` | [`String`](#string) | Human readable message explaining what happened. |
|
| <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`
|
### `TimelineEventType`
|
||||||
|
|
||||||
Describes an incident management timeline event.
|
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="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="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="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="timelineeventtypeupdatedat"></a>`updatedAt` | [`Time!`](#time) | Timestamp when the event updated. |
|
||||||
| <a id="timelineeventtypeupdatedbyuser"></a>`updatedByUser` | [`UserCore`](#usercore) | User that updated the timeline event. |
|
| <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"`.
|
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`
|
### `Int`
|
||||||
|
|
||||||
Represents non-fractional signed whole numeric values. Int can represent values between -(2^31) and 2^31 - 1.
|
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
|
||||||
GET /groups/:id/iterations?state=opened
|
GET /groups/:id/iterations?state=opened
|
||||||
GET /groups/:id/iterations?state=closed
|
GET /groups/:id/iterations?state=closed
|
||||||
GET /groups/:id/iterations?title=1.0
|
|
||||||
GET /groups/:id/iterations?search=version
|
GET /groups/:id/iterations?search=version
|
||||||
|
GET /groups/:id/iterations?include_ancestors=false
|
||||||
```
|
```
|
||||||
|
|
||||||
| Attribute | Type | Required | Description |
|
| Attribute | Type | Required | Description |
|
||||||
|
|
|
@ -22,8 +22,8 @@ Returns a list of project iterations.
|
||||||
GET /projects/:id/iterations
|
GET /projects/:id/iterations
|
||||||
GET /projects/:id/iterations?state=opened
|
GET /projects/:id/iterations?state=opened
|
||||||
GET /projects/:id/iterations?state=closed
|
GET /projects/:id/iterations?state=closed
|
||||||
GET /projects/:id/iterations?title=1.0
|
|
||||||
GET /projects/:id/iterations?search=version
|
GET /projects/:id/iterations?search=version
|
||||||
|
GET /projects/:id/iterations?include_ancestors=false
|
||||||
```
|
```
|
||||||
|
|
||||||
| Attribute | Type | Required | Description |
|
| Attribute | Type | Required | Description |
|
||||||
|
|
|
@ -1144,7 +1144,7 @@ The `output_example_snapshots` directory contains files which are generated by t
|
||||||
`glfm_specification/input` directory.
|
`glfm_specification/input` directory.
|
||||||
|
|
||||||
The `output-specification.rb` script generates
|
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,
|
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:
|
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
|
can be used to disable automatic generation of some examples. They can instead
|
||||||
be manually edited as necessary to help drive the implementations.
|
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).
|
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
|
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:
|
It is similar to [`spec.txt`](#spectxt), with the following differences:
|
||||||
|
|
||||||
1. [`spec.txt`](#spectxt) contains only examples for GitLab Flavored Markdown, but
|
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/)
|
"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.
|
and the [CommonMark specification](https://spec.commonmark.org/0.30/) specifications.
|
||||||
1. [`spec.txt`](#spectxt) represents the full GLFM specification, including additional header sections
|
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
|
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
|
the other [`output example snapshot files`](#output-example-snapshot-files) - it is not intended
|
||||||
to serve as an actual [specification file](#output-specification-files)
|
to serve as an actual [specification file](#output-specification-files)
|
||||||
like [`spec.txt`](#spectxt) or [`spec.html`](#spechtml).
|
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)
|
[`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 `glfm_snapshot_spec.md`. It is
|
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
|
generated (or updated) by the `update-specification.rb` script at the same time as
|
||||||
`glfm_snapshot_spec.md`.
|
`snapshot_spec.md`.
|
||||||
|
|
||||||
NOTE:
|
NOTE:
|
||||||
The formatting of this HTML is currently not identical to the GFM and CommonMark
|
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
|
the GitLab Markdown renderer. Properly formatting the HTML will require
|
||||||
duplicating or reusing the Lua script and template from the CommonMark project:
|
duplicating or reusing the Lua script and template from the CommonMark project:
|
||||||
[CommonMark Makefile](https://github.com/commonmark/commonmark-spec/blob/master/Makefile#L11)
|
[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'
|
requires :file_name, type: String, desc: 'Repository metadata file name'
|
||||||
end
|
end
|
||||||
get 'repodata/*file_name', requirements: { file_name: API::NO_SLASH_URL_PART_REGEX } do
|
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
|
end
|
||||||
|
|
||||||
desc 'Download RPM package files'
|
desc 'Download RPM package files'
|
||||||
|
|
|
@ -4,9 +4,10 @@ FactoryBot.define do
|
||||||
factory :rpm_repository_file, class: 'Packages::Rpm::RepositoryFile' do
|
factory :rpm_repository_file, class: 'Packages::Rpm::RepositoryFile' do
|
||||||
project
|
project
|
||||||
|
|
||||||
file_name { 'repomd.xml' }
|
file_name { '364c77dd49e8f814d56e621d0b3306c4fd0696dcad506f527329b818eb0f5db3-repomd.xml' }
|
||||||
file_sha1 { 'efae869b4e95d54796a46481f3a211d6a88d0323' }
|
file_sha1 { 'efae869b4e95d54796a46481f3a211d6a88d0323' }
|
||||||
file_md5 { 'ddf8a75330c896a8d7709e75f8b5982a' }
|
file_md5 { 'ddf8a75330c896a8d7709e75f8b5982a' }
|
||||||
|
file_sha256 { '364c77dd49e8f814d56e621d0b3306c4fd0696dcad506f527329b818eb0f5db3' }
|
||||||
size { 3127.kilobytes }
|
size { 3127.kilobytes }
|
||||||
status { :default }
|
status { :default }
|
||||||
|
|
||||||
|
@ -15,7 +16,11 @@ FactoryBot.define do
|
||||||
end
|
end
|
||||||
|
|
||||||
transient do
|
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
|
end
|
||||||
|
|
||||||
after(:build) do |package_file, evaluator|
|
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>
|
<revision>1644602779</revision>
|
||||||
<data type="filelists">
|
<data type="filelists">
|
||||||
<checksum type="sha256">6503673de76312406ff8ecb06d9733c32b546a65abae4d4170d9b51fb75bf253</checksum>
|
<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(:current_user) { create(:user) }
|
||||||
let_it_be(:project) { create(:project) }
|
let_it_be(:project) { create(:project) }
|
||||||
let_it_be(:incident) { create(:incident, project: 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 } }
|
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',
|
it_behaves_like 'responding with an incident timeline errors',
|
||||||
errors: ["Occurred at can't be blank and Timeline text can't be blank"]
|
errors: ["Occurred at can't be blank and Timeline text can't be blank"]
|
||||||
end
|
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
|
end
|
||||||
|
|
||||||
it_behaves_like 'failing to create an incident timeline event'
|
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
|
occurred_at
|
||||||
created_at
|
created_at
|
||||||
updated_at
|
updated_at
|
||||||
|
timeline_event_tags
|
||||||
]
|
]
|
||||||
|
|
||||||
expect(described_class).to have_graphql_fields(*expected_fields)
|
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::START_TIME_TAG_NAME).to eq('Start time') }
|
||||||
it { expect(described_class::END_TIME_TAG_NAME).to eq('End time') }
|
it { expect(described_class::END_TIME_TAG_NAME).to eq('End time') }
|
||||||
end
|
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
|
end
|
||||||
|
|
|
@ -533,6 +533,24 @@ RSpec.describe ProjectPolicy do
|
||||||
end
|
end
|
||||||
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
|
context 'reading a project' do
|
||||||
it 'allows access when a user has read access to the repo' 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)
|
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(:incident) { create(:incident, project: project) }
|
||||||
let_it_be(:event_occurred_at) { Time.current }
|
let_it_be(:event_occurred_at) { Time.current }
|
||||||
let_it_be(:note) { 'demo note' }
|
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
|
let(:mutation) do
|
||||||
graphql_mutation(:timeline_event_create, input) do
|
graphql_mutation(:timeline_event_create, input) do
|
||||||
<<~QL
|
<<~QL
|
||||||
|
@ -22,6 +30,7 @@ RSpec.describe 'Creating an incident timeline event' do
|
||||||
author { id username }
|
author { id username }
|
||||||
incident { id title }
|
incident { id title }
|
||||||
note
|
note
|
||||||
|
timelineEventTags { nodes { name } }
|
||||||
editable
|
editable
|
||||||
action
|
action
|
||||||
occurredAt
|
occurredAt
|
||||||
|
@ -64,4 +73,18 @@ RSpec.describe 'Creating an incident timeline event' do
|
||||||
it_behaves_like 'timeline event mutation responds with validation error',
|
it_behaves_like 'timeline event mutation responds with validation error',
|
||||||
error_message: 'Timeline text is too long (maximum is 280 characters)'
|
error_message: 'Timeline text is too long (maximum is 280 characters)'
|
||||||
end
|
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
|
end
|
||||||
|
|
|
@ -48,6 +48,7 @@ RSpec.describe 'getting incident timeline events' do
|
||||||
note
|
note
|
||||||
noteHtml
|
noteHtml
|
||||||
promotedFromNote { id body }
|
promotedFromNote { id body }
|
||||||
|
timelineEventTags { nodes { name } }
|
||||||
editable
|
editable
|
||||||
action
|
action
|
||||||
occurredAt
|
occurredAt
|
||||||
|
@ -100,6 +101,7 @@ RSpec.describe 'getting incident timeline events' do
|
||||||
'id' => promoted_from_note.to_global_id.to_s,
|
'id' => promoted_from_note.to_global_id.to_s,
|
||||||
'body' => promoted_from_note.note
|
'body' => promoted_from_note.note
|
||||||
},
|
},
|
||||||
|
'timelineEventTags' => { 'nodes' => [] },
|
||||||
'editable' => true,
|
'editable' => true,
|
||||||
'action' => timeline_event.action,
|
'action' => timeline_event.action,
|
||||||
'occurredAt' => timeline_event.occurred_at.iso8601,
|
'occurredAt' => timeline_event.occurred_at.iso8601,
|
||||||
|
@ -108,6 +110,47 @@ RSpec.describe 'getting incident timeline events' do
|
||||||
)
|
)
|
||||||
end
|
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
|
context 'when filtering by id' do
|
||||||
let(:params) { { incident_id: incident.to_global_id.to_s, id: timeline_event.to_global_id.to_s } }
|
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
|
end
|
||||||
|
|
||||||
context 'when authenticated as project owner' do
|
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
|
it 'updates visibility_level' do
|
||||||
project_param = { visibility: 'public' }
|
project_param = { visibility: 'public' }
|
||||||
|
|
||||||
|
@ -3795,10 +3783,16 @@ RSpec.describe API::Projects do
|
||||||
expect(json_response['message']['path']).to eq(['has already been taken'])
|
expect(json_response['message']['path']).to eq(['has already been taken'])
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'does not update name' do
|
it 'updates name' do
|
||||||
project_param = { name: 'bar' }
|
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
|
end
|
||||||
|
|
||||||
it 'does not update visibility_level' do
|
it 'does not update visibility_level' do
|
||||||
|
|
|
@ -37,7 +37,7 @@ RSpec.describe API::RpmProjectPackages do
|
||||||
it_behaves_like 'returning response status', status
|
it_behaves_like 'returning response status', status
|
||||||
end
|
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
|
context 'with deploy token headers' do
|
||||||
before do
|
before do
|
||||||
project.update_column(:visibility_level, Gitlab::VisibilityLevel::PRIVATE)
|
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) }
|
let(:headers) { basic_auth_header(deploy_token.username, deploy_token.token) }
|
||||||
|
|
||||||
context 'when token is valid' do
|
context 'when token is valid' do
|
||||||
it_behaves_like 'returning response status', :not_found
|
it_behaves_like 'returning response status', success_status
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when token is invalid' do
|
context 'when token is invalid' do
|
||||||
|
@ -57,7 +57,7 @@ RSpec.describe API::RpmProjectPackages do
|
||||||
end
|
end
|
||||||
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
|
context 'with job token headers' do
|
||||||
let(:headers) { basic_auth_header(::Gitlab::Auth::CI_JOB_USER, job.token) }
|
let(:headers) { basic_auth_header(::Gitlab::Auth::CI_JOB_USER, job.token) }
|
||||||
|
|
||||||
|
@ -67,7 +67,7 @@ RSpec.describe API::RpmProjectPackages do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with valid token' do
|
context 'with valid token' do
|
||||||
it_behaves_like 'returning response status', :not_found
|
it_behaves_like 'returning response status', success_status
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with invalid token' do
|
context 'with invalid token' do
|
||||||
|
@ -84,10 +84,10 @@ RSpec.describe API::RpmProjectPackages do
|
||||||
end
|
end
|
||||||
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
|
context 'with valid project' do
|
||||||
where(:visibility_level, :user_role, :member, :user_token, :shared_examples_name, :expected_status) 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' | :guest | true | true | 'process rpm packages upload/download' | :forbidden
|
||||||
'PUBLIC' | :developer | true | false | 'rejects rpm packages access' | :unauthorized
|
'PUBLIC' | :developer | true | false | 'rejects rpm packages access' | :unauthorized
|
||||||
'PUBLIC' | :guest | 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' | :developer | false | false | 'rejects rpm packages access' | :unauthorized
|
||||||
'PUBLIC' | :guest | 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
|
'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' | :guest | true | true | 'rejects rpm packages access' | :forbidden
|
||||||
'PRIVATE' | :developer | true | false | 'rejects rpm packages access' | :unauthorized
|
'PRIVATE' | :developer | true | false | 'rejects rpm packages access' | :unauthorized
|
||||||
'PRIVATE' | :guest | 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
|
end
|
||||||
|
|
||||||
describe 'GET /api/v4/projects/:project_id/packages/rpm/repodata/:filename' do
|
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 }
|
subject { get api(url), headers: headers }
|
||||||
|
|
||||||
it_behaves_like 'a job token for RPM requests'
|
it_behaves_like 'a job token for RPM requests', :success
|
||||||
it_behaves_like 'a deploy token for RPM requests'
|
it_behaves_like 'a deploy token for RPM requests', :success
|
||||||
it_behaves_like 'a user token for RPM requests'
|
it_behaves_like 'a user token for RPM requests', :success
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'GET /api/v4/projects/:id/packages/rpm/:package_file_id/:filename' do
|
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(:project) { create(:project) }
|
||||||
let_it_be_with_refind(:incident) { create(:incident, project: project) }
|
let_it_be_with_refind(:incident) { create(:incident, project: project) }
|
||||||
let_it_be(:comment) { create(:note, project: project, noteable: incident) }
|
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
|
let(:args) do
|
||||||
{
|
{
|
||||||
|
@ -134,6 +137,25 @@ RSpec.describe IncidentManagement::TimelineEvents::CreateService do
|
||||||
end
|
end
|
||||||
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
|
context 'with editable param' do
|
||||||
let(:args) 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_project admin_project_member admin_snippet admin_terraform_state
|
||||||
admin_wiki create_deploy_token destroy_deploy_token
|
admin_wiki create_deploy_token destroy_deploy_token
|
||||||
push_to_delete_protected_branch read_deploy_token update_snippet
|
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
|
end
|
||||||
|
|
||||||
|
@ -83,7 +83,7 @@ RSpec.shared_context 'ProjectPolicy context' do
|
||||||
let(:base_owner_permissions) do
|
let(:base_owner_permissions) do
|
||||||
%i[
|
%i[
|
||||||
archive_project change_namespace change_visibility_level destroy_issue
|
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_issue_created_at set_issue_iid set_issue_updated_at
|
||||||
set_note_created_at
|
set_note_created_at
|
||||||
]
|
]
|
||||||
|
|
Loading…
Reference in New Issue