@itentialopensource/adapter-git 0.1.1

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/adapter.js ADDED
@@ -0,0 +1,562 @@
1
+ /* @copyright Itential, LLC 2019 (pre-modifications) */
2
+
3
+ // Set globals
4
+ /* global eventSystem log */
5
+ /* eslint no-underscore-dangle: warn */
6
+ /* eslint no-loop-func: warn */
7
+ /* eslint no-cond-assign: warn */
8
+ /* eslint no-unused-vars: warn */
9
+ /* eslint consistent-return: warn */
10
+ /* eslint import/no-dynamic-require: warn */
11
+ /* eslint global-require: warn */
12
+ /* eslint no-await-in-loop: warn */
13
+
14
+ /* Required libraries. */
15
+ const fs = require('fs-extra');
16
+ const builtInfs = require('fs');
17
+ const path = require('path');
18
+ const util = require('util');
19
+ /* Fetch in the other needed components for the this Adaptor */
20
+ const EventEmitterCl = require('events').EventEmitter;
21
+ const request = require('request');
22
+ const AjvCl = require('ajv');
23
+ const git = require('isomorphic-git');
24
+ const http = require('isomorphic-git/http/node');
25
+
26
+ let myid = null;
27
+ let errors = [];
28
+
29
+ /**
30
+ * @summary Build a standard error object from the data provided
31
+ *
32
+ * @function formatErrorObject
33
+ * @param {String} origin - the originator of the error (optional).
34
+ * @param {String} type - the internal error type (optional).
35
+ * @param {String} variables - the variables to put into the error message (optional).
36
+ * @param {Integer} sysCode - the error code from the other system (optional).
37
+ * @param {Object} sysRes - the raw response from the other system (optional).
38
+ * @param {Exception} stack - any available stack trace from the issue (optional).
39
+ *
40
+ * @return {Object} - the error object, null if missing pertinent information
41
+ */
42
+ function formatErrorObject(origin, type, variables, sysCode, sysRes, stack) {
43
+ log.trace(`${myid}-adapter-formatErrorObject`);
44
+
45
+ // add the required fields
46
+ const errorObject = {
47
+ icode: 'AD.999',
48
+ IAPerror: {
49
+ origin: `${myid}-unidentified`,
50
+ displayString: 'error not provided',
51
+ recommendation: 'report this issue to the adapter team!'
52
+ }
53
+ };
54
+
55
+ if (origin) {
56
+ errorObject.IAPerror.origin = origin;
57
+ }
58
+ if (type) {
59
+ errorObject.IAPerror.displayString = type;
60
+ }
61
+
62
+ // add the messages from the error.json
63
+ for (let e = 0; e < errors.length; e += 1) {
64
+ if (errors[e].key === type) {
65
+ errorObject.icode = errors[e].icode;
66
+ errorObject.IAPerror.displayString = errors[e].displayString;
67
+ errorObject.IAPerror.recommendation = errors[e].recommendation;
68
+ } else if (errors[e].icode === type) {
69
+ errorObject.icode = errors[e].icode;
70
+ errorObject.IAPerror.displayString = errors[e].displayString;
71
+ errorObject.IAPerror.recommendation = errors[e].recommendation;
72
+ }
73
+ }
74
+
75
+ // replace the variables
76
+ let varCnt = 0;
77
+ while (errorObject.IAPerror.displayString.indexOf('$VARIABLE$') >= 0) {
78
+ let curVar = '';
79
+
80
+ // get the current variable
81
+ if (variables && Array.isArray(variables) && variables.length >= varCnt + 1) {
82
+ curVar = variables[varCnt];
83
+ }
84
+ varCnt += 1;
85
+ errorObject.IAPerror.displayString = errorObject.IAPerror.displayString.replace('$VARIABLE$', curVar);
86
+ }
87
+
88
+ // add all of the optional fields
89
+ if (sysCode) {
90
+ errorObject.IAPerror.code = sysCode;
91
+ }
92
+ if (sysRes) {
93
+ errorObject.IAPerror.raw_response = sysRes;
94
+ }
95
+ if (stack) {
96
+ errorObject.IAPerror.stack = stack;
97
+ }
98
+
99
+ // return the object
100
+ return errorObject;
101
+ }
102
+
103
+ /**
104
+ * This is the adapter/interface into Git
105
+ */
106
+ class Git extends EventEmitterCl {
107
+ /**
108
+ * Git Adapter
109
+ * @constructor
110
+ */
111
+ constructor(prongid, properties) {
112
+ super();
113
+ this.props = properties;
114
+ this.alive = false;
115
+ this.id = prongid;
116
+ myid = prongid;
117
+ this.username = null;
118
+ this.password = null;
119
+
120
+ // get the path for the specific error file
121
+ const errorFile = path.join(__dirname, '/error.json');
122
+
123
+ // if the file does not exist - error
124
+ if (!fs.existsSync(errorFile)) {
125
+ const origin = `${this.id}-adapter-constructor`;
126
+ log.warn(`${origin}: Could not locate ${errorFile} - errors will be missing details`);
127
+ }
128
+
129
+ // Read the action from the file system
130
+ const errorData = JSON.parse(fs.readFileSync(errorFile, 'utf-8'));
131
+ ({ errors } = errorData);
132
+ }
133
+
134
+ /**
135
+ * Connect function is used during Pronghorn startup to provide instantiation feedback.
136
+ *
137
+ * @function connect
138
+ */
139
+ connect() {
140
+ const meth = 'adapter-connect';
141
+ const origin = `${this.id}-${meth}`;
142
+ log.trace(origin);
143
+
144
+ if (this.props.authentication.username) {
145
+ this.username = this.props.authentication.username;
146
+ }
147
+ if (this.props.authentication.password) {
148
+ this.password = this.props.authentication.password;
149
+ }
150
+ this.onAuth = () => ({
151
+ username: this.username,
152
+ password: this.password
153
+ });
154
+ this.emit('ONLINE', {
155
+ id: this.id
156
+ });
157
+ }
158
+
159
+ /**
160
+ * @summary HealthCheck function is used to provide Pronghorn the status of this adapter.
161
+ *
162
+ * @function healthCheck
163
+ * @param callback - a callback function to return the result id and status
164
+ */
165
+ healthCheck(callback) {
166
+ // need to work on this later
167
+ const retstatus = { id: this.id, status: 'success' };
168
+ return callback(retstatus);
169
+ }
170
+
171
+ /**
172
+ * getAllFunctions is used to get all of the exposed function in the adapter
173
+ *
174
+ * @function getAllFunctions
175
+ */
176
+ getAllFunctions() {
177
+ let myfunctions = [];
178
+ let obj = this;
179
+
180
+ // find the functions in this class
181
+ do {
182
+ const l = Object.getOwnPropertyNames(obj)
183
+ .concat(Object.getOwnPropertySymbols(obj).map((s) => s.toString()))
184
+ .sort()
185
+ .filter((p, i, arr) => typeof obj[p] === 'function' && p !== 'constructor' && (i === 0 || p !== arr[i - 1]) && myfunctions.indexOf(p) === -1);
186
+ myfunctions = myfunctions.concat(l);
187
+ }
188
+ while (
189
+ (obj = Object.getPrototypeOf(obj)) && Object.getPrototypeOf(obj)
190
+ );
191
+
192
+ return myfunctions;
193
+ }
194
+
195
+ /**
196
+ * getWorkflowFunctions is used to get all of the workflow function in the adapter
197
+ *
198
+ * @function getWorkflowFunctions
199
+ */
200
+ getWorkflowFunctions() {
201
+ const myfunctions = this.getAllFunctions();
202
+ const wffunctions = [];
203
+
204
+ // remove the functions that should not be in a Workflow
205
+ for (let m = 0; m < myfunctions.length; m += 1) {
206
+ if (myfunctions[m] === 'addListener') {
207
+ // got to the second tier (adapterBase)
208
+ break;
209
+ }
210
+ if (myfunctions[m] !== 'connect' && myfunctions[m] !== 'healthCheck'
211
+ && myfunctions[m] !== 'getAllFunctions' && myfunctions[m] !== 'getWorkflowFunctions'
212
+ && myfunctions[m] !== 'refreshProperties') {
213
+ wffunctions.push(myfunctions[m]);
214
+ }
215
+ }
216
+
217
+ return wffunctions;
218
+ }
219
+
220
+ cloneRepo(options, callback) {
221
+ const meth = 'adapter-cloneRepo';
222
+ const origin = `${this.id}-${meth}`;
223
+ log.trace(origin);
224
+ try {
225
+ // verify the required data has been provided
226
+ if (options === undefined || options === null || options === '') {
227
+ const errorObj = formatErrorObject(origin, 'Missing Data', ['options'], null, null, null);
228
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
229
+ return callback(null, errorObj);
230
+ }
231
+
232
+ const copiedOptions = { ...options };
233
+ copiedOptions.fs = fs;
234
+ copiedOptions.http = http;
235
+ copiedOptions.onAuth = this.onAuth;
236
+ git.clone(copiedOptions)
237
+ .then(() => callback({
238
+ status: 'success',
239
+ code: 200
240
+ }))
241
+ .catch((err) => {
242
+ const errorObj = formatErrorObject(origin, 'Cloning repo failed', null, null, null, err);
243
+ log.error('error:', err);
244
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
245
+ return callback(null, errorObj);
246
+ });
247
+ } catch (ex) {
248
+ const errorObj = formatErrorObject(origin, 'Caught Exception', null, null, null, ex);
249
+ log.error(ex);
250
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
251
+ return callback(null, errorObj);
252
+ }
253
+ }
254
+
255
+ removeFile(options, callback) {
256
+ const meth = 'adapter-removeFile';
257
+ const origin = `${this.id}-${meth}`;
258
+ log.trace(origin);
259
+ try {
260
+ // verify the required data has been provided
261
+ if (options === undefined || options === null || options === '') {
262
+ const errorObj = formatErrorObject(origin, 'Missing Data', ['options'], null, null, null);
263
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
264
+ return callback(null, errorObj);
265
+ }
266
+
267
+ const copiedOptions = { ...options };
268
+ copiedOptions.fs = fs;
269
+ git.remove(copiedOptions)
270
+ .then(() => callback({
271
+ status: 'success',
272
+ code: 200
273
+ }))
274
+ .catch((err) => {
275
+ const errorObj = formatErrorObject(origin, 'Removing file failed', null, null, null, err);
276
+ log.error('error:', err);
277
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
278
+ return callback(null, errorObj);
279
+ });
280
+ } catch (ex) {
281
+ const errorObj = formatErrorObject(origin, 'Caught Exception', null, null, null, ex);
282
+ log.error(ex);
283
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
284
+ return callback(null, errorObj);
285
+ }
286
+ }
287
+
288
+ addFile(options, callback) {
289
+ const meth = 'adapter-addFile';
290
+ const origin = `${this.id}-${meth}`;
291
+ log.trace(origin);
292
+ try {
293
+ // verify the required data has been provided
294
+ if (options === undefined || options === null || options === '') {
295
+ const errorObj = formatErrorObject(origin, 'Missing Data', ['options'], null, null, null);
296
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
297
+ return callback(null, errorObj);
298
+ }
299
+
300
+ const copiedOptions = { ...options };
301
+ copiedOptions.fs = fs;
302
+ git.add(copiedOptions)
303
+ .then(() => callback({
304
+ status: 'success',
305
+ code: 200
306
+ }))
307
+ .catch((err) => {
308
+ const errorObj = formatErrorObject(origin, 'Adding file failed', null, null, null, err);
309
+ log.error('error:', err);
310
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
311
+ return callback(null, errorObj);
312
+ });
313
+ } catch (ex) {
314
+ const errorObj = formatErrorObject(origin, 'Caught Exception', null, null, null, ex);
315
+ log.error(ex);
316
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
317
+ return callback(null, errorObj);
318
+ }
319
+ }
320
+
321
+ getStatus(options, callback) {
322
+ const meth = 'adapter-getStatus';
323
+ const origin = `${this.id}-${meth}`;
324
+ log.trace(origin);
325
+ try {
326
+ // verify the required data has been provided
327
+ if (options === undefined || options === null || options === '') {
328
+ const errorObj = formatErrorObject(origin, 'Missing Data', ['options'], null, null, null);
329
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
330
+ return callback(null, errorObj);
331
+ }
332
+
333
+ const copiedOptions = { ...options };
334
+ copiedOptions.fs = fs;
335
+ git.status(copiedOptions)
336
+ .then((stat) => callback({
337
+ status: 'success',
338
+ code: 200,
339
+ result: stat
340
+ }))
341
+ .catch((err) => {
342
+ const errorObj = formatErrorObject(origin, 'Getting status failed', null, null, null, err);
343
+ log.error('error:', err);
344
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
345
+ return callback(null, errorObj);
346
+ });
347
+ } catch (ex) {
348
+ const errorObj = formatErrorObject(origin, 'Caught Exception', null, null, null, ex);
349
+ log.error(ex);
350
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
351
+ return callback(null, errorObj);
352
+ }
353
+ }
354
+
355
+ getStatusMatrix(options, callback) {
356
+ const meth = 'adapter-getStatusMatrix';
357
+ const origin = `${this.id}-${meth}`;
358
+ log.trace(origin);
359
+ try {
360
+ // verify the required data has been provided
361
+ if (options === undefined || options === null || options === '') {
362
+ const errorObj = formatErrorObject(origin, 'Missing Data', ['options'], null, null, null);
363
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
364
+ return callback(null, errorObj);
365
+ }
366
+
367
+ const copiedOptions = { ...options };
368
+ copiedOptions.fs = fs;
369
+ git.statusMatrix(copiedOptions)
370
+ .then((stat) => callback({
371
+ status: 'success',
372
+ code: 200,
373
+ result: stat
374
+ }))
375
+ .catch((err) => {
376
+ const errorObj = formatErrorObject(origin, 'Getting status matrix failed', null, null, null, err);
377
+ log.error('error:', err);
378
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
379
+ return callback(null, errorObj);
380
+ });
381
+ } catch (ex) {
382
+ const errorObj = formatErrorObject(origin, 'Caught Exception', null, null, null, ex);
383
+ log.error(ex);
384
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
385
+ return callback(null, errorObj);
386
+ }
387
+ }
388
+
389
+ createCommit(options, callback) {
390
+ const meth = 'adapter-createCommit';
391
+ const origin = `${this.id}-${meth}`;
392
+ log.trace(origin);
393
+ try {
394
+ // verify the required data has been provided
395
+ if (options === undefined || options === null || options === '') {
396
+ const errorObj = formatErrorObject(origin, 'Missing Data', ['options'], null, null, null);
397
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
398
+ return callback(null, errorObj);
399
+ }
400
+
401
+ const copiedOptions = { ...options };
402
+ copiedOptions.fs = fs;
403
+ git.commit(copiedOptions)
404
+ .then((res) => callback({
405
+ status: 'success',
406
+ code: 200,
407
+ result: res
408
+ }))
409
+ .catch((err) => {
410
+ const errorObj = formatErrorObject(origin, 'Creating commit failed', null, null, null, err);
411
+ log.error('error:', err);
412
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
413
+ return callback(null, errorObj);
414
+ });
415
+ } catch (ex) {
416
+ const errorObj = formatErrorObject(origin, 'Caught Exception', null, null, null, ex);
417
+ log.error(ex);
418
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
419
+ return callback(null, errorObj);
420
+ }
421
+ }
422
+
423
+ pushChanges(options, callback) {
424
+ const meth = 'adapter-pushChanges';
425
+ const origin = `${this.id}-${meth}`;
426
+ log.trace(origin);
427
+ try {
428
+ // verify the required data has been provided
429
+ if (options === undefined || options === null || options === '') {
430
+ const errorObj = formatErrorObject(origin, 'Missing Data', ['options'], null, null, null);
431
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
432
+ return callback(null, errorObj);
433
+ }
434
+
435
+ const copiedOptions = { ...options };
436
+ copiedOptions.fs = fs;
437
+ copiedOptions.http = http;
438
+ copiedOptions.onAuth = this.onAuth;
439
+ git.push(copiedOptions)
440
+ .then((res) => callback({
441
+ status: 'success',
442
+ code: 200,
443
+ result: res
444
+ }))
445
+ .catch((err) => {
446
+ const errorObj = formatErrorObject(origin, 'Pushing changes failed', null, null, null, err);
447
+ log.error('error:', err);
448
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
449
+ return callback(null, errorObj);
450
+ });
451
+ } catch (ex) {
452
+ const errorObj = formatErrorObject(origin, 'Caught Exception', null, null, null, ex);
453
+ log.error(ex);
454
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
455
+ return callback(null, errorObj);
456
+ }
457
+ }
458
+
459
+ removeAndPushFile(cloneOptions, removeFileOptions, commitOptions, pushOptions, callback) {
460
+ const meth = 'adapter-removeAndPushFile';
461
+ const origin = `${this.id}-${meth}`;
462
+ log.trace(origin);
463
+
464
+ try {
465
+ // verify the required data has been provided
466
+ if (cloneOptions === undefined || cloneOptions === null || cloneOptions === '') {
467
+ const errorObj = formatErrorObject(origin, 'Missing Data', ['clone options'], null, null, null);
468
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
469
+ return callback(null, errorObj);
470
+ }
471
+ if (removeFileOptions === undefined || removeFileOptions === null || removeFileOptions === '') {
472
+ const errorObj = formatErrorObject(origin, 'Missing Data', ['remove file options'], null, null, null);
473
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
474
+ return callback(null, errorObj);
475
+ }
476
+ if (commitOptions === undefined || commitOptions === null || commitOptions === '') {
477
+ const errorObj = formatErrorObject(origin, 'Missing Data', ['commit options'], null, null, null);
478
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
479
+ return callback(null, errorObj);
480
+ }
481
+ if (pushOptions === undefined || pushOptions === null || pushOptions === '') {
482
+ const errorObj = formatErrorObject(origin, 'Missing Data', ['push options'], null, null, null);
483
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
484
+ return callback(null, errorObj);
485
+ }
486
+
487
+ const copiedCloneOptions = { ...cloneOptions };
488
+ copiedCloneOptions.fs = fs;
489
+ copiedCloneOptions.http = http;
490
+ copiedCloneOptions.onAuth = this.onAuth;
491
+
492
+ const copiedRemoveFileOptions = { ...removeFileOptions };
493
+ copiedRemoveFileOptions.fs = fs;
494
+
495
+ const copiedCommitOptions = { ...commitOptions };
496
+ copiedCommitOptions.fs = fs;
497
+
498
+ const copiedPushOptions = { ...pushOptions };
499
+ copiedPushOptions.fs = fs;
500
+ copiedPushOptions.http = http;
501
+ copiedPushOptions.onAuth = this.onAuth;
502
+
503
+ const successfulResult = {
504
+ status: 'success',
505
+ code: 200,
506
+ results: []
507
+ };
508
+
509
+ git.clone(copiedCloneOptions)
510
+ .then(() => {
511
+ successfulResult.results.push({
512
+ task: 'clone',
513
+ result: 'success'
514
+ });
515
+ if (Array.isArray(removeFileOptions.filepath)) {
516
+ return Promise.all(
517
+ removeFileOptions.filepath.map((file) => {
518
+ copiedRemoveFileOptions.filepath = file;
519
+ return git.remove(copiedRemoveFileOptions);
520
+ })
521
+ );
522
+ }
523
+ return git.remove(copiedRemoveFileOptions);
524
+ })
525
+ .then(() => {
526
+ successfulResult.results.push({
527
+ task: 'remove a file',
528
+ result: 'success'
529
+ });
530
+ return git.commit(copiedCommitOptions);
531
+ })
532
+ .then((res) => {
533
+ successfulResult.results.push({
534
+ task: 'commit',
535
+ result: res
536
+ });
537
+ return git.push(copiedPushOptions);
538
+ })
539
+ .then((res) => {
540
+ successfulResult.results.push({
541
+ task: 'push',
542
+ result: res
543
+ });
544
+ // delete the repo after pushing changes successfully
545
+ builtInfs.rmdirSync(copiedCloneOptions.dir, { recursive: true });
546
+ return callback(successfulResult);
547
+ })
548
+ .catch((err) => {
549
+ const errorObj = formatErrorObject(origin, 'Removing And Pushing file failed', null, null, null, err);
550
+ log.error('error:', err);
551
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
552
+ return callback(null, errorObj);
553
+ });
554
+ } catch (ex) {
555
+ const errorObj = formatErrorObject(origin, 'Missing Data', ['push options'], null, null, null);
556
+ log.error(`${origin}: ${errorObj.IAPerror.displayString}`);
557
+ return callback(null, errorObj);
558
+ }
559
+ }
560
+ }
561
+
562
+ module.exports = Git;