Commit 7955bc37 authored by Heinrich Lee Yu's avatar Heinrich Lee Yu

Add GraphQL channel for handling subscriptions

This ActionCable channel will be used to execute GraphQL subscriptions
parent 912665e9
# frozen_string_literal: true
# This is based on https://github.com/rmosolgo/graphql-ruby/blob/v1.11.8/lib/graphql/subscriptions/action_cable_subscriptions.rb#L19-L82
# modified to work with our own ActionCableLink client
class GraphqlChannel < ApplicationCable::Channel # rubocop:disable Gitlab/NamespacedClass
def subscribed
@subscription_ids = []
query = params['query']
variables = Gitlab::Graphql::Variables.new(params['variables']).to_h
operation_name = params['operationName']
result = GitlabSchema.execute(
query,
context: context,
variables: variables,
operation_name: operation_name
)
payload = {
result: result.to_h,
more: result.subscription?
}
# Track the subscription here so we can remove it
# on unsubscribe.
if result.context[:subscription_id]
@subscription_ids << result.context[:subscription_id]
end
transmit(payload)
end
def unsubscribed
@subscription_ids.each do |sid|
GitlabSchema.subscriptions.delete_subscription(sid)
end
end
rescue_from Gitlab::Graphql::Variables::Invalid do |exception|
transmit({ errors: [{ message: exception.message }] })
end
private
# When modifying the context, also update GraphqlController#context if needed
# so that we have similar context when executing queries, mutations, and subscriptions
#
# Objects added to the context may also need to be reloaded in
# `Subscriptions::BaseSubscription` so that they are not stale
def context
# is_sessionless_user is always false because we only support cookie auth in ActionCable
{ channel: self, current_user: current_user, is_sessionless_user: false }
end
end
......@@ -109,6 +109,8 @@ class GraphqlController < ApplicationController
end
end
# When modifying the context, also update GraphqlChannel#context if needed
# so that we have similar context when executing queries, mutations, and subscriptions
def context
@context ||= { current_user: current_user, is_sessionless_user: !!sessionless_user?, request: request }
end
......
......@@ -10,6 +10,7 @@ class GitlabSchema < GraphQL::Schema
DEFAULT_MAX_DEPTH = 15
AUTHENTICATED_MAX_DEPTH = 20
use GraphQL::Subscriptions::ActionCableSubscriptions
use GraphQL::Pagination::Connections
use BatchLoader::GraphQL
use Gitlab::Graphql::Pagination::Connections
......@@ -24,6 +25,7 @@ class GitlabSchema < GraphQL::Schema
query Types::QueryType
mutation Types::MutationType
subscription Types::SubscriptionType
default_max_page_size 100
......
# frozen_string_literal: true
module Subscriptions
class BaseSubscription < GraphQL::Schema::Subscription
object_class Types::BaseObject
field_class Types::BaseField
def initialize(object:, context:, field:)
super
# Reset user so that we don't use a stale user for authorization
current_user.reset if current_user
end
def authorized?(*)
raise NotImplementedError
end
private
def current_user
context[:current_user]
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