Commit 9584af7c authored by Jan Provaznik's avatar Jan Provaznik Committed by Sean McGivern

Add delete endpoint for related epics

Allows deleting related epic links through REST API, similar to
issue_links endpoint for issues.

Changelog: added
EE: true
parent 66f9d9b7
......@@ -91,6 +91,8 @@ Example response:
## Create a related epic link
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/352840) in GitLab 14.10.
Create a two-way relation between two epics. The user must be allowed to
update both epics to succeed.
......@@ -205,3 +207,120 @@ Example response:
"link_type": "relates_to"
}
```
## Delete a related epic link
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/352840) in GitLab 14.10.
Delete a two-way relation between two epics. The user must be allowed to
update both epics to succeed.
```plaintext
DELETE /groups/:id/epics/:epic_iid/related_epics/:related_epic_link_id
```
Supported attributes:
| Attribute | Type | Required | Description |
|--------------------------|----------------|-----------------------------|---------------------------------------|
| `epic_iid` | integer | **{check-circle}** Yes | Internal ID of a group's epic. |
| `id` | integer/string | **{check-circle}** Yes | ID or [URL-encoded path of the group](index.md#namespaced-path-encoding) owned by the authenticated user. |
| `related_epic_link_id` | integer/string | **{check-circle}** Yes | Internal ID of a related epic link. |
Example request:
```shell
curl --request DELETE --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/groups/26/epics/1/related_epics/1"
```
Example response:
```json
{
"source_epic": {
"id": 21,
"iid": 1,
"color": "#1068bf",
"text_color": "#FFFFFF",
"group_id": 26,
"parent_id": null,
"parent_iid": null,
"title": "Aspernatur recusandae distinctio omnis et qui est iste.",
"description": "some description",
"confidential": false,
"author": {
"id": 15,
"username": "trina",
"name": "Theresia Robel",
"state": "active",
"avatar_url": "https://www.gravatar.com/avatar/085e28df717e16484cbf6ceca75e9a93?s=80&d=identicon",
"web_url": "http://gitlab.example.com/trina"
},
"start_date": null,
"end_date": null,
"due_date": null,
"state": "opened",
"web_url": "http://gitlab.example.com/groups/flightjs/-/epics/1",
"references": {
"short": "&1",
"relative": "&1",
"full": "flightjs&1"
},
"created_at": "2022-01-31T15:10:44.988Z",
"updated_at": "2022-03-16T09:32:35.712Z",
"closed_at": null,
"labels": [],
"upvotes": 0,
"downvotes": 0,
"_links": {
"self": "http://gitlab.example.com/api/v4/groups/26/epics/1",
"epic_issues": "http://gitlab.example.com/api/v4/groups/26/epics/1/issues",
"group": "http://gitlab.example.com/api/v4/groups/26",
"parent": null
}
},
"target_epic": {
"id": 25,
"iid": 5,
"color": "#1068bf",
"text_color": "#FFFFFF",
"group_id": 26,
"parent_id": null,
"parent_iid": null,
"title": "Aut assumenda id nihil distinctio fugiat vel numquam est.",
"description": "some description",
"confidential": false,
"author": {
"id": 3,
"username": "valerie",
"name": "Erika Wolf",
"state": "active",
"avatar_url": "https://www.gravatar.com/avatar/9ef7666abb101418a4716a8ed4dded80?s=80&d=identicon",
"web_url": "http://gitlab.example.com/valerie"
},
"start_date": null,
"end_date": null,
"due_date": null,
"state": "opened",
"web_url": "http://gitlab.example.com/groups/flightjs/-/epics/5",
"references": {
"short": "&5",
"relative": "&5",
"full": "flightjs&5"
},
"created_at": "2022-01-31T15:10:45.080Z",
"updated_at": "2022-03-16T09:32:35.842Z",
"closed_at": null,
"labels": [],
"upvotes": 0,
"downvotes": 0,
"_links": {
"self": "http://gitlab.example.com/api/v4/groups/26/epics/5",
"epic_issues": "http://gitlab.example.com/api/v4/groups/26/epics/5/issues",
"group": "http://gitlab.example.com/api/v4/groups/26",
"parent": null
}
},
"link_type": "relates_to"
}
```
......@@ -84,6 +84,28 @@ module API
render_api_error!(result[:message], result[:http_status])
end
end
desc 'Remove epics relation' do
success Entities::RelatedEpicLink
end
params do
requires :related_epic_link_id, type: Integer, desc: 'The ID of a related epic link'
end
delete ':id/epics/:epic_iid/related_epics/:related_epic_link_id' do
find_permissioned_epic!(params[:epic_iid])
epic_link = ::Epic::RelatedEpicLink.find(declared_params[:related_epic_link_id])
result = ::Epics::RelatedEpicLinks::DestroyService
.new(epic_link, current_user)
.execute
if result[:status] == :success
present epic_link, with: Entities::RelatedEpicLink
else
render_api_error!(result[:message], result[:http_status])
end
end
end
end
end
......@@ -189,13 +189,97 @@ RSpec.describe API::RelatedEpicLinks do
it_behaves_like 'not found resource', '404 Not found'
end
end
end
end
describe 'DELETE /related_epics' do
let_it_be(:target_group) { create(:group, :private) }
let_it_be(:target_epic) { create(:epic, group: target_group) }
let_it_be_with_reload(:related_epic_link) { create(:related_epic_link, source: epic, target: target_epic) }
subject { perform_request(user) }
def perform_request(user = nil)
delete api("/groups/#{group.id}/epics/#{epic.iid}/related_epics/#{related_epic_link.id}", user)
end
shared_examples 'not found resource' do |message|
it 'returns 404' do
subject
expect(response).to have_gitlab_http_status(:not_found)
expect(json_response['message']).to eq(message)
end
end
shared_examples 'forbidden resource' do |message|
it 'returns 403' do
subject
expect(response).to have_gitlab_http_status(:forbidden)
end
end
context 'when unauthenticated' do
it 'returns 401' do
perform_request
expect(response).to have_gitlab_http_status(:unauthorized)
end
end
context 'when user can not access source epic' do
before do
target_group.add_reporter(user)
end
it_behaves_like 'not found resource', '404 Group Not Found'
end
context 'when user can only read source epic' do
before do
group.add_guest(user)
target_group.add_reporter(user)
end
it_behaves_like 'forbidden resource'
end
context 'when user can manage source epic' do
before do
group.add_reporter(user)
end
def expect_link_response(link_type: 'relates_to')
expect(response).to have_gitlab_http_status(:created)
expect(response).to match_response_schema('public_api/v4/related_epic_link')
expect(json_response['link_type']).to eq(link_type)
it_behaves_like 'not found resource', 'No Related Epic Link found'
context 'when user is guest in target group' do
before do
target_group.add_guest(user)
end
it_behaves_like 'not found resource', 'No Related Epic Link found'
end
context 'when user can relate epics' do
before do
target_group.add_reporter(user)
end
it_behaves_like 'a not available endpoint'
it 'returns 200 status and contains the expected link response' do
subject
expect_link_response(status: :ok)
end
end
end
end
def expect_link_response(link_type: 'relates_to', status: :created)
expect(response).to have_gitlab_http_status(status)
expect(response).to match_response_schema('public_api/v4/related_epic_link')
expect(json_response['link_type']).to eq(link_type)
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