Skip to content
代码片段 群组 项目
未验证 提交 f30a777d 编辑于 作者: Heinrich Lee Yu's avatar Heinrich Lee Yu
浏览文件

Add assignees to work item widgets in GraphQL

Allows querying for a work item's assignees via GraphQL
上级 8833f404
No related branches found
No related tags found
无相关合并请求
显示 150 个添加25 个删除
...@@ -131,6 +131,7 @@ ...@@ -131,6 +131,7 @@
"VulnerabilityLocationSecretDetection" "VulnerabilityLocationSecretDetection"
], ],
"WorkItemWidget": [ "WorkItemWidget": [
"WorkItemWidgetAssignees",
"WorkItemWidgetDescription", "WorkItemWidgetDescription",
"WorkItemWidgetHierarchy" "WorkItemWidgetHierarchy"
] ]
......
...@@ -16,13 +16,16 @@ def self.resolve_type(object, context) ...@@ -16,13 +16,16 @@ def self.resolve_type(object, context)
::Types::WorkItems::Widgets::DescriptionType ::Types::WorkItems::Widgets::DescriptionType
when ::WorkItems::Widgets::Hierarchy when ::WorkItems::Widgets::Hierarchy
::Types::WorkItems::Widgets::HierarchyType ::Types::WorkItems::Widgets::HierarchyType
when ::WorkItems::Widgets::Assignees
::Types::WorkItems::Widgets::AssigneesType
else else
raise "Unknown GraphQL type for widget #{object}" raise "Unknown GraphQL type for widget #{object}"
end end
end end
orphan_types ::Types::WorkItems::Widgets::DescriptionType, orphan_types ::Types::WorkItems::Widgets::DescriptionType,
::Types::WorkItems::Widgets::HierarchyType ::Types::WorkItems::Widgets::HierarchyType,
::Types::WorkItems::Widgets::AssigneesType
end end
end end
end end
# frozen_string_literal: true
module Types
module WorkItems
module Widgets
# Disabling widget level authorization as it might be too granular
# and we already authorize the parent work item
# rubocop:disable Graphql/AuthorizeTypes
class AssigneesType < BaseObject
graphql_name 'WorkItemWidgetAssignees'
description 'Represents an assignees widget'
implements Types::WorkItems::WidgetInterface
field :assignees, Types::UserType.connection_type, null: true,
description: 'Assignees of the work item.'
field :allows_multiple_assignees, GraphQL::Types::Boolean, null: true, method: :allows_multiple_assignees?,
description: 'Indicates whether multiple assignees are allowed.'
end
# rubocop:enable Graphql/AuthorizeTypes
end
end
end
...@@ -21,11 +21,11 @@ class Type < ApplicationRecord ...@@ -21,11 +21,11 @@ class Type < ApplicationRecord
}.freeze }.freeze
WIDGETS_FOR_TYPE = { WIDGETS_FOR_TYPE = {
issue: [Widgets::Description, Widgets::Hierarchy], issue: [Widgets::Description, Widgets::Hierarchy, Widgets::Assignees],
incident: [Widgets::Description], incident: [Widgets::Description],
test_case: [Widgets::Description], test_case: [Widgets::Description],
requirement: [Widgets::Description], requirement: [Widgets::Description],
task: [Widgets::Description, Widgets::Hierarchy] task: [Widgets::Description, Widgets::Hierarchy, Widgets::Assignees]
}.freeze }.freeze
cache_markdown_field :description, pipeline: :single_line cache_markdown_field :description, pipeline: :single_line
......
# frozen_string_literal: true
module WorkItems
module Widgets
class Assignees < Base
delegate :assignees, to: :work_item
delegate :allows_multiple_assignees?, to: :work_item
end
end
end
...@@ -18366,6 +18366,18 @@ Check permissions for the current user on a work item. ...@@ -18366,6 +18366,18 @@ Check permissions for the current user on a work item.
| <a id="workitemtypeid"></a>`id` | [`WorkItemsTypeID!`](#workitemstypeid) | Global ID of the work item type. | | <a id="workitemtypeid"></a>`id` | [`WorkItemsTypeID!`](#workitemstypeid) | Global ID of the work item type. |
| <a id="workitemtypename"></a>`name` | [`String!`](#string) | Name of the work item type. | | <a id="workitemtypename"></a>`name` | [`String!`](#string) | Name of the work item type. |
   
### `WorkItemWidgetAssignees`
Represents an assignees widget.
#### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="workitemwidgetassigneesallowsmultipleassignees"></a>`allowsMultipleAssignees` | [`Boolean`](#boolean) | Indicates whether multiple assignees are allowed. |
| <a id="workitemwidgetassigneesassignees"></a>`assignees` | [`UserCoreConnection`](#usercoreconnection) | Assignees of the work item. (see [Connections](#connections)) |
| <a id="workitemwidgetassigneestype"></a>`type` | [`WorkItemWidgetType`](#workitemwidgettype) | Widget type. |
### `WorkItemWidgetDescription` ### `WorkItemWidgetDescription`
   
Represents a description widget. Represents a description widget.
...@@ -20234,6 +20246,7 @@ Type of a work item widget. ...@@ -20234,6 +20246,7 @@ Type of a work item widget.
   
| Value | Description | | Value | Description |
| ----- | ----------- | | ----- | ----------- |
| <a id="workitemwidgettypeassignees"></a>`ASSIGNEES` | Assignees widget. |
| <a id="workitemwidgettypedescription"></a>`DESCRIPTION` | Description widget. | | <a id="workitemwidgettypedescription"></a>`DESCRIPTION` | Description widget. |
| <a id="workitemwidgettypehierarchy"></a>`HIERARCHY` | Hierarchy widget. | | <a id="workitemwidgettypehierarchy"></a>`HIERARCHY` | Hierarchy widget. |
   
...@@ -21454,6 +21467,7 @@ four standard [pagination arguments](#connection-pagination-arguments): ...@@ -21454,6 +21467,7 @@ four standard [pagination arguments](#connection-pagination-arguments):
   
Implementations: Implementations:
   
- [`WorkItemWidgetAssignees`](#workitemwidgetassignees)
- [`WorkItemWidgetDescription`](#workitemwidgetdescription) - [`WorkItemWidgetDescription`](#workitemwidgetdescription)
- [`WorkItemWidgetHierarchy`](#workitemwidgethierarchy) - [`WorkItemWidgetHierarchy`](#workitemwidgethierarchy)
   
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
where(:widget_class, :widget_type_name) do where(:widget_class, :widget_type_name) do
WorkItems::Widgets::Description | Types::WorkItems::Widgets::DescriptionType WorkItems::Widgets::Description | Types::WorkItems::Widgets::DescriptionType
WorkItems::Widgets::Hierarchy | Types::WorkItems::Widgets::HierarchyType WorkItems::Widgets::Hierarchy | Types::WorkItems::Widgets::HierarchyType
WorkItems::Widgets::Assignees | Types::WorkItems::Widgets::AssigneesType
end end
with_them do with_them do
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Types::WorkItems::Widgets::AssigneesType do
it 'exposes the expected fields' do
expected_fields = %i[assignees allows_multiple_assignees type]
expect(described_class).to have_graphql_fields(*expected_fields)
end
end
...@@ -38,7 +38,8 @@ ...@@ -38,7 +38,8 @@
it 'returns instances of supported widgets' do it 'returns instances of supported widgets' do
is_expected.to match_array([instance_of(WorkItems::Widgets::Description), is_expected.to match_array([instance_of(WorkItems::Widgets::Description),
instance_of(WorkItems::Widgets::Hierarchy)]) instance_of(WorkItems::Widgets::Hierarchy),
instance_of(WorkItems::Widgets::Assignees)])
end end
end end
......
...@@ -65,7 +65,8 @@ ...@@ -65,7 +65,8 @@
it 'returns list of all possible widgets' do it 'returns list of all possible widgets' do
is_expected.to match_array([::WorkItems::Widgets::Description, is_expected.to match_array([::WorkItems::Widgets::Description,
::WorkItems::Widgets::Hierarchy]) ::WorkItems::Widgets::Hierarchy,
::WorkItems::Widgets::Assignees])
end end
end end
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe WorkItems::Widgets::Assignees do
let_it_be(:work_item) { create(:work_item, assignees: [create(:user)]) }
describe '.type' do
subject { described_class.type }
it { is_expected.to eq(:assignees) }
end
describe '#type' do
subject { described_class.new(work_item).type }
it { is_expected.to eq(:assignees) }
end
describe '#assignees' do
subject { described_class.new(work_item).assignees }
it { is_expected.to eq(work_item.assignees) }
end
describe '#allows_multiple_assignees?' do
subject { described_class.new(work_item).allows_multiple_assignees? }
it { is_expected.to eq(work_item.allows_multiple_assignees?) }
end
end
...@@ -64,16 +64,13 @@ ...@@ -64,16 +64,13 @@
it 'returns widget information' do it 'returns widget information' do
expect(work_item_data).to include( expect(work_item_data).to include(
'id' => work_item.to_gid.to_s, 'id' => work_item.to_gid.to_s,
'widgets' => match_array([ 'widgets' => include(
hash_including( hash_including(
'type' => 'DESCRIPTION', 'type' => 'DESCRIPTION',
'description' => work_item.description, 'description' => work_item.description,
'descriptionHtml' => ::MarkupHelper.markdown_field(work_item, :description, {}) 'descriptionHtml' => ::MarkupHelper.markdown_field(work_item, :description, {})
),
hash_including(
'type' => 'HIERARCHY'
) )
]) )
) )
end end
end end
...@@ -101,10 +98,7 @@ ...@@ -101,10 +98,7 @@
it 'returns widget information' do it 'returns widget information' do
expect(work_item_data).to include( expect(work_item_data).to include(
'id' => work_item.to_gid.to_s, 'id' => work_item.to_gid.to_s,
'widgets' => match_array([ 'widgets' => include(
hash_including(
'type' => 'DESCRIPTION'
),
hash_including( hash_including(
'type' => 'HIERARCHY', 'type' => 'HIERARCHY',
'parent' => nil, 'parent' => nil,
...@@ -113,7 +107,7 @@ ...@@ -113,7 +107,7 @@
hash_including('id' => child_link2.work_item.to_gid.to_s) hash_including('id' => child_link2.work_item.to_gid.to_s)
]) } ]) }
) )
]) )
) )
end end
...@@ -137,10 +131,7 @@ ...@@ -137,10 +131,7 @@
it 'filters out not accessible children or parent' do it 'filters out not accessible children or parent' do
expect(work_item_data).to include( expect(work_item_data).to include(
'id' => work_item.to_gid.to_s, 'id' => work_item.to_gid.to_s,
'widgets' => match_array([ 'widgets' => include(
hash_including(
'type' => 'DESCRIPTION'
),
hash_including( hash_including(
'type' => 'HIERARCHY', 'type' => 'HIERARCHY',
'parent' => nil, 'parent' => nil,
...@@ -148,7 +139,7 @@ ...@@ -148,7 +139,7 @@
hash_including('id' => child_link1.work_item.to_gid.to_s) hash_including('id' => child_link1.work_item.to_gid.to_s)
]) } ]) }
) )
]) )
) )
end end
end end
...@@ -160,20 +151,57 @@ ...@@ -160,20 +151,57 @@
it 'returns parent information' do it 'returns parent information' do
expect(work_item_data).to include( expect(work_item_data).to include(
'id' => work_item.to_gid.to_s, 'id' => work_item.to_gid.to_s,
'widgets' => match_array([ 'widgets' => include(
hash_including(
'type' => 'DESCRIPTION'
),
hash_including( hash_including(
'type' => 'HIERARCHY', 'type' => 'HIERARCHY',
'parent' => hash_including('id' => parent_link.work_item_parent.to_gid.to_s), 'parent' => hash_including('id' => parent_link.work_item_parent.to_gid.to_s),
'children' => { 'nodes' => match_array([]) } 'children' => { 'nodes' => match_array([]) }
) )
]) )
) )
end end
end end
end end
describe 'assignees widget' do
let(:assignees) { create_list(:user, 2) }
let(:work_item) { create(:work_item, project: project, assignees: assignees) }
let(:work_item_fields) do
<<~GRAPHQL
id
widgets {
type
... on WorkItemWidgetAssignees {
allowsMultipleAssignees
assignees {
nodes {
id
username
}
}
}
}
GRAPHQL
end
it 'returns widget information' do
expect(work_item_data).to include(
'id' => work_item.to_gid.to_s,
'widgets' => include(
hash_including(
'type' => 'ASSIGNEES',
'allowsMultipleAssignees' => boolean,
'assignees' => {
'nodes' => match_array(
assignees.map { |a| { 'id' => a.to_gid.to_s, 'username' => a.username } }
)
}
)
)
)
end
end
end end
context 'when an Issue Global ID is provided' do context 'when an Issue Global ID is provided' do
......
0% 加载中 .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册