graphlient 0.6.0 → 0.8.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/.github/ISSUE_TEMPLATE/bug_report.md +38 -0
- data/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
- data/.github/workflows/ci.yml +2 -1
- data/.github/workflows/danger.yml +3 -1
- data/CHANGELOG.md +14 -0
- data/Dangerfile +24 -1
- data/Gemfile +1 -1
- data/Gemfile.danger +5 -0
- data/README.md +96 -0
- data/graphlient.gemspec +1 -2
- data/lib/graphlient/adapters/http/faraday_adapter.rb +35 -2
- data/lib/graphlient/client.rb +8 -1
- data/lib/graphlient/query.rb +21 -2
- data/lib/graphlient/version.rb +1 -1
- data/spec/graphlient/adapters/http/faraday_adapter_spec.rb +59 -4
- data/spec/graphlient/client_query_spec.rb +37 -0
- data/spec/graphlient/client_schema_spec.rb +33 -3
- data/spec/graphlient/schema_spec.rb +4 -1
- data/spec/graphlient/webmock_client_query_spec.rb +2 -1
- data/spec/spec_helper.rb +1 -0
- metadata +10 -21
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ed5f47ad98345af440481f9dca800767524807bc667eace999d7ba36826e59fa
|
4
|
+
data.tar.gz: 22ec20f5b725b3bb85bc9c8e8371693824f7fd85b2db43336ce7f6a6be31543f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1c5ddbaafe24dfbdd6e99535bedb7a021eba55a956b4b716661c9c8778018a96118afc5a6def029aad14da883de8d47a747b4a85ead2cee0868d5ddcfb298359
|
7
|
+
data.tar.gz: 3fd8b40976687fd83b00b865f62d514a87571de0bf68015486328d526a2d9cc76a8c4454a5d78f86acba6480860ac1abde3ff2edda1e92c26b03c58f60b067e2
|
@@ -0,0 +1,38 @@
|
|
1
|
+
---
|
2
|
+
name: Bug report
|
3
|
+
about: Create a report to help us improve
|
4
|
+
title: ''
|
5
|
+
labels: ''
|
6
|
+
assignees: ''
|
7
|
+
|
8
|
+
---
|
9
|
+
|
10
|
+
**Describe the bug**
|
11
|
+
A clear and concise description of what the bug is.
|
12
|
+
|
13
|
+
**To Reproduce**
|
14
|
+
Steps to reproduce the behavior:
|
15
|
+
1. Go to '...'
|
16
|
+
2. Click on '....'
|
17
|
+
3. Scroll down to '....'
|
18
|
+
4. See error
|
19
|
+
|
20
|
+
**Expected behavior**
|
21
|
+
A clear and concise description of what you expected to happen.
|
22
|
+
|
23
|
+
**Screenshots**
|
24
|
+
If applicable, add screenshots to help explain your problem.
|
25
|
+
|
26
|
+
**Desktop (please complete the following information):**
|
27
|
+
- OS: [e.g. iOS]
|
28
|
+
- Browser [e.g. chrome, safari]
|
29
|
+
- Version [e.g. 22]
|
30
|
+
|
31
|
+
**Smartphone (please complete the following information):**
|
32
|
+
- Device: [e.g. iPhone6]
|
33
|
+
- OS: [e.g. iOS8.1]
|
34
|
+
- Browser [e.g. stock browser, safari]
|
35
|
+
- Version [e.g. 22]
|
36
|
+
|
37
|
+
**Additional context**
|
38
|
+
Add any other context about the problem here.
|
@@ -0,0 +1,20 @@
|
|
1
|
+
---
|
2
|
+
name: Feature request
|
3
|
+
about: Suggest an idea for this project
|
4
|
+
title: ''
|
5
|
+
labels: ''
|
6
|
+
assignees: ''
|
7
|
+
|
8
|
+
---
|
9
|
+
|
10
|
+
**Is your feature request related to a problem? Please describe.**
|
11
|
+
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
12
|
+
|
13
|
+
**Describe the solution you'd like**
|
14
|
+
A clear and concise description of what you want to happen.
|
15
|
+
|
16
|
+
**Describe alternatives you've considered**
|
17
|
+
A clear and concise description of any alternative solutions or features you've considered.
|
18
|
+
|
19
|
+
**Additional context**
|
20
|
+
Add any other context or screenshots about the feature request here.
|
data/.github/workflows/ci.yml
CHANGED
@@ -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.
|
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
|
@@ -5,6 +5,8 @@ on: pull_request
|
|
5
5
|
jobs:
|
6
6
|
danger:
|
7
7
|
runs-on: ubuntu-latest
|
8
|
+
env:
|
9
|
+
BUNDLE_GEMFILE: ${{ github.workspace }}/Gemfile.danger
|
8
10
|
steps:
|
9
11
|
- uses: actions/checkout@v3
|
10
12
|
with:
|
@@ -16,5 +18,5 @@ jobs:
|
|
16
18
|
bundler-cache: true
|
17
19
|
- run: |
|
18
20
|
# the personal token is public, this is ok, base64 encode to avoid tripping Github
|
19
|
-
TOKEN=$(echo -n
|
21
|
+
TOKEN=$(echo -n Z2hwX0xNQ3VmanBFeTBvYkZVTWh6NVNqVFFBOEUxU25abzBqRUVuaAo= | base64 --decode)
|
20
22
|
DANGER_GITHUB_API_TOKEN=$TOKEN bundle exec danger --verbose
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,17 @@
|
|
1
|
+
### (Next)
|
2
|
+
* Your contribution here.
|
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
|
+
|
10
|
+
### 0.7.0 (2022/10/11)
|
11
|
+
* [#98](https://github.com/ashkan18/graphlient/pull/98): Bring back danger checks and improve them - [@ashkan18](https://github.com/ashkan18).
|
12
|
+
* [#94](https://github.com/ashkan18/graphlient/pull/94): Enabled fragments - [@rellampec](https://github.com/rellampec).
|
13
|
+
* [#95](https://github.com/ashkan18/graphlient/pull/95): Upgrade faraday dependency to version 2 - [@kirillkaiumov](https://github.com/kirillkaiumov).
|
14
|
+
|
1
15
|
### 0.6.0 (2022/06/11)
|
2
16
|
|
3
17
|
* [#87](https://github.com/ashkan18/graphlient/pull/87): Raised `ExecutionError` with partial error response - [@QQism](https://github.com/QQism).
|
data/Dangerfile
CHANGED
@@ -1 +1,24 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# --------------------------------------------------------------------------------------------------------------------
|
4
|
+
# Has any changes happened inside the actual library code?
|
5
|
+
# --------------------------------------------------------------------------------------------------------------------
|
6
|
+
has_app_changes = !git.modified_files.grep(/lib/).empty?
|
7
|
+
has_spec_changes = !git.modified_files.grep(/spec/).empty?
|
8
|
+
|
9
|
+
# --------------------------------------------------------------------------------------------------------------------
|
10
|
+
# You've made changes to lib, but didn't write any tests?
|
11
|
+
# --------------------------------------------------------------------------------------------------------------------
|
12
|
+
warn("There're library changes, but not tests. That's OK as long as you're refactoring existing code.", sticky: false) if has_app_changes && !has_spec_changes
|
13
|
+
|
14
|
+
# --------------------------------------------------------------------------------------------------------------------
|
15
|
+
# You've made changes to specs, but no library code has changed?
|
16
|
+
# --------------------------------------------------------------------------------------------------------------------
|
17
|
+
if !has_app_changes && has_spec_changes
|
18
|
+
message('We really appreciate pull requests that demonstrate issues, even without a fix. That said, the next step is to try and fix the failing tests!', sticky: false)
|
19
|
+
end
|
20
|
+
|
21
|
+
# --------------------------------------------------------------------------------------------------------------------
|
22
|
+
# Have you updated CHANGELOG.md?
|
23
|
+
# --------------------------------------------------------------------------------------------------------------------
|
24
|
+
changelog.check!
|
data/Gemfile
CHANGED
@@ -10,11 +10,11 @@ end
|
|
10
10
|
|
11
11
|
group :development do
|
12
12
|
gem 'byebug', platform: :ruby
|
13
|
-
gem 'danger-changelog', '~> 0.2.1'
|
14
13
|
gem 'rubocop', '0.56.0'
|
15
14
|
end
|
16
15
|
|
17
16
|
group :test do
|
17
|
+
gem 'faraday-rack', '~> 2.0'
|
18
18
|
gem 'graphql', '~> 1.9'
|
19
19
|
gem 'graphql-errors'
|
20
20
|
gem 'rack-parser'
|
data/Gemfile.danger
ADDED
data/README.md
CHANGED
@@ -5,6 +5,22 @@
|
|
5
5
|
|
6
6
|
A friendlier Ruby client for consuming GraphQL-based APIs. Built on top of your usual [graphql-client](https://github.com/github/graphql-client), but with better defaults, more consistent error handling, and using the [faraday](https://github.com/lostisland/faraday) HTTP client.
|
7
7
|
|
8
|
+
# Table of Contents
|
9
|
+
|
10
|
+
- [Installation](#installation)
|
11
|
+
- [Usage](#usage)
|
12
|
+
- [Schema Storing and Loading on Disk](#schema-storing-and-loading-on-disk)
|
13
|
+
- [Preloading Schema Once](#preloading-schema-once)
|
14
|
+
- [Error Handling](#error-handling)
|
15
|
+
- [Executing Parameterized Queries and Mutations](#executing-parameterized-queries-and-mutations)
|
16
|
+
- [Parse and Execute Queries Separately](#parse-and-execute-queries-separately)
|
17
|
+
- [Dynamic vs. Static Queries](#dynamic-vs-static-queries)
|
18
|
+
- [Generate Queries with Graphlient::Query](#generate-queries-with-graphlientquery)
|
19
|
+
- [Create API Client Classes with Graphlient::Extension::Query](#create-api-client-classes-with-graphlientextensionquery)
|
20
|
+
- [Swapping the HTTP Stack](#swapping-the-http-stack)
|
21
|
+
- [Testing with Graphlient and RSpec](#testing-with-graphlient-and-rspec)
|
22
|
+
- [License](#license)
|
23
|
+
|
8
24
|
## Installation
|
9
25
|
|
10
26
|
Add the following line to your Gemfile.
|
@@ -125,6 +141,30 @@ client = Client.new(url, schema_path: 'config/your_graphql_schema.json')
|
|
125
141
|
client.schema.dump! # you only need to call this when graphql schema changes
|
126
142
|
```
|
127
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
|
+
|
128
168
|
### Error Handling
|
129
169
|
|
130
170
|
Unlike graphql-client, Graphlient will always raise an exception unless the query has succeeded.
|
@@ -300,6 +340,62 @@ query.to_s
|
|
300
340
|
# "\nquery {\n invoice(id: 10){\n line_items\n }\n }\n"
|
301
341
|
```
|
302
342
|
|
343
|
+
### Use of Fragments
|
344
|
+
|
345
|
+
[Fragments](https://github.com/github/graphql-client#defining-queries) should be referred by constant:
|
346
|
+
|
347
|
+
```ruby
|
348
|
+
module Fragments
|
349
|
+
Invoice = client.parse <<~'GRAPHQL'
|
350
|
+
fragment on Invoice {
|
351
|
+
id
|
352
|
+
feeInCents
|
353
|
+
}
|
354
|
+
GRAPHQL
|
355
|
+
end
|
356
|
+
```
|
357
|
+
|
358
|
+
`Graphlient` offers the syntax below to refer to the original constant:
|
359
|
+
* Triple underscore `___` to refer to the fragment
|
360
|
+
* Double underscore `__` for namespace separator
|
361
|
+
|
362
|
+
In this example, `Fragments::Invoice` would be referred as follows:
|
363
|
+
|
364
|
+
```ruby
|
365
|
+
invoice_query = client.parse do
|
366
|
+
query do
|
367
|
+
invoice(id: 10) do
|
368
|
+
id
|
369
|
+
___Fragments__Invoice
|
370
|
+
end
|
371
|
+
end
|
372
|
+
end
|
373
|
+
```
|
374
|
+
|
375
|
+
The wrapped response only allows access to fields that have been explicitly asked for.
|
376
|
+
In this example, while `id` has been referenced directly in the main query, `feeInCents` has been spread via fragment and trying to access it in the original wrapped response will throw [`GraphQL::Client::ImplicitlyFetchedFieldError`](https://github.com/github/graphql-client/blob/master/guides/implicitly-fetched-field-error.md) (to prevent data leaks between components).
|
377
|
+
|
378
|
+
```ruby
|
379
|
+
response = client.execute(invoice_query)
|
380
|
+
result = response.data.invoice
|
381
|
+
result.to_h
|
382
|
+
# {"id" => 10, "feeInCents"=> 20000}
|
383
|
+
result.id
|
384
|
+
# 10
|
385
|
+
result.fee_in_cents
|
386
|
+
# raises GraphQL::Client::ImplicitlyFetchedFieldError
|
387
|
+
```
|
388
|
+
|
389
|
+
`feeInCents` cannot be fetched directly from the main query, but from the fragment as shown below:
|
390
|
+
|
391
|
+
```ruby
|
392
|
+
invoice = Fragments::Invoice.new(result)
|
393
|
+
invoice.id
|
394
|
+
# 10
|
395
|
+
invoice.fee_in_cents
|
396
|
+
# 20000
|
397
|
+
```
|
398
|
+
|
303
399
|
### Create API Client Classes with Graphlient::Extension::Query
|
304
400
|
|
305
401
|
You can include `Graphlient::Extensions::Query` in your class. This will add a new `method_missing` method to your context which will be used to generate GraphQL queries.
|
data/graphlient.gemspec
CHANGED
@@ -14,7 +14,6 @@ Gem::Specification.new do |s|
|
|
14
14
|
s.homepage = 'http://github.com/ashkan18/graphlient'
|
15
15
|
s.licenses = ['MIT']
|
16
16
|
s.summary = 'A friendlier Ruby client for consuming GraphQL-based APIs.'
|
17
|
-
s.add_dependency 'faraday', '
|
18
|
-
s.add_dependency 'faraday_middleware'
|
17
|
+
s.add_dependency 'faraday', '~> 2.0'
|
19
18
|
s.add_dependency 'graphql-client'
|
20
19
|
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
require 'faraday'
|
2
|
-
require '
|
2
|
+
require 'json'
|
3
3
|
|
4
4
|
module Graphlient
|
5
5
|
module Adapters
|
@@ -14,7 +14,8 @@ module Graphlient
|
|
14
14
|
variables: variables
|
15
15
|
}.to_json
|
16
16
|
end
|
17
|
-
|
17
|
+
|
18
|
+
parse_body(response.body)
|
18
19
|
rescue Faraday::ConnectionFailed => e
|
19
20
|
raise Graphlient::Errors::ConnectionFailedError, e
|
20
21
|
rescue Faraday::TimeoutError => e
|
@@ -40,6 +41,38 @@ module Graphlient
|
|
40
41
|
end
|
41
42
|
end
|
42
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
|
43
76
|
end
|
44
77
|
end
|
45
78
|
end
|
data/lib/graphlient/client.rb
CHANGED
@@ -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
|
data/lib/graphlient/query.rb
CHANGED
@@ -9,13 +9,15 @@ module Graphlient
|
|
9
9
|
|
10
10
|
ROOT_NODES = %w[query mutation subscription].freeze
|
11
11
|
|
12
|
+
FRAGMENT_DEFITION = /___(?<const>[A-Z][a-zA-Z0-9_]*(__[A-Z][a-zA-Z0-9_]*)*)/
|
13
|
+
|
12
14
|
attr_accessor :query_str
|
13
15
|
|
14
16
|
def initialize(&block)
|
15
17
|
@indents = 0
|
16
18
|
@query_str = ''
|
17
19
|
@variables = []
|
18
|
-
|
20
|
+
evaluate(&block)
|
19
21
|
end
|
20
22
|
|
21
23
|
def method_missing(method_name, *args, &block)
|
@@ -39,7 +41,24 @@ module Graphlient
|
|
39
41
|
|
40
42
|
private
|
41
43
|
|
44
|
+
def evaluate(&block)
|
45
|
+
@last_block = block || self
|
46
|
+
(@context ||= {})[@last_block] ||= @last_block.binding
|
47
|
+
instance_eval(&block)
|
48
|
+
end
|
49
|
+
|
50
|
+
def resolve_fragment_constant(value)
|
51
|
+
return nil unless (match = value.to_s.match(FRAGMENT_DEFITION))
|
52
|
+
raw_const = match[:const].gsub('__', '::')
|
53
|
+
@context[@last_block].eval(raw_const).tap do |const|
|
54
|
+
msg = "Expected constant #{raw_const} to be GraphQL::Client::FragmentDefinition. Given #{const.class}"
|
55
|
+
raise Graphlient::Errors::Error, msg unless const.is_a? GraphQL::Client::FragmentDefinition
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
42
59
|
def append_node(node, args, arg_processor: nil, &block)
|
60
|
+
node = "...#{resolve_fragment_constant(node)}".to_sym if node.to_s.start_with?('___')
|
61
|
+
|
43
62
|
# add field
|
44
63
|
@query_str << "\n#{indent}#{node}"
|
45
64
|
# add filter
|
@@ -49,7 +68,7 @@ module Graphlient
|
|
49
68
|
if block_given?
|
50
69
|
@indents += 1
|
51
70
|
@query_str << '{'
|
52
|
-
|
71
|
+
evaluate(&block)
|
53
72
|
@query_str << '}'
|
54
73
|
@indents -= 1
|
55
74
|
end
|
data/lib/graphlient/version.rb
CHANGED
@@ -19,8 +19,8 @@ describe Graphlient::Adapters::HTTP::FaradayAdapter do
|
|
19
19
|
expect(client.http.connection.builder.handlers).to eq(
|
20
20
|
[
|
21
21
|
Faraday::Response::RaiseError,
|
22
|
-
|
23
|
-
|
22
|
+
Faraday::Request::Json,
|
23
|
+
Faraday::Response::Json
|
24
24
|
]
|
25
25
|
)
|
26
26
|
end
|
@@ -65,7 +65,8 @@ describe Graphlient::Adapters::HTTP::FaradayAdapter do
|
|
65
65
|
before do
|
66
66
|
stub_request(:post, url).to_return(
|
67
67
|
status: 200,
|
68
|
-
body: DummySchema.execute(GraphQL::Introspection::INTROSPECTION_QUERY).to_json
|
68
|
+
body: DummySchema.execute(GraphQL::Introspection::INTROSPECTION_QUERY).to_json,
|
69
|
+
headers: { 'Content-Type' => 'application/json' }
|
69
70
|
)
|
70
71
|
end
|
71
72
|
|
@@ -74,6 +75,61 @@ describe Graphlient::Adapters::HTTP::FaradayAdapter do
|
|
74
75
|
end
|
75
76
|
end
|
76
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
|
+
|
77
133
|
context 'Failed to open TCP connection error' do
|
78
134
|
let(:url) { 'http://example.com/graphql' }
|
79
135
|
let(:client) { Graphlient::Client.new(url) }
|
@@ -90,7 +146,6 @@ describe Graphlient::Adapters::HTTP::FaradayAdapter do
|
|
90
146
|
|
91
147
|
specify do
|
92
148
|
expected_error_message = "Connection refused - #{error_message}"
|
93
|
-
|
94
149
|
expect { client.schema }.to raise_error(Graphlient::Errors::ConnectionFailedError, expected_error_message)
|
95
150
|
end
|
96
151
|
end
|
@@ -25,6 +25,43 @@ describe Graphlient::Client do
|
|
25
25
|
invoice = response.data.invoice
|
26
26
|
expect(invoice.id).to eq '10'
|
27
27
|
end
|
28
|
+
|
29
|
+
context 'with fragment' do
|
30
|
+
let(:invoice_fragment) do
|
31
|
+
client.parse <<~'GRAPHQL'
|
32
|
+
fragment on Invoice {
|
33
|
+
id
|
34
|
+
feeInCents
|
35
|
+
}
|
36
|
+
GRAPHQL
|
37
|
+
end
|
38
|
+
|
39
|
+
let(:invoice_fragment_const) do
|
40
|
+
stub_const('Graphlient::InvoiceFragment', invoice_fragment)
|
41
|
+
end
|
42
|
+
|
43
|
+
let(:query) do
|
44
|
+
invoice_fragment_const
|
45
|
+
client.parse do
|
46
|
+
query do
|
47
|
+
invoice(id: 10) do
|
48
|
+
___Graphlient__InvoiceFragment
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
it '#parse' do
|
55
|
+
expect(query).to be_a GraphQL::Client::OperationDefinition
|
56
|
+
end
|
57
|
+
|
58
|
+
it '#execute' do
|
59
|
+
response = client.execute(query)
|
60
|
+
invoice = response.data.invoice
|
61
|
+
fragment = invoice_fragment.new(invoice)
|
62
|
+
expect(fragment.id).to eq '10'
|
63
|
+
end
|
64
|
+
end
|
28
65
|
end
|
29
66
|
|
30
67
|
context 'parameterized query' do
|
@@ -7,13 +7,23 @@ describe Graphlient::Client do
|
|
7
7
|
|
8
8
|
describe '#schema' do
|
9
9
|
before do
|
10
|
-
stub_request(:post, url)
|
11
|
-
|
10
|
+
stub_request(:post, url).to_return(
|
11
|
+
body: DummySchema.execute(GraphQL::Introspection::INTROSPECTION_QUERY).to_json,
|
12
|
+
headers: { 'Content-Type' => 'application/json' }
|
13
|
+
)
|
12
14
|
end
|
13
15
|
|
14
16
|
context 'when server returns error' do
|
15
17
|
before do
|
16
|
-
stub_request(:post, url).to_return(
|
18
|
+
stub_request(:post, url).to_return(
|
19
|
+
status: 500,
|
20
|
+
body: {
|
21
|
+
errors: [
|
22
|
+
{ message: 'test message', extensions: { code: 'SOMETHING', timestamp: Time.now } }
|
23
|
+
]
|
24
|
+
}.to_json,
|
25
|
+
headers: { 'Content-Type' => 'application/json' }
|
26
|
+
)
|
17
27
|
end
|
18
28
|
|
19
29
|
it 'fails with an exception' do
|
@@ -41,5 +51,25 @@ describe Graphlient::Client do
|
|
41
51
|
expect(client.schema.path).to eq 'config/schema.json'
|
42
52
|
end
|
43
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
|
44
74
|
end
|
45
75
|
end
|
@@ -10,7 +10,10 @@ describe Graphlient::Schema do
|
|
10
10
|
let!(:introspection_query_request) do
|
11
11
|
stub_request(:post, url)
|
12
12
|
.with(body: /query IntrospectionQuery/)
|
13
|
-
.to_return(
|
13
|
+
.to_return(
|
14
|
+
body: DummySchema.execute(GraphQL::Introspection::INTROSPECTION_QUERY).to_json,
|
15
|
+
headers: { 'Content-Type' => 'application/json' }
|
16
|
+
)
|
14
17
|
end
|
15
18
|
|
16
19
|
context 'when schema path is not given' do
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,43 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: graphlient
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 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:
|
11
|
+
date: 2024-01-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: faraday
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
17
|
+
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '2.0'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - "
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
version: '1.0'
|
27
|
-
- !ruby/object:Gem::Dependency
|
28
|
-
name: faraday_middleware
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
30
|
-
requirements:
|
31
|
-
- - ">="
|
24
|
+
- - "~>"
|
32
25
|
- !ruby/object:Gem::Version
|
33
|
-
version: '0'
|
34
|
-
type: :runtime
|
35
|
-
prerelease: false
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
37
|
-
requirements:
|
38
|
-
- - ">="
|
39
|
-
- !ruby/object:Gem::Version
|
40
|
-
version: '0'
|
26
|
+
version: '2.0'
|
41
27
|
- !ruby/object:Gem::Dependency
|
42
28
|
name: graphql-client
|
43
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -58,6 +44,8 @@ executables: []
|
|
58
44
|
extensions: []
|
59
45
|
extra_rdoc_files: []
|
60
46
|
files:
|
47
|
+
- ".github/ISSUE_TEMPLATE/bug_report.md"
|
48
|
+
- ".github/ISSUE_TEMPLATE/feature_request.md"
|
61
49
|
- ".github/workflows/ci.yml"
|
62
50
|
- ".github/workflows/danger.yml"
|
63
51
|
- ".github/workflows/rubocop.yml"
|
@@ -69,6 +57,7 @@ files:
|
|
69
57
|
- CONTRIBUTING.md
|
70
58
|
- Dangerfile
|
71
59
|
- Gemfile
|
60
|
+
- Gemfile.danger
|
72
61
|
- LICENSE
|
73
62
|
- README.md
|
74
63
|
- RELEASING.md
|
@@ -142,7 +131,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
142
131
|
- !ruby/object:Gem::Version
|
143
132
|
version: 1.3.6
|
144
133
|
requirements: []
|
145
|
-
rubygems_version: 3.
|
134
|
+
rubygems_version: 3.3.7
|
146
135
|
signing_key:
|
147
136
|
specification_version: 4
|
148
137
|
summary: A friendlier Ruby client for consuming GraphQL-based APIs.
|