Commit 2c229f97 authored by Lin Jen-Shin's avatar Lin Jen-Shin

Merge branch '347527-no-way-to-access-parent-epic-from-epic-api-details' into 'master'

feat: Include epic parent_iid when epic has a parent

See merge request gitlab-org/gitlab!76443
parents a898ae32 16c439ad
...@@ -49,6 +49,8 @@ NOTE: ...@@ -49,6 +49,8 @@ NOTE:
## List epics for a group ## List epics for a group
> `parent_iid` and `_links[parent]` in response were [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/347527) in GitLab 14.6.
Gets all epics of the requested group and its subgroups. Gets all epics of the requested group and its subgroups.
```plaintext ```plaintext
...@@ -89,6 +91,7 @@ Example response: ...@@ -89,6 +91,7 @@ Example response:
"iid": 4, "iid": 4,
"group_id": 7, "group_id": 7,
"parent_id": 23, "parent_id": 23,
"parent_iid": 3,
"title": "Accusamus iste et ullam ratione voluptatem omnis debitis dolor est.", "title": "Accusamus iste et ullam ratione voluptatem omnis debitis dolor est.",
"description": "Molestias dolorem eos vitae expedita impedit necessitatibus quo voluptatum.", "description": "Molestias dolorem eos vitae expedita impedit necessitatibus quo voluptatum.",
"state": "opened", "state": "opened",
...@@ -128,7 +131,8 @@ Example response: ...@@ -128,7 +131,8 @@ Example response:
"_links":{ "_links":{
"self": "http://gitlab.example.com/api/v4/groups/7/epics/4", "self": "http://gitlab.example.com/api/v4/groups/7/epics/4",
"epic_issues": "http://gitlab.example.com/api/v4/groups/7/epics/4/issues", "epic_issues": "http://gitlab.example.com/api/v4/groups/7/epics/4/issues",
"group":"http://gitlab.example.com/api/v4/groups/7" "group":"http://gitlab.example.com/api/v4/groups/7",
"parent":"http://gitlab.example.com/api/v4/groups/7/epics/3"
} }
}, },
{ {
...@@ -136,6 +140,7 @@ Example response: ...@@ -136,6 +140,7 @@ Example response:
"iid": 35, "iid": 35,
"group_id": 17, "group_id": 17,
"parent_id": 19, "parent_id": 19,
"parent_iid": 1,
"title": "Accusamus iste et ullam ratione voluptatem omnis debitis dolor est.", "title": "Accusamus iste et ullam ratione voluptatem omnis debitis dolor est.",
"description": "Molestias dolorem eos vitae expedita impedit necessitatibus quo voluptatum.", "description": "Molestias dolorem eos vitae expedita impedit necessitatibus quo voluptatum.",
"state": "opened", "state": "opened",
...@@ -174,7 +179,8 @@ Example response: ...@@ -174,7 +179,8 @@ Example response:
"_links":{ "_links":{
"self": "http://gitlab.example.com/api/v4/groups/17/epics/35", "self": "http://gitlab.example.com/api/v4/groups/17/epics/35",
"epic_issues": "http://gitlab.example.com/api/v4/groups/17/epics/35/issues", "epic_issues": "http://gitlab.example.com/api/v4/groups/17/epics/35/issues",
"group":"http://gitlab.example.com/api/v4/groups/17" "group":"http://gitlab.example.com/api/v4/groups/17",
"parent":"http://gitlab.example.com/api/v4/groups/17/epics/1"
} }
} }
] ]
...@@ -182,6 +188,8 @@ Example response: ...@@ -182,6 +188,8 @@ Example response:
## Single epic ## Single epic
> `parent_iid` and `_links[parent]` in response were [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/347527) in GitLab 14.6.
Gets a single epic Gets a single epic
```plaintext ```plaintext
...@@ -204,6 +212,8 @@ Example response: ...@@ -204,6 +212,8 @@ Example response:
"id": 30, "id": 30,
"iid": 5, "iid": 5,
"group_id": 7, "group_id": 7,
"parent_id": null,
"parent_iid": null,
"title": "Ea cupiditate dolores ut vero consequatur quasi veniam voluptatem et non.", "title": "Ea cupiditate dolores ut vero consequatur quasi veniam voluptatem et non.",
"description": "Molestias dolorem eos vitae expedita impedit necessitatibus quo voluptatum.", "description": "Molestias dolorem eos vitae expedita impedit necessitatibus quo voluptatum.",
"state": "opened", "state": "opened",
...@@ -243,13 +253,16 @@ Example response: ...@@ -243,13 +253,16 @@ Example response:
"_links":{ "_links":{
"self": "http://gitlab.example.com/api/v4/groups/7/epics/5", "self": "http://gitlab.example.com/api/v4/groups/7/epics/5",
"epic_issues": "http://gitlab.example.com/api/v4/groups/7/epics/5/issues", "epic_issues": "http://gitlab.example.com/api/v4/groups/7/epics/5/issues",
"group":"http://gitlab.example.com/api/v4/groups/7" "group":"http://gitlab.example.com/api/v4/groups/7",
"parent": null
} }
} }
``` ```
## New epic ## New epic
> `parent_iid` and `_links[parent]` in response were [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/347527) in GitLab 14.6.
Creates a new epic. Creates a new epic.
NOTE: NOTE:
...@@ -276,7 +289,7 @@ POST /groups/:id/epics ...@@ -276,7 +289,7 @@ POST /groups/:id/epics
| `parent_id` | integer/string | no | The ID of a parent epic (in GitLab 11.11 and later) | | `parent_id` | integer/string | no | The ID of a parent epic (in GitLab 11.11 and later) |
```shell ```shell
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/groups/1/epics?title=Epic&description=Epic%20description" curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/groups/1/epics?title=Epic&description=Epic%20description&parent_id=29"
``` ```
Example response: Example response:
...@@ -286,6 +299,8 @@ Example response: ...@@ -286,6 +299,8 @@ Example response:
"id": 33, "id": 33,
"iid": 6, "iid": 6,
"group_id": 7, "group_id": 7,
"parent_id": 29,
"parent_iid": 4,
"title": "Epic", "title": "Epic",
"description": "Epic description", "description": "Epic description",
"state": "opened", "state": "opened",
...@@ -325,13 +340,16 @@ Example response: ...@@ -325,13 +340,16 @@ Example response:
"_links":{ "_links":{
"self": "http://gitlab.example.com/api/v4/groups/7/epics/6", "self": "http://gitlab.example.com/api/v4/groups/7/epics/6",
"epic_issues": "http://gitlab.example.com/api/v4/groups/7/epics/6/issues", "epic_issues": "http://gitlab.example.com/api/v4/groups/7/epics/6/issues",
"group":"http://gitlab.example.com/api/v4/groups/7" "group":"http://gitlab.example.com/api/v4/groups/7",
"parent": "http://gitlab.example.com/api/v4/groups/7/epics/4"
} }
} }
``` ```
## Update epic ## Update epic
> `parent_iid` and `_links[parent]` in response were [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/347527) in GitLab 14.6.
Updates an epic. Updates an epic.
NOTE: NOTE:
...@@ -371,6 +389,8 @@ Example response: ...@@ -371,6 +389,8 @@ Example response:
"id": 33, "id": 33,
"iid": 6, "iid": 6,
"group_id": 7, "group_id": 7,
"parent_id": null,
"parent_iid": null,
"title": "New Title", "title": "New Title",
"description": "Epic description", "description": "Epic description",
"state": "opened", "state": "opened",
......
...@@ -137,7 +137,7 @@ module EE ...@@ -137,7 +137,7 @@ module EE
where(boards_epic_board_positions: { relative_position: nil }) where(boards_epic_board_positions: { relative_position: nil })
end end
scope :with_api_entity_associations, -> { preload(:author, :labels, group: :route) } scope :with_api_entity_associations, -> { preload(:author, :labels, :parent, group: :route) }
scope :start_date_inherited, -> { where(start_date_is_fixed: [nil, false]) } scope :start_date_inherited, -> { where(start_date_is_fixed: [nil, false]) }
scope :due_date_inherited, -> { where(due_date_is_fixed: [nil, false]) } scope :due_date_inherited, -> { where(due_date_is_fixed: [nil, false]) }
......
...@@ -12,6 +12,9 @@ module EE ...@@ -12,6 +12,9 @@ module EE
expose :iid expose :iid
expose :group_id expose :group_id
expose :parent_id expose :parent_id
expose :parent_iid do |epic|
epic.parent.iid if epic.has_parent?
end
expose :title expose :title
expose :description expose :description
expose :confidential expose :confidential
...@@ -92,6 +95,10 @@ module EE ...@@ -92,6 +95,10 @@ module EE
expose :group do |epic| expose :group do |epic|
expose_url(api_v4_groups_path(id: epic.group_id)) expose_url(api_v4_groups_path(id: epic.group_id))
end end
expose :parent do |epic|
expose_url(api_v4_groups_epics_path(id: epic.parent.group_id, epic_iid: epic.parent.iid)) if epic.has_parent?
end
end end
end end
end end
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
"iid": { "type": "integer" }, "iid": { "type": "integer" },
"group_id": { "type": "integer" }, "group_id": { "type": "integer" },
"parent_id": { "type": ["integer", "null"] }, "parent_id": { "type": ["integer", "null"] },
"parent_iid": { "type": ["integer", "null"] },
"title": { "type": "string" }, "title": { "type": "string" },
"description": { "type": ["string", "null"] }, "description": { "type": ["string", "null"] },
"confidential": { "type": "boolean" }, "confidential": { "type": "boolean" },
...@@ -58,6 +59,7 @@ ...@@ -58,6 +59,7 @@
"self": { "type": "uri" }, "self": { "type": "uri" },
"epic_issues": { "type": "uri" }, "epic_issues": { "type": "uri" },
"group": { "type": "uri" }, "group": { "type": "uri" },
"parent": { "type": "uri" },
"additionalProperties": false "additionalProperties": false
} }
} }
......
...@@ -96,8 +96,8 @@ RSpec.describe API::Epics do ...@@ -96,8 +96,8 @@ RSpec.describe API::Epics do
pat = create(:personal_access_token, user: user) pat = create(:personal_access_token, user: user)
subgroup_1 = create(:group, parent: group) subgroup_1 = create(:group, parent: group)
subgroup_2 = create(:group, parent: subgroup_1) subgroup_2 = create(:group, parent: subgroup_1)
create(:epic, group: subgroup_1) epic1 = create(:epic, group: subgroup_1)
create(:epic, group: subgroup_2) epic2 = create(:epic, group: subgroup_2, parent_id: epic.id)
control = ActiveRecord::QueryRecorder.new(skip_cached: false) do control = ActiveRecord::QueryRecorder.new(skip_cached: false) do
get api(url, personal_access_token: pat), params: params get api(url, personal_access_token: pat), params: params
...@@ -105,6 +105,8 @@ RSpec.describe API::Epics do ...@@ -105,6 +105,8 @@ RSpec.describe API::Epics do
label_2 = create(:label) label_2 = create(:label)
create_list(:labeled_epic, 2, group: group, labels: [label_2]) create_list(:labeled_epic, 2, group: group, labels: [label_2])
create_list(:epic, 2, group: subgroup_1, parent_id: epic1.id)
create_list(:epic, 2, group: subgroup_2, parent_id: epic2.id)
expect { get api(url, personal_access_token: pat), params: params }.not_to exceed_all_query_limit(control) expect { get api(url, personal_access_token: pat), params: params }.not_to exceed_all_query_limit(control)
expect(response).to have_gitlab_http_status(:ok) expect(response).to have_gitlab_http_status(:ok)
...@@ -156,6 +158,24 @@ RSpec.describe API::Epics do ...@@ -156,6 +158,24 @@ RSpec.describe API::Epics do
end end
end end
context 'with a parent epic' do
let!(:epic) { create(:epic, group: group) }
let!(:epic2) { create(:epic, group: group, parent_id: epic.id) }
before do
stub_licensed_features(epics: true)
end
it 'returns parent_id and parent_iid' do
get api(url, user)
epics = json_response
expect(epics.map { |e| e["parent_id"] }).to match_array([nil, epic.id])
expect(epics.map { |e| e["parent_iid"] }).to match_array([nil, epic.iid])
end
end
context 'with multiple epics' do context 'with multiple epics' do
let(:user2) { create(:user) } let(:user2) { create(:user) }
let!(:epic) do let!(:epic) do
...@@ -569,6 +589,20 @@ RSpec.describe API::Epics do ...@@ -569,6 +589,20 @@ RSpec.describe API::Epics do
expect(links['self']).to end_with("/api/v4/groups/#{epic.group.id}/epics/#{epic.iid}") expect(links['self']).to end_with("/api/v4/groups/#{epic.group.id}/epics/#{epic.iid}")
expect(links['epic_issues']).to end_with("/api/v4/groups/#{epic.group.id}/epics/#{epic.iid}/issues") expect(links['epic_issues']).to end_with("/api/v4/groups/#{epic.group.id}/epics/#{epic.iid}/issues")
expect(links['group']).to end_with("/api/v4/groups/#{epic.group.id}") expect(links['group']).to end_with("/api/v4/groups/#{epic.group.id}")
expect(links['parent']).to eq(nil)
end
context 'with a parent epic' do
let!(:epic2) { create(:epic, group: group, parent_id: epic.id) }
let(:url) { "/groups/#{group.path}/epics/#{epic2.iid}" }
it 'exposes parent link' do
get api(url)
links = json_response['_links']
expect(links['parent']).to end_with("/api/v4/groups/#{epic.group.id}/epics/#{epic.iid}")
end
end end
it_behaves_like 'can admin epics' it_behaves_like 'can admin epics'
...@@ -619,6 +653,14 @@ RSpec.describe API::Epics do ...@@ -619,6 +653,14 @@ RSpec.describe API::Epics do
expect(response).to match_response_schema('public_api/v4/epic', dir: 'ee') expect(response).to match_response_schema('public_api/v4/epic', dir: 'ee')
end end
it 'exposes parent information' do
post api(url, user), params: params
expect(json_response['parent_id']).to eq(parent_epic.id)
expect(json_response['parent_iid']).to eq(parent_epic.iid)
expect(json_response['_links']['parent']).to end_with("/api/v4/groups/#{parent_epic.group.id}/epics/#{parent_epic.iid}")
end
it 'creates a new epic' do it 'creates a new epic' do
epic = Epic.last epic = Epic.last
......
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