graphql-extras 0.2.5 → 0.4.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/.github/workflows/build.yml +25 -0
- data/.github/workflows/publish.yml +41 -0
- data/README.md +74 -33
- data/graphql-extras.gemspec +5 -6
- data/lib/graphql/extras.rb +1 -1
- data/lib/graphql/extras/preload.rb +33 -0
- data/lib/graphql/extras/test.rb +1 -0
- data/lib/graphql/extras/test/loader.rb +59 -0
- data/lib/graphql/extras/test/response.rb +37 -0
- data/lib/graphql/extras/test/schema.rb +57 -0
- data/lib/graphql/extras/version.rb +1 -1
- metadata +19 -29
- data/.travis.yml +0 -7
- data/lib/graphql/extras/batch.rb +0 -47
- data/lib/graphql/extras/rspec.rb +0 -133
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1ad1e3740a8a7ef06a6dad31a459153a121e254824225bf1a12c2718a59a699f
|
4
|
+
data.tar.gz: 36ce81c3f1f0888924668ccef5bde169958e45a97837cc669d94a416edc573de
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ba8b4d40badcfba328280f397f8fb7ab7f42ce5257478c0482e48c4f0abf533f4b170d51f507caf6378ed6239d51460b4fe0f4623a6cfb93e0b565bbbf81a80d
|
7
|
+
data.tar.gz: de3c6a7f726b0b9ca1311906c1c3d05c0896fe8ba1b64d9d059cc0b7c5d82c227ae7ec17611b999b666201b97818daea30d2f996fda5f9d9fefa849d00ec9fc2
|
@@ -0,0 +1,25 @@
|
|
1
|
+
name: Build
|
2
|
+
on: [push]
|
3
|
+
jobs:
|
4
|
+
build:
|
5
|
+
runs-on: ubuntu-latest
|
6
|
+
steps:
|
7
|
+
- name: Checkout
|
8
|
+
uses: actions/checkout@v2
|
9
|
+
|
10
|
+
- name: Setup Ruby
|
11
|
+
uses: actions/setup-ruby@v1
|
12
|
+
with:
|
13
|
+
ruby-version: 2.6.x
|
14
|
+
|
15
|
+
- name: Install packages
|
16
|
+
run: sudo apt-get install libsqlite3-dev
|
17
|
+
|
18
|
+
- name: Install bundler
|
19
|
+
run: gem install bundler
|
20
|
+
|
21
|
+
- name: Install dependencies
|
22
|
+
run: bundle install
|
23
|
+
|
24
|
+
- name: Test
|
25
|
+
run: bundle exec rspec
|
@@ -0,0 +1,41 @@
|
|
1
|
+
name: Publish
|
2
|
+
on:
|
3
|
+
release:
|
4
|
+
types: [published]
|
5
|
+
jobs:
|
6
|
+
publish:
|
7
|
+
runs-on: ubuntu-latest
|
8
|
+
steps:
|
9
|
+
- name: Checkout
|
10
|
+
uses: actions/checkout@v2
|
11
|
+
|
12
|
+
- name: Setup Ruby
|
13
|
+
uses: actions/setup-ruby@v1
|
14
|
+
with:
|
15
|
+
ruby-version: 2.6.x
|
16
|
+
|
17
|
+
- name: Install packages
|
18
|
+
run: sudo apt-get install libsqlite3-dev
|
19
|
+
|
20
|
+
- name: Install bundler
|
21
|
+
run: gem install bundler
|
22
|
+
|
23
|
+
- name: Install dependencies
|
24
|
+
run: bundle install
|
25
|
+
|
26
|
+
- name: Test
|
27
|
+
run: bundle exec rspec
|
28
|
+
|
29
|
+
- name: Set version
|
30
|
+
run: perl -pi -e "s/0\.0\.0/${GITHUB_REF:11}/" lib/graphql/extras/version.rb
|
31
|
+
|
32
|
+
- name: Publish
|
33
|
+
run: |
|
34
|
+
mkdir -p $HOME/.gem
|
35
|
+
touch $HOME/.gem/credentials
|
36
|
+
chmod 0600 $HOME/.gem/credentials
|
37
|
+
printf -- "---\n:rubygems_api_key: ${RUBYGEMS_TOKEN}\n" > $HOME/.gem/credentials
|
38
|
+
gem build *.gemspec
|
39
|
+
gem push *.gem
|
40
|
+
env:
|
41
|
+
RUBYGEMS_TOKEN: ${{ secrets.RUBYGEMS_TOKEN }}
|
data/README.md
CHANGED
@@ -1,7 +1,30 @@
|
|
1
|
-
|
1
|
+
<h1 align="center">GraphQL::Extras</h1>
|
2
|
+
|
3
|
+
<div align="center">
|
4
|
+
|
5
|
+

|
6
|
+

|
7
|
+
|
8
|
+
</div>
|
2
9
|
|
3
10
|
A collection of utilities for building GraphQL APIs.
|
4
11
|
|
12
|
+
**Table of Contents**
|
13
|
+
|
14
|
+
- [Installation](#installation)
|
15
|
+
- [Usage](#usage)
|
16
|
+
- [GraphQL::Extras::Controller](#graphqlextrascontroller)
|
17
|
+
- [GraphQL::Extras::Preload](#graphqlextraspreload)
|
18
|
+
- [GraphQL::Extras::PreloadSource](#graphqlextraspreloadsource)
|
19
|
+
- [GraphQL::Extras::Types](#graphqlextrastypes)
|
20
|
+
- [Date](#date)
|
21
|
+
- [DateTime](#datetime)
|
22
|
+
- [Decimal](#decimal)
|
23
|
+
- [Upload](#upload)
|
24
|
+
- [GraphQL::Extras::Test](#graphqlextrastest)
|
25
|
+
- [Development](#development)
|
26
|
+
- [Contributing](#contributing)
|
27
|
+
|
5
28
|
## Installation
|
6
29
|
|
7
30
|
Add this line to your application's Gemfile:
|
@@ -30,34 +53,44 @@ class GraphqlController < ApplicationController
|
|
30
53
|
end
|
31
54
|
```
|
32
55
|
|
33
|
-
### GraphQL::Extras::
|
56
|
+
### GraphQL::Extras::Preload
|
34
57
|
|
35
|
-
This
|
58
|
+
This allows you to preload associations before resolving fields.
|
36
59
|
|
37
60
|
```ruby
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
```
|
42
|
-
|
43
|
-
### GraphQL::Extras::Batch::Resolvers
|
61
|
+
class Schema < GraphQL::Schema
|
62
|
+
use GraphQL::Dataloader
|
63
|
+
end
|
44
64
|
|
45
|
-
|
65
|
+
class BaseField < GraphQL::Schema::Field
|
66
|
+
prepend GraphQL::Extras::Preload
|
67
|
+
end
|
46
68
|
|
47
|
-
|
48
|
-
|
49
|
-
|
69
|
+
class BaseObject < GraphQL::Schema::Object
|
70
|
+
field_class BaseField
|
71
|
+
end
|
50
72
|
|
51
|
-
|
52
|
-
field :
|
53
|
-
field :
|
73
|
+
class PostType < BaseObject
|
74
|
+
field :author, AuthorType, preload: :author, null: false
|
75
|
+
field :author_posts, [PostType], preload: {author: :posts}, null: false
|
76
|
+
field :depends_on_author, Integer, preload: :author, null: false
|
54
77
|
|
55
|
-
def
|
56
|
-
|
78
|
+
def author_posts
|
79
|
+
object.author.posts
|
57
80
|
end
|
58
81
|
end
|
59
82
|
```
|
60
83
|
|
84
|
+
### GraphQL::Extras::PreloadSource
|
85
|
+
|
86
|
+
This is a subclass of [`GraphQL::Dataloader::Source`](https://graphql-ruby.org/dataloader/overview.html) that performs eager loading of Active Record associations.
|
87
|
+
|
88
|
+
```ruby
|
89
|
+
loader = dataloader.with(GraphQL::Extras::PreloadSource, :blog)
|
90
|
+
loader.load(Post.first)
|
91
|
+
loader.load_many(Post.all)
|
92
|
+
```
|
93
|
+
|
61
94
|
### GraphQL::Extras::Types
|
62
95
|
|
63
96
|
In your base classes, you should include the `GraphQL::Extras::Types`.
|
@@ -88,7 +121,7 @@ This scalar takes a `DateTime` and transmits it as a string, using ISO 8601 form
|
|
88
121
|
field :created_at, DateTime, required: true
|
89
122
|
```
|
90
123
|
|
91
|
-
|
124
|
+
_Note: This is just an alias for the `ISO8601DateTime` type that is included in the `graphql` gem._
|
92
125
|
|
93
126
|
#### Decimal
|
94
127
|
|
@@ -119,36 +152,44 @@ Take note of the correspondence between the value `"image"` and the additional H
|
|
119
152
|
|
120
153
|
See [apollo-link-upload](https://github.com/rzane/apollo-link-upload) for the client-side implementation.
|
121
154
|
|
122
|
-
###
|
155
|
+
### GraphQL::Extras::Test
|
156
|
+
|
157
|
+
This module makes it really easy to test your schema.
|
123
158
|
|
124
|
-
|
159
|
+
First, create a test schema:
|
125
160
|
|
126
161
|
```ruby
|
127
|
-
|
162
|
+
# spec/support/test_schema.rb
|
163
|
+
require "graphql/extras/test"
|
164
|
+
|
165
|
+
class TestSchema < GraphQL::Extras::Test::Schema
|
166
|
+
configure schema: Schema, queries: "spec/**/*.graphql"
|
167
|
+
end
|
128
168
|
```
|
129
169
|
|
130
170
|
Now, you can run tests like so:
|
131
171
|
|
132
172
|
```ruby
|
173
|
+
require "support/test_schema"
|
174
|
+
|
133
175
|
RSpec.describe "hello" do
|
134
176
|
let(:context) { { name: "Ray" } }
|
135
|
-
let(:schema) {
|
136
|
-
let(:queries) { graphql_fixture("hello.graphql") }
|
177
|
+
let(:schema) { TestSchema.new(context) }
|
137
178
|
|
138
179
|
it "allows easily executing queries" do
|
139
|
-
|
180
|
+
query = schema.hello
|
140
181
|
|
141
|
-
expect(
|
142
|
-
expect(
|
182
|
+
expect(query).to be_successful
|
183
|
+
expect(query.data["hello"]).to eq("world")
|
143
184
|
end
|
144
|
-
end
|
145
|
-
```
|
146
185
|
|
147
|
-
|
186
|
+
it "parses errors" do
|
187
|
+
query = schema.kaboom
|
148
188
|
|
149
|
-
|
150
|
-
|
151
|
-
|
189
|
+
expect(query).not_to be_successful
|
190
|
+
expect(query.errors[0].message).to eq("Invalid")
|
191
|
+
expect(query.errors[0].code).to eq("VALIDATION_ERROR")
|
192
|
+
end
|
152
193
|
end
|
153
194
|
```
|
154
195
|
|
data/graphql-extras.gemspec
CHANGED
@@ -10,10 +10,10 @@ Gem::Specification.new do |spec|
|
|
10
10
|
|
11
11
|
spec.summary = %q{Utiltities for building GraphQL APIs.}
|
12
12
|
spec.description = %q{A set of modules and types for buildign GraphQL APIs.}
|
13
|
-
spec.homepage = "https://github.com/
|
13
|
+
spec.homepage = "https://github.com/rzane/graphql-extras"
|
14
14
|
|
15
15
|
spec.metadata["homepage_uri"] = spec.homepage
|
16
|
-
spec.metadata["source_code_uri"] = "https://github.com/
|
16
|
+
spec.metadata["source_code_uri"] = "https://github.com/rzane/graphql-extras"
|
17
17
|
|
18
18
|
# Specify which files should be added to the gem when it is released.
|
19
19
|
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
@@ -25,13 +25,12 @@ Gem::Specification.new do |spec|
|
|
25
25
|
spec.require_paths = ["lib"]
|
26
26
|
|
27
27
|
spec.add_dependency "activesupport", ">= 5.2"
|
28
|
-
spec.add_dependency "graphql", "~> 1.
|
29
|
-
spec.add_dependency "graphql-batch", "~> 0.4"
|
28
|
+
spec.add_dependency "graphql", "~> 1.12"
|
30
29
|
|
31
30
|
spec.add_development_dependency "bundler", "~> 2.0"
|
32
|
-
spec.add_development_dependency "rake", "~>
|
31
|
+
spec.add_development_dependency "rake", "~> 13.0"
|
33
32
|
spec.add_development_dependency "rspec", "~> 3.0"
|
34
|
-
spec.add_development_dependency "rspec-rails", "~>
|
33
|
+
spec.add_development_dependency "rspec-rails", "~> 4.0"
|
35
34
|
spec.add_development_dependency "actionpack", ">= 5.2"
|
36
35
|
spec.add_development_dependency "activerecord", ">= 5.2"
|
37
36
|
spec.add_development_dependency "sqlite3", "~> 1.4"
|
data/lib/graphql/extras.rb
CHANGED
@@ -0,0 +1,33 @@
|
|
1
|
+
module GraphQL
|
2
|
+
module Extras
|
3
|
+
class PreloadSource < GraphQL::Dataloader::Source
|
4
|
+
def initialize(preload)
|
5
|
+
@preload = preload
|
6
|
+
end
|
7
|
+
|
8
|
+
def fetch(records)
|
9
|
+
preloader = ActiveRecord::Associations::Preloader.new
|
10
|
+
preloader.preload(records, @preload)
|
11
|
+
records
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
module Preload
|
16
|
+
# @override
|
17
|
+
def initialize(*args, preload: nil, **opts, &block)
|
18
|
+
@preload = preload
|
19
|
+
super(*args, **opts, &block)
|
20
|
+
end
|
21
|
+
|
22
|
+
# @override
|
23
|
+
def resolve(object, args, context)
|
24
|
+
if @preload
|
25
|
+
loader = context.dataloader.with(PreloadSource, @preload)
|
26
|
+
loader.load(object.object)
|
27
|
+
end
|
28
|
+
|
29
|
+
super
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require "graphql/extras/test/schema"
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module GraphQL
|
2
|
+
module Extras
|
3
|
+
module Test
|
4
|
+
class Loader
|
5
|
+
FragmentNotFoundError = Class.new(StandardError)
|
6
|
+
|
7
|
+
attr_reader :fragments
|
8
|
+
attr_reader :operations
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@fragments = {}
|
12
|
+
@operations = {}
|
13
|
+
end
|
14
|
+
|
15
|
+
def load(path)
|
16
|
+
document = ::GraphQL.parse_file(path)
|
17
|
+
document.definitions.each do |node|
|
18
|
+
case node
|
19
|
+
when Nodes::FragmentDefinition
|
20
|
+
fragments[node.name] = node
|
21
|
+
when Nodes::OperationDefinition
|
22
|
+
operations[node.name] = node
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def print(operation)
|
28
|
+
printer = ::GraphQL::Language::Printer.new
|
29
|
+
nodes = [operation, *resolve_fragments(operation)]
|
30
|
+
nodes.map { |node| printer.print(node) }.join("\n")
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
Nodes = ::GraphQL::Language::Nodes
|
36
|
+
|
37
|
+
# Recursively iterate through the node's fields and find
|
38
|
+
# resolve all of the fragment definitions that are needed.
|
39
|
+
def resolve_fragments(node)
|
40
|
+
node.selections.flat_map do |field|
|
41
|
+
case field
|
42
|
+
when Nodes::FragmentSpread
|
43
|
+
fragment = fetch_fragment!(field.name)
|
44
|
+
[fragment, *resolve_fragments(fragment)]
|
45
|
+
else
|
46
|
+
resolve_fragments(field)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def fetch_fragment!(name)
|
52
|
+
fragments.fetch(name) do
|
53
|
+
raise FragmentNotFoundError, "Fragment `#{name}` is not defined"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module GraphQL
|
2
|
+
module Extras
|
3
|
+
module Test
|
4
|
+
class Response
|
5
|
+
attr_reader :data
|
6
|
+
attr_reader :errors
|
7
|
+
|
8
|
+
def initialize(payload)
|
9
|
+
@data = payload["data"]
|
10
|
+
@errors = payload.fetch("errors", []).map do |error|
|
11
|
+
Error.new(error)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def successful?
|
16
|
+
errors.empty?
|
17
|
+
end
|
18
|
+
|
19
|
+
class Error
|
20
|
+
attr_reader :message
|
21
|
+
attr_reader :extensions
|
22
|
+
attr_reader :code
|
23
|
+
attr_reader :path
|
24
|
+
attr_reader :locations
|
25
|
+
|
26
|
+
def initialize(payload)
|
27
|
+
@message = payload["message"]
|
28
|
+
@path = payload["path"]
|
29
|
+
@locations = payload["locations"]
|
30
|
+
@extensions = payload["extensions"]
|
31
|
+
@code = payload.dig("extensions", "code")
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require "securerandom"
|
2
|
+
require "active_support/inflector"
|
3
|
+
require "active_support/core_ext/hash"
|
4
|
+
require "graphql/extras/test/loader"
|
5
|
+
require "graphql/extras/test/response"
|
6
|
+
|
7
|
+
module GraphQL
|
8
|
+
module Extras
|
9
|
+
module Test
|
10
|
+
class Schema
|
11
|
+
def self.configure(schema:, queries:)
|
12
|
+
loader = Loader.new
|
13
|
+
|
14
|
+
Dir.glob(queries) do |path|
|
15
|
+
loader.load(path)
|
16
|
+
end
|
17
|
+
|
18
|
+
loader.operations.each do |name, operation|
|
19
|
+
query = loader.print(operation)
|
20
|
+
|
21
|
+
define_method(name.underscore) do |variables = {}|
|
22
|
+
__execute(schema, query, variables)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def initialize(context = {})
|
28
|
+
@context = context
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def __execute(schema, query, variables)
|
34
|
+
uploads = {}
|
35
|
+
|
36
|
+
variables = variables.deep_transform_keys do |key|
|
37
|
+
key.to_s.camelize(:lower)
|
38
|
+
end
|
39
|
+
|
40
|
+
variables = variables.deep_transform_values do |value|
|
41
|
+
if value.respond_to? :tempfile
|
42
|
+
id = SecureRandom.uuid
|
43
|
+
uploads[id] = value
|
44
|
+
id
|
45
|
+
else
|
46
|
+
value
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
context = @context.merge(uploads: uploads)
|
51
|
+
result = schema.execute(query, variables: variables, context: context)
|
52
|
+
Response.new(result.to_h)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: graphql-extras
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ray Zane
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-03-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -30,28 +30,14 @@ dependencies:
|
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '1.
|
33
|
+
version: '1.12'
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '1.
|
41
|
-
- !ruby/object:Gem::Dependency
|
42
|
-
name: graphql-batch
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
44
|
-
requirements:
|
45
|
-
- - "~>"
|
46
|
-
- !ruby/object:Gem::Version
|
47
|
-
version: '0.4'
|
48
|
-
type: :runtime
|
49
|
-
prerelease: false
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
51
|
-
requirements:
|
52
|
-
- - "~>"
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
version: '0.4'
|
40
|
+
version: '1.12'
|
55
41
|
- !ruby/object:Gem::Dependency
|
56
42
|
name: bundler
|
57
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -72,14 +58,14 @@ dependencies:
|
|
72
58
|
requirements:
|
73
59
|
- - "~>"
|
74
60
|
- !ruby/object:Gem::Version
|
75
|
-
version: '
|
61
|
+
version: '13.0'
|
76
62
|
type: :development
|
77
63
|
prerelease: false
|
78
64
|
version_requirements: !ruby/object:Gem::Requirement
|
79
65
|
requirements:
|
80
66
|
- - "~>"
|
81
67
|
- !ruby/object:Gem::Version
|
82
|
-
version: '
|
68
|
+
version: '13.0'
|
83
69
|
- !ruby/object:Gem::Dependency
|
84
70
|
name: rspec
|
85
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -100,14 +86,14 @@ dependencies:
|
|
100
86
|
requirements:
|
101
87
|
- - "~>"
|
102
88
|
- !ruby/object:Gem::Version
|
103
|
-
version: '
|
89
|
+
version: '4.0'
|
104
90
|
type: :development
|
105
91
|
prerelease: false
|
106
92
|
version_requirements: !ruby/object:Gem::Requirement
|
107
93
|
requirements:
|
108
94
|
- - "~>"
|
109
95
|
- !ruby/object:Gem::Version
|
110
|
-
version: '
|
96
|
+
version: '4.0'
|
111
97
|
- !ruby/object:Gem::Dependency
|
112
98
|
name: actionpack
|
113
99
|
requirement: !ruby/object:Gem::Requirement
|
@@ -157,9 +143,10 @@ executables: []
|
|
157
143
|
extensions: []
|
158
144
|
extra_rdoc_files: []
|
159
145
|
files:
|
146
|
+
- ".github/workflows/build.yml"
|
147
|
+
- ".github/workflows/publish.yml"
|
160
148
|
- ".gitignore"
|
161
149
|
- ".rspec"
|
162
|
-
- ".travis.yml"
|
163
150
|
- Gemfile
|
164
151
|
- README.md
|
165
152
|
- Rakefile
|
@@ -167,16 +154,19 @@ files:
|
|
167
154
|
- bin/setup
|
168
155
|
- graphql-extras.gemspec
|
169
156
|
- lib/graphql/extras.rb
|
170
|
-
- lib/graphql/extras/batch.rb
|
171
157
|
- lib/graphql/extras/controller.rb
|
172
|
-
- lib/graphql/extras/
|
158
|
+
- lib/graphql/extras/preload.rb
|
159
|
+
- lib/graphql/extras/test.rb
|
160
|
+
- lib/graphql/extras/test/loader.rb
|
161
|
+
- lib/graphql/extras/test/response.rb
|
162
|
+
- lib/graphql/extras/test/schema.rb
|
173
163
|
- lib/graphql/extras/types.rb
|
174
164
|
- lib/graphql/extras/version.rb
|
175
|
-
homepage: https://github.com/
|
165
|
+
homepage: https://github.com/rzane/graphql-extras
|
176
166
|
licenses: []
|
177
167
|
metadata:
|
178
|
-
homepage_uri: https://github.com/
|
179
|
-
source_code_uri: https://github.com/
|
168
|
+
homepage_uri: https://github.com/rzane/graphql-extras
|
169
|
+
source_code_uri: https://github.com/rzane/graphql-extras
|
180
170
|
post_install_message:
|
181
171
|
rdoc_options: []
|
182
172
|
require_paths:
|
@@ -192,7 +182,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
192
182
|
- !ruby/object:Gem::Version
|
193
183
|
version: '0'
|
194
184
|
requirements: []
|
195
|
-
rubygems_version: 3.
|
185
|
+
rubygems_version: 3.0.3
|
196
186
|
signing_key:
|
197
187
|
specification_version: 4
|
198
188
|
summary: Utiltities for building GraphQL APIs.
|
data/.travis.yml
DELETED
data/lib/graphql/extras/batch.rb
DELETED
@@ -1,47 +0,0 @@
|
|
1
|
-
require "graphql/batch"
|
2
|
-
|
3
|
-
module GraphQL
|
4
|
-
module Extras
|
5
|
-
module Batch
|
6
|
-
class AssociationLoader < GraphQL::Batch::Loader
|
7
|
-
def initialize(name, preload: name)
|
8
|
-
@name = name
|
9
|
-
@preload = preload
|
10
|
-
end
|
11
|
-
|
12
|
-
def cache_key(record)
|
13
|
-
record.object_id
|
14
|
-
end
|
15
|
-
|
16
|
-
def perform(records)
|
17
|
-
preloader = ActiveRecord::Associations::Preloader.new
|
18
|
-
preloader.preload(records, @preload)
|
19
|
-
|
20
|
-
records.each do |record|
|
21
|
-
fulfill(record, record.public_send(@name))
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
module Resolvers
|
27
|
-
def self.included(base)
|
28
|
-
base.extend ClassMethods
|
29
|
-
end
|
30
|
-
|
31
|
-
module ClassMethods
|
32
|
-
def association(name)
|
33
|
-
lambda do |record, _args, _ctx|
|
34
|
-
loader = AssociationLoader.for(name)
|
35
|
-
loader.load(record)
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
def association(record, name)
|
41
|
-
loader = AssociationLoader.for(name)
|
42
|
-
loader.load(record)
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
data/lib/graphql/extras/rspec.rb
DELETED
@@ -1,133 +0,0 @@
|
|
1
|
-
require "yaml"
|
2
|
-
require "active_support/inflector"
|
3
|
-
require "active_support/core_ext/hash"
|
4
|
-
|
5
|
-
module GraphQL
|
6
|
-
module Extras
|
7
|
-
module RSpec
|
8
|
-
class Queries
|
9
|
-
def add(key, value)
|
10
|
-
define_singleton_method(key) { value }
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
class Schema
|
15
|
-
def initialize(schema, context: {})
|
16
|
-
@schema = schema
|
17
|
-
@context = context
|
18
|
-
end
|
19
|
-
|
20
|
-
def execute(query, variables = {})
|
21
|
-
variables = deep_camelize_keys(variables)
|
22
|
-
variables, uploads = extract_uploads(variables)
|
23
|
-
context = @context.merge(uploads: uploads)
|
24
|
-
|
25
|
-
result = @schema.execute(query, variables: variables, context: context)
|
26
|
-
result.to_h
|
27
|
-
end
|
28
|
-
|
29
|
-
private
|
30
|
-
|
31
|
-
def extract_uploads(variables)
|
32
|
-
uploads = {}
|
33
|
-
variables = deep_transform_values(variables) { |value|
|
34
|
-
if upload?(value)
|
35
|
-
SecureRandom.hex.tap { |key| uploads.merge!(key => value) }
|
36
|
-
else
|
37
|
-
value
|
38
|
-
end
|
39
|
-
}
|
40
|
-
|
41
|
-
[variables, uploads]
|
42
|
-
end
|
43
|
-
|
44
|
-
def deep_camelize_keys(variables)
|
45
|
-
variables.deep_transform_keys { |key| key.to_s.camelize(:lower) }
|
46
|
-
end
|
47
|
-
|
48
|
-
def deep_transform_values(data, &block)
|
49
|
-
case data
|
50
|
-
when Array
|
51
|
-
data.map { |v| deep_transform_values(v, &block) }
|
52
|
-
when Hash
|
53
|
-
data.transform_values { |v| deep_transform_values(v, &block) }
|
54
|
-
else
|
55
|
-
yield data
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
def upload?(value)
|
60
|
-
value.respond_to?(:tempfile) && value.respond_to?(:original_filename)
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
class Parser
|
65
|
-
include ::GraphQL::Language
|
66
|
-
|
67
|
-
def initialize(document)
|
68
|
-
@operations = document.definitions
|
69
|
-
.grep(Nodes::OperationDefinition)
|
70
|
-
|
71
|
-
@fragments = document.definitions
|
72
|
-
.grep(Nodes::FragmentDefinition)
|
73
|
-
.reduce({}) { |acc, f| acc.merge(f.name => f) }
|
74
|
-
end
|
75
|
-
|
76
|
-
def parse
|
77
|
-
queries = Queries.new
|
78
|
-
printer = Printer.new
|
79
|
-
|
80
|
-
@operations.each do |op|
|
81
|
-
nodes = [op, *find_fragments(op)]
|
82
|
-
nodes = nodes.map { |node| printer.print(node) }
|
83
|
-
queries.add op.name.underscore, nodes.join
|
84
|
-
end
|
85
|
-
|
86
|
-
queries
|
87
|
-
end
|
88
|
-
|
89
|
-
private
|
90
|
-
|
91
|
-
def find_fragments(node)
|
92
|
-
node.selections.flat_map do |field|
|
93
|
-
if field.is_a? Nodes::FragmentSpread
|
94
|
-
fragment = @fragments.fetch(field.name)
|
95
|
-
[fragment, *find_fragments(fragment)]
|
96
|
-
else
|
97
|
-
find_fragments(field)
|
98
|
-
end
|
99
|
-
end
|
100
|
-
end
|
101
|
-
end
|
102
|
-
|
103
|
-
def graphql_fixture(filename)
|
104
|
-
root = ::RSpec.configuration.graphql_fixture_path
|
105
|
-
file = File.join(root, filename)
|
106
|
-
document = ::GraphQL.parse_file(file)
|
107
|
-
parser = Parser.new(document)
|
108
|
-
parser.parse
|
109
|
-
end
|
110
|
-
|
111
|
-
def use_schema(*args)
|
112
|
-
Schema.new(*args)
|
113
|
-
end
|
114
|
-
end
|
115
|
-
end
|
116
|
-
end
|
117
|
-
|
118
|
-
RSpec::Matchers.define :be_successful_query do
|
119
|
-
match do |result|
|
120
|
-
result['errors'].nil?
|
121
|
-
end
|
122
|
-
|
123
|
-
failure_message do |result|
|
124
|
-
errors = result['errors'].map(&:deep_stringify_keys)
|
125
|
-
message = "expected query to be successful, but encountered errors:\n"
|
126
|
-
message + errors.to_yaml.lines.drop(1).join.indent(2)
|
127
|
-
end
|
128
|
-
end
|
129
|
-
|
130
|
-
RSpec.configure do |config|
|
131
|
-
config.add_setting :graphql_fixture_path, default: "spec/fixtures/graphql"
|
132
|
-
config.include GraphQL::Extras::RSpec, type: :graphql
|
133
|
-
end
|