Commit 5f304f1f authored by Nick Thomas's avatar Nick Thomas

Merge branch 'ajk-gql-mr-update' into 'master'

[GraphQL] Add mutation to update merge requests

See merge request gitlab-org/gitlab!34748
parents f01541bf bd797d72
# frozen_string_literal: true
module Mutations
module MergeRequests
class Update < Base
graphql_name 'MergeRequestUpdate'
description 'Update attributes of a merge request'
argument :title, GraphQL::STRING_TYPE,
required: false,
description: copy_field_description(Types::MergeRequestType, :title)
argument :target_branch, GraphQL::STRING_TYPE,
required: false,
description: copy_field_description(Types::MergeRequestType, :target_branch)
argument :description, GraphQL::STRING_TYPE,
required: false,
description: copy_field_description(Types::MergeRequestType, :description)
def resolve(args)
merge_request = authorized_find!(args.slice(:project_path, :iid))
attributes = args.slice(:title, :description, :target_branch).compact
::MergeRequests::UpdateService
.new(merge_request.project, current_user, attributes)
.execute(merge_request)
errors = errors_on_object(merge_request)
{
merge_request: merge_request.reset,
errors: errors
}
end
end
end
end
...@@ -20,6 +20,7 @@ module Types ...@@ -20,6 +20,7 @@ module Types
mount_mutation Mutations::Issues::SetDueDate mount_mutation Mutations::Issues::SetDueDate
mount_mutation Mutations::Issues::Update mount_mutation Mutations::Issues::Update
mount_mutation Mutations::MergeRequests::Create mount_mutation Mutations::MergeRequests::Create
mount_mutation Mutations::MergeRequests::Update
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 mutation to update merge requests
merge_request: 34748
author:
type: added
...@@ -7321,6 +7321,61 @@ enum MergeRequestState { ...@@ -7321,6 +7321,61 @@ enum MergeRequestState {
opened opened
} }
"""
Autogenerated input type of MergeRequestUpdate
"""
input MergeRequestUpdateInput {
"""
A unique identifier for the client performing the mutation.
"""
clientMutationId: String
"""
Description of the merge request (Markdown rendered as HTML for caching)
"""
description: String
"""
The iid of the merge request to mutate
"""
iid: String!
"""
The project the merge request to mutate is in
"""
projectPath: ID!
"""
Target branch of the merge request
"""
targetBranch: String
"""
Title of the merge request
"""
title: String
}
"""
Autogenerated return type of MergeRequestUpdate
"""
type MergeRequestUpdatePayload {
"""
A unique identifier for the client performing the mutation.
"""
clientMutationId: String
"""
Errors encountered during execution of the mutation.
"""
errors: [String!]!
"""
The merge request after mutation
"""
mergeRequest: MergeRequest
}
type Metadata { type Metadata {
""" """
Revision Revision
...@@ -7607,6 +7662,11 @@ type Mutation { ...@@ -7607,6 +7662,11 @@ type Mutation {
mergeRequestSetMilestone(input: MergeRequestSetMilestoneInput!): MergeRequestSetMilestonePayload mergeRequestSetMilestone(input: MergeRequestSetMilestoneInput!): MergeRequestSetMilestonePayload
mergeRequestSetSubscription(input: MergeRequestSetSubscriptionInput!): MergeRequestSetSubscriptionPayload mergeRequestSetSubscription(input: MergeRequestSetSubscriptionInput!): MergeRequestSetSubscriptionPayload
mergeRequestSetWip(input: MergeRequestSetWipInput!): MergeRequestSetWipPayload mergeRequestSetWip(input: MergeRequestSetWipInput!): MergeRequestSetWipPayload
"""
Update attributes of a merge request
"""
mergeRequestUpdate(input: MergeRequestUpdateInput!): MergeRequestUpdatePayload
removeAwardEmoji(input: RemoveAwardEmojiInput!): RemoveAwardEmojiPayload removeAwardEmoji(input: RemoveAwardEmojiInput!): RemoveAwardEmojiPayload
removeProjectFromSecurityDashboard(input: RemoveProjectFromSecurityDashboardInput!): RemoveProjectFromSecurityDashboardPayload removeProjectFromSecurityDashboard(input: RemoveProjectFromSecurityDashboardInput!): RemoveProjectFromSecurityDashboardPayload
runDastScan(input: RunDASTScanInput!): RunDASTScanPayload runDastScan(input: RunDASTScanInput!): RunDASTScanPayload
......
...@@ -20432,6 +20432,152 @@ ...@@ -20432,6 +20432,152 @@
], ],
"possibleTypes": null "possibleTypes": null
}, },
{
"kind": "INPUT_OBJECT",
"name": "MergeRequestUpdateInput",
"description": "Autogenerated input type of MergeRequestUpdate",
"fields": null,
"inputFields": [
{
"name": "projectPath",
"description": "The project the merge request 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 merge request to mutate",
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
},
"defaultValue": null
},
{
"name": "title",
"description": "Title of the merge request",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
},
{
"name": "targetBranch",
"description": "Target branch of the merge request",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
},
{
"name": "description",
"description": "Description of the merge request (Markdown rendered as HTML for caching)",
"type": {
"kind": "SCALAR",
"name": "String",
"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": "MergeRequestUpdatePayload",
"description": "Autogenerated return type of MergeRequestUpdate",
"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": "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
},
{
"name": "mergeRequest",
"description": "The merge request after mutation",
"args": [
],
"type": {
"kind": "OBJECT",
"name": "MergeRequest",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [
],
"enumValues": null,
"possibleTypes": null
},
{ {
"kind": "OBJECT", "kind": "OBJECT",
"name": "Metadata", "name": "Metadata",
...@@ -22266,6 +22412,33 @@ ...@@ -22266,6 +22412,33 @@
"isDeprecated": false, "isDeprecated": false,
"deprecationReason": null "deprecationReason": null
}, },
{
"name": "mergeRequestUpdate",
"description": "Update attributes of a merge request",
"args": [
{
"name": "input",
"description": null,
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "INPUT_OBJECT",
"name": "MergeRequestUpdateInput",
"ofType": null
}
},
"defaultValue": null
}
],
"type": {
"kind": "OBJECT",
"name": "MergeRequestUpdatePayload",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{ {
"name": "removeAwardEmoji", "name": "removeAwardEmoji",
"description": null, "description": null,
...@@ -1100,6 +1100,16 @@ Autogenerated return type of MergeRequestSetWip ...@@ -1100,6 +1100,16 @@ Autogenerated return type of MergeRequestSetWip
| `errors` | String! => Array | Errors encountered during execution of the mutation. | | `errors` | String! => Array | Errors encountered during execution of the mutation. |
| `mergeRequest` | MergeRequest | The merge request after mutation | | `mergeRequest` | MergeRequest | The merge request after mutation |
## MergeRequestUpdatePayload
Autogenerated return type of MergeRequestUpdate
| Name | Type | Description |
| --- | ---- | ---------- |
| `clientMutationId` | String | A unique identifier for the client performing the mutation. |
| `errors` | String! => Array | Errors encountered during execution of the mutation. |
| `mergeRequest` | MergeRequest | The merge request after mutation |
## Metadata ## Metadata
| Name | Type | Description | | Name | Type | Description |
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Mutations::MergeRequests::Update do
let(:merge_request) { create(:merge_request) }
let(:user) { create(:user) }
subject(:mutation) { described_class.new(object: nil, context: { current_user: user }, field: nil) }
specify { expect(described_class).to require_graphql_authorizations(:update_merge_request) }
describe '#resolve' do
let(:attributes) { { title: 'new title', description: 'new description', target_branch: 'new-branch' } }
let(:mutated_merge_request) { subject[:merge_request] }
subject do
mutation.resolve(project_path: merge_request.project.full_path, iid: merge_request.iid, **attributes)
end
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 merge request' do
before do
merge_request.project.add_developer(user)
end
it 'applies all attributes' do
expect(mutated_merge_request).to eq(merge_request)
expect(mutated_merge_request).to have_attributes(attributes)
expect(subject[:errors]).to be_empty
end
context 'the merge request is invalid' do
before do
merge_request.allow_broken = true
merge_request.update!(source_project: nil)
end
it 'returns error information, and changes were not applied' do
expect(mutated_merge_request).not_to have_attributes(attributes)
expect(subject[:errors]).not_to be_empty
end
end
context 'our change is invalid' do
let(:attributes) { { target_branch: 'this is not a branch' } }
it 'returns error information, and changes were not applied' do
expect(mutated_merge_request).not_to have_attributes(attributes)
expect(subject[:errors]).not_to be_empty
end
end
context 'when passing subset of attributes' do
let(:attributes) { { title: 'no, this title' } }
it 'only changes the mentioned attributes' do
expect { subject }.not_to change { merge_request.reset.description }
expect(mutated_merge_request).to have_attributes(attributes)
end
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