Class: ElasticGraph::GraphQL::Resolvers::GraphQLAdapter

Inherits:
Object
  • Object
show all
Defined in:
lib/elastic_graph/graphql/resolvers/graphql_adapter.rb

Overview

Adapts the GraphQL gem’s resolver interface to the interface implemented by our resolvers. Responsible for routing a resolution request to the appropriate resolver.

Instance Method Summary collapse

Constructor Details

#initialize(schema:, datastore_query_builder:, datastore_query_adapters:, runtime_metadata:, resolvers:) ⇒ GraphQLAdapter

Returns a new instance of GraphQLAdapter.



18
19
20
21
22
23
24
25
26
27
28
29
30
31
# File 'lib/elastic_graph/graphql/resolvers/graphql_adapter.rb', line 18

def initialize(schema:, datastore_query_builder:, datastore_query_adapters:, runtime_metadata:, resolvers:)
  @schema = schema
  @query_adapter = QueryAdapter.new(
    datastore_query_builder: datastore_query_builder,
    datastore_query_adapters: datastore_query_adapters
  )

  @resolvers = resolvers

  scalar_types_by_name = .scalar_types_by_name
  @coercion_adapters_by_scalar_type_name = ::Hash.new do |hash, name|
    scalar_types_by_name.fetch(name).load_coercion_adapter.extension_class
  end
end

Instance Method Details

#call(parent_type, field, object, args, context) ⇒ Object

To be a valid resolver, we must implement ‘call`, accepting the 5 arguments listed here.

See graphql-ruby.org/api-doc/1.9.6/GraphQL/Schema.html#from_definition-class_method (specifically, the ‘default_resolve` argument) for the API documentation.



37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/elastic_graph/graphql/resolvers/graphql_adapter.rb', line 37

def call(parent_type, field, object, args, context)
  schema_field = @schema.field_named(parent_type.graphql_name, field.name)

  # Extract the `:lookahead` extra that we have configured all fields to provide.
  # See https://graphql-ruby.org/api-doc/1.10.8/GraphQL/Execution/Lookahead.html for more info.
  # It is not a "real" arg in the schema and breaks `args_to_schema_form` when we call that
  # so we need to peel it off here.
  lookahead = args[:lookahead]
  # Convert args to the form they were defined in the schema, undoing the normalization
  # the GraphQL gem does to convert them to Ruby keyword args form.
  args = schema_field.args_to_schema_form(args.except(:lookahead))

  resolver = resolver_for(schema_field, object) do
    raise <<~ERROR
      No resolver yet implemented for this case.

      parent_type: #{schema_field.parent_type}

      field: #{schema_field}

      obj: #{object.inspect}

      args: #{args.inspect}

      ctx: #{context.inspect}
    ERROR
  end

  result = resolver.resolve(field: schema_field, object: object, args: args, context: context, lookahead: lookahead) do
    @query_adapter.build_query_from(field: schema_field, args: args, lookahead: lookahead, context: context)
  end

  # Give the field a chance to coerce the result before returning it. Initially, this is only used to deal with
  # enum value overrides (e.g. so that if `DayOfWeek.MONDAY` has been overridden to `DayOfWeek.MON`, we can coerce
  # a `MONDAY` value being returned by a painless script to `MON`), but this is designed to be general purpose
  # and we may use it for other coercions in the future.
  #
  # Note that coercion of scalar values is handled by the `coerce_result` callback below.
  schema_field.coerce_result(result)
end

#coerce_input(type, value, ctx) ⇒ Object



92
93
94
# File 'lib/elastic_graph/graphql/resolvers/graphql_adapter.rb', line 92

def coerce_input(type, value, ctx)
  scalar_coercion_adapter_for(type).coerce_input(value, ctx)
end

#coerce_result(type, value, ctx) ⇒ Object



96
97
98
# File 'lib/elastic_graph/graphql/resolvers/graphql_adapter.rb', line 96

def coerce_result(type, value, ctx)
  scalar_coercion_adapter_for(type).coerce_result(value, ctx)
end

#resolve_type(supertype, object, context) ⇒ Object

In order to support unions and interfaces, we must implement ‘resolve_type`.



79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/elastic_graph/graphql/resolvers/graphql_adapter.rb', line 79

def resolve_type(supertype, object, context)
  # If `__typename` is available, use that to resolve. It should be available on any embedded abstract types...
  # (See `Inventor` in `config/schema.graphql` for an example of this kind of type union.)
  if (typename = object["__typename"])
    @schema.graphql_schema.possible_types(supertype).find { |t| t.graphql_name == typename }
  else
    # ...otherwise infer the type based on what index the object came from. This is the case
    # with unions/interfaces of individually indexed types.
    # (See `Part` in `config/schema/widgets.rb` for an example of this kind of type union.)
    @schema.document_type_stored_in(object.index_definition_name).graphql_type
  end
end