Commit 02b38723 authored by Alexandru Croitor's avatar Alexandru Croitor

Update and expose board labels in graphql

Allow for boards to be updated with labels as well as expose board
labels in graphql.
parent 09adf1cd
......@@ -1072,6 +1072,31 @@ type Board {
"""
id: ID!
"""
Labels of the board
"""
labels(
"""
Returns the elements in the list that come after the specified cursor.
"""
after: String
"""
Returns the elements in the list that come before the specified cursor.
"""
before: String
"""
Returns the first _n_ elements from the list.
"""
first: Int
"""
Returns the last _n_ elements from the list.
"""
last: Int
): LabelConnection
"""
Lists of the board
"""
......@@ -18763,6 +18788,16 @@ input UpdateBoardInput {
"""
id: ID!
"""
The IDs of labels to be added to the board.
"""
labelIds: [LabelID!]
"""
Labels of the issue
"""
labels: [String!]
"""
The id of milestone to be assigned to the board.
"""
......
......@@ -2840,6 +2840,59 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "labels",
"description": "Labels of the board",
"args": [
{
"name": "after",
"description": "Returns the elements in the list that come after the specified cursor.",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
},
{
"name": "before",
"description": "Returns the elements in the list that come before the specified cursor.",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
},
{
"name": "first",
"description": "Returns the first _n_ elements from the list.",
"type": {
"kind": "SCALAR",
"name": "Int",
"ofType": null
},
"defaultValue": null
},
{
"name": "last",
"description": "Returns the last _n_ elements from the list.",
"type": {
"kind": "SCALAR",
"name": "Int",
"ofType": null
},
"defaultValue": null
}
],
"type": {
"kind": "OBJECT",
"name": "LabelConnection",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "lists",
"description": "Lists of the board",
......@@ -54769,6 +54822,42 @@
},
"defaultValue": null
},
{
"name": "labels",
"description": "Labels of the issue",
"type": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
}
},
"defaultValue": null
},
{
"name": "labelIds",
"description": "The IDs of labels to be added to the board.",
"type": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "LabelID",
"ofType": null
}
}
},
"defaultValue": null
},
{
"name": "clientMutationId",
"description": "A unique identifier for the client performing the mutation.",
......@@ -9,8 +9,10 @@ module EE
field :assignee, type: ::Types::UserType, null: true,
description: 'The board assignee.'
field :milestone, type: ::Types::MilestoneType, null: true,
description: 'The board milestone.'
field :epics, ::Types::Boards::BoardEpicType.connection_type, null: true,
description: 'Epics associated with board issues.',
resolver: ::Resolvers::BoardGroupings::EpicsResolver,
complexity: 5
field :hide_backlog_list, type: GraphQL::BOOLEAN_TYPE, null: true,
description: 'Whether or not backlog list is hidden.'
......@@ -18,13 +20,14 @@ module EE
field :hide_closed_list, type: GraphQL::BOOLEAN_TYPE, null: true,
description: 'Whether or not closed list is hidden.'
field :labels, ::Types::LabelType.connection_type, null: true,
description: 'Labels of the board'
field :milestone, type: ::Types::MilestoneType, null: true,
description: 'The board milestone.'
field :weight, type: GraphQL::INT_TYPE, null: true,
description: 'Weight of the board.'
field :epics, ::Types::Boards::BoardEpicType.connection_type, null: true,
description: 'Epics associated with board issues.',
resolver: ::Resolvers::BoardGroupings::EpicsResolver,
complexity: 5
end
end
end
......
......@@ -41,6 +41,14 @@ module Mutations
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.'
field :board,
Types::BoardType,
null: true,
......@@ -61,6 +69,15 @@ module Mutations
}
end
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
private
def find_object(id:)
......@@ -76,8 +93,16 @@ module Mutations
args[:milestone_id] = GitlabSchema.parse_gid(args[:milestone_id], expected_type: ::Milestone).model_id
end
args[:label_ids] &&= args[:label_ids].map do |label_id|
::GitlabSchema.parse_gid(label_id, expected_type: ::Label).model_id
end
args
end
def mutually_exclusive_args
[:labels, :label_ids]
end
end
end
end
---
title: Update and expose board labels trough GraphQL API
merge_request: 44204
author:
type: added
......@@ -4,6 +4,8 @@ require 'spec_helper'
RSpec.describe GitlabSchema.types['Board'] do
it 'includes the ee specific fields' do
expect(described_class).to have_graphql_fields(:weight, :epics).at_least
expect(described_class).to have_graphql_fields(
:assignee, :epics, :hide_backlog_list, :hide_closed_list, :labels, :milestone, :weight
).at_least
end
end
......@@ -7,26 +7,31 @@ RSpec.describe Mutations::Boards::Update do
let_it_be(:user) { create(:user) }
let_it_be(:board) { create(:board, project: project) }
let_it_be(:milestone) { create(:milestone, project: project) }
let_it_be(:label1) { create(:label, project: project) }
let_it_be(:label2) { create(:label, project: project) }
let(:new_labels) { %w(new_label1 new_label2) }
let(:mutation) { described_class.new(object: nil, context: { current_user: user }, field: nil) }
let(:mutated_board) { subject[:board] }
specify { expect(described_class).to require_graphql_authorizations(:admin_board) }
let(:mutation_params) do
{
id: board.to_global_id,
name: 'Test board 1',
hide_backlog_list: true,
hide_closed_list: true,
weight: 3,
assignee_id: user.to_global_id,
milestone_id: milestone.to_global_id,
label_ids: [label1.to_global_id, label2.to_global_id]
}
end
describe '#resolve' do
let(:mutation_params) do
{
id: board.to_global_id,
name: 'Test board 1',
hide_backlog_list: true,
hide_closed_list: true,
weight: 3,
assignee_id: user.to_global_id,
milestone_id: milestone.to_global_id
}
end
subject { mutation.resolve(mutation_params) }
subject { mutation.resolve(mutation_params) }
specify { expect(described_class).to require_graphql_authorizations(:admin_board) }
describe '#resolve' do
context 'when the user cannot admin the board' do
it 'raises an error' do
expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
......@@ -45,13 +50,40 @@ RSpec.describe Mutations::Boards::Update do
hide_closed_list: true,
weight: 3,
assignee: user,
milestone: milestone
milestone: milestone,
labels: [label1, label2]
}
subject
expect(board.reload).to have_attributes(expected_attributes)
end
context 'when passing labels param' do
before do
mutation_params.delete(:label_ids)
mutation_params.merge!(labels: new_labels)
end
it 'updates board with correct labels' do
subject
expect(board.reload.labels.pluck(:title)).to eq(new_labels)
end
end
end
end
describe '#ready' do
context 'when passing both labels & label_ids param' do
before do
mutation_params.merge!(labels: new_labels)
end
it 'raises exception when mutually exclusive params are given' do
expect { mutation.ready?(mutation_params) }
.to raise_error(Gitlab::Graphql::Errors::ArgumentError, /one and only one of/)
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