maestrano-connector-rails 0.4.1 → 0.4.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|