meadow-integration 1.0.5 → 1.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/.dockerignore +11 -0
  2. package/Docker-Build.sh +2 -0
  3. package/Docker-Compose.sh +2 -0
  4. package/Docker-Push.sh +2 -0
  5. package/Docker-Tag.sh +2 -0
  6. package/Dockerfile +28 -0
  7. package/Dockerfile_LUXURYCode +23 -0
  8. package/README.md +139 -25
  9. package/docker-compose.yml +16 -0
  10. package/docs/README.md +65 -18
  11. package/docs/_cover.md +3 -2
  12. package/docs/_sidebar.md +52 -7
  13. package/docs/_topbar.md +2 -0
  14. package/docs/api/clone-rest-client.md +278 -0
  15. package/docs/api/connection-manager.md +179 -0
  16. package/docs/api/guid-map.md +234 -0
  17. package/docs/api/integration-adapter.md +283 -0
  18. package/docs/api/operation.md +241 -0
  19. package/docs/api/sync-entity-initial.md +227 -0
  20. package/docs/api/sync-entity-ongoing.md +244 -0
  21. package/docs/api/sync.md +213 -0
  22. package/docs/api/tabular-check.md +213 -0
  23. package/docs/api/tabular-transform.md +316 -0
  24. package/docs/architecture.md +423 -0
  25. package/docs/cli/comprehensionarray.md +111 -0
  26. package/docs/cli/comprehensionintersect.md +132 -0
  27. package/docs/cli/csvcheck.md +111 -0
  28. package/docs/cli/csvtransform.md +170 -0
  29. package/docs/cli/data-clone.md +277 -0
  30. package/docs/cli/jsonarraytransform.md +166 -0
  31. package/docs/cli/load-comprehension.md +129 -0
  32. package/docs/cli/objectarraytocsv.md +159 -0
  33. package/docs/cli/overview.md +96 -0
  34. package/docs/cli/serve.md +102 -0
  35. package/docs/cli/tsvtransform.md +144 -0
  36. package/docs/data-clone/configuration.md +357 -0
  37. package/docs/data-clone/connection-manager.md +206 -0
  38. package/docs/data-clone/docker.md +290 -0
  39. package/docs/data-clone/overview.md +173 -0
  40. package/docs/data-clone/sync-modes.md +186 -0
  41. package/docs/implementation-reference.md +311 -0
  42. package/docs/overview.md +156 -0
  43. package/docs/quickstart.md +233 -0
  44. package/docs/rest/comprehension-push.md +209 -0
  45. package/docs/rest/comprehension.md +506 -0
  46. package/docs/rest/csv.md +255 -0
  47. package/docs/rest/entity-generation.md +158 -0
  48. package/docs/rest/json-array.md +243 -0
  49. package/docs/rest/overview.md +120 -0
  50. package/docs/rest/status.md +63 -0
  51. package/docs/rest/tsv.md +241 -0
  52. package/docs/retold-catalog.json +93 -3
  53. package/docs/retold-keyword-index.json +23683 -1901
  54. package/package.json +6 -3
  55. package/scripts/run.sh +18 -0
  56. package/source/Meadow-Integration.js +15 -1
  57. package/source/cli/Default-Meadow-Integration-Configuration.json +37 -2
  58. package/source/cli/Meadow-Integration-CLI-Program.js +4 -1
  59. package/source/cli/commands/Meadow-Integration-Command-DataClone.js +284 -0
  60. package/source/services/clone/Meadow-Service-ConnectionManager.js +251 -0
  61. package/source/services/clone/Meadow-Service-Operation.js +196 -0
  62. package/source/services/clone/Meadow-Service-RestClient.js +364 -0
  63. package/source/services/clone/Meadow-Service-Sync-Entity-Initial.js +367 -0
  64. package/source/services/clone/Meadow-Service-Sync-Entity-Ongoing.js +457 -0
  65. package/source/services/clone/Meadow-Service-Sync.js +142 -0
  66. /package/docs/examples/bookstore/{mapping_books_Author.json → mapping_books_author.json} +0 -0
  67. /package/docs/examples/bookstore/{mapping_books_Book.json → mapping_books_book.json} +0 -0
@@ -0,0 +1,196 @@
1
+ class MeadowOperation
2
+ {
3
+ constructor(pFable)
4
+ {
5
+ this.log = pFable.log;
6
+
7
+ this.timeStamps = {};
8
+ this.progressTrackers = {};
9
+ }
10
+
11
+ createTimeStamp(pTimeStampHash)
12
+ {
13
+ const tmpTimeStampHash = (typeof(pTimeStampHash) == 'string') ? pTimeStampHash : 'Default';
14
+ this.timeStamps[tmpTimeStampHash] = +new Date();
15
+ return this.timeStamps[tmpTimeStampHash];
16
+ }
17
+
18
+ getTimeDelta(pTimeStampHash)
19
+ {
20
+ const tmpTimeStampHash = (typeof(pTimeStampHash) == 'string') ? pTimeStampHash : 'Default';
21
+ if (this.timeStamps.hasOwnProperty(tmpTimeStampHash))
22
+ {
23
+ const tmpEndTime = +new Date();
24
+ return tmpEndTime - this.timeStamps[tmpTimeStampHash];
25
+ }
26
+ else
27
+ {
28
+ return -1;
29
+ }
30
+ }
31
+
32
+ logTimeDelta(pTimeStampHash, pMessage)
33
+ {
34
+ const tmpTimeStampHash = (typeof(pTimeStampHash) == 'string') ? pTimeStampHash : 'Default';
35
+ const tmpMessage = (typeof(pMessage) !== 'undefined') ? pMessage : `Elapsed for ${tmpTimeStampHash}: `;
36
+ const tmpOperationTime = this.getTimeDelta(pTimeStampHash);
37
+ this.log.info(tmpMessage + ' (' + tmpOperationTime + 'ms)');
38
+ return tmpOperationTime;
39
+ }
40
+
41
+ createProgressTracker(pTotalOperations, pProgressTrackerHash)
42
+ {
43
+ const tmpProgressTrackerHash = (typeof(pProgressTrackerHash) == 'string') ? pProgressTrackerHash : 'DefaultProgressTracker';
44
+ const tmpTotalOperations = (typeof(pTotalOperations) == 'number') ? pTotalOperations : 100;
45
+
46
+ const tmpProgressTracker = (
47
+ {
48
+ Hash: tmpProgressTrackerHash,
49
+ StartTime: this.createTimeStamp(tmpProgressTrackerHash),
50
+ EndTime: 0,
51
+ CurrentTime: 0,
52
+ PercentComplete: -1,
53
+ AverageOperationTime: -1,
54
+ EstimatedCompletionTime: -1,
55
+ TotalCount: tmpTotalOperations,
56
+ CurrentCount: 0,
57
+ });
58
+
59
+ this.progressTrackers[tmpProgressTrackerHash] = tmpProgressTracker;
60
+
61
+ return tmpProgressTracker;
62
+ }
63
+
64
+ solveProgressTrackerStatus(pProgressTrackerHash)
65
+ {
66
+ const tmpProgressTrackerHash = (typeof(pProgressTrackerHash) == 'string') ? pProgressTrackerHash : 'DefaultProgressTracker';
67
+
68
+ if (!this.progressTrackers.hasOwnProperty(tmpProgressTrackerHash))
69
+ {
70
+ this.createProgressTracker(100, tmpProgressTrackerHash);
71
+ }
72
+
73
+ const tmpProgressTracker = this.progressTrackers[tmpProgressTrackerHash];
74
+
75
+ tmpProgressTracker.CurrentTime = this.getTimeDelta(tmpProgressTracker.Hash);
76
+
77
+ if ((tmpProgressTracker.CurrentCount > 0) && (tmpProgressTracker.TotalCount > 0))
78
+ {
79
+ tmpProgressTracker.PercentComplete = (tmpProgressTracker.CurrentCount / tmpProgressTracker.TotalCount) * 100.0;
80
+ }
81
+
82
+ if ((tmpProgressTracker.CurrentCount > 0) && (tmpProgressTracker.CurrentTime > 0))
83
+ {
84
+ tmpProgressTracker.AverageOperationTime = tmpProgressTracker.CurrentTime / tmpProgressTracker.CurrentCount;
85
+ }
86
+
87
+ if ((tmpProgressTracker.CurrentCount < tmpProgressTracker.TotalCount) && (tmpProgressTracker.AverageOperationTime > 0))
88
+ {
89
+ tmpProgressTracker.EstimatedCompletionTime = (tmpProgressTracker.TotalCount - tmpProgressTracker.CurrentCount) * tmpProgressTracker.AverageOperationTime;
90
+ }
91
+ }
92
+
93
+ updateProgressTrackerStatus(pProgressTrackerHash, pCurrentOperations)
94
+ {
95
+ const tmpProgressTrackerHash = (typeof(pProgressTrackerHash) == 'string') ? pProgressTrackerHash : 'DefaultProgressTracker';
96
+ const tmpCurrentOperations = parseInt(pCurrentOperations);
97
+
98
+ if (isNaN(tmpCurrentOperations))
99
+ {
100
+ return false;
101
+ }
102
+
103
+ if (!this.progressTrackers.hasOwnProperty(tmpProgressTrackerHash))
104
+ {
105
+ this.createProgressTracker(100, tmpProgressTrackerHash);
106
+ }
107
+
108
+ this.progressTrackers[tmpProgressTrackerHash].CurrentCount = tmpCurrentOperations;
109
+ this.progressTrackers[tmpProgressTrackerHash].CurrentTime = this.getTimeDelta(tmpProgressTrackerHash);
110
+
111
+ this.solveProgressTrackerStatus(tmpProgressTrackerHash);
112
+
113
+ return this.progressTrackers[tmpProgressTrackerHash];
114
+ }
115
+
116
+ incrementProgressTrackerStatus(pProgressTrackerHash, pIncrementSize)
117
+ {
118
+ const tmpProgressTrackerHash = (typeof(pProgressTrackerHash) == 'string') ? pProgressTrackerHash : 'DefaultProgressTracker';
119
+ const tmpIncrementSize = parseInt(pIncrementSize);
120
+
121
+ if (isNaN(tmpIncrementSize))
122
+ {
123
+ return false;
124
+ }
125
+
126
+ if (!this.progressTrackers.hasOwnProperty(tmpProgressTrackerHash))
127
+ {
128
+ this.createProgressTracker(100, tmpProgressTrackerHash);
129
+ }
130
+
131
+ this.progressTrackers[tmpProgressTrackerHash].CurrentCount = this.progressTrackers[tmpProgressTrackerHash].CurrentCount + tmpIncrementSize;
132
+ this.progressTrackers[tmpProgressTrackerHash].CurrentTime = this.getTimeDelta(tmpProgressTrackerHash);
133
+
134
+ this.solveProgressTrackerStatus(tmpProgressTrackerHash);
135
+
136
+ return this.progressTrackers[tmpProgressTrackerHash];
137
+ }
138
+
139
+ setProgressTrackerEndTime(pProgressTrackerHash, pCurrentOperations)
140
+ {
141
+ const tmpProgressTrackerHash = (typeof(pProgressTrackerHash) == 'string') ? pProgressTrackerHash : 'DefaultProgressTracker';
142
+ const tmpCurrentOperations = parseInt(pCurrentOperations);
143
+
144
+ if (!this.progressTrackers.hasOwnProperty(tmpProgressTrackerHash))
145
+ {
146
+ return false;
147
+ }
148
+ if (!isNaN(tmpCurrentOperations))
149
+ {
150
+ this.updateProgressTrackerStatus(tmpProgressTrackerHash, tmpCurrentOperations);
151
+ }
152
+
153
+ this.progressTrackers[tmpProgressTrackerHash].EndTime = this.getTimeDelta(tmpProgressTrackerHash);
154
+
155
+ this.solveProgressTrackerStatus(tmpProgressTrackerHash);
156
+
157
+ return this.progressTrackers[tmpProgressTrackerHash];
158
+ }
159
+
160
+ printProgressTrackerStatus(pProgressTrackerHash)
161
+ {
162
+ const tmpProgressTrackerHash = (typeof(pProgressTrackerHash) == 'string') ? pProgressTrackerHash : 'DefaultProgressTracker';
163
+
164
+ if (!this.progressTrackers.hasOwnProperty(tmpProgressTrackerHash))
165
+ {
166
+ this.log.info(`>> Progress Tracker ${tmpProgressTrackerHash} does not exist! No stats to display.`);
167
+ }
168
+ else
169
+ {
170
+ const tmpProgressTracker = this.progressTrackers[tmpProgressTrackerHash];
171
+
172
+ if (tmpProgressTracker.CurrentCount < 1)
173
+ {
174
+ this.log.info(`>> Progress Tracker ${tmpProgressTracker.Hash} has no completed operations. ${tmpProgressTracker.CurrentTime}ms have elapsed since it was started.`);
175
+ }
176
+ else if (tmpProgressTracker.EndTime < 1)
177
+ {
178
+ this.log.info(`>> Progress Tracker ${tmpProgressTracker.Hash} is ${tmpProgressTracker.PercentComplete.toFixed(3)}% completed` +
179
+ ` - ${tmpProgressTracker.CurrentCount} / ${tmpProgressTracker.TotalCount} operations over ${tmpProgressTracker.CurrentTime}ms ` +
180
+ `(median ${tmpProgressTracker.AverageOperationTime.toFixed(3)} per). Estimated completion in ${tmpProgressTracker.EstimatedCompletionTime.toFixed(0)}ms or ${(tmpProgressTracker.EstimatedCompletionTime / 1000 / 60).toFixed(2)} minutes`);
181
+ }
182
+ else
183
+ {
184
+ this.log.info(`>> Progress Tracker ${tmpProgressTracker.Hash} is done and completed ${tmpProgressTracker.CurrentCount} / ${tmpProgressTracker.TotalCount} operations in ${tmpProgressTracker.EndTime}ms.`);
185
+ }
186
+ }
187
+ }
188
+
189
+ logMemoryResourcesUsed()
190
+ {
191
+ const tmpResourcesUsed = process.memoryUsage().heapUsed / 1024 / 1024;
192
+ this.log.info(`Memory usage at ${Math.round(tmpResourcesUsed * 100) / 100} MB`);
193
+ }
194
+ }
195
+
196
+ module.exports = MeadowOperation;
@@ -0,0 +1,364 @@
1
+ const libFableServiceProviderBase = require('fable-serviceproviderbase');
2
+ const Http = require('http');
3
+ const Https = require('https');
4
+
5
+ const defaultRestClientOptions = (
6
+ {
7
+ DownloadBatchSize: 100,
8
+
9
+ ServerURL: 'https://localhost:8080/1.0/',
10
+ UserID: false,
11
+ Password: false,
12
+ });
13
+
14
+ class MeadowCloneRestClient extends libFableServiceProviderBase
15
+ {
16
+ constructor(pFable, pOptions, pServiceHash)
17
+ {
18
+ const tmpOptions = Object.assign({}, defaultRestClientOptions, pOptions);
19
+ super(pFable, tmpOptions, pServiceHash);
20
+
21
+ this.serviceType = 'MeadowCloneRestClient';
22
+
23
+ this.serverURL = this.options.ServerURL;
24
+ this.userID = this.options.UserID;
25
+ this.password = this.options.Password;
26
+
27
+ this._SessionData = false;
28
+ this._SessionToken = false;
29
+ this._LoggedIn = false;
30
+
31
+ if (this.options.SessionToken)
32
+ {
33
+ this._SessionToken = this.options.SessionToken;
34
+ }
35
+
36
+ this.restClient = this.fable.serviceManager.instantiateServiceProvider('RestClient', {}, 'MeadowCloneRestClient-RestClient');
37
+ this.cache = {};
38
+
39
+ const agentOptions = { keepAlive: true };
40
+
41
+ if (this.serverURL && this.serverURL.startsWith('http:'))
42
+ {
43
+ this.agent = new Http.Agent(agentOptions);
44
+ }
45
+ else
46
+ {
47
+ this.agent = new Https.Agent(agentOptions);
48
+ }
49
+
50
+ this.restClient.prepareRequestOptions = (pOptions) =>
51
+ {
52
+ pOptions.agent = this.agent;
53
+ return pOptions;
54
+ };
55
+ }
56
+
57
+ prepareRequestOptions(pOptions)
58
+ {
59
+ return pOptions;
60
+ }
61
+
62
+ _prepareRequestOptions(pOptions)
63
+ {
64
+ if (!this._SessionData && this._SessionToken)
65
+ {
66
+ if ((pOptions.url.indexOf('?') > -1) && (pOptions.url.indexOf('SessionToken=') === -1))
67
+ {
68
+ pOptions.url += `&SessionToken=${this._SessionToken}`;
69
+ }
70
+ else if (pOptions.url.indexOf('SessionToken=') === -1)
71
+ {
72
+ pOptions.url += `?SessionToken=${this._SessionToken}`;
73
+ }
74
+ }
75
+ return this.prepareRequestOptions(pOptions);
76
+ }
77
+
78
+ getJSON(pURL, fCallback)
79
+ {
80
+ let tmpRequestOptions = { url: `${this.serverURL}${pURL}` };
81
+ tmpRequestOptions = this._prepareRequestOptions(tmpRequestOptions);
82
+ return this.restClient.getJSON(tmpRequestOptions, fCallback);
83
+ }
84
+
85
+ createEntity(pEntity, pRecord, fCallback)
86
+ {
87
+ let tmpRequestOptions = (
88
+ {
89
+ url: `${this.serverURL}${pEntity}`,
90
+ body: pRecord,
91
+ });
92
+ tmpRequestOptions = this._prepareRequestOptions(tmpRequestOptions);
93
+
94
+ this.restClient.postJSON(tmpRequestOptions,
95
+ (pError, pResponse, pBody) =>
96
+ {
97
+ if (pError)
98
+ {
99
+ this.log.error(`Error creating ${pEntity} record: ${pError.message}`);
100
+ }
101
+ return fCallback(pError, pBody);
102
+ });
103
+ }
104
+
105
+ updateEntity(pEntity, pRecord, fCallback)
106
+ {
107
+ let tmpRequestOptions = (
108
+ {
109
+ url: `${this.serverURL}${pEntity}`,
110
+ body: pRecord,
111
+ });
112
+ tmpRequestOptions = this._prepareRequestOptions(tmpRequestOptions);
113
+
114
+ this.restClient.putJSON(tmpRequestOptions,
115
+ (pError, pResponse, pBody) =>
116
+ {
117
+ if (pError)
118
+ {
119
+ this.log.error(`Error updating ${pEntity} record: ${pError.message}`);
120
+ }
121
+ return fCallback(pError, pBody);
122
+ });
123
+ }
124
+
125
+ upsertEntity(pEntity, pRecord, fCallback)
126
+ {
127
+ let tmpRequestOptions = (
128
+ {
129
+ url: `${this.serverURL}${pEntity}/Upsert`,
130
+ body: pRecord,
131
+ });
132
+ tmpRequestOptions = this._prepareRequestOptions(tmpRequestOptions);
133
+
134
+ this.restClient.putJSON(tmpRequestOptions,
135
+ (pError, pResponse, pBody) =>
136
+ {
137
+ if (pError)
138
+ {
139
+ this.log.error(`Error upserting ${pEntity} record: ${pError.message}`);
140
+ }
141
+ return fCallback(pError, pBody);
142
+ });
143
+ }
144
+
145
+ deleteEntity(pEntity, pIDRecord, fCallback)
146
+ {
147
+ let tmpRequestOptions = (
148
+ {
149
+ url: `${this.serverURL}${pEntity}/${pIDRecord}`,
150
+ });
151
+ tmpRequestOptions = this._prepareRequestOptions(tmpRequestOptions);
152
+
153
+ this.restClient.delJSON(tmpRequestOptions,
154
+ (pError, pResponse, pBody) =>
155
+ {
156
+ if (pError)
157
+ {
158
+ this.log.error(`Error deleting ${pEntity} record ID ${pIDRecord}: ${pError.message}`);
159
+ }
160
+ return fCallback(pError, pBody);
161
+ });
162
+ }
163
+
164
+ initializeCache(pEntity)
165
+ {
166
+ if (!this.cache.hasOwnProperty(pEntity))
167
+ {
168
+ this.cache[pEntity] = this.fable.serviceManager.instantiateServiceProviderWithoutRegistration('ObjectCache');
169
+ this.cache[pEntity].maxAge = 30000;
170
+ this.cache[pEntity].maxLength = 10000;
171
+ }
172
+ }
173
+
174
+ getEntity(pEntity, pIDRecord, fCallback)
175
+ {
176
+ this.initializeCache(pEntity);
177
+ this.cache[pEntity].prune(
178
+ () =>
179
+ {
180
+ const tmpPossibleRecord = this.cache[pEntity].read(pIDRecord);
181
+
182
+ if (tmpPossibleRecord)
183
+ {
184
+ return fCallback(null, tmpPossibleRecord);
185
+ }
186
+
187
+ let tmpRequestOptions = (
188
+ {
189
+ url: `${this.serverURL}${pEntity}/${pIDRecord}`,
190
+ });
191
+ tmpRequestOptions = this._prepareRequestOptions(tmpRequestOptions);
192
+
193
+ return this.restClient.getJSON(tmpRequestOptions,
194
+ (pError, pResponse, pBody) =>
195
+ {
196
+ if (pBody)
197
+ {
198
+ this.cache[pEntity].put(pBody, pIDRecord);
199
+ }
200
+ return fCallback(pError, pBody);
201
+ });
202
+ });
203
+ }
204
+
205
+ getEntitySet(pEntity, pMeadowFilterExpression, fCallback)
206
+ {
207
+ this.initializeCache(pEntity);
208
+ let tmpCountOptions = (
209
+ {
210
+ url: `${this.serverURL}${pEntity}s/Count/FilteredTo/${pMeadowFilterExpression}`,
211
+ });
212
+ tmpCountOptions = this._prepareRequestOptions(tmpCountOptions);
213
+
214
+ return this.restClient.getJSON(tmpCountOptions,
215
+ (pError, pResponse, pBody) =>
216
+ {
217
+ if (pError)
218
+ {
219
+ this.log.error(`Error getting bulk entity count of [${pEntity}] filtered to [${pMeadowFilterExpression}]: ${pError}`);
220
+ return fCallback(pError);
221
+ }
222
+ let tmpRecordCount = 0;
223
+ if (pBody && pBody.Count)
224
+ {
225
+ tmpRecordCount = pBody.Count;
226
+ }
227
+
228
+ const tmpDownloadURIFragments = [];
229
+ const tmpDownloadBatchSize = this.options.DownloadBatchSize;
230
+ for (let i = 0; i < (tmpRecordCount / tmpDownloadBatchSize); i++)
231
+ {
232
+ tmpDownloadURIFragments.push(`${this.serverURL}${pEntity}s/FilteredTo/${pMeadowFilterExpression}/${i * tmpDownloadBatchSize}/${tmpDownloadBatchSize}`);
233
+ }
234
+
235
+ let tmpEntitySet = [];
236
+ this.fable.Utility.eachLimit(tmpDownloadURIFragments, 1,
237
+ (pURIFragment, fDownloadCallback) =>
238
+ {
239
+ let tmpRecordBatchRequestOptions = (
240
+ {
241
+ url: `${pURIFragment}`,
242
+ });
243
+ tmpRecordBatchRequestOptions = this._prepareRequestOptions(tmpRecordBatchRequestOptions);
244
+
245
+ this.restClient.getJSON(tmpRecordBatchRequestOptions,
246
+ (pDownloadError, pDownloadResponse, pDownloadBody) =>
247
+ {
248
+ if (pDownloadBody)
249
+ {
250
+ tmpEntitySet = tmpEntitySet.concat(pDownloadBody);
251
+ }
252
+ return fDownloadCallback(pDownloadError);
253
+ });
254
+ },
255
+ (pFullDownloadError) =>
256
+ {
257
+ return fCallback(pFullDownloadError, tmpEntitySet);
258
+ });
259
+ });
260
+ }
261
+
262
+ get session()
263
+ {
264
+ return this._SessionData;
265
+ }
266
+
267
+ get loggedIn()
268
+ {
269
+ return this._LoggedIn;
270
+ }
271
+
272
+ setSessionToken(pSessionToken)
273
+ {
274
+ this._SessionToken = pSessionToken;
275
+ }
276
+
277
+ setSessionData(pSessionData)
278
+ {
279
+ this._SessionData = pSessionData;
280
+
281
+ if (!this.restClient.cookie)
282
+ {
283
+ this.restClient.cookie = {};
284
+ }
285
+
286
+ if ((typeof(this._SessionData) == 'object') && this._SessionData.SessionID)
287
+ {
288
+ this.restClient.cookie['UserSession'] = this._SessionData.SessionID;
289
+ }
290
+ }
291
+
292
+ resetSessionData()
293
+ {
294
+ if (typeof(this.restClient.cookie) === 'object')
295
+ {
296
+ this.restClient.cookie = false;
297
+ }
298
+ this._SessionData = false;
299
+ }
300
+
301
+ authenticate(fCallback)
302
+ {
303
+ if (!this.userID || !this.password)
304
+ {
305
+ this.log.info('No credentials configured; skipping authentication.');
306
+ return fCallback();
307
+ }
308
+
309
+ this.restClient.postJSON(
310
+ {
311
+ url: `${this.serverURL}/Authenticate`,
312
+ body:
313
+ {
314
+ UserName: this.userID,
315
+ Password: this.password,
316
+ },
317
+ },
318
+ (pError, pResponse, pBody) =>
319
+ {
320
+ if (pError)
321
+ {
322
+ this.log.error(`Problem authenticating with server [${this.serverURL}] as ${this.userID}:`, pError);
323
+ this.resetSessionData();
324
+ return fCallback(pError, this._SessionData);
325
+ }
326
+
327
+ if (pBody && pBody.hasOwnProperty('Error'))
328
+ {
329
+ this.log.error(`Problem authenticating with server [${this.serverURL}] as ${this.userID}: ${pBody.Error}`);
330
+ return fCallback(new Error(pBody.Error), this._SessionData);
331
+ }
332
+
333
+ this.setSessionData(pBody);
334
+ this._LoggedIn = true;
335
+
336
+ return fCallback(null, this._SessionData);
337
+ });
338
+ }
339
+
340
+ deauthenticate(fCallback)
341
+ {
342
+ this.restClient.getJSON(
343
+ {
344
+ url: `${this.serverURL}/Deauthenticate`,
345
+ },
346
+ (pError) =>
347
+ {
348
+ if (pError)
349
+ {
350
+ this.log.error(`Problem logging out of server [${this.serverURL}]:`, pError);
351
+ }
352
+
353
+ this.log.info('Successfully logged out of the API server.');
354
+ this.resetSessionData();
355
+ this._LoggedIn = false;
356
+
357
+ return fCallback(pError, this._SessionData);
358
+ });
359
+ }
360
+ }
361
+
362
+ module.exports = MeadowCloneRestClient;
363
+
364
+ module.exports.default_configuration = defaultRestClientOptions;