Skip to content

Instantly share code, notes, and snippets.

@rmosolgo
Created February 5, 2025 13:38
Show Gist options
  • Save rmosolgo/f4201e8dd94ebe9f84881b523df1602a to your computer and use it in GitHub Desktop.
Save rmosolgo/f4201e8dd94ebe9f84881b523df1602a to your computer and use it in GitHub Desktop.
Assigning a GraphQL-Ruby changeset version from a directive in the query
require "bundler/inline"
gemfile do
gem "graphql", "2.4.8", source: "https://rubygems.org"
gem "graphql-enterprise", source: "https://gems.graphql.pro"
end
class MySchema < GraphQL::Schema
class SwitchFields < GraphQL::Enterprise::Changeset
release "2025-02-01"
end
class BaseField < GraphQL::Schema::Field
include GraphQL::Enterprise::Changeset::FieldIntegration
end
class Query < GraphQL::Schema::Object
field_class BaseField
field :old_field, String, fallback_value: "Old", removed_in: SwitchFields
field :new_field, String, fallback_value: "New", added_in: SwitchFields
end
# This directive definition is used for validation.
class Changeset < GraphQL::Schema::Directive
locations(QUERY, SUBSCRIPTION, MUTATION)
argument :version, String
end
# This trace module will read the value from the query AST
# and apply it to the `query.context` Hash.
#
# (Directive classes have a `.resolve` method, but it's too late for this
# application because it's called during execution, but we need this
# key to be present in context during _validation_.)
#
# `execute_multiplex` is the top-level hook, so we can do it there.
module SetChangesetFromDirective
def execute_multiplex(multiplex:)
multiplex.queries.each do |query|
operation = query.selected_operation
if (dir = operation.directives.find { |d| d.name == "changeset" })
changeset_version = dir.arguments.first.value
query.context[:changeset_version] = changeset_version
end
end
super
end
end
query(Query)
directive(Changeset)
trace_with(SetChangesetFromDirective)
use GraphQL::Schema::Visibility
use GraphQL::Enterprise::Changeset::Release, changesets: [SwitchFields]
end
# Using context:
pp MySchema.execute("{ newField }", context: { changeset_version: "2025-02-01" }).to_h
# {"data" => {"newField" => "New"}}
pp MySchema.execute("{ oldField }", context: { changeset_version: "2025-02-01" }).to_h
# {"errors" => [{"message" => "Field 'oldField' doesn't exist on type 'Query'", "locations" => [{"line" => 1, "column" => 3}], "path" => ["query", "oldField"], "extensions" => {"code" => "undefinedField", "typeName" => "Query", "fieldName" => "oldField"}}]}
pp MySchema.execute("{ oldField }", context: { changeset_version: "2024-02-01" }).to_h
# {"data" => {"oldField" => "Old"}}
# Using the directive in a multiplex:
res = MySchema.multiplex(
[
{ query: "query @changeset(version: \"2025-02-01\") { newField }" },
{ query: "query @changeset(version: \"2025-02-01\") { oldField }" },
{ query: "query @changeset(version: \"2024-02-01\") { oldField }" },
]
)
pp res.map(&:to_h)
# [
# {"data" => {"newField" => "New"}},
# {"errors" => [{"message" => "Field 'oldField' doesn't exist on type 'Query'", "locations" => [{"line" => 1, "column" => 43}], "path" => ["query", "oldField"], "extensions" => {"code" => "undefinedField", "typeName" => "Query", "fieldName" => "oldField"}}]},
# {"data" => {"oldField" => "Old"}}
# ]
puts MySchema.to_definition(context: { changeset_version: "2025-02-01" })
# directive @changeset(version: String!) on MUTATION | QUERY | SUBSCRIPTION
# type Query {
# newField: String
# }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment