graphiti-activegraph 1.3.1 → 1.3.3

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.
Files changed (62) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/specs.yml +58 -59
  3. data/.gitignore +59 -59
  4. data/.hound.yml +4 -0
  5. data/.rspec +1 -1
  6. data/.rubocop.yml +6 -0
  7. data/CHANGELOG.md +71 -54
  8. data/CHANGELOG_PRE_1.0.0.md +70 -70
  9. data/Gemfile +3 -3
  10. data/LICENSE.txt +21 -21
  11. data/README.md +130 -130
  12. data/docs/deserializer.md +40 -40
  13. data/graphiti-activegraph.gemspec +35 -34
  14. data/lib/graphiti/active_graph/adapters/active_graph/function_sideload.rb +7 -7
  15. data/lib/graphiti/active_graph/adapters/active_graph/has_many_sideload.rb +7 -7
  16. data/lib/graphiti/active_graph/adapters/active_graph/has_one_sideload.rb +7 -7
  17. data/lib/graphiti/active_graph/adapters/active_graph/polymorphic_belongs_to.rb +11 -11
  18. data/lib/graphiti/active_graph/adapters/active_graph/sideload.rb +26 -26
  19. data/lib/graphiti/active_graph/adapters/active_graph.rb +183 -183
  20. data/lib/graphiti/active_graph/concerns/path_relationships.rb +44 -44
  21. data/lib/graphiti/active_graph/concerns/relationships.rb +15 -15
  22. data/lib/graphiti/active_graph/deserializer.rb +138 -138
  23. data/lib/graphiti/active_graph/extensions/context.rb +17 -17
  24. data/lib/graphiti/active_graph/extensions/grouping/params.rb +101 -52
  25. data/lib/graphiti/active_graph/extensions/query_dsl/performer.rb +38 -38
  26. data/lib/graphiti/active_graph/extensions/query_dsl/query_generator.rb +20 -20
  27. data/lib/graphiti/active_graph/extensions/query_params.rb +27 -27
  28. data/lib/graphiti/active_graph/extensions/resources/authorizationable.rb +29 -29
  29. data/lib/graphiti/active_graph/extensions/resources/payload_combinable.rb +24 -24
  30. data/lib/graphiti/active_graph/extensions/resources/preloadable.rb +19 -19
  31. data/lib/graphiti/active_graph/extensions/resources/rel.rb +19 -19
  32. data/lib/graphiti/active_graph/jsonapi_ext/include_directive.rb +66 -66
  33. data/lib/graphiti/active_graph/jsonapi_ext/serializable/resource_ext.rb +8 -8
  34. data/lib/graphiti/active_graph/query.rb +76 -76
  35. data/lib/graphiti/active_graph/request_validators/validator.rb +9 -9
  36. data/lib/graphiti/active_graph/resource.rb +103 -103
  37. data/lib/graphiti/active_graph/resource_proxy.rb +86 -86
  38. data/lib/graphiti/active_graph/resources/interface.rb +14 -14
  39. data/lib/graphiti/active_graph/resources/persistence.rb +25 -25
  40. data/lib/graphiti/active_graph/runner.rb +39 -39
  41. data/lib/graphiti/active_graph/scope.rb +28 -28
  42. data/lib/graphiti/active_graph/scoping/association_eager_load.rb +35 -34
  43. data/lib/graphiti/active_graph/scoping/filter.rb +49 -49
  44. data/lib/graphiti/active_graph/scoping/filterable.rb +12 -12
  45. data/lib/graphiti/active_graph/scoping/include.rb +48 -48
  46. data/lib/graphiti/active_graph/scoping/internal/extra_field_normalizer.rb +76 -76
  47. data/lib/graphiti/active_graph/scoping/internal/include_normalizer.rb +82 -82
  48. data/lib/graphiti/active_graph/scoping/internal/path_descriptor.rb +94 -94
  49. data/lib/graphiti/active_graph/scoping/internal/sort_normalizer.rb +54 -54
  50. data/lib/graphiti/active_graph/scoping/internal/sorting_aliases.rb +35 -35
  51. data/lib/graphiti/active_graph/scoping/internal/sparse_fields_eagerloading.rb +28 -28
  52. data/lib/graphiti/active_graph/serializer.rb +15 -15
  53. data/lib/graphiti/active_graph/sideload_resolve.rb +119 -119
  54. data/lib/graphiti/active_graph/util/parsers/rel_chain.rb +27 -27
  55. data/lib/graphiti/active_graph/util/relationship_payload.rb +33 -33
  56. data/lib/graphiti/active_graph/util/serializer_attribute.rb +17 -17
  57. data/lib/graphiti/active_graph/util/serializer_relationship.rb +28 -28
  58. data/lib/graphiti/active_graph/util/transformers/relation_param.rb +56 -56
  59. data/lib/graphiti/active_graph/version.rb +5 -5
  60. data/lib/graphiti/sidepost_configuration.rb +9 -9
  61. data/lib/graphiti-activegraph.rb +43 -43
  62. metadata +21 -5
@@ -1,183 +1,183 @@
1
- module Graphiti::ActiveGraph
2
- module Adapters
3
- class ActiveGraph < Graphiti::Adapters::Abstract
4
- def self.sideloading_classes
5
- {
6
- has_many: Graphiti::ActiveGraph::Adapters::ActiveGraph::HasManySideload,
7
- has_one: Graphiti::ActiveGraph::Adapters::ActiveGraph::HasOneSideload,
8
- polymorphic_belongs_to: Graphiti::ActiveGraph::Adapters::ActiveGraph::PolymorphicBelongsTo,
9
- belongs_to: Graphiti::ActiveGraph::Adapters::ActiveGraph::HasOneSideload,
10
- }
11
- end
12
-
13
- def base_scope(model)
14
- model
15
- end
16
-
17
- def assign_attributes(model_instance, attributes)
18
- model_instance.before_assign_resource_attr if model_instance.respond_to?(:before_assign_resource_attr)
19
-
20
- # currently there is no possible way to assign association on activegraph without triggering save
21
- # https://github.com/neo4jrb/activegraph/issues/1445
22
- # using "=" operator bypasses validations and callbacks in case of associations
23
- # once above issue is fixed, we can change the below code to assign instead of update
24
-
25
- model_instance.update(attributes)
26
- end
27
-
28
- def paginate(scope, current_page, per_page, offset)
29
- offset ||= (current_page - 1) * per_page
30
- scope.skip(offset).limit(per_page)
31
- end
32
-
33
- def transaction(_model_class)
34
- ::ActiveGraph::Base.transaction do
35
- yield
36
- end
37
- end
38
-
39
- def order(scope, attribute, direction, extra_field = false)
40
- if extra_field
41
- scope.query.order("#{attribute} #{direction}").proxy_as(scope.model, scope.identity)
42
- else
43
- scope.send(resource.relation_resource? ? :rel_order : :order, attribute => direction)
44
- end
45
- end
46
-
47
- def count(scope, _attr)
48
- scope.skip(0).limit(nil).count
49
- end
50
-
51
- def save(model_instance)
52
- model_instance.save if model_instance.changed?
53
- model_instance
54
- end
55
-
56
- def destroy(model_instance)
57
- model_instance.destroy
58
- model_instance
59
- end
60
-
61
- def resolve(scope, resolve_to_rel = false)
62
- resolve_to_rel ? scope.to_a(false, true) : scope.to_a
63
- end
64
-
65
- # def associate_all(parent, children, association_name, association_type)
66
- # if association_type == :has_many
67
- # if !parent.send(:"#{association_name}").present?
68
- # parent.send(:"#{association_name}=", children)
69
- # else
70
- # parent.send(:"#{association_name}") << children
71
- # end
72
- # else
73
- # parent.send(:"#{association_name}=", children)
74
- # end
75
- # end
76
-
77
- def process_belongs_to(persistence, attributes)
78
- []
79
- end
80
-
81
- def process_has_many(persistence, caller_model)
82
- []
83
- end
84
-
85
- def persistence_attributes(persistence, attributes)
86
- rel_attrs = {}
87
- @persistence = persistence
88
-
89
- del_empty_rels(rel_attrs) unless resource.relation_resource?
90
- attributes_for_has_one(rel_attrs)
91
- attributes_for_has_many(rel_attrs)
92
-
93
- attributes.merge rel_attrs
94
- end
95
-
96
- def associate(parent, child, association_name, type)
97
- end
98
-
99
- def disassociate(parent, child, association_name, type)
100
- parent.send(:"#{association_name}=", nil)
101
- end
102
-
103
- def clear_active_connections!
104
- end
105
-
106
- def filter_eq(scope, attribute, value)
107
- scope.where(attribute => value)
108
- end
109
- alias filter_integer_eq filter_eq
110
- alias filter_float_eq filter_eq
111
- alias filter_big_decimal_eq filter_eq
112
- alias filter_date_eq filter_eq
113
- alias filter_boolean_eq filter_eq
114
- alias filter_uuid_eq filter_eq
115
- alias filter_enum_eq filter_eq
116
-
117
- def filter_not_eq(scope, attribute, value)
118
- scope.where_not(attribute => value)
119
- end
120
- alias filter_integer_not_eq filter_not_eq
121
- alias filter_float_not_eq filter_not_eq
122
- alias filter_big_decimal_not_eq filter_not_eq
123
- alias filter_date_not_eq filter_not_eq
124
- alias filter_boolean_not_eq filter_not_eq
125
- alias filter_uuid_not_eq filter_not_eq
126
- alias filter_enum_not_eq filter_not_eq
127
-
128
- private
129
-
130
- def del_empty_rels(rel_attrs)
131
- relationships = @persistence.instance_variable_get(:@relationships)
132
- relationships.each do |rel_name, rel_data|
133
- rel_attrs[rel_name] = nil if rel_data.blank?
134
- end
135
- end
136
-
137
- def attributes_for_has_one(rel_attrs)
138
- @persistence.iterate(only: [:has_one]) do |x|
139
- process_relationship_attrs(x, rel_attrs, false)
140
- end
141
- end
142
-
143
- def attributes_for_has_many(rel_attrs)
144
- @persistence.iterate(only: [:has_many]) do |x|
145
- process_relationship_attrs(x, rel_attrs, true)
146
- end
147
- end
148
-
149
- def process_relationship_attrs(x, rel_attrs, assign_multiple)
150
- x[:object] = find_record(x)
151
- resource = @persistence.instance_variable_get(:@resource)
152
- meta = @persistence.instance_variable_get(:@meta)
153
- # Relationship start/end nodes cannot be changed once persisted
154
- unless meta[:method] == :update && resource.relation_resource?
155
- if assign_multiple
156
- rel_attrs[x[:foreign_key]] ||= []
157
- rel_attrs[x[:foreign_key]] << resource_association_value(x)
158
- else
159
- rel_attrs[x[:foreign_key]] = resource_association_value(x)
160
- end
161
- end
162
- end
163
-
164
- def resource_association_value(rel_map)
165
- if [:destroy, :disassociate].include?(rel_map[:meta][:method]) || rel_map[:attributes].blank?
166
- nil
167
- else
168
- rel_map[:object]
169
- end
170
- end
171
-
172
- def find_record(x)
173
- if Graphiti.config.allow_sidepost
174
- x[:object] = x[:resource]
175
- .persist_with_relationships(x[:meta], x[:attributes], x[:relationships], self, x[:foreign_key])
176
- else
177
- id = x.dig(:attributes, :id)
178
- x[:resource].model.find(id) if id
179
- end
180
- end
181
- end
182
- end
183
- end
1
+ module Graphiti::ActiveGraph
2
+ module Adapters
3
+ class ActiveGraph < Graphiti::Adapters::Abstract
4
+ def self.sideloading_classes
5
+ {
6
+ has_many: Graphiti::ActiveGraph::Adapters::ActiveGraph::HasManySideload,
7
+ has_one: Graphiti::ActiveGraph::Adapters::ActiveGraph::HasOneSideload,
8
+ polymorphic_belongs_to: Graphiti::ActiveGraph::Adapters::ActiveGraph::PolymorphicBelongsTo,
9
+ belongs_to: Graphiti::ActiveGraph::Adapters::ActiveGraph::HasOneSideload,
10
+ }
11
+ end
12
+
13
+ def base_scope(model)
14
+ model
15
+ end
16
+
17
+ def assign_attributes(model_instance, attributes)
18
+ model_instance.before_assign_resource_attr if model_instance.respond_to?(:before_assign_resource_attr)
19
+
20
+ # currently there is no possible way to assign association on activegraph without triggering save
21
+ # https://github.com/neo4jrb/activegraph/issues/1445
22
+ # using "=" operator bypasses validations and callbacks in case of associations
23
+ # once above issue is fixed, we can change the below code to assign instead of update
24
+
25
+ model_instance.update(attributes)
26
+ end
27
+
28
+ def paginate(scope, current_page, per_page, offset)
29
+ offset ||= (current_page - 1) * per_page
30
+ scope.skip(offset).limit(per_page)
31
+ end
32
+
33
+ def transaction(_model_class)
34
+ ::ActiveGraph::Base.transaction do
35
+ yield
36
+ end
37
+ end
38
+
39
+ def order(scope, attribute, direction, extra_field = false)
40
+ if extra_field
41
+ scope.query.order("#{attribute} #{direction}").proxy_as(scope.model, scope.identity)
42
+ else
43
+ scope.send(resource.relation_resource? ? :rel_order : :order, attribute => direction)
44
+ end
45
+ end
46
+
47
+ def count(scope, _attr)
48
+ scope.skip(0).limit(nil).count
49
+ end
50
+
51
+ def save(model_instance)
52
+ model_instance.save if model_instance.changed?
53
+ model_instance
54
+ end
55
+
56
+ def destroy(model_instance)
57
+ model_instance.destroy
58
+ model_instance
59
+ end
60
+
61
+ def resolve(scope, resolve_to_rel = false)
62
+ resolve_to_rel ? scope.to_a(false, true) : scope.to_a
63
+ end
64
+
65
+ # def associate_all(parent, children, association_name, association_type)
66
+ # if association_type == :has_many
67
+ # if !parent.send(:"#{association_name}").present?
68
+ # parent.send(:"#{association_name}=", children)
69
+ # else
70
+ # parent.send(:"#{association_name}") << children
71
+ # end
72
+ # else
73
+ # parent.send(:"#{association_name}=", children)
74
+ # end
75
+ # end
76
+
77
+ def process_belongs_to(persistence, attributes)
78
+ []
79
+ end
80
+
81
+ def process_has_many(persistence, caller_model)
82
+ []
83
+ end
84
+
85
+ def persistence_attributes(persistence, attributes)
86
+ rel_attrs = {}
87
+ @persistence = persistence
88
+
89
+ del_empty_rels(rel_attrs) unless resource.relation_resource?
90
+ attributes_for_has_one(rel_attrs)
91
+ attributes_for_has_many(rel_attrs)
92
+
93
+ attributes.merge rel_attrs
94
+ end
95
+
96
+ def associate(parent, child, association_name, type)
97
+ end
98
+
99
+ def disassociate(parent, child, association_name, type)
100
+ parent.send(:"#{association_name}=", nil)
101
+ end
102
+
103
+ def clear_active_connections!
104
+ end
105
+
106
+ def filter_eq(scope, attribute, value)
107
+ scope.where(attribute => value)
108
+ end
109
+ alias filter_integer_eq filter_eq
110
+ alias filter_float_eq filter_eq
111
+ alias filter_big_decimal_eq filter_eq
112
+ alias filter_date_eq filter_eq
113
+ alias filter_boolean_eq filter_eq
114
+ alias filter_uuid_eq filter_eq
115
+ alias filter_enum_eq filter_eq
116
+
117
+ def filter_not_eq(scope, attribute, value)
118
+ scope.where_not(attribute => value)
119
+ end
120
+ alias filter_integer_not_eq filter_not_eq
121
+ alias filter_float_not_eq filter_not_eq
122
+ alias filter_big_decimal_not_eq filter_not_eq
123
+ alias filter_date_not_eq filter_not_eq
124
+ alias filter_boolean_not_eq filter_not_eq
125
+ alias filter_uuid_not_eq filter_not_eq
126
+ alias filter_enum_not_eq filter_not_eq
127
+
128
+ private
129
+
130
+ def del_empty_rels(rel_attrs)
131
+ relationships = @persistence.instance_variable_get(:@relationships)
132
+ relationships.each do |rel_name, rel_data|
133
+ rel_attrs[rel_name] = nil if rel_data.blank?
134
+ end
135
+ end
136
+
137
+ def attributes_for_has_one(rel_attrs)
138
+ @persistence.iterate(only: [:has_one]) do |x|
139
+ process_relationship_attrs(x, rel_attrs, false)
140
+ end
141
+ end
142
+
143
+ def attributes_for_has_many(rel_attrs)
144
+ @persistence.iterate(only: [:has_many]) do |x|
145
+ process_relationship_attrs(x, rel_attrs, true)
146
+ end
147
+ end
148
+
149
+ def process_relationship_attrs(x, rel_attrs, assign_multiple)
150
+ x[:object] = find_record(x)
151
+ resource = @persistence.instance_variable_get(:@resource)
152
+ meta = @persistence.instance_variable_get(:@meta)
153
+ # Relationship start/end nodes cannot be changed once persisted
154
+ unless meta[:method] == :update && resource.relation_resource?
155
+ if assign_multiple
156
+ rel_attrs[x[:foreign_key]] ||= []
157
+ rel_attrs[x[:foreign_key]] << resource_association_value(x)
158
+ else
159
+ rel_attrs[x[:foreign_key]] = resource_association_value(x)
160
+ end
161
+ end
162
+ end
163
+
164
+ def resource_association_value(rel_map)
165
+ if [:destroy, :disassociate].include?(rel_map[:meta][:method]) || rel_map[:attributes].blank?
166
+ nil
167
+ else
168
+ rel_map[:object]
169
+ end
170
+ end
171
+
172
+ def find_record(x)
173
+ if Graphiti.config.allow_sidepost
174
+ x[:object] = x[:resource]
175
+ .persist_with_relationships(x[:meta], x[:attributes], x[:relationships], self, x[:foreign_key])
176
+ else
177
+ id = x.dig(:attributes, :id)
178
+ x[:resource].model.find(id) if id
179
+ end
180
+ end
181
+ end
182
+ end
183
+ end
@@ -1,44 +1,44 @@
1
- module Graphiti::ActiveGraph::Concerns
2
- module PathRelationships
3
- def add_path_id_to_relationships!(params)
4
- return params if path_relationships_updated?
5
- detect_conflict(:id, @params[:id]&.to_s, attributes[:id]&.to_s)
6
- path_map.each do |rel_name, path_value|
7
- body_value = relationships.dig(rel_name, :attributes, :id)
8
- if body_value
9
- detect_conflict(rel_name, path_value&.to_s, body_value&.to_s)
10
- else
11
- update_params(params, rel_name, path_value)
12
- update_realationships(rel_name, path_value)
13
- end
14
- end
15
- path_relationships_updated!
16
- params
17
- end
18
-
19
- private
20
-
21
- def path_relationships_updated!
22
- @path_relationships_updated = true
23
- end
24
-
25
- def path_relationships_updated?
26
- @path_relationships_updated.present?
27
- end
28
-
29
- def update_params(params, rel_name, path_value)
30
- params[:data] ||= {}
31
- params[:data][:relationships] ||= {}
32
- params[:data][:relationships][rel_name] = {
33
- data: {
34
- type: derive_resource_type(rel_name),
35
- id: path_value
36
- }
37
- }
38
- end
39
-
40
- def update_realationships(rel_name, path_value)
41
- relationships[rel_name] = { meta: {}, attributes: { id: path_value } }
42
- end
43
- end
44
- end
1
+ module Graphiti::ActiveGraph::Concerns
2
+ module PathRelationships
3
+ def add_path_id_to_relationships!(params)
4
+ return params if path_relationships_updated?
5
+ detect_conflict(:id, @params[:id]&.to_s, attributes[:id]&.to_s)
6
+ path_map.each do |rel_name, path_value|
7
+ body_value = relationships.dig(rel_name, :attributes, :id)
8
+ if body_value
9
+ detect_conflict(rel_name, path_value&.to_s, body_value&.to_s)
10
+ else
11
+ update_params(params, rel_name, path_value)
12
+ update_realationships(rel_name, path_value)
13
+ end
14
+ end
15
+ path_relationships_updated!
16
+ params
17
+ end
18
+
19
+ private
20
+
21
+ def path_relationships_updated!
22
+ @path_relationships_updated = true
23
+ end
24
+
25
+ def path_relationships_updated?
26
+ @path_relationships_updated.present?
27
+ end
28
+
29
+ def update_params(params, rel_name, path_value)
30
+ params[:data] ||= {}
31
+ params[:data][:relationships] ||= {}
32
+ params[:data][:relationships][rel_name] = {
33
+ data: {
34
+ type: derive_resource_type(rel_name),
35
+ id: path_value
36
+ }
37
+ }
38
+ end
39
+
40
+ def update_realationships(rel_name, path_value)
41
+ relationships[rel_name] = { meta: {}, attributes: { id: path_value } }
42
+ end
43
+ end
44
+ end
@@ -1,15 +1,15 @@
1
- module Graphiti::ActiveGraph::Concerns
2
- module Relationships
3
- def relationship?(name)
4
- relationships[name.to_sym].present?
5
- end
6
-
7
- def relationship_id(name)
8
- relationships[name]&.dig(:attributes, :id)
9
- end
10
-
11
- def relationship_ids(name)
12
- Array.wrap(relationships[name]).pluck(:attributes).pluck(:id)
13
- end
14
- end
15
- end
1
+ module Graphiti::ActiveGraph::Concerns
2
+ module Relationships
3
+ def relationship?(name)
4
+ relationships[name.to_sym].present?
5
+ end
6
+
7
+ def relationship_id(name)
8
+ relationships[name]&.dig(:attributes, :id)
9
+ end
10
+
11
+ def relationship_ids(name)
12
+ Array.wrap(relationships[name]).pluck(:attributes).pluck(:id)
13
+ end
14
+ end
15
+ end