Commit aecc475a authored by Alexandru Croitor's avatar Alexandru Croitor

Allow iteration cadence argument on iteration creation

An iteration has to be linked to an iteration cadence. With
introduction of multiple cadences per group we need to be able
to specify to which cadence a new iteration should belong.

For now the iterations_cadence_id argument is optional, and we set
the iteration to the only cadence in the group.

The iterations_cadence_id argument would need to become required
once multiple iteration cadences per group are introduced.

Changelog: changed
parent 744edfc9
......@@ -1205,6 +1205,10 @@ Input type: `CreateIssueInput`
### `Mutation.createIteration`
WARNING:
**Deprecated** in 14.0.
Use iterationCreate.
Input type: `CreateIterationInput`
#### Arguments
......@@ -1215,6 +1219,7 @@ Input type: `CreateIterationInput`
| <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="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="mutationcreateiterationstartdate"></a>`startDate` | [`String`](#string) | The start date of the iteration. |
| <a id="mutationcreateiterationtitle"></a>`title` | [`String`](#string) | The title of the iteration. |
......@@ -2635,6 +2640,31 @@ Input type: `IterationCadenceUpdateInput`
| <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. |
### `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`
Input type: `IterationDeleteInput`
......
......@@ -6,13 +6,17 @@ module EE
extend ActiveSupport::Concern
prepended do
mount_mutation ::Mutations::Pipelines::RunDastScan, deprecated: { reason: 'Use DastOnDemandScanCreate', milestone: '13.4' }
mount_aliased_mutation 'DismissVulnerability',
::Mutations::Vulnerabilities::Dismiss,
deprecated: { reason: 'Use vulnerabilityDismiss', milestone: '13.5' }
mount_aliased_mutation 'RevertVulnerabilityToDetected',
::Mutations::Vulnerabilities::RevertToDetected,
deprecated: { reason: 'Use vulnerabilityRevertToDetected', milestone: '13.5' }
mount_mutation ::Mutations::Pipelines::RunDastScan,
deprecated: { reason: 'Use DastOnDemandScanCreate', milestone: '13.4' }
mount_aliased_mutation 'DismissVulnerability', ::Mutations::Vulnerabilities::Dismiss,
deprecated: { reason: 'Use vulnerabilityDismiss', milestone: '13.5' }
mount_aliased_mutation 'RevertVulnerabilityToDetected', ::Mutations::Vulnerabilities::RevertToDetected,
deprecated: { reason: 'Use vulnerabilityRevertToDetected', milestone: '13.5' }
mount_aliased_mutation 'CreateIteration', ::Mutations::Iterations::Create,
deprecated: { reason: 'Use iterationCreate', milestone: '14.0' }
end
end
end
......
......@@ -5,7 +5,7 @@ module Mutations
class Create < BaseMutation
include Mutations::ResolvesResourceParent
graphql_name 'CreateIteration'
graphql_name 'iterationCreate'
authorize :create_iteration
......@@ -14,6 +14,12 @@ module Mutations
null: true,
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,
GraphQL::STRING_TYPE,
required: false,
......@@ -35,10 +41,10 @@ module Mutations
description: 'The end date of the iteration.'
def resolve(args)
validate_arguments!(args)
parent = authorized_resource_parent_find!(args)
validate_arguments!(parent, args)
response = ::Iterations::CreateService.new(parent, current_user, args).execute
response_object = response.payload[:iteration] if response.success?
......@@ -52,10 +58,18 @@ module Mutations
private
def validate_arguments!(args)
def validate_arguments!(parent, args)
if args.except(:group_path, :project_path).empty?
raise Gitlab::Graphql::Errors::ArgumentError,
'The list of iteration attributes is empty'
raise Gitlab::Graphql::Errors::ArgumentError, '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
......
......@@ -7,6 +7,7 @@ RSpec.describe 'Creating an Iteration' do
let_it_be(:current_user) { create(:user) }
let_it_be(:group) { create(:group) }
let_it_be(:cadence) { create(:iterations_cadence, group: group)}
let(:start_date) { Time.now.strftime('%F') }
let(:end_date) { 1.day.from_now.strftime('%F') }
......@@ -65,41 +66,63 @@ RSpec.describe 'Creating an Iteration' do
stub_licensed_features(iterations: true)
end
it 'creates the iteration for a group' do
post_graphql_mutation(mutation, current_user: current_user)
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
post_graphql_mutation(mutation, current_user: current_user)
iteration_hash = mutation_response['iteration']
aggregate_failures do
expect(iteration_hash['title']).to eq('title')
expect(iteration_hash['description']).to eq('some description')
expect(iteration_hash['startDate']).to eq(start_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
iteration_hash = mutation_response['iteration']
aggregate_failures do
expect(iteration_hash['title']).to eq('title')
expect(iteration_hash['description']).to eq('some description')
expect(iteration_hash['startDate']).to eq(start_date)
expect(iteration_hash['dueDate']).to eq(end_date)
context 'and there are several iteration cadences in the group' do
let_it_be(:extra_cadence) { create(:iterations_cadence, group: group)}
it_behaves_like 'a mutation that returns top-level errors',
errors: ['Please provide iterations_cadence_id argument to assign iteration to respective cadence']
end
end
# Skipping creation of project level iterations.
# Pending https://gitlab.com/gitlab-org/gitlab/-/issues/299864
xcontext 'when a project_path is given' do
let_it_be(:project) { create(:project, namespace: group) }
let(:params) { { project_path: project.full_path } }
context 'when cadence provided' do
context 'with correct cadence' do
let_it_be(:extra_cadence) { create(:iterations_cadence, group: group)}
before do
project.add_developer(current_user)
end
before do
attributes.merge!(iterations_cadence_id: extra_cadence.to_global_id.to_s)
end
it 'creates the iteration for a project' do
allow_next_instance_of(Iteration) do |iteration|
allow(iteration).to receive(:skip_project_validation).and_return(true)
it 'creates the iteration for the cadence' do
post_graphql_mutation(mutation, current_user: current_user)
iteration_hash = mutation_response['iteration']
aggregate_failures do
expect(iteration_hash['title']).to eq('title')
expect(iteration_hash['description']).to eq('some description')
expect(iteration_hash['startDate']).to eq(start_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
post_graphql_mutation(mutation, current_user: current_user)
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
iteration_hash = mutation_response['iteration']
aggregate_failures do
expect(iteration_hash['title']).to eq('title')
expect(iteration_hash['description']).to eq('some description')
expect(iteration_hash['startDate']).to eq(start_date)
expect(iteration_hash['dueDate']).to eq(end_date)
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
......
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