Commit 1ed13b94 authored by Brett Walker's avatar Brett Walker

Mutation for creaing a board list

either a backlog and label list
parent 973c6177
# frozen_string_literal: true
module Mutations
module Boards
module Lists
class Base < BaseMutation
include Mutations::ResolvesIssuable
argument :board_id, ::Types::GlobalIDType[::Board],
required: true,
description: 'The Global ID of the issue board to mutate'
field :list,
Types::BoardListType,
null: true,
description: 'List of the issue board'
authorize :admin_list
private
def find_object(id:)
GitlabSchema.object_from_id(id, expected_type: ::Board)
end
end
end
end
end
# frozen_string_literal: true
module Mutations
module Boards
module Lists
class Create < Base
graphql_name 'BoardListCreate'
argument :backlog, GraphQL::BOOLEAN_TYPE,
required: false,
description: 'Create the backlog list'
argument :label_id, ::Types::GlobalIDType[::Label],
required: false,
description: 'ID of an existing label'
def ready?(**args)
if args.slice(*mutually_exclusive_args).size != 1
arg_str = mutually_exclusive_args.map { |x| x.to_s.camelize(:lower) }.join(' or ')
raise Gitlab::Graphql::Errors::ArgumentError, "one and only one of #{arg_str} is required"
end
super
end
def resolve(**args)
board = authorized_find!(id: args[:board_id])
params = create_list_params(args)
authorize_list_type_resource!(board, params)
list = create_list(board, params)
{
list: list.valid? ? list : nil,
errors: errors_on_object(list)
}
end
private
def authorize_list_type_resource!(board, params)
return unless params[:label_id]
labels = ::Labels::AvailableLabelsService.new(current_user, board.resource_parent, params)
.filter_labels_ids_in_param(:label_id)
unless labels.present?
raise Gitlab::Graphql::Errors::ArgumentError, 'Label not found!'
end
end
def create_list(board, params)
create_list_service =
::Boards::Lists::CreateService.new(board.resource_parent, current_user, params)
create_list_service.execute(board)
end
def create_list_params(args)
params = args.slice(*mutually_exclusive_args).with_indifferent_access
params[:label_id] = GitlabSchema.parse_gid(params[:label_id]).model_id if params[:label_id]
params
end
def mutually_exclusive_args
[:backlog, :label_id]
end
end
end
end
end
......@@ -15,6 +15,7 @@ module Types
mount_mutation Mutations::AwardEmojis::Remove
mount_mutation Mutations::AwardEmojis::Toggle
mount_mutation Mutations::Boards::Issues::IssueMoveList
mount_mutation Mutations::Boards::Lists::Create
mount_mutation Mutations::Branches::Create, calls_gitaly: true
mount_mutation Mutations::Commits::Create, calls_gitaly: true
mount_mutation Mutations::Discussions::ToggleResolve
......
......@@ -30,7 +30,7 @@ module Labels
end
def filter_labels_ids_in_param(key)
ids = params[key].to_a
ids = Array.wrap(params[key])
return [] if ids.empty?
# rubocop:disable CodeReuse/ActiveRecord
......
---
title: 'Add mutation to create a label or default backlog list for an issue board'
merge_request: 31233
author:
type: added
......@@ -1165,6 +1165,11 @@ input BoardEpicIssueInput {
weight: String
}
"""
Identifier of Board
"""
scalar BoardID
"""
Represents a list for an issue board
"""
......@@ -1270,6 +1275,51 @@ type BoardListConnection {
pageInfo: PageInfo!
}
"""
Autogenerated input type of BoardListCreate
"""
input BoardListCreateInput {
"""
Create the backlog list
"""
backlog: Boolean
"""
The Global ID of the issue board to mutate
"""
boardId: BoardID!
"""
A unique identifier for the client performing the mutation.
"""
clientMutationId: String
"""
ID of an existing label
"""
labelId: LabelID
}
"""
Autogenerated return type of BoardListCreate
"""
type BoardListCreatePayload {
"""
A unique identifier for the client performing the mutation.
"""
clientMutationId: String
"""
Errors encountered during execution of the mutation.
"""
errors: [String!]!
"""
List of the issue board
"""
list: BoardList
}
"""
An edge in a connection.
"""
......@@ -8103,6 +8153,11 @@ type LabelEdge {
node: Label
}
"""
Identifier of Label
"""
scalar LabelID
"""
List limit metric setting
"""
......@@ -9387,6 +9442,7 @@ type Mutation {
awardEmojiAdd(input: AwardEmojiAddInput!): AwardEmojiAddPayload
awardEmojiRemove(input: AwardEmojiRemoveInput!): AwardEmojiRemovePayload
awardEmojiToggle(input: AwardEmojiToggleInput!): AwardEmojiTogglePayload
boardListCreate(input: BoardListCreateInput!): BoardListCreatePayload
boardListUpdateLimitMetrics(input: BoardListUpdateLimitMetricsInput!): BoardListUpdateLimitMetricsPayload
commitCreate(input: CommitCreateInput!): CommitCreatePayload
configureSast(input: ConfigureSastInput!): ConfigureSastPayload
......
......@@ -3108,6 +3108,16 @@
"enumValues": null,
"possibleTypes": null
},
{
"kind": "SCALAR",
"name": "BoardID",
"description": "Identifier of Board",
"fields": null,
"inputFields": null,
"interfaces": null,
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "BoardList",
......@@ -3407,6 +3417,128 @@
"enumValues": null,
"possibleTypes": null
},
{
"kind": "INPUT_OBJECT",
"name": "BoardListCreateInput",
"description": "Autogenerated input type of BoardListCreate",
"fields": null,
"inputFields": [
{
"name": "boardId",
"description": "The Global ID of the issue board to mutate",
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "BoardID",
"ofType": null
}
},
"defaultValue": null
},
{
"name": "backlog",
"description": "Create the backlog list",
"type": {
"kind": "SCALAR",
"name": "Boolean",
"ofType": null
},
"defaultValue": null
},
{
"name": "labelId",
"description": "ID of an existing label",
"type": {
"kind": "SCALAR",
"name": "LabelID",
"ofType": null
},
"defaultValue": null
},
{
"name": "clientMutationId",
"description": "A unique identifier for the client performing the mutation.",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
}
],
"interfaces": null,
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "BoardListCreatePayload",
"description": "Autogenerated return type of BoardListCreate",
"fields": [
{
"name": "clientMutationId",
"description": "A unique identifier for the client performing the mutation.",
"args": [
],
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "errors",
"description": "Errors encountered during execution of the mutation.",
"args": [
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
}
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "list",
"description": "List of the issue board",
"args": [
],
"type": {
"kind": "OBJECT",
"name": "BoardList",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [
],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "BoardListEdge",
......@@ -22508,6 +22640,16 @@
"enumValues": null,
"possibleTypes": null
},
{
"kind": "SCALAR",
"name": "LabelID",
"description": "Identifier of Label",
"fields": null,
"inputFields": null,
"interfaces": null,
"enumValues": null,
"possibleTypes": null
},
{
"kind": "ENUM",
"name": "ListLimitMetric",
......@@ -26491,6 +26633,33 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "boardListCreate",
"description": null,
"args": [
{
"name": "input",
"description": null,
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "INPUT_OBJECT",
"name": "BoardListCreateInput",
"ofType": null
}
},
"defaultValue": null
}
],
"type": {
"kind": "OBJECT",
"name": "BoardListCreatePayload",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "boardListUpdateLimitMetrics",
"description": null,
......@@ -214,6 +214,16 @@ Represents a list for an issue board
| `position` | Int | Position of list within the board |
| `title` | String! | Title of the list |
## BoardListCreatePayload
Autogenerated return type of BoardListCreate
| Name | Type | Description |
| --- | ---- | ---------- |
| `clientMutationId` | String | A unique identifier for the client performing the mutation. |
| `errors` | String! => Array | Errors encountered during execution of the mutation. |
| `list` | BoardList | List of the issue board |
## BoardListUpdateLimitMetricsPayload
Autogenerated return type of BoardListUpdateLimitMetrics
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Mutations::Boards::Lists::Create do
include GraphqlHelpers
let_it_be(:group) { create(:group, :private) }
let_it_be(:board) { create(:board, group: group) }
let_it_be(:user) { create(:user) }
let_it_be(:guest) { create(:user) }
let(:current_user) { user }
let(:mutation) { described_class.new(object: nil, context: { current_user: current_user }, field: nil) }
let(:list_create_params) { {} }
before_all do
group.add_reporter(user)
group.add_guest(guest)
end
subject { mutation.resolve(board_id: board.to_global_id.to_s, **list_create_params) }
describe '#ready?' do
it 'raises an error if required arguments are missing' do
expect { mutation.ready?({ board_id: 'some id' }) }
.to raise_error(Gitlab::Graphql::Errors::ArgumentError,
'one and only one of backlog or labelId is required')
end
it 'raises an error if too many required arguments are specified' do
expect { mutation.ready?({ board_id: 'some id', backlog: true, label_id: 'some label' }) }
.to raise_error(Gitlab::Graphql::Errors::ArgumentError,
'one and only one of backlog or labelId is required')
end
end
describe '#resolve' do
context 'with proper permissions' do
describe 'backlog list' do
let(:list_create_params) { { backlog: true } }
it 'creates one and only one backlog' do
expect { subject }.to change { board.lists.backlog.count }.from(0).to(1)
expect(board.lists.backlog.first.list_type).to eq 'backlog'
backlog_id = board.lists.backlog.first.id
expect { subject }.not_to change { board.lists.backlog.count }
expect(board.lists.backlog.last.id).to eq backlog_id
end
end
describe 'label list' do
let_it_be(:dev_label) do
create(:group_label, title: 'Development', color: '#FFAABB', group: group)
end
let(:list_create_params) { { label_id: dev_label.to_global_id.to_s } }
it 'creates a new issue board list for labels' do
expect { subject }.to change { board.lists.count }.from(1).to(2)
new_list = subject[:list]
expect(new_list.title).to eq dev_label.title
expect(new_list.position).to eq 0
end
end
end
context 'without proper permissions' do
let(:current_user) { guest }
it 'raises an error' do
expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
end
end
end
end
......@@ -88,6 +88,12 @@ RSpec.describe Labels::AvailableLabelsService do
expect(result).to match_array([group_label.id])
end
end
it 'accepts a single id parameter' do
result = described_class.new(user, project, label_id: project_label.id).filter_labels_ids_in_param(:label_id)
expect(result).to match_array([project_label.id])
end
end
describe '#available_labels' 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