artemis 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +15 -0
- data/README.md +56 -59
- data/Rakefile +10 -3
- data/artemis.gemspec +2 -2
- data/lib/artemis/adapters/abstract_adapter.rb +3 -0
- data/lib/artemis/adapters/net_http_persistent_adapter.rb +3 -1
- data/lib/artemis/client.rb +89 -34
- data/lib/artemis/graphql_endpoint.rb +8 -1
- data/lib/artemis/version.rb +1 -1
- data/lib/generators/artemis/{USAGE → install/USAGE} +1 -1
- data/lib/generators/artemis/{install_generator.rb → install/install_generator.rb} +18 -0
- data/lib/generators/artemis/{templates → install/templates}/client.rb +0 -0
- data/lib/generators/artemis/install/templates/graphql.yml +34 -0
- data/lib/generators/artemis/mutation/USAGE +27 -0
- data/lib/generators/artemis/mutation/mutation_generator.rb +52 -0
- data/lib/generators/artemis/mutation/templates/mutation.graphql +5 -0
- data/lib/generators/artemis/query/USAGE +27 -0
- data/lib/generators/artemis/query/query_generator.rb +52 -0
- data/lib/generators/artemis/query/templates/query.graphql +5 -0
- data/spec/adapters_spec.rb +8 -0
- metadata +17 -7
- data/lib/generators/artemis/templates/.gitkeep +0 -0
- data/lib/generators/artemis/templates/graphql.yml +0 -19
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ac727e89f6d2c0845b888ea9eeb526bf2607689fa268c9f815b9af3c25c83d72
|
4
|
+
data.tar.gz: 1cb2c9c3de64e95b729f579dfb4a8bd93a07d6ac4732647bf7aa94a360a6624a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 041b006e10af9271e6cb024f2acfdc1597969ad44ccf371235a12f95c2464afac66110967db926ece6df3c46c3161b3b3f54436ea276b0362234c27f13e5933e
|
7
|
+
data.tar.gz: 0daaea8bebcbf4ea6c9225781f591b4cd069aa6517dacb09043500b29357c1140d4169ed00e55046003702489addbfea60ef7703bc607692186ef61c7d94c0ac
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
## Unreleased
|
2
|
+
|
3
|
+
* Add a new entry here
|
4
|
+
* [#43](https://github.com/yuki24/artemis/pull/43): Keep persistent connections open for 30 minutes
|
5
|
+
* [#42](https://github.com/yuki24/artemis/pull/42): Allow for setting up the test adapter without `url`
|
6
|
+
* [#41](https://github.com/yuki24/artemis/pull/41): Add a mutation generator
|
7
|
+
* [#40](https://github.com/yuki24/artemis/pull/40): Add a query generator
|
8
|
+
* [#39](https://github.com/yuki24/artemis/pull/39): Installer now adds a new service if config/graphql.yml is present
|
9
|
+
|
10
|
+
## [v0.1.0: First release!](https://github.com/yuki24/artemis/tree/v0.1.0)
|
11
|
+
|
12
|
+
_<sup>released at 2018-10-16 20:57:51 UTC</sup>_
|
13
|
+
|
14
|
+
First release of Artemis! <g-emoji class="g-emoji" alias="tada" fallback-src="https://assets-cdn.github.com/images/icons/emoji/unicode/1f389.png">🎉</g-emoji>
|
15
|
+
|
data/README.md
CHANGED
@@ -6,7 +6,9 @@
|
|
6
6
|
once in production and it'll never affect runtime performance. Comes with options that enable persistent connections
|
7
7
|
and even HTTP/2.0, the next-gen high-performance protocol.
|
8
8
|
|
9
|
-
|
9
|
+
<img width="24" height="24" src="https://avatars1.githubusercontent.com/u/541332?s=48&v=4"> Battled-tested at [Artsy](https://www.artsy.net)
|
10
|
+
|
11
|
+
## Getting started
|
10
12
|
|
11
13
|
Add this line to your application's Gemfile:
|
12
14
|
|
@@ -18,87 +20,72 @@ And then execute:
|
|
18
20
|
|
19
21
|
$ bundle
|
20
22
|
|
21
|
-
Once you run `bundle install` on your Rails app,
|
23
|
+
Once you run `bundle install` on your Rails app, run the install command:
|
22
24
|
|
23
25
|
|
24
26
|
```sh
|
25
27
|
$ rails g artemis:install artsy https://metaphysics-production.artsy.net/
|
26
28
|
```
|
27
29
|
|
28
|
-
|
29
|
-
to assign a token so the installer can properly download the GraphQL schema for the service:
|
30
|
+
You could also use the `--authorization` option to assign a token so the installer can download the GraphQL schema:
|
30
31
|
|
31
32
|
```sh
|
32
33
|
$ rails g artemis:install github https://api.github.com/graphql --authorization 'token ...'
|
33
34
|
```
|
34
35
|
|
35
|
-
|
36
|
+
### Generating your first query
|
36
37
|
|
37
|
-
Artemis
|
38
|
+
Artemis comes with a query generator. For exmaple, you could use the query generator to generate a query stub for `artist`:
|
38
39
|
|
39
|
-
```
|
40
|
-
|
41
|
-
│ ├── artsy
|
42
|
-
│ │ ├── _artist_fragment.graphql
|
43
|
-
│ │ ├── artwork.graphql
|
44
|
-
│ │ ├── artist.graphql
|
45
|
-
│ │ └── artists.graphql
|
46
|
-
│ └── artsy.rb
|
47
|
-
├──config/graphql.yml
|
48
|
-
└──vendor/graphql/schema/artsy.json
|
40
|
+
```sh
|
41
|
+
$ rails g artemis:query artist
|
49
42
|
```
|
50
43
|
|
51
|
-
|
44
|
+
Then this will generate:
|
52
45
|
|
53
|
-
```
|
54
|
-
#
|
55
|
-
|
56
|
-
|
57
|
-
|
46
|
+
```graphql
|
47
|
+
# app/operations/artist.graphql
|
48
|
+
query($id: String!) {
|
49
|
+
artist(id: $id) {
|
50
|
+
# Add fields here...
|
51
|
+
}
|
52
|
+
}
|
58
53
|
```
|
59
54
|
|
55
|
+
Then you could the class method that has the matching name `artist`:
|
56
|
+
|
60
57
|
```ruby
|
61
|
-
|
62
|
-
|
63
|
-
end
|
58
|
+
Artsy.artist(id: "pablo-picasso")
|
59
|
+
# => makes a GraphQL query that's in app/operations/artist.graphql
|
64
60
|
```
|
65
61
|
|
66
|
-
|
67
|
-
# app/queries/artsy/artwork.graphql
|
68
|
-
query($id: String!) {
|
69
|
-
artwork(id: $id) {
|
70
|
-
title
|
71
|
-
}
|
72
|
-
}
|
62
|
+
You can also specify a file name:
|
73
63
|
|
74
|
-
|
75
|
-
query
|
76
|
-
|
77
|
-
name
|
78
|
-
}
|
79
|
-
}
|
64
|
+
```sh
|
65
|
+
$ rails g artemis:query artist artist_details_on_artwork
|
66
|
+
# => generates app/operations/artist_details_on_artwork.graphql
|
80
67
|
```
|
81
68
|
|
69
|
+
Then you can make a query in `artist_details_on_artwork.graphql` with:
|
70
|
+
|
82
71
|
```ruby
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
# }
|
101
|
-
# }
|
72
|
+
Artsy.artist_details_on_artwork(id: "pablo-picasso")
|
73
|
+
```
|
74
|
+
|
75
|
+
## The convention
|
76
|
+
|
77
|
+
Artemis assumes that the files related to GraphQL are organized in a certain way. For example, a service that talks to Artsy's GraphQL API could have the following structure:
|
78
|
+
|
79
|
+
```
|
80
|
+
├──app/operations
|
81
|
+
│ ├── artsy
|
82
|
+
│ │ ├── _artist_fragment.graphql
|
83
|
+
│ │ ├── artwork.graphql
|
84
|
+
│ │ ├── artist.graphql
|
85
|
+
│ │ └── artists.graphql
|
86
|
+
│ └── artsy.rb
|
87
|
+
├──config/graphql.yml
|
88
|
+
└──vendor/graphql/schema/artsy.json
|
102
89
|
```
|
103
90
|
|
104
91
|
## Callbacks
|
@@ -149,11 +136,20 @@ There are four adapter options available. Choose the adapter that best fits on y
|
|
149
136
|
|
150
137
|
| Adapter | Protocol | Keep-alive | Performance | Dependencies |
|
151
138
|
| ---------------------- | ------------------------ | ----------- | ----------- | ------------ |
|
152
|
-
| `:curb` | HTTP/1.1, **HTTP/2.0** | **Yes** | **Fastest** | [`curb 0.9.6+`]
|
139
|
+
| `:curb` | HTTP/1.1, **HTTP/2.0** | **Yes** | **Fastest** | [`curb 0.9.6+`][curb]<br>[`libcurl 7.64.0+`][curl]<br>[`nghttp2 1.0.0+`][nghttp]
|
153
140
|
| `:net_http` (default) | HTTP/1.1 only | No | Slow | **None**
|
154
|
-
| `:net_http_persistent` | HTTP/1.1 only | **Yes** | **Fast** | [`net-http-persistent 3.0.0+`]
|
141
|
+
| `:net_http_persistent` | HTTP/1.1 only | **Yes** | **Fast** | [`net-http-persistent 3.0.0+`][nhp]
|
155
142
|
| `:test` | N/A (See Testing)
|
156
143
|
|
144
|
+
## Rake tasks
|
145
|
+
|
146
|
+
Artemis also adds a useful `rake graphql:schema:update` rake task that downloads the GraphQL schema using the
|
147
|
+
`Introspection` query.
|
148
|
+
|
149
|
+
| Task Name | Options | Description |
|
150
|
+
| ---------------------------- | --------- | ------------|
|
151
|
+
| `graphql:schema:update` | `SERVICE`: Service name the schema is downloaded from<br>`AUTHORIZATION`: HTTTP `Authroization` header value used to download the schema| Downloads and saves the GraphQL schema
|
152
|
+
|
157
153
|
## Testing
|
158
154
|
|
159
155
|
**The testing support is incomplete, but there are some examples [available in Artemis' client spec](https://github.com/yuki24/artemis/blob/74095f3acb050e87251439aed5f8b17778ffdd06/spec/client_spec.rb#L36-L54).**
|
@@ -176,6 +172,7 @@ The gem is available as open source under the terms of the [MIT License](https:/
|
|
176
172
|
|
177
173
|
Everyone interacting in the Artemis project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/artemis/blob/master/CODE_OF_CONDUCT.md).
|
178
174
|
|
175
|
+
[curb]: https://rubygems.org/gems/curb
|
179
176
|
[curl]: https://curl.haxx.se/docs/http2.html
|
180
177
|
[nghttp]: https://nghttp2.org/
|
181
178
|
[nhp]: https://rubygems.org/gems/net-http-persistent
|
data/Rakefile
CHANGED
@@ -2,13 +2,20 @@ require "bundler/gem_tasks"
|
|
2
2
|
require "rake/testtask"
|
3
3
|
require 'rspec/core/rake_task'
|
4
4
|
|
5
|
+
TESTS_IN_ISOLATION = ['test/railtie_test.rb', 'test/rake_tasks_test.rb']
|
6
|
+
|
7
|
+
Rake::TestTask.new(:test) do |t|
|
8
|
+
t.libs << "test"
|
9
|
+
t.test_files = FileList['test/**/*_test.rb'] - TESTS_IN_ISOLATION
|
10
|
+
t.warning = false
|
11
|
+
end
|
12
|
+
|
5
13
|
Rake::TestTask.new('test:isolated') do |t|
|
6
14
|
t.libs << "test"
|
7
|
-
t.
|
8
|
-
t.test_files = FileList['test/**/*_test.rb']
|
15
|
+
t.test_files = TESTS_IN_ISOLATION
|
9
16
|
t.warning = false
|
10
17
|
end
|
11
18
|
|
12
19
|
RSpec::Core::RakeTask.new(:spec)
|
13
20
|
|
14
|
-
task default: ['spec', 'test:isolated']
|
21
|
+
task default: ['spec', 'test', 'test:isolated']
|
data/artemis.gemspec
CHANGED
@@ -6,8 +6,8 @@ require "artemis/version"
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
7
|
spec.name = "artemis"
|
8
8
|
spec.version = Artemis::VERSION
|
9
|
-
spec.authors = ["Yuki Nishijima"]
|
10
|
-
spec.email = ["yk.nishijima@gmail.com"]
|
9
|
+
spec.authors = ["Jon Allured", "Yuki Nishijima"]
|
10
|
+
spec.email = ["jon.allured@gmail.com", "yk.nishijima@gmail.com"]
|
11
11
|
spec.summary = %q{GraphQL on Rails}
|
12
12
|
spec.description = %q{GraphQL client on Rails + Convention over Configuration = ❤️}
|
13
13
|
spec.homepage = "https://github.com/yuki24/artemis"
|
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'active_support/core_ext/object/blank'
|
3
4
|
require 'graphql/client/http'
|
4
5
|
|
5
6
|
module Artemis
|
@@ -10,6 +11,8 @@ module Artemis
|
|
10
11
|
EMPTY_HEADERS = {}.freeze
|
11
12
|
|
12
13
|
def initialize(uri, service_name: , timeout: , pool_size: )
|
14
|
+
raise ArgumentError, "url is required (given `#{uri.inspect}')" if uri.blank?
|
15
|
+
|
13
16
|
super(uri) # Do not pass in the block to avoid getting #headers and #connection overridden.
|
14
17
|
|
15
18
|
@service_name = service_name.to_s
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require 'delegate'
|
4
4
|
|
5
|
+
require 'active_support/core_ext/numeric/time'
|
5
6
|
require 'net/http/persistent'
|
6
7
|
|
7
8
|
require 'artemis/adapters/net_http_adapter'
|
@@ -17,6 +18,7 @@ module Artemis
|
|
17
18
|
@raw_connection = Net::HTTP::Persistent.new(name: service_name, pool_size: pool_size)
|
18
19
|
@raw_connection.open_timeout = timeout
|
19
20
|
@raw_connection.read_timeout = timeout
|
21
|
+
@raw_connection.idle_timeout = 30.minutes.to_i # TODO: Make it configurable
|
20
22
|
|
21
23
|
@_connection = ConnectionWrapper.new(@raw_connection, uri)
|
22
24
|
end
|
@@ -28,7 +30,7 @@ module Artemis
|
|
28
30
|
_connection
|
29
31
|
end
|
30
32
|
|
31
|
-
class ConnectionWrapper < SimpleDelegator
|
33
|
+
class ConnectionWrapper < SimpleDelegator #:nodoc:
|
32
34
|
def initialize(obj, url)
|
33
35
|
super(obj)
|
34
36
|
|
data/lib/artemis/client.rb
CHANGED
@@ -44,13 +44,13 @@ module Artemis
|
|
44
44
|
# }
|
45
45
|
#
|
46
46
|
# # app/operations/github.rb
|
47
|
-
# class
|
47
|
+
# class Github < Artemis::Client
|
48
48
|
# end
|
49
49
|
#
|
50
|
-
# github =
|
50
|
+
# github = Github.new
|
51
51
|
# github.user(id: 'yuki24').data.user.name # => "Yuki Nishijima"
|
52
52
|
#
|
53
|
-
# github =
|
53
|
+
# github = Github.new(context: { headers: { Authorization: "bearer ..." } })
|
54
54
|
# github.user(id: 'yuki24').data.user.name # => "Yuki Nishijima"
|
55
55
|
#
|
56
56
|
def initialize(context = {})
|
@@ -60,12 +60,65 @@ module Artemis
|
|
60
60
|
class << self
|
61
61
|
delegate :query_paths, :default_context, :query_paths=, :default_context=, to: :config
|
62
62
|
|
63
|
+
# Creates a new instance of the GraphQL client for the service.
|
64
|
+
#
|
65
|
+
# # app/operations/github/user.graphql
|
66
|
+
# query($id: String!) {
|
67
|
+
# user(login: $id) {
|
68
|
+
# name
|
69
|
+
# }
|
70
|
+
# }
|
71
|
+
#
|
72
|
+
# # app/operations/github.rb
|
73
|
+
# class Github < Artemis::Client
|
74
|
+
# end
|
75
|
+
#
|
76
|
+
# github = Github.new
|
77
|
+
# github.user(id: 'yuki24').data.user.name # => "Yuki Nishijima"
|
78
|
+
#
|
79
|
+
# github = Github.new(context: { headers: { Authorization: "bearer ..." } })
|
80
|
+
# github.user(id: 'yuki24').data.user.name # => "Yuki Nishijima"
|
81
|
+
#
|
63
82
|
alias with_context new
|
64
83
|
|
84
|
+
# Returns the registered meta data (generally present in +config/graphql.yml+) for the client.
|
85
|
+
#
|
86
|
+
# # config/graphql.yml
|
87
|
+
# development:
|
88
|
+
# github:
|
89
|
+
# url: https://api.github.com/graphql
|
90
|
+
# adapter: :net_http
|
91
|
+
#
|
92
|
+
# # app/operations/github.rb
|
93
|
+
# class Github < Artemis::Client
|
94
|
+
# end
|
95
|
+
#
|
96
|
+
# Github.endpoint.url # => "https://api.github.com/graphql"
|
97
|
+
# Github.endpoint.adapter # => :net_http
|
98
|
+
#
|
65
99
|
def endpoint
|
66
100
|
Artemis::GraphQLEndpoint.lookup(name)
|
67
101
|
end
|
68
102
|
|
103
|
+
# Instantiates a new instance of +GraphQL::Client+ for the service.
|
104
|
+
#
|
105
|
+
# # app/operations/github/user.graphql
|
106
|
+
# query($id: String!) {
|
107
|
+
# user(login: $id) {
|
108
|
+
# name
|
109
|
+
# }
|
110
|
+
# }
|
111
|
+
#
|
112
|
+
# # app/operations/github.rb
|
113
|
+
# class Github < Artemis::Client
|
114
|
+
# end
|
115
|
+
#
|
116
|
+
# client = Github.instantiate_client
|
117
|
+
# client.query(Github::User, arguments: { id: 'yuki24' }) # makes a Graphql request
|
118
|
+
#
|
119
|
+
# client = Github.instantiate_client(context: { headers: { Authorization: "bearer ..." } })
|
120
|
+
# client.query(Github::User, arguments: { id: 'yuki24' }) # makes a Graphql request with Authorization header
|
121
|
+
#
|
69
122
|
def instantiate_client(context = {})
|
70
123
|
::GraphQL::Client.new(schema: endpoint.schema, execute: connection(context))
|
71
124
|
end
|
@@ -73,7 +126,7 @@ module Artemis
|
|
73
126
|
# Defines a callback that will get called right before the
|
74
127
|
# client's execute method is executed.
|
75
128
|
#
|
76
|
-
# class
|
129
|
+
# class Github < Artemis::Client
|
77
130
|
#
|
78
131
|
# before_execute do |document, operation_name, variables, context|
|
79
132
|
# Analytics.log(operation_name, variables, context[:user_id])
|
@@ -89,7 +142,7 @@ module Artemis
|
|
89
142
|
# Defines a callback that will get called right after the
|
90
143
|
# client's execute method has finished.
|
91
144
|
#
|
92
|
-
# class
|
145
|
+
# class Github < Artemis::Client
|
93
146
|
#
|
94
147
|
# after_execute do |data, errors, extensions|
|
95
148
|
# if errors.present?
|
@@ -136,7 +189,6 @@ module Artemis
|
|
136
189
|
end
|
137
190
|
alias load_query load_constant
|
138
191
|
|
139
|
-
# @api private
|
140
192
|
def connection(context = {})
|
141
193
|
Executor.new(endpoint.connection, callbacks, default_context.deep_merge(context))
|
142
194
|
end
|
@@ -157,15 +209,12 @@ module Artemis
|
|
157
209
|
end
|
158
210
|
end
|
159
211
|
|
160
|
-
|
161
|
-
def respond_to_missing?(method_name, *_, &block)
|
212
|
+
def respond_to_missing?(method_name, *_, &block) #:nodoc:
|
162
213
|
resolve_graphql_file_path(method_name) || super
|
163
214
|
end
|
164
215
|
|
165
|
-
Callbacks
|
166
|
-
|
167
|
-
private_constant :Callbacks
|
168
|
-
|
216
|
+
# Returns a +Callbacks+ collection object that implements the interface for the +Executor+ object.
|
217
|
+
#
|
169
218
|
# @api private
|
170
219
|
def callbacks
|
171
220
|
Callbacks.new(config.before_callbacks, config.after_callbacks)
|
@@ -194,8 +243,7 @@ module Artemis
|
|
194
243
|
end
|
195
244
|
end
|
196
245
|
|
197
|
-
|
198
|
-
def respond_to_missing?(method_name, *_, &block)
|
246
|
+
def respond_to_missing?(method_name, *_, &block) #:nodoc:
|
199
247
|
self.class.resolve_graphql_file_path(method_name) || super
|
200
248
|
end
|
201
249
|
|
@@ -213,33 +261,40 @@ module Artemis
|
|
213
261
|
end
|
214
262
|
RUBY
|
215
263
|
end
|
216
|
-
end
|
217
264
|
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
@callbacks = callbacks
|
224
|
-
@default_context = default_context
|
225
|
-
end
|
265
|
+
# Internal collection object that holds references to the callback blocks.
|
266
|
+
#
|
267
|
+
# @api private
|
268
|
+
Callbacks = Struct.new(:before_callbacks, :after_callbacks) #:nodoc:
|
226
269
|
|
227
|
-
|
228
|
-
|
270
|
+
# Wrapper object around the adapter that wires up callbacks.
|
271
|
+
#
|
272
|
+
# @api private
|
273
|
+
class Executor < SimpleDelegator
|
274
|
+
def initialize(connection, callbacks, default_context) #:nodoc:
|
275
|
+
super(connection)
|
229
276
|
|
230
|
-
|
231
|
-
|
277
|
+
@callbacks = callbacks
|
278
|
+
@default_context = default_context
|
232
279
|
end
|
233
280
|
|
234
|
-
|
281
|
+
def execute(document:, operation_name: nil, variables: {}, context: {}) #:nodoc:
|
282
|
+
_context = @default_context.deep_merge(context)
|
235
283
|
|
236
|
-
|
237
|
-
|
238
|
-
|
284
|
+
@callbacks.before_callbacks.each do |callback|
|
285
|
+
callback.call(document, operation_name, variables, _context)
|
286
|
+
end
|
239
287
|
|
240
|
-
|
288
|
+
response = __getobj__.execute(document: document, operation_name: operation_name, variables: variables, context: _context)
|
289
|
+
|
290
|
+
@callbacks.after_callbacks.each do |callback|
|
291
|
+
callback.call(response['data'], response['errors'], response['extensions'])
|
292
|
+
end
|
293
|
+
|
294
|
+
response
|
295
|
+
end
|
241
296
|
end
|
242
|
-
end
|
243
297
|
|
244
|
-
|
298
|
+
private_constant :Callbacks, :Executor
|
299
|
+
end
|
245
300
|
end
|
@@ -27,11 +27,18 @@ module Artemis
|
|
27
27
|
def register!(service_name, configurations)
|
28
28
|
ENDPOINT_INSTANCES[service_name.to_s.underscore] = new(service_name.to_s, configurations.symbolize_keys)
|
29
29
|
end
|
30
|
+
|
31
|
+
##
|
32
|
+
# Returns the registered services as an array.
|
33
|
+
#
|
34
|
+
def registered_services
|
35
|
+
ENDPOINT_INSTANCES.keys
|
36
|
+
end
|
30
37
|
end
|
31
38
|
|
32
39
|
attr_reader :name, :url, :adapter, :timeout, :schema_path, :pool_size
|
33
40
|
|
34
|
-
def initialize(name, url: , adapter: :net_http, timeout:
|
41
|
+
def initialize(name, url: nil, adapter: :net_http, timeout: 10, schema_path: nil, pool_size: 25)
|
35
42
|
@name, @url, @adapter, @timeout, @schema_path, @pool_size = name.to_s, url, adapter, timeout, schema_path, pool_size
|
36
43
|
|
37
44
|
@mutex_for_schema = Mutex.new
|
data/lib/artemis/version.rb
CHANGED
@@ -2,7 +2,7 @@ Description:
|
|
2
2
|
Generates a client stub and config files and downloads the schema from the given endpoint.
|
3
3
|
|
4
4
|
Example:
|
5
|
-
rails generate install Artsy https://metaphysics-production.artsy.net
|
5
|
+
rails generate artemis:install Artsy https://metaphysics-production.artsy.net
|
6
6
|
|
7
7
|
This will create:
|
8
8
|
app/operations/artsy.rb
|
@@ -17,6 +17,24 @@ class Artemis::InstallGenerator < Rails::Generators::NamedBase
|
|
17
17
|
in_root do
|
18
18
|
if behavior == :invoke && !File.exist?(config_file_name)
|
19
19
|
template "graphql.yml", config_file_name
|
20
|
+
else
|
21
|
+
inject_into_file config_file_name, <<-YAML, after: "development:\n"
|
22
|
+
#{file_name}:
|
23
|
+
<<: *default
|
24
|
+
url: #{endpoint_url}\n
|
25
|
+
YAML
|
26
|
+
|
27
|
+
inject_into_file config_file_name, <<-YAML, after: "test:\n", force: true
|
28
|
+
#{file_name}:
|
29
|
+
<<: *default
|
30
|
+
url: #{endpoint_url}\n
|
31
|
+
YAML
|
32
|
+
|
33
|
+
inject_into_file config_file_name, <<-YAML, after: "production:\n", force: true
|
34
|
+
#{file_name}:
|
35
|
+
<<: *default
|
36
|
+
url: #{endpoint_url}\n
|
37
|
+
YAML
|
20
38
|
end
|
21
39
|
end
|
22
40
|
end
|
File without changes
|
@@ -0,0 +1,34 @@
|
|
1
|
+
default: &default
|
2
|
+
# The underlying client library that actually makes an HTTP request.
|
3
|
+
# Available adapters are :net_http, :net_http_persistent, :curb, and :test.
|
4
|
+
#
|
5
|
+
# It is set to :net_http by default.
|
6
|
+
adapter: :net_http
|
7
|
+
|
8
|
+
# HTTP timeout set for the adapter in seconds. This will be set to both
|
9
|
+
# `read_timeout` and `write_timeout` and there is no way to configure them
|
10
|
+
# with a different value as of writing (PRs welcome!)
|
11
|
+
#
|
12
|
+
# It is set to 10 by default.
|
13
|
+
timeout: 10
|
14
|
+
|
15
|
+
# The number of keep-alive connections. The `:net_http` adapter will ignore
|
16
|
+
# this option.
|
17
|
+
#
|
18
|
+
# It is set to 25 by default.
|
19
|
+
pool_size: 25
|
20
|
+
|
21
|
+
development:
|
22
|
+
<%= file_name %>:
|
23
|
+
<<: *default
|
24
|
+
url: <%= endpoint_url %>
|
25
|
+
|
26
|
+
test:
|
27
|
+
<%= file_name %>:
|
28
|
+
<<: *default
|
29
|
+
url: <%= endpoint_url %>
|
30
|
+
|
31
|
+
production:
|
32
|
+
<%= file_name %>:
|
33
|
+
<<: *default
|
34
|
+
url: <%= endpoint_url %>
|
@@ -0,0 +1,27 @@
|
|
1
|
+
Description:
|
2
|
+
Generates a mutation stub.
|
3
|
+
|
4
|
+
rails g artemis:mutation MUTATION_TYPE [FILE_NAME]
|
5
|
+
|
6
|
+
Example:
|
7
|
+
rails g artemis:mutation followArtist
|
8
|
+
|
9
|
+
This will create:
|
10
|
+
app/operations/artsy.rb
|
11
|
+
app/operations/artsy/follow_artist.graphql
|
12
|
+
|
13
|
+
The GraphQL file name could be specified by giving the third argument:
|
14
|
+
|
15
|
+
rails g artemis:mutation followArtist follow_artist_on_artwork
|
16
|
+
|
17
|
+
This will create:
|
18
|
+
app/operations/artsy.rb
|
19
|
+
app/operations/artsy/follow_artist_on_artwork.graphql
|
20
|
+
|
21
|
+
If there are multiple services registered, the service could be specified with +--service+ option:
|
22
|
+
|
23
|
+
rails g artemis:mutation createProject --service github
|
24
|
+
|
25
|
+
This will create:
|
26
|
+
app/operations/github.rb
|
27
|
+
app/operations/github/create_project.graphql
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'graphql/schema/finder'
|
4
|
+
|
5
|
+
class Artemis::MutationGenerator < Rails::Generators::Base
|
6
|
+
source_root File.expand_path('../templates', __FILE__)
|
7
|
+
|
8
|
+
argument :mutation_type, type: :string, required: true, banner: "Mutation type"
|
9
|
+
argument :graphql_file_name, type: :string, required: false, default: nil, banner: "The name of the GraphQL file to be generated"
|
10
|
+
|
11
|
+
class_option :service, type: :string, default: nil, aliases: "-A"
|
12
|
+
|
13
|
+
def generate_mutation_file
|
14
|
+
template "mutation.graphql", graphql_file_path
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def mutation_name
|
20
|
+
mutation_type.underscore
|
21
|
+
end
|
22
|
+
|
23
|
+
def graphql_file_path
|
24
|
+
"app/operations/#{service_name.underscore}/#{graphql_file_name.presence || mutation_name}.graphql"
|
25
|
+
end
|
26
|
+
|
27
|
+
def arguments
|
28
|
+
target_mutation.arguments
|
29
|
+
end
|
30
|
+
|
31
|
+
def target_mutation
|
32
|
+
schema.find("Mutation").fields[mutation_type] ||
|
33
|
+
raise(GraphQL::Schema::Finder::MemberNotFoundError, "Could not find type `#{mutation_type}` in schema.")
|
34
|
+
end
|
35
|
+
|
36
|
+
def schema
|
37
|
+
service_name.camelize.constantize.endpoint.schema
|
38
|
+
end
|
39
|
+
|
40
|
+
def service_name
|
41
|
+
options['service'].presence || begin
|
42
|
+
services = Artemis::GraphQLEndpoint.registered_services
|
43
|
+
|
44
|
+
if services.size == 1
|
45
|
+
services.first
|
46
|
+
else
|
47
|
+
fail "Please specify a service name (available services: #{services.join(", ")}):\n\n" \
|
48
|
+
" rails g artemis:mutation #{mutation_type} #{graphql_file_name} --service SERVICE"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,5 @@
|
|
1
|
+
mutation<%= arguments.present? && "(#{ arguments.map {|name, type| "$#{name}: #{type.type}" }.join(", ") })" %> {
|
2
|
+
<%= target_mutation.name %><%= arguments.present? && "(#{ arguments.map {|name, type| "#{name}: $#{name}" }.join(", ") })" %> {
|
3
|
+
# Add fields here...
|
4
|
+
}
|
5
|
+
}
|
@@ -0,0 +1,27 @@
|
|
1
|
+
Description:
|
2
|
+
Generates a query stub.
|
3
|
+
|
4
|
+
rails g artemis:query QUERY_TYPE [FILE_NAME]
|
5
|
+
|
6
|
+
Example:
|
7
|
+
rails g artemis:query artist
|
8
|
+
|
9
|
+
This will create:
|
10
|
+
app/operations/artsy.rb
|
11
|
+
app/operations/artsy/artist.graphql
|
12
|
+
|
13
|
+
The GraphQL file name could be specified by giving the third argument:
|
14
|
+
|
15
|
+
rails g artemis:query artist artist_in_tooltip
|
16
|
+
|
17
|
+
This will create:
|
18
|
+
app/operations/artsy.rb
|
19
|
+
app/operations/artsy/artist_in_tooltip.graphql
|
20
|
+
|
21
|
+
If there are multiple services registered, the service could be specified with +--service+ option:
|
22
|
+
|
23
|
+
rails g artemis:query repository --service github
|
24
|
+
|
25
|
+
This will create:
|
26
|
+
app/operations/github.rb
|
27
|
+
app/operations/github/repository.graphql
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'graphql/schema/finder'
|
4
|
+
|
5
|
+
class Artemis::QueryGenerator < Rails::Generators::Base
|
6
|
+
source_root File.expand_path('../templates', __FILE__)
|
7
|
+
|
8
|
+
argument :query_type, type: :string, required: true, banner: "Query type"
|
9
|
+
argument :graphql_file_name, type: :string, required: false, default: nil, banner: "The name of the GraphQL file to be generated"
|
10
|
+
|
11
|
+
class_option :service, type: :string, default: nil, aliases: "-A"
|
12
|
+
|
13
|
+
def generate_query_file
|
14
|
+
template "query.graphql", graphql_file_path
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def query_name
|
20
|
+
query_type.underscore
|
21
|
+
end
|
22
|
+
|
23
|
+
def graphql_file_path
|
24
|
+
"app/operations/#{service_name.underscore}/#{graphql_file_name.presence || query_name}.graphql"
|
25
|
+
end
|
26
|
+
|
27
|
+
def arguments
|
28
|
+
target_query.arguments
|
29
|
+
end
|
30
|
+
|
31
|
+
def target_query
|
32
|
+
schema.find("Query").fields[query_type] ||
|
33
|
+
raise(GraphQL::Schema::Finder::MemberNotFoundError, "Could not find type `#{query_type}` in schema.")
|
34
|
+
end
|
35
|
+
|
36
|
+
def schema
|
37
|
+
service_name.camelize.constantize.endpoint.schema
|
38
|
+
end
|
39
|
+
|
40
|
+
def service_name
|
41
|
+
options['service'].presence || begin
|
42
|
+
services = Artemis::GraphQLEndpoint.registered_services
|
43
|
+
|
44
|
+
if services.size == 1
|
45
|
+
services.first
|
46
|
+
else
|
47
|
+
fail "Please specify a service name (available services: #{services.join(", ")}):\n\n" \
|
48
|
+
" rails g artemis:query #{query_type} #{graphql_file_name} --service SERVICE"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
data/spec/adapters_spec.rb
CHANGED
@@ -49,6 +49,14 @@ describe 'Adapters' do
|
|
49
49
|
end
|
50
50
|
|
51
51
|
shared_examples 'an adapter' do
|
52
|
+
describe '#initialize' do
|
53
|
+
it 'requires an url' do
|
54
|
+
expect do
|
55
|
+
adapter.class.new(nil, service_name: nil, timeout: 2, pool_size: 5)
|
56
|
+
end.to raise_error(ArgumentError, "url is required (given `nil')")
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
52
60
|
describe '#execute' do
|
53
61
|
it 'makes an actual HTTP request' do
|
54
62
|
response = adapter.execute(
|
metadata
CHANGED
@@ -1,14 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: artemis
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
|
+
- Jon Allured
|
7
8
|
- Yuki Nishijima
|
8
9
|
autorequire:
|
9
10
|
bindir: bin
|
10
11
|
cert_chain: []
|
11
|
-
date: 2018-10-
|
12
|
+
date: 2018-10-30 00:00:00.000000000 Z
|
12
13
|
dependencies:
|
13
14
|
- !ruby/object:Gem::Dependency
|
14
15
|
name: activesupport
|
@@ -138,6 +139,7 @@ dependencies:
|
|
138
139
|
version: '3.8'
|
139
140
|
description: GraphQL client on Rails + Convention over Configuration = ❤️
|
140
141
|
email:
|
142
|
+
- jon.allured@gmail.com
|
141
143
|
- yk.nishijima@gmail.com
|
142
144
|
executables: []
|
143
145
|
extensions: []
|
@@ -147,6 +149,7 @@ files:
|
|
147
149
|
- ".rspec"
|
148
150
|
- ".travis.yml"
|
149
151
|
- Appraisals
|
152
|
+
- CHANGELOG.md
|
150
153
|
- CODE_OF_CONDUCT.md
|
151
154
|
- Gemfile
|
152
155
|
- LICENSE.txt
|
@@ -155,6 +158,8 @@ files:
|
|
155
158
|
- artemis.gemspec
|
156
159
|
- bin/console
|
157
160
|
- bin/setup
|
161
|
+
- doc/CHANGELOG.md.erb
|
162
|
+
- doc/changelog_generator.rb
|
158
163
|
- gemfiles/.bundle/config
|
159
164
|
- gemfiles/rails_42.gemfile
|
160
165
|
- gemfiles/rails_50.gemfile
|
@@ -173,11 +178,16 @@ files:
|
|
173
178
|
- lib/artemis/graphql_endpoint.rb
|
174
179
|
- lib/artemis/railtie.rb
|
175
180
|
- lib/artemis/version.rb
|
176
|
-
- lib/generators/artemis/USAGE
|
177
|
-
- lib/generators/artemis/install_generator.rb
|
178
|
-
- lib/generators/artemis/templates
|
179
|
-
- lib/generators/artemis/templates/
|
180
|
-
- lib/generators/artemis/
|
181
|
+
- lib/generators/artemis/install/USAGE
|
182
|
+
- lib/generators/artemis/install/install_generator.rb
|
183
|
+
- lib/generators/artemis/install/templates/client.rb
|
184
|
+
- lib/generators/artemis/install/templates/graphql.yml
|
185
|
+
- lib/generators/artemis/mutation/USAGE
|
186
|
+
- lib/generators/artemis/mutation/mutation_generator.rb
|
187
|
+
- lib/generators/artemis/mutation/templates/mutation.graphql
|
188
|
+
- lib/generators/artemis/query/USAGE
|
189
|
+
- lib/generators/artemis/query/query_generator.rb
|
190
|
+
- lib/generators/artemis/query/templates/query.graphql
|
181
191
|
- lib/tasks/artemis.rake
|
182
192
|
- spec/adapters_spec.rb
|
183
193
|
- spec/autoloading_spec.rb
|
File without changes
|
@@ -1,19 +0,0 @@
|
|
1
|
-
default: &default
|
2
|
-
adapter: :net_http
|
3
|
-
timeout: 10
|
4
|
-
pool_size: 25
|
5
|
-
|
6
|
-
development:
|
7
|
-
<%= file_name %>:
|
8
|
-
<<: *default
|
9
|
-
url: <%= endpoint_url %>
|
10
|
-
|
11
|
-
test:
|
12
|
-
<%= file_name %>:
|
13
|
-
<<: *default
|
14
|
-
url: <%= endpoint_url %>
|
15
|
-
|
16
|
-
production:
|
17
|
-
<%= file_name %>:
|
18
|
-
<<: *default
|
19
|
-
url: <%= endpoint_url %>
|