graphiti-activegraph 0.1.24 → 0.1.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 +4 -4
- data/.gitignore +1 -1
- data/CHANGELOG.md +6 -0
- data/graphiti-activegraph.gemspec +3 -0
- data/lib/graphiti/active_graph/concerns/path_relationships.rb +44 -0
- data/lib/graphiti/active_graph/deserializer.rb +32 -41
- data/lib/graphiti/active_graph/extensions/grouping/params.rb +52 -0
- data/lib/graphiti/active_graph/extensions/query_params.rb +27 -0
- data/lib/graphiti/active_graph/request_validators/validator.rb +9 -0
- data/lib/graphiti/active_graph/resource.rb +8 -0
- data/lib/graphiti/active_graph/sideload_resolve.rb +37 -0
- data/lib/graphiti/active_graph/version.rb +1 -1
- data/lib/graphiti-activegraph.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: 4e2e06c991422f868705fa49e812c51af9c537366fac6246ebfc882969e7bdae
|
|
4
|
+
data.tar.gz: 7fc80649e1455f255450e00dcf6c66fe0ff889d1d74e16ebe123dc70362e8266
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3db8cee492d4ac415bddc3a44cf755cfa4f32a0b3865f90821c62a681838776d48c6e12dff4fd30fafce88f204c56884c37bc016bc4b71166fe572ec725ab1df
|
|
7
|
+
data.tar.gz: 8d7f27a547460a725247b4b28ea629fec384f20e0255f29771c6f805e096063f8bfb5138e576f7ef7d6c7eb1acfe4cfca735940d2852dee41fee3042462ca604
|
data/.gitignore
CHANGED
data/CHANGELOG.md
CHANGED
|
@@ -56,6 +56,12 @@ Features:
|
|
|
56
56
|
|
|
57
57
|
- Added preliminary support for Sideload backed by function instead of model association
|
|
58
58
|
|
|
59
|
+
## 0.1.25 (04-12-2024)
|
|
60
|
+
|
|
61
|
+
Features:
|
|
62
|
+
|
|
63
|
+
- Added support to preload extra_fields for the main resource, replacing N+1 queries with a single query. This does not apply to sideloaded resources.
|
|
64
|
+
|
|
59
65
|
<!-- ### [version (DD-MM-YYYY)] -->
|
|
60
66
|
<!-- Breaking changes:-->
|
|
61
67
|
<!-- Features:-->
|
|
@@ -25,6 +25,9 @@ Gem::Specification.new do |spec|
|
|
|
25
25
|
spec.add_development_dependency "faraday", "~> 0.15"
|
|
26
26
|
spec.add_development_dependency "kaminari", "~> 0.17"
|
|
27
27
|
spec.add_development_dependency "bundler"
|
|
28
|
+
# dependency conflict introduced during runtime
|
|
29
|
+
# jar-dependencies is a default gem in JRuby and also a dependency in neo4j-ruby-driver
|
|
30
|
+
spec.add_development_dependency "jar-dependencies", "~> 0.4.1"
|
|
28
31
|
spec.add_development_dependency "rake", "~> 10.0"
|
|
29
32
|
spec.add_development_dependency "graphiti_spec_helpers", "1.0.beta.4"
|
|
30
33
|
spec.add_development_dependency "standard"
|
|
@@ -0,0 +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,5 +1,7 @@
|
|
|
1
1
|
module Graphiti::ActiveGraph
|
|
2
|
-
|
|
2
|
+
class Deserializer < Graphiti::Deserializer
|
|
3
|
+
include Concerns::PathRelationships
|
|
4
|
+
|
|
3
5
|
class Conflict < StandardError
|
|
4
6
|
attr_reader :key, :path_value, :body_value
|
|
5
7
|
|
|
@@ -14,13 +16,33 @@ module Graphiti::ActiveGraph
|
|
|
14
16
|
end
|
|
15
17
|
end
|
|
16
18
|
|
|
17
|
-
def initialize(payload, env=nil, model=nil, parent_map=nil)
|
|
19
|
+
def initialize(payload, env = nil, model = nil, parent_map = nil)
|
|
18
20
|
super(payload)
|
|
19
21
|
|
|
20
22
|
@params = payload
|
|
21
23
|
@model = model
|
|
22
24
|
@parent_map = parent_map || {}
|
|
23
25
|
@env = env
|
|
26
|
+
|
|
27
|
+
return unless data.blank? && env && parsable_content?
|
|
28
|
+
|
|
29
|
+
raise ArgumentError, "JSON API payload must contain the 'data' key"
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def process_relationship_datum(datum)
|
|
33
|
+
{
|
|
34
|
+
meta: {
|
|
35
|
+
jsonapi_type: datum[:type],
|
|
36
|
+
temp_id: datum[:'temp-id'],
|
|
37
|
+
method: datum[:method]&.to_sym
|
|
38
|
+
},
|
|
39
|
+
attributes: datum[:id] ? { id: datum[:id] } : {},
|
|
40
|
+
relationships: {}
|
|
41
|
+
}
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def meta_params
|
|
45
|
+
data[:meta] || {}
|
|
24
46
|
end
|
|
25
47
|
|
|
26
48
|
def process_relationships(relationship_hash)
|
|
@@ -33,6 +55,10 @@ module Graphiti::ActiveGraph
|
|
|
33
55
|
end
|
|
34
56
|
end
|
|
35
57
|
|
|
58
|
+
def relationship?(name)
|
|
59
|
+
relationships[name.to_sym].present?
|
|
60
|
+
end
|
|
61
|
+
|
|
36
62
|
# change empty relationship as `disassociate` hash so they will be removed
|
|
37
63
|
def process_nil_relationship(name)
|
|
38
64
|
attributes = {}
|
|
@@ -62,45 +88,6 @@ module Graphiti::ActiveGraph
|
|
|
62
88
|
results
|
|
63
89
|
end
|
|
64
90
|
|
|
65
|
-
def add_path_id_to_relationships!(params)
|
|
66
|
-
return params if path_relationships_updated?
|
|
67
|
-
detect_conflict(:id, @params[:id]&.to_s, attributes[:id]&.to_s)
|
|
68
|
-
path_map.each do |rel_name, path_value|
|
|
69
|
-
body_value = relationships.dig(rel_name, :attributes, :id)
|
|
70
|
-
if body_value
|
|
71
|
-
detect_conflict(rel_name, path_value&.to_s, body_value&.to_s)
|
|
72
|
-
else
|
|
73
|
-
update_params(params, rel_name, path_value)
|
|
74
|
-
update_realationships(rel_name, path_value)
|
|
75
|
-
end
|
|
76
|
-
end
|
|
77
|
-
path_relationships_updated!
|
|
78
|
-
params
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
def path_relationships_updated!
|
|
82
|
-
@path_relationships_updated = true
|
|
83
|
-
end
|
|
84
|
-
|
|
85
|
-
def path_relationships_updated?
|
|
86
|
-
@path_relationships_updated.present?
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
def update_params(params, rel_name, path_value)
|
|
90
|
-
params[:data] ||= {}
|
|
91
|
-
params[:data][:relationships] ||= {}
|
|
92
|
-
params[:data][:relationships][rel_name] = {
|
|
93
|
-
data: {
|
|
94
|
-
type: derive_resource_type(rel_name),
|
|
95
|
-
id: path_value
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
end
|
|
99
|
-
|
|
100
|
-
def update_realationships(rel_name, path_value)
|
|
101
|
-
relationships[rel_name] = { meta: {}, attributes: { id: path_value } }
|
|
102
|
-
end
|
|
103
|
-
|
|
104
91
|
def path_map
|
|
105
92
|
map = @params.select { |key, _| key =~ /_id$/ }.permit!.to_h
|
|
106
93
|
map = filter_keys(map) { |key| key.gsub(/_id$/, '').to_sym }
|
|
@@ -127,6 +114,10 @@ module Graphiti::ActiveGraph
|
|
|
127
114
|
|
|
128
115
|
private
|
|
129
116
|
|
|
117
|
+
def parsable_content?
|
|
118
|
+
true
|
|
119
|
+
end
|
|
120
|
+
|
|
130
121
|
def derive_resource_type(rel_name)
|
|
131
122
|
if @model.include?(ActiveGraph::Node)
|
|
132
123
|
@model.associations[rel_name].target_class.model_name.plural.to_s
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
module Graphiti::ActiveGraph::Extensions::Grouping
|
|
2
|
+
class Params
|
|
3
|
+
attr_reader :params, :grouping_criteria_list, :resource_class
|
|
4
|
+
def initialize(params, resource_class)
|
|
5
|
+
@params = params
|
|
6
|
+
@grouping_criteria_list = params.fetch(:group_by, nil)&.split(',') || []
|
|
7
|
+
@resource_class = resource_class
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def single_grouping_criteria?
|
|
11
|
+
grouping_criteria_list.size < 2
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def grouping_criteria_on_attribute?
|
|
15
|
+
grouping_criteria_list.any? { |criteria| ends_with_attribute?(resource_class.model, criteria) }
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def empty?
|
|
19
|
+
grouping_criteria_list.empty?
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def ends_with_attribute?(model, criteria)
|
|
23
|
+
return false if criteria.blank?
|
|
24
|
+
|
|
25
|
+
last_segment_attribute?(model, criteria.split('.'))
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def last_segment_attribute?(model, segments)
|
|
31
|
+
last_segment = segments.last
|
|
32
|
+
intermediate_model = traverse_to_last_associated_model(model, segments[0...-1])
|
|
33
|
+
|
|
34
|
+
intermediate_model && attribute?(intermediate_model, last_segment)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def traverse_to_last_associated_model(model, intermediate_segments)
|
|
38
|
+
intermediate_segments.each do |segment|
|
|
39
|
+
return false unless(model = associated_model(model, segment))
|
|
40
|
+
end
|
|
41
|
+
model
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def attribute?(model, segment)
|
|
45
|
+
model.attribute_names.include?(segment)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def associated_model(model, segment)
|
|
49
|
+
model.associations[segment.to_sym]&.target_class
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
module Graphiti::ActiveGraph::Extensions
|
|
2
|
+
class QueryParams
|
|
3
|
+
attr_reader :params, :grouping_extra_params, :resource_class
|
|
4
|
+
|
|
5
|
+
def initialize(params, resource_class, grouping_extra_params: {})
|
|
6
|
+
@params = params
|
|
7
|
+
@resource_class = resource_class
|
|
8
|
+
@grouping_extra_params = grouping_extra_params
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def group_by
|
|
12
|
+
Grouping::Params.new(params, resource_class)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def group_by_params
|
|
16
|
+
group_by_params_hash unless group_by.empty?
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def group_by_params_hash
|
|
20
|
+
{ group_by: group_by.grouping_criteria_list }.merge(grouping_extra_params)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def extra_field?(type, name)
|
|
24
|
+
params.dig(:extra_fields, type)&.include?(name.to_s)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -23,6 +23,10 @@ module Graphiti
|
|
|
23
23
|
|
|
24
24
|
def guard_nil_id!(params)
|
|
25
25
|
end
|
|
26
|
+
|
|
27
|
+
def extra_attribute?(name)
|
|
28
|
+
extra_attributes.has_key?(name)
|
|
29
|
+
end
|
|
26
30
|
end
|
|
27
31
|
|
|
28
32
|
module ResourceInstanceMethods
|
|
@@ -30,6 +34,10 @@ module Graphiti
|
|
|
30
34
|
self.class.relation_resource?
|
|
31
35
|
end
|
|
32
36
|
|
|
37
|
+
def extra_attribute?(name)
|
|
38
|
+
self.class.extra_attribute?(name)
|
|
39
|
+
end
|
|
40
|
+
|
|
33
41
|
def sideload_name_arr(query)
|
|
34
42
|
query.sideloads.keys.map(&:to_sym)
|
|
35
43
|
end
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
module Graphiti::ActiveGraph
|
|
2
2
|
module SideloadResolve
|
|
3
|
+
PRELOAD_METHOD_PREFIX = 'preload_'.freeze
|
|
4
|
+
|
|
3
5
|
def initialize(object, resource, query, opts = {})
|
|
4
6
|
@object = object
|
|
5
7
|
@resource = resource
|
|
@@ -15,5 +17,40 @@ module Graphiti::ActiveGraph
|
|
|
15
17
|
|
|
16
18
|
def resolve_sideloads(parents)
|
|
17
19
|
end
|
|
20
|
+
|
|
21
|
+
def resolve
|
|
22
|
+
super.tap { |results| preload_extra_fields(results) }
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
def preload_extra_fields(results)
|
|
28
|
+
requested_extra_fields.each do |extra_field_name|
|
|
29
|
+
next unless preload_extra_field?(extra_field_name)
|
|
30
|
+
|
|
31
|
+
result_map = fetch_preloaded_data(extra_field_name, results)
|
|
32
|
+
assign_preloaded_data(results, extra_field_name, result_map)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def requested_extra_fields
|
|
37
|
+
@query.extra_fields[@resource.type] || []
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def fetch_preloaded_data(extra_field_name, results)
|
|
41
|
+
@resource.model.public_send(default_preload_method(extra_field_name), results.pluck(:id))
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def assign_preloaded_data(results, extra_field_name, result_map)
|
|
45
|
+
results.each { |r| r.public_send("#{extra_field_name}=", result_map[r.id]) }
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def preload_extra_field?(extra_field_name)
|
|
49
|
+
@resource.extra_attribute?(extra_field_name) && @resource.model.respond_to?(default_preload_method(extra_field_name))
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def default_preload_method(extra_field_name)
|
|
53
|
+
"#{PRELOAD_METHOD_PREFIX}#{extra_field_name}"
|
|
54
|
+
end
|
|
18
55
|
end
|
|
19
56
|
end
|
data/lib/graphiti-activegraph.rb
CHANGED
|
@@ -27,7 +27,6 @@ Graphiti::Scoping::Filter.prepend Graphiti::ActiveGraph::Scoping::Filter
|
|
|
27
27
|
Graphiti::Util::SerializerRelationship.prepend Graphiti::ActiveGraph::Util::SerializerRelationship
|
|
28
28
|
Graphiti::Util::SerializerAttribute.prepend Graphiti::ActiveGraph::Util::SerializerAttribute
|
|
29
29
|
Graphiti::Util::RelationshipPayload.prepend Graphiti::ActiveGraph::Util::RelationshipPayload
|
|
30
|
-
Graphiti::Deserializer.prepend Graphiti::ActiveGraph::Deserializer
|
|
31
30
|
Graphiti::Query.prepend Graphiti::ActiveGraph::Query
|
|
32
31
|
Graphiti::Resource.prepend Graphiti::ActiveGraph::ResourceInstanceMethods
|
|
33
32
|
Graphiti::Resource.extend Graphiti::ActiveGraph::Resource
|
|
@@ -35,6 +34,7 @@ Graphiti::ResourceProxy.prepend Graphiti::ActiveGraph::ResourceProxy
|
|
|
35
34
|
Graphiti::Runner.prepend Graphiti::ActiveGraph::Runner
|
|
36
35
|
Graphiti::Scope.prepend Graphiti::ActiveGraph::SideloadResolve
|
|
37
36
|
Graphiti::Configuration.include Graphiti::SidepostConfiguration
|
|
37
|
+
Graphiti::RequestValidators::Validator.prepend Graphiti::ActiveGraph::RequestValidators::Validator
|
|
38
38
|
|
|
39
39
|
# JSONAPI extensions
|
|
40
40
|
JSONAPI::Serializable::Resource.prepend Graphiti::ActiveGraph::JsonapiExt::Serializable::ResourceExt
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: graphiti-activegraph
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.26
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Hardik Joshi
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2025-01-13 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -108,6 +108,20 @@ dependencies:
|
|
|
108
108
|
- - ">="
|
|
109
109
|
- !ruby/object:Gem::Version
|
|
110
110
|
version: '0'
|
|
111
|
+
- !ruby/object:Gem::Dependency
|
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
|
113
|
+
requirements:
|
|
114
|
+
- - "~>"
|
|
115
|
+
- !ruby/object:Gem::Version
|
|
116
|
+
version: 0.4.1
|
|
117
|
+
name: jar-dependencies
|
|
118
|
+
type: :development
|
|
119
|
+
prerelease: false
|
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
121
|
+
requirements:
|
|
122
|
+
- - "~>"
|
|
123
|
+
- !ruby/object:Gem::Version
|
|
124
|
+
version: 0.4.1
|
|
111
125
|
- !ruby/object:Gem::Dependency
|
|
112
126
|
requirement: !ruby/object:Gem::Requirement
|
|
113
127
|
requirements:
|
|
@@ -199,9 +213,13 @@ files:
|
|
|
199
213
|
- lib/graphiti/active_graph/adapters/active_graph/has_one_sideload.rb
|
|
200
214
|
- lib/graphiti/active_graph/adapters/active_graph/polymorphic_belongs_to.rb
|
|
201
215
|
- lib/graphiti/active_graph/adapters/active_graph/sideload.rb
|
|
216
|
+
- lib/graphiti/active_graph/concerns/path_relationships.rb
|
|
202
217
|
- lib/graphiti/active_graph/deserializer.rb
|
|
218
|
+
- lib/graphiti/active_graph/extensions/grouping/params.rb
|
|
219
|
+
- lib/graphiti/active_graph/extensions/query_params.rb
|
|
203
220
|
- lib/graphiti/active_graph/jsonapi_ext/serializable/resource_ext.rb
|
|
204
221
|
- lib/graphiti/active_graph/query.rb
|
|
222
|
+
- lib/graphiti/active_graph/request_validators/validator.rb
|
|
205
223
|
- lib/graphiti/active_graph/resource.rb
|
|
206
224
|
- lib/graphiti/active_graph/resource/interface.rb
|
|
207
225
|
- lib/graphiti/active_graph/resource/persistence.rb
|