hq-graphql 2.1.0 → 2.1.5
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 +4 -1
- data/lib/hq/graphql/active_record_extensions.rb +1 -1
- data/lib/hq/graphql/config.rb +1 -0
- 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 +38 -16
- data/lib/hq/graphql/object_association.rb +67 -0
- data/lib/hq/graphql/paginated_association_loader.rb +86 -31
- data/lib/hq/graphql/resource.rb +44 -31
- data/lib/hq/graphql/resource/auto_mutation.rb +4 -0
- data/lib/hq/graphql/types.rb +11 -4
- 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: 212005e9fc2e97777c5853273618ff5608c310db175324ac75a2e594cb3fa862
|
4
|
+
data.tar.gz: 2b656d56fee16a199bc865370fcdaa4f3460414909b07b2086d9df82c56d2738
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 65e69ef87752fcd9322b06001fb8bd5dd91e7e6e234c43738776681c96d333d7729d3de88118ac1e7829b24d2c139aa71cb3affb055def6a5a63a63af3379079
|
7
|
+
data.tar.gz: 42016b3a7db045b5a768abb5217b9e68baf6b1d8b29e25b758f25c2c559190d2569671a297c0bf423518dae1f20130541904f87b74608105279a237a3677b8ad
|
data/lib/hq/graphql.rb
CHANGED
@@ -8,6 +8,10 @@ require "hq/graphql/config"
|
|
8
8
|
|
9
9
|
module HQ
|
10
10
|
module GraphQL
|
11
|
+
class << self
|
12
|
+
delegate :default_object_class, to: :config
|
13
|
+
end
|
14
|
+
|
11
15
|
def self.config
|
12
16
|
@config ||= ::HQ::GraphQL::Config.new
|
13
17
|
end
|
@@ -64,7 +68,6 @@ module HQ
|
|
64
68
|
end
|
65
69
|
end
|
66
70
|
|
67
|
-
require "hq/graphql/active_record_extensions"
|
68
71
|
require "hq/graphql/association_loader"
|
69
72
|
require "hq/graphql/scalars"
|
70
73
|
require "hq/graphql/comparator"
|
data/lib/hq/graphql/config.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,38 @@ module HQ
|
|
46
62
|
@authorized_action ||= :read
|
47
63
|
end
|
48
64
|
|
49
|
-
def field_from_association(association, auto_nil:)
|
50
|
-
# The PaginationAssociationLoader doesn't support through associations yet
|
51
|
-
return if association.through_reflection? && ::HQ::GraphQL.use_experimental_associations?
|
52
|
-
|
65
|
+
def field_from_association(association, auto_nil:, internal_association: false, &block)
|
53
66
|
association_klass = association.klass
|
54
|
-
|
55
|
-
|
67
|
+
name = association.name.to_s
|
68
|
+
return if fields[name]
|
69
|
+
|
70
|
+
klass = model_klass
|
71
|
+
type = Types[association_klass]
|
56
72
|
case association.macro
|
57
73
|
when :has_many
|
58
74
|
field name, [type], null: false, klass: model_name do
|
59
|
-
if ::HQ::GraphQL.use_experimental_associations?
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
75
|
+
if ::HQ::GraphQL.use_experimental_associations?
|
76
|
+
extension FieldExtension::PaginatedArguments, klass: association_klass
|
77
|
+
extension FieldExtension::PaginatedLoader, klass: klass, association: name, internal_association: internal_association
|
78
|
+
else
|
79
|
+
extension FieldExtension::AssociationLoaderExtension, klass: klass
|
64
80
|
end
|
81
|
+
instance_eval(&block) if block
|
65
82
|
end
|
66
83
|
else
|
67
|
-
field name, type, null: !auto_nil || !association_required?(association), klass: model_name
|
84
|
+
field name, type, null: !auto_nil || !association_required?(association), klass: model_name do
|
85
|
+
extension FieldExtension::AssociationLoaderExtension, klass: klass
|
86
|
+
end
|
68
87
|
end
|
69
|
-
rescue
|
88
|
+
rescue Types::Error
|
70
89
|
nil
|
71
90
|
end
|
72
91
|
|
73
92
|
def field_from_column(column, auto_nil:)
|
74
|
-
|
93
|
+
name = column.name
|
94
|
+
return if fields[name]
|
95
|
+
|
96
|
+
field name, Types.type_from_column(column), null: !auto_nil || column.null
|
75
97
|
end
|
76
98
|
|
77
99
|
def association_required?(association)
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module HQ
|
4
|
+
module GraphQL
|
5
|
+
module ObjectAssociation
|
6
|
+
def reflect_on_association(association)
|
7
|
+
resource_reflections[association.to_s]&.reflection(model_klass)
|
8
|
+
end
|
9
|
+
|
10
|
+
def update(name, &block)
|
11
|
+
resource_reflections[name.to_s] = UpdatedReflection.new(name, block)
|
12
|
+
end
|
13
|
+
|
14
|
+
def belongs_to(name, scope = nil, **options, &block)
|
15
|
+
add_reflection(name, scope, options, :belongs_to, block)
|
16
|
+
end
|
17
|
+
|
18
|
+
def has_many(name, scope = nil, through: nil, **options, &block)
|
19
|
+
raise TypeError, "has_many through is unsupported" if through
|
20
|
+
add_reflection(name, scope, options, :has_many, block)
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def resource_reflections
|
26
|
+
@resource_reflections ||= {}
|
27
|
+
end
|
28
|
+
|
29
|
+
def add_reflection(name, scope, options, macro, block)
|
30
|
+
resource_reflections[name.to_s] = ResourceReflection.new(name, scope, options, macro, block)
|
31
|
+
end
|
32
|
+
|
33
|
+
class ResourceReflection
|
34
|
+
attr_reader :name, :scope, :options, :macro, :block
|
35
|
+
|
36
|
+
def initialize(name, scope, options, macro, block)
|
37
|
+
@name = name
|
38
|
+
@scope = scope
|
39
|
+
@options = options
|
40
|
+
@macro = macro
|
41
|
+
@block = block
|
42
|
+
end
|
43
|
+
|
44
|
+
def reflection(model_klass)
|
45
|
+
if macro == :has_many
|
46
|
+
::ActiveRecord::Associations::Builder::HasMany.create_reflection(model_klass, name, scope, options)
|
47
|
+
elsif macro == :belongs_to
|
48
|
+
::ActiveRecord::Associations::Builder::BelongsTo.create_reflection(model_klass, name, scope, options)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
class UpdatedReflection
|
54
|
+
attr_reader :name, :block
|
55
|
+
|
56
|
+
def initialize(name, block)
|
57
|
+
@name = name
|
58
|
+
@block = block
|
59
|
+
end
|
60
|
+
|
61
|
+
def reflection(model_klass)
|
62
|
+
model_klass.reflect_on_association(name)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
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 || :created_at
|
27
|
+
@sort_order = normalize_sort_order(sort_order)
|
13
28
|
|
14
29
|
validate!
|
15
30
|
end
|
@@ -24,7 +39,8 @@ module HQ
|
|
24
39
|
end
|
25
40
|
|
26
41
|
def perform(records)
|
27
|
-
|
42
|
+
values = records.map { |r| source_value(r) }
|
43
|
+
scope =
|
28
44
|
if @limit || @offset
|
29
45
|
# If a limit or offset is added, then we need to transform the query
|
30
46
|
# into a lateral join so that we can limit on groups of data.
|
@@ -42,59 +58,86 @@ module HQ
|
|
42
58
|
# > ) a_top ON TRUE
|
43
59
|
# > WHERE addresses.user_id IN ($1, $2, ..., $N)
|
44
60
|
# > ORDER BY a_top.created_at DESC
|
45
|
-
inner_table
|
46
|
-
|
61
|
+
inner_table = association_class.arel_table
|
62
|
+
lateral_join_table = through_reflection? ? through_association.klass.arel_table : inner_table
|
63
|
+
from_table = lateral_join_table.alias("outer")
|
47
64
|
|
48
65
|
inside_scope = default_scope.
|
49
66
|
select(inner_table[::Arel.star]).
|
50
67
|
from(inner_table).
|
51
|
-
where(
|
68
|
+
where(lateral_join_table[target_join_key].eq(from_table[target_join_key])).
|
52
69
|
reorder(arel_order(inner_table)).
|
53
70
|
limit(@limit).
|
54
71
|
offset(@offset)
|
55
72
|
|
56
|
-
|
73
|
+
if through_reflection?
|
74
|
+
# expose the through_reflection key
|
75
|
+
inside_scope = inside_scope.select(lateral_join_table[target_join_key])
|
76
|
+
end
|
77
|
+
|
78
|
+
lateral_table = ::Arel::Table.new("top")
|
57
79
|
association_class.
|
58
|
-
select(
|
59
|
-
from(
|
60
|
-
|
61
|
-
|
62
|
-
reorder(arel_order(
|
80
|
+
select(lateral_table[::Arel.star]).distinct.
|
81
|
+
from(from_table).
|
82
|
+
where(from_table[target_join_key].in(values)).
|
83
|
+
joins("INNER JOIN LATERAL (#{inside_scope.to_sql}) #{lateral_table.name} ON TRUE").
|
84
|
+
reorder(arel_order(lateral_table))
|
63
85
|
else
|
64
|
-
default_scope.
|
65
|
-
|
66
|
-
|
86
|
+
scope = default_scope.reorder(arel_order(association_class.arel_table))
|
87
|
+
|
88
|
+
if through_reflection?
|
89
|
+
scope.where(through_association.name => { target_join_key => values }).
|
90
|
+
# expose the through_reflection key
|
91
|
+
select(association_class.arel_table[::Arel.star], through_association.klass.arel_table[target_join_key])
|
92
|
+
else
|
93
|
+
scope.where(target_join_key => values)
|
94
|
+
end
|
67
95
|
end
|
68
96
|
|
69
97
|
results = scope.to_a
|
70
98
|
records.each do |record|
|
71
|
-
fulfill(record,
|
99
|
+
fulfill(record, target_value(record, results)) unless fulfilled?(record)
|
72
100
|
end
|
73
101
|
end
|
74
102
|
|
75
103
|
private
|
76
104
|
|
77
|
-
def
|
78
|
-
belongs_to? ? association.
|
105
|
+
def source_join_key
|
106
|
+
belongs_to? ? association.foreign_key : association.association_primary_key
|
79
107
|
end
|
80
108
|
|
81
|
-
def
|
82
|
-
|
83
|
-
results.send(enumerator) { |r| r.send(association_key) == join_value(record) }
|
109
|
+
def source_value(record)
|
110
|
+
record.send(source_join_key)
|
84
111
|
end
|
85
112
|
|
86
|
-
def
|
87
|
-
|
113
|
+
def target_join_key
|
114
|
+
if through_reflection?
|
115
|
+
through_association.foreign_key
|
116
|
+
elsif belongs_to?
|
117
|
+
association.association_primary_key
|
118
|
+
else
|
119
|
+
association.foreign_key
|
120
|
+
end
|
88
121
|
end
|
89
122
|
|
90
|
-
def
|
91
|
-
|
123
|
+
def target_value(record, results)
|
124
|
+
enumerator = has_many? ? :select : :detect
|
125
|
+
results.send(enumerator) { |r| r.send(target_join_key) == source_value(record) }
|
92
126
|
end
|
93
127
|
|
94
128
|
def default_scope
|
95
129
|
scope = association_class
|
96
130
|
scope = association.scopes.reduce(scope, &:merge)
|
97
131
|
scope = association_class.default_scopes.reduce(scope, &:merge)
|
132
|
+
scope = scope.merge(@scope) if @scope
|
133
|
+
|
134
|
+
if through_reflection?
|
135
|
+
source = association_class.arel_table
|
136
|
+
target = through_association.klass.arel_table
|
137
|
+
join = source.join(target).on(target[association.foreign_key].eq(source[source_join_key]))
|
138
|
+
scope = scope.joins(join.join_sources)
|
139
|
+
end
|
140
|
+
|
98
141
|
scope
|
99
142
|
end
|
100
143
|
|
@@ -106,8 +149,20 @@ module HQ
|
|
106
149
|
association.macro == :has_many
|
107
150
|
end
|
108
151
|
|
152
|
+
def through_association
|
153
|
+
association.through_reflection
|
154
|
+
end
|
155
|
+
|
156
|
+
def through_reflection?
|
157
|
+
association.through_reflection?
|
158
|
+
end
|
159
|
+
|
109
160
|
def association
|
110
|
-
@
|
161
|
+
if @internal_association
|
162
|
+
Types[@model].reflect_on_association(@association_name)
|
163
|
+
else
|
164
|
+
@model.reflect_on_association(@association_name)
|
165
|
+
end
|
111
166
|
end
|
112
167
|
|
113
168
|
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)
|
@@ -72,12 +59,24 @@ module HQ
|
|
72
59
|
@input_klass ||= build_input_object
|
73
60
|
end
|
74
61
|
|
75
|
-
def
|
76
|
-
@
|
62
|
+
def nil_query_object
|
63
|
+
@nil_query_object ||= build_graphql_object(name: "#{graphql_name}Copy", auto_nil: false)
|
77
64
|
end
|
78
65
|
|
79
|
-
def
|
80
|
-
@
|
66
|
+
def query_object
|
67
|
+
@query_object ||= begin
|
68
|
+
if @query_object_options
|
69
|
+
options, block = @query_object_options
|
70
|
+
@query_object_options = nil
|
71
|
+
build_graphql_object(**options, &block)
|
72
|
+
else
|
73
|
+
build_graphql_object
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def sort_fields_enum
|
79
|
+
@sort_fields_enum || ::HQ::GraphQL::Enum::SortBy
|
81
80
|
end
|
82
81
|
|
83
82
|
protected
|
@@ -98,14 +97,22 @@ module HQ
|
|
98
97
|
end
|
99
98
|
|
100
99
|
def query(**options, &block)
|
101
|
-
@
|
100
|
+
@query_object_options = [options, block]
|
101
|
+
end
|
102
|
+
|
103
|
+
def query_class(klass)
|
104
|
+
@query_class = klass
|
105
|
+
end
|
106
|
+
|
107
|
+
def sort_fields(*fields)
|
108
|
+
self.sort_fields_enum = fields
|
102
109
|
end
|
103
110
|
|
104
111
|
def def_root(field_name, is_array: false, null: true, &block)
|
105
112
|
resource = self
|
106
113
|
resolver = -> {
|
107
114
|
Class.new(::GraphQL::Schema::Resolver) do
|
108
|
-
type = is_array ? [resource.
|
115
|
+
type = is_array ? [resource.query_object] : resource.query_object
|
109
116
|
type type, null: null
|
110
117
|
class_eval(&block) if block
|
111
118
|
end
|
@@ -134,12 +141,7 @@ module HQ
|
|
134
141
|
|
135
142
|
if find_all
|
136
143
|
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
|
144
|
+
extension FieldExtension::PaginatedArguments, klass: scoped_self.model_klass if pagination
|
143
145
|
|
144
146
|
define_method(:resolve) do |limit: nil, offset: nil, sort_by: nil, sort_order: nil, **_attrs|
|
145
147
|
scope = scoped_self.scope(context).all
|
@@ -166,7 +168,8 @@ module HQ
|
|
166
168
|
def build_graphql_object(name: graphql_name, **options, &block)
|
167
169
|
scoped_graphql_name = name
|
168
170
|
scoped_model_name = model_name
|
169
|
-
|
171
|
+
object_class = @query_class || ::HQ::GraphQL.default_object_class || ::HQ::GraphQL::Object
|
172
|
+
Class.new(object_class) do
|
170
173
|
graphql_name scoped_graphql_name
|
171
174
|
|
172
175
|
with_model scoped_model_name, **options
|
@@ -186,6 +189,16 @@ module HQ
|
|
186
189
|
class_eval(&block) if block
|
187
190
|
end
|
188
191
|
end
|
192
|
+
|
193
|
+
def sort_fields_enum=(fields)
|
194
|
+
@sort_fields_enum ||= Class.new(::HQ::GraphQL::Enum::SortBy).tap do |c|
|
195
|
+
c.graphql_name "#{graphql_name}Sort"
|
196
|
+
end
|
197
|
+
|
198
|
+
Array(fields).each do |field|
|
199
|
+
@sort_fields_enum.value field.to_s.classify, value: field
|
200
|
+
end
|
201
|
+
end
|
189
202
|
end
|
190
203
|
end
|
191
204
|
end
|
data/lib/hq/graphql/types.rb
CHANGED
@@ -1,5 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "hq/graphql/types/object"
|
4
|
+
require "hq/graphql/types/uuid"
|
5
|
+
|
3
6
|
module HQ
|
4
7
|
module GraphQL
|
5
8
|
module Types
|
@@ -10,7 +13,7 @@ module HQ
|
|
10
13
|
def self.registry
|
11
14
|
@registry ||= Hash.new do |hash, options|
|
12
15
|
klass, nil_klass = Array(options)
|
13
|
-
hash[
|
16
|
+
hash[options] = nil_klass ? nil_query_object(klass) : klass_for(klass)
|
14
17
|
end
|
15
18
|
end
|
16
19
|
|
@@ -39,6 +42,10 @@ module HQ
|
|
39
42
|
::GraphQL::Types::Float
|
40
43
|
when :boolean
|
41
44
|
::GraphQL::Types::Boolean
|
45
|
+
when :date
|
46
|
+
::GraphQL::Types::ISO8601Date
|
47
|
+
when :datetime
|
48
|
+
::GraphQL::Types::ISO8601DateTime
|
42
49
|
else
|
43
50
|
::GraphQL::Types::String
|
44
51
|
end
|
@@ -54,12 +61,12 @@ module HQ
|
|
54
61
|
class << self
|
55
62
|
private
|
56
63
|
|
57
|
-
def
|
58
|
-
find_klass(klass_or_string, :
|
64
|
+
def nil_query_object(klass_or_string)
|
65
|
+
find_klass(klass_or_string, :nil_query_object)
|
59
66
|
end
|
60
67
|
|
61
68
|
def klass_for(klass_or_string)
|
62
|
-
find_klass(klass_or_string, :
|
69
|
+
find_klass(klass_or_string, :query_object)
|
63
70
|
end
|
64
71
|
|
65
72
|
def find_klass(klass_or_string, method)
|
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.5
|
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-
|
11
|
+
date: 2020-07-08 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
|