maestrano-connector-rails 2.0.2.pre.RC6 → 2.0.2.pre.RC7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (113) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +6 -0
  3. data/DEVELOPER.md +1 -4
  4. data/Gemfile +2 -32
  5. data/Rakefile +6 -30
  6. data/app/models/maestrano/connector/rails/concerns/entity.rb +12 -0
  7. data/app/models/maestrano/connector/rails/concerns/sub_entity_base.rb +3 -2
  8. data/app/models/maestrano/connector/rails/id_map.rb +1 -0
  9. data/app/models/maestrano/connector/rails/services/data_consolidator.rb +4 -4
  10. data/db/migrate/20170315032224_add_metadata_to_id_map.rb +5 -0
  11. data/lib/maestrano/connector/rails/version.rb +7 -0
  12. data/lib/maestrano_connector_rails/factories.rb +1 -0
  13. data/maestrano-connector-rails.gemspec +44 -308
  14. metadata +13 -151
  15. data/VERSION +0 -1
  16. data/spec/controllers/connec_controller_spec.rb +0 -179
  17. data/spec/controllers/dependancies_controller_spec.rb +0 -31
  18. data/spec/controllers/group_users_controller_spec.rb +0 -39
  19. data/spec/controllers/groups_controller_spec.rb +0 -36
  20. data/spec/controllers/synchronizations_controller_spec.rb +0 -211
  21. data/spec/controllers/version_controller_spec.rb +0 -17
  22. data/spec/dummy/README.md +0 -1
  23. data/spec/dummy/Rakefile +0 -6
  24. data/spec/dummy/app/assets/images/.keep +0 -0
  25. data/spec/dummy/app/assets/javascripts/application.js +0 -13
  26. data/spec/dummy/app/assets/stylesheets/application.css +0 -15
  27. data/spec/dummy/app/controllers/application_controller.rb +0 -8
  28. data/spec/dummy/app/controllers/concerns/.keep +0 -0
  29. data/spec/dummy/app/helpers/application_helper.rb +0 -2
  30. data/spec/dummy/app/mailers/.keep +0 -0
  31. data/spec/dummy/app/models/.keep +0 -0
  32. data/spec/dummy/app/models/concerns/.keep +0 -0
  33. data/spec/dummy/app/models/entities/.keep +0 -0
  34. data/spec/dummy/app/views/home/index.html.erb +0 -1
  35. data/spec/dummy/app/views/layouts/application.html.erb +0 -14
  36. data/spec/dummy/bin/bundle +0 -3
  37. data/spec/dummy/bin/rails +0 -4
  38. data/spec/dummy/bin/rake +0 -4
  39. data/spec/dummy/bin/setup +0 -29
  40. data/spec/dummy/config.ru +0 -4
  41. data/spec/dummy/config/application.rb +0 -26
  42. data/spec/dummy/config/boot.rb +0 -5
  43. data/spec/dummy/config/database.yml +0 -25
  44. data/spec/dummy/config/environment.rb +0 -5
  45. data/spec/dummy/config/environments/development.rb +0 -41
  46. data/spec/dummy/config/environments/production.rb +0 -79
  47. data/spec/dummy/config/environments/test.rb +0 -39
  48. data/spec/dummy/config/initializers/assets.rb +0 -11
  49. data/spec/dummy/config/initializers/backtrace_silencers.rb +0 -7
  50. data/spec/dummy/config/initializers/cookies_serializer.rb +0 -3
  51. data/spec/dummy/config/initializers/filter_parameter_logging.rb +0 -4
  52. data/spec/dummy/config/initializers/inflections.rb +0 -16
  53. data/spec/dummy/config/initializers/maestrano.rb +0 -2
  54. data/spec/dummy/config/initializers/mime_types.rb +0 -4
  55. data/spec/dummy/config/initializers/session_store.rb +0 -3
  56. data/spec/dummy/config/initializers/wrap_parameters.rb +0 -14
  57. data/spec/dummy/config/locales/en.yml +0 -23
  58. data/spec/dummy/config/routes.rb +0 -9
  59. data/spec/dummy/config/secrets.yml +0 -22
  60. data/spec/dummy/config/settings.yml +0 -2
  61. data/spec/dummy/config/settings/development.yml +0 -12
  62. data/spec/dummy/config/settings/production.yml +0 -1
  63. data/spec/dummy/config/settings/test.yml +0 -12
  64. data/spec/dummy/config/settings/uat.yml +0 -1
  65. data/spec/dummy/db/schema.rb +0 -102
  66. data/spec/dummy/lib/assets/.keep +0 -0
  67. data/spec/dummy/log/.keep +0 -0
  68. data/spec/dummy/public/404.html +0 -67
  69. data/spec/dummy/public/422.html +0 -67
  70. data/spec/dummy/public/500.html +0 -66
  71. data/spec/dummy/public/favicon.ico +0 -0
  72. data/spec/dummy/tmp/cache/.gitkeep +0 -0
  73. data/spec/integration/complex_id_references_spec.rb +0 -249
  74. data/spec/integration/complex_naming_spec.rb +0 -192
  75. data/spec/integration/complex_spec.rb +0 -578
  76. data/spec/integration/connec_to_external_spec.rb +0 -327
  77. data/spec/integration/external_to_connec_spec.rb +0 -159
  78. data/spec/integration/id_references_spec.rb +0 -584
  79. data/spec/integration/singleton_spec.rb +0 -297
  80. data/spec/jobs/all_synchronizations_job_spec.rb +0 -24
  81. data/spec/jobs/push_to_connec_job_spec.rb +0 -111
  82. data/spec/jobs/push_to_connec_worker_spec.rb +0 -37
  83. data/spec/jobs/synchronization_job_spec.rb +0 -216
  84. data/spec/models/complex_entity_spec.rb +0 -407
  85. data/spec/models/connec_helper_spec.rb +0 -588
  86. data/spec/models/connector_logger_spec.rb +0 -26
  87. data/spec/models/entity_base_spec.rb +0 -20
  88. data/spec/models/entity_spec.rb +0 -1078
  89. data/spec/models/external_spec.rb +0 -27
  90. data/spec/models/id_map_spec.rb +0 -12
  91. data/spec/models/organization_spec.rb +0 -243
  92. data/spec/models/sub_entity_base_spec.rb +0 -155
  93. data/spec/models/synchronization_spec.rb +0 -106
  94. data/spec/models/user_organization_rel_spec.rb +0 -14
  95. data/spec/models/user_spec.rb +0 -15
  96. data/spec/routing/connec_routing_spec.rb +0 -12
  97. data/spec/spec_helper.rb +0 -34
  98. data/template/Procfile +0 -2
  99. data/template/application-sample.yml +0 -13
  100. data/template/database.yml +0 -31
  101. data/template/gitignore +0 -23
  102. data/template/maestrano.rb +0 -2
  103. data/template/maestrano_connector_template.rb +0 -135
  104. data/template/routes.rb +0 -25
  105. data/template/rubocop.yml +0 -55
  106. data/template/settings/development.yml +0 -0
  107. data/template/settings/production.yml +0 -0
  108. data/template/settings/settings.yml +0 -2
  109. data/template/settings/test.yml +0 -0
  110. data/template/settings/uat.yml +0 -0
  111. data/template/sidekiq.rb +0 -9
  112. data/template/sidekiq.yml +0 -4
  113. data/template/spec_helper.rb +0 -24
@@ -1,26 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe Maestrano::Connector::Rails::ConnectorLogger do
4
- subject { Maestrano::Connector::Rails::ConnectorLogger }
5
-
6
- describe 'self.log' do
7
- let(:organization) { create(:organization) }
8
-
9
- it 'calls rails.logger' do
10
- expect(Rails.logger).to receive(:info)
11
- subject.log('info', organization, 'msg')
12
- end
13
-
14
- it 'includes the organization uid and tenant' do
15
- expect(Rails.logger).to receive(:info).with("uid=\"#{organization.uid}\", org_uid=\"#{organization.org_uid}\", tenant=\"#{organization.tenant}\", message=\"msg\"")
16
-
17
- subject.log('info', organization, 'msg')
18
- end
19
-
20
- it 'includes extra params' do
21
- expect(Rails.logger).to receive(:info).with("uid=\"#{organization.uid}\", org_uid=\"#{organization.org_uid}\", tenant=\"#{organization.tenant}\", foo=\"bar\", message=\"msg\"")
22
-
23
- subject.log('info', organization, 'msg', foo: :bar)
24
- end
25
- end
26
- end
@@ -1,20 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe Maestrano::Connector::Rails::EntityBase do
4
- describe 'instance methods' do
5
- let!(:organization) { create(:organization, uid: 'cld-123') }
6
- let!(:connec_client) { Maestrano::Connec::Client[organization.tenant].new(organization.uid) }
7
- let!(:external_client) { Object.new }
8
- let(:opts) { {} }
9
- subject { Maestrano::Connector::Rails::EntityBase.new(organization, connec_client, external_client, opts) }
10
-
11
- describe 'opts_merge!' do
12
- let(:opts) { {a: 1, opts: 2} }
13
-
14
- it 'merges options with the instance variable' do
15
- subject.opts_merge!(opts: 3, test: 'test')
16
- expect(subject.instance_variable_get(:@opts)).to eql(a: 1, opts: 3, test: 'test')
17
- end
18
- end
19
- end
20
- end
@@ -1,1078 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe Maestrano::Connector::Rails::Entity do
4
-
5
- describe 'class methods' do
6
- subject { Maestrano::Connector::Rails::Entity }
7
-
8
- # IdMap methods
9
- describe 'idmaps methods' do
10
- before do
11
- allow(subject).to receive(:connec_entity_name).and_return('Ab')
12
- allow(subject).to receive(:external_entity_name).and_return('Ab')
13
- end
14
- let(:n_hash) { {connec_entity: 'ab', external_entity: 'ab'} }
15
-
16
- it { expect(subject.names_hash).to eql(n_hash) }
17
-
18
- it {
19
- expect(Maestrano::Connector::Rails::IdMap).to receive(:find_or_create_by).with(n_hash.merge(id: 'lala'))
20
- subject.find_or_create_idmap({id: 'lala'})
21
- }
22
-
23
- it {
24
- expect(Maestrano::Connector::Rails::IdMap).to receive(:find_by).with(n_hash.merge(id: 'lala'))
25
- subject.find_idmap({id: 'lala'})
26
- }
27
-
28
- it {
29
- expect(Maestrano::Connector::Rails::IdMap).to receive(:create).with(n_hash.merge(id: 'lala'))
30
- subject.create_idmap({id: 'lala'})
31
- }
32
- end
33
-
34
- describe 'normalized_connec_entity_name' do
35
- before do
36
- allow(subject).to receive(:connec_entity_name).and_return(connec_name)
37
- end
38
- context 'for a singleton resource' do
39
- before do
40
- allow(subject).to receive(:singleton?).and_return(true)
41
- end
42
-
43
- context 'for a simple name' do
44
- let(:connec_name) { 'Person' }
45
- it { expect(subject.normalized_connec_entity_name).to eql('person') }
46
- end
47
-
48
- context 'for a complex name' do
49
- let(:connec_name) { 'Credit Note' }
50
- it { expect(subject.normalized_connec_entity_name).to eql('credit_note') }
51
- end
52
- end
53
-
54
- context 'for a non singleton resource' do
55
- before do
56
- allow(subject).to receive(:singleton?).and_return(false)
57
- end
58
-
59
- context 'for a simple name' do
60
- let(:connec_name) { 'Person' }
61
- it { expect(subject.normalized_connec_entity_name).to eql('people') }
62
- end
63
-
64
- context 'for a complex name' do
65
- let(:connec_name) { 'Credit Note' }
66
- it { expect(subject.normalized_connec_entity_name).to eql('credit_notes') }
67
- end
68
- end
69
- end
70
-
71
- describe 'id_from_external_entity_hash' do
72
- it { expect{ subject.id_from_external_entity_hash(nil) }.to raise_error('Not implemented') }
73
- end
74
-
75
- describe 'last_update_date_from_external_entity_hash' do
76
- it { expect{ subject.last_update_date_from_external_entity_hash(nil) }.to raise_error('Not implemented') }
77
- end
78
-
79
- describe 'creation_date_from_external_entity_hash' do
80
- it { expect{ subject.creation_date_from_external_entity_hash(nil) }.to raise_error('Not implemented') }
81
- end
82
-
83
- # Entity specific methods
84
- describe 'singleton?' do
85
- it 'is false by default' do
86
- expect(subject.singleton?).to be false
87
- end
88
- end
89
-
90
- describe 'connec_entity_name' do
91
- it { expect{ subject.connec_entity_name }.to raise_error('Not implemented') }
92
- end
93
-
94
- describe 'external_entity_name' do
95
- it { expect{ subject.external_entity_name }.to raise_error('Not implemented') }
96
- end
97
-
98
- describe 'mapper_class' do
99
- it { expect{ subject.mapper_class }.to raise_error('Not implemented') }
100
- end
101
-
102
- describe 'object_name_from_connec_entity_hash' do
103
- it { expect{ subject.object_name_from_connec_entity_hash({}) }.to raise_error('Not implemented') }
104
- end
105
-
106
- describe 'object_name_from_external_entity_hash' do
107
- it { expect{ subject.object_name_from_external_entity_hash({}) }.to raise_error('Not implemented') }
108
- end
109
-
110
- describe 'connec_matching_fields' do
111
- it { expect(subject.connec_matching_fields).to be_nil }
112
- end
113
-
114
- describe 'count_and_first' do
115
- it 'returns the array size and the first element' do
116
- expect(subject.count_and_first([*1..27])).to eql(count: 27, first: 1)
117
- end
118
- end
119
-
120
- describe 'public_connec_entity_name' do
121
- it 'returns the pluralized connec_entity_name' do
122
- allow(subject).to receive(:connec_entity_name).and_return('tree')
123
- expect(subject.public_connec_entity_name).to eql('trees')
124
- end
125
-
126
- context 'when singleton' do
127
- before do
128
- allow(subject).to receive(:singleton?).and_return(true)
129
- end
130
-
131
- it 'returns the connec_entity_name' do
132
- allow(subject).to receive(:connec_entity_name).and_return('tree')
133
- expect(subject.public_connec_entity_name).to eql('tree')
134
- end
135
- end
136
- end
137
-
138
- describe 'public_external_entity_name' do
139
- it 'returns the pluralized external_entity_name' do
140
- allow(subject).to receive(:external_entity_name).and_return('tree')
141
- expect(subject.public_external_entity_name).to eql('trees')
142
- end
143
- end
144
- end
145
-
146
- describe 'instance methods' do
147
- let!(:organization) { create(:organization, uid: 'cld-123') }
148
- let!(:connec_client) { Maestrano::Connec::Client[organization.tenant].new(organization.uid) }
149
- let!(:external_client) { Object.new }
150
- let(:opts) { {} }
151
- subject { Maestrano::Connector::Rails::Entity.new(organization, connec_client, external_client, opts) }
152
- let(:connec_name) { 'Person' }
153
- let(:external_name) { 'external_name' }
154
- before do
155
- allow(subject.class).to receive(:connec_entity_name).and_return(connec_name)
156
- allow(subject.class).to receive(:external_entity_name).and_return(external_name)
157
- allow(Maestrano::Connector::Rails::External).to receive(:entities_list).and_return(%w(entity))
158
- organization.reset_synchronized_entities(true)
159
- end
160
-
161
- describe 'Mapper methods' do
162
- before(:each) do
163
- class AMapper
164
- extend HashMapper
165
- end
166
- allow(subject.class).to receive(:mapper_class).and_return(AMapper)
167
- allow(subject.class).to receive(:creation_mapper_class).and_call_original
168
- end
169
-
170
- describe 'map_to_external' do
171
- it 'calls the mapper normalize' do
172
- expect(AMapper).to receive(:normalize).with({}, subject.instance_values).and_return({})
173
- subject.map_to_external({})
174
- end
175
-
176
- it 'calls the creation_mapper normalize if passed true as second argument' do
177
- expect(subject.class).to receive(:creation_mapper_class)
178
- #if not overridden #creation_mapper_class calls #mapper_class
179
- expect(AMapper).to receive(:normalize).with({}, subject.instance_values).and_return({})
180
- subject.map_to_external({}, true)
181
- end
182
- end
183
-
184
- describe 'map_to_connec' do
185
- before do
186
- allow(subject.class).to receive(:id_from_external_entity_hash).and_return('this id')
187
- end
188
- it 'calls the mapper denormalize' do
189
- expect(AMapper).to receive(:denormalize).with({}, subject.instance_values).and_return({})
190
- subject.map_to_connec({})
191
- end
192
-
193
- it 'calls the creation_mapper denormalize if passed true as second argument' do
194
- expect(subject.class).to receive(:creation_mapper_class)
195
- #if not overridden #creation_mapper_class calls #mapper_class
196
- expect(AMapper).to receive(:denormalize).with({}, subject.instance_values).and_return({})
197
- subject.map_to_connec({}, true)
198
- end
199
-
200
- it 'calls for reference folding' do
201
- refs = %w(organization_id person_id)
202
- allow(subject.class).to receive(:references).and_return(refs)
203
- expect(Maestrano::Connector::Rails::ConnecHelper).to receive(:fold_references).with({id: 'this id'}, refs, organization)
204
- subject.map_to_connec({})
205
- end
206
-
207
- it 'merges the smart merging options' do
208
- allow(AMapper).to receive(:denormalize).and_return({opts: {some_opt: 4}})
209
- allow(subject.class).to receive(:connec_matching_fields).and_return([['first_name'], ['last_name']])
210
- 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)
211
- end
212
- end
213
- end
214
-
215
- # Connec! methods
216
- describe 'connec_methods' do
217
- let(:sync) { create(:synchronization, organization: organization) }
218
-
219
- describe 'filter_connec_entities' do
220
- it 'does nothing by default' do
221
- expect(subject.filter_connec_entities({a: 2})).to eql({a: 2})
222
- end
223
- end
224
-
225
- describe 'get_connec_entities' do
226
- describe 'when write only' do
227
- before do
228
- allow(subject.class).to receive(:can_read_connec?).and_return(false)
229
- allow(connec_client).to receive(:get).and_return(ActionDispatch::Response.new(200, {}, {people: [{first_name: 'Lea'}]}.to_json, {}))
230
- end
231
-
232
- it { expect(subject.get_connec_entities(nil)).to eql([]) }
233
- end
234
-
235
- describe 'when skip_connec' do
236
- let(:opts) { {__skip_connec: true} }
237
- it { expect(subject.get_connec_entities(nil)).to eql([]) }
238
- end
239
-
240
- describe 'with response' do
241
- context 'for a singleton resource' do
242
- before do
243
- allow(connec_client).to receive(:get).and_return(ActionDispatch::Response.new(200, {}, {person: []}.to_json, {}))
244
- allow(subject.class).to receive(:singleton?).and_return(true)
245
- end
246
-
247
- it 'calls get with a singularize url' do
248
- expect(connec_client).to receive(:get).with("#{connec_name.downcase}?")
249
- subject.get_connec_entities(nil)
250
- end
251
- end
252
-
253
- context 'for a non singleton resource' do
254
- before do
255
- allow(connec_client).to receive(:get).and_return(ActionDispatch::Response.new(200, {}, {people: []}.to_json, {}))
256
- end
257
-
258
- context 'with limit and skip opts' do
259
- let(:opts) { {__skip: 100, __limit: 50} }
260
- before do
261
- 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, {}))
262
- end
263
-
264
- it 'performs a size limited date and do not paginate' do
265
- uri_param = {"$filter" => "updated_at gt '#{sync.updated_at.iso8601}'", "$skip" => 100, "$top" => 50}.to_query
266
- expect(connec_client).to receive(:get).once.with("#{connec_name.downcase.pluralize}?#{uri_param}")
267
- subject.get_connec_entities(sync.updated_at)
268
- end
269
- end
270
-
271
- context 'when opts[:full_sync] is true' do
272
- let(:opts) { {full_sync: true} }
273
- it 'performs a full get' do
274
- expect(connec_client).to receive(:get).with("#{connec_name.downcase.pluralize}?")
275
- subject.get_connec_entities(sync.updated_at)
276
- end
277
- end
278
-
279
- context 'when there is no last sync' do
280
- it 'performs a full get' do
281
- expect(connec_client).to receive(:get).with("#{connec_name.downcase.pluralize}?")
282
- subject.get_connec_entities(nil)
283
- end
284
- end
285
-
286
- context 'when there is a last sync' do
287
- it 'performs a time limited get' do
288
- uri_param = {"$filter" => "updated_at gt '#{sync.updated_at.iso8601}'"}.to_query
289
- expect(connec_client).to receive(:get).with("#{connec_name.downcase.pluralize}?#{uri_param}")
290
- subject.get_connec_entities(sync.updated_at)
291
- end
292
- end
293
-
294
- context 'with options' do
295
- it 'support filter option for full sync' do
296
- subject.instance_variable_set(:@opts, {full_sync: true, :$filter => "code eq 'PEO12'"})
297
- uri_param = {'$filter'=>'code eq \'PEO12\''}.to_query
298
- expect(connec_client).to receive(:get).with("#{connec_name.downcase.pluralize}?#{uri_param}")
299
- subject.get_connec_entities(sync.updated_at)
300
- end
301
-
302
- it 'support filter option for time limited sync' do
303
- subject.instance_variable_set(:@opts, {:$filter => "code eq 'PEO12'"})
304
- uri_param = {"$filter"=>"updated_at gt '#{sync.updated_at.iso8601}' and code eq 'PEO12'"}.to_query
305
- expect(connec_client).to receive(:get).with("#{connec_name.downcase.pluralize}?#{uri_param}")
306
- subject.get_connec_entities(sync.updated_at)
307
- end
308
-
309
- it 'support orderby option for time limited sync' do
310
- subject.instance_variable_set(:@opts, {:$orderby => "name asc"})
311
- uri_param = {"$orderby"=>"name asc", "$filter"=>"updated_at gt '#{sync.updated_at.iso8601}'"}.to_query
312
- expect(connec_client).to receive(:get).with("#{connec_name.downcase.pluralize}?#{uri_param}")
313
- subject.get_connec_entities(sync.updated_at)
314
- end
315
- end
316
-
317
- context 'with pagination' do
318
- before do
319
- 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, {}))
320
- end
321
-
322
- it 'calls get multiple times' do
323
- expect(connec_client).to receive(:get).with('people?')
324
- expect(connec_client).to receive(:get).with('people?%24skip=10&%24top=10')
325
- subject.get_connec_entities(nil)
326
- end
327
- end
328
-
329
- context 'with an actual response' do
330
- let(:people) { [{first_name: 'John'}, {last_name: 'Durand'}, {job_title: 'Engineer'}] }
331
-
332
- it 'returns an array of entities' do
333
- allow(connec_client).to receive(:get).and_return(ActionDispatch::Response.new(200, {}, {people: people}.to_json, {}))
334
- expect(subject.get_connec_entities(nil)).to eql(JSON.parse(people.to_json))
335
- end
336
- end
337
- end
338
- end
339
-
340
- describe 'failures' do
341
- context 'when no response' do
342
- before do
343
- allow(connec_client).to receive(:get).and_return(ActionDispatch::Response.new(200, {}, nil, {}))
344
- end
345
- it { expect{ subject.get_connec_entities(nil) }.to raise_error(RuntimeError) }
346
- end
347
-
348
- context 'when invalid response' do
349
- before do
350
- allow(connec_client).to receive(:get).and_return(ActionDispatch::Response.new(200, {}, {not_an_entity: []}.to_json, {}))
351
- end
352
- it { expect{ subject.get_connec_entities(nil) }.to raise_error(RuntimeError) }
353
- end
354
-
355
- context 'when no response in pagination' do
356
- before do
357
- 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, {}))
358
- end
359
- it { expect{ subject.get_connec_entities(nil) }.to raise_error(RuntimeError) }
360
- end
361
-
362
- context 'when invalid response in pagination' do
363
- before do
364
- 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, {}))
365
- end
366
- it { expect{ subject.get_connec_entities(nil) }.to raise_error(RuntimeError) }
367
- end
368
- end
369
- end
370
-
371
- describe 'push_entities_to_connec' do
372
- it 'calls push_entities_to_connec_to' do
373
- expect(subject).to receive(:push_entities_to_connec_to).with([{entity: {}, idmap: nil}], connec_name)
374
- subject.push_entities_to_connec([{entity: {}, idmap: nil}])
375
- end
376
- end
377
-
378
- describe 'push_entities_to_connec_to' do
379
- let(:idmap1) { create(:idmap, organization: organization) }
380
- let(:idmap2) { create(:idmap, organization: organization, connec_id: nil) }
381
- let(:entity1) { {name: 'John'} }
382
- let(:entity2) { {name: 'Jane'} }
383
- let(:entity_with_idmap1) { {entity: entity1, idmap: idmap1} }
384
- let(:entity_with_idmap2) { {entity: entity2, idmap: idmap2} }
385
- let(:entities_with_idmaps) { [entity_with_idmap1, entity_with_idmap2] }
386
-
387
- context 'when read only' do
388
- before do
389
- allow(subject.class).to receive(:can_write_connec?).and_return(false)
390
- end
391
-
392
- it 'does nothing' do
393
- expect(subject).to_not receive(:batch_op)
394
- subject.push_entities_to_connec_to(entities_with_idmaps, connec_name)
395
- end
396
- end
397
-
398
- context 'when no update' do
399
- before do
400
- allow(subject.class).to receive(:can_update_connec?).and_return(false)
401
- allow(connec_client).to receive(:batch).and_return(ActionDispatch::Response.new(200, {}, {results: []}.to_json, {}))
402
- end
403
-
404
- it 'filters out the one with a connec_id' do
405
- expect(subject).to receive(:batch_op).once.with('post', entity2, nil, 'people')
406
- subject.push_entities_to_connec_to(entities_with_idmaps, connec_name)
407
- end
408
- end
409
-
410
- context 'without errors' do
411
- let(:result200) { {status: 200, body: {connec_name.downcase.pluralize.to_sym => {id: [{provider: 'connec', id: 'id1'}]}}} }
412
- let(:result201) { {status: 201, body: {connec_name.downcase.pluralize.to_sym => {id: [{provider: 'connec', id: 'id2'}]}}} }
413
- before do
414
- allow(connec_client).to receive(:batch).and_return(ActionDispatch::Response.new(200, {}, {results: [result200, result201]}.to_json, {}))
415
- end
416
-
417
- let(:batch_request) {
418
- {
419
- sequential: true,
420
- ops: [
421
- {
422
- :method=>"post",
423
- :url=>"/api/v2/cld-123/people/",
424
- :params=>{:people=>{:name=>"John"}}
425
- },
426
- {
427
- :method=>"post",
428
- :url=>"/api/v2/cld-123/people/",
429
- :params=>{:people=>{:name=>"Jane"}}
430
- }
431
- ]
432
- }
433
- }
434
-
435
- it 'calls batch op' do
436
- expect(subject).to receive(:batch_op).twice
437
- subject.push_entities_to_connec_to(entities_with_idmaps, connec_name)
438
- end
439
-
440
- it 'creates a batch request' do
441
- expect(connec_client).to receive(:batch).with(batch_request)
442
- subject.push_entities_to_connec_to(entities_with_idmaps, connec_name)
443
- end
444
-
445
- it 'update the idmaps push dates' do
446
- old_push_date = idmap1.last_push_to_connec
447
-
448
- subject.push_entities_to_connec_to(entities_with_idmaps, connec_name)
449
-
450
- idmap1.reload
451
- expect(idmap1.last_push_to_connec).to_not eql(old_push_date)
452
- idmap2.reload
453
- expect(idmap2.last_push_to_connec).to_not be_nil
454
- end
455
-
456
- describe 'batch batch calls' do
457
- let(:entities) { [] }
458
- let(:results) { [] }
459
-
460
- context 'when 100 entities' do
461
- before do
462
- 100.times do
463
- entities << entity_with_idmap1
464
- results << result200
465
- end
466
- allow(connec_client).to receive(:batch).and_return(ActionDispatch::Response.new(200, {}, {results: results}.to_json, {}))
467
- end
468
-
469
- it 'does one call' do
470
- expect(connec_client).to receive(:batch).once
471
- subject.push_entities_to_connec_to(entities, connec_name)
472
- end
473
- end
474
-
475
- context 'when more than 100 entities' do
476
- before do
477
- 100.times do
478
- entities << entity_with_idmap1
479
- results << result200
480
- end
481
- entities << entity_with_idmap2
482
- allow(connec_client).to receive(:batch).and_return(ActionDispatch::Response.new(200, {}, {results: results}.to_json, {}), ActionDispatch::Response.new(200, {}, {results: [result201]}.to_json, {}))
483
- end
484
-
485
- it 'does several call' do
486
- expect(connec_client).to receive(:batch).twice
487
- subject.push_entities_to_connec_to(entities, connec_name)
488
- end
489
-
490
- it 'updates the idmap push dates' do
491
- subject.push_entities_to_connec_to(entities, connec_name)
492
- idmap2.reload
493
- expect(idmap2.last_push_to_connec).to_not be_nil
494
- end
495
- end
496
- end
497
- end
498
-
499
- context 'with errors' do
500
- let(:err_msg) { 'Not Found' }
501
- let(:result400) { {status: 400, body: err_msg} }
502
- before do
503
- allow(connec_client).to receive(:batch).and_return(ActionDispatch::Response.new(200, {}, {results: [result400, result400]}.to_json, {}))
504
- end
505
-
506
- it 'stores the errr in the idmap' do
507
- subject.push_entities_to_connec_to(entities_with_idmaps, '')
508
- idmap2.reload
509
- expect(idmap2.message).to eq result400[:body]
510
- end
511
-
512
- context 'with a long error message' do
513
- 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' }
514
- it 'truncates the error message' do
515
- subject.push_entities_to_connec_to(entities_with_idmaps, '')
516
- idmap2.reload
517
- expect(idmap2.message).to eq err_msg.truncate(255)
518
- end
519
- end
520
- end
521
- end
522
- end
523
-
524
- # External methods
525
- describe 'external methods' do
526
- before do
527
- allow(subject.class).to receive(:id_from_external_entity_hash).and_return('id')
528
- end
529
- let(:idmap1) { create(:idmap, organization: organization) }
530
- let(:idmap2) { create(:idmap, organization: organization, external_id: nil, external_entity: nil, last_push_to_external: nil) }
531
- let(:entity1) { {name: 'John'} }
532
- let(:entity2) { {name: 'Jane'} }
533
- let(:id_refs_only_connec_entity1) { {} }
534
- let(:id_refs_only_connec_entity2) { {} }
535
- let(:entity_with_idmap1) { {entity: entity1, idmap: idmap1, id_refs_only_connec_entity: id_refs_only_connec_entity1} }
536
- let(:connec_id2) { 'connec_id2' }
537
- let(:entity_with_idmap2) { {entity: entity2, idmap: idmap2, id_refs_only_connec_entity: id_refs_only_connec_entity2} }
538
- let(:entities_with_idmaps) { [entity_with_idmap1, entity_with_idmap2] }
539
-
540
- describe 'get_external_entities_wrapper' do
541
- context 'when write only' do
542
- before { allow(subject.class).to receive(:can_read_external?).and_return(false) }
543
-
544
- it 'returns an empty array and does not call get_external_entities' do
545
- expect(subject).to_not receive(:get_connec_entities)
546
- expect(subject.get_external_entities_wrapper(nil)).to eql([])
547
- end
548
- end
549
-
550
- context 'when skip external' do
551
- let(:opts) { {__skip_external: true} }
552
-
553
- it 'returns an empty array and does not call get_external_entities' do
554
- expect(subject).to_not receive(:get_connec_entities)
555
- expect(subject.get_external_entities_wrapper(nil)).to eql([])
556
- end
557
- end
558
-
559
- it 'calls get_external_entities' do
560
- expect(subject).to receive(:get_external_entities).and_return([])
561
- subject.get_external_entities_wrapper(nil)
562
- end
563
- end
564
-
565
- describe 'get_external_entities' do
566
- it { expect{ subject.get_external_entities('') }.to raise_error('Not implemented') }
567
- end
568
-
569
- describe 'push_entities_to_external' do
570
- it 'calls push_entities_to_external_to' do
571
- expect(subject).to receive(:push_entities_to_external_to).with(entities_with_idmaps, external_name)
572
- subject.push_entities_to_external(entities_with_idmaps)
573
- end
574
- end
575
-
576
- describe 'push_entities_to_external_to' do
577
- context 'when read only' do
578
- it 'does nothing' do
579
- allow(subject.class).to receive(:can_write_external?).and_return(false)
580
- expect(subject).to_not receive(:push_entity_to_external)
581
- subject.push_entities_to_external_to(entities_with_idmaps, external_name)
582
- end
583
- end
584
-
585
- it 'calls push_entity_to_external for each entity' do
586
- expect(subject).to receive(:push_entity_to_external).twice
587
- subject.push_entities_to_external_to(entities_with_idmaps, external_name)
588
- end
589
-
590
- describe 'ids' do
591
- before do
592
- allow(subject.class).to receive(:id_from_external_entity_hash).and_return('id')
593
- allow(subject).to receive(:create_external_entity).and_return({'id' => 'id'})
594
- allow(subject).to receive(:update_external_entity).and_return(nil)
595
- end
596
-
597
- context 'when ids to send to connec' do
598
- let(:batch_param) {
599
- {: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}]}}}]}
600
- }
601
-
602
- it 'does a batch call on connec' do
603
- expect(connec_client).to receive(:batch).with(batch_param).and_return(ActionDispatch::Response.new(200, {}, {results: []}.to_json, {}))
604
- subject.push_entities_to_external_to(entities_with_idmaps, external_name)
605
- end
606
- end
607
-
608
- context 'when no id to send to connec' do
609
- before do
610
- idmap2.update(external_id: 'id')
611
- end
612
-
613
- it 'does not do a call on connec' do
614
- expect(connec_client).to_not receive(:batch)
615
- subject.push_entities_to_external_to(entities_with_idmaps, external_name)
616
- end
617
- end
618
- end
619
-
620
- describe 'id_references' do
621
- let(:connec_line_id1) { 'connec_line_id1' }
622
- let(:connec_line_id2) { 'connec_line_id2' }
623
- let(:ext_line_id1) { 'ext_line_id1' }
624
- let(:ext_line_id2) { 'ext_line_id2' }
625
- let(:id_refs_only_connec_entity1) { {lines: [{id: [{provider: 'connec', realm: 'org', id: connec_line_id1}]}]}.with_indifferent_access }
626
- let(:id_refs_only_connec_entity2) { {lines: [{id: [{provider: 'connec', realm: 'org', id: connec_line_id2}]}]}.with_indifferent_access }
627
- before {
628
- allow(subject.class).to receive(:id_from_external_entity_hash).and_return('id')
629
- allow(subject.class).to receive(:references).and_return({record_references: [], id_references: ['lines/id']})
630
- allow(subject).to receive(:create_external_entity).and_return({'id' => 'id', invoice_lines: [{ID: ext_line_id1}]})
631
- allow(subject).to receive(:update_external_entity).and_return({'id' => 'id', invoice_lines: [{ID: ext_line_id2}]})
632
- allow(subject).to receive(:map_to_connec).and_return({lines: [{id: [{id: ext_line_id1, provider: organization.oauth_provider, realm: organization.oauth_uid}]}]}, {lines: [{id: [{id: ext_line_id2, provider: organization.oauth_provider, realm: organization.oauth_uid}]}]})
633
- }
634
- let(:batch_param) {
635
- {
636
- :sequential=>true,
637
- :ops=> [
638
- {
639
- :method=>"put",
640
- :url=>"/api/v2/cld-123/people/#{idmap1.connec_id}",
641
- :params=>{:people=>{id: [{:id=>idmap1.external_id, :provider=>organization.oauth_provider, :realm=>organization.oauth_uid}], lines: [{id: [{provider: 'connec', realm: 'org', id: connec_line_id1}, {id: ext_line_id1, provider: organization.oauth_provider, realm: organization.oauth_uid}]}]}.with_indifferent_access}
642
- },
643
- {
644
- :method=>"put",
645
- :url=>"/api/v2/cld-123/people/#{idmap2.connec_id}",
646
- :params=>{:people=>{id: [{:id=>'id', :provider=>organization.oauth_provider, :realm=>organization.oauth_uid}], lines: [{id: [{provider: 'connec', realm: 'org', id: connec_line_id2}, {id: ext_line_id2, provider: organization.oauth_provider, realm: organization.oauth_uid}]}]}.with_indifferent_access}
647
- }
648
- ]
649
- }
650
- }
651
-
652
- it 'send both the id and the id references to connec' do
653
- expect(connec_client).to receive(:batch).with(batch_param).and_return(ActionDispatch::Response.new(200, {}, {results: []}.to_json, {}))
654
- subject.push_entities_to_external_to(entities_with_idmaps, external_name)
655
- end
656
- end
657
- end
658
-
659
- describe 'push_entity_to_external' do
660
- context 'when the entity idmap has an external id' do
661
- it 'does not calls update if create_only' do
662
- allow(subject.class).to receive(:can_update_external?).and_return(false)
663
- expect(subject).to_not receive(:update_external_entity)
664
- expect(subject.push_entity_to_external(entity_with_idmap1, external_name)).to be_nil
665
- end
666
-
667
- it 'calls update_external_entity' do
668
- expect(subject).to receive(:update_external_entity).with(entity1, idmap1.external_id, external_name)
669
- subject.push_entity_to_external(entity_with_idmap1, external_name)
670
- end
671
-
672
- it 'updates the idmap last push to external' do
673
- allow(subject).to receive(:update_external_entity)
674
- time_before = idmap1.last_push_to_external
675
- expect(subject.push_entity_to_external(entity_with_idmap1, external_name)).to be_nil
676
- idmap1.reload
677
- expect(idmap1.last_push_to_external).to_not eql(time_before)
678
- end
679
- end
680
-
681
- context 'when the entity idmap does not have an external id' do
682
- it 'calls create_external_entity' do
683
- expect(subject).to receive(:create_external_entity).with(entity2, external_name)
684
- subject.push_entity_to_external(entity_with_idmap2, external_name)
685
- end
686
-
687
- it 'updates the idmap external id, entity and last push' do
688
- allow(subject).to receive(:create_external_entity).and_return({'id' => '999111'})
689
- allow(subject.class).to receive(:id_from_external_entity_hash).and_return('999111')
690
- subject.push_entity_to_external(entity_with_idmap2, external_name)
691
- idmap2.reload
692
- expect(idmap2.external_id).to eql('999111')
693
- expect(idmap2.last_push_to_external).to_not be_nil
694
- end
695
-
696
- it 'returns the idmap' do
697
- allow(subject).to receive(:create_external_entity).and_return({'id' => '999111'})
698
- allow(subject.class).to receive(:id_from_external_entity_hash).and_return('999111')
699
- expect(subject.push_entity_to_external(entity_with_idmap2, external_name)).to eql({idmap: idmap2, completed_hash: nil})
700
- end
701
- end
702
-
703
- describe 'failures' do
704
-
705
- it 'stores the error in the idmap' do
706
- allow(subject).to receive(:create_external_entity).and_raise('Kabooooom')
707
- allow(subject).to receive(:update_external_entity).and_raise('Kabooooom')
708
- subject.push_entity_to_external(entity_with_idmap1, external_name)
709
- subject.push_entity_to_external(entity_with_idmap2, external_name)
710
- expect(idmap1.reload.message).to include('Kabooooom')
711
- expect(idmap2.reload.message).to include('Kabooooom')
712
- end
713
-
714
- it 'truncates the message' do
715
- 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.'
716
- allow(subject).to receive(:create_external_entity).and_raise(msg)
717
- subject.push_entity_to_external(entity_with_idmap2, external_name)
718
- expect(idmap2.reload.message).to include(msg.truncate(255))
719
- end
720
-
721
- it 'can raise a custom exception' do
722
- allow(subject).to receive(:update_external_entity).and_raise(Maestrano::Connector::Rails::Exceptions::EntityNotFoundError.new)
723
- subject.push_entity_to_external(entity_with_idmap1, external_name)
724
- expect(idmap1.reload.message).to include("The external_name record has been deleted in External app.")
725
- end
726
- end
727
- end
728
-
729
- describe 'create_external_entity' do
730
- let(:organization) { create(:organization) }
731
-
732
- it { expect{ subject.create_external_entity(nil, nil) }.to raise_error('Not implemented') }
733
- end
734
-
735
- describe 'update_external_entity' do
736
- let(:organization) { create(:organization) }
737
-
738
- it { expect{ subject.update_external_entity(nil, nil, nil) }.to raise_error('Not implemented') }
739
- end
740
- end
741
-
742
- describe 'consolidate_and_map methods' do
743
- let(:id) { '56882' }
744
- let(:date) { 2.hour.ago }
745
- before do
746
- allow(subject.class).to receive(:id_from_external_entity_hash).and_return(id)
747
- allow(subject.class).to receive(:last_update_date_from_external_entity_hash).and_return(date)
748
- allow(subject.class).to receive(:creation_date_from_external_entity_hash).and_return(date)
749
- end
750
-
751
- describe 'consolidate_and_map_data' do
752
- context 'singleton' do
753
- before do
754
- allow(subject.class).to receive(:singleton?).and_return(true)
755
- end
756
-
757
- it 'returns the consolidate_and_map_singleton method result' do
758
- expect(subject).to receive(:consolidate_and_map_singleton).with({}, {}).and_return({result: 1})
759
- expect(subject.consolidate_and_map_data({}, {})).to eql({result: 1})
760
- end
761
- end
762
-
763
- context 'not singleton' do
764
- it 'calls the consolidation on both connec and external and returns an hash with the results' do
765
- expect(subject).to receive(:consolidate_and_map_connec_entities).with({}, {}, [], external_name).and_return({connec_result: 1})
766
- expect(subject).to receive(:consolidate_and_map_external_entities).with({}, connec_name).and_return({ext_result: 1})
767
- expect(subject.consolidate_and_map_data({}, {})).to eql({connec_entities: {connec_result: 1}, external_entities: {ext_result: 1}})
768
- end
769
- end
770
- end
771
-
772
- describe 'consolidate_and_map_singleton' do
773
- let(:connec_id) { [{'id' => 'lala', 'provider' => 'connec', 'realm' => 'realm'}] }
774
- before do
775
- allow(subject).to receive(:map_to_connec).and_return({map: 'connec'})
776
- allow(subject).to receive(:map_to_external).and_return({map: 'external'})
777
- allow(subject.class).to receive(:object_name_from_connec_entity_hash).and_return('connec human name')
778
- allow(subject.class).to receive(:object_name_from_external_entity_hash).and_return('external human name')
779
- end
780
-
781
- it { expect(subject.consolidate_and_map_singleton([], [])).to eql({connec_entities: [], external_entities: []}) }
782
-
783
- context 'with no idmap' do
784
- it 'creates one for connec' do
785
- expect{
786
- subject.consolidate_and_map_singleton([{'id' => connec_id}], [])
787
- }.to change{ Maestrano::Connector::Rails::IdMap.count }.by(1)
788
- idmap = Maestrano::Connector::Rails::IdMap.last
789
- expect(idmap.connec_entity).to eql(connec_name.downcase)
790
- expect(idmap.external_entity).to eql(external_name.downcase)
791
- expect(idmap.name).to eql('connec human name')
792
- end
793
-
794
- it 'creates one for external' do
795
- expect{
796
- subject.consolidate_and_map_singleton([], [{}])
797
- }.to change{ Maestrano::Connector::Rails::IdMap.count }.by(1)
798
- idmap = Maestrano::Connector::Rails::IdMap.last
799
- expect(idmap.connec_entity).to eql(connec_name.downcase)
800
- expect(idmap.external_entity).to eql(external_name.downcase)
801
- expect(idmap.external_id).to eql(id)
802
- expect(idmap.name).to eql('external human name')
803
- end
804
- end
805
-
806
- context 'with an idmap' do
807
- let!(:idmap) { create(:idmap, connec_entity: connec_name.downcase, external_entity: external_name.downcase, organization: organization) }
808
-
809
- it { expect{ subject.consolidate_and_map_singleton([{'id' => connec_id}], []) }.to_not change{ Maestrano::Connector::Rails::IdMap.count } }
810
- end
811
-
812
- context 'with conflict' do
813
- let!(:idmap) { create(:idmap, connec_entity: connec_name.downcase, external_entity: external_name.downcase, organization: organization, external_id: id) }
814
- let(:updated) { 3.hour.ago }
815
- let(:connec_entity) { {'id' => connec_id, 'updated_at' => updated} }
816
-
817
- context 'with options' do
818
- it 'keep the external one if connec_preemption is false' do
819
- subject.instance_variable_set(:@opts, {connec_preemption: false})
820
- expect(subject.consolidate_and_map_singleton([connec_entity], [{}])).to eql({connec_entities: [], external_entities: [{entity: {map: 'connec'}, idmap: idmap}]})
821
- end
822
-
823
- context 'when connec preemption is true' do
824
- let(:opts) { {connec_preemption: true} }
825
-
826
- it 'keep the connec one' do
827
- expect(subject.consolidate_and_map_singleton([connec_entity], [{}])).to eql({connec_entities: [{entity: {map: 'external'}, idmap: idmap, id_refs_only_connec_entity: {}}], external_entities: []})
828
- end
829
-
830
- it 'map with the unfolded references' do
831
- expect(subject).to receive(:map_to_external).with('id' => nil, 'updated_at' => updated)
832
- subject.consolidate_and_map_singleton([connec_entity], [{}])
833
- end
834
- end
835
- end
836
-
837
- context 'without options' do
838
- context 'with a more recent external one' do
839
- it { expect(subject.consolidate_and_map_singleton([connec_entity], [{}])).to eql({connec_entities: [], external_entities: [{entity: {map: 'connec'}, idmap: idmap}]}) }
840
- end
841
- context 'with a more recent connec one' do
842
- let(:updated) { 2.minute.ago }
843
- it { expect(subject.consolidate_and_map_singleton([connec_entity], [{}])).to eql({connec_entities: [{entity: {map: 'external'}, idmap: idmap, id_refs_only_connec_entity: {}}], external_entities: []}) }
844
- end
845
- end
846
- end
847
- end
848
-
849
- describe 'consolidate_and_map_connec_entities' do
850
- let(:connec_human_name) { 'connec human name' }
851
- let(:id1) { 'external-unfolded-id' }
852
- let(:id2) { nil }
853
- let(:connec_id1) { 'connec-id-1' }
854
- let(:connec_id2) { 'connec-id-2' }
855
- let(:entity1) { {'id' => id1, 'name' => 'John', 'updated_at' => date, 'created_at' => date} }
856
- let(:entity2) { {'id' => id2, 'name' => 'Jane', 'updated_at' => date, 'created_at' => date} }
857
- let(:entity_without_refs) { {} }
858
- let(:entities) { [entity1, entity2] }
859
- let(:id_refs_only_connec_entity) { {a:1} }
860
- before {
861
- allow(subject.class).to receive(:object_name_from_connec_entity_hash).and_return(connec_human_name)
862
- allow(subject).to receive(:map_to_external).and_return({mapped: 'entity'})
863
- allow(Maestrano::Connector::Rails::ConnecHelper).to receive(:unfold_references).and_return({entity: entity1, connec_id: connec_id1, id_refs_only_connec_entity: id_refs_only_connec_entity}, {entity: entity2, connec_id: connec_id2, id_refs_only_connec_entity: id_refs_only_connec_entity}, {entity: nil})
864
- }
865
-
866
- context 'when idmaps do not exist' do
867
- it 'creates the idmaps with a name and returns the mapped entities with their idmaps' do
868
- expect{
869
- expect(subject.consolidate_and_map_connec_entities(entities, [], [], external_name)).to eql([{entity: {mapped: 'entity'}, idmap: Maestrano::Connector::Rails::IdMap.first, id_refs_only_connec_entity: id_refs_only_connec_entity}, {entity: {mapped: 'entity'}, idmap: Maestrano::Connector::Rails::IdMap.last, id_refs_only_connec_entity: id_refs_only_connec_entity}])
870
- }.to change{ Maestrano::Connector::Rails::IdMap.count }.by(2)
871
- expect(Maestrano::Connector::Rails::IdMap.last.name).to eql(connec_human_name)
872
- end
873
- end
874
-
875
- context 'when idmap exists' do
876
- let(:entities) { [entity1] }
877
- let!(:idmap1) { create(:idmap, organization: organization, connec_entity: connec_name.downcase, external_entity: external_name.downcase, external_id: id1, connec_id: connec_id1) }
878
-
879
- it 'does not create an idmap' do
880
- expect{
881
- subject.consolidate_and_map_connec_entities(entities, [], [], external_name)
882
- }.to_not change{ Maestrano::Connector::Rails::IdMap.count }
883
- end
884
-
885
- it 'returns the entity with its idmap' do
886
- expect(subject.consolidate_and_map_connec_entities(entities, [], [], external_name)).to eql([{entity: {mapped: 'entity'}, idmap: idmap1, id_refs_only_connec_entity: id_refs_only_connec_entity}])
887
- end
888
-
889
- context 'when external inactive' do
890
- before { idmap1.update(external_inactive: true) }
891
- it 'discards the entity' do
892
- expect(subject.consolidate_and_map_connec_entities(entities, [], [], external_name)).to eql([])
893
- end
894
- end
895
-
896
- context 'when to external flag is false' do
897
- before { idmap1.update(to_external: false) }
898
- it 'discards the entity' do
899
- expect(subject.consolidate_and_map_connec_entities(entities, [], [], external_name)).to eql([])
900
- end
901
- end
902
-
903
- context 'when last_push_to_external is recent' do
904
- before { idmap1.update(last_push_to_external: 2.second.ago) }
905
- it 'discards the entity' do
906
- expect(subject.consolidate_and_map_connec_entities(entities, [], [], external_name)).to eql([])
907
- end
908
-
909
- context 'with full synchronization opts' do
910
- let(:opts) { {full_sync: true} }
911
-
912
- it 'keeps the entity' do
913
- expect(subject.consolidate_and_map_connec_entities(entities, [], [], external_name)).to eql([{entity: {mapped: 'entity'}, idmap: idmap1, id_refs_only_connec_entity: id_refs_only_connec_entity}])
914
- end
915
- end
916
- end
917
-
918
- context 'when before date_filtering_limit' do
919
- before do
920
- organization.update(date_filtering_limit: 5.minutes.ago)
921
- end
922
-
923
- it 'discards the entity' do
924
- expect(subject.consolidate_and_map_connec_entities(entities, [], [], external_name)).to eql([])
925
- end
926
-
927
- context 'with full synchronization opts' do
928
- let(:opts) { {full_sync: true} }
929
-
930
- it 'keeps the entity' do
931
- expect(subject.consolidate_and_map_connec_entities(entities, [], [], external_name)).to eql([{entity: {mapped: 'entity'}, idmap: idmap1, id_refs_only_connec_entity: id_refs_only_connec_entity}])
932
- end
933
- end
934
- end
935
-
936
- end
937
-
938
- context 'when conflict' do
939
- let(:entities) { [entity1] }
940
- let(:external_entity_1) { {'id' => id1} }
941
- let(:external_entities) { [external_entity_1] }
942
- before do
943
- allow(subject.class).to receive(:id_from_external_entity_hash).and_return(id1)
944
- end
945
-
946
- context 'with opts' do
947
- context 'with connec preemption false' do
948
- it 'discards the entity and keep the external one' do
949
- subject.instance_variable_set(:@opts, {connec_preemption: false})
950
- expect(subject.consolidate_and_map_connec_entities(entities, external_entities, [], external_name)).to eql([])
951
- expect(external_entities).to_not be_empty
952
- end
953
- end
954
-
955
- context 'with connec preemption true' do
956
- it 'keeps the entity and discards the external one' do
957
- subject.instance_variable_set(:@opts, {connec_preemption: true})
958
- expect(subject.consolidate_and_map_connec_entities(entities, external_entities, [], external_name)).to eql([{entity: {mapped: 'entity'}, idmap: Maestrano::Connector::Rails::IdMap.first, id_refs_only_connec_entity: id_refs_only_connec_entity}])
959
- expect(external_entities).to be_empty
960
- end
961
- end
962
- end
963
-
964
- context 'without opts' do
965
- before do
966
- allow(subject.class).to receive(:last_update_date_from_external_entity_hash).and_return(external_date)
967
- end
968
-
969
- context 'with connec one more recent' do
970
- let(:external_date) { 1.year.ago }
971
- let(:date) { 1.day.ago }
972
-
973
- it 'keeps the entity and discards the external one' do
974
- expect(subject.consolidate_and_map_connec_entities(entities, external_entities, [], external_name)).to eql([{entity: {mapped: 'entity'}, idmap: Maestrano::Connector::Rails::IdMap.first, id_refs_only_connec_entity: id_refs_only_connec_entity}])
975
- expect(external_entities).to be_empty
976
- end
977
- end
978
-
979
- context 'with external one more recent' do
980
- let(:external_date) { 1.month.ago }
981
- let(:date) { 1.year.ago }
982
-
983
- it 'discards the entity and keep the external one' do
984
- expect(subject.consolidate_and_map_connec_entities(entities, external_entities, [], external_name)).to eql([])
985
- expect(external_entities).to_not be_empty
986
- end
987
- end
988
- end
989
- end
990
- end
991
-
992
- describe 'consolidate_and_map_external_entities' do
993
- let(:entity) { {'id' => id, 'name' => 'Jane'} }
994
- let(:id) { 'id' }
995
- let(:external_human_name) { 'external human name' }
996
- before do
997
- allow(subject.class).to receive(:id_from_external_entity_hash).and_return(id)
998
- allow(subject.class).to receive(:object_name_from_external_entity_hash).and_return(external_human_name)
999
- allow(subject).to receive(:map_to_connec).and_return({mapped: 'ext_entity'})
1000
- end
1001
-
1002
- context 'when idmap exists' do
1003
- let!(:idmap) { create(:idmap, organization: organization, connec_entity: connec_name.downcase, external_entity: external_name.downcase, external_id: id) }
1004
-
1005
- it 'does not create one' do
1006
- expect{
1007
- subject.consolidate_and_map_external_entities([entity], connec_name)
1008
- }.to_not change{ Maestrano::Connector::Rails::IdMap.count }
1009
- end
1010
-
1011
- it 'returns the mapped entity with the idmap' do
1012
- expect(subject.consolidate_and_map_external_entities([entity], connec_name)).to eql([{entity: {mapped: 'ext_entity'}, idmap: idmap}])
1013
- end
1014
-
1015
- context 'when to_connec is false' do
1016
- before { idmap.update(to_connec: false) }
1017
-
1018
- it 'discards the entity' do
1019
- expect(subject.consolidate_and_map_external_entities([entity], connec_name)).to eql([])
1020
- end
1021
- end
1022
-
1023
- context 'when entity is inactive' do
1024
- before do
1025
- allow(subject.class).to receive(:inactive_from_external_entity_hash?).and_return(true)
1026
- end
1027
-
1028
- it 'discards the entity' do
1029
- expect(subject.consolidate_and_map_external_entities([entity], connec_name)).to eql([])
1030
- end
1031
-
1032
- it 'updates the idmaps' do
1033
- subject.consolidate_and_map_external_entities([entity], connec_name)
1034
- expect(idmap.reload.external_inactive).to be true
1035
- end
1036
- end
1037
-
1038
- context 'when last_push_to_connec is recent' do
1039
- before { idmap.update(last_push_to_connec: 2.second.ago) }
1040
-
1041
- it 'discards the entity' do
1042
- expect(subject.consolidate_and_map_external_entities([entity], connec_name)).to eql([])
1043
- end
1044
-
1045
- context 'with full synchronization opts' do
1046
- let(:opts) { {full_sync: true} }
1047
-
1048
- it 'keeps the entity' do
1049
- expect(subject.consolidate_and_map_external_entities([entity], connec_name)).to eql([{entity: {mapped: 'ext_entity'}, idmap: idmap}])
1050
- end
1051
- end
1052
- end
1053
-
1054
- context 'when before date_filtering_limit' do
1055
- before do
1056
- organization.update(date_filtering_limit: 5.minutes.ago)
1057
- end
1058
-
1059
- it 'discards the entity' do
1060
- expect(subject.consolidate_and_map_external_entities([entity], connec_name)).to eql([])
1061
- end
1062
-
1063
- context 'with full synchronization opts' do
1064
- let(:opts) { {full_sync: true} }
1065
-
1066
- it 'keeps the entity' do
1067
- expect(subject.consolidate_and_map_external_entities([entity], connec_name)).to eql([{entity: {mapped: 'ext_entity'}, idmap: idmap}])
1068
- end
1069
- end
1070
- end
1071
-
1072
- end
1073
- end
1074
- end
1075
-
1076
-
1077
- end
1078
- end