meadow-integration 1.0.18 → 1.0.20
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.
- package/docs/README.md +1 -0
- package/docs/_sidebar.md +6 -1
- package/docs/comprehension-push/configuration.md +308 -0
- package/docs/integration-adapter.md +164 -15
- package/examples/example-comprehension-push.meadow.config.json +23 -0
- package/package.json +4 -2
- package/source/Meadow-Integration.js +21 -1
- package/source/Meadow-Service-Integration-Adapter.js +678 -245
- package/source/Meadow-Service-Integration-GUIDMap.js +19 -2
- package/source/cli/commands/Meadow-Integration-Command-ComprehensionPush.js +210 -38
- package/source/services/clone/Meadow-Service-RestClient.js +46 -0
- package/source/services/parser/Service-FileParser-CSV.js +263 -0
- package/source/services/parser/Service-FileParser-FixedWidth.js +158 -0
- package/source/services/parser/Service-FileParser-JSON.js +255 -0
- package/source/services/parser/Service-FileParser-XLSX.js +194 -0
- package/source/services/parser/Service-FileParser-XML.js +190 -0
- package/source/services/parser/Service-FileParser.js +142 -0
- package/test/Meadow-Integration-ComprehensionPush_test.js +580 -0
|
@@ -0,0 +1,580 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Integration tests for Comprehension Push via Integration Adapter
|
|
3
|
+
|
|
4
|
+
Starts a retold-harness server backed by SQLite (in-memory), generates
|
|
5
|
+
random Book data with fable's DataGeneration service, pushes a
|
|
6
|
+
comprehension through the Integration Adapter, then reads the records
|
|
7
|
+
back from the API to verify they were created.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const Chai = require('chai');
|
|
11
|
+
const Expect = Chai.expect;
|
|
12
|
+
|
|
13
|
+
const libFable = require('fable');
|
|
14
|
+
const libMeadowConnectionSQLite = require('meadow-connection-sqlite');
|
|
15
|
+
const libRetoldDataService = require('retold-data-service');
|
|
16
|
+
|
|
17
|
+
const libIntegrationAdapter = require('../source/Meadow-Service-Integration-Adapter.js');
|
|
18
|
+
const libGUIDMap = require('../source/Meadow-Service-Integration-GUIDMap.js');
|
|
19
|
+
const libRestClient = require('../source/services/clone/Meadow-Service-RestClient.js');
|
|
20
|
+
|
|
21
|
+
// Use a unique port to avoid collisions with other test suites
|
|
22
|
+
const _APIServerPort = 19876;
|
|
23
|
+
const _ServerURL = `http://localhost:${_APIServerPort}/1.0/`;
|
|
24
|
+
|
|
25
|
+
let _Fable;
|
|
26
|
+
let _RetoldDataService;
|
|
27
|
+
|
|
28
|
+
suite
|
|
29
|
+
(
|
|
30
|
+
'Comprehension Push – Random Books',
|
|
31
|
+
function ()
|
|
32
|
+
{
|
|
33
|
+
suiteSetup
|
|
34
|
+
(
|
|
35
|
+
function (fDone)
|
|
36
|
+
{
|
|
37
|
+
this.timeout(15000);
|
|
38
|
+
|
|
39
|
+
let tmpSettings = {
|
|
40
|
+
Product: 'MeadowIntegrationPushTest',
|
|
41
|
+
ProductVersion: '1.0.0',
|
|
42
|
+
APIServerPort: _APIServerPort,
|
|
43
|
+
SQLite:
|
|
44
|
+
{
|
|
45
|
+
SQLiteFilePath: ':memory:'
|
|
46
|
+
},
|
|
47
|
+
LogStreams:
|
|
48
|
+
[
|
|
49
|
+
{
|
|
50
|
+
streamtype: 'console',
|
|
51
|
+
level: 'fatal'
|
|
52
|
+
}
|
|
53
|
+
]
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
_Fable = new libFable(tmpSettings);
|
|
57
|
+
|
|
58
|
+
// ---- SQLite provider ----
|
|
59
|
+
_Fable.serviceManager.addServiceType('MeadowSQLiteProvider', libMeadowConnectionSQLite);
|
|
60
|
+
_Fable.serviceManager.instantiateServiceProvider('MeadowSQLiteProvider');
|
|
61
|
+
|
|
62
|
+
_Fable.MeadowSQLiteProvider.connectAsync(
|
|
63
|
+
(pError) =>
|
|
64
|
+
{
|
|
65
|
+
if (pError)
|
|
66
|
+
{
|
|
67
|
+
return fDone(pError);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
let tmpDB = _Fable.MeadowSQLiteProvider.db;
|
|
71
|
+
|
|
72
|
+
// Create only the tables we need for this test
|
|
73
|
+
tmpDB.exec(`
|
|
74
|
+
CREATE TABLE IF NOT EXISTS User (
|
|
75
|
+
IDUser INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
76
|
+
GUIDUser INTEGER DEFAULT 0,
|
|
77
|
+
LoginID TEXT DEFAULT '',
|
|
78
|
+
Password TEXT DEFAULT '',
|
|
79
|
+
NameFirst TEXT DEFAULT '',
|
|
80
|
+
NameLast TEXT DEFAULT '',
|
|
81
|
+
FullName TEXT DEFAULT '',
|
|
82
|
+
Config TEXT DEFAULT ''
|
|
83
|
+
);
|
|
84
|
+
CREATE TABLE IF NOT EXISTS Book (
|
|
85
|
+
IDBook INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
86
|
+
GUIDBook TEXT DEFAULT '',
|
|
87
|
+
CreateDate TEXT DEFAULT '',
|
|
88
|
+
CreatingIDUser INTEGER DEFAULT 0,
|
|
89
|
+
UpdateDate TEXT DEFAULT '',
|
|
90
|
+
UpdatingIDUser INTEGER DEFAULT 0,
|
|
91
|
+
Deleted INTEGER DEFAULT 0,
|
|
92
|
+
DeleteDate TEXT DEFAULT '',
|
|
93
|
+
DeletingIDUser INTEGER DEFAULT 0,
|
|
94
|
+
Title TEXT DEFAULT '',
|
|
95
|
+
Type TEXT DEFAULT '',
|
|
96
|
+
Genre TEXT DEFAULT '',
|
|
97
|
+
ISBN TEXT DEFAULT '',
|
|
98
|
+
Language TEXT DEFAULT '',
|
|
99
|
+
ImageURL TEXT DEFAULT '',
|
|
100
|
+
PublicationYear INTEGER DEFAULT 0
|
|
101
|
+
);
|
|
102
|
+
CREATE TABLE IF NOT EXISTS BookAuthorJoin (
|
|
103
|
+
IDBookAuthorJoin INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
104
|
+
GUIDBookAuthorJoin TEXT DEFAULT '',
|
|
105
|
+
IDBook INTEGER DEFAULT 0,
|
|
106
|
+
IDAuthor INTEGER DEFAULT 0
|
|
107
|
+
);
|
|
108
|
+
CREATE TABLE IF NOT EXISTS Author (
|
|
109
|
+
IDAuthor INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
110
|
+
GUIDAuthor TEXT DEFAULT '',
|
|
111
|
+
CreateDate TEXT DEFAULT '',
|
|
112
|
+
CreatingIDUser INTEGER DEFAULT 0,
|
|
113
|
+
UpdateDate TEXT DEFAULT '',
|
|
114
|
+
UpdatingIDUser INTEGER DEFAULT 0,
|
|
115
|
+
Deleted INTEGER DEFAULT 0,
|
|
116
|
+
DeleteDate TEXT DEFAULT '',
|
|
117
|
+
DeletingIDUser INTEGER DEFAULT 0,
|
|
118
|
+
Name TEXT DEFAULT '',
|
|
119
|
+
IDUser INTEGER DEFAULT 0
|
|
120
|
+
);
|
|
121
|
+
CREATE TABLE IF NOT EXISTS BookPrice (
|
|
122
|
+
IDBookPrice INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
123
|
+
GUIDBookPrice TEXT DEFAULT '',
|
|
124
|
+
CreateDate TEXT DEFAULT '',
|
|
125
|
+
CreatingIDUser INTEGER DEFAULT 0,
|
|
126
|
+
UpdateDate TEXT DEFAULT '',
|
|
127
|
+
UpdatingIDUser INTEGER DEFAULT 0,
|
|
128
|
+
Deleted INTEGER DEFAULT 0,
|
|
129
|
+
DeleteDate TEXT DEFAULT '',
|
|
130
|
+
DeletingIDUser INTEGER DEFAULT 0,
|
|
131
|
+
Price REAL DEFAULT 0,
|
|
132
|
+
StartDate TEXT DEFAULT '',
|
|
133
|
+
EndDate TEXT DEFAULT '',
|
|
134
|
+
Discountable INTEGER DEFAULT 0,
|
|
135
|
+
CouponCode TEXT DEFAULT '',
|
|
136
|
+
IDBook INTEGER DEFAULT 0
|
|
137
|
+
);
|
|
138
|
+
CREATE TABLE IF NOT EXISTS BookStore (
|
|
139
|
+
IDBookStore INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
140
|
+
GUIDBookStore TEXT DEFAULT '',
|
|
141
|
+
CreateDate TEXT DEFAULT '',
|
|
142
|
+
CreatingIDUser INTEGER DEFAULT 0,
|
|
143
|
+
UpdateDate TEXT DEFAULT '',
|
|
144
|
+
UpdatingIDUser INTEGER DEFAULT 0,
|
|
145
|
+
Deleted INTEGER DEFAULT 0,
|
|
146
|
+
DeleteDate TEXT DEFAULT '',
|
|
147
|
+
DeletingIDUser INTEGER DEFAULT 0,
|
|
148
|
+
Name TEXT DEFAULT '',
|
|
149
|
+
Address TEXT DEFAULT '',
|
|
150
|
+
City TEXT DEFAULT '',
|
|
151
|
+
State TEXT DEFAULT '',
|
|
152
|
+
Postal TEXT DEFAULT '',
|
|
153
|
+
Country TEXT DEFAULT ''
|
|
154
|
+
);
|
|
155
|
+
CREATE TABLE IF NOT EXISTS BookStoreInventory (
|
|
156
|
+
IDBookStoreInventory INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
157
|
+
GUIDBookStoreInventory TEXT DEFAULT '',
|
|
158
|
+
CreateDate TEXT DEFAULT '',
|
|
159
|
+
CreatingIDUser INTEGER DEFAULT 0,
|
|
160
|
+
UpdateDate TEXT DEFAULT '',
|
|
161
|
+
UpdatingIDUser INTEGER DEFAULT 0,
|
|
162
|
+
Deleted INTEGER DEFAULT 0,
|
|
163
|
+
DeleteDate TEXT DEFAULT '',
|
|
164
|
+
DeletingIDUser INTEGER DEFAULT 0,
|
|
165
|
+
StockDate TEXT DEFAULT '',
|
|
166
|
+
BookCount INTEGER DEFAULT 0,
|
|
167
|
+
AggregateBookCount INTEGER DEFAULT 0,
|
|
168
|
+
IDBook INTEGER DEFAULT 0,
|
|
169
|
+
IDBookStore INTEGER DEFAULT 0,
|
|
170
|
+
IDBookPrice INTEGER DEFAULT 0,
|
|
171
|
+
StockingAssociate INTEGER DEFAULT 0
|
|
172
|
+
);
|
|
173
|
+
CREATE TABLE IF NOT EXISTS Review (
|
|
174
|
+
IDReview INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
175
|
+
GUIDReview TEXT DEFAULT '',
|
|
176
|
+
CreateDate TEXT DEFAULT '',
|
|
177
|
+
CreatingIDUser INTEGER DEFAULT 0,
|
|
178
|
+
UpdateDate TEXT DEFAULT '',
|
|
179
|
+
UpdatingIDUser INTEGER DEFAULT 0,
|
|
180
|
+
Deleted INTEGER DEFAULT 0,
|
|
181
|
+
DeleteDate TEXT DEFAULT '',
|
|
182
|
+
DeletingIDUser INTEGER DEFAULT 0,
|
|
183
|
+
Text TEXT DEFAULT '',
|
|
184
|
+
Rating INTEGER DEFAULT 0,
|
|
185
|
+
IDBook INTEGER DEFAULT 0,
|
|
186
|
+
IDUser INTEGER DEFAULT 0
|
|
187
|
+
);
|
|
188
|
+
`);
|
|
189
|
+
|
|
190
|
+
// Seed a minimal user so CreatingIDUser 0 is fine
|
|
191
|
+
let tmpInsertUser = tmpDB.prepare(
|
|
192
|
+
`INSERT INTO User (IDUser, GUIDUser, LoginID, Password, NameFirst, NameLast, FullName, Config)
|
|
193
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`);
|
|
194
|
+
tmpInsertUser.run(1, 1001, 'admin', 'hash123', 'Admin', 'User', 'Admin User', '{}');
|
|
195
|
+
|
|
196
|
+
// ---- RetoldDataService ----
|
|
197
|
+
_Fable.serviceManager.addServiceType('RetoldDataService', libRetoldDataService);
|
|
198
|
+
_RetoldDataService = _Fable.serviceManager.instantiateServiceProvider('RetoldDataService',
|
|
199
|
+
{
|
|
200
|
+
FullMeadowSchemaPath: `${__dirname}/../node_modules/retold-harness/source/schemas/bookstore/`,
|
|
201
|
+
FullMeadowSchemaFilename: `Schema.json`,
|
|
202
|
+
|
|
203
|
+
StorageProvider: 'SQLite',
|
|
204
|
+
StorageProviderModule: 'meadow-connection-sqlite',
|
|
205
|
+
|
|
206
|
+
AutoInitializeDataService: true,
|
|
207
|
+
AutoStartOrator: true
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
_RetoldDataService.initializeService(
|
|
211
|
+
(pInitError) =>
|
|
212
|
+
{
|
|
213
|
+
if (pInitError)
|
|
214
|
+
{
|
|
215
|
+
return fDone(pInitError);
|
|
216
|
+
}
|
|
217
|
+
return fDone();
|
|
218
|
+
});
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
);
|
|
222
|
+
|
|
223
|
+
suiteTeardown
|
|
224
|
+
(
|
|
225
|
+
function (fDone)
|
|
226
|
+
{
|
|
227
|
+
this.timeout(5000);
|
|
228
|
+
if (_Fable && _Fable.MeadowSQLiteProvider && _Fable.MeadowSQLiteProvider.db)
|
|
229
|
+
{
|
|
230
|
+
try { _Fable.MeadowSQLiteProvider.db.close(); }
|
|
231
|
+
catch (pIgnore) { /* already closed */ }
|
|
232
|
+
}
|
|
233
|
+
if (_Fable && _Fable.OratorServiceServer && _Fable.OratorServiceServer.Active && _Fable.OratorServiceServer.server)
|
|
234
|
+
{
|
|
235
|
+
_Fable.OratorServiceServer.server.close(
|
|
236
|
+
() =>
|
|
237
|
+
{
|
|
238
|
+
_Fable.OratorServiceServer.Active = false;
|
|
239
|
+
fDone();
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
else
|
|
243
|
+
{
|
|
244
|
+
fDone();
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
);
|
|
248
|
+
|
|
249
|
+
suite
|
|
250
|
+
(
|
|
251
|
+
'Generate and Push Random Books',
|
|
252
|
+
function ()
|
|
253
|
+
{
|
|
254
|
+
let _GeneratedBooks = [];
|
|
255
|
+
let _BookCount = 10;
|
|
256
|
+
|
|
257
|
+
test
|
|
258
|
+
(
|
|
259
|
+
'Generate random book records using fable DataGeneration',
|
|
260
|
+
function (fDone)
|
|
261
|
+
{
|
|
262
|
+
_Fable.instantiateServiceProviderIfNotExists('DataGeneration');
|
|
263
|
+
let tmpDataGen = _Fable.DataGeneration;
|
|
264
|
+
|
|
265
|
+
Expect(tmpDataGen).to.be.an('object');
|
|
266
|
+
|
|
267
|
+
let tmpGenres = ['Science Fiction', 'Fantasy', 'Mystery', 'Romance', 'Thriller', 'Horror', 'Historical', 'Comedy'];
|
|
268
|
+
let tmpTypes = ['Fiction', 'Non-Fiction'];
|
|
269
|
+
let tmpLanguages = ['English', 'Spanish', 'French', 'German', 'Japanese'];
|
|
270
|
+
|
|
271
|
+
for (let i = 0; i < _BookCount; i++)
|
|
272
|
+
{
|
|
273
|
+
// Combine random data to create fun book titles like "The Blue Tuesday Chronicles"
|
|
274
|
+
let tmpTitle = `The ${tmpDataGen.randomColor()} ${tmpDataGen.randomDayOfWeek()} of ${tmpDataGen.randomName()} ${tmpDataGen.randomSurname()}`;
|
|
275
|
+
let tmpGenre = tmpGenres[tmpDataGen.randomIntegerUpTo(tmpGenres.length)];
|
|
276
|
+
let tmpType = tmpTypes[tmpDataGen.randomIntegerUpTo(tmpTypes.length)];
|
|
277
|
+
let tmpLanguage = tmpLanguages[tmpDataGen.randomIntegerUpTo(tmpLanguages.length)];
|
|
278
|
+
let tmpYear = tmpDataGen.randomIntegerBetween(1900, 2025);
|
|
279
|
+
let tmpISBN = `978-${tmpDataGen.randomNumericString(10)}`;
|
|
280
|
+
|
|
281
|
+
_GeneratedBooks.push(
|
|
282
|
+
{
|
|
283
|
+
GUIDBook: `RandBook-${i}`,
|
|
284
|
+
Title: tmpTitle,
|
|
285
|
+
Type: tmpType,
|
|
286
|
+
Genre: tmpGenre,
|
|
287
|
+
ISBN: tmpISBN,
|
|
288
|
+
Language: tmpLanguage,
|
|
289
|
+
PublicationYear: tmpYear
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
Expect(_GeneratedBooks.length).to.equal(_BookCount);
|
|
294
|
+
|
|
295
|
+
// Verify each book has the expected structure
|
|
296
|
+
for (let i = 0; i < _GeneratedBooks.length; i++)
|
|
297
|
+
{
|
|
298
|
+
Expect(_GeneratedBooks[i]).to.have.property('GUIDBook');
|
|
299
|
+
Expect(_GeneratedBooks[i]).to.have.property('Title');
|
|
300
|
+
Expect(_GeneratedBooks[i].Title.length).to.be.greaterThan(0);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
return fDone();
|
|
304
|
+
}
|
|
305
|
+
);
|
|
306
|
+
|
|
307
|
+
test
|
|
308
|
+
(
|
|
309
|
+
'Push generated books through the Integration Adapter',
|
|
310
|
+
function (fDone)
|
|
311
|
+
{
|
|
312
|
+
this.timeout(30000);
|
|
313
|
+
|
|
314
|
+
// Register services on fable
|
|
315
|
+
_Fable.serviceManager.addServiceType('IntegrationAdapter', libIntegrationAdapter);
|
|
316
|
+
_Fable.serviceManager.addServiceType('MeadowGUIDMap', libGUIDMap);
|
|
317
|
+
_Fable.serviceManager.addServiceType('MeadowCloneRestClient', libRestClient);
|
|
318
|
+
|
|
319
|
+
// Create a REST client pointing at our in-memory harness server
|
|
320
|
+
let tmpRestClient = _Fable.serviceManager.instantiateServiceProvider('MeadowCloneRestClient',
|
|
321
|
+
{
|
|
322
|
+
ServerURL: _ServerURL
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
Expect(tmpRestClient).to.be.an('object');
|
|
326
|
+
|
|
327
|
+
// Create the adapter for the Book entity
|
|
328
|
+
let tmpAdapter = _Fable.serviceManager.instantiateServiceProvider('IntegrationAdapter',
|
|
329
|
+
{
|
|
330
|
+
Entity: 'Book',
|
|
331
|
+
AdapterSetGUIDMarshalPrefix: 'TEST',
|
|
332
|
+
EntityGUIDMarshalPrefix: 'BK',
|
|
333
|
+
ForceMarshal: true
|
|
334
|
+
}, 'Book');
|
|
335
|
+
|
|
336
|
+
tmpAdapter.setRestClient(tmpRestClient);
|
|
337
|
+
|
|
338
|
+
Expect(tmpAdapter).to.be.an('object');
|
|
339
|
+
Expect(tmpAdapter.Entity).to.equal('Book');
|
|
340
|
+
|
|
341
|
+
// Add each generated book as a source record
|
|
342
|
+
for (let i = 0; i < _GeneratedBooks.length; i++)
|
|
343
|
+
{
|
|
344
|
+
tmpAdapter.addSourceRecord(_GeneratedBooks[i]);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// Push the records to the server
|
|
348
|
+
tmpAdapter.integrateRecords(
|
|
349
|
+
(pError) =>
|
|
350
|
+
{
|
|
351
|
+
Expect(pError).to.not.be.an('Error');
|
|
352
|
+
|
|
353
|
+
// Verify GUIDs were mapped
|
|
354
|
+
let tmpGUIDMap = _Fable.MeadowGUIDMap;
|
|
355
|
+
for (let i = 0; i < _GeneratedBooks.length; i++)
|
|
356
|
+
{
|
|
357
|
+
let tmpMeadowGUID = tmpGUIDMap.getMeadowGUIDFromExternalGUID('Book', _GeneratedBooks[i].GUIDBook);
|
|
358
|
+
Expect(tmpMeadowGUID).to.be.a('string');
|
|
359
|
+
Expect(tmpMeadowGUID).to.contain('TEST-');
|
|
360
|
+
Expect(tmpMeadowGUID).to.contain('BK-');
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
return fDone();
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
);
|
|
367
|
+
|
|
368
|
+
test
|
|
369
|
+
(
|
|
370
|
+
'Read back the pushed books from the REST API',
|
|
371
|
+
function (fDone)
|
|
372
|
+
{
|
|
373
|
+
this.timeout(15000);
|
|
374
|
+
|
|
375
|
+
let tmpRestClient = _Fable.MeadowCloneRestClient;
|
|
376
|
+
Expect(tmpRestClient).to.be.an('object');
|
|
377
|
+
|
|
378
|
+
// Read each pushed book by its generated Meadow GUID
|
|
379
|
+
let tmpGUIDMap = _Fable.MeadowGUIDMap;
|
|
380
|
+
let tmpRemaining = _GeneratedBooks.length;
|
|
381
|
+
let tmpVerified = 0;
|
|
382
|
+
|
|
383
|
+
for (let i = 0; i < _GeneratedBooks.length; i++)
|
|
384
|
+
{
|
|
385
|
+
let tmpOriginal = _GeneratedBooks[i];
|
|
386
|
+
let tmpMeadowGUID = tmpGUIDMap.getMeadowGUIDFromExternalGUID('Book', tmpOriginal.GUIDBook);
|
|
387
|
+
|
|
388
|
+
tmpRestClient.getEntityByGUID('Book', tmpMeadowGUID,
|
|
389
|
+
(pError, pBody) =>
|
|
390
|
+
{
|
|
391
|
+
Expect(pError).to.not.be.an('Error');
|
|
392
|
+
Expect(pBody).to.be.an('object');
|
|
393
|
+
Expect(pBody.IDBook).to.be.greaterThan(0);
|
|
394
|
+
Expect(pBody.GUIDBook).to.equal(tmpMeadowGUID);
|
|
395
|
+
Expect(pBody.Title).to.equal(tmpOriginal.Title);
|
|
396
|
+
Expect(pBody.Genre).to.equal(tmpOriginal.Genre);
|
|
397
|
+
Expect(pBody.Type).to.equal(tmpOriginal.Type);
|
|
398
|
+
Expect(pBody.Language).to.equal(tmpOriginal.Language);
|
|
399
|
+
Expect(pBody.ISBN).to.equal(tmpOriginal.ISBN);
|
|
400
|
+
|
|
401
|
+
tmpVerified++;
|
|
402
|
+
tmpRemaining--;
|
|
403
|
+
|
|
404
|
+
if (tmpRemaining <= 0)
|
|
405
|
+
{
|
|
406
|
+
Expect(tmpVerified).to.equal(_BookCount);
|
|
407
|
+
return fDone();
|
|
408
|
+
}
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
);
|
|
413
|
+
|
|
414
|
+
test
|
|
415
|
+
(
|
|
416
|
+
'Upsert existing books with updated titles',
|
|
417
|
+
function (fDone)
|
|
418
|
+
{
|
|
419
|
+
this.timeout(30000);
|
|
420
|
+
|
|
421
|
+
// Create a new adapter for the same entity (will re-use the GUIDMap)
|
|
422
|
+
let tmpAdapter = libIntegrationAdapter.getAdapter(_Fable, 'Book', 'BK',
|
|
423
|
+
{
|
|
424
|
+
AdapterSetGUIDMarshalPrefix: 'TEST',
|
|
425
|
+
ForceMarshal: true
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
tmpAdapter.setRestClient(_Fable.MeadowCloneRestClient);
|
|
429
|
+
|
|
430
|
+
// Update the title of each generated book
|
|
431
|
+
for (let i = 0; i < _GeneratedBooks.length; i++)
|
|
432
|
+
{
|
|
433
|
+
let tmpUpdated = Object.assign({}, _GeneratedBooks[i]);
|
|
434
|
+
tmpUpdated.Title = `Updated: ${tmpUpdated.Title}`;
|
|
435
|
+
tmpAdapter.addSourceRecord(tmpUpdated);
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
tmpAdapter.integrateRecords(
|
|
439
|
+
(pError) =>
|
|
440
|
+
{
|
|
441
|
+
Expect(pError).to.not.be.an('Error');
|
|
442
|
+
return fDone();
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
);
|
|
446
|
+
|
|
447
|
+
test
|
|
448
|
+
(
|
|
449
|
+
'Verify updated titles via REST API',
|
|
450
|
+
function (fDone)
|
|
451
|
+
{
|
|
452
|
+
this.timeout(15000);
|
|
453
|
+
|
|
454
|
+
let tmpRestClient = _Fable.MeadowCloneRestClient;
|
|
455
|
+
let tmpGUIDMap = _Fable.MeadowGUIDMap;
|
|
456
|
+
let tmpRemaining = _GeneratedBooks.length;
|
|
457
|
+
let tmpVerified = 0;
|
|
458
|
+
|
|
459
|
+
for (let i = 0; i < _GeneratedBooks.length; i++)
|
|
460
|
+
{
|
|
461
|
+
let tmpOriginal = _GeneratedBooks[i];
|
|
462
|
+
let tmpMeadowGUID = tmpGUIDMap.getMeadowGUIDFromExternalGUID('Book', tmpOriginal.GUIDBook);
|
|
463
|
+
|
|
464
|
+
tmpRestClient.getEntityByGUID('Book', tmpMeadowGUID,
|
|
465
|
+
(pError, pBody) =>
|
|
466
|
+
{
|
|
467
|
+
Expect(pError).to.not.be.an('Error');
|
|
468
|
+
Expect(pBody).to.be.an('object');
|
|
469
|
+
Expect(pBody.GUIDBook).to.equal(tmpMeadowGUID);
|
|
470
|
+
Expect(pBody.Title).to.equal(`Updated: ${tmpOriginal.Title}`);
|
|
471
|
+
|
|
472
|
+
tmpVerified++;
|
|
473
|
+
tmpRemaining--;
|
|
474
|
+
|
|
475
|
+
if (tmpRemaining <= 0)
|
|
476
|
+
{
|
|
477
|
+
Expect(tmpVerified).to.equal(_BookCount);
|
|
478
|
+
return fDone();
|
|
479
|
+
}
|
|
480
|
+
});
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
);
|
|
484
|
+
|
|
485
|
+
test
|
|
486
|
+
(
|
|
487
|
+
'Push authors and join records with FK resolution',
|
|
488
|
+
function (fDone)
|
|
489
|
+
{
|
|
490
|
+
this.timeout(30000);
|
|
491
|
+
|
|
492
|
+
// Generate some random authors
|
|
493
|
+
_Fable.instantiateServiceProviderIfNotExists('DataGeneration');
|
|
494
|
+
let tmpDataGen = _Fable.DataGeneration;
|
|
495
|
+
|
|
496
|
+
// Create 3 random authors
|
|
497
|
+
let tmpAuthors = [];
|
|
498
|
+
for (let i = 0; i < 3; i++)
|
|
499
|
+
{
|
|
500
|
+
tmpAuthors.push(
|
|
501
|
+
{
|
|
502
|
+
GUIDAuthor: `RandAuthor-${i}`,
|
|
503
|
+
Name: `${tmpDataGen.randomName()} ${tmpDataGen.randomSurname()}`
|
|
504
|
+
});
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
// Push authors first
|
|
508
|
+
let tmpAuthorAdapter = libIntegrationAdapter.getAdapter(_Fable, 'Author', 'AU',
|
|
509
|
+
{
|
|
510
|
+
AdapterSetGUIDMarshalPrefix: 'TEST',
|
|
511
|
+
ForceMarshal: true
|
|
512
|
+
});
|
|
513
|
+
tmpAuthorAdapter.setRestClient(_Fable.MeadowCloneRestClient);
|
|
514
|
+
|
|
515
|
+
for (let i = 0; i < tmpAuthors.length; i++)
|
|
516
|
+
{
|
|
517
|
+
tmpAuthorAdapter.addSourceRecord(tmpAuthors[i]);
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
tmpAuthorAdapter.integrateRecords(
|
|
521
|
+
(pAuthorError) =>
|
|
522
|
+
{
|
|
523
|
+
Expect(pAuthorError).to.not.be.an('Error');
|
|
524
|
+
|
|
525
|
+
// Now create BookAuthorJoin records that reference both
|
|
526
|
+
// Book (via external GUID) and Author (via external GUID)
|
|
527
|
+
let tmpJoinAdapter = libIntegrationAdapter.getAdapter(_Fable, 'BookAuthorJoin', 'BAJ',
|
|
528
|
+
{
|
|
529
|
+
AdapterSetGUIDMarshalPrefix: 'TEST',
|
|
530
|
+
ForceMarshal: true
|
|
531
|
+
});
|
|
532
|
+
tmpJoinAdapter.setRestClient(_Fable.MeadowCloneRestClient);
|
|
533
|
+
|
|
534
|
+
// Link first 3 books to the 3 authors
|
|
535
|
+
for (let i = 0; i < 3; i++)
|
|
536
|
+
{
|
|
537
|
+
tmpJoinAdapter.addSourceRecord(
|
|
538
|
+
{
|
|
539
|
+
GUIDBookAuthorJoin: `RandJoin-${i}`,
|
|
540
|
+
GUIDBook: `RandBook-${i}`,
|
|
541
|
+
GUIDAuthor: `RandAuthor-${i}`
|
|
542
|
+
});
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
tmpJoinAdapter.integrateRecords(
|
|
546
|
+
(pJoinError) =>
|
|
547
|
+
{
|
|
548
|
+
Expect(pJoinError).to.not.be.an('Error');
|
|
549
|
+
|
|
550
|
+
// Verify the join records were created with correct FK IDs
|
|
551
|
+
let tmpGUIDMap = _Fable.MeadowGUIDMap;
|
|
552
|
+
let tmpRestClient = _Fable.MeadowCloneRestClient;
|
|
553
|
+
let tmpRemaining = 3;
|
|
554
|
+
|
|
555
|
+
for (let i = 0; i < 3; i++)
|
|
556
|
+
{
|
|
557
|
+
let tmpJoinGUID = tmpGUIDMap.getMeadowGUIDFromExternalGUID('BookAuthorJoin', `RandJoin-${i}`);
|
|
558
|
+
tmpRestClient.getEntityByGUID('BookAuthorJoin', tmpJoinGUID,
|
|
559
|
+
(pReadError, pJoinBody) =>
|
|
560
|
+
{
|
|
561
|
+
Expect(pReadError).to.not.be.an('Error');
|
|
562
|
+
Expect(pJoinBody).to.be.an('object');
|
|
563
|
+
Expect(pJoinBody.IDBook).to.be.greaterThan(0);
|
|
564
|
+
Expect(pJoinBody.IDAuthor).to.be.greaterThan(0);
|
|
565
|
+
|
|
566
|
+
tmpRemaining--;
|
|
567
|
+
if (tmpRemaining <= 0)
|
|
568
|
+
{
|
|
569
|
+
return fDone();
|
|
570
|
+
}
|
|
571
|
+
});
|
|
572
|
+
}
|
|
573
|
+
});
|
|
574
|
+
});
|
|
575
|
+
}
|
|
576
|
+
);
|
|
577
|
+
}
|
|
578
|
+
);
|
|
579
|
+
}
|
|
580
|
+
);
|