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`
### `Mutation.createIteration`
WARNING:
**Deprecated** in 14.0.
Use iterationCreate.
Input type: `CreateIterationInput`
#### Arguments
......@@ -1237,6 +1241,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. |
......@@ -2657,6 +2662,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