graphlient 0.6.0 → 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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2b777ec75d94d228faa748cadb28c1687b16a377892138a9fc8d1048773b8853
4
- data.tar.gz: a3bc9f948ffa5c4fe6368282834b7cd4e7addc00b7d1c90ced575ce41e8a8b3a
3
+ metadata.gz: aa3b9416b7374b0c3881224177f2a5646432e27ce9fcfdb2454b31ba318e231a
4
+ data.tar.gz: 6ca64471c0129dc021248fa309ed187bde0c9f9e58cf07a98d9072768e5446fe
5
5
  SHA512:
6
- metadata.gz: fc0080c0fab50ac5ecee6815fb24e6b216cba32558d010ebd3d9661581b55f3b3fca8b70e11f0e61f96eb471931645c12b3a3b8c2361909da31a15c650ab6bcb
7
- data.tar.gz: feb39198706b8b9650da8f6f35f080744df668420d948f2b7d69d84dd3b812301542d5735d1a69f77d865706e5853703861fe440a1ab47ea2cfef3b91712f794
6
+ metadata.gz: f035b28bfd747c72574b124cd5130e92798ac6b14d9000b73c6ce25357489f23ded22ca11dd74cc777c96dfeada948f86a9dd3b754838abf230cf82849e8f893
7
+ data.tar.gz: e6fd794359759c72051ddd7114fbe85c1110d0b743f55cd521cdcbb56fc9e6cecd4a70b6a6383f76d5304fd542d889e965d28738394995282768794059067b6c
@@ -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.
@@ -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:
data/CHANGELOG.md CHANGED
@@ -1,3 +1,11 @@
1
+ ### (Next)
2
+ * Your contribution here.
3
+
4
+ ### 0.7.0 (2022/10/11)
5
+ * [#98](https://github.com/ashkan18/graphlient/pull/98): Bring back danger checks and improve them - [@ashkan18](https://github.com/ashkan18).
6
+ * [#94](https://github.com/ashkan18/graphlient/pull/94): Enabled fragments - [@rellampec](https://github.com/rellampec).
7
+ * [#95](https://github.com/ashkan18/graphlient/pull/95): Upgrade faraday dependency to version 2 - [@kirillkaiumov](https://github.com/kirillkaiumov).
8
+
1
9
  ### 0.6.0 (2022/06/11)
2
10
 
3
11
  * [#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
- changelog.check
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
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+
3
+ group :test do
4
+ gem 'danger-changelog', '~> 0.6.0'
5
+ end
data/README.md CHANGED
@@ -5,6 +5,21 @@
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
+ - [Error Handling](#error-handling)
14
+ - [Executing Parameterized Queries and Mutations](#executing-parameterized-queries-and-mutations)
15
+ - [Parse and Execute Queries Separately](#parse-and-execute-queries-separately)
16
+ - [Dynamic vs. Static Queries](#dynamic-vs-static-queries)
17
+ - [Generate Queries with Graphlient::Query](#generate-queries-with-graphlientquery)
18
+ - [Create API Client Classes with Graphlient::Extension::Query](#create-api-client-classes-with-graphlientextensionquery)
19
+ - [Swapping the HTTP Stack](#swapping-the-http-stack)
20
+ - [Testing with Graphlient and RSpec](#testing-with-graphlient-and-rspec)
21
+ - [License](#license)
22
+
8
23
  ## Installation
9
24
 
10
25
  Add the following line to your Gemfile.
@@ -300,6 +315,62 @@ query.to_s
300
315
  # "\nquery {\n invoice(id: 10){\n line_items\n }\n }\n"
301
316
  ```
302
317
 
318
+ ### Use of Fragments
319
+
320
+ [Fragments](https://github.com/github/graphql-client#defining-queries) should be referred by constant:
321
+
322
+ ```ruby
323
+ module Fragments
324
+ Invoice = client.parse <<~'GRAPHQL'
325
+ fragment on Invoice {
326
+ id
327
+ feeInCents
328
+ }
329
+ GRAPHQL
330
+ end
331
+ ```
332
+
333
+ `Graphlient` offers the syntax below to refer to the original constant:
334
+ * Triple underscore `___` to refer to the fragment
335
+ * Double underscore `__` for namespace separator
336
+
337
+ In this example, `Fragments::Invoice` would be referred as follows:
338
+
339
+ ```ruby
340
+ invoice_query = client.parse do
341
+ query do
342
+ invoice(id: 10) do
343
+ id
344
+ ___Graphlient__InvoiceFragment
345
+ end
346
+ end
347
+ end
348
+ ```
349
+
350
+ The wrapped response only allows access to fields that have been explicitly asked for.
351
+ 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).
352
+
353
+ ```ruby
354
+ response = client.execute(invoice_query)
355
+ result = response.data.invoice
356
+ result.to_h
357
+ # {"id" => 10, "feeInCents"=> 20000}
358
+ result.id
359
+ # 10
360
+ result.fee_in_cents
361
+ # raises GraphQL::Client::ImplicitlyFetchedFieldError
362
+ ```
363
+
364
+ `feeInCents` cannot be fetched directly from the main query, but from the fragment as shown below:
365
+
366
+ ```ruby
367
+ invoice = Fragments::Invoice.new(result)
368
+ invoice.id
369
+ # 10
370
+ invoice.fee_in_cents
371
+ # 20000
372
+ ```
373
+
303
374
  ### Create API Client Classes with Graphlient::Extension::Query
304
375
 
305
376
  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', '>= 1.0'
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,4 @@
1
1
  require 'faraday'
2
- require 'faraday_middleware'
3
2
 
4
3
  module Graphlient
5
4
  module Adapters
@@ -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
- instance_eval(&block)
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
- instance_eval(&block)
71
+ evaluate(&block)
53
72
  @query_str << '}'
54
73
  @indents -= 1
55
74
  end
@@ -1,3 +1,3 @@
1
1
  module Graphlient
2
- VERSION = '0.6.0'.freeze
2
+ VERSION = '0.7.0'.freeze
3
3
  end
@@ -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
- FaradayMiddleware::EncodeJson,
23
- FaradayMiddleware::ParseJson
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
 
@@ -90,7 +91,6 @@ describe Graphlient::Adapters::HTTP::FaradayAdapter do
90
91
 
91
92
  specify do
92
93
  expected_error_message = "Connection refused - #{error_message}"
93
-
94
94
  expect { client.schema }.to raise_error(Graphlient::Errors::ConnectionFailedError, expected_error_message)
95
95
  end
96
96
  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
- .to_return(body: DummySchema.execute(GraphQL::Introspection::INTROSPECTION_QUERY).to_json)
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(status: 500, body: { errors: [{ message: 'test message', extensions: { code: 'SOMETHING', timestamp: Time.now } }] }.to_json)
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
@@ -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(body: DummySchema.execute(GraphQL::Introspection::INTROSPECTION_QUERY).to_json)
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
@@ -27,7 +27,8 @@ describe 'App' do
27
27
  before do
28
28
  stub_request(:post, url).to_return(
29
29
  status: 200,
30
- body: json_response
30
+ body: json_response,
31
+ headers: { 'Content-Type' => 'application/json' }
31
32
  )
32
33
  end
33
34
 
data/spec/spec_helper.rb CHANGED
@@ -7,6 +7,7 @@ require 'byebug' if RUBY_ENGINE != 'jruby'
7
7
  require 'rack/test'
8
8
  require 'webmock/rspec'
9
9
  require 'vcr'
10
+ require 'faraday/rack'
10
11
 
11
12
  Dir[File.join(File.dirname(__FILE__), 'support', '**/*.rb')].each do |file|
12
13
  require file
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.6.0
4
+ version: 0.7.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-06-11 00:00:00.000000000 Z
11
+ date: 2022-10-11 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: '1.0'
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.1.3
134
+ rubygems_version: 3.1.4
146
135
  signing_key:
147
136
  specification_version: 4
148
137
  summary: A friendlier Ruby client for consuming GraphQL-based APIs.