graphql-decorate 0.2.1 → 1.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +38 -0
- data/.github/workflows/gem-push-on-release.yml +4 -3
- data/.rubocop.yml +14 -0
- data/Gemfile +4 -2
- data/README.md +62 -31
- data/Rakefile +5 -3
- data/bin/console +4 -3
- data/graphql-decorate.gemspec +33 -18
- data/lib/graphql/decorate/configuration.rb +4 -2
- data/lib/graphql/decorate/decoration.rb +34 -0
- data/lib/graphql/decorate/extract_type.rb +14 -0
- data/lib/graphql/decorate/field_extension.rb +27 -13
- data/lib/graphql/decorate/metadata.rb +29 -0
- data/lib/graphql/decorate/object_integration.rb +15 -19
- data/lib/graphql/decorate/type_attributes.rb +11 -14
- data/lib/graphql/decorate/undecorated_field.rb +114 -0
- data/lib/graphql/decorate/version.rb +3 -1
- data/lib/graphql/decorate.rb +22 -4
- metadata +72 -14
- data/.github/workflows/rspec.yml +0 -32
- data/lib/graphql/decorate/connection.rb +0 -52
- data/lib/graphql/decorate/field_context.rb +0 -18
- data/lib/graphql/decorate/field_integration.rb +0 -38
- data/lib/graphql/decorate/object.rb +0 -71
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ac0949a542979b4218036dae55013d1b148cda15cfafdab0d741aa23d882d289
|
4
|
+
data.tar.gz: ac03a2c2c2d554fadd4a7b1ad489d572954c84d7efb77f8afe301c3e8f5909ab
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4646f9ea8f791e8ae56163ee8f1ff67849a697365eed6fc5ac9d3ef4327933d6d650cb2aef6cf8160ebea76826de0152d13c9c0b5719da5bdb02e012d9371587
|
7
|
+
data.tar.gz: 02e3384f30868f20d490db4dc3481c77bc94f1ce1e3ad446c15a9bb63a3825fd98d7712341452ed532e4a794379503a0e9d4db518080c521d980579337373fd3
|
@@ -0,0 +1,38 @@
|
|
1
|
+
name: CI
|
2
|
+
|
3
|
+
on:
|
4
|
+
push:
|
5
|
+
branches: [ master ]
|
6
|
+
pull_request:
|
7
|
+
branches: [ master ]
|
8
|
+
|
9
|
+
jobs:
|
10
|
+
ci:
|
11
|
+
runs-on: ubuntu-latest
|
12
|
+
strategy:
|
13
|
+
matrix:
|
14
|
+
ruby-version: ['2.6', '2.7', '3.0']
|
15
|
+
|
16
|
+
steps:
|
17
|
+
- uses: actions/checkout@v2
|
18
|
+
|
19
|
+
- name: Set up Ruby
|
20
|
+
uses: ruby/setup-ruby@v1
|
21
|
+
with:
|
22
|
+
ruby-version: ${{ matrix.ruby-version }}
|
23
|
+
bundler-cache: true
|
24
|
+
|
25
|
+
- name: Run tests
|
26
|
+
run: bundle exec rake
|
27
|
+
|
28
|
+
- name: Run RuboCop
|
29
|
+
run: bundle exec rubocop
|
30
|
+
|
31
|
+
- name: Check documentation completion
|
32
|
+
run: |
|
33
|
+
completion_percentage=$(bundle exec yard | tee /dev/stdout | tail -1 | cut -d'%' -f1 | xargs)
|
34
|
+
echo $completion_percentage
|
35
|
+
if [[ $completion_percentage != "100.00" ]]; then
|
36
|
+
echo "YARD documentation must be at 100%"
|
37
|
+
exit 2;
|
38
|
+
fi
|
@@ -11,10 +11,11 @@ jobs:
|
|
11
11
|
|
12
12
|
steps:
|
13
13
|
- uses: actions/checkout@v2
|
14
|
-
- name: Set up Ruby
|
15
|
-
uses:
|
14
|
+
- name: Set up Ruby
|
15
|
+
uses: ruby/setup-ruby@v1
|
16
16
|
with:
|
17
|
-
ruby-version: 2.6
|
17
|
+
ruby-version: 2.6
|
18
|
+
bundler-cache: true
|
18
19
|
|
19
20
|
- name: Verify version number matches
|
20
21
|
run: |
|
data/.rubocop.yml
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require:
|
2
|
+
rubocop-rspec
|
3
|
+
AllCops:
|
4
|
+
NewCops: enable
|
5
|
+
TargetRubyVersion: 2.6
|
6
|
+
SuggestExtensions: false
|
7
|
+
RSpec/FilePath:
|
8
|
+
CustomTransform:
|
9
|
+
GraphQL: graphql
|
10
|
+
Metrics/BlockLength:
|
11
|
+
Exclude:
|
12
|
+
- 'spec/**/*.rb'
|
13
|
+
RSpec/MessageSpies:
|
14
|
+
EnforcedStyle: receive
|
data/Gemfile
CHANGED
@@ -1,6 +1,8 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
3
|
+
source 'https://rubygems.org'
|
4
|
+
|
5
|
+
git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
|
4
6
|
|
5
7
|
# Specify your gem's dependencies in graphql-decorate.gemspec
|
6
8
|
gemspec
|
data/README.md
CHANGED
@@ -1,6 +1,9 @@
|
|
1
|
+
[![Gem Version](https://badge.fury.io/rb/graphql-decorate.svg)](https://badge.fury.io/rb/graphql-decorate)
|
2
|
+
![CI](https://github.com/TrueCar/graphql-decorate/actions/workflows/ci.yml/badge.svg)
|
3
|
+
|
1
4
|
# GraphQL Decorate
|
2
5
|
|
3
|
-
`graphql-decorate` adds an easy-to-use interface for decorating types in `graphql-ruby
|
6
|
+
`graphql-decorate` adds an easy-to-use interface for decorating types in [`graphql-ruby`](https://github.com/rmosolgo/graphql-ruby). It lets
|
4
7
|
you move logic out of your type files and keep them declarative.
|
5
8
|
|
6
9
|
## Installation
|
@@ -19,16 +22,19 @@ Or install it yourself as:
|
|
19
22
|
|
20
23
|
$ gem install graphql-decorate
|
21
24
|
|
22
|
-
Once the gem is installed, you need to add the
|
25
|
+
Once the gem is installed, you need to add the plugin to your schema and the integration into
|
26
|
+
your base object class.
|
23
27
|
```ruby
|
24
|
-
class
|
25
|
-
|
28
|
+
class Schema < GraphQL::Schema
|
29
|
+
use GraphQL::Decorate
|
26
30
|
end
|
27
31
|
|
28
|
-
class
|
29
|
-
include GraphQL::Decorate::
|
32
|
+
class BaseObject < GraphQL::Schema::Object
|
33
|
+
include GraphQL::Decorate::ObjectIntegration
|
30
34
|
end
|
31
35
|
```
|
36
|
+
Note that `use GraphQL::Decorate` must be included in the schema _after_ `query` and `mutation`
|
37
|
+
so that the fields to be extended are initialized first.
|
32
38
|
|
33
39
|
## Usage
|
34
40
|
|
@@ -48,7 +54,7 @@ class RectangleDecorator < BaseDecorator
|
|
48
54
|
end
|
49
55
|
end
|
50
56
|
|
51
|
-
class
|
57
|
+
class RectangleType < BaseObject
|
52
58
|
decorate_with RectangleDecorator
|
53
59
|
|
54
60
|
field :area, Int, null: false
|
@@ -59,24 +65,27 @@ In this example, the `Rectangle` type is being decorated with a `RectangleDecora
|
|
59
65
|
`RectangleDecorator`. All of the methods on the decorator are accessible on the type.
|
60
66
|
|
61
67
|
### Decorators
|
62
|
-
By default, `graphql-decorate` is set up to work with
|
68
|
+
By default, `graphql-decorate` is set up to work with [`draper`](https://github.com/drapergem/draper) style decorators. These decorators
|
63
69
|
provide a `decorate` method that wraps the original object and returns an instance of the
|
64
|
-
decorator. They can also take in
|
70
|
+
decorator. They can also take in additional metadata.
|
65
71
|
```ruby
|
66
|
-
RectangleDecorator.decorate(rectangle, context)
|
72
|
+
RectangleDecorator.decorate(rectangle, context: metadata)
|
67
73
|
```
|
68
74
|
If you are using a different decorator pattern then you can override this default behavior in
|
69
75
|
the configuration.
|
70
76
|
```ruby
|
71
77
|
GraphQL::Decorate.configure do |config|
|
72
|
-
config.decorate do |decorator_class, object,
|
78
|
+
config.decorate do |decorator_class, object, _metadata|
|
73
79
|
decorator_class.decorate_differently(object)
|
74
80
|
end
|
75
81
|
end
|
76
82
|
```
|
77
83
|
|
78
84
|
### Types
|
79
|
-
|
85
|
+
Two methods are made available on your type classes: `decorate_with` and `decorate_metadata`.
|
86
|
+
Every method that yields the underlying object will also yield the current GraphQL `context`.
|
87
|
+
If decoration depends on some context in the current query then you can access it when the field is resolved.
|
88
|
+
|
80
89
|
#### decorate_with
|
81
90
|
`decorate_with` accepts a decorator class that will decorate every instance of your type.
|
82
91
|
```ruby
|
@@ -85,12 +94,11 @@ class Rectangle < GraphQL::Schema::Object
|
|
85
94
|
end
|
86
95
|
```
|
87
96
|
|
88
|
-
|
89
|
-
`decorate_when` accepts a block which yields the underlying object. If you have multiple
|
97
|
+
`decorate_with` optionally accepts a block which yields the underlying object. If you have multiple
|
90
98
|
possible decorator classes you can return the one intended for the underling object.
|
91
99
|
```ruby
|
92
100
|
class Rectangle < GraphQL::Schema::Object
|
93
|
-
|
101
|
+
decorate_with do |object, _graphql_context|
|
94
102
|
if object.length == object.width
|
95
103
|
SquareDecorator
|
96
104
|
else
|
@@ -100,30 +108,50 @@ class Rectangle < GraphQL::Schema::Object
|
|
100
108
|
end
|
101
109
|
```
|
102
110
|
|
103
|
-
####
|
104
|
-
|
105
|
-
|
111
|
+
#### decorate_metadata
|
112
|
+
If your decorator pattern allows additional metadata to be passed into the decorators, you can
|
113
|
+
define it here. By default every metadata hash will contain `{ graphql: true }`. This is
|
114
|
+
useful if your decorator logic needs to diverge when used in a GraphQL context. Ideally your
|
115
|
+
decorators are agnostic to where they are being used, but it is available if needed.
|
116
|
+
|
117
|
+
`decorate_metadata` yields a `GraphQL::Decorate::Metadata` metadata instance. It responds to two
|
118
|
+
methods: `unscoped` and `scoped`. `unscoped` sets metadata for a resolved field. `scoped` sets
|
119
|
+
metadata for a resolved field and all of its child fields. `unscoped` and `scoped` are expected
|
120
|
+
to return `Hash`s.
|
121
|
+
|
106
122
|
```ruby
|
107
123
|
class Rectangle < GraphQL::Schema::Object
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
124
|
+
decorate_metadata do |metadata|
|
125
|
+
metadata.unscoped do |object, _graphql_context|
|
126
|
+
{
|
127
|
+
name: object.name
|
128
|
+
}
|
129
|
+
end
|
130
|
+
|
131
|
+
metadata.scoped do |object, _graphql_context|
|
132
|
+
{
|
133
|
+
inside_rectangle: true
|
134
|
+
}
|
135
|
+
end
|
112
136
|
end
|
113
137
|
end
|
114
138
|
```
|
115
|
-
`RectangleDecorator` will be initialized with
|
139
|
+
`RectangleDecorator` will be initialized with metadata `{ name: <object_name>,
|
140
|
+
inside_rectangle: true, graphql: true }`. All child fields of `Rectangle` will be initialized
|
141
|
+
with metadata `{ inside_rectangle: true, graphql: true }`.
|
116
142
|
|
117
143
|
#### Combinations
|
118
|
-
You can mix and match these methods to suit your needs. Note that if `
|
119
|
-
`
|
144
|
+
You can mix and match these methods to suit your needs. Note that if `unscoped` and
|
145
|
+
`scoped` are both provided for metadata that `scoped` will override any shared keys.
|
120
146
|
```ruby
|
121
147
|
class Rectangle < GraphQL::Schema::Object
|
122
148
|
decorate_with RectangleDecorator
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
149
|
+
decorate_metadata do |metadata|
|
150
|
+
metadata.scoped do |object, _graphql_context|
|
151
|
+
{
|
152
|
+
name: object.name
|
153
|
+
}
|
154
|
+
end
|
127
155
|
end
|
128
156
|
end
|
129
157
|
```
|
@@ -141,8 +169,11 @@ end
|
|
141
169
|
|
142
170
|
## Development
|
143
171
|
|
144
|
-
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to
|
172
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to
|
173
|
+
run the tests. You can also run `bin/console` for an interactive prompt that will allow you to
|
174
|
+
experiment.
|
145
175
|
|
146
176
|
## License
|
147
177
|
|
148
|
-
The gem is available as open source under the terms of the
|
178
|
+
The gem is available as open source under the terms of the
|
179
|
+
[MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
CHANGED
data/bin/console
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
2
3
|
|
3
|
-
require
|
4
|
-
require
|
4
|
+
require 'bundler/setup'
|
5
|
+
require 'graphql/decorate'
|
5
6
|
|
6
7
|
# You can add fixtures and/or initialization code here to make experimenting
|
7
8
|
# with your gem easier. You can also use a different console, if you like.
|
@@ -10,5 +11,5 @@ require "graphql/decorate"
|
|
10
11
|
# require "pry"
|
11
12
|
# Pry.start
|
12
13
|
|
13
|
-
require
|
14
|
+
require 'irb'
|
14
15
|
IRB.start(__FILE__)
|
data/graphql-decorate.gemspec
CHANGED
@@ -1,30 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
|
2
|
-
lib = File.expand_path(
|
3
|
+
lib = File.expand_path('lib', __dir__)
|
3
4
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
-
require
|
5
|
+
require 'graphql/decorate/version'
|
5
6
|
|
7
|
+
# rubocop:disable Metrics/BlockLength
|
6
8
|
Gem::Specification.new do |spec|
|
7
|
-
spec.name
|
8
|
-
spec.version
|
9
|
-
spec.authors
|
10
|
-
spec.email
|
9
|
+
spec.name = 'graphql-decorate'
|
10
|
+
spec.version = GraphQL::Decorate::VERSION
|
11
|
+
spec.authors = ['Ben Brook']
|
12
|
+
spec.email = ['bbrook154@gmail.com']
|
11
13
|
|
12
|
-
spec.summary
|
13
|
-
spec.homepage
|
14
|
-
spec.license
|
14
|
+
spec.summary = 'A decorator integration for the GraphQL gem'
|
15
|
+
spec.homepage = 'https://www.github.com/TrueCar/graphql-decorate'
|
16
|
+
spec.license = 'MIT'
|
17
|
+
spec.metadata = {
|
18
|
+
'rubygems_mfa_required' => 'true'
|
19
|
+
}
|
15
20
|
|
16
21
|
# Specify which files should be added to the gem when it is released.
|
17
|
-
# The `git ls-files -z` loads the files in the RubyGem that have been added
|
18
|
-
|
19
|
-
|
22
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added
|
23
|
+
# into git.
|
24
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
25
|
+
`git ls-files -z`.split("\x0").reject do |f|
|
26
|
+
f.match(%r{^(test|spec|features)/})
|
27
|
+
end
|
20
28
|
end
|
21
|
-
spec.bindir =
|
29
|
+
spec.bindir = 'exe'
|
22
30
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
23
|
-
spec.require_paths = [
|
31
|
+
spec.require_paths = ['lib']
|
24
32
|
|
25
|
-
spec.
|
33
|
+
spec.required_ruby_version = '>= 2.6.0'
|
26
34
|
|
27
|
-
spec.
|
28
|
-
|
29
|
-
spec.add_development_dependency
|
35
|
+
spec.add_runtime_dependency 'graphql', '>= 1.3', '< 2'
|
36
|
+
|
37
|
+
spec.add_development_dependency 'bundler', '>= 2'
|
38
|
+
spec.add_development_dependency 'rake', '>= 12.3.3'
|
39
|
+
spec.add_development_dependency 'rspec', '~> 3.0'
|
40
|
+
spec.add_development_dependency 'rubocop', ' >= 1.11.0 '
|
41
|
+
spec.add_development_dependency 'rubocop-rspec', '2.2.0'
|
42
|
+
spec.add_development_dependency 'simplecov', '~> 0.21.2'
|
43
|
+
spec.add_development_dependency 'yard', '~> 0.9.26'
|
30
44
|
end
|
45
|
+
# rubocop:enable Metrics/BlockLength
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module GraphQL
|
2
4
|
module Decorate
|
3
5
|
# Allows overriding default decoration and custom collection class behavior.
|
@@ -9,8 +11,8 @@ module GraphQL
|
|
9
11
|
attr_accessor :custom_collection_classes
|
10
12
|
|
11
13
|
def initialize
|
12
|
-
@evaluate_decorator = lambda do |decorator_class, object,
|
13
|
-
decorator_class.decorate(object, context:
|
14
|
+
@evaluate_decorator = lambda do |decorator_class, object, metadata|
|
15
|
+
decorator_class.decorate(object, context: metadata)
|
14
16
|
end
|
15
17
|
@custom_collection_classes = []
|
16
18
|
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GraphQL
|
4
|
+
module Decorate
|
5
|
+
# Handles decorating an value at runtime given its current field.
|
6
|
+
class Decoration
|
7
|
+
# Resolve the undecorated_field.value with decoration.
|
8
|
+
# @param undecorated_field [GraphQL::Decorate::UndecoratedField]
|
9
|
+
# @return [Object] Decorated undecorated_field.value if possible, otherwise the original undecorated_field.value.
|
10
|
+
def self.decorate(undecorated_field)
|
11
|
+
new(undecorated_field).decorate
|
12
|
+
end
|
13
|
+
|
14
|
+
# @param undecorated_field [GraphQL::Decorate::UndecoratedField]
|
15
|
+
def initialize(undecorated_field)
|
16
|
+
@undecorated_field = undecorated_field
|
17
|
+
end
|
18
|
+
|
19
|
+
# @return [Object] Decorated undecorated_field.value if possible, otherwise the original undecorated_field.value.
|
20
|
+
def decorate
|
21
|
+
if undecorated_field.decorator_class
|
22
|
+
GraphQL::Decorate.configuration.evaluate_decorator.call(undecorated_field.decorator_class,
|
23
|
+
undecorated_field.value, undecorated_field.metadata)
|
24
|
+
else
|
25
|
+
undecorated_field.value
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
attr_reader :undecorated_field
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Allows extraction of a type class from a particular field.
|
4
|
+
module ExtractType
|
5
|
+
private
|
6
|
+
|
7
|
+
def extract_type(field)
|
8
|
+
if field.respond_to?(:of_type)
|
9
|
+
extract_type(field.of_type)
|
10
|
+
else
|
11
|
+
field
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -1,36 +1,50 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module GraphQL
|
3
4
|
module Decorate
|
4
5
|
# Extension run after fields are resolved to decorate their value.
|
5
6
|
class FieldExtension < GraphQL::Schema::FieldExtension
|
7
|
+
include ExtractType
|
8
|
+
|
6
9
|
# Extension to be called after lazy loading.
|
7
10
|
# @param context [GraphQL::Query::Context] The current GraphQL query context.
|
8
|
-
# @param value [Object, GraphQL::Schema::Object] The object being decorated. Can
|
9
|
-
#
|
11
|
+
# @param value [Object, Array, GraphQL::Schema::Object] The object being decorated. Can
|
12
|
+
# be a schema object if the field hasn't been resolved yet or a connection.
|
13
|
+
# @return [Object] Decorated object.
|
10
14
|
def after_resolve(context:, value:, **_rest)
|
11
15
|
return if value.nil?
|
12
16
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
17
|
+
resolve_decorated_value(value, context)
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def resolve_decorated_value(value, context)
|
23
|
+
type = extract_type(context.to_h[:current_field].type)
|
24
|
+
|
25
|
+
if collection?(value)
|
26
|
+
value.each_with_index.map do |item, index|
|
27
|
+
decorate(item, type, context, index)
|
28
|
+
end
|
18
29
|
else
|
19
|
-
decorate(value,
|
30
|
+
decorate(value, type, context)
|
20
31
|
end
|
21
32
|
end
|
22
33
|
|
23
|
-
|
34
|
+
def decorate(value, type, context, index = nil)
|
35
|
+
undecorated_field = GraphQL::Decorate::UndecoratedField.new(value, type, context, index)
|
36
|
+
GraphQL::Decorate::Decoration.decorate(undecorated_field)
|
37
|
+
end
|
38
|
+
|
39
|
+
def collection?(value)
|
40
|
+
collection_classes.any? { |c| value.is_a?(c) }
|
41
|
+
end
|
24
42
|
|
25
43
|
def collection_classes
|
26
44
|
klasses = [Array] + GraphQL::Decorate.configuration.custom_collection_classes
|
27
45
|
klasses << ::ActiveRecord::Relation if defined?(ActiveRecord::Relation)
|
28
46
|
klasses
|
29
47
|
end
|
30
|
-
|
31
|
-
def decorate(object, field_context)
|
32
|
-
GraphQL::Decorate::Object.new(object, field_context).decorate
|
33
|
-
end
|
34
48
|
end
|
35
49
|
end
|
36
50
|
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GraphQL
|
4
|
+
module Decorate
|
5
|
+
# Contains methods to evaluate different types of metadata
|
6
|
+
class Metadata
|
7
|
+
# @return [Proc]
|
8
|
+
attr_reader :unscoped_proc
|
9
|
+
|
10
|
+
# @return [Proc]
|
11
|
+
attr_reader :scoped_proc
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
@unscoped_proc = nil
|
15
|
+
@scoped_proc = nil
|
16
|
+
end
|
17
|
+
|
18
|
+
# @yield [object, graphql_context] Evaluate metadata for a single resolved field
|
19
|
+
def unscoped(&block)
|
20
|
+
@unscoped_proc = block
|
21
|
+
end
|
22
|
+
|
23
|
+
# @yield [object, graphql_context] Evaluate metadata for a resolved field and all child fields
|
24
|
+
def scoped(&block)
|
25
|
+
@scoped_proc = block
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -1,41 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module GraphQL
|
2
4
|
module Decorate
|
3
5
|
# Extends GraphQL::Schema::Object classes with methods to set the desired decorator class and context.
|
4
6
|
module ObjectIntegration
|
7
|
+
# @param base [Class] Base class the module is being included in
|
8
|
+
# @return [nil]
|
9
|
+
def self.included(base)
|
10
|
+
base.extend(self)
|
11
|
+
end
|
12
|
+
|
5
13
|
# Decorate the type with a decorator class.
|
6
14
|
# @param klass [Class] Class the object should be decorated with.
|
7
|
-
def decorate_with(klass)
|
15
|
+
def decorate_with(klass = nil, &block)
|
8
16
|
@decorator_class = klass
|
9
|
-
end
|
10
|
-
|
11
|
-
# Dynamically choose the decorator class based on the underlying object.
|
12
|
-
# @yield [object] Gives the underlying object to the block.
|
13
|
-
# @return [Proc] Proc to evaluate decorator class. Proc should return a decorator class.
|
14
|
-
def decorate_when(&block)
|
15
17
|
@decorator_evaluator = block
|
16
18
|
end
|
17
19
|
|
18
20
|
# Pass additional data to the decorator context (if supported).
|
19
21
|
# @yield [object] Gives the underlying object to the block.
|
20
22
|
# @return [Proc] Proc to evaluate decorator context. Proc should return Hash.
|
21
|
-
def
|
22
|
-
@
|
23
|
+
def decorate_metadata
|
24
|
+
@decorator_metadata ||= GraphQL::Decorate::Metadata.new
|
25
|
+
yield(@decorator_metadata)
|
23
26
|
end
|
24
27
|
|
25
28
|
# @return [Class, nil] Gets the currently set decorator class.
|
26
|
-
|
27
|
-
@decorator_class
|
28
|
-
end
|
29
|
+
attr_reader :decorator_class
|
29
30
|
|
30
31
|
# @return [Proc, nil] Gets the currently set decorator evaluator.
|
31
|
-
|
32
|
-
@decorator_evaluator
|
33
|
-
end
|
32
|
+
attr_reader :decorator_evaluator
|
34
33
|
|
35
|
-
|
36
|
-
def decorator_context_evaluator
|
37
|
-
@decorator_context_evaluator
|
38
|
-
end
|
34
|
+
attr_reader :decorator_metadata
|
39
35
|
end
|
40
36
|
end
|
41
37
|
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module GraphQL
|
3
4
|
module Decorate
|
4
5
|
# Extracts configured decorator attributes from a GraphQL::Schema::Object type.
|
@@ -11,6 +12,11 @@ module GraphQL
|
|
11
12
|
@type = type
|
12
13
|
end
|
13
14
|
|
15
|
+
# @return [Boolean] True if the type can be decorated, false otherwise
|
16
|
+
def decoratable?
|
17
|
+
!!(decorator_class || decorator_evaluator || unresolved_type?)
|
18
|
+
end
|
19
|
+
|
14
20
|
# @return [Class, nil] Decorator class for the type if available
|
15
21
|
def decorator_class
|
16
22
|
get_attribute(:decorator_class)
|
@@ -21,9 +27,9 @@ module GraphQL
|
|
21
27
|
get_attribute(:decorator_evaluator)
|
22
28
|
end
|
23
29
|
|
24
|
-
# @return [Proc, nil] Decorator
|
25
|
-
def
|
26
|
-
get_attribute(:
|
30
|
+
# @return [Proc, nil] Decorator metadata evaluator for the type if available
|
31
|
+
def decorator_metadata
|
32
|
+
get_attribute(:decorator_metadata)
|
27
33
|
end
|
28
34
|
|
29
35
|
# @return [GraphQL::Schema::Object, nil] Decorator evaluator for the type if available
|
@@ -33,7 +39,7 @@ module GraphQL
|
|
33
39
|
|
34
40
|
# @return [Boolean] True if type is not yet resolved, false if it is resolved
|
35
41
|
def unresolved_type?
|
36
|
-
type.respond_to?(:
|
42
|
+
type.respond_to?(:kind) && [GraphQL::TypeKinds::INTERFACE, GraphQL::TypeKinds::UNION].include?(type.kind)
|
37
43
|
end
|
38
44
|
|
39
45
|
# @return [Boolean] True if type is resolved, false if it is not resolved
|
@@ -41,19 +47,10 @@ module GraphQL
|
|
41
47
|
!unresolved_type?
|
42
48
|
end
|
43
49
|
|
44
|
-
# @return [Boolean] True if type is a connection, false if it is resolved
|
45
|
-
def connection?
|
46
|
-
resolved_type? && type.respond_to?(:node_type)
|
47
|
-
end
|
48
|
-
|
49
50
|
private
|
50
51
|
|
51
52
|
def get_attribute(name)
|
52
|
-
|
53
|
-
type.node_type.respond_to?(name) && type.node_type.public_send(name)
|
54
|
-
elsif resolved_type?
|
55
|
-
type.respond_to?(name) ? type.public_send(name) : nil
|
56
|
-
end
|
53
|
+
type.respond_to?(name) ? type.public_send(name) : nil
|
57
54
|
end
|
58
55
|
end
|
59
56
|
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GraphQL
|
4
|
+
module Decorate
|
5
|
+
# Wraps current value, parents, and graphql_context and extracts relevant decoration data to resolve the field.
|
6
|
+
class UndecoratedField
|
7
|
+
# @return [Object] Value to be decorated
|
8
|
+
attr_reader :value
|
9
|
+
|
10
|
+
# @param value [Object] Value to be decorated
|
11
|
+
# @param type [GraphQL::Schema::Object] Type class of value to be decorated
|
12
|
+
# @param graphql_context [GraphQL::Query::Context] Current query graphql_context
|
13
|
+
def initialize(value, type, graphql_context, index = nil)
|
14
|
+
@value = value
|
15
|
+
@type = type
|
16
|
+
@type_attributes = GraphQL::Decorate::TypeAttributes.new(type)
|
17
|
+
@graphql_context = graphql_context
|
18
|
+
@default_metadata = { graphql: true }
|
19
|
+
@path = graphql_context[:current_path].dup
|
20
|
+
@path << index if index
|
21
|
+
end
|
22
|
+
|
23
|
+
# @return [Class] Decorator class for the current field
|
24
|
+
def decorator_class
|
25
|
+
resolved_class = type_attributes.decorator_class || resolve_decorator_class
|
26
|
+
return resolved_class if resolved_class
|
27
|
+
|
28
|
+
class_evaluator = type_attributes.decorator_evaluator || resolve_decorator_evaluator
|
29
|
+
class_evaluator&.call(value, graphql_context)
|
30
|
+
end
|
31
|
+
|
32
|
+
# @return [Hash] Metadata to be provided to a decorator for the current field
|
33
|
+
def metadata
|
34
|
+
default_metadata.merge(unscoped_metadata, scoped_metadata)
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
attr_reader :type, :type_attributes, :graphql_context, :default_metadata, :path
|
40
|
+
|
41
|
+
def unscoped_metadata
|
42
|
+
unscoped_metadata_proc&.call(value, graphql_context) || {}
|
43
|
+
end
|
44
|
+
|
45
|
+
def scoped_metadata
|
46
|
+
insert_scoped_metadata(new_scoped_metadata)
|
47
|
+
end
|
48
|
+
|
49
|
+
def new_scoped_metadata
|
50
|
+
scoped_metadata_proc&.call(value, graphql_context) || {}
|
51
|
+
end
|
52
|
+
|
53
|
+
# rubocop:disable Metrics/AbcSize
|
54
|
+
def insert_scoped_metadata(metadata)
|
55
|
+
# Save metadata at each level in the path of the current execution.
|
56
|
+
# If a field's direct parent does not have metadata then it will
|
57
|
+
# use the next highest metadata in the tree that matches its path.
|
58
|
+
scoped_metadata = graphql_context[:scoped_decorator_metadata] ||= {}
|
59
|
+
prev_value = {}
|
60
|
+
|
61
|
+
path[0...-1].each do |step|
|
62
|
+
# Write the parent's metadata to the child if it doesn't already exist
|
63
|
+
scoped_metadata[step] = { value: prev_value, children: {} } unless scoped_metadata[step]
|
64
|
+
# Update the next parent's metadata to include anything at the current level
|
65
|
+
prev_value = prev_value.merge(scoped_metadata[step][:value])
|
66
|
+
# Move to the child fields and repeat
|
67
|
+
scoped_metadata = scoped_metadata[step][:children]
|
68
|
+
end
|
69
|
+
|
70
|
+
# The last step in the path is the current field, merge in new metadata from
|
71
|
+
# the field itself and return it.
|
72
|
+
merged_metadata = { value: prev_value.merge(metadata), children: {} }
|
73
|
+
scoped_metadata[path[-1]] = merged_metadata
|
74
|
+
merged_metadata[:value]
|
75
|
+
end
|
76
|
+
# rubocop:enable Metrics/AbcSize
|
77
|
+
|
78
|
+
def unscoped_metadata_proc
|
79
|
+
type_attributes.decorator_metadata&.unscoped_proc || resolve_unscoped_proc
|
80
|
+
end
|
81
|
+
|
82
|
+
def scoped_metadata_proc
|
83
|
+
type_attributes.decorator_metadata&.scoped_proc || resolve_scoped_proc
|
84
|
+
end
|
85
|
+
|
86
|
+
def resolve_decorator_class
|
87
|
+
resolved_type_attributes&.decorator_class
|
88
|
+
end
|
89
|
+
|
90
|
+
def resolve_decorator_evaluator
|
91
|
+
resolved_type_attributes&.decorator_evaluator
|
92
|
+
end
|
93
|
+
|
94
|
+
def resolve_unscoped_proc
|
95
|
+
resolved_type_attributes&.decorator_metadata&.unscoped_proc
|
96
|
+
end
|
97
|
+
|
98
|
+
def resolve_scoped_proc
|
99
|
+
resolved_type_attributes&.decorator_metadata&.scoped_proc
|
100
|
+
end
|
101
|
+
|
102
|
+
def resolved_type_attributes
|
103
|
+
@resolved_type_attributes ||= if type_attributes.unresolved_type?
|
104
|
+
if type.respond_to?(:resolve_type)
|
105
|
+
GraphQL::Decorate::TypeAttributes.new(type.resolve_type(value,
|
106
|
+
graphql_context))
|
107
|
+
else
|
108
|
+
graphql_context.schema.resolve_type(type, value, graphql_context)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
data/lib/graphql/decorate.rb
CHANGED
@@ -1,18 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'graphql'
|
2
4
|
require_relative 'decorate/version'
|
3
5
|
require_relative 'decorate/configuration'
|
6
|
+
require_relative 'decorate/extract_type'
|
4
7
|
require_relative 'decorate/object_integration'
|
5
|
-
require_relative 'decorate/field_integration'
|
6
8
|
require_relative 'decorate/field_extension'
|
7
|
-
require_relative 'decorate/
|
9
|
+
require_relative 'decorate/decoration'
|
8
10
|
require_relative 'decorate/type_attributes'
|
9
|
-
require_relative 'decorate/
|
10
|
-
require_relative 'decorate/
|
11
|
+
require_relative 'decorate/undecorated_field'
|
12
|
+
require_relative 'decorate/metadata'
|
11
13
|
|
12
14
|
# Matching the graphql-ruby namespace
|
13
15
|
module GraphQL
|
14
16
|
# Entry point for graphql-decorate. Handles configuration.
|
15
17
|
module Decorate
|
18
|
+
extend ExtractType
|
19
|
+
|
16
20
|
# @return [Configuration] Returns a new instance of GraphQL::Decorate::Configuration.
|
17
21
|
def self.configuration
|
18
22
|
@configuration ||= Configuration.new
|
@@ -27,5 +31,19 @@ module GraphQL
|
|
27
31
|
def self.reset_configuration!
|
28
32
|
@configuration = Configuration.new
|
29
33
|
end
|
34
|
+
|
35
|
+
# @param schema_defn [GraphQL::Schema] Current schema class
|
36
|
+
# @return [nil]
|
37
|
+
def self.use(schema_defn)
|
38
|
+
schema_defn.types.each do |_name, type|
|
39
|
+
next unless type.respond_to?(:fields)
|
40
|
+
|
41
|
+
type.fields.each do |_name, field|
|
42
|
+
field_type = extract_type(field.type_class.type)
|
43
|
+
type_attributes = GraphQL::Decorate::TypeAttributes.new(field_type)
|
44
|
+
field.type_class.extension(GraphQL::Decorate::FieldExtension) if type_attributes.decoratable?
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
30
48
|
end
|
31
49
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: graphql-decorate
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2
|
4
|
+
version: 1.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ben Brook
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-04-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: graphql
|
@@ -48,16 +48,16 @@ dependencies:
|
|
48
48
|
name: rake
|
49
49
|
requirement: !ruby/object:Gem::Requirement
|
50
50
|
requirements:
|
51
|
-
- - "
|
51
|
+
- - ">="
|
52
52
|
- !ruby/object:Gem::Version
|
53
|
-
version:
|
53
|
+
version: 12.3.3
|
54
54
|
type: :development
|
55
55
|
prerelease: false
|
56
56
|
version_requirements: !ruby/object:Gem::Requirement
|
57
57
|
requirements:
|
58
|
-
- - "
|
58
|
+
- - ">="
|
59
59
|
- !ruby/object:Gem::Version
|
60
|
-
version:
|
60
|
+
version: 12.3.3
|
61
61
|
- !ruby/object:Gem::Dependency
|
62
62
|
name: rspec
|
63
63
|
requirement: !ruby/object:Gem::Requirement
|
@@ -72,6 +72,62 @@ dependencies:
|
|
72
72
|
- - "~>"
|
73
73
|
- !ruby/object:Gem::Version
|
74
74
|
version: '3.0'
|
75
|
+
- !ruby/object:Gem::Dependency
|
76
|
+
name: rubocop
|
77
|
+
requirement: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - ">="
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: 1.11.0
|
82
|
+
type: :development
|
83
|
+
prerelease: false
|
84
|
+
version_requirements: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: 1.11.0
|
89
|
+
- !ruby/object:Gem::Dependency
|
90
|
+
name: rubocop-rspec
|
91
|
+
requirement: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - '='
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: 2.2.0
|
96
|
+
type: :development
|
97
|
+
prerelease: false
|
98
|
+
version_requirements: !ruby/object:Gem::Requirement
|
99
|
+
requirements:
|
100
|
+
- - '='
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: 2.2.0
|
103
|
+
- !ruby/object:Gem::Dependency
|
104
|
+
name: simplecov
|
105
|
+
requirement: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - "~>"
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: 0.21.2
|
110
|
+
type: :development
|
111
|
+
prerelease: false
|
112
|
+
version_requirements: !ruby/object:Gem::Requirement
|
113
|
+
requirements:
|
114
|
+
- - "~>"
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: 0.21.2
|
117
|
+
- !ruby/object:Gem::Dependency
|
118
|
+
name: yard
|
119
|
+
requirement: !ruby/object:Gem::Requirement
|
120
|
+
requirements:
|
121
|
+
- - "~>"
|
122
|
+
- !ruby/object:Gem::Version
|
123
|
+
version: 0.9.26
|
124
|
+
type: :development
|
125
|
+
prerelease: false
|
126
|
+
version_requirements: !ruby/object:Gem::Requirement
|
127
|
+
requirements:
|
128
|
+
- - "~>"
|
129
|
+
- !ruby/object:Gem::Version
|
130
|
+
version: 0.9.26
|
75
131
|
description:
|
76
132
|
email:
|
77
133
|
- bbrook154@gmail.com
|
@@ -79,9 +135,10 @@ executables: []
|
|
79
135
|
extensions: []
|
80
136
|
extra_rdoc_files: []
|
81
137
|
files:
|
138
|
+
- ".github/workflows/ci.yml"
|
82
139
|
- ".github/workflows/gem-push-on-release.yml"
|
83
|
-
- ".github/workflows/rspec.yml"
|
84
140
|
- ".gitignore"
|
141
|
+
- ".rubocop.yml"
|
85
142
|
- Gemfile
|
86
143
|
- LICENSE.txt
|
87
144
|
- README.md
|
@@ -91,18 +148,19 @@ files:
|
|
91
148
|
- graphql-decorate.gemspec
|
92
149
|
- lib/graphql/decorate.rb
|
93
150
|
- lib/graphql/decorate/configuration.rb
|
94
|
-
- lib/graphql/decorate/
|
95
|
-
- lib/graphql/decorate/
|
151
|
+
- lib/graphql/decorate/decoration.rb
|
152
|
+
- lib/graphql/decorate/extract_type.rb
|
96
153
|
- lib/graphql/decorate/field_extension.rb
|
97
|
-
- lib/graphql/decorate/
|
98
|
-
- lib/graphql/decorate/object.rb
|
154
|
+
- lib/graphql/decorate/metadata.rb
|
99
155
|
- lib/graphql/decorate/object_integration.rb
|
100
156
|
- lib/graphql/decorate/type_attributes.rb
|
157
|
+
- lib/graphql/decorate/undecorated_field.rb
|
101
158
|
- lib/graphql/decorate/version.rb
|
102
159
|
homepage: https://www.github.com/TrueCar/graphql-decorate
|
103
160
|
licenses:
|
104
161
|
- MIT
|
105
|
-
metadata:
|
162
|
+
metadata:
|
163
|
+
rubygems_mfa_required: 'true'
|
106
164
|
post_install_message:
|
107
165
|
rdoc_options: []
|
108
166
|
require_paths:
|
@@ -111,14 +169,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
111
169
|
requirements:
|
112
170
|
- - ">="
|
113
171
|
- !ruby/object:Gem::Version
|
114
|
-
version:
|
172
|
+
version: 2.6.0
|
115
173
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
116
174
|
requirements:
|
117
175
|
- - ">="
|
118
176
|
- !ruby/object:Gem::Version
|
119
177
|
version: '0'
|
120
178
|
requirements: []
|
121
|
-
rubygems_version: 3.0.3
|
179
|
+
rubygems_version: 3.0.3.1
|
122
180
|
signing_key:
|
123
181
|
specification_version: 4
|
124
182
|
summary: A decorator integration for the GraphQL gem
|
data/.github/workflows/rspec.yml
DELETED
@@ -1,32 +0,0 @@
|
|
1
|
-
# This workflow uses actions that are not certified by GitHub.
|
2
|
-
# They are provided by a third-party and are governed by
|
3
|
-
# separate terms of service, privacy policy, and support
|
4
|
-
# documentation.
|
5
|
-
# This workflow will download a prebuilt Ruby version, install dependencies and run tests with Rake
|
6
|
-
# For more information see: https://github.com/marketplace/actions/setup-ruby-jruby-and-truffleruby
|
7
|
-
|
8
|
-
name: Ruby
|
9
|
-
|
10
|
-
on:
|
11
|
-
push:
|
12
|
-
branches: [ master ]
|
13
|
-
pull_request:
|
14
|
-
branches: [ master ]
|
15
|
-
|
16
|
-
jobs:
|
17
|
-
test:
|
18
|
-
|
19
|
-
runs-on: ubuntu-latest
|
20
|
-
strategy:
|
21
|
-
matrix:
|
22
|
-
ruby-version: ['2.6', '2.7', '3.0']
|
23
|
-
|
24
|
-
steps:
|
25
|
-
- uses: actions/checkout@v2
|
26
|
-
- name: Set up Ruby
|
27
|
-
uses: ruby/setup-ruby@v1
|
28
|
-
with:
|
29
|
-
ruby-version: ${{ matrix.ruby-version }}
|
30
|
-
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
|
31
|
-
- name: Run tests
|
32
|
-
run: bundle exec rake
|
@@ -1,52 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
module GraphQL
|
3
|
-
module Decorate
|
4
|
-
# Wraps a GraphQL::Pagination::Connection object to decorate values after pagination is applied.
|
5
|
-
class Connection
|
6
|
-
# @return [GraphQL::Pagination::Connection] Connection being decorated
|
7
|
-
attr_reader :connection
|
8
|
-
|
9
|
-
# @return [GraphQL::Decorate::FieldContext] Current field context
|
10
|
-
attr_reader :field_context
|
11
|
-
|
12
|
-
def initialize(connection, field_context)
|
13
|
-
@connection = connection
|
14
|
-
@field_context = field_context
|
15
|
-
end
|
16
|
-
|
17
|
-
# @return [Array] Decorated nodes after pagination is applied
|
18
|
-
def nodes
|
19
|
-
nodes = @connection.nodes
|
20
|
-
nodes.map { |node| GraphQL::Decorate::Object.new(node, field_context).decorate }
|
21
|
-
end
|
22
|
-
|
23
|
-
# @see nodes
|
24
|
-
# @return [Array] Decorated nodes after pagination is applied
|
25
|
-
def edge_nodes
|
26
|
-
nodes
|
27
|
-
end
|
28
|
-
|
29
|
-
class << self
|
30
|
-
private
|
31
|
-
|
32
|
-
def method_missing(symbol, *args, &block)
|
33
|
-
@connection.class.send(symbol, *args, &block)
|
34
|
-
end
|
35
|
-
|
36
|
-
def respond_to_missing?(method, include_private = false)
|
37
|
-
@connection.class.respond_to_missing(method, include_private)
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
private
|
42
|
-
|
43
|
-
def method_missing(symbol, *args, &block)
|
44
|
-
@connection.send(symbol, *args, &block)
|
45
|
-
end
|
46
|
-
|
47
|
-
def respond_to_missing?(method, include_private = false)
|
48
|
-
@connection.respond_to_missing(method, include_private)
|
49
|
-
end
|
50
|
-
end
|
51
|
-
end
|
52
|
-
end
|
@@ -1,18 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
module GraphQL
|
3
|
-
module Decorate
|
4
|
-
# Wraps current GraphQL::Query::Context and options provided to a field for portability.
|
5
|
-
class FieldContext
|
6
|
-
# @return [GraphQL::Query::Context] Current GraphQL query context
|
7
|
-
attr_reader :context
|
8
|
-
|
9
|
-
# @return [Hash] Options provided to the field being decorated
|
10
|
-
attr_reader :options
|
11
|
-
|
12
|
-
def initialize(context, options)
|
13
|
-
@context = context
|
14
|
-
@options = options
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
@@ -1,38 +0,0 @@
|
|
1
|
-
module GraphQL
|
2
|
-
module Decorate
|
3
|
-
# Extends default field behavior and adds extension to the field if it should be decorated.
|
4
|
-
module FieldIntegration
|
5
|
-
# Overridden field initializer
|
6
|
-
# @param type [GraphQL::Schema::Object] The type to add the extension to.
|
7
|
-
# @return [Void]
|
8
|
-
def initialize(type:, **rest, &block)
|
9
|
-
super
|
10
|
-
field_type = [type].flatten(1).first
|
11
|
-
extension_options = get_extension_options(field_type)
|
12
|
-
extend_with_decorator(extension_options) if extension_options
|
13
|
-
end
|
14
|
-
|
15
|
-
private
|
16
|
-
|
17
|
-
def get_extension_options(type)
|
18
|
-
type_attributes = GraphQL::Decorate::TypeAttributes.new(type)
|
19
|
-
return unless type_attributes.decorator_class
|
20
|
-
|
21
|
-
{
|
22
|
-
decorator_class: type_attributes.decorator_class,
|
23
|
-
decorator_evaluator: type_attributes.decorator_evaluator,
|
24
|
-
decorator_context_evaluator: type_attributes.decorator_context_evaluator,
|
25
|
-
unresolved_type: type_attributes.unresolved_type
|
26
|
-
}
|
27
|
-
end
|
28
|
-
|
29
|
-
def extend_with_decorator(options)
|
30
|
-
extension(GraphQL::Decorate::FieldExtension, options)
|
31
|
-
# ext = GraphQL::Decorate::FieldExtension.new(field: self, options: options)
|
32
|
-
# @extensions = @extensions.dup
|
33
|
-
# @extensions.unshift(ext)
|
34
|
-
# @extensions.freeze
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
@@ -1,71 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
module GraphQL
|
3
|
-
module Decorate
|
4
|
-
# Handles decorating an object given its current field context.
|
5
|
-
class Object
|
6
|
-
# @param object [Object] Object being decorated.
|
7
|
-
# @param field_context [GraphQL::Decorate::FieldContext] Current GraphQL field context and options.
|
8
|
-
def initialize(object, field_context)
|
9
|
-
@object = object
|
10
|
-
@field_context = field_context
|
11
|
-
@default_decorator_context = { graphql: true }
|
12
|
-
end
|
13
|
-
|
14
|
-
# Resolve the object with decoration.
|
15
|
-
# @return [Object] Decorated object if possible, otherwise the original object.
|
16
|
-
def decorate
|
17
|
-
if decorator_class
|
18
|
-
GraphQL::Decorate.configuration.evaluate_decorator.call(decorator_class, object, decorator_context)
|
19
|
-
else
|
20
|
-
object
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
private
|
25
|
-
|
26
|
-
attr_reader :object, :field_context, :default_decorator_context
|
27
|
-
|
28
|
-
def decorator_class
|
29
|
-
if field_context.options[:decorator_class]
|
30
|
-
field_context.options[:decorator_class]
|
31
|
-
elsif field_context.options[:decorator_evaluator]
|
32
|
-
field_context.options[:decorator_evaluator].call(object)
|
33
|
-
else
|
34
|
-
resolve_decorator_class
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
def decorator_context_evaluator
|
39
|
-
field_context.options[:decorator_context_evaluator] || resolve_decorator_context_evaluator
|
40
|
-
end
|
41
|
-
|
42
|
-
private
|
43
|
-
|
44
|
-
def evaluate_decoration_context
|
45
|
-
decorator_context_evaluator ? decorator_context_evaluator.call(object) : {}
|
46
|
-
end
|
47
|
-
|
48
|
-
def decorator_context
|
49
|
-
evaluate_decoration_context.merge(default_decorator_context)
|
50
|
-
end
|
51
|
-
|
52
|
-
def resolve_decorator_class
|
53
|
-
type = resolve_type
|
54
|
-
if type.respond_to?(:decorator_class) && type.decorator_class
|
55
|
-
type.decorator_class
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
def resolve_decorator_context_evaluator
|
60
|
-
type = resolve_type
|
61
|
-
if type.respond_to?(:decorator_context_evaluator) && type.decorator_context_evaluator
|
62
|
-
type.decorator_context_evaluator
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
def resolve_type
|
67
|
-
field_context.options[:unresolved_type]&.resolve_type(object, field_context.context)
|
68
|
-
end
|
69
|
-
end
|
70
|
-
end
|
71
|
-
end
|