graphiti_gql 0.2.23 → 0.2.26

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c5dfc016180ec5fd536f236be63b24cad42471a74d44ab234dee1966f2de942e
4
- data.tar.gz: f97bbe243fa4a205004abdbd522942005b09e87fa37ba1db465000b383e355d0
3
+ metadata.gz: 00d26d5be2b1e9f1f277e0d0922faec91599f72a28fabe77ff16fb1f2ae6870f
4
+ data.tar.gz: a868703b6bf2a2fbf88464396f0fe17ba836ffc183cee22e2aad06b5bd6a1223
5
5
  SHA512:
6
- metadata.gz: '08c76642c2b6be4401a6efcce4c9070c52eee1661c333bcefc30a328826a0558aa6ed013028cb4d173bb2b47565aeeda18c2648a946d6ebe1dbcde2147325ced'
7
- data.tar.gz: d16a38a32815b6df020817bb5797ac26ce6507a443375955aad3c2c1fe8fe29192dbf0f01a854d6d565946daae1fe9a713c67df050bbc230c320c282a35e339f
6
+ metadata.gz: 02f91817b443a31d0eaa280316ddd1ab18691936e401c431a38354370f8a6a1b435076fa022df6d37041f35e08d06fb99fc8104aa957cca4e3b9507bba828117
7
+ data.tar.gz: 94ca96b977800303c54ecf61fabfeeca65c4b839fc1ae501e6192fdbb30af86181a1aada59d64a47339c28ff3ca4e5469d071a27db9280a59e8c0b339b0f70f6
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- graphiti_gql (0.2.23)
4
+ graphiti_gql (0.2.26)
5
5
  activemodel (> 6.0, < 8.0)
6
6
  graphiti (~> 1.3.9)
7
7
  graphql (~> 2.0)
@@ -21,9 +21,9 @@ GEM
21
21
  coderay (1.1.3)
22
22
  concurrent-ruby (1.1.10)
23
23
  diff-lcs (1.5.0)
24
- dry-container (0.10.0)
24
+ dry-container (0.10.1)
25
25
  concurrent-ruby (~> 1.0)
26
- dry-core (0.8.0)
26
+ dry-core (0.8.1)
27
27
  concurrent-ruby (~> 1.0)
28
28
  dry-inflector (0.3.0)
29
29
  dry-logic (1.2.0)
@@ -25,6 +25,10 @@ module GraphitiGql
25
25
  end
26
26
  end
27
27
 
28
+ def value_object?
29
+ self.class.value_object?
30
+ end
31
+
28
32
  def filterings
29
33
  @filterings ||= begin
30
34
  if @params.key?(:filter)
@@ -67,6 +71,11 @@ module GraphitiGql
67
71
  end
68
72
 
69
73
  class_methods do
74
+ def config
75
+ return @config if @config
76
+ super
77
+ @config = @config.merge(value_objects: {}, is_value_object: false)
78
+ end
70
79
 
71
80
  def attribute(*args)
72
81
  super(*args).tap do
@@ -79,7 +88,14 @@ module GraphitiGql
79
88
  end
80
89
 
81
90
  def filter(name, *args, &blk)
91
+ is_bool = (filters[name] && filters[name][:type] == :boolean) ||
92
+ args[0] == :boolean
93
+ opts = args.length == 1 ? args[0] : args[1]
94
+ boolean_array = is_bool && opts[:single] == false
82
95
  super
96
+ # default behavior is to force single: true
97
+ filters[name][:single] = false if boolean_array
98
+
83
99
  opts = args.extract_options!
84
100
  if opts[:if]
85
101
  attributes[name][:filterable] = opts[:if]
@@ -93,6 +109,40 @@ module GraphitiGql
93
109
  super
94
110
  end
95
111
  end
112
+
113
+ def value_object?
114
+ !!config[:is_value_object]
115
+ end
116
+
117
+ def value_object!
118
+ config[:is_value_object] = true
119
+ self.adapter = ::Graphiti::Adapters::Null
120
+ config[:filters] = {}
121
+ config[:stats] = {}
122
+ config[:sorts] = {}
123
+ config[:attributes].delete(:id)
124
+ define_method :base_scope do
125
+ {}
126
+ end
127
+ define_method :resolve do |parent|
128
+ [parent]
129
+ end
130
+ end
131
+
132
+ def value_object(name, opts = {})
133
+ opts[:array] ||= false
134
+ opts[:null] = true if opts[:null] != false
135
+ config[:value_objects][name] = Graphiti::ValueObjectAssociation.new(
136
+ name,
137
+ parent_resource_class: self,
138
+ resource_class: opts[:resource],
139
+ _alias: opts[:alias],
140
+ is_array: opts[:array],
141
+ null: opts[:null],
142
+ readable: opts[:readable],
143
+ deprecation_reason: opts[:deprecation_reason]
144
+ )
145
+ end
96
146
  end
97
147
  end
98
148
  Graphiti::Resource.send(:prepend, ResourceExtras)
@@ -264,6 +314,20 @@ module GraphitiGql
264
314
  description: 'Datetime with milliseconds'
265
315
  }
266
316
 
317
+ [:string, :integer, :float, :datetime, :precise_datetime].each do |kind|
318
+ duped_hash = Graphiti::Util::Hash.deep_dup(Graphiti::Types[:hash])
319
+ type = Graphiti::Types[:"#{kind}_range"] = duped_hash
320
+ type[:canonical_name] = :"#{kind}_range"
321
+ Graphiti::Types[:"array_of_#{kind}_ranges"] = {
322
+ canonical_name: :"#{kind}_range",
323
+ params: Dry::Types["strict.array"].of(type[:params]),
324
+ read: Dry::Types["strict.array"].of(type[:read]),
325
+ write: Dry::Types["strict.array"].of(type[:write]),
326
+ kind: "array",
327
+ description: "Base Type."
328
+ }
329
+ end
330
+
267
331
  module ActiveRecordAdapterExtras
268
332
  extend ActiveSupport::Concern
269
333
 
@@ -363,8 +427,18 @@ module GraphitiGql
363
427
 
364
428
  Graphiti::Query.send(:prepend, QueryExtras)
365
429
  module ScopeExtras
430
+ def initialize(object, resource, query, opts = {})
431
+ if resource.value_object?
432
+ object = query.params[:parent]
433
+ super(object, resource, query, opts)
434
+ else
435
+ super
436
+ end
437
+ end
438
+
366
439
  def resolve(*args)
367
440
  results = super
441
+ raise Graphiti::Errors::InvalidResolve unless results.is_a?(Array)
368
442
  results.reverse! if @query.hash[:reverse]
369
443
  results
370
444
  end
@@ -383,4 +457,66 @@ module GraphitiGql
383
457
  ::Graphiti::Adapters::ActiveRecord::ManyToManySideload
384
458
  .send(:prepend, ActiveRecordManyToManyExtras)
385
459
  end
460
+ end
461
+
462
+ class Graphiti::Errors::InvalidResolve < Graphiti::Errors::Base
463
+ def message
464
+ "Resource#resolve must always return an array"
465
+ end
466
+ end
467
+
468
+ class Graphiti::Errors::InvalidValueObject < Graphiti::Errors::Base
469
+ def initialize(resource, name, value)
470
+ @resource = resource
471
+ @name = name
472
+ @value = value
473
+ end
474
+
475
+ def message
476
+ "#{@resource} - value object '#{@name}' configured with array: true but returned non-array: #{@value.inspect}"
477
+ end
478
+ end
479
+
480
+ class Graphiti::ValueObjectAssociation
481
+ attr_reader :name,
482
+ :parent_resource_class,
483
+ :alias,
484
+ :readable,
485
+ :null,
486
+ :deprecation_reason
487
+
488
+ def initialize(
489
+ name,
490
+ parent_resource_class:,
491
+ resource_class:,
492
+ is_array: false,
493
+ readable: nil,
494
+ null: true,
495
+ _alias: nil,
496
+ deprecation_reason: nil
497
+ )
498
+ @name = name
499
+ @parent_resource_class = parent_resource_class
500
+ @resource_class = resource_class
501
+ @readable = readable
502
+ @array = is_array
503
+ @alias = _alias
504
+ @null = null
505
+ @deprecation_reason = deprecation_reason
506
+ end
507
+
508
+ def array?
509
+ !!@array
510
+ end
511
+
512
+ def resource_class
513
+ @resource_class ||= Graphiti::Util::Class
514
+ .infer_resource_class(@parent_resource_class, name)
515
+ end
516
+
517
+ def build_resource(parent)
518
+ instance = resource_class.new
519
+ instance.parent = parent
520
+ instance
521
+ end
386
522
  end
@@ -30,7 +30,11 @@ module GraphitiGql
30
30
  value = if _config[:proc]
31
31
  instance_eval(&_config[:proc])
32
32
  else
33
- object.send(_alias || _name)
33
+ if object.is_a?(Hash)
34
+ object[_name] || object[_name.to_s]
35
+ else
36
+ object.send(_alias || _name)
37
+ end
34
38
  end
35
39
  return if value.nil?
36
40
  Graphiti::Types[_config[:type]][:read].call(value)
@@ -67,6 +67,7 @@ module GraphitiGql
67
67
  edge_resource = @sideload.edge_resource
68
68
  ResourceType.add_fields(klass, edge_resource, id: false)
69
69
  ResourceType.add_relationships(edge_resource, klass)
70
+ ResourceType.add_value_objects(edge_resource, klass)
70
71
  klass
71
72
  end
72
73
 
@@ -10,6 +10,7 @@ module GraphitiGql
10
10
  def build
11
11
  @resources.each { |resource| ResourceType.new(resource).build }
12
12
  define_entrypoints
13
+ add_value_objects
13
14
  add_relationships
14
15
  @query_class
15
16
  end
@@ -35,6 +36,13 @@ module GraphitiGql
35
36
  ResourceType.add_relationships(resource, type)
36
37
  end
37
38
  end
39
+
40
+ def add_value_objects
41
+ registry.resource_types.each do |registered|
42
+ resource, type = registered[:resource], registered[:type]
43
+ ResourceType.add_value_objects(resource, type)
44
+ end
45
+ end
38
46
  end
39
47
  end
40
48
  end
@@ -52,7 +52,8 @@ module GraphitiGql
52
52
  # When polymorphic parent, returns the Interface not the Class
53
53
  def resource_types
54
54
  values
55
- .select { |v| v.key?(:resource) && !v[:interface] }
55
+ .select { |v| v.key?(:resource) }
56
+ .reject { |v| v[:interface] || v[:resource].value_object? }
56
57
  .map { |registered| get(registered[:resource]) }
57
58
  end
58
59
 
@@ -14,7 +14,7 @@ module GraphitiGql
14
14
  end
15
15
  end
16
16
 
17
- def self.add_fields(type, resource, id: true)
17
+ def self.add_fields(type, resource, id: true) # id: false for edges
18
18
  resource.attributes.each_pair do |name, config|
19
19
  next if name == :id && id == false
20
20
  if config[:readable]
@@ -23,6 +23,41 @@ module GraphitiGql
23
23
  end
24
24
  end
25
25
 
26
+ def self.add_value_objects(resource, type)
27
+ resource.config[:value_objects].each_pair do |name, vo_association|
28
+ vo_resource_class = vo_association.resource_class
29
+ value_object_type = Schema.registry.get(vo_resource_class)[:type]
30
+ if vo_association.array?
31
+ value_object_type = [value_object_type]
32
+ end
33
+
34
+ _array = vo_association.array?
35
+ opts = { null: vo_association.null }
36
+ opts[:deprecation_reason] = vo_association.deprecation_reason if vo_association.deprecation_reason
37
+ type.field name, value_object_type, **opts
38
+ type.define_method name do
39
+ if (method_name = vo_association.readable)
40
+ unless vo_association.parent_resource_class.new.send(method_name)
41
+ raise ::Graphiti::Errors::UnreadableAttribute
42
+ .new(vo_association.parent_resource_class, name)
43
+ end
44
+ end
45
+
46
+ result = vo_resource_class.all({ parent: object }).to_a
47
+ default_behavior = result == [object]
48
+ result = result.first if !_array
49
+ if default_behavior
50
+ method_name = vo_association.alias.presence || name
51
+ result = object.send(method_name)
52
+ if _array && !result.is_a?(Array)
53
+ raise Graphiti::Errors::InvalidValueObject.new(resource, name, result)
54
+ end
55
+ end
56
+ result
57
+ end
58
+ end
59
+ end
60
+
26
61
  def self.add_relationships(resource, type)
27
62
  resource.sideloads.each do |name, sideload|
28
63
  next unless sideload.readable?
@@ -4,6 +4,31 @@ module GraphitiGql
4
4
  self.time_precision = 6
5
5
  end
6
6
 
7
+ class DatetimeRange < GraphQL::Schema::Object
8
+ field :from, GraphQL::Types::ISO8601DateTime
9
+ field :to, GraphQL::Types::ISO8601DateTime
10
+ end
11
+
12
+ class PreciseDatetimeRange < GraphQL::Schema::Object
13
+ field :from, PreciseDatetime
14
+ field :to, PreciseDatetime
15
+ end
16
+
17
+ class StringRange < GraphQL::Schema::Object
18
+ field :from, String
19
+ field :to, String
20
+ end
21
+
22
+ class IntegerRange < GraphQL::Schema::Object
23
+ field :from, Integer
24
+ field :to, Integer
25
+ end
26
+
27
+ class FloatRange < GraphQL::Schema::Object
28
+ field :from, Float
29
+ field :to, Float
30
+ end
31
+
7
32
  GQL_TYPE_MAP = {
8
33
  integer_id: String,
9
34
  string: String,
@@ -11,18 +36,28 @@ module GraphitiGql
11
36
  integer: Integer,
12
37
  big_integer: GraphQL::Types::BigInt,
13
38
  float: Float,
14
- boolean: GraphQL::Schema::Member::GraphQLTypeNames::Boolean,
39
+ boolean: GraphQL::Types::Boolean,
15
40
  date: GraphQL::Types::ISO8601Date,
16
41
  datetime: GraphQL::Types::ISO8601DateTime,
17
42
  precise_datetime: PreciseDatetime,
18
43
  hash: GraphQL::Types::JSON,
44
+ string_range: StringRange,
45
+ integer_range: IntegerRange,
46
+ float_range: FloatRange,
47
+ datetime_range: DatetimeRange,
48
+ precise_datetime_range: PreciseDatetimeRange,
19
49
  array: [GraphQL::Types::JSON],
20
50
  array_of_strings: [String],
21
51
  array_of_integers: [Integer],
22
52
  array_of_floats: [Float],
23
53
  array_of_dates: [GraphQL::Types::ISO8601Date],
24
54
  array_of_datetimes: [GraphQL::Types::ISO8601DateTime],
25
- array_of_precise_datetimes: [PreciseDatetime]
55
+ array_of_precise_datetimes: [PreciseDatetime],
56
+ array_of_string_ranges: [StringRange],
57
+ array_of_integer_ranges: [IntegerRange],
58
+ array_of_float_ranges: [FloatRange],
59
+ array_of_datetime_ranges: [DatetimeRange],
60
+ array_of_precise_datetime_ranges: [PreciseDatetimeRange]
26
61
  }
27
62
 
28
63
  class RelayConnectionExtension < GraphQL::Schema::Field::ConnectionExtension
@@ -1,3 +1,3 @@
1
1
  module GraphitiGql
2
- VERSION = "0.2.23"
2
+ VERSION = "0.2.26"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: graphiti_gql
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.23
4
+ version: 0.2.26
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lee Richmond
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-08-10 00:00:00.000000000 Z
11
+ date: 2022-08-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: graphql