artemis 0.5.1 → 0.7.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.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +64 -0
  3. data/.gitignore +1 -0
  4. data/Appraisals +12 -20
  5. data/CHANGELOG.md +45 -0
  6. data/Gemfile +2 -0
  7. data/README.md +71 -6
  8. data/artemis.gemspec +0 -1
  9. data/gemfiles/rails_50.gemfile +3 -1
  10. data/gemfiles/rails_51.gemfile +3 -1
  11. data/gemfiles/rails_52.gemfile +3 -1
  12. data/gemfiles/rails_60.gemfile +3 -1
  13. data/gemfiles/{rails_40.gemfile → rails_61.gemfile} +5 -3
  14. data/gemfiles/{rails_41.gemfile → rails_70.gemfile} +5 -4
  15. data/gemfiles/rails_edge.gemfile +2 -0
  16. data/lib/artemis/adapters/abstract_adapter.rb +1 -1
  17. data/lib/artemis/adapters/curb_adapter.rb +17 -7
  18. data/lib/artemis/adapters/multi_domain_adapter.rb +58 -0
  19. data/lib/artemis/adapters/net_http_adapter.rb +25 -16
  20. data/lib/artemis/adapters/net_http_persistent_adapter.rb +7 -1
  21. data/lib/artemis/adapters/test_adapter.rb +22 -3
  22. data/lib/artemis/adapters.rb +1 -0
  23. data/lib/artemis/client.rb +42 -8
  24. data/lib/artemis/graphql_endpoint.rb +11 -5
  25. data/lib/artemis/railtie.rb +16 -8
  26. data/lib/artemis/test_helper.rb +2 -1
  27. data/lib/artemis/version.rb +1 -1
  28. data/lib/generators/artemis/mutation/templates/mutation.graphql +1 -1
  29. data/lib/generators/artemis/query/query_generator.rb +14 -2
  30. data/lib/generators/artemis/query/templates/fixture.yml +19 -0
  31. data/lib/generators/artemis/query/templates/query.graphql +2 -2
  32. data/spec/adapters_spec.rb +165 -10
  33. data/spec/autoloading_spec.rb +4 -4
  34. data/spec/client_spec.rb +38 -2
  35. data/spec/fixtures/metaphysics/{_artist_fragment.graphql → _artist_fields.graphql} +0 -0
  36. data/spec/fixtures/metaphysics/artists.graphql +1 -1
  37. data/spec/fixtures/responses/{spotify → spotify_client}/artist.yml +0 -0
  38. data/spec/test_helper_spec.rb +10 -1
  39. metadata +13 -26
  40. data/.travis.yml +0 -69
  41. data/gemfiles/rails_42.gemfile +0 -12
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6d63a9c3c0a9082fd9b93c55499fae9865425d01178c4ec637fb96a16acc9a3f
4
- data.tar.gz: c0892fe01fe9b5d9c6c4e8b19c13d313e8efc75c1b3235db141cacd472f0dff6
3
+ metadata.gz: 7b64a8fd49d4d32ab4fd0dbda0f20005b68129fc1eee01de0f525424a19d1d28
4
+ data.tar.gz: eae9f56e2f7bbd832278cf1546c931824ea5f7bfa969e28cdf9dbe2e8a6e4ab6
5
5
  SHA512:
6
- metadata.gz: efced6e7780065414b95d47248f6f2b71055ed1512a5a19b2f86c82dcb42e9ef205894935ec8a0f3a106dfd109cb8bf12f978ceeb3357e6caac61a6a7818cce7
7
- data.tar.gz: a45b2cb975cb8399a3a9e26e6dc4abdbf8afeaeead80ace29a2f6fd9c31966d7ede04724a53d23cb7abfa1e1611ea9d30839be4eb2677e71e9fce846cd5d27d4
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
@@ -8,3 +8,4 @@
8
8
  /tmp/
9
9
  Gemfile.lock
10
10
  gemfiles/*.lock
11
+ .byebug_history
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
@@ -7,3 +7,5 @@ gemspec
7
7
 
8
8
  gem 'pry'
9
9
  gem 'pry-byebug', platforms: :mri
10
+ gem 'curb', '>= 0.9.6' if RUBY_ENGINE == 'ruby'
11
+ gem 'webrick' if RUBY_VERSION >= '3.0.0'
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Artemis [![Build Status](https://travis-ci.org/yuki24/artemis.svg?branch=master)](https://travis-ci.org/yuki24/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
- Youcan use the `before_execute` callback to intercept outgoing requests and the `after_execute` callback to observe the
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
- | `:test` | N/A (See Testing)
210
+ | `:multi_domain` | See [multi domain support](#multi-domain-support)
211
+ | `:test` | See [Testing](#testing)
147
212
 
148
- ### Third-party adapters
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
- ### Writing your own adapter
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"
@@ -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"
@@ -2,9 +2,11 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "rails", "~> 5.1.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.1.0"
8
10
  gem "railties", "~> 5.1.0"
9
11
  gem "activesupport", "~> 5.1.0"
10
12
 
@@ -2,9 +2,11 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "rails", "~> 5.2.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.2.0"
8
10
  gem "railties", "~> 5.2.0"
9
11
  gem "activesupport", "~> 5.2.0"
10
12
 
@@ -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 "railties", "~> 4.0.0"
9
- gem "activesupport", "~> 4.0.0"
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 "railties", "~> 4.1.0"
9
- gem "activesupport", "~> 4.1.0"
10
- gem "minitest", "5.10.3"
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: "../"
@@ -10,5 +10,7 @@ end
10
10
 
11
11
  gem "pry"
12
12
  gem "pry-byebug", platforms: :mri
13
+ gem "curb", ">= 0.9.6"
14
+ gem "webrick"
13
15
 
14
16
  gemspec path: "../"
@@ -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 execute(document:, operation_name: nil, variables: {}, context: {})
24
- easy = Curl::Easy.new(uri.to_s)
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
- easy.timeout = timeout
32
- easy.multi = multi
33
- easy.headers = DEFAULT_HEADERS.merge(headers(context))
34
- easy.post_body = JSON.generate(body)
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' => {}
@@ -7,6 +7,7 @@ module Artemis
7
7
  extend ActiveSupport::Autoload
8
8
 
9
9
  autoload :CurbAdapter
10
+ autoload :MultiDomainAdapter
10
11
  autoload :NetHttpAdapter
11
12
  autoload :NetHttpPersistentAdapter
12
13
  autoload :TestAdapter