UNPKG

122 kBJavaScriptView Raw
1#!/usr/bin/env node
2/*----------------------------------------------
3 * Generated by rollup. Written by John Schmidt.
4 * Rally Tools CLI v1.20.0
5 *--------------------------------------------*/
6const importLazy = require("import-lazy")(require);
7
8'use strict';
9
10function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
11
12var argparse = _interopDefault(require('minimist'));
13var chalk$1 = _interopDefault(require('chalk'));
14var os = require('os');
15var fs = require('fs');
16var fs__default = _interopDefault(fs);
17var child_process = require('child_process');
18var perf_hooks = require('perf_hooks');
19var rp = _interopDefault(require('request-promise'));
20var path = require('path');
21var path__default = _interopDefault(path);
22var moment = _interopDefault(require('moment'));
23
24function _applyDecoratedDescriptor(target, property, decorators, descriptor, context) {
25 var desc = {};
26 Object['ke' + 'ys'](descriptor).forEach(function (key) {
27 desc[key] = descriptor[key];
28 });
29 desc.enumerable = !!desc.enumerable;
30 desc.configurable = !!desc.configurable;
31
32 if ('value' in desc || desc.initializer) {
33 desc.writable = true;
34 }
35
36 desc = decorators.slice().reverse().reduce(function (desc, decorator) {
37 return decorator(target, property, desc) || desc;
38 }, desc);
39
40 if (context && desc.initializer !== void 0) {
41 desc.value = desc.initializer ? desc.initializer.call(context) : void 0;
42 desc.initializer = undefined;
43 }
44
45 if (desc.initializer === void 0) {
46 Object['define' + 'Property'](target, property, desc);
47 desc = null;
48 }
49
50 return desc;
51}
52
53let configFile = null;
54
55if (os.homedir) {
56 configFile = os.homedir() + "/.rallyconfig";
57}
58
59let configObject;
60function loadConfig(file) {
61 if (file) configFile = file;
62 if (!configFile) return;
63 configObject = {
64 hasConfig: true
65 };
66
67 try {
68 let json = fs.readFileSync(configFile);
69 configObject = JSON.parse(json);
70 configObject.hasConfig = true;
71 } catch (e) {
72 if (e.code == "ENOENT") {
73 configObject.hasConfig = false; //ok, they should probably make a config
74 } else {
75 throw e;
76 }
77 }
78}
79function loadConfigFromArgs(args) {
80 let tempConfig = {
81 hasConfig: true,
82 ...args.config
83 };
84 configObject = tempConfig;
85}
86function setConfig(obj) {
87 configObject = obj;
88}
89
90//these are the help entries for each command
91let helpEntries = {};
92
93let helpEntry = name => helpEntries[name] ? helpEntries[name] : helpEntries[name] = {
94 name
95}; //short description
96
97
98function helpText(text) {
99 return function (func, name) {
100 helpEntry(name).text = text;
101 return func;
102 };
103} //flag type argument like -f or --file
104
105function arg(long, short, desc) {
106 return function (func, name) {
107 let args = helpEntry(name).args = helpEntry(name).args || [];
108 args.unshift({
109 long,
110 short,
111 desc
112 });
113 return func;
114 };
115} //normal argument
116
117function param(param, desc) {
118 return function (func, name) {
119 let params = helpEntry(name).params = helpEntry(name).params || [];
120 params.unshift({
121 param,
122 desc
123 });
124 return func;
125 };
126} //usage string
127
128function usage(usage) {
129 return function (func, name) {
130 usage = usage.replace(/[\[<](\w+)[\]>]/g, chalk`[{blue $1}]`);
131 helpEntry(name).usage = usage;
132 return func;
133 };
134}
135//function retuns obj.a.b.c
136
137function deepAccess(obj, path) {
138 let o = obj;
139
140 for (let key of path) {
141 if (!o) return [];
142 o = o[key];
143 }
144
145 return o;
146} //This takes a class as the first argument, then adds a getter/setter pair that
147//corresponds to an object in this.data
148
149
150function defineAssoc(classname, shortname, path) {
151 path = path.split(".");
152 let lastKey = path.pop();
153 Object.defineProperty(classname.prototype, shortname, {
154 get() {
155 return deepAccess(this, path)[lastKey];
156 },
157
158 set(val) {
159 deepAccess(this, path)[lastKey] = val;
160 }
161
162 });
163}
164
165function spawn(options, ...args) {
166 if (typeof options !== "object") {
167 args.unshift(options);
168 options = {};
169 } //todo options
170
171
172 return new Promise((resolve, reject) => {
173 let start = perf_hooks.performance.now();
174 let stdout = "";
175 let stderr = "";
176 let cp = child_process.spawn(...args);
177 let write = global.write;
178
179 if (options.noecho) {
180 write = () => {};
181 }
182
183 if (cp.stdout) cp.stdout.on("data", chunk => {
184 stdout += chunk;
185 write(chunk);
186 });
187 if (cp.stderr) cp.stderr.on("data", chunk => {
188 stderr += chunk;
189 write(chunk);
190 });
191 cp.on("error", reject);
192 cp.on("close", code => {
193 let end = perf_hooks.performance.now();
194 let time = end - start;
195 let timestr = time > 1000 ? (time / 100 | 0) / 10 + "s" : (time | 0) + "ms";
196 resolve({
197 stdout,
198 stderr,
199 exitCode: code,
200 time,
201 timestr
202 });
203 });
204 });
205}
206
207global.chalk = chalk$1;
208
209global.log = (...text) => console.log(...text);
210
211global.write = (...text) => process.stdout.write(...text);
212
213global.elog = (...text) => console.log(...text);
214
215global.ewrite = (...text) => process.stderr.write(...text);
216
217global.errorLog = (...text) => log(...text.map(chalk$1.red));
218
219class lib {
220 //This function takes 2 required arguemnts:
221 // env: the enviornment you wish to use
222 // and either:
223 // 'path', the short path to the resource. ex '/presets/'
224 // 'path_full', the full path to the resource like 'https://discovery-dev.sdvi.com/presets'
225 //
226 // If the method is anything but GET, either payload or body should be set.
227 // payload should be a javascript object to be turned into json as the request body
228 // body should be a string that is passed as the body. for example: the python code of a preset.
229 //
230 // qs are the querystring parameters, in a key: value object.
231 // {filter: "name=test name"} becomes something like 'filter=name=test+name'
232 //
233 // headers are the headers of the request. "Content-Type" is already set if
234 // payload is given as a parameter
235 //
236 // fullResponse should be true if you want to receive the request object,
237 // not just the returned data.
238 static async makeAPIRequest({
239 env,
240 path,
241 path_full,
242 fullPath,
243 payload,
244 body,
245 method = "GET",
246 qs,
247 headers = {},
248 fullResponse = false,
249 timeout = configObject.timeout || 20000
250 }) {
251 var _configObject$api;
252
253 //backwards compatability from ruby script
254 if (fullPath) path_full = fullPath; //Keys are defined in enviornment variables
255
256 let config = configObject === null || configObject === void 0 ? void 0 : (_configObject$api = configObject.api) === null || _configObject$api === void 0 ? void 0 : _configObject$api[env];
257
258 if (!config) {
259 throw new UnconfiguredEnvError(env);
260 }
261
262 if (method !== "GET" && !configObject.dangerModify) {
263 if (env === "UAT" && configObject.restrictUAT || env === "PROD") {
264 throw new ProtectedEnvError(env);
265 }
266 }
267
268 let rally_api_key = config.key;
269 let rally_api = config.url;
270
271 if (path && path.startsWith("/v1.0/")) {
272 rally_api = rally_api.replace("/api/v2", "/api");
273 }
274
275 path = path_full || rally_api + path;
276
277 if (payload) {
278 body = JSON.stringify(payload, null, 4);
279 }
280
281 if (payload) {
282 headers["Content-Type"] = "application/vnd.api+json";
283 }
284
285 let fullHeaders = {
286 //SDVI ignores this header sometimes.
287 Accept: "application/vnd.api+json",
288 "X-SDVI-Client-Application": "Discovery-rtlib-" + (configObject.appName || "commandline"),
289 ...headers
290 };
291
292 if (configObject.vvverbose) {
293 log(`${method} @ ${path}`);
294 log(JSON.stringify(fullHeaders, null, 4));
295
296 if (body) {
297 log(body);
298 } else {
299 log("(No body");
300 }
301 }
302
303 let requestOptions = {
304 method,
305 body,
306 qs,
307 uri: path,
308 timeout,
309 auth: {
310 bearer: rally_api_key
311 },
312 headers: fullHeaders,
313 simple: false,
314 resolveWithFullResponse: true
315 };
316 let response;
317
318 try {
319 response = await rp(requestOptions);
320
321 if (configObject.vverbose || configObject.vvverbose) {
322 log(chalk$1`${method} @ ${response.request.uri.href}`);
323 }
324 } catch (e) {
325 if ((e === null || e === void 0 ? void 0 : e.cause.code) === "ESOCKETTIMEDOUT") {
326 throw new APIError(response || {}, requestOptions, body);
327 } else {
328 throw e;
329 }
330 } //Throw an error for any 5xx or 4xx
331
332
333 if (!fullResponse && ![200, 201, 202, 203, 204].includes(response.statusCode)) {
334 throw new APIError(response, requestOptions, body);
335 }
336
337 let contentType = response.headers["content-type"];
338 let isJSONResponse = contentType === "application/vnd.api+json" || contentType === "application/json";
339
340 if (configObject.vvverbose) {
341 log(response.body);
342 }
343
344 if (fullResponse) {
345 return response;
346 } else if (isJSONResponse) {
347 var _response, _response$body;
348
349 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 {};
350
351 try {
352 return JSON.parse(response.body);
353 } catch (e) {
354 log(response.body);
355 throw new AbortError("Body is not valid json: ");
356 }
357 } else {
358 return response.body;
359 }
360 } //Index a json endpoint that returns a {links} field.
361 //This function returns the merged data objects as an array
362 //
363 //Additonal options (besides makeAPIRequest options):
364 // - Observe: function to be called for each set of data from the api
365
366
367 static async indexPath(env, path) {
368 let all = [];
369 let opts = typeof env === "string" ? {
370 env,
371 path
372 } : env;
373 let json = await this.makeAPIRequest(opts);
374 let [numPages, pageSize] = this.numPages(json.links.last); //log(`num pages: ${numPages} * ${pageSize}`);
375
376 all = [...json.data];
377
378 while (json.links.next) {
379 json = await this.makeAPIRequest({ ...opts,
380 path_full: json.links.next
381 });
382 if (opts.observe) await opts.observe(json.data);
383 all = [...all, ...json.data];
384 }
385
386 return all;
387 } //Returns number of pages and pagination size
388
389
390 static numPages(str) {
391 return /page=(\d+)p(\d+)/.exec(str).slice(1);
392 }
393
394 static arrayChunk(array, chunkSize) {
395 let newArr = [];
396
397 for (let i = 0; i < array.length; i += chunkSize) {
398 newArr.push(array.slice(i, i + chunkSize));
399 }
400
401 return newArr;
402 }
403
404 static async doPromises(promises, result = [], cb) {
405 for (let promise of promises) {
406 let res = await promise;
407 result.push(res);
408
409 if (cb) {
410 cb(res.data);
411 }
412 }
413
414 return result;
415 }
416
417 static clearProgress(size = 30) {
418 if (!configObject.globalProgress) return;
419 process.stderr.write(`\r${" ".repeat(size + 15)}\r`);
420 }
421
422 static async drawProgress(i, max, size = process.stdout.columns - 15 || 15) {
423 if (!configObject.globalProgress) return;
424 if (size > 45) size = 45;
425 let pct = Number(i) / Number(max); //clamp between 0 and 1
426
427 pct = pct < 0 ? 0 : pct > 1 ? 1 : pct;
428 let numFilled = Math.floor(pct * size);
429 let numEmpty = size - numFilled;
430 this.clearProgress(size);
431 process.stderr.write(`[${"*".repeat(numFilled)}${" ".repeat(numEmpty)}] ${i} / ${max}`);
432 }
433
434 static async keepalive(func, inputData, {
435 chunksize = 20,
436 observe = async _ => _,
437 progress = configObject.globalProgress
438 } = {}) {
439 let total = inputData ? inputData.length : func.length;
440 let i = 0;
441
442 let createPromise = () => {
443 let ret;
444 if (i >= total) return [];
445
446 if (inputData) {
447 ret = [i, func(inputData[i])];
448 } else {
449 ret = [i, func[i]()];
450 }
451
452 i++;
453 return ret;
454 };
455
456 let values = [];
457 let finished = 0;
458 if (progress) process.stderr.write("\n");
459 let threads = [...this.range(chunksize)].map(async whichThread => {
460 while (true) {
461 let [i, currentPromise] = createPromise();
462 if (i == undefined) break;
463 values[i] = await observe((await currentPromise));
464 if (progress) this.drawProgress(++finished, total);
465 }
466 });
467 await Promise.all(threads);
468 if (progress) process.stderr.write("\n");
469 return values;
470 }
471
472 static *range(start, end) {
473 if (end === undefined) {
474 end = start;
475 start = 0;
476 }
477
478 while (start < end) yield start++;
479 } //Index a json endpoint that returns a {links} field.
480 //
481 //This function is faster than indexPath because it can guess the pages it
482 //needs to retreive so that it can request all assets at once.
483 //
484 //This function assumes that the content from the inital request is the
485 //first page, so starting on another page may cause issues. Consider
486 //indexPath for that.
487 //
488 //Additional opts, besides default indexPath opts:
489 // - chunksize[10]: How often to break apart concurrent requests
490
491
492 static async indexPathFast(env, path) {
493 let opts = typeof env === "string" ? {
494 env,
495 path
496 } : env; //Create a copy of the options in case we need to have a special first request
497
498 let start = opts.start || 1;
499 let initOpts = { ...opts
500 };
501
502 if (opts.pageSize) {
503 initOpts.qs = { ...opts.qs
504 };
505 initOpts.qs.page = `${start}p${opts.pageSize}`;
506 }
507
508 let json = await this.makeAPIRequest(initOpts);
509 if (opts.observe && opts.start !== 1) json = await opts.observe(json);
510 let baselink = json.links.first;
511
512 const linkToPage = page => baselink.replace(`page=1p`, `page=${page}p`);
513
514 let [numPages, pageSize] = this.numPages(json.links.last); //Construct an array of all the requests that are done simultanously.
515 //Assume that the content from the inital request is the first page.
516
517 let allResults = await this.keepalive(this.makeAPIRequest, [...this.range(start + 1, Number(numPages) + 1 || opts.limit + 1)].map(i => ({ ...opts,
518 path_full: linkToPage(i)
519 })), {
520 chunksize: opts.chunksize,
521 observe: opts.observe
522 });
523
524 if (start == 1) {
525 allResults.unshift(json);
526 }
527
528 this.clearProgress();
529 let all = [];
530
531 for (let result of allResults) {
532 for (let item of result.data) {
533 all.push(item);
534 }
535 }
536
537 return all;
538 }
539
540 static isLocalEnv(env) {
541 return !env || env === "LOCAL" || env === "LOC";
542 }
543
544 static envName(env) {
545 if (this.isLocalEnv(env)) return "LOCAL";
546 return env;
547 }
548
549}
550class AbortError extends Error {
551 constructor(message) {
552 super(message);
553 Error.captureStackTrace(this, this.constructor);
554 this.name = "AbortError";
555 }
556
557}
558class APIError extends Error {
559 constructor(response, opts, body) {
560 super(chalk$1`
561{reset Request returned} {yellow ${response === null || response === void 0 ? void 0 : response.statusCode}}{
562{green ${JSON.stringify(opts, null, 4)}}
563{green ${body}}
564{reset ${response.body}}
565===============================
566{red ${response.body ? "Request timed out" : "Bad response from API"}}
567===============================
568 `);
569 this.response = response;
570 this.opts = opts;
571 this.body = body;
572 Error.captureStackTrace(this, this.constructor);
573 this.name = "ApiError";
574 }
575
576}
577class UnconfiguredEnvError extends AbortError {
578 constructor(env) {
579 super("Unconfigured enviornment: " + env);
580 this.name = "Unconfigured Env Error";
581 }
582
583}
584class ProtectedEnvError extends AbortError {
585 constructor(env) {
586 super("Protected enviornment: " + env);
587 this.name = "Protected Env Error";
588 }
589
590}
591class FileTooLargeError extends Error {
592 constructor(file) {
593 super(`File ${file.parentAsset ? file.parentAsset.name : "(unknown)"}/${file.name} size is: ${file.sizeGB}g (> ~.2G)`);
594 this.name = "File too large error";
595 }
596
597}
598class Collection {
599 constructor(arr) {
600 this.arr = arr;
601 }
602
603 [Symbol.iterator]() {
604 return this.arr[Symbol.iterator]();
605 }
606
607 findById(id) {
608 return this.arr.find(x => x.id == id);
609 }
610
611 findByName(name) {
612 return this.arr.find(x => x.name == name);
613 }
614
615 findByNameContains(name) {
616 return this.arr.find(x => x.name.includes(name));
617 }
618
619 log() {
620 for (let d of this) {
621 if (d) {
622 log(d.chalkPrint(true));
623 } else {
624 log(chalk$1`{red (None)}`);
625 }
626 }
627 }
628
629 get length() {
630 return this.arr.length;
631 }
632
633}
634class RallyBase {
635 static handleCaching() {
636 if (!this.cache) this.cache = [];
637 }
638
639 static isLoaded(env) {
640 if (!this.hasLoadedAll) return;
641 return this.hasLoadedAll[env];
642 }
643
644 static async getById(env, id, qs) {
645 this.handleCaching();
646
647 for (let item of this.cache) {
648 if (item.id == id && item.remote === env || `${env}-${id}` === item.metastring) return item;
649 }
650
651 let data = await lib.makeAPIRequest({
652 env,
653 path: `/${this.endpoint}/${id}`,
654 qs
655 });
656
657 if (data.data) {
658 let o = new this({
659 data: data.data,
660 remote: env,
661 included: data.included
662 });
663 this.cache.push(o);
664 return o;
665 }
666 }
667
668 static async getByName(env, name, qs) {
669 this.handleCaching();
670
671 for (let item of this.cache) {
672 if (item.name === name && item.remote === env) return item;
673 }
674
675 let data = await lib.makeAPIRequest({
676 env,
677 path: `/${this.endpoint}`,
678 qs: { ...qs,
679 filter: `name=${name}` + (qs ? qs.filter : "")
680 }
681 }); //TODO included might not wokr correctly here
682
683 if (data.data[0]) {
684 let o = new this({
685 data: data.data[0],
686 remote: env,
687 included: data.included
688 });
689 this.cache.push(o);
690 return o;
691 }
692 }
693
694 static async getAllPreCollect(d) {
695 return d;
696 }
697
698 static async getAll(env) {
699 this.handleCaching();
700 let datas = await lib.indexPathFast({
701 env,
702 path: `/${this.endpoint}`,
703 pageSize: "50",
704 qs: {
705 sort: "id"
706 }
707 });
708 datas = await this.getAllPreCollect(datas);
709 let all = new Collection(datas.map(data => new this({
710 data,
711 remote: env
712 })));
713 this.cache = [...this.cache, ...all.arr];
714 return all;
715 }
716
717 static async removeCache(env) {
718 this.handleCaching();
719 this.cache = this.cache.filter(x => x.remote !== env);
720 } //Specific turns name into id based on env
721 //Generic turns ids into names
722
723
724 async resolveApply(type, dataObj, direction) {
725 let obj;
726
727 if (direction == "generic") {
728 obj = await type.getById(this.remote, dataObj.id);
729
730 if (obj) {
731 dataObj.name = obj.name;
732 }
733 } else if (direction == "specific") {
734 obj = await type.getByName(this.remote, dataObj.name);
735
736 if (obj) {
737 dataObj.id = obj.id;
738 }
739 }
740
741 return obj;
742 } //Type is the baseclass you are looking for (should extend RallyBase)
743 //name is the name of the field
744 //isArray is true if it has multiple cardinailty, false if it is single
745 //direction gets passed directly to resolveApply
746
747
748 async resolveField(type, name, isArray = false, direction = "generic") {
749 // ignore empty fields
750 let field = this.relationships[name];
751 if (!(field === null || field === void 0 ? void 0 : field.data)) return;
752
753 if (isArray) {
754 return await Promise.all(field.data.map(o => this.resolveApply(type, o, direction)));
755 } else {
756 return await this.resolveApply(type, field.data, direction);
757 }
758 }
759
760 cleanup() {
761 for (let [key, val] of Object.entries(this.relationships)) {
762 //Remove ids from data
763 if (val.data) {
764 if (val.data.id) {
765 delete val.data.id;
766 } else if (val.data[0]) {
767 for (let x of val.data) delete x.id;
768 }
769 }
770
771 delete val.links;
772 } // organization is unused (?)
773
774
775 delete this.relationships.organization; // id is specific to envs
776 // but save source inside meta string in case we need it
777
778 this.metastring = this.remote + "-" + this.data.id;
779 delete this.data.id; // links too
780
781 delete this.data.links;
782 }
783
784}
785function sleep(time = 1000) {
786 return new Promise(resolve => setTimeout(resolve, time));
787}
788
789const inquirer = importLazy("inquirer");
790const readdir = importLazy("recursive-readdir");
791let hasAutoCompletePrompt = false;
792function addAutoCompletePrompt() {
793 if (hasAutoCompletePrompt) return;
794 hasAutoCompletePrompt = true;
795 inquirer.registerPrompt("autocomplete", require("inquirer-autocomplete-prompt"));
796}
797async function $api(propArray) {
798 let q;
799 q = await inquirer.prompt([{
800 type: "input",
801 name: "company",
802 message: `What is your company?`,
803 default: `discovery`
804 }]);
805 let company = q.company;
806 const defaults = {
807 DEV: `https://${company}-dev.sdvi.com/api/v2`,
808 UAT: `https://${company}-uat.sdvi.com/api/v2`,
809 QA: `https://${company}-qa.sdvi.com/api/v2`,
810 PROD: `https://${company}.sdvi.com/api/v2`
811 };
812
813 if (propArray && propArray[1]) {
814 q = {
815 envs: [propArray[1]]
816 };
817 } else {
818 //Create a checkbox prompt to choose enviornments
819 q = await inquirer.prompt([{
820 type: "checkbox",
821 name: "envs",
822 message: `What enviornments would you like to configure?`,
823 choices: Object.keys(defaults).map(name => ({
824 name,
825 checked: true
826 }))
827 }]);
828 } //Each env should ask 2 for two things: The url and the key.
829
830
831 let questions = q.envs.map(env => {
832 let defaultKey = process.env[`rally_api_key_${env}`];
833
834 if (configObject && configObject.api && configObject.api[env]) {
835 defaultKey = configObject.api[env].key;
836 }
837
838 return [{
839 type: "input",
840 name: `api.${env}.url`,
841 message: `What is the api endpoint for ${env}?`,
842 default: defaults[env]
843 }, {
844 type: "input",
845 name: `api.${env}.key`,
846 message: `What is your api key for ${env}?`,
847 default: defaultKey
848 }];
849 }); //flatten and ask
850
851 questions = [].concat(...questions);
852 q = await inquirer.prompt(questions);
853
854 if (propArray) {
855 q.api = { ...configObject.api,
856 ...q.api
857 };
858 }
859
860 return q;
861}
862async function $chalk(propArray) {
863 return {
864 chalk: await askQuestion("Would you like chalk enabled (Adds coloring)?")
865 };
866}
867async function $restrictUAT(propArray) {
868 return {
869 restrictUAT: await askQuestion("Would you like to protect UAT?")
870 };
871}
872async function $repodir(propArray) {
873 return await inquirer.prompt([{
874 type: "input",
875 name: `repodir`,
876 message: `Where is your rally repository?`,
877 default: process.env["rally_repo_path"]
878 }]);
879}
880async function $appName(propArray) {
881 let defaultAppName = "cmdline-" + (process.env.USERNAME || process.env.LOGNAME);
882 let project = await askInput("Application name?", defaultAppName);
883
884 if (project === "none" || project === "-" || project === "" || !project) {
885 project = null;
886 }
887
888 return {
889 appName: project
890 };
891}
892async function $project(propArray) {
893 let project = await askInput("Subproject directory?");
894
895 if (project === "none" || project === "-" || project === "" || !project) {
896 project = null;
897 }
898
899 return {
900 project
901 };
902}
903async function $defaultEnv(propArray) {
904 return await inquirer.prompt([{
905 type: "input",
906 name: `defaultEnv`,
907 message: `Default enviornment?`,
908 default: "DEV"
909 }]);
910} //Internal usage/testing
911
912async function selectProvider(providers, autoDefault = false) {
913 addAutoCompletePrompt();
914 let defaultProvider = providers.findByName("SdviEvaluate");
915
916 if (autoDefault) {
917 return defaultProvider;
918 } else {
919 let choices = providers.arr.map(x => ({
920 name: x.chalkPrint(true),
921 value: x
922 }));
923 let q = await inquirer.prompt([{
924 type: "autocomplete",
925 name: "provider",
926 default: defaultProvider,
927 source: async (sofar, input) => {
928 return choices.filter(x => input ? x.value.name.toLowerCase().includes(input.toLowerCase()) : true);
929 }
930 }]);
931 return q.provider;
932 }
933}
934async function loadLocals(path, Class) {
935 let basePath = configObject.repodir;
936 let f = await readdir(basePath);
937 let objs = f.filter(name => name.includes(path)).map(name => new Class({
938 path: name
939 }));
940 return objs;
941}
942async function selectLocal(path, typeName, Class, canSelectNone = true) {
943 addAutoCompletePrompt();
944 let objs = loadLocals(path, Class);
945 let objsMap = objs.map(x => ({
946 name: x.chalkPrint(true),
947 value: x
948 }));
949 let none = {
950 name: chalk` {red None}: {red None}`,
951 value: null
952 };
953 if (canSelectNone) objsMap.unshift(none);
954 let q = await inquirer.prompt([{
955 type: "autocomplete",
956 name: "obj",
957 message: `What ${typeName} do you want?`,
958 source: async (sofar, input) => {
959 return objsMap.filter(x => input ? x.name.toLowerCase().includes(input.toLowerCase()) : true);
960 }
961 }]);
962 return q.obj;
963}
964async function selectPreset({
965 purpose = "preset",
966 canSelectNone
967}) {
968 return selectLocal("silo-presets", purpose, Preset, canSelectNone);
969}
970async function selectRule({
971 purpose = "rule",
972 canSelectNone
973}) {
974 return selectLocal("silo-rules", purpose, Rule, canSelectNone);
975}
976async function askInput(question, def) {
977 return (await inquirer.prompt([{
978 type: "input",
979 name: "ok",
980 message: question,
981 default: def
982 }])).ok;
983}
984async function askQuestion(question) {
985 return (await inquirer.prompt([{
986 type: "confirm",
987 name: "ok",
988 message: question
989 }])).ok;
990}
991
992var configHelpers = /*#__PURE__*/Object.freeze({
993 __proto__: null,
994 inquirer: inquirer,
995 addAutoCompletePrompt: addAutoCompletePrompt,
996 $api: $api,
997 $chalk: $chalk,
998 $restrictUAT: $restrictUAT,
999 $repodir: $repodir,
1000 $appName: $appName,
1001 $project: $project,
1002 $defaultEnv: $defaultEnv,
1003 selectProvider: selectProvider,
1004 loadLocals: loadLocals,
1005 selectLocal: selectLocal,
1006 selectPreset: selectPreset,
1007 selectRule: selectRule,
1008 askInput: askInput,
1009 askQuestion: askQuestion
1010});
1011
1012class Provider extends RallyBase {
1013 constructor({
1014 data,
1015 remote
1016 }) {
1017 super();
1018 this.data = data;
1019 this.meta = {};
1020 this.remote = remote;
1021 } //cached
1022
1023
1024 async getEditorConfig() {
1025 if (this.editorConfig) return this.editorConfig;
1026 this.editorConfig = await lib.makeAPIRequest({
1027 env: this.remote,
1028 path_full: this.data.links.editorConfig
1029 });
1030 this.editorConfig.fileExt = await this.getFileExtension();
1031 return this.editorConfig;
1032 }
1033
1034 static async getAllPreCollect(providers) {
1035 return providers.sort((a, b) => {
1036 return a.attributes.category.localeCompare(b.attributes.category) || a.attributes.name.localeCompare(b.attributes.name);
1037 });
1038 }
1039
1040 async getFileExtension() {
1041 let config = await this.getEditorConfig();
1042 let map = {
1043 python: "py",
1044 text: "txt",
1045
1046 getmap(key) {
1047 if (this.name === "Aurora") return "zip";
1048 if (this[key]) return this[key];
1049 return key;
1050 }
1051
1052 };
1053 return map.getmap(config.lang);
1054 }
1055
1056 chalkPrint(pad = true) {
1057 let id = String(this.id);
1058 if (pad) id = id.padStart(4);
1059 return chalk`{green ${id}}: {blue ${this.category}} - {green ${this.name}}`;
1060 }
1061
1062}
1063
1064defineAssoc(Provider, "id", "data.id");
1065defineAssoc(Provider, "name", "data.attributes.name");
1066defineAssoc(Provider, "category", "data.attributes.category");
1067defineAssoc(Provider, "remote", "meta.remote");
1068defineAssoc(Provider, "editorConfig", "meta.editorConfig");
1069Provider.endpoint = "providerTypes";
1070
1071class File extends RallyBase {
1072 constructor({
1073 data,
1074 remote,
1075 included,
1076 parent
1077 }) {
1078 super();
1079 this.data = data;
1080 this.meta = {};
1081 this.remote = remote;
1082 this.parentAsset = parent;
1083 }
1084
1085 chalkPrint(pad = false) {
1086 let id = String("F-" + (this.remote && this.remote + "-" + this.id || "LOCAL"));
1087 if (pad) id = id.padStart(15);
1088 return chalk`{green ${id}}: {blue ${this.data.attributes ? this.name : "(lite asset)"}}`;
1089 }
1090
1091 canBeDownloaded() {
1092 return this.sizeGB <= .2;
1093 }
1094
1095 async getContent(force = false) {
1096 if (!this.canBeDownloaded() && !force) {
1097 throw new FileTooLargeError(this);
1098 }
1099
1100 return lib.makeAPIRequest({
1101 env: this.remote,
1102 fullPath: this.contentLink
1103 });
1104 }
1105
1106 async delete(remove = true) {
1107 return lib.makeAPIRequest({
1108 env: this.remote,
1109 fullPath: this.selfLink,
1110 method: "DELETE"
1111 });
1112 }
1113
1114 get size() {
1115 return Object.values(this.data.attributes.instances)[0].size;
1116 }
1117
1118 get sizeGB() {
1119 return Math.round(this.size / 1024 / 1024 / 1024 * 10) / 10;
1120 }
1121
1122}
1123
1124defineAssoc(File, "id", "data.id");
1125defineAssoc(File, "name", "data.attributes.label");
1126defineAssoc(File, "contentLink", "data.links.content");
1127defineAssoc(File, "selfLink", "data.links.self");
1128defineAssoc(File, "label", "data.attributes.label");
1129defineAssoc(File, "md5", "data.attributes.md5");
1130defineAssoc(File, "sha512", "data.attributes.sha512");
1131defineAssoc(File, "tags", "data.attributes.tagList");
1132defineAssoc(File, "instances", "data.attributes.instances");
1133File.endpoint = null;
1134
1135class Asset extends RallyBase {
1136 constructor({
1137 data,
1138 remote,
1139 included,
1140 lite
1141 }) {
1142 super();
1143 this.data = data;
1144 this.meta = {};
1145 this.remote = remote;
1146
1147 if (included) {
1148 this.meta.metadata = Asset.normalizeMetadata(included);
1149 }
1150
1151 this.lite = !!lite;
1152 }
1153
1154 static normalizeMetadata(payload) {
1155 let newMetadata = {};
1156
1157 for (let md of payload) {
1158 if (md.type !== "metadata") continue;
1159 newMetadata[md.attributes.usage] = md.attributes.metadata;
1160 }
1161
1162 return newMetadata;
1163 }
1164
1165 async getMetadata(forceRefresh = false) {
1166 if (this.meta.metadata && !forceRefresh) return this.meta.metadata;
1167 let req = await lib.makeAPIRequest({
1168 env: this.remote,
1169 path: `/movies/${this.id}/metadata`
1170 });
1171 return this.meta.metadata = Asset.normalizeMetadata(req.data);
1172 }
1173
1174 async patchMetadata(metadata) {
1175 if (metadata.Workflow && false) {
1176 let req = await lib.makeAPIRequest({
1177 env: this.remote,
1178 path: `/movies/${this.id}/metadata/Workflow`,
1179 method: "PATCH",
1180 payload: {
1181 "data": {
1182 "type": "metadata",
1183 "attributes": {
1184 "metadata": metadata.Workflow
1185 }
1186 }
1187 }
1188 });
1189 }
1190
1191 if (metadata.Metadata) {
1192 let req = await lib.makeAPIRequest({
1193 env: this.remote,
1194 path: `/movies/${this.id}/metadata/Metadata`,
1195 method: "PATCH",
1196 payload: {
1197 "data": {
1198 "type": "metadata",
1199 "attributes": {
1200 "metadata": metadata.Metadata
1201 }
1202 }
1203 }
1204 });
1205 }
1206 }
1207
1208 static lite(id, remote) {
1209 return new this({
1210 data: {
1211 id
1212 },
1213 remote,
1214 lite: true
1215 });
1216 }
1217
1218 chalkPrint(pad = false) {
1219 let id = String("A-" + (this.remote && this.remote + "-" + this.id || "LOCAL"));
1220 if (pad) id = id.padStart(15);
1221 return chalk`{green ${id}}: {blue ${this.data.attributes ? this.name : "(lite asset)"}}`;
1222 }
1223
1224 static async createNew(name, env) {
1225 let req = await lib.makeAPIRequest({
1226 env,
1227 path: "/assets",
1228 method: "POST",
1229 payload: {
1230 data: {
1231 attributes: {
1232 name
1233 },
1234 type: "assets"
1235 }
1236 }
1237 });
1238 return new this({
1239 data: req.data,
1240 remote: env
1241 });
1242 }
1243
1244 async delete() {
1245 let req = await lib.makeAPIRequest({
1246 env: this.remote,
1247 path: "/assets/" + this.id,
1248 method: "DELETE"
1249 });
1250 }
1251
1252 async getFiles() {
1253 let req = await lib.indexPathFast({
1254 env: this.remote,
1255 path: `/assets/${this.id}/files`,
1256 method: "GET"
1257 }); //return req;
1258
1259 return new Collection(req.map(x => new File({
1260 data: x,
1261 remote: this.remote,
1262 parent: this
1263 })));
1264 }
1265
1266 async addFile(label, fileuris) {
1267 if (!Array.isArray(fileuris)) fileuris = [fileuris];
1268 let instances = {};
1269
1270 for (let i = 0; i < fileuris.length; i++) {
1271 instances[String(i + 1)] = {
1272 uri: fileuris[i]
1273 };
1274 }
1275
1276 let req = await lib.makeAPIRequest({
1277 env: this.remote,
1278 path: "/files",
1279 method: "POST",
1280 payload: {
1281 "data": {
1282 "attributes": {
1283 label,
1284 instances
1285 },
1286 "relationships": {
1287 "asset": {
1288 "data": {
1289 id: this.id,
1290 "type": "assets"
1291 }
1292 }
1293 },
1294 "type": "files"
1295 }
1296 }
1297 });
1298 return req;
1299 }
1300
1301 async startWorkflow(jobName, {
1302 initData,
1303 priority
1304 } = {}) {
1305 let attributes = {};
1306
1307 if (initData) {
1308 //Convert init data to string
1309 initData = typeof initData === "string" ? initData : JSON.stringify(initData);
1310 attributes.initData = initData;
1311 }
1312
1313 if (priority) {
1314 attributes.priority = priority;
1315 }
1316
1317 let req = await lib.makeAPIRequest({
1318 env: this.remote,
1319 path: "/workflows",
1320 method: "POST",
1321 payload: {
1322 "data": {
1323 "type": "workflows",
1324 attributes,
1325 "relationships": {
1326 "movie": {
1327 "data": {
1328 id: this.id,
1329 "type": "movies"
1330 }
1331 },
1332 "rule": {
1333 "data": {
1334 "attributes": {
1335 "name": jobName
1336 },
1337 "type": "rules"
1338 }
1339 }
1340 }
1341 }
1342 }
1343 });
1344 return req;
1345 }
1346
1347 static async startAnonWorkflow(env, jobName, {
1348 initData,
1349 priority
1350 } = {}) {
1351 let attributes = {};
1352
1353 if (initData) {
1354 //Convert init data to string
1355 initData = typeof initData === "string" ? initData : JSON.stringify(initData);
1356 attributes.initData = initData;
1357 }
1358
1359 if (priority) {
1360 attributes.priority = priority;
1361 }
1362
1363 let req = await lib.makeAPIRequest({
1364 env,
1365 path: "/workflows",
1366 method: "POST",
1367 payload: {
1368 "data": {
1369 "type": "workflows",
1370 attributes,
1371 "relationships": {
1372 "rule": {
1373 "data": {
1374 "attributes": {
1375 "name": jobName
1376 },
1377 "type": "rules"
1378 }
1379 }
1380 }
1381 }
1382 }
1383 });
1384 return req;
1385 }
1386
1387 async startEphemeralEvaluateIdeal(preset, dynamicPresetData) {
1388 let res;
1389 const env = this.remote;
1390 let provider = await Provider.getByName(this.remote, "SdviEvaluate");
1391 write(chalk`Starting ephemeral evaluate on ${this.chalkPrint(false)}...`); // Fire and forget.
1392
1393 let evalInfo = await lib.makeAPIRequest({
1394 env: this.remote,
1395 path: "/jobs",
1396 method: "POST",
1397 payload: {
1398 data: {
1399 attributes: {
1400 category: provider.category,
1401 providerTypeName: provider.name,
1402 rallyConfiguration: {},
1403 providerData: Buffer.from(preset.code, "binary").toString("base64"),
1404 dynamicPresetData
1405 },
1406 type: "jobs",
1407 relationships: {
1408 movie: {
1409 data: {
1410 id: this.id,
1411 type: "movies"
1412 }
1413 }
1414 }
1415 }
1416 }
1417 });
1418 write(" Waiting for finish...");
1419
1420 for (;;) {
1421 res = await lib.makeAPIRequest({
1422 env,
1423 path_full: evalInfo.data.links.self
1424 });
1425 write(".");
1426
1427 if (res.data.attributes.state == "Complete") {
1428 write(chalk`{green Done}...\n`);
1429 break;
1430 }
1431
1432 await sleep(300);
1433 }
1434
1435 return;
1436 }
1437
1438 async startEvaluate(presetid, dynamicPresetData) {
1439 // Fire and forget.
1440 let data = await lib.makeAPIRequest({
1441 env: this.remote,
1442 path: "/jobs",
1443 method: "POST",
1444 payload: {
1445 data: {
1446 type: "jobs",
1447 attributes: {
1448 dynamicPresetData
1449 },
1450 relationships: {
1451 movie: {
1452 data: {
1453 id: this.id,
1454 type: "movies"
1455 }
1456 },
1457 preset: {
1458 data: {
1459 id: presetid,
1460 type: "presets"
1461 }
1462 }
1463 }
1464 }
1465 }
1466 });
1467 return data;
1468 }
1469
1470 async rename(newName) {
1471 let req = await lib.makeAPIRequest({
1472 env: this.remote,
1473 path: `/assets/${this.id}`,
1474 method: "PATCH",
1475 payload: {
1476 data: {
1477 attributes: {
1478 name: newName
1479 },
1480 type: "assets"
1481 }
1482 }
1483 });
1484 this.name = newName;
1485 return req;
1486 }
1487
1488}
1489
1490defineAssoc(Asset, "id", "data.id");
1491defineAssoc(Asset, "name", "data.attributes.name");
1492defineAssoc(Asset, "remote", "meta.remote");
1493defineAssoc(Asset, "md", "meta.metadata");
1494defineAssoc(Asset, "lite", "meta.lite");
1495Asset.endpoint = "movies";
1496
1497let home;
1498
1499if (os.homedir) {
1500 home = os.homedir();
1501}
1502
1503const colon = /:/g;
1504const siloLike = /(silo\-\w+?)s?\/([^\/]+)\.([\w1234567890]+)$/g;
1505function pathTransform(path) {
1506 if (path.includes(":")) {
1507 //Ignore the first colon in window-like filesystems
1508 path = path.slice(0, 3) + path.slice(3).replace(colon, "--");
1509 }
1510
1511 if (configObject.invertedPath) {
1512 path = path.replace(siloLike, "$2-$1.$3");
1513 }
1514
1515 if (path.includes("\\342\\200\\220")) {
1516 path = path.replace("\\342\\200\\220", "‐");
1517 }
1518
1519 return path;
1520}
1521function readFileSync(path, options) {
1522 return fs__default.readFileSync(pathTransform(path), options);
1523} //Create writefilesync, with ability to create directory if it doesnt exist
1524
1525function writeFileSync(path$1, data, options, dircreated = false) {
1526 path$1 = pathTransform(path$1);
1527
1528 try {
1529 return fs__default.writeFileSync(path$1, data, options);
1530 } catch (e) {
1531 if (dircreated) throw e;
1532 let directory = path.dirname(path$1);
1533
1534 try {
1535 fs__default.statSync(directory);
1536 throw e;
1537 } catch (nodir) {
1538 fs__default.mkdirSync(directory);
1539 return writeFileSync(path$1, data, options, true);
1540 }
1541 }
1542}
1543
1544let exists = {};
1545
1546class Preset extends RallyBase {
1547 constructor({
1548 path: path$1,
1549 remote,
1550 data,
1551 subProject
1552 } = {}) {
1553 // Get full path if possible
1554 if (path$1) {
1555 path$1 = path.resolve(path$1);
1556
1557 if (path.dirname(path$1).includes("silo-metadata")) {
1558 throw new AbortError("Constructing preset from metadata file");
1559 }
1560 }
1561
1562 super(); // Cache by path
1563
1564 if (path$1) {
1565 if (exists[pathTransform(path$1)]) return exists[pathTransform(path$1)];
1566 exists[pathTransform(path$1)] = this;
1567 }
1568
1569 this.meta = {};
1570 this.subproject = subProject;
1571 this.remote = remote;
1572
1573 if (lib.isLocalEnv(this.remote)) {
1574 if (path$1) {
1575 this.path = path$1;
1576 let pathspl = this.path.split(".");
1577 this.ext = pathspl[pathspl.length - 1];
1578
1579 try {
1580 this.code = this.getLocalCode();
1581 } catch (e) {
1582 if (e.code === "ENOENT" && configObject.ignoreMissing) {
1583 this.missing = true;
1584 return undefined;
1585 } else {
1586 log(chalk`{red Node Error} ${e.message}`);
1587 throw new AbortError("Could not load code of local file");
1588 }
1589 }
1590
1591 let name = this.parseFilenameForName() || this.parseCodeForName();
1592
1593 try {
1594 this.data = this.getLocalMetadata();
1595 this.isGeneric = true;
1596 name = this.name;
1597 } catch (e) {
1598 log(chalk`{yellow Warning}: ${path$1} does not have a readable metadata file! Looking for ${this.localmetadatapath}`);
1599 this.data = Preset.newShell(name);
1600 this.isGeneric = false;
1601 }
1602
1603 this.name = name;
1604 } else {
1605 this.data = Preset.newShell();
1606 }
1607 } else {
1608 this.data = data; //this.name = data.attributes.name;
1609 //this.id = data.id;
1610
1611 this.isGeneric = false;
1612 }
1613
1614 this.data.attributes.rallyConfiguration = undefined;
1615 this.data.attributes.systemManaged = undefined;
1616 } //Given a metadata file, get its actualy file
1617
1618
1619 static async fromMetadata(path, subproject) {
1620 let data;
1621
1622 try {
1623 data = JSON.parse(readFileSync(path));
1624 } catch (e) {
1625 if (e.code === "ENOENT" && configObject.ignoreMissing) {
1626 return null;
1627 } else {
1628 throw e;
1629 }
1630 }
1631
1632 let providerType = data.relationships.providerType.data.name;
1633 let provider = await Provider.getByName("DEV", providerType);
1634
1635 if (!provider) {
1636 log(chalk`{red The provider type {green ${providerType}} does not exist}`);
1637 log(chalk`{red Skipping {green ${path}}.}`);
1638 return null;
1639 }
1640
1641 let ext = await provider.getFileExtension();
1642 let name = data.attributes.name;
1643 let realpath = Preset.getLocalPath(name, ext, subproject);
1644 return new Preset({
1645 path: realpath,
1646 subProject: subproject
1647 });
1648 }
1649
1650 static newShell(name = undefined) {
1651 return {
1652 "attributes": {
1653 "providerSettings": {
1654 "PresetName": name
1655 }
1656 },
1657 "relationships": {},
1658 "type": "presets"
1659 };
1660 }
1661
1662 cleanup() {
1663 super.cleanup();
1664 delete this.attributes["createdAt"];
1665 delete this.attributes["updatedAt"];
1666 }
1667
1668 async acclimatize(env) {
1669 if (!this.isGeneric) throw new AbortError("Cannot acclimatize non-generics or shells");
1670 let providers = await Provider.getAll(env);
1671 let ptype = this.relationships["providerType"];
1672 ptype = ptype.data;
1673 let provider = providers.findByName(ptype.name);
1674 ptype.id = provider.id;
1675 }
1676
1677 get test() {
1678 if (!this.code) return [];
1679 const regex = /[^-]autotest:\s?([\w\d_\-. \/]+)[\r\s\n]*?/gm;
1680 let match;
1681 let matches = [];
1682
1683 while (match = regex.exec(this.code)) {
1684 matches.push(match[1]);
1685 }
1686
1687 return matches;
1688 }
1689
1690 async runTest(env) {
1691 let remote = await Preset.getByName(env, this.name);
1692
1693 for (let test of this.test) {
1694 log("Tests...");
1695 let asset;
1696
1697 if (test.startsWith("id")) {
1698 let match = /id:\s*(\d+)/g.exec(test);
1699
1700 if (!match) {
1701 log(chalk`{red Could not parse autotest} ${test}.`);
1702 throw new AbortError("Could not properly parse the preset header");
1703 }
1704
1705 asset = await Asset.getById(env, match[1]);
1706 } else {
1707 asset = await Asset.getByName(env, test);
1708 }
1709
1710 if (!asset) {
1711 log(chalk`{yellow No movie found}, skipping test.`);
1712 continue;
1713 }
1714
1715 log(chalk`Starting job {green ${this.name}} on ${asset.chalkPrint(false)}... `);
1716 await asset.startEvaluate(remote.id);
1717 }
1718 }
1719
1720 async resolve() {
1721 if (this.isGeneric) return;
1722 let proType = await this.resolveField(Provider, "providerType");
1723 this.ext = await proType.getFileExtension();
1724 this.isGeneric = true;
1725 return {
1726 proType
1727 };
1728 }
1729
1730 async saveLocal() {
1731 await this.saveLocalMetadata();
1732 await this.saveLocalFile();
1733 }
1734
1735 async saveLocalMetadata() {
1736 if (!this.isGeneric) {
1737 await this.resolve();
1738 this.cleanup();
1739 }
1740
1741 writeFileSync(this.localmetadatapath, JSON.stringify(this.data, null, 4));
1742 }
1743
1744 async saveLocalFile() {
1745 writeFileSync(this.localpath, this.code);
1746 }
1747
1748 async uploadRemote(env) {
1749 await this.uploadCodeToEnv(env, true);
1750 }
1751
1752 async save(env) {
1753 this.saved = true;
1754
1755 if (!this.isGeneric) {
1756 await this.resolve();
1757 }
1758
1759 this.cleanup();
1760
1761 if (lib.isLocalEnv(env)) {
1762 log(chalk`Saving preset {green ${this.name}} to {blue ${lib.envName(env)}}.`);
1763 await this.saveLocal();
1764 } else {
1765 await this.uploadRemote(env);
1766 }
1767 }
1768
1769 async downloadCode() {
1770 if (!this.remote || this.code) return this.code;
1771 let code = await lib.makeAPIRequest({
1772 env: this.remote,
1773 path_full: this.data.links.providerData,
1774 json: false
1775 }); //match header like
1776 // # c: d
1777 // # b
1778 // # a
1779 // ##################
1780
1781 let headerRegex = /(^# .+[\r\n]+)+#+[\r\n]+/gim;
1782 let hasHeader = headerRegex.exec(code);
1783
1784 if (hasHeader) {
1785 this.header = code.substring(0, hasHeader[0].length - 1);
1786 code = code.substring(hasHeader[0].length);
1787 }
1788
1789 return this.code = code;
1790 }
1791
1792 get code() {
1793 if (this._code) return this._code;
1794 }
1795
1796 set code(v) {
1797 this._code = v;
1798 }
1799
1800 chalkPrint(pad = true) {
1801 let id = String("P-" + (this.remote && this.remote + "-" + this.id || "LOCAL"));
1802 let sub = "";
1803
1804 if (this.subproject) {
1805 sub = chalk`{yellow ${this.subproject}}`;
1806 }
1807
1808 if (pad) id = id.padStart(10);
1809
1810 if (this.name == undefined) {
1811 return chalk`{green ${id}}: ${sub}{red ${this.path}}`;
1812 } else if (this.meta.proType) {
1813 return chalk`{green ${id}}: ${sub}{red ${this.meta.proType.name}} {blue ${this.name}}`;
1814 } else {
1815 return chalk`{green ${id}}: ${sub}{blue ${this.name}}`;
1816 }
1817 }
1818
1819 parseFilenameForName() {
1820 if (this.path.endsWith(".jinja") || this.path.endsWith(".json")) {
1821 return path.basename(this.path).replace("_", " ").replace("-", " ").replace(".json", "").replace(".jinja", "");
1822 }
1823 }
1824
1825 parseCodeForName() {
1826 const name_regex = /name\s?:\s([\w\d. \/]+)[\r\s\n]*?/;
1827 const match = name_regex.exec(this.code);
1828 if (match) return match[1];
1829 }
1830
1831 findStringsInCode(strings) {
1832 if (!this.code) return [];
1833 return strings.filter(str => {
1834 let regex = new RegExp(str);
1835 return !!this.code.match(regex);
1836 });
1837 }
1838
1839 static getLocalPath(name, ext, subproject) {
1840 return path__default.join(configObject.repodir, subproject || "", "silo-presets", name + "." + ext);
1841 }
1842
1843 get localpath() {
1844 return Preset.getLocalPath(this.name, this.ext, this.subproject);
1845 }
1846
1847 get path() {
1848 if (this._path) return this._path;
1849 }
1850
1851 set path(val) {
1852 this._path = val;
1853 }
1854
1855 get name() {
1856 return this._nameOuter;
1857 }
1858
1859 set name(val) {
1860 if (!this._nameInner) this._nameInner = val;
1861 this._nameOuter = val;
1862 }
1863
1864 set providerType(value) {
1865 this.relationships["providerType"] = {
1866 data: { ...value,
1867 type: "providerTypes"
1868 }
1869 };
1870 }
1871
1872 get localmetadatapath() {
1873 if (this.path) {
1874 return this.path.replace("silo-presets", "silo-metadata").replace(new RegExp(this.ext + "$"), "json");
1875 }
1876
1877 return path__default.join(configObject.repodir, this.subproject || "", "silo-metadata", this.name + ".json");
1878 }
1879
1880 get immutable() {
1881 return this.name.includes("Constant") && !configObject.updateImmutable;
1882 }
1883
1884 async uploadPresetData(env, id) {
1885 var _this$relationships, _this$relationships$p, _this$relationships$p2;
1886
1887 if (this.code.trim() === "NOUPLOAD") {
1888 write(chalk`code skipped {yellow :)}, `);
1889 return;
1890 }
1891
1892 let code = this.code;
1893 let headers = {};
1894 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;
1895
1896 if (!configObject.skipHeader && (providerName === "SdviEvaluate" || providerName === "SdviEvalPro")) {
1897 write(chalk`generate header, `);
1898 let repodir = configObject.repodir;
1899 let localpath = this.path.replace(repodir, "");
1900 if (localpath.startsWith("/")) localpath = localpath.substring(1);
1901
1902 try {
1903 let {
1904 stdout: headerText
1905 } = await spawn({
1906 noecho: true
1907 }, "sh", [path__default.join(configObject.repodir, `bin/header.sh`), moment(Date.now()).format("ddd YYYY/MM/DD hh:mm:ssa"), localpath]);
1908 code = headerText + code;
1909 write(chalk`header ok, `);
1910 } catch (e) {
1911 write(chalk`missing unix, `);
1912 }
1913 } //binary presets
1914
1915
1916 if (providerName == "Vantage") {
1917 code = code.toString("base64");
1918 headers["Content-Transfer-Encoding"] = "base64";
1919 }
1920
1921 let res = await lib.makeAPIRequest({
1922 env,
1923 path: `/presets/${id}/providerData`,
1924 body: code,
1925 method: "PUT",
1926 fullResponse: true,
1927 timeout: 10000,
1928 headers
1929 });
1930 write(chalk`code up {yellow ${res.statusCode}}, `);
1931 }
1932
1933 async grabMetadata(env) {
1934 let remote = await Preset.getByName(env, this.name);
1935 this.isGeneric = false;
1936
1937 if (!remote) {
1938 throw new AbortError(`No file found on remote ${env} with name ${this.name}`);
1939 }
1940
1941 this.data = remote.data;
1942 this.remote = env;
1943 }
1944
1945 async deleteRemoteVersion(env, id = null) {
1946 if (lib.isLocalEnv(env)) return false;
1947
1948 if (!id) {
1949 let remote = await Preset.getByName(env, this.name);
1950 id = remote.id;
1951 }
1952
1953 return await lib.makeAPIRequest({
1954 env,
1955 path: `/presets/${id}`,
1956 method: "DELETE"
1957 });
1958 }
1959
1960 async delete() {
1961 if (lib.isLocalEnv(this.remote)) return false;
1962 return await this.deleteRemoteVersion(this.remote, this.id);
1963 }
1964
1965 async uploadCodeToEnv(env, includeMetadata, shouldTest = true) {
1966 if (!this.name) {
1967 let match;
1968
1969 if (match = /^(#|["']{3})\s*EPH (\d+)/.exec(this.code.trim())) {
1970 let a = await Asset.getById(env, Number(match[2]));
1971 return a.startEphemeralEvaluateIdeal(this);
1972 } else {
1973 log(chalk`Failed uploading {red ${this.path}}. No name found.`);
1974 return;
1975 }
1976 }
1977
1978 write(chalk`Uploading preset {green ${this.name}} to {green ${env}}: `);
1979
1980 if (this.immutable) {
1981 log(chalk`{magenta IMMUTABLE}. Nothing to do.`);
1982 return;
1983 } //First query the api to see if this already exists.
1984
1985
1986 let remote = await Preset.getByName(env, this.name);
1987
1988 if (remote) {
1989 //If it exists we can replace it
1990 write("replace, ");
1991
1992 if (includeMetadata) {
1993 let payload = {
1994 data: {
1995 attributes: this.data.attributes,
1996 type: "presets"
1997 }
1998 };
1999
2000 if (this.relationships.tagNames) {
2001 payload.relationships = {
2002 tagNames: this.relationships.tagNames
2003 };
2004 }
2005
2006 let res = await lib.makeAPIRequest({
2007 env,
2008 path: `/presets/${remote.id}`,
2009 method: "PATCH",
2010 payload,
2011 fullResponse: true
2012 });
2013 write(chalk`metadata {yellow ${res.statusCode}}, `);
2014
2015 if (res.statusCode == 500) {
2016 log(chalk`skipping code upload, did not successfully upload metadata`);
2017 return;
2018 }
2019 }
2020
2021 await this.uploadPresetData(env, remote.id);
2022 } else {
2023 write("create, ");
2024 let metadata = {
2025 data: this.data
2026 };
2027
2028 if (!this.relationships["providerType"]) {
2029 throw new AbortError("Cannot acclimatize shelled presets. (try creating it on the env first)");
2030 }
2031
2032 await this.acclimatize(env);
2033 write("Posting to create preset... ");
2034 let res = await lib.makeAPIRequest({
2035 env,
2036 path: `/presets`,
2037 method: "POST",
2038 payload: metadata,
2039 timeout: 5000
2040 });
2041 let id = res.data.id;
2042 write(chalk`Created id {green ${id}}... Uploading Code... `);
2043 await this.uploadPresetData(env, id);
2044 }
2045
2046 if (this.test[0] && shouldTest) {
2047 await this.runTest(env);
2048 } else {
2049 log("No tests. Done.");
2050 }
2051 }
2052
2053 getLocalMetadata() {
2054 return JSON.parse(readFileSync(this.localmetadatapath, "utf-8"));
2055 }
2056
2057 getLocalCode() {
2058 //todo fixup for binary presets, see uploadPresetData
2059 return readFileSync(this.path, "utf-8");
2060 }
2061
2062 parseHeaderInfo() {
2063 var _$exec$, _$exec$2, _$exec$3, _$exec$4, _$exec$5, _$exec$6, _$exec$7;
2064
2065 if (!this.header) return null;
2066 let abs = {
2067 built: (_$exec$ = /Built On:(.+)/.exec(this.header)[1]) === null || _$exec$ === void 0 ? void 0 : _$exec$.trim(),
2068 author: (_$exec$2 = /Author:(.+)/.exec(this.header)[1]) === null || _$exec$2 === void 0 ? void 0 : _$exec$2.trim(),
2069 build: (_$exec$3 = /Build:(.+)/.exec(this.header)[1]) === null || _$exec$3 === void 0 ? void 0 : _$exec$3.trim(),
2070 version: (_$exec$4 = /Version:(.+)/.exec(this.header)[1]) === null || _$exec$4 === void 0 ? void 0 : _$exec$4.trim(),
2071 branch: (_$exec$5 = /Branch:(.+)/.exec(this.header)[1]) === null || _$exec$5 === void 0 ? void 0 : _$exec$5.trim(),
2072 commit: (_$exec$6 = /Commit:(.+)/.exec(this.header)[1]) === null || _$exec$6 === void 0 ? void 0 : _$exec$6.trim(),
2073 local: (_$exec$7 = /Local File:(.+)/.exec(this.header)[1]) === null || _$exec$7 === void 0 ? void 0 : _$exec$7.trim()
2074 };
2075 let tryFormats = [[true, "ddd MMM DD HH:mm:ss YYYY"], [false, "ddd YYYY/MM/DD LTS"]];
2076
2077 for (let [isUTC, format] of tryFormats) {
2078 let date;
2079
2080 if (isUTC) {
2081 date = moment.utc(abs.built, format);
2082 } else {
2083 date = moment(abs.built, format);
2084 }
2085
2086 if (!date.isValid()) continue;
2087 abs.offset = date.fromNow();
2088 break;
2089 }
2090
2091 return abs;
2092 }
2093
2094 async printRemoteInfo(env) {
2095 let remote = await Preset.getByName(env, this.name);
2096 await remote.downloadCode();
2097 let i = remote.parseHeaderInfo();
2098
2099 if (i) {
2100 log(chalk`
2101 ENV: {red ${env}}, updated {yellow ~${i.offset}}
2102 Built on {blue ${i.built}} by {green ${i.author}}
2103 From ${i.build || "(unknown)"} on ${i.branch} ({yellow ${i.commit}})
2104 `.replace(/^[ \t]+/gim, "").trim());
2105 } else {
2106 log(chalk`No header on {red ${env}}`);
2107 }
2108 }
2109
2110 async getInfo(envs) {
2111 await this.printDepends();
2112
2113 for (let env of envs.split(",")) {
2114 await this.printRemoteInfo(env);
2115 }
2116 }
2117
2118 async printDepends(indent = 0, locals = null, seen = {}) {
2119 let includeRegex = /@include "(.+)"/gim;
2120 let includes = this.code.split("\n").map(x => includeRegex.exec(x)).filter(x => x).map(x => x[1]);
2121
2122 if (!locals) {
2123 locals = new Collection((await loadLocals("silo-presets", Preset)));
2124 }
2125
2126 log(Array(indent + 1).join(" ") + "- " + this.name);
2127
2128 for (let include of includes) {
2129 if (seen[include]) {
2130 log(Array(indent + 1).join(" ") + " - (seen) " + include);
2131 } else {
2132 seen[include] = true;
2133 await locals.findByName(include).printDepends(indent + 2, locals, seen);
2134 }
2135 }
2136 }
2137
2138}
2139
2140defineAssoc(Preset, "_nameInner", "data.attributes.providerSettings.PresetName");
2141defineAssoc(Preset, "_nameOuter", "data.attributes.name");
2142defineAssoc(Preset, "id", "data.id");
2143defineAssoc(Preset, "attributes", "data.attributes");
2144defineAssoc(Preset, "relationships", "data.relationships");
2145defineAssoc(Preset, "remote", "meta.remote");
2146defineAssoc(Preset, "_code", "meta.code");
2147defineAssoc(Preset, "_path", "meta.path");
2148defineAssoc(Preset, "isGeneric", "meta.isGeneric");
2149defineAssoc(Preset, "ext", "meta.ext");
2150defineAssoc(Preset, "subproject", "meta.project");
2151defineAssoc(Preset, "metastring", "meta.metastring");
2152Preset.endpoint = "presets";
2153
2154class Notification extends RallyBase {
2155 constructor({
2156 data,
2157 remote
2158 }) {
2159 super();
2160 this.data = data;
2161 this.meta = {};
2162 this.remote = remote;
2163 }
2164
2165 static async getAllPreCollect(notifications) {
2166 return notifications.sort((a, b) => {
2167 return a.attributes.type.localeCompare(b.attributes.type) || a.attributes.name.localeCompare(b.attributes.name);
2168 });
2169 }
2170
2171 chalkPrint(pad = false) {
2172 let id = String("N-" + this.id);
2173 if (pad) id = id.padStart(4);
2174 return chalk`{green ${id}}: {blue ${this.type}} - {green ${this.name}}`;
2175 }
2176
2177}
2178
2179defineAssoc(Notification, "id", "data.id");
2180defineAssoc(Notification, "name", "data.attributes.name");
2181defineAssoc(Notification, "address", "data.attributes.address");
2182defineAssoc(Notification, "type", "data.attributes.type");
2183defineAssoc(Notification, "remote", "meta.remote");
2184Notification.endpoint = "notificationPresets";
2185
2186class Rule extends RallyBase {
2187 constructor({
2188 path: path$1,
2189 data,
2190 remote,
2191 subProject
2192 } = {}) {
2193 super();
2194
2195 if (path$1) {
2196 path$1 = path.resolve(path$1);
2197
2198 try {
2199 let f = readFileSync(path$1, "utf-8");
2200 data = JSON.parse(readFileSync(path$1, "utf-8"));
2201 } catch (e) {
2202 if (e.code === "ENOENT") {
2203 if (configObject.ignoreMissing) {
2204 this.missing = true;
2205 return undefined;
2206 } else {
2207 throw new AbortError("Could not load code of local file");
2208 }
2209 } else {
2210 throw new AbortError(`Unreadable JSON in ${path$1}. ${e}`);
2211 }
2212 }
2213 }
2214
2215 this.meta = {};
2216 this.subproject = subProject;
2217
2218 if (!data) {
2219 data = Rule.newShell();
2220 }
2221
2222 this.data = data;
2223 this.remote = remote;
2224 this.isGeneric = !this.remote;
2225 }
2226
2227 static newShell() {
2228 return {
2229 "attributes": {
2230 "description": "-",
2231 "priority": "PriorityNorm",
2232 "starred": false
2233 },
2234 "relationships": {},
2235 "type": "workflowRules"
2236 };
2237 }
2238
2239 async acclimatize(env) {
2240 this.remote = env;
2241 let preset = await this.resolveField(Preset, "preset", false, "specific");
2242 let pNext = await this.resolveField(Rule, "passNext", false, "specific");
2243 let eNext = await this.resolveField(Rule, "errorNext", false, "specific");
2244 let proType = await this.resolveField(Provider, "providerType", false, "specific");
2245 let dynamicNexts = await this.resolveField(Rule, "dynamicNexts", true, "specific");
2246 let enterNotif = await this.resolveField(Notification, "enterNotifications", true, "specific");
2247 let errorNotif = await this.resolveField(Notification, "errorNotifications", true, "specific");
2248 let passNotif = await this.resolveField(Notification, "passNotifications", true, "specific");
2249 }
2250
2251 async saveA(env) {
2252 if (lib.isLocalEnv(env)) return;
2253 return await this.createIfNotExist(env);
2254 }
2255
2256 async saveB(env) {
2257 if (!this.isGeneric) {
2258 await this.resolve();
2259 }
2260
2261 this.cleanup();
2262
2263 if (lib.isLocalEnv(env)) {
2264 log(chalk`Saving rule {green ${this.name}} to {blue ${lib.envName(env)}}.`);
2265 writeFileSync(this.localpath, JSON.stringify(this.data, null, 4));
2266 } else {
2267 await this.acclimatize(env);
2268 await this.uploadRemote(env);
2269 }
2270 }
2271
2272 get immutable() {
2273 return false;
2274 }
2275
2276 async createIfNotExist(env) {
2277 write(chalk`First pass rule {green ${this.name}} to {green ${env}}: `);
2278
2279 if (this.immutable) {
2280 log(chalk`{magenta IMMUTABLE}. Nothing to do.`);
2281 return;
2282 } //First query the api to see if this already exists.
2283
2284
2285 let remote = await Rule.getByName(env, this.name);
2286 this.idMap = this.idMap || {};
2287
2288 if (remote) {
2289 this.idMap[env] = remote.id;
2290 log(chalk`exists ${remote.chalkPrint(false)}`);
2291 return;
2292 } //If it exists we can replace it
2293
2294
2295 write("create, ");
2296 let res = await lib.makeAPIRequest({
2297 env,
2298 path: `/workflowRules`,
2299 method: "POST",
2300 payload: {
2301 data: {
2302 attributes: {
2303 name: this.name
2304 },
2305 type: "workflowRules"
2306 }
2307 }
2308 });
2309 this.idMap = this.idMap || {};
2310 this.idMap[env] = res.data.id;
2311 write("id ");
2312 log(this.idMap[env]);
2313 }
2314
2315 async patchStrip() {
2316 delete this.data.attributes.createdAt;
2317 delete this.data.attributes.starred;
2318 delete this.data.attributes.updatedAt; // TEMP FIX FOR BUG IN SDVI
2319
2320 if (this.relationships.passMetadata && this.relationships.passMetadata[0]) {
2321 log("HAS PASS");
2322 log(this.name);
2323 log("HAS PASS");
2324 }
2325
2326 delete this.relationships.passMetadata;
2327
2328 if (this.relationships.errorMetadata && this.relationships.errorMetadata[0]) {
2329 log("HAS PASS");
2330 log(this.name);
2331 log("HAS PASS");
2332 }
2333
2334 delete this.relationships.errorMetadata; // This is commented out because it was fixed.
2335 //for(let key in this.relationships){
2336 //let relationship = this.relationships[key];
2337 //if(!relationship.data || relationship.data instanceof Array && !relationship.data[0]){
2338 //delete this.relationships[key];
2339 //}
2340 //}
2341 }
2342
2343 async uploadRemote(env) {
2344 write(chalk`Uploading rule {green ${this.name}} to {green ${env}}: `);
2345
2346 if (this.immutable) {
2347 log(chalk`{magenta IMMUTABLE}. Nothing to do.`);
2348 return;
2349 }
2350
2351 if (this.idMap[env]) {
2352 this.remote = env;
2353 await this.patchStrip();
2354 this.data.id = this.idMap[env]; //If it exists we can replace it
2355
2356 write("replace, ");
2357 let res = await lib.makeAPIRequest({
2358 env,
2359 path: `/workflowRules/${this.idMap[env]}`,
2360 method: "PATCH",
2361 payload: {
2362 data: this.data
2363 },
2364 fullResponse: true
2365 });
2366 log(chalk`response {yellow ${res.statusCode}}`);
2367
2368 if (res.statusCode !== 200) {
2369 log(res.body);
2370 log(JSON.stringify(this.data, null, 4));
2371 }
2372 } else {
2373 throw Error("Bad idmap!");
2374 }
2375 }
2376
2377 get localpath() {
2378 return path.join(configObject.repodir, this.subproject || "", "silo-rules", this.name + ".json");
2379 }
2380
2381 async resolve() {
2382 let preset = await this.resolveField(Preset, "preset", false); //log(preset);
2383
2384 let pNext = await this.resolveField(Rule, "passNext", false);
2385 let eNext = await this.resolveField(Rule, "errorNext", false);
2386 let proType = await this.resolveField(Provider, "providerType", false); //log("Dynamic nexts")
2387
2388 let dynamicNexts = await this.resolveField(Rule, "dynamicNexts", true); //log(dynamicNexts);
2389
2390 let enterNotif = await this.resolveField(Notification, "enterNotifications", true);
2391 let errorNotif = await this.resolveField(Notification, "errorNotifications", true);
2392 let passNotif = await this.resolveField(Notification, "passNotifications", true); //TODO Unsupported
2393
2394 delete this.relationships["enterMetadata"];
2395 delete this.relationships["errorMetadata"];
2396 this.isGeneric = true;
2397 return {
2398 preset,
2399 proType,
2400 pNext,
2401 eNext,
2402 dynamicNexts,
2403 errorNotif,
2404 enterNotif,
2405 passNotif
2406 };
2407 }
2408
2409 chalkPrint(pad = true) {
2410 let id = String("R-" + (this.remote && this.remote + "-" + this.id || "LOCAL"));
2411 let sub = "";
2412
2413 if (this.subproject) {
2414 sub = chalk`{yellow ${this.subproject}}`;
2415 }
2416
2417 if (pad) id = id.padStart(10);
2418
2419 try {
2420 return chalk`{green ${id}}: ${sub}{blue ${this.name}}`;
2421 } catch (e) {
2422 return this.data;
2423 }
2424 }
2425
2426}
2427
2428defineAssoc(Rule, "name", "data.attributes.name");
2429defineAssoc(Rule, "description", "data.attributes.description");
2430defineAssoc(Rule, "id", "data.id");
2431defineAssoc(Rule, "relationships", "data.relationships");
2432defineAssoc(Rule, "isGeneric", "meta.isGeneric");
2433defineAssoc(Rule, "remote", "meta.remote");
2434defineAssoc(Rule, "subproject", "meta.project");
2435defineAssoc(Rule, "idMap", "meta.idMap");
2436Rule.endpoint = "workflowRules";
2437
2438//Move project into silo metadata
2439//move autotest into silo metadata
2440//
2441
2442class SupplyChain {
2443 constructor(startingRule, stopRule) {
2444 if (startingRule) {
2445 this.startingRule = startingRule;
2446 this.stopRule = stopRule;
2447 this.remote = startingRule.remote;
2448 }
2449 }
2450
2451 async downloadPresetCode(objs = this.allPresets) {
2452 log("Downloading code... ");
2453 await lib.keepalive(objs.arr.map(x => () => x.downloadCode()));
2454 }
2455
2456 async calculate() {
2457 log("Getting rules... ");
2458 this.allRules = await Rule.getAll(this.remote);
2459 log(this.allRules.length);
2460 log("Getting presets... ");
2461 this.allPresets = await Preset.getAll(this.remote);
2462 log(this.allPresets.length);
2463 log("Getting providers... ");
2464 this.allProviders = await Provider.getAll(this.remote);
2465 log(this.allProviders.length);
2466 log("Getting notifications... ");
2467 this.allNotifications = await Notification.getAll(this.remote);
2468 log(this.allNotifications.length);
2469
2470 if (!this.startingRule) {
2471 this.rules = this.allRules;
2472 this.presets = this.allPresets;
2473 this.notifications = new Collection([]);
2474 await this.downloadPresetCode();
2475 return;
2476 } else {
2477 await this.downloadPresetCode();
2478 }
2479
2480 log("Done!"); //Now we have everything we need to find a whole supply chain
2481
2482 write("Calculating Supply chain... ");
2483 log(this.startingRule.chalkPrint());
2484 let allRuleNames = this.allRules.arr.map(x => x.name).filter(x => x.length >= 4);
2485 let allPresetNames = this.allPresets.arr.map(x => x.name).filter(x => x.length >= 4);
2486 let allNotifNames = this.allNotifications.arr.map(x => x.name).filter(x => x.length >= 4);
2487 let requiredNotifications = new Set();
2488 let ruleQueue = [this.startingRule];
2489 let presetQueue = [];
2490
2491 for (let currentRule of ruleQueue) {
2492 if (currentRule === this.stopRule) continue;
2493 let {
2494 eNext,
2495 pNext,
2496 preset,
2497 passNotif,
2498 errorNotif,
2499 enterNotif
2500 } = await currentRule.resolve();
2501 passNotif.forEach(n => requiredNotifications.add(n));
2502 enterNotif.forEach(n => requiredNotifications.add(n));
2503 errorNotif.forEach(n => requiredNotifications.add(n));
2504 if (eNext && !ruleQueue.includes(eNext)) ruleQueue.push(eNext);
2505 if (pNext && !ruleQueue.includes(eNext)) ruleQueue.push(pNext);
2506 let neededPresets = preset.findStringsInCode(allPresetNames);
2507 neededPresets = neededPresets.map(x => this.allPresets.findByName(x));
2508 let neededRules = preset.findStringsInCode(allRuleNames);
2509 neededRules = neededRules.map(x => this.allRules.findByName(x));
2510 preset.findStringsInCode(allNotifNames).map(str => this.allNotifications.findByName(str)).forEach(notif => requiredNotifications.add(notif));
2511 neededPresets.push(preset);
2512
2513 for (let p of neededPresets) if (!presetQueue.includes(p)) presetQueue.push(p);
2514
2515 for (let p of neededRules) if (!ruleQueue.includes(p)) ruleQueue.push(p);
2516
2517 if (configObject.verbose) {
2518 write(currentRule.chalkPrint(false));
2519 log(":");
2520 write(" ");
2521 write(preset.chalkPrint(false));
2522 log(":");
2523 write(" Pass Next: ");
2524 if (pNext) write(pNext.chalkPrint(false));else write("None");
2525 log("");
2526 write(" Err Next: ");
2527 if (eNext) write(eNext.chalkPrint(false));else write("None");
2528 log("");
2529 log(" Rules:");
2530
2531 for (let p of neededRules) log(" " + p.chalkPrint(true));
2532
2533 log(" Presets:");
2534
2535 for (let p of neededPresets) log(" " + p.chalkPrint(true));
2536
2537 log("\n");
2538 }
2539 }
2540
2541 log("Done!");
2542 this.rules = new Collection(ruleQueue);
2543 this.presets = new Collection(presetQueue);
2544 requiredNotifications.delete(undefined);
2545 this.notifications = new Collection([...requiredNotifications]);
2546 }
2547
2548 async log() {
2549 if (this.notifications.arr.length > 0) {
2550 log("Required notifications: ");
2551 this.notifications.log();
2552 }
2553
2554 if (this.rules.arr.length > 0) {
2555 write("Required rules: ");
2556 log(this.rules.arr.length);
2557 this.rules.log();
2558 }
2559
2560 if (this.presets.arr.length > 0) {
2561 write("Required presets: ");
2562 log(this.presets.arr.length);
2563 this.presets.log();
2564 }
2565
2566 if (configObject.rawOutput) {
2567 return {
2568 presets: this.presets.arr,
2569 rules: this.rules.arr,
2570 notifications: this.notifications.arr
2571 };
2572 }
2573 }
2574
2575 async deleteTo(env) {
2576 for (let preset of this.presets) {
2577 try {
2578 await preset.deleteRemoteVersion(env);
2579 } catch (e) {
2580 log(e);
2581 }
2582 }
2583 }
2584
2585 async syncTo(env) {
2586 for (let preset of this.presets) {
2587 try {
2588 await preset.save(env);
2589 } catch (e) {
2590 log(e);
2591 }
2592 }
2593
2594 if (this.rules.arr[0]) {
2595 log("Starting create phase for rules");
2596
2597 for (let rule of this.rules) {
2598 try {
2599 await rule.saveA(env);
2600 } catch (e) {
2601 log(e);
2602 }
2603 }
2604
2605 log("OK");
2606 log("Starting link phase for rules");
2607 Rule.removeCache(env);
2608
2609 for (let rule of this.rules) {
2610 try {
2611 await rule.saveB(env);
2612 } catch (e) {
2613 log(e);
2614 }
2615 }
2616 }
2617 }
2618
2619}
2620
2621class User extends RallyBase {
2622 constructor({
2623 data,
2624 remote
2625 }) {
2626 super();
2627 this.data = data;
2628 this.meta = {};
2629 this.remote = remote;
2630 }
2631
2632 chalkPrint(pad = false) {
2633 let id = String("U-" + this.id);
2634 if (pad) id = id.padStart(7);
2635 return chalk`{green ${id}}: {blue ${this.name}}`;
2636 }
2637
2638}
2639
2640defineAssoc(User, "id", "data.id");
2641defineAssoc(User, "name", "data.attributes.name");
2642defineAssoc(User, "email", "data.attributes.email");
2643defineAssoc(User, "remote", "meta.remote");
2644User.endpoint = "users";
2645
2646class Tag extends RallyBase {
2647 constructor({
2648 data,
2649 remote
2650 } = {}) {
2651 super();
2652 this.meta = {};
2653 this.remote = remote;
2654 this.data = data; //this.data.attributes.rallyConfiguration = undefined;
2655 //this.data.attributes.systemManaged = undefined;
2656 }
2657
2658 chalkPrint(pad = true) {
2659 let id = String("T-" + this.remote + "-" + this.id);
2660 if (pad) id = id.padStart(10);
2661 let prefix = this.curated ? "blue +" : "red -";
2662 return chalk`{green ${id}}: {${prefix}${this.name}}`;
2663 }
2664
2665 static async create(env, name, {
2666 notCurated
2667 } = {}) {
2668 return new Tag({
2669 data: await lib.makeAPIRequest({
2670 env,
2671 path: `/${this.endpoint}`,
2672 method: "POST",
2673 payload: {
2674 data: {
2675 attributes: {
2676 name,
2677 curated: notCurated ? false : true
2678 },
2679 type: "tagNames"
2680 }
2681 }
2682 }),
2683 remote: env
2684 });
2685 }
2686
2687}
2688
2689defineAssoc(Tag, "id", "data.id");
2690defineAssoc(Tag, "attributes", "data.attributes");
2691defineAssoc(Tag, "relationships", "data.relationships");
2692defineAssoc(Tag, "name", "data.attributes.name");
2693defineAssoc(Tag, "curated", "data.attributes.curated");
2694defineAssoc(Tag, "remote", "meta.remote");
2695Tag.endpoint = "tagNames";
2696
2697async function findLineInFile(renderedPreset, lineNumber) {
2698 let trueFileLine = lineNumber;
2699 let linedRenderedPreset = renderedPreset.split("\n").slice(2, -2);
2700 renderedPreset = renderedPreset.split("\n").slice(2, -2).join("\n");
2701 let includeLocation = renderedPreset.split("\n").filter(x => x.includes("@include"));
2702 let endIncludeNumber = -1,
2703 addTabDepth = 2;
2704 let lineBeforeIncludeStatement = '';
2705 let withinInclude = true;
2706
2707 if (lineNumber > linedRenderedPreset.indexOf(includeLocation[includeLocation.length - 1])) {
2708 addTabDepth = 0;
2709 withinInclude = false;
2710 }
2711
2712 for (let index = includeLocation.length - 1; index >= 0; index--) {
2713 let currIncludeIndex = linedRenderedPreset.indexOf(includeLocation[index]);
2714 let tabDepth = includeLocation[index].split(" ").length;
2715
2716 if (lineNumber > currIncludeIndex) {
2717 if (includeLocation[index].split(" ").filter(Boolean)[1] != "ERROR:") {
2718 if (lineBeforeIncludeStatement.split(" ").length == tabDepth && withinInclude) {
2719 trueFileLine = trueFileLine - currIncludeIndex;
2720 break;
2721 } else if (lineBeforeIncludeStatement.split(" ").length + addTabDepth == tabDepth && endIncludeNumber == -1) {
2722 endIncludeNumber = currIncludeIndex;
2723 } else if (lineBeforeIncludeStatement.split(" ").length + addTabDepth == tabDepth) {
2724 trueFileLine = trueFileLine - (endIncludeNumber - currIncludeIndex);
2725 endIncludeNumber = -1;
2726 }
2727 }
2728 } else {
2729 lineBeforeIncludeStatement = includeLocation[index];
2730 }
2731 }
2732
2733 let funcLine = "";
2734
2735 for (let line of linedRenderedPreset.slice(0, lineNumber).reverse()) {
2736 let match = /def (\w+)/.exec(line);
2737
2738 if (match) {
2739 funcLine = match[1];
2740 break;
2741 }
2742 }
2743
2744 let includeFilename;
2745
2746 if (lineBeforeIncludeStatement != "") {
2747 includeFilename = lineBeforeIncludeStatement.slice(1).trim().slice(14, -1);
2748 } else {
2749 includeFilename = null;
2750 }
2751
2752 if (includeLocation.length !== 0) {
2753 trueFileLine -= 1;
2754 lineNumber -= 1;
2755 }
2756
2757 return {
2758 lineNumber: trueFileLine,
2759 includeFilename,
2760 line: linedRenderedPreset[lineNumber],
2761 funcLine
2762 };
2763}
2764function printOutLine(eLine) {
2765 return log(chalk`{blue ${eLine.includeFilename || "Main"}}:{green ${eLine.lineNumber}} in ${eLine.funcLine}
2766${eLine.line}`);
2767}
2768async function getInfo(env, jobid) {
2769 log(env, jobid);
2770 let trace = lib.makeAPIRequest({
2771 env,
2772 path: `/jobs/${jobid}/artifacts/trace`
2773 }).catch(x => null);
2774 let renderedPreset = lib.makeAPIRequest({
2775 env,
2776 path: `/jobs/${jobid}/artifacts/preset`
2777 }).catch(x => null);
2778 let result = lib.makeAPIRequest({
2779 env,
2780 path: `/jobs/${jobid}/artifacts/result`
2781 }).catch(x => null);
2782 let error = lib.makeAPIRequest({
2783 env,
2784 path: `/jobs/${jobid}/artifacts/error`
2785 }).catch(x => null);
2786 let output = lib.makeAPIRequest({
2787 env,
2788 path: `/jobs/${jobid}/artifacts/output`
2789 }).catch(x => null);
2790 [trace, renderedPreset, result, output, error] = await Promise.all([trace, renderedPreset, result, output, error]);
2791 return {
2792 trace,
2793 renderedPreset,
2794 result,
2795 output,
2796 error
2797 };
2798}
2799async function parseTrace(env, jobid) {
2800 let {
2801 trace,
2802 renderedPreset
2803 } = await getInfo(env, jobid);
2804 let lineNumber = -1;
2805 let errorLines = [];
2806 let shouldBreak = 0;
2807
2808 for (let tr of trace.split("\n\n").reverse()) {
2809 errorLines.push(tr);
2810 shouldBreak--;
2811 if (tr.includes("Exception")) shouldBreak = 1;
2812 if (tr.includes("raised")) shouldBreak = 1;
2813 if (!shouldBreak) break;
2814 }
2815
2816 let errorList = [];
2817
2818 for (let errLine of errorLines) {
2819 lineNumber = /^[\w ]+:(\d+):/g.exec(errLine);
2820
2821 if (lineNumber && lineNumber[1]) {
2822 errorList.push((await findLineInFile(renderedPreset, lineNumber[1])));
2823 } else {
2824 errorList.push(errLine);
2825 }
2826 }
2827
2828 return errorList;
2829}
2830const Trace = {
2831 parseTrace,
2832 printOutLine,
2833 getInfo,
2834 findLineInFile
2835};
2836
2837require("source-map-support").install();
2838const rallyFunctions = {
2839 async bestPagintation() {
2840 global.silentAPI = true;
2841
2842 for (let i = 10; i <= 30; i += 5) {
2843 console.time("test with " + i);
2844 let dl = await lib.indexPathFast("DEV", `/workflowRules?page=1p${i}`);
2845 console.timeEnd("test with " + i);
2846 }
2847 },
2848
2849 async uploadPresets(env, presets, createFunc = () => false) {
2850 for (let preset of presets) {
2851 await preset.uploadCodeToEnv(env, createFunc);
2852 }
2853 },
2854
2855 //Dummy test access
2856 async testAccess(env) {
2857 if (lib.isLocalEnv(env)) {
2858 //TODO
2859 return true;
2860 }
2861
2862 let result = await lib.makeAPIRequest({
2863 env,
2864 path: "/providers?page=1p1",
2865 fullResponse: true,
2866 timeout: 1000
2867 });
2868 return result.statusCode;
2869 }
2870
2871};
2872
2873var allIndexBundle = /*#__PURE__*/Object.freeze({
2874 __proto__: null,
2875 rallyFunctions: rallyFunctions,
2876 SupplyChain: SupplyChain,
2877 Preset: Preset,
2878 Rule: Rule,
2879 Provider: Provider,
2880 Notification: Notification,
2881 Asset: Asset,
2882 User: User,
2883 Tag: Tag,
2884 Trace: Trace,
2885 get configFile () { return configFile; },
2886 loadConfig: loadConfig,
2887 loadConfigFromArgs: loadConfigFromArgs,
2888 setConfig: setConfig,
2889 get configObject () { return configObject; },
2890 lib: lib,
2891 AbortError: AbortError,
2892 APIError: APIError,
2893 UnconfiguredEnvError: UnconfiguredEnvError,
2894 ProtectedEnvError: ProtectedEnvError,
2895 FileTooLargeError: FileTooLargeError,
2896 Collection: Collection,
2897 RallyBase: RallyBase,
2898 sleep: sleep
2899});
2900
2901var version = "2.1.1";
2902
2903var baseCode = {
2904 SdviContentMover: `{
2905 "tasks": [
2906 {
2907 "operation": "copy" | "move" | "delete",
2908
2909 "source": {
2910 "optional": true | false,
2911
2912 # must specify either inventory OR externalStorage
2913 "inventory": {
2914 "labels": ["<label>" | "*", ],
2915 "tags": ["<tag>", ],
2916 "storageSet": ["<storage location name>" | "*", ], # only valid for move & delete tasks
2917 "expandCollections": true | false
2918 },
2919 "externalStorage": {
2920 "uri": "<protocol>://<host>/<path>/<file>",
2921 "credentials": {
2922 "key": "<parameter store key>",
2923 "roleArn": "<role to assume to access the parameter store>",
2924 "roleId": "<external ID to be used in role assumption>",
2925 "region": "<AWS region of the parameter store>"
2926 }
2927 }
2928 },
2929
2930 "destination": {
2931 "name": "<path within the storage location>/<filename>",
2932 "overwrite": "never" | "notInAnyAsset" | "notInOtherAsset" | "always",
2933 "storageMetadata": {"<key>": "<value>",...} | "<sourceStorageMetadata>",
2934
2935 # must specify either inventory OR externalStorage
2936 "inventory": {
2937 "storage": "<storage location name>",
2938 "newLabel": "<fileLabel>",
2939 "newTags": ["<tag>", "<tag>"],
2940 },
2941 "externalStorage": {
2942 "uri": "<protocol>://<host>",
2943 "credentials": {
2944 "key": "<parameter store key>",
2945 "roleArn": "<role to assume to access the parameter store>",
2946 "roleId": "<external ID to be used in role assumption>",
2947 "region": "<AWS region of the parameter store>"
2948 }
2949 }
2950 }
2951 },
2952
2953 {
2954 <another task>
2955 },
2956
2957 {
2958 <another task>
2959 },
2960
2961 ...
2962
2963 ]
2964}`,
2965 SdviEvaluate: `'''
2966name: {name}
2967'''
2968
2969# code here`,
2970 SdviEvalPro: `'''
2971name: {name}
2972'''
2973
2974import rally
2975
2976def evalMain(context):
2977 # code here`
2978};
2979
2980var _dec, _dec2, _dec3, _dec4, _dec5, _dec6, _dec7, _dec8, _dec9, _dec10, _dec11, _dec12, _dec13, _dec14, _dec15, _dec16, _dec17, _dec18, _dec19, _dec20, _dec21, _dec22, _dec23, _dec24, _dec25, _dec26, _dec27, _dec28, _dec29, _dec30, _dec31, _dec32, _dec33, _dec34, _dec35, _dec36, _dec37, _dec38, _dec39, _dec40, _dec41, _dec42, _dec43, _dec44, _dec45, _dec46, _dec47, _dec48, _dec49, _dec50, _dec51, _dec52, _dec53, _dec54, _dec55, _dec56, _obj;
2981
2982require("source-map-support").install();
2983let argv = argparse(process.argv.slice(2), {
2984 string: ["file", "env"],
2985 //boolean: ["no-protect"],
2986 boolean: ["anon"],
2987 default: {
2988 protect: true
2989 },
2990 alias: {
2991 f: "file",
2992 e: "env"
2993 }
2994}); //help menu helper
2995
2996function printHelp(help, short) {
2997 let helpText = chalk`
2998{white ${help.name}}: ${help.text}
2999 Usage: ${help.usage || "<unknown>"}
3000`; //Trim newlines
3001
3002 helpText = helpText.substring(1, helpText.length - 1);
3003
3004 if (!short) {
3005 for (let param of help.params || []) {
3006 helpText += chalk`\n {blue ${param.param}}: ${param.desc}`;
3007 }
3008
3009 for (let arg of help.args || []) {
3010 helpText += chalk`\n {blue ${arg.short}}, {blue ${arg.long}}: ${arg.desc}`;
3011 }
3012 }
3013
3014 return helpText;
3015}
3016
3017async function getFilesFromArgs(args) {
3018 let lastArg = args._.shift();
3019
3020 if (args.file) {
3021 let files = args.file;
3022 if (typeof files === "string") files = [files];
3023 return files;
3024 }
3025
3026 if (lastArg == "-") {
3027 log("Reading from stdin");
3028
3029 let getStdin = require("get-stdin");
3030
3031 let stdin = await getStdin();
3032 let files = stdin.split("\n");
3033 if (files[files.length - 1] === "") files.pop();
3034 return files;
3035 } else {
3036 args._.push(lastArg);
3037 }
3038}
3039
3040let presetsub = {
3041 async before(args) {
3042 this.env = args.env;
3043 if (!this.env) throw new AbortError("No env supplied");
3044 this.files = await getFilesFromArgs(args);
3045 },
3046
3047 async $grab(args) {
3048 if (!this.files) {
3049 throw new AbortError("No files provided to grab (use --file argument)");
3050 }
3051
3052 log(chalk`Grabbing {green ${this.files.length}} preset(s) metadata from {green ${this.env}}.`);
3053 let presets = this.files.map(path => new Preset({
3054 path,
3055 remote: false
3056 }));
3057
3058 for (let preset of presets) {
3059 //TODO small refactor
3060 await preset.grabMetadata(this.env);
3061 await preset.saveLocalMetadata();
3062
3063 if (args.full) {
3064 let remo = await Preset.getByName(this.env, preset.name);
3065 await remo.resolve();
3066 await remo.downloadCode();
3067 await remo.saveLocalFile();
3068 }
3069 }
3070 },
3071
3072 async $create(args) {
3073 let provider, name, ext;
3074
3075 if (args.provider) {
3076 provider = {
3077 name: args.provider
3078 };
3079 ext = args.ext;
3080 } else {
3081 provider = await selectProvider((await Provider.getAll(this.env)));
3082 ext = (await provider.getEditorConfig()).fileExt;
3083 }
3084
3085 if (args.name) {
3086 name = args.name;
3087 } else {
3088 name = await askInput("Preset Name", "What is the preset name?");
3089 }
3090
3091 let preset = new Preset({
3092 subProject: configObject.project
3093 });
3094 preset.providerType = {
3095 name: provider.name
3096 };
3097 preset.isGeneric = true;
3098 preset.name = name;
3099 preset.ext = ext;
3100
3101 if (baseCode[provider.name]) {
3102 preset._code = baseCode[provider.name].replace("{name}", name);
3103 } else {
3104 preset._code = " ";
3105 }
3106
3107 preset.saveLocalMetadata();
3108 if (!args["only-metadata"]) preset.saveLocalFile();
3109 },
3110
3111 async $list(args) {
3112 log("Loading...");
3113 let presets = await Preset.getAll(this.env);
3114
3115 if (args.resolve) {
3116 Provider.getAll(this.env);
3117
3118 for (let preset of presets) {
3119 let resolve = await preset.resolve(this.env);
3120
3121 if (args.attach) {
3122 let {
3123 proType
3124 } = resolve;
3125 proType.editorConfig.helpText = "";
3126 preset.meta = { ...preset.meta,
3127 proType
3128 };
3129 }
3130 }
3131 }
3132
3133 if (configObject.rawOutput) return presets;
3134 log(chalk`{yellow ${presets.length}} presets on {green ${this.env}}.`);
3135 presets.arr.sort((a, b) => {
3136 return Number(a.attributes.updatedAt) - Number(b.attributes.updatedAt);
3137 });
3138
3139 for (let preset of presets) {
3140 log(preset.chalkPrint());
3141 }
3142 },
3143
3144 async $upload(args) {
3145 if (!this.files) {
3146 throw new AbortError("No files provided to upload (use --file argument)");
3147 }
3148
3149 log(chalk`Uploading {green ${this.files.length}} preset(s) to {green ${this.env}}.`);
3150 let presets = this.files.map(path => new Preset({
3151 path,
3152 remote: false
3153 }));
3154 await rallyFunctions.uploadPresets(this.env, presets);
3155 },
3156
3157 async $deleteRemote(args) {
3158 let file = this.files[0];
3159
3160 if (!this.files) {
3161 throw new AbortError("No files provided to diff (use --file argument)");
3162 }
3163
3164 let preset = new Preset({
3165 path: file,
3166 remote: false
3167 });
3168
3169 if (!preset.name) {
3170 throw new AbortError(chalk`No preset header found. Cannot get name.`);
3171 }
3172
3173 let preset2 = await Preset.getByName(this.env, preset.name);
3174
3175 if (!preset2) {
3176 throw new AbortError(chalk`No preset found with name {red ${preset.name}} on {blue ${this.env}}`);
3177 }
3178
3179 log(chalk`Deleting ${preset2.chalkPrint(true)}.`);
3180 log((await preset2.delete()));
3181 },
3182
3183 async $diff(args) {
3184 let file = this.files[0];
3185
3186 if (!this.files) {
3187 throw new AbortError("No files provided to diff (use --file argument)");
3188 }
3189
3190 let preset = new Preset({
3191 path: file,
3192 remote: false
3193 });
3194
3195 if (!preset.name) {
3196 throw new AbortError(chalk`No preset header found. Cannot get name.`);
3197 }
3198
3199 let preset2 = await Preset.getByName(this.env, preset.name);
3200
3201 if (!preset2) {
3202 throw new AbortError(chalk`No preset found with name {red ${preset.name}} on {blue ${this.env}}`);
3203 }
3204
3205 await preset2.downloadCode();
3206
3207 let tempfile = require("tempy").file;
3208
3209 let temp = tempfile({
3210 extension: `${this.env}.${preset.ext}`
3211 });
3212 fs.writeFileSync(temp, preset2.code);
3213 let ptr = `${file},${temp}`; //raw output returns "file1" "file2"
3214
3215 if (configObject.rawOutput) {
3216 if (args["only-new"]) return temp;else return ptr;
3217 } //standard diff
3218
3219
3220 argv.command = argv.command || "diff";
3221 await spawn(argv.command, [file, temp], {
3222 stdio: "inherit"
3223 });
3224 },
3225
3226 async $info(args) {
3227 if (!this.files) {
3228 throw new AbortError("No files provided to diff (use --file argument)");
3229 }
3230
3231 let file = this.files[0];
3232 let preset = new Preset({
3233 path: file,
3234 remote: false
3235 });
3236
3237 if (!preset.name) {
3238 throw new AbortError(chalk`No preset header found. Cannot get name.`);
3239 }
3240
3241 await preset.getInfo(args.env);
3242 },
3243
3244 async unknown(arg, args) {
3245 log(chalk`Unknown action {red ${arg}} try '{white rally help preset}'`);
3246 }
3247
3248};
3249let rulesub = {
3250 async before(args) {
3251 this.env = args.env;
3252 if (!this.env) throw new AbortError("No env supplied");
3253 },
3254
3255 async $list(args) {
3256 log("Loading...");
3257 let rules = await Rule.getAll(this.env);
3258 if (configObject.rawOutput) return rules;
3259 log(chalk`{yellow ${rules.length}} rules on {green ${this.env}}.`);
3260 rules.arr.sort((a, b) => {
3261 return Number(a.data.attributes.updatedAt) - Number(b.data.attributes.updatedAt);
3262 });
3263
3264 for (let rule of rules) log(rule.chalkPrint());
3265 },
3266
3267 async $create(args) {
3268 let preset = await selectPreset({
3269 canSelectNone: false
3270 });
3271 let passNext = await selectRule({
3272 purpose: "'On Exit OK'"
3273 });
3274 let errorNext = await selectRule({
3275 purpose: "'On Exit Error'"
3276 });
3277 let name = await askInput("Rule Name", "What is the rule name?");
3278 name = name.replace("@", preset.name);
3279 let desc = await askInput("Description", "Enter a description.");
3280 let dynamicNexts = [];
3281 let next;
3282
3283 while (next = await selectRule({
3284 purpose: "dynamic next"
3285 })) {
3286 let name = await askInput("Key", "Key name for dynamic next");
3287 dynamicNexts.push({
3288 meta: {
3289 transition: name
3290 },
3291 type: "workflowRules",
3292 name: next.name
3293 });
3294 }
3295
3296 let rule = new Rule({
3297 subProject: configObject.project
3298 });
3299 rule.name = name;
3300 rule.description = desc;
3301 rule.relationships.preset = {
3302 data: {
3303 name: preset.name,
3304 type: "presets"
3305 }
3306 };
3307 if (errorNext) rule.relationships.errorNext = {
3308 data: {
3309 name: errorNext.name,
3310 type: "workflowRules"
3311 }
3312 };
3313 if (passNext) rule.relationships.passNext = {
3314 data: {
3315 name: passNext.name,
3316 type: "workflowRules"
3317 }
3318 };
3319
3320 if (dynamicNexts[0]) {
3321 rule.relationships.dynamicNexts = {
3322 data: dynamicNexts
3323 };
3324 }
3325
3326 rule.saveB();
3327 },
3328
3329 async unknown(arg, args) {
3330 log(chalk`Unknown action {red ${arg}} try '{white rally help rule}'`);
3331 }
3332
3333};
3334let jupytersub = {
3335 async before(args) {
3336 this.input = args._.shift() || "main.ipynb";
3337 this.output = args._.shift() || "main.py";
3338 },
3339
3340 async $build(args) {
3341 let cmd = `jupyter nbconvert --to python ${this.input} --TagRemovePreprocessor.remove_cell_tags={\"remove_cell\"} --output ${this.output} --TemplateExporter.exclude_markdown=True --TemplateExporter.exclude_input_prompt=True --TemplateExporter.exclude_output_prompt=True`.split(" ");
3342 log(chalk`Compiling GCR file {green ${this.input}} into {green ${this.output}} using jupyter...`);
3343
3344 try {
3345 let {
3346 timestr
3347 } = await spawn(cmd[0], cmd.slice(1));
3348 log(chalk`Complete in ~{green.bold ${timestr}}.`);
3349 } catch (e) {
3350 if (e.code !== "ENOENT") throw e;
3351 log(chalk`Cannot run the build command. Make sure that you have jupyter notebook installed.\n{green pip install jupyter}`);
3352 return;
3353 }
3354 }
3355
3356};
3357
3358async function categorizeString(str, defaultSubproject = undefined) {
3359 str = str.trim();
3360
3361 if (str.startsWith('"')) {
3362 str = str.slice(1, -1);
3363 }
3364
3365 let match;
3366
3367 if (match = /^(\w)-(\w{1,10})-(\d{1,10}):/.exec(str)) {
3368 if (match[1] === "P") {
3369 let ret = await Preset.getById(match[2], match[3]); //TODO modify for subproject a bit
3370
3371 return ret;
3372 } else if (match[1] === "R") {
3373 return await Rule.getById(match[2], match[3]);
3374 } else {
3375 return null;
3376 }
3377 } else if (match = /^([\w \/\\\-_]*)[\/\\]?silo\-(\w+)[\/\\]/.exec(str)) {
3378 try {
3379 switch (match[2]) {
3380 case "presets":
3381 return new Preset({
3382 path: str,
3383 subProject: match[1]
3384 });
3385
3386 case "rules":
3387 return new Rule({
3388 path: str,
3389 subProject: match[1]
3390 });
3391
3392 case "metadata":
3393 return await Preset.fromMetadata(str, match[1]);
3394 }
3395 } catch (e) {
3396 log(e);
3397 }
3398 } else {
3399 return null;
3400 }
3401}
3402
3403let tagsub = {
3404 async before(args) {
3405 this.env = args.env;
3406 if (!this.env) throw new AbortError("No env supplied");
3407 },
3408
3409 async $list(args) {
3410 log("Loading...");
3411 let tags = await Tag.getAll(this.env);
3412 if (configObject.rawOutput) return tags;
3413 log(chalk`{yellow ${tags.length}} tags on {green ${this.env}}.`);
3414 tags.arr.sort((a, b) => {
3415 return Number(a.data.attributes.updatedAt) - Number(b.data.attributes.updatedAt);
3416 });
3417
3418 for (let tag of tags) log(tag.chalkPrint());
3419 },
3420
3421 async $create(args) {
3422 return Tag.create(this.env, args._.shift());
3423 }
3424
3425};
3426let supplysub = {
3427 async before(args) {
3428 this.env = args.env;
3429 if (!this.env) throw new AbortError("No env supplied");
3430 this.files = await getFilesFromArgs(args);
3431 },
3432
3433 //Calculate a supply chain based on a starting rule at the top of the stack
3434 async $calc(args) {
3435 let name = args._.shift();
3436
3437 let stopName = args._.shift();
3438
3439 if (!name) {
3440 throw new AbortError("No starting rule or @ supplied");
3441 }
3442
3443 if (name === "@") {
3444 log(chalk`Silo clone started`);
3445 this.chain = new SupplyChain();
3446 this.chain.remote = args.env;
3447 } else {
3448 let rules = await Rule.getAll(this.env);
3449 let stop, start;
3450 start = rules.findByNameContains(name);
3451 if (stopName) stop = rules.findByNameContains(stopName);
3452
3453 if (!start) {
3454 throw new AbortError(chalk`No starting rule found by name {blue ${name}}`);
3455 }
3456
3457 log(chalk`Analzying supply chain: ${start.chalkPrint(false)} - ${stop ? stop.chalkPrint(false) : "(open)"}`);
3458 this.chain = new SupplyChain(start, stop);
3459 }
3460
3461 await this.chain.calculate();
3462 return await this.postAction(args);
3463 },
3464
3465 async postAction(args) {
3466 //Now that we ahve a supply chain object, do something with it
3467 if (args["to"]) {
3468 this.chain.log();
3469
3470 if (this.chain.presets.arr[0]) {
3471 await this.chain.downloadPresetCode(this.chain.presets);
3472 log("Done");
3473 }
3474
3475 if (Array.isArray(args["to"])) {
3476 for (let to of args["to"]) {
3477 await this.chain.syncTo(to);
3478 }
3479 } else {
3480 await this.chain.syncTo(args["to"]);
3481 }
3482 } else if (args["delete"]) {
3483 if (Array.isArray(args["delete"])) {
3484 for (let to of args["delete"]) {
3485 await this.chain.deleteTo(to);
3486 }
3487 } else {
3488 await this.chain.deleteTo(args["delete"]);
3489 }
3490 } else if (args["diff"]) {
3491 //Very basic diff
3492 let env = args["diff"];
3493 await Promise.all(this.chain.presets.arr.map(obj => obj.downloadCode()));
3494 await Promise.all(this.chain.presets.arr.map(obj => obj.resolve()));
3495 let otherPresets = await Promise.all(this.chain.presets.arr.map(obj => Preset.getByName(env, obj.name)));
3496 otherPresets = new Collection(otherPresets.filter(x => x));
3497 await Promise.all(otherPresets.arr.map(obj => obj.downloadCode()));
3498 await Promise.all(otherPresets.arr.map(obj => obj.resolve()));
3499
3500 const printPresets = (preset, otherPreset) => {
3501 log(preset.chalkPrint(true));
3502
3503 if (otherPreset.name) {
3504 log(otherPreset.chalkPrint(true));
3505 } else {
3506 log(chalk`{red (None)}`);
3507 }
3508 };
3509
3510 for (let preset of this.chain.presets) {
3511 let otherPreset = otherPresets.arr.find(x => x.name === preset.name) || {};
3512 preset.code = preset.code.replace(/[\r\n ]/, "");
3513 otherPreset.code = (otherPreset.code || "").replace(/[\r\n ]/, "");
3514
3515 if (preset.code === otherPreset.code) {
3516 if (!args["ignore-same"]) {
3517 printPresets(preset, otherPreset);
3518 log("Code Same");
3519 }
3520 } else {
3521 printPresets(preset, otherPreset);
3522
3523 if (args["ignore-same"]) {
3524 log("-------");
3525 } else {
3526 log("Code Different");
3527 }
3528 }
3529 }
3530 } else {
3531 return await this.chain.log();
3532 }
3533 },
3534
3535 async $make(args) {
3536 let set = new Set();
3537 let hints = args.hint ? Array.isArray(args.hint) ? args.hint : [args.hint] : []; //TODO modify for better hinting, and add this elsewhere
3538
3539 for (let hint of hints) {
3540 if (hint === "presets-uat") {
3541 log("got hint");
3542 await Preset.getAll("UAT");
3543 }
3544 }
3545
3546 for (let file of this.files) {
3547 set.add((await categorizeString(file)));
3548 }
3549
3550 let files = [...set];
3551 files = files.filter(f => f && !f.missing);
3552 this.chain = new SupplyChain();
3553 this.chain.rules = new Collection(files.filter(f => f instanceof Rule));
3554 this.chain.presets = new Collection(files.filter(f => f instanceof Preset));
3555 this.chain.notifications = new Collection([]);
3556 return await this.postAction(args);
3557 },
3558
3559 async unknown(arg, args) {
3560 log(chalk`Unknown action {red ${arg}} try '{white rally help supply}'`);
3561 }
3562
3563};
3564
3565function subCommand(object) {
3566 object = {
3567 before() {},
3568
3569 after() {},
3570
3571 unknown() {},
3572
3573 ...object
3574 };
3575 return async function (args) {
3576 //Grab the next arg on the stack, find a function tied to it, and run
3577 let arg = args._.shift();
3578
3579 let key = "$" + arg;
3580 let ret;
3581
3582 if (object[key]) {
3583 await object.before(args);
3584 ret = await object[key](args);
3585 await object.after(args);
3586 } else {
3587 if (arg === undefined) arg = "(None)";
3588 object.unknown(arg, args);
3589 }
3590
3591 return ret;
3592 };
3593}
3594
3595let cli = (_dec = helpText(`Display the help menu`), _dec2 = usage(`rally help [subhelp]`), _dec3 = param("subhelp", "The name of the command to see help for"), _dec4 = helpText("Rally tools jupyter interface. Requires jupyter to be installed."), _dec5 = usage("rally jupyter build [in] [out]"), _dec6 = param("in/out", "input and output file for jupyter. By default main.ipyrb and main.py"), _dec7 = helpText(`Preset related actions`), _dec8 = usage(`rally preset [action] --env <enviornment> --file [file1] --file [file2] ...`), _dec9 = param("action", "The action to perform. Can be upload, diff, list, deleteRemote"), _dec10 = arg("-e", "--env", "The enviornment you wish to perform the action on"), _dec11 = arg("-f", "--file", "A file to act on"), _dec12 = arg("~", "--command", "If the action is diff, this is the command to run instead of diff"), _dec13 = helpText(`Rule related actions`), _dec14 = usage(`rally rule [action] --env [enviornment]`), _dec15 = param("action", "The action to perform. Only list is supported right now"), _dec16 = arg("-e", "--env", "The enviornment you wish to perform the action on"), _dec17 = helpText(`supply chain related actions`), _dec18 = usage(`rally supply [action] [identifier] --env [enviornment] [post actions]`), _dec19 = param("action", "The action to perform. Can be calc or make."), _dec20 = param("identifier", "If the action is calc, then this identifier should be the first rule in the chain. If this is make, then supply '-' to read from stdin"), _dec21 = param("post actions", "The action to perform on the created supply chain. See commands below"), _dec22 = arg("-e", "--env", "(calc only) environment to do the calculation on"), _dec23 = arg("~", "--diff", "(post action) Use as `--diff [env]`. List all files with differences on the given env."), _dec24 = arg("~", "--to", "(post action) Use as `--to [env]`. Upload all objects."), _dec25 = arg("~", "--delete", "(post action) Use as `--delete [env]`. The reverse of uploading. Only presets are supported right now."), _dec26 = helpText(`tags stuff`), _dec27 = usage(`rally tags [action]`), _dec28 = param("action", "The action to perform. Can be list or create."), _dec29 = arg("-e", "--env", "The enviornment you wish to perform the action on"), _dec30 = helpText(`print out some trace info`), _dec31 = usage(`rally trace -e [env] [jobid]`), _dec32 = param("jobid", "a job id like b86d7d90-f0a5-4622-8754-486ca8e9ecbd"), _dec33 = arg("-e", "--env", "The enviornment you wish to perform the action on"), _dec34 = helpText(`List all available providers, or find one by name/id`), _dec35 = usage(`rally providers [identifier] --env [env] --raw`), _dec36 = param("identifier", "Either the name or id of the provider"), _dec37 = arg("-e", "--env", "The enviornment you wish to perform the action on"), _dec38 = arg("~", "--raw", "Raw output of command. If [identifier] is given, then print editorConfig too"), _dec39 = helpText(`Change config for rally tools`), _dec40 = usage("rally config [key] --set [value] --raw"), _dec41 = param("key", chalk`Key you want to edit. For example, {green chalk} or {green api.DEV}`), _dec42 = arg("~", "--set", "If this value is given, no interactive prompt will launch and the config option will change."), _dec43 = arg("~", "--raw", "Raw output of json config"), _dec44 = helpText(`create/modify asset`), _dec45 = usage("rally asset [action] [action...]"), _dec46 = param("action", chalk`Options are create, delete, launch, addfile, metadata, show, patchMetadata, and launchEvalute. You can supply multiple actions to chain them`), _dec47 = arg(`-i`, `--id`, chalk`MOVIE_ID of asset to select`), _dec48 = arg(`-n`, `--name`, chalk`MOVIE_NAME of asset. with {white create}, '{white #}' will be replaced with a uuid. Default is '{white TEST_#}'`), _dec49 = arg(`~`, `--anon`, chalk`Supply this if no asset is needed (used to lauch anonymous workflows)`), _dec50 = arg(`-j`, `--job-name`, chalk`Job name to start (used with launch and launchEvalute)`), _dec51 = arg(`~`, `--init-data`, chalk`Init data to use when launching job. can be string, or {white @path/to/file} for a file`), _dec52 = arg(`~`, `--file-label`, chalk`File label (used with addfile)`), _dec53 = arg(`~`, `--file-uri`, chalk`File s3 uri. Can use multiple uri's for the same label (used with addfile)`), _dec54 = arg(`~`, `--metadata`, chalk`Metadata to use with patchMetadata. Can be string, or {white @path/to/file} for a file. Data must contain a top level key Metadata, or Workflow. Metadata will be pached into METADATA. Workflow will be patched into WORKFLOW_METADATA(not currently available)`), _dec55 = arg(`~`, `--priority`, chalk`set the priority of all launched jobs`), _dec56 = arg(`~`, `--new-name`, chalk`set the new name`), (_obj = {
3596 async help(args) {
3597 let arg = args._.shift();
3598
3599 if (arg) {
3600 let help = helpEntries[arg];
3601
3602 if (!help) {
3603 log(chalk`No help found for '{red ${arg}}'`);
3604 } else {
3605 log(printHelp(helpEntries[arg]));
3606 }
3607 } else {
3608 for (let helpArg in helpEntries) {
3609 log(printHelp(helpEntries[helpArg], true));
3610 }
3611 }
3612 },
3613
3614 async jupyter(args) {
3615 return subCommand(jupytersub)(args);
3616 },
3617
3618 //@helpText(`Print input args, for debugging`)
3619 async printArgs(args) {
3620 log(args);
3621 },
3622
3623 async preset(args) {
3624 return subCommand(presetsub)(args);
3625 },
3626
3627 async rule(args) {
3628 return subCommand(rulesub)(args);
3629 },
3630
3631 async supply(args) {
3632 return subCommand(supplysub)(args);
3633 },
3634
3635 async tag(args) {
3636 return subCommand(tagsub)(args);
3637 },
3638
3639 async trace(args) {
3640 let jobId = args._.shift();
3641
3642 if (!jobId) throw new AbortError("No job id");
3643 if (!args.env) throw new AbortError("no env");
3644
3645 let ln = args._.shift();
3646
3647 if (!ln) {
3648 log("is trace");
3649 let traceInfo = await parseTrace(args.env, jobId);
3650
3651 for (let line of traceInfo) {
3652 if (typeof line == "string") {
3653 log(chalk.red(line));
3654 } else {
3655 printOutLine(line);
3656 }
3657 }
3658 } else {
3659 log("is ln");
3660 let {
3661 renderedPreset
3662 } = await getInfo(args.env, jobId);
3663 return findLineInFile(renderedPreset, Number(ln));
3664 }
3665 },
3666
3667 async providers(args) {
3668 let env = args.env;
3669 if (!env) return errorLog("No env supplied.");
3670
3671 let ident = args._.shift();
3672
3673 let providers = await Provider.getAll(env);
3674
3675 if (ident) {
3676 let pro = providers.arr.find(x => x.id == ident || x.name.includes(ident));
3677
3678 if (!pro) {
3679 log(chalk`Couldn't find provider by {green ${ident}}`);
3680 } else {
3681 log(pro.chalkPrint(false));
3682 let econfig = await pro.getEditorConfig();
3683
3684 if (args.raw) {
3685 return pro;
3686 } else {
3687 if (econfig.helpText.length > 100) {
3688 econfig.helpText = "<too long to display>";
3689 }
3690
3691 if (econfig.completions.length > 5) {
3692 econfig.completions = "<too long to display>";
3693 }
3694
3695 log(econfig);
3696 }
3697 }
3698 } else {
3699 if (args.raw) return providers;
3700
3701 for (let pro of providers) log(pro.chalkPrint());
3702 }
3703 },
3704
3705 async config(args) {
3706 let prop = args._.shift();
3707
3708 let propArray = prop && prop.split("."); //if(!await configHelpers.askQuestion(`Would you like to create a new config file in ${configFile}`)) return;
3709
3710 let newConfigObject;
3711
3712 if (!prop) {
3713 if (configObject.rawOutput) return configObject;
3714 log("Creating new config");
3715 newConfigObject = { ...configObject
3716 };
3717
3718 for (let helperName in configHelpers) {
3719 if (helperName.startsWith("$")) {
3720 newConfigObject = { ...newConfigObject,
3721 ...(await configHelpers[helperName](false))
3722 };
3723 }
3724 }
3725 } else {
3726 log(chalk`Editing option {green ${prop}}`);
3727
3728 if (args.set) {
3729 newConfigObject = { ...configObject,
3730 [prop]: args.set
3731 };
3732 } else {
3733 let ident = "$" + propArray[0];
3734
3735 if (configHelpers[ident]) {
3736 newConfigObject = { ...configObject,
3737 ...(await configHelpers[ident](propArray))
3738 };
3739 } else {
3740 log(chalk`No helper for {red ${ident}}`);
3741 return;
3742 }
3743 }
3744 }
3745
3746 newConfigObject.hasConfig = true; //Create readable json and make sure the user is ok with it
3747
3748 let newConfig = JSON.stringify(newConfigObject, null, 4);
3749 log(newConfig); //-y or --set will make this not prompt
3750
3751 if (!args.y && !args.set && !(await askQuestion("Write this config to disk?"))) return;
3752 fs.writeFileSync(configFile, newConfig, {
3753 mode: 0o600
3754 });
3755 log(chalk`Created file {green ${configFile}}.`);
3756 },
3757
3758 async asset(args) {
3759 function uuid(args) {
3760 const digits = 16;
3761 return String(Math.floor(Math.random() * Math.pow(10, digits))).padStart(digits, "0");
3762 }
3763
3764 let name = args.name || `TEST_#`;
3765 let env = args.env;
3766 let asset;
3767
3768 let arg = args._.shift();
3769
3770 if (!arg) {
3771 throw new AbortError(chalk`Missing arguments: see {white 'rally help asset'}`);
3772 }
3773
3774 if (args.anon) {
3775 args._.unshift(arg);
3776 } else if (arg == "create") {
3777 name = name.replace("#", uuid());
3778 asset = await Asset.createNew(name, env);
3779 } else {
3780 args._.unshift(arg);
3781
3782 if (args.id) {
3783 asset = Asset.lite(args.id, env);
3784 } else {
3785 asset = await Asset.getByName(env, args.name);
3786 }
3787 }
3788
3789 if (!asset && !args.anon) {
3790 throw new AbortError("No asset found/created");
3791 }
3792
3793 let launchArg = 0;
3794 let fileArg = 0;
3795
3796 let arrayify = (obj, i) => Array.isArray(obj) ? obj[i] : i == 0 ? obj : undefined;
3797
3798 while (arg = args._.shift()) {
3799 if (arg === "launch") {
3800 let initData = arrayify(args["init-data"], launchArg);
3801
3802 if (initData && initData.startsWith("@")) {
3803 log(chalk`Reading init data from {white ${initData.slice(1)}}`);
3804 initData = fs.readFileSync(initData.slice(1), "utf-8");
3805 }
3806
3807 let jobName = arrayify(args["job-name"], launchArg);
3808 let p = await Rule.getByName(env, jobName);
3809
3810 if (!p) {
3811 throw new AbortError(`Cannot launch job ${jobName}, does not exist (?)`);
3812 } else {
3813 log(chalk`Launching ${p.chalkPrint(false)} on ${asset ? asset.chalkPrint(false) : "(None)"}`);
3814 }
3815
3816 if (asset) {
3817 await asset.startWorkflow(jobName, {
3818 initData,
3819 priority: args.priority
3820 });
3821 } else {
3822 await Asset.startAnonWorkflow(env, jobName, {
3823 initData,
3824 priority: args.priority
3825 });
3826 }
3827
3828 launchArg++;
3829 } else if (arg === "launchEvaluate") {
3830 let initData = arrayify(args["init-data"], launchArg);
3831
3832 if (initData && initData.startsWith("@")) {
3833 log(chalk`Reading init data from {white ${initData.slice(1)}}`);
3834 initData = fs.readFileSync(initData.slice(1), "utf-8");
3835 }
3836
3837 let jobName = arrayify(args["job-name"], launchArg);
3838 let jobData;
3839 let ephemeralEval = false;
3840 let p;
3841
3842 if (jobName.startsWith("@")) {
3843 ephemeralEval = true;
3844 jobData = fs.readFileSync(jobName.slice(1));
3845 } else {
3846 p = await Preset.getByName(env, jobName);
3847
3848 if (!p) {
3849 throw new AbortError(`Cannot launch preset ${jobName}, does not exist (?)`);
3850 } else {
3851 log(chalk`Launching ${p.chalkPrint(false)} on ${asset ? asset.chalkPrint(false) : "(None)"}`);
3852 }
3853 }
3854
3855 if (ephemeralEval) {
3856 await Asset.startEphemeralEvaluateIdeal(env, p.id, initData);
3857 } else {
3858 await asset.startEvaluate(p.id, initData);
3859 }
3860
3861 launchArg++;
3862 } else if (arg === "addfile") {
3863 let label = arrayify(args["file-label"], fileArg);
3864 let uri = arrayify(args["file-uri"], fileArg);
3865
3866 if (label === undefined || !uri) {
3867 throw new AbortError("Number of file-label and file-uri does not match");
3868 }
3869
3870 await asset.addFile(label, uri);
3871 log(chalk`Added file ${label}`);
3872 fileArg++;
3873 } else if (arg === "delete") {
3874 await asset.delete();
3875 } else if (arg === "create") {
3876 throw new AbortError(`Cannot have more than 1 create/get per asset call`);
3877 } else if (arg === "show" || arg == "load") {
3878 if (asset.lite) asset = await Asset.getById(env, asset.id);
3879 if (arg == "show") log(asset);
3880 } else if (arg === "metadata" || arg === "md") {
3881 log((await asset.getMetadata()));
3882 } else if (arg === "patchMetadata") {
3883 let initData = arrayify(args["metadata"], launchArg);
3884
3885 if (initData && initData.startsWith("@")) {
3886 log(chalk`Reading data from {white ${initData.slice(1)}}`);
3887 initData = fs.readFileSync(initData.slice(1), "utf-8");
3888 }
3889
3890 initData = JSON.parse(initData);
3891 await asset.patchMetadata(initData);
3892 } else if (arg === "rename") {
3893 let newName = args["new-name"];
3894 let oldName = asset.name;
3895 await asset.rename(newName);
3896 log(chalk`Rename: {green ${oldName}} -> {green ${newName}}`);
3897 }
3898 }
3899
3900 if (configObject.rawOutput) return asset;
3901 },
3902
3903 async checkSegments(args) {
3904 let asset = args._.shift();
3905
3906 let res = await lib.makeAPIRequest({
3907 env: args.env,
3908 path: `/movies/${asset}/metadata/Metadata`
3909 });
3910 let segments = res.data.attributes.metadata.userMetaData.segments.segments;
3911 let r = segments.reduce((lastSegment, val, ind) => {
3912 let curSegment = val.startTime;
3913
3914 if (curSegment < lastSegment) {
3915 log("bad segment " + (ind + 1));
3916 }
3917
3918 return val.endTime;
3919 }, "00:00:00:00");
3920 },
3921
3922 async getJobs(args) {
3923 let req = await lib.indexPathFast({
3924 env: args.env,
3925 path: "/jobs",
3926 qs: {
3927 filter: "presetName=DCTC Add Element US Checkin"
3928 }
3929 });
3930 log(req.map(x => x.relationships.asset.data.id).join("\n"));
3931 },
3932
3933 async listAssets(args, tag) {
3934 let req = await lib.indexPathFast({
3935 env: args.env,
3936 path: "/assets",
3937 qs: {
3938 noRelationships: true,
3939 sort: "id"
3940 },
3941 chunksize: 30
3942 });
3943
3944 for (let asset of req) {
3945 log(asset.id);
3946 }
3947
3948 return req;
3949 },
3950
3951 async listSegments(args) {
3952 let f = JSON.parse(fs.readFileSync(args.file, "utf-8"));
3953
3954 for (let asset of f) {
3955 var _r$data$attributes$me, _r$data$attributes$me2;
3956
3957 let r = await lib.makeAPIRequest({
3958 env: args.env,
3959 path: `/movies/${asset.id}/metadata/Metadata`
3960 });
3961 let segs = (_r$data$attributes$me = r.data.attributes.metadata.userMetaData) === null || _r$data$attributes$me === void 0 ? void 0 : (_r$data$attributes$me2 = _r$data$attributes$me.segments) === null || _r$data$attributes$me2 === void 0 ? void 0 : _r$data$attributes$me2.segments;
3962
3963 if (segs && segs.length > 1) {
3964 log(asset.id);
3965 log(asset.name);
3966 }
3967 }
3968 },
3969
3970 async test4(args) {
3971 let things = await lib.indexPathFast({
3972 env: args.env,
3973 path: "/assets",
3974 qs: {
3975 filter: `createdBefore=${Date.now() - 1000 * 160 * 24 * 60 * 60},createdSince=${Date.now() - 1000 * 170 * 24 * 60 * 60}`
3976 }
3977 });
3978 log(JSON.stringify(things, null, 4));
3979 },
3980
3981 async test5(args) {
3982 let things = await lib.indexPathFast({
3983 env: args.env,
3984 path: "/jobs",
3985 qs: {
3986 filter: `state=Queued,presetName=E2 P4101 - DNE Compliance Edit - US Output Deal - Edit WorkOrder`
3987 }
3988 });
3989 log(JSON.stringify(things, null, 4));
3990 },
3991
3992 async test2(args) {
3993 let wfr = await lib.indexPath({
3994 env: args.env,
3995 path: "/workflowRuleMetadata"
3996 });
3997 log(wfr);
3998
3999 for (let wfrm of wfr) {
4000 try {
4001 wfrm.id = undefined;
4002 wfrm.links = undefined;
4003 log(wfrm);
4004 let req = await lib.makeAPIRequest({
4005 env: "DEV",
4006 path: "/workflowRuleMetadata",
4007 method: "POST",
4008 payload: {
4009 data: wfrm
4010 }
4011 });
4012 } catch (e) {
4013 log("caught");
4014 } //break;
4015
4016 }
4017 },
4018
4019 async test3(args) {
4020 let wfr = await lib.indexPath({
4021 env: args.env,
4022 path: "/workflowRuleMetadata"
4023 });
4024 log(wfr);
4025
4026 for (let wfrm of wfr) {
4027 try {
4028 wfrm.id = undefined;
4029 wfrm.links = undefined;
4030 log(wfrm);
4031 let req = await lib.makeAPIRequest({
4032 env: "DEV",
4033 path: "/workflowRuleMetadata",
4034 method: "POST",
4035 payload: {
4036 data: wfrm
4037 }
4038 });
4039 } catch (e) {
4040 log("caught");
4041 } //break;
4042
4043 }
4044 },
4045
4046 async deleteOmneons(args) {
4047 let id = args._.shift();
4048
4049 let asset;
4050
4051 if (Number(id)) {
4052 asset = await Asset.getById("PROD", Number(id));
4053 } else {
4054 asset = await Asset.getByName("PROD", id);
4055 }
4056
4057 log(asset);
4058 let f = await asset.getFiles();
4059
4060 for (let file of f) {
4061 if (file.label.includes("Omneon")) {
4062 log(`removing ${file.label}`);
4063 await file.delete();
4064 }
4065 }
4066 },
4067
4068 async audit(args) {
4069 let supportedAudits = ["presets", "rule", "other"];
4070 await addAutoCompletePrompt();
4071 let q = await inquirer.prompt([{
4072 type: "autocomplete",
4073 name: "obj",
4074 message: `What audit do you want?`,
4075 source: async (sofar, input) => {
4076 return supportedAudits.filter(x => input ? x.includes(input.toLowerCase()) : true);
4077 }
4078 }]);
4079 let choice = q.obj;
4080 let resourceId = undefined;
4081
4082 let filterFunc = _ => _;
4083
4084 if (choice === "presets") {
4085 let preset = await selectPreset({
4086 canSelectNone: false
4087 });
4088 let remote = await Preset.getByName(args.env, preset.name);
4089 if (!remote) throw new AbortError("Could not find this item on remote env");
4090
4091 filterFunc = ev => ev.resource == "Preset";
4092
4093 resourceId = remote.id;
4094 } else if (choice === "rule") {
4095 let preset = await selectRule({
4096 canSelectNone: false
4097 });
4098 let remote = await Rule.getByName(args.env, preset.name);
4099 if (!remote) throw new AbortError("Could not find this item on remote env");
4100
4101 filterFunc = ev => ev.resource == "Rule";
4102
4103 resourceId = remote.id;
4104 } else {
4105 resourceId = await askInput(null, "What resourceID?");
4106 }
4107
4108 log(chalk`Resource ID on {blue ${args.env}} is {yellow ${resourceId}}`);
4109 log(`Loading audits (this might take a while)`);
4110 const numRows = 100;
4111 let r = await lib.makeAPIRequest({
4112 env: args.env,
4113 path: `/v1.0/audit?perPage=${numRows}&count=${numRows}&filter=%7B%22resourceId%22%3A%22${resourceId}%22%7D&autoload=false&pageNum=1&include=`,
4114 timeout: 180000
4115 });
4116 r.data = r.data.filter(filterFunc);
4117 log("Data recieved, parsing users");
4118
4119 for (let event of r.data) {
4120 var _event$correlation;
4121
4122 let uid = event === null || event === void 0 ? void 0 : (_event$correlation = event.correlation) === null || _event$correlation === void 0 ? void 0 : _event$correlation.userId;
4123 if (!uid) continue;
4124
4125 try {
4126 event.user = await User.getById(args.env, uid);
4127 } catch (e) {
4128 event.user = {
4129 name: "????"
4130 };
4131 }
4132 }
4133
4134 if (args.raw) return r.data;
4135 let evCounter = 0;
4136
4137 for (let event of r.data) {
4138 var _event$user;
4139
4140 let evtime = moment(event.createdAt);
4141 let date = evtime.format("ddd YYYY/MM/DD hh:mm:ssa");
4142 let timedist = evtime.fromNow();
4143 log(chalk`${date} {yellow ${timedist}} {green ${(_event$user = event.user) === null || _event$user === void 0 ? void 0 : _event$user.name}} ${event.event}`);
4144 if (++evCounter >= 30) break;
4145 }
4146 },
4147
4148 async audit2(args) {
4149 const numRows = 1000;
4150 let r = await lib.makeAPIRequest({
4151 env: args.env,
4152 //path: `/v1.0/audit?perPage=${numRows}&count=${numRows}&autoload=false&pageNum=1&include=`,
4153 path: `/v1.0/audit?perPage=${numRows}&count=${numRows}&filter=%7B%22correlation.userId%22%3A%5B%22164%22%5D%7D&autoload=false&pageNum=1&include=`,
4154 timeout: 60000
4155 });
4156
4157 for (let event of r.data) {
4158 log(event.event);
4159 }
4160 },
4161
4162 async findIDs(args) {
4163 let files = await getFilesFromArgs(args);
4164
4165 for (let file of files) {
4166 let preset = await Preset.getByName(args.env, file);
4167 await preset.resolve();
4168 log(`silo-presets/${file}.${preset.ext}`);
4169 }
4170 },
4171
4172 async getAssets(env, name) {
4173 if (!this.callid) this.callid = 0;
4174 this.callid++;
4175 let callid = this.callid;
4176 await sleep(500);
4177 if (callid != this.callid) return this.lastResult || [];
4178 let req = await lib.makeAPIRequest({
4179 env,
4180 path: `/assets`,
4181 qs: name ? {
4182 filter: `nameContains=${name}`
4183 } : undefined
4184 });
4185 this.lastCall = Date.now();
4186 return this.lastResult = req.data;
4187 },
4188
4189 async content(args) {
4190 addAutoCompletePrompt();
4191 let q = await inquirer.prompt([{
4192 type: "autocomplete",
4193 name: "what",
4194 message: `What asset do you want?`,
4195 source: async (sofar, input) => {
4196 let assets = await this.getAssets(args.env, input);
4197 assets = assets.map(x => new Asset({
4198 data: x,
4199 remote: args.env
4200 }));
4201 return assets.map(x => ({
4202 name: x.chalkPrint(true) + ": " + x.data.links.self.replace("/api/v2/assets/", "/content/"),
4203 value: x
4204 }));
4205 }
4206 }]);
4207 },
4208
4209 async ["@"](args) {
4210 args._.unshift("-");
4211
4212 args._.unshift("make");
4213
4214 return this.supply(args);
4215 },
4216
4217 async test(args) {
4218 let asset = await Asset.getByName("UAT", args.name);
4219 log(asset);
4220 },
4221
4222 //Used to test startup and teardown speed.
4223 noop() {
4224 return true;
4225 }
4226
4227}, (_applyDecoratedDescriptor(_obj, "help", [_dec, _dec2, _dec3], Object.getOwnPropertyDescriptor(_obj, "help"), _obj), _applyDecoratedDescriptor(_obj, "jupyter", [_dec4, _dec5, _dec6], Object.getOwnPropertyDescriptor(_obj, "jupyter"), _obj), _applyDecoratedDescriptor(_obj, "preset", [_dec7, _dec8, _dec9, _dec10, _dec11, _dec12], Object.getOwnPropertyDescriptor(_obj, "preset"), _obj), _applyDecoratedDescriptor(_obj, "rule", [_dec13, _dec14, _dec15, _dec16], Object.getOwnPropertyDescriptor(_obj, "rule"), _obj), _applyDecoratedDescriptor(_obj, "supply", [_dec17, _dec18, _dec19, _dec20, _dec21, _dec22, _dec23, _dec24, _dec25], Object.getOwnPropertyDescriptor(_obj, "supply"), _obj), _applyDecoratedDescriptor(_obj, "tag", [_dec26, _dec27, _dec28, _dec29], Object.getOwnPropertyDescriptor(_obj, "tag"), _obj), _applyDecoratedDescriptor(_obj, "trace", [_dec30, _dec31, _dec32, _dec33], Object.getOwnPropertyDescriptor(_obj, "trace"), _obj), _applyDecoratedDescriptor(_obj, "providers", [_dec34, _dec35, _dec36, _dec37, _dec38], Object.getOwnPropertyDescriptor(_obj, "providers"), _obj), _applyDecoratedDescriptor(_obj, "config", [_dec39, _dec40, _dec41, _dec42, _dec43], Object.getOwnPropertyDescriptor(_obj, "config"), _obj), _applyDecoratedDescriptor(_obj, "asset", [_dec44, _dec45, _dec46, _dec47, _dec48, _dec49, _dec50, _dec51, _dec52, _dec53, _dec54, _dec55, _dec56], Object.getOwnPropertyDescriptor(_obj, "asset"), _obj)), _obj));
4228
4229async function unknownCommand(cmd) {
4230 log(chalk`Unknown command {red ${cmd}}.`);
4231}
4232
4233async function noCommand() {
4234 write(chalk`
4235Rally Tools {yellow v${version} (alpha)} CLI
4236by John Schmidt <John_Schmidt@discovery.com>
4237`); //Prompt users to setup one time config.
4238
4239 if (!configObject.hasConfig) {
4240 write(chalk`
4241It looks like you haven't setup the config yet. Please run '{green rally config}'.
4242`);
4243 return;
4244 }
4245
4246 let envs = new Set(["LOCAL", "UAT", "DEV", "PROD", "QA", ...Object.keys(configObject.api)]);
4247 let proms = [];
4248
4249 for (let env of envs) {
4250 proms.push({
4251 env,
4252 prom: rallyFunctions.testAccess(env)
4253 });
4254 } //API Access tests
4255
4256
4257 for (let {
4258 env,
4259 prom
4260 } of proms) {
4261 //Test access. Returns HTTP response code
4262 let resultStr;
4263
4264 try {
4265 let result = await prom; //Create a colored display and response
4266
4267 resultStr = chalk`{yellow ${result} <unknown>}`;
4268 if (result === 200) resultStr = chalk`{green 200 OK}`;else if (result === 401) resultStr = chalk`{red 401 No Access}`;else if (result >= 500) resultStr = chalk`{yellow ${result} API Down?}`;else if (result === true) resultStr = chalk`{green OK}`;else if (result === false) resultStr = chalk`{red BAD}`;
4269 } catch (e) {
4270 if (e instanceof UnconfiguredEnvError) {
4271 resultStr = chalk`{yellow Unconfigured}`;
4272 } else if (e instanceof APIError) {
4273 if (!e.response.body) {
4274 resultStr = chalk`{red Timeout (?)}`;
4275 }
4276 } else if (e.name == "RequestError") {
4277 resultStr = chalk`{red Low level error (check internet): ${e.error.errno}}`;
4278 } else {
4279 throw e;
4280 }
4281 }
4282
4283 log(chalk` ${env}: ${resultStr}`);
4284 }
4285}
4286
4287async function $main() {
4288 //Supply --config to load a different config file
4289 if (typeof argv.config === "string") {
4290 loadConfig(argv.config);
4291 } else if (typeof argv.config === "object") {
4292 loadConfigFromArgs(argv);
4293 } else {
4294 loadConfig();
4295 } // First we need to decide if the user wants color or not. If they do want
4296 // color, we need to make sure we use the right mode
4297
4298
4299 chalk.enabled = configObject.hasConfig ? configObject.chalk : true;
4300
4301 if (chalk.level === 0 || !chalk.enabled) {
4302 let force = argv["force-color"];
4303
4304 if (force) {
4305 chalk.enabled = true;
4306
4307 if (force === true && chalk.level === 0) {
4308 chalk.level = 1;
4309 } else if (Number(force)) {
4310 chalk.level = Number(force);
4311 }
4312 }
4313 } //This flag being true allows you to modify UAT and PROD
4314
4315
4316 if (!argv["protect"]) {
4317 configObject.dangerModify = true;
4318 } //This enables raw output for some functions
4319
4320
4321 if (argv["raw"]) {
4322 configObject.rawOutput = true;
4323
4324 global.log = () => {};
4325
4326 global.errorLog = () => {};
4327
4328 global.write = () => {};
4329 }
4330
4331 if (argv["ignore-missing"]) {
4332 configObject.ignoreMissing = true;
4333 }
4334
4335 if (argv["update-immutable"]) {
4336 configObject.updateImmutable = true;
4337 }
4338
4339 if (argv["skip-header"]) {
4340 configObject.skipHeader = true;
4341 }
4342
4343 configObject.globalProgress = !argv["hide-progress"]; //Default enviornment should normally be from config, but it can be
4344 //overridden by the -e/--env flag
4345
4346 if (configObject.defaultEnv) {
4347 argv.env = argv.env || configObject.defaultEnv;
4348 } //Enable verbose logging in some places.
4349
4350
4351 if (argv["vverbose"]) {
4352 configObject.verbose = argv["vverbose"];
4353 configObject.vverbose = true;
4354 } else if (argv["verbose"]) {
4355 configObject.verbose = argv["verbose"];
4356 } else if (argv["vvverbose"]) {
4357 configObject.verbose = true;
4358 configObject.vverbose = true;
4359 configObject.vvverbose = true;
4360 } //copy argument array to new object to allow modification
4361
4362
4363 argv._old = argv._.slice(); //Take first argument after `node bundle.js`
4364 //If there is no argument, display the default version info and API access.
4365
4366 let func = argv._.shift();
4367
4368 if (func) {
4369 if (!cli[func]) return await unknownCommand(func);
4370
4371 try {
4372 //Call the cli function
4373 let ret = await cli[func](argv);
4374
4375 if (ret) {
4376 write(chalk.white("CLI returned: "));
4377 if (ret instanceof Collection) ret = ret.arr; //Directly use console.log so that --raw works as intended.
4378
4379 if (typeof ret === "object") {
4380 console.log(JSON.stringify(ret, null, 4));
4381 } else {
4382 console.log(ret);
4383 }
4384 }
4385 } catch (e) {
4386 if (e instanceof AbortError) {
4387 log(chalk`{red CLI Aborted}: ${e.message}`);
4388 } else {
4389 throw e;
4390 }
4391 }
4392 } else {
4393 await noCommand();
4394 }
4395}
4396
4397async function main(...args) {
4398 //Catch all for errors to avoid ugly default node promise catcher
4399 try {
4400 await $main(...args);
4401 } catch (e) {
4402 errorLog(e.stack);
4403 }
4404} // If this is an imported module, then we should exec the cli interface.
4405// Oterwise just export everything.
4406
4407
4408if (require.main === module) {
4409 main();
4410} else {
4411 loadConfig();
4412 module.exports = allIndexBundle;
4413}
4414//# sourceMappingURL=bundle.js.map