Commit 3e16285d authored by Heinrich Lee Yu's avatar Heinrich Lee Yu

Merge branch 'revert-03b99718' into 'master'

Revert "Merge branch '300897-add-iteration-arguments-to-board-issue-creation-api' into 'master'"

See merge request gitlab-org/gitlab!71674
parents d6844d2e 3b71d015
...@@ -71,7 +71,7 @@ module Mutations ...@@ -71,7 +71,7 @@ module Mutations
def resolve(project_path:, **attributes) def resolve(project_path:, **attributes)
project = authorized_find!(project_path) project = authorized_find!(project_path)
params = build_create_issue_params(attributes.merge(author_id: current_user.id), project) params = build_create_issue_params(attributes.merge(author_id: current_user.id))
spam_params = ::Spam::SpamParams.new_from_request(request: context[:request]) spam_params = ::Spam::SpamParams.new_from_request(request: context[:request])
issue = ::Issues::CreateService.new(project: project, current_user: current_user, params: params, spam_params: spam_params).execute issue = ::Issues::CreateService.new(project: project, current_user: current_user, params: params, spam_params: spam_params).execute
...@@ -88,8 +88,7 @@ module Mutations ...@@ -88,8 +88,7 @@ module Mutations
private private
# _project argument is unused here, but it is necessary on the EE version of the method def build_create_issue_params(params)
def build_create_issue_params(params, _project)
params[:milestone_id] &&= params[:milestone_id]&.model_id params[:milestone_id] &&= params[:milestone_id]&.model_id
params[:assignee_ids] &&= params[:assignee_ids].map { |assignee_id| assignee_id&.model_id } params[:assignee_ids] &&= params[:assignee_ids].map { |assignee_id| assignee_id&.model_id }
params[:label_ids] &&= params[:label_ids].map { |label_id| label_id&.model_id } params[:label_ids] &&= params[:label_ids].map { |label_id| label_id&.model_id }
......
...@@ -2,8 +2,6 @@ ...@@ -2,8 +2,6 @@
# Base class, scoped by project # Base class, scoped by project
class BaseProjectService < ::BaseContainerService class BaseProjectService < ::BaseContainerService
include ::Gitlab::Utils::StrongMemoize
attr_accessor :project attr_accessor :project
def initialize(project:, current_user: nil, params: {}) def initialize(project:, current_user: nil, params: {})
...@@ -13,12 +11,4 @@ class BaseProjectService < ::BaseContainerService ...@@ -13,12 +11,4 @@ class BaseProjectService < ::BaseContainerService
end end
delegate :repository, to: :project delegate :repository, to: :project
private
def project_group
strong_memoize(:project_group) do
project.group
end
end
end end
...@@ -1267,9 +1267,6 @@ Input type: `CreateIssueInput` ...@@ -1267,9 +1267,6 @@ Input type: `CreateIssueInput`
| <a id="mutationcreateissueepicid"></a>`epicId` | [`EpicID`](#epicid) | ID of an epic to associate the issue with. | | <a id="mutationcreateissueepicid"></a>`epicId` | [`EpicID`](#epicid) | ID of an epic to associate the issue with. |
| <a id="mutationcreateissuehealthstatus"></a>`healthStatus` | [`HealthStatus`](#healthstatus) | Desired health status. | | <a id="mutationcreateissuehealthstatus"></a>`healthStatus` | [`HealthStatus`](#healthstatus) | Desired health status. |
| <a id="mutationcreateissueiid"></a>`iid` | [`Int`](#int) | IID (internal ID) of a project issue. Only admins and project owners can modify. | | <a id="mutationcreateissueiid"></a>`iid` | [`Int`](#int) | IID (internal ID) of a project issue. Only admins and project owners can modify. |
| <a id="mutationcreateissueiterationcadenceid"></a>`iterationCadenceId` | [`IterationsCadenceID`](#iterationscadenceid) | Global iteration cadence ID. Required when `iterationWildcardId` is provided. |
| <a id="mutationcreateissueiterationid"></a>`iterationId` | [`IterationID`](#iterationid) | Global iteration ID. Mutually exlusive argument with `iterationWildcardId`. |
| <a id="mutationcreateissueiterationwildcardid"></a>`iterationWildcardId` | [`IssueCreationIterationWildcardId`](#issuecreationiterationwildcardid) | Iteration wildcard ID. Supported values are: `CURRENT`. Mutually exclusive argument with `iterationId`. iterationCadenceId also required when this argument is provided. |
| <a id="mutationcreateissuelabelids"></a>`labelIds` | [`[LabelID!]`](#labelid) | IDs of labels to be added to the issue. | | <a id="mutationcreateissuelabelids"></a>`labelIds` | [`[LabelID!]`](#labelid) | IDs of labels to be added to the issue. |
| <a id="mutationcreateissuelabels"></a>`labels` | [`[String!]`](#string) | Labels of the issue. | | <a id="mutationcreateissuelabels"></a>`labels` | [`[String!]`](#string) | Labels of the issue. |
| <a id="mutationcreateissuelocked"></a>`locked` | [`Boolean`](#boolean) | Indicates discussion is locked on the issue. | | <a id="mutationcreateissuelocked"></a>`locked` | [`Boolean`](#boolean) | Indicates discussion is locked on the issue. |
...@@ -15815,14 +15812,6 @@ State of a GitLab issue or merge request. ...@@ -15815,14 +15812,6 @@ State of a GitLab issue or merge request.
| <a id="issuablestatelocked"></a>`locked` | Discussion has been locked. | | <a id="issuablestatelocked"></a>`locked` | Discussion has been locked. |
| <a id="issuablestateopened"></a>`opened` | In open state. | | <a id="issuablestateopened"></a>`opened` | In open state. |
### `IssueCreationIterationWildcardId`
Iteration ID wildcard values for issue creation.
| Value | Description |
| ----- | ----------- |
| <a id="issuecreationiterationwildcardidcurrent"></a>`CURRENT` | Current iteration. |
### `IssueSort` ### `IssueSort`
Values for sorting issues. Values for sorting issues.
......
...@@ -116,7 +116,9 @@ module EE ...@@ -116,7 +116,9 @@ module EE
{ {
parent: params.parent, parent: params.parent,
include_ancestors: true, include_ancestors: true,
iteration_wildcard_id: ::Iteration::Predefined::Current.title state: 'opened',
start_date: Date.today,
end_date: Date.today
} }
end end
end end
......
...@@ -23,8 +23,6 @@ class IterationsFinder ...@@ -23,8 +23,6 @@ class IterationsFinder
def execute(skip_authorization: false) def execute(skip_authorization: false)
@skip_authorization = skip_authorization @skip_authorization = skip_authorization
handle_wildcard_params
items = Iteration.all items = Iteration.all
items = by_id(items) items = by_id(items)
items = by_iid(items) items = by_iid(items)
...@@ -42,14 +40,6 @@ class IterationsFinder ...@@ -42,14 +40,6 @@ class IterationsFinder
attr_reader :skip_authorization attr_reader :skip_authorization
# wildcard params do not override other explicitely given params
def handle_wildcard_params
if params[:iteration_wildcard_id] && params[:iteration_wildcard_id].casecmp?(::Iteration::Predefined::Current.title)
params[:start_date] ||= Date.today
params[:end_date] ||= Date.today
end
end
def by_groups(items) def by_groups(items)
return Iteration.none unless skip_authorization || Ability.allowed?(current_user, :read_iteration, params[:parent]) return Iteration.none unless skip_authorization || Ability.allowed?(current_user, :read_iteration, params[:parent])
......
...@@ -13,17 +13,6 @@ module EE ...@@ -13,17 +13,6 @@ module EE
argument :epic_id, ::Types::GlobalIDType[::Epic], argument :epic_id, ::Types::GlobalIDType[::Epic],
required: false, required: false,
description: 'ID of an epic to associate the issue with.' description: 'ID of an epic to associate the issue with.'
argument :iteration_id, ::Types::GlobalIDType[::Iteration],
required: false,
description: 'Global iteration ID. Mutually exlusive argument with `iterationWildcardId`.'
argument :iteration_wildcard_id, ::Types::IssueCreationIterationWildcardIdEnum,
required: false,
description: 'Iteration wildcard ID. Supported values are: `CURRENT`.' \
' Mutually exclusive argument with `iterationId`.' \
' iterationCadenceId also required when this argument is provided.'
argument :iteration_cadence_id, ::Types::GlobalIDType[::Iterations::Cadence],
required: false,
description: 'Global iteration cadence ID. Required when `iterationWildcardId` is provided.'
end end
override :resolve override :resolve
...@@ -31,53 +20,18 @@ module EE ...@@ -31,53 +20,18 @@ module EE
super super
rescue ActiveRecord::RecordNotFound => e rescue ActiveRecord::RecordNotFound => e
{ errors: [e.message], issue: nil } { errors: [e.message], issue: nil }
rescue ::Issues::BaseService::IterationAssignmentError => e
raise(
::Gitlab::Graphql::Errors::ArgumentError,
transform_field_names(e.message)
)
end end
private private
override :build_create_issue_params override :build_create_issue_params
def build_create_issue_params(params, project) def build_create_issue_params(params)
# TODO: remove this line when the compatibility layer is removed # TODO: remove this line when the compatibility layer is removed
# See: https://gitlab.com/gitlab-org/gitlab/-/issues/257883 # See: https://gitlab.com/gitlab-org/gitlab/-/issues/257883
params[:epic_id] = ::Types::GlobalIDType[::Epic].coerce_isolated_input(params[:epic_id]) if params[:epic_id] params[:epic_id] = ::Types::GlobalIDType[::Epic].coerce_isolated_input(params[:epic_id]) if params[:epic_id]
params[:epic_id] = params[:epic_id]&.model_id if params.key?(:epic_id) params[:epic_id] = params[:epic_id]&.model_id if params.key?(:epic_id)
handle_iteration_params(params, project) super(params)
super
end
def handle_iteration_params(params, project)
group = project.group
return unless group && group.licensed_feature_available?(:iterations)
# TODO: remove this line when the compatibility layer is removed
# See: https://gitlab.com/gitlab-org/gitlab/-/issues/257883
params[:iteration_id] = ::Types::GlobalIDType[::Iteration].coerce_isolated_input(params[:iteration_id]) if params[:iteration_id]
params[:iteration_id] = params[:iteration_id]&.model_id
# TODO: remove this line when the compatibility layer is removed
# See: https://gitlab.com/gitlab-org/gitlab/-/issues/257883
params[:iteration_cadence_id] = ::Types::GlobalIDType[::Iterations::Cadence].coerce_isolated_input(params[:iteration_cadence_id]) if params[:iteration_cadence_id]
params[:iteration_cadence_id] = params[:iteration_cadence_id]&.model_id
end
def name_mappings
{
'iteration_wildcard_id' => 'iterationWildcardId',
'iteration_cadence_id' => 'iterationCadenceId',
'iteration_id' => 'iterationId'
}
end
def transform_field_names(message)
name_mappings.reduce(message) do |transformed_message, (k, v)|
transformed_message.gsub(k, v)
end
end end
end end
end end
......
# frozen_string_literal: true
module Types
class IssueCreationIterationWildcardIdEnum < BaseEnum
graphql_name 'IssueCreationIterationWildcardId'
description 'Iteration ID wildcard values for issue creation'
value 'CURRENT', 'Current iteration.'
end
end
...@@ -21,10 +21,6 @@ module EE ...@@ -21,10 +21,6 @@ module EE
params.delete(:health_status) params.delete(:health_status)
end end
# Filter these iteration params unconditionally as they do not exist on the model.
# They must be used before reaching this filter if present.
[:iteration_wildcard_id, :iteration_cadence_id, :iteration_id].each { |iteration_param| params.delete(iteration_param) }
super super
end end
......
...@@ -5,8 +5,7 @@ module EE ...@@ -5,8 +5,7 @@ module EE
module BaseService module BaseService
extend ::Gitlab::Utils::Override extend ::Gitlab::Utils::Override
EpicAssignmentError = Class.new(::ArgumentError) class EpicAssignmentError < ::ArgumentError; end
IterationAssignmentError = Class.new(StandardError)
def filter_epic(issue) def filter_epic(issue)
return unless epic_param_present? return unless epic_param_present?
...@@ -108,42 +107,6 @@ module EE ...@@ -108,42 +107,6 @@ module EE
raise EpicAssignmentError, result[:message] raise EpicAssignmentError, result[:message]
end end
end end
private
def validate_iteration_params!(iteration_params)
if iteration_params[:iteration_wildcard_id].present? && iteration_params[:iteration_cadence_id].blank?
raise IterationAssignmentError, 'iteration_cadence_id is required when iteration_wildcard_id is provided.'
end
if [iteration_params[:iteration_id], iteration_params[:iteration_wildcard_id]].all?(&:present?)
raise IterationAssignmentError, 'Incompatible arguments: iteration_id, iteration_wildcard_id.'
end
end
def find_iteration!(iteration_params, group)
validate_iteration_params!(iteration_params)
# converts params to keys the finder understands
finder_params = iteration_params.slice(:iteration_wildcard_id).merge(parent: group, include_ancestors: true)
finder_params[:id] = iteration_params[:iteration_id]
finder_params[:iteration_cadence_ids] = iteration_params[:iteration_cadence_id]
iteration = IterationsFinder.new(current_user, finder_params.compact).execute.first
return unless iteration && current_user.can?(:read_iteration, iteration)
iteration
end
def process_iteration_id
return unless project_group&.licensed_feature_available?(:iterations)
iteration_params = params.slice(:iteration_wildcard_id, :iteration_cadence_id, :iteration_id)
iteration = find_iteration!(iteration_params, project_group)
params[:iteration] = iteration if iteration
end
end end
end end
end end
...@@ -5,13 +5,6 @@ module EE ...@@ -5,13 +5,6 @@ module EE
module CreateService module CreateService
extend ::Gitlab::Utils::Override extend ::Gitlab::Utils::Override
override :create
def create(issuable, skip_system_notes: false)
process_iteration_id
super
end
override :filter_params override :filter_params
def filter_params(issue) def filter_params(issue)
filter_epic(issue) filter_epic(issue)
......
...@@ -67,28 +67,6 @@ RSpec.describe IterationsFinder do ...@@ -67,28 +67,6 @@ RSpec.describe IterationsFinder do
it 'returns iterations for groups' do it 'returns iterations for groups' do
expect(subject).to contain_exactly(closed_iteration, started_group_iteration, upcoming_group_iteration) expect(subject).to contain_exactly(closed_iteration, started_group_iteration, upcoming_group_iteration)
end end
context 'with filters' do
context 'by iteration_wildcard_id' do
let_it_be(:started_group_iteration2) { create(:current_iteration, :skip_future_date_validation, iterations_cadence: iteration_cadence1, group: iteration_cadence1.group, title: 'one test', start_date: 1.day.ago, due_date: Date.today) }
before do
params[:iteration_wildcard_id] = 'CURRENT'
end
it 'returns CURRENT iterations without ancestors' do
expect(subject).to contain_exactly(started_group_iteration, started_group_iteration2)
end
context 'when iteration_cadence_id is provided' do
it 'returns CURRENT iteration for the given cadence' do
params[:iteration_cadence_ids] = iteration_cadence1.id
expect(subject).to contain_exactly(started_group_iteration2)
end
end
end
end
end end
context 'iterations for project with ancestors' do context 'iterations for project with ancestors' do
...@@ -156,22 +134,6 @@ RSpec.describe IterationsFinder do ...@@ -156,22 +134,6 @@ RSpec.describe IterationsFinder do
expect(subject).to contain_exactly(closed_iteration, started_group_iteration, upcoming_group_iteration) expect(subject).to contain_exactly(closed_iteration, started_group_iteration, upcoming_group_iteration)
end end
context 'by iteration_wildcard_id' do
before do
params[:iteration_wildcard_id] = 'CURRENT'
end
it 'returns CURRENT iterations' do
expect(subject).to contain_exactly(root_group_iteration, started_group_iteration)
end
it 'returns CURRENT iteration for the specified cadence' do
params[:iteration_cadence_ids] = started_group_iteration.iterations_cadence.id
expect(subject).to contain_exactly(started_group_iteration)
end
end
context 'by timeframe' do context 'by timeframe' do
it 'returns iterations with start_date and due_date between timeframe' do it 'returns iterations with start_date and due_date between timeframe' do
params.merge!(start_date: 1.day.ago, end_date: 3.days.from_now) params.merge!(start_date: 1.day.ago, end_date: 3.days.from_now)
......
...@@ -7,8 +7,6 @@ RSpec.describe Mutations::Issues::Create do ...@@ -7,8 +7,6 @@ RSpec.describe Mutations::Issues::Create do
let_it_be(:group) { create(:group) } let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project, group: group) } let_it_be(:project) { create(:project, group: group) }
let_it_be(:cadence1) { create(:iterations_cadence, group: group) }
let_it_be(:current_iteration) { create(:iteration, group: group, iterations_cadence: cadence1, start_date: 2.days.ago, due_date: 5.days.from_now) }
let_it_be(:user) { create(:user) } let_it_be(:user) { create(:user) }
let_it_be(:assignee1) { create(:user) } let_it_be(:assignee1) { create(:user) }
let_it_be(:assignee2) { create(:user) } let_it_be(:assignee2) { create(:user) }
...@@ -33,117 +31,27 @@ RSpec.describe Mutations::Issues::Create do ...@@ -33,117 +31,27 @@ RSpec.describe Mutations::Issues::Create do
end end
let(:mutation_params) do let(:mutation_params) do
inputs.merge(expected_attributes).merge(additional_attributes) inputs.merge(expected_attributes)
end end
let(:additional_attributes) { {} }
let(:mutation) { described_class.new(object: nil, context: { current_user: user }, field: nil) } let(:mutation) { described_class.new(object: nil, context: { current_user: user }, field: nil) }
let(:mutated_issue) { resolved_mutation[:issue] } let(:mutated_issue) { subject[:issue] }
before_all do
project.add_guest(assignee1)
project.add_guest(assignee2)
end
specify { expect(described_class).to require_graphql_authorizations(:create_issue) } specify { expect(described_class).to require_graphql_authorizations(:create_issue) }
describe '#resolve' do describe '#resolve' do
before do before do
project.add_guest(assignee1)
project.add_guest(assignee2)
stub_licensed_features(issuable_health_status: true) stub_licensed_features(issuable_health_status: true)
stub_spam_services stub_spam_services
end end
subject(:resolved_mutation) { mutation.resolve(**mutation_params) } subject { mutation.resolve(**mutation_params) }
context 'when user can create issues' do context 'when user can create issues' do
before_all do
group.add_developer(user)
end
context 'when iterations are available' do
let_it_be(:past_iteration) { create(:iteration, group: group, iterations_cadence: cadence1, start_date: 9.days.ago, due_date: 3.days.ago) }
let_it_be(:future_iteration) { create(:iteration, group: group, iterations_cadence: cadence1, start_date: 6.days.from_now, due_date: 9.days.from_now) }
before do
stub_licensed_features(iterations: true)
end
context 'when iteration_id is provided' do
let(:additional_attributes) { { iteration_id: past_iteration.to_global_id } }
it 'is successful, and assigns the current iteration to the issue' do
expect(resolved_mutation[:errors]).to be_empty
expect(mutated_issue).to have_attributes(iteration: past_iteration)
end
context 'when iteration_wildcard_id is provided' do
let(:additional_attributes) { { iteration_id: past_iteration.to_global_id, iteration_wildcard_id: 'CURRENT', iteration_cadence_id: cadence1.to_global_id } }
it 'raises a mutually exclusive argument error' do
expect { resolved_mutation }.to raise_error(
::Gitlab::Graphql::Errors::ArgumentError,
'Incompatible arguments: iterationId, iterationWildcardId.'
)
end
end
context 'when iteration cadences feature flag is disabled' do
before do before do
stub_feature_flags(iteration_cadences: false) group.add_developer(user)
end
it 'is successful, and assigns the current iteration to the issue' do
expect(resolved_mutation[:errors]).to be_empty
expect(mutated_issue).to have_attributes(iteration: past_iteration)
end
end
end
context 'when iteration_wildcard_id is CURRENT' do
let(:additional_attributes) { { iteration_wildcard_id: 'CURRENT' } }
context 'when iteration_cadence_id is provided' do
let(:additional_attributes) { { iteration_wildcard_id: 'CURRENT', iteration_cadence_id: cadence1.to_global_id } }
it 'is successful, and assigns the current iteration to the issue' do
expect(resolved_mutation[:errors]).to be_empty
expect(mutated_issue).to have_attributes(iteration: current_iteration)
end
end
context 'when iteration_cadence_id is not provided' do
it 'always requires iteration cadence id when wildcard is provided' do
expect { resolved_mutation }.to raise_error(
::Gitlab::Graphql::Errors::ArgumentError,
'iterationCadenceId is required when iterationWildcardId is provided.'
)
end
end
end
end
context 'when iterations are not available' do
before do
stub_licensed_features(iterations: false)
end
context 'when iteration_wildcard_id is provided' do
let(:additional_attributes) { { iteration_wildcard_id: 'CURRENT' } }
it 'is successful, but it does not add the iteration' do
expect(resolved_mutation[:errors]).to be_empty
expect(mutated_issue).to have_attributes(iteration: nil)
end
end
context 'when iteration_id is provided' do
let(:additional_attributes) { { iteration_id: current_iteration.to_global_id } }
it 'is successful, but it does not add the iteration' do
expect(resolved_mutation[:errors]).to be_empty
expect(mutated_issue).to have_attributes(iteration: nil)
end
end
end end
it 'creates issue with correct EE values' do it 'creates issue with correct EE values' do
...@@ -165,7 +73,7 @@ RSpec.describe Mutations::Issues::Create do ...@@ -165,7 +73,7 @@ RSpec.describe Mutations::Issues::Create do
end end
it 'is successful, and assigns the issue to the epic' do it 'is successful, and assigns the issue to the epic' do
expect(resolved_mutation[:errors]).to be_empty expect(subject[:errors]).to be_empty
expect(mutated_issue).to have_attributes(epic: epic) expect(mutated_issue).to have_attributes(epic: epic)
end end
...@@ -175,7 +83,7 @@ RSpec.describe Mutations::Issues::Create do ...@@ -175,7 +83,7 @@ RSpec.describe Mutations::Issues::Create do
it 'is successful, but it does not add the epic' do it 'is successful, but it does not add the epic' do
project.add_developer(user) project.add_developer(user)
expect(resolved_mutation[:errors]).to be_empty expect(subject[:errors]).to be_empty
expect(mutated_issue).not_to have_attributes(epic: epic) expect(mutated_issue).not_to have_attributes(epic: epic)
end end
end end
...@@ -183,7 +91,7 @@ RSpec.describe Mutations::Issues::Create do ...@@ -183,7 +91,7 @@ RSpec.describe Mutations::Issues::Create do
context 'epics are unavailable' do context 'epics are unavailable' do
it 'is unsuccessful' do it 'is unsuccessful' do
expect(resolved_mutation[:errors]).to contain_exactly("Couldn't find Epic") expect(subject[:errors]).to contain_exactly("Couldn't find Epic")
end end
it 'does not create an issue' do it 'does not create an issue' do
......
...@@ -6,17 +6,13 @@ RSpec.describe 'Create an issue' do ...@@ -6,17 +6,13 @@ RSpec.describe 'Create an issue' do
include GraphqlHelpers include GraphqlHelpers
let_it_be(:current_user) { create(:user) } let_it_be(:current_user) { create(:user) }
let_it_be(:group) { create(:group) } let_it_be(:project) { create(:project) }
let_it_be(:project) { create(:project, namespace: group) }
let_it_be(:current_iteration) { create(:iteration, group: group, start_date: 2.days.ago, due_date: 10.days.from_now) }
let(:input) do let(:input) do
{ {
'title' => 'new title', 'title' => 'new title',
'weight' => 2, 'weight' => 2,
'healthStatus' => 'atRisk', 'healthStatus' => 'atRisk'
'iterationWildcardId' => 'CURRENT',
'iterationCadenceId' => current_iteration.iterations_cadence.to_global_id.to_s
} }
end end
...@@ -25,53 +21,14 @@ RSpec.describe 'Create an issue' do ...@@ -25,53 +21,14 @@ RSpec.describe 'Create an issue' do
let(:mutation_response) { graphql_mutation_response(:create_issue) } let(:mutation_response) { graphql_mutation_response(:create_issue) }
before do before do
stub_licensed_features(issuable_health_status: true, iterations: true) stub_licensed_features(issuable_health_status: true)
group.add_developer(current_user) project.add_developer(current_user)
end end
it 'creates the issue' do it 'creates the issue' do
post_graphql_mutation(mutation, current_user: current_user) post_graphql_mutation(mutation, current_user: current_user)
expect(response).to have_gitlab_http_status(:success) expect(response).to have_gitlab_http_status(:success)
expect(mutation_response['issue']).to include(input.except('iterationWildcardId', 'iterationCadenceId')) expect(mutation_response['issue']).to include(input)
expect(mutation_response['issue']).to include('iteration' => hash_including('id' => current_iteration.to_global_id.to_s))
end
context 'when iterationId is provided' do
let(:input) do
{
'title' => 'new title',
'weight' => 2,
'healthStatus' => 'atRisk',
'iterationId' => current_iteration.to_global_id.to_s
}
end
it 'creates the issue' do
post_graphql_mutation(mutation, current_user: current_user)
expect(response).to have_gitlab_http_status(:success)
expect(mutation_response['issue']).to include(input.except('iterationId'))
expect(mutation_response['issue']).to include('iteration' => hash_including('id' => current_iteration.to_global_id.to_s))
end
context 'when iterationId and iterationWildcardId are provided' do
let(:input) do
{
'title' => 'new title',
'weight' => 2,
'healthStatus' => 'atRisk',
'iterationId' => current_iteration.to_global_id.to_s,
'iterationWildcardId' => 'CURRENT',
'iterationCadenceId' => current_iteration.iterations_cadence.to_global_id.to_s
}
end
it 'returns a mutually exclusive argument error' do
post_graphql_mutation(mutation, current_user: current_user)
expect(graphql_errors).to contain_exactly(hash_including('message' => 'Incompatible arguments: iterationId, iterationWildcardId.'))
end
end
end end
end end
...@@ -7,11 +7,8 @@ RSpec.describe Issues::CreateService do ...@@ -7,11 +7,8 @@ RSpec.describe Issues::CreateService do
let_it_be(:user) { create(:user) } let_it_be(:user) { create(:user) }
let_it_be_with_reload(:project) { create(:project, group: group) } let_it_be_with_reload(:project) { create(:project, group: group) }
let(:base_params) { { title: 'Awesome issue', description: 'please fix', weight: 9 } } let(:params) { { title: 'Awesome issue', description: 'please fix', weight: 9 } }
let(:additional_params) { {} }
let(:params) { base_params.merge(additional_params) }
let(:service) { described_class.new(project: project, current_user: user, params: params, spam_params: nil) } let(:service) { described_class.new(project: project, current_user: user, params: params, spam_params: nil) }
let(:created_issue) { service.execute }
describe '#execute' do describe '#execute' do
context 'when current user cannot admin issues in the project' do context 'when current user cannot admin issues in the project' do
...@@ -20,8 +17,10 @@ RSpec.describe Issues::CreateService do ...@@ -20,8 +17,10 @@ RSpec.describe Issues::CreateService do
end end
it 'filters out params that cannot be set without the :admin_issue permission' do it 'filters out params that cannot be set without the :admin_issue permission' do
expect(created_issue).to be_persisted issue = service.execute
expect(created_issue.weight).to be_nil
expect(issue).to be_persisted
expect(issue.weight).to be_nil
end end
end end
...@@ -32,8 +31,10 @@ RSpec.describe Issues::CreateService do ...@@ -32,8 +31,10 @@ RSpec.describe Issues::CreateService do
end end
it 'sets permitted params correctly' do it 'sets permitted params correctly' do
expect(created_issue).to be_persisted issue = service.execute
expect(created_issue.weight).to eq(9)
expect(issue).to be_persisted
expect(issue.weight).to eq(9)
end end
context 'when epics are enabled' do context 'when epics are enabled' do
...@@ -57,9 +58,11 @@ RSpec.describe Issues::CreateService do ...@@ -57,9 +58,11 @@ RSpec.describe Issues::CreateService do
let(:params) { { title: 'New issue', description: "/epic #{epic.to_reference(project)}" } } let(:params) { { title: 'New issue', description: "/epic #{epic.to_reference(project)}" } }
it 'adds an issue to the passed epic' do it 'adds an issue to the passed epic' do
expect(created_issue).to be_persisted issue = service.execute
expect(created_issue.reload.epic).to eq(epic)
expect(created_issue.confidential).to eq(false) expect(issue).to be_persisted
expect(issue.reload.epic).to eq(epic)
expect(issue.confidential).to eq(false)
end end
end end
...@@ -79,8 +82,10 @@ RSpec.describe Issues::CreateService do ...@@ -79,8 +82,10 @@ RSpec.describe Issues::CreateService do
end end
it 'sets epic and milestone to issuable and update epic start and due date' do it 'sets epic and milestone to issuable and update epic start and due date' do
expect(created_issue.milestone).to eq(milestone) issue = service.execute
expect(created_issue.reload.epic).to eq(epic)
expect(issue.milestone).to eq(milestone)
expect(issue.reload.epic).to eq(epic)
expect(epic.reload.start_date).to eq(milestone.start_date) expect(epic.reload.start_date).to eq(milestone.start_date)
expect(epic.due_date).to eq(milestone.due_date) expect(epic.due_date).to eq(milestone.due_date)
end end
...@@ -101,82 +106,23 @@ RSpec.describe Issues::CreateService do ...@@ -101,82 +106,23 @@ RSpec.describe Issues::CreateService do
end end
context 'when adding a public issue to confidential epic' do context 'when adding a public issue to confidential epic' do
let(:confidential_epic) { create(:epic, group: group, confidential: true) }
let(:params) { { title: 'confidential issue', epic_id: confidential_epic.id } }
it 'creates confidential child issue' do it 'creates confidential child issue' do
expect(created_issue).to be_confidential confidential_epic = create(:epic, group: group, confidential: true)
end params = { title: 'confidential issue', epic_id: confidential_epic.id }
end
context 'when adding a confidential issue to public epic' do
let(:params) { { title: 'confidential issue', epic_id: epic.id, confidential: true } }
it 'creates a confidential child issue' do
expect(created_issue).to be_confidential
end
end
end
end
context 'when iterations are available' do
let_it_be(:iteration_cadence1) { create(:iterations_cadence, group: group) }
let_it_be(:iteration_cadence2) { create(:iterations_cadence, group: group) }
let_it_be(:current_iteration1) { create(:iteration, group: group, iterations_cadence: iteration_cadence1, start_date: 4.days.ago, due_date: 3.days.from_now) }
let_it_be(:current_iteration2) { create(:iteration, group: group, iterations_cadence: iteration_cadence2, start_date: 4.days.ago, due_date: 3.days.from_now) }
let_it_be(:future_iteration) { create(:iteration, group: group, iterations_cadence: iteration_cadence1, start_date: 6.days.from_now, due_date: 13.days.from_now) }
before do issue = described_class.new(project: project, current_user: user, params: params, spam_params: nil).execute
stub_licensed_features(iterations: true)
end
context 'when iteration_id is provided' do
let(:additional_params) { { iteration_id: future_iteration.id } }
it 'is successful, and assigns the current iteration to the issue' do
expect(created_issue).to be_persisted
expect(created_issue).to have_attributes(iteration: future_iteration)
end
context 'when iteration_wildcard_id is provided' do
let(:additional_params) { { iteration_id: future_iteration.id, iteration_wildcard_id: 'CURRENT', iteration_cadence_id: iteration_cadence2.id } }
it 'raises a mutually exclusive argument error' do
expect { service.execute }.to raise_error(
::Issues::BaseService::IterationAssignmentError,
'Incompatible arguments: iteration_id, iteration_wildcard_id.'
)
end
end
context "when user can't read the given iteration" do
let(:additional_params) { { iteration_id: create(:iteration, group: create(:group, :private)).id } }
it 'is successful but does not assign the iteration' do expect(issue.confidential).to eq(true)
expect(created_issue).to be_persisted
expect(created_issue).to have_attributes(iteration: nil)
end end
end end
end
context 'when iteration_wildcard_id is CURRENT' do
let(:additional_params) { { iteration_wildcard_id: 'CURRENT' } }
context 'when iteration_cadence_id is provided' do context 'when adding a confidential issue to public epic' do
let(:additional_params) { { iteration_wildcard_id: 'CURRENT', iteration_cadence_id: iteration_cadence2.id } } it 'creates a confidential child issue' do
params = { title: 'confidential issue', epic_id: epic.id, confidential: true }
it 'is successful, and assigns the current iteration to the issue' do issue = described_class.new(project: project, current_user: user, params: params, spam_params: nil).execute
expect(created_issue).to be_persisted
expect(created_issue).to have_attributes(iteration: current_iteration2)
end
end
context 'when iteration_cadence_id is not provided' do expect(issue.confidential).to eq(true)
it 'always requires iteration cadence id when wildcard is provided' do
expect { service.execute }.to raise_error(
::Issues::BaseService::IterationAssignmentError,
'iteration_cadence_id is required when iteration_wildcard_id is provided.'
)
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