artemis 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +10 -0
- data/.rspec +2 -0
- data/.travis.yml +36 -0
- data/Appraisals +32 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +10 -0
- data/LICENSE.txt +21 -0
- data/README.md +181 -0
- data/Rakefile +14 -0
- data/artemis.gemspec +28 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/gemfiles/.bundle/config +2 -0
- data/gemfiles/rails_42.gemfile +12 -0
- data/gemfiles/rails_50.gemfile +11 -0
- data/gemfiles/rails_51.gemfile +11 -0
- data/gemfiles/rails_52.gemfile +11 -0
- data/gemfiles/rails_edge.gemfile +14 -0
- data/lib/artemis.rb +3 -0
- data/lib/artemis/adapters.rb +25 -0
- data/lib/artemis/adapters/abstract_adapter.rb +46 -0
- data/lib/artemis/adapters/curb_adapter.rb +56 -0
- data/lib/artemis/adapters/net_http_adapter.rb +51 -0
- data/lib/artemis/adapters/net_http_persistent_adapter.rb +46 -0
- data/lib/artemis/adapters/test_adapter.rb +29 -0
- data/lib/artemis/client.rb +245 -0
- data/lib/artemis/exceptions.rb +19 -0
- data/lib/artemis/graphql_endpoint.rb +54 -0
- data/lib/artemis/railtie.rb +50 -0
- data/lib/artemis/version.rb +3 -0
- data/lib/generators/artemis/USAGE +11 -0
- data/lib/generators/artemis/install_generator.rb +59 -0
- data/lib/generators/artemis/templates/.gitkeep +0 -0
- data/lib/generators/artemis/templates/client.rb +10 -0
- data/lib/generators/artemis/templates/graphql.yml +19 -0
- data/lib/tasks/artemis.rake +47 -0
- data/spec/adapters_spec.rb +106 -0
- data/spec/autoloading_spec.rb +146 -0
- data/spec/callbacks_spec.rb +46 -0
- data/spec/client_spec.rb +161 -0
- data/spec/endpoint_spec.rb +45 -0
- data/spec/fixtures/metaphysics.rb +2 -0
- data/spec/fixtures/metaphysics/_artist_fragment.graphql +4 -0
- data/spec/fixtures/metaphysics/artist.graphql +7 -0
- data/spec/fixtures/metaphysics/artists.graphql +8 -0
- data/spec/fixtures/metaphysics/artwork.graphql +8 -0
- data/spec/fixtures/metaphysics/schema.json +49811 -0
- data/spec/spec_helper.rb +48 -0
- metadata +219 -0
@@ -0,0 +1,12 @@
|
|
1
|
+
# This file was generated by Appraisal
|
2
|
+
|
3
|
+
source "https://rubygems.org"
|
4
|
+
|
5
|
+
gem "pry"
|
6
|
+
gem "pry-byebug", platforms: :mri
|
7
|
+
gem "rails", "~> 4.2"
|
8
|
+
gem "railties", "~> 4.2"
|
9
|
+
gem "activesupport", "~> 4.2"
|
10
|
+
gem "minitest", "5.10.3"
|
11
|
+
|
12
|
+
gemspec path: "../"
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# This file was generated by Appraisal
|
2
|
+
|
3
|
+
source "https://rubygems.org"
|
4
|
+
|
5
|
+
git "git://github.com/rails/rails.git" do
|
6
|
+
gem "rails"
|
7
|
+
gem "railties"
|
8
|
+
gem "activesupport"
|
9
|
+
end
|
10
|
+
|
11
|
+
gem "pry"
|
12
|
+
gem "pry-byebug", platforms: :mri
|
13
|
+
|
14
|
+
gemspec path: "../"
|
data/lib/artemis.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
|
3
|
+
require 'active_support/dependencies/autoload'
|
4
|
+
|
5
|
+
module Artemis
|
6
|
+
module Adapters
|
7
|
+
extend ActiveSupport::Autoload
|
8
|
+
|
9
|
+
autoload :CurbAdapter
|
10
|
+
autoload :NetHttpAdapter
|
11
|
+
autoload :NetHttpPersistentAdapter
|
12
|
+
autoload :TestAdapter
|
13
|
+
|
14
|
+
class << self
|
15
|
+
##
|
16
|
+
# Returns the constant for the specified adapter name.
|
17
|
+
#
|
18
|
+
# Artemis::Adapters.lookup(:net_http)
|
19
|
+
# # => Artemis::Adapters::NetHttpAdapter
|
20
|
+
def lookup(name)
|
21
|
+
const_get("#{name.to_s.camelize}Adapter")
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'graphql/client/http'
|
4
|
+
|
5
|
+
module Artemis
|
6
|
+
module Adapters
|
7
|
+
class AbstractAdapter < ::GraphQL::Client::HTTP
|
8
|
+
attr_reader :service_name, :timeout, :pool_size
|
9
|
+
|
10
|
+
EMPTY_HEADERS = {}.freeze
|
11
|
+
|
12
|
+
def initialize(uri, service_name: , timeout: , pool_size: )
|
13
|
+
super(uri) # Do not pass in the block to avoid getting #headers and #connection overridden.
|
14
|
+
|
15
|
+
@service_name = service_name.to_s
|
16
|
+
@timeout = timeout
|
17
|
+
@pool_size = pool_size
|
18
|
+
end
|
19
|
+
|
20
|
+
# Public: Extension point for subclasses to set custom request headers.
|
21
|
+
#
|
22
|
+
# Returns Hash of String header names and values.
|
23
|
+
def headers(_context)
|
24
|
+
_context[:headers] || EMPTY_HEADERS
|
25
|
+
end
|
26
|
+
|
27
|
+
# Public: Make an HTTP request for GraphQL query.
|
28
|
+
#
|
29
|
+
# A subclass that inherits from +AbstractAdapter+ can override this method if it needs more flexibility for how
|
30
|
+
# it makes a request.
|
31
|
+
#
|
32
|
+
# For more details, see +GraphQL::Client::HTTP#execute+.
|
33
|
+
def execute(*)
|
34
|
+
super
|
35
|
+
end
|
36
|
+
|
37
|
+
# Public: Extension point for subclasses to customize the Net:HTTP client
|
38
|
+
#
|
39
|
+
# A subclass that inherits from +AbstractAdapter+ should returns a Net::HTTP object or an object that responds
|
40
|
+
# to +request+ that is given a Net::HTTP request object.
|
41
|
+
def connection
|
42
|
+
raise "AbstractAdapter is an abstract class that can not be instantiated!"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'delegate'
|
4
|
+
require 'json'
|
5
|
+
|
6
|
+
require 'curb'
|
7
|
+
|
8
|
+
require 'artemis/adapters/abstract_adapter'
|
9
|
+
require 'artemis/exceptions'
|
10
|
+
|
11
|
+
module Artemis
|
12
|
+
module Adapters
|
13
|
+
class CurbAdapter < AbstractAdapter
|
14
|
+
attr_reader :multi
|
15
|
+
|
16
|
+
def initialize(uri, service_name: , timeout: , pool_size: )
|
17
|
+
super
|
18
|
+
|
19
|
+
@multi = Curl::Multi.new
|
20
|
+
@multi.pipeline = Curl::CURLPIPE_MULTIPLEX if defined?(Curl::CURLPIPE_MULTIPLEX)
|
21
|
+
end
|
22
|
+
|
23
|
+
def execute(document:, operation_name: nil, variables: {}, context: {})
|
24
|
+
easy = Curl::Easy.new(uri.to_s)
|
25
|
+
|
26
|
+
body = {}
|
27
|
+
body["query"] = document.to_query_string
|
28
|
+
body["variables"] = variables if variables.any?
|
29
|
+
body["operationName"] = operation_name if operation_name
|
30
|
+
|
31
|
+
easy.timeout = timeout
|
32
|
+
easy.multi = multi
|
33
|
+
easy.headers = headers(context) || {}
|
34
|
+
easy.post_body = JSON.generate(body)
|
35
|
+
|
36
|
+
if defined?(Curl::CURLPIPE_MULTIPLEX)
|
37
|
+
# This ensures libcurl waits for the connection to reveal if it is
|
38
|
+
# possible to pipeline/multiplex on before it continues.
|
39
|
+
easy.setopt(Curl::CURLOPT_PIPEWAIT, 1)
|
40
|
+
easy.version = Curl::HTTP_2_0
|
41
|
+
end
|
42
|
+
|
43
|
+
easy.http_post
|
44
|
+
|
45
|
+
case easy.response_code
|
46
|
+
when 200, 400
|
47
|
+
JSON.parse(easy.body)
|
48
|
+
when 500..599
|
49
|
+
raise Artemis::GraphQLServerError, "Received server error status #{easy.response_code}: #{easy.body}"
|
50
|
+
else
|
51
|
+
{ "errors" => [{ "message" => "#{easy.response_code} #{easy.body}" }] }
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
require 'net/http'
|
5
|
+
|
6
|
+
require 'artemis/adapters/abstract_adapter'
|
7
|
+
require 'artemis/exceptions'
|
8
|
+
|
9
|
+
module Artemis
|
10
|
+
module Adapters
|
11
|
+
class NetHttpAdapter < AbstractAdapter
|
12
|
+
# Makes an HTTP request for GraphQL query.
|
13
|
+
def execute(document:, operation_name: nil, variables: {}, context: {})
|
14
|
+
request = Net::HTTP::Post.new(uri.request_uri)
|
15
|
+
|
16
|
+
request.basic_auth(uri.user, uri.password) if uri.user || uri.password
|
17
|
+
|
18
|
+
request["Accept"] = "application/json"
|
19
|
+
request["Content-Type"] = "application/json"
|
20
|
+
headers(context).each { |name, value| request[name] = value }
|
21
|
+
|
22
|
+
body = {}
|
23
|
+
body["query"] = document.to_query_string
|
24
|
+
body["variables"] = variables if variables.any?
|
25
|
+
body["operationName"] = operation_name if operation_name
|
26
|
+
request.body = JSON.generate(body)
|
27
|
+
|
28
|
+
response = connection.request(request)
|
29
|
+
|
30
|
+
case response.code.to_i
|
31
|
+
when 200, 400
|
32
|
+
JSON.parse(response.body)
|
33
|
+
when 500..599
|
34
|
+
raise Artemis::GraphQLServerError, "Received server error status #{response.code}: #{response.body}"
|
35
|
+
else
|
36
|
+
{ "errors" => [{ "message" => "#{response.code} #{response.message}" }] }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# Returns a fresh Net::HTTP object that creates a new connection.
|
41
|
+
def connection
|
42
|
+
Net::HTTP.new(uri.host, uri.port).tap do |client|
|
43
|
+
client.use_ssl = uri.scheme == "https"
|
44
|
+
client.open_timeout = timeout
|
45
|
+
client.read_timeout = timeout
|
46
|
+
client.write_timeout = timeout if client.respond_to?(:write_timeout=)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'delegate'
|
4
|
+
|
5
|
+
require 'net/http/persistent'
|
6
|
+
|
7
|
+
require 'artemis/adapters/net_http_adapter'
|
8
|
+
|
9
|
+
module Artemis
|
10
|
+
module Adapters
|
11
|
+
class NetHttpPersistentAdapter < NetHttpAdapter
|
12
|
+
attr_reader :_connection, :raw_connection
|
13
|
+
|
14
|
+
def initialize(uri, service_name: , timeout: , pool_size: )
|
15
|
+
super
|
16
|
+
|
17
|
+
@raw_connection = Net::HTTP::Persistent.new(name: service_name, pool_size: pool_size)
|
18
|
+
@raw_connection.open_timeout = timeout
|
19
|
+
@raw_connection.read_timeout = timeout
|
20
|
+
|
21
|
+
@_connection = ConnectionWrapper.new(@raw_connection, uri)
|
22
|
+
end
|
23
|
+
|
24
|
+
# Public: Extension point for subclasses to customize the Net:HTTP client
|
25
|
+
#
|
26
|
+
# Returns a Net::HTTP object
|
27
|
+
def connection
|
28
|
+
_connection
|
29
|
+
end
|
30
|
+
|
31
|
+
class ConnectionWrapper < SimpleDelegator
|
32
|
+
def initialize(obj, url)
|
33
|
+
super(obj)
|
34
|
+
|
35
|
+
@url = url
|
36
|
+
end
|
37
|
+
|
38
|
+
def request(req)
|
39
|
+
__getobj__.request(@url, req)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
private_constant :ConnectionWrapper
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_support/core_ext/module/attribute_accessors'
|
4
|
+
|
5
|
+
module Artemis
|
6
|
+
module Adapters
|
7
|
+
class TestAdapter
|
8
|
+
cattr_accessor :requests
|
9
|
+
self.requests = []
|
10
|
+
|
11
|
+
Request = Struct.new(:document, :operation_name, :variables, :context)
|
12
|
+
|
13
|
+
private_constant :Request
|
14
|
+
|
15
|
+
def initialize(*)
|
16
|
+
end
|
17
|
+
|
18
|
+
def execute(**arguments)
|
19
|
+
self.requests << Request.new(*arguments.values_at(:document, :operation_name, :variables, :context))
|
20
|
+
|
21
|
+
{
|
22
|
+
'data' => { 'test' => 'data' },
|
23
|
+
'errors' => [],
|
24
|
+
'extensions' => {}
|
25
|
+
}
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,245 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'delegate'
|
4
|
+
|
5
|
+
require 'active_support/configurable'
|
6
|
+
require 'active_support/core_ext/hash/deep_merge'
|
7
|
+
require 'active_support/core_ext/string/inflections'
|
8
|
+
|
9
|
+
require 'artemis/graphql_endpoint'
|
10
|
+
require 'artemis/exceptions'
|
11
|
+
|
12
|
+
module Artemis
|
13
|
+
class Client
|
14
|
+
include ActiveSupport::Configurable
|
15
|
+
|
16
|
+
# The paths in which the Artemis client looks for files that have the +.graphql+ extension.
|
17
|
+
# In a rails app, this value will be set to +["app/operations"]+ by Artemis' +Artemis::Railtie+.
|
18
|
+
config.query_paths = []
|
19
|
+
|
20
|
+
# Default context that is appended to every GraphQL request for the client.
|
21
|
+
config.default_context = {}
|
22
|
+
|
23
|
+
# List of before callbacks that get invoked in every +execute+ call.
|
24
|
+
#
|
25
|
+
# @api private
|
26
|
+
config.before_callbacks = []
|
27
|
+
|
28
|
+
# List of after callbacks that get invoked in every +execute+ call.
|
29
|
+
#
|
30
|
+
# @api private
|
31
|
+
config.after_callbacks = []
|
32
|
+
|
33
|
+
# Returns a plain +GraphQL::Client+ object. For more details please refer to the official documentation for
|
34
|
+
# {the +graphql-client+ gem}[https://github.com/github/graphql-client].
|
35
|
+
attr_reader :client
|
36
|
+
|
37
|
+
# Creates a new instance of the GraphQL client for the service.
|
38
|
+
#
|
39
|
+
# # app/operations/github/user.graphql
|
40
|
+
# query($id: String!) {
|
41
|
+
# user(login: $id) {
|
42
|
+
# name
|
43
|
+
# }
|
44
|
+
# }
|
45
|
+
#
|
46
|
+
# # app/operations/github.rb
|
47
|
+
# class GitHub < Artemis::Client
|
48
|
+
# end
|
49
|
+
#
|
50
|
+
# github = GitHub.new
|
51
|
+
# github.user(id: 'yuki24').data.user.name # => "Yuki Nishijima"
|
52
|
+
#
|
53
|
+
# github = GitHub.new(context: { headers: { Authorization: "bearer ..." } })
|
54
|
+
# github.user(id: 'yuki24').data.user.name # => "Yuki Nishijima"
|
55
|
+
#
|
56
|
+
def initialize(context = {})
|
57
|
+
@client = self.class.instantiate_client(context)
|
58
|
+
end
|
59
|
+
|
60
|
+
class << self
|
61
|
+
delegate :query_paths, :default_context, :query_paths=, :default_context=, to: :config
|
62
|
+
|
63
|
+
alias with_context new
|
64
|
+
|
65
|
+
def endpoint
|
66
|
+
Artemis::GraphQLEndpoint.lookup(name)
|
67
|
+
end
|
68
|
+
|
69
|
+
def instantiate_client(context = {})
|
70
|
+
::GraphQL::Client.new(schema: endpoint.schema, execute: connection(context))
|
71
|
+
end
|
72
|
+
|
73
|
+
# Defines a callback that will get called right before the
|
74
|
+
# client's execute method is executed.
|
75
|
+
#
|
76
|
+
# class GitHub < Artemis::Client
|
77
|
+
#
|
78
|
+
# before_execute do |document, operation_name, variables, context|
|
79
|
+
# Analytics.log(operation_name, variables, context[:user_id])
|
80
|
+
# end
|
81
|
+
#
|
82
|
+
# ...
|
83
|
+
# end
|
84
|
+
#
|
85
|
+
def before_execute(&block)
|
86
|
+
config.before_callbacks << block
|
87
|
+
end
|
88
|
+
|
89
|
+
# Defines a callback that will get called right after the
|
90
|
+
# client's execute method has finished.
|
91
|
+
#
|
92
|
+
# class GitHub < Artemis::Client
|
93
|
+
#
|
94
|
+
# after_execute do |data, errors, extensions|
|
95
|
+
# if errors.present?
|
96
|
+
# Rails.logger.error(errors.to_json)
|
97
|
+
# end
|
98
|
+
# end
|
99
|
+
#
|
100
|
+
# ...
|
101
|
+
# end
|
102
|
+
#
|
103
|
+
def after_execute(&block)
|
104
|
+
config.after_callbacks << block
|
105
|
+
end
|
106
|
+
|
107
|
+
def resolve_graphql_file_path(filename, fragment: false)
|
108
|
+
namespace = name.underscore
|
109
|
+
filename = filename.to_s.underscore
|
110
|
+
|
111
|
+
graphql_file_paths.detect do |path|
|
112
|
+
path.end_with?("#{namespace}/#{filename}.graphql") ||
|
113
|
+
(fragment && filename.end_with?('fragment') && path.end_with?("#{namespace}/_#{filename}.graphql"))
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def graphql_file_paths
|
118
|
+
@graphql_file_paths ||= query_paths.flat_map {|path| Dir["#{path}/#{name.underscore}/*.graphql"] }
|
119
|
+
end
|
120
|
+
|
121
|
+
def preload!
|
122
|
+
graphql_file_paths.each do |path|
|
123
|
+
load_constant(File.basename(path, File.extname(path)).camelize)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def load_constant(const_name)
|
128
|
+
graphql_file = resolve_graphql_file_path(const_name.to_s.underscore, fragment: true)
|
129
|
+
|
130
|
+
if graphql_file
|
131
|
+
graphql = File.open(graphql_file).read
|
132
|
+
ast = instantiate_client.parse(graphql)
|
133
|
+
|
134
|
+
const_set(const_name, ast)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
alias load_query load_constant
|
138
|
+
|
139
|
+
# @api private
|
140
|
+
def connection(context = {})
|
141
|
+
Executor.new(endpoint.connection, callbacks, default_context.deep_merge(context))
|
142
|
+
end
|
143
|
+
|
144
|
+
private
|
145
|
+
|
146
|
+
# @api private
|
147
|
+
def const_missing(const_name)
|
148
|
+
load_constant(const_name) || super
|
149
|
+
end
|
150
|
+
|
151
|
+
# @api private
|
152
|
+
def method_missing(method_name, *arguments, &block)
|
153
|
+
if resolve_graphql_file_path(method_name)
|
154
|
+
new(default_context).public_send(method_name, *arguments, &block)
|
155
|
+
else
|
156
|
+
super
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
# @api private
|
161
|
+
def respond_to_missing?(method_name, *_, &block)
|
162
|
+
resolve_graphql_file_path(method_name) || super
|
163
|
+
end
|
164
|
+
|
165
|
+
Callbacks = Struct.new(:before_callbacks, :after_callbacks)
|
166
|
+
|
167
|
+
private_constant :Callbacks
|
168
|
+
|
169
|
+
# @api private
|
170
|
+
def callbacks
|
171
|
+
Callbacks.new(config.before_callbacks, config.after_callbacks)
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
private
|
176
|
+
|
177
|
+
# @api private
|
178
|
+
def method_missing(method_name, context: {}, **arguments)
|
179
|
+
if self.class.resolve_graphql_file_path(method_name)
|
180
|
+
const_name = method_name.to_s.camelize
|
181
|
+
|
182
|
+
# This check will be unnecessary once we drop support for Ruby 2.4 and earlier
|
183
|
+
if !self.class.const_get(const_name).is_a?(GraphQL::Client::OperationDefinition)
|
184
|
+
self.class.load_constant(const_name)
|
185
|
+
end
|
186
|
+
|
187
|
+
client.query(
|
188
|
+
self.class.const_get(const_name),
|
189
|
+
variables: arguments.deep_transform_keys {|key| key.to_s.camelize(:lower) },
|
190
|
+
context: context
|
191
|
+
)
|
192
|
+
else
|
193
|
+
super
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
# @api private
|
198
|
+
def respond_to_missing?(method_name, *_, &block)
|
199
|
+
self.class.resolve_graphql_file_path(method_name) || super
|
200
|
+
end
|
201
|
+
|
202
|
+
# @api private
|
203
|
+
def compile_query_method!(method_name)
|
204
|
+
const_name = method_name.to_s.camelize
|
205
|
+
|
206
|
+
self.class.send(:class_eval, <<-RUBY, __FILE__, __LINE__ + 1)
|
207
|
+
def #{method_name}(context: {}, **arguments)
|
208
|
+
client.query(
|
209
|
+
self.class::#{const_name},
|
210
|
+
variables: arguments.deep_transform_keys {|key| key.to_s.camelize(:lower) },
|
211
|
+
context: context
|
212
|
+
)
|
213
|
+
end
|
214
|
+
RUBY
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
# @api private
|
219
|
+
class Executor < SimpleDelegator
|
220
|
+
def initialize(connection, callbacks, default_context)
|
221
|
+
super(connection)
|
222
|
+
|
223
|
+
@callbacks = callbacks
|
224
|
+
@default_context = default_context
|
225
|
+
end
|
226
|
+
|
227
|
+
def execute(document:, operation_name: nil, variables: {}, context: {})
|
228
|
+
_context = @default_context.deep_merge(context)
|
229
|
+
|
230
|
+
@callbacks.before_callbacks.each do |callback|
|
231
|
+
callback.call(document, operation_name, variables, _context)
|
232
|
+
end
|
233
|
+
|
234
|
+
response = __getobj__.execute(document: document, operation_name: operation_name, variables: variables, context: _context)
|
235
|
+
|
236
|
+
@callbacks.after_callbacks.each do |callback|
|
237
|
+
callback.call(response['data'], response['errors'], response['extensions'])
|
238
|
+
end
|
239
|
+
|
240
|
+
response
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
private_constant :Executor
|
245
|
+
end
|