gqli 0.5.0 → 0.6.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/CHANGELOG.md +5 -0
- data/README.md +49 -8
- data/lib/gqli.rb +1 -0
- data/lib/gqli/clients.rb +4 -0
- data/lib/gqli/clients/contentful.rb +20 -0
- data/lib/gqli/clients/github.rb +17 -0
- data/lib/gqli/dsl.rb +15 -0
- data/lib/gqli/subscription.rb +27 -0
- data/lib/gqli/validation.rb +12 -4
- data/lib/gqli/version.rb +1 -1
- data/spec/lib/gqli/client_spec.rb +19 -11
- data/spec/lib/gqli/dsl_spec.rb +98 -2
- data/spec/lib/gqli/introspection_spec.rb +26 -4
- metadata +9 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9f9f3f5a26b24da043c5cf373457ad78ac32ccf2171db0aca3aa2152e594fa76
|
4
|
+
data.tar.gz: 0ada10622897729a6be228f68dc1af8095eeb22609d364b1a853f816d10796b0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2e36d05568c87653075775dd6b75b90e869f00d62bb945b9f9c33fd41706753a39cd2991df346e0c50a02abf850ec6c8ad4974fdca623671bbb4e90d3555fe56
|
7
|
+
data.tar.gz: 89cfb285c28747cfd19274e2a13aed0e8d661e4a2f946e10e4a15b10e4c98c5e92708d0179fe698cfb4a49e827e409ac05c3cfa26cbf585e338c79fc59cfc5d0
|
data/CHANGELOG.md
CHANGED
@@ -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::
|
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::
|
49
|
-
|
50
|
-
|
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
|
data/lib/gqli.rb
CHANGED
data/lib/gqli/clients.rb
ADDED
@@ -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
|
data/lib/gqli/dsl.rb
CHANGED
@@ -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
|
data/lib/gqli/validation.rb
CHANGED
@@ -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
|
-
|
52
|
-
|
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 '#{
|
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
|
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
|
|
data/lib/gqli/version.rb
CHANGED
@@ -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
|
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
|
-
|
18
|
-
|
19
|
-
|
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
|
|
data/spec/lib/gqli/dsl_spec.rb
CHANGED
@@ -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::
|
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.
|
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-
|
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.
|
386
|
+
rubygems_version: 2.7.8
|
381
387
|
signing_key:
|
382
388
|
specification_version: 4
|
383
389
|
summary: GraphQL client for humans
|