Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions guides/subscriptions/subscription_classes.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,11 +137,11 @@ subscription($roomId: ID!) {

If you remove `null: false`, then you can return different data in the initial subscription and the subsequent updates. (See lifecycle methods below.)

Instead of a generated type, you can provide an already-configured type with `payload_type`:
Instead of a generated type, you can provide an already-configured type with `type` just like mutations and resolvers:

```ruby
# Just return a message
payload_type Types::MessageType
type Types::MessageType
```

(In that case, don't return a hash from `#subscribe` or `#update`, return a `message` object instead.)
Expand Down
5 changes: 1 addition & 4 deletions lib/graphql/schema/subscription.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,6 @@ class Subscription < GraphQL::Schema::Resolver
extend GraphQL::Schema::Resolver::HasPayloadType
extend GraphQL::Schema::Member::HasFields
NO_UPDATE = :no_update
# The generated payload type is required; If there's no payload,
# propagate null.
null false

# @api private
def initialize(object:, context:, field:)
Expand Down Expand Up @@ -69,7 +66,7 @@ def resolve(**args)
def resolve_subscribe(**args)
ret_val = !args.empty? ? subscribe(**args) : subscribe
if ret_val == :no_response
context.skip
@null ? nil : context.skip
else
ret_val
end
Expand Down
97 changes: 90 additions & 7 deletions spec/graphql/schema/subscription_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ class UsersJoinedManualPayload < GraphQL::Schema::Object
description: "Includes newly-created users, or all users on the initial load"
end

payload_type UsersJoinedManualPayload
type UsersJoinedManualPayload

def subscribe
{ users: USERS.values }
Expand All @@ -137,13 +137,51 @@ class NewUsersJoined < BaseSubscription
description: "Includes newly-created users, or all users on the initial load"
end

# The newer API changes using type and not needing to implement the "subscribe" method
# which aligns with how relay or apollo digest the payload.
class UserChanged < BaseSubscription
type User

argument :handle, String, loads: User, as: :user, camelize: false

# don't need to implement the subscribe method. Returns an empty payload as
# the initial subscribe action shouldn't need to load any data, only when the event happens

def update(user:)
user
end

def self.visible?(context)
!context[:legacy_schema]
end
end

# The newer API changes using type and not needing to implement the "subscribe" method
# which aligns with how relay or apollo digest the payload.
class LegacyUserChanged < BaseSubscription
null true
payload_type User

argument :handle, String, loads: User, as: :user, camelize: false

def update(user:)
user
end

def self.visible?(context)
!!context[:legacy_schema]
end
end

class Subscription < GraphQL::Schema::Object
field :toot_was_tooted, subscription: TootWasTooted, extras: [:path, :query]
field :toot_was_tooted, subscription: LegacyTootWasTooted, extras: [:path, :query]
field :direct_toot_was_tooted, subscription: DirectTootWasTooted
field :direct_toot_was_tooted_with_optional_scope, subscription: DirectTootWasTootedWithOptionalScope
field :users_joined, subscription: UsersJoined
field :new_users_joined, subscription: NewUsersJoined
field :user_changed, subscription: UserChanged
field :user_changed, subscription: LegacyUserChanged
end

class Mutation < GraphQL::Schema::Object
Expand Down Expand Up @@ -254,13 +292,13 @@ def in_memory_subscription_count
end

it "generates a return type" do
return_type = SubscriptionFieldSchema::TootWasTooted.payload_type
return_type = SubscriptionFieldSchema::TootWasTooted.type
assert_equal "TootWasTootedPayload", return_type.graphql_name
assert_equal ["toot", "user"], return_type.fields.keys
end

it "can use a premade `payload_type`" do
return_type = SubscriptionFieldSchema::UsersJoined.payload_type
it "can use a premade `type`" do
return_type = SubscriptionFieldSchema::UsersJoined.type
assert_equal "UsersJoinedManualPayload", return_type.graphql_name
assert_equal ["users"], return_type.fields.keys
assert_equal SubscriptionFieldSchema::UsersJoined::UsersJoinedManualPayload, return_type
Expand Down Expand Up @@ -304,7 +342,7 @@ def in_memory_subscription_count
GRAPHQL

expected_response = {
"data"=>nil,
"data"=> { "tootWasTooted" => nil },
"errors"=>[
{
"message"=>"You don't have permission to subscribe",
Expand All @@ -328,7 +366,7 @@ def in_memory_subscription_count
GRAPHQL

expected_response = {
"data" => nil,
"data" => { "tootWasTooted" => nil },
"errors" => [
{
"message"=>"No object found for `handle: \"jack\"`",
Expand All @@ -350,7 +388,7 @@ def in_memory_subscription_count
}
GRAPHQL
expected_response = {
"data"=>nil,
"data"=> { "tootWasTooted" => nil },
"errors"=>[
{
"message"=>"Can't subscribe to private user ([\"tootWasTooted\"])",
Expand All @@ -376,6 +414,34 @@ def in_memory_subscription_count
assert_equal 1, in_memory_subscription_count
end

it "sends a minimal initial response if :no_response is returned when using type instead of payload_type, which is the default" do
assert_equal 0, in_memory_subscription_count

res = exec_query <<-GRAPHQL
subscription {
userChanged(handle: "matz") {
handle
}
}
GRAPHQL
assert_equal({"data" => { "userChanged" => nil }}, res)
assert_equal 1, in_memory_subscription_count
end

it "sends no initial response if :no_response is returned when using legacy payload_type" do
assert_equal 0, in_memory_subscription_count

res = exec_query <<-GRAPHQL, context: { legacy_schema: true }
subscription {
userChanged(handle: "matz") {
handle
}
}
GRAPHQL
assert_equal({"data" => nil}, res)
assert_equal 1, in_memory_subscription_count
end

it "works when there are no arguments" do
assert_equal 0, in_memory_subscription_count

Expand Down Expand Up @@ -411,6 +477,23 @@ def in_memory_subscription_count
assert_equal "I am a C programmer", update_payload["data"]["tootWasTooted"]["toot"]["body"]
end

it "updates with the returned value but no initial subscription value" do
res = exec_query <<-GRAPHQL
subscription {
userChanged(handle: "matz") {
handle
}
}
GRAPHQL

assert_equal({"data" => { "userChanged" => nil }}, res)
assert_equal 1, in_memory_subscription_count
SubscriptionFieldSchema.subscriptions.trigger(:user_changed, { handle: "matz" }, {handle: "matz", private: false })

update = res.context[:subscription_mailbox].first
assert_equal "matz", update["data"]["userChanged"]["handle"]
end

it "updates with the returned value" do
res = exec_query <<-GRAPHQL
subscription {
Expand Down
Loading