Class: GraphQL::Stitching::Request

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

Constant Summary collapse

SUPPORTED_OPERATIONS =
["query", "mutation"].freeze
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.



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

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.



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

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.



13
14
15
# File 'lib/graphql/stitching/request.rb', line 13

def document
  @document
end

#operation_nameString (readonly)

Returns operation name selected for the request.

Returns:

  • (String)

    operation name selected for the request.



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

def operation_name
  @operation_name
end

#supergraphSupergraph (readonly)

Returns supergraph instance that resolves the request.

Returns:

  • (Supergraph)

    supergraph instance that resolves the request.



10
11
12
# File 'lib/graphql/stitching/request.rb', line 10

def supergraph
  @supergraph
end

#variablesHash (readonly)

Returns input variables for the request.

Returns:

  • (Hash)

    input variables for the request.



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

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.



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

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.



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

def digest
  @digest ||= Digest::SHA2.hexdigest(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.



172
173
174
# File 'lib/graphql/stitching/request.rb', line 172

def execute(raw: false)
  GraphQL::Stitching::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.



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

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

#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.



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

def normalized_digest
  @normalized_digest ||= Digest::SHA2.hexdigest(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.



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

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.



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

def operation
  @operation ||= begin
    operation_defs = @document.definitions.select do |d|
      next unless d.is_a?(GraphQL::Language::Nodes::OperationDefinition)
      next unless SUPPORTED_OPERATIONS.include?(d.operation_type)
      @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.



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

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.



160
161
162
163
164
165
166
167
# File 'lib/graphql/stitching/request.rb', line 160

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 ||= GraphQL::Stitching::Planner.new(self).perform
  end
end

#prepare!Object

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



130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
# File 'lib/graphql/stitching/request.rb', line 130

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

#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.



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

def string
  @string || normalized_string
end

#validateArray<GraphQL::ExecutionError>

Validates the request using the combined supergraph schema.

Returns:

  • (Array<GraphQL::ExecutionError>)

    an array of static validation errors



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

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.



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

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