graphlient 0.7.0 → 0.9.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 (80) hide show
  1. checksums.yaml +4 -4
  2. data/.github/ISSUE_TEMPLATE/bug_report.md +38 -38
  3. data/.github/ISSUE_TEMPLATE/feature_request.md +20 -20
  4. data/.github/workflows/ci.yml +33 -29
  5. data/.github/workflows/danger-comment.yml +10 -0
  6. data/.github/workflows/danger.yml +13 -22
  7. data/.github/workflows/rubocop.yml +19 -19
  8. data/.gitignore +59 -52
  9. data/.rspec +1 -1
  10. data/.rubocop.yml +26 -28
  11. data/.rubocop_todo.yml +72 -48
  12. data/CHANGELOG.md +128 -107
  13. data/CONTRIBUTING.md +125 -125
  14. data/Dangerfile +24 -24
  15. data/Gemfile +35 -27
  16. data/LICENSE +21 -21
  17. data/README.md +800 -508
  18. data/RELEASING.md +63 -63
  19. data/Rakefile +15 -15
  20. data/UPGRADING.md +23 -23
  21. data/graphlient.gemspec +19 -19
  22. data/lib/graphlient/adapters/http/adapter.rb +41 -41
  23. data/lib/graphlient/adapters/http/faraday_adapter.rb +79 -45
  24. data/lib/graphlient/adapters/http/http_adapter.rb +40 -39
  25. data/lib/graphlient/adapters/http.rb +3 -3
  26. data/lib/graphlient/adapters.rb +1 -1
  27. data/lib/graphlient/client.rb +98 -73
  28. data/lib/graphlient/errors/client_error.rb +6 -6
  29. data/lib/graphlient/errors/connection_failed_error.rb +6 -6
  30. data/lib/graphlient/errors/error.rb +13 -12
  31. data/lib/graphlient/errors/execution_error.rb +29 -29
  32. data/lib/graphlient/errors/faraday_server_error.rb +12 -12
  33. data/lib/graphlient/errors/graphql_error.rb +53 -52
  34. data/lib/graphlient/errors/http_options_error.rb +6 -6
  35. data/lib/graphlient/errors/http_server_error.rb +13 -13
  36. data/lib/graphlient/errors/server_error.rb +7 -7
  37. data/lib/graphlient/errors/timeout_error.rb +6 -6
  38. data/lib/graphlient/errors.rb +10 -10
  39. data/lib/graphlient/extensions/query.rb +15 -15
  40. data/lib/graphlient/extensions.rb +1 -1
  41. data/lib/graphlient/query/directive.rb +47 -0
  42. data/lib/graphlient/query/serializer/arguments.rb +67 -0
  43. data/lib/graphlient/query/serializer/directives.rb +17 -0
  44. data/lib/graphlient/query/serializer/evaluator.rb +15 -0
  45. data/lib/graphlient/query/serializer/fragments.rb +28 -0
  46. data/lib/graphlient/query/serializer/scalars.rb +63 -0
  47. data/lib/graphlient/query/serializer.rb +153 -0
  48. data/lib/graphlient/query.rb +29 -128
  49. data/lib/graphlient/schema.rb +27 -26
  50. data/lib/graphlient/version.rb +3 -3
  51. data/lib/graphlient.rb +8 -8
  52. data/spec/graphlient/adapters/http/faraday_adapter_spec.rb +172 -112
  53. data/spec/graphlient/adapters/http/http_adapter_spec.rb +60 -60
  54. data/spec/graphlient/client_dsl_spec.rb +153 -0
  55. data/spec/graphlient/client_query_spec.rb +369 -346
  56. data/spec/graphlient/client_schema_spec.rb +75 -55
  57. data/spec/graphlient/extensions/query_spec.rb +16 -16
  58. data/spec/graphlient/github_query_spec.rb +32 -32
  59. data/spec/graphlient/query_dsl_spec.rb +231 -0
  60. data/spec/graphlient/query_spec.rb +105 -105
  61. data/spec/graphlient/schema_spec.rb +56 -56
  62. data/spec/graphlient/static_client_query_spec.rb +75 -68
  63. data/spec/graphlient/webmock_client_query_spec.rb +41 -41
  64. data/spec/spec_helper.rb +26 -14
  65. data/spec/support/context/dummy_client.rb +33 -26
  66. data/spec/support/context/github_client.rb +18 -18
  67. data/spec/support/dummy_app.rb +21 -19
  68. data/spec/support/dummy_schema.rb +17 -17
  69. data/spec/support/fixtures/github/schema.yml +14282 -14282
  70. data/spec/support/fixtures/github/user.yml +76 -76
  71. data/spec/support/fixtures/github/viewer.yml +76 -76
  72. data/spec/support/fixtures/invoice_api.json +1288 -1288
  73. data/spec/support/mutations/create_invoice.rb +18 -18
  74. data/spec/support/queries/query.rb +48 -47
  75. data/spec/support/schema/github.json +44479 -44479
  76. data/spec/support/types/invoice_type.rb +13 -13
  77. data/spec/support/types/mutation_type.rb +5 -5
  78. data/spec/support/vcr.rb +9 -9
  79. metadata +13 -8
  80. data/Gemfile.danger +0 -5
@@ -1,56 +1,56 @@
1
- require 'spec_helper'
2
- require 'tempfile'
3
-
4
- describe Graphlient::Schema do
5
- let(:client) { Graphlient::Client.new(url) }
6
- let(:url) { 'http://graph.biz/graphql' }
7
- let(:schema) { client.schema }
8
-
9
- describe '#dump!' do
10
- let!(:introspection_query_request) do
11
- stub_request(:post, url)
12
- .with(body: /query IntrospectionQuery/)
13
- .to_return(
14
- body: DummySchema.execute(GraphQL::Introspection::INTROSPECTION_QUERY).to_json,
15
- headers: { 'Content-Type' => 'application/json' }
16
- )
17
- end
18
-
19
- context 'when schema path is not given' do
20
- it 'raises error' do
21
- expect { schema.dump! }.to raise_error(Graphlient::Schema::MissingConfigurationError)
22
- end
23
- end
24
-
25
- context 'when schema path is given' do
26
- let(:client) { Graphlient::Client.new(url, schema_path: @schema_path) }
27
- let(:schema_path) { @schema_path }
28
-
29
- around(:each) do |example|
30
- Tempfile.open('graphql_schema.json') do |file|
31
- @schema_path = file.path
32
- example.run
33
- @schema_path = nil
34
- end
35
- end
36
-
37
- it 'makes introspection query' do
38
- schema.dump!
39
- expect(introspection_query_request).to have_been_made.once
40
- end
41
-
42
- it 'updates schema json file' do
43
- expect { schema.dump! }.to(change { File.read(@schema_path) })
44
- end
45
-
46
- context 'with a schema file' do
47
- before do
48
- schema.dump!
49
- end
50
- it 'reads the schema' do
51
- expect(client.schema).to be_a Graphlient::Schema
52
- end
53
- end
54
- end
55
- end
56
- end
1
+ require 'spec_helper'
2
+ require 'tempfile'
3
+
4
+ describe Graphlient::Schema do
5
+ let(:client) { Graphlient::Client.new(url) }
6
+ let(:url) { 'http://graph.biz/graphql' }
7
+ let(:schema) { client.schema }
8
+
9
+ describe '#dump!' do
10
+ let!(:introspection_query_request) do
11
+ stub_request(:post, url)
12
+ .with(body: /query IntrospectionQuery/)
13
+ .to_return(
14
+ body: DummySchema.execute(GraphQL::Introspection::INTROSPECTION_QUERY).to_json,
15
+ headers: { 'Content-Type' => 'application/json' }
16
+ )
17
+ end
18
+
19
+ context 'when schema path is not given' do
20
+ it 'raises error' do
21
+ expect { schema.dump! }.to raise_error(Graphlient::Schema::MissingConfigurationError)
22
+ end
23
+ end
24
+
25
+ context 'when schema path is given' do
26
+ let(:client) { Graphlient::Client.new(url, schema_path: @schema_path) }
27
+ let(:schema_path) { @schema_path }
28
+
29
+ around(:each) do |example|
30
+ Tempfile.open('graphql_schema.json') do |file|
31
+ @schema_path = file.path
32
+ example.run
33
+ @schema_path = nil
34
+ end
35
+ end
36
+
37
+ it 'makes introspection query' do
38
+ schema.dump!
39
+ expect(introspection_query_request).to have_been_made.once
40
+ end
41
+
42
+ it 'updates schema json file' do
43
+ expect { schema.dump! }.to(change { File.read(@schema_path) })
44
+ end
45
+
46
+ context 'with a schema file' do
47
+ before do
48
+ schema.dump!
49
+ end
50
+ it 'reads the schema' do
51
+ expect(client.schema).to be_a Graphlient::Schema
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -1,68 +1,75 @@
1
- require 'spec_helper'
2
-
3
- describe Graphlient::Client do
4
- describe 'parse and execute' do
5
- module Graphlient::Client::Spec
6
- Client = Graphlient::Client.new(
7
- 'http://graph.biz/graphql',
8
- headers: { 'Authorization' => 'Bearer 1231' },
9
- allow_dynamic_queries: false
10
- ) do |client|
11
- client.http do |h|
12
- h.connection do |c|
13
- c.adapter Faraday::Adapter::Rack, Sinatra::Application
14
- end
15
- end
16
- end
17
-
18
- StringQuery = Client.parse <<~GRAPHQL
19
- query($some_id: Int) {
20
- invoice(id: $some_id) {
21
- id
22
- feeInCents
23
- }
24
- }
25
- GRAPHQL
26
-
27
- BlockQuery = Client.parse do
28
- query(some_id: :int) do
29
- invoice(id: :some_id) do
30
- id
31
- feeInCents
32
- end
33
- end
34
- end
35
- end
36
-
37
- it 'defaults allow_dynamic_queries to false' do
38
- expect(Graphlient::Client::Spec::Client.send(:client).allow_dynamic_queries).to be false
39
- end
40
-
41
- context 'with string-based queries' do
42
- it 'parses a string query to an OperationDefinition' do
43
- expect(Graphlient::Client::Spec::StringQuery.class).to be GraphQL::Client::OperationDefinition
44
- end
45
-
46
- it 'sets the OperationDefinition that came from a string to have a name' do
47
- expect(Graphlient::Client::Spec::StringQuery.definition_name).to eql 'Graphlient__Client__Spec__StringQuery'
48
- end
49
- end
50
-
51
- context 'with both string- and block-based queries' do
52
- it 'gets identical results parsing equivalent string- and block-based queries' do
53
- block_response = Graphlient::Client::Spec::Client.execute(Graphlient::Client::Spec::BlockQuery, some_id: 42)
54
- string_response = Graphlient::Client::Spec::Client.execute(Graphlient::Client::Spec::StringQuery, some_id: 42)
55
- expect(string_response.to_h).to eq block_response.to_h
56
- end
57
- end
58
-
59
- context 'executing a query' do
60
- it 'succeeds with expected feeInCents' do
61
- response = Graphlient::Client::Spec::Client.execute(Graphlient::Client::Spec::BlockQuery, some_id: 42)
62
- invoice = response.data.invoice
63
- expect(invoice.id).to eq '42'
64
- expect(invoice.fee_in_cents).to eq 20_000
65
- end
66
- end
67
- end
68
- end
1
+ require 'spec_helper'
2
+
3
+ describe Graphlient::Client do
4
+ describe 'parse and execute' do
5
+ module Graphlient::Client::Spec
6
+ # schema_path avoids an HTTP schema fetch at file-load time.
7
+ # No Rack adapter needed here; parsing is local and execution is handled
8
+ # by the stub_request below.
9
+ Client = Graphlient::Client.new(
10
+ 'http://graph.biz/graphql',
11
+ schema_path: 'spec/support/fixtures/invoice_api.json',
12
+ headers: { 'Authorization' => 'Bearer 1231' },
13
+ allow_dynamic_queries: false
14
+ )
15
+
16
+ StringQuery = Client.parse <<~GRAPHQL
17
+ query($some_id: Int) {
18
+ invoice(id: $some_id) {
19
+ id
20
+ feeInCents
21
+ }
22
+ }
23
+ GRAPHQL
24
+
25
+ BlockQuery = Client.parse do
26
+ query(some_id: :int) do
27
+ invoice(id: :some_id) do
28
+ id
29
+ feeInCents
30
+ end
31
+ end
32
+ end
33
+ end
34
+
35
+ # Route execution requests through the Sinatra dummy app via WebMock.
36
+ # to_rack works reliably across Ruby/gem versions; Faraday::Adapter::Rack
37
+ # can be intercepted before dispatch on some Ruby 3.x + WebMock combinations.
38
+ before do
39
+ stub_request(:post, 'http://graph.biz/graphql')
40
+ .with(headers: { 'Authorization' => 'Bearer 1231' })
41
+ .to_rack(Sinatra::Application)
42
+ end
43
+
44
+ it 'defaults allow_dynamic_queries to false' do
45
+ expect(Graphlient::Client::Spec::Client.send(:client).allow_dynamic_queries).to be false
46
+ end
47
+
48
+ context 'with string-based queries' do
49
+ it 'parses a string query to an OperationDefinition' do
50
+ expect(Graphlient::Client::Spec::StringQuery.class).to be GraphQL::Client::OperationDefinition
51
+ end
52
+
53
+ it 'sets the OperationDefinition that came from a string to have a name' do
54
+ expect(Graphlient::Client::Spec::StringQuery.definition_name).to eql 'Graphlient__Client__Spec__StringQuery'
55
+ end
56
+ end
57
+
58
+ context 'with both string- and block-based queries' do
59
+ it 'gets identical results parsing equivalent string- and block-based queries' do
60
+ block_response = Graphlient::Client::Spec::Client.execute(Graphlient::Client::Spec::BlockQuery, some_id: 42)
61
+ string_response = Graphlient::Client::Spec::Client.execute(Graphlient::Client::Spec::StringQuery, some_id: 42)
62
+ expect(string_response.to_h).to eq block_response.to_h
63
+ end
64
+ end
65
+
66
+ context 'executing a query' do
67
+ it 'succeeds with expected feeInCents' do
68
+ response = Graphlient::Client::Spec::Client.execute(Graphlient::Client::Spec::BlockQuery, some_id: 42)
69
+ invoice = response.data.invoice
70
+ expect(invoice.id).to eq '42'
71
+ expect(invoice.fee_in_cents).to eq 20_000
72
+ end
73
+ end
74
+ end
75
+ end
@@ -1,41 +1,41 @@
1
- require 'spec_helper'
2
-
3
- describe 'App' do
4
- let(:url) { 'http://graph.biz/graphql' }
5
- let(:client) { Graphlient::Client.new(url, schema_path: 'spec/support/fixtures/invoice_api.json') }
6
- let(:query) do
7
- <<~GRAPHQL
8
- query{
9
- invoice(id: 42) {
10
- id
11
- feeInCents
12
- }
13
- }
14
- GRAPHQL
15
- end
16
- let(:json_response) do
17
- {
18
- 'data' => {
19
- 'invoice' => {
20
- 'id' => '42',
21
- 'feeInCents' => 2000
22
- }
23
- }
24
- }.to_json
25
- end
26
-
27
- before do
28
- stub_request(:post, url).to_return(
29
- status: 200,
30
- body: json_response,
31
- headers: { 'Content-Type' => 'application/json' }
32
- )
33
- end
34
-
35
- it 'returns invoice fees' do
36
- response = client.query(query)
37
- expect(response.data).to be_truthy
38
- expect(response.data.invoice.id).to eq('42')
39
- expect(response.data.invoice.fee_in_cents).to eq(2000)
40
- end
41
- end
1
+ require 'spec_helper'
2
+
3
+ describe 'App' do
4
+ let(:url) { 'http://graph.biz/graphql' }
5
+ let(:client) { Graphlient::Client.new(url, schema_path: 'spec/support/fixtures/invoice_api.json') }
6
+ let(:query) do
7
+ <<~GRAPHQL
8
+ query{
9
+ invoice(id: 42) {
10
+ id
11
+ feeInCents
12
+ }
13
+ }
14
+ GRAPHQL
15
+ end
16
+ let(:json_response) do
17
+ {
18
+ 'data' => {
19
+ 'invoice' => {
20
+ 'id' => '42',
21
+ 'feeInCents' => 2000
22
+ }
23
+ }
24
+ }.to_json
25
+ end
26
+
27
+ before do
28
+ stub_request(:post, url).to_return(
29
+ status: 200,
30
+ body: json_response,
31
+ headers: { 'Content-Type' => 'application/json' }
32
+ )
33
+ end
34
+
35
+ it 'returns invoice fees' do
36
+ response = client.query(query)
37
+ expect(response.data).to be_truthy
38
+ expect(response.data.invoice.id).to eq('42')
39
+ expect(response.data.invoice.fee_in_cents).to eq(2000)
40
+ end
41
+ end
data/spec/spec_helper.rb CHANGED
@@ -1,14 +1,26 @@
1
- $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
-
3
- require 'rubygems'
4
- require 'rspec'
5
- require 'graphlient'
6
- require 'byebug' if RUBY_ENGINE != 'jruby'
7
- require 'rack/test'
8
- require 'webmock/rspec'
9
- require 'vcr'
10
- require 'faraday/rack'
11
-
12
- Dir[File.join(File.dirname(__FILE__), 'support', '**/*.rb')].each do |file|
13
- require file
14
- end
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+
3
+ require 'rubygems'
4
+ require 'rspec'
5
+
6
+ # Standard-library gems unbundled from newer Ruby (3.4+) and needed by the test
7
+ # dependencies below — required here so they're loaded before `graphlient` pulls
8
+ # them in transitively:
9
+ # - ostruct: graphql 1.13's compatibility specs reference OpenStruct
10
+ # - mutex_m: activesupport 5.x depends on it
11
+ require 'ostruct'
12
+ require 'mutex_m'
13
+ require 'graphlient'
14
+ begin
15
+ require 'byebug' if RUBY_ENGINE != 'jruby'
16
+ rescue LoadError
17
+ # byebug not available on all platforms (e.g. Windows x64-mingw-ucrt)
18
+ end
19
+ require 'rack/test'
20
+ require 'webmock/rspec'
21
+ require 'vcr'
22
+ require 'faraday/rack'
23
+
24
+ Dir[File.join(File.dirname(__FILE__), 'support', '**/*.rb')].each do |file|
25
+ require file
26
+ end
@@ -1,26 +1,33 @@
1
- RSpec.shared_context 'Dummy Client', shared_context: :metadata do
2
- include Rack::Test::Methods
3
-
4
- def app
5
- Sinatra::Application
6
- end
7
-
8
- let(:endpoint) { 'http://graph.biz/graphql' }
9
-
10
- let(:headers) do
11
- {
12
- 'Authorization' => 'Bearer 1231',
13
- 'Content-Type' => 'application/json'
14
- }
15
- end
16
-
17
- let(:client) do
18
- Graphlient::Client.new(endpoint, headers: headers) do |client|
19
- client.http do |h|
20
- h.connection do |c|
21
- c.adapter Faraday::Adapter::Rack, app
22
- end
23
- end
24
- end
25
- end
26
- end
1
+ RSpec.shared_context 'Dummy Client', shared_context: :metadata do
2
+ include Rack::Test::Methods
3
+
4
+ def app
5
+ Sinatra::Application
6
+ end
7
+
8
+ let(:endpoint) { 'http://graph.biz/graphql' }
9
+
10
+ let(:headers) do
11
+ {
12
+ 'Authorization' => 'Bearer 1231',
13
+ 'Content-Type' => 'application/json'
14
+ }
15
+ end
16
+
17
+ # Route all requests through the Sinatra dummy app via WebMock's to_rack adapter.
18
+ # This is more reliable than configuring Faraday::Adapter::Rack directly because
19
+ # WebMock intercepts Faraday requests before they reach the adapter layer on some
20
+ # Ruby/gem version combinations; to_rack routes at the WebMock level instead.
21
+ before do
22
+ stub_request(:post, endpoint)
23
+ .with(headers: { 'Authorization' => 'Bearer 1231' })
24
+ .to_rack(app)
25
+ end
26
+
27
+ # No schema_path: the to_rack stub above routes schema introspection to the
28
+ # Sinatra app as well, so graphql-client gets the full live schema including
29
+ # test-only fields (executionErrorInvoice, partialSuccess etc.).
30
+ let(:client) do
31
+ Graphlient::Client.new(endpoint, headers: headers)
32
+ end
33
+ end
@@ -1,18 +1,18 @@
1
- RSpec.shared_context 'Github Client', shared_context: :metadata do
2
- let(:endpoint) { 'https://api.github.com/graphql' }
3
-
4
- let(:headers) do
5
- {
6
- 'Authorization' => "Bearer #{ENV['GITHUB_ACCESS_TOKEN']}",
7
- 'Content-Type' => 'application/json'
8
- }
9
- end
10
-
11
- let(:client) do
12
- Graphlient::Client.new(
13
- endpoint,
14
- headers: headers,
15
- schema_path: File.expand_path("#{__dir__}/../schema/github.json")
16
- )
17
- end
18
- end
1
+ RSpec.shared_context 'Github Client', shared_context: :metadata do
2
+ let(:endpoint) { 'https://api.github.com/graphql' }
3
+
4
+ let(:headers) do
5
+ {
6
+ 'Authorization' => "Bearer #{ENV['GITHUB_ACCESS_TOKEN']}",
7
+ 'Content-Type' => 'application/json'
8
+ }
9
+ end
10
+
11
+ let(:client) do
12
+ Graphlient::Client.new(
13
+ endpoint,
14
+ headers: headers,
15
+ schema_path: File.expand_path("#{__dir__}/../schema/github.json")
16
+ )
17
+ end
18
+ end
@@ -1,19 +1,21 @@
1
- require 'sinatra'
2
- require 'rack/parser'
3
- require_relative './dummy_schema'
4
-
5
- use Rack::Parser
6
-
7
- before do
8
- halt! 403 unless request.env['HTTP_AUTHORIZATION'] == 'Bearer 1231'
9
- end
10
-
11
- post '/graphql' do
12
- headers['Content-Type'] = 'application/json'
13
- DummySchema.execute(
14
- params[:query],
15
- variables: params[:variables] || {},
16
- context: {},
17
- operation_name: params[:operationName]
18
- ).to_json
19
- end
1
+ require 'sinatra'
2
+ require 'rack/parser'
3
+ require_relative './dummy_schema'
4
+
5
+ set :environment, :test
6
+
7
+ use Rack::Parser
8
+
9
+ before do
10
+ halt 403 unless request.env['HTTP_AUTHORIZATION'] == 'Bearer 1231'
11
+ end
12
+
13
+ post '/graphql' do
14
+ headers['Content-Type'] = 'application/json'
15
+ DummySchema.execute(
16
+ params[:query],
17
+ variables: params[:variables] || {},
18
+ context: {},
19
+ operation_name: params[:operationName]
20
+ ).to_json
21
+ end
@@ -1,17 +1,17 @@
1
- require_relative 'types/invoice_type'
2
-
3
- require_relative 'queries/query'
4
- require_relative 'types/mutation_type.rb'
5
-
6
- require 'graphql/errors'
7
-
8
- class DummySchema < GraphQL::Schema
9
- query(Query)
10
- mutation(MutationType)
11
- end
12
-
13
- GraphQL::Errors.configure(DummySchema) do
14
- rescue_from StandardError do |e|
15
- GraphQL::ExecutionError.new(e.message)
16
- end
17
- end
1
+ require_relative 'types/invoice_type'
2
+
3
+ require_relative 'queries/query'
4
+ require_relative 'types/mutation_type'
5
+
6
+ require 'graphql/errors'
7
+
8
+ class DummySchema < GraphQL::Schema
9
+ query(Query)
10
+ mutation(MutationType)
11
+ end
12
+
13
+ GraphQL::Errors.configure(DummySchema) do
14
+ rescue_from StandardError do |e|
15
+ GraphQL::ExecutionError.new(e.message)
16
+ end
17
+ end