maestrano-connector-rails 0.4.4 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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