Commit b904038c authored by Mario Celi's avatar Mario Celi

Allow Types::GlobalIDType[::WorkItem] to receive IssueID as input

During the transition to work items, we want to allow
clients to send issue or work item global IDs
interchangeably to the work items API on ID fields
parent e351b9d1
......@@ -49,7 +49,11 @@ module Types
# Construct a restricted type, that can only be inhabited by an ID of
# a given model class.
def self.[](model_class)
@id_types ||= {}
@id_types ||= {
# WorkItem has a special class as we want to allow IssueID
# on WorkItemID while we transition into work items
::WorkItem => ::Types::WorkItemIdType
}
@id_types[model_class] ||= Class.new(self) do
model_name = model_class.name
......
# frozen_string_literal: true
module Types
# rubocop:disable Graphql/AuthorizeTypes
# TODO: This type should be removed when Work Items become generally available.
# This mechanism is introduced temporarily to make the client implementation easier during this transition.
class WorkItemIdType < GlobalIDType
graphql_name 'WorkItemID'
description <<~DESC
A `WorkItemID` is a global ID. It is encoded as a string.
An example `WorkItemID` is: `"gid://gitlab/WorkItem/1"`.
While we transition from Issues into Work Items this type will temporarily support
`IssueID` like: `"gid://gitlab/Issue/1"`. This behavior will be removed without notice in the future.
DESC
class << self
def coerce_result(gid, ctx)
global_id = ::Gitlab::GlobalId.as_global_id(gid, model_name: 'WorkItem')
raise GraphQL::CoercionError, "Expected a WorkItem ID, got #{global_id}" unless suitable?(global_id)
# Always return a WorkItemID even if an Issue is returned by a resolver
work_item_gid(global_id).to_s
end
def coerce_input(string, ctx)
gid = super
# Always return a WorkItemID even if an Issue Global ID is provided as input
return work_item_gid(gid) if suitable?(gid)
raise GraphQL::CoercionError, "#{string.inspect} does not represent an instance of WorkItem"
end
def suitable?(gid)
return false if gid&.model_name&.safe_constantize.blank?
[::WorkItem, ::Issue].any? { |model_class| gid.model_class == model_class }
end
private
def work_item_gid(gid)
GlobalID.new(::Gitlab::GlobalId.build(model_name: 'WorkItem', id: gid.model_id))
end
end
end
# rubocop:enable Graphql/AuthorizeTypes
end
......@@ -19070,6 +19070,9 @@ A `WorkItemID` is a global ID. It is encoded as a string.
An example `WorkItemID` is: `"gid://gitlab/WorkItem/1"`.
While we transition from Issues into Work Items this type will temporarily support
`IssueID` like: `"gid://gitlab/Issue/1"`. This behavior will be removed without notice in the future.
### `WorkItemsTypeID`
A `WorkItemsTypeID` is a global ID. It is encoded as a string.
......@@ -376,4 +376,10 @@ RSpec.describe Types::GlobalIDType do
expect(described_class.model_name_to_graphql_name('DesignManagement::Design')).to eq('DesignManagementDesignID')
end
end
describe '.[]' do
it 'returns a custom class for work items' do
expect(described_class[::WorkItem]).to eq(::Types::WorkItemIdType)
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Types::WorkItemIdType do
let_it_be(:project) { create(:project) }
let_it_be(:work_item) { create(:work_item, project: project) }
let_it_be(:issue) { create(:issue, project: project) }
let(:work_item_gid) { work_item.to_gid }
let(:issue_gid) { issue.to_gid }
let(:ctx) { {} }
describe '.coerce_input' do
it 'can coerce valid issue input' do
coerced = described_class.coerce_input(issue_gid.to_s, ctx)
expect(coerced).to eq(WorkItem.find(issue.id).to_gid)
end
it 'can coerce valid work item input' do
coerced = described_class.coerce_input(work_item_gid.to_s, ctx)
expect(coerced).to eq(work_item_gid)
end
it 'fails for other input types' do
project_gid = project.to_gid
expect { described_class.coerce_input(project_gid.to_s, ctx) }
.to raise_error(GraphQL::CoercionError, "#{project_gid.to_s.inspect} does not represent an instance of WorkItem")
end
end
describe '.coerce_result' do
it 'can coerce issue results and return a WorkItem global ID' do
expect(described_class.coerce_result(issue_gid, ctx)).to eq(WorkItem.find(issue.id).to_gid.to_s)
end
it 'can coerce work item results' do
expect(described_class.coerce_result(work_item_gid, ctx)).to eq(work_item_gid.to_s)
end
it 'fails for other input types' do
project_gid = project.to_gid
expect { described_class.coerce_result(project_gid, ctx) }
.to raise_error(GraphQL::CoercionError, "Expected a WorkItem ID, got #{project_gid}")
end
end
end
......@@ -12,9 +12,10 @@ RSpec.describe 'Query.work_item(id)' do
let(:current_user) { developer }
let(:work_item_data) { graphql_data['workItem'] }
let(:work_item_fields) { all_graphql_fields_for('WorkItem') }
let(:global_id) { work_item.to_gid.to_s }
let(:query) do
graphql_query_for('workItem', { 'id' => work_item.to_gid.to_s }, work_item_fields)
graphql_query_for('workItem', { 'id' => global_id }, work_item_fields)
end
context 'when the user can read the work item' do
......@@ -35,6 +36,14 @@ RSpec.describe 'Query.work_item(id)' do
'workItemType' => hash_including('id' => work_item.work_item_type.to_gid.to_s)
)
end
context 'when an Issue Global ID is provided' do
let(:global_id) { Issue.find(work_item.id).to_gid.to_s }
it 'allows an Issue GID as input' do
expect(work_item_data).to include('id' => work_item.to_gid.to_s)
end
end
end
context 'when the user can not read the work item' do
......
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