module WCC::Contentful::Graphql::Federation

Extend this module inside a root query definition to do schema federation. blog.apollographql.com/apollo-federation-f260cf525d21

This handles only queries, not mutations or subscriptions.

Constants

BuildsArguments

Public Instance Methods

delegate_to_schema(schema, field_name: nil, arguments: nil) click to toggle source
# File lib/wcc/contentful/graphql/federation.rb, line 51
def delegate_to_schema(schema, field_name: nil, arguments: nil)
  ->(obj, inner_args, context) {
    field_name ||= context.ast_node.name

    arguments = arguments.call(obj, inner_args, context) if arguments&.respond_to?(:call)
    arguments = BuildsArguments.call(arguments) if arguments
    arguments ||= context.ast_node.arguments

    field_node = GraphQL::Language::Nodes::Field.new(
      name: field_name,
      arguments: arguments,
      selections: context.ast_node.selections,
      directives: context.ast_node.directives
    )

    query_node = GraphQL::Language::Nodes::OperationDefinition.new(
      name: context.query.operation_name,
      operation_type: 'query',
      variables: context.query.selected_operation.variables,
      selections: [
        field_node
      ]
    )

    # the ast_node.to_query_string prints the relevant section of the query to
    # a string.  We build a query out of that which we execute on the external
    # schema.
    query = query_node.to_query_string

    result = schema.execute(query,
      variables: context.query.variables)

    if result['errors'].present?
      raise GraphQL::ExecutionError.new(
        result.dig('errors', 0, 'message'),
        ast_node: context.ast_node
      )
    end

    result.dig('data', field_name)
  }
end
schema_stitch(schema, namespace: nil) click to toggle source

Accepts an externally defined schema with a root query, and “stitches” it's query root into the current GraphQL::ObjectType definition. All fields on the external query object like `resource()`, `allResource()` will be inserted into the current object. The `resolve` method for those fields will execute a query on the external schema, returning the results.

# File lib/wcc/contentful/graphql/federation.rb, line 15
def schema_stitch(schema, namespace: nil)
  ns_titleized = namespace&.titleize
  ns = NamespacesTypes.new(namespace: ns_titleized)

  def_fields =
    proc {
      schema.query.fields.each do |(key, field_def)|
        field key, ns.namespaced(field_def.type) do
          description field_def.description

          field_def.arguments.each do |(arg_name, arg)|
            argument arg_name, ns.namespaced(arg.type)
          end

          resolve delegate_to_schema(schema)
        end
      end
    }

  if namespace
    stub_class = Struct.new(:name)
    namespaced_type =
      GraphQL::ObjectType.define do
        name ns_titleized

        instance_exec(&def_fields)
      end

    field namespace, namespaced_type do
      resolve ->(_obj, _arguments, _context) { stub_class.new(namespace) }
    end
  else
    def_fields.call
  end
end