hq-graphql 2.0.0 → 2.0.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/field/association_loader.rb +27 -0
- data/lib/hq/graphql/field.rb +4 -0
- data/lib/hq/graphql/loaders/association.rb +52 -0
- data/lib/hq/graphql/loaders.rb +4 -0
- data/lib/hq/graphql/object.rb +17 -8
- data/lib/hq/graphql/resource/mutation.rb +2 -2
- data/lib/hq/graphql/resource.rb +12 -3
- data/lib/hq/graphql/scalars.rb +1 -1
- data/lib/hq/graphql/types.rb +15 -6
- data/lib/hq/graphql/version.rb +1 -1
- data/lib/hq/graphql.rb +3 -0
- data/lib/hq-graphql.rb +1 -1
- metadata +20 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e2508bf165d504937a9ea21ecc4b463e98e75c4e0d1ae12156df59ed649e4c2d
|
4
|
+
data.tar.gz: 07c40cac0b734c307953864ba415700b0a5dad4769d48e168361449cbe3e3a8e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8c7d4b1e507b25c0288a55387c183079e2dfbe8fec512a15c09d47f1352ed14655dd2507f86f996848a7bf4513969d59fa16f6df8b873ce74488867b31a9832e
|
7
|
+
data.tar.gz: 86c8781c2e074dc2d6882144d8604f33d11cd943f1e3effe0949c5efb8ee26ee9b687c697e2e30e146a7651b41f31252b7ff7bfeb237ff331a917a8aba02e798
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# typed: false
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module HQ
|
5
|
+
module GraphQL
|
6
|
+
module Field
|
7
|
+
class AssociationLoader < ::GraphQL::Schema::Field
|
8
|
+
attr_reader :loader_klass
|
9
|
+
|
10
|
+
def initialize(*args, loader_klass: nil, **options, &block)
|
11
|
+
super(*args, **options, &block)
|
12
|
+
@loader_klass = loader_klass
|
13
|
+
end
|
14
|
+
|
15
|
+
def resolve_field(object, args, ctx)
|
16
|
+
if loader_klass.present? && !!::GraphQL::Batch::Executor.current && object.object
|
17
|
+
Loaders::Association.for(loader_klass.constantize, original_name).load(object.object).then do
|
18
|
+
super
|
19
|
+
end
|
20
|
+
else
|
21
|
+
super
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# typed: false
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module HQ
|
5
|
+
module GraphQL
|
6
|
+
module Loaders
|
7
|
+
class Association < ::GraphQL::Batch::Loader
|
8
|
+
def initialize(model, association_name)
|
9
|
+
@model = model
|
10
|
+
@association_name = association_name
|
11
|
+
validate
|
12
|
+
end
|
13
|
+
|
14
|
+
def load(record)
|
15
|
+
raise TypeError, "#{@model} loader can't load association for #{record.class}" unless record.is_a?(@model)
|
16
|
+
return Promise.resolve(read_association(record)) if association_loaded?(record)
|
17
|
+
super
|
18
|
+
end
|
19
|
+
|
20
|
+
# We want to load the associations on all records, even if they have the same id
|
21
|
+
def cache_key(record)
|
22
|
+
record.object_id
|
23
|
+
end
|
24
|
+
|
25
|
+
def perform(records)
|
26
|
+
preload_association(records)
|
27
|
+
records.each { |record| fulfill(record, read_association(record)) }
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def validate
|
33
|
+
unless @model.reflect_on_association(@association_name)
|
34
|
+
raise ArgumentError, "No association #{@association_name} on #{@model}"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def preload_association(records)
|
39
|
+
::ActiveRecord::Associations::Preloader.new.preload(records, @association_name)
|
40
|
+
end
|
41
|
+
|
42
|
+
def read_association(record)
|
43
|
+
record.public_send(@association_name)
|
44
|
+
end
|
45
|
+
|
46
|
+
def association_loaded?(record)
|
47
|
+
record.association(@association_name).loaded?
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
data/lib/hq/graphql/object.rb
CHANGED
@@ -7,18 +7,20 @@ module HQ
|
|
7
7
|
include Scalars
|
8
8
|
include ::HQ::GraphQL::ActiveRecordExtensions
|
9
9
|
|
10
|
-
|
10
|
+
field_class ::HQ::GraphQL::Field::AssociationLoader
|
11
|
+
|
12
|
+
def self.with_model(model_name, attributes: true, associations: true, auto_nil: true)
|
11
13
|
self.model_name = model_name
|
12
14
|
self.auto_load_attributes = attributes
|
13
15
|
self.auto_load_associations = associations
|
14
16
|
|
15
17
|
lazy_load do
|
16
18
|
model_columns.each do |column|
|
17
|
-
field_from_column(column)
|
19
|
+
field_from_column(column, auto_nil: auto_nil)
|
18
20
|
end
|
19
21
|
|
20
22
|
model_associations.each do |association|
|
21
|
-
field_from_association
|
23
|
+
field_from_association(association, auto_nil: auto_nil)
|
22
24
|
end
|
23
25
|
end
|
24
26
|
end
|
@@ -31,21 +33,28 @@ module HQ
|
|
31
33
|
class << self
|
32
34
|
private
|
33
35
|
|
34
|
-
def field_from_association(association)
|
36
|
+
def field_from_association(association, auto_nil:)
|
35
37
|
type = ::HQ::GraphQL::Types[association.klass]
|
36
38
|
name = association.name
|
37
39
|
case association.macro
|
38
40
|
when :has_many
|
39
|
-
field name, [type], null: false
|
41
|
+
field name, [type], null: false, loader_klass: model_name
|
40
42
|
else
|
41
|
-
field name, type, null:
|
43
|
+
field name, type, null: !auto_nil || !association_required?(association), loader_klass: model_name
|
42
44
|
end
|
43
45
|
rescue ::HQ::GraphQL::Types::Error
|
44
46
|
nil
|
45
47
|
end
|
46
48
|
|
47
|
-
def field_from_column(column)
|
48
|
-
field column.name, ::HQ::GraphQL::Types.type_from_column(column), null:
|
49
|
+
def field_from_column(column, auto_nil:)
|
50
|
+
field column.name, ::HQ::GraphQL::Types.type_from_column(column), null: !auto_nil || column.null
|
51
|
+
end
|
52
|
+
|
53
|
+
def association_required?(association)
|
54
|
+
!association.options[:optional] || model_klass.validators.any? do |validation|
|
55
|
+
next unless validation.class == ActiveRecord::Validations::PresenceValidator
|
56
|
+
validation.attributes.any? { |a| a.to_s == association.name.to_s }
|
57
|
+
end
|
49
58
|
end
|
50
59
|
end
|
51
60
|
end
|
@@ -5,13 +5,13 @@ module HQ
|
|
5
5
|
module GraphQL
|
6
6
|
module Resource
|
7
7
|
module Mutation
|
8
|
-
def self.build(model_name, graphql_name:, require_primary_key: false, &block)
|
8
|
+
def self.build(model_name, graphql_name:, require_primary_key: false, show_copy: false, &block)
|
9
9
|
Class.new(::HQ::GraphQL::Mutation) do
|
10
10
|
graphql_name graphql_name
|
11
11
|
|
12
12
|
lazy_load do
|
13
13
|
field :errors, ::HQ::GraphQL::Types::Object, null: false
|
14
|
-
field :resource, ::HQ::GraphQL::Types[model_name], null: true
|
14
|
+
field :resource, ::HQ::GraphQL::Types[model_name, show_copy], null: true
|
15
15
|
end
|
16
16
|
|
17
17
|
instance_eval(&block)
|
data/lib/hq/graphql/resource.rb
CHANGED
@@ -53,6 +53,10 @@ module HQ
|
|
53
53
|
@input_klass ||= build_input_object
|
54
54
|
end
|
55
55
|
|
56
|
+
def input_result_klass
|
57
|
+
@input_result_klass ||= build_graphql_object(name: "#{graphql_name}Copy", auto_nil: false)
|
58
|
+
end
|
59
|
+
|
56
60
|
def query_klass
|
57
61
|
@query_klass ||= build_graphql_object
|
58
62
|
end
|
@@ -67,6 +71,10 @@ module HQ
|
|
67
71
|
@input_klass = build_input_object(**options, &block)
|
68
72
|
end
|
69
73
|
|
74
|
+
def input_result(**options, &block)
|
75
|
+
@input_result_klass = build_graphql_object(name: "#{graphql_name}Copy", **options, auto_nil: false, &block)
|
76
|
+
end
|
77
|
+
|
70
78
|
def mutations(create: true, copy: true, update: true, destroy: true)
|
71
79
|
scoped_graphql_name = graphql_name
|
72
80
|
scoped_model_name = model_name
|
@@ -102,7 +110,8 @@ module HQ
|
|
102
110
|
copy_mutation = ::HQ::GraphQL::Resource::Mutation.build(
|
103
111
|
model_name,
|
104
112
|
graphql_name: "#{scoped_graphql_name}Copy",
|
105
|
-
require_primary_key: true
|
113
|
+
require_primary_key: true,
|
114
|
+
show_copy: true
|
106
115
|
) do
|
107
116
|
define_method(:resolve) do |**args|
|
108
117
|
resource = scoped_self.find_record(args, context)
|
@@ -251,8 +260,8 @@ module HQ
|
|
251
260
|
|
252
261
|
private
|
253
262
|
|
254
|
-
def build_graphql_object(**options, &block)
|
255
|
-
scoped_graphql_name =
|
263
|
+
def build_graphql_object(name: graphql_name, **options, &block)
|
264
|
+
scoped_graphql_name = name
|
256
265
|
scoped_model_name = model_name
|
257
266
|
Class.new(::HQ::GraphQL::Object) do
|
258
267
|
graphql_name scoped_graphql_name
|
data/lib/hq/graphql/scalars.rb
CHANGED
data/lib/hq/graphql/types.rb
CHANGED
@@ -8,11 +8,12 @@ module HQ
|
|
8
8
|
MISSING_TYPE_MSG = "The GraphQL type for `%{klass}` is missing."
|
9
9
|
end
|
10
10
|
|
11
|
-
def self.[](key)
|
12
|
-
@types ||= Hash.new do |hash,
|
13
|
-
|
11
|
+
def self.[](key, *args)
|
12
|
+
@types ||= Hash.new do |hash, options|
|
13
|
+
klass, show_copy = Array(options)
|
14
|
+
hash[klass] = show_copy ? copy_klass(klass) : klass_for(klass)
|
14
15
|
end
|
15
|
-
@types[key]
|
16
|
+
@types[[key, *args]]
|
16
17
|
end
|
17
18
|
|
18
19
|
def self.type_from_column(column)
|
@@ -43,10 +44,18 @@ module HQ
|
|
43
44
|
class << self
|
44
45
|
private
|
45
46
|
|
47
|
+
def copy_klass(klass_or_string)
|
48
|
+
find_klass(klass_or_string, :input_result_klass)
|
49
|
+
end
|
50
|
+
|
46
51
|
def klass_for(klass_or_string)
|
52
|
+
find_klass(klass_or_string, :query_klass)
|
53
|
+
end
|
54
|
+
|
55
|
+
def find_klass(klass_or_string, method)
|
47
56
|
klass = klass_or_string.is_a?(String) ? klass_or_string.constantize : klass_or_string
|
48
|
-
::HQ::GraphQL.types.detect { |t| t.model_klass == klass }&.
|
49
|
-
::HQ::GraphQL.types.detect { |t| t.model_klass == klass.base_class }&.
|
57
|
+
::HQ::GraphQL.types.detect { |t| t.model_klass == klass }&.send(method) ||
|
58
|
+
::HQ::GraphQL.types.detect { |t| t.model_klass == klass.base_class }&.send(method) ||
|
50
59
|
raise(Error, Error::MISSING_TYPE_MSG % { klass: klass.name })
|
51
60
|
end
|
52
61
|
end
|
data/lib/hq/graphql/version.rb
CHANGED
data/lib/hq/graphql.rb
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
|
4
4
|
require "rails"
|
5
5
|
require "graphql"
|
6
|
+
require "graphql/batch"
|
6
7
|
require "sorbet-runtime"
|
7
8
|
require "hq/graphql/config"
|
8
9
|
|
@@ -49,8 +50,10 @@ end
|
|
49
50
|
require "hq/graphql/active_record_extensions"
|
50
51
|
require "hq/graphql/scalars"
|
51
52
|
|
53
|
+
require "hq/graphql/field"
|
52
54
|
require "hq/graphql/inputs"
|
53
55
|
require "hq/graphql/input_object"
|
56
|
+
require "hq/graphql/loaders"
|
54
57
|
require "hq/graphql/mutation"
|
55
58
|
require "hq/graphql/object"
|
56
59
|
require "hq/graphql/resource"
|
data/lib/hq-graphql.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.0.
|
4
|
+
version: 2.0.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: 2019-
|
11
|
+
date: 2019-10-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -44,6 +44,20 @@ dependencies:
|
|
44
44
|
- - ">="
|
45
45
|
- !ruby/object:Gem::Version
|
46
46
|
version: 1.9.6
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: graphql-batch
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - "~>"
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0.4'
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - "~>"
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '0.4'
|
47
61
|
- !ruby/object:Gem::Dependency
|
48
62
|
name: pg
|
49
63
|
requirement: !ruby/object:Gem::Requirement
|
@@ -241,8 +255,12 @@ files:
|
|
241
255
|
- lib/hq/graphql/active_record_extensions.rb
|
242
256
|
- lib/hq/graphql/config.rb
|
243
257
|
- lib/hq/graphql/engine.rb
|
258
|
+
- lib/hq/graphql/field.rb
|
259
|
+
- lib/hq/graphql/field/association_loader.rb
|
244
260
|
- lib/hq/graphql/input_object.rb
|
245
261
|
- lib/hq/graphql/inputs.rb
|
262
|
+
- lib/hq/graphql/loaders.rb
|
263
|
+
- lib/hq/graphql/loaders/association.rb
|
246
264
|
- lib/hq/graphql/mutation.rb
|
247
265
|
- lib/hq/graphql/object.rb
|
248
266
|
- lib/hq/graphql/resource.rb
|