maestrano-connector-rails 0.4.1 → 0.4.2
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/VERSION +1 -1
- data/app/controllers/maestrano/auth/saml_controller.rb +1 -1
- data/app/controllers/maestrano/connec_controller.rb +26 -25
- data/app/jobs/maestrano/connector/rails/push_to_connec_job.rb +10 -3
- data/app/jobs/maestrano/connector/rails/synchronization_job.rb +4 -4
- data/app/models/maestrano/connector/rails/concerns/complex_entity.rb +12 -5
- data/app/models/maestrano/connector/rails/concerns/entity.rb +30 -21
- data/app/models/maestrano/connector/rails/organization.rb +9 -0
- data/db/migrate/20160427112250_add_inactive_to_idmaps.rb +5 -0
- data/lib/generators/connector/templates/entity.rb +6 -0
- data/maestrano-connector-rails.gemspec +4 -3
- data/spec/controllers/connec_controller_spec.rb +4 -0
- data/spec/dummy/db/schema.rb +7 -3
- data/spec/jobs/push_to_connec_job_spec.rb +14 -1
- data/spec/jobs/synchronization_job_spec.rb +41 -21
- data/spec/models/complex_entity_spec.rb +78 -1
- data/spec/models/entity_spec.rb +44 -0
- data/spec/models/organization_spec.rb +41 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 43df5981576854c5a7433146a874a38b6826a030
|
4
|
+
data.tar.gz: 865f0399ab96db0323b281f18ab80f7f38359e5d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b9e01ee3b43e36ad310f5babf1819b9a215be1d325afbb378092213cdad72802cac6ae5cf77b4546189b4649a5b4635bf3af1a37c21ba19fa04552c9940b2eac
|
7
|
+
data.tar.gz: 77fbe67ebc5496d2f1b8e74ea9237b53fa3b880121fbc73b0a9b4265235c83833e79bdb418c77f4eb0ffdd3a0545af3decdd8cb470f2ad4ae708128cd580a901
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.4.
|
1
|
+
0.4.2
|
@@ -25,7 +25,7 @@ class Maestrano::Auth::SamlController < Maestrano::Rails::SamlBaseController
|
|
25
25
|
end
|
26
26
|
|
27
27
|
if session[:settings]
|
28
|
-
session.delete(:
|
28
|
+
session.delete(:settings)
|
29
29
|
redirect_to main_app.root_path
|
30
30
|
else
|
31
31
|
if current_organization && current_organization.oauth_uid && current_organization.sync_enabled
|
@@ -5,31 +5,33 @@ class Maestrano::ConnecController < Maestrano::Rails::WebHookController
|
|
5
5
|
|
6
6
|
begin
|
7
7
|
params.except(:tenant, :controller, :action).each do |entity_name, entities|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
8
|
+
|
9
|
+
entity_instance_hash = find_entity_instance(entity_name)
|
10
|
+
next Rails.logger.info "Received notification from Connec! for unknow entity: #{entity_name}" unless entity_instance_hash
|
11
|
+
|
12
|
+
entity_instance = entity_instance_hash[:instance]
|
13
|
+
|
14
|
+
entities.each do |entity|
|
15
|
+
organization = Maestrano::Connector::Rails::Organization.find_by_uid_and_tenant(entity[:group_id], params[:tenant])
|
16
|
+
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[entity_instance_hash[:name].to_sym]
|
18
|
+
|
19
|
+
|
20
|
+
Maestrano::Connector::Rails::ConnectorLogger.log('info', organization, "Received entity from Connec! webhook: Entity=#{entity_name}, Data=#{entity}")
|
21
|
+
connec_client = Maestrano::Connec::Client[organization.tenant].new(organization.uid)
|
22
|
+
external_client = Maestrano::Connector::Rails::External.get_client(organization)
|
23
|
+
last_synchronization = organization.last_successful_synchronization
|
24
|
+
|
25
|
+
entity_instance.before_sync(connec_client, external_client, last_synchronization, organization, {})
|
26
|
+
# Build expected input for consolidate_and_map_data
|
27
|
+
if entity_instance_hash[:is_complex]
|
28
|
+
mapped_entity = entity_instance.consolidate_and_map_data(Hash[ *entity_instance.class.connec_entities_names.collect{|name| name.parameterize('_').pluralize == entity_name ? [name, [entity]] : [ name, []]}.flatten(1) ], Hash[ *entity_instance.class.external_entities_names.collect{|name| [ name, []]}.flatten(1) ], organization, {})
|
29
|
+
else
|
30
|
+
mapped_entity = entity_instance.consolidate_and_map_data([entity], [], organization, {})
|
30
31
|
end
|
31
|
-
|
32
|
-
|
32
|
+
entity_instance.push_entities_to_external(external_client, mapped_entity[:connec_entities], organization)
|
33
|
+
|
34
|
+
entity_instance.after_sync(connec_client, external_client, last_synchronization, organization, {})
|
33
35
|
end
|
34
36
|
end
|
35
37
|
rescue => e
|
@@ -40,7 +42,6 @@ class Maestrano::ConnecController < Maestrano::Rails::WebHookController
|
|
40
42
|
end
|
41
43
|
|
42
44
|
|
43
|
-
|
44
45
|
private
|
45
46
|
def find_entity_instance(entity_name)
|
46
47
|
Maestrano::Connector::Rails::Entity.entities_list.each do |entity_name_from_list|
|
@@ -3,22 +3,29 @@ module Maestrano::Connector::Rails
|
|
3
3
|
queue_as :default
|
4
4
|
|
5
5
|
# expected hash: {"external_entity_name1" => [entity1, entity2], "external_entity_name2" => []}
|
6
|
-
def perform(organization, entities_hash)
|
6
|
+
def perform(organization, entities_hash, opts={})
|
7
7
|
return unless organization.sync_enabled && organization.oauth_uid
|
8
8
|
|
9
9
|
connec_client = Maestrano::Connec::Client[organization.tenant].new(organization.uid)
|
10
|
+
external_client = Maestrano::Connector::Rails::External.get_client(organization)
|
11
|
+
last_synchronization = organization.last_successful_synchronization
|
10
12
|
|
11
13
|
entities_hash.each do |external_entity_name, entities|
|
12
14
|
if entity_instance_hash = find_entity_instance(external_entity_name)
|
13
|
-
entity_instance = entity_instance_hash[:instance]
|
14
15
|
next unless organization.synchronized_entities[entity_instance_hash[:name].to_sym]
|
16
|
+
|
17
|
+
entity_instance = entity_instance_hash[:instance]
|
18
|
+
|
19
|
+
entity_instance.before_sync(connec_client, external_client, last_synchronization, organization, opts)
|
20
|
+
# Build expected input for consolidate_and_map_data
|
15
21
|
if entity_instance_hash[:is_complex]
|
16
22
|
mapped_entities = entity_instance.consolidate_and_map_data(Hash[ *entity_instance.class.connec_entities_names.collect{|name| [ name, []]}.flatten(1) ], Hash[ *entity_instance.class.external_entities_names.collect{|name| name == external_entity_name ? [name, entities] : [ name, []]}.flatten(1) ], organization, {})
|
17
23
|
else
|
18
24
|
mapped_entities = entity_instance.consolidate_and_map_data([], entities, organization, {})
|
19
25
|
end
|
20
|
-
|
21
26
|
entity_instance.push_entities_to_connec(connec_client, mapped_entities[:external_entities], organization)
|
27
|
+
|
28
|
+
entity_instance.after_sync(connec_client, external_client, last_synchronization, organization, opts)
|
22
29
|
else
|
23
30
|
Rails.logger.warn "Called push to connec job with unknow entity: #{external_entity_name}"
|
24
31
|
end
|
@@ -3,7 +3,7 @@ module Maestrano::Connector::Rails
|
|
3
3
|
queue_as :default
|
4
4
|
|
5
5
|
# Supported options:
|
6
|
-
# * :forced => true synchronization has been triggered manually
|
6
|
+
# * :forced => true synchronization has been triggered manually
|
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)
|
@@ -17,8 +17,8 @@ module Maestrano::Connector::Rails
|
|
17
17
|
end
|
18
18
|
|
19
19
|
# Check if recovery mode: last 3 synchronizations have failed
|
20
|
-
if
|
21
|
-
&&
|
20
|
+
if !opts[:forced] && organization.last_three_synchronizations_failed? \
|
21
|
+
&& organization.synchronizations.order(created_at: :desc).limit(1).first.updated_at > 1.day.ago
|
22
22
|
ConnectorLogger.log('info', organization, "Synchronization skipped: Recovery mode (three previous synchronizations have failed)")
|
23
23
|
return
|
24
24
|
end
|
@@ -28,7 +28,7 @@ module Maestrano::Connector::Rails
|
|
28
28
|
current_synchronization = Synchronization.create_running(organization)
|
29
29
|
|
30
30
|
begin
|
31
|
-
last_synchronization =
|
31
|
+
last_synchronization = organization.last_successful_synchronization
|
32
32
|
connec_client = Maestrano::Connec::Client[organization.tenant].new(organization.uid)
|
33
33
|
external_client = External.get_client(organization)
|
34
34
|
|
@@ -56,11 +56,13 @@ module Maestrano::Connector::Rails::Concerns::ComplexEntity
|
|
56
56
|
idmap = sub_entity_instance.class.find_idmap({connec_id: entity['id'], external_entity: external_entity_name.downcase, organization_id: organization.id})
|
57
57
|
|
58
58
|
if idmap
|
59
|
-
idmap.
|
60
|
-
|
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']
|
61
62
|
Maestrano::Connector::Rails::ConnectorLogger.log('info', organization, "Discard Connec! #{sub_entity_instance.class.entity_name} : #{entity}")
|
62
63
|
nil
|
63
64
|
else
|
65
|
+
idmap.update(name: sub_entity_instance.class.object_name_from_connec_entity_hash(entity))
|
64
66
|
{entity: sub_entity_instance.map_to(external_entity_name, entity, organization), idmap: idmap}
|
65
67
|
end
|
66
68
|
else
|
@@ -103,18 +105,23 @@ module Maestrano::Connector::Rails::Concerns::ComplexEntity
|
|
103
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)
|
104
106
|
|
105
107
|
# No idmap: creating one, nothing else to do
|
106
|
-
|
107
|
-
idmap.update(name: sub_entity_instance.class.object_name_from_external_entity_hash(entity))
|
108
|
-
else
|
108
|
+
unless idmap
|
109
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
110
|
end
|
111
111
|
|
112
112
|
# Not pushing entity to Connec!
|
113
113
|
next nil unless idmap.to_connec
|
114
114
|
|
115
|
+
# Not pushing to Connec! and flagging as inactive if inactive in external application
|
116
|
+
inactive = sub_entity_instance.class.inactive_from_external_entity_hash?(entity)
|
117
|
+
idmap.update(external_inactive: inactive)
|
118
|
+
next nil if inactive
|
119
|
+
|
115
120
|
# Entity has not been modified since its last push to connec!
|
116
121
|
next nil if Maestrano::Connector::Rails::Entity.not_modified_since_last_push_to_connec?(idmap, entity, sub_entity_instance, organization)
|
117
122
|
|
123
|
+
idmap.update(name: sub_entity_instance.class.object_name_from_external_entity_hash(entity))
|
124
|
+
|
118
125
|
# Check for conflict with entities from connec!
|
119
126
|
equivalent_connec_entities = modeled_connec_entities[connec_entity_name][external_entity_name] || []
|
120
127
|
Maestrano::Connector::Rails::Entity.solve_conflict(entity, sub_entity_instance, equivalent_connec_entities, connec_entity_name, idmap, organization, opts)
|
@@ -75,6 +75,21 @@ module Maestrano::Connector::Rails::Concerns::Entity
|
|
75
75
|
raise "Not implemented"
|
76
76
|
end
|
77
77
|
|
78
|
+
# Return a string representing the object from a connec! entity hash
|
79
|
+
def object_name_from_connec_entity_hash(entity)
|
80
|
+
raise "Not implemented"
|
81
|
+
end
|
82
|
+
|
83
|
+
# Return a string representing the object from an external entity hash
|
84
|
+
def object_name_from_external_entity_hash(entity)
|
85
|
+
raise "Not implemented"
|
86
|
+
end
|
87
|
+
|
88
|
+
# Returns a boolean
|
89
|
+
# Returns true is the entity is flagged as inactive (deleted) in the external application
|
90
|
+
def inactive_from_external_entity_hash?(entity)
|
91
|
+
false
|
92
|
+
end
|
78
93
|
# ----------------------------------------------
|
79
94
|
# Entity specific methods
|
80
95
|
# Those methods need to be define in each entity
|
@@ -99,19 +114,7 @@ module Maestrano::Connector::Rails::Concerns::Entity
|
|
99
114
|
raise "Not implemented"
|
100
115
|
end
|
101
116
|
|
102
|
-
# Return a string representing the object from a connec! entity hash
|
103
|
-
def object_name_from_connec_entity_hash(entity)
|
104
|
-
raise "Not implemented"
|
105
|
-
end
|
106
|
-
|
107
|
-
# Return a string representing the object from an external entity hash
|
108
|
-
def object_name_from_external_entity_hash(entity)
|
109
|
-
raise "Not implemented"
|
110
|
-
end
|
111
|
-
|
112
117
|
# [{reference_class: Entities::.., connec_field: 'account_id', external_field: 'account/something/id'}]
|
113
|
-
# ledger_account_idmap = Entities::Account.find_idmap({connec_id: entity['account_id'], organization_id: organization.id})
|
114
|
-
# ledger_account_id = ledger_account_idmap && ledger_account_idmap.external_id
|
115
118
|
def references
|
116
119
|
[]
|
117
120
|
end
|
@@ -286,11 +289,13 @@ module Maestrano::Connector::Rails::Concerns::Entity
|
|
286
289
|
idmap = self.class.find_idmap({connec_id: entity['id'], organization_id: organization.id})
|
287
290
|
|
288
291
|
if idmap
|
289
|
-
idmap.
|
290
|
-
|
292
|
+
return nil if idmap.external_inactive || !idmap.to_external
|
293
|
+
|
294
|
+
if idmap.last_push_to_external && idmap.last_push_to_external > entity['updated_at']
|
291
295
|
Maestrano::Connector::Rails::ConnectorLogger.log('info', organization, "Discard Connec! #{self.class.connec_entity_name} : #{entity}")
|
292
296
|
nil
|
293
297
|
else
|
298
|
+
idmap.update(name: self.class.object_name_from_connec_entity_hash(entity))
|
294
299
|
{entity: map_to_external(entity, organization), idmap: idmap}
|
295
300
|
end
|
296
301
|
else
|
@@ -360,28 +365,32 @@ module Maestrano::Connector::Rails::Concerns::Entity
|
|
360
365
|
|
361
366
|
mapped_external_entities = external_entities.map{|entity|
|
362
367
|
idmap = self.class.find_idmap({external_id: self.class.id_from_external_entity_hash(entity), organization_id: organization.id})
|
368
|
+
|
363
369
|
# No idmap: creating one, nothing else to do
|
364
|
-
|
365
|
-
idmap.update(name: self.class.object_name_from_external_entity_hash(entity))
|
366
|
-
else
|
370
|
+
unless idmap
|
367
371
|
next {entity: map_to_connec(entity, organization), idmap: self.class.create_idmap_from_external_entity(entity, organization)}
|
368
372
|
end
|
369
373
|
|
370
374
|
# Not pushing entity to Connec!
|
371
375
|
next nil unless idmap.to_connec
|
372
376
|
|
377
|
+
# Not pushing to Connec! and flagging as inactive if inactive in external application
|
378
|
+
inactive = self.class.inactive_from_external_entity_hash?(entity)
|
379
|
+
idmap.update(external_inactive: inactive)
|
380
|
+
next nil if inactive
|
381
|
+
|
373
382
|
# Entity has not been modified since its last push to connec!
|
374
383
|
next nil if self.class.not_modified_since_last_push_to_connec?(idmap, entity, self, organization)
|
375
384
|
|
385
|
+
idmap.update(name: self.class.object_name_from_external_entity_hash(entity))
|
386
|
+
|
376
387
|
# Check for conflict with entities from connec!
|
377
388
|
self.class.solve_conflict(entity, self, connec_entities, self.class.connec_entity_name, idmap, organization, opts)
|
378
|
-
}
|
379
|
-
mapped_external_entities.compact!
|
389
|
+
}.compact
|
380
390
|
|
381
391
|
mapped_connec_entities = connec_entities.map{|entity|
|
382
392
|
map_to_external_with_idmap(entity, organization)
|
383
|
-
}
|
384
|
-
mapped_connec_entities.compact!
|
393
|
+
}.compact
|
385
394
|
|
386
395
|
return {connec_entities: mapped_connec_entities, external_entities: mapped_external_entities}
|
387
396
|
end
|
@@ -59,5 +59,14 @@ module Maestrano::Connector::Rails
|
|
59
59
|
self.instance_url = auth.credentials.instance_url
|
60
60
|
self.save!
|
61
61
|
end
|
62
|
+
|
63
|
+
def last_three_synchronizations_failed?
|
64
|
+
arr = self.synchronizations.last(3).map(&:is_error?)
|
65
|
+
arr.count == 3 && arr.uniq == [true]
|
66
|
+
end
|
67
|
+
|
68
|
+
def last_successful_synchronization
|
69
|
+
self.synchronizations.where(status: 'SUCCESS', partial: false).order(updated_at: :desc).first
|
70
|
+
end
|
62
71
|
end
|
63
72
|
end
|
@@ -46,4 +46,10 @@ class Maestrano::Connector::Rails::Entity
|
|
46
46
|
# e.g entity['last_update']
|
47
47
|
end
|
48
48
|
|
49
|
+
def self.inactive_from_external_entity_hash?(entity)
|
50
|
+
# TODO
|
51
|
+
# This method return true is entity is inactive in the external application
|
52
|
+
# e.g entity['status'] == 'INACTIVE'
|
53
|
+
end
|
54
|
+
|
49
55
|
end
|
@@ -2,16 +2,16 @@
|
|
2
2
|
# DO NOT EDIT THIS FILE DIRECTLY
|
3
3
|
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
4
|
# -*- encoding: utf-8 -*-
|
5
|
-
# stub: maestrano-connector-rails 0.4.
|
5
|
+
# stub: maestrano-connector-rails 0.4.2 ruby lib
|
6
6
|
|
7
7
|
Gem::Specification.new do |s|
|
8
8
|
s.name = "maestrano-connector-rails"
|
9
|
-
s.version = "0.4.
|
9
|
+
s.version = "0.4.2"
|
10
10
|
|
11
11
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
12
12
|
s.require_paths = ["lib"]
|
13
13
|
s.authors = ["Pierre Berard"]
|
14
|
-
s.date = "2016-04-
|
14
|
+
s.date = "2016-04-29"
|
15
15
|
s.description = "Maestrano is the next generation marketplace for SME applications. See https://maestrano.com for details."
|
16
16
|
s.email = "pierre.berard@maestrano.com"
|
17
17
|
s.executables = ["rails"]
|
@@ -62,6 +62,7 @@ Gem::Specification.new do |s|
|
|
62
62
|
"db/migrate/20151122163449_create_id_maps.rb",
|
63
63
|
"db/migrate/20160205132857_add_sync_enabled_to_organizations.rb",
|
64
64
|
"db/migrate/20160215103120_add_name_to_id_map.rb",
|
65
|
+
"db/migrate/20160427112250_add_inactive_to_idmaps.rb",
|
65
66
|
"lib/generators/connector/USAGE",
|
66
67
|
"lib/generators/connector/complex_entity_generator.rb",
|
67
68
|
"lib/generators/connector/install_generator.rb",
|
@@ -70,8 +70,10 @@ describe Maestrano::ConnecController, type: :controller do
|
|
70
70
|
let!(:organization) { create(:organization, uid: group_id, oauth_uid: 'lala', sync_enabled: true, synchronized_entities: {contact_and_lead: true}) }
|
71
71
|
|
72
72
|
it 'process the data and push them' do
|
73
|
+
expect_any_instance_of(Entities::ContactAndLead).to receive(:before_sync)
|
73
74
|
expect_any_instance_of(Entities::ContactAndLead).to receive(:consolidate_and_map_data).with({"Lead"=>[entity]}, {}, organization, {}).and_return({})
|
74
75
|
expect_any_instance_of(Entities::ContactAndLead).to receive(:push_entities_to_external)
|
76
|
+
expect_any_instance_of(Entities::ContactAndLead).to receive(:after_sync)
|
75
77
|
subject
|
76
78
|
end
|
77
79
|
end
|
@@ -135,8 +137,10 @@ describe Maestrano::ConnecController, type: :controller do
|
|
135
137
|
let!(:organization) { create(:organization, uid: group_id, oauth_uid: 'lala', sync_enabled: true, synchronized_entities: {person: true}) }
|
136
138
|
|
137
139
|
it 'process the data and push them' do
|
140
|
+
expect_any_instance_of(Entities::Person).to receive(:before_sync)
|
138
141
|
expect_any_instance_of(Entities::Person).to receive(:consolidate_and_map_data).with([entity], [], organization, {}).and_return({})
|
139
142
|
expect_any_instance_of(Entities::Person).to receive(:push_entities_to_external)
|
143
|
+
expect_any_instance_of(Entities::Person).to receive(:after_sync)
|
140
144
|
subject
|
141
145
|
end
|
142
146
|
end
|
data/spec/dummy/db/schema.rb
CHANGED
@@ -11,7 +11,7 @@
|
|
11
11
|
#
|
12
12
|
# It's strongly recommended that you check this file into your version control system.
|
13
13
|
|
14
|
-
ActiveRecord::Schema.define(version:
|
14
|
+
ActiveRecord::Schema.define(version: 20160427120963) do
|
15
15
|
|
16
16
|
create_table "id_maps", force: :cascade do |t|
|
17
17
|
t.string "connec_id"
|
@@ -21,12 +21,13 @@ ActiveRecord::Schema.define(version: 20160215103120) do
|
|
21
21
|
t.integer "organization_id"
|
22
22
|
t.datetime "last_push_to_connec"
|
23
23
|
t.datetime "last_push_to_external"
|
24
|
-
t.datetime "created_at",
|
25
|
-
t.datetime "updated_at",
|
24
|
+
t.datetime "created_at", null: false
|
25
|
+
t.datetime "updated_at", null: false
|
26
26
|
t.boolean "to_connec", default: true
|
27
27
|
t.boolean "to_external", default: true
|
28
28
|
t.string "name"
|
29
29
|
t.string "message"
|
30
|
+
t.boolean "external_inactive", default: false
|
30
31
|
end
|
31
32
|
|
32
33
|
add_index "id_maps", ["connec_id", "connec_entity", "organization_id"], name: "idmap_connec_index"
|
@@ -40,6 +41,7 @@ ActiveRecord::Schema.define(version: 20160215103120) do
|
|
40
41
|
t.string "tenant"
|
41
42
|
t.string "oauth_provider"
|
42
43
|
t.string "oauth_uid"
|
44
|
+
t.string "oauth_name"
|
43
45
|
t.string "oauth_token"
|
44
46
|
t.string "refresh_token"
|
45
47
|
t.string "instance_url"
|
@@ -78,6 +80,8 @@ ActiveRecord::Schema.define(version: 20160215103120) do
|
|
78
80
|
t.string "first_name"
|
79
81
|
t.string "last_name"
|
80
82
|
t.string "email"
|
83
|
+
t.string "locale"
|
84
|
+
t.string "timezone"
|
81
85
|
t.string "tenant"
|
82
86
|
t.datetime "created_at", null: false
|
83
87
|
t.datetime "updated_at", null: false
|
@@ -60,7 +60,7 @@ describe Maestrano::Connector::Rails::PushToConnecJob do
|
|
60
60
|
end
|
61
61
|
end
|
62
62
|
|
63
|
-
describe 'with entities in
|
63
|
+
describe 'with entities in synchronized entities' do
|
64
64
|
|
65
65
|
describe 'complex entity' do
|
66
66
|
before { organization.update(synchronized_entities: {:"#{entity_name1}" => false, :"#{entity_name2}" => true})}
|
@@ -75,6 +75,12 @@ describe Maestrano::Connector::Rails::PushToConnecJob do
|
|
75
75
|
expect_any_instance_of(Entities::Entity1).to_not receive(:consolidate_and_map_data)
|
76
76
|
subject
|
77
77
|
end
|
78
|
+
|
79
|
+
it 'calls before and after sync' do
|
80
|
+
expect_any_instance_of(Entities::Entity2).to receive(:before_sync)
|
81
|
+
expect_any_instance_of(Entities::Entity2).to receive(:after_sync)
|
82
|
+
subject
|
83
|
+
end
|
78
84
|
end
|
79
85
|
|
80
86
|
describe 'non complex entity' do
|
@@ -91,6 +97,13 @@ describe Maestrano::Connector::Rails::PushToConnecJob do
|
|
91
97
|
expect_any_instance_of(Entities::Entity2).to_not receive(:consolidate_and_map_data)
|
92
98
|
subject
|
93
99
|
end
|
100
|
+
|
101
|
+
it 'calls before and after sync' do
|
102
|
+
allow_any_instance_of(Entities::Entity1).to receive(:consolidate_and_map_data).and_return({})
|
103
|
+
expect_any_instance_of(Entities::Entity1).to receive(:before_sync)
|
104
|
+
expect_any_instance_of(Entities::Entity1).to receive(:after_sync)
|
105
|
+
subject
|
106
|
+
end
|
94
107
|
end
|
95
108
|
end
|
96
109
|
end
|
@@ -2,52 +2,72 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe Maestrano::Connector::Rails::SynchronizationJob do
|
4
4
|
let(:organization) { create(:organization) }
|
5
|
-
|
5
|
+
let(:opts) { {} }
|
6
|
+
subject { Maestrano::Connector::Rails::SynchronizationJob.perform_now(organization, opts) }
|
7
|
+
|
8
|
+
def does_not_perform
|
9
|
+
expect_any_instance_of(Maestrano::Connector::Rails::SynchronizationJob).to_not receive(:sync_entity)
|
10
|
+
expect{ subject }.to change{ Maestrano::Connector::Rails::Synchronization.count }.by(0)
|
11
|
+
end
|
12
|
+
|
13
|
+
def performes
|
14
|
+
expect{ subject }.to change{ Maestrano::Connector::Rails::Synchronization.count }.by(1)
|
15
|
+
end
|
6
16
|
|
7
17
|
describe 'perform' do
|
8
18
|
context 'with sync_enabled set to false' do
|
9
|
-
it
|
10
|
-
expect{ subject }.to change{ Maestrano::Connector::Rails::Synchronization.count }.by(0)
|
11
|
-
end
|
12
|
-
|
13
|
-
it 'does not calls sync entity' do
|
14
|
-
expect_any_instance_of(Maestrano::Connector::Rails::SynchronizationJob).to_not receive(:sync_entity)
|
15
|
-
subject
|
16
|
-
end
|
19
|
+
it { does_not_perform }
|
17
20
|
end
|
18
21
|
|
19
22
|
context 'with sync_enabled set to true' do
|
20
23
|
before {organization.update(sync_enabled: true)}
|
21
24
|
|
25
|
+
context 'with a sync still running for less than 30 minutes' do
|
26
|
+
let!(:running_sync) { create(:synchronization, organization: organization, status: 'RUNNING', created_at: 29.minutes.ago) }
|
27
|
+
it { does_not_perform }
|
28
|
+
end
|
29
|
+
|
30
|
+
context 'with a sync still running for more than 30 minutes' do
|
31
|
+
let!(:running_sync) { create(:synchronization, organization: organization, status: 'RUNNING', created_at: 31.minutes.ago) }
|
32
|
+
it { performes }
|
33
|
+
end
|
34
|
+
|
22
35
|
describe 'recovery mode' do
|
23
|
-
|
36
|
+
context 'three last sync failed and last sync less than 24 hours ago' do
|
24
37
|
before {
|
25
38
|
3.times do
|
26
|
-
organization.synchronizations.create(status: 'ERROR')
|
39
|
+
organization.synchronizations.create(status: 'ERROR', created_at: 2.hour.ago)
|
27
40
|
end
|
28
41
|
}
|
42
|
+
it { does_not_perform }
|
29
43
|
|
30
|
-
|
31
|
-
|
44
|
+
context 'synchronization is forced' do
|
45
|
+
let(:opts) { {forced: true} }
|
46
|
+
it { performes }
|
32
47
|
end
|
33
48
|
end
|
34
49
|
|
35
|
-
|
50
|
+
context 'three last sync failed and last sync more than 24 hours ago' do
|
36
51
|
before {
|
37
52
|
3.times do
|
38
53
|
organization.synchronizations.create(status: 'ERROR', created_at: 2.day.ago, updated_at: 2.day.ago)
|
39
54
|
end
|
40
55
|
}
|
56
|
+
it { performes }
|
57
|
+
end
|
41
58
|
|
42
|
-
|
43
|
-
|
44
|
-
|
59
|
+
context 'three sync failed but last sync is successfull' do
|
60
|
+
before {
|
61
|
+
3.times do
|
62
|
+
organization.synchronizations.create(status: 'ERROR', created_at: 2.hour.ago)
|
63
|
+
end
|
64
|
+
organization.synchronizations.create(status: 'SUCCESS', created_at: 1.hour.ago)
|
65
|
+
}
|
66
|
+
it { performes }
|
45
67
|
end
|
46
68
|
end
|
47
69
|
|
48
|
-
it
|
49
|
-
expect{ subject }.to change{ Maestrano::Connector::Rails::Synchronization.count }.by(1)
|
50
|
-
end
|
70
|
+
it { performes }
|
51
71
|
|
52
72
|
it 'calls sync entity on all the organization synchronized entities set to true' do
|
53
73
|
organization.synchronized_entities[organization.synchronized_entities.keys.first] = false
|
@@ -58,7 +78,7 @@ describe Maestrano::Connector::Rails::SynchronizationJob do
|
|
58
78
|
|
59
79
|
context 'with options' do
|
60
80
|
context 'with only_entities' do
|
61
|
-
|
81
|
+
let(:opts) { {only_entities: %w(people price)} }
|
62
82
|
|
63
83
|
it 'calls sync entity on the specified entities' do
|
64
84
|
expect_any_instance_of(Maestrano::Connector::Rails::SynchronizationJob).to receive(:sync_entity).twice
|
@@ -80,6 +80,14 @@ describe Maestrano::Connector::Rails::ComplexEntity do
|
|
80
80
|
expect(subject.map_to_external_with_idmap(entity, organization, external_name, sub_instance)).to be_nil
|
81
81
|
end
|
82
82
|
end
|
83
|
+
|
84
|
+
context 'when entity has an idmap with external_inactive set to true' do
|
85
|
+
let!(:idmap) { create(:idmap, organization: organization, connec_id: id, connec_entity: connec_name.downcase, external_inactive: true, external_entity: external_name.downcase) }
|
86
|
+
|
87
|
+
it 'discards the entity' do
|
88
|
+
expect(subject.map_to_external_with_idmap(entity, organization, external_name, sub_instance)).to be_nil
|
89
|
+
end
|
90
|
+
end
|
83
91
|
end
|
84
92
|
|
85
93
|
end
|
@@ -218,7 +226,7 @@ describe Maestrano::Connector::Rails::ComplexEntity do
|
|
218
226
|
expect(mapped_entities).to eql(external_entities: {
|
219
227
|
'sc_e1' => {'Connec1' => [{entity: mapped_entity1, idmap: Maestrano::Connector::Rails::IdMap.first}]},
|
220
228
|
'ScE2' => {
|
221
|
-
'Connec1' => [{entity: mapped_entity1, idmap: Maestrano::Connector::Rails::IdMap.
|
229
|
+
'Connec1' => [{entity: mapped_entity1, idmap: Maestrano::Connector::Rails::IdMap.second}],
|
222
230
|
'connec2' => [{entity: mapped_entity2, idmap: Maestrano::Connector::Rails::IdMap.last}],
|
223
231
|
}
|
224
232
|
},
|
@@ -244,6 +252,75 @@ describe Maestrano::Connector::Rails::ComplexEntity do
|
|
244
252
|
end
|
245
253
|
end
|
246
254
|
|
255
|
+
describe 'external_inactive flagging' do
|
256
|
+
|
257
|
+
context 'idmaps external_inactive set to true' do
|
258
|
+
let!(:idmap1) { create(:idmap, organization: organization, external_id: id1, external_entity: 'sc_e1', connec_entity: 'connec1', external_inactive: true) }
|
259
|
+
let!(:idmap21) { create(:idmap, organization: organization, external_id: id1, external_entity: 'sce2', connec_entity: 'connec1', external_inactive: true) }
|
260
|
+
let!(:idmap22) { create(:idmap, organization: organization, external_id: id2, external_entity: 'sce2', connec_entity: 'connec2', external_inactive: true) }
|
261
|
+
|
262
|
+
context 'entities inactive' do
|
263
|
+
before {
|
264
|
+
allow(Entities::SubEntities::ScE2).to receive(:inactive_from_external_entity_hash?).and_return(true)
|
265
|
+
allow(Entities::SubEntities::ScE1).to receive(:inactive_from_external_entity_hash?).and_return(true)
|
266
|
+
}
|
267
|
+
|
268
|
+
it 'discards the entities' do
|
269
|
+
expect(subject.consolidate_and_map_data({}, external_hash, organization, opt)).to eql(external_entities: {
|
270
|
+
'sc_e1' => {'Connec1' => []},
|
271
|
+
'ScE2' => {
|
272
|
+
'Connec1' => [],
|
273
|
+
'connec2' => [],
|
274
|
+
}
|
275
|
+
},
|
276
|
+
connec_entities: {})
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
context 'entities active' do
|
281
|
+
before {
|
282
|
+
allow(Entities::SubEntities::ScE2).to receive(:inactive_from_external_entity_hash?).and_return(false)
|
283
|
+
allow(Entities::SubEntities::ScE1).to receive(:inactive_from_external_entity_hash?).and_return(false)
|
284
|
+
[idmap1, idmap21, idmap22].each{|i| i.update(last_push_to_connec: 1.second.ago)} #Used only to simplify specing
|
285
|
+
}
|
286
|
+
|
287
|
+
it 'updates the idmaps' do
|
288
|
+
subject.consolidate_and_map_data({}, external_hash, organization, opt)
|
289
|
+
expect(idmap1.reload.external_inactive).to be false
|
290
|
+
expect(idmap21.reload.external_inactive).to be false
|
291
|
+
expect(idmap22.reload.external_inactive).to be false
|
292
|
+
end
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
296
|
+
context 'idmaps external_inactive set to false' do
|
297
|
+
let!(:idmap1) { create(:idmap, organization: organization, external_id: id1, external_entity: 'sc_e1', connec_entity: 'connec1', external_inactive: false) }
|
298
|
+
let!(:idmap21) { create(:idmap, organization: organization, external_id: id1, external_entity: 'sce2', connec_entity: 'connec1', external_inactive: false) }
|
299
|
+
let!(:idmap22) { create(:idmap, organization: organization, external_id: id2, external_entity: 'sce2', connec_entity: 'connec2', external_inactive: false) }
|
300
|
+
|
301
|
+
context 'entities inactive' do
|
302
|
+
before {
|
303
|
+
allow(Entities::SubEntities::ScE2).to receive(:inactive_from_external_entity_hash?).and_return(true)
|
304
|
+
allow(Entities::SubEntities::ScE1).to receive(:inactive_from_external_entity_hash?).and_return(true)
|
305
|
+
}
|
306
|
+
|
307
|
+
it 'discards the entities and updates the idmaps' do
|
308
|
+
expect(subject.consolidate_and_map_data({}, external_hash, organization, opt)).to eql(external_entities: {
|
309
|
+
'sc_e1' => {'Connec1' => []},
|
310
|
+
'ScE2' => {
|
311
|
+
'Connec1' => [],
|
312
|
+
'connec2' => [],
|
313
|
+
}
|
314
|
+
},
|
315
|
+
connec_entities: {})
|
316
|
+
expect(idmap1.reload.external_inactive).to be true
|
317
|
+
expect(idmap21.reload.external_inactive).to be true
|
318
|
+
expect(idmap22.reload.external_inactive).to be true
|
319
|
+
end
|
320
|
+
end
|
321
|
+
end
|
322
|
+
end
|
323
|
+
|
247
324
|
context 'when entities have idmaps with more recent last_push_to_connec' do
|
248
325
|
let!(:idmap1) { create(:idmap, organization: organization, external_id: id1, external_entity: 'sc_e1', connec_entity: 'connec1', last_push_to_connec: 1.second.ago) }
|
249
326
|
let!(:idmap21) { create(:idmap, organization: organization, external_id: id1, external_entity: 'sce2', connec_entity: 'connec1', last_push_to_connec: 1.second.ago) }
|
data/spec/models/entity_spec.rb
CHANGED
@@ -474,6 +474,17 @@ describe Maestrano::Connector::Rails::Entity do
|
|
474
474
|
expect(subject.map_to_external_with_idmap(entity, organization)).to be_nil
|
475
475
|
end
|
476
476
|
end
|
477
|
+
|
478
|
+
context 'when external_inactive is true' do
|
479
|
+
let(:entity) { {'id' => id, 'name' => 'John' } }
|
480
|
+
before {
|
481
|
+
idmap.update(external_inactive: true)
|
482
|
+
}
|
483
|
+
|
484
|
+
it 'discards the entity' do
|
485
|
+
expect(subject.map_to_external_with_idmap(entity, organization)).to be_nil
|
486
|
+
end
|
487
|
+
end
|
477
488
|
end
|
478
489
|
|
479
490
|
context 'when entity has no idmap' do
|
@@ -706,6 +717,39 @@ describe Maestrano::Connector::Rails::Entity do
|
|
706
717
|
end
|
707
718
|
end
|
708
719
|
|
720
|
+
describe 'inactive flagging' do
|
721
|
+
context 'when idmap is inactive' do
|
722
|
+
let!(:idmap) { create(:idmap, external_entity: external_name.downcase, connec_entity: connec_name.downcase, external_id: id, organization: organization, external_inactive: true) }
|
723
|
+
|
724
|
+
context 'when entity is inactive' do
|
725
|
+
before { allow(subject.class).to receive(:inactive_from_external_entity_hash?).and_return(true) }
|
726
|
+
|
727
|
+
it 'discards the entity' do
|
728
|
+
expect(subject.consolidate_and_map_data([], entities, organization)).to eql({connec_entities: [], external_entities: []})
|
729
|
+
end
|
730
|
+
end
|
731
|
+
|
732
|
+
context 'when entity is active' do
|
733
|
+
before { allow(subject.class).to receive(:inactive_from_external_entity_hash?).and_return(false) }
|
734
|
+
|
735
|
+
it 'set the idmap as active' do
|
736
|
+
subject.consolidate_and_map_data([], entities, organization)
|
737
|
+
expect(idmap.reload.external_inactive).to be false
|
738
|
+
end
|
739
|
+
end
|
740
|
+
end
|
741
|
+
|
742
|
+
context 'when idmap is active and entity is inactive' do
|
743
|
+
let!(:idmap) { create(:idmap, external_entity: external_name.downcase, connec_entity: connec_name.downcase, external_id: id, organization: organization, external_inactive: false) }
|
744
|
+
before { allow(subject.class).to receive(:inactive_from_external_entity_hash?).and_return(true) }
|
745
|
+
|
746
|
+
it 'set the idmap as inactive and discards the entity' do
|
747
|
+
expect(subject.consolidate_and_map_data([], entities, organization)).to eql({connec_entities: [], external_entities: []})
|
748
|
+
expect(idmap.reload.external_inactive).to be true
|
749
|
+
end
|
750
|
+
end
|
751
|
+
end
|
752
|
+
|
709
753
|
context 'when entity has an idmap with a last_push_to_connec more recent than date' do
|
710
754
|
let!(:idmap) { create(:idmap, connec_entity: connec_name.downcase, external_entity: external_name.downcase, external_id: id, organization: organization, last_push_to_connec: 2.minute.ago) }
|
711
755
|
|
@@ -102,6 +102,47 @@ describe Maestrano::Connector::Rails::Organization do
|
|
102
102
|
describe 'from_omniauth' do
|
103
103
|
#TODO
|
104
104
|
end
|
105
|
+
|
106
|
+
describe 'last_three_synchronizations_failed?' do
|
107
|
+
it 'returns true when last three syncs are failed' do
|
108
|
+
3.times do
|
109
|
+
subject.synchronizations.create(status: 'ERROR')
|
110
|
+
end
|
111
|
+
expect(subject.last_three_synchronizations_failed?).to be true
|
112
|
+
end
|
113
|
+
|
114
|
+
it 'returns false when on of the last three sync is success' do
|
115
|
+
subject.synchronizations.create(status: 'SUCCESS')
|
116
|
+
2.times do
|
117
|
+
subject.synchronizations.create(status: 'ERROR')
|
118
|
+
end
|
119
|
+
|
120
|
+
expect(subject.last_three_synchronizations_failed?).to be false
|
121
|
+
end
|
122
|
+
|
123
|
+
it 'returns false when no sync' do
|
124
|
+
expect(subject.last_three_synchronizations_failed?).to be false
|
125
|
+
end
|
126
|
+
|
127
|
+
it 'returns false when less than three sync' do
|
128
|
+
2.times do
|
129
|
+
subject.synchronizations.create(status: 'ERROR')
|
130
|
+
end
|
131
|
+
|
132
|
+
expect(subject.last_three_synchronizations_failed?).to be false
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
describe 'last_successful_synchronization' do
|
137
|
+
let!(:running_sync) { create(:synchronization, organization: subject, status: 'RUNNING') }
|
138
|
+
let!(:failed_sync) { create(:synchronization, organization: subject, status: 'ERROR') }
|
139
|
+
let!(:success_sync) { create(:synchronization, organization: subject, status: 'SUCCESS') }
|
140
|
+
let!(:success_sync2) { create(:synchronization, organization: subject, status: 'SUCCESS', updated_at: 3.hours.ago) }
|
141
|
+
let!(:partial) { create(:synchronization, organization: subject, status: 'SUCCESS', partial: true) }
|
142
|
+
|
143
|
+
it { expect(subject.last_successful_synchronization).to eql(success_sync) }
|
144
|
+
end
|
105
145
|
end
|
106
146
|
|
147
|
+
|
107
148
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: maestrano-connector-rails
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Pierre Berard
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-04-
|
11
|
+
date: 2016-04-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: maestrano-rails
|
@@ -272,6 +272,7 @@ files:
|
|
272
272
|
- db/migrate/20151122163449_create_id_maps.rb
|
273
273
|
- db/migrate/20160205132857_add_sync_enabled_to_organizations.rb
|
274
274
|
- db/migrate/20160215103120_add_name_to_id_map.rb
|
275
|
+
- db/migrate/20160427112250_add_inactive_to_idmaps.rb
|
275
276
|
- lib/generators/connector/USAGE
|
276
277
|
- lib/generators/connector/complex_entity_generator.rb
|
277
278
|
- lib/generators/connector/install_generator.rb
|