Commit 6a3f50e4 authored by David Fernandez's avatar David Fernandez

Merge branch '296099_allow_setting_cadence_id_on_iteration_creation_mutation' into 'master'

Allow iteration cadence argument on iteration creation

See merge request gitlab-org/gitlab!63505
parents 914f669d aecc475a
...@@ -1227,6 +1227,10 @@ Input type: `CreateIssueInput` ...@@ -1227,6 +1227,10 @@ Input type: `CreateIssueInput`
### `Mutation.createIteration` ### `Mutation.createIteration`
WARNING:
**Deprecated** in 14.0.
Use iterationCreate.
Input type: `CreateIterationInput` Input type: `CreateIterationInput`
#### Arguments #### Arguments
...@@ -1237,6 +1241,7 @@ Input type: `CreateIterationInput` ...@@ -1237,6 +1241,7 @@ Input type: `CreateIterationInput`
| <a id="mutationcreateiterationdescription"></a>`description` | [`String`](#string) | The description of the iteration. | | <a id="mutationcreateiterationdescription"></a>`description` | [`String`](#string) | The description of the iteration. |
| <a id="mutationcreateiterationduedate"></a>`dueDate` | [`String`](#string) | The end date of the iteration. | | <a id="mutationcreateiterationduedate"></a>`dueDate` | [`String`](#string) | The end date of the iteration. |
| <a id="mutationcreateiterationgrouppath"></a>`groupPath` | [`ID`](#id) | Full path of the group with which the resource is associated. | | <a id="mutationcreateiterationgrouppath"></a>`groupPath` | [`ID`](#id) | Full path of the group with which the resource is associated. |
| <a id="mutationcreateiterationiterationscadenceid"></a>`iterationsCadenceId` | [`IterationsCadenceID`](#iterationscadenceid) | Global ID of the iterations cadence to be assigned to newly created iteration. |
| <a id="mutationcreateiterationprojectpath"></a>`projectPath` | [`ID`](#id) | Full path of the project with which the resource is associated. | | <a id="mutationcreateiterationprojectpath"></a>`projectPath` | [`ID`](#id) | Full path of the project with which the resource is associated. |
| <a id="mutationcreateiterationstartdate"></a>`startDate` | [`String`](#string) | The start date of the iteration. | | <a id="mutationcreateiterationstartdate"></a>`startDate` | [`String`](#string) | The start date of the iteration. |
| <a id="mutationcreateiterationtitle"></a>`title` | [`String`](#string) | The title of the iteration. | | <a id="mutationcreateiterationtitle"></a>`title` | [`String`](#string) | The title of the iteration. |
...@@ -2657,6 +2662,31 @@ Input type: `IterationCadenceUpdateInput` ...@@ -2657,6 +2662,31 @@ Input type: `IterationCadenceUpdateInput`
| <a id="mutationiterationcadenceupdateerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. | | <a id="mutationiterationcadenceupdateerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
| <a id="mutationiterationcadenceupdateiterationcadence"></a>`iterationCadence` | [`IterationCadence`](#iterationcadence) | The updated iteration cadence. | | <a id="mutationiterationcadenceupdateiterationcadence"></a>`iterationCadence` | [`IterationCadence`](#iterationcadence) | The updated iteration cadence. |
### `Mutation.iterationCreate`
Input type: `iterationCreateInput`
#### Arguments
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="mutationiterationcreateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationiterationcreatedescription"></a>`description` | [`String`](#string) | The description of the iteration. |
| <a id="mutationiterationcreateduedate"></a>`dueDate` | [`String`](#string) | The end date of the iteration. |
| <a id="mutationiterationcreategrouppath"></a>`groupPath` | [`ID`](#id) | Full path of the group with which the resource is associated. |
| <a id="mutationiterationcreateiterationscadenceid"></a>`iterationsCadenceId` | [`IterationsCadenceID`](#iterationscadenceid) | Global ID of the iterations cadence to be assigned to newly created iteration. |
| <a id="mutationiterationcreateprojectpath"></a>`projectPath` | [`ID`](#id) | Full path of the project with which the resource is associated. |
| <a id="mutationiterationcreatestartdate"></a>`startDate` | [`String`](#string) | The start date of the iteration. |
| <a id="mutationiterationcreatetitle"></a>`title` | [`String`](#string) | The title of the iteration. |
#### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="mutationiterationcreateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationiterationcreateerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
| <a id="mutationiterationcreateiteration"></a>`iteration` | [`Iteration`](#iteration) | The created iteration. |
### `Mutation.iterationDelete` ### `Mutation.iterationDelete`
Input type: `IterationDeleteInput` Input type: `IterationDeleteInput`
......
...@@ -6,13 +6,17 @@ module EE ...@@ -6,13 +6,17 @@ module EE
extend ActiveSupport::Concern extend ActiveSupport::Concern
prepended do prepended do
mount_mutation ::Mutations::Pipelines::RunDastScan, deprecated: { reason: 'Use DastOnDemandScanCreate', milestone: '13.4' } mount_mutation ::Mutations::Pipelines::RunDastScan,
mount_aliased_mutation 'DismissVulnerability', deprecated: { reason: 'Use DastOnDemandScanCreate', milestone: '13.4' }
::Mutations::Vulnerabilities::Dismiss,
mount_aliased_mutation 'DismissVulnerability', ::Mutations::Vulnerabilities::Dismiss,
deprecated: { reason: 'Use vulnerabilityDismiss', milestone: '13.5' } deprecated: { reason: 'Use vulnerabilityDismiss', milestone: '13.5' }
mount_aliased_mutation 'RevertVulnerabilityToDetected',
::Mutations::Vulnerabilities::RevertToDetected, mount_aliased_mutation 'RevertVulnerabilityToDetected', ::Mutations::Vulnerabilities::RevertToDetected,
deprecated: { reason: 'Use vulnerabilityRevertToDetected', milestone: '13.5' } deprecated: { reason: 'Use vulnerabilityRevertToDetected', milestone: '13.5' }
mount_aliased_mutation 'CreateIteration', ::Mutations::Iterations::Create,
deprecated: { reason: 'Use iterationCreate', milestone: '14.0' }
end end
end end
end end
......
...@@ -5,7 +5,7 @@ module Mutations ...@@ -5,7 +5,7 @@ module Mutations
class Create < BaseMutation class Create < BaseMutation
include Mutations::ResolvesResourceParent include Mutations::ResolvesResourceParent
graphql_name 'CreateIteration' graphql_name 'iterationCreate'
authorize :create_iteration authorize :create_iteration
...@@ -14,6 +14,12 @@ module Mutations ...@@ -14,6 +14,12 @@ module Mutations
null: true, null: true,
description: 'The created iteration.' description: 'The created iteration.'
argument :iterations_cadence_id,
::Types::GlobalIDType[::Iterations::Cadence],
loads: ::Types::Iterations::CadenceType,
required: false,
description: 'Global ID of the iterations cadence to be assigned to newly created iteration.'
argument :title, argument :title,
GraphQL::STRING_TYPE, GraphQL::STRING_TYPE,
required: false, required: false,
...@@ -35,10 +41,10 @@ module Mutations ...@@ -35,10 +41,10 @@ module Mutations
description: 'The end date of the iteration.' description: 'The end date of the iteration.'
def resolve(args) def resolve(args)
validate_arguments!(args)
parent = authorized_resource_parent_find!(args) parent = authorized_resource_parent_find!(args)
validate_arguments!(parent, args)
response = ::Iterations::CreateService.new(parent, current_user, args).execute response = ::Iterations::CreateService.new(parent, current_user, args).execute
response_object = response.payload[:iteration] if response.success? response_object = response.payload[:iteration] if response.success?
...@@ -52,10 +58,18 @@ module Mutations ...@@ -52,10 +58,18 @@ module Mutations
private private
def validate_arguments!(args) def validate_arguments!(parent, args)
if args.except(:group_path, :project_path).empty? if args.except(:group_path, :project_path).empty?
raise Gitlab::Graphql::Errors::ArgumentError, raise Gitlab::Graphql::Errors::ArgumentError, 'The list of iteration attributes is empty'
'The list of iteration attributes is empty' end
# Currently there is a single iteration cadence per group, so if `iterations_cadence_id` argument is not provided
# we assign iteration to the only cadence in the group(see `Iteration#set_iterations_cadence`).
# Once we introduce cadence CRUD support we need to specify to which iteration cadence a given iteration
# belongs if there are more than once cadence in the group. Eventually `iterations_cadence_id` argument should
# become required and there should be no need for group_path argument for iteration.
if args[:iterations_cadence].blank? && parent.iterations_cadences.count > 1
raise Gitlab::Graphql::Errors::ArgumentError, 'Please provide iterations_cadence_id argument to assign iteration to respective cadence'
end end
end end
end end
......
...@@ -7,6 +7,7 @@ RSpec.describe 'Creating an Iteration' do ...@@ -7,6 +7,7 @@ RSpec.describe 'Creating an Iteration' do
let_it_be(:current_user) { create(:user) } let_it_be(:current_user) { create(:user) }
let_it_be(:group) { create(:group) } let_it_be(:group) { create(:group) }
let_it_be(:cadence) { create(:iterations_cadence, group: group)}
let(:start_date) { Time.now.strftime('%F') } let(:start_date) { Time.now.strftime('%F') }
let(:end_date) { 1.day.from_now.strftime('%F') } let(:end_date) { 1.day.from_now.strftime('%F') }
...@@ -65,6 +66,8 @@ RSpec.describe 'Creating an Iteration' do ...@@ -65,6 +66,8 @@ RSpec.describe 'Creating an Iteration' do
stub_licensed_features(iterations: true) stub_licensed_features(iterations: true)
end end
context 'when iteration cadence id is not provided' do
context 'and there is only one iteration cadence in the group' do
it 'creates the iteration for a group' do it 'creates the iteration for a group' do
post_graphql_mutation(mutation, current_user: current_user) post_graphql_mutation(mutation, current_user: current_user)
...@@ -74,24 +77,28 @@ RSpec.describe 'Creating an Iteration' do ...@@ -74,24 +77,28 @@ RSpec.describe 'Creating an Iteration' do
expect(iteration_hash['description']).to eq('some description') expect(iteration_hash['description']).to eq('some description')
expect(iteration_hash['startDate']).to eq(start_date) expect(iteration_hash['startDate']).to eq(start_date)
expect(iteration_hash['dueDate']).to eq(end_date) expect(iteration_hash['dueDate']).to eq(end_date)
expect(iteration_hash['iterationCadence']['id']).to eq(group.iterations_cadences.first.to_global_id.to_s)
end
end end
end end
# Skipping creation of project level iterations. context 'and there are several iteration cadences in the group' do
# Pending https://gitlab.com/gitlab-org/gitlab/-/issues/299864 let_it_be(:extra_cadence) { create(:iterations_cadence, group: group)}
xcontext 'when a project_path is given' do
let_it_be(:project) { create(:project, namespace: group) }
let(:params) { { project_path: project.full_path } }
before do it_behaves_like 'a mutation that returns top-level errors',
project.add_developer(current_user) errors: ['Please provide iterations_cadence_id argument to assign iteration to respective cadence']
end
end end
it 'creates the iteration for a project' do context 'when cadence provided' do
allow_next_instance_of(Iteration) do |iteration| context 'with correct cadence' do
allow(iteration).to receive(:skip_project_validation).and_return(true) let_it_be(:extra_cadence) { create(:iterations_cadence, group: group)}
before do
attributes.merge!(iterations_cadence_id: extra_cadence.to_global_id.to_s)
end end
it 'creates the iteration for the cadence' do
post_graphql_mutation(mutation, current_user: current_user) post_graphql_mutation(mutation, current_user: current_user)
iteration_hash = mutation_response['iteration'] iteration_hash = mutation_response['iteration']
...@@ -100,6 +107,22 @@ RSpec.describe 'Creating an Iteration' do ...@@ -100,6 +107,22 @@ RSpec.describe 'Creating an Iteration' do
expect(iteration_hash['description']).to eq('some description') expect(iteration_hash['description']).to eq('some description')
expect(iteration_hash['startDate']).to eq(start_date) expect(iteration_hash['startDate']).to eq(start_date)
expect(iteration_hash['dueDate']).to eq(end_date) expect(iteration_hash['dueDate']).to eq(end_date)
expect(iteration_hash['iterationCadence']['id']).to eq(extra_cadence.to_global_id.to_s)
end
end
end
context 'with non-existing cadence and a signle cadence in the group' do
let(:non_existing_cadence_id) { "gid://gitlab/Iterations::Cadence/#{non_existing_record_id}" }
before do
attributes.merge!(iterations_cadence_id: non_existing_cadence_id)
end
it_behaves_like 'a mutation that returns top-level errors' do
let(:match_errors) do
contain_exactly(include("No object found for `iterationsCadenceId: "))
end
end 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