graphlient 0.8.0 → 0.9.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 +4 -4
- data/.github/ISSUE_TEMPLATE/bug_report.md +38 -38
- data/.github/ISSUE_TEMPLATE/feature_request.md +20 -20
- data/.github/workflows/ci.yml +33 -30
- data/.github/workflows/danger-comment.yml +10 -0
- data/.github/workflows/danger.yml +13 -22
- data/.github/workflows/rubocop.yml +19 -19
- data/.gitignore +59 -52
- data/.rspec +1 -1
- data/.rubocop.yml +26 -28
- data/.rubocop_todo.yml +72 -48
- data/CHANGELOG.md +128 -113
- data/CONTRIBUTING.md +125 -125
- data/Dangerfile +24 -24
- data/Gemfile +35 -27
- data/LICENSE +21 -21
- data/README.md +800 -533
- data/RELEASING.md +63 -63
- data/Rakefile +15 -15
- data/UPGRADING.md +23 -23
- data/graphlient.gemspec +19 -19
- data/lib/graphlient/adapters/http/adapter.rb +41 -41
- data/lib/graphlient/adapters/http/faraday_adapter.rb +79 -79
- data/lib/graphlient/adapters/http/http_adapter.rb +40 -39
- data/lib/graphlient/adapters/http.rb +3 -3
- data/lib/graphlient/adapters.rb +1 -1
- data/lib/graphlient/client.rb +98 -80
- data/lib/graphlient/errors/client_error.rb +6 -6
- data/lib/graphlient/errors/connection_failed_error.rb +6 -6
- data/lib/graphlient/errors/error.rb +13 -12
- data/lib/graphlient/errors/execution_error.rb +29 -29
- data/lib/graphlient/errors/faraday_server_error.rb +12 -12
- data/lib/graphlient/errors/graphql_error.rb +53 -52
- data/lib/graphlient/errors/http_options_error.rb +6 -6
- data/lib/graphlient/errors/http_server_error.rb +13 -13
- data/lib/graphlient/errors/server_error.rb +7 -7
- data/lib/graphlient/errors/timeout_error.rb +6 -6
- data/lib/graphlient/errors.rb +10 -10
- data/lib/graphlient/extensions/query.rb +15 -15
- data/lib/graphlient/extensions.rb +1 -1
- data/lib/graphlient/query/directive.rb +47 -0
- data/lib/graphlient/query/serializer/arguments.rb +67 -0
- data/lib/graphlient/query/serializer/directives.rb +17 -0
- data/lib/graphlient/query/serializer/evaluator.rb +15 -0
- data/lib/graphlient/query/serializer/fragments.rb +28 -0
- data/lib/graphlient/query/serializer/scalars.rb +63 -0
- data/lib/graphlient/query/serializer.rb +153 -0
- data/lib/graphlient/query.rb +29 -128
- data/lib/graphlient/schema.rb +27 -26
- data/lib/graphlient/version.rb +3 -3
- data/lib/graphlient.rb +8 -8
- data/spec/graphlient/adapters/http/faraday_adapter_spec.rb +172 -167
- data/spec/graphlient/adapters/http/http_adapter_spec.rb +60 -60
- data/spec/graphlient/client_dsl_spec.rb +153 -0
- data/spec/graphlient/client_query_spec.rb +369 -346
- data/spec/graphlient/client_schema_spec.rb +75 -75
- data/spec/graphlient/extensions/query_spec.rb +16 -16
- data/spec/graphlient/github_query_spec.rb +32 -32
- data/spec/graphlient/query_dsl_spec.rb +231 -0
- data/spec/graphlient/query_spec.rb +105 -105
- data/spec/graphlient/schema_spec.rb +56 -56
- data/spec/graphlient/static_client_query_spec.rb +75 -68
- data/spec/graphlient/webmock_client_query_spec.rb +41 -41
- data/spec/spec_helper.rb +26 -14
- data/spec/support/context/dummy_client.rb +33 -26
- data/spec/support/context/github_client.rb +18 -18
- data/spec/support/dummy_app.rb +21 -19
- data/spec/support/dummy_schema.rb +17 -17
- data/spec/support/fixtures/github/schema.yml +14282 -14282
- data/spec/support/fixtures/github/user.yml +76 -76
- data/spec/support/fixtures/github/viewer.yml +76 -76
- data/spec/support/fixtures/invoice_api.json +1288 -1288
- data/spec/support/mutations/create_invoice.rb +18 -18
- data/spec/support/queries/query.rb +48 -47
- data/spec/support/schema/github.json +44479 -44479
- data/spec/support/types/invoice_type.rb +13 -13
- data/spec/support/types/mutation_type.rb +5 -5
- data/spec/support/vcr.rb +9 -9
- metadata +13 -8
- data/Gemfile.danger +0 -5
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
module Graphlient
|
|
2
|
+
class Query
|
|
3
|
+
class Serializer
|
|
4
|
+
module Arguments
|
|
5
|
+
private
|
|
6
|
+
|
|
7
|
+
def variable_processor
|
|
8
|
+
@variable_processor ||= ->(k, v) { "$#{k}: #{variable_string(v)}" }
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# Translate a DSL variable type symbol to its GraphQL type string.
|
|
12
|
+
# :id/:id! -> ID/ID! :int -> Int :date -> Date (if registered) [:int] -> [Int]
|
|
13
|
+
def variable_string(val)
|
|
14
|
+
case val
|
|
15
|
+
when :id, :id!
|
|
16
|
+
val.to_s.upcase
|
|
17
|
+
when ->(v) { scalar?(v) }
|
|
18
|
+
scalar_type(val)
|
|
19
|
+
when Array
|
|
20
|
+
"[#{variable_string(val.first)}]"
|
|
21
|
+
else
|
|
22
|
+
val.to_s
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Separate Directive objects from regular field arguments.
|
|
27
|
+
def field_args(args)
|
|
28
|
+
args.reject { |a| a.is_a?(Directive) }
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def directive_args(args)
|
|
32
|
+
args.select { |a| a.is_a?(Directive) }
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def hash_arg(args)
|
|
36
|
+
args.detect { |arg| arg.is_a?(Hash) }
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def args_str(hash_args, arg_processor: nil)
|
|
40
|
+
hash_args.map do |k, v|
|
|
41
|
+
arg_processor ? arg_processor.call(k, v) : argument_string(k, v)
|
|
42
|
+
end.join(', ')
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def argument_string(key, val)
|
|
46
|
+
"#{key}: #{argument_value_string(val)}"
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def argument_value_string(value)
|
|
50
|
+
case value
|
|
51
|
+
when String then "\"#{value}\""
|
|
52
|
+
when Numeric then value.to_s
|
|
53
|
+
when Array then "[#{value.map { |v| argument_value_string(v) }.join(', ')}]"
|
|
54
|
+
when Hash then "{ #{value.map { |k, v| "#{k}: #{argument_value_string(v)}" }.join(', ')} }"
|
|
55
|
+
when Symbol then symbol_argument_value(value)
|
|
56
|
+
else
|
|
57
|
+
value
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def symbol_argument_value(value)
|
|
62
|
+
@variables.respond_to?(:key?) && @variables.key?(value) ? "$#{value}" : value.to_s.camelize(:lower)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
module Graphlient
|
|
2
|
+
class Query
|
|
3
|
+
class Serializer
|
|
4
|
+
module Directives
|
|
5
|
+
# Matches _skip, _include, _myCustomDirective etc.
|
|
6
|
+
# Does NOT match ___Fragment (three underscores -- checked first in method_missing).
|
|
7
|
+
DIRECTIVE_PREFIX = /\A_[a-z]/.freeze
|
|
8
|
+
|
|
9
|
+
private
|
|
10
|
+
|
|
11
|
+
def directive?(method_name)
|
|
12
|
+
method_name.to_s.match?(DIRECTIVE_PREFIX)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module Graphlient
|
|
2
|
+
class Query
|
|
3
|
+
class Serializer
|
|
4
|
+
module Evaluator
|
|
5
|
+
private
|
|
6
|
+
|
|
7
|
+
def evaluate(&block)
|
|
8
|
+
@last_block = block || self
|
|
9
|
+
(@context ||= {})[@last_block] ||= @last_block.binding
|
|
10
|
+
instance_eval(&block)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
module Graphlient
|
|
2
|
+
class Query
|
|
3
|
+
class Serializer
|
|
4
|
+
module Fragments
|
|
5
|
+
# Pattern for the legacy ___Const::Name triple-underscore convention.
|
|
6
|
+
FRAGMENT_DEFINITION = /___(?<const>[A-Z][a-zA-Z0-9_]*(?:__[A-Z][a-zA-Z0-9_]*)*)/.freeze
|
|
7
|
+
|
|
8
|
+
private
|
|
9
|
+
|
|
10
|
+
def fragment?(method_name)
|
|
11
|
+
method_name.to_s.start_with?('___')
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def resolve_fragment_constant(value)
|
|
15
|
+
return nil unless (match = value.to_s.match(FRAGMENT_DEFINITION))
|
|
16
|
+
|
|
17
|
+
raw_const = match[:const].gsub('__', '::')
|
|
18
|
+
@context[@last_block].eval(raw_const).tap do |const|
|
|
19
|
+
next if const.is_a?(GraphQL::Client::FragmentDefinition)
|
|
20
|
+
|
|
21
|
+
msg = "Expected constant #{raw_const} to be GraphQL::Client::FragmentDefinition. Given #{const.class}"
|
|
22
|
+
raise Graphlient::Errors::Error, msg
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
module Graphlient
|
|
2
|
+
class Query
|
|
3
|
+
class Serializer
|
|
4
|
+
module Scalars
|
|
5
|
+
BUILT_IN_SCALAR_TYPES = {
|
|
6
|
+
int: 'Int',
|
|
7
|
+
float: 'Float',
|
|
8
|
+
string: 'String',
|
|
9
|
+
boolean: 'Boolean'
|
|
10
|
+
}.freeze
|
|
11
|
+
|
|
12
|
+
def self.included(base)
|
|
13
|
+
base.extend(ClassMethods)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
module ClassMethods
|
|
17
|
+
# Register a custom scalar type for use in variable declarations.
|
|
18
|
+
#
|
|
19
|
+
# Graphlient::Query::Serializer.scalar(:date, 'Date')
|
|
20
|
+
#
|
|
21
|
+
# Typically called via the client configuration block:
|
|
22
|
+
# client = Graphlient::Client.new(url) do |c|
|
|
23
|
+
# c.scalar :date, 'Date'
|
|
24
|
+
# c.scalar :uuid, 'UUID'
|
|
25
|
+
# c.scalar :decimal, 'Decimal'
|
|
26
|
+
# end
|
|
27
|
+
#
|
|
28
|
+
# After registration, use the symbol in variable declarations:
|
|
29
|
+
# query(created_after: :date) { ... } # -> query($createdAfter: Date)
|
|
30
|
+
def scalar(sym, graphql_type)
|
|
31
|
+
custom_scalar_types[sym.to_sym] = graphql_type.to_s
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def custom_scalar_types
|
|
35
|
+
@custom_scalar_types ||= {}
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def all_scalar_types
|
|
39
|
+
BUILT_IN_SCALAR_TYPES.merge(custom_scalar_types)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
private
|
|
44
|
+
|
|
45
|
+
def scalar?(value)
|
|
46
|
+
all_scalar_types.key?(value.to_s.delete('!').to_sym)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def scalar_type(value)
|
|
50
|
+
str = value.to_s
|
|
51
|
+
base = str.delete('!')
|
|
52
|
+
non_null = str.end_with?('!')
|
|
53
|
+
type = all_scalar_types[base.to_sym]
|
|
54
|
+
non_null ? "#{type}!" : type
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def all_scalar_types
|
|
58
|
+
self.class.all_scalar_types
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
require_relative '../errors/error'
|
|
2
|
+
require_relative 'directive'
|
|
3
|
+
require_relative 'serializer/scalars'
|
|
4
|
+
require_relative 'serializer/arguments'
|
|
5
|
+
require_relative 'serializer/evaluator'
|
|
6
|
+
require_relative 'serializer/fragments'
|
|
7
|
+
require_relative 'serializer/directives'
|
|
8
|
+
|
|
9
|
+
module Graphlient
|
|
10
|
+
class Query
|
|
11
|
+
# Builds a GraphQL query string from a DSL block.
|
|
12
|
+
# Composed from focused concern modules; Query is a thin public wrapper.
|
|
13
|
+
class Serializer
|
|
14
|
+
include Scalars
|
|
15
|
+
include Arguments
|
|
16
|
+
include Evaluator
|
|
17
|
+
include Fragments
|
|
18
|
+
include Directives
|
|
19
|
+
|
|
20
|
+
ROOT_NODES = %w[query mutation subscription].freeze
|
|
21
|
+
|
|
22
|
+
ROOT_NODES.each do |root_node|
|
|
23
|
+
define_method(root_node) do |*args, &block|
|
|
24
|
+
@variables = args.first || {}
|
|
25
|
+
append_node(root_node, args, arg_processor: variable_processor, &block)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
attr_accessor :query_str
|
|
30
|
+
|
|
31
|
+
def initialize(&block)
|
|
32
|
+
@indents = 0
|
|
33
|
+
@query_str = ''
|
|
34
|
+
@variables = {}
|
|
35
|
+
@fragments = {}
|
|
36
|
+
evaluate(&block) if block
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Full output: main query string + any inline fragment definitions.
|
|
40
|
+
def to_s
|
|
41
|
+
parts = [query_str.strip]
|
|
42
|
+
parts.concat(@fragments.values) unless @fragments.empty?
|
|
43
|
+
parts.join("\n\n")
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Raised when `spread` is called with neither a fragment name nor `on:`.
|
|
47
|
+
SPREAD_REQUIRES_NAME_OR_ON =
|
|
48
|
+
'spread requires a fragment name (`spread :InvoiceFields`) or an inline ' \
|
|
49
|
+
'type condition (`spread on: :PaidInvoice { ... }`).'.freeze
|
|
50
|
+
# Raised when a named spread is given a block (a named spread has no selection set).
|
|
51
|
+
SPREAD_NAME_TAKES_NO_BLOCK =
|
|
52
|
+
'a named fragment spread takes no block; for an inline fragment use ' \
|
|
53
|
+
'`spread on: :Type { ... }`.'.freeze
|
|
54
|
+
|
|
55
|
+
# Fragment spread OR inline fragment -- one consistent entry point.
|
|
56
|
+
#
|
|
57
|
+
# Named fragment spread: ...FragmentName [@directive ...]
|
|
58
|
+
# spread :InvoiceFields
|
|
59
|
+
# spread :InvoiceFields, _skip(if: :x) # -> ...InvoiceFields @skip(if: $x)
|
|
60
|
+
#
|
|
61
|
+
# Inline fragment / type condition: ... on Type [@directive ...] { fields }
|
|
62
|
+
# spread on: :PaidInvoice { amount_paid } # -> ... on PaidInvoice { amountPaid }
|
|
63
|
+
# spread on: :DraftInvoice, _skip(if: :skip) { draft_id }
|
|
64
|
+
# spread on: :Invoice # bare type condition, no block
|
|
65
|
+
#
|
|
66
|
+
# A single verb covers both GraphQL forms; `on:` is the same keyword used by
|
|
67
|
+
# `fragment(name, on:)`, keeping the DSL consistent (and leaving room for a future
|
|
68
|
+
# `spread(:X).skip(...)` chaining form without a breaking change).
|
|
69
|
+
def spread(*args, on: nil, &block)
|
|
70
|
+
directives = args.select { |a| a.is_a?(Directive) }
|
|
71
|
+
if on
|
|
72
|
+
append_inline_fragment(on, directives, &block)
|
|
73
|
+
else
|
|
74
|
+
append_named_spread(args, directives, &block)
|
|
75
|
+
end
|
|
76
|
+
@query_str << "\n#{indent}"
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Inline fragment definition -- collected and appended after the main query.
|
|
80
|
+
# fragment(:InvoiceFields, on: :Invoice) { id; fee_in_cents }
|
|
81
|
+
def fragment(name, on:, &block)
|
|
82
|
+
body = self.class.new(&block).query_str.strip
|
|
83
|
+
@fragments[name] = "fragment #{name} on #{on} {\n#{body}\n}"
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def method_missing(method_name, *args, &block)
|
|
87
|
+
if fragment?(method_name)
|
|
88
|
+
append_node("...#{resolve_fragment_constant(method_name)}".to_sym, args, &block)
|
|
89
|
+
elsif directive?(method_name)
|
|
90
|
+
Directive.new(method_name, args.first || {})
|
|
91
|
+
else
|
|
92
|
+
append_node(method_name, args, &block)
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def respond_to_missing?(method_name, include_private = false)
|
|
97
|
+
super
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
private
|
|
101
|
+
|
|
102
|
+
def indent
|
|
103
|
+
' ' * @indents
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# Inline fragment / type condition: ... on Type [@directive ...] [{ fields }]
|
|
107
|
+
def append_inline_fragment(type, directives, &block)
|
|
108
|
+
@query_str << "\n#{indent}... on #{type}"
|
|
109
|
+
directives.each { |d| @query_str << " #{d}" }
|
|
110
|
+
return unless block
|
|
111
|
+
|
|
112
|
+
@indents += 1
|
|
113
|
+
@query_str << '{'
|
|
114
|
+
evaluate(&block)
|
|
115
|
+
@query_str << '}'
|
|
116
|
+
@indents -= 1
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# Named fragment spread: ...FragmentName [@directive ...]
|
|
120
|
+
def append_named_spread(args, directives, &block)
|
|
121
|
+
fragment_name = args.find { |a| !a.is_a?(Directive) }
|
|
122
|
+
raise Graphlient::Errors::Error, SPREAD_REQUIRES_NAME_OR_ON if fragment_name.nil?
|
|
123
|
+
raise Graphlient::Errors::Error, SPREAD_NAME_TAKES_NO_BLOCK if block
|
|
124
|
+
|
|
125
|
+
@query_str << "\n#{indent}...#{fragment_name}"
|
|
126
|
+
directives.each { |d| @query_str << " #{d}" }
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def append_node(node, args, arg_processor: nil, &block)
|
|
130
|
+
regular = field_args(args)
|
|
131
|
+
dirs = directive_args(args)
|
|
132
|
+
|
|
133
|
+
@query_str << "\n#{indent}#{node}"
|
|
134
|
+
|
|
135
|
+
if (h = hash_arg(regular))
|
|
136
|
+
@query_str << "(#{args_str(h, arg_processor: arg_processor)})"
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
dirs.each { |d| @query_str << " #{d}" }
|
|
140
|
+
|
|
141
|
+
if block_given?
|
|
142
|
+
@indents += 1
|
|
143
|
+
@query_str << '{'
|
|
144
|
+
evaluate(&block)
|
|
145
|
+
@query_str << '}'
|
|
146
|
+
@indents -= 1
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
@query_str << "\n#{indent}"
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
end
|
data/lib/graphlient/query.rb
CHANGED
|
@@ -1,128 +1,29 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
append_node(root_node, args, arg_processor: ->(k, v) { "$#{k}: #{variable_string(v)}" }, &block)
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
def respond_to_missing?(method_name, include_private = false)
|
|
35
|
-
super
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
def to_s
|
|
39
|
-
query_str.strip
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
private
|
|
43
|
-
|
|
44
|
-
def evaluate(&block)
|
|
45
|
-
@last_block = block || self
|
|
46
|
-
(@context ||= {})[@last_block] ||= @last_block.binding
|
|
47
|
-
instance_eval(&block)
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
def resolve_fragment_constant(value)
|
|
51
|
-
return nil unless (match = value.to_s.match(FRAGMENT_DEFITION))
|
|
52
|
-
raw_const = match[:const].gsub('__', '::')
|
|
53
|
-
@context[@last_block].eval(raw_const).tap do |const|
|
|
54
|
-
msg = "Expected constant #{raw_const} to be GraphQL::Client::FragmentDefinition. Given #{const.class}"
|
|
55
|
-
raise Graphlient::Errors::Error, msg unless const.is_a? GraphQL::Client::FragmentDefinition
|
|
56
|
-
end
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
def append_node(node, args, arg_processor: nil, &block)
|
|
60
|
-
node = "...#{resolve_fragment_constant(node)}".to_sym if node.to_s.start_with?('___')
|
|
61
|
-
|
|
62
|
-
# add field
|
|
63
|
-
@query_str << "\n#{indent}#{node}"
|
|
64
|
-
# add filter
|
|
65
|
-
hash_arguments = hash_arg(args)
|
|
66
|
-
@query_str << "(#{args_str(hash_arguments, arg_processor: arg_processor)})" if hash_arguments
|
|
67
|
-
|
|
68
|
-
if block_given?
|
|
69
|
-
@indents += 1
|
|
70
|
-
@query_str << '{'
|
|
71
|
-
evaluate(&block)
|
|
72
|
-
@query_str << '}'
|
|
73
|
-
@indents -= 1
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
@query_str << "\n#{indent}"
|
|
77
|
-
end
|
|
78
|
-
|
|
79
|
-
def indent
|
|
80
|
-
' ' * @indents
|
|
81
|
-
end
|
|
82
|
-
|
|
83
|
-
def hash_arg(args)
|
|
84
|
-
args.detect { |arg| arg.is_a? Hash }
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
def args_str(hash_args, arg_processor: nil)
|
|
88
|
-
hash_args.map do |k, v|
|
|
89
|
-
arg_processor ? arg_processor.call(k, v) : argument_string(k, v)
|
|
90
|
-
end.join(', ')
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
def argument_string(key, val)
|
|
94
|
-
"#{key}: #{argument_value_string(val)}"
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
def variable_string(val)
|
|
98
|
-
case val
|
|
99
|
-
when :id, :id!
|
|
100
|
-
val.to_s.upcase
|
|
101
|
-
when ->(v) { SCALAR_TYPES.key?(v.to_s.delete('!').to_sym) }
|
|
102
|
-
# scalar types
|
|
103
|
-
val.to_s.camelize
|
|
104
|
-
when Array
|
|
105
|
-
"[#{variable_string(val.first)}]"
|
|
106
|
-
else
|
|
107
|
-
val.to_s
|
|
108
|
-
end
|
|
109
|
-
end
|
|
110
|
-
|
|
111
|
-
def argument_value_string(value)
|
|
112
|
-
case value
|
|
113
|
-
when String
|
|
114
|
-
"\"#{value}\""
|
|
115
|
-
when Numeric
|
|
116
|
-
value.to_s
|
|
117
|
-
when Array
|
|
118
|
-
"[#{value.map { |v| argument_value_string(v) }.join(', ')}]"
|
|
119
|
-
when Hash
|
|
120
|
-
"{ #{value.map { |k, v| "#{k}: #{argument_value_string(v)}" }.join(', ')} }"
|
|
121
|
-
when Symbol
|
|
122
|
-
@variables.key?(value) ? "$#{value}" : value.to_s.camelize(:lower)
|
|
123
|
-
else
|
|
124
|
-
value
|
|
125
|
-
end
|
|
126
|
-
end
|
|
127
|
-
end
|
|
128
|
-
end
|
|
1
|
+
require_relative 'query/serializer'
|
|
2
|
+
|
|
3
|
+
module Graphlient
|
|
4
|
+
# Public interface for building GraphQL query strings via a DSL block.
|
|
5
|
+
# Delegates all string-building work to Query::Serializer.
|
|
6
|
+
#
|
|
7
|
+
# Usage:
|
|
8
|
+
# Graphlient::Query.new do
|
|
9
|
+
# query do
|
|
10
|
+
# invoice(id: 10) { id; fee_in_cents }
|
|
11
|
+
# end
|
|
12
|
+
# end.to_s
|
|
13
|
+
class Query
|
|
14
|
+
# Register a custom scalar type for variable declarations.
|
|
15
|
+
# Delegates to Serializer so client configuration works:
|
|
16
|
+
# client = Graphlient::Client.new(url) { |c| c.scalar :date, 'Date' }
|
|
17
|
+
def self.scalar(sym, graphql_type)
|
|
18
|
+
Serializer.scalar(sym, graphql_type)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def initialize(&block)
|
|
22
|
+
@serializer = Serializer.new(&block)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def to_s
|
|
26
|
+
@serializer.to_s
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
data/lib/graphlient/schema.rb
CHANGED
|
@@ -1,26 +1,27 @@
|
|
|
1
|
-
require 'delegate'
|
|
2
|
-
|
|
3
|
-
module Graphlient
|
|
4
|
-
class Schema < SimpleDelegator
|
|
5
|
-
PATH_ERROR_MESSAGE = 'schema_path is missing. Please add it like this: `Graphlient.new(url, schema_path: YOUR_PATH)`'.freeze
|
|
6
|
-
|
|
7
|
-
class MissingConfigurationError < StandardError; end
|
|
8
|
-
|
|
9
|
-
alias graphql_schema __getobj__
|
|
10
|
-
|
|
11
|
-
attr_reader :http, :path
|
|
12
|
-
|
|
13
|
-
def initialize(http, path)
|
|
14
|
-
schema_source = path || http
|
|
15
|
-
super(GraphQL::Client.load_schema(schema_source))
|
|
16
|
-
|
|
17
|
-
@path = path
|
|
18
|
-
@http = http
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
def dump!
|
|
22
|
-
raise MissingConfigurationError, PATH_ERROR_MESSAGE unless path
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
end
|
|
1
|
+
require 'delegate'
|
|
2
|
+
|
|
3
|
+
module Graphlient
|
|
4
|
+
class Schema < SimpleDelegator
|
|
5
|
+
PATH_ERROR_MESSAGE = 'schema_path is missing. Please add it like this: `Graphlient.new(url, schema_path: YOUR_PATH)`'.freeze
|
|
6
|
+
|
|
7
|
+
class MissingConfigurationError < StandardError; end
|
|
8
|
+
|
|
9
|
+
alias graphql_schema __getobj__
|
|
10
|
+
|
|
11
|
+
attr_reader :http, :path
|
|
12
|
+
|
|
13
|
+
def initialize(http, path)
|
|
14
|
+
schema_source = path || http
|
|
15
|
+
super(GraphQL::Client.load_schema(schema_source))
|
|
16
|
+
|
|
17
|
+
@path = path
|
|
18
|
+
@http = http
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def dump!
|
|
22
|
+
raise MissingConfigurationError, PATH_ERROR_MESSAGE unless path
|
|
23
|
+
|
|
24
|
+
GraphQL::Client.dump_schema(http, path)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
data/lib/graphlient/version.rb
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
module Graphlient
|
|
2
|
-
VERSION = '0.
|
|
3
|
-
end
|
|
1
|
+
module Graphlient
|
|
2
|
+
VERSION = '0.9.0'.freeze
|
|
3
|
+
end
|
data/lib/graphlient.rb
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
require 'graphql/client'
|
|
2
|
-
require 'graphlient/version'
|
|
3
|
-
require 'graphlient/extensions'
|
|
4
|
-
require 'graphlient/errors'
|
|
5
|
-
require 'graphlient/query'
|
|
6
|
-
require 'graphlient/adapters'
|
|
7
|
-
require 'graphlient/client'
|
|
8
|
-
require 'graphlient/schema'
|
|
1
|
+
require 'graphql/client'
|
|
2
|
+
require 'graphlient/version'
|
|
3
|
+
require 'graphlient/extensions'
|
|
4
|
+
require 'graphlient/errors'
|
|
5
|
+
require 'graphlient/query'
|
|
6
|
+
require 'graphlient/adapters'
|
|
7
|
+
require 'graphlient/client'
|
|
8
|
+
require 'graphlient/schema'
|