Commit a7055be1fdecc51afc4e8f0e94267fcd9d9ef0c1
Exists in
master
and in
4 other branches
Merge pull request #2835 from Asquera/fixes/api
Fix API return codes
Showing
31 changed files
with
1095 additions
and
248 deletions
Show diff stats
app/models/merge_request.rb
| ... | ... | @@ -91,7 +91,7 @@ class MergeRequest < ActiveRecord::Base |
| 91 | 91 | |
| 92 | 92 | def validate_branches |
| 93 | 93 | if target_branch == source_branch |
| 94 | - errors.add :base, "You can not use same branch for source and target branches" | |
| 94 | + errors.add :branch_conflict, "You can not use same branch for source and target branches" | |
| 95 | 95 | end |
| 96 | 96 | end |
| 97 | 97 | ... | ... |
app/models/project.rb
| ... | ... | @@ -160,7 +160,7 @@ class Project < ActiveRecord::Base |
| 160 | 160 | |
| 161 | 161 | def check_limit |
| 162 | 162 | unless creator.can_create_project? |
| 163 | - errors[:base] << ("Your own projects limit is #{creator.projects_limit}! Please contact administrator to increase it") | |
| 163 | + errors[:limit_reached] << ("Your own projects limit is #{creator.projects_limit}! Please contact administrator to increase it") | |
| 164 | 164 | end |
| 165 | 165 | rescue |
| 166 | 166 | errors[:base] << ("Can't check your ability to create project") | ... | ... |
config/routes.rb
doc/api/README.md
| 1 | 1 | # GitLab API |
| 2 | 2 | |
| 3 | -All API requests require authentication. You need to pass a `private_token` parameter by url or header. You can find or reset your private token in your profile. | |
| 3 | +All API requests require authentication. You need to pass a `private_token` parameter by url or header. If passed as header, the header name must be "PRIVATE-TOKEN" (capital and with dash instead of underscore). You can find or reset your private token in your profile. | |
| 4 | 4 | |
| 5 | 5 | If no, or an invalid, `private_token` is provided then an error message will be returned with status code 401: |
| 6 | 6 | |
| ... | ... | @@ -18,8 +18,48 @@ Example of a valid API request: |
| 18 | 18 | GET http://example.com/api/v3/projects?private_token=QVy1PB7sTxfy4pqfZM1U |
| 19 | 19 | ``` |
| 20 | 20 | |
| 21 | +Example for a valid API request using curl and authentication via header: | |
| 22 | + | |
| 23 | +``` | |
| 24 | +curl --header "PRIVATE-TOKEN: QVy1PB7sTxfy4pqfZM1U" "http://example.com/api/v3/projects" | |
| 25 | +``` | |
| 26 | + | |
| 27 | + | |
| 21 | 28 | The API uses JSON to serialize data. You don't need to specify `.json` at the end of API URL. |
| 22 | 29 | |
| 30 | + | |
| 31 | + | |
| 32 | +## Status codes | |
| 33 | + | |
| 34 | +The API is designed to return different status codes according to context and action. In this way | |
| 35 | +if a request results in an error the caller is able to get insight into what went wrong, e.g. | |
| 36 | +status code `400 Bad Request` is returned if a required attribute is missing from the request. | |
| 37 | +The following list gives an overview of how the API functions generally behave. | |
| 38 | + | |
| 39 | +API request types: | |
| 40 | + | |
| 41 | +* `GET` requests access one or more resources and return the result as JSON | |
| 42 | +* `POST` requests return `201 Created` if the resource is successfully created and return the newly created resource as JSON | |
| 43 | +* `GET`, `PUT` and `DELETE` return `200 Ok` if the resource is accessed, modified or deleted successfully, the (modified) result is returned as JSON | |
| 44 | +* `DELETE` requests are designed to be idempotent, meaning a request a resource still returns `200 Ok` even it was deleted before or is not available. The reasoning behind it is the user is not really interested if the resource existed before or not. | |
| 45 | + | |
| 46 | + | |
| 47 | +The following list shows the possible return codes for API requests. | |
| 48 | + | |
| 49 | +Return values: | |
| 50 | + | |
| 51 | +* `200 Ok` - The `GET`, `PUT` or `DELETE` request was successful, the resource(s) itself is returned as JSON | |
| 52 | +* `201 Created` - The `POST` request was successful and the resource is returned as JSON | |
| 53 | +* `400 Bad Request` - A required attribute of the API request is missing, e.g. the title of an issue is not given | |
| 54 | +* `401 Unauthorized` - The user is not authenticated, a valid user token is necessary, see above | |
| 55 | +* `403 Forbidden` - The request is not allowed, e.g. the user is not allowed to delete a project | |
| 56 | +* `404 Not Found` - A resource could not be accessed, e.g. an ID for a resource could not be found | |
| 57 | +* `405 Method Not Allowed` - The request is not supported | |
| 58 | +* `409 Conflict` - A conflicting resource already exists, e.g. creating a project with a name that already exists | |
| 59 | +* `500 Server Error` - While handling the request something went wrong on the server side | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 23 | 63 | #### Pagination |
| 24 | 64 | |
| 25 | 65 | When listing resources you can pass the following parameters: | ... | ... |
doc/api/groups.md
| ... | ... | @@ -17,7 +17,8 @@ GET /groups |
| 17 | 17 | ] |
| 18 | 18 | ``` |
| 19 | 19 | |
| 20 | -## Details of group | |
| 20 | + | |
| 21 | +## Details of a group | |
| 21 | 22 | |
| 22 | 23 | Get all details of a group. |
| 23 | 24 | |
| ... | ... | @@ -29,19 +30,19 @@ Parameters: |
| 29 | 30 | |
| 30 | 31 | + `id` (required) - The ID of a group |
| 31 | 32 | |
| 33 | + | |
| 32 | 34 | ## New group |
| 33 | 35 | |
| 34 | -Create a new project group. Available only for admin | |
| 36 | +Creates a new project group. Available only for admin. | |
| 35 | 37 | |
| 36 | 38 | ``` |
| 37 | 39 | POST /groups |
| 38 | 40 | ``` |
| 39 | 41 | |
| 40 | 42 | Parameters: |
| 41 | -+ `name` (required) - Email | |
| 42 | -+ `path` - Password | |
| 43 | 43 | |
| 44 | -Will return created group with status `201 Created` on success, or `404 Not found` on fail. | |
| 44 | ++ `name` (required) - The name of the group | |
| 45 | ++ `path` (required) - The path of the group | |
| 45 | 46 | |
| 46 | 47 | ## Transfer project to group |
| 47 | 48 | ... | ... |
doc/api/issues.md
| 1 | 1 | ## List issues |
| 2 | 2 | |
| 3 | -Get all issues created by authenticed user. | |
| 3 | +Get all issues created by authenticed user. This function takes pagination parameters | |
| 4 | +`page` and `per_page` to restrict the list of issues. | |
| 4 | 5 | |
| 5 | 6 | ``` |
| 6 | 7 | GET /issues |
| ... | ... | @@ -68,9 +69,11 @@ GET /issues |
| 68 | 69 | ] |
| 69 | 70 | ``` |
| 70 | 71 | |
| 72 | + | |
| 71 | 73 | ## List project issues |
| 72 | 74 | |
| 73 | -Get a list of project issues. | |
| 75 | +Get a list of project issues. This function accepts pagination parameters `page` and `per_page` | |
| 76 | +to return the list of project issues. | |
| 74 | 77 | |
| 75 | 78 | ``` |
| 76 | 79 | GET /projects/:id/issues |
| ... | ... | @@ -80,9 +83,10 @@ Parameters: |
| 80 | 83 | |
| 81 | 84 | + `id` (required) - The ID of a project |
| 82 | 85 | |
| 86 | + | |
| 83 | 87 | ## Single issue |
| 84 | 88 | |
| 85 | -Get a project issue. | |
| 89 | +Gets a single project issue. | |
| 86 | 90 | |
| 87 | 91 | ``` |
| 88 | 92 | GET /projects/:id/issues/:issue_id |
| ... | ... | @@ -133,9 +137,10 @@ Parameters: |
| 133 | 137 | } |
| 134 | 138 | ``` |
| 135 | 139 | |
| 140 | + | |
| 136 | 141 | ## New issue |
| 137 | 142 | |
| 138 | -Create a new project issue. | |
| 143 | +Creates a new project issue. | |
| 139 | 144 | |
| 140 | 145 | ``` |
| 141 | 146 | POST /projects/:id/issues |
| ... | ... | @@ -150,11 +155,10 @@ Parameters: |
| 150 | 155 | + `milestone_id` (optional) - The ID of a milestone to assign issue |
| 151 | 156 | + `labels` (optional) - Comma-separated label names for an issue |
| 152 | 157 | |
| 153 | -Will return created issue with status `201 Created` on success, or `404 Not found` on fail. | |
| 154 | 158 | |
| 155 | 159 | ## Edit issue |
| 156 | 160 | |
| 157 | -Update an existing project issue. | |
| 161 | +Updates an existing project issue. This function is also used to mark an issue as closed. | |
| 158 | 162 | |
| 159 | 163 | ``` |
| 160 | 164 | PUT /projects/:id/issues/:issue_id |
| ... | ... | @@ -171,5 +175,19 @@ Parameters: |
| 171 | 175 | + `labels` (optional) - Comma-separated label names for an issue |
| 172 | 176 | + `closed` (optional) - The state of an issue (0 = false, 1 = true) |
| 173 | 177 | |
| 174 | -Will return updated issue with status `200 OK` on success, or `404 Not found` on fail. | |
| 178 | + | |
| 179 | +## Delete existing issue (**Deprecated**) | |
| 180 | + | |
| 181 | +The function is deprecated and returns a `405 Method Not Allowed` | |
| 182 | +error if called. An issue gets now closed and is done by calling `PUT /projects/:id/issues/:issue_id` with | |
| 183 | +parameter `closed` set to 1. | |
| 184 | + | |
| 185 | +``` | |
| 186 | +DELETE /projects/:id/issues/:issue_id | |
| 187 | +``` | |
| 188 | + | |
| 189 | +Parameters: | |
| 190 | + | |
| 191 | ++ `id` (required) - The project ID | |
| 192 | ++ `issue_id` (required) - The ID of the issue | |
| 175 | 193 | ... | ... |
doc/api/merge_requests.md
| 1 | 1 | ## List merge requests |
| 2 | 2 | |
| 3 | -Get all MR for this project. | |
| 3 | +Get all merge requests for this project. This function takes pagination parameters | |
| 4 | +`page` and `per_page` to restrict the list of merge requests. | |
| 4 | 5 | |
| 5 | 6 | ``` |
| 6 | 7 | GET /projects/:id/merge_requests |
| ... | ... | @@ -40,9 +41,10 @@ Parameters: |
| 40 | 41 | ] |
| 41 | 42 | ``` |
| 42 | 43 | |
| 43 | -## Show MR | |
| 44 | 44 | |
| 45 | -Show information about MR. | |
| 45 | +## Get single MR | |
| 46 | + | |
| 47 | +Shows information about a single merge request. | |
| 46 | 48 | |
| 47 | 49 | ``` |
| 48 | 50 | GET /projects/:id/merge_request/:merge_request_id |
| ... | ... | @@ -84,7 +86,7 @@ Parameters: |
| 84 | 86 | |
| 85 | 87 | ## Create MR |
| 86 | 88 | |
| 87 | -Create MR. | |
| 89 | +Creates a new merge request. | |
| 88 | 90 | |
| 89 | 91 | ``` |
| 90 | 92 | POST /projects/:id/merge_requests |
| ... | ... | @@ -126,9 +128,10 @@ Parameters: |
| 126 | 128 | } |
| 127 | 129 | ``` |
| 128 | 130 | |
| 131 | + | |
| 129 | 132 | ## Update MR |
| 130 | 133 | |
| 131 | -Update MR. You can change branches, title, or even close the MR. | |
| 134 | +Updates an existing merge request. You can change branches, title, or even close the MR. | |
| 132 | 135 | |
| 133 | 136 | ``` |
| 134 | 137 | PUT /projects/:id/merge_request/:merge_request_id |
| ... | ... | @@ -172,9 +175,11 @@ Parameters: |
| 172 | 175 | } |
| 173 | 176 | } |
| 174 | 177 | ``` |
| 178 | + | |
| 179 | + | |
| 175 | 180 | ## Post comment to MR |
| 176 | 181 | |
| 177 | -Post comment to MR | |
| 182 | +Adds a comment to a merge request. | |
| 178 | 183 | |
| 179 | 184 | ``` |
| 180 | 185 | POST /projects/:id/merge_request/:merge_request_id/comments |
| ... | ... | @@ -183,10 +188,9 @@ POST /projects/:id/merge_request/:merge_request_id/comments |
| 183 | 188 | Parameters: |
| 184 | 189 | |
| 185 | 190 | + `id` (required) - The ID of a project |
| 186 | -+ `merge_request_id` (required) - ID of MR | |
| 191 | ++ `merge_request_id` (required) - ID of merge request | |
| 187 | 192 | + `note` (required) - Text of comment |
| 188 | 193 | |
| 189 | -Will return created note with status `201 Created` on success, or `404 Not found` on fail. | |
| 190 | 194 | |
| 191 | 195 | ```json |
| 192 | 196 | { | ... | ... |
doc/api/milestones.md
| 1 | 1 | ## List project milestones |
| 2 | 2 | |
| 3 | -Get a list of project milestones. | |
| 3 | +Returns a list of project milestones. | |
| 4 | 4 | |
| 5 | 5 | ``` |
| 6 | 6 | GET /projects/:id/milestones |
| ... | ... | @@ -10,9 +10,10 @@ Parameters: |
| 10 | 10 | |
| 11 | 11 | + `id` (required) - The ID of a project |
| 12 | 12 | |
| 13 | -## Single milestone | |
| 14 | 13 | |
| 15 | -Get a single project milestone. | |
| 14 | +## Get single milestone | |
| 15 | + | |
| 16 | +Gets a single project milestone. | |
| 16 | 17 | |
| 17 | 18 | ``` |
| 18 | 19 | GET /projects/:id/milestones/:milestone_id |
| ... | ... | @@ -23,9 +24,10 @@ Parameters: |
| 23 | 24 | + `id` (required) - The ID of a project |
| 24 | 25 | + `milestone_id` (required) - The ID of a project milestone |
| 25 | 26 | |
| 26 | -## New milestone | |
| 27 | 27 | |
| 28 | -Create a new project milestone. | |
| 28 | +## Create new milestone | |
| 29 | + | |
| 30 | +Creates a new project milestone. | |
| 29 | 31 | |
| 30 | 32 | ``` |
| 31 | 33 | POST /projects/:id/milestones |
| ... | ... | @@ -38,9 +40,10 @@ Parameters: |
| 38 | 40 | + `description` (optional) - The description of the milestone |
| 39 | 41 | + `due_date` (optional) - The due date of the milestone |
| 40 | 42 | |
| 43 | + | |
| 41 | 44 | ## Edit milestone |
| 42 | 45 | |
| 43 | -Update an existing project milestone. | |
| 46 | +Updates an existing project milestone. | |
| 44 | 47 | |
| 45 | 48 | ``` |
| 46 | 49 | PUT /projects/:id/milestones/:milestone_id |
| ... | ... | @@ -54,3 +57,4 @@ Parameters: |
| 54 | 57 | + `description` (optional) - The description of a milestone |
| 55 | 58 | + `due_date` (optional) - The due date of the milestone |
| 56 | 59 | + `closed` (optional) - The status of the milestone |
| 60 | + | ... | ... |
doc/api/notes.md
| 1 | -## List notes | |
| 1 | +## Wall | |
| 2 | 2 | |
| 3 | 3 | ### List project wall notes |
| 4 | 4 | |
| ... | ... | @@ -30,22 +30,40 @@ Parameters: |
| 30 | 30 | |
| 31 | 31 | + `id` (required) - The ID of a project |
| 32 | 32 | |
| 33 | -### List merge request notes | |
| 34 | 33 | |
| 35 | -Get a list of merge request notes. | |
| 34 | +### Get single wall note | |
| 35 | + | |
| 36 | +Returns a single wall note. | |
| 36 | 37 | |
| 37 | 38 | ``` |
| 38 | -GET /projects/:id/merge_requests/:merge_request_id/notes | |
| 39 | +GET /projects/:id/notes/:note_id | |
| 39 | 40 | ``` |
| 40 | 41 | |
| 41 | 42 | Parameters: |
| 42 | 43 | |
| 43 | 44 | + `id` (required) - The ID of a project |
| 44 | -+ `merge_request_id` (required) - The ID of an merge request | |
| 45 | ++ `note_id` (required) - The ID of a wall note | |
| 45 | 46 | |
| 46 | -### List issue notes | |
| 47 | 47 | |
| 48 | -Get a list of issue notes. | |
| 48 | +### Create new wall note | |
| 49 | + | |
| 50 | +Creates a new wall note. | |
| 51 | + | |
| 52 | +``` | |
| 53 | +POST /projects/:id/notes | |
| 54 | +``` | |
| 55 | + | |
| 56 | +Parameters: | |
| 57 | + | |
| 58 | ++ `id` (required) - The ID of a project | |
| 59 | ++ `body` (required) - The content of a note | |
| 60 | + | |
| 61 | + | |
| 62 | +## Issues | |
| 63 | + | |
| 64 | +### List project issue notes | |
| 65 | + | |
| 66 | +Gets a list of all notes for a single issue. | |
| 49 | 67 | |
| 50 | 68 | ``` |
| 51 | 69 | GET /projects/:id/issues/:issue_id/notes |
| ... | ... | @@ -56,54 +74,59 @@ Parameters: |
| 56 | 74 | + `id` (required) - The ID of a project |
| 57 | 75 | + `issue_id` (required) - The ID of an issue |
| 58 | 76 | |
| 59 | -### List snippet notes | |
| 60 | 77 | |
| 61 | -Get a list of snippet notes. | |
| 78 | +### Get single issue note | |
| 79 | + | |
| 80 | +Returns a single note for a specific project issue | |
| 62 | 81 | |
| 63 | 82 | ``` |
| 64 | -GET /projects/:id/snippets/:snippet_id/notes | |
| 83 | +GET /projects/:id/issues/:issue_id/notes/:note_id | |
| 65 | 84 | ``` |
| 66 | 85 | |
| 67 | 86 | Parameters: |
| 68 | 87 | |
| 69 | 88 | + `id` (required) - The ID of a project |
| 70 | -+ `snippet_id` (required) - The ID of a snippet | |
| 89 | ++ `issue_id` (required) - The ID of a project issue | |
| 90 | ++ `note_id` (required) - The ID of an issue note | |
| 71 | 91 | |
| 72 | -## Single note | |
| 73 | 92 | |
| 74 | -### Single wall note | |
| 93 | +### Create new issue note | |
| 75 | 94 | |
| 76 | -Get a wall note. | |
| 95 | +Creates a new note to a single project issue. | |
| 77 | 96 | |
| 78 | 97 | ``` |
| 79 | -GET /projects/:id/notes/:note_id | |
| 98 | +POST /projects/:id/issues/:issue_id/notes | |
| 80 | 99 | ``` |
| 81 | 100 | |
| 82 | 101 | Parameters: |
| 83 | 102 | |
| 84 | 103 | + `id` (required) - The ID of a project |
| 85 | -+ `note_id` (required) - The ID of a wall note | |
| 104 | ++ `issue_id` (required) - The ID of an issue | |
| 105 | ++ `body` (required) - The content of a note | |
| 86 | 106 | |
| 87 | -### Single issue note | |
| 88 | 107 | |
| 89 | -Get an issue note. | |
| 108 | +## Snippets | |
| 109 | + | |
| 110 | +### List all snippet notes | |
| 111 | + | |
| 112 | +Gets a list of all notes for a single snippet. Snippet notes are comments users can post to a snippet. | |
| 90 | 113 | |
| 91 | 114 | ``` |
| 92 | -GET /projects/:id/issues/:issue_id/:notes/:note_id | |
| 115 | +GET /projects/:id/snippets/:snippet_id/notes | |
| 93 | 116 | ``` |
| 94 | 117 | |
| 95 | 118 | Parameters: |
| 96 | 119 | |
| 97 | 120 | + `id` (required) - The ID of a project |
| 98 | -+ `issue_id` (required) - The ID of a project issue | |
| 99 | -+ `note_id` (required) - The ID of an issue note | |
| 121 | ++ `snippet_id` (required) - The ID of a project snippet | |
| 122 | + | |
| 100 | 123 | |
| 101 | -### Single snippet note | |
| 124 | +### Get single snippet note | |
| 102 | 125 | |
| 103 | -Get a snippet note. | |
| 126 | +Returns a single note for a given snippet. | |
| 104 | 127 | |
| 105 | 128 | ``` |
| 106 | -GET /projects/:id/issues/:snippet_id/:notes/:note_id | |
| 129 | +GET /projects/:id/snippets/:snippet_id/notes/:note_id | |
| 107 | 130 | ``` |
| 108 | 131 | |
| 109 | 132 | Parameters: |
| ... | ... | @@ -112,52 +135,64 @@ Parameters: |
| 112 | 135 | + `snippet_id` (required) - The ID of a project snippet |
| 113 | 136 | + `note_id` (required) - The ID of an snippet note |
| 114 | 137 | |
| 115 | -## New note | |
| 116 | 138 | |
| 117 | -### New wall note | |
| 139 | +### Create new snippet note | |
| 118 | 140 | |
| 119 | -Create a new wall note. | |
| 141 | +Creates a new note for a single snippet. Snippet notes are comments users can post to a snippet. | |
| 120 | 142 | |
| 121 | 143 | ``` |
| 122 | -POST /projects/:id/notes | |
| 144 | +POST /projects/:id/snippets/:snippet_id/notes | |
| 123 | 145 | ``` |
| 124 | 146 | |
| 125 | 147 | Parameters: |
| 126 | 148 | |
| 127 | 149 | + `id` (required) - The ID of a project |
| 150 | ++ `snippet_id` (required) - The ID of an snippet | |
| 128 | 151 | + `body` (required) - The content of a note |
| 129 | 152 | |
| 130 | -Will return created note with status `201 Created` on success, or `404 Not found` on fail. | |
| 131 | 153 | |
| 154 | +## Merge Requests | |
| 132 | 155 | |
| 133 | -### New issue note | |
| 156 | +### List all merge request notes | |
| 134 | 157 | |
| 135 | -Create a new issue note. | |
| 158 | +Gets a list of all notes for a single merge request. | |
| 136 | 159 | |
| 137 | 160 | ``` |
| 138 | -POST /projects/:id/issues/:issue_id/notes | |
| 161 | +GET /projects/:id/merge_requests/:merge_request_id/notes | |
| 139 | 162 | ``` |
| 140 | 163 | |
| 141 | 164 | Parameters: |
| 142 | 165 | |
| 143 | 166 | + `id` (required) - The ID of a project |
| 144 | -+ `issue_id` (required) - The ID of an issue | |
| 145 | -+ `body` (required) - The content of a note | |
| 167 | ++ `merge_request_id` (required) - The ID of a project merge request | |
| 146 | 168 | |
| 147 | -Will return created note with status `201 Created` on success, or `404 Not found` on fail. | |
| 148 | 169 | |
| 149 | -### New snippet note | |
| 170 | +### Get single merge request note | |
| 150 | 171 | |
| 151 | -Create a new snippet note. | |
| 172 | +Returns a single note for a given merge request. | |
| 152 | 173 | |
| 153 | 174 | ``` |
| 154 | -POST /projects/:id/snippets/:snippet_id/notes | |
| 175 | +GET /projects/:id/merge_requests/:merge_request_id/notes/:note_id | |
| 155 | 176 | ``` |
| 156 | 177 | |
| 157 | 178 | Parameters: |
| 158 | 179 | |
| 159 | 180 | + `id` (required) - The ID of a project |
| 160 | -+ `snippet_id` (required) - The ID of an snippet | |
| 181 | ++ `merge_request_id` (required) - The ID of a project merge request | |
| 182 | ++ `note_id` (required) - The ID of a merge request note | |
| 183 | + | |
| 184 | + | |
| 185 | +### Create new merge request note | |
| 186 | + | |
| 187 | +Creates a new note for a single merge request. | |
| 188 | + | |
| 189 | +``` | |
| 190 | +POST /projects/:id/merge_requests/:merge_request_id/notes | |
| 191 | +``` | |
| 192 | + | |
| 193 | +Parameters: | |
| 194 | + | |
| 195 | ++ `id` (required) - The ID of a project | |
| 196 | ++ `merge_request_id` (required) - The ID of a merge request | |
| 161 | 197 | + `body` (required) - The content of a note |
| 162 | 198 | |
| 163 | -Will return created note with status `201 Created` on success, or `404 Not found` on fail. | ... | ... |
doc/api/projects.md
| 1 | -## List projects | |
| 1 | +## Projects | |
| 2 | + | |
| 3 | +### List projects | |
| 2 | 4 | |
| 3 | 5 | Get a list of projects owned by the authenticated user. |
| 4 | 6 | |
| ... | ... | @@ -55,9 +57,11 @@ GET /projects |
| 55 | 57 | ] |
| 56 | 58 | ``` |
| 57 | 59 | |
| 58 | -## Single project | |
| 59 | 60 | |
| 60 | -Get a specific project, identified by project ID, which is owned by the authentication user. | |
| 61 | +### Get single project | |
| 62 | + | |
| 63 | +Get a specific project, identified by project ID or NAME, which is owned by the authentication user. | |
| 64 | +Currently namespaced projects cannot retrieved by name. | |
| 61 | 65 | |
| 62 | 66 | ``` |
| 63 | 67 | GET /projects/:id |
| ... | ... | @@ -65,7 +69,7 @@ GET /projects/:id |
| 65 | 69 | |
| 66 | 70 | Parameters: |
| 67 | 71 | |
| 68 | -+ `id` (required) - The ID of a project | |
| 72 | ++ `id` (required) - The ID or NAME of a project | |
| 69 | 73 | |
| 70 | 74 | ```json |
| 71 | 75 | { |
| ... | ... | @@ -92,9 +96,10 @@ Parameters: |
| 92 | 96 | } |
| 93 | 97 | ``` |
| 94 | 98 | |
| 95 | -## Create project | |
| 96 | 99 | |
| 97 | -Create new project owned by user | |
| 100 | +### Create project | |
| 101 | + | |
| 102 | +Creates new project owned by user. | |
| 98 | 103 | |
| 99 | 104 | ``` |
| 100 | 105 | POST /projects |
| ... | ... | @@ -110,12 +115,21 @@ Parameters: |
| 110 | 115 | + `merge_requests_enabled` (optional) - enabled by default |
| 111 | 116 | + `wiki_enabled` (optional) - enabled by default |
| 112 | 117 | |
| 113 | -Will return created project with status `201 Created` on success, or `404 Not | |
| 114 | -found` on fail. | |
| 118 | +**Project access levels** | |
| 115 | 119 | |
| 116 | -## Create project for user | |
| 120 | +The project access levels are defined in the `user_project.rb` class. Currently, these levels are recoginized: | |
| 121 | + | |
| 122 | +``` | |
| 123 | + GUEST = 10 | |
| 124 | + REPORTER = 20 | |
| 125 | + DEVELOPER = 30 | |
| 126 | + MASTER = 40 | |
| 127 | +``` | |
| 117 | 128 | |
| 118 | -Create new project owned by user. Available only for admin | |
| 129 | + | |
| 130 | +### Create project for user | |
| 131 | + | |
| 132 | +Creates a new project owned by user. Available only for admins. | |
| 119 | 133 | |
| 120 | 134 | ``` |
| 121 | 135 | POST /projects/user/:user_id |
| ... | ... | @@ -132,10 +146,11 @@ Parameters: |
| 132 | 146 | + `merge_requests_enabled` (optional) - enabled by default |
| 133 | 147 | + `wiki_enabled` (optional) - enabled by default |
| 134 | 148 | |
| 135 | -Will return created project with status `201 Created` on success, or `404 Not | |
| 136 | -found` on fail. | |
| 137 | 149 | |
| 138 | -## List project team members | |
| 150 | + | |
| 151 | +## Team members | |
| 152 | + | |
| 153 | +### List project team members | |
| 139 | 154 | |
| 140 | 155 | Get a list of project team members. |
| 141 | 156 | |
| ... | ... | @@ -145,12 +160,13 @@ GET /projects/:id/members |
| 145 | 160 | |
| 146 | 161 | Parameters: |
| 147 | 162 | |
| 148 | -+ `id` (required) - The ID of a project | |
| 149 | -+ `query` - Query string | |
| 163 | ++ `id` (required) - The ID or NAME of a project | |
| 164 | ++ `query` (optional) - Query string to search for members | |
| 165 | + | |
| 150 | 166 | |
| 151 | -## Get project team member | |
| 167 | +### Get project team member | |
| 152 | 168 | |
| 153 | -Get a project team member. | |
| 169 | +Gets a project team member. | |
| 154 | 170 | |
| 155 | 171 | ``` |
| 156 | 172 | GET /projects/:id/members/:user_id |
| ... | ... | @@ -158,12 +174,11 @@ GET /projects/:id/members/:user_id |
| 158 | 174 | |
| 159 | 175 | Parameters: |
| 160 | 176 | |
| 161 | -+ `id` (required) - The ID of a project | |
| 177 | ++ `id` (required) - The ID or NAME of a project | |
| 162 | 178 | + `user_id` (required) - The ID of a user |
| 163 | 179 | |
| 164 | 180 | ```json |
| 165 | 181 | { |
| 166 | - | |
| 167 | 182 | "id": 1, |
| 168 | 183 | "username": "john_smith", |
| 169 | 184 | "email": "john@example.com", |
| ... | ... | @@ -174,9 +189,12 @@ Parameters: |
| 174 | 189 | } |
| 175 | 190 | ``` |
| 176 | 191 | |
| 177 | -## Add project team member | |
| 178 | 192 | |
| 179 | -Add a user to a project team. | |
| 193 | +### Add project team member | |
| 194 | + | |
| 195 | +Adds a user to a project team. This is an idempotent method and can be called multiple times | |
| 196 | +with the same parameters. Adding team membership to a user that is already a member does not | |
| 197 | +affect the existing membership. | |
| 180 | 198 | |
| 181 | 199 | ``` |
| 182 | 200 | POST /projects/:id/members |
| ... | ... | @@ -184,15 +202,14 @@ POST /projects/:id/members |
| 184 | 202 | |
| 185 | 203 | Parameters: |
| 186 | 204 | |
| 187 | -+ `id` (required) - The ID of a project | |
| 205 | ++ `id` (required) - The ID or NAME of a project | |
| 188 | 206 | + `user_id` (required) - The ID of a user to add |
| 189 | 207 | + `access_level` (required) - Project access level |
| 190 | 208 | |
| 191 | -Will return status `201 Created` on success, or `404 Not found` on fail. | |
| 192 | 209 | |
| 193 | -## Edit project team member | |
| 210 | +### Edit project team member | |
| 194 | 211 | |
| 195 | -Update project team member to specified access level. | |
| 212 | +Updates project team member to a specified access level. | |
| 196 | 213 | |
| 197 | 214 | ``` |
| 198 | 215 | PUT /projects/:id/members/:user_id |
| ... | ... | @@ -200,13 +217,12 @@ PUT /projects/:id/members/:user_id |
| 200 | 217 | |
| 201 | 218 | Parameters: |
| 202 | 219 | |
| 203 | -+ `id` (required) - The ID of a project | |
| 220 | ++ `id` (required) - The ID or NAME of a project | |
| 204 | 221 | + `user_id` (required) - The ID of a team member |
| 205 | 222 | + `access_level` (required) - Project access level |
| 206 | 223 | |
| 207 | -Will return status `200 OK` on success, or `404 Not found` on fail. | |
| 208 | 224 | |
| 209 | -## Remove project team member | |
| 225 | +### Remove project team member | |
| 210 | 226 | |
| 211 | 227 | Removes user from project team. |
| 212 | 228 | |
| ... | ... | @@ -216,14 +232,20 @@ DELETE /projects/:id/members/:user_id |
| 216 | 232 | |
| 217 | 233 | Parameters: |
| 218 | 234 | |
| 219 | -+ `id` (required) - The ID of a project | |
| 235 | ++ `id` (required) - The ID or NAME of a project | |
| 220 | 236 | + `user_id` (required) - The ID of a team member |
| 221 | 237 | |
| 222 | -Status code `200` will be returned on success. | |
| 238 | +This method is idempotent and can be called multiple times with the same parameters. | |
| 239 | +Revoking team membership for a user who is not currently a team member is considered success. | |
| 240 | +Please note that the returned JSON currently differs slightly. Thus you should not | |
| 241 | +rely on the returned JSON structure. | |
| 242 | + | |
| 223 | 243 | |
| 224 | -## List project hooks | |
| 244 | +## Hooks | |
| 225 | 245 | |
| 226 | -Get list for project hooks | |
| 246 | +### List project hooks | |
| 247 | + | |
| 248 | +Get list of project hooks. | |
| 227 | 249 | |
| 228 | 250 | ``` |
| 229 | 251 | GET /projects/:id/hooks |
| ... | ... | @@ -231,13 +253,12 @@ GET /projects/:id/hooks |
| 231 | 253 | |
| 232 | 254 | Parameters: |
| 233 | 255 | |
| 234 | -+ `id` (required) - The ID of a project | |
| 256 | ++ `id` (required) - The ID or NAME of a project | |
| 235 | 257 | |
| 236 | -Will return hooks with status `200 OK` on success, or `404 Not found` on fail. | |
| 237 | 258 | |
| 238 | -## Get project hook | |
| 259 | +### Get project hook | |
| 239 | 260 | |
| 240 | -Get hook for project | |
| 261 | +Get a specific hook for project. | |
| 241 | 262 | |
| 242 | 263 | ``` |
| 243 | 264 | GET /projects/:id/hooks/:hook_id |
| ... | ... | @@ -245,14 +266,21 @@ GET /projects/:id/hooks/:hook_id |
| 245 | 266 | |
| 246 | 267 | Parameters: |
| 247 | 268 | |
| 248 | -+ `id` (required) - The ID of a project | |
| 269 | ++ `id` (required) - The ID or NAME of a project | |
| 249 | 270 | + `hook_id` (required) - The ID of a project hook |
| 250 | 271 | |
| 251 | -Will return hook with status `200 OK` on success, or `404 Not found` on fail. | |
| 272 | +```json | |
| 273 | +{ | |
| 274 | + "id": 1, | |
| 275 | + "url": "http://example.com/hook", | |
| 276 | + "created_at": "2012-10-12T17:04:47Z" | |
| 277 | +} | |
| 278 | +``` | |
| 252 | 279 | |
| 253 | -## Add project hook | |
| 254 | 280 | |
| 255 | -Add hook to project | |
| 281 | +### Add project hook | |
| 282 | + | |
| 283 | +Adds a hook to project. | |
| 256 | 284 | |
| 257 | 285 | ``` |
| 258 | 286 | POST /projects/:id/hooks |
| ... | ... | @@ -260,14 +288,13 @@ POST /projects/:id/hooks |
| 260 | 288 | |
| 261 | 289 | Parameters: |
| 262 | 290 | |
| 263 | -+ `id` (required) - The ID of a project | |
| 291 | ++ `id` (required) - The ID or NAME of a project | |
| 264 | 292 | + `url` (required) - The hook URL |
| 265 | 293 | |
| 266 | -Will return status `201 Created` on success, or `404 Not found` on fail. | |
| 267 | 294 | |
| 268 | -## Edit project hook | |
| 295 | +### Edit project hook | |
| 269 | 296 | |
| 270 | -Edit hook for project | |
| 297 | +Edits a hook for project. | |
| 271 | 298 | |
| 272 | 299 | ``` |
| 273 | 300 | PUT /projects/:id/hooks/:hook_id |
| ... | ... | @@ -275,30 +302,125 @@ PUT /projects/:id/hooks/:hook_id |
| 275 | 302 | |
| 276 | 303 | Parameters: |
| 277 | 304 | |
| 278 | -+ `id` (required) - The ID of a project | |
| 305 | ++ `id` (required) - The ID or NAME of a project | |
| 279 | 306 | + `hook_id` (required) - The ID of a project hook |
| 280 | 307 | + `url` (required) - The hook URL |
| 281 | 308 | |
| 282 | -Will return status `201 Created` on success, or `404 Not found` on fail. | |
| 283 | - | |
| 284 | 309 | |
| 285 | -## Delete project hook | |
| 310 | +### Delete project hook | |
| 286 | 311 | |
| 287 | -Delete hook from project | |
| 312 | +Removes a hook from project. This is an idempotent method and can be called multiple times. | |
| 313 | +Either the hook is available or not. | |
| 288 | 314 | |
| 289 | 315 | ``` |
| 290 | -DELETE /projects/:id/hooks/:hook_id | |
| 316 | +DELETE /projects/:id/hooks/ | |
| 291 | 317 | ``` |
| 292 | 318 | |
| 293 | 319 | Parameters: |
| 294 | 320 | |
| 295 | -+ `id` (required) - The ID of a project | |
| 321 | ++ `id` (required) - The ID or NAME of a project | |
| 296 | 322 | + `hook_id` (required) - The ID of hook to delete |
| 297 | 323 | |
| 298 | -Will return status `200 OK` on success, or `404 Not found` on fail. | |
| 324 | +Note the JSON response differs if the hook is available or not. If the project hook | |
| 325 | +is available before it is returned in the JSON response or an empty response is returned. | |
| 299 | 326 | |
| 300 | 327 | |
| 301 | -## List deploy keys | |
| 328 | +## Branches | |
| 329 | + | |
| 330 | +### List branches | |
| 331 | + | |
| 332 | +Lists all branches of a project. | |
| 333 | + | |
| 334 | +``` | |
| 335 | +GET /projects/:id/repository/branches | |
| 336 | +``` | |
| 337 | + | |
| 338 | +Parameters: | |
| 339 | + | |
| 340 | ++ `id` (required) - The ID of the project | |
| 341 | + | |
| 342 | + | |
| 343 | +### List single branch | |
| 344 | + | |
| 345 | +Lists a specific branch of a project. | |
| 346 | + | |
| 347 | +``` | |
| 348 | +GET /projects/:id/repository/branches/:branch | |
| 349 | +``` | |
| 350 | + | |
| 351 | +Parameters: | |
| 352 | + | |
| 353 | ++ `id` (required) - The ID of the project. | |
| 354 | ++ `branch` (required) - The name of the branch. | |
| 355 | + | |
| 356 | + | |
| 357 | +### Protect single branch | |
| 358 | + | |
| 359 | +Protects a single branch of a project. | |
| 360 | + | |
| 361 | +``` | |
| 362 | +PUT /projects/:id/repository/branches/:branch/protect | |
| 363 | +``` | |
| 364 | + | |
| 365 | +Parameters: | |
| 366 | + | |
| 367 | ++ `id` (required) - The ID of the project. | |
| 368 | ++ `branch` (required) - The name of the branch. | |
| 369 | + | |
| 370 | + | |
| 371 | +### Unprotect single branch | |
| 372 | + | |
| 373 | +Unprotects a single branch of a project. | |
| 374 | + | |
| 375 | +``` | |
| 376 | +PUT /projects/:id/repository/branches/:branch/unprotect | |
| 377 | +``` | |
| 378 | + | |
| 379 | +Parameters: | |
| 380 | + | |
| 381 | ++ `id` (required) - The ID of the project. | |
| 382 | ++ `branch` (required) - The name of the branch. | |
| 383 | + | |
| 384 | + | |
| 385 | +### List tags | |
| 386 | + | |
| 387 | +Lists all tags of a project. | |
| 388 | + | |
| 389 | +``` | |
| 390 | +GET /projects/:id/repository/tags | |
| 391 | +``` | |
| 392 | + | |
| 393 | +Parameters: | |
| 394 | + | |
| 395 | ++ `id` (required) - The ID of the project | |
| 396 | + | |
| 397 | + | |
| 398 | +### List commits | |
| 399 | + | |
| 400 | +Lists all commits with pagination. If the optional `ref_name` name is not given the commits of | |
| 401 | +the default branch (usually master) are returned. | |
| 402 | + | |
| 403 | +``` | |
| 404 | +GET /projects/:id/repository/commits | |
| 405 | +``` | |
| 406 | + | |
| 407 | +Parameters: | |
| 408 | + | |
| 409 | ++ `id` (required) - The Id of the project | |
| 410 | ++ `ref_name` (optional) - The name of a repository branch or tag | |
| 411 | ++ `page` (optional) - The page of commits to return (`0` default) | |
| 412 | ++ `per_page` (optional) - The number of commits per page (`20` default) | |
| 413 | + | |
| 414 | +Returns values: | |
| 415 | + | |
| 416 | ++ `200 Ok` on success and a list with commits | |
| 417 | ++ `404 Not Found` if project with id or the branch with `ref_name` not found | |
| 418 | + | |
| 419 | + | |
| 420 | + | |
| 421 | +## Deploy Keys | |
| 422 | + | |
| 423 | +### List deploy keys | |
| 302 | 424 | |
| 303 | 425 | Get a list of a project's deploy keys. |
| 304 | 426 | |
| ... | ... | @@ -306,6 +428,10 @@ Get a list of a project's deploy keys. |
| 306 | 428 | GET /projects/:id/keys |
| 307 | 429 | ``` |
| 308 | 430 | |
| 431 | +Parameters: | |
| 432 | + | |
| 433 | ++ `id` (required) - The ID of the project | |
| 434 | + | |
| 309 | 435 | ```json |
| 310 | 436 | [ |
| 311 | 437 | { |
| ... | ... | @@ -325,7 +451,8 @@ GET /projects/:id/keys |
| 325 | 451 | ] |
| 326 | 452 | ``` |
| 327 | 453 | |
| 328 | -## Single deploy key | |
| 454 | + | |
| 455 | +### Single deploy key | |
| 329 | 456 | |
| 330 | 457 | Get a single key. |
| 331 | 458 | |
| ... | ... | @@ -335,7 +462,8 @@ GET /projects/:id/keys/:key_id |
| 335 | 462 | |
| 336 | 463 | Parameters: |
| 337 | 464 | |
| 338 | -+ `id` (required) - The ID of an deploy key | |
| 465 | ++ `id` (required) - The ID of the project | |
| 466 | ++ `key_id` (required) - The ID of the deploy key | |
| 339 | 467 | |
| 340 | 468 | ```json |
| 341 | 469 | { |
| ... | ... | @@ -346,9 +474,11 @@ Parameters: |
| 346 | 474 | soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=" |
| 347 | 475 | } |
| 348 | 476 | ``` |
| 349 | -## Add deploy key | |
| 350 | 477 | |
| 351 | -Create new deploy key for a project | |
| 478 | + | |
| 479 | +### Add deploy key | |
| 480 | + | |
| 481 | +Creates a new deploy key for a project. | |
| 352 | 482 | |
| 353 | 483 | ``` |
| 354 | 484 | POST /projects/:id/keys |
| ... | ... | @@ -356,13 +486,12 @@ POST /projects/:id/keys |
| 356 | 486 | |
| 357 | 487 | Parameters: |
| 358 | 488 | |
| 359 | -+ `title` (required) - new deploy key's title | |
| 360 | -+ `key` (required) - new deploy key | |
| 489 | ++ `id` (required) - The ID of the project | |
| 490 | ++ `title` (required) - New deploy key's title | |
| 491 | ++ `key` (required) - New deploy key | |
| 361 | 492 | |
| 362 | -Will return created key with status `201 Created` on success, or `404 Not | |
| 363 | -found` on fail. | |
| 364 | 493 | |
| 365 | -## Delete deploy key | |
| 494 | +### Delete deploy key | |
| 366 | 495 | |
| 367 | 496 | Delete a deploy key from a project |
| 368 | 497 | |
| ... | ... | @@ -372,6 +501,6 @@ DELETE /projects/:id/keys/:key_id |
| 372 | 501 | |
| 373 | 502 | Parameters: |
| 374 | 503 | |
| 375 | -+ `id` (required) - Deploy key ID | |
| 504 | ++ `id` (required) - The ID of the project | |
| 505 | ++ `key_id` (required) - The ID of the deploy key | |
| 376 | 506 | |
| 377 | -Will return `200 OK` on success, or `404 Not Found` on fail. | ... | ... |
doc/api/repositories.md
| 1 | -## Project repository branches | |
| 1 | +## List repository branches | |
| 2 | 2 | |
| 3 | 3 | Get a list of repository branches from a project, sorted by name alphabetically. |
| 4 | 4 | |
| ... | ... | @@ -39,7 +39,8 @@ Parameters: |
| 39 | 39 | ] |
| 40 | 40 | ``` |
| 41 | 41 | |
| 42 | -## Project repository branch | |
| 42 | + | |
| 43 | +## Get single repository branch | |
| 43 | 44 | |
| 44 | 45 | Get a single project repository branch. |
| 45 | 46 | |
| ... | ... | @@ -79,12 +80,11 @@ Parameters: |
| 79 | 80 | } |
| 80 | 81 | ``` |
| 81 | 82 | |
| 82 | -Will return status code `200` on success or `404 Not found` if the branch is not available. | |
| 83 | - | |
| 84 | 83 | |
| 85 | -## Protect a project repository branch | |
| 84 | +## Protect repository branch | |
| 86 | 85 | |
| 87 | -Protect a single project repository branch. | |
| 86 | +Protects a single project repository branch. This is an idempotent function, protecting an already | |
| 87 | +protected repository branch still returns a `200 Ok` status code. | |
| 88 | 88 | |
| 89 | 89 | ``` |
| 90 | 90 | PUT /projects/:id/repository/branches/:branch/protect |
| ... | ... | @@ -122,9 +122,11 @@ Parameters: |
| 122 | 122 | } |
| 123 | 123 | ``` |
| 124 | 124 | |
| 125 | -## Unprotect a project repository branch | |
| 126 | 125 | |
| 127 | -Unprotect a single project repository branch. | |
| 126 | +## Unprotect repository branch | |
| 127 | + | |
| 128 | +Unprotects a single project repository branch. This is an idempotent function, unprotecting an already | |
| 129 | +unprotected repository branch still returns a `200 Ok` status code. | |
| 128 | 130 | |
| 129 | 131 | ``` |
| 130 | 132 | PUT /projects/:id/repository/branches/:branch/unprotect |
| ... | ... | @@ -162,7 +164,8 @@ Parameters: |
| 162 | 164 | } |
| 163 | 165 | ``` |
| 164 | 166 | |
| 165 | -## Project repository tags | |
| 167 | + | |
| 168 | +## List project repository tags | |
| 166 | 169 | |
| 167 | 170 | Get a list of repository tags from a project, sorted by name in reverse alphabetical order. |
| 168 | 171 | |
| ... | ... | @@ -201,7 +204,8 @@ Parameters: |
| 201 | 204 | ] |
| 202 | 205 | ``` |
| 203 | 206 | |
| 204 | -## Project repository commits | |
| 207 | + | |
| 208 | +## List repository commits | |
| 205 | 209 | |
| 206 | 210 | Get a list of repository commits in a project. |
| 207 | 211 | |
| ... | ... | @@ -212,7 +216,7 @@ GET /projects/:id/repository/commits |
| 212 | 216 | Parameters: |
| 213 | 217 | |
| 214 | 218 | + `id` (required) - The ID of a project |
| 215 | -+ `ref_name` (optional) - The name of a repository branch or tag | |
| 219 | ++ `ref_name` (optional) - The name of a repository branch or tag or if not given the default branch | |
| 216 | 220 | |
| 217 | 221 | ```json |
| 218 | 222 | [ |
| ... | ... | @@ -235,6 +239,7 @@ Parameters: |
| 235 | 239 | ] |
| 236 | 240 | ``` |
| 237 | 241 | |
| 242 | + | |
| 238 | 243 | ## Raw blob content |
| 239 | 244 | |
| 240 | 245 | Get the raw file contents for a file. |
| ... | ... | @@ -248,5 +253,3 @@ Parameters: |
| 248 | 253 | + `id` (required) - The ID of a project |
| 249 | 254 | + `sha` (required) - The commit or branch name |
| 250 | 255 | + `filepath` (required) - The path the file |
| 251 | - | |
| 252 | -Will return the raw file contents. | ... | ... |
doc/api/snippets.md
| ... | ... | @@ -10,9 +10,10 @@ Parameters: |
| 10 | 10 | |
| 11 | 11 | + `id` (required) - The ID of a project |
| 12 | 12 | |
| 13 | + | |
| 13 | 14 | ## Single snippet |
| 14 | 15 | |
| 15 | -Get a project snippet. | |
| 16 | +Get a single project snippet. | |
| 16 | 17 | |
| 17 | 18 | ``` |
| 18 | 19 | GET /projects/:id/snippets/:snippet_id |
| ... | ... | @@ -42,22 +43,10 @@ Parameters: |
| 42 | 43 | } |
| 43 | 44 | ``` |
| 44 | 45 | |
| 45 | -## Snippet content | |
| 46 | - | |
| 47 | -Get a raw project snippet. | |
| 48 | - | |
| 49 | -``` | |
| 50 | -GET /projects/:id/snippets/:snippet_id/raw | |
| 51 | -``` | |
| 52 | - | |
| 53 | -Parameters: | |
| 54 | - | |
| 55 | -+ `id` (required) - The ID of a project | |
| 56 | -+ `snippet_id` (required) - The ID of a project's snippet | |
| 57 | 46 | |
| 58 | -## New snippet | |
| 47 | +## Create new snippet | |
| 59 | 48 | |
| 60 | -Create a new project snippet. | |
| 49 | +Creates a new project snippet. The user must have permission to create new snippets. | |
| 61 | 50 | |
| 62 | 51 | ``` |
| 63 | 52 | POST /projects/:id/snippets |
| ... | ... | @@ -71,11 +60,10 @@ Parameters: |
| 71 | 60 | + `lifetime` (optional) - The expiration date of a snippet |
| 72 | 61 | + `code` (required) - The content of a snippet |
| 73 | 62 | |
| 74 | -Will return created snippet with status `201 Created` on success, or `404 Not found` on fail. | |
| 75 | 63 | |
| 76 | -## Edit snippet | |
| 64 | +## Update snippet | |
| 77 | 65 | |
| 78 | -Update an existing project snippet. | |
| 66 | +Updates an existing project snippet. The user must have permission to change an existing snippet. | |
| 79 | 67 | |
| 80 | 68 | ``` |
| 81 | 69 | PUT /projects/:id/snippets/:snippet_id |
| ... | ... | @@ -90,11 +78,11 @@ Parameters: |
| 90 | 78 | + `lifetime` (optional) - The expiration date of a snippet |
| 91 | 79 | + `code` (optional) - The content of a snippet |
| 92 | 80 | |
| 93 | -Will return updated snippet with status `200 OK` on success, or `404 Not found` on fail. | |
| 94 | 81 | |
| 95 | 82 | ## Delete snippet |
| 96 | 83 | |
| 97 | -Delete existing project snippet. | |
| 84 | +Deletes an existing project snippet. This is an idempotent function and deleting a non-existent | |
| 85 | +snippet still returns a `200 Ok` status code. | |
| 98 | 86 | |
| 99 | 87 | ``` |
| 100 | 88 | DELETE /projects/:id/snippets/:snippet_id |
| ... | ... | @@ -105,5 +93,16 @@ Parameters: |
| 105 | 93 | + `id` (required) - The ID of a project |
| 106 | 94 | + `snippet_id` (required) - The ID of a project's snippet |
| 107 | 95 | |
| 108 | -Status code `200` will be returned on success. | |
| 109 | 96 | |
| 97 | +## Snippet content | |
| 98 | + | |
| 99 | +Returns the raw project snippet as plain text. | |
| 100 | + | |
| 101 | +``` | |
| 102 | +GET /projects/:id/snippets/:snippet_id/raw | |
| 103 | +``` | |
| 104 | + | |
| 105 | +Parameters: | |
| 106 | + | |
| 107 | ++ `id` (required) - The ID of a project | |
| 108 | ++ `snippet_id` (required) - The ID of a project's snippet | ... | ... |
doc/api/users.md
| ... | ... | @@ -43,6 +43,7 @@ GET /users |
| 43 | 43 | ] |
| 44 | 44 | ``` |
| 45 | 45 | |
| 46 | + | |
| 46 | 47 | ## Single user |
| 47 | 48 | |
| 48 | 49 | Get a single user. |
| ... | ... | @@ -74,37 +75,40 @@ Parameters: |
| 74 | 75 | } |
| 75 | 76 | ``` |
| 76 | 77 | |
| 78 | + | |
| 77 | 79 | ## User creation |
| 78 | -Create user. Available only for admin | |
| 80 | + | |
| 81 | +Creates a new user. Note only administrators can create new users. | |
| 79 | 82 | |
| 80 | 83 | ``` |
| 81 | 84 | POST /users |
| 82 | 85 | ``` |
| 83 | 86 | |
| 84 | 87 | Parameters: |
| 85 | -+ `email` (required) - Email | |
| 86 | -+ `password` (required) - Password | |
| 87 | -+ `username` (required) - Username | |
| 88 | -+ `name` (required) - Name | |
| 89 | -+ `skype` - Skype ID | |
| 90 | -+ `linkedin` - Linkedin | |
| 91 | -+ `twitter` - Twitter account | |
| 92 | -+ `projects_limit` - Number of projects user can create | |
| 93 | -+ `extern_uid` - External UID | |
| 94 | -+ `provider` - External provider name | |
| 95 | -+ `bio` - User's bio | |
| 96 | 88 | |
| 97 | -Will return created user with status `201 Created` on success, or `404 Not | |
| 98 | -found` on fail. | |
| 89 | ++ `email` (required) - Email | |
| 90 | ++ `password` (required) - Password | |
| 91 | ++ `username` (required) - Username | |
| 92 | ++ `name` (required) - Name | |
| 93 | ++ `skype` (optional) - Skype ID | |
| 94 | ++ `linkedin` (optional) - Linkedin | |
| 95 | ++ `twitter` (optional) - Twitter account | |
| 96 | ++ `projects_limit` (optional) - Number of projects user can create | |
| 97 | ++ `extern_uid` (optional) - External UID | |
| 98 | ++ `provider` (optional) - External provider name | |
| 99 | ++ `bio` (optional) - User's bio | |
| 100 | + | |
| 99 | 101 | |
| 100 | 102 | ## User modification |
| 101 | -Modify user. Available only for admin | |
| 103 | + | |
| 104 | +Modifies an existing user. Only administrators can change attributes of a user. | |
| 102 | 105 | |
| 103 | 106 | ``` |
| 104 | 107 | PUT /users/:id |
| 105 | 108 | ``` |
| 106 | 109 | |
| 107 | 110 | Parameters: |
| 111 | + | |
| 108 | 112 | + `email` - Email |
| 109 | 113 | + `username` - Username |
| 110 | 114 | + `name` - Name |
| ... | ... | @@ -117,23 +121,28 @@ Parameters: |
| 117 | 121 | + `provider` - External provider name |
| 118 | 122 | + `bio` - User's bio |
| 119 | 123 | |
| 124 | +Note, at the moment this method does only return a 404 error, even in cases where a 409 (Conflict) would | |
| 125 | +be more appropriate, e.g. when renaming the email address to some exsisting one. | |
| 120 | 126 | |
| 121 | -Will return created user with status `200 OK` on success, or `404 Not | |
| 122 | -found` on fail. | |
| 123 | 127 | |
| 124 | 128 | ## User deletion |
| 125 | -Delete user. Available only for admin | |
| 129 | + | |
| 130 | +Deletes a user. Available only for administrators. This is an idempotent function, calling this function | |
| 131 | +for a non-existent user id still returns a status code `200 Ok`. The JSON response differs if the user | |
| 132 | +was actually deleted or not. In the former the user is returned and in the latter not. | |
| 126 | 133 | |
| 127 | 134 | ``` |
| 128 | 135 | DELETE /users/:id |
| 129 | 136 | ``` |
| 130 | 137 | |
| 131 | -Will return deleted user with status `200 OK` on success, or `404 Not | |
| 132 | -found` on fail. | |
| 138 | +Parameters: | |
| 139 | + | |
| 140 | ++ `id` (required) - The ID of the user | |
| 141 | + | |
| 133 | 142 | |
| 134 | 143 | ## Current user |
| 135 | 144 | |
| 136 | -Get currently authenticated user. | |
| 145 | +Gets currently authenticated user. | |
| 137 | 146 | |
| 138 | 147 | ``` |
| 139 | 148 | GET /user |
| ... | ... | @@ -156,6 +165,7 @@ GET /user |
| 156 | 165 | } |
| 157 | 166 | ``` |
| 158 | 167 | |
| 168 | + | |
| 159 | 169 | ## List SSH keys |
| 160 | 170 | |
| 161 | 171 | Get a list of currently authenticated user's SSH keys. |
| ... | ... | @@ -183,6 +193,11 @@ GET /user/keys |
| 183 | 193 | ] |
| 184 | 194 | ``` |
| 185 | 195 | |
| 196 | +Parameters: | |
| 197 | + | |
| 198 | ++ **none** | |
| 199 | + | |
| 200 | + | |
| 186 | 201 | ## Single SSH key |
| 187 | 202 | |
| 188 | 203 | Get a single key. |
| ... | ... | @@ -204,9 +219,11 @@ Parameters: |
| 204 | 219 | soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=" |
| 205 | 220 | } |
| 206 | 221 | ``` |
| 222 | + | |
| 223 | + | |
| 207 | 224 | ## Add SSH key |
| 208 | 225 | |
| 209 | -Create new key owned by currently authenticated user | |
| 226 | +Creates a new key owned by the currently authenticated user. | |
| 210 | 227 | |
| 211 | 228 | ``` |
| 212 | 229 | POST /user/keys |
| ... | ... | @@ -217,8 +234,6 @@ Parameters: |
| 217 | 234 | + `title` (required) - new SSH Key's title |
| 218 | 235 | + `key` (required) - new SSH key |
| 219 | 236 | |
| 220 | -Will return created key with status `201 Created` on success, or `404 Not | |
| 221 | -found` on fail. | |
| 222 | 237 | |
| 223 | 238 | ## Add SSH key for user |
| 224 | 239 | |
| ... | ... | @@ -239,7 +254,8 @@ found` on fail. |
| 239 | 254 | |
| 240 | 255 | ## Delete SSH key |
| 241 | 256 | |
| 242 | -Delete key owned by currently authenticated user | |
| 257 | +Deletes key owned by currently authenticated user. This is an idempotent function and calling it on a key that is already | |
| 258 | +deleted or not available results in `200 Ok`. | |
| 243 | 259 | |
| 244 | 260 | ``` |
| 245 | 261 | DELETE /user/keys/:id |
| ... | ... | @@ -249,4 +265,3 @@ Parameters: |
| 249 | 265 | |
| 250 | 266 | + `id` (required) - SSH key ID |
| 251 | 267 | |
| 252 | -Will return `200 OK` on success, or `404 Not Found` on fail. | ... | ... |
lib/api.rb
| ... | ... | @@ -8,6 +8,19 @@ module Gitlab |
| 8 | 8 | rack_response({'message' => '404 Not found'}.to_json, 404) |
| 9 | 9 | end |
| 10 | 10 | |
| 11 | + rescue_from :all do |exception| | |
| 12 | + # lifted from https://github.com/rails/rails/blob/master/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb#L60 | |
| 13 | + # why is this not wrapped in something reusable? | |
| 14 | + trace = exception.backtrace | |
| 15 | + | |
| 16 | + message = "\n#{exception.class} (#{exception.message}):\n" | |
| 17 | + message << exception.annoted_source_code.to_s if exception.respond_to?(:annoted_source_code) | |
| 18 | + message << " " << trace.join("\n ") | |
| 19 | + | |
| 20 | + API.logger.add Logger::FATAL, message | |
| 21 | + rack_response({'message' => '500 Internal Server Error'}, 500) | |
| 22 | + end | |
| 23 | + | |
| 11 | 24 | format :json |
| 12 | 25 | helpers APIHelpers |
| 13 | 26 | ... | ... |
lib/api/groups.rb
| ... | ... | @@ -20,12 +20,14 @@ module Gitlab |
| 20 | 20 | # Create group. Available only for admin |
| 21 | 21 | # |
| 22 | 22 | # Parameters: |
| 23 | - # name (required) - Name | |
| 24 | - # path (required) - Path | |
| 23 | + # name (required) - The name of the group | |
| 24 | + # path (required) - The path of the group | |
| 25 | 25 | # Example Request: |
| 26 | 26 | # POST /groups |
| 27 | 27 | post do |
| 28 | 28 | authenticated_as_admin! |
| 29 | + required_attributes! [:name, :path] | |
| 30 | + | |
| 29 | 31 | attrs = attributes_for_keys [:name, :path] |
| 30 | 32 | @group = Group.new(attrs) |
| 31 | 33 | @group.owner = current_user | ... | ... |
lib/api/helpers.rb
| ... | ... | @@ -41,6 +41,17 @@ module Gitlab |
| 41 | 41 | abilities.allowed?(object, action, subject) |
| 42 | 42 | end |
| 43 | 43 | |
| 44 | + # Checks the occurrences of required attributes, each attribute must be present in the params hash | |
| 45 | + # or a Bad Request error is invoked. | |
| 46 | + # | |
| 47 | + # Parameters: | |
| 48 | + # keys (required) - A hash consisting of keys that must be present | |
| 49 | + def required_attributes!(keys) | |
| 50 | + keys.each do |key| | |
| 51 | + bad_request!(key) unless params[key].present? | |
| 52 | + end | |
| 53 | + end | |
| 54 | + | |
| 44 | 55 | def attributes_for_keys(keys) |
| 45 | 56 | attrs = {} |
| 46 | 57 | keys.each do |key| |
| ... | ... | @@ -55,6 +66,12 @@ module Gitlab |
| 55 | 66 | render_api_error!('403 Forbidden', 403) |
| 56 | 67 | end |
| 57 | 68 | |
| 69 | + def bad_request!(attribute) | |
| 70 | + message = ["400 (Bad request)"] | |
| 71 | + message << "\"" + attribute.to_s + "\" not given" | |
| 72 | + render_api_error!(message.join(' '), 400) | |
| 73 | + end | |
| 74 | + | |
| 58 | 75 | def not_found!(resource = nil) |
| 59 | 76 | message = ["404"] |
| 60 | 77 | message << resource if resource | ... | ... |
lib/api/issues.rb
| ... | ... | @@ -48,6 +48,7 @@ module Gitlab |
| 48 | 48 | # Example Request: |
| 49 | 49 | # POST /projects/:id/issues |
| 50 | 50 | post ":id/issues" do |
| 51 | + required_attributes! [:title] | |
| 51 | 52 | attrs = attributes_for_keys [:title, :description, :assignee_id, :milestone_id] |
| 52 | 53 | attrs[:label_list] = params[:labels] if params[:labels].present? |
| 53 | 54 | @issue = user_project.issues.new attrs | ... | ... |
lib/api/merge_requests.rb
| ... | ... | @@ -4,6 +4,16 @@ module Gitlab |
| 4 | 4 | before { authenticate! } |
| 5 | 5 | |
| 6 | 6 | resource :projects do |
| 7 | + helpers do | |
| 8 | + def handle_merge_request_errors!(errors) | |
| 9 | + if errors[:project_access].any? | |
| 10 | + error!(errors[:project_access], 422) | |
| 11 | + elsif errors[:branch_conflict].any? | |
| 12 | + error!(errors[:branch_conflict], 422) | |
| 13 | + end | |
| 14 | + not_found! | |
| 15 | + end | |
| 16 | + end | |
| 7 | 17 | |
| 8 | 18 | # List merge requests |
| 9 | 19 | # |
| ... | ... | @@ -51,6 +61,7 @@ module Gitlab |
| 51 | 61 | # |
| 52 | 62 | post ":id/merge_requests" do |
| 53 | 63 | authorize! :write_merge_request, user_project |
| 64 | + required_attributes! [:source_branch, :target_branch, :title] | |
| 54 | 65 | |
| 55 | 66 | attrs = attributes_for_keys [:source_branch, :target_branch, :assignee_id, :title] |
| 56 | 67 | merge_request = user_project.merge_requests.new(attrs) |
| ... | ... | @@ -60,7 +71,7 @@ module Gitlab |
| 60 | 71 | merge_request.reload_code |
| 61 | 72 | present merge_request, with: Entities::MergeRequest |
| 62 | 73 | else |
| 63 | - not_found! | |
| 74 | + handle_merge_request_errors! merge_request.errors | |
| 64 | 75 | end |
| 65 | 76 | end |
| 66 | 77 | |
| ... | ... | @@ -88,7 +99,7 @@ module Gitlab |
| 88 | 99 | merge_request.mark_as_unchecked |
| 89 | 100 | present merge_request, with: Entities::MergeRequest |
| 90 | 101 | else |
| 91 | - not_found! | |
| 102 | + handle_merge_request_errors! merge_request.errors | |
| 92 | 103 | end |
| 93 | 104 | end |
| 94 | 105 | |
| ... | ... | @@ -102,6 +113,8 @@ module Gitlab |
| 102 | 113 | # POST /projects/:id/merge_request/:merge_request_id/comments |
| 103 | 114 | # |
| 104 | 115 | post ":id/merge_request/:merge_request_id/comments" do |
| 116 | + required_attributes! [:note] | |
| 117 | + | |
| 105 | 118 | merge_request = user_project.merge_requests.find(params[:merge_request_id]) |
| 106 | 119 | note = merge_request.notes.new(note: params[:note], project_id: user_project.id) |
| 107 | 120 | note.author = current_user | ... | ... |
lib/api/milestones.rb
| ... | ... | @@ -41,6 +41,7 @@ module Gitlab |
| 41 | 41 | # POST /projects/:id/milestones |
| 42 | 42 | post ":id/milestones" do |
| 43 | 43 | authorize! :admin_milestone, user_project |
| 44 | + required_attributes! [:title] | |
| 44 | 45 | |
| 45 | 46 | attrs = attributes_for_keys [:title, :description, :due_date] |
| 46 | 47 | @milestone = user_project.milestones.new attrs | ... | ... |
lib/api/notes.rb
| ... | ... | @@ -37,12 +37,16 @@ module Gitlab |
| 37 | 37 | # Example Request: |
| 38 | 38 | # POST /projects/:id/notes |
| 39 | 39 | post ":id/notes" do |
| 40 | + required_attributes! [:body] | |
| 41 | + | |
| 40 | 42 | @note = user_project.notes.new(note: params[:body]) |
| 41 | 43 | @note.author = current_user |
| 42 | 44 | |
| 43 | 45 | if @note.save |
| 44 | 46 | present @note, with: Entities::Note |
| 45 | 47 | else |
| 48 | + # :note is exposed as :body, but :note is set on error | |
| 49 | + bad_request!(:note) if @note.errors[:note].any? | |
| 46 | 50 | not_found! |
| 47 | 51 | end |
| 48 | 52 | end |
| ... | ... | @@ -89,6 +93,8 @@ module Gitlab |
| 89 | 93 | # POST /projects/:id/issues/:noteable_id/notes |
| 90 | 94 | # POST /projects/:id/snippets/:noteable_id/notes |
| 91 | 95 | post ":id/#{noteables_str}/:#{noteable_id_str}/notes" do |
| 96 | + required_attributes! [:body] | |
| 97 | + | |
| 92 | 98 | @noteable = user_project.send(:"#{noteables_str}").find(params[:"#{noteable_id_str}"]) |
| 93 | 99 | @note = @noteable.notes.new(note: params[:body]) |
| 94 | 100 | @note.author = current_user | ... | ... |
lib/api/projects.rb
| ... | ... | @@ -4,6 +4,15 @@ module Gitlab |
| 4 | 4 | before { authenticate! } |
| 5 | 5 | |
| 6 | 6 | resource :projects do |
| 7 | + helpers do | |
| 8 | + def handle_project_member_errors(errors) | |
| 9 | + if errors[:project_access].any? | |
| 10 | + error!(errors[:project_access], 422) | |
| 11 | + end | |
| 12 | + not_found! | |
| 13 | + end | |
| 14 | + end | |
| 15 | + | |
| 7 | 16 | # Get a projects list for authenticated user |
| 8 | 17 | # |
| 9 | 18 | # Example Request: |
| ... | ... | @@ -33,9 +42,11 @@ module Gitlab |
| 33 | 42 | # wall_enabled (optional) - enabled by default |
| 34 | 43 | # merge_requests_enabled (optional) - enabled by default |
| 35 | 44 | # wiki_enabled (optional) - enabled by default |
| 45 | + # namespace_id (optional) - defaults to user namespace | |
| 36 | 46 | # Example Request |
| 37 | 47 | # POST /projects |
| 38 | 48 | post do |
| 49 | + required_attributes! [:name] | |
| 39 | 50 | attrs = attributes_for_keys [:name, |
| 40 | 51 | :description, |
| 41 | 52 | :default_branch, |
| ... | ... | @@ -48,6 +59,9 @@ module Gitlab |
| 48 | 59 | if @project.saved? |
| 49 | 60 | present @project, with: Entities::Project |
| 50 | 61 | else |
| 62 | + if @project.errors[:limit_reached].present? | |
| 63 | + error!(@project.errors[:limit_reached], 403) | |
| 64 | + end | |
| 51 | 65 | not_found! |
| 52 | 66 | end |
| 53 | 67 | end |
| ... | ... | @@ -122,16 +136,22 @@ module Gitlab |
| 122 | 136 | # POST /projects/:id/members |
| 123 | 137 | post ":id/members" do |
| 124 | 138 | authorize! :admin_project, user_project |
| 125 | - users_project = user_project.users_projects.new( | |
| 126 | - user_id: params[:user_id], | |
| 127 | - project_access: params[:access_level] | |
| 128 | - ) | |
| 139 | + required_attributes! [:user_id, :access_level] | |
| 140 | + | |
| 141 | + # either the user is already a team member or a new one | |
| 142 | + team_member = user_project.team_member_by_id(params[:user_id]) | |
| 143 | + if team_member.nil? | |
| 144 | + team_member = user_project.users_projects.new( | |
| 145 | + user_id: params[:user_id], | |
| 146 | + project_access: params[:access_level] | |
| 147 | + ) | |
| 148 | + end | |
| 129 | 149 | |
| 130 | - if users_project.save | |
| 131 | - @member = users_project.user | |
| 150 | + if team_member.save | |
| 151 | + @member = team_member.user | |
| 132 | 152 | present @member, with: Entities::ProjectMember, project: user_project |
| 133 | 153 | else |
| 134 | - not_found! | |
| 154 | + handle_project_member_errors team_member.errors | |
| 135 | 155 | end |
| 136 | 156 | end |
| 137 | 157 | |
| ... | ... | @@ -145,13 +165,16 @@ module Gitlab |
| 145 | 165 | # PUT /projects/:id/members/:user_id |
| 146 | 166 | put ":id/members/:user_id" do |
| 147 | 167 | authorize! :admin_project, user_project |
| 148 | - users_project = user_project.users_projects.find_by_user_id params[:user_id] | |
| 168 | + required_attributes! [:access_level] | |
| 169 | + | |
| 170 | + team_member = user_project.users_projects.find_by_user_id(params[:user_id]) | |
| 171 | + not_found!("User can not be found") if team_member.nil? | |
| 149 | 172 | |
| 150 | - if users_project.update_attributes(project_access: params[:access_level]) | |
| 151 | - @member = users_project.user | |
| 173 | + if team_member.update_attributes(project_access: params[:access_level]) | |
| 174 | + @member = team_member.user | |
| 152 | 175 | present @member, with: Entities::ProjectMember, project: user_project |
| 153 | 176 | else |
| 154 | - not_found! | |
| 177 | + handle_project_member_errors team_member.errors | |
| 155 | 178 | end |
| 156 | 179 | end |
| 157 | 180 | |
| ... | ... | @@ -164,8 +187,12 @@ module Gitlab |
| 164 | 187 | # DELETE /projects/:id/members/:user_id |
| 165 | 188 | delete ":id/members/:user_id" do |
| 166 | 189 | authorize! :admin_project, user_project |
| 167 | - users_project = user_project.users_projects.find_by_user_id params[:user_id] | |
| 168 | - users_project.destroy | |
| 190 | + team_member = user_project.users_projects.find_by_user_id(params[:user_id]) | |
| 191 | + unless team_member.nil? | |
| 192 | + team_member.destroy | |
| 193 | + else | |
| 194 | + {:message => "Access revoked", :id => params[:user_id].to_i} | |
| 195 | + end | |
| 169 | 196 | end |
| 170 | 197 | |
| 171 | 198 | # Get project hooks |
| ... | ... | @@ -203,11 +230,16 @@ module Gitlab |
| 203 | 230 | # POST /projects/:id/hooks |
| 204 | 231 | post ":id/hooks" do |
| 205 | 232 | authorize! :admin_project, user_project |
| 233 | + required_attributes! [:url] | |
| 234 | + | |
| 206 | 235 | @hook = user_project.hooks.new({"url" => params[:url]}) |
| 207 | 236 | if @hook.save |
| 208 | 237 | present @hook, with: Entities::Hook |
| 209 | 238 | else |
| 210 | - error!({'message' => '404 Not found'}, 404) | |
| 239 | + if @hook.errors[:url].present? | |
| 240 | + error!("Invalid url given", 422) | |
| 241 | + end | |
| 242 | + not_found! | |
| 211 | 243 | end |
| 212 | 244 | end |
| 213 | 245 | |
| ... | ... | @@ -222,27 +254,36 @@ module Gitlab |
| 222 | 254 | put ":id/hooks/:hook_id" do |
| 223 | 255 | @hook = user_project.hooks.find(params[:hook_id]) |
| 224 | 256 | authorize! :admin_project, user_project |
| 257 | + required_attributes! [:url] | |
| 225 | 258 | |
| 226 | 259 | attrs = attributes_for_keys [:url] |
| 227 | - | |
| 228 | 260 | if @hook.update_attributes attrs |
| 229 | 261 | present @hook, with: Entities::Hook |
| 230 | 262 | else |
| 263 | + if @hook.errors[:url].present? | |
| 264 | + error!("Invalid url given", 422) | |
| 265 | + end | |
| 231 | 266 | not_found! |
| 232 | 267 | end |
| 233 | 268 | end |
| 234 | 269 | |
| 235 | - # Delete project hook | |
| 270 | + # Deletes project hook. This is an idempotent function. | |
| 236 | 271 | # |
| 237 | 272 | # Parameters: |
| 238 | 273 | # id (required) - The ID of a project |
| 239 | 274 | # hook_id (required) - The ID of hook to delete |
| 240 | 275 | # Example Request: |
| 241 | 276 | # DELETE /projects/:id/hooks/:hook_id |
| 242 | - delete ":id/hooks/:hook_id" do | |
| 277 | + delete ":id/hooks" do | |
| 243 | 278 | authorize! :admin_project, user_project |
| 244 | - @hook = user_project.hooks.find(params[:hook_id]) | |
| 245 | - @hook.destroy | |
| 279 | + required_attributes! [:hook_id] | |
| 280 | + | |
| 281 | + begin | |
| 282 | + @hook = ProjectHook.find(params[:hook_id]) | |
| 283 | + @hook.destroy | |
| 284 | + rescue | |
| 285 | + # ProjectHook can raise Error if hook_id not found | |
| 286 | + end | |
| 246 | 287 | end |
| 247 | 288 | |
| 248 | 289 | # Get a project repository branches |
| ... | ... | @@ -277,6 +318,7 @@ module Gitlab |
| 277 | 318 | # PUT /projects/:id/repository/branches/:branch/protect |
| 278 | 319 | put ":id/repository/branches/:branch/protect" do |
| 279 | 320 | @branch = user_project.repo.heads.find { |item| item.name == params[:branch] } |
| 321 | + not_found! unless @branch | |
| 280 | 322 | protected = user_project.protected_branches.find_by_name(@branch.name) |
| 281 | 323 | |
| 282 | 324 | unless protected |
| ... | ... | @@ -295,6 +337,7 @@ module Gitlab |
| 295 | 337 | # PUT /projects/:id/repository/branches/:branch/unprotect |
| 296 | 338 | put ":id/repository/branches/:branch/unprotect" do |
| 297 | 339 | @branch = user_project.repo.heads.find { |item| item.name == params[:branch] } |
| 340 | + not_found! unless @branch | |
| 298 | 341 | protected = user_project.protected_branches.find_by_name(@branch.name) |
| 299 | 342 | |
| 300 | 343 | if protected |
| ... | ... | @@ -318,7 +361,7 @@ module Gitlab |
| 318 | 361 | # |
| 319 | 362 | # Parameters: |
| 320 | 363 | # id (required) - The ID of a project |
| 321 | - # ref_name (optional) - The name of a repository branch or tag | |
| 364 | + # ref_name (optional) - The name of a repository branch or tag, if not given the default branch is used | |
| 322 | 365 | # Example Request: |
| 323 | 366 | # GET /projects/:id/repository/commits |
| 324 | 367 | get ":id/repository/commits" do |
| ... | ... | @@ -366,6 +409,7 @@ module Gitlab |
| 366 | 409 | # POST /projects/:id/snippets |
| 367 | 410 | post ":id/snippets" do |
| 368 | 411 | authorize! :write_snippet, user_project |
| 412 | + required_attributes! [:title, :file_name, :code] | |
| 369 | 413 | |
| 370 | 414 | attrs = attributes_for_keys [:title, :file_name] |
| 371 | 415 | attrs[:expires_at] = params[:lifetime] if params[:lifetime].present? |
| ... | ... | @@ -414,10 +458,12 @@ module Gitlab |
| 414 | 458 | # Example Request: |
| 415 | 459 | # DELETE /projects/:id/snippets/:snippet_id |
| 416 | 460 | delete ":id/snippets/:snippet_id" do |
| 417 | - @snippet = user_project.snippets.find(params[:snippet_id]) | |
| 418 | - authorize! :modify_snippet, @snippet | |
| 419 | - | |
| 420 | - @snippet.destroy | |
| 461 | + begin | |
| 462 | + @snippet = user_project.snippets.find(params[:snippet_id]) | |
| 463 | + authorize! :modify_snippet, user_project | |
| 464 | + @snippet.destroy | |
| 465 | + rescue | |
| 466 | + end | |
| 421 | 467 | end |
| 422 | 468 | |
| 423 | 469 | # Get a raw project snippet |
| ... | ... | @@ -443,6 +489,7 @@ module Gitlab |
| 443 | 489 | # GET /projects/:id/repository/commits/:sha/blob |
| 444 | 490 | get ":id/repository/commits/:sha/blob" do |
| 445 | 491 | authorize! :download_code, user_project |
| 492 | + required_attributes! [:filepath] | |
| 446 | 493 | |
| 447 | 494 | ref = params[:sha] |
| 448 | 495 | ... | ... |
lib/api/users.rb
| ... | ... | @@ -41,6 +41,8 @@ module Gitlab |
| 41 | 41 | # POST /users |
| 42 | 42 | post do |
| 43 | 43 | authenticated_as_admin! |
| 44 | + required_attributes! [:email, :password, :name, :username] | |
| 45 | + | |
| 44 | 46 | attrs = attributes_for_keys [:email, :name, :password, :skype, :linkedin, :twitter, :projects_limit, :username, :extern_uid, :provider, :bio] |
| 45 | 47 | user = User.new attrs, as: :admin |
| 46 | 48 | if user.save |
| ... | ... | @@ -67,10 +69,12 @@ module Gitlab |
| 67 | 69 | # PUT /users/:id |
| 68 | 70 | put ":id" do |
| 69 | 71 | authenticated_as_admin! |
| 72 | + | |
| 70 | 73 | attrs = attributes_for_keys [:email, :name, :password, :skype, :linkedin, :twitter, :projects_limit, :username, :extern_uid, :provider, :bio] |
| 71 | - user = User.find_by_id(params[:id]) | |
| 74 | + user = User.find(params[:id]) | |
| 75 | + not_found!("User not found") unless user | |
| 72 | 76 | |
| 73 | - if user && user.update_attributes(attrs) | |
| 77 | + if user.update_attributes(attrs) | |
| 74 | 78 | present user, with: Entities::User |
| 75 | 79 | else |
| 76 | 80 | not_found! |
| ... | ... | @@ -147,6 +151,8 @@ module Gitlab |
| 147 | 151 | # Example Request: |
| 148 | 152 | # POST /user/keys |
| 149 | 153 | post "keys" do |
| 154 | + required_attributes! [:title, :key] | |
| 155 | + | |
| 150 | 156 | attrs = attributes_for_keys [:title, :key] |
| 151 | 157 | key = current_user.keys.new attrs |
| 152 | 158 | if key.save |
| ... | ... | @@ -156,15 +162,18 @@ module Gitlab |
| 156 | 162 | end |
| 157 | 163 | end |
| 158 | 164 | |
| 159 | - # Delete existed ssh key of currently authenticated user | |
| 165 | + # Delete existing ssh key of currently authenticated user | |
| 160 | 166 | # |
| 161 | 167 | # Parameters: |
| 162 | 168 | # id (required) - SSH Key ID |
| 163 | 169 | # Example Request: |
| 164 | 170 | # DELETE /user/keys/:id |
| 165 | 171 | delete "keys/:id" do |
| 166 | - key = current_user.keys.find params[:id] | |
| 167 | - key.delete | |
| 172 | + begin | |
| 173 | + key = current_user.keys.find params[:id] | |
| 174 | + key.delete | |
| 175 | + rescue | |
| 176 | + end | |
| 168 | 177 | end |
| 169 | 178 | end |
| 170 | 179 | end | ... | ... |
spec/models/project_spec.rb
| ... | ... | @@ -65,7 +65,7 @@ describe Project do |
| 65 | 65 | it "should not allow new projects beyond user limits" do |
| 66 | 66 | project.stub(:creator).and_return(double(can_create_project?: false, projects_limit: 1)) |
| 67 | 67 | project.should_not be_valid |
| 68 | - project.errors[:base].first.should match(/Your own projects limit is 1/) | |
| 68 | + project.errors[:limit_reached].first.should match(/Your own projects limit is 1/) | |
| 69 | 69 | end |
| 70 | 70 | end |
| 71 | 71 | ... | ... |
spec/requests/api/groups_spec.rb
| ... | ... | @@ -88,6 +88,16 @@ describe Gitlab::API do |
| 88 | 88 | post api("/groups", admin), {:name => "Duplicate Test", :path => group2.path} |
| 89 | 89 | response.status.should == 404 |
| 90 | 90 | end |
| 91 | + | |
| 92 | + it "should return 400 bad request error if name not given" do | |
| 93 | + post api("/groups", admin), { :path => group2.path } | |
| 94 | + response.status.should == 400 | |
| 95 | + end | |
| 96 | + | |
| 97 | + it "should return 400 bad request error if path not given" do | |
| 98 | + post api("/groups", admin), { :name => 'test' } | |
| 99 | + response.status.should == 400 | |
| 100 | + end | |
| 91 | 101 | end |
| 92 | 102 | end |
| 93 | 103 | ... | ... |
spec/requests/api/issues_spec.rb
| ... | ... | @@ -41,6 +41,11 @@ describe Gitlab::API do |
| 41 | 41 | response.status.should == 200 |
| 42 | 42 | json_response['title'].should == issue.title |
| 43 | 43 | end |
| 44 | + | |
| 45 | + it "should return 404 if issue id not found" do | |
| 46 | + get api("/projects/#{project.id}/issues/54321", user) | |
| 47 | + response.status.should == 404 | |
| 48 | + end | |
| 44 | 49 | end |
| 45 | 50 | |
| 46 | 51 | describe "POST /projects/:id/issues" do |
| ... | ... | @@ -52,6 +57,11 @@ describe Gitlab::API do |
| 52 | 57 | json_response['description'].should be_nil |
| 53 | 58 | json_response['labels'].should == ['label', 'label2'] |
| 54 | 59 | end |
| 60 | + | |
| 61 | + it "should return a 400 bad request if title not given" do | |
| 62 | + post api("/projects/#{project.id}/issues", user), labels: 'label, label2' | |
| 63 | + response.status.should == 400 | |
| 64 | + end | |
| 55 | 65 | end |
| 56 | 66 | |
| 57 | 67 | describe "PUT /projects/:id/issues/:issue_id to update only title" do |
| ... | ... | @@ -62,6 +72,12 @@ describe Gitlab::API do |
| 62 | 72 | |
| 63 | 73 | json_response['title'].should == 'updated title' |
| 64 | 74 | end |
| 75 | + | |
| 76 | + it "should return 404 error if issue id not found" do | |
| 77 | + put api("/projects/#{project.id}/issues/44444", user), | |
| 78 | + title: 'updated title' | |
| 79 | + response.status.should == 404 | |
| 80 | + end | |
| 65 | 81 | end |
| 66 | 82 | |
| 67 | 83 | describe "PUT /projects/:id/issues/:issue_id to update state and label" do | ... | ... |
spec/requests/api/merge_requests_spec.rb
| ... | ... | @@ -32,6 +32,11 @@ describe Gitlab::API do |
| 32 | 32 | response.status.should == 200 |
| 33 | 33 | json_response['title'].should == merge_request.title |
| 34 | 34 | end |
| 35 | + | |
| 36 | + it "should return a 404 error if merge_request_id not found" do | |
| 37 | + get api("/projects/#{project.id}/merge_request/999", user) | |
| 38 | + response.status.should == 404 | |
| 39 | + end | |
| 35 | 40 | end |
| 36 | 41 | |
| 37 | 42 | describe "POST /projects/:id/merge_requests" do |
| ... | ... | @@ -41,6 +46,30 @@ describe Gitlab::API do |
| 41 | 46 | response.status.should == 201 |
| 42 | 47 | json_response['title'].should == 'Test merge_request' |
| 43 | 48 | end |
| 49 | + | |
| 50 | + it "should return 422 when source_branch equals target_branch" do | |
| 51 | + post api("/projects/#{project.id}/merge_requests", user), | |
| 52 | + title: "Test merge_request", source_branch: "master", target_branch: "master", author: user | |
| 53 | + response.status.should == 422 | |
| 54 | + end | |
| 55 | + | |
| 56 | + it "should return 400 when source_branch is missing" do | |
| 57 | + post api("/projects/#{project.id}/merge_requests", user), | |
| 58 | + title: "Test merge_request", target_branch: "master", author: user | |
| 59 | + response.status.should == 400 | |
| 60 | + end | |
| 61 | + | |
| 62 | + it "should return 400 when target_branch is missing" do | |
| 63 | + post api("/projects/#{project.id}/merge_requests", user), | |
| 64 | + title: "Test merge_request", source_branch: "stable", author: user | |
| 65 | + response.status.should == 400 | |
| 66 | + end | |
| 67 | + | |
| 68 | + it "should return 400 when title is missing" do | |
| 69 | + post api("/projects/#{project.id}/merge_requests", user), | |
| 70 | + target_branch: 'master', source_branch: 'stable' | |
| 71 | + response.status.should == 400 | |
| 72 | + end | |
| 44 | 73 | end |
| 45 | 74 | |
| 46 | 75 | describe "PUT /projects/:id/merge_request/:merge_request_id to close MR" do |
| ... | ... | @@ -59,13 +88,24 @@ describe Gitlab::API do |
| 59 | 88 | end |
| 60 | 89 | end |
| 61 | 90 | |
| 62 | - | |
| 63 | 91 | describe "PUT /projects/:id/merge_request/:merge_request_id" do |
| 64 | 92 | it "should return merge_request" do |
| 65 | 93 | put api("/projects/#{project.id}/merge_request/#{merge_request.id}", user), title: "New title" |
| 66 | 94 | response.status.should == 200 |
| 67 | 95 | json_response['title'].should == 'New title' |
| 68 | 96 | end |
| 97 | + | |
| 98 | + it "should return 422 when source_branch and target_branch are renamed the same" do | |
| 99 | + put api("/projects/#{project.id}/merge_request/#{merge_request.id}", user), | |
| 100 | + source_branch: "master", target_branch: "master" | |
| 101 | + response.status.should == 422 | |
| 102 | + end | |
| 103 | + | |
| 104 | + it "should return merge_request with renamed target_branch" do | |
| 105 | + put api("/projects/#{project.id}/merge_request/#{merge_request.id}", user), target_branch: "test" | |
| 106 | + response.status.should == 200 | |
| 107 | + json_response['target_branch'].should == 'test' | |
| 108 | + end | |
| 69 | 109 | end |
| 70 | 110 | |
| 71 | 111 | describe "POST /projects/:id/merge_request/:merge_request_id/comments" do |
| ... | ... | @@ -74,6 +114,16 @@ describe Gitlab::API do |
| 74 | 114 | response.status.should == 201 |
| 75 | 115 | json_response['note'].should == 'My comment' |
| 76 | 116 | end |
| 117 | + | |
| 118 | + it "should return 400 if note is missing" do | |
| 119 | + post api("/projects/#{project.id}/merge_request/#{merge_request.id}/comments", user) | |
| 120 | + response.status.should == 400 | |
| 121 | + end | |
| 122 | + | |
| 123 | + it "should return 404 if note is attached to non existent merge request" do | |
| 124 | + post api("/projects/#{project.id}/merge_request/111/comments", user), note: "My comment" | |
| 125 | + response.status.should == 404 | |
| 126 | + end | |
| 77 | 127 | end |
| 78 | 128 | |
| 79 | 129 | end | ... | ... |
spec/requests/api/milestones_spec.rb
| ... | ... | @@ -16,6 +16,11 @@ describe Gitlab::API do |
| 16 | 16 | json_response.should be_an Array |
| 17 | 17 | json_response.first['title'].should == milestone.title |
| 18 | 18 | end |
| 19 | + | |
| 20 | + it "should return a 401 error if user not authenticated" do | |
| 21 | + get api("/projects/#{project.id}/milestones") | |
| 22 | + response.status.should == 401 | |
| 23 | + end | |
| 19 | 24 | end |
| 20 | 25 | |
| 21 | 26 | describe "GET /projects/:id/milestones/:milestone_id" do |
| ... | ... | @@ -24,16 +29,38 @@ describe Gitlab::API do |
| 24 | 29 | response.status.should == 200 |
| 25 | 30 | json_response['title'].should == milestone.title |
| 26 | 31 | end |
| 32 | + | |
| 33 | + it "should return 401 error if user not authenticated" do | |
| 34 | + get api("/projects/#{project.id}/milestones/#{milestone.id}") | |
| 35 | + response.status.should == 401 | |
| 36 | + end | |
| 37 | + | |
| 38 | + it "should return a 404 error if milestone id not found" do | |
| 39 | + get api("/projects/#{project.id}/milestones/1234", user) | |
| 40 | + response.status.should == 404 | |
| 41 | + end | |
| 27 | 42 | end |
| 28 | 43 | |
| 29 | 44 | describe "POST /projects/:id/milestones" do |
| 30 | 45 | it "should create a new project milestone" do |
| 31 | - post api("/projects/#{project.id}/milestones", user), | |
| 32 | - title: 'new milestone' | |
| 46 | + post api("/projects/#{project.id}/milestones", user), title: 'new milestone' | |
| 33 | 47 | response.status.should == 201 |
| 34 | 48 | json_response['title'].should == 'new milestone' |
| 35 | 49 | json_response['description'].should be_nil |
| 36 | 50 | end |
| 51 | + | |
| 52 | + it "should create a new project milestone with description and due date" do | |
| 53 | + post api("/projects/#{project.id}/milestones", user), | |
| 54 | + title: 'new milestone', description: 'release', due_date: '2013-03-02' | |
| 55 | + response.status.should == 201 | |
| 56 | + json_response['description'].should == 'release' | |
| 57 | + json_response['due_date'].should == '2013-03-02' | |
| 58 | + end | |
| 59 | + | |
| 60 | + it "should return a 400 error if title is missing" do | |
| 61 | + post api("/projects/#{project.id}/milestones", user) | |
| 62 | + response.status.should == 400 | |
| 63 | + end | |
| 37 | 64 | end |
| 38 | 65 | |
| 39 | 66 | describe "PUT /projects/:id/milestones/:milestone_id" do |
| ... | ... | @@ -43,6 +70,12 @@ describe Gitlab::API do |
| 43 | 70 | response.status.should == 200 |
| 44 | 71 | json_response['title'].should == 'updated title' |
| 45 | 72 | end |
| 73 | + | |
| 74 | + it "should return a 404 error if milestone id not found" do | |
| 75 | + put api("/projects/#{project.id}/milestones/1234", user), | |
| 76 | + title: 'updated title' | |
| 77 | + response.status.should == 404 | |
| 78 | + end | |
| 46 | 79 | end |
| 47 | 80 | |
| 48 | 81 | describe "PUT /projects/:id/milestones/:milestone_id to close milestone" do | ... | ... |
spec/requests/api/notes_spec.rb
| ... | ... | @@ -38,6 +38,11 @@ describe Gitlab::API do |
| 38 | 38 | response.status.should == 200 |
| 39 | 39 | json_response['body'].should == wall_note.note |
| 40 | 40 | end |
| 41 | + | |
| 42 | + it "should return a 404 error if note not found" do | |
| 43 | + get api("/projects/#{project.id}/notes/123", user) | |
| 44 | + response.status.should == 404 | |
| 45 | + end | |
| 41 | 46 | end |
| 42 | 47 | |
| 43 | 48 | describe "POST /projects/:id/notes" do |
| ... | ... | @@ -46,6 +51,16 @@ describe Gitlab::API do |
| 46 | 51 | response.status.should == 201 |
| 47 | 52 | json_response['body'].should == 'hi!' |
| 48 | 53 | end |
| 54 | + | |
| 55 | + it "should return 401 unauthorized error" do | |
| 56 | + post api("/projects/#{project.id}/notes") | |
| 57 | + response.status.should == 401 | |
| 58 | + end | |
| 59 | + | |
| 60 | + it "should return a 400 bad request if body is missing" do | |
| 61 | + post api("/projects/#{project.id}/notes", user) | |
| 62 | + response.status.should == 400 | |
| 63 | + end | |
| 49 | 64 | end |
| 50 | 65 | |
| 51 | 66 | describe "GET /projects/:id/noteable/:noteable_id/notes" do |
| ... | ... | @@ -56,6 +71,11 @@ describe Gitlab::API do |
| 56 | 71 | json_response.should be_an Array |
| 57 | 72 | json_response.first['body'].should == issue_note.note |
| 58 | 73 | end |
| 74 | + | |
| 75 | + it "should return a 404 error when issue id not found" do | |
| 76 | + get api("/projects/#{project.id}/issues/123/notes", user) | |
| 77 | + response.status.should == 404 | |
| 78 | + end | |
| 59 | 79 | end |
| 60 | 80 | |
| 61 | 81 | context "when noteable is a Snippet" do |
| ... | ... | @@ -65,6 +85,11 @@ describe Gitlab::API do |
| 65 | 85 | json_response.should be_an Array |
| 66 | 86 | json_response.first['body'].should == snippet_note.note |
| 67 | 87 | end |
| 88 | + | |
| 89 | + it "should return a 404 error when snippet id not found" do | |
| 90 | + get api("/projects/#{project.id}/snippets/42/notes", user) | |
| 91 | + response.status.should == 404 | |
| 92 | + end | |
| 68 | 93 | end |
| 69 | 94 | |
| 70 | 95 | context "when noteable is a Merge Request" do |
| ... | ... | @@ -74,6 +99,11 @@ describe Gitlab::API do |
| 74 | 99 | json_response.should be_an Array |
| 75 | 100 | json_response.first['body'].should == merge_request_note.note |
| 76 | 101 | end |
| 102 | + | |
| 103 | + it "should return a 404 error if merge request id not found" do | |
| 104 | + get api("/projects/#{project.id}/merge_requests/4444/notes", user) | |
| 105 | + response.status.should == 404 | |
| 106 | + end | |
| 77 | 107 | end |
| 78 | 108 | end |
| 79 | 109 | |
| ... | ... | @@ -84,6 +114,11 @@ describe Gitlab::API do |
| 84 | 114 | response.status.should == 200 |
| 85 | 115 | json_response['body'].should == issue_note.note |
| 86 | 116 | end |
| 117 | + | |
| 118 | + it "should return a 404 error if issue note not found" do | |
| 119 | + get api("/projects/#{project.id}/issues/#{issue.id}/notes/123", user) | |
| 120 | + response.status.should == 404 | |
| 121 | + end | |
| 87 | 122 | end |
| 88 | 123 | |
| 89 | 124 | context "when noteable is a Snippet" do |
| ... | ... | @@ -92,6 +127,11 @@ describe Gitlab::API do |
| 92 | 127 | response.status.should == 200 |
| 93 | 128 | json_response['body'].should == snippet_note.note |
| 94 | 129 | end |
| 130 | + | |
| 131 | + it "should return a 404 error if snippet note not found" do | |
| 132 | + get api("/projects/#{project.id}/snippets/#{snippet.id}/notes/123", user) | |
| 133 | + response.status.should == 404 | |
| 134 | + end | |
| 95 | 135 | end |
| 96 | 136 | end |
| 97 | 137 | |
| ... | ... | @@ -103,6 +143,16 @@ describe Gitlab::API do |
| 103 | 143 | json_response['body'].should == 'hi!' |
| 104 | 144 | json_response['author']['email'].should == user.email |
| 105 | 145 | end |
| 146 | + | |
| 147 | + it "should return a 400 bad request error if body not given" do | |
| 148 | + post api("/projects/#{project.id}/issues/#{issue.id}/notes", user) | |
| 149 | + response.status.should == 400 | |
| 150 | + end | |
| 151 | + | |
| 152 | + it "should return a 401 unauthorized error if user not authenticated" do | |
| 153 | + post api("/projects/#{project.id}/issues/#{issue.id}/notes"), body: 'hi!' | |
| 154 | + response.status.should == 401 | |
| 155 | + end | |
| 106 | 156 | end |
| 107 | 157 | |
| 108 | 158 | context "when noteable is a Snippet" do |
| ... | ... | @@ -112,6 +162,16 @@ describe Gitlab::API do |
| 112 | 162 | json_response['body'].should == 'hi!' |
| 113 | 163 | json_response['author']['email'].should == user.email |
| 114 | 164 | end |
| 165 | + | |
| 166 | + it "should return a 400 bad request error if body not given" do | |
| 167 | + post api("/projects/#{project.id}/snippets/#{snippet.id}/notes", user) | |
| 168 | + response.status.should == 400 | |
| 169 | + end | |
| 170 | + | |
| 171 | + it "should return a 401 unauthorized error if user not authenticated" do | |
| 172 | + post api("/projects/#{project.id}/snippets/#{snippet.id}/notes"), body: 'hi!' | |
| 173 | + response.status.should == 401 | |
| 174 | + end | |
| 115 | 175 | end |
| 116 | 176 | end |
| 117 | 177 | end | ... | ... |
spec/requests/api/projects_spec.rb
| ... | ... | @@ -7,8 +7,8 @@ describe Gitlab::API do |
| 7 | 7 | let(:user2) { create(:user) } |
| 8 | 8 | let(:user3) { create(:user) } |
| 9 | 9 | let(:admin) { create(:admin) } |
| 10 | - let!(:hook) { create(:project_hook, project: project, url: "http://example.com") } | |
| 11 | 10 | let!(:project) { create(:project, namespace: user.namespace ) } |
| 11 | + let!(:hook) { create(:project_hook, project: project, url: "http://example.com") } | |
| 12 | 12 | let!(:snippet) { create(:snippet, author: user, project: project, title: 'example') } |
| 13 | 13 | let!(:users_project) { create(:users_project, user: user, project: project, project_access: UsersProject::MASTER) } |
| 14 | 14 | let!(:users_project2) { create(:users_project, user: user3, project: project, project_access: UsersProject::DEVELOPER) } |
| ... | ... | @@ -58,6 +58,11 @@ describe Gitlab::API do |
| 58 | 58 | expect { post api("/projects", user) }.to_not change {Project.count} |
| 59 | 59 | end |
| 60 | 60 | |
| 61 | + it "should return a 400 error if name not given" do | |
| 62 | + post api("/projects", user) | |
| 63 | + response.status.should == 400 | |
| 64 | + end | |
| 65 | + | |
| 61 | 66 | it "should create last project before reaching project limit" do |
| 62 | 67 | (1..user2.projects_limit-1).each { |p| post api("/projects", user2), name: "foo#{p}" } |
| 63 | 68 | post api("/projects", user2), name: "foo" |
| ... | ... | @@ -69,9 +74,17 @@ describe Gitlab::API do |
| 69 | 74 | response.status.should == 201 |
| 70 | 75 | end |
| 71 | 76 | |
| 72 | - it "should respond with 404 on failure" do | |
| 77 | + it "should respond with 400 if name is not given" do | |
| 73 | 78 | post api("/projects", user) |
| 74 | - response.status.should == 404 | |
| 79 | + response.status.should == 400 | |
| 80 | + end | |
| 81 | + | |
| 82 | + it "should return a 403 error if project limit reached" do | |
| 83 | + (1..user.projects_limit).each do |p| | |
| 84 | + post api("/projects", user), name: "foo#{p}" | |
| 85 | + end | |
| 86 | + post api("/projects", user), name: 'bar' | |
| 87 | + response.status.should == 403 | |
| 75 | 88 | end |
| 76 | 89 | |
| 77 | 90 | it "should assign attributes to project" do |
| ... | ... | @@ -152,6 +165,12 @@ describe Gitlab::API do |
| 152 | 165 | response.status.should == 404 |
| 153 | 166 | json_response['message'].should == '404 Not Found' |
| 154 | 167 | end |
| 168 | + | |
| 169 | + it "should return a 404 error if user is not a member" do | |
| 170 | + other_user = create(:user) | |
| 171 | + get api("/projects/#{project.id}", other_user) | |
| 172 | + response.status.should == 404 | |
| 173 | + end | |
| 155 | 174 | end |
| 156 | 175 | |
| 157 | 176 | describe "GET /projects/:id/repository/branches" do |
| ... | ... | @@ -188,6 +207,17 @@ describe Gitlab::API do |
| 188 | 207 | json_response['commit']['id'].should == '621491c677087aa243f165eab467bfdfbee00be1' |
| 189 | 208 | json_response['protected'].should == true |
| 190 | 209 | end |
| 210 | + | |
| 211 | + it "should return a 404 error if branch not found" do | |
| 212 | + put api("/projects/#{project.id}/repository/branches/unknown/protect", user) | |
| 213 | + response.status.should == 404 | |
| 214 | + end | |
| 215 | + | |
| 216 | + it "should return success when protect branch again" do | |
| 217 | + put api("/projects/#{project.id}/repository/branches/new_design/protect", user) | |
| 218 | + put api("/projects/#{project.id}/repository/branches/new_design/protect", user) | |
| 219 | + response.status.should == 200 | |
| 220 | + end | |
| 191 | 221 | end |
| 192 | 222 | |
| 193 | 223 | describe "PUT /projects/:id/repository/branches/:branch/unprotect" do |
| ... | ... | @@ -199,6 +229,17 @@ describe Gitlab::API do |
| 199 | 229 | json_response['commit']['id'].should == '621491c677087aa243f165eab467bfdfbee00be1' |
| 200 | 230 | json_response['protected'].should == false |
| 201 | 231 | end |
| 232 | + | |
| 233 | + it "should return success when unprotect branch" do | |
| 234 | + put api("/projects/#{project.id}/repository/branches/unknown/unprotect", user) | |
| 235 | + response.status.should == 404 | |
| 236 | + end | |
| 237 | + | |
| 238 | + it "should return success when unprotect branch again" do | |
| 239 | + put api("/projects/#{project.id}/repository/branches/new_design/unprotect", user) | |
| 240 | + put api("/projects/#{project.id}/repository/branches/new_design/unprotect", user) | |
| 241 | + response.status.should == 200 | |
| 242 | + end | |
| 202 | 243 | end |
| 203 | 244 | |
| 204 | 245 | describe "GET /projects/:id/members" do |
| ... | ... | @@ -217,6 +258,11 @@ describe Gitlab::API do |
| 217 | 258 | json_response.count.should == 1 |
| 218 | 259 | json_response.first['email'].should == user.email |
| 219 | 260 | end |
| 261 | + | |
| 262 | + it "should return a 404 error if id not found" do | |
| 263 | + get api("/projects/9999/members", user) | |
| 264 | + response.status.should == 404 | |
| 265 | + end | |
| 220 | 266 | end |
| 221 | 267 | |
| 222 | 268 | describe "GET /projects/:id/members/:user_id" do |
| ... | ... | @@ -226,6 +272,11 @@ describe Gitlab::API do |
| 226 | 272 | json_response['email'].should == user.email |
| 227 | 273 | json_response['access_level'].should == UsersProject::MASTER |
| 228 | 274 | end |
| 275 | + | |
| 276 | + it "should return a 404 error if user id not found" do | |
| 277 | + get api("/projects/#{project.id}/members/1234", user) | |
| 278 | + response.status.should == 404 | |
| 279 | + end | |
| 229 | 280 | end |
| 230 | 281 | |
| 231 | 282 | describe "POST /projects/:id/members" do |
| ... | ... | @@ -239,6 +290,34 @@ describe Gitlab::API do |
| 239 | 290 | json_response['email'].should == user2.email |
| 240 | 291 | json_response['access_level'].should == UsersProject::DEVELOPER |
| 241 | 292 | end |
| 293 | + | |
| 294 | + it "should return a 201 status if user is already project member" do | |
| 295 | + post api("/projects/#{project.id}/members", user), user_id: user2.id, | |
| 296 | + access_level: UsersProject::DEVELOPER | |
| 297 | + expect { | |
| 298 | + post api("/projects/#{project.id}/members", user), user_id: user2.id, | |
| 299 | + access_level: UsersProject::DEVELOPER | |
| 300 | + }.not_to change { UsersProject.count }.by(1) | |
| 301 | + | |
| 302 | + response.status.should == 201 | |
| 303 | + json_response['email'].should == user2.email | |
| 304 | + json_response['access_level'].should == UsersProject::DEVELOPER | |
| 305 | + end | |
| 306 | + | |
| 307 | + it "should return a 400 error when user id is not given" do | |
| 308 | + post api("/projects/#{project.id}/members", user), access_level: UsersProject::MASTER | |
| 309 | + response.status.should == 400 | |
| 310 | + end | |
| 311 | + | |
| 312 | + it "should return a 400 error when access level is not given" do | |
| 313 | + post api("/projects/#{project.id}/members", user), user_id: user2.id | |
| 314 | + response.status.should == 400 | |
| 315 | + end | |
| 316 | + | |
| 317 | + it "should return a 422 error when access level is not known" do | |
| 318 | + post api("/projects/#{project.id}/members", user), user_id: user2.id, access_level: 1234 | |
| 319 | + response.status.should == 422 | |
| 320 | + end | |
| 242 | 321 | end |
| 243 | 322 | |
| 244 | 323 | describe "PUT /projects/:id/members/:user_id" do |
| ... | ... | @@ -248,6 +327,21 @@ describe Gitlab::API do |
| 248 | 327 | json_response['email'].should == user3.email |
| 249 | 328 | json_response['access_level'].should == UsersProject::MASTER |
| 250 | 329 | end |
| 330 | + | |
| 331 | + it "should return a 404 error if user_id is not found" do | |
| 332 | + put api("/projects/#{project.id}/members/1234", user), access_level: UsersProject::MASTER | |
| 333 | + response.status.should == 404 | |
| 334 | + end | |
| 335 | + | |
| 336 | + it "should return a 400 error when access level is not given" do | |
| 337 | + put api("/projects/#{project.id}/members/#{user3.id}", user) | |
| 338 | + response.status.should == 400 | |
| 339 | + end | |
| 340 | + | |
| 341 | + it "should return a 422 error when access level is not known" do | |
| 342 | + put api("/projects/#{project.id}/members/#{user3.id}", user), access_level: 123 | |
| 343 | + response.status.should == 422 | |
| 344 | + end | |
| 251 | 345 | end |
| 252 | 346 | |
| 253 | 347 | describe "DELETE /projects/:id/members/:user_id" do |
| ... | ... | @@ -256,6 +350,30 @@ describe Gitlab::API do |
| 256 | 350 | delete api("/projects/#{project.id}/members/#{user3.id}", user) |
| 257 | 351 | }.to change { UsersProject.count }.by(-1) |
| 258 | 352 | end |
| 353 | + | |
| 354 | + it "should return 200 if team member is not part of a project" do | |
| 355 | + delete api("/projects/#{project.id}/members/#{user3.id}", user) | |
| 356 | + expect { | |
| 357 | + delete api("/projects/#{project.id}/members/#{user3.id}", user) | |
| 358 | + }.to_not change { UsersProject.count }.by(1) | |
| 359 | + end | |
| 360 | + | |
| 361 | + it "should return 200 if team member already removed" do | |
| 362 | + delete api("/projects/#{project.id}/members/#{user3.id}", user) | |
| 363 | + delete api("/projects/#{project.id}/members/#{user3.id}", user) | |
| 364 | + response.status.should == 200 | |
| 365 | + end | |
| 366 | + end | |
| 367 | + | |
| 368 | + describe "DELETE /projects/:id/members/:user_id" do | |
| 369 | + it "should return 200 OK when the user was not member" do | |
| 370 | + expect { | |
| 371 | + delete api("/projects/#{project.id}/members/1000000", user) | |
| 372 | + }.to change { UsersProject.count }.by(0) | |
| 373 | + response.status.should == 200 | |
| 374 | + json_response['message'].should == "Access revoked" | |
| 375 | + json_response['id'].should == 1000000 | |
| 376 | + end | |
| 259 | 377 | end |
| 260 | 378 | |
| 261 | 379 | describe "GET /projects/:id/hooks" do |
| ... | ... | @@ -298,6 +416,11 @@ describe Gitlab::API do |
| 298 | 416 | response.status.should == 403 |
| 299 | 417 | end |
| 300 | 418 | end |
| 419 | + | |
| 420 | + it "should return a 404 error if hook id is not available" do | |
| 421 | + get api("/projects/#{project.id}/hooks/1234", user) | |
| 422 | + response.status.should == 404 | |
| 423 | + end | |
| 301 | 424 | end |
| 302 | 425 | |
| 303 | 426 | describe "POST /projects/:id/hooks" do |
| ... | ... | @@ -306,6 +429,17 @@ describe Gitlab::API do |
| 306 | 429 | post api("/projects/#{project.id}/hooks", user), |
| 307 | 430 | url: "http://example.com" |
| 308 | 431 | }.to change {project.hooks.count}.by(1) |
| 432 | + response.status.should == 201 | |
| 433 | + end | |
| 434 | + | |
| 435 | + it "should return a 400 error if url not given" do | |
| 436 | + post api("/projects/#{project.id}/hooks", user) | |
| 437 | + response.status.should == 400 | |
| 438 | + end | |
| 439 | + | |
| 440 | + it "should return a 422 error if url not valid" do | |
| 441 | + post api("/projects/#{project.id}/hooks", user), "url" => "ftp://example.com" | |
| 442 | + response.status.should == 422 | |
| 309 | 443 | end |
| 310 | 444 | end |
| 311 | 445 | |
| ... | ... | @@ -316,13 +450,44 @@ describe Gitlab::API do |
| 316 | 450 | response.status.should == 200 |
| 317 | 451 | json_response['url'].should == 'http://example.org' |
| 318 | 452 | end |
| 453 | + | |
| 454 | + it "should return 404 error if hook id not found" do | |
| 455 | + put api("/projects/#{project.id}/hooks/1234", user), url: 'http://example.org' | |
| 456 | + response.status.should == 404 | |
| 457 | + end | |
| 458 | + | |
| 459 | + it "should return 400 error if url is not given" do | |
| 460 | + put api("/projects/#{project.id}/hooks/#{hook.id}", user) | |
| 461 | + response.status.should == 400 | |
| 462 | + end | |
| 463 | + | |
| 464 | + it "should return a 422 error if url is not valid" do | |
| 465 | + put api("/projects/#{project.id}/hooks/#{hook.id}", user), url: 'ftp://example.com' | |
| 466 | + response.status.should == 422 | |
| 467 | + end | |
| 319 | 468 | end |
| 320 | 469 | |
| 321 | - describe "DELETE /projects/:id/hooks/:hook_id" do | |
| 470 | + describe "DELETE /projects/:id/hooks" do | |
| 322 | 471 | it "should delete hook from project" do |
| 323 | 472 | expect { |
| 324 | - delete api("/projects/#{project.id}/hooks/#{hook.id}", user) | |
| 473 | + delete api("/projects/#{project.id}/hooks", user), hook_id: hook.id | |
| 325 | 474 | }.to change {project.hooks.count}.by(-1) |
| 475 | + response.status.should == 200 | |
| 476 | + end | |
| 477 | + | |
| 478 | + it "should return success when deleting hook" do | |
| 479 | + delete api("/projects/#{project.id}/hooks", user), hook_id: hook.id | |
| 480 | + response.status.should == 200 | |
| 481 | + end | |
| 482 | + | |
| 483 | + it "should return success when deleting non existent hook" do | |
| 484 | + delete api("/projects/#{project.id}/hooks", user), hook_id: 42 | |
| 485 | + response.status.should == 200 | |
| 486 | + end | |
| 487 | + | |
| 488 | + it "should return a 400 error if hook id not given" do | |
| 489 | + delete api("/projects/#{project.id}/hooks", user) | |
| 490 | + response.status.should == 400 | |
| 326 | 491 | end |
| 327 | 492 | end |
| 328 | 493 | |
| ... | ... | @@ -371,6 +536,11 @@ describe Gitlab::API do |
| 371 | 536 | response.status.should == 200 |
| 372 | 537 | json_response['title'].should == snippet.title |
| 373 | 538 | end |
| 539 | + | |
| 540 | + it "should return a 404 error if snippet id not found" do | |
| 541 | + get api("/projects/#{project.id}/snippets/1234", user) | |
| 542 | + response.status.should == 404 | |
| 543 | + end | |
| 374 | 544 | end |
| 375 | 545 | |
| 376 | 546 | describe "POST /projects/:id/snippets" do |
| ... | ... | @@ -380,6 +550,24 @@ describe Gitlab::API do |
| 380 | 550 | response.status.should == 201 |
| 381 | 551 | json_response['title'].should == 'api test' |
| 382 | 552 | end |
| 553 | + | |
| 554 | + it "should return a 400 error if title is not given" do | |
| 555 | + post api("/projects/#{project.id}/snippets", user), | |
| 556 | + file_name: 'sample.rb', code: 'test' | |
| 557 | + response.status.should == 400 | |
| 558 | + end | |
| 559 | + | |
| 560 | + it "should return a 400 error if file_name not given" do | |
| 561 | + post api("/projects/#{project.id}/snippets", user), | |
| 562 | + title: 'api test', code: 'test' | |
| 563 | + response.status.should == 400 | |
| 564 | + end | |
| 565 | + | |
| 566 | + it "should return a 400 error if code not given" do | |
| 567 | + post api("/projects/#{project.id}/snippets", user), | |
| 568 | + title: 'api test', file_name: 'sample.rb' | |
| 569 | + response.status.should == 400 | |
| 570 | + end | |
| 383 | 571 | end |
| 384 | 572 | |
| 385 | 573 | describe "PUT /projects/:id/snippets/:shippet_id" do |
| ... | ... | @@ -390,6 +578,13 @@ describe Gitlab::API do |
| 390 | 578 | json_response['title'].should == 'example' |
| 391 | 579 | snippet.reload.content.should == 'updated code' |
| 392 | 580 | end |
| 581 | + | |
| 582 | + it "should update an existing project snippet with new title" do | |
| 583 | + put api("/projects/#{project.id}/snippets/#{snippet.id}", user), | |
| 584 | + title: 'other api test' | |
| 585 | + response.status.should == 200 | |
| 586 | + json_response['title'].should == 'other api test' | |
| 587 | + end | |
| 393 | 588 | end |
| 394 | 589 | |
| 395 | 590 | describe "DELETE /projects/:id/snippets/:snippet_id" do |
| ... | ... | @@ -397,6 +592,12 @@ describe Gitlab::API do |
| 397 | 592 | expect { |
| 398 | 593 | delete api("/projects/#{project.id}/snippets/#{snippet.id}", user) |
| 399 | 594 | }.to change { Snippet.count }.by(-1) |
| 595 | + response.status.should == 200 | |
| 596 | + end | |
| 597 | + | |
| 598 | + it "should return success when deleting unknown snippet id" do | |
| 599 | + delete api("/projects/#{project.id}/snippets/1234", user) | |
| 600 | + response.status.should == 200 | |
| 400 | 601 | end |
| 401 | 602 | end |
| 402 | 603 | |
| ... | ... | @@ -405,9 +606,14 @@ describe Gitlab::API do |
| 405 | 606 | get api("/projects/#{project.id}/snippets/#{snippet.id}/raw", user) |
| 406 | 607 | response.status.should == 200 |
| 407 | 608 | end |
| 609 | + | |
| 610 | + it "should return a 404 error if raw project snippet not found" do | |
| 611 | + get api("/projects/#{project.id}/snippets/5555/raw", user) | |
| 612 | + response.status.should == 404 | |
| 613 | + end | |
| 408 | 614 | end |
| 409 | 615 | |
| 410 | - describe "GET /projects/:id/:sha/blob" do | |
| 616 | + describe "GET /projects/:id/repository/commits/:sha/blob" do | |
| 411 | 617 | it "should get the raw file contents" do |
| 412 | 618 | get api("/projects/#{project.id}/repository/commits/master/blob?filepath=README.md", user) |
| 413 | 619 | response.status.should == 200 |
| ... | ... | @@ -422,6 +628,11 @@ describe Gitlab::API do |
| 422 | 628 | get api("/projects/#{project.id}/repository/commits/master/blob?filepath=README.invalid", user) |
| 423 | 629 | response.status.should == 404 |
| 424 | 630 | end |
| 631 | + | |
| 632 | + it "should return a 400 error if filepath is missing" do | |
| 633 | + get api("/projects/#{project.id}/repository/commits/master/blob", user) | |
| 634 | + response.status.should == 400 | |
| 635 | + end | |
| 425 | 636 | end |
| 426 | 637 | |
| 427 | 638 | describe "GET /projects/:id/keys" do | ... | ... |
spec/requests/api/session_spec.rb
| ... | ... | @@ -35,5 +35,15 @@ describe Gitlab::API do |
| 35 | 35 | json_response['private_token'].should be_nil |
| 36 | 36 | end |
| 37 | 37 | end |
| 38 | + | |
| 39 | + context "when empty name" do | |
| 40 | + it "should return authentication error" do | |
| 41 | + post api("/session"), password: user.password | |
| 42 | + response.status.should == 401 | |
| 43 | + | |
| 44 | + json_response['email'].should be_nil | |
| 45 | + json_response['private_token'].should be_nil | |
| 46 | + end | |
| 47 | + end | |
| 38 | 48 | end |
| 39 | 49 | end | ... | ... |
spec/requests/api/users_spec.rb
| ... | ... | @@ -31,15 +31,20 @@ describe Gitlab::API do |
| 31 | 31 | response.status.should == 200 |
| 32 | 32 | json_response['email'].should == user.email |
| 33 | 33 | end |
| 34 | - end | |
| 35 | 34 | |
| 36 | - describe "POST /users" do | |
| 37 | - before{ admin } | |
| 35 | + it "should return a 401 if unauthenticated" do | |
| 36 | + get api("/users/9998") | |
| 37 | + response.status.should == 401 | |
| 38 | + end | |
| 38 | 39 | |
| 39 | - it "should not create invalid user" do | |
| 40 | - post api("/users", admin), { email: "invalid email" } | |
| 40 | + it "should return a 404 error if user id not found" do | |
| 41 | + get api("/users/9999", user) | |
| 41 | 42 | response.status.should == 404 |
| 42 | 43 | end |
| 44 | + end | |
| 45 | + | |
| 46 | + describe "POST /users" do | |
| 47 | + before{ admin } | |
| 43 | 48 | |
| 44 | 49 | it "should create user" do |
| 45 | 50 | expect { |
| ... | ... | @@ -47,10 +52,48 @@ describe Gitlab::API do |
| 47 | 52 | }.to change { User.count }.by(1) |
| 48 | 53 | end |
| 49 | 54 | |
| 55 | + it "should return 201 Created on success" do | |
| 56 | + post api("/users", admin), attributes_for(:user, projects_limit: 3) | |
| 57 | + response.status.should == 201 | |
| 58 | + end | |
| 59 | + | |
| 60 | + it "should not create user with invalid email" do | |
| 61 | + post api("/users", admin), { email: "invalid email", password: 'password' } | |
| 62 | + response.status.should == 400 | |
| 63 | + end | |
| 64 | + | |
| 65 | + it "should return 400 error if password not given" do | |
| 66 | + post api("/users", admin), { email: 'test@example.com' } | |
| 67 | + response.status.should == 400 | |
| 68 | + end | |
| 69 | + | |
| 70 | + it "should return 400 error if email not given" do | |
| 71 | + post api("/users", admin), { password: 'pass1234' } | |
| 72 | + response.status.should == 400 | |
| 73 | + end | |
| 74 | + | |
| 50 | 75 | it "shouldn't available for non admin users" do |
| 51 | 76 | post api("/users", user), attributes_for(:user) |
| 52 | 77 | response.status.should == 403 |
| 53 | 78 | end |
| 79 | + | |
| 80 | + context "with existing user" do | |
| 81 | + before { post api("/users", admin), { email: 'test@example.com', password: 'password', username: 'test' } } | |
| 82 | + | |
| 83 | + it "should not create user with same email" do | |
| 84 | + expect { | |
| 85 | + post api("/users", admin), { email: 'test@example.com', password: 'password' } | |
| 86 | + }.to change { User.count }.by(0) | |
| 87 | + end | |
| 88 | + | |
| 89 | + it "should return 409 conflict error if user with email exists" do | |
| 90 | + post api("/users", admin), { email: 'test@example.com', password: 'password' } | |
| 91 | + end | |
| 92 | + | |
| 93 | + it "should return 409 conflict error if same username exists" do | |
| 94 | + post api("/users", admin), { email: 'foo@example.com', password: 'pass', username: 'test' } | |
| 95 | + end | |
| 96 | + end | |
| 54 | 97 | end |
| 55 | 98 | |
| 56 | 99 | describe "GET /users/sign_up" do |
| ... | ... | @@ -81,7 +124,7 @@ describe Gitlab::API do |
| 81 | 124 | describe "PUT /users/:id" do |
| 82 | 125 | before { admin } |
| 83 | 126 | |
| 84 | - it "should update user" do | |
| 127 | + it "should update user with new bio" do | |
| 85 | 128 | put api("/users/#{user.id}", admin), {bio: 'new test bio'} |
| 86 | 129 | response.status.should == 200 |
| 87 | 130 | json_response['bio'].should == 'new test bio' |
| ... | ... | @@ -103,6 +146,25 @@ describe Gitlab::API do |
| 103 | 146 | put api("/users/999999", admin), {bio: 'update should fail'} |
| 104 | 147 | response.status.should == 404 |
| 105 | 148 | end |
| 149 | + | |
| 150 | + context "with existing user" do | |
| 151 | + before { | |
| 152 | + post api("/users", admin), { email: 'test@example.com', password: 'password', username: 'test', name: 'test' } | |
| 153 | + post api("/users", admin), { email: 'foo@bar.com', password: 'password', username: 'john', name: 'john' } | |
| 154 | + @user_id = User.all.last.id | |
| 155 | + } | |
| 156 | + | |
| 157 | +# it "should return 409 conflict error if email address exists" do | |
| 158 | +# put api("/users/#{@user_id}", admin), { email: 'test@example.com' } | |
| 159 | +# response.status.should == 409 | |
| 160 | +# end | |
| 161 | +# | |
| 162 | +# it "should return 409 conflict error if username taken" do | |
| 163 | +# @user_id = User.all.last.id | |
| 164 | +# put api("/users/#{@user_id}", admin), { username: 'test' } | |
| 165 | +# response.status.should == 409 | |
| 166 | +# end | |
| 167 | + end | |
| 106 | 168 | end |
| 107 | 169 | |
| 108 | 170 | describe "POST /users/:id/keys" do |
| ... | ... | @@ -131,6 +193,11 @@ describe Gitlab::API do |
| 131 | 193 | json_response['email'].should == user.email |
| 132 | 194 | end |
| 133 | 195 | |
| 196 | + it "should not delete for unauthenticated user" do | |
| 197 | + delete api("/users/#{user.id}") | |
| 198 | + response.status.should == 401 | |
| 199 | + end | |
| 200 | + | |
| 134 | 201 | it "shouldn't available for non admin users" do |
| 135 | 202 | delete api("/users/#{user.id}", user) |
| 136 | 203 | response.status.should == 403 |
| ... | ... | @@ -148,6 +215,11 @@ describe Gitlab::API do |
| 148 | 215 | response.status.should == 200 |
| 149 | 216 | json_response['email'].should == user.email |
| 150 | 217 | end |
| 218 | + | |
| 219 | + it "should return 401 error if user is unauthenticated" do | |
| 220 | + get api("/user") | |
| 221 | + response.status.should == 401 | |
| 222 | + end | |
| 151 | 223 | end |
| 152 | 224 | |
| 153 | 225 | describe "GET /user/keys" do |
| ... | ... | @@ -183,19 +255,38 @@ describe Gitlab::API do |
| 183 | 255 | get api("/user/keys/42", user) |
| 184 | 256 | response.status.should == 404 |
| 185 | 257 | end |
| 186 | - end | |
| 187 | 258 | |
| 188 | - describe "POST /user/keys" do | |
| 189 | - it "should not create invalid ssh key" do | |
| 190 | - post api("/user/keys", user), { title: "invalid key" } | |
| 259 | + it "should return 404 error if admin accesses user's ssh key" do | |
| 260 | + user.keys << key | |
| 261 | + user.save | |
| 262 | + admin | |
| 263 | + get api("/user/keys/#{key.id}", admin) | |
| 191 | 264 | response.status.should == 404 |
| 192 | 265 | end |
| 266 | + end | |
| 193 | 267 | |
| 268 | + describe "POST /user/keys" do | |
| 194 | 269 | it "should create ssh key" do |
| 195 | 270 | key_attrs = attributes_for :key |
| 196 | 271 | expect { |
| 197 | 272 | post api("/user/keys", user), key_attrs |
| 198 | 273 | }.to change{ user.keys.count }.by(1) |
| 274 | + response.status.should == 201 | |
| 275 | + end | |
| 276 | + | |
| 277 | + it "should return a 401 error if unauthorized" do | |
| 278 | + post api("/user/keys"), title: 'some title', key: 'some key' | |
| 279 | + response.status.should == 401 | |
| 280 | + end | |
| 281 | + | |
| 282 | + it "should not create ssh key without key" do | |
| 283 | + post api("/user/keys", user), title: 'title' | |
| 284 | + response.status.should == 400 | |
| 285 | + end | |
| 286 | + | |
| 287 | + it "should not create ssh key without title" do | |
| 288 | + post api("/user/keys", user), key: "somekey" | |
| 289 | + response.status.should == 400 | |
| 199 | 290 | end |
| 200 | 291 | end |
| 201 | 292 | |
| ... | ... | @@ -206,11 +297,19 @@ describe Gitlab::API do |
| 206 | 297 | expect { |
| 207 | 298 | delete api("/user/keys/#{key.id}", user) |
| 208 | 299 | }.to change{user.keys.count}.by(-1) |
| 300 | + response.status.should == 200 | |
| 209 | 301 | end |
| 210 | 302 | |
| 211 | - it "should return 404 Not Found within invalid ID" do | |
| 303 | + it "should return sucess if key ID not found" do | |
| 212 | 304 | delete api("/user/keys/42", user) |
| 213 | - response.status.should == 404 | |
| 305 | + response.status.should == 200 | |
| 306 | + end | |
| 307 | + | |
| 308 | + it "should return 401 error if unauthorized" do | |
| 309 | + user.keys << key | |
| 310 | + user.save | |
| 311 | + delete api("/user/keys/#{key.id}") | |
| 312 | + response.status.should == 401 | |
| 214 | 313 | end |
| 215 | 314 | end |
| 216 | 315 | end | ... | ... |