Commit 42e5487b authored by Eugenia Grieff's avatar Eugenia Grieff

Add mutation for changing due date of an issue

Add mutation base for Issues
Update graphQL docs
Add specs for IssueSetDueDate mutation
Update GraphQL schema
parent 4e3839d8
# frozen_string_literal: true
module Mutations
module Issues
class Base < BaseMutation
include Mutations::ResolvesProject
argument :project_path, GraphQL::ID_TYPE,
required: true,
description: "The project the issue to mutate is in"
argument :iid, GraphQL::STRING_TYPE,
required: true,
description: "The iid of the issue to mutate"
field :issue,
Types::IssueType,
null: true,
description: "The issue after mutation"
authorize :update_issue
private
def find_object(project_path:, iid:)
project = resolve_project(full_path: project_path)
resolver = Resolvers::IssuesResolver
.single.new(object: project, context: context)
resolver.resolve(iid: iid)
end
end
end
end
# frozen_string_literal: true
module Mutations
module Issues
class SetDueDate < Base
graphql_name 'IssueSetDueDate'
argument :due_date,
Types::TimeType,
required: true,
description: 'The desired due date for the issue'
def resolve(project_path:, iid:, due_date:)
issue = authorized_find!(project_path: project_path, iid: iid)
project = issue.project
::Issues::UpdateService.new(project, current_user, due_date: due_date)
.execute(issue)
{
issue: issue,
errors: issue.errors.full_messages
}
end
end
end
end
...@@ -9,6 +9,7 @@ module Types ...@@ -9,6 +9,7 @@ module Types
mount_mutation Mutations::AwardEmojis::Add mount_mutation Mutations::AwardEmojis::Add
mount_mutation Mutations::AwardEmojis::Remove mount_mutation Mutations::AwardEmojis::Remove
mount_mutation Mutations::AwardEmojis::Toggle mount_mutation Mutations::AwardEmojis::Toggle
mount_mutation Mutations::Issues::SetDueDate
mount_mutation Mutations::MergeRequests::SetLabels mount_mutation Mutations::MergeRequests::SetLabels
mount_mutation Mutations::MergeRequests::SetLocked mount_mutation Mutations::MergeRequests::SetLocked
mount_mutation Mutations::MergeRequests::SetMilestone mount_mutation Mutations::MergeRequests::SetMilestone
......
---
title: Add GraphQL mutation for changing due date of an issue
merge_request: 20577
author:
type: added
...@@ -2518,6 +2518,51 @@ type IssuePermissions { ...@@ -2518,6 +2518,51 @@ type IssuePermissions {
updateIssue: Boolean! updateIssue: Boolean!
} }
"""
Autogenerated input type of IssueSetDueDate
"""
input IssueSetDueDateInput {
"""
A unique identifier for the client performing the mutation.
"""
clientMutationId: String
"""
The desired due date for the issue
"""
dueDate: Time!
"""
The iid of the issue to mutate
"""
iid: String!
"""
The project the issue to mutate is in
"""
projectPath: ID!
}
"""
Autogenerated return type of IssueSetDueDate
"""
type IssueSetDueDatePayload {
"""
A unique identifier for the client performing the mutation.
"""
clientMutationId: String
"""
Reasons why the mutation failed.
"""
errors: [String!]!
"""
The issue after mutation
"""
issue: Issue
}
""" """
Values for sorting issues Values for sorting issues
""" """
...@@ -3511,6 +3556,7 @@ type Mutation { ...@@ -3511,6 +3556,7 @@ type Mutation {
destroyNote(input: DestroyNoteInput!): DestroyNotePayload destroyNote(input: DestroyNoteInput!): DestroyNotePayload
epicSetSubscription(input: EpicSetSubscriptionInput!): EpicSetSubscriptionPayload epicSetSubscription(input: EpicSetSubscriptionInput!): EpicSetSubscriptionPayload
epicTreeReorder(input: EpicTreeReorderInput!): EpicTreeReorderPayload epicTreeReorder(input: EpicTreeReorderInput!): EpicTreeReorderPayload
issueSetDueDate(input: IssueSetDueDateInput!): IssueSetDueDatePayload
mergeRequestSetAssignees(input: MergeRequestSetAssigneesInput!): MergeRequestSetAssigneesPayload mergeRequestSetAssignees(input: MergeRequestSetAssigneesInput!): MergeRequestSetAssigneesPayload
mergeRequestSetLabels(input: MergeRequestSetLabelsInput!): MergeRequestSetLabelsPayload mergeRequestSetLabels(input: MergeRequestSetLabelsInput!): MergeRequestSetLabelsPayload
mergeRequestSetLocked(input: MergeRequestSetLockedInput!): MergeRequestSetLockedPayload mergeRequestSetLocked(input: MergeRequestSetLockedInput!): MergeRequestSetLockedPayload
......
...@@ -14112,6 +14112,33 @@ ...@@ -14112,6 +14112,33 @@
"isDeprecated": false, "isDeprecated": false,
"deprecationReason": null "deprecationReason": null
}, },
{
"name": "issueSetDueDate",
"description": null,
"args": [
{
"name": "input",
"description": null,
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "INPUT_OBJECT",
"name": "IssueSetDueDateInput",
"ofType": null
}
},
"defaultValue": null
}
],
"type": {
"kind": "OBJECT",
"name": "IssueSetDueDatePayload",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{ {
"name": "mergeRequestSetAssignees", "name": "mergeRequestSetAssignees",
"description": null, "description": null,
...@@ -14958,6 +14985,136 @@ ...@@ -14958,6 +14985,136 @@
"enumValues": null, "enumValues": null,
"possibleTypes": null "possibleTypes": null
}, },
{
"kind": "OBJECT",
"name": "IssueSetDueDatePayload",
"description": "Autogenerated return type of IssueSetDueDate",
"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": "issue",
"description": "The issue after mutation",
"args": [
],
"type": {
"kind": "OBJECT",
"name": "Issue",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [
],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "INPUT_OBJECT",
"name": "IssueSetDueDateInput",
"description": "Autogenerated input type of IssueSetDueDate",
"fields": null,
"inputFields": [
{
"name": "projectPath",
"description": "The project the issue to mutate is in",
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "ID",
"ofType": null
}
},
"defaultValue": null
},
{
"name": "iid",
"description": "The iid of the issue to mutate",
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
},
"defaultValue": null
},
{
"name": "dueDate",
"description": "The desired due date for the issue",
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "Time",
"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", "kind": "OBJECT",
"name": "MergeRequestSetLabelsPayload", "name": "MergeRequestSetLabelsPayload",
......
...@@ -375,6 +375,14 @@ The API can be explored interactively using the [GraphiQL IDE](../index.md#graph ...@@ -375,6 +375,14 @@ The API can be explored interactively using the [GraphiQL IDE](../index.md#graph
| `createDesign` | Boolean! | Whether or not a user can perform `create_design` on this resource | | `createDesign` | Boolean! | Whether or not a user can perform `create_design` on this resource |
| `destroyDesign` | Boolean! | Whether or not a user can perform `destroy_design` on this resource | | `destroyDesign` | Boolean! | Whether or not a user can perform `destroy_design` on this resource |
### IssueSetDueDatePayload
| Name | Type | Description |
| --- | ---- | ---------- |
| `clientMutationId` | String | A unique identifier for the client performing the mutation. |
| `errors` | String! => Array | Reasons why the mutation failed. |
| `issue` | Issue | The issue after mutation |
### Label ### Label
| Name | Type | Description | | Name | Type | Description |
......
# frozen_string_literal: true
require 'spec_helper'
describe Mutations::Issues::SetDueDate do
let(:issue) { create(:issue) }
let(:user) { create(:user) }
subject(:mutation) { described_class.new(object: nil, context: { current_user: user }) }
describe '#resolve' do
let(:due_date) { 2.days.since }
let(:mutated_issue) { subject[:issue] }
subject { mutation.resolve(project_path: issue.project.full_path, iid: issue.iid, due_date: due_date) }
it 'raises an error if the resource is not accessible to the user' do
expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
end
context 'when the user can update the issue' do
before do
issue.project.add_developer(user)
end
it 'returns the issue with updated due date' do
expect(mutated_issue).to eq(issue)
expect(mutated_issue.due_date).to eq(Date.today + 2.days)
expect(subject[:errors]).to be_empty
end
context 'when passing incorrect due date value' do
let(:due_date) { 'test' }
it 'does not update due date' do
expect(mutated_issue.due_date).to eq(issue.due_date)
end
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
describe 'Setting Due Date of an issue' do
include GraphqlHelpers
let(:current_user) { create(:user) }
let(:issue) { create(:issue) }
let(:project) { issue.project }
let(:input) { { due_date: 2.days.since } }
let(:mutation) do
variables = {
project_path: project.full_path,
iid: issue.iid.to_s
}
graphql_mutation(:issue_set_due_date, variables.merge(input),
<<-QL.strip_heredoc
clientMutationId
errors
issue {
iid
dueDate
}
QL
)
end
def mutation_response
graphql_mutation_response(:issue_set_due_date)
end
before do
project.add_developer(current_user)
end
it 'returns an error if the user is not allowed to update the issue' do
error = "The resource that you are attempting to access does not exist or you don't have permission to perform this action"
post_graphql_mutation(mutation, current_user: create(:user))
expect(graphql_errors).to include(a_hash_including('message' => error))
end
it 'updates the issue due date' do
post_graphql_mutation(mutation, current_user: current_user)
expect(response).to have_gitlab_http_status(:success)
expect(mutation_response['issue']['dueDate']).to eq(2.days.since.to_date.to_s)
end
context 'when passing due date without a date value' do
let(:input) { { due_date: 'test' } }
it 'returns internal server error' do
post_graphql_mutation(mutation, current_user: current_user)
expect(graphql_errors).to include(a_hash_including('message' => 'Internal server error'))
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