Skip to content
代码片段 群组 项目
提交 2e1b4b89 编辑于 作者: Eugenia Grieff's avatar Eugenia Grieff 提交者: Alexandru Croitor
浏览文件

Move description widget update to service layer

- Add services for description widget
上级 1ab5b7f8
No related branches found
No related tags found
无相关合并请求
显示
190 个添加40 个删除
......@@ -15,6 +15,9 @@ module UpdateArguments
argument :title, GraphQL::Types::String,
required: false,
description: copy_field_description(Types::WorkItemType, :title)
argument :description_widget, ::Types::WorkItems::Widgets::DescriptionInputType,
required: false,
description: 'Input for description widget.'
end
end
end
......
......@@ -24,11 +24,13 @@ def resolve(id:, **attributes)
end
spam_params = ::Spam::SpamParams.new_from_request(request: context[:request])
widget_params = extract_widget_params(work_item, attributes)
::WorkItems::UpdateService.new(
project: work_item.project,
current_user: current_user,
params: attributes,
widget_params: widget_params,
spam_params: spam_params
).execute(work_item)
......@@ -45,6 +47,16 @@ def resolve(id:, **attributes)
def find_object(id:)
GitlabSchema.find_by_gid(id)
end
def extract_widget_params(work_item, attributes)
# Get the list of widgets for the work item's type to extract only the supported attributes
widget_keys = work_item.work_item_type.widgets.map(&:api_symbol)
widget_params = attributes.extract!(*widget_keys)
# Cannot use prepare to use `.to_h` on each input due to
# https://gitlab.com/gitlab-org/gitlab/-/merge_requests/87472#note_945199865
widget_params.transform_values { |values| values.to_h }
end
end
end
end
......@@ -2,6 +2,7 @@
module Mutations
module WorkItems
# TODO: Deprecate in favor of using WorkItemUpdate. See https://gitlab.com/gitlab-org/gitlab/-/issues/366300
class UpdateWidgets < BaseMutation
graphql_name 'WorkItemUpdateWidgets'
description "Updates the attributes of a work item's widgets by global ID." \
......
......@@ -4,10 +4,6 @@ module WorkItems
module Widgets
class Description < Base
delegate :description, to: :work_item
def update(params:)
work_item.description = params[:description] if params&.key?(:description)
end
end
end
end
......@@ -6,6 +6,7 @@ def initialize(project:, current_user: nil, params: {}, spam_params: nil, widget
super(project: project, current_user: current_user, params: params, spam_params: nil)
@widget_params = widget_params
@widget_services = {}
end
private
......@@ -24,8 +25,20 @@ def after_update(work_item)
def execute_widgets(work_item:, callback:)
work_item.widgets.each do |widget|
widget.try(callback, params: @widget_params[widget.class.api_symbol])
widget_service(widget).try(callback, params: @widget_params[widget.class.api_symbol])
end
end
def widget_service(widget)
service_class = begin
"WorkItems::Widgets::#{widget.type.capitalize}Service::UpdateService".constantize
rescue NameError
nil
end
return unless service_class
@widget_services[widget] ||= service_class.new(widget: widget, current_user: current_user)
end
end
end
# frozen_string_literal: true
module WorkItems
module Widgets
class BaseService < ::BaseService
attr_reader :widget, :current_user
def initialize(widget:, current_user:)
@widget = widget
@current_user = current_user
end
end
end
end
# frozen_string_literal: true
module WorkItems
module Widgets
module DescriptionService
class UpdateService < WorkItems::Widgets::BaseService
def update(params: {})
return unless params.present? && params[:description]
widget.work_item.description = params[:description]
end
end
end
end
end
......@@ -5608,6 +5608,7 @@ Input type: `WorkItemUpdateInput`
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="mutationworkitemupdateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationworkitemupdatedescriptionwidget"></a>`descriptionWidget` | [`WorkItemWidgetDescriptionInput`](#workitemwidgetdescriptioninput) | Input for description widget. |
| <a id="mutationworkitemupdateid"></a>`id` | [`WorkItemID!`](#workitemid) | Global ID of the work item. |
| <a id="mutationworkitemupdatestateevent"></a>`stateEvent` | [`WorkItemStateEvent`](#workitemstateevent) | Close or reopen a work item. |
| <a id="mutationworkitemupdatetitle"></a>`title` | [`String`](#string) | Title of the work item. |
......@@ -21925,6 +21926,7 @@ A time-frame defined as a closed inclusive range of two dates.
 
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="workitemupdatedtaskinputdescriptionwidget"></a>`descriptionWidget` | [`WorkItemWidgetDescriptionInput`](#workitemwidgetdescriptioninput) | Input for description widget. |
| <a id="workitemupdatedtaskinputid"></a>`id` | [`WorkItemID!`](#workitemid) | Global ID of the work item. |
| <a id="workitemupdatedtaskinputstateevent"></a>`stateEvent` | [`WorkItemStateEvent`](#workitemstateevent) | Close or reopen a work item. |
| <a id="workitemupdatedtaskinputtitle"></a>`title` | [`String`](#string) | Title of the work item. |
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe ::Types::WorkItems::Widgets::DescriptionInputType do
it { expect(described_class.graphql_name).to eq('WorkItemWidgetDescriptionInput') }
it { expect(described_class.arguments.keys).to match_array(%w[description]) }
end
......@@ -11,8 +11,17 @@
let(:work_item_event) { 'CLOSE' }
let(:input) { { 'stateEvent' => work_item_event, 'title' => 'updated title' } }
let(:fields) do
<<~FIELDS
workItem {
state
title
}
errors
FIELDS
end
let(:mutation) { graphql_mutation(:workItemUpdate, input.merge('id' => work_item.to_global_id.to_s)) }
let(:mutation) { graphql_mutation(:workItemUpdate, input.merge('id' => work_item.to_global_id.to_s), fields) }
let(:mutation_response) { graphql_mutation_response(:work_item_update) }
......@@ -80,5 +89,29 @@
expect(mutation_response['errors']).to contain_exactly('`work_items` feature flag disabled for this project')
end
end
context 'with description widget input' do
let(:fields) do
<<~FIELDS
workItem {
description
widgets {
type
... on WorkItemWidgetDescription {
description
}
}
}
errors
FIELDS
end
it_behaves_like 'update work item description widget' do
let(:new_description) { 'updated description' }
let(:input) do
{ 'descriptionWidget' => { 'description' => new_description } }
end
end
end
end
end
......@@ -9,16 +9,23 @@
let_it_be(:developer) { create(:user).tap { |user| project.add_developer(user) } }
let_it_be(:work_item, refind: true) { create(:work_item, project: project) }
let(:input) do
{
'descriptionWidget' => { 'description' => 'updated description' }
let(:input) { { 'descriptionWidget' => { 'description' => 'updated description' } } }
let(:mutation_response) { graphql_mutation_response(:work_item_update_widgets) }
let(:mutation) do
graphql_mutation(:workItemUpdateWidgets, input.merge('id' => work_item.to_global_id.to_s), <<~FIELDS)
errors
workItem {
description
widgets {
type
... on WorkItemWidgetDescription {
description
}
}
}
FIELDS
end
let(:mutation) { graphql_mutation(:workItemUpdateWidgets, input.merge('id' => work_item.to_global_id.to_s)) }
let(:mutation_response) { graphql_mutation_response(:work_item_update_widgets) }
context 'the user is not allowed to update a work item' do
let(:current_user) { create(:user) }
......@@ -28,32 +35,8 @@
context 'when user has permissions to update a work item', :aggregate_failures do
let(:current_user) { developer }
context 'when the updated work item is not valid' do
it 'returns validation errors without the work item' do
errors = ActiveModel::Errors.new(work_item).tap { |e| e.add(:description, 'error message') }
allow_next_found_instance_of(::WorkItem) do |instance|
allow(instance).to receive(:valid?).and_return(false)
allow(instance).to receive(:errors).and_return(errors)
end
post_graphql_mutation(mutation, current_user: current_user)
expect(mutation_response['workItem']).to be_nil
expect(mutation_response['errors']).to match_array(['Description error message'])
end
end
it 'updates the work item widgets' do
expect do
post_graphql_mutation(mutation, current_user: current_user)
work_item.reload
end.to change(work_item, :description).from(nil).to('updated description')
expect(response).to have_gitlab_http_status(:success)
expect(mutation_response['workItem']).to include(
'title' => work_item.title
)
it_behaves_like 'update work item description widget' do
let(:new_description) { 'updated description' }
end
it_behaves_like 'has spam protection' do
......@@ -69,7 +52,7 @@
expect do
post_graphql_mutation(mutation, current_user: current_user)
work_item.reload
end.to not_change(work_item, :title)
end.to not_change(work_item, :description)
expect(mutation_response['errors']).to contain_exactly('`work_items` feature flag disabled for this project')
end
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe WorkItems::Widgets::DescriptionService::UpdateService do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project) }
let_it_be_with_reload(:work_item) { create(:work_item, project: project, description: 'old description') }
let(:widget) { work_item.widgets.find {|widget| widget.is_a?(WorkItems::Widgets::Description) } }
describe '#update' do
subject { described_class.new(widget: widget, current_user: user).update(params: params) } # rubocop:disable Rails/SaveBang
context 'when description param is present' do
let(:params) { { description: 'updated description' } }
it 'correctly sets work item description value' do
subject
expect(work_item.description).to eq('updated description')
end
end
context 'when description param is not present' do
let(:params) { {} }
it 'does not change work item description value' do
subject
expect(work_item.description).to eq('old description')
end
end
end
end
# frozen_string_literal: true
RSpec.shared_examples 'update work item description widget' do
it 'updates the description widget' do
expect do
post_graphql_mutation(mutation, current_user: current_user)
work_item.reload
end.to change(work_item, :description).from(nil).to(new_description)
expect(response).to have_gitlab_http_status(:success)
expect(mutation_response['workItem']['widgets']).to include(
{
'description' => new_description,
'type' => 'DESCRIPTION'
}
)
end
context 'when the updated work item is not valid' do
it 'returns validation errors without the work item' do
errors = ActiveModel::Errors.new(work_item).tap { |e| e.add(:description, 'error message') }
allow_next_found_instance_of(::WorkItem) do |instance|
allow(instance).to receive(:valid?).and_return(false)
allow(instance).to receive(:errors).and_return(errors)
end
post_graphql_mutation(mutation, current_user: current_user)
expect(mutation_response['workItem']).to be_nil
expect(mutation_response['errors']).to match_array(['Description error message'])
end
end
end
0% 加载中 .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册