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, document, operation_name: nil, variables: nil, context: nil) ⇒ Request

Creates a new supergraph request.

Parameters:

  • supergraph (Supergraph)

    supergraph instance that resolves the request.

  • document (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.



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

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

  @document = if document.is_a?(String)
    @string = document
    GraphQL.parse(document)
  else
    document
  end

  @operation_name = operation_name
  @variables = variables || {}

  @query = GraphQL::Query.new(@supergraph.schema, document: @document, context: context)
  @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.



27
28
29
# File 'lib/graphql/stitching/request.rb', line 27

def context
  @context
end

#documentGraphQL::Language::Nodes::Document (readonly)

Returns the parsed GraphQL AST document.

Returns:

  • (GraphQL::Language::Nodes::Document)

    the parsed GraphQL AST document.



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

def document
  @document
end

#operation_nameString (readonly)

Returns operation name selected for the request.

Returns:

  • (String)

    operation name selected for the request.



24
25
26
# File 'lib/graphql/stitching/request.rb', line 24

def operation_name
  @operation_name
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

#variablesHash (readonly)

Returns input variables for the request.

Returns:

  • (Hash)

    input variables for the request.



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

def variables
  @variables
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.



73
74
75
# File 'lib/graphql/stitching/request.rb', line 73

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.



187
188
189
190
# File 'lib/graphql/stitching/request.rb', line 187

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.



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

def fragment_definitions
  @fragment_definitions ||= @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



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

def mutation?
  operation.operation_type == MUTATION_OP
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.



78
79
80
# File 'lib/graphql/stitching/request.rb', line 78

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.



68
69
70
# File 'lib/graphql/stitching/request.rb', line 68

def normalized_string
  @normalized_string ||= @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.



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

def operation
  @operation ||= begin
    operation_defs = @document.definitions.select do |d|
      next unless d.is_a?(GraphQL::Language::Nodes::OperationDefinition)
      @operation_name ? d.name == @operation_name : true
    end

    if operation_defs.length < 1
      raise GraphQL::ExecutionError, "Invalid root operation for given name and operation type."
    elsif operation_defs.length > 1
      raise GraphQL::ExecutionError, "An operation name is required when sending multiple operations."
    end

    operation_defs.first
  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.



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

def operation_directives
  @operation_directives ||= if operation.directives.any?
    printer = GraphQL::Language::Printer.new
    operation.directives.map { printer.print(_1) }.join(" ")
  end
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.



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

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

#prepare!Object

Prepares the request for stitching by inserting variable defaults and applying @skip/@include conditionals.



145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# File 'lib/graphql/stitching/request.rb', line 145

def prepare!
  operation.variables.each do |v|
    @variables[v.name] = v.default_value if @variables[v.name].nil? && !v.default_value.nil?
  end

  if @string.nil? || @string.match?(SKIP_INCLUDE_DIRECTIVE)
    SkipInclude.render(@document, @variables) do |modified_ast|
      @document = modified_ast
      @string = @normalized_string = nil
      @digest = @normalized_digest = nil
      @operation = @operation_directives = @variable_definitions = @plan = nil
    end
  end

  self
end

#query?Boolean

Returns true if operation type is a query.

Returns:

  • (Boolean)

    true if operation type is a query



101
102
103
# File 'lib/graphql/stitching/request.rb', line 101

def query?
  operation.operation_type == QUERY_OP
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.



63
64
65
# File 'lib/graphql/stitching/request.rb', line 63

def string
  @string || normalized_string
end

#subscription?Boolean

Returns true if operation type is a subscription.

Returns:

  • (Boolean)

    true if operation type is a subscription



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

def subscription?
  operation.operation_type == SUBSCRIPTION_OP
end

#validateArray<GraphQL::ExecutionError>

Validates the request using the combined supergraph schema.

Returns:

  • (Array<GraphQL::ExecutionError>)

    an array of static validation errors



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

def validate
  result = @supergraph.schema.static_validator.validate(@query)
  result[: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.



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

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