hq-graphql 2.1.0 → 2.1.1
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/lib/hq/graphql.rb +0 -1
- data/lib/hq/graphql/enum.rb +2 -3
- data/lib/hq/graphql/enum/sort_by.rb +2 -0
- data/lib/hq/graphql/enum/sort_order.rb +2 -0
- data/lib/hq/graphql/field.rb +10 -29
- data/lib/hq/graphql/field_extension/association_loader_extension.rb +15 -0
- data/lib/hq/graphql/field_extension/paginated_arguments.rb +22 -0
- data/lib/hq/graphql/field_extension/paginated_loader.rb +45 -0
- data/lib/hq/graphql/input_object.rb +4 -0
- data/lib/hq/graphql/object.rb +33 -13
- data/lib/hq/graphql/object_association.rb +50 -0
- data/lib/hq/graphql/paginated_association_loader.rb +28 -8
- data/lib/hq/graphql/resource.rb +24 -24
- data/lib/hq/graphql/resource/auto_mutation.rb +4 -0
- data/lib/hq/graphql/types.rb +3 -0
- data/lib/hq/graphql/version.rb +1 -1
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5aefcd1b96fb05c456ebdef007f9bcad196e7aba2cd864d6dd7c83f3b3fbc95c
|
4
|
+
data.tar.gz: 0173c91a781e4872cbf65c597f3266030850c8e0ba31b05d8f9b578f11263216
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8a7b74ef1631a0d98004beb6bec61ea6898994c3ff25366afbc1fa5ba47bc60e36061ea3875c655ecfc9ac088b5f809b28fb411c05bfaeeda4e7256742345d5e
|
7
|
+
data.tar.gz: 3f6ecdc50f8a59d0e4c5c204fe4ed5967064e82f0f4a9aa961277532d6a49be5fb534204fa03a928777133e1b9541ec86fd2f9b196c383ff9da2ce1c94736cdc
|
data/lib/hq/graphql.rb
CHANGED
data/lib/hq/graphql/enum.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "hq/graphql/types"
|
4
|
+
|
3
5
|
module HQ::GraphQL
|
4
6
|
class Enum < ::GraphQL::Schema::Enum
|
5
7
|
## Auto generate enums from the database using ActiveRecord
|
@@ -73,6 +75,3 @@ module HQ::GraphQL
|
|
73
75
|
end
|
74
76
|
end
|
75
77
|
end
|
76
|
-
|
77
|
-
require "hq/graphql/enum/sort_by"
|
78
|
-
require "hq/graphql/enum/sort_order"
|
data/lib/hq/graphql/field.rb
CHANGED
@@ -9,7 +9,15 @@ module HQ
|
|
9
9
|
super(*args, **options, &block)
|
10
10
|
@authorize_action = authorize_action
|
11
11
|
@authorize = authorize
|
12
|
-
@
|
12
|
+
@klass_or_string = klass
|
13
|
+
end
|
14
|
+
|
15
|
+
def scope(&block)
|
16
|
+
if block
|
17
|
+
@scope = block
|
18
|
+
else
|
19
|
+
@scope
|
20
|
+
end
|
13
21
|
end
|
14
22
|
|
15
23
|
def authorized?(object, ctx)
|
@@ -18,35 +26,8 @@ module HQ
|
|
18
26
|
::HQ::GraphQL.authorize_field(authorize_action, self, object, ctx)
|
19
27
|
end
|
20
28
|
|
21
|
-
def resolve_field(object, args, ctx)
|
22
|
-
if klass.present? && !!::GraphQL::Batch::Executor.current && object.object
|
23
|
-
loader =
|
24
|
-
if ::HQ::GraphQL.use_experimental_associations?
|
25
|
-
limit = args[:limit]
|
26
|
-
offset = args[:offset]
|
27
|
-
sort_by = args[:sortBy]
|
28
|
-
sort_order = args[:sortOrder]
|
29
|
-
|
30
|
-
PaginatedAssociationLoader.for(
|
31
|
-
klass,
|
32
|
-
original_name,
|
33
|
-
limit: limit,
|
34
|
-
offset: offset,
|
35
|
-
sort_by: sort_by,
|
36
|
-
sort_order: sort_order
|
37
|
-
)
|
38
|
-
else
|
39
|
-
AssociationLoader.for(klass, original_name)
|
40
|
-
end
|
41
|
-
|
42
|
-
loader.load(object.object)
|
43
|
-
else
|
44
|
-
super
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
29
|
def klass
|
49
|
-
@klass ||= @
|
30
|
+
@klass ||= @klass_or_string.is_a?(String) ? @klass_or_string.constantize : @klass_or_string
|
50
31
|
end
|
51
32
|
end
|
52
33
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "hq/graphql/association_loader"
|
4
|
+
|
5
|
+
module HQ
|
6
|
+
module GraphQL
|
7
|
+
module FieldExtension
|
8
|
+
class AssociationLoaderExtension < ::GraphQL::Schema::FieldExtension
|
9
|
+
def resolve(object:, **_kwargs)
|
10
|
+
AssociationLoader.for(options[:klass], field.original_name).load(object.object)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "hq/graphql/enum/sort_by"
|
4
|
+
require "hq/graphql/enum/sort_order"
|
5
|
+
|
6
|
+
module HQ
|
7
|
+
module GraphQL
|
8
|
+
module FieldExtension
|
9
|
+
class PaginatedArguments < ::GraphQL::Schema::FieldExtension
|
10
|
+
def apply
|
11
|
+
field.argument :offset, Integer, required: false
|
12
|
+
field.argument :limit, Integer, required: false
|
13
|
+
field.argument :sort_order, Enum::SortOrder, required: false
|
14
|
+
|
15
|
+
resource = ::HQ::GraphQL.lookup_resource(options[:klass])
|
16
|
+
enum = resource ? resource.sort_fields_enum : ::HQ::GraphQL::Enum::SortBy
|
17
|
+
field.argument :sort_by, enum, required: false
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "hq/graphql/paginated_association_loader"
|
4
|
+
|
5
|
+
module HQ
|
6
|
+
module GraphQL
|
7
|
+
module FieldExtension
|
8
|
+
class PaginatedLoader < ::GraphQL::Schema::FieldExtension
|
9
|
+
def resolve(object:, arguments:, **_options)
|
10
|
+
limit = arguments[:limit]
|
11
|
+
offset = arguments[:offset]
|
12
|
+
sort_by = arguments[:sort_by]
|
13
|
+
sort_order = arguments[:sort_order]
|
14
|
+
scope = field.scope.call(**arguments.except(:limit, :offset, :sort_by, :sort_order)) if field.scope
|
15
|
+
loader = PaginatedAssociationLoader.for(
|
16
|
+
klass,
|
17
|
+
association,
|
18
|
+
internal_association: internal_association,
|
19
|
+
scope: scope,
|
20
|
+
limit: limit,
|
21
|
+
offset: offset,
|
22
|
+
sort_by: sort_by,
|
23
|
+
sort_order: sort_order
|
24
|
+
)
|
25
|
+
|
26
|
+
loader.load(object.object)
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def association
|
32
|
+
options[:association]
|
33
|
+
end
|
34
|
+
|
35
|
+
def internal_association
|
36
|
+
options[:internal_association]
|
37
|
+
end
|
38
|
+
|
39
|
+
def klass
|
40
|
+
options[:klass]
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
data/lib/hq/graphql/object.rb
CHANGED
@@ -1,12 +1,21 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "hq/graphql/active_record_extensions"
|
4
|
+
require "hq/graphql/field"
|
5
|
+
require "hq/graphql/field_extension/association_loader_extension"
|
6
|
+
require "hq/graphql/field_extension/paginated_arguments"
|
7
|
+
require "hq/graphql/field_extension/paginated_loader"
|
8
|
+
require "hq/graphql/object_association"
|
9
|
+
require "hq/graphql/types"
|
10
|
+
|
3
11
|
module HQ
|
4
12
|
module GraphQL
|
5
13
|
class Object < ::GraphQL::Schema::Object
|
6
14
|
include Scalars
|
7
|
-
include
|
15
|
+
include ActiveRecordExtensions
|
16
|
+
extend ObjectAssociation
|
8
17
|
|
9
|
-
field_class
|
18
|
+
field_class Field
|
10
19
|
|
11
20
|
def self.authorize_action(action)
|
12
21
|
self.authorized_action = action
|
@@ -28,8 +37,15 @@ module HQ
|
|
28
37
|
end
|
29
38
|
|
30
39
|
model_associations.each do |association|
|
40
|
+
next if resource_reflections[association.name.to_s]
|
31
41
|
field_from_association(association, auto_nil: auto_nil)
|
32
42
|
end
|
43
|
+
|
44
|
+
resource_reflections.values.each do |resource_reflection|
|
45
|
+
reflection = resource_reflection.reflection(model_klass)
|
46
|
+
next unless reflection
|
47
|
+
field_from_association(reflection, auto_nil: auto_nil, internal_association: true, &resource_reflection.block)
|
48
|
+
end
|
33
49
|
end
|
34
50
|
end
|
35
51
|
|
@@ -46,32 +62,36 @@ module HQ
|
|
46
62
|
@authorized_action ||= :read
|
47
63
|
end
|
48
64
|
|
49
|
-
def field_from_association(association, auto_nil:)
|
65
|
+
def field_from_association(association, auto_nil:, internal_association: false, &block)
|
50
66
|
# The PaginationAssociationLoader doesn't support through associations yet
|
51
67
|
return if association.through_reflection? && ::HQ::GraphQL.use_experimental_associations?
|
52
68
|
|
53
69
|
association_klass = association.klass
|
54
|
-
|
55
|
-
|
70
|
+
name = association.name
|
71
|
+
klass = model_klass
|
72
|
+
type = Types[association_klass]
|
56
73
|
case association.macro
|
57
74
|
when :has_many
|
58
75
|
field name, [type], null: false, klass: model_name do
|
59
|
-
if ::HQ::GraphQL.use_experimental_associations?
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
76
|
+
if ::HQ::GraphQL.use_experimental_associations?
|
77
|
+
extension FieldExtension::PaginatedArguments, klass: association_klass
|
78
|
+
extension FieldExtension::PaginatedLoader, klass: klass, association: name, internal_association: internal_association
|
79
|
+
else
|
80
|
+
extension FieldExtension::AssociationLoaderExtension, klass: klass
|
64
81
|
end
|
82
|
+
instance_eval(&block) if block
|
65
83
|
end
|
66
84
|
else
|
67
|
-
field name, type, null: !auto_nil || !association_required?(association), klass: model_name
|
85
|
+
field name, type, null: !auto_nil || !association_required?(association), klass: model_name do
|
86
|
+
extension FieldExtension::AssociationLoaderExtension, klass: klass
|
87
|
+
end
|
68
88
|
end
|
69
|
-
rescue
|
89
|
+
rescue Types::Error
|
70
90
|
nil
|
71
91
|
end
|
72
92
|
|
73
93
|
def field_from_column(column, auto_nil:)
|
74
|
-
field column.name,
|
94
|
+
field column.name, Types.type_from_column(column), null: !auto_nil || column.null
|
75
95
|
end
|
76
96
|
|
77
97
|
def association_required?(association)
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module HQ
|
4
|
+
module GraphQL
|
5
|
+
module ObjectAssociation
|
6
|
+
class ResourceReflection
|
7
|
+
attr_reader :name, :scope, :options, :macro, :block
|
8
|
+
|
9
|
+
def initialize(name, scope, options, macro, block)
|
10
|
+
@name = name
|
11
|
+
@scope = scope
|
12
|
+
@options = options
|
13
|
+
@macro = macro
|
14
|
+
@block = block
|
15
|
+
end
|
16
|
+
|
17
|
+
def reflection(model_klass)
|
18
|
+
if macro == :has_many
|
19
|
+
::ActiveRecord::Associations::Builder::HasMany.create_reflection(model_klass, name, scope, options)
|
20
|
+
elsif macro == :belongs_to
|
21
|
+
::ActiveRecord::Associations::Builder::BelongsTo.create_reflection(model_klass, name, scope, options)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def reflect_on_association(association)
|
27
|
+
resource_reflections[association.to_s]&.reflection(model_klass)
|
28
|
+
end
|
29
|
+
|
30
|
+
def belongs_to(name, scope = nil, **options, &block)
|
31
|
+
add_reflection(name, scope, options, :belongs_to, block)
|
32
|
+
end
|
33
|
+
|
34
|
+
def has_many(name, scope = nil, through: nil, **options, &block)
|
35
|
+
raise TypeError, "has_many through is unsupported" if through
|
36
|
+
add_reflection(name, scope, options, :has_many, block)
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def resource_reflections
|
42
|
+
@resource_reflections ||= {}
|
43
|
+
end
|
44
|
+
|
45
|
+
def add_reflection(name, scope, options, macro, block)
|
46
|
+
resource_reflections[name.to_s] = ResourceReflection.new(name, scope, options, macro, block)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -1,15 +1,30 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "hq/graphql/types"
|
4
|
+
|
3
5
|
module HQ
|
4
6
|
module GraphQL
|
5
7
|
class PaginatedAssociationLoader < ::GraphQL::Batch::Loader
|
6
|
-
def
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
8
|
+
def self.for(*args, scope: nil, **kwargs)
|
9
|
+
if scope
|
10
|
+
raise TypeError, "scope must be an ActiveRecord::Relation" unless scope.is_a?(::ActiveRecord::Relation)
|
11
|
+
executor = ::GraphQL::Batch::Executor.current
|
12
|
+
loader_key = loader_key_for(*args, **kwargs, scope: scope.to_sql)
|
13
|
+
executor.loader(loader_key) { new(*args, **kwargs, scope: scope) }
|
14
|
+
else
|
15
|
+
super
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def initialize(model, association_name, internal_association: false, limit: nil, offset: nil, scope: nil, sort_by: nil, sort_order: nil)
|
20
|
+
@model = model
|
21
|
+
@association_name = association_name
|
22
|
+
@internal_association = internal_association
|
23
|
+
@limit = [0, limit].max if limit
|
24
|
+
@offset = [0, offset].max if offset
|
25
|
+
@scope = scope
|
26
|
+
@sort_by = sort_by || :updated_at
|
27
|
+
@sort_order = normalize_sort_order(sort_order)
|
13
28
|
|
14
29
|
validate!
|
15
30
|
end
|
@@ -95,6 +110,7 @@ module HQ
|
|
95
110
|
scope = association_class
|
96
111
|
scope = association.scopes.reduce(scope, &:merge)
|
97
112
|
scope = association_class.default_scopes.reduce(scope, &:merge)
|
113
|
+
scope = scope.merge(@scope) if @scope
|
98
114
|
scope
|
99
115
|
end
|
100
116
|
|
@@ -107,7 +123,11 @@ module HQ
|
|
107
123
|
end
|
108
124
|
|
109
125
|
def association
|
110
|
-
@
|
126
|
+
if @internal_association
|
127
|
+
Types[@model].reflect_on_association(@association_name)
|
128
|
+
else
|
129
|
+
@model.reflect_on_association(@association_name)
|
130
|
+
end
|
111
131
|
end
|
112
132
|
|
113
133
|
def association_class
|
data/lib/hq/graphql/resource.rb
CHANGED
@@ -1,6 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "hq/graphql/enum/sort_by"
|
4
|
+
require "hq/graphql/field_extension/paginated_arguments"
|
5
|
+
require "hq/graphql/input_object"
|
6
|
+
require "hq/graphql/object"
|
3
7
|
require "hq/graphql/resource/auto_mutation"
|
8
|
+
require "hq/graphql/scalars"
|
4
9
|
|
5
10
|
module HQ
|
6
11
|
module GraphQL
|
@@ -18,24 +23,6 @@ module HQ
|
|
18
23
|
|
19
24
|
attr_writer :graphql_name, :model_name
|
20
25
|
|
21
|
-
def sort_fields(*fields)
|
22
|
-
self.sort_fields_enum = fields
|
23
|
-
end
|
24
|
-
|
25
|
-
def sort_fields_enum
|
26
|
-
@sort_fields_enum || ::HQ::GraphQL::Enum::SortBy
|
27
|
-
end
|
28
|
-
|
29
|
-
def sort_fields_enum=(fields)
|
30
|
-
@sort_fields_enum ||= Class.new(::HQ::GraphQL::Enum::SortBy).tap do |c|
|
31
|
-
c.graphql_name "#{graphql_name}Sort"
|
32
|
-
end
|
33
|
-
|
34
|
-
Array(fields).each do |field|
|
35
|
-
@sort_fields_enum.value field.to_s.classify, value: field
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
26
|
def scope(context)
|
40
27
|
scope = model_klass
|
41
28
|
scope = ::HQ::GraphQL.default_scope(scope, context)
|
@@ -80,6 +67,10 @@ module HQ
|
|
80
67
|
@query_klass ||= build_graphql_object
|
81
68
|
end
|
82
69
|
|
70
|
+
def sort_fields_enum
|
71
|
+
@sort_fields_enum || ::HQ::GraphQL::Enum::SortBy
|
72
|
+
end
|
73
|
+
|
83
74
|
protected
|
84
75
|
|
85
76
|
def default_scope(&block)
|
@@ -101,6 +92,10 @@ module HQ
|
|
101
92
|
@query_klass = build_graphql_object(**options, &block)
|
102
93
|
end
|
103
94
|
|
95
|
+
def sort_fields(*fields)
|
96
|
+
self.sort_fields_enum = fields
|
97
|
+
end
|
98
|
+
|
104
99
|
def def_root(field_name, is_array: false, null: true, &block)
|
105
100
|
resource = self
|
106
101
|
resolver = -> {
|
@@ -134,12 +129,7 @@ module HQ
|
|
134
129
|
|
135
130
|
if find_all
|
136
131
|
def_root field_name.pluralize, is_array: true, null: false do
|
137
|
-
if pagination
|
138
|
-
argument :offset, Integer, required: false
|
139
|
-
argument :limit, Integer, required: false
|
140
|
-
end
|
141
|
-
argument :sort_by, scoped_self.sort_fields_enum, required: false
|
142
|
-
argument :sort_order, Enum::SortOrder, required: false
|
132
|
+
extension FieldExtension::PaginatedArguments, klass: scoped_self.model_klass if pagination
|
143
133
|
|
144
134
|
define_method(:resolve) do |limit: nil, offset: nil, sort_by: nil, sort_order: nil, **_attrs|
|
145
135
|
scope = scoped_self.scope(context).all
|
@@ -186,6 +176,16 @@ module HQ
|
|
186
176
|
class_eval(&block) if block
|
187
177
|
end
|
188
178
|
end
|
179
|
+
|
180
|
+
def sort_fields_enum=(fields)
|
181
|
+
@sort_fields_enum ||= Class.new(::HQ::GraphQL::Enum::SortBy).tap do |c|
|
182
|
+
c.graphql_name "#{graphql_name}Sort"
|
183
|
+
end
|
184
|
+
|
185
|
+
Array(fields).each do |field|
|
186
|
+
@sort_fields_enum.value field.to_s.classify, value: field
|
187
|
+
end
|
188
|
+
end
|
189
189
|
end
|
190
190
|
end
|
191
191
|
end
|
data/lib/hq/graphql/types.rb
CHANGED
data/lib/hq/graphql/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hq-graphql
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.1.
|
4
|
+
version: 2.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Danny Jones
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-06-
|
11
|
+
date: 2020-06-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -255,10 +255,14 @@ files:
|
|
255
255
|
- lib/hq/graphql/enum/sort_by.rb
|
256
256
|
- lib/hq/graphql/enum/sort_order.rb
|
257
257
|
- lib/hq/graphql/field.rb
|
258
|
+
- lib/hq/graphql/field_extension/association_loader_extension.rb
|
259
|
+
- lib/hq/graphql/field_extension/paginated_arguments.rb
|
260
|
+
- lib/hq/graphql/field_extension/paginated_loader.rb
|
258
261
|
- lib/hq/graphql/input_object.rb
|
259
262
|
- lib/hq/graphql/inputs.rb
|
260
263
|
- lib/hq/graphql/mutation.rb
|
261
264
|
- lib/hq/graphql/object.rb
|
265
|
+
- lib/hq/graphql/object_association.rb
|
262
266
|
- lib/hq/graphql/paginated_association_loader.rb
|
263
267
|
- lib/hq/graphql/resource.rb
|
264
268
|
- lib/hq/graphql/resource/auto_mutation.rb
|