Commit cffdff1f authored by charlie ablett's avatar charlie ablett

Merge branch 'cherry-pick-8d0ec9fb' into 'master'

[RUN AS-IF-FOSS] Reuse scope board mutation logic for epic boards (2nd attempt)

See merge request gitlab-org/gitlab!56467
parents b53aec54 a9762c79
...@@ -22,7 +22,7 @@ module Mutations ...@@ -22,7 +22,7 @@ module Mutations
response = ::Boards::CreateService.new(board_parent, current_user, args).execute response = ::Boards::CreateService.new(board_parent, current_user, args).execute
{ {
board: response.payload, board: response.success? ? response.payload : nil,
errors: response.errors errors: response.errors
} }
end end
......
...@@ -7,6 +7,7 @@ class Board < ApplicationRecord ...@@ -7,6 +7,7 @@ class Board < ApplicationRecord
has_many :lists, -> { ordered }, dependent: :delete_all # rubocop:disable Cop/ActiveRecordDependent has_many :lists, -> { ordered }, dependent: :delete_all # rubocop:disable Cop/ActiveRecordDependent
has_many :destroyable_lists, -> { destroyable.ordered }, class_name: "List" has_many :destroyable_lists, -> { destroyable.ordered }, class_name: "List"
validates :name, presence: true
validates :project, presence: true, if: :project_needed? validates :project, presence: true, if: :project_needed?
validates :group, presence: true, unless: :project validates :group, presence: true, unless: :project
......
---
title: Fixed error handling GraphQL API when issue board creation fails
merge_request: 56467
author:
type: fixed
...@@ -5,10 +5,10 @@ module EE ...@@ -5,10 +5,10 @@ module EE
module Boards module Boards
module Create module Create
extend ActiveSupport::Concern extend ActiveSupport::Concern
prepend ::Mutations::Boards::ScopedBoardMutation
prepended do prepended do
include Mutations::Boards::ScopedBoardArguments prepend ::Mutations::Boards::ScopedIssueBoardArguments
prepend ::Mutations::Boards::ScopedBoardMutation
end end
end end
end end
......
# frozen_string_literal: true
module EE
module Mutations
module Boards
module ScopedBoardArguments
extend ActiveSupport::Concern
included do
argument :assignee_id,
::Types::GlobalIDType[::User],
required: false,
description: 'The ID of user to be assigned to the board.'
# Cannot pre-load ::Types::MilestoneType because we are also assigning values like:
# ::Timebox::None(0), ::Timebox::Upcoming(-2) or ::Timebox::Started(-3), that cannot be resolved to a DB record.
argument :milestone_id,
::Types::GlobalIDType[::Milestone],
required: false,
description: 'The ID of milestone to be assigned to the board.'
# Cannot pre-load ::Types::IterationType because we are also assigning values like:
# ::Iteration::Predefined::None(0) or ::Iteration::Predefined::Current(-4), that cannot be resolved to a DB record.
argument :iteration_id,
::Types::GlobalIDType[::Iteration],
required: false,
description: 'The ID of iteration to be assigned to the board.'
argument :weight,
GraphQL::INT_TYPE,
required: false,
description: 'The weight value to be assigned to the board.'
argument :labels, [GraphQL::STRING_TYPE],
required: false,
description: copy_field_description(::Types::IssueType, :labels)
argument :label_ids, [::Types::GlobalIDType[::Label]],
required: false,
description: 'The IDs of labels to be added to the board.'
end
end
end
end
end
...@@ -5,10 +5,10 @@ module EE ...@@ -5,10 +5,10 @@ module EE
module Boards module Boards
module Update module Update
extend ActiveSupport::Concern extend ActiveSupport::Concern
prepend ::Mutations::Boards::ScopedBoardMutation
prepended do prepended do
include Mutations::Boards::ScopedBoardArguments prepend ::Mutations::Boards::ScopedIssueBoardArguments
prepend ::Mutations::Boards::ScopedBoardMutation
end end
end end
end end
......
...@@ -6,6 +6,7 @@ module Mutations ...@@ -6,6 +6,7 @@ module Mutations
class Create < ::Mutations::BaseMutation class Create < ::Mutations::BaseMutation
include Mutations::ResolvesGroup include Mutations::ResolvesGroup
include Mutations::Boards::CommonMutationArguments include Mutations::Boards::CommonMutationArguments
prepend Mutations::Boards::ScopedBoardMutation
graphql_name 'EpicBoardCreate' graphql_name 'EpicBoardCreate'
...@@ -27,7 +28,7 @@ module Mutations ...@@ -27,7 +28,7 @@ module Mutations
service_response = ::Boards::EpicBoards::CreateService.new(group, current_user, args).execute service_response = ::Boards::EpicBoards::CreateService.new(group, current_user, args).execute
{ {
epic_board: service_response.payload, epic_board: service_response.success? ? service_response.payload : nil,
errors: service_response.errors errors: service_response.errors
} }
end end
......
...@@ -16,14 +16,6 @@ module Mutations ...@@ -16,14 +16,6 @@ module Mutations
required: true, required: true,
description: 'The epic board global ID.' description: 'The epic board global ID.'
argument :labels, [GraphQL::STRING_TYPE],
required: false,
description: 'Labels to be added to the board.'
argument :label_ids, [::Types::GlobalIDType[::Label]],
required: false,
description: 'The IDs of labels to be added to the board.'
field :epic_board, field :epic_board,
Types::Boards::EpicBoardType, Types::Boards::EpicBoardType,
null: true, null: true,
......
...@@ -5,6 +5,16 @@ module Mutations ...@@ -5,6 +5,16 @@ module Mutations
module ScopedBoardMutation module ScopedBoardMutation
extend ActiveSupport::Concern extend ActiveSupport::Concern
prepended do
argument :labels, [GraphQL::STRING_TYPE],
required: false,
description: copy_field_description(::Types::IssueType, :labels)
argument :label_ids, [::Types::GlobalIDType[::Label]],
required: false,
description: 'IDs of labels to be added to the board.'
end
def resolve(**args) def resolve(**args)
parsed_params = parse_arguments(args) parsed_params = parse_arguments(args)
......
# frozen_string_literal: true
module Mutations
module Boards
module ScopedIssueBoardArguments
extend ActiveSupport::Concern
prepended do
argument :assignee_id,
::Types::GlobalIDType[::User],
required: false,
description: 'ID of user to be assigned to the board.'
# Cannot pre-load ::Types::MilestoneType because we are also assigning values like:
# ::Timebox::None(0), ::Timebox::Upcoming(-2) or ::Timebox::Started(-3), that cannot be resolved to a DB record.
argument :milestone_id,
::Types::GlobalIDType[::Milestone],
required: false,
description: 'ID of milestone to be assigned to the board.'
# Cannot pre-load ::Types::IterationType because we are also assigning values like:
# ::Iteration::Predefined::None(0) or ::Iteration::Predefined::Current(-4), that cannot be resolved to a DB record.
argument :iteration_id,
::Types::GlobalIDType[::Iteration],
required: false,
description: 'ID of iteration to be assigned to the board.'
argument :weight,
GraphQL::INT_TYPE,
required: false,
description: 'Weight value to be assigned to the board.'
end
end
end
end
...@@ -24,8 +24,6 @@ module EE ...@@ -24,8 +24,6 @@ module EE
has_many :labels, through: :board_labels has_many :labels, through: :board_labels
validates :name, presence: true
scope :with_associations, -> { preload(:destroyable_lists, :labels, :assignee) } scope :with_associations, -> { preload(:destroyable_lists, :labels, :assignee) }
end end
......
...@@ -23,7 +23,7 @@ RSpec.describe ::Mutations::Boards::EpicBoards::Create do ...@@ -23,7 +23,7 @@ RSpec.describe ::Mutations::Boards::EpicBoards::Create do
context 'field tests' do context 'field tests' do
subject { described_class } subject { described_class }
it { is_expected.to have_graphql_arguments(:groupPath, :name, :hideBacklogList, :hideClosedList) } it { is_expected.to have_graphql_arguments(:groupPath, :name, :hideBacklogList, :hideClosedList, :labels, :labelIds) }
it { is_expected.to have_graphql_fields(:epic_board).at_least } it { is_expected.to have_graphql_fields(:epic_board).at_least }
end end
......
...@@ -18,10 +18,6 @@ RSpec.describe Board do ...@@ -18,10 +18,6 @@ RSpec.describe Board do
it { is_expected.to have_many(:boards_epic_user_preferences).class_name('Boards::EpicUserPreference') } it { is_expected.to have_many(:boards_epic_user_preferences).class_name('Boards::EpicUserPreference') }
end end
describe 'validations' do
it { is_expected.to validate_presence_of(:name) }
end
context 'validations' do context 'validations' do
context 'when group is present' do context 'when group is present' do
subject { described_class.new(group: create(:group)) } subject { described_class.new(group: create(:group)) }
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Mutations::Boards::EpicBoards::Create do
include GraphqlHelpers
let_it_be(:current_user) { create(:user) }
let_it_be(:group) { create(:group) }
let(:name) { 'board name' }
let(:mutation) { graphql_mutation(:epic_board_create, params) }
let(:label) { create(:group_label, group: group) }
let(:params) { { groupPath: group.full_path, name: 'foo', hide_backlog_list: true, labels: [label.name] } }
subject { post_graphql_mutation(mutation, current_user: current_user) }
def mutation_response
graphql_mutation_response(:epic_board_create)
end
before do
stub_licensed_features(epics: true, scoped_issue_board: true)
end
context 'when the user does not have permission' do
it_behaves_like 'a mutation that returns a top-level access error'
end
context 'when the user has permission' do
before do
group.add_developer(current_user)
end
it 'returns the created board' do
post_graphql_mutation(mutation, current_user: current_user)
expect(mutation_response).to have_key('epicBoard')
expect(mutation_response['epicBoard']['name']).to eq(params[:name])
expect(mutation_response['epicBoard']['hideBacklogList']).to eq(params[:hide_backlog_list])
expect(mutation_response['epicBoard']['labels']['count']).to eq(1)
end
context 'when create fails' do
let(:params) { { groupPath: group.full_path, name: 'x' * 256 } }
it 'returns an error' do
post_graphql_mutation(mutation, current_user: current_user)
expect(mutation_response).to have_key('epicBoard')
expect(mutation_response['epicBoard']).to be_nil
expect(mutation_response['errors'].first).to eq('There was an error when creating a board.')
end
end
end
end
...@@ -12,6 +12,7 @@ RSpec.describe Board do ...@@ -12,6 +12,7 @@ RSpec.describe Board do
end end
describe 'validations' do describe 'validations' do
it { is_expected.to validate_presence_of(:name) }
it { is_expected.to validate_presence_of(:project) } it { is_expected.to validate_presence_of(:project) }
end end
......
...@@ -66,9 +66,7 @@ RSpec.shared_examples 'boards create mutation' do ...@@ -66,9 +66,7 @@ RSpec.shared_examples 'boards create mutation' do
context 'when the Boards::CreateService returns an error response' do context 'when the Boards::CreateService returns an error response' do
before do before do
allow_next_instance_of(Boards::CreateService) do |service| params[:name] = ''
allow(service).to receive(:execute).and_return(ServiceResponse.error(message: 'There was an error.'))
end
end end
it 'does not create a board' do it 'does not create a board' do
...@@ -80,7 +78,7 @@ RSpec.shared_examples 'boards create mutation' do ...@@ -80,7 +78,7 @@ RSpec.shared_examples 'boards create mutation' do
expect(mutation_response).to have_key('board') expect(mutation_response).to have_key('board')
expect(mutation_response['board']).to be_nil expect(mutation_response['board']).to be_nil
expect(mutation_response['errors'].first).to eq('There was an error.') expect(mutation_response['errors'].first).to eq('There was an error when creating a board.')
end end
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