Commit 4f12cfcd authored by Andy Soiron's avatar Andy Soiron

Merge branch '352938-add-graphql-to-update-saved-reply' into 'master'

Add GraphQL API to Update Saved Reply

See merge request gitlab-org/gitlab!82642
parents 5af41694 73202f58
......@@ -26,6 +26,14 @@ module Mutations
def feature_enabled?
Feature.enabled?(:saved_replies, current_user, default_enabled: :yaml)
end
def find_object(id)
# TODO: remove this line when the compatibility layer is removed
# See: https://gitlab.com/gitlab-org/gitlab/-/issues/257883
id = ::Types::GlobalIDType[::Users::SavedReply].coerce_isolated_input(id)
GitlabSchema.find_by_gid(id)
end
end
end
end
# frozen_string_literal: true
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::SavedReplyType, :id)
argument :name, GraphQL::Types::String,
required: true,
description: copy_field_description(Types::SavedReplyType, :name)
argument :content, GraphQL::Types::String,
required: true,
description: copy_field_description(Types::SavedReplyType, :content)
def resolve(id:, name:, content:)
raise Gitlab::Graphql::Errors::ResourceNotAvailable, 'Feature disabled' unless feature_enabled?
saved_reply = authorized_find!(id)
result = ::Users::SavedReplies::UpdateService.new(current_user: current_user, saved_reply: saved_reply, name: name, content: content).execute
present_result(result)
end
end
end
end
......@@ -130,6 +130,7 @@ module Types
mount_mutation Mutations::WorkItems::Delete
mount_mutation Mutations::WorkItems::Update
mount_mutation Mutations::SavedReplies::Create
mount_mutation Mutations::SavedReplies::Update
end
end
......
......@@ -24,6 +24,7 @@ class UserPolicy < BasePolicy
enable :update_user
enable :update_user_status
enable :create_saved_replies
enable :update_saved_replies
enable :read_user_personal_access_tokens
enable :read_group_count
enable :read_user_groups
......
# frozen_string_literal: true
module Users
module SavedReplies
class UpdateService
def initialize(current_user:, saved_reply:, name:, content:)
@current_user = current_user
@saved_reply = saved_reply
@name = name
@content = content
end
def execute
if saved_reply.update(name: name, content: content)
ServiceResponse.success(payload: { saved_reply: saved_reply.reset })
else
ServiceResponse.error(message: saved_reply.errors.full_messages)
end
end
private
attr_reader :current_user, :saved_reply, :name, :content
end
end
end
......@@ -4263,6 +4263,27 @@ Input type: `SavedReplyCreateInput`
| <a id="mutationsavedreplycreateerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
| <a id="mutationsavedreplycreatesavedreply"></a>`savedReply` | [`SavedReply`](#savedreply) | Updated saved reply. |
### `Mutation.savedReplyUpdate`
Input type: `SavedReplyUpdateInput`
#### Arguments
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="mutationsavedreplyupdateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationsavedreplyupdatecontent"></a>`content` | [`String!`](#string) | Content of the saved reply. |
| <a id="mutationsavedreplyupdateid"></a>`id` | [`UsersSavedReplyID!`](#userssavedreplyid) | Global ID of the saved reply. |
| <a id="mutationsavedreplyupdatename"></a>`name` | [`String!`](#string) | Name of the saved reply. |
#### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="mutationsavedreplyupdateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationsavedreplyupdateerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
| <a id="mutationsavedreplyupdatesavedreply"></a>`savedReply` | [`SavedReply`](#savedreply) | Updated saved reply. |
### `Mutation.scanExecutionPolicyCommit`
Commits the `policy_yaml` content to the assigned security policy project for the given project(`project_path`).
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Mutations::SavedReplies::Update do
let_it_be(:current_user) { create(:user) }
let_it_be(:saved_reply) { create(:saved_reply, user: current_user) }
let(:mutation) { described_class.new(object: nil, context: { current_user: current_user }, field: nil) }
let(:mutation_arguments) { { name: 'save_reply_name', content: 'Save Reply Content' } }
describe '#resolve' do
subject(:resolve) do
mutation.resolve(id: saved_reply.to_global_id, **mutation_arguments)
end
context 'when feature is disabled' do
before do
stub_feature_flags(saved_replies: false)
end
it 'raises Gitlab::Graphql::Errors::ResourceNotAvailable' do
expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable, 'Feature disabled')
end
end
context 'when feature is enabled for current user' do
before do
stub_feature_flags(saved_replies: current_user)
end
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", "Name can contain only lowercase letters, digits, '_' and '-'. Must start with a letter, and cannot end with '-' or '_'"]) }
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 }
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Users::SavedReplies::UpdateService do
describe '#execute' do
let_it_be(:current_user) { create(:user) }
let_it_be(:saved_reply) { create(:saved_reply, user: current_user) }
let_it_be(:other_saved_reply) { create(:saved_reply, user: current_user) }
let_it_be(:saved_reply_from_other_user) { create(:saved_reply) }
subject { described_class.new(current_user: current_user, saved_reply: saved_reply, name: name, content: content).execute }
context 'when update fails' do
let(:name) { other_saved_reply.name }
let(:content) { '' }
it { is_expected.not_to be_success }
it 'returns error messages' do
expect(subject.errors).to match_array(["Content can't be blank", "Name has already been taken"])
end
end
context 'when update succeeds' do
let(:name) { saved_reply_from_other_user.name }
let(:content) { 'New content for Saved Reply' }
it { is_expected.to be_success }
it 'updates new Saved Reply in database' do
expect { subject }.not_to change(::Users::SavedReply, :count)
end
it 'returns saved reply' do
expect(subject[:saved_reply]).to eq(saved_reply)
end
end
end
end
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