artemis 0.5.1 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
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