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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: efabe776dea52e8b1601a0de62be567ad16961a1
4
- data.tar.gz: b2c779a4717875396a79a9dae1bd2d80017d0095
3
+ metadata.gz: 43df5981576854c5a7433146a874a38b6826a030
4
+ data.tar.gz: 865f0399ab96db0323b281f18ab80f7f38359e5d
5
5
  SHA512:
6
- metadata.gz: c064cc33f54e2a4571f93ba294b26ae52a889a678d7b5f7a26968ef3a25997d29108c61a6d0518c19142520e10642cf8b9f8279da08a77a3cb04a1c4b4b8bfde
7
- data.tar.gz: c0ad8eee085a0bf878786773bc61a7e698368c8cf57d0aa0651f8e51c1ed78f1aa9c382f45cd2c6c8c94111aefd780a9643608ea0f0ad8357c94170d51bdc534
6
+ metadata.gz: b9e01ee3b43e36ad310f5babf1819b9a215be1d325afbb378092213cdad72802cac6ae5cf77b4546189b4649a5b4635bf3af1a37c21ba19fa04552c9940b2eac
7
+ data.tar.gz: 77fbe67ebc5496d2f1b8e74ea9237b53fa3b880121fbc73b0a9b4265235c83833e79bdb418c77f4eb0ffdd3a0545af3decdd8cb470f2ad4ae708128cd580a901
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.4.1
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(:setting)
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
- if entity_instance_hash = find_entity_instance(entity_name)
9
- entity_instance = entity_instance_hash[:instance]
10
-
11
- entities.each do |entity|
12
- if (organization = Maestrano::Connector::Rails::Organization.find_by(uid: entity[:group_id], tenant: params[:tenant])) && organization.oauth_uid
13
- Maestrano::Connector::Rails::ConnectorLogger.log('info', organization, "Received entity from Connec! webhook: Entity=#{entity_name}, Data=#{entity}")
14
- if organization.sync_enabled && organization.synchronized_entities[entity_instance_hash[:name].to_sym]
15
- external_client = Maestrano::Connector::Rails::External.get_client(organization)
16
-
17
- # Build expected input for consolidate_and_map_data
18
- if entity_instance_hash[:is_complex]
19
- 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, {})
20
- else
21
- mapped_entity = entity_instance.consolidate_and_map_data([entity], [], organization, {})
22
- end
23
-
24
- entity_instance.push_entities_to_external(external_client, mapped_entity[:connec_entities], organization)
25
- end
26
-
27
- else
28
- Rails.logger.warn "Received notification from Connec! for unknown group or group without oauth: #{entity['group_id']} (tenant: #{params[:tenant]})"
29
- end
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
- else
32
- Rails.logger.info "Received notification from Connec! for unknow entity: #{entity_name}"
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 (for logging purposes only)
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 Synchronization.where(organization_id: organization.id, status: 'ERROR').order(created_at: :desc).limit(3).count == 3 \
21
- && Synchronization.where(organization_id: organization.id).order(created_at: :desc).limit(1).first.updated_at > 1.day.ago
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 = Synchronization.where(organization_id: organization.id, status: 'SUCCESS', partial: false).order(updated_at: :desc).first
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.update(name: sub_entity_instance.class.object_name_from_connec_entity_hash(entity))
60
- if (!idmap.to_external) || idmap.last_push_to_external && idmap.last_push_to_external > entity['updated_at']
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
- if idmap
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.update(name: self.class.object_name_from_connec_entity_hash(entity))
290
- if (!idmap.to_external) || (idmap.last_push_to_external && idmap.last_push_to_external > entity['updated_at'])
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
- if idmap
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
@@ -0,0 +1,5 @@
1
+ class AddInactiveToIdmaps < ActiveRecord::Migration
2
+ def change
3
+ add_column :id_maps, :external_inactive, :boolean, default: false
4
+ end
5
+ 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.1 ruby lib
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.1"
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-19"
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
@@ -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: 20160215103120) do
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", null: false
25
- t.datetime "updated_at", null: false
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 syncrhonized entities' do
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
- subject { Maestrano::Connector::Rails::SynchronizationJob.perform_now(organization, {}) }
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 'does not creates a syncrhonization' do
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
- describe 'skipping' do
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
- it 'skipped the sync if 3 failed sync' do
31
- expect{ subject }.to_not change{ Maestrano::Connector::Rails::Synchronization.count }
44
+ context 'synchronization is forced' do
45
+ let(:opts) { {forced: true} }
46
+ it { performes }
32
47
  end
33
48
  end
34
49
 
35
- describe 'not skipping' do
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
- it 'does not skip the sync if 3 failed sync but last sync more than a day ago' do
43
- expect{ subject }.to change{ Maestrano::Connector::Rails::Synchronization.count }.by(1)
44
- end
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 'creates a synchronization' do
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
- subject { Maestrano::Connector::Rails::SynchronizationJob.perform_now(organization, {only_entities: %w(people price)}) }
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.all[1]}],
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) }
@@ -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.1
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-19 00:00:00.000000000 Z
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