Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
e17e27b84b
commit
9a35de9bc5
|
@ -0,0 +1,25 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module Mutations
|
||||||
|
module IncidentManagement
|
||||||
|
module TimelineEventTag
|
||||||
|
class Base < BaseMutation
|
||||||
|
field :timeline_event_tag,
|
||||||
|
::Types::IncidentManagement::TimelineEventTagType,
|
||||||
|
null: true,
|
||||||
|
description: 'Timeline event tag.'
|
||||||
|
|
||||||
|
authorize :admin_incident_management_timeline_event_tag
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def response(result)
|
||||||
|
{
|
||||||
|
timeline_event_tag: result.payload[:timeline_event_tag],
|
||||||
|
errors: result.errors
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,29 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module Mutations
|
||||||
|
module IncidentManagement
|
||||||
|
module TimelineEventTag
|
||||||
|
class Create < Base
|
||||||
|
graphql_name 'TimelineEventTagCreate'
|
||||||
|
|
||||||
|
include FindsProject
|
||||||
|
|
||||||
|
argument :project_path, GraphQL::Types::ID,
|
||||||
|
required: true,
|
||||||
|
description: 'Project to create the timeline event tag in.'
|
||||||
|
|
||||||
|
argument :name, GraphQL::Types::String,
|
||||||
|
required: true,
|
||||||
|
description: 'Name of the tag.'
|
||||||
|
|
||||||
|
def resolve(project_path:, **args)
|
||||||
|
project = authorized_find!(project_path)
|
||||||
|
|
||||||
|
response ::IncidentManagement::TimelineEventTags::CreateService.new(
|
||||||
|
project, current_user, args
|
||||||
|
).execute
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -51,6 +51,7 @@ module Types
|
||||||
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
|
||||||
|
mount_mutation Mutations::IncidentManagement::TimelineEventTag::Create
|
||||||
mount_mutation Mutations::Issues::Create
|
mount_mutation Mutations::Issues::Create
|
||||||
mount_mutation Mutations::Issues::SetAssignees
|
mount_mutation Mutations::Issues::SetAssignees
|
||||||
mount_mutation Mutations::Issues::SetCrmContacts
|
mount_mutation Mutations::Issues::SetCrmContacts
|
||||||
|
|
|
@ -537,6 +537,7 @@ class ProjectPolicy < BasePolicy
|
||||||
enable :read_web_hooks
|
enable :read_web_hooks
|
||||||
enable :read_upload
|
enable :read_upload
|
||||||
enable :destroy_upload
|
enable :destroy_upload
|
||||||
|
enable :admin_incident_management_timeline_event_tag
|
||||||
end
|
end
|
||||||
|
|
||||||
rule { public_project & metrics_dashboard_allowed }.policy do
|
rule { public_project & metrics_dashboard_allowed }.policy do
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module IncidentManagement
|
||||||
|
module TimelineEventTags
|
||||||
|
class BaseService
|
||||||
|
def allowed?
|
||||||
|
user&.can?(:admin_incident_management_timeline_event_tag, project)
|
||||||
|
end
|
||||||
|
|
||||||
|
def success(timeline_event_tag)
|
||||||
|
ServiceResponse.success(payload: { timeline_event_tag: timeline_event_tag })
|
||||||
|
end
|
||||||
|
|
||||||
|
def error(message)
|
||||||
|
ServiceResponse.error(message: message)
|
||||||
|
end
|
||||||
|
|
||||||
|
def error_no_permissions
|
||||||
|
error(_('You have insufficient permissions to manage timeline event tags for this project'))
|
||||||
|
end
|
||||||
|
|
||||||
|
def error_in_save(timeline_event_tag)
|
||||||
|
error(timeline_event_tag.errors.full_messages.to_sentence)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,32 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module IncidentManagement
|
||||||
|
module TimelineEventTags
|
||||||
|
class CreateService < TimelineEventTags::BaseService
|
||||||
|
attr_reader :project, :user, :params
|
||||||
|
|
||||||
|
def initialize(project, user, params)
|
||||||
|
@project = project
|
||||||
|
@user = user
|
||||||
|
@params = params
|
||||||
|
end
|
||||||
|
|
||||||
|
def execute
|
||||||
|
return error_no_permissions unless allowed?
|
||||||
|
|
||||||
|
timeline_event_tag_params = {
|
||||||
|
project: project,
|
||||||
|
name: params[:name]
|
||||||
|
}
|
||||||
|
|
||||||
|
timeline_event_tag = IncidentManagement::TimelineEventTag.new(timeline_event_tag_params)
|
||||||
|
|
||||||
|
if timeline_event_tag.save
|
||||||
|
success(timeline_event_tag)
|
||||||
|
else
|
||||||
|
error_in_save(timeline_event_tag)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -4944,6 +4944,26 @@ Input type: `TimelineEventPromoteFromNoteInput`
|
||||||
| <a id="mutationtimelineeventpromotefromnoteerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
|
| <a id="mutationtimelineeventpromotefromnoteerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
|
||||||
| <a id="mutationtimelineeventpromotefromnotetimelineevent"></a>`timelineEvent` | [`TimelineEventType`](#timelineeventtype) | Timeline event. |
|
| <a id="mutationtimelineeventpromotefromnotetimelineevent"></a>`timelineEvent` | [`TimelineEventType`](#timelineeventtype) | Timeline event. |
|
||||||
|
|
||||||
|
### `Mutation.timelineEventTagCreate`
|
||||||
|
|
||||||
|
Input type: `TimelineEventTagCreateInput`
|
||||||
|
|
||||||
|
#### Arguments
|
||||||
|
|
||||||
|
| Name | Type | Description |
|
||||||
|
| ---- | ---- | ----------- |
|
||||||
|
| <a id="mutationtimelineeventtagcreateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
|
||||||
|
| <a id="mutationtimelineeventtagcreatename"></a>`name` | [`String!`](#string) | Name of the tag. |
|
||||||
|
| <a id="mutationtimelineeventtagcreateprojectpath"></a>`projectPath` | [`ID!`](#id) | Project to create the timeline event tag in. |
|
||||||
|
|
||||||
|
#### Fields
|
||||||
|
|
||||||
|
| Name | Type | Description |
|
||||||
|
| ---- | ---- | ----------- |
|
||||||
|
| <a id="mutationtimelineeventtagcreateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
|
||||||
|
| <a id="mutationtimelineeventtagcreateerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
|
||||||
|
| <a id="mutationtimelineeventtagcreatetimelineeventtag"></a>`timelineEventTag` | [`TimelineEventTagType`](#timelineeventtagtype) | Timeline event tag. |
|
||||||
|
|
||||||
### `Mutation.timelineEventUpdate`
|
### `Mutation.timelineEventUpdate`
|
||||||
|
|
||||||
Input type: `TimelineEventUpdateInput`
|
Input type: `TimelineEventUpdateInput`
|
||||||
|
|
|
@ -46753,6 +46753,9 @@ msgstr ""
|
||||||
msgid "You have insufficient permissions to manage resource links for this incident"
|
msgid "You have insufficient permissions to manage resource links for this incident"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "You have insufficient permissions to manage timeline event tags for this project"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "You have insufficient permissions to manage timeline events for this incident"
|
msgid "You have insufficient permissions to manage timeline events for this incident"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
RSpec.describe Mutations::IncidentManagement::TimelineEventTag::Create do
|
||||||
|
let_it_be(:current_user) { create(:user) }
|
||||||
|
let_it_be_with_reload(:project) { create(:project) }
|
||||||
|
|
||||||
|
let(:args) { { name: 'Test tag 1' } }
|
||||||
|
|
||||||
|
before do
|
||||||
|
project.add_maintainer(current_user)
|
||||||
|
end
|
||||||
|
|
||||||
|
specify { expect(described_class).to require_graphql_authorizations(:admin_incident_management_timeline_event_tag) }
|
||||||
|
|
||||||
|
describe '#resolve' do
|
||||||
|
subject(:resolve) { mutation_for(project, current_user).resolve(project_path: project.full_path, **args) }
|
||||||
|
|
||||||
|
context 'when user has permission to create timeline event tag' do
|
||||||
|
it 'adds the tag to the project' do
|
||||||
|
expect { resolve }.to change(IncidentManagement::TimelineEventTag, :count).by(1)
|
||||||
|
expect(project.incident_management_timeline_event_tags.by_names(['Test tag 1']).pluck_names)
|
||||||
|
.to match_array(['Test tag 1'])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when TimelineEventTags::CreateService responds with an error' do
|
||||||
|
let(:args) { {} }
|
||||||
|
|
||||||
|
it 'returns errors' do
|
||||||
|
expect(resolve).to eq(timeline_event_tag: nil, errors: ["Name can't be blank and Name is invalid"])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when user has no permissions to create tags on a project' do
|
||||||
|
before do
|
||||||
|
project.add_developer(current_user)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'raises an error' do
|
||||||
|
expect { resolve }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def mutation_for(project, user)
|
||||||
|
described_class.new(object: project, context: { current_user: user }, field: nil)
|
||||||
|
end
|
||||||
|
end
|
|
@ -538,15 +538,32 @@ RSpec.describe ProjectPolicy do
|
||||||
it 'allows access to timeline event tags' 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(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(developer, project)).to be_allowed(:read_incident_management_timeline_event_tag)
|
||||||
|
expect(described_class.new(guest, 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)
|
expect(described_class.new(admin, project)).to be_allowed(:read_incident_management_timeline_event_tag)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'when user is a maintainer/owner' do
|
||||||
|
it 'allows to create timeline event tags' do
|
||||||
|
expect(described_class.new(maintainer, project)).to be_allowed(:admin_incident_management_timeline_event_tag)
|
||||||
|
expect(described_class.new(owner, project)).to be_allowed(:admin_incident_management_timeline_event_tag)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when user is a developer/guest/reporter' do
|
||||||
|
it 'disallows creation' do
|
||||||
|
expect(described_class.new(developer, project)).to be_disallowed(:admin_incident_management_timeline_event_tag)
|
||||||
|
expect(described_class.new(guest, project)).to be_disallowed(:admin_incident_management_timeline_event_tag)
|
||||||
|
expect(described_class.new(reporter, project)).to be_disallowed(:admin_incident_management_timeline_event_tag)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
context 'when user is not a member of the project' do
|
context 'when user is not a member of the project' do
|
||||||
let(:project) { private_project }
|
let(:project) { private_project }
|
||||||
|
|
||||||
it 'disallows access to the timeline event tags' do
|
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)
|
expect(described_class.new(non_member, project)).to be_disallowed(:read_incident_management_timeline_event_tag)
|
||||||
|
expect(described_class.new(non_member, project)).to be_disallowed(:admin_incident_management_timeline_event_tag)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
RSpec.describe 'Creating a timeline event tag' do
|
||||||
|
include GraphqlHelpers
|
||||||
|
|
||||||
|
let_it_be(:user) { create(:user) }
|
||||||
|
let_it_be(:project) { create(:project) }
|
||||||
|
let_it_be(:name) { 'Test tag 1' }
|
||||||
|
|
||||||
|
let(:input) { { project_path: project.full_path, name: name } }
|
||||||
|
let(:mutation) do
|
||||||
|
graphql_mutation(:timeline_event_tag_create, input) do
|
||||||
|
<<~QL
|
||||||
|
clientMutationId
|
||||||
|
errors
|
||||||
|
timelineEventTag {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
}
|
||||||
|
QL
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:mutation_response) { graphql_mutation_response(:timeline_event_tag_create) }
|
||||||
|
|
||||||
|
context 'when user has permissions to create timeline event tag' do
|
||||||
|
before do
|
||||||
|
project.add_maintainer(user)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'creates timeline event tag', :aggregate_failures do
|
||||||
|
post_graphql_mutation(mutation, current_user: user)
|
||||||
|
|
||||||
|
timeline_event_tag_response = mutation_response['timelineEventTag']
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(:success)
|
||||||
|
expect(timeline_event_tag_response).to include(
|
||||||
|
'name' => name
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when user does not have permissions to create timeline event tag' do
|
||||||
|
before do
|
||||||
|
project.add_developer(user)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'raises error' do
|
||||||
|
post_graphql_mutation(mutation, current_user: user)
|
||||||
|
|
||||||
|
expect(mutation_response).to be_nil
|
||||||
|
expect_graphql_errors_to_include(Gitlab::Graphql::Authorize::AuthorizeResource::RESOURCE_ACCESS_ERROR)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,71 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
RSpec.describe IncidentManagement::TimelineEventTags::CreateService do
|
||||||
|
let_it_be(:user_with_permissions) { create(:user) }
|
||||||
|
let_it_be(:user_without_permissions) { create(:user) }
|
||||||
|
let_it_be_with_reload(:project) { create(:project) }
|
||||||
|
|
||||||
|
let(:current_user) { user_with_permissions }
|
||||||
|
let(:args) { { 'name': 'Test tag 1', 'project_path': project.full_path } }
|
||||||
|
|
||||||
|
let(:service) { described_class.new(project, current_user, args) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
project.add_maintainer(user_with_permissions)
|
||||||
|
project.add_developer(user_without_permissions)
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#execute' do
|
||||||
|
shared_examples 'error response' do |message|
|
||||||
|
it 'has an informative message' do
|
||||||
|
expect(execute).to be_error
|
||||||
|
expect(execute.message).to eq(message)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
shared_examples 'success response' do
|
||||||
|
it 'has timeline event tag' do
|
||||||
|
expect(execute).to be_success
|
||||||
|
|
||||||
|
result = execute.payload[:timeline_event_tag]
|
||||||
|
expect(result).to be_a(::IncidentManagement::TimelineEventTag)
|
||||||
|
expect(result.name).to eq(args[:name])
|
||||||
|
expect(result.project).to eq(project)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
subject(:execute) { service.execute }
|
||||||
|
|
||||||
|
context 'when current user is nil' do
|
||||||
|
let(:current_user) { nil }
|
||||||
|
|
||||||
|
it_behaves_like 'error response',
|
||||||
|
'You have insufficient permissions to manage timeline event tags for this project'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when user does not have permissions to create tags' do
|
||||||
|
let(:current_user) { user_without_permissions }
|
||||||
|
|
||||||
|
it_behaves_like 'error response',
|
||||||
|
'You have insufficient permissions to manage timeline event tags for this project'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when error occurs during creation' do
|
||||||
|
let(:args) { {} }
|
||||||
|
|
||||||
|
it_behaves_like 'error response', "Name can't be blank and Name is invalid"
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when user has permissions' do
|
||||||
|
it_behaves_like 'success response'
|
||||||
|
|
||||||
|
it 'creates database record' do
|
||||||
|
expect { execute }.to change {
|
||||||
|
::IncidentManagement::TimelineEventTag.where(project_id: project.id).count
|
||||||
|
}.by(1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in New Issue