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.



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
# File 'lib/graphql/stitching/request.rb', line 38

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)
  @warden = @query.warden
  @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

#wardenGraphQL::Schema::Warden (readonly)

Returns a visibility warden for this request.

Returns:

  • (GraphQL::Schema::Warden)

    a visibility warden for this request.



30
31
32
# File 'lib/graphql/stitching/request.rb', line 30

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



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

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.



191
192
193
194
# File 'lib/graphql/stitching/request.rb', line 191

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.



135
136
137
138
139
# File 'lib/graphql/stitching/request.rb', line 135

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



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

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.



82
83
84
# File 'lib/graphql/stitching/request.rb', line 82

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.



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

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.



87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/graphql/stitching/request.rb', line 87

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.



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

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.



179
180
181
182
183
184
185
186
# File 'lib/graphql/stitching/request.rb', line 179

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.



149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
# File 'lib/graphql/stitching/request.rb', line 149

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



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

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.



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

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



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

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



143
144
145
146
# File 'lib/graphql/stitching/request.rb', line 143

def validate
  result = @supergraph.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.



128
129
130
131
132
# File 'lib/graphql/stitching/request.rb', line 128

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