Class: GraphQL::Stitching::Request

Inherits:
Object
  • Object
show all
Defined in:
lib/graphql/stitching/request.rb,
lib/graphql/stitching/request/skip_include.rb

Overview

Request combines a supergraph, GraphQL document, variables, variable/fragment definitions, and the selected operation. It provides the lifecycle of validating, preparing, planning, and executing upon these inputs.

Defined Under Namespace

Classes: SkipInclude

Constant Summary collapse

SKIP_INCLUDE_DIRECTIVE =
/@(?:skip|include)/

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(supergraph, source, operation_name: nil, variables: nil, context: nil) ⇒ Request

Creates a new supergraph request.

Parameters:

  • supergraph (Supergraph)

    supergraph instance that resolves the request.

  • source (String, GraphQL::Language::Nodes::Document)

    the request string or parsed AST.

  • operation_name (String, nil) (defaults to: nil)

    operation name selected for the request.

  • variables (Hash, nil) (defaults to: nil)

    input variables for the request.

  • context (Hash, nil) (defaults to: nil)

    a contextual object passed through resolver flows.



29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# File 'lib/graphql/stitching/request.rb', line 29

def initialize(supergraph, source, operation_name: nil, variables: nil, context: nil)
  @supergraph = supergraph
  @prepared_document = nil
  @string = nil
  @digest = nil
  @normalized_string = nil
  @normalized_digest = nil
  @operation = nil
  @operation_directives = nil
  @variable_definitions = nil
  @fragment_definitions = nil
  @plan = nil

  params = {
    operation_name: operation_name,
    variables: variables,
    context: context,
  }

  if source.is_a?(String)
    @string = source
    params[:query] = source
  else
    params[:document] = source
  end

  @query = GraphQL::Query.new(@supergraph.schema, **params)
  @context = @query.context
  @context[:request] = self
end

Instance Attribute Details

#contextHash (readonly)

Returns contextual object passed through resolver flows.

Returns:

  • (Hash)

    contextual object passed through resolver flows.



21
22
23
# File 'lib/graphql/stitching/request.rb', line 21

def context
  @context
end

#queryGraphQL::Query (readonly)

Returns query object defining the request.

Returns:

  • (GraphQL::Query)

    query object defining the request.



18
19
20
# File 'lib/graphql/stitching/request.rb', line 18

def query
  @query
end

#supergraphSupergraph (readonly)

Returns supergraph instance that resolves the request.

Returns:

  • (Supergraph)

    supergraph instance that resolves the request.



15
16
17
# File 'lib/graphql/stitching/request.rb', line 15

def supergraph
  @supergraph
end

Instance Method Details

#digestString

Returns a digest of the original document string. Generally faster but less consistent.

Returns:

  • (String)

    a digest of the original document string. Generally faster but less consistent.



75
76
77
# File 'lib/graphql/stitching/request.rb', line 75

def digest
  @digest ||= Stitching.digest.call("#{Stitching::VERSION}/#{string}")
end

#execute(raw: false) ⇒ Hash

Executes the request and returns the rendered response.

Parameters:

  • raw (Boolean) (defaults to: false)

    specifies the result should be unshaped without pruning or null bubbling. Useful for debugging.

Returns:

  • (Hash)

    the rendered GraphQL response with "data" and "errors" sections.



175
176
177
178
# File 'lib/graphql/stitching/request.rb', line 175

def execute(raw: false)
  add_subscription_update_handler if subscription?
  Executor.new(self).perform(raw: raw)
end

#fragment_definitionsHash<String, GraphQL::Language::Nodes::FragmentDefinition>

Returns map of fragment names to their AST definitions.

Returns:

  • (Hash<String, GraphQL::Language::Nodes::FragmentDefinition>)

    map of fragment names to their AST definitions.



138
139
140
141
142
# File 'lib/graphql/stitching/request.rb', line 138

def fragment_definitions
  @fragment_definitions ||= prepared_document.definitions.each_with_object({}) do |d, memo|
    memo[d.name] = d if d.is_a?(GraphQL::Language::Nodes::FragmentDefinition)
  end
end

#mutation?Boolean

Returns true if operation type is a mutation.

Returns:

  • (Boolean)

    true if operation type is a mutation



116
117
118
# File 'lib/graphql/stitching/request.rb', line 116

def mutation?
  @query.mutation?
end

#normalized_digestString

Returns a digest of the normalized document string. Slower but more consistent.

Returns:

  • (String)

    a digest of the normalized document string. Slower but more consistent.



80
81
82
# File 'lib/graphql/stitching/request.rb', line 80

def normalized_digest
  @normalized_digest ||= Stitching.digest.call("#{Stitching::VERSION}/#{normalized_string}")
end

#normalized_stringString

Returns a print of the parsed AST document with consistent whitespace.

Returns:

  • (String)

    a print of the parsed AST document with consistent whitespace.



70
71
72
# File 'lib/graphql/stitching/request.rb', line 70

def normalized_string
  @normalized_string ||= prepared_document.to_query_string
end

#operationGraphQL::Language::Nodes::OperationDefinition

Returns The selected root operation for the request.

Returns:

  • (GraphQL::Language::Nodes::OperationDefinition)

    The selected root operation for the request.



85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/graphql/stitching/request.rb', line 85

def operation
  @operation ||= with_prepared_document do
    selected_op = @query.selected_operation
    raise GraphQL::ExecutionError, "No operation selected" unless selected_op

    @prepared_document.definitions.find do |d|
      next unless d.is_a?(GraphQL::Language::Nodes::OperationDefinition)

      selected_op.name.nil? || d.name == selected_op.name
    end
  end
end

#operation_directivesString

Returns A string of directives applied to the root operation. These are passed through in all subgraph requests.

Returns:

  • (String)

    A string of directives applied to the root operation. These are passed through in all subgraph requests.



103
104
105
106
107
108
# File 'lib/graphql/stitching/request.rb', line 103

def operation_directives
  @operation_directives ||= if operation.directives.any?
    printer = GraphQL::Language::Printer.new
    operation.directives.map { printer.print(_1) }.join(" ")
  end
end

#operation_nameObject



98
99
100
# File 'lib/graphql/stitching/request.rb', line 98

def operation_name
  operation.name
end

#original_documentObject



60
61
62
# File 'lib/graphql/stitching/request.rb', line 60

def original_document
  @query.document
end

#plan(new_plan = nil) ⇒ Plan

Gets and sets the query plan for the request. Assigned query plans may pull from a cache, which is useful for redundant GraphQL documents (commonly sent by frontend clients).

if cached_plan = $cache.get(request.digest)
  plan = GraphQL::Stitching::Plan.from_json(JSON.parse(cached_plan))
  request.plan(plan)
else
  plan = request.plan
  $cache.set(request.digest, JSON.generate(plan.as_json))
end

Parameters:

  • new_plan (Plan, nil) (defaults to: nil)

    a cached query plan for the request.

Returns:

  • (Plan)

    query plan for the request.



163
164
165
166
167
168
169
170
# File 'lib/graphql/stitching/request.rb', line 163

def plan(new_plan = nil)
  if new_plan
    raise StitchingError, "Plan must be a `GraphQL::Stitching::Plan`." unless new_plan.is_a?(Plan)
    @plan = new_plan
  else
    @plan ||= Planner.new(self).perform
  end
end

#query?Boolean

Returns true if operation type is a query.

Returns:

  • (Boolean)

    true if operation type is a query



111
112
113
# File 'lib/graphql/stitching/request.rb', line 111

def query?
  @query.query?
end

#stringString

Returns the original document string, or a print of the parsed AST document.

Returns:

  • (String)

    the original document string, or a print of the parsed AST document.



65
66
67
# File 'lib/graphql/stitching/request.rb', line 65

def string
  with_prepared_document { @string || normalized_string }
end

#subscription?Boolean

Returns true if operation type is a subscription.

Returns:

  • (Boolean)

    true if operation type is a subscription



121
122
123
# File 'lib/graphql/stitching/request.rb', line 121

def subscription?
  @query.subscription?
end

#validateArray<GraphQL::ExecutionError>

Validates the request using the combined supergraph schema.

Returns:

  • (Array<GraphQL::ExecutionError>)

    an array of static validation errors



146
147
148
# File 'lib/graphql/stitching/request.rb', line 146

def validate
  @query.static_errors
end

#variable_definitionsHash<String, GraphQL::Language::Nodes::AbstractNode>

Returns map of variable names to AST type definitions.

Returns:

  • (Hash<String, GraphQL::Language::Nodes::AbstractNode>)

    map of variable names to AST type definitions.



131
132
133
134
135
# File 'lib/graphql/stitching/request.rb', line 131

def variable_definitions
  @variable_definitions ||= operation.variables.each_with_object({}) do |v, memo|
    memo[v.name] = v.type
  end
end

#variablesHash<String, Any>

Returns provided variables hash filled in with default values from definitions.

Returns:

  • (Hash<String, Any>)

    provided variables hash filled in with default values from definitions



126
127
128
# File 'lib/graphql/stitching/request.rb', line 126

def variables
  @variables || with_prepared_document { @variables }
end