Class: GraphQL::Stitching::HttpExecutable

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

Instance Method Summary collapse

Constructor Details

#initialize(url:, headers: {}, upload_types: nil) ⇒ HttpExecutable

Builds a new executable for proxying subgraph requests via HTTP.

Parameters:

  • url (String)

    the url of the remote location to proxy.

  • headers (Hash) (defaults to: {})

    headers to include in upstream requests.

  • upload_types (Array<String>, nil) (defaults to: nil)

    a list of scalar names that represent file uploads. These types extract into multipart forms.

[View source]

14
15
16
17
18
# File 'lib/graphql/stitching/http_executable.rb', line 14

def initialize(url:, headers: {}, upload_types: nil)
  @url = url
  @headers = { "Content-Type" => "application/json" }.merge!(headers)
  @upload_types = upload_types
end

Instance Method Details

#call(request, document, variables) ⇒ Object

[View source]

20
21
22
23
24
25
26
27
28
29
30
# File 'lib/graphql/stitching/http_executable.rb', line 20

def call(request, document, variables)
  form_data = extract_multipart_form(request, document, variables)

  response = if form_data
    send_multipart_form(request, form_data)
  else
    send(request, document, variables)
  end

  JSON.parse(response.body)
end

#extract_multipart_form(request, document, variables) ⇒ Object

Extracts multipart upload forms per the spec: https://github.com/jaydenseric/graphql-multipart-request-spec

Parameters:

  • request (Request)

    the original supergraph request.

  • document (String)

    the location-specific subgraph document to send.

  • variables (Hash)

    a hash of variables specific to the subgraph document.

[View source]

65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/graphql/stitching/http_executable.rb', line 65

def extract_multipart_form(request, document, variables)
  return unless @upload_types && request.variable_definitions.any? && variables&.any?

  files_by_path = {}

  # extract all upload scalar values mapped by their input path
  variables.each_with_object([]) do |(key, value), path|
    ast_node = request.variable_definitions[key]
    path << key
    extract_ast_node(ast_node, value, files_by_path, path, request) if ast_node
    path.pop
  end

  return if files_by_path.none?

  map = {}
  files = files_by_path.values.tap(&:uniq!)
  variables_copy = variables.dup

  files_by_path.each_key do |path|
    orig = variables
    copy = variables_copy
    path.each_with_index do |key, i|
      if i == path.length - 1
        file_index = files.index(copy[key]).to_s
        map[file_index] ||= []
        map[file_index] << "variables.#{path.join(".")}"
        copy[key] = nil
      elsif orig[key].object_id == copy[key].object_id
        copy[key] = copy[key].dup
      end
      orig = orig[key]
      copy = copy[key]
    end
  end

  form = {
    "operations" => JSON.generate({
      "query" => document,
      "variables" => variables_copy,
    }),
    "map" => JSON.generate(map),
  }

  files.each_with_object(form).with_index do |(file, memo), index|
    memo[index.to_s] = file.respond_to?(:tempfile) ? file.tempfile : file
  end
end

#send(_request, document, variables) ⇒ Object

Sends a POST request to the remote location.

Parameters:

  • request (Request)

    the original supergraph request.

  • document (String)

    the location-specific subgraph document to send.

  • variables (Hash)

    a hash of variables specific to the subgraph document.

[View source]

36
37
38
39
40
41
42
# File 'lib/graphql/stitching/http_executable.rb', line 36

def send(_request, document, variables)
  Net::HTTP.post(
    URI(@url),
    JSON.generate({ "query" => document, "variables" => variables }),
    @headers,
  )
end

#send_multipart_form(_request, form_data) ⇒ Object

Sends a POST request to the remote location with multipart form data.

Parameters:

  • request (Request)

    the original supergraph request.

  • form_data (Hash)

    a rendered multipart form with an "operations", "map", and file sections.

[View source]

47
48
49
50
51
52
53
54
55
56
57
58
# File 'lib/graphql/stitching/http_executable.rb', line 47

def send_multipart_form(_request, form_data)
  uri = URI(@url)
  req = Net::HTTP::Post.new(uri)
  @headers.each_pair do |key, value|
    req[key] = value
  end

  req.set_form(form_data.to_a, "multipart/form-data")
  Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == "https") do |http|
    http.request(req)
  end
end