Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
1
Merge Requests
1
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
gitlab-ce
Commits
6d63abe8
Commit
6d63abe8
authored
Jan 22, 2021
by
charlie ablett
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Create epic board
- Create epic board service - Create epic board mutation
parent
01a8bb6f
Changes
15
Show whitespace changes
Inline
Side-by-side
Showing
15 changed files
with
440 additions
and
59 deletions
+440
-59
app/services/boards/create_service.rb
app/services/boards/create_service.rb
+6
-2
doc/api/graphql/reference/gitlab_schema.graphql
doc/api/graphql/reference/gitlab_schema.graphql
+46
-0
doc/api/graphql/reference/gitlab_schema.json
doc/api/graphql/reference/gitlab_schema.json
+145
-0
doc/api/graphql/reference/index.md
doc/api/graphql/reference/index.md
+10
-0
ee/app/graphql/ee/types/mutation_type.rb
ee/app/graphql/ee/types/mutation_type.rb
+2
-1
ee/app/graphql/mutations/boards/epics/create.rb
ee/app/graphql/mutations/boards/epics/create.rb
+39
-0
ee/app/models/boards/epic_board.rb
ee/app/models/boards/epic_board.rb
+1
-1
ee/app/policies/ee/group_policy.rb
ee/app/policies/ee/group_policy.rb
+1
-0
ee/app/services/boards/epics/create_service.rb
ee/app/services/boards/epics/create_service.rb
+19
-0
ee/changelogs/unreleased/233434-cablett-create_epic_board.yml
...hangelogs/unreleased/233434-cablett-create_epic_board.yml
+5
-0
ee/spec/graphql/mutations/boards/epics/create_spec.rb
ee/spec/graphql/mutations/boards/epics/create_spec.rb
+63
-0
ee/spec/policies/group_policy_spec.rb
ee/spec/policies/group_policy_spec.rb
+1
-1
ee/spec/services/boards/create_service_spec.rb
ee/spec/services/boards/create_service_spec.rb
+1
-54
ee/spec/services/boards/epics/create_service_spec.rb
ee/spec/services/boards/epics/create_service_spec.rb
+43
-0
ee/spec/support/shared_examples/services/boards/create_boards_shared_examples.rb
...examples/services/boards/create_boards_shared_examples.rb
+58
-0
No files found.
app/services/boards/create_service.rb
View file @
6d63abe8
...
...
@@ -13,11 +13,11 @@ module Boards
private
def
can_create_board?
parent
.
boards
.
empty?
||
parent
.
multiple_issue_boards_available?
parent
_board_collection
.
empty?
||
parent
.
multiple_issue_boards_available?
end
def
create_board!
board
=
parent
.
boards
.
create
(
params
)
board
=
parent
_board_collection
.
create
(
params
)
unless
board
.
persisted?
return
ServiceResponse
.
error
(
message:
"There was an error when creating a board."
,
payload:
board
)
...
...
@@ -30,6 +30,10 @@ module Boards
ServiceResponse
.
success
(
payload:
board
)
end
def
parent_board_collection
parent
.
boards
end
end
end
...
...
doc/api/graphql/reference/gitlab_schema.graphql
View file @
6d63abe8
...
...
@@ -8967,6 +8967,51 @@ type EpicBoardConnection {
pageInfo
:
PageInfo
!
}
"""
Autogenerated input type of EpicBoardCreate
"""
input
EpicBoardCreateInput
{
"""
A
unique
identifier
for
the
client
performing
the
mutation
.
"""
clientMutationId
:
String
"""
Whether
or
not
backlog
list
is
hidden
.
"""
hideBacklogList
:
Boolean
"""
Whether
or
not
closed
list
is
hidden
.
"""
hideClosedList
:
Boolean
"""
The
board
name
.
"""
name
:
String
}
"""
Autogenerated return type of EpicBoardCreate
"""
type
EpicBoardCreatePayload
{
"""
A
unique
identifier
for
the
client
performing
the
mutation
.
"""
clientMutationId
:
String
"""
The
created
epic
board
.
"""
epicBoard
:
EpicBoard
"""
Errors
encountered
during
execution
of
the
mutation
.
"""
errors
:
[
String
!]!
}
"""
An edge in a connection.
"""
...
...
@@ -16051,6 +16096,7 @@ type Mutation {
dismissVulnerability
(
input
:
DismissVulnerabilityInput
!):
DismissVulnerabilityPayload
@
deprecated
(
reason
:
"
Use
vulnerabilityDismiss
.
Deprecated
in
13.5."
)
environmentsCanaryIngressUpdate
(
input
:
EnvironmentsCanaryIngressUpdateInput
!):
EnvironmentsCanaryIngressUpdatePayload
epicAddIssue
(
input
:
EpicAddIssueInput
!):
EpicAddIssuePayload
epicBoardCreate
(
input
:
EpicBoardCreateInput
!):
EpicBoardCreatePayload
epicSetSubscription
(
input
:
EpicSetSubscriptionInput
!):
EpicSetSubscriptionPayload
epicTreeReorder
(
input
:
EpicTreeReorderInput
!):
EpicTreeReorderPayload
exportRequirements
(
input
:
ExportRequirementsInput
!):
ExportRequirementsPayload
...
...
doc/api/graphql/reference/gitlab_schema.json
View file @
6d63abe8
...
...
@@ -24811,6 +24811,124 @@
"enumValues": null,
"possibleTypes": null
},
{
"kind": "INPUT_OBJECT",
"name": "EpicBoardCreateInput",
"description": "Autogenerated input type of EpicBoardCreate",
"fields": null,
"inputFields": [
{
"name": "name",
"description": "The board name.",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
},
{
"name": "hideBacklogList",
"description": "Whether or not backlog list is hidden.",
"type": {
"kind": "SCALAR",
"name": "Boolean",
"ofType": null
},
"defaultValue": null
},
{
"name": "hideClosedList",
"description": "Whether or not closed list is hidden.",
"type": {
"kind": "SCALAR",
"name": "Boolean",
"ofType": null
},
"defaultValue": null
},
{
"name": "clientMutationId",
"description": "A unique identifier for the client performing the mutation.",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
}
],
"interfaces": null,
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "EpicBoardCreatePayload",
"description": "Autogenerated return type of EpicBoardCreate",
"fields": [
{
"name": "clientMutationId",
"description": "A unique identifier for the client performing the mutation.",
"args": [
],
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "epicBoard",
"description": "The created epic board.",
"args": [
],
"type": {
"kind": "OBJECT",
"name": "EpicBoard",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "errors",
"description": "Errors encountered during execution of the mutation.",
"args": [
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
}
}
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [
],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "EpicBoardEdge",
...
...
@@ -45574,6 +45692,33 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "epicBoardCreate",
"description": null,
"args": [
{
"name": "input",
"description": null,
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "INPUT_OBJECT",
"name": "EpicBoardCreateInput",
"ofType": null
}
},
"defaultValue": null
}
],
"type": {
"kind": "OBJECT",
"name": "EpicBoardCreatePayload",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "epicSetSubscription",
"description": null,
doc/api/graphql/reference/index.md
View file @
6d63abe8
...
...
@@ -1448,6 +1448,16 @@ Represents an epic board.
|
`lists`
| EpicListConnection | Epic board lists. |
|
`name`
| String | Name of the board. |
### EpicBoardCreatePayload
Autogenerated return type of EpicBoardCreate.
| Field | Type | Description |
| ----- | ---- | ----------- |
|
`clientMutationId`
| String | A unique identifier for the client performing the mutation. |
|
`epicBoard`
| EpicBoard | The created epic board. |
|
`errors`
| String! => Array | Errors encountered during execution of the mutation. |
### EpicDescendantCount
Counts of descendent epics.
...
...
ee/app/graphql/ee/types/mutation_type.rb
View file @
6d63abe8
...
...
@@ -35,8 +35,9 @@ module EE
mount_mutation
::
Mutations
::
Vulnerabilities
::
CreateExternalIssueLink
mount_mutation
::
Mutations
::
Vulnerabilities
::
DestroyExternalIssueLink
mount_mutation
::
Mutations
::
Boards
::
Update
mount_mutation
::
Mutations
::
Boards
::
Lists
::
UpdateLimitMetrics
mount_mutation
::
Mutations
::
Boards
::
UpdateEpicUserPreferences
mount_mutation
::
Mutations
::
Boards
::
Epics
::
Create
mount_mutation
::
Mutations
::
Boards
::
Lists
::
UpdateLimitMetrics
mount_mutation
::
Mutations
::
InstanceSecurityDashboard
::
AddProject
mount_mutation
::
Mutations
::
InstanceSecurityDashboard
::
RemoveProject
mount_mutation
::
Mutations
::
DastOnDemandScans
::
Create
...
...
ee/app/graphql/mutations/boards/epics/create.rb
0 → 100644
View file @
6d63abe8
# frozen_string_literal: true
module
Mutations
module
Boards
module
Epics
class
Create
<
::
Mutations
::
BaseMutation
include
Mutations
::
ResolvesGroup
include
Mutations
::
Boards
::
CommonMutationArguments
graphql_name
'EpicBoardCreate'
authorize
:admin_epic_board
field
:epic_board
,
Types
::
Boards
::
EpicBoardType
,
null:
true
,
description:
'The created epic board.'
def
resolve
(
args
)
group_path
=
args
.
delete
(
:group_path
)
group
=
authorized_find!
(
group_path:
group_path
)
service_response
=
::
Boards
::
Epics
::
CreateService
.
new
(
group
,
current_user
,
args
).
execute
{
epic_board:
service_response
.
payload
,
errors:
service_response
.
errors
}
end
private
def
find_object
(
group_path
:)
resolve_group
(
full_path:
group_path
)
end
end
end
end
end
ee/app/models/boards/epic_board.rb
View file @
6d63abe8
...
...
@@ -7,7 +7,7 @@ module Boards
has_many
:epic_board_positions
,
foreign_key: :epic_board_id
,
inverse_of: :epic_board
has_many
:epic_lists
,
->
{
ordered
},
foreign_key: :epic_board_id
,
inverse_of: :epic_board
validates
:name
,
length:
{
maximum:
255
}
validates
:name
,
length:
{
maximum:
255
}
,
presence:
true
scope
:order_by_name_asc
,
->
{
order
(
arel_table
[
:name
].
lower
.
asc
).
order
(
id: :asc
)
}
...
...
ee/app/policies/ee/group_policy.rb
View file @
6d63abe8
...
...
@@ -197,6 +197,7 @@ module EE
enable
:update_epic
enable
:read_confidential_epic
enable
:destroy_epic_link
enable
:admin_epic_board
end
rule
{
reporter
&
subepics_available
}.
policy
do
...
...
ee/app/services/boards/epics/create_service.rb
0 → 100644
View file @
6d63abe8
# frozen_string_literal: true
module
Boards
module
Epics
class
CreateService
<
Boards
::
CreateService
extend
::
Gitlab
::
Utils
::
Override
override
:can_create_board?
def
can_create_board?
Feature
.
enabled?
(
:epic_boards
,
parent
)
end
override
:parent_board_collection
def
parent_board_collection
parent
.
epic_boards
end
end
end
end
ee/changelogs/unreleased/233434-cablett-create_epic_board.yml
0 → 100644
View file @
6d63abe8
---
title
:
Add create epic board via GraphQL
merge_request
:
52258
author
:
type
:
added
ee/spec/graphql/mutations/boards/epics/create_spec.rb
0 → 100644
View file @
6d63abe8
# frozen_string_literal: true
require
'spec_helper'
RSpec
.
describe
Mutations
::
Boards
::
Epics
::
Create
do
include
GraphqlHelpers
let_it_be
(
:current_user
)
{
create
(
:user
)
}
let_it_be
(
:group
)
{
create
(
:group
,
:private
)
}
let
(
:mutation
)
{
described_class
.
new
(
object:
nil
,
context:
{
current_user:
current_user
},
field:
nil
)
}
let
(
:name
)
{
'A glorious epic board'
}
subject
{
mutation
.
resolve
(
group_path:
group
.
full_path
,
name:
name
)
}
shared_examples
'epic board creation error'
do
it
'raises error'
do
expect
{
mutation
.
resolve
(
group_path:
group
.
full_path
,
name:
name
)
}
.
to
raise_error
(
Gitlab
::
Graphql
::
Errors
::
ResourceNotAvailable
)
end
end
context
'with epic feature enabled and epic_boards feature flag enabled'
do
before
do
stub_licensed_features
(
epics:
true
)
stub_feature_flags
(
epic_boards:
true
)
end
context
'when user does not have permission to create epic board'
do
it_behaves_like
'epic board creation error'
end
context
'when user has permission to create epic board'
do
before
do
group
.
add_reporter
(
current_user
)
end
it
'creates an epic board'
do
result
=
mutation
.
resolve
(
group_path:
group
.
full_path
,
name:
name
)
expect
(
result
[
:epic_board
]).
to
be_valid
expect
(
result
[
:epic_board
].
group
).
to
eq
(
group
)
expect
(
result
[
:epic_board
].
name
).
to
eq
(
name
)
end
end
end
context
'with epic_boards feature flag disabled'
do
before
do
stub_feature_flags
(
epic_boards:
false
)
end
it_behaves_like
'epic board creation error'
end
context
'with epic feature disabled'
do
before
do
stub_licensed_features
(
epics:
false
)
end
it_behaves_like
'epic board creation error'
end
end
ee/spec/policies/group_policy_spec.rb
View file @
6d63abe8
...
...
@@ -7,7 +7,7 @@ RSpec.describe GroupPolicy do
let
(
:epic_rules
)
do
%i(read_epic create_epic admin_epic destroy_epic read_confidential_epic
destroy_epic_link read_epic_board read_epic_list)
destroy_epic_link read_epic_board read_epic_list
admin_epic_board
)
end
context
'when epics feature is disabled'
do
...
...
ee/spec/services/boards/create_service_spec.rb
View file @
6d63abe8
...
...
@@ -13,60 +13,7 @@ RSpec.describe Boards::CreateService, services: true do
stub_licensed_features
(
multiple_group_issue_boards:
true
)
end
context
'with valid params'
do
subject
(
:service
)
{
described_class
.
new
(
parent
,
double
,
name:
'Backend'
)
}
it
'creates a new board'
do
expect
{
service
.
execute
}.
to
change
(
parent
.
boards
,
:count
).
by
(
1
)
end
it
'returns a successful response'
do
expect
(
service
.
execute
).
to
be_success
end
it
'creates the default lists'
do
board
=
created_board
expect
(
board
.
lists
.
size
).
to
eq
2
expect
(
board
.
lists
.
first
).
to
be_backlog
expect
(
board
.
lists
.
last
).
to
be_closed
end
end
context
'with invalid params'
do
subject
(
:service
)
{
described_class
.
new
(
parent
,
double
,
name:
nil
)
}
it
'does not create a new parent board'
do
expect
{
service
.
execute
}.
not_to
change
(
parent
.
boards
,
:count
)
end
it
'returns an error response'
do
expect
(
service
.
execute
).
to
be_error
end
it
"does not create board's default lists"
do
expect
(
created_board
.
lists
.
size
).
to
eq
0
end
end
context
'without params'
do
subject
(
:service
)
{
described_class
.
new
(
parent
,
double
)
}
it
'creates a new parent board'
do
expect
{
service
.
execute
}.
to
change
(
parent
.
boards
,
:count
).
by
(
1
)
end
it
'returns a successful response'
do
expect
(
service
.
execute
).
to
be_success
end
it
"creates board's default lists"
do
board
=
created_board
expect
(
board
.
lists
.
size
).
to
eq
2
expect
(
board
.
lists
.
last
).
to
be_closed
end
end
it_behaves_like
'create a board'
,
:boards
end
end
...
...
ee/spec/services/boards/epics/create_service_spec.rb
0 → 100644
View file @
6d63abe8
# frozen_string_literal: true
require
'spec_helper'
RSpec
.
describe
Boards
::
Epics
::
CreateService
,
services:
true
do
def
created_board
service
.
execute
.
payload
end
let
(
:parent
)
{
create
(
:group
)
}
let
(
:epics_enabled
)
{
false
}
let
(
:epic_boards_enabled
)
{
false
}
before
do
stub_licensed_features
(
epics:
epics_enabled
)
stub_feature_flags
(
epic_boards:
epic_boards_enabled
)
end
shared_examples
'does not create a board'
do
specify
do
service
=
described_class
.
new
(
parent
,
double
)
expect
(
service
.
execute
.
payload
).
not_to
be_nil
expect
{
service
.
execute
}.
not_to
change
(
parent
.
epic_boards
,
:count
)
end
end
context
'With epics feature available'
do
let
(
:epics_enabled
)
{
true
}
it_behaves_like
'does not create a board'
context
'with epic boards feature available'
do
let
(
:epic_boards_enabled
)
{
true
}
it_behaves_like
'create a board'
,
:epic_boards
end
end
context
'with epics feature not available'
do
it_behaves_like
'does not create a board'
end
end
ee/spec/support/shared_examples/services/boards/create_boards_shared_examples.rb
0 → 100644
View file @
6d63abe8
# frozen_string_literal: true
RSpec
.
shared_examples
'create a board'
do
|
scope
|
context
'with valid params'
do
subject
(
:service
)
{
described_class
.
new
(
parent
,
double
,
name:
'Backend'
)
}
it
'creates a new board'
do
expect
{
service
.
execute
}.
to
change
(
parent
.
send
(
scope
),
:count
).
by
(
1
)
end
it
'returns a successful response'
do
expect
(
service
.
execute
).
to
be_success
end
it
'creates the default lists'
do
board
=
created_board
expect
(
board
.
lists
.
size
).
to
eq
2
expect
(
board
.
lists
.
first
).
to
be_backlog
expect
(
board
.
lists
.
last
).
to
be_closed
end
end
context
'with invalid params'
do
subject
(
:service
)
{
described_class
.
new
(
parent
,
double
,
name:
nil
)
}
it
'does not create a new parent board'
do
expect
{
service
.
execute
}.
not_to
change
(
parent
.
send
(
scope
),
:count
)
end
it
'returns an error response'
do
expect
(
service
.
execute
).
to
be_error
end
it
"does not create board's default lists"
do
expect
(
created_board
.
lists
.
size
).
to
eq
0
end
end
context
'without params'
do
subject
(
:service
)
{
described_class
.
new
(
parent
,
double
)
}
it
'creates a new parent board'
do
expect
{
service
.
execute
}.
to
change
(
parent
.
send
(
scope
),
:count
).
by
(
1
)
end
it
'returns a successful response'
do
expect
(
service
.
execute
).
to
be_success
end
it
"creates board's default lists"
do
board
=
created_board
expect
(
board
.
lists
.
size
).
to
eq
2
expect
(
board
.
lists
.
last
).
to
be_closed
end
end
end
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment