Commit d3c54296 authored by Eugenia Grieff's avatar Eugenia Grieff

Add GraphQL mutations for Issues

Add support for setting issue weight
Add support for setting issue confidentiality
Add specs for new mutations
Update GraphQL schema
parent d9019477
# frozen_string_literal: true
module Mutations
module Issues
class SetConfidential < Base
graphql_name 'IssueSetConfidential'
argument :confidential,
GraphQL::BOOLEAN_TYPE,
required: true,
description: 'Whether or not to set the issue as a confidential.'
def resolve(project_path:, iid:, confidential:)
issue = authorized_find!(project_path: project_path, iid: iid)
project = issue.project
::Issues::UpdateService.new(project, current_user, confidential: confidential)
.execute(issue)
{
issue: issue,
errors: issue.errors.full_messages
}
end
end
end
end
......@@ -9,6 +9,7 @@ module Types
mount_mutation Mutations::AwardEmojis::Add
mount_mutation Mutations::AwardEmojis::Remove
mount_mutation Mutations::AwardEmojis::Toggle
mount_mutation Mutations::Issues::SetConfidential
mount_mutation Mutations::Issues::SetDueDate
mount_mutation Mutations::MergeRequests::SetLabels
mount_mutation Mutations::MergeRequests::SetLocked
......
---
title: Add GraphQL mutation for setting an issue as confidential
merge_request: 20785
author:
type: added
......@@ -2518,6 +2518,51 @@ type IssuePermissions {
updateIssue: Boolean!
}
"""
Autogenerated input type of IssueSetConfidential
"""
input IssueSetConfidentialInput {
"""
A unique identifier for the client performing the mutation.
"""
clientMutationId: String
"""
Whether or not to set the issue as a confidential.
"""
confidential: Boolean!
"""
The iid of the issue to mutate
"""
iid: String!
"""
The project the issue to mutate is in
"""
projectPath: ID!
}
"""
Autogenerated return type of IssueSetConfidential
"""
type IssueSetConfidentialPayload {
"""
A unique identifier for the client performing the mutation.
"""
clientMutationId: String
"""
Reasons why the mutation failed.
"""
errors: [String!]!
"""
The issue after mutation
"""
issue: Issue
}
"""
Autogenerated input type of IssueSetDueDate
"""
......@@ -3556,6 +3601,7 @@ type Mutation {
destroyNote(input: DestroyNoteInput!): DestroyNotePayload
epicSetSubscription(input: EpicSetSubscriptionInput!): EpicSetSubscriptionPayload
epicTreeReorder(input: EpicTreeReorderInput!): EpicTreeReorderPayload
issueSetConfidential(input: IssueSetConfidentialInput!): IssueSetConfidentialPayload
issueSetDueDate(input: IssueSetDueDateInput!): IssueSetDueDatePayload
mergeRequestSetAssignees(input: MergeRequestSetAssigneesInput!): MergeRequestSetAssigneesPayload
mergeRequestSetLabels(input: MergeRequestSetLabelsInput!): MergeRequestSetLabelsPayload
......
......@@ -14112,6 +14112,33 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "issueSetConfidential",
"description": null,
"args": [
{
"name": "input",
"description": null,
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "INPUT_OBJECT",
"name": "IssueSetConfidentialInput",
"ofType": null
}
},
"defaultValue": null
}
],
"type": {
"kind": "OBJECT",
"name": "IssueSetConfidentialPayload",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "issueSetDueDate",
"description": null,
......@@ -14985,6 +15012,136 @@
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "IssueSetConfidentialPayload",
"description": "Autogenerated return type of IssueSetConfidential",
"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": "IssueSetConfidentialInput",
"description": "Autogenerated input type of IssueSetConfidential",
"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": "confidential",
"description": "Whether or not to set the issue as a confidential.",
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "Boolean",
"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": "IssueSetDueDatePayload",
......
# frozen_string_literal: true
require 'spec_helper'
describe Mutations::Issues::SetConfidential do
let(:issue) { create(:issue) }
let(:user) { create(:user) }
subject(:mutation) { described_class.new(object: nil, context: { current_user: user }) }
describe '#resolve' do
let(:confidential) { true }
let(:mutated_issue) { subject[:issue] }
subject { mutation.resolve(project_path: issue.project.full_path, iid: issue.iid, confidential: confidential) }
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 as confidential' do
expect(mutated_issue).to eq(issue)
expect(mutated_issue.confidential).to be_truthy
expect(subject[:errors]).to be_empty
end
context 'when passing confidential as false' do
let(:confidential) { false }
it 'updates the issue confidentiality to false' do
expect(mutated_issue.confidential).to be_falsey
end
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
describe 'Setting an issue as confidential' do
include GraphqlHelpers
let(:current_user) { create(:user) }
let(:issue) { create(:issue) }
let(:project) { issue.project }
let(:input) { { confidential: true } }
let(:mutation) do
variables = {
project_path: project.full_path,
iid: issue.iid.to_s
}
graphql_mutation(:issue_set_confidential, variables.merge(input),
<<-QL.strip_heredoc
clientMutationId
errors
issue {
iid
confidential
}
QL
)
end
def mutation_response
graphql_mutation_response(:issue_set_confidential)
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 confidentiality' do
post_graphql_mutation(mutation, current_user: current_user)
expect(response).to have_gitlab_http_status(:success)
expect(mutation_response['issue']['confidential']).to be_truthy
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