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