Commit 68edac04 authored by Douwe Maan's avatar Douwe Maan

Merge branch 'ee-27501-api-use-visibility-everywhere' into 'master'

Port of 27501-api-use-visibility-everywhere to EE

See merge request !1267
parents 9eeed71f 97a373a4
......@@ -198,6 +198,7 @@ class ApplicationSetting < ActiveRecord::Base
default_project_visibility: Settings.gitlab.default_projects_features['visibility_level'],
default_projects_limit: Settings.gitlab['default_projects_limit'],
default_snippet_visibility: Settings.gitlab.default_projects_features['visibility_level'],
default_group_visibility: Settings.gitlab.default_projects_features['visibility_level'],
disabled_oauth_sign_in_sources: [],
domain_whitelist: Settings.gitlab['domain_whitelist'],
gravatar_enabled: Settings.gravatar['enabled'],
......@@ -318,6 +319,22 @@ class ApplicationSetting < ActiveRecord::Base
self.repository_storages = [value]
end
def default_project_visibility=(level)
super(Gitlab::VisibilityLevel.level_value(level))
end
def default_snippet_visibility=(level)
super(Gitlab::VisibilityLevel.level_value(level))
end
def default_group_visibility=(level)
super(Gitlab::VisibilityLevel.level_value(level))
end
def restricted_visibility_levels=(levels)
super(levels.map { |level| Gitlab::VisibilityLevel.level_value(level) })
end
# Choose one of the available repository storage options. Currently all have
# equal weighting.
def pick_repository_storage
......
......@@ -109,7 +109,7 @@ class Group < Namespace
end
def visibility_level_field
visibility_level
:visibility_level
end
def visibility_level_allowed_by_projects
......
......@@ -376,7 +376,7 @@ class Project < ActiveRecord::Base
end
def search_by_visibility(level)
where(visibility_level: Gitlab::VisibilityLevel.const_get(level.upcase))
where(visibility_level: Gitlab::VisibilityLevel.string_options[level])
end
def search_by_title(query)
......@@ -1121,7 +1121,7 @@ class Project < ActiveRecord::Base
end
def visibility_level_field
visibility_level
:visibility_level
end
def archive!
......
......@@ -121,7 +121,7 @@ class Snippet < ActiveRecord::Base
end
def visibility_level_field
visibility_level
:visibility_level
end
def no_highlighting?
......
......@@ -12,7 +12,7 @@ module Projects
@project = Project.new(params)
# Make sure that the user is allowed to use the specified visibility level
unless Gitlab::VisibilityLevel.allowed_for?(current_user, params[:visibility_level])
unless Gitlab::VisibilityLevel.allowed_for?(current_user, @project.visibility_level)
deny_visibility_level(@project)
return @project
end
......
......@@ -84,7 +84,7 @@ class SystemHooksService
project_id: model.id,
owner_name: owner.name,
owner_email: owner.respond_to?(:email) ? owner.email : "",
project_visibility: Project.visibility_levels.key(model.visibility_level_field).downcase
project_visibility: Project.visibility_levels.key(model.visibility_level_value).downcase
}
end
......@@ -101,7 +101,7 @@ class SystemHooksService
user_email: model.user.email,
user_id: model.user.id,
access_level: model.human_access,
project_visibility: Project.visibility_levels.key(project.visibility_level_field).downcase
project_visibility: Project.visibility_levels.key(project.visibility_level_value).downcase
}
end
......
---
title: "API: Use `visibility` as string parameter everywhere"
merge_request: 9337
author:
......@@ -27,7 +27,7 @@ GET /groups
"name": "Foobar Group",
"path": "foo-bar",
"description": "An interesting group",
"visibility_level": 20,
"visibility": "public",
"lfs_enabled": true,
"avatar_url": "http://localhost:3000/uploads/group/avatar/1/foo.jpg",
"web_url": "http://localhost:3000/groups/foo-bar",
......@@ -72,9 +72,8 @@ Example response:
"description": "foo",
"default_branch": "master",
"tag_list": [],
"public": false,
"archived": false,
"visibility_level": 10,
"visibility": "internal",
"ssh_url_to_repo": "git@gitlab.example.com/html5-boilerplate.git",
"http_url_to_repo": "http://gitlab.example.com/h5bp/html5-boilerplate.git",
"web_url": "http://gitlab.example.com/h5bp/html5-boilerplate",
......@@ -134,7 +133,7 @@ Example response:
"name": "Twitter",
"path": "twitter",
"description": "Aliquid qui quis dignissimos distinctio ut commodi voluptas est.",
"visibility_level": 20,
"visibility": "public",
"avatar_url": null,
"web_url": "https://gitlab.example.com/groups/twitter",
"request_access_enabled": false,
......@@ -147,9 +146,8 @@ Example response:
"description": "Voluptas veniam qui et beatae voluptas doloremque explicabo facilis.",
"default_branch": "master",
"tag_list": [],
"public": true,
"archived": false,
"visibility_level": 20,
"visibility": "public",
"ssh_url_to_repo": "git@gitlab.example.com:twitter/typeahead-js.git",
"http_url_to_repo": "https://gitlab.example.com/twitter/typeahead-js.git",
"web_url": "https://gitlab.example.com/twitter/typeahead-js",
......@@ -186,9 +184,8 @@ Example response:
"description": "Aspernatur omnis repudiandae qui voluptatibus eaque.",
"default_branch": "master",
"tag_list": [],
"public": false,
"archived": false,
"visibility_level": 10,
"visibility": "internal",
"ssh_url_to_repo": "git@gitlab.example.com:twitter/flight.git",
"http_url_to_repo": "https://gitlab.example.com/twitter/flight.git",
"web_url": "https://gitlab.example.com/twitter/flight",
......@@ -227,9 +224,8 @@ Example response:
"description": "Velit eveniet provident fugiat saepe eligendi autem.",
"default_branch": "master",
"tag_list": [],
"public": false,
"archived": false,
"visibility_level": 0,
"visibility": "private",
"ssh_url_to_repo": "git@gitlab.example.com:h5bp/html5-boilerplate.git",
"http_url_to_repo": "https://gitlab.example.com/h5bp/html5-boilerplate.git",
"web_url": "https://gitlab.example.com/h5bp/html5-boilerplate",
......@@ -306,7 +302,7 @@ Parameters:
- `description` (optional) - The group's description
- `membership_lock` (optional, boolean) - Prevent adding new members to project membership within this group
- `share_with_group_lock` (optional, boolean) - Prevent sharing a project with another group within this group
- `visibility_level` (optional) - The group's visibility. 0 for private, 10 for internal, 20 for public.
- `visibility` (optional) - The group's visibility. Can be `private`, `internal`, or `public`.
- `lfs_enabled` (optional) - Enable/disable Large File Storage (LFS) for the projects in this group
- `request_access_enabled` (optional) - Allow users to request member access.
- `parent_id` (optional) - The parent group id for creating nested group.
......@@ -338,7 +334,7 @@ PUT /groups/:id
| `name` | string | no | The name of the group |
| `path` | string | no | The path of the group |
| `description` | string | no | The description of the group |
| `visibility_level` | integer | no | The visibility level of the group. 0 for private, 10 for internal, 20 for public. |
| `visibility` | string | no | The visibility level of the group. Can be `private`, `internal`, or `public`. |
| `lfs_enabled` (optional) | boolean | no | Enable/disable Large File Storage (LFS) for the projects in this group |
| `request_access_enabled` | boolean | no | Allow users to request member access. |
......@@ -355,7 +351,7 @@ Example response:
"name": "Experimental",
"path": "h5bp",
"description": "foo",
"visibility_level": 10,
"visibility": "internal",
"avatar_url": null,
"web_url": "http://gitlab.example.com/groups/h5bp",
"request_access_enabled": false,
......@@ -370,7 +366,7 @@ Example response:
"tag_list": [],
"public": false,
"archived": false,
"visibility_level": 10,
"visibility": "internal",
"ssh_url_to_repo": "git@gitlab.example.com/html5-boilerplate.git",
"http_url_to_repo": "http://gitlab.example.com/h5bp/html5-boilerplate.git",
"web_url": "http://gitlab.example.com/h5bp/html5-boilerplate",
......
......@@ -3,15 +3,15 @@
### Snippet visibility level
Snippets in GitLab can be either private, internal or public.
You can set it with the `visibility_level` field in the snippet.
You can set it with the `visibility` field in the snippet.
Constants for snippet visibility levels are:
| Visibility | visibility_level | Description |
| ---------- | ---------------- | ----------- |
| Private | `0` | The snippet is visible only the snippet creator |
| Internal | `10` | The snippet is visible for any logged in user |
| Public | `20` | The snippet can be accessed without any authentication |
| visibility | Description |
| ---------- | ----------- |
| `private` | The snippet is visible only the snippet creator |
| `internal` | The snippet is visible for any logged in user |
| `public` | The snippet can be accessed without any authentication |
## List snippets
......@@ -71,7 +71,7 @@ Parameters:
- `title` (required) - The title of a snippet
- `file_name` (required) - The name of a snippet file
- `code` (required) - The content of a snippet
- `visibility_level` (required) - The snippet's visibility
- `visibility` (required) - The snippet's visibility
## Update snippet
......@@ -88,7 +88,7 @@ Parameters:
- `title` (optional) - The title of a snippet
- `file_name` (optional) - The name of a snippet file
- `code` (optional) - The content of a snippet
- `visibility_level` (optional) - The snippet's visibility
- `visibility` (optional) - The snippet's visibility
## Delete snippet
......
......@@ -4,17 +4,17 @@
### Project visibility level
Project in GitLab has be either private, internal or public.
You can determine it by `visibility_level` field in project.
You can determine it by `visibility` field in project.
Constants for project visibility levels are next:
* Private. `visibility_level` is `0`.
* `private`:
Project access must be granted explicitly for each user.
* Internal. `visibility_level` is `10`.
* `internal`:
The project can be cloned by any logged in user.
* Public. `visibility_level` is `20`.
* `public`:
The project can be cloned without any authentication.
......@@ -45,8 +45,7 @@ Parameters:
"id": 4,
"description": null,
"default_branch": "master",
"public": false,
"visibility_level": 0,
"visibility": "private",
"ssh_url_to_repo": "git@example.com:diaspora/diaspora-client.git",
"http_url_to_repo": "http://example.com/diaspora/diaspora-client.git",
"web_url": "http://example.com/diaspora/diaspora-client",
......@@ -96,8 +95,7 @@ Parameters:
"id": 6,
"description": null,
"default_branch": "master",
"public": false,
"visibility_level": 0,
"visibility": "private",
"ssh_url_to_repo": "git@example.com:brightbox/puppet.git",
"http_url_to_repo": "http://example.com/brightbox/puppet.git",
"web_url": "http://example.com/brightbox/puppet",
......@@ -177,8 +175,7 @@ Parameters:
"id": 3,
"description": null,
"default_branch": "master",
"public": false,
"visibility_level": 0,
"visibility": "private",
"ssh_url_to_repo": "git@example.com:diaspora/diaspora-project-site.git",
"http_url_to_repo": "http://example.com/diaspora/diaspora-project-site.git",
"web_url": "http://example.com/diaspora/diaspora-project-site",
......@@ -448,7 +445,7 @@ Parameters:
| `snippets_enabled` | boolean | no | Enable snippets for this project |
| `container_registry_enabled` | boolean | no | Enable container registry for this project |
| `shared_runners_enabled` | boolean | no | Enable shared runners for this project |
| `visibility_level` | integer | no | See [project visibility level](#project-visibility-level) |
| `visibility` | String | no | See [project visibility level](#project-visibility-level) |
| `import_url` | string | no | URL to import repository from |
| `public_builds` | boolean | no | If `true`, builds can be viewed by non-project-members |
| `only_allow_merge_if_pipeline_succeeds` | boolean | no | Set whether merge requests can only be merged with successful builds |
......@@ -482,7 +479,7 @@ Parameters:
| `snippets_enabled` | boolean | no | Enable snippets for this project |
| `container_registry_enabled` | boolean | no | Enable container registry for this project |
| `shared_runners_enabled` | boolean | no | Enable shared runners for this project |
| `visibility_level` | integer | no | See [project visibility level](#project-visibility-level) |
| `visibility` | string | no | See [project visibility level](#project-visibility-level) |
| `import_url` | string | no | URL to import repository from |
| `public_builds` | boolean | no | If `true`, builds can be viewed by non-project-members |
| `only_allow_merge_if_pipeline_succeeds` | boolean | no | Set whether merge requests can only be merged with successful builds |
......@@ -516,7 +513,7 @@ Parameters:
| `snippets_enabled` | boolean | no | Enable snippets for this project |
| `container_registry_enabled` | boolean | no | Enable container registry for this project |
| `shared_runners_enabled` | boolean | no | Enable shared runners for this project |
| `visibility_level` | integer | no | See [project visibility level](#project-visibility-level) |
| `visibility` | string | no | See [project visibility level](#project-visibility-level) |
| `import_url` | string | no | URL to import repository from |
| `public_builds` | boolean | no | If `true`, builds can be viewed by non-project-members |
| `only_allow_merge_if_pipeline_succeeds` | boolean | no | Set whether merge requests can only be merged with successful builds |
......@@ -566,8 +563,7 @@ Example response:
"id": 3,
"description": null,
"default_branch": "master",
"public": false,
"visibility_level": 10,
"visibility": "internal",
"ssh_url_to_repo": "git@example.com:diaspora/diaspora-project-site.git",
"http_url_to_repo": "http://example.com/diaspora/diaspora-project-site.git",
"web_url": "http://example.com/diaspora/diaspora-project-site",
......@@ -632,8 +628,7 @@ Example response:
"id": 3,
"description": null,
"default_branch": "master",
"public": false,
"visibility_level": 10,
"visibility": "internal",
"ssh_url_to_repo": "git@example.com:diaspora/diaspora-project-site.git",
"http_url_to_repo": "http://example.com/diaspora/diaspora-project-site.git",
"web_url": "http://example.com/diaspora/diaspora-project-site",
......@@ -699,8 +694,7 @@ Example response:
"id": 3,
"description": null,
"default_branch": "master",
"public": false,
"visibility_level": 0,
"visibility": "private",
"ssh_url_to_repo": "git@example.com:diaspora/diaspora-project-site.git",
"http_url_to_repo": "http://example.com/diaspora/diaspora-project-site.git",
"web_url": "http://example.com/diaspora/diaspora-project-site",
......@@ -782,8 +776,7 @@ Example response:
"id": 3,
"description": null,
"default_branch": "master",
"public": false,
"visibility_level": 0,
"visibility": "private",
"ssh_url_to_repo": "git@example.com:diaspora/diaspora-project-site.git",
"http_url_to_repo": "http://example.com/diaspora/diaspora-project-site.git",
"web_url": "http://example.com/diaspora/diaspora-project-site",
......
......@@ -32,12 +32,13 @@ Example response:
"updated_at" : "2016-01-04T15:44:55.176Z",
"session_expire_delay" : 10080,
"home_page_url" : null,
"default_snippet_visibility" : 0,
"default_snippet_visibility" : "private",
"domain_whitelist" : [],
"domain_blacklist_enabled" : false,
"domain_blacklist" : [],
"created_at" : "2016-01-04T15:44:55.176Z",
"default_project_visibility" : 0,
"default_project_visibility" : "private",
"default_group_visibility" : "private",
"gravatar_enabled" : true,
"sign_in_text" : null,
"container_registry_token_expire_delay": 5,
......@@ -65,11 +66,12 @@ PUT /application/settings
| `sign_in_text` | string | no | Text on login page |
| `home_page_url` | string | no | Redirect to this URL when not logged in |
| `default_branch_protection` | integer | no | Determine if developers can push to master. Can take `0` _(not protected, both developers and masters can push new commits, force push or delete the branch)_, `1` _(partially protected, developers can push new commits, but cannot force push or delete the branch, masters can do anything)_ or `2` _(fully protected, developers cannot push new commits, force push or delete the branch, masters can do anything)_ as a parameter. Default is `2`. |
| `restricted_visibility_levels` | array of integers | no | Selected levels cannot be used by non-admin users for projects or snippets. Can take `0` _(Private)_, `1` _(Internal)_ and `2` _(Public)_ as a parameter. Default is null which means there is no restriction. |
| `restricted_visibility_levels` | array of strings | no | Selected levels cannot be used by non-admin users for projects or snippets. Can take `private`, `internal` and `public` as a parameter. Default is null which means there is no restriction. |
| `max_attachment_size` | integer | no | Limit attachment size in MB |
| `session_expire_delay` | integer | no | Session duration in minutes. GitLab restart is required to apply changes |
| `default_project_visibility` | integer | no | What visibility level new projects receive. Can take `0` _(Private)_, `1` _(Internal)_ and `2` _(Public)_ as a parameter. Default is `0`.|
| `default_snippet_visibility` | integer | no | What visibility level new snippets receive. Can take `0` _(Private)_, `1` _(Internal)_ and `2` _(Public)_ as a parameter. Default is `0`.|
| `default_project_visibility` | string | no | What visibility level new projects receive. Can take `private`, `internal` and `public` as a parameter. Default is `private`.|
| `default_snippet_visibility` | string | no | What visibility level new snippets receive. Can take `private`, `internal` and `public` as a parameter. Default is `private`.|
| `default_group_visibility` | string | no | What visibility level new groups receive. Can take `private`, `internal` and `public` as a parameter. Default is `private`.|
| `domain_whitelist` | array of strings | no | Force people to use only corporate emails for sign-up. Default is null, meaning there is no restriction. |
| `domain_blacklist_enabled` | boolean | no | Enable/disable the `domain_blacklist` |
| `domain_blacklist` | array of strings | yes (if `domain_blacklist_enabled` is `true`) | People trying to sign-up with emails from this domain will not be allowed to do so. |
......@@ -93,7 +95,7 @@ PUT /application/settings
| `terminal_max_session_time` | integer | no | Maximum time for web terminal websocket connection (in seconds). Set to 0 for unlimited time. |
```bash
curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/application/settings?signup_enabled=false&default_project_visibility=1
curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/application/settings?signup_enabled=false&default_project_visibility=internal
```
Example response:
......@@ -113,8 +115,9 @@ Example response:
"restricted_visibility_levels": [],
"max_attachment_size": 10,
"session_expire_delay": 10080,
"default_project_visibility": 1,
"default_snippet_visibility": 0,
"default_project_visibility": "internal",
"default_snippet_visibility": "private",
"default_group_visibility": "private",
"domain_whitelist": [],
"domain_blacklist_enabled" : false,
"domain_blacklist" : [],
......
......@@ -5,15 +5,15 @@
### Snippet visibility level
Snippets in GitLab can be either private, internal, or public.
You can set it with the `visibility_level` field in the snippet.
You can set it with the `visibility` field in the snippet.
Constants for snippet visibility levels are:
| Visibility | Visibility level | Description |
| ---------- | ---------------- | ----------- |
| Private | `0` | The snippet is visible only to the snippet creator |
| Internal | `10` | The snippet is visible for any logged in user |
| Public | `20` | The snippet can be accessed without any authentication |
| Visibility | Description |
| ---------- | ----------- |
| `private` | The snippet is visible only to the snippet creator |
| `internal` | The snippet is visible for any logged in user |
| `public` | The snippet can be accessed without any authentication |
## List snippets
......@@ -78,11 +78,11 @@ Parameters:
| `title` | String | yes | The title of a snippet |
| `file_name` | String | yes | The name of a snippet file |
| `content` | String | yes | The content of a snippet |
| `visibility_level` | Integer | yes | The snippet's visibility |
| `visibility` | String | yes | The snippet's visibility |
``` bash
curl --request POST --data '{"title": "This is a snippet", "content": "Hello world", "file_name": "test.txt", "visibility_level": 10 }' --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/snippets
curl --request POST --data '{"title": "This is a snippet", "content": "Hello world", "file_name": "test.txt", "visibility": "internal" }' --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/snippets
```
Example response:
......@@ -123,7 +123,7 @@ Parameters:
| `title` | String | no | The title of a snippet |
| `file_name` | String | no | The name of a snippet file |
| `content` | String | no | The content of a snippet |
| `visibility_level` | Integer | no | The snippet's visibility |
| `visibility` | String | no | The snippet's visibility |
``` bash
......@@ -154,7 +154,7 @@ Example response:
## Delete snippet
Deletes an existing snippet.
Deletes an existing snippet.
```
DELETE /snippets/:id
......@@ -229,4 +229,3 @@ Example response:
}
]
```
......@@ -47,6 +47,7 @@ changes are in V4:
- PUT `projects/:id`
- Renamed `branch_name` to `branch` on DELETE `id/repository/branches/:branch` response [!8936](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8936)
- Remove `public` param from create and edit actions of projects [!8736](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8736)
- Use `visibility` as string parameter everywhere [!9337](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9337)
- Notes do not return deprecated field `upvote` and `downvote` [!9384](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9384)
- Return HTTP status code `400` for all validation errors when creating or updating a member instead of sometimes `422` error. [!9523](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9523)
- Remove `GET /groups/owned`. Use `GET /groups?owned=true` instead [!9505](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9505)
......
......@@ -30,6 +30,7 @@ module API
mount ::API::V3::Runners
mount ::API::V3::Services
mount ::API::V3::Settings
mount ::API::V3::Snippets
mount ::API::V3::Subscriptions
mount ::API::V3::SystemHooks
mount ::API::V3::Tags
......
......@@ -81,9 +81,8 @@ module API
class Project < Grape::Entity
expose :id, :description, :default_branch, :tag_list
expose :public?, as: :public
expose :archived?, as: :archived
expose :visibility_level, :ssh_url_to_repo, :http_url_to_repo, :web_url
expose :visibility, :ssh_url_to_repo, :http_url_to_repo, :web_url
expose :owner, using: Entities::UserBasic, unless: ->(project, options) { project.group }
expose :name, :name_with_namespace
expose :path, :path_with_namespace
......@@ -150,7 +149,7 @@ module API
end
class Group < Grape::Entity
expose :id, :name, :path, :description, :visibility_level
expose :id, :name, :path, :description, :visibility
expose :ldap_cn, :ldap_access
expose :ldap_group_links,
......@@ -605,12 +604,14 @@ module API
expose :updated_at
expose :home_page_url
expose :default_branch_protection
expose :restricted_visibility_levels
expose(:restricted_visibility_levels) do |setting, _options|
setting.restricted_visibility_levels.map { |level| Gitlab::VisibilityLevel.string_level(level) }
end
expose :max_attachment_size
expose :session_expire_delay
expose :default_project_visibility
expose :default_snippet_visibility
expose :default_group_visibility
expose(:default_project_visibility) { |setting, _options| Gitlab::VisibilityLevel.string_level(setting.default_project_visibility) }
expose(:default_snippet_visibility) { |setting, _options| Gitlab::VisibilityLevel.string_level(setting.default_snippet_visibility) }
expose(:default_group_visibility) { |setting, _options| Gitlab::VisibilityLevel.string_level(setting.default_group_visibility) }
expose :default_artifacts_expire_in
expose :domain_whitelist
expose :domain_blacklist_enabled
......
......@@ -7,7 +7,7 @@ module API
helpers do
params :optional_params do
optional :description, type: String, desc: 'The description of the group'
optional :visibility_level, type: Integer, desc: 'The visibility level of the group'
optional :visibility, type: String, values: Gitlab::VisibilityLevel.string_values, desc: 'The visibility of the group'
optional :lfs_enabled, type: Boolean, desc: 'Enable/disable LFS for the projects in this group'
optional :request_access_enabled, type: Boolean, desc: 'Allow users to request member access'
optional :membership_lock, type: Boolean, desc: 'Prevent adding new members to project membership within this group'
......@@ -114,7 +114,7 @@ module API
optional :name, type: String, desc: 'The name of the group'
optional :path, type: String, desc: 'The path of the group'
use :optional_params
at_least_one_of :name, :path, :description, :visibility_level,
at_least_one_of :name, :path, :description, :visibility,
:lfs_enabled, :request_access_enabled
end
put ':id' do
......@@ -148,7 +148,7 @@ module API
end
params do
optional :archived, type: Boolean, default: false, desc: 'Limit by archived status'
optional :visibility, type: String, values: %w[public internal private],
optional :visibility, type: String, values: Gitlab::VisibilityLevel.string_values,
desc: 'Limit by visibility'
optional :search, type: String, desc: 'Return list of authorized projects matching the search criteria'
optional :order_by, type: String, values: %w[id name path created_at updated_at last_activity_at],
......
......@@ -50,11 +50,9 @@ module API
requires :title, type: String, desc: 'The title of the snippet'
requires :file_name, type: String, desc: 'The file name of the snippet'
requires :code, type: String, desc: 'The content of the snippet'
requires :visibility_level, type: Integer,
values: [Gitlab::VisibilityLevel::PRIVATE,
Gitlab::VisibilityLevel::INTERNAL,
Gitlab::VisibilityLevel::PUBLIC],
desc: 'The visibility level of the snippet'
requires :visibility, type: String,
values: Gitlab::VisibilityLevel.string_values,
desc: 'The visibility of the snippet'
end
post ":id/snippets" do
authorize! :create_project_snippet, user_project
......@@ -80,11 +78,9 @@ module API
optional :title, type: String, desc: 'The title of the snippet'
optional :file_name, type: String, desc: 'The file name of the snippet'
optional :code, type: String, desc: 'The content of the snippet'
optional :visibility_level, type: Integer,
values: [Gitlab::VisibilityLevel::PRIVATE,
Gitlab::VisibilityLevel::INTERNAL,
Gitlab::VisibilityLevel::PUBLIC],
desc: 'The visibility level of the snippet'
optional :visibility, type: String,
values: Gitlab::VisibilityLevel.string_values,
desc: 'The visibility of the snippet'
at_least_one_of :title, :file_name, :code, :visibility_level
end
put ":id/snippets/:snippet_id" do
......
......@@ -16,11 +16,7 @@ module API
optional :shared_runners_enabled, type: Boolean, desc: 'Flag indication if shared runners are enabled for that project'
optional :container_registry_enabled, type: Boolean, desc: 'Flag indication if the container registry is enabled for that project'
optional :lfs_enabled, type: Boolean, desc: 'Flag indication if Git LFS is enabled for that project'
optional :visibility_level, type: Integer, values: [
Gitlab::VisibilityLevel::PRIVATE,
Gitlab::VisibilityLevel::INTERNAL,
Gitlab::VisibilityLevel::PUBLIC
], desc: 'Create a public project. The same as visibility_level = 20.'
optional :visibility, type: String, values: Gitlab::VisibilityLevel.string_values, desc: 'The visibility of the project.'
optional :public_builds, type: Boolean, desc: 'Perform public builds'
optional :request_access_enabled, type: Boolean, desc: 'Allow users to request member access'
optional :only_allow_merge_if_pipeline_succeeds, type: Boolean, desc: 'Only allow to merge if builds succeed'
......@@ -52,7 +48,7 @@ module API
params :filter_params do
optional :archived, type: Boolean, default: false, desc: 'Limit by archived status'
optional :visibility, type: String, values: %w[public internal private],
optional :visibility, type: String, values: Gitlab::VisibilityLevel.string_values,
desc: 'Limit by visibility'
optional :search, type: String, desc: 'Return list of authorized projects matching the search criteria'
optional :owned, type: Boolean, default: false, desc: 'Limit by owned by authenticated user'
......@@ -212,7 +208,7 @@ module API
at_least_one_of :name, :description, :issues_enabled, :merge_requests_enabled,
:wiki_enabled, :builds_enabled, :snippets_enabled,
:shared_runners_enabled, :container_registry_enabled,
:lfs_enabled, :visibility_level, :public_builds,
:lfs_enabled, :visibility, :public_builds,
:request_access_enabled, :only_allow_merge_if_pipeline_succeeds,
:only_allow_merge_if_all_discussions_are_resolved, :path,
:default_branch,
......@@ -223,7 +219,7 @@ module API
authorize_admin_project
attrs = declared_params(include_missing: false)
authorize! :rename_project, user_project if attrs[:name].present?
authorize! :change_visibility_level, user_project if attrs[:visibility_level].present?
authorize! :change_visibility_level, user_project if attrs[:visibility].present?
result = ::Projects::UpdateService.new(user_project, current_user, attrs).execute
......
......@@ -21,9 +21,9 @@ module API
end
params do
optional :default_branch_protection, type: Integer, values: [0, 1, 2], desc: 'Determine if developers can push to master'
optional :default_project_visibility, type: Integer, values: Gitlab::VisibilityLevel.values, desc: 'The default project visibility'
optional :default_snippet_visibility, type: Integer, values: Gitlab::VisibilityLevel.values, desc: 'The default snippet visibility'
optional :default_group_visibility, type: Integer, values: Gitlab::VisibilityLevel.values, desc: 'The default group visibility'
optional :default_project_visibility, type: String, values: Gitlab::VisibilityLevel.string_values, desc: 'The default project visibility'
optional :default_snippet_visibility, type: String, values: Gitlab::VisibilityLevel.string_values, desc: 'The default snippet visibility'
optional :default_group_visibility, type: String, values: Gitlab::VisibilityLevel.string_values, desc: 'The default group visibility'
optional :restricted_visibility_levels, type: Array[String], desc: 'Selected levels cannot be used by non-admin users for projects or snippets. If the public level is restricted, user profiles are only visible to logged in users.'
optional :import_sources, type: Array[String], values: %w[github bitbucket gitlab google_code fogbugz git gitlab_project],
desc: 'Enabled sources for code import during project creation. OmniAuth must be configured for GitHub, Bitbucket, and GitLab.com'
......@@ -141,7 +141,9 @@ module API
:repository_storages, :repository_size_limit
end
put "application/settings" do
if current_settings.update_attributes(declared_params(include_missing: false))
attrs = declared_params(include_missing: false)
if current_settings.update_attributes(attrs)
present current_settings, with: Entities::ApplicationSetting
else
render_validation_error!(current_settings)
......
......@@ -58,10 +58,10 @@ module API
requires :title, type: String, desc: 'The title of a snippet'
requires :file_name, type: String, desc: 'The name of a snippet file'
requires :content, type: String, desc: 'The content of a snippet'
optional :visibility_level, type: Integer,
values: Gitlab::VisibilityLevel.values,
default: Gitlab::VisibilityLevel::INTERNAL,
desc: 'The visibility level of the snippet'
optional :visibility, type: String,
values: Gitlab::VisibilityLevel.string_values,
default: 'internal',
desc: 'The visibility of the snippet'
end
post do
attrs = declared_params(include_missing: false).merge(request: request, api: true)
......@@ -85,10 +85,10 @@ module API
optional :title, type: String, desc: 'The title of a snippet'
optional :file_name, type: String, desc: 'The name of a snippet file'
optional :content, type: String, desc: 'The content of a snippet'
optional :visibility_level, type: Integer,
values: Gitlab::VisibilityLevel.values,
desc: 'The visibility level of the snippet'
at_least_one_of :title, :file_name, :content, :visibility_level
optional :visibility, type: String,
values: Gitlab::VisibilityLevel.string_values,
desc: 'The visibility of the snippet'
at_least_one_of :title, :file_name, :content, :visibility
end
put ':id' do
snippet = snippets_for_current_user.find_by(id: params.delete(:id))
......
......@@ -164,6 +164,73 @@ module API
Gitlab::UrlBuilder.build(merge_request)
end
end
class Group < Grape::Entity
expose :id, :name, :path, :description, :visibility_level
expose :ldap_cn, :ldap_access
expose :ldap_group_links,
using: ::API::Entities::LdapGroupLink,
if: lambda { |group, options| group.ldap_group_links.any? }
expose :lfs_enabled?, as: :lfs_enabled
expose :avatar_url
expose :web_url
expose :request_access_enabled
expose :full_name, :full_path
expose :parent_id
expose :statistics, if: :statistics do
with_options format_with: -> (value) { value.to_i } do
expose :storage_size
expose :repository_size
expose :lfs_objects_size
expose :build_artifacts_size
end
end
end
class GroupDetail < Group
expose :projects, using: Entities::Project
expose :shared_projects, using: Entities::Project
end
class ApplicationSetting < Grape::Entity
expose :id
expose :default_projects_limit
expose :signup_enabled
expose :signin_enabled
expose :gravatar_enabled
expose :sign_in_text
expose :after_sign_up_text
expose :created_at
expose :updated_at
expose :home_page_url
expose :default_branch_protection
expose :restricted_visibility_levels
expose :max_attachment_size
expose :session_expire_delay
expose :default_project_visibility
expose :default_snippet_visibility
expose :default_group_visibility
expose :domain_whitelist
expose :domain_blacklist_enabled
expose :domain_blacklist
expose :user_oauth_applications
expose :after_sign_out_path
expose :container_registry_token_expire_delay
expose :repository_storage
expose :repository_storages
expose :koding_enabled
expose :koding_url
expose :plantuml_enabled
expose :plantuml_url
expose :terminal_max_session_time
end
class Environment < ::API::Entities::EnvironmentBasic
expose :project, using: Entities::Project
end
end
end
end
module API
module V3
class Environments < Grape::API
include ::API::Helpers::CustomValidators
include PaginationParams
before { authenticate! }
......@@ -9,9 +10,66 @@ module API
requires :id, type: String, desc: 'The project ID'
end
resource :projects do
desc 'Get all environments of the project' do
detail 'This feature was introduced in GitLab 8.11.'
success Entities::Environment
end
params do
use :pagination
end
get ':id/environments' do
authorize! :read_environment, user_project
present paginate(user_project.environments), with: Entities::Environment
end
desc 'Creates a new environment' do
detail 'This feature was introduced in GitLab 8.11.'
success Entities::Environment
end
params do
requires :name, type: String, desc: 'The name of the environment to be created'
optional :external_url, type: String, desc: 'URL on which this deployment is viewable'
optional :slug, absence: { message: "is automatically generated and cannot be changed" }
end
post ':id/environments' do
authorize! :create_environment, user_project
environment = user_project.environments.create(declared_params)
if environment.persisted?
present environment, with: Entities::Environment
else
render_validation_error!(environment)
end
end
desc 'Updates an existing environment' do
detail 'This feature was introduced in GitLab 8.11.'
success Entities::Environment
end
params do
requires :environment_id, type: Integer, desc: 'The environment ID'
optional :name, type: String, desc: 'The new environment name'
optional :external_url, type: String, desc: 'The new URL on which this deployment is viewable'
optional :slug, absence: { message: "is automatically generated and cannot be changed" }
end
put ':id/environments/:environment_id' do
authorize! :update_environment, user_project
environment = user_project.environments.find(params[:environment_id])
update_params = declared_params(include_missing: false).extract!(:name, :external_url)
if environment.update(update_params)
present environment, with: Entities::Environment
else
render_validation_error!(environment)
end
end
desc 'Deletes an existing environment' do
detail 'This feature was introduced in GitLab 8.11.'
success ::API::Entities::Environment
success Entities::Environment
end
params do
requires :environment_id, type: Integer, desc: 'The environment ID'
......@@ -21,7 +79,7 @@ module API
environment = user_project.environments.find(params[:environment_id])
present environment.destroy, with: ::API::Entities::Environment
present environment.destroy, with: Entities::Environment
end
end
end
......
......@@ -6,13 +6,28 @@ module API
before { authenticate! }
helpers do
params :optional_params do
optional :description, type: String, desc: 'The description of the group'
optional :visibility_level, type: Integer, desc: 'The visibility level of the group'
optional :lfs_enabled, type: Boolean, desc: 'Enable/disable LFS for the projects in this group'
optional :request_access_enabled, type: Boolean, desc: 'Allow users to request member access'
optional :membership_lock, type: Boolean, desc: 'Prevent adding new members to project membership within this group'
optional :share_with_group_lock, type: Boolean, desc: 'Prevent sharing a project with another group within this group'
end
params :optional_params_ee do
optional :ldap_cn, type: String, desc: 'LDAP Common Name'
optional :ldap_access, type: Integer, desc: 'A valid access level'
all_or_none_of :ldap_cn, :ldap_access
end
params :statistics_params do
optional :statistics, type: Boolean, default: false, desc: 'Include project statistics'
end
def present_groups(groups, options = {})
options = options.reverse_merge(
with: ::API::Entities::Group,
with: Entities::Group,
current_user: current_user,
)
......@@ -22,8 +37,36 @@ module API
end
resource :groups do
desc 'Get a groups list' do
success Entities::Group
end
params do
use :statistics_params
optional :skip_groups, type: Array[Integer], desc: 'Array of group ids to exclude from list'
optional :all_available, type: Boolean, desc: 'Show all group that you have access to'
optional :search, type: String, desc: 'Search for a specific group'
optional :order_by, type: String, values: %w[name path], default: 'name', desc: 'Order by name or path'
optional :sort, type: String, values: %w[asc desc], default: 'asc', desc: 'Sort by asc (ascending) or desc (descending)'
use :pagination
end
get do
groups = if current_user.admin
Group.all
elsif params[:all_available]
GroupsFinder.new.execute(current_user)
else
current_user.groups
end
groups = groups.search(params[:search]) if params[:search].present?
groups = groups.where.not(id: params[:skip_groups]) if params[:skip_groups].present?
groups = groups.reorder(params[:order_by] => params[:sort])
present_groups groups, statistics: params[:statistics] && current_user.is_admin?
end
desc 'Get list of owned groups for authenticated user' do
success ::API::Entities::Group
success Entities::Group
end
params do
use :pagination
......@@ -32,6 +75,128 @@ module API
get '/owned' do
present_groups current_user.owned_groups, statistics: params[:statistics]
end
desc 'Create a group. Available only for users who can create groups.' do
success Entities::Group
end
params do
requires :name, type: String, desc: 'The name of the group'
requires :path, type: String, desc: 'The path of the group'
optional :parent_id, type: Integer, desc: 'The parent group id for creating nested group'
use :optional_params
use :optional_params_ee
end
post do
authorize! :create_group
ldap_link_attrs = {
cn: params.delete(:ldap_cn),
group_access: params.delete(:ldap_access)
}
group = ::Groups::CreateService.new(current_user, declared_params(include_missing: false)).execute
if group.persisted?
# NOTE: add backwards compatibility for single ldap link
if ldap_link_attrs[:cn].present?
group.ldap_group_links.create(
cn: ldap_link_attrs[:cn],
group_access: ldap_link_attrs[:group_access]
)
end
present group, with: Entities::Group, current_user: current_user
else
render_api_error!("Failed to save group #{group.errors.messages}", 400)
end
end
end
params do
requires :id, type: String, desc: 'The ID of a group'
end
resource :groups do
desc 'Update a group. Available only for users who can administrate groups.' do
success Entities::Group
end
params do
optional :name, type: String, desc: 'The name of the group'
optional :path, type: String, desc: 'The path of the group'
use :optional_params
at_least_one_of :name, :path, :description, :visibility_level,
:lfs_enabled, :request_access_enabled
end
put ':id' do
group = find_group!(params[:id])
authorize! :admin_group, group
if ::Groups::UpdateService.new(group, current_user, declared_params(include_missing: false)).execute
present group, with: Entities::GroupDetail, current_user: current_user
else
render_validation_error!(group)
end
end
desc 'Get a single group, with containing projects.' do
success Entities::GroupDetail
end
get ":id" do
group = find_group!(params[:id])
present group, with: Entities::GroupDetail, current_user: current_user
end
desc 'Remove a group.'
delete ":id" do
group = find_group!(params[:id])
authorize! :admin_group, group
present ::Groups::DestroyService.new(group, current_user).execute, with: Entities::GroupDetail, current_user: current_user
end
desc 'Get a list of projects in this group.' do
success Entities::Project
end
params do
optional :archived, type: Boolean, default: false, desc: 'Limit by archived status'
optional :visibility, type: String, values: %w[public internal private],
desc: 'Limit by visibility'
optional :search, type: String, desc: 'Return list of authorized projects matching the search criteria'
optional :order_by, type: String, values: %w[id name path created_at updated_at last_activity_at],
default: 'created_at', desc: 'Return projects ordered by field'
optional :sort, type: String, values: %w[asc desc], default: 'desc',
desc: 'Return projects sorted in ascending and descending order'
optional :simple, type: Boolean, default: false,
desc: 'Return only the ID, URL, name, and path of each project'
optional :owned, type: Boolean, default: false, desc: 'Limit by owned by authenticated user'
optional :starred, type: Boolean, default: false, desc: 'Limit by starred status'
use :pagination
end
get ":id/projects" do
group = find_group!(params[:id])
projects = GroupProjectsFinder.new(group).execute(current_user)
projects = filter_projects(projects)
entity = params[:simple] ? ::API::Entities::BasicProjectDetails : Entities::Project
present paginate(projects), with: entity, current_user: current_user
end
desc 'Transfer a project to the group namespace. Available only for admin.' do
success Entities::GroupDetail
end
params do
requires :project_id, type: String, desc: 'The ID or path of the project'
end
post ":id/projects/:project_id" do
authenticated_as_admin!
group = find_group!(params[:id])
project = find_project!(params[:project_id])
result = ::Projects::TransferService.new(project, current_user).execute(group)
if result
present group, with: Entities::GroupDetail, current_user: current_user
else
render_api_error!("Failed to transfer project #{project.errors.messages}", 400)
end
end
end
end
end
......
module API
module V3
class Snippets < Grape::API
include PaginationParams
before { authenticate! }
resource :snippets do
helpers do
def snippets_for_current_user
SnippetsFinder.new.execute(current_user, filter: :by_user, user: current_user)
end
def public_snippets
SnippetsFinder.new.execute(current_user, filter: :public)
end
end
desc 'Get a snippets list for authenticated user' do
detail 'This feature was introduced in GitLab 8.15.'
success ::API::Entities::PersonalSnippet
end
params do
use :pagination
end
get do
present paginate(snippets_for_current_user), with: ::API::Entities::PersonalSnippet
end
desc 'List all public snippets current_user has access to' do
detail 'This feature was introduced in GitLab 8.15.'
success ::API::Entities::PersonalSnippet
end
params do
use :pagination
end
get 'public' do
present paginate(public_snippets), with: ::API::Entities::PersonalSnippet
end
desc 'Get a single snippet' do
detail 'This feature was introduced in GitLab 8.15.'
success ::API::Entities::PersonalSnippet
end
params do
requires :id, type: Integer, desc: 'The ID of a snippet'
end
get ':id' do
snippet = snippets_for_current_user.find(params[:id])
present snippet, with: ::API::Entities::PersonalSnippet
end
desc 'Create new snippet' do
detail 'This feature was introduced in GitLab 8.15.'
success ::API::Entities::PersonalSnippet
end
params do
requires :title, type: String, desc: 'The title of a snippet'
requires :file_name, type: String, desc: 'The name of a snippet file'
requires :content, type: String, desc: 'The content of a snippet'
optional :visibility_level, type: Integer,
values: Gitlab::VisibilityLevel.values,
default: Gitlab::VisibilityLevel::INTERNAL,
desc: 'The visibility level of the snippet'
end
post do
attrs = declared_params(include_missing: false).merge(request: request, api: true)
snippet = CreateSnippetService.new(nil, current_user, attrs).execute
if snippet.persisted?
present snippet, with: ::API::Entities::PersonalSnippet
else
render_validation_error!(snippet)
end
end
desc 'Update an existing snippet' do
detail 'This feature was introduced in GitLab 8.15.'
success ::API::Entities::PersonalSnippet
end
params do
requires :id, type: Integer, desc: 'The ID of a snippet'
optional :title, type: String, desc: 'The title of a snippet'
optional :file_name, type: String, desc: 'The name of a snippet file'
optional :content, type: String, desc: 'The content of a snippet'
optional :visibility_level, type: Integer,
values: Gitlab::VisibilityLevel.values,
desc: 'The visibility level of the snippet'
at_least_one_of :title, :file_name, :content, :visibility_level
end
put ':id' do
snippet = snippets_for_current_user.find_by(id: params.delete(:id))
return not_found!('Snippet') unless snippet
authorize! :update_personal_snippet, snippet
attrs = declared_params(include_missing: false)
UpdateSnippetService.new(nil, current_user, snippet, attrs).execute
if snippet.persisted?
present snippet, with: ::API::Entities::PersonalSnippet
else
render_validation_error!(snippet)
end
end
desc 'Remove snippet' do
detail 'This feature was introduced in GitLab 8.15.'
success ::API::Entities::PersonalSnippet
end
params do
requires :id, type: Integer, desc: 'The ID of a snippet'
end
delete ':id' do
snippet = snippets_for_current_user.find_by(id: params.delete(:id))
return not_found!('Snippet') unless snippet
authorize! :destroy_personal_snippet, snippet
snippet.destroy
no_content!
end
desc 'Get a raw snippet' do
detail 'This feature was introduced in GitLab 8.15.'
end
params do
requires :id, type: Integer, desc: 'The ID of a snippet'
end
get ":id/raw" do
snippet = snippets_for_current_user.find_by(id: params.delete(:id))
return not_found!('Snippet') unless snippet
env['api.format'] = :txt
content_type 'text/plain'
present snippet.content
end
end
end
end
end
......@@ -35,6 +35,10 @@ module Gitlab
class << self
delegate :values, to: :options
def string_values
string_options.keys
end
def options
{
'Private' => PRIVATE,
......@@ -43,6 +47,14 @@ module Gitlab
}
end
def string_options
{
'private' => PRIVATE,
'internal' => INTERNAL,
'public' => PUBLIC
}
end
def highest_allowed_level
restricted_levels = current_application_settings.restricted_visibility_levels
......@@ -82,18 +94,39 @@ module Gitlab
level_name
end
def level_value(level)
return string_options[level] if level.is_a? String
level
end
def string_level(level)
string_options.key(level)
end
end
def private?
visibility_level_field == PRIVATE
visibility_level_value == PRIVATE
end
def internal?
visibility_level_field == INTERNAL
visibility_level_value == INTERNAL
end
def public?
visibility_level_field == PUBLIC
visibility_level_value == PUBLIC
end
def visibility_level_value
self[visibility_level_field]
end
def visibility
Gitlab::VisibilityLevel.string_level(visibility_level_value)
end
def visibility=(level)
self[visibility_level_field] = Gitlab::VisibilityLevel.level_value(level)
end
end
end
......@@ -24,6 +24,7 @@ describe API::Environments, api: true do
expect(json_response.first['name']).to eq(environment.name)
expect(json_response.first['external_url']).to eq(environment.external_url)
expect(json_response.first['project']['id']).to eq(project.id)
expect(json_response.first['project']['visibility']).to be_present
end
end
......
......@@ -190,7 +190,7 @@ describe API::Groups, api: true do
expect(json_response['name']).to eq(group1.name)
expect(json_response['path']).to eq(group1.path)
expect(json_response['description']).to eq(group1.description)
expect(json_response['visibility_level']).to eq(group1.visibility_level)
expect(json_response['visibility']).to eq(Gitlab::VisibilityLevel.string_level(group1.visibility_level))
expect(json_response['avatar_url']).to eq(group1.avatar_url)
expect(json_response['web_url']).to eq(group1.web_url)
expect(json_response['request_access_enabled']).to eq(group1.request_access_enabled)
......@@ -309,7 +309,7 @@ describe API::Groups, api: true do
expect(json_response.length).to eq(2)
project_names = json_response.map { |proj| proj['name'] }
expect(project_names).to match_array([project1.name, project3.name])
expect(json_response.first['visibility_level']).to be_present
expect(json_response.first['visibility']).to be_present
end
it "returns the group's projects with simple representation" do
......@@ -320,7 +320,7 @@ describe API::Groups, api: true do
expect(json_response.length).to eq(2)
project_names = json_response.map { |proj| proj['name'] }
expect(project_names).to match_array([project1.name, project3.name])
expect(json_response.first['visibility_level']).not_to be_present
expect(json_response.first['visibility']).not_to be_present
end
it 'filters the groups projects' do
......
......@@ -44,7 +44,7 @@ describe API::ProjectSnippets, api: true do
title: 'Test Title',
file_name: 'test.rb',
code: 'puts "hello world"',
visibility_level: Snippet::PUBLIC
visibility: 'public'
}
end
......@@ -56,7 +56,7 @@ describe API::ProjectSnippets, api: true do
expect(snippet.content).to eq(params[:code])
expect(snippet.title).to eq(params[:title])
expect(snippet.file_name).to eq(params[:file_name])
expect(snippet.visibility_level).to eq(params[:visibility_level])
expect(snippet.visibility_level).to eq(Snippet::PUBLIC)
end
it 'returns 400 for missing parameters' do
......@@ -80,14 +80,14 @@ describe API::ProjectSnippets, api: true do
context 'when the snippet is private' do
it 'creates the snippet' do
expect { create_snippet(project, visibility_level: Snippet::PRIVATE) }.
expect { create_snippet(project, visibility: 'private') }.
to change { Snippet.count }.by(1)
end
end
context 'when the snippet is public' do
it 'rejects the shippet' do
expect { create_snippet(project, visibility_level: Snippet::PUBLIC) }.
it 'rejects the snippet' do
expect { create_snippet(project, visibility: 'public') }.
not_to change { Snippet.count }
expect(response).to have_http_status(400)
......@@ -95,7 +95,7 @@ describe API::ProjectSnippets, api: true do
end
it 'creates a spam log' do
expect { create_snippet(project, visibility_level: Snippet::PUBLIC) }.
expect { create_snippet(project, visibility: 'public') }.
to change { SpamLog.count }.by(1)
end
end
......@@ -165,7 +165,7 @@ describe API::ProjectSnippets, api: true do
let(:visibility_level) { Snippet::PRIVATE }
it 'rejects the snippet' do
expect { update_snippet(title: 'Foo', visibility_level: Snippet::PUBLIC) }.
expect { update_snippet(title: 'Foo', visibility: 'public') }.
not_to change { snippet.reload.title }
expect(response).to have_http_status(400)
......@@ -173,7 +173,7 @@ describe API::ProjectSnippets, api: true do
end
it 'creates a spam log' do
expect { update_snippet(title: 'Foo', visibility_level: Snippet::PUBLIC) }.
expect { update_snippet(title: 'Foo', visibility: 'public') }.
to change { SpamLog.count }.by(1)
end
end
......
......@@ -340,24 +340,27 @@ describe API::Projects, api: true do
end
it 'sets a project as public' do
project = attributes_for(:project, :public)
project = attributes_for(:project, visibility: 'public')
post api('/projects', user), project
expect(json_response['public']).to be_truthy
expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::PUBLIC)
expect(json_response['visibility']).to eq('public')
end
it 'sets a project as internal' do
project = attributes_for(:project, :internal)
project = attributes_for(:project, visibility: 'internal')
post api('/projects', user), project
expect(json_response['public']).to be_falsey
expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::INTERNAL)
expect(json_response['visibility']).to eq('internal')
end
it 'sets a project as private' do
project = attributes_for(:project, :private)
project = attributes_for(:project, visibility: 'private')
post api('/projects', user), project
expect(json_response['public']).to be_falsey
expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::PRIVATE)
expect(json_response['visibility']).to eq('private')
end
it 'sets a project as allowing merge even if build fails' do
......@@ -397,7 +400,7 @@ describe API::Projects, api: true do
end
context 'when a visibility level is restricted' do
let(:project_param) { attributes_for(:project, :public) }
let(:project_param) { attributes_for(:project, visibility: 'public') }
before do
stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC])
......@@ -415,10 +418,7 @@ describe API::Projects, api: true do
it 'allows an admin to override restricted visibility settings' do
post api('/projects', admin), project_param
expect(json_response['public']).to be_truthy
expect(json_response['visibility_level']).to(
eq(Gitlab::VisibilityLevel::PUBLIC)
)
expect(json_response['visibility']).to eq('public')
end
end
end
......@@ -459,28 +459,29 @@ describe API::Projects, api: true do
end
it 'sets a project as public' do
project = attributes_for(:project, :public)
project = attributes_for(:project, visibility: 'public')
post api("/projects/user/#{user.id}", admin), project
expect(response).to have_http_status(201)
expect(json_response['public']).to be_truthy
expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::PUBLIC)
expect(json_response['visibility']).to eq('public')
end
it 'sets a project as internal' do
project = attributes_for(:project, :internal)
project = attributes_for(:project, visibility: 'internal')
post api("/projects/user/#{user.id}", admin), project
expect(response).to have_http_status(201)
expect(json_response['public']).to be_falsey
expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::INTERNAL)
expect(json_response['visibility']).to eq('internal')
end
it 'sets a project as private' do
project = attributes_for(:project, :private)
project = attributes_for(:project, visibility: 'private')
post api("/projects/user/#{user.id}", admin), project
expect(json_response['public']).to be_falsey
expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::PRIVATE)
expect(json_response['visibility']).to eq('private')
end
it 'sets a project as allowing merge even if build fails' do
......@@ -553,9 +554,8 @@ describe API::Projects, api: true do
expect(json_response['description']).to eq(project.description)
expect(json_response['default_branch']).to eq(project.default_branch)
expect(json_response['tag_list']).to be_an Array
expect(json_response['public']).to be_falsey
expect(json_response['archived']).to be_falsey
expect(json_response['visibility_level']).to be_present
expect(json_response['visibility']).to be_present
expect(json_response['ssh_url_to_repo']).to be_present
expect(json_response['http_url_to_repo']).to be_present
expect(json_response['web_url']).to be_present
......@@ -606,9 +606,8 @@ describe API::Projects, api: true do
expect(json_response['description']).to eq(project.description)
expect(json_response['default_branch']).to eq(project.default_branch)
expect(json_response['tag_list']).to be_an Array
expect(json_response['public']).to be_falsey
expect(json_response['archived']).to be_falsey
expect(json_response['visibility_level']).to be_present
expect(json_response['visibility']).to be_present
expect(json_response['ssh_url_to_repo']).to be_present
expect(json_response['http_url_to_repo']).to be_present
expect(json_response['web_url']).to be_present
......@@ -863,8 +862,7 @@ describe API::Projects, api: true do
describe 'POST /projects/:id/snippets' do
it 'creates a new project snippet' do
post api("/projects/#{project.id}/snippets", user),
title: 'api test', file_name: 'sample.rb', code: 'test',
visibility_level: Gitlab::VisibilityLevel::PRIVATE
title: 'api test', file_name: 'sample.rb', code: 'test', visibility: 'private'
expect(response).to have_http_status(201)
expect(json_response['title']).to eq('api test')
end
......@@ -1116,7 +1114,7 @@ describe API::Projects, api: true do
end
it 'updates visibility_level' do
project_param = { visibility_level: Gitlab::VisibilityLevel::PUBLIC }
project_param = { visibility: 'public' }
put api("/projects/#{project3.id}", user), project_param
expect(response).to have_http_status(200)
project_param.each_pair do |k, v|
......@@ -1126,13 +1124,13 @@ describe API::Projects, api: true do
it 'updates visibility_level from public to private' do
project3.update_attributes({ visibility_level: Gitlab::VisibilityLevel::PUBLIC })
project_param = { visibility_level: Gitlab::VisibilityLevel::PRIVATE }
project_param = { visibility: 'private' }
put api("/projects/#{project3.id}", user), project_param
expect(response).to have_http_status(200)
project_param.each_pair do |k, v|
expect(json_response[k.to_s]).to eq(v)
end
expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::PRIVATE)
expect(json_response['visibility']).to eq('private')
end
it 'does not update name to existing name' do
......@@ -1208,7 +1206,7 @@ describe API::Projects, api: true do
end
it 'does not update visibility_level' do
project_param = { visibility_level: Gitlab::VisibilityLevel::PUBLIC }
project_param = { visibility: 'public' }
put api("/projects/#{project3.id}", user4), project_param
expect(response).to have_http_status(403)
end
......
......@@ -19,6 +19,9 @@ describe API::Settings, 'Settings', api: true do
expect(json_response['koding_url']).to be_nil
expect(json_response['plantuml_enabled']).to be_falsey
expect(json_response['plantuml_url']).to be_nil
expect(json_response['default_project_visibility']).to be_a String
expect(json_response['default_snippet_visibility']).to be_a String
expect(json_response['default_group_visibility']).to be_a String
end
end
......@@ -38,6 +41,8 @@ describe API::Settings, 'Settings', api: true do
koding_url: 'http://koding.example.com',
plantuml_enabled: true,
plantuml_url: 'http://plantuml.example.com',
default_snippet_visibility: 'internal',
restricted_visibility_levels: ['public'],
default_artifacts_expire_in: '2 days'
expect(response).to have_http_status(200)
......@@ -48,6 +53,8 @@ describe API::Settings, 'Settings', api: true do
expect(json_response['koding_url']).to eq('http://koding.example.com')
expect(json_response['plantuml_enabled']).to be_truthy
expect(json_response['plantuml_url']).to eq('http://plantuml.example.com')
expect(json_response['default_snippet_visibility']).to eq('internal')
expect(json_response['restricted_visibility_levels']).to eq(['public'])
expect(json_response['default_artifacts_expire_in']).to eq('2 days')
end
end
......
......@@ -87,7 +87,7 @@ describe API::Snippets, api: true do
title: 'Test Title',
file_name: 'test.rb',
content: 'puts "hello world"',
visibility_level: Snippet::PUBLIC
visibility: 'public'
}
end
......@@ -120,14 +120,14 @@ describe API::Snippets, api: true do
context 'when the snippet is private' do
it 'creates the snippet' do
expect { create_snippet(visibility_level: Snippet::PRIVATE) }.
expect { create_snippet(visibility: 'private') }.
to change { Snippet.count }.by(1)
end
end
context 'when the snippet is public' do
it 'rejects the shippet' do
expect { create_snippet(visibility_level: Snippet::PUBLIC) }.
expect { create_snippet(visibility: 'public') }.
not_to change { Snippet.count }
expect(response).to have_http_status(400)
......@@ -135,7 +135,7 @@ describe API::Snippets, api: true do
end
it 'creates a spam log' do
expect { create_snippet(visibility_level: Snippet::PUBLIC) }.
expect { create_snippet(visibility: 'public') }.
to change { SpamLog.count }.by(1)
end
end
......@@ -218,12 +218,12 @@ describe API::Snippets, api: true do
let(:visibility_level) { Snippet::PRIVATE }
it 'rejects the snippet' do
expect { update_snippet(title: 'Foo', visibility_level: Snippet::PUBLIC) }.
expect { update_snippet(title: 'Foo', visibility: 'public') }.
not_to change { snippet.reload.title }
end
it 'creates a spam log' do
expect { update_snippet(title: 'Foo', visibility_level: Snippet::PUBLIC) }.
expect { update_snippet(title: 'Foo', visibility: 'public') }.
to change { SpamLog.count }.by(1)
end
end
......
......@@ -12,6 +12,132 @@ describe API::V3::Environments, api: true do
project.team << [user, :master]
end
shared_examples 'a paginated resources' do
before do
# Fires the request
request
end
it 'has pagination headers' do
expect(response.headers).to include('X-Total')
expect(response.headers).to include('X-Total-Pages')
expect(response.headers).to include('X-Per-Page')
expect(response.headers).to include('X-Page')
expect(response.headers).to include('X-Next-Page')
expect(response.headers).to include('X-Prev-Page')
expect(response.headers).to include('Link')
end
end
describe 'GET /projects/:id/environments' do
context 'as member of the project' do
it_behaves_like 'a paginated resources' do
let(:request) { get v3_api("/projects/#{project.id}/environments", user) }
end
it 'returns project environments' do
get v3_api("/projects/#{project.id}/environments", user)
expect(response).to have_http_status(200)
expect(json_response).to be_an Array
expect(json_response.size).to eq(1)
expect(json_response.first['name']).to eq(environment.name)
expect(json_response.first['external_url']).to eq(environment.external_url)
expect(json_response.first['project']['id']).to eq(project.id)
expect(json_response.first['project']['visibility_level']).to be_present
end
end
context 'as non member' do
it 'returns a 404 status code' do
get v3_api("/projects/#{project.id}/environments", non_member)
expect(response).to have_http_status(404)
end
end
end
describe 'POST /projects/:id/environments' do
context 'as a member' do
it 'creates a environment with valid params' do
post v3_api("/projects/#{project.id}/environments", user), name: "mepmep"
expect(response).to have_http_status(201)
expect(json_response['name']).to eq('mepmep')
expect(json_response['slug']).to eq('mepmep')
expect(json_response['external']).to be nil
end
it 'requires name to be passed' do
post v3_api("/projects/#{project.id}/environments", user), external_url: 'test.gitlab.com'
expect(response).to have_http_status(400)
end
it 'returns a 400 if environment already exists' do
post v3_api("/projects/#{project.id}/environments", user), name: environment.name
expect(response).to have_http_status(400)
end
it 'returns a 400 if slug is specified' do
post v3_api("/projects/#{project.id}/environments", user), name: "foo", slug: "foo"
expect(response).to have_http_status(400)
expect(json_response["error"]).to eq("slug is automatically generated and cannot be changed")
end
end
context 'a non member' do
it 'rejects the request' do
post v3_api("/projects/#{project.id}/environments", non_member), name: 'gitlab.com'
expect(response).to have_http_status(404)
end
it 'returns a 400 when the required params are missing' do
post v3_api("/projects/12345/environments", non_member), external_url: 'http://env.git.com'
end
end
end
describe 'PUT /projects/:id/environments/:environment_id' do
it 'returns a 200 if name and external_url are changed' do
url = 'https://mepmep.whatever.ninja'
put v3_api("/projects/#{project.id}/environments/#{environment.id}", user),
name: 'Mepmep', external_url: url
expect(response).to have_http_status(200)
expect(json_response['name']).to eq('Mepmep')
expect(json_response['external_url']).to eq(url)
end
it "won't allow slug to be changed" do
slug = environment.slug
api_url = v3_api("/projects/#{project.id}/environments/#{environment.id}", user)
put api_url, slug: slug + "-foo"
expect(response).to have_http_status(400)
expect(json_response["error"]).to eq("slug is automatically generated and cannot be changed")
end
it "won't update the external_url if only the name is passed" do
url = environment.external_url
put v3_api("/projects/#{project.id}/environments/#{environment.id}", user),
name: 'Mepmep'
expect(response).to have_http_status(200)
expect(json_response['name']).to eq('Mepmep')
expect(json_response['external_url']).to eq(url)
end
it 'returns a 404 if the environment does not exist' do
put v3_api("/projects/#{project.id}/environments/12345", user)
expect(response).to have_http_status(404)
end
end
describe 'DELETE /projects/:id/environments/:environment_id' do
context 'as a master' do
it 'returns a 200 for an existing environment' do
......
......@@ -4,12 +4,151 @@ describe API::V3::Groups, api: true do
include ApiHelpers
include UploadHelpers
let(:user1) { create(:user, can_create_group: false) }
let(:user2) { create(:user) }
let(:user3) { create(:user) }
let(:admin) { create(:admin) }
let!(:group1) { create(:group, avatar: File.open(uploaded_image_temp_path)) }
let!(:group2) { create(:group, :private) }
let!(:project1) { create(:empty_project, namespace: group1) }
let!(:project2) { create(:empty_project, namespace: group2) }
let!(:project3) { create(:empty_project, namespace: group1, path: 'test', visibility_level: Gitlab::VisibilityLevel::PRIVATE) }
before do
group1.add_owner(user1)
group2.add_owner(user2)
group1.ldap_group_links.create cn: 'ldap-group', group_access: Gitlab::Access::MASTER, provider: 'ldap'
end
describe "GET /groups" do
context "when unauthenticated" do
it "returns authentication error" do
get v3_api("/groups")
expect(response).to have_http_status(401)
end
end
context "when authenticated as user" do
it "normal user: returns an array of groups of user1" do
get v3_api("/groups", user1)
expect(response).to have_http_status(200)
expect(json_response).to be_an Array
expect(json_response.length).to eq(1)
expect(json_response)
.to satisfy_one { |group| group['name'] == group1.name }
expect(json_response.first['ldap_cn']).to eq(group1.ldap_cn)
expect(json_response.first['ldap_access']).to eq(group1.ldap_access)
ldap_group_link = json_response.first['ldap_group_links'].first
expect(ldap_group_link['cn']).to eq(group1.ldap_cn)
expect(ldap_group_link['group_access']).to eq(group1.ldap_access)
expect(ldap_group_link['provider']).to eq('ldap')
end
it "does not include statistics" do
get v3_api("/groups", user1), statistics: true
expect(response).to have_http_status(200)
expect(json_response).to be_an Array
expect(json_response.first).not_to include 'statistics'
end
end
context "when authenticated as admin" do
it "admin: returns an array of all groups" do
get v3_api("/groups", admin)
expect(response).to have_http_status(200)
expect(json_response).to be_an Array
expect(json_response.length).to eq(2)
end
it "does not include statistics by default" do
get v3_api("/groups", admin)
expect(response).to have_http_status(200)
expect(json_response).to be_an Array
expect(json_response.first).not_to include('statistics')
end
it "includes statistics if requested" do
attributes = {
storage_size: 702,
repository_size: 123,
lfs_objects_size: 234,
build_artifacts_size: 345,
}.stringify_keys
project1.statistics.update!(attributes)
get v3_api("/groups", admin), statistics: true
expect(response).to have_http_status(200)
expect(json_response).to be_an Array
expect(json_response)
.to satisfy_one { |group| group['statistics'] == attributes }
end
end
context "when using skip_groups in request" do
it "returns all groups excluding skipped groups" do
get v3_api("/groups", admin), skip_groups: [group2.id]
expect(response).to have_http_status(200)
expect(json_response).to be_an Array
expect(json_response.length).to eq(1)
end
end
context "when using all_available in request" do
let(:response_groups) { json_response.map { |group| group['name'] } }
it "returns all groups you have access to" do
public_group = create :group, :public
get v3_api("/groups", user1), all_available: true
expect(response).to have_http_status(200)
expect(json_response).to be_an Array
expect(response_groups).to contain_exactly(public_group.name, group1.name)
end
end
context "when using sorting" do
let(:group3) { create(:group, name: "a#{group1.name}", path: "z#{group1.path}") }
let(:response_groups) { json_response.map { |group| group['name'] } }
before do
group3.add_owner(user1)
end
it "sorts by name ascending by default" do
get v3_api("/groups", user1)
expect(response).to have_http_status(200)
expect(json_response).to be_an Array
expect(response_groups).to eq([group3.name, group1.name])
end
it "sorts in descending order when passed" do
get v3_api("/groups", user1), sort: "desc"
expect(response).to have_http_status(200)
expect(json_response).to be_an Array
expect(response_groups).to eq([group1.name, group3.name])
end
it "sorts by the order_by param" do
get v3_api("/groups", user1), order_by: "path"
expect(response).to have_http_status(200)
expect(json_response).to be_an Array
expect(response_groups).to eq([group1.name, group3.name])
end
end
end
describe 'GET /groups/owned' do
......@@ -32,4 +171,427 @@ describe API::V3::Groups, api: true do
end
end
end
describe "GET /groups/:id" do
context "when authenticated as user" do
it "returns one of user1's groups" do
project = create(:empty_project, namespace: group2, path: 'Foo')
create(:project_group_link, project: project, group: group1)
get v3_api("/groups/#{group1.id}", user1)
expect(response).to have_http_status(200)
expect(json_response['id']).to eq(group1.id)
expect(json_response['name']).to eq(group1.name)
expect(json_response['path']).to eq(group1.path)
expect(json_response['description']).to eq(group1.description)
expect(json_response['visibility_level']).to eq(group1.visibility_level)
expect(json_response['avatar_url']).to eq(group1.avatar_url)
expect(json_response['web_url']).to eq(group1.web_url)
expect(json_response['request_access_enabled']).to eq(group1.request_access_enabled)
expect(json_response['full_name']).to eq(group1.full_name)
expect(json_response['full_path']).to eq(group1.full_path)
expect(json_response['parent_id']).to eq(group1.parent_id)
expect(json_response['projects']).to be_an Array
expect(json_response['projects'].length).to eq(2)
expect(json_response['shared_projects']).to be_an Array
expect(json_response['shared_projects'].length).to eq(1)
expect(json_response['shared_projects'][0]['id']).to eq(project.id)
end
it "does not return a non existing group" do
get v3_api("/groups/1328", user1)
expect(response).to have_http_status(404)
end
it "does not return a group not attached to user1" do
get v3_api("/groups/#{group2.id}", user1)
expect(response).to have_http_status(404)
end
end
context "when authenticated as admin" do
it "returns any existing group" do
get v3_api("/groups/#{group2.id}", admin)
expect(response).to have_http_status(200)
expect(json_response['name']).to eq(group2.name)
end
it "does not return a non existing group" do
get v3_api("/groups/1328", admin)
expect(response).to have_http_status(404)
end
end
context 'when using group path in URL' do
it 'returns any existing group' do
get v3_api("/groups/#{group1.path}", admin)
expect(response).to have_http_status(200)
expect(json_response['name']).to eq(group1.name)
end
it 'does not return a non existing group' do
get v3_api('/groups/unknown', admin)
expect(response).to have_http_status(404)
end
it 'does not return a group not attached to user1' do
get v3_api("/groups/#{group2.path}", user1)
expect(response).to have_http_status(404)
end
end
end
describe 'PUT /groups/:id' do
let(:new_group_name) { 'New Group'}
context 'when authenticated as the group owner' do
it 'updates the group' do
put v3_api("/groups/#{group1.id}", user1), name: new_group_name, request_access_enabled: true
expect(response).to have_http_status(200)
expect(json_response['name']).to eq(new_group_name)
expect(json_response['request_access_enabled']).to eq(true)
end
it 'returns 404 for a non existing group' do
put v3_api('/groups/1328', user1), name: new_group_name
expect(response).to have_http_status(404)
end
end
context 'when authenticated as the admin' do
it 'updates the group' do
put v3_api("/groups/#{group1.id}", admin), name: new_group_name
expect(response).to have_http_status(200)
expect(json_response['name']).to eq(new_group_name)
end
end
context 'when authenticated as an user that can see the group' do
it 'does not updates the group' do
put v3_api("/groups/#{group1.id}", user2), name: new_group_name
expect(response).to have_http_status(403)
end
end
context 'when authenticated as an user that cannot see the group' do
it 'returns 404 when trying to update the group' do
put v3_api("/groups/#{group2.id}", user1), name: new_group_name
expect(response).to have_http_status(404)
end
end
end
describe "GET /groups/:id/projects" do
context "when authenticated as user" do
it "returns the group's projects" do
get v3_api("/groups/#{group1.id}/projects", user1)
expect(response).to have_http_status(200)
expect(json_response.length).to eq(2)
project_names = json_response.map { |proj| proj['name'] }
expect(project_names).to match_array([project1.name, project3.name])
expect(json_response.first['visibility_level']).to be_present
end
it "returns the group's projects with simple representation" do
get v3_api("/groups/#{group1.id}/projects", user1), simple: true
expect(response).to have_http_status(200)
expect(json_response.length).to eq(2)
project_names = json_response.map { |proj| proj['name'] }
expect(project_names).to match_array([project1.name, project3.name])
expect(json_response.first['visibility_level']).not_to be_present
end
it 'filters the groups projects' do
public_project = create(:empty_project, :public, path: 'test1', group: group1)
get v3_api("/groups/#{group1.id}/projects", user1), visibility: 'public'
expect(response).to have_http_status(200)
expect(json_response).to be_an(Array)
expect(json_response.length).to eq(1)
expect(json_response.first['name']).to eq(public_project.name)
end
it "does not return a non existing group" do
get v3_api("/groups/1328/projects", user1)
expect(response).to have_http_status(404)
end
it "does not return a group not attached to user1" do
get v3_api("/groups/#{group2.id}/projects", user1)
expect(response).to have_http_status(404)
end
it "only returns projects to which user has access" do
project3.team << [user3, :developer]
get v3_api("/groups/#{group1.id}/projects", user3)
expect(response).to have_http_status(200)
expect(json_response.length).to eq(1)
expect(json_response.first['name']).to eq(project3.name)
end
it 'only returns the projects owned by user' do
project2.group.add_owner(user3)
get v3_api("/groups/#{project2.group.id}/projects", user3), owned: true
expect(response).to have_http_status(200)
expect(json_response.length).to eq(1)
expect(json_response.first['name']).to eq(project2.name)
end
it 'only returns the projects starred by user' do
user1.starred_projects = [project1]
get v3_api("/groups/#{group1.id}/projects", user1), starred: true
expect(response).to have_http_status(200)
expect(json_response.length).to eq(1)
expect(json_response.first['name']).to eq(project1.name)
end
end
context "when authenticated as admin" do
it "returns any existing group" do
get v3_api("/groups/#{group2.id}/projects", admin)
expect(response).to have_http_status(200)
expect(json_response.length).to eq(1)
expect(json_response.first['name']).to eq(project2.name)
end
it "does not return a non existing group" do
get v3_api("/groups/1328/projects", admin)
expect(response).to have_http_status(404)
end
end
context 'when using group path in URL' do
it 'returns any existing group' do
get v3_api("/groups/#{group1.path}/projects", admin)
expect(response).to have_http_status(200)
project_names = json_response.map { |proj| proj['name'] }
expect(project_names).to match_array([project1.name, project3.name])
end
it 'does not return a non existing group' do
get v3_api('/groups/unknown/projects', admin)
expect(response).to have_http_status(404)
end
it 'does not return a group not attached to user1' do
get v3_api("/groups/#{group2.path}/projects", user1)
expect(response).to have_http_status(404)
end
end
end
describe "POST /groups" do
context "when authenticated as user without group permissions" do
it "does not create group" do
post v3_api("/groups", user1), attributes_for(:group)
expect(response).to have_http_status(403)
end
end
context "when authenticated as user with group permissions" do
it "creates group" do
group = attributes_for(:group, { request_access_enabled: false })
post v3_api("/groups", user3), group
expect(response).to have_http_status(201)
expect(json_response["name"]).to eq(group[:name])
expect(json_response["path"]).to eq(group[:path])
expect(json_response["request_access_enabled"]).to eq(group[:request_access_enabled])
end
it "creates a nested group" do
parent = create(:group)
parent.add_owner(user3)
group = attributes_for(:group, { parent_id: parent.id })
post v3_api("/groups", user3), group
expect(response).to have_http_status(201)
expect(json_response["full_path"]).to eq("#{parent.path}/#{group[:path]}")
expect(json_response["parent_id"]).to eq(parent.id)
end
it "does not create group, duplicate" do
post v3_api("/groups", user3), { name: 'Duplicate Test', path: group2.path }
expect(response).to have_http_status(400)
expect(response.message).to eq("Bad Request")
end
it "returns 400 bad request error if name not given" do
post v3_api("/groups", user3), { path: group2.path }
expect(response).to have_http_status(400)
end
it "returns 400 bad request error if path not given" do
post v3_api("/groups", user3), { name: 'test' }
expect(response).to have_http_status(400)
end
it "creates an ldap_group_link if ldap_cn and ldap_access are supplied" do
group_attributes = attributes_for(:group, ldap_cn: 'ldap-group', ldap_access: Gitlab::Access::DEVELOPER)
expect { post v3_api("/groups", admin), group_attributes }.to change{ LdapGroupLink.count }.by(1)
end
end
end
describe "PUT /groups" do
context "when authenticated as user without group permissions" do
it "does not create group" do
put v3_api("/groups/#{group2.id}", user1), attributes_for(:group)
expect(response.status).to eq(404)
end
end
context "when authenticated as user with group permissions" do
it "updates group" do
group2.update(owner: user2)
put v3_api("/groups/#{group2.id}", user2), { name: 'Renamed' }
expect(response.status).to eq(200)
expect(group2.reload.name).to eq('Renamed')
end
end
end
describe "DELETE /groups/:id" do
context "when authenticated as user" do
it "removes group" do
delete v3_api("/groups/#{group1.id}", user1)
expect(response).to have_http_status(200)
end
it "does not remove a group if not an owner" do
user4 = create(:user)
group1.add_master(user4)
delete v3_api("/groups/#{group1.id}", user3)
expect(response).to have_http_status(403)
end
it "does not remove a non existing group" do
delete v3_api("/groups/1328", user1)
expect(response).to have_http_status(404)
end
it "does not remove a group not attached to user1" do
delete v3_api("/groups/#{group2.id}", user1)
expect(response).to have_http_status(404)
end
end
context "when authenticated as admin" do
it "removes any existing group" do
delete v3_api("/groups/#{group2.id}", admin)
expect(response).to have_http_status(200)
end
it "does not remove a non existing group" do
delete v3_api("/groups/1328", admin)
expect(response).to have_http_status(404)
end
end
end
describe "POST /groups/:id/projects/:project_id" do
let(:project) { create(:empty_project) }
let(:project_path) { "#{project.namespace.path}%2F#{project.path}" }
before(:each) do
allow_any_instance_of(Projects::TransferService).
to receive(:execute).and_return(true)
end
context "when authenticated as user" do
it "does not transfer project to group" do
post v3_api("/groups/#{group1.id}/projects/#{project.id}", user2)
expect(response).to have_http_status(403)
end
end
context "when authenticated as admin" do
it "transfers project to group" do
post v3_api("/groups/#{group1.id}/projects/#{project.id}", admin)
expect(response).to have_http_status(201)
end
context 'when using project path in URL' do
context 'with a valid project path' do
it "transfers project to group" do
post v3_api("/groups/#{group1.id}/projects/#{project_path}", admin)
expect(response).to have_http_status(201)
end
end
context 'with a non-existent project path' do
it "does not transfer project to group" do
post v3_api("/groups/#{group1.id}/projects/nogroup%2Fnoproject", admin)
expect(response).to have_http_status(404)
end
end
end
context 'when using a group path in URL' do
context 'with a valid group path' do
it "transfers project to group" do
post v3_api("/groups/#{group1.path}/projects/#{project_path}", admin)
expect(response).to have_http_status(201)
end
end
context 'with a non-existent group path' do
it "does not transfer project to group" do
post v3_api("/groups/noexist/projects/#{project_path}", admin)
expect(response).to have_http_status(404)
end
end
end
end
end
end
require 'rails_helper'
describe API::V3::Snippets, api: true do
include ApiHelpers
let!(:user) { create(:user) }
describe 'GET /snippets/' do
it 'returns snippets available' do
public_snippet = create(:personal_snippet, :public, author: user)
private_snippet = create(:personal_snippet, :private, author: user)
internal_snippet = create(:personal_snippet, :internal, author: user)
get v3_api("/snippets/", user)
expect(response).to have_http_status(200)
expect(json_response.map { |snippet| snippet['id']} ).to contain_exactly(
public_snippet.id,
internal_snippet.id,
private_snippet.id)
expect(json_response.last).to have_key('web_url')
expect(json_response.last).to have_key('raw_url')
end
it 'hides private snippets from regular user' do
create(:personal_snippet, :private)
get v3_api("/snippets/", user)
expect(response).to have_http_status(200)
expect(json_response.size).to eq(0)
end
end
describe 'GET /snippets/public' do
let!(:other_user) { create(:user) }
let!(:public_snippet) { create(:personal_snippet, :public, author: user) }
let!(:private_snippet) { create(:personal_snippet, :private, author: user) }
let!(:internal_snippet) { create(:personal_snippet, :internal, author: user) }
let!(:public_snippet_other) { create(:personal_snippet, :public, author: other_user) }
let!(:private_snippet_other) { create(:personal_snippet, :private, author: other_user) }
let!(:internal_snippet_other) { create(:personal_snippet, :internal, author: other_user) }
it 'returns all snippets with public visibility from all users' do
get v3_api("/snippets/public", user)
expect(response).to have_http_status(200)
expect(json_response.map { |snippet| snippet['id']} ).to contain_exactly(
public_snippet.id,
public_snippet_other.id)
expect(json_response.map{ |snippet| snippet['web_url']} ).to include(
"http://localhost/snippets/#{public_snippet.id}",
"http://localhost/snippets/#{public_snippet_other.id}")
expect(json_response.map{ |snippet| snippet['raw_url']} ).to include(
"http://localhost/snippets/#{public_snippet.id}/raw",
"http://localhost/snippets/#{public_snippet_other.id}/raw")
end
end
describe 'GET /snippets/:id/raw' do
let(:snippet) { create(:personal_snippet, author: user) }
it 'returns raw text' do
get v3_api("/snippets/#{snippet.id}/raw", user)
expect(response).to have_http_status(200)
expect(response.content_type).to eq 'text/plain'
expect(response.body).to eq(snippet.content)
end
it 'returns 404 for invalid snippet id' do
delete v3_api("/snippets/1234", user)
expect(response).to have_http_status(404)
expect(json_response['message']).to eq('404 Snippet Not Found')
end
end
describe 'POST /snippets/' do
let(:params) do
{
title: 'Test Title',
file_name: 'test.rb',
content: 'puts "hello world"',
visibility_level: Snippet::PUBLIC
}
end
it 'creates a new snippet' do
expect do
post v3_api("/snippets/", user), params
end.to change { PersonalSnippet.count }.by(1)
expect(response).to have_http_status(201)
expect(json_response['title']).to eq(params[:title])
expect(json_response['file_name']).to eq(params[:file_name])
end
it 'returns 400 for missing parameters' do
params.delete(:title)
post v3_api("/snippets/", user), params
expect(response).to have_http_status(400)
end
context 'when the snippet is spam' do
def create_snippet(snippet_params = {})
post v3_api('/snippets', user), params.merge(snippet_params)
end
before do
allow_any_instance_of(AkismetService).to receive(:is_spam?).and_return(true)
end
context 'when the snippet is private' do
it 'creates the snippet' do
expect { create_snippet(visibility_level: Snippet::PRIVATE) }.
to change { Snippet.count }.by(1)
end
end
context 'when the snippet is public' do
it 'rejects the shippet' do
expect { create_snippet(visibility_level: Snippet::PUBLIC) }.
not_to change { Snippet.count }
expect(response).to have_http_status(400)
end
it 'creates a spam log' do
expect { create_snippet(visibility_level: Snippet::PUBLIC) }.
to change { SpamLog.count }.by(1)
end
end
end
end
describe 'PUT /snippets/:id' do
let(:other_user) { create(:user) }
let(:public_snippet) { create(:personal_snippet, :public, author: user) }
it 'updates snippet' do
new_content = 'New content'
put v3_api("/snippets/#{public_snippet.id}", user), content: new_content
expect(response).to have_http_status(200)
public_snippet.reload
expect(public_snippet.content).to eq(new_content)
end
it 'returns 404 for invalid snippet id' do
put v3_api("/snippets/1234", user), title: 'foo'
expect(response).to have_http_status(404)
expect(json_response['message']).to eq('404 Snippet Not Found')
end
it "returns 404 for another user's snippet" do
put v3_api("/snippets/#{public_snippet.id}", other_user), title: 'fubar'
expect(response).to have_http_status(404)
expect(json_response['message']).to eq('404 Snippet Not Found')
end
it 'returns 400 for missing parameters' do
put v3_api("/snippets/1234", user)
expect(response).to have_http_status(400)
end
end
describe 'DELETE /snippets/:id' do
let!(:public_snippet) { create(:personal_snippet, :public, author: user) }
it 'deletes snippet' do
expect do
delete v3_api("/snippets/#{public_snippet.id}", user)
expect(response).to have_http_status(204)
end.to change { PersonalSnippet.count }.by(-1)
end
it 'returns 404 for invalid snippet id' do
delete v3_api("/snippets/1234", user)
expect(response).to have_http_status(404)
expect(json_response['message']).to eq('404 Snippet Not Found')
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