Commit 58db64fe authored by Patrick Derichs's avatar Patrick Derichs

Add mutation to update limit metric settings on board lists

Also update schema and docs.
parent ca720e7e
......@@ -411,6 +411,56 @@ type BoardListEdge {
node: BoardList
}
"""
Autogenerated input type of BoardListUpdateLimitMetrics
"""
input BoardListUpdateLimitMetricsInput {
"""
A unique identifier for the client performing the mutation.
"""
clientMutationId: String
"""
The new limit metric type for the list.
"""
limitMetric: ListLimitMetric
"""
The global ID of the list.
"""
listId: ID!
"""
The new maximum issue count limit.
"""
maxIssueCount: Int
"""
The new maximum issue weight limit.
"""
maxIssueWeight: Int
}
"""
Autogenerated return type of BoardListUpdateLimitMetrics
"""
type BoardListUpdateLimitMetricsPayload {
"""
A unique identifier for the client performing the mutation.
"""
clientMutationId: String
"""
Reasons why the mutation failed.
"""
errors: [String!]!
"""
The updated list
"""
list: BoardList
}
type Commit {
"""
Author of the commit
......@@ -5689,6 +5739,7 @@ enum MoveType {
type Mutation {
addAwardEmoji(input: AddAwardEmojiInput!): AddAwardEmojiPayload
adminSidekiqQueuesDeleteJobs(input: AdminSidekiqQueuesDeleteJobsInput!): AdminSidekiqQueuesDeleteJobsPayload
boardListUpdateLimitMetrics(input: BoardListUpdateLimitMetricsInput!): BoardListUpdateLimitMetricsPayload
createDiffNote(input: CreateDiffNoteInput!): CreateDiffNotePayload
createEpic(input: CreateEpicInput!): CreateEpicPayload
createImageDiffNote(input: CreateImageDiffNoteInput!): CreateImageDiffNotePayload
......
......@@ -1262,6 +1262,138 @@
"enumValues": null,
"possibleTypes": null
},
{
"kind": "INPUT_OBJECT",
"name": "BoardListUpdateLimitMetricsInput",
"description": "Autogenerated input type of BoardListUpdateLimitMetrics",
"fields": null,
"inputFields": [
{
"name": "listId",
"description": "The global ID of the list.",
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "ID",
"ofType": null
}
},
"defaultValue": null
},
{
"name": "limitMetric",
"description": "The new limit metric type for the list.",
"type": {
"kind": "ENUM",
"name": "ListLimitMetric",
"ofType": null
},
"defaultValue": null
},
{
"name": "maxIssueCount",
"description": "The new maximum issue count limit.",
"type": {
"kind": "SCALAR",
"name": "Int",
"ofType": null
},
"defaultValue": null
},
{
"name": "maxIssueWeight",
"description": "The new maximum issue weight limit.",
"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": "BoardListUpdateLimitMetricsPayload",
"description": "Autogenerated return type of BoardListUpdateLimitMetrics",
"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": "Reasons why the mutation failed.",
"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": "The updated list",
"args": [
],
"type": {
"kind": "OBJECT",
"name": "BoardList",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [
],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "SCALAR",
"name": "Boolean",
......@@ -16352,6 +16484,33 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "boardListUpdateLimitMetrics",
"description": null,
"args": [
{
"name": "input",
"description": null,
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "INPUT_OBJECT",
"name": "BoardListUpdateLimitMetricsInput",
"ofType": null
}
},
"defaultValue": null
}
],
"type": {
"kind": "OBJECT",
"name": "BoardListUpdateLimitMetricsPayload",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "createDiffNote",
"description": null,
......
......@@ -97,6 +97,16 @@ Represents a list for an issue board
| `position` | Int | Position of list within the board |
| `title` | String! | Title of the list |
## BoardListUpdateLimitMetricsPayload
Autogenerated return type of BoardListUpdateLimitMetrics
| Name | Type | Description |
| --- | ---- | ---------- |
| `clientMutationId` | String | A unique identifier for the client performing the mutation. |
| `errors` | String! => Array | Reasons why the mutation failed. |
| `list` | BoardList | The updated list |
## Commit
| Name | Type | Description |
......
......@@ -17,6 +17,7 @@ module EE
mount_mutation ::Mutations::Requirements::Create
mount_mutation ::Mutations::Requirements::Update
mount_mutation ::Mutations::Vulnerabilities::Dismiss
mount_mutation ::Mutations::Boards::Lists::UpdateLimitMetrics
end
end
end
......
# frozen_string_literal: true
module Mutations
module Boards
module Lists
class UpdateLimitMetrics < ::Mutations::BaseMutation
graphql_name 'BoardListUpdateLimitMetrics'
argument :list_id,
GraphQL::ID_TYPE,
required: true,
description: 'The global ID of the list.'
argument :limit_metric,
EE::Types::ListLimitMetricEnum,
required: false,
description: 'The new limit metric type for the list.'
argument :max_issue_count,
GraphQL::INT_TYPE,
required: false,
description: 'The new maximum issue count limit.'
argument :max_issue_weight,
GraphQL::INT_TYPE,
required: false,
description: 'The new maximum issue weight limit.'
field :list,
::Types::BoardListType,
null: true,
description: 'The updated list'
def ready?(**args)
if limit_metric_settings_of(args).blank?
raise Gitlab::Graphql::Errors::ArgumentError,
'At least one of the arguments limitMetric, maxIssueCount or maxIssueWeight is required'
end
super
end
def resolve(**args)
find_list_by_args!(args)
update_result = update_list(args)
{
list: update_result[:list],
errors: list.errors.full_messages
}
end
private
attr_reader :list
def find_list_by_args!(args)
@list ||= find_list_by_global_id(args[:list_id])
raise_resource_not_available_error! unless list
authorize_admin_rights!
end
def update_list(args)
service = ::Boards::Lists::UpdateService.new(board, current_user, limit_metric_settings_of(args))
service.execute(list)
end
def authorize_admin_rights!
raise_resource_not_available_error! unless Ability.allowed?(current_user, :admin_list, board)
end
def find_list_by_global_id(gid)
parsed_gid = GlobalID.parse(gid)
id = parsed_gid.model_id.to_i if list_accessible?(parsed_gid)
return unless id
List.find_by_id(id)
end
def list_accessible?(gid)
gid.app == GlobalID.app && list?(gid)
end
def list?(gid)
model_class = gid&.model_class
return unless model_class
model_class <= List
end
def board
@board ||= list.board
end
def limit_metric_settings_of(args)
args.slice(:limit_metric, :max_issue_count, :max_issue_weight)
end
end
end
end
end
---
title: Add GraphQL mutation to update limit metric settings on board lists
merge_request: 29897
author:
type: added
# frozen_string_literal: true
require 'spec_helper'
describe Mutations::Boards::Lists::UpdateLimitMetrics do
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_it_be(:list) { create(:list, board: board) }
let(:current_user) { user }
let(:mutation) { described_class.new(object: nil, context: { current_user: current_user }, field: nil) }
let(:list_update_params) { { limit_metric: :all_metrics, max_issue_count: 10, max_issue_weight: 50 } }
before_all do
group.add_maintainer(user)
group.add_guest(guest)
end
subject { mutation.resolve(list_id: list.to_global_id.to_s, **list_update_params) }
describe '#ready?' do
it 'raises an error if required arguments are missing' do
expect { mutation.ready?({ list_id: 'some id' }) }.to raise_error(Gitlab::Graphql::Errors::ArgumentError, "At least one of the arguments " \
"limitMetric, maxIssueCount or maxIssueWeight is required")
end
end
describe '#resolve' do
context 'with admin rights' do
it 'updates the list as expected' do
subject
reloaded_list = list.reload
expect(reloaded_list.limit_metric).to eq('all_metrics')
expect(reloaded_list.max_issue_count).to eq(10)
expect(reloaded_list.max_issue_weight).to eq(50)
end
end
context 'without admin rights' do
let(:current_user) { guest }
it 'fails' do
expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
describe 'Update list limit metrics' 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(:list) { create(:list, board: board) }
let(:mutation_class) { Mutations::Boards::Lists::UpdateLimitMetrics }
let(:mutation_name) { mutation_class.graphql_name }
let(:mutation_result_identifier) { mutation_name.camelize(:lower) }
before_all do
group.add_maintainer(user)
end
it 'returns an error if the user is not allowed to update the issue' do
post_graphql_mutation(mutation('all_metrics'), current_user: create(:user))
expect(graphql_errors).to include(a_hash_including('message' => "The resource that you are attempting to access does " \
"not exist or you don't have permission to perform this action"))
end
it 'returns an error if the list cannot be found' do
list_gid = ::URI::GID.parse("gid://#{GlobalID.app}/List/0")
post_graphql_mutation(mutation('all_metrics', list_id: list_gid), current_user: create(:user))
expect(graphql_errors).to include(a_hash_including('message' => "The resource that you are attempting to access does " \
"not exist or you don't have permission to perform this action"))
end
it 'returns an error if the gid identifies another object' do
post_graphql_mutation(mutation('all_metrics', list_id: user.to_global_id.to_s), current_user: create(:user))
expect(graphql_errors).to include(a_hash_including('message' => "The resource that you are attempting to access does " \
"not exist or you don't have permission to perform this action"))
end
%w[all_metrics issue_count issue_weights].each do |metric|
it "updates the list limit metrics for limit metric #{metric}" do
post_graphql_mutation(mutation(metric), current_user: user)
expect(response).to have_gitlab_http_status(:success)
response_list = json_response['data'][mutation_result_identifier]['list']
expect(response_list['id']).to eq(list.to_global_id.to_s)
expect(response_list['limitMetric']).to eq(metric.to_s)
expect(response_list['maxIssueCount']).to eq(3)
expect(response_list['maxIssueWeight']).to eq(42)
expect_list_update(list, metric: metric, count: 3, weight: 42)
end
end
def list_update_params(metric)
{
list_id: list.to_global_id.to_s,
limit_metric: metric,
max_issue_count: 3,
max_issue_weight: 42
}
end
def mutation(metric, additional_params = {})
graphql_mutation(mutation_name, list_update_params(metric).merge(additional_params),
<<-QL.strip_heredoc
clientMutationId
list {
id, maxIssueCount, maxIssueWeight, limitMetric
}
errors
QL
)
end
def expect_list_update(list, metric:, count:, weight:)
reloaded_list = list.reload
expect(reloaded_list.limit_metric).to eq(metric)
expect(reloaded_list.max_issue_count).to eq(count)
expect(reloaded_list.max_issue_weight).to eq(weight)
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