artemis 0.5.1 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ruby.yml +64 -0
- data/.gitignore +1 -0
- data/Appraisals +12 -20
- data/CHANGELOG.md +45 -0
- data/Gemfile +2 -0
- data/README.md +71 -6
- data/artemis.gemspec +0 -1
- data/gemfiles/rails_50.gemfile +3 -1
- data/gemfiles/rails_51.gemfile +3 -1
- data/gemfiles/rails_52.gemfile +3 -1
- data/gemfiles/rails_60.gemfile +3 -1
- data/gemfiles/{rails_40.gemfile → rails_61.gemfile} +5 -3
- data/gemfiles/{rails_41.gemfile → rails_70.gemfile} +5 -4
- data/gemfiles/rails_edge.gemfile +2 -0
- data/lib/artemis/adapters/abstract_adapter.rb +1 -1
- data/lib/artemis/adapters/curb_adapter.rb +17 -7
- data/lib/artemis/adapters/multi_domain_adapter.rb +58 -0
- data/lib/artemis/adapters/net_http_adapter.rb +25 -16
- data/lib/artemis/adapters/net_http_persistent_adapter.rb +7 -1
- data/lib/artemis/adapters/test_adapter.rb +22 -3
- data/lib/artemis/adapters.rb +1 -0
- data/lib/artemis/client.rb +42 -8
- data/lib/artemis/graphql_endpoint.rb +11 -5
- data/lib/artemis/railtie.rb +16 -8
- data/lib/artemis/test_helper.rb +2 -1
- data/lib/artemis/version.rb +1 -1
- data/lib/generators/artemis/mutation/templates/mutation.graphql +1 -1
- data/lib/generators/artemis/query/query_generator.rb +14 -2
- data/lib/generators/artemis/query/templates/fixture.yml +19 -0
- data/lib/generators/artemis/query/templates/query.graphql +2 -2
- data/spec/adapters_spec.rb +165 -10
- data/spec/autoloading_spec.rb +4 -4
- data/spec/client_spec.rb +38 -2
- data/spec/fixtures/metaphysics/{_artist_fragment.graphql → _artist_fields.graphql} +0 -0
- data/spec/fixtures/metaphysics/artists.graphql +1 -1
- data/spec/fixtures/responses/{spotify → spotify_client}/artist.yml +0 -0
- data/spec/test_helper_spec.rb +10 -1
- metadata +13 -26
- data/.travis.yml +0 -69
- data/gemfiles/rails_42.gemfile +0 -12
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7b64a8fd49d4d32ab4fd0dbda0f20005b68129fc1eee01de0f525424a19d1d28
|
4
|
+
data.tar.gz: eae9f56e2f7bbd832278cf1546c931824ea5f7bfa969e28cdf9dbe2e8a6e4ab6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 061e522011314b8f3a712a1a13c504653e7b7979e17ff239298d98742f08ae3fea14cd9772970dd72dd1c9e361572d6c70b076f3b0e4ee552f9d1fc1e2a7e69d
|
7
|
+
data.tar.gz: 2d1fec853399e9ec52b74e3c5c7eae407a9affe39222f7719d993f098ef839cd2da72dfbb353f967c025c2d72b464dbdc3f56dadfa4e596b2d9ea32c43d6119f
|
@@ -0,0 +1,64 @@
|
|
1
|
+
name: build
|
2
|
+
|
3
|
+
on:
|
4
|
+
- push
|
5
|
+
- pull_request
|
6
|
+
|
7
|
+
jobs:
|
8
|
+
build:
|
9
|
+
strategy:
|
10
|
+
matrix:
|
11
|
+
ruby_version:
|
12
|
+
- '3.1'
|
13
|
+
- '3.0'
|
14
|
+
- '2.7'
|
15
|
+
- '2.6'
|
16
|
+
# - 'jruby-9.2.17.0'
|
17
|
+
gemfile:
|
18
|
+
- gemfiles/rails_70.gemfile
|
19
|
+
- gemfiles/rails_61.gemfile
|
20
|
+
- gemfiles/rails_60.gemfile
|
21
|
+
- gemfiles/rails_52.gemfile
|
22
|
+
- gemfiles/rails_51.gemfile
|
23
|
+
- gemfiles/rails_50.gemfile
|
24
|
+
# - gemfiles/rails_edge.gemfile
|
25
|
+
exclude:
|
26
|
+
- ruby_version: '3.1'
|
27
|
+
gemfile: gemfiles/rails_61.gemfile
|
28
|
+
- ruby_version: '3.1'
|
29
|
+
gemfile: gemfiles/rails_60.gemfile
|
30
|
+
- ruby_version: '3.1'
|
31
|
+
gemfile: gemfiles/rails_52.gemfile
|
32
|
+
- ruby_version: '3.1'
|
33
|
+
gemfile: gemfiles/rails_51.gemfile
|
34
|
+
- ruby_version: '3.1'
|
35
|
+
gemfile: gemfiles/rails_50.gemfile
|
36
|
+
- ruby_version: '3.0'
|
37
|
+
gemfile: gemfiles/rails_52.gemfile
|
38
|
+
- ruby_version: '3.0'
|
39
|
+
gemfile: gemfiles/rails_51.gemfile
|
40
|
+
- ruby_version: '3.0'
|
41
|
+
gemfile: gemfiles/rails_50.gemfile
|
42
|
+
- ruby_version: '2.7'
|
43
|
+
gemfile: gemfiles/rails_52.gemfile
|
44
|
+
- ruby_version: '2.7'
|
45
|
+
gemfile: gemfiles/rails_51.gemfile
|
46
|
+
- ruby_version: '2.7'
|
47
|
+
gemfile: gemfiles/rails_50.gemfile
|
48
|
+
- ruby_version: '2.6'
|
49
|
+
gemfile: gemfiles/rails_70.gemfile
|
50
|
+
# - ruby_version: '2.6'
|
51
|
+
# gemfile: gemfiles/rails_edge.gemfile
|
52
|
+
runs-on: ubuntu-18.04
|
53
|
+
env:
|
54
|
+
BUNDLE_GEMFILE: ${{ matrix.gemfile }}
|
55
|
+
steps:
|
56
|
+
- uses: actions/checkout@v2
|
57
|
+
- name: Install curl
|
58
|
+
run: sudo apt-get install curl libcurl4-openssl-dev
|
59
|
+
- name: Set up Ruby
|
60
|
+
uses: ruby/setup-ruby@v1
|
61
|
+
with:
|
62
|
+
ruby-version: ${{ matrix.ruby_version }}
|
63
|
+
bundler-cache: true
|
64
|
+
- run: bundle exec rake
|
data/.gitignore
CHANGED
data/Appraisals
CHANGED
@@ -6,6 +6,18 @@ appraise "rails_edge" do
|
|
6
6
|
end
|
7
7
|
end
|
8
8
|
|
9
|
+
appraise "rails_70" do
|
10
|
+
gem "rails", '~> 7.0.0'
|
11
|
+
gem "railties", '~> 7.0.0'
|
12
|
+
gem "activesupport", '~> 7.0.0'
|
13
|
+
end
|
14
|
+
|
15
|
+
appraise "rails_61" do
|
16
|
+
gem "rails", '~> 6.1.0'
|
17
|
+
gem "railties", '~> 6.1.0'
|
18
|
+
gem "activesupport", '~> 6.1.0'
|
19
|
+
end
|
20
|
+
|
9
21
|
appraise "rails_60" do
|
10
22
|
gem "rails", '~> 6.0.0.rc1'
|
11
23
|
gem "railties", '~> 6.0.0.rc1'
|
@@ -30,23 +42,3 @@ appraise "rails_50" do
|
|
30
42
|
gem "activesupport", '~> 5.0.0'
|
31
43
|
gem "minitest", '5.10.3'
|
32
44
|
end
|
33
|
-
|
34
|
-
appraise "rails_42" do
|
35
|
-
gem "rails", '~> 4.2.0'
|
36
|
-
gem "railties", '~> 4.2.0'
|
37
|
-
gem "activesupport", '~> 4.2.0'
|
38
|
-
gem "minitest", '5.10.3'
|
39
|
-
end
|
40
|
-
|
41
|
-
appraise "rails_41" do
|
42
|
-
gem "rails", '~> 4.1.0'
|
43
|
-
gem "railties", '~> 4.1.0'
|
44
|
-
gem "activesupport", '~> 4.1.0'
|
45
|
-
gem "minitest", '5.10.3'
|
46
|
-
end
|
47
|
-
|
48
|
-
appraise "rails_40" do
|
49
|
-
gem "rails", '~> 4.0.0'
|
50
|
-
gem "railties", '~> 4.0.0'
|
51
|
-
gem "activesupport", '~> 4.0.0'
|
52
|
-
end
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,48 @@
|
|
1
|
+
|
2
|
+
## v0.7.0
|
3
|
+
|
4
|
+
_<sup>unreleased</sup>_
|
5
|
+
|
6
|
+
#### Features
|
7
|
+
|
8
|
+
- Add support for Ruby 3.1 and Rails 7.0
|
9
|
+
- Add support for [the Multiplex query](https://graphql-ruby.org/queries/multiplex.html)
|
10
|
+
- Do not allow the usage of the `multi_domain` adapter to be nested ([<tt>9b7b520</tt>](https://github.com/yuki24/artemis/commit/9b7b5202c9fbe424d4ca22f05dc9c9759b5202c3))
|
11
|
+
- Do not require fragment files to end with `_fragment.graphql` ([<tt>3c6c0fa</tt>](https://github.com/yuki24/artemis/commit/3c6c0fa))
|
12
|
+
- Allow for overriding the namespace used for resolving graphql file paths ([<tt>bd18762</tt>](https://github.com/yuki24/artemis/commit/bd18762))
|
13
|
+
|
14
|
+
## [v0.6.0](https://github.com/yuki24/artemis/tree/v0.6.0)
|
15
|
+
|
16
|
+
_<sup>released at 2021-09-03 04:17:55 UTC</sup>_
|
17
|
+
|
18
|
+
#### Features
|
19
|
+
|
20
|
+
- Add support for Ruby 3.0 and Rails 6.0, 6.1
|
21
|
+
- Add the multi domain adapter ([<tt>744b8ea</tt>](https://github.com/yuki24/artemis/commit/744b8ea35795b4e6cc4fdc1ebb63dd9a4e9819f0))
|
22
|
+
|
23
|
+
#### Fixes
|
24
|
+
|
25
|
+
- ~~Generate fixture YAML files on `rails g artemis:query queryName` ([#78](https://github.com/yuki24/artemis/pull/78))~~ Removed due to a bug for now.
|
26
|
+
- Address warnings from Ruby 2.7 ([<tt>408adcb</tt>](https://github.com/yuki24/artemis/commit/408adcb3f39912f7afb7b3690a52f1d593662b7b))
|
27
|
+
- Avoid crashing when config/graphql.yml does not exist ([@dlackty](https://github.com/dlackty), [#76](https://github.com/yuki24/artemis/pull/76))
|
28
|
+
|
29
|
+
## [v0.5.2](https://github.com/yuki24/artemis/tree/v0.5.2)
|
30
|
+
|
31
|
+
_<sup>released at 2019-07-26 03:21:43 UTC</sup>_
|
32
|
+
|
33
|
+
#### Fixes
|
34
|
+
|
35
|
+
- Fixes an issue where fixtures can not be looked up properly when the client has two or more words in its name ([@JanStevens](https://github.com/JanStevens), issue: [#72](https://github.com/yuki24/artemis/issues/72), PR: [#73](https://github.com/yuki24/artemis/pull/73))
|
36
|
+
|
37
|
+
## [v0.5.1](https://github.com/yuki24/artemis/tree/v0.5.1)
|
38
|
+
|
39
|
+
_<sup>released at 2019-07-01 14:25:35 UTC</sup>_
|
40
|
+
|
41
|
+
#### Fixes
|
42
|
+
|
43
|
+
- Fixes an issue where callbacks are shared across all clients ([@JanStevens](https://github.com/JanStevens), issue: [#69](https://github.com/yuki24/artemis/issues/69), PR: [#70](https://github.com/yuki24/artemis/pull/70))
|
44
|
+
- Fixes an issue where fixtures with the same name cause a conflict ([@JanStevens](https://github.com/JanStevens), Issue: [#68](https://github.com/yuki24/artemis/issues/68), commit: [<tt>e1f57f4</tt>](https://github.com/yuki24/artemis/commit/e1f57f49ebb032553d7a6f70e48422fc9825c119))
|
45
|
+
|
1
46
|
## [v0.5.0](https://github.com/yuki24/artemis/tree/v0.5.0)
|
2
47
|
|
3
48
|
_<sup>released at 2019-06-02 22:01:57 UTC</sup>_
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Artemis [![
|
1
|
+
# Artemis [![build](https://github.com/yuki24/artemis/actions/workflows/ruby.yml/badge.svg)](https://github.com/yuki24/artemis/actions/workflows/ruby.yml) [![Gem Version](https://badge.fury.io/rb/artemis.svg)](https://rubygems.org/gems/artemis)
|
2
2
|
|
3
3
|
Artemis is a GraphQL client that is designed to fit well on Rails.
|
4
4
|
|
@@ -51,7 +51,7 @@ query($id: String!) {
|
|
51
51
|
}
|
52
52
|
```
|
53
53
|
|
54
|
-
Then you could the class method that has the matching name `artist`:
|
54
|
+
Then you could call the class method that has the matching name `artist`:
|
55
55
|
|
56
56
|
```ruby
|
57
57
|
Artsy.artist(id: "pablo-picasso")
|
@@ -92,9 +92,35 @@ Artemis assumes that the files related to GraphQL are organized in a certain way
|
|
92
92
|
└──vendor/graphql/schema/artsy.json
|
93
93
|
```
|
94
94
|
|
95
|
+
### Fragments
|
96
|
+
Fragments are defined in defined in a standard way in a file named `_artwork_fragment.graphql` with the standard convention:
|
97
|
+
|
98
|
+
```graphql
|
99
|
+
fragment on Artwork {
|
100
|
+
id,
|
101
|
+
name,
|
102
|
+
artist_id
|
103
|
+
# other artwork fields here
|
104
|
+
}
|
105
|
+
```
|
106
|
+
|
107
|
+
The way of calling an Artemis fragment on other queries or models is with a **Rails convention**. Let us suppose we have the Artist model and its corresponding artwork. The way of nesting or calling the artwork on the artist model would look like this:
|
108
|
+
|
109
|
+
```graphql
|
110
|
+
fragment on Artist {
|
111
|
+
id,
|
112
|
+
name,
|
113
|
+
artworks {
|
114
|
+
...Artsy::ArtworkFragment
|
115
|
+
}
|
116
|
+
}
|
117
|
+
```
|
118
|
+
|
119
|
+
Where `Artsy` is the name of the folder/module.
|
120
|
+
|
95
121
|
## Callbacks
|
96
122
|
|
97
|
-
|
123
|
+
You can use the `before_execute` callback to intercept outgoing requests and the `after_execute` callback to observe the
|
98
124
|
response. A common operation that's done in the `before_execute` hook is assigning a token to the header:
|
99
125
|
|
100
126
|
```ruby
|
@@ -121,6 +147,44 @@ class Artsy < Artemis::Client
|
|
121
147
|
end
|
122
148
|
```
|
123
149
|
|
150
|
+
## Multi domain support
|
151
|
+
|
152
|
+
Services like Shopify provide
|
153
|
+
[a different endpoint per customer](https://shopify.dev/api/admin/graphql/reference#graphql-endpoint) (e.g.
|
154
|
+
`https://{shop}.myshopify.com`). In order to switch the endpoint on a per-request basis, you will have to use the
|
155
|
+
`:multi_domain` adapter. This is a wrapper adapter that relies on an actual HTTP adapter such as `:net_http` and
|
156
|
+
`:curb` so that e.g. it can maintain multiple connections for each endpoint if necessary. This could be configured
|
157
|
+
as shown below:
|
158
|
+
|
159
|
+
```yaml
|
160
|
+
default: &default
|
161
|
+
# Specify the :multi_domain adapter:
|
162
|
+
adapter: :multi_domain
|
163
|
+
|
164
|
+
# Other configurations such as `timeout` and `pool_size` are passed down to the underlying adapter:
|
165
|
+
timeout: 10
|
166
|
+
pool_size: 25
|
167
|
+
|
168
|
+
# Additional adapter-specific configurations could be configured as `adapter_options`:
|
169
|
+
adapter_options:
|
170
|
+
# Here you can configure the actual adapter to use. By default, it is set to :net_http. Available adapters are
|
171
|
+
# :net_http, :net_http_persistent, :curb, and :test. You can not nest the use of the `:multi_domain` adapter.
|
172
|
+
adapter: :net_http
|
173
|
+
|
174
|
+
development:
|
175
|
+
shopify:
|
176
|
+
<<: *default
|
177
|
+
|
178
|
+
...
|
179
|
+
```
|
180
|
+
|
181
|
+
Upon making a request you will also have to specify the `url` option:
|
182
|
+
|
183
|
+
```ruby
|
184
|
+
# Makes a request to https://myawesomeshop.myshopify.com:
|
185
|
+
Shopify.with_context(url: "https://myawesomeshop.myshopify.com").product(id: "...")
|
186
|
+
```
|
187
|
+
|
124
188
|
## Configuration
|
125
189
|
|
126
190
|
You can configure the GraphQL client using the following options. Those configurations are found in the
|
@@ -143,9 +207,10 @@ There are four adapter options available. Choose the adapter that best fits on y
|
|
143
207
|
| `:curb` | HTTP/1.1, **HTTP/2** | **Yes** | **Fastest** | [`curb 0.9.6+`][curb]<br>[`libcurl 7.64.0+`][curl]<br>[`nghttp2 1.0.0+`][nghttp]
|
144
208
|
| `:net_http` (default) | HTTP/1.1 only | No | Slow | **None**
|
145
209
|
| `:net_http_persistent` | HTTP/1.1 only | **Yes** | **Fast** | [`net-http-persistent 3.0.0+`][nhp]
|
146
|
-
| `:
|
210
|
+
| `:multi_domain` | See [multi domain support](#multi-domain-support)
|
211
|
+
| `:test` | See [Testing](#testing)
|
147
212
|
|
148
|
-
|
213
|
+
#### Third-party adapters
|
149
214
|
|
150
215
|
This is a comminuty-maintained adapter. Want to add yours? Send us a pull request!
|
151
216
|
|
@@ -153,7 +218,7 @@ This is a comminuty-maintained adapter. Want to add yours? Send us a pull reques
|
|
153
218
|
| ---------------------- | ------------|
|
154
219
|
| [`:net_http_hmac`](https://github.com/JanStevens/artemis-api-auth/tree/master) | provides a new Adapter for the Artemis GraphQL ruby client to support HMAC Authentication using [ApiAuth](https://github.com/mgomes/api_auth). |
|
155
220
|
|
156
|
-
|
221
|
+
#### Writing your own adapter
|
157
222
|
|
158
223
|
When the built-in adapters do not satisfy your needs, you may want to implement your own adapter. You could do so by following the steps below. Let's implement the [`:net_http_hmac`](https://github.com/JanStevens/artemis-api-auth/tree/master) adapter as an example.
|
159
224
|
|
data/artemis.gemspec
CHANGED
@@ -22,7 +22,6 @@ Gem::Specification.new do |spec|
|
|
22
22
|
|
23
23
|
spec.add_development_dependency "appraisal", ">= 2.2"
|
24
24
|
spec.add_development_dependency "bundler", ">= 1.16"
|
25
|
-
spec.add_development_dependency "curb", ">= 0.9.6"
|
26
25
|
spec.add_development_dependency "net-http-persistent", ">= 3.0"
|
27
26
|
spec.add_development_dependency "rake", ">= 10.0"
|
28
27
|
spec.add_development_dependency "rspec", ">= 3.8"
|
data/gemfiles/rails_50.gemfile
CHANGED
@@ -2,9 +2,11 @@
|
|
2
2
|
|
3
3
|
source "https://rubygems.org"
|
4
4
|
|
5
|
-
gem "rails", "~> 5.0.0"
|
6
5
|
gem "pry"
|
7
6
|
gem "pry-byebug", platforms: :mri
|
7
|
+
gem "curb", ">= 0.9.6"
|
8
|
+
gem "webrick"
|
9
|
+
gem "rails", "~> 5.0.0"
|
8
10
|
gem "railties", "~> 5.0.0"
|
9
11
|
gem "activesupport", "~> 5.0.0"
|
10
12
|
gem "minitest", "5.10.3"
|
data/gemfiles/rails_51.gemfile
CHANGED
data/gemfiles/rails_52.gemfile
CHANGED
data/gemfiles/rails_60.gemfile
CHANGED
@@ -2,9 +2,11 @@
|
|
2
2
|
|
3
3
|
source "https://rubygems.org"
|
4
4
|
|
5
|
-
gem "rails", "~> 6.0.0.rc1"
|
6
5
|
gem "pry"
|
7
6
|
gem "pry-byebug", platforms: :mri
|
7
|
+
gem "curb", ">= 0.9.6"
|
8
|
+
gem "webrick"
|
9
|
+
gem "rails", "~> 6.0.0.rc1"
|
8
10
|
gem "railties", "~> 6.0.0.rc1"
|
9
11
|
gem "activesupport", "~> 6.0.0.rc1"
|
10
12
|
|
@@ -2,10 +2,12 @@
|
|
2
2
|
|
3
3
|
source "https://rubygems.org"
|
4
4
|
|
5
|
-
gem "rails", "~> 4.0.0"
|
6
5
|
gem "pry"
|
7
6
|
gem "pry-byebug", platforms: :mri
|
8
|
-
gem "
|
9
|
-
gem "
|
7
|
+
gem "curb", ">= 0.9.6"
|
8
|
+
gem "webrick"
|
9
|
+
gem "rails", "~> 6.1.0"
|
10
|
+
gem "railties", "~> 6.1.0"
|
11
|
+
gem "activesupport", "~> 6.1.0"
|
10
12
|
|
11
13
|
gemspec path: "../"
|
@@ -2,11 +2,12 @@
|
|
2
2
|
|
3
3
|
source "https://rubygems.org"
|
4
4
|
|
5
|
-
gem "rails", "~> 4.1.0"
|
6
5
|
gem "pry"
|
7
6
|
gem "pry-byebug", platforms: :mri
|
8
|
-
gem "
|
9
|
-
gem "
|
10
|
-
gem "
|
7
|
+
gem "curb", ">= 0.9.6"
|
8
|
+
gem "webrick"
|
9
|
+
gem "rails", "~> 7.0.0"
|
10
|
+
gem "railties", "~> 7.0.0"
|
11
|
+
gem "activesupport", "~> 7.0.0"
|
11
12
|
|
12
13
|
gemspec path: "../"
|
data/gemfiles/rails_edge.gemfile
CHANGED
@@ -15,7 +15,7 @@ module Artemis
|
|
15
15
|
"Content-Type" => "application/json"
|
16
16
|
}.freeze
|
17
17
|
|
18
|
-
def initialize(uri, service_name: , timeout: , pool_size: )
|
18
|
+
def initialize(uri, service_name: , timeout: , pool_size: , adapter_options: {})
|
19
19
|
raise ArgumentError, "url is required (given `#{uri.inspect}')" if uri.blank?
|
20
20
|
|
21
21
|
super(uri) # Do not pass in the block to avoid getting #headers and #connection overridden.
|
@@ -13,25 +13,35 @@ module Artemis
|
|
13
13
|
class CurbAdapter < AbstractAdapter
|
14
14
|
attr_reader :multi
|
15
15
|
|
16
|
-
def initialize(uri, service_name: , timeout: , pool_size: )
|
16
|
+
def initialize(uri, service_name: , timeout: , pool_size: , adapter_options: {})
|
17
17
|
super
|
18
18
|
|
19
19
|
@multi = Curl::Multi.new
|
20
20
|
@multi.pipeline = Curl::CURLPIPE_MULTIPLEX if defined?(Curl::CURLPIPE_MULTIPLEX)
|
21
21
|
end
|
22
22
|
|
23
|
-
def
|
24
|
-
|
23
|
+
def multiplex(queries, context: {})
|
24
|
+
make_request({ _json: queries }, context)
|
25
|
+
end
|
25
26
|
|
27
|
+
def execute(document:, operation_name: nil, variables: {}, context: {})
|
26
28
|
body = {}
|
27
29
|
body["query"] = document.to_query_string
|
28
30
|
body["variables"] = variables if variables.any?
|
29
31
|
body["operationName"] = operation_name if operation_name
|
30
32
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
33
|
+
make_request(body, context)
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def make_request(body, context)
|
39
|
+
easy = Curl::Easy.new(uri.to_s)
|
40
|
+
|
41
|
+
easy.timeout = timeout
|
42
|
+
easy.multi = multi
|
43
|
+
easy.headers = DEFAULT_HEADERS.merge(headers(context))
|
44
|
+
easy.post_body = JSON.generate(body)
|
35
45
|
|
36
46
|
if defined?(Curl::CURLPIPE_MULTIPLEX)
|
37
47
|
# This ensures libcurl waits for the connection to reveal if it is
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'artemis/adapters/abstract_adapter'
|
4
|
+
|
5
|
+
module Artemis
|
6
|
+
module Adapters
|
7
|
+
class MultiDomainAdapter < AbstractAdapter
|
8
|
+
attr_reader :adapter
|
9
|
+
|
10
|
+
def initialize(_uri, service_name: , timeout: , pool_size: , adapter_options: {})
|
11
|
+
if adapter_options[:adapter] == :multi_domain
|
12
|
+
raise ArgumentError, "You can not use the :multi_domain adapter with the :multi_domain adapter."
|
13
|
+
end
|
14
|
+
|
15
|
+
@connection_by_url = {}
|
16
|
+
@service_name = service_name.to_s
|
17
|
+
@timeout = timeout
|
18
|
+
@pool_size = pool_size
|
19
|
+
@adapter = adapter_options[:adapter] || :net_http
|
20
|
+
@mutex_for_connection = Mutex.new
|
21
|
+
end
|
22
|
+
|
23
|
+
def multiplex(queries, context: {})
|
24
|
+
url = context[:url]
|
25
|
+
|
26
|
+
if url.nil?
|
27
|
+
raise ArgumentError, 'The MultiDomain adapter requires a url on every request. Please specify a url with a context: ' \
|
28
|
+
'Client.multiplex(url: "https://awesomeshop.domain.conm") { ... }'
|
29
|
+
end
|
30
|
+
|
31
|
+
connection_for_url(url).multiplex(queries, context: context)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Makes an HTTP request for GraphQL query.
|
35
|
+
def execute(document:, operation_name: nil, variables: {}, context: {})
|
36
|
+
url = context[:url]
|
37
|
+
|
38
|
+
if url.nil?
|
39
|
+
raise ArgumentError, 'The MultiDomain adapter requires a url on every request. Please specify a url with a context: ' \
|
40
|
+
'Client.with_context(url: "https://awesomeshop.domain.conm")'
|
41
|
+
end
|
42
|
+
|
43
|
+
connection_for_url(url).execute(document: document, operation_name: operation_name, variables: variables, context: context)
|
44
|
+
end
|
45
|
+
|
46
|
+
def connection
|
47
|
+
raise NotImplementedError, "Calling the #connection method without a URI is not supported. Please use the " \
|
48
|
+
"#connection_for_url(uri) instead."
|
49
|
+
end
|
50
|
+
|
51
|
+
def connection_for_url(url)
|
52
|
+
@connection_by_url[url.to_s] || @mutex_for_connection.synchronize do
|
53
|
+
@connection_by_url[url.to_s] ||= ::Artemis::Adapters.lookup(adapter).new(url, service_name: service_name, timeout: timeout, pool_size: pool_size)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -9,20 +9,39 @@ require 'artemis/exceptions'
|
|
9
9
|
module Artemis
|
10
10
|
module Adapters
|
11
11
|
class NetHttpAdapter < AbstractAdapter
|
12
|
+
def multiplex(queries, context: {})
|
13
|
+
make_request({ _json: queries }, context)
|
14
|
+
end
|
15
|
+
|
12
16
|
# Makes an HTTP request for GraphQL query.
|
13
17
|
def execute(document:, operation_name: nil, variables: {}, context: {})
|
14
|
-
request = Net::HTTP::Post.new(uri.request_uri)
|
15
|
-
|
16
|
-
request.basic_auth(uri.user, uri.password) if uri.user || uri.password
|
17
|
-
|
18
|
-
DEFAULT_HEADERS.merge(headers(context)).each { |name, value| request[name] = value }
|
19
|
-
|
20
18
|
body = {}
|
21
19
|
body["query"] = document.to_query_string
|
22
20
|
body["variables"] = variables if variables.any?
|
23
21
|
body["operationName"] = operation_name if operation_name
|
22
|
+
|
23
|
+
make_request(body, context)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Returns a fresh Net::HTTP object that creates a new connection.
|
27
|
+
def connection
|
28
|
+
Net::HTTP.new(uri.host, uri.port).tap do |client|
|
29
|
+
client.use_ssl = uri.scheme == "https"
|
30
|
+
client.open_timeout = timeout
|
31
|
+
client.read_timeout = timeout
|
32
|
+
client.write_timeout = timeout if client.respond_to?(:write_timeout=)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def make_request(body, context)
|
39
|
+
request = Net::HTTP::Post.new(uri.request_uri)
|
40
|
+
request.basic_auth(uri.user, uri.password) if uri.user || uri.password
|
24
41
|
request.body = JSON.generate(body)
|
25
42
|
|
43
|
+
DEFAULT_HEADERS.merge(headers(context)).each { |name, value| request[name] = value }
|
44
|
+
|
26
45
|
response = connection.request(request)
|
27
46
|
|
28
47
|
case response.code.to_i
|
@@ -34,16 +53,6 @@ module Artemis
|
|
34
53
|
{ "errors" => [{ "message" => "#{response.code} #{response.message}" }] }
|
35
54
|
end
|
36
55
|
end
|
37
|
-
|
38
|
-
# Returns a fresh Net::HTTP object that creates a new connection.
|
39
|
-
def connection
|
40
|
-
Net::HTTP.new(uri.host, uri.port).tap do |client|
|
41
|
-
client.use_ssl = uri.scheme == "https"
|
42
|
-
client.open_timeout = timeout
|
43
|
-
client.read_timeout = timeout
|
44
|
-
client.write_timeout = timeout if client.respond_to?(:write_timeout=)
|
45
|
-
end
|
46
|
-
end
|
47
56
|
end
|
48
57
|
end
|
49
58
|
end
|
@@ -2,6 +2,12 @@
|
|
2
2
|
|
3
3
|
require 'delegate'
|
4
4
|
|
5
|
+
begin
|
6
|
+
require "active_support/isolated_execution_state"
|
7
|
+
rescue LoadError
|
8
|
+
# no-op... Rails 7.0 requires this.
|
9
|
+
end
|
10
|
+
|
5
11
|
require 'active_support/core_ext/numeric/time'
|
6
12
|
require 'net/http/persistent'
|
7
13
|
|
@@ -12,7 +18,7 @@ module Artemis
|
|
12
18
|
class NetHttpPersistentAdapter < NetHttpAdapter
|
13
19
|
attr_reader :_connection, :raw_connection
|
14
20
|
|
15
|
-
def initialize(uri, service_name: , timeout: , pool_size: )
|
21
|
+
def initialize(uri, service_name: , timeout: , pool_size: , adapter_options: {})
|
16
22
|
super
|
17
23
|
|
18
24
|
@raw_connection = Net::HTTP::Persistent.new(name: service_name, pool_size: pool_size)
|
@@ -12,20 +12,39 @@ module Artemis
|
|
12
12
|
self.responses = []
|
13
13
|
|
14
14
|
Request = Struct.new(:document, :operation_name, :variables, :context)
|
15
|
+
Multiplex = Struct.new(:queries, :context)
|
15
16
|
|
16
|
-
private_constant :Request
|
17
|
+
private_constant :Request, :Multiplex
|
17
18
|
|
18
19
|
def initialize(*)
|
19
20
|
end
|
20
21
|
|
22
|
+
def multiplex(queries, context: {})
|
23
|
+
self.requests << Multiplex.new(queries, context)
|
24
|
+
|
25
|
+
queries.map do |query|
|
26
|
+
result = responses.detect do |mock|
|
27
|
+
query[:operationName] == mock.operation_name && (mock.arguments == :__unspecified__ || query[:variables] == mock.arguments)
|
28
|
+
end
|
29
|
+
|
30
|
+
result&.data || fake_response
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
21
34
|
def execute(**arguments)
|
22
35
|
self.requests << Request.new(*arguments.values_at(:document, :operation_name, :variables, :context))
|
23
36
|
|
24
37
|
response = responses.detect do |mock|
|
25
|
-
arguments[:operation_name] == mock.operation_name && mock.arguments == :__unspecified__ || arguments[:variables] == mock.arguments
|
38
|
+
arguments[:operation_name] == mock.operation_name && (mock.arguments == :__unspecified__ || arguments[:variables] == mock.arguments)
|
26
39
|
end
|
27
40
|
|
28
|
-
response&.data ||
|
41
|
+
response&.data || fake_response
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def fake_response
|
47
|
+
{
|
29
48
|
'data' => { 'test' => 'data' },
|
30
49
|
'errors' => [],
|
31
50
|
'extensions' => {}
|