diff options
| -rw-r--r-- | app/models/project.rb | 2 | ||||
| -rw-r--r-- | config/routes.rb | 1 | ||||
| -rw-r--r-- | doc/api/notes.md | 10 | ||||
| -rw-r--r-- | doc/api/projects.md | 382 | ||||
| -rw-r--r-- | doc/api/users.md | 131 | ||||
| -rw-r--r-- | lib/api.rb | 13 | ||||
| -rw-r--r-- | lib/api/helpers.rb | 6 | ||||
| -rw-r--r-- | lib/api/merge_requests.rb | 22 | ||||
| -rw-r--r-- | lib/api/milestones.rb | 16 | ||||
| -rw-r--r-- | lib/api/notes.rb | 2 | ||||
| -rw-r--r-- | lib/api/projects.rb | 96 | ||||
| -rw-r--r-- | lib/api/users.rb | 24 | ||||
| -rw-r--r-- | spec/requests/api/merge_requests_spec.rb | 45 | ||||
| -rw-r--r-- | spec/requests/api/milestones_spec.rb | 24 | ||||
| -rw-r--r-- | spec/requests/api/notes_spec.rb | 30 | ||||
| -rw-r--r-- | spec/requests/api/projects_spec.rb | 220 | ||||
| -rw-r--r-- | spec/requests/api/users_spec.rb | 123 | 
17 files changed, 1041 insertions, 106 deletions
diff --git a/app/models/project.rb b/app/models/project.rb index b7d688bf8f5..54bfb88e915 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -155,7 +155,7 @@ class Project < ActiveRecord::Base    def check_limit      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    rescue      errors[:base] << ("Can't check your ability to create project") diff --git a/config/routes.rb b/config/routes.rb index 7537a11de96..10536a6e529 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -8,6 +8,7 @@ Gitlab::Application.routes.draw do    # API    require 'api' +  Gitlab::API.logger Rails.logger    mount Gitlab::API => '/api'    constraint = lambda { |request| request.env["warden"].authenticate? and request.env['warden'].user.admin? } diff --git a/doc/api/notes.md b/doc/api/notes.md index a4ba2826076..eef4b63fcaf 100644 --- a/doc/api/notes.md +++ b/doc/api/notes.md @@ -89,7 +89,7 @@ Parameters:  Get an issue note.  ``` -GET /projects/:id/issues/:issue_id/:notes/:note_id +GET /projects/:id/issues/:issue_id/notes/:note_id  ```  Parameters: @@ -103,7 +103,7 @@ Parameters:  Get a snippet note.  ``` -GET /projects/:id/issues/:snippet_id/:notes/:note_id +GET /projects/:id/issues/:snippet_id/notes/:note_id  ```  Parameters: @@ -127,7 +127,7 @@ Parameters:  + `id` (required) - The ID of a project  + `body` (required) - The content of a note -Will return created note with status `201 Created` on success, or `404 Not found` on fail. +Will return created note with status `201 Created` on success, `400 Bad Request` if the body attribute is missing or `404 Not found` on fail.  ### New issue note @@ -144,7 +144,7 @@ Parameters:  + `issue_id` (required) - The ID of an issue  + `body` (required) - The content of a note -Will return created note with status `201 Created` on success, or `404 Not found` on fail. +Will return created note with status `201 Created` on success, `400 Bad Request` if the body attribute is missing or `404 Not found` on fail.  ### New snippet note @@ -160,4 +160,4 @@ Parameters:  + `snippet_id` (required) - The ID of an snippet  + `body` (required) - The content of a note -Will return created note with status `201 Created` on success, or `404 Not found` on fail. +Will return created note with status `201 Created` on success, `400 Bad Request` if the body attribute is missing or `404 Not found` on fail. diff --git a/doc/api/projects.md b/doc/api/projects.md index 13c53880db0..cff4327a58c 100644 --- a/doc/api/projects.md +++ b/doc/api/projects.md @@ -1,4 +1,6 @@ -## List projects +## Projects + +### List projects  Get a list of projects owned by the authenticated user. @@ -55,7 +57,13 @@ GET /projects  ]  ``` -## Single project +Return values: + ++ `200 Ok` on success and a list of projects ++ `401 Unauthorized` if the user is not allowed to access projects + + +### Get single project  Get a specific project, identified by project ID, which is owned by the authentication user. @@ -92,9 +100,15 @@ Parameters:  }  ``` -## Create project +Return Values: + ++ `200 Ok` if the project with given ID is found and the JSON response ++ `404 Not Found` if no project with ID found + -Create new project owned by user +### Create project + +Creates new project owned by user.  ```  POST /projects @@ -110,10 +124,15 @@ Parameters:  + `merge_requests_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. +Return values: + ++ `201 Created` on success with the project data (see example at `GET /projects/:id`) ++ `400 Bad Request` if the required attribute name is not given ++ `403 Forbidden` if the user is not allowed to create a project, e.g. reached the project limit already ++ `404 Not Found` if something else fails + -## List project team members +### List project members  Get a list of project team members. @@ -126,7 +145,15 @@ Parameters:  + `id` (required) - The ID of a project  + `query`         - Query string -## Get project team member +Return Values: + ++ `200 Ok` on success and a list of found team members ++ `404 Not Found` if project with ID not found + + +## Team members + +### Get project team member  Get a project team member. @@ -141,7 +168,6 @@ Parameters:  ```json  { -    "id": 1,    "username": "john_smith",    "email": "john@example.com", @@ -152,9 +178,17 @@ Parameters:  }  ``` -## Add project team member +Return Values: + ++ `200 Ok` on success and the team member, see example ++ `404 Not Found` if either the project or the team member could not be found + -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 membership.  ```  POST /projects/:id/members @@ -166,9 +200,16 @@ Parameters:  + `user_id` (required) - The ID of a user to add  + `access_level` (required) - Project access level -Will return status `201 Created` on success, or `404 Not found` on fail. +Return Values: + ++ `201 Created` on success and the added user is returned, even if the user is already team member ++ `400 Bad Request` if the required attribute access_level is not given ++ `401 Unauthorized` if the user is not allowed to add a new team member ++ `404 Not Found` if a resource can not be found, e.g. project with ID not available ++ `422 Unprocessable Entity` if an unknown access_level is given + -## Edit project team member +### Edit project team member  Update project team member to specified access level. @@ -182,9 +223,16 @@ Parameters:  + `user_id` (required) - The ID of a team member  + `access_level` (required) - Project access level -Will return status `200 OK` on success, or `404 Not found` on fail. +Return Values: + ++ `200 Ok` on succes and the modified team member ++ `400 Bad Request` if the required attribute access_level is not given ++ `401 Unauthorized` if the user is not allowed to modify a team member ++ `404 Not Found` if a resource can not be found, e.g. project with ID not available ++ `422 Unprocessable Entity` if an unknown access_level is given + -## Remove project team member +### Remove project team member  Removes user from project team. @@ -197,11 +245,23 @@ Parameters:  + `id` (required) - The ID of a project  + `user_id` (required) - The ID of a team member -Status code `200` will be returned on success. +Return Values: -## List project hooks ++ `200 Ok` on success ++ `401 Unauthorized` if user is not allowed to remove a team member ++ `404 Not Found` if either project or user can not be found -Get list for project hooks +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. + + +## Hooks + +### List project hooks + +Get list of project hooks.  ```  GET /projects/:id/hooks @@ -211,11 +271,16 @@ Parameters:  + `id` (required) - The ID of a project -Will return hooks with status `200 OK` on success, or `404 Not found` on fail. +Return values: + ++ `200 Ok` on success with a list of hooks ++ `401 Unauthorized` if user is not allowed to get list of hooks ++ `404 Not Found` if project can not be found -## Get project hook -Get hook for project +### Get project hook + +Get a specific hook for project.  ```  GET /projects/:id/hooks/:hook_id @@ -226,11 +291,23 @@ Parameters:  + `id` (required) - The ID of a project  + `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" +} +``` + +Return values: + ++ `200 Ok` on sucess and the hook with the given ID ++ `404 Not Found` if the hook can not be found -## Add project hook -Add hook to project +### Add project hook + +Adds a hook to project.  ```  POST /projects/:id/hooks @@ -241,11 +318,17 @@ Parameters:  + `id` (required) - The ID of a project  + `url` (required) - The hook URL -Will return status `201 Created` on success, or `404 Not found` on fail. +Return values: + ++ `201 Created` on success and the newly created hook ++ `400 Bad Request` if url is not given ++ `404 Not Found` if project with ID not found ++ `422 Unprocessable Entity` if the url is invalid (must begin with `http` or `https`) -## Edit project hook -Edit hook for project +### Edit project hook + +Edits a hook for project.  ```  PUT /projects/:id/hooks/:hook_id @@ -257,12 +340,18 @@ Parameters:  + `hook_id` (required) - The ID of a project hook  + `url` (required) - The hook URL -Will return status `201 Created` on success, or `404 Not found` on fail. +Return values: + ++ `200 Ok` on success and the modified hook (see JSON response above) ++ `400 Bad Request` if the url attribute is not given ++ `404 Not Found` if project or hook can not be found ++ `422 Unprocessable Entity` if the url is invalid (must begin with `http` or `https`) -## 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 @@ -273,4 +362,235 @@ Parameters:  + `id` (required) - The ID of a project  + `hook_id` (required) - The ID of hook to delete -Will return status `200 OK` on success, or `404 Not found` on fail. +Return values: + ++ `200 Ok` on succes ++ `404 Not Found` if the project can not be found + +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. + + +## Branches + +### List branches + +Lists all branches of a project. + +``` +GET /projects/:id/repository/branches +``` + +Parameters: + ++ `id` (required) - The ID of the project + +Return values: + ++ `200 Ok` on success and a list of branches ++ `404 Not Found` if project is not found + + +### 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. + +Return values: + ++ `200 Ok` on success ++ `404 Not Found` if either project with ID or branch could not be found + + +### 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. + +Return values: + ++ `200 Ok` on success ++ `404 Not Found` if either project or branch could not be found + + +### 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. + +Return values: + ++ `200 Ok` on success ++ `404 Not Found` if either project or branch could not be found + + +### List tags + +Lists all tags of a project. + +``` +GET /projects/:id/repository/tags +``` + +Parameters: + ++ `id` (required) - The ID of the project + +Return values: + ++ `200 Ok` on success and a list of tags ++ `404 Not Found` if project with id not found + + +### 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 + + +## Snippets + +### List snippets + +Lists the snippets of a project. + +``` +GET /projects/:id/snippets +``` + +Parameters: + ++ `id` (required) - The ID of the project + +Return values: + ++ `200 Ok` on success and the list of snippets ++ `404 Not Found` if project with id not found + + +### List single snippet + +Lists a single snippet of a project + +``` +GET /projects/:id/snippets/:snippet_id +``` + +Parameters: + ++ `id` (required) - The ID of the project ++ `snippet_id` (required) - The ID of the snippet + +Return values: + ++ `200 Ok` on success and the project snippet ++ `404 Not Found` if project ID or snippet ID not found + + +### Create snippet + +Creates a new project snippet. + +``` +POST /projects/:id/snippets +``` + +Parameters: + ++ `id` (required) - The ID of the project ++ `title` (required) - The title of the new snippet ++ `file_name` (required) - The file name of the snippet ++ `code` (required) - The content of the snippet ++ `lifetime` (optional) - The expiration date of a snippet + +Return values: + ++ `201 Created` on success and the new snippet ++ `400 Bad Request` if one of the required attributes is missing ++ `401 Unauthorized` if it is not allowed to post a new snippet ++ `404 Not Found` if the project ID is not found + + +### Update snippet + +Updates an existing project snippet. + +``` +PUT /projects/:id/snippets/:snippet_id +``` + +Parameters: + ++ `id` (required) - The ID of the project ++ `snippet_id` (required) - The id of the project snippet ++ `title` (optional) - The new title of the project snippet ++ `file_name` (optional) - The new file name of the project snippet ++ `lifetime` (optional) - The new expiration date of the snippet ++ `code` (optional) - The content of the snippet + +Return values: + ++ `200 Ok` on success and the content of the updated snippet ++ `401 Unauthorized` if the user is not allowed to modify the snippet ++ `404 Not Found` if project ID or snippet ID is not found + + +## Delete snippet + +Deletes a project snippet. This is an idempotent function call and returns `200 Ok` +even if the snippet with the id is not available. + +``` +DELETE /projects/:id/snippets/:snippet_id +``` + +Paramaters: + ++ `id` (required) - The ID of the project ++ `snippet_id` (required) - The ID of the snippet + +Return values: + ++ `200 Ok` on success, if the snippet got deleted it is returned, if not available then an empty JSON response ++ `401 Unauthorized` if the user is not allowed to remove the snippet ++ `404 Not Found` if the project ID not found diff --git a/doc/api/users.md b/doc/api/users.md index b94d7c0f789..96aebffafd8 100644 --- a/doc/api/users.md +++ b/doc/api/users.md @@ -43,6 +43,12 @@ GET /users  ]  ``` +Return values: + ++ `200 Ok` on success and a list with all users ++ `401 Unauthorized` if user is not allowed to access the list + +  ## Single user  Get a single user. @@ -74,37 +80,55 @@ Parameters:  }  ``` +Return values: + ++ `200 Ok` on success and the user entry ++ `401 Unauthorized` if it is not allowed to access the user ++ `404 Not Found` if the user with ID is not found + +  ## User creation -Create user. Available only for admin + +Creates a new user. Note only administrators can create new users.  ```  POST /users  ```  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 -found` on fail. ++ `email` (required)          - Email ++ `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 + +Return values: + ++ `201 Created` on success and returns the new user ++ `400 Bad Request` if one of the required attributes is missing from the request ++ `401 Unauthorized` if the user is not authorized ++ `403 Forbidden` if the user is not allowed to create a new user (must be admin) ++ `404 Not Found` if something else fails ++ `409 Conflict` if a user with the same email address or username already exists +  ## User modification -Modify user. Available only for admin + +Modifies an existing user. Only administrators can change attributes of a user.  ```  PUT /users/:id  ```  Parameters: +  + `email`                             - Email  + `username`                          - Username  + `name`                              - Name @@ -117,23 +141,42 @@ Parameters:  + `provider`                          - External provider name  + `bio`                               - User's bio +Return values: + ++ `200 Ok` on success and returns the new user ++ `401 Unauthorized` if the user is not authorized ++ `403 Forbidden` if the user is not allowed to create a new user (must be admin) ++ `404 Not Found` if something else fails + +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 -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  ``` -Will return deleted user with status `200 OK` on success, or `404 Not -found` on fail. +Parameters: + ++ `id` (required) - The ID of the user + +Return values: + ++ `200 Ok` on success and returns the deleted user ++ `401 Unauthorized` if the user is not authorized ++ `403 Forbidden` if the user is not allowed to create a new user (must be admin) ++ `404 Not Found` if user with ID not found or something else fails +  ## Current user -Get currently authenticated user. +Gets currently authenticated user.  ```  GET /user @@ -156,6 +199,13 @@ GET /user  }  ``` +Return values: + ++ `200 Ok` on success and returns the current user ++ `401 Unauthorized` if the user is not authorized ++ `404 Not Found` if something else fails + +  ## List SSH keys  Get a list of currently authenticated user's SSH keys. @@ -183,6 +233,17 @@ GET /user/keys  ]  ``` +Parameters: + ++ **none** + +Return values: + ++ `200 Ok` on success and a list of ssh keys ++ `401 Unauthorized` if the user is not authenticated ++ `404 Not Found` if something else fails + +  ## Single SSH key  Get a single key. @@ -204,9 +265,17 @@ Parameters:        soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0="  }  ``` + +Return values: + ++ `200 Ok` on success and the ssh key with ID ++ `401 Unauthorized` if it is not allowed to access the user ++ `404 Not Found` if the ssh key with ID not found + +  ## Add SSH key -Create new key owned by currently authenticated user +Creates a new key owned by the currently authenticated user.  ```  POST /user/keys @@ -217,12 +286,18 @@ Parameters:  + `title` (required) - new SSH Key's title  + `key` (required) - new SSH key -Will return created key with status `201 Created` on success, or `404 Not -found` on fail. +Return values: + ++ `201 Created` on success and the added key ++ `400 Bad Request` if one of the required attributes is not given ++ `401 Unauthorized` if user is not authorized to add ssh key ++ `404 Not Found` if something else fails +  ## 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 @@ -232,4 +307,8 @@ Parameters:  + `id` (required) - SSH key ID -Will return `200 OK` on success, or `404 Not Found` on fail. +Return values: + ++ `200 Ok` on success ++ `401 Unauthorized` if user is not allowed to delete they key ++ `404 Not Found` if something else fails diff --git a/lib/api.rb b/lib/api.rb index d9dce7c70cc..ffd980ca7e0 100644 --- a/lib/api.rb +++ b/lib/api.rb @@ -8,6 +8,19 @@ module Gitlab        rack_response({'message' => '404 Not found'}.to_json, 404)      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      error_format :json      helpers APIHelpers diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb index 6bd8111c2b2..becb3bce5b0 100644 --- a/lib/api/helpers.rb +++ b/lib/api/helpers.rb @@ -55,6 +55,12 @@ module Gitlab        render_api_error!('403 Forbidden', 403)      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)        message = ["404"]        message << resource if resource diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb index 7f763eb49d5..4b28094f1a4 100644 --- a/lib/api/merge_requests.rb +++ b/lib/api/merge_requests.rb @@ -5,6 +5,23 @@ module Gitlab      resource :projects do +      helpers do +        # If an error occurred this helper method provides an appropriate status code +        # +        # Parameters: +        #   merge_request_errors (required) - The errors collection of MR +        # +        def handle_merge_request_error(merge_request_errors) +          if merge_request_errors[:target_branch].any? +            bad_request!(:target_branch) +          elsif merge_request_errors[:source_branch].any? +            bad_request!(:source_branch) +          elsif merge_request_errors[:base].any? +            error!(merge_request_errors[:base], 422) +          end +        end +      end +        # List merge requests        #        # Parameters: @@ -60,6 +77,7 @@ module Gitlab            merge_request.reload_code            present merge_request, with: Entities::MergeRequest          else +          handle_merge_request_error(merge_request.errors)            not_found!          end        end @@ -88,6 +106,7 @@ module Gitlab            merge_request.mark_as_unchecked            present merge_request, with: Entities::MergeRequest          else +          handle_merge_request_error(merge_request.errors)            not_found!          end        end @@ -109,6 +128,9 @@ module Gitlab          if note.save            present note, with: Entities::MRNote          else +          if note.errors[:note].any? +            bad_request!(:note) +          end            not_found!          end        end diff --git a/lib/api/milestones.rb b/lib/api/milestones.rb index eaf0d37c18b..7f8fe053ba1 100644 --- a/lib/api/milestones.rb +++ b/lib/api/milestones.rb @@ -4,6 +4,20 @@ module Gitlab      before { authenticate! }      resource :projects do + +      helpers do +        # If an error occurs this helper method handles error codes for a given milestone +        # +        # Parameters: +        #   milestone_errors (required) - The erros collection of a milestone +        # +        def handle_milestone_errors(milestone_errors) +          if milestone_errors[:title].any? +            bad_request!(:title) +          end +        end +      end +        # Get a list of project milestones        #        # Parameters: @@ -47,6 +61,7 @@ module Gitlab          if @milestone.save            present @milestone, with: Entities::Milestone          else +          handle_milestone_errors(@milestone.errors)            not_found!          end        end @@ -70,6 +85,7 @@ module Gitlab          if @milestone.update_attributes attrs            present @milestone, with: Entities::Milestone          else +          handle_milestone_errors(@milestone.errors)            not_found!          end        end diff --git a/lib/api/notes.rb b/lib/api/notes.rb index 70344d6e381..56de6e090e5 100644 --- a/lib/api/notes.rb +++ b/lib/api/notes.rb @@ -43,6 +43,8 @@ module Gitlab          if @note.save            present @note, with: Entities::Note          else +          # :note is exposed as :body, but :note is set on error +          bad_request!(:note) if @note.errors[:note].any?            not_found!          end        end diff --git a/lib/api/projects.rb b/lib/api/projects.rb index 921aa237f26..d171acc1ce1 100644 --- a/lib/api/projects.rb +++ b/lib/api/projects.rb @@ -4,6 +4,15 @@ module Gitlab      before { authenticate! }      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        #        # Example Request: @@ -36,6 +45,7 @@ module Gitlab        # Example Request        #   POST /projects        post do +        bad_request!(:name) if !params.has_key? :name          attrs = attributes_for_keys [:name,                                      :description,                                      :default_branch, @@ -43,10 +53,14 @@ module Gitlab                                      :wall_enabled,                                      :merge_requests_enabled,                                      :wiki_enabled] +          @project = ::Projects::CreateContext.new(current_user, attrs).execute          if @project.saved?            present @project, with: Entities::Project          else +          if @project.errors[:limit_reached].present? +            error!(@project.errors[:limit_reached], 403) +          end            not_found!          end        end @@ -89,16 +103,24 @@ module Gitlab        #   POST /projects/:id/members        post ":id/members" do          authorize! :admin_project, user_project -        users_project = user_project.users_projects.new( -          user_id: params[:user_id], -          project_access: params[:access_level] -        ) -        if users_project.save -          @member = users_project.user +        bad_request!(:user_id) if !params.has_key? :user_id +        bad_request!(:access_level) if !params.has_key? :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 team_member.save +          @member = team_member.user            present @member, with: Entities::ProjectMember, project: user_project          else -          not_found! +          handle_project_member_errors team_member.errors          end        end @@ -112,13 +134,16 @@ module Gitlab        #   PUT /projects/:id/members/:user_id        put ":id/members/:user_id" do          authorize! :admin_project, user_project -        users_project = user_project.users_projects.find_by_user_id params[:user_id] -        if users_project.update_attributes(project_access: params[:access_level]) -          @member = users_project.user +        team_member = user_project.users_projects.find_by_user_id(params[:user_id]) +        bad_request!(:access_level) if !params.has_key? :access_level +        not_found!("User can not be found") if team_member.nil? + +        if team_member.update_attributes(project_access: params[:access_level]) +          @member = team_member.user            present @member, with: Entities::ProjectMember, project: user_project          else -          not_found! +          handle_project_member_errors team_member.errors          end        end @@ -131,8 +156,12 @@ module Gitlab        #   DELETE /projects/:id/members/:user_id        delete ":id/members/:user_id" do          authorize! :admin_project, user_project -        users_project = user_project.users_projects.find_by_user_id params[:user_id] -        users_project.destroy +        team_member = user_project.users_projects.find_by_user_id(params[:user_id]) +        unless team_member.nil? +          team_member.destroy +        else +          {:message => "Access revoked", :id => params[:user_id].to_i} +        end        end        # Get project hooks @@ -170,11 +199,17 @@ module Gitlab        #   POST /projects/:id/hooks        post ":id/hooks" do          authorize! :admin_project, user_project + +        bad_request!(:url) unless params.has_key? :url +          @hook = user_project.hooks.new({"url" => params[:url]})          if @hook.save            present @hook, with: Entities::Hook          else -          error!({'message' => '404 Not found'}, 404) +          if @hook.errors[:url].present? +            error!("Invalid url given", 422) +          end +          not_found!          end        end @@ -190,11 +225,15 @@ module Gitlab          @hook = user_project.hooks.find(params[:hook_id])          authorize! :admin_project, user_project -        attrs = attributes_for_keys [:url] +        bad_request!(:url) unless params.has_key? :url +        attrs = attributes_for_keys [:url]          if @hook.update_attributes attrs            present @hook, with: Entities::Hook          else +          if @hook.errors[:url].present? +            error!("Invalid url given", 422) +          end            not_found!          end        end @@ -208,8 +247,13 @@ module Gitlab        #   DELETE /projects/:id/hooks        delete ":id/hooks" do          authorize! :admin_project, user_project -        @hook = user_project.hooks.find(params[:hook_id]) -        @hook.destroy +        bad_request!(:hook_id) unless params.has_key? :hook_id + +        begin +          @hook = ProjectHook.find(params[:hook_id]) +          @hook.destroy +        rescue +        end        end        # Get a project repository branches @@ -244,6 +288,7 @@ module Gitlab        #   PUT /projects/:id/repository/branches/:branch/protect        put ":id/repository/branches/:branch/protect" do          @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)          unless protected @@ -262,6 +307,7 @@ module Gitlab        #   PUT /projects/:id/repository/branches/:branch/unprotect        put ":id/repository/branches/:branch/unprotect" do          @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)          if protected @@ -334,6 +380,10 @@ module Gitlab        post ":id/snippets" do          authorize! :write_snippet, user_project +        bad_request!(:title) if !params[:title].present? +        bad_request!(:file_name) if !params[:file_name].present? +        bad_request!(:code) if !params[:code].present? +          attrs = attributes_for_keys [:title, :file_name]          attrs[:expires_at] = params[:lifetime] if params[:lifetime].present?          attrs[:content] = params[:code] if params[:code].present? @@ -381,10 +431,12 @@ module Gitlab        # Example Request:        #   DELETE /projects/:id/snippets/:snippet_id        delete ":id/snippets/:snippet_id" do -        @snippet = user_project.snippets.find(params[:snippet_id]) -        authorize! :modify_snippet, @snippet - -        @snippet.destroy +        begin +          @snippet = user_project.snippets.find(params[:snippet_id]) +          authorize! :modify_snippet, user_project +          @snippet.destroy +        rescue +        end        end        # Get a raw project snippet @@ -411,6 +463,8 @@ module Gitlab        get ":id/repository/commits/:sha/blob" do          authorize! :download_code, user_project +        bad_request!(:filepath) if !params.has_key? :filepath +          ref = params[:sha]          commit = user_project.repository.commit ref diff --git a/lib/api/users.rb b/lib/api/users.rb index 7ea90c75e9e..b9dce58a13d 100644 --- a/lib/api/users.rb +++ b/lib/api/users.rb @@ -41,6 +41,12 @@ module Gitlab        #   POST /users        post do          authenticated_as_admin! + +        bad_request!(:email)    if !params.has_key? :email +        bad_request!(:password) if !params.has_key? :password +        bad_request!(:name)     if !params.has_key? :name +        bad_request!(:username) if !params.has_key? :username +          attrs = attributes_for_keys [:email, :name, :password, :skype, :linkedin, :twitter, :projects_limit, :username, :extern_uid, :provider, :bio]          user = User.new attrs, as: :admin          if user.save @@ -67,10 +73,12 @@ module Gitlab        #   PUT /users/:id        put ":id" do          authenticated_as_admin! +          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          else            not_found! @@ -127,6 +135,9 @@ module Gitlab        # Example Request:        #   POST /user/keys        post "keys" do +        bad_request!(:title) unless params[:title].present? +        bad_request!(:key) unless params[:key].present? +          attrs = attributes_for_keys [:title, :key]          key = current_user.keys.new attrs          if key.save @@ -136,15 +147,18 @@ module Gitlab          end        end -      # Delete existed ssh key of currently authenticated user +      # Delete existing ssh key of currently authenticated user        #        # Parameters:        #   id (required) - SSH Key ID        # Example Request:        #   DELETE /user/keys/:id        delete "keys/:id" do -        key = current_user.keys.find params[:id] -        key.delete +        begin +          key = current_user.keys.find params[:id] +          key.delete +        rescue +        end        end      end    end diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb index 1abd7a20dec..8de06c33394 100644 --- a/spec/requests/api/merge_requests_spec.rb +++ b/spec/requests/api/merge_requests_spec.rb @@ -32,6 +32,11 @@ describe Gitlab::API do        response.status.should == 200        json_response['title'].should == merge_request.title      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    describe "POST /projects/:id/merge_requests" do @@ -41,6 +46,24 @@ describe Gitlab::API do        response.status.should == 201        json_response['title'].should == 'Test merge_request'      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    end    describe "PUT /projects/:id/merge_request/:merge_request_id to close MR" do @@ -66,6 +89,18 @@ describe Gitlab::API do        response.status.should == 200        json_response['title'].should == 'New title'      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    describe "POST /projects/:id/merge_request/:merge_request_id/comments" do @@ -74,6 +109,16 @@ describe Gitlab::API do        response.status.should == 201        json_response['note'].should == 'My comment'      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 diff --git a/spec/requests/api/milestones_spec.rb b/spec/requests/api/milestones_spec.rb index d1b5e449bc5..00e800e8b91 100644 --- a/spec/requests/api/milestones_spec.rb +++ b/spec/requests/api/milestones_spec.rb @@ -24,6 +24,11 @@ describe Gitlab::API do        response.status.should == 200        json_response['title'].should == milestone.title      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    describe "POST /projects/:id/milestones" do @@ -34,6 +39,19 @@ describe Gitlab::API do        json_response['title'].should == 'new milestone'        json_response['description'].should be_nil      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    describe "PUT /projects/:id/milestones/:milestone_id" do @@ -43,6 +61,12 @@ describe Gitlab::API do        response.status.should == 200        json_response['title'].should == 'updated title'      end + +    it "should return a 404 error if milestone is not found" do +      put api("/projects/#{project.id}/milestones/1234", user), +        title: 'updated title' +      response.status.should == 404 +    end    end    describe "PUT /projects/:id/milestones/:milestone_id to close milestone" do diff --git a/spec/requests/api/notes_spec.rb b/spec/requests/api/notes_spec.rb index ee99d85df4d..d382d7d9294 100644 --- a/spec/requests/api/notes_spec.rb +++ b/spec/requests/api/notes_spec.rb @@ -38,6 +38,11 @@ describe Gitlab::API do        response.status.should == 200        json_response['body'].should == wall_note.note      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    describe "POST /projects/:id/notes" do @@ -46,6 +51,11 @@ describe Gitlab::API do        response.status.should == 201        json_response['body'].should == 'hi!'      end + +    it "should return a 400 error if body is missing" do +      post api("/projects/#{project.id}/notes", user) +      response.status.should == 400 +    end    end    describe "GET /projects/:id/noteable/:noteable_id/notes" do @@ -56,6 +66,11 @@ describe Gitlab::API do          json_response.should be_an Array          json_response.first['body'].should == issue_note.note        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      context "when noteable is a Snippet" do @@ -65,6 +80,11 @@ describe Gitlab::API do          json_response.should be_an Array          json_response.first['body'].should == snippet_note.note        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      context "when noteable is a Merge Request" do @@ -84,6 +104,11 @@ describe Gitlab::API do          response.status.should == 200          json_response['body'].should == issue_note.note        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      context "when noteable is a Snippet" do @@ -92,6 +117,11 @@ describe Gitlab::API do          response.status.should == 200          json_response['body'].should == snippet_note.note        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 diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index 586f108ca9e..06ab0f5e684 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -6,8 +6,8 @@ describe Gitlab::API do    let(:user) { create(:user) }    let(:user2) { create(:user) }    let(:user3) { create(:user) } -  let!(:hook) { create(:project_hook, project: project, url: "http://example.com") }    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!(: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) } @@ -55,6 +55,11 @@ describe Gitlab::API do        expect { post api("/projects", user) }.to_not change {Project.count}      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        (1..user2.projects_limit-1).each { |p| post api("/projects", user2), name: "foo#{p}" }        post api("/projects", user2), name: "foo" @@ -66,9 +71,17 @@ describe Gitlab::API do        response.status.should == 201      end -    it "should respond with 404 on failure" do +    it "should respond with 400 if name is not given" do        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      it "should assign attributes to project" do @@ -109,6 +122,12 @@ describe Gitlab::API do        response.status.should == 404        json_response['message'].should == '404 Not Found'      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    describe "GET /projects/:id/repository/branches" do @@ -145,6 +164,17 @@ describe Gitlab::API do        json_response['commit']['id'].should == '621491c677087aa243f165eab467bfdfbee00be1'        json_response['protected'].should == true      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    describe "PUT /projects/:id/repository/branches/:branch/unprotect" do @@ -156,6 +186,17 @@ describe Gitlab::API do        json_response['commit']['id'].should == '621491c677087aa243f165eab467bfdfbee00be1'        json_response['protected'].should == false      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    describe "GET /projects/:id/members" do @@ -174,6 +215,11 @@ describe Gitlab::API do        json_response.count.should == 1        json_response.first['email'].should == user.email      end + +    it "should return a 404 error if id not found" do +      get api("/projects/9999/members", user) +      response.status.should == 404 +    end    end    describe "GET /projects/:id/members/:user_id" do @@ -183,6 +229,11 @@ describe Gitlab::API do        json_response['email'].should == user.email        json_response['access_level'].should == UsersProject::MASTER      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    describe "POST /projects/:id/members" do @@ -196,6 +247,34 @@ describe Gitlab::API do        json_response['email'].should == user2.email        json_response['access_level'].should == UsersProject::DEVELOPER      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    describe "PUT /projects/:id/members/:user_id" do @@ -205,6 +284,21 @@ describe Gitlab::API do        json_response['email'].should == user3.email        json_response['access_level'].should == UsersProject::MASTER      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    describe "DELETE /projects/:id/members/:user_id" do @@ -213,6 +307,30 @@ describe Gitlab::API do          delete api("/projects/#{project.id}/members/#{user3.id}", user)        }.to change { UsersProject.count }.by(-1)      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    describe "GET /projects/:id/hooks" do @@ -255,6 +373,11 @@ describe Gitlab::API do          response.status.should == 403        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    describe "POST /projects/:id/hooks" do @@ -263,6 +386,17 @@ describe Gitlab::API do          post api("/projects/#{project.id}/hooks", user),            "url" => "http://example.com"        }.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 @@ -273,8 +407,22 @@ describe Gitlab::API do        response.status.should == 200        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    describe "DELETE /projects/:id/hooks" do      it "should delete hook from project" do @@ -282,6 +430,22 @@ describe Gitlab::API do          delete api("/projects/#{project.id}/hooks", user),            hook_id: hook.id        }.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 @@ -330,6 +494,11 @@ describe Gitlab::API do        response.status.should == 200        json_response['title'].should == snippet.title      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    describe "POST /projects/:id/snippets" do @@ -339,6 +508,24 @@ describe Gitlab::API do        response.status.should == 201        json_response['title'].should == 'api test'      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    describe "PUT /projects/:id/snippets/:shippet_id" do @@ -349,6 +536,13 @@ describe Gitlab::API do        json_response['title'].should == 'example'        snippet.reload.content.should == 'updated code'      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    describe "DELETE /projects/:id/snippets/:snippet_id" do @@ -356,6 +550,12 @@ describe Gitlab::API do        expect {          delete api("/projects/#{project.id}/snippets/#{snippet.id}", user)        }.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 @@ -364,9 +564,14 @@ describe Gitlab::API do        get api("/projects/#{project.id}/snippets/#{snippet.id}/raw", user)        response.status.should == 200      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 -  describe "GET /projects/:id/:sha/blob" do +  describe "GET /projects/:id/repository/commits/:sha/blob" do      it "should get the raw file contents" do        get api("/projects/#{project.id}/repository/commits/master/blob?filepath=README.md", user)        response.status.should == 200 @@ -381,5 +586,10 @@ describe Gitlab::API do        get api("/projects/#{project.id}/repository/commits/master/blob?filepath=README.invalid", user)        response.status.should == 404      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 diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb index 1645117e231..b0cf12654be 100644 --- a/spec/requests/api/users_spec.rb +++ b/spec/requests/api/users_spec.rb @@ -31,15 +31,20 @@ describe Gitlab::API do        response.status.should == 200        json_response['email'].should == user.email      end -  end -  describe "POST /users" do -    before{ admin } +    it "should return a 401 if unauthenticated" do +      get api("/users/9998") +      response.status.should == 401 +    end -    it "should not create invalid user" do -      post api("/users", admin), { email: "invalid email" } +    it "should return a 404 error if user id not found" do +      get api("/users/9999", user)        response.status.should == 404      end +  end + +  describe "POST /users" do +    before{ admin }      it "should create user" do        expect { @@ -47,10 +52,48 @@ describe Gitlab::API do        }.to change { User.count }.by(1)      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        post api("/users", user), attributes_for(:user)        response.status.should == 403      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    describe "GET /users/sign_up" do @@ -86,7 +129,7 @@ describe Gitlab::API do    describe "PUT /users/:id" do      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'}        response.status.should == 200        json_response['bio'].should == 'new test bio' @@ -108,6 +151,25 @@ describe Gitlab::API do        put api("/users/999999", admin), {bio: 'update should fail'}        response.status.should == 404      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    describe "DELETE /users/:id" do @@ -120,6 +182,11 @@ describe Gitlab::API do        json_response['email'].should == user.email      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        delete api("/users/#{user.id}", user)        response.status.should == 403 @@ -137,6 +204,11 @@ describe Gitlab::API do        response.status.should == 200        json_response['email'].should == user.email      end + +    it "should return 401 error if user is unauthenticated" do +      get api("/user") +      response.status.should == 401 +    end    end    describe "GET /user/keys" do @@ -172,19 +244,38 @@ describe Gitlab::API do        get api("/user/keys/42", user)        response.status.should == 404      end -  end -  describe "POST /user/keys" do -    it "should not create invalid ssh key" do -      post api("/user/keys", user), { title: "invalid key" } +    it "should return 404 error if admin accesses user's ssh key" do +      user.keys << key +      user.save +      admin +      get api("/user/keys/#{key.id}", admin)        response.status.should == 404      end +  end +  describe "POST /user/keys" do      it "should create ssh key" do        key_attrs = attributes_for :key        expect {          post api("/user/keys", user), key_attrs        }.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 @@ -195,11 +286,19 @@ describe Gitlab::API do        expect {          delete api("/user/keys/#{key.id}", user)        }.to change{user.keys.count}.by(-1) +      response.status.should == 200      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) -      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  | 
