Commit b3d179e5 authored by Mayra Cabrera's avatar Mayra Cabrera

Merge branch '2256-add-customer-relations-contact-create-mutation' into 'master'

Add customer relations contact create mutation

See merge request gitlab-org/gitlab!70733
parents 8aeca539 78cd670b
# frozen_string_literal: true
module Mutations
module CustomerRelations
module Contacts
class Create < BaseMutation
include ResolvesIds
include Gitlab::Graphql::Authorize::AuthorizeResource
graphql_name 'CustomerRelationsContactCreate'
field :contact,
Types::CustomerRelations::ContactType,
null: true,
description: 'Contact after the mutation.'
argument :group_id, ::Types::GlobalIDType[::Group],
required: true,
description: 'Group for the contact.'
argument :organization_id, ::Types::GlobalIDType[::CustomerRelations::Organization],
required: false,
description: 'Organization for the contact.'
argument :first_name, GraphQL::Types::String,
required: true,
description: 'First name of the contact.'
argument :last_name, GraphQL::Types::String,
required: true,
description: 'Last name of the contact.'
argument :phone, GraphQL::Types::String,
required: false,
description: 'Phone number of the contact.'
argument :email, GraphQL::Types::String,
required: false,
description: 'Email address of the contact.'
argument :description, GraphQL::Types::String,
required: false,
description: 'Description or notes for the contact.'
authorize :admin_contact
def resolve(args)
group = authorized_find!(id: args[:group_id])
raise Gitlab::Graphql::Errors::ResourceNotAvailable, 'Feature disabled' unless Feature.enabled?(:customer_relations, group, default_enabled: :yaml)
set_organization!(args)
result = ::CustomerRelations::Contacts::CreateService.new(group: group, current_user: current_user, params: args).execute
{ contact: result.payload, errors: result.errors }
end
def find_object(id:)
GitlabSchema.object_from_id(id, expected_type: ::Group)
end
def set_organization!(args)
return unless args[:organization_id]
args[:organization_id] = resolve_ids(args[:organization_id], ::Types::GlobalIDType[::CustomerRelations::Organization])[0]
end
end
end
end
end
...@@ -38,6 +38,7 @@ module Types ...@@ -38,6 +38,7 @@ module Types
mount_mutation Mutations::Commits::Create, calls_gitaly: true mount_mutation Mutations::Commits::Create, calls_gitaly: true
mount_mutation Mutations::CustomEmoji::Create, feature_flag: :custom_emoji mount_mutation Mutations::CustomEmoji::Create, feature_flag: :custom_emoji
mount_mutation Mutations::CustomEmoji::Destroy, feature_flag: :custom_emoji mount_mutation Mutations::CustomEmoji::Destroy, feature_flag: :custom_emoji
mount_mutation Mutations::CustomerRelations::Contacts::Create
mount_mutation Mutations::CustomerRelations::Organizations::Create mount_mutation Mutations::CustomerRelations::Organizations::Create
mount_mutation Mutations::CustomerRelations::Organizations::Update mount_mutation Mutations::CustomerRelations::Organizations::Update
mount_mutation Mutations::Discussions::ToggleResolve mount_mutation Mutations::Discussions::ToggleResolve
......
...@@ -134,6 +134,8 @@ class GroupPolicy < BasePolicy ...@@ -134,6 +134,8 @@ class GroupPolicy < BasePolicy
enable :create_package enable :create_package
enable :create_package_settings enable :create_package_settings
enable :developer_access enable :developer_access
enable :admin_organization
enable :admin_contact
end end
rule { reporter }.policy do rule { reporter }.policy do
...@@ -147,7 +149,6 @@ class GroupPolicy < BasePolicy ...@@ -147,7 +149,6 @@ class GroupPolicy < BasePolicy
enable :read_prometheus enable :read_prometheus
enable :read_package enable :read_package
enable :read_package_settings enable :read_package_settings
enable :admin_organization
end end
rule { maintainer }.policy do rule { maintainer }.policy do
......
# frozen_string_literal: true
module CustomerRelations
module Contacts
class BaseService < ::BaseGroupService
private
def allowed?
current_user&.can?(:admin_contact, group)
end
def error(message)
ServiceResponse.error(message: Array(message))
end
end
end
end
# frozen_string_literal: true
module CustomerRelations
module Contacts
class CreateService < BaseService
def execute
return error_no_permissions unless allowed?
return error_organization_invalid unless organization_valid?
contact = Contact.create(params.merge(group_id: group.id))
return error_creating(contact) unless contact.persisted?
ServiceResponse.success(payload: contact)
end
private
def organization_valid?
return true unless params[:organization_id]
organization = Organization.find(params[:organization_id])
organization.group_id == group.id
rescue ActiveRecord::RecordNotFound
false
end
def error_organization_invalid
error('The specified organization was not found or does not belong to this group')
end
def error_no_permissions
error('You have insufficient permissions to create a contact for this group')
end
def error_creating(contact)
error(contact&.errors&.full_messages || 'Failed to create contact')
end
end
end
end
...@@ -10,7 +10,7 @@ module CustomerRelations ...@@ -10,7 +10,7 @@ module CustomerRelations
end end
def error(message) def error(message)
ServiceResponse.error(message: message) ServiceResponse.error(message: Array(message))
end end
end end
end end
......
...@@ -7,9 +7,7 @@ module CustomerRelations ...@@ -7,9 +7,7 @@ module CustomerRelations
def execute def execute
return error_no_permissions unless allowed? return error_no_permissions unless allowed?
params[:group_id] = group.id organization = Organization.create(params.merge(group_id: group.id))
organization = Organization.create(params)
return error_creating(organization) unless organization.persisted? return error_creating(organization) unless organization.persisted?
......
...@@ -1416,6 +1416,31 @@ Input type: `CreateTestCaseInput` ...@@ -1416,6 +1416,31 @@ Input type: `CreateTestCaseInput`
| <a id="mutationcreatetestcaseerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. | | <a id="mutationcreatetestcaseerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
| <a id="mutationcreatetestcasetestcase"></a>`testCase` | [`Issue`](#issue) | Test case created. | | <a id="mutationcreatetestcasetestcase"></a>`testCase` | [`Issue`](#issue) | Test case created. |
### `Mutation.customerRelationsContactCreate`
Input type: `CustomerRelationsContactCreateInput`
#### Arguments
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="mutationcustomerrelationscontactcreateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationcustomerrelationscontactcreatedescription"></a>`description` | [`String`](#string) | Description or notes for the contact. |
| <a id="mutationcustomerrelationscontactcreateemail"></a>`email` | [`String`](#string) | Email address of the contact. |
| <a id="mutationcustomerrelationscontactcreatefirstname"></a>`firstName` | [`String!`](#string) | First name of the contact. |
| <a id="mutationcustomerrelationscontactcreategroupid"></a>`groupId` | [`GroupID!`](#groupid) | Group for the contact. |
| <a id="mutationcustomerrelationscontactcreatelastname"></a>`lastName` | [`String!`](#string) | Last name of the contact. |
| <a id="mutationcustomerrelationscontactcreateorganizationid"></a>`organizationId` | [`CustomerRelationsOrganizationID`](#customerrelationsorganizationid) | Organization for the contact. |
| <a id="mutationcustomerrelationscontactcreatephone"></a>`phone` | [`String`](#string) | Phone number of the contact. |
#### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="mutationcustomerrelationscontactcreateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationcustomerrelationscontactcreatecontact"></a>`contact` | [`CustomerRelationsContact`](#customerrelationscontact) | Contact after the mutation. |
| <a id="mutationcustomerrelationscontactcreateerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
### `Mutation.customerRelationsOrganizationCreate` ### `Mutation.customerRelationsOrganizationCreate`
Input type: `CustomerRelationsOrganizationCreateInput` Input type: `CustomerRelationsOrganizationCreateInput`
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Mutations::CustomerRelations::Contacts::Create do
let_it_be(:user) { create(:user) }
let_it_be(:not_found_or_does_not_belong) { 'The specified organization was not found or does not belong to this group' }
let(:valid_params) do
attributes_for(:contact,
group: group,
description: 'Managing Director'
)
end
describe '#resolve' do
subject(:resolve_mutation) do
described_class.new(object: nil, context: { current_user: user }, field: nil).resolve(
**valid_params,
group_id: group.to_global_id
)
end
context 'when the user does not have permission' do
let_it_be(:group) { create(:group) }
before do
group.add_reporter(user)
end
it 'raises an error' do
expect { resolve_mutation }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
.with_message("The resource that you are attempting to access does not exist or you don't have permission to perform this action")
end
end
context 'when the user has permission' do
let_it_be(:group) { create(:group) }
before_all do
group.add_developer(user)
end
context 'when the feature is disabled' do
before do
stub_feature_flags(customer_relations: false)
end
it 'raises an error' do
expect { resolve_mutation }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
.with_message('Feature disabled')
end
end
context 'when the params are invalid' do
it 'returns the validation error' do
valid_params[:first_name] = nil
expect(resolve_mutation[:errors]).to match_array(["First name can't be blank"])
end
end
context 'when attaching to an organization' do
context 'when all ok' do
before do
organization = create(:organization, group: group)
valid_params[:organization_id] = organization.to_global_id
end
it 'creates contact with correct values' do
expect(resolve_mutation[:contact].organization).to be_present
end
end
context 'when organization_id is invalid' do
before do
valid_params[:organization_id] = "gid://gitlab/CustomerRelations::Organization/#{non_existing_record_id}"
end
it 'returns the relevant error' do
expect(resolve_mutation[:errors]).to match_array([not_found_or_does_not_belong])
end
end
context 'when organzation belongs to a different group' do
before do
organization = create(:organization)
valid_params[:organization_id] = organization.to_global_id
end
it 'returns the relevant error' do
expect(resolve_mutation[:errors]).to match_array([not_found_or_does_not_belong])
end
end
end
it 'creates contact with correct values' do
expect(resolve_mutation[:contact]).to have_attributes(valid_params)
end
end
end
specify { expect(described_class).to require_graphql_authorizations(:admin_contact) }
end
...@@ -26,11 +26,12 @@ RSpec.describe Mutations::CustomerRelations::Organizations::Create do ...@@ -26,11 +26,12 @@ RSpec.describe Mutations::CustomerRelations::Organizations::Create do
let_it_be(:group) { create(:group) } let_it_be(:group) { create(:group) }
before do before do
group.add_guest(user) group.add_reporter(user)
end end
it 'raises an error' do it 'raises an error' do
expect { resolve_mutation }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable) expect { resolve_mutation }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
.with_message("The resource that you are attempting to access does not exist or you don't have permission to perform this action")
end end
end end
...@@ -38,7 +39,7 @@ RSpec.describe Mutations::CustomerRelations::Organizations::Create do ...@@ -38,7 +39,7 @@ RSpec.describe Mutations::CustomerRelations::Organizations::Create do
let_it_be(:group) { create(:group) } let_it_be(:group) { create(:group) }
before_all do before_all do
group.add_reporter(user) group.add_developer(user)
end end
context 'when the feature is disabled' do context 'when the feature is disabled' do
...@@ -48,6 +49,7 @@ RSpec.describe Mutations::CustomerRelations::Organizations::Create do ...@@ -48,6 +49,7 @@ RSpec.describe Mutations::CustomerRelations::Organizations::Create do
it 'raises an error' do it 'raises an error' do
expect { resolve_mutation }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable) expect { resolve_mutation }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
.with_message('Feature disabled')
end end
end end
......
...@@ -7,6 +7,7 @@ RSpec.describe Mutations::CustomerRelations::Organizations::Update do ...@@ -7,6 +7,7 @@ RSpec.describe Mutations::CustomerRelations::Organizations::Update do
let_it_be(:name) { 'GitLab' } let_it_be(:name) { 'GitLab' }
let_it_be(:default_rate) { 1000.to_f } let_it_be(:default_rate) { 1000.to_f }
let_it_be(:description) { 'VIP' } let_it_be(:description) { 'VIP' }
let_it_be(:does_not_exist_or_no_permission) { "The resource that you are attempting to access does not exist or you don't have permission to perform this action" }
let(:organization) { create(:organization, group: group) } let(:organization) { create(:organization, group: group) }
let(:attributes) do let(:attributes) do
...@@ -29,11 +30,12 @@ RSpec.describe Mutations::CustomerRelations::Organizations::Update do ...@@ -29,11 +30,12 @@ RSpec.describe Mutations::CustomerRelations::Organizations::Update do
let_it_be(:group) { create(:group) } let_it_be(:group) { create(:group) }
before do before do
group.add_guest(user) group.add_reporter(user)
end end
it 'raises an error' do it 'raises an error' do
expect { resolve_mutation }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable) expect { resolve_mutation }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
.with_message(does_not_exist_or_no_permission)
end end
end end
...@@ -41,9 +43,10 @@ RSpec.describe Mutations::CustomerRelations::Organizations::Update do ...@@ -41,9 +43,10 @@ RSpec.describe Mutations::CustomerRelations::Organizations::Update do
let_it_be(:group) { create(:group) } let_it_be(:group) { create(:group) }
it 'raises an error' do it 'raises an error' do
attributes[:id] = 'gid://gitlab/CustomerRelations::Organization/999' attributes[:id] = "gid://gitlab/CustomerRelations::Organization/#{non_existing_record_id}"
expect { resolve_mutation }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable) expect { resolve_mutation }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
.with_message(does_not_exist_or_no_permission)
end end
end end
...@@ -51,7 +54,7 @@ RSpec.describe Mutations::CustomerRelations::Organizations::Update do ...@@ -51,7 +54,7 @@ RSpec.describe Mutations::CustomerRelations::Organizations::Update do
let_it_be(:group) { create(:group) } let_it_be(:group) { create(:group) }
before_all do before_all do
group.add_reporter(user) group.add_developer(user)
end end
it 'updates the organization with correct values' do it 'updates the organization with correct values' do
...@@ -65,6 +68,7 @@ RSpec.describe Mutations::CustomerRelations::Organizations::Update do ...@@ -65,6 +68,7 @@ RSpec.describe Mutations::CustomerRelations::Organizations::Update do
it 'raises an error' do it 'raises an error' do
expect { resolve_mutation }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable) expect { resolve_mutation }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
.with_message('Feature disabled')
end end
end end
end end
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe CustomerRelations::Contacts::CreateService do
describe '#execute' do
let_it_be(:user) { create(:user) }
let_it_be(:not_found_or_does_not_belong) { 'The specified organization was not found or does not belong to this group' }
let(:params) { attributes_for(:contact, group: group) }
subject(:response) { described_class.new(group: group, current_user: user, params: params).execute }
context 'when user does not have permission' do
let_it_be(:group) { create(:group) }
before_all do
group.add_reporter(user)
end
it 'returns an error' do
expect(response).to be_error
expect(response.message).to match_array(['You have insufficient permissions to create a contact for this group'])
end
end
context 'when user has permission' do
let_it_be(:group) { create(:group) }
before_all do
group.add_developer(user)
end
it 'creates a contact' do
expect(response).to be_success
end
it 'returns an error when the contact is not persisted' do
params[:last_name] = nil
expect(response).to be_error
expect(response.message).to match_array(["Last name can't be blank"])
end
it 'returns an error when the organization_id is invalid' do
params[:organization_id] = non_existing_record_id
expect(response).to be_error
expect(response.message).to match_array([not_found_or_does_not_belong])
end
it 'returns an error when the organization belongs to a different group' do
organization = create(:organization)
params[:organization_id] = organization.id
expect(response).to be_error
expect(response.message).to match_array([not_found_or_does_not_belong])
end
end
end
end
...@@ -12,22 +12,24 @@ RSpec.describe CustomerRelations::Organizations::CreateService do ...@@ -12,22 +12,24 @@ RSpec.describe CustomerRelations::Organizations::CreateService do
subject(:response) { described_class.new(group: group, current_user: user, params: params).execute } subject(:response) { described_class.new(group: group, current_user: user, params: params).execute }
it 'creates an organization' do it 'creates an organization' do
group.add_reporter(user) group.add_developer(user)
expect(response).to be_success expect(response).to be_success
end end
it 'returns an error when user does not have permission' do it 'returns an error when user does not have permission' do
group.add_reporter(user)
expect(response).to be_error expect(response).to be_error
expect(response.message).to eq('You have insufficient permissions to create an organization for this group') expect(response.message).to match_array(['You have insufficient permissions to create an organization for this group'])
end end
it 'returns an error when the organization is not persisted' do it 'returns an error when the organization is not persisted' do
group.add_reporter(user) group.add_developer(user)
params[:name] = nil params[:name] = nil
expect(response).to be_error expect(response).to be_error
expect(response.message).to eq(["Name can't be blank"]) expect(response.message).to match_array(["Name can't be blank"])
end end
end end
end end
...@@ -19,7 +19,7 @@ RSpec.describe CustomerRelations::Organizations::UpdateService do ...@@ -19,7 +19,7 @@ RSpec.describe CustomerRelations::Organizations::UpdateService do
response = update response = update
expect(response).to be_error expect(response).to be_error
expect(response.message).to eq('You have insufficient permissions to update an organization for this group') expect(response.message).to eq(['You have insufficient permissions to update an organization for this group'])
end end
end end
...@@ -27,7 +27,7 @@ RSpec.describe CustomerRelations::Organizations::UpdateService do ...@@ -27,7 +27,7 @@ RSpec.describe CustomerRelations::Organizations::UpdateService do
let_it_be(:group) { create(:group) } let_it_be(:group) { create(:group) }
before_all do before_all do
group.add_reporter(user) group.add_developer(user)
end end
context 'when name is changed' do context 'when name is changed' do
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment