graphql-extras 0.2.4 → 0.3.2
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 +5 -5
- data/.github/workflows/build.yml +25 -0
- data/.github/workflows/publish.yml +41 -0
- data/README.md +64 -27
- data/graphql-extras.gemspec +5 -5
- data/lib/graphql/extras.rb +2 -1
- data/lib/graphql/extras/association_loader.rb +24 -0
- data/lib/graphql/extras/preload.rb +23 -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 +20 -16
- data/.travis.yml +0 -7
- data/lib/graphql/extras/batch.rb +0 -47
- data/lib/graphql/extras/rspec.rb +0 -103
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: eb6a2324dfea95555ab147de0f4d79601603e21ca7dde008939795b12d40064c
|
4
|
+
data.tar.gz: ef735334a0b5be7ac0286c45886aa60d507482b73a95c0af89a2d1b5dfae27cc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b412b43cd6d6c3865726770b1bd54d121e5a122fdafcd0f4f0e141edb6cd9e83ca13c47f73530025be3d16498144f97f3ddc40c02897049679bfebad8189ed56
|
7
|
+
data.tar.gz: '018ccec04e7e67b563e460fcbe7d1649a097087846b488985c34072004bcd2839162e4a3f3131c76718ea99a1d15633bc5c9f7ec3190cef65de203eb6c533a4b'
|
@@ -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::AssociationLoader](#graphqlextrasassociationloader)
|
18
|
+
- [GraphQL::Extras::Preload](#graphqlextraspreload)
|
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,30 +53,36 @@ class GraphqlController < ApplicationController
|
|
30
53
|
end
|
31
54
|
```
|
32
55
|
|
33
|
-
### GraphQL::Extras::
|
56
|
+
### GraphQL::Extras::AssociationLoader
|
34
57
|
|
35
58
|
This is a subclass of [`GraphQL::Batch::Loader`](https://github.com/Shopify/graphql-batch) that performs eager loading of Active Record associations.
|
36
59
|
|
37
60
|
```ruby
|
38
|
-
loader = GraphQL::Extras::
|
61
|
+
loader = GraphQL::Extras::AssociationLoader.for(:blog)
|
39
62
|
loader.load(Post.first)
|
40
63
|
loader.load_many(Post.all)
|
41
64
|
```
|
42
65
|
|
43
|
-
### GraphQL::Extras::
|
66
|
+
### GraphQL::Extras::Preload
|
44
67
|
|
45
|
-
This
|
68
|
+
This allows you to preload associations before resolving fields.
|
46
69
|
|
47
70
|
```ruby
|
48
|
-
class
|
49
|
-
|
71
|
+
class BaseField < GraphQL::Schema::Field
|
72
|
+
prepend GraphQL::Extras::Preload
|
73
|
+
end
|
74
|
+
|
75
|
+
class BaseObject < GraphQL::Schema::Object
|
76
|
+
field_class BaseField
|
77
|
+
end
|
50
78
|
|
51
|
-
|
52
|
-
field :
|
53
|
-
field :
|
79
|
+
class PostType < BaseObject
|
80
|
+
field :author, AuthorType, preload: :author, null: false
|
81
|
+
field :author_posts, [PostType], preload: {author: :posts}, null: false
|
82
|
+
field :depends_on_author, Integer, preload: :author, null: false
|
54
83
|
|
55
|
-
def
|
56
|
-
|
84
|
+
def author_posts
|
85
|
+
object.author.posts
|
57
86
|
end
|
58
87
|
end
|
59
88
|
```
|
@@ -88,7 +117,7 @@ This scalar takes a `DateTime` and transmits it as a string, using ISO 8601 form
|
|
88
117
|
field :created_at, DateTime, required: true
|
89
118
|
```
|
90
119
|
|
91
|
-
|
120
|
+
_Note: This is just an alias for the `ISO8601DateTime` type that is included in the `graphql` gem._
|
92
121
|
|
93
122
|
#### Decimal
|
94
123
|
|
@@ -119,36 +148,44 @@ Take note of the correspondence between the value `"image"` and the additional H
|
|
119
148
|
|
120
149
|
See [apollo-link-upload](https://github.com/rzane/apollo-link-upload) for the client-side implementation.
|
121
150
|
|
122
|
-
###
|
151
|
+
### GraphQL::Extras::Test
|
123
152
|
|
124
|
-
|
153
|
+
This module makes it really easy to test your schema.
|
154
|
+
|
155
|
+
First, create a test schema:
|
125
156
|
|
126
157
|
```ruby
|
127
|
-
|
158
|
+
# spec/support/test_schema.rb
|
159
|
+
require "graphql/extras/test"
|
160
|
+
|
161
|
+
class TestSchema < GraphQL::Extras::Test::Schema
|
162
|
+
configure schema: Schema, queries: "spec/**/*.graphql"
|
163
|
+
end
|
128
164
|
```
|
129
165
|
|
130
166
|
Now, you can run tests like so:
|
131
167
|
|
132
168
|
```ruby
|
169
|
+
require "support/test_schema"
|
170
|
+
|
133
171
|
RSpec.describe "hello" do
|
134
172
|
let(:context) { { name: "Ray" } }
|
135
|
-
let(:schema) {
|
136
|
-
let(:queries) { graphql_fixture("hello.graphql") }
|
173
|
+
let(:schema) { TestSchema.new(context) }
|
137
174
|
|
138
175
|
it "allows easily executing queries" do
|
139
|
-
|
176
|
+
query = schema.hello
|
140
177
|
|
141
|
-
expect(
|
142
|
-
expect(
|
178
|
+
expect(query).to be_successful
|
179
|
+
expect(query.data["hello"]).to eq("world")
|
143
180
|
end
|
144
|
-
end
|
145
|
-
```
|
146
181
|
|
147
|
-
|
182
|
+
it "parses errors" do
|
183
|
+
query = schema.kaboom
|
148
184
|
|
149
|
-
|
150
|
-
|
151
|
-
|
185
|
+
expect(query).not_to be_successful
|
186
|
+
expect(query.errors[0].message).to eq("Invalid")
|
187
|
+
expect(query.errors[0].code).to eq("VALIDATION_ERROR")
|
188
|
+
end
|
152
189
|
end
|
153
190
|
```
|
154
191
|
|
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,13 @@ 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.
|
28
|
+
spec.add_dependency "graphql", "~> 1.12"
|
29
29
|
spec.add_dependency "graphql-batch", "~> 0.4"
|
30
30
|
|
31
31
|
spec.add_development_dependency "bundler", "~> 2.0"
|
32
|
-
spec.add_development_dependency "rake", "~>
|
32
|
+
spec.add_development_dependency "rake", "~> 13.0"
|
33
33
|
spec.add_development_dependency "rspec", "~> 3.0"
|
34
|
-
spec.add_development_dependency "rspec-rails", "~>
|
34
|
+
spec.add_development_dependency "rspec-rails", "~> 4.0"
|
35
35
|
spec.add_development_dependency "actionpack", ">= 5.2"
|
36
36
|
spec.add_development_dependency "activerecord", ">= 5.2"
|
37
37
|
spec.add_development_dependency "sqlite3", "~> 1.4"
|
data/lib/graphql/extras.rb
CHANGED
@@ -0,0 +1,24 @@
|
|
1
|
+
require "graphql/batch"
|
2
|
+
|
3
|
+
module GraphQL
|
4
|
+
module Extras
|
5
|
+
class AssociationLoader < GraphQL::Batch::Loader
|
6
|
+
def initialize(preload)
|
7
|
+
@preload = preload
|
8
|
+
end
|
9
|
+
|
10
|
+
def cache_key(record)
|
11
|
+
record.object_id
|
12
|
+
end
|
13
|
+
|
14
|
+
def perform(records)
|
15
|
+
preloader = ActiveRecord::Associations::Preloader.new
|
16
|
+
preloader.preload(records, @preload)
|
17
|
+
|
18
|
+
records.each do |record|
|
19
|
+
fulfill(record, nil) unless fulfilled?(record)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require "graphql/extras/association_loader"
|
2
|
+
|
3
|
+
module GraphQL
|
4
|
+
module Extras
|
5
|
+
module Preload
|
6
|
+
# @override
|
7
|
+
def initialize(*args, preload: nil, **opts, &block)
|
8
|
+
@preload = preload
|
9
|
+
super(*args, **opts, &block)
|
10
|
+
end
|
11
|
+
|
12
|
+
# @override
|
13
|
+
def resolve(object, args, ctx)
|
14
|
+
return super unless @preload
|
15
|
+
|
16
|
+
loader = AssociationLoader.for(@preload)
|
17
|
+
loader.load(object.object).then do
|
18
|
+
super(object, args, ctx)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
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.2
|
4
|
+
version: 0.3.2
|
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-02-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -30,14 +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.
|
40
|
+
version: '1.12'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: graphql-batch
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -72,14 +72,14 @@ dependencies:
|
|
72
72
|
requirements:
|
73
73
|
- - "~>"
|
74
74
|
- !ruby/object:Gem::Version
|
75
|
-
version: '
|
75
|
+
version: '13.0'
|
76
76
|
type: :development
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
80
|
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
|
-
version: '
|
82
|
+
version: '13.0'
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
84
|
name: rspec
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -100,14 +100,14 @@ dependencies:
|
|
100
100
|
requirements:
|
101
101
|
- - "~>"
|
102
102
|
- !ruby/object:Gem::Version
|
103
|
-
version: '
|
103
|
+
version: '4.0'
|
104
104
|
type: :development
|
105
105
|
prerelease: false
|
106
106
|
version_requirements: !ruby/object:Gem::Requirement
|
107
107
|
requirements:
|
108
108
|
- - "~>"
|
109
109
|
- !ruby/object:Gem::Version
|
110
|
-
version: '
|
110
|
+
version: '4.0'
|
111
111
|
- !ruby/object:Gem::Dependency
|
112
112
|
name: actionpack
|
113
113
|
requirement: !ruby/object:Gem::Requirement
|
@@ -157,9 +157,10 @@ executables: []
|
|
157
157
|
extensions: []
|
158
158
|
extra_rdoc_files: []
|
159
159
|
files:
|
160
|
+
- ".github/workflows/build.yml"
|
161
|
+
- ".github/workflows/publish.yml"
|
160
162
|
- ".gitignore"
|
161
163
|
- ".rspec"
|
162
|
-
- ".travis.yml"
|
163
164
|
- Gemfile
|
164
165
|
- README.md
|
165
166
|
- Rakefile
|
@@ -167,16 +168,20 @@ files:
|
|
167
168
|
- bin/setup
|
168
169
|
- graphql-extras.gemspec
|
169
170
|
- lib/graphql/extras.rb
|
170
|
-
- lib/graphql/extras/
|
171
|
+
- lib/graphql/extras/association_loader.rb
|
171
172
|
- lib/graphql/extras/controller.rb
|
172
|
-
- lib/graphql/extras/
|
173
|
+
- lib/graphql/extras/preload.rb
|
174
|
+
- lib/graphql/extras/test.rb
|
175
|
+
- lib/graphql/extras/test/loader.rb
|
176
|
+
- lib/graphql/extras/test/response.rb
|
177
|
+
- lib/graphql/extras/test/schema.rb
|
173
178
|
- lib/graphql/extras/types.rb
|
174
179
|
- lib/graphql/extras/version.rb
|
175
|
-
homepage: https://github.com/
|
180
|
+
homepage: https://github.com/rzane/graphql-extras
|
176
181
|
licenses: []
|
177
182
|
metadata:
|
178
|
-
homepage_uri: https://github.com/
|
179
|
-
source_code_uri: https://github.com/
|
183
|
+
homepage_uri: https://github.com/rzane/graphql-extras
|
184
|
+
source_code_uri: https://github.com/rzane/graphql-extras
|
180
185
|
post_install_message:
|
181
186
|
rdoc_options: []
|
182
187
|
require_paths:
|
@@ -192,8 +197,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
192
197
|
- !ruby/object:Gem::Version
|
193
198
|
version: '0'
|
194
199
|
requirements: []
|
195
|
-
|
196
|
-
rubygems_version: 2.6.14
|
200
|
+
rubygems_version: 3.0.3
|
197
201
|
signing_key:
|
198
202
|
specification_version: 4
|
199
203
|
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,103 +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 initialize(values)
|
10
|
-
values.each do |key, value|
|
11
|
-
define_singleton_method(key) { value }
|
12
|
-
end
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
class Schema
|
17
|
-
def initialize(schema, context: {})
|
18
|
-
@schema = schema
|
19
|
-
@context = context
|
20
|
-
end
|
21
|
-
|
22
|
-
def execute(query, variables = {})
|
23
|
-
variables = deep_camelize_keys(variables)
|
24
|
-
variables, uploads = extract_uploads(variables)
|
25
|
-
context = @context.merge(uploads: uploads)
|
26
|
-
|
27
|
-
result = @schema.execute(query, variables: variables, context: context)
|
28
|
-
result.to_h
|
29
|
-
end
|
30
|
-
|
31
|
-
private
|
32
|
-
|
33
|
-
def extract_uploads(variables)
|
34
|
-
uploads = {}
|
35
|
-
variables = deep_transform_values(variables) { |value|
|
36
|
-
if upload?(value)
|
37
|
-
SecureRandom.hex.tap { |key| uploads.merge!(key => value) }
|
38
|
-
else
|
39
|
-
value
|
40
|
-
end
|
41
|
-
}
|
42
|
-
|
43
|
-
[variables, uploads]
|
44
|
-
end
|
45
|
-
|
46
|
-
def deep_camelize_keys(variables)
|
47
|
-
variables.deep_transform_keys { |key| key.to_s.camelize(:lower) }
|
48
|
-
end
|
49
|
-
|
50
|
-
def deep_transform_values(data, &block)
|
51
|
-
case data
|
52
|
-
when Array
|
53
|
-
data.map { |v| deep_transform_values(v, &block) }
|
54
|
-
when Hash
|
55
|
-
data.transform_values { |v| deep_transform_values(v, &block) }
|
56
|
-
else
|
57
|
-
yield data
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
def upload?(value)
|
62
|
-
value.respond_to?(:tempfile) && value.respond_to?(:original_filename)
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
def graphql_fixture(filename)
|
67
|
-
root = ::RSpec.configuration.graphql_fixture_path
|
68
|
-
contents = File.read(File.join(root, filename))
|
69
|
-
|
70
|
-
parts = contents.split(/^(?=fragment|query|mutation|subscription)/)
|
71
|
-
queries, fragments = parts.partition { |query| query !~ /^fragment/ }
|
72
|
-
|
73
|
-
result = queries.reduce({}) { |acc, query|
|
74
|
-
name = query.split(/\W+/).at(1).underscore.to_sym
|
75
|
-
acc.merge(name => [*fragments, query].join)
|
76
|
-
}
|
77
|
-
|
78
|
-
Queries.new(result)
|
79
|
-
end
|
80
|
-
|
81
|
-
def use_schema(*args)
|
82
|
-
Schema.new(*args)
|
83
|
-
end
|
84
|
-
end
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
RSpec::Matchers.define :be_successful_query do
|
89
|
-
match do |result|
|
90
|
-
result['errors'].nil?
|
91
|
-
end
|
92
|
-
|
93
|
-
failure_message do |result|
|
94
|
-
errors = result['errors'].map(&:deep_stringify_keys)
|
95
|
-
message = "expected query to be successful, but encountered errors:\n"
|
96
|
-
message + errors.to_yaml.lines.drop(1).join.indent(2)
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
|
-
RSpec.configure do |config|
|
101
|
-
config.add_setting :graphql_fixture_path, default: "spec/fixtures/graphql"
|
102
|
-
config.include GraphQL::Extras::RSpec, type: :graphql
|
103
|
-
end
|