Commit a7055be1 authored by Dmitriy Zaporozhets's avatar Dmitriy Zaporozhets

Merge pull request #2835 from Asquera/fixes/api

Fix API return codes
parents d2cec126 ecf53bb9
...@@ -91,7 +91,7 @@ class MergeRequest < ActiveRecord::Base ...@@ -91,7 +91,7 @@ class MergeRequest < ActiveRecord::Base
def validate_branches def validate_branches
if target_branch == source_branch if target_branch == source_branch
errors.add :base, "You can not use same branch for source and target branches" errors.add :branch_conflict, "You can not use same branch for source and target branches"
end end
end end
......
...@@ -160,7 +160,7 @@ class Project < ActiveRecord::Base ...@@ -160,7 +160,7 @@ class Project < ActiveRecord::Base
def check_limit def check_limit
unless creator.can_create_project? unless creator.can_create_project?
errors[:base] << ("Your own projects limit is #{creator.projects_limit}! Please contact administrator to increase it") errors[:limit_reached] << ("Your own projects limit is #{creator.projects_limit}! Please contact administrator to increase it")
end end
rescue rescue
errors[:base] << ("Can't check your ability to create project") errors[:base] << ("Can't check your ability to create project")
......
...@@ -8,6 +8,7 @@ Gitlab::Application.routes.draw do ...@@ -8,6 +8,7 @@ Gitlab::Application.routes.draw do
# API # API
require 'api' require 'api'
Gitlab::API.logger Rails.logger
mount Gitlab::API => '/api' mount Gitlab::API => '/api'
constraint = lambda { |request| request.env["warden"].authenticate? and request.env['warden'].user.admin? } constraint = lambda { |request| request.env["warden"].authenticate? and request.env['warden'].user.admin? }
......
# GitLab API # GitLab API
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. 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.
If no, or an invalid, `private_token` is provided then an error message will be returned with status code 401: If no, or an invalid, `private_token` is provided then an error message will be returned with status code 401:
...@@ -18,8 +18,48 @@ Example of a valid API request: ...@@ -18,8 +18,48 @@ Example of a valid API request:
GET http://example.com/api/v3/projects?private_token=QVy1PB7sTxfy4pqfZM1U GET http://example.com/api/v3/projects?private_token=QVy1PB7sTxfy4pqfZM1U
``` ```
Example for a valid API request using curl and authentication via header:
```
curl --header "PRIVATE-TOKEN: QVy1PB7sTxfy4pqfZM1U" "http://example.com/api/v3/projects"
```
The API uses JSON to serialize data. You don't need to specify `.json` at the end of API URL. The API uses JSON to serialize data. You don't need to specify `.json` at the end of API URL.
## Status codes
The API is designed to return different status codes according to context and action. In this way
if a request results in an error the caller is able to get insight into what went wrong, e.g.
status code `400 Bad Request` is returned if a required attribute is missing from the request.
The following list gives an overview of how the API functions generally behave.
API request types:
* `GET` requests access one or more resources and return the result as JSON
* `POST` requests return `201 Created` if the resource is successfully created and return the newly created resource as JSON
* `GET`, `PUT` and `DELETE` return `200 Ok` if the resource is accessed, modified or deleted successfully, the (modified) result is returned as JSON
* `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.
The following list shows the possible return codes for API requests.
Return values:
* `200 Ok` - The `GET`, `PUT` or `DELETE` request was successful, the resource(s) itself is returned as JSON
* `201 Created` - The `POST` request was successful and the resource is returned as JSON
* `400 Bad Request` - A required attribute of the API request is missing, e.g. the title of an issue is not given
* `401 Unauthorized` - The user is not authenticated, a valid user token is necessary, see above
* `403 Forbidden` - The request is not allowed, e.g. the user is not allowed to delete a project
* `404 Not Found` - A resource could not be accessed, e.g. an ID for a resource could not be found
* `405 Method Not Allowed` - The request is not supported
* `409 Conflict` - A conflicting resource already exists, e.g. creating a project with a name that already exists
* `500 Server Error` - While handling the request something went wrong on the server side
#### Pagination #### Pagination
When listing resources you can pass the following parameters: When listing resources you can pass the following parameters:
......
...@@ -17,7 +17,8 @@ GET /groups ...@@ -17,7 +17,8 @@ GET /groups
] ]
``` ```
## Details of group
## Details of a group
Get all details of a group. Get all details of a group.
...@@ -29,19 +30,19 @@ Parameters: ...@@ -29,19 +30,19 @@ Parameters:
+ `id` (required) - The ID of a group + `id` (required) - The ID of a group
## New group ## New group
Create a new project group. Available only for admin Creates a new project group. Available only for admin.
``` ```
POST /groups POST /groups
``` ```
Parameters: Parameters:
+ `name` (required) - Email
+ `path` - Password
Will return created group with status `201 Created` on success, or `404 Not found` on fail. + `name` (required) - The name of the group
+ `path` (required) - The path of the group
## Transfer project to group ## Transfer project to group
......
## List issues ## List issues
Get all issues created by authenticed user. Get all issues created by authenticed user. This function takes pagination parameters
`page` and `per_page` to restrict the list of issues.
``` ```
GET /issues GET /issues
...@@ -68,9 +69,11 @@ GET /issues ...@@ -68,9 +69,11 @@ GET /issues
] ]
``` ```
## List project issues ## List project issues
Get a list of project issues. Get a list of project issues. This function accepts pagination parameters `page` and `per_page`
to return the list of project issues.
``` ```
GET /projects/:id/issues GET /projects/:id/issues
...@@ -80,9 +83,10 @@ Parameters: ...@@ -80,9 +83,10 @@ Parameters:
+ `id` (required) - The ID of a project + `id` (required) - The ID of a project
## Single issue ## Single issue
Get a project issue. Gets a single project issue.
``` ```
GET /projects/:id/issues/:issue_id GET /projects/:id/issues/:issue_id
...@@ -133,9 +137,10 @@ Parameters: ...@@ -133,9 +137,10 @@ Parameters:
} }
``` ```
## New issue ## New issue
Create a new project issue. Creates a new project issue.
``` ```
POST /projects/:id/issues POST /projects/:id/issues
...@@ -150,11 +155,10 @@ Parameters: ...@@ -150,11 +155,10 @@ Parameters:
+ `milestone_id` (optional) - The ID of a milestone to assign issue + `milestone_id` (optional) - The ID of a milestone to assign issue
+ `labels` (optional) - Comma-separated label names for an issue + `labels` (optional) - Comma-separated label names for an issue
Will return created issue with status `201 Created` on success, or `404 Not found` on fail.
## Edit issue ## Edit issue
Update an existing project issue. Updates an existing project issue. This function is also used to mark an issue as closed.
``` ```
PUT /projects/:id/issues/:issue_id PUT /projects/:id/issues/:issue_id
...@@ -171,5 +175,19 @@ Parameters: ...@@ -171,5 +175,19 @@ Parameters:
+ `labels` (optional) - Comma-separated label names for an issue + `labels` (optional) - Comma-separated label names for an issue
+ `closed` (optional) - The state of an issue (0 = false, 1 = true) + `closed` (optional) - The state of an issue (0 = false, 1 = true)
Will return updated issue with status `200 OK` on success, or `404 Not found` on fail.
## Delete existing issue (**Deprecated**)
The function is deprecated and returns a `405 Method Not Allowed`
error if called. An issue gets now closed and is done by calling `PUT /projects/:id/issues/:issue_id` with
parameter `closed` set to 1.
```
DELETE /projects/:id/issues/:issue_id
```
Parameters:
+ `id` (required) - The project ID
+ `issue_id` (required) - The ID of the issue
## List merge requests ## List merge requests
Get all MR for this project. Get all merge requests for this project. This function takes pagination parameters
`page` and `per_page` to restrict the list of merge requests.
``` ```
GET /projects/:id/merge_requests GET /projects/:id/merge_requests
...@@ -40,9 +41,10 @@ Parameters: ...@@ -40,9 +41,10 @@ Parameters:
] ]
``` ```
## Show MR
Show information about MR. ## Get single MR
Shows information about a single merge request.
``` ```
GET /projects/:id/merge_request/:merge_request_id GET /projects/:id/merge_request/:merge_request_id
...@@ -84,7 +86,7 @@ Parameters: ...@@ -84,7 +86,7 @@ Parameters:
## Create MR ## Create MR
Create MR. Creates a new merge request.
``` ```
POST /projects/:id/merge_requests POST /projects/:id/merge_requests
...@@ -126,9 +128,10 @@ Parameters: ...@@ -126,9 +128,10 @@ Parameters:
} }
``` ```
## Update MR ## Update MR
Update MR. You can change branches, title, or even close the MR. Updates an existing merge request. You can change branches, title, or even close the MR.
``` ```
PUT /projects/:id/merge_request/:merge_request_id PUT /projects/:id/merge_request/:merge_request_id
...@@ -172,9 +175,11 @@ Parameters: ...@@ -172,9 +175,11 @@ Parameters:
} }
} }
``` ```
## Post comment to MR ## Post comment to MR
Post comment to MR Adds a comment to a merge request.
``` ```
POST /projects/:id/merge_request/:merge_request_id/comments POST /projects/:id/merge_request/:merge_request_id/comments
...@@ -183,10 +188,9 @@ POST /projects/:id/merge_request/:merge_request_id/comments ...@@ -183,10 +188,9 @@ POST /projects/:id/merge_request/:merge_request_id/comments
Parameters: Parameters:
+ `id` (required) - The ID of a project + `id` (required) - The ID of a project
+ `merge_request_id` (required) - ID of MR + `merge_request_id` (required) - ID of merge request
+ `note` (required) - Text of comment + `note` (required) - Text of comment
Will return created note with status `201 Created` on success, or `404 Not found` on fail.
```json ```json
{ {
......
## List project milestones ## List project milestones
Get a list of project milestones. Returns a list of project milestones.
``` ```
GET /projects/:id/milestones GET /projects/:id/milestones
...@@ -10,9 +10,10 @@ Parameters: ...@@ -10,9 +10,10 @@ Parameters:
+ `id` (required) - The ID of a project + `id` (required) - The ID of a project
## Single milestone
Get a single project milestone. ## Get single milestone
Gets a single project milestone.
``` ```
GET /projects/:id/milestones/:milestone_id GET /projects/:id/milestones/:milestone_id
...@@ -23,9 +24,10 @@ Parameters: ...@@ -23,9 +24,10 @@ Parameters:
+ `id` (required) - The ID of a project + `id` (required) - The ID of a project
+ `milestone_id` (required) - The ID of a project milestone + `milestone_id` (required) - The ID of a project milestone
## New milestone
Create a new project milestone. ## Create new milestone
Creates a new project milestone.
``` ```
POST /projects/:id/milestones POST /projects/:id/milestones
...@@ -38,9 +40,10 @@ Parameters: ...@@ -38,9 +40,10 @@ Parameters:
+ `description` (optional) - The description of the milestone + `description` (optional) - The description of the milestone
+ `due_date` (optional) - The due date of the milestone + `due_date` (optional) - The due date of the milestone
## Edit milestone ## Edit milestone
Update an existing project milestone. Updates an existing project milestone.
``` ```
PUT /projects/:id/milestones/:milestone_id PUT /projects/:id/milestones/:milestone_id
...@@ -54,3 +57,4 @@ Parameters: ...@@ -54,3 +57,4 @@ Parameters:
+ `description` (optional) - The description of a milestone + `description` (optional) - The description of a milestone
+ `due_date` (optional) - The due date of the milestone + `due_date` (optional) - The due date of the milestone
+ `closed` (optional) - The status of the milestone + `closed` (optional) - The status of the milestone
## List notes ## Wall
### List project wall notes ### List project wall notes
...@@ -30,22 +30,40 @@ Parameters: ...@@ -30,22 +30,40 @@ Parameters:
+ `id` (required) - The ID of a project + `id` (required) - The ID of a project
### List merge request notes
Get a list of merge request notes. ### Get single wall note
Returns a single wall note.
``` ```
GET /projects/:id/merge_requests/:merge_request_id/notes GET /projects/:id/notes/:note_id
``` ```
Parameters: Parameters:
+ `id` (required) - The ID of a project + `id` (required) - The ID of a project
+ `merge_request_id` (required) - The ID of an merge request + `note_id` (required) - The ID of a wall note
### List issue notes
Get a list of issue notes. ### Create new wall note
Creates a new wall note.
```
POST /projects/:id/notes
```
Parameters:
+ `id` (required) - The ID of a project
+ `body` (required) - The content of a note
## Issues
### List project issue notes
Gets a list of all notes for a single issue.
``` ```
GET /projects/:id/issues/:issue_id/notes GET /projects/:id/issues/:issue_id/notes
...@@ -56,54 +74,59 @@ Parameters: ...@@ -56,54 +74,59 @@ Parameters:
+ `id` (required) - The ID of a project + `id` (required) - The ID of a project
+ `issue_id` (required) - The ID of an issue + `issue_id` (required) - The ID of an issue
### List snippet notes
Get a list of snippet notes. ### Get single issue note
Returns a single note for a specific project issue
``` ```
GET /projects/:id/snippets/:snippet_id/notes GET /projects/:id/issues/:issue_id/notes/:note_id
``` ```
Parameters: Parameters:
+ `id` (required) - The ID of a project + `id` (required) - The ID of a project
+ `snippet_id` (required) - The ID of a snippet + `issue_id` (required) - The ID of a project issue
+ `note_id` (required) - The ID of an issue note
## Single note
### Single wall note ### Create new issue note
Get a wall note. Creates a new note to a single project issue.
``` ```
GET /projects/:id/notes/:note_id POST /projects/:id/issues/:issue_id/notes
``` ```
Parameters: Parameters:
+ `id` (required) - The ID of a project + `id` (required) - The ID of a project
+ `note_id` (required) - The ID of a wall note + `issue_id` (required) - The ID of an issue
+ `body` (required) - The content of a note
### Single issue note
Get an issue note. ## Snippets
### List all snippet notes
Gets a list of all notes for a single snippet. Snippet notes are comments users can post to a snippet.
``` ```
GET /projects/:id/issues/:issue_id/:notes/:note_id GET /projects/:id/snippets/:snippet_id/notes
``` ```
Parameters: Parameters:
+ `id` (required) - The ID of a project + `id` (required) - The ID of a project
+ `issue_id` (required) - The ID of a project issue + `snippet_id` (required) - The ID of a project snippet
+ `note_id` (required) - The ID of an issue note
### Single snippet note ### Get single snippet note
Get a snippet note. Returns a single note for a given snippet.
``` ```
GET /projects/:id/issues/:snippet_id/:notes/:note_id GET /projects/:id/snippets/:snippet_id/notes/:note_id
``` ```
Parameters: Parameters:
...@@ -112,52 +135,64 @@ Parameters: ...@@ -112,52 +135,64 @@ Parameters:
+ `snippet_id` (required) - The ID of a project snippet + `snippet_id` (required) - The ID of a project snippet
+ `note_id` (required) - The ID of an snippet note + `note_id` (required) - The ID of an snippet note
## New note
### New wall note ### Create new snippet note
Create a new wall note. Creates a new note for a single snippet. Snippet notes are comments users can post to a snippet.
``` ```
POST /projects/:id/notes POST /projects/:id/snippets/:snippet_id/notes
``` ```
Parameters: Parameters:
+ `id` (required) - The ID of a project + `id` (required) - The ID of a project
+ `snippet_id` (required) - The ID of an snippet
+ `body` (required) - The content of a note + `body` (required) - The content of a note
Will return created note with status `201 Created` on success, or `404 Not found` on fail.
## Merge Requests
### New issue note ### List all merge request notes
Create a new issue note. Gets a list of all notes for a single merge request.
``` ```
POST /projects/:id/issues/:issue_id/notes GET /projects/:id/merge_requests/:merge_request_id/notes
``` ```
Parameters: Parameters:
+ `id` (required) - The ID of a project + `id` (required) - The ID of a project
+ `issue_id` (required) - The ID of an issue + `merge_request_id` (required) - The ID of a project merge request
+ `body` (required) - The content of a note
Will return created note with status `201 Created` on success, or `404 Not found` on fail.
### New snippet note ### Get single merge request note
Create a new snippet note. Returns a single note for a given merge request.
``` ```
POST /projects/:id/snippets/:snippet_id/notes GET /projects/:id/merge_requests/:merge_request_id/notes/:note_id
``` ```
Parameters: Parameters:
+ `id` (required) - The ID of a project + `id` (required) - The ID of a project
+ `snippet_id` (required) - The ID of an snippet + `merge_request_id` (required) - The ID of a project merge request
+ `note_id` (required) - The ID of a merge request note
### Create new merge request note
Creates a new note for a single merge request.
```
POST /projects/:id/merge_requests/:merge_request_id/notes
```
Parameters:
+ `id` (required) - The ID of a project
+ `merge_request_id` (required) - The ID of a merge request
+ `body` (required) - The content of a note + `body` (required) - The content of a note
Will return created note with status `201 Created` on success, or `404 Not found` on fail.
## List projects ## Projects
### List projects
Get a list of projects owned by the authenticated user. Get a list of projects owned by the authenticated user.
...@@ -55,9 +57,11 @@ GET /projects ...@@ -55,9 +57,11 @@ GET /projects
] ]
``` ```
## Single project
Get a specific project, identified by project ID, which is owned by the authentication user. ### Get single project
Get a specific project, identified by project ID or NAME, which is owned by the authentication user.
Currently namespaced projects cannot retrieved by name.
``` ```
GET /projects/:id GET /projects/:id
...@@ -65,7 +69,7 @@ GET /projects/:id ...@@ -65,7 +69,7 @@ GET /projects/:id
Parameters: Parameters:
+ `id` (required) - The ID of a project + `id` (required) - The ID or NAME of a project
```json ```json
{ {
...@@ -92,9 +96,10 @@ Parameters: ...@@ -92,9 +96,10 @@ Parameters:
} }
``` ```
## Create project
Create new project owned by user ### Create project
Creates new project owned by user.
``` ```
POST /projects POST /projects
...@@ -110,12 +115,21 @@ Parameters: ...@@ -110,12 +115,21 @@ Parameters:
+ `merge_requests_enabled` (optional) - enabled by default + `merge_requests_enabled` (optional) - enabled by default
+ `wiki_enabled` (optional) - enabled by default + `wiki_enabled` (optional) - enabled by default
Will return created project with status `201 Created` on success, or `404 Not **Project access levels**
found` on fail.
## Create project for user The project access levels are defined in the `user_project.rb` class. Currently, these levels are recoginized:
```
GUEST = 10
REPORTER = 20
DEVELOPER = 30
MASTER = 40
```
Create new project owned by user. Available only for admin
### Create project for user
Creates a new project owned by user. Available only for admins.
``` ```
POST /projects/user/:user_id POST /projects/user/:user_id
...@@ -132,10 +146,11 @@ Parameters: ...@@ -132,10 +146,11 @@ Parameters:
+ `merge_requests_enabled` (optional) - enabled by default + `merge_requests_enabled` (optional) - enabled by default
+ `wiki_enabled` (optional) - enabled by default + `wiki_enabled` (optional) - enabled by default
Will return created project with status `201 Created` on success, or `404 Not
found` on fail.
## List project team members
## Team members
### List project team members
Get a list of project team members. Get a list of project team members.
...@@ -145,12 +160,13 @@ GET /projects/:id/members ...@@ -145,12 +160,13 @@ GET /projects/:id/members
Parameters: Parameters:
+ `id` (required) - The ID of a project + `id` (required) - The ID or NAME of a project
+ `query` - Query string + `query` (optional) - Query string to search for members
## Get project team member ### Get project team member
Get a project team member. Gets a project team member.
``` ```
GET /projects/:id/members/:user_id GET /projects/:id/members/:user_id
...@@ -158,12 +174,11 @@ GET /projects/:id/members/:user_id ...@@ -158,12 +174,11 @@ GET /projects/:id/members/:user_id
Parameters: Parameters:
+ `id` (required) - The ID of a project + `id` (required) - The ID or NAME of a project
+ `user_id` (required) - The ID of a user + `user_id` (required) - The ID of a user
```json ```json
{ {
"id": 1, "id": 1,
"username": "john_smith", "username": "john_smith",
"email": "john@example.com", "email": "john@example.com",
...@@ -174,9 +189,12 @@ Parameters: ...@@ -174,9 +189,12 @@ Parameters:
} }
``` ```
## Add project team member
Add a user to a project team. ### Add project team member
Adds a user to a project team. This is an idempotent method and can be called multiple times
with the same parameters. Adding team membership to a user that is already a member does not
affect the existing membership.
``` ```
POST /projects/:id/members POST /projects/:id/members
...@@ -184,15 +202,14 @@ POST /projects/:id/members ...@@ -184,15 +202,14 @@ POST /projects/:id/members
Parameters: Parameters:
+ `id` (required) - The ID of a project + `id` (required) - The ID or NAME of a project
+ `user_id` (required) - The ID of a user to add + `user_id` (required) - The ID of a user to add
+ `access_level` (required) - Project access level + `access_level` (required) - Project access level
Will return status `201 Created` on success, or `404 Not found` on fail.
## Edit project team member ### Edit project team member
Update project team member to specified access level. Updates project team member to a specified access level.
``` ```
PUT /projects/:id/members/:user_id PUT /projects/:id/members/:user_id
...@@ -200,13 +217,12 @@ PUT /projects/:id/members/:user_id ...@@ -200,13 +217,12 @@ PUT /projects/:id/members/:user_id
Parameters: Parameters:
+ `id` (required) - The ID of a project + `id` (required) - The ID or NAME of a project
+ `user_id` (required) - The ID of a team member + `user_id` (required) - The ID of a team member
+ `access_level` (required) - Project access level + `access_level` (required) - Project access level
Will return status `200 OK` on success, or `404 Not found` on fail.
## Remove project team member ### Remove project team member
Removes user from project team. Removes user from project team.
...@@ -216,14 +232,20 @@ DELETE /projects/:id/members/:user_id ...@@ -216,14 +232,20 @@ DELETE /projects/:id/members/:user_id
Parameters: Parameters:
+ `id` (required) - The ID of a project + `id` (required) - The ID or NAME of a project
+ `user_id` (required) - The ID of a team member + `user_id` (required) - The ID of a team member
Status code `200` will be returned on success. This method is idempotent and can be called multiple times with the same parameters.
Revoking team membership for a user who is not currently a team member is considered success.
Please note that the returned JSON currently differs slightly. Thus you should not
rely on the returned JSON structure.
## List project hooks ## Hooks
Get list for project hooks ### List project hooks
Get list of project hooks.
``` ```
GET /projects/:id/hooks GET /projects/:id/hooks
...@@ -231,13 +253,12 @@ GET /projects/:id/hooks ...@@ -231,13 +253,12 @@ GET /projects/:id/hooks
Parameters: Parameters:
+ `id` (required) - The ID of a project + `id` (required) - The ID or NAME of a project
Will return hooks with status `200 OK` on success, or `404 Not found` on fail.
## Get project hook ### Get project hook
Get hook for project Get a specific hook for project.
``` ```
GET /projects/:id/hooks/:hook_id GET /projects/:id/hooks/:hook_id
...@@ -245,14 +266,21 @@ GET /projects/:id/hooks/:hook_id ...@@ -245,14 +266,21 @@ GET /projects/:id/hooks/:hook_id
Parameters: Parameters:
+ `id` (required) - The ID of a project + `id` (required) - The ID or NAME of a project
+ `hook_id` (required) - The ID of a project hook + `hook_id` (required) - The ID of a project hook
Will return hook with status `200 OK` on success, or `404 Not found` on fail. ```json
{
"id": 1,
"url": "http://example.com/hook",
"created_at": "2012-10-12T17:04:47Z"
}
```
## Add project hook
Add hook to project ### Add project hook
Adds a hook to project.
``` ```
POST /projects/:id/hooks POST /projects/:id/hooks
...@@ -260,14 +288,13 @@ POST /projects/:id/hooks ...@@ -260,14 +288,13 @@ POST /projects/:id/hooks
Parameters: Parameters:
+ `id` (required) - The ID of a project + `id` (required) - The ID or NAME of a project
+ `url` (required) - The hook URL + `url` (required) - The hook URL
Will return status `201 Created` on success, or `404 Not found` on fail.
## Edit project hook ### Edit project hook
Edit hook for project Edits a hook for project.
``` ```
PUT /projects/:id/hooks/:hook_id PUT /projects/:id/hooks/:hook_id
...@@ -275,30 +302,125 @@ PUT /projects/:id/hooks/:hook_id ...@@ -275,30 +302,125 @@ PUT /projects/:id/hooks/:hook_id
Parameters: Parameters:
+ `id` (required) - The ID of a project + `id` (required) - The ID or NAME of a project
+ `hook_id` (required) - The ID of a project hook + `hook_id` (required) - The ID of a project hook
+ `url` (required) - The hook URL + `url` (required) - The hook URL
Will return status `201 Created` on success, or `404 Not found` on fail.
## Delete project hook ### Delete project hook
Delete hook from project Removes a hook from project. This is an idempotent method and can be called multiple times.
Either the hook is available or not.
``` ```
DELETE /projects/:id/hooks/:hook_id DELETE /projects/:id/hooks/
``` ```
Parameters: Parameters:
+ `id` (required) - The ID of a project + `id` (required) - The ID or NAME of a project
+ `hook_id` (required) - The ID of hook to delete + `hook_id` (required) - The ID of hook to delete
Will return status `200 OK` on success, or `404 Not found` on fail. Note the JSON response differs if the hook is available or not. If the project hook
is available before it is returned in the JSON response or an empty response is returned.
## List deploy keys ## Branches
### List branches
Lists all branches of a project.
```
GET /projects/:id/repository/branches
```
Parameters:
+ `id` (required) - The ID of the project
### List single branch
Lists a specific branch of a project.
```
GET /projects/:id/repository/branches/:branch
```
Parameters:
+ `id` (required) - The ID of the project.
+ `branch` (required) - The name of the branch.
### Protect single branch
Protects a single branch of a project.
```
PUT /projects/:id/repository/branches/:branch/protect
```
Parameters:
+ `id` (required) - The ID of the project.
+ `branch` (required) - The name of the branch.
### Unprotect single branch
Unprotects a single branch of a project.
```
PUT /projects/:id/repository/branches/:branch/unprotect
```
Parameters:
+ `id` (required) - The ID of the project.
+ `branch` (required) - The name of the branch.
### List tags
Lists all tags of a project.
```
GET /projects/:id/repository/tags
```
Parameters:
+ `id` (required) - The ID of the project
### List commits
Lists all commits with pagination. If the optional `ref_name` name is not given the commits of
the default branch (usually master) are returned.
```
GET /projects/:id/repository/commits
```
Parameters:
+ `id` (required) - The Id of the project
+ `ref_name` (optional) - The name of a repository branch or tag
+ `page` (optional) - The page of commits to return (`0` default)
+ `per_page` (optional) - The number of commits per page (`20` default)
Returns values:
+ `200 Ok` on success and a list with commits
+ `404 Not Found` if project with id or the branch with `ref_name` not found
## Deploy Keys
### List deploy keys
Get a list of a project's deploy keys. Get a list of a project's deploy keys.
...@@ -306,6 +428,10 @@ Get a list of a project's deploy keys. ...@@ -306,6 +428,10 @@ Get a list of a project's deploy keys.
GET /projects/:id/keys GET /projects/:id/keys
``` ```
Parameters:
+ `id` (required) - The ID of the project
```json ```json
[ [
{ {
...@@ -325,7 +451,8 @@ GET /projects/:id/keys ...@@ -325,7 +451,8 @@ GET /projects/:id/keys
] ]
``` ```
## Single deploy key
### Single deploy key
Get a single key. Get a single key.
...@@ -335,7 +462,8 @@ GET /projects/:id/keys/:key_id ...@@ -335,7 +462,8 @@ GET /projects/:id/keys/:key_id
Parameters: Parameters:
+ `id` (required) - The ID of an deploy key + `id` (required) - The ID of the project
+ `key_id` (required) - The ID of the deploy key
```json ```json
{ {
...@@ -346,9 +474,11 @@ Parameters: ...@@ -346,9 +474,11 @@ Parameters:
soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=" soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0="
} }
``` ```
## Add deploy key
Create new deploy key for a project
### Add deploy key
Creates a new deploy key for a project.
``` ```
POST /projects/:id/keys POST /projects/:id/keys
...@@ -356,13 +486,12 @@ POST /projects/:id/keys ...@@ -356,13 +486,12 @@ POST /projects/:id/keys
Parameters: Parameters:
+ `title` (required) - new deploy key's title + `id` (required) - The ID of the project
+ `key` (required) - new deploy key + `title` (required) - New deploy key's title
+ `key` (required) - New deploy key
Will return created key with status `201 Created` on success, or `404 Not
found` on fail.
## Delete deploy key ### Delete deploy key
Delete a deploy key from a project Delete a deploy key from a project
...@@ -372,6 +501,6 @@ DELETE /projects/:id/keys/:key_id ...@@ -372,6 +501,6 @@ DELETE /projects/:id/keys/:key_id
Parameters: Parameters:
+ `id` (required) - Deploy key ID + `id` (required) - The ID of the project
+ `key_id` (required) - The ID of the deploy key
Will return `200 OK` on success, or `404 Not Found` on fail.
## Project repository branches ## List repository branches
Get a list of repository branches from a project, sorted by name alphabetically. Get a list of repository branches from a project, sorted by name alphabetically.
...@@ -39,7 +39,8 @@ Parameters: ...@@ -39,7 +39,8 @@ Parameters:
] ]
``` ```
## Project repository branch
## Get single repository branch
Get a single project repository branch. Get a single project repository branch.
...@@ -79,12 +80,11 @@ Parameters: ...@@ -79,12 +80,11 @@ Parameters:
} }
``` ```
Will return status code `200` on success or `404 Not found` if the branch is not available.
## Protect a project repository branch ## Protect repository branch
Protect a single project repository branch. Protects a single project repository branch. This is an idempotent function, protecting an already
protected repository branch still returns a `200 Ok` status code.
``` ```
PUT /projects/:id/repository/branches/:branch/protect PUT /projects/:id/repository/branches/:branch/protect
...@@ -122,9 +122,11 @@ Parameters: ...@@ -122,9 +122,11 @@ Parameters:
} }
``` ```
## Unprotect a project repository branch
Unprotect a single project repository branch. ## Unprotect repository branch
Unprotects a single project repository branch. This is an idempotent function, unprotecting an already
unprotected repository branch still returns a `200 Ok` status code.
``` ```
PUT /projects/:id/repository/branches/:branch/unprotect PUT /projects/:id/repository/branches/:branch/unprotect
...@@ -162,7 +164,8 @@ Parameters: ...@@ -162,7 +164,8 @@ Parameters:
} }
``` ```
## Project repository tags
## List project repository tags
Get a list of repository tags from a project, sorted by name in reverse alphabetical order. Get a list of repository tags from a project, sorted by name in reverse alphabetical order.
...@@ -201,7 +204,8 @@ Parameters: ...@@ -201,7 +204,8 @@ Parameters:
] ]
``` ```
## Project repository commits
## List repository commits
Get a list of repository commits in a project. Get a list of repository commits in a project.
...@@ -212,7 +216,7 @@ GET /projects/:id/repository/commits ...@@ -212,7 +216,7 @@ GET /projects/:id/repository/commits
Parameters: Parameters:
+ `id` (required) - The ID of a project + `id` (required) - The ID of a project
+ `ref_name` (optional) - The name of a repository branch or tag + `ref_name` (optional) - The name of a repository branch or tag or if not given the default branch
```json ```json
[ [
...@@ -235,6 +239,7 @@ Parameters: ...@@ -235,6 +239,7 @@ Parameters:
] ]
``` ```
## Raw blob content ## Raw blob content
Get the raw file contents for a file. Get the raw file contents for a file.
...@@ -248,5 +253,3 @@ Parameters: ...@@ -248,5 +253,3 @@ Parameters:
+ `id` (required) - The ID of a project + `id` (required) - The ID of a project
+ `sha` (required) - The commit or branch name + `sha` (required) - The commit or branch name
+ `filepath` (required) - The path the file + `filepath` (required) - The path the file
Will return the raw file contents.
...@@ -10,9 +10,10 @@ Parameters: ...@@ -10,9 +10,10 @@ Parameters:
+ `id` (required) - The ID of a project + `id` (required) - The ID of a project
## Single snippet ## Single snippet
Get a project snippet. Get a single project snippet.
``` ```
GET /projects/:id/snippets/:snippet_id GET /projects/:id/snippets/:snippet_id
...@@ -42,22 +43,10 @@ Parameters: ...@@ -42,22 +43,10 @@ Parameters:
} }
``` ```
## Snippet content
Get a raw project snippet.
```
GET /projects/:id/snippets/:snippet_id/raw
```
Parameters:
+ `id` (required) - The ID of a project
+ `snippet_id` (required) - The ID of a project's snippet
## New snippet ## Create new snippet
Create a new project snippet. Creates a new project snippet. The user must have permission to create new snippets.
``` ```
POST /projects/:id/snippets POST /projects/:id/snippets
...@@ -71,11 +60,10 @@ Parameters: ...@@ -71,11 +60,10 @@ Parameters:
+ `lifetime` (optional) - The expiration date of a snippet + `lifetime` (optional) - The expiration date of a snippet
+ `code` (required) - The content of a snippet + `code` (required) - The content of a snippet
Will return created snippet with status `201 Created` on success, or `404 Not found` on fail.
## Edit snippet ## Update snippet
Update an existing project snippet. Updates an existing project snippet. The user must have permission to change an existing snippet.
``` ```
PUT /projects/:id/snippets/:snippet_id PUT /projects/:id/snippets/:snippet_id
...@@ -90,11 +78,11 @@ Parameters: ...@@ -90,11 +78,11 @@ Parameters:
+ `lifetime` (optional) - The expiration date of a snippet + `lifetime` (optional) - The expiration date of a snippet
+ `code` (optional) - The content of a snippet + `code` (optional) - The content of a snippet
Will return updated snippet with status `200 OK` on success, or `404 Not found` on fail.
## Delete snippet ## Delete snippet
Delete existing project snippet. Deletes an existing project snippet. This is an idempotent function and deleting a non-existent
snippet still returns a `200 Ok` status code.
``` ```
DELETE /projects/:id/snippets/:snippet_id DELETE /projects/:id/snippets/:snippet_id
...@@ -105,5 +93,16 @@ Parameters: ...@@ -105,5 +93,16 @@ Parameters:
+ `id` (required) - The ID of a project + `id` (required) - The ID of a project
+ `snippet_id` (required) - The ID of a project's snippet + `snippet_id` (required) - The ID of a project's snippet
Status code `200` will be returned on success.
## Snippet content
Returns the raw project snippet as plain text.
```
GET /projects/:id/snippets/:snippet_id/raw
```
Parameters:
+ `id` (required) - The ID of a project
+ `snippet_id` (required) - The ID of a project's snippet
...@@ -43,6 +43,7 @@ GET /users ...@@ -43,6 +43,7 @@ GET /users
] ]
``` ```
## Single user ## Single user
Get a single user. Get a single user.
...@@ -74,37 +75,40 @@ Parameters: ...@@ -74,37 +75,40 @@ Parameters:
} }
``` ```
## User creation ## User creation
Create user. Available only for admin
Creates a new user. Note only administrators can create new users.
``` ```
POST /users POST /users
``` ```
Parameters: Parameters:
+ `email` (required) - Email
+ `password` (required) - Password
+ `username` (required) - Username
+ `name` (required) - Name
+ `skype` - Skype ID
+ `linkedin` - Linkedin
+ `twitter` - Twitter account
+ `projects_limit` - Number of projects user can create
+ `extern_uid` - External UID
+ `provider` - External provider name
+ `bio` - User's bio
Will return created user with status `201 Created` on success, or `404 Not + `email` (required) - Email
found` on fail. + `password` (required) - Password
+ `username` (required) - Username
+ `name` (required) - Name
+ `skype` (optional) - Skype ID
+ `linkedin` (optional) - Linkedin
+ `twitter` (optional) - Twitter account
+ `projects_limit` (optional) - Number of projects user can create
+ `extern_uid` (optional) - External UID
+ `provider` (optional) - External provider name
+ `bio` (optional) - User's bio
## User modification ## User modification
Modify user. Available only for admin
Modifies an existing user. Only administrators can change attributes of a user.
``` ```
PUT /users/:id PUT /users/:id
``` ```
Parameters: Parameters:
+ `email` - Email + `email` - Email
+ `username` - Username + `username` - Username
+ `name` - Name + `name` - Name
...@@ -117,23 +121,28 @@ Parameters: ...@@ -117,23 +121,28 @@ Parameters:
+ `provider` - External provider name + `provider` - External provider name
+ `bio` - User's bio + `bio` - User's bio
Note, at the moment this method does only return a 404 error, even in cases where a 409 (Conflict) would
be more appropriate, e.g. when renaming the email address to some exsisting one.
Will return created user with status `200 OK` on success, or `404 Not
found` on fail.
## User deletion ## User deletion
Delete user. Available only for admin
Deletes a user. Available only for administrators. This is an idempotent function, calling this function
for a non-existent user id still returns a status code `200 Ok`. The JSON response differs if the user
was actually deleted or not. In the former the user is returned and in the latter not.
``` ```
DELETE /users/:id DELETE /users/:id
``` ```
Will return deleted user with status `200 OK` on success, or `404 Not Parameters:
found` on fail.
+ `id` (required) - The ID of the user
## Current user ## Current user
Get currently authenticated user. Gets currently authenticated user.
``` ```
GET /user GET /user
...@@ -156,6 +165,7 @@ GET /user ...@@ -156,6 +165,7 @@ GET /user
} }
``` ```
## List SSH keys ## List SSH keys
Get a list of currently authenticated user's SSH keys. Get a list of currently authenticated user's SSH keys.
...@@ -183,6 +193,11 @@ GET /user/keys ...@@ -183,6 +193,11 @@ GET /user/keys
] ]
``` ```
Parameters:
+ **none**
## Single SSH key ## Single SSH key
Get a single key. Get a single key.
...@@ -204,9 +219,11 @@ Parameters: ...@@ -204,9 +219,11 @@ Parameters:
soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=" soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0="
} }
``` ```
## Add SSH key ## Add SSH key
Create new key owned by currently authenticated user Creates a new key owned by the currently authenticated user.
``` ```
POST /user/keys POST /user/keys
...@@ -217,8 +234,6 @@ Parameters: ...@@ -217,8 +234,6 @@ Parameters:
+ `title` (required) - new SSH Key's title + `title` (required) - new SSH Key's title
+ `key` (required) - new SSH key + `key` (required) - new SSH key
Will return created key with status `201 Created` on success, or `404 Not
found` on fail.
## Add SSH key for user ## Add SSH key for user
...@@ -239,7 +254,8 @@ found` on fail. ...@@ -239,7 +254,8 @@ found` on fail.
## Delete SSH key ## Delete SSH key
Delete key owned by currently authenticated user Deletes key owned by currently authenticated user. This is an idempotent function and calling it on a key that is already
deleted or not available results in `200 Ok`.
``` ```
DELETE /user/keys/:id DELETE /user/keys/:id
...@@ -249,4 +265,3 @@ Parameters: ...@@ -249,4 +265,3 @@ Parameters:
+ `id` (required) - SSH key ID + `id` (required) - SSH key ID
Will return `200 OK` on success, or `404 Not Found` on fail.
...@@ -8,6 +8,19 @@ module Gitlab ...@@ -8,6 +8,19 @@ module Gitlab
rack_response({'message' => '404 Not found'}.to_json, 404) rack_response({'message' => '404 Not found'}.to_json, 404)
end end
rescue_from :all do |exception|
# lifted from https://github.com/rails/rails/blob/master/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb#L60
# why is this not wrapped in something reusable?
trace = exception.backtrace
message = "\n#{exception.class} (#{exception.message}):\n"
message << exception.annoted_source_code.to_s if exception.respond_to?(:annoted_source_code)
message << " " << trace.join("\n ")
API.logger.add Logger::FATAL, message
rack_response({'message' => '500 Internal Server Error'}, 500)
end
format :json format :json
helpers APIHelpers helpers APIHelpers
......
...@@ -20,12 +20,14 @@ module Gitlab ...@@ -20,12 +20,14 @@ module Gitlab
# Create group. Available only for admin # Create group. Available only for admin
# #
# Parameters: # Parameters:
# name (required) - Name # name (required) - The name of the group
# path (required) - Path # path (required) - The path of the group
# Example Request: # Example Request:
# POST /groups # POST /groups
post do post do
authenticated_as_admin! authenticated_as_admin!
required_attributes! [:name, :path]
attrs = attributes_for_keys [:name, :path] attrs = attributes_for_keys [:name, :path]
@group = Group.new(attrs) @group = Group.new(attrs)
@group.owner = current_user @group.owner = current_user
......
...@@ -41,6 +41,17 @@ module Gitlab ...@@ -41,6 +41,17 @@ module Gitlab
abilities.allowed?(object, action, subject) abilities.allowed?(object, action, subject)
end end
# Checks the occurrences of required attributes, each attribute must be present in the params hash
# or a Bad Request error is invoked.
#
# Parameters:
# keys (required) - A hash consisting of keys that must be present
def required_attributes!(keys)
keys.each do |key|
bad_request!(key) unless params[key].present?
end
end
def attributes_for_keys(keys) def attributes_for_keys(keys)
attrs = {} attrs = {}
keys.each do |key| keys.each do |key|
...@@ -55,6 +66,12 @@ module Gitlab ...@@ -55,6 +66,12 @@ module Gitlab
render_api_error!('403 Forbidden', 403) render_api_error!('403 Forbidden', 403)
end end
def bad_request!(attribute)
message = ["400 (Bad request)"]
message << "\"" + attribute.to_s + "\" not given"
render_api_error!(message.join(' '), 400)
end
def not_found!(resource = nil) def not_found!(resource = nil)
message = ["404"] message = ["404"]
message << resource if resource message << resource if resource
......
...@@ -48,6 +48,7 @@ module Gitlab ...@@ -48,6 +48,7 @@ module Gitlab
# Example Request: # Example Request:
# POST /projects/:id/issues # POST /projects/:id/issues
post ":id/issues" do post ":id/issues" do
required_attributes! [:title]
attrs = attributes_for_keys [:title, :description, :assignee_id, :milestone_id] attrs = attributes_for_keys [:title, :description, :assignee_id, :milestone_id]
attrs[:label_list] = params[:labels] if params[:labels].present? attrs[:label_list] = params[:labels] if params[:labels].present?
@issue = user_project.issues.new attrs @issue = user_project.issues.new attrs
......
...@@ -4,6 +4,16 @@ module Gitlab ...@@ -4,6 +4,16 @@ module Gitlab
before { authenticate! } before { authenticate! }
resource :projects do resource :projects do
helpers do
def handle_merge_request_errors!(errors)
if errors[:project_access].any?
error!(errors[:project_access], 422)
elsif errors[:branch_conflict].any?
error!(errors[:branch_conflict], 422)
end
not_found!
end
end
# List merge requests # List merge requests
# #
...@@ -51,6 +61,7 @@ module Gitlab ...@@ -51,6 +61,7 @@ module Gitlab
# #
post ":id/merge_requests" do post ":id/merge_requests" do
authorize! :write_merge_request, user_project authorize! :write_merge_request, user_project
required_attributes! [:source_branch, :target_branch, :title]
attrs = attributes_for_keys [:source_branch, :target_branch, :assignee_id, :title] attrs = attributes_for_keys [:source_branch, :target_branch, :assignee_id, :title]
merge_request = user_project.merge_requests.new(attrs) merge_request = user_project.merge_requests.new(attrs)
...@@ -60,7 +71,7 @@ module Gitlab ...@@ -60,7 +71,7 @@ module Gitlab
merge_request.reload_code merge_request.reload_code
present merge_request, with: Entities::MergeRequest present merge_request, with: Entities::MergeRequest
else else
not_found! handle_merge_request_errors! merge_request.errors
end end
end end
...@@ -88,7 +99,7 @@ module Gitlab ...@@ -88,7 +99,7 @@ module Gitlab
merge_request.mark_as_unchecked merge_request.mark_as_unchecked
present merge_request, with: Entities::MergeRequest present merge_request, with: Entities::MergeRequest
else else
not_found! handle_merge_request_errors! merge_request.errors
end end
end end
...@@ -102,6 +113,8 @@ module Gitlab ...@@ -102,6 +113,8 @@ module Gitlab
# POST /projects/:id/merge_request/:merge_request_id/comments # POST /projects/:id/merge_request/:merge_request_id/comments
# #
post ":id/merge_request/:merge_request_id/comments" do post ":id/merge_request/:merge_request_id/comments" do
required_attributes! [:note]
merge_request = user_project.merge_requests.find(params[:merge_request_id]) merge_request = user_project.merge_requests.find(params[:merge_request_id])
note = merge_request.notes.new(note: params[:note], project_id: user_project.id) note = merge_request.notes.new(note: params[:note], project_id: user_project.id)
note.author = current_user note.author = current_user
......
...@@ -41,6 +41,7 @@ module Gitlab ...@@ -41,6 +41,7 @@ module Gitlab
# POST /projects/:id/milestones # POST /projects/:id/milestones
post ":id/milestones" do post ":id/milestones" do
authorize! :admin_milestone, user_project authorize! :admin_milestone, user_project
required_attributes! [:title]
attrs = attributes_for_keys [:title, :description, :due_date] attrs = attributes_for_keys [:title, :description, :due_date]
@milestone = user_project.milestones.new attrs @milestone = user_project.milestones.new attrs
......
...@@ -37,12 +37,16 @@ module Gitlab ...@@ -37,12 +37,16 @@ module Gitlab
# Example Request: # Example Request:
# POST /projects/:id/notes # POST /projects/:id/notes
post ":id/notes" do post ":id/notes" do
required_attributes! [:body]
@note = user_project.notes.new(note: params[:body]) @note = user_project.notes.new(note: params[:body])
@note.author = current_user @note.author = current_user
if @note.save if @note.save
present @note, with: Entities::Note present @note, with: Entities::Note
else else
# :note is exposed as :body, but :note is set on error
bad_request!(:note) if @note.errors[:note].any?
not_found! not_found!
end end
end end
...@@ -89,6 +93,8 @@ module Gitlab ...@@ -89,6 +93,8 @@ module Gitlab
# POST /projects/:id/issues/:noteable_id/notes # POST /projects/:id/issues/:noteable_id/notes
# POST /projects/:id/snippets/:noteable_id/notes # POST /projects/:id/snippets/:noteable_id/notes
post ":id/#{noteables_str}/:#{noteable_id_str}/notes" do post ":id/#{noteables_str}/:#{noteable_id_str}/notes" do
required_attributes! [:body]
@noteable = user_project.send(:"#{noteables_str}").find(params[:"#{noteable_id_str}"]) @noteable = user_project.send(:"#{noteables_str}").find(params[:"#{noteable_id_str}"])
@note = @noteable.notes.new(note: params[:body]) @note = @noteable.notes.new(note: params[:body])
@note.author = current_user @note.author = current_user
......
...@@ -4,6 +4,15 @@ module Gitlab ...@@ -4,6 +4,15 @@ module Gitlab
before { authenticate! } before { authenticate! }
resource :projects do resource :projects do
helpers do
def handle_project_member_errors(errors)
if errors[:project_access].any?
error!(errors[:project_access], 422)
end
not_found!
end
end
# Get a projects list for authenticated user # Get a projects list for authenticated user
# #
# Example Request: # Example Request:
...@@ -33,9 +42,11 @@ module Gitlab ...@@ -33,9 +42,11 @@ module Gitlab
# wall_enabled (optional) - enabled by default # wall_enabled (optional) - enabled by default
# merge_requests_enabled (optional) - enabled by default # merge_requests_enabled (optional) - enabled by default
# wiki_enabled (optional) - enabled by default # wiki_enabled (optional) - enabled by default
# namespace_id (optional) - defaults to user namespace
# Example Request # Example Request
# POST /projects # POST /projects
post do post do
required_attributes! [:name]
attrs = attributes_for_keys [:name, attrs = attributes_for_keys [:name,
:description, :description,
:default_branch, :default_branch,
...@@ -48,6 +59,9 @@ module Gitlab ...@@ -48,6 +59,9 @@ module Gitlab
if @project.saved? if @project.saved?
present @project, with: Entities::Project present @project, with: Entities::Project
else else
if @project.errors[:limit_reached].present?
error!(@project.errors[:limit_reached], 403)
end
not_found! not_found!
end end
end end
...@@ -122,16 +136,22 @@ module Gitlab ...@@ -122,16 +136,22 @@ module Gitlab
# POST /projects/:id/members # POST /projects/:id/members
post ":id/members" do post ":id/members" do
authorize! :admin_project, user_project authorize! :admin_project, user_project
users_project = user_project.users_projects.new( required_attributes! [:user_id, :access_level]
user_id: params[:user_id],
project_access: params[:access_level] # either the user is already a team member or a new one
) team_member = user_project.team_member_by_id(params[:user_id])
if team_member.nil?
team_member = user_project.users_projects.new(
user_id: params[:user_id],
project_access: params[:access_level]
)
end
if users_project.save if team_member.save
@member = users_project.user @member = team_member.user
present @member, with: Entities::ProjectMember, project: user_project present @member, with: Entities::ProjectMember, project: user_project
else else
not_found! handle_project_member_errors team_member.errors
end end
end end
...@@ -145,13 +165,16 @@ module Gitlab ...@@ -145,13 +165,16 @@ module Gitlab
# PUT /projects/:id/members/:user_id # PUT /projects/:id/members/:user_id
put ":id/members/:user_id" do put ":id/members/:user_id" do
authorize! :admin_project, user_project authorize! :admin_project, user_project
users_project = user_project.users_projects.find_by_user_id params[:user_id] required_attributes! [:access_level]
team_member = user_project.users_projects.find_by_user_id(params[:user_id])
not_found!("User can not be found") if team_member.nil?
if users_project.update_attributes(project_access: params[:access_level]) if team_member.update_attributes(project_access: params[:access_level])
@member = users_project.user @member = team_member.user
present @member, with: Entities::ProjectMember, project: user_project present @member, with: Entities::ProjectMember, project: user_project
else else
not_found! handle_project_member_errors team_member.errors
end end
end end
...@@ -164,8 +187,12 @@ module Gitlab ...@@ -164,8 +187,12 @@ module Gitlab
# DELETE /projects/:id/members/:user_id # DELETE /projects/:id/members/:user_id
delete ":id/members/:user_id" do delete ":id/members/:user_id" do
authorize! :admin_project, user_project authorize! :admin_project, user_project
users_project = user_project.users_projects.find_by_user_id params[:user_id] team_member = user_project.users_projects.find_by_user_id(params[:user_id])
users_project.destroy unless team_member.nil?
team_member.destroy
else
{:message => "Access revoked", :id => params[:user_id].to_i}
end
end end
# Get project hooks # Get project hooks
...@@ -203,11 +230,16 @@ module Gitlab ...@@ -203,11 +230,16 @@ module Gitlab
# POST /projects/:id/hooks # POST /projects/:id/hooks
post ":id/hooks" do post ":id/hooks" do
authorize! :admin_project, user_project authorize! :admin_project, user_project
required_attributes! [:url]
@hook = user_project.hooks.new({"url" => params[:url]}) @hook = user_project.hooks.new({"url" => params[:url]})
if @hook.save if @hook.save
present @hook, with: Entities::Hook present @hook, with: Entities::Hook
else else
error!({'message' => '404 Not found'}, 404) if @hook.errors[:url].present?
error!("Invalid url given", 422)
end
not_found!
end end
end end
...@@ -222,27 +254,36 @@ module Gitlab ...@@ -222,27 +254,36 @@ module Gitlab
put ":id/hooks/:hook_id" do put ":id/hooks/:hook_id" do
@hook = user_project.hooks.find(params[:hook_id]) @hook = user_project.hooks.find(params[:hook_id])
authorize! :admin_project, user_project authorize! :admin_project, user_project
required_attributes! [:url]
attrs = attributes_for_keys [:url] attrs = attributes_for_keys [:url]
if @hook.update_attributes attrs if @hook.update_attributes attrs
present @hook, with: Entities::Hook present @hook, with: Entities::Hook
else else
if @hook.errors[:url].present?
error!("Invalid url given", 422)
end
not_found! not_found!
end end
end end
# Delete project hook # Deletes project hook. This is an idempotent function.
# #
# Parameters: # Parameters:
# id (required) - The ID of a project # id (required) - The ID of a project
# hook_id (required) - The ID of hook to delete # hook_id (required) - The ID of hook to delete
# Example Request: # Example Request:
# DELETE /projects/:id/hooks/:hook_id # DELETE /projects/:id/hooks/:hook_id
delete ":id/hooks/:hook_id" do delete ":id/hooks" do
authorize! :admin_project, user_project authorize! :admin_project, user_project
@hook = user_project.hooks.find(params[:hook_id]) required_attributes! [:hook_id]
@hook.destroy
begin
@hook = ProjectHook.find(params[:hook_id])
@hook.destroy
rescue
# ProjectHook can raise Error if hook_id not found
end
end end
# Get a project repository branches # Get a project repository branches
...@@ -277,6 +318,7 @@ module Gitlab ...@@ -277,6 +318,7 @@ module Gitlab
# PUT /projects/:id/repository/branches/:branch/protect # PUT /projects/:id/repository/branches/:branch/protect
put ":id/repository/branches/:branch/protect" do put ":id/repository/branches/:branch/protect" do
@branch = user_project.repo.heads.find { |item| item.name == params[:branch] } @branch = user_project.repo.heads.find { |item| item.name == params[:branch] }
not_found! unless @branch
protected = user_project.protected_branches.find_by_name(@branch.name) protected = user_project.protected_branches.find_by_name(@branch.name)
unless protected unless protected
...@@ -295,6 +337,7 @@ module Gitlab ...@@ -295,6 +337,7 @@ module Gitlab
# PUT /projects/:id/repository/branches/:branch/unprotect # PUT /projects/:id/repository/branches/:branch/unprotect
put ":id/repository/branches/:branch/unprotect" do put ":id/repository/branches/:branch/unprotect" do
@branch = user_project.repo.heads.find { |item| item.name == params[:branch] } @branch = user_project.repo.heads.find { |item| item.name == params[:branch] }
not_found! unless @branch
protected = user_project.protected_branches.find_by_name(@branch.name) protected = user_project.protected_branches.find_by_name(@branch.name)
if protected if protected
...@@ -318,7 +361,7 @@ module Gitlab ...@@ -318,7 +361,7 @@ module Gitlab
# #
# Parameters: # Parameters:
# id (required) - The ID of a project # id (required) - The ID of a project
# ref_name (optional) - The name of a repository branch or tag # ref_name (optional) - The name of a repository branch or tag, if not given the default branch is used
# Example Request: # Example Request:
# GET /projects/:id/repository/commits # GET /projects/:id/repository/commits
get ":id/repository/commits" do get ":id/repository/commits" do
...@@ -366,6 +409,7 @@ module Gitlab ...@@ -366,6 +409,7 @@ module Gitlab
# POST /projects/:id/snippets # POST /projects/:id/snippets
post ":id/snippets" do post ":id/snippets" do
authorize! :write_snippet, user_project authorize! :write_snippet, user_project
required_attributes! [:title, :file_name, :code]
attrs = attributes_for_keys [:title, :file_name] attrs = attributes_for_keys [:title, :file_name]
attrs[:expires_at] = params[:lifetime] if params[:lifetime].present? attrs[:expires_at] = params[:lifetime] if params[:lifetime].present?
...@@ -414,10 +458,12 @@ module Gitlab ...@@ -414,10 +458,12 @@ module Gitlab
# Example Request: # Example Request:
# DELETE /projects/:id/snippets/:snippet_id # DELETE /projects/:id/snippets/:snippet_id
delete ":id/snippets/:snippet_id" do delete ":id/snippets/:snippet_id" do
@snippet = user_project.snippets.find(params[:snippet_id]) begin
authorize! :modify_snippet, @snippet @snippet = user_project.snippets.find(params[:snippet_id])
authorize! :modify_snippet, user_project
@snippet.destroy @snippet.destroy
rescue
end
end end
# Get a raw project snippet # Get a raw project snippet
...@@ -443,6 +489,7 @@ module Gitlab ...@@ -443,6 +489,7 @@ module Gitlab
# GET /projects/:id/repository/commits/:sha/blob # GET /projects/:id/repository/commits/:sha/blob
get ":id/repository/commits/:sha/blob" do get ":id/repository/commits/:sha/blob" do
authorize! :download_code, user_project authorize! :download_code, user_project
required_attributes! [:filepath]
ref = params[:sha] ref = params[:sha]
......
...@@ -41,6 +41,8 @@ module Gitlab ...@@ -41,6 +41,8 @@ module Gitlab
# POST /users # POST /users
post do post do
authenticated_as_admin! authenticated_as_admin!
required_attributes! [:email, :password, :name, :username]
attrs = attributes_for_keys [:email, :name, :password, :skype, :linkedin, :twitter, :projects_limit, :username, :extern_uid, :provider, :bio] attrs = attributes_for_keys [:email, :name, :password, :skype, :linkedin, :twitter, :projects_limit, :username, :extern_uid, :provider, :bio]
user = User.new attrs, as: :admin user = User.new attrs, as: :admin
if user.save if user.save
...@@ -67,10 +69,12 @@ module Gitlab ...@@ -67,10 +69,12 @@ module Gitlab
# PUT /users/:id # PUT /users/:id
put ":id" do put ":id" do
authenticated_as_admin! authenticated_as_admin!
attrs = attributes_for_keys [:email, :name, :password, :skype, :linkedin, :twitter, :projects_limit, :username, :extern_uid, :provider, :bio] attrs = attributes_for_keys [:email, :name, :password, :skype, :linkedin, :twitter, :projects_limit, :username, :extern_uid, :provider, :bio]
user = User.find_by_id(params[:id]) user = User.find(params[:id])
not_found!("User not found") unless user
if user && user.update_attributes(attrs) if user.update_attributes(attrs)
present user, with: Entities::User present user, with: Entities::User
else else
not_found! not_found!
...@@ -147,6 +151,8 @@ module Gitlab ...@@ -147,6 +151,8 @@ module Gitlab
# Example Request: # Example Request:
# POST /user/keys # POST /user/keys
post "keys" do post "keys" do
required_attributes! [:title, :key]
attrs = attributes_for_keys [:title, :key] attrs = attributes_for_keys [:title, :key]
key = current_user.keys.new attrs key = current_user.keys.new attrs
if key.save if key.save
...@@ -156,15 +162,18 @@ module Gitlab ...@@ -156,15 +162,18 @@ module Gitlab
end end
end end
# Delete existed ssh key of currently authenticated user # Delete existing ssh key of currently authenticated user
# #
# Parameters: # Parameters:
# id (required) - SSH Key ID # id (required) - SSH Key ID
# Example Request: # Example Request:
# DELETE /user/keys/:id # DELETE /user/keys/:id
delete "keys/:id" do delete "keys/:id" do
key = current_user.keys.find params[:id] begin
key.delete key = current_user.keys.find params[:id]
key.delete
rescue
end
end end
end end
end end
......
...@@ -65,7 +65,7 @@ describe Project do ...@@ -65,7 +65,7 @@ describe Project do
it "should not allow new projects beyond user limits" do it "should not allow new projects beyond user limits" do
project.stub(:creator).and_return(double(can_create_project?: false, projects_limit: 1)) project.stub(:creator).and_return(double(can_create_project?: false, projects_limit: 1))
project.should_not be_valid project.should_not be_valid
project.errors[:base].first.should match(/Your own projects limit is 1/) project.errors[:limit_reached].first.should match(/Your own projects limit is 1/)
end end
end end
......
...@@ -88,6 +88,16 @@ describe Gitlab::API do ...@@ -88,6 +88,16 @@ describe Gitlab::API do
post api("/groups", admin), {:name => "Duplicate Test", :path => group2.path} post api("/groups", admin), {:name => "Duplicate Test", :path => group2.path}
response.status.should == 404 response.status.should == 404
end end
it "should return 400 bad request error if name not given" do
post api("/groups", admin), { :path => group2.path }
response.status.should == 400
end
it "should return 400 bad request error if path not given" do
post api("/groups", admin), { :name => 'test' }
response.status.should == 400
end
end end
end end
......
...@@ -41,6 +41,11 @@ describe Gitlab::API do ...@@ -41,6 +41,11 @@ describe Gitlab::API do
response.status.should == 200 response.status.should == 200
json_response['title'].should == issue.title json_response['title'].should == issue.title
end end
it "should return 404 if issue id not found" do
get api("/projects/#{project.id}/issues/54321", user)
response.status.should == 404
end
end end
describe "POST /projects/:id/issues" do describe "POST /projects/:id/issues" do
...@@ -52,6 +57,11 @@ describe Gitlab::API do ...@@ -52,6 +57,11 @@ describe Gitlab::API do
json_response['description'].should be_nil json_response['description'].should be_nil
json_response['labels'].should == ['label', 'label2'] json_response['labels'].should == ['label', 'label2']
end end
it "should return a 400 bad request if title not given" do
post api("/projects/#{project.id}/issues", user), labels: 'label, label2'
response.status.should == 400
end
end end
describe "PUT /projects/:id/issues/:issue_id to update only title" do describe "PUT /projects/:id/issues/:issue_id to update only title" do
...@@ -62,6 +72,12 @@ describe Gitlab::API do ...@@ -62,6 +72,12 @@ describe Gitlab::API do
json_response['title'].should == 'updated title' json_response['title'].should == 'updated title'
end end
it "should return 404 error if issue id not found" do
put api("/projects/#{project.id}/issues/44444", user),
title: 'updated title'
response.status.should == 404
end
end end
describe "PUT /projects/:id/issues/:issue_id to update state and label" do describe "PUT /projects/:id/issues/:issue_id to update state and label" do
......
...@@ -32,6 +32,11 @@ describe Gitlab::API do ...@@ -32,6 +32,11 @@ describe Gitlab::API do
response.status.should == 200 response.status.should == 200
json_response['title'].should == merge_request.title json_response['title'].should == merge_request.title
end end
it "should return a 404 error if merge_request_id not found" do
get api("/projects/#{project.id}/merge_request/999", user)
response.status.should == 404
end
end end
describe "POST /projects/:id/merge_requests" do describe "POST /projects/:id/merge_requests" do
...@@ -41,6 +46,30 @@ describe Gitlab::API do ...@@ -41,6 +46,30 @@ describe Gitlab::API do
response.status.should == 201 response.status.should == 201
json_response['title'].should == 'Test merge_request' json_response['title'].should == 'Test merge_request'
end end
it "should return 422 when source_branch equals target_branch" do
post api("/projects/#{project.id}/merge_requests", user),
title: "Test merge_request", source_branch: "master", target_branch: "master", author: user
response.status.should == 422
end
it "should return 400 when source_branch is missing" do
post api("/projects/#{project.id}/merge_requests", user),
title: "Test merge_request", target_branch: "master", author: user
response.status.should == 400
end
it "should return 400 when target_branch is missing" do
post api("/projects/#{project.id}/merge_requests", user),
title: "Test merge_request", source_branch: "stable", author: user
response.status.should == 400
end
it "should return 400 when title is missing" do
post api("/projects/#{project.id}/merge_requests", user),
target_branch: 'master', source_branch: 'stable'
response.status.should == 400
end
end end
describe "PUT /projects/:id/merge_request/:merge_request_id to close MR" do describe "PUT /projects/:id/merge_request/:merge_request_id to close MR" do
...@@ -59,13 +88,24 @@ describe Gitlab::API do ...@@ -59,13 +88,24 @@ describe Gitlab::API do
end end
end end
describe "PUT /projects/:id/merge_request/:merge_request_id" do describe "PUT /projects/:id/merge_request/:merge_request_id" do
it "should return merge_request" do it "should return merge_request" do
put api("/projects/#{project.id}/merge_request/#{merge_request.id}", user), title: "New title" put api("/projects/#{project.id}/merge_request/#{merge_request.id}", user), title: "New title"
response.status.should == 200 response.status.should == 200
json_response['title'].should == 'New title' json_response['title'].should == 'New title'
end end
it "should return 422 when source_branch and target_branch are renamed the same" do
put api("/projects/#{project.id}/merge_request/#{merge_request.id}", user),
source_branch: "master", target_branch: "master"
response.status.should == 422
end
it "should return merge_request with renamed target_branch" do
put api("/projects/#{project.id}/merge_request/#{merge_request.id}", user), target_branch: "test"
response.status.should == 200
json_response['target_branch'].should == 'test'
end
end end
describe "POST /projects/:id/merge_request/:merge_request_id/comments" do describe "POST /projects/:id/merge_request/:merge_request_id/comments" do
...@@ -74,6 +114,16 @@ describe Gitlab::API do ...@@ -74,6 +114,16 @@ describe Gitlab::API do
response.status.should == 201 response.status.should == 201
json_response['note'].should == 'My comment' json_response['note'].should == 'My comment'
end end
it "should return 400 if note is missing" do
post api("/projects/#{project.id}/merge_request/#{merge_request.id}/comments", user)
response.status.should == 400
end
it "should return 404 if note is attached to non existent merge request" do
post api("/projects/#{project.id}/merge_request/111/comments", user), note: "My comment"
response.status.should == 404
end
end end
end end
...@@ -16,6 +16,11 @@ describe Gitlab::API do ...@@ -16,6 +16,11 @@ describe Gitlab::API do
json_response.should be_an Array json_response.should be_an Array
json_response.first['title'].should == milestone.title json_response.first['title'].should == milestone.title
end end
it "should return a 401 error if user not authenticated" do
get api("/projects/#{project.id}/milestones")
response.status.should == 401
end
end end
describe "GET /projects/:id/milestones/:milestone_id" do describe "GET /projects/:id/milestones/:milestone_id" do
...@@ -24,16 +29,38 @@ describe Gitlab::API do ...@@ -24,16 +29,38 @@ describe Gitlab::API do
response.status.should == 200 response.status.should == 200
json_response['title'].should == milestone.title json_response['title'].should == milestone.title
end end
it "should return 401 error if user not authenticated" do
get api("/projects/#{project.id}/milestones/#{milestone.id}")
response.status.should == 401
end
it "should return a 404 error if milestone id not found" do
get api("/projects/#{project.id}/milestones/1234", user)
response.status.should == 404
end
end end
describe "POST /projects/:id/milestones" do describe "POST /projects/:id/milestones" do
it "should create a new project milestone" do it "should create a new project milestone" do
post api("/projects/#{project.id}/milestones", user), post api("/projects/#{project.id}/milestones", user), title: 'new milestone'
title: 'new milestone'
response.status.should == 201 response.status.should == 201
json_response['title'].should == 'new milestone' json_response['title'].should == 'new milestone'
json_response['description'].should be_nil json_response['description'].should be_nil
end end
it "should create a new project milestone with description and due date" do
post api("/projects/#{project.id}/milestones", user),
title: 'new milestone', description: 'release', due_date: '2013-03-02'
response.status.should == 201
json_response['description'].should == 'release'
json_response['due_date'].should == '2013-03-02'
end
it "should return a 400 error if title is missing" do
post api("/projects/#{project.id}/milestones", user)
response.status.should == 400
end
end end
describe "PUT /projects/:id/milestones/:milestone_id" do describe "PUT /projects/:id/milestones/:milestone_id" do
...@@ -43,6 +70,12 @@ describe Gitlab::API do ...@@ -43,6 +70,12 @@ describe Gitlab::API do
response.status.should == 200 response.status.should == 200
json_response['title'].should == 'updated title' json_response['title'].should == 'updated title'
end end
it "should return a 404 error if milestone id not found" do
put api("/projects/#{project.id}/milestones/1234", user),
title: 'updated title'
response.status.should == 404
end
end end
describe "PUT /projects/:id/milestones/:milestone_id to close milestone" do describe "PUT /projects/:id/milestones/:milestone_id to close milestone" do
......
...@@ -38,6 +38,11 @@ describe Gitlab::API do ...@@ -38,6 +38,11 @@ describe Gitlab::API do
response.status.should == 200 response.status.should == 200
json_response['body'].should == wall_note.note json_response['body'].should == wall_note.note
end end
it "should return a 404 error if note not found" do
get api("/projects/#{project.id}/notes/123", user)
response.status.should == 404
end
end end
describe "POST /projects/:id/notes" do describe "POST /projects/:id/notes" do
...@@ -46,6 +51,16 @@ describe Gitlab::API do ...@@ -46,6 +51,16 @@ describe Gitlab::API do
response.status.should == 201 response.status.should == 201
json_response['body'].should == 'hi!' json_response['body'].should == 'hi!'
end end
it "should return 401 unauthorized error" do
post api("/projects/#{project.id}/notes")
response.status.should == 401
end
it "should return a 400 bad request if body is missing" do
post api("/projects/#{project.id}/notes", user)
response.status.should == 400
end
end end
describe "GET /projects/:id/noteable/:noteable_id/notes" do describe "GET /projects/:id/noteable/:noteable_id/notes" do
...@@ -56,6 +71,11 @@ describe Gitlab::API do ...@@ -56,6 +71,11 @@ describe Gitlab::API do
json_response.should be_an Array json_response.should be_an Array
json_response.first['body'].should == issue_note.note json_response.first['body'].should == issue_note.note
end end
it "should return a 404 error when issue id not found" do
get api("/projects/#{project.id}/issues/123/notes", user)
response.status.should == 404
end
end end
context "when noteable is a Snippet" do context "when noteable is a Snippet" do
...@@ -65,6 +85,11 @@ describe Gitlab::API do ...@@ -65,6 +85,11 @@ describe Gitlab::API do
json_response.should be_an Array json_response.should be_an Array
json_response.first['body'].should == snippet_note.note json_response.first['body'].should == snippet_note.note
end end
it "should return a 404 error when snippet id not found" do
get api("/projects/#{project.id}/snippets/42/notes", user)
response.status.should == 404
end
end end
context "when noteable is a Merge Request" do context "when noteable is a Merge Request" do
...@@ -74,6 +99,11 @@ describe Gitlab::API do ...@@ -74,6 +99,11 @@ describe Gitlab::API do
json_response.should be_an Array json_response.should be_an Array
json_response.first['body'].should == merge_request_note.note json_response.first['body'].should == merge_request_note.note
end end
it "should return a 404 error if merge request id not found" do
get api("/projects/#{project.id}/merge_requests/4444/notes", user)
response.status.should == 404
end
end end
end end
...@@ -84,6 +114,11 @@ describe Gitlab::API do ...@@ -84,6 +114,11 @@ describe Gitlab::API do
response.status.should == 200 response.status.should == 200
json_response['body'].should == issue_note.note json_response['body'].should == issue_note.note
end end
it "should return a 404 error if issue note not found" do
get api("/projects/#{project.id}/issues/#{issue.id}/notes/123", user)
response.status.should == 404
end
end end
context "when noteable is a Snippet" do context "when noteable is a Snippet" do
...@@ -92,6 +127,11 @@ describe Gitlab::API do ...@@ -92,6 +127,11 @@ describe Gitlab::API do
response.status.should == 200 response.status.should == 200
json_response['body'].should == snippet_note.note json_response['body'].should == snippet_note.note
end end
it "should return a 404 error if snippet note not found" do
get api("/projects/#{project.id}/snippets/#{snippet.id}/notes/123", user)
response.status.should == 404
end
end end
end end
...@@ -103,6 +143,16 @@ describe Gitlab::API do ...@@ -103,6 +143,16 @@ describe Gitlab::API do
json_response['body'].should == 'hi!' json_response['body'].should == 'hi!'
json_response['author']['email'].should == user.email json_response['author']['email'].should == user.email
end end
it "should return a 400 bad request error if body not given" do
post api("/projects/#{project.id}/issues/#{issue.id}/notes", user)
response.status.should == 400
end
it "should return a 401 unauthorized error if user not authenticated" do
post api("/projects/#{project.id}/issues/#{issue.id}/notes"), body: 'hi!'
response.status.should == 401
end
end end
context "when noteable is a Snippet" do context "when noteable is a Snippet" do
...@@ -112,6 +162,16 @@ describe Gitlab::API do ...@@ -112,6 +162,16 @@ describe Gitlab::API do
json_response['body'].should == 'hi!' json_response['body'].should == 'hi!'
json_response['author']['email'].should == user.email json_response['author']['email'].should == user.email
end end
it "should return a 400 bad request error if body not given" do
post api("/projects/#{project.id}/snippets/#{snippet.id}/notes", user)
response.status.should == 400
end
it "should return a 401 unauthorized error if user not authenticated" do
post api("/projects/#{project.id}/snippets/#{snippet.id}/notes"), body: 'hi!'
response.status.should == 401
end
end end
end end
end end
...@@ -7,8 +7,8 @@ describe Gitlab::API do ...@@ -7,8 +7,8 @@ describe Gitlab::API do
let(:user2) { create(:user) } let(:user2) { create(:user) }
let(:user3) { create(:user) } let(:user3) { create(:user) }
let(:admin) { create(:admin) } let(:admin) { create(:admin) }
let!(:hook) { create(:project_hook, project: project, url: "http://example.com") }
let!(:project) { create(:project, namespace: user.namespace ) } let!(:project) { create(:project, namespace: user.namespace ) }
let!(:hook) { create(:project_hook, project: project, url: "http://example.com") }
let!(:snippet) { create(:snippet, author: user, project: project, title: 'example') } let!(:snippet) { create(:snippet, author: user, project: project, title: 'example') }
let!(:users_project) { create(:users_project, user: user, project: project, project_access: UsersProject::MASTER) } let!(:users_project) { create(:users_project, user: user, project: project, project_access: UsersProject::MASTER) }
let!(:users_project2) { create(:users_project, user: user3, project: project, project_access: UsersProject::DEVELOPER) } let!(:users_project2) { create(:users_project, user: user3, project: project, project_access: UsersProject::DEVELOPER) }
...@@ -58,6 +58,11 @@ describe Gitlab::API do ...@@ -58,6 +58,11 @@ describe Gitlab::API do
expect { post api("/projects", user) }.to_not change {Project.count} expect { post api("/projects", user) }.to_not change {Project.count}
end end
it "should return a 400 error if name not given" do
post api("/projects", user)
response.status.should == 400
end
it "should create last project before reaching project limit" do it "should create last project before reaching project limit" do
(1..user2.projects_limit-1).each { |p| post api("/projects", user2), name: "foo#{p}" } (1..user2.projects_limit-1).each { |p| post api("/projects", user2), name: "foo#{p}" }
post api("/projects", user2), name: "foo" post api("/projects", user2), name: "foo"
...@@ -69,9 +74,17 @@ describe Gitlab::API do ...@@ -69,9 +74,17 @@ describe Gitlab::API do
response.status.should == 201 response.status.should == 201
end end
it "should respond with 404 on failure" do it "should respond with 400 if name is not given" do
post api("/projects", user) post api("/projects", user)
response.status.should == 404 response.status.should == 400
end
it "should return a 403 error if project limit reached" do
(1..user.projects_limit).each do |p|
post api("/projects", user), name: "foo#{p}"
end
post api("/projects", user), name: 'bar'
response.status.should == 403
end end
it "should assign attributes to project" do it "should assign attributes to project" do
...@@ -152,6 +165,12 @@ describe Gitlab::API do ...@@ -152,6 +165,12 @@ describe Gitlab::API do
response.status.should == 404 response.status.should == 404
json_response['message'].should == '404 Not Found' json_response['message'].should == '404 Not Found'
end end
it "should return a 404 error if user is not a member" do
other_user = create(:user)
get api("/projects/#{project.id}", other_user)
response.status.should == 404
end
end end
describe "GET /projects/:id/repository/branches" do describe "GET /projects/:id/repository/branches" do
...@@ -188,6 +207,17 @@ describe Gitlab::API do ...@@ -188,6 +207,17 @@ describe Gitlab::API do
json_response['commit']['id'].should == '621491c677087aa243f165eab467bfdfbee00be1' json_response['commit']['id'].should == '621491c677087aa243f165eab467bfdfbee00be1'
json_response['protected'].should == true json_response['protected'].should == true
end end
it "should return a 404 error if branch not found" do
put api("/projects/#{project.id}/repository/branches/unknown/protect", user)
response.status.should == 404
end
it "should return success when protect branch again" do
put api("/projects/#{project.id}/repository/branches/new_design/protect", user)
put api("/projects/#{project.id}/repository/branches/new_design/protect", user)
response.status.should == 200
end
end end
describe "PUT /projects/:id/repository/branches/:branch/unprotect" do describe "PUT /projects/:id/repository/branches/:branch/unprotect" do
...@@ -199,6 +229,17 @@ describe Gitlab::API do ...@@ -199,6 +229,17 @@ describe Gitlab::API do
json_response['commit']['id'].should == '621491c677087aa243f165eab467bfdfbee00be1' json_response['commit']['id'].should == '621491c677087aa243f165eab467bfdfbee00be1'
json_response['protected'].should == false json_response['protected'].should == false
end end
it "should return success when unprotect branch" do
put api("/projects/#{project.id}/repository/branches/unknown/unprotect", user)
response.status.should == 404
end
it "should return success when unprotect branch again" do
put api("/projects/#{project.id}/repository/branches/new_design/unprotect", user)
put api("/projects/#{project.id}/repository/branches/new_design/unprotect", user)
response.status.should == 200
end
end end
describe "GET /projects/:id/members" do describe "GET /projects/:id/members" do
...@@ -217,6 +258,11 @@ describe Gitlab::API do ...@@ -217,6 +258,11 @@ describe Gitlab::API do
json_response.count.should == 1 json_response.count.should == 1
json_response.first['email'].should == user.email json_response.first['email'].should == user.email
end end
it "should return a 404 error if id not found" do
get api("/projects/9999/members", user)
response.status.should == 404
end
end end
describe "GET /projects/:id/members/:user_id" do describe "GET /projects/:id/members/:user_id" do
...@@ -226,6 +272,11 @@ describe Gitlab::API do ...@@ -226,6 +272,11 @@ describe Gitlab::API do
json_response['email'].should == user.email json_response['email'].should == user.email
json_response['access_level'].should == UsersProject::MASTER json_response['access_level'].should == UsersProject::MASTER
end end
it "should return a 404 error if user id not found" do
get api("/projects/#{project.id}/members/1234", user)
response.status.should == 404
end
end end
describe "POST /projects/:id/members" do describe "POST /projects/:id/members" do
...@@ -239,6 +290,34 @@ describe Gitlab::API do ...@@ -239,6 +290,34 @@ describe Gitlab::API do
json_response['email'].should == user2.email json_response['email'].should == user2.email
json_response['access_level'].should == UsersProject::DEVELOPER json_response['access_level'].should == UsersProject::DEVELOPER
end end
it "should return a 201 status if user is already project member" do
post api("/projects/#{project.id}/members", user), user_id: user2.id,
access_level: UsersProject::DEVELOPER
expect {
post api("/projects/#{project.id}/members", user), user_id: user2.id,
access_level: UsersProject::DEVELOPER
}.not_to change { UsersProject.count }.by(1)
response.status.should == 201
json_response['email'].should == user2.email
json_response['access_level'].should == UsersProject::DEVELOPER
end
it "should return a 400 error when user id is not given" do
post api("/projects/#{project.id}/members", user), access_level: UsersProject::MASTER
response.status.should == 400
end
it "should return a 400 error when access level is not given" do
post api("/projects/#{project.id}/members", user), user_id: user2.id
response.status.should == 400
end
it "should return a 422 error when access level is not known" do
post api("/projects/#{project.id}/members", user), user_id: user2.id, access_level: 1234
response.status.should == 422
end
end end
describe "PUT /projects/:id/members/:user_id" do describe "PUT /projects/:id/members/:user_id" do
...@@ -248,6 +327,21 @@ describe Gitlab::API do ...@@ -248,6 +327,21 @@ describe Gitlab::API do
json_response['email'].should == user3.email json_response['email'].should == user3.email
json_response['access_level'].should == UsersProject::MASTER json_response['access_level'].should == UsersProject::MASTER
end end
it "should return a 404 error if user_id is not found" do
put api("/projects/#{project.id}/members/1234", user), access_level: UsersProject::MASTER
response.status.should == 404
end
it "should return a 400 error when access level is not given" do
put api("/projects/#{project.id}/members/#{user3.id}", user)
response.status.should == 400
end
it "should return a 422 error when access level is not known" do
put api("/projects/#{project.id}/members/#{user3.id}", user), access_level: 123
response.status.should == 422
end
end end
describe "DELETE /projects/:id/members/:user_id" do describe "DELETE /projects/:id/members/:user_id" do
...@@ -256,6 +350,30 @@ describe Gitlab::API do ...@@ -256,6 +350,30 @@ describe Gitlab::API do
delete api("/projects/#{project.id}/members/#{user3.id}", user) delete api("/projects/#{project.id}/members/#{user3.id}", user)
}.to change { UsersProject.count }.by(-1) }.to change { UsersProject.count }.by(-1)
end end
it "should return 200 if team member is not part of a project" do
delete api("/projects/#{project.id}/members/#{user3.id}", user)
expect {
delete api("/projects/#{project.id}/members/#{user3.id}", user)
}.to_not change { UsersProject.count }.by(1)
end
it "should return 200 if team member already removed" do
delete api("/projects/#{project.id}/members/#{user3.id}", user)
delete api("/projects/#{project.id}/members/#{user3.id}", user)
response.status.should == 200
end
end
describe "DELETE /projects/:id/members/:user_id" do
it "should return 200 OK when the user was not member" do
expect {
delete api("/projects/#{project.id}/members/1000000", user)
}.to change { UsersProject.count }.by(0)
response.status.should == 200
json_response['message'].should == "Access revoked"
json_response['id'].should == 1000000
end
end end
describe "GET /projects/:id/hooks" do describe "GET /projects/:id/hooks" do
...@@ -298,6 +416,11 @@ describe Gitlab::API do ...@@ -298,6 +416,11 @@ describe Gitlab::API do
response.status.should == 403 response.status.should == 403
end end
end end
it "should return a 404 error if hook id is not available" do
get api("/projects/#{project.id}/hooks/1234", user)
response.status.should == 404
end
end end
describe "POST /projects/:id/hooks" do describe "POST /projects/:id/hooks" do
...@@ -306,6 +429,17 @@ describe Gitlab::API do ...@@ -306,6 +429,17 @@ describe Gitlab::API do
post api("/projects/#{project.id}/hooks", user), post api("/projects/#{project.id}/hooks", user),
url: "http://example.com" url: "http://example.com"
}.to change {project.hooks.count}.by(1) }.to change {project.hooks.count}.by(1)
response.status.should == 201
end
it "should return a 400 error if url not given" do
post api("/projects/#{project.id}/hooks", user)
response.status.should == 400
end
it "should return a 422 error if url not valid" do
post api("/projects/#{project.id}/hooks", user), "url" => "ftp://example.com"
response.status.should == 422
end end
end end
...@@ -316,13 +450,44 @@ describe Gitlab::API do ...@@ -316,13 +450,44 @@ describe Gitlab::API do
response.status.should == 200 response.status.should == 200
json_response['url'].should == 'http://example.org' json_response['url'].should == 'http://example.org'
end end
it "should return 404 error if hook id not found" do
put api("/projects/#{project.id}/hooks/1234", user), url: 'http://example.org'
response.status.should == 404
end
it "should return 400 error if url is not given" do
put api("/projects/#{project.id}/hooks/#{hook.id}", user)
response.status.should == 400
end
it "should return a 422 error if url is not valid" do
put api("/projects/#{project.id}/hooks/#{hook.id}", user), url: 'ftp://example.com'
response.status.should == 422
end
end end
describe "DELETE /projects/:id/hooks/:hook_id" do describe "DELETE /projects/:id/hooks" do
it "should delete hook from project" do it "should delete hook from project" do
expect { expect {
delete api("/projects/#{project.id}/hooks/#{hook.id}", user) delete api("/projects/#{project.id}/hooks", user), hook_id: hook.id
}.to change {project.hooks.count}.by(-1) }.to change {project.hooks.count}.by(-1)
response.status.should == 200
end
it "should return success when deleting hook" do
delete api("/projects/#{project.id}/hooks", user), hook_id: hook.id
response.status.should == 200
end
it "should return success when deleting non existent hook" do
delete api("/projects/#{project.id}/hooks", user), hook_id: 42
response.status.should == 200
end
it "should return a 400 error if hook id not given" do
delete api("/projects/#{project.id}/hooks", user)
response.status.should == 400
end end
end end
...@@ -371,6 +536,11 @@ describe Gitlab::API do ...@@ -371,6 +536,11 @@ describe Gitlab::API do
response.status.should == 200 response.status.should == 200
json_response['title'].should == snippet.title json_response['title'].should == snippet.title
end end
it "should return a 404 error if snippet id not found" do
get api("/projects/#{project.id}/snippets/1234", user)
response.status.should == 404
end
end end
describe "POST /projects/:id/snippets" do describe "POST /projects/:id/snippets" do
...@@ -380,6 +550,24 @@ describe Gitlab::API do ...@@ -380,6 +550,24 @@ describe Gitlab::API do
response.status.should == 201 response.status.should == 201
json_response['title'].should == 'api test' json_response['title'].should == 'api test'
end end
it "should return a 400 error if title is not given" do
post api("/projects/#{project.id}/snippets", user),
file_name: 'sample.rb', code: 'test'
response.status.should == 400
end
it "should return a 400 error if file_name not given" do
post api("/projects/#{project.id}/snippets", user),
title: 'api test', code: 'test'
response.status.should == 400
end
it "should return a 400 error if code not given" do
post api("/projects/#{project.id}/snippets", user),
title: 'api test', file_name: 'sample.rb'
response.status.should == 400
end
end end
describe "PUT /projects/:id/snippets/:shippet_id" do describe "PUT /projects/:id/snippets/:shippet_id" do
...@@ -390,6 +578,13 @@ describe Gitlab::API do ...@@ -390,6 +578,13 @@ describe Gitlab::API do
json_response['title'].should == 'example' json_response['title'].should == 'example'
snippet.reload.content.should == 'updated code' snippet.reload.content.should == 'updated code'
end end
it "should update an existing project snippet with new title" do
put api("/projects/#{project.id}/snippets/#{snippet.id}", user),
title: 'other api test'
response.status.should == 200
json_response['title'].should == 'other api test'
end
end end
describe "DELETE /projects/:id/snippets/:snippet_id" do describe "DELETE /projects/:id/snippets/:snippet_id" do
...@@ -397,6 +592,12 @@ describe Gitlab::API do ...@@ -397,6 +592,12 @@ describe Gitlab::API do
expect { expect {
delete api("/projects/#{project.id}/snippets/#{snippet.id}", user) delete api("/projects/#{project.id}/snippets/#{snippet.id}", user)
}.to change { Snippet.count }.by(-1) }.to change { Snippet.count }.by(-1)
response.status.should == 200
end
it "should return success when deleting unknown snippet id" do
delete api("/projects/#{project.id}/snippets/1234", user)
response.status.should == 200
end end
end end
...@@ -405,9 +606,14 @@ describe Gitlab::API do ...@@ -405,9 +606,14 @@ describe Gitlab::API do
get api("/projects/#{project.id}/snippets/#{snippet.id}/raw", user) get api("/projects/#{project.id}/snippets/#{snippet.id}/raw", user)
response.status.should == 200 response.status.should == 200
end end
it "should return a 404 error if raw project snippet not found" do
get api("/projects/#{project.id}/snippets/5555/raw", user)
response.status.should == 404
end
end end
describe "GET /projects/:id/:sha/blob" do describe "GET /projects/:id/repository/commits/:sha/blob" do
it "should get the raw file contents" do it "should get the raw file contents" do
get api("/projects/#{project.id}/repository/commits/master/blob?filepath=README.md", user) get api("/projects/#{project.id}/repository/commits/master/blob?filepath=README.md", user)
response.status.should == 200 response.status.should == 200
...@@ -422,6 +628,11 @@ describe Gitlab::API do ...@@ -422,6 +628,11 @@ describe Gitlab::API do
get api("/projects/#{project.id}/repository/commits/master/blob?filepath=README.invalid", user) get api("/projects/#{project.id}/repository/commits/master/blob?filepath=README.invalid", user)
response.status.should == 404 response.status.should == 404
end end
it "should return a 400 error if filepath is missing" do
get api("/projects/#{project.id}/repository/commits/master/blob", user)
response.status.should == 400
end
end end
describe "GET /projects/:id/keys" do describe "GET /projects/:id/keys" do
......
...@@ -35,5 +35,15 @@ describe Gitlab::API do ...@@ -35,5 +35,15 @@ describe Gitlab::API do
json_response['private_token'].should be_nil json_response['private_token'].should be_nil
end end
end end
context "when empty name" do
it "should return authentication error" do
post api("/session"), password: user.password
response.status.should == 401
json_response['email'].should be_nil
json_response['private_token'].should be_nil
end
end
end end
end end
...@@ -31,15 +31,20 @@ describe Gitlab::API do ...@@ -31,15 +31,20 @@ describe Gitlab::API do
response.status.should == 200 response.status.should == 200
json_response['email'].should == user.email json_response['email'].should == user.email
end end
end
describe "POST /users" do it "should return a 401 if unauthenticated" do
before{ admin } get api("/users/9998")
response.status.should == 401
end
it "should not create invalid user" do it "should return a 404 error if user id not found" do
post api("/users", admin), { email: "invalid email" } get api("/users/9999", user)
response.status.should == 404 response.status.should == 404
end end
end
describe "POST /users" do
before{ admin }
it "should create user" do it "should create user" do
expect { expect {
...@@ -47,10 +52,48 @@ describe Gitlab::API do ...@@ -47,10 +52,48 @@ describe Gitlab::API do
}.to change { User.count }.by(1) }.to change { User.count }.by(1)
end end
it "should return 201 Created on success" do
post api("/users", admin), attributes_for(:user, projects_limit: 3)
response.status.should == 201
end
it "should not create user with invalid email" do
post api("/users", admin), { email: "invalid email", password: 'password' }
response.status.should == 400
end
it "should return 400 error if password not given" do
post api("/users", admin), { email: 'test@example.com' }
response.status.should == 400
end
it "should return 400 error if email not given" do
post api("/users", admin), { password: 'pass1234' }
response.status.should == 400
end
it "shouldn't available for non admin users" do it "shouldn't available for non admin users" do
post api("/users", user), attributes_for(:user) post api("/users", user), attributes_for(:user)
response.status.should == 403 response.status.should == 403
end end
context "with existing user" do
before { post api("/users", admin), { email: 'test@example.com', password: 'password', username: 'test' } }
it "should not create user with same email" do
expect {
post api("/users", admin), { email: 'test@example.com', password: 'password' }
}.to change { User.count }.by(0)
end
it "should return 409 conflict error if user with email exists" do
post api("/users", admin), { email: 'test@example.com', password: 'password' }
end
it "should return 409 conflict error if same username exists" do
post api("/users", admin), { email: 'foo@example.com', password: 'pass', username: 'test' }
end
end
end end
describe "GET /users/sign_up" do describe "GET /users/sign_up" do
...@@ -81,7 +124,7 @@ describe Gitlab::API do ...@@ -81,7 +124,7 @@ describe Gitlab::API do
describe "PUT /users/:id" do describe "PUT /users/:id" do
before { admin } before { admin }
it "should update user" do it "should update user with new bio" do
put api("/users/#{user.id}", admin), {bio: 'new test bio'} put api("/users/#{user.id}", admin), {bio: 'new test bio'}
response.status.should == 200 response.status.should == 200
json_response['bio'].should == 'new test bio' json_response['bio'].should == 'new test bio'
...@@ -103,6 +146,25 @@ describe Gitlab::API do ...@@ -103,6 +146,25 @@ describe Gitlab::API do
put api("/users/999999", admin), {bio: 'update should fail'} put api("/users/999999", admin), {bio: 'update should fail'}
response.status.should == 404 response.status.should == 404
end end
context "with existing user" do
before {
post api("/users", admin), { email: 'test@example.com', password: 'password', username: 'test', name: 'test' }
post api("/users", admin), { email: 'foo@bar.com', password: 'password', username: 'john', name: 'john' }
@user_id = User.all.last.id
}
# it "should return 409 conflict error if email address exists" do
# put api("/users/#{@user_id}", admin), { email: 'test@example.com' }
# response.status.should == 409
# end
#
# it "should return 409 conflict error if username taken" do
# @user_id = User.all.last.id
# put api("/users/#{@user_id}", admin), { username: 'test' }
# response.status.should == 409
# end
end
end end
describe "POST /users/:id/keys" do describe "POST /users/:id/keys" do
...@@ -131,6 +193,11 @@ describe Gitlab::API do ...@@ -131,6 +193,11 @@ describe Gitlab::API do
json_response['email'].should == user.email json_response['email'].should == user.email
end end
it "should not delete for unauthenticated user" do
delete api("/users/#{user.id}")
response.status.should == 401
end
it "shouldn't available for non admin users" do it "shouldn't available for non admin users" do
delete api("/users/#{user.id}", user) delete api("/users/#{user.id}", user)
response.status.should == 403 response.status.should == 403
...@@ -148,6 +215,11 @@ describe Gitlab::API do ...@@ -148,6 +215,11 @@ describe Gitlab::API do
response.status.should == 200 response.status.should == 200
json_response['email'].should == user.email json_response['email'].should == user.email
end end
it "should return 401 error if user is unauthenticated" do
get api("/user")
response.status.should == 401
end
end end
describe "GET /user/keys" do describe "GET /user/keys" do
...@@ -183,19 +255,38 @@ describe Gitlab::API do ...@@ -183,19 +255,38 @@ describe Gitlab::API do
get api("/user/keys/42", user) get api("/user/keys/42", user)
response.status.should == 404 response.status.should == 404
end end
end
describe "POST /user/keys" do it "should return 404 error if admin accesses user's ssh key" do
it "should not create invalid ssh key" do user.keys << key
post api("/user/keys", user), { title: "invalid key" } user.save
admin
get api("/user/keys/#{key.id}", admin)
response.status.should == 404 response.status.should == 404
end end
end
describe "POST /user/keys" do
it "should create ssh key" do it "should create ssh key" do
key_attrs = attributes_for :key key_attrs = attributes_for :key
expect { expect {
post api("/user/keys", user), key_attrs post api("/user/keys", user), key_attrs
}.to change{ user.keys.count }.by(1) }.to change{ user.keys.count }.by(1)
response.status.should == 201
end
it "should return a 401 error if unauthorized" do
post api("/user/keys"), title: 'some title', key: 'some key'
response.status.should == 401
end
it "should not create ssh key without key" do
post api("/user/keys", user), title: 'title'
response.status.should == 400
end
it "should not create ssh key without title" do
post api("/user/keys", user), key: "somekey"
response.status.should == 400
end end
end end
...@@ -206,11 +297,19 @@ describe Gitlab::API do ...@@ -206,11 +297,19 @@ describe Gitlab::API do
expect { expect {
delete api("/user/keys/#{key.id}", user) delete api("/user/keys/#{key.id}", user)
}.to change{user.keys.count}.by(-1) }.to change{user.keys.count}.by(-1)
response.status.should == 200
end end
it "should return 404 Not Found within invalid ID" do it "should return sucess if key ID not found" do
delete api("/user/keys/42", user) delete api("/user/keys/42", user)
response.status.should == 404 response.status.should == 200
end
it "should return 401 error if unauthorized" do
user.keys << key
user.save
delete api("/user/keys/#{key.id}")
response.status.should == 401
end end
end end
end end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment