UNPKG

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