UNPKG

77.4 kBJavaScriptView Raw
1(function (global, factory) {
2 typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('chalk'), require('os'), require('fs'), require('child_process'), require('perf_hooks'), require('request-promise'), require('path'), require('moment')) :
3 typeof define === 'function' && define.amd ? define(['exports', 'chalk', 'os', 'fs', 'child_process', 'perf_hooks', 'request-promise', 'path', 'moment'], factory) :
4 (global = global || self, factory(global.RallyTools = {}, global.chalk$1, global.os, global.fs, global.child_process, global.perf_hooks, global.rp, global.path, global.moment));
5}(this, (function (exports, chalk$1, os, fs, child_process, perf_hooks, rp, path, moment) { 'use strict';
6
7 chalk$1 = chalk$1 && Object.prototype.hasOwnProperty.call(chalk$1, 'default') ? chalk$1['default'] : chalk$1;
8 var fs__default = 'default' in fs ? fs['default'] : fs;
9 rp = rp && Object.prototype.hasOwnProperty.call(rp, 'default') ? rp['default'] : rp;
10 var path__default = 'default' in path ? path['default'] : path;
11 moment = moment && Object.prototype.hasOwnProperty.call(moment, 'default') ? moment['default'] : moment;
12
13 exports.configFile = null;
14
15 if (os.homedir) {
16 exports.configFile = os.homedir() + "/.rallyconfig";
17 }
18
19
20 function loadConfig(file) {
21 if (file) exports.configFile = file;
22 if (!exports.configFile) return;
23 exports.configObject = {
24 hasConfig: true
25 };
26
27 try {
28 let json = fs.readFileSync(exports.configFile);
29 exports.configObject = JSON.parse(json);
30 exports.configObject.hasConfig = true;
31 } catch (e) {
32 if (e.code == "ENOENT") {
33 exports.configObject.hasConfig = false; //ok, they should probably make a config
34 } else {
35 throw e;
36 }
37 }
38 }
39 function loadConfigFromArgs(args) {
40 let tempConfig = {
41 hasConfig: true,
42 ...args.config
43 };
44 exports.configObject = tempConfig;
45 }
46 function setConfig(obj) {
47 exports.configObject = obj;
48 }
49
50 //these are the help entries for each command
51 //function retuns obj.a.b.c
52
53 function deepAccess(obj, path) {
54 let o = obj;
55
56 for (let key of path) {
57 if (!o) return [];
58 o = o[key];
59 }
60
61 return o;
62 } //This takes a class as the first argument, then adds a getter/setter pair that
63 //corresponds to an object in this.data
64
65
66 function defineAssoc(classname, shortname, path) {
67 path = path.split(".");
68 let lastKey = path.pop();
69 Object.defineProperty(classname.prototype, shortname, {
70 get() {
71 return deepAccess(this, path)[lastKey];
72 },
73
74 set(val) {
75 deepAccess(this, path)[lastKey] = val;
76 }
77
78 });
79 }
80
81 function spawn(options, ...args) {
82 if (typeof options !== "object") {
83 args.unshift(options);
84 options = {};
85 } //todo options
86
87
88 return new Promise((resolve, reject) => {
89 let start = perf_hooks.performance.now();
90 let stdout = "";
91 let stderr = "";
92 let cp = child_process.spawn(...args);
93 let write = global.write;
94
95 if (options.noecho) {
96 write = () => {};
97 }
98
99 if (cp.stdout) cp.stdout.on("data", chunk => {
100 stdout += chunk;
101 write(chunk);
102 });
103 if (cp.stderr) cp.stderr.on("data", chunk => {
104 stderr += chunk;
105 write(chunk);
106 });
107 cp.on("error", reject);
108 cp.on("close", code => {
109 let end = perf_hooks.performance.now();
110 let time = end - start;
111 let timestr = time > 1000 ? (time / 100 | 0) / 10 + "s" : (time | 0) + "ms";
112 resolve({
113 stdout,
114 stderr,
115 exitCode: code,
116 time,
117 timestr
118 });
119 });
120 });
121 }
122
123 global.chalk = chalk$1;
124
125 global.log = (...text) => console.log(...text);
126
127 global.write = (...text) => process.stdout.write(...text);
128
129 global.elog = (...text) => console.log(...text);
130
131 global.ewrite = (...text) => process.stderr.write(...text);
132
133 global.errorLog = (...text) => log(...text.map(chalk$1.red));
134
135 class lib {
136 //This function takes 2 required arguemnts:
137 // env: the enviornment you wish to use
138 // and either:
139 // 'path', the short path to the resource. ex '/presets/'
140 // 'path_full', the full path to the resource like 'https://discovery-dev.sdvi.com/presets'
141 //
142 // If the method is anything but GET, either payload or body should be set.
143 // payload should be a javascript object to be turned into json as the request body
144 // body should be a string that is passed as the body. for example: the python code of a preset.
145 //
146 // qs are the querystring parameters, in a key: value object.
147 // {filter: "name=test name"} becomes something like 'filter=name=test+name'
148 //
149 // headers are the headers of the request. "Content-Type" is already set if
150 // payload is given as a parameter
151 //
152 // fullResponse should be true if you want to receive the request object,
153 // not just the returned data.
154 static async makeAPIRequest({
155 env,
156 path,
157 path_full,
158 fullPath,
159 payload,
160 body,
161 method = "GET",
162 qs,
163 headers = {},
164 fullResponse = false,
165 timeout = exports.configObject.timeout || 20000
166 }) {
167 var _configObject$api;
168
169 //backwards compatability from ruby script
170 if (fullPath) path_full = fullPath; //Keys are defined in enviornment variables
171
172 let config = exports.configObject === null || exports.configObject === void 0 ? void 0 : (_configObject$api = exports.configObject.api) === null || _configObject$api === void 0 ? void 0 : _configObject$api[env];
173
174 if (!config) {
175 throw new UnconfiguredEnvError(env);
176 }
177
178 if (method !== "GET" && !exports.configObject.dangerModify) {
179 if (env === "UAT" && exports.configObject.restrictUAT || env === "PROD") {
180 throw new ProtectedEnvError(env);
181 }
182 }
183
184 let rally_api_key = config.key;
185 let rally_api = config.url;
186
187 if (path && path.startsWith("/v1.0/")) {
188 rally_api = rally_api.replace("/api/v2", "/api");
189 }
190
191 path = path_full || rally_api + path;
192
193 if (payload) {
194 body = JSON.stringify(payload, null, 4);
195 }
196
197 if (payload) {
198 headers["Content-Type"] = "application/vnd.api+json";
199 }
200
201 let fullHeaders = {
202 //SDVI ignores this header sometimes.
203 Accept: "application/vnd.api+json",
204 "X-SDVI-Client-Application": "Discovery-rtlib-" + (exports.configObject.appName || "commandline"),
205 ...headers
206 };
207
208 if (exports.configObject.vvverbose) {
209 log(`${method} @ ${path}`);
210 log(JSON.stringify(fullHeaders, null, 4));
211
212 if (body) {
213 log(body);
214 } else {
215 log("(No body");
216 }
217 }
218
219 let requestOptions = {
220 method,
221 body,
222 qs,
223 uri: path,
224 timeout,
225 auth: {
226 bearer: rally_api_key
227 },
228 headers: fullHeaders,
229 simple: false,
230 resolveWithFullResponse: true
231 };
232 let response;
233
234 try {
235 response = await rp(requestOptions);
236
237 if (exports.configObject.vverbose || exports.configObject.vvverbose) {
238 log(chalk$1`${method} @ ${response.request.uri.href}`);
239 }
240 } catch (e) {
241 if ((e === null || e === void 0 ? void 0 : e.cause.code) === "ESOCKETTIMEDOUT") {
242 throw new APIError(response || {}, requestOptions, body);
243 } else {
244 throw e;
245 }
246 } //Throw an error for any 5xx or 4xx
247
248
249 if (!fullResponse && ![200, 201, 202, 203, 204].includes(response.statusCode)) {
250 throw new APIError(response, requestOptions, body);
251 }
252
253 let contentType = response.headers["content-type"];
254 let isJSONResponse = contentType === "application/vnd.api+json" || contentType === "application/json";
255
256 if (exports.configObject.vvverbose) {
257 log(response.body);
258 }
259
260 if (fullResponse) {
261 return response;
262 } else if (isJSONResponse) {
263 var _response, _response$body;
264
265 if ([200, 201, 202, 203, 204].includes(response.statusCode) && !((_response = response) === null || _response === void 0 ? void 0 : (_response$body = _response.body) === null || _response$body === void 0 ? void 0 : _response$body.trim())) return {};
266
267 try {
268 return JSON.parse(response.body);
269 } catch (e) {
270 log(response.body);
271 throw new AbortError("Body is not valid json: ");
272 }
273 } else {
274 return response.body;
275 }
276 } //Index a json endpoint that returns a {links} field.
277 //This function returns the merged data objects as an array
278 //
279 //Additonal options (besides makeAPIRequest options):
280 // - Observe: function to be called for each set of data from the api
281
282
283 static async indexPath(env, path) {
284 let all = [];
285 let opts = typeof env === "string" ? {
286 env,
287 path
288 } : env;
289 let json = await this.makeAPIRequest(opts);
290 let [numPages, pageSize] = this.numPages(json.links.last); //log(`num pages: ${numPages} * ${pageSize}`);
291
292 all = [...json.data];
293
294 while (json.links.next) {
295 json = await this.makeAPIRequest({ ...opts,
296 path_full: json.links.next
297 });
298 if (opts.observe) await opts.observe(json.data);
299 all = [...all, ...json.data];
300 }
301
302 return all;
303 } //Returns number of pages and pagination size
304
305
306 static numPages(str) {
307 return /page=(\d+)p(\d+)/.exec(str).slice(1);
308 }
309
310 static arrayChunk(array, chunkSize) {
311 let newArr = [];
312
313 for (let i = 0; i < array.length; i += chunkSize) {
314 newArr.push(array.slice(i, i + chunkSize));
315 }
316
317 return newArr;
318 }
319
320 static async doPromises(promises, result = [], cb) {
321 for (let promise of promises) {
322 let res = await promise;
323 result.push(res);
324
325 if (cb) {
326 cb(res.data);
327 }
328 }
329
330 return result;
331 }
332
333 static clearProgress(size = 30) {
334 if (!exports.configObject.globalProgress) return;
335 process.stderr.write(`\r${" ".repeat(size + 15)}\r`);
336 }
337
338 static async drawProgress(i, max, size = process.stdout.columns - 15 || 15) {
339 if (!exports.configObject.globalProgress) return;
340 if (size > 45) size = 45;
341 let pct = Number(i) / Number(max); //clamp between 0 and 1
342
343 pct = pct < 0 ? 0 : pct > 1 ? 1 : pct;
344 let numFilled = Math.floor(pct * size);
345 let numEmpty = size - numFilled;
346 this.clearProgress(size);
347 process.stderr.write(`[${"*".repeat(numFilled)}${" ".repeat(numEmpty)}] ${i} / ${max}`);
348 }
349
350 static async keepalive(func, inputData, {
351 chunksize = 20,
352 observe = async _ => _,
353 progress = exports.configObject.globalProgress
354 } = {}) {
355 let total = inputData ? inputData.length : func.length;
356 let i = 0;
357
358 let createPromise = () => {
359 let ret;
360 if (i >= total) return [];
361
362 if (inputData) {
363 ret = [i, func(inputData[i])];
364 } else {
365 ret = [i, func[i]()];
366 }
367
368 i++;
369 return ret;
370 };
371
372 let values = [];
373 let finished = 0;
374 if (progress) process.stderr.write("\n");
375 let threads = [...this.range(chunksize)].map(async whichThread => {
376 while (true) {
377 let [i, currentPromise] = createPromise();
378 if (i == undefined) break;
379 values[i] = await observe((await currentPromise));
380 if (progress) this.drawProgress(++finished, total);
381 }
382 });
383 await Promise.all(threads);
384 if (progress) process.stderr.write("\n");
385 return values;
386 }
387
388 static *range(start, end) {
389 if (end === undefined) {
390 end = start;
391 start = 0;
392 }
393
394 while (start < end) yield start++;
395 } //Index a json endpoint that returns a {links} field.
396 //
397 //This function is faster than indexPath because it can guess the pages it
398 //needs to retreive so that it can request all assets at once.
399 //
400 //This function assumes that the content from the inital request is the
401 //first page, so starting on another page may cause issues. Consider
402 //indexPath for that.
403 //
404 //Additional opts, besides default indexPath opts:
405 // - chunksize[10]: How often to break apart concurrent requests
406
407
408 static async indexPathFast(env, path) {
409 let opts = typeof env === "string" ? {
410 env,
411 path
412 } : env; //Create a copy of the options in case we need to have a special first request
413
414 let start = opts.start || 1;
415 let initOpts = { ...opts
416 };
417
418 if (opts.pageSize) {
419 initOpts.qs = { ...opts.qs
420 };
421 initOpts.qs.page = `${start}p${opts.pageSize}`;
422 }
423
424 let json = await this.makeAPIRequest(initOpts);
425 if (opts.observe && opts.start !== 1) json = await opts.observe(json);
426 let baselink = json.links.first;
427
428 const linkToPage = page => baselink.replace(`page=1p`, `page=${page}p`);
429
430 let [numPages, pageSize] = this.numPages(json.links.last); //Construct an array of all the requests that are done simultanously.
431 //Assume that the content from the inital request is the first page.
432
433 let allResults = await this.keepalive(this.makeAPIRequest, [...this.range(start + 1, Number(numPages) + 1 || opts.limit + 1)].map(i => ({ ...opts,
434 path_full: linkToPage(i)
435 })), {
436 chunksize: opts.chunksize,
437 observe: opts.observe
438 });
439
440 if (start == 1) {
441 allResults.unshift(json);
442 }
443
444 this.clearProgress();
445 let all = [];
446
447 for (let result of allResults) {
448 for (let item of result.data) {
449 all.push(item);
450 }
451 }
452
453 return all;
454 }
455
456 static isLocalEnv(env) {
457 return !env || env === "LOCAL" || env === "LOC";
458 }
459
460 static envName(env) {
461 if (this.isLocalEnv(env)) return "LOCAL";
462 return env;
463 }
464
465 }
466 class AbortError extends Error {
467 constructor(message) {
468 super(message);
469 Error.captureStackTrace(this, this.constructor);
470 this.name = "AbortError";
471 }
472
473 }
474 class APIError extends Error {
475 constructor(response, opts, body) {
476 super(chalk$1`
477{reset Request returned} {yellow ${response === null || response === void 0 ? void 0 : response.statusCode}}{
478{green ${JSON.stringify(opts, null, 4)}}
479{green ${body}}
480{reset ${response.body}}
481===============================
482{red ${response.body ? "Request timed out" : "Bad response from API"}}
483===============================
484 `);
485 this.response = response;
486 this.opts = opts;
487 this.body = body;
488 Error.captureStackTrace(this, this.constructor);
489 this.name = "ApiError";
490 }
491
492 }
493 class UnconfiguredEnvError extends AbortError {
494 constructor(env) {
495 super("Unconfigured enviornment: " + env);
496 this.name = "Unconfigured Env Error";
497 }
498
499 }
500 class ProtectedEnvError extends AbortError {
501 constructor(env) {
502 super("Protected enviornment: " + env);
503 this.name = "Protected Env Error";
504 }
505
506 }
507 class FileTooLargeError extends Error {
508 constructor(file) {
509 super(`File ${file.parentAsset ? file.parentAsset.name : "(unknown)"}/${file.name} size is: ${file.sizeGB}g (> ~.2G)`);
510 this.name = "File too large error";
511 }
512
513 }
514 class Collection {
515 constructor(arr) {
516 this.arr = arr;
517 }
518
519 [Symbol.iterator]() {
520 return this.arr[Symbol.iterator]();
521 }
522
523 findById(id) {
524 return this.arr.find(x => x.id == id);
525 }
526
527 findByName(name) {
528 return this.arr.find(x => x.name == name);
529 }
530
531 findByNameContains(name) {
532 return this.arr.find(x => x.name.includes(name));
533 }
534
535 log() {
536 for (let d of this) {
537 if (d) {
538 log(d.chalkPrint(true));
539 } else {
540 log(chalk$1`{red (None)}`);
541 }
542 }
543 }
544
545 get length() {
546 return this.arr.length;
547 }
548
549 }
550 class RallyBase {
551 static handleCaching() {
552 if (!this.cache) this.cache = [];
553 }
554
555 static isLoaded(env) {
556 if (!this.hasLoadedAll) return;
557 return this.hasLoadedAll[env];
558 }
559
560 static async getById(env, id, qs) {
561 this.handleCaching();
562
563 for (let item of this.cache) {
564 if (item.id == id && item.remote === env || `${env}-${id}` === item.metastring) return item;
565 }
566
567 let data = await lib.makeAPIRequest({
568 env,
569 path: `/${this.endpoint}/${id}`,
570 qs
571 });
572
573 if (data.data) {
574 let o = new this({
575 data: data.data,
576 remote: env,
577 included: data.included
578 });
579 this.cache.push(o);
580 return o;
581 }
582 }
583
584 static async getByName(env, name, qs) {
585 this.handleCaching();
586
587 for (let item of this.cache) {
588 if (item.name === name && item.remote === env) return item;
589 }
590
591 let data = await lib.makeAPIRequest({
592 env,
593 path: `/${this.endpoint}`,
594 qs: { ...qs,
595 filter: `name=${name}` + (qs ? qs.filter : "")
596 }
597 }); //TODO included might not wokr correctly here
598
599 if (data.data[0]) {
600 let o = new this({
601 data: data.data[0],
602 remote: env,
603 included: data.included
604 });
605 this.cache.push(o);
606 return o;
607 }
608 }
609
610 static async getAllPreCollect(d) {
611 return d;
612 }
613
614 static async getAll(env) {
615 this.handleCaching();
616 let datas = await lib.indexPathFast({
617 env,
618 path: `/${this.endpoint}`,
619 pageSize: "50",
620 qs: {
621 sort: "id"
622 }
623 });
624 datas = await this.getAllPreCollect(datas);
625 let all = new Collection(datas.map(data => new this({
626 data,
627 remote: env
628 })));
629 this.cache = [...this.cache, ...all.arr];
630 return all;
631 }
632
633 static async removeCache(env) {
634 this.handleCaching();
635 this.cache = this.cache.filter(x => x.remote !== env);
636 } //Specific turns name into id based on env
637 //Generic turns ids into names
638
639
640 async resolveApply(type, dataObj, direction) {
641 let obj;
642
643 if (direction == "generic") {
644 obj = await type.getById(this.remote, dataObj.id);
645
646 if (obj) {
647 dataObj.name = obj.name;
648 }
649 } else if (direction == "specific") {
650 obj = await type.getByName(this.remote, dataObj.name);
651
652 if (obj) {
653 dataObj.id = obj.id;
654 }
655 }
656
657 return obj;
658 } //Type is the baseclass you are looking for (should extend RallyBase)
659 //name is the name of the field
660 //isArray is true if it has multiple cardinailty, false if it is single
661 //direction gets passed directly to resolveApply
662
663
664 async resolveField(type, name, isArray = false, direction = "generic") {
665 // ignore empty fields
666 let field = this.relationships[name];
667 if (!(field === null || field === void 0 ? void 0 : field.data)) return;
668
669 if (isArray) {
670 return await Promise.all(field.data.map(o => this.resolveApply(type, o, direction)));
671 } else {
672 return await this.resolveApply(type, field.data, direction);
673 }
674 }
675
676 cleanup() {
677 for (let [key, val] of Object.entries(this.relationships)) {
678 //Remove ids from data
679 if (val.data) {
680 if (val.data.id) {
681 delete val.data.id;
682 } else if (val.data[0]) {
683 for (let x of val.data) delete x.id;
684 }
685 }
686
687 delete val.links;
688 } // organization is unused (?)
689
690
691 delete this.relationships.organization; // id is specific to envs
692 // but save source inside meta string in case we need it
693
694 this.metastring = this.remote + "-" + this.data.id;
695 delete this.data.id; // links too
696
697 delete this.data.links;
698 }
699
700 }
701 function sleep(time = 1000) {
702 return new Promise(resolve => setTimeout(resolve, time));
703 }
704
705 const inquirer = importLazy("inquirer");
706 const readdir = importLazy("recursive-readdir");
707 async function loadLocals(path, Class) {
708 let basePath = exports.configObject.repodir;
709 let f = await readdir(basePath);
710 let objs = f.filter(name => name.includes(path)).map(name => new Class({
711 path: name
712 }));
713 return objs;
714 }
715
716 class Provider extends RallyBase {
717 constructor({
718 data,
719 remote
720 }) {
721 super();
722 this.data = data;
723 this.meta = {};
724 this.remote = remote;
725 } //cached
726
727
728 async getEditorConfig() {
729 if (this.editorConfig) return this.editorConfig;
730 this.editorConfig = await lib.makeAPIRequest({
731 env: this.remote,
732 path_full: this.data.links.editorConfig
733 });
734 this.editorConfig.fileExt = await this.getFileExtension();
735 return this.editorConfig;
736 }
737
738 static async getAllPreCollect(providers) {
739 return providers.sort((a, b) => {
740 return a.attributes.category.localeCompare(b.attributes.category) || a.attributes.name.localeCompare(b.attributes.name);
741 });
742 }
743
744 async getFileExtension() {
745 let config = await this.getEditorConfig();
746 let map = {
747 python: "py",
748 text: "txt",
749
750 getmap(key) {
751 if (this.name === "Aurora") return "zip";
752 if (this[key]) return this[key];
753 return key;
754 }
755
756 };
757 return map.getmap(config.lang);
758 }
759
760 chalkPrint(pad = true) {
761 let id = String(this.id);
762 if (pad) id = id.padStart(4);
763 return chalk`{green ${id}}: {blue ${this.category}} - {green ${this.name}}`;
764 }
765
766 }
767
768 defineAssoc(Provider, "id", "data.id");
769 defineAssoc(Provider, "name", "data.attributes.name");
770 defineAssoc(Provider, "category", "data.attributes.category");
771 defineAssoc(Provider, "remote", "meta.remote");
772 defineAssoc(Provider, "editorConfig", "meta.editorConfig");
773 Provider.endpoint = "providerTypes";
774
775 class File extends RallyBase {
776 constructor({
777 data,
778 remote,
779 included,
780 parent
781 }) {
782 super();
783 this.data = data;
784 this.meta = {};
785 this.remote = remote;
786 this.parentAsset = parent;
787 }
788
789 chalkPrint(pad = false) {
790 let id = String("F-" + (this.remote && this.remote + "-" + this.id || "LOCAL"));
791 if (pad) id = id.padStart(15);
792 return chalk`{green ${id}}: {blue ${this.data.attributes ? this.name : "(lite asset)"}}`;
793 }
794
795 canBeDownloaded() {
796 return this.sizeGB <= .2;
797 }
798
799 async getContent(force = false) {
800 if (!this.canBeDownloaded() && !force) {
801 throw new FileTooLargeError(this);
802 }
803
804 return lib.makeAPIRequest({
805 env: this.remote,
806 fullPath: this.contentLink
807 });
808 }
809
810 async delete(remove = true) {
811 return lib.makeAPIRequest({
812 env: this.remote,
813 fullPath: this.selfLink,
814 method: "DELETE"
815 });
816 }
817
818 get size() {
819 return Object.values(this.data.attributes.instances)[0].size;
820 }
821
822 get sizeGB() {
823 return Math.round(this.size / 1024 / 1024 / 1024 * 10) / 10;
824 }
825
826 }
827
828 defineAssoc(File, "id", "data.id");
829 defineAssoc(File, "name", "data.attributes.label");
830 defineAssoc(File, "contentLink", "data.links.content");
831 defineAssoc(File, "selfLink", "data.links.self");
832 defineAssoc(File, "label", "data.attributes.label");
833 defineAssoc(File, "md5", "data.attributes.md5");
834 defineAssoc(File, "sha512", "data.attributes.sha512");
835 defineAssoc(File, "tags", "data.attributes.tagList");
836 defineAssoc(File, "instances", "data.attributes.instances");
837 File.endpoint = null;
838
839 class Asset extends RallyBase {
840 constructor({
841 data,
842 remote,
843 included,
844 lite
845 }) {
846 super();
847 this.data = data;
848 this.meta = {};
849 this.remote = remote;
850
851 if (included) {
852 this.meta.metadata = Asset.normalizeMetadata(included);
853 }
854
855 this.lite = !!lite;
856 }
857
858 static normalizeMetadata(payload) {
859 let newMetadata = {};
860
861 for (let md of payload) {
862 if (md.type !== "metadata") continue;
863 newMetadata[md.attributes.usage] = md.attributes.metadata;
864 }
865
866 return newMetadata;
867 }
868
869 async getMetadata(forceRefresh = false) {
870 if (this.meta.metadata && !forceRefresh) return this.meta.metadata;
871 let req = await lib.makeAPIRequest({
872 env: this.remote,
873 path: `/movies/${this.id}/metadata`
874 });
875 return this.meta.metadata = Asset.normalizeMetadata(req.data);
876 }
877
878 async patchMetadata(metadata) {
879 if (metadata.Workflow && false) {
880 let req = await lib.makeAPIRequest({
881 env: this.remote,
882 path: `/movies/${this.id}/metadata/Workflow`,
883 method: "PATCH",
884 payload: {
885 "data": {
886 "type": "metadata",
887 "attributes": {
888 "metadata": metadata.Workflow
889 }
890 }
891 }
892 });
893 }
894
895 if (metadata.Metadata) {
896 let req = await lib.makeAPIRequest({
897 env: this.remote,
898 path: `/movies/${this.id}/metadata/Metadata`,
899 method: "PATCH",
900 payload: {
901 "data": {
902 "type": "metadata",
903 "attributes": {
904 "metadata": metadata.Metadata
905 }
906 }
907 }
908 });
909 }
910 }
911
912 static lite(id, remote) {
913 return new this({
914 data: {
915 id
916 },
917 remote,
918 lite: true
919 });
920 }
921
922 chalkPrint(pad = false) {
923 let id = String("A-" + (this.remote && this.remote + "-" + this.id || "LOCAL"));
924 if (pad) id = id.padStart(15);
925 return chalk`{green ${id}}: {blue ${this.data.attributes ? this.name : "(lite asset)"}}`;
926 }
927
928 static async createNew(name, env) {
929 let req = await lib.makeAPIRequest({
930 env,
931 path: "/assets",
932 method: "POST",
933 payload: {
934 data: {
935 attributes: {
936 name
937 },
938 type: "assets"
939 }
940 }
941 });
942 return new this({
943 data: req.data,
944 remote: env
945 });
946 }
947
948 async delete() {
949 let req = await lib.makeAPIRequest({
950 env: this.remote,
951 path: "/assets/" + this.id,
952 method: "DELETE"
953 });
954 }
955
956 async getFiles() {
957 let req = await lib.indexPathFast({
958 env: this.remote,
959 path: `/assets/${this.id}/files`,
960 method: "GET"
961 }); //return req;
962
963 return new Collection(req.map(x => new File({
964 data: x,
965 remote: this.remote,
966 parent: this
967 })));
968 }
969
970 async addFile(label, fileuris) {
971 if (!Array.isArray(fileuris)) fileuris = [fileuris];
972 let instances = {};
973
974 for (let i = 0; i < fileuris.length; i++) {
975 instances[String(i + 1)] = {
976 uri: fileuris[i]
977 };
978 }
979
980 let req = await lib.makeAPIRequest({
981 env: this.remote,
982 path: "/files",
983 method: "POST",
984 payload: {
985 "data": {
986 "attributes": {
987 label,
988 instances
989 },
990 "relationships": {
991 "asset": {
992 "data": {
993 id: this.id,
994 "type": "assets"
995 }
996 }
997 },
998 "type": "files"
999 }
1000 }
1001 });
1002 return req;
1003 }
1004
1005 async startWorkflow(jobName, {
1006 initData,
1007 priority
1008 } = {}) {
1009 let attributes = {};
1010
1011 if (initData) {
1012 //Convert init data to string
1013 initData = typeof initData === "string" ? initData : JSON.stringify(initData);
1014 attributes.initData = initData;
1015 }
1016
1017 if (priority) {
1018 attributes.priority = priority;
1019 }
1020
1021 let req = await lib.makeAPIRequest({
1022 env: this.remote,
1023 path: "/workflows",
1024 method: "POST",
1025 payload: {
1026 "data": {
1027 "type": "workflows",
1028 attributes,
1029 "relationships": {
1030 "movie": {
1031 "data": {
1032 id: this.id,
1033 "type": "movies"
1034 }
1035 },
1036 "rule": {
1037 "data": {
1038 "attributes": {
1039 "name": jobName
1040 },
1041 "type": "rules"
1042 }
1043 }
1044 }
1045 }
1046 }
1047 });
1048 return req;
1049 }
1050
1051 static async startAnonWorkflow(env, jobName, {
1052 initData,
1053 priority
1054 } = {}) {
1055 let attributes = {};
1056
1057 if (initData) {
1058 //Convert init data to string
1059 initData = typeof initData === "string" ? initData : JSON.stringify(initData);
1060 attributes.initData = initData;
1061 }
1062
1063 if (priority) {
1064 attributes.priority = priority;
1065 }
1066
1067 let req = await lib.makeAPIRequest({
1068 env,
1069 path: "/workflows",
1070 method: "POST",
1071 payload: {
1072 "data": {
1073 "type": "workflows",
1074 attributes,
1075 "relationships": {
1076 "rule": {
1077 "data": {
1078 "attributes": {
1079 "name": jobName
1080 },
1081 "type": "rules"
1082 }
1083 }
1084 }
1085 }
1086 }
1087 });
1088 return req;
1089 }
1090
1091 async startEphemeralEvaluateIdeal(preset, dynamicPresetData) {
1092 let res;
1093 const env = this.remote;
1094 let provider = await Provider.getByName(this.remote, "SdviEvaluate");
1095 write(chalk`Starting ephemeral evaluate on ${this.chalkPrint(false)}...`); // Fire and forget.
1096
1097 let evalInfo = await lib.makeAPIRequest({
1098 env: this.remote,
1099 path: "/jobs",
1100 method: "POST",
1101 payload: {
1102 data: {
1103 attributes: {
1104 category: provider.category,
1105 providerTypeName: provider.name,
1106 rallyConfiguration: {},
1107 providerData: Buffer.from(preset.code, "binary").toString("base64"),
1108 dynamicPresetData
1109 },
1110 type: "jobs",
1111 relationships: {
1112 movie: {
1113 data: {
1114 id: this.id,
1115 type: "movies"
1116 }
1117 }
1118 }
1119 }
1120 }
1121 });
1122 write(" Waiting for finish...");
1123
1124 for (;;) {
1125 res = await lib.makeAPIRequest({
1126 env,
1127 path_full: evalInfo.data.links.self
1128 });
1129 write(".");
1130
1131 if (res.data.attributes.state == "Complete") {
1132 write(chalk`{green Done}...\n`);
1133 break;
1134 }
1135
1136 await sleep(300);
1137 }
1138
1139 return;
1140 }
1141
1142 async startEvaluate(presetid, dynamicPresetData) {
1143 // Fire and forget.
1144 let data = await lib.makeAPIRequest({
1145 env: this.remote,
1146 path: "/jobs",
1147 method: "POST",
1148 payload: {
1149 data: {
1150 type: "jobs",
1151 attributes: {
1152 dynamicPresetData
1153 },
1154 relationships: {
1155 movie: {
1156 data: {
1157 id: this.id,
1158 type: "movies"
1159 }
1160 },
1161 preset: {
1162 data: {
1163 id: presetid,
1164 type: "presets"
1165 }
1166 }
1167 }
1168 }
1169 }
1170 });
1171 return data;
1172 }
1173
1174 async rename(newName) {
1175 let req = await lib.makeAPIRequest({
1176 env: this.remote,
1177 path: `/assets/${this.id}`,
1178 method: "PATCH",
1179 payload: {
1180 data: {
1181 attributes: {
1182 name: newName
1183 },
1184 type: "assets"
1185 }
1186 }
1187 });
1188 this.name = newName;
1189 return req;
1190 }
1191
1192 }
1193
1194 defineAssoc(Asset, "id", "data.id");
1195 defineAssoc(Asset, "name", "data.attributes.name");
1196 defineAssoc(Asset, "remote", "meta.remote");
1197 defineAssoc(Asset, "md", "meta.metadata");
1198 defineAssoc(Asset, "lite", "meta.lite");
1199 Asset.endpoint = "movies";
1200
1201 let home;
1202
1203 if (os.homedir) {
1204 home = os.homedir();
1205 }
1206
1207 const colon = /:/g;
1208 const siloLike = /(silo\-\w+?)s?\/([^\/]+)\.([\w1234567890]+)$/g;
1209 function pathTransform(path) {
1210 if (path.includes(":")) {
1211 //Ignore the first colon in window-like filesystems
1212 path = path.slice(0, 3) + path.slice(3).replace(colon, "--");
1213 }
1214
1215 if (exports.configObject.invertedPath) {
1216 path = path.replace(siloLike, "$2-$1.$3");
1217 }
1218
1219 if (path.includes("\\342\\200\\220")) {
1220 path = path.replace("\\342\\200\\220", "‐");
1221 }
1222
1223 return path;
1224 }
1225 function readFileSync(path, options) {
1226 return fs__default.readFileSync(pathTransform(path), options);
1227 } //Create writefilesync, with ability to create directory if it doesnt exist
1228
1229 function writeFileSync(path$1, data, options, dircreated = false) {
1230 path$1 = pathTransform(path$1);
1231
1232 try {
1233 return fs__default.writeFileSync(path$1, data, options);
1234 } catch (e) {
1235 if (dircreated) throw e;
1236 let directory = path.dirname(path$1);
1237
1238 try {
1239 fs__default.statSync(directory);
1240 throw e;
1241 } catch (nodir) {
1242 fs__default.mkdirSync(directory);
1243 return writeFileSync(path$1, data, options, true);
1244 }
1245 }
1246 }
1247
1248 let exists = {};
1249
1250 class Preset extends RallyBase {
1251 constructor({
1252 path: path$1,
1253 remote,
1254 data,
1255 subProject
1256 } = {}) {
1257 // Get full path if possible
1258 if (path$1) {
1259 path$1 = path.resolve(path$1);
1260
1261 if (path.dirname(path$1).includes("silo-metadata")) {
1262 throw new AbortError("Constructing preset from metadata file");
1263 }
1264 }
1265
1266 super(); // Cache by path
1267
1268 if (path$1) {
1269 if (exists[pathTransform(path$1)]) return exists[pathTransform(path$1)];
1270 exists[pathTransform(path$1)] = this;
1271 }
1272
1273 this.meta = {};
1274 this.subproject = subProject;
1275 this.remote = remote;
1276
1277 if (lib.isLocalEnv(this.remote)) {
1278 if (path$1) {
1279 this.path = path$1;
1280 let pathspl = this.path.split(".");
1281 this.ext = pathspl[pathspl.length - 1];
1282
1283 try {
1284 this.code = this.getLocalCode();
1285 } catch (e) {
1286 if (e.code === "ENOENT" && exports.configObject.ignoreMissing) {
1287 this.missing = true;
1288 return undefined;
1289 } else {
1290 log(chalk`{red Node Error} ${e.message}`);
1291 throw new AbortError("Could not load code of local file");
1292 }
1293 }
1294
1295 let name = this.parseFilenameForName() || this.parseCodeForName();
1296
1297 try {
1298 this.data = this.getLocalMetadata();
1299 this.isGeneric = true;
1300 name = this.name;
1301 } catch (e) {
1302 log(chalk`{yellow Warning}: ${path$1} does not have a readable metadata file! Looking for ${this.localmetadatapath}`);
1303 this.data = Preset.newShell(name);
1304 this.isGeneric = false;
1305 }
1306
1307 this.name = name;
1308 } else {
1309 this.data = Preset.newShell();
1310 }
1311 } else {
1312 this.data = data; //this.name = data.attributes.name;
1313 //this.id = data.id;
1314
1315 this.isGeneric = false;
1316 }
1317
1318 this.data.attributes.rallyConfiguration = undefined;
1319 this.data.attributes.systemManaged = undefined;
1320 } //Given a metadata file, get its actualy file
1321
1322
1323 static async fromMetadata(path, subproject) {
1324 let data;
1325
1326 try {
1327 data = JSON.parse(readFileSync(path));
1328 } catch (e) {
1329 if (e.code === "ENOENT" && exports.configObject.ignoreMissing) {
1330 return null;
1331 } else {
1332 throw e;
1333 }
1334 }
1335
1336 let providerType = data.relationships.providerType.data.name;
1337 let provider = await Provider.getByName("DEV", providerType);
1338
1339 if (!provider) {
1340 log(chalk`{red The provider type {green ${providerType}} does not exist}`);
1341 log(chalk`{red Skipping {green ${path}}.}`);
1342 return null;
1343 }
1344
1345 let ext = await provider.getFileExtension();
1346 let name = data.attributes.name;
1347 let realpath = Preset.getLocalPath(name, ext, subproject);
1348 return new Preset({
1349 path: realpath,
1350 subProject: subproject
1351 });
1352 }
1353
1354 static newShell(name = undefined) {
1355 return {
1356 "attributes": {
1357 "providerSettings": {
1358 "PresetName": name
1359 }
1360 },
1361 "relationships": {},
1362 "type": "presets"
1363 };
1364 }
1365
1366 cleanup() {
1367 super.cleanup();
1368 delete this.attributes["createdAt"];
1369 delete this.attributes["updatedAt"];
1370 }
1371
1372 async acclimatize(env) {
1373 if (!this.isGeneric) throw new AbortError("Cannot acclimatize non-generics or shells");
1374 let providers = await Provider.getAll(env);
1375 let ptype = this.relationships["providerType"];
1376 ptype = ptype.data;
1377 let provider = providers.findByName(ptype.name);
1378 ptype.id = provider.id;
1379 }
1380
1381 get test() {
1382 if (!this.code) return [];
1383 const regex = /[^-]autotest:\s?([\w\d_\-. \/]+)[\r\s\n]*?/gm;
1384 let match;
1385 let matches = [];
1386
1387 while (match = regex.exec(this.code)) {
1388 matches.push(match[1]);
1389 }
1390
1391 return matches;
1392 }
1393
1394 async runTest(env) {
1395 let remote = await Preset.getByName(env, this.name);
1396
1397 for (let test of this.test) {
1398 log("Tests...");
1399 let asset;
1400
1401 if (test.startsWith("id")) {
1402 let match = /id:\s*(\d+)/g.exec(test);
1403
1404 if (!match) {
1405 log(chalk`{red Could not parse autotest} ${test}.`);
1406 throw new AbortError("Could not properly parse the preset header");
1407 }
1408
1409 asset = await Asset.getById(env, match[1]);
1410 } else {
1411 asset = await Asset.getByName(env, test);
1412 }
1413
1414 if (!asset) {
1415 log(chalk`{yellow No movie found}, skipping test.`);
1416 continue;
1417 }
1418
1419 log(chalk`Starting job {green ${this.name}} on ${asset.chalkPrint(false)}... `);
1420 await asset.startEvaluate(remote.id);
1421 }
1422 }
1423
1424 async resolve() {
1425 if (this.isGeneric) return;
1426 let proType = await this.resolveField(Provider, "providerType");
1427 this.ext = await proType.getFileExtension();
1428 this.isGeneric = true;
1429 return {
1430 proType
1431 };
1432 }
1433
1434 async saveLocal() {
1435 await this.saveLocalMetadata();
1436 await this.saveLocalFile();
1437 }
1438
1439 async saveLocalMetadata() {
1440 if (!this.isGeneric) {
1441 await this.resolve();
1442 this.cleanup();
1443 }
1444
1445 writeFileSync(this.localmetadatapath, JSON.stringify(this.data, null, 4));
1446 }
1447
1448 async saveLocalFile() {
1449 writeFileSync(this.localpath, this.code);
1450 }
1451
1452 async uploadRemote(env) {
1453 await this.uploadCodeToEnv(env, true);
1454 }
1455
1456 async save(env) {
1457 this.saved = true;
1458
1459 if (!this.isGeneric) {
1460 await this.resolve();
1461 }
1462
1463 this.cleanup();
1464
1465 if (lib.isLocalEnv(env)) {
1466 log(chalk`Saving preset {green ${this.name}} to {blue ${lib.envName(env)}}.`);
1467 await this.saveLocal();
1468 } else {
1469 await this.uploadRemote(env);
1470 }
1471 }
1472
1473 async downloadCode() {
1474 if (!this.remote || this.code) return this.code;
1475 let code = await lib.makeAPIRequest({
1476 env: this.remote,
1477 path_full: this.data.links.providerData,
1478 json: false
1479 }); //match header like
1480 // # c: d
1481 // # b
1482 // # a
1483 // ##################
1484
1485 let headerRegex = /(^# .+[\r\n]+)+#+[\r\n]+/gim;
1486 let hasHeader = headerRegex.exec(code);
1487
1488 if (hasHeader) {
1489 this.header = code.substring(0, hasHeader[0].length - 1);
1490 code = code.substring(hasHeader[0].length);
1491 }
1492
1493 return this.code = code;
1494 }
1495
1496 get code() {
1497 if (this._code) return this._code;
1498 }
1499
1500 set code(v) {
1501 this._code = v;
1502 }
1503
1504 chalkPrint(pad = true) {
1505 let id = String("P-" + (this.remote && this.remote + "-" + this.id || "LOCAL"));
1506 let sub = "";
1507
1508 if (this.subproject) {
1509 sub = chalk`{yellow ${this.subproject}}`;
1510 }
1511
1512 if (pad) id = id.padStart(10);
1513
1514 if (this.name == undefined) {
1515 return chalk`{green ${id}}: ${sub}{red ${this.path}}`;
1516 } else if (this.meta.proType) {
1517 return chalk`{green ${id}}: ${sub}{red ${this.meta.proType.name}} {blue ${this.name}}`;
1518 } else {
1519 return chalk`{green ${id}}: ${sub}{blue ${this.name}}`;
1520 }
1521 }
1522
1523 parseFilenameForName() {
1524 if (this.path.endsWith(".jinja") || this.path.endsWith(".json")) {
1525 return path.basename(this.path).replace("_", " ").replace("-", " ").replace(".json", "").replace(".jinja", "");
1526 }
1527 }
1528
1529 parseCodeForName() {
1530 const name_regex = /name\s?:\s([\w\d. \/]+)[\r\s\n]*?/;
1531 const match = name_regex.exec(this.code);
1532 if (match) return match[1];
1533 }
1534
1535 findStringsInCode(strings) {
1536 if (!this.code) return [];
1537 return strings.filter(str => {
1538 let regex = new RegExp(str);
1539 return !!this.code.match(regex);
1540 });
1541 }
1542
1543 static getLocalPath(name, ext, subproject) {
1544 return path__default.join(exports.configObject.repodir, subproject || "", "silo-presets", name + "." + ext);
1545 }
1546
1547 get localpath() {
1548 return Preset.getLocalPath(this.name, this.ext, this.subproject);
1549 }
1550
1551 get path() {
1552 if (this._path) return this._path;
1553 }
1554
1555 set path(val) {
1556 this._path = val;
1557 }
1558
1559 get name() {
1560 return this._nameOuter;
1561 }
1562
1563 set name(val) {
1564 if (!this._nameInner) this._nameInner = val;
1565 this._nameOuter = val;
1566 }
1567
1568 set providerType(value) {
1569 this.relationships["providerType"] = {
1570 data: { ...value,
1571 type: "providerTypes"
1572 }
1573 };
1574 }
1575
1576 get localmetadatapath() {
1577 if (this.path) {
1578 return this.path.replace("silo-presets", "silo-metadata").replace(new RegExp(this.ext + "$"), "json");
1579 }
1580
1581 return path__default.join(exports.configObject.repodir, this.subproject || "", "silo-metadata", this.name + ".json");
1582 }
1583
1584 get immutable() {
1585 return this.name.includes("Constant") && !exports.configObject.updateImmutable;
1586 }
1587
1588 async uploadPresetData(env, id) {
1589 var _this$relationships, _this$relationships$p, _this$relationships$p2;
1590
1591 if (this.code.trim() === "NOUPLOAD") {
1592 write(chalk`code skipped {yellow :)}, `);
1593 return;
1594 }
1595
1596 let code = this.code;
1597 let headers = {};
1598 let providerName = (_this$relationships = this.relationships) === null || _this$relationships === void 0 ? void 0 : (_this$relationships$p = _this$relationships.providerType) === null || _this$relationships$p === void 0 ? void 0 : (_this$relationships$p2 = _this$relationships$p.data) === null || _this$relationships$p2 === void 0 ? void 0 : _this$relationships$p2.name;
1599
1600 if (!exports.configObject.skipHeader && (providerName === "SdviEvaluate" || providerName === "SdviEvalPro")) {
1601 write(chalk`generate header, `);
1602 let repodir = exports.configObject.repodir;
1603 let localpath = this.path.replace(repodir, "");
1604 if (localpath.startsWith("/")) localpath = localpath.substring(1);
1605
1606 try {
1607 let {
1608 stdout: headerText
1609 } = await spawn({
1610 noecho: true
1611 }, "sh", [path__default.join(exports.configObject.repodir, `bin/header.sh`), moment(Date.now()).format("ddd YYYY/MM/DD hh:mm:ssa"), localpath]);
1612 code = headerText + code;
1613 write(chalk`header ok, `);
1614 } catch (e) {
1615 write(chalk`missing unix, `);
1616 }
1617 } //binary presets
1618
1619
1620 if (providerName == "Vantage") {
1621 code = code.toString("base64");
1622 headers["Content-Transfer-Encoding"] = "base64";
1623 }
1624
1625 let res = await lib.makeAPIRequest({
1626 env,
1627 path: `/presets/${id}/providerData`,
1628 body: code,
1629 method: "PUT",
1630 fullResponse: true,
1631 timeout: 10000,
1632 headers
1633 });
1634 write(chalk`code up {yellow ${res.statusCode}}, `);
1635 }
1636
1637 async grabMetadata(env) {
1638 let remote = await Preset.getByName(env, this.name);
1639 this.isGeneric = false;
1640
1641 if (!remote) {
1642 throw new AbortError(`No file found on remote ${env} with name ${this.name}`);
1643 }
1644
1645 this.data = remote.data;
1646 this.remote = env;
1647 }
1648
1649 async deleteRemoteVersion(env, id = null) {
1650 if (lib.isLocalEnv(env)) return false;
1651
1652 if (!id) {
1653 let remote = await Preset.getByName(env, this.name);
1654 id = remote.id;
1655 }
1656
1657 return await lib.makeAPIRequest({
1658 env,
1659 path: `/presets/${id}`,
1660 method: "DELETE"
1661 });
1662 }
1663
1664 async delete() {
1665 if (lib.isLocalEnv(this.remote)) return false;
1666 return await this.deleteRemoteVersion(this.remote, this.id);
1667 }
1668
1669 async uploadCodeToEnv(env, includeMetadata, shouldTest = true) {
1670 if (!this.name) {
1671 let match;
1672
1673 if (match = /^(#|["']{3})\s*EPH (\d+)/.exec(this.code.trim())) {
1674 let a = await Asset.getById(env, Number(match[2]));
1675 return a.startEphemeralEvaluateIdeal(this);
1676 } else {
1677 log(chalk`Failed uploading {red ${this.path}}. No name found.`);
1678 return;
1679 }
1680 }
1681
1682 write(chalk`Uploading preset {green ${this.name}} to {green ${env}}: `);
1683
1684 if (this.immutable) {
1685 log(chalk`{magenta IMMUTABLE}. Nothing to do.`);
1686 return;
1687 } //First query the api to see if this already exists.
1688
1689
1690 let remote = await Preset.getByName(env, this.name);
1691
1692 if (remote) {
1693 //If it exists we can replace it
1694 write("replace, ");
1695
1696 if (includeMetadata) {
1697 let payload = {
1698 data: {
1699 attributes: this.data.attributes,
1700 type: "presets"
1701 }
1702 };
1703
1704 if (this.relationships.tagNames) {
1705 payload.relationships = {
1706 tagNames: this.relationships.tagNames
1707 };
1708 }
1709
1710 let res = await lib.makeAPIRequest({
1711 env,
1712 path: `/presets/${remote.id}`,
1713 method: "PATCH",
1714 payload,
1715 fullResponse: true
1716 });
1717 write(chalk`metadata {yellow ${res.statusCode}}, `);
1718
1719 if (res.statusCode == 500) {
1720 log(chalk`skipping code upload, did not successfully upload metadata`);
1721 return;
1722 }
1723 }
1724
1725 await this.uploadPresetData(env, remote.id);
1726 } else {
1727 write("create, ");
1728 let metadata = {
1729 data: this.data
1730 };
1731
1732 if (!this.relationships["providerType"]) {
1733 throw new AbortError("Cannot acclimatize shelled presets. (try creating it on the env first)");
1734 }
1735
1736 await this.acclimatize(env);
1737 write("Posting to create preset... ");
1738 let res = await lib.makeAPIRequest({
1739 env,
1740 path: `/presets`,
1741 method: "POST",
1742 payload: metadata,
1743 timeout: 5000
1744 });
1745 let id = res.data.id;
1746 write(chalk`Created id {green ${id}}... Uploading Code... `);
1747 await this.uploadPresetData(env, id);
1748 }
1749
1750 if (this.test[0] && shouldTest) {
1751 await this.runTest(env);
1752 } else {
1753 log("No tests. Done.");
1754 }
1755 }
1756
1757 getLocalMetadata() {
1758 return JSON.parse(readFileSync(this.localmetadatapath, "utf-8"));
1759 }
1760
1761 getLocalCode() {
1762 //todo fixup for binary presets, see uploadPresetData
1763 return readFileSync(this.path, "utf-8");
1764 }
1765
1766 parseHeaderInfo() {
1767 var _$exec$, _$exec$2, _$exec$3, _$exec$4, _$exec$5, _$exec$6, _$exec$7;
1768
1769 if (!this.header) return null;
1770 let abs = {
1771 built: (_$exec$ = /Built On:(.+)/.exec(this.header)[1]) === null || _$exec$ === void 0 ? void 0 : _$exec$.trim(),
1772 author: (_$exec$2 = /Author:(.+)/.exec(this.header)[1]) === null || _$exec$2 === void 0 ? void 0 : _$exec$2.trim(),
1773 build: (_$exec$3 = /Build:(.+)/.exec(this.header)[1]) === null || _$exec$3 === void 0 ? void 0 : _$exec$3.trim(),
1774 version: (_$exec$4 = /Version:(.+)/.exec(this.header)[1]) === null || _$exec$4 === void 0 ? void 0 : _$exec$4.trim(),
1775 branch: (_$exec$5 = /Branch:(.+)/.exec(this.header)[1]) === null || _$exec$5 === void 0 ? void 0 : _$exec$5.trim(),
1776 commit: (_$exec$6 = /Commit:(.+)/.exec(this.header)[1]) === null || _$exec$6 === void 0 ? void 0 : _$exec$6.trim(),
1777 local: (_$exec$7 = /Local File:(.+)/.exec(this.header)[1]) === null || _$exec$7 === void 0 ? void 0 : _$exec$7.trim()
1778 };
1779 let tryFormats = [[true, "ddd MMM DD HH:mm:ss YYYY"], [false, "ddd YYYY/MM/DD LTS"]];
1780
1781 for (let [isUTC, format] of tryFormats) {
1782 let date;
1783
1784 if (isUTC) {
1785 date = moment.utc(abs.built, format);
1786 } else {
1787 date = moment(abs.built, format);
1788 }
1789
1790 if (!date.isValid()) continue;
1791 abs.offset = date.fromNow();
1792 break;
1793 }
1794
1795 return abs;
1796 }
1797
1798 async printRemoteInfo(env) {
1799 let remote = await Preset.getByName(env, this.name);
1800 await remote.downloadCode();
1801 let i = remote.parseHeaderInfo();
1802
1803 if (i) {
1804 log(chalk`
1805 ENV: {red ${env}}, updated {yellow ~${i.offset}}
1806 Built on {blue ${i.built}} by {green ${i.author}}
1807 From ${i.build || "(unknown)"} on ${i.branch} ({yellow ${i.commit}})
1808 `.replace(/^[ \t]+/gim, "").trim());
1809 } else {
1810 log(chalk`No header on {red ${env}}`);
1811 }
1812 }
1813
1814 async getInfo(envs) {
1815 await this.printDepends();
1816
1817 for (let env of envs.split(",")) {
1818 await this.printRemoteInfo(env);
1819 }
1820 }
1821
1822 async printDepends(indent = 0, locals = null, seen = {}) {
1823 let includeRegex = /@include "(.+)"/gim;
1824 let includes = this.code.split("\n").map(x => includeRegex.exec(x)).filter(x => x).map(x => x[1]);
1825
1826 if (!locals) {
1827 locals = new Collection((await loadLocals("silo-presets", Preset)));
1828 }
1829
1830 log(Array(indent + 1).join(" ") + "- " + this.name);
1831
1832 for (let include of includes) {
1833 if (seen[include]) {
1834 log(Array(indent + 1).join(" ") + " - (seen) " + include);
1835 } else {
1836 seen[include] = true;
1837 await locals.findByName(include).printDepends(indent + 2, locals, seen);
1838 }
1839 }
1840 }
1841
1842 }
1843
1844 defineAssoc(Preset, "_nameInner", "data.attributes.providerSettings.PresetName");
1845 defineAssoc(Preset, "_nameOuter", "data.attributes.name");
1846 defineAssoc(Preset, "id", "data.id");
1847 defineAssoc(Preset, "attributes", "data.attributes");
1848 defineAssoc(Preset, "relationships", "data.relationships");
1849 defineAssoc(Preset, "remote", "meta.remote");
1850 defineAssoc(Preset, "_code", "meta.code");
1851 defineAssoc(Preset, "_path", "meta.path");
1852 defineAssoc(Preset, "isGeneric", "meta.isGeneric");
1853 defineAssoc(Preset, "ext", "meta.ext");
1854 defineAssoc(Preset, "subproject", "meta.project");
1855 defineAssoc(Preset, "metastring", "meta.metastring");
1856 Preset.endpoint = "presets";
1857
1858 class Notification extends RallyBase {
1859 constructor({
1860 data,
1861 remote
1862 }) {
1863 super();
1864 this.data = data;
1865 this.meta = {};
1866 this.remote = remote;
1867 }
1868
1869 static async getAllPreCollect(notifications) {
1870 return notifications.sort((a, b) => {
1871 return a.attributes.type.localeCompare(b.attributes.type) || a.attributes.name.localeCompare(b.attributes.name);
1872 });
1873 }
1874
1875 chalkPrint(pad = false) {
1876 let id = String("N-" + this.id);
1877 if (pad) id = id.padStart(4);
1878 return chalk`{green ${id}}: {blue ${this.type}} - {green ${this.name}}`;
1879 }
1880
1881 }
1882
1883 defineAssoc(Notification, "id", "data.id");
1884 defineAssoc(Notification, "name", "data.attributes.name");
1885 defineAssoc(Notification, "address", "data.attributes.address");
1886 defineAssoc(Notification, "type", "data.attributes.type");
1887 defineAssoc(Notification, "remote", "meta.remote");
1888 Notification.endpoint = "notificationPresets";
1889
1890 class Rule extends RallyBase {
1891 constructor({
1892 path: path$1,
1893 data,
1894 remote,
1895 subProject
1896 } = {}) {
1897 super();
1898
1899 if (path$1) {
1900 path$1 = path.resolve(path$1);
1901
1902 try {
1903 let f = readFileSync(path$1, "utf-8");
1904 data = JSON.parse(readFileSync(path$1, "utf-8"));
1905 } catch (e) {
1906 if (e.code === "ENOENT") {
1907 if (exports.configObject.ignoreMissing) {
1908 this.missing = true;
1909 return undefined;
1910 } else {
1911 throw new AbortError("Could not load code of local file");
1912 }
1913 } else {
1914 throw new AbortError(`Unreadable JSON in ${path$1}. ${e}`);
1915 }
1916 }
1917 }
1918
1919 this.meta = {};
1920 this.subproject = subProject;
1921
1922 if (!data) {
1923 data = Rule.newShell();
1924 }
1925
1926 this.data = data;
1927 this.remote = remote;
1928 this.isGeneric = !this.remote;
1929 }
1930
1931 static newShell() {
1932 return {
1933 "attributes": {
1934 "description": "-",
1935 "priority": "PriorityNorm",
1936 "starred": false
1937 },
1938 "relationships": {},
1939 "type": "workflowRules"
1940 };
1941 }
1942
1943 async acclimatize(env) {
1944 this.remote = env;
1945 let preset = await this.resolveField(Preset, "preset", false, "specific");
1946 let pNext = await this.resolveField(Rule, "passNext", false, "specific");
1947 let eNext = await this.resolveField(Rule, "errorNext", false, "specific");
1948 let proType = await this.resolveField(Provider, "providerType", false, "specific");
1949 let dynamicNexts = await this.resolveField(Rule, "dynamicNexts", true, "specific");
1950 let enterNotif = await this.resolveField(Notification, "enterNotifications", true, "specific");
1951 let errorNotif = await this.resolveField(Notification, "errorNotifications", true, "specific");
1952 let passNotif = await this.resolveField(Notification, "passNotifications", true, "specific");
1953 }
1954
1955 async saveA(env) {
1956 if (lib.isLocalEnv(env)) return;
1957 return await this.createIfNotExist(env);
1958 }
1959
1960 async saveB(env) {
1961 if (!this.isGeneric) {
1962 await this.resolve();
1963 }
1964
1965 this.cleanup();
1966
1967 if (lib.isLocalEnv(env)) {
1968 log(chalk`Saving rule {green ${this.name}} to {blue ${lib.envName(env)}}.`);
1969 writeFileSync(this.localpath, JSON.stringify(this.data, null, 4));
1970 } else {
1971 await this.acclimatize(env);
1972 await this.uploadRemote(env);
1973 }
1974 }
1975
1976 get immutable() {
1977 return false;
1978 }
1979
1980 async createIfNotExist(env) {
1981 write(chalk`First pass rule {green ${this.name}} to {green ${env}}: `);
1982
1983 if (this.immutable) {
1984 log(chalk`{magenta IMMUTABLE}. Nothing to do.`);
1985 return;
1986 } //First query the api to see if this already exists.
1987
1988
1989 let remote = await Rule.getByName(env, this.name);
1990 this.idMap = this.idMap || {};
1991
1992 if (remote) {
1993 this.idMap[env] = remote.id;
1994 log(chalk`exists ${remote.chalkPrint(false)}`);
1995 return;
1996 } //If it exists we can replace it
1997
1998
1999 write("create, ");
2000 let res = await lib.makeAPIRequest({
2001 env,
2002 path: `/workflowRules`,
2003 method: "POST",
2004 payload: {
2005 data: {
2006 attributes: {
2007 name: this.name
2008 },
2009 type: "workflowRules"
2010 }
2011 }
2012 });
2013 this.idMap = this.idMap || {};
2014 this.idMap[env] = res.data.id;
2015 write("id ");
2016 log(this.idMap[env]);
2017 }
2018
2019 async patchStrip() {
2020 delete this.data.attributes.createdAt;
2021 delete this.data.attributes.starred;
2022 delete this.data.attributes.updatedAt; // TEMP FIX FOR BUG IN SDVI
2023
2024 if (this.relationships.passMetadata && this.relationships.passMetadata[0]) {
2025 log("HAS PASS");
2026 log(this.name);
2027 log("HAS PASS");
2028 }
2029
2030 delete this.relationships.passMetadata;
2031
2032 if (this.relationships.errorMetadata && this.relationships.errorMetadata[0]) {
2033 log("HAS PASS");
2034 log(this.name);
2035 log("HAS PASS");
2036 }
2037
2038 delete this.relationships.errorMetadata; // This is commented out because it was fixed.
2039 //for(let key in this.relationships){
2040 //let relationship = this.relationships[key];
2041 //if(!relationship.data || relationship.data instanceof Array && !relationship.data[0]){
2042 //delete this.relationships[key];
2043 //}
2044 //}
2045 }
2046
2047 async uploadRemote(env) {
2048 write(chalk`Uploading rule {green ${this.name}} to {green ${env}}: `);
2049
2050 if (this.immutable) {
2051 log(chalk`{magenta IMMUTABLE}. Nothing to do.`);
2052 return;
2053 }
2054
2055 if (this.idMap[env]) {
2056 this.remote = env;
2057 await this.patchStrip();
2058 this.data.id = this.idMap[env]; //If it exists we can replace it
2059
2060 write("replace, ");
2061 let res = await lib.makeAPIRequest({
2062 env,
2063 path: `/workflowRules/${this.idMap[env]}`,
2064 method: "PATCH",
2065 payload: {
2066 data: this.data
2067 },
2068 fullResponse: true
2069 });
2070 log(chalk`response {yellow ${res.statusCode}}`);
2071
2072 if (res.statusCode !== 200) {
2073 log(res.body);
2074 log(JSON.stringify(this.data, null, 4));
2075 }
2076 } else {
2077 throw Error("Bad idmap!");
2078 }
2079 }
2080
2081 get localpath() {
2082 return path.join(exports.configObject.repodir, this.subproject || "", "silo-rules", this.name + ".json");
2083 }
2084
2085 async resolve() {
2086 let preset = await this.resolveField(Preset, "preset", false); //log(preset);
2087
2088 let pNext = await this.resolveField(Rule, "passNext", false);
2089 let eNext = await this.resolveField(Rule, "errorNext", false);
2090 let proType = await this.resolveField(Provider, "providerType", false); //log("Dynamic nexts")
2091
2092 let dynamicNexts = await this.resolveField(Rule, "dynamicNexts", true); //log(dynamicNexts);
2093
2094 let enterNotif = await this.resolveField(Notification, "enterNotifications", true);
2095 let errorNotif = await this.resolveField(Notification, "errorNotifications", true);
2096 let passNotif = await this.resolveField(Notification, "passNotifications", true); //TODO Unsupported
2097
2098 delete this.relationships["enterMetadata"];
2099 delete this.relationships["errorMetadata"];
2100 this.isGeneric = true;
2101 return {
2102 preset,
2103 proType,
2104 pNext,
2105 eNext,
2106 dynamicNexts,
2107 errorNotif,
2108 enterNotif,
2109 passNotif
2110 };
2111 }
2112
2113 chalkPrint(pad = true) {
2114 let id = String("R-" + (this.remote && this.remote + "-" + this.id || "LOCAL"));
2115 let sub = "";
2116
2117 if (this.subproject) {
2118 sub = chalk`{yellow ${this.subproject}}`;
2119 }
2120
2121 if (pad) id = id.padStart(10);
2122
2123 try {
2124 return chalk`{green ${id}}: ${sub}{blue ${this.name}}`;
2125 } catch (e) {
2126 return this.data;
2127 }
2128 }
2129
2130 }
2131
2132 defineAssoc(Rule, "name", "data.attributes.name");
2133 defineAssoc(Rule, "description", "data.attributes.description");
2134 defineAssoc(Rule, "id", "data.id");
2135 defineAssoc(Rule, "relationships", "data.relationships");
2136 defineAssoc(Rule, "isGeneric", "meta.isGeneric");
2137 defineAssoc(Rule, "remote", "meta.remote");
2138 defineAssoc(Rule, "subproject", "meta.project");
2139 defineAssoc(Rule, "idMap", "meta.idMap");
2140 Rule.endpoint = "workflowRules";
2141
2142 //Move project into silo metadata
2143 //move autotest into silo metadata
2144 //
2145
2146 class SupplyChain {
2147 constructor(startingRule, stopRule) {
2148 if (startingRule) {
2149 this.startingRule = startingRule;
2150 this.stopRule = stopRule;
2151 this.remote = startingRule.remote;
2152 }
2153 }
2154
2155 async downloadPresetCode(objs = this.allPresets) {
2156 log("Downloading code... ");
2157 await lib.keepalive(objs.arr.map(x => () => x.downloadCode()));
2158 }
2159
2160 async calculate() {
2161 log("Getting rules... ");
2162 this.allRules = await Rule.getAll(this.remote);
2163 log(this.allRules.length);
2164 log("Getting presets... ");
2165 this.allPresets = await Preset.getAll(this.remote);
2166 log(this.allPresets.length);
2167 log("Getting providers... ");
2168 this.allProviders = await Provider.getAll(this.remote);
2169 log(this.allProviders.length);
2170 log("Getting notifications... ");
2171 this.allNotifications = await Notification.getAll(this.remote);
2172 log(this.allNotifications.length);
2173
2174 if (!this.startingRule) {
2175 this.rules = this.allRules;
2176 this.presets = this.allPresets;
2177 this.notifications = new Collection([]);
2178 await this.downloadPresetCode();
2179 return;
2180 } else {
2181 await this.downloadPresetCode();
2182 }
2183
2184 log("Done!"); //Now we have everything we need to find a whole supply chain
2185
2186 write("Calculating Supply chain... ");
2187 log(this.startingRule.chalkPrint());
2188 let allRuleNames = this.allRules.arr.map(x => x.name).filter(x => x.length >= 4);
2189 let allPresetNames = this.allPresets.arr.map(x => x.name).filter(x => x.length >= 4);
2190 let allNotifNames = this.allNotifications.arr.map(x => x.name).filter(x => x.length >= 4);
2191 let requiredNotifications = new Set();
2192 let ruleQueue = [this.startingRule];
2193 let presetQueue = [];
2194
2195 for (let currentRule of ruleQueue) {
2196 if (currentRule === this.stopRule) continue;
2197 let {
2198 eNext,
2199 pNext,
2200 preset,
2201 passNotif,
2202 errorNotif,
2203 enterNotif
2204 } = await currentRule.resolve();
2205 passNotif.forEach(n => requiredNotifications.add(n));
2206 enterNotif.forEach(n => requiredNotifications.add(n));
2207 errorNotif.forEach(n => requiredNotifications.add(n));
2208 if (eNext && !ruleQueue.includes(eNext)) ruleQueue.push(eNext);
2209 if (pNext && !ruleQueue.includes(eNext)) ruleQueue.push(pNext);
2210 let neededPresets = preset.findStringsInCode(allPresetNames);
2211 neededPresets = neededPresets.map(x => this.allPresets.findByName(x));
2212 let neededRules = preset.findStringsInCode(allRuleNames);
2213 neededRules = neededRules.map(x => this.allRules.findByName(x));
2214 preset.findStringsInCode(allNotifNames).map(str => this.allNotifications.findByName(str)).forEach(notif => requiredNotifications.add(notif));
2215 neededPresets.push(preset);
2216
2217 for (let p of neededPresets) if (!presetQueue.includes(p)) presetQueue.push(p);
2218
2219 for (let p of neededRules) if (!ruleQueue.includes(p)) ruleQueue.push(p);
2220
2221 if (exports.configObject.verbose) {
2222 write(currentRule.chalkPrint(false));
2223 log(":");
2224 write(" ");
2225 write(preset.chalkPrint(false));
2226 log(":");
2227 write(" Pass Next: ");
2228 if (pNext) write(pNext.chalkPrint(false));else write("None");
2229 log("");
2230 write(" Err Next: ");
2231 if (eNext) write(eNext.chalkPrint(false));else write("None");
2232 log("");
2233 log(" Rules:");
2234
2235 for (let p of neededRules) log(" " + p.chalkPrint(true));
2236
2237 log(" Presets:");
2238
2239 for (let p of neededPresets) log(" " + p.chalkPrint(true));
2240
2241 log("\n");
2242 }
2243 }
2244
2245 log("Done!");
2246 this.rules = new Collection(ruleQueue);
2247 this.presets = new Collection(presetQueue);
2248 requiredNotifications.delete(undefined);
2249 this.notifications = new Collection([...requiredNotifications]);
2250 }
2251
2252 async log() {
2253 if (this.notifications.arr.length > 0) {
2254 log("Required notifications: ");
2255 this.notifications.log();
2256 }
2257
2258 if (this.rules.arr.length > 0) {
2259 write("Required rules: ");
2260 log(this.rules.arr.length);
2261 this.rules.log();
2262 }
2263
2264 if (this.presets.arr.length > 0) {
2265 write("Required presets: ");
2266 log(this.presets.arr.length);
2267 this.presets.log();
2268 }
2269
2270 if (exports.configObject.rawOutput) {
2271 return {
2272 presets: this.presets.arr,
2273 rules: this.rules.arr,
2274 notifications: this.notifications.arr
2275 };
2276 }
2277 }
2278
2279 async deleteTo(env) {
2280 for (let preset of this.presets) {
2281 try {
2282 await preset.deleteRemoteVersion(env);
2283 } catch (e) {
2284 log(e);
2285 }
2286 }
2287 }
2288
2289 async syncTo(env) {
2290 for (let preset of this.presets) {
2291 try {
2292 await preset.save(env);
2293 } catch (e) {
2294 log(e);
2295 }
2296 }
2297
2298 if (this.rules.arr[0]) {
2299 log("Starting create phase for rules");
2300
2301 for (let rule of this.rules) {
2302 try {
2303 await rule.saveA(env);
2304 } catch (e) {
2305 log(e);
2306 }
2307 }
2308
2309 log("OK");
2310 log("Starting link phase for rules");
2311 Rule.removeCache(env);
2312
2313 for (let rule of this.rules) {
2314 try {
2315 await rule.saveB(env);
2316 } catch (e) {
2317 log(e);
2318 }
2319 }
2320 }
2321 }
2322
2323 }
2324
2325 class User extends RallyBase {
2326 constructor({
2327 data,
2328 remote
2329 }) {
2330 super();
2331 this.data = data;
2332 this.meta = {};
2333 this.remote = remote;
2334 }
2335
2336 chalkPrint(pad = false) {
2337 let id = String("U-" + this.id);
2338 if (pad) id = id.padStart(7);
2339 return chalk`{green ${id}}: {blue ${this.name}}`;
2340 }
2341
2342 }
2343
2344 defineAssoc(User, "id", "data.id");
2345 defineAssoc(User, "name", "data.attributes.name");
2346 defineAssoc(User, "email", "data.attributes.email");
2347 defineAssoc(User, "remote", "meta.remote");
2348 User.endpoint = "users";
2349
2350 class Tag extends RallyBase {
2351 constructor({
2352 data,
2353 remote
2354 } = {}) {
2355 super();
2356 this.meta = {};
2357 this.remote = remote;
2358 this.data = data; //this.data.attributes.rallyConfiguration = undefined;
2359 //this.data.attributes.systemManaged = undefined;
2360 }
2361
2362 chalkPrint(pad = true) {
2363 let id = String("T-" + this.remote + "-" + this.id);
2364 if (pad) id = id.padStart(10);
2365 let prefix = this.curated ? "blue +" : "red -";
2366 return chalk`{green ${id}}: {${prefix}${this.name}}`;
2367 }
2368
2369 static async create(env, name, {
2370 notCurated
2371 } = {}) {
2372 return new Tag({
2373 data: await lib.makeAPIRequest({
2374 env,
2375 path: `/${this.endpoint}`,
2376 method: "POST",
2377 payload: {
2378 data: {
2379 attributes: {
2380 name,
2381 curated: notCurated ? false : true
2382 },
2383 type: "tagNames"
2384 }
2385 }
2386 }),
2387 remote: env
2388 });
2389 }
2390
2391 }
2392
2393 defineAssoc(Tag, "id", "data.id");
2394 defineAssoc(Tag, "attributes", "data.attributes");
2395 defineAssoc(Tag, "relationships", "data.relationships");
2396 defineAssoc(Tag, "name", "data.attributes.name");
2397 defineAssoc(Tag, "curated", "data.attributes.curated");
2398 defineAssoc(Tag, "remote", "meta.remote");
2399 Tag.endpoint = "tagNames";
2400
2401 async function findLineInFile(renderedPreset, lineNumber) {
2402 let trueFileLine = lineNumber;
2403 let linedRenderedPreset = renderedPreset.split("\n").slice(2, -2);
2404 renderedPreset = renderedPreset.split("\n").slice(2, -2).join("\n");
2405 let includeLocation = renderedPreset.split("\n").filter(x => x.includes("@include"));
2406 let endIncludeNumber = -1,
2407 addTabDepth = 2;
2408 let lineBeforeIncludeStatement = '';
2409 let withinInclude = true;
2410
2411 if (lineNumber > linedRenderedPreset.indexOf(includeLocation[includeLocation.length - 1])) {
2412 addTabDepth = 0;
2413 withinInclude = false;
2414 }
2415
2416 for (let index = includeLocation.length - 1; index >= 0; index--) {
2417 let currIncludeIndex = linedRenderedPreset.indexOf(includeLocation[index]);
2418 let tabDepth = includeLocation[index].split(" ").length;
2419
2420 if (lineNumber > currIncludeIndex) {
2421 if (includeLocation[index].split(" ").filter(Boolean)[1] != "ERROR:") {
2422 if (lineBeforeIncludeStatement.split(" ").length == tabDepth && withinInclude) {
2423 trueFileLine = trueFileLine - currIncludeIndex;
2424 break;
2425 } else if (lineBeforeIncludeStatement.split(" ").length + addTabDepth == tabDepth && endIncludeNumber == -1) {
2426 endIncludeNumber = currIncludeIndex;
2427 } else if (lineBeforeIncludeStatement.split(" ").length + addTabDepth == tabDepth) {
2428 trueFileLine = trueFileLine - (endIncludeNumber - currIncludeIndex);
2429 endIncludeNumber = -1;
2430 }
2431 }
2432 } else {
2433 lineBeforeIncludeStatement = includeLocation[index];
2434 }
2435 }
2436
2437 let funcLine = "";
2438
2439 for (let line of linedRenderedPreset.slice(0, lineNumber).reverse()) {
2440 let match = /def (\w+)/.exec(line);
2441
2442 if (match) {
2443 funcLine = match[1];
2444 break;
2445 }
2446 }
2447
2448 let includeFilename;
2449
2450 if (lineBeforeIncludeStatement != "") {
2451 includeFilename = lineBeforeIncludeStatement.slice(1).trim().slice(14, -1);
2452 } else {
2453 includeFilename = null;
2454 }
2455
2456 if (includeLocation.length !== 0) {
2457 trueFileLine -= 1;
2458 lineNumber -= 1;
2459 }
2460
2461 return {
2462 lineNumber: trueFileLine,
2463 includeFilename,
2464 line: linedRenderedPreset[lineNumber],
2465 funcLine
2466 };
2467 }
2468 function printOutLine(eLine) {
2469 return log(chalk`{blue ${eLine.includeFilename || "Main"}}:{green ${eLine.lineNumber}} in ${eLine.funcLine}
2470${eLine.line}`);
2471 }
2472 async function getInfo(env, jobid) {
2473 log(env, jobid);
2474 let trace = lib.makeAPIRequest({
2475 env,
2476 path: `/jobs/${jobid}/artifacts/trace`
2477 }).catch(x => null);
2478 let renderedPreset = lib.makeAPIRequest({
2479 env,
2480 path: `/jobs/${jobid}/artifacts/preset`
2481 }).catch(x => null);
2482 let result = lib.makeAPIRequest({
2483 env,
2484 path: `/jobs/${jobid}/artifacts/result`
2485 }).catch(x => null);
2486 let error = lib.makeAPIRequest({
2487 env,
2488 path: `/jobs/${jobid}/artifacts/error`
2489 }).catch(x => null);
2490 let output = lib.makeAPIRequest({
2491 env,
2492 path: `/jobs/${jobid}/artifacts/output`
2493 }).catch(x => null);
2494 [trace, renderedPreset, result, output, error] = await Promise.all([trace, renderedPreset, result, output, error]);
2495 return {
2496 trace,
2497 renderedPreset,
2498 result,
2499 output,
2500 error
2501 };
2502 }
2503 async function parseTrace(env, jobid) {
2504 let {
2505 trace,
2506 renderedPreset
2507 } = await getInfo(env, jobid);
2508 let lineNumber = -1;
2509 let errorLines = [];
2510 let shouldBreak = 0;
2511
2512 for (let tr of trace.split("\n\n").reverse()) {
2513 errorLines.push(tr);
2514 shouldBreak--;
2515 if (tr.includes("Exception")) shouldBreak = 1;
2516 if (tr.includes("raised")) shouldBreak = 1;
2517 if (!shouldBreak) break;
2518 }
2519
2520 let errorList = [];
2521
2522 for (let errLine of errorLines) {
2523 lineNumber = /^[\w ]+:(\d+):/g.exec(errLine);
2524
2525 if (lineNumber && lineNumber[1]) {
2526 errorList.push((await findLineInFile(renderedPreset, lineNumber[1])));
2527 } else {
2528 errorList.push(errLine);
2529 }
2530 }
2531
2532 return errorList;
2533 }
2534 const Trace = {
2535 parseTrace,
2536 printOutLine,
2537 getInfo,
2538 findLineInFile
2539 };
2540
2541 require("source-map-support").install();
2542 const rallyFunctions = {
2543 async bestPagintation() {
2544 global.silentAPI = true;
2545
2546 for (let i = 10; i <= 30; i += 5) {
2547 console.time("test with " + i);
2548 let dl = await lib.indexPathFast("DEV", `/workflowRules?page=1p${i}`);
2549 console.timeEnd("test with " + i);
2550 }
2551 },
2552
2553 async uploadPresets(env, presets, createFunc = () => false) {
2554 for (let preset of presets) {
2555 await preset.uploadCodeToEnv(env, createFunc);
2556 }
2557 },
2558
2559 //Dummy test access
2560 async testAccess(env) {
2561 if (lib.isLocalEnv(env)) {
2562 //TODO
2563 return true;
2564 }
2565
2566 let result = await lib.makeAPIRequest({
2567 env,
2568 path: "/providers?page=1p1",
2569 fullResponse: true,
2570 timeout: 1000
2571 });
2572 return result.statusCode;
2573 }
2574
2575 };
2576
2577 exports.APIError = APIError;
2578 exports.AbortError = AbortError;
2579 exports.Asset = Asset;
2580 exports.Collection = Collection;
2581 exports.FileTooLargeError = FileTooLargeError;
2582 exports.Notification = Notification;
2583 exports.Preset = Preset;
2584 exports.ProtectedEnvError = ProtectedEnvError;
2585 exports.Provider = Provider;
2586 exports.RallyBase = RallyBase;
2587 exports.Rule = Rule;
2588 exports.SupplyChain = SupplyChain;
2589 exports.Tag = Tag;
2590 exports.Trace = Trace;
2591 exports.UnconfiguredEnvError = UnconfiguredEnvError;
2592 exports.User = User;
2593 exports.lib = lib;
2594 exports.loadConfig = loadConfig;
2595 exports.loadConfigFromArgs = loadConfigFromArgs;
2596 exports.rallyFunctions = rallyFunctions;
2597 exports.setConfig = setConfig;
2598 exports.sleep = sleep;
2599
2600 Object.defineProperty(exports, '__esModule', { value: true });
2601
2602})));
2603//# sourceMappingURL=web.js.map