artemis 0.1.0 → 0.2.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/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 %>
|