maestrano-connector-rails 0.4.4 → 1.0.0
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/Gemfile +2 -0
- data/README.md +1 -1
- data/VERSION +1 -1
- data/app/controllers/maestrano/connec_controller.rb +17 -19
- data/app/jobs/maestrano/connector/rails/all_synchronizations_job.rb +1 -1
- data/app/jobs/maestrano/connector/rails/push_to_connec_job.rb +11 -11
- data/app/jobs/maestrano/connector/rails/synchronization_job.rb +20 -11
- data/app/models/maestrano/connector/rails/concerns/complex_entity.rb +66 -74
- data/app/models/maestrano/connector/rails/concerns/connec_helper.rb +102 -0
- data/app/models/maestrano/connector/rails/concerns/entity.rb +233 -231
- data/app/models/maestrano/connector/rails/concerns/external.rb +7 -0
- data/app/models/maestrano/connector/rails/concerns/sub_entity_base.rb +14 -43
- data/app/models/maestrano/connector/rails/connec_helper.rb +5 -0
- data/app/models/maestrano/connector/rails/organization.rb +8 -2
- data/db/20160524112054_add_encryption_on_oauth_keys.rb +8 -0
- data/lib/generators/connector/install_generator.rb +1 -0
- data/lib/generators/connector/templates/complex_entity_example/contact.rb +1 -1
- data/lib/generators/connector/templates/complex_entity_example/contact_and_lead.rb +2 -2
- data/lib/generators/connector/templates/entity.rb +13 -19
- data/lib/generators/connector/templates/example_entity.rb +2 -2
- data/lib/generators/connector/templates/example_entity_spec.rb +73 -0
- data/lib/generators/connector/templates/external.rb +11 -0
- data/maestrano-connector-rails.gemspec +13 -8
- data/release_notes.md +81 -0
- data/spec/controllers/connec_controller_spec.rb +19 -6
- data/spec/dummy/app/models/maestrano/connector/rails/entity.rb +0 -7
- data/spec/dummy/app/models/maestrano/connector/rails/external.rb +7 -0
- data/spec/dummy/app/views/home/index.html.erb +1 -36
- data/spec/factories.rb +3 -0
- data/spec/integration/connec_to_external_spec.rb +188 -0
- data/spec/integration/external_to_connec_spec.rb +155 -0
- data/spec/integration/integration_complex_spec.rb +281 -0
- data/spec/integration/singleton_spec.rb +288 -0
- data/spec/jobs/all_synchronizations_job_spec.rb +5 -0
- data/spec/jobs/push_to_connec_job_spec.rb +3 -6
- data/spec/jobs/synchronization_job_spec.rb +29 -17
- data/spec/models/complex_entity_spec.rb +257 -412
- data/spec/models/connec_helper_spec.rb +143 -0
- data/spec/models/entity_spec.rb +420 -348
- data/spec/models/external_spec.rb +4 -0
- data/spec/models/organization_spec.rb +2 -1
- data/spec/models/sub_entity_base_spec.rb +28 -69
- data/template/factories.rb +3 -1
- data/template/maestrano-connector-template.rb +11 -13
- data/template/maestrano.rb +2 -1
- data/template/settings/development.yml +4 -2
- data/template/settings/production.yml +1 -11
- data/template/settings/settings.yml +8 -0
- data/template/settings/test.yml +2 -0
- data/template/settings/uat.yml +1 -9
- metadata +12 -7
- data/Gemfile.lock +0 -256
- data/realse_notes.md +0 -16
- data/spec/dummy/app/views/admin/index.html.erb +0 -51
- data/spec/dummy/db/development.sqlite3 +0 -0
- data/spec/dummy/db/test.sqlite3 +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2ee22c8ca44f84ce0e4a717c80892f1765af5987
|
4
|
+
data.tar.gz: 65e99c4163462829142e439f8323e8e2cd6f6306
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5a480181d0599bc16c945364ff6949c684eaa431f89b1c5af2f41f7f8999f451ab48a470abe680a1d854f57284fa6831bd73b3ecaf0b503a2053b34674f3a4b9
|
7
|
+
data.tar.gz: 00ec463ca23d1994baaf7873ce4a26c8c3cc76c72809e5a1b06cba7ca54899f67ed5dcc92c97f019ac4fbf6363d42862edf770c579245fb42ce30ae488fbdcc5
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -10,4 +10,4 @@
|
|
10
10
|
Maestrano Connector is a Rails Engine that bootstraps the implementation of data syncrhonization between an external application API and the Maestrano ecosystem.
|
11
11
|
Maestrano Connector Integration is currently in closed beta. Want to know more? Send us an email to <contact@maestrano.com>.
|
12
12
|
|
13
|
-
You can find the documentation [here](https://maestrano.atlassian.net/wiki/display/
|
13
|
+
You can find the documentation [here](https://maestrano.atlassian.net/wiki/display/DEV/Create+a+connector+using+our+framework+-+Ruby+on+Rails). See also some generic documentation about integration with Maestrano [here](https://maestrano.atlassian.net/wiki/display/DEV/Maestrano+Developers).
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
1.0.0
|
@@ -6,36 +6,34 @@ class Maestrano::ConnecController < Maestrano::Rails::WebHookController
|
|
6
6
|
begin
|
7
7
|
params.except(:tenant, :controller, :action, :connec).each do |entity_name, entities|
|
8
8
|
|
9
|
-
|
10
|
-
next Rails.logger.info "Received notification from Connec! for unknow entity: #{entity_name}" unless
|
11
|
-
|
12
|
-
entity_instance = entity_instance_hash[:instance]
|
9
|
+
entity_class_hash = find_entity_class(entity_name)
|
10
|
+
next Rails.logger.info "Received notification from Connec! for unknow entity: #{entity_name}" unless entity_class_hash
|
13
11
|
|
14
12
|
entities.each do |entity|
|
15
13
|
organization = Maestrano::Connector::Rails::Organization.find_by_uid_and_tenant(entity[:group_id], params[:tenant])
|
16
14
|
next Rails.logger.warn "Received notification from Connec! for unknown group or group without oauth: #{entity['group_id']} (tenant: #{params[:tenant]})" unless organization && organization.oauth_uid
|
17
|
-
next unless organization.sync_enabled && organization.synchronized_entities[
|
15
|
+
next unless organization.sync_enabled && organization.synchronized_entities[entity_class_hash[:name].to_sym]
|
18
16
|
|
19
17
|
|
20
18
|
Maestrano::Connector::Rails::ConnectorLogger.log('info', organization, "Received entity from Connec! webhook: Entity=#{entity_name}, Data=#{entity}")
|
21
|
-
connec_client = Maestrano::
|
19
|
+
connec_client = Maestrano::Connector::Rails::ConnecHelper.get_client(organization)
|
22
20
|
external_client = Maestrano::Connector::Rails::External.get_client(organization)
|
23
21
|
last_synchronization = organization.last_successful_synchronization
|
24
22
|
|
25
|
-
entity_instance.
|
23
|
+
entity_instance = entity_class_hash[:class].new(organization, connec_client, external_client, {})
|
24
|
+
entity_instance.before_sync(last_synchronization)
|
26
25
|
|
27
|
-
|
28
26
|
# Build expected input for consolidate_and_map_data
|
29
|
-
if
|
30
|
-
filtered_entities = entity_instance.filter_connec_entities(
|
31
|
-
mapped_entity = entity_instance.consolidate_and_map_data(filtered_entities,
|
27
|
+
if entity_class_hash[:is_complex]
|
28
|
+
filtered_entities = entity_instance.filter_connec_entities(Maestrano::Connector::Rails::ComplexEntity.build_hash_with_entities(entity_instance.class.connec_entities_names, entity_name, lambda{|name| name.parameterize('_').pluralize}, [entity]))
|
29
|
+
mapped_entity = entity_instance.consolidate_and_map_data(filtered_entities, Maestrano::Connector::Rails::ComplexEntity.build_empty_hash(entity_instance.class.external_entities_names))
|
32
30
|
else
|
33
|
-
filtered_entities = entity_instance.filter_connec_entities([entity]
|
34
|
-
mapped_entity = entity_instance.consolidate_and_map_data(filtered_entities, []
|
31
|
+
filtered_entities = entity_instance.filter_connec_entities([entity])
|
32
|
+
mapped_entity = entity_instance.consolidate_and_map_data(filtered_entities, [])
|
35
33
|
end
|
36
|
-
entity_instance.push_entities_to_external(
|
34
|
+
entity_instance.push_entities_to_external(mapped_entity[:connec_entities])
|
37
35
|
|
38
|
-
entity_instance.after_sync(
|
36
|
+
entity_instance.after_sync(last_synchronization)
|
39
37
|
end
|
40
38
|
end
|
41
39
|
rescue => e
|
@@ -47,13 +45,13 @@ class Maestrano::ConnecController < Maestrano::Rails::WebHookController
|
|
47
45
|
|
48
46
|
|
49
47
|
private
|
50
|
-
def
|
51
|
-
Maestrano::Connector::Rails::
|
48
|
+
def find_entity_class(entity_name)
|
49
|
+
Maestrano::Connector::Rails::External.entities_list.each do |entity_name_from_list|
|
52
50
|
clazz = "Entities::#{entity_name_from_list.singularize.titleize.split.join}".constantize
|
53
51
|
if clazz.methods.include?('connec_entities_names'.to_sym)
|
54
|
-
return {
|
52
|
+
return {class: clazz, is_complex: true, name: entity_name_from_list} if clazz.connec_entities_names.map{|n| n.parameterize('_').pluralize}.include?(entity_name)
|
55
53
|
elsif clazz.methods.include?('connec_entity_name'.to_sym)
|
56
|
-
return {
|
54
|
+
return {class: clazz, is_complex: false, name: entity_name_from_list} if clazz.normalized_connec_entity_name == entity_name
|
57
55
|
end
|
58
56
|
end
|
59
57
|
nil
|
@@ -4,7 +4,7 @@ module Maestrano::Connector::Rails
|
|
4
4
|
|
5
5
|
# Trigger synchronization of all active organizations
|
6
6
|
def perform(name=nil, count=nil)
|
7
|
-
Maestrano::Connector::Rails::Organization.where(
|
7
|
+
Maestrano::Connector::Rails::Organization.where('oauth_provider IS NOT NULL AND oauth_token IS NOT NULL').each do |o|
|
8
8
|
next unless [true, 1].include?(o.sync_enabled)
|
9
9
|
Maestrano::Connector::Rails::SynchronizationJob.perform_later(o, {})
|
10
10
|
end
|
@@ -6,26 +6,26 @@ module Maestrano::Connector::Rails
|
|
6
6
|
def perform(organization, entities_hash, opts={})
|
7
7
|
return unless organization.sync_enabled && organization.oauth_uid
|
8
8
|
|
9
|
-
connec_client = Maestrano::
|
9
|
+
connec_client = Maestrano::Connector::Rails::ConnecHelper.get_client(organization)
|
10
10
|
external_client = Maestrano::Connector::Rails::External.get_client(organization)
|
11
11
|
last_synchronization = organization.last_successful_synchronization
|
12
12
|
|
13
13
|
entities_hash.each do |external_entity_name, entities|
|
14
|
-
if entity_instance_hash = find_entity_instance(external_entity_name)
|
14
|
+
if entity_instance_hash = find_entity_instance(external_entity_name, organization, connec_client, external_client, opts)
|
15
15
|
next unless organization.synchronized_entities[entity_instance_hash[:name].to_sym]
|
16
16
|
|
17
17
|
entity_instance = entity_instance_hash[:instance]
|
18
18
|
|
19
|
-
entity_instance.before_sync(
|
19
|
+
entity_instance.before_sync(last_synchronization)
|
20
20
|
# Build expected input for consolidate_and_map_data
|
21
21
|
if entity_instance_hash[:is_complex]
|
22
|
-
mapped_entities = entity_instance.consolidate_and_map_data(
|
22
|
+
mapped_entities = entity_instance.consolidate_and_map_data(ComplexEntity.build_empty_hash(entity_instance.class.connec_entities_names), ComplexEntity.build_hash_with_entities(entity_instance.class.external_entities_names, external_entity_name, lambda{|name| name}, entities))
|
23
23
|
else
|
24
|
-
mapped_entities = entity_instance.consolidate_and_map_data([], entities
|
24
|
+
mapped_entities = entity_instance.consolidate_and_map_data([], entities)
|
25
25
|
end
|
26
|
-
entity_instance.push_entities_to_connec(
|
26
|
+
entity_instance.push_entities_to_connec(mapped_entities[:external_entities])
|
27
27
|
|
28
|
-
entity_instance.after_sync(
|
28
|
+
entity_instance.after_sync(last_synchronization)
|
29
29
|
else
|
30
30
|
Rails.logger.warn "Called push to connec job with unknow entity: #{external_entity_name}"
|
31
31
|
end
|
@@ -33,13 +33,13 @@ module Maestrano::Connector::Rails
|
|
33
33
|
end
|
34
34
|
|
35
35
|
private
|
36
|
-
def find_entity_instance(entity_name)
|
37
|
-
Maestrano::Connector::Rails::
|
36
|
+
def find_entity_instance(entity_name, organization, connec_client, external_client, opts)
|
37
|
+
Maestrano::Connector::Rails::External.entities_list.each do |entity_name_from_list|
|
38
38
|
clazz = "Entities::#{entity_name_from_list.singularize.titleize.split.join}".constantize
|
39
39
|
if clazz.methods.include?('external_entities_names'.to_sym)
|
40
|
-
return {instance: clazz.new, is_complex: true, name: entity_name_from_list} if clazz.external_entities_names.include?(entity_name)
|
40
|
+
return {instance: clazz.new(organization, connec_client, external_client, opts), is_complex: true, name: entity_name_from_list} if clazz.external_entities_names.include?(entity_name)
|
41
41
|
elsif clazz.methods.include?('external_entity_name'.to_sym)
|
42
|
-
return {instance: clazz.new, is_complex: false, name: entity_name_from_list} if clazz.external_entity_name == entity_name
|
42
|
+
return {instance: clazz.new(organization, connec_client, external_client, opts), is_complex: false, name: entity_name_from_list} if clazz.external_entity_name == entity_name
|
43
43
|
end
|
44
44
|
end
|
45
45
|
nil
|
@@ -7,7 +7,7 @@ module Maestrano::Connector::Rails
|
|
7
7
|
# * :only_entities => [person, tasks_list]
|
8
8
|
# * :full_sync => true synchronization is performed without date filtering
|
9
9
|
# * :connec_preemption => true|false : preemption is always|never given to connec in case of conflict (if not set, the most recently updated entity is kept)
|
10
|
-
def perform(organization, opts)
|
10
|
+
def perform(organization, opts={})
|
11
11
|
return unless organization.sync_enabled
|
12
12
|
|
13
13
|
# Check if previous synchronization is still running
|
@@ -29,10 +29,19 @@ module Maestrano::Connector::Rails
|
|
29
29
|
|
30
30
|
begin
|
31
31
|
last_synchronization = organization.last_successful_synchronization
|
32
|
-
connec_client =
|
32
|
+
connec_client = ConnecHelper.get_client(organization)
|
33
33
|
external_client = External.get_client(organization)
|
34
34
|
|
35
|
-
|
35
|
+
# First synchronization should be from external to Connec! only to let the smart merging works
|
36
|
+
# We do a doube sync: only from external, then only from connec!
|
37
|
+
if last_synchronization.nil?
|
38
|
+
ConnectorLogger.log('info', organization, "First synchronization ever. Doing two half syncs to allow smart merging to work its magic.")
|
39
|
+
[{skip_connec: true}, {skip_external: true}].each do |opt|
|
40
|
+
organization.synchronized_entities.select{|k, v| v}.keys.each do |entity|
|
41
|
+
sync_entity(entity.to_s, organization, connec_client, external_client, last_synchronization, opts.merge(opt))
|
42
|
+
end
|
43
|
+
end
|
44
|
+
elsif opts[:only_entities]
|
36
45
|
ConnectorLogger.log('info', organization, "Synchronization is partial and will synchronize only #{opts[:only_entities].join(' ')}")
|
37
46
|
# The synchronization is marked as partial and will not be considered as the last-synchronization for the next sync
|
38
47
|
current_synchronization.set_partial
|
@@ -54,15 +63,15 @@ module Maestrano::Connector::Rails
|
|
54
63
|
end
|
55
64
|
|
56
65
|
def sync_entity(entity_name, organization, connec_client, external_client, last_synchronization, opts)
|
57
|
-
entity_instance = "Entities::#{entity_name.titleize.split.join}".constantize.new
|
66
|
+
entity_instance = "Entities::#{entity_name.titleize.split.join}".constantize.new(organization, connec_client, external_client, opts)
|
58
67
|
|
59
|
-
entity_instance.before_sync(
|
60
|
-
external_entities = entity_instance.
|
61
|
-
connec_entities = entity_instance.get_connec_entities(
|
62
|
-
mapped_entities = entity_instance.consolidate_and_map_data(connec_entities, external_entities
|
63
|
-
entity_instance.push_entities_to_external(
|
64
|
-
entity_instance.push_entities_to_connec(
|
65
|
-
entity_instance.after_sync(
|
68
|
+
entity_instance.before_sync(last_synchronization)
|
69
|
+
external_entities = entity_instance.get_external_entities_wrapper(last_synchronization)
|
70
|
+
connec_entities = entity_instance.get_connec_entities(last_synchronization)
|
71
|
+
mapped_entities = entity_instance.consolidate_and_map_data(connec_entities, external_entities)
|
72
|
+
entity_instance.push_entities_to_external(mapped_entities[:connec_entities])
|
73
|
+
entity_instance.push_entities_to_connec(mapped_entities[:external_entities])
|
74
|
+
entity_instance.after_sync(last_synchronization)
|
66
75
|
end
|
67
76
|
end
|
68
77
|
end
|
@@ -1,6 +1,13 @@
|
|
1
1
|
module Maestrano::Connector::Rails::Concerns::ComplexEntity
|
2
2
|
extend ActiveSupport::Concern
|
3
3
|
|
4
|
+
def initialize(organization, connec_client, external_client, opts={})
|
5
|
+
@organization = organization
|
6
|
+
@connec_client = connec_client
|
7
|
+
@external_client = external_client
|
8
|
+
@opts = opts
|
9
|
+
end
|
10
|
+
|
4
11
|
# -------------------------------------------------------------
|
5
12
|
# Complex specific methods
|
6
13
|
# Those methods needs to be implemented in each complex entity
|
@@ -28,7 +35,7 @@ module Maestrano::Connector::Rails::Concerns::ComplexEntity
|
|
28
35
|
# external_entities_names[1]: [unmapped_connec_entitiy4]
|
29
36
|
# }
|
30
37
|
# }
|
31
|
-
def connec_model_to_external_model(connec_hash_of_entities
|
38
|
+
def connec_model_to_external_model(connec_hash_of_entities)
|
32
39
|
raise "Not implemented"
|
33
40
|
end
|
34
41
|
|
@@ -45,100 +52,65 @@ module Maestrano::Connector::Rails::Concerns::ComplexEntity
|
|
45
52
|
# connec_entities_names[0]: [unmapped_external_entity3, unmapped_external_entity4]
|
46
53
|
# }
|
47
54
|
# }
|
48
|
-
def external_model_to_connec_model(external_hash_of_entities
|
55
|
+
def external_model_to_connec_model(external_hash_of_entities)
|
49
56
|
raise "Not implemented"
|
50
57
|
end
|
51
58
|
|
52
|
-
# -------------------------------------------------------------
|
53
|
-
# General methods
|
54
|
-
# -------------------------------------------------------------
|
55
|
-
def map_to_external_with_idmap(entity, organization, external_entity_name, sub_entity_instance)
|
56
|
-
idmap = sub_entity_instance.class.find_idmap({connec_id: entity['id'], external_entity: external_entity_name.downcase, organization_id: organization.id})
|
57
|
-
|
58
|
-
if idmap
|
59
|
-
return nil if idmap.external_inactive || !idmap.to_external
|
60
|
-
|
61
|
-
if idmap.last_push_to_external && idmap.last_push_to_external > entity['updated_at']
|
62
|
-
Maestrano::Connector::Rails::ConnectorLogger.log('info', organization, "Discard Connec! #{sub_entity_instance.class.entity_name} : #{entity}")
|
63
|
-
nil
|
64
|
-
else
|
65
|
-
idmap.update(name: sub_entity_instance.class.object_name_from_connec_entity_hash(entity))
|
66
|
-
{entity: sub_entity_instance.map_to(external_entity_name, entity, organization), idmap: idmap}
|
67
|
-
end
|
68
|
-
else
|
69
|
-
{entity: sub_entity_instance.map_to(external_entity_name, entity, organization), idmap: sub_entity_instance.class.create_idmap_from_connec_entity(entity, external_entity_name, organization)}
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
59
|
# -------------------------------------------------------------
|
74
60
|
# Entity equivalent methods
|
75
61
|
# -------------------------------------------------------------
|
76
|
-
def get_connec_entities(
|
62
|
+
def get_connec_entities(last_synchronization)
|
77
63
|
entities = ActiveSupport::HashWithIndifferentAccess.new
|
78
64
|
|
79
65
|
self.class.connec_entities_names.each do |connec_entity_name|
|
80
|
-
sub_entity_instance =
|
81
|
-
entities[connec_entity_name] = sub_entity_instance.get_connec_entities(
|
66
|
+
sub_entity_instance = instantiate_sub_entity_instance(connec_entity_name)
|
67
|
+
entities[connec_entity_name] = sub_entity_instance.get_connec_entities(last_synchronization)
|
82
68
|
end
|
83
69
|
entities
|
84
70
|
end
|
85
71
|
|
86
|
-
def
|
72
|
+
def get_external_entities_wrapper(last_synchronization)
|
87
73
|
entities = ActiveSupport::HashWithIndifferentAccess.new
|
88
74
|
|
89
75
|
self.class.external_entities_names.each do |external_entity_name|
|
90
|
-
sub_entity_instance =
|
91
|
-
entities[external_entity_name] = sub_entity_instance.
|
76
|
+
sub_entity_instance = instantiate_sub_entity_instance(external_entity_name)
|
77
|
+
entities[external_entity_name] = sub_entity_instance.get_external_entities_wrapper(last_synchronization)
|
92
78
|
end
|
93
79
|
entities
|
94
80
|
end
|
95
81
|
|
96
|
-
def consolidate_and_map_data(connec_entities, external_entities
|
97
|
-
modeled_external_entities = external_model_to_connec_model(external_entities
|
98
|
-
modeled_connec_entities = connec_model_to_external_model(connec_entities
|
82
|
+
def consolidate_and_map_data(connec_entities, external_entities)
|
83
|
+
modeled_external_entities = external_model_to_connec_model(external_entities)
|
84
|
+
modeled_connec_entities = connec_model_to_external_model(connec_entities)
|
99
85
|
|
100
|
-
|
101
|
-
|
102
|
-
sub_entity_instance = "Entities::SubEntities::#{external_entity_name.titleize.split.join}".constantize.new
|
103
|
-
|
104
|
-
entities.map!{|entity|
|
105
|
-
idmap = sub_entity_instance.class.find_idmap(external_id: sub_entity_instance.class.id_from_external_entity_hash(entity), connec_entity: connec_entity_name.downcase, organization_id: organization.id)
|
106
|
-
|
107
|
-
# No idmap: creating one, nothing else to do
|
108
|
-
unless idmap
|
109
|
-
next {entity: sub_entity_instance.map_to(connec_entity_name, entity, organization), idmap: sub_entity_instance.class.create_idmap_from_external_entity(entity, connec_entity_name, organization)}
|
110
|
-
end
|
111
|
-
|
112
|
-
# Not pushing entity to Connec!
|
113
|
-
next nil unless idmap.to_connec
|
86
|
+
mapped_connec_entities = consolidate_and_map_connec_entities(modeled_connec_entities, modeled_external_entities)
|
87
|
+
mapped_external_entities = consolidate_and_map_external_entities(modeled_external_entities)
|
114
88
|
|
115
|
-
|
116
|
-
|
117
|
-
idmap.update(external_inactive: inactive)
|
118
|
-
next nil if inactive
|
89
|
+
return {connec_entities: mapped_connec_entities, external_entities: mapped_external_entities}
|
90
|
+
end
|
119
91
|
|
120
|
-
|
121
|
-
|
92
|
+
def consolidate_and_map_connec_entities(modeled_connec_entities, modeled_external_entities)
|
93
|
+
modeled_connec_entities.each do |connec_entity_name, entities_in_external_model|
|
94
|
+
entities_in_external_model.each do |external_entity_name, entities|
|
122
95
|
|
123
|
-
|
96
|
+
sub_entity_instance = instantiate_sub_entity_instance(connec_entity_name)
|
97
|
+
equivalent_external_entities = (modeled_external_entities[external_entity_name] && modeled_external_entities[external_entity_name][connec_entity_name]) || []
|
124
98
|
|
125
|
-
|
126
|
-
equivalent_connec_entities = modeled_connec_entities[connec_entity_name][external_entity_name] || []
|
127
|
-
Maestrano::Connector::Rails::Entity.solve_conflict(entity, sub_entity_instance, equivalent_connec_entities, connec_entity_name, idmap, organization, opts)
|
128
|
-
}.compact!
|
99
|
+
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)
|
129
100
|
end
|
130
101
|
end
|
102
|
+
modeled_connec_entities
|
103
|
+
end
|
131
104
|
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
105
|
+
def consolidate_and_map_external_entities(modeled_external_entities)
|
106
|
+
modeled_external_entities.each do |external_entity_name, entities_in_connec_model|
|
107
|
+
entities_in_connec_model.each do |connec_entity_name, entities|
|
108
|
+
sub_entity_instance = instantiate_sub_entity_instance(external_entity_name)
|
109
|
+
|
110
|
+
entities_in_connec_model[connec_entity_name] = sub_entity_instance.consolidate_and_map_external_entities(entities, connec_entity_name)
|
138
111
|
end
|
139
112
|
end
|
140
|
-
|
141
|
-
return {connec_entities: modeled_connec_entities, external_entities: modeled_external_entities}
|
113
|
+
modeled_external_entities
|
142
114
|
end
|
143
115
|
|
144
116
|
# input : {
|
@@ -150,30 +122,30 @@ module Maestrano::Connector::Rails::Concerns::ComplexEntity
|
|
150
122
|
# connec_entities_names[0]: [mapped_external_entity3, mapped_external_entity4]
|
151
123
|
# }
|
152
124
|
# }
|
153
|
-
def push_entities_to_connec(
|
125
|
+
def push_entities_to_connec(mapped_external_entities_with_idmaps)
|
154
126
|
mapped_external_entities_with_idmaps.each do |external_entity_name, entities_in_connec_model|
|
155
|
-
sub_entity_instance =
|
127
|
+
sub_entity_instance = instantiate_sub_entity_instance(external_entity_name)
|
156
128
|
entities_in_connec_model.each do |connec_entity_name, mapped_entities_with_idmaps|
|
157
|
-
sub_entity_instance.push_entities_to_connec_to(
|
129
|
+
sub_entity_instance.push_entities_to_connec_to(mapped_entities_with_idmaps, connec_entity_name)
|
158
130
|
end
|
159
131
|
end
|
160
132
|
end
|
161
133
|
|
162
134
|
|
163
|
-
def push_entities_to_external(
|
135
|
+
def push_entities_to_external(mapped_connec_entities_with_idmaps)
|
164
136
|
mapped_connec_entities_with_idmaps.each do |connec_entity_name, entities_in_external_model|
|
165
|
-
sub_entity_instance =
|
137
|
+
sub_entity_instance = instantiate_sub_entity_instance(connec_entity_name)
|
166
138
|
entities_in_external_model.each do |external_entity_name, mapped_entities_with_idmaps|
|
167
|
-
sub_entity_instance.push_entities_to_external_to(
|
139
|
+
sub_entity_instance.push_entities_to_external_to(mapped_entities_with_idmaps, external_entity_name)
|
168
140
|
end
|
169
141
|
end
|
170
142
|
end
|
171
143
|
|
172
|
-
def before_sync(
|
144
|
+
def before_sync(last_synchronization)
|
173
145
|
# Does nothing by default
|
174
146
|
end
|
175
147
|
|
176
|
-
def after_sync(
|
148
|
+
def after_sync(last_synchronization)
|
177
149
|
# Does nothing by default
|
178
150
|
end
|
179
151
|
|
@@ -183,7 +155,27 @@ module Maestrano::Connector::Rails::Concerns::ComplexEntity
|
|
183
155
|
# external_entities_names[0]: [unmapped_external_entity1}, unmapped_external_entity2],
|
184
156
|
# external_entities_names[1]: [unmapped_external_entity3}, unmapped_external_entity4]
|
185
157
|
# }
|
186
|
-
def filter_connec_entities(entities
|
158
|
+
def filter_connec_entities(entities)
|
187
159
|
entities
|
188
160
|
end
|
161
|
+
|
162
|
+
def instantiate_sub_entity_instance(entity_name)
|
163
|
+
"Entities::SubEntities::#{entity_name.titleize.split.join}".constantize.new(@organization, @connec_client, @external_client, @opts)
|
164
|
+
end
|
165
|
+
|
166
|
+
# -------------------------------------------------------------
|
167
|
+
# Helper methods
|
168
|
+
# -------------------------------------------------------------
|
169
|
+
module ClassMethods
|
170
|
+
# output : {entities_names[0] => [], entities_names[1] => []}
|
171
|
+
def build_empty_hash(entities_names)
|
172
|
+
Hash[ *entities_names.collect{|name| [ name, []]}.flatten(1) ]
|
173
|
+
end
|
174
|
+
|
175
|
+
# output: {entities_name[0] => [], entities_name[1] => entities}
|
176
|
+
# with proc.call(entities_name[1] == entity_name)
|
177
|
+
def build_hash_with_entities(entities_name, entity_name, proc, entities)
|
178
|
+
Hash[ *entities_name.collect{|name| proc.call(name) == entity_name ? [name, entities] : [ name, []]}.flatten(1) ]
|
179
|
+
end
|
180
|
+
end
|
189
181
|
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
module Maestrano::Connector::Rails::Concerns::ConnecHelper
|
2
|
+
extend ActiveSupport::Concern
|
3
|
+
|
4
|
+
module ClassMethods
|
5
|
+
|
6
|
+
def get_client(organization)
|
7
|
+
client = Maestrano::Connec::Client[organization.tenant].new(organization.uid)
|
8
|
+
client.class.headers('CONNEC-EXTERNAL-IDS' => 'true')
|
9
|
+
client
|
10
|
+
end
|
11
|
+
|
12
|
+
# Replace the ids arrays by the external id
|
13
|
+
# If a reference has no id for this oauth_provider and oauth_uid but has one for connec returns nil
|
14
|
+
def unfold_references(connec_entity, references, organization)
|
15
|
+
unfolded_connec_entity = connec_entity.with_indifferent_access
|
16
|
+
not_nil = true
|
17
|
+
|
18
|
+
# Id
|
19
|
+
id = unfolded_connec_entity['id'].find{|id| id['provider'] == organization.oauth_provider && id['realm'] == organization.oauth_uid}
|
20
|
+
unfolded_connec_entity[:__connec_id] = unfolded_connec_entity['id'].find{|id| id['provider'] == 'connec'}['id']
|
21
|
+
if id
|
22
|
+
unfolded_connec_entity['id'] = id['id']
|
23
|
+
else
|
24
|
+
unfolded_connec_entity['id'] = nil
|
25
|
+
end
|
26
|
+
|
27
|
+
# Other refs
|
28
|
+
references.each do |reference|
|
29
|
+
not_nil &&= unfold_references_helper(unfolded_connec_entity, reference.split('/'), organization)
|
30
|
+
end
|
31
|
+
not_nil ? unfolded_connec_entity : nil
|
32
|
+
end
|
33
|
+
|
34
|
+
def fold_references(mapped_external_entity, references, organization)
|
35
|
+
mapped_external_entity = mapped_external_entity.with_indifferent_access
|
36
|
+
(references + ['id']).each do |reference|
|
37
|
+
fold_references_helper(mapped_external_entity, reference.split('/'), organization)
|
38
|
+
end
|
39
|
+
mapped_external_entity
|
40
|
+
end
|
41
|
+
|
42
|
+
def id_hash(id, organization)
|
43
|
+
{
|
44
|
+
id: id,
|
45
|
+
provider: organization.oauth_provider,
|
46
|
+
realm: organization.oauth_uid
|
47
|
+
}
|
48
|
+
end
|
49
|
+
|
50
|
+
def fold_references_helper(entity, array_of_refs, organization)
|
51
|
+
ref = array_of_refs.shift
|
52
|
+
field = entity[ref]
|
53
|
+
|
54
|
+
# Follow embedment path, remplace if it's a string
|
55
|
+
unless field.blank?
|
56
|
+
case field
|
57
|
+
when Array
|
58
|
+
field.each do |f|
|
59
|
+
fold_references_helper(f, array_of_refs.dup, organization)
|
60
|
+
end
|
61
|
+
when HashWithIndifferentAccess
|
62
|
+
fold_references_helper(entity[ref], array_of_refs, organization)
|
63
|
+
when String
|
64
|
+
id = field
|
65
|
+
entity[ref] = [id_hash(id, organization)]
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def unfold_references_helper(entity, array_of_refs, organization)
|
71
|
+
ref = array_of_refs.shift
|
72
|
+
field = entity[ref]
|
73
|
+
|
74
|
+
# Unfold the id
|
75
|
+
if array_of_refs.empty?
|
76
|
+
if id = field && field.find{|id| id[:provider] == organization.oauth_provider && id[:realm] == organization.oauth_uid}
|
77
|
+
entity[ref] = id['id']
|
78
|
+
elsif field && field.find{|id| id[:provider] == 'connec'}
|
79
|
+
# We may enqueue a fetch on the endpoint of the missing association, followed by a re-fetch on this one.
|
80
|
+
# 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.
|
81
|
+
# Worst case it'll be done on following sync
|
82
|
+
return nil
|
83
|
+
end
|
84
|
+
|
85
|
+
# Follow embedment path
|
86
|
+
else
|
87
|
+
unless field.blank?
|
88
|
+
case field
|
89
|
+
when Array
|
90
|
+
field.each do |f|
|
91
|
+
unfold_references_helper(f, array_of_refs.dup, organization)
|
92
|
+
end
|
93
|
+
when HashWithIndifferentAccess
|
94
|
+
unfold_references_helper(entity[ref], array_of_refs, organization)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
true
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
102
|
+
end
|