Commit 7a774d1a authored by Douwe Maan's avatar Douwe Maan

Merge branch '28251-mr-and-issue-iids-for-api-v4' into 'master'

API routes referencing a specific issue should use the issue `iid`

Closes #28251

See merge request !9530
parents bec5c370 6384bf7a
...@@ -14,8 +14,8 @@ requests, snippets, and notes/comments. Issues, merge requests, snippets, and no ...@@ -14,8 +14,8 @@ requests, snippets, and notes/comments. Issues, merge requests, snippets, and no
Gets a list of all award emoji Gets a list of all award emoji
``` ```
GET /projects/:id/issues/:issue_id/award_emoji GET /projects/:id/issues/:issue_iid/award_emoji
GET /projects/:id/merge_requests/:merge_request_id/award_emoji GET /projects/:id/merge_requests/:merge_request_iid/award_emoji
GET /projects/:id/snippets/:snippet_id/award_emoji GET /projects/:id/snippets/:snippet_id/award_emoji
``` ```
...@@ -24,7 +24,7 @@ Parameters: ...@@ -24,7 +24,7 @@ Parameters:
| Attribute | Type | Required | Description | | Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- | | --------- | ---- | -------- | ----------- |
| `id` | integer | yes | The ID of a project | | `id` | integer | yes | The ID of a project |
| `awardable_id` | integer | yes | The ID of an awardable | | `awardable_id` | integer | yes | The ID (`iid` for merge requests/issues, `id` for snippets) of an awardable |
```bash ```bash
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/1/issues/80/award_emoji curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/1/issues/80/award_emoji
...@@ -74,8 +74,8 @@ Example Response: ...@@ -74,8 +74,8 @@ Example Response:
Gets a single award emoji from an issue, snippet, or merge request. Gets a single award emoji from an issue, snippet, or merge request.
``` ```
GET /projects/:id/issues/:issue_id/award_emoji/:award_id GET /projects/:id/issues/:issue_iid/award_emoji/:award_id
GET /projects/:id/merge_requests/:merge_request_id/award_emoji/:award_id GET /projects/:id/merge_requests/:merge_request_iid/award_emoji/:award_id
GET /projects/:id/snippets/:snippet_id/award_emoji/:award_id GET /projects/:id/snippets/:snippet_id/award_emoji/:award_id
``` ```
...@@ -84,7 +84,7 @@ Parameters: ...@@ -84,7 +84,7 @@ Parameters:
| Attribute | Type | Required | Description | | Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- | | --------- | ---- | -------- | ----------- |
| `id` | integer | yes | The ID of a project | | `id` | integer | yes | The ID of a project |
| `awardable_id` | integer | yes | The ID of an awardable | | `awardable_id` | integer | yes | The ID (`iid` for merge requests/issues, `id` for snippets) of an awardable |
| `award_id` | integer | yes | The ID of the award emoji | | `award_id` | integer | yes | The ID of the award emoji |
```bash ```bash
...@@ -117,8 +117,8 @@ Example Response: ...@@ -117,8 +117,8 @@ Example Response:
This end point creates an award emoji on the specified resource This end point creates an award emoji on the specified resource
``` ```
POST /projects/:id/issues/:issue_id/award_emoji POST /projects/:id/issues/:issue_iid/award_emoji
POST /projects/:id/merge_requests/:merge_request_id/award_emoji POST /projects/:id/merge_requests/:merge_request_iid/award_emoji
POST /projects/:id/snippets/:snippet_id/award_emoji POST /projects/:id/snippets/:snippet_id/award_emoji
``` ```
...@@ -127,7 +127,7 @@ Parameters: ...@@ -127,7 +127,7 @@ Parameters:
| Attribute | Type | Required | Description | | Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- | | --------- | ---- | -------- | ----------- |
| `id` | integer | yes | The ID of a project | | `id` | integer | yes | The ID of a project |
| `awardable_id` | integer | yes | The ID of an awardable | | `awardable_id` | integer | yes | The ID (`iid` for merge requests/issues, `id` for snippets) of an awardable |
| `name` | string | yes | The name of the emoji, without colons | | `name` | string | yes | The name of the emoji, without colons |
```bash ```bash
...@@ -161,8 +161,8 @@ Sometimes its just not meant to be, and you'll have to remove your award. Only a ...@@ -161,8 +161,8 @@ Sometimes its just not meant to be, and you'll have to remove your award. Only a
admins or the author of the award. admins or the author of the award.
``` ```
DELETE /projects/:id/issues/:issue_id/award_emoji/:award_id DELETE /projects/:id/issues/:issue_iid/award_emoji/:award_id
DELETE /projects/:id/merge_requests/:merge_request_id/award_emoji/:award_id DELETE /projects/:id/merge_requests/:merge_request_iid/award_emoji/:award_id
DELETE /projects/:id/snippets/:snippet_id/award_emoji/:award_id DELETE /projects/:id/snippets/:snippet_id/award_emoji/:award_id
``` ```
...@@ -171,7 +171,7 @@ Parameters: ...@@ -171,7 +171,7 @@ Parameters:
| Attribute | Type | Required | Description | | Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- | | --------- | ---- | -------- | ----------- |
| `id` | integer | yes | The ID of a project | | `id` | integer | yes | The ID of a project |
| `issue_id` | integer | yes | The ID of an issue | | `issue_iid` | integer | yes | The internal ID of an issue |
| `award_id` | integer | yes | The ID of a award_emoji | | `award_id` | integer | yes | The ID of a award_emoji |
```bash ```bash
...@@ -188,7 +188,7 @@ easily adapted for notes on a Merge Request. ...@@ -188,7 +188,7 @@ easily adapted for notes on a Merge Request.
### List a note's award emoji ### List a note's award emoji
``` ```
GET /projects/:id/issues/:issue_id/notes/:note_id/award_emoji GET /projects/:id/issues/:issue_iid/notes/:note_id/award_emoji
``` ```
Parameters: Parameters:
...@@ -196,7 +196,7 @@ Parameters: ...@@ -196,7 +196,7 @@ Parameters:
| Attribute | Type | Required | Description | | Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- | | --------- | ---- | -------- | ----------- |
| `id` | integer | yes | The ID of a project | | `id` | integer | yes | The ID of a project |
| `issue_id` | integer | yes | The ID of an issue | | `issue_iid` | integer | yes | The internal ID of an issue |
| `note_id` | integer | yes | The ID of an note | | `note_id` | integer | yes | The ID of an note |
...@@ -230,7 +230,7 @@ Example Response: ...@@ -230,7 +230,7 @@ Example Response:
### Get single note's award emoji ### Get single note's award emoji
``` ```
GET /projects/:id/issues/:issue_id/notes/:note_id/award_emoji/:award_id GET /projects/:id/issues/:issue_iid/notes/:note_id/award_emoji/:award_id
``` ```
Parameters: Parameters:
...@@ -238,7 +238,7 @@ Parameters: ...@@ -238,7 +238,7 @@ Parameters:
| Attribute | Type | Required | Description | | Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- | | --------- | ---- | -------- | ----------- |
| `id` | integer | yes | The ID of a project | | `id` | integer | yes | The ID of a project |
| `issue_id` | integer | yes | The ID of an issue | | `issue_iid` | integer | yes | The internal ID of an issue |
| `note_id` | integer | yes | The ID of a note | | `note_id` | integer | yes | The ID of a note |
| `award_id` | integer | yes | The ID of the award emoji | | `award_id` | integer | yes | The ID of the award emoji |
...@@ -270,7 +270,7 @@ Example Response: ...@@ -270,7 +270,7 @@ Example Response:
### Award a new emoji on a note ### Award a new emoji on a note
``` ```
POST /projects/:id/issues/:issue_id/notes/:note_id/award_emoji POST /projects/:id/issues/:issue_iid/notes/:note_id/award_emoji
``` ```
Parameters: Parameters:
...@@ -278,7 +278,7 @@ Parameters: ...@@ -278,7 +278,7 @@ Parameters:
| Attribute | Type | Required | Description | | Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- | | --------- | ---- | -------- | ----------- |
| `id` | integer | yes | The ID of a project | | `id` | integer | yes | The ID of a project |
| `issue_id` | integer | yes | The ID of an issue | | `issue_iid` | integer | yes | The internal ID of an issue |
| `note_id` | integer | yes | The ID of a note | | `note_id` | integer | yes | The ID of a note |
| `name` | string | yes | The name of the emoji, without colons | | `name` | string | yes | The name of the emoji, without colons |
...@@ -313,7 +313,7 @@ Sometimes its just not meant to be, and you'll have to remove your award. Only a ...@@ -313,7 +313,7 @@ Sometimes its just not meant to be, and you'll have to remove your award. Only a
admins or the author of the award. admins or the author of the award.
``` ```
DELETE /projects/:id/issues/:issue_id/notes/:note_id/award_emoji/:award_id DELETE /projects/:id/issues/:issue_iid/notes/:note_id/award_emoji/:award_id
``` ```
Parameters: Parameters:
...@@ -321,7 +321,7 @@ Parameters: ...@@ -321,7 +321,7 @@ Parameters:
| Attribute | Type | Required | Description | | Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- | | --------- | ---- | -------- | ----------- |
| `id` | integer | yes | The ID of a project | | `id` | integer | yes | The ID of a project |
| `issue_id` | integer | yes | The ID of an issue | | `issue_iid` | integer | yes | The internal ID of an issue |
| `note_id` | integer | yes | The ID of a note | | `note_id` | integer | yes | The ID of a note |
| `award_id` | integer | yes | The ID of a award_emoji | | `award_id` | integer | yes | The ID of a award_emoji |
......
...@@ -261,13 +261,13 @@ Example response: ...@@ -261,13 +261,13 @@ Example response:
Get a single project issue. Get a single project issue.
``` ```
GET /projects/:id/issues/:issue_id GET /projects/:id/issues/:issue_iid
``` ```
| Attribute | Type | Required | Description | | Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- | | --------- | ---- | -------- | ----------- |
| `id` | integer | yes | The ID of a project | | `id` | integer | yes | The ID of a project |
| `issue_id`| integer | yes | The ID of a project's issue | | `issue_iid` | integer | yes | The internal ID of a project's issue |
```bash ```bash
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/4/issues/41 curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/4/issues/41
...@@ -385,13 +385,13 @@ Updates an existing project issue. This call is also used to mark an issue as ...@@ -385,13 +385,13 @@ Updates an existing project issue. This call is also used to mark an issue as
closed. closed.
``` ```
PUT /projects/:id/issues/:issue_id PUT /projects/:id/issues/:issue_iid
``` ```
| Attribute | Type | Required | Description | | Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- | | --------- | ---- | -------- | ----------- |
| `id` | integer | yes | The ID of a project | | `id` | integer | yes | The ID of a project |
| `issue_id` | integer | yes | The ID of a project's issue | | `issue_iid` | integer | yes | The internal ID of a project's issue |
| `title` | string | no | The title of an issue | | `title` | string | no | The title of an issue |
| `description` | string | no | The description of an issue | | `description` | string | no | The description of an issue |
| `confidential` | boolean | no | Updates an issue to be confidential | | `confidential` | boolean | no | Updates an issue to be confidential |
...@@ -444,13 +444,13 @@ Example response: ...@@ -444,13 +444,13 @@ Example response:
Only for admins and project owners. Soft deletes the issue in question. Only for admins and project owners. Soft deletes the issue in question.
``` ```
DELETE /projects/:id/issues/:issue_id DELETE /projects/:id/issues/:issue_iid
``` ```
| Attribute | Type | Required | Description | | Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- | | --------- | ---- | -------- | ----------- |
| `id` | integer | yes | The ID of a project | | `id` | integer | yes | The ID of a project |
| `issue_id` | integer | yes | The ID of a project's issue | | `issue_iid` | integer | yes | The internal ID of a project's issue |
```bash ```bash
curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/4/issues/85 curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/4/issues/85
...@@ -466,13 +466,13 @@ If a given label and/or milestone with the same name also exists in the target ...@@ -466,13 +466,13 @@ If a given label and/or milestone with the same name also exists in the target
project, it will then be assigned to the issue that is being moved. project, it will then be assigned to the issue that is being moved.
``` ```
POST /projects/:id/issues/:issue_id/move POST /projects/:id/issues/:issue_iid/move
``` ```
| Attribute | Type | Required | Description | | Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- | | --------- | ---- | -------- | ----------- |
| `id` | integer | yes | The ID of a project | | `id` | integer | yes | The ID of a project |
| `issue_id` | integer | yes | The ID of a project's issue | | `issue_iid` | integer | yes | The internal ID of a project's issue |
| `to_project_id` | integer | yes | The ID of the new project | | `to_project_id` | integer | yes | The ID of the new project |
```bash ```bash
...@@ -522,13 +522,13 @@ If the user is already subscribed to the issue, the status code `304` ...@@ -522,13 +522,13 @@ If the user is already subscribed to the issue, the status code `304`
is returned. is returned.
``` ```
POST /projects/:id/issues/:issue_id/subscribe POST /projects/:id/issues/:issue_iid/subscribe
``` ```
| Attribute | Type | Required | Description | | Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- | | --------- | ---- | -------- | ----------- |
| `id` | integer | yes | The ID of a project | | `id` | integer | yes | The ID of a project |
| `issue_id` | integer | yes | The ID of a project's issue | | `issue_iid` | integer | yes | The internal ID of a project's issue |
```bash ```bash
curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/issues/93/subscribe curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/issues/93/subscribe
...@@ -577,13 +577,13 @@ from it. If the user is not subscribed to the issue, the ...@@ -577,13 +577,13 @@ from it. If the user is not subscribed to the issue, the
status code `304` is returned. status code `304` is returned.
``` ```
POST /projects/:id/issues/:issue_id/unsubscribe POST /projects/:id/issues/:issue_iid/unsubscribe
``` ```
| Attribute | Type | Required | Description | | Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- | | --------- | ---- | -------- | ----------- |
| `id` | integer | yes | The ID of a project | | `id` | integer | yes | The ID of a project |
| `issue_id` | integer | yes | The ID of a project's issue | | `issue_iid` | integer | yes | The internal ID of a project's issue |
```bash ```bash
curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/issues/93/unsubscribe curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/issues/93/unsubscribe
...@@ -596,13 +596,13 @@ there already exists a todo for the user on that issue, status code `304` is ...@@ -596,13 +596,13 @@ there already exists a todo for the user on that issue, status code `304` is
returned. returned.
``` ```
POST /projects/:id/issues/:issue_id/todo POST /projects/:id/issues/:issue_iid/todo
``` ```
| Attribute | Type | Required | Description | | Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- | | --------- | ---- | -------- | ----------- |
| `id` | integer | yes | The ID of a project | | `id` | integer | yes | The ID of a project |
| `issue_id` | integer | yes | The ID of a project's issue | | `issue_iid` | integer | yes | The internal ID of a project's issue |
```bash ```bash
curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/issues/93/todo curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/issues/93/todo
...@@ -687,13 +687,13 @@ Example response: ...@@ -687,13 +687,13 @@ Example response:
Sets an estimated time of work for this issue. Sets an estimated time of work for this issue.
``` ```
POST /projects/:id/issues/:issue_id/time_estimate POST /projects/:id/issues/:issue_iid/time_estimate
``` ```
| Attribute | Type | Required | Description | | Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- | | --------- | ---- | -------- | ----------- |
| `id` | integer | yes | The ID of a project | | `id` | integer | yes | The ID of a project |
| `issue_id` | integer | yes | The ID of a project's issue | | `issue_iid` | integer | yes | The internal ID of a project's issue |
| `duration` | string | yes | The duration in human format. e.g: 3h30m | | `duration` | string | yes | The duration in human format. e.g: 3h30m |
```bash ```bash
...@@ -716,13 +716,13 @@ Example response: ...@@ -716,13 +716,13 @@ Example response:
Resets the estimated time for this issue to 0 seconds. Resets the estimated time for this issue to 0 seconds.
``` ```
POST /projects/:id/issues/:issue_id/reset_time_estimate POST /projects/:id/issues/:issue_iid/reset_time_estimate
``` ```
| Attribute | Type | Required | Description | | Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- | | --------- | ---- | -------- | ----------- |
| `id` | integer | yes | The ID of a project | | `id` | integer | yes | The ID of a project |
| `issue_id` | integer | yes | The ID of a project's issue | | `issue_iid` | integer | yes | The internal ID of a project's issue |
```bash ```bash
curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/issues/93/reset_time_estimate curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/issues/93/reset_time_estimate
...@@ -744,13 +744,13 @@ Example response: ...@@ -744,13 +744,13 @@ Example response:
Adds spent time for this issue Adds spent time for this issue
``` ```
POST /projects/:id/issues/:issue_id/add_spent_time POST /projects/:id/issues/:issue_iid/add_spent_time
``` ```
| Attribute | Type | Required | Description | | Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- | | --------- | ---- | -------- | ----------- |
| `id` | integer | yes | The ID of a project | | `id` | integer | yes | The ID of a project |
| `issue_id` | integer | yes | The ID of a project's issue | | `issue_iid` | integer | yes | The internal ID of a project's issue |
| `duration` | string | yes | The duration in human format. e.g: 3h30m | | `duration` | string | yes | The duration in human format. e.g: 3h30m |
```bash ```bash
...@@ -773,13 +773,13 @@ Example response: ...@@ -773,13 +773,13 @@ Example response:
Resets the total spent time for this issue to 0 seconds. Resets the total spent time for this issue to 0 seconds.
``` ```
POST /projects/:id/issues/:issue_id/reset_spent_time POST /projects/:id/issues/:issue_iid/reset_spent_time
``` ```
| Attribute | Type | Required | Description | | Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- | | --------- | ---- | -------- | ----------- |
| `id` | integer | yes | The ID of a project | | `id` | integer | yes | The ID of a project |
| `issue_id` | integer | yes | The ID of a project's issue | | `issue_iid` | integer | yes | The internal ID of a project's issue |
```bash ```bash
curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/issues/93/reset_spent_time curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/issues/93/reset_spent_time
...@@ -799,13 +799,13 @@ Example response: ...@@ -799,13 +799,13 @@ Example response:
## Get time tracking stats ## Get time tracking stats
``` ```
GET /projects/:id/issues/:issue_id/time_stats GET /projects/:id/issues/:issue_iid/time_stats
``` ```
| Attribute | Type | Required | Description | | Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- | | --------- | ---- | -------- | ----------- |
| `id` | integer | yes | The ID of a project | | `id` | integer | yes | The ID of a project |
| `issue_id` | integer | yes | The ID of a project's issue | | `issue_iid` | integer | yes | The internal ID of a project's issue |
```bash ```bash
curl --request GET --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/issues/93/time_stats curl --request GET --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/issues/93/time_stats
......
This diff is collapsed.
...@@ -69,4 +69,5 @@ changes are in V4: ...@@ -69,4 +69,5 @@ changes are in V4:
- `POST /projects/:id/trigger/builds` to `POST /projects/:id/trigger/pipeline` - `POST /projects/:id/trigger/builds` to `POST /projects/:id/trigger/pipeline`
- Require description when creating a new trigger `POST /projects/:id/triggers` - Require description when creating a new trigger `POST /projects/:id/triggers`
- Simplify project payload exposed on Environment endpoints [!9675](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9675) - Simplify project payload exposed on Environment endpoints [!9675](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9675)
- API uses merge request `IID`s (internal ID, as in the web UI) rather than `ID`s. This affects the merge requests, award emoji, todos, and time tracking APIs. [!9530](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9530)
- API uses issue `IID`s (internal ID, as in the web UI) rather than `ID`s. This affects the issues, award emoji, todos, and time tracking APIs. [!9530](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9530)
...@@ -5,6 +5,8 @@ module API ...@@ -5,6 +5,8 @@ module API
version %w(v3 v4), using: :path version %w(v3 v4), using: :path
version 'v3', using: :path do version 'v3', using: :path do
helpers ::API::V3::Helpers
mount ::API::V3::AwardEmoji mount ::API::V3::AwardEmoji
mount ::API::V3::Boards mount ::API::V3::Boards
mount ::API::V3::Branches mount ::API::V3::Branches
......
...@@ -3,12 +3,16 @@ module API ...@@ -3,12 +3,16 @@ module API
include PaginationParams include PaginationParams
before { authenticate! } before { authenticate! }
AWARDABLES = %w[issue merge_request snippet].freeze AWARDABLES = [
{ type: 'issue', find_by: :iid },
{ type: 'merge_request', find_by: :iid },
{ type: 'snippet', find_by: :id }
].freeze
resource :projects do resource :projects do
AWARDABLES.each do |awardable_type| AWARDABLES.each do |awardable_params|
awardable_string = awardable_type.pluralize awardable_string = awardable_params[:type].pluralize
awardable_id_string = "#{awardable_type}_id" awardable_id_string = "#{awardable_params[:type]}_#{awardable_params[:find_by]}"
params do params do
requires :id, type: String, desc: 'The ID of a project' requires :id, type: String, desc: 'The ID of a project'
...@@ -104,10 +108,10 @@ module API ...@@ -104,10 +108,10 @@ module API
note_id = params.delete(:note_id) note_id = params.delete(:note_id)
awardable.notes.find(note_id) awardable.notes.find(note_id)
elsif params.include?(:issue_id) elsif params.include?(:issue_iid)
user_project.issues.find(params[:issue_id]) user_project.issues.find_by!(iid: params[:issue_iid])
elsif params.include?(:merge_request_id) elsif params.include?(:merge_request_iid)
user_project.merge_requests.find(params[:merge_request_id]) user_project.merge_requests.find_by!(iid: params[:merge_request_iid])
else else
user_project.snippets.find(params[:snippet_id]) user_project.snippets.find(params[:snippet_id])
end end
......
...@@ -82,16 +82,16 @@ module API ...@@ -82,16 +82,16 @@ module API
label || not_found!('Label') label || not_found!('Label')
end end
def find_project_issue(id) def find_project_issue(iid)
IssuesFinder.new(current_user, project_id: user_project.id).find(id) IssuesFinder.new(current_user, project_id: user_project.id).find_by!(iid: iid)
end end
def find_project_merge_request(id) def find_project_merge_request(iid)
MergeRequestsFinder.new(current_user, project_id: user_project.id).find(id) MergeRequestsFinder.new(current_user, project_id: user_project.id).find_by!(iid: iid)
end end
def find_merge_request_with_access(id, access_level = :read_merge_request) def find_merge_request_with_access(iid, access_level = :read_merge_request)
merge_request = user_project.merge_requests.find(id) merge_request = user_project.merge_requests.find_by!(iid: iid)
authorize! access_level, merge_request authorize! access_level, merge_request
merge_request merge_request
end end
......
...@@ -102,10 +102,10 @@ module API ...@@ -102,10 +102,10 @@ module API
success Entities::Issue success Entities::Issue
end end
params do params do
requires :issue_id, type: Integer, desc: 'The ID of a project issue' requires :issue_iid, type: Integer, desc: 'The internal ID of a project issue'
end end
get ":id/issues/:issue_id" do get ":id/issues/:issue_iid" do
issue = find_project_issue(params[:issue_id]) issue = find_project_issue(params[:issue_iid])
present issue, with: Entities::Issue, current_user: current_user, project: user_project present issue, with: Entities::Issue, current_user: current_user, project: user_project
end end
...@@ -152,7 +152,7 @@ module API ...@@ -152,7 +152,7 @@ module API
success Entities::Issue success Entities::Issue
end end
params do params do
requires :issue_id, type: Integer, desc: 'The ID of a project issue' requires :issue_iid, type: Integer, desc: 'The internal ID of a project issue'
optional :title, type: String, desc: 'The title of an issue' optional :title, type: String, desc: 'The title of an issue'
optional :updated_at, type: DateTime, optional :updated_at, type: DateTime,
desc: 'Date time when the issue was updated. Available only for admins and project owners.' desc: 'Date time when the issue was updated. Available only for admins and project owners.'
...@@ -161,8 +161,8 @@ module API ...@@ -161,8 +161,8 @@ module API
at_least_one_of :title, :description, :assignee_id, :milestone_id, at_least_one_of :title, :description, :assignee_id, :milestone_id,
:labels, :created_at, :due_date, :confidential, :state_event :labels, :created_at, :due_date, :confidential, :state_event
end end
put ':id/issues/:issue_id' do put ':id/issues/:issue_iid' do
issue = user_project.issues.find(params.delete(:issue_id)) issue = user_project.issues.find_by!(iid: params.delete(:issue_iid))
authorize! :update_issue, issue authorize! :update_issue, issue
# Setting created_at time only allowed for admins and project owners # Setting created_at time only allowed for admins and project owners
...@@ -189,11 +189,11 @@ module API ...@@ -189,11 +189,11 @@ module API
success Entities::Issue success Entities::Issue
end end
params do params do
requires :issue_id, type: Integer, desc: 'The ID of a project issue' requires :issue_iid, type: Integer, desc: 'The internal ID of a project issue'
requires :to_project_id, type: Integer, desc: 'The ID of the new project' requires :to_project_id, type: Integer, desc: 'The ID of the new project'
end end
post ':id/issues/:issue_id/move' do post ':id/issues/:issue_iid/move' do
issue = user_project.issues.find_by(id: params[:issue_id]) issue = user_project.issues.find_by(iid: params[:issue_iid])
not_found!('Issue') unless issue not_found!('Issue') unless issue
new_project = Project.find_by(id: params[:to_project_id]) new_project = Project.find_by(id: params[:to_project_id])
...@@ -209,10 +209,10 @@ module API ...@@ -209,10 +209,10 @@ module API
desc 'Delete a project issue' desc 'Delete a project issue'
params do params do
requires :issue_id, type: Integer, desc: 'The ID of a project issue' requires :issue_iid, type: Integer, desc: 'The internal ID of a project issue'
end end
delete ":id/issues/:issue_id" do delete ":id/issues/:issue_iid" do
issue = user_project.issues.find_by(id: params[:issue_id]) issue = user_project.issues.find_by(iid: params[:issue_iid])
not_found!('Issue') unless issue not_found!('Issue') unless issue
authorize!(:destroy_issue, issue) authorize!(:destroy_issue, issue)
......
...@@ -13,11 +13,11 @@ module API ...@@ -13,11 +13,11 @@ module API
params do params do
requires :id, type: String, desc: 'The ID of a project' requires :id, type: String, desc: 'The ID of a project'
requires :merge_request_id, type: Integer, desc: 'The ID of a merge request' requires :merge_request_iid, type: Integer, desc: 'The IID of a merge request'
use :pagination use :pagination
end end
get ":id/merge_requests/:merge_request_id/versions" do get ":id/merge_requests/:merge_request_iid/versions" do
merge_request = find_merge_request_with_access(params[:merge_request_id]) merge_request = find_merge_request_with_access(params[:merge_request_iid])
present paginate(merge_request.merge_request_diffs), with: Entities::MergeRequestDiff present paginate(merge_request.merge_request_diffs), with: Entities::MergeRequestDiff
end end
...@@ -29,12 +29,12 @@ module API ...@@ -29,12 +29,12 @@ module API
params do params do
requires :id, type: String, desc: 'The ID of a project' requires :id, type: String, desc: 'The ID of a project'
requires :merge_request_id, type: Integer, desc: 'The ID of a merge request' requires :merge_request_iid, type: Integer, desc: 'The IID of a merge request'
requires :version_id, type: Integer, desc: 'The ID of a merge request diff version' requires :version_id, type: Integer, desc: 'The ID of a merge request diff version'
end end
get ":id/merge_requests/:merge_request_id/versions/:version_id" do get ":id/merge_requests/:merge_request_iid/versions/:version_id" do
merge_request = find_merge_request_with_access(params[:merge_request_id]) merge_request = find_merge_request_with_access(params[:merge_request_iid])
present merge_request.merge_request_diffs.find(params[:version_id]), with: Entities::MergeRequestDiffFull present merge_request.merge_request_diffs.find(params[:version_id]), with: Entities::MergeRequestDiffFull
end end
......
...@@ -101,23 +101,23 @@ module API ...@@ -101,23 +101,23 @@ module API
desc 'Delete a merge request' desc 'Delete a merge request'
params do params do
requires :merge_request_id, type: Integer, desc: 'The ID of a merge request' requires :merge_request_iid, type: Integer, desc: 'The IID of a merge request'
end end
delete ":id/merge_requests/:merge_request_id" do delete ":id/merge_requests/:merge_request_iid" do
merge_request = find_project_merge_request(params[:merge_request_id]) merge_request = find_project_merge_request(params[:merge_request_iid])
authorize!(:destroy_merge_request, merge_request) authorize!(:destroy_merge_request, merge_request)
merge_request.destroy merge_request.destroy
end end
params do params do
requires :merge_request_id, type: Integer, desc: 'The ID of a merge request' requires :merge_request_iid, type: Integer, desc: 'The IID of a merge request'
end end
desc 'Get a single merge request' do desc 'Get a single merge request' do
success Entities::MergeRequest success Entities::MergeRequest
end end
get ':id/merge_requests/:merge_request_id' do get ':id/merge_requests/:merge_request_iid' do
merge_request = find_merge_request_with_access(params[:merge_request_id]) merge_request = find_merge_request_with_access(params[:merge_request_iid])
present merge_request, with: Entities::MergeRequest, current_user: current_user, project: user_project present merge_request, with: Entities::MergeRequest, current_user: current_user, project: user_project
end end
...@@ -125,8 +125,8 @@ module API ...@@ -125,8 +125,8 @@ module API
desc 'Get the commits of a merge request' do desc 'Get the commits of a merge request' do
success Entities::RepoCommit success Entities::RepoCommit
end end
get ':id/merge_requests/:merge_request_id/commits' do get ':id/merge_requests/:merge_request_iid/commits' do
merge_request = find_merge_request_with_access(params[:merge_request_id]) merge_request = find_merge_request_with_access(params[:merge_request_iid])
commits = ::Kaminari.paginate_array(merge_request.commits) commits = ::Kaminari.paginate_array(merge_request.commits)
present paginate(commits), with: Entities::RepoCommit present paginate(commits), with: Entities::RepoCommit
...@@ -135,8 +135,8 @@ module API ...@@ -135,8 +135,8 @@ module API
desc 'Show the merge request changes' do desc 'Show the merge request changes' do
success Entities::MergeRequestChanges success Entities::MergeRequestChanges
end end
get ':id/merge_requests/:merge_request_id/changes' do get ':id/merge_requests/:merge_request_iid/changes' do
merge_request = find_merge_request_with_access(params[:merge_request_id]) merge_request = find_merge_request_with_access(params[:merge_request_iid])
present merge_request, with: Entities::MergeRequestChanges, current_user: current_user present merge_request, with: Entities::MergeRequestChanges, current_user: current_user
end end
...@@ -154,8 +154,8 @@ module API ...@@ -154,8 +154,8 @@ module API
:milestone_id, :labels, :state_event, :milestone_id, :labels, :state_event,
:remove_source_branch :remove_source_branch
end end
put ':id/merge_requests/:merge_request_id' do put ':id/merge_requests/:merge_request_iid' do
merge_request = find_merge_request_with_access(params.delete(:merge_request_id), :update_merge_request) merge_request = find_merge_request_with_access(params.delete(:merge_request_iid), :update_merge_request)
mr_params = declared_params(include_missing: false) mr_params = declared_params(include_missing: false)
mr_params[:force_remove_source_branch] = mr_params.delete(:remove_source_branch) if mr_params[:remove_source_branch].present? mr_params[:force_remove_source_branch] = mr_params.delete(:remove_source_branch) if mr_params[:remove_source_branch].present?
...@@ -180,8 +180,8 @@ module API ...@@ -180,8 +180,8 @@ module API
desc: 'When true, this merge request will be merged when the pipeline succeeds' desc: 'When true, this merge request will be merged when the pipeline succeeds'
optional :sha, type: String, desc: 'When present, must have the HEAD SHA of the source branch' optional :sha, type: String, desc: 'When present, must have the HEAD SHA of the source branch'
end end
put ':id/merge_requests/:merge_request_id/merge' do put ':id/merge_requests/:merge_request_iid/merge' do
merge_request = find_project_merge_request(params[:merge_request_id]) merge_request = find_project_merge_request(params[:merge_request_iid])
# Merge request can not be merged # Merge request can not be merged
# because user dont have permissions to push into target branch # because user dont have permissions to push into target branch
...@@ -216,8 +216,8 @@ module API ...@@ -216,8 +216,8 @@ module API
desc 'Cancel merge if "Merge When Pipeline Succeeds" is enabled' do desc 'Cancel merge if "Merge When Pipeline Succeeds" is enabled' do
success Entities::MergeRequest success Entities::MergeRequest
end end
post ':id/merge_requests/:merge_request_id/cancel_merge_when_pipeline_succeeds' do post ':id/merge_requests/:merge_request_iid/cancel_merge_when_pipeline_succeeds' do
merge_request = find_project_merge_request(params[:merge_request_id]) merge_request = find_project_merge_request(params[:merge_request_iid])
unauthorized! unless merge_request.can_cancel_merge_when_pipeline_succeeds?(current_user) unauthorized! unless merge_request.can_cancel_merge_when_pipeline_succeeds?(current_user)
...@@ -232,8 +232,8 @@ module API ...@@ -232,8 +232,8 @@ module API
params do params do
use :pagination use :pagination
end end
get ':id/merge_requests/:merge_request_id/comments' do get ':id/merge_requests/:merge_request_iid/comments' do
merge_request = find_merge_request_with_access(params[:merge_request_id]) merge_request = find_merge_request_with_access(params[:merge_request_iid])
present paginate(merge_request.notes.fresh), with: Entities::MRNote present paginate(merge_request.notes.fresh), with: Entities::MRNote
end end
...@@ -243,8 +243,8 @@ module API ...@@ -243,8 +243,8 @@ module API
params do params do
requires :note, type: String, desc: 'The text of the comment' requires :note, type: String, desc: 'The text of the comment'
end end
post ':id/merge_requests/:merge_request_id/comments' do post ':id/merge_requests/:merge_request_iid/comments' do
merge_request = find_merge_request_with_access(params[:merge_request_id], :create_note) merge_request = find_merge_request_with_access(params[:merge_request_iid], :create_note)
opts = { opts = {
note: params[:note], note: params[:note],
...@@ -267,8 +267,8 @@ module API ...@@ -267,8 +267,8 @@ module API
params do params do
use :pagination use :pagination
end end
get ':id/merge_requests/:merge_request_id/closes_issues' do get ':id/merge_requests/:merge_request_iid/closes_issues' do
merge_request = find_merge_request_with_access(params[:merge_request_id]) merge_request = find_merge_request_with_access(params[:merge_request_iid])
issues = ::Kaminari.paginate_array(merge_request.closes_issues(current_user)) issues = ::Kaminari.paginate_array(merge_request.closes_issues(current_user))
present paginate(issues), with: issue_entity(user_project), current_user: current_user present paginate(issues), with: issue_entity(user_project), current_user: current_user
end end
......
...@@ -5,11 +5,11 @@ module API ...@@ -5,11 +5,11 @@ module API
included do included do
helpers do helpers do
def issuable_name def issuable_name
declared_params.has_key?(:issue_id) ? 'issue' : 'merge_request' declared_params.has_key?(:issue_iid) ? 'issue' : 'merge_request'
end end
def issuable_key def issuable_key
"#{issuable_name}_id".to_sym "#{issuable_name}_iid".to_sym
end end
def update_issuable_key def update_issuable_key
...@@ -50,7 +50,7 @@ module API ...@@ -50,7 +50,7 @@ module API
issuable_name = name.end_with?('Issues') ? 'issue' : 'merge_request' issuable_name = name.end_with?('Issues') ? 'issue' : 'merge_request'
issuable_collection_name = issuable_name.pluralize issuable_collection_name = issuable_name.pluralize
issuable_key = "#{issuable_name}_id".to_sym issuable_key = "#{issuable_name}_iid".to_sym
desc "Set a time estimate for a project #{issuable_name}" desc "Set a time estimate for a project #{issuable_name}"
params do params do
......
...@@ -5,8 +5,8 @@ module API ...@@ -5,8 +5,8 @@ module API
before { authenticate! } before { authenticate! }
ISSUABLE_TYPES = { ISSUABLE_TYPES = {
'merge_requests' => ->(id) { find_merge_request_with_access(id) }, 'merge_requests' => ->(iid) { find_merge_request_with_access(iid) },
'issues' => ->(id) { find_project_issue(id) } 'issues' => ->(iid) { find_project_issue(iid) }
}.freeze }.freeze
params do params do
...@@ -14,13 +14,13 @@ module API ...@@ -14,13 +14,13 @@ module API
end end
resource :projects do resource :projects do
ISSUABLE_TYPES.each do |type, finder| ISSUABLE_TYPES.each do |type, finder|
type_id_str = "#{type.singularize}_id".to_sym type_id_str = "#{type.singularize}_iid".to_sym
desc 'Create a todo on an issuable' do desc 'Create a todo on an issuable' do
success Entities::Todo success Entities::Todo
end end
params do params do
requires type_id_str, type: Integer, desc: 'The ID of an issuable' requires type_id_str, type: Integer, desc: 'The IID of an issuable'
end end
post ":id/#{type}/:#{type_id_str}/todo" do post ":id/#{type}/:#{type_id_str}/todo" do
issuable = instance_exec(params[type_id_str], &finder) issuable = instance_exec(params[type_id_str], &finder)
......
...@@ -16,11 +16,64 @@ module API ...@@ -16,11 +16,64 @@ module API
requires :"#{awardable_id_string}", type: Integer, desc: "The ID of an Issue, Merge Request or Snippet" requires :"#{awardable_id_string}", type: Integer, desc: "The ID of an Issue, Merge Request or Snippet"
end end
[":id/#{awardable_string}/:#{awardable_id_string}/award_emoji", [
":id/#{awardable_string}/:#{awardable_id_string}/notes/:note_id/award_emoji"].each do |endpoint| ":id/#{awardable_string}/:#{awardable_id_string}/award_emoji",
":id/#{awardable_string}/:#{awardable_id_string}/notes/:note_id/award_emoji"
].each do |endpoint|
desc 'Get a list of project +awardable+ award emoji' do
detail 'This feature was introduced in 8.9'
success Entities::AwardEmoji
end
params do
use :pagination
end
get endpoint do
if can_read_awardable?
awards = awardable.award_emoji
present paginate(awards), with: Entities::AwardEmoji
else
not_found!("Award Emoji")
end
end
desc 'Get a specific award emoji' do
detail 'This feature was introduced in 8.9'
success Entities::AwardEmoji
end
params do
requires :award_id, type: Integer, desc: 'The ID of the award'
end
get "#{endpoint}/:award_id" do
if can_read_awardable?
present awardable.award_emoji.find(params[:award_id]), with: Entities::AwardEmoji
else
not_found!("Award Emoji")
end
end
desc 'Award a new Emoji' do
detail 'This feature was introduced in 8.9'
success Entities::AwardEmoji
end
params do
requires :name, type: String, desc: 'The name of a award_emoji (without colons)'
end
post endpoint do
not_found!('Award Emoji') unless can_read_awardable? && can_award_awardable?
award = awardable.create_award_emoji(params[:name], current_user)
if award.persisted?
present award, with: Entities::AwardEmoji
else
not_found!("Award Emoji #{award.errors.messages}")
end
end
desc 'Delete a +awardables+ award emoji' do desc 'Delete a +awardables+ award emoji' do
detail 'This feature was introduced in 8.9' detail 'This feature was introduced in 8.9'
success ::API::Entities::AwardEmoji success Entities::AwardEmoji
end end
params do params do
requires :award_id, type: Integer, desc: 'The ID of an award emoji' requires :award_id, type: Integer, desc: 'The ID of an award emoji'
...@@ -30,13 +83,22 @@ module API ...@@ -30,13 +83,22 @@ module API
unauthorized! unless award.user == current_user || current_user.admin? unauthorized! unless award.user == current_user || current_user.admin?
present award.destroy, with: ::API::Entities::AwardEmoji award.destroy
present award, with: Entities::AwardEmoji
end end
end end
end end
end end
helpers do helpers do
def can_read_awardable?
can?(current_user, read_ability(awardable), awardable)
end
def can_award_awardable?
awardable.user_can_award?(current_user, params[:name])
end
def awardable def awardable
@awardable ||= @awardable ||=
begin begin
...@@ -53,6 +115,15 @@ module API ...@@ -53,6 +115,15 @@ module API
end end
end end
end end
def read_ability(awardable)
case awardable
when Note
read_ability(awardable.noteable)
else
:"read_#{awardable.class.to_s.underscore}"
end
end
end end
end end
end end
......
module API
module V3
module Helpers
def find_project_issue(id)
IssuesFinder.new(current_user, project_id: user_project.id).find(id)
end
def find_project_merge_request(id)
MergeRequestsFinder.new(current_user, project_id: user_project.id).find(id)
end
def find_merge_request_with_access(id, access_level = :read_merge_request)
merge_request = user_project.merge_requests.find(id)
authorize! access_level, merge_request
merge_request
end
end
end
end
module API
module V3
module TimeTrackingEndpoints
extend ActiveSupport::Concern
included do
helpers do
def issuable_name
declared_params.has_key?(:issue_id) ? 'issue' : 'merge_request'
end
def issuable_key
"#{issuable_name}_id".to_sym
end
def update_issuable_key
"update_#{issuable_name}".to_sym
end
def read_issuable_key
"read_#{issuable_name}".to_sym
end
def load_issuable
@issuable ||= begin
case issuable_name
when 'issue'
find_project_issue(params.delete(issuable_key))
when 'merge_request'
find_project_merge_request(params.delete(issuable_key))
end
end
end
def update_issuable(attrs)
custom_params = declared_params(include_missing: false)
custom_params.merge!(attrs)
issuable = update_service.new(user_project, current_user, custom_params).execute(load_issuable)
if issuable.valid?
present issuable, with: ::API::Entities::IssuableTimeStats
else
render_validation_error!(issuable)
end
end
def update_service
issuable_name == 'issue' ? ::Issues::UpdateService : ::MergeRequests::UpdateService
end
end
issuable_name = name.end_with?('Issues') ? 'issue' : 'merge_request'
issuable_collection_name = issuable_name.pluralize
issuable_key = "#{issuable_name}_id".to_sym
desc "Set a time estimate for a project #{issuable_name}"
params do
requires issuable_key, type: Integer, desc: "The ID of a project #{issuable_name}"
requires :duration, type: String, desc: 'The duration to be parsed'
end
post ":id/#{issuable_collection_name}/:#{issuable_key}/time_estimate" do
authorize! update_issuable_key, load_issuable
status :ok
update_issuable(time_estimate: Gitlab::TimeTrackingFormatter.parse(params.delete(:duration)))
end
desc "Reset the time estimate for a project #{issuable_name}"
params do
requires issuable_key, type: Integer, desc: "The ID of a project #{issuable_name}"
end
post ":id/#{issuable_collection_name}/:#{issuable_key}/reset_time_estimate" do
authorize! update_issuable_key, load_issuable
status :ok
update_issuable(time_estimate: 0)
end
desc "Add spent time for a project #{issuable_name}"
params do
requires issuable_key, type: Integer, desc: "The ID of a project #{issuable_name}"
requires :duration, type: String, desc: 'The duration to be parsed'
end
post ":id/#{issuable_collection_name}/:#{issuable_key}/add_spent_time" do
authorize! update_issuable_key, load_issuable
update_issuable(spend_time: {
duration: Gitlab::TimeTrackingFormatter.parse(params.delete(:duration)),
user: current_user
})
end
desc "Reset spent time for a project #{issuable_name}"
params do
requires issuable_key, type: Integer, desc: "The ID of a project #{issuable_name}"
end
post ":id/#{issuable_collection_name}/:#{issuable_key}/reset_spent_time" do
authorize! update_issuable_key, load_issuable
status :ok
update_issuable(spend_time: { duration: :reset, user: current_user })
end
desc "Show time stats for a project #{issuable_name}"
params do
requires issuable_key, type: Integer, desc: "The ID of a project #{issuable_name}"
end
get ":id/#{issuable_collection_name}/:#{issuable_key}/time_stats" do
authorize! read_issuable_key, load_issuable
present load_issuable, with: ::API::Entities::IssuableTimeStats
end
end
end
end
end
This diff is collapsed.
This diff is collapsed.
...@@ -13,9 +13,9 @@ describe API::MergeRequestDiffs, 'MergeRequestDiffs', api: true do ...@@ -13,9 +13,9 @@ describe API::MergeRequestDiffs, 'MergeRequestDiffs', api: true do
project.team << [user, :master] project.team << [user, :master]
end end
describe 'GET /projects/:id/merge_requests/:merge_request_id/versions' do describe 'GET /projects/:id/merge_requests/:merge_request_iid/versions' do
it 'returns 200 for a valid merge request' do it 'returns 200 for a valid merge request' do
get api("/projects/#{project.id}/merge_requests/#{merge_request.id}/versions", user) get api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/versions", user)
merge_request_diff = merge_request.merge_request_diffs.first merge_request_diff = merge_request.merge_request_diffs.first
expect(response.status).to eq 200 expect(response.status).to eq 200
...@@ -26,16 +26,22 @@ describe API::MergeRequestDiffs, 'MergeRequestDiffs', api: true do ...@@ -26,16 +26,22 @@ describe API::MergeRequestDiffs, 'MergeRequestDiffs', api: true do
expect(json_response.first['head_commit_sha']).to eq(merge_request_diff.head_commit_sha) expect(json_response.first['head_commit_sha']).to eq(merge_request_diff.head_commit_sha)
end end
it 'returns a 404 when merge_request_id not found' do it 'returns a 404 when merge_request id is used instead of the iid' do
get api("/projects/#{project.id}/merge_requests/#{merge_request.id}/versions", user)
expect(response).to have_http_status(404)
end
it 'returns a 404 when merge_request_iid not found' do
get api("/projects/#{project.id}/merge_requests/999/versions", user) get api("/projects/#{project.id}/merge_requests/999/versions", user)
expect(response).to have_http_status(404) expect(response).to have_http_status(404)
end end
end end
describe 'GET /projects/:id/merge_requests/:merge_request_id/versions/:version_id' do describe 'GET /projects/:id/merge_requests/:merge_request_iid/versions/:version_id' do
let(:merge_request_diff) { merge_request.merge_request_diffs.first }
it 'returns a 200 for a valid merge request' do it 'returns a 200 for a valid merge request' do
merge_request_diff = merge_request.merge_request_diffs.first get api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/versions/#{merge_request_diff.id}", user)
get api("/projects/#{project.id}/merge_requests/#{merge_request.id}/versions/#{merge_request_diff.id}", user)
expect(response.status).to eq 200 expect(response.status).to eq 200
expect(json_response['id']).to eq(merge_request_diff.id) expect(json_response['id']).to eq(merge_request_diff.id)
...@@ -43,8 +49,18 @@ describe API::MergeRequestDiffs, 'MergeRequestDiffs', api: true do ...@@ -43,8 +49,18 @@ describe API::MergeRequestDiffs, 'MergeRequestDiffs', api: true do
expect(json_response['diffs'].size).to eq(merge_request_diff.diffs.size) expect(json_response['diffs'].size).to eq(merge_request_diff.diffs.size)
end end
it 'returns a 404 when merge_request_id not found' do it 'returns a 404 when merge_request id is used instead of the iid' do
get api("/projects/#{project.id}/merge_requests/#{merge_request.id}/versions/999", user) get api("/projects/#{project.id}/merge_requests/#{merge_request.id}/versions/#{merge_request_diff.id}", user)
expect(response).to have_http_status(404)
end
it 'returns a 404 when merge_request version_id is not found' do
get api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/versions/999", user)
expect(response).to have_http_status(404)
end
it 'returns a 404 when merge_request_iid is not found' do
get api("/projects/#{project.id}/merge_requests/12345/versions/#{merge_request_diff.id}", user)
expect(response).to have_http_status(404) expect(response).to have_http_status(404)
end end
end end
......
This diff is collapsed.
...@@ -163,7 +163,7 @@ describe API::Todos, api: true do ...@@ -163,7 +163,7 @@ describe API::Todos, api: true do
shared_examples 'an issuable' do |issuable_type| shared_examples 'an issuable' do |issuable_type|
it 'creates a todo on an issuable' do it 'creates a todo on an issuable' do
post api("/projects/#{project_1.id}/#{issuable_type}/#{issuable.id}/todo", john_doe) post api("/projects/#{project_1.id}/#{issuable_type}/#{issuable.iid}/todo", john_doe)
expect(response.status).to eq(201) expect(response.status).to eq(201)
expect(json_response['project']).to be_a Hash expect(json_response['project']).to be_a Hash
...@@ -180,7 +180,7 @@ describe API::Todos, api: true do ...@@ -180,7 +180,7 @@ describe API::Todos, api: true do
it 'returns 304 there already exist a todo on that issuable' do it 'returns 304 there already exist a todo on that issuable' do
create(:todo, project: project_1, author: author_1, user: john_doe, target: issuable) create(:todo, project: project_1, author: author_1, user: john_doe, target: issuable)
post api("/projects/#{project_1.id}/#{issuable_type}/#{issuable.id}/todo", john_doe) post api("/projects/#{project_1.id}/#{issuable_type}/#{issuable.iid}/todo", john_doe)
expect(response.status).to eq(304) expect(response.status).to eq(304)
end end
...@@ -195,7 +195,7 @@ describe API::Todos, api: true do ...@@ -195,7 +195,7 @@ describe API::Todos, api: true do
guest = create(:user) guest = create(:user)
project_1.team << [guest, :guest] project_1.team << [guest, :guest]
post api("/projects/#{project_1.id}/#{issuable_type}/#{issuable.id}/todo", guest) post api("/projects/#{project_1.id}/#{issuable_type}/#{issuable.iid}/todo", guest)
if issuable_type == 'merge_requests' if issuable_type == 'merge_requests'
expect(response).to have_http_status(403) expect(response).to have_http_status(403)
......
...@@ -13,6 +13,231 @@ describe API::V3::AwardEmoji, api: true do ...@@ -13,6 +13,231 @@ describe API::V3::AwardEmoji, api: true do
before { project.team << [user, :master] } before { project.team << [user, :master] }
describe "GET /projects/:id/awardable/:awardable_id/award_emoji" do
context 'on an issue' do
it "returns an array of award_emoji" do
get v3_api("/projects/#{project.id}/issues/#{issue.id}/award_emoji", user)
expect(response).to have_http_status(200)
expect(json_response).to be_an Array
expect(json_response.first['name']).to eq(award_emoji.name)
end
it "returns a 404 error when issue id not found" do
get v3_api("/projects/#{project.id}/issues/12345/award_emoji", user)
expect(response).to have_http_status(404)
end
end
context 'on a merge request' do
it "returns an array of award_emoji" do
get v3_api("/projects/#{project.id}/merge_requests/#{merge_request.id}/award_emoji", user)
expect(response).to have_http_status(200)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
expect(json_response.first['name']).to eq(downvote.name)
end
end
context 'on a snippet' do
let(:snippet) { create(:project_snippet, :public, project: project) }
let!(:award) { create(:award_emoji, awardable: snippet) }
it 'returns the awarded emoji' do
get v3_api("/projects/#{project.id}/snippets/#{snippet.id}/award_emoji", user)
expect(response).to have_http_status(200)
expect(json_response).to be_an Array
expect(json_response.first['name']).to eq(award.name)
end
end
context 'when the user has no access' do
it 'returns a status code 404' do
user1 = create(:user)
get v3_api("/projects/#{project.id}/merge_requests/#{merge_request.id}/award_emoji", user1)
expect(response).to have_http_status(404)
end
end
end
describe 'GET /projects/:id/awardable/:awardable_id/notes/:note_id/award_emoji' do
let!(:rocket) { create(:award_emoji, awardable: note, name: 'rocket') }
it 'returns an array of award emoji' do
get v3_api("/projects/#{project.id}/issues/#{issue.id}/notes/#{note.id}/award_emoji", user)
expect(response).to have_http_status(200)
expect(json_response).to be_an Array
expect(json_response.first['name']).to eq(rocket.name)
end
end
describe "GET /projects/:id/awardable/:awardable_id/award_emoji/:award_id" do
context 'on an issue' do
it "returns the award emoji" do
get v3_api("/projects/#{project.id}/issues/#{issue.id}/award_emoji/#{award_emoji.id}", user)
expect(response).to have_http_status(200)
expect(json_response['name']).to eq(award_emoji.name)
expect(json_response['awardable_id']).to eq(issue.id)
expect(json_response['awardable_type']).to eq("Issue")
end
it "returns a 404 error if the award is not found" do
get v3_api("/projects/#{project.id}/issues/#{issue.id}/award_emoji/12345", user)
expect(response).to have_http_status(404)
end
end
context 'on a merge request' do
it 'returns the award emoji' do
get v3_api("/projects/#{project.id}/merge_requests/#{merge_request.id}/award_emoji/#{downvote.id}", user)
expect(response).to have_http_status(200)
expect(json_response['name']).to eq(downvote.name)
expect(json_response['awardable_id']).to eq(merge_request.id)
expect(json_response['awardable_type']).to eq("MergeRequest")
end
end
context 'on a snippet' do
let(:snippet) { create(:project_snippet, :public, project: project) }
let!(:award) { create(:award_emoji, awardable: snippet) }
it 'returns the awarded emoji' do
get v3_api("/projects/#{project.id}/snippets/#{snippet.id}/award_emoji/#{award.id}", user)
expect(response).to have_http_status(200)
expect(json_response['name']).to eq(award.name)
expect(json_response['awardable_id']).to eq(snippet.id)
expect(json_response['awardable_type']).to eq("Snippet")
end
end
context 'when the user has no access' do
it 'returns a status code 404' do
user1 = create(:user)
get v3_api("/projects/#{project.id}/merge_requests/#{merge_request.id}/award_emoji/#{downvote.id}", user1)
expect(response).to have_http_status(404)
end
end
end
describe 'GET /projects/:id/awardable/:awardable_id/notes/:note_id/award_emoji/:award_id' do
let!(:rocket) { create(:award_emoji, awardable: note, name: 'rocket') }
it 'returns an award emoji' do
get v3_api("/projects/#{project.id}/issues/#{issue.id}/notes/#{note.id}/award_emoji/#{rocket.id}", user)
expect(response).to have_http_status(200)
expect(json_response).not_to be_an Array
expect(json_response['name']).to eq(rocket.name)
end
end
describe "POST /projects/:id/awardable/:awardable_id/award_emoji" do
let(:issue2) { create(:issue, project: project, author: user) }
context "on an issue" do
it "creates a new award emoji" do
post v3_api("/projects/#{project.id}/issues/#{issue.id}/award_emoji", user), name: 'blowfish'
expect(response).to have_http_status(201)
expect(json_response['name']).to eq('blowfish')
expect(json_response['user']['username']).to eq(user.username)
end
it "returns a 400 bad request error if the name is not given" do
post v3_api("/projects/#{project.id}/issues/#{issue.id}/award_emoji", user)
expect(response).to have_http_status(400)
end
it "returns a 401 unauthorized error if the user is not authenticated" do
post v3_api("/projects/#{project.id}/issues/#{issue.id}/award_emoji"), name: 'thumbsup'
expect(response).to have_http_status(401)
end
it "returns a 404 error if the user authored issue" do
post v3_api("/projects/#{project.id}/issues/#{issue2.id}/award_emoji", user), name: 'thumbsup'
expect(response).to have_http_status(404)
end
it "normalizes +1 as thumbsup award" do
post v3_api("/projects/#{project.id}/issues/#{issue.id}/award_emoji", user), name: '+1'
expect(issue.award_emoji.last.name).to eq("thumbsup")
end
context 'when the emoji already has been awarded' do
it 'returns a 404 status code' do
post v3_api("/projects/#{project.id}/issues/#{issue.id}/award_emoji", user), name: 'thumbsup'
post v3_api("/projects/#{project.id}/issues/#{issue.id}/award_emoji", user), name: 'thumbsup'
expect(response).to have_http_status(404)
expect(json_response["message"]).to match("has already been taken")
end
end
end
context 'on a snippet' do
it 'creates a new award emoji' do
snippet = create(:project_snippet, :public, project: project)
post v3_api("/projects/#{project.id}/snippets/#{snippet.id}/award_emoji", user), name: 'blowfish'
expect(response).to have_http_status(201)
expect(json_response['name']).to eq('blowfish')
expect(json_response['user']['username']).to eq(user.username)
end
end
end
describe "POST /projects/:id/awardable/:awardable_id/notes/:note_id/award_emoji" do
let(:note2) { create(:note, project: project, noteable: issue, author: user) }
it 'creates a new award emoji' do
expect do
post v3_api("/projects/#{project.id}/issues/#{issue.id}/notes/#{note.id}/award_emoji", user), name: 'rocket'
end.to change { note.award_emoji.count }.from(0).to(1)
expect(response).to have_http_status(201)
expect(json_response['user']['username']).to eq(user.username)
end
it "it returns 404 error when user authored note" do
post v3_api("/projects/#{project.id}/issues/#{issue.id}/notes/#{note2.id}/award_emoji", user), name: 'thumbsup'
expect(response).to have_http_status(404)
end
it "normalizes +1 as thumbsup award" do
post v3_api("/projects/#{project.id}/issues/#{issue.id}/notes/#{note.id}/award_emoji", user), name: '+1'
expect(note.award_emoji.last.name).to eq("thumbsup")
end
context 'when the emoji already has been awarded' do
it 'returns a 404 status code' do
post v3_api("/projects/#{project.id}/issues/#{issue.id}/notes/#{note.id}/award_emoji", user), name: 'rocket'
post v3_api("/projects/#{project.id}/issues/#{issue.id}/notes/#{note.id}/award_emoji", user), name: 'rocket'
expect(response).to have_http_status(404)
expect(json_response["message"]).to match("has already been taken")
end
end
end
describe 'DELETE /projects/:id/awardable/:awardable_id/award_emoji/:award_id' do describe 'DELETE /projects/:id/awardable/:awardable_id/award_emoji/:award_id' do
context 'when the awardable is an Issue' do context 'when the awardable is an Issue' do
it 'deletes the award' do it 'deletes the award' do
......
...@@ -1288,6 +1288,6 @@ describe API::V3::Issues, api: true do ...@@ -1288,6 +1288,6 @@ describe API::V3::Issues, api: true do
describe 'time tracking endpoints' do describe 'time tracking endpoints' do
let(:issuable) { issue } let(:issuable) { issue }
include_examples 'time tracking endpoints', 'issue' include_examples 'V3 time tracking endpoints', 'issue'
end end
end end
require "spec_helper" require "spec_helper"
describe API::MergeRequestDiffs, 'MergeRequestDiffs', api: true do describe API::V3::MergeRequestDiffs, 'MergeRequestDiffs', api: true do
include ApiHelpers include ApiHelpers
let!(:user) { create(:user) } let!(:user) { create(:user) }
...@@ -15,7 +15,7 @@ describe API::MergeRequestDiffs, 'MergeRequestDiffs', api: true do ...@@ -15,7 +15,7 @@ describe API::MergeRequestDiffs, 'MergeRequestDiffs', api: true do
describe 'GET /projects/:id/merge_requests/:merge_request_id/versions' do describe 'GET /projects/:id/merge_requests/:merge_request_id/versions' do
it 'returns 200 for a valid merge request' do it 'returns 200 for a valid merge request' do
get api("/projects/#{project.id}/merge_requests/#{merge_request.id}/versions", user) get v3_api("/projects/#{project.id}/merge_requests/#{merge_request.id}/versions", user)
merge_request_diff = merge_request.merge_request_diffs.first merge_request_diff = merge_request.merge_request_diffs.first
expect(response.status).to eq 200 expect(response.status).to eq 200
...@@ -25,7 +25,7 @@ describe API::MergeRequestDiffs, 'MergeRequestDiffs', api: true do ...@@ -25,7 +25,7 @@ describe API::MergeRequestDiffs, 'MergeRequestDiffs', api: true do
end end
it 'returns a 404 when merge_request_id not found' do it 'returns a 404 when merge_request_id not found' do
get api("/projects/#{project.id}/merge_requests/999/versions", user) get v3_api("/projects/#{project.id}/merge_requests/999/versions", user)
expect(response).to have_http_status(404) expect(response).to have_http_status(404)
end end
end end
...@@ -33,7 +33,7 @@ describe API::MergeRequestDiffs, 'MergeRequestDiffs', api: true do ...@@ -33,7 +33,7 @@ describe API::MergeRequestDiffs, 'MergeRequestDiffs', api: true do
describe 'GET /projects/:id/merge_requests/:merge_request_id/versions/:version_id' do describe 'GET /projects/:id/merge_requests/:merge_request_id/versions/:version_id' do
it 'returns a 200 for a valid merge request' do it 'returns a 200 for a valid merge request' do
merge_request_diff = merge_request.merge_request_diffs.first merge_request_diff = merge_request.merge_request_diffs.first
get api("/projects/#{project.id}/merge_requests/#{merge_request.id}/versions/#{merge_request_diff.id}", user) get v3_api("/projects/#{project.id}/merge_requests/#{merge_request.id}/versions/#{merge_request_diff.id}", user)
expect(response.status).to eq 200 expect(response.status).to eq 200
expect(json_response['id']).to eq(merge_request_diff.id) expect(json_response['id']).to eq(merge_request_diff.id)
...@@ -42,7 +42,8 @@ describe API::MergeRequestDiffs, 'MergeRequestDiffs', api: true do ...@@ -42,7 +42,8 @@ describe API::MergeRequestDiffs, 'MergeRequestDiffs', api: true do
end end
it 'returns a 404 when merge_request_id not found' do it 'returns a 404 when merge_request_id not found' do
get api("/projects/#{project.id}/merge_requests/#{merge_request.id}/versions/999", user) get v3_api("/projects/#{project.id}/merge_requests/#{merge_request.id}/versions/999", user)
expect(response).to have_http_status(404) expect(response).to have_http_status(404)
end end
end end
......
...@@ -712,7 +712,7 @@ describe API::MergeRequests, api: true do ...@@ -712,7 +712,7 @@ describe API::MergeRequests, api: true do
describe 'Time tracking' do describe 'Time tracking' do
let(:issuable) { merge_request } let(:issuable) { merge_request }
include_examples 'time tracking endpoints', 'merge_request' include_examples 'V3 time tracking endpoints', 'merge_request'
end end
def mr_with_later_created_and_updated_at_time def mr_with_later_created_and_updated_at_time
......
...@@ -7,13 +7,13 @@ shared_examples 'time tracking endpoints' do |issuable_name| ...@@ -7,13 +7,13 @@ shared_examples 'time tracking endpoints' do |issuable_name|
describe "POST /projects/:id/#{issuable_collection_name}/:#{issuable_name}_id/time_estimate" do describe "POST /projects/:id/#{issuable_collection_name}/:#{issuable_name}_id/time_estimate" do
context 'with an unauthorized user' do context 'with an unauthorized user' do
subject { post(api("/projects/#{project.id}/#{issuable_collection_name}/#{issuable.id}/time_estimate", non_member), duration: '1w') } subject { post(api("/projects/#{project.id}/#{issuable_collection_name}/#{issuable.iid}/time_estimate", non_member), duration: '1w') }
it_behaves_like 'an unauthorized API user' it_behaves_like 'an unauthorized API user'
end end
it "sets the time estimate for #{issuable_name}" do it "sets the time estimate for #{issuable_name}" do
post api("/projects/#{project.id}/#{issuable_collection_name}/#{issuable.id}/time_estimate", user), duration: '1w' post api("/projects/#{project.id}/#{issuable_collection_name}/#{issuable.iid}/time_estimate", user), duration: '1w'
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
expect(json_response['human_time_estimate']).to eq('1w') expect(json_response['human_time_estimate']).to eq('1w')
...@@ -21,12 +21,12 @@ shared_examples 'time tracking endpoints' do |issuable_name| ...@@ -21,12 +21,12 @@ shared_examples 'time tracking endpoints' do |issuable_name|
describe 'updating the current estimate' do describe 'updating the current estimate' do
before do before do
post api("/projects/#{project.id}/#{issuable_collection_name}/#{issuable.id}/time_estimate", user), duration: '1w' post api("/projects/#{project.id}/#{issuable_collection_name}/#{issuable.iid}/time_estimate", user), duration: '1w'
end end
context 'when duration has a bad format' do context 'when duration has a bad format' do
it 'does not modify the original estimate' do it 'does not modify the original estimate' do
post api("/projects/#{project.id}/#{issuable_collection_name}/#{issuable.id}/time_estimate", user), duration: 'foo' post api("/projects/#{project.id}/#{issuable_collection_name}/#{issuable.iid}/time_estimate", user), duration: 'foo'
expect(response).to have_http_status(400) expect(response).to have_http_status(400)
expect(issuable.reload.human_time_estimate).to eq('1w') expect(issuable.reload.human_time_estimate).to eq('1w')
...@@ -35,7 +35,7 @@ shared_examples 'time tracking endpoints' do |issuable_name| ...@@ -35,7 +35,7 @@ shared_examples 'time tracking endpoints' do |issuable_name|
context 'with a valid duration' do context 'with a valid duration' do
it 'updates the estimate' do it 'updates the estimate' do
post api("/projects/#{project.id}/#{issuable_collection_name}/#{issuable.id}/time_estimate", user), duration: '3w1h' post api("/projects/#{project.id}/#{issuable_collection_name}/#{issuable.iid}/time_estimate", user), duration: '3w1h'
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
expect(issuable.reload.human_time_estimate).to eq('3w 1h') expect(issuable.reload.human_time_estimate).to eq('3w 1h')
...@@ -46,13 +46,13 @@ shared_examples 'time tracking endpoints' do |issuable_name| ...@@ -46,13 +46,13 @@ shared_examples 'time tracking endpoints' do |issuable_name|
describe "POST /projects/:id/#{issuable_collection_name}/:#{issuable_name}_id/reset_time_estimate" do describe "POST /projects/:id/#{issuable_collection_name}/:#{issuable_name}_id/reset_time_estimate" do
context 'with an unauthorized user' do context 'with an unauthorized user' do
subject { post(api("/projects/#{project.id}/#{issuable_collection_name}/#{issuable.id}/reset_time_estimate", non_member)) } subject { post(api("/projects/#{project.id}/#{issuable_collection_name}/#{issuable.iid}/reset_time_estimate", non_member)) }
it_behaves_like 'an unauthorized API user' it_behaves_like 'an unauthorized API user'
end end
it "resets the time estimate for #{issuable_name}" do it "resets the time estimate for #{issuable_name}" do
post api("/projects/#{project.id}/#{issuable_collection_name}/#{issuable.id}/reset_time_estimate", user) post api("/projects/#{project.id}/#{issuable_collection_name}/#{issuable.iid}/reset_time_estimate", user)
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
expect(json_response['time_estimate']).to eq(0) expect(json_response['time_estimate']).to eq(0)
...@@ -62,7 +62,7 @@ shared_examples 'time tracking endpoints' do |issuable_name| ...@@ -62,7 +62,7 @@ shared_examples 'time tracking endpoints' do |issuable_name|
describe "POST /projects/:id/#{issuable_collection_name}/:#{issuable_name}_id/add_spent_time" do describe "POST /projects/:id/#{issuable_collection_name}/:#{issuable_name}_id/add_spent_time" do
context 'with an unauthorized user' do context 'with an unauthorized user' do
subject do subject do
post api("/projects/#{project.id}/#{issuable_collection_name}/#{issuable.id}/add_spent_time", non_member), post api("/projects/#{project.id}/#{issuable_collection_name}/#{issuable.iid}/add_spent_time", non_member),
duration: '2h' duration: '2h'
end end
...@@ -70,7 +70,7 @@ shared_examples 'time tracking endpoints' do |issuable_name| ...@@ -70,7 +70,7 @@ shared_examples 'time tracking endpoints' do |issuable_name|
end end
it "add spent time for #{issuable_name}" do it "add spent time for #{issuable_name}" do
post api("/projects/#{project.id}/#{issuable_collection_name}/#{issuable.id}/add_spent_time", user), post api("/projects/#{project.id}/#{issuable_collection_name}/#{issuable.iid}/add_spent_time", user),
duration: '2h' duration: '2h'
expect(response).to have_http_status(201) expect(response).to have_http_status(201)
...@@ -81,7 +81,7 @@ shared_examples 'time tracking endpoints' do |issuable_name| ...@@ -81,7 +81,7 @@ shared_examples 'time tracking endpoints' do |issuable_name|
it 'subtracts time of the total spent time' do it 'subtracts time of the total spent time' do
issuable.update_attributes!(spend_time: { duration: 7200, user: user }) issuable.update_attributes!(spend_time: { duration: 7200, user: user })
post api("/projects/#{project.id}/#{issuable_collection_name}/#{issuable.id}/add_spent_time", user), post api("/projects/#{project.id}/#{issuable_collection_name}/#{issuable.iid}/add_spent_time", user),
duration: '-1h' duration: '-1h'
expect(response).to have_http_status(201) expect(response).to have_http_status(201)
...@@ -93,7 +93,7 @@ shared_examples 'time tracking endpoints' do |issuable_name| ...@@ -93,7 +93,7 @@ shared_examples 'time tracking endpoints' do |issuable_name|
it 'does not modify the total time spent' do it 'does not modify the total time spent' do
issuable.update_attributes!(spend_time: { duration: 7200, user: user }) issuable.update_attributes!(spend_time: { duration: 7200, user: user })
post api("/projects/#{project.id}/#{issuable_collection_name}/#{issuable.id}/add_spent_time", user), post api("/projects/#{project.id}/#{issuable_collection_name}/#{issuable.iid}/add_spent_time", user),
duration: '-1w' duration: '-1w'
expect(response).to have_http_status(400) expect(response).to have_http_status(400)
...@@ -104,13 +104,13 @@ shared_examples 'time tracking endpoints' do |issuable_name| ...@@ -104,13 +104,13 @@ shared_examples 'time tracking endpoints' do |issuable_name|
describe "POST /projects/:id/#{issuable_collection_name}/:#{issuable_name}_id/reset_spent_time" do describe "POST /projects/:id/#{issuable_collection_name}/:#{issuable_name}_id/reset_spent_time" do
context 'with an unauthorized user' do context 'with an unauthorized user' do
subject { post(api("/projects/#{project.id}/#{issuable_collection_name}/#{issuable.id}/reset_spent_time", non_member)) } subject { post(api("/projects/#{project.id}/#{issuable_collection_name}/#{issuable.iid}/reset_spent_time", non_member)) }
it_behaves_like 'an unauthorized API user' it_behaves_like 'an unauthorized API user'
end end
it "resets spent time for #{issuable_name}" do it "resets spent time for #{issuable_name}" do
post api("/projects/#{project.id}/#{issuable_collection_name}/#{issuable.id}/reset_spent_time", user) post api("/projects/#{project.id}/#{issuable_collection_name}/#{issuable.iid}/reset_spent_time", user)
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
expect(json_response['total_time_spent']).to eq(0) expect(json_response['total_time_spent']).to eq(0)
...@@ -122,7 +122,7 @@ shared_examples 'time tracking endpoints' do |issuable_name| ...@@ -122,7 +122,7 @@ shared_examples 'time tracking endpoints' do |issuable_name|
issuable.update_attributes!(spend_time: { duration: 1800, user: user }, issuable.update_attributes!(spend_time: { duration: 1800, user: user },
time_estimate: 3600) time_estimate: 3600)
get api("/projects/#{project.id}/#{issuable_collection_name}/#{issuable.id}/time_stats", user) get api("/projects/#{project.id}/#{issuable_collection_name}/#{issuable.iid}/time_stats", user)
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
expect(json_response['total_time_spent']).to eq(1800) expect(json_response['total_time_spent']).to eq(1800)
......
shared_examples 'V3 time tracking endpoints' do |issuable_name|
issuable_collection_name = issuable_name.pluralize
describe "POST /projects/:id/#{issuable_collection_name}/:#{issuable_name}_id/time_estimate" do
context 'with an unauthorized user' do
subject { post(v3_api("/projects/#{project.id}/#{issuable_collection_name}/#{issuable.id}/time_estimate", non_member), duration: '1w') }
it_behaves_like 'an unauthorized API user'
end
it "sets the time estimate for #{issuable_name}" do
post v3_api("/projects/#{project.id}/#{issuable_collection_name}/#{issuable.id}/time_estimate", user), duration: '1w'
expect(response).to have_http_status(200)
expect(json_response['human_time_estimate']).to eq('1w')
end
describe 'updating the current estimate' do
before do
post v3_api("/projects/#{project.id}/#{issuable_collection_name}/#{issuable.id}/time_estimate", user), duration: '1w'
end
context 'when duration has a bad format' do
it 'does not modify the original estimate' do
post v3_api("/projects/#{project.id}/#{issuable_collection_name}/#{issuable.id}/time_estimate", user), duration: 'foo'
expect(response).to have_http_status(400)
expect(issuable.reload.human_time_estimate).to eq('1w')
end
end
context 'with a valid duration' do
it 'updates the estimate' do
post v3_api("/projects/#{project.id}/#{issuable_collection_name}/#{issuable.id}/time_estimate", user), duration: '3w1h'
expect(response).to have_http_status(200)
expect(issuable.reload.human_time_estimate).to eq('3w 1h')
end
end
end
end
describe "POST /projects/:id/#{issuable_collection_name}/:#{issuable_name}_id/reset_time_estimate" do
context 'with an unauthorized user' do
subject { post(v3_api("/projects/#{project.id}/#{issuable_collection_name}/#{issuable.id}/reset_time_estimate", non_member)) }
it_behaves_like 'an unauthorized API user'
end
it "resets the time estimate for #{issuable_name}" do
post v3_api("/projects/#{project.id}/#{issuable_collection_name}/#{issuable.id}/reset_time_estimate", user)
expect(response).to have_http_status(200)
expect(json_response['time_estimate']).to eq(0)
end
end
describe "POST /projects/:id/#{issuable_collection_name}/:#{issuable_name}_id/add_spent_time" do
context 'with an unauthorized user' do
subject do
post v3_api("/projects/#{project.id}/#{issuable_collection_name}/#{issuable.id}/add_spent_time", non_member),
duration: '2h'
end
it_behaves_like 'an unauthorized API user'
end
it "add spent time for #{issuable_name}" do
post v3_api("/projects/#{project.id}/#{issuable_collection_name}/#{issuable.id}/add_spent_time", user),
duration: '2h'
expect(response).to have_http_status(201)
expect(json_response['human_total_time_spent']).to eq('2h')
end
context 'when subtracting time' do
it 'subtracts time of the total spent time' do
issuable.update_attributes!(spend_time: { duration: 7200, user: user })
post v3_api("/projects/#{project.id}/#{issuable_collection_name}/#{issuable.id}/add_spent_time", user),
duration: '-1h'
expect(response).to have_http_status(201)
expect(json_response['total_time_spent']).to eq(3600)
end
end
context 'when time to subtract is greater than the total spent time' do
it 'does not modify the total time spent' do
issuable.update_attributes!(spend_time: { duration: 7200, user: user })
post v3_api("/projects/#{project.id}/#{issuable_collection_name}/#{issuable.id}/add_spent_time", user),
duration: '-1w'
expect(response).to have_http_status(400)
expect(json_response['message']['time_spent'].first).to match(/exceeds the total time spent/)
end
end
end
describe "POST /projects/:id/#{issuable_collection_name}/:#{issuable_name}_id/reset_spent_time" do
context 'with an unauthorized user' do
subject { post(v3_api("/projects/#{project.id}/#{issuable_collection_name}/#{issuable.id}/reset_spent_time", non_member)) }
it_behaves_like 'an unauthorized API user'
end
it "resets spent time for #{issuable_name}" do
post v3_api("/projects/#{project.id}/#{issuable_collection_name}/#{issuable.id}/reset_spent_time", user)
expect(response).to have_http_status(200)
expect(json_response['total_time_spent']).to eq(0)
end
end
describe "GET /projects/:id/#{issuable_collection_name}/:#{issuable_name}_id/time_stats" do
it "returns the time stats for #{issuable_name}" do
issuable.update_attributes!(spend_time: { duration: 1800, user: user },
time_estimate: 3600)
get v3_api("/projects/#{project.id}/#{issuable_collection_name}/#{issuable.id}/time_stats", user)
expect(response).to have_http_status(200)
expect(json_response['total_time_spent']).to eq(1800)
expect(json_response['time_estimate']).to eq(3600)
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