graphlient 0.7.0 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: aa3b9416b7374b0c3881224177f2a5646432e27ce9fcfdb2454b31ba318e231a
4
- data.tar.gz: 6ca64471c0129dc021248fa309ed187bde0c9f9e58cf07a98d9072768e5446fe
3
+ metadata.gz: ed5f47ad98345af440481f9dca800767524807bc667eace999d7ba36826e59fa
4
+ data.tar.gz: 22ec20f5b725b3bb85bc9c8e8371693824f7fd85b2db43336ce7f6a6be31543f
5
5
  SHA512:
6
- metadata.gz: f035b28bfd747c72574b124cd5130e92798ac6b14d9000b73c6ce25357489f23ded22ca11dd74cc777c96dfeada948f86a9dd3b754838abf230cf82849e8f893
7
- data.tar.gz: e6fd794359759c72051ddd7114fbe85c1110d0b743f55cd521cdcbb56fc9e6cecd4a70b6a6383f76d5304fd542d889e965d28738394995282768794059067b6c
6
+ metadata.gz: 1c5ddbaafe24dfbdd6e99535bedb7a021eba55a956b4b716661c9c8778018a96118afc5a6def029aad14da883de8d47a747b4a85ead2cee0868d5ddcfb298359
7
+ data.tar.gz: 3fd8b40976687fd83b00b865f62d514a87571de0bf68015486328d526a2d9cc76a8c4454a5d78f86acba6480860ac1abde3ff2edda1e92c26b03c58f60b067e2
@@ -14,8 +14,9 @@ jobs:
14
14
  - { ruby: 2.7.2 }
15
15
  - { ruby: 3.0.0 }
16
16
  - { ruby: 3.1.2 }
17
+ - { ruby: 3.2.2 }
17
18
  - { ruby: "ruby-head", ignore: true }
18
- - { ruby: "jruby-9.1.17", ignore: true }
19
+ - { ruby: "jruby-9.3.9.0", ignore: true }
19
20
  - { ruby: "jruby-head", ignore: true }
20
21
  steps:
21
22
  - uses: actions/checkout@v3
@@ -18,5 +18,5 @@ jobs:
18
18
  bundler-cache: true
19
19
  - run: |
20
20
  # the personal token is public, this is ok, base64 encode to avoid tripping Github
21
- TOKEN=$(echo -n NWY1ZmM5MzEyMzNlYWY4OTZiOGU3MmI3MWQ3Mzk0MzgxMWE4OGVmYwo= | base64 --decode)
21
+ TOKEN=$(echo -n Z2hwX0xNQ3VmanBFeTBvYkZVTWh6NVNqVFFBOEUxU25abzBqRUVuaAo= | base64 --decode)
22
22
  DANGER_GITHUB_API_TOKEN=$TOKEN bundle exec danger --verbose
data/CHANGELOG.md CHANGED
@@ -1,6 +1,12 @@
1
1
  ### (Next)
2
2
  * Your contribution here.
3
3
 
4
+ ### 0.8.0 (2024/01/06)
5
+ * [#110](https://github.com/ashkan18/graphlient/pull/110): Ensure correct Faraday JSON response body parsing with invalid response header - [@taylorthurlow](https://github.com/taylorthurlow).
6
+ * [#107](https://github.com/ashkan18/graphlient/pull/107): Pass in initialized schema as an option - [@kbaum](https://github.com/kbaum).
7
+ * [#106](https://github.com/ashkan18/graphlient/pull/106): Add 3.2 to the list of ruby ci versions - [@igor-drozdov](https://github.com/igor-drozdov).
8
+ * [#102](https://github.com/ashkan18/graphlient/pull/102): Update ci to test latest jruby - [@ashkan18](https://github.com/ashkan18).
9
+
4
10
  ### 0.7.0 (2022/10/11)
5
11
  * [#98](https://github.com/ashkan18/graphlient/pull/98): Bring back danger checks and improve them - [@ashkan18](https://github.com/ashkan18).
6
12
  * [#94](https://github.com/ashkan18/graphlient/pull/94): Enabled fragments - [@rellampec](https://github.com/rellampec).
data/README.md CHANGED
@@ -9,7 +9,8 @@ A friendlier Ruby client for consuming GraphQL-based APIs. Built on top of your
9
9
 
10
10
  - [Installation](#installation)
11
11
  - [Usage](#usage)
12
- - [Schema storing and loading on disk](#schema-storing-and-loading-on-disk)
12
+ - [Schema Storing and Loading on Disk](#schema-storing-and-loading-on-disk)
13
+ - [Preloading Schema Once](#preloading-schema-once)
13
14
  - [Error Handling](#error-handling)
14
15
  - [Executing Parameterized Queries and Mutations](#executing-parameterized-queries-and-mutations)
15
16
  - [Parse and Execute Queries Separately](#parse-and-execute-queries-separately)
@@ -140,6 +141,30 @@ client = Client.new(url, schema_path: 'config/your_graphql_schema.json')
140
141
  client.schema.dump! # you only need to call this when graphql schema changes
141
142
  ```
142
143
 
144
+ ### Preloading Schema Once
145
+
146
+ Even if caching the schema on disk, instantiating `Graphlient::Client` often can be both time and memory intensive due to loading the schema for each instance. This is especially true if the schema is a large file. To get around these performance issues, instantiate your schema once and pass it in as a configuration option.
147
+
148
+ One time in an initializer
149
+
150
+ ```ruby
151
+ schema = Graphlient::Schema.new(
152
+ 'https://graphql.foo.com/graphql', 'lib/graphql_schema_foo.json'
153
+ )
154
+ ```
155
+
156
+ Pass in each time you initialize a client
157
+
158
+ ```
159
+ client = Graphlient::Client.new(
160
+ 'https://graphql.foo.com/graphql',
161
+ schema: schema,
162
+ headers: {
163
+ 'Authorization' => 'Bearer 123',
164
+ }
165
+ )
166
+ ```
167
+
143
168
  ### Error Handling
144
169
 
145
170
  Unlike graphql-client, Graphlient will always raise an exception unless the query has succeeded.
@@ -341,7 +366,7 @@ invoice_query = client.parse do
341
366
  query do
342
367
  invoice(id: 10) do
343
368
  id
344
- ___Graphlient__InvoiceFragment
369
+ ___Fragments__Invoice
345
370
  end
346
371
  end
347
372
  end
@@ -1,4 +1,5 @@
1
1
  require 'faraday'
2
+ require 'json'
2
3
 
3
4
  module Graphlient
4
5
  module Adapters
@@ -13,7 +14,8 @@ module Graphlient
13
14
  variables: variables
14
15
  }.to_json
15
16
  end
16
- response.body
17
+
18
+ parse_body(response.body)
17
19
  rescue Faraday::ConnectionFailed => e
18
20
  raise Graphlient::Errors::ConnectionFailedError, e
19
21
  rescue Faraday::TimeoutError => e
@@ -39,6 +41,38 @@ module Graphlient
39
41
  end
40
42
  end
41
43
  end
44
+
45
+ private
46
+
47
+ # Faraday 2.x's JSON response middleware will only parse a JSON
48
+ # response body into a Hash (or Array) object if the response headers
49
+ # match a specific content type regex. See Faraday's response JSON
50
+ # middleware definition for specifics on what the datatype of the
51
+ # response body will be. This method will handle the situation where
52
+ # the response header is not set appropriately, but contains JSON
53
+ # anyways. If the body cannot be parsed as JSON, an exception will be
54
+ # raised.
55
+ def parse_body(body)
56
+ case body
57
+ when Hash, Array
58
+ body
59
+ when String
60
+ begin
61
+ JSON.parse(body)
62
+ rescue JSON::ParserError
63
+ raise Graphlient::Errors::ServerError, 'Failed to parse response body as JSON'
64
+ end
65
+ else
66
+ inner_exception = StandardError.new <<~ERR.strip.tr("\n", ' ')
67
+ Unexpected response body type '#{body.class}'. Graphlient doesn't
68
+ know how to handle a response body of this type, but Faraday is
69
+ returning it. Please open an issue, particularly if the response
70
+ body does actually contain valid JSON.
71
+ ERR
72
+
73
+ raise Graphlient::Errors::ClientError, inner_exception
74
+ end
75
+ end
42
76
  end
43
77
  end
44
78
  end
@@ -2,9 +2,12 @@ module Graphlient
2
2
  class Client
3
3
  attr_accessor :uri, :options
4
4
 
5
+ class InvalidConfigurationError < StandardError; end
6
+
5
7
  def initialize(url, options = {}, &_block)
6
8
  @url = url
7
9
  @options = options.dup
10
+ raise_error_if_invalid_configuration!
8
11
  yield self if block_given?
9
12
  end
10
13
 
@@ -51,11 +54,15 @@ module Graphlient
51
54
  end
52
55
 
53
56
  def schema
54
- @schema ||= Graphlient::Schema.new(http, schema_path)
57
+ @schema ||= options[:schema] || Graphlient::Schema.new(http, schema_path)
55
58
  end
56
59
 
57
60
  private
58
61
 
62
+ def raise_error_if_invalid_configuration!
63
+ raise InvalidConfigurationError, 'schema_path and schema cannot both be provided' if options.key?(:schema_path) && options.key?(:schema)
64
+ end
65
+
59
66
  def schema_path
60
67
  return options[:schema_path].to_s if options[:schema_path]
61
68
  end
@@ -1,3 +1,3 @@
1
1
  module Graphlient
2
- VERSION = '0.7.0'.freeze
2
+ VERSION = '0.8.0'.freeze
3
3
  end
@@ -75,6 +75,61 @@ describe Graphlient::Adapters::HTTP::FaradayAdapter do
75
75
  end
76
76
  end
77
77
 
78
+ context 'a non-error response without an appropriate JSON header' do
79
+ let(:url) { 'http://example.com/graphql' }
80
+ let(:headers) { { 'Content-Type' => 'application/notjson' } }
81
+ let(:client) { Graphlient::Client.new(url) }
82
+ let(:response_headers) { { 'Content-Type' => 'application/notjson' } }
83
+
84
+ context 'when the response body is valid JSON' do
85
+ before do
86
+ stub_request(:post, url).to_return(
87
+ status: 200,
88
+ headers: response_headers,
89
+ body: DummySchema.execute(GraphQL::Introspection::INTROSPECTION_QUERY).to_json
90
+ )
91
+ end
92
+
93
+ it 'retrieves schema' do
94
+ expect(client.schema).to be_a Graphlient::Schema
95
+ end
96
+ end
97
+
98
+ context 'when the response body is not valid JSON' do
99
+ before do
100
+ stub_request(:post, url).to_return(
101
+ status: 200,
102
+ headers: response_headers,
103
+ body: ''
104
+ )
105
+ end
106
+
107
+ it 'raises Graphlient::Errors::ServerError' do
108
+ expect { client.schema }.to raise_error(Graphlient::Errors::ServerError) { |error|
109
+ expect(error.message).to include('Failed to parse response body as JSON')
110
+ }
111
+ end
112
+ end
113
+
114
+ context 'when the Faraday response body object is not a type we expect from Faraday' do
115
+ before do
116
+ stub_request(:post, url).to_return(
117
+ status: 200,
118
+ headers: response_headers
119
+ )
120
+ end
121
+
122
+ it 'raises Graphlient::Errors::ClientError' do
123
+ mock_response = double('response', body: nil)
124
+ allow(client.http.connection).to receive(:post).and_return(mock_response)
125
+
126
+ expect { client.schema }.to raise_error(Graphlient::Errors::ClientError) { |error|
127
+ expect(error.message).to include "Unexpected response body type 'NilClass'"
128
+ }
129
+ end
130
+ end
131
+ end
132
+
78
133
  context 'Failed to open TCP connection error' do
79
134
  let(:url) { 'http://example.com/graphql' }
80
135
  let(:client) { Graphlient::Client.new(url) }
@@ -51,5 +51,25 @@ describe Graphlient::Client do
51
51
  expect(client.schema.path).to eq 'config/schema.json'
52
52
  end
53
53
  end
54
+
55
+ context 'when preloaded schema is provided' do
56
+ let(:schema) { Graphlient::Schema.new(url, 'spec/support/fixtures/invoice_api.json') }
57
+ let(:client) { described_class.new(url, schema: schema) }
58
+
59
+ it 'returns the passed in schema' do
60
+ expect(client.schema).not_to be_nil
61
+ expect(client.schema).to eq(schema)
62
+ end
63
+ end
64
+
65
+ context 'when and a schema and a schema path are provided' do
66
+ let(:schema) { Graphlient::Schema.new(url, 'spec/support/fixtures/invoice_api.json') }
67
+ let(:client) { described_class.new(url, schema: schema, schema_path: 'spec/support/fixtures/invoice_api.json') }
68
+
69
+ it 'raises an invalid configuration error' do
70
+ expect { client }.to raise_error(Graphlient::Client::InvalidConfigurationError,
71
+ /schema_path and schema cannot both be provided/)
72
+ end
73
+ end
54
74
  end
55
75
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: graphlient
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.0
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ashkan Nasseri
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-10-11 00:00:00.000000000 Z
11
+ date: 2024-01-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -131,7 +131,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
131
131
  - !ruby/object:Gem::Version
132
132
  version: 1.3.6
133
133
  requirements: []
134
- rubygems_version: 3.1.4
134
+ rubygems_version: 3.3.7
135
135
  signing_key:
136
136
  specification_version: 4
137
137
  summary: A friendlier Ruby client for consuming GraphQL-based APIs.