UNPKG

81.5 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.error(...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$1, Class) {
708 let basePath = exports.configObject.repodir;
709 let objs = (await readdir(basePath)).filter(name => name.includes(path$1)).filter(name => !path.basename(name).startsWith(".")).map(name => new Class({
710 path: name
711 }));
712 return objs;
713 }
714
715 class Provider extends RallyBase {
716 constructor({
717 data,
718 remote
719 }) {
720 super();
721 this.data = data;
722 this.meta = {};
723 this.remote = remote;
724 } //cached
725
726
727 async getEditorConfig() {
728 if (this.editorConfig) return this.editorConfig;
729 this.editorConfig = await lib.makeAPIRequest({
730 env: this.remote,
731 path_full: this.data.links.editorConfig
732 });
733 this.editorConfig.fileExt = await this.getFileExtension();
734 return this.editorConfig;
735 }
736
737 static async getAllPreCollect(providers) {
738 return providers.sort((a, b) => {
739 return a.attributes.category.localeCompare(b.attributes.category) || a.attributes.name.localeCompare(b.attributes.name);
740 });
741 }
742
743 async getFileExtension() {
744 let config = await this.getEditorConfig();
745 let map = {
746 python: "py",
747 text: "txt",
748
749 getmap(key) {
750 if (this.name === "Aurora") return "zip";
751 if (this[key]) return this[key];
752 return key;
753 }
754
755 };
756 return map.getmap(config.lang);
757 }
758
759 chalkPrint(pad = true) {
760 let id = String(this.id);
761 if (pad) id = id.padStart(4);
762 return chalk`{green ${id}}: {blue ${this.category}} - {green ${this.name}}`;
763 }
764
765 }
766
767 defineAssoc(Provider, "id", "data.id");
768 defineAssoc(Provider, "name", "data.attributes.name");
769 defineAssoc(Provider, "category", "data.attributes.category");
770 defineAssoc(Provider, "remote", "meta.remote");
771 defineAssoc(Provider, "editorConfig", "meta.editorConfig");
772 Provider.endpoint = "providerTypes";
773
774 class File extends RallyBase {
775 constructor({
776 data,
777 remote,
778 included,
779 parent
780 }) {
781 super();
782 this.data = data;
783 this.meta = {};
784 this.remote = remote;
785 this.parentAsset = parent;
786 }
787
788 chalkPrint(pad = false) {
789 let id = String("F-" + (this.remote && this.remote + "-" + this.id || "LOCAL"));
790 if (pad) id = id.padStart(15);
791 return chalk`{green ${id}}: {blue ${this.data.attributes ? this.name : "(lite file)"}} {red ${this.sizeHR}}`;
792 }
793
794 canBeDownloaded() {
795 return this.sizeGB <= .2;
796 }
797
798 async getContent(force = false) {
799 if (!this.canBeDownloaded() && !force) {
800 throw new FileTooLargeError(this);
801 }
802
803 return lib.makeAPIRequest({
804 env: this.remote,
805 fullPath: this.contentLink
806 });
807 }
808
809 async delete(remove = true) {
810 return lib.makeAPIRequest({
811 env: this.remote,
812 fullPath: this.selfLink,
813 method: "DELETE"
814 });
815 }
816
817 get size() {
818 return Object.values(this.data.attributes.instances)[0].size;
819 }
820
821 get sizeGB() {
822 return Math.round(this.size / 1024 / 1024 / 1024 * 10) / 10;
823 }
824
825 get sizeHR() {
826 let units = ["B", "K", "M", "G", "T"];
827 let unitIdx = 0;
828 let size = this.size;
829
830 while (size > 1000) {
831 size /= 1024;
832 unitIdx++;
833 }
834
835 if (size > 100) {
836 size = Math.round(size);
837 } else {
838 size = Math.round(size * 10) / 10;
839 }
840
841 return size + units[unitIdx];
842 }
843
844 get instancesList() {
845 let instances = [];
846
847 for (let [key, val] of Object.entries(this.instances)) {
848 let n = {
849 id: key
850 };
851 Object.assign(n, val);
852 instances.push(n);
853 }
854
855 return instances;
856 }
857
858 static rslURL(instance) {
859 return `rsl://${instance.storageLocationName}/${instance.name}`;
860 }
861
862 }
863
864 defineAssoc(File, "id", "data.id");
865 defineAssoc(File, "name", "data.attributes.label");
866 defineAssoc(File, "contentLink", "data.links.content");
867 defineAssoc(File, "selfLink", "data.links.self");
868 defineAssoc(File, "label", "data.attributes.label");
869 defineAssoc(File, "md5", "data.attributes.md5");
870 defineAssoc(File, "sha512", "data.attributes.sha512");
871 defineAssoc(File, "tags", "data.attributes.tagList");
872 defineAssoc(File, "instances", "data.attributes.instances");
873 File.endpoint = null;
874
875 class Asset extends RallyBase {
876 constructor({
877 data,
878 remote,
879 included,
880 lite
881 }) {
882 super();
883 this.data = data;
884 this.meta = {};
885 this.remote = remote;
886
887 if (included) {
888 this.meta.metadata = Asset.normalizeMetadata(included);
889 }
890
891 this.lite = !!lite;
892 }
893
894 static normalizeMetadata(payload) {
895 let newMetadata = {};
896
897 for (let md of payload) {
898 if (md.type !== "metadata") continue;
899 newMetadata[md.attributes.usage] = md.attributes.metadata;
900 }
901
902 return newMetadata;
903 }
904
905 async getMetadata(forceRefresh = false) {
906 if (this.meta.metadata && !forceRefresh) return this.meta.metadata;
907 let req = await lib.makeAPIRequest({
908 env: this.remote,
909 path: `/movies/${this.id}/metadata`
910 });
911 return this.meta.metadata = Asset.normalizeMetadata(req.data);
912 }
913
914 async patchMetadata(metadata) {
915 if (metadata.Workflow && false) {
916 let req = await lib.makeAPIRequest({
917 env: this.remote,
918 path: `/movies/${this.id}/metadata/Workflow`,
919 method: "PATCH",
920 payload: {
921 "data": {
922 "type": "metadata",
923 "attributes": {
924 "metadata": metadata.Workflow
925 }
926 }
927 }
928 });
929 }
930
931 if (metadata.Metadata) {
932 let req = await lib.makeAPIRequest({
933 env: this.remote,
934 path: `/movies/${this.id}/metadata/Metadata`,
935 method: "PATCH",
936 payload: {
937 "data": {
938 "type": "metadata",
939 "attributes": {
940 "metadata": metadata.Metadata
941 }
942 }
943 }
944 });
945 }
946 }
947
948 static lite(id, remote) {
949 return new this({
950 data: {
951 id
952 },
953 remote,
954 lite: true
955 });
956 }
957
958 chalkPrint(pad = false) {
959 let id = String("A-" + (this.remote && this.remote + "-" + this.id || "LOCAL"));
960 if (pad) id = id.padStart(15);
961 return chalk`{green ${id}}: {blue ${this.data.attributes ? this.name : "(lite asset)"}}`;
962 }
963
964 static async createNew(name, env) {
965 let req = await lib.makeAPIRequest({
966 env,
967 path: "/assets",
968 method: "POST",
969 payload: {
970 data: {
971 attributes: {
972 name
973 },
974 type: "assets"
975 }
976 }
977 });
978 return new this({
979 data: req.data,
980 remote: env
981 });
982 }
983
984 async delete() {
985 let req = await lib.makeAPIRequest({
986 env: this.remote,
987 path: "/assets/" + this.id,
988 method: "DELETE"
989 });
990 }
991
992 async getFiles() {
993 let req = await lib.indexPathFast({
994 env: this.remote,
995 path: `/assets/${this.id}/files`,
996 method: "GET"
997 }); //return req;
998
999 return new Collection(req.map(x => new File({
1000 data: x,
1001 remote: this.remote,
1002 parent: this
1003 })));
1004 }
1005
1006 async addFile(label, fileuris) {
1007 if (!Array.isArray(fileuris)) fileuris = [fileuris];
1008 let instances = {};
1009
1010 for (let i = 0; i < fileuris.length; i++) {
1011 instances[String(i + 1)] = {
1012 uri: fileuris[i]
1013 };
1014 }
1015
1016 let req = await lib.makeAPIRequest({
1017 env: this.remote,
1018 path: "/files",
1019 method: "POST",
1020 payload: {
1021 "data": {
1022 "attributes": {
1023 label,
1024 instances
1025 },
1026 "relationships": {
1027 "asset": {
1028 "data": {
1029 id: this.id,
1030 "type": "assets"
1031 }
1032 }
1033 },
1034 "type": "files"
1035 }
1036 }
1037 });
1038 return req;
1039 }
1040
1041 async startWorkflow(jobName, {
1042 initData,
1043 priority
1044 } = {}) {
1045 let attributes = {};
1046
1047 if (initData) {
1048 //Convert init data to string
1049 initData = typeof initData === "string" ? initData : JSON.stringify(initData);
1050 attributes.initData = initData;
1051 }
1052
1053 if (priority) {
1054 attributes.priority = priority;
1055 }
1056
1057 let req = await lib.makeAPIRequest({
1058 env: this.remote,
1059 path: "/workflows",
1060 method: "POST",
1061 payload: {
1062 "data": {
1063 "type": "workflows",
1064 attributes,
1065 "relationships": {
1066 "movie": {
1067 "data": {
1068 id: this.id,
1069 "type": "movies"
1070 }
1071 },
1072 "rule": {
1073 "data": {
1074 "attributes": {
1075 "name": jobName
1076 },
1077 "type": "rules"
1078 }
1079 }
1080 }
1081 }
1082 }
1083 });
1084 return req;
1085 }
1086
1087 static async startAnonWorkflow(env, jobName, {
1088 initData,
1089 priority
1090 } = {}) {
1091 let attributes = {};
1092
1093 if (initData) {
1094 //Convert init data to string
1095 initData = typeof initData === "string" ? initData : JSON.stringify(initData);
1096 attributes.initData = initData;
1097 }
1098
1099 if (priority) {
1100 attributes.priority = priority;
1101 }
1102
1103 let req = await lib.makeAPIRequest({
1104 env,
1105 path: "/workflows",
1106 method: "POST",
1107 payload: {
1108 "data": {
1109 "type": "workflows",
1110 attributes,
1111 "relationships": {
1112 "rule": {
1113 "data": {
1114 "attributes": {
1115 "name": jobName
1116 },
1117 "type": "rules"
1118 }
1119 }
1120 }
1121 }
1122 }
1123 });
1124 return req;
1125 }
1126
1127 async startEphemeralEvaluateIdeal(preset, dynamicPresetData) {
1128 let res;
1129 const env = this.remote;
1130 let provider = await Provider.getByName(this.remote, "SdviEvaluate");
1131 write(chalk`Starting ephemeral evaluate on ${this.chalkPrint(false)}...`); // Fire and forget.
1132
1133 let evalInfo = await lib.makeAPIRequest({
1134 env: this.remote,
1135 path: "/jobs",
1136 method: "POST",
1137 payload: {
1138 data: {
1139 attributes: {
1140 category: provider.category,
1141 providerTypeName: provider.name,
1142 rallyConfiguration: {},
1143 providerData: Buffer.from(preset.code, "binary").toString("base64"),
1144 dynamicPresetData
1145 },
1146 type: "jobs",
1147 relationships: {
1148 movie: {
1149 data: {
1150 id: this.id,
1151 type: "movies"
1152 }
1153 }
1154 }
1155 }
1156 }
1157 });
1158 write(" Waiting for finish...");
1159
1160 for (;;) {
1161 res = await lib.makeAPIRequest({
1162 env,
1163 path_full: evalInfo.data.links.self
1164 });
1165 write(".");
1166
1167 if (res.data.attributes.state == "Complete") {
1168 write(chalk`{green Done}...\n`);
1169 break;
1170 }
1171
1172 await sleep(300);
1173 }
1174
1175 return;
1176 }
1177
1178 async startEvaluate(presetid, dynamicPresetData) {
1179 // Fire and forget.
1180 let data = await lib.makeAPIRequest({
1181 env: this.remote,
1182 path: "/jobs",
1183 method: "POST",
1184 payload: {
1185 data: {
1186 type: "jobs",
1187 attributes: {
1188 dynamicPresetData
1189 },
1190 relationships: {
1191 movie: {
1192 data: {
1193 id: this.id,
1194 type: "movies"
1195 }
1196 },
1197 preset: {
1198 data: {
1199 id: presetid,
1200 type: "presets"
1201 }
1202 }
1203 }
1204 }
1205 }
1206 });
1207 return data;
1208 }
1209
1210 async rename(newName) {
1211 let req = await lib.makeAPIRequest({
1212 env: this.remote,
1213 path: `/assets/${this.id}`,
1214 method: "PATCH",
1215 payload: {
1216 data: {
1217 attributes: {
1218 name: newName
1219 },
1220 type: "assets"
1221 }
1222 }
1223 });
1224 this.name = newName;
1225 return req;
1226 }
1227
1228 async migrate(targetEnv) {
1229 exports.configObject.globalProgress = false;
1230 log(`Creating paired file in ${targetEnv}`); //Fetch metadata in parallel, we await it later
1231
1232 let _mdPromise = this.getMetadata();
1233
1234 let targetAsset = await Asset.getByName(targetEnv, this.name);
1235
1236 if (targetAsset) {
1237 log(`Asset already exists ${targetAsset.chalkPrint()}`); //if(configObject.script) process.exit(10);
1238 } else {
1239 targetAsset = await Asset.createNew(this.name, targetEnv);
1240 log(`Asset created ${targetAsset.chalkPrint()}`);
1241 } //wait for metadata to be ready before patching
1242
1243
1244 await _mdPromise;
1245 log("Adding asset metadata");
1246 await targetAsset.patchMetadata(this.md); //FIXME
1247 //Currently, WORKFLOW_METADATA cannot be patched via api: we need to
1248 //start a ephemeral eval to upload it
1249
1250 log("Adding asset workflow metadata");
1251 let md = JSON.stringify(JSON.stringify(this.md.Workflow));
1252 let fakePreset = {
1253 code: `WORKFLOW_METADATA = json.loads(${md})`
1254 };
1255 await targetAsset.startEphemeralEvaluateIdeal(fakePreset);
1256 let fileCreations = [];
1257
1258 for (let file of await this.getFiles()) {
1259 //Check for any valid copy-able instances
1260 for (let inst of file.instancesList) {
1261 //We need to skip internal files
1262 if (inst.storageLocationName === "Rally Platform Bucket") continue;
1263 log(`Adding file: ${file.chalkPrint()}`);
1264 fileCreations.push(targetAsset.addFile(file, inst));
1265 }
1266 }
1267
1268 await Promise.all(fileCreations);
1269 if (exports.configObject.script) console.log(this.name);
1270 }
1271
1272 async addFile(file, inst, tagList = []) {
1273 let newInst = {
1274 uri: File.rslURL(inst),
1275 name: inst.name,
1276 size: inst.size,
1277 lastModified: inst.lastModified,
1278 storageLocationName: inst.storageLocationName
1279 };
1280 let request = lib.makeAPIRequest({
1281 env: this.remote,
1282 path: `/files`,
1283 method: "POST",
1284 payload: {
1285 data: {
1286 type: "files",
1287 attributes: {
1288 label: file.label,
1289 tagList,
1290 instances: {
1291 "1": newInst
1292 }
1293 },
1294 relationships: {
1295 asset: {
1296 data: {
1297 id: this.id,
1298 type: "assets"
1299 }
1300 }
1301 }
1302 }
1303 }
1304 });
1305
1306 try {
1307 await request;
1308 } catch (e) {
1309 log(chalk`{red Failed file: ${file.chalkPrint()}}`);
1310 }
1311 }
1312
1313 }
1314
1315 defineAssoc(Asset, "id", "data.id");
1316 defineAssoc(Asset, "name", "data.attributes.name");
1317 defineAssoc(Asset, "remote", "meta.remote");
1318 defineAssoc(Asset, "md", "meta.metadata");
1319 defineAssoc(Asset, "lite", "meta.lite");
1320 Asset.endpoint = "movies";
1321
1322 let home;
1323
1324 if (os.homedir) {
1325 home = os.homedir();
1326 }
1327
1328 const colon = /:/g;
1329 const siloLike = /(silo\-\w+?)s?\/([^\/]+)\.([\w1234567890]+)$/g;
1330 function pathTransform(path) {
1331 if (path.includes(":")) {
1332 //Ignore the first colon in window-like filesystems
1333 path = path.slice(0, 3) + path.slice(3).replace(colon, "--");
1334 }
1335
1336 if (exports.configObject.invertedPath) {
1337 path = path.replace(siloLike, "$2-$1.$3");
1338 }
1339
1340 if (path.includes("\\342\\200\\220")) {
1341 path = path.replace("\\342\\200\\220", "‐");
1342 }
1343
1344 return path;
1345 }
1346 function readFileSync(path, options) {
1347 return fs__default.readFileSync(pathTransform(path), options);
1348 } //Create writefilesync, with ability to create directory if it doesnt exist
1349
1350 function writeFileSync(path$1, data, options, dircreated = false) {
1351 path$1 = pathTransform(path$1);
1352
1353 try {
1354 return fs__default.writeFileSync(path$1, data, options);
1355 } catch (e) {
1356 if (dircreated) throw e;
1357 let directory = path.dirname(path$1);
1358
1359 try {
1360 fs__default.statSync(directory);
1361 throw e;
1362 } catch (nodir) {
1363 fs__default.mkdirSync(directory);
1364 return writeFileSync(path$1, data, options, true);
1365 }
1366 }
1367 }
1368
1369 let exists = {};
1370
1371 class Preset extends RallyBase {
1372 constructor({
1373 path: path$1,
1374 remote,
1375 data,
1376 subProject
1377 } = {}) {
1378 // Get full path if possible
1379 if (path$1) {
1380 path$1 = path.resolve(path$1);
1381
1382 if (path.dirname(path$1).includes("silo-metadata")) {
1383 throw new AbortError("Constructing preset from metadata file");
1384 }
1385 }
1386
1387 super(); // Cache by path
1388
1389 if (path$1) {
1390 if (exists[pathTransform(path$1)]) return exists[pathTransform(path$1)];
1391 exists[pathTransform(path$1)] = this;
1392 }
1393
1394 this.meta = {};
1395 this.subproject = subProject;
1396 this.remote = remote;
1397
1398 if (lib.isLocalEnv(this.remote)) {
1399 if (path$1) {
1400 this.path = path$1;
1401 let pathspl = this.path.split(".");
1402 this.ext = pathspl[pathspl.length - 1];
1403
1404 try {
1405 this.code = this.getLocalCode();
1406 } catch (e) {
1407 if (e.code === "ENOENT" && exports.configObject.ignoreMissing) {
1408 this.missing = true;
1409 return undefined;
1410 } else {
1411 log(chalk`{red Node Error} ${e.message}`);
1412 throw new AbortError("Could not load code of local file");
1413 }
1414 }
1415
1416 let name = this.parseFilenameForName() || this.parseCodeForName();
1417
1418 try {
1419 this.data = this.getLocalMetadata();
1420 this.isGeneric = true;
1421 name = this.name;
1422 } catch (e) {
1423 log(chalk`{yellow Warning}: ${path$1} does not have a readable metadata file! Looking for ${this.localmetadatapath}`);
1424 this.data = Preset.newShell(name);
1425 this.isGeneric = false;
1426 }
1427
1428 this.name = name;
1429 } else {
1430 this.data = Preset.newShell();
1431 }
1432 } else {
1433 this.data = data; //this.name = data.attributes.name;
1434 //this.id = data.id;
1435
1436 this.isGeneric = false;
1437 }
1438
1439 this.data.attributes.rallyConfiguration = undefined;
1440 this.data.attributes.systemManaged = undefined;
1441 } //Given a metadata file, get its actualy file
1442
1443
1444 static async fromMetadata(path, subproject) {
1445 let data;
1446
1447 try {
1448 data = JSON.parse(readFileSync(path));
1449 } catch (e) {
1450 if (e.code === "ENOENT" && exports.configObject.ignoreMissing) {
1451 return null;
1452 } else {
1453 throw e;
1454 }
1455 }
1456
1457 let providerType = data.relationships.providerType.data.name;
1458 let provider = await Provider.getByName("DEV", providerType);
1459
1460 if (!provider) {
1461 log(chalk`{red The provider type {green ${providerType}} does not exist}`);
1462 log(chalk`{red Skipping {green ${path}}.}`);
1463 return null;
1464 }
1465
1466 let ext = await provider.getFileExtension();
1467 let name = data.attributes.name;
1468 let realpath = Preset.getLocalPath(name, ext, subproject);
1469 return new Preset({
1470 path: realpath,
1471 subProject: subproject
1472 });
1473 }
1474
1475 static newShell(name = undefined) {
1476 return {
1477 "attributes": {
1478 "providerSettings": {
1479 "PresetName": name
1480 }
1481 },
1482 "relationships": {},
1483 "type": "presets"
1484 };
1485 }
1486
1487 cleanup() {
1488 super.cleanup();
1489 delete this.attributes["createdAt"];
1490 delete this.attributes["updatedAt"];
1491 }
1492
1493 async acclimatize(env) {
1494 if (!this.isGeneric) throw new AbortError("Cannot acclimatize non-generics or shells");
1495 let providers = await Provider.getAll(env);
1496 let ptype = this.relationships["providerType"];
1497 ptype = ptype.data;
1498 let provider = providers.findByName(ptype.name);
1499 ptype.id = provider.id;
1500 }
1501
1502 get test() {
1503 if (!this.code) return [];
1504 const regex = /[^-]autotest:\s?([\w\d_\-. \/]+)[\r\s\n]*?/gm;
1505 let match;
1506 let matches = [];
1507
1508 while (match = regex.exec(this.code)) {
1509 matches.push(match[1]);
1510 }
1511
1512 return matches;
1513 }
1514
1515 async runTest(env) {
1516 let remote = await Preset.getByName(env, this.name);
1517
1518 for (let test of this.test) {
1519 log("Tests...");
1520 let asset;
1521
1522 if (test.startsWith("id")) {
1523 let match = /id:\s*(\d+)/g.exec(test);
1524
1525 if (!match) {
1526 log(chalk`{red Could not parse autotest} ${test}.`);
1527 throw new AbortError("Could not properly parse the preset header");
1528 }
1529
1530 asset = await Asset.getById(env, match[1]);
1531 } else {
1532 asset = await Asset.getByName(env, test);
1533 }
1534
1535 if (!asset) {
1536 log(chalk`{yellow No movie found}, skipping test.`);
1537 continue;
1538 }
1539
1540 log(chalk`Starting job {green ${this.name}} on ${asset.chalkPrint(false)}... `);
1541 await asset.startEvaluate(remote.id);
1542 }
1543 }
1544
1545 async resolve() {
1546 if (this.isGeneric) return;
1547 let proType = await this.resolveField(Provider, "providerType");
1548 this.ext = await proType.getFileExtension();
1549 this.isGeneric = true;
1550 return {
1551 proType
1552 };
1553 }
1554
1555 async saveLocal() {
1556 await this.saveLocalMetadata();
1557 await this.saveLocalFile();
1558 }
1559
1560 async saveLocalMetadata() {
1561 if (!this.isGeneric) {
1562 await this.resolve();
1563 this.cleanup();
1564 }
1565
1566 writeFileSync(this.localmetadatapath, JSON.stringify(this.data, null, 4));
1567 }
1568
1569 async saveLocalFile() {
1570 writeFileSync(this.localpath, this.code);
1571 }
1572
1573 async uploadRemote(env) {
1574 await this.uploadCodeToEnv(env, true);
1575 }
1576
1577 async save(env) {
1578 this.saved = true;
1579
1580 if (!this.isGeneric) {
1581 await this.resolve();
1582 }
1583
1584 this.cleanup();
1585
1586 if (lib.isLocalEnv(env)) {
1587 log(chalk`Saving preset {green ${this.name}} to {blue ${lib.envName(env)}}.`);
1588 await this.saveLocal();
1589 } else {
1590 await this.uploadRemote(env);
1591 }
1592 }
1593
1594 async downloadCode() {
1595 if (!this.remote || this.code) return this.code;
1596 let code = await lib.makeAPIRequest({
1597 env: this.remote,
1598 path_full: this.data.links.providerData,
1599 json: false
1600 }); //match header like
1601 // # c: d
1602 // # b
1603 // # a
1604 // ##################
1605
1606 let headerRegex = /(^# .+[\r\n]+)+#+[\r\n]+/gim;
1607 let hasHeader = headerRegex.exec(code);
1608
1609 if (hasHeader) {
1610 this.header = code.substring(0, hasHeader[0].length - 1);
1611 code = code.substring(hasHeader[0].length);
1612 }
1613
1614 return this.code = code;
1615 }
1616
1617 get code() {
1618 if (this._code) return this._code;
1619 }
1620
1621 set code(v) {
1622 this._code = v;
1623 }
1624
1625 chalkPrint(pad = true) {
1626 let id = String("P-" + (this.remote && this.remote + "-" + this.id || "LOCAL"));
1627 let sub = "";
1628
1629 if (this.subproject) {
1630 sub = chalk`{yellow ${this.subproject}}`;
1631 }
1632
1633 if (pad) id = id.padStart(10);
1634
1635 if (this.name == undefined) {
1636 return chalk`{green ${id}}: ${sub}{red ${this.path}}`;
1637 } else if (this.meta.proType) {
1638 return chalk`{green ${id}}: ${sub}{red ${this.meta.proType.name}} {blue ${this.name}}`;
1639 } else {
1640 return chalk`{green ${id}}: ${sub}{blue ${this.name}}`;
1641 }
1642 }
1643
1644 parseFilenameForName() {
1645 if (this.path.endsWith(".jinja") || this.path.endsWith(".json")) {
1646 return path.basename(this.path).replace("_", " ").replace("-", " ").replace(".json", "").replace(".jinja", "");
1647 }
1648 }
1649
1650 parseCodeForName() {
1651 const name_regex = /name\s?:\s([\w\d. \/]+)[\r\s\n]*?/;
1652 const match = name_regex.exec(this.code);
1653 if (match) return match[1];
1654 }
1655
1656 findStringsInCode(strings) {
1657 if (!this.code) return [];
1658 return strings.filter(str => {
1659 let regex = new RegExp(str);
1660 return !!this.code.match(regex);
1661 });
1662 }
1663
1664 static getLocalPath(name, ext, subproject) {
1665 return path__default.join(exports.configObject.repodir, subproject || "", "silo-presets", name + "." + ext);
1666 }
1667
1668 get localpath() {
1669 return Preset.getLocalPath(this.name, this.ext, this.subproject);
1670 }
1671
1672 get path() {
1673 if (this._path) return this._path;
1674 }
1675
1676 set path(val) {
1677 this._path = val;
1678 }
1679
1680 get name() {
1681 return this._nameOuter;
1682 }
1683
1684 set name(val) {
1685 if (!this._nameInner) this._nameInner = val;
1686 this._nameOuter = val;
1687 }
1688
1689 set providerType(value) {
1690 this.relationships["providerType"] = {
1691 data: { ...value,
1692 type: "providerTypes"
1693 }
1694 };
1695 }
1696
1697 get localmetadatapath() {
1698 if (this.path) {
1699 return this.path.replace("silo-presets", "silo-metadata").replace(new RegExp(this.ext + "$"), "json");
1700 }
1701
1702 return path__default.join(exports.configObject.repodir, this.subproject || "", "silo-metadata", this.name + ".json");
1703 }
1704
1705 get immutable() {
1706 return this.name.includes("Constant") && !exports.configObject.updateImmutable;
1707 }
1708
1709 async uploadPresetData(env, id) {
1710 var _this$relationships, _this$relationships$p, _this$relationships$p2;
1711
1712 if (this.code.trim() === "NOUPLOAD") {
1713 write(chalk`code skipped {yellow :)}, `);
1714 return;
1715 }
1716
1717 let code = this.code;
1718 let headers = {};
1719 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;
1720
1721 if (!exports.configObject.skipHeader && (providerName === "SdviEvaluate" || providerName === "SdviEvalPro")) {
1722 write(chalk`generate header, `);
1723 let repodir = exports.configObject.repodir;
1724 let localpath = this.path.replace(repodir, "");
1725 if (localpath.startsWith("/")) localpath = localpath.substring(1);
1726
1727 try {
1728 let {
1729 stdout: headerText
1730 } = await spawn({
1731 noecho: true
1732 }, "sh", [path__default.join(exports.configObject.repodir, `bin/header.sh`), moment(Date.now()).format("ddd YYYY/MM/DD hh:mm:ssa"), localpath]);
1733 code = headerText + code;
1734 write(chalk`header ok, `);
1735 } catch (e) {
1736 write(chalk`missing unix, `);
1737 }
1738 } //binary presets
1739
1740
1741 if (providerName == "Vantage") {
1742 code = code.toString("base64");
1743 headers["Content-Transfer-Encoding"] = "base64";
1744 }
1745
1746 let res = await lib.makeAPIRequest({
1747 env,
1748 path: `/presets/${id}/providerData`,
1749 body: code,
1750 method: "PUT",
1751 fullResponse: true,
1752 timeout: 10000,
1753 headers
1754 });
1755 write(chalk`code up {yellow ${res.statusCode}}, `);
1756 }
1757
1758 async grabMetadata(env) {
1759 let remote = await Preset.getByName(env, this.name);
1760 this.isGeneric = false;
1761
1762 if (!remote) {
1763 throw new AbortError(`No file found on remote ${env} with name ${this.name}`);
1764 }
1765
1766 this.data = remote.data;
1767 this.remote = env;
1768 }
1769
1770 async deleteRemoteVersion(env, id = null) {
1771 if (lib.isLocalEnv(env)) return false;
1772
1773 if (!id) {
1774 let remote = await Preset.getByName(env, this.name);
1775 id = remote.id;
1776 }
1777
1778 return await lib.makeAPIRequest({
1779 env,
1780 path: `/presets/${id}`,
1781 method: "DELETE"
1782 });
1783 }
1784
1785 async delete() {
1786 if (lib.isLocalEnv(this.remote)) return false;
1787 return await this.deleteRemoteVersion(this.remote, this.id);
1788 }
1789
1790 async uploadCodeToEnv(env, includeMetadata, shouldTest = true) {
1791 if (!this.name) {
1792 let match;
1793
1794 if (match = /^(#|["']{3})\s*EPH (\d+)/.exec(this.code.trim())) {
1795 let a = await Asset.getById(env, Number(match[2]));
1796 return a.startEphemeralEvaluateIdeal(this);
1797 } else {
1798 log(chalk`Failed uploading {red ${this.path}}. No name found.`);
1799 return;
1800 }
1801 }
1802
1803 write(chalk`Uploading preset {green ${this.name}} to {green ${env}}: `);
1804
1805 if (this.immutable) {
1806 log(chalk`{magenta IMMUTABLE}. Nothing to do.`);
1807 return;
1808 } //First query the api to see if this already exists.
1809
1810
1811 let remote = await Preset.getByName(env, this.name);
1812
1813 if (remote) {
1814 //If it exists we can replace it
1815 write("replace, ");
1816
1817 if (includeMetadata) {
1818 let payload = {
1819 data: {
1820 attributes: this.data.attributes,
1821 type: "presets"
1822 }
1823 };
1824
1825 if (this.relationships.tagNames) {
1826 payload.relationships = {
1827 tagNames: this.relationships.tagNames
1828 };
1829 }
1830
1831 let res = await lib.makeAPIRequest({
1832 env,
1833 path: `/presets/${remote.id}`,
1834 method: "PATCH",
1835 payload,
1836 fullResponse: true
1837 });
1838 write(chalk`metadata {yellow ${res.statusCode}}, `);
1839
1840 if (res.statusCode == 500) {
1841 log(chalk`skipping code upload, did not successfully upload metadata`);
1842 return;
1843 }
1844 }
1845
1846 await this.uploadPresetData(env, remote.id);
1847 } else {
1848 write("create, ");
1849 let metadata = {
1850 data: this.data
1851 };
1852
1853 if (!this.relationships["providerType"]) {
1854 throw new AbortError("Cannot acclimatize shelled presets. (try creating it on the env first)");
1855 }
1856
1857 await this.acclimatize(env);
1858 write("Posting to create preset... ");
1859 let res = await lib.makeAPIRequest({
1860 env,
1861 path: `/presets`,
1862 method: "POST",
1863 payload: metadata,
1864 timeout: 5000
1865 });
1866 let id = res.data.id;
1867 write(chalk`Created id {green ${id}}... Uploading Code... `);
1868 await this.uploadPresetData(env, id);
1869 }
1870
1871 if (this.test[0] && shouldTest) {
1872 await this.runTest(env);
1873 } else {
1874 log("No tests. Done.");
1875 }
1876 }
1877
1878 getLocalMetadata() {
1879 return JSON.parse(readFileSync(this.localmetadatapath, "utf-8"));
1880 }
1881
1882 getLocalCode() {
1883 //todo fixup for binary presets, see uploadPresetData
1884 return readFileSync(this.path, "utf-8");
1885 }
1886
1887 parseHeaderInfo() {
1888 var _$exec$, _$exec$2, _$exec$3, _$exec$4, _$exec$5, _$exec$6, _$exec$7;
1889
1890 if (!this.header) return null;
1891 let abs = {
1892 built: (_$exec$ = /Built On:(.+)/.exec(this.header)[1]) === null || _$exec$ === void 0 ? void 0 : _$exec$.trim(),
1893 author: (_$exec$2 = /Author:(.+)/.exec(this.header)[1]) === null || _$exec$2 === void 0 ? void 0 : _$exec$2.trim(),
1894 build: (_$exec$3 = /Build:(.+)/.exec(this.header)[1]) === null || _$exec$3 === void 0 ? void 0 : _$exec$3.trim(),
1895 version: (_$exec$4 = /Version:(.+)/.exec(this.header)[1]) === null || _$exec$4 === void 0 ? void 0 : _$exec$4.trim(),
1896 branch: (_$exec$5 = /Branch:(.+)/.exec(this.header)[1]) === null || _$exec$5 === void 0 ? void 0 : _$exec$5.trim(),
1897 commit: (_$exec$6 = /Commit:(.+)/.exec(this.header)[1]) === null || _$exec$6 === void 0 ? void 0 : _$exec$6.trim(),
1898 local: (_$exec$7 = /Local File:(.+)/.exec(this.header)[1]) === null || _$exec$7 === void 0 ? void 0 : _$exec$7.trim()
1899 };
1900 let tryFormats = [[true, "ddd MMM DD HH:mm:ss YYYY"], [false, "ddd YYYY/MM/DD LTS"]];
1901
1902 for (let [isUTC, format] of tryFormats) {
1903 let date;
1904
1905 if (isUTC) {
1906 date = moment.utc(abs.built, format);
1907 } else {
1908 date = moment(abs.built, format);
1909 }
1910
1911 if (!date.isValid()) continue;
1912 abs.offset = date.fromNow();
1913 break;
1914 }
1915
1916 return abs;
1917 }
1918
1919 async printRemoteInfo(env) {
1920 let remote = await Preset.getByName(env, this.name);
1921 await remote.downloadCode();
1922 let i = remote.parseHeaderInfo();
1923
1924 if (i) {
1925 log(chalk`
1926 ENV: {red ${env}}, updated {yellow ~${i.offset}}
1927 Built on {blue ${i.built}} by {green ${i.author}}
1928 From ${i.build || "(unknown)"} on ${i.branch} ({yellow ${i.commit}})
1929 `.replace(/^[ \t]+/gim, "").trim());
1930 } else {
1931 log(chalk`No header on {red ${env}}`);
1932 }
1933 }
1934
1935 async getInfo(envs) {
1936 await this.printDepends();
1937
1938 for (let env of envs.split(",")) {
1939 await this.printRemoteInfo(env);
1940 }
1941 }
1942
1943 async printDepends(indent = 0, locals = null, seen = {}) {
1944 let includeRegex = /@include "(.+)"/gim; //let includeRegex = /@include/g;
1945
1946 let includes = [];
1947 let inc;
1948
1949 while (inc = includeRegex.exec(this.code)) {
1950 includes.push(inc[1]);
1951 } //let includes = this.code
1952 //.split("\n")
1953 //.map(x => includeRegex.exec(x))
1954 //.filter(x => x)
1955 //.map(x => x[1]);
1956 //log(includes);
1957
1958
1959 if (!locals) {
1960 locals = new Collection((await loadLocals("silo-presets", Preset)));
1961 }
1962
1963 log(Array(indent + 1).join(" ") + "- " + this.name);
1964
1965 for (let include of includes) {
1966 if (seen[include]) {
1967 log(Array(indent + 1).join(" ") + " - (seen) " + include);
1968 } else {
1969 seen[include] = true;
1970 let file = await locals.findByName(include);
1971
1972 if (file) {
1973 await file.printDepends(indent + 2, locals, seen);
1974 } else {
1975 log(Array(indent + 1).join(" ") + " - (miss) " + include);
1976 }
1977 }
1978 }
1979 }
1980
1981 }
1982
1983 defineAssoc(Preset, "_nameInner", "data.attributes.providerSettings.PresetName");
1984 defineAssoc(Preset, "_nameOuter", "data.attributes.name");
1985 defineAssoc(Preset, "id", "data.id");
1986 defineAssoc(Preset, "attributes", "data.attributes");
1987 defineAssoc(Preset, "relationships", "data.relationships");
1988 defineAssoc(Preset, "remote", "meta.remote");
1989 defineAssoc(Preset, "_code", "meta.code");
1990 defineAssoc(Preset, "_path", "meta.path");
1991 defineAssoc(Preset, "isGeneric", "meta.isGeneric");
1992 defineAssoc(Preset, "ext", "meta.ext");
1993 defineAssoc(Preset, "subproject", "meta.project");
1994 defineAssoc(Preset, "metastring", "meta.metastring");
1995 Preset.endpoint = "presets";
1996
1997 class Notification extends RallyBase {
1998 constructor({
1999 data,
2000 remote
2001 }) {
2002 super();
2003 this.data = data;
2004 this.meta = {};
2005 this.remote = remote;
2006 }
2007
2008 static async getAllPreCollect(notifications) {
2009 return notifications.sort((a, b) => {
2010 return a.attributes.type.localeCompare(b.attributes.type) || a.attributes.name.localeCompare(b.attributes.name);
2011 });
2012 }
2013
2014 chalkPrint(pad = false) {
2015 let id = String("N-" + this.id);
2016 if (pad) id = id.padStart(4);
2017 return chalk`{green ${id}}: {blue ${this.type}} - {green ${this.name}}`;
2018 }
2019
2020 }
2021
2022 defineAssoc(Notification, "id", "data.id");
2023 defineAssoc(Notification, "name", "data.attributes.name");
2024 defineAssoc(Notification, "address", "data.attributes.address");
2025 defineAssoc(Notification, "type", "data.attributes.type");
2026 defineAssoc(Notification, "remote", "meta.remote");
2027 Notification.endpoint = "notificationPresets";
2028
2029 class Rule extends RallyBase {
2030 constructor({
2031 path: path$1,
2032 data,
2033 remote,
2034 subProject
2035 } = {}) {
2036 super();
2037
2038 if (path$1) {
2039 path$1 = path.resolve(path$1);
2040
2041 try {
2042 let f = readFileSync(path$1, "utf-8");
2043 data = JSON.parse(readFileSync(path$1, "utf-8"));
2044 } catch (e) {
2045 if (e.code === "ENOENT") {
2046 if (exports.configObject.ignoreMissing) {
2047 this.missing = true;
2048 return undefined;
2049 } else {
2050 throw new AbortError("Could not load code of local file");
2051 }
2052 } else {
2053 throw new AbortError(`Unreadable JSON in ${path$1}. ${e}`);
2054 }
2055 }
2056 }
2057
2058 this.meta = {};
2059 this.subproject = subProject;
2060
2061 if (!data) {
2062 data = Rule.newShell();
2063 }
2064
2065 this.data = data;
2066 this.remote = remote;
2067 this.isGeneric = !this.remote;
2068 }
2069
2070 static newShell() {
2071 return {
2072 "attributes": {
2073 "description": "-",
2074 "priority": "PriorityNorm",
2075 "starred": false
2076 },
2077 "relationships": {},
2078 "type": "workflowRules"
2079 };
2080 }
2081
2082 async acclimatize(env) {
2083 this.remote = env;
2084 let preset = await this.resolveField(Preset, "preset", false, "specific");
2085 let pNext = await this.resolveField(Rule, "passNext", false, "specific");
2086 let eNext = await this.resolveField(Rule, "errorNext", false, "specific");
2087 let proType = await this.resolveField(Provider, "providerType", false, "specific");
2088 let dynamicNexts = await this.resolveField(Rule, "dynamicNexts", true, "specific");
2089 let enterNotif = await this.resolveField(Notification, "enterNotifications", true, "specific");
2090 let errorNotif = await this.resolveField(Notification, "errorNotifications", true, "specific");
2091 let passNotif = await this.resolveField(Notification, "passNotifications", true, "specific");
2092 }
2093
2094 async saveA(env) {
2095 if (lib.isLocalEnv(env)) return;
2096 return await this.createIfNotExist(env);
2097 }
2098
2099 async saveB(env) {
2100 if (!this.isGeneric) {
2101 await this.resolve();
2102 }
2103
2104 this.cleanup();
2105
2106 if (lib.isLocalEnv(env)) {
2107 log(chalk`Saving rule {green ${this.name}} to {blue ${lib.envName(env)}}.`);
2108 writeFileSync(this.localpath, JSON.stringify(this.data, null, 4));
2109 } else {
2110 await this.acclimatize(env);
2111 await this.uploadRemote(env);
2112 }
2113 }
2114
2115 get immutable() {
2116 return false;
2117 }
2118
2119 async createIfNotExist(env) {
2120 write(chalk`First pass rule {green ${this.name}} to {green ${env}}: `);
2121
2122 if (this.immutable) {
2123 log(chalk`{magenta IMMUTABLE}. Nothing to do.`);
2124 return;
2125 } //First query the api to see if this already exists.
2126
2127
2128 let remote = await Rule.getByName(env, this.name);
2129 this.idMap = this.idMap || {};
2130
2131 if (remote) {
2132 this.idMap[env] = remote.id;
2133 log(chalk`exists ${remote.chalkPrint(false)}`);
2134 return;
2135 } //If it exists we can replace it
2136
2137
2138 write("create, ");
2139 let res = await lib.makeAPIRequest({
2140 env,
2141 path: `/workflowRules`,
2142 method: "POST",
2143 payload: {
2144 data: {
2145 attributes: {
2146 name: this.name
2147 },
2148 type: "workflowRules"
2149 }
2150 }
2151 });
2152 this.idMap = this.idMap || {};
2153 this.idMap[env] = res.data.id;
2154 write("id ");
2155 log(this.idMap[env]);
2156 }
2157
2158 async patchStrip() {
2159 delete this.data.attributes.createdAt;
2160 delete this.data.attributes.starred;
2161 delete this.data.attributes.updatedAt; // TEMP FIX FOR BUG IN SDVI
2162
2163 if (this.relationships.passMetadata && this.relationships.passMetadata[0]) {
2164 log("HAS PASS");
2165 log(this.name);
2166 log("HAS PASS");
2167 }
2168
2169 delete this.relationships.passMetadata;
2170
2171 if (this.relationships.errorMetadata && this.relationships.errorMetadata[0]) {
2172 log("HAS PASS");
2173 log(this.name);
2174 log("HAS PASS");
2175 }
2176
2177 delete this.relationships.errorMetadata; // This is commented out because it was fixed.
2178 //for(let key in this.relationships){
2179 //let relationship = this.relationships[key];
2180 //if(!relationship.data || relationship.data instanceof Array && !relationship.data[0]){
2181 //delete this.relationships[key];
2182 //}
2183 //}
2184 }
2185
2186 async uploadRemote(env) {
2187 write(chalk`Uploading rule {green ${this.name}} to {green ${env}}: `);
2188
2189 if (this.immutable) {
2190 log(chalk`{magenta IMMUTABLE}. Nothing to do.`);
2191 return;
2192 }
2193
2194 if (this.idMap[env]) {
2195 this.remote = env;
2196 await this.patchStrip();
2197 this.data.id = this.idMap[env]; //If it exists we can replace it
2198
2199 write("replace, ");
2200 let res = await lib.makeAPIRequest({
2201 env,
2202 path: `/workflowRules/${this.idMap[env]}`,
2203 method: "PATCH",
2204 payload: {
2205 data: this.data
2206 },
2207 fullResponse: true
2208 });
2209 log(chalk`response {yellow ${res.statusCode}}`);
2210
2211 if (res.statusCode !== 200) {
2212 log(res.body);
2213 log(JSON.stringify(this.data, null, 4));
2214 }
2215 } else {
2216 throw Error("Bad idmap!");
2217 }
2218 }
2219
2220 get localpath() {
2221 return path.join(exports.configObject.repodir, this.subproject || "", "silo-rules", this.name + ".json");
2222 }
2223
2224 async resolve() {
2225 let preset = await this.resolveField(Preset, "preset", false); //log(preset);
2226
2227 let pNext = await this.resolveField(Rule, "passNext", false);
2228 let eNext = await this.resolveField(Rule, "errorNext", false);
2229 let proType = await this.resolveField(Provider, "providerType", false); //log("Dynamic nexts")
2230
2231 let dynamicNexts = await this.resolveField(Rule, "dynamicNexts", true); //log(dynamicNexts);
2232
2233 let enterNotif = await this.resolveField(Notification, "enterNotifications", true);
2234 let errorNotif = await this.resolveField(Notification, "errorNotifications", true);
2235 let passNotif = await this.resolveField(Notification, "passNotifications", true); //TODO Unsupported
2236
2237 delete this.relationships["enterMetadata"];
2238 delete this.relationships["errorMetadata"];
2239 this.isGeneric = true;
2240 return {
2241 preset,
2242 proType,
2243 pNext,
2244 eNext,
2245 dynamicNexts,
2246 errorNotif,
2247 enterNotif,
2248 passNotif
2249 };
2250 }
2251
2252 chalkPrint(pad = true) {
2253 let id = String("R-" + (this.remote && this.remote + "-" + this.id || "LOCAL"));
2254 let sub = "";
2255
2256 if (this.subproject) {
2257 sub = chalk`{yellow ${this.subproject}}`;
2258 }
2259
2260 if (pad) id = id.padStart(10);
2261
2262 try {
2263 return chalk`{green ${id}}: ${sub}{blue ${this.name}}`;
2264 } catch (e) {
2265 return this.data;
2266 }
2267 }
2268
2269 }
2270
2271 defineAssoc(Rule, "name", "data.attributes.name");
2272 defineAssoc(Rule, "description", "data.attributes.description");
2273 defineAssoc(Rule, "id", "data.id");
2274 defineAssoc(Rule, "relationships", "data.relationships");
2275 defineAssoc(Rule, "isGeneric", "meta.isGeneric");
2276 defineAssoc(Rule, "remote", "meta.remote");
2277 defineAssoc(Rule, "subproject", "meta.project");
2278 defineAssoc(Rule, "idMap", "meta.idMap");
2279 Rule.endpoint = "workflowRules";
2280
2281 //Move project into silo metadata
2282 //move autotest into silo metadata
2283 //
2284
2285 class SupplyChain {
2286 constructor(startingRule, stopRule) {
2287 if (startingRule) {
2288 this.startingRule = startingRule;
2289 this.stopRule = stopRule;
2290 this.remote = startingRule.remote;
2291 }
2292 }
2293
2294 async downloadPresetCode(objs = this.allPresets) {
2295 log("Downloading code... ");
2296 await lib.keepalive(objs.arr.map(x => () => x.downloadCode()));
2297 }
2298
2299 async calculate() {
2300 log("Getting rules... ");
2301 this.allRules = await Rule.getAll(this.remote);
2302 log(this.allRules.length);
2303 log("Getting presets... ");
2304 this.allPresets = await Preset.getAll(this.remote);
2305 log(this.allPresets.length);
2306 log("Getting providers... ");
2307 this.allProviders = await Provider.getAll(this.remote);
2308 log(this.allProviders.length);
2309 log("Getting notifications... ");
2310 this.allNotifications = await Notification.getAll(this.remote);
2311 log(this.allNotifications.length);
2312
2313 if (!this.startingRule) {
2314 this.rules = this.allRules;
2315 this.presets = this.allPresets;
2316 this.notifications = new Collection([]);
2317 await this.downloadPresetCode();
2318 return;
2319 } else {
2320 await this.downloadPresetCode();
2321 }
2322
2323 log("Done!"); //Now we have everything we need to find a whole supply chain
2324
2325 write("Calculating Supply chain... ");
2326 log(this.startingRule.chalkPrint());
2327 let allRuleNames = this.allRules.arr.map(x => x.name).filter(x => x.length >= 4);
2328 let allPresetNames = this.allPresets.arr.map(x => x.name).filter(x => x.length >= 4);
2329 let allNotifNames = this.allNotifications.arr.map(x => x.name).filter(x => x.length >= 4);
2330 let requiredNotifications = new Set();
2331 let ruleQueue = [this.startingRule];
2332 let presetQueue = [];
2333
2334 for (let currentRule of ruleQueue) {
2335 if (currentRule === this.stopRule) continue;
2336 let {
2337 eNext,
2338 pNext,
2339 preset,
2340 passNotif,
2341 errorNotif,
2342 enterNotif
2343 } = await currentRule.resolve();
2344 passNotif.forEach(n => requiredNotifications.add(n));
2345 enterNotif.forEach(n => requiredNotifications.add(n));
2346 errorNotif.forEach(n => requiredNotifications.add(n));
2347 if (eNext && !ruleQueue.includes(eNext)) ruleQueue.push(eNext);
2348 if (pNext && !ruleQueue.includes(eNext)) ruleQueue.push(pNext);
2349 let neededPresets = preset.findStringsInCode(allPresetNames);
2350 neededPresets = neededPresets.map(x => this.allPresets.findByName(x));
2351 let neededRules = preset.findStringsInCode(allRuleNames);
2352 neededRules = neededRules.map(x => this.allRules.findByName(x));
2353 preset.findStringsInCode(allNotifNames).map(str => this.allNotifications.findByName(str)).forEach(notif => requiredNotifications.add(notif));
2354 neededPresets.push(preset);
2355
2356 for (let p of neededPresets) if (!presetQueue.includes(p)) presetQueue.push(p);
2357
2358 for (let p of neededRules) if (!ruleQueue.includes(p)) ruleQueue.push(p);
2359
2360 if (exports.configObject.verbose) {
2361 write(currentRule.chalkPrint(false));
2362 log(":");
2363 write(" ");
2364 write(preset.chalkPrint(false));
2365 log(":");
2366 write(" Pass Next: ");
2367 if (pNext) write(pNext.chalkPrint(false));else write("None");
2368 log("");
2369 write(" Err Next: ");
2370 if (eNext) write(eNext.chalkPrint(false));else write("None");
2371 log("");
2372 log(" Rules:");
2373
2374 for (let p of neededRules) log(" " + p.chalkPrint(true));
2375
2376 log(" Presets:");
2377
2378 for (let p of neededPresets) log(" " + p.chalkPrint(true));
2379
2380 log("\n");
2381 }
2382 }
2383
2384 log("Done!");
2385 this.rules = new Collection(ruleQueue);
2386 this.presets = new Collection(presetQueue);
2387 requiredNotifications.delete(undefined);
2388 this.notifications = new Collection([...requiredNotifications]);
2389 }
2390
2391 async log() {
2392 if (this.notifications.arr.length > 0) {
2393 log("Required notifications: ");
2394 this.notifications.log();
2395 }
2396
2397 if (this.rules.arr.length > 0) {
2398 write("Required rules: ");
2399 log(this.rules.arr.length);
2400 this.rules.log();
2401 }
2402
2403 if (this.presets.arr.length > 0) {
2404 write("Required presets: ");
2405 log(this.presets.arr.length);
2406 this.presets.log();
2407 }
2408
2409 if (exports.configObject.rawOutput) {
2410 return {
2411 presets: this.presets.arr,
2412 rules: this.rules.arr,
2413 notifications: this.notifications.arr
2414 };
2415 }
2416 }
2417
2418 async deleteTo(env) {
2419 for (let preset of this.presets) {
2420 try {
2421 await preset.deleteRemoteVersion(env);
2422 } catch (e) {
2423 log(e);
2424 }
2425 }
2426 }
2427
2428 async syncTo(env) {
2429 for (let preset of this.presets) {
2430 try {
2431 await preset.save(env);
2432 } catch (e) {
2433 log(e);
2434 }
2435 }
2436
2437 if (this.rules.arr[0]) {
2438 log("Starting create phase for rules");
2439
2440 for (let rule of this.rules) {
2441 try {
2442 await rule.saveA(env);
2443 } catch (e) {
2444 log(e);
2445 }
2446 }
2447
2448 log("OK");
2449 log("Starting link phase for rules");
2450 Rule.removeCache(env);
2451
2452 for (let rule of this.rules) {
2453 try {
2454 await rule.saveB(env);
2455 } catch (e) {
2456 log(e);
2457 }
2458 }
2459 }
2460 }
2461
2462 }
2463
2464 class User extends RallyBase {
2465 constructor({
2466 data,
2467 remote
2468 }) {
2469 super();
2470 this.data = data;
2471 this.meta = {};
2472 this.remote = remote;
2473 }
2474
2475 chalkPrint(pad = false) {
2476 let id = String("U-" + this.id);
2477 if (pad) id = id.padStart(7);
2478 return chalk`{green ${id}}: {blue ${this.name}}`;
2479 }
2480
2481 }
2482
2483 defineAssoc(User, "id", "data.id");
2484 defineAssoc(User, "name", "data.attributes.name");
2485 defineAssoc(User, "email", "data.attributes.email");
2486 defineAssoc(User, "remote", "meta.remote");
2487 User.endpoint = "users";
2488
2489 class Tag extends RallyBase {
2490 constructor({
2491 data,
2492 remote
2493 } = {}) {
2494 super();
2495 this.meta = {};
2496 this.remote = remote;
2497 this.data = data; //this.data.attributes.rallyConfiguration = undefined;
2498 //this.data.attributes.systemManaged = undefined;
2499 }
2500
2501 chalkPrint(pad = true) {
2502 let id = String("T-" + this.remote + "-" + this.id);
2503 if (pad) id = id.padStart(10);
2504 let prefix = this.curated ? "blue +" : "red -";
2505 return chalk`{green ${id}}: {${prefix}${this.name}}`;
2506 }
2507
2508 static async create(env, name, {
2509 notCurated
2510 } = {}) {
2511 return new Tag({
2512 data: await lib.makeAPIRequest({
2513 env,
2514 path: `/${this.endpoint}`,
2515 method: "POST",
2516 payload: {
2517 data: {
2518 attributes: {
2519 name,
2520 curated: notCurated ? false : true
2521 },
2522 type: "tagNames"
2523 }
2524 }
2525 }),
2526 remote: env
2527 });
2528 }
2529
2530 }
2531
2532 defineAssoc(Tag, "id", "data.id");
2533 defineAssoc(Tag, "attributes", "data.attributes");
2534 defineAssoc(Tag, "relationships", "data.relationships");
2535 defineAssoc(Tag, "name", "data.attributes.name");
2536 defineAssoc(Tag, "curated", "data.attributes.curated");
2537 defineAssoc(Tag, "remote", "meta.remote");
2538 Tag.endpoint = "tagNames";
2539
2540 async function findLineInFile(renderedPreset, lineNumber) {
2541 let trueFileLine = lineNumber;
2542 let linedRenderedPreset = renderedPreset.split("\n").slice(2, -2);
2543 renderedPreset = renderedPreset.split("\n").slice(2, -2).join("\n");
2544 let includeLocation = renderedPreset.split("\n").filter(x => x.includes("@include"));
2545 let endIncludeNumber = -1,
2546 addTabDepth = 2;
2547 let lineBeforeIncludeStatement = '';
2548 let withinInclude = true;
2549
2550 if (lineNumber > linedRenderedPreset.indexOf(includeLocation[includeLocation.length - 1])) {
2551 addTabDepth = 0;
2552 withinInclude = false;
2553 }
2554
2555 for (let index = includeLocation.length - 1; index >= 0; index--) {
2556 let currIncludeIndex = linedRenderedPreset.indexOf(includeLocation[index]);
2557 let tabDepth = includeLocation[index].split(" ").length;
2558
2559 if (lineNumber > currIncludeIndex) {
2560 if (includeLocation[index].split(" ").filter(Boolean)[1] != "ERROR:") {
2561 if (lineBeforeIncludeStatement.split(" ").length == tabDepth && withinInclude) {
2562 trueFileLine = trueFileLine - currIncludeIndex;
2563 break;
2564 } else if (lineBeforeIncludeStatement.split(" ").length + addTabDepth == tabDepth && endIncludeNumber == -1) {
2565 endIncludeNumber = currIncludeIndex;
2566 } else if (lineBeforeIncludeStatement.split(" ").length + addTabDepth == tabDepth) {
2567 trueFileLine = trueFileLine - (endIncludeNumber - currIncludeIndex);
2568 endIncludeNumber = -1;
2569 }
2570 }
2571 } else {
2572 lineBeforeIncludeStatement = includeLocation[index];
2573 }
2574 }
2575
2576 let funcLine = "";
2577
2578 for (let line of linedRenderedPreset.slice(0, lineNumber).reverse()) {
2579 let match = /def (\w+)/.exec(line);
2580
2581 if (match) {
2582 funcLine = match[1];
2583 break;
2584 }
2585 }
2586
2587 let includeFilename;
2588
2589 if (lineBeforeIncludeStatement != "") {
2590 includeFilename = lineBeforeIncludeStatement.slice(1).trim().slice(14, -1);
2591 } else {
2592 includeFilename = null;
2593 }
2594
2595 if (includeLocation.length !== 0) {
2596 trueFileLine -= 1;
2597 lineNumber -= 1;
2598 }
2599
2600 return {
2601 lineNumber: trueFileLine,
2602 includeFilename,
2603 line: linedRenderedPreset[lineNumber],
2604 funcLine
2605 };
2606 }
2607 function printOutLine(eLine) {
2608 return log(chalk`{blue ${eLine.includeFilename || "Main"}}:{green ${eLine.lineNumber}} in ${eLine.funcLine}
2609${eLine.line}`);
2610 }
2611 async function getInfo(env, jobid) {
2612 log(env, jobid);
2613 let trace = lib.makeAPIRequest({
2614 env,
2615 path: `/jobs/${jobid}/artifacts/trace`
2616 }).catch(x => null);
2617 let renderedPreset = lib.makeAPIRequest({
2618 env,
2619 path: `/jobs/${jobid}/artifacts/preset`
2620 }).catch(x => null);
2621 let result = lib.makeAPIRequest({
2622 env,
2623 path: `/jobs/${jobid}/artifacts/result`
2624 }).catch(x => null);
2625 let error = lib.makeAPIRequest({
2626 env,
2627 path: `/jobs/${jobid}/artifacts/error`
2628 }).catch(x => null);
2629 let output = lib.makeAPIRequest({
2630 env,
2631 path: `/jobs/${jobid}/artifacts/output`
2632 }).catch(x => null);
2633 [trace, renderedPreset, result, output, error] = await Promise.all([trace, renderedPreset, result, output, error]);
2634 return {
2635 trace,
2636 renderedPreset,
2637 result,
2638 output,
2639 error
2640 };
2641 }
2642 async function parseTrace(env, jobid) {
2643 let {
2644 trace,
2645 renderedPreset
2646 } = await getInfo(env, jobid);
2647 let lineNumber = -1;
2648 let errorLines = [];
2649 let shouldBreak = 0;
2650
2651 for (let tr of trace.split("\n\n").reverse()) {
2652 errorLines.push(tr);
2653 shouldBreak--;
2654 if (tr.includes("Exception")) shouldBreak = 1;
2655 if (tr.includes("raised")) shouldBreak = 1;
2656 if (!shouldBreak) break;
2657 }
2658
2659 let errorList = [];
2660
2661 for (let errLine of errorLines) {
2662 lineNumber = /^[\w ]+:(\d+):/g.exec(errLine);
2663
2664 if (lineNumber && lineNumber[1]) {
2665 errorList.push((await findLineInFile(renderedPreset, lineNumber[1])));
2666 } else {
2667 errorList.push(errLine);
2668 }
2669 }
2670
2671 return errorList;
2672 }
2673 const Trace = {
2674 parseTrace,
2675 printOutLine,
2676 getInfo,
2677 findLineInFile
2678 };
2679
2680 require("source-map-support").install();
2681 const rallyFunctions = {
2682 async bestPagintation() {
2683 global.silentAPI = true;
2684
2685 for (let i = 10; i <= 30; i += 5) {
2686 console.time("test with " + i);
2687 let dl = await lib.indexPathFast("DEV", `/workflowRules?page=1p${i}`);
2688 console.timeEnd("test with " + i);
2689 }
2690 },
2691
2692 async uploadPresets(env, presets, createFunc = () => false) {
2693 for (let preset of presets) {
2694 await preset.uploadCodeToEnv(env, createFunc);
2695 }
2696 },
2697
2698 //Dummy test access
2699 async testAccess(env) {
2700 if (lib.isLocalEnv(env)) {
2701 //TODO
2702 return true;
2703 }
2704
2705 let result = await lib.makeAPIRequest({
2706 env,
2707 path: "/providers?page=1p1",
2708 fullResponse: true,
2709 timeout: 1000
2710 });
2711 return result.statusCode;
2712 }
2713
2714 };
2715
2716 exports.APIError = APIError;
2717 exports.AbortError = AbortError;
2718 exports.Asset = Asset;
2719 exports.Collection = Collection;
2720 exports.FileTooLargeError = FileTooLargeError;
2721 exports.Notification = Notification;
2722 exports.Preset = Preset;
2723 exports.ProtectedEnvError = ProtectedEnvError;
2724 exports.Provider = Provider;
2725 exports.RallyBase = RallyBase;
2726 exports.Rule = Rule;
2727 exports.SupplyChain = SupplyChain;
2728 exports.Tag = Tag;
2729 exports.Trace = Trace;
2730 exports.UnconfiguredEnvError = UnconfiguredEnvError;
2731 exports.User = User;
2732 exports.lib = lib;
2733 exports.loadConfig = loadConfig;
2734 exports.loadConfigFromArgs = loadConfigFromArgs;
2735 exports.rallyFunctions = rallyFunctions;
2736 exports.setConfig = setConfig;
2737 exports.sleep = sleep;
2738
2739 Object.defineProperty(exports, '__esModule', { value: true });
2740
2741})));
2742//# sourceMappingURL=web.js.map