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,7 +91,7 @@ class MergeRequest < ActiveRecord::Base | ||
91 | 91 | ||
92 | def validate_branches | 92 | def validate_branches |
93 | if target_branch == source_branch | 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 | end | 95 | end |
96 | end | 96 | end |
97 | 97 |
app/models/project.rb
@@ -160,7 +160,7 @@ class Project < ActiveRecord::Base | @@ -160,7 +160,7 @@ class Project < ActiveRecord::Base | ||
160 | 160 | ||
161 | def check_limit | 161 | def check_limit |
162 | unless creator.can_create_project? | 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 | end | 164 | end |
165 | rescue | 165 | rescue |
166 | errors[:base] << ("Can't check your ability to create project") | 166 | errors[:base] << ("Can't check your ability to create project") |
config/routes.rb
@@ -8,6 +8,7 @@ Gitlab::Application.routes.draw do | @@ -8,6 +8,7 @@ Gitlab::Application.routes.draw do | ||
8 | 8 | ||
9 | # API | 9 | # API |
10 | require 'api' | 10 | require 'api' |
11 | + Gitlab::API.logger Rails.logger | ||
11 | mount Gitlab::API => '/api' | 12 | mount Gitlab::API => '/api' |
12 | 13 | ||
13 | constraint = lambda { |request| request.env["warden"].authenticate? and request.env['warden'].user.admin? } | 14 | constraint = lambda { |request| request.env["warden"].authenticate? and request.env['warden'].user.admin? } |
doc/api/README.md
1 | # GitLab API | 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 | If no, or an invalid, `private_token` is provided then an error message will be returned with status code 401: | 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,8 +18,48 @@ Example of a valid API request: | ||
18 | GET http://example.com/api/v3/projects?private_token=QVy1PB7sTxfy4pqfZM1U | 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 | The API uses JSON to serialize data. You don't need to specify `.json` at the end of API URL. | 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 | #### Pagination | 63 | #### Pagination |
24 | 64 | ||
25 | When listing resources you can pass the following parameters: | 65 | When listing resources you can pass the following parameters: |
doc/api/groups.md
@@ -17,7 +17,8 @@ GET /groups | @@ -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 | Get all details of a group. | 23 | Get all details of a group. |
23 | 24 | ||
@@ -29,19 +30,19 @@ Parameters: | @@ -29,19 +30,19 @@ Parameters: | ||
29 | 30 | ||
30 | + `id` (required) - The ID of a group | 31 | + `id` (required) - The ID of a group |
31 | 32 | ||
33 | + | ||
32 | ## New group | 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 | POST /groups | 39 | POST /groups |
38 | ``` | 40 | ``` |
39 | 41 | ||
40 | Parameters: | 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 | ## Transfer project to group | 47 | ## Transfer project to group |
47 | 48 |
doc/api/issues.md
1 | ## List issues | 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 | GET /issues | 7 | GET /issues |
@@ -68,9 +69,11 @@ GET /issues | @@ -68,9 +69,11 @@ GET /issues | ||
68 | ] | 69 | ] |
69 | ``` | 70 | ``` |
70 | 71 | ||
72 | + | ||
71 | ## List project issues | 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 | GET /projects/:id/issues | 79 | GET /projects/:id/issues |
@@ -80,9 +83,10 @@ Parameters: | @@ -80,9 +83,10 @@ Parameters: | ||
80 | 83 | ||
81 | + `id` (required) - The ID of a project | 84 | + `id` (required) - The ID of a project |
82 | 85 | ||
86 | + | ||
83 | ## Single issue | 87 | ## Single issue |
84 | 88 | ||
85 | -Get a project issue. | 89 | +Gets a single project issue. |
86 | 90 | ||
87 | ``` | 91 | ``` |
88 | GET /projects/:id/issues/:issue_id | 92 | GET /projects/:id/issues/:issue_id |
@@ -133,9 +137,10 @@ Parameters: | @@ -133,9 +137,10 @@ Parameters: | ||
133 | } | 137 | } |
134 | ``` | 138 | ``` |
135 | 139 | ||
140 | + | ||
136 | ## New issue | 141 | ## New issue |
137 | 142 | ||
138 | -Create a new project issue. | 143 | +Creates a new project issue. |
139 | 144 | ||
140 | ``` | 145 | ``` |
141 | POST /projects/:id/issues | 146 | POST /projects/:id/issues |
@@ -150,11 +155,10 @@ Parameters: | @@ -150,11 +155,10 @@ Parameters: | ||
150 | + `milestone_id` (optional) - The ID of a milestone to assign issue | 155 | + `milestone_id` (optional) - The ID of a milestone to assign issue |
151 | + `labels` (optional) - Comma-separated label names for an issue | 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 | ## Edit issue | 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 | PUT /projects/:id/issues/:issue_id | 164 | PUT /projects/:id/issues/:issue_id |
@@ -171,5 +175,19 @@ Parameters: | @@ -171,5 +175,19 @@ Parameters: | ||
171 | + `labels` (optional) - Comma-separated label names for an issue | 175 | + `labels` (optional) - Comma-separated label names for an issue |
172 | + `closed` (optional) - The state of an issue (0 = false, 1 = true) | 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 | ## List merge requests | 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 | GET /projects/:id/merge_requests | 7 | GET /projects/:id/merge_requests |
@@ -40,9 +41,10 @@ Parameters: | @@ -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 | GET /projects/:id/merge_request/:merge_request_id | 50 | GET /projects/:id/merge_request/:merge_request_id |
@@ -84,7 +86,7 @@ Parameters: | @@ -84,7 +86,7 @@ Parameters: | ||
84 | 86 | ||
85 | ## Create MR | 87 | ## Create MR |
86 | 88 | ||
87 | -Create MR. | 89 | +Creates a new merge request. |
88 | 90 | ||
89 | ``` | 91 | ``` |
90 | POST /projects/:id/merge_requests | 92 | POST /projects/:id/merge_requests |
@@ -126,9 +128,10 @@ Parameters: | @@ -126,9 +128,10 @@ Parameters: | ||
126 | } | 128 | } |
127 | ``` | 129 | ``` |
128 | 130 | ||
131 | + | ||
129 | ## Update MR | 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 | PUT /projects/:id/merge_request/:merge_request_id | 137 | PUT /projects/:id/merge_request/:merge_request_id |
@@ -172,9 +175,11 @@ Parameters: | @@ -172,9 +175,11 @@ Parameters: | ||
172 | } | 175 | } |
173 | } | 176 | } |
174 | ``` | 177 | ``` |
178 | + | ||
179 | + | ||
175 | ## Post comment to MR | 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 | POST /projects/:id/merge_request/:merge_request_id/comments | 185 | POST /projects/:id/merge_request/:merge_request_id/comments |
@@ -183,10 +188,9 @@ POST /projects/:id/merge_request/:merge_request_id/comments | @@ -183,10 +188,9 @@ POST /projects/:id/merge_request/:merge_request_id/comments | ||
183 | Parameters: | 188 | Parameters: |
184 | 189 | ||
185 | + `id` (required) - The ID of a project | 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 | + `note` (required) - Text of comment | 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 | ```json | 195 | ```json |
192 | { | 196 | { |
doc/api/milestones.md
1 | ## List project milestones | 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 | GET /projects/:id/milestones | 6 | GET /projects/:id/milestones |
@@ -10,9 +10,10 @@ Parameters: | @@ -10,9 +10,10 @@ Parameters: | ||
10 | 10 | ||
11 | + `id` (required) - The ID of a project | 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 | GET /projects/:id/milestones/:milestone_id | 19 | GET /projects/:id/milestones/:milestone_id |
@@ -23,9 +24,10 @@ Parameters: | @@ -23,9 +24,10 @@ Parameters: | ||
23 | + `id` (required) - The ID of a project | 24 | + `id` (required) - The ID of a project |
24 | + `milestone_id` (required) - The ID of a project milestone | 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 | POST /projects/:id/milestones | 33 | POST /projects/:id/milestones |
@@ -38,9 +40,10 @@ Parameters: | @@ -38,9 +40,10 @@ Parameters: | ||
38 | + `description` (optional) - The description of the milestone | 40 | + `description` (optional) - The description of the milestone |
39 | + `due_date` (optional) - The due date of the milestone | 41 | + `due_date` (optional) - The due date of the milestone |
40 | 42 | ||
43 | + | ||
41 | ## Edit milestone | 44 | ## Edit milestone |
42 | 45 | ||
43 | -Update an existing project milestone. | 46 | +Updates an existing project milestone. |
44 | 47 | ||
45 | ``` | 48 | ``` |
46 | PUT /projects/:id/milestones/:milestone_id | 49 | PUT /projects/:id/milestones/:milestone_id |
@@ -54,3 +57,4 @@ Parameters: | @@ -54,3 +57,4 @@ Parameters: | ||
54 | + `description` (optional) - The description of a milestone | 57 | + `description` (optional) - The description of a milestone |
55 | + `due_date` (optional) - The due date of the milestone | 58 | + `due_date` (optional) - The due date of the milestone |
56 | + `closed` (optional) - The status of the milestone | 59 | + `closed` (optional) - The status of the milestone |
60 | + |
doc/api/notes.md
1 | -## List notes | 1 | +## Wall |
2 | 2 | ||
3 | ### List project wall notes | 3 | ### List project wall notes |
4 | 4 | ||
@@ -30,22 +30,40 @@ Parameters: | @@ -30,22 +30,40 @@ Parameters: | ||
30 | 30 | ||
31 | + `id` (required) - The ID of a project | 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 | Parameters: | 42 | Parameters: |
42 | 43 | ||
43 | + `id` (required) - The ID of a project | 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 | GET /projects/:id/issues/:issue_id/notes | 69 | GET /projects/:id/issues/:issue_id/notes |
@@ -56,54 +74,59 @@ Parameters: | @@ -56,54 +74,59 @@ Parameters: | ||
56 | + `id` (required) - The ID of a project | 74 | + `id` (required) - The ID of a project |
57 | + `issue_id` (required) - The ID of an issue | 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 | Parameters: | 86 | Parameters: |
68 | 87 | ||
69 | + `id` (required) - The ID of a project | 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 | Parameters: | 101 | Parameters: |
83 | 102 | ||
84 | + `id` (required) - The ID of a project | 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 | Parameters: | 118 | Parameters: |
96 | 119 | ||
97 | + `id` (required) - The ID of a project | 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 | Parameters: | 132 | Parameters: |
@@ -112,52 +135,64 @@ Parameters: | @@ -112,52 +135,64 @@ Parameters: | ||
112 | + `snippet_id` (required) - The ID of a project snippet | 135 | + `snippet_id` (required) - The ID of a project snippet |
113 | + `note_id` (required) - The ID of an snippet note | 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 | Parameters: | 147 | Parameters: |
126 | 148 | ||
127 | + `id` (required) - The ID of a project | 149 | + `id` (required) - The ID of a project |
150 | ++ `snippet_id` (required) - The ID of an snippet | ||
128 | + `body` (required) - The content of a note | 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 | Parameters: | 164 | Parameters: |
142 | 165 | ||
143 | + `id` (required) - The ID of a project | 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 | Parameters: | 178 | Parameters: |
158 | 179 | ||
159 | + `id` (required) - The ID of a project | 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 | + `body` (required) - The content of a note | 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 | Get a list of projects owned by the authenticated user. | 5 | Get a list of projects owned by the authenticated user. |
4 | 6 | ||
@@ -55,9 +57,11 @@ GET /projects | @@ -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 | GET /projects/:id | 67 | GET /projects/:id |
@@ -65,7 +69,7 @@ GET /projects/:id | @@ -65,7 +69,7 @@ GET /projects/:id | ||
65 | 69 | ||
66 | Parameters: | 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 | ```json | 74 | ```json |
71 | { | 75 | { |
@@ -92,9 +96,10 @@ Parameters: | @@ -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 | POST /projects | 105 | POST /projects |
@@ -110,12 +115,21 @@ Parameters: | @@ -110,12 +115,21 @@ Parameters: | ||
110 | + `merge_requests_enabled` (optional) - enabled by default | 115 | + `merge_requests_enabled` (optional) - enabled by default |
111 | + `wiki_enabled` (optional) - enabled by default | 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 | POST /projects/user/:user_id | 135 | POST /projects/user/:user_id |
@@ -132,10 +146,11 @@ Parameters: | @@ -132,10 +146,11 @@ Parameters: | ||
132 | + `merge_requests_enabled` (optional) - enabled by default | 146 | + `merge_requests_enabled` (optional) - enabled by default |
133 | + `wiki_enabled` (optional) - enabled by default | 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 | Get a list of project team members. | 155 | Get a list of project team members. |
141 | 156 | ||
@@ -145,12 +160,13 @@ GET /projects/:id/members | @@ -145,12 +160,13 @@ GET /projects/:id/members | ||
145 | 160 | ||
146 | Parameters: | 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 | GET /projects/:id/members/:user_id | 172 | GET /projects/:id/members/:user_id |
@@ -158,12 +174,11 @@ GET /projects/:id/members/:user_id | @@ -158,12 +174,11 @@ GET /projects/:id/members/:user_id | ||
158 | 174 | ||
159 | Parameters: | 175 | Parameters: |
160 | 176 | ||
161 | -+ `id` (required) - The ID of a project | 177 | ++ `id` (required) - The ID or NAME of a project |
162 | + `user_id` (required) - The ID of a user | 178 | + `user_id` (required) - The ID of a user |
163 | 179 | ||
164 | ```json | 180 | ```json |
165 | { | 181 | { |
166 | - | ||
167 | "id": 1, | 182 | "id": 1, |
168 | "username": "john_smith", | 183 | "username": "john_smith", |
169 | "email": "john@example.com", | 184 | "email": "john@example.com", |
@@ -174,9 +189,12 @@ Parameters: | @@ -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 | POST /projects/:id/members | 200 | POST /projects/:id/members |
@@ -184,15 +202,14 @@ POST /projects/:id/members | @@ -184,15 +202,14 @@ POST /projects/:id/members | ||
184 | 202 | ||
185 | Parameters: | 203 | Parameters: |
186 | 204 | ||
187 | -+ `id` (required) - The ID of a project | 205 | ++ `id` (required) - The ID or NAME of a project |
188 | + `user_id` (required) - The ID of a user to add | 206 | + `user_id` (required) - The ID of a user to add |
189 | + `access_level` (required) - Project access level | 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 | PUT /projects/:id/members/:user_id | 215 | PUT /projects/:id/members/:user_id |
@@ -200,13 +217,12 @@ PUT /projects/:id/members/:user_id | @@ -200,13 +217,12 @@ PUT /projects/:id/members/:user_id | ||
200 | 217 | ||
201 | Parameters: | 218 | Parameters: |
202 | 219 | ||
203 | -+ `id` (required) - The ID of a project | 220 | ++ `id` (required) - The ID or NAME of a project |
204 | + `user_id` (required) - The ID of a team member | 221 | + `user_id` (required) - The ID of a team member |
205 | + `access_level` (required) - Project access level | 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 | Removes user from project team. | 227 | Removes user from project team. |
212 | 228 | ||
@@ -216,14 +232,20 @@ DELETE /projects/:id/members/:user_id | @@ -216,14 +232,20 @@ DELETE /projects/:id/members/:user_id | ||
216 | 232 | ||
217 | Parameters: | 233 | Parameters: |
218 | 234 | ||
219 | -+ `id` (required) - The ID of a project | 235 | ++ `id` (required) - The ID or NAME of a project |
220 | + `user_id` (required) - The ID of a team member | 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 | GET /projects/:id/hooks | 251 | GET /projects/:id/hooks |
@@ -231,13 +253,12 @@ GET /projects/:id/hooks | @@ -231,13 +253,12 @@ GET /projects/:id/hooks | ||
231 | 253 | ||
232 | Parameters: | 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 | GET /projects/:id/hooks/:hook_id | 264 | GET /projects/:id/hooks/:hook_id |
@@ -245,14 +266,21 @@ GET /projects/:id/hooks/:hook_id | @@ -245,14 +266,21 @@ GET /projects/:id/hooks/:hook_id | ||
245 | 266 | ||
246 | Parameters: | 267 | Parameters: |
247 | 268 | ||
248 | -+ `id` (required) - The ID of a project | 269 | ++ `id` (required) - The ID or NAME of a project |
249 | + `hook_id` (required) - The ID of a project hook | 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 | POST /projects/:id/hooks | 286 | POST /projects/:id/hooks |
@@ -260,14 +288,13 @@ POST /projects/:id/hooks | @@ -260,14 +288,13 @@ POST /projects/:id/hooks | ||
260 | 288 | ||
261 | Parameters: | 289 | Parameters: |
262 | 290 | ||
263 | -+ `id` (required) - The ID of a project | 291 | ++ `id` (required) - The ID or NAME of a project |
264 | + `url` (required) - The hook URL | 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 | PUT /projects/:id/hooks/:hook_id | 300 | PUT /projects/:id/hooks/:hook_id |
@@ -275,30 +302,125 @@ PUT /projects/:id/hooks/:hook_id | @@ -275,30 +302,125 @@ PUT /projects/:id/hooks/:hook_id | ||
275 | 302 | ||
276 | Parameters: | 303 | Parameters: |
277 | 304 | ||
278 | -+ `id` (required) - The ID of a project | 305 | ++ `id` (required) - The ID or NAME of a project |
279 | + `hook_id` (required) - The ID of a project hook | 306 | + `hook_id` (required) - The ID of a project hook |
280 | + `url` (required) - The hook URL | 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 | Parameters: | 319 | Parameters: |
294 | 320 | ||
295 | -+ `id` (required) - The ID of a project | 321 | ++ `id` (required) - The ID or NAME of a project |
296 | + `hook_id` (required) - The ID of hook to delete | 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 | Get a list of a project's deploy keys. | 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,6 +428,10 @@ Get a list of a project's deploy keys. | ||
306 | GET /projects/:id/keys | 428 | GET /projects/:id/keys |
307 | ``` | 429 | ``` |
308 | 430 | ||
431 | +Parameters: | ||
432 | + | ||
433 | ++ `id` (required) - The ID of the project | ||
434 | + | ||
309 | ```json | 435 | ```json |
310 | [ | 436 | [ |
311 | { | 437 | { |
@@ -325,7 +451,8 @@ GET /projects/:id/keys | @@ -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 | Get a single key. | 457 | Get a single key. |
331 | 458 | ||
@@ -335,7 +462,8 @@ GET /projects/:id/keys/:key_id | @@ -335,7 +462,8 @@ GET /projects/:id/keys/:key_id | ||
335 | 462 | ||
336 | Parameters: | 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 | ```json | 468 | ```json |
341 | { | 469 | { |
@@ -346,9 +474,11 @@ Parameters: | @@ -346,9 +474,11 @@ Parameters: | ||
346 | soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=" | 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 | POST /projects/:id/keys | 484 | POST /projects/:id/keys |
@@ -356,13 +486,12 @@ POST /projects/:id/keys | @@ -356,13 +486,12 @@ POST /projects/:id/keys | ||
356 | 486 | ||
357 | Parameters: | 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 | Delete a deploy key from a project | 496 | Delete a deploy key from a project |
368 | 497 | ||
@@ -372,6 +501,6 @@ DELETE /projects/:id/keys/:key_id | @@ -372,6 +501,6 @@ DELETE /projects/:id/keys/:key_id | ||
372 | 501 | ||
373 | Parameters: | 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 | Get a list of repository branches from a project, sorted by name alphabetically. | 3 | Get a list of repository branches from a project, sorted by name alphabetically. |
4 | 4 | ||
@@ -39,7 +39,8 @@ Parameters: | @@ -39,7 +39,8 @@ Parameters: | ||
39 | ] | 39 | ] |
40 | ``` | 40 | ``` |
41 | 41 | ||
42 | -## Project repository branch | 42 | + |
43 | +## Get single repository branch | ||
43 | 44 | ||
44 | Get a single project repository branch. | 45 | Get a single project repository branch. |
45 | 46 | ||
@@ -79,12 +80,11 @@ Parameters: | @@ -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 | PUT /projects/:id/repository/branches/:branch/protect | 90 | PUT /projects/:id/repository/branches/:branch/protect |
@@ -122,9 +122,11 @@ Parameters: | @@ -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 | PUT /projects/:id/repository/branches/:branch/unprotect | 132 | PUT /projects/:id/repository/branches/:branch/unprotect |
@@ -162,7 +164,8 @@ Parameters: | @@ -162,7 +164,8 @@ Parameters: | ||
162 | } | 164 | } |
163 | ``` | 165 | ``` |
164 | 166 | ||
165 | -## Project repository tags | 167 | + |
168 | +## List project repository tags | ||
166 | 169 | ||
167 | Get a list of repository tags from a project, sorted by name in reverse alphabetical order. | 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,7 +204,8 @@ Parameters: | ||
201 | ] | 204 | ] |
202 | ``` | 205 | ``` |
203 | 206 | ||
204 | -## Project repository commits | 207 | + |
208 | +## List repository commits | ||
205 | 209 | ||
206 | Get a list of repository commits in a project. | 210 | Get a list of repository commits in a project. |
207 | 211 | ||
@@ -212,7 +216,7 @@ GET /projects/:id/repository/commits | @@ -212,7 +216,7 @@ GET /projects/:id/repository/commits | ||
212 | Parameters: | 216 | Parameters: |
213 | 217 | ||
214 | + `id` (required) - The ID of a project | 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 | ```json | 221 | ```json |
218 | [ | 222 | [ |
@@ -235,6 +239,7 @@ Parameters: | @@ -235,6 +239,7 @@ Parameters: | ||
235 | ] | 239 | ] |
236 | ``` | 240 | ``` |
237 | 241 | ||
242 | + | ||
238 | ## Raw blob content | 243 | ## Raw blob content |
239 | 244 | ||
240 | Get the raw file contents for a file. | 245 | Get the raw file contents for a file. |
@@ -248,5 +253,3 @@ Parameters: | @@ -248,5 +253,3 @@ Parameters: | ||
248 | + `id` (required) - The ID of a project | 253 | + `id` (required) - The ID of a project |
249 | + `sha` (required) - The commit or branch name | 254 | + `sha` (required) - The commit or branch name |
250 | + `filepath` (required) - The path the file | 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,9 +10,10 @@ Parameters: | ||
10 | 10 | ||
11 | + `id` (required) - The ID of a project | 11 | + `id` (required) - The ID of a project |
12 | 12 | ||
13 | + | ||
13 | ## Single snippet | 14 | ## Single snippet |
14 | 15 | ||
15 | -Get a project snippet. | 16 | +Get a single project snippet. |
16 | 17 | ||
17 | ``` | 18 | ``` |
18 | GET /projects/:id/snippets/:snippet_id | 19 | GET /projects/:id/snippets/:snippet_id |
@@ -42,22 +43,10 @@ Parameters: | @@ -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 | POST /projects/:id/snippets | 52 | POST /projects/:id/snippets |
@@ -71,11 +60,10 @@ Parameters: | @@ -71,11 +60,10 @@ Parameters: | ||
71 | + `lifetime` (optional) - The expiration date of a snippet | 60 | + `lifetime` (optional) - The expiration date of a snippet |
72 | + `code` (required) - The content of a snippet | 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 | PUT /projects/:id/snippets/:snippet_id | 69 | PUT /projects/:id/snippets/:snippet_id |
@@ -90,11 +78,11 @@ Parameters: | @@ -90,11 +78,11 @@ Parameters: | ||
90 | + `lifetime` (optional) - The expiration date of a snippet | 78 | + `lifetime` (optional) - The expiration date of a snippet |
91 | + `code` (optional) - The content of a snippet | 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 | ## Delete snippet | 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 | DELETE /projects/:id/snippets/:snippet_id | 88 | DELETE /projects/:id/snippets/:snippet_id |
@@ -105,5 +93,16 @@ Parameters: | @@ -105,5 +93,16 @@ Parameters: | ||
105 | + `id` (required) - The ID of a project | 93 | + `id` (required) - The ID of a project |
106 | + `snippet_id` (required) - The ID of a project's snippet | 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,6 +43,7 @@ GET /users | ||
43 | ] | 43 | ] |
44 | ``` | 44 | ``` |
45 | 45 | ||
46 | + | ||
46 | ## Single user | 47 | ## Single user |
47 | 48 | ||
48 | Get a single user. | 49 | Get a single user. |
@@ -74,37 +75,40 @@ Parameters: | @@ -74,37 +75,40 @@ Parameters: | ||
74 | } | 75 | } |
75 | ``` | 76 | ``` |
76 | 77 | ||
78 | + | ||
77 | ## User creation | 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 | POST /users | 84 | POST /users |
82 | ``` | 85 | ``` |
83 | 86 | ||
84 | Parameters: | 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 | ## User modification | 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 | PUT /users/:id | 107 | PUT /users/:id |
105 | ``` | 108 | ``` |
106 | 109 | ||
107 | Parameters: | 110 | Parameters: |
111 | + | ||
108 | + `email` - Email | 112 | + `email` - Email |
109 | + `username` - Username | 113 | + `username` - Username |
110 | + `name` - Name | 114 | + `name` - Name |
@@ -117,23 +121,28 @@ Parameters: | @@ -117,23 +121,28 @@ Parameters: | ||
117 | + `provider` - External provider name | 121 | + `provider` - External provider name |
118 | + `bio` - User's bio | 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 | ## User deletion | 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 | DELETE /users/:id | 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 | ## Current user | 143 | ## Current user |
135 | 144 | ||
136 | -Get currently authenticated user. | 145 | +Gets currently authenticated user. |
137 | 146 | ||
138 | ``` | 147 | ``` |
139 | GET /user | 148 | GET /user |
@@ -156,6 +165,7 @@ GET /user | @@ -156,6 +165,7 @@ GET /user | ||
156 | } | 165 | } |
157 | ``` | 166 | ``` |
158 | 167 | ||
168 | + | ||
159 | ## List SSH keys | 169 | ## List SSH keys |
160 | 170 | ||
161 | Get a list of currently authenticated user's SSH keys. | 171 | Get a list of currently authenticated user's SSH keys. |
@@ -183,6 +193,11 @@ GET /user/keys | @@ -183,6 +193,11 @@ GET /user/keys | ||
183 | ] | 193 | ] |
184 | ``` | 194 | ``` |
185 | 195 | ||
196 | +Parameters: | ||
197 | + | ||
198 | ++ **none** | ||
199 | + | ||
200 | + | ||
186 | ## Single SSH key | 201 | ## Single SSH key |
187 | 202 | ||
188 | Get a single key. | 203 | Get a single key. |
@@ -204,9 +219,11 @@ Parameters: | @@ -204,9 +219,11 @@ Parameters: | ||
204 | soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=" | 219 | soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=" |
205 | } | 220 | } |
206 | ``` | 221 | ``` |
222 | + | ||
223 | + | ||
207 | ## Add SSH key | 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 | POST /user/keys | 229 | POST /user/keys |
@@ -217,8 +234,6 @@ Parameters: | @@ -217,8 +234,6 @@ Parameters: | ||
217 | + `title` (required) - new SSH Key's title | 234 | + `title` (required) - new SSH Key's title |
218 | + `key` (required) - new SSH key | 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 | ## Add SSH key for user | 238 | ## Add SSH key for user |
224 | 239 | ||
@@ -239,7 +254,8 @@ found` on fail. | @@ -239,7 +254,8 @@ found` on fail. | ||
239 | 254 | ||
240 | ## Delete SSH key | 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 | DELETE /user/keys/:id | 261 | DELETE /user/keys/:id |
@@ -249,4 +265,3 @@ Parameters: | @@ -249,4 +265,3 @@ Parameters: | ||
249 | 265 | ||
250 | + `id` (required) - SSH key ID | 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,6 +8,19 @@ module Gitlab | ||
8 | rack_response({'message' => '404 Not found'}.to_json, 404) | 8 | rack_response({'message' => '404 Not found'}.to_json, 404) |
9 | end | 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 | format :json | 24 | format :json |
12 | helpers APIHelpers | 25 | helpers APIHelpers |
13 | 26 |
lib/api/groups.rb
@@ -20,12 +20,14 @@ module Gitlab | @@ -20,12 +20,14 @@ module Gitlab | ||
20 | # Create group. Available only for admin | 20 | # Create group. Available only for admin |
21 | # | 21 | # |
22 | # Parameters: | 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 | # Example Request: | 25 | # Example Request: |
26 | # POST /groups | 26 | # POST /groups |
27 | post do | 27 | post do |
28 | authenticated_as_admin! | 28 | authenticated_as_admin! |
29 | + required_attributes! [:name, :path] | ||
30 | + | ||
29 | attrs = attributes_for_keys [:name, :path] | 31 | attrs = attributes_for_keys [:name, :path] |
30 | @group = Group.new(attrs) | 32 | @group = Group.new(attrs) |
31 | @group.owner = current_user | 33 | @group.owner = current_user |
lib/api/helpers.rb
@@ -41,6 +41,17 @@ module Gitlab | @@ -41,6 +41,17 @@ module Gitlab | ||
41 | abilities.allowed?(object, action, subject) | 41 | abilities.allowed?(object, action, subject) |
42 | end | 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 | def attributes_for_keys(keys) | 55 | def attributes_for_keys(keys) |
45 | attrs = {} | 56 | attrs = {} |
46 | keys.each do |key| | 57 | keys.each do |key| |
@@ -55,6 +66,12 @@ module Gitlab | @@ -55,6 +66,12 @@ module Gitlab | ||
55 | render_api_error!('403 Forbidden', 403) | 66 | render_api_error!('403 Forbidden', 403) |
56 | end | 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 | def not_found!(resource = nil) | 75 | def not_found!(resource = nil) |
59 | message = ["404"] | 76 | message = ["404"] |
60 | message << resource if resource | 77 | message << resource if resource |
lib/api/issues.rb
@@ -48,6 +48,7 @@ module Gitlab | @@ -48,6 +48,7 @@ module Gitlab | ||
48 | # Example Request: | 48 | # Example Request: |
49 | # POST /projects/:id/issues | 49 | # POST /projects/:id/issues |
50 | post ":id/issues" do | 50 | post ":id/issues" do |
51 | + required_attributes! [:title] | ||
51 | attrs = attributes_for_keys [:title, :description, :assignee_id, :milestone_id] | 52 | attrs = attributes_for_keys [:title, :description, :assignee_id, :milestone_id] |
52 | attrs[:label_list] = params[:labels] if params[:labels].present? | 53 | attrs[:label_list] = params[:labels] if params[:labels].present? |
53 | @issue = user_project.issues.new attrs | 54 | @issue = user_project.issues.new attrs |
lib/api/merge_requests.rb
@@ -4,6 +4,16 @@ module Gitlab | @@ -4,6 +4,16 @@ module Gitlab | ||
4 | before { authenticate! } | 4 | before { authenticate! } |
5 | 5 | ||
6 | resource :projects do | 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 | # List merge requests | 18 | # List merge requests |
9 | # | 19 | # |
@@ -51,6 +61,7 @@ module Gitlab | @@ -51,6 +61,7 @@ module Gitlab | ||
51 | # | 61 | # |
52 | post ":id/merge_requests" do | 62 | post ":id/merge_requests" do |
53 | authorize! :write_merge_request, user_project | 63 | authorize! :write_merge_request, user_project |
64 | + required_attributes! [:source_branch, :target_branch, :title] | ||
54 | 65 | ||
55 | attrs = attributes_for_keys [:source_branch, :target_branch, :assignee_id, :title] | 66 | attrs = attributes_for_keys [:source_branch, :target_branch, :assignee_id, :title] |
56 | merge_request = user_project.merge_requests.new(attrs) | 67 | merge_request = user_project.merge_requests.new(attrs) |
@@ -60,7 +71,7 @@ module Gitlab | @@ -60,7 +71,7 @@ module Gitlab | ||
60 | merge_request.reload_code | 71 | merge_request.reload_code |
61 | present merge_request, with: Entities::MergeRequest | 72 | present merge_request, with: Entities::MergeRequest |
62 | else | 73 | else |
63 | - not_found! | 74 | + handle_merge_request_errors! merge_request.errors |
64 | end | 75 | end |
65 | end | 76 | end |
66 | 77 | ||
@@ -88,7 +99,7 @@ module Gitlab | @@ -88,7 +99,7 @@ module Gitlab | ||
88 | merge_request.mark_as_unchecked | 99 | merge_request.mark_as_unchecked |
89 | present merge_request, with: Entities::MergeRequest | 100 | present merge_request, with: Entities::MergeRequest |
90 | else | 101 | else |
91 | - not_found! | 102 | + handle_merge_request_errors! merge_request.errors |
92 | end | 103 | end |
93 | end | 104 | end |
94 | 105 | ||
@@ -102,6 +113,8 @@ module Gitlab | @@ -102,6 +113,8 @@ module Gitlab | ||
102 | # POST /projects/:id/merge_request/:merge_request_id/comments | 113 | # POST /projects/:id/merge_request/:merge_request_id/comments |
103 | # | 114 | # |
104 | post ":id/merge_request/:merge_request_id/comments" do | 115 | post ":id/merge_request/:merge_request_id/comments" do |
116 | + required_attributes! [:note] | ||
117 | + | ||
105 | merge_request = user_project.merge_requests.find(params[:merge_request_id]) | 118 | merge_request = user_project.merge_requests.find(params[:merge_request_id]) |
106 | note = merge_request.notes.new(note: params[:note], project_id: user_project.id) | 119 | note = merge_request.notes.new(note: params[:note], project_id: user_project.id) |
107 | note.author = current_user | 120 | note.author = current_user |
lib/api/milestones.rb
@@ -41,6 +41,7 @@ module Gitlab | @@ -41,6 +41,7 @@ module Gitlab | ||
41 | # POST /projects/:id/milestones | 41 | # POST /projects/:id/milestones |
42 | post ":id/milestones" do | 42 | post ":id/milestones" do |
43 | authorize! :admin_milestone, user_project | 43 | authorize! :admin_milestone, user_project |
44 | + required_attributes! [:title] | ||
44 | 45 | ||
45 | attrs = attributes_for_keys [:title, :description, :due_date] | 46 | attrs = attributes_for_keys [:title, :description, :due_date] |
46 | @milestone = user_project.milestones.new attrs | 47 | @milestone = user_project.milestones.new attrs |
lib/api/notes.rb
@@ -37,12 +37,16 @@ module Gitlab | @@ -37,12 +37,16 @@ module Gitlab | ||
37 | # Example Request: | 37 | # Example Request: |
38 | # POST /projects/:id/notes | 38 | # POST /projects/:id/notes |
39 | post ":id/notes" do | 39 | post ":id/notes" do |
40 | + required_attributes! [:body] | ||
41 | + | ||
40 | @note = user_project.notes.new(note: params[:body]) | 42 | @note = user_project.notes.new(note: params[:body]) |
41 | @note.author = current_user | 43 | @note.author = current_user |
42 | 44 | ||
43 | if @note.save | 45 | if @note.save |
44 | present @note, with: Entities::Note | 46 | present @note, with: Entities::Note |
45 | else | 47 | else |
48 | + # :note is exposed as :body, but :note is set on error | ||
49 | + bad_request!(:note) if @note.errors[:note].any? | ||
46 | not_found! | 50 | not_found! |
47 | end | 51 | end |
48 | end | 52 | end |
@@ -89,6 +93,8 @@ module Gitlab | @@ -89,6 +93,8 @@ module Gitlab | ||
89 | # POST /projects/:id/issues/:noteable_id/notes | 93 | # POST /projects/:id/issues/:noteable_id/notes |
90 | # POST /projects/:id/snippets/:noteable_id/notes | 94 | # POST /projects/:id/snippets/:noteable_id/notes |
91 | post ":id/#{noteables_str}/:#{noteable_id_str}/notes" do | 95 | post ":id/#{noteables_str}/:#{noteable_id_str}/notes" do |
96 | + required_attributes! [:body] | ||
97 | + | ||
92 | @noteable = user_project.send(:"#{noteables_str}").find(params[:"#{noteable_id_str}"]) | 98 | @noteable = user_project.send(:"#{noteables_str}").find(params[:"#{noteable_id_str}"]) |
93 | @note = @noteable.notes.new(note: params[:body]) | 99 | @note = @noteable.notes.new(note: params[:body]) |
94 | @note.author = current_user | 100 | @note.author = current_user |
lib/api/projects.rb
@@ -4,6 +4,15 @@ module Gitlab | @@ -4,6 +4,15 @@ module Gitlab | ||
4 | before { authenticate! } | 4 | before { authenticate! } |
5 | 5 | ||
6 | resource :projects do | 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 | # Get a projects list for authenticated user | 16 | # Get a projects list for authenticated user |
8 | # | 17 | # |
9 | # Example Request: | 18 | # Example Request: |
@@ -33,9 +42,11 @@ module Gitlab | @@ -33,9 +42,11 @@ module Gitlab | ||
33 | # wall_enabled (optional) - enabled by default | 42 | # wall_enabled (optional) - enabled by default |
34 | # merge_requests_enabled (optional) - enabled by default | 43 | # merge_requests_enabled (optional) - enabled by default |
35 | # wiki_enabled (optional) - enabled by default | 44 | # wiki_enabled (optional) - enabled by default |
45 | + # namespace_id (optional) - defaults to user namespace | ||
36 | # Example Request | 46 | # Example Request |
37 | # POST /projects | 47 | # POST /projects |
38 | post do | 48 | post do |
49 | + required_attributes! [:name] | ||
39 | attrs = attributes_for_keys [:name, | 50 | attrs = attributes_for_keys [:name, |
40 | :description, | 51 | :description, |
41 | :default_branch, | 52 | :default_branch, |
@@ -48,6 +59,9 @@ module Gitlab | @@ -48,6 +59,9 @@ module Gitlab | ||
48 | if @project.saved? | 59 | if @project.saved? |
49 | present @project, with: Entities::Project | 60 | present @project, with: Entities::Project |
50 | else | 61 | else |
62 | + if @project.errors[:limit_reached].present? | ||
63 | + error!(@project.errors[:limit_reached], 403) | ||
64 | + end | ||
51 | not_found! | 65 | not_found! |
52 | end | 66 | end |
53 | end | 67 | end |
@@ -122,16 +136,22 @@ module Gitlab | @@ -122,16 +136,22 @@ module Gitlab | ||
122 | # POST /projects/:id/members | 136 | # POST /projects/:id/members |
123 | post ":id/members" do | 137 | post ":id/members" do |
124 | authorize! :admin_project, user_project | 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 | present @member, with: Entities::ProjectMember, project: user_project | 152 | present @member, with: Entities::ProjectMember, project: user_project |
133 | else | 153 | else |
134 | - not_found! | 154 | + handle_project_member_errors team_member.errors |
135 | end | 155 | end |
136 | end | 156 | end |
137 | 157 | ||
@@ -145,13 +165,16 @@ module Gitlab | @@ -145,13 +165,16 @@ module Gitlab | ||
145 | # PUT /projects/:id/members/:user_id | 165 | # PUT /projects/:id/members/:user_id |
146 | put ":id/members/:user_id" do | 166 | put ":id/members/:user_id" do |
147 | authorize! :admin_project, user_project | 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 | present @member, with: Entities::ProjectMember, project: user_project | 175 | present @member, with: Entities::ProjectMember, project: user_project |
153 | else | 176 | else |
154 | - not_found! | 177 | + handle_project_member_errors team_member.errors |
155 | end | 178 | end |
156 | end | 179 | end |
157 | 180 | ||
@@ -164,8 +187,12 @@ module Gitlab | @@ -164,8 +187,12 @@ module Gitlab | ||
164 | # DELETE /projects/:id/members/:user_id | 187 | # DELETE /projects/:id/members/:user_id |
165 | delete ":id/members/:user_id" do | 188 | delete ":id/members/:user_id" do |
166 | authorize! :admin_project, user_project | 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 | end | 196 | end |
170 | 197 | ||
171 | # Get project hooks | 198 | # Get project hooks |
@@ -203,11 +230,16 @@ module Gitlab | @@ -203,11 +230,16 @@ module Gitlab | ||
203 | # POST /projects/:id/hooks | 230 | # POST /projects/:id/hooks |
204 | post ":id/hooks" do | 231 | post ":id/hooks" do |
205 | authorize! :admin_project, user_project | 232 | authorize! :admin_project, user_project |
233 | + required_attributes! [:url] | ||
234 | + | ||
206 | @hook = user_project.hooks.new({"url" => params[:url]}) | 235 | @hook = user_project.hooks.new({"url" => params[:url]}) |
207 | if @hook.save | 236 | if @hook.save |
208 | present @hook, with: Entities::Hook | 237 | present @hook, with: Entities::Hook |
209 | else | 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 | end | 243 | end |
212 | end | 244 | end |
213 | 245 | ||
@@ -222,27 +254,36 @@ module Gitlab | @@ -222,27 +254,36 @@ module Gitlab | ||
222 | put ":id/hooks/:hook_id" do | 254 | put ":id/hooks/:hook_id" do |
223 | @hook = user_project.hooks.find(params[:hook_id]) | 255 | @hook = user_project.hooks.find(params[:hook_id]) |
224 | authorize! :admin_project, user_project | 256 | authorize! :admin_project, user_project |
257 | + required_attributes! [:url] | ||
225 | 258 | ||
226 | attrs = attributes_for_keys [:url] | 259 | attrs = attributes_for_keys [:url] |
227 | - | ||
228 | if @hook.update_attributes attrs | 260 | if @hook.update_attributes attrs |
229 | present @hook, with: Entities::Hook | 261 | present @hook, with: Entities::Hook |
230 | else | 262 | else |
263 | + if @hook.errors[:url].present? | ||
264 | + error!("Invalid url given", 422) | ||
265 | + end | ||
231 | not_found! | 266 | not_found! |
232 | end | 267 | end |
233 | end | 268 | end |
234 | 269 | ||
235 | - # Delete project hook | 270 | + # Deletes project hook. This is an idempotent function. |
236 | # | 271 | # |
237 | # Parameters: | 272 | # Parameters: |
238 | # id (required) - The ID of a project | 273 | # id (required) - The ID of a project |
239 | # hook_id (required) - The ID of hook to delete | 274 | # hook_id (required) - The ID of hook to delete |
240 | # Example Request: | 275 | # Example Request: |
241 | # DELETE /projects/:id/hooks/:hook_id | 276 | # DELETE /projects/:id/hooks/:hook_id |
242 | - delete ":id/hooks/:hook_id" do | 277 | + delete ":id/hooks" do |
243 | authorize! :admin_project, user_project | 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 | end | 287 | end |
247 | 288 | ||
248 | # Get a project repository branches | 289 | # Get a project repository branches |
@@ -277,6 +318,7 @@ module Gitlab | @@ -277,6 +318,7 @@ module Gitlab | ||
277 | # PUT /projects/:id/repository/branches/:branch/protect | 318 | # PUT /projects/:id/repository/branches/:branch/protect |
278 | put ":id/repository/branches/:branch/protect" do | 319 | put ":id/repository/branches/:branch/protect" do |
279 | @branch = user_project.repo.heads.find { |item| item.name == params[:branch] } | 320 | @branch = user_project.repo.heads.find { |item| item.name == params[:branch] } |
321 | + not_found! unless @branch | ||
280 | protected = user_project.protected_branches.find_by_name(@branch.name) | 322 | protected = user_project.protected_branches.find_by_name(@branch.name) |
281 | 323 | ||
282 | unless protected | 324 | unless protected |
@@ -295,6 +337,7 @@ module Gitlab | @@ -295,6 +337,7 @@ module Gitlab | ||
295 | # PUT /projects/:id/repository/branches/:branch/unprotect | 337 | # PUT /projects/:id/repository/branches/:branch/unprotect |
296 | put ":id/repository/branches/:branch/unprotect" do | 338 | put ":id/repository/branches/:branch/unprotect" do |
297 | @branch = user_project.repo.heads.find { |item| item.name == params[:branch] } | 339 | @branch = user_project.repo.heads.find { |item| item.name == params[:branch] } |
340 | + not_found! unless @branch | ||
298 | protected = user_project.protected_branches.find_by_name(@branch.name) | 341 | protected = user_project.protected_branches.find_by_name(@branch.name) |
299 | 342 | ||
300 | if protected | 343 | if protected |
@@ -318,7 +361,7 @@ module Gitlab | @@ -318,7 +361,7 @@ module Gitlab | ||
318 | # | 361 | # |
319 | # Parameters: | 362 | # Parameters: |
320 | # id (required) - The ID of a project | 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 | # Example Request: | 365 | # Example Request: |
323 | # GET /projects/:id/repository/commits | 366 | # GET /projects/:id/repository/commits |
324 | get ":id/repository/commits" do | 367 | get ":id/repository/commits" do |
@@ -366,6 +409,7 @@ module Gitlab | @@ -366,6 +409,7 @@ module Gitlab | ||
366 | # POST /projects/:id/snippets | 409 | # POST /projects/:id/snippets |
367 | post ":id/snippets" do | 410 | post ":id/snippets" do |
368 | authorize! :write_snippet, user_project | 411 | authorize! :write_snippet, user_project |
412 | + required_attributes! [:title, :file_name, :code] | ||
369 | 413 | ||
370 | attrs = attributes_for_keys [:title, :file_name] | 414 | attrs = attributes_for_keys [:title, :file_name] |
371 | attrs[:expires_at] = params[:lifetime] if params[:lifetime].present? | 415 | attrs[:expires_at] = params[:lifetime] if params[:lifetime].present? |
@@ -414,10 +458,12 @@ module Gitlab | @@ -414,10 +458,12 @@ module Gitlab | ||
414 | # Example Request: | 458 | # Example Request: |
415 | # DELETE /projects/:id/snippets/:snippet_id | 459 | # DELETE /projects/:id/snippets/:snippet_id |
416 | delete ":id/snippets/:snippet_id" do | 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 | end | 467 | end |
422 | 468 | ||
423 | # Get a raw project snippet | 469 | # Get a raw project snippet |
@@ -443,6 +489,7 @@ module Gitlab | @@ -443,6 +489,7 @@ module Gitlab | ||
443 | # GET /projects/:id/repository/commits/:sha/blob | 489 | # GET /projects/:id/repository/commits/:sha/blob |
444 | get ":id/repository/commits/:sha/blob" do | 490 | get ":id/repository/commits/:sha/blob" do |
445 | authorize! :download_code, user_project | 491 | authorize! :download_code, user_project |
492 | + required_attributes! [:filepath] | ||
446 | 493 | ||
447 | ref = params[:sha] | 494 | ref = params[:sha] |
448 | 495 |
lib/api/users.rb
@@ -41,6 +41,8 @@ module Gitlab | @@ -41,6 +41,8 @@ module Gitlab | ||
41 | # POST /users | 41 | # POST /users |
42 | post do | 42 | post do |
43 | authenticated_as_admin! | 43 | authenticated_as_admin! |
44 | + required_attributes! [:email, :password, :name, :username] | ||
45 | + | ||
44 | attrs = attributes_for_keys [:email, :name, :password, :skype, :linkedin, :twitter, :projects_limit, :username, :extern_uid, :provider, :bio] | 46 | attrs = attributes_for_keys [:email, :name, :password, :skype, :linkedin, :twitter, :projects_limit, :username, :extern_uid, :provider, :bio] |
45 | user = User.new attrs, as: :admin | 47 | user = User.new attrs, as: :admin |
46 | if user.save | 48 | if user.save |
@@ -67,10 +69,12 @@ module Gitlab | @@ -67,10 +69,12 @@ module Gitlab | ||
67 | # PUT /users/:id | 69 | # PUT /users/:id |
68 | put ":id" do | 70 | put ":id" do |
69 | authenticated_as_admin! | 71 | authenticated_as_admin! |
72 | + | ||
70 | attrs = attributes_for_keys [:email, :name, :password, :skype, :linkedin, :twitter, :projects_limit, :username, :extern_uid, :provider, :bio] | 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 | present user, with: Entities::User | 78 | present user, with: Entities::User |
75 | else | 79 | else |
76 | not_found! | 80 | not_found! |
@@ -147,6 +151,8 @@ module Gitlab | @@ -147,6 +151,8 @@ module Gitlab | ||
147 | # Example Request: | 151 | # Example Request: |
148 | # POST /user/keys | 152 | # POST /user/keys |
149 | post "keys" do | 153 | post "keys" do |
154 | + required_attributes! [:title, :key] | ||
155 | + | ||
150 | attrs = attributes_for_keys [:title, :key] | 156 | attrs = attributes_for_keys [:title, :key] |
151 | key = current_user.keys.new attrs | 157 | key = current_user.keys.new attrs |
152 | if key.save | 158 | if key.save |
@@ -156,15 +162,18 @@ module Gitlab | @@ -156,15 +162,18 @@ module Gitlab | ||
156 | end | 162 | end |
157 | end | 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 | # Parameters: | 167 | # Parameters: |
162 | # id (required) - SSH Key ID | 168 | # id (required) - SSH Key ID |
163 | # Example Request: | 169 | # Example Request: |
164 | # DELETE /user/keys/:id | 170 | # DELETE /user/keys/:id |
165 | delete "keys/:id" do | 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 | end | 177 | end |
169 | end | 178 | end |
170 | end | 179 | end |
spec/models/project_spec.rb
@@ -65,7 +65,7 @@ describe Project do | @@ -65,7 +65,7 @@ describe Project do | ||
65 | it "should not allow new projects beyond user limits" do | 65 | it "should not allow new projects beyond user limits" do |
66 | project.stub(:creator).and_return(double(can_create_project?: false, projects_limit: 1)) | 66 | project.stub(:creator).and_return(double(can_create_project?: false, projects_limit: 1)) |
67 | project.should_not be_valid | 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 | end | 69 | end |
70 | end | 70 | end |
71 | 71 |
spec/requests/api/groups_spec.rb
@@ -88,6 +88,16 @@ describe Gitlab::API do | @@ -88,6 +88,16 @@ describe Gitlab::API do | ||
88 | post api("/groups", admin), {:name => "Duplicate Test", :path => group2.path} | 88 | post api("/groups", admin), {:name => "Duplicate Test", :path => group2.path} |
89 | response.status.should == 404 | 89 | response.status.should == 404 |
90 | end | 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 | end | 101 | end |
92 | end | 102 | end |
93 | 103 |
spec/requests/api/issues_spec.rb
@@ -41,6 +41,11 @@ describe Gitlab::API do | @@ -41,6 +41,11 @@ describe Gitlab::API do | ||
41 | response.status.should == 200 | 41 | response.status.should == 200 |
42 | json_response['title'].should == issue.title | 42 | json_response['title'].should == issue.title |
43 | end | 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 | end | 49 | end |
45 | 50 | ||
46 | describe "POST /projects/:id/issues" do | 51 | describe "POST /projects/:id/issues" do |
@@ -52,6 +57,11 @@ describe Gitlab::API do | @@ -52,6 +57,11 @@ describe Gitlab::API do | ||
52 | json_response['description'].should be_nil | 57 | json_response['description'].should be_nil |
53 | json_response['labels'].should == ['label', 'label2'] | 58 | json_response['labels'].should == ['label', 'label2'] |
54 | end | 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 | end | 65 | end |
56 | 66 | ||
57 | describe "PUT /projects/:id/issues/:issue_id to update only title" do | 67 | describe "PUT /projects/:id/issues/:issue_id to update only title" do |
@@ -62,6 +72,12 @@ describe Gitlab::API do | @@ -62,6 +72,12 @@ describe Gitlab::API do | ||
62 | 72 | ||
63 | json_response['title'].should == 'updated title' | 73 | json_response['title'].should == 'updated title' |
64 | end | 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 | end | 81 | end |
66 | 82 | ||
67 | describe "PUT /projects/:id/issues/:issue_id to update state and label" do | 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,6 +32,11 @@ describe Gitlab::API do | ||
32 | response.status.should == 200 | 32 | response.status.should == 200 |
33 | json_response['title'].should == merge_request.title | 33 | json_response['title'].should == merge_request.title |
34 | end | 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 | end | 40 | end |
36 | 41 | ||
37 | describe "POST /projects/:id/merge_requests" do | 42 | describe "POST /projects/:id/merge_requests" do |
@@ -41,6 +46,30 @@ describe Gitlab::API do | @@ -41,6 +46,30 @@ describe Gitlab::API do | ||
41 | response.status.should == 201 | 46 | response.status.should == 201 |
42 | json_response['title'].should == 'Test merge_request' | 47 | json_response['title'].should == 'Test merge_request' |
43 | end | 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 | end | 73 | end |
45 | 74 | ||
46 | describe "PUT /projects/:id/merge_request/:merge_request_id to close MR" do | 75 | describe "PUT /projects/:id/merge_request/:merge_request_id to close MR" do |
@@ -59,13 +88,24 @@ describe Gitlab::API do | @@ -59,13 +88,24 @@ describe Gitlab::API do | ||
59 | end | 88 | end |
60 | end | 89 | end |
61 | 90 | ||
62 | - | ||
63 | describe "PUT /projects/:id/merge_request/:merge_request_id" do | 91 | describe "PUT /projects/:id/merge_request/:merge_request_id" do |
64 | it "should return merge_request" do | 92 | it "should return merge_request" do |
65 | put api("/projects/#{project.id}/merge_request/#{merge_request.id}", user), title: "New title" | 93 | put api("/projects/#{project.id}/merge_request/#{merge_request.id}", user), title: "New title" |
66 | response.status.should == 200 | 94 | response.status.should == 200 |
67 | json_response['title'].should == 'New title' | 95 | json_response['title'].should == 'New title' |
68 | end | 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 | end | 109 | end |
70 | 110 | ||
71 | describe "POST /projects/:id/merge_request/:merge_request_id/comments" do | 111 | describe "POST /projects/:id/merge_request/:merge_request_id/comments" do |
@@ -74,6 +114,16 @@ describe Gitlab::API do | @@ -74,6 +114,16 @@ describe Gitlab::API do | ||
74 | response.status.should == 201 | 114 | response.status.should == 201 |
75 | json_response['note'].should == 'My comment' | 115 | json_response['note'].should == 'My comment' |
76 | end | 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 | end | 127 | end |
78 | 128 | ||
79 | end | 129 | end |
spec/requests/api/milestones_spec.rb
@@ -16,6 +16,11 @@ describe Gitlab::API do | @@ -16,6 +16,11 @@ describe Gitlab::API do | ||
16 | json_response.should be_an Array | 16 | json_response.should be_an Array |
17 | json_response.first['title'].should == milestone.title | 17 | json_response.first['title'].should == milestone.title |
18 | end | 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 | end | 24 | end |
20 | 25 | ||
21 | describe "GET /projects/:id/milestones/:milestone_id" do | 26 | describe "GET /projects/:id/milestones/:milestone_id" do |
@@ -24,16 +29,38 @@ describe Gitlab::API do | @@ -24,16 +29,38 @@ describe Gitlab::API do | ||
24 | response.status.should == 200 | 29 | response.status.should == 200 |
25 | json_response['title'].should == milestone.title | 30 | json_response['title'].should == milestone.title |
26 | end | 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 | end | 42 | end |
28 | 43 | ||
29 | describe "POST /projects/:id/milestones" do | 44 | describe "POST /projects/:id/milestones" do |
30 | it "should create a new project milestone" do | 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 | response.status.should == 201 | 47 | response.status.should == 201 |
34 | json_response['title'].should == 'new milestone' | 48 | json_response['title'].should == 'new milestone' |
35 | json_response['description'].should be_nil | 49 | json_response['description'].should be_nil |
36 | end | 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 | end | 64 | end |
38 | 65 | ||
39 | describe "PUT /projects/:id/milestones/:milestone_id" do | 66 | describe "PUT /projects/:id/milestones/:milestone_id" do |
@@ -43,6 +70,12 @@ describe Gitlab::API do | @@ -43,6 +70,12 @@ describe Gitlab::API do | ||
43 | response.status.should == 200 | 70 | response.status.should == 200 |
44 | json_response['title'].should == 'updated title' | 71 | json_response['title'].should == 'updated title' |
45 | end | 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 | end | 79 | end |
47 | 80 | ||
48 | describe "PUT /projects/:id/milestones/:milestone_id to close milestone" do | 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,6 +38,11 @@ describe Gitlab::API do | ||
38 | response.status.should == 200 | 38 | response.status.should == 200 |
39 | json_response['body'].should == wall_note.note | 39 | json_response['body'].should == wall_note.note |
40 | end | 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 | end | 46 | end |
42 | 47 | ||
43 | describe "POST /projects/:id/notes" do | 48 | describe "POST /projects/:id/notes" do |
@@ -46,6 +51,16 @@ describe Gitlab::API do | @@ -46,6 +51,16 @@ describe Gitlab::API do | ||
46 | response.status.should == 201 | 51 | response.status.should == 201 |
47 | json_response['body'].should == 'hi!' | 52 | json_response['body'].should == 'hi!' |
48 | end | 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 | end | 64 | end |
50 | 65 | ||
51 | describe "GET /projects/:id/noteable/:noteable_id/notes" do | 66 | describe "GET /projects/:id/noteable/:noteable_id/notes" do |
@@ -56,6 +71,11 @@ describe Gitlab::API do | @@ -56,6 +71,11 @@ describe Gitlab::API do | ||
56 | json_response.should be_an Array | 71 | json_response.should be_an Array |
57 | json_response.first['body'].should == issue_note.note | 72 | json_response.first['body'].should == issue_note.note |
58 | end | 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 | end | 79 | end |
60 | 80 | ||
61 | context "when noteable is a Snippet" do | 81 | context "when noteable is a Snippet" do |
@@ -65,6 +85,11 @@ describe Gitlab::API do | @@ -65,6 +85,11 @@ describe Gitlab::API do | ||
65 | json_response.should be_an Array | 85 | json_response.should be_an Array |
66 | json_response.first['body'].should == snippet_note.note | 86 | json_response.first['body'].should == snippet_note.note |
67 | end | 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 | end | 93 | end |
69 | 94 | ||
70 | context "when noteable is a Merge Request" do | 95 | context "when noteable is a Merge Request" do |
@@ -74,6 +99,11 @@ describe Gitlab::API do | @@ -74,6 +99,11 @@ describe Gitlab::API do | ||
74 | json_response.should be_an Array | 99 | json_response.should be_an Array |
75 | json_response.first['body'].should == merge_request_note.note | 100 | json_response.first['body'].should == merge_request_note.note |
76 | end | 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 | end | 107 | end |
78 | end | 108 | end |
79 | 109 | ||
@@ -84,6 +114,11 @@ describe Gitlab::API do | @@ -84,6 +114,11 @@ describe Gitlab::API do | ||
84 | response.status.should == 200 | 114 | response.status.should == 200 |
85 | json_response['body'].should == issue_note.note | 115 | json_response['body'].should == issue_note.note |
86 | end | 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 | end | 122 | end |
88 | 123 | ||
89 | context "when noteable is a Snippet" do | 124 | context "when noteable is a Snippet" do |
@@ -92,6 +127,11 @@ describe Gitlab::API do | @@ -92,6 +127,11 @@ describe Gitlab::API do | ||
92 | response.status.should == 200 | 127 | response.status.should == 200 |
93 | json_response['body'].should == snippet_note.note | 128 | json_response['body'].should == snippet_note.note |
94 | end | 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 | end | 135 | end |
96 | end | 136 | end |
97 | 137 | ||
@@ -103,6 +143,16 @@ describe Gitlab::API do | @@ -103,6 +143,16 @@ describe Gitlab::API do | ||
103 | json_response['body'].should == 'hi!' | 143 | json_response['body'].should == 'hi!' |
104 | json_response['author']['email'].should == user.email | 144 | json_response['author']['email'].should == user.email |
105 | end | 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 | end | 156 | end |
107 | 157 | ||
108 | context "when noteable is a Snippet" do | 158 | context "when noteable is a Snippet" do |
@@ -112,6 +162,16 @@ describe Gitlab::API do | @@ -112,6 +162,16 @@ describe Gitlab::API do | ||
112 | json_response['body'].should == 'hi!' | 162 | json_response['body'].should == 'hi!' |
113 | json_response['author']['email'].should == user.email | 163 | json_response['author']['email'].should == user.email |
114 | end | 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 | end | 175 | end |
116 | end | 176 | end |
117 | end | 177 | end |
spec/requests/api/projects_spec.rb
@@ -7,8 +7,8 @@ describe Gitlab::API do | @@ -7,8 +7,8 @@ describe Gitlab::API do | ||
7 | let(:user2) { create(:user) } | 7 | let(:user2) { create(:user) } |
8 | let(:user3) { create(:user) } | 8 | let(:user3) { create(:user) } |
9 | let(:admin) { create(:admin) } | 9 | let(:admin) { create(:admin) } |
10 | - let!(:hook) { create(:project_hook, project: project, url: "http://example.com") } | ||
11 | let!(:project) { create(:project, namespace: user.namespace ) } | 10 | let!(:project) { create(:project, namespace: user.namespace ) } |
11 | + let!(:hook) { create(:project_hook, project: project, url: "http://example.com") } | ||
12 | let!(:snippet) { create(:snippet, author: user, project: project, title: 'example') } | 12 | let!(:snippet) { create(:snippet, author: user, project: project, title: 'example') } |
13 | let!(:users_project) { create(:users_project, user: user, project: project, project_access: UsersProject::MASTER) } | 13 | let!(:users_project) { create(:users_project, user: user, project: project, project_access: UsersProject::MASTER) } |
14 | let!(:users_project2) { create(:users_project, user: user3, project: project, project_access: UsersProject::DEVELOPER) } | 14 | let!(:users_project2) { create(:users_project, user: user3, project: project, project_access: UsersProject::DEVELOPER) } |
@@ -58,6 +58,11 @@ describe Gitlab::API do | @@ -58,6 +58,11 @@ describe Gitlab::API do | ||
58 | expect { post api("/projects", user) }.to_not change {Project.count} | 58 | expect { post api("/projects", user) }.to_not change {Project.count} |
59 | end | 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 | it "should create last project before reaching project limit" do | 66 | it "should create last project before reaching project limit" do |
62 | (1..user2.projects_limit-1).each { |p| post api("/projects", user2), name: "foo#{p}" } | 67 | (1..user2.projects_limit-1).each { |p| post api("/projects", user2), name: "foo#{p}" } |
63 | post api("/projects", user2), name: "foo" | 68 | post api("/projects", user2), name: "foo" |
@@ -69,9 +74,17 @@ describe Gitlab::API do | @@ -69,9 +74,17 @@ describe Gitlab::API do | ||
69 | response.status.should == 201 | 74 | response.status.should == 201 |
70 | end | 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 | post api("/projects", user) | 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 | end | 88 | end |
76 | 89 | ||
77 | it "should assign attributes to project" do | 90 | it "should assign attributes to project" do |
@@ -152,6 +165,12 @@ describe Gitlab::API do | @@ -152,6 +165,12 @@ describe Gitlab::API do | ||
152 | response.status.should == 404 | 165 | response.status.should == 404 |
153 | json_response['message'].should == '404 Not Found' | 166 | json_response['message'].should == '404 Not Found' |
154 | end | 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 | end | 174 | end |
156 | 175 | ||
157 | describe "GET /projects/:id/repository/branches" do | 176 | describe "GET /projects/:id/repository/branches" do |
@@ -188,6 +207,17 @@ describe Gitlab::API do | @@ -188,6 +207,17 @@ describe Gitlab::API do | ||
188 | json_response['commit']['id'].should == '621491c677087aa243f165eab467bfdfbee00be1' | 207 | json_response['commit']['id'].should == '621491c677087aa243f165eab467bfdfbee00be1' |
189 | json_response['protected'].should == true | 208 | json_response['protected'].should == true |
190 | end | 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 | end | 221 | end |
192 | 222 | ||
193 | describe "PUT /projects/:id/repository/branches/:branch/unprotect" do | 223 | describe "PUT /projects/:id/repository/branches/:branch/unprotect" do |
@@ -199,6 +229,17 @@ describe Gitlab::API do | @@ -199,6 +229,17 @@ describe Gitlab::API do | ||
199 | json_response['commit']['id'].should == '621491c677087aa243f165eab467bfdfbee00be1' | 229 | json_response['commit']['id'].should == '621491c677087aa243f165eab467bfdfbee00be1' |
200 | json_response['protected'].should == false | 230 | json_response['protected'].should == false |
201 | end | 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 | end | 243 | end |
203 | 244 | ||
204 | describe "GET /projects/:id/members" do | 245 | describe "GET /projects/:id/members" do |
@@ -217,6 +258,11 @@ describe Gitlab::API do | @@ -217,6 +258,11 @@ describe Gitlab::API do | ||
217 | json_response.count.should == 1 | 258 | json_response.count.should == 1 |
218 | json_response.first['email'].should == user.email | 259 | json_response.first['email'].should == user.email |
219 | end | 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 | end | 266 | end |
221 | 267 | ||
222 | describe "GET /projects/:id/members/:user_id" do | 268 | describe "GET /projects/:id/members/:user_id" do |
@@ -226,6 +272,11 @@ describe Gitlab::API do | @@ -226,6 +272,11 @@ describe Gitlab::API do | ||
226 | json_response['email'].should == user.email | 272 | json_response['email'].should == user.email |
227 | json_response['access_level'].should == UsersProject::MASTER | 273 | json_response['access_level'].should == UsersProject::MASTER |
228 | end | 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 | end | 280 | end |
230 | 281 | ||
231 | describe "POST /projects/:id/members" do | 282 | describe "POST /projects/:id/members" do |
@@ -239,6 +290,34 @@ describe Gitlab::API do | @@ -239,6 +290,34 @@ describe Gitlab::API do | ||
239 | json_response['email'].should == user2.email | 290 | json_response['email'].should == user2.email |
240 | json_response['access_level'].should == UsersProject::DEVELOPER | 291 | json_response['access_level'].should == UsersProject::DEVELOPER |
241 | end | 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 | end | 321 | end |
243 | 322 | ||
244 | describe "PUT /projects/:id/members/:user_id" do | 323 | describe "PUT /projects/:id/members/:user_id" do |
@@ -248,6 +327,21 @@ describe Gitlab::API do | @@ -248,6 +327,21 @@ describe Gitlab::API do | ||
248 | json_response['email'].should == user3.email | 327 | json_response['email'].should == user3.email |
249 | json_response['access_level'].should == UsersProject::MASTER | 328 | json_response['access_level'].should == UsersProject::MASTER |
250 | end | 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 | end | 345 | end |
252 | 346 | ||
253 | describe "DELETE /projects/:id/members/:user_id" do | 347 | describe "DELETE /projects/:id/members/:user_id" do |
@@ -256,6 +350,30 @@ describe Gitlab::API do | @@ -256,6 +350,30 @@ describe Gitlab::API do | ||
256 | delete api("/projects/#{project.id}/members/#{user3.id}", user) | 350 | delete api("/projects/#{project.id}/members/#{user3.id}", user) |
257 | }.to change { UsersProject.count }.by(-1) | 351 | }.to change { UsersProject.count }.by(-1) |
258 | end | 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 | end | 377 | end |
260 | 378 | ||
261 | describe "GET /projects/:id/hooks" do | 379 | describe "GET /projects/:id/hooks" do |
@@ -298,6 +416,11 @@ describe Gitlab::API do | @@ -298,6 +416,11 @@ describe Gitlab::API do | ||
298 | response.status.should == 403 | 416 | response.status.should == 403 |
299 | end | 417 | end |
300 | end | 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 | end | 424 | end |
302 | 425 | ||
303 | describe "POST /projects/:id/hooks" do | 426 | describe "POST /projects/:id/hooks" do |
@@ -306,6 +429,17 @@ describe Gitlab::API do | @@ -306,6 +429,17 @@ describe Gitlab::API do | ||
306 | post api("/projects/#{project.id}/hooks", user), | 429 | post api("/projects/#{project.id}/hooks", user), |
307 | url: "http://example.com" | 430 | url: "http://example.com" |
308 | }.to change {project.hooks.count}.by(1) | 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 | end | 443 | end |
310 | end | 444 | end |
311 | 445 | ||
@@ -316,13 +450,44 @@ describe Gitlab::API do | @@ -316,13 +450,44 @@ describe Gitlab::API do | ||
316 | response.status.should == 200 | 450 | response.status.should == 200 |
317 | json_response['url'].should == 'http://example.org' | 451 | json_response['url'].should == 'http://example.org' |
318 | end | 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 | end | 468 | end |
320 | 469 | ||
321 | - describe "DELETE /projects/:id/hooks/:hook_id" do | 470 | + describe "DELETE /projects/:id/hooks" do |
322 | it "should delete hook from project" do | 471 | it "should delete hook from project" do |
323 | expect { | 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 | }.to change {project.hooks.count}.by(-1) | 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 | end | 491 | end |
327 | end | 492 | end |
328 | 493 | ||
@@ -371,6 +536,11 @@ describe Gitlab::API do | @@ -371,6 +536,11 @@ describe Gitlab::API do | ||
371 | response.status.should == 200 | 536 | response.status.should == 200 |
372 | json_response['title'].should == snippet.title | 537 | json_response['title'].should == snippet.title |
373 | end | 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 | end | 544 | end |
375 | 545 | ||
376 | describe "POST /projects/:id/snippets" do | 546 | describe "POST /projects/:id/snippets" do |
@@ -380,6 +550,24 @@ describe Gitlab::API do | @@ -380,6 +550,24 @@ describe Gitlab::API do | ||
380 | response.status.should == 201 | 550 | response.status.should == 201 |
381 | json_response['title'].should == 'api test' | 551 | json_response['title'].should == 'api test' |
382 | end | 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 | end | 571 | end |
384 | 572 | ||
385 | describe "PUT /projects/:id/snippets/:shippet_id" do | 573 | describe "PUT /projects/:id/snippets/:shippet_id" do |
@@ -390,6 +578,13 @@ describe Gitlab::API do | @@ -390,6 +578,13 @@ describe Gitlab::API do | ||
390 | json_response['title'].should == 'example' | 578 | json_response['title'].should == 'example' |
391 | snippet.reload.content.should == 'updated code' | 579 | snippet.reload.content.should == 'updated code' |
392 | end | 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 | end | 588 | end |
394 | 589 | ||
395 | describe "DELETE /projects/:id/snippets/:snippet_id" do | 590 | describe "DELETE /projects/:id/snippets/:snippet_id" do |
@@ -397,6 +592,12 @@ describe Gitlab::API do | @@ -397,6 +592,12 @@ describe Gitlab::API do | ||
397 | expect { | 592 | expect { |
398 | delete api("/projects/#{project.id}/snippets/#{snippet.id}", user) | 593 | delete api("/projects/#{project.id}/snippets/#{snippet.id}", user) |
399 | }.to change { Snippet.count }.by(-1) | 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 | end | 601 | end |
401 | end | 602 | end |
402 | 603 | ||
@@ -405,9 +606,14 @@ describe Gitlab::API do | @@ -405,9 +606,14 @@ describe Gitlab::API do | ||
405 | get api("/projects/#{project.id}/snippets/#{snippet.id}/raw", user) | 606 | get api("/projects/#{project.id}/snippets/#{snippet.id}/raw", user) |
406 | response.status.should == 200 | 607 | response.status.should == 200 |
407 | end | 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 | end | 614 | end |
409 | 615 | ||
410 | - describe "GET /projects/:id/:sha/blob" do | 616 | + describe "GET /projects/:id/repository/commits/:sha/blob" do |
411 | it "should get the raw file contents" do | 617 | it "should get the raw file contents" do |
412 | get api("/projects/#{project.id}/repository/commits/master/blob?filepath=README.md", user) | 618 | get api("/projects/#{project.id}/repository/commits/master/blob?filepath=README.md", user) |
413 | response.status.should == 200 | 619 | response.status.should == 200 |
@@ -422,6 +628,11 @@ describe Gitlab::API do | @@ -422,6 +628,11 @@ describe Gitlab::API do | ||
422 | get api("/projects/#{project.id}/repository/commits/master/blob?filepath=README.invalid", user) | 628 | get api("/projects/#{project.id}/repository/commits/master/blob?filepath=README.invalid", user) |
423 | response.status.should == 404 | 629 | response.status.should == 404 |
424 | end | 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 | end | 636 | end |
426 | 637 | ||
427 | describe "GET /projects/:id/keys" do | 638 | describe "GET /projects/:id/keys" do |
spec/requests/api/session_spec.rb
@@ -35,5 +35,15 @@ describe Gitlab::API do | @@ -35,5 +35,15 @@ describe Gitlab::API do | ||
35 | json_response['private_token'].should be_nil | 35 | json_response['private_token'].should be_nil |
36 | end | 36 | end |
37 | end | 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 | end | 48 | end |
39 | end | 49 | end |
spec/requests/api/users_spec.rb
@@ -31,15 +31,20 @@ describe Gitlab::API do | @@ -31,15 +31,20 @@ describe Gitlab::API do | ||
31 | response.status.should == 200 | 31 | response.status.should == 200 |
32 | json_response['email'].should == user.email | 32 | json_response['email'].should == user.email |
33 | end | 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 | response.status.should == 404 | 42 | response.status.should == 404 |
42 | end | 43 | end |
44 | + end | ||
45 | + | ||
46 | + describe "POST /users" do | ||
47 | + before{ admin } | ||
43 | 48 | ||
44 | it "should create user" do | 49 | it "should create user" do |
45 | expect { | 50 | expect { |
@@ -47,10 +52,48 @@ describe Gitlab::API do | @@ -47,10 +52,48 @@ describe Gitlab::API do | ||
47 | }.to change { User.count }.by(1) | 52 | }.to change { User.count }.by(1) |
48 | end | 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 | it "shouldn't available for non admin users" do | 75 | it "shouldn't available for non admin users" do |
51 | post api("/users", user), attributes_for(:user) | 76 | post api("/users", user), attributes_for(:user) |
52 | response.status.should == 403 | 77 | response.status.should == 403 |
53 | end | 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 | end | 97 | end |
55 | 98 | ||
56 | describe "GET /users/sign_up" do | 99 | describe "GET /users/sign_up" do |
@@ -81,7 +124,7 @@ describe Gitlab::API do | @@ -81,7 +124,7 @@ describe Gitlab::API do | ||
81 | describe "PUT /users/:id" do | 124 | describe "PUT /users/:id" do |
82 | before { admin } | 125 | before { admin } |
83 | 126 | ||
84 | - it "should update user" do | 127 | + it "should update user with new bio" do |
85 | put api("/users/#{user.id}", admin), {bio: 'new test bio'} | 128 | put api("/users/#{user.id}", admin), {bio: 'new test bio'} |
86 | response.status.should == 200 | 129 | response.status.should == 200 |
87 | json_response['bio'].should == 'new test bio' | 130 | json_response['bio'].should == 'new test bio' |
@@ -103,6 +146,25 @@ describe Gitlab::API do | @@ -103,6 +146,25 @@ describe Gitlab::API do | ||
103 | put api("/users/999999", admin), {bio: 'update should fail'} | 146 | put api("/users/999999", admin), {bio: 'update should fail'} |
104 | response.status.should == 404 | 147 | response.status.should == 404 |
105 | end | 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 | end | 168 | end |
107 | 169 | ||
108 | describe "POST /users/:id/keys" do | 170 | describe "POST /users/:id/keys" do |
@@ -131,6 +193,11 @@ describe Gitlab::API do | @@ -131,6 +193,11 @@ describe Gitlab::API do | ||
131 | json_response['email'].should == user.email | 193 | json_response['email'].should == user.email |
132 | end | 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 | it "shouldn't available for non admin users" do | 201 | it "shouldn't available for non admin users" do |
135 | delete api("/users/#{user.id}", user) | 202 | delete api("/users/#{user.id}", user) |
136 | response.status.should == 403 | 203 | response.status.should == 403 |
@@ -148,6 +215,11 @@ describe Gitlab::API do | @@ -148,6 +215,11 @@ describe Gitlab::API do | ||
148 | response.status.should == 200 | 215 | response.status.should == 200 |
149 | json_response['email'].should == user.email | 216 | json_response['email'].should == user.email |
150 | end | 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 | end | 223 | end |
152 | 224 | ||
153 | describe "GET /user/keys" do | 225 | describe "GET /user/keys" do |
@@ -183,19 +255,38 @@ describe Gitlab::API do | @@ -183,19 +255,38 @@ describe Gitlab::API do | ||
183 | get api("/user/keys/42", user) | 255 | get api("/user/keys/42", user) |
184 | response.status.should == 404 | 256 | response.status.should == 404 |
185 | end | 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 | response.status.should == 404 | 264 | response.status.should == 404 |
192 | end | 265 | end |
266 | + end | ||
193 | 267 | ||
268 | + describe "POST /user/keys" do | ||
194 | it "should create ssh key" do | 269 | it "should create ssh key" do |
195 | key_attrs = attributes_for :key | 270 | key_attrs = attributes_for :key |
196 | expect { | 271 | expect { |
197 | post api("/user/keys", user), key_attrs | 272 | post api("/user/keys", user), key_attrs |
198 | }.to change{ user.keys.count }.by(1) | 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 | end | 290 | end |
200 | end | 291 | end |
201 | 292 | ||
@@ -206,11 +297,19 @@ describe Gitlab::API do | @@ -206,11 +297,19 @@ describe Gitlab::API do | ||
206 | expect { | 297 | expect { |
207 | delete api("/user/keys/#{key.id}", user) | 298 | delete api("/user/keys/#{key.id}", user) |
208 | }.to change{user.keys.count}.by(-1) | 299 | }.to change{user.keys.count}.by(-1) |
300 | + response.status.should == 200 | ||
209 | end | 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 | delete api("/user/keys/42", user) | 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 | end | 313 | end |
215 | end | 314 | end |
216 | end | 315 | end |