Merge branch 'backport-issue_3413' into 'master'
[EE-backport] Add group boards API endpoint See merge request gitlab-org/gitlab-ce!16194
This commit is contained in:
commit
0f33ac6442
16 changed files with 516 additions and 248 deletions
|
@ -1,4 +1,6 @@
|
||||||
class LabelsFinder < UnionFinder
|
class LabelsFinder < UnionFinder
|
||||||
|
include Gitlab::Utils::StrongMemoize
|
||||||
|
|
||||||
def initialize(current_user, params = {})
|
def initialize(current_user, params = {})
|
||||||
@current_user = current_user
|
@current_user = current_user
|
||||||
@params = params
|
@params = params
|
||||||
|
@ -32,6 +34,8 @@ class LabelsFinder < UnionFinder
|
||||||
label_ids << project.labels
|
label_ids << project.labels
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
elsif only_group_labels?
|
||||||
|
label_ids << Label.where(group_id: group.id)
|
||||||
else
|
else
|
||||||
label_ids << Label.where(group_id: projects.group_ids)
|
label_ids << Label.where(group_id: projects.group_ids)
|
||||||
label_ids << Label.where(project_id: projects.select(:id))
|
label_ids << Label.where(project_id: projects.select(:id))
|
||||||
|
@ -51,6 +55,13 @@ class LabelsFinder < UnionFinder
|
||||||
items.where(title: title)
|
items.where(title: title)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def group
|
||||||
|
strong_memoize(:group) do
|
||||||
|
group = Group.find(params[:group_id])
|
||||||
|
authorized_to_read_labels?(group) && group
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def group?
|
def group?
|
||||||
params[:group_id].present?
|
params[:group_id].present?
|
||||||
end
|
end
|
||||||
|
@ -63,6 +74,10 @@ class LabelsFinder < UnionFinder
|
||||||
params[:project_ids].present?
|
params[:project_ids].present?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def only_group_labels?
|
||||||
|
params[:only_group_labels]
|
||||||
|
end
|
||||||
|
|
||||||
def title
|
def title
|
||||||
params[:title] || params[:name]
|
params[:title] || params[:name]
|
||||||
end
|
end
|
||||||
|
@ -96,9 +111,9 @@ class LabelsFinder < UnionFinder
|
||||||
@projects
|
@projects
|
||||||
end
|
end
|
||||||
|
|
||||||
def authorized_to_read_labels?(project)
|
def authorized_to_read_labels?(label_parent)
|
||||||
return true if skip_authorization
|
return true if skip_authorization
|
||||||
|
|
||||||
Ability.allowed?(current_user, :read_label, project)
|
Ability.allowed?(current_user, :read_label, label_parent)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -28,12 +28,18 @@ class GroupPolicy < BasePolicy
|
||||||
with_options scope: :subject, score: 0
|
with_options scope: :subject, score: 0
|
||||||
condition(:request_access_enabled) { @subject.request_access_enabled }
|
condition(:request_access_enabled) { @subject.request_access_enabled }
|
||||||
|
|
||||||
rule { public_group } .enable :read_group
|
rule { public_group }.policy do
|
||||||
|
enable :read_group
|
||||||
|
enable :read_list
|
||||||
|
enable :read_label
|
||||||
|
end
|
||||||
|
|
||||||
rule { logged_in_viewable }.enable :read_group
|
rule { logged_in_viewable }.enable :read_group
|
||||||
|
|
||||||
rule { guest }.policy do
|
rule { guest }.policy do
|
||||||
enable :read_group
|
enable :read_group
|
||||||
enable :upload_file
|
enable :upload_file
|
||||||
|
enable :read_label
|
||||||
end
|
end
|
||||||
|
|
||||||
rule { admin } .enable :read_group
|
rule { admin } .enable :read_group
|
||||||
|
|
|
@ -15,10 +15,10 @@ GET /projects/:id/boards
|
||||||
|
|
||||||
| Attribute | Type | Required | Description |
|
| Attribute | Type | Required | Description |
|
||||||
| --------- | ---- | -------- | ----------- |
|
| --------- | ---- | -------- | ----------- |
|
||||||
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
|
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/:id/boards
|
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/boards
|
||||||
```
|
```
|
||||||
|
|
||||||
Example response:
|
Example response:
|
||||||
|
@ -27,6 +27,19 @@ Example response:
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"id" : 1,
|
"id" : 1,
|
||||||
|
"project": {
|
||||||
|
"id": 5,
|
||||||
|
"name": "Diaspora Project Site",
|
||||||
|
"name_with_namespace": "Diaspora / Diaspora Project Site",
|
||||||
|
"path": "diaspora-project-site",
|
||||||
|
"path_with_namespace": "diaspora/diaspora-project-site",
|
||||||
|
"http_url_to_repo": "http://example.com/diaspora/diaspora-project-site.git",
|
||||||
|
"web_url": "http://example.com/diaspora/diaspora-project-site"
|
||||||
|
},
|
||||||
|
"milestone": {
|
||||||
|
"id": 12
|
||||||
|
"title": "10.0"
|
||||||
|
},
|
||||||
"lists" : [
|
"lists" : [
|
||||||
{
|
{
|
||||||
"id" : 1,
|
"id" : 1,
|
||||||
|
@ -60,6 +73,74 @@ Example response:
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Single board
|
||||||
|
|
||||||
|
Get a single board.
|
||||||
|
|
||||||
|
```
|
||||||
|
GET /projects/:id/boards/:board_id
|
||||||
|
```
|
||||||
|
|
||||||
|
| Attribute | Type | Required | Description |
|
||||||
|
| --------- | ---- | -------- | ----------- |
|
||||||
|
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
|
||||||
|
| `board_id` | integer | yes | The ID of a board |
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/boards/1
|
||||||
|
```
|
||||||
|
|
||||||
|
Example response:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"name:": "project issue board",
|
||||||
|
"project": {
|
||||||
|
"id": 5,
|
||||||
|
"name": "Diaspora Project Site",
|
||||||
|
"name_with_namespace": "Diaspora / Diaspora Project Site",
|
||||||
|
"path": "diaspora-project-site",
|
||||||
|
"path_with_namespace": "diaspora/diaspora-project-site",
|
||||||
|
"http_url_to_repo": "http://example.com/diaspora/diaspora-project-site.git",
|
||||||
|
"web_url": "http://example.com/diaspora/diaspora-project-site"
|
||||||
|
},
|
||||||
|
"milestone": {
|
||||||
|
"id": 12
|
||||||
|
"title": "10.0"
|
||||||
|
},
|
||||||
|
"lists" : [
|
||||||
|
{
|
||||||
|
"id" : 1,
|
||||||
|
"label" : {
|
||||||
|
"name" : "Testing",
|
||||||
|
"color" : "#F0AD4E",
|
||||||
|
"description" : null
|
||||||
|
},
|
||||||
|
"position" : 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id" : 2,
|
||||||
|
"label" : {
|
||||||
|
"name" : "Ready",
|
||||||
|
"color" : "#FF0000",
|
||||||
|
"description" : null
|
||||||
|
},
|
||||||
|
"position" : 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id" : 3,
|
||||||
|
"label" : {
|
||||||
|
"name" : "Production",
|
||||||
|
"color" : "#FF5F00",
|
||||||
|
"description" : null
|
||||||
|
},
|
||||||
|
"position" : 3
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## List board lists
|
## List board lists
|
||||||
|
|
||||||
Get a list of the board's lists.
|
Get a list of the board's lists.
|
||||||
|
@ -71,8 +152,8 @@ GET /projects/:id/boards/:board_id/lists
|
||||||
|
|
||||||
| Attribute | Type | Required | Description |
|
| Attribute | Type | Required | Description |
|
||||||
| --------- | ---- | -------- | ----------- |
|
| --------- | ---- | -------- | ----------- |
|
||||||
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
|
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
|
||||||
| `board_id` | integer | yes | The ID of a board |
|
| `board_id` | integer | yes | The ID of a board |
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/boards/1/lists
|
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/boards/1/lists
|
||||||
|
@ -122,9 +203,9 @@ GET /projects/:id/boards/:board_id/lists/:list_id
|
||||||
|
|
||||||
| Attribute | Type | Required | Description |
|
| Attribute | Type | Required | Description |
|
||||||
| --------- | ---- | -------- | ----------- |
|
| --------- | ---- | -------- | ----------- |
|
||||||
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
|
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
|
||||||
| `board_id` | integer | yes | The ID of a board |
|
| `board_id` | integer | yes | The ID of a board |
|
||||||
| `list_id`| integer | yes | The ID of a board's list |
|
| `list_id`| integer | yes | The ID of a board's list |
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/boards/1/lists/1
|
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/boards/1/lists/1
|
||||||
|
@ -154,9 +235,9 @@ POST /projects/:id/boards/:board_id/lists
|
||||||
|
|
||||||
| Attribute | Type | Required | Description |
|
| Attribute | Type | Required | Description |
|
||||||
| --------- | ---- | -------- | ----------- |
|
| --------- | ---- | -------- | ----------- |
|
||||||
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
|
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
|
||||||
| `board_id` | integer | yes | The ID of a board |
|
| `board_id` | integer | yes | The ID of a board |
|
||||||
| `label_id` | integer | yes | The ID of a label |
|
| `label_id` | integer | yes | The ID of a label |
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/boards/1/lists?label_id=5
|
curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/boards/1/lists?label_id=5
|
||||||
|
@ -186,10 +267,10 @@ PUT /projects/:id/boards/:board_id/lists/:list_id
|
||||||
|
|
||||||
| Attribute | Type | Required | Description |
|
| Attribute | Type | Required | Description |
|
||||||
| --------- | ---- | -------- | ----------- |
|
| --------- | ---- | -------- | ----------- |
|
||||||
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
|
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
|
||||||
| `board_id` | integer | yes | The ID of a board |
|
| `board_id` | integer | yes | The ID of a board |
|
||||||
| `list_id` | integer | yes | The ID of a board's list |
|
| `list_id` | integer | yes | The ID of a board's list |
|
||||||
| `position` | integer | yes | The position of the list |
|
| `position` | integer | yes | The position of the list |
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/boards/1/lists/1?position=2
|
curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/boards/1/lists/1?position=2
|
||||||
|
@ -219,9 +300,9 @@ DELETE /projects/:id/boards/:board_id/lists/:list_id
|
||||||
|
|
||||||
| Attribute | Type | Required | Description |
|
| Attribute | Type | Required | Description |
|
||||||
| --------- | ---- | -------- | ----------- |
|
| --------- | ---- | -------- | ----------- |
|
||||||
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
|
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
|
||||||
| `board_id` | integer | yes | The ID of a board |
|
| `board_id` | integer | yes | The ID of a board |
|
||||||
| `list_id` | integer | yes | The ID of a board's list |
|
| `list_id` | integer | yes | The ID of a board's list |
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/boards/1/lists/1
|
curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/boards/1/lists/1
|
||||||
|
|
|
@ -119,6 +119,7 @@ module API
|
||||||
mount ::API::Features
|
mount ::API::Features
|
||||||
mount ::API::Files
|
mount ::API::Files
|
||||||
mount ::API::Groups
|
mount ::API::Groups
|
||||||
|
mount ::API::GroupMilestones
|
||||||
mount ::API::Internal
|
mount ::API::Internal
|
||||||
mount ::API::Issues
|
mount ::API::Issues
|
||||||
mount ::API::Jobs
|
mount ::API::Jobs
|
||||||
|
@ -129,8 +130,6 @@ module API
|
||||||
mount ::API::Members
|
mount ::API::Members
|
||||||
mount ::API::MergeRequestDiffs
|
mount ::API::MergeRequestDiffs
|
||||||
mount ::API::MergeRequests
|
mount ::API::MergeRequests
|
||||||
mount ::API::ProjectMilestones
|
|
||||||
mount ::API::GroupMilestones
|
|
||||||
mount ::API::Namespaces
|
mount ::API::Namespaces
|
||||||
mount ::API::Notes
|
mount ::API::Notes
|
||||||
mount ::API::NotificationSettings
|
mount ::API::NotificationSettings
|
||||||
|
@ -139,6 +138,7 @@ module API
|
||||||
mount ::API::PipelineSchedules
|
mount ::API::PipelineSchedules
|
||||||
mount ::API::ProjectHooks
|
mount ::API::ProjectHooks
|
||||||
mount ::API::Projects
|
mount ::API::Projects
|
||||||
|
mount ::API::ProjectMilestones
|
||||||
mount ::API::ProjectSnippets
|
mount ::API::ProjectSnippets
|
||||||
mount ::API::ProtectedBranches
|
mount ::API::ProtectedBranches
|
||||||
mount ::API::Repositories
|
mount ::API::Repositories
|
||||||
|
|
|
@ -1,45 +1,46 @@
|
||||||
module API
|
module API
|
||||||
class Boards < Grape::API
|
class Boards < Grape::API
|
||||||
|
include BoardsResponses
|
||||||
include PaginationParams
|
include PaginationParams
|
||||||
|
|
||||||
before { authenticate! }
|
before { authenticate! }
|
||||||
|
|
||||||
|
helpers do
|
||||||
|
def board_parent
|
||||||
|
user_project
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
params do
|
params do
|
||||||
requires :id, type: String, desc: 'The ID of a project'
|
requires :id, type: String, desc: 'The ID of a project'
|
||||||
end
|
end
|
||||||
resource :projects, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
|
resource :projects, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
|
||||||
desc 'Get all project boards' do
|
segment ':id/boards' do
|
||||||
detail 'This feature was introduced in 8.13'
|
desc 'Get all project boards' do
|
||||||
success Entities::Board
|
detail 'This feature was introduced in 8.13'
|
||||||
end
|
success Entities::Board
|
||||||
params do
|
end
|
||||||
use :pagination
|
params do
|
||||||
end
|
use :pagination
|
||||||
get ':id/boards' do
|
end
|
||||||
authorize!(:read_board, user_project)
|
get '/' do
|
||||||
present paginate(user_project.boards), with: Entities::Board
|
authorize!(:read_board, user_project)
|
||||||
|
present paginate(board_parent.boards), with: Entities::Board
|
||||||
|
end
|
||||||
|
|
||||||
|
desc 'Find a project board' do
|
||||||
|
detail 'This feature was introduced in 10.4'
|
||||||
|
success Entities::Board
|
||||||
|
end
|
||||||
|
get '/:board_id' do
|
||||||
|
present board, with: Entities::Board
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
params do
|
params do
|
||||||
requires :board_id, type: Integer, desc: 'The ID of a board'
|
requires :board_id, type: Integer, desc: 'The ID of a board'
|
||||||
end
|
end
|
||||||
segment ':id/boards/:board_id' do
|
segment ':id/boards/:board_id' do
|
||||||
helpers do
|
|
||||||
def project_board
|
|
||||||
board = user_project.boards.first
|
|
||||||
|
|
||||||
if params[:board_id] == board.id
|
|
||||||
board
|
|
||||||
else
|
|
||||||
not_found!('Board')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def board_lists
|
|
||||||
project_board.lists.destroyable
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
desc 'Get the lists of a project board' do
|
desc 'Get the lists of a project board' do
|
||||||
detail 'Does not include `done` list. This feature was introduced in 8.13'
|
detail 'Does not include `done` list. This feature was introduced in 8.13'
|
||||||
success Entities::List
|
success Entities::List
|
||||||
|
@ -72,22 +73,13 @@ module API
|
||||||
requires :label_id, type: Integer, desc: 'The ID of an existing label'
|
requires :label_id, type: Integer, desc: 'The ID of an existing label'
|
||||||
end
|
end
|
||||||
post '/lists' do
|
post '/lists' do
|
||||||
unless available_labels.exists?(params[:label_id])
|
unless available_labels_for(user_project).exists?(params[:label_id])
|
||||||
render_api_error!({ error: 'Label not found!' }, 400)
|
render_api_error!({ error: 'Label not found!' }, 400)
|
||||||
end
|
end
|
||||||
|
|
||||||
authorize!(:admin_list, user_project)
|
authorize!(:admin_list, user_project)
|
||||||
|
|
||||||
service = ::Boards::Lists::CreateService.new(user_project, current_user,
|
create_list
|
||||||
{ label_id: params[:label_id] })
|
|
||||||
|
|
||||||
list = service.execute(project_board)
|
|
||||||
|
|
||||||
if list.valid?
|
|
||||||
present list, with: Entities::List
|
|
||||||
else
|
|
||||||
render_validation_error!(list)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
desc 'Moves a board list to a new position' do
|
desc 'Moves a board list to a new position' do
|
||||||
|
@ -99,18 +91,11 @@ module API
|
||||||
requires :position, type: Integer, desc: 'The position of the list'
|
requires :position, type: Integer, desc: 'The position of the list'
|
||||||
end
|
end
|
||||||
put '/lists/:list_id' do
|
put '/lists/:list_id' do
|
||||||
list = project_board.lists.movable.find(params[:list_id])
|
list = board_lists.find(params[:list_id])
|
||||||
|
|
||||||
authorize!(:admin_list, user_project)
|
authorize!(:admin_list, user_project)
|
||||||
|
|
||||||
service = ::Boards::Lists::MoveService.new(user_project, current_user,
|
move_list(list)
|
||||||
{ position: params[:position] })
|
|
||||||
|
|
||||||
if service.execute(list)
|
|
||||||
present list, with: Entities::List
|
|
||||||
else
|
|
||||||
render_api_error!({ error: "List could not be moved!" }, 400)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
desc 'Delete a board list' do
|
desc 'Delete a board list' do
|
||||||
|
@ -124,12 +109,7 @@ module API
|
||||||
authorize!(:admin_list, user_project)
|
authorize!(:admin_list, user_project)
|
||||||
list = board_lists.find(params[:list_id])
|
list = board_lists.find(params[:list_id])
|
||||||
|
|
||||||
destroy_conditionally!(list) do |list|
|
destroy_list(list)
|
||||||
service = ::Boards::Lists::DestroyService.new(user_project, current_user)
|
|
||||||
unless service.execute(list)
|
|
||||||
render_api_error!({ error: 'List could not be deleted!' }, 400)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
50
lib/api/boards_responses.rb
Normal file
50
lib/api/boards_responses.rb
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
module API
|
||||||
|
module BoardsResponses
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
included do
|
||||||
|
helpers do
|
||||||
|
def board
|
||||||
|
board_parent.boards.find(params[:board_id])
|
||||||
|
end
|
||||||
|
|
||||||
|
def board_lists
|
||||||
|
board.lists.destroyable
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_list
|
||||||
|
create_list_service =
|
||||||
|
::Boards::Lists::CreateService.new(board_parent, current_user, { label_id: params[:label_id] })
|
||||||
|
|
||||||
|
list = create_list_service.execute(board)
|
||||||
|
|
||||||
|
if list.valid?
|
||||||
|
present list, with: Entities::List
|
||||||
|
else
|
||||||
|
render_validation_error!(list)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def move_list(list)
|
||||||
|
move_list_service =
|
||||||
|
::Boards::Lists::MoveService.new(board_parent, current_user, { position: params[:position].to_i })
|
||||||
|
|
||||||
|
if move_list_service.execute(list)
|
||||||
|
present list, with: Entities::List
|
||||||
|
else
|
||||||
|
render_api_error!({ error: "List could not be moved!" }, 400)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def destroy_list(list)
|
||||||
|
destroy_conditionally!(list) do |list|
|
||||||
|
service = ::Boards::Lists::DestroyService.new(board_parent, current_user)
|
||||||
|
unless service.execute(list)
|
||||||
|
render_api_error!({ error: 'List could not be deleted!' }, 400)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -791,6 +791,8 @@ module API
|
||||||
|
|
||||||
class Board < Grape::Entity
|
class Board < Grape::Entity
|
||||||
expose :id
|
expose :id
|
||||||
|
expose :project, using: Entities::BasicProjectDetails
|
||||||
|
|
||||||
expose :lists, using: Entities::List do |board|
|
expose :lists, using: Entities::List do |board|
|
||||||
board.lists.destroyable
|
board.lists.destroyable
|
||||||
end
|
end
|
||||||
|
|
|
@ -74,8 +74,15 @@ module API
|
||||||
page || not_found!('Wiki Page')
|
page || not_found!('Wiki Page')
|
||||||
end
|
end
|
||||||
|
|
||||||
def available_labels
|
def available_labels_for(label_parent)
|
||||||
@available_labels ||= LabelsFinder.new(current_user, project_id: user_project.id).execute
|
search_params =
|
||||||
|
if label_parent.is_a?(Project)
|
||||||
|
{ project_id: label_parent.id }
|
||||||
|
else
|
||||||
|
{ group_id: label_parent.id, only_group_labels: true }
|
||||||
|
end
|
||||||
|
|
||||||
|
LabelsFinder.new(current_user, search_params).execute
|
||||||
end
|
end
|
||||||
|
|
||||||
def find_user(id)
|
def find_user(id)
|
||||||
|
@ -141,7 +148,9 @@ module API
|
||||||
end
|
end
|
||||||
|
|
||||||
def find_project_label(id)
|
def find_project_label(id)
|
||||||
label = available_labels.find_by_id(id) || available_labels.find_by_title(id)
|
labels = available_labels_for(user_project)
|
||||||
|
label = labels.find_by_id(id) || labels.find_by_title(id)
|
||||||
|
|
||||||
label || not_found!('Label')
|
label || not_found!('Label')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ module API
|
||||||
use :pagination
|
use :pagination
|
||||||
end
|
end
|
||||||
get ':id/labels' do
|
get ':id/labels' do
|
||||||
present paginate(available_labels), with: Entities::Label, current_user: current_user, project: user_project
|
present paginate(available_labels_for(user_project)), with: Entities::Label, current_user: current_user, project: user_project
|
||||||
end
|
end
|
||||||
|
|
||||||
desc 'Create a new label' do
|
desc 'Create a new label' do
|
||||||
|
@ -30,7 +30,7 @@ module API
|
||||||
post ':id/labels' do
|
post ':id/labels' do
|
||||||
authorize! :admin_label, user_project
|
authorize! :admin_label, user_project
|
||||||
|
|
||||||
label = available_labels.find_by(title: params[:name])
|
label = available_labels_for(user_project).find_by(title: params[:name])
|
||||||
conflict!('Label already exists') if label
|
conflict!('Label already exists') if label
|
||||||
|
|
||||||
priority = params.delete(:priority)
|
priority = params.delete(:priority)
|
||||||
|
|
|
@ -11,7 +11,7 @@ module API
|
||||||
success ::API::Entities::Label
|
success ::API::Entities::Label
|
||||||
end
|
end
|
||||||
get ':id/labels' do
|
get ':id/labels' do
|
||||||
present available_labels, with: ::API::Entities::Label, current_user: current_user, project: user_project
|
present available_labels_for(user_project), with: ::API::Entities::Label, current_user: current_user, project: user_project
|
||||||
end
|
end
|
||||||
|
|
||||||
desc 'Delete an existing label' do
|
desc 'Delete an existing label' do
|
||||||
|
|
|
@ -56,6 +56,16 @@ describe LabelsFinder do
|
||||||
|
|
||||||
expect(finder.execute).to eq [group_label_2, group_label_1, project_label_5]
|
expect(finder.execute).to eq [group_label_2, group_label_1, project_label_5]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'when only_group_labels is true' do
|
||||||
|
it 'returns only group labels' do
|
||||||
|
group_1.add_developer(user)
|
||||||
|
|
||||||
|
finder = described_class.new(user, group_id: group_1.id, only_group_labels: true)
|
||||||
|
|
||||||
|
expect(finder.execute).to eq [group_label_2, group_label_1]
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'filtering by project_id' do
|
context 'filtering by project_id' do
|
||||||
|
|
86
spec/fixtures/api/schemas/public_api/v4/board.json
vendored
Normal file
86
spec/fixtures/api/schemas/public_api/v4/board.json
vendored
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"required" : [
|
||||||
|
"id",
|
||||||
|
"project",
|
||||||
|
"lists"
|
||||||
|
],
|
||||||
|
"properties" : {
|
||||||
|
"id": { "type": "integer" },
|
||||||
|
"project": {
|
||||||
|
"type": ["object", "null"],
|
||||||
|
"required": [
|
||||||
|
"id",
|
||||||
|
"avatar_url",
|
||||||
|
"description",
|
||||||
|
"default_branch",
|
||||||
|
"tag_list",
|
||||||
|
"ssh_url_to_repo",
|
||||||
|
"http_url_to_repo",
|
||||||
|
"web_url",
|
||||||
|
"name",
|
||||||
|
"name_with_namespace",
|
||||||
|
"path",
|
||||||
|
"path_with_namespace",
|
||||||
|
"star_count",
|
||||||
|
"forks_count",
|
||||||
|
"created_at",
|
||||||
|
"last_activity_at"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"id": { "type": "integer" },
|
||||||
|
"avatar_url": { "type": ["string", "null"] },
|
||||||
|
"description": { "type": ["string", "null"] },
|
||||||
|
"default_branch": { "type": ["string", "null"] },
|
||||||
|
"tag_list": { "type": "array" },
|
||||||
|
"ssh_url_to_repo": { "type": "string" },
|
||||||
|
"http_url_to_repo": { "type": "string" },
|
||||||
|
"web_url": { "type": "string" },
|
||||||
|
"name": { "type": "string" },
|
||||||
|
"name_with_namespace": { "type": "string" },
|
||||||
|
"path": { "type": "string" },
|
||||||
|
"path_with_namespace": { "type": "string" },
|
||||||
|
"star_count": { "type": "integer" },
|
||||||
|
"forks_count": { "type": "integer" },
|
||||||
|
"created_at": { "type": "date" },
|
||||||
|
"last_activity_at": { "type": "date" }
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
"lists": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "object",
|
||||||
|
"required" : [
|
||||||
|
"id",
|
||||||
|
"label",
|
||||||
|
"position"
|
||||||
|
],
|
||||||
|
"properties" : {
|
||||||
|
"id": { "type": "integer" },
|
||||||
|
"label": {
|
||||||
|
"type": ["object", "null"],
|
||||||
|
"required": [
|
||||||
|
"id",
|
||||||
|
"color",
|
||||||
|
"description",
|
||||||
|
"name"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"id": { "type": "integer" },
|
||||||
|
"color": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^#[0-9A-Fa-f]{3}{1,2}+$"
|
||||||
|
},
|
||||||
|
"description": { "type": ["string", "null"] },
|
||||||
|
"name": { "type": "string" }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"position": { "type": ["integer", "null"] }
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": true
|
||||||
|
}
|
4
spec/fixtures/api/schemas/public_api/v4/boards.json
vendored
Normal file
4
spec/fixtures/api/schemas/public_api/v4/boards.json
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"type": "array",
|
||||||
|
"items": { "$ref": "board.json" }
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"type": "object",
|
"type": ["object", "null"],
|
||||||
"required": [
|
"required": [
|
||||||
"id",
|
"id",
|
||||||
"state",
|
"state",
|
||||||
|
|
|
@ -6,18 +6,18 @@ describe API::Boards do
|
||||||
set(:non_member) { create(:user) }
|
set(:non_member) { create(:user) }
|
||||||
set(:guest) { create(:user) }
|
set(:guest) { create(:user) }
|
||||||
set(:admin) { create(:user, :admin) }
|
set(:admin) { create(:user, :admin) }
|
||||||
set(:project) { create(:project, :public, creator_id: user.id, namespace: user.namespace ) }
|
set(:board_parent) { create(:project, :public, creator_id: user.id, namespace: user.namespace ) }
|
||||||
|
|
||||||
set(:dev_label) do
|
set(:dev_label) do
|
||||||
create(:label, title: 'Development', color: '#FFAABB', project: project)
|
create(:label, title: 'Development', color: '#FFAABB', project: board_parent)
|
||||||
end
|
end
|
||||||
|
|
||||||
set(:test_label) do
|
set(:test_label) do
|
||||||
create(:label, title: 'Testing', color: '#FFAACC', project: project)
|
create(:label, title: 'Testing', color: '#FFAACC', project: board_parent)
|
||||||
end
|
end
|
||||||
|
|
||||||
set(:ux_label) do
|
set(:ux_label) do
|
||||||
create(:label, title: 'UX', color: '#FF0000', project: project)
|
create(:label, title: 'UX', color: '#FF0000', project: board_parent)
|
||||||
end
|
end
|
||||||
|
|
||||||
set(:dev_list) do
|
set(:dev_list) do
|
||||||
|
@ -28,180 +28,25 @@ describe API::Boards do
|
||||||
create(:list, label: test_label, position: 2)
|
create(:list, label: test_label, position: 2)
|
||||||
end
|
end
|
||||||
|
|
||||||
set(:board) do
|
set(:milestone) { create(:milestone, project: board_parent) }
|
||||||
create(:board, project: project, lists: [dev_list, test_list])
|
set(:board_label) { create(:label, project: board_parent) }
|
||||||
end
|
set(:board) { create(:board, project: board_parent, lists: [dev_list, test_list]) }
|
||||||
|
|
||||||
before do
|
it_behaves_like 'group and project boards', "/projects/:id/boards"
|
||||||
project.add_reporter(user)
|
|
||||||
project.add_guest(guest)
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "GET /projects/:id/boards" do
|
describe "POST /projects/:id/boards/lists" do
|
||||||
let(:base_url) { "/projects/#{project.id}/boards" }
|
let(:url) { "/projects/#{board_parent.id}/boards/#{board.id}/lists" }
|
||||||
|
|
||||||
context "when unauthenticated" do
|
|
||||||
it "returns authentication error" do
|
|
||||||
get api(base_url)
|
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(401)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "when authenticated" do
|
|
||||||
it "returns the project issue board" do
|
|
||||||
get api(base_url, user)
|
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(200)
|
|
||||||
expect(response).to include_pagination_headers
|
|
||||||
expect(json_response).to be_an Array
|
|
||||||
expect(json_response.length).to eq(1)
|
|
||||||
expect(json_response.first['id']).to eq(board.id)
|
|
||||||
expect(json_response.first['lists']).to be_an Array
|
|
||||||
expect(json_response.first['lists'].length).to eq(2)
|
|
||||||
expect(json_response.first['lists'].last).to have_key('position')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "GET /projects/:id/boards/:board_id/lists" do
|
|
||||||
let(:base_url) { "/projects/#{project.id}/boards/#{board.id}/lists" }
|
|
||||||
|
|
||||||
it 'returns issue board lists' do
|
|
||||||
get api(base_url, user)
|
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(200)
|
|
||||||
expect(response).to include_pagination_headers
|
|
||||||
expect(json_response).to be_an Array
|
|
||||||
expect(json_response.length).to eq(2)
|
|
||||||
expect(json_response.first['label']['name']).to eq(dev_label.title)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'returns 404 if board not found' do
|
|
||||||
get api("/projects/#{project.id}/boards/22343/lists", user)
|
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(404)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "GET /projects/:id/boards/:board_id/lists/:list_id" do
|
|
||||||
let(:base_url) { "/projects/#{project.id}/boards/#{board.id}/lists" }
|
|
||||||
|
|
||||||
it 'returns a list' do
|
|
||||||
get api("#{base_url}/#{dev_list.id}", user)
|
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(200)
|
|
||||||
expect(json_response['id']).to eq(dev_list.id)
|
|
||||||
expect(json_response['label']['name']).to eq(dev_label.title)
|
|
||||||
expect(json_response['position']).to eq(1)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'returns 404 if list not found' do
|
|
||||||
get api("#{base_url}/5324", user)
|
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(404)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "POST /projects/:id/board/lists" do
|
|
||||||
let(:base_url) { "/projects/#{project.id}/boards/#{board.id}/lists" }
|
|
||||||
|
|
||||||
it 'creates a new issue board list for group labels' do
|
it 'creates a new issue board list for group labels' do
|
||||||
group = create(:group)
|
group = create(:group)
|
||||||
group_label = create(:group_label, group: group)
|
group_label = create(:group_label, group: group)
|
||||||
project.update(group: group)
|
board_parent.update(group: group)
|
||||||
|
|
||||||
post api(base_url, user), label_id: group_label.id
|
post api(url, user), label_id: group_label.id
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(201)
|
expect(response).to have_gitlab_http_status(201)
|
||||||
expect(json_response['label']['name']).to eq(group_label.title)
|
expect(json_response['label']['name']).to eq(group_label.title)
|
||||||
expect(json_response['position']).to eq(3)
|
expect(json_response['position']).to eq(3)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'creates a new issue board list for project labels' do
|
|
||||||
post api(base_url, user), label_id: ux_label.id
|
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(201)
|
|
||||||
expect(json_response['label']['name']).to eq(ux_label.title)
|
|
||||||
expect(json_response['position']).to eq(3)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'returns 400 when creating a new list if label_id is invalid' do
|
|
||||||
post api(base_url, user), label_id: 23423
|
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(400)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'returns 403 for project members with guest role' do
|
|
||||||
put api("#{base_url}/#{test_list.id}", guest), position: 1
|
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(403)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "PUT /projects/:id/boards/:board_id/lists/:list_id to update only position" do
|
|
||||||
let(:base_url) { "/projects/#{project.id}/boards/#{board.id}/lists" }
|
|
||||||
|
|
||||||
it "updates a list" do
|
|
||||||
put api("#{base_url}/#{test_list.id}", user),
|
|
||||||
position: 1
|
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(200)
|
|
||||||
expect(json_response['position']).to eq(1)
|
|
||||||
end
|
|
||||||
|
|
||||||
it "returns 404 error if list id not found" do
|
|
||||||
put api("#{base_url}/44444", user),
|
|
||||||
position: 1
|
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(404)
|
|
||||||
end
|
|
||||||
|
|
||||||
it "returns 403 for project members with guest role" do
|
|
||||||
put api("#{base_url}/#{test_list.id}", guest),
|
|
||||||
position: 1
|
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(403)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "DELETE /projects/:id/board/lists/:list_id" do
|
|
||||||
let(:base_url) { "/projects/#{project.id}/boards/#{board.id}/lists" }
|
|
||||||
|
|
||||||
it "rejects a non member from deleting a list" do
|
|
||||||
delete api("#{base_url}/#{dev_list.id}", non_member)
|
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(403)
|
|
||||||
end
|
|
||||||
|
|
||||||
it "rejects a user with guest role from deleting a list" do
|
|
||||||
delete api("#{base_url}/#{dev_list.id}", guest)
|
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(403)
|
|
||||||
end
|
|
||||||
|
|
||||||
it "returns 404 error if list id not found" do
|
|
||||||
delete api("#{base_url}/44444", user)
|
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(404)
|
|
||||||
end
|
|
||||||
|
|
||||||
context "when the user is project owner" do
|
|
||||||
set(:owner) { create(:user) }
|
|
||||||
|
|
||||||
before do
|
|
||||||
project.update(namespace: owner.namespace)
|
|
||||||
end
|
|
||||||
|
|
||||||
it "deletes the list if an admin requests it" do
|
|
||||||
delete api("#{base_url}/#{dev_list.id}", owner)
|
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(204)
|
|
||||||
end
|
|
||||||
|
|
||||||
it_behaves_like '412 response' do
|
|
||||||
let(:request) { api("#{base_url}/#{dev_list.id}", owner) }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
180
spec/support/api/boards_shared_examples.rb
Normal file
180
spec/support/api/boards_shared_examples.rb
Normal file
|
@ -0,0 +1,180 @@
|
||||||
|
shared_examples_for 'group and project boards' do |route_definition, ee = false|
|
||||||
|
let(:root_url) { route_definition.gsub(":id", board_parent.id.to_s) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
board_parent.add_reporter(user)
|
||||||
|
board_parent.add_guest(guest)
|
||||||
|
end
|
||||||
|
|
||||||
|
def expect_schema_match_for(response, schema_file, ee)
|
||||||
|
if ee
|
||||||
|
expect(response).to match_response_schema(schema_file, dir: "ee")
|
||||||
|
else
|
||||||
|
expect(response).to match_response_schema(schema_file)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "GET #{route_definition}" do
|
||||||
|
context "when unauthenticated" do
|
||||||
|
it "returns authentication error" do
|
||||||
|
get api(root_url)
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(401)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when authenticated" do
|
||||||
|
it "returns the issue boards" do
|
||||||
|
get api(root_url, user)
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(200)
|
||||||
|
expect(response).to include_pagination_headers
|
||||||
|
|
||||||
|
expect_schema_match_for(response, 'public_api/v4/boards', ee)
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "GET #{route_definition}/:board_id" do
|
||||||
|
let(:url) { "#{root_url}/#{board.id}" }
|
||||||
|
|
||||||
|
it 'get a single board by id' do
|
||||||
|
get api(url, user)
|
||||||
|
|
||||||
|
expect_schema_match_for(response, 'public_api/v4/board', ee)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "GET #{route_definition}/:board_id/lists" do
|
||||||
|
let(:url) { "#{root_url}/#{board.id}/lists" }
|
||||||
|
|
||||||
|
it 'returns issue board lists' do
|
||||||
|
get api(url, user)
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(200)
|
||||||
|
expect(response).to include_pagination_headers
|
||||||
|
expect(json_response).to be_an Array
|
||||||
|
expect(json_response.length).to eq(2)
|
||||||
|
expect(json_response.first['label']['name']).to eq(dev_label.title)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns 404 if board not found' do
|
||||||
|
get api("#{root_url}/22343/lists", user)
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(404)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "GET #{route_definition}/:board_id/lists/:list_id" do
|
||||||
|
let(:url) { "#{root_url}/#{board.id}/lists" }
|
||||||
|
|
||||||
|
it 'returns a list' do
|
||||||
|
get api("#{url}/#{dev_list.id}", user)
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(200)
|
||||||
|
expect(json_response['id']).to eq(dev_list.id)
|
||||||
|
expect(json_response['label']['name']).to eq(dev_label.title)
|
||||||
|
expect(json_response['position']).to eq(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns 404 if list not found' do
|
||||||
|
get api("#{url}/5324", user)
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(404)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "POST #{route_definition}/lists" do
|
||||||
|
let(:url) { "#{root_url}/#{board.id}/lists" }
|
||||||
|
|
||||||
|
it 'creates a new issue board list for labels' do
|
||||||
|
post api(url, user), label_id: ux_label.id
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(201)
|
||||||
|
expect(json_response['label']['name']).to eq(ux_label.title)
|
||||||
|
expect(json_response['position']).to eq(3)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns 400 when creating a new list if label_id is invalid' do
|
||||||
|
post api(url, user), label_id: 23423
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(400)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns 403 for members with guest role' do
|
||||||
|
put api("#{url}/#{test_list.id}", guest), position: 1
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(403)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "PUT #{route_definition}/:board_id/lists/:list_id to update only position" do
|
||||||
|
let(:url) { "#{root_url}/#{board.id}/lists" }
|
||||||
|
|
||||||
|
it "updates a list" do
|
||||||
|
put api("#{url}/#{test_list.id}", user),
|
||||||
|
position: 1
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(200)
|
||||||
|
expect(json_response['position']).to eq(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns 404 error if list id not found" do
|
||||||
|
put api("#{url}/44444", user),
|
||||||
|
position: 1
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(404)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns 403 for members with guest role" do
|
||||||
|
put api("#{url}/#{test_list.id}", guest),
|
||||||
|
position: 1
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(403)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "DELETE #{route_definition}/lists/:list_id" do
|
||||||
|
let(:url) { "#{root_url}/#{board.id}/lists" }
|
||||||
|
|
||||||
|
it "rejects a non member from deleting a list" do
|
||||||
|
delete api("#{url}/#{dev_list.id}", non_member)
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(403)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "rejects a user with guest role from deleting a list" do
|
||||||
|
delete api("#{url}/#{dev_list.id}", guest)
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(403)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns 404 error if list id not found" do
|
||||||
|
delete api("#{url}/44444", user)
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(404)
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when the user is parent owner" do
|
||||||
|
set(:owner) { create(:user) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
if board_parent.try(:namespace)
|
||||||
|
board_parent.update(namespace: owner.namespace)
|
||||||
|
else
|
||||||
|
board.parent.add_owner(owner)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it "deletes the list if an admin requests it" do
|
||||||
|
delete api("#{url}/#{dev_list.id}", owner)
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(204)
|
||||||
|
end
|
||||||
|
|
||||||
|
it_behaves_like '412 response' do
|
||||||
|
let(:request) { api("#{url}/#{dev_list.id}", owner) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in a new issue