diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-10-17 13:22:04 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-10-17 13:22:04 +0000 |
commit | 6dce4e9bb103a3dd14df7eca5c00a3f37565fc32 (patch) | |
tree | 07058855ed3e14a5401c81b14ea95c30b78e761e | |
parent | ef309cf1466c3d2a9297d735c7127fcaf0c08d48 (diff) | |
download | gitlab-ce-6dce4e9bb103a3dd14df7eca5c00a3f37565fc32.tar.gz |
Add latest changes from gitlab-org/gitlab@15-4-stable-ee
-rw-r--r-- | app/assets/javascripts/graphql_shared/possible_types.json | 3 | ||||
-rw-r--r-- | app/graphql/resolvers/work_items_resolver.rb | 3 | ||||
-rw-r--r-- | app/graphql/types/todo_target_enum.rb | 1 | ||||
-rw-r--r-- | app/graphql/types/todoable_interface.rb | 2 | ||||
-rw-r--r-- | app/graphql/types/work_item_type.rb | 6 | ||||
-rw-r--r-- | doc/api/graphql/reference/index.md | 3 | ||||
-rw-r--r-- | lib/api/entities/work_item.rb | 8 | ||||
-rw-r--r-- | spec/graphql/types/work_item_type_spec.rb | 1 | ||||
-rw-r--r-- | spec/requests/api/graphql/todo_query_spec.rb | 40 | ||||
-rw-r--r-- | spec/requests/api/todos_spec.rb | 96 |
10 files changed, 120 insertions, 43 deletions
diff --git a/app/assets/javascripts/graphql_shared/possible_types.json b/app/assets/javascripts/graphql_shared/possible_types.json index 72dbf9e7b7b..c6bd9e563c0 100644 --- a/app/assets/javascripts/graphql_shared/possible_types.json +++ b/app/assets/javascripts/graphql_shared/possible_types.json @@ -102,7 +102,8 @@ "Epic", "EpicIssue", "Issue", - "MergeRequest" + "MergeRequest", + "WorkItem" ], "User": [ "MergeRequestAssignee", diff --git a/app/graphql/resolvers/work_items_resolver.rb b/app/graphql/resolvers/work_items_resolver.rb index 9c7931a4edb..a8c0d363325 100644 --- a/app/graphql/resolvers/work_items_resolver.rb +++ b/app/graphql/resolvers/work_items_resolver.rb @@ -37,7 +37,8 @@ module Resolvers def preloads { - last_edited_by: :last_edited_by + last_edited_by: :last_edited_by, + web_url: { project: { namespace: :route } } } end diff --git a/app/graphql/types/todo_target_enum.rb b/app/graphql/types/todo_target_enum.rb index dbf7b42ffcc..d2527ef5267 100644 --- a/app/graphql/types/todo_target_enum.rb +++ b/app/graphql/types/todo_target_enum.rb @@ -4,6 +4,7 @@ module Types class TodoTargetEnum < BaseEnum value 'COMMIT', value: 'Commit', description: 'Commit.' value 'ISSUE', value: 'Issue', description: 'Issue.' + value 'WORKITEM', value: 'WorkItem', description: 'Work item.' value 'MERGEREQUEST', value: 'MergeRequest', description: 'Merge request.' value 'DESIGN', value: 'DesignManagement::Design', description: 'Design.' value 'ALERT', value: 'AlertManagement::Alert', description: 'Alert.' diff --git a/app/graphql/types/todoable_interface.rb b/app/graphql/types/todoable_interface.rb index 7d437973c12..063c1edfb5b 100644 --- a/app/graphql/types/todoable_interface.rb +++ b/app/graphql/types/todoable_interface.rb @@ -10,6 +10,8 @@ module Types def self.resolve_type(object, context) case object + when WorkItem + Types::WorkItemType when Issue Types::IssueType when MergeRequest diff --git a/app/graphql/types/work_item_type.rb b/app/graphql/types/work_item_type.rb index 7904841863b..6a1a4f158be 100644 --- a/app/graphql/types/work_item_type.rb +++ b/app/graphql/types/work_item_type.rb @@ -4,6 +4,8 @@ module Types class WorkItemType < BaseObject graphql_name 'WorkItem' + implements(Types::TodoableInterface) + authorize :read_work_item field :closed_at, Types::TimeType, null: true, @@ -42,5 +44,9 @@ module Types markdown_field :description_html, null: true expose_permissions Types::PermissionTypes::WorkItem + + def web_url + Gitlab::UrlBuilder.build(object) + end end end diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index f9125e5f4d4..cf218d6e2d7 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -19338,6 +19338,7 @@ Represents vulnerability letter grades with associated projects. | <a id="workitemtitlehtml"></a>`titleHtml` | [`String`](#string) | The GitLab Flavored Markdown rendering of `title`. | | <a id="workitemupdatedat"></a>`updatedAt` | [`Time!`](#time) | Timestamp of when the work item was last updated. | | <a id="workitemuserpermissions"></a>`userPermissions` | [`WorkItemPermissions!`](#workitempermissions) | Permissions for the current user on the resource. | +| <a id="workitemweburl"></a>`webUrl` | [`String`](#string) | URL of this object. | | <a id="workitemwidgets"></a>`widgets` | [`[WorkItemWidget!]`](#workitemwidget) | Collection of widgets that belong to the work item. | | <a id="workitemworkitemtype"></a>`workItemType` | [`WorkItemType!`](#workitemtype) | Type assigned to the work item. | @@ -21167,6 +21168,7 @@ Category of error. | <a id="todotargetenumepic"></a>`EPIC` | An Epic. | | <a id="todotargetenumissue"></a>`ISSUE` | Issue. | | <a id="todotargetenummergerequest"></a>`MERGEREQUEST` | Merge request. | +| <a id="todotargetenumworkitem"></a>`WORKITEM` | Work item. | ### `TrainingUrlRequestStatus` @@ -22457,6 +22459,7 @@ Implementations: - [`EpicIssue`](#epicissue) - [`Issue`](#issue) - [`MergeRequest`](#mergerequest) +- [`WorkItem`](#workitem) ##### Fields diff --git a/lib/api/entities/work_item.rb b/lib/api/entities/work_item.rb new file mode 100644 index 00000000000..5e881f8ea12 --- /dev/null +++ b/lib/api/entities/work_item.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +module API + module Entities + class WorkItem < Issue + end + end +end diff --git a/spec/graphql/types/work_item_type_spec.rb b/spec/graphql/types/work_item_type_spec.rb index 11b02a88dbd..dded96fde3a 100644 --- a/spec/graphql/types/work_item_type_spec.rb +++ b/spec/graphql/types/work_item_type_spec.rb @@ -26,6 +26,7 @@ RSpec.describe GitlabSchema.types['WorkItem'] do created_at updated_at closed_at + web_url ] expect(described_class).to have_graphql_fields(*fields) diff --git a/spec/requests/api/graphql/todo_query_spec.rb b/spec/requests/api/graphql/todo_query_spec.rb index 3f743f4402a..be7242d95bd 100644 --- a/spec/requests/api/graphql/todo_query_spec.rb +++ b/spec/requests/api/graphql/todo_query_spec.rb @@ -11,23 +11,31 @@ RSpec.describe 'Todo Query' do let_it_be(:todo_owner) { create(:user) } - let_it_be(:todo) { create(:todo, user: todo_owner, target: project) } - - before do - project.add_developer(todo_owner) - end + let_it_be(:todo) { create(:todo, user: todo_owner, target: issue) } + let(:todo_subject) { todo } let(:fields) do <<~GRAPHQL id + targetType + target { + webUrl + ... on WorkItem { + id + } + } GRAPHQL end let(:query) do - graphql_query_for(:todo, { id: todo.to_global_id.to_s }, fields) + graphql_query_for(:todo, { id: todo_subject.to_global_id.to_s }, fields) + end + + before do + project.add_developer(todo_owner) end - subject do + subject(:graphql_response) do result = GitlabSchema.execute(query, context: { current_user: current_user }).to_h graphql_dig_at(result, :data, :todo) end @@ -35,7 +43,23 @@ RSpec.describe 'Todo Query' do context 'when requesting user is todo owner' do let(:current_user) { todo_owner } - it { is_expected.to include('id' => todo.to_global_id.to_s) } + it { is_expected.to include('id' => todo_subject.to_global_id.to_s) } + + context 'when todo target is WorkItem' do + let(:work_item) { create(:work_item, :task, project: project) } + let(:todo_subject) { create(:todo, user: todo_owner, target: work_item, target_type: WorkItem.name) } + + it 'works with a WorkItem target' do + expect(graphql_response).to include( + 'id' => todo_subject.to_gid.to_s, + 'targetType' => 'WORKITEM', + 'target' => { + 'id' => work_item.to_gid.to_s, + 'webUrl' => Gitlab::UrlBuilder.build(work_item) + } + ) + end + end end context 'when requesting user is not todo owner' do diff --git a/spec/requests/api/todos_spec.rb b/spec/requests/api/todos_spec.rb index d75ca0d4f6f..0fcb6412a2d 100644 --- a/spec/requests/api/todos_spec.rb +++ b/spec/requests/api/todos_spec.rb @@ -12,6 +12,7 @@ RSpec.describe API::Todos do let_it_be(:author_2) { create(:user) } let_it_be(:john_doe) { create(:user, username: 'john_doe') } let_it_be(:issue) { create(:issue, project: project_1) } + let_it_be(:work_item) { create(:work_item, :task, project: project_1) } let_it_be(:merge_request) { create(:merge_request, source_project: project_1) } let_it_be(:alert) { create(:alert_management_alert, project: project_1) } let_it_be(:alert_todo) { create(:todo, project: project_1, author: john_doe, user: john_doe, target: alert) } @@ -20,6 +21,7 @@ RSpec.describe API::Todos do let_it_be(:pending_2) { create(:todo, project: project_2, author: author_2, user: john_doe, target: issue) } let_it_be(:pending_3) { create(:on_commit_todo, project: project_1, author: author_2, user: john_doe) } let_it_be(:pending_4) { create(:on_commit_todo, project: project_1, author: author_2, user: john_doe, commit_id: 'invalid_id') } + let_it_be(:pending_5) { create(:todo, :mentioned, project: project_1, author: author_1, user: john_doe, target: work_item, target_type: WorkItem.name) } let_it_be(:done) { create(:todo, :done, project: project_1, author: author_1, user: john_doe, target: issue) } let_it_be(:award_emoji_1) { create(:award_emoji, awardable: merge_request, user: author_1, name: 'thumbsup') } let_it_be(:award_emoji_2) { create(:award_emoji, awardable: pending_1.target, user: author_1, name: 'thumbsup') } @@ -69,38 +71,62 @@ RSpec.describe API::Todos do expect(response).to have_gitlab_http_status(:ok) expect(response).to include_pagination_headers expect(json_response).to be_an Array - expect(json_response.length).to eq(5) - expect(json_response[0]['id']).to eq(pending_3.id) - expect(json_response[0]['project']).to be_a Hash - expect(json_response[0]['author']).to be_a Hash - expect(json_response[0]['target_type']).to be_present - expect(json_response[0]['target']).to be_a Hash - expect(json_response[0]['target_url']).to be_present - expect(json_response[0]['body']).to be_present - expect(json_response[0]['state']).to eq('pending') - expect(json_response[0]['action_name']).to eq('assigned') - expect(json_response[0]['created_at']).to be_present - expect(json_response[0]['target_type']).to eq('Commit') - - expect(json_response[1]['target_type']).to eq('Issue') - expect(json_response[1]['target']['upvotes']).to eq(1) - expect(json_response[1]['target']['downvotes']).to eq(1) - expect(json_response[1]['target']['merge_requests_count']).to eq(0) - - expect(json_response[2]['target_type']).to eq('Issue') - expect(json_response[2]['target']['upvotes']).to eq(1) - expect(json_response[2]['target']['downvotes']).to eq(1) - expect(json_response[2]['target']['merge_requests_count']).to eq(0) - - expect(json_response[3]['target_type']).to eq('MergeRequest') + expect(json_response.length).to eq(6) + + expect(json_response[0]).to include( + 'id' => pending_5.id, + 'target_type' => 'WorkItem', + 'target' => hash_including('type' => 'TASK') + ) + + expect(json_response[1]).to include( + 'id' => pending_3.id, + 'project' => instance_of(Hash), + 'author' => instance_of(Hash), + 'target' => instance_of(Hash), + 'state' => 'pending', + 'action_name' => 'assigned', + 'target_type' => 'Commit', + 'target_url' => be_present, + 'body' => be_present, + 'created_at' => be_present + ) + + expect(json_response[2]).to include( + 'target_type' => 'Issue', + 'target' => hash_including( + 'upvotes' => 1, + 'downvotes' => 1, + 'merge_requests_count' => 0 + ) + ) + + expect(json_response[3]).to include( + 'target_type' => 'Issue', + 'target' => hash_including( + 'upvotes' => 1, + 'downvotes' => 1, + 'merge_requests_count' => 0 + ) + ) + # Only issues get a merge request count at the moment - expect(json_response[3]['target']['merge_requests_count']).to be_nil - expect(json_response[3]['target']['upvotes']).to eq(1) - expect(json_response[3]['target']['downvotes']).to eq(0) + expect(json_response[4].dig('target', 'merge_requests_count')).to be_nil + expect(json_response[4]).to include( + 'target_type' => 'MergeRequest', + 'target' => hash_including( + 'upvotes' => 1, + 'downvotes' => 0 + ) + ) - expect(json_response[4]['target_type']).to eq('AlertManagement::Alert') - expect(json_response[4]['target']['iid']).to eq(alert.iid) - expect(json_response[4]['target']['title']).to eq(alert.title) + expect(json_response[5]).to include( + 'target_type' => 'AlertManagement::Alert', + 'target' => hash_including( + 'iid' => alert.iid, + 'title' => alert.title + ) + ) end context "when current user does not have access to one of the TODO's target" do @@ -111,7 +137,7 @@ RSpec.describe API::Todos do get api('/todos', john_doe) - expect(json_response.count).to eq(5) + expect(json_response.count).to eq(6) expect(json_response.map { |t| t['id'] }).not_to include(no_access_todo.id, pending_4.id) end end @@ -169,7 +195,7 @@ RSpec.describe API::Todos do expect(response).to have_gitlab_http_status(:ok) expect(response).to include_pagination_headers expect(json_response).to be_an Array - expect(json_response.length).to eq(4) + expect(json_response.length).to eq(5) end end @@ -180,7 +206,7 @@ RSpec.describe API::Todos do expect(response).to have_gitlab_http_status(:ok) expect(response).to include_pagination_headers expect(json_response).to be_an Array - expect(json_response.length).to eq(1) + expect(json_response.length).to eq(2) end end end @@ -189,6 +215,10 @@ RSpec.describe API::Todos do create_issue_todo_for(john_doe) create(:todo, project: project_1, author: author_2, user: john_doe, target: merge_request) + # Destroying to preserve behavior prior to fixing todo's target as WorkItem + # TODO: https://gitlab.com/gitlab-org/gitlab/-/issues/375293 remove when N + 1 are fixed + pending_5.destroy! + get api('/todos', john_doe) control1 = ActiveRecord::QueryRecorder.new { get api('/todos', john_doe) } |