graphql-repl 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (6) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +20 -0
  3. data/README.md +13 -0
  4. data/bin/graphql-repl +27 -0
  5. data/lib/graphql/repl.rb +199 -0
  6. metadata +90 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 747c28926505ecac64db4e2c981ab95832690b3d
4
+ data.tar.gz: 88256b18cb342a255a7042618c8399df26933f7e
5
+ SHA512:
6
+ metadata.gz: 15c3ba2c8bfcfc87eb0299d9d59cc50faeb86252667de1d329a5908e8e6d80b24059b9f073055074677f9b7cae4c85000b01dba6d1d08ee8fe87da9e15b485b6
7
+ data.tar.gz: bde1260e3ecc07487ab92ea4920dfe544ab9e197f3a1d2055b74a7f40868b791540ab3ebd2256adf4bfb519969d66bde9fc6e1db4477f381365930a9db8accc5
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2017 Joshua Peek
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,13 @@
1
+ # GraphQL REPL
2
+
3
+ ```
4
+ $ bin/graphql-repl -a "Bearer abc123" https://api.github.com/graphql
5
+ > viewer.login
6
+ "josh"
7
+
8
+ > node(id: "MDEwOlJlcG9zaXRvcnk4MDkwNTI3NQ==").on(Repository).url
9
+ "https://github.com/josh/graphql-repl"
10
+
11
+ > viewer.lo<tab>
12
+ viewer.location viewer.login
13
+ ```
data/bin/graphql-repl ADDED
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'graphql/repl'
5
+ require 'optparse'
6
+ require 'readline'
7
+
8
+ options = {
9
+ headers: {}
10
+ }
11
+
12
+ OptionParser.new do |opts|
13
+ opts.banner = "Usage: graphql-repl url [options]"
14
+
15
+ opts.on("-a=", "--authorization=", "Set Authorization HTTP header") do |v|
16
+ options[:headers]["Authorization"] = v
17
+ end
18
+ end.parse!
19
+
20
+ repl = GraphQL::REPL.new(ARGV[0], options[:headers])
21
+
22
+ Readline.completion_append_character = ""
23
+ Readline.completion_proc = proc { |s| repl.completions(s) }
24
+
25
+ while line = Readline.readline('> ', true)
26
+ p repl.eval(line.chomp)
27
+ end
@@ -0,0 +1,199 @@
1
+ require 'graphql'
2
+ require 'json'
3
+ require 'net/http'
4
+
5
+ module GraphQL
6
+ class REPL
7
+ IntrospectionDocument = GraphQL.parse(GraphQL::Introspection::INTROSPECTION_QUERY)
8
+
9
+ def initialize(uri, headers = {})
10
+ @uri = URI.parse(uri)
11
+ @headers = headers
12
+
13
+ @connection = Net::HTTP.new(@uri.host, @uri.port).tap do |client|
14
+ client.use_ssl = @uri.scheme == "https"
15
+ end
16
+
17
+ @schema = GraphQL::Schema::Loader.load(request(IntrospectionDocument.to_query_string))
18
+ end
19
+
20
+ def request(query)
21
+ request = Net::HTTP::Post.new(@uri.request_uri)
22
+ request["Accept"] = "application/json"
23
+ request["Content-Type"] = "application/json"
24
+ @headers.each { |k, v| request[k] = v }
25
+ request.body = JSON.generate({"query" => query})
26
+ response = @connection.request(request)
27
+ JSON.parse(response.body)
28
+ end
29
+
30
+ def eval(str)
31
+ expr = Expression.new(@schema, str)
32
+
33
+ if error = expr.error_message
34
+ return error
35
+ end
36
+
37
+ result = request(expr.document.to_query_string)
38
+
39
+ if result["errors"] && result["errors"][0]
40
+ result["errors"][0]["message"]
41
+ else
42
+ expr.response_path.reduce(result["data"]) { |obj, key| obj[key] }
43
+ end
44
+ end
45
+
46
+ def completions(str)
47
+ Expression.new(@schema, str).completions
48
+ end
49
+
50
+ class Proxy
51
+ def self.const_missing(name)
52
+ name
53
+ end
54
+
55
+ attr_accessor :calls
56
+
57
+ def initialize
58
+ @calls = []
59
+ end
60
+
61
+ def get_binding
62
+ binding
63
+ end
64
+
65
+ def method_missing(*args)
66
+ @calls << args
67
+ self
68
+ end
69
+ end
70
+
71
+ class Expression
72
+ def initialize(schema, str)
73
+ @schema = schema
74
+ @str = str
75
+ end
76
+
77
+ def calls
78
+ proxy = Proxy.new
79
+ b = proxy.get_binding
80
+ b.eval(@str).calls
81
+ end
82
+
83
+ def completions
84
+ first, partial = @str.scan(/^(.+)\.(.*)$/)[0]
85
+
86
+ if first
87
+ type = Expression.new(@schema, first).return_type
88
+ else
89
+ partial = @str
90
+ type = @schema.query.to_s
91
+ end
92
+
93
+ if type
94
+ @schema.types[type].all_fields.map(&:name).grep(/^#{partial}/).map { |name|
95
+ [first, name].compact.join(".")
96
+ }
97
+ else
98
+ []
99
+ end
100
+ end
101
+
102
+ def response_path
103
+ calls.reduce([]) do |path, (name, *args)|
104
+ case name
105
+ when :query, :mutation, :on
106
+ path
107
+ when :[]
108
+ path << args[0]
109
+ else
110
+ path << name.to_s
111
+ end
112
+ end
113
+ end
114
+
115
+ def document
116
+ calls = self.calls
117
+
118
+ operation_type = case calls[0][0]
119
+ when :query
120
+ calls.shift
121
+ "query"
122
+ when :mutation
123
+ calls.shift
124
+ "mutation"
125
+ else
126
+ "query"
127
+ end
128
+
129
+ selection = calls.reverse.reduce(nil) do |child, call|
130
+ case call[0]
131
+ when :[]
132
+ child
133
+ when :on
134
+ type = GraphQL::Language::Nodes::TypeName.new(name: call[1].to_s)
135
+ node = GraphQL::Language::Nodes::InlineFragment.new(type: type)
136
+ node.selections = [child] if child
137
+ node
138
+ else
139
+ arguments = []
140
+
141
+ (call[1] || {}).each do |name, value|
142
+ arguments << GraphQL::Language::Nodes::Argument.new(name: name.to_s, value: value)
143
+ end
144
+
145
+ node = GraphQL::Language::Nodes::Field.new(name: call[0].to_s, arguments: arguments)
146
+ node.selections = [child] if child
147
+ node
148
+ end
149
+ end
150
+
151
+ operation = GraphQL::Language::Nodes::OperationDefinition.new(operation_type: operation_type, selections: [selection])
152
+ GraphQL::Language::Nodes::Document.new(definitions: [operation])
153
+ end
154
+
155
+ def return_type
156
+ visitor = GraphQL::Language::Visitor.new(document)
157
+ type_stack = GraphQL::StaticValidation::TypeStack.new(@schema, visitor)
158
+
159
+ stack = []
160
+
161
+ visitor[GraphQL::Language::Nodes::OperationDefinition] << ->(node, _parent) do
162
+ stack << type_stack.object_types.last
163
+ end
164
+ visitor[GraphQL::Language::Nodes::FragmentDefinition] << ->(node, _parent) do
165
+ stack << type_stack.object_types.last
166
+ end
167
+ visitor[GraphQL::Language::Nodes::Field] << ->(node, _parent) do
168
+ stack << type_stack.field_definitions.last&.type
169
+ end
170
+ visitor[GraphQL::Language::Nodes::InlineFragment] << ->(node, _parent) do
171
+ stack << type_stack.object_types.last
172
+ end
173
+ visitor.visit
174
+
175
+ type = stack.last
176
+ type = type.of_type if type.is_a?(GraphQL::NonNullType)
177
+
178
+ if type.is_a?(GraphQL::ListType)
179
+ if response_path.last.is_a?(Integer)
180
+ type.unwrap.name
181
+ else
182
+ "List"
183
+ end
184
+ elsif type
185
+ type.name
186
+ else
187
+ nil
188
+ end
189
+ end
190
+
191
+ def error_message
192
+ validator = GraphQL::StaticValidation::Validator.new(schema: @schema)
193
+ query = GraphQL::Query.new(@schema, document: document)
194
+ errors = validator.validate(query)
195
+ errors.fetch(:errors)[0]&.message
196
+ end
197
+ end
198
+ end
199
+ end
metadata ADDED
@@ -0,0 +1,90 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: graphql-repl
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Joshua Peek
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-02-04 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: graphql
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.4'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.4'
27
+ - !ruby/object:Gem::Dependency
28
+ name: minitest
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '5.10'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '5.10'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '12.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '12.0'
55
+ description: ''
56
+ email: josh@joshpeek.com
57
+ executables:
58
+ - graphql-repl
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - LICENSE
63
+ - README.md
64
+ - bin/graphql-repl
65
+ - lib/graphql/repl.rb
66
+ homepage: https://github.com/josh/graphql-repl
67
+ licenses:
68
+ - MIT
69
+ metadata: {}
70
+ post_install_message:
71
+ rdoc_options: []
72
+ require_paths:
73
+ - lib
74
+ required_ruby_version: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ version: 2.3.3
79
+ required_rubygems_version: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ requirements: []
85
+ rubyforge_project:
86
+ rubygems_version: 2.5.2
87
+ signing_key:
88
+ specification_version: 4
89
+ summary: GraphQL REPL
90
+ test_files: []