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 +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
|