Commit e7c1b65a authored by Jan Provaznik's avatar Jan Provaznik

Expose link_type in REST API

Exposes issue link's type in REST API.
parent 430a2ccd
---
title: Expose issue link type in REST API
merge_request: 21375
author:
type: added
...@@ -48,6 +48,7 @@ Parameters: ...@@ -48,6 +48,7 @@ Parameters:
"web_url": "http://example.com/example/example/issues/14", "web_url": "http://example.com/example/example/issues/14",
"confidential": false, "confidential": false,
"weight": null, "weight": null,
"link_type": "relates_to",
} }
] ]
``` ```
...@@ -66,6 +67,7 @@ POST /projects/:id/issues/:issue_iid/links ...@@ -66,6 +67,7 @@ POST /projects/:id/issues/:issue_iid/links
| `issue_iid` | integer | yes | The internal ID of a project's issue | | `issue_iid` | integer | yes | The internal ID of a project's issue |
| `target_project_id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) of a target project | | `target_project_id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) of a target project |
| `target_issue_iid` | integer/string | yes | The internal ID of a target project's issue | | `target_issue_iid` | integer/string | yes | The internal ID of a target project's issue |
| `link_type` | string | no | The type of the relation ("relates_to", "blocks", "is_blocked_by"), defaults to "relates_to"). Ignored unless `issue_link_types` feature flag is enabled. |
```bash ```bash
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/4/issues/1/links?target_project_id=5&target_issue_iid=1" curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/4/issues/1/links?target_project_id=5&target_issue_iid=1"
...@@ -134,7 +136,8 @@ Example response: ...@@ -134,7 +136,8 @@ Example response:
"web_url": "http://example.com/example/example/issues/14", "web_url": "http://example.com/example/example/issues/14",
"confidential": false, "confidential": false,
"weight": null, "weight": null,
} },
"link_type": "relates_to"
} }
``` ```
...@@ -151,6 +154,7 @@ DELETE /projects/:id/issues/:issue_iid/links/:issue_link_id ...@@ -151,6 +154,7 @@ DELETE /projects/:id/issues/:issue_iid/links/:issue_link_id
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user | | `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `issue_iid` | integer | yes | The internal ID of a project's issue | | `issue_iid` | integer | yes | The internal ID of a project's issue |
| `issue_link_id` | integer/string | yes | The ID of an issue relationship | | `issue_link_id` | integer/string | yes | The ID of an issue relationship |
| `link_type` | string | no | The type of the relation ('relates_to', 'blocks', 'is_blocked_by'), defaults to 'relates_to' |
```json ```json
{ {
...@@ -213,6 +217,7 @@ DELETE /projects/:id/issues/:issue_iid/links/:issue_link_id ...@@ -213,6 +217,7 @@ DELETE /projects/:id/issues/:issue_iid/links/:issue_link_id
"web_url": "http://example.com/example/example/issues/14", "web_url": "http://example.com/example/example/issues/14",
"confidential": false, "confidential": false,
"weight": null, "weight": null,
} },
"link_type": "relates_to"
} }
``` ```
...@@ -30,6 +30,8 @@ module API ...@@ -30,6 +30,8 @@ module API
params do params do
requires :target_project_id, type: String, desc: 'The ID of the target project' requires :target_project_id, type: String, desc: 'The ID of the target project'
requires :target_issue_iid, type: Integer, desc: 'The IID of the target issue' requires :target_issue_iid, type: Integer, desc: 'The IID of the target issue'
optional :link_type, type: String, values: IssueLink.link_types.keys,
desc: 'The type of the relation. Ignored unless `issue_link_types` feature flag is enabled.'
end end
# rubocop: disable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord
post ':id/issues/:issue_iid/links' do post ':id/issues/:issue_iid/links' do
...@@ -37,7 +39,7 @@ module API ...@@ -37,7 +39,7 @@ module API
target_issue = find_project_issue(declared_params[:target_issue_iid], target_issue = find_project_issue(declared_params[:target_issue_iid],
declared_params[:target_project_id]) declared_params[:target_project_id])
create_params = { target_issuable: target_issue } create_params = { target_issuable: target_issue, link_type: params[:link_type] }
result = ::IssueLinks::CreateService result = ::IssueLinks::CreateService
.new(source_issue, current_user, create_params) .new(source_issue, current_user, create_params)
......
...@@ -269,6 +269,7 @@ module EE ...@@ -269,6 +269,7 @@ module EE
class RelatedIssue < ::API::Entities::Issue class RelatedIssue < ::API::Entities::Issue
expose :issue_link_id expose :issue_link_id
expose :issue_link_type, as: :link_type
end end
class LinkedEpic < Grape::Entity class LinkedEpic < Grape::Entity
...@@ -395,6 +396,7 @@ module EE ...@@ -395,6 +396,7 @@ module EE
class IssueLink < Grape::Entity class IssueLink < Grape::Entity
expose :source, as: :source_issue, using: ::API::Entities::IssueBasic expose :source, as: :source_issue, using: ::API::Entities::IssueBasic
expose :target, as: :target_issue, using: ::API::Entities::IssueBasic expose :target, as: :target_issue, using: ::API::Entities::IssueBasic
expose :link_type
end end
class SpecialBoardFilter < Grape::Entity class SpecialBoardFilter < Grape::Entity
......
...@@ -3,9 +3,9 @@ ...@@ -3,9 +3,9 @@
require 'spec_helper' require 'spec_helper'
describe API::IssueLinks do describe API::IssueLinks do
let(:user) { create(:user) } let_it_be(:user) { create(:user) }
let(:project) { create(:project) } let_it_be(:project) { create(:project) }
let(:issue) { create(:issue, project: project) } let_it_be(:issue) { create(:issue, project: project) }
before do before do
project.add_guest(user) project.add_guest(user)
...@@ -30,7 +30,7 @@ describe API::IssueLinks do ...@@ -30,7 +30,7 @@ describe API::IssueLinks do
expect(response).to have_gitlab_http_status(200) expect(response).to have_gitlab_http_status(200)
expect(json_response).to be_an Array expect(json_response).to be_an Array
expect(json_response.length).to eq(1) expect(json_response.length).to eq(1)
expect(json_response.first).to include('iid', 'title', 'issue_link_id') expect(json_response.first).to include('iid', 'title', 'issue_link_id', 'link_type')
end end
end end
end end
...@@ -113,26 +113,43 @@ describe API::IssueLinks do ...@@ -113,26 +113,43 @@ describe API::IssueLinks do
end end
context 'when user has ability to create an issue link' do context 'when user has ability to create an issue link' do
it 'returns 201' do let_it_be(:target_issue) { create(:issue, project: project) }
target_issue = create(:issue, project: project)
before do
project.add_reporter(user) project.add_reporter(user)
end
it 'returns 201' do
post api("/projects/#{project.id}/issues/#{issue.iid}/links", user), post api("/projects/#{project.id}/issues/#{issue.iid}/links", user),
params: { target_project_id: project.id, target_issue_iid: target_issue.iid } params: { target_project_id: project.id, target_issue_iid: target_issue.iid, link_type: 'blocks' }
expect(response).to have_gitlab_http_status(201) expect_link_response(link_type: 'blocks')
expect(json_response).to include('source_issue', 'target_issue')
end end
it 'returns 201 when sending full path of target project' do context 'when `issue_link_type` is disabled' do
target_issue = create(:issue, project: project) before do
project.add_reporter(user) stub_feature_flags(issue_link_types: false)
end
it 'ignores `link_type` parameter' do
post api("/projects/#{project.id}/issues/#{issue.iid}/links", user),
params: { target_project_id: project.id, target_issue_iid: target_issue.iid, link_type: 'blocks' }
expect_link_response
end
end
it 'returns 201 when sending full path of target project' do
post api("/projects/#{project.id}/issues/#{issue.iid}/links", user), post api("/projects/#{project.id}/issues/#{issue.iid}/links", user),
params: { target_project_id: project.to_reference(full: true), target_issue_iid: target_issue.iid } params: { target_project_id: project.to_reference(full: true), target_issue_iid: target_issue.iid }
expect_link_response
end
def expect_link_response(link_type: 'relates_to')
expect(response).to have_gitlab_http_status(201) expect(response).to have_gitlab_http_status(201)
expect(json_response).to include('source_issue', 'target_issue') expect(json_response).to include('source_issue', 'target_issue', 'link_type')
expect(json_response['link_type']).to eq(link_type)
end end
end end
end end
...@@ -195,7 +212,7 @@ describe API::IssueLinks do ...@@ -195,7 +212,7 @@ describe API::IssueLinks do
delete api("/projects/#{project.id}/issues/#{issue.iid}/links/#{issue_link.id}", user) delete api("/projects/#{project.id}/issues/#{issue.iid}/links/#{issue_link.id}", user)
expect(response).to have_gitlab_http_status(200) expect(response).to have_gitlab_http_status(200)
expect(json_response).to include('source_issue', 'target_issue') expect(json_response).to include('source_issue', 'target_issue', 'link_type')
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