From ff48c07735c9fc74a82a4be37f2466019719d67d Mon Sep 17 00:00:00 2001 From: Phil Hughes <me@iamphill.com> Date: Wed, 14 Feb 2024 10:42:21 +0000 Subject: [PATCH] Added group saved replies GraphQL mutations Adds mutations to create, update, and destroy group saved replies. https://gitlab.com/gitlab-org/gitlab/-/issues/440817 --- app/graphql/mutations/saved_replies/base.rb | 4 - app/graphql/mutations/saved_replies/create.rb | 11 +-- .../mutations/saved_replies/destroy.rb | 6 -- app/graphql/mutations/saved_replies/update.rb | 6 -- .../mutations/users/saved_replies/create.rb | 20 +++++ .../mutations/users/saved_replies/destroy.rb | 19 +++++ .../mutations/users/saved_replies/update.rb | 19 +++++ app/graphql/types/mutation_type.rb | 6 +- doc/api/graphql/reference/index.md | 73 +++++++++++++++++++ ee/app/graphql/ee/types/mutation_type.rb | 3 + .../mutations/groups/saved_replies/create.rb | 26 +++++++ .../mutations/groups/saved_replies/destroy.rb | 19 +++++ .../mutations/groups/saved_replies/update.rb | 19 +++++ ee/app/policies/ee/group_policy.rb | 3 + ee/spec/policies/group_policy_spec.rb | 12 +-- .../groups/saved_replies/create_spec.rb | 67 +++++++++++++++++ .../groups/saved_replies/destroy_spec.rb | 57 +++++++++++++++ .../groups/saved_replies/update_spec.rb | 60 +++++++++++++++ .../{ => users}/saved_replies/create_spec.rb | 12 +-- .../{ => users}/saved_replies/destroy_spec.rb | 6 +- .../{ => users}/saved_replies/update_spec.rb | 12 +-- 21 files changed, 411 insertions(+), 49 deletions(-) create mode 100644 app/graphql/mutations/users/saved_replies/create.rb create mode 100644 app/graphql/mutations/users/saved_replies/destroy.rb create mode 100644 app/graphql/mutations/users/saved_replies/update.rb create mode 100644 ee/app/graphql/mutations/groups/saved_replies/create.rb create mode 100644 ee/app/graphql/mutations/groups/saved_replies/destroy.rb create mode 100644 ee/app/graphql/mutations/groups/saved_replies/update.rb create mode 100644 ee/spec/requests/api/graphql/groups/saved_replies/create_spec.rb create mode 100644 ee/spec/requests/api/graphql/groups/saved_replies/destroy_spec.rb create mode 100644 ee/spec/requests/api/graphql/groups/saved_replies/update_spec.rb rename spec/graphql/mutations/{ => users}/saved_replies/create_spec.rb (64%) rename spec/graphql/mutations/{ => users}/saved_replies/destroy_spec.rb (76%) rename spec/graphql/mutations/{ => users}/saved_replies/update_spec.rb (67%) diff --git a/app/graphql/mutations/saved_replies/base.rb b/app/graphql/mutations/saved_replies/base.rb index 6fd9e03171c7..6320d242beb2 100644 --- a/app/graphql/mutations/saved_replies/base.rb +++ b/app/graphql/mutations/saved_replies/base.rb @@ -3,10 +3,6 @@ module Mutations module SavedReplies class Base < BaseMutation - field :saved_reply, ::Types::Users::SavedReplyType, - null: true, - description: 'Saved reply after mutation.' - private def present_result(result) diff --git a/app/graphql/mutations/saved_replies/create.rb b/app/graphql/mutations/saved_replies/create.rb index 1e71688af3fe..ad9815e8f0bc 100644 --- a/app/graphql/mutations/saved_replies/create.rb +++ b/app/graphql/mutations/saved_replies/create.rb @@ -3,22 +3,15 @@ module Mutations module SavedReplies class Create < Base - graphql_name 'SavedReplyCreate' - authorize :create_saved_replies argument :name, GraphQL::Types::String, required: true, - description: copy_field_description(Types::SavedReplyType, :name) + description: copy_field_description(::Types::SavedReplyType, :name) argument :content, GraphQL::Types::String, required: true, - description: copy_field_description(Types::SavedReplyType, :content) - - def resolve(name:, content:) - result = ::SavedReplies::CreateService.new(object: current_user, name: name, content: content).execute - present_result(result) - end + description: copy_field_description(::Types::SavedReplyType, :content) end end end diff --git a/app/graphql/mutations/saved_replies/destroy.rb b/app/graphql/mutations/saved_replies/destroy.rb index b67bd11e0cbb..c9aded6fbe13 100644 --- a/app/graphql/mutations/saved_replies/destroy.rb +++ b/app/graphql/mutations/saved_replies/destroy.rb @@ -3,14 +3,8 @@ module Mutations module SavedReplies class Destroy < Base - graphql_name 'SavedReplyDestroy' - authorize :destroy_saved_replies - argument :id, Types::GlobalIDType[::Users::SavedReply], - required: true, - description: copy_field_description(::Types::Users::SavedReplyType, :id) - def resolve(id:) saved_reply = authorized_find!(id: id) result = ::SavedReplies::DestroyService.new(saved_reply: saved_reply).execute diff --git a/app/graphql/mutations/saved_replies/update.rb b/app/graphql/mutations/saved_replies/update.rb index 6acbdd8d770a..a58633c178bf 100644 --- a/app/graphql/mutations/saved_replies/update.rb +++ b/app/graphql/mutations/saved_replies/update.rb @@ -3,14 +3,8 @@ module Mutations module SavedReplies class Update < Base - graphql_name 'SavedReplyUpdate' - authorize :update_saved_replies - argument :id, Types::GlobalIDType[::Users::SavedReply], - required: true, - description: copy_field_description(::Types::Users::SavedReplyType, :id) - argument :name, GraphQL::Types::String, required: true, description: copy_field_description(::Types::SavedReplyType, :name) diff --git a/app/graphql/mutations/users/saved_replies/create.rb b/app/graphql/mutations/users/saved_replies/create.rb new file mode 100644 index 000000000000..67b7733a750c --- /dev/null +++ b/app/graphql/mutations/users/saved_replies/create.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +module Mutations + module Users + module SavedReplies + class Create < ::Mutations::SavedReplies::Create + graphql_name 'SavedReplyCreate' + + field :saved_reply, ::Types::Users::SavedReplyType, + null: true, + description: 'Saved reply after mutation.' + + def resolve(name:, content:) + result = ::SavedReplies::CreateService.new(object: current_user, name: name, content: content).execute + present_result(result) + end + end + end + end +end diff --git a/app/graphql/mutations/users/saved_replies/destroy.rb b/app/graphql/mutations/users/saved_replies/destroy.rb new file mode 100644 index 000000000000..35693f24cc03 --- /dev/null +++ b/app/graphql/mutations/users/saved_replies/destroy.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module Mutations + module Users + module SavedReplies + class Destroy < ::Mutations::SavedReplies::Destroy + graphql_name 'SavedReplyDestroy' + + field :saved_reply, ::Types::Users::SavedReplyType, + null: true, + description: 'Saved reply after mutation.' + + argument :id, Types::GlobalIDType[::Users::SavedReply], + required: true, + description: copy_field_description(::Types::Users::SavedReplyType, :id) + end + end + end +end diff --git a/app/graphql/mutations/users/saved_replies/update.rb b/app/graphql/mutations/users/saved_replies/update.rb new file mode 100644 index 000000000000..65a1777afd48 --- /dev/null +++ b/app/graphql/mutations/users/saved_replies/update.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module Mutations + module Users + module SavedReplies + class Update < ::Mutations::SavedReplies::Update + graphql_name 'SavedReplyUpdate' + + field :saved_reply, ::Types::Users::SavedReplyType, + null: true, + description: 'Saved reply after mutation.' + + argument :id, Types::GlobalIDType[::Users::SavedReply], + required: true, + description: copy_field_description(::Types::Users::SavedReplyType, :id) + end + end + end +end diff --git a/app/graphql/types/mutation_type.rb b/app/graphql/types/mutation_type.rb index ab4e78435f19..21e9c76249b8 100644 --- a/app/graphql/types/mutation_type.rb +++ b/app/graphql/types/mutation_type.rb @@ -197,10 +197,10 @@ class MutationType < BaseObject mount_mutation Mutations::WorkItems::Convert, alpha: { milestone: '15.11' } mount_mutation Mutations::WorkItems::LinkedItems::Add, alpha: { milestone: '16.3' } mount_mutation Mutations::WorkItems::LinkedItems::Remove, alpha: { milestone: '16.3' } - mount_mutation Mutations::SavedReplies::Create - mount_mutation Mutations::SavedReplies::Update + mount_mutation Mutations::Users::SavedReplies::Create + mount_mutation Mutations::Users::SavedReplies::Update + mount_mutation Mutations::Users::SavedReplies::Destroy mount_mutation Mutations::Pages::MarkOnboardingComplete - mount_mutation Mutations::SavedReplies::Destroy mount_mutation Mutations::Uploads::Delete mount_mutation Mutations::Users::SetNamespaceCommitEmail mount_mutation Mutations::WorkItems::Subscribe, alpha: { milestone: '16.3' } diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index b4c29f41922d..b1a1e88e15ab 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -4565,6 +4565,79 @@ Input type: `GroupMemberBulkUpdateInput` | <a id="mutationgroupmemberbulkupdateerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. | | <a id="mutationgroupmemberbulkupdategroupmembers"></a>`groupMembers` | [`[GroupMember!]`](#groupmember) | Group members after mutation. | +### `Mutation.groupSavedReplyCreate` + +NOTE: +**Introduced** in 16.10. +**Status**: Experiment. + +Input type: `GroupSavedReplyCreateInput` + +#### Arguments + +| Name | Type | Description | +| ---- | ---- | ----------- | +| <a id="mutationgroupsavedreplycreateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. | +| <a id="mutationgroupsavedreplycreatecontent"></a>`content` | [`String!`](#string) | Content of the saved reply. | +| <a id="mutationgroupsavedreplycreategroupid"></a>`groupId` | [`GroupID!`](#groupid) | Group for the save reply. | +| <a id="mutationgroupsavedreplycreatename"></a>`name` | [`String!`](#string) | Name of the saved reply. | + +#### Fields + +| Name | Type | Description | +| ---- | ---- | ----------- | +| <a id="mutationgroupsavedreplycreateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. | +| <a id="mutationgroupsavedreplycreateerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. | +| <a id="mutationgroupsavedreplycreatesavedreply"></a>`savedReply` | [`GroupSavedReply`](#groupsavedreply) | Saved reply after mutation. | + +### `Mutation.groupSavedReplyDestroy` + +NOTE: +**Introduced** in 16.10. +**Status**: Experiment. + +Input type: `GroupSavedReplyDestroyInput` + +#### Arguments + +| Name | Type | Description | +| ---- | ---- | ----------- | +| <a id="mutationgroupsavedreplydestroyclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. | +| <a id="mutationgroupsavedreplydestroyid"></a>`id` | [`GroupsSavedReplyID!`](#groupssavedreplyid) | Global ID of the group-level saved reply. | + +#### Fields + +| Name | Type | Description | +| ---- | ---- | ----------- | +| <a id="mutationgroupsavedreplydestroyclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. | +| <a id="mutationgroupsavedreplydestroyerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. | +| <a id="mutationgroupsavedreplydestroysavedreply"></a>`savedReply` | [`GroupSavedReply`](#groupsavedreply) | Saved reply after mutation. | + +### `Mutation.groupSavedReplyUpdate` + +NOTE: +**Introduced** in 16.10. +**Status**: Experiment. + +Input type: `GroupSavedReplyUpdateInput` + +#### Arguments + +| Name | Type | Description | +| ---- | ---- | ----------- | +| <a id="mutationgroupsavedreplyupdateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. | +| <a id="mutationgroupsavedreplyupdatecontent"></a>`content` | [`String!`](#string) | Content of the saved reply. | +| <a id="mutationgroupsavedreplyupdateid"></a>`id` | [`GroupsSavedReplyID!`](#groupssavedreplyid) | Global ID of the group-level saved reply. | +| <a id="mutationgroupsavedreplyupdatename"></a>`name` | [`String!`](#string) | Name of the saved reply. | + +#### Fields + +| Name | Type | Description | +| ---- | ---- | ----------- | +| <a id="mutationgroupsavedreplyupdateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. | +| <a id="mutationgroupsavedreplyupdateerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. | +| <a id="mutationgroupsavedreplyupdatesavedreply"></a>`savedReply` | [`GroupSavedReply`](#groupsavedreply) | Saved reply after mutation. | + ### `Mutation.groupUpdate` Input type: `GroupUpdateInput` diff --git a/ee/app/graphql/ee/types/mutation_type.rb b/ee/app/graphql/ee/types/mutation_type.rb index 1b666c3d6280..f48f49d78989 100644 --- a/ee/app/graphql/ee/types/mutation_type.rb +++ b/ee/app/graphql/ee/types/mutation_type.rb @@ -153,6 +153,9 @@ module MutationType mount_mutation ::Mutations::AuditEvents::Streaming::HTTP::NamespaceFilters::Delete mount_mutation ::Mutations::Ai::Agents::Create, alpha: { milestone: '16.8' } mount_mutation ::Mutations::ComplianceManagement::Standards::RefreshAdherenceChecks + mount_mutation ::Mutations::Groups::SavedReplies::Create, alpha: { milestone: '16.10' } + mount_mutation ::Mutations::Groups::SavedReplies::Update, alpha: { milestone: '16.10' } + mount_mutation ::Mutations::Groups::SavedReplies::Destroy, alpha: { milestone: '16.10' } prepend(Types::DeprecatedMutations) end diff --git a/ee/app/graphql/mutations/groups/saved_replies/create.rb b/ee/app/graphql/mutations/groups/saved_replies/create.rb new file mode 100644 index 000000000000..c745041fc3a9 --- /dev/null +++ b/ee/app/graphql/mutations/groups/saved_replies/create.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +module Mutations + module Groups + module SavedReplies + class Create < ::Mutations::SavedReplies::Create + graphql_name 'GroupSavedReplyCreate' + + field :saved_reply, ::Types::Groups::SavedReplyType, + null: true, + description: 'Saved reply after mutation.' + + argument :group_id, ::Types::GlobalIDType[::Group], + required: true, + description: 'Group for the save reply.' + + def resolve(group_id:, name:, content:) + group = authorized_find!(id: group_id) + + result = ::SavedReplies::CreateService.new(object: group, name: name, content: content).execute + present_result(result) + end + end + end + end +end diff --git a/ee/app/graphql/mutations/groups/saved_replies/destroy.rb b/ee/app/graphql/mutations/groups/saved_replies/destroy.rb new file mode 100644 index 000000000000..950466f19d7f --- /dev/null +++ b/ee/app/graphql/mutations/groups/saved_replies/destroy.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module Mutations + module Groups + module SavedReplies + class Destroy < ::Mutations::SavedReplies::Destroy + graphql_name 'GroupSavedReplyDestroy' + + field :saved_reply, ::Types::Groups::SavedReplyType, + null: true, + description: 'Saved reply after mutation.' + + argument :id, Types::GlobalIDType[::Groups::SavedReply], + required: true, + description: copy_field_description(::Types::Groups::SavedReplyType, :id) + end + end + end +end diff --git a/ee/app/graphql/mutations/groups/saved_replies/update.rb b/ee/app/graphql/mutations/groups/saved_replies/update.rb new file mode 100644 index 000000000000..7ab10d55a131 --- /dev/null +++ b/ee/app/graphql/mutations/groups/saved_replies/update.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module Mutations + module Groups + module SavedReplies + class Update < ::Mutations::SavedReplies::Update + graphql_name 'GroupSavedReplyUpdate' + + field :saved_reply, ::Types::Groups::SavedReplyType, + null: true, + description: 'Saved reply after mutation.' + + argument :id, Types::GlobalIDType[::Groups::SavedReply], + required: true, + description: copy_field_description(::Types::Groups::SavedReplyType, :id) + end + end + end +end diff --git a/ee/app/policies/ee/group_policy.rb b/ee/app/policies/ee/group_policy.rb index 992154ec5acb..010ec03a4d4e 100644 --- a/ee/app/policies/ee/group_policy.rb +++ b/ee/app/policies/ee/group_policy.rb @@ -695,6 +695,9 @@ module GroupPolicy rule { supports_saved_replies & developer }.policy do enable :read_saved_replies + enable :create_saved_replies + enable :destroy_saved_replies + enable :update_saved_replies end end diff --git a/ee/spec/policies/group_policy_spec.rb b/ee/spec/policies/group_policy_spec.rb index 3b9cfe59ffa3..a13ed1bfc2b2 100644 --- a/ee/spec/policies/group_policy_spec.rb +++ b/ee/spec/policies/group_policy_spec.rb @@ -3300,7 +3300,7 @@ def create_member_role(member, abilities = member_role_abilities) end end - context 'for :read_saved_replies' do + context 'saved replies permissions' do let(:current_user) { owner } context 'when group_saved_replies_flag feature flag is disabled' do @@ -3308,7 +3308,7 @@ def create_member_role(member, abilities = member_role_abilities) stub_feature_flags(group_saved_replies_flag: false) end - it { is_expected.to be_disallowed(:read_saved_replies) } + it { is_expected.to be_disallowed(:read_saved_replies, :create_saved_replies, :update_saved_replies, :destroy_saved_replies) } end context 'when no license is present' do @@ -3316,7 +3316,7 @@ def create_member_role(member, abilities = member_role_abilities) stub_licensed_features(group_saved_replies: false) end - it { is_expected.to be_disallowed(:read_saved_replies) } + it { is_expected.to be_disallowed(:read_saved_replies, :create_saved_replies, :update_saved_replies, :destroy_saved_replies) } end context 'with correct license' do @@ -3324,18 +3324,18 @@ def create_member_role(member, abilities = member_role_abilities) stub_licensed_features(group_saved_replies: true) end - it { is_expected.to be_allowed(:read_saved_replies) } + it { is_expected.to be_allowed(:read_saved_replies, :create_saved_replies, :update_saved_replies, :destroy_saved_replies) } context 'when the user is a developer' do let(:current_user) { developer } - it { is_expected.to be_allowed(:read_saved_replies) } + it { is_expected.to be_allowed(:read_saved_replies, :create_saved_replies, :update_saved_replies, :destroy_saved_replies) } end context 'when the user is a guest member of the group' do let(:current_user) { guest } - it { is_expected.to be_disallowed(:read_saved_replies) } + it { is_expected.to be_disallowed(:read_saved_replies, :create_saved_replies, :update_saved_replies, :destroy_saved_replies) } end end end diff --git a/ee/spec/requests/api/graphql/groups/saved_replies/create_spec.rb b/ee/spec/requests/api/graphql/groups/saved_replies/create_spec.rb new file mode 100644 index 000000000000..d4ebb7061385 --- /dev/null +++ b/ee/spec/requests/api/graphql/groups/saved_replies/create_spec.rb @@ -0,0 +1,67 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Create group saved reply', feature_category: :code_review_workflow do + include GraphqlHelpers + + let_it_be(:group) { create(:group) } + let_it_be(:current_user) { create(:user) } + + let(:input) { { group_id: group.to_global_id, name: 'Test name', content: 'Test content' } } + + let(:mutation) { graphql_mutation(:group_saved_reply_create, input) } + let(:mutation_response) { graphql_mutation_response(:group_saved_reply_create) } + + before_all do + group.add_maintainer(current_user) + end + + context 'with group_saved_replies_flag disabled' do + before do + stub_feature_flags(group_saved_replies_flag: false) + stub_licensed_features(group_saved_replies: true) + end + + it 'returns null' do + post_graphql_mutation(mutation, current_user: current_user) + + expect(mutation_response).to be_nil + end + end + + context 'when license is invalid' do + before do + stub_licensed_features(group_saved_replies: false) + end + + it 'returns null' do + post_graphql_mutation(mutation, current_user: current_user) + + expect(mutation_response).to be_nil + end + end + + context 'when license is valid' do + before do + stub_licensed_features(group_saved_replies: true) + end + + it 'creates a saved reply' do + expect do + post_graphql_mutation(mutation, current_user: current_user) + + expect(mutation_response['savedReply']).to include( + 'name' => 'Test name', + 'content' => 'Test content' + ) + end.to change { ::Groups::SavedReply.count }.by(1) + end + + context 'when saved reply exists' do + let_it_be(:saved_reply) { create(:group_saved_reply, group: group, name: 'Test name') } + + it_behaves_like 'a mutation that returns errors in the response', errors: ["Name has already been taken"] + end + end +end diff --git a/ee/spec/requests/api/graphql/groups/saved_replies/destroy_spec.rb b/ee/spec/requests/api/graphql/groups/saved_replies/destroy_spec.rb new file mode 100644 index 000000000000..05574765d590 --- /dev/null +++ b/ee/spec/requests/api/graphql/groups/saved_replies/destroy_spec.rb @@ -0,0 +1,57 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Destroy group saved reply', feature_category: :code_review_workflow do + include GraphqlHelpers + + let_it_be(:group) { create(:group) } + let_it_be(:current_user) { create(:user) } + let_it_be(:saved_reply) { create(:group_saved_reply, group: group) } + + let(:input) { { id: saved_reply.to_global_id } } + + let(:mutation) { graphql_mutation(:group_saved_reply_destroy, input) } + let(:mutation_response) { graphql_mutation_response(:group_saved_reply_destroy) } + + before_all do + group.add_maintainer(current_user) + end + + context 'with group_saved_replies_flag disabled' do + before do + stub_feature_flags(group_saved_replies_flag: false) + stub_licensed_features(group_saved_replies: true) + end + + it 'returns null' do + post_graphql_mutation(mutation, current_user: current_user) + + expect(mutation_response).to be_nil + end + end + + context 'when license is invalid' do + before do + stub_licensed_features(group_saved_replies: false) + end + + it 'returns null' do + post_graphql_mutation(mutation, current_user: current_user) + + expect(mutation_response).to be_nil + end + end + + context 'when license is valid' do + before do + stub_licensed_features(group_saved_replies: true) + end + + it 'deletes the saved reply' do + expect do + post_graphql_mutation(mutation, current_user: current_user) + end.to change { ::Groups::SavedReply.count }.by(-1) + end + end +end diff --git a/ee/spec/requests/api/graphql/groups/saved_replies/update_spec.rb b/ee/spec/requests/api/graphql/groups/saved_replies/update_spec.rb new file mode 100644 index 000000000000..77da54ff6dec --- /dev/null +++ b/ee/spec/requests/api/graphql/groups/saved_replies/update_spec.rb @@ -0,0 +1,60 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Update group saved reply', feature_category: :code_review_workflow do + include GraphqlHelpers + + let_it_be(:group) { create(:group) } + let_it_be(:current_user) { create(:user) } + let_it_be(:saved_reply) { create(:group_saved_reply, group: group, name: 'Old name', content: 'Old content') } + + let(:input) { { id: saved_reply.to_global_id, name: 'New name', content: 'New content' } } + + let(:mutation) { graphql_mutation(:group_saved_reply_update, input) } + let(:mutation_response) { graphql_mutation_response(:group_saved_reply_update) } + + before_all do + group.add_maintainer(current_user) + end + + context 'with group_saved_replies_flag disabled' do + before do + stub_feature_flags(group_saved_replies_flag: false) + stub_licensed_features(group_saved_replies: true) + end + + it 'returns null' do + post_graphql_mutation(mutation, current_user: current_user) + + expect(mutation_response).to be_nil + end + end + + context 'when license is invalid' do + before do + stub_licensed_features(group_saved_replies: false) + end + + it 'returns null' do + post_graphql_mutation(mutation, current_user: current_user) + + expect(mutation_response).to be_nil + end + end + + context 'when license is valid' do + before do + stub_licensed_features(group_saved_replies: true) + end + + it 'updates the saved reply' do + post_graphql_mutation(mutation, current_user: current_user) + + expect(mutation_response['savedReply']).to include( + 'name' => 'New name', + 'content' => 'New content' + ) + end + end +end diff --git a/spec/graphql/mutations/saved_replies/create_spec.rb b/spec/graphql/mutations/users/saved_replies/create_spec.rb similarity index 64% rename from spec/graphql/mutations/saved_replies/create_spec.rb rename to spec/graphql/mutations/users/saved_replies/create_spec.rb index 9db23ab5345f..a2530e665318 100644 --- a/spec/graphql/mutations/saved_replies/create_spec.rb +++ b/spec/graphql/mutations/users/saved_replies/create_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Mutations::SavedReplies::Create do +RSpec.describe Mutations::Users::SavedReplies::Create, feature_category: :code_review_workflow do let_it_be(:current_user) { create(:user) } let(:mutation) { described_class.new(object: nil, context: { current_user: current_user }, field: nil) } @@ -17,14 +17,14 @@ context 'when service fails to create a new saved reply' do let(:mutation_arguments) { { name: '', content: '' } } - it { expect(subject[:saved_reply]).to be_nil } - it { expect(subject[:errors]).to match_array(["Content can't be blank", "Name can't be blank"]) } + it { expect(resolve[:saved_reply]).to be_nil } + it { expect(resolve[:errors]).to match_array(["Content can't be blank", "Name can't be blank"]) } end context 'when service successfully creates a new saved reply' do - it { expect(subject[:saved_reply].name).to eq(mutation_arguments[:name]) } - it { expect(subject[:saved_reply].content).to eq(mutation_arguments[:content]) } - it { expect(subject[:errors]).to be_empty } + it { expect(resolve[:saved_reply].name).to eq(mutation_arguments[:name]) } + it { expect(resolve[:saved_reply].content).to eq(mutation_arguments[:content]) } + it { expect(resolve[:errors]).to be_empty } end end end diff --git a/spec/graphql/mutations/saved_replies/destroy_spec.rb b/spec/graphql/mutations/users/saved_replies/destroy_spec.rb similarity index 76% rename from spec/graphql/mutations/saved_replies/destroy_spec.rb rename to spec/graphql/mutations/users/saved_replies/destroy_spec.rb index 84efd9ee0b8c..dad237d1e2bc 100644 --- a/spec/graphql/mutations/saved_replies/destroy_spec.rb +++ b/spec/graphql/mutations/users/saved_replies/destroy_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Mutations::SavedReplies::Destroy do +RSpec.describe Mutations::Users::SavedReplies::Destroy, feature_category: :code_review_workflow do let_it_be(:current_user) { create(:user) } let_it_be(:saved_reply) { create(:saved_reply, user: current_user) } @@ -19,12 +19,12 @@ end it 'raises Gitlab::Graphql::Errors::ResourceNotAvailable' do - expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable) + expect { resolve }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable) end end context 'when service successfully deletes the saved reply' do - it { expect(subject[:errors]).to be_empty } + it { expect(resolve[:errors]).to be_empty } end end end diff --git a/spec/graphql/mutations/saved_replies/update_spec.rb b/spec/graphql/mutations/users/saved_replies/update_spec.rb similarity index 67% rename from spec/graphql/mutations/saved_replies/update_spec.rb rename to spec/graphql/mutations/users/saved_replies/update_spec.rb index cc358d946a5f..4b5b9fef46b0 100644 --- a/spec/graphql/mutations/saved_replies/update_spec.rb +++ b/spec/graphql/mutations/users/saved_replies/update_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Mutations::SavedReplies::Update do +RSpec.describe Mutations::Users::SavedReplies::Update, feature_category: :code_review_workflow do let_it_be(:current_user) { create(:user) } let_it_be(:saved_reply) { create(:saved_reply, user: current_user) } @@ -18,14 +18,14 @@ context 'when service fails to update a new saved reply' do let(:mutation_arguments) { { name: '', content: '' } } - it { expect(subject[:saved_reply]).to be_nil } - it { expect(subject[:errors]).to match_array(["Content can't be blank", "Name can't be blank"]) } + it { expect(resolve[:saved_reply]).to be_nil } + it { expect(resolve[:errors]).to match_array(["Content can't be blank", "Name can't be blank"]) } end context 'when service successfully updates the saved reply' do - it { expect(subject[:saved_reply].name).to eq(mutation_arguments[:name]) } - it { expect(subject[:saved_reply].content).to eq(mutation_arguments[:content]) } - it { expect(subject[:errors]).to be_empty } + it { expect(resolve[:saved_reply].name).to eq(mutation_arguments[:name]) } + it { expect(resolve[:saved_reply].content).to eq(mutation_arguments[:content]) } + it { expect(resolve[:errors]).to be_empty } end end end -- GitLab