maestrano-connector-rails 0.4.4 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +2 -0
  3. data/README.md +1 -1
  4. data/VERSION +1 -1
  5. data/app/controllers/maestrano/connec_controller.rb +17 -19
  6. data/app/jobs/maestrano/connector/rails/all_synchronizations_job.rb +1 -1
  7. data/app/jobs/maestrano/connector/rails/push_to_connec_job.rb +11 -11
  8. data/app/jobs/maestrano/connector/rails/synchronization_job.rb +20 -11
  9. data/app/models/maestrano/connector/rails/concerns/complex_entity.rb +66 -74
  10. data/app/models/maestrano/connector/rails/concerns/connec_helper.rb +102 -0
  11. data/app/models/maestrano/connector/rails/concerns/entity.rb +233 -231
  12. data/app/models/maestrano/connector/rails/concerns/external.rb +7 -0
  13. data/app/models/maestrano/connector/rails/concerns/sub_entity_base.rb +14 -43
  14. data/app/models/maestrano/connector/rails/connec_helper.rb +5 -0
  15. data/app/models/maestrano/connector/rails/organization.rb +8 -2
  16. data/db/20160524112054_add_encryption_on_oauth_keys.rb +8 -0
  17. data/lib/generators/connector/install_generator.rb +1 -0
  18. data/lib/generators/connector/templates/complex_entity_example/contact.rb +1 -1
  19. data/lib/generators/connector/templates/complex_entity_example/contact_and_lead.rb +2 -2
  20. data/lib/generators/connector/templates/entity.rb +13 -19
  21. data/lib/generators/connector/templates/example_entity.rb +2 -2
  22. data/lib/generators/connector/templates/example_entity_spec.rb +73 -0
  23. data/lib/generators/connector/templates/external.rb +11 -0
  24. data/maestrano-connector-rails.gemspec +13 -8
  25. data/release_notes.md +81 -0
  26. data/spec/controllers/connec_controller_spec.rb +19 -6
  27. data/spec/dummy/app/models/maestrano/connector/rails/entity.rb +0 -7
  28. data/spec/dummy/app/models/maestrano/connector/rails/external.rb +7 -0
  29. data/spec/dummy/app/views/home/index.html.erb +1 -36
  30. data/spec/factories.rb +3 -0
  31. data/spec/integration/connec_to_external_spec.rb +188 -0
  32. data/spec/integration/external_to_connec_spec.rb +155 -0
  33. data/spec/integration/integration_complex_spec.rb +281 -0
  34. data/spec/integration/singleton_spec.rb +288 -0
  35. data/spec/jobs/all_synchronizations_job_spec.rb +5 -0
  36. data/spec/jobs/push_to_connec_job_spec.rb +3 -6
  37. data/spec/jobs/synchronization_job_spec.rb +29 -17
  38. data/spec/models/complex_entity_spec.rb +257 -412
  39. data/spec/models/connec_helper_spec.rb +143 -0
  40. data/spec/models/entity_spec.rb +420 -348
  41. data/spec/models/external_spec.rb +4 -0
  42. data/spec/models/organization_spec.rb +2 -1
  43. data/spec/models/sub_entity_base_spec.rb +28 -69
  44. data/template/factories.rb +3 -1
  45. data/template/maestrano-connector-template.rb +11 -13
  46. data/template/maestrano.rb +2 -1
  47. data/template/settings/development.yml +4 -2
  48. data/template/settings/production.yml +1 -11
  49. data/template/settings/settings.yml +8 -0
  50. data/template/settings/test.yml +2 -0
  51. data/template/settings/uat.yml +1 -9
  52. metadata +12 -7
  53. data/Gemfile.lock +0 -256
  54. data/realse_notes.md +0 -16
  55. data/spec/dummy/app/views/admin/index.html.erb +0 -51
  56. data/spec/dummy/db/development.sqlite3 +0 -0
  57. data/spec/dummy/db/test.sqlite3 +0 -0
@@ -0,0 +1,143 @@
1
+ require 'spec_helper'
2
+
3
+ describe Maestrano::Connector::Rails::ConnecHelper do
4
+ subject { Maestrano::Connector::Rails::ConnecHelper }
5
+
6
+ let!(:organization) { create(:organization) }
7
+
8
+ describe 'unfold_references' do
9
+ let(:connec_hash) {
10
+ {
11
+ id: id,
12
+ organization_id: org_id,
13
+ lines: [
14
+ {
15
+ linked_transaction: {
16
+ id: lt1_id
17
+ }
18
+ },
19
+ {
20
+ linked_transaction: {
21
+ id: lt2_id
22
+ }
23
+ }
24
+ ]
25
+ }
26
+ }
27
+
28
+ let(:output_hash) {
29
+ {
30
+ __connec_id: 'abcd',
31
+ id: id_id,
32
+ organization_id: org_id_id,
33
+ lines: [
34
+ {
35
+ linked_transaction: {
36
+ id: lt1_id_id
37
+ }
38
+ },
39
+ {
40
+ linked_transaction: {
41
+ id: lt2_id_id
42
+ }
43
+ }
44
+ ]
45
+ }
46
+ }
47
+ let(:lt1_id_id) { 'lt1_id' }
48
+ let(:lt2_id_id) { 'lt2_id' }
49
+ let(:lt1_id) { [subject.id_hash(lt1_id_id, organization)] }
50
+ let(:lt2_id) { [subject.id_hash(lt2_id_id, organization)] }
51
+
52
+ context 'when all ids are here' do
53
+ let(:id_id) { 'id' }
54
+ let(:org_id_id) { 'org_id' }
55
+ let(:id) { [subject.id_hash(id_id, organization), {'provider' => 'connec', 'id' => 'abcd'}] }
56
+ let(:org_id) { [subject.id_hash(org_id_id, organization), {'provider' => 'connec', 'id' => 'abcd'}] }
57
+
58
+ it 'unfolds everything' do
59
+ expect(subject.unfold_references(connec_hash, ['organization_id', 'lines/linked_transaction/id'], organization)).to eql(output_hash.with_indifferent_access)
60
+ end
61
+ end
62
+
63
+ context 'when only id is missing' do
64
+ let(:id_id) { nil }
65
+ let(:org_id_id) { 'org_id' }
66
+ let(:id) { [{'provider' => 'connec', 'realm' => 'some realm', 'id' => 'id'}] }
67
+ let(:org_id) { [subject.id_hash(org_id_id, organization)] }
68
+
69
+ it 'unfolds the other refs and keep the connec_id' do
70
+ expect(subject.unfold_references(connec_hash, ['organization_id', 'lines/linked_transaction/id'], organization)).to eql(output_hash.merge(__connec_id: 'id').with_indifferent_access)
71
+ end
72
+ end
73
+
74
+ context 'when at least one ref is missing and there is a connec id' do
75
+ let(:id_id) { nil }
76
+ let(:org_id_id) { 'org_id' }
77
+ let(:id) { [{'provider' => 'connec', 'realm' => 'some realm', 'id' => 'id'}] }
78
+ let(:org_id) { [{'provider' => 'connec', 'realm' => 'some realm', 'id' => org_id_id}] }
79
+
80
+ it 'returns nil' do
81
+ expect(subject.unfold_references(connec_hash, ['organization_id', 'lines/linked_transaction/id'], organization)).to be_nil
82
+ end
83
+ end
84
+ context 'when at least one ref is missing but there is no connec id' do
85
+ let(:id_id) { 'id' }
86
+ let(:id) { [subject.id_hash(id_id, organization), {'provider' => 'connec', 'id' => 'abcd'}] }
87
+ let(:org_id_id) { nil }
88
+ let(:org_id) { nil }
89
+
90
+ it 'unfold the others refs' do
91
+ expect(subject.unfold_references(connec_hash, ['organization_id', 'lines/linked_transaction/id'], organization)).to eql(output_hash.merge(organization_id: nil).with_indifferent_access)
92
+ end
93
+ end
94
+ end
95
+
96
+ describe 'fold_references' do
97
+ let(:mapped_hash) {
98
+ {
99
+ id: 'id1',
100
+ organization_id: nil,
101
+ contact: {
102
+ id: ''
103
+ },
104
+ lines: [
105
+ {
106
+ id: 'id2'
107
+ },
108
+ {
109
+ id: 'id3'
110
+ }
111
+ ]
112
+ }
113
+ }
114
+
115
+ let(:output_hash) {
116
+ {
117
+ "id" => [
118
+ subject.id_hash('id1', organization)
119
+ ],
120
+ "organization_id" => nil,
121
+ "contact" => {
122
+ "id" => ""
123
+ },
124
+ "lines" => [
125
+ {
126
+ "id" => [
127
+ subject.id_hash('id2', organization)
128
+ ]
129
+ },
130
+ {
131
+ "id" => [
132
+ subject.id_hash('id3', organization)
133
+ ]
134
+ }
135
+ ]
136
+ }
137
+ }
138
+
139
+ it 'folds the existing refs' do
140
+ expect(subject.fold_references(mapped_hash, ['organization_id', 'contact/id', 'lines/id', 'not_here_ref'], organization)).to eql(output_hash.with_indifferent_access)
141
+ end
142
+ end
143
+ end
@@ -5,10 +5,6 @@ describe Maestrano::Connector::Rails::Entity do
5
5
  describe 'class methods' do
6
6
  subject { Maestrano::Connector::Rails::Entity }
7
7
 
8
- describe 'entities_list' do
9
- it { expect(subject.entities_list).to eql(%w(entity1 entity2))}
10
- end
11
-
12
8
  # IdMap methods
13
9
  describe 'idmaps mehtods' do
14
10
  before {
@@ -18,32 +14,21 @@ describe Maestrano::Connector::Rails::Entity do
18
14
  let(:n_hash) { {connec_entity: 'ab', external_entity: 'ab'} }
19
15
 
20
16
  it { expect(subject.names_hash).to eql(n_hash) }
17
+
21
18
  it {
22
19
  expect(Maestrano::Connector::Rails::IdMap).to receive(:find_or_create_by).with(n_hash.merge(id: 'lala'))
23
20
  subject.find_or_create_idmap({id: 'lala'})
24
21
  }
22
+
25
23
  it {
26
24
  expect(Maestrano::Connector::Rails::IdMap).to receive(:find_by).with(n_hash.merge(id: 'lala'))
27
25
  subject.find_idmap({id: 'lala'})
28
26
  }
29
- describe 'creates' do
30
- let(:organization) { create(:organization) }
31
- let(:connec_entity) { {'id' => 'lala'} }
32
- before {
33
- allow(subject).to receive(:object_name_from_external_entity_hash).and_return('name_e')
34
- allow(subject).to receive(:object_name_from_connec_entity_hash).and_return('name_c')
35
- allow(subject).to receive(:id_from_external_entity_hash).and_return('id')
36
- }
37
27
 
38
- it {
39
- expect(Maestrano::Connector::Rails::IdMap).to receive(:create).with(n_hash.merge(connec_id: 'lala', name: 'name_c', organization_id: organization.id))
40
- subject.create_idmap_from_connec_entity(connec_entity, organization)
41
- }
42
- it {
43
- expect(Maestrano::Connector::Rails::IdMap).to receive(:create).with(n_hash.merge(external_id: 'id', name: 'name_e', organization_id: organization.id))
44
- subject.create_idmap_from_external_entity({}, organization)
45
- }
46
- end
28
+ it {
29
+ expect(Maestrano::Connector::Rails::IdMap).to receive(:create).with(n_hash.merge(id: 'lala'))
30
+ subject.create_idmap({id: 'lala'})
31
+ }
47
32
  end
48
33
 
49
34
  describe 'normalized_connec_entity_name' do
@@ -117,10 +102,24 @@ describe Maestrano::Connector::Rails::Entity do
117
102
  describe 'object_name_from_external_entity_hash' do
118
103
  it { expect{ subject.object_name_from_external_entity_hash({}) }.to raise_error('Not implemented') }
119
104
  end
105
+
106
+ describe 'connec_matching_fields' do
107
+ it { expect(subject.connec_matching_fields).to be_nil }
108
+ end
120
109
  end
121
110
 
122
111
  describe 'instance methods' do
123
- subject { Maestrano::Connector::Rails::Entity.new }
112
+ let!(:organization) { create(:organization, uid: 'cld-123') }
113
+ let!(:connec_client) { Maestrano::Connec::Client[organization.tenant].new(organization.uid) }
114
+ let!(:external_client) { Object.new }
115
+ let(:opts) { {} }
116
+ subject { Maestrano::Connector::Rails::Entity.new(organization, connec_client, external_client, opts) }
117
+ let(:connec_name) { 'Person' }
118
+ let(:external_name) { 'external_name' }
119
+ before {
120
+ allow(subject.class).to receive(:connec_entity_name).and_return(connec_name)
121
+ allow(subject.class).to receive(:external_entity_name).and_return(external_name)
122
+ }
124
123
 
125
124
  describe 'Mapper methods' do
126
125
  before(:each) {
@@ -133,136 +132,135 @@ describe Maestrano::Connector::Rails::Entity do
133
132
  describe 'map_to_external' do
134
133
  it 'calls the mapper normalize' do
135
134
  expect(AMapper).to receive(:normalize).with({}).and_return({})
136
- subject.map_to_external({}, nil)
135
+ subject.map_to_external({})
137
136
  end
138
137
 
139
- context 'with references' do
140
- let!(:organization) { create(:organization) }
141
- let!(:idmap) { create(:idmap, organization: organization) }
142
- before {
143
- clazz = Maestrano::Connector::Rails::Entity
144
- allow(clazz).to receive(:find_idmap).and_return(idmap)
145
- allow(clazz).to receive(:references).and_return([{reference_class: clazz, connec_field: 'organization_id', external_field: 'contact_id'}])
146
- }
147
-
148
- it 'returns the mapped entity with its references' do
149
- expect(subject.map_to_external({'organization_id' => 'abcd'}, organization)).to eql({contact_id: idmap.external_id})
150
- end
138
+ it 'preserve the __connec_id' do
139
+ expect(subject.map_to_external({__connec_id: 'connec id'})).to eql({__connec_id: 'connec id'}.with_indifferent_access)
151
140
  end
152
141
  end
153
142
 
154
143
  describe 'map_to_connec' do
144
+ before {
145
+ allow(subject.class).to receive(:id_from_external_entity_hash).and_return('this id')
146
+ }
155
147
  it 'calls the mapper denormalize' do
156
148
  expect(AMapper).to receive(:denormalize).with({}).and_return({})
157
- subject.map_to_connec({}, nil)
149
+ subject.map_to_connec({})
158
150
  end
159
151
 
160
- context 'with references' do
161
- let!(:organization) { create(:organization) }
162
- let!(:idmap) { create(:idmap, organization: organization) }
163
- before {
164
- clazz = Maestrano::Connector::Rails::Entity
165
- allow(clazz).to receive(:find_idmap).and_return(idmap)
166
- allow(clazz).to receive(:references).and_return([{reference_class: clazz, connec_field: 'organization_id', external_field: 'contact_id'}])
167
- }
152
+ it 'calls for reference folding' do
153
+ refs = %w(organization_id person_id)
154
+ allow(subject.class).to receive(:references).and_return(refs)
155
+ expect(Maestrano::Connector::Rails::ConnecHelper).to receive(:fold_references).with({id: 'this id'}, refs, organization)
156
+ subject.map_to_connec({})
157
+ end
168
158
 
169
- it 'returns the mapped entity with its references' do
170
- expect(subject.map_to_connec({'contact_id' => 'abcd'}, organization)).to eql({organization_id: idmap.connec_id})
171
- end
159
+ it 'merges the smart merging options' do
160
+ allow(AMapper).to receive(:denormalize).and_return({opts: {some_opt: 4}})
161
+ allow(subject.class).to receive(:connec_matching_fields).and_return([['first_name'], ['last_name']])
162
+ expect(subject.map_to_connec({})).to eql({id: [{id: 'this id', provider: organization.oauth_provider, realm: organization.oauth_uid}], opts: {some_opt: 4, matching_fields: [['first_name'], ['last_name']]}}.with_indifferent_access)
172
163
  end
173
164
  end
174
165
  end
175
166
 
176
167
  # Connec! methods
177
168
  describe 'connec_methods' do
178
- let(:organization) { create(:organization) }
179
- let(:client) { Maestrano::Connec::Client.new(organization.uid) }
180
- let(:connec_name) { 'Person' }
181
- let(:external_name) { 'external_name' }
182
169
  let(:sync) { create(:synchronization) }
183
- before {
184
- allow(subject.class).to receive(:connec_entity_name).and_return(connec_name)
185
- allow(subject.class).to receive(:external_entity_name).and_return(external_name)
186
- }
170
+
171
+ describe 'filter_connec_entities' do
172
+ it 'does nothing by default' do
173
+ expect(subject.filter_connec_entities({a: 2})).to eql({a: 2})
174
+ end
175
+ end
187
176
 
188
177
  describe 'get_connec_entities' do
189
178
  describe 'when write only' do
190
179
  before {
191
180
  allow(subject.class).to receive(:can_read_connec?).and_return(false)
192
- allow(client).to receive(:get).and_return(ActionDispatch::Response.new(200, {}, {people: [{first_name: 'Lea'}]}.to_json, {}))
181
+ allow(connec_client).to receive(:get).and_return(ActionDispatch::Response.new(200, {}, {people: [{first_name: 'Lea'}]}.to_json, {}))
193
182
  }
194
183
 
195
- it { expect(subject.get_connec_entities(client, nil, organization)).to eql([]) }
184
+ it { expect(subject.get_connec_entities(nil)).to eql([]) }
185
+ end
186
+
187
+ describe 'when skip_connec' do
188
+ let(:opts) { {skip_connec: true} }
189
+ it { expect(subject.get_connec_entities(nil)).to eql([]) }
196
190
  end
197
191
 
198
192
  describe 'with response' do
199
193
  context 'for a singleton resource' do
200
194
  before {
201
- allow(client).to receive(:get).and_return(ActionDispatch::Response.new(200, {}, {person: []}.to_json, {}))
195
+ allow(connec_client).to receive(:get).and_return(ActionDispatch::Response.new(200, {}, {person: []}.to_json, {}))
202
196
  allow(subject.class).to receive(:singleton?).and_return(true)
203
197
  }
204
198
 
205
199
  it 'calls get with a singularize url' do
206
- expect(client).to receive(:get).with("/#{connec_name.downcase}?")
207
- subject.get_connec_entities(client, nil, organization)
200
+ expect(connec_client).to receive(:get).with("/#{connec_name.downcase}?")
201
+ subject.get_connec_entities(nil)
208
202
  end
209
203
  end
210
204
 
211
205
  context 'for a non singleton resource' do
212
206
  before {
213
- allow(client).to receive(:get).and_return(ActionDispatch::Response.new(200, {}, {people: []}.to_json, {}))
207
+ allow(connec_client).to receive(:get).and_return(ActionDispatch::Response.new(200, {}, {people: []}.to_json, {}))
214
208
  }
215
209
 
216
210
  context 'when opts[:full_sync] is true' do
211
+ let(:opts) { {full_sync: true} }
217
212
  it 'performs a full get' do
218
- expect(client).to receive(:get).with("/#{connec_name.downcase.pluralize}?")
219
- subject.get_connec_entities(client, sync, organization, {full_sync: true})
213
+ expect(connec_client).to receive(:get).with("/#{connec_name.downcase.pluralize}?")
214
+ subject.get_connec_entities(sync)
220
215
  end
221
216
  end
222
217
 
223
218
  context 'when there is no last sync' do
224
219
  it 'performs a full get' do
225
- expect(client).to receive(:get).with("/#{connec_name.downcase.pluralize}?")
226
- subject.get_connec_entities(client, nil, organization)
220
+ expect(connec_client).to receive(:get).with("/#{connec_name.downcase.pluralize}?")
221
+ subject.get_connec_entities(nil)
227
222
  end
228
223
  end
229
224
 
230
225
  context 'when there is a last sync' do
231
226
  it 'performs a time limited get' do
232
227
  uri_param = {"$filter" => "updated_at gt '#{sync.updated_at.iso8601}'"}.to_query
233
- expect(client).to receive(:get).with("/#{connec_name.downcase.pluralize}?#{uri_param}")
234
- subject.get_connec_entities(client, sync, organization)
228
+ expect(connec_client).to receive(:get).with("/#{connec_name.downcase.pluralize}?#{uri_param}")
229
+ subject.get_connec_entities(sync)
235
230
  end
236
231
  end
237
232
 
238
233
  context 'with options' do
239
234
  it 'support filter option for full sync' do
235
+ subject.instance_variable_set(:@opts, {full_sync: true, :$filter => "code eq 'PEO12'"})
240
236
  uri_param = {'$filter'=>'code eq \'PEO12\''}.to_query
241
- expect(client).to receive(:get).with("/#{connec_name.downcase.pluralize}?#{uri_param}")
242
- subject.get_connec_entities(client, sync, organization, {full_sync: true, :$filter => "code eq 'PEO12'"})
237
+ expect(connec_client).to receive(:get).with("/#{connec_name.downcase.pluralize}?#{uri_param}")
238
+ subject.get_connec_entities(sync)
243
239
  end
244
240
 
245
241
  it 'support filter option for time limited sync' do
242
+ subject.instance_variable_set(:@opts, {:$filter => "code eq 'PEO12'"})
246
243
  uri_param = {"$filter"=>"updated_at gt '#{sync.updated_at.iso8601}' and code eq 'PEO12'"}.to_query
247
- expect(client).to receive(:get).with("/#{connec_name.downcase.pluralize}?#{uri_param}")
248
- subject.get_connec_entities(client, sync, organization, {:$filter => "code eq 'PEO12'"})
244
+ expect(connec_client).to receive(:get).with("/#{connec_name.downcase.pluralize}?#{uri_param}")
245
+ subject.get_connec_entities(sync)
249
246
  end
250
247
 
251
248
  it 'support orderby option for time limited sync' do
249
+ subject.instance_variable_set(:@opts, {:$orderby => "name asc"})
252
250
  uri_param = {"$orderby"=>"name asc", "$filter"=>"updated_at gt '#{sync.updated_at.iso8601}'"}.to_query
253
- expect(client).to receive(:get).with("/#{connec_name.downcase.pluralize}?#{uri_param}")
254
- subject.get_connec_entities(client, sync, organization, {:$orderby => "name asc"})
251
+ expect(connec_client).to receive(:get).with("/#{connec_name.downcase.pluralize}?#{uri_param}")
252
+ subject.get_connec_entities(sync)
255
253
  end
256
254
  end
257
255
 
258
256
  context 'with pagination' do
259
257
  before {
260
- allow(client).to receive(:get).and_return(ActionDispatch::Response.new(200, {}, {people: [], pagination: {next: "https://api-connec.maestrano.com/api/v2/cld-dkg601/people?%24skip=10&%24top=10"}}.to_json, {}), ActionDispatch::Response.new(200, {}, {people: []}.to_json, {}))
258
+ allow(connec_client).to receive(:get).and_return(ActionDispatch::Response.new(200, {}, {people: [], pagination: {next: "https://api-connec.maestrano.com/api/v2/cld-dkg601/people?%24skip=10&%24top=10"}}.to_json, {}), ActionDispatch::Response.new(200, {}, {people: []}.to_json, {}))
261
259
  }
262
260
 
263
261
  it 'calls get multiple times' do
264
- expect(client).to receive(:get).twice
265
- subject.get_connec_entities(client, nil, organization)
262
+ expect(connec_client).to receive(:get).twice
263
+ subject.get_connec_entities(nil)
266
264
  end
267
265
  end
268
266
 
@@ -270,39 +268,59 @@ describe Maestrano::Connector::Rails::Entity do
270
268
  let(:people) { [{first_name: 'John'}, {last_name: 'Durand'}, {job_title: 'Engineer'}] }
271
269
 
272
270
  it 'returns an array of entities' do
273
- allow(client).to receive(:get).and_return(ActionDispatch::Response.new(200, {}, {people: people}.to_json, {}))
274
- expect(subject.get_connec_entities(client, nil, organization)).to eql(JSON.parse(people.to_json))
271
+ allow(connec_client).to receive(:get).and_return(ActionDispatch::Response.new(200, {}, {people: people}.to_json, {}))
272
+ expect(subject.get_connec_entities(nil)).to eql(JSON.parse(people.to_json))
275
273
  end
276
274
  end
277
275
  end
278
276
  end
279
277
 
280
- describe 'without response' do
281
- before {
282
- allow(client).to receive(:get).and_return(ActionDispatch::Response.new(200, {}, nil, {}))
283
- }
284
- it { expect{ subject.get_connec_entities(client, nil, organization) }.to raise_error("No data received from Connec! when trying to fetch #{connec_name.pluralize.downcase}") }
278
+ describe 'failures' do
279
+ context 'when no response' do
280
+ before {
281
+ allow(connec_client).to receive(:get).and_return(ActionDispatch::Response.new(200, {}, nil, {}))
282
+ }
283
+ it { expect{ subject.get_connec_entities(nil) }.to raise_error(RuntimeError) }
284
+ end
285
+
286
+ context 'when invalid response' do
287
+ before {
288
+ allow(connec_client).to receive(:get).and_return(ActionDispatch::Response.new(200, {}, {not_an_entity: []}.to_json, {}))
289
+ }
290
+ it { expect{ subject.get_connec_entities(nil) }.to raise_error(RuntimeError) }
291
+ end
292
+
293
+ context 'when no response in pagination' do
294
+ before {
295
+ allow(connec_client).to receive(:get).and_return(ActionDispatch::Response.new(200, {}, {people: [], pagination: {next: "https://api-connec.maestrano.com/api/v2/cld-dkg601/people?%24skip=10&%24top=10"}}.to_json, {}), ActionDispatch::Response.new(200, {}, nil, {}))
296
+ }
297
+ it { expect{ subject.get_connec_entities(nil) }.to raise_error(RuntimeError) }
298
+ end
299
+
300
+ context 'when invalid response in pagination' do
301
+ before {
302
+ allow(connec_client).to receive(:get).and_return(ActionDispatch::Response.new(200, {}, {people: [], pagination: {next: "https://api-connec.maestrano.com/api/v2/cld-dkg601/people?%24skip=10&%24top=10"}}.to_json, {}), ActionDispatch::Response.new(200, {}, {not_an_entity: []}.to_json, {}))
303
+ }
304
+ it { expect{ subject.get_connec_entities(nil) }.to raise_error(RuntimeError) }
305
+ end
285
306
  end
286
307
  end
287
308
 
288
309
  describe 'push_entities_to_connec' do
289
310
  it 'calls push_entities_to_connec_to' do
290
- allow(subject).to receive(:connec_entity_name).and_return(connec_name)
291
- expect(subject).to receive(:push_entities_to_connec_to).with(client, [{entity: {}, idmap: nil}], connec_name, nil)
292
- subject.push_entities_to_connec(client, [{entity: {}, idmap: nil}], nil)
311
+ expect(subject).to receive(:push_entities_to_connec_to).with([{entity: {}, idmap: nil}], connec_name)
312
+ subject.push_entities_to_connec([{entity: {}, idmap: nil}])
293
313
  end
294
314
  end
295
315
 
296
316
  describe 'push_entities_to_connec_to' do
297
- let(:organization) { create(:organization, uid: 'cld-123') }
298
317
  let(:idmap1) { create(:idmap, organization: organization) }
299
- let(:idmap2) { create(:idmap, organization: organization, connec_id: nil, last_push_to_connec: nil) }
318
+ let(:idmap2) { create(:idmap, organization: organization, last_push_to_connec: nil) }
300
319
  let(:entity1) { {name: 'John'} }
301
320
  let(:entity2) { {name: 'Jane'} }
302
321
  let(:entity_with_idmap1) { {entity: entity1, idmap: idmap1} }
303
322
  let(:entity_with_idmap2) { {entity: entity2, idmap: idmap2} }
304
323
  let(:entities_with_idmaps) { [entity_with_idmap1, entity_with_idmap2] }
305
- let(:id) { 'ab12-34re' }
306
324
 
307
325
  context 'when read only' do
308
326
  before {
@@ -311,28 +329,15 @@ describe Maestrano::Connector::Rails::Entity do
311
329
 
312
330
  it 'does nothing' do
313
331
  expect(subject).to_not receive(:batch_op)
314
- subject.push_entities_to_connec_to(client, entities_with_idmaps, connec_name, organization)
315
- end
316
- end
317
-
318
- context 'when create_only' do
319
- before {
320
- allow(subject.class).to receive(:can_update_connec?).and_return(false)
321
- allow(client).to receive(:batch).and_return(ActionDispatch::Response.new(200, {}, {results: []}.to_json, {}))
322
- }
323
-
324
- it 'creates batch op for create only' do
325
- expect(subject).to receive(:batch_op).once.with('post', entity2, nil, connec_name.downcase.pluralize, organization)
326
- expect(subject).to_not receive(:batch_op).with('put', any_args)
327
- subject.push_entities_to_connec_to(client, entities_with_idmaps, connec_name, organization)
332
+ subject.push_entities_to_connec_to(entities_with_idmaps, connec_name)
328
333
  end
329
334
  end
330
335
 
331
336
  context 'without errors' do
332
- let(:result200) { {status: 200, body: {connec_name.downcase.pluralize.to_sym => {}}} }
333
- let(:result201) { {status: 201, body: {connec_name.downcase.pluralize.to_sym => {id: id}}} }
337
+ let(:result200) { {status: 200, body: {connec_name.downcase.pluralize.to_sym => {id: [{provider: 'connec', id: 'id1'}]}}} }
338
+ let(:result201) { {status: 201, body: {connec_name.downcase.pluralize.to_sym => {id: [{provider: 'connec', id: 'id2'}]}}} }
334
339
  before {
335
- allow(client).to receive(:batch).and_return(ActionDispatch::Response.new(200, {}, {results: [result200, result201]}.to_json, {}))
340
+ allow(connec_client).to receive(:batch).and_return(ActionDispatch::Response.new(200, {}, {results: [result200, result201]}.to_json, {}))
336
341
  }
337
342
 
338
343
  let(:batch_request) {
@@ -340,13 +345,13 @@ describe Maestrano::Connector::Rails::Entity do
340
345
  sequential: true,
341
346
  ops: [
342
347
  {
343
- :method=>"put",
344
- :url=>"/api/v2/cld-123/people/#{idmap1.connec_id}",
348
+ :method=>"post",
349
+ :url=>"/api/v2/cld-123/people/",
345
350
  :params=>{:people=>{:name=>"John"}}
346
351
  },
347
352
  {
348
353
  :method=>"post",
349
- :url=>"/api/v2/cld-123/people",
354
+ :url=>"/api/v2/cld-123/people/",
350
355
  :params=>{:people=>{:name=>"Jane"}}
351
356
  }
352
357
  ]
@@ -355,23 +360,22 @@ describe Maestrano::Connector::Rails::Entity do
355
360
 
356
361
  it 'calls batch op' do
357
362
  expect(subject).to receive(:batch_op).twice
358
- subject.push_entities_to_connec_to(client, entities_with_idmaps, connec_name, organization)
363
+ subject.push_entities_to_connec_to(entities_with_idmaps, connec_name)
359
364
  end
360
365
 
361
366
  it 'creates a batch request' do
362
- expect(client).to receive(:batch).with(batch_request)
363
- subject.push_entities_to_connec_to(client, entities_with_idmaps, connec_name, organization)
367
+ expect(connec_client).to receive(:batch).with(batch_request)
368
+ subject.push_entities_to_connec_to(entities_with_idmaps, connec_name)
364
369
  end
365
370
 
366
- it 'update the idmaps' do
371
+ it 'update the idmaps push dates' do
367
372
  old_push_date = idmap1.last_push_to_connec
368
373
 
369
- subject.push_entities_to_connec_to(client, entities_with_idmaps, connec_name, organization)
374
+ subject.push_entities_to_connec_to(entities_with_idmaps, connec_name)
370
375
 
371
376
  idmap1.reload
372
377
  expect(idmap1.last_push_to_connec).to_not eql(old_push_date)
373
378
  idmap2.reload
374
- expect(idmap2.connec_id).to eql(id)
375
379
  expect(idmap2.last_push_to_connec).to_not be_nil
376
380
  end
377
381
 
@@ -385,12 +389,12 @@ describe Maestrano::Connector::Rails::Entity do
385
389
  entities << entity_with_idmap1
386
390
  results << result200
387
391
  end
388
- allow(client).to receive(:batch).and_return(ActionDispatch::Response.new(200, {}, {results: results}.to_json, {}))
392
+ allow(connec_client).to receive(:batch).and_return(ActionDispatch::Response.new(200, {}, {results: results}.to_json, {}))
389
393
  }
390
394
 
391
395
  it 'does one call' do
392
- expect(client).to receive(:batch).once
393
- subject.push_entities_to_connec_to(client, entities, connec_name, organization)
396
+ expect(connec_client).to receive(:batch).once
397
+ subject.push_entities_to_connec_to(entities, connec_name)
394
398
  end
395
399
  end
396
400
 
@@ -401,18 +405,17 @@ describe Maestrano::Connector::Rails::Entity do
401
405
  results << result200
402
406
  end
403
407
  entities << entity_with_idmap2
404
- allow(client).to receive(:batch).and_return(ActionDispatch::Response.new(200, {}, {results: results}.to_json, {}), ActionDispatch::Response.new(200, {}, {results: [result201]}.to_json, {}))
408
+ allow(connec_client).to receive(:batch).and_return(ActionDispatch::Response.new(200, {}, {results: results}.to_json, {}), ActionDispatch::Response.new(200, {}, {results: [result201]}.to_json, {}))
405
409
  }
406
410
 
407
411
  it 'does several call' do
408
- expect(client).to receive(:batch).twice
409
- subject.push_entities_to_connec_to(client, entities, connec_name, organization)
412
+ expect(connec_client).to receive(:batch).twice
413
+ subject.push_entities_to_connec_to(entities, connec_name)
410
414
  end
411
415
 
412
- it 'updates the idmap' do
413
- subject.push_entities_to_connec_to(client, entities, connec_name, organization)
416
+ it 'updates the idmap push dates' do
417
+ subject.push_entities_to_connec_to(entities, connec_name)
414
418
  idmap2.reload
415
- expect(idmap2.connec_id).to eql(id)
416
419
  expect(idmap2.last_push_to_connec).to_not be_nil
417
420
  end
418
421
  end
@@ -423,11 +426,11 @@ describe Maestrano::Connector::Rails::Entity do
423
426
  let(:err_msg) { 'Not Found' }
424
427
  let(:result400) { {status: 400, body: err_msg} }
425
428
  before {
426
- allow(client).to receive(:batch).and_return(ActionDispatch::Response.new(200, {}, {results: [result400, result400]}.to_json, {}))
429
+ allow(connec_client).to receive(:batch).and_return(ActionDispatch::Response.new(200, {}, {results: [result400, result400]}.to_json, {}))
427
430
  }
428
431
 
429
432
  it 'stores the errr in the idmap' do
430
- subject.push_entities_to_connec_to(client, entities_with_idmaps, '', organization)
433
+ subject.push_entities_to_connec_to(entities_with_idmaps, '')
431
434
  idmap2.reload
432
435
  expect(idmap2.message).to eq result400[:body]
433
436
  end
@@ -435,115 +438,59 @@ describe Maestrano::Connector::Rails::Entity do
435
438
  context 'with a long error message' do
436
439
  let(:err_msg) { 'A very long sentence with a lot of error or more likely a badly designed API that return an html 404 page instead of a nice json answer an then the world is sad and the kitten are unhappy. So juste to be safe we are truncating the error message and I am running out of words to write I hope it is long enough' }
437
440
  it 'truncates the error message' do
438
- subject.push_entities_to_connec_to(client, entities_with_idmaps, '', organization)
441
+ subject.push_entities_to_connec_to(entities_with_idmaps, '')
439
442
  idmap2.reload
440
443
  expect(idmap2.message).to eq err_msg.truncate(255)
441
444
  end
442
445
  end
443
446
  end
444
447
  end
445
-
446
- describe 'map_to_external_with_idmap' do
447
- let(:organization) { create(:organization) }
448
- let(:id) { '765e-zer4' }
449
- let(:mapped_entity) { {'first_name' => 'John'} }
450
- before {
451
- allow(subject.class).to receive(:connec_entity_name).and_return(connec_name)
452
- allow(subject.class).to receive(:external_entity_name).and_return(external_name)
453
- allow(subject).to receive(:map_to_external).and_return(mapped_entity)
454
- allow(subject.class).to receive(:object_name_from_connec_entity_hash).and_return('name')
455
- allow(subject.class).to receive(:object_name_from_external_entity_hash).and_return('name')
456
- }
457
-
458
- context 'when entity has an idmap' do
459
- let!(:idmap) { create(:idmap, organization: organization, external_entity: external_name.downcase, connec_entity: connec_name.downcase, connec_id: id, last_push_to_external: 3.hour.ago)}
460
-
461
- context 'when updated_at field is most recent than idmap last_push_to_external' do
462
- let(:entity) { {'id' => id, 'name' => 'John', 'updated_at' => 2.hour.ago } }
463
-
464
- it 'returns the entity with its idmap' do
465
- expect(subject.map_to_external_with_idmap(entity, organization)).to eql({entity: mapped_entity, idmap: idmap})
466
- end
467
- end
468
-
469
- context 'when updated_at field is older than idmap last_push_to_external' do
470
- let(:entity) { {'id' => id, 'name' => 'John', 'updated_at' => 5.hour.ago } }
471
-
472
- it 'discards the entity' do
473
- expect(subject.map_to_external_with_idmap(entity, organization)).to be_nil
474
- end
475
- end
476
-
477
- context 'when to_external is set to false' do
478
- let(:entity) { {'id' => id, 'name' => 'John' } }
479
- before {
480
- idmap.update(to_external: false)
481
- }
482
-
483
- it 'discards the entity' do
484
- expect(subject.map_to_external_with_idmap(entity, organization)).to be_nil
485
- end
486
- end
487
-
488
- context 'when external_inactive is true' do
489
- let(:entity) { {'id' => id, 'name' => 'John' } }
490
- before {
491
- idmap.update(external_inactive: true)
492
- }
493
-
494
- it 'discards the entity' do
495
- expect(subject.map_to_external_with_idmap(entity, organization)).to be_nil
496
- end
497
- end
498
- end
499
-
500
- context 'when entity has no idmap' do
501
- let(:entity) { {'id' => id, 'name' => 'John', 'updated_at' => 5.hour.ago } }
502
- before {
503
- allow(subject.class).to receive(:object_name_from_connec_entity_hash).and_return('human readable stuff')
504
- }
505
-
506
- it { expect{ subject.map_to_external_with_idmap(entity, organization) }.to change{Maestrano::Connector::Rails::IdMap.count}.by(1) }
507
-
508
- it 'returns the entity with its new idmap' do
509
- expect(subject.map_to_external_with_idmap(entity, organization)).to eql({entity: mapped_entity, idmap: Maestrano::Connector::Rails::IdMap.last})
510
- end
511
-
512
- it 'save the entity name in the idmap' do
513
- subject.map_to_external_with_idmap(entity, organization)
514
- expect(Maestrano::Connector::Rails::IdMap.last.name).to eql('human readable stuff')
515
- end
516
- end
517
- end
518
-
519
- describe 'filter_connec_entities' do
520
- it { expect(subject.filter_connec_entities([{a: 2}], organization)).to eql([{a: 2}]) }
521
- end
522
448
  end
523
449
 
524
-
525
450
  # External methods
526
451
  describe 'external methods' do
527
- let(:organization) { create(:organization) }
528
- let(:connec_name) { 'connec_name' }
529
- let(:external_name) { 'external_name' }
530
452
  let(:idmap1) { create(:idmap, organization: organization) }
531
453
  let(:idmap2) { create(:idmap, organization: organization, external_id: nil, external_entity: nil, last_push_to_external: nil) }
532
454
  let(:entity1) { {name: 'John'} }
533
455
  let(:entity2) { {name: 'Jane'} }
534
456
  let(:entity_with_idmap1) { {entity: entity1, idmap: idmap1} }
457
+ let(:connec_id2) { 'connec_id2' }
535
458
  let(:entity_with_idmap2) { {entity: entity2, idmap: idmap2} }
536
459
  let(:entities_with_idmaps) { [entity_with_idmap1, entity_with_idmap2] }
537
460
 
461
+ describe 'get_external_entities_wrapper' do
462
+ context 'when write only' do
463
+ before { allow(subject.class).to receive(:can_read_external?).and_return(false) }
464
+
465
+ it 'returns an empty array and does not call get_external_entities' do
466
+ expect(subject).to_not receive(:get_connec_entities)
467
+ expect(subject.get_external_entities_wrapper(nil)).to eql([])
468
+ end
469
+ end
470
+
471
+ context 'when skip external' do
472
+ let(:opts) { {skip_external: true} }
473
+
474
+ it 'returns an empty array and does not call get_external_entities' do
475
+ expect(subject).to_not receive(:get_connec_entities)
476
+ expect(subject.get_external_entities_wrapper(nil)).to eql([])
477
+ end
478
+ end
479
+
480
+ it 'calls get_external_entities' do
481
+ expect(subject).to receive(:get_external_entities).and_return([])
482
+ subject.get_external_entities_wrapper(nil)
483
+ end
484
+ end
485
+
538
486
  describe 'get_external_entities' do
539
- it { expect{ subject.get_external_entities(nil, nil, organization) }.to raise_error('Not implemented') }
487
+ it { expect{ subject.get_external_entities(nil) }.to raise_error('Not implemented') }
540
488
  end
541
489
 
542
490
  describe 'push_entities_to_external' do
543
491
  it 'calls push_entities_to_external_to' do
544
- allow(subject.class).to receive(:external_entity_name).and_return(external_name)
545
- expect(subject).to receive(:push_entities_to_external_to).with(nil, entities_with_idmaps, external_name, organization)
546
- subject.push_entities_to_external(nil, entities_with_idmaps, organization)
492
+ expect(subject).to receive(:push_entities_to_external_to).with(entities_with_idmaps, external_name)
493
+ subject.push_entities_to_external(entities_with_idmaps)
547
494
  end
548
495
  end
549
496
 
@@ -552,14 +499,42 @@ describe Maestrano::Connector::Rails::Entity do
552
499
  it 'does nothing' do
553
500
  allow(subject.class).to receive(:can_write_external?).and_return(false)
554
501
  expect(subject).to_not receive(:push_entity_to_external)
555
- subject.push_entities_to_external_to(nil, entities_with_idmaps, external_name, organization)
502
+ subject.push_entities_to_external_to(entities_with_idmaps, external_name)
556
503
  end
557
504
  end
558
505
 
559
506
  it 'calls push_entity_to_external for each entity' do
560
- allow(subject.class).to receive(:connec_entity_name).and_return(connec_name)
561
507
  expect(subject).to receive(:push_entity_to_external).twice
562
- subject.push_entities_to_external_to(nil, entities_with_idmaps, external_name, organization)
508
+ subject.push_entities_to_external_to(entities_with_idmaps, external_name)
509
+ end
510
+
511
+ describe 'ids' do
512
+ before {
513
+ allow(subject).to receive(:create_external_entity).and_return('id')
514
+ allow(subject).to receive(:update_external_entity).and_return(nil)
515
+ }
516
+
517
+ context 'when ids to send to connec' do
518
+ let(:batch_param) {
519
+ {:sequential=>true, :ops=>[{:method=>"put", :url=>"/api/v2/cld-123/people/#{idmap2.connec_id}", :params=>{:people=>{id: [{:id=>"id", :provider=>organization.oauth_provider, :realm=>organization.oauth_uid}]}}}]}
520
+ }
521
+
522
+ it 'does a batch call on connec' do
523
+ expect(connec_client).to receive(:batch).with(batch_param).and_return(ActionDispatch::Response.new(200, {}, {results: []}.to_json, {}))
524
+ subject.push_entities_to_external_to(entities_with_idmaps, external_name)
525
+ end
526
+ end
527
+
528
+ context 'when no id to send to connec' do
529
+ before {
530
+ idmap2.update(external_id: 'id')
531
+ }
532
+
533
+ it 'does not do a call on connec' do
534
+ expect(connec_client).to_not receive(:batch)
535
+ subject.push_entities_to_external_to(entities_with_idmaps, external_name)
536
+ end
537
+ end
563
538
  end
564
539
  end
565
540
 
@@ -568,18 +543,18 @@ describe Maestrano::Connector::Rails::Entity do
568
543
  it 'does not calls update if create_only' do
569
544
  allow(subject.class).to receive(:can_update_external?).and_return(false)
570
545
  expect(subject).to_not receive(:update_external_entity)
571
- subject.push_entity_to_external(nil, entity_with_idmap1, external_name, organization)
546
+ expect(subject.push_entity_to_external(entity_with_idmap1, external_name)).to be_nil
572
547
  end
573
548
 
574
549
  it 'calls update_external_entity' do
575
- expect(subject).to receive(:update_external_entity).with(nil, entity1, idmap1.external_id, external_name, organization)
576
- subject.push_entity_to_external(nil, entity_with_idmap1, external_name, organization)
550
+ expect(subject).to receive(:update_external_entity).with(entity1, idmap1.external_id, external_name)
551
+ subject.push_entity_to_external(entity_with_idmap1, external_name)
577
552
  end
578
553
 
579
554
  it 'updates the idmap last push to external' do
580
555
  allow(subject).to receive(:update_external_entity)
581
556
  time_before = idmap1.last_push_to_external
582
- subject.push_entity_to_external(nil, entity_with_idmap1, external_name, organization)
557
+ expect(subject.push_entity_to_external(entity_with_idmap1, external_name)).to be_nil
583
558
  idmap1.reload
584
559
  expect(idmap1.last_push_to_external).to_not eql(time_before)
585
560
  end
@@ -587,256 +562,353 @@ describe Maestrano::Connector::Rails::Entity do
587
562
 
588
563
  context 'when the entity idmap does not have an external id' do
589
564
  it 'calls create_external_entity' do
590
- expect(subject).to receive(:create_external_entity).with(nil, entity2, external_name, organization)
591
- subject.push_entity_to_external(nil, entity_with_idmap2, external_name, organization)
565
+ expect(subject).to receive(:create_external_entity).with(entity2, external_name)
566
+ subject.push_entity_to_external(entity_with_idmap2, external_name)
592
567
  end
593
568
 
594
569
  it 'updates the idmap external id, entity and last push' do
595
570
  allow(subject).to receive(:create_external_entity).and_return('999111')
596
- subject.push_entity_to_external(nil, entity_with_idmap2, external_name, organization)
571
+ subject.push_entity_to_external(entity_with_idmap2, external_name)
597
572
  idmap2.reload
598
573
  expect(idmap2.external_id).to eql('999111')
599
574
  expect(idmap2.last_push_to_external).to_not be_nil
600
575
  end
576
+
577
+ it 'returns an hash with the external_id' do
578
+ allow(subject).to receive(:create_external_entity).and_return('999111')
579
+ expect(subject.push_entity_to_external(entity_with_idmap2, external_name)).to eql({connec_id: idmap2.connec_id, external_id: '999111', idmap: idmap2})
580
+ end
581
+ end
582
+
583
+ describe 'failures' do
584
+
585
+ it 'stores the error in the idmap' do
586
+ allow(subject).to receive(:create_external_entity).and_raise('Kabooooom')
587
+ allow(subject).to receive(:update_external_entity).and_raise('Kabooooom')
588
+ subject.push_entity_to_external(entity_with_idmap1, external_name)
589
+ subject.push_entity_to_external(entity_with_idmap2, external_name)
590
+ expect(idmap1.reload.message).to include('Kabooooom')
591
+ expect(idmap2.reload.message).to include('Kabooooom')
592
+ end
593
+
594
+ it 'truncates the message' do
595
+ msg = 'Large corporations use our integrated platform to provide a fully customized environment to their clients, increasing revenue, engagement and gaining insight on client behavior through our Big Data technology. Large corporations use our integrated platform to provide a fully customized environment to their clients, increasing revenue, engagement and gaining insight on client behavior through our Big Data technology.'
596
+ allow(subject).to receive(:create_external_entity).and_raise(msg)
597
+ subject.push_entity_to_external(entity_with_idmap2, external_name)
598
+ expect(idmap2.reload.message).to include(msg.truncate(255))
599
+ end
601
600
  end
602
601
  end
603
602
 
604
603
  describe 'create_external_entity' do
605
604
  let(:organization) { create(:organization) }
606
605
 
607
- it { expect{ subject.create_external_entity(nil, nil, nil, organization) }.to raise_error('Not implemented') }
606
+ it { expect{ subject.create_external_entity(nil, nil) }.to raise_error('Not implemented') }
608
607
  end
609
608
 
610
609
  describe 'update_external_entity' do
611
610
  let(:organization) { create(:organization) }
612
611
 
613
- it { expect{ subject.update_external_entity(nil, nil, nil, nil, organization) }.to raise_error('Not implemented') }
612
+ it { expect{ subject.update_external_entity(nil, nil, nil) }.to raise_error('Not implemented') }
614
613
  end
615
614
  end
616
615
 
617
-
618
- # General methods
619
- describe 'consolidate_and_map_data' do
620
- let(:organization) { create(:organization) }
621
- let(:external_name) { 'External_name' }
622
- let(:connec_name) { 'Connec_name' }
616
+ describe 'consolidate_and_map methods' do
623
617
  let(:id) { '56882' }
624
618
  let(:date) { 2.hour.ago }
625
619
  before {
626
620
  allow(subject.class).to receive(:id_from_external_entity_hash).and_return(id)
627
621
  allow(subject.class).to receive(:last_update_date_from_external_entity_hash).and_return(date)
628
- allow(subject.class).to receive(:external_entity_name).and_return(external_name)
629
- allow(subject.class).to receive(:connec_entity_name).and_return(connec_name)
630
622
  }
623
+
624
+ describe 'consolidate_and_map_data' do
625
+ context 'singleton' do
626
+ before {
627
+ allow(subject.class).to receive(:singleton?).and_return(true)
628
+ }
629
+
630
+ it 'returns the consolidate_and_map_singleton method result' do
631
+ expect(subject).to receive(:consolidate_and_map_singleton).with({}, {}).and_return({result: 1})
632
+ expect(subject.consolidate_and_map_data({}, {})).to eql({result: 1})
633
+ end
634
+ end
635
+
636
+ context 'not singleton' do
637
+ it 'calls the consolidation on both connec and external and returns an hash with the results' do
638
+ expect(subject).to receive(:consolidate_and_map_connec_entities).with({}, {}, [], external_name).and_return({connec_result: 1})
639
+ expect(subject).to receive(:consolidate_and_map_external_entities).with({}, connec_name).and_return({ext_result: 1})
640
+ expect(subject.consolidate_and_map_data({}, {})).to eql({connec_entities: {connec_result: 1}, external_entities: {ext_result: 1}})
641
+ end
642
+ end
643
+ end
631
644
 
632
- context 'for a singleton method' do
645
+ describe 'consolidate_and_map_singleton' do
646
+ let(:connec_id) { [{'id' => 'lala', 'provider' => 'connec', 'realm' => 'realm'}] }
633
647
  before {
634
- allow(subject.class).to receive(:singleton?).and_return(true)
635
648
  allow(subject).to receive(:map_to_connec).and_return({map: 'connec'})
636
649
  allow(subject).to receive(:map_to_external).and_return({map: 'external'})
637
650
  allow(subject.class).to receive(:object_name_from_connec_entity_hash).and_return('connec human name')
638
651
  allow(subject.class).to receive(:object_name_from_external_entity_hash).and_return('external human name')
639
652
  }
640
653
 
641
- it { expect(subject.consolidate_and_map_data([], [], organization)).to eql({connec_entities: [], external_entities: []}) }
654
+ it { expect(subject.consolidate_and_map_singleton([], [])).to eql({connec_entities: [], external_entities: []}) }
642
655
 
643
656
  context 'with no idmap' do
644
657
  it 'creates one for connec' do
645
- subject.consolidate_and_map_data([{'id' => 'lala'}], [], organization)
658
+ expect{
659
+ subject.consolidate_and_map_singleton([{'id' => connec_id}], [])
660
+ }.to change{ Maestrano::Connector::Rails::IdMap.count }.by(1)
646
661
  idmap = Maestrano::Connector::Rails::IdMap.last
647
662
  expect(idmap.connec_entity).to eql(connec_name.downcase)
648
663
  expect(idmap.external_entity).to eql(external_name.downcase)
649
- expect(idmap.connec_id).to eql('lala')
664
+ expect(idmap.name).to eql('connec human name')
650
665
  end
651
666
 
652
667
  it 'creates one for external' do
653
- subject.consolidate_and_map_data([], [{}], organization)
668
+ expect{
669
+ subject.consolidate_and_map_singleton([], [{}])
670
+ }.to change{ Maestrano::Connector::Rails::IdMap.count }.by(1)
654
671
  idmap = Maestrano::Connector::Rails::IdMap.last
655
672
  expect(idmap.connec_entity).to eql(connec_name.downcase)
656
673
  expect(idmap.external_entity).to eql(external_name.downcase)
657
674
  expect(idmap.external_id).to eql(id)
675
+ expect(idmap.name).to eql('external human name')
658
676
  end
659
677
  end
660
678
 
661
679
  context 'with an idmap' do
662
680
  let!(:idmap) { create(:idmap, connec_entity: connec_name.downcase, external_entity: external_name.downcase, organization: organization) }
663
681
 
664
- it { expect{ subject.consolidate_and_map_data([{}], [], organization) }.to_not change{ Maestrano::Connector::Rails::IdMap } }
682
+ it { expect{ subject.consolidate_and_map_singleton([{'id' => connec_id}], []) }.to_not change{ Maestrano::Connector::Rails::IdMap.count } }
665
683
  end
666
684
 
667
685
  context 'with conflict' do
668
- let!(:idmap) { create(:idmap, connec_entity: connec_name.downcase, external_entity: external_name.downcase, organization: organization, external_id: id, connec_id: 'lala') }
686
+ let!(:idmap) { create(:idmap, connec_entity: connec_name.downcase, external_entity: external_name.downcase, organization: organization, external_id: id) }
669
687
  let(:updated) { 3.hour.ago }
670
- let(:connec_entity) { {'id' => 'lala', 'updated_at' => updated} }
688
+ let(:connec_entity) { {'id' => connec_id, 'updated_at' => updated} }
671
689
 
672
690
  context 'with options' do
673
- it { expect(subject.consolidate_and_map_data([connec_entity], [{}], organization, connec_preemption: true)).to eql({connec_entities: [{entity: {map: 'external'}, idmap: idmap}], external_entities: []}) }
674
- it { expect(subject.consolidate_and_map_data([connec_entity], [{}], organization, connec_preemption: false)).to eql({connec_entities: [], external_entities: [{entity: {map: 'connec'}, idmap: idmap}]}) }
691
+ it 'keep the external one if connec_preemption is false' do
692
+ subject.instance_variable_set(:@opts, {connec_preemption: false})
693
+ expect(subject.consolidate_and_map_singleton([connec_entity], [{}])).to eql({connec_entities: [], external_entities: [{entity: {map: 'connec'}, idmap: idmap}]})
694
+ end
695
+
696
+ context 'when connec preemption is true' do
697
+ let(:opts) { {connec_preemption: true} }
698
+
699
+ it 'keep the connec one' do
700
+ expect(subject.consolidate_and_map_singleton([connec_entity], [{}])).to eql({connec_entities: [{entity: {map: 'external'}, idmap: idmap}], external_entities: []})
701
+ end
702
+
703
+ it 'map with the unfolded references' do
704
+ expect(subject).to receive(:map_to_external).with('id' => nil, 'updated_at' => updated)
705
+ subject.consolidate_and_map_singleton([connec_entity], [{}])
706
+ end
707
+ end
675
708
  end
676
709
 
677
710
  context 'without options' do
678
711
  context 'with a more recent external one' do
679
- it { expect(subject.consolidate_and_map_data([connec_entity], [{}], organization)).to eql({connec_entities: [], external_entities: [{entity: {map: 'connec'}, idmap: idmap}]}) }
712
+ it { expect(subject.consolidate_and_map_singleton([connec_entity], [{}])).to eql({connec_entities: [], external_entities: [{entity: {map: 'connec'}, idmap: idmap}]}) }
680
713
  end
681
714
  context 'with a more recent connec one' do
682
715
  let(:updated) { 2.minute.ago }
683
- it { expect(subject.consolidate_and_map_data([connec_entity], [{}], organization)).to eql({connec_entities: [{entity: {map: 'external'}, idmap: idmap}], external_entities: []}) }
716
+ it { expect(subject.consolidate_and_map_singleton([connec_entity], [{}])).to eql({connec_entities: [{entity: {map: 'external'}, idmap: idmap}], external_entities: []}) }
684
717
  end
685
718
  end
686
719
  end
687
720
  end
688
721
 
689
- context 'for a non singleton method' do
690
-
691
- describe 'connec_entities treatment' do
692
- let(:entity1) { {name: 'John'} }
693
- let(:entity2) { {name: 'Jane'} }
722
+ describe 'consolidate_and_map_connec_entities' do
723
+ let(:connec_human_name) { 'connec human name' }
724
+ let(:id1) { 'external-unfolded-id' }
725
+ let(:id2) { nil }
726
+ let(:connec_id1) { 'connec-id-1' }
727
+ let(:connec_id2) { 'connec-id-2' }
728
+ let(:entity1) { {'id' => id1, 'name' => 'John', 'updated_at' => date} }
729
+ let(:entity2) { {'id' => id2, 'name' => 'Jane', 'updated_at' => date} }
730
+ let(:entity_without_refs) { {} }
731
+ let(:entities) { [entity1, entity2] }
732
+ before {
733
+ allow(subject.class).to receive(:object_name_from_connec_entity_hash).and_return(connec_human_name)
734
+ allow(subject).to receive(:map_to_external).and_return({mapped: 'entity'})
735
+ allow(Maestrano::Connector::Rails::ConnecHelper).to receive(:unfold_references).and_return(entity1.merge(__connec_id: connec_id1), entity2.merge(__connec_id: connec_id2), nil)
736
+ }
694
737
 
695
- it 'calls map_to_external_with_idmap for each entity' do
696
- expect(subject).to receive(:map_to_external_with_idmap).with(entity1, organization)
697
- expect(subject).to receive(:map_to_external_with_idmap).with(entity2, organization)
698
- subject.consolidate_and_map_data([entity1, entity2], [], organization)
738
+ context 'when idmaps do not exist' do
739
+ it 'creates the idmaps with a name and returns the mapped entities with their idmaps' do
740
+ expect{
741
+ expect(subject.consolidate_and_map_connec_entities(entities, [], [], external_name)).to eql([{entity: {mapped: 'entity'}, idmap: Maestrano::Connector::Rails::IdMap.first}, {entity: {mapped: 'entity'}, idmap: Maestrano::Connector::Rails::IdMap.last}])
742
+ }.to change{ Maestrano::Connector::Rails::IdMap.count }.by(2)
743
+ expect(Maestrano::Connector::Rails::IdMap.last.name).to eql(connec_human_name)
699
744
  end
700
745
  end
701
746
 
702
- describe 'external_entities treatment' do
703
- let(:entity) { {id: id, name: 'John', modifiedDate: date} }
704
- let(:mapped_entity) { {first_name: 'John'} }
705
- let(:entities) { [entity] }
706
- let(:human_name) { 'alien' }
747
+ context 'when idmap exists' do
748
+ let(:entities) { [entity1] }
749
+ let!(:idmap1) { create(:idmap, organization: organization, connec_entity: connec_name.downcase, external_entity: external_name.downcase, external_id: id1, connec_id: connec_id1) }
707
750
 
708
- before {
709
- allow(subject).to receive(:map_to_connec).and_return(mapped_entity)
710
- allow(subject.class).to receive(:object_name_from_external_entity_hash).and_return(human_name)
711
- }
751
+ it 'does not create an idmap' do
752
+ expect{
753
+ subject.consolidate_and_map_connec_entities(entities, [], [], external_name)
754
+ }.to_not change{ Maestrano::Connector::Rails::IdMap.count }
755
+ end
712
756
 
713
- context 'when entity has no idmap' do
757
+ it 'returns the entity with its idmap' do
758
+ expect(subject.consolidate_and_map_connec_entities(entities, [], [], external_name)).to eql([{entity: {mapped: 'entity'}, idmap: idmap1}])
759
+ end
714
760
 
715
- it 'creates an idmap and returns the mapped entity with its new idmap' do
716
- mapped_entities = subject.consolidate_and_map_data([], entities, organization)
717
- expect(mapped_entities).to eql({connec_entities: [], external_entities: [{entity: mapped_entity, idmap: Maestrano::Connector::Rails::IdMap.last}]})
761
+ context 'when external inactive' do
762
+ before { idmap1.update(external_inactive: true) }
763
+ it 'discards the entity' do
764
+ expect(subject.consolidate_and_map_connec_entities(entities, [], [], external_name)).to eql([])
718
765
  end
766
+ end
719
767
 
720
- it 'save the name in the idmap' do
721
- subject.consolidate_and_map_data([], entities, organization)
722
- expect(Maestrano::Connector::Rails::IdMap.last.name).to eql(human_name)
768
+ context 'when to external flag is false' do
769
+ before { idmap1.update(to_external: false) }
770
+ it 'discards the entity' do
771
+ expect(subject.consolidate_and_map_connec_entities(entities, [], [], external_name)).to eql([])
723
772
  end
724
773
  end
725
774
 
726
- context 'when entity has an idmap with to_connec set to false' do
727
- let!(:idmap) { create(:idmap, external_entity: external_name.downcase, connec_entity: connec_name.downcase, external_id: id, organization: organization, to_connec: false) }
775
+ context 'when last_push_to_external is recent' do
776
+ before { idmap1.update(last_push_to_external: 2.second.ago) }
728
777
  it 'discards the entity' do
729
- mapped_entities = subject.consolidate_and_map_data([], entities, organization)
730
- expect(mapped_entities).to eql({connec_entities: [], external_entities: []})
778
+ expect(subject.consolidate_and_map_connec_entities(entities, [], [], external_name)).to eql([])
779
+ end
780
+
781
+ context 'with full synchronization opts' do
782
+ let(:opts) { {full_sync: true} }
783
+
784
+ it 'keeps the entity' do
785
+ expect(subject.consolidate_and_map_connec_entities(entities, [], [], external_name)).to eql([{entity: {mapped: 'entity'}, idmap: idmap1}])
786
+ end
731
787
  end
732
788
  end
733
789
 
734
- describe 'inactive flagging' do
735
- context 'when idmap is inactive' do
736
- let!(:idmap) { create(:idmap, external_entity: external_name.downcase, connec_entity: connec_name.downcase, external_id: id, organization: organization, external_inactive: true) }
790
+ end
737
791
 
738
- context 'when entity is inactive' do
739
- before { allow(subject.class).to receive(:inactive_from_external_entity_hash?).and_return(true) }
792
+ context 'when conflict' do
793
+ let(:entities) { [entity1] }
794
+ let(:external_entity_1) { {'id' => id1} }
795
+ let(:external_entities) { [external_entity_1] }
796
+ before {
797
+ allow(subject.class).to receive(:id_from_external_entity_hash).and_return(id1)
798
+ }
740
799
 
741
- it 'discards the entity' do
742
- expect(subject.consolidate_and_map_data([], entities, organization)).to eql({connec_entities: [], external_entities: []})
743
- end
800
+ context 'with opts' do
801
+ context 'with connec preemption false' do
802
+ it 'discards the entity and keep the external one' do
803
+ subject.instance_variable_set(:@opts, {connec_preemption: false})
804
+ expect(subject.consolidate_and_map_connec_entities(entities, external_entities, [], external_name)).to eql([])
805
+ expect(external_entities).to_not be_empty
806
+ end
807
+ end
808
+
809
+ context 'with connec preemption true' do
810
+ it 'keeps the entity and discards the external one' do
811
+ subject.instance_variable_set(:@opts, {connec_preemption: true})
812
+ expect(subject.consolidate_and_map_connec_entities(entities, external_entities, [], external_name)).to eql([{entity: {mapped: 'entity'}, idmap: Maestrano::Connector::Rails::IdMap.first}])
813
+ expect(external_entities).to be_empty
744
814
  end
815
+ end
816
+ end
745
817
 
746
- context 'when entity is active' do
747
- before { allow(subject.class).to receive(:inactive_from_external_entity_hash?).and_return(false) }
818
+ context 'without opts' do
819
+ before {
820
+ allow(subject.class).to receive(:last_update_date_from_external_entity_hash).and_return(external_date)
821
+ }
748
822
 
749
- it 'set the idmap as active' do
750
- subject.consolidate_and_map_data([], entities, organization)
751
- expect(idmap.reload.external_inactive).to be false
752
- end
823
+ context 'with connec one more recent' do
824
+ let(:external_date) { 1.year.ago }
825
+ let(:date) { 1.day.ago }
826
+
827
+ it 'keeps the entity and discards the external one' do
828
+ expect(subject.consolidate_and_map_connec_entities(entities, external_entities, [], external_name)).to eql([{entity: {mapped: 'entity'}, idmap: Maestrano::Connector::Rails::IdMap.first}])
829
+ expect(external_entities).to be_empty
753
830
  end
754
831
  end
755
832
 
756
- context 'when idmap is active and entity is inactive' do
757
- let!(:idmap) { create(:idmap, external_entity: external_name.downcase, connec_entity: connec_name.downcase, external_id: id, organization: organization, external_inactive: false) }
758
- before { allow(subject.class).to receive(:inactive_from_external_entity_hash?).and_return(true) }
833
+ context 'with external one more recent' do
834
+ let(:external_date) { 1.month.ago }
835
+ let(:date) { 1.year.ago }
759
836
 
760
- it 'set the idmap as inactive and discards the entity' do
761
- expect(subject.consolidate_and_map_data([], entities, organization)).to eql({connec_entities: [], external_entities: []})
762
- expect(idmap.reload.external_inactive).to be true
837
+ it 'discards the entity and keep the external one' do
838
+ expect(subject.consolidate_and_map_connec_entities(entities, external_entities, [], external_name)).to eql([])
839
+ expect(external_entities).to_not be_empty
763
840
  end
764
841
  end
765
842
  end
843
+ end
844
+ end
766
845
 
767
- context 'when entity has an idmap with a last_push_to_connec more recent than date' do
768
- 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) }
846
+ describe 'consolidate_and_map_external_entities' do
847
+ let(:entity) { {'id' => id, 'name' => 'Jane'} }
848
+ let(:id) { 'id' }
849
+ let(:external_human_name) { 'external human name' }
850
+ before {
851
+ allow(subject.class).to receive(:id_from_external_entity_hash).and_return(id)
852
+ allow(subject.class).to receive(:object_name_from_external_entity_hash).and_return(external_human_name)
853
+ allow(subject).to receive(:map_to_connec).and_return({mapped: 'ext_entity'})
854
+ }
769
855
 
770
- it 'discards the entity' do
771
- mapped_entities = subject.consolidate_and_map_data([], entities, organization)
772
- expect(mapped_entities).to eql({connec_entities: [], external_entities: []})
773
- end
856
+ context 'when idmap exists' do
857
+ let!(:idmap) { create(:idmap, organization: organization, connec_entity: connec_name.downcase, external_entity: external_name.downcase, external_id: id) }
858
+
859
+ it 'does not create one' do
860
+ expect{
861
+ subject.consolidate_and_map_external_entities([entity], connec_name)
862
+ }.to_not change{ Maestrano::Connector::Rails::IdMap.count }
774
863
  end
775
864
 
776
- context 'when entity has an idmap with a last_push_to_connec older than date' do
865
+ it 'returns the mapped entity with the idmap' do
866
+ expect(subject.consolidate_and_map_external_entities([entity], connec_name)).to eql([{entity: {mapped: 'ext_entity'}, idmap: idmap}])
867
+ end
777
868
 
778
- context 'with no conflict' do
779
- let!(:idmap) { create(:idmap, connec_entity: connec_name.downcase, external_entity: external_name.downcase, external_id: id, organization: organization, last_push_to_connec: 2.day.ago) }
869
+ context 'when to_connec is false' do
870
+ before { idmap.update(to_connec: false) }
780
871
 
781
- it 'returns the mapped entity with its idmap' do
782
- mapped_entities = subject.consolidate_and_map_data([], entities, organization)
783
- expect(mapped_entities).to eql({connec_entities: [], external_entities: [{entity: mapped_entity, idmap: idmap}]})
784
- end
872
+ it 'discards the entity' do
873
+ expect(subject.consolidate_and_map_external_entities([entity], connec_name)).to eql([])
785
874
  end
875
+ end
786
876
 
787
- context 'with conflict' do
788
- let(:connec_id) { '34uuu-778aa' }
789
- let!(:idmap) { create(:idmap, connec_id: connec_id, connec_entity: connec_name.downcase, external_entity: external_name.downcase, external_id: id, organization: organization, last_push_to_connec: 2.day.ago) }
790
- before {
791
- allow(subject).to receive(:map_to_external_with_idmap)
792
- }
793
-
794
- context 'with connec_preemption opt' do
877
+ context 'when entity is inactive' do
878
+ before {
879
+ allow(subject.class).to receive(:inactive_from_external_entity_hash?).and_return(true)
880
+ }
795
881
 
796
- context 'set to true' do
797
- let(:connec_entity) { {'id' => connec_id, 'first_name' => 'Richard', 'updated_at' => 1.day.ago} }
798
- it 'discards the entity' do
799
- mapped_entities = subject.consolidate_and_map_data([connec_entity], entities, organization, {connec_preemption: true})
800
- expect(mapped_entities).to eql({connec_entities: [], external_entities: []})
801
- end
802
- end
882
+ it 'discards the entity' do
883
+ expect(subject.consolidate_and_map_external_entities([entity], connec_name)).to eql([])
884
+ end
803
885
 
804
- context 'set to false' do
805
- let(:connec_entity) { {'id' => connec_id, 'first_name' => 'Richard', 'updated_at' => 1.second.ago} }
806
- it 'returns the mapped entity with its idmap' do
807
- mapped_entities = subject.consolidate_and_map_data([connec_entity], entities, organization, {connec_preemption: false})
808
- expect(mapped_entities).to eql({connec_entities: [], external_entities: [{entity: mapped_entity, idmap: idmap}]})
809
- end
810
- end
811
- end
886
+ it 'updates the idmaps' do
887
+ subject.consolidate_and_map_external_entities([entity], connec_name)
888
+ expect(idmap.reload.external_inactive).to be true
889
+ end
890
+ end
812
891
 
813
- context 'without opt' do
814
- context 'with a more recent connec entity' do
815
- let(:connec_entity) { {'id' => connec_id, 'first_name' => 'Richard', 'updated_at' => 1.second.ago} }
892
+ context 'when last_push_to_connec is recent' do
893
+ before { idmap.update(last_push_to_connec: 2.second.ago) }
816
894
 
817
- it 'discards the entity' do
818
- mapped_entities = subject.consolidate_and_map_data([connec_entity], entities, organization, {connec_preemption: true})
819
- expect(mapped_entities).to eql({connec_entities: [], external_entities: []})
820
- end
821
- end
895
+ it 'discards the entity' do
896
+ expect(subject.consolidate_and_map_external_entities([entity], connec_name)).to eql([])
897
+ end
822
898
 
823
- context 'with a more recent external_entity' do
824
- let(:connec_entity) { {'id' => connec_id, 'first_name' => 'Richard', 'updated_at' => 1.year.ago} }
899
+ context 'with full synchronization opts' do
900
+ let(:opts) { {full_sync: true} }
825
901
 
826
- it 'returns the mapped entity with its idmap' do
827
- mapped_entities = subject.consolidate_and_map_data([connec_entity], entities, organization, {connec_preemption: false})
828
- expect(mapped_entities).to eql({connec_entities: [], external_entities: [{entity: mapped_entity, idmap: idmap}]})
829
- end
830
- end
902
+ it 'keeps the entity' do
903
+ expect(subject.consolidate_and_map_external_entities([entity], connec_name)).to eql([{entity: {mapped: 'ext_entity'}, idmap: idmap}])
831
904
  end
832
-
833
905
  end
834
906
  end
835
907
 
836
908
  end
837
-
838
909
  end
839
910
  end
840
- end
841
911
 
912
+
913
+ end
842
914
  end