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.
- checksums.yaml +4 -4
- data/Gemfile +2 -0
- data/README.md +1 -1
- data/VERSION +1 -1
- data/app/controllers/maestrano/connec_controller.rb +17 -19
- data/app/jobs/maestrano/connector/rails/all_synchronizations_job.rb +1 -1
- data/app/jobs/maestrano/connector/rails/push_to_connec_job.rb +11 -11
- data/app/jobs/maestrano/connector/rails/synchronization_job.rb +20 -11
- data/app/models/maestrano/connector/rails/concerns/complex_entity.rb +66 -74
- data/app/models/maestrano/connector/rails/concerns/connec_helper.rb +102 -0
- data/app/models/maestrano/connector/rails/concerns/entity.rb +233 -231
- data/app/models/maestrano/connector/rails/concerns/external.rb +7 -0
- data/app/models/maestrano/connector/rails/concerns/sub_entity_base.rb +14 -43
- data/app/models/maestrano/connector/rails/connec_helper.rb +5 -0
- data/app/models/maestrano/connector/rails/organization.rb +8 -2
- data/db/20160524112054_add_encryption_on_oauth_keys.rb +8 -0
- data/lib/generators/connector/install_generator.rb +1 -0
- data/lib/generators/connector/templates/complex_entity_example/contact.rb +1 -1
- data/lib/generators/connector/templates/complex_entity_example/contact_and_lead.rb +2 -2
- data/lib/generators/connector/templates/entity.rb +13 -19
- data/lib/generators/connector/templates/example_entity.rb +2 -2
- data/lib/generators/connector/templates/example_entity_spec.rb +73 -0
- data/lib/generators/connector/templates/external.rb +11 -0
- data/maestrano-connector-rails.gemspec +13 -8
- data/release_notes.md +81 -0
- data/spec/controllers/connec_controller_spec.rb +19 -6
- data/spec/dummy/app/models/maestrano/connector/rails/entity.rb +0 -7
- data/spec/dummy/app/models/maestrano/connector/rails/external.rb +7 -0
- data/spec/dummy/app/views/home/index.html.erb +1 -36
- data/spec/factories.rb +3 -0
- data/spec/integration/connec_to_external_spec.rb +188 -0
- data/spec/integration/external_to_connec_spec.rb +155 -0
- data/spec/integration/integration_complex_spec.rb +281 -0
- data/spec/integration/singleton_spec.rb +288 -0
- data/spec/jobs/all_synchronizations_job_spec.rb +5 -0
- data/spec/jobs/push_to_connec_job_spec.rb +3 -6
- data/spec/jobs/synchronization_job_spec.rb +29 -17
- data/spec/models/complex_entity_spec.rb +257 -412
- data/spec/models/connec_helper_spec.rb +143 -0
- data/spec/models/entity_spec.rb +420 -348
- data/spec/models/external_spec.rb +4 -0
- data/spec/models/organization_spec.rb +2 -1
- data/spec/models/sub_entity_base_spec.rb +28 -69
- data/template/factories.rb +3 -1
- data/template/maestrano-connector-template.rb +11 -13
- data/template/maestrano.rb +2 -1
- data/template/settings/development.yml +4 -2
- data/template/settings/production.yml +1 -11
- data/template/settings/settings.yml +8 -0
- data/template/settings/test.yml +2 -0
- data/template/settings/uat.yml +1 -9
- metadata +12 -7
- data/Gemfile.lock +0 -256
- data/realse_notes.md +0 -16
- data/spec/dummy/app/views/admin/index.html.erb +0 -51
- data/spec/dummy/db/development.sqlite3 +0 -0
- 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
|
data/spec/models/entity_spec.rb
CHANGED
@@ -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
|
-
|
39
|
-
|
40
|
-
|
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
|
-
|
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({}
|
135
|
+
subject.map_to_external({})
|
137
136
|
end
|
138
137
|
|
139
|
-
|
140
|
-
|
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({}
|
149
|
+
subject.map_to_connec({})
|
158
150
|
end
|
159
151
|
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
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
|
-
|
170
|
-
|
171
|
-
|
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
|
-
|
184
|
-
|
185
|
-
|
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(
|
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(
|
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(
|
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(
|
207
|
-
subject.get_connec_entities(
|
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(
|
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(
|
219
|
-
subject.get_connec_entities(
|
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(
|
226
|
-
subject.get_connec_entities(
|
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(
|
234
|
-
subject.get_connec_entities(
|
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(
|
242
|
-
subject.get_connec_entities(
|
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(
|
248
|
-
subject.get_connec_entities(
|
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(
|
254
|
-
subject.get_connec_entities(
|
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(
|
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(
|
265
|
-
subject.get_connec_entities(
|
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(
|
274
|
-
expect(subject.get_connec_entities(
|
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 '
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
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
|
-
|
291
|
-
|
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,
|
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(
|
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(
|
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=>"
|
344
|
-
:url=>"/api/v2/cld-123/people
|
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(
|
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(
|
363
|
-
subject.push_entities_to_connec_to(
|
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(
|
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(
|
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(
|
393
|
-
subject.push_entities_to_connec_to(
|
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(
|
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(
|
409
|
-
subject.push_entities_to_connec_to(
|
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(
|
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(
|
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(
|
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(
|
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
|
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
|
-
|
545
|
-
|
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(
|
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(
|
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(
|
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(
|
576
|
-
subject.push_entity_to_external(
|
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(
|
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(
|
591
|
-
subject.push_entity_to_external(
|
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(
|
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
|
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
|
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
|
-
|
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.
|
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
|
-
|
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.
|
664
|
+
expect(idmap.name).to eql('connec human name')
|
650
665
|
end
|
651
666
|
|
652
667
|
it 'creates one for external' do
|
653
|
-
|
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.
|
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
|
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' =>
|
688
|
+
let(:connec_entity) { {'id' => connec_id, 'updated_at' => updated} }
|
671
689
|
|
672
690
|
context 'with options' do
|
673
|
-
it
|
674
|
-
|
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.
|
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.
|
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
|
-
|
690
|
-
|
691
|
-
|
692
|
-
|
693
|
-
|
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
|
-
|
696
|
-
|
697
|
-
expect
|
698
|
-
|
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
|
-
|
703
|
-
let(:
|
704
|
-
let(:
|
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
|
-
|
709
|
-
|
710
|
-
|
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
|
-
|
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
|
-
|
716
|
-
|
717
|
-
|
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
|
-
|
721
|
-
|
722
|
-
|
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
|
727
|
-
|
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
|
-
|
730
|
-
|
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
|
-
|
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
|
-
|
739
|
-
|
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
|
-
|
742
|
-
|
743
|
-
|
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
|
-
|
747
|
-
|
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
|
-
|
750
|
-
|
751
|
-
|
752
|
-
|
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 '
|
757
|
-
let
|
758
|
-
|
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 '
|
761
|
-
expect(subject.
|
762
|
-
expect(
|
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
|
-
|
768
|
-
|
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
|
-
|
771
|
-
|
772
|
-
|
773
|
-
|
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
|
-
|
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
|
-
|
779
|
-
|
869
|
+
context 'when to_connec is false' do
|
870
|
+
before { idmap.update(to_connec: false) }
|
780
871
|
|
781
|
-
|
782
|
-
|
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
|
-
|
788
|
-
|
789
|
-
|
790
|
-
|
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
|
-
|
797
|
-
|
798
|
-
|
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
|
-
|
805
|
-
|
806
|
-
|
807
|
-
|
808
|
-
|
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
|
-
|
814
|
-
|
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
|
-
|
818
|
-
|
819
|
-
|
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
|
-
|
824
|
-
|
899
|
+
context 'with full synchronization opts' do
|
900
|
+
let(:opts) { {full_sync: true} }
|
825
901
|
|
826
|
-
|
827
|
-
|
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
|