gqli 0.5.0 → 0.6.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: 546f032ddd8089b8c2709b097dfcf42eedbfaa244fa487aed9d35240530c5a22
4
- data.tar.gz: acbdf1e8755552a48a08085764dae8b53877a95e13e9cef1b875d58c3a5a4912
3
+ metadata.gz: 9f9f3f5a26b24da043c5cf373457ad78ac32ccf2171db0aca3aa2152e594fa76
4
+ data.tar.gz: 0ada10622897729a6be228f68dc1af8095eeb22609d364b1a853f816d10796b0
5
5
  SHA512:
6
- metadata.gz: 9220a899f2fa1acbcdef297b7dc86a3fd6480017e7dc004fe5f3ead83c10c4f1087eb9b37b3d74e9a7b89592191026ba22baa4f9740eb56c6293ec844f367788
7
- data.tar.gz: 31ab9c1aeaa34d692bc5f0fd85066d58cb6f6f4625fa8b6b3800d73a1dee0b17fe03acdecee9551d2cc4bf19c4d9627d88ca9954aa791a3733ee3aacdaf090db
6
+ metadata.gz: 2e36d05568c87653075775dd6b75b90e869f00d62bb945b9f9c33fd41706753a39cd2991df346e0c50a02abf850ec6c8ad4974fdca623671bbb4e90d3555fe56
7
+ data.tar.gz: 89cfb285c28747cfd19274e2a13aed0e8d661e4a2f946e10e4a15b10e4c98c5e92708d0179fe698cfb4a49e827e409ac05c3cfa26cbf585e338c79fc59cfc5d0
@@ -2,6 +2,11 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## v0.6.0
6
+ * Add Subscription DSL support. (by @hschne) [#5](https://github.com/contentful-labs/gqli.rb/pull/5)
7
+ * Add aliases support.
8
+ * Add default client constructors for Contentful and Github.
9
+
5
10
  ## v0.5.0
6
11
  ### Added
7
12
  * Add Enum support.
data/README.md CHANGED
@@ -29,7 +29,7 @@ gem 'gqli'
29
29
 
30
30
  ### Creating a GraphQL Client
31
31
 
32
- For the examples throughout this README, we'll be using the Contentful and Github GraphQL APIs.
32
+ For the examples throughout this README, we'll be using the Contentful and Github GraphQL APIs, for which we have factory methods.
33
33
  Therefore, here's the initialization code required for both of them:
34
34
 
35
35
  ```ruby
@@ -38,16 +38,26 @@ require 'gqli'
38
38
  # Creating a Contentful GraphQL Client
39
39
  SPACE_ID = 'cfexampleapi'
40
40
  CF_ACCESS_TOKEN = 'b4c0n73n7fu1'
41
- CONTENTFUL_GQL = GQLi::Client.new(
42
- "https://graphql.contentful.com/content/v1/spaces/#{SPACE_ID}",
43
- headers: { "Authorization" => "Bearer #{CF_ACCESS_TOKEN}" }
44
- )
41
+ CONTENTFUL_GQL = GQLi::Contentful.create(SPACE_ID, CF_ACCESS_TOKEN)
45
42
 
46
43
  # Creating a Github GraphQL Client
47
44
  GITHUB_ACCESS_TOKEN = ENV['GITHUB_TOKEN']
48
- GITHUB_GQL = GQLi::Client.new(
49
- "https://api.github.com/graphql",
50
- headers: { "Authorization" => "Bearer #{GITHUB_ACCESS_TOKEN}" }
45
+ GITHUB_GQL = GQLi::Github.create(GITHUB_ACCESS_TOKEN)
46
+ ```
47
+
48
+ *Note*: Please feel free to contribute factories for your favorite GraphQL services.
49
+
50
+ For creating a custom GraphQL client:
51
+
52
+ ```ruby
53
+ require 'gqli'
54
+
55
+ # Create a custom client
56
+ GQL_CLIENT = GQLi::Client.new(
57
+ "https://graphql.yourservice.com",
58
+ headers: {
59
+ "Authorization" => "Bearer AUTH_TOKEN"
60
+ }
51
61
  )
52
62
  ```
53
63
 
@@ -347,6 +357,37 @@ query {
347
357
  }
348
358
  ```
349
359
 
360
+ ### Aliases
361
+
362
+ There may be times where it is useful to have parts of the query aliased, for example, when querying for `pinned` and `unpinned` articles for a news site.
363
+
364
+ This can be accomplished as follows:
365
+
366
+ ```ruby
367
+ ArticleFragment = GQLi::DSL.fragment('ArticleFragment', 'ArticleCollection') {
368
+ items {
369
+ title
370
+ description
371
+ heroImage {
372
+ url
373
+ }
374
+ }
375
+ }
376
+
377
+ query = GQLi::DSL.query {
378
+ __node('pinned: articleCollection', where: {
379
+ sys: { id_in: ['articleID'] }
380
+ }) {
381
+ ___ ArticleFragment
382
+ }
383
+ __node('unpinned: articleCollection', where: {
384
+ sys: { id_not_in: ['articleID'] }
385
+ }) {
386
+ ___ ArticleFragment
387
+ }
388
+ }
389
+ ```
390
+
350
391
  ## Yet to be implemented
351
392
 
352
393
  * Mutation queries
@@ -4,3 +4,4 @@ require_relative './gqli/dsl'
4
4
  require_relative './gqli/client'
5
5
  require_relative './gqli/introspection'
6
6
  require_relative './gqli/version'
7
+ require_relative './gqli/clients'
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './clients/contentful'
4
+ require_relative './clients/github'
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GQLi
4
+ # Module for creating a Contentful GraphQL client
5
+ module Contentful
6
+ # Creates a Contentful GraphQL client
7
+ def self.create(space, access_token, environment: nil, validate_query: true)
8
+ api_url = "https://graphql.contentful.com/content/v1/spaces/#{space}"
9
+ api_url += "/environments/#{environment}" unless environment.nil?
10
+
11
+ GQLi::Client.new(
12
+ api_url,
13
+ headers: {
14
+ 'Authorization' => "Bearer #{access_token}"
15
+ },
16
+ validate_query: validate_query
17
+ )
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GQLi
4
+ # Module for creating a Github GraphQL client
5
+ module Github
6
+ # Creates a Github GraphQL client
7
+ def self.create(access_token, validate_query: true)
8
+ GQLi::Client.new(
9
+ 'https://api.github.com/graphql',
10
+ headers: {
11
+ 'Authorization' => "Bearer #{access_token}"
12
+ },
13
+ validate_query: validate_query
14
+ )
15
+ end
16
+ end
17
+ end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative './query'
4
+ require_relative './subscription'
4
5
  require_relative './fragment'
5
6
  require_relative './enum_value'
6
7
 
@@ -14,6 +15,13 @@ module GQLi
14
15
  Query.new(name, &block)
15
16
  end
16
17
 
18
+ # Creates a Subscription object
19
+ #
20
+ # Can be used at a class level
21
+ def self.subscription(name = nil, &block)
22
+ Subscription.new(name, &block)
23
+ end
24
+
17
25
  # Creates a Fragment object
18
26
  #
19
27
  # Can be used at a class level
@@ -35,6 +43,13 @@ module GQLi
35
43
  Query.new(name, &block)
36
44
  end
37
45
 
46
+ # Creates a Subscription object
47
+ #
48
+ # Can be used at a class level
49
+ def subscription(name = nil, &block)
50
+ Subscription.new(name, &block)
51
+ end
52
+
38
53
  # Creates a Fragment object
39
54
  #
40
55
  # Can be used at an instance level
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GQLi
4
+ # Query node
5
+ class Subscription < Base
6
+ # Serializes to a GraphQL string
7
+ def to_gql
8
+ result = <<~GQL
9
+ subscription #{__name ? __name + ' ' : ''}{
10
+ #{__nodes.map(&:to_gql).join("\n")}
11
+ }
12
+ GQL
13
+
14
+ result.lstrip
15
+ end
16
+
17
+ # Delegates itself to the client to be executed
18
+ def __execute(client)
19
+ client.execute(self)
20
+ end
21
+
22
+ # Serializes to a GraphQL string
23
+ def to_s
24
+ to_gql
25
+ end
26
+ end
27
+ end
@@ -39,6 +39,12 @@ module GQLi
39
39
 
40
40
  private
41
41
 
42
+ def remove_alias(name)
43
+ return name unless name.include?(':')
44
+
45
+ name.split(':')[1].strip
46
+ end
47
+
42
48
  def types
43
49
  schema.types
44
50
  end
@@ -48,13 +54,15 @@ module GQLi
48
54
 
49
55
  return valid_match_node?(parent_type, node) if node.__name.start_with?('... on')
50
56
 
51
- node_type = parent_type.fetch('fields', []).find { |f| f.name == node.__name }
52
- fail "Node type not found for '#{node.__name}'" if node_type.nil?
57
+ node_name = remove_alias(node.__name)
58
+
59
+ node_type = parent_type.fetch('fields', []).find { |f| f.name == node_name }
60
+ fail "Node type not found for '#{node_name}'" if node_type.nil?
53
61
 
54
62
  validate_params(node_type, node)
55
63
 
56
64
  resolved_node_type = type_for(node_type)
57
- fail "Node type not found for '#{node.__name}'" if resolved_node_type.nil?
65
+ fail "Node type not found for '#{node_name}'" if resolved_node_type.nil?
58
66
 
59
67
  validate_nesting_node(resolved_node_type, node)
60
68
 
@@ -67,7 +75,7 @@ module GQLi
67
75
  end
68
76
 
69
77
  def validate_directives(node)
70
- return unless node.__params.size == 1
78
+ return unless node.__params.size >= 1
71
79
  node.__params.first.tap do |k, v|
72
80
  break unless k.to_s.start_with?('@')
73
81
 
@@ -3,5 +3,5 @@
3
3
  # GraphQL Client and DSL library
4
4
  module GQLi
5
5
  # Gem version
6
- VERSION = '0.5.0'
6
+ VERSION = '0.6.0'
7
7
  end
@@ -1,24 +1,20 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe GQLi::Client do
4
+ let(:space_id) { 'cfexampleapi' }
5
+ let(:token) { 'b4c0n73n7fu1' }
6
+
4
7
  let(:client) do
5
8
  vcr('client') {
6
- space_id = 'cfexampleapi'
7
- token = 'b4c0n73n7fu1'
8
- GQLi::Client.new(
9
- "https://graphql.contentful.com/content/v1/spaces/#{space_id}",
10
- headers: { "Authorization" => "Bearer #{token}" }
11
- )
9
+ GQLi::Contentful.create(space_id, token)
12
10
  }
13
11
  end
14
12
 
15
13
  let(:client_no_validations) do
16
14
  vcr('client') {
17
- space_id = 'cfexampleapi'
18
- token = 'b4c0n73n7fu1'
19
- GQLi::Client.new(
20
- "https://graphql.contentful.com/content/v1/spaces/#{space_id}",
21
- headers: { "Authorization" => "Bearer #{token}" },
15
+ GQLi::Contentful.create(
16
+ space_id,
17
+ token,
22
18
  validate_query: false
23
19
  )
24
20
  }
@@ -26,6 +22,18 @@ describe GQLi::Client do
26
22
 
27
23
  let(:dsl) { GQLi::DSL }
28
24
 
25
+ describe 'default clients' do
26
+ it 'contentful client' do
27
+ expect(client).to be_a(GQLi::Client)
28
+ end
29
+
30
+ it 'github client' do
31
+ client = GQLi::Github.create('foobar', validate_query: false)
32
+
33
+ expect(client).to be_a(GQLi::Client)
34
+ end
35
+ end
36
+
29
37
  describe 'query can call the client to execute' do
30
38
  subject { client }
31
39
 
@@ -37,6 +37,35 @@ describe GQLi::DSL do
37
37
  end
38
38
  end
39
39
 
40
+ describe '::subscription' do
41
+ it 'can create a subscription without name' do
42
+ subscription = subject.subscription {
43
+ someField
44
+ }
45
+
46
+ expect(subscription).to be_a GQLi::Subscription
47
+ expect(subscription.to_gql).to eq <<~GRAPHQL
48
+ subscription {
49
+ someField
50
+ }
51
+ GRAPHQL
52
+ expect(subscription.to_gql).to eq subscription.to_s
53
+ end
54
+
55
+ it 'can create a subscription with a name' do
56
+ subscription = subject.subscription('FooBar') {
57
+ someOtherField
58
+ }
59
+
60
+ expect(subscription).to be_a GQLi::Subscription
61
+ expect(subscription.to_gql).to eq <<~GRAPHQL
62
+ subscription FooBar {
63
+ someOtherField
64
+ }
65
+ GRAPHQL
66
+ end
67
+ end
68
+
40
69
  describe '::fragment' do
41
70
  it 'can create a fragment' do
42
71
  fragment = subject.fragment('FooBar', 'Foo') {
@@ -93,6 +122,39 @@ describe GQLi::DSL do
93
122
  end
94
123
  end
95
124
 
125
+
126
+ describe '#subscription does the same as ::subscription' do
127
+
128
+ subject { MockGQLInterface.new }
129
+
130
+ it 'can create a subscription without name' do
131
+ subscription = subject.subscription {
132
+ someField
133
+ }
134
+
135
+ expect(subscription).to be_a GQLi::Subscription
136
+ expect(subscription.to_gql).to eq <<~GRAPHQL
137
+ subscription {
138
+ someField
139
+ }
140
+ GRAPHQL
141
+ expect(subscription.to_gql).to eq subscription.to_s
142
+ end
143
+
144
+ it 'can create a subscription with a name' do
145
+ subscription = subject.subscription('FooBar') {
146
+ someOtherField
147
+ }
148
+
149
+ expect(subscription).to be_a GQLi::Subscription
150
+ expect(subscription.to_gql).to eq <<~GRAPHQL
151
+ subscription FooBar {
152
+ someOtherField
153
+ }
154
+ GRAPHQL
155
+ end
156
+ end
157
+
96
158
  describe '#fragment does the same as ::fragment' do
97
159
  it 'can create a fragment' do
98
160
  fragment = subject.fragment('FooBar', 'Foo') {
@@ -135,10 +197,44 @@ describe GQLi::DSL do
135
197
  GRAPHQL
136
198
  end
137
199
 
200
+ it 'nodes can have aliases' do
201
+ query = subject.query {
202
+ __node('pinned: catCollection', where: {
203
+ sys: { id_in: 'nyancat' }
204
+ }) {
205
+ items {
206
+ name
207
+ }
208
+ }
209
+ __node('unpinned: catCollection', where: {
210
+ sys: { id_not_in: 'nyancat' }
211
+ }, limit: 4) {
212
+ items {
213
+ name
214
+ }
215
+ }
216
+ }
217
+
218
+ expect(query.to_gql).to eq <<~GRAPHQL
219
+ query {
220
+ pinned: catCollection(where: {sys: {id_in: "nyancat"}}) {
221
+ items {
222
+ name
223
+ }
224
+ }
225
+ unpinned: catCollection(where: {sys: {id_not_in: "nyancat"}}, limit: 4) {
226
+ items {
227
+ name
228
+ }
229
+ }
230
+ }
231
+ GRAPHQL
232
+ end
233
+
138
234
  it 'nodes can have directives' do
139
235
  query = subject.query {
140
- someNode(:@include => {if: true})
141
- otherNode(:@skip => {if: false})
236
+ someNode(:@include => { if: true })
237
+ otherNode(:@skip => { if: false })
142
238
  }
143
239
 
144
240
  expect(query.to_gql).to eq <<~GRAPHQL
@@ -6,10 +6,7 @@ describe GQLi::Introspection do
6
6
  vcr('client') {
7
7
  space_id = 'cfexampleapi'
8
8
  token = 'b4c0n73n7fu1'
9
- GQLi::Client.new(
10
- "https://graphql.contentful.com/content/v1/spaces/#{space_id}",
11
- headers: { "Authorization" => "Bearer #{token}" }
12
- )
9
+ GQLi::Contentful.create(space_id, token)
13
10
  }
14
11
  end
15
12
 
@@ -208,6 +205,31 @@ describe GQLi::Introspection do
208
205
  end
209
206
  end
210
207
 
208
+ describe 'aliases' do
209
+ it 'can create a query with an alias' do
210
+ query = dsl.query {
211
+ __node('pinned: catCollection', where: {
212
+ sys: { id_in: 'nyancat' }
213
+ }) {
214
+ items {
215
+ name
216
+ }
217
+ }
218
+ __node('unpinned: catCollection', where: {
219
+ sys: { id_not_in: 'nyancat' }
220
+ }, limit: 4) {
221
+ items {
222
+ name
223
+ }
224
+ }
225
+ }
226
+
227
+ validation = subject.validate(query)
228
+ expect(validation.valid?).to be_truthy
229
+ expect(validation.errors).to be_empty
230
+ end
231
+ end
232
+
211
233
  describe 'directives' do
212
234
  it 'can create a query with a directive and validations should not fail' do
213
235
  query = dsl.query {
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gqli
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Contentful GmbH (David Litvak Bruno)
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-01-25 00:00:00.000000000 Z
11
+ date: 2019-02-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: http
@@ -310,9 +310,11 @@ files:
310
310
  - doc/GQLi.html
311
311
  - doc/GQLi/Base.html
312
312
  - doc/GQLi/Client.html
313
+ - doc/GQLi/Contentful.html
313
314
  - doc/GQLi/DSL.html
314
315
  - doc/GQLi/EnumValue.html
315
316
  - doc/GQLi/Fragment.html
317
+ - doc/GQLi/Github.html
316
318
  - doc/GQLi/Introspection.html
317
319
  - doc/GQLi/Node.html
318
320
  - doc/GQLi/Query.html
@@ -338,6 +340,9 @@ files:
338
340
  - lib/gqli.rb
339
341
  - lib/gqli/base.rb
340
342
  - lib/gqli/client.rb
343
+ - lib/gqli/clients.rb
344
+ - lib/gqli/clients/contentful.rb
345
+ - lib/gqli/clients/github.rb
341
346
  - lib/gqli/dsl.rb
342
347
  - lib/gqli/enum_value.rb
343
348
  - lib/gqli/fragment.rb
@@ -345,6 +350,7 @@ files:
345
350
  - lib/gqli/node.rb
346
351
  - lib/gqli/query.rb
347
352
  - lib/gqli/response.rb
353
+ - lib/gqli/subscription.rb
348
354
  - lib/gqli/validation.rb
349
355
  - lib/gqli/version.rb
350
356
  - spec/fixtures/vcr_cassettes/catCollection.yml
@@ -377,7 +383,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
377
383
  version: '0'
378
384
  requirements: []
379
385
  rubyforge_project:
380
- rubygems_version: 2.7.6
386
+ rubygems_version: 2.7.8
381
387
  signing_key:
382
388
  specification_version: 4
383
389
  summary: GraphQL client for humans