maestrano-connector-rails 1.2.3 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop_todo.yml +7 -20
- data/VERSION +1 -1
- data/app/controllers/maestrano/synchronizations_controller.rb +3 -3
- data/app/models/maestrano/connector/rails/concerns/complex_entity.rb +50 -22
- data/app/models/maestrano/connector/rails/concerns/connec_helper.rb +157 -19
- data/app/models/maestrano/connector/rails/concerns/entity.rb +63 -42
- data/app/models/maestrano/connector/rails/concerns/external.rb +4 -0
- data/app/models/maestrano/connector/rails/concerns/sub_entity_base.rb +18 -5
- data/app/models/maestrano/connector/rails/organization.rb +1 -1
- data/lib/generators/connector/templates/complex_entity_example/contact_and_lead.rb +5 -5
- data/lib/generators/connector/templates/entity.rb +4 -5
- data/lib/generators/connector/templates/external.rb +6 -0
- data/lib/generators/connector/templates/home_index.haml +7 -4
- data/maestrano-connector-rails.gemspec +7 -4
- data/release_notes.md +5 -0
- data/spec/factories.rb +2 -2
- data/spec/integration/complex_id_references_spec.rb +248 -0
- data/spec/integration/complex_naming_spec.rb +191 -0
- data/spec/integration/{integration_complex_spec.rb → complex_spec.rb} +9 -9
- data/spec/integration/connec_to_external_spec.rb +7 -3
- data/spec/integration/id_references_spec.rb +581 -0
- data/spec/integration/singleton_spec.rb +3 -3
- data/spec/models/complex_entity_spec.rb +42 -27
- data/spec/models/connec_helper_spec.rb +399 -31
- data/spec/models/entity_spec.rb +76 -21
- data/spec/models/external_spec.rb +4 -0
- data/spec/models/sub_entity_base_spec.rb +11 -4
- metadata +6 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 01e80e8ec8f1cf42c1b4dc6e1c55939d010bc8e3
|
4
|
+
data.tar.gz: ad2dc078eb9f605c2bd0a236690801577f38a43b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ef502e52b0d613ba02a37ec02db1c248142553c18178008a4cab46e472c86213eff01ba772db527b360b8f190a943df1e69b549a857a7480996db02b68bb7413
|
7
|
+
data.tar.gz: cc32cc071376de85c8e5269c1cbdd4dff33ca20049fedf8711bbd09c56f69b3f21a05d090206bb614a65b6e8a78fd067d6382ec46046a71e34e6727036cd20d9
|
data/.rubocop_todo.yml
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# This configuration was generated by
|
2
2
|
# `rubocop --auto-gen-config`
|
3
|
-
# on 2016-07-
|
3
|
+
# on 2016-07-26 04:25:37 +0100 using RuboCop version 0.41.1.
|
4
4
|
# The point is for the user to remove these configuration records
|
5
5
|
# one by one as the offenses are removed from the code base.
|
6
6
|
# Note that changes in the inspected code, or installation of new
|
@@ -19,7 +19,7 @@ Lint/UnusedBlockArgument:
|
|
19
19
|
Exclude:
|
20
20
|
- 'app/jobs/maestrano/connector/rails/synchronization_job.rb'
|
21
21
|
|
22
|
-
# Offense count:
|
22
|
+
# Offense count: 17
|
23
23
|
# Cop supports --auto-correct.
|
24
24
|
# Configuration parameters: AllowUnusedKeywordArguments, IgnoreEmptyMethods.
|
25
25
|
Lint/UnusedMethodArgument:
|
@@ -29,29 +29,23 @@ Lint/UnusedMethodArgument:
|
|
29
29
|
- 'app/models/maestrano/connector/rails/concerns/entity.rb'
|
30
30
|
- 'app/models/maestrano/connector/rails/concerns/external.rb'
|
31
31
|
|
32
|
-
# Offense count:
|
32
|
+
# Offense count: 23
|
33
33
|
Metrics/AbcSize:
|
34
34
|
Max: 70
|
35
35
|
|
36
|
-
# Offense count:
|
36
|
+
# Offense count: 10
|
37
37
|
Metrics/CyclomaticComplexity:
|
38
38
|
Max: 14
|
39
39
|
|
40
|
-
# Offense count:
|
41
|
-
# Configuration parameters: AllowHeredoc, AllowURI, URISchemes.
|
42
|
-
# URISchemes: http, https
|
43
|
-
Metrics/LineLength:
|
44
|
-
Max: 311
|
45
|
-
|
46
|
-
# Offense count: 16
|
40
|
+
# Offense count: 20
|
47
41
|
# Configuration parameters: CountComments.
|
48
42
|
Metrics/MethodLength:
|
49
43
|
Max: 42
|
50
44
|
|
51
|
-
# Offense count:
|
45
|
+
# Offense count: 2
|
52
46
|
# Configuration parameters: CountComments.
|
53
47
|
Metrics/ModuleLength:
|
54
|
-
Max:
|
48
|
+
Max: 263
|
55
49
|
|
56
50
|
# Offense count: 2
|
57
51
|
# Configuration parameters: CountKeywordArgs.
|
@@ -136,13 +130,6 @@ Style/PredicateName:
|
|
136
130
|
- 'app/helpers/maestrano/connector/rails/session_helper.rb'
|
137
131
|
- 'app/models/maestrano/connector/rails/concerns/entity.rb'
|
138
132
|
|
139
|
-
# Offense count: 1
|
140
|
-
# Cop supports --auto-correct.
|
141
|
-
# Configuration parameters: AllowMultipleReturnValues.
|
142
|
-
Style/RedundantReturn:
|
143
|
-
Exclude:
|
144
|
-
- 'app/jobs/maestrano/connector/rails/push_to_connec_worker.rb'
|
145
|
-
|
146
133
|
# Offense count: 1
|
147
134
|
# Cop supports --auto-correct.
|
148
135
|
Style/RedundantSelf:
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.
|
1
|
+
1.3.0
|
@@ -2,7 +2,7 @@ class Maestrano::SynchronizationsController < Maestrano::Rails::WebHookControlle
|
|
2
2
|
def show
|
3
3
|
uid = params[:id]
|
4
4
|
organization = Maestrano::Connector::Rails::Organization.find_by_uid(uid)
|
5
|
-
return render json: {errors: {message: "Organization not found", code: 404}}, status: :not_found unless organization
|
5
|
+
return render json: {errors: [{message: "Organization not found", code: 404}]}, status: :not_found unless organization
|
6
6
|
|
7
7
|
h = {
|
8
8
|
group_id: organization.uid,
|
@@ -25,7 +25,7 @@ class Maestrano::SynchronizationsController < Maestrano::Rails::WebHookControlle
|
|
25
25
|
uid = params[:group_id]
|
26
26
|
opts = params[:opts] || {}
|
27
27
|
organization = Maestrano::Connector::Rails::Organization.find_by_uid(uid)
|
28
|
-
return render json: {errors: {message: "Organization not found", code: 404}}, status: :not_found unless organization
|
28
|
+
return render json: {errors: [{message: "Organization not found", code: 404}]}, status: :not_found unless organization
|
29
29
|
|
30
30
|
Maestrano::Connector::Rails::SynchronizationJob.perform_later(organization, opts.with_indifferent_access)
|
31
31
|
head :created
|
@@ -34,7 +34,7 @@ class Maestrano::SynchronizationsController < Maestrano::Rails::WebHookControlle
|
|
34
34
|
def toggle_sync
|
35
35
|
uid = params[:group_id]
|
36
36
|
organization = Maestrano::Connector::Rails::Organization.find_by_uid(uid)
|
37
|
-
return render json: {errors: {message: "Organization not found", code: 404}}, status: :not_found unless organization
|
37
|
+
return render json: {errors: [{message: "Organization not found", code: 404}]}, status: :not_found unless organization
|
38
38
|
|
39
39
|
organization.toggle(:sync_enabled)
|
40
40
|
organization.save
|
@@ -14,6 +14,19 @@ module Maestrano::Connector::Rails::Concerns::ComplexEntity
|
|
14
14
|
raise 'Not implemented'
|
15
15
|
end
|
16
16
|
|
17
|
+
def formatted_external_entities_names
|
18
|
+
formatted_entities_names(external_entities_names)
|
19
|
+
end
|
20
|
+
|
21
|
+
def formatted_connec_entities_names
|
22
|
+
formatted_entities_names(connec_entities_names)
|
23
|
+
end
|
24
|
+
|
25
|
+
def formatted_entities_names(names)
|
26
|
+
return names.with_indifferent_access if names.is_a?(Hash)
|
27
|
+
names.index_by { |name| name }.with_indifferent_access
|
28
|
+
end
|
29
|
+
|
17
30
|
def count_entities(entities)
|
18
31
|
entities.values.map(&:size).max
|
19
32
|
end
|
@@ -59,54 +72,54 @@ module Maestrano::Connector::Rails::Concerns::ComplexEntity
|
|
59
72
|
def get_connec_entities(last_synchronization_date = nil)
|
60
73
|
entities = ActiveSupport::HashWithIndifferentAccess.new
|
61
74
|
|
62
|
-
self.class.
|
63
|
-
sub_entity_instance = instantiate_sub_entity_instance(
|
75
|
+
self.class.formatted_connec_entities_names.each do |connec_entity_name, connec_class_name|
|
76
|
+
sub_entity_instance = instantiate_sub_entity_instance(connec_class_name)
|
64
77
|
entities[connec_entity_name] = sub_entity_instance.get_connec_entities(last_synchronization_date)
|
65
78
|
end
|
66
79
|
entities
|
67
80
|
end
|
68
81
|
|
69
|
-
def get_external_entities_wrapper(last_synchronization_date = nil)
|
82
|
+
def get_external_entities_wrapper(last_synchronization_date = nil, entity_name = '')
|
70
83
|
entities = ActiveSupport::HashWithIndifferentAccess.new
|
71
84
|
|
72
|
-
self.class.
|
73
|
-
sub_entity_instance = instantiate_sub_entity_instance(
|
74
|
-
entities[external_entity_name] = sub_entity_instance.get_external_entities_wrapper(last_synchronization_date)
|
85
|
+
self.class.formatted_external_entities_names.each do |external_entity_name, external_class_name|
|
86
|
+
sub_entity_instance = instantiate_sub_entity_instance(external_class_name)
|
87
|
+
entities[external_entity_name] = sub_entity_instance.get_external_entities_wrapper(last_synchronization_date, external_entity_name)
|
75
88
|
end
|
76
89
|
entities
|
77
90
|
end
|
78
91
|
|
79
92
|
def consolidate_and_map_data(connec_entities, external_entities)
|
80
|
-
|
81
|
-
|
93
|
+
modelled_external_entities = external_model_to_connec_model(external_entities)
|
94
|
+
modelled_connec_entities = connec_model_to_external_model(connec_entities)
|
82
95
|
|
83
|
-
mapped_connec_entities = consolidate_and_map_connec_entities(
|
84
|
-
mapped_external_entities = consolidate_and_map_external_entities(
|
96
|
+
mapped_connec_entities = consolidate_and_map_connec_entities(modelled_connec_entities, modelled_external_entities)
|
97
|
+
mapped_external_entities = consolidate_and_map_external_entities(modelled_external_entities)
|
85
98
|
|
86
99
|
{connec_entities: mapped_connec_entities, external_entities: mapped_external_entities}
|
87
100
|
end
|
88
101
|
|
89
|
-
def consolidate_and_map_connec_entities(
|
90
|
-
|
102
|
+
def consolidate_and_map_connec_entities(modelled_connec_entities, modelled_external_entities)
|
103
|
+
modelled_connec_entities.each do |connec_entity_name, entities_in_external_model|
|
91
104
|
entities_in_external_model.each do |external_entity_name, entities|
|
92
|
-
sub_entity_instance = instantiate_sub_entity_instance(connec_entity_name)
|
93
|
-
equivalent_external_entities = (
|
105
|
+
sub_entity_instance = instantiate_sub_entity_instance(self.class.formatted_connec_entities_names[connec_entity_name])
|
106
|
+
equivalent_external_entities = (modelled_external_entities[external_entity_name] && modelled_external_entities[external_entity_name][connec_entity_name]) || []
|
94
107
|
|
95
108
|
entities_in_external_model[external_entity_name] = sub_entity_instance.consolidate_and_map_connec_entities(entities, equivalent_external_entities, sub_entity_instance.class.references[external_entity_name] || [], external_entity_name)
|
96
109
|
end
|
97
110
|
end
|
98
|
-
|
111
|
+
modelled_connec_entities
|
99
112
|
end
|
100
113
|
|
101
|
-
def consolidate_and_map_external_entities(
|
102
|
-
|
114
|
+
def consolidate_and_map_external_entities(modelled_external_entities)
|
115
|
+
modelled_external_entities.each do |external_entity_name, entities_in_connec_model|
|
103
116
|
entities_in_connec_model.each do |connec_entity_name, entities|
|
104
|
-
sub_entity_instance = instantiate_sub_entity_instance(external_entity_name)
|
117
|
+
sub_entity_instance = instantiate_sub_entity_instance(self.class.formatted_external_entities_names[external_entity_name])
|
105
118
|
|
106
119
|
entities_in_connec_model[connec_entity_name] = sub_entity_instance.consolidate_and_map_external_entities(entities, connec_entity_name)
|
107
120
|
end
|
108
121
|
end
|
109
|
-
|
122
|
+
modelled_external_entities
|
110
123
|
end
|
111
124
|
|
112
125
|
# input : {
|
@@ -120,7 +133,7 @@ module Maestrano::Connector::Rails::Concerns::ComplexEntity
|
|
120
133
|
# }
|
121
134
|
def push_entities_to_connec(mapped_external_entities_with_idmaps)
|
122
135
|
mapped_external_entities_with_idmaps.each do |external_entity_name, entities_in_connec_model|
|
123
|
-
sub_entity_instance = instantiate_sub_entity_instance(external_entity_name)
|
136
|
+
sub_entity_instance = instantiate_sub_entity_instance(self.class.formatted_external_entities_names[external_entity_name])
|
124
137
|
entities_in_connec_model.each do |connec_entity_name, mapped_entities_with_idmaps|
|
125
138
|
sub_entity_instance.push_entities_to_connec_to(mapped_entities_with_idmaps, connec_entity_name)
|
126
139
|
end
|
@@ -129,7 +142,7 @@ module Maestrano::Connector::Rails::Concerns::ComplexEntity
|
|
129
142
|
|
130
143
|
def push_entities_to_external(mapped_connec_entities_with_idmaps)
|
131
144
|
mapped_connec_entities_with_idmaps.each do |connec_entity_name, entities_in_external_model|
|
132
|
-
sub_entity_instance = instantiate_sub_entity_instance(connec_entity_name)
|
145
|
+
sub_entity_instance = instantiate_sub_entity_instance(self.class.formatted_connec_entities_names[connec_entity_name])
|
133
146
|
entities_in_external_model.each do |external_entity_name, mapped_entities_with_idmaps|
|
134
147
|
sub_entity_instance.push_entities_to_external_to(mapped_entities_with_idmaps, external_entity_name)
|
135
148
|
end
|
@@ -137,7 +150,7 @@ module Maestrano::Connector::Rails::Concerns::ComplexEntity
|
|
137
150
|
end
|
138
151
|
|
139
152
|
def instantiate_sub_entity_instance(entity_name)
|
140
|
-
|
153
|
+
self.class.instantiate_sub_entity_instance(entity_name, @organization, @connec_client, @external_client, @opts)
|
141
154
|
end
|
142
155
|
|
143
156
|
# -------------------------------------------------------------
|
@@ -154,5 +167,20 @@ module Maestrano::Connector::Rails::Concerns::ComplexEntity
|
|
154
167
|
def build_hash_with_entities(entities_name, entity_name, proc, entities)
|
155
168
|
Hash[*entities_name.collect { |name| proc.call(name) == entity_name ? [name, entities] : [name, []] }.flatten(1)]
|
156
169
|
end
|
170
|
+
|
171
|
+
def instantiate_sub_entity_instance(entity_name, organization, connec_client, external_client, opts)
|
172
|
+
"Entities::SubEntities::#{entity_name.titleize.split.join}".constantize.new(organization, connec_client, external_client, opts)
|
173
|
+
end
|
174
|
+
|
175
|
+
def find_complex_entity_and_instantiate_external_sub_entity_instance(entity_name, organization, connec_client, external_client, opts)
|
176
|
+
Maestrano::Connector::Rails::External.entities_list.each do |entity_name_from_list|
|
177
|
+
clazz = "Entities::#{entity_name_from_list.singularize.titleize.split.join}".constantize
|
178
|
+
if clazz.methods.include?('external_entities_names'.to_sym)
|
179
|
+
formatted_names = clazz.formatted_external_entities_names
|
180
|
+
return instantiate_sub_entity_instance(formatted_names[entity_name], organization, connec_client, external_client, opts) if formatted_names[entity_name]
|
181
|
+
end
|
182
|
+
end
|
183
|
+
nil
|
184
|
+
end
|
157
185
|
end
|
158
186
|
end
|
@@ -17,6 +17,8 @@ module Maestrano::Connector::Rails::Concerns::ConnecHelper
|
|
17
17
|
client
|
18
18
|
end
|
19
19
|
|
20
|
+
# Returns a string of the tenant's current connec version.
|
21
|
+
# Can use Gem::Version for version comparison
|
20
22
|
def connec_version(organization)
|
21
23
|
@@connec_version = Rails.cache.fetch("connec_version_#{organization.tenant}", namespace: 'maestrano', expires_in: 1.day) do
|
22
24
|
response = get_client(organization).class.get("#{Maestrano[organization.tenant].param('connec.host')}/version")
|
@@ -26,32 +28,52 @@ module Maestrano::Connector::Rails::Concerns::ConnecHelper
|
|
26
28
|
@@connec_version
|
27
29
|
end
|
28
30
|
|
29
|
-
#
|
30
|
-
#
|
31
|
+
# Replaces the arrays of id received from Connec! by the id of the external application
|
32
|
+
# Returns a hash {entity: {}, connec_id: '', id_refs_only_connec_entity: {}}
|
33
|
+
# If an array has no id for this oauth_provider and oauth_uid but has one for connec, it returns a nil entity (skip the record)
|
31
34
|
def unfold_references(connec_entity, references, organization)
|
32
|
-
|
35
|
+
references = format_references(references)
|
36
|
+
unfolded_connec_entity = connec_entity.deep_dup.with_indifferent_access
|
33
37
|
not_nil = true
|
34
38
|
|
35
39
|
# Id
|
36
40
|
id_hash = unfolded_connec_entity['id'].find { |id| id['provider'] == organization.oauth_provider && id['realm'] == organization.oauth_uid }
|
37
|
-
|
41
|
+
connec_id = unfolded_connec_entity['id'].find { |id| id['provider'] == 'connec' }['id']
|
38
42
|
unfolded_connec_entity['id'] = id_hash ? id_hash['id'] : nil
|
39
43
|
|
40
|
-
# Other
|
41
|
-
references
|
42
|
-
|
44
|
+
# Other references
|
45
|
+
# Record references are references to other records (organization_id, item_id, ...)
|
46
|
+
references[:record_references].each do |reference|
|
47
|
+
not_nil &= unfold_references_helper(unfolded_connec_entity, reference.split('/'), organization)
|
43
48
|
end
|
44
|
-
|
49
|
+
# Id references are references to sub entities ids (invoice lines id, ...)
|
50
|
+
# We do not return nil if we're missing an id reference
|
51
|
+
references[:id_references].each do |reference|
|
52
|
+
unfold_references_helper(unfolded_connec_entity, reference.split('/'), organization)
|
53
|
+
end
|
54
|
+
unfolded_connec_entity = not_nil ? unfolded_connec_entity : nil
|
55
|
+
|
56
|
+
# Filter the connec entity to keep only the id_references fields (in order to save some memory)
|
57
|
+
# Give an empty hash if there's nothing left
|
58
|
+
id_refs_only_connec_entity = filter_connec_entity_for_id_refs(connec_entity, references[:id_references])
|
59
|
+
|
60
|
+
{entity: unfolded_connec_entity, connec_id: connec_id, id_refs_only_connec_entity: id_refs_only_connec_entity}
|
45
61
|
end
|
46
62
|
|
63
|
+
# Replaces ids from the external application by arrays containing them
|
47
64
|
def fold_references(mapped_external_entity, references, organization)
|
65
|
+
references = format_references(references)
|
48
66
|
mapped_external_entity = mapped_external_entity.with_indifferent_access
|
49
|
-
|
67
|
+
|
68
|
+
# Use both record_references and id_references + the id
|
69
|
+
(references.values.flatten + ['id']).each do |reference|
|
50
70
|
fold_references_helper(mapped_external_entity, reference.split('/'), organization)
|
51
71
|
end
|
72
|
+
|
52
73
|
mapped_external_entity
|
53
74
|
end
|
54
75
|
|
76
|
+
# Builds an id_hash from the id and organization
|
55
77
|
def id_hash(id, organization)
|
56
78
|
{
|
57
79
|
id: id,
|
@@ -60,6 +82,7 @@ module Maestrano::Connector::Rails::Concerns::ConnecHelper
|
|
60
82
|
}
|
61
83
|
end
|
62
84
|
|
85
|
+
# Recursive method for folding references
|
63
86
|
def fold_references_helper(entity, array_of_refs, organization)
|
64
87
|
ref = array_of_refs.shift
|
65
88
|
field = entity[ref]
|
@@ -71,7 +94,7 @@ module Maestrano::Connector::Rails::Concerns::ConnecHelper
|
|
71
94
|
field.each do |f|
|
72
95
|
fold_references_helper(f, array_of_refs.dup, organization)
|
73
96
|
end
|
74
|
-
when
|
97
|
+
when Hash
|
75
98
|
fold_references_helper(entity[ref], array_of_refs, organization)
|
76
99
|
else
|
77
100
|
id = field
|
@@ -79,6 +102,7 @@ module Maestrano::Connector::Rails::Concerns::ConnecHelper
|
|
79
102
|
end
|
80
103
|
end
|
81
104
|
|
105
|
+
# Recursive method for unfolding references
|
82
106
|
def unfold_references_helper(entity, array_of_refs, organization)
|
83
107
|
ref = array_of_refs.shift
|
84
108
|
field = entity[ref]
|
@@ -93,23 +117,137 @@ module Maestrano::Connector::Rails::Concerns::ConnecHelper
|
|
93
117
|
# We may enqueue a fetch on the endpoint of the missing association, followed by a re-fetch on this one.
|
94
118
|
# However it's expected to be an edge case, so for now we rely on the fact that the webhooks should be relativly in order.
|
95
119
|
# Worst case it'll be done on following sync
|
120
|
+
entity.delete(ref)
|
96
121
|
return nil
|
97
122
|
end
|
123
|
+
true
|
98
124
|
|
99
125
|
# Follow embedment path
|
100
126
|
else
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
127
|
+
return true if field.blank?
|
128
|
+
case field
|
129
|
+
when Array
|
130
|
+
bool = true
|
131
|
+
field.each do |f|
|
132
|
+
bool &= unfold_references_helper(f, array_of_refs.dup, organization)
|
133
|
+
end
|
134
|
+
bool
|
135
|
+
when Hash
|
136
|
+
unfold_references_helper(entity[ref], array_of_refs, organization)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
# Transforms the references into an hash {record_references: [], id_references: []}
|
142
|
+
# References can either be an array (only record references), or a hash
|
143
|
+
def format_references(references)
|
144
|
+
return {record_references: references, id_references: []} if references.is_a?(Array)
|
145
|
+
references[:record_references] ||= []
|
146
|
+
references[:id_references] ||= []
|
147
|
+
references
|
148
|
+
end
|
149
|
+
|
150
|
+
# Returns the connec_entity without all the fields that are not id_references
|
151
|
+
def filter_connec_entity_for_id_refs(connec_entity, id_references)
|
152
|
+
return {} if id_references.empty?
|
153
|
+
|
154
|
+
entity = connec_entity.dup.with_indifferent_access
|
155
|
+
tree = build_id_references_tree(id_references)
|
156
|
+
|
157
|
+
filter_connec_entity_for_id_refs_helper(entity, tree)
|
158
|
+
|
159
|
+
# TODO, improve performance by returning an empty hash if all the id_references have their id in the connec hash
|
160
|
+
# We should still return all of them if at least one is missing as we are relying on the id
|
161
|
+
entity
|
162
|
+
end
|
163
|
+
|
164
|
+
# Recursive method for filtering connec entities
|
165
|
+
def filter_connec_entity_for_id_refs_helper(entity_hash, tree)
|
166
|
+
return if tree.empty?
|
167
|
+
entity_hash.slice!(*tree.keys)
|
168
|
+
|
169
|
+
tree.each do |key, children|
|
170
|
+
case entity_hash[key]
|
171
|
+
when Array
|
172
|
+
entity_hash[key].each do |hash|
|
173
|
+
filter_connec_entity_for_id_refs_helper(hash, children)
|
174
|
+
end
|
175
|
+
when Hash
|
176
|
+
filter_connec_entity_for_id_refs_helper(entity_hash[key], children)
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
# Builds a tree from an array of id_references
|
182
|
+
# input: %w(lines/id lines/linked/id linked/id)
|
183
|
+
# output: {"lines"=>{"id"=>{}, "linked"=>{"id"=>{}}}, "linked"=>{"id"=>{}}}
|
184
|
+
def build_id_references_tree(id_references)
|
185
|
+
tree = {}
|
186
|
+
|
187
|
+
id_references.each do |id_reference|
|
188
|
+
array_of_refs = id_reference.split('/')
|
189
|
+
|
190
|
+
t = tree
|
191
|
+
array_of_refs.each do |ref|
|
192
|
+
t[ref] ||= {}
|
193
|
+
t = t[ref]
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
tree
|
198
|
+
end
|
199
|
+
|
200
|
+
# Merges the id arrays from two hashes while keeping only the id_references fields
|
201
|
+
def merge_id_hashes(dist, src, id_references)
|
202
|
+
dist = dist.with_indifferent_access
|
203
|
+
src = src.with_indifferent_access
|
204
|
+
|
205
|
+
id_references.each do |id_reference|
|
206
|
+
array_of_refs = id_reference.split('/')
|
207
|
+
|
208
|
+
merge_id_hashes_helper(dist, array_of_refs, src)
|
209
|
+
end
|
210
|
+
|
211
|
+
dist
|
212
|
+
end
|
213
|
+
|
214
|
+
# Recursive helper for merging id hashes
|
215
|
+
def merge_id_hashes_helper(hash, array_of_refs, src, path = [])
|
216
|
+
ref = array_of_refs.shift
|
217
|
+
field = hash[ref]
|
218
|
+
|
219
|
+
if array_of_refs.empty? && field
|
220
|
+
value = value_from_hash(src, path + [ref])
|
221
|
+
if value.is_a?(Array)
|
222
|
+
hash[ref] = (field + value).uniq
|
223
|
+
else
|
224
|
+
hash.delete(ref)
|
225
|
+
end
|
226
|
+
else
|
227
|
+
case field
|
228
|
+
when Array
|
229
|
+
field.each_with_index do |f, index|
|
230
|
+
merge_id_hashes_helper(f, array_of_refs.dup, src, path + [ref, index])
|
109
231
|
end
|
232
|
+
when Hash
|
233
|
+
merge_id_hashes_helper(field, array_of_refs, src, path + [ref])
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
# Returns the value from a hash following the given path
|
239
|
+
# Path sould be an array like [:lines, 0, :id]
|
240
|
+
def value_from_hash(hash, path)
|
241
|
+
value = hash
|
242
|
+
|
243
|
+
begin
|
244
|
+
path.each do |p|
|
245
|
+
value = value[p]
|
110
246
|
end
|
247
|
+
value
|
248
|
+
rescue NoMethodError
|
249
|
+
nil
|
111
250
|
end
|
112
|
-
true
|
113
251
|
end
|
114
252
|
end
|
115
253
|
end
|