Commit 2e5179b0 authored by Felipe Artur's avatar Felipe Artur Committed by Douglas Barbosa Alexandre

Add mutation to update boards

Allow to update boards via GraphQL
parent dcea86d0
...@@ -992,6 +992,21 @@ enum BlobViewersType { ...@@ -992,6 +992,21 @@ enum BlobViewersType {
Represents a project or group board Represents a project or group board
""" """
type Board { type Board {
"""
The board assignee.
"""
assignee: User
"""
Whether or not backlog list is hidden.
"""
hideBacklogList: Boolean
"""
Whether or not closed list is hidden.
"""
hideClosedList: Boolean
""" """
ID (global ID) of the board ID (global ID) of the board
""" """
...@@ -1027,13 +1042,18 @@ type Board { ...@@ -1027,13 +1042,18 @@ type Board {
last: Int last: Int
): BoardListConnection ): BoardListConnection
"""
The board milestone.
"""
milestone: Milestone
""" """
Name of the board Name of the board
""" """
name: String name: String
""" """
Weight of the board Weight of the board.
""" """
weight: Int weight: Int
} }
...@@ -9075,6 +9095,7 @@ type Mutation { ...@@ -9075,6 +9095,7 @@ type Mutation {
todosMarkAllDone(input: TodosMarkAllDoneInput!): TodosMarkAllDonePayload todosMarkAllDone(input: TodosMarkAllDoneInput!): TodosMarkAllDonePayload
toggleAwardEmoji(input: ToggleAwardEmojiInput!): ToggleAwardEmojiPayload @deprecated(reason: "Use awardEmojiToggle. Deprecated in 13.2") toggleAwardEmoji(input: ToggleAwardEmojiInput!): ToggleAwardEmojiPayload @deprecated(reason: "Use awardEmojiToggle. Deprecated in 13.2")
updateAlertStatus(input: UpdateAlertStatusInput!): UpdateAlertStatusPayload updateAlertStatus(input: UpdateAlertStatusInput!): UpdateAlertStatusPayload
updateBoard(input: UpdateBoardInput!): UpdateBoardPayload
updateContainerExpirationPolicy(input: UpdateContainerExpirationPolicyInput!): UpdateContainerExpirationPolicyPayload updateContainerExpirationPolicy(input: UpdateContainerExpirationPolicyInput!): UpdateContainerExpirationPolicyPayload
updateEpic(input: UpdateEpicInput!): UpdateEpicPayload updateEpic(input: UpdateEpicInput!): UpdateEpicPayload
...@@ -14892,6 +14913,71 @@ type UpdateAlertStatusPayload { ...@@ -14892,6 +14913,71 @@ type UpdateAlertStatusPayload {
todo: Todo todo: Todo
} }
"""
Autogenerated input type of UpdateBoard
"""
input UpdateBoardInput {
"""
The id of user to be assigned to the board.
"""
assigneeId: ID
"""
A unique identifier for the client performing the mutation.
"""
clientMutationId: String
"""
Whether or not backlog list is hidden.
"""
hideBacklogList: Boolean
"""
Whether or not closed list is hidden.
"""
hideClosedList: Boolean
"""
The board global id.
"""
id: ID!
"""
The id of milestone to be assigned to the board.
"""
milestoneId: ID
"""
Name of the board
"""
name: String
"""
The weight value to be assigned to the board.
"""
weight: Int
}
"""
Autogenerated return type of UpdateBoard
"""
type UpdateBoardPayload {
"""
The board after mutation.
"""
board: Board
"""
A unique identifier for the client performing the mutation.
"""
clientMutationId: String
"""
Errors encountered during execution of the mutation.
"""
errors: [String!]!
}
""" """
Autogenerated input type of UpdateContainerExpirationPolicy Autogenerated input type of UpdateContainerExpirationPolicy
""" """
......
...@@ -2661,6 +2661,48 @@ ...@@ -2661,6 +2661,48 @@
"name": "Board", "name": "Board",
"description": "Represents a project or group board", "description": "Represents a project or group board",
"fields": [ "fields": [
{
"name": "assignee",
"description": "The board assignee.",
"args": [
],
"type": {
"kind": "OBJECT",
"name": "User",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "hideBacklogList",
"description": "Whether or not backlog list is hidden.",
"args": [
],
"type": {
"kind": "SCALAR",
"name": "Boolean",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "hideClosedList",
"description": "Whether or not closed list is hidden.",
"args": [
],
"type": {
"kind": "SCALAR",
"name": "Boolean",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{ {
"name": "id", "name": "id",
"description": "ID (global ID) of the board", "description": "ID (global ID) of the board",
...@@ -2742,6 +2784,20 @@ ...@@ -2742,6 +2784,20 @@
"isDeprecated": false, "isDeprecated": false,
"deprecationReason": null "deprecationReason": null
}, },
{
"name": "milestone",
"description": "The board milestone.",
"args": [
],
"type": {
"kind": "OBJECT",
"name": "Milestone",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{ {
"name": "name", "name": "name",
"description": "Name of the board", "description": "Name of the board",
...@@ -2758,7 +2814,7 @@ ...@@ -2758,7 +2814,7 @@
}, },
{ {
"name": "weight", "name": "weight",
"description": "Weight of the board", "description": "Weight of the board.",
"args": [ "args": [
], ],
...@@ -27030,6 +27086,33 @@ ...@@ -27030,6 +27086,33 @@
"isDeprecated": false, "isDeprecated": false,
"deprecationReason": null "deprecationReason": null
}, },
{
"name": "updateBoard",
"description": null,
"args": [
{
"name": "input",
"description": null,
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "INPUT_OBJECT",
"name": "UpdateBoardInput",
"ofType": null
}
},
"defaultValue": null
}
],
"type": {
"kind": "OBJECT",
"name": "UpdateBoardPayload",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{ {
"name": "updateContainerExpirationPolicy", "name": "updateContainerExpirationPolicy",
"description": null, "description": null,
...@@ -44014,6 +44097,168 @@ ...@@ -44014,6 +44097,168 @@
"enumValues": null, "enumValues": null,
"possibleTypes": null "possibleTypes": null
}, },
{
"kind": "INPUT_OBJECT",
"name": "UpdateBoardInput",
"description": "Autogenerated input type of UpdateBoard",
"fields": null,
"inputFields": [
{
"name": "id",
"description": "The board global id.",
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "ID",
"ofType": null
}
},
"defaultValue": null
},
{
"name": "name",
"description": "Name of the board",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
},
{
"name": "hideBacklogList",
"description": "Whether or not backlog list is hidden.",
"type": {
"kind": "SCALAR",
"name": "Boolean",
"ofType": null
},
"defaultValue": null
},
{
"name": "hideClosedList",
"description": "Whether or not closed list is hidden.",
"type": {
"kind": "SCALAR",
"name": "Boolean",
"ofType": null
},
"defaultValue": null
},
{
"name": "assigneeId",
"description": "The id of user to be assigned to the board.",
"type": {
"kind": "SCALAR",
"name": "ID",
"ofType": null
},
"defaultValue": null
},
{
"name": "milestoneId",
"description": "The id of milestone to be assigned to the board.",
"type": {
"kind": "SCALAR",
"name": "ID",
"ofType": null
},
"defaultValue": null
},
{
"name": "weight",
"description": "The weight value to be assigned to the board.",
"type": {
"kind": "SCALAR",
"name": "Int",
"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": "UpdateBoardPayload",
"description": "Autogenerated return type of UpdateBoard",
"fields": [
{
"name": "board",
"description": "The board after mutation.",
"args": [
],
"type": {
"kind": "OBJECT",
"name": "Board",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"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
}
],
"inputFields": null,
"interfaces": [
],
"enumValues": null,
"possibleTypes": null
},
{ {
"kind": "INPUT_OBJECT", "kind": "INPUT_OBJECT",
"name": "UpdateContainerExpirationPolicyInput", "name": "UpdateContainerExpirationPolicyInput",
...@@ -188,9 +188,13 @@ Represents a project or group board ...@@ -188,9 +188,13 @@ Represents a project or group board
| Name | Type | Description | | Name | Type | Description |
| --- | ---- | ---------- | | --- | ---- | ---------- |
| `assignee` | User | The board assignee. |
| `hideBacklogList` | Boolean | Whether or not backlog list is hidden. |
| `hideClosedList` | Boolean | Whether or not closed list is hidden. |
| `id` | ID! | ID (global ID) of the board | | `id` | ID! | ID (global ID) of the board |
| `milestone` | Milestone | The board milestone. |
| `name` | String | Name of the board | | `name` | String | Name of the board |
| `weight` | Int | Weight of the board | | `weight` | Int | Weight of the board. |
## BoardList ## BoardList
...@@ -2236,6 +2240,16 @@ Autogenerated return type of UpdateAlertStatus ...@@ -2236,6 +2240,16 @@ Autogenerated return type of UpdateAlertStatus
| `issue` | Issue | The issue created after mutation | | `issue` | Issue | The issue created after mutation |
| `todo` | Todo | The todo after mutation | | `todo` | Todo | The todo after mutation |
## UpdateBoardPayload
Autogenerated return type of UpdateBoard
| Name | Type | Description |
| --- | ---- | ---------- |
| `board` | Board | The board after mutation. |
| `clientMutationId` | String | A unique identifier for the client performing the mutation. |
| `errors` | String! => Array | Errors encountered during execution of the mutation. |
## UpdateContainerExpirationPolicyPayload ## UpdateContainerExpirationPolicyPayload
Autogenerated return type of UpdateContainerExpirationPolicy Autogenerated return type of UpdateContainerExpirationPolicy
......
...@@ -6,8 +6,20 @@ module EE ...@@ -6,8 +6,20 @@ module EE
extend ActiveSupport::Concern extend ActiveSupport::Concern
prepended do prepended do
field :assignee, type: ::Types::UserType, null: true,
description: 'The board assignee.'
field :milestone, type: ::Types::MilestoneType, null: true,
description: 'The board milestone.'
field :hide_backlog_list, type: GraphQL::BOOLEAN_TYPE, null: true,
description: 'Whether or not backlog list is hidden.'
field :hide_closed_list, type: GraphQL::BOOLEAN_TYPE, null: true,
description: 'Whether or not closed list is hidden.'
field :weight, type: GraphQL::INT_TYPE, null: true, field :weight, type: GraphQL::INT_TYPE, null: true,
description: 'Weight of the board' description: 'Weight of the board.'
end end
end end
end end
......
...@@ -19,6 +19,7 @@ module EE ...@@ -19,6 +19,7 @@ module EE
mount_mutation ::Mutations::RequirementsManagement::CreateRequirement mount_mutation ::Mutations::RequirementsManagement::CreateRequirement
mount_mutation ::Mutations::RequirementsManagement::UpdateRequirement mount_mutation ::Mutations::RequirementsManagement::UpdateRequirement
mount_mutation ::Mutations::Vulnerabilities::Dismiss mount_mutation ::Mutations::Vulnerabilities::Dismiss
mount_mutation ::Mutations::Boards::Update
mount_mutation ::Mutations::Boards::Lists::UpdateLimitMetrics mount_mutation ::Mutations::Boards::Lists::UpdateLimitMetrics
mount_mutation ::Mutations::InstanceSecurityDashboard::AddProject mount_mutation ::Mutations::InstanceSecurityDashboard::AddProject
mount_mutation ::Mutations::InstanceSecurityDashboard::RemoveProject mount_mutation ::Mutations::InstanceSecurityDashboard::RemoveProject
......
# frozen_string_literal: true
module Mutations
module Boards
class Update < ::Mutations::BaseMutation
graphql_name 'UpdateBoard'
argument :id,
GraphQL::ID_TYPE,
required: true,
description: 'The board global id.'
argument :name,
GraphQL::STRING_TYPE,
required: false,
description: copy_field_description(Types::BoardType, :name)
argument :hide_backlog_list,
GraphQL::BOOLEAN_TYPE,
required: false,
description: copy_field_description(Types::BoardType, :hide_backlog_list)
argument :hide_closed_list,
GraphQL::BOOLEAN_TYPE,
required: false,
description: copy_field_description(Types::BoardType, :hide_closed_list)
argument :assignee_id,
GraphQL::ID_TYPE,
required: false,
loads: ::Types::UserType,
description: 'The id of user to be assigned to the board.'
argument :milestone_id,
GraphQL::ID_TYPE,
required: false,
description: 'The id of milestone to be assigned to the board.'
argument :weight,
GraphQL::INT_TYPE,
required: false,
description: 'The weight value to be assigned to the board.'
field :board,
Types::BoardType,
null: true,
description: "The board after mutation."
authorize :admin_board
def resolve(id:, assignee: nil, **args)
board = authorized_find!(id: id)
parsed_params = parse_arguments(args)
::Boards::UpdateService.new(board.resource_parent, current_user, parsed_params).execute(board)
{
board: board,
errors: errors_on_object(board)
}
end
private
def find_object(id:)
GitlabSchema.object_from_id(id)
end
def parse_arguments(args = {})
if args[:assignee_id]
args[:assignee_id] = GitlabSchema.parse_gid(args[:assignee_id], expected_type: ::User).model_id
end
if args[:milestone_id]
args[:milestone_id] = GitlabSchema.parse_gid(args[:milestone_id], expected_type: ::Milestone).model_id
end
args
end
end
end
end
---
title: Add ability to update issue board configuration options using GraphQL
merge_request: 38413
author:
type: added
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Mutations::Boards::Update do
let_it_be(:project) { create(:project) }
let_it_be(:user) { create(:user) }
let_it_be(:board) { create(:board, project: project) }
let_it_be(:milestone) { create(:milestone, project: project) }
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) }
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) }
context 'when the user cannot admin the board' do
it 'raises an error' do
expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
end
end
context 'when user can update board' do
before do
board.resource_parent.add_reporter(user)
end
it 'updates issue with correct values' do
expected_attributes = {
name: 'Test board 1',
hide_backlog_list: true,
hide_closed_list: true,
weight: 3,
assignee: user,
milestone: milestone
}
subject
expect(board.reload).to have_attributes(expected_attributes)
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