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
45fbc742
Commit
45fbc742
authored
Apr 20, 2021
by
Heinrich Lee Yu
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add GraphQL subscription for assignee updates
This will be used for updating sidebar assignees in real time
parent
7955bc37
Changes
12
Hide whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
246 additions
and
3 deletions
+246
-3
app/graphql/subscriptions/base_subscription.rb
app/graphql/subscriptions/base_subscription.rb
+6
-0
app/graphql/subscriptions/issuable_updated.rb
app/graphql/subscriptions/issuable_updated.rb
+29
-0
app/graphql/types/issuable_type.rb
app/graphql/types/issuable_type.rb
+23
-0
app/graphql/types/subscription_type.rb
app/graphql/types/subscription_type.rb
+10
-0
doc/api/graphql/reference/index.md
doc/api/graphql/reference/index.md
+32
-0
ee/app/graphql/ee/types/issuable_type.rb
ee/app/graphql/ee/types/issuable_type.rb
+24
-0
ee/spec/graphql/ee/types/issuable_type_spec.rb
ee/spec/graphql/ee/types/issuable_type_spec.rb
+15
-0
rubocop/cop/graphql/authorize_types.rb
rubocop/cop/graphql/authorize_types.rb
+1
-1
spec/graphql/subscriptions/issuable_updated_spec.rb
spec/graphql/subscriptions/issuable_updated_spec.rb
+68
-0
spec/graphql/types/issuable_type_spec.rb
spec/graphql/types/issuable_type_spec.rb
+23
-0
spec/graphql/types/subscription_type_spec.rb
spec/graphql/types/subscription_type_spec.rb
+13
-0
spec/support/helpers/graphql_helpers.rb
spec/support/helpers/graphql_helpers.rb
+2
-2
No files found.
app/graphql/subscriptions/base_subscription.rb
View file @
45fbc742
...
...
@@ -18,6 +18,12 @@ module Subscriptions
private
def
unauthorized!
unsubscribe
if
context
.
query
.
subscription_update?
raise
GraphQL
::
ExecutionError
,
'Unauthorized subscription'
end
def
current_user
context
[
:current_user
]
end
...
...
app/graphql/subscriptions/issuable_updated.rb
0 → 100644
View file @
45fbc742
# frozen_string_literal: true
module
Subscriptions
class
IssuableUpdated
<
BaseSubscription
include
Gitlab
::
Graphql
::
Laziness
payload_type
Types
::
IssuableType
argument
:issuable_id
,
Types
::
GlobalIDType
[
Issuable
],
required:
true
,
description:
'ID of the issuable.'
def
subscribe
(
issuable_id
:)
nil
end
def
authorized?
(
issuable_id
:)
# TODO: remove this check when the compatibility layer is removed
# See: https://gitlab.com/gitlab-org/gitlab/-/issues/257883
raise
Gitlab
::
Graphql
::
Errors
::
ArgumentError
,
'Invalid IssuableID'
unless
issuable_id
.
is_a?
(
GlobalID
)
issuable
=
force
(
GitlabSchema
.
find_by_gid
(
issuable_id
))
unauthorized!
unless
issuable
&&
Ability
.
allowed?
(
current_user
,
:"read_
#{
issuable
.
to_ability_name
}
"
,
issuable
)
true
end
end
end
app/graphql/types/issuable_type.rb
0 → 100644
View file @
45fbc742
# frozen_string_literal: true
module
Types
class
IssuableType
<
BaseUnion
graphql_name
'Issuable'
description
'Represents an issuable.'
possible_types
Types
::
IssueType
,
Types
::
MergeRequestType
def
self
.
resolve_type
(
object
,
context
)
case
object
when
Issue
Types
::
IssueType
when
MergeRequest
Types
::
MergeRequestType
else
raise
'Unsupported issuable type'
end
end
end
end
Types
::
IssuableType
.
prepend_if_ee
(
'::EE::Types::IssuableType'
)
app/graphql/types/subscription_type.rb
0 → 100644
View file @
45fbc742
# frozen_string_literal: true
module
Types
class
SubscriptionType
<
::
Types
::
BaseObject
graphql_name
'Subscription'
field
:issuable_assignees_updated
,
subscription:
Subscriptions
::
IssuableUpdated
,
null:
true
,
description:
'Triggered when the assignees of an issuable are updated.'
end
end
doc/api/graphql/reference/index.md
View file @
45fbc742
...
...
@@ -11795,6 +11795,22 @@ Represents the Geo sync and verification state of a snippet repository.
|
<a
id=
"submoduletype"
></a>
`type`
|
[
`EntryType!`
](
#entrytype
)
| Type of tree entry. |
|
<a
id=
"submoduleweburl"
></a>
`webUrl`
|
[
`String`
](
#string
)
| Web URL for the sub-module. |
### `Subscription`
#### Fields with arguments
##### `Subscription.issuableAssigneesUpdated`
Triggered when the assignees of an issuable are updated.
Returns
[
`Issuable`
](
#issuable
)
.
###### Arguments
| Name | Type | Description |
| ---- | ---- | ----------- |
|
<a
id=
"subscriptionissuableassigneesupdatedissuableid"
></a>
`issuableId`
|
[
`IssuableID!`
](
#issuableid
)
| ID of the issuable. |
### `TaskCompletionStatus`
Completion status of tasks.
...
...
@@ -14158,6 +14174,12 @@ An example `IncidentManagementOncallRotationID` is: `"gid://gitlab/IncidentManag
Represents non-fractional signed whole numeric values. Int can represent values between -(2^31) and 2^31 - 1.
### `IssuableID`
A
`IssuableID`
is a global ID. It is encoded as a string.
An example
`IssuableID`
is:
`"gid://gitlab/Issuable/1"`
.
### `IssueID`
A
`IssueID`
is a global ID. It is encoded as a string.
...
...
@@ -14367,6 +14389,16 @@ abstract types.
### Unions
#### `Issuable`
Represents an issuable.
One of:
-
[
`Epic`
](
#epic
)
-
[
`Issue`
](
#issue
)
-
[
`MergeRequest`
](
#mergerequest
)
#### `PackageMetadata`
Represents metadata associated with a Package.
...
...
ee/app/graphql/ee/types/issuable_type.rb
0 → 100644
View file @
45fbc742
# frozen_string_literal: true
module
EE
module
Types
module
IssuableType
extend
ActiveSupport
::
Concern
prepended
do
possible_types
::
Types
::
EpicType
end
class_methods
do
def
resolve_type
(
object
,
context
)
case
object
when
::
Epic
::
Types
::
EpicType
else
super
end
end
end
end
end
end
ee/spec/graphql/ee/types/issuable_type_spec.rb
0 → 100644
View file @
45fbc742
# frozen_string_literal: true
require
'spec_helper'
RSpec
.
describe
GitlabSchema
.
types
[
'Issuable'
]
do
it
'returns possible types'
do
expect
(
described_class
.
possible_types
).
to
include
(
Types
::
EpicType
)
end
describe
'.resolve_type'
do
it
'resolves epics'
do
expect
(
described_class
.
resolve_type
(
build
(
:epic
),
{})).
to
eq
(
Types
::
EpicType
)
end
end
end
rubocop/cop/graphql/authorize_types.rb
View file @
45fbc742
...
...
@@ -8,7 +8,7 @@ module RuboCop
'https://docs.gitlab.com/ee/development/api_graphql_styleguide.html#type-authorization'
# We want to exclude our own basetypes and scalars
ALLOWED_TYPES
=
%w[BaseEnum BaseScalar BasePermissionType MutationType
ALLOWED_TYPES
=
%w[BaseEnum BaseScalar BasePermissionType MutationType
SubscriptionType
QueryType GraphQL::Schema BaseUnion BaseInputObject]
.
freeze
def_node_search
:authorize?
,
<<~
PATTERN
...
...
spec/graphql/subscriptions/issuable_updated_spec.rb
0 → 100644
View file @
45fbc742
# frozen_string_literal: true
require
'spec_helper'
RSpec
.
describe
Subscriptions
::
IssuableUpdated
do
include
GraphqlHelpers
it
{
expect
(
described_class
).
to
have_graphql_arguments
(
:issuable_id
)
}
it
{
expect
(
described_class
.
payload_type
).
to
eq
(
Types
::
IssuableType
)
}
describe
'#resolve'
do
let_it_be
(
:unauthorized_user
)
{
create
(
:user
)
}
let_it_be
(
:issue
)
{
create
(
:issue
)
}
let
(
:current_user
)
{
issue
.
author
}
let
(
:issuable_id
)
{
issue
.
to_gid
}
subject
{
resolver
.
resolve_with_support
(
issuable_id:
issuable_id
)
}
context
'initial subscription'
do
let
(
:resolver
)
{
resolver_instance
(
described_class
,
ctx:
{
current_user:
current_user
},
subscription_update:
false
)
}
it
'returns nil'
do
expect
(
subject
).
to
eq
(
nil
)
end
context
'when user is unauthorized'
do
let
(
:current_user
)
{
unauthorized_user
}
it
'raises an exception'
do
expect
{
subject
}.
to
raise_error
(
GraphQL
::
ExecutionError
)
end
end
context
'when issue does not exist'
do
let
(
:issuable_id
)
{
GlobalID
.
parse
(
"gid://gitlab/Issue/
#{
non_existing_record_id
}
"
)
}
it
'raises an exception'
do
expect
{
subject
}.
to
raise_error
(
GraphQL
::
ExecutionError
)
end
end
context
'when a GraphQL::ID_TYPE is provided'
do
let
(
:issuable_id
)
{
issue
.
to_gid
.
to_s
}
it
'raises an exception'
do
expect
{
subject
}.
to
raise_error
(
Gitlab
::
Graphql
::
Errors
::
ArgumentError
)
end
end
end
context
'subscription updates'
do
let
(
:resolver
)
{
resolver_instance
(
described_class
,
obj:
issue
,
ctx:
{
current_user:
current_user
},
subscription_update:
true
)
}
it
'returns the resolved object'
do
expect
(
subject
).
to
eq
(
issue
)
end
context
'when user is unauthorized'
do
let
(
:current_user
)
{
unauthorized_user
}
it
'unsubscribes the user'
do
expect
{
subject
}.
to
throw_symbol
(
:graphql_subscription_unsubscribed
)
end
end
end
end
end
spec/graphql/types/issuable_type_spec.rb
0 → 100644
View file @
45fbc742
# frozen_string_literal: true
require
'spec_helper'
RSpec
.
describe
GitlabSchema
.
types
[
'Issuable'
]
do
it
'returns possible types'
do
expect
(
described_class
.
possible_types
).
to
include
(
Types
::
IssueType
,
Types
::
MergeRequestType
)
end
describe
'.resolve_type'
do
it
'resolves issues'
do
expect
(
described_class
.
resolve_type
(
build
(
:issue
),
{})).
to
eq
(
Types
::
IssueType
)
end
it
'resolves merge requests'
do
expect
(
described_class
.
resolve_type
(
build
(
:merge_request
),
{})).
to
eq
(
Types
::
MergeRequestType
)
end
it
'raises an error for invalid types'
do
expect
{
described_class
.
resolve_type
(
build
(
:user
),
{})
}.
to
raise_error
'Unsupported issuable type'
end
end
end
spec/graphql/types/subscription_type_spec.rb
0 → 100644
View file @
45fbc742
# frozen_string_literal: true
require
'spec_helper'
RSpec
.
describe
GitlabSchema
.
types
[
'Subscription'
]
do
it
'has the expected fields'
do
expected_fields
=
%i[
issuable_assignees_updated
]
expect
(
described_class
).
to
have_graphql_fields
(
*
expected_fields
).
only
end
end
spec/support/helpers/graphql_helpers.rb
View file @
45fbc742
...
...
@@ -142,9 +142,9 @@ module GraphqlHelpers
Class
.
new
(
::
Types
::
BaseObject
)
{
graphql_name
name
}
end
def
resolver_instance
(
resolver_class
,
obj:
nil
,
ctx:
{},
field:
nil
,
schema:
GitlabSchema
)
def
resolver_instance
(
resolver_class
,
obj:
nil
,
ctx:
{},
field:
nil
,
schema:
GitlabSchema
,
subscription_update:
false
)
if
ctx
.
is_a?
(
Hash
)
q
=
double
(
'Query'
,
schema:
schema
)
q
=
double
(
'Query'
,
schema:
schema
,
subscription_update?:
subscription_update
)
ctx
=
GraphQL
::
Query
::
Context
.
new
(
query:
q
,
object:
obj
,
values:
ctx
)
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