From 6b2d53b31baa64756af5ade539493a41d211bad6 Mon Sep 17 00:00:00 2001 From: Florian Lippisch Date: Thu, 17 Apr 2025 11:43:41 +0200 Subject: [PATCH 01/45] Adding minor comments --- package.json | 5 +- src/celonis-cli.ts | 66 ++++++++ src/commands/profile.command.ts | 61 ------- src/content-cli-profile.ts | 2 +- src/core/Context.ts | 14 ++ src/core/ModuleHandler.ts | 150 ++++++++++++++++++ src/modules/profile/module.ts | 54 +++++++ src/modules/profile/profile.command.ts | 66 ++++++++ .../profile/services/question.service.ts | 29 ++++ src/modules/test/module.ts | 33 ++++ src/services/question.service.ts | 16 -- src/util/logger.ts | 1 + tsconfig.json | 6 +- yarn.lock | 18 +++ 14 files changed, 440 insertions(+), 81 deletions(-) create mode 100644 src/celonis-cli.ts delete mode 100644 src/commands/profile.command.ts create mode 100644 src/core/Context.ts create mode 100644 src/core/ModuleHandler.ts create mode 100644 src/modules/profile/module.ts create mode 100644 src/modules/profile/profile.command.ts create mode 100644 src/modules/profile/services/question.service.ts create mode 100644 src/modules/test/module.ts delete mode 100644 src/services/question.service.ts diff --git a/package.json b/package.json index 12fca760..fad05402 100644 --- a/package.json +++ b/package.json @@ -23,8 +23,8 @@ "axios": "1.7.9", "commander": "13.1.0", "form-data": "4.0.1", - "openid-client": "5.6.1", "hpagent": "1.2.0", + "openid-client": "5.6.1", "semver": "7.6.3", "valid-url": "1.0.9", "winston": "3.17.0", @@ -42,7 +42,8 @@ "tslint": "5.20.1", "tslint-config-prettier": "1.18.0", "tslint-consistent-codestyle": "1.16.0", - "typescript": "5.3.3" + "typescript": "5.3.3", + "why-is-node-running": "2.3.0" }, "resolutions": { "glob": "10.0.0" diff --git a/src/celonis-cli.ts b/src/celonis-cli.ts new file mode 100644 index 00000000..955b68ac --- /dev/null +++ b/src/celonis-cli.ts @@ -0,0 +1,66 @@ +//const logProc = require('why-is-node-running') + +import semverSatisfies = require("semver/functions/satisfies"); +import {VersionUtils} from "./util/version"; +import { logger } from "./util/logger"; + +import { fileURLToPath } from 'url'; // Needed for ES Modules __dirname equivalent +import path = require("path"); +import * as fs from "fs"; +import { ModuleHandler } from "./core/ModuleHandler"; +import { Command } from "commander"; +import { Context } from "./core/Context"; + + +// Check if the Node.js version satisfies the minimum requirements +const requiredVersion = ">=10.10.0"; +if (!semverSatisfies(process.version, requiredVersion)) { + logger.error( + `Node version ${process.version} not supported. Please upgrade your node version to ${requiredVersion}` + ); + process.exit(1); +} + +const program: Command = new Command(); +program.version(VersionUtils.getCurrentCliVersion()); +program.option("-q, --quitemode", "Reduce output to a minimum", false); +program.option("--debug", "Print debug messages", false); +program.parseOptions(process.argv); + +/** + * Celonis Content CLI. + * + * This is the main entry point for the CLI. + */ +if (!program.opts().quitemode) { + console.log(`Celonis CLI - (C) Copyright 2025 - Celonis SE - Version ${VersionUtils.getCurrentCliVersion()}`); + console.log(); +} + +if (program.opts().debug) { + logger.level = 'debug'; +} + +let context = new Context(); + +let moduleHandler = new ModuleHandler(program, context); + +moduleHandler.discoverAndRegisterModules(__dirname); + + +if (!process.argv.slice(2).length) { + program.outputHelp(); + process.exit(1); +} + +async function run() { + await program.parse(process.argv); + // safe to exit now + /* -- Uncomment the below to find out why the process does not exit... + setTimeout(() => { + console.error("Node is still running. Active Handles:"); + logProc(); // Log details about active handles. + }, 5000); // Wait 5 seconds before logging, adjust as needed + */ +} +run(); \ No newline at end of file diff --git a/src/commands/profile.command.ts b/src/commands/profile.command.ts deleted file mode 100644 index 5a51ff8b..00000000 --- a/src/commands/profile.command.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { QuestionService } from "../services/question.service"; -import {Profile, ProfileType} from "../interfaces/profile.interface"; -import { ProfileService } from "../services/profile.service"; -import { ProfileValidator } from "../validators/profile.validator"; -import { FatalError, logger } from "../util/logger"; - -export class ProfileCommand { - private profileService = new ProfileService(); - - public async createProfile(setAsDefault: boolean): Promise { - const profile: Profile = {} as Profile; - profile.name = await QuestionService.ask("Name of the profile: "); - profile.team = await QuestionService.ask("Your team (please provide the full url): "); - const type = await QuestionService.ask("Profile type: OAuth Device Code (1), OAuth Client Credentials (2) or Application Key / API Key (3): " ); - switch (type) { - case "1": - profile.type = ProfileType.DEVICE_CODE; - break; - case "2": - profile.type = ProfileType.CLIENT_CREDENTIALS; - profile.clientId = await QuestionService.ask("Your client id: "); - profile.clientSecret = await QuestionService.ask("Your client secret: "); - break; - case "3": - profile.type = ProfileType.KEY; - profile.apiToken = await QuestionService.ask("Your api token: "); - break; - default: - logger.error(new FatalError("Invalid type")); - break; - } - profile.authenticationType = await ProfileValidator.validateProfile(profile); - await this.profileService.authorizeProfile(profile); - - this.profileService.storeProfile(profile); - if (setAsDefault) { - await this.makeDefaultProfile(profile.name); - } - logger.info("Profile created successfully!"); - } - - public async listProfiles(): Promise { - this.profileService.readAllProfiles().then((profiles: string[]) => { - const defaultProfile = this.profileService.getDefaultProfile(); - if (profiles) { - profiles.forEach(profile => { - if (defaultProfile && defaultProfile === profile) { - logger.info(profile + " (default)"); - } else { - logger.info(profile); - } - }); - } - }); - } - - public async makeDefaultProfile(profile: string): Promise { - await this.profileService.makeDefaultProfile(profile); - logger.info("Default profile: " + profile); - } -} diff --git a/src/content-cli-profile.ts b/src/content-cli-profile.ts index 5bf8d69a..4ac3b1da 100644 --- a/src/content-cli-profile.ts +++ b/src/content-cli-profile.ts @@ -1,4 +1,4 @@ -import { ProfileCommand } from "./commands/profile.command"; +import { ProfileCommand } from "./modules/profile/profile.command"; import { Command } from "commander"; import { program } from "./util/program"; diff --git a/src/core/Context.ts b/src/core/Context.ts new file mode 100644 index 00000000..d595f8c1 --- /dev/null +++ b/src/core/Context.ts @@ -0,0 +1,14 @@ +import { Profile } from "../interfaces/profile.interface"; +import { logger } from "../util/logger"; + +/** + * The execution context object is passed to the modules to access + * foundational services such as APIs, profiles, logging etc. It is + * configured upon the start of the CLI. + */ + +export class Context { + log = logger; + api = null; // TODO - provide access to an initialized API (http api etc.) + profile: Profile; +} \ No newline at end of file diff --git a/src/core/ModuleHandler.ts b/src/core/ModuleHandler.ts new file mode 100644 index 00000000..698dd82c --- /dev/null +++ b/src/core/ModuleHandler.ts @@ -0,0 +1,150 @@ +import { logger } from "../util/logger"; +import { fileURLToPath } from 'url'; // Needed for ES Modules __dirname equivalent +import path = require("path"); +import * as fs from "fs"; +import { Command, CommandOptions, ExecutableCommandOptions } from "commander"; +import { Context } from "./Context"; + +export interface IModule { + register(context: Context, commandConfig: CommandConfig); +} + +export interface IModuleConstructor { + new(): IModule; +} + +export class ModuleHandler { + + constructor(public program: Command, public context: Context) { + + } + + // Store registered module instances if needed later + registeredModules: IModule[] = []; + + /** + * Discovers modules in the specified directory, imports them, + * instantiates the default exported class, and calls its register method. + * + * @param {any} rootPath - __dirname when invoked from the main entry file + */ + discoverAndRegisterModules(rootPath) { + let modulesDirPath = path.resolve(rootPath, 'modules'); + logger.debug(`Scanning for modules in: ${modulesDirPath}`); + + try { + const moduleFolders = fs.readdirSync(modulesDirPath, { withFileTypes: true }); + + for (const dirent of moduleFolders) { + if (dirent.isDirectory()) { + const moduleFolderName = dirent.name; + + // Calculate path relative to *this file's location in dist* + const potentialModuleJsPath = path.resolve( + rootPath, 'modules', moduleFolderName, + 'module.js' // Look for the compiled JS file + ); + + // Check if the compiled JS file exists + try { + fs.accessSync(potentialModuleJsPath); // Check existence + logger.debug(`Found potential module definition: ${potentialModuleJsPath}`); + + // Dynamically require the module + const requiredModule = require(potentialModuleJsPath); + + // With 'export =' or 'module.exports =', the required value *is* the class + const ModuleClass = requiredModule as IModuleConstructor; // Cast for TS check + + // Basic check: Is it a class (function)? + if (typeof ModuleClass === 'function' && ModuleClass.prototype) { + const moduleInstance: IModule = new ModuleClass(); // Instantiate + + // Check if the instance has the register method + if (typeof moduleInstance.register === 'function') { + logger.debug(`Registering module: ${moduleFolderName}`); + // Call register - can still be async even if require() is sync + let ctx = this.context; + let commandName = moduleFolderName; // may let the module change this + let command = this.program.command(commandName) + moduleInstance.register(ctx, new CommandConfig(command, this.context)); + this.registeredModules.push(moduleInstance); + } else { + logger.warn(`Module ${moduleFolderName} export does not have a 'register' method.`); + } + } else { + logger.warn(`Module ${moduleFolderName} export is not a class/constructor function.`); + } + + } catch (error: any) { + if (error.code === 'ENOENT') { + // Compiled module.js not found, maybe folder doesn't contain a valid module + logger.warn(`Directory ${moduleFolderName} does not contain a compiled module.js file.`); + } else if (error.code === 'MODULE_NOT_FOUND') { + logger.debug(`Error details`, error); + logger.warn(`Could not require module ${moduleFolderName}. Check dependencies or compilation. Path: ${potentialModuleJsPath}`); + } + else { + logger.error(`Error processing module in ${moduleFolderName}:`, error); + } + } + } + } + } catch (error: any) { + if (error.code === 'ENOENT') { + logger.error(`Modules directory not found relative to JS output: ${path.resolve(path.dirname(__filename), 'modules')}`); + } else { + logger.error('Failed to read modules directory:', error); + } + } + logger.debug(`Module discovery complete. ${this.registeredModules.length} modules registered.`); + } + +} + +type CommandHandler = (context: Context, command: Command) => void; + +/** + * Delegate wrapper around the Command object, to simply change the way the program is + * executed. + */ +export class CommandConfig { + constructor(private cmd: Command, private ctx: Context) { + + } + + command(nameAndArgs: string, opts?: CommandOptions) : CommandConfig { + return new CommandConfig(this.cmd.command(nameAndArgs, opts), this.ctx); + } + + description(description: string) { + this.cmd.description(description); + return this; + } + + argument(name: string, description?: string, defaultValue?: unknown): this { + this.cmd.argument(name, description, defaultValue); + return this; + } + + option(flags: string, description?: string, defaultValue?: string | boolean | string[]): this { + this.cmd.option(flags, description, defaultValue); + return this; + } + + requiredOption(flags: string, description?: string, defaultValue?: string | boolean | string[]): this { + this.cmd.requiredOption(flags, description, defaultValue); + return this; + } + + action(handler: CommandHandler) { + let ctx = this.ctx; + this.cmd.action(async function () { + await handler(ctx, this); + // TODO - avoid exiting directly here, so the app can complete. + //process.exit(); + }) + } + +} + diff --git a/src/modules/profile/module.ts b/src/modules/profile/module.ts new file mode 100644 index 00000000..9a55af07 --- /dev/null +++ b/src/modules/profile/module.ts @@ -0,0 +1,54 @@ +/** + * Commands to create and list access profiles. + */ + +import { Command } from "commander"; +import { CommandConfig, IModule } from "../../core/ModuleHandler"; +import { logger } from "../../util/logger"; +import { Context } from "../../core/Context"; +import { ProfileCommand } from "./profile.command"; + +class ProfileModule implements IModule { + + register(context: Context, command: CommandConfig) { + + // action if no command is provided + command.action(this.showHelp); + + // let's add a subcommand + command.command("list") + .description("Command to list all stored profiles") + .action(this.listProfiles); + + command.command("create") + .description("Command to create a new profile") + .option("--setAsDefault", "Set this profile as default") + .action(this.createProfile); + + command.command("default ") + .description("Command to set a profile as default") + .action(this.defaultProfile); + + } + + async defaultProfile(context: Context, command: Command) { + let profile = command.args[0]; + await new ProfileCommand().makeDefaultProfile(profile); + } + + async createProfile(context: Context, command: Command) { + let options = command.opts(); + await new ProfileCommand().createProfile(options.setAsDefault); + } + + async listProfiles(context: Context, command: Command) { + logger.debug(`List profiles`); + await new ProfileCommand().listProfiles(); + } + + showHelp(context: Context, command: Command) { + command.outputHelp(); + } +} + +export = ProfileModule; \ No newline at end of file diff --git a/src/modules/profile/profile.command.ts b/src/modules/profile/profile.command.ts new file mode 100644 index 00000000..6c654115 --- /dev/null +++ b/src/modules/profile/profile.command.ts @@ -0,0 +1,66 @@ +import { QuestionService } from "./services/question.service"; +import {Profile, ProfileType} from "../../interfaces/profile.interface"; +import { ProfileService } from "../../services/profile.service"; +import { ProfileValidator } from "../../validators/profile.validator"; +import { FatalError, logger } from "../../util/logger"; + +export class ProfileCommand { + private profileService = new ProfileService(); + + public async createProfile(setAsDefault: boolean): Promise { + const profile: Profile = {} as Profile; + const questions = new QuestionService(); + try { + profile.name = await questions.ask("Name of the profile: "); + profile.team = await questions.ask("Your team (please provide the full url): "); + const type = await questions.ask("Profile type: OAuth Device Code (1), OAuth Client Credentials (2) or Application Key / API Key (3): " ); + switch (type) { + case "1": + profile.type = ProfileType.DEVICE_CODE; + break; + case "2": + profile.type = ProfileType.CLIENT_CREDENTIALS; + profile.clientId = await questions.ask("Your client id: "); + profile.clientSecret = await questions.ask("Your client secret: "); + break; + case "3": + profile.type = ProfileType.KEY; + profile.apiToken = await questions.ask("Your api token: "); + break; + default: + logger.error(new FatalError("Invalid type")); + break; + } + profile.authenticationType = await ProfileValidator.validateProfile(profile); + await this.profileService.authorizeProfile(profile); + + this.profileService.storeProfile(profile); + if (setAsDefault) { + await this.makeDefaultProfile(profile.name); + } + } finally { + questions.close(); + } + logger.info("Profile created successfully!"); + } + + public async listProfiles(): Promise { + this.profileService.readAllProfiles().then((profiles: string[]) => { + const defaultProfile = this.profileService.getDefaultProfile(); + if (profiles) { + profiles.forEach(profile => { + if (defaultProfile && defaultProfile === profile) { + logger.info(profile + " (default)"); + } else { + logger.info(profile); + } + }); + } + }); + } + + public async makeDefaultProfile(profile: string): Promise { + await this.profileService.makeDefaultProfile(profile); + logger.info("Default profile: " + profile); + } +} diff --git a/src/modules/profile/services/question.service.ts b/src/modules/profile/services/question.service.ts new file mode 100644 index 00000000..a6281c6b --- /dev/null +++ b/src/modules/profile/services/question.service.ts @@ -0,0 +1,29 @@ +import * as readline from "readline"; +import { ReadLine } from "readline"; + +/** + * Simple method to get input from the user/console using ReadLine. The + * class must be closed in order to avoid the CLI to not terminate properly. + */ +export class QuestionService { + + readLine: ReadLine; + + constructor() { + this.readLine = readline.createInterface({ + input: process.stdin, + output: process.stdout, + terminal: true, + }); + } + + async ask(question: string): Promise { + return new Promise((resolve) => { + this.readLine.question(question, input => resolve(input)); + }); + } + + close() { + this.readLine.close(); + } +} diff --git a/src/modules/test/module.ts b/src/modules/test/module.ts new file mode 100644 index 00000000..797a826e --- /dev/null +++ b/src/modules/test/module.ts @@ -0,0 +1,33 @@ +/** + * Configures the module commands, options, etc. + */ + +import { Command } from "commander"; +import { CommandConfig, IModule } from "../../core/ModuleHandler"; +import { logger } from "../../util/logger"; +import { Context } from "../../core/Context"; + +class TestModule implements IModule { + register(context: Context, command: CommandConfig) { + + command.option('-k, --key [string]', 'The key'); + command.action(this.invoke); + + // let's add a subcommand + command.command('blink') + .description('Blink a few times') + .option('-c, --count [number]') + .action(this.blink); + } + + blink(context: Context, command: Command) { + logger.info(`I will blink`); + } + + invoke(context: Context, command: Command) { + let options = command.opts(); + logger.info(`invoking me, key is ${options.key}`); + } +} + +export = TestModule; \ No newline at end of file diff --git a/src/services/question.service.ts b/src/services/question.service.ts deleted file mode 100644 index 926e656e..00000000 --- a/src/services/question.service.ts +++ /dev/null @@ -1,16 +0,0 @@ -import * as readline from "readline"; -import { ReadLine } from "readline"; - -export class QuestionService { - private static readLine: ReadLine = readline.createInterface({ - input: process.stdin, - output: process.stdout, - terminal: true, - }); - - public static async ask(question: string): Promise { - return new Promise((resolve, reject) => { - this.readLine.question(question, input => resolve(input)); - }); - } -} diff --git a/src/util/logger.ts b/src/util/logger.ts index 75cbb159..4792747c 100644 --- a/src/util/logger.ts +++ b/src/util/logger.ts @@ -21,6 +21,7 @@ class CustomTransport extends Transport { export const logger: Logger = winston.createLogger({ format: winston.format.combine(winston.format.cli()), + level: 'info', transports: [new winston.transports.Console(), new CustomTransport({})], exceptionHandlers: [new winston.transports.Console(), new CustomTransport({})], exitOnError: true, diff --git a/tsconfig.json b/tsconfig.json index b3e7c60f..9614f1fb 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,6 +2,10 @@ "compilerOptions": { "lib": ["es2015", "es6", "dom", "esnext"], "outDir": "./dist/", + "baseUrl": ".", + "paths": { + "@/*": ["src/*"] + }, "declarationDir": "./dist/lib", "noImplicitAny": false, "target": "es2015", @@ -14,5 +18,5 @@ "experimentalDecorators": true, "types": ["node", "jest"], }, - "exclude": ["./tests", "./jest.config.ts"] + "exclude": ["./tests", "./jest.config.ts", "./dist/"] } diff --git a/yarn.lock b/yarn.lock index 41c43a62..8d290bd3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4814,6 +4814,11 @@ shebang-regex@^3.0.0: resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== +siginfo@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/siginfo/-/siginfo-2.0.0.tgz#32e76c70b79724e3bb567cb9d543eb858ccfaf30" + integrity sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g== + signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: version "3.0.7" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" @@ -4958,6 +4963,11 @@ stack-utils@^2.0.3: dependencies: escape-string-regexp "^2.0.0" +stackback@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/stackback/-/stackback-0.0.2.tgz#1ac8a0d9483848d1695e418b6d031a3c3ce68e3b" + integrity sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw== + stream-events@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/stream-events/-/stream-events-1.0.5.tgz#bbc898ec4df33a4902d892333d47da9bf1c406d5" @@ -5377,6 +5387,14 @@ which@^2.0.1: dependencies: isexe "^2.0.0" +why-is-node-running@2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/why-is-node-running/-/why-is-node-running-2.3.0.tgz#a3f69a97107f494b3cdc3bdddd883a7d65cebf04" + integrity sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w== + dependencies: + siginfo "^2.0.0" + stackback "0.0.2" + winston-transport@^4.9.0: version "4.9.0" resolved "https://registry.yarnpkg.com/winston-transport/-/winston-transport-4.9.0.tgz#3bba345de10297654ea6f33519424560003b3bf9" From 2145ce2ecbd74a74657234e34534ca0c9584bbc0 Mon Sep 17 00:00:00 2001 From: Florian Lippisch Date: Thu, 17 Apr 2025 14:53:24 +0200 Subject: [PATCH 02/45] Profile is now loaded as part of the context --- src/celonis-cli.ts | 32 ++++++++-------- src/core/Context.ts | 37 +++++++++++++++++++ src/modules/list/module.ts | 37 +++++++++++++++++++ src/modules/profile/profile.command.ts | 2 +- .../{services => }/question.service.ts | 0 src/modules/test/module.ts | 2 +- tsconfig.json | 4 -- 7 files changed, 91 insertions(+), 23 deletions(-) create mode 100644 src/modules/list/module.ts rename src/modules/profile/{services => }/question.service.ts (100%) diff --git a/src/celonis-cli.ts b/src/celonis-cli.ts index 955b68ac..79f8eaf1 100644 --- a/src/celonis-cli.ts +++ b/src/celonis-cli.ts @@ -3,10 +3,6 @@ import semverSatisfies = require("semver/functions/satisfies"); import {VersionUtils} from "./util/version"; import { logger } from "./util/logger"; - -import { fileURLToPath } from 'url'; // Needed for ES Modules __dirname equivalent -import path = require("path"); -import * as fs from "fs"; import { ModuleHandler } from "./core/ModuleHandler"; import { Command } from "commander"; import { Context } from "./core/Context"; @@ -24,6 +20,7 @@ if (!semverSatisfies(process.version, requiredVersion)) { const program: Command = new Command(); program.version(VersionUtils.getCurrentCliVersion()); program.option("-q, --quitemode", "Reduce output to a minimum", false); +program.option("-p, --profile [profile]"); program.option("--debug", "Print debug messages", false); program.parseOptions(process.argv); @@ -41,21 +38,22 @@ if (program.opts().debug) { logger.level = 'debug'; } -let context = new Context(); - -let moduleHandler = new ModuleHandler(program, context); - -moduleHandler.discoverAndRegisterModules(__dirname); - +async function run() { -if (!process.argv.slice(2).length) { - program.outputHelp(); - process.exit(1); -} + let context = new Context(program.opts()); + await context.init(); -async function run() { - await program.parse(process.argv); - // safe to exit now + let moduleHandler = new ModuleHandler(program, context); + + moduleHandler.discoverAndRegisterModules(__dirname); + + if (!process.argv.slice(2).length) { + program.outputHelp(); + process.exit(1); + } + + program.parse(process.argv); + /* -- Uncomment the below to find out why the process does not exit... setTimeout(() => { console.error("Node is still running. Active Handles:"); diff --git a/src/core/Context.ts b/src/core/Context.ts index d595f8c1..836a1ade 100644 --- a/src/core/Context.ts +++ b/src/core/Context.ts @@ -1,3 +1,4 @@ +import { ProfileService } from "../services/profile.service"; import { Profile } from "../interfaces/profile.interface"; import { logger } from "../util/logger"; @@ -11,4 +12,40 @@ export class Context { log = logger; api = null; // TODO - provide access to an initialized API (http api etc.) profile: Profile; + profileName: string | undefined; + + private profileService = new ProfileService(); + + constructor(options: any) { + this.profileName = options.profile; + } + + async init() { + await this.loadProfile(this.profileName); + } + + async loadProfile(profileName: string | undefined) { + if (!profileName) { + this.log.debug(`Profile name not specified, using default profile name`); + profileName = this.profileService.getDefaultProfile(); + if (!profileName) { + this.log.debug(`A default profile is not configured.`); + } + } + if (profileName) { + try { + this.profile = await this.profileService.findProfile(profileName); + this.profileName = profileName; + this.log.info(`Using profile ${profileName}`); + } catch (err) { + // TODO - The error message is incorrect, overriding it here for the time being. + // change it though after the ProfileService is completely migrated/fixed. + //this.log.error(err); + this.log.error(`The profile ${profileName} cannot be loaded.`); + this.profile = undefined; + this.profileName = undefined; + } + } + } + } \ No newline at end of file diff --git a/src/modules/list/module.ts b/src/modules/list/module.ts new file mode 100644 index 00000000..4e48a3cb --- /dev/null +++ b/src/modules/list/module.ts @@ -0,0 +1,37 @@ +/** + * Commands to create and list access profiles. + */ + +import { Command } from "commander"; +import { CommandConfig, IModule } from "../../core/ModuleHandler"; +import { logger } from "../../util/logger"; +import { Context } from "../../core/Context"; +import { SpaceCommand } from "../../commands/space.command"; + +class ListModule implements IModule { + + register(context: Context, command: CommandConfig) { + + // action if no command is provided + command.action(this.showHelp); + + // let's add a subcommand + command.command("spaces") + .description("Command to list all spaces") + .option("--json", "Return response as json type", "") + .action(this.listSpaces); + + } + + async listSpaces(context: Context, command: Command) { + let options = command.opts(); + await new SpaceCommand().listSpaces(undefined, options.json); + } + + + showHelp(context: Context, command: Command) { + command.outputHelp(); + } +} + +export = ListModule; \ No newline at end of file diff --git a/src/modules/profile/profile.command.ts b/src/modules/profile/profile.command.ts index 6c654115..3f3cacc8 100644 --- a/src/modules/profile/profile.command.ts +++ b/src/modules/profile/profile.command.ts @@ -1,4 +1,4 @@ -import { QuestionService } from "./services/question.service"; +import { QuestionService } from "./question.service"; import {Profile, ProfileType} from "../../interfaces/profile.interface"; import { ProfileService } from "../../services/profile.service"; import { ProfileValidator } from "../../validators/profile.validator"; diff --git a/src/modules/profile/services/question.service.ts b/src/modules/profile/question.service.ts similarity index 100% rename from src/modules/profile/services/question.service.ts rename to src/modules/profile/question.service.ts diff --git a/src/modules/test/module.ts b/src/modules/test/module.ts index 797a826e..9d79e8f3 100644 --- a/src/modules/test/module.ts +++ b/src/modules/test/module.ts @@ -26,7 +26,7 @@ class TestModule implements IModule { invoke(context: Context, command: Command) { let options = command.opts(); - logger.info(`invoking me, key is ${options.key}`); + logger.info(`Test invocation, key is ${options.key}. Profile is ${context.profileName}`); } } diff --git a/tsconfig.json b/tsconfig.json index 9614f1fb..97f1f6ce 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,10 +2,6 @@ "compilerOptions": { "lib": ["es2015", "es6", "dom", "esnext"], "outDir": "./dist/", - "baseUrl": ".", - "paths": { - "@/*": ["src/*"] - }, "declarationDir": "./dist/lib", "noImplicitAny": false, "target": "es2015", From 97a056dc71cc28e09346a9baf3aaeef57bcaef90 Mon Sep 17 00:00:00 2001 From: Florian Lippisch Date: Mon, 21 Apr 2025 08:04:42 +0200 Subject: [PATCH 03/45] Logger now writes logs into the config directory --- src/celonis-cli.ts | 9 +- src/core/{Context.ts => cli-context.ts} | 9 +- src/core/http-client.ts | 212 ++++++++++++++++++ .../{ModuleHandler.ts => module-handler.ts} | 2 +- src/modules/list/module.ts | 17 +- src/modules/profile/module.ts | 4 +- src/modules/test/module.ts | 4 +- src/util/logger.ts | 72 +++++- 8 files changed, 313 insertions(+), 16 deletions(-) rename src/core/{Context.ts => cli-context.ts} (83%) create mode 100644 src/core/http-client.ts rename src/core/{ModuleHandler.ts => module-handler.ts} (99%) diff --git a/src/celonis-cli.ts b/src/celonis-cli.ts index 79f8eaf1..b55ae311 100644 --- a/src/celonis-cli.ts +++ b/src/celonis-cli.ts @@ -3,9 +3,9 @@ import semverSatisfies = require("semver/functions/satisfies"); import {VersionUtils} from "./util/version"; import { logger } from "./util/logger"; -import { ModuleHandler } from "./core/ModuleHandler"; +import { ModuleHandler } from "./core/module-handler"; import { Command } from "commander"; -import { Context } from "./core/Context"; +import { Context } from "./core/cli-context"; // Check if the Node.js version satisfies the minimum requirements @@ -49,11 +49,10 @@ async function run() { if (!process.argv.slice(2).length) { program.outputHelp(); - process.exit(1); } program.parse(process.argv); - + logger.end(); /* -- Uncomment the below to find out why the process does not exit... setTimeout(() => { console.error("Node is still running. Active Handles:"); @@ -61,4 +60,4 @@ async function run() { }, 5000); // Wait 5 seconds before logging, adjust as needed */ } -run(); \ No newline at end of file +run(); diff --git a/src/core/Context.ts b/src/core/cli-context.ts similarity index 83% rename from src/core/Context.ts rename to src/core/cli-context.ts index 836a1ade..29d0cc0a 100644 --- a/src/core/Context.ts +++ b/src/core/cli-context.ts @@ -1,6 +1,7 @@ import { ProfileService } from "../services/profile.service"; import { Profile } from "../interfaces/profile.interface"; import { logger } from "../util/logger"; +import { HttpClient } from "./http-client"; /** * The execution context object is passed to the modules to access @@ -10,7 +11,7 @@ import { logger } from "../util/logger"; export class Context { log = logger; - api = null; // TODO - provide access to an initialized API (http api etc.) + httpClient: HttpClient; // TODO - provide access to an initialized API (http api etc.) profile: Profile; profileName: string | undefined; @@ -22,6 +23,12 @@ export class Context { async init() { await this.loadProfile(this.profileName); + + if (this.profile) { + // only if a profile is available, it makes sense to provide an initialized + // HttpClient API. + this.httpClient = new HttpClient(this); + } } async loadProfile(profileName: string | undefined) { diff --git a/src/core/http-client.ts b/src/core/http-client.ts new file mode 100644 index 00000000..c4ffb42a --- /dev/null +++ b/src/core/http-client.ts @@ -0,0 +1,212 @@ +import { AuthenticationType, Profile } from "../interfaces/profile.interface"; +import { FatalError, logger } from "../util/logger"; +import {TracingUtils} from "../util/tracing"; +import {VersionUtils} from "../util/version"; +import {AxiosResponse, RawAxiosRequestHeaders} from "axios"; +import * as FormData from "form-data"; +import { AxiosInitializer } from "../util/axios-initializer"; +import { Context } from "./cli-context"; + +/** + * Http client configured based upon the CLI context. It will authenticate + * based on the profile in the context and also use the base URL accordingly. + */ +export class HttpClient { + + private axios = AxiosInitializer.initializeAxios(); + + constructor(private context: Context) {} + + public async get(url: string): Promise { + return new Promise((resolve, reject) => { + this.axios.get(this.resolveUrl(url), { + headers: this.buildHeaders() + }).then(response => { + this.handleResponse(response, resolve, reject); + }).catch(err => { + this.handleError(err, resolve, reject); + }) + }).catch(e => { + throw new FatalError(e); + }) + } + + public async getFile(url: string): Promise { + return new Promise((resolve, reject) => { + this.axios.get(this.resolveUrl(url), { + headers: this.buildHeaders(), + responseType: "stream", + validateStatus: status => status >= 200 + }).then(response => { + const data: Buffer[] = []; + response.data.on("data", (chunk: Buffer) => { + data.push(chunk); + }); + response.data.on("end", () => { + if (response.status !== 200) { + reject(Buffer.concat(data as any).toString()); + return; + } + + this.handleResponseStreamData(Buffer.concat(data as any), resolve, reject); + }); + }).catch(err => { + this.handleError(err, resolve, reject); + }) + }); + } + + public async postFile(url: string, formData: FormData, parameters?: {}): Promise { + return new Promise((resolve, reject) => { + this.axios.post( + this.resolveUrl(url), + formData, + { + headers: { + ...this.buildHeaders("multipart/form-data"), + ...formData.getHeaders() + }, + params: parameters + } + ).then(response => { + this.handleResponse(response, resolve, reject); + }).catch(err => { + this.handleError(err, resolve, reject); + }) + }).catch(e => { + throw new FatalError(e); + }) + } + + public async post(url: string, body: any): Promise { + return new Promise((resolve, reject) => { + this.axios.post( + this.resolveUrl(url), + typeof body === "string" || body instanceof String ? body : JSON.stringify(body), + { + headers: this.buildHeaders("application/json;charset=utf-8") + } + ).then(response => { + this.handleResponse(response, resolve, reject); + }).catch(err => { + this.handleError(err, resolve, reject); + }) + }).catch(e => { + throw new FatalError(e); + }) + } + + public async put(url: string, body: object): Promise { + return new Promise((resolve, reject) => { + this.axios.put( + this.resolveUrl(url), + JSON.stringify(body), + { + headers: this.buildHeaders("application/json;charset=utf-8") + } + ).then(response => { + this.handleResponse(response, resolve, reject); + }).catch(err => { + this.handleError(err, resolve, reject); + }) + }).catch(e => { + throw new FatalError(e); + }) + } + + public async delete(url: string): Promise { + return new Promise((resolve, reject) => { + this.axios.delete(this.resolveUrl(url), { + headers: this.buildHeaders("application/json;charset=utf-8") + }).then(response => { + this.handleResponse(response, resolve, reject); + }).catch(err => { + this.handleError(err, resolve, reject); + }) + }).catch(e => { + throw new FatalError(e); + }) + } + + public async downloadFile(url: string): Promise { + return new Promise((resolve, reject) => { + this.axios.post(this.resolveUrl(url), null, { + headers: this.buildHeaders(), + responseType: "stream" + }).then(response => { + const data: Buffer[] = []; + response.data.on("data", (chunk: Buffer) => { + data.push(chunk); + }); + response.data.on("end", () => { + if (this.checkBadRequest(response.status)) { + this.handleBadRequest(response.status, data.toString(), reject); + } else { + this.handleResponseStreamData(Buffer.concat(data as any), resolve, reject); + } + }) + }).catch(err => { + this.handleError(err, resolve, reject); + }) + }); + } + + private handleResponseStreamData(data, resolve, reject): void { + if (data) { + resolve(data); + return; + } + + logger.error("Could not get file stream from response"); + reject(); + } + + private resolveUrl(url: string): string { + return this.context.profile.team.replace(/\/?$/, url); + } + + private handleResponse(res: AxiosResponse, resolve, reject): void { + if (this.checkBadRequest(res.status)) { + this.handleBadRequest(res.status, res.data, reject); + return; + } + resolve(res.data); + } + + private handleError(err: any, resolve, reject): void { + if (err.response) { + this.handleResponse(err.response, resolve, reject); + } else { + reject(err.message); + } + } + + private checkBadRequest(statusCode: number): boolean { + return statusCode >= 400; + } + + // tslint:disable-next-line:typedef + private handleBadRequest(statusCode, data, reject): void { + if (data) { + reject(JSON.stringify(data)); + } else { + reject("Backend responded with status code " + statusCode); + } + } + + private buildHeaders(contentType?: string): RawAxiosRequestHeaders { + return { + ...this.buildAuthorizationHeaders(this.context.profile, contentType), + ...TracingUtils.getTracingHeaders(), + "User-Agent": "content-cli v" + VersionUtils.getCurrentCliVersion() + } + } + + private buildAuthorizationHeaders(profile: Profile, contentType?: string): RawAxiosRequestHeaders { + const authenticationType = profile.authenticationType || AuthenticationType.BEARER; + return { + Authorization: `${authenticationType} ${profile.apiToken}`, + "Content-Type": contentType ?? "application/json", + }; + } +} diff --git a/src/core/ModuleHandler.ts b/src/core/module-handler.ts similarity index 99% rename from src/core/ModuleHandler.ts rename to src/core/module-handler.ts index 698dd82c..83e2ce57 100644 --- a/src/core/ModuleHandler.ts +++ b/src/core/module-handler.ts @@ -3,7 +3,7 @@ import { fileURLToPath } from 'url'; // Needed for ES Modules __dirname equivale import path = require("path"); import * as fs from "fs"; import { Command, CommandOptions, ExecutableCommandOptions } from "commander"; -import { Context } from "./Context"; +import { Context } from "./cli-context"; export interface IModule { register(context: Context, commandConfig: CommandConfig); diff --git a/src/modules/list/module.ts b/src/modules/list/module.ts index 4e48a3cb..ee74384f 100644 --- a/src/modules/list/module.ts +++ b/src/modules/list/module.ts @@ -3,10 +3,11 @@ */ import { Command } from "commander"; -import { CommandConfig, IModule } from "../../core/ModuleHandler"; +import { CommandConfig, IModule } from "../../core/module-handler"; import { logger } from "../../util/logger"; -import { Context } from "../../core/Context"; +import { Context } from "../../core/cli-context"; import { SpaceCommand } from "../../commands/space.command"; +import { PackageCommand } from "../../commands/package.command"; class ListModule implements IModule { @@ -16,6 +17,13 @@ class ListModule implements IModule { command.action(this.showHelp); // let's add a subcommand + command.command("packages") + .description("Command to list all packages") + .option("--json", "Return response as json type", "") + .option("--includeDependencies", "Include variables and dependencies", "") + .option("--packageKeys ", "Lists only given package keys") + .action(this.listPackages); + command.command("spaces") .description("Command to list all spaces") .option("--json", "Return response as json type", "") @@ -23,6 +31,11 @@ class ListModule implements IModule { } + async listPackages(context: Context, command: Command) { + let options = command.opts(); + await new PackageCommand().listPackages(options.json, options.includeDependencies, options.packageKeys); + } + async listSpaces(context: Context, command: Command) { let options = command.opts(); await new SpaceCommand().listSpaces(undefined, options.json); diff --git a/src/modules/profile/module.ts b/src/modules/profile/module.ts index 9a55af07..3aab4461 100644 --- a/src/modules/profile/module.ts +++ b/src/modules/profile/module.ts @@ -3,9 +3,9 @@ */ import { Command } from "commander"; -import { CommandConfig, IModule } from "../../core/ModuleHandler"; +import { CommandConfig, IModule } from "../../core/module-handler"; import { logger } from "../../util/logger"; -import { Context } from "../../core/Context"; +import { Context } from "../../core/cli-context"; import { ProfileCommand } from "./profile.command"; class ProfileModule implements IModule { diff --git a/src/modules/test/module.ts b/src/modules/test/module.ts index 9d79e8f3..da96fd15 100644 --- a/src/modules/test/module.ts +++ b/src/modules/test/module.ts @@ -3,9 +3,9 @@ */ import { Command } from "commander"; -import { CommandConfig, IModule } from "../../core/ModuleHandler"; +import { CommandConfig, IModule } from "../../core/module-handler"; import { logger } from "../../util/logger"; -import { Context } from "../../core/Context"; +import { Context } from "../../core/cli-context"; class TestModule implements IModule { register(context: Context, command: CommandConfig) { diff --git a/src/util/logger.ts b/src/util/logger.ts index 4792747c..5a2e09b6 100644 --- a/src/util/logger.ts +++ b/src/util/logger.ts @@ -1,6 +1,9 @@ import * as winston from "winston"; import * as Transport from "winston-transport"; import { Logger } from "winston"; +import os = require("os"); +import * as path from "path"; +import * as fs from "fs"; class CustomTransport extends Transport { constructor(opts: any) { @@ -19,11 +22,74 @@ class CustomTransport extends Transport { } } +// log into the same directory as where the profiles are, to avoid creating more +// directories for now. Consider changing this to a general 'home' directory such +// as .celonis-cli or alike. +const logDirName = ".celonis-content-cli-profiles"; +const logFileName = 'celonis-cli.log'; +const exceptionLogFileName = 'exceptions.log'; +const maxLogSizeMB = 3; +const logDir = path.join(os.homedir(), logDirName); +const logFilePath = path.join(logDir, logFileName); +const exceptionLogFilePath = path.join(logDir, exceptionLogFileName); +const maxSizeBytes = maxLogSizeMB * 1024 * 1024; // 3 MB in bytes + +// --- Ensure log directory exists --- +try { + if (!fs.existsSync(logDir)) { + fs.mkdirSync(logDir, { recursive: true }); + } +} catch (error) { + console.error(`Error creating log directory: ${logDir}`, error); +} + +console.log(`Logging to ${logFilePath}`); + export const logger: Logger = winston.createLogger({ format: winston.format.combine(winston.format.cli()), - level: 'info', - transports: [new winston.transports.Console(), new CustomTransport({})], - exceptionHandlers: [new winston.transports.Console(), new CustomTransport({})], + level: 'debug', + transports: [ + new winston.transports.Console({ + level: 'info', + format: winston.format.combine( + winston.format.colorize(), + winston.format.cli() + ), + }), + new winston.transports.File({ + level: 'debug', // Log everything from debug up to the file + filename: logFilePath, + format: winston.format.combine( + winston.format.timestamp(), // Add timestamp to file logs + winston.format.errors({ stack: true }), // Log stack traces + winston.format.json() // Log in JSON format + ), + maxsize: maxSizeBytes, + maxFiles: 5, + tailable: true, + }), + new CustomTransport({}) + ], + exceptionHandlers: [ + new winston.transports.Console({ + format: winston.format.combine( + winston.format.colorize(), + winston.format.cli() + ), + }), + new winston.transports.File({ + filename: exceptionLogFilePath, // Separate file recommended + format: winston.format.combine( + winston.format.timestamp(), + winston.format.errors({ stack: true }), + winston.format.json() + ), + maxsize: maxSizeBytes, + maxFiles: 2, // Keep fewer exception logs if desired + tailable: true, + }), + new CustomTransport({}) + ], exitOnError: true, }); From 1b0bbde12930d8fd6d2418379ea6260f65c6585b Mon Sep 17 00:00:00 2001 From: Florian Lippisch Date: Mon, 21 Apr 2025 08:09:35 +0200 Subject: [PATCH 04/45] fix for --debug --- src/celonis-cli.ts | 4 +++- src/util/logger.ts | 4 +--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/celonis-cli.ts b/src/celonis-cli.ts index b55ae311..07d8fedc 100644 --- a/src/celonis-cli.ts +++ b/src/celonis-cli.ts @@ -35,7 +35,9 @@ if (!program.opts().quitemode) { } if (program.opts().debug) { - logger.level = 'debug'; + logger.transports.forEach(t => { + t.level = 'debug'; + }); } async function run() { diff --git a/src/util/logger.ts b/src/util/logger.ts index 5a2e09b6..72909e0b 100644 --- a/src/util/logger.ts +++ b/src/util/logger.ts @@ -43,8 +43,6 @@ try { console.error(`Error creating log directory: ${logDir}`, error); } -console.log(`Logging to ${logFilePath}`); - export const logger: Logger = winston.createLogger({ format: winston.format.combine(winston.format.cli()), level: 'debug', @@ -57,7 +55,7 @@ export const logger: Logger = winston.createLogger({ ), }), new winston.transports.File({ - level: 'debug', // Log everything from debug up to the file + level: 'info', // Log everything from debug up to the file filename: logFilePath, format: winston.format.combine( winston.format.timestamp(), // Add timestamp to file logs From 101a96c59707d532b3a0b97310d229c4d288815d Mon Sep 17 00:00:00 2001 From: Florian Lippisch Date: Tue, 22 Apr 2025 09:15:49 +0200 Subject: [PATCH 05/45] Migrated list command as an example implementation --- src/celonis-cli.ts | 20 +++- src/core/http-client.ts | 7 +- src/core/module-handler.ts | 8 +- src/modules/list/list-api.ts | 95 +++++++++++++++++++ src/modules/list/list-commands.ts | 17 ++++ src/modules/list/list-service.ts | 85 +++++++++++++++++ src/modules/list/module.ts | 4 +- src/modules/test/module.ts | 3 + .../package-manager/package-service.ts | 1 + src/util/logger.ts | 2 +- 10 files changed, 231 insertions(+), 11 deletions(-) create mode 100644 src/modules/list/list-api.ts create mode 100644 src/modules/list/list-commands.ts create mode 100644 src/modules/list/list-service.ts diff --git a/src/celonis-cli.ts b/src/celonis-cli.ts index 07d8fedc..4b06cd14 100644 --- a/src/celonis-cli.ts +++ b/src/celonis-cli.ts @@ -19,7 +19,7 @@ if (!semverSatisfies(process.version, requiredVersion)) { const program: Command = new Command(); program.version(VersionUtils.getCurrentCliVersion()); -program.option("-q, --quitemode", "Reduce output to a minimum", false); +program.option("-q, --quietmode", "Reduce output to a minimum", false); program.option("-p, --profile [profile]"); program.option("--debug", "Print debug messages", false); program.parseOptions(process.argv); @@ -29,7 +29,7 @@ program.parseOptions(process.argv); * * This is the main entry point for the CLI. */ -if (!program.opts().quitemode) { +if (!program.opts().quietmode) { console.log(`Celonis CLI - (C) Copyright 2025 - Celonis SE - Version ${VersionUtils.getCurrentCliVersion()}`); console.log(); } @@ -53,8 +53,12 @@ async function run() { program.outputHelp(); } - program.parse(process.argv); - logger.end(); + try { + program.parse(process.argv); + } catch (error) { + logger.error(`An unexpected error occured: ${error}`); + } + /* -- Uncomment the below to find out why the process does not exit... setTimeout(() => { console.error("Node is still running. Active Handles:"); @@ -63,3 +67,11 @@ async function run() { */ } run(); + +// catch uncaught exceptions +process.on('uncaughtException', (error: Error, origin: NodeJS.UncaughtExceptionOrigin) => { + console.error(`\n💥 UNCAUGHT EXCEPTION!\n`); + console.error('Error:', error); + console.error('Origin:', origin); + process.exit(1); +}); \ No newline at end of file diff --git a/src/core/http-client.ts b/src/core/http-client.ts index c4ffb42a..94eb8484 100644 --- a/src/core/http-client.ts +++ b/src/core/http-client.ts @@ -18,15 +18,20 @@ export class HttpClient { constructor(private context: Context) {} public async get(url: string): Promise { + const fullUrl = this.resolveUrl(url); + logger.debug(`HttpClient - GET ${fullUrl}`); return new Promise((resolve, reject) => { - this.axios.get(this.resolveUrl(url), { + this.axios.get(fullUrl, { headers: this.buildHeaders() }).then(response => { + logger.debug(`Response ${response.status}`); this.handleResponse(response, resolve, reject); }).catch(err => { + logger.error(err); this.handleError(err, resolve, reject); }) }).catch(e => { + logger.error(e); throw new FatalError(e); }) } diff --git a/src/core/module-handler.ts b/src/core/module-handler.ts index 83e2ce57..32589840 100644 --- a/src/core/module-handler.ts +++ b/src/core/module-handler.ts @@ -140,9 +140,11 @@ export class CommandConfig { action(handler: CommandHandler) { let ctx = this.ctx; this.cmd.action(async function () { - await handler(ctx, this); - // TODO - avoid exiting directly here, so the app can complete. - //process.exit(); + try { + await handler(ctx, this); + } catch (error) { + logger.error(`An unexpected error occured executing a command: ${error}`); + } }) } diff --git a/src/modules/list/list-api.ts b/src/modules/list/list-api.ts new file mode 100644 index 00000000..b4e1bcbf --- /dev/null +++ b/src/modules/list/list-api.ts @@ -0,0 +1,95 @@ +import { Context } from "../../core/cli-context"; +import { HttpClient } from "../../core/http-client"; +import { + ActivatePackageTransport, + ContentNodeTransport, + PackageHistoryTransport, + PackageManagerVariableType, + PackageWithVariableAssignments +} from "../../interfaces/package-manager.interfaces"; +import {FatalError} from "../../util/logger"; + + +export class ListApi { + + httpClient: HttpClient; + + constructor(private context : Context) { + this.httpClient = context.httpClient; + } + + public async findAllPackages(): Promise { + return this.httpClient.get("/package-manager/api/packages").catch(e => { + throw new FatalError(`Problem getting packages: ${e}`); + }); + } + + public async exportPackage(rootPackageKey: string, version?: string, excludeActionFlows?: boolean): Promise { + const queryParams = new URLSearchParams(); + queryParams.set("newKey", rootPackageKey); + queryParams.set("version", version ?? ""); + queryParams.set("excludeActionFlows", excludeActionFlows ? "true" : "false"); + + return await this.httpClient.downloadFile(`/package-manager/api/packages/${rootPackageKey}/export?${queryParams.toString()}`).catch(e => { + throw new FatalError(`Pacakge ${rootPackageKey}_${version} failed to export.`) + }); + } + + public async findAllPackagesWithVariableAssignments(type: PackageManagerVariableType): Promise { + const queryParams = new URLSearchParams(); + if (type) { + queryParams.set("type", type); + } + + return this.httpClient.get(`/package-manager/api/packages/with-variable-assignments?${queryParams.toString()}`).catch(e => { + throw new FatalError(`Problem getting variables of packages: : ${e}`); + }); + } + + public async findLatestVersionById(nodeId: string): Promise { + return this.httpClient.get(`/package-manager/api/packages/${nodeId}/latest-version`).catch(e => { + throw new FatalError(`Problem getting latest version of package: ${e}`); + }); + } + + public async findActiveVersionById(nodeId: string): Promise { + return this.httpClient.get(`/package-manager/api/packages/${nodeId}/active`).catch(e => { + throw new FatalError(`Problem getting latest version of package: ${e}`); + }); + } + + public async findActiveVersionByIds(nodeIds: string[]): Promise { + return this.httpClient.post("/package-manager/api/packages/active/by-ids", nodeIds).catch(e => { + throw new FatalError(`Problem getting latest version of packages: ${e}`); + }); + } + + public async findNextVersion(nodeId: string): Promise { + return this.httpClient.get(`/package-manager/api/packages/${nodeId}/next-version`).catch(e => { + throw new FatalError(`Problem getting latest version of package: ${e}`); + }); + } + + public async importPackage(nodeContent: any, spaceId: string, overwrite: boolean, excludeActionFlows?: boolean): Promise { + await this.httpClient.postFile("/package-manager/api/packages/import", nodeContent, { + spaceId: spaceId, + overwrite: overwrite, + excludeActionFlows: excludeActionFlows + }).catch(e => { + throw new FatalError(`Problem importing package: ${e}`); + }); + } + + public async movePackageToSpace(nodeId: string, spaceId: string): Promise { + await this.httpClient.put(`/package-manager/api/packages/${nodeId}/move/${spaceId}`, {}).catch(e => { + throw new FatalError(`Problem moving package: ${e}`); + }); + } + + public async publishPackage(activatePackage: ActivatePackageTransport): Promise { + await this.httpClient.post(`/package-manager/api/packages/${activatePackage.packageKey}/activate`, activatePackage).catch(e => { + throw new FatalError(`Problem activating package with key ${activatePackage.packageKey}: ${e}`); + }); + } +} + diff --git a/src/modules/list/list-commands.ts b/src/modules/list/list-commands.ts new file mode 100644 index 00000000..dfaa4021 --- /dev/null +++ b/src/modules/list/list-commands.ts @@ -0,0 +1,17 @@ +import { Context } from "../../core/cli-context"; +import { ListService } from "./list-service"; + +export class ListCommand { + + constructor(private context : Context) {} + + public async listPackages(jsonResponse: boolean, includeDependencies: boolean, packageKeys: string[]): Promise { + const listService = new ListService(this.context); + if (jsonResponse) { + await listService.findAndExportListOfAllPackages(includeDependencies, packageKeys ?? []); + } else { + await listService.listPackages(); + } + } + +} diff --git a/src/modules/list/list-service.ts b/src/modules/list/list-service.ts new file mode 100644 index 00000000..94ff1564 --- /dev/null +++ b/src/modules/list/list-service.ts @@ -0,0 +1,85 @@ +import { logger } from "../../util/logger"; +import { v4 as uuidv4 } from "uuid"; +import { FileService, fileService } from "../../services/file-service"; +import { BatchExportNodeTransport } from "../../interfaces/batch-export-node-transport"; +import { dataModelService } from "./../../services/package-manager/datamodel-service"; +import { PackageDependencyTransport, PackageManagerVariableType } from "../../interfaces/package-manager.interfaces"; +import { variableService } from "./../../services/package-manager/variable-service"; +import AdmZip = require("adm-zip"); +import {packageDependenciesApi} from "../../api/package-dependencies-api"; +import { Context } from "../../core/cli-context"; +import { ListApi } from "./list-api"; + +export class ListService { + protected readonly fileDownloadedMessage = "File downloaded successfully. New filename: "; + + listApi : ListApi; + constructor(private context: Context) { + this.listApi = new ListApi(context); + } + + public async listPackages(): Promise { + const nodes = await this.listApi.findAllPackages(); + nodes.forEach(node => { + logger.info(`${node.name} - Key: "${node.key}"`); + }); + logger.info(`Found ${nodes.length} package(s)`); + } + + public async findAndExportListOfAllPackages(includeDependencies: boolean, packageKeys: string[]): Promise { + const fieldsToInclude = ["key", "name", "changeDate", "activatedDraftId", "workingDraftId", "spaceId"]; + + let nodesListToExport: BatchExportNodeTransport[] = await this.listApi.findAllPackages(); + if (packageKeys.length > 0) { + nodesListToExport = nodesListToExport.filter(node => packageKeys.includes(node.rootNodeKey)); + } + + if (includeDependencies) { + fieldsToInclude.push("type", "value", "dependencies", "id", "updateAvailable", "version", "poolId", "node", "dataModelId", "dataPool", "datamodels"); + const unPublishedNodes = nodesListToExport.filter(node => !node.activatedDraftId); + let publishedNodes = nodesListToExport.filter(node => node.activatedDraftId); + publishedNodes = await this.getNodesWithActiveVersion(publishedNodes); + nodesListToExport = [...publishedNodes, ...unPublishedNodes]; + + const packageWithDataModelVariableAssignments = await variableService.getVariableAssignmentsForNodes(PackageManagerVariableType.DATA_MODEL); + const dataModelDetailsByNode = await dataModelService.getDataModelDetailsForPackages(packageWithDataModelVariableAssignments); + nodesListToExport.forEach(node => { + node.datamodels = dataModelDetailsByNode.get(node.key); + }); + + const draftIdByNodeId = new Map(); + nodesListToExport.forEach(node => draftIdByNodeId.set(node.workingDraftId, node.id)); + + const dependenciesByPackageIds = await this.getPackagesWithDependencies(draftIdByNodeId); + + nodesListToExport = nodesListToExport.map(nodeToExport => { + nodeToExport.dependencies = dependenciesByPackageIds[nodeToExport.workingDraftId] ?? []; + return nodeToExport; + }) + } + this.exportListOfPackages(nodesListToExport, fieldsToInclude); + } + + public async getPackagesWithDependencies(draftIdByNodeId: Map): Promise> { + const allPackageDependencies: Map = await packageDependenciesApi.findPackageDependenciesByIds(draftIdByNodeId); + return allPackageDependencies; + } + + private exportListOfPackages(nodes: BatchExportNodeTransport[], fieldsToInclude: string[]): void { + const filename = uuidv4() + ".json"; + fileService.writeToFileWithGivenName(JSON.stringify(nodes, fieldsToInclude), filename); + logger.info(FileService.fileDownloadedMessage + filename); + } + + public async getNodesWithActiveVersion(nodes: BatchExportNodeTransport[]): Promise { + const activeVersionsOfPackage = await this.listApi.findActiveVersionByIds(nodes.map(node => node.id)); + + nodes.forEach(node => { + node.version = activeVersionsOfPackage.find(packageVersion => packageVersion.id === node.id); + }) + + return nodes; + } + +} + diff --git a/src/modules/list/module.ts b/src/modules/list/module.ts index ee74384f..09b58651 100644 --- a/src/modules/list/module.ts +++ b/src/modules/list/module.ts @@ -7,7 +7,7 @@ import { CommandConfig, IModule } from "../../core/module-handler"; import { logger } from "../../util/logger"; import { Context } from "../../core/cli-context"; import { SpaceCommand } from "../../commands/space.command"; -import { PackageCommand } from "../../commands/package.command"; +import { ListCommand } from "./list-commands"; class ListModule implements IModule { @@ -33,7 +33,7 @@ class ListModule implements IModule { async listPackages(context: Context, command: Command) { let options = command.opts(); - await new PackageCommand().listPackages(options.json, options.includeDependencies, options.packageKeys); + await new ListCommand(context).listPackages(options.json, options.includeDependencies, options.packageKeys); } async listSpaces(context: Context, command: Command) { diff --git a/src/modules/test/module.ts b/src/modules/test/module.ts index da96fd15..48fba5a3 100644 --- a/src/modules/test/module.ts +++ b/src/modules/test/module.ts @@ -27,6 +27,9 @@ class TestModule implements IModule { invoke(context: Context, command: Command) { let options = command.opts(); logger.info(`Test invocation, key is ${options.key}. Profile is ${context.profileName}`); + if (context.httpClient) { + logger.info(`HttpClient is present`); + } } } diff --git a/src/services/package-manager/package-service.ts b/src/services/package-manager/package-service.ts index bba49197..675186c9 100644 --- a/src/services/package-manager/package-service.ts +++ b/src/services/package-manager/package-service.ts @@ -32,6 +32,7 @@ class PackageService { nodes.forEach(node => { logger.info(`${node.name} - Key: "${node.key}"`); }); + logger.info(`Found ${nodes.length} packages`); } public async findAndExportListOfAllPackages(includeDependencies: boolean, packageKeys: string[]): Promise { diff --git a/src/util/logger.ts b/src/util/logger.ts index 72909e0b..cddd6464 100644 --- a/src/util/logger.ts +++ b/src/util/logger.ts @@ -88,7 +88,7 @@ export const logger: Logger = winston.createLogger({ }), new CustomTransport({}) ], - exitOnError: true, + exitOnError: false, }); // tslint:disable-next-line: max-classes-per-file From 0ab31e060e87d2d0c561660208e26d02a8d4181e Mon Sep 17 00:00:00 2001 From: Florian Lippisch Date: Wed, 23 Apr 2025 17:03:14 +0200 Subject: [PATCH 06/45] modules can extend any root level command --- src/celonis-cli.ts | 2 +- src/core/module-handler.ts | 39 ++++++++++++++++++++++++++++++----- src/modules/list/module.ts | 5 +++-- src/modules/profile/module.ts | 6 ++++-- src/modules/test/module.ts | 20 ++++++++++++++++-- 5 files changed, 60 insertions(+), 12 deletions(-) diff --git a/src/celonis-cli.ts b/src/celonis-cli.ts index 4b06cd14..9010b240 100644 --- a/src/celonis-cli.ts +++ b/src/celonis-cli.ts @@ -1,7 +1,7 @@ //const logProc = require('why-is-node-running') import semverSatisfies = require("semver/functions/satisfies"); -import {VersionUtils} from "./util/version"; +import { VersionUtils } from "./util/version"; import { logger } from "./util/logger"; import { ModuleHandler } from "./core/module-handler"; import { Command } from "commander"; diff --git a/src/core/module-handler.ts b/src/core/module-handler.ts index 32589840..2afded20 100644 --- a/src/core/module-handler.ts +++ b/src/core/module-handler.ts @@ -6,7 +6,7 @@ import { Command, CommandOptions, ExecutableCommandOptions } from "commander"; import { Context } from "./cli-context"; export interface IModule { - register(context: Context, commandConfig: CommandConfig); + register(context: Context, commandConfig: Configurator); } export interface IModuleConstructor { @@ -15,8 +15,10 @@ export interface IModuleConstructor { export class ModuleHandler { - constructor(public program: Command, public context: Context) { + configurator: Configurator; + constructor(public program: Command, public context: Context) { + this.configurator = new Configurator(this.program, this.context); } // Store registered module instances if needed later @@ -65,9 +67,7 @@ export class ModuleHandler { logger.debug(`Registering module: ${moduleFolderName}`); // Call register - can still be async even if require() is sync let ctx = this.context; - let commandName = moduleFolderName; // may let the module change this - let command = this.program.command(commandName) - moduleInstance.register(ctx, new CommandConfig(command, this.context)); + moduleInstance.register(ctx, this.configurator); this.registeredModules.push(moduleInstance); } else { logger.warn(`Module ${moduleFolderName} export does not have a 'register' method.`); @@ -104,6 +104,35 @@ export class ModuleHandler { type CommandHandler = (context: Context, command: Command) => void; +/** + * Allows the creation of root level commands. + */ +export class Configurator { + + rootCommandMap = new Map(); + + constructor(private program: Command, private ctx: Context) { + + } + + /** + * Get or create a root level command. + * @param nameAndArgs + * @param opts + * @returns + */ + command(name: string) : CommandConfig { + if (this.rootCommandMap.has(name)) { + return this.rootCommandMap.get(name); + } + let cmd = this.program.command(name); + let cmdConfig = new CommandConfig(cmd, this.ctx); + this.rootCommandMap.set(name, cmdConfig); + return cmdConfig; + } + +} + /** * Delegate wrapper around the Command object, to simply change the way the program is * executed. diff --git a/src/modules/list/module.ts b/src/modules/list/module.ts index 09b58651..76d6f4d9 100644 --- a/src/modules/list/module.ts +++ b/src/modules/list/module.ts @@ -3,7 +3,7 @@ */ import { Command } from "commander"; -import { CommandConfig, IModule } from "../../core/module-handler"; +import { CommandConfig, Configurator, IModule } from "../../core/module-handler"; import { logger } from "../../util/logger"; import { Context } from "../../core/cli-context"; import { SpaceCommand } from "../../commands/space.command"; @@ -11,8 +11,9 @@ import { ListCommand } from "./list-commands"; class ListModule implements IModule { - register(context: Context, command: CommandConfig) { + register(context: Context, configurator: Configurator) { + let command = configurator.command('list'); // action if no command is provided command.action(this.showHelp); diff --git a/src/modules/profile/module.ts b/src/modules/profile/module.ts index 3aab4461..64cfb2c2 100644 --- a/src/modules/profile/module.ts +++ b/src/modules/profile/module.ts @@ -3,15 +3,17 @@ */ import { Command } from "commander"; -import { CommandConfig, IModule } from "../../core/module-handler"; +import { CommandConfig, Configurator, IModule } from "../../core/module-handler"; import { logger } from "../../util/logger"; import { Context } from "../../core/cli-context"; import { ProfileCommand } from "./profile.command"; class ProfileModule implements IModule { - register(context: Context, command: CommandConfig) { + register(context: Context, configurator: Configurator) { + let command = configurator.command('profile'); + // action if no command is provided command.action(this.showHelp); diff --git a/src/modules/test/module.ts b/src/modules/test/module.ts index 48fba5a3..f792e6fc 100644 --- a/src/modules/test/module.ts +++ b/src/modules/test/module.ts @@ -3,13 +3,14 @@ */ import { Command } from "commander"; -import { CommandConfig, IModule } from "../../core/module-handler"; +import { CommandConfig, Configurator, IModule } from "../../core/module-handler"; import { logger } from "../../util/logger"; import { Context } from "../../core/cli-context"; class TestModule implements IModule { - register(context: Context, command: CommandConfig) { + register(context: Context, configurator: Configurator) { + let command = configurator.command('test'); command.option('-k, --key [string]', 'The key'); command.action(this.invoke); @@ -18,6 +19,21 @@ class TestModule implements IModule { .description('Blink a few times') .option('-c, --count [number]') .action(this.blink); + + + // lets add 'test' sub-command under the list command + let listCommand = configurator.command('list'); + listCommand.command('test') + .description('Test List') + .option('-c, --count [number]') + .action(this.testList); + + + } + + testList(context: Context, command: Command) { + + logger.info(`Test List`); } blink(context: Context, command: Command) { From 7c23b9767b7f98757236d79e563b34be162ce254 Mon Sep 17 00:00:00 2001 From: Florian Lippisch Date: Thu, 24 Apr 2025 15:52:24 +0200 Subject: [PATCH 07/45] entry point file can be of 3 variants now, supporting foldername as prefix. --- src/core/module-handler.ts | 108 +++++++++++------- .../profile/{module.ts => profile-module.ts} | 0 .../test/{module.ts => test-module.ts} | 0 3 files changed, 64 insertions(+), 44 deletions(-) rename src/modules/profile/{module.ts => profile-module.ts} (100%) rename src/modules/test/{module.ts => test-module.ts} (100%) diff --git a/src/core/module-handler.ts b/src/core/module-handler.ts index 2afded20..01f0eb34 100644 --- a/src/core/module-handler.ts +++ b/src/core/module-handler.ts @@ -1,8 +1,7 @@ import { logger } from "../util/logger"; -import { fileURLToPath } from 'url'; // Needed for ES Modules __dirname equivalent import path = require("path"); import * as fs from "fs"; -import { Command, CommandOptions, ExecutableCommandOptions } from "commander"; +import { Command, CommandOptions } from "commander"; import { Context } from "./cli-context"; export interface IModule { @@ -24,7 +23,7 @@ export class ModuleHandler { // Store registered module instances if needed later registeredModules: IModule[] = []; - /** + /** * Discovers modules in the specified directory, imports them, * instantiates the default exported class, and calls its register method. * @@ -41,51 +40,72 @@ export class ModuleHandler { if (dirent.isDirectory()) { const moduleFolderName = dirent.name; + // the specification allows different variants, so we test for each. + const fileVariants = [ + `${moduleFolderName}-module.js`, + 'module.js', + 'index.js' + ]; + // Calculate path relative to *this file's location in dist* - const potentialModuleJsPath = path.resolve( - rootPath, 'modules', moduleFolderName, - 'module.js' // Look for the compiled JS file - ); - - // Check if the compiled JS file exists - try { - fs.accessSync(potentialModuleJsPath); // Check existence - logger.debug(`Found potential module definition: ${potentialModuleJsPath}`); - - // Dynamically require the module - const requiredModule = require(potentialModuleJsPath); - - // With 'export =' or 'module.exports =', the required value *is* the class - const ModuleClass = requiredModule as IModuleConstructor; // Cast for TS check - - // Basic check: Is it a class (function)? - if (typeof ModuleClass === 'function' && ModuleClass.prototype) { - const moduleInstance: IModule = new ModuleClass(); // Instantiate - - // Check if the instance has the register method - if (typeof moduleInstance.register === 'function') { - logger.debug(`Registering module: ${moduleFolderName}`); - // Call register - can still be async even if require() is sync - let ctx = this.context; - moduleInstance.register(ctx, this.configurator); - this.registeredModules.push(moduleInstance); + let potentialModuleJsPath; + for (let name of fileVariants) { + potentialModuleJsPath = path.resolve( + rootPath, 'modules', moduleFolderName, + name // Look for the compiled JS file + ); + try { + fs.accessSync(potentialModuleJsPath); + break; // use this variant + } catch (err) { + // apparently the file does not exist of is not accessible + potentialModuleJsPath = null; + } + } + + if (!potentialModuleJsPath) { + logger.debug(`Module folder ${moduleFolderName} does not contain a valid entry point and is skipped.`); + } else { + + // Check if the compiled JS file exists + try { + logger.debug(`Found potential module definition: ${potentialModuleJsPath}`); + + // Dynamically require the module + const requiredModule = require(potentialModuleJsPath); + + // With 'export =' or 'module.exports =', the required value *is* the class + const ModuleClass = requiredModule as IModuleConstructor; // Cast for TS check + + // Basic check: Is it a class (function)? + if (typeof ModuleClass === 'function' && ModuleClass.prototype) { + const moduleInstance: IModule = new ModuleClass(); // Instantiate + + // Check if the instance has the register method + if (typeof moduleInstance.register === 'function') { + logger.debug(`Registering module: ${moduleFolderName}`); + // Call register - can still be async even if require() is sync + let ctx = this.context; + moduleInstance.register(ctx, this.configurator); + this.registeredModules.push(moduleInstance); + } else { + logger.warn(`Module ${moduleFolderName} export does not have a 'register' method.`); + } } else { - logger.warn(`Module ${moduleFolderName} export does not have a 'register' method.`); + logger.warn(`Module ${moduleFolderName} export is not a class/constructor function.`); } - } else { - logger.warn(`Module ${moduleFolderName} export is not a class/constructor function.`); - } - } catch (error: any) { - if (error.code === 'ENOENT') { - // Compiled module.js not found, maybe folder doesn't contain a valid module - logger.warn(`Directory ${moduleFolderName} does not contain a compiled module.js file.`); - } else if (error.code === 'MODULE_NOT_FOUND') { - logger.debug(`Error details`, error); - logger.warn(`Could not require module ${moduleFolderName}. Check dependencies or compilation. Path: ${potentialModuleJsPath}`); - } - else { - logger.error(`Error processing module in ${moduleFolderName}:`, error); + } catch (error: any) { + if (error.code === 'ENOENT') { + // Compiled module.js not found, maybe folder doesn't contain a valid module + logger.warn(`Directory ${moduleFolderName} does not contain a compiled module.js file.`); + } else if (error.code === 'MODULE_NOT_FOUND') { + logger.debug(`Error details`, error); + logger.warn(`Could not require module ${moduleFolderName}. Check dependencies or compilation. Path: ${potentialModuleJsPath}`); + } + else { + logger.error(`Error processing module in ${moduleFolderName}:`, error); + } } } } diff --git a/src/modules/profile/module.ts b/src/modules/profile/profile-module.ts similarity index 100% rename from src/modules/profile/module.ts rename to src/modules/profile/profile-module.ts diff --git a/src/modules/test/module.ts b/src/modules/test/test-module.ts similarity index 100% rename from src/modules/test/module.ts rename to src/modules/test/test-module.ts From 5ab1e348a46816c2b1c380ae895420df472029a1 Mon Sep 17 00:00:00 2001 From: Florian Lippisch Date: Tue, 29 Apr 2025 10:35:46 +0200 Subject: [PATCH 08/45] Modules are now structured by owning team, not command structure --- src/celonis-cli.ts | 29 +++++-- src/core/module-handler.ts | 15 +++- src/modules/pacman/pacman-module.ts | 82 +++++++++++++++++++ src/modules/profile/profile-module.ts | 3 +- src/modules/{list => studio}/list-api.ts | 0 src/modules/{list => studio}/list-commands.ts | 0 src/modules/{list => studio}/list-service.ts | 4 +- .../module.ts => studio/studio-module.ts} | 17 ++-- src/modules/test/test-module.ts | 5 +- 9 files changed, 129 insertions(+), 26 deletions(-) create mode 100644 src/modules/pacman/pacman-module.ts rename src/modules/{list => studio}/list-api.ts (100%) rename src/modules/{list => studio}/list-commands.ts (100%) rename src/modules/{list => studio}/list-service.ts (96%) rename src/modules/{list/module.ts => studio/studio-module.ts} (75%) diff --git a/src/celonis-cli.ts b/src/celonis-cli.ts index 9010b240..247c11a8 100644 --- a/src/celonis-cli.ts +++ b/src/celonis-cli.ts @@ -1,12 +1,20 @@ +#!/usr/bin/env node + //const logProc = require('why-is-node-running') import semverSatisfies = require("semver/functions/satisfies"); import { VersionUtils } from "./util/version"; import { logger } from "./util/logger"; -import { ModuleHandler } from "./core/module-handler"; +import { Configurator, ModuleHandler } from "./core/module-handler"; import { Command } from "commander"; import { Context } from "./core/cli-context"; +/** + * Celonis Content CLI. + * + * This is the main entry point for the CLI. + */ + // Check if the Node.js version satisfies the minimum requirements const requiredVersion = ">=10.10.0"; @@ -17,6 +25,7 @@ if (!semverSatisfies(process.version, requiredVersion)) { process.exit(1); } +// Global configuration options const program: Command = new Command(); program.version(VersionUtils.getCurrentCliVersion()); program.option("-q, --quietmode", "Reduce output to a minimum", false); @@ -24,11 +33,7 @@ program.option("-p, --profile [profile]"); program.option("--debug", "Print debug messages", false); program.parseOptions(process.argv); -/** - * Celonis Content CLI. - * - * This is the main entry point for the CLI. - */ +// go for it... if (!program.opts().quietmode) { console.log(`Celonis CLI - (C) Copyright 2025 - Celonis SE - Version ${VersionUtils.getCurrentCliVersion()}`); console.log(); @@ -40,6 +45,16 @@ if (program.opts().debug) { }); } +/** + * To support the legacy command structure, we have to configure some root commands + * that the individual modules will extend. + */ +function configureRootCommands(configurator: Configurator) { + configurator.command("list") + .description("Commands to list content.") + .alias("ls"); +} + async function run() { let context = new Context(program.opts()); @@ -47,6 +62,8 @@ async function run() { let moduleHandler = new ModuleHandler(program, context); + configureRootCommands(moduleHandler.configurator); + moduleHandler.discoverAndRegisterModules(__dirname); if (!process.argv.slice(2).length) { diff --git a/src/core/module-handler.ts b/src/core/module-handler.ts index 01f0eb34..26770769 100644 --- a/src/core/module-handler.ts +++ b/src/core/module-handler.ts @@ -1,7 +1,7 @@ import { logger } from "../util/logger"; import path = require("path"); import * as fs from "fs"; -import { Command, CommandOptions } from "commander"; +import { Command, CommandOptions, OptionValues } from "commander"; import { Context } from "./cli-context"; export interface IModule { @@ -14,7 +14,7 @@ export interface IModuleConstructor { export class ModuleHandler { - configurator: Configurator; + public configurator: Configurator; constructor(public program: Command, public context: Context) { this.configurator = new Configurator(this.program, this.context); @@ -122,7 +122,7 @@ export class ModuleHandler { } -type CommandHandler = (context: Context, command: Command) => void; +type CommandHandler = (context: Context, command: Command, options: OptionValues) => void; /** * Allows the creation of root level commands. @@ -166,6 +166,11 @@ export class CommandConfig { return new CommandConfig(this.cmd.command(nameAndArgs, opts), this.ctx); } + alias(alias: string) { + this.cmd.alias(alias); + return this; + } + description(description: string) { this.cmd.description(description); return this; @@ -190,7 +195,9 @@ export class CommandConfig { let ctx = this.ctx; this.cmd.action(async function () { try { - await handler(ctx, this); + let cmd = this; // in the context of the execution, this refers to the Command object + let cmdOptions = cmd.opts(); + await handler(ctx, this, cmdOptions); } catch (error) { logger.error(`An unexpected error occured executing a command: ${error}`); } diff --git a/src/modules/pacman/pacman-module.ts b/src/modules/pacman/pacman-module.ts new file mode 100644 index 00000000..098cfcf0 --- /dev/null +++ b/src/modules/pacman/pacman-module.ts @@ -0,0 +1,82 @@ +/** + * This module provides all 'PacMan' related commands. + */ + +import { Command, OptionValues } from "commander"; +import { CommandConfig, Configurator, IModule } from "../../core/module-handler"; +import { logger } from "../../util/logger"; +import { Context } from "../../core/cli-context"; + +class PacManModule implements IModule { + register(context: Context, configurator: Configurator) { + + let configCmd = configurator.command('config'); + configCmd.description('Configuration management functions, such as export, import, etc.'); + configCmd.action(this.showHelp); + + + configCmd.command("list") + .description("Command to list active packages that can be exported") + .option("--json", "Return response as json type", "") + .option("--flavors ", "Lists only active packages of the given flavors") + .option("--withDependencies", "Include dependencies", "") + .option("--packageKeys ", "Lists only given package keys") + .action(this.listCommand); + + + configCmd.command("variables") + .description("Commands related to variable configs") + .command("list") + .description("Command to list versioned variables of packages") + .option("--json", "Return response as json type", "") + .option("--keysByVersion ", "Mapping of package keys and versions", "") + .option("--keysByVersionFile ", "Package keys by version mappings file path.", "") + .action(this.variablesCommand); + + configCmd.command("export") + .description("Command to export package configs") + .requiredOption("--packageKeys ", "Keys of packages to export") + .option("--withDependencies", "Include variables and dependencies", "") + .action(this.configCommmand); + + configCmd.command("import") + .description("Command to import package configs") + .option("--overwrite", "Flag to allow overwriting of packages") + .requiredOption("-f, --file ", "Exported packages file (relative path)") + .action(this.importCommand); + + configCmd.command("diff") + .description("Command to diff configs of packages") + .option("--hasChanges", "Flag to return only the information if the package has changes without the actual changes") + .option("--json", "Return the response as a JSON file") + .requiredOption("-f, --file ", "Exported packages file (relative or absolute path)") + .action(this.diffCommand); + + } + + diffCommand(context: Context, command: Command, options: OptionValues) { + //new ConfigCommand().diffPackages(cmd.file, cmd.hasChanges, cmd.json); + } + + importCommand(context: Context, command: Command, options: OptionValues) { + //new ConfigCommand().batchImportPackages(cmd.file, cmd.overwrite); + } + + configCommmand(context: Context, command: Command, options: OptionValues) { + //new ConfigCommand().batchExportPackages(cmd.packageKeys, cmd.withDependencies); + } + + variablesCommand(context: Context, command: Command, options: OptionValues) { + //new ConfigCommand().listVariables(cmd.json, cmd.keysByVersion, cmd.keysByVersionFile); + } + + listCommand(context: Context, command: Command, options: OptionValues) { + //new ConfigCommand().listActivePackages(cmd.json, cmd.flavors, cmd.withDependencies, cmd.packageKeys); + } + + showHelp(context: Context, command: Command) { + command.outputHelp(); + } +} + +export = PacManModule; \ No newline at end of file diff --git a/src/modules/profile/profile-module.ts b/src/modules/profile/profile-module.ts index 64cfb2c2..8c655e0c 100644 --- a/src/modules/profile/profile-module.ts +++ b/src/modules/profile/profile-module.ts @@ -13,7 +13,8 @@ class ProfileModule implements IModule { register(context: Context, configurator: Configurator) { let command = configurator.command('profile'); - + command.description('Manage profiles required to access a system.'); + // action if no command is provided command.action(this.showHelp); diff --git a/src/modules/list/list-api.ts b/src/modules/studio/list-api.ts similarity index 100% rename from src/modules/list/list-api.ts rename to src/modules/studio/list-api.ts diff --git a/src/modules/list/list-commands.ts b/src/modules/studio/list-commands.ts similarity index 100% rename from src/modules/list/list-commands.ts rename to src/modules/studio/list-commands.ts diff --git a/src/modules/list/list-service.ts b/src/modules/studio/list-service.ts similarity index 96% rename from src/modules/list/list-service.ts rename to src/modules/studio/list-service.ts index 94ff1564..2605f44f 100644 --- a/src/modules/list/list-service.ts +++ b/src/modules/studio/list-service.ts @@ -2,9 +2,9 @@ import { logger } from "../../util/logger"; import { v4 as uuidv4 } from "uuid"; import { FileService, fileService } from "../../services/file-service"; import { BatchExportNodeTransport } from "../../interfaces/batch-export-node-transport"; -import { dataModelService } from "./../../services/package-manager/datamodel-service"; +import { dataModelService } from "../../services/package-manager/datamodel-service"; import { PackageDependencyTransport, PackageManagerVariableType } from "../../interfaces/package-manager.interfaces"; -import { variableService } from "./../../services/package-manager/variable-service"; +import { variableService } from "../../services/package-manager/variable-service"; import AdmZip = require("adm-zip"); import {packageDependenciesApi} from "../../api/package-dependencies-api"; import { Context } from "../../core/cli-context"; diff --git a/src/modules/list/module.ts b/src/modules/studio/studio-module.ts similarity index 75% rename from src/modules/list/module.ts rename to src/modules/studio/studio-module.ts index 76d6f4d9..eb853636 100644 --- a/src/modules/list/module.ts +++ b/src/modules/studio/studio-module.ts @@ -2,7 +2,7 @@ * Commands to create and list access profiles. */ -import { Command } from "commander"; +import { Command, OptionValues } from "commander"; import { CommandConfig, Configurator, IModule } from "../../core/module-handler"; import { logger } from "../../util/logger"; import { Context } from "../../core/cli-context"; @@ -13,32 +13,29 @@ class ListModule implements IModule { register(context: Context, configurator: Configurator) { - let command = configurator.command('list'); - // action if no command is provided - command.action(this.showHelp); + // Extend the "list" command with some studio specific options + let listCmd = configurator.command('list'); // let's add a subcommand - command.command("packages") + listCmd.command("packages") .description("Command to list all packages") .option("--json", "Return response as json type", "") .option("--includeDependencies", "Include variables and dependencies", "") .option("--packageKeys ", "Lists only given package keys") .action(this.listPackages); - command.command("spaces") + listCmd.command("spaces") .description("Command to list all spaces") .option("--json", "Return response as json type", "") .action(this.listSpaces); } - async listPackages(context: Context, command: Command) { - let options = command.opts(); + async listPackages(context: Context, command: Command, options: OptionValues) { await new ListCommand(context).listPackages(options.json, options.includeDependencies, options.packageKeys); } - async listSpaces(context: Context, command: Command) { - let options = command.opts(); + async listSpaces(context: Context, command: Command, options: OptionValues) { await new SpaceCommand().listSpaces(undefined, options.json); } diff --git a/src/modules/test/test-module.ts b/src/modules/test/test-module.ts index f792e6fc..45db1f51 100644 --- a/src/modules/test/test-module.ts +++ b/src/modules/test/test-module.ts @@ -2,7 +2,7 @@ * Configures the module commands, options, etc. */ -import { Command } from "commander"; +import { Command, CommandOptions, OptionValues } from "commander"; import { CommandConfig, Configurator, IModule } from "../../core/module-handler"; import { logger } from "../../util/logger"; import { Context } from "../../core/cli-context"; @@ -40,8 +40,7 @@ class TestModule implements IModule { logger.info(`I will blink`); } - invoke(context: Context, command: Command) { - let options = command.opts(); + invoke(context: Context, command: Command, options: OptionValues) { logger.info(`Test invocation, key is ${options.key}. Profile is ${context.profileName}`); if (context.httpClient) { logger.info(`HttpClient is present`); From 5f107c12ea1f883e4105a4313cb4e2554f4ca45d Mon Sep 17 00:00:00 2001 From: Florian Lippisch Date: Fri, 2 May 2025 07:34:16 +0200 Subject: [PATCH 09/45] Base API with some refactored error handling --- src/core/base-api.ts | 114 ++++++++++++++++++ src/core/http-client.ts | 8 +- src/modules/pacman/api/config-service-api.ts | 64 ++++++++++ .../pacman/commands/config-list-command.ts | 30 +++++ src/modules/pacman/pacman-module.ts | 14 ++- 5 files changed, 226 insertions(+), 4 deletions(-) create mode 100644 src/core/base-api.ts create mode 100644 src/modules/pacman/api/config-service-api.ts create mode 100644 src/modules/pacman/commands/config-list-command.ts diff --git a/src/core/base-api.ts b/src/core/base-api.ts new file mode 100644 index 00000000..0851e361 --- /dev/null +++ b/src/core/base-api.ts @@ -0,0 +1,114 @@ +/** + * Base API implementation, which provides basic error handling for common problems. + */ + +import { logger } from "../util/logger"; +import { HttpClient } from "./http-client"; + + +export class ForbiddenError extends Error { + constructor(message = 'Access Forbidden') { + super(message); + this.name = 'ForbiddenError'; + // Maintains proper stack trace in V8 environments (Node, Chrome) + if (Error.captureStackTrace) { + Error.captureStackTrace(this, ForbiddenError); + } + } +} + +// Custom error for Server (5xx) responses. +export class ServerError extends Error { + constructor(message = 'Internal Server Error') { + super(message); + this.name = 'ServerError'; + if (Error.captureStackTrace) { + Error.captureStackTrace(this, ServerError); + } + } +} + +// Custom error for Not Found (404) responses. +export class NotFoundError extends Error { + constructor(message = 'Resource Not Found') { + super(message); + this.name = 'NotFoundError'; + if (Error.captureStackTrace) { + Error.captureStackTrace(this, NotFoundError); + } + } +} + +// Custom error for general network or unexpected issues. +export class NetworkError extends Error { + constructor(message = 'Network or unexpected error occurred') { + super(message); + this.name = 'NetworkError'; + if (Error.captureStackTrace) { + Error.captureStackTrace(this, NetworkError); + } + } +} + +interface HttpError extends Error { + status?: number; // HTTP status code (e.g., 404, 500) + response?: any; // Optional: Include the full response body/details +} + + +export abstract class BaseApi { + + protected readonly httpClient: HttpClient; + + /** + * Constructor for BaseApiService. + * @param httpClient - An instance of HttpClient to be used for requests. + */ + constructor(httpClient: HttpClient) { + this.httpClient = httpClient; + } + + /** + * Handles errors from HttpClient requests. + * Logs the error and throws specific custom errors based on HTTP status codes. + * Subclasses can override this for more specific error handling. + * @param error - The error object caught from the HttpClient promise. + * @returns Never, as this method always throws an error. + */ + protected handleError(error: any): never { + logger.debug('API Error:', error); // Basic logging + + // Check if it looks like an HTTP error with a status code + if (error && typeof error.status === 'number') { + const httpError = error as HttpError; + switch (httpError.status) { + case 401: + // Handle Unauthorized (e.g., redirect to login) + // Depending on the app, might not throw, but trigger auth flow + // Example: throw new AuthenticationError('Authentication required'); + throw new Error(`Unauthorized (401): ${httpError.message || 'Authentication required'}`); // Placeholder + case 403: + throw new ForbiddenError(`Forbidden (403): ${httpError.message || 'Access denied'}`); + case 404: + throw new NotFoundError(`Not Found (404): ${httpError.message || 'Resource not found'}`); + case 500: + case 501: + case 502: + case 503: + case 504: + throw new ServerError(`Server Error (${httpError.status}): ${httpError.message || 'Server issue'}`); + default: + // Handle other HTTP errors (e.g., 400 Bad Request) + throw new Error(`HTTP Error (${httpError.status}): ${httpError.message || 'An HTTP error occurred'}`); + } + } else if (error instanceof Error) { + // Handle non-HTTP errors (e.g., network issues, client-side errors) + console.debug('Non-HTTP Error:', error.message); + throw new NetworkError(`Network or client-side error: ${error.message}`); + } else { + // Handle cases where the caught object is not an Error instance + console.debug('Unknown error structure:', error); + throw new Error('An unknown error occurred'); + } + } +} \ No newline at end of file diff --git a/src/core/http-client.ts b/src/core/http-client.ts index 94eb8484..b0252ad2 100644 --- a/src/core/http-client.ts +++ b/src/core/http-client.ts @@ -27,12 +27,12 @@ export class HttpClient { logger.debug(`Response ${response.status}`); this.handleResponse(response, resolve, reject); }).catch(err => { - logger.error(err); + logger.debug(`HTTP GET resulted in error`, err); this.handleError(err, resolve, reject); }) }).catch(e => { logger.error(e); - throw new FatalError(e); + throw e; }) } @@ -179,11 +179,15 @@ export class HttpClient { } private handleError(err: any, resolve, reject): void { + reject(err); + /* + console.log(err); if (err.response) { this.handleResponse(err.response, resolve, reject); } else { reject(err.message); } + */ } private checkBadRequest(statusCode: number): boolean { diff --git a/src/modules/pacman/api/config-service-api.ts b/src/modules/pacman/api/config-service-api.ts new file mode 100644 index 00000000..9d491549 --- /dev/null +++ b/src/modules/pacman/api/config-service-api.ts @@ -0,0 +1,64 @@ +import { BaseApi } from "../../../core/base-api"; +import { Context } from "../../../core/cli-context"; +import { HttpClient } from "../../../core/http-client"; +import { + PackageExportTransport, + PackageKeyAndVersionPair, PostPackageImportData, + VariableManifestTransport +} from "../../../interfaces/package-export-transport"; +import {FatalError} from "../../../util/logger"; +import * as FormData from "form-data"; + +export class ConfigServiceApi extends BaseApi { + + constructor(private context : Context) { + super(context.httpClient); + } + + public findAllActivePackages(flavors: string[], withDependencies: boolean = false): Promise { + const queryParams = new URLSearchParams(); + + queryParams.set("withDependencies", withDependencies.toString()); + flavors.forEach(flavor => queryParams.append("flavors", flavor)) + + return this.httpClient.get(`/package-manager/api/core/packages/export/list?${queryParams.toString()}`) + .catch(e => { + return this.handleError(e); + }); + } + + public findActivePackagesByKeys(packageKeys: string[], withDependencies: boolean = false): Promise { + const queryParams = new URLSearchParams(); + + packageKeys.forEach(key => queryParams.append("packageKeys", key)) + queryParams.set("withDependencies", withDependencies.toString()); + + return this.httpClient.get(`/package-manager/api/core/packages/export/list-by-keys?${queryParams.toString()}`).catch(e => { + throw new FatalError(`Problem getting active packages by keys: ${e}`); + }); + } + + public exportPackages(packageKeys: string[], withDependencies: boolean = false): Promise { + const queryParams = new URLSearchParams(); + packageKeys.forEach(packageKey => queryParams.append("packageKeys", packageKey)); + queryParams.set("withDependencies", withDependencies.toString()); + + return this.httpClient.getFile(`/package-manager/api/core/packages/export/batch?${queryParams.toString()}`).catch(e => { + throw new FatalError(`Problem exporting packages: ${e}`); + }); + } + + public importPackages(data: FormData, overwrite: boolean): Promise { + return this.httpClient.postFile( + "/package-manager/api/core/packages/import/batch", + data, + {overwrite} + ); + } + + public findVariablesWithValuesByPackageKeysAndVersion(packagesByKeyAndVersion: PackageKeyAndVersionPair[]): Promise { + return this.httpClient.post("/package-manager/api/core/packages/export/batch/variables-with-assignments", packagesByKeyAndVersion).catch(e => { + throw new FatalError(`Problem exporting package variables: ${e}`); + }) + } +} \ No newline at end of file diff --git a/src/modules/pacman/commands/config-list-command.ts b/src/modules/pacman/commands/config-list-command.ts new file mode 100644 index 00000000..e17e4307 --- /dev/null +++ b/src/modules/pacman/commands/config-list-command.ts @@ -0,0 +1,30 @@ +import { Context } from "../../../core/cli-context"; +import { logger } from "../../../util/logger"; +import { ConfigServiceApi } from "../api/config-service-api"; + + +export class ConfigListCommand { + + api: ConfigServiceApi; + + constructor(private context: Context) { + this.api = new ConfigServiceApi(context); + } + + async execute(jsonResponse: boolean, flavors: string[], withDependencies: boolean, packageKeys:string[]): Promise { + + flavors = flavors ?? []; + + if (jsonResponse) { + //await batchImportExportService.findAndExportListOfActivePackages(flavors ?? [], packageKeys ?? [], withDependencies) + } else { + logger.info(`List of active packages:`); + const activePackages = await this.api.findAllActivePackages(flavors); + activePackages.forEach(pkg => { + logger.info(`${pkg.name} - Key: "${pkg.key}"`) + }); + logger.info(`${activePackages.length} package(s) found.`); + } + } + +} \ No newline at end of file diff --git a/src/modules/pacman/pacman-module.ts b/src/modules/pacman/pacman-module.ts index 098cfcf0..921f61f9 100644 --- a/src/modules/pacman/pacman-module.ts +++ b/src/modules/pacman/pacman-module.ts @@ -6,6 +6,8 @@ import { Command, OptionValues } from "commander"; import { CommandConfig, Configurator, IModule } from "../../core/module-handler"; import { logger } from "../../util/logger"; import { Context } from "../../core/cli-context"; +import { ConfigListCommand } from "./commands/config-list-command"; +import { ForbiddenError } from "../../core/base-api"; class PacManModule implements IModule { register(context: Context, configurator: Configurator) { @@ -70,8 +72,16 @@ class PacManModule implements IModule { //new ConfigCommand().listVariables(cmd.json, cmd.keysByVersion, cmd.keysByVersionFile); } - listCommand(context: Context, command: Command, options: OptionValues) { - //new ConfigCommand().listActivePackages(cmd.json, cmd.flavors, cmd.withDependencies, cmd.packageKeys); + async listCommand(context: Context, command: Command, options: OptionValues) { + try { + await new ConfigListCommand(context).execute(options.json, options.flavors, options.withDependencies, options.packageKeys); + } catch (error) { + if (error instanceof ForbiddenError) { + logger.error(`You do not have the rights to perform this operation. Notice that you need a personal API key for 'config' operations.`); + } else { + throw error; + } + } } showHelp(context: Context, command: Command) { From 601623dbfceba421fa74b04729015ebb2babf543 Mon Sep 17 00:00:00 2001 From: Florian Lippisch Date: Fri, 2 May 2025 13:45:09 +0200 Subject: [PATCH 10/45] minor tweaks to align with documentation --- .../pacman/commands/config-list-command.ts | 30 +++++++++++++------ src/modules/pacman/pacman-module.ts | 12 ++------ src/modules/profile/profile-module.ts | 11 +++---- 3 files changed, 27 insertions(+), 26 deletions(-) diff --git a/src/modules/pacman/commands/config-list-command.ts b/src/modules/pacman/commands/config-list-command.ts index e17e4307..1b53c110 100644 --- a/src/modules/pacman/commands/config-list-command.ts +++ b/src/modules/pacman/commands/config-list-command.ts @@ -1,3 +1,4 @@ +import { ForbiddenError } from "../../../core/base-api"; import { Context } from "../../../core/cli-context"; import { logger } from "../../../util/logger"; import { ConfigServiceApi } from "../api/config-service-api"; @@ -15,15 +16,26 @@ export class ConfigListCommand { flavors = flavors ?? []; - if (jsonResponse) { - //await batchImportExportService.findAndExportListOfActivePackages(flavors ?? [], packageKeys ?? [], withDependencies) - } else { - logger.info(`List of active packages:`); - const activePackages = await this.api.findAllActivePackages(flavors); - activePackages.forEach(pkg => { - logger.info(`${pkg.name} - Key: "${pkg.key}"`) - }); - logger.info(`${activePackages.length} package(s) found.`); + try { + + if (jsonResponse) { + //await batchImportExportService.findAndExportListOfActivePackages(flavors ?? [], packageKeys ?? [], withDependencies) + } else { + logger.info(`List of active packages:`); + const activePackages = await this.api.findAllActivePackages(flavors); + activePackages.forEach(pkg => { + logger.info(`${pkg.name} - Key: "${pkg.key}"`) + }); + logger.info(`${activePackages.length} package(s) found.`); + } + + } catch (error) { + // handle the error in a nice way.... + if (error instanceof ForbiddenError) { + logger.error(`You do not have the rights to perform this operation. Notice that you need a personal API key for 'config' operations.`); + } else { + throw error; + } } } diff --git a/src/modules/pacman/pacman-module.ts b/src/modules/pacman/pacman-module.ts index 921f61f9..e6dbb5bb 100644 --- a/src/modules/pacman/pacman-module.ts +++ b/src/modules/pacman/pacman-module.ts @@ -72,16 +72,8 @@ class PacManModule implements IModule { //new ConfigCommand().listVariables(cmd.json, cmd.keysByVersion, cmd.keysByVersionFile); } - async listCommand(context: Context, command: Command, options: OptionValues) { - try { - await new ConfigListCommand(context).execute(options.json, options.flavors, options.withDependencies, options.packageKeys); - } catch (error) { - if (error instanceof ForbiddenError) { - logger.error(`You do not have the rights to perform this operation. Notice that you need a personal API key for 'config' operations.`); - } else { - throw error; - } - } + listCommand(context: Context, command: Command, options: OptionValues) { + new ConfigListCommand(context).execute(options.json, options.flavors, options.withDependencies, options.packageKeys); } showHelp(context: Context, command: Command) { diff --git a/src/modules/profile/profile-module.ts b/src/modules/profile/profile-module.ts index 8c655e0c..9c6c53ca 100644 --- a/src/modules/profile/profile-module.ts +++ b/src/modules/profile/profile-module.ts @@ -2,7 +2,7 @@ * Commands to create and list access profiles. */ -import { Command } from "commander"; +import { Command, OptionValues } from "commander"; import { CommandConfig, Configurator, IModule } from "../../core/module-handler"; import { logger } from "../../util/logger"; import { Context } from "../../core/cli-context"; @@ -13,10 +13,8 @@ class ProfileModule implements IModule { register(context: Context, configurator: Configurator) { let command = configurator.command('profile'); - command.description('Manage profiles required to access a system.'); - - // action if no command is provided - command.action(this.showHelp); + command.description('Manage profiles required to access a system.') + .action(this.showHelp); // action if no command is provided // let's add a subcommand command.command("list") @@ -39,8 +37,7 @@ class ProfileModule implements IModule { await new ProfileCommand().makeDefaultProfile(profile); } - async createProfile(context: Context, command: Command) { - let options = command.opts(); + async createProfile(context: Context, command: Command, options: OptionValues) { await new ProfileCommand().createProfile(options.setAsDefault); } From 0b60d45247fdb0ca9c8ef10e8b03df372db52690 Mon Sep 17 00:00:00 2001 From: zgjimhaziri Date: Tue, 13 May 2025 17:59:37 +0200 Subject: [PATCH 11/45] TA-3749: Define foundation of the CLI --- src/content-cli-profile.ts | 106 +- .../profile/profile-command.service.ts} | 10 +- .../commands}/profile/profile-module.ts | 27 +- .../commands}/profile/question.service.ts | 0 .../content-cli-refactored.ts} | 25 +- .../core/command}/cli-context.ts | 8 +- .../core/command}/module-handler.ts | 23 +- .../core/http/axios-initializer.ts | 21 + .../core/http}/base-api.ts | 3 +- .../core/http}/http-client.ts | 12 +- src/content-cli-refactor/core/http/tracing.ts | 24 + .../core/profile/profile.interface.ts | 34 + .../core/profile/profile.service.ts | 302 ++++ .../core/profile/profile.validator.ts | 23 + .../core/utils/file-service.ts | 36 + src/content-cli-refactor/core/utils/logger.ts | 104 ++ .../core/utils/version.ts | 8 + src/modules/pacman/api/config-service-api.ts | 64 - .../pacman/commands/config-list-command.ts | 42 - src/modules/pacman/pacman-module.ts | 84 - src/modules/studio/list-api.ts | 95 -- src/modules/studio/list-commands.ts | 17 - src/modules/studio/list-service.ts | 85 - src/modules/studio/studio-module.ts | 48 - src/modules/test/test-module.ts | 51 - yarn.lock | 1378 ++++++++--------- 26 files changed, 1339 insertions(+), 1291 deletions(-) rename src/{modules/profile/profile.command.ts => content-cli-refactor/commands/profile/profile-command.service.ts} (89%) rename src/{modules => content-cli-refactor/commands}/profile/profile-module.ts (56%) rename src/{modules => content-cli-refactor/commands}/profile/question.service.ts (100%) rename src/{celonis-cli.ts => content-cli-refactor/content-cli-refactored.ts} (76%) rename src/{core => content-cli-refactor/core/command}/cli-context.ts (90%) rename src/{core => content-cli-refactor/core/command}/module-handler.ts (93%) create mode 100644 src/content-cli-refactor/core/http/axios-initializer.ts rename src/{core => content-cli-refactor/core/http}/base-api.ts (99%) rename src/{core => content-cli-refactor/core/http}/http-client.ts (95%) create mode 100644 src/content-cli-refactor/core/http/tracing.ts create mode 100644 src/content-cli-refactor/core/profile/profile.interface.ts create mode 100644 src/content-cli-refactor/core/profile/profile.service.ts create mode 100644 src/content-cli-refactor/core/profile/profile.validator.ts create mode 100644 src/content-cli-refactor/core/utils/file-service.ts create mode 100644 src/content-cli-refactor/core/utils/logger.ts create mode 100644 src/content-cli-refactor/core/utils/version.ts delete mode 100644 src/modules/pacman/api/config-service-api.ts delete mode 100644 src/modules/pacman/commands/config-list-command.ts delete mode 100644 src/modules/pacman/pacman-module.ts delete mode 100644 src/modules/studio/list-api.ts delete mode 100644 src/modules/studio/list-commands.ts delete mode 100644 src/modules/studio/list-service.ts delete mode 100644 src/modules/studio/studio-module.ts delete mode 100644 src/modules/test/test-module.ts diff --git a/src/content-cli-profile.ts b/src/content-cli-profile.ts index 4ac3b1da..c8541dfe 100644 --- a/src/content-cli-profile.ts +++ b/src/content-cli-profile.ts @@ -1,53 +1,53 @@ -import { ProfileCommand } from "./modules/profile/profile.command"; -import { Command } from "commander"; -import { program } from "./util/program"; - -class Profile { - public static listProfile(program: Command): Command { - program - .command("list") - .description("Command to list all stored profiles") - .action(async () => { - await new ProfileCommand().listProfiles(); - process.exit(); - }); - - return program; - } - - public static createProfile(program: Command): Command { - program - .command("create") - .description("Command to create a new profile") - .option("--setAsDefault", "Set this profile as default") - .action(async cmd => { - await new ProfileCommand().createProfile(cmd.setAsDefault); - process.exit(); - }); - - return program; - } - - public static defaultProfile(program: Command): Command { - program - .command("default ") - .description("Command to set a profile as default") - .action(async profile => { - await new ProfileCommand().makeDefaultProfile(profile); - process.exit(); - }); - - return program; - } -} - -Profile.listProfile(program); -Profile.createProfile(program); -Profile.defaultProfile(program); - -program.parse(process.argv); - -if (!process.argv.slice(2).length) { - program.outputHelp(); - process.exit(1); -} +// import { ProfileCommand } from "./commands/profile.command"; +// import { Command } from "commander"; +// import { program } from "./util/program"; +// +// class Profile { +// public static listProfile(program: Command): Command { +// program +// .command("list") +// .description("Command to list all stored profiles") +// .action(async () => { +// await new ProfileCommand().listProfiles(); +// process.exit(); +// }); +// +// return program; +// } +// +// public static createProfile(program: Command): Command { +// program +// .command("create") +// .description("Command to create a new profile") +// .option("--setAsDefault", "Set this profile as default") +// .action(async cmd => { +// await new ProfileCommand().createProfile(cmd.setAsDefault); +// process.exit(); +// }); +// +// return program; +// } +// +// public static defaultProfile(program: Command): Command { +// program +// .command("default ") +// .description("Command to set a profile as default") +// .action(async profile => { +// await new ProfileCommand().makeDefaultProfile(profile); +// process.exit(); +// }); +// +// return program; +// } +// } +// +// Profile.listProfile(program); +// Profile.createProfile(program); +// Profile.defaultProfile(program); +// +// program.parse(process.argv); +// +// if (!process.argv.slice(2).length) { +// program.outputHelp(); +// process.exit(1); +// } diff --git a/src/modules/profile/profile.command.ts b/src/content-cli-refactor/commands/profile/profile-command.service.ts similarity index 89% rename from src/modules/profile/profile.command.ts rename to src/content-cli-refactor/commands/profile/profile-command.service.ts index 3f3cacc8..c3e3c1af 100644 --- a/src/modules/profile/profile.command.ts +++ b/src/content-cli-refactor/commands/profile/profile-command.service.ts @@ -1,10 +1,10 @@ import { QuestionService } from "./question.service"; -import {Profile, ProfileType} from "../../interfaces/profile.interface"; -import { ProfileService } from "../../services/profile.service"; -import { ProfileValidator } from "../../validators/profile.validator"; -import { FatalError, logger } from "../../util/logger"; +import { ProfileService } from "../../core/profile/profile.service"; +import { FatalError, logger } from "../../core/utils/logger"; +import {Profile, ProfileType} from "../../core/profile/profile.interface"; +import {ProfileValidator} from "../../core/profile/profile.validator"; -export class ProfileCommand { +export class ProfileCommandService { private profileService = new ProfileService(); public async createProfile(setAsDefault: boolean): Promise { diff --git a/src/modules/profile/profile-module.ts b/src/content-cli-refactor/commands/profile/profile-module.ts similarity index 56% rename from src/modules/profile/profile-module.ts rename to src/content-cli-refactor/commands/profile/profile-module.ts index 9c6c53ca..abc25535 100644 --- a/src/modules/profile/profile-module.ts +++ b/src/content-cli-refactor/commands/profile/profile-module.ts @@ -3,20 +3,18 @@ */ import { Command, OptionValues } from "commander"; -import { CommandConfig, Configurator, IModule } from "../../core/module-handler"; -import { logger } from "../../util/logger"; -import { Context } from "../../core/cli-context"; -import { ProfileCommand } from "./profile.command"; +import { Configurator, IModule } from "../../core/command/module-handler"; +import { logger } from "../../core/utils/logger"; +import { Context } from "../../core/command/cli-context"; +import { ProfileCommandService } from "./profile-command.service"; -class ProfileModule implements IModule { +class ProfileModule extends IModule { register(context: Context, configurator: Configurator) { - let command = configurator.command('profile'); - command.description('Manage profiles required to access a system.') - .action(this.showHelp); // action if no command is provided + let command = configurator.command("profile"); + command.description("Manage profiles required to access a system."); - // let's add a subcommand command.command("list") .description("Command to list all stored profiles") .action(this.listProfiles); @@ -29,25 +27,20 @@ class ProfileModule implements IModule { command.command("default ") .description("Command to set a profile as default") .action(this.defaultProfile); - } async defaultProfile(context: Context, command: Command) { let profile = command.args[0]; - await new ProfileCommand().makeDefaultProfile(profile); + await new ProfileCommandService().makeDefaultProfile(profile); } async createProfile(context: Context, command: Command, options: OptionValues) { - await new ProfileCommand().createProfile(options.setAsDefault); + await new ProfileCommandService().createProfile(options.setAsDefault); } async listProfiles(context: Context, command: Command) { logger.debug(`List profiles`); - await new ProfileCommand().listProfiles(); - } - - showHelp(context: Context, command: Command) { - command.outputHelp(); + await new ProfileCommandService().listProfiles(); } } diff --git a/src/modules/profile/question.service.ts b/src/content-cli-refactor/commands/profile/question.service.ts similarity index 100% rename from src/modules/profile/question.service.ts rename to src/content-cli-refactor/commands/profile/question.service.ts diff --git a/src/celonis-cli.ts b/src/content-cli-refactor/content-cli-refactored.ts similarity index 76% rename from src/celonis-cli.ts rename to src/content-cli-refactor/content-cli-refactored.ts index 247c11a8..f8e9f40f 100644 --- a/src/celonis-cli.ts +++ b/src/content-cli-refactor/content-cli-refactored.ts @@ -1,21 +1,18 @@ #!/usr/bin/env node -//const logProc = require('why-is-node-running') - import semverSatisfies = require("semver/functions/satisfies"); -import { VersionUtils } from "./util/version"; -import { logger } from "./util/logger"; -import { Configurator, ModuleHandler } from "./core/module-handler"; +import { VersionUtils } from "../util/version"; +import { logger } from "../util/logger"; import { Command } from "commander"; -import { Context } from "./core/cli-context"; +import { Configurator, ModuleHandler } from "./core/command/module-handler"; +import { Context } from "./core/command/cli-context"; /** - * Celonis Content CLI. + * In-progress version of Content CLI. * * This is the main entry point for the CLI. */ - // Check if the Node.js version satisfies the minimum requirements const requiredVersion = ">=10.10.0"; if (!semverSatisfies(process.version, requiredVersion)) { @@ -33,9 +30,8 @@ program.option("-p, --profile [profile]"); program.option("--debug", "Print debug messages", false); program.parseOptions(process.argv); -// go for it... if (!program.opts().quietmode) { - console.log(`Celonis CLI - (C) Copyright 2025 - Celonis SE - Version ${VersionUtils.getCurrentCliVersion()}`); + console.log(`Content CLI - (C) Copyright 2025 - Celonis SE - Version ${VersionUtils.getCurrentCliVersion()}`); console.log(); } @@ -56,7 +52,6 @@ function configureRootCommands(configurator: Configurator) { } async function run() { - let context = new Context(program.opts()); await context.init(); @@ -75,14 +70,8 @@ async function run() { } catch (error) { logger.error(`An unexpected error occured: ${error}`); } - - /* -- Uncomment the below to find out why the process does not exit... - setTimeout(() => { - console.error("Node is still running. Active Handles:"); - logProc(); // Log details about active handles. - }, 5000); // Wait 5 seconds before logging, adjust as needed - */ } + run(); // catch uncaught exceptions diff --git a/src/core/cli-context.ts b/src/content-cli-refactor/core/command/cli-context.ts similarity index 90% rename from src/core/cli-context.ts rename to src/content-cli-refactor/core/command/cli-context.ts index 29d0cc0a..f1b8a9b4 100644 --- a/src/core/cli-context.ts +++ b/src/content-cli-refactor/core/command/cli-context.ts @@ -1,7 +1,7 @@ -import { ProfileService } from "../services/profile.service"; -import { Profile } from "../interfaces/profile.interface"; -import { logger } from "../util/logger"; -import { HttpClient } from "./http-client"; +import { HttpClient } from "../http/http-client"; +import {ProfileService} from "../profile/profile.service"; +import {logger} from "../utils/logger"; +import {Profile} from "../profile/profile.interface"; /** * The execution context object is passed to the modules to access diff --git a/src/core/module-handler.ts b/src/content-cli-refactor/core/command/module-handler.ts similarity index 93% rename from src/core/module-handler.ts rename to src/content-cli-refactor/core/command/module-handler.ts index 26770769..8b7ddb2a 100644 --- a/src/core/module-handler.ts +++ b/src/content-cli-refactor/core/command/module-handler.ts @@ -1,11 +1,15 @@ -import { logger } from "../util/logger"; +import { logger } from "../../../util/logger"; import path = require("path"); import * as fs from "fs"; import { Command, CommandOptions, OptionValues } from "commander"; import { Context } from "./cli-context"; -export interface IModule { - register(context: Context, commandConfig: Configurator); +export abstract class IModule { + abstract register(context: Context, commandConfig: Configurator); + + showHelp(context: Context, command: Command) { + command.outputHelp(); + } } export interface IModuleConstructor { @@ -30,8 +34,7 @@ export class ModuleHandler { * @param {any} rootPath - __dirname when invoked from the main entry file */ discoverAndRegisterModules(rootPath) { - let modulesDirPath = path.resolve(rootPath, 'modules'); - logger.debug(`Scanning for modules in: ${modulesDirPath}`); + let modulesDirPath = path.resolve(rootPath, "commands"); try { const moduleFolders = fs.readdirSync(modulesDirPath, { withFileTypes: true }); @@ -43,15 +46,14 @@ export class ModuleHandler { // the specification allows different variants, so we test for each. const fileVariants = [ `${moduleFolderName}-module.js`, - 'module.js', - 'index.js' + 'module.js' ]; // Calculate path relative to *this file's location in dist* let potentialModuleJsPath; for (let name of fileVariants) { potentialModuleJsPath = path.resolve( - rootPath, 'modules', moduleFolderName, + rootPath, 'commands', moduleFolderName, name // Look for the compiled JS file ); try { @@ -112,7 +114,7 @@ export class ModuleHandler { } } catch (error: any) { if (error.code === 'ENOENT') { - logger.error(`Modules directory not found relative to JS output: ${path.resolve(path.dirname(__filename), 'modules')}`); + logger.error(`Modules directory not found relative to JS output: ${path.resolve(path.dirname(__filename), "content-cli-refactor/commands")}`); } else { logger.error('Failed to read modules directory:', error); } @@ -137,8 +139,7 @@ export class Configurator { /** * Get or create a root level command. - * @param nameAndArgs - * @param opts + * @param name * @returns */ command(name: string) : CommandConfig { diff --git a/src/content-cli-refactor/core/http/axios-initializer.ts b/src/content-cli-refactor/core/http/axios-initializer.ts new file mode 100644 index 00000000..2154d592 --- /dev/null +++ b/src/content-cli-refactor/core/http/axios-initializer.ts @@ -0,0 +1,21 @@ +import axios, { AxiosInstance } from "axios"; +import { HttpsProxyAgent } from "hpagent"; + +export class AxiosInitializer { + public static initializeAxios(): AxiosInstance { + const httpsProxy = process.env.https_proxy || process.env.HTTPS_PROXY; + + if (httpsProxy) { + const httpsAgent = new HttpsProxyAgent({ + proxy: httpsProxy + }) + + return axios.create({ + httpsAgent, + proxy: false + }) + } else { + return axios.create(); + } + } +} \ No newline at end of file diff --git a/src/core/base-api.ts b/src/content-cli-refactor/core/http/base-api.ts similarity index 99% rename from src/core/base-api.ts rename to src/content-cli-refactor/core/http/base-api.ts index 0851e361..e33245da 100644 --- a/src/core/base-api.ts +++ b/src/content-cli-refactor/core/http/base-api.ts @@ -2,10 +2,9 @@ * Base API implementation, which provides basic error handling for common problems. */ -import { logger } from "../util/logger"; +import { logger } from "../utils/logger"; import { HttpClient } from "./http-client"; - export class ForbiddenError extends Error { constructor(message = 'Access Forbidden') { super(message); diff --git a/src/core/http-client.ts b/src/content-cli-refactor/core/http/http-client.ts similarity index 95% rename from src/core/http-client.ts rename to src/content-cli-refactor/core/http/http-client.ts index b0252ad2..68437f0f 100644 --- a/src/core/http-client.ts +++ b/src/content-cli-refactor/core/http/http-client.ts @@ -1,11 +1,11 @@ -import { AuthenticationType, Profile } from "../interfaces/profile.interface"; -import { FatalError, logger } from "../util/logger"; -import {TracingUtils} from "../util/tracing"; -import {VersionUtils} from "../util/version"; import {AxiosResponse, RawAxiosRequestHeaders} from "axios"; import * as FormData from "form-data"; -import { AxiosInitializer } from "../util/axios-initializer"; -import { Context } from "./cli-context"; +import { Context } from "../command/cli-context"; +import {AxiosInitializer} from "./axios-initializer"; +import {FatalError, logger} from "../utils/logger"; +import {TracingUtils} from "./tracing"; +import {AuthenticationType, Profile} from "../profile/profile.interface"; +import {VersionUtils} from "../utils/version"; /** * Http client configured based upon the CLI context. It will authenticate diff --git a/src/content-cli-refactor/core/http/tracing.ts b/src/content-cli-refactor/core/http/tracing.ts new file mode 100644 index 00000000..85acb987 --- /dev/null +++ b/src/content-cli-refactor/core/http/tracing.ts @@ -0,0 +1,24 @@ +import * as crypto from "crypto"; + +export class TracingUtils { + + public static getTracingHeaders(): { [key: string]: string } { + return { + "x-datadog-trace-id": this.getTraceId(), + "x-datadog-parent-id": this.getParentTraceId(), + "x-datadog-sampling-priority": "1", + }; + } + + private static getTraceId(): string { + return process.env.TRACE_ID || this.generateId(); + } + + private static getParentTraceId(): string { + return process.env.PARENT_TRACE_ID || this.generateId(); + } + + private static generateId(): string { + return crypto.randomBytes(8).toString("hex"); + } +} diff --git a/src/content-cli-refactor/core/profile/profile.interface.ts b/src/content-cli-refactor/core/profile/profile.interface.ts new file mode 100644 index 00000000..ae21f547 --- /dev/null +++ b/src/content-cli-refactor/core/profile/profile.interface.ts @@ -0,0 +1,34 @@ +export interface Profile { + name: string; + team: string; + type: ProfileType; + apiToken: string; + authenticationType: AuthenticationType; + clientId?: string; + clientSecret?: string; + scopes?: string[]; + clientAuthenticationMethod?: ClientAuthenticationMethod; + refreshToken?: string; + expiresAt?: number; +} + +export type AuthenticationType = "Bearer" | "AppKey"; +export type ProfileType = "Device Code" | "Client Credentials" | "Key"; + +export type ClientAuthenticationMethod = "client_secret_basic" | "client_secret_post"; +// tslint:disable-next-line:variable-name +export const AuthenticationType: { [key: string]: AuthenticationType } = { + BEARER: "Bearer", + APPKEY: "AppKey", +}; +// tslint:disable-next-line:variable-name +export const ProfileType: { [key: string]: ProfileType } = { + DEVICE_CODE: "Device Code", + CLIENT_CREDENTIALS: "Client Credentials", + KEY: "Key" +}; +// tslint:disable-next-line:variable-name +export const ClientAuthenticationMethod: { [key: string]: ClientAuthenticationMethod } = { + CLIENT_SECRET_BASIC: "client_secret_basic", + CLIENT_SECRET_POST: "client_secret_post", +}; diff --git a/src/content-cli-refactor/core/profile/profile.service.ts b/src/content-cli-refactor/core/profile/profile.service.ts new file mode 100644 index 00000000..3f1b27e8 --- /dev/null +++ b/src/content-cli-refactor/core/profile/profile.service.ts @@ -0,0 +1,302 @@ +import { + AuthenticationType, ClientAuthenticationMethod, + Profile, ProfileType +} from "./profile.interface"; +import { ProfileValidator } from "./profile.validator"; +import * as path from "path"; +import * as fs from "fs"; +import { FatalError, logger } from "../utils/logger"; +import { Issuer } from "openid-client"; +import axios from "axios"; +import os = require("os"); + +const homedir = os.homedir(); +// use 5 seconds buffer to avoid rare cases when accessToken is just about to expire before the command is sent +const expiryBuffer = 5000; +const deviceCodeScopes = ["studio", "package-manager", "integration.data-pools", "action-engine.projects"]; +const clientCredentialsScopes = ["studio", "integration.data-pools", "action-engine.projects"]; + +export interface Config { + defaultProfile: string; +} + +export class ProfileService { + private profileContainerPath = path.resolve(homedir, ".celonis-content-cli-profiles"); + private configContainer = path.resolve(this.profileContainerPath, "config.json"); + + public async findProfile(profileName: string): Promise { + return new Promise((resolve, reject) => { + try { + if (process.env.TEAM_URL && process.env.API_TOKEN) { + resolve(this.buildProfileFromEnvVariables()); + } else { + const file = fs.readFileSync( + path.resolve(this.profileContainerPath, this.constructProfileFileName(profileName)), + { encoding: "utf-8" } + ); + const profile : Profile = JSON.parse(file); + this.refreshProfile(profile) + .then(() => resolve(profile)); + } + } catch (e) { + reject( + "No profile provided. Please provide a profile or an TEAM_URL and API_TOKEN through env variables" + ); + } + }); + } + + public async makeDefaultProfile(profileName: string): Promise { + return new Promise((resolve, reject) => { + this.findProfile(profileName) + .then((profile: Profile) => { + this.createProfileContainerIfNotExists(); + this.storeConfig({ defaultProfile: profileName }); + resolve(profile); + }) + .catch(err => { + logger.error(new FatalError("Profile does not exit.")); + reject(err); + }); + }); + } + + public getDefaultProfile(): string { + if (fs.existsSync(this.configContainer)) { + const config = JSON.parse(fs.readFileSync(this.configContainer, { encoding: "utf-8" })) as Config; + return config.defaultProfile; + } else { + return null; + } + } + + public storeProfile(profile: Profile): void { + this.createProfileContainerIfNotExists(); + const newProfileFileName = this.constructProfileFileName(profile.name); + profile.team = this.getBaseTeamUrl(profile.team); + fs.writeFileSync(path.resolve(this.profileContainerPath, newProfileFileName), JSON.stringify(profile), { + encoding: "utf-8", + }); + } + + private async buildProfileFromEnvVariables(): Promise { + const profileVariables = this.getProfileEnvVariables(); + const profile: Profile = { + name: profileVariables.teamUrl, + team: profileVariables.teamUrl, + apiToken: profileVariables.apiToken, + authenticationType: AuthenticationType.BEARER, + type: ProfileType.KEY + }; + profile.authenticationType = await ProfileValidator.validateProfile(profile); + return profile; + } + + private storeConfig(config: Config): void { + fs.writeFileSync(this.configContainer, JSON.stringify(config), { encoding: "utf-8" }); + } + + private createProfileContainerIfNotExists(): void { + if (!fs.existsSync(this.profileContainerPath)) { + fs.mkdirSync(this.profileContainerPath); + } + } + + private constructProfileFileName(profileName: string): string { + return profileName + ".json"; + } + + public readAllProfiles(): Promise { + return new Promise((resolve, reject) => { + const profiles = this.getAllFilesInDirectory(); + resolve(profiles); + }); + } + + public getAllFilesInDirectory(): string[] { + let fileNames: string[] = []; + try { + if (fs.existsSync(this.profileContainerPath)) { + fileNames = fs + // @ts-ignore + .readdirSync(this.profileContainerPath, { withFileTypes: true }) + .filter( + dirent => + !dirent.isDirectory() && dirent.name.endsWith(".json") && dirent.name !== "config.json" + ) + .map(dirent => dirent.name.replace(".json", "")); + } + } catch (err) { + logger.error(new FatalError(err)); + } + return fileNames; + } + + public async authorizeProfile(profile: Profile) : Promise { + switch (profile.type) { + case ProfileType.KEY: + const url = profile.team.replace(/\/?$/, "/api/cloud/team"); + try { + await this.tryKeyAuthentication(url, AuthenticationType.BEARER, profile.apiToken); + profile.authenticationType = AuthenticationType.BEARER; + } catch (e) { + try { + await this.tryKeyAuthentication(url, AuthenticationType.APPKEY, profile.apiToken); + profile.authenticationType = AuthenticationType.APPKEY; + } catch (err) { + logger.error(new FatalError("The provided team or api key is wrong.")); + logger.error(err); + } + } + break; + case ProfileType.DEVICE_CODE: + try { + const deviceCodeIssuer = await Issuer.discover(profile.team); + const deviceCodeOAuthClient = new deviceCodeIssuer.Client({ + client_id: "content-cli", + token_endpoint_auth_method: "none", + }); + const deviceCodeHandle = await deviceCodeOAuthClient.deviceAuthorization({ + scope: deviceCodeScopes.join(" ") + }); + logger.info(`Continue authorization here: ${deviceCodeHandle.verification_uri_complete}`); + const deviceCodeTokenSet = await deviceCodeHandle.poll(); + profile.apiToken = deviceCodeTokenSet.access_token; + profile.refreshToken = deviceCodeTokenSet.refresh_token; + profile.expiresAt = deviceCodeTokenSet.expires_at; + } catch (err) { + logger.error(new FatalError("The provided team is wrong.")); + logger.error(err); + } + break; + case ProfileType.CLIENT_CREDENTIALS: + const clientCredentialsIssuer = await Issuer.discover(profile.team); + try { + // try with client secret basic + const clientCredentialsOAuthClient = new clientCredentialsIssuer.Client({ + client_id: profile.clientId, + client_secret: profile.clientSecret, + token_endpoint_auth_method: ClientAuthenticationMethod.CLIENT_SECRET_BASIC, + }); + const clientCredentialsTokenSet = await clientCredentialsOAuthClient.grant({ + grant_type: "client_credentials", + scope: clientCredentialsScopes.join(" ") + }); + profile.clientAuthenticationMethod = ClientAuthenticationMethod.CLIENT_SECRET_BASIC; + profile.apiToken = clientCredentialsTokenSet.access_token; + profile.expiresAt = clientCredentialsTokenSet.expires_at; + } catch (e) { + try { + // try with client secret post + const clientCredentialsOAuthClient = new clientCredentialsIssuer.Client({ + client_id: profile.clientId, + client_secret: profile.clientSecret, + token_endpoint_auth_method: ClientAuthenticationMethod.CLIENT_SECRET_POST, + }); + const clientCredentialsTokenSet = await clientCredentialsOAuthClient.grant({ + grant_type: "client_credentials", + scope: clientCredentialsScopes.join(" ") + }); + profile.clientAuthenticationMethod = ClientAuthenticationMethod.CLIENT_SECRET_POST; + profile.apiToken = clientCredentialsTokenSet.access_token; + profile.expiresAt = clientCredentialsTokenSet.expires_at; + } catch (err) { + logger.error(new FatalError("The OAuth client configuration is incorrect. " + + "Check the id, secret and scopes for correctness.")); + } + } + profile.scopes = [...clientCredentialsScopes]; + + break; + default: + logger.error(new FatalError("Unsupported profile type")); + break; + } + } + + public async refreshProfile(profile: Profile) : Promise { + if (!this.isProfileExpired(profile, expiryBuffer)) { + return; + } + const issuer = await Issuer.discover(profile.team); + if (profile.type === ProfileType.DEVICE_CODE) { + try { + const oauthClient = new issuer.Client({ + client_id: "content-cli", + token_endpoint_auth_method: "none", + }); + const tokenSet = await oauthClient.refresh(profile.refreshToken); + profile.apiToken = tokenSet.access_token; + profile.expiresAt = tokenSet.expires_at; + profile.refreshToken = tokenSet.refresh_token; + } catch (err) { + logger.error(new FatalError("The profile cannot be refreshed. Please retry or recreate profile.")); + } + } + else { + try { + const oauthClient = new issuer.Client({ + client_id: profile.clientId, + client_secret: profile.clientSecret, + token_endpoint_auth_method: profile.clientAuthenticationMethod, + }); + const tokenSet = await oauthClient.grant({ + grant_type: "client_credentials", + scope: profile.scopes.join(" ") + }); + profile.apiToken = tokenSet.access_token; + profile.expiresAt = tokenSet.expires_at; + } catch (err) { + logger.error(new FatalError("The profile cannot be refreshed. Please retry or recreate profile.")); + } + } + + this.storeProfile(profile); + } + + private getProfileEnvVariables(): any { + return { + teamUrl: this.getBaseTeamUrl(process.env.TEAM_URL), + apiToken: process.env.API_TOKEN, + }; + } + + private getBaseTeamUrl(teamUrl: string): string { + if (!teamUrl) { + return null; + } + + const url = new URL(teamUrl); + return url.origin; + } + + private isProfileExpired(profile: Profile, buffer: number = 0): boolean { + if (profile.type === null || profile.type === undefined || profile.type === ProfileType.KEY) { + return false; + } + const now = new Date(); + const expirationTime = new Date(profile.expiresAt * 1000 - buffer); + + return now > expirationTime; + } + + private tryKeyAuthentication(url: string, authType: AuthenticationType, apiToken: string): Promise { + return new Promise((resolve, reject) => { + axios.get(url, { + headers: { + Authorization: `${authType} ${apiToken}` + } + }).then(response => { + if (response.status === 200 && response.data.domain) { + resolve(); + } else { + reject(); + } + }).catch(() => { + reject(); + }) + }) + } +} + +export const profileService = new ProfileService(); diff --git a/src/content-cli-refactor/core/profile/profile.validator.ts b/src/content-cli-refactor/core/profile/profile.validator.ts new file mode 100644 index 00000000..8b78dd47 --- /dev/null +++ b/src/content-cli-refactor/core/profile/profile.validator.ts @@ -0,0 +1,23 @@ +import { FatalError, logger } from "../utils/logger"; +import validUrl = require("valid-url"); +import { Profile, ProfileType } from "./profile.interface"; + +export class ProfileValidator { + public static async validateProfile(profile: Profile): Promise { + if (profile.name == null) { + logger.error(new FatalError("The name can not be empty")); + } + if (profile.team == null) { + logger.error(new FatalError("The team can not be empty")); + } + if (profile.type === ProfileType.KEY && profile.apiToken == null) { + logger.error(new FatalError("The api token can not be empty for this profile type")); + } + if (profile.type === ProfileType.CLIENT_CREDENTIALS && (profile.clientId == null || profile.clientSecret == null)) { + logger.error(new FatalError("The client id and secret can not be empty for this profile type")); + } + if (!validUrl.isUri(profile.team)) { + logger.error(new FatalError("The provided url is not a valid url.")); + } + } +} diff --git a/src/content-cli-refactor/core/utils/file-service.ts b/src/content-cli-refactor/core/utils/file-service.ts new file mode 100644 index 00000000..98352c38 --- /dev/null +++ b/src/content-cli-refactor/core/utils/file-service.ts @@ -0,0 +1,36 @@ +import * as fs from "fs"; +import * as path from "path"; +import {FatalError, logger} from "./logger"; + +export class FileService { + public static readonly fileDownloadedMessage = "File downloaded successfully. New filename: "; + + public writeToFileWithGivenName(data: any, filename: string): void { + fs.writeFileSync(path.resolve(process.cwd(), filename), this.getSerializedFileContent(data), { + encoding: "utf-8", + }); + } + + public createDirectoryWithGivenName(dirName: string): void { + fs.mkdirSync(path.resolve(process.cwd(), dirName)); + } + + public async readFileToJson(fileName: string): Promise { + const fileContent = this.readFile(fileName); + + return JSON.parse(fileContent); + } + + public readFile(filename: string): string { + if (!fs.existsSync(path.resolve(process.cwd(), filename))) { + logger.error(new FatalError(`The provided file '${filename}' does not exit`)); + } + return fs.readFileSync(path.resolve(process.cwd(), filename), {encoding: "utf-8"}); + } + + private getSerializedFileContent(data: any): string { + return data; + } +} + +export const fileService = new FileService(); diff --git a/src/content-cli-refactor/core/utils/logger.ts b/src/content-cli-refactor/core/utils/logger.ts new file mode 100644 index 00000000..cddd6464 --- /dev/null +++ b/src/content-cli-refactor/core/utils/logger.ts @@ -0,0 +1,104 @@ +import * as winston from "winston"; +import * as Transport from "winston-transport"; +import { Logger } from "winston"; +import os = require("os"); +import * as path from "path"; +import * as fs from "fs"; + +class CustomTransport extends Transport { + constructor(opts: any) { + super(opts); + } + + public log(info: any, cb: () => void): void { + setImmediate(() => { + this.emit("logged", info); + }); + + cb(); + if (info.error || (info.errno && info.errno !== "__CELGRACEFULERROR")) { + process.exit(1); + } + } +} + +// log into the same directory as where the profiles are, to avoid creating more +// directories for now. Consider changing this to a general 'home' directory such +// as .celonis-cli or alike. +const logDirName = ".celonis-content-cli-profiles"; +const logFileName = 'celonis-cli.log'; +const exceptionLogFileName = 'exceptions.log'; +const maxLogSizeMB = 3; +const logDir = path.join(os.homedir(), logDirName); +const logFilePath = path.join(logDir, logFileName); +const exceptionLogFilePath = path.join(logDir, exceptionLogFileName); +const maxSizeBytes = maxLogSizeMB * 1024 * 1024; // 3 MB in bytes + +// --- Ensure log directory exists --- +try { + if (!fs.existsSync(logDir)) { + fs.mkdirSync(logDir, { recursive: true }); + } +} catch (error) { + console.error(`Error creating log directory: ${logDir}`, error); +} + +export const logger: Logger = winston.createLogger({ + format: winston.format.combine(winston.format.cli()), + level: 'debug', + transports: [ + new winston.transports.Console({ + level: 'info', + format: winston.format.combine( + winston.format.colorize(), + winston.format.cli() + ), + }), + new winston.transports.File({ + level: 'info', // Log everything from debug up to the file + filename: logFilePath, + format: winston.format.combine( + winston.format.timestamp(), // Add timestamp to file logs + winston.format.errors({ stack: true }), // Log stack traces + winston.format.json() // Log in JSON format + ), + maxsize: maxSizeBytes, + maxFiles: 5, + tailable: true, + }), + new CustomTransport({}) + ], + exceptionHandlers: [ + new winston.transports.Console({ + format: winston.format.combine( + winston.format.colorize(), + winston.format.cli() + ), + }), + new winston.transports.File({ + filename: exceptionLogFilePath, // Separate file recommended + format: winston.format.combine( + winston.format.timestamp(), + winston.format.errors({ stack: true }), + winston.format.json() + ), + maxsize: maxSizeBytes, + maxFiles: 2, // Keep fewer exception logs if desired + tailable: true, + }), + new CustomTransport({}) + ], + exitOnError: false, +}); + +// tslint:disable-next-line: max-classes-per-file +export class FatalError extends Error { + public error = "FatalError"; +} + +// By default the logger will process.exit(1) when logging an uncaught fatal error +// This interface allows us to throw errors that do not force the process to exit and can be handled gracefully +// tslint:disable-next-line: max-classes-per-file +export class GracefulError extends Error { + public code: "__CELGRACEFULERROR"; +} diff --git a/src/content-cli-refactor/core/utils/version.ts b/src/content-cli-refactor/core/utils/version.ts new file mode 100644 index 00000000..40bea993 --- /dev/null +++ b/src/content-cli-refactor/core/utils/version.ts @@ -0,0 +1,8 @@ +// tslint:disable-next-line:no-var-requires +const { version } = require("../../../../package.json"); + +export class VersionUtils { + public static getCurrentCliVersion(): string { + return version; + } +} diff --git a/src/modules/pacman/api/config-service-api.ts b/src/modules/pacman/api/config-service-api.ts deleted file mode 100644 index 9d491549..00000000 --- a/src/modules/pacman/api/config-service-api.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { BaseApi } from "../../../core/base-api"; -import { Context } from "../../../core/cli-context"; -import { HttpClient } from "../../../core/http-client"; -import { - PackageExportTransport, - PackageKeyAndVersionPair, PostPackageImportData, - VariableManifestTransport -} from "../../../interfaces/package-export-transport"; -import {FatalError} from "../../../util/logger"; -import * as FormData from "form-data"; - -export class ConfigServiceApi extends BaseApi { - - constructor(private context : Context) { - super(context.httpClient); - } - - public findAllActivePackages(flavors: string[], withDependencies: boolean = false): Promise { - const queryParams = new URLSearchParams(); - - queryParams.set("withDependencies", withDependencies.toString()); - flavors.forEach(flavor => queryParams.append("flavors", flavor)) - - return this.httpClient.get(`/package-manager/api/core/packages/export/list?${queryParams.toString()}`) - .catch(e => { - return this.handleError(e); - }); - } - - public findActivePackagesByKeys(packageKeys: string[], withDependencies: boolean = false): Promise { - const queryParams = new URLSearchParams(); - - packageKeys.forEach(key => queryParams.append("packageKeys", key)) - queryParams.set("withDependencies", withDependencies.toString()); - - return this.httpClient.get(`/package-manager/api/core/packages/export/list-by-keys?${queryParams.toString()}`).catch(e => { - throw new FatalError(`Problem getting active packages by keys: ${e}`); - }); - } - - public exportPackages(packageKeys: string[], withDependencies: boolean = false): Promise { - const queryParams = new URLSearchParams(); - packageKeys.forEach(packageKey => queryParams.append("packageKeys", packageKey)); - queryParams.set("withDependencies", withDependencies.toString()); - - return this.httpClient.getFile(`/package-manager/api/core/packages/export/batch?${queryParams.toString()}`).catch(e => { - throw new FatalError(`Problem exporting packages: ${e}`); - }); - } - - public importPackages(data: FormData, overwrite: boolean): Promise { - return this.httpClient.postFile( - "/package-manager/api/core/packages/import/batch", - data, - {overwrite} - ); - } - - public findVariablesWithValuesByPackageKeysAndVersion(packagesByKeyAndVersion: PackageKeyAndVersionPair[]): Promise { - return this.httpClient.post("/package-manager/api/core/packages/export/batch/variables-with-assignments", packagesByKeyAndVersion).catch(e => { - throw new FatalError(`Problem exporting package variables: ${e}`); - }) - } -} \ No newline at end of file diff --git a/src/modules/pacman/commands/config-list-command.ts b/src/modules/pacman/commands/config-list-command.ts deleted file mode 100644 index 1b53c110..00000000 --- a/src/modules/pacman/commands/config-list-command.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { ForbiddenError } from "../../../core/base-api"; -import { Context } from "../../../core/cli-context"; -import { logger } from "../../../util/logger"; -import { ConfigServiceApi } from "../api/config-service-api"; - - -export class ConfigListCommand { - - api: ConfigServiceApi; - - constructor(private context: Context) { - this.api = new ConfigServiceApi(context); - } - - async execute(jsonResponse: boolean, flavors: string[], withDependencies: boolean, packageKeys:string[]): Promise { - - flavors = flavors ?? []; - - try { - - if (jsonResponse) { - //await batchImportExportService.findAndExportListOfActivePackages(flavors ?? [], packageKeys ?? [], withDependencies) - } else { - logger.info(`List of active packages:`); - const activePackages = await this.api.findAllActivePackages(flavors); - activePackages.forEach(pkg => { - logger.info(`${pkg.name} - Key: "${pkg.key}"`) - }); - logger.info(`${activePackages.length} package(s) found.`); - } - - } catch (error) { - // handle the error in a nice way.... - if (error instanceof ForbiddenError) { - logger.error(`You do not have the rights to perform this operation. Notice that you need a personal API key for 'config' operations.`); - } else { - throw error; - } - } - } - -} \ No newline at end of file diff --git a/src/modules/pacman/pacman-module.ts b/src/modules/pacman/pacman-module.ts deleted file mode 100644 index e6dbb5bb..00000000 --- a/src/modules/pacman/pacman-module.ts +++ /dev/null @@ -1,84 +0,0 @@ -/** - * This module provides all 'PacMan' related commands. - */ - -import { Command, OptionValues } from "commander"; -import { CommandConfig, Configurator, IModule } from "../../core/module-handler"; -import { logger } from "../../util/logger"; -import { Context } from "../../core/cli-context"; -import { ConfigListCommand } from "./commands/config-list-command"; -import { ForbiddenError } from "../../core/base-api"; - -class PacManModule implements IModule { - register(context: Context, configurator: Configurator) { - - let configCmd = configurator.command('config'); - configCmd.description('Configuration management functions, such as export, import, etc.'); - configCmd.action(this.showHelp); - - - configCmd.command("list") - .description("Command to list active packages that can be exported") - .option("--json", "Return response as json type", "") - .option("--flavors ", "Lists only active packages of the given flavors") - .option("--withDependencies", "Include dependencies", "") - .option("--packageKeys ", "Lists only given package keys") - .action(this.listCommand); - - - configCmd.command("variables") - .description("Commands related to variable configs") - .command("list") - .description("Command to list versioned variables of packages") - .option("--json", "Return response as json type", "") - .option("--keysByVersion ", "Mapping of package keys and versions", "") - .option("--keysByVersionFile ", "Package keys by version mappings file path.", "") - .action(this.variablesCommand); - - configCmd.command("export") - .description("Command to export package configs") - .requiredOption("--packageKeys ", "Keys of packages to export") - .option("--withDependencies", "Include variables and dependencies", "") - .action(this.configCommmand); - - configCmd.command("import") - .description("Command to import package configs") - .option("--overwrite", "Flag to allow overwriting of packages") - .requiredOption("-f, --file ", "Exported packages file (relative path)") - .action(this.importCommand); - - configCmd.command("diff") - .description("Command to diff configs of packages") - .option("--hasChanges", "Flag to return only the information if the package has changes without the actual changes") - .option("--json", "Return the response as a JSON file") - .requiredOption("-f, --file ", "Exported packages file (relative or absolute path)") - .action(this.diffCommand); - - } - - diffCommand(context: Context, command: Command, options: OptionValues) { - //new ConfigCommand().diffPackages(cmd.file, cmd.hasChanges, cmd.json); - } - - importCommand(context: Context, command: Command, options: OptionValues) { - //new ConfigCommand().batchImportPackages(cmd.file, cmd.overwrite); - } - - configCommmand(context: Context, command: Command, options: OptionValues) { - //new ConfigCommand().batchExportPackages(cmd.packageKeys, cmd.withDependencies); - } - - variablesCommand(context: Context, command: Command, options: OptionValues) { - //new ConfigCommand().listVariables(cmd.json, cmd.keysByVersion, cmd.keysByVersionFile); - } - - listCommand(context: Context, command: Command, options: OptionValues) { - new ConfigListCommand(context).execute(options.json, options.flavors, options.withDependencies, options.packageKeys); - } - - showHelp(context: Context, command: Command) { - command.outputHelp(); - } -} - -export = PacManModule; \ No newline at end of file diff --git a/src/modules/studio/list-api.ts b/src/modules/studio/list-api.ts deleted file mode 100644 index b4e1bcbf..00000000 --- a/src/modules/studio/list-api.ts +++ /dev/null @@ -1,95 +0,0 @@ -import { Context } from "../../core/cli-context"; -import { HttpClient } from "../../core/http-client"; -import { - ActivatePackageTransport, - ContentNodeTransport, - PackageHistoryTransport, - PackageManagerVariableType, - PackageWithVariableAssignments -} from "../../interfaces/package-manager.interfaces"; -import {FatalError} from "../../util/logger"; - - -export class ListApi { - - httpClient: HttpClient; - - constructor(private context : Context) { - this.httpClient = context.httpClient; - } - - public async findAllPackages(): Promise { - return this.httpClient.get("/package-manager/api/packages").catch(e => { - throw new FatalError(`Problem getting packages: ${e}`); - }); - } - - public async exportPackage(rootPackageKey: string, version?: string, excludeActionFlows?: boolean): Promise { - const queryParams = new URLSearchParams(); - queryParams.set("newKey", rootPackageKey); - queryParams.set("version", version ?? ""); - queryParams.set("excludeActionFlows", excludeActionFlows ? "true" : "false"); - - return await this.httpClient.downloadFile(`/package-manager/api/packages/${rootPackageKey}/export?${queryParams.toString()}`).catch(e => { - throw new FatalError(`Pacakge ${rootPackageKey}_${version} failed to export.`) - }); - } - - public async findAllPackagesWithVariableAssignments(type: PackageManagerVariableType): Promise { - const queryParams = new URLSearchParams(); - if (type) { - queryParams.set("type", type); - } - - return this.httpClient.get(`/package-manager/api/packages/with-variable-assignments?${queryParams.toString()}`).catch(e => { - throw new FatalError(`Problem getting variables of packages: : ${e}`); - }); - } - - public async findLatestVersionById(nodeId: string): Promise { - return this.httpClient.get(`/package-manager/api/packages/${nodeId}/latest-version`).catch(e => { - throw new FatalError(`Problem getting latest version of package: ${e}`); - }); - } - - public async findActiveVersionById(nodeId: string): Promise { - return this.httpClient.get(`/package-manager/api/packages/${nodeId}/active`).catch(e => { - throw new FatalError(`Problem getting latest version of package: ${e}`); - }); - } - - public async findActiveVersionByIds(nodeIds: string[]): Promise { - return this.httpClient.post("/package-manager/api/packages/active/by-ids", nodeIds).catch(e => { - throw new FatalError(`Problem getting latest version of packages: ${e}`); - }); - } - - public async findNextVersion(nodeId: string): Promise { - return this.httpClient.get(`/package-manager/api/packages/${nodeId}/next-version`).catch(e => { - throw new FatalError(`Problem getting latest version of package: ${e}`); - }); - } - - public async importPackage(nodeContent: any, spaceId: string, overwrite: boolean, excludeActionFlows?: boolean): Promise { - await this.httpClient.postFile("/package-manager/api/packages/import", nodeContent, { - spaceId: spaceId, - overwrite: overwrite, - excludeActionFlows: excludeActionFlows - }).catch(e => { - throw new FatalError(`Problem importing package: ${e}`); - }); - } - - public async movePackageToSpace(nodeId: string, spaceId: string): Promise { - await this.httpClient.put(`/package-manager/api/packages/${nodeId}/move/${spaceId}`, {}).catch(e => { - throw new FatalError(`Problem moving package: ${e}`); - }); - } - - public async publishPackage(activatePackage: ActivatePackageTransport): Promise { - await this.httpClient.post(`/package-manager/api/packages/${activatePackage.packageKey}/activate`, activatePackage).catch(e => { - throw new FatalError(`Problem activating package with key ${activatePackage.packageKey}: ${e}`); - }); - } -} - diff --git a/src/modules/studio/list-commands.ts b/src/modules/studio/list-commands.ts deleted file mode 100644 index dfaa4021..00000000 --- a/src/modules/studio/list-commands.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { Context } from "../../core/cli-context"; -import { ListService } from "./list-service"; - -export class ListCommand { - - constructor(private context : Context) {} - - public async listPackages(jsonResponse: boolean, includeDependencies: boolean, packageKeys: string[]): Promise { - const listService = new ListService(this.context); - if (jsonResponse) { - await listService.findAndExportListOfAllPackages(includeDependencies, packageKeys ?? []); - } else { - await listService.listPackages(); - } - } - -} diff --git a/src/modules/studio/list-service.ts b/src/modules/studio/list-service.ts deleted file mode 100644 index 2605f44f..00000000 --- a/src/modules/studio/list-service.ts +++ /dev/null @@ -1,85 +0,0 @@ -import { logger } from "../../util/logger"; -import { v4 as uuidv4 } from "uuid"; -import { FileService, fileService } from "../../services/file-service"; -import { BatchExportNodeTransport } from "../../interfaces/batch-export-node-transport"; -import { dataModelService } from "../../services/package-manager/datamodel-service"; -import { PackageDependencyTransport, PackageManagerVariableType } from "../../interfaces/package-manager.interfaces"; -import { variableService } from "../../services/package-manager/variable-service"; -import AdmZip = require("adm-zip"); -import {packageDependenciesApi} from "../../api/package-dependencies-api"; -import { Context } from "../../core/cli-context"; -import { ListApi } from "./list-api"; - -export class ListService { - protected readonly fileDownloadedMessage = "File downloaded successfully. New filename: "; - - listApi : ListApi; - constructor(private context: Context) { - this.listApi = new ListApi(context); - } - - public async listPackages(): Promise { - const nodes = await this.listApi.findAllPackages(); - nodes.forEach(node => { - logger.info(`${node.name} - Key: "${node.key}"`); - }); - logger.info(`Found ${nodes.length} package(s)`); - } - - public async findAndExportListOfAllPackages(includeDependencies: boolean, packageKeys: string[]): Promise { - const fieldsToInclude = ["key", "name", "changeDate", "activatedDraftId", "workingDraftId", "spaceId"]; - - let nodesListToExport: BatchExportNodeTransport[] = await this.listApi.findAllPackages(); - if (packageKeys.length > 0) { - nodesListToExport = nodesListToExport.filter(node => packageKeys.includes(node.rootNodeKey)); - } - - if (includeDependencies) { - fieldsToInclude.push("type", "value", "dependencies", "id", "updateAvailable", "version", "poolId", "node", "dataModelId", "dataPool", "datamodels"); - const unPublishedNodes = nodesListToExport.filter(node => !node.activatedDraftId); - let publishedNodes = nodesListToExport.filter(node => node.activatedDraftId); - publishedNodes = await this.getNodesWithActiveVersion(publishedNodes); - nodesListToExport = [...publishedNodes, ...unPublishedNodes]; - - const packageWithDataModelVariableAssignments = await variableService.getVariableAssignmentsForNodes(PackageManagerVariableType.DATA_MODEL); - const dataModelDetailsByNode = await dataModelService.getDataModelDetailsForPackages(packageWithDataModelVariableAssignments); - nodesListToExport.forEach(node => { - node.datamodels = dataModelDetailsByNode.get(node.key); - }); - - const draftIdByNodeId = new Map(); - nodesListToExport.forEach(node => draftIdByNodeId.set(node.workingDraftId, node.id)); - - const dependenciesByPackageIds = await this.getPackagesWithDependencies(draftIdByNodeId); - - nodesListToExport = nodesListToExport.map(nodeToExport => { - nodeToExport.dependencies = dependenciesByPackageIds[nodeToExport.workingDraftId] ?? []; - return nodeToExport; - }) - } - this.exportListOfPackages(nodesListToExport, fieldsToInclude); - } - - public async getPackagesWithDependencies(draftIdByNodeId: Map): Promise> { - const allPackageDependencies: Map = await packageDependenciesApi.findPackageDependenciesByIds(draftIdByNodeId); - return allPackageDependencies; - } - - private exportListOfPackages(nodes: BatchExportNodeTransport[], fieldsToInclude: string[]): void { - const filename = uuidv4() + ".json"; - fileService.writeToFileWithGivenName(JSON.stringify(nodes, fieldsToInclude), filename); - logger.info(FileService.fileDownloadedMessage + filename); - } - - public async getNodesWithActiveVersion(nodes: BatchExportNodeTransport[]): Promise { - const activeVersionsOfPackage = await this.listApi.findActiveVersionByIds(nodes.map(node => node.id)); - - nodes.forEach(node => { - node.version = activeVersionsOfPackage.find(packageVersion => packageVersion.id === node.id); - }) - - return nodes; - } - -} - diff --git a/src/modules/studio/studio-module.ts b/src/modules/studio/studio-module.ts deleted file mode 100644 index eb853636..00000000 --- a/src/modules/studio/studio-module.ts +++ /dev/null @@ -1,48 +0,0 @@ -/** - * Commands to create and list access profiles. - */ - -import { Command, OptionValues } from "commander"; -import { CommandConfig, Configurator, IModule } from "../../core/module-handler"; -import { logger } from "../../util/logger"; -import { Context } from "../../core/cli-context"; -import { SpaceCommand } from "../../commands/space.command"; -import { ListCommand } from "./list-commands"; - -class ListModule implements IModule { - - register(context: Context, configurator: Configurator) { - - // Extend the "list" command with some studio specific options - let listCmd = configurator.command('list'); - - // let's add a subcommand - listCmd.command("packages") - .description("Command to list all packages") - .option("--json", "Return response as json type", "") - .option("--includeDependencies", "Include variables and dependencies", "") - .option("--packageKeys ", "Lists only given package keys") - .action(this.listPackages); - - listCmd.command("spaces") - .description("Command to list all spaces") - .option("--json", "Return response as json type", "") - .action(this.listSpaces); - - } - - async listPackages(context: Context, command: Command, options: OptionValues) { - await new ListCommand(context).listPackages(options.json, options.includeDependencies, options.packageKeys); - } - - async listSpaces(context: Context, command: Command, options: OptionValues) { - await new SpaceCommand().listSpaces(undefined, options.json); - } - - - showHelp(context: Context, command: Command) { - command.outputHelp(); - } -} - -export = ListModule; \ No newline at end of file diff --git a/src/modules/test/test-module.ts b/src/modules/test/test-module.ts deleted file mode 100644 index 45db1f51..00000000 --- a/src/modules/test/test-module.ts +++ /dev/null @@ -1,51 +0,0 @@ -/** - * Configures the module commands, options, etc. - */ - -import { Command, CommandOptions, OptionValues } from "commander"; -import { CommandConfig, Configurator, IModule } from "../../core/module-handler"; -import { logger } from "../../util/logger"; -import { Context } from "../../core/cli-context"; - -class TestModule implements IModule { - register(context: Context, configurator: Configurator) { - - let command = configurator.command('test'); - command.option('-k, --key [string]', 'The key'); - command.action(this.invoke); - - // let's add a subcommand - command.command('blink') - .description('Blink a few times') - .option('-c, --count [number]') - .action(this.blink); - - - // lets add 'test' sub-command under the list command - let listCommand = configurator.command('list'); - listCommand.command('test') - .description('Test List') - .option('-c, --count [number]') - .action(this.testList); - - - } - - testList(context: Context, command: Command) { - - logger.info(`Test List`); - } - - blink(context: Context, command: Command) { - logger.info(`I will blink`); - } - - invoke(context: Context, command: Command, options: OptionValues) { - logger.info(`Test invocation, key is ${options.key}. Profile is ${context.profileName}`); - if (context.httpClient) { - logger.info(`HttpClient is present`); - } - } -} - -export = TestModule; \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 8d290bd3..2e09e25d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4,7 +4,7 @@ "@ampproject/remapping@^2.2.0": version "2.3.0" - resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.3.0.tgz#ed441b6fa600072520ce18b43d2c8cc8caecc7f4" + resolved "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz" integrity sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw== dependencies: "@jridgewell/gen-mapping" "^0.3.5" @@ -12,7 +12,7 @@ "@aws-crypto/crc32@5.2.0": version "5.2.0" - resolved "https://registry.yarnpkg.com/@aws-crypto/crc32/-/crc32-5.2.0.tgz#cfcc22570949c98c6689cfcbd2d693d36cdae2e1" + resolved "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-5.2.0.tgz" integrity sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg== dependencies: "@aws-crypto/util" "^5.2.0" @@ -21,7 +21,7 @@ "@aws-crypto/sha256-browser@5.2.0": version "5.2.0" - resolved "https://registry.yarnpkg.com/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz#153895ef1dba6f9fce38af550e0ef58988eb649e" + resolved "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz" integrity sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw== dependencies: "@aws-crypto/sha256-js" "^5.2.0" @@ -34,7 +34,7 @@ "@aws-crypto/sha256-js@5.2.0", "@aws-crypto/sha256-js@^5.2.0": version "5.2.0" - resolved "https://registry.yarnpkg.com/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz#c4fdb773fdbed9a664fc1a95724e206cf3860042" + resolved "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz" integrity sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA== dependencies: "@aws-crypto/util" "^5.2.0" @@ -43,14 +43,14 @@ "@aws-crypto/supports-web-crypto@^5.2.0": version "5.2.0" - resolved "https://registry.yarnpkg.com/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz#a1e399af29269be08e695109aa15da0a07b5b5fb" + resolved "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz" integrity sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg== dependencies: tslib "^2.6.2" "@aws-crypto/util@^5.2.0": version "5.2.0" - resolved "https://registry.yarnpkg.com/@aws-crypto/util/-/util-5.2.0.tgz#71284c9cffe7927ddadac793c14f14886d3876da" + resolved "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz" integrity sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ== dependencies: "@aws-sdk/types" "^3.222.0" @@ -59,7 +59,7 @@ "@aws-sdk/client-cloudwatch-logs@^3.624.0": version "3.732.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/client-cloudwatch-logs/-/client-cloudwatch-logs-3.732.0.tgz#4fc0d78e9af3cb7f434b8068f253ab605592130f" + resolved "https://registry.npmjs.org/@aws-sdk/client-cloudwatch-logs/-/client-cloudwatch-logs-3.732.0.tgz" integrity sha512-R5H68f8P3n0nxMJ7UGvjCQq79UTP6jvxAeHrcrprQ90hfI2vCMjLpy5Px/GOTOjtkVSpggyeI90Q9y2qGQECig== dependencies: "@aws-crypto/sha256-browser" "5.2.0" @@ -109,7 +109,7 @@ "@aws-sdk/client-cognito-identity@3.731.1": version "3.731.1" - resolved "https://registry.yarnpkg.com/@aws-sdk/client-cognito-identity/-/client-cognito-identity-3.731.1.tgz#ddedb7b8b38a4c7c57883928c1be270bcb4c8f54" + resolved "https://registry.npmjs.org/@aws-sdk/client-cognito-identity/-/client-cognito-identity-3.731.1.tgz" integrity sha512-hlYxRERFNxa4Jplh8rjxbCvk6e4ybNKu2wQdiK46GS2N6io9Z62/CNqx3bMiqmjhk92LWXnYcpYwI2MG/WOEMQ== dependencies: "@aws-crypto/sha256-browser" "5.2.0" @@ -154,7 +154,7 @@ "@aws-sdk/client-iam@^3.624.0": version "3.731.1" - resolved "https://registry.yarnpkg.com/@aws-sdk/client-iam/-/client-iam-3.731.1.tgz#ddb914d954505cc086564bd4f4dbfe47e23c7e87" + resolved "https://registry.npmjs.org/@aws-sdk/client-iam/-/client-iam-3.731.1.tgz" integrity sha512-GLsUbzDSWXc+Jmgm1t4/A9VEu9zVeDn/u/rywMQgZ6jkkB795fvyyS/PyIdqvGiCrKMGBLn7VW9Uk130a1BLoA== dependencies: "@aws-crypto/sha256-browser" "5.2.0" @@ -200,7 +200,7 @@ "@aws-sdk/client-lambda@^3.624.0": version "3.731.1" - resolved "https://registry.yarnpkg.com/@aws-sdk/client-lambda/-/client-lambda-3.731.1.tgz#2c5e2c9b91c59ab8edc1d1262386eb4a5f2c48db" + resolved "https://registry.npmjs.org/@aws-sdk/client-lambda/-/client-lambda-3.731.1.tgz" integrity sha512-W6fjtBVFjq+loCKKO4Gi/oOFclUPTdtQflx/tTZrOej2OMy/CgCFdqoxyc017+MkfYc3v1ZX0P7pl/qQiBAWfQ== dependencies: "@aws-crypto/sha256-browser" "5.2.0" @@ -250,7 +250,7 @@ "@aws-sdk/client-sfn@^3.624.0": version "3.731.1" - resolved "https://registry.yarnpkg.com/@aws-sdk/client-sfn/-/client-sfn-3.731.1.tgz#5e7d2f65bc5401202975b7be8d5572d32f62791e" + resolved "https://registry.npmjs.org/@aws-sdk/client-sfn/-/client-sfn-3.731.1.tgz" integrity sha512-XKJQuRvULWAL3ONx76w6xi3Ob4L1KzgGpWd6osiW+4+3lM/SAVLCra10DjSNFhsqOOANyAezX/p3dGM2FgF61Q== dependencies: "@aws-crypto/sha256-browser" "5.2.0" @@ -297,7 +297,7 @@ "@aws-sdk/client-sso@3.731.0": version "3.731.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/client-sso/-/client-sso-3.731.0.tgz#6e3c13f9865863ad1fdedf848710d5fe9aa0cad6" + resolved "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.731.0.tgz" integrity sha512-O4C/UYGgqMsBg21MMApFdgyh8BX568hQhbdoNFmRVTBoSnCZ3w+H4a1wBPX4Gyl0NX+ab6Xxo9rId8HiyPXJ0A== dependencies: "@aws-crypto/sha256-browser" "5.2.0" @@ -341,7 +341,7 @@ "@aws-sdk/core@3.731.0", "@aws-sdk/core@^3.624.0": version "3.731.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/core/-/core-3.731.0.tgz#86b7cbdd63b20aa5e6339536d2c94a728dd4d83c" + resolved "https://registry.npmjs.org/@aws-sdk/core/-/core-3.731.0.tgz" integrity sha512-ithBN1VWASkvAIlozJmenqDvNnFddr/SZXAs58+jCnBHgy3tXLHABZGVNCjetZkHRqNdXEO1kirnoxaFeXMeDA== dependencies: "@aws-sdk/types" "3.731.0" @@ -358,7 +358,7 @@ "@aws-sdk/credential-provider-cognito-identity@3.731.1": version "3.731.1" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-cognito-identity/-/credential-provider-cognito-identity-3.731.1.tgz#f195044c6fb742db0a6b5eac3c763b634dc691c0" + resolved "https://registry.npmjs.org/@aws-sdk/credential-provider-cognito-identity/-/credential-provider-cognito-identity-3.731.1.tgz" integrity sha512-4MdhrZFkMxS/5ZUXaf6NIVa7N3NV259Q10jvfd6AzePd6sq10stJSyShvV7nC1dc/XneHammpYdXV2hlh6Almw== dependencies: "@aws-sdk/client-cognito-identity" "3.731.1" @@ -369,7 +369,7 @@ "@aws-sdk/credential-provider-env@3.731.0": version "3.731.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-env/-/credential-provider-env-3.731.0.tgz#456bee6ac9911f48c17f64a2955aa187cc91ef21" + resolved "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.731.0.tgz" integrity sha512-h0WWZg4QMLgFVyIvQrC43zpVqsUWg1mPM1clpogP43B8+wEhDEQ4qWRzvFs3dQ4cqx/FLyDUZZF4cqgd94z7kw== dependencies: "@aws-sdk/core" "3.731.0" @@ -380,7 +380,7 @@ "@aws-sdk/credential-provider-http@3.731.0": version "3.731.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-http/-/credential-provider-http-3.731.0.tgz#f3a2264744bd6af1c1de61b5ce2079c36f875fb3" + resolved "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.731.0.tgz" integrity sha512-iRtrjtcYaWgbvtu2cvDhIsPWXZGvhy1Hgks4682MEBNTc9AUwlfvDrYz2EEnTtJJyrbOdEHVrYrzqD8qPyVLCg== dependencies: "@aws-sdk/core" "3.731.0" @@ -396,7 +396,7 @@ "@aws-sdk/credential-provider-ini@3.731.1", "@aws-sdk/credential-provider-ini@^3.624.0": version "3.731.1" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.731.1.tgz#eade17c4086ac67be0a75e8b5414ba9777d178d7" + resolved "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.731.1.tgz" integrity sha512-0M0ejuqW8iHNcTH2ZXSY9m+I7Y06qVkj6k3vfQU9XaB//mTUCxxfGfqWAtgfr7Yi73egABTcPc0jyPdcvSW4Kw== dependencies: "@aws-sdk/core" "3.731.0" @@ -415,7 +415,7 @@ "@aws-sdk/credential-provider-node@3.731.1": version "3.731.1" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-node/-/credential-provider-node-3.731.1.tgz#2399fdcfd93ecc7f8a2c83f0580d8f16c63b65f8" + resolved "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.731.1.tgz" integrity sha512-5c0ZiagMTPmWilXNffeXJCLoCEz97jilHr3QJWwf2GaTay4tzN+Ld71rpdfEenzUR7fuxEWFfVlwQbFOzFNYHg== dependencies: "@aws-sdk/credential-provider-env" "3.731.0" @@ -433,7 +433,7 @@ "@aws-sdk/credential-provider-process@3.731.0": version "3.731.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-process/-/credential-provider-process-3.731.0.tgz#50cc40fa1919d6fc8ac9b8dea26b3ce317f15ece" + resolved "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.731.0.tgz" integrity sha512-6yNMY6q3xHLbs2f2+C6GhvMrjTgtFBiPJJqKaPLsTIhlTRvh4sK8pGm3ITcma0jOxtPDIuoPfBAV8N8XVMBlZg== dependencies: "@aws-sdk/core" "3.731.0" @@ -445,7 +445,7 @@ "@aws-sdk/credential-provider-sso@3.731.1": version "3.731.1" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.731.1.tgz#bb2228a5cfac6521741e69a74c2db57ab0ceb0e4" + resolved "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.731.1.tgz" integrity sha512-p1tp+rMUf5YNQLr8rVRmDgNtKGYLL0KCdq3K2hwwvFnx9MjReF1sA4lfm3xWsxBQM+j3QN9AvMQqBzDJ+NOSdw== dependencies: "@aws-sdk/client-sso" "3.731.0" @@ -459,7 +459,7 @@ "@aws-sdk/credential-provider-web-identity@3.731.1": version "3.731.1" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.731.1.tgz#1bb7b21ae579cbcc0b111e29319a2b5bdc187e85" + resolved "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.731.1.tgz" integrity sha512-+ynAvEGWDR5ZJFxgpwwzhvlQ3WQ7BleWXU6JwpIw3yFrD4eZEn85b8DZC1aEz7C9kb1HSV6B3gpqHqlyS6wj8g== dependencies: "@aws-sdk/core" "3.731.0" @@ -471,7 +471,7 @@ "@aws-sdk/credential-providers@^3.624.0": version "3.731.1" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-providers/-/credential-providers-3.731.1.tgz#5b6484caa0649f99f3ee0a6ae4b19e7e6f846554" + resolved "https://registry.npmjs.org/@aws-sdk/credential-providers/-/credential-providers-3.731.1.tgz" integrity sha512-Rjb14vXPa3flBJu9YDZkld0pYuR15DESMWGvCtQgGhcgpY8QH7vzxPU2C224SgYYkP0JM+7SRfadbcI5seTFuw== dependencies: "@aws-sdk/client-cognito-identity" "3.731.1" @@ -493,7 +493,7 @@ "@aws-sdk/middleware-host-header@3.731.0": version "3.731.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-host-header/-/middleware-host-header-3.731.0.tgz#7f62d4d1d6243bdba4c8737fc34668c95c6d0e1b" + resolved "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.731.0.tgz" integrity sha512-ndAJsm5uWPPJRZowLKpB1zuL17qWlWVtCJP4I/ynBkq1PU1DijDXBul2UZaG6Mpvsgms1NXo/h9noHuK7T3v8w== dependencies: "@aws-sdk/types" "3.731.0" @@ -503,7 +503,7 @@ "@aws-sdk/middleware-logger@3.731.0": version "3.731.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-logger/-/middleware-logger-3.731.0.tgz#8ab06f4c6c27be8893e3eb256d686e2bee5c4bf6" + resolved "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.731.0.tgz" integrity sha512-IIZrOdjbY2vKzPJPrwE7FoFQCIPEL6UqURi8LEaiVyCag4p2fvaTN5pgKuQtGC2+iYd/HHcGT4qn2bAqF5Jmmw== dependencies: "@aws-sdk/types" "3.731.0" @@ -512,7 +512,7 @@ "@aws-sdk/middleware-recursion-detection@3.731.0": version "3.731.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.731.0.tgz#c16057884029d9b10a822a47bfd51f59f3f8bf3a" + resolved "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.731.0.tgz" integrity sha512-y6FLASB1iKWuR5tUipMyo77bt0lEl3OnCrrd2xw/H24avq1HhJjjPR0HHhJE6QKJzF/FYXeV88tcyPSMe32VDw== dependencies: "@aws-sdk/types" "3.731.0" @@ -522,7 +522,7 @@ "@aws-sdk/middleware-user-agent@3.731.0": version "3.731.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.731.0.tgz#5a0c2b118c1a63a37cc4d4db1eb585115ffe4285" + resolved "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.731.0.tgz" integrity sha512-Ngr2Gz0aec/uduoKaO3srN52SYkEHndYtFzkK/gDUyQwQzi4ha2eIisxPiuHEX6RvXT31V9ouqn/YtVkt0R76A== dependencies: "@aws-sdk/core" "3.731.0" @@ -535,7 +535,7 @@ "@aws-sdk/nested-clients@3.731.1": version "3.731.1" - resolved "https://registry.yarnpkg.com/@aws-sdk/nested-clients/-/nested-clients-3.731.1.tgz#b60839691f0bbdcb1a1efe8668b1b814704811e6" + resolved "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.731.1.tgz" integrity sha512-/L8iVrulnXZl+kgmTn+oxRxNnhcSIbf+r12C06vGUq60w0YMidLvxJZN7vt8H9SnCAGCHqud2MS7ExCEvhc0gA== dependencies: "@aws-crypto/sha256-browser" "5.2.0" @@ -579,7 +579,7 @@ "@aws-sdk/region-config-resolver@3.731.0": version "3.731.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/region-config-resolver/-/region-config-resolver-3.731.0.tgz#d7508a1489b43a0767553c82f58c83788bbe3673" + resolved "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.731.0.tgz" integrity sha512-XlDpRNkDVHF59f07JmkuAidEv//m3hT6/JL85h0l3+zrpaRWhf8n8lVUyAPNq35ZujK8AcorYM+93u7hdWsliQ== dependencies: "@aws-sdk/types" "3.731.0" @@ -591,7 +591,7 @@ "@aws-sdk/token-providers@3.731.1": version "3.731.1" - resolved "https://registry.yarnpkg.com/@aws-sdk/token-providers/-/token-providers-3.731.1.tgz#02cd2ed586635f1ccdc91a1763994dbb545f9983" + resolved "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.731.1.tgz" integrity sha512-t34GOPwBZsX7zGHjiTXmMHGY3kHM7fLiQ60Jqk0On9P0ASHTDE5U75RgCXboE3u+qEv9wyKyaqMNyMWj9qQlFg== dependencies: "@aws-sdk/nested-clients" "3.731.1" @@ -603,7 +603,7 @@ "@aws-sdk/types@3.731.0", "@aws-sdk/types@^3.222.0": version "3.731.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/types/-/types-3.731.0.tgz#c35cc2a8c4c9eca768563037ffbdc0cb599f4cd4" + resolved "https://registry.npmjs.org/@aws-sdk/types/-/types-3.731.0.tgz" integrity sha512-NrdkJg6oOUbXR2r9WvHP408CLyvST8cJfp1/jP9pemtjvjPoh6NukbCtiSFdOOb1eryP02CnqQWItfJC1p2Y/Q== dependencies: "@smithy/types" "^4.0.0" @@ -611,7 +611,7 @@ "@aws-sdk/util-endpoints@3.731.0": version "3.731.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/util-endpoints/-/util-endpoints-3.731.0.tgz#21822554efd1f9a22742a4163a312a5dc9372a46" + resolved "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.731.0.tgz" integrity sha512-riztxTAfncFS9yQWcBJffGgOgLoKSa63ph+rxWJxKl6BHAmWEvHICj1qDcVmnWfIcvJ5cClclY75l9qKaUH7rQ== dependencies: "@aws-sdk/types" "3.731.0" @@ -621,14 +621,14 @@ "@aws-sdk/util-locate-window@^3.0.0": version "3.723.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/util-locate-window/-/util-locate-window-3.723.0.tgz#174551bfdd2eb36d3c16e7023fd7e7ee96ad0fa9" + resolved "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.723.0.tgz" integrity sha512-Yf2CS10BqK688DRsrKI/EO6B8ff5J86NXe4C+VCysK7UOgN0l1zOTeTukZ3H8Q9tYYX3oaF1961o8vRkFm7Nmw== dependencies: tslib "^2.6.2" "@aws-sdk/util-user-agent-browser@3.731.0": version "3.731.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.731.0.tgz#09139c7a5d04b0d07571f57b405ca71f761e4d3a" + resolved "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.731.0.tgz" integrity sha512-EnYXxTkCNCjTTBjW/pelRPv4Thsi9jepoB6qQjPMA9/ixrZ71BhhQecz9kgqzZLR9BPCwb6hgJ/Yd702jqJ4aQ== dependencies: "@aws-sdk/types" "3.731.0" @@ -638,7 +638,7 @@ "@aws-sdk/util-user-agent-node@3.731.0": version "3.731.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.731.0.tgz#97751200f073326b170263aabc43d1c01b6520bf" + resolved "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.731.0.tgz" integrity sha512-Rze78Ym5Bx7aWMvmZE2iL3JPo2INNCC5N9rLVx98Gg1G0ZaxclVRUvJrh1AojNlOFxU+otkxAe7FA3Foy2iLLQ== dependencies: "@aws-sdk/middleware-user-agent" "3.731.0" @@ -649,7 +649,7 @@ "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.25.9", "@babel/code-frame@^7.26.0", "@babel/code-frame@^7.26.2": version "7.26.2" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.26.2.tgz#4b5fab97d33338eff916235055f0ebc21e573a85" + resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz" integrity sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ== dependencies: "@babel/helper-validator-identifier" "^7.25.9" @@ -658,12 +658,12 @@ "@babel/compat-data@^7.26.5": version "7.26.5" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.26.5.tgz#df93ac37f4417854130e21d72c66ff3d4b897fc7" + resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.5.tgz" integrity sha512-XvcZi1KWf88RVbF9wn8MN6tYFloU5qX8KjuF3E1PVBmJ9eypXfs4GRiJwLuTZL0iSnJUKn1BFPa5BPZZJyFzPg== "@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.23.9": version "7.26.0" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.26.0.tgz#d78b6023cc8f3114ccf049eb219613f74a747b40" + resolved "https://registry.npmjs.org/@babel/core/-/core-7.26.0.tgz" integrity sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg== dependencies: "@ampproject/remapping" "^2.2.0" @@ -684,7 +684,7 @@ "@babel/generator@^7.26.0", "@babel/generator@^7.26.5", "@babel/generator@^7.7.2": version "7.26.5" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.26.5.tgz#e44d4ab3176bbcaf78a5725da5f1dc28802a9458" + resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.26.5.tgz" integrity sha512-2caSP6fN9I7HOe6nqhtft7V4g7/V/gfDsC3Ag4W7kEzzvRGKqiv0pu0HogPiZ3KaVSoNDhUws6IJjDjpfmYIXw== dependencies: "@babel/parser" "^7.26.5" @@ -695,7 +695,7 @@ "@babel/helper-compilation-targets@^7.25.9": version "7.26.5" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.26.5.tgz#75d92bb8d8d51301c0d49e52a65c9a7fe94514d8" + resolved "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.26.5.tgz" integrity sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA== dependencies: "@babel/compat-data" "^7.26.5" @@ -706,7 +706,7 @@ "@babel/helper-module-imports@^7.25.9": version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz#e7f8d20602ebdbf9ebbea0a0751fb0f2a4141715" + resolved "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz" integrity sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw== dependencies: "@babel/traverse" "^7.25.9" @@ -714,7 +714,7 @@ "@babel/helper-module-transforms@^7.26.0": version "7.26.0" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz#8ce54ec9d592695e58d84cd884b7b5c6a2fdeeae" + resolved "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz" integrity sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw== dependencies: "@babel/helper-module-imports" "^7.25.9" @@ -723,27 +723,27 @@ "@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.25.9", "@babel/helper-plugin-utils@^7.8.0": version "7.26.5" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz#18580d00c9934117ad719392c4f6585c9333cc35" + resolved "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz" integrity sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg== "@babel/helper-string-parser@^7.25.9": version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz#1aabb72ee72ed35789b4bbcad3ca2862ce614e8c" + resolved "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz" integrity sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA== "@babel/helper-validator-identifier@^7.25.9": version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz#24b64e2c3ec7cd3b3c547729b8d16871f22cbdc7" + resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz" integrity sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ== "@babel/helper-validator-option@^7.25.9": version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz#86e45bd8a49ab7e03f276577f96179653d41da72" + resolved "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz" integrity sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw== "@babel/helpers@^7.26.0": version "7.26.0" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.26.0.tgz#30e621f1eba5aa45fe6f4868d2e9154d884119a4" + resolved "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.0.tgz" integrity sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw== dependencies: "@babel/template" "^7.25.9" @@ -751,133 +751,133 @@ "@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.23.9", "@babel/parser@^7.25.9", "@babel/parser@^7.26.0", "@babel/parser@^7.26.5": version "7.26.5" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.26.5.tgz#6fec9aebddef25ca57a935c86dbb915ae2da3e1f" + resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.26.5.tgz" integrity sha512-SRJ4jYmXRqV1/Xc+TIVG84WjHBXKlxO9sHQnA2Pf12QQEAp1LOh6kDzNHXcUnbH1QI0FDoPPVOt+vyUDucxpaw== dependencies: "@babel/types" "^7.26.5" "@babel/plugin-syntax-async-generators@^7.8.4": version "7.8.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz" integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw== dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-bigint@^7.8.3": version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz#4c9a6f669f5d0cdf1b90a1671e9a146be5300cea" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz" integrity sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg== dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-class-properties@^7.12.13": version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz" integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA== dependencies: "@babel/helper-plugin-utils" "^7.12.13" "@babel/plugin-syntax-class-static-block@^7.14.5": version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz#195df89b146b4b78b3bf897fd7a257c84659d406" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz" integrity sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw== dependencies: "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-import-attributes@^7.24.7": version "7.26.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.26.0.tgz#3b1412847699eea739b4f2602c74ce36f6b0b0f7" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.26.0.tgz" integrity sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A== dependencies: "@babel/helper-plugin-utils" "^7.25.9" "@babel/plugin-syntax-import-meta@^7.10.4": version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz" integrity sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g== dependencies: "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-json-strings@^7.8.3": version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz" integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA== dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-jsx@^7.7.2": version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.25.9.tgz#a34313a178ea56f1951599b929c1ceacee719290" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.25.9.tgz" integrity sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA== dependencies: "@babel/helper-plugin-utils" "^7.25.9" "@babel/plugin-syntax-logical-assignment-operators@^7.10.4": version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz" integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig== dependencies: "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-nullish-coalescing-operator@^7.8.3": version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz" integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ== dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-numeric-separator@^7.10.4": version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz" integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug== dependencies: "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-object-rest-spread@^7.8.3": version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz" integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-optional-catch-binding@^7.8.3": version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz" integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q== dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-optional-chaining@^7.8.3": version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz" integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-private-property-in-object@^7.14.5": version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz#0dc6671ec0ea22b6e94a1114f857970cd39de1ad" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz" integrity sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg== dependencies: "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-top-level-await@^7.14.5": version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz#c1cfdadc35a646240001f06138247b741c34d94c" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz" integrity sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw== dependencies: "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-typescript@^7.7.2": version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.9.tgz#67dda2b74da43727cf21d46cf9afef23f4365399" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.9.tgz" integrity sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ== dependencies: "@babel/helper-plugin-utils" "^7.25.9" "@babel/template@^7.25.9", "@babel/template@^7.3.3": version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.25.9.tgz#ecb62d81a8a6f5dc5fe8abfc3901fc52ddf15016" + resolved "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz" integrity sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg== dependencies: "@babel/code-frame" "^7.25.9" @@ -886,7 +886,7 @@ "@babel/traverse@^7.25.9": version "7.26.5" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.26.5.tgz#6d0be3e772ff786456c1a37538208286f6e79021" + resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.5.tgz" integrity sha512-rkOSPOw+AXbgtwUga3U4u8RpoK9FEFWBNAlTpcnkLFjL5CT+oyHNuUUC/xx6XefEJ16r38r8Bc/lfp6rYuHeJQ== dependencies: "@babel/code-frame" "^7.26.2" @@ -899,7 +899,7 @@ "@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.25.9", "@babel/types@^7.26.0", "@babel/types@^7.26.5", "@babel/types@^7.3.3": version "7.26.5" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.26.5.tgz#7a1e1c01d28e26d1fe7f8ec9567b3b92b9d07747" + resolved "https://registry.npmjs.org/@babel/types/-/types-7.26.5.tgz" integrity sha512-L6mZmwFDK6Cjh1nRCLXpa6no13ZIioJDz7mdkzHv399pThrTa/k0nUlNaenOeh2kWu/iaOQYElEpKPUswUa9Vg== dependencies: "@babel/helper-string-parser" "^7.25.9" @@ -907,24 +907,24 @@ "@bcoe/v8-coverage@^0.2.3": version "0.2.3" - resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" + resolved "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== "@colors/colors@1.6.0", "@colors/colors@^1.6.0": version "1.6.0" - resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.6.0.tgz#ec6cd237440700bc23ca23087f513c75508958b0" + resolved "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz" integrity sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA== "@cspotcode/source-map-support@^0.8.0": version "0.8.1" - resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1" + resolved "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz" integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw== dependencies: "@jridgewell/trace-mapping" "0.3.9" "@dabh/diagnostics@^2.0.2": version "2.0.3" - resolved "https://registry.yarnpkg.com/@dabh/diagnostics/-/diagnostics-2.0.3.tgz#7f7e97ee9a725dffc7808d93668cc984e1dc477a" + resolved "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz" integrity sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA== dependencies: colorspace "1.1.x" @@ -933,7 +933,7 @@ "@datadog/datadog-ci@2.48.0": version "2.48.0" - resolved "https://registry.yarnpkg.com/@datadog/datadog-ci/-/datadog-ci-2.48.0.tgz#89a7c25a68b7f67e699b3eea3edda2d34c5ecb82" + resolved "https://registry.npmjs.org/@datadog/datadog-ci/-/datadog-ci-2.48.0.tgz" integrity sha512-Xseeb1Fu0CBl2SpbYzTiqok9bs5bKSY9ZAGDuDcWTjSI3jpCuDt4BiIxE/HOMzV4t+S6KKjCABhquahXo/u0TA== dependencies: "@aws-sdk/client-cloudwatch-logs" "^3.624.0" @@ -986,7 +986,7 @@ "@fimbul/bifrost@^0.21.0": version "0.21.0" - resolved "https://registry.yarnpkg.com/@fimbul/bifrost/-/bifrost-0.21.0.tgz#d0fafa25938fda475657a6a1e407a21bbe02c74e" + resolved "https://registry.npmjs.org/@fimbul/bifrost/-/bifrost-0.21.0.tgz" integrity sha512-ou8VU+nTmOW1jeg+FT+sn+an/M0Xb9G16RucrfhjXGWv1Q97kCoM5CG9Qj7GYOSdu7km72k7nY83Eyr53Bkakg== dependencies: "@fimbul/ymir" "^0.21.0" @@ -996,7 +996,7 @@ "@fimbul/ymir@^0.21.0": version "0.21.0" - resolved "https://registry.yarnpkg.com/@fimbul/ymir/-/ymir-0.21.0.tgz#8525726787aceeafd4e199472c0d795160b5d4a1" + resolved "https://registry.npmjs.org/@fimbul/ymir/-/ymir-0.21.0.tgz" integrity sha512-T/y7WqPsm4n3zhT08EpB5sfdm2Kvw3gurAxr2Lr5dQeLi8ZsMlNT/Jby+ZmuuAAd1PnXYzKp+2SXgIkQIIMCUg== dependencies: inversify "^5.0.0" @@ -1005,7 +1005,7 @@ "@google-cloud/common@^5.0.0": version "5.0.2" - resolved "https://registry.yarnpkg.com/@google-cloud/common/-/common-5.0.2.tgz#423ad94b125d44263cbed2b5eb1ce1d4d53dc038" + resolved "https://registry.npmjs.org/@google-cloud/common/-/common-5.0.2.tgz" integrity sha512-V7bmBKYQyu0eVG2BFejuUjlBt+zrya6vtsKdY+JxMM/dNntPF41vZ9+LhOshEUH01zOHEqBSvI7Dad7ZS6aUeA== dependencies: "@google-cloud/projectify" "^4.0.0" @@ -1020,7 +1020,7 @@ "@google-cloud/logging@^11.1.0": version "11.2.0" - resolved "https://registry.yarnpkg.com/@google-cloud/logging/-/logging-11.2.0.tgz#d282f136467cc4500932f1fa6d4fe58325eb2db2" + resolved "https://registry.npmjs.org/@google-cloud/logging/-/logging-11.2.0.tgz" integrity sha512-Ma94jvuoMpbgNniwtelOt8w82hxK62FuOXZonEv0Hyk3B+/YVuLG/SWNyY9yMso/RXnPEc1fP2qo9kDrjf/b2w== dependencies: "@google-cloud/common" "^5.0.0" @@ -1042,7 +1042,7 @@ "@google-cloud/paginator@^5.0.0": version "5.0.2" - resolved "https://registry.yarnpkg.com/@google-cloud/paginator/-/paginator-5.0.2.tgz#86ad773266ce9f3b82955a8f75e22cd012ccc889" + resolved "https://registry.npmjs.org/@google-cloud/paginator/-/paginator-5.0.2.tgz" integrity sha512-DJS3s0OVH4zFDB1PzjxAsHqJT6sKVbRwwML0ZBP9PbU7Yebtu/7SWMRzvO2J3nUi9pRNITCfu4LJeooM2w4pjg== dependencies: arrify "^2.0.0" @@ -1050,24 +1050,24 @@ "@google-cloud/projectify@^4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@google-cloud/projectify/-/projectify-4.0.0.tgz#d600e0433daf51b88c1fa95ac7f02e38e80a07be" + resolved "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-4.0.0.tgz" integrity sha512-MmaX6HeSvyPbWGwFq7mXdo0uQZLGBYCwziiLIGq5JVX+/bdI3SAq6bP98trV5eTWfLuvsMcIC1YJOF2vfteLFA== "@google-cloud/promisify@^4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@google-cloud/promisify/-/promisify-4.0.0.tgz#a906e533ebdd0f754dca2509933334ce58b8c8b1" + resolved "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-4.0.0.tgz" integrity sha512-Orxzlfb9c67A15cq2JQEyVc7wEsmFBmHjZWZYQMUyJ1qivXyMwdyNOs9odi79hze+2zqdTtu1E19IM/FtqZ10g== "@google-cloud/run@^1.4.0": version "1.5.0" - resolved "https://registry.yarnpkg.com/@google-cloud/run/-/run-1.5.0.tgz#95e16c9a8527083b193c3722c8063d3a25cafeed" + resolved "https://registry.npmjs.org/@google-cloud/run/-/run-1.5.0.tgz" integrity sha512-Ct0ZIuicd2O6fJHv7Lbwl4CcCWKK7NjACvUc4pOJVbKo75B5proOa7/9TrXVpI6oWu1n7EFx1s8xsavYHLxRAg== dependencies: google-gax "^4.0.3" "@grpc/grpc-js@^1.10.9": version "1.12.5" - resolved "https://registry.yarnpkg.com/@grpc/grpc-js/-/grpc-js-1.12.5.tgz#0064a28fe9b1ec54ac27e1c9bf70720aa01285e8" + resolved "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.12.5.tgz" integrity sha512-d3iiHxdpg5+ZcJ6jnDSOT8Z0O0VMVGy34jAnYLUX8yd36b1qn8f1TwOA/Lc7TsOh03IkPJ38eGI5qD2EjNkoEA== dependencies: "@grpc/proto-loader" "^0.7.13" @@ -1075,7 +1075,7 @@ "@grpc/proto-loader@^0.7.13": version "0.7.13" - resolved "https://registry.yarnpkg.com/@grpc/proto-loader/-/proto-loader-0.7.13.tgz#f6a44b2b7c9f7b609f5748c6eac2d420e37670cf" + resolved "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.13.tgz" integrity sha512-AiXO/bfe9bmxBjxxtYxFAXGZvMaN5s8kO+jBHAJCON8rJoB5YS/D6X7ZNc6XQkuHNmyl4CYaMI1fJ/Gn27RGGw== dependencies: lodash.camelcase "^4.3.0" @@ -1085,7 +1085,7 @@ "@istanbuljs/load-nyc-config@^1.0.0": version "1.1.0" - resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" + resolved "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz" integrity sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ== dependencies: camelcase "^5.3.1" @@ -1096,12 +1096,12 @@ "@istanbuljs/schema@^0.1.2", "@istanbuljs/schema@^0.1.3": version "0.1.3" - resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" + resolved "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz" integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== "@jest/console@^29.7.0": version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/console/-/console-29.7.0.tgz#cd4822dbdb84529265c5a2bdb529a3c9cc950ffc" + resolved "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz" integrity sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg== dependencies: "@jest/types" "^29.6.3" @@ -1113,7 +1113,7 @@ "@jest/core@^29.7.0": version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/core/-/core-29.7.0.tgz#b6cccc239f30ff36609658c5a5e2291757ce448f" + resolved "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz" integrity sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg== dependencies: "@jest/console" "^29.7.0" @@ -1147,7 +1147,7 @@ "@jest/environment@^29.7.0": version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-29.7.0.tgz#24d61f54ff1f786f3cd4073b4b94416383baf2a7" + resolved "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz" integrity sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw== dependencies: "@jest/fake-timers" "^29.7.0" @@ -1157,14 +1157,14 @@ "@jest/expect-utils@^29.7.0": version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.7.0.tgz#023efe5d26a8a70f21677d0a1afc0f0a44e3a1c6" + resolved "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz" integrity sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA== dependencies: jest-get-type "^29.6.3" "@jest/expect@^29.7.0": version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-29.7.0.tgz#76a3edb0cb753b70dfbfe23283510d3d45432bf2" + resolved "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz" integrity sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ== dependencies: expect "^29.7.0" @@ -1172,7 +1172,7 @@ "@jest/fake-timers@^29.7.0": version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-29.7.0.tgz#fd91bf1fffb16d7d0d24a426ab1a47a49881a565" + resolved "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz" integrity sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ== dependencies: "@jest/types" "^29.6.3" @@ -1184,7 +1184,7 @@ "@jest/globals@^29.7.0": version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-29.7.0.tgz#8d9290f9ec47ff772607fa864ca1d5a2efae1d4d" + resolved "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz" integrity sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ== dependencies: "@jest/environment" "^29.7.0" @@ -1194,7 +1194,7 @@ "@jest/reporters@^29.7.0": version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-29.7.0.tgz#04b262ecb3b8faa83b0b3d321623972393e8f4c7" + resolved "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz" integrity sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg== dependencies: "@bcoe/v8-coverage" "^0.2.3" @@ -1224,14 +1224,14 @@ "@jest/schemas@^29.6.3": version "29.6.3" - resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.6.3.tgz#430b5ce8a4e0044a7e3819663305a7b3091c8e03" + resolved "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz" integrity sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA== dependencies: "@sinclair/typebox" "^0.27.8" "@jest/source-map@^29.6.3": version "29.6.3" - resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-29.6.3.tgz#d90ba772095cf37a34a5eb9413f1b562a08554c4" + resolved "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz" integrity sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw== dependencies: "@jridgewell/trace-mapping" "^0.3.18" @@ -1240,7 +1240,7 @@ "@jest/test-result@^29.7.0": version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-29.7.0.tgz#8db9a80aa1a097bb2262572686734baed9b1657c" + resolved "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz" integrity sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA== dependencies: "@jest/console" "^29.7.0" @@ -1250,7 +1250,7 @@ "@jest/test-sequencer@^29.7.0": version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz#6cef977ce1d39834a3aea887a1726628a6f072ce" + resolved "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz" integrity sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw== dependencies: "@jest/test-result" "^29.7.0" @@ -1260,7 +1260,7 @@ "@jest/transform@^29.7.0": version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-29.7.0.tgz#df2dd9c346c7d7768b8a06639994640c642e284c" + resolved "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz" integrity sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw== dependencies: "@babel/core" "^7.11.6" @@ -1281,7 +1281,7 @@ "@jest/types@^29.6.3": version "29.6.3" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.6.3.tgz#1131f8cf634e7e84c5e77bab12f052af585fba59" + resolved "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz" integrity sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw== dependencies: "@jest/schemas" "^29.6.3" @@ -1293,7 +1293,7 @@ "@jridgewell/gen-mapping@^0.3.5": version "0.3.8" - resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz#4f0e06362e01362f823d348f1872b08f666d8142" + resolved "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz" integrity sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA== dependencies: "@jridgewell/set-array" "^1.2.1" @@ -1302,22 +1302,22 @@ "@jridgewell/resolve-uri@^3.0.3", "@jridgewell/resolve-uri@^3.1.0": version "3.1.2" - resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" + resolved "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz" integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== "@jridgewell/set-array@^1.2.1": version "1.2.1" - resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.2.1.tgz#558fb6472ed16a4c850b889530e6b36438c49280" + resolved "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz" integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A== "@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14": version "1.5.0" - resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a" + resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz" integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ== "@jridgewell/trace-mapping@0.3.9": version "0.3.9" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9" + resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz" integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ== dependencies: "@jridgewell/resolve-uri" "^3.0.3" @@ -1325,7 +1325,7 @@ "@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": version "0.3.25" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0" + resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz" integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== dependencies: "@jridgewell/resolve-uri" "^3.1.0" @@ -1333,49 +1333,49 @@ "@js-sdsl/ordered-map@^4.4.2": version "4.4.2" - resolved "https://registry.yarnpkg.com/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz#9299f82874bab9e4c7f9c48d865becbfe8d6907c" + resolved "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz" integrity sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw== "@kwsites/file-exists@^1.1.1": version "1.1.1" - resolved "https://registry.yarnpkg.com/@kwsites/file-exists/-/file-exists-1.1.1.tgz#ad1efcac13e1987d8dbaf235ef3be5b0d96faa99" + resolved "https://registry.npmjs.org/@kwsites/file-exists/-/file-exists-1.1.1.tgz" integrity sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw== dependencies: debug "^4.1.1" "@kwsites/promise-deferred@^1.1.1": version "1.1.1" - resolved "https://registry.yarnpkg.com/@kwsites/promise-deferred/-/promise-deferred-1.1.1.tgz#8ace5259254426ccef57f3175bc64ed7095ed919" + resolved "https://registry.npmjs.org/@kwsites/promise-deferred/-/promise-deferred-1.1.1.tgz" integrity sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw== "@opentelemetry/api@^1.7.0": version "1.9.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.9.0.tgz#d03eba68273dc0f7509e2a3d5cba21eae10379fe" + resolved "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz" integrity sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg== "@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": version "1.1.2" - resolved "https://registry.yarnpkg.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf" + resolved "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz" integrity sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ== "@protobufjs/base64@^1.1.2": version "1.1.2" - resolved "https://registry.yarnpkg.com/@protobufjs/base64/-/base64-1.1.2.tgz#4c85730e59b9a1f1f349047dbf24296034bb2735" + resolved "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz" integrity sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg== "@protobufjs/codegen@^2.0.4": version "2.0.4" - resolved "https://registry.yarnpkg.com/@protobufjs/codegen/-/codegen-2.0.4.tgz#7ef37f0d010fb028ad1ad59722e506d9262815cb" + resolved "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz" integrity sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg== "@protobufjs/eventemitter@^1.1.0": version "1.1.0" - resolved "https://registry.yarnpkg.com/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz#355cbc98bafad5978f9ed095f397621f1d066b70" + resolved "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz" integrity sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q== "@protobufjs/fetch@^1.1.0": version "1.1.0" - resolved "https://registry.yarnpkg.com/@protobufjs/fetch/-/fetch-1.1.0.tgz#ba99fb598614af65700c1619ff06d454b0d84c45" + resolved "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz" integrity sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ== dependencies: "@protobufjs/aspromise" "^1.1.1" @@ -1383,51 +1383,51 @@ "@protobufjs/float@^1.0.2": version "1.0.2" - resolved "https://registry.yarnpkg.com/@protobufjs/float/-/float-1.0.2.tgz#5e9e1abdcb73fc0a7cb8b291df78c8cbd97b87d1" + resolved "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz" integrity sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ== "@protobufjs/inquire@^1.1.0": version "1.1.0" - resolved "https://registry.yarnpkg.com/@protobufjs/inquire/-/inquire-1.1.0.tgz#ff200e3e7cf2429e2dcafc1140828e8cc638f089" + resolved "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz" integrity sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q== "@protobufjs/path@^1.1.2": version "1.1.2" - resolved "https://registry.yarnpkg.com/@protobufjs/path/-/path-1.1.2.tgz#6cc2b20c5c9ad6ad0dccfd21ca7673d8d7fbf68d" + resolved "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz" integrity sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA== "@protobufjs/pool@^1.1.0": version "1.1.0" - resolved "https://registry.yarnpkg.com/@protobufjs/pool/-/pool-1.1.0.tgz#09fd15f2d6d3abfa9b65bc366506d6ad7846ff54" + resolved "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz" integrity sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw== "@protobufjs/utf8@^1.1.0": version "1.1.0" - resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570" + resolved "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz" integrity sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw== "@sinclair/typebox@^0.27.8": version "0.27.8" - resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" + resolved "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz" integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA== "@sinonjs/commons@^3.0.0": version "3.0.1" - resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-3.0.1.tgz#1029357e44ca901a615585f6d27738dbc89084cd" + resolved "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz" integrity sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ== dependencies: type-detect "4.0.8" "@sinonjs/fake-timers@^10.0.2": version "10.3.0" - resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz#55fdff1ecab9f354019129daf4df0dd4d923ea66" + resolved "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz" integrity sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA== dependencies: "@sinonjs/commons" "^3.0.0" "@smithy/abort-controller@^4.0.1": version "4.0.1" - resolved "https://registry.yarnpkg.com/@smithy/abort-controller/-/abort-controller-4.0.1.tgz#7c5e73690c4105ad264c2896bd1ea822450c3819" + resolved "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.0.1.tgz" integrity sha512-fiUIYgIgRjMWznk6iLJz35K2YxSLHzLBA/RC6lBrKfQ8fHbPfvk7Pk9UvpKoHgJjI18MnbPuEju53zcVy6KF1g== dependencies: "@smithy/types" "^4.1.0" @@ -1435,7 +1435,7 @@ "@smithy/config-resolver@^4.0.0", "@smithy/config-resolver@^4.0.1": version "4.0.1" - resolved "https://registry.yarnpkg.com/@smithy/config-resolver/-/config-resolver-4.0.1.tgz#3d6c78bbc51adf99c9819bb3f0ea197fe03ad363" + resolved "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.0.1.tgz" integrity sha512-Igfg8lKu3dRVkTSEm98QpZUvKEOa71jDX4vKRcvJVyRc3UgN3j7vFMf0s7xLQhYmKa8kyJGQgUJDOV5V3neVlQ== dependencies: "@smithy/node-config-provider" "^4.0.1" @@ -1446,7 +1446,7 @@ "@smithy/core@^3.0.0", "@smithy/core@^3.1.1": version "3.1.1" - resolved "https://registry.yarnpkg.com/@smithy/core/-/core-3.1.1.tgz#e82e526ba2dbec8e740a86c5c14b97a46e5a5128" + resolved "https://registry.npmjs.org/@smithy/core/-/core-3.1.1.tgz" integrity sha512-hhUZlBWYuh9t6ycAcN90XOyG76C1AzwxZZgaCVPMYpWqqk9uMFo7HGG5Zu2cEhCJn7DdOi5krBmlibWWWPgdsw== dependencies: "@smithy/middleware-serde" "^4.0.1" @@ -1460,7 +1460,7 @@ "@smithy/credential-provider-imds@^4.0.0", "@smithy/credential-provider-imds@^4.0.1": version "4.0.1" - resolved "https://registry.yarnpkg.com/@smithy/credential-provider-imds/-/credential-provider-imds-4.0.1.tgz#807110739982acd1588a4847b61e6edf196d004e" + resolved "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.0.1.tgz" integrity sha512-l/qdInaDq1Zpznpmev/+52QomsJNZ3JkTl5yrTl02V6NBgJOQ4LY0SFw/8zsMwj3tLe8vqiIuwF6nxaEwgf6mg== dependencies: "@smithy/node-config-provider" "^4.0.1" @@ -1471,7 +1471,7 @@ "@smithy/eventstream-codec@^4.0.1": version "4.0.1" - resolved "https://registry.yarnpkg.com/@smithy/eventstream-codec/-/eventstream-codec-4.0.1.tgz#8e0beae84013eb3b497dd189470a44bac4411bae" + resolved "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-4.0.1.tgz" integrity sha512-Q2bCAAR6zXNVtJgifsU16ZjKGqdw/DyecKNgIgi7dlqw04fqDu0mnq+JmGphqheypVc64CYq3azSuCpAdFk2+A== dependencies: "@aws-crypto/crc32" "5.2.0" @@ -1481,7 +1481,7 @@ "@smithy/eventstream-serde-browser@^4.0.0": version "4.0.1" - resolved "https://registry.yarnpkg.com/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-4.0.1.tgz#cdbbb18b9371da363eff312d78a10f6bad82df28" + resolved "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-4.0.1.tgz" integrity sha512-HbIybmz5rhNg+zxKiyVAnvdM3vkzjE6ccrJ620iPL8IXcJEntd3hnBl+ktMwIy12Te/kyrSbUb8UCdnUT4QEdA== dependencies: "@smithy/eventstream-serde-universal" "^4.0.1" @@ -1490,7 +1490,7 @@ "@smithy/eventstream-serde-config-resolver@^4.0.0": version "4.0.1" - resolved "https://registry.yarnpkg.com/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-4.0.1.tgz#3662587f507ad7fac5bd4505c4ed6ed0ac49a010" + resolved "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-4.0.1.tgz" integrity sha512-lSipaiq3rmHguHa3QFF4YcCM3VJOrY9oq2sow3qlhFY+nBSTF/nrO82MUQRPrxHQXA58J5G1UnU2WuJfi465BA== dependencies: "@smithy/types" "^4.1.0" @@ -1498,7 +1498,7 @@ "@smithy/eventstream-serde-node@^4.0.0": version "4.0.1" - resolved "https://registry.yarnpkg.com/@smithy/eventstream-serde-node/-/eventstream-serde-node-4.0.1.tgz#3799c33e0148d2b923a66577d1dbc590865742ce" + resolved "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-4.0.1.tgz" integrity sha512-o4CoOI6oYGYJ4zXo34U8X9szDe3oGjmHgsMGiZM0j4vtNoT+h80TLnkUcrLZR3+E6HIxqW+G+9WHAVfl0GXK0Q== dependencies: "@smithy/eventstream-serde-universal" "^4.0.1" @@ -1507,7 +1507,7 @@ "@smithy/eventstream-serde-universal@^4.0.1": version "4.0.1" - resolved "https://registry.yarnpkg.com/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-4.0.1.tgz#ddb2ab9f62b8ab60f50acd5f7c8b3ac9d27468e2" + resolved "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-4.0.1.tgz" integrity sha512-Z94uZp0tGJuxds3iEAZBqGU2QiaBHP4YytLUjwZWx+oUeohCsLyUm33yp4MMBmhkuPqSbQCXq5hDet6JGUgHWA== dependencies: "@smithy/eventstream-codec" "^4.0.1" @@ -1516,7 +1516,7 @@ "@smithy/fetch-http-handler@^5.0.0", "@smithy/fetch-http-handler@^5.0.1": version "5.0.1" - resolved "https://registry.yarnpkg.com/@smithy/fetch-http-handler/-/fetch-http-handler-5.0.1.tgz#8463393442ca6a1644204849e42c386066f0df79" + resolved "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.0.1.tgz" integrity sha512-3aS+fP28urrMW2KTjb6z9iFow6jO8n3MFfineGbndvzGZit3taZhKWtTorf+Gp5RpFDDafeHlhfsGlDCXvUnJA== dependencies: "@smithy/protocol-http" "^5.0.1" @@ -1527,7 +1527,7 @@ "@smithy/hash-node@^4.0.0": version "4.0.1" - resolved "https://registry.yarnpkg.com/@smithy/hash-node/-/hash-node-4.0.1.tgz#ce78fc11b848a4f47c2e1e7a07fb6b982d2f130c" + resolved "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.0.1.tgz" integrity sha512-TJ6oZS+3r2Xu4emVse1YPB3Dq3d8RkZDKcPr71Nj/lJsdAP1c7oFzYqEn1IBc915TsgLl2xIJNuxCz+gLbLE0w== dependencies: "@smithy/types" "^4.1.0" @@ -1537,7 +1537,7 @@ "@smithy/invalid-dependency@^4.0.0": version "4.0.1" - resolved "https://registry.yarnpkg.com/@smithy/invalid-dependency/-/invalid-dependency-4.0.1.tgz#704d1acb6fac105558c17d53f6d55da6b0d6b6fc" + resolved "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.0.1.tgz" integrity sha512-gdudFPf4QRQ5pzj7HEnu6FhKRi61BfH/Gk5Yf6O0KiSbr1LlVhgjThcvjdu658VE6Nve8vaIWB8/fodmS1rBPQ== dependencies: "@smithy/types" "^4.1.0" @@ -1545,21 +1545,21 @@ "@smithy/is-array-buffer@^2.2.0": version "2.2.0" - resolved "https://registry.yarnpkg.com/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz#f84f0d9f9a36601a9ca9381688bd1b726fd39111" + resolved "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz" integrity sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA== dependencies: tslib "^2.6.2" "@smithy/is-array-buffer@^4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@smithy/is-array-buffer/-/is-array-buffer-4.0.0.tgz#55a939029321fec462bcc574890075cd63e94206" + resolved "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-4.0.0.tgz" integrity sha512-saYhF8ZZNoJDTvJBEWgeBccCg+yvp1CX+ed12yORU3NilJScfc6gfch2oVb4QgxZrGUx3/ZJlb+c/dJbyupxlw== dependencies: tslib "^2.6.2" "@smithy/middleware-content-length@^4.0.0": version "4.0.1" - resolved "https://registry.yarnpkg.com/@smithy/middleware-content-length/-/middleware-content-length-4.0.1.tgz#378bc94ae623f45e412fb4f164b5bb90b9de2ba3" + resolved "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.0.1.tgz" integrity sha512-OGXo7w5EkB5pPiac7KNzVtfCW2vKBTZNuCctn++TTSOMpe6RZO/n6WEC1AxJINn3+vWLKW49uad3lo/u0WJ9oQ== dependencies: "@smithy/protocol-http" "^5.0.1" @@ -1568,7 +1568,7 @@ "@smithy/middleware-endpoint@^4.0.0", "@smithy/middleware-endpoint@^4.0.2": version "4.0.2" - resolved "https://registry.yarnpkg.com/@smithy/middleware-endpoint/-/middleware-endpoint-4.0.2.tgz#f433dcd214e89f17bdf21b3af5fbdd3810bebf6d" + resolved "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.0.2.tgz" integrity sha512-Z9m67CXizGpj8CF/AW/7uHqYNh1VXXOn9Ap54fenWsCa0HnT4cJuE61zqG3cBkTZJDCy0wHJphilI41co/PE5g== dependencies: "@smithy/core" "^3.1.1" @@ -1582,7 +1582,7 @@ "@smithy/middleware-retry@^4.0.0": version "4.0.3" - resolved "https://registry.yarnpkg.com/@smithy/middleware-retry/-/middleware-retry-4.0.3.tgz#4073369e54c1beb7a764633ca218a6e39b9da688" + resolved "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.0.3.tgz" integrity sha512-TiKwwQTwUDeDtwWW8UWURTqu7s6F3wN2pmziLU215u7bqpVT9Mk2oEvURjpRLA+5XeQhM68R5BpAGzVtomsqgA== dependencies: "@smithy/node-config-provider" "^4.0.1" @@ -1597,7 +1597,7 @@ "@smithy/middleware-serde@^4.0.0", "@smithy/middleware-serde@^4.0.1": version "4.0.1" - resolved "https://registry.yarnpkg.com/@smithy/middleware-serde/-/middleware-serde-4.0.1.tgz#4c9218cecd5316ab696e73fdc1c80b38bcaffa99" + resolved "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.0.1.tgz" integrity sha512-Fh0E2SOF+S+P1+CsgKyiBInAt3o2b6Qk7YOp2W0Qx2XnfTdfMuSDKUEcnrtpxCzgKJnqXeLUZYqtThaP0VGqtA== dependencies: "@smithy/types" "^4.1.0" @@ -1605,7 +1605,7 @@ "@smithy/middleware-stack@^4.0.0", "@smithy/middleware-stack@^4.0.1": version "4.0.1" - resolved "https://registry.yarnpkg.com/@smithy/middleware-stack/-/middleware-stack-4.0.1.tgz#c157653f9df07f7c26e32f49994d368e4e071d22" + resolved "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.0.1.tgz" integrity sha512-dHwDmrtR/ln8UTHpaIavRSzeIk5+YZTBtLnKwDW3G2t6nAupCiQUvNzNoHBpik63fwUaJPtlnMzXbQrNFWssIA== dependencies: "@smithy/types" "^4.1.0" @@ -1613,7 +1613,7 @@ "@smithy/node-config-provider@^4.0.0", "@smithy/node-config-provider@^4.0.1": version "4.0.1" - resolved "https://registry.yarnpkg.com/@smithy/node-config-provider/-/node-config-provider-4.0.1.tgz#4e84fe665c0774d5f4ebb75144994fc6ebedf86e" + resolved "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.0.1.tgz" integrity sha512-8mRTjvCtVET8+rxvmzRNRR0hH2JjV0DFOmwXPrISmTIJEfnCBugpYYGAsCj8t41qd+RB5gbheSQ/6aKZCQvFLQ== dependencies: "@smithy/property-provider" "^4.0.1" @@ -1623,7 +1623,7 @@ "@smithy/node-http-handler@^4.0.0", "@smithy/node-http-handler@^4.0.2": version "4.0.2" - resolved "https://registry.yarnpkg.com/@smithy/node-http-handler/-/node-http-handler-4.0.2.tgz#48d47a046cf900ab86bfbe7f5fd078b52c82fab6" + resolved "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.0.2.tgz" integrity sha512-X66H9aah9hisLLSnGuzRYba6vckuFtGE+a5DcHLliI/YlqKrGoxhisD5XbX44KyoeRzoNlGr94eTsMVHFAzPOw== dependencies: "@smithy/abort-controller" "^4.0.1" @@ -1634,7 +1634,7 @@ "@smithy/property-provider@^2.0.12": version "2.2.0" - resolved "https://registry.yarnpkg.com/@smithy/property-provider/-/property-provider-2.2.0.tgz#37e3525a3fa3e11749f86a4f89f0fd7765a6edb0" + resolved "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-2.2.0.tgz" integrity sha512-+xiil2lFhtTRzXkx8F053AV46QnIw6e7MV8od5Mi68E1ICOjCeCHw2XfLnDEUHnT9WGUIkwcqavXjfwuJbGlpg== dependencies: "@smithy/types" "^2.12.0" @@ -1642,7 +1642,7 @@ "@smithy/property-provider@^4.0.0", "@smithy/property-provider@^4.0.1": version "4.0.1" - resolved "https://registry.yarnpkg.com/@smithy/property-provider/-/property-provider-4.0.1.tgz#8d35d5997af2a17cf15c5e921201ef6c5e3fc870" + resolved "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.0.1.tgz" integrity sha512-o+VRiwC2cgmk/WFV0jaETGOtX16VNPp2bSQEzu0whbReqE1BMqsP2ami2Vi3cbGVdKu1kq9gQkDAGKbt0WOHAQ== dependencies: "@smithy/types" "^4.1.0" @@ -1650,7 +1650,7 @@ "@smithy/protocol-http@^5.0.0", "@smithy/protocol-http@^5.0.1": version "5.0.1" - resolved "https://registry.yarnpkg.com/@smithy/protocol-http/-/protocol-http-5.0.1.tgz#37c248117b29c057a9adfad4eb1d822a67079ff1" + resolved "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.0.1.tgz" integrity sha512-TE4cpj49jJNB/oHyh/cRVEgNZaoPaxd4vteJNB0yGidOCVR0jCw/hjPVsT8Q8FRmj8Bd3bFZt8Dh7xGCT+xMBQ== dependencies: "@smithy/types" "^4.1.0" @@ -1658,7 +1658,7 @@ "@smithy/querystring-builder@^4.0.1": version "4.0.1" - resolved "https://registry.yarnpkg.com/@smithy/querystring-builder/-/querystring-builder-4.0.1.tgz#37e1e05d0d33c6f694088abc3e04eafb65cb6976" + resolved "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.0.1.tgz" integrity sha512-wU87iWZoCbcqrwszsOewEIuq+SU2mSoBE2CcsLwE0I19m0B2gOJr1MVjxWcDQYOzHbR1xCk7AcOBbGFUYOKvdg== dependencies: "@smithy/types" "^4.1.0" @@ -1667,7 +1667,7 @@ "@smithy/querystring-parser@^4.0.1": version "4.0.1" - resolved "https://registry.yarnpkg.com/@smithy/querystring-parser/-/querystring-parser-4.0.1.tgz#312dc62b146f8bb8a67558d82d4722bb9211af42" + resolved "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.0.1.tgz" integrity sha512-Ma2XC7VS9aV77+clSFylVUnPZRindhB7BbmYiNOdr+CHt/kZNJoPP0cd3QxCnCFyPXC4eybmyE98phEHkqZ5Jw== dependencies: "@smithy/types" "^4.1.0" @@ -1675,21 +1675,21 @@ "@smithy/service-error-classification@^2.1.5": version "2.1.5" - resolved "https://registry.yarnpkg.com/@smithy/service-error-classification/-/service-error-classification-2.1.5.tgz#0568a977cc0db36299d8703a5d8609c1f600c005" + resolved "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-2.1.5.tgz" integrity sha512-uBDTIBBEdAQryvHdc5W8sS5YX7RQzF683XrHePVdFmAgKiMofU15FLSM0/HU03hKTnazdNRFa0YHS7+ArwoUSQ== dependencies: "@smithy/types" "^2.12.0" "@smithy/service-error-classification@^4.0.1": version "4.0.1" - resolved "https://registry.yarnpkg.com/@smithy/service-error-classification/-/service-error-classification-4.0.1.tgz#84e78579af46c7b79c900b6d6cc822c9465f3259" + resolved "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.0.1.tgz" integrity sha512-3JNjBfOWpj/mYfjXJHB4Txc/7E4LVq32bwzE7m28GN79+M1f76XHflUaSUkhOriprPDzev9cX/M+dEB80DNDKA== dependencies: "@smithy/types" "^4.1.0" "@smithy/shared-ini-file-loader@^4.0.0", "@smithy/shared-ini-file-loader@^4.0.1": version "4.0.1" - resolved "https://registry.yarnpkg.com/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.0.1.tgz#d35c21c29454ca4e58914a4afdde68d3b2def1ee" + resolved "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.0.1.tgz" integrity sha512-hC8F6qTBbuHRI/uqDgqqi6J0R4GtEZcgrZPhFQnMhfJs3MnUTGSnR1NSJCJs5VWlMydu0kJz15M640fJlRsIOw== dependencies: "@smithy/types" "^4.1.0" @@ -1697,7 +1697,7 @@ "@smithy/signature-v4@^5.0.0": version "5.0.1" - resolved "https://registry.yarnpkg.com/@smithy/signature-v4/-/signature-v4-5.0.1.tgz#f93401b176150286ba246681031b0503ec359270" + resolved "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.0.1.tgz" integrity sha512-nCe6fQ+ppm1bQuw5iKoeJ0MJfz2os7Ic3GBjOkLOPtavbD1ONoyE3ygjBfz2ythFWm4YnRm6OxW+8p/m9uCoIA== dependencies: "@smithy/is-array-buffer" "^4.0.0" @@ -1711,7 +1711,7 @@ "@smithy/smithy-client@^4.0.0", "@smithy/smithy-client@^4.1.2": version "4.1.2" - resolved "https://registry.yarnpkg.com/@smithy/smithy-client/-/smithy-client-4.1.2.tgz#1bf707d48998a559d3e91e30c20eec243e16d45b" + resolved "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.1.2.tgz" integrity sha512-0yApeHWBqocelHGK22UivZyShNxFbDNrgREBllGh5Ws0D0rg/yId/CJfeoKKpjbfY2ju8j6WgDUGZHYQmINZ5w== dependencies: "@smithy/core" "^3.1.1" @@ -1724,21 +1724,21 @@ "@smithy/types@^2.12.0": version "2.12.0" - resolved "https://registry.yarnpkg.com/@smithy/types/-/types-2.12.0.tgz#c44845f8ba07e5e8c88eda5aed7e6a0c462da041" + resolved "https://registry.npmjs.org/@smithy/types/-/types-2.12.0.tgz" integrity sha512-QwYgloJ0sVNBeBuBs65cIkTbfzV/Q6ZNPCJ99EICFEdJYG50nGIY/uYXp+TbsdJReIuPr0a0kXmCvren3MbRRw== dependencies: tslib "^2.6.2" "@smithy/types@^4.0.0", "@smithy/types@^4.1.0": version "4.1.0" - resolved "https://registry.yarnpkg.com/@smithy/types/-/types-4.1.0.tgz#19de0b6087bccdd4182a334eb5d3d2629699370f" + resolved "https://registry.npmjs.org/@smithy/types/-/types-4.1.0.tgz" integrity sha512-enhjdwp4D7CXmwLtD6zbcDMbo6/T6WtuuKCY49Xxc6OMOmUWlBEBDREsxxgV2LIdeQPW756+f97GzcgAwp3iLw== dependencies: tslib "^2.6.2" "@smithy/url-parser@^4.0.0", "@smithy/url-parser@^4.0.1": version "4.0.1" - resolved "https://registry.yarnpkg.com/@smithy/url-parser/-/url-parser-4.0.1.tgz#b47743f785f5b8d81324878cbb1b5f834bf8d85a" + resolved "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.0.1.tgz" integrity sha512-gPXcIEUtw7VlK8f/QcruNXm7q+T5hhvGu9tl63LsJPZ27exB6dtNwvh2HIi0v7JcXJ5emBxB+CJxwaLEdJfA+g== dependencies: "@smithy/querystring-parser" "^4.0.1" @@ -1747,7 +1747,7 @@ "@smithy/util-base64@^4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@smithy/util-base64/-/util-base64-4.0.0.tgz#8345f1b837e5f636e5f8470c4d1706ae0c6d0358" + resolved "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-4.0.0.tgz" integrity sha512-CvHfCmO2mchox9kjrtzoHkWHxjHZzaFojLc8quxXY7WAAMAg43nuxwv95tATVgQFNDwd4M9S1qFzj40Ul41Kmg== dependencies: "@smithy/util-buffer-from" "^4.0.0" @@ -1756,21 +1756,21 @@ "@smithy/util-body-length-browser@^4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@smithy/util-body-length-browser/-/util-body-length-browser-4.0.0.tgz#965d19109a4b1e5fe7a43f813522cce718036ded" + resolved "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-4.0.0.tgz" integrity sha512-sNi3DL0/k64/LO3A256M+m3CDdG6V7WKWHdAiBBMUN8S3hK3aMPhwnPik2A/a2ONN+9doY9UxaLfgqsIRg69QA== dependencies: tslib "^2.6.2" "@smithy/util-body-length-node@^4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@smithy/util-body-length-node/-/util-body-length-node-4.0.0.tgz#3db245f6844a9b1e218e30c93305bfe2ffa473b3" + resolved "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-4.0.0.tgz" integrity sha512-q0iDP3VsZzqJyje8xJWEJCNIu3lktUGVoSy1KB0UWym2CL1siV3artm+u1DFYTLejpsrdGyCSWBdGNjJzfDPjg== dependencies: tslib "^2.6.2" "@smithy/util-buffer-from@^2.2.0": version "2.2.0" - resolved "https://registry.yarnpkg.com/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz#6fc88585165ec73f8681d426d96de5d402021e4b" + resolved "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz" integrity sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA== dependencies: "@smithy/is-array-buffer" "^2.2.0" @@ -1778,7 +1778,7 @@ "@smithy/util-buffer-from@^4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@smithy/util-buffer-from/-/util-buffer-from-4.0.0.tgz#b23b7deb4f3923e84ef50c8b2c5863d0dbf6c0b9" + resolved "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-4.0.0.tgz" integrity sha512-9TOQ7781sZvddgO8nxueKi3+yGvkY35kotA0Y6BWRajAv8jjmigQ1sBwz0UX47pQMYXJPahSKEKYFgt+rXdcug== dependencies: "@smithy/is-array-buffer" "^4.0.0" @@ -1786,14 +1786,14 @@ "@smithy/util-config-provider@^4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@smithy/util-config-provider/-/util-config-provider-4.0.0.tgz#e0c7c8124c7fba0b696f78f0bd0ccb060997d45e" + resolved "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-4.0.0.tgz" integrity sha512-L1RBVzLyfE8OXH+1hsJ8p+acNUSirQnWQ6/EgpchV88G6zGBTDPdXiiExei6Z1wR2RxYvxY/XLw6AMNCCt8H3w== dependencies: tslib "^2.6.2" "@smithy/util-defaults-mode-browser@^4.0.0": version "4.0.3" - resolved "https://registry.yarnpkg.com/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.0.3.tgz#52a5a22e6a4eecbc0e2ebdeee0979081ec99667a" + resolved "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.0.3.tgz" integrity sha512-7c5SF1fVK0EOs+2EOf72/qF199zwJflU1d02AevwKbAUPUZyE9RUZiyJxeUmhVxfKDWdUKaaVojNiaDQgnHL9g== dependencies: "@smithy/property-provider" "^4.0.1" @@ -1804,7 +1804,7 @@ "@smithy/util-defaults-mode-node@^4.0.0": version "4.0.3" - resolved "https://registry.yarnpkg.com/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.0.3.tgz#2dc140363dc35366c21c93939f61e4514f9a2fa6" + resolved "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.0.3.tgz" integrity sha512-CVnD42qYD3JKgDlImZ9+On+MqJHzq9uJgPbMdeBE8c2x8VJ2kf2R3XO/yVFx+30ts5lD/GlL0eFIShY3x9ROgQ== dependencies: "@smithy/config-resolver" "^4.0.1" @@ -1817,7 +1817,7 @@ "@smithy/util-endpoints@^3.0.0": version "3.0.1" - resolved "https://registry.yarnpkg.com/@smithy/util-endpoints/-/util-endpoints-3.0.1.tgz#44ccbf1721447966f69496c9003b87daa8f61975" + resolved "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.0.1.tgz" integrity sha512-zVdUENQpdtn9jbpD9SCFK4+aSiavRb9BxEtw9ZGUR1TYo6bBHbIoi7VkrFQ0/RwZlzx0wRBaRmPclj8iAoJCLA== dependencies: "@smithy/node-config-provider" "^4.0.1" @@ -1826,14 +1826,14 @@ "@smithy/util-hex-encoding@^4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@smithy/util-hex-encoding/-/util-hex-encoding-4.0.0.tgz#dd449a6452cffb37c5b1807ec2525bb4be551e8d" + resolved "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-4.0.0.tgz" integrity sha512-Yk5mLhHtfIgW2W2WQZWSg5kuMZCVbvhFmC7rV4IO2QqnZdbEFPmQnCcGMAX2z/8Qj3B9hYYNjZOhWym+RwhePw== dependencies: tslib "^2.6.2" "@smithy/util-middleware@^4.0.0", "@smithy/util-middleware@^4.0.1": version "4.0.1" - resolved "https://registry.yarnpkg.com/@smithy/util-middleware/-/util-middleware-4.0.1.tgz#58d363dcd661219298c89fa176a28e98ccc4bf43" + resolved "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.0.1.tgz" integrity sha512-HiLAvlcqhbzhuiOa0Lyct5IIlyIz0PQO5dnMlmQ/ubYM46dPInB+3yQGkfxsk6Q24Y0n3/JmcA1v5iEhmOF5mA== dependencies: "@smithy/types" "^4.1.0" @@ -1841,7 +1841,7 @@ "@smithy/util-retry@^2.0.4": version "2.2.0" - resolved "https://registry.yarnpkg.com/@smithy/util-retry/-/util-retry-2.2.0.tgz#e8e019537ab47ba6b2e87e723ec51ee223422d85" + resolved "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-2.2.0.tgz" integrity sha512-q9+pAFPTfftHXRytmZ7GzLFFrEGavqapFc06XxzZFcSIGERXMerXxCitjOG1prVDR9QdjqotF40SWvbqcCpf8g== dependencies: "@smithy/service-error-classification" "^2.1.5" @@ -1850,7 +1850,7 @@ "@smithy/util-retry@^4.0.0", "@smithy/util-retry@^4.0.1": version "4.0.1" - resolved "https://registry.yarnpkg.com/@smithy/util-retry/-/util-retry-4.0.1.tgz#fb5f26492383dcb9a09cc4aee23a10f839cd0769" + resolved "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.0.1.tgz" integrity sha512-WmRHqNVwn3kI3rKk1LsKcVgPBG6iLTBGC1iYOV3GQegwJ3E8yjzHytPt26VNzOWr1qu0xE03nK0Ug8S7T7oufw== dependencies: "@smithy/service-error-classification" "^4.0.1" @@ -1859,7 +1859,7 @@ "@smithy/util-stream@^4.0.0", "@smithy/util-stream@^4.0.2": version "4.0.2" - resolved "https://registry.yarnpkg.com/@smithy/util-stream/-/util-stream-4.0.2.tgz#63495d3f7fba9d78748d540921136dc4a8d4c067" + resolved "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.0.2.tgz" integrity sha512-0eZ4G5fRzIoewtHtwaYyl8g2C+osYOT4KClXgfdNEDAgkbe2TYPqcnw4GAWabqkZCax2ihRGPe9LZnsPdIUIHA== dependencies: "@smithy/fetch-http-handler" "^5.0.1" @@ -1873,14 +1873,14 @@ "@smithy/util-uri-escape@^4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@smithy/util-uri-escape/-/util-uri-escape-4.0.0.tgz#a96c160c76f3552458a44d8081fade519d214737" + resolved "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-4.0.0.tgz" integrity sha512-77yfbCbQMtgtTylO9itEAdpPXSog3ZxMe09AEhm0dU0NLTalV70ghDZFR+Nfi1C60jnJoh/Re4090/DuZh2Omg== dependencies: tslib "^2.6.2" "@smithy/util-utf8@^2.0.0": version "2.3.0" - resolved "https://registry.yarnpkg.com/@smithy/util-utf8/-/util-utf8-2.3.0.tgz#dd96d7640363259924a214313c3cf16e7dd329c5" + resolved "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz" integrity sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A== dependencies: "@smithy/util-buffer-from" "^2.2.0" @@ -1888,7 +1888,7 @@ "@smithy/util-utf8@^4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@smithy/util-utf8/-/util-utf8-4.0.0.tgz#09ca2d9965e5849e72e347c130f2a29d5c0c863c" + resolved "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.0.0.tgz" integrity sha512-b+zebfKCfRdgNJDknHCob3O7FpeYQN6ZG6YLExMcasDHsCXlsXCEuiPZeLnJLpwa5dvPetGlnGCiMHuLwGvFow== dependencies: "@smithy/util-buffer-from" "^4.0.0" @@ -1896,7 +1896,7 @@ "@smithy/util-waiter@^4.0.0": version "4.0.2" - resolved "https://registry.yarnpkg.com/@smithy/util-waiter/-/util-waiter-4.0.2.tgz#0a73a0fcd30ea7bbc3009cf98ad199f51b8eac51" + resolved "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-4.0.2.tgz" integrity sha512-piUTHyp2Axx3p/kc2CIJkYSv0BAaheBQmbACZgQSSfWUumWNW+R1lL+H9PDBxKJkvOeEX+hKYEFiwO8xagL8AQ== dependencies: "@smithy/abort-controller" "^4.0.1" @@ -1905,44 +1905,44 @@ "@tootallnate/once@2": version "2.0.0" - resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf" + resolved "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz" integrity sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A== "@tootallnate/quickjs-emscripten@^0.23.0": version "0.23.0" - resolved "https://registry.yarnpkg.com/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz#db4ecfd499a9765ab24002c3b696d02e6d32a12c" + resolved "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz" integrity sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA== "@tsconfig/node10@^1.0.7": version "1.0.11" - resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.11.tgz#6ee46400685f130e278128c7b38b7e031ff5b2f2" + resolved "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz" integrity sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw== "@tsconfig/node12@^1.0.7": version "1.0.11" - resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.11.tgz#ee3def1f27d9ed66dac6e46a295cffb0152e058d" + resolved "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz" integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag== "@tsconfig/node14@^1.0.0": version "1.0.3" - resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.3.tgz#e4386316284f00b98435bf40f72f75a09dabf6c1" + resolved "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz" integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow== "@tsconfig/node16@^1.0.2": version "1.0.4" - resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.4.tgz#0b92dcc0cc1c81f6f306a381f28e31b1a56536e9" + resolved "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz" integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA== "@types/adm-zip@0.5.7": version "0.5.7" - resolved "https://registry.yarnpkg.com/@types/adm-zip/-/adm-zip-0.5.7.tgz#eec10b6f717d3948beb64aca0abebc4b344ac7e9" + resolved "https://registry.npmjs.org/@types/adm-zip/-/adm-zip-0.5.7.tgz" integrity sha512-DNEs/QvmyRLurdQPChqq0Md4zGvPwHerAJYWk9l2jCbD1VPpnzRJorOdiq4zsw09NFbYnhfsoEhWtxIzXpn2yw== dependencies: "@types/node" "*" "@types/babel__core@^7.1.14": version "7.20.5" - resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.5.tgz#3df15f27ba85319caa07ba08d0721889bb39c017" + resolved "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz" integrity sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA== dependencies: "@babel/parser" "^7.20.7" @@ -1953,14 +1953,14 @@ "@types/babel__generator@*": version "7.6.8" - resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.6.8.tgz#f836c61f48b1346e7d2b0d93c6dacc5b9535d3ab" + resolved "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz" integrity sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw== dependencies: "@babel/types" "^7.0.0" "@types/babel__template@*": version "7.4.4" - resolved "https://registry.yarnpkg.com/@types/babel__template/-/babel__template-7.4.4.tgz#5672513701c1b2199bc6dad636a9d7491586766f" + resolved "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz" integrity sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A== dependencies: "@babel/parser" "^7.1.0" @@ -1968,50 +1968,50 @@ "@types/babel__traverse@*", "@types/babel__traverse@^7.0.6": version "7.20.6" - resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.20.6.tgz#8dc9f0ae0f202c08d8d4dab648912c8d6038e3f7" + resolved "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.6.tgz" integrity sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg== dependencies: "@babel/types" "^7.20.7" "@types/caseless@*": version "0.12.5" - resolved "https://registry.yarnpkg.com/@types/caseless/-/caseless-0.12.5.tgz#db9468cb1b1b5a925b8f34822f1669df0c5472f5" + resolved "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.5.tgz" integrity sha512-hWtVTC2q7hc7xZ/RLbxapMvDMgUnDvKvMOpKal4DrMyfGBUfB1oKaZlIRr6mJL+If3bAP6sV/QneGzF6tJjZDg== "@types/datadog-metrics@0.6.1": version "0.6.1" - resolved "https://registry.yarnpkg.com/@types/datadog-metrics/-/datadog-metrics-0.6.1.tgz#9957ad75fe3a4742c87b94273e94c40228c8dd4c" + resolved "https://registry.npmjs.org/@types/datadog-metrics/-/datadog-metrics-0.6.1.tgz" integrity sha512-p6zVpfmNcXwtcXjgpz7do/fKyfndGhU5sGJVtb5Gn5PvLDiQUAgD0mI/itf/99sBi9DRxeyhFQ9dQF6OxxQNbA== "@types/graceful-fs@^4.1.3": version "4.1.9" - resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.9.tgz#2a06bc0f68a20ab37b3e36aa238be6abdf49e8b4" + resolved "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz" integrity sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ== dependencies: "@types/node" "*" "@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": version "2.0.6" - resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz#7739c232a1fee9b4d3ce8985f314c0c6d33549d7" + resolved "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz" integrity sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w== "@types/istanbul-lib-report@*": version "3.0.3" - resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz#53047614ae72e19fc0401d872de3ae2b4ce350bf" + resolved "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz" integrity sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA== dependencies: "@types/istanbul-lib-coverage" "*" "@types/istanbul-reports@^3.0.0": version "3.0.4" - resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz#0f03e3d2f670fbdac586e34b433783070cc16f54" + resolved "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz" integrity sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ== dependencies: "@types/istanbul-lib-report" "*" "@types/jest@29.5.14": version "29.5.14" - resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.14.tgz#2b910912fa1d6856cadcd0c1f95af7df1d6049e5" + resolved "https://registry.npmjs.org/@types/jest/-/jest-29.5.14.tgz" integrity sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ== dependencies: expect "^29.0.0" @@ -2019,26 +2019,26 @@ "@types/long@^4.0.0": version "4.0.2" - resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.2.tgz#b74129719fc8d11c01868010082d483b7545591a" + resolved "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz" integrity sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA== -"@types/node@*", "@types/node@>=13.7.0": - version "22.10.7" - resolved "https://registry.yarnpkg.com/@types/node/-/node-22.10.7.tgz#14a1ca33fd0ebdd9d63593ed8d3fbc882a6d28d7" - integrity sha512-V09KvXxFiutGp6B7XkpaDXlNadZxrzajcY50EuoLIpQ6WWYCSvf19lVIazzfIzQvhUN2HjX12spLojTnhuKlGg== - dependencies: - undici-types "~6.20.0" - -"@types/node@20.10.4": +"@types/node@*", "@types/node@20.10.4": version "20.10.4" - resolved "https://registry.yarnpkg.com/@types/node/-/node-20.10.4.tgz#b246fd84d55d5b1b71bf51f964bd514409347198" + resolved "https://registry.npmjs.org/@types/node/-/node-20.10.4.tgz" integrity sha512-D08YG6rr8X90YB56tSIuBaddy/UXAA9RKJoFvrsnogAum/0pmjkgi4+2nx96A330FmioegBWmEYQ+syqCFaveg== dependencies: undici-types "~5.26.4" +"@types/node@>=13.7.0": + version "22.10.7" + resolved "https://registry.npmjs.org/@types/node/-/node-22.10.7.tgz" + integrity sha512-V09KvXxFiutGp6B7XkpaDXlNadZxrzajcY50EuoLIpQ6WWYCSvf19lVIazzfIzQvhUN2HjX12spLojTnhuKlGg== + dependencies: + undici-types "~6.20.0" + "@types/request@^2.48.8": version "2.48.12" - resolved "https://registry.yarnpkg.com/@types/request/-/request-2.48.12.tgz#0f590f615a10f87da18e9790ac94c29ec4c5ef30" + resolved "https://registry.npmjs.org/@types/request/-/request-2.48.12.tgz" integrity sha512-G3sY+NpsA9jnwm0ixhAFQSJ3Q9JkpLZpJbI3GMv0mIAT0y3mRabYeINzal5WOChIiaTEGQYlHOKgkaM9EisWHw== dependencies: "@types/caseless" "*" @@ -2048,82 +2048,82 @@ "@types/stack-utils@^2.0.0": version "2.0.3" - resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.3.tgz#6209321eb2c1712a7e7466422b8cb1fc0d9dd5d8" + resolved "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz" integrity sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw== "@types/tough-cookie@*": version "4.0.5" - resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.5.tgz#cb6e2a691b70cb177c6e3ae9c1d2e8b2ea8cd304" + resolved "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz" integrity sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA== "@types/triple-beam@^1.3.2": version "1.3.5" - resolved "https://registry.yarnpkg.com/@types/triple-beam/-/triple-beam-1.3.5.tgz#74fef9ffbaa198eb8b588be029f38b00299caa2c" + resolved "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz" integrity sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw== "@types/uuid@^9.0.1": version "9.0.8" - resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-9.0.8.tgz#7545ba4fc3c003d6c756f651f3bf163d8f0f29ba" + resolved "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz" integrity sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA== "@types/yargs-parser@*": version "21.0.3" - resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.3.tgz#815e30b786d2e8f0dcd85fd5bcf5e1a04d008f15" + resolved "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz" integrity sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ== "@types/yargs@^17.0.8": version "17.0.33" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.33.tgz#8c32303da83eec050a84b3c7ae7b9f922d13e32d" + resolved "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz" integrity sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA== dependencies: "@types/yargs-parser" "*" abort-controller@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" + resolved "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz" integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg== dependencies: event-target-shim "^5.0.0" acorn-walk@^8.1.1: version "8.3.4" - resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.4.tgz#794dd169c3977edf4ba4ea47583587c5866236b7" + resolved "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz" integrity sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g== dependencies: acorn "^8.11.0" acorn@^8.11.0, acorn@^8.4.1: version "8.14.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.14.0.tgz#063e2c70cac5fb4f6467f0b11152e04c682795b0" + resolved "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz" integrity sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA== adm-zip@0.5.14: version "0.5.14" - resolved "https://registry.yarnpkg.com/adm-zip/-/adm-zip-0.5.14.tgz#2c557c0bf12af4311cf6d32970f4060cf8133b2a" + resolved "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.14.tgz" integrity sha512-DnyqqifT4Jrcvb8USYjp6FHtBpEIz1mnXu6pTRHZ0RL69LbQYiO+0lDFg5+OKA7U29oWSs3a/i8fhn8ZcceIWg== agent-base@6: version "6.0.2" - resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" + resolved "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz" integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== dependencies: debug "4" agent-base@^7.1.0, agent-base@^7.1.2: version "7.1.3" - resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.3.tgz#29435eb821bc4194633a5b89e5bc4703bafc25a1" + resolved "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz" integrity sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw== ajv-formats@^2.1.1: version "2.1.1" - resolved "https://registry.yarnpkg.com/ajv-formats/-/ajv-formats-2.1.1.tgz#6e669400659eb74973bbf2e33327180a0996b520" + resolved "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz" integrity sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA== dependencies: ajv "^8.0.0" ajv@^8.0.0, ajv@^8.12.0: version "8.17.1" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.17.1.tgz#37d9a5c776af6bc92d7f4f9510eba4c0a60d11a6" + resolved "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz" integrity sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g== dependencies: fast-deep-equal "^3.1.3" @@ -2133,55 +2133,55 @@ ajv@^8.0.0, ajv@^8.12.0: ansi-escapes@^4.2.1: version "4.3.2" - resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" + resolved "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz" integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== dependencies: type-fest "^0.21.3" ansi-escapes@^7.0.0: version "7.0.0" - resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-7.0.0.tgz#00fc19f491bbb18e1d481b97868204f92109bfe7" + resolved "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.0.0.tgz" integrity sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw== dependencies: environment "^1.0.0" ansi-regex@^5.0.1: version "5.0.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== ansi-regex@^6.0.1: version "6.1.0" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.1.0.tgz#95ec409c69619d6cb1b8b34f14b660ef28ebd654" + resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz" integrity sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA== ansi-styles@^3.2.1: version "3.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz" integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== dependencies: color-convert "^1.9.0" ansi-styles@^4.0.0, ansi-styles@^4.1.0: version "4.3.0" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz" integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== dependencies: color-convert "^2.0.1" ansi-styles@^5.0.0: version "5.2.0" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz" integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== ansi-styles@^6.0.0, ansi-styles@^6.2.1: version "6.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz" integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== anymatch@^3.0.3: version "3.1.3" - resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" + resolved "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz" integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== dependencies: normalize-path "^3.0.0" @@ -2189,60 +2189,60 @@ anymatch@^3.0.3: arg@^4.1.0: version "4.1.3" - resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" + resolved "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz" integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== argparse@^1.0.7: version "1.0.10" - resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + resolved "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz" integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== dependencies: sprintf-js "~1.0.2" arrify@^2.0.0, arrify@^2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/arrify/-/arrify-2.0.1.tgz#c9655e9331e0abcd588d2a7cad7e9956f66701fa" + resolved "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz" integrity sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug== asn1@^0.2.6, asn1@~0.2.0, asn1@~0.2.3: version "0.2.6" - resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.6.tgz#0d3a7bb6e64e02a90c0303b31f292868ea09a08d" + resolved "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz" integrity sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ== dependencies: safer-buffer "~2.1.0" assert-plus@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" + resolved "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz" integrity sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw== ast-types@^0.13.4: version "0.13.4" - resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.13.4.tgz#ee0d77b343263965ecc3fb62da16e7222b2b6782" + resolved "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz" integrity sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w== dependencies: tslib "^2.0.1" async-retry@1.3.1: version "1.3.1" - resolved "https://registry.yarnpkg.com/async-retry/-/async-retry-1.3.1.tgz#139f31f8ddce50c0870b0ba558a6079684aaed55" + resolved "https://registry.npmjs.org/async-retry/-/async-retry-1.3.1.tgz" integrity sha512-aiieFW/7h3hY0Bq5d+ktDBejxuwR78vRu9hDUdR8rNhSaQ29VzPL4AoIRG7D/c7tdenwOcKvgPM6tIxB3cB6HA== dependencies: retry "0.12.0" async@^3.2.3: version "3.2.6" - resolved "https://registry.yarnpkg.com/async/-/async-3.2.6.tgz#1b0728e14929d51b85b449b7f06e27c1145e38ce" + resolved "https://registry.npmjs.org/async/-/async-3.2.6.tgz" integrity sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA== asynckit@^0.4.0: version "0.4.0" - resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + resolved "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz" integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== axios@1.7.9, axios@^1.7.4: version "1.7.9" - resolved "https://registry.yarnpkg.com/axios/-/axios-1.7.9.tgz#d7d071380c132a24accda1b2cfc1535b79ec650a" + resolved "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz" integrity sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw== dependencies: follow-redirects "^1.15.6" @@ -2251,7 +2251,7 @@ axios@1.7.9, axios@^1.7.4: babel-jest@^29.7.0: version "29.7.0" - resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.7.0.tgz#f4369919225b684c56085998ac63dbd05be020d5" + resolved "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz" integrity sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg== dependencies: "@jest/transform" "^29.7.0" @@ -2264,7 +2264,7 @@ babel-jest@^29.7.0: babel-plugin-istanbul@^6.1.1: version "6.1.1" - resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz#fa88ec59232fd9b4e36dbbc540a8ec9a9b47da73" + resolved "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz" integrity sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA== dependencies: "@babel/helper-plugin-utils" "^7.0.0" @@ -2275,7 +2275,7 @@ babel-plugin-istanbul@^6.1.1: babel-plugin-jest-hoist@^29.6.3: version "29.6.3" - resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz#aadbe943464182a8922c3c927c3067ff40d24626" + resolved "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz" integrity sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg== dependencies: "@babel/template" "^7.3.3" @@ -2285,7 +2285,7 @@ babel-plugin-jest-hoist@^29.6.3: babel-preset-current-node-syntax@^1.0.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.1.0.tgz#9a929eafece419612ef4ae4f60b1862ebad8ef30" + resolved "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.1.0.tgz" integrity sha512-ldYss8SbBlWva1bs28q78Ju5Zq1F+8BrqBZZ0VFhLBvhh6lCpC2o3gDJi/5DRLs9FgYZCnmPYIVFU4lRXCkyUw== dependencies: "@babel/plugin-syntax-async-generators" "^7.8.4" @@ -2306,7 +2306,7 @@ babel-preset-current-node-syntax@^1.0.0: babel-preset-jest@^29.6.3: version "29.6.3" - resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz#fa05fa510e7d493896d7b0dd2033601c840f171c" + resolved "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz" integrity sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA== dependencies: babel-plugin-jest-hoist "^29.6.3" @@ -2314,34 +2314,34 @@ babel-preset-jest@^29.6.3: balanced-match@^1.0.0: version "1.0.2" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== base64-js@^1.3.0, base64-js@^1.3.1: version "1.5.1" - resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + resolved "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== basic-ftp@^5.0.2: version "5.0.5" - resolved "https://registry.yarnpkg.com/basic-ftp/-/basic-ftp-5.0.5.tgz#14a474f5fffecca1f4f406f1c26b18f800225ac0" + resolved "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.5.tgz" integrity sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg== bcrypt-pbkdf@^1.0.0, bcrypt-pbkdf@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" + resolved "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz" integrity sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w== dependencies: tweetnacl "^0.14.3" bignumber.js@^9.0.0: version "9.1.2" - resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.1.2.tgz#b7c4242259c008903b13707983b5f4bbd31eda0c" + resolved "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz" integrity sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug== bl@^4.1.0: version "4.1.0" - resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" + resolved "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz" integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== dependencies: buffer "^5.5.0" @@ -2350,12 +2350,12 @@ bl@^4.1.0: bowser@^2.11.0: version "2.11.0" - resolved "https://registry.yarnpkg.com/bowser/-/bowser-2.11.0.tgz#5ca3c35757a7aa5771500c70a73a9f91ef420a8f" + resolved "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz" integrity sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA== brace-expansion@^1.1.7: version "1.1.11" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz" integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== dependencies: balanced-match "^1.0.0" @@ -2363,21 +2363,21 @@ brace-expansion@^1.1.7: brace-expansion@^2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" + resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz" integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== dependencies: balanced-match "^1.0.0" braces@^3.0.3: version "3.0.3" - resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" + resolved "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz" integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== dependencies: fill-range "^7.1.1" browserslist@^4.24.0: version "4.24.4" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.24.4.tgz#c6b2865a3f08bcb860a0e827389003b9fe686e4b" + resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz" integrity sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A== dependencies: caniuse-lite "^1.0.30001688" @@ -2387,31 +2387,31 @@ browserslist@^4.24.0: bs-logger@^0.2.6: version "0.2.6" - resolved "https://registry.yarnpkg.com/bs-logger/-/bs-logger-0.2.6.tgz#eb7d365307a72cf974cc6cda76b68354ad336bd8" + resolved "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz" integrity sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog== dependencies: fast-json-stable-stringify "2.x" bser@2.1.1: version "2.1.1" - resolved "https://registry.yarnpkg.com/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05" + resolved "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz" integrity sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ== dependencies: node-int64 "^0.4.0" buffer-equal-constant-time@1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819" + resolved "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz" integrity sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA== buffer-from@^1.0.0: version "1.1.2" - resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" + resolved "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz" integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== buffer@^5.5.0: version "5.7.1" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" + resolved "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz" integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== dependencies: base64-js "^1.3.1" @@ -2419,37 +2419,37 @@ buffer@^5.5.0: buildcheck@~0.0.6: version "0.0.6" - resolved "https://registry.yarnpkg.com/buildcheck/-/buildcheck-0.0.6.tgz#89aa6e417cfd1e2196e3f8fe915eb709d2fe4238" + resolved "https://registry.npmjs.org/buildcheck/-/buildcheck-0.0.6.tgz" integrity sha512-8f9ZJCUXyT1M35Jx7MkBgmBMo3oHTTBIPLiY9xyL0pl3T5RwcPEY8cUHr5LBNfu/fk6c2T4DJZuVM/8ZZT2D2A== builtin-modules@^1.1.1: version "1.1.1" - resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" + resolved "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz" integrity sha512-wxXCdllwGhI2kCC0MnvTGYTMvnVZTvqgypkiTI8Pa5tcz2i6VqsqwYGgqwXji+4RgCzms6EajE4IxiUH6HH8nQ== callsites@^3.0.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + resolved "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz" integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== camelcase@^5.3.1: version "5.3.1" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" + resolved "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz" integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== camelcase@^6.2.0: version "6.3.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" + resolved "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== caniuse-lite@^1.0.30001688: version "1.0.30001695" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001695.tgz#39dfedd8f94851132795fdf9b79d29659ad9c4d4" + resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001695.tgz" integrity sha512-vHyLade6wTgI2u1ec3WQBxv+2BrTERV28UXQu9LO6lZ9pYeMk34vjXFLOxo1A4UBA8XTL4njRQZdno/yYaSmWw== chalk@3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4" + resolved "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz" integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg== dependencies: ansi-styles "^4.1.0" @@ -2457,7 +2457,7 @@ chalk@3.0.0: chalk@4.1.2, chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.0, chalk@^4.1.1: version "4.1.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== dependencies: ansi-styles "^4.1.0" @@ -2465,7 +2465,7 @@ chalk@4.1.2, chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.0, chalk@^4.1.1: chalk@^2.3.0: version "2.4.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== dependencies: ansi-styles "^3.2.1" @@ -2474,51 +2474,51 @@ chalk@^2.3.0: chalk@~5.4.1: version "5.4.1" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.4.1.tgz#1b48bf0963ec158dce2aacf69c093ae2dd2092d8" + resolved "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz" integrity sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w== char-regex@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" + resolved "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz" integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== chardet@^0.7.0: version "0.7.0" - resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" + resolved "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz" integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== ci-info@^3.2.0: version "3.9.0" - resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.9.0.tgz#4279a62028a7b1f262f3473fc9605f5e218c59b4" + resolved "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz" integrity sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ== cjs-module-lexer@^1.0.0: version "1.4.1" - resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.4.1.tgz#707413784dbb3a72aa11c2f2b042a0bef4004170" + resolved "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.1.tgz" integrity sha512-cuSVIHi9/9E/+821Qjdvngor+xpnlwnuwIyZOaLmHBVdXL+gP+I6QQB9VkO7RI77YIcTV+S1W9AreJ5eN63JBA== cli-cursor@^3.1.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" + resolved "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz" integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== dependencies: restore-cursor "^3.1.0" cli-cursor@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-5.0.0.tgz#24a4831ecf5a6b01ddeb32fb71a4b2088b0dce38" + resolved "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz" integrity sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw== dependencies: restore-cursor "^5.0.0" cli-spinners@^2.5.0: version "2.9.2" - resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.9.2.tgz#1773a8f4b9c4d6ac31563df53b3fc1d79462fe41" + resolved "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz" integrity sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg== cli-truncate@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-4.0.0.tgz#6cc28a2924fee9e25ce91e973db56c7066e6172a" + resolved "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz" integrity sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA== dependencies: slice-ansi "^5.0.0" @@ -2526,19 +2526,19 @@ cli-truncate@^4.0.0: cli-width@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" + resolved "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz" integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw== clipanion@^3.2.1: version "3.2.1" - resolved "https://registry.yarnpkg.com/clipanion/-/clipanion-3.2.1.tgz#2887db4cb232e80ba57cf19347a4e3a1c4a74133" + resolved "https://registry.npmjs.org/clipanion/-/clipanion-3.2.1.tgz" integrity sha512-dYFdjLb7y1ajfxQopN05mylEpK9ZX0sO1/RfMXdfmwjlIsPkbh4p7A682x++zFPLDCo1x3p82dtljHf5cW2LKA== dependencies: typanion "^3.8.0" cliui@^8.0.1: version "8.0.1" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" + resolved "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz" integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== dependencies: string-width "^4.2.0" @@ -2547,46 +2547,46 @@ cliui@^8.0.1: clone@^1.0.2: version "1.0.4" - resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" + resolved "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz" integrity sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg== co@^4.6.0: version "4.6.0" - resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" + resolved "https://registry.npmjs.org/co/-/co-4.6.0.tgz" integrity sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ== collect-v8-coverage@^1.0.0: version "1.0.2" - resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz#c0b29bcd33bcd0779a1344c2136051e6afd3d9e9" + resolved "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz" integrity sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q== color-convert@^1.9.0, color-convert@^1.9.3: version "1.9.3" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + resolved "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz" integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== dependencies: color-name "1.1.3" color-convert@^2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + resolved "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz" integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== dependencies: color-name "~1.1.4" color-name@1.1.3: version "1.1.3" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz" integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== color-name@^1.0.0, color-name@~1.1.4: version "1.1.4" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== color-string@^1.6.0: version "1.9.1" - resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.9.1.tgz#4467f9146f036f855b764dfb5bf8582bf342c7a4" + resolved "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz" integrity sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg== dependencies: color-name "^1.0.0" @@ -2594,7 +2594,7 @@ color-string@^1.6.0: color@^3.1.3: version "3.2.1" - resolved "https://registry.yarnpkg.com/color/-/color-3.2.1.tgz#3544dc198caf4490c3ecc9a790b54fe9ff45e164" + resolved "https://registry.npmjs.org/color/-/color-3.2.1.tgz" integrity sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA== dependencies: color-convert "^1.9.3" @@ -2602,12 +2602,12 @@ color@^3.1.3: colorette@^2.0.20: version "2.0.20" - resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" + resolved "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz" integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== colorspace@1.1.x: version "1.1.4" - resolved "https://registry.yarnpkg.com/colorspace/-/colorspace-1.1.4.tgz#8d442d1186152f60453bf8070cd66eb364e59243" + resolved "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz" integrity sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w== dependencies: color "^3.1.3" @@ -2615,44 +2615,44 @@ colorspace@1.1.x: combined-stream@^1.0.6, combined-stream@^1.0.8: version "1.0.8" - resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + resolved "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz" integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== dependencies: delayed-stream "~1.0.0" commander@13.1.0: version "13.1.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-13.1.0.tgz#776167db68c78f38dcce1f9b8d7b8b9a488abf46" + resolved "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz" integrity sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw== commander@^2.12.1: version "2.20.3" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + resolved "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== commander@~12.1.0: version "12.1.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-12.1.0.tgz#01423b36f501259fdaac4d0e4d60c96c991585d3" + resolved "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz" integrity sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA== concat-map@0.0.1: version "0.0.1" - resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== convert-source-map@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" + resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz" integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== core-util-is@~1.0.0: version "1.0.3" - resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" + resolved "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz" integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== cpu-features@~0.0.10: version "0.0.10" - resolved "https://registry.yarnpkg.com/cpu-features/-/cpu-features-0.0.10.tgz#9aae536db2710c7254d7ed67cb3cbc7d29ad79c5" + resolved "https://registry.npmjs.org/cpu-features/-/cpu-features-0.0.10.tgz" integrity sha512-9IkYqtX3YHPCzoVg1Py+o9057a3i0fp7S530UWokCSaFVTc7CwXPRiOjRjBQQ18ZCNafx78YfnG+HALxtVmOGA== dependencies: buildcheck "~0.0.6" @@ -2660,7 +2660,7 @@ cpu-features@~0.0.10: create-jest@^29.7.0: version "29.7.0" - resolved "https://registry.yarnpkg.com/create-jest/-/create-jest-29.7.0.tgz#a355c5b3cb1e1af02ba177fe7afd7feee49a5320" + resolved "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz" integrity sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q== dependencies: "@jest/types" "^29.6.3" @@ -2673,12 +2673,12 @@ create-jest@^29.7.0: create-require@^1.1.0: version "1.1.1" - resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" + resolved "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz" integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== cross-spawn@^7.0.3: version "7.0.6" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" + resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz" integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== dependencies: path-key "^3.1.0" @@ -2687,19 +2687,19 @@ cross-spawn@^7.0.3: dashdash@^1.12.0: version "1.14.1" - resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" + resolved "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz" integrity sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g== dependencies: assert-plus "^1.0.0" data-uri-to-buffer@^6.0.2: version "6.0.2" - resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz#8a58bb67384b261a38ef18bea1810cb01badd28b" + resolved "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz" integrity sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw== datadog-metrics@0.9.3: version "0.9.3" - resolved "https://registry.yarnpkg.com/datadog-metrics/-/datadog-metrics-0.9.3.tgz#e62d92b9619129805802d82111c8bcc4439fc859" + resolved "https://registry.npmjs.org/datadog-metrics/-/datadog-metrics-0.9.3.tgz" integrity sha512-BVsBX2t+4yA3tHs7DnB5H01cHVNiGJ/bHA8y6JppJDyXG7s2DLm6JaozPGpgsgVGd42Is1CHRG/yMDQpt877Xg== dependencies: debug "3.1.0" @@ -2707,48 +2707,48 @@ datadog-metrics@0.9.3: debug@3.1.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" + resolved "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz" integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== dependencies: ms "2.0.0" debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.4, debug@~4.4.0: version "4.4.0" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.0.tgz#2b3f2aea2ffeb776477460267377dc8710faba8a" + resolved "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz" integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA== dependencies: ms "^2.1.3" dedent@^1.0.0: version "1.5.3" - resolved "https://registry.yarnpkg.com/dedent/-/dedent-1.5.3.tgz#99aee19eb9bae55a67327717b6e848d0bf777e5a" + resolved "https://registry.npmjs.org/dedent/-/dedent-1.5.3.tgz" integrity sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ== deep-extend@0.6.0, deep-extend@^0.6.0: version "0.6.0" - resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" + resolved "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz" integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== deep-object-diff@^1.1.9: version "1.1.9" - resolved "https://registry.yarnpkg.com/deep-object-diff/-/deep-object-diff-1.1.9.tgz#6df7ef035ad6a0caa44479c536ed7b02570f4595" + resolved "https://registry.npmjs.org/deep-object-diff/-/deep-object-diff-1.1.9.tgz" integrity sha512-Rn+RuwkmkDwCi2/oXOFS9Gsr5lJZu/yTGpK7wAaAIE75CC+LCGEZHpY6VQJa/RoJcrmaA/docWJZvYohlNkWPA== deepmerge@^4.2.2: version "4.3.1" - resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a" + resolved "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz" integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== defaults@^1.0.3: version "1.0.4" - resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.4.tgz#b0b02062c1e2aa62ff5d9528f0f98baa90978d7a" + resolved "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz" integrity sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A== dependencies: clone "^1.0.2" degenerator@^5.0.0: version "5.0.1" - resolved "https://registry.yarnpkg.com/degenerator/-/degenerator-5.0.1.tgz#9403bf297c6dad9a1ece409b37db27954f91f2f5" + resolved "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz" integrity sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ== dependencies: ast-types "^0.13.4" @@ -2757,27 +2757,27 @@ degenerator@^5.0.0: delayed-stream@~1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + resolved "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz" integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== detect-newline@^3.0.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" + resolved "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz" integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== diff-sequences@^29.6.3: version "29.6.3" - resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.6.3.tgz#4deaf894d11407c51efc8418012f9e70b84ea921" + resolved "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz" integrity sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q== diff@^4.0.1: version "4.0.2" - resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" + resolved "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz" integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== dogapi@2.8.4: version "2.8.4" - resolved "https://registry.yarnpkg.com/dogapi/-/dogapi-2.8.4.tgz#ada64f20c6acdea206b9fd9e70df0c96241b6621" + resolved "https://registry.npmjs.org/dogapi/-/dogapi-2.8.4.tgz" integrity sha512-065fsvu5dB0o4+ENtLjZILvXMClDNH/yA9H6L8nsdcNiz9l0Hzpn7aQaCOPYXxqyzq4CRPOdwkFXUjDOXfRGbg== dependencies: extend "^3.0.2" @@ -2788,14 +2788,14 @@ dogapi@2.8.4: dot-prop@^6.0.0: version "6.0.1" - resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-6.0.1.tgz#fc26b3cf142b9e59b74dbd39ed66ce620c681083" + resolved "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.1.tgz" integrity sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA== dependencies: is-obj "^2.0.0" duplexify@^4.0.0, duplexify@^4.1.1: version "4.1.3" - resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-4.1.3.tgz#a07e1c0d0a2c001158563d32592ba58bddb0236f" + resolved "https://registry.npmjs.org/duplexify/-/duplexify-4.1.3.tgz" integrity sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA== dependencies: end-of-stream "^1.4.1" @@ -2805,7 +2805,7 @@ duplexify@^4.0.0, duplexify@^4.1.1: ecc-jsbn@~0.1.1: version "0.1.2" - resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" + resolved "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz" integrity sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw== dependencies: jsbn "~0.1.0" @@ -2813,85 +2813,85 @@ ecc-jsbn@~0.1.1: ecdsa-sig-formatter@1.0.11, ecdsa-sig-formatter@^1.0.11: version "1.0.11" - resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf" + resolved "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz" integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ== dependencies: safe-buffer "^5.0.1" ee-first@1.1.1: version "1.1.1" - resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + resolved "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz" integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== ejs@^3.1.10: version "3.1.10" - resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.10.tgz#69ab8358b14e896f80cc39e62087b88500c3ac3b" + resolved "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz" integrity sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA== dependencies: jake "^10.8.5" electron-to-chromium@^1.5.73: version "1.5.84" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.84.tgz#8e334ca206bb293a20b16418bf454783365b0a95" + resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.84.tgz" integrity sha512-I+DQ8xgafao9Ha6y0qjHHvpZ9OfyA1qKlkHkjywxzniORU2awxyz7f/iVJcULmrF2yrM3nHQf+iDjJtbbexd/g== emittery@^0.13.1: version "0.13.1" - resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.13.1.tgz#c04b8c3457490e0847ae51fced3af52d338e3dad" + resolved "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz" integrity sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ== emoji-regex@^10.3.0: version "10.4.0" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-10.4.0.tgz#03553afea80b3975749cfcb36f776ca268e413d4" + resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz" integrity sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw== emoji-regex@^8.0.0: version "8.0.0" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== enabled@2.0.x: version "2.0.0" - resolved "https://registry.yarnpkg.com/enabled/-/enabled-2.0.0.tgz#f9dd92ec2d6f4bbc0d5d1e64e21d61cd4665e7c2" + resolved "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz" integrity sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ== end-of-stream@^1.1.0, end-of-stream@^1.4.1: version "1.4.4" - resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + resolved "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz" integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== dependencies: once "^1.4.0" environment@^1.0.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/environment/-/environment-1.1.0.tgz#8e86c66b180f363c7ab311787e0259665f45a9f1" + resolved "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz" integrity sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q== error-ex@^1.3.1: version "1.3.2" - resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" + resolved "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz" integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== dependencies: is-arrayish "^0.2.1" escalade@^3.1.1, escalade@^3.2.0: version "3.2.0" - resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" + resolved "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz" integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== escape-string-regexp@^1.0.5: version "1.0.5" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== escape-string-regexp@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" + resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz" integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== escodegen@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-2.1.0.tgz#ba93bbb7a43986d29d6041f99f5262da773e2e17" + resolved "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz" integrity sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w== dependencies: esprima "^4.0.1" @@ -2902,39 +2902,39 @@ escodegen@^2.1.0: esprima@^4.0.0, esprima@^4.0.1: version "4.0.1" - resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + resolved "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== estraverse@^5.2.0: version "5.3.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" + resolved "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz" integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== esutils@^2.0.2: version "2.0.3" - resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + resolved "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz" integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== event-target-shim@^5.0.0: version "5.0.1" - resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" + resolved "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz" integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== eventemitter3@^5.0.1: version "5.0.1" - resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-5.0.1.tgz#53f5ffd0a492ac800721bb42c66b841de96423c4" + resolved "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz" integrity sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA== eventid@^2.0.0: version "2.0.1" - resolved "https://registry.yarnpkg.com/eventid/-/eventid-2.0.1.tgz#574e860149457a79a2efe788c459f0c3062d02ec" + resolved "https://registry.npmjs.org/eventid/-/eventid-2.0.1.tgz" integrity sha512-sPNTqiMokAvV048P2c9+foqVJzk49o6d4e0D/sq5jog3pw+4kBgyR0gaM1FM7Mx6Kzd9dztesh9oYz1LWWOpzw== dependencies: uuid "^8.0.0" execa@^5.0.0: version "5.1.1" - resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" + resolved "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz" integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== dependencies: cross-spawn "^7.0.3" @@ -2949,7 +2949,7 @@ execa@^5.0.0: execa@~8.0.1: version "8.0.1" - resolved "https://registry.yarnpkg.com/execa/-/execa-8.0.1.tgz#51f6a5943b580f963c3ca9c6321796db8cc39b8c" + resolved "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz" integrity sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg== dependencies: cross-spawn "^7.0.3" @@ -2964,12 +2964,12 @@ execa@~8.0.1: exit@^0.1.2: version "0.1.2" - resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" + resolved "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz" integrity sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ== expect@^29.0.0, expect@^29.7.0: version "29.7.0" - resolved "https://registry.yarnpkg.com/expect/-/expect-29.7.0.tgz#578874590dcb3214514084c08115d8aee61e11bc" + resolved "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz" integrity sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw== dependencies: "@jest/expect-utils" "^29.7.0" @@ -2980,12 +2980,12 @@ expect@^29.0.0, expect@^29.7.0: extend@^3.0.2: version "3.0.2" - resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + resolved "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz" integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== external-editor@^3.0.3: version "3.1.0" - resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" + resolved "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz" integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== dependencies: chardet "^0.7.0" @@ -2994,81 +2994,81 @@ external-editor@^3.0.3: fast-deep-equal@^3.1.3: version "3.1.3" - resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + resolved "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== fast-levenshtein@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-3.0.0.tgz#37b899ae47e1090e40e3fd2318e4d5f0142ca912" + resolved "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-3.0.0.tgz" integrity sha512-hKKNajm46uNmTlhHSyZkmToAc56uZJwYq7yrciZjqOxnlfQwERDQJmHPUp7m1m9wx8vgOe8IaCKZ5Kv2k1DdCQ== dependencies: fastest-levenshtein "^1.0.7" fast-uri@^3.0.1: version "3.0.6" - resolved "https://registry.yarnpkg.com/fast-uri/-/fast-uri-3.0.6.tgz#88f130b77cfaea2378d56bf970dea21257a68748" + resolved "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz" integrity sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw== fast-xml-parser@4.4.1: version "4.4.1" - resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-4.4.1.tgz#86dbf3f18edf8739326447bcaac31b4ae7f6514f" + resolved "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.4.1.tgz" integrity sha512-xkjOecfnKGkSsOwtZ5Pz7Us/T6mrbPQrq0nh+aCO5V9nk5NLWmasAHumTKjiPJPWANe+kAZ84Jc8ooJkzZ88Sw== dependencies: strnum "^1.0.5" fast-xml-parser@^4.4.1: version "4.5.1" - resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-4.5.1.tgz#a7e665ff79b7919100a5202f23984b6150f9b31e" + resolved "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.1.tgz" integrity sha512-y655CeyUQ+jj7KBbYMc4FG01V8ZQqjN+gDYGJ50RtfsUB8iG9AmwmwoAgeKLJdmueKKMrH1RJ7yXHTSoczdv5w== dependencies: strnum "^1.0.5" fastest-levenshtein@^1.0.7: version "1.0.16" - resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz#210e61b6ff181de91ea9b3d1b84fdedd47e034e5" + resolved "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz" integrity sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg== fb-watchman@^2.0.0: version "2.0.2" - resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.2.tgz#e9524ee6b5c77e9e5001af0f85f3adbb8623255c" + resolved "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz" integrity sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA== dependencies: bser "2.1.1" fecha@^4.2.0: version "4.2.3" - resolved "https://registry.yarnpkg.com/fecha/-/fecha-4.2.3.tgz#4d9ccdbc61e8629b259fdca67e65891448d569fd" + resolved "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz" integrity sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw== figures@^3.0.0: version "3.2.0" - resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" + resolved "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz" integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== dependencies: escape-string-regexp "^1.0.5" filelist@^1.0.4: version "1.0.4" - resolved "https://registry.yarnpkg.com/filelist/-/filelist-1.0.4.tgz#f78978a1e944775ff9e62e744424f215e58352b5" + resolved "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz" integrity sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q== dependencies: minimatch "^5.0.1" fill-range@^7.1.1: version "7.1.1" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" + resolved "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz" integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== dependencies: to-regex-range "^5.0.1" find-up@^4.0.0, find-up@^4.1.0: version "4.1.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" + resolved "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz" integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== dependencies: locate-path "^5.0.0" @@ -3076,17 +3076,17 @@ find-up@^4.0.0, find-up@^4.1.0: fn.name@1.x.x: version "1.1.0" - resolved "https://registry.yarnpkg.com/fn.name/-/fn.name-1.1.0.tgz#26cad8017967aea8731bc42961d04a3d5988accc" + resolved "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz" integrity sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw== follow-redirects@^1.15.6: version "1.15.9" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.9.tgz#a604fa10e443bf98ca94228d9eebcc2e8a2c8ee1" + resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz" integrity sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ== form-data@4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" + resolved "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz" integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== dependencies: asynckit "^0.4.0" @@ -3095,7 +3095,7 @@ form-data@4.0.0: form-data@4.0.1, form-data@^4.0.0: version "4.0.1" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.1.tgz#ba1076daaaa5bfd7e99c1a6cb02aa0a5cff90d48" + resolved "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz" integrity sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw== dependencies: asynckit "^0.4.0" @@ -3104,7 +3104,7 @@ form-data@4.0.1, form-data@^4.0.0: form-data@^2.5.0: version "2.5.2" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.5.2.tgz#dc653743d1de2fcc340ceea38079daf6e9069fd2" + resolved "https://registry.npmjs.org/form-data/-/form-data-2.5.2.tgz" integrity sha512-GgwY0PS7DbXqajuGf4OYlsrIu3zgxD6Vvql43IBhm6MahqA5SK/7mwhtNj2AdH2z35YR34ujJ7BN+3fFC3jP5Q== dependencies: asynckit "^0.4.0" @@ -3114,27 +3114,27 @@ form-data@^2.5.0: fs.realpath@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== fsevents@^2.3.2: version "2.3.3" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz" integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== function-bind@^1.1.2: version "1.1.2" - resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz" integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== fuzzy@^0.1.3: version "0.1.3" - resolved "https://registry.yarnpkg.com/fuzzy/-/fuzzy-0.1.3.tgz#4c76ec2ff0ac1a36a9dccf9a00df8623078d4ed8" + resolved "https://registry.npmjs.org/fuzzy/-/fuzzy-0.1.3.tgz" integrity sha512-/gZffu4ykarLrCiP3Ygsa86UAo1E5vEVlvTrpkKywXSbP9Xhln3oSp9QSV57gEq3JFFpGJ4GZ+5zdEp3FcUh4w== gaxios@^6.0.0, gaxios@^6.1.1: version "6.7.1" - resolved "https://registry.yarnpkg.com/gaxios/-/gaxios-6.7.1.tgz#ebd9f7093ede3ba502685e73390248bb5b7f71fb" + resolved "https://registry.npmjs.org/gaxios/-/gaxios-6.7.1.tgz" integrity sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ== dependencies: extend "^3.0.2" @@ -3145,7 +3145,7 @@ gaxios@^6.0.0, gaxios@^6.1.1: gcp-metadata@^6.0.0, gcp-metadata@^6.1.0: version "6.1.0" - resolved "https://registry.yarnpkg.com/gcp-metadata/-/gcp-metadata-6.1.0.tgz#9b0dd2b2445258e7597f2024332d20611cbd6b8c" + resolved "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-6.1.0.tgz" integrity sha512-Jh/AIwwgaxan+7ZUUmRLCjtchyDiqh4KjBJ5tW3plBZb5iL/BPcso8A5DlzeD9qlw0duCamnNdpFjxwaT0KyKg== dependencies: gaxios "^6.0.0" @@ -3153,37 +3153,37 @@ gcp-metadata@^6.0.0, gcp-metadata@^6.1.0: gensync@^1.0.0-beta.2: version "1.0.0-beta.2" - resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" + resolved "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz" integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== get-caller-file@^2.0.0, get-caller-file@^2.0.5: version "2.0.5" - resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + resolved "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== get-east-asian-width@^1.0.0: version "1.3.0" - resolved "https://registry.yarnpkg.com/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz#21b4071ee58ed04ee0db653371b55b4299875389" + resolved "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz" integrity sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ== get-package-type@^0.1.0: version "0.1.0" - resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" + resolved "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz" integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== get-stream@^6.0.0: version "6.0.1" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" + resolved "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz" integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== get-stream@^8.0.1: version "8.0.1" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-8.0.1.tgz#def9dfd71742cd7754a7761ed43749a27d02eca2" + resolved "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz" integrity sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA== get-uri@^6.0.1: version "6.0.4" - resolved "https://registry.yarnpkg.com/get-uri/-/get-uri-6.0.4.tgz#6daaee9e12f9759e19e55ba313956883ef50e0a7" + resolved "https://registry.npmjs.org/get-uri/-/get-uri-6.0.4.tgz" integrity sha512-E1b1lFFLvLgak2whF2xDBcOy6NLVGZBqqjJjsIhvopKfWWEi64pLVTWWehV8KlLerZkfNTA95sTe2OdJKm1OzQ== dependencies: basic-ftp "^5.0.2" @@ -3192,7 +3192,7 @@ get-uri@^6.0.1: getpass@^0.1.1: version "0.1.7" - resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" + resolved "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz" integrity sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng== dependencies: assert-plus "^1.0.0" @@ -3209,12 +3209,12 @@ glob@10.0.0, glob@^7.1.1, glob@^7.1.3, glob@^7.1.4: globals@^11.1.0: version "11.12.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" + resolved "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz" integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== google-auth-library@^9.0.0, google-auth-library@^9.12.0, google-auth-library@^9.3.0: version "9.15.0" - resolved "https://registry.yarnpkg.com/google-auth-library/-/google-auth-library-9.15.0.tgz#1b009c08557929c881d72f953f17e839e91b009b" + resolved "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.15.0.tgz" integrity sha512-7ccSEJFDFO7exFbO6NRyC+xH8/mZ1GZGG2xxx9iHxZWcjUjJpjWxIMw3cofAKcueZ6DATiukmmprD7yavQHOyQ== dependencies: base64-js "^1.3.0" @@ -3226,7 +3226,7 @@ google-auth-library@^9.0.0, google-auth-library@^9.12.0, google-auth-library@^9. google-gax@^4.0.3: version "4.4.1" - resolved "https://registry.yarnpkg.com/google-gax/-/google-gax-4.4.1.tgz#95a9cf7ee7777ac22d1926a45b5f886dd8beecae" + resolved "https://registry.npmjs.org/google-gax/-/google-gax-4.4.1.tgz" integrity sha512-Phyp9fMfA00J3sZbJxbbB4jC55b7DBjE3F6poyL3wKMEBVKA79q6BGuHcTiM28yOzVql0NDbRL8MLLh8Iwk9Dg== dependencies: "@grpc/grpc-js" "^1.10.9" @@ -3244,12 +3244,12 @@ google-gax@^4.0.3: graceful-fs@^4.2.9: version "4.2.11" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== gtoken@^7.0.0: version "7.1.0" - resolved "https://registry.yarnpkg.com/gtoken/-/gtoken-7.1.0.tgz#d61b4ebd10132222817f7222b1e6064bd463fc26" + resolved "https://registry.npmjs.org/gtoken/-/gtoken-7.1.0.tgz" integrity sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw== dependencies: gaxios "^6.0.0" @@ -3257,39 +3257,39 @@ gtoken@^7.0.0: has-flag@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + resolved "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz" integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== has-flag@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + resolved "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== hasown@^2.0.2: version "2.0.2" - resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" + resolved "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz" integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== dependencies: function-bind "^1.1.2" hpagent@1.2.0: version "1.2.0" - resolved "https://registry.yarnpkg.com/hpagent/-/hpagent-1.2.0.tgz#0ae417895430eb3770c03443456b8d90ca464903" + resolved "https://registry.npmjs.org/hpagent/-/hpagent-1.2.0.tgz" integrity sha512-A91dYTeIB6NoXG+PxTQpCCDDnfHsW9kc06Lvpu1TEe9gnd6ZFeiBoRO9JvzEv6xK7EX97/dUE8g/vBMTqTS3CA== html-entities@^2.5.2: version "2.5.2" - resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-2.5.2.tgz#201a3cf95d3a15be7099521620d19dfb4f65359f" + resolved "https://registry.npmjs.org/html-entities/-/html-entities-2.5.2.tgz" integrity sha512-K//PSRMQk4FZ78Kyau+mZurHn3FH0Vwr+H36eE0rPbeYkRRi9YxceYPhuN60UwWorxyKHhqoAJl2OFKa4BVtaA== html-escaper@^2.0.0: version "2.0.2" - resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" + resolved "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz" integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== http-proxy-agent@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43" + resolved "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz" integrity sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w== dependencies: "@tootallnate/once" "2" @@ -3298,7 +3298,7 @@ http-proxy-agent@^5.0.0: http-proxy-agent@^7.0.0, http-proxy-agent@^7.0.1: version "7.0.2" - resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz#9a8b1f246866c028509486585f62b8f2c18c270e" + resolved "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz" integrity sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig== dependencies: agent-base "^7.1.0" @@ -3306,7 +3306,7 @@ http-proxy-agent@^7.0.0, http-proxy-agent@^7.0.1: https-proxy-agent@^5.0.0: version "5.0.1" - resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" + resolved "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz" integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== dependencies: agent-base "6" @@ -3314,7 +3314,7 @@ https-proxy-agent@^5.0.0: https-proxy-agent@^7.0.1, https-proxy-agent@^7.0.6: version "7.0.6" - resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz#da8dfeac7da130b05c2ba4b59c9b6cd66611a6b9" + resolved "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz" integrity sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw== dependencies: agent-base "^7.1.2" @@ -3322,34 +3322,34 @@ https-proxy-agent@^7.0.1, https-proxy-agent@^7.0.6: human-signals@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" + resolved "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz" integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== human-signals@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-5.0.0.tgz#42665a284f9ae0dade3ba41ebc37eb4b852f3a28" + resolved "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz" integrity sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ== iconv-lite@^0.4.24: version "0.4.24" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz" integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== dependencies: safer-buffer ">= 2.1.2 < 3" ieee754@^1.1.13: version "1.2.1" - resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" + resolved "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== immediate@~3.0.5: version "3.0.6" - resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b" + resolved "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz" integrity sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ== import-local@^3.0.2: version "3.2.0" - resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.2.0.tgz#c3d5c745798c02a6f8b897726aba5100186ee260" + resolved "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz" integrity sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA== dependencies: pkg-dir "^4.2.0" @@ -3357,22 +3357,22 @@ import-local@^3.0.2: imurmurhash@^0.1.4: version "0.1.4" - resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + resolved "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz" integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3: version "2.0.4" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== ini@~1.3.0: version "1.3.8" - resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" + resolved "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz" integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== inquirer-checkbox-plus-prompt@^1.4.2: version "1.4.2" - resolved "https://registry.yarnpkg.com/inquirer-checkbox-plus-prompt/-/inquirer-checkbox-plus-prompt-1.4.2.tgz#5a5bb42a3f4bca2f6f4e582d163733eb59f4b195" + resolved "https://registry.npmjs.org/inquirer-checkbox-plus-prompt/-/inquirer-checkbox-plus-prompt-1.4.2.tgz" integrity sha512-W8/NL9x5A81Oq9ZfbYW5c1LuwtAhc/oB/u9YZZejna0pqrajj27XhnUHygJV0Vn5TvcDy1VJcD2Ld9kTk40dvg== dependencies: chalk "4.1.2" @@ -3383,7 +3383,7 @@ inquirer-checkbox-plus-prompt@^1.4.2: inquirer@^8.2.5: version "8.2.6" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-8.2.6.tgz#733b74888195d8d400a67ac332011b5fae5ea562" + resolved "https://registry.npmjs.org/inquirer/-/inquirer-8.2.6.tgz" integrity sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg== dependencies: ansi-escapes "^4.2.1" @@ -3404,12 +3404,12 @@ inquirer@^8.2.5: inversify@^5.0.0: version "5.1.1" - resolved "https://registry.yarnpkg.com/inversify/-/inversify-5.1.1.tgz#6fbd668c591337404e005a1946bfe0d802c08730" + resolved "https://registry.npmjs.org/inversify/-/inversify-5.1.1.tgz" integrity sha512-j8grHGDzv1v+8T1sAQ+3boTCntFPfvxLCkNcxB1J8qA0lUN+fAlSyYd+RXKvaPRL4AGyPxViutBEJHNXOyUdFQ== ip-address@^9.0.5: version "9.0.5" - resolved "https://registry.yarnpkg.com/ip-address/-/ip-address-9.0.5.tgz#117a960819b08780c3bd1f14ef3c1cc1d3f3ea5a" + resolved "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz" integrity sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g== dependencies: jsbn "1.1.0" @@ -3417,91 +3417,91 @@ ip-address@^9.0.5: is-arrayish@^0.2.1: version "0.2.1" - resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + resolved "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz" integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== is-arrayish@^0.3.1: version "0.3.2" - resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03" + resolved "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz" integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ== is-core-module@^2.16.0: version "2.16.1" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.16.1.tgz#2a98801a849f43e2add644fbb6bc6229b19a4ef4" + resolved "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz" integrity sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w== dependencies: hasown "^2.0.2" is-fullwidth-code-point@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz" integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== is-fullwidth-code-point@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz#fae3167c729e7463f8461ce512b080a49268aa88" + resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz" integrity sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ== is-fullwidth-code-point@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-5.0.0.tgz#9609efced7c2f97da7b60145ef481c787c7ba704" + resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.0.0.tgz" integrity sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA== dependencies: get-east-asian-width "^1.0.0" is-generator-fn@^2.0.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" + resolved "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz" integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== is-interactive@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" + resolved "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz" integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w== is-number@^7.0.0: version "7.0.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + resolved "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== is-obj@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982" + resolved "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz" integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w== is-stream@^2.0.0: version "2.0.1" - resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" + resolved "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz" integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== is-stream@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-3.0.0.tgz#e6bfd7aa6bef69f4f472ce9bb681e3e57b4319ac" + resolved "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz" integrity sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA== is-unicode-supported@^0.1.0: version "0.1.0" - resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" + resolved "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz" integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== isarray@~1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + resolved "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== isexe@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz" integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0: version "3.2.2" - resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz#2d166c4b0644d43a39f04bf6c2edd1e585f31756" + resolved "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz" integrity sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg== istanbul-lib-instrument@^5.0.4: version "5.2.1" - resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz#d10c8885c2125574e1c231cacadf955675e1ce3d" + resolved "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz" integrity sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg== dependencies: "@babel/core" "^7.12.3" @@ -3512,7 +3512,7 @@ istanbul-lib-instrument@^5.0.4: istanbul-lib-instrument@^6.0.0: version "6.0.3" - resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz#fa15401df6c15874bcb2105f773325d78c666765" + resolved "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz" integrity sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q== dependencies: "@babel/core" "^7.23.9" @@ -3523,7 +3523,7 @@ istanbul-lib-instrument@^6.0.0: istanbul-lib-report@^3.0.0: version "3.0.1" - resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz#908305bac9a5bd175ac6a74489eafd0fc2445a7d" + resolved "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz" integrity sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw== dependencies: istanbul-lib-coverage "^3.0.0" @@ -3532,7 +3532,7 @@ istanbul-lib-report@^3.0.0: istanbul-lib-source-maps@^4.0.0: version "4.0.1" - resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz#895f3a709fcfba34c6de5a42939022f3e4358551" + resolved "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz" integrity sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw== dependencies: debug "^4.1.1" @@ -3541,7 +3541,7 @@ istanbul-lib-source-maps@^4.0.0: istanbul-reports@^3.1.3: version "3.1.7" - resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.1.7.tgz#daed12b9e1dca518e15c056e1e537e741280fa0b" + resolved "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz" integrity sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g== dependencies: html-escaper "^2.0.0" @@ -3549,7 +3549,7 @@ istanbul-reports@^3.1.3: jake@^10.8.5: version "10.9.2" - resolved "https://registry.yarnpkg.com/jake/-/jake-10.9.2.tgz#6ae487e6a69afec3a5e167628996b59f35ae2b7f" + resolved "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz" integrity sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA== dependencies: async "^3.2.3" @@ -3559,7 +3559,7 @@ jake@^10.8.5: jest-changed-files@^29.7.0: version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-29.7.0.tgz#1c06d07e77c78e1585d020424dedc10d6e17ac3a" + resolved "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz" integrity sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w== dependencies: execa "^5.0.0" @@ -3568,7 +3568,7 @@ jest-changed-files@^29.7.0: jest-circus@^29.7.0: version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-29.7.0.tgz#b6817a45fcc835d8b16d5962d0c026473ee3668a" + resolved "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz" integrity sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw== dependencies: "@jest/environment" "^29.7.0" @@ -3594,7 +3594,7 @@ jest-circus@^29.7.0: jest-cli@^29.7.0: version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-29.7.0.tgz#5592c940798e0cae677eec169264f2d839a37995" + resolved "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz" integrity sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg== dependencies: "@jest/core" "^29.7.0" @@ -3611,7 +3611,7 @@ jest-cli@^29.7.0: jest-config@^29.7.0: version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-29.7.0.tgz#bcbda8806dbcc01b1e316a46bb74085a84b0245f" + resolved "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz" integrity sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ== dependencies: "@babel/core" "^7.11.6" @@ -3639,7 +3639,7 @@ jest-config@^29.7.0: jest-diff@^29.7.0: version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.7.0.tgz#017934a66ebb7ecf6f205e84699be10afd70458a" + resolved "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz" integrity sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw== dependencies: chalk "^4.0.0" @@ -3649,14 +3649,14 @@ jest-diff@^29.7.0: jest-docblock@^29.7.0: version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-29.7.0.tgz#8fddb6adc3cdc955c93e2a87f61cfd350d5d119a" + resolved "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz" integrity sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g== dependencies: detect-newline "^3.0.0" jest-each@^29.7.0: version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-29.7.0.tgz#162a9b3f2328bdd991beaabffbb74745e56577d1" + resolved "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz" integrity sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ== dependencies: "@jest/types" "^29.6.3" @@ -3667,7 +3667,7 @@ jest-each@^29.7.0: jest-environment-node@^29.7.0: version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-29.7.0.tgz#0b93e111dda8ec120bc8300e6d1fb9576e164376" + resolved "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz" integrity sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw== dependencies: "@jest/environment" "^29.7.0" @@ -3679,12 +3679,12 @@ jest-environment-node@^29.7.0: jest-get-type@^29.6.3: version "29.6.3" - resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.6.3.tgz#36f499fdcea197c1045a127319c0481723908fd1" + resolved "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz" integrity sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw== jest-haste-map@^29.7.0: version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-29.7.0.tgz#3c2396524482f5a0506376e6c858c3bbcc17b104" + resolved "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz" integrity sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA== dependencies: "@jest/types" "^29.6.3" @@ -3703,7 +3703,7 @@ jest-haste-map@^29.7.0: jest-leak-detector@^29.7.0: version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz#5b7ec0dadfdfec0ca383dc9aa016d36b5ea4c728" + resolved "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz" integrity sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw== dependencies: jest-get-type "^29.6.3" @@ -3711,7 +3711,7 @@ jest-leak-detector@^29.7.0: jest-matcher-utils@^29.7.0: version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz#ae8fec79ff249fd592ce80e3ee474e83a6c44f12" + resolved "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz" integrity sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g== dependencies: chalk "^4.0.0" @@ -3721,7 +3721,7 @@ jest-matcher-utils@^29.7.0: jest-message-util@^29.7.0: version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.7.0.tgz#8bc392e204e95dfe7564abbe72a404e28e51f7f3" + resolved "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz" integrity sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w== dependencies: "@babel/code-frame" "^7.12.13" @@ -3736,7 +3736,7 @@ jest-message-util@^29.7.0: jest-mock@^29.7.0: version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-29.7.0.tgz#4e836cf60e99c6fcfabe9f99d017f3fdd50a6347" + resolved "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz" integrity sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw== dependencies: "@jest/types" "^29.6.3" @@ -3745,17 +3745,17 @@ jest-mock@^29.7.0: jest-pnp-resolver@^1.2.2: version "1.2.3" - resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz#930b1546164d4ad5937d5540e711d4d38d4cad2e" + resolved "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz" integrity sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w== jest-regex-util@^29.6.3: version "29.6.3" - resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-29.6.3.tgz#4a556d9c776af68e1c5f48194f4d0327d24e8a52" + resolved "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz" integrity sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg== jest-resolve-dependencies@^29.7.0: version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz#1b04f2c095f37fc776ff40803dc92921b1e88428" + resolved "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz" integrity sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA== dependencies: jest-regex-util "^29.6.3" @@ -3763,7 +3763,7 @@ jest-resolve-dependencies@^29.7.0: jest-resolve@^29.7.0: version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-29.7.0.tgz#64d6a8992dd26f635ab0c01e5eef4399c6bcbc30" + resolved "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz" integrity sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA== dependencies: chalk "^4.0.0" @@ -3778,7 +3778,7 @@ jest-resolve@^29.7.0: jest-runner@^29.7.0: version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-29.7.0.tgz#809af072d408a53dcfd2e849a4c976d3132f718e" + resolved "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz" integrity sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ== dependencies: "@jest/console" "^29.7.0" @@ -3805,7 +3805,7 @@ jest-runner@^29.7.0: jest-runtime@^29.7.0: version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-29.7.0.tgz#efecb3141cf7d3767a3a0cc8f7c9990587d3d817" + resolved "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz" integrity sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ== dependencies: "@jest/environment" "^29.7.0" @@ -3833,7 +3833,7 @@ jest-runtime@^29.7.0: jest-snapshot@^29.7.0: version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-29.7.0.tgz#c2c574c3f51865da1bb329036778a69bf88a6be5" + resolved "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz" integrity sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw== dependencies: "@babel/core" "^7.11.6" @@ -3859,7 +3859,7 @@ jest-snapshot@^29.7.0: jest-util@^29.0.0, jest-util@^29.7.0: version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.7.0.tgz#23c2b62bfb22be82b44de98055802ff3710fc0bc" + resolved "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz" integrity sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA== dependencies: "@jest/types" "^29.6.3" @@ -3871,7 +3871,7 @@ jest-util@^29.0.0, jest-util@^29.7.0: jest-validate@^29.7.0: version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-29.7.0.tgz#7bf705511c64da591d46b15fce41400d52147d9c" + resolved "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz" integrity sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw== dependencies: "@jest/types" "^29.6.3" @@ -3883,7 +3883,7 @@ jest-validate@^29.7.0: jest-watcher@^29.7.0: version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-29.7.0.tgz#7810d30d619c3a62093223ce6bb359ca1b28a2f2" + resolved "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz" integrity sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g== dependencies: "@jest/test-result" "^29.7.0" @@ -3897,7 +3897,7 @@ jest-watcher@^29.7.0: jest-worker@^29.7.0: version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-29.7.0.tgz#acad073acbbaeb7262bd5389e1bcf43e10058d4a" + resolved "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz" integrity sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw== dependencies: "@types/node" "*" @@ -3907,7 +3907,7 @@ jest-worker@^29.7.0: jest@29.7.0: version "29.7.0" - resolved "https://registry.yarnpkg.com/jest/-/jest-29.7.0.tgz#994676fc24177f088f1c5e3737f5697204ff2613" + resolved "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz" integrity sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw== dependencies: "@jest/core" "^29.7.0" @@ -3917,17 +3917,17 @@ jest@29.7.0: jose@^4.15.1: version "4.15.9" - resolved "https://registry.yarnpkg.com/jose/-/jose-4.15.9.tgz#9b68eda29e9a0614c042fa29387196c7dd800100" + resolved "https://registry.npmjs.org/jose/-/jose-4.15.9.tgz" integrity sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA== js-tokens@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== js-yaml@3.13.1: version "3.13.1" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" + resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz" integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw== dependencies: argparse "^1.0.7" @@ -3935,7 +3935,7 @@ js-yaml@3.13.1: js-yaml@^3.13.1: version "3.14.1" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" + resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz" integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== dependencies: argparse "^1.0.7" @@ -3943,44 +3943,44 @@ js-yaml@^3.13.1: jsbn@1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-1.1.0.tgz#b01307cb29b618a1ed26ec79e911f803c4da0040" + resolved "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz" integrity sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A== jsbn@~0.1.0: version "0.1.1" - resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" + resolved "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz" integrity sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg== jsesc@^3.0.2: version "3.1.0" - resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-3.1.0.tgz#74d335a234f67ed19907fdadfac7ccf9d409825d" + resolved "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz" integrity sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA== json-bigint@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/json-bigint/-/json-bigint-1.0.0.tgz#ae547823ac0cad8398667f8cd9ef4730f5b01ff1" + resolved "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz" integrity sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ== dependencies: bignumber.js "^9.0.0" json-parse-even-better-errors@^2.3.0: version "2.3.1" - resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" + resolved "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz" integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== json-schema-traverse@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" + resolved "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz" integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== json5@^2.2.3: version "2.2.3" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" + resolved "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz" integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== jszip@^3.10.1: version "3.10.1" - resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.10.1.tgz#34aee70eb18ea1faec2f589208a157d1feb091c2" + resolved "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz" integrity sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g== dependencies: lie "~3.3.0" @@ -3990,7 +3990,7 @@ jszip@^3.10.1: jwa@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/jwa/-/jwa-2.0.0.tgz#a7e9c3f29dae94027ebcaf49975c9345593410fc" + resolved "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz" integrity sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA== dependencies: buffer-equal-constant-time "1.0.1" @@ -3999,7 +3999,7 @@ jwa@^2.0.0: jws@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/jws/-/jws-4.0.0.tgz#2d4e8cf6a318ffaa12615e9dec7e86e6c97310f4" + resolved "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz" integrity sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg== dependencies: jwa "^2.0.0" @@ -4007,39 +4007,39 @@ jws@^4.0.0: kleur@^3.0.3: version "3.0.3" - resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" + resolved "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz" integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== kuler@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/kuler/-/kuler-2.0.0.tgz#e2c570a3800388fb44407e851531c1d670b061b3" + resolved "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz" integrity sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A== leven@^3.1.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" + resolved "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz" integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== lie@~3.3.0: version "3.3.0" - resolved "https://registry.yarnpkg.com/lie/-/lie-3.3.0.tgz#dcf82dee545f46074daf200c7c1c5a08e0f40f6a" + resolved "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz" integrity sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ== dependencies: immediate "~3.0.5" lilconfig@~3.1.3: version "3.1.3" - resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-3.1.3.tgz#a1bcfd6257f9585bf5ae14ceeebb7b559025e4c4" + resolved "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz" integrity sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw== lines-and-columns@^1.1.6: version "1.2.4" - resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" + resolved "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz" integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== lint-staged@15.4.1: version "15.4.1" - resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-15.4.1.tgz#b34e3297ae13fdb2d99b3456e2dbd8e20798bced" + resolved "https://registry.npmjs.org/lint-staged/-/lint-staged-15.4.1.tgz" integrity sha512-P8yJuVRyLrm5KxCtFx+gjI5Bil+wO7wnTl7C3bXhvtTaAFGirzeB24++D0wGoUwxrUKecNiehemgCob9YL39NA== dependencies: chalk "~5.4.1" @@ -4055,7 +4055,7 @@ lint-staged@15.4.1: listr2@~8.2.5: version "8.2.5" - resolved "https://registry.yarnpkg.com/listr2/-/listr2-8.2.5.tgz#5c9db996e1afeb05db0448196d3d5f64fec2593d" + resolved "https://registry.npmjs.org/listr2/-/listr2-8.2.5.tgz" integrity sha512-iyAZCeyD+c1gPyE9qpFu8af0Y+MRtmKOncdGoA2S5EY8iFq99dmmvkNnHiWo+pj0s7yH7l3KPIgee77tKpXPWQ== dependencies: cli-truncate "^4.0.0" @@ -4067,29 +4067,29 @@ listr2@~8.2.5: locate-path@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" + resolved "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz" integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== dependencies: p-locate "^4.1.0" lodash.camelcase@^4.3.0: version "4.3.0" - resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" + resolved "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz" integrity sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA== lodash.memoize@^4.1.2: version "4.1.2" - resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" + resolved "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz" integrity sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag== lodash@^4.17.21, lodash@^4.17.5: version "4.17.21" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== log-symbols@^4.1.0: version "4.1.0" - resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" + resolved "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz" integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== dependencies: chalk "^4.1.0" @@ -4097,7 +4097,7 @@ log-symbols@^4.1.0: log-update@^6.1.0: version "6.1.0" - resolved "https://registry.yarnpkg.com/log-update/-/log-update-6.1.0.tgz#1a04ff38166f94647ae1af562f4bd6a15b1b7cd4" + resolved "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz" integrity sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w== dependencies: ansi-escapes "^7.0.0" @@ -4108,7 +4108,7 @@ log-update@^6.1.0: logform@^2.7.0: version "2.7.0" - resolved "https://registry.yarnpkg.com/logform/-/logform-2.7.0.tgz#cfca97528ef290f2e125a08396805002b2d060d1" + resolved "https://registry.npmjs.org/logform/-/logform-2.7.0.tgz" integrity sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ== dependencies: "@colors/colors" "1.6.0" @@ -4120,7 +4120,7 @@ logform@^2.7.0: long@^5.0.0: version "5.2.4" - resolved "https://registry.yarnpkg.com/long/-/long-5.2.4.tgz#ee651d5c7c25901cfca5e67220ae9911695e99b2" + resolved "https://registry.npmjs.org/long/-/long-5.2.4.tgz" integrity sha512-qtzLbJE8hq7VabR3mISmVGtoXP8KGc2Z/AT8OuqlYD7JTR3oqrgwdjnk07wpj1twXxYmgDXgoKVWUG/fReSzHg== lru-cache@^10.2.0: @@ -4130,50 +4130,50 @@ lru-cache@^10.2.0: lru-cache@^5.1.1: version "5.1.1" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" + resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz" integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== dependencies: yallist "^3.0.2" lru-cache@^6.0.0: version "6.0.0" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz" integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== dependencies: yallist "^4.0.0" lru-cache@^7.14.1: version "7.18.3" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.18.3.tgz#f793896e0fd0e954a59dfdd82f0773808df6aa89" + resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz" integrity sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA== make-dir@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-4.0.0.tgz#c3c2307a771277cd9638305f915c29ae741b614e" + resolved "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz" integrity sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw== dependencies: semver "^7.5.3" make-error@^1.1.1, make-error@^1.3.6: version "1.3.6" - resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" + resolved "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz" integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== makeerror@1.0.12: version "1.0.12" - resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.12.tgz#3e5dd2079a82e812e983cc6610c4a2cb0eaa801a" + resolved "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz" integrity sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg== dependencies: tmpl "1.0.5" merge-stream@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" + resolved "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz" integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== micromatch@^4.0.4, micromatch@~4.0.8: version "4.0.8" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202" + resolved "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz" integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== dependencies: braces "^3.0.3" @@ -4181,41 +4181,41 @@ micromatch@^4.0.4, micromatch@~4.0.8: mime-db@1.52.0: version "1.52.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz" integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== mime-types@^2.1.12: version "2.1.35" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz" integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== dependencies: mime-db "1.52.0" mimic-fn@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + resolved "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== mimic-fn@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-4.0.0.tgz#60a90550d5cb0b239cca65d893b1a53b29871ecc" + resolved "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz" integrity sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw== mimic-function@^5.0.0: version "5.0.1" - resolved "https://registry.yarnpkg.com/mimic-function/-/mimic-function-5.0.1.tgz#acbe2b3349f99b9deaca7fb70e48b83e94e67076" + resolved "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz" integrity sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA== minimatch@^3.0.4, minimatch@^3.1.2: version "3.1.2" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz" integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== dependencies: brace-expansion "^1.1.7" minimatch@^5.0.1: version "5.1.6" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz" integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== dependencies: brace-expansion "^2.0.1" @@ -4229,7 +4229,7 @@ minimatch@^9.0.0: minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6: version "1.2.8" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== minipass@^5.0.0: @@ -4244,137 +4244,137 @@ minipass@^5.0.0: mkdirp@^0.5.1: version "0.5.6" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" + resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz" integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== dependencies: minimist "^1.2.6" ms@2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz" integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== ms@^2.1.1, ms@^2.1.3: version "2.1.3" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== mute-stream@0.0.8: version "0.0.8" - resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" + resolved "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz" integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== nan@^2.19.0, nan@^2.20.0: version "2.22.0" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.22.0.tgz#31bc433fc33213c97bad36404bb68063de604de3" + resolved "https://registry.npmjs.org/nan/-/nan-2.22.0.tgz" integrity sha512-nbajikzWTMwsW+eSsNm3QwlOs7het9gGJU5dDZzRTQGk03vyBOauxgI4VakDzE0PtsGTmXPsXTbbjVhRwR5mpw== natural-compare@^1.4.0: version "1.4.0" - resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + resolved "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz" integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== netmask@^2.0.2: version "2.0.2" - resolved "https://registry.yarnpkg.com/netmask/-/netmask-2.0.2.tgz#8b01a07644065d536383835823bc52004ebac5e7" + resolved "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz" integrity sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg== node-fetch@^2.6.9, node-fetch@^2.7.0: version "2.7.0" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d" + resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz" integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== dependencies: whatwg-url "^5.0.0" node-int64@^0.4.0: version "0.4.0" - resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" + resolved "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz" integrity sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw== node-releases@^2.0.19: version "2.0.19" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.19.tgz#9e445a52950951ec4d177d843af370b411caf314" + resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz" integrity sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw== normalize-path@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + resolved "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== npm-run-path@^4.0.1: version "4.0.1" - resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" + resolved "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz" integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== dependencies: path-key "^3.0.0" npm-run-path@^5.1.0: version "5.3.0" - resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-5.3.0.tgz#e23353d0ebb9317f174e93417e4a4d82d0249e9f" + resolved "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz" integrity sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ== dependencies: path-key "^4.0.0" object-hash@^2.2.0: version "2.2.0" - resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-2.2.0.tgz#5ad518581eefc443bd763472b8ff2e9c2c0d54a5" + resolved "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz" integrity sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw== object-hash@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-3.0.0.tgz#73f97f753e7baffc0e2cc9d6e079079744ac82e9" + resolved "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz" integrity sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw== oidc-token-hash@^5.0.3: version "5.0.3" - resolved "https://registry.yarnpkg.com/oidc-token-hash/-/oidc-token-hash-5.0.3.tgz#9a229f0a1ce9d4fc89bcaee5478c97a889e7b7b6" + resolved "https://registry.npmjs.org/oidc-token-hash/-/oidc-token-hash-5.0.3.tgz" integrity sha512-IF4PcGgzAr6XXSff26Sk/+P4KZFJVuHAJZj3wgO3vX2bMdNVp/QXTP3P7CEm9V1IdG8lDLY3HhiqpsE/nOwpPw== on-finished@^2.3.0: version "2.4.1" - resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" + resolved "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz" integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== dependencies: ee-first "1.1.1" once@^1.3.1, once@^1.4.0: version "1.4.0" - resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + resolved "https://registry.npmjs.org/once/-/once-1.4.0.tgz" integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== dependencies: wrappy "1" one-time@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/one-time/-/one-time-1.0.0.tgz#e06bc174aed214ed58edede573b433bbf827cb45" + resolved "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz" integrity sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g== dependencies: fn.name "1.x.x" onetime@^5.1.0, onetime@^5.1.2: version "5.1.2" - resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" + resolved "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz" integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== dependencies: mimic-fn "^2.1.0" onetime@^6.0.0: version "6.0.0" - resolved "https://registry.yarnpkg.com/onetime/-/onetime-6.0.0.tgz#7c24c18ed1fd2e9bca4bd26806a33613c77d34b4" + resolved "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz" integrity sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ== dependencies: mimic-fn "^4.0.0" onetime@^7.0.0: version "7.0.0" - resolved "https://registry.yarnpkg.com/onetime/-/onetime-7.0.0.tgz#9f16c92d8c9ef5120e3acd9dd9957cceecc1ab60" + resolved "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz" integrity sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ== dependencies: mimic-function "^5.0.0" openid-client@5.6.1: version "5.6.1" - resolved "https://registry.yarnpkg.com/openid-client/-/openid-client-5.6.1.tgz#8f7526a50c290a5e28a7fe21b3ece3107511bc73" + resolved "https://registry.npmjs.org/openid-client/-/openid-client-5.6.1.tgz" integrity sha512-PtrWsY+dXg6y8mtMPyL/namZSYVz8pjXz3yJiBNZsEdCnu9miHLB4ELVC85WvneMKo2Rg62Ay7NkuCpM0bgiLQ== dependencies: jose "^4.15.1" @@ -4384,7 +4384,7 @@ openid-client@5.6.1: ora@5.4.1, ora@^5.4.1: version "5.4.1" - resolved "https://registry.yarnpkg.com/ora/-/ora-5.4.1.tgz#1b2678426af4ac4a509008e5e4ac9e9959db9e18" + resolved "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz" integrity sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ== dependencies: bl "^4.1.0" @@ -4399,38 +4399,38 @@ ora@5.4.1, ora@^5.4.1: os-tmpdir@~1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + resolved "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz" integrity sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g== p-limit@^2.2.0: version "2.3.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + resolved "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz" integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== dependencies: p-try "^2.0.0" p-limit@^3.1.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + resolved "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz" integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== dependencies: yocto-queue "^0.1.0" p-locate@^4.1.0: version "4.1.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" + resolved "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz" integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== dependencies: p-limit "^2.2.0" p-try@^2.0.0: version "2.2.0" - resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + resolved "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== pac-proxy-agent@^7.1.0: version "7.1.0" - resolved "https://registry.yarnpkg.com/pac-proxy-agent/-/pac-proxy-agent-7.1.0.tgz#da7c3b5c4cccc6655aaafb701ae140fb23f15df2" + resolved "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.1.0.tgz" integrity sha512-Z5FnLVVZSnX7WjBg0mhDtydeRZ1xMcATZThjySQUHqr+0ksP8kqaw23fNKkaaN/Z8gwLUs/W7xdl0I75eP2Xyw== dependencies: "@tootallnate/quickjs-emscripten" "^0.23.0" @@ -4444,7 +4444,7 @@ pac-proxy-agent@^7.1.0: pac-resolver@^7.0.1: version "7.0.1" - resolved "https://registry.yarnpkg.com/pac-resolver/-/pac-resolver-7.0.1.tgz#54675558ea368b64d210fd9c92a640b5f3b8abb6" + resolved "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.1.tgz" integrity sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg== dependencies: degenerator "^5.0.0" @@ -4452,17 +4452,17 @@ pac-resolver@^7.0.1: packageurl-js@^2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/packageurl-js/-/packageurl-js-2.0.1.tgz#a8fa43a64971b5dd0dca5fb904b950a6cc317a6f" + resolved "https://registry.npmjs.org/packageurl-js/-/packageurl-js-2.0.1.tgz" integrity sha512-N5ixXjzTy4QDQH0Q9YFjqIWd6zH6936Djpl2m9QNFmDv5Fum8q8BjkpAcHNMzOFE0IwQrFhJWex3AN6kS0OSwg== pako@~1.0.2: version "1.0.11" - resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" + resolved "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz" integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw== parse-json@^5.2.0: version "5.2.0" - resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" + resolved "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz" integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== dependencies: "@babel/code-frame" "^7.0.0" @@ -4472,22 +4472,22 @@ parse-json@^5.2.0: path-exists@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + resolved "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz" integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== path-key@^3.0.0, path-key@^3.1.0: version "3.1.1" - resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + resolved "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== path-key@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/path-key/-/path-key-4.0.0.tgz#295588dc3aee64154f877adb9d780b81c554bf18" + resolved "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz" integrity sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ== path-parse@^1.0.7: version "1.0.7" - resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + resolved "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== path-scurry@^1.6.4: @@ -4500,39 +4500,39 @@ path-scurry@^1.6.4: picocolors@^1.0.0, picocolors@^1.1.1: version "1.1.1" - resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" + resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz" integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== picomatch@^2.0.4, picomatch@^2.2.3, picomatch@^2.3.1: version "2.3.1" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== pidtree@~0.6.0: version "0.6.0" - resolved "https://registry.yarnpkg.com/pidtree/-/pidtree-0.6.0.tgz#90ad7b6d42d5841e69e0a2419ef38f8883aa057c" + resolved "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz" integrity sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g== pirates@^4.0.4: version "4.0.6" - resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.6.tgz#3018ae32ecfcff6c29ba2267cbf21166ac1f36b9" + resolved "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz" integrity sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg== pkg-dir@^4.2.0: version "4.2.0" - resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" + resolved "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz" integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== dependencies: find-up "^4.0.0" prettier@3.4.2: version "3.4.2" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.4.2.tgz#a5ce1fb522a588bf2b78ca44c6e6fe5aa5a2b13f" + resolved "https://registry.npmjs.org/prettier/-/prettier-3.4.2.tgz" integrity sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ== pretty-format@^29.0.0, pretty-format@^29.7.0: version "29.7.0" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.7.0.tgz#ca42c758310f365bfa71a0bda0a807160b776812" + resolved "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz" integrity sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ== dependencies: "@jest/schemas" "^29.6.3" @@ -4541,12 +4541,12 @@ pretty-format@^29.0.0, pretty-format@^29.7.0: process-nextick-args@~2.0.0: version "2.0.1" - resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + resolved "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz" integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== prompts@^2.0.1: version "2.4.2" - resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.2.tgz#7b57e73b3a48029ad10ebd44f74b01722a4cb069" + resolved "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz" integrity sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q== dependencies: kleur "^3.0.3" @@ -4554,14 +4554,14 @@ prompts@^2.0.1: proto3-json-serializer@^2.0.2: version "2.0.2" - resolved "https://registry.yarnpkg.com/proto3-json-serializer/-/proto3-json-serializer-2.0.2.tgz#5b705203b4d58f3880596c95fad64902617529dd" + resolved "https://registry.npmjs.org/proto3-json-serializer/-/proto3-json-serializer-2.0.2.tgz" integrity sha512-SAzp/O4Yh02jGdRc+uIrGoe87dkN/XtwxfZ4ZyafJHymd79ozp5VG5nyZ7ygqPM5+cpLDjjGnYFUkngonyDPOQ== dependencies: protobufjs "^7.2.5" protobufjs@^7.2.5, protobufjs@^7.3.2: version "7.4.0" - resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-7.4.0.tgz#7efe324ce9b3b61c82aae5de810d287bc08a248a" + resolved "https://registry.npmjs.org/protobufjs/-/protobufjs-7.4.0.tgz" integrity sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw== dependencies: "@protobufjs/aspromise" "^1.1.2" @@ -4579,7 +4579,7 @@ protobufjs@^7.2.5, protobufjs@^7.3.2: proxy-agent@^6.4.0: version "6.5.0" - resolved "https://registry.yarnpkg.com/proxy-agent/-/proxy-agent-6.5.0.tgz#9e49acba8e4ee234aacb539f89ed9c23d02f232d" + resolved "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.5.0.tgz" integrity sha512-TmatMXdr2KlRiA2CyDu8GqR8EjahTG3aY3nXjdzFyoZbmB8hrBsTyMezhULIXKnC0jpfjlmiZ3+EaCzoInSu/A== dependencies: agent-base "^7.1.2" @@ -4593,12 +4593,12 @@ proxy-agent@^6.4.0: proxy-from-env@^1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" + resolved "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz" integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== pump@^3.0.0: version "3.0.2" - resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.2.tgz#836f3edd6bc2ee599256c924ffe0d88573ddcbf8" + resolved "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz" integrity sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw== dependencies: end-of-stream "^1.1.0" @@ -4606,7 +4606,7 @@ pump@^3.0.0: pumpify@^2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/pumpify/-/pumpify-2.0.1.tgz#abfc7b5a621307c728b551decbbefb51f0e4aa1e" + resolved "https://registry.npmjs.org/pumpify/-/pumpify-2.0.1.tgz" integrity sha512-m7KOje7jZxrmutanlkS1daj1dS6z6BgslzOXmcSEpIlCxM3VJH7lG5QLeck/6hgF6F4crFf01UtQmNsJfweTAw== dependencies: duplexify "^4.1.1" @@ -4615,12 +4615,12 @@ pumpify@^2.0.1: pure-rand@^6.0.0: version "6.1.0" - resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-6.1.0.tgz#d173cf23258231976ccbdb05247c9787957604f2" + resolved "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz" integrity sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA== rc@^1.2.8: version "1.2.8" - resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" + resolved "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz" integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== dependencies: deep-extend "^0.6.0" @@ -4630,12 +4630,12 @@ rc@^1.2.8: react-is@^18.0.0: version "18.3.1" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.3.1.tgz#e83557dc12eae63a99e003a46388b1dcbb44db7e" + resolved "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz" integrity sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg== readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.2: version "3.6.2" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" + resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz" integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== dependencies: inherits "^2.0.3" @@ -4644,7 +4644,7 @@ readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.2: readable-stream@~2.3.6: version "2.3.8" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" + resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz" integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== dependencies: core-util-is "~1.0.0" @@ -4657,39 +4657,39 @@ readable-stream@~2.3.6: reflect-metadata@^0.1.12: version "0.1.14" - resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.14.tgz#24cf721fe60677146bb77eeb0e1f9dece3d65859" + resolved "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.14.tgz" integrity sha512-ZhYeb6nRaXCfhnndflDK8qI6ZQ/YcWZCISRAWICW9XYqMUwjZM9Z0DveWX/ABN01oxSHwVxKQmxeYZSsm0jh5A== require-directory@^2.1.1: version "2.1.1" - resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + resolved "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz" integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== require-from-string@^2.0.2: version "2.0.2" - resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" + resolved "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz" integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== resolve-cwd@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" + resolved "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz" integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg== dependencies: resolve-from "^5.0.0" resolve-from@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" + resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz" integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== resolve.exports@^2.0.0: version "2.0.3" - resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.3.tgz#41955e6f1b4013b7586f873749a635dea07ebe3f" + resolved "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz" integrity sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A== resolve@^1.20.0, resolve@^1.3.2: version "1.22.10" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.10.tgz#b663e83ffb09bbf2386944736baae803029b8b39" + resolved "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz" integrity sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w== dependencies: is-core-module "^2.16.0" @@ -4698,7 +4698,7 @@ resolve@^1.20.0, resolve@^1.3.2: restore-cursor@^3.1.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" + resolved "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz" integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== dependencies: onetime "^5.1.0" @@ -4706,7 +4706,7 @@ restore-cursor@^3.1.0: restore-cursor@^5.0.0: version "5.1.0" - resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-5.1.0.tgz#0766d95699efacb14150993f55baf0953ea1ebe7" + resolved "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz" integrity sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA== dependencies: onetime "^7.0.0" @@ -4714,7 +4714,7 @@ restore-cursor@^5.0.0: retry-request@^7.0.0: version "7.0.2" - resolved "https://registry.yarnpkg.com/retry-request/-/retry-request-7.0.2.tgz#60bf48cfb424ec01b03fca6665dee91d06dd95f3" + resolved "https://registry.npmjs.org/retry-request/-/retry-request-7.0.2.tgz" integrity sha512-dUOvLMJ0/JJYEn8NrpOaGNE7X3vpI5XlZS/u0ANjqtcZVKnIxP7IgCFwrKTxENw29emmwug53awKtaMm4i9g5w== dependencies: "@types/request" "^2.48.8" @@ -4723,115 +4723,115 @@ retry-request@^7.0.0: retry@0.12.0: version "0.12.0" - resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" + resolved "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz" integrity sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow== rfdc@^1.4.1: version "1.4.1" - resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.4.1.tgz#778f76c4fb731d93414e8f925fbecf64cce7f6ca" + resolved "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz" integrity sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA== rimraf@^3.0.2: version "3.0.2" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + resolved "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz" integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== dependencies: glob "^7.1.3" run-async@^2.4.0: version "2.4.1" - resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" + resolved "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz" integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== rxjs@^6.6.7: version "6.6.7" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9" + resolved "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz" integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ== dependencies: tslib "^1.9.0" rxjs@^7.5.5: version "7.8.1" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.1.tgz#6f6f3d99ea8044291efd92e7c7fcf562c4057543" + resolved "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz" integrity sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg== dependencies: tslib "^2.1.0" safe-buffer@^5.0.1, safe-buffer@^5.2.1, safe-buffer@~5.2.0: version "5.2.1" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== safe-stable-stringify@^2.3.1: version "2.5.0" - resolved "https://registry.yarnpkg.com/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz#4ca2f8e385f2831c432a719b108a3bf7af42a1dd" + resolved "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz" integrity sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA== "safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: version "2.1.2" - resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== sax@>=0.6.0: version "1.4.1" - resolved "https://registry.yarnpkg.com/sax/-/sax-1.4.1.tgz#44cc8988377f126304d3b3fc1010c733b929ef0f" + resolved "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz" integrity sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg== semver@7.6.3, semver@^7.5.3, semver@^7.5.4, semver@^7.6.3: version "7.6.3" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143" + resolved "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz" integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== semver@^5.3.0: version "5.7.2" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" + resolved "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz" integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== semver@^6.3.0, semver@^6.3.1: version "6.3.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + resolved "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== setimmediate@^1.0.5: version "1.0.5" - resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" + resolved "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz" integrity sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA== shebang-command@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz" integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== dependencies: shebang-regex "^3.0.0" shebang-regex@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== siginfo@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/siginfo/-/siginfo-2.0.0.tgz#32e76c70b79724e3bb567cb9d543eb858ccfaf30" + resolved "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz" integrity sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g== signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: version "3.0.7" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" + resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== signal-exit@^4.1.0: version "4.1.0" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" + resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz" integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== simple-git@3.16.0: version "3.16.0" - resolved "https://registry.yarnpkg.com/simple-git/-/simple-git-3.16.0.tgz#421773e24680f5716999cc4a1d60127b4b6a9dec" + resolved "https://registry.npmjs.org/simple-git/-/simple-git-3.16.0.tgz" integrity sha512-zuWYsOLEhbJRWVxpjdiXl6eyAyGo/KzVW+KFhhw9MqEEJttcq+32jTWSGyxTdf9e/YCohxRE+9xpWFj9FdiJNw== dependencies: "@kwsites/file-exists" "^1.1.1" @@ -4840,24 +4840,24 @@ simple-git@3.16.0: simple-swizzle@^0.2.2: version "0.2.2" - resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a" + resolved "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz" integrity sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg== dependencies: is-arrayish "^0.3.1" sisteransi@^1.0.5: version "1.0.5" - resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" + resolved "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz" integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== slash@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + resolved "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz" integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== slice-ansi@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-5.0.0.tgz#b73063c57aa96f9cd881654b15294d95d285c42a" + resolved "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz" integrity sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ== dependencies: ansi-styles "^6.0.0" @@ -4865,7 +4865,7 @@ slice-ansi@^5.0.0: slice-ansi@^7.1.0: version "7.1.0" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-7.1.0.tgz#cd6b4655e298a8d1bdeb04250a433094b347b9a9" + resolved "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.0.tgz" integrity sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg== dependencies: ansi-styles "^6.2.1" @@ -4873,12 +4873,12 @@ slice-ansi@^7.1.0: smart-buffer@^4.2.0: version "4.2.0" - resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae" + resolved "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz" integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg== socks-proxy-agent@^8.0.5: version "8.0.5" - resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz#b9cdb4e7e998509d7659d689ce7697ac21645bee" + resolved "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz" integrity sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw== dependencies: agent-base "^7.1.2" @@ -4887,7 +4887,7 @@ socks-proxy-agent@^8.0.5: socks@^2.8.3: version "2.8.3" - resolved "https://registry.yarnpkg.com/socks/-/socks-2.8.3.tgz#1ebd0f09c52ba95a09750afe3f3f9f724a800cb5" + resolved "https://registry.npmjs.org/socks/-/socks-2.8.3.tgz" integrity sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw== dependencies: ip-address "^9.0.5" @@ -4895,7 +4895,7 @@ socks@^2.8.3: source-map-support@0.5.13: version "0.5.13" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.13.tgz#31b24a9c2e73c2de85066c0feb7d44767ed52932" + resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz" integrity sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w== dependencies: buffer-from "^1.0.0" @@ -4903,22 +4903,22 @@ source-map-support@0.5.13: source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: version "0.6.1" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== sprintf-js@^1.1.3: version "1.1.3" - resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.3.tgz#4914b903a2f8b685d17fdf78a70e917e872e444a" + resolved "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz" integrity sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA== sprintf-js@~1.0.2: version "1.0.3" - resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + resolved "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz" integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== ssh2-streams@0.4.10: version "0.4.10" - resolved "https://registry.yarnpkg.com/ssh2-streams/-/ssh2-streams-0.4.10.tgz#48ef7e8a0e39d8f2921c30521d56dacb31d23a34" + resolved "https://registry.npmjs.org/ssh2-streams/-/ssh2-streams-0.4.10.tgz" integrity sha512-8pnlMjvnIZJvmTzUIIA5nT4jr2ZWNNVHwyXfMGdRJbug9TpI3kd99ffglgfSWqujVv/0gxwMsDn9j9RVst8yhQ== dependencies: asn1 "~0.2.0" @@ -4927,7 +4927,7 @@ ssh2-streams@0.4.10: ssh2@^1.15.0: version "1.16.0" - resolved "https://registry.yarnpkg.com/ssh2/-/ssh2-1.16.0.tgz#79221d40cbf4d03d07fe881149de0a9de928c9f0" + resolved "https://registry.npmjs.org/ssh2/-/ssh2-1.16.0.tgz" integrity sha512-r1X4KsBGedJqo7h8F5c4Ybpcr5RjyP+aWIG007uBPRjmdQWfEiVLzSK71Zji1B9sKxwaCvD8y8cwSkYrlLiRRg== dependencies: asn1 "^0.2.6" @@ -4938,7 +4938,7 @@ ssh2@^1.15.0: sshpk@1.16.1: version "1.16.1" - resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877" + resolved "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz" integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg== dependencies: asn1 "~0.2.3" @@ -4953,46 +4953,46 @@ sshpk@1.16.1: stack-trace@0.0.x: version "0.0.10" - resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0" + resolved "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz" integrity sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg== stack-utils@^2.0.3: version "2.0.6" - resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.6.tgz#aaf0748169c02fc33c8232abccf933f54a1cc34f" + resolved "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz" integrity sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ== dependencies: escape-string-regexp "^2.0.0" stackback@0.0.2: version "0.0.2" - resolved "https://registry.yarnpkg.com/stackback/-/stackback-0.0.2.tgz#1ac8a0d9483848d1695e418b6d031a3c3ce68e3b" + resolved "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz" integrity sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw== stream-events@^1.0.5: version "1.0.5" - resolved "https://registry.yarnpkg.com/stream-events/-/stream-events-1.0.5.tgz#bbc898ec4df33a4902d892333d47da9bf1c406d5" + resolved "https://registry.npmjs.org/stream-events/-/stream-events-1.0.5.tgz" integrity sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg== dependencies: stubs "^3.0.0" stream-shift@^1.0.2: version "1.0.3" - resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.3.tgz#85b8fab4d71010fc3ba8772e8046cc49b8a3864b" + resolved "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.3.tgz" integrity sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ== streamsearch@~0.1.2: version "0.1.2" - resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-0.1.2.tgz#808b9d0e56fc273d809ba57338e929919a1a9f1a" + resolved "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz" integrity sha512-jos8u++JKm0ARcSUTAZXOVC0mSox7Bhn6sBgty73P1f3JGf7yG2clTbBNHUdde/kdvP2FESam+vM6l8jBrNxHA== string-argv@~0.3.2: version "0.3.2" - resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.2.tgz#2b6d0ef24b656274d957d54e0a4bbf6153dc02b6" + resolved "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz" integrity sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q== string-length@^4.0.1: version "4.0.2" - resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a" + resolved "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz" integrity sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ== dependencies: char-regex "^1.0.2" @@ -5000,7 +5000,7 @@ string-length@^4.0.1: string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== dependencies: emoji-regex "^8.0.0" @@ -5009,7 +5009,7 @@ string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: string-width@^7.0.0: version "7.2.0" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-7.2.0.tgz#b5bb8e2165ce275d4d43476dd2700ad9091db6dc" + resolved "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz" integrity sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ== dependencies: emoji-regex "^10.3.0" @@ -5018,91 +5018,91 @@ string-width@^7.0.0: string_decoder@^1.1.1: version "1.3.0" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz" integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== dependencies: safe-buffer "~5.2.0" string_decoder@~1.1.1: version "1.1.1" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz" integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== dependencies: safe-buffer "~5.1.0" strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== dependencies: ansi-regex "^5.0.1" strip-ansi@^7.1.0: version "7.1.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz" integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== dependencies: ansi-regex "^6.0.1" strip-bom@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878" + resolved "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz" integrity sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w== strip-final-newline@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" + resolved "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz" integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== strip-final-newline@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-3.0.0.tgz#52894c313fbff318835280aed60ff71ebf12b8fd" + resolved "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz" integrity sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw== strip-json-comments@^3.1.1: version "3.1.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== strip-json-comments@~2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz" integrity sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ== strnum@^1.0.5: version "1.0.5" - resolved "https://registry.yarnpkg.com/strnum/-/strnum-1.0.5.tgz#5c4e829fe15ad4ff0d20c3db5ac97b73c9b072db" + resolved "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz" integrity sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA== stubs@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/stubs/-/stubs-3.0.0.tgz#e8d2ba1fa9c90570303c030b6900f7d5f89abe5b" + resolved "https://registry.npmjs.org/stubs/-/stubs-3.0.0.tgz" integrity sha512-PdHt7hHUJKxvTCgbKX9C1V/ftOcjJQgz8BZwNfV5c4B6dcGqlpelTbJ999jBGZ2jYiPAwcX5dP6oBwVlBlUbxw== supports-color@^5.3.0: version "5.5.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz" integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== dependencies: has-flag "^3.0.0" supports-color@^7.0.0, supports-color@^7.1.0: version "7.2.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz" integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== dependencies: has-flag "^4.0.0" supports-color@^8.0.0: version "8.1.1" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz" integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== dependencies: has-flag "^4.0.0" supports-hyperlinks@^2.0.0: version "2.3.0" - resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz#3943544347c1ff90b15effb03fc14ae45ec10624" + resolved "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz" integrity sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA== dependencies: has-flag "^4.0.0" @@ -5110,12 +5110,12 @@ supports-hyperlinks@^2.0.0: supports-preserve-symlinks-flag@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + resolved "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== teeny-request@^9.0.0: version "9.0.0" - resolved "https://registry.yarnpkg.com/teeny-request/-/teeny-request-9.0.0.tgz#18140de2eb6595771b1b02203312dfad79a4716d" + resolved "https://registry.npmjs.org/teeny-request/-/teeny-request-9.0.0.tgz" integrity sha512-resvxdc6Mgb7YEThw6G6bExlXKkv6+YbuzGg9xuXxSgxJF7Ozs+o8Y9+2R3sArdWdW8nOokoQb1yrpFB0pQK2g== dependencies: http-proxy-agent "^5.0.0" @@ -5126,7 +5126,7 @@ teeny-request@^9.0.0: terminal-link@2.1.1: version "2.1.1" - resolved "https://registry.yarnpkg.com/terminal-link/-/terminal-link-2.1.1.tgz#14a64a27ab3c0df933ea546fba55f2d078edc994" + resolved "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz" integrity sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ== dependencies: ansi-escapes "^4.2.1" @@ -5134,7 +5134,7 @@ terminal-link@2.1.1: test-exclude@^6.0.0: version "6.0.0" - resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e" + resolved "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz" integrity sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w== dependencies: "@istanbuljs/schema" "^0.1.2" @@ -5143,51 +5143,51 @@ test-exclude@^6.0.0: text-hex@1.0.x: version "1.0.0" - resolved "https://registry.yarnpkg.com/text-hex/-/text-hex-1.0.0.tgz#69dc9c1b17446ee79a92bf5b884bb4b9127506f5" + resolved "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz" integrity sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg== through@^2.3.6: version "2.3.8" - resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + resolved "https://registry.npmjs.org/through/-/through-2.3.8.tgz" integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== tiny-async-pool@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/tiny-async-pool/-/tiny-async-pool-2.1.0.tgz#3ec126568c18a7916912fb9fbecf812337ec6b84" + resolved "https://registry.npmjs.org/tiny-async-pool/-/tiny-async-pool-2.1.0.tgz" integrity sha512-ltAHPh/9k0STRQqaoUX52NH4ZQYAJz24ZAEwf1Zm+HYg3l9OXTWeqWKyYsHu40wF/F0rxd2N2bk5sLvX2qlSvg== tmp@^0.0.33: version "0.0.33" - resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" + resolved "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz" integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== dependencies: os-tmpdir "~1.0.2" tmpl@1.0.5: version "1.0.5" - resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc" + resolved "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz" integrity sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw== to-regex-range@^5.0.1: version "5.0.1" - resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + resolved "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz" integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== dependencies: is-number "^7.0.0" tr46@~0.0.3: version "0.0.3" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" + resolved "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz" integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== triple-beam@^1.3.0: version "1.4.1" - resolved "https://registry.yarnpkg.com/triple-beam/-/triple-beam-1.4.1.tgz#6fde70271dc6e5d73ca0c3b24e2d92afb7441984" + resolved "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz" integrity sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg== ts-jest@29.2.5: version "29.2.5" - resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.2.5.tgz#591a3c108e1f5ebd013d3152142cb5472b399d63" + resolved "https://registry.npmjs.org/ts-jest/-/ts-jest-29.2.5.tgz" integrity sha512-KD8zB2aAZrcKIdGk4OwpJggeLcH1FgrICqDSROWqlnJXGCXK4Mn6FcdK2B6670Xr73lHMG1kHw8R87A0ecZ+vA== dependencies: bs-logger "^0.2.6" @@ -5202,7 +5202,7 @@ ts-jest@29.2.5: ts-node@10.9.2: version "10.9.2" - resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.2.tgz#70f021c9e185bccdca820e26dc413805c101c71f" + resolved "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz" integrity sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ== dependencies: "@cspotcode/source-map-support" "^0.8.0" @@ -5221,22 +5221,22 @@ ts-node@10.9.2: tslib@^1.7.1, tslib@^1.8.0, tslib@^1.8.1, tslib@^1.9.0: version "1.14.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" + resolved "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== tslib@^2.0.1, tslib@^2.1.0, tslib@^2.6.2: version "2.8.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" + resolved "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz" integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== tslint-config-prettier@1.18.0: version "1.18.0" - resolved "https://registry.yarnpkg.com/tslint-config-prettier/-/tslint-config-prettier-1.18.0.tgz#75f140bde947d35d8f0d238e0ebf809d64592c37" + resolved "https://registry.npmjs.org/tslint-config-prettier/-/tslint-config-prettier-1.18.0.tgz" integrity sha512-xPw9PgNPLG3iKRxmK7DWr+Ea/SzrvfHtjFt5LBl61gk2UBG/DB9kCXRjv+xyIU1rUtnayLeMUVJBcMX8Z17nDg== tslint-consistent-codestyle@1.16.0: version "1.16.0" - resolved "https://registry.yarnpkg.com/tslint-consistent-codestyle/-/tslint-consistent-codestyle-1.16.0.tgz#52348ea899a7e025b37cc6545751c6a566a19077" + resolved "https://registry.npmjs.org/tslint-consistent-codestyle/-/tslint-consistent-codestyle-1.16.0.tgz" integrity sha512-ebR/xHyMEuU36hGNOgCfjGBNYxBPixf0yU1Yoo6s3BrpBRFccjPOmIVaVvQsWAUAMdmfzHOCihVkcaMfimqvHw== dependencies: "@fimbul/bifrost" "^0.21.0" @@ -5245,7 +5245,7 @@ tslint-consistent-codestyle@1.16.0: tslint@5.20.1: version "5.20.1" - resolved "https://registry.yarnpkg.com/tslint/-/tslint-5.20.1.tgz#e401e8aeda0152bc44dd07e614034f3f80c67b7d" + resolved "https://registry.npmjs.org/tslint/-/tslint-5.20.1.tgz" integrity sha512-EcMxhzCFt8k+/UP5r8waCf/lzmeSyVlqxqMEDQE7rWYiQky8KpIBz1JAoYXfROHrPZ1XXd43q8yQnULOLiBRQg== dependencies: "@babel/code-frame" "^7.0.0" @@ -5264,56 +5264,56 @@ tslint@5.20.1: tsutils@^2.29.0: version "2.29.0" - resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.29.0.tgz#32b488501467acbedd4b85498673a0812aca0b99" + resolved "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz" integrity sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA== dependencies: tslib "^1.8.1" tsutils@^3.5.0: version "3.21.0" - resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" + resolved "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz" integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA== dependencies: tslib "^1.8.1" tweetnacl@^0.14.3, tweetnacl@~0.14.0: version "0.14.5" - resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" + resolved "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz" integrity sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA== typanion@^3.14.0, typanion@^3.8.0: version "3.14.0" - resolved "https://registry.yarnpkg.com/typanion/-/typanion-3.14.0.tgz#a766a91810ce8258033975733e836c43a2929b94" + resolved "https://registry.npmjs.org/typanion/-/typanion-3.14.0.tgz" integrity sha512-ZW/lVMRabETuYCd9O9ZvMhAh8GslSqaUjxmK/JLPCh6l73CvLBiuXswj/+7LdnWOgYsQ130FqLzFz5aGT4I3Ug== type-detect@4.0.8: version "4.0.8" - resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" + resolved "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz" integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== type-fest@^0.21.3: version "0.21.3" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" + resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz" integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== typescript@5.3.3: version "5.3.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.3.3.tgz#b3ce6ba258e72e6305ba66f5c9b452aaee3ffe37" + resolved "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz" integrity sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw== undici-types@~5.26.4: version "5.26.5" - resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" + resolved "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz" integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== undici-types@~6.20.0: version "6.20.0" - resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.20.0.tgz#8171bf22c1f588d1554d55bf204bc624af388433" + resolved "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz" integrity sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg== update-browserslist-db@^1.1.1: version "1.1.2" - resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.2.tgz#97e9c96ab0ae7bcac08e9ae5151d26e6bc6b5580" + resolved "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.2.tgz" integrity sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg== dependencies: escalade "^3.2.0" @@ -5321,27 +5321,27 @@ update-browserslist-db@^1.1.1: util-deprecate@^1.0.1, util-deprecate@~1.0.1: version "1.0.2" - resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== uuid@^8.0.0: version "8.3.2" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" + resolved "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== uuid@^9.0.0, uuid@^9.0.1: version "9.0.1" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30" + resolved "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz" integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA== v8-compile-cache-lib@^3.0.1: version "3.0.1" - resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" + resolved "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz" integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== v8-to-istanbul@^9.0.1: version "9.3.0" - resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz#b9572abfa62bd556c16d75fdebc1a411d5ff3175" + resolved "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz" integrity sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA== dependencies: "@jridgewell/trace-mapping" "^0.3.12" @@ -5350,31 +5350,31 @@ v8-to-istanbul@^9.0.1: valid-url@1.0.9: version "1.0.9" - resolved "https://registry.yarnpkg.com/valid-url/-/valid-url-1.0.9.tgz#1c14479b40f1397a75782f115e4086447433a200" + resolved "https://registry.npmjs.org/valid-url/-/valid-url-1.0.9.tgz" integrity sha512-QQDsV8OnSf5Uc30CKSwG9lnhMPe6exHtTXLRYX8uMwKENy640pU+2BgBL0LRbDh/eYRahNCS7aewCx0wf3NYVA== walker@^1.0.8: version "1.0.8" - resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.8.tgz#bd498db477afe573dc04185f011d3ab8a8d7653f" + resolved "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz" integrity sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ== dependencies: makeerror "1.0.12" wcwidth@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" + resolved "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz" integrity sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg== dependencies: defaults "^1.0.3" webidl-conversions@^3.0.0: version "3.0.1" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" + resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz" integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== whatwg-url@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" + resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz" integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== dependencies: tr46 "~0.0.3" @@ -5382,14 +5382,14 @@ whatwg-url@^5.0.0: which@^2.0.1: version "2.0.2" - resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + resolved "https://registry.npmjs.org/which/-/which-2.0.2.tgz" integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== dependencies: isexe "^2.0.0" why-is-node-running@2.3.0: version "2.3.0" - resolved "https://registry.yarnpkg.com/why-is-node-running/-/why-is-node-running-2.3.0.tgz#a3f69a97107f494b3cdc3bdddd883a7d65cebf04" + resolved "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz" integrity sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w== dependencies: siginfo "^2.0.0" @@ -5397,7 +5397,7 @@ why-is-node-running@2.3.0: winston-transport@^4.9.0: version "4.9.0" - resolved "https://registry.yarnpkg.com/winston-transport/-/winston-transport-4.9.0.tgz#3bba345de10297654ea6f33519424560003b3bf9" + resolved "https://registry.npmjs.org/winston-transport/-/winston-transport-4.9.0.tgz" integrity sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A== dependencies: logform "^2.7.0" @@ -5406,7 +5406,7 @@ winston-transport@^4.9.0: winston@3.17.0: version "3.17.0" - resolved "https://registry.yarnpkg.com/winston/-/winston-3.17.0.tgz#74b8665ce9b4ea7b29d0922cfccf852a08a11423" + resolved "https://registry.npmjs.org/winston/-/winston-3.17.0.tgz" integrity sha512-DLiFIXYC5fMPxaRg832S6F5mJYvePtmO5G9v9IgUFPhXm9/GkXarH/TUrBAVzhTCzAj9anE/+GjrgXp/54nOgw== dependencies: "@colors/colors" "^1.6.0" @@ -5423,7 +5423,7 @@ winston@3.17.0: wrap-ansi@^6.0.1: version "6.2.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" + resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz" integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== dependencies: ansi-styles "^4.0.0" @@ -5432,7 +5432,7 @@ wrap-ansi@^6.0.1: wrap-ansi@^7.0.0: version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== dependencies: ansi-styles "^4.0.0" @@ -5441,7 +5441,7 @@ wrap-ansi@^7.0.0: wrap-ansi@^9.0.0: version "9.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-9.0.0.tgz#1a3dc8b70d85eeb8398ddfb1e4a02cd186e58b3e" + resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz" integrity sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q== dependencies: ansi-styles "^6.2.1" @@ -5450,12 +5450,12 @@ wrap-ansi@^9.0.0: wrappy@1: version "1.0.2" - resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== write-file-atomic@^4.0.2: version "4.0.2" - resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-4.0.2.tgz#a9df01ae5b77858a027fd2e80768ee433555fcfd" + resolved "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz" integrity sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg== dependencies: imurmurhash "^0.1.4" @@ -5463,12 +5463,12 @@ write-file-atomic@^4.0.2: ws@^7.5.10: version "7.5.10" - resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.10.tgz#58b5c20dc281633f6c19113f39b349bd8bd558d9" + resolved "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz" integrity sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ== xml2js@0.5.0: version "0.5.0" - resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.5.0.tgz#d9440631fbb2ed800203fad106f2724f62c493b7" + resolved "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz" integrity sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA== dependencies: sax ">=0.6.0" @@ -5476,47 +5476,47 @@ xml2js@0.5.0: xmlbuilder@~11.0.0: version "11.0.1" - resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz#be9bae1c8a046e76b31127726347d0ad7002beb3" + resolved "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz" integrity sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA== y18n@^5.0.5: version "5.0.8" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" + resolved "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz" integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== yallist@^3.0.2: version "3.1.1" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + resolved "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz" integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== yallist@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + resolved "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== yaml@2.7.0: version "2.7.0" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.7.0.tgz#aef9bb617a64c937a9a748803786ad8d3ffe1e98" + resolved "https://registry.npmjs.org/yaml/-/yaml-2.7.0.tgz" integrity sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA== yaml@~2.6.1: version "2.6.1" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.6.1.tgz#42f2b1ba89203f374609572d5349fb8686500773" + resolved "https://registry.npmjs.org/yaml/-/yaml-2.6.1.tgz" integrity sha512-7r0XPzioN/Q9kXBro/XPnA6kznR73DHq+GXh5ON7ZozRO6aMjbmiBuKste2wslTFkC5d1dw0GooOCepZXJ2SAg== yamux-js@0.1.2: version "0.1.2" - resolved "https://registry.yarnpkg.com/yamux-js/-/yamux-js-0.1.2.tgz#a157e4922f8f0393725955c352b418f16259fd48" + resolved "https://registry.npmjs.org/yamux-js/-/yamux-js-0.1.2.tgz" integrity sha512-bhsPlPZ9xB4Dawyf6nkS58u4F3IvGCaybkEKGnneUeepcI7MPoG3Tt6SaKCU5x/kP2/2w20Qm/GqbpwAM16vYw== yargs-parser@^21.1.1: version "21.1.1" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" + resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz" integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== yargs@^17.3.1, yargs@^17.7.2: version "17.7.2" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" + resolved "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz" integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== dependencies: cliui "^8.0.1" @@ -5529,10 +5529,10 @@ yargs@^17.3.1, yargs@^17.7.2: yn@3.1.1: version "3.1.1" - resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" + resolved "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz" integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== yocto-queue@^0.1.0: version "0.1.0" - resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + resolved "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== From 38f86e7f7995d4c2342df2726d80c94536bb5b3e Mon Sep 17 00:00:00 2001 From: zgjimhaziri Date: Tue, 13 May 2025 18:00:01 +0200 Subject: [PATCH 12/45] TA-3749: Revert unnecessary change --- src/content-cli-profile.ts | 106 ++++++++++++++++++------------------- 1 file changed, 53 insertions(+), 53 deletions(-) diff --git a/src/content-cli-profile.ts b/src/content-cli-profile.ts index c8541dfe..5bf8d69a 100644 --- a/src/content-cli-profile.ts +++ b/src/content-cli-profile.ts @@ -1,53 +1,53 @@ -// import { ProfileCommand } from "./commands/profile.command"; -// import { Command } from "commander"; -// import { program } from "./util/program"; -// -// class Profile { -// public static listProfile(program: Command): Command { -// program -// .command("list") -// .description("Command to list all stored profiles") -// .action(async () => { -// await new ProfileCommand().listProfiles(); -// process.exit(); -// }); -// -// return program; -// } -// -// public static createProfile(program: Command): Command { -// program -// .command("create") -// .description("Command to create a new profile") -// .option("--setAsDefault", "Set this profile as default") -// .action(async cmd => { -// await new ProfileCommand().createProfile(cmd.setAsDefault); -// process.exit(); -// }); -// -// return program; -// } -// -// public static defaultProfile(program: Command): Command { -// program -// .command("default ") -// .description("Command to set a profile as default") -// .action(async profile => { -// await new ProfileCommand().makeDefaultProfile(profile); -// process.exit(); -// }); -// -// return program; -// } -// } -// -// Profile.listProfile(program); -// Profile.createProfile(program); -// Profile.defaultProfile(program); -// -// program.parse(process.argv); -// -// if (!process.argv.slice(2).length) { -// program.outputHelp(); -// process.exit(1); -// } +import { ProfileCommand } from "./commands/profile.command"; +import { Command } from "commander"; +import { program } from "./util/program"; + +class Profile { + public static listProfile(program: Command): Command { + program + .command("list") + .description("Command to list all stored profiles") + .action(async () => { + await new ProfileCommand().listProfiles(); + process.exit(); + }); + + return program; + } + + public static createProfile(program: Command): Command { + program + .command("create") + .description("Command to create a new profile") + .option("--setAsDefault", "Set this profile as default") + .action(async cmd => { + await new ProfileCommand().createProfile(cmd.setAsDefault); + process.exit(); + }); + + return program; + } + + public static defaultProfile(program: Command): Command { + program + .command("default ") + .description("Command to set a profile as default") + .action(async profile => { + await new ProfileCommand().makeDefaultProfile(profile); + process.exit(); + }); + + return program; + } +} + +Profile.listProfile(program); +Profile.createProfile(program); +Profile.defaultProfile(program); + +program.parse(process.argv); + +if (!process.argv.slice(2).length) { + program.outputHelp(); + process.exit(1); +} From 6f4f515665599e1ba2e198f0734f06f0387972de Mon Sep 17 00:00:00 2001 From: zgjimhaziri Date: Tue, 13 May 2025 18:00:33 +0200 Subject: [PATCH 13/45] TA-3749: Revert unnecessary change --- package.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/package.json b/package.json index d859bdd9..19e4d6b2 100644 --- a/package.json +++ b/package.json @@ -42,8 +42,7 @@ "tslint": "5.20.1", "tslint-config-prettier": "1.18.0", "tslint-consistent-codestyle": "1.16.0", - "typescript": "5.3.3", - "why-is-node-running": "2.3.0" + "typescript": "5.3.3" }, "resolutions": { "glob": "10.0.0" From 128fcab3965166f8cc774870d66d44fa8dbcdf19 Mon Sep 17 00:00:00 2001 From: zgjimhaziri Date: Tue, 13 May 2025 18:02:40 +0200 Subject: [PATCH 14/45] TA-3749: Revert unnecessary change --- src/commands/profile.command.ts | 61 ++++++++++++++++++++++++++++++++ src/services/question.service.ts | 16 +++++++++ 2 files changed, 77 insertions(+) create mode 100644 src/commands/profile.command.ts create mode 100644 src/services/question.service.ts diff --git a/src/commands/profile.command.ts b/src/commands/profile.command.ts new file mode 100644 index 00000000..0badb10b --- /dev/null +++ b/src/commands/profile.command.ts @@ -0,0 +1,61 @@ +import { QuestionService } from "../services/question.service"; +import {Profile, ProfileType} from "../interfaces/profile.interface"; +import { ProfileService } from "../services/profile.service"; +import { ProfileValidator } from "../validators/profile.validator"; +import { FatalError, logger } from "../util/logger"; + +export class ProfileCommand { + private profileService = new ProfileService(); + + public async createProfile(setAsDefault: boolean): Promise { + const profile: Profile = {} as Profile; + profile.name = await QuestionService.ask("Name of the profile: "); + profile.team = await QuestionService.ask("Your team (please provide the full url): "); + const type = await QuestionService.ask("Profile type: OAuth Device Code (1), OAuth Client Credentials (2) or Application Key / API Key (3): " ); + switch (type) { + case "1": + profile.type = ProfileType.DEVICE_CODE; + break; + case "2": + profile.type = ProfileType.CLIENT_CREDENTIALS; + profile.clientId = await QuestionService.ask("Your client id: "); + profile.clientSecret = await QuestionService.ask("Your client secret: "); + break; + case "3": + profile.type = ProfileType.KEY; + profile.apiToken = await QuestionService.ask("Your api token: "); + break; + default: + logger.error(new FatalError("Invalid type")); + break; + } + profile.authenticationType = await ProfileValidator.validateProfile(profile); + await this.profileService.authorizeProfile(profile); + + this.profileService.storeProfile(profile); + if (setAsDefault) { + await this.makeDefaultProfile(profile.name); + } + logger.info("Profile created successfully!"); + } + + public async listProfiles(): Promise { + this.profileService.readAllProfiles().then((profiles: string[]) => { + const defaultProfile = this.profileService.getDefaultProfile(); + if (profiles) { + profiles.forEach(profile => { + if (defaultProfile && defaultProfile === profile) { + logger.info(profile + " (default)"); + } else { + logger.info(profile); + } + }); + } + }); + } + + public async makeDefaultProfile(profile: string): Promise { + await this.profileService.makeDefaultProfile(profile); + logger.info("Default profile: " + profile); + } +} \ No newline at end of file diff --git a/src/services/question.service.ts b/src/services/question.service.ts new file mode 100644 index 00000000..8822ccab --- /dev/null +++ b/src/services/question.service.ts @@ -0,0 +1,16 @@ +import * as readline from "readline"; +import { ReadLine } from "readline"; + +export class QuestionService { + private static readLine: ReadLine = readline.createInterface({ + input: process.stdin, + output: process.stdout, + terminal: true, + }); + + public static async ask(question: string): Promise { + return new Promise((resolve, reject) => { + this.readLine.question(question, input => resolve(input)); + }); + } +} \ No newline at end of file From b624e744feea07c281a7c23d2126f4911ce4dd2a Mon Sep 17 00:00:00 2001 From: zgjimhaziri Date: Tue, 13 May 2025 18:04:36 +0200 Subject: [PATCH 15/45] TA-3749: Revert unnecessary change --- .../package-manager/package-service.ts | 1 - src/util/logger.ts | 73 +------------------ 2 files changed, 4 insertions(+), 70 deletions(-) diff --git a/src/services/package-manager/package-service.ts b/src/services/package-manager/package-service.ts index 675186c9..bba49197 100644 --- a/src/services/package-manager/package-service.ts +++ b/src/services/package-manager/package-service.ts @@ -32,7 +32,6 @@ class PackageService { nodes.forEach(node => { logger.info(`${node.name} - Key: "${node.key}"`); }); - logger.info(`Found ${nodes.length} packages`); } public async findAndExportListOfAllPackages(includeDependencies: boolean, packageKeys: string[]): Promise { diff --git a/src/util/logger.ts b/src/util/logger.ts index cddd6464..179bd862 100644 --- a/src/util/logger.ts +++ b/src/util/logger.ts @@ -1,9 +1,6 @@ import * as winston from "winston"; import * as Transport from "winston-transport"; import { Logger } from "winston"; -import os = require("os"); -import * as path from "path"; -import * as fs from "fs"; class CustomTransport extends Transport { constructor(opts: any) { @@ -22,73 +19,11 @@ class CustomTransport extends Transport { } } -// log into the same directory as where the profiles are, to avoid creating more -// directories for now. Consider changing this to a general 'home' directory such -// as .celonis-cli or alike. -const logDirName = ".celonis-content-cli-profiles"; -const logFileName = 'celonis-cli.log'; -const exceptionLogFileName = 'exceptions.log'; -const maxLogSizeMB = 3; -const logDir = path.join(os.homedir(), logDirName); -const logFilePath = path.join(logDir, logFileName); -const exceptionLogFilePath = path.join(logDir, exceptionLogFileName); -const maxSizeBytes = maxLogSizeMB * 1024 * 1024; // 3 MB in bytes - -// --- Ensure log directory exists --- -try { - if (!fs.existsSync(logDir)) { - fs.mkdirSync(logDir, { recursive: true }); - } -} catch (error) { - console.error(`Error creating log directory: ${logDir}`, error); -} - export const logger: Logger = winston.createLogger({ format: winston.format.combine(winston.format.cli()), - level: 'debug', - transports: [ - new winston.transports.Console({ - level: 'info', - format: winston.format.combine( - winston.format.colorize(), - winston.format.cli() - ), - }), - new winston.transports.File({ - level: 'info', // Log everything from debug up to the file - filename: logFilePath, - format: winston.format.combine( - winston.format.timestamp(), // Add timestamp to file logs - winston.format.errors({ stack: true }), // Log stack traces - winston.format.json() // Log in JSON format - ), - maxsize: maxSizeBytes, - maxFiles: 5, - tailable: true, - }), - new CustomTransport({}) - ], - exceptionHandlers: [ - new winston.transports.Console({ - format: winston.format.combine( - winston.format.colorize(), - winston.format.cli() - ), - }), - new winston.transports.File({ - filename: exceptionLogFilePath, // Separate file recommended - format: winston.format.combine( - winston.format.timestamp(), - winston.format.errors({ stack: true }), - winston.format.json() - ), - maxsize: maxSizeBytes, - maxFiles: 2, // Keep fewer exception logs if desired - tailable: true, - }), - new CustomTransport({}) - ], - exitOnError: false, + transports: [new winston.transports.Console(), new CustomTransport({})], + exceptionHandlers: [new winston.transports.Console(), new CustomTransport({})], + exitOnError: true, }); // tslint:disable-next-line: max-classes-per-file @@ -101,4 +36,4 @@ export class FatalError extends Error { // tslint:disable-next-line: max-classes-per-file export class GracefulError extends Error { public code: "__CELGRACEFULERROR"; -} +} \ No newline at end of file From f5e25b8064cd0e15ec74314ab3e417ba675992b0 Mon Sep 17 00:00:00 2001 From: zgjimhaziri Date: Tue, 13 May 2025 18:05:58 +0200 Subject: [PATCH 16/45] TA-3749: yarn-lock update --- yarn.lock | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/yarn.lock b/yarn.lock index 2e09e25d..1b222838 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4814,11 +4814,6 @@ shebang-regex@^3.0.0: resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== -siginfo@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz" - integrity sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g== - signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: version "3.0.7" resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz" @@ -4963,11 +4958,6 @@ stack-utils@^2.0.3: dependencies: escape-string-regexp "^2.0.0" -stackback@0.0.2: - version "0.0.2" - resolved "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz" - integrity sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw== - stream-events@^1.0.5: version "1.0.5" resolved "https://registry.npmjs.org/stream-events/-/stream-events-1.0.5.tgz" @@ -5387,14 +5377,6 @@ which@^2.0.1: dependencies: isexe "^2.0.0" -why-is-node-running@2.3.0: - version "2.3.0" - resolved "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz" - integrity sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w== - dependencies: - siginfo "^2.0.0" - stackback "0.0.2" - winston-transport@^4.9.0: version "4.9.0" resolved "https://registry.npmjs.org/winston-transport/-/winston-transport-4.9.0.tgz" From 5b4a28349b456e7b4ec773d12491edc83ffe9a40 Mon Sep 17 00:00:00 2001 From: zgjimhaziri Date: Tue, 13 May 2025 18:06:42 +0200 Subject: [PATCH 17/45] TA-3749: Rever package.json update --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 19e4d6b2..d3a81d77 100644 --- a/package.json +++ b/package.json @@ -23,8 +23,8 @@ "axios": "1.7.9", "commander": "13.1.0", "form-data": "4.0.1", - "hpagent": "1.2.0", "openid-client": "5.6.1", + "hpagent": "1.2.0", "semver": "7.6.3", "valid-url": "1.0.9", "winston": "3.17.0", From 7f6cc1b2a0216f4b6ff8e03aae9c6f12459e57d8 Mon Sep 17 00:00:00 2001 From: zgjimhaziri Date: Wed, 14 May 2025 10:44:55 +0200 Subject: [PATCH 18/45] TA-3749: Move code to top level --- src/api/action-flow-api.ts | 31 - src/api/asset-api.ts | 23 - src/api/batch-import-export-api.ts | 60 -- src/api/compute-pool-api.ts | 15 - src/api/data-pool-api.ts | 51 -- src/api/diff-api.ts | 23 - src/api/node-api.ts | 22 - src/api/package-api.ts | 90 --- src/api/package-dependencies-api.ts | 41 -- src/api/space-api.ts | 28 - src/api/variable-assignment-apis.ts | 10 - src/api/variables-api.ts | 43 -- src/commands/action-flow.command.ts | 16 - src/commands/analysis-bookmarks.command.ts | 18 - src/commands/asset.command.ts | 28 - src/commands/config.command.ts | 34 -- src/commands/connection.command.ts | 18 - src/commands/ctp.command.ts | 44 -- src/commands/data-pool.command.ts | 40 -- src/commands/package.command.ts | 54 -- src/commands/profile.command.ts | 61 -- .../profile/profile-command.service.ts | 0 .../commands/profile/profile-module.ts | 0 .../commands/profile/question.service.ts | 0 src/commands/skill.command.ts | 15 - src/commands/space.command.ts | 11 - src/commands/variable.command.ts | 12 - src/commands/widget.command.ts | 14 - src/content-cli-analyze.ts | 42 -- src/content-cli-config.ts | 108 ---- src/content-cli-export.ts | 78 --- src/content-cli-get.ts | 36 -- src/content-cli-import.ts | 86 --- src/content-cli-list.ts | 121 ---- src/content-cli-profile.ts | 53 -- src/content-cli-pull.ts | 97 --- src/content-cli-push.ts | 223 ------- .../content-cli-refactored.ts | 83 --- .../core/utils/version.ts | 8 - src/content-cli-set.ts | 40 -- src/content-cli-update.ts | 28 - src/content-cli.ts | 85 ++- .../analysis-bookmarks-manager.factory.ts | 31 - src/content/factory/asset-manager.factory.ts | 50 -- src/content/factory/ctp-manager.factory.ts | 40 -- .../factory/data-pool-manager.factory.ts | 39 -- .../factory/package-manager.factory.ts | 72 --- src/content/factory/skill-manager.factory.ts | 24 - src/content/factory/space-manager.factory.ts | 16 - src/content/factory/widget-manager.factory.ts | 87 --- .../manager/analysis-bookmarks.manager.ts | 62 -- src/content/manager/asset.manager.ts | 70 --- src/content/manager/base.manager.ts | 128 ---- src/content/manager/ctp.analysis.manager.ts | 18 - src/content/manager/ctp.datamodel.manager.ts | 29 - src/content/manager/ctp.manager.ts | 48 -- src/content/manager/data-pool.manager.ts | 61 -- src/content/manager/package.manager.ts | 143 ----- src/content/manager/skill.manager.ts | 57 -- src/content/manager/space.manager.ts | 48 -- src/content/manager/widget.manager.ts | 62 -- .../core/command/cli-context.ts | 0 .../core/command/module-handler.ts | 2 +- .../core/http/axios-initializer.ts | 0 .../core/http/base-api.ts | 0 .../core/http/http-client.ts | 0 .../core/http/tracing.ts | 0 .../core/profile/profile.interface.ts | 0 .../core/profile/profile.service.ts | 0 .../core/profile/profile.validator.ts | 0 .../core/utils/file-service.ts | 0 .../core/utils/logger.ts | 0 src/{util => core/utils}/version.ts | 2 +- .../batch-export-import-constants.ts | 11 - src/interfaces/batch-export-node-transport.ts | 28 - .../data-pool-manager.interfaces.ts | 30 - src/interfaces/diff-package.transport.ts | 34 -- src/interfaces/manager-config.interface.ts | 11 - src/interfaces/manifest-transport.ts | 31 - src/interfaces/package-export-transport.ts | 82 --- src/interfaces/package-manager.interfaces.ts | 99 ---- src/interfaces/profile.interface.ts | 34 -- src/interfaces/save-content-node.interface.ts | 12 - src/interfaces/save-space.interface.ts | 5 - .../variable-assignment-api.interface.ts | 3 - .../action-flow/action-flow-service.ts | 75 --- src/services/command.service.ts | 14 - src/services/connection/connection.service.ts | 56 -- src/services/content.service.ts | 124 ---- src/services/context.service.ts | 26 - src/services/data-pool/data-pool-service.ts | 65 -- src/services/file-service.ts | 45 -- src/services/http-client-service.v2.ts | 208 ------- src/services/http-client.service.ts | 136 ----- src/services/package-manager/asset-service.ts | 28 - .../batch-import-export-service.ts | 160 ----- .../package-manager/datamodel-service.ts | 25 - src/services/package-manager/diff-service.ts | 83 --- .../package-manager/package-service.ts | 513 ---------------- src/services/package-manager/space-service.ts | 45 -- .../package-manager/variable-service.ts | 111 ---- src/services/profile.service.ts | 302 ---------- src/services/question.service.ts | 16 - src/services/studio/studio.service.ts | 266 --------- src/util/axios-initializer.ts | 21 - src/util/context-initializer.ts | 17 - src/util/json.ts | 15 - src/util/logger.ts | 39 -- src/util/program.ts | 6 - src/util/semantic-versioning.ts | 23 - src/util/tracing.ts | 24 - src/util/yaml.ts | 9 - src/validators/profile.validator.ts | 23 - tests/.gitkeep | 0 tests/analyze/action-flows.spec.ts | 69 --- tests/config/config-diff.spec.ts | 176 ------ tests/config/config-export.spec.ts | 553 ------------------ tests/config/config-import.spec.ts | 326 ----------- tests/config/config-list-variables.spec.ts | 173 ------ tests/config/config-list.spec.ts | 182 ------ tests/export/action-flows.spec.ts | 181 ------ tests/import/action-flows.spec.ts | 55 -- tests/jest.setup.ts | 33 -- tests/list/assignments.spec.ts | 65 -- tests/mocks/package.json | 3 - tests/utls/config-utils.ts | 107 ---- tests/utls/context-mock.ts | 13 - tests/utls/fs-mock-utils.ts | 21 - tests/utls/http-requests-mock.ts | 85 --- tests/utls/package-manager-api.utils.ts | 41 -- tests/utls/test-transport.ts | 15 - 131 files changed, 64 insertions(+), 7868 deletions(-) delete mode 100644 src/api/action-flow-api.ts delete mode 100644 src/api/asset-api.ts delete mode 100644 src/api/batch-import-export-api.ts delete mode 100644 src/api/compute-pool-api.ts delete mode 100644 src/api/data-pool-api.ts delete mode 100644 src/api/diff-api.ts delete mode 100644 src/api/node-api.ts delete mode 100644 src/api/package-api.ts delete mode 100644 src/api/package-dependencies-api.ts delete mode 100644 src/api/space-api.ts delete mode 100644 src/api/variable-assignment-apis.ts delete mode 100644 src/api/variables-api.ts delete mode 100644 src/commands/action-flow.command.ts delete mode 100644 src/commands/analysis-bookmarks.command.ts delete mode 100644 src/commands/asset.command.ts delete mode 100644 src/commands/config.command.ts delete mode 100644 src/commands/connection.command.ts delete mode 100644 src/commands/ctp.command.ts delete mode 100644 src/commands/data-pool.command.ts delete mode 100644 src/commands/package.command.ts delete mode 100644 src/commands/profile.command.ts rename src/{content-cli-refactor => }/commands/profile/profile-command.service.ts (100%) rename src/{content-cli-refactor => }/commands/profile/profile-module.ts (100%) rename src/{content-cli-refactor => }/commands/profile/question.service.ts (100%) delete mode 100644 src/commands/skill.command.ts delete mode 100644 src/commands/space.command.ts delete mode 100644 src/commands/variable.command.ts delete mode 100644 src/commands/widget.command.ts delete mode 100644 src/content-cli-analyze.ts delete mode 100644 src/content-cli-config.ts delete mode 100644 src/content-cli-export.ts delete mode 100644 src/content-cli-get.ts delete mode 100644 src/content-cli-import.ts delete mode 100644 src/content-cli-list.ts delete mode 100644 src/content-cli-profile.ts delete mode 100644 src/content-cli-pull.ts delete mode 100644 src/content-cli-push.ts delete mode 100644 src/content-cli-refactor/content-cli-refactored.ts delete mode 100644 src/content-cli-refactor/core/utils/version.ts delete mode 100644 src/content-cli-set.ts delete mode 100644 src/content-cli-update.ts delete mode 100644 src/content/factory/analysis-bookmarks-manager.factory.ts delete mode 100644 src/content/factory/asset-manager.factory.ts delete mode 100644 src/content/factory/ctp-manager.factory.ts delete mode 100644 src/content/factory/data-pool-manager.factory.ts delete mode 100644 src/content/factory/package-manager.factory.ts delete mode 100644 src/content/factory/skill-manager.factory.ts delete mode 100644 src/content/factory/space-manager.factory.ts delete mode 100644 src/content/factory/widget-manager.factory.ts delete mode 100644 src/content/manager/analysis-bookmarks.manager.ts delete mode 100644 src/content/manager/asset.manager.ts delete mode 100644 src/content/manager/base.manager.ts delete mode 100644 src/content/manager/ctp.analysis.manager.ts delete mode 100644 src/content/manager/ctp.datamodel.manager.ts delete mode 100644 src/content/manager/ctp.manager.ts delete mode 100644 src/content/manager/data-pool.manager.ts delete mode 100644 src/content/manager/package.manager.ts delete mode 100644 src/content/manager/skill.manager.ts delete mode 100644 src/content/manager/space.manager.ts delete mode 100644 src/content/manager/widget.manager.ts rename src/{content-cli-refactor => }/core/command/cli-context.ts (100%) rename src/{content-cli-refactor => }/core/command/module-handler.ts (99%) rename src/{content-cli-refactor => }/core/http/axios-initializer.ts (100%) rename src/{content-cli-refactor => }/core/http/base-api.ts (100%) rename src/{content-cli-refactor => }/core/http/http-client.ts (100%) rename src/{content-cli-refactor => }/core/http/tracing.ts (100%) rename src/{content-cli-refactor => }/core/profile/profile.interface.ts (100%) rename src/{content-cli-refactor => }/core/profile/profile.service.ts (100%) rename src/{content-cli-refactor => }/core/profile/profile.validator.ts (100%) rename src/{content-cli-refactor => }/core/utils/file-service.ts (100%) rename src/{content-cli-refactor => }/core/utils/logger.ts (100%) rename src/{util => core/utils}/version.ts (74%) delete mode 100644 src/interfaces/batch-export-import-constants.ts delete mode 100644 src/interfaces/batch-export-node-transport.ts delete mode 100644 src/interfaces/data-pool-manager.interfaces.ts delete mode 100644 src/interfaces/diff-package.transport.ts delete mode 100644 src/interfaces/manager-config.interface.ts delete mode 100644 src/interfaces/manifest-transport.ts delete mode 100644 src/interfaces/package-export-transport.ts delete mode 100644 src/interfaces/package-manager.interfaces.ts delete mode 100644 src/interfaces/profile.interface.ts delete mode 100644 src/interfaces/save-content-node.interface.ts delete mode 100644 src/interfaces/save-space.interface.ts delete mode 100644 src/interfaces/variable-assignment-api.interface.ts delete mode 100644 src/services/action-flow/action-flow-service.ts delete mode 100644 src/services/command.service.ts delete mode 100644 src/services/connection/connection.service.ts delete mode 100644 src/services/content.service.ts delete mode 100644 src/services/context.service.ts delete mode 100644 src/services/data-pool/data-pool-service.ts delete mode 100644 src/services/file-service.ts delete mode 100644 src/services/http-client-service.v2.ts delete mode 100644 src/services/http-client.service.ts delete mode 100644 src/services/package-manager/asset-service.ts delete mode 100644 src/services/package-manager/batch-import-export-service.ts delete mode 100644 src/services/package-manager/datamodel-service.ts delete mode 100644 src/services/package-manager/diff-service.ts delete mode 100644 src/services/package-manager/package-service.ts delete mode 100644 src/services/package-manager/space-service.ts delete mode 100644 src/services/package-manager/variable-service.ts delete mode 100644 src/services/profile.service.ts delete mode 100644 src/services/question.service.ts delete mode 100644 src/services/studio/studio.service.ts delete mode 100644 src/util/axios-initializer.ts delete mode 100644 src/util/context-initializer.ts delete mode 100644 src/util/json.ts delete mode 100644 src/util/logger.ts delete mode 100644 src/util/program.ts delete mode 100644 src/util/semantic-versioning.ts delete mode 100644 src/util/tracing.ts delete mode 100644 src/util/yaml.ts delete mode 100644 src/validators/profile.validator.ts create mode 100644 tests/.gitkeep delete mode 100644 tests/analyze/action-flows.spec.ts delete mode 100644 tests/config/config-diff.spec.ts delete mode 100644 tests/config/config-export.spec.ts delete mode 100644 tests/config/config-import.spec.ts delete mode 100644 tests/config/config-list-variables.spec.ts delete mode 100644 tests/config/config-list.spec.ts delete mode 100644 tests/export/action-flows.spec.ts delete mode 100644 tests/import/action-flows.spec.ts delete mode 100644 tests/jest.setup.ts delete mode 100644 tests/list/assignments.spec.ts delete mode 100644 tests/mocks/package.json delete mode 100644 tests/utls/config-utils.ts delete mode 100644 tests/utls/context-mock.ts delete mode 100644 tests/utls/fs-mock-utils.ts delete mode 100644 tests/utls/http-requests-mock.ts delete mode 100644 tests/utls/package-manager-api.utils.ts delete mode 100644 tests/utls/test-transport.ts diff --git a/src/api/action-flow-api.ts b/src/api/action-flow-api.ts deleted file mode 100644 index ad602008..00000000 --- a/src/api/action-flow-api.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { httpClientV2 } from "../services/http-client-service.v2"; -import { FatalError } from "../util/logger"; -import * as FormData from "form-data"; - -class ActionFlowApi { - public static readonly INSTANCE = new ActionFlowApi(); - - public async exportRawAssets(packageId: string): Promise { - return httpClientV2.getFile(`/ems-automation/api/root/${packageId}/export/assets`).catch(e => { - throw new FatalError(`Problem getting Action Flow assets: ${e}`); - }); - } - - public async analyzeAssets(packageId: string): Promise { - return httpClientV2.get(`/ems-automation/api/root/${packageId}/export/assets/analyze`).catch(e => { - throw new FatalError(`Problem analyzing Action Flow assets: ${e}`); - }); - } - - public async importAssets(packageId: string, data: FormData, dryRun: boolean): Promise { - const params = { - dryRun: dryRun, - }; - - return httpClientV2.postFile(`/ems-automation/api/root/${packageId}/import/assets`, data, params).catch(e => { - throw new FatalError(`Problem importing Action Flow assets: ${e}`); - }); - } -} - -export const actionFlowApi = ActionFlowApi.INSTANCE; \ No newline at end of file diff --git a/src/api/asset-api.ts b/src/api/asset-api.ts deleted file mode 100644 index fc3cb2bd..00000000 --- a/src/api/asset-api.ts +++ /dev/null @@ -1,23 +0,0 @@ -import {httpClientV2} from "../services/http-client-service.v2"; -import {FatalError} from "../util/logger"; -import {SaveContentNode} from "../interfaces/save-content-node.interface"; - -class AssetApi { - public static readonly INSTANCE = new AssetApi(); - - public async findAllAssets(assetType: string): Promise { - return httpClientV2.get(this.getFindAllAssetsUrl(assetType)).catch(e => { - throw new FatalError(`Problem getting assets: ${e}`); - }); - } - - private getFindAllAssetsUrl(assetType: string): string { - const findAllAssetsUrl = "/package-manager/api/nodes"; - if (assetType) { - return `${findAllAssetsUrl}?assetType=${assetType}`; - } - return findAllAssetsUrl; - } -} - -export const assetApi = AssetApi.INSTANCE; \ No newline at end of file diff --git a/src/api/batch-import-export-api.ts b/src/api/batch-import-export-api.ts deleted file mode 100644 index c14d3840..00000000 --- a/src/api/batch-import-export-api.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { - PackageExportTransport, - PackageKeyAndVersionPair, PostPackageImportData, - VariableManifestTransport -} from "../interfaces/package-export-transport"; -import {httpClientV2} from "../services/http-client-service.v2"; -import {FatalError} from "../util/logger"; -import * as FormData from "form-data"; - -class BatchImportExportApi { - public static readonly INSTANCE = new BatchImportExportApi(); - - public findAllActivePackages(flavors: string[], withDependencies: boolean = false): Promise { - const queryParams = new URLSearchParams(); - - queryParams.set("withDependencies", withDependencies.toString()); - flavors.forEach(flavor => queryParams.append("flavors", flavor)) - - return httpClientV2.get(`/package-manager/api/core/packages/export/list?${queryParams.toString()}`).catch(e => { - throw new FatalError(`Problem getting active packages: ${e}`); - }); - } - - public findActivePackagesByKeys(packageKeys: string[], withDependencies: boolean = false): Promise { - const queryParams = new URLSearchParams(); - - packageKeys.forEach(key => queryParams.append("packageKeys", key)) - queryParams.set("withDependencies", withDependencies.toString()); - - return httpClientV2.get(`/package-manager/api/core/packages/export/list-by-keys?${queryParams.toString()}`).catch(e => { - throw new FatalError(`Problem getting active packages by keys: ${e}`); - }); - } - - public exportPackages(packageKeys: string[], withDependencies: boolean = false): Promise { - const queryParams = new URLSearchParams(); - packageKeys.forEach(packageKey => queryParams.append("packageKeys", packageKey)); - queryParams.set("withDependencies", withDependencies.toString()); - - return httpClientV2.getFile(`/package-manager/api/core/packages/export/batch?${queryParams.toString()}`).catch(e => { - throw new FatalError(`Problem exporting packages: ${e}`); - }); - } - - public importPackages(data: FormData, overwrite: boolean): Promise { - return httpClientV2.postFile( - "/package-manager/api/core/packages/import/batch", - data, - {overwrite} - ); - } - - public findVariablesWithValuesByPackageKeysAndVersion(packagesByKeyAndVersion: PackageKeyAndVersionPair[]): Promise { - return httpClientV2.post("/package-manager/api/core/packages/export/batch/variables-with-assignments", packagesByKeyAndVersion).catch(e => { - throw new FatalError(`Problem exporting package variables: ${e}`); - }) - } -} - -export const batchImportExportApi = BatchImportExportApi.INSTANCE; \ No newline at end of file diff --git a/src/api/compute-pool-api.ts b/src/api/compute-pool-api.ts deleted file mode 100644 index 714f0f89..00000000 --- a/src/api/compute-pool-api.ts +++ /dev/null @@ -1,15 +0,0 @@ -import {StudioComputeNodeDescriptor} from "../interfaces/package-manager.interfaces"; -import {httpClientV2} from "../services/http-client-service.v2"; - -class ComputePoolApi { - public static readonly INSTANCE = new ComputePoolApi(); - - public async findAllDataModelsDetails(): Promise { - return httpClientV2.get("/package-manager/api/compute-pools/data-models/details") - .catch(e => { - return null; - }); - } -} - -export const computePoolApi = ComputePoolApi.INSTANCE; \ No newline at end of file diff --git a/src/api/data-pool-api.ts b/src/api/data-pool-api.ts deleted file mode 100644 index e63cd6be..00000000 --- a/src/api/data-pool-api.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { httpClientV2 } from "../services/http-client-service.v2"; -import { FatalError } from "../util/logger"; -import { DataPoolInstallVersionReport, DataPoolPageTransport } from "../interfaces/data-pool-manager.interfaces"; - -class DataPoolApi { - public static readonly INSTANCE = new DataPoolApi(); - - public async findAllPagedPools(limit: string, page: string): Promise { - return httpClientV2.get(`/integration/api/pools/paged?limit=${limit}&page=${page}`).catch(e => { - throw new FatalError(`Problem getting data pools: : ${e}`); - }); - } - - public async executeDataPoolsBatchImport(importRequest: string): Promise { - return httpClientV2.post("/integration/api/pool/batch-import", importRequest).catch(e => { - throw new FatalError(`Data Pool batch import failed: : ${e}`); - }); - } - - public async exportDataPool(poolId: string): Promise { - return httpClientV2.get(`/integration/api/pools/${poolId}/v2/export`).catch(e => { - throw new FatalError(`Data Pool export failed: : ${e}`); - }); - } - - public async listConnections(poolId: string): Promise { - return httpClientV2.get(`/integration/api/pools/${poolId}/overviews/data-sources`).catch(e => { - throw new FatalError(`Can not list connections: : ${e}`); - }); - } - - public async getConnection(poolId: string, connectionId: string): Promise { - return httpClientV2.get(`/integration/api/pools/${poolId}/data-sources/${connectionId}`).catch(e => { - throw new FatalError(`Can not get connection: : ${e}`); - }); - } - - public async getTypedConnection(poolId: string, connectionId: string, type: string): Promise { - return httpClientV2.get(`/integration/api/datasource/${type}/${connectionId}`).catch(e => { - throw new FatalError(`Can get typed connection: : ${e}`); - }); - } - - public async updateTypedConnection(poolId: string, connectionId: string, type: string, data: any): Promise { - return httpClientV2.put(`/integration/api/datasource/${type}/${connectionId}`, data).catch(e => { - throw new FatalError(`Can not update typed connection: ${e}`); - }); - } -} - -export const dataPoolApi = DataPoolApi.INSTANCE; diff --git a/src/api/diff-api.ts b/src/api/diff-api.ts deleted file mode 100644 index 9dcc2c09..00000000 --- a/src/api/diff-api.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { PackageDiffMetadata, PackageDiffTransport } from "../interfaces/diff-package.transport"; -import {httpClientV2} from "../services/http-client-service.v2"; -import * as FormData from "form-data"; - -class DiffApi { - public static readonly INSTANCE = new DiffApi(); - - public async diffPackages(data: FormData): Promise { - return httpClientV2.postFile( - "/package-manager/api/core/packages/diff/configuration", - data - ); - } - - public async hasChanges(data: FormData): Promise { - return httpClientV2.postFile( - "/package-manager/api/core/packages/diff/configuration/has-changes", - data - ); - } -} - -export const diffApi = DiffApi.INSTANCE; \ No newline at end of file diff --git a/src/api/node-api.ts b/src/api/node-api.ts deleted file mode 100644 index 53a15680..00000000 --- a/src/api/node-api.ts +++ /dev/null @@ -1,22 +0,0 @@ -import {ContentNodeTransport} from "../interfaces/package-manager.interfaces"; -import {httpClientV2} from "../services/http-client-service.v2"; -import {FatalError} from "../util/logger"; - -class NodeApi { - public static readonly INSTANCE = new NodeApi(); - - public async findAllNodesOfType(assetType?: string): Promise { - return httpClientV2.get(`/package-manager/api/nodes?assetType=${assetType}`) - .catch(e => { - throw new FatalError(`Problem getting nodes of type ${assetType}: ${e}`); - }); - } - - public async findOneByKeyAndRootNodeKey(packageKey: string, nodeKey: string): Promise { - return httpClientV2.get(`/package-manager/api/nodes/${packageKey}/${nodeKey}`).catch(e => { - return null; - }); - } -} - -export const nodeApi = NodeApi.INSTANCE; \ No newline at end of file diff --git a/src/api/package-api.ts b/src/api/package-api.ts deleted file mode 100644 index 8c19a5e5..00000000 --- a/src/api/package-api.ts +++ /dev/null @@ -1,90 +0,0 @@ -import {httpClientV2} from "../services/http-client-service.v2"; -import { - ActivatePackageTransport, - ContentNodeTransport, - PackageHistoryTransport, - PackageManagerVariableType, - PackageWithVariableAssignments -} from "../interfaces/package-manager.interfaces"; -import {FatalError} from "../util/logger"; - - -class PackageApi { - public static readonly INSTANCE = new PackageApi(); - - public async findAllPackages(): Promise { - return httpClientV2.get("/package-manager/api/packages").catch(e => { - throw new FatalError(`Problem getting packages: ${e}`); - }); - } - - public async exportPackage(rootPackageKey: string, version?: string, excludeActionFlows?: boolean): Promise { - const queryParams = new URLSearchParams(); - queryParams.set("newKey", rootPackageKey); - queryParams.set("version", version ?? ""); - queryParams.set("excludeActionFlows", excludeActionFlows ? "true" : "false"); - - return await httpClientV2.downloadFile(`/package-manager/api/packages/${rootPackageKey}/export?${queryParams.toString()}`).catch(e => { - throw new FatalError(`Pacakge ${rootPackageKey}_${version} failed to export.`) - }); - } - - public async findAllPackagesWithVariableAssignments(type: PackageManagerVariableType): Promise { - const queryParams = new URLSearchParams(); - if (type) { - queryParams.set("type", type); - } - - return httpClientV2.get(`/package-manager/api/packages/with-variable-assignments?${queryParams.toString()}`).catch(e => { - throw new FatalError(`Problem getting variables of packages: : ${e}`); - }); - } - - public async findLatestVersionById(nodeId: string): Promise { - return httpClientV2.get(`/package-manager/api/packages/${nodeId}/latest-version`).catch(e => { - throw new FatalError(`Problem getting latest version of package: ${e}`); - }); - } - - public async findActiveVersionById(nodeId: string): Promise { - return httpClientV2.get(`/package-manager/api/packages/${nodeId}/active`).catch(e => { - throw new FatalError(`Problem getting latest version of package: ${e}`); - }); - } - - public async findActiveVersionByIds(nodeIds: string[]): Promise { - return httpClientV2.post("/package-manager/api/packages/active/by-ids", nodeIds).catch(e => { - throw new FatalError(`Problem getting latest version of packages: ${e}`); - }); - } - - public async findNextVersion(nodeId: string): Promise { - return httpClientV2.get(`/package-manager/api/packages/${nodeId}/next-version`).catch(e => { - throw new FatalError(`Problem getting latest version of package: ${e}`); - }); - } - - public async importPackage(nodeContent: any, spaceId: string, overwrite: boolean, excludeActionFlows?: boolean): Promise { - await httpClientV2.postFile("/package-manager/api/packages/import", nodeContent, { - spaceId: spaceId, - overwrite: overwrite, - excludeActionFlows: excludeActionFlows - }).catch(e => { - throw new FatalError(`Problem importing package: ${e}`); - }); - } - - public async movePackageToSpace(nodeId: string, spaceId: string): Promise { - await httpClientV2.put(`/package-manager/api/packages/${nodeId}/move/${spaceId}`, {}).catch(e => { - throw new FatalError(`Problem moving package: ${e}`); - }); - } - - public async publishPackage(activatePackage: ActivatePackageTransport): Promise { - await httpClientV2.post(`/package-manager/api/packages/${activatePackage.packageKey}/activate`, activatePackage).catch(e => { - throw new FatalError(`Problem activating package with key ${activatePackage.packageKey}: ${e}`); - }); - } -} - -export const packageApi = PackageApi.INSTANCE; diff --git a/src/api/package-dependencies-api.ts b/src/api/package-dependencies-api.ts deleted file mode 100644 index 9223e922..00000000 --- a/src/api/package-dependencies-api.ts +++ /dev/null @@ -1,41 +0,0 @@ -import {PackageDependencyTransport} from "../interfaces/package-manager.interfaces"; -import {httpClientV2} from "../services/http-client-service.v2"; -import {FatalError} from "../util/logger"; - -class PackageDependenciesApi { - public static readonly INSTANCE = new PackageDependenciesApi(); - - public async findDependenciesOfPackage(nodeId: string, draftId: string): Promise { - return httpClientV2.get(`/package-manager/api/package-dependencies/${nodeId}/by-root-draft-id/${draftId}`) - .catch(e=> { - throw new FatalError(`Problem getting dependencies of package: ${e}`); - }); - } - - public async findPackageDependenciesByIds(nodeDraftIdMap: Map): Promise> { - return await httpClientV2.post("/package-manager/api/package-dependencies/by-ids", Object.fromEntries(nodeDraftIdMap)) - .catch(e=> { - throw new FatalError(`Problem getting dependencies of package: ${e}`); - }); - } - - public async updatePackageDependency(nodeId: string, packageDependency: PackageDependencyTransport): Promise { - await httpClientV2.put(`/package-manager/api/package-dependencies/${nodeId}/dependency/by-key/${packageDependency.key}`, packageDependency).catch(e => { - throw new FatalError(`Problem updating package dependency: ${e}`); - }); - } - - public async createDependencies(packageId: string, packageDependency: PackageDependencyTransport[]): Promise { - await httpClientV2.post(`/package-manager/api/package-dependencies/${packageId}`, packageDependency).catch(e => { - throw new FatalError(`Problem updating package dependency: ${e}`); - }); - } - - public async deleteDependency(packageId: string, packageDependencyKey: string): Promise { - await httpClientV2.delete(`/package-manager/api/package-dependencies/${packageId}/dependency/by-key/${packageDependencyKey}`).catch(e => { - throw new FatalError(`Problem updating package dependency: ${e}`); - }); - } -} - -export const packageDependenciesApi = PackageDependenciesApi.INSTANCE; \ No newline at end of file diff --git a/src/api/space-api.ts b/src/api/space-api.ts deleted file mode 100644 index 4914d763..00000000 --- a/src/api/space-api.ts +++ /dev/null @@ -1,28 +0,0 @@ -import {httpClientV2} from "../services/http-client-service.v2"; -import {FatalError} from "../util/logger"; -import {SpaceTransport} from "../interfaces/save-space.interface"; - -class SpaceApi { - public static readonly INSTANCE = new SpaceApi(); - - public async findOne(spaceId: string): Promise { - return httpClientV2.get(`/package-manager/api/spaces/${spaceId}`).catch(e => { - throw new FatalError(`Problem getting space: ${spaceId} ${e}`); - }); - } - - public async findAllSpaces(): Promise { - return httpClientV2.get("/package-manager/api/spaces").catch(e => { - throw new FatalError(`Problem getting spaces: ${e}`); - }); - } - - public async createSpace(space: SpaceTransport): Promise { - return httpClientV2.post("/package-manager/api/spaces", space).catch(e => { - throw new FatalError(`Problem space creation: ${e}`); - }); - } - -} - -export const spaceApi = SpaceApi.INSTANCE; diff --git a/src/api/variable-assignment-apis.ts b/src/api/variable-assignment-apis.ts deleted file mode 100644 index c6b8b659..00000000 --- a/src/api/variable-assignment-apis.ts +++ /dev/null @@ -1,10 +0,0 @@ -import {VariableAssignmentApi} from "../interfaces/variable-assignment-api.interface"; - -export const variableAssignmentApis: { [key: string]: VariableAssignmentApi } = { - DATA_MODEL: { - url: "/package-manager/api/compute-pools/pools-with-data-models" - }, - CONNECTION: { - url: "/process-automation-v2/api/connections" - } -}; \ No newline at end of file diff --git a/src/api/variables-api.ts b/src/api/variables-api.ts deleted file mode 100644 index b1489f5b..00000000 --- a/src/api/variables-api.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { - ContentNodeTransport, - VariablesAssignments -} from "../interfaces/package-manager.interfaces"; -import {httpClientV2} from "../services/http-client-service.v2"; -import {FatalError} from "../util/logger"; -import {variableAssignmentApis} from "./variable-assignment-apis"; -import {URLSearchParams} from "url"; - -class VariablesApi { - public static readonly INSTANCE = new VariablesApi(); - public static readonly ASSIGNMENT_APIS = variableAssignmentApis; - - - public async assignVariableValues(packageKey: string, variablesAssignments: VariablesAssignments[]): Promise { - return httpClientV2.post(`/package-manager/api/nodes/by-package-key/${packageKey}/variables/values`, variablesAssignments).catch(e => { - throw new FatalError(`Problem updating variables of package ${packageKey}: ${e}`); - }); - } - - public async getCandidateAssignments(type: string, params: URLSearchParams): Promise { - if (!VariablesApi.ASSIGNMENT_APIS[type]) { - throw new FatalError(`Variable type ${type} not supported.`); - } - - const apiUrl: string = VariablesApi.ASSIGNMENT_APIS[type].url + (params.toString().length ? `?${params.toString()}` : ""); - - return httpClientV2.get(apiUrl).catch(e => { - throw new FatalError(`Problem getting variables assignment values for type ${type}: ${e}`); - }); - } - - public getRuntimeVariableValues(packageKey: string, appMode: string): Promise { - const queryParams = new URLSearchParams(); - queryParams.set("appMode", appMode); - - return httpClientV2.get(`/package-manager/api/nodes/by-package-key/${packageKey}/variables/runtime-values?${queryParams.toString()}`).catch(e => { - throw new FatalError(`Problem getting runtime variables of package ${packageKey}: ${e}`); - }); - } -} - -export const variablesApi = VariablesApi.INSTANCE; \ No newline at end of file diff --git a/src/commands/action-flow.command.ts b/src/commands/action-flow.command.ts deleted file mode 100644 index 6d0b4313..00000000 --- a/src/commands/action-flow.command.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { actionFlowService } from "../services/action-flow/action-flow-service"; - -export class ActionFlowCommand { - - public async exportActionFlows(packageId: string, metadataFile: string): Promise { - await actionFlowService.exportActionFlows(packageId, metadataFile); - } - - public async analyzeActionFlows(packageId: string, outputToJsonFile: boolean): Promise { - await actionFlowService.analyzeActionFlows(packageId, outputToJsonFile); - } - - public async importActionFlows(packageId: string, filePath: string, dryRun: boolean, outputToJsonFile: boolean): Promise { - await actionFlowService.importActionFlows(packageId, filePath, dryRun, outputToJsonFile); - } -} \ No newline at end of file diff --git a/src/commands/analysis-bookmarks.command.ts b/src/commands/analysis-bookmarks.command.ts deleted file mode 100644 index cbc62044..00000000 --- a/src/commands/analysis-bookmarks.command.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { ContentService } from "../services/content.service"; -import { AnalysisBookmarksManagerFactory } from "../content/factory/analysis-bookmarks-manager.factory"; - -export class AnalysisBookmarksCommand { - private contentService = new ContentService(); - private analysisBookmarksManagerFactory = new AnalysisBookmarksManagerFactory(); - - public async pullAnalysisBookmarks(profile: string, analysisId: string, type: string): Promise { - await this.contentService.pull( - profile, - this.analysisBookmarksManagerFactory.createAnalysisBookmarksManager(null, analysisId, type) - ); - } - - public async pushAnalysisBookmarks(profile: string, analysisId: string, filename: string): Promise { - await this.contentService.push(profile, this.analysisBookmarksManagerFactory.createAnalysisBookmarksManager(filename, analysisId)); - } -} diff --git a/src/commands/asset.command.ts b/src/commands/asset.command.ts deleted file mode 100644 index 19f2d6ea..00000000 --- a/src/commands/asset.command.ts +++ /dev/null @@ -1,28 +0,0 @@ -import {ContentService} from "../services/content.service"; -import {AssetManagerFactory} from "../content/factory/asset-manager.factory"; -import {assetService} from "../services/package-manager/asset-service"; - -export class AssetCommand { - private contentService = new ContentService(); - private assetManagerFactory = new AssetManagerFactory(); - - public async pullAsset(profile: string, key: string): Promise { - await this.contentService.pull(profile, this.assetManagerFactory.createManager(key)); - } - - public async pushAsset(profile: string, fileName: string, packageKey: string): Promise { - await this.contentService.push(profile, this.assetManagerFactory.createManager(null, fileName, packageKey)); - } - - public async pushAssets(profile: string, packageKey: string): Promise { - await this.contentService.batchPush(profile, this.assetManagerFactory.createManagers(packageKey)); - } - - public async listAssets(profile: string, jsonResponse: boolean, assetType: string): Promise { - if (jsonResponse) { - await assetService.findAndExportAllAssets(assetType); - } else { - await assetService.listAssets(assetType); - } - } -} diff --git a/src/commands/config.command.ts b/src/commands/config.command.ts deleted file mode 100644 index f2ad14ed..00000000 --- a/src/commands/config.command.ts +++ /dev/null @@ -1,34 +0,0 @@ -import {batchImportExportService} from "../services/package-manager/batch-import-export-service"; -import {variableService} from "../services/package-manager/variable-service"; -import {diffService} from "../services/package-manager/diff-service"; - -export class ConfigCommand { - - public async listActivePackages(jsonResponse: boolean, flavors: string[], withDependencies: boolean, packageKeys:string[]): Promise { - if (jsonResponse) { - await batchImportExportService.findAndExportListOfActivePackages(flavors ?? [], packageKeys ?? [], withDependencies) - } else { - await batchImportExportService.listActivePackages(flavors ?? []); - } - } - - public async listVariables(jsonResponse: boolean, keysByVersion: string[], keysByVersionFile: string): Promise { - if (jsonResponse) { - await variableService.exportVariables(keysByVersion, keysByVersionFile); - } else { - await variableService.listVariables(keysByVersion, keysByVersionFile); - } - } - - public batchExportPackages(packageKeys: string[], withDependencies: boolean = false): Promise { - return batchImportExportService.batchExportPackages(packageKeys, withDependencies); - } - - public batchImportPackages(file: string, overwrite: boolean): Promise { - return batchImportExportService.batchImportPackages(file, overwrite); - } - - public diffPackages(file: string, hasChanges: boolean, jsonResponse: boolean): Promise { - return diffService.diffPackages(file, hasChanges, jsonResponse); - } -} \ No newline at end of file diff --git a/src/commands/connection.command.ts b/src/commands/connection.command.ts deleted file mode 100644 index ab71d3a5..00000000 --- a/src/commands/connection.command.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { ContentService } from "../services/content.service"; -import { DataPoolManagerFactory } from "../content/factory/data-pool-manager.factory"; -import { connectionService } from "../services/connection/connection.service"; - -export class ConnectionCommand { - - public async updateProperty(profile: string, dataPoolId: string, connectionId: string, property: string, value: string): Promise{ - await connectionService.updateProperty(dataPoolId, connectionId, property, value); - } - - public async getProperties(profile: string, dataPoolId: string, connectionId: string): Promise { - await connectionService.listProperties(dataPoolId, connectionId); - } - - public async listConnections(profile: string, dataPoolId: string): Promise { - return await connectionService.findAllConnections(dataPoolId); - } -} diff --git a/src/commands/ctp.command.ts b/src/commands/ctp.command.ts deleted file mode 100644 index e4744ff5..00000000 --- a/src/commands/ctp.command.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { ContentService } from "../services/content.service"; -import { CTPManagerFactory } from "../content/factory/ctp-manager.factory"; -import { FatalError, logger } from "../util/logger"; - -export class CTPCommand { - private contentService = new ContentService(); - private ctpManagerFactory = new CTPManagerFactory(); - - public async pushCTPFile( - profile: string, - filename: string, - password: string, - pushAnalysis: boolean, - pushDataModels: boolean, - existingPoolId: string, - globalPoolName: string, - spaceKey: string - ): Promise { - if (pushAnalysis) { - await this.contentService.push( - profile, - this.ctpManagerFactory.createCtpAnalysisManager(filename, password, spaceKey) - ); - } - - if (pushDataModels) { - this.validateParamsForDataModelPush(existingPoolId, globalPoolName); - await this.contentService.push( - profile, - this.ctpManagerFactory.createCtpDataModelManager(filename, password, existingPoolId, globalPoolName) - ); - } - } - - private validateParamsForDataModelPush(existingPoolId: string, globalPoolName: string): void { - if (existingPoolId != null && globalPoolName != null) { - logger.error( - new FatalError( - "You should specify only one of those options --globalPoolName, --existingPoolId, they are mutual exclusive" - ) - ); - } - } -} diff --git a/src/commands/data-pool.command.ts b/src/commands/data-pool.command.ts deleted file mode 100644 index 185591a2..00000000 --- a/src/commands/data-pool.command.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { ContentService } from "../services/content.service"; -import { DataPoolManagerFactory } from "../content/factory/data-pool-manager.factory"; -import { dataPoolService } from "../services/data-pool/data-pool-service"; - -export class DataPoolCommand { - private contentService = new ContentService(); - private dataPoolManagerFactory = new DataPoolManagerFactory(); - - public async pullDataPool(profile: string, id: string): Promise { - await this.contentService.pull(profile, this.dataPoolManagerFactory.createManager(id, null)); - } - - public async pushDataPool(profile: string, filename: string): Promise { - await this.contentService.push(profile, this.dataPoolManagerFactory.createManager(null, filename)); - } - - public async exportDataPool(poolId: string, outputToJsonFile: boolean): Promise { - await dataPoolService.exportDataPool(poolId, outputToJsonFile); - } - - public async pushDataPools(profile: string): Promise { - await this.contentService.batchPush(profile, this.dataPoolManagerFactory.createManagers()); - } - - public async batchImportDataPools(requestFile: string, outputToJsonFile: boolean): Promise { - await dataPoolService.batchImportDataPools(requestFile, outputToJsonFile); - } - - public async updateDataPool(profile: string, id: string, filename: string): Promise { - await this.contentService.update(profile, this.dataPoolManagerFactory.createManager(id, filename)); - } - - public async listDataPools(profile: string, jsonResponse: boolean): Promise { - if (jsonResponse) { - await dataPoolService.findAndExportAllPools(); - } else { - await dataPoolService.listDataPools(); - } - } -} diff --git a/src/commands/package.command.ts b/src/commands/package.command.ts deleted file mode 100644 index 4ddfe94d..00000000 --- a/src/commands/package.command.ts +++ /dev/null @@ -1,54 +0,0 @@ -import {ContentService} from "../services/content.service"; -import {PackageManagerFactory} from "../content/factory/package-manager.factory"; -import {packageService} from "../services/package-manager/package-service"; - -export class PackageCommand { - private contentService = new ContentService(); - private packageManagerFactory = new PackageManagerFactory(); - - public async pullPackage( - profile: string, - key: string, - store: boolean, - newKey: string, - draft: boolean - ): Promise { - await this.contentService.pullFile( - profile, - this.packageManagerFactory.createPullManager(key, store, newKey, draft) - ); - } - - public async pushPackage( - profile: string, - spaceKey: string, - fileName: string, - newKey: string, - overwrite: boolean - ): Promise { - await this.contentService.push( - profile, - this.packageManagerFactory.createPushManager(spaceKey, fileName, newKey, overwrite) - ); - } - - public async pushPackages(profile: string, spaceKey: string): Promise { - await this.contentService.batchPush(profile, this.packageManagerFactory.createPushManagers(spaceKey)); - } - - public async listPackages(jsonResponse: boolean, includeDependencies: boolean, packageKeys: string[]): Promise { - if (jsonResponse) { - await packageService.findAndExportListOfAllPackages(includeDependencies, packageKeys ?? []); - } else { - await packageService.listPackages(); - } - } - - public async batchExportPackages(packageKeys: string[], includeDependencies: boolean, excludeActionFlows?: boolean): Promise { - await packageService.batchExportPackages(packageKeys, includeDependencies, excludeActionFlows); - } - - public async batchImportPackages(spaceMappings: string[], dataModelMappingsFilePath: string, exportedPackagesFile: string, overwrite: boolean, excludeActionFlows?: boolean): Promise { - await packageService.batchImportPackages(spaceMappings ?? [], dataModelMappingsFilePath, exportedPackagesFile, overwrite, excludeActionFlows); - } -} diff --git a/src/commands/profile.command.ts b/src/commands/profile.command.ts deleted file mode 100644 index 0badb10b..00000000 --- a/src/commands/profile.command.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { QuestionService } from "../services/question.service"; -import {Profile, ProfileType} from "../interfaces/profile.interface"; -import { ProfileService } from "../services/profile.service"; -import { ProfileValidator } from "../validators/profile.validator"; -import { FatalError, logger } from "../util/logger"; - -export class ProfileCommand { - private profileService = new ProfileService(); - - public async createProfile(setAsDefault: boolean): Promise { - const profile: Profile = {} as Profile; - profile.name = await QuestionService.ask("Name of the profile: "); - profile.team = await QuestionService.ask("Your team (please provide the full url): "); - const type = await QuestionService.ask("Profile type: OAuth Device Code (1), OAuth Client Credentials (2) or Application Key / API Key (3): " ); - switch (type) { - case "1": - profile.type = ProfileType.DEVICE_CODE; - break; - case "2": - profile.type = ProfileType.CLIENT_CREDENTIALS; - profile.clientId = await QuestionService.ask("Your client id: "); - profile.clientSecret = await QuestionService.ask("Your client secret: "); - break; - case "3": - profile.type = ProfileType.KEY; - profile.apiToken = await QuestionService.ask("Your api token: "); - break; - default: - logger.error(new FatalError("Invalid type")); - break; - } - profile.authenticationType = await ProfileValidator.validateProfile(profile); - await this.profileService.authorizeProfile(profile); - - this.profileService.storeProfile(profile); - if (setAsDefault) { - await this.makeDefaultProfile(profile.name); - } - logger.info("Profile created successfully!"); - } - - public async listProfiles(): Promise { - this.profileService.readAllProfiles().then((profiles: string[]) => { - const defaultProfile = this.profileService.getDefaultProfile(); - if (profiles) { - profiles.forEach(profile => { - if (defaultProfile && defaultProfile === profile) { - logger.info(profile + " (default)"); - } else { - logger.info(profile); - } - }); - } - }); - } - - public async makeDefaultProfile(profile: string): Promise { - await this.profileService.makeDefaultProfile(profile); - logger.info("Default profile: " + profile); - } -} \ No newline at end of file diff --git a/src/content-cli-refactor/commands/profile/profile-command.service.ts b/src/commands/profile/profile-command.service.ts similarity index 100% rename from src/content-cli-refactor/commands/profile/profile-command.service.ts rename to src/commands/profile/profile-command.service.ts diff --git a/src/content-cli-refactor/commands/profile/profile-module.ts b/src/commands/profile/profile-module.ts similarity index 100% rename from src/content-cli-refactor/commands/profile/profile-module.ts rename to src/commands/profile/profile-module.ts diff --git a/src/content-cli-refactor/commands/profile/question.service.ts b/src/commands/profile/question.service.ts similarity index 100% rename from src/content-cli-refactor/commands/profile/question.service.ts rename to src/commands/profile/question.service.ts diff --git a/src/commands/skill.command.ts b/src/commands/skill.command.ts deleted file mode 100644 index 6c3a9762..00000000 --- a/src/commands/skill.command.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { ContentService } from "../services/content.service"; -import { SkillManagerFactory } from "../content/factory/skill-manager.factory"; - -export class SkillCommand { - private contentService = new ContentService(); - private skillManagerFactory = new SkillManagerFactory(); - - public async pullSkill(profile: string, projectId: string, skillId: string): Promise { - await this.contentService.pull(profile, this.skillManagerFactory.createManager(projectId, skillId, null)); - } - - public async pushSkill(profile: string, projectId: string, filename: string): Promise { - await this.contentService.push(profile, this.skillManagerFactory.createManager(projectId, null, filename)); - } -} diff --git a/src/commands/space.command.ts b/src/commands/space.command.ts deleted file mode 100644 index 2aede049..00000000 --- a/src/commands/space.command.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { ContentService } from "../services/content.service"; -import { SpaceManagerFactory } from "../content/factory/space-manager.factory"; - -export class SpaceCommand { - private contentService = new ContentService(); - private spaceManagerFactory = new SpaceManagerFactory(); - - public async listSpaces(profile: string, jsonResponse: boolean): Promise { - await this.contentService.findAll(profile, this.spaceManagerFactory.createListManager(jsonResponse)); - } -} diff --git a/src/commands/variable.command.ts b/src/commands/variable.command.ts deleted file mode 100644 index 0f8426b8..00000000 --- a/src/commands/variable.command.ts +++ /dev/null @@ -1,12 +0,0 @@ -import {variableService} from "../services/package-manager/variable-service"; - -export class VariableCommand { - - public async listAssignments(variableType: string, jsonResponse: boolean, params: string): Promise { - if (jsonResponse) { - await variableService.findAndExportCandidateAssignments(variableType, params); - } else { - await variableService.listCandidateAssignments(variableType, params); - } - } -} diff --git a/src/commands/widget.command.ts b/src/commands/widget.command.ts deleted file mode 100644 index 69c5d0e2..00000000 --- a/src/commands/widget.command.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { ContentService } from "../services/content.service"; -import { WidgetManagerFactory } from "../content/factory/widget-manager.factory"; - -export class WidgetCommand { - private contentService = new ContentService(); - private widgetManagerFactory = new WidgetManagerFactory(); - - public async pushWidget(profile: string, tenantIndependent: boolean, userSpecific: boolean): Promise { - await this.contentService.push( - profile, - this.widgetManagerFactory.createManager(tenantIndependent, userSpecific) - ); - } -} diff --git a/src/content-cli-analyze.ts b/src/content-cli-analyze.ts deleted file mode 100644 index 8010c57d..00000000 --- a/src/content-cli-analyze.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { ContextInitializer } from "./util/context-initializer"; -import { logger } from "./util/logger"; -import { ActionFlowCommand } from "./commands/action-flow.command"; -import { program } from "./util/program"; -import { Command } from "commander"; - -class Analyze { - public static actionFlows(program: Command): Command { - program - .command("action-flows") - .description("Analyze Action Flows dependencies for a certain package") - .option("-p, --profile ", "Profile which you want to use to analyze Action Flows") - .requiredOption("--packageId ", "ID of the package from which you want to export Action Flows") - .option("-o, --outputToJsonFile", "Output the analyze result in a JSON file") - .action(async cmd => { - await new ActionFlowCommand().analyzeActionFlows(cmd.packageId, cmd.outputToJsonFile); - process.exit(); - }); - return program; - } -} - -const loadCommands = () => { - getAllCommands(); -}; - -ContextInitializer.initContext() - .then(loadCommands, loadCommands) - .catch(e => { - logger.error(e); - }); - -if (!process.argv.slice(2).length) { - program.outputHelp(); - process.exit(1); -} - -function getAllCommands(): void { - Analyze.actionFlows(program); - - program.parse(process.argv); -} diff --git a/src/content-cli-config.ts b/src/content-cli-config.ts deleted file mode 100644 index 060972b7..00000000 --- a/src/content-cli-config.ts +++ /dev/null @@ -1,108 +0,0 @@ -import {ConfigCommand} from "./commands/config.command"; -import { program } from "./util/program"; -import {logger} from "./util/logger"; -import {ContextInitializer} from "./util/context-initializer"; -import { Command } from "commander"; - -export class Config { - public static list(program: Command): Command { - program - .command("list") - .description("Command to list active packages that can be exported") - .option("-p, --profile ", "Profile which you want to use to list possible variable assignments") - .option("--json", "Return response as json type", "") - .option("--flavors ", "Lists only active packages of the given flavors") - .option("--withDependencies", "Include dependencies", "") - .option("--packageKeys ", "Lists only given package keys") - .action(async cmd => { - await new ConfigCommand().listActivePackages(cmd.json, cmd.flavors, cmd.withDependencies, cmd.packageKeys); - process.exit(); - }); - - return program; - } - - public static listVariables(program: Command): Command { - program - .command("variables") - .description("Commands related to variable configs") - .command("list") - .description("Command to list versioned variables of packages") - .option("-p, --profile ", "Profile which you want to use to list packages") - .option("--json", "Return response as json type", "") - .option("--keysByVersion ", "Mapping of package keys and versions", "") - .option("--keysByVersionFile ", "Package keys by version mappings file path.", "") - .action(async cmd => { - await new ConfigCommand().listVariables(cmd.json, cmd.keysByVersion, cmd.keysByVersionFile); - process.exit(); - }); - - return program; - } - - public static export(program: Command): Command { - program - .command("export") - .description("Command to export package configs") - .option("-p, --profile ", "Profile which you want to use to export packages") - .requiredOption("--packageKeys ", "Keys of packages to export") - .option("--withDependencies", "Include variables and dependencies", "") - .action(async cmd => { - await new ConfigCommand().batchExportPackages(cmd.packageKeys, cmd.withDependencies); - process.exit(); - }); - - return program; - } - - public static import(program: Command): Command { - program - .command("import") - .description("Command to import package configs") - .option("-p, --profile ", "Profile which you want to use to import packages") - .option("--overwrite", "Flag to allow overwriting of packages") - .requiredOption("-f, --file ", "Exported packages file (relative path)") - .action(async cmd => { - await new ConfigCommand().batchImportPackages(cmd.file, cmd.overwrite); - process.exit(); - }); - - return program; - } - - public static diff(program: Command): Command { - program - .command("diff") - .description("Command to diff configs of packages") - .option("-p, --profile ", "Profile of the team/realm which you want to use to diff the packages with") - .option("--hasChanges", "Flag to return only the information if the package has changes without the actual changes") - .option("--json", "Return the response as a JSON file") - .requiredOption("-f, --file ", "Exported packages file (relative or absolute path)") - .action(async cmd => { - await new ConfigCommand().diffPackages(cmd.file, cmd.hasChanges, cmd.json); - process.exit(); - }); - - return program; - } -} - -const loadAllCommands = () => { - Config.list(program); - Config.listVariables(program); - Config.export(program); - Config.import(program); - Config.diff(program) - program.parse(process.argv); -}; - -ContextInitializer.initContext() - .then(loadAllCommands, loadAllCommands) - .catch(e => { - logger.error(e); - }); - -if (!process.argv.slice(2).length) { - program.outputHelp(); - process.exit(1); -} diff --git a/src/content-cli-export.ts b/src/content-cli-export.ts deleted file mode 100644 index 3dc91a07..00000000 --- a/src/content-cli-export.ts +++ /dev/null @@ -1,78 +0,0 @@ -import { Command } from "commander"; -import { PackageCommand } from "./commands/package.command"; -import { logger } from "./util/logger"; -import { DataPoolCommand } from "./commands/data-pool.command"; -import { ContextInitializer } from "./util/context-initializer"; -import { ActionFlowCommand } from "./commands/action-flow.command"; -import { program } from "./util/program"; - -export class Export { - public static packages(program: Command): Command { - program - .command("packages") - .description("Command to export all given packages") - .option("-p, --profile ", "Profile which you want to use to list packages") - .requiredOption("--packageKeys ", "Exports only given package keys") - .option("--includeDependencies", "Include variables and dependencies", "") - .option("--excludeActionFlows", "Don't export action flows") - .action(async cmd => { - await new PackageCommand().batchExportPackages(cmd.packageKeys, cmd.includeDependencies, cmd.excludeActionFlows ?? false); - process.exit(); - }); - - return program; - } - - public static dataPool(program: Command): Command { - program - .command("data-pool") - .description("Command to export a data pool") - .option("-p, --profile ", "Profile which you want to use to export the data pool") - .requiredOption("--id ", "ID of the data pool you want to export") - .option("--outputToJsonFile", "Output the exported data pool to a JSON file") - .action(async cmd => { - await new DataPoolCommand().exportDataPool(cmd.id, cmd.outputToJsonFile); - process.exit(); - }); - - return program; - } - - public static actionFlows(program: Command): Command { - program - .command("action-flows") - .description("Command to export all Action Flows in a package with their objects and dependencies") - .option("-p, --profile ", "Profile which you want to use to export Action Flows") - .requiredOption("--packageId ", "ID of the package from which you want to export Action Flows") - .option("-f, --file ", "Action flows metadata file (relative path)") - .action(async cmd => { - await new ActionFlowCommand().exportActionFlows(cmd.packageId, cmd.file); - process.exit(); - }); - - return program; - } -} - -const loadCommands = () => { - getAllCommands(); -}; - -ContextInitializer.initContext() - .then(loadCommands, loadCommands) - .catch(e => { - logger.error(e); - }); - -if (!process.argv.slice(2).length) { - program.outputHelp(); - process.exit(1); -} - -function getAllCommands(): void { - Export.packages(program); - Export.dataPool(program); - Export.actionFlows(program); - - program.parse(process.argv); -} diff --git a/src/content-cli-get.ts b/src/content-cli-get.ts deleted file mode 100644 index 176e6e32..00000000 --- a/src/content-cli-get.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { ConnectionCommand } from "./commands/connection.command"; - -import { ContextInitializer } from "./util/context-initializer"; -import { logger } from "./util/logger"; -import { program } from "./util/program"; -import { Command } from "commander"; - -class Get { - public static connection(program: Command): Command { - program - .command("connection") - .description("Programmatically read properties of your connections") - .option("-p, --profile ", "Profile which you want to use to update the data pool configuration") - .requiredOption("--dataPoolId ", "Id of the data pool you want to update") - .requiredOption("--connectionId ", "Id of the connection you want to update") - .action(async cmd => { - await new ConnectionCommand().getProperties(cmd.profile, cmd.dataPoolId, cmd.connectionId); - process.exit(); - }); - return program; - } -} - -ContextInitializer.initContext() - .then(() => { - Get.connection(program); - program.parse(process.argv); - }) - .catch(e => { - logger.error(e); - }); - -if (!process.argv.slice(2).length) { - program.outputHelp(); - process.exit(1); -} diff --git a/src/content-cli-import.ts b/src/content-cli-import.ts deleted file mode 100644 index bf6350cf..00000000 --- a/src/content-cli-import.ts +++ /dev/null @@ -1,86 +0,0 @@ -import * as commander from "commander"; -import { Command } from "commander"; -import { PackageCommand } from "./commands/package.command"; -import { DataPoolCommand } from "./commands/data-pool.command"; -import { ContextInitializer } from "./util/context-initializer"; -import { logger } from "./util/logger"; -import { ActionFlowCommand } from "./commands/action-flow.command"; -import { program } from "./util/program"; - -export class Import { - public static packages(program: Command): Command { - program - .command("packages") - .description("Command to import all given packages") - .option("-p, --profile ", "Profile which you want to use to list packages") - .option( - "--spaceMappings ", - "List of mappings for importing packages to different target spaces. Mappings should follow format 'packageKey:targetSpaceKey'" - ) - .option("--overwrite", "Flag to allow overwriting of packages") - .option("--excludeActionFlows", "Skip overwrite of action flows of package") - .option("--dataModelMappingsFile ", "DataModel variable mappings file path. If missing, variables will be mapped from manifest file.") - .requiredOption("-f, --file ", "Exported packages file (relative path)") - .action(async cmd => { - await new PackageCommand().batchImportPackages(cmd.spaceMappings, cmd.dataModelMappingsFile, cmd.file, cmd.overwrite, cmd.excludeActionFlows); - process.exit(); - }); - - return program; - } - - public static dataPools(program: Command): Command { - program - .command("data-pools") - .description("Command to batch import multiple data pools with their objects and dependencies") - .option("-p, --profile ", "Profile which you want to use to import the data pools") - .requiredOption("-f, --jsonFile ", "The file with the JSON data pool batch import request") - .option("--outputToJsonFile", "Output the batch import result in a JSON file") - .action(async cmd => { - await new DataPoolCommand().batchImportDataPools(cmd.jsonFile, cmd.outputToJsonFile); - process.exit(); - }); - - return program; - } - - public static actionFlows(program: Command): Command { - program - .command("action-flows") - .description("Command to import all Action Flows in a package with their objects and dependencies") - .option("-p, --profile ", "Profile which you want to use to import Action Flows") - .requiredOption("--packageId ", "ID of the package to which you want to export Action Flows") - .requiredOption("-f, --file ", "Exported Action Flows file (relative path)") - .requiredOption("-d, --dryRun ", "Execute the import on dry run mode") - .option("-o, --outputToJsonFile", "Output the import result in a JSON file") - .action(async cmd => { - await new ActionFlowCommand().importActionFlows(cmd.packageId, cmd.file, cmd.dryRun, cmd.outputToJsonFile); - process.exit(); - }); - - return program; - } -} - -const loadCommands = () => { - getAllCommands(); -}; - -ContextInitializer.initContext() - .then(loadCommands, loadCommands) - .catch(e => { - logger.error(e); - }); - -if (!process.argv.slice(2).length) { - program.outputHelp(); - process.exit(1); -} - -function getAllCommands(): void { - Import.packages(program); - Import.dataPools(program); - Import.actionFlows(program); - - program.parse(process.argv); -} diff --git a/src/content-cli-list.ts b/src/content-cli-list.ts deleted file mode 100644 index 169723eb..00000000 --- a/src/content-cli-list.ts +++ /dev/null @@ -1,121 +0,0 @@ -import { PackageCommand } from "./commands/package.command"; -import { SpaceCommand } from "./commands/space.command"; -import { DataPoolCommand } from "./commands/data-pool.command"; -import { AssetCommand } from "./commands/asset.command"; -import { logger } from "./util/logger"; -import { ContextInitializer } from "./util/context-initializer"; -import { ConnectionCommand } from "./commands/connection.command"; -import { VariableCommand } from "./commands/variable.command"; -import { Command } from "commander"; -import { program } from "./util/program"; - -export class List { - public static packages(program: Command): Command { - program - .command("packages") - .description("Command to list all packages") - .option("-p, --profile ", "Profile which you want to use to list packages") - .option("--json", "Return response as json type", "") - .option("--includeDependencies", "Include variables and dependencies", "") - .option("--packageKeys ", "Lists only given package keys") - .action(async cmd => { - await new PackageCommand().listPackages(cmd.json, cmd.includeDependencies, cmd.packageKeys); - process.exit(); - }); - - return program; - } - - public static spaces(program: Command): Command { - program - .command("spaces") - .description("Command to list all spaces") - .option("-p, --profile ", "Profile which you want to use to list spaces") - .option("--json", "Return response as json type", "") - .action(async cmd => { - await new SpaceCommand().listSpaces(cmd.profile, cmd.json); - process.exit(); - }); - - return program; - } - - public static dataPools(program: Command): Command { - program - .command("data-pools") - .description("Command to list all Data Pools") - .option("-p, --profile ", "Profile which you want to use to list data pools") - .option("--json", "Return response as json type", "") - .action(async cmd => { - await new DataPoolCommand().listDataPools(cmd.profile, cmd.json); - process.exit(); - }); - - return program; - } - - public static connections(program: Command):Command { - program - .command("connection") - .description("Command to list all connections in a Data Pool") - .option("-p, --profile ", "Profile which you want to use to list connections") - .requiredOption("--dataPoolId ", "ID of the data pool") - .action(async cmd => { - await new ConnectionCommand().listConnections(cmd.profile, cmd.dataPoolId); - process.exit(); - }); - return program; - } - - public static assets(program: Command): Command { - program - .command("assets") - .description("Command to list all assets") - .option("-p, --profile ", "Profile which you want to use to list assets") - .option("--json", "Return response as json type", "") - .option("--assetType ", "type of assets") - .action(async cmd => { - await new AssetCommand().listAssets(cmd.profile, cmd.json, cmd.assetType); - process.exit(); - }); - - return program; - } - - public static assignments(program: Command): Command { - program - .command("assignments") - .description("Command to list possible variable assignments for a type") - .option("-p, --profile ", "Profile which you want to use to list possible variable assignments") - .option("--json", "Return response as json type", "") - .requiredOption("--type ", "Type of variable") - .option("--params ", "Variable query params") - .action(async cmd => { - await new VariableCommand().listAssignments(cmd.type, cmd.json, cmd.params); - process.exit(); - }); - - return program; - } -} - -const loadAllCommands = () => { - List.packages(program); - List.spaces(program); - List.dataPools(program); - List.assets(program); - List.connections(program); - List.assignments(program); - program.parse(process.argv); -}; - -ContextInitializer.initContext() - .then(loadAllCommands, loadAllCommands) - .catch(e => { - logger.error(e); - }); - -if (!process.argv.slice(2).length) { - program.outputHelp(); - process.exit(1); -} diff --git a/src/content-cli-profile.ts b/src/content-cli-profile.ts deleted file mode 100644 index 5bf8d69a..00000000 --- a/src/content-cli-profile.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { ProfileCommand } from "./commands/profile.command"; -import { Command } from "commander"; -import { program } from "./util/program"; - -class Profile { - public static listProfile(program: Command): Command { - program - .command("list") - .description("Command to list all stored profiles") - .action(async () => { - await new ProfileCommand().listProfiles(); - process.exit(); - }); - - return program; - } - - public static createProfile(program: Command): Command { - program - .command("create") - .description("Command to create a new profile") - .option("--setAsDefault", "Set this profile as default") - .action(async cmd => { - await new ProfileCommand().createProfile(cmd.setAsDefault); - process.exit(); - }); - - return program; - } - - public static defaultProfile(program: Command): Command { - program - .command("default ") - .description("Command to set a profile as default") - .action(async profile => { - await new ProfileCommand().makeDefaultProfile(profile); - process.exit(); - }); - - return program; - } -} - -Profile.listProfile(program); -Profile.createProfile(program); -Profile.defaultProfile(program); - -program.parse(process.argv); - -if (!process.argv.slice(2).length) { - program.outputHelp(); - process.exit(1); -} diff --git a/src/content-cli-pull.ts b/src/content-cli-pull.ts deleted file mode 100644 index 0b4f5a31..00000000 --- a/src/content-cli-pull.ts +++ /dev/null @@ -1,97 +0,0 @@ -import { SkillCommand } from "./commands/skill.command"; -import { DataPoolCommand } from "./commands/data-pool.command"; -import { AssetCommand } from "./commands/asset.command"; -import { PackageCommand } from "./commands/package.command"; -import { AnalysisBookmarksCommand } from "./commands/analysis-bookmarks.command"; -import { Command } from "commander"; -import { program } from "./util/program"; - -class Pull { - public static analysisBookmarks(program: Command): Command { - program - .command("bookmarks") - .description("Command to pull an analysis bookmarks") - .option("-p, --profile ", "Profile which you want to use to pull the analysis bookmarks") - .option("--type ", "Pull shared/all Analysis Bookmarks, else by default get user bookmarks") - .requiredOption("--id ", "Id of the analysis you want to pull") - .action(async cmd => { - await new AnalysisBookmarksCommand().pullAnalysisBookmarks(cmd.profile, cmd.id, cmd.type); - process.exit(); - }); - - return program; - } - - public static skill(program: Command): Command { - program - .command("skill") - .description("Command to pull a skill") - .option("-p, --profile ", "Profile which you want to use to pull the skill") - .requiredOption("--projectId ", "Id of the project you want to pull") - .requiredOption("--skillId ", "Id of the skill you want to pull") - .action(async cmd => { - await new SkillCommand().pullSkill(cmd.profile, cmd.projectId, cmd.skillId); - process.exit(); - }); - - return program; - } - - public static dataPool(program: Command): Command { - program - .command("data-pool") - .description("Command to pull a data pool") - .option("-p, --profile ", "Profile which you want to use to pull the data pool") - .requiredOption("--id ", "Id of the data pool you want to pull") - .action(async cmd => { - await new DataPoolCommand().pullDataPool(cmd.profile, cmd.id); - process.exit(); - }); - - return program; - } - - public static asset(program: Command): Command { - program - .command("asset") - .description("Command to pull an asset from Studio") - .option("-p, --profile ", "Profile which you want to use to pull the asset") - .requiredOption("--key ", "Key of asset you want to pull") - .action(async cmd => { - await new AssetCommand().pullAsset(cmd.profile, cmd.key); - process.exit(); - }); - - return program; - } - - public static package(program: Command): Command { - program - .command("package") - .description("Command to pull a package") - .option("-p, --profile ", "Profile which you want to use to pull the package") - .requiredOption("--key ", "Key of the package you want to pull") - .option("--store", "Pull package with store deployment metadata") - .option("--newKey ", "Define a new key for your package") - .option("--draft", "Pull draft version of package") - .action(async cmd => { - await new PackageCommand().pullPackage(cmd.profile, cmd.key, !!cmd.store, cmd.newKey, !!cmd.draft); - process.exit(); - }); - - return program; - } -} - -Pull.analysisBookmarks(program); -Pull.skill(program); -Pull.dataPool(program); -Pull.asset(program); -Pull.package(program); - -program.parse(process.argv); - -if (!process.argv.slice(2).length) { - program.outputHelp(); - process.exit(1); -} diff --git a/src/content-cli-push.ts b/src/content-cli-push.ts deleted file mode 100644 index 5e907e21..00000000 --- a/src/content-cli-push.ts +++ /dev/null @@ -1,223 +0,0 @@ -import * as fs from "fs"; -import * as path from "path"; - -import { SkillCommand } from "./commands/skill.command"; -import { WidgetCommand } from "./commands/widget.command"; -import { DataPoolCommand } from "./commands/data-pool.command"; -import { AssetCommand } from "./commands/asset.command"; -import { PackageCommand } from "./commands/package.command"; -import { CTPCommand } from "./commands/ctp.command"; -import { AnalysisBookmarksCommand } from "./commands/analysis-bookmarks.command"; -import { execSync } from "child_process"; -import { GracefulError, logger } from "./util/logger"; -import { Command } from "commander"; -import { program } from "./util/program"; - -class Push { - - public static analysisBookmarks(program: Command): Command { - program - .command("bookmarks") - .description("Command to push an analysis to a workspace") - .option("-p, --profile ", "Profile which you want to use to push the analysis") - .requiredOption("--id ", "Id of the Analysis to which you want to push the analysis bookmarks") - .requiredOption("-f, --file ", "The file you want to push") - .action(async cmd => { - await new AnalysisBookmarksCommand().pushAnalysisBookmarks(cmd.profile, cmd.id, cmd.file); - process.exit(); - }); - - return program; - } - - public static ctp(program: Command): Command { - program - .command("ctp") - .description("Command to push a .ctp (Celonis 4 transport file) to create a package") - .option("-p, --profile ", "Profile which you want to use to push the analysis") - .option("-a, --pushAnalysis", "Specify this option if you want to push analysis from the CTP file") - .option("-d, --pushDataModels", "Specify this option if you want to push data models from the CTP file") - .option( - "--globalPoolName ", - "Specify this option if you want to push all Data models into one newly created pool along with value to set the name of the pool to be created", - null - ) - .option( - "--existingPoolId ", - "Specify this option if you want to push all Data models into one already existing pool with provided ID", - null - ) - .option( - "-s, --spaceKey ", - "The key of the destination space where the analyses from .ctp file will be created.", - "" - ) - .requiredOption("-f, --file ", "The .ctp file you want to push") - .requiredOption("--password ", "The password used for extracting the .ctp file") - .action(async cmd => { - await new CTPCommand().pushCTPFile( - cmd.profile, - cmd.file, - cmd.password, - cmd.pushAnalysis, - cmd.pushDataModels, - cmd.existingPoolId, - cmd.globalPoolName, - cmd.spaceKey - ); - process.exit(); - }); - - return program; - } - - public static skill(program: Command): Command { - program - .command("skill") - .description("Command to push a skill to a project") - .option("-p, --profile ", "Profile which you want to use to push the skill") - .requiredOption("--projectId ", "Id of the project you want to push") - .requiredOption("-f, --file ", "The file you want to push") - .action(async cmd => { - await new SkillCommand().pushSkill(cmd.profile, cmd.projectId, cmd.file); - process.exit(); - }); - - return program; - } - - public static widget(program: Command): Command { - program - .command("widget") - .description("Command to push a widget") - .option("-p, --profile ", "Profile which you want to use to push the widget") - .option("--tenantIndependent", "Upload widget tenant independently") - .option("--userSpecific", "Upload widget only for the user in the provided api token") - .option("--packageManager", "Upload widget to package manager (deprecated)") // Deprecated - .action(async cmd => { - await new WidgetCommand().pushWidget(cmd.profile, !!cmd.tenantIndependent, !!cmd.userSpecific); - - if (process.env.AWS_ACCESS_KEY_ID_CDN && process.env.AWS_SECRET_ACCESS_KEY_CDN) { - try { - const dir = path.resolve(process.cwd()); - const pushToS3stdout = execSync( - `aws s3 cp ${dir} s3://celonis-static-origin/static/package-manager/ --recursive --exclude="*.map" --exclude="*.yaml" --profile default` - ).toString("utf-8"); - logger.info(pushToS3stdout); - } catch (error) { - logger.error(new GracefulError(error.stderr?.toString() || error.message)); - } - } - - const zipFileName = path.resolve(process.cwd(), "output.zip"); - fs.unlinkSync(zipFileName); - process.exit(); - }); - - return program; - } - - public static dataPool(program: Command): Command { - program - .command("data-pool") - .description("Command to push a data pool") - .option("-p, --profile ", "Profile which you want to use to push the data pool") - .requiredOption("-f, --file ", "The file you want to push") - .action(async cmd => { - await new DataPoolCommand().pushDataPool(cmd.profile, cmd.file); - process.exit(); - }); - return program; - } - - public static dataPools(program: Command): Command { - program - .command("data-pools") - .description("Command to push data pools") - .option("-p, --profile ", "Profile which you want to use to push the data pools") - .action(async cmd => { - await new DataPoolCommand().pushDataPools(cmd.profile); - process.exit(); - }); - - return program; - } - - public static asset(program: Command): Command { - program - .command("asset") - .description("Command to push an asset to Studio") - .option("-p, --profile ", "Profile which you want to use to push the asset") - .requiredOption("-f, --file ", "The file you want to push") - .requiredOption("--package ", "Key of the package you want to push asset to") - .action(async cmd => { - await new AssetCommand().pushAsset(cmd.profile, cmd.file, cmd.package); - process.exit(); - }); - - return program; - } - - public static assets(program: Command): Command { - program - .command("assets") - .description("Command to push assets to Studio") - .option("-p, --profile ", "Profile which you want to use to push the assets") - .requiredOption("--package ", "Key of the package you want to push assets to") - .action(async cmd => { - await new AssetCommand().pushAssets(cmd.profile, cmd.package); - process.exit(); - }); - - return program; - } - - public static package(program: Command): Command { - program - .command("package") - .description("Command to push a package to Studio") - .option("-p, --profile ", "Profile which you want to use to push the package") - .option("--newKey ", "Define a new key for your package") - .option("--overwrite", "Overwrite package and its assets") - .requiredOption("-f, --file ", "The file you want to push") - .requiredOption("--spaceKey ", "The key of the destination space") - .action(async cmd => { - await new PackageCommand().pushPackage(cmd.profile, cmd.spaceKey, cmd.file, cmd.newKey, cmd.overwrite); - process.exit(); - }); - - return program; - } - - public static packages(program: Command): Command { - program - .command("packages") - .description("Command to push packages to Studio") - .option("-p, --profile ", "Profile which you want to use to push the packages") - .requiredOption("--spaceKey ", "The key of the destination space") - .action(async cmd => { - await new PackageCommand().pushPackages(cmd.profile, cmd.spaceKey); - process.exit(); - }); - - return program; - } -} - -Push.analysisBookmarks(program); -Push.ctp(program); -Push.skill(program); -Push.widget(program); -Push.dataPool(program); -Push.dataPools(program); -Push.asset(program); -Push.assets(program); -Push.package(program); -Push.packages(program); - -program.parse(process.argv); - -if (!process.argv.slice(2).length) { - program.outputHelp(); - process.exit(1); -} diff --git a/src/content-cli-refactor/content-cli-refactored.ts b/src/content-cli-refactor/content-cli-refactored.ts deleted file mode 100644 index f8e9f40f..00000000 --- a/src/content-cli-refactor/content-cli-refactored.ts +++ /dev/null @@ -1,83 +0,0 @@ -#!/usr/bin/env node - -import semverSatisfies = require("semver/functions/satisfies"); -import { VersionUtils } from "../util/version"; -import { logger } from "../util/logger"; -import { Command } from "commander"; -import { Configurator, ModuleHandler } from "./core/command/module-handler"; -import { Context } from "./core/command/cli-context"; - -/** - * In-progress version of Content CLI. - * - * This is the main entry point for the CLI. - */ - -// Check if the Node.js version satisfies the minimum requirements -const requiredVersion = ">=10.10.0"; -if (!semverSatisfies(process.version, requiredVersion)) { - logger.error( - `Node version ${process.version} not supported. Please upgrade your node version to ${requiredVersion}` - ); - process.exit(1); -} - -// Global configuration options -const program: Command = new Command(); -program.version(VersionUtils.getCurrentCliVersion()); -program.option("-q, --quietmode", "Reduce output to a minimum", false); -program.option("-p, --profile [profile]"); -program.option("--debug", "Print debug messages", false); -program.parseOptions(process.argv); - -if (!program.opts().quietmode) { - console.log(`Content CLI - (C) Copyright 2025 - Celonis SE - Version ${VersionUtils.getCurrentCliVersion()}`); - console.log(); -} - -if (program.opts().debug) { - logger.transports.forEach(t => { - t.level = 'debug'; - }); -} - -/** - * To support the legacy command structure, we have to configure some root commands - * that the individual modules will extend. - */ -function configureRootCommands(configurator: Configurator) { - configurator.command("list") - .description("Commands to list content.") - .alias("ls"); -} - -async function run() { - let context = new Context(program.opts()); - await context.init(); - - let moduleHandler = new ModuleHandler(program, context); - - configureRootCommands(moduleHandler.configurator); - - moduleHandler.discoverAndRegisterModules(__dirname); - - if (!process.argv.slice(2).length) { - program.outputHelp(); - } - - try { - program.parse(process.argv); - } catch (error) { - logger.error(`An unexpected error occured: ${error}`); - } -} - -run(); - -// catch uncaught exceptions -process.on('uncaughtException', (error: Error, origin: NodeJS.UncaughtExceptionOrigin) => { - console.error(`\n💥 UNCAUGHT EXCEPTION!\n`); - console.error('Error:', error); - console.error('Origin:', origin); - process.exit(1); -}); \ No newline at end of file diff --git a/src/content-cli-refactor/core/utils/version.ts b/src/content-cli-refactor/core/utils/version.ts deleted file mode 100644 index 40bea993..00000000 --- a/src/content-cli-refactor/core/utils/version.ts +++ /dev/null @@ -1,8 +0,0 @@ -// tslint:disable-next-line:no-var-requires -const { version } = require("../../../../package.json"); - -export class VersionUtils { - public static getCurrentCliVersion(): string { - return version; - } -} diff --git a/src/content-cli-set.ts b/src/content-cli-set.ts deleted file mode 100644 index 7671731f..00000000 --- a/src/content-cli-set.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { ConnectionCommand } from "./commands/connection.command"; -import { ContextInitializer } from "./util/context-initializer"; -import { logger } from "./util/logger"; -import { Command } from "commander"; -import { program } from "./util/program"; - -class Set { - public static connection(program: Command): Command { - program - .command("connection") - .description("Programmatically update properties of your connections") - .option("-p, --profile ", "Profile which you want to use to update the data pool configuration") - .requiredOption("--dataPoolId ", "Id of the data pool you want to update") - .requiredOption("--connectionId ", "Id of the connection you want to update") - .requiredOption("--property ", "The property you want to update") - .requiredOption("--value ", "The value you want to update") - .action(async cmd => { - await new ConnectionCommand().updateProperty(cmd.profile, cmd.dataPoolId, cmd.connectionId, cmd.property, cmd.value); - process.exit(); - }); - - return program; - } -} - -const loadAllCommands = () => { - Set.connection(program); - program.parse(process.argv); -}; - -ContextInitializer.initContext() - .then(loadAllCommands, loadAllCommands) - .catch(e => { - logger.error(e); - }); - -if (!process.argv.slice(2).length) { - program.outputHelp(); - process.exit(1); -} diff --git a/src/content-cli-update.ts b/src/content-cli-update.ts deleted file mode 100644 index 25d2d785..00000000 --- a/src/content-cli-update.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { DataPoolCommand } from "./commands/data-pool.command"; -import { Command } from "commander"; -import { program } from "./util/program"; - -class Update { - public static dataPool(program: Command): Command { - program - .command("data-pool") - .description("Command to update a data pool using a data pool configuration file") - .option("-p, --profile ", "Profile which you want to use to update the data pool configuration") - .requiredOption("--id ", "Id of the data pool you want to update") - .requiredOption("-f, --file ", "The file you want to push") - .action(async cmd => { - await new DataPoolCommand().updateDataPool(cmd.profile, cmd.id, cmd.file); - process.exit(); - }); - - return program; - } -} - -Update.dataPool(program); -program.parse(process.argv); - -if (!process.argv.slice(2).length) { - program.outputHelp(); - process.exit(1); -} diff --git a/src/content-cli.ts b/src/content-cli.ts index e920b061..a036ad89 100644 --- a/src/content-cli.ts +++ b/src/content-cli.ts @@ -1,10 +1,19 @@ #!/usr/bin/env node -import { logger } from "./util/logger"; import semverSatisfies = require("semver/functions/satisfies"); -import { program } from "./util/program"; -import {VersionUtils} from "./util/version"; +import { Command } from "commander"; +import { Configurator, ModuleHandler } from "./core/command/module-handler"; +import { Context } from "./core/command/cli-context"; +import { VersionUtils } from "./core/utils/version"; +import { logger } from "./core/utils/logger"; +/** + * Celonis Content CLI. + * + * This is the main entry point for the CLI. + */ + +// Check if the Node.js version satisfies the minimum requirements const requiredVersion = ">=10.10.0"; if (!semverSatisfies(process.version, requiredVersion)) { logger.error( @@ -13,32 +22,62 @@ if (!semverSatisfies(process.version, requiredVersion)) { process.exit(1); } -program.command("profile", "Commands related to profiles."); - -program.command("pull", "Commands to pull content."); - -program.command("export", "Commands to export content.") -program.command("import", "Commands to import content.") - -program.command("push", "Commands to push content."); +// Global configuration options +const program: Command = new Command(); +program.version(VersionUtils.getCurrentCliVersion()); +program.option("-q, --quietmode", "Reduce output to a minimum", false); +program.option("-p, --profile [profile]"); +program.option("--debug", "Print debug messages", false); +program.parseOptions(process.argv); -program.command("update", "Commands to update content."); +if (!program.opts().quietmode) { + console.log(`Content CLI - (C) Copyright 2025 - Celonis SE - Version ${VersionUtils.getCurrentCliVersion()}`); + console.log(); +} -program.command("list", "Commands to list content.").alias("ls"); +if (program.opts().debug) { + logger.transports.forEach(t => { + t.level = 'debug'; + }); +} -program.command("get", "Commands to get configuration properties."); +/** + * To support the legacy command structure, we have to configure some root commands + * that the individual modules will extend. + */ +function configureRootCommands(configurator: Configurator) { + configurator.command("list") + .description("Commands to list content.") + .alias("ls"); +} -program.command("set", "Commands to set configuration properties."); +async function run() { + let context = new Context(program.opts()); + await context.init(); -program.command("config", "Commands related to config management.") + let moduleHandler = new ModuleHandler(program, context); + + configureRootCommands(moduleHandler.configurator); -program.command("analyze", "Commands to analyze assets dependencies."); + moduleHandler.discoverAndRegisterModules(__dirname); + + if (!process.argv.slice(2).length) { + program.outputHelp(); + } + + try { + program.parse(process.argv); + } catch (error) { + logger.error(`An unexpected error occured: ${error}`); + } +} -program.version(VersionUtils.getCurrentCliVersion()); -program.parse(process.argv); +run(); -if (!process.argv.slice(2).length) { - program.outputHelp(); +// catch uncaught exceptions +process.on('uncaughtException', (error: Error, origin: NodeJS.UncaughtExceptionOrigin) => { + console.error(`\n💥 UNCAUGHT EXCEPTION!\n`); + console.error('Error:', error); + console.error('Origin:', origin); process.exit(1); -} - +}); \ No newline at end of file diff --git a/src/content/factory/analysis-bookmarks-manager.factory.ts b/src/content/factory/analysis-bookmarks-manager.factory.ts deleted file mode 100644 index c29e3653..00000000 --- a/src/content/factory/analysis-bookmarks-manager.factory.ts +++ /dev/null @@ -1,31 +0,0 @@ -import * as fs from "fs"; -import * as path from "path"; -import { FatalError, logger } from "../../util/logger"; -import { AnalysisBookmarksManager } from "../manager/analysis-bookmarks.manager"; - -export class AnalysisBookmarksManagerFactory { - public createAnalysisBookmarksManager( - filename: string, - analysisId: string, - type?: string - ): AnalysisBookmarksManager { - const analysisBookmarksManager = new AnalysisBookmarksManager(); - analysisBookmarksManager.analysisId = analysisId; - if (type === undefined || type === null) { - type = "user"; - } - - analysisBookmarksManager.type = type; - if (filename !== null) { - analysisBookmarksManager.fileName = this.readFile(filename); - } - return analysisBookmarksManager; - } - - private readFile(fileName: string): string { - if (!fs.existsSync(path.resolve(process.cwd(), fileName))) { - logger.error(new FatalError("The provided file does not exist")); - } - return path.resolve(process.cwd(), fileName); - } -} diff --git a/src/content/factory/asset-manager.factory.ts b/src/content/factory/asset-manager.factory.ts deleted file mode 100644 index 45b35e07..00000000 --- a/src/content/factory/asset-manager.factory.ts +++ /dev/null @@ -1,50 +0,0 @@ -import * as fs from "fs"; -import * as path from "path"; - -import { FatalError, logger } from "../../util/logger"; -import { AssetManager } from "../manager/asset.manager"; - -export class AssetManagerFactory { - public createManager(key?: string, fileName?: string, packageKey?: string): AssetManager { - const assetManager = new AssetManager(); - assetManager.key = key; - - if (fileName) { - assetManager.content = this.readFile(fileName); - } - - assetManager.packageKey = packageKey; - return assetManager; - } - - public createManagers(packageKey: string): AssetManager[] { - const filePaths = fs.readdirSync(process.cwd()); - - return filePaths - .filter(filePath => { - if (!this.isAssetFilePath(filePath)) { - return false; - } - - const file = fs.lstatSync(filePath); - return file.isFile(); - }) - .map(filePath => { - return this.createManager(null, filePath, packageKey); - }); - } - - private isAssetFilePath(filePath: string): boolean { - return ( - filePath.startsWith(AssetManager.ASSET_FILE_PREFIX) && - (filePath.endsWith("yml") || filePath.endsWith("yaml")) - ); - } - - private readFile(fileName: string): string { - if (!fs.existsSync(path.resolve(process.cwd(), fileName))) { - logger.error(new FatalError("The provided file does not exit")); - } - return fs.readFileSync(path.resolve(process.cwd(), fileName), { encoding: "utf-8" }); - } -} diff --git a/src/content/factory/ctp-manager.factory.ts b/src/content/factory/ctp-manager.factory.ts deleted file mode 100644 index c751b94b..00000000 --- a/src/content/factory/ctp-manager.factory.ts +++ /dev/null @@ -1,40 +0,0 @@ -import * as fs from "fs"; -import * as path from "path"; -import { FatalError, logger } from "../../util/logger"; -import { CtpAnalysisManager } from "../manager/ctp.analysis.manager"; -import { ReadStream } from "fs"; -import { CtpManager } from "../manager/ctp.manager"; -import { CtpDataModelManager } from "../manager/ctp.datamodel.manager"; - -export class CTPManagerFactory { - public createCtpAnalysisManager(filename: string, password: string, spaceKey: string): CtpManager { - const ctpManager = new CtpAnalysisManager(); - return this.initManager(ctpManager, filename, password, spaceKey); - } - - public createCtpDataModelManager( - filename: string, - password: string, - existingPoolId: string, - globalPoolName: string - ): CtpManager { - const ctpManager = new CtpDataModelManager(existingPoolId, globalPoolName); - return this.initManager(ctpManager, filename, password); - } - - private initManager(ctpManager: CtpManager, filename: string, password: string, spaceKey?: string): CtpManager { - ctpManager.password = password; - ctpManager.spaceKey = spaceKey; - if (filename !== null) { - ctpManager.content = this.readFile(filename); - } - return ctpManager; - } - - private readFile(filename: string): ReadStream { - if (!fs.existsSync(path.resolve(process.cwd(), filename))) { - logger.error(new FatalError("The provided file does not exit")); - } - return fs.createReadStream(path.resolve(process.cwd(), filename)); - } -} diff --git a/src/content/factory/data-pool-manager.factory.ts b/src/content/factory/data-pool-manager.factory.ts deleted file mode 100644 index f0506d7e..00000000 --- a/src/content/factory/data-pool-manager.factory.ts +++ /dev/null @@ -1,39 +0,0 @@ -import * as fs from "fs"; -import * as path from "path"; -import { FatalError, logger } from "../../util/logger"; -import { DataPoolManager } from "../manager/data-pool.manager"; - -export class DataPoolManagerFactory { - public createManager(id: string, filename: string): DataPoolManager { - const dataPoolManager = new DataPoolManager(); - dataPoolManager.id = id; - if (filename !== null) { - dataPoolManager.content = this.readFile(filename); - } - return dataPoolManager; - } - - public createManagers(): DataPoolManager[] { - const dataPools = fs.readdirSync(process.cwd()); - return dataPools - .filter(filePath => { - if (filePath.startsWith(DataPoolManager.DATA_POOL_FILE_NAME_PREFIX) && filePath.endsWith(".json")) { - const file = fs.lstatSync(filePath); - return file.isFile(); - } - return false; - }) - .map(dataPool => { - const dataPoolManager = new DataPoolManager(); - dataPoolManager.content = this.readFile(dataPool); - return dataPoolManager; - }); - } - - private readFile(filename: string): string { - if (!fs.existsSync(path.resolve(process.cwd(), filename))) { - logger.error(new FatalError("The provided file does not exit")); - } - return fs.readFileSync(path.resolve(process.cwd(), filename), { encoding: "utf-8" }); - } -} diff --git a/src/content/factory/package-manager.factory.ts b/src/content/factory/package-manager.factory.ts deleted file mode 100644 index b42ff471..00000000 --- a/src/content/factory/package-manager.factory.ts +++ /dev/null @@ -1,72 +0,0 @@ -import * as fs from "fs"; -import * as path from "path"; - -import {FatalError, logger} from "../../util/logger"; -import {PackageManager} from "../manager/package.manager"; - -export class PackageManagerFactory { - public createPullManager(key: string, store?: boolean, newKey?: string, draft?: boolean): PackageManager { - return this.createManager(key, null, null, store, newKey, false, draft); - } - - public createPushManager(spaceKey: string, fileName: string, newKey?: string, overwrite?: boolean): PackageManager { - return this.createManager(null, spaceKey, fileName, false, newKey, overwrite, false); - } - - public createPushManagers(spaceKey: string): PackageManager[] { - const filePaths = fs.readdirSync(process.cwd()); - - return filePaths - .filter(filePath => { - if (!this.isPackageFilePath(filePath)) { - return false; - } - - const file = fs.lstatSync(filePath); - return file.isFile(); - }) - .map(filePath => { - return this.createPushManager(spaceKey, filePath); - }); - } - - public createManager( - key?: string, - spaceKey?: string, - fileName?: string, - store?: boolean, - newKey?: string, - overwrite?: boolean, - draft?: boolean, - ): PackageManager { - const packageManager = new PackageManager(); - - if (fileName) { - packageManager.fileName = this.resolvePackageFilePath(fileName); - } - - packageManager.key = key; - packageManager.spaceKey = spaceKey; - packageManager.store = store; - packageManager.newKey = newKey; - packageManager.overwrite = overwrite; - packageManager.draft = draft; - - return packageManager; - } - - private isPackageFilePath(filePath: string): boolean { - return ( - filePath.startsWith(PackageManager.PACKAGE_FILE_PREFIX) && - filePath.endsWith(PackageManager.PACKAGE_FILE_EXTENSION) - ); - } - - private resolvePackageFilePath(fileName: string): string { - if (!fs.existsSync(path.resolve(process.cwd(), fileName))) { - logger.error(new FatalError("The provided file does not exit")); - } - - return path.resolve(process.cwd(), fileName); - } -} diff --git a/src/content/factory/skill-manager.factory.ts b/src/content/factory/skill-manager.factory.ts deleted file mode 100644 index ba26a889..00000000 --- a/src/content/factory/skill-manager.factory.ts +++ /dev/null @@ -1,24 +0,0 @@ -import * as fs from "fs"; -import * as path from "path"; -import { FatalError, logger } from "../../util/logger"; -import { SkillManager } from "../manager/skill.manager"; -import { Stream } from "stream"; - -export class SkillManagerFactory { - public createManager(projectId: string, skillId: string, filename: string): SkillManager { - const skillManager = new SkillManager(); - skillManager.skillId = skillId; - skillManager.projectId = projectId; - if (filename !== null) { - skillManager.content = this.readFile(filename); - } - return skillManager; - } - - private readFile(filename: string): Stream { - if (!fs.existsSync(path.resolve(process.cwd(), filename))) { - logger.error(new FatalError("The provided file does not exit")); - } - return fs.createReadStream(path.resolve(process.cwd(), filename), { encoding: "binary" }); - } -} diff --git a/src/content/factory/space-manager.factory.ts b/src/content/factory/space-manager.factory.ts deleted file mode 100644 index b064dc6a..00000000 --- a/src/content/factory/space-manager.factory.ts +++ /dev/null @@ -1,16 +0,0 @@ -import {SpaceManager} from "../manager/space.manager"; - -export class SpaceManagerFactory { - - public createListManager(jsonResponse: boolean): SpaceManager { - return this.createManager(jsonResponse); - } - - public createManager( - jsonResponse?: boolean - ): SpaceManager { - const spaceManager = new SpaceManager(); - spaceManager.jsonResponse = jsonResponse; - return spaceManager; - } -} \ No newline at end of file diff --git a/src/content/factory/widget-manager.factory.ts b/src/content/factory/widget-manager.factory.ts deleted file mode 100644 index beb28417..00000000 --- a/src/content/factory/widget-manager.factory.ts +++ /dev/null @@ -1,87 +0,0 @@ -import * as fs from "fs"; -import * as path from "path"; -import { FatalError, logger } from "../../util/logger"; -import { WidgetManager } from "../manager/widget.manager"; -import * as AdmZip from "adm-zip"; -import {parse} from "../../util/yaml"; - -interface Manifest { - key: string; - name: string; - bundle: string; - version: string; - externalResource: string; - widgets: ManifestWidget[]; -} - -interface ManifestWidget { - widgetId: string; - name: string; -} - -export class WidgetManagerFactory { - public createManager(tenantIndependent: boolean = false, userSpecific: boolean = false): WidgetManager { - const widgetManager = new WidgetManager(); - widgetManager.content = this.readContent(); - widgetManager.tenantIndependent = tenantIndependent; - widgetManager.userSpecific = userSpecific; - return widgetManager; - } - - private readContent(): any { - const manifest = this.fetchManifest(); - - if (!manifest) { - logger.error(new FatalError("Missing manifest file.")); - } - - this.validateManifest(manifest); - - const zip = new AdmZip(); - const zipFileName = path.resolve(process.cwd(), "output.zip"); - zip.addLocalFolder(path.resolve(process.cwd())); - zip.writeZip(zipFileName); - return fs.createReadStream(path.resolve(process.cwd(), "output.zip")); - } - - public fetchManifest(): Manifest { - if (fs.existsSync(path.resolve(process.cwd(), "manifest.yaml"))) { - return parse(fs.readFileSync(path.resolve(process.cwd(), "manifest.yaml"), { encoding: "utf-8" })); - } - - if (fs.existsSync(path.resolve(process.cwd(), "manifest.yml"))) { - return parse(fs.readFileSync(path.resolve(process.cwd(), "manifest.yml"), { encoding: "utf-8" })); - } - - return null; - } - - public validateManifest(manifest: Manifest): void { - if (!manifest.bundle) { - logger.error(new FatalError("Missing 'bundle' attribute.")); - } - - if (!manifest.name) { - logger.error(new FatalError("Missing 'name' attribute.")); - } - - if (!manifest.key) { - logger.error(new FatalError("Missing 'key' attribute.")); - } - - if (!fs.existsSync(manifest.bundle)) { - logger.error(new FatalError("Missing bundle.")); - } - - if ( - fs.existsSync(path.resolve(process.cwd(), "assets")) && - !fs.existsSync(path.resolve(process.cwd(), "assets", "widgets", manifest.key)) - ) { - logger.error( - new FatalError( - "Assets directory does not exist. Assets should live under 'assets/widgets/" + manifest.key + "'." - ) - ); - } - } -} diff --git a/src/content/manager/analysis-bookmarks.manager.ts b/src/content/manager/analysis-bookmarks.manager.ts deleted file mode 100644 index 81a820a5..00000000 --- a/src/content/manager/analysis-bookmarks.manager.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { BaseManager } from "./base.manager"; -import { ManagerConfig } from "../../interfaces/manager-config.interface"; -import * as fs from "fs"; -import * as FormData from "form-data"; - -export class AnalysisBookmarksManager extends BaseManager { - private static BASE_URL = "/process-analytics/api/bookmarks"; - private static ANALYSIS_BOOKMARKS_FILE_PREFIX = "studio_analysis_bookmarks_"; - - private _analysisId: string; - private _fileName: string; - private _type: string; - - public get fileName(): string { - return this._fileName; - } - - public set fileName(value: string) { - this._fileName = value; - } - - public get analysisId(): string { - return this._analysisId; - } - - public set analysisId(value: string) { - this._analysisId = value; - } - - public get type(): string { - return this._type; - } - - public set type(value: string) { - this._type = value; - } - - public getConfig(): ManagerConfig { - const pullUrl = `${AnalysisBookmarksManager.BASE_URL}/export?analysisId=${this.analysisId}&type=${this.type}`; - return { - pushUrl: this.profile.team.replace( - /\/?$/, - `${AnalysisBookmarksManager.BASE_URL}/import?analysisId=${this.analysisId}` - ), - pullUrl: this.profile.team.replace(/\/?$/, pullUrl), - exportFileName: `${AnalysisBookmarksManager.ANALYSIS_BOOKMARKS_FILE_PREFIX}${this.analysisId}${".json"}`, - onPushSuccessMessage: (): string => { - return "Analysis Bookmarks was pushed successfully."; - }, - }; - } - - public getBody(): any { - const formData = new FormData(); - formData.append("file", fs.createReadStream(this.fileName)); - return formData; - } - - protected getSerializedFileContent(data: any): string { - return JSON.stringify(data); - } -} diff --git a/src/content/manager/asset.manager.ts b/src/content/manager/asset.manager.ts deleted file mode 100644 index 9db91c25..00000000 --- a/src/content/manager/asset.manager.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { BaseManager } from "./base.manager"; -import { ManagerConfig } from "../../interfaces/manager-config.interface"; -import { SaveContentNode } from "../../interfaces/save-content-node.interface"; -import { parse, stringify } from "../../util/yaml"; - -export class AssetManager extends BaseManager { - public static ASSET_FILE_PREFIX = "asset_"; - private static BASE_URL = "/package-manager/api/nodes"; - - private static PUSH_URL = `${AssetManager.BASE_URL}/asset/import`; - private static PULL_URL = `${AssetManager.BASE_URL}/asset/export`; - - private _key: string; - private _content: string; - private _packageKey: string; - - public get key(): string { - return this._key; - } - - public set key(value: string) { - this._key = value; - } - - public get content(): string { - return this._content; - } - public set content(value: string) { - this._content = value; - } - - public get packageKey(): string { - return this._packageKey; - } - - public set packageKey(value: string) { - this._packageKey = value; - } - - private get onlyKey(): string { - if (this.key) { - return this.key.split(".")[1]; - } - } - - public getConfig(): ManagerConfig { - return { - pushUrl: this.profile.team.replace(/\/?$/, AssetManager.PUSH_URL), - pullUrl: this.profile.team.replace(/\/?$/, `${AssetManager.PULL_URL}/${this.key}`), - exportFileName: `${AssetManager.ASSET_FILE_PREFIX}${this.onlyKey}.yml`, - onPushSuccessMessage: (data: any): string => { - return "Asset was pushed successfully. New key: " + data.rootWithKey; - }, - }; - } - - public getBody(): any { - return this.toNodeTransport(); - } - - private toNodeTransport(): SaveContentNode { - const asset = parse(this.content) as SaveContentNode; - asset.rootNodeKey = this.packageKey; - return asset; - } - - protected getSerializedFileContent(data: any): string { - return stringify(data); - } -} diff --git a/src/content/manager/base.manager.ts b/src/content/manager/base.manager.ts deleted file mode 100644 index 2db43926..00000000 --- a/src/content/manager/base.manager.ts +++ /dev/null @@ -1,128 +0,0 @@ -import { Profile } from "../../interfaces/profile.interface"; -import { FatalError, logger } from "../../util/logger"; -import { HttpClientService } from "../../services/http-client.service"; -import * as fs from "fs"; -import * as path from "path"; -import { ManagerConfig } from "../../interfaces/manager-config.interface"; - -export abstract class BaseManager { - private httpClientService = new HttpClientService(); - - public get profile(): Profile { - return this._profile; - } - - public set profile(value: Profile) { - this._profile = value; - } - - private _profile: Profile; - - protected readonly fileDownloadedMessage = "File downloaded successfully. New filename: "; - - public async pull(): Promise { - return new Promise((resolve, reject) => { - this.httpClientService - .pullData(this.getConfig().pullUrl, this._profile) - .then(data => { - try { - const filename = this.writeToFile(data); - logger.info(this.fileDownloadedMessage + filename); - resolve(); - } catch (e) { - logger.error(new FatalError(e)); - reject(); - } - }) - .catch(err => { - logger.error(new FatalError(err)); - reject(); - }); - }); - } - - public async pullFile(): Promise { - return new Promise((resolve, reject) => { - this.httpClientService - .pullFileData(this.getConfig().pullUrl, this._profile) - .then(data => { - const filename = this.writeStreamToFile(data); - logger.info(this.fileDownloadedMessage + filename); - resolve(); - }) - .catch(err => { - logger.error(new FatalError(err)); - reject(); - }); - }); - } - - public async push(): Promise { - return new Promise((resolve, reject) => { - this.httpClientService - .pushData(this.getConfig().pushUrl, this._profile, this.getBody()) - .then(data => { - logger.info(this.getConfig().onPushSuccessMessage(data)); - resolve(data); - }) - .catch(err => { - logger.error(new FatalError(err)); - reject(); - }); - }); - } - - public async update(): Promise { - return new Promise((resolve, reject) => { - this.httpClientService - .updateData(this.getConfig().updateUrl, this._profile, this.getBody()) - .then(data => { - logger.info(this.getConfig().onUpdateSuccessMessage()); - resolve(data); - }) - .catch(err => { - logger.error(new FatalError(err)); - reject(); - }); - }); - } - - public async findAll(): Promise { - return new Promise((resolve, reject) => { - this.httpClientService - .findAll(this.getConfig().findAllUrl, this._profile) - .then(data => { - this.getConfig().onFindAll(data); - resolve(data); - }) - .catch(err => { - logger.error(new FatalError(err)); - reject(); - }); - }); - } - - protected writeToFile(data: any): string { - const filename = this.getConfig().exportFileName; - this.writeToFileWithGivenName(data, filename); - return filename; - } - - protected writeStreamToFile(data: any): string { - const filename = this.getConfig().exportFileName; - fs.writeFileSync(filename, data); - return filename; - } - - protected writeToFileWithGivenName(data: any, filename: string): void { - fs.writeFileSync(path.resolve(process.cwd(), filename), this.getSerializedFileContent(data), { - encoding: "utf-8", - }); - } - - protected abstract getConfig(): ManagerConfig; - - protected abstract getBody(): object; - - protected abstract getSerializedFileContent(data: any): string; -} diff --git a/src/content/manager/ctp.analysis.manager.ts b/src/content/manager/ctp.analysis.manager.ts deleted file mode 100644 index e6044325..00000000 --- a/src/content/manager/ctp.analysis.manager.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { CtpManager } from "./ctp.manager"; -import * as FormData from "form-data"; - -export class CtpAnalysisManager extends CtpManager { - private static BASE_URL = "/process-analytics/import/ctp"; - - protected getUrl(): string { - return CtpAnalysisManager.BASE_URL; - } - - public getBody(): any { - const formData = new FormData(); - formData.append("file", this.content); - formData.append("password", this.password); - formData.append("spaceId", this.spaceKey); - return formData; - } -} diff --git a/src/content/manager/ctp.datamodel.manager.ts b/src/content/manager/ctp.datamodel.manager.ts deleted file mode 100644 index 43ee759f..00000000 --- a/src/content/manager/ctp.datamodel.manager.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { CtpManager } from "./ctp.manager"; -import * as FormData from "form-data"; - -export class CtpDataModelManager extends CtpManager { - private static BASE_URL = "/cpm-ems-migrator/migration/api/ctp"; - private readonly existingPoolId: string; - private readonly globalPoolName: string; - - constructor(existingPoolId: string, globalPoolName: string) { - super(); - this.existingPoolId = existingPoolId; - this.globalPoolName = globalPoolName; - } - - protected getUrl(): string { - return CtpDataModelManager.BASE_URL; - } - - protected getBody(): object { - const formData = new FormData(); - formData.append("file", this.content); - formData.append("transport", JSON.stringify({ - password: this.password, - existingPoolId: this.existingPoolId, - globalPoolName: this.globalPoolName, - })); - return formData; - } -} diff --git a/src/content/manager/ctp.manager.ts b/src/content/manager/ctp.manager.ts deleted file mode 100644 index 663be129..00000000 --- a/src/content/manager/ctp.manager.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { BaseManager } from "./base.manager"; -import { ManagerConfig } from "../../interfaces/manager-config.interface"; - -export abstract class CtpManager extends BaseManager { - protected _content: any; - protected _password: string; - protected _spaceKey?: string; - - public get content(): any { - return this._content; - } - - public set content(value: any) { - this._content = value; - } - - public get password(): string { - return this._password; - } - - public set password(value: string) { - this._password = value; - } - - public get spaceKey(): string { - return this._spaceKey; - } - - public set spaceKey(value: string) { - this._spaceKey = value; - } - - public getConfig(): ManagerConfig { - const baseUrl = this.getUrl(); - return { - pushUrl: this.profile.team.replace(/\/?$/, `${baseUrl}`), - onPushSuccessMessage: (): string => { - return "CTP File was pushed successfully"; - }, - }; - } - - protected getSerializedFileContent(data: any): string { - return JSON.stringify(data); - } - - protected abstract getUrl(): string; -} diff --git a/src/content/manager/data-pool.manager.ts b/src/content/manager/data-pool.manager.ts deleted file mode 100644 index 435512cb..00000000 --- a/src/content/manager/data-pool.manager.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { BaseManager } from "./base.manager"; -import { ManagerConfig } from "../../interfaces/manager-config.interface"; - -export class DataPoolManager extends BaseManager { - public static DATA_POOL_FILE_NAME_PREFIX = "data-pool_"; - private static API_URL = "/integration/api"; - private static DATA_POOL_PUSH_URL = DataPoolManager.API_URL + "/pool-import"; - private static DATA_POOL_ACTIONS_URL = DataPoolManager.API_URL + "/pools/{id}"; - private static DATA_POOL_PULL_URL = DataPoolManager.DATA_POOL_ACTIONS_URL + "/export"; - - private _id: string; - private _content: string; - - public get content(): string { - return this._content; - } - - public set content(value: string) { - this._content = value; - } - - public get id(): string { - return this._id; - } - - public set id(value: string) { - this._id = value; - } - - public getConfig(): ManagerConfig { - return { - pushUrl: this.profile.team.replace(/\/?$/, `${DataPoolManager.DATA_POOL_PUSH_URL}`), - pullUrl: this.profile.team - .replace(/\/?$/, `${DataPoolManager.DATA_POOL_PULL_URL}`) - .replace("{id}", this.id), - updateUrl: this.profile.team - .replace(/\/?$/, DataPoolManager.DATA_POOL_ACTIONS_URL) - .replace("{id}", this.id), - exportFileName: DataPoolManager.DATA_POOL_FILE_NAME_PREFIX + this.id + ".json", - onPushSuccessMessage: (data: any): string => { - return "Data Pool was pushed successfully. New ID: " + data.id; - }, - onUpdateSuccessMessage: (): string => { - return "Data Pool was updated successfully!"; - }, - }; - } - - public getBody(): any { - if (this.id != null) { - const parsedContent = JSON.parse(this.content); - return parsedContent.dataPool; - } - - return this.content; - } - - protected getSerializedFileContent(data: any): string { - return JSON.stringify(data); - } -} diff --git a/src/content/manager/package.manager.ts b/src/content/manager/package.manager.ts deleted file mode 100644 index 0c822320..00000000 --- a/src/content/manager/package.manager.ts +++ /dev/null @@ -1,143 +0,0 @@ -import {ManagerConfig} from "../../interfaces/manager-config.interface"; -import {BaseManager} from "./base.manager"; -import * as fs from "fs"; -import {logger} from "../../util/logger"; -import { - SaveContentNode, -} from "../../interfaces/save-content-node.interface"; -import * as FormData from "form-data"; - -export class PackageManager extends BaseManager { - public static PACKAGE_FILE_PREFIX = "package_"; - public static PACKAGE_FILE_EXTENSION = ".zip"; - - private static BASE_URL = "/package-manager/api/packages"; - - private static IMPORT_ENDPOINT_PATH = "import"; - private static EXPORT_ENDPOINT_PATH = "export"; - - private _key: string; - private _spaceKey: string; - private _fileName: string; - private _store: boolean; - private _newKey: string; - private _overwrite: boolean; - private _draft: boolean; - - public get key(): string { - return this._key; - } - - public set key(value: string) { - this._key = value; - } - - public get spaceKey(): string { - return this._spaceKey; - } - - public set spaceKey(value: string) { - this._spaceKey = value; - } - - public get fileName(): string { - return this._fileName; - } - - public set fileName(value: string) { - this._fileName = value; - } - - public get store(): boolean { - return this._store; - } - - public set store(value: boolean) { - this._store = value; - } - - public get newKey(): string { - return this._newKey; - } - - public set newKey(value: string) { - this._newKey = value; - } - - public get overwrite(): boolean { - return this._overwrite; - } - - public set overwrite(value: boolean) { - this._overwrite = value; - } - - public get draft(): boolean { - return this._draft; - } - - public set draft(value: boolean) { - this._draft = value; - } - - public getConfig(): ManagerConfig { - return { - pushUrl: this.profile.team.replace(/\/?$/, this.buildPushUrl()), - pullUrl: this.profile.team.replace( - /\/?$/, - `${PackageManager.BASE_URL}/${this.key}/${PackageManager.EXPORT_ENDPOINT_PATH}?store=${ - this.store - }&draft=${this.draft}${this.newKey ? `&newKey=${this.newKey}` : ""}` - ), - findAllUrl: this.profile.team.replace(/\/?$/, PackageManager.BASE_URL), - exportFileName: - PackageManager.PACKAGE_FILE_PREFIX + - (this.newKey ? this.newKey : this.key) + - PackageManager.PACKAGE_FILE_EXTENSION, - onPushSuccessMessage: (): string => "Package was pushed successfully.", - onFindAll: (data: SaveContentNode[]) => this.listPackages(data), - }; - } - - public getBody(): any { - const formData = new FormData(); - formData.append("package", fs.createReadStream(this.fileName)); - return formData; - } - - protected getSerializedFileContent(data: any): string { - return data; - } - - private buildPushUrl(): string { - this.validateOptions(); - const pushUrl = `${PackageManager.BASE_URL}/${PackageManager.IMPORT_ENDPOINT_PATH}`; - return this.getPushUrlWithParams(pushUrl); - } - - private listPackages(nodes: SaveContentNode[]): void { - nodes.forEach(node => { - logger.info(`${node.name} - Key: "${node.key}"`); - }); - } - - private validateOptions(): void { - if (this.newKey && this.overwrite) { - logger.error( - "You cannot overwrite a package and set a new key at the same time. Please use only one of the options." - ); - process.exit(); - } - } - - private getPushUrlWithParams(pushUrl: string): string { - const pushUrlWithParams = `${pushUrl}?spaceId=${this.spaceKey}&`; - if (this.newKey) { - return `${pushUrlWithParams}newKey=${this.newKey}`; - } - if (this.overwrite) { - return `${pushUrlWithParams}overwrite=${this.overwrite}`; - } - return pushUrlWithParams; - } -} diff --git a/src/content/manager/skill.manager.ts b/src/content/manager/skill.manager.ts deleted file mode 100644 index fd6ae237..00000000 --- a/src/content/manager/skill.manager.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { BaseManager } from "./base.manager"; -import { ManagerConfig } from "../../interfaces/manager-config.interface"; -import * as FormData from "form-data"; - -export class SkillManager extends BaseManager { - private static BASE_URL = "/action-engine/api/projects"; - private _skillId: string; - private _projectId: string; - private _content: any; - - public get content(): any { - return this._content; - } - - public set content(value: any) { - this._content = value; - } - public get skillId(): string { - return this._skillId; - } - - public set skillId(value: string) { - this._skillId = value; - } - - public get projectId(): string { - return this._projectId; - } - - public set projectId(value: string) { - this._projectId = value; - } - - public getConfig(): ManagerConfig { - return { - pushUrl: this.profile.team.replace(/\/?$/, `${SkillManager.BASE_URL}/${this.projectId}/skills/import-file`), - pullUrl: this.profile.team.replace( - /\/?$/, - `${SkillManager.BASE_URL}/${this.projectId}/skills/${this.skillId}/export` - ), - exportFileName: "skill_" + this.skillId + ".json", - onPushSuccessMessage: (skill: any): string => { - return "Skill was pushed successfully. New ID: " + skill.id; - }, - }; - } - - public getBody(): any { - const formData = new FormData(); - formData.append("file", this.content); - return formData; - } - - protected getSerializedFileContent(data: any): string { - return JSON.stringify(data); - } -} diff --git a/src/content/manager/space.manager.ts b/src/content/manager/space.manager.ts deleted file mode 100644 index 63b70d43..00000000 --- a/src/content/manager/space.manager.ts +++ /dev/null @@ -1,48 +0,0 @@ -import {ManagerConfig} from "../../interfaces/manager-config.interface"; -import {BaseManager} from "./base.manager"; -import {logger} from "../../util/logger"; -import {v4 as uuidv4} from "uuid"; -import {SpaceTransport} from "../../interfaces/save-space.interface"; - -export class SpaceManager extends BaseManager { - - private static BASE_URL = "/package-manager/api/spaces"; - - private _jsonResponse: boolean; - - public get jsonResponse(): boolean { - return this._jsonResponse; - } - - public set jsonResponse(value: boolean) { - this._jsonResponse = value; - } - - public getConfig(): ManagerConfig { - return { - findAllUrl: this.profile.team.replace(/\/?$/, SpaceManager.BASE_URL), - onFindAll: (data: SpaceTransport[]) => this.listSpaces(data), - }; - } - - private listSpaces(nodes: SpaceTransport[]): void { - if (this.jsonResponse) { - const filename = uuidv4() + ".json"; - this.writeToFileWithGivenName(JSON.stringify(nodes, ["id", "name"]), filename); - logger.info(this.fileDownloadedMessage + filename); - } else { - nodes.forEach(node => { - logger.info(`${node.id} - Name: "${node.name}"`); - }); - } - } - - protected getBody(): object { - return {}; - } - - protected getSerializedFileContent(data: any): string { - return data; - } - -} diff --git a/src/content/manager/widget.manager.ts b/src/content/manager/widget.manager.ts deleted file mode 100644 index 0a38680f..00000000 --- a/src/content/manager/widget.manager.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { BaseManager } from "./base.manager"; -import { ManagerConfig } from "../../interfaces/manager-config.interface"; -import * as FormData from "form-data"; - -export class WidgetManager extends BaseManager { - private static PACKAGE_MANAGER_BASE_URL = "/package-manager"; - private static WIDGET_API = "/api/widgets/upload"; - private static WIDGET_TENANT_INDEPENDENT_API = "/api/widgets/upload-tenant-independently"; - private static WIDGET_USER_SPECIFIC_API = "/api/widgets/upload-user"; - private _content: any; - private _tenantIndependent = false; - private _userSpecific = false; - - public get content(): any { - return this._content; - } - - public set content(value: any) { - this._content = value; - } - - public get tenantIndependent(): any { - return this._tenantIndependent; - } - - public set tenantIndependent(value: any) { - this._tenantIndependent = value; - } - - public get userSpecific(): any { - return this._userSpecific; - } - - public set userSpecific(value: any) { - this._userSpecific = value; - } - - public getConfig(): ManagerConfig { - const baseUrl = WidgetManager.PACKAGE_MANAGER_BASE_URL; - const widgetUrl = this.userSpecific - ? WidgetManager.WIDGET_USER_SPECIFIC_API - : this.tenantIndependent - ? WidgetManager.WIDGET_TENANT_INDEPENDENT_API - : WidgetManager.WIDGET_API; - return { - pushUrl: this.profile.team.replace(/\/?$/, `${baseUrl}${widgetUrl}`), - onPushSuccessMessage: (): string => { - return "Widget was pushed successfully"; - }, - }; - } - - public getBody(): any { - const formData = new FormData(); - formData.append("file", this.content); - return formData; - } - - protected getSerializedFileContent(data: any): string { - return data; - } -} diff --git a/src/content-cli-refactor/core/command/cli-context.ts b/src/core/command/cli-context.ts similarity index 100% rename from src/content-cli-refactor/core/command/cli-context.ts rename to src/core/command/cli-context.ts diff --git a/src/content-cli-refactor/core/command/module-handler.ts b/src/core/command/module-handler.ts similarity index 99% rename from src/content-cli-refactor/core/command/module-handler.ts rename to src/core/command/module-handler.ts index 8b7ddb2a..8814a0f8 100644 --- a/src/content-cli-refactor/core/command/module-handler.ts +++ b/src/core/command/module-handler.ts @@ -1,8 +1,8 @@ -import { logger } from "../../../util/logger"; import path = require("path"); import * as fs from "fs"; import { Command, CommandOptions, OptionValues } from "commander"; import { Context } from "./cli-context"; +import { logger } from "../utils/logger"; export abstract class IModule { abstract register(context: Context, commandConfig: Configurator); diff --git a/src/content-cli-refactor/core/http/axios-initializer.ts b/src/core/http/axios-initializer.ts similarity index 100% rename from src/content-cli-refactor/core/http/axios-initializer.ts rename to src/core/http/axios-initializer.ts diff --git a/src/content-cli-refactor/core/http/base-api.ts b/src/core/http/base-api.ts similarity index 100% rename from src/content-cli-refactor/core/http/base-api.ts rename to src/core/http/base-api.ts diff --git a/src/content-cli-refactor/core/http/http-client.ts b/src/core/http/http-client.ts similarity index 100% rename from src/content-cli-refactor/core/http/http-client.ts rename to src/core/http/http-client.ts diff --git a/src/content-cli-refactor/core/http/tracing.ts b/src/core/http/tracing.ts similarity index 100% rename from src/content-cli-refactor/core/http/tracing.ts rename to src/core/http/tracing.ts diff --git a/src/content-cli-refactor/core/profile/profile.interface.ts b/src/core/profile/profile.interface.ts similarity index 100% rename from src/content-cli-refactor/core/profile/profile.interface.ts rename to src/core/profile/profile.interface.ts diff --git a/src/content-cli-refactor/core/profile/profile.service.ts b/src/core/profile/profile.service.ts similarity index 100% rename from src/content-cli-refactor/core/profile/profile.service.ts rename to src/core/profile/profile.service.ts diff --git a/src/content-cli-refactor/core/profile/profile.validator.ts b/src/core/profile/profile.validator.ts similarity index 100% rename from src/content-cli-refactor/core/profile/profile.validator.ts rename to src/core/profile/profile.validator.ts diff --git a/src/content-cli-refactor/core/utils/file-service.ts b/src/core/utils/file-service.ts similarity index 100% rename from src/content-cli-refactor/core/utils/file-service.ts rename to src/core/utils/file-service.ts diff --git a/src/content-cli-refactor/core/utils/logger.ts b/src/core/utils/logger.ts similarity index 100% rename from src/content-cli-refactor/core/utils/logger.ts rename to src/core/utils/logger.ts diff --git a/src/util/version.ts b/src/core/utils/version.ts similarity index 74% rename from src/util/version.ts rename to src/core/utils/version.ts index 772a7299..78343fc5 100644 --- a/src/util/version.ts +++ b/src/core/utils/version.ts @@ -1,5 +1,5 @@ // tslint:disable-next-line:no-var-requires -const { version } = require("./../package.json"); +const { version } = require("../../../package.json"); export class VersionUtils { public static getCurrentCliVersion(): string { diff --git a/src/interfaces/batch-export-import-constants.ts b/src/interfaces/batch-export-import-constants.ts deleted file mode 100644 index a0c815ea..00000000 --- a/src/interfaces/batch-export-import-constants.ts +++ /dev/null @@ -1,11 +0,0 @@ -export enum BatchExportImportConstants { - STUDIO_FILE_NAME = "studio.json", - VARIABLES_FILE_NAME = "variables.json", - MANIFEST_FILE_NAME = "manifest.json", - STUDIO = "STUDIO", - APP_MODE_VIEWER = "VIEWER", - ZIP_EXTENSION = ".zip", - JSON_EXTENSION = ".json", - NODES_FOLDER_NAME = "nodes/", - SCENARIO_NODE = "SCENARIO" -} \ No newline at end of file diff --git a/src/interfaces/batch-export-node-transport.ts b/src/interfaces/batch-export-node-transport.ts deleted file mode 100644 index 91fc4a22..00000000 --- a/src/interfaces/batch-export-node-transport.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { - ContentNodeTransport, - PackageDependencyTransport, - PackageHistoryTransport, - StudioComputeNodeDescriptor, - VariablesAssignments -} from "./package-manager.interfaces"; -import {SpaceTransport} from "./save-space.interface"; - -export interface BatchExportNodeTransport extends ContentNodeTransport { - workingDraftId: string; - activatedDraftId: string; - version?: PackageHistoryTransport; - dependencies?: PackageDependencyTransport[]; - datamodels?: StudioComputeNodeDescriptor[]; - variables?: VariablesAssignments[]; - space?: SpaceTransport; -} - -export interface PackageAndAssetTransport { - rootNode: ContentNodeTransport, - nodes: ContentNodeTransport[] -} - -export interface SpaceMappingTransport { - packageKey: string, - spaceId: string -} \ No newline at end of file diff --git a/src/interfaces/data-pool-manager.interfaces.ts b/src/interfaces/data-pool-manager.interfaces.ts deleted file mode 100644 index 71c00bff..00000000 --- a/src/interfaces/data-pool-manager.interfaces.ts +++ /dev/null @@ -1,30 +0,0 @@ -export declare type DataPoolStatus = "CANCEL" | "FAIL" | "QUEUED" | "RUNNING" | "SKIPPED" | "SUCCESS" | "UNCONFIGURED"; -export type Tag = {}; - -export declare class DataSourceSlimTransport { - id: string; - name: string; - imported: boolean; - importedPoolId: string; -} - -export interface DataPoolSlimTransport { - id: string; - name: string; - status: DataPoolStatus; - lastExecutionStartDate: Date; - createdBy: string; - tags: Tag[]; - rawDataSize: number; - dataSources: DataSourceSlimTransport[]; -} -export declare class DataPoolPageTransport { - content: DataPoolSlimTransport[]; - pageSize: number; - pageNumber: number; - totalCount: number; -} - -export declare class DataPoolInstallVersionReport { - dataModelIdMappings: Map; -} diff --git a/src/interfaces/diff-package.transport.ts b/src/interfaces/diff-package.transport.ts deleted file mode 100644 index 507242a1..00000000 --- a/src/interfaces/diff-package.transport.ts +++ /dev/null @@ -1,34 +0,0 @@ -export interface ConfigurationChangeTransport { - op: string; - path: string; - from: string; - value: object; - fromValue: object; -} - -export enum NodeConfigurationChangeType { - ADDED = "ADDED", - DELETED = "DELETED", - CHANGED = "CHANGED", - UNCHANGED = "UNCHANGED", - INVALID = "INVALID" -} - -export interface NodeDiffTransport { - nodeKey: string; - name: string; - type: string; - changeType: NodeConfigurationChangeType; - changes: ConfigurationChangeTransport[]; -} - -export interface PackageDiffTransport { - packageKey: string; - packageChanges: ConfigurationChangeTransport[]; - nodesWithChanges: NodeDiffTransport[]; -} - -export interface PackageDiffMetadata { - packageKey: string; - hasChanges: boolean; -} \ No newline at end of file diff --git a/src/interfaces/manager-config.interface.ts b/src/interfaces/manager-config.interface.ts deleted file mode 100644 index abead4c4..00000000 --- a/src/interfaces/manager-config.interface.ts +++ /dev/null @@ -1,11 +0,0 @@ -export interface ManagerConfig { - pushUrl?: string; - pullUrl?: string; - updateUrl?: string; - findAllUrl?: string; - exportFileName?: string; - onPushSuccessMessage?: (data: any) => string; - onUpdateSuccessMessage?: () => string; - onFindAll?: (data: any) => void; - onFindAllAndExport?: (data: any) => void; -} diff --git a/src/interfaces/manifest-transport.ts b/src/interfaces/manifest-transport.ts deleted file mode 100644 index b3d42261..00000000 --- a/src/interfaces/manifest-transport.ts +++ /dev/null @@ -1,31 +0,0 @@ -import {VariablesAssignments} from "./package-manager.interfaces"; - -export interface ManifestNodeTransport { - packageKey: string; - packageId: string, - variables: ManifestVariable[], - space: ManifestSpace, - dependenciesByVersion: Map; -} - -export interface ManifestVariable extends VariablesAssignments { - dataPoolName?: string, - dataModelName?: string -} - -export interface ManifestSpace { - spaceName: string, - spaceIcon: string -} - -export interface ManifestDependency { - id: string; - key: string; - name: string; - version: string; - rootNodeId: string; - external: boolean - draftId: string; - updateAvailable: boolean; - deleted: boolean; -} diff --git a/src/interfaces/package-export-transport.ts b/src/interfaces/package-export-transport.ts deleted file mode 100644 index 9f20c518..00000000 --- a/src/interfaces/package-export-transport.ts +++ /dev/null @@ -1,82 +0,0 @@ -import {StudioComputeNodeDescriptor, VariableDefinition, VariablesAssignments} from "./package-manager.interfaces"; -import {SpaceTransport} from "./save-space.interface"; - -export interface DependencyTransport { - key: string; - version: string; -} - -export interface PackageExportTransport { - id: string; - key: string; - name: string; - changeDate: string; - activatedDraftId: string; - workingDraftId: string; - flavor: string; - version: string; - dependencies: DependencyTransport[]; - spaceId?: string; - datamodels?: StudioComputeNodeDescriptor[]; -} - -export interface PackageManifestTransport { - packageKey: string; - flavor: string; - activeVersion: string; - dependenciesByVersion: Map; -} - -export interface VariableExportTransport { - key: string; - value: any; - type: string; - metadata: object; -} - -export interface VariableManifestTransport { - packageKey: string; - version: string; - variables?: VariableExportTransport[]; -} - -export interface PackageKeyAndVersionPair { - packageKey: string; - version: string; -} - -export interface NodeExportTransport { - key: string; - parentNodeKey: string; - name: string; - type: string; - exportSerializationType: string; - configuration: NodeConfiguration; - schemaVersion: number; - - spaceId: string; - - invalidContent?: boolean; - serializedDocument?: Buffer; -} - -export interface NodeConfiguration { - variables?: VariableDefinition[]; - [key: string]: any; -} - -export interface StudioPackageManifest { - packageKey: string; - space: Partial; - runtimeVariableAssignments: VariablesAssignments[]; -} - -export interface PackageVersionImport { - oldVersion: string; - newVersion: string; -} - -export interface PostPackageImportData { - packageKey: string; - importedVersions: PackageVersionImport[]; -} \ No newline at end of file diff --git a/src/interfaces/package-manager.interfaces.ts b/src/interfaces/package-manager.interfaces.ts deleted file mode 100644 index 07b3eb1c..00000000 --- a/src/interfaces/package-manager.interfaces.ts +++ /dev/null @@ -1,99 +0,0 @@ -export interface AssetMetadataTransport { - hidden: boolean; -} - -export interface ContentNodeTransport { - id: string; - key: string; - name: string; - rootNodeKey: string; - workingDraftId: string; - activatedDraftId: string; - rootNodeId: string; - assetMetadataTransport: AssetMetadataTransport; - spaceId: string; -} - -export interface ActivatePackageTransport { - packageKey: string; - version: string; - publishMessage: string; - nodeIdsToExclude: string[]; -} - -export interface DataModelTransport { - id: string; - name: string, - poolId: string; -} - -export interface PackageDependencyTransport { - id: string; - key: string; - name: string; - version: string; - rootNodeId: string; - external: boolean - draftId: string; - updateAvailable: boolean; - deleted: boolean; -} - -export interface AssetMetadataTransport { - hidden: boolean; -} - -export interface PackageWithVariableAssignments { - id: string; - key: string; - name: string; - createdBy: string; - spaceId: string; - variableAssignments: VariablesAssignments[] -} - -export interface VariablesAssignments { - key: string; - value: object; - type: string; -} - -export interface VariableDefinition { - key: string; - type: PackageManagerVariableType; - description?: string; - source?: string; - runtime?: boolean; - metadata?: object; -} - -export enum PackageManagerVariableType { - DATA_MODEL="DATA_MODEL", - CONNECTION="CONNECTION", - ASSIGNMENT_RULE="ASSIGNMENT_RULE", - PLAIN_TEXT= "PLAIN_TEXT" -} - -export interface PackageHistoryTransport { - id: string; - key: string; - name: string; - version: string; -} - -export interface StudioDataModelTransport { - node: StudioComputeNodeDescriptor; - dataPool: ComputePoolTransport; -} - -export interface StudioComputeNodeDescriptor { - name: string; - dataModelId: string; - poolId: string; -} - -export interface ComputePoolTransport { - id: string; - name: string; -} - diff --git a/src/interfaces/profile.interface.ts b/src/interfaces/profile.interface.ts deleted file mode 100644 index ae21f547..00000000 --- a/src/interfaces/profile.interface.ts +++ /dev/null @@ -1,34 +0,0 @@ -export interface Profile { - name: string; - team: string; - type: ProfileType; - apiToken: string; - authenticationType: AuthenticationType; - clientId?: string; - clientSecret?: string; - scopes?: string[]; - clientAuthenticationMethod?: ClientAuthenticationMethod; - refreshToken?: string; - expiresAt?: number; -} - -export type AuthenticationType = "Bearer" | "AppKey"; -export type ProfileType = "Device Code" | "Client Credentials" | "Key"; - -export type ClientAuthenticationMethod = "client_secret_basic" | "client_secret_post"; -// tslint:disable-next-line:variable-name -export const AuthenticationType: { [key: string]: AuthenticationType } = { - BEARER: "Bearer", - APPKEY: "AppKey", -}; -// tslint:disable-next-line:variable-name -export const ProfileType: { [key: string]: ProfileType } = { - DEVICE_CODE: "Device Code", - CLIENT_CREDENTIALS: "Client Credentials", - KEY: "Key" -}; -// tslint:disable-next-line:variable-name -export const ClientAuthenticationMethod: { [key: string]: ClientAuthenticationMethod } = { - CLIENT_SECRET_BASIC: "client_secret_basic", - CLIENT_SECRET_POST: "client_secret_post", -}; diff --git a/src/interfaces/save-content-node.interface.ts b/src/interfaces/save-content-node.interface.ts deleted file mode 100644 index efca40d4..00000000 --- a/src/interfaces/save-content-node.interface.ts +++ /dev/null @@ -1,12 +0,0 @@ -import {AssetMetadataTransport} from "./package-manager.interfaces"; - -export interface SaveContentNode { - key: string; - rootNodeKey: string; - name: string; - nodeType: string; - assetType: string; - serializedContent: string; - assetMetadataTransport: AssetMetadataTransport; -} - diff --git a/src/interfaces/save-space.interface.ts b/src/interfaces/save-space.interface.ts deleted file mode 100644 index bd64fc53..00000000 --- a/src/interfaces/save-space.interface.ts +++ /dev/null @@ -1,5 +0,0 @@ -export interface SpaceTransport { - id: string; - name: string; - iconReference: string -} \ No newline at end of file diff --git a/src/interfaces/variable-assignment-api.interface.ts b/src/interfaces/variable-assignment-api.interface.ts deleted file mode 100644 index d18e9efb..00000000 --- a/src/interfaces/variable-assignment-api.interface.ts +++ /dev/null @@ -1,3 +0,0 @@ -export interface VariableAssignmentApi { - url: string; -} \ No newline at end of file diff --git a/src/services/action-flow/action-flow-service.ts b/src/services/action-flow/action-flow-service.ts deleted file mode 100644 index c3e1c162..00000000 --- a/src/services/action-flow/action-flow-service.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { logger } from "../../util/logger"; -import { v4 as uuidv4 } from "uuid"; -import { FileService, fileService } from "../file-service"; -import { actionFlowApi } from "../../api/action-flow-api"; -import * as AdmZip from "adm-zip"; -import * as FormData from "form-data"; -import * as fs from "fs"; - -class ActionFlowService { - public static readonly METADATA_FILE_NAME = "metadata.json"; - - public async exportActionFlows(packageId: string, metadataFilePath: string): Promise { - const exportedActionFlowsData = await actionFlowApi.exportRawAssets(packageId); - const tmpZip: AdmZip = new AdmZip(exportedActionFlowsData); - - const zip = new AdmZip(); - tmpZip.getEntries().forEach(entry => { - zip.addFile(entry.entryName, entry.getData()); - }); - - if (metadataFilePath) { - this.attachMetadataFile(metadataFilePath, zip); - } - - const fileName = "action-flows_export_" + uuidv4() + ".zip"; - zip.writeZip(fileName); - logger.info(FileService.fileDownloadedMessage + fileName); - } - - public async analyzeActionFlows(packageId: string, outputToJsonFile: boolean): Promise { - const actionFlowsMetadata = await actionFlowApi.analyzeAssets(packageId); - const actionFlowsMetadataString = JSON.stringify(actionFlowsMetadata, null, 4); - - if (outputToJsonFile) { - const metadataFileName = "action-flows_metadata_" + uuidv4() + ".json"; - fileService.writeToFileWithGivenName(actionFlowsMetadataString, metadataFileName); - logger.info(FileService.fileDownloadedMessage + metadataFileName); - } else { - logger.info("Action flows analyze metadata: \n" + actionFlowsMetadataString); - } - } - - public async importActionFlows(packageId: string, filePath: string, dryRun: boolean, outputToJsonFile: boolean): Promise { - const actionFlowsZip = this.createBodyForImport(filePath); - const eventLog = await actionFlowApi.importAssets(packageId, actionFlowsZip, dryRun); - const eventLogString = JSON.stringify(eventLog, null, 4); - - if (outputToJsonFile) { - const eventLogFileName = "action-flows_import_event_log_" + uuidv4() + ".json"; - fileService.writeToFileWithGivenName(eventLogString, eventLogFileName); - logger.info(FileService.fileDownloadedMessage + eventLogFileName); - } else { - logger.info("Action flows import event log: \n" + eventLogString); - } - } - - private createBodyForImport(fileName: string): FormData { - fileName = fileName + (fileName.endsWith(".zip") ? "" : ".zip"); - - const formData = new FormData(); - formData.append("file", fs.createReadStream(fileName, { encoding: null }), { filename: fileName }); - - return formData; - } - - private attachMetadataFile(fileName: string, zip: AdmZip): void { - fileName = fileName + (fileName.endsWith(".json") ? "" : ".json"); - const metadata = fileService.readFile(fileName); - - zip.addFile(ActionFlowService.METADATA_FILE_NAME, Buffer.from(metadata)); - } -} - -export const actionFlowService = new ActionFlowService(); -export const metadataFileName = ActionFlowService.METADATA_FILE_NAME; \ No newline at end of file diff --git a/src/services/command.service.ts b/src/services/command.service.ts deleted file mode 100644 index fc02b179..00000000 --- a/src/services/command.service.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { Command } from "commander"; - -export class CommandService { - private readonly command: Command; - - constructor() { - this.command = new Command(); - this.command.parseOptions(process.argv); - } - - public get program(): Command { - return this.command; - } -} \ No newline at end of file diff --git a/src/services/connection/connection.service.ts b/src/services/connection/connection.service.ts deleted file mode 100644 index 7882c022..00000000 --- a/src/services/connection/connection.service.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { dataPoolApi } from "../../api/data-pool-api"; -import { logger } from "../../util/logger"; - -class ConnectionService { - public async findAllConnections(dataPoolId: string): Promise { - const connections = await dataPoolApi.listConnections(dataPoolId); - connections.forEach(connection => { - logger.info(`Connection ID: ${connection.id} - Name: ${connection.name} - Type: ${connection.type}`); - }) - return connections; - } - - public async listProperties(dataPoolId: string, connectionId: string): Promise { - const connection = await dataPoolApi.getConnection(dataPoolId, connectionId); - const type = connection.type; - const typedConnection = await dataPoolApi.getTypedConnection(dataPoolId, connectionId, type); - logger.info(`Connection ID: ${connection.id} - Name: ${connection.name} - Type: ${connection.type}`); - logger.info(`Properties:`) - for (let k in typedConnection) { - if (typeof typedConnection[k] === 'object') { - for (let o in typedConnection[k]) { - logger.info(` ${k}.${o} : ${typeof typedConnection[k][o]} := ${typedConnection[k][o]}`) - } - } else { - logger.info(` ${k} : ${typeof typedConnection[k]} := ${typedConnection[k]}`); - } - } - return typedConnection; - } - - public async updateProperty(dataPoolId: string, connectionId: string, property: string, value: string) { - const connection = await dataPoolApi.getConnection(dataPoolId, connectionId); - const type = connection.type; - const typedConnection = await dataPoolApi.getTypedConnection(dataPoolId, connectionId, type); - const parts = property.split("."); - // update the typed connection object - let currentObject = typedConnection; - for (let i = 0; i < parts.length; i++) { - const part = parts[i]; - if (currentObject.hasOwnProperty(part)) { - if (i < parts.length - 1) { - currentObject = currentObject[part]; - } else { - currentObject[part] = value; - } - } else { - logger.error(`Property ${property} not found on connection.`) - return; - } - } - await dataPoolApi.updateTypedConnection(dataPoolId, connectionId, type, typedConnection); - logger.info(`Property ${property} updated.`); - } -} - -export const connectionService = new ConnectionService(); \ No newline at end of file diff --git a/src/services/content.service.ts b/src/services/content.service.ts deleted file mode 100644 index 7ec64ebd..00000000 --- a/src/services/content.service.ts +++ /dev/null @@ -1,124 +0,0 @@ -import { ProfileService } from "./profile.service"; -import { Profile } from "../interfaces/profile.interface"; -import { FatalError, logger } from "../util/logger"; -import { BaseManager } from "../content/manager/base.manager"; - -export class ContentService { - private profileService = new ProfileService(); - - public async pull(profileName: string, baseManager: BaseManager): Promise { - return new Promise((resolve, reject) => { - this.profileService - .findProfile(this.resolveProfile(profileName)) - .then((profile: Profile) => { - baseManager.profile = profile; - baseManager.pull().then( - () => resolve(), - () => reject() - ); - }) - .catch(err => { - logger.error(new FatalError(err)); - }); - }); - } - - public async pullFile(profileName: string, baseManager: BaseManager): Promise { - return new Promise((resolve, reject) => { - this.profileService - .findProfile(this.resolveProfile(profileName)) - .then((profile: Profile) => { - baseManager.profile = profile; - baseManager.pullFile().then( - () => resolve(), - () => reject() - ); - }) - .catch(err => { - logger.error(new FatalError(err)); - }); - }); - } - - public async push(profileName: string, baseManager: BaseManager): Promise { - return new Promise((resolve, reject) => { - this.profileService - .findProfile(this.resolveProfile(profileName)) - .then((profile: Profile) => { - baseManager.profile = profile; - baseManager.push().then( - () => resolve(), - () => reject() - ); - }) - .catch(err => { - logger.error(new FatalError(err)); - }); - }); - } - - public async batchPush(profileName: string, baseManagers: BaseManager[]): Promise { - return new Promise((resolve, reject) => { - this.profileService - .findProfile(this.resolveProfile(profileName)) - .then((profile: Profile) => { - const promises: Array> = []; - - baseManagers.forEach(baseManager => { - baseManager.profile = profile; - promises.push(baseManager.push()); - }); - - Promise.all(promises).then( - () => resolve(), - () => reject() - ); - }) - .catch(err => { - logger.error(new FatalError(err)); - }); - }); - } - - public async update(profileName: string, baseManager: BaseManager): Promise { - return new Promise((resolve, reject) => { - this.profileService - .findProfile(this.resolveProfile(profileName)) - .then((profile: Profile) => { - baseManager.profile = profile; - baseManager.update().then( - () => resolve(), - () => reject() - ); - }) - .catch(err => { - logger.error(new FatalError(err)); - }); - }); - } - - public async findAll(profileName: string, baseManager: BaseManager): Promise { - return new Promise((resolve, reject) => { - this.profileService - .findProfile(this.resolveProfile(profileName)) - .then((profile: Profile) => { - baseManager.profile = profile; - baseManager.findAll().then( - () => resolve(), - () => reject() - ); - }) - .catch(err => { - logger.error(new FatalError(err)); - }); - }); - } - - private resolveProfile(profile: string): string { - if (profile) { - return profile; - } - - return this.profileService.getDefaultProfile(); - } -} diff --git a/src/services/context.service.ts b/src/services/context.service.ts deleted file mode 100644 index ddb98733..00000000 --- a/src/services/context.service.ts +++ /dev/null @@ -1,26 +0,0 @@ -import {Profile} from "../interfaces/profile.interface"; -import {profileService} from "./profile.service"; - -interface Context { - profile: Profile; -} - -class ContextService { - public static readonly INSTANCE = new ContextService(); - private context: Context; - - public getContext(): Context { - return this.context; - } - - public async resolveProfile(profile: string): Promise { - const resolvedProfile = await profileService.findProfile(profile); - this.setContext({profile: resolvedProfile}); - }; - - public setContext(context: Context): void { - this.context = context; - } -} - -export const contextService = ContextService.INSTANCE; diff --git a/src/services/data-pool/data-pool-service.ts b/src/services/data-pool/data-pool-service.ts deleted file mode 100644 index 21e10c8d..00000000 --- a/src/services/data-pool/data-pool-service.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { dataPoolApi } from "../../api/data-pool-api"; -import { logger } from "../../util/logger"; -import { v4 as uuidv4 } from "uuid"; -import { FileService, fileService } from "../file-service"; -import { DataPoolSlimTransport } from "../../interfaces/data-pool-manager.interfaces"; - -class DataPoolService { - public async batchImportDataPools(requestFilePath: string, outputToJsonFile: boolean): Promise { - const requestFileContent: string = fileService.readFile(requestFilePath); - const importReport = await dataPoolApi.executeDataPoolsBatchImport(requestFileContent); - const importReportString = JSON.stringify(importReport, null, 4); - - if (outputToJsonFile) { - const reportFileName = "batch_import_report_" + uuidv4() + ".json"; - fileService.writeToFileWithGivenName(importReportString, reportFileName); - logger.info("Batch import report file: " + reportFileName); - } else { - logger.info("Batch import report: \n" + importReportString); - } - } - - public async exportDataPool(poolId: string, outputToJsonFile: boolean): Promise { - const exportedDataPool = await dataPoolApi.exportDataPool(poolId); - const exportedDataPoolString = JSON.stringify(exportedDataPool, null, 4); - - if (outputToJsonFile) { - const reportFileName = uuidv4() + "_data_pool_" + poolId + ".json"; - fileService.writeToFileWithGivenName(exportedDataPoolString, reportFileName); - logger.info(FileService.fileDownloadedMessage + reportFileName); - } else { - logger.info("Exported Data Pool: \n" + exportedDataPoolString); - } - } - - public async listDataPools(): Promise { - const dataPools = await this.findAllPools(); - dataPools.forEach(pool => { - logger.info(`Pool Id: ${pool.id} - Pool Name: ${pool.name}`); - }); - } - - public async findAndExportAllPools(): Promise { - const dataPools = await this.findAllPools(); - this.exportListOfPools(dataPools); - } - - private async findAllPools(): Promise { - let page = 0; - const dataPools: DataPoolSlimTransport[] = []; - let tmpPage = await dataPoolApi.findAllPagedPools("100", page.toString()); - while (tmpPage.pageNumber < tmpPage.totalCount) { - dataPools.push(...tmpPage.content); - tmpPage = await dataPoolApi.findAllPagedPools("100", (++page).toString()); - } - return dataPools; - } - - private exportListOfPools(nodes: DataPoolSlimTransport[]): void { - const filename = uuidv4() + ".json"; - fileService.writeToFileWithGivenName(JSON.stringify(nodes), filename); - logger.info(FileService.fileDownloadedMessage + filename); - } -} - -export const dataPoolService = new DataPoolService(); diff --git a/src/services/file-service.ts b/src/services/file-service.ts deleted file mode 100644 index 9da10aa4..00000000 --- a/src/services/file-service.ts +++ /dev/null @@ -1,45 +0,0 @@ -import * as fs from "fs"; -import * as path from "path"; -import { ManifestNodeTransport } from "../interfaces/manifest-transport"; -import { FatalError, logger } from "../util/logger"; -import {parse} from "../util/yaml"; - -export class FileService { - public static readonly fileDownloadedMessage = "File downloaded successfully. New filename: "; - - public writeToFileWithGivenName(data: any, filename: string): void { - fs.writeFileSync(path.resolve(process.cwd(), filename), this.getSerializedFileContent(data), { - encoding: "utf-8", - }); - } - - public createDirectoryWithGivenName(dirName: string): void { - fs.mkdirSync(path.resolve(process.cwd(), dirName)); - } - - public readManifestFile(importedFileName: string): Promise { - const manifest: ManifestNodeTransport[] = parse( - fs.readFileSync(path.resolve(importedFileName + "/manifest.yml"), { encoding: "utf-8" }) - ); - return Promise.all(manifest); - } - - public async readFileToJson(fileName: string): Promise { - const fileContent = this.readFile(fileName); - - return JSON.parse(fileContent); - } - - public readFile(filename: string): string { - if (!fs.existsSync(path.resolve(process.cwd(), filename))) { - logger.error(new FatalError(`The provided file '${filename}' does not exit`)); - } - return fs.readFileSync(path.resolve(process.cwd(), filename), {encoding: "utf-8"}); - } - - private getSerializedFileContent(data: any): string { - return data; - } -} - -export const fileService = new FileService(); diff --git a/src/services/http-client-service.v2.ts b/src/services/http-client-service.v2.ts deleted file mode 100644 index 63b128fc..00000000 --- a/src/services/http-client-service.v2.ts +++ /dev/null @@ -1,208 +0,0 @@ -import { AuthenticationType, Profile } from "../interfaces/profile.interface"; -import { contextService } from "./context.service"; -import { FatalError, logger } from "../util/logger"; -import {TracingUtils} from "../util/tracing"; -import {VersionUtils} from "../util/version"; -import {AxiosResponse, RawAxiosRequestHeaders} from "axios"; -import * as FormData from "form-data"; -import { AxiosInitializer } from "../util/axios-initializer"; - -class HttpClientServiceV2 { - - private axios = AxiosInitializer.initializeAxios(); - - public async get(url: string): Promise { - return new Promise((resolve, reject) => { - this.axios.get(this.resolveUrl(url), { - headers: this.buildHeaders(contextService.getContext().profile) - }).then(response => { - this.handleResponse(response, resolve, reject); - }).catch(err => { - this.handleError(err, resolve, reject); - }) - }).catch(e => { - throw new FatalError(e); - }) - } - - public async getFile(url: string): Promise { - return new Promise((resolve, reject) => { - this.axios.get(this.resolveUrl(url), { - headers: this.buildHeaders(contextService.getContext().profile), - responseType: "stream", - validateStatus: status => status >= 200 - }).then(response => { - const data: Buffer[] = []; - response.data.on("data", (chunk: Buffer) => { - data.push(chunk); - }); - response.data.on("end", () => { - if (response.status !== 200) { - reject(Buffer.concat(data).toString()); - return; - } - - this.handleResponseStreamData(Buffer.concat(data), resolve, reject); - }); - }).catch(err => { - this.handleError(err, resolve, reject); - }) - }); - } - - public async postFile(url: string, formData: FormData, parameters?: {}): Promise { - return new Promise((resolve, reject) => { - this.axios.post( - this.resolveUrl(url), - formData, - { - headers: { - ...this.buildHeaders(contextService.getContext().profile, "multipart/form-data"), - ...formData.getHeaders() - }, - params: parameters - } - ).then(response => { - this.handleResponse(response, resolve, reject); - }).catch(err => { - this.handleError(err, resolve, reject); - }) - }).catch(e => { - throw new FatalError(e); - }) - } - - public async post(url: string, body: any): Promise { - return new Promise((resolve, reject) => { - this.axios.post( - this.resolveUrl(url), - typeof body === "string" || body instanceof String ? body : JSON.stringify(body), - { - headers: this.buildHeaders(contextService.getContext().profile, "application/json;charset=utf-8") - } - ).then(response => { - this.handleResponse(response, resolve, reject); - }).catch(err => { - this.handleError(err, resolve, reject); - }) - }).catch(e => { - throw new FatalError(e); - }) - } - - public async put(url: string, body: object): Promise { - return new Promise((resolve, reject) => { - this.axios.put( - this.resolveUrl(url), - JSON.stringify(body), - { - headers: this.buildHeaders(contextService.getContext().profile, "application/json;charset=utf-8") - } - ).then(response => { - this.handleResponse(response, resolve, reject); - }).catch(err => { - this.handleError(err, resolve, reject); - }) - }).catch(e => { - throw new FatalError(e); - }) - } - - public async delete(url: string): Promise { - return new Promise((resolve, reject) => { - this.axios.delete(this.resolveUrl(url), { - headers: this.buildHeaders(contextService.getContext().profile, "application/json;charset=utf-8") - }).then(response => { - this.handleResponse(response, resolve, reject); - }).catch(err => { - this.handleError(err, resolve, reject); - }) - }).catch(e => { - throw new FatalError(e); - }) - } - - public async downloadFile(url: string): Promise { - return new Promise((resolve, reject) => { - this.axios.post(this.resolveUrl(url), null, { - headers: this.buildHeaders(contextService.getContext().profile), - responseType: "stream" - }).then(response => { - const data: Buffer[] = []; - response.data.on("data", (chunk: Buffer) => { - data.push(chunk); - }); - response.data.on("end", () => { - if (this.checkBadRequest(response.status)) { - this.handleBadRequest(response.status, data.toString(), reject); - } else { - this.handleResponseStreamData(Buffer.concat(data), resolve, reject); - } - }) - }).catch(err => { - this.handleError(err, resolve, reject); - }) - }); - } - - private handleResponseStreamData(data, resolve, reject): void { - if (data) { - resolve(data); - return; - } - - logger.error("Could not get file stream from response"); - reject(); - } - - private resolveUrl(url: string): string { - return contextService.getContext().profile.team.replace(/\/?$/, url); - } - - private handleResponse(res: AxiosResponse, resolve, reject): void { - if (this.checkBadRequest(res.status)) { - this.handleBadRequest(res.status, res.data, reject); - return; - } - resolve(res.data); - } - - private handleError(err: any, resolve, reject): void { - if (err.response) { - this.handleResponse(err.response, resolve, reject); - } else { - reject(err.message); - } - } - - private checkBadRequest(statusCode: number): boolean { - return statusCode >= 400; - } - - // tslint:disable-next-line:typedef - private handleBadRequest(statusCode, data, reject): void { - if (data) { - reject(JSON.stringify(data)); - } else { - reject("Backend responded with status code " + statusCode); - } - } - - private buildHeaders(profile: Profile, contentType?: string): RawAxiosRequestHeaders { - return { - ...this.buildAuthorizationHeaders(profile, contentType), - ...TracingUtils.getTracingHeaders(), - "User-Agent": "content-cli v" + VersionUtils.getCurrentCliVersion() - } - } - - private buildAuthorizationHeaders(profile: Profile, contentType?: string): RawAxiosRequestHeaders { - const authenticationType = profile.authenticationType || AuthenticationType.BEARER; - return { - Authorization: `${authenticationType} ${profile.apiToken}`, - "Content-Type": contentType ?? "application/json", - }; - } -} - -export const httpClientV2 = new HttpClientServiceV2(); diff --git a/src/services/http-client.service.ts b/src/services/http-client.service.ts deleted file mode 100644 index fa3d5d7d..00000000 --- a/src/services/http-client.service.ts +++ /dev/null @@ -1,136 +0,0 @@ -import { AuthenticationType, Profile } from "../interfaces/profile.interface"; -import {logger} from "../util/logger"; -import {AxiosResponse, RawAxiosRequestHeaders} from "axios"; -import * as FormData from "form-data"; -import { AxiosInitializer } from "../util/axios-initializer"; - -export class HttpClientService { - - private axios = AxiosInitializer.initializeAxios(); - - public async pushData(url: string, profile: Profile, body: any): Promise { - return new Promise((resolve, reject) => { - const headers = this.buildAuthorizationHeaders(profile); - - if (body instanceof FormData) { - headers["Content-Type"] = "multipart/form-data"; - } - - this.axios.post(url, body, { - headers - }).then(response => { - this.handleResponse(response, resolve, reject); - }).catch(err => { - this.handleError(err, resolve, reject); - }); - }); - } - - public async pullData(url: string, profile: Profile): Promise { - return new Promise((resolve, reject) => { - this.axios.get(url, { - headers: this.buildAuthorizationHeaders(profile) - }).then(response => { - this.handleResponse(response, resolve, reject); - }).catch(err => { - this.handleError(err, resolve, reject); - }); - }); - } - - public async pullFileData(url: string, profile: Profile): Promise { - return new Promise((resolve, reject) => { - this.axios.post(url, null, { - headers: this.buildAuthorizationHeaders(profile), - responseType: "stream" - }).then(response => { - const data: Buffer[] = []; - response.data.on("data", (chunk: Buffer) => { - data.push(chunk); - }); - response.data.on("end", () => { - if (this.checkBadRequest(response.status)) { - this.handleBadRequest(response.status, data.toString(), reject); - } else { - this.handleResponseStreamData(Buffer.concat(data), resolve, reject); - } - }) - }).catch(err => { - this.handleError(err, resolve, reject); - }); - }); - } - - public async updateData(url: string, profile: Profile, body: object): Promise { - return new Promise((resolve, reject) => { - this.axios.put(url, body, { - headers: this.buildAuthorizationHeaders(profile) - }).then(response => { - this.handleResponse(response, resolve, reject); - }).catch(err => { - this.handleError(err, resolve, reject); - }); - }); - } - - public async findAll(url: string, profile: Profile): Promise { - return new Promise((resolve, reject) => { - this.axios.get(url, { - headers: this.buildAuthorizationHeaders(profile) - }).then(response => { - this.handleResponse(response, resolve, reject); - }).catch(err => { - this.handleError(err, resolve, reject); - }); - }); - } - - private buildAuthorizationHeaders(profile: Profile): RawAxiosRequestHeaders { - const authenticationType = profile.authenticationType || AuthenticationType.BEARER; - return { - Authorization: `${authenticationType} ${profile.apiToken}`, - "Content-Type": "application/json", - }; - } - - // tslint:disable-next-line:typedef - private handleResponseStreamData(data, resolve, reject): void { - if (data) { - resolve(data); - return; - } - - logger.error("Could not get file stream from response"); - reject(); - } - - // tslint:disable-next-line:typedef - private handleResponse(res: AxiosResponse, resolve, reject): void { - if (this.checkBadRequest(res.status)) { - this.handleBadRequest(res.status, res.data, reject); - return; - } - resolve(res.data); - } - - private handleError(err: any, resolve, reject): void { - if (err.response) { - this.handleResponse(err.response, resolve, reject); - } else { - reject(err.message); - } - } - - private checkBadRequest(statusCode: number): boolean { - return statusCode >= 400; - } - - // tslint:disable-next-line:typedef - private handleBadRequest(statusCode, data, reject): void { - if (data) { - reject(JSON.stringify(data)); - } else { - reject("Backend responded with status code " + statusCode); - } - } -} diff --git a/src/services/package-manager/asset-service.ts b/src/services/package-manager/asset-service.ts deleted file mode 100644 index 274bbf80..00000000 --- a/src/services/package-manager/asset-service.ts +++ /dev/null @@ -1,28 +0,0 @@ -import {logger} from "../../util/logger"; -import {assetApi} from "../../api/asset-api"; -import {v4 as uuidv4} from "uuid"; -import {fileService} from "../file-service"; -import {SaveContentNode} from "../../interfaces/save-content-node.interface"; - -class AssetService { - protected readonly fileDownloadedMessage = "File downloaded successfully. New filename: "; - - public async listAssets(assetType: string): Promise { - const nodes = await assetApi.findAllAssets(assetType); - nodes.forEach(node => { - logger.info(`${node.name} - Key: "${node.key}"`); - }); - } - - public async findAndExportAllAssets(assetType: string): Promise { - const fieldsToInclude = ["key", "name", "assetType", "rootNodeKey", "activatedDraftId"]; - - const nodes: SaveContentNode[] = await assetApi.findAllAssets(assetType); - - const filename = uuidv4() + ".json"; - fileService.writeToFileWithGivenName(JSON.stringify(nodes, fieldsToInclude), filename); - logger.info(this.fileDownloadedMessage + filename); - } -} - -export const assetService = new AssetService(); \ No newline at end of file diff --git a/src/services/package-manager/batch-import-export-service.ts b/src/services/package-manager/batch-import-export-service.ts deleted file mode 100644 index b304d8d2..00000000 --- a/src/services/package-manager/batch-import-export-service.ts +++ /dev/null @@ -1,160 +0,0 @@ -import {batchImportExportApi} from "../../api/batch-import-export-api"; -import {logger} from "../../util/logger"; -import {v4 as uuidv4} from "uuid"; -import { - PackageExportTransport, - PackageKeyAndVersionPair, - PackageManifestTransport, - StudioPackageManifest, - VariableManifestTransport -} from "../../interfaces/package-export-transport"; -import {FileService, fileService} from "../file-service"; -import {studioService} from "../studio/studio.service"; -import {parse, stringify} from "../../util/json" -import * as FormData from "form-data"; -import {BatchExportImportConstants} from "../../interfaces/batch-export-import-constants"; -import {packageApi} from "../../api/package-api"; -import {Readable} from "stream"; -import * as AdmZip from "adm-zip"; - -class BatchImportExportService { - - public async listActivePackages(flavors: string[]): Promise { - const activePackages = await batchImportExportApi.findAllActivePackages(flavors); - activePackages.forEach(pkg => { - logger.info(`${pkg.name} - Key: "${pkg.key}"`) - }); - } - - public async findAndExportListOfActivePackages(flavors: string[], packageKeys: string[], withDependencies: boolean): Promise { - let packagesToExport: PackageExportTransport[]; - - if (packageKeys.length) { - packagesToExport = await batchImportExportApi.findActivePackagesByKeys(packageKeys, withDependencies); - } else { - packagesToExport = await batchImportExportApi.findAllActivePackages(flavors, withDependencies); - } - - packagesToExport = await studioService.getExportPackagesWithStudioData(packagesToExport, withDependencies); - - this.exportListOfPackages(packagesToExport); - } - - public async batchExportPackages(packageKeys: string[], withDependencies: boolean = false): Promise { - const exportedPackagesData: Buffer = await batchImportExportApi.exportPackages(packageKeys, withDependencies); - const exportedPackagesZip: AdmZip = new AdmZip(exportedPackagesData); - - const manifest: PackageManifestTransport[] = parse( - exportedPackagesZip.getEntry(BatchExportImportConstants.MANIFEST_FILE_NAME).getData().toString() - ); - - const versionsByPackageKey = this.getVersionsByPackageKey(manifest); - - let exportedVariables = await this.getVersionedVariablesForPackagesWithKeys(versionsByPackageKey); - exportedVariables = studioService.fixConnectionVariables(exportedVariables); - exportedPackagesZip.addFile(BatchExportImportConstants.VARIABLES_FILE_NAME, Buffer.from(stringify(exportedVariables), "utf8")); - - const studioPackageKeys = manifest.filter(packageManifest => packageManifest.flavor === BatchExportImportConstants.STUDIO) - .map(packageManifest => packageManifest.packageKey); - - const studioData = await studioService.getStudioPackageManifests(studioPackageKeys); - exportedPackagesZip.addFile(BatchExportImportConstants.STUDIO_FILE_NAME, Buffer.from(stringify(studioData), "utf8")); - - exportedPackagesZip.getEntries().forEach(entry => { - if (entry.name.endsWith(BatchExportImportConstants.ZIP_EXTENSION)) { - const lastUnderscoreIndex = entry.name.lastIndexOf("_"); - const packageKey = entry.name.substring(0, lastUnderscoreIndex); - - if (studioPackageKeys.includes(packageKey)) { - const updatedPackage = studioService.processPackageForExport(entry, exportedVariables); - exportedPackagesZip.updateFile(entry, updatedPackage.toBuffer()); - } - } - }); - - const fileDownloadedMessage = "File downloaded successfully. New filename: "; - const filename = `export_${uuidv4()}.zip`; - exportedPackagesZip.writeZip(filename); - logger.info(fileDownloadedMessage + filename); - } - - public async batchImportPackages(file: string, overwrite: boolean): Promise { - let configs = new AdmZip(file); - const studioManifests = this.parseEntryData(configs, BatchExportImportConstants.STUDIO_FILE_NAME) as StudioPackageManifest[]; - const variablesManifests: VariableManifestTransport[] = this.parseEntryData(configs, BatchExportImportConstants.VARIABLES_FILE_NAME) as VariableManifestTransport[]; - - configs = await studioService.mapSpaces(configs, studioManifests); - const existingStudioPackages = await packageApi.findAllPackages(); - - const formData = this.buildBodyForImport(configs, variablesManifests); - const postPackageImportData = await batchImportExportApi.importPackages(formData, overwrite); - await studioService.processImportedPackages(configs, existingStudioPackages, studioManifests); - - const reportFileName = "config_import_report_" + uuidv4() + ".json"; - fileService.writeToFileWithGivenName(JSON.stringify(postPackageImportData), reportFileName); - logger.info("Config import report file: " + reportFileName); - } - - private exportListOfPackages(packages: PackageExportTransport[]): void { - const filename = uuidv4() + ".json"; - fileService.writeToFileWithGivenName(JSON.stringify(packages), filename); - logger.info(FileService.fileDownloadedMessage + filename); - } - - private getVersionsByPackageKey(manifests: PackageManifestTransport[]): Map { - const versionsByPackageKey = new Map(); - manifests.forEach(packageManifest => { - versionsByPackageKey.set(packageManifest.packageKey, Object.keys(packageManifest.dependenciesByVersion)); - }) - - return versionsByPackageKey; - } - - private getVersionedVariablesForPackagesWithKeys(versionsByPackageKey: Map): Promise { - const variableExportRequest: PackageKeyAndVersionPair[] = []; - versionsByPackageKey?.forEach((versions, key) => { - versions?.forEach(version => { - variableExportRequest.push({ - packageKey: key, - version: version, - }) - }) - }); - - return batchImportExportApi.findVariablesWithValuesByPackageKeysAndVersion(variableExportRequest) - } - - private buildBodyForImport(configs: AdmZip, variablesManifests: VariableManifestTransport[]): FormData { - const formData = new FormData(); - const readableStream = this.getReadableStream(configs); - - formData.append("file", readableStream, {filename: "configs.zip"}); - - if (variablesManifests) { - formData.append("mappedVariables", JSON.stringify(variablesManifests), { - contentType: "application/json" - }); - } - - return formData; - } - - private getReadableStream(configs: AdmZip): Readable { - return new Readable({ - read(): void { - this.push(configs.toBuffer()); - this.push(null); - }, - }); - } - - private parseEntryData(configs: AdmZip, fileName: string): any { - const entry = configs.getEntry(fileName); - if (entry) { - return (parse(entry.getData().toString())); - } - return null; - } -} - -export const batchImportExportService = new BatchImportExportService(); \ No newline at end of file diff --git a/src/services/package-manager/datamodel-service.ts b/src/services/package-manager/datamodel-service.ts deleted file mode 100644 index 1f6854cf..00000000 --- a/src/services/package-manager/datamodel-service.ts +++ /dev/null @@ -1,25 +0,0 @@ -import {computePoolApi} from "../../api/compute-pool-api"; -import { - PackageWithVariableAssignments, - StudioComputeNodeDescriptor -} from "../../interfaces/package-manager.interfaces"; - -class DataModelService { - public static readonly INSTANCE = new DataModelService(); - - public async getDataModelDetailsForPackages(packagesWithDataModelVariables: PackageWithVariableAssignments[]): Promise> { - const dataModelsMap = new Map(); - const allAvailableDataModels = await computePoolApi.findAllDataModelsDetails(); - - for (const node of packagesWithDataModelVariables) { - const variablesOfPackage = packagesWithDataModelVariables.find(nodeWithVariablesAssignment => nodeWithVariablesAssignment.key === node.key)?.variableAssignments; - const dataModelIds = variablesOfPackage.filter(variable => variable.value).map(variable => variable.value.toString()); - - const assignedDataModels = allAvailableDataModels.filter(dataModel => dataModelIds.includes(dataModel.dataModelId)); - dataModelsMap.set(node.key, assignedDataModels); - } - return dataModelsMap; - } -} - -export const dataModelService = DataModelService.INSTANCE; \ No newline at end of file diff --git a/src/services/package-manager/diff-service.ts b/src/services/package-manager/diff-service.ts deleted file mode 100644 index aa178791..00000000 --- a/src/services/package-manager/diff-service.ts +++ /dev/null @@ -1,83 +0,0 @@ -import * as AdmZip from "adm-zip"; -import {Readable} from "stream"; -import * as FormData from "form-data"; -import {diffApi} from "../../api/diff-api"; -import {FileService, fileService} from "../file-service"; -import {logger} from "../../util/logger"; -import { PackageDiffMetadata, PackageDiffTransport } from "../../interfaces/diff-package.transport"; -import {v4 as uuidv4} from "uuid"; - -class DiffService { - - public async diffPackages(file: string, hasChanges: boolean, jsonResponse: boolean): Promise { - if (hasChanges) { - await this.hasChanges(file, jsonResponse); - } else { - await this.diffPackagesAndReturnDiff(file, jsonResponse); - } - } - - private async hasChanges(file: string, jsonResponse: boolean): Promise { - const packages = new AdmZip(file); - const formData = this.buildBodyForDiff(packages); - const returnedHasChangesData = await diffApi.hasChanges(formData); - - if (jsonResponse) { - this.exportListOfPackageDiffMetadata(returnedHasChangesData); - } else { - logger.info(this.buildStringResponseForPackageDiffMetadataList(returnedHasChangesData)); - } - } - - private async diffPackagesAndReturnDiff(file: string, jsonResponse: boolean): Promise { - const packages = new AdmZip(file); - const formData = this.buildBodyForDiff(packages); - const returnedHasChangesData = await diffApi.diffPackages(formData); - - if (jsonResponse) { - this.exportListOfPackageDiffs(returnedHasChangesData); - } else { - logger.info(this.buildStringResponseForPackageDiffs(returnedHasChangesData)); - } - } - - private buildBodyForDiff(packages: AdmZip): FormData { - const formData = new FormData(); - const readableStream = this.getReadableStream(packages); - - formData.append("file", readableStream, {filename: "packages.zip"}); - - return formData; - } - - private getReadableStream(packages: AdmZip): Readable { - return new Readable({ - read(): void { - this.push(packages.toBuffer()); - this.push(null); - } - }); - } - - private exportListOfPackageDiffs(packageDiffs: PackageDiffTransport[]): void { - const filename = uuidv4() + ".json"; - fileService.writeToFileWithGivenName(JSON.stringify(packageDiffs), filename); - logger.info(FileService.fileDownloadedMessage + filename); - } - - private exportListOfPackageDiffMetadata(packageDiffMetadata: PackageDiffMetadata[]): void { - const filename = uuidv4() + ".json"; - fileService.writeToFileWithGivenName(JSON.stringify(packageDiffMetadata), filename); - logger.info(FileService.fileDownloadedMessage + filename); - } - - private buildStringResponseForPackageDiffs(packageDiffs: PackageDiffTransport[]): string { - return "\n" + JSON.stringify(packageDiffs, null, 2); - } - - private buildStringResponseForPackageDiffMetadataList(packageDiffMetadata: PackageDiffMetadata[]): string { - return "\n" + JSON.stringify(packageDiffMetadata, null, 2); - } -} - -export const diffService = new DiffService(); \ No newline at end of file diff --git a/src/services/package-manager/package-service.ts b/src/services/package-manager/package-service.ts deleted file mode 100644 index bba49197..00000000 --- a/src/services/package-manager/package-service.ts +++ /dev/null @@ -1,513 +0,0 @@ -import {FatalError, logger} from "../../util/logger"; -import {packageApi} from "../../api/package-api"; -import {v4 as uuidv4} from "uuid"; -import {FileService, fileService} from "../file-service"; -import {BatchExportNodeTransport} from "../../interfaces/batch-export-node-transport"; -import {dataModelService} from "./datamodel-service"; -import { - ContentNodeTransport, - PackageDependencyTransport, - PackageManagerVariableType -} from "../../interfaces/package-manager.interfaces"; -import {nodeApi} from "../../api/node-api"; -import {packageDependenciesApi} from "../../api/package-dependencies-api"; -import {variableService} from "./variable-service"; -import {spaceService} from "./space-service"; -import * as fs from "fs"; -import AdmZip = require("adm-zip"); -import * as path from "path"; -import {tmpdir} from "os"; -import {SpaceTransport} from "../../interfaces/save-space.interface"; -import {ManifestDependency, ManifestNodeTransport} from "../../interfaces/manifest-transport"; -import {DataPoolInstallVersionReport} from "../../interfaces/data-pool-manager.interfaces"; -import {SemanticVersioning} from "../../util/semantic-versioning" -import {stringify} from "../../util/yaml"; -import * as FormData from "form-data"; - -class PackageService { - protected readonly fileDownloadedMessage = "File downloaded successfully. New filename: "; - - public async listPackages(): Promise { - const nodes = await packageApi.findAllPackages(); - nodes.forEach(node => { - logger.info(`${node.name} - Key: "${node.key}"`); - }); - } - - public async findAndExportListOfAllPackages(includeDependencies: boolean, packageKeys: string[]): Promise { - const fieldsToInclude = ["key", "name", "changeDate", "activatedDraftId", "workingDraftId", "spaceId"]; - - let nodesListToExport: BatchExportNodeTransport[] = await packageApi.findAllPackages(); - if (packageKeys.length > 0) { - nodesListToExport = nodesListToExport.filter(node => packageKeys.includes(node.rootNodeKey)); - } - - if (includeDependencies) { - fieldsToInclude.push("type", "value", "dependencies", "id", "updateAvailable", "version", "poolId", "node", "dataModelId", "dataPool", "datamodels"); - const unPublishedNodes = nodesListToExport.filter(node => !node.activatedDraftId); - let publishedNodes = nodesListToExport.filter(node => node.activatedDraftId); - publishedNodes = await this.getNodesWithActiveVersion(publishedNodes); - nodesListToExport = [...publishedNodes, ...unPublishedNodes]; - - const packageWithDataModelVariableAssignments = await variableService.getVariableAssignmentsForNodes(PackageManagerVariableType.DATA_MODEL); - const dataModelDetailsByNode = await dataModelService.getDataModelDetailsForPackages(packageWithDataModelVariableAssignments); - nodesListToExport.forEach(node => { - node.datamodels = dataModelDetailsByNode.get(node.key); - }); - - const draftIdByNodeId = new Map(); - nodesListToExport.forEach(node => draftIdByNodeId.set(node.workingDraftId, node.id)); - - const dependenciesByPackageIds = await this.getPackagesWithDependencies(draftIdByNodeId); - - nodesListToExport = nodesListToExport.map(nodeToExport => { - nodeToExport.dependencies = dependenciesByPackageIds[nodeToExport.workingDraftId] ?? []; - return nodeToExport; - }) - } - this.exportListOfPackages(nodesListToExport, fieldsToInclude); - } - - public async batchImportPackages(spaceMappings: string[], dataModelMappingsFilePath: string, exportedPackagesFile: string, overwrite: boolean, excludeActionFlows: boolean): Promise { - exportedPackagesFile = exportedPackagesFile + (exportedPackagesFile.includes(".zip") ? "" : ".zip"); - const zip = new AdmZip(exportedPackagesFile); - const importedFilePath = path.resolve(tmpdir(), "export_" + uuidv4()); - await fs.mkdirSync(importedFilePath); - await zip.extractAllTo(importedFilePath); - - const manifestNodes = await fileService.readManifestFile(importedFilePath); - - if (!overwrite) { - const allTargetPackages = await packageApi.findAllPackages(); - const manifestNodeKeys = manifestNodes.map(node => node.packageKey); - const packagesWithDraftChanges = allTargetPackages - .filter(node => manifestNodeKeys.includes(node.key) && node.workingDraftId !== node.activatedDraftId) - .map(node => node.key) - .join(", "); - if (!!packagesWithDraftChanges) { - throw new FatalError(`Failed to import. Cannot overwrite packages with key(s) ${packagesWithDraftChanges}`) - } - } - - let dmTargetIdsBySourceIds: Map = new Map(); - if (dataModelMappingsFilePath) { - const dataModelMappings: DataPoolInstallVersionReport = await fileService.readFileToJson(dataModelMappingsFilePath); - dmTargetIdsBySourceIds = new Map(Object.entries(dataModelMappings.dataModelIdMappings)); - } - - manifestNodes.map(node => node.dependenciesByVersion = new Map(Object.entries(node.dependenciesByVersion))); - - const importedVersionsByNodeKey = new Map(); - const sourceToTargetVersionsByNodeKey = new Map>(); - - for (const node of manifestNodes) { - for (const version of node.dependenciesByVersion.keys()) { - await this.checkNodeForCircularDependency(node, version, manifestNodes, []); - } - } - - const customSpacesMap: Map = new Map(); - spaceMappings.forEach(spaceMap => { - const packageAndSpaceid = spaceMap.split(":"); - customSpacesMap.set(packageAndSpaceid[0], packageAndSpaceid[1]) - }) - - const draftIdsByPackageKeyAndVersion = new Map(); - for (const node of manifestNodes) { - await this.importPackage(node, manifestNodes, sourceToTargetVersionsByNodeKey, customSpacesMap, dmTargetIdsBySourceIds, importedVersionsByNodeKey, draftIdsByPackageKeyAndVersion, importedFilePath, excludeActionFlows) - } - } - - private async checkNodeForCircularDependency(node: ManifestNodeTransport, version: string, manifestNodes: ManifestNodeTransport[], iteratedNodeAndVersions: string[]): Promise { - const dependencies = node.dependenciesByVersion.get(version); - - if (iteratedNodeAndVersions.includes(`${node.packageKey}_${version}`)) { - throw new Error("Circular dependency detected!"); - } - - iteratedNodeAndVersions.push(`${node.packageKey}_${version}`); - - for (const dependency of dependencies) { - const manifestNodeOfDependency = manifestNodes.find(manifestNode => manifestNode.packageKey === dependency.key); - - await this.checkNodeForCircularDependency(manifestNodeOfDependency, dependency.version, manifestNodes, iteratedNodeAndVersions); - } - } - - private async importPackage(packageToImport: ManifestNodeTransport, - manifestNodes: ManifestNodeTransport[], - sourceToTargetVersionsByNodeKey: Map>, - spaceMappings: Map, - dmTargetIdsBySourceIds: Map, - importedVersionsByNodeKey: Map, - draftIdsByPackageKeyAndVersion: Map, - importedFilePath: string, - excludeActionFlows?: boolean): Promise { - const importedPackageVersion = importedVersionsByNodeKey.get(packageToImport.packageKey) ?? []; - const versionsOfPackage = [...packageToImport.dependenciesByVersion.keys()].sort((k1, k2) => { - const version1 = new SemanticVersioning(k1); - const version2 = new SemanticVersioning(k1); - return version1.isGreaterThan(version2) ? 1 : -1; - }).filter(version => !importedPackageVersion.includes(version)); - - for (const version of versionsOfPackage) { - try { - await this.importPackageVersion(packageToImport, manifestNodes, sourceToTargetVersionsByNodeKey, spaceMappings, dmTargetIdsBySourceIds, importedVersionsByNodeKey, draftIdsByPackageKeyAndVersion, importedFilePath, version, excludeActionFlows); - } catch (e) { - logger.error(`Problem import package with key: ${packageToImport.packageKey} ${version} ${e}`); - } - } - } - - private async importPackageVersion(packageToImport: ManifestNodeTransport, - manifestNodes: ManifestNodeTransport[], - sourceToTargetVersionsByNodeKey: Map>, - spaceMappings: Map, - dmTargetIdsBySourceIds: Map, - importedVersionsByNodeKey: Map, - draftIdsByPackageKeyAndVersion: Map, - importedFilePath: string, - versionOfPackageBeingImported: string, - excludeActionFlows?: boolean): Promise { - if (packageToImport.dependenciesByVersion.get(versionOfPackageBeingImported).length) { - const dependenciesOfPackageVersion = packageToImport.dependenciesByVersion.get(versionOfPackageBeingImported); - await this.importDependencyPackages(dependenciesOfPackageVersion, manifestNodes, sourceToTargetVersionsByNodeKey, spaceMappings, dmTargetIdsBySourceIds, - importedVersionsByNodeKey, draftIdsByPackageKeyAndVersion, importedFilePath, excludeActionFlows - ); - } - - if (this.checkIfPackageVersionHasBeenImported(packageToImport.packageKey, versionOfPackageBeingImported, importedVersionsByNodeKey)) { - return; - } - - const targetSpace = await this.getTargetSpaceForExportedPackage(packageToImport, spaceMappings); - - let nodeInTargetTeam = await nodeApi.findOneByKeyAndRootNodeKey(packageToImport.packageKey, packageToImport.packageKey); - - const pathToZipFile = path.resolve(importedFilePath, packageToImport.packageKey + "_" + versionOfPackageBeingImported + ".zip"); - const packageZip = this.createBodyForImport(pathToZipFile); - - await packageApi.importPackage(packageZip, targetSpace.id, !!nodeInTargetTeam, excludeActionFlows); - - if (nodeInTargetTeam) { - await packageApi.movePackageToSpace(nodeInTargetTeam.id, targetSpace.id) - } - - nodeInTargetTeam = await nodeApi.findOneByKeyAndRootNodeKey(packageToImport.packageKey, packageToImport.packageKey); - - if (this.isLatestVersion(versionOfPackageBeingImported, [...packageToImport.dependenciesByVersion.keys()])) { - if (dmTargetIdsBySourceIds.size > 0) { - const variableAssignments = packageToImport.variables - .filter(variable => variable.type === PackageManagerVariableType.DATA_MODEL).map(variable => { - variable.value = dmTargetIdsBySourceIds.get(variable.value?.toString()) as unknown as object; - return variable; - }) - - await variableService.assignVariableValues(nodeInTargetTeam.key, variableAssignments); - } else { - await variableService.assignVariableValues(nodeInTargetTeam.key, packageToImport.variables); - } - } - - draftIdsByPackageKeyAndVersion.set(`${nodeInTargetTeam.key}_${versionOfPackageBeingImported}`, nodeInTargetTeam.workingDraftId); - - await this.updateDependencyVersions(packageToImport, versionOfPackageBeingImported, sourceToTargetVersionsByNodeKey, draftIdsByPackageKeyAndVersion); - await this.publishPackage(packageToImport); - - const packageVersionInTargetTeam = await packageApi.findActiveVersionById(nodeInTargetTeam.id); - - const mappedVersions = sourceToTargetVersionsByNodeKey.get(packageToImport.packageKey) ?? new Map(); - mappedVersions.set(versionOfPackageBeingImported, packageVersionInTargetTeam.version); - sourceToTargetVersionsByNodeKey.set(packageToImport.packageKey, mappedVersions); - - const importedVersionsOfPackage = importedVersionsByNodeKey.get(packageToImport.packageKey) ?? []; - importedVersionsOfPackage.push(versionOfPackageBeingImported); - importedVersionsByNodeKey.set(packageToImport.packageKey, importedVersionsOfPackage); - - const mappedVersion = sourceToTargetVersionsByNodeKey.get(packageToImport.packageKey).get(versionOfPackageBeingImported); - - logger.info(`Imported package with key: ${packageToImport.packageKey} ${versionOfPackageBeingImported} successfully. New version: ${mappedVersion}`) - } - - private isLatestVersion(packageVersion: string, allPackageVersions: string[]): boolean { - let isLatestVersion = true; - - for (const version of allPackageVersions) { - const version1 = new SemanticVersioning(packageVersion); - const version2 = new SemanticVersioning(version); - - if (version2.isGreaterThan(version1)) { - isLatestVersion = false; - break; - } - } - - return isLatestVersion; - } - - private async importDependencyPackages(dependenciesToImport: ManifestDependency[], - manifestNodes: ManifestNodeTransport[], - sourceToTargetVersionsByNodeKey: Map>, - spaceMappings: Map, - dmTargetIdsBySourceIds: Map, - importedVersionsByNodeKey: Map, - draftIdsByPackageKeyAndVersion: Map, - importedFilePath: string, - excludeActionFlows?: boolean): Promise { - for (const dependency of dependenciesToImport) { - if (this.checkIfPackageVersionHasBeenImported(dependency.key, dependency.version, importedVersionsByNodeKey)) { - continue; - } - - const dependentPackage = manifestNodes.find((node) => node.packageKey === dependency.key); - await this.importPackage(dependentPackage, manifestNodes, sourceToTargetVersionsByNodeKey, spaceMappings, dmTargetIdsBySourceIds, importedVersionsByNodeKey, draftIdsByPackageKeyAndVersion, importedFilePath, excludeActionFlows); - } - } - - private checkIfPackageVersionHasBeenImported(packageKey: string, - version: string, - importedVersions: Map): boolean { - const importedPackages = importedVersions.get(packageKey) ?? []; - return importedPackages.includes(version); - } - - private createBodyForImport(filename: string): FormData { - const formData = new FormData(); - formData.append("package", fs.createReadStream(filename, {encoding: null})); - - return formData; - } - - private async getTargetSpaceForExportedPackage(packageToImport: ManifestNodeTransport, spaceMappings: Map): Promise { - let targetSpace; - const allSpaces = await spaceService.refreshAndGetAllSpaces(); - if (spaceMappings.has(packageToImport.packageKey)) { - const customSpaceId = spaceMappings.get(packageToImport.packageKey); - const customSpace = allSpaces.find(space => space.id === customSpaceId); - - if (!customSpace) { - throw Error("Provided space id does not exist"); - } - - targetSpace = customSpace; - } else { - targetSpace = allSpaces.find(space => space.name === packageToImport.space.spaceName); - - if (!targetSpace) { - targetSpace = await spaceService.createSpace(packageToImport.space.spaceName, packageToImport.space.spaceIcon); - } - - } - - return targetSpace; - } - - private async updateDependencyVersions(node: ManifestNodeTransport, - versionOfPackage: string, - sourceToTargetVersionsByNodeKey: Map>, - draftIdsByPackageKeyAndVersion: Map): Promise { - - const createdNode = await nodeApi.findOneByKeyAndRootNodeKey(node.packageKey, node.packageKey); - const newDependencies = []; - for (const dependency of [...node.dependenciesByVersion.get(versionOfPackage)]) { - const nodeInTargetTeam = await nodeApi.findOneByKeyAndRootNodeKey(dependency.key, dependency.key); - const draftIdOfVersionedDependency = draftIdsByPackageKeyAndVersion.get(`${dependency.key}_${dependency.version}`); - dependency.version = sourceToTargetVersionsByNodeKey.get(dependency.key).get(dependency.version); - dependency.updateAvailable = dependency.updateAvailable; - dependency.id = nodeInTargetTeam.rootNodeId; - dependency.rootNodeId = createdNode.rootNodeId; - dependency.draftId = draftIdOfVersionedDependency; - newDependencies.push(dependency); - - await packageDependenciesApi.deleteDependency(createdNode.id, dependency.key); - } - - if (newDependencies.length) { - await packageDependenciesApi.createDependencies(createdNode.id, newDependencies); - } - } - - public async publishPackage(packageToImport: ManifestNodeTransport): Promise { - const nodeInTargetTeam = await nodeApi.findOneByKeyAndRootNodeKey(packageToImport.packageKey, packageToImport.packageKey); - const nextVersion = await packageApi.findNextVersion(nodeInTargetTeam.id); - await packageApi.publishPackage({ - packageKey: packageToImport.packageKey, - version: nextVersion.version, - publishMessage: "Published package after import", - nodeIdsToExclude: [] - }); - } - - public async batchExportPackages(packageKeys: string[], includeDependencies: boolean, excludeActionFlows?: boolean): Promise { - const allPackages = await packageApi.findAllPackages(); - let nodesListToExport: BatchExportNodeTransport[] = allPackages.filter(node => packageKeys.includes(node.key)); - - const versionsByNodeKey = new Map(); - - const allPackageKeys = allPackages.map(p => p.key); - - for (const packageKey of packageKeys) { - if (!allPackageKeys.includes(packageKey)) { - throw new Error(`Package ${packageKey} does not exist.`); - } - } - - nodesListToExport = await this.getNodesWithActiveVersion(nodesListToExport); - if (includeDependencies) { - const dependencyPackages = await this.getDependencyPackages(nodesListToExport, [], allPackages, [], versionsByNodeKey); - nodesListToExport.push(...dependencyPackages); - } - - const packagesWithVariableAssignments = await variableService.getVariableAssignmentsForNodes(); - - nodesListToExport.forEach(node => { - node.variables = packagesWithVariableAssignments.find(nodeWithVariablesAssignment => nodeWithVariablesAssignment.key === node.key)?.variableAssignments; - }); - - const packagesWithDataModelVariables = packagesWithVariableAssignments.map(packageWithVariablesAssignments => { - packageWithVariablesAssignments.variableAssignments = packageWithVariablesAssignments.variableAssignments.filter(variable => variable.value && variable.type === PackageManagerVariableType.DATA_MODEL); - return packageWithVariablesAssignments; - }); - const dataModelDetailsByNode = await dataModelService.getDataModelDetailsForPackages(packagesWithDataModelVariables); - - nodesListToExport.forEach(node => { - node.datamodels = dataModelDetailsByNode.get(node.key); - }); - - nodesListToExport = await spaceService.getParentSpaces(nodesListToExport); - await this.exportToZip(nodesListToExport, versionsByNodeKey, excludeActionFlows); - } - - public async getNodesWithActiveVersion(nodes: BatchExportNodeTransport[]): Promise { - const activeVersionsOfPackage = await packageApi.findActiveVersionByIds(nodes.map(node => node.id)); - - nodes.forEach(node => { - node.version = activeVersionsOfPackage.find(packageVersion => packageVersion.id === node.id); - }) - - return nodes; - } - - public async getPackagesWithDependencies(draftIdByNodeId: Map): Promise> { - const allPackageDependencies: Map = await packageDependenciesApi.findPackageDependenciesByIds(draftIdByNodeId); - return allPackageDependencies; - } - - private async getDependencyPackages(nodesToResolve: BatchExportNodeTransport[], dependencyPackages: BatchExportNodeTransport[], allPackages: ContentNodeTransport[], resolvedDependencies: string[], versionsByNodeKey: Map): Promise { - const draftIdByNodeId = new Map(); - nodesToResolve.forEach(node => draftIdByNodeId.set(node.activatedDraftId, node.id)); - - const dependenciesByPackageDraftIds = await this.getPackagesWithDependencies(draftIdByNodeId); - - const nodesWithDependencies = nodesToResolve.map(nodeToExport => { - nodeToExport.dependencies = dependenciesByPackageDraftIds[nodeToExport.activatedDraftId] ?? []; - return nodeToExport; - }); - - for (const node of nodesWithDependencies) { - node.dependencies = node.dependencies.filter(dependency => !(dependency.external || dependency.deleted)); - node.dependencies.forEach(dependency => { - const dependencyVersions = versionsByNodeKey.get(dependency.key) ?? []; - if (!dependencyVersions.includes(dependency.version)) { - dependencyVersions.push(dependency.version); - versionsByNodeKey.set(dependency.key, dependencyVersions); - } - }); - - const nodesToGetKeys = node.dependencies.filter(dependency => { - return !this.nodeHasBeenResolvedBefore(resolvedDependencies, dependency.key, dependency.version) - }).map(iteratedNode => iteratedNode.key); - - if (nodesToGetKeys.length > 0) { - const dependencyPackagesOfNode = allPackages.filter(packageNode => nodesToGetKeys.includes(packageNode.key)).map(dependency => { - const versionedDep = node.dependencies.find(dep => dependency.key === dep.key); - return { - ...dependency, - version: { - version: versionedDep.version - }, - activatedDraftId: versionedDep.draftId - } as BatchExportNodeTransport - }); - - dependencyPackages.push(...dependencyPackagesOfNode); - await this.getDependencyPackages(dependencyPackagesOfNode, dependencyPackages, allPackages, resolvedDependencies, versionsByNodeKey) - } - } - - return dependencyPackages; - } - - private nodeHasBeenResolvedBefore(nodePath: string[], nodeKey: string, version: string): boolean { - return nodePath.includes(nodeKey + ":" + version); - } - - private exportListOfPackages(nodes: BatchExportNodeTransport[], fieldsToInclude: string[]): void { - const filename = uuidv4() + ".json"; - fileService.writeToFileWithGivenName(JSON.stringify(nodes, fieldsToInclude), filename); - logger.info(FileService.fileDownloadedMessage + filename); - } - - private async exportPackagesAndAssets(nodes: BatchExportNodeTransport[], excludeActionFlows?: boolean): Promise { - const zips = []; - for (const rootPackage of nodes) { - const exportedPackage = await packageApi.exportPackage(rootPackage.key, rootPackage.version.version, excludeActionFlows) - zips.push({ - data: exportedPackage, - packageKey: rootPackage.key, - version: rootPackage.version.version - }); - } - return zips; - } - - private async exportToZip(nodes: BatchExportNodeTransport[], versionsByNodeKey: Map, excludeActionFlows?: boolean): Promise { - const manifestNodes = this.exportManifestOfPackages(nodes, versionsByNodeKey); - const packageZips = await this.exportPackagesAndAssets(nodes, excludeActionFlows); - - const zip = new AdmZip(); - - zip.addFile("manifest.yml", Buffer.from(stringify(manifestNodes), "utf8")); - for (const packageZip of packageZips) { - zip.addFile(`${packageZip.packageKey}_${packageZip.version}.zip`, packageZip.data) - } - - const fileName = "export_" + uuidv4() + ".zip"; - zip.writeZip(fileName); - logger.info(this.fileDownloadedMessage + fileName); - } - - private exportManifestOfPackages(nodes: BatchExportNodeTransport[], dependencyVersionsByNodeKey: Map): ManifestNodeTransport[] { - const manifestNodesByPackageKey = new Map(); - - nodes.forEach((node) => { - const manifestNode = manifestNodesByPackageKey.get(node.key) ?? {} as ManifestNodeTransport; - manifestNode.packageKey = node.key; - manifestNode.packageId = node.id; - manifestNode.space = { - spaceName: node.space.name, - spaceIcon: node.space.iconReference - } - manifestNode.variables = node.variables?.map((variable) => { - if (variable.type === PackageManagerVariableType.DATA_MODEL) { - // @ts-ignore - const dataModel = node.datamodels?.find(dataModel => dataModel.dataModelId === variable.value); - return { - key: variable.key, - type: variable.type, - value: variable.value, - dataModelName: dataModel?.name, - } - } - return variable; - }); - manifestNode.dependenciesByVersion = manifestNode.dependenciesByVersion ?? new Map(); - manifestNode.dependenciesByVersion.set(node.version.version, node.dependencies ?? []); - manifestNodesByPackageKey.set(node.key, manifestNode); - }) - - return [...manifestNodesByPackageKey.values()]; - } -} - -export const packageService = new PackageService(); diff --git a/src/services/package-manager/space-service.ts b/src/services/package-manager/space-service.ts deleted file mode 100644 index 466476db..00000000 --- a/src/services/package-manager/space-service.ts +++ /dev/null @@ -1,45 +0,0 @@ -import {spaceApi} from "../../api/space-api"; -import {BatchExportNodeTransport} from "../../interfaces/batch-export-node-transport"; -import {SpaceTransport} from "../../interfaces/save-space.interface"; -import {v4 as uuidv4} from "uuid"; - -class SpaceService { - public static readonly INSTANCE = new SpaceService(); - - private allSpaces: SpaceTransport[] = []; - - public async getParentSpaces(nodes: BatchExportNodeTransport[]): Promise { - const promises = []; - - nodes.forEach(node => { - promises.push(new Promise(async resolve => { - node.space = await spaceApi.findOne(node.spaceId); - resolve(node); - })); - }) - - return Promise.all(promises); - } - - public async createSpace(spaceName: string, spaceIcon: string): Promise { - const newSpace = await spaceApi.createSpace({ - id: uuidv4(), - name: spaceName, - iconReference: spaceIcon - }); - - await this.refreshAndGetAllSpaces(); - this.allSpaces.push(newSpace); - return newSpace; - } - - public async refreshAndGetAllSpaces(): Promise { - if (this.allSpaces.length) { - return this.allSpaces; - } - this.allSpaces = await spaceApi.findAllSpaces(); - return this.allSpaces; - } -} - -export const spaceService = SpaceService.INSTANCE; \ No newline at end of file diff --git a/src/services/package-manager/variable-service.ts b/src/services/package-manager/variable-service.ts deleted file mode 100644 index 2c0b09ff..00000000 --- a/src/services/package-manager/variable-service.ts +++ /dev/null @@ -1,111 +0,0 @@ -import {packageApi} from "../../api/package-api"; -import { - PackageManagerVariableType, - PackageWithVariableAssignments, - VariablesAssignments -} from "../../interfaces/package-manager.interfaces"; -import {variablesApi} from "../../api/variables-api"; -import { v4 as uuidv4 } from "uuid"; -import {FatalError, logger} from "../../util/logger"; -import {FileService, fileService} from "../file-service"; -import {URLSearchParams} from "url"; -import {studioService} from "../studio/studio.service"; -import {batchImportExportApi} from "../../api/batch-import-export-api"; -import {PackageKeyAndVersionPair, VariableManifestTransport} from "../../interfaces/package-export-transport"; - -class VariableService { - - public async getVariableAssignmentsForNodes(type?: PackageManagerVariableType): Promise { - return await packageApi.findAllPackagesWithVariableAssignments(type); - } - - public async assignVariableValues(packageKey: string, variablesAssignments: VariablesAssignments[]): Promise { - await variablesApi.assignVariableValues(packageKey, variablesAssignments); - } - - public async listCandidateAssignments(type: string, params: string): Promise { - const parsedParams = this.parseParams(params); - const assignments = await variablesApi.getCandidateAssignments(type, parsedParams); - - assignments.forEach(assignment => { - logger.info(JSON.stringify(assignment)); - }); - } - - public async findAndExportCandidateAssignments(type: string, params: string): Promise { - const parsedParams = this.parseParams(params); - const assignments = await variablesApi.getCandidateAssignments(type, parsedParams); - - this.exportToJson(assignments) - } - - public async listVariables(keysByVersion: string[], keysByVersionFile: string): Promise { - const variableManifests = await this.getVersionedVariablesByKeyVersionPairs(keysByVersion, keysByVersionFile); - - variableManifests.forEach(variableManifest => { - logger.info(JSON.stringify(variableManifest)); - }); - } - - public async exportVariables(keysByVersion: string[], keysByVersionFile: string): Promise { - const variableManifests = await this.getVersionedVariablesByKeyVersionPairs(keysByVersion, keysByVersionFile); - - this.exportToJson(variableManifests); - } - - private parseParams(params?: string): URLSearchParams { - const queryParams = new URLSearchParams(); - - if (params) { - try { - params.split(",").forEach((param: string) => { - const paramKeyValuePair: string[] = param.split("="); - queryParams.set(paramKeyValuePair[0], paramKeyValuePair[1]); - }) - } catch (e) { - throw new FatalError(`Problem parsing query params: ${e}`); - } - } - - return queryParams; - } - - private async getVersionedVariablesByKeyVersionPairs(keysByVersion: string[], keysByVersionFile: string): Promise { - const variablesExportRequest: PackageKeyAndVersionPair[] = await this.buildKeyVersionPairs(keysByVersion, keysByVersionFile); - - const variableManifests = await batchImportExportApi.findVariablesWithValuesByPackageKeysAndVersion(variablesExportRequest); - return studioService.fixConnectionVariables(variableManifests); - } - - private async buildKeyVersionPairs(keysByVersion: string[], keysByVersionFile: string): Promise { - let variablesExportRequest: PackageKeyAndVersionPair[] = []; - - if (keysByVersion.length !== 0) { - variablesExportRequest = this.buildKeyAndVersionPairsFromArrayInput(keysByVersion); - } else if (keysByVersion.length === 0 && keysByVersionFile !== "") { - variablesExportRequest = await fileService.readFileToJson(keysByVersionFile); - } else { - throw new FatalError("Please provide keysByVersion mappings or file path!"); - } - - return variablesExportRequest; - } - - private buildKeyAndVersionPairsFromArrayInput(keysByVersion: string[]): PackageKeyAndVersionPair[] { - return keysByVersion.map(keyAndVersion => { - const keyAndVersionSplit = keyAndVersion.split(":"); - return { - packageKey: keyAndVersionSplit[0], - version: keyAndVersionSplit[1] - }; - }); - } - - private exportToJson(data: any): void { - const filename = uuidv4() + ".json"; - fileService.writeToFileWithGivenName(JSON.stringify(data), filename); - logger.info(FileService.fileDownloadedMessage + filename); - } -} - -export const variableService = new VariableService(); diff --git a/src/services/profile.service.ts b/src/services/profile.service.ts deleted file mode 100644 index 5e97dc0c..00000000 --- a/src/services/profile.service.ts +++ /dev/null @@ -1,302 +0,0 @@ -import { - AuthenticationType, ClientAuthenticationMethod, - Profile, ProfileType -} from "../interfaces/profile.interface"; -import { ProfileValidator } from "../validators/profile.validator"; -import * as path from "path"; -import * as fs from "fs"; -import { FatalError, logger } from "../util/logger"; -import { Issuer } from "openid-client"; -import axios from "axios"; -import os = require("os"); - -const homedir = os.homedir(); -// use 5 seconds buffer to avoid rare cases when accessToken is just about to expire before the command is sent -const expiryBuffer = 5000; -const deviceCodeScopes = ["studio", "package-manager", "integration.data-pools", "action-engine.projects"]; -const clientCredentialsScopes = ["studio", "integration.data-pools", "action-engine.projects"]; - -export interface Config { - defaultProfile: string; -} - -export class ProfileService { - private profileContainerPath = path.resolve(homedir, ".celonis-content-cli-profiles"); - private configContainer = path.resolve(this.profileContainerPath, "config.json"); - - public async findProfile(profileName: string): Promise { - return new Promise((resolve, reject) => { - try { - if (process.env.TEAM_URL && process.env.API_TOKEN) { - resolve(this.buildProfileFromEnvVariables()); - } else { - const file = fs.readFileSync( - path.resolve(this.profileContainerPath, this.constructProfileFileName(profileName)), - { encoding: "utf-8" } - ); - const profile : Profile = JSON.parse(file); - this.refreshProfile(profile) - .then(() => resolve(profile)); - } - } catch (e) { - reject( - "No profile provided. Please provide a profile or an TEAM_URL and API_TOKEN through env variables" - ); - } - }); - } - - public async makeDefaultProfile(profileName: string): Promise { - return new Promise((resolve, reject) => { - this.findProfile(profileName) - .then((profile: Profile) => { - this.createProfileContainerIfNotExists(); - this.storeConfig({ defaultProfile: profileName }); - resolve(profile); - }) - .catch(err => { - logger.error(new FatalError("Profile does not exit.")); - reject(err); - }); - }); - } - - public getDefaultProfile(): string { - if (fs.existsSync(this.configContainer)) { - const config = JSON.parse(fs.readFileSync(this.configContainer, { encoding: "utf-8" })) as Config; - return config.defaultProfile; - } else { - return null; - } - } - - public storeProfile(profile: Profile): void { - this.createProfileContainerIfNotExists(); - const newProfileFileName = this.constructProfileFileName(profile.name); - profile.team = this.getBaseTeamUrl(profile.team); - fs.writeFileSync(path.resolve(this.profileContainerPath, newProfileFileName), JSON.stringify(profile), { - encoding: "utf-8", - }); - } - - private async buildProfileFromEnvVariables(): Promise { - const profileVariables = this.getProfileEnvVariables(); - const profile: Profile = { - name: profileVariables.teamUrl, - team: profileVariables.teamUrl, - apiToken: profileVariables.apiToken, - authenticationType: AuthenticationType.BEARER, - type: ProfileType.KEY - }; - profile.authenticationType = await ProfileValidator.validateProfile(profile); - return profile; - } - - private storeConfig(config: Config): void { - fs.writeFileSync(this.configContainer, JSON.stringify(config), { encoding: "utf-8" }); - } - - private createProfileContainerIfNotExists(): void { - if (!fs.existsSync(this.profileContainerPath)) { - fs.mkdirSync(this.profileContainerPath); - } - } - - private constructProfileFileName(profileName: string): string { - return profileName + ".json"; - } - - public readAllProfiles(): Promise { - return new Promise((resolve, reject) => { - const profiles = this.getAllFilesInDirectory(); - resolve(profiles); - }); - } - - public getAllFilesInDirectory(): string[] { - let fileNames: string[] = []; - try { - if (fs.existsSync(this.profileContainerPath)) { - fileNames = fs - // @ts-ignore - .readdirSync(this.profileContainerPath, { withFileTypes: true }) - .filter( - dirent => - !dirent.isDirectory() && dirent.name.endsWith(".json") && dirent.name !== "config.json" - ) - .map(dirent => dirent.name.replace(".json", "")); - } - } catch (err) { - logger.error(new FatalError(err)); - } - return fileNames; - } - - public async authorizeProfile(profile: Profile) : Promise { - switch (profile.type) { - case ProfileType.KEY: - const url = profile.team.replace(/\/?$/, "/api/cloud/team"); - try { - await this.tryKeyAuthentication(url, AuthenticationType.BEARER, profile.apiToken); - profile.authenticationType = AuthenticationType.BEARER; - } catch (e) { - try { - await this.tryKeyAuthentication(url, AuthenticationType.APPKEY, profile.apiToken); - profile.authenticationType = AuthenticationType.APPKEY; - } catch (err) { - logger.error(new FatalError("The provided team or api key is wrong.")); - logger.error(err); - } - } - break; - case ProfileType.DEVICE_CODE: - try { - const deviceCodeIssuer = await Issuer.discover(profile.team); - const deviceCodeOAuthClient = new deviceCodeIssuer.Client({ - client_id: "content-cli", - token_endpoint_auth_method: "none", - }); - const deviceCodeHandle = await deviceCodeOAuthClient.deviceAuthorization({ - scope: deviceCodeScopes.join(" ") - }); - logger.info(`Continue authorization here: ${deviceCodeHandle.verification_uri_complete}`); - const deviceCodeTokenSet = await deviceCodeHandle.poll(); - profile.apiToken = deviceCodeTokenSet.access_token; - profile.refreshToken = deviceCodeTokenSet.refresh_token; - profile.expiresAt = deviceCodeTokenSet.expires_at; - } catch (err) { - logger.error(new FatalError("The provided team is wrong.")); - logger.error(err); - } - break; - case ProfileType.CLIENT_CREDENTIALS: - const clientCredentialsIssuer = await Issuer.discover(profile.team); - try { - // try with client secret basic - const clientCredentialsOAuthClient = new clientCredentialsIssuer.Client({ - client_id: profile.clientId, - client_secret: profile.clientSecret, - token_endpoint_auth_method: ClientAuthenticationMethod.CLIENT_SECRET_BASIC, - }); - const clientCredentialsTokenSet = await clientCredentialsOAuthClient.grant({ - grant_type: "client_credentials", - scope: clientCredentialsScopes.join(" ") - }); - profile.clientAuthenticationMethod = ClientAuthenticationMethod.CLIENT_SECRET_BASIC; - profile.apiToken = clientCredentialsTokenSet.access_token; - profile.expiresAt = clientCredentialsTokenSet.expires_at; - } catch (e) { - try { - // try with client secret post - const clientCredentialsOAuthClient = new clientCredentialsIssuer.Client({ - client_id: profile.clientId, - client_secret: profile.clientSecret, - token_endpoint_auth_method: ClientAuthenticationMethod.CLIENT_SECRET_POST, - }); - const clientCredentialsTokenSet = await clientCredentialsOAuthClient.grant({ - grant_type: "client_credentials", - scope: clientCredentialsScopes.join(" ") - }); - profile.clientAuthenticationMethod = ClientAuthenticationMethod.CLIENT_SECRET_POST; - profile.apiToken = clientCredentialsTokenSet.access_token; - profile.expiresAt = clientCredentialsTokenSet.expires_at; - } catch (err) { - logger.error(new FatalError("The OAuth client configuration is incorrect. " + - "Check the id, secret and scopes for correctness.")); - } - } - profile.scopes = [...clientCredentialsScopes]; - - break; - default: - logger.error(new FatalError("Unsupported profile type")); - break; - } - } - - public async refreshProfile(profile: Profile) : Promise { - if (!this.isProfileExpired(profile, expiryBuffer)) { - return; - } - const issuer = await Issuer.discover(profile.team); - if (profile.type === ProfileType.DEVICE_CODE) { - try { - const oauthClient = new issuer.Client({ - client_id: "content-cli", - token_endpoint_auth_method: "none", - }); - const tokenSet = await oauthClient.refresh(profile.refreshToken); - profile.apiToken = tokenSet.access_token; - profile.expiresAt = tokenSet.expires_at; - profile.refreshToken = tokenSet.refresh_token; - } catch (err) { - logger.error(new FatalError("The profile cannot be refreshed. Please retry or recreate profile.")); - } - } - else { - try { - const oauthClient = new issuer.Client({ - client_id: profile.clientId, - client_secret: profile.clientSecret, - token_endpoint_auth_method: profile.clientAuthenticationMethod, - }); - const tokenSet = await oauthClient.grant({ - grant_type: "client_credentials", - scope: profile.scopes.join(" ") - }); - profile.apiToken = tokenSet.access_token; - profile.expiresAt = tokenSet.expires_at; - } catch (err) { - logger.error(new FatalError("The profile cannot be refreshed. Please retry or recreate profile.")); - } - } - - this.storeProfile(profile); - } - - private getProfileEnvVariables(): any { - return { - teamUrl: this.getBaseTeamUrl(process.env.TEAM_URL), - apiToken: process.env.API_TOKEN, - }; - } - - private getBaseTeamUrl(teamUrl: string): string { - if (!teamUrl) { - return null; - } - - const url = new URL(teamUrl); - return url.origin; - } - - private isProfileExpired(profile: Profile, buffer: number = 0): boolean { - if (profile.type === null || profile.type === undefined || profile.type === ProfileType.KEY) { - return false; - } - const now = new Date(); - const expirationTime = new Date(profile.expiresAt * 1000 - buffer); - - return now > expirationTime; - } - - private tryKeyAuthentication(url: string, authType: AuthenticationType, apiToken: string): Promise { - return new Promise((resolve, reject) => { - axios.get(url, { - headers: { - Authorization: `${authType} ${apiToken}` - } - }).then(response => { - if (response.status === 200 && response.data.domain) { - resolve(); - } else { - reject(); - } - }).catch(() => { - reject(); - }) - }) - } -} - -export const profileService = new ProfileService(); diff --git a/src/services/question.service.ts b/src/services/question.service.ts deleted file mode 100644 index 8822ccab..00000000 --- a/src/services/question.service.ts +++ /dev/null @@ -1,16 +0,0 @@ -import * as readline from "readline"; -import { ReadLine } from "readline"; - -export class QuestionService { - private static readLine: ReadLine = readline.createInterface({ - input: process.stdin, - output: process.stdout, - terminal: true, - }); - - public static async ask(question: string): Promise { - return new Promise((resolve, reject) => { - this.readLine.question(question, input => resolve(input)); - }); - } -} \ No newline at end of file diff --git a/src/services/studio/studio.service.ts b/src/services/studio/studio.service.ts deleted file mode 100644 index 83b659ba..00000000 --- a/src/services/studio/studio.service.ts +++ /dev/null @@ -1,266 +0,0 @@ -import { - NodeConfiguration, - NodeExportTransport, - PackageExportTransport, - PackageKeyAndVersionPair, - StudioPackageManifest, - VariableExportTransport, - VariableManifestTransport, -} from "../../interfaces/package-export-transport"; - -import {packageApi} from "../../api/package-api"; -import { - ContentNodeTransport, - PackageManagerVariableType, - PackageWithVariableAssignments, - StudioComputeNodeDescriptor -} from "../../interfaces/package-manager.interfaces"; -import {dataModelService} from "../package-manager/datamodel-service"; -import {IZipEntry} from "adm-zip"; -import {parse, stringify} from "../../util/json"; -import {nodeApi} from "../../api/node-api"; -import {variablesApi} from "../../api/variables-api"; -import {spaceApi} from "../../api/space-api"; -import {SpaceTransport} from "../../interfaces/save-space.interface"; -import {spaceService} from "../package-manager/space-service"; -import {variableService} from "../package-manager/variable-service"; -import {BatchExportImportConstants} from "../../interfaces/batch-export-import-constants"; -import * as AdmZip from "adm-zip"; - - -class StudioService { - - public async getExportPackagesWithStudioData(packagesToExport: PackageExportTransport[], withDependencies: boolean): Promise { - const studioPackagesWithDataModels = await packageApi.findAllPackagesWithVariableAssignments(PackageManagerVariableType.DATA_MODEL); - - packagesToExport = studioService.setSpaceIdForStudioPackages(packagesToExport, studioPackagesWithDataModels); - - if (withDependencies) { - const dataModelDetailsByNode = await dataModelService.getDataModelDetailsForPackages(studioPackagesWithDataModels); - packagesToExport = studioService.setDataModelsForStudioPackages(packagesToExport, studioPackagesWithDataModels, dataModelDetailsByNode); - } - - return packagesToExport; - } - - public fixConnectionVariables(variables: VariableManifestTransport[]): VariableManifestTransport[] { - return variables.map(variableManifest => ({ - ...variableManifest, - variables: variableManifest.variables.map(variable => { - if (variable.type !== PackageManagerVariableType.CONNECTION) { - return variable; - } - - return this.fixConnectionVariable(variable); - }) - })); - } - - public async getStudioPackageManifests(studioPackageKeys: string[]): Promise { - return Promise.all(studioPackageKeys.map(async packageKey => { - const node = await nodeApi.findOneByKeyAndRootNodeKey(packageKey, packageKey); - const nodeSpace: SpaceTransport = await spaceApi.findOne(node.spaceId); - const variableAssignments = await variablesApi.getRuntimeVariableValues(packageKey, BatchExportImportConstants.APP_MODE_VIEWER); - - return { - packageKey: packageKey, - space: { - name: nodeSpace.name, - iconReference: nodeSpace.iconReference - }, - runtimeVariableAssignments: variableAssignments - } - })); - } - - public processPackageForExport(exportedPackage: IZipEntry, exportedVariables: VariableManifestTransport[]): AdmZip { - const packageZip = new AdmZip(exportedPackage.getData()); - this.deleteScenarioAssets(packageZip); - this.fixConnectionVariablesForRootNodeFiles(packageZip, exportedPackage.name, exportedVariables); - - return packageZip; - } - - public async processImportedPackages(configs: AdmZip, existingStudioPackages: ContentNodeTransport[], studioManifests: StudioPackageManifest[]): Promise { - if(studioManifests == null) { - return; - } - for (const manifest of studioManifests) { - const existingPackage = existingStudioPackages.find(existingPackage => existingPackage.key === manifest.packageKey); - if (existingPackage) { - await packageApi.movePackageToSpace(existingPackage.id, manifest.space.id); - } - await this.assignRuntimeVariables(manifest); - } - } - - private setSpaceIdForStudioPackages(packages: PackageExportTransport[], studioPackages: PackageWithVariableAssignments[]): PackageExportTransport[] { - const studioPackageByKey = new Map(); - studioPackages.forEach(pkg => studioPackageByKey.set(pkg.key, pkg)); - - return packages.map(pkg => { - return studioPackageByKey.has(pkg.key) ? { - ...pkg, - spaceId: studioPackageByKey.get(pkg.key).spaceId - } : pkg; - }); - } - - private setDataModelsForStudioPackages(packages: PackageExportTransport[], - studioPackageWithDataModels: PackageWithVariableAssignments[], - dataModelDetailsByNode: Map): PackageExportTransport[] { - const studioPackageByKey = new Map(); - studioPackageWithDataModels.forEach(pkg => studioPackageByKey.set(pkg.key, pkg)); - - return packages.map(pkg => { - return studioPackageByKey.has(pkg.key) ? { - ...pkg, - datamodels: dataModelDetailsByNode.get(pkg.key) - .map(dataModel => ({ - name: dataModel.name, - poolId: dataModel.poolId, - dataModelId: dataModel.dataModelId - })) - } : pkg; - }); - } - - private fixConnectionVariable(variable: VariableExportTransport): VariableExportTransport { - if (!variable.value.appName) { - return variable; - } - - return { - ...variable, - metadata: { - ...variable.metadata, - appName: variable.value.appName - } - } - } - - private deleteScenarioAssets(packageZip: AdmZip): void { - packageZip.getEntries().filter(entry => entry.entryName.startsWith(BatchExportImportConstants.NODES_FOLDER_NAME) && entry.entryName.endsWith(BatchExportImportConstants.JSON_EXTENSION)) - .forEach(entry => { - const node: NodeExportTransport = parse(entry.getData().toString()); - if (node.type === BatchExportImportConstants.SCENARIO_NODE) { - packageZip.deleteFile(entry); - } - }); - } - - private fixConnectionVariablesForRootNodeFiles(packageZip: AdmZip, zipName: string, exportedVariables: VariableManifestTransport[]): void { - const packageKeyAndVersion = this.getPackageKeyAndVersion(zipName); - const connectionVariablesByKey = this.getConnectionVariablesByKeyForPackage(packageKeyAndVersion.packageKey, packageKeyAndVersion.version, exportedVariables); - - if (connectionVariablesByKey.size === 0) { - return; - } - - const packageEntry = packageZip.getEntry("package.json"); - - const exportedNode: NodeExportTransport = parse(packageEntry.getData().toString()); - const nodeContent: NodeConfiguration = exportedNode.configuration; - - nodeContent.variables = nodeContent.variables.map(variable => ({ - ...variable, - metadata: variable.type === PackageManagerVariableType.CONNECTION ? - connectionVariablesByKey.get(variable.key).metadata : variable.metadata - })); - - packageZip.updateFile(packageEntry, Buffer.from(stringify(exportedNode))); - } - - private getPackageKeyAndVersion(zipName: string): PackageKeyAndVersionPair { - const lastUnderscoreIndex = zipName.lastIndexOf("_"); - const packageKey = zipName.replace(BatchExportImportConstants.ZIP_EXTENSION, "").substring(0, lastUnderscoreIndex); - const packageVersion = zipName.replace(BatchExportImportConstants.ZIP_EXTENSION, "").substring(lastUnderscoreIndex + 1); - - return { - packageKey: packageKey, - version: packageVersion - } - } - - private getConnectionVariablesByKeyForPackage(packageKey: string, packageVersion: string, variables: VariableManifestTransport[]): Map { - const variablesByKey = new Map(); - const packageVariables = variables.find(exportedVariable => exportedVariable.packageKey === packageKey && exportedVariable.version === packageVersion); - - if (packageVariables && packageVariables.variables.length) { - packageVariables.variables.filter(variable => variable.type === PackageManagerVariableType.CONNECTION) - .forEach(variable => variablesByKey.set(variable.key, variable)); - } - - return variablesByKey; - } - - public async mapSpaces(exportedFiles: AdmZip, studioManifests: StudioPackageManifest[]): Promise { - if (studioManifests == null) { - return exportedFiles; - } - for (const file of exportedFiles.getEntries()) { - if(file.name.endsWith(BatchExportImportConstants.ZIP_EXTENSION)) { - const packageKey = this.getPackageKeyAndVersion(file.name).packageKey; - - if (this.isStudioPackage(studioManifests, packageKey)) { - const studioManifest = studioManifests.find(manifest => manifest.packageKey === packageKey); - - if (studioManifest) { - const spaceId = await this.findDesiredSpaceIdForPackage(studioManifest); - studioManifest.space.id = spaceId; - - const packageZip = new AdmZip(file.getData()); - packageZip.getEntries().forEach(nodeFile => { - if (nodeFile.entryName.endsWith(BatchExportImportConstants.JSON_EXTENSION)) { - const updatedNodeFile = this.updateSpaceIdForNode(nodeFile.getData().toString(), spaceId); - packageZip.updateFile(nodeFile, Buffer.from(updatedNodeFile)); - } - }); - exportedFiles.updateFile(file, packageZip.toBuffer()); - } - } - } - } - return exportedFiles; - } - - private isStudioPackage(studioManifests: StudioPackageManifest[], packageKey: string): boolean { - return studioManifests.some(manifest => manifest.packageKey === packageKey); - } - - private async findDesiredSpaceIdForPackage(studioPackageManifest: StudioPackageManifest): Promise { - const allSpaces = await spaceService.refreshAndGetAllSpaces(); - - if (studioPackageManifest.space.id) { - const targetSpace = allSpaces.find(space => space.id === studioPackageManifest.space.id); - if (!targetSpace) { - throw Error("Provided space ID does not exist."); - } - return targetSpace.id; - } - - const targetSpaceByName = allSpaces.find(space => space.name === studioPackageManifest.space.name); - if (targetSpaceByName) { - return targetSpaceByName.id; - } - - const spaceTransport = await spaceService.createSpace(studioPackageManifest.space.name, studioPackageManifest.space.iconReference); - return spaceTransport.id; - } - - private async assignRuntimeVariables(manifest: StudioPackageManifest): Promise { - if (manifest.runtimeVariableAssignments.length) { - await variableService.assignVariableValues(manifest.packageKey, manifest.runtimeVariableAssignments); - } - } - - private updateSpaceIdForNode(nodeContent: string, spaceId: string): string { - const exportedNode: NodeExportTransport = parse(nodeContent); - const oldSpaceId = exportedNode.spaceId; - - nodeContent = nodeContent.replace(new RegExp(oldSpaceId, "g"), spaceId); - return nodeContent; - } -} - -export const studioService = new StudioService(); \ No newline at end of file diff --git a/src/util/axios-initializer.ts b/src/util/axios-initializer.ts deleted file mode 100644 index 2154d592..00000000 --- a/src/util/axios-initializer.ts +++ /dev/null @@ -1,21 +0,0 @@ -import axios, { AxiosInstance } from "axios"; -import { HttpsProxyAgent } from "hpagent"; - -export class AxiosInitializer { - public static initializeAxios(): AxiosInstance { - const httpsProxy = process.env.https_proxy || process.env.HTTPS_PROXY; - - if (httpsProxy) { - const httpsAgent = new HttpsProxyAgent({ - proxy: httpsProxy - }) - - return axios.create({ - httpsAgent, - proxy: false - }) - } else { - return axios.create(); - } - } -} \ No newline at end of file diff --git a/src/util/context-initializer.ts b/src/util/context-initializer.ts deleted file mode 100644 index 9b4ada44..00000000 --- a/src/util/context-initializer.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { logger } from "./logger"; -import { contextService } from "../services/context.service"; -import { program } from "./program"; - -export class ContextInitializer { - public static async initContext(): Promise { - const options = program.parseOptions(process.argv); - const pOptionIndex = options.unknown.indexOf("-p"); - const indexOfProfileOption = pOptionIndex !== -1 ? pOptionIndex : options.unknown.indexOf("--profile"); - - process.on("unhandledRejection", (e, promise) => { - logger.error(e.toString()); - }); - - await contextService.resolveProfile(options.unknown[indexOfProfileOption + 1]); - } -} diff --git a/src/util/json.ts b/src/util/json.ts deleted file mode 100644 index 7f45a685..00000000 --- a/src/util/json.ts +++ /dev/null @@ -1,15 +0,0 @@ - -export function stringify(data: any): string { - return JSON.stringify(data, replacer, 2); -} - -export function parse(data: string): T { - return JSON.parse(data); -} - -const replacer = (key, value) => { - if (value instanceof Map) { - return Object.fromEntries(value); - } - return value; -}; diff --git a/src/util/logger.ts b/src/util/logger.ts deleted file mode 100644 index 179bd862..00000000 --- a/src/util/logger.ts +++ /dev/null @@ -1,39 +0,0 @@ -import * as winston from "winston"; -import * as Transport from "winston-transport"; -import { Logger } from "winston"; - -class CustomTransport extends Transport { - constructor(opts: any) { - super(opts); - } - - public log(info: any, cb: () => void): void { - setImmediate(() => { - this.emit("logged", info); - }); - - cb(); - if (info.error || (info.errno && info.errno !== "__CELGRACEFULERROR")) { - process.exit(1); - } - } -} - -export const logger: Logger = winston.createLogger({ - format: winston.format.combine(winston.format.cli()), - transports: [new winston.transports.Console(), new CustomTransport({})], - exceptionHandlers: [new winston.transports.Console(), new CustomTransport({})], - exitOnError: true, -}); - -// tslint:disable-next-line: max-classes-per-file -export class FatalError extends Error { - public error = "FatalError"; -} - -// By default the logger will process.exit(1) when logging an uncaught fatal error -// This interface allows us to throw errors that do not force the process to exit and can be handled gracefully -// tslint:disable-next-line: max-classes-per-file -export class GracefulError extends Error { - public code: "__CELGRACEFULERROR"; -} \ No newline at end of file diff --git a/src/util/program.ts b/src/util/program.ts deleted file mode 100644 index 4f956fc1..00000000 --- a/src/util/program.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { CommandService } from "../services/command.service"; -import { Command } from "commander"; - -const commandService = new CommandService(); - -export const program: Command = commandService.program; \ No newline at end of file diff --git a/src/util/semantic-versioning.ts b/src/util/semantic-versioning.ts deleted file mode 100644 index 6a55d464..00000000 --- a/src/util/semantic-versioning.ts +++ /dev/null @@ -1,23 +0,0 @@ -export class SemanticVersioning { - private version: string; - - constructor(version: string) { - this.version = version; - } - - public isGreaterThan(versionToCompare: SemanticVersioning): boolean { - const splitVersion1 = this.version.split("."); - const splitVersion2 = versionToCompare.version.split("."); - - const majorVersion1 = splitVersion1[0]; - const majorVersion2 = splitVersion2[0]; - - const minorVersion1 = splitVersion1[1]; - const minorVersion2 = splitVersion2[1]; - - const patchVersion1 = splitVersion1[2]; - const patchVersion2 = splitVersion2[2]; - - return (majorVersion1 > majorVersion2) || (minorVersion1 > minorVersion2) || (patchVersion1 > patchVersion2); - } -} \ No newline at end of file diff --git a/src/util/tracing.ts b/src/util/tracing.ts deleted file mode 100644 index 85acb987..00000000 --- a/src/util/tracing.ts +++ /dev/null @@ -1,24 +0,0 @@ -import * as crypto from "crypto"; - -export class TracingUtils { - - public static getTracingHeaders(): { [key: string]: string } { - return { - "x-datadog-trace-id": this.getTraceId(), - "x-datadog-parent-id": this.getParentTraceId(), - "x-datadog-sampling-priority": "1", - }; - } - - private static getTraceId(): string { - return process.env.TRACE_ID || this.generateId(); - } - - private static getParentTraceId(): string { - return process.env.PARENT_TRACE_ID || this.generateId(); - } - - private static generateId(): string { - return crypto.randomBytes(8).toString("hex"); - } -} diff --git a/src/util/yaml.ts b/src/util/yaml.ts deleted file mode 100644 index 8c9c9518..00000000 --- a/src/util/yaml.ts +++ /dev/null @@ -1,9 +0,0 @@ -import * as YAML from "yaml"; - -export function stringify(data: any): string { - return YAML.stringify(data, {doubleQuotedAsJSON: true, indent: 2, lineWidth: 200}); -} - -export function parse(data: string): T { - return YAML.parse(data); -} diff --git a/src/validators/profile.validator.ts b/src/validators/profile.validator.ts deleted file mode 100644 index b311ae9c..00000000 --- a/src/validators/profile.validator.ts +++ /dev/null @@ -1,23 +0,0 @@ -import {Profile, ProfileType} from "../interfaces/profile.interface"; -import { FatalError, logger } from "../util/logger"; -import validUrl = require("valid-url"); - -export class ProfileValidator { - public static async validateProfile(profile: Profile): Promise { - if (profile.name == null) { - logger.error(new FatalError("The name can not be empty")); - } - if (profile.team == null) { - logger.error(new FatalError("The team can not be empty")); - } - if (profile.type === ProfileType.KEY && profile.apiToken == null) { - logger.error(new FatalError("The api token can not be empty for this profile type")); - } - if (profile.type === ProfileType.CLIENT_CREDENTIALS && (profile.clientId == null || profile.clientSecret == null)) { - logger.error(new FatalError("The client id and secret can not be empty for this profile type")); - } - if (!validUrl.isUri(profile.team)) { - logger.error(new FatalError("The provided url is not a valid url.")); - } - } -} diff --git a/tests/.gitkeep b/tests/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/tests/analyze/action-flows.spec.ts b/tests/analyze/action-flows.spec.ts deleted file mode 100644 index 8cb1f7a1..00000000 --- a/tests/analyze/action-flows.spec.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { FileService } from "../../src/services/file-service"; -import * as path from "path"; -import { mockWriteFileSync, testTransport } from "../jest.setup"; -import { mockedAxiosInstance } from "../utls/http-requests-mock"; -import { ActionFlowCommand } from "../../src/commands/action-flow.command"; - -describe("Analyze action-flows", () => { - - const packageId = "123-456-789"; - const mockAnalyzeResponse = { - "actionFlows": [ - { - "key": "987_asset_key", - "rootNodeKey": "123_root_key_node", - "parentNodeKey": "555_parent_node_key", - "name": "T2T - simple package Automation", - "scenarioId": "321", - "webHookUrl": null, - "version": "10", - "sensorType": null, - "schedule": { - "type": "indefinitely", - "interval": 900, - }, - "teamSpecific": { - "connections": [], - "variables": [], - "celonisApps": [], - "callingOtherAf": [], - "datastructures": [], - }, - }, - ], - "connections": [], - "dataPools": [], - "dataModels": [], - "skills": [], - "analyses": [], - "datastructures": [], - "mappings": [], - "actionFlowsTeamId": "1234", - }; - - it("Should call import API and return non-json response", async () => { - const resp = { data: mockAnalyzeResponse }; - (mockedAxiosInstance.get as jest.Mock).mockResolvedValue(resp); - - await new ActionFlowCommand().analyzeActionFlows(packageId, false); - - expect(testTransport.logMessages.length).toBe(1); - expect(testTransport.logMessages[0].message).toContain(JSON.stringify(mockAnalyzeResponse, null, 4)); - - expect(mockedAxiosInstance.get).toHaveBeenCalledWith(`https://myTeam.celonis.cloud/ems-automation/api/root/${packageId}/export/assets/analyze`, expect.anything()); - }); - - it("Should call import API and return json response", async () => { - const resp = { data: mockAnalyzeResponse }; - (mockedAxiosInstance.get as jest.Mock).mockResolvedValue(resp); - - await new ActionFlowCommand().analyzeActionFlows(packageId, true); - - expect(testTransport.logMessages.length).toBe(1); - expect(testTransport.logMessages[0].message).toContain(FileService.fileDownloadedMessage); - const expectedFileName = testTransport.logMessages[0].message.split(FileService.fileDownloadedMessage)[1]; - - expect(mockWriteFileSync).toHaveBeenCalledWith(path.resolve(process.cwd(), expectedFileName), JSON.stringify(mockAnalyzeResponse, null, 4), { encoding: "utf-8" }); - expect(mockedAxiosInstance.get).toHaveBeenCalledWith(`https://myTeam.celonis.cloud/ems-automation/api/root/${packageId}/export/assets/analyze`, expect.anything()); - }); -}); \ No newline at end of file diff --git a/tests/config/config-diff.spec.ts b/tests/config/config-diff.spec.ts deleted file mode 100644 index 72e3c8df..00000000 --- a/tests/config/config-diff.spec.ts +++ /dev/null @@ -1,176 +0,0 @@ -import {PackageManifestTransport} from "../../src/interfaces/package-export-transport"; -import {ConfigUtils} from "../utls/config-utils"; -import * as path from "path"; -import {mockCreateReadStream, mockExistsSync, mockReadFileSync} from "../utls/fs-mock-utils"; -import { - NodeConfigurationChangeType, - PackageDiffMetadata, - PackageDiffTransport, -} from "../../src/interfaces/diff-package.transport"; -import {mockAxiosPost} from "../utls/http-requests-mock"; -import {ConfigCommand} from "../../src/commands/config.command"; -import { mockWriteFileSync, mockWriteSync, testTransport } from "../jest.setup"; -import { FileService } from "../../src/services/file-service"; - -describe("Config diff", () => { - - beforeEach(() => { - mockExistsSync(); - }); - - it("Should show on terminal if packages have changes with hasChanges set to true and jsonResponse false", async () => { - const manifest: PackageManifestTransport[] = []; - manifest.push(ConfigUtils.buildManifestForKeyAndFlavor("package-key", "STUDIO")); - - const firstPackageNode = ConfigUtils.buildPackageNode("package-key", {metadata: {description: "test"}, variables: [], dependencies: []}); - const firstChildNode = ConfigUtils.buildChildNode("key-1", "package-key", "TEST"); - const firstPackageZip = ConfigUtils.buildExportPackageZip(firstPackageNode, [firstChildNode], "1.0.0"); - const exportedPackagesZip = ConfigUtils.buildBatchExportZip(manifest, [firstPackageZip]); - - mockReadFileSync(exportedPackagesZip.toBuffer()); - mockCreateReadStream(exportedPackagesZip.toBuffer()); - - const diffResponse: PackageDiffMetadata[] = [{ - packageKey: "package-key", - hasChanges: true - }]; - - mockAxiosPost("https://myTeam.celonis.cloud/package-manager/api/core/packages/diff/configuration/has-changes", diffResponse); - - await new ConfigCommand().diffPackages("./packages.zip", true, false); - - expect(testTransport.logMessages.length).toBe(1); - expect(testTransport.logMessages[0].message).toContain( - JSON.stringify(diffResponse, null, 2) - ); - }); - - it("Should show diff on terminal with hasChanges set to false and jsonResponse false", async () => { - const manifest: PackageManifestTransport[] = []; - manifest.push(ConfigUtils.buildManifestForKeyAndFlavor("package-key", "STUDIO")); - - const firstPackageNode = ConfigUtils.buildPackageNode("package-key", {metadata: {description: "test"}, variables: [], dependencies: []}); - const firstChildNode = ConfigUtils.buildChildNode("key-1", "package-key", "TEST"); - const firstPackageZip = ConfigUtils.buildExportPackageZip(firstPackageNode, [firstChildNode], "1.0.0"); - const exportedPackagesZip = ConfigUtils.buildBatchExportZip(manifest, [firstPackageZip]); - - mockReadFileSync(exportedPackagesZip.toBuffer()); - mockCreateReadStream(exportedPackagesZip.toBuffer()); - - const diffResponse: PackageDiffTransport[] = [{ - packageKey: "package-key", - packageChanges: [ - { - op: "add", - path: "/test", - from: "bbbb", - value: JSON.parse("123"), - fromValue: null - }], - nodesWithChanges: [{ - nodeKey: firstChildNode.key, - name: firstChildNode.name, - type: firstChildNode.type, - changeType: NodeConfigurationChangeType.ADDED, - changes: [{ - op: "add", - path: "/test", - from: "bbb", - value: JSON.parse("234"), - fromValue: null - }] - }] - }]; - - mockAxiosPost("https://myTeam.celonis.cloud/package-manager/api/core/packages/diff/configuration", diffResponse); - - await new ConfigCommand().diffPackages("./packages.zip", false, false); - - expect(testTransport.logMessages.length).toBe(1); - expect(testTransport.logMessages[0].message).toContain( - JSON.stringify(diffResponse, null, 2) - ); - }); - - it("Should generate a json file with diff info when hasChanges is set to false and jsonResponse is set to true", async () => { - const manifest: PackageManifestTransport[] = []; - manifest.push(ConfigUtils.buildManifestForKeyAndFlavor("package-key", "STUDIO")); - - const firstPackageNode = ConfigUtils.buildPackageNode("package-key", {metadata: {description: "test"}, variables: [], dependencies: []}); - const firstChildNode = ConfigUtils.buildChildNode("key-1", "package-key", "TEST"); - const firstPackageZip = ConfigUtils.buildExportPackageZip(firstPackageNode, [firstChildNode], "1.0.0"); - const exportedPackagesZip = ConfigUtils.buildBatchExportZip(manifest, [firstPackageZip]); - - mockReadFileSync(exportedPackagesZip.toBuffer()); - mockCreateReadStream(exportedPackagesZip.toBuffer()); - - const diffResponse: PackageDiffTransport[] = [{ - packageKey: "package-key", - packageChanges: [ - { - op: "add", - path: "/test", - from: "bbbb", - value: JSON.parse("123"), - fromValue: null - }], - nodesWithChanges: [{ - nodeKey: firstChildNode.key, - name: firstChildNode.name, - type: firstChildNode.type, - changeType: NodeConfigurationChangeType.ADDED, - changes: [{ - op: "add", - path: "/test", - from: "bbb", - value: JSON.parse("234"), - fromValue: null - }] - }] - }]; - - mockAxiosPost("https://myTeam.celonis.cloud/package-manager/api/core/packages/diff/configuration", diffResponse); - - await new ConfigCommand().diffPackages("./packages.zip", false, true); - - const expectedFileName = testTransport.logMessages[0].message.split(FileService.fileDownloadedMessage)[1]; - - expect(mockWriteFileSync).toHaveBeenCalledWith(path.resolve(process.cwd(), expectedFileName), expect.any(String), {encoding: "utf-8"}); - const exportedPackageDiffTransport = JSON.parse(mockWriteFileSync.mock.calls[0][1]) as PackageDiffTransport[]; - expect(exportedPackageDiffTransport.length).toBe(1); - - const exportedFirstPackageDiffTransport = exportedPackageDiffTransport.filter(diffTransport => diffTransport.packageKey === firstPackageNode.key); - expect(exportedFirstPackageDiffTransport).toEqual(diffResponse); - }); - - it("Should generate a json file with info whether packages have changes when hasChanges is set to true and jsonResponse is set to true", async () => { - const manifest: PackageManifestTransport[] = []; - manifest.push(ConfigUtils.buildManifestForKeyAndFlavor("package-key", "STUDIO")); - - const firstPackageNode = ConfigUtils.buildPackageNode("package-key", {metadata: {description: "test"}, variables: [], dependencies: []}); - const firstChildNode = ConfigUtils.buildChildNode("key-1", "package-key", "TEST"); - const firstPackageZip = ConfigUtils.buildExportPackageZip(firstPackageNode, [firstChildNode], "1.0.0"); - const exportedPackagesZip = ConfigUtils.buildBatchExportZip(manifest, [firstPackageZip]); - - mockReadFileSync(exportedPackagesZip.toBuffer()); - mockCreateReadStream(exportedPackagesZip.toBuffer()); - - const diffResponse: PackageDiffMetadata[] = [{ - packageKey: "package-key", - hasChanges: true - }]; - - mockAxiosPost("https://myTeam.celonis.cloud/package-manager/api/core/packages/diff/configuration/has-changes", diffResponse); - - await new ConfigCommand().diffPackages("./packages.zip", true, true); - - const expectedFileName = testTransport.logMessages[0].message.split(FileService.fileDownloadedMessage)[1]; - - expect(mockWriteFileSync).toHaveBeenCalledWith(path.resolve(process.cwd(), expectedFileName), expect.any(String), {encoding: "utf-8"}); - const exportedPackageDiffTransport = JSON.parse(mockWriteFileSync.mock.calls[0][1]) as PackageDiffTransport[]; - expect(exportedPackageDiffTransport.length).toBe(1); - - const exportedFirstPackageDiffTransport = exportedPackageDiffTransport.filter(diffTransport => diffTransport.packageKey === firstPackageNode.key); - expect(exportedFirstPackageDiffTransport).toEqual(diffResponse); - }); -}); \ No newline at end of file diff --git a/tests/config/config-export.spec.ts b/tests/config/config-export.spec.ts deleted file mode 100644 index a6d520a4..00000000 --- a/tests/config/config-export.spec.ts +++ /dev/null @@ -1,553 +0,0 @@ -import {ConfigUtils} from "../utls/config-utils"; -import { - DependencyTransport, NodeConfiguration, NodeExportTransport, - PackageManifestTransport, - StudioPackageManifest, - VariableManifestTransport, -} from "../../src/interfaces/package-export-transport"; -import {mockAxiosGet, mockAxiosPost, mockedPostRequestBodyByUrl} from "../utls/http-requests-mock"; -import {ConfigCommand} from "../../src/commands/config.command"; -import {PackageManagerApiUtils} from "../utls/package-manager-api.utils"; -import {mockWriteSync, testTransport} from "../jest.setup"; -import {FileService} from "../../src/services/file-service"; -import * as fs from "fs"; -import AdmZip = require("adm-zip"); - -import { parse, stringify } from "../../src/util/json"; -import { - PackageManagerVariableType, - VariableDefinition, - VariablesAssignments -} from "../../src/interfaces/package-manager.interfaces"; -import {BatchExportImportConstants} from "../../src/interfaces/batch-export-import-constants"; - -describe("Config export", () => { - - const firstSpace = PackageManagerApiUtils.buildSpaceTransport("space-1", "First space", "Icon1"); - const secondSpace = PackageManagerApiUtils.buildSpaceTransport("space-2", "Second space", "Icon2"); - - beforeEach(() => { - (fs.openSync as jest.Mock).mockReturnValue(100); - mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/spaces/space-1", {...firstSpace}); - mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/spaces/space-2", {...secondSpace}); - }); - - it("Should export studio file for studio packageKeys", async () => { - const manifest: PackageManifestTransport[] = []; - manifest.push(ConfigUtils.buildManifestForKeyAndFlavor("key-1", BatchExportImportConstants.STUDIO)); - manifest.push(ConfigUtils.buildManifestForKeyAndFlavor("key-2", BatchExportImportConstants.STUDIO)); - manifest.push(ConfigUtils.buildManifestForKeyAndFlavor("key-3", "TEST")); - const exportedPackagesZip = ConfigUtils.buildBatchExportZip(manifest, []); - - const firstStudioPackage = PackageManagerApiUtils.buildContentNodeTransport("key-1", "space-1"); - const firstPackageRuntimeVariable: VariablesAssignments = { - key: "varKey", - type: PackageManagerVariableType.PLAIN_TEXT, - value: "default-value" as unknown as object - }; - - const secondStudioPackage = PackageManagerApiUtils.buildContentNodeTransport("key-2", "space-2"); - - mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/core/packages/export/batch?packageKeys=key-1&packageKeys=key-2&packageKeys=key-3&withDependencies=true", exportedPackagesZip.toBuffer()); - mockAxiosPost("https://myTeam.celonis.cloud/package-manager/api/core/packages/export/batch/variables-with-assignments", []); - mockAxiosGet(`https://myTeam.celonis.cloud/package-manager/api/nodes/${firstStudioPackage.key}/${firstStudioPackage.key}`, firstStudioPackage); - mockAxiosGet(`https://myTeam.celonis.cloud/package-manager/api/nodes/${secondStudioPackage.key}/${secondStudioPackage.key}`, secondStudioPackage); - mockAxiosGet(`https://myTeam.celonis.cloud/package-manager/api/nodes/by-package-key/${firstStudioPackage.key}/variables/runtime-values?appMode=VIEWER`, [firstPackageRuntimeVariable]); - mockAxiosGet(`https://myTeam.celonis.cloud/package-manager/api/nodes/by-package-key/${secondStudioPackage.key}/variables/runtime-values?appMode=VIEWER`, []); - - await new ConfigCommand().batchExportPackages(["key-1", "key-2", "key-3"], true); - - const expectedFileName = testTransport.logMessages[0].message.split(FileService.fileDownloadedMessage)[1]; - expect(fs.openSync).toHaveBeenCalledWith(expectedFileName, expect.anything(), expect.anything()); - expect(mockWriteSync).toHaveBeenCalled(); - - const fileBuffer = mockWriteSync.mock.calls[0][1]; - const actualZip = new AdmZip(fileBuffer); - - const studioManifest: StudioPackageManifest[] = parse(actualZip.getEntry(BatchExportImportConstants.STUDIO_FILE_NAME).getData().toString()); - expect(studioManifest).toHaveLength(2); - expect(studioManifest).toContainEqual({ - packageKey: firstStudioPackage.key, - space: { - name: firstSpace.name, - iconReference: firstSpace.iconReference - }, - runtimeVariableAssignments: [firstPackageRuntimeVariable] - }); - expect(studioManifest).toContainEqual({ - packageKey: secondStudioPackage.key, - space: { - name: secondSpace.name, - iconReference: secondSpace.iconReference - }, - runtimeVariableAssignments: [] - }); - }) - - it("Should export variables file with connection variables fixed", async () => { - const firstPackageDependencies = new Map(); - firstPackageDependencies.set("1.0.0", []); - - const secondPackageDependencies = new Map(); - secondPackageDependencies.set("1.0.0", []); - secondPackageDependencies.set("1.1.1", []); - - const manifest: PackageManifestTransport[] = []; - manifest.push(ConfigUtils.buildManifestForKeyAndFlavor("key-1", BatchExportImportConstants.STUDIO, firstPackageDependencies)); - manifest.push(ConfigUtils.buildManifestForKeyAndFlavor("key-2", BatchExportImportConstants.STUDIO, secondPackageDependencies)); - - const firstPackageVariableDefinition: VariableDefinition[] = [ - { - key: "key1-var", - type: PackageManagerVariableType.DATA_MODEL, - runtime: false - }, - { - key: "key-1-connection", - type: PackageManagerVariableType.CONNECTION, - runtime: false - } - ]; - - const firstPackageNode = ConfigUtils.buildPackageNode("key-1", {variables: firstPackageVariableDefinition}); - const firstPackageZip = ConfigUtils.buildExportPackageZip(firstPackageNode, [], "1.0.0"); - - const secondPackageVariableDefinition: VariableDefinition[] = [ - { - key: "key2-var", - type: PackageManagerVariableType.DATA_MODEL, - runtime: false - }, - { - key: "key-2-connection", - type: PackageManagerVariableType.CONNECTION, - runtime: false - } - ]; - - const secondPackageNode = ConfigUtils.buildPackageNode("key-2", {variables: secondPackageVariableDefinition}); - const secondPackageZip = ConfigUtils.buildExportPackageZip(secondPackageNode, [], "1.0.0"); - - const exportedPackagesZip = ConfigUtils.buildBatchExportZip(manifest, [firstPackageZip, secondPackageZip]); - - const exportedVariables: VariableManifestTransport[] = [ - { - packageKey: "key-1", - version: "1.0.0", - variables: [ - { - key: "key1-var", - type: PackageManagerVariableType.DATA_MODEL, - value: "dm-id" as unknown as object, - metadata: {} - }, - { - key: "key-1-connection", - type: PackageManagerVariableType.CONNECTION, - value: { - appName: "celonis", - connectionId: "connection-id" - } as unknown as object, - metadata: null - } - ] - }, - { - packageKey: "key-2", - version: "1.0.0", - variables: [ - { - key: "key2-var", - type: PackageManagerVariableType.DATA_MODEL, - value: "dm-id" as unknown as object, - metadata: {} - }, - { - key: "key-2-connection", - type: PackageManagerVariableType.CONNECTION, - value: "connection-id", - metadata: { - appName: "nameOfApp" - } - } - ] - }, - ]; - - mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/core/packages/export/batch?packageKeys=key-1&packageKeys=key-2&withDependencies=true", exportedPackagesZip.toBuffer()); - mockAxiosPost("https://myTeam.celonis.cloud/package-manager/api/core/packages/export/batch/variables-with-assignments", [...exportedVariables]); - mockAxiosGet(`https://myTeam.celonis.cloud/package-manager/api/nodes/${firstPackageNode.key}/${firstPackageNode.key}`, {...firstPackageNode, spaceId: "space-1"}); - mockAxiosGet(`https://myTeam.celonis.cloud/package-manager/api/nodes/${secondPackageNode.key}/${secondPackageNode.key}`, {...secondPackageNode, spaceId: "space-2"}); - mockAxiosGet(`https://myTeam.celonis.cloud/package-manager/api/nodes/by-package-key/${firstPackageNode.key}/variables/runtime-values?appMode=VIEWER`, []); - mockAxiosGet(`https://myTeam.celonis.cloud/package-manager/api/nodes/by-package-key/${secondPackageNode.key}/variables/runtime-values?appMode=VIEWER`, []); - - await new ConfigCommand().batchExportPackages(["key-1", "key-2"], true); - - const expectedFileName = testTransport.logMessages[0].message.split(FileService.fileDownloadedMessage)[1]; - expect(fs.openSync).toHaveBeenCalledWith(expectedFileName, expect.anything(), expect.anything()); - expect(mockWriteSync).toHaveBeenCalled(); - - const fileBuffer = mockWriteSync.mock.calls[0][1]; - const actualZip = new AdmZip(fileBuffer); - - const exportedVariablesFileContent: VariableManifestTransport[] = parse(actualZip.getEntry(BatchExportImportConstants.VARIABLES_FILE_NAME).getData().toString()); - expect(exportedVariablesFileContent).toHaveLength(2); - expect(exportedVariablesFileContent).toContainEqual({ - packageKey: "key-1", - version: "1.0.0", - variables: [ - { - key: "key1-var", - type: PackageManagerVariableType.DATA_MODEL, - value: "dm-id", - metadata: {} - }, - { - key: "key-1-connection", - type: PackageManagerVariableType.CONNECTION, - value: { - appName: "celonis", - connectionId: "connection-id" - }, - metadata: { - appName: "celonis" - } - } - ] - }); - expect(exportedVariablesFileContent).toContainEqual({ - packageKey: "key-2", - version: "1.0.0", - variables: [ - { - key: "key2-var", - type: PackageManagerVariableType.DATA_MODEL, - value: "dm-id", - metadata: {} - }, - { - key: "key-2-connection", - type: PackageManagerVariableType.CONNECTION, - value: "connection-id", - metadata: { - appName: "nameOfApp" - } - } - ] - }); - - const variableExportRequest = parse(mockedPostRequestBodyByUrl.get("https://myTeam.celonis.cloud/package-manager/api/core/packages/export/batch/variables-with-assignments")); - expect(variableExportRequest).toBeTruthy(); - expect(variableExportRequest).toHaveLength(3); - expect(variableExportRequest).toContainEqual({ - packageKey: "key-1", - version: "1.0.0" - }); - expect(variableExportRequest).toContainEqual({ - packageKey: "key-2", - version: "1.0.0" - }); - expect(variableExportRequest).toContainEqual({ - packageKey: "key-2", - version: "1.1.1" - }); - }) - - it("Should remove SCENARIO asset files of packages", async () => { - const manifest: PackageManifestTransport[] = []; - manifest.push(ConfigUtils.buildManifestForKeyAndFlavor("key-1", BatchExportImportConstants.STUDIO)); - manifest.push(ConfigUtils.buildManifestForKeyAndFlavor("key-2", BatchExportImportConstants.STUDIO)); - - const firstPackageNode = ConfigUtils.buildPackageNode("key-1", {}); - const firstPackageScenarioChild = ConfigUtils.buildChildNode("child-1-scenario", firstPackageNode.key, "SCENARIO"); - const firstPackageTestChild = ConfigUtils.buildChildNode("child-2", firstPackageNode.key, "TEST"); - const firstPackageZip = ConfigUtils.buildExportPackageZip(firstPackageNode, [firstPackageScenarioChild, firstPackageTestChild], "1.0.0"); - - const secondPackageNode = ConfigUtils.buildPackageNode("key-2", {}); - const secondPackageScenarioChild = ConfigUtils.buildChildNode("child-3-scenario", secondPackageNode.key, "SCENARIO"); - const secondPackageTestChild = ConfigUtils.buildChildNode("child-4", secondPackageNode.key, "TEST"); - const secondPackageZip = ConfigUtils.buildExportPackageZip(secondPackageNode, [secondPackageScenarioChild, secondPackageTestChild], "1.0.0"); - - const exportedPackagesZip = ConfigUtils.buildBatchExportZip(manifest, [firstPackageZip, secondPackageZip]); - - mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/core/packages/export/batch?packageKeys=key-1&packageKeys=key-2&withDependencies=true", exportedPackagesZip.toBuffer()); - mockAxiosPost("https://myTeam.celonis.cloud/package-manager/api/core/packages/export/batch/variables-with-assignments", []); - mockAxiosGet(`https://myTeam.celonis.cloud/package-manager/api/nodes/${firstPackageNode.key}/${firstPackageNode.key}`, {...firstPackageNode, spaceId: "space-1"}); - mockAxiosGet(`https://myTeam.celonis.cloud/package-manager/api/nodes/${secondPackageNode.key}/${secondPackageNode.key}`, {...secondPackageNode, spaceId: "space-2"}); - mockAxiosGet(`https://myTeam.celonis.cloud/package-manager/api/nodes/by-package-key/${firstPackageNode.key}/variables/runtime-values?appMode=VIEWER`, []); - mockAxiosGet(`https://myTeam.celonis.cloud/package-manager/api/nodes/by-package-key/${secondPackageNode.key}/variables/runtime-values?appMode=VIEWER`, []); - - await new ConfigCommand().batchExportPackages(["key-1", "key-2"], true); - - const expectedFileName = testTransport.logMessages[0].message.split(FileService.fileDownloadedMessage)[1]; - expect(fs.openSync).toHaveBeenCalledWith(expectedFileName, expect.anything(), expect.anything()); - expect(mockWriteSync).toHaveBeenCalled(); - - const fileBuffer = mockWriteSync.mock.calls[0][1]; - const actualZip = new AdmZip(fileBuffer); - - const firstPackageExportedZip = new AdmZip(actualZip.getEntry("key-1_1.0.0.zip").getData()); - expect(firstPackageExportedZip.getEntry("nodes/child-1-scenario.json")).toBeNull(); - expect(firstPackageExportedZip.getEntry("nodes/child-2.json").getData().toString()).toEqual(stringify(firstPackageTestChild)); - - const secondPackageExportedZip = new AdmZip(actualZip.getEntry("key-2_1.0.0.zip").getData()); - expect(secondPackageExportedZip.getEntry("nodes/child-3-scenario.json")).toBeNull(); - expect(secondPackageExportedZip.getEntry("nodes/child-4.json").getData().toString()).toEqual(stringify(secondPackageTestChild)); - }) - - it("Should add appName to metadata for CONNECTION variables of package.json files", async () => { - const manifest: PackageManifestTransport[] = []; - manifest.push(ConfigUtils.buildManifestForKeyAndFlavor("key-1", BatchExportImportConstants.STUDIO)); - manifest.push(ConfigUtils.buildManifestForKeyAndFlavor("key-2", BatchExportImportConstants.STUDIO)); - - const firstPackageVariableDefinition: VariableDefinition[] = [ - { - key: "key1-var", - type: PackageManagerVariableType.DATA_MODEL, - runtime: false - }, - { - key: "key-1-connection", - type: PackageManagerVariableType.CONNECTION, - runtime: false - } - ]; - - const firstPackageNode = ConfigUtils.buildPackageNode("key-1", {variables: firstPackageVariableDefinition}); - const firstPackageZip = ConfigUtils.buildExportPackageZip(firstPackageNode, [], "1.0.0"); - - const secondPackageVariableDefinition: VariableDefinition[] = [ - { - key: "key2-var", - type: PackageManagerVariableType.CONNECTION, - runtime: false, - metadata: { - appName: "celonis" - } - }, - { - key: "key-2-connection", - type: PackageManagerVariableType.CONNECTION, - runtime: false - } - ]; - - const secondPackageNode = ConfigUtils.buildPackageNode("key-2", {variables: secondPackageVariableDefinition}); - const secondPackageZip = ConfigUtils.buildExportPackageZip(secondPackageNode, [], "1.0.0"); - - const exportedPackagesZip = ConfigUtils.buildBatchExportZip(manifest, [firstPackageZip, secondPackageZip]); - - const exportedVariables: VariableManifestTransport[] = [ - { - packageKey: "key-1", - version: "1.0.0", - variables: [ - { - key: "key1-var", - type: PackageManagerVariableType.DATA_MODEL, - value: "dm-id" as unknown as object, - metadata: {} - }, - { - key: "key-1-connection", - type: PackageManagerVariableType.CONNECTION, - value: { - appName: "celonis", - connectionId: "connection-id" - } as unknown as object, - metadata: null - } - ] - }, - { - packageKey: "key-2", - version: "1.0.0", - variables: [ - { - key: "key2-var", - type: PackageManagerVariableType.CONNECTION, - value: { - appName: "celonis", - connectionId: "connection-id" - } as unknown as object, - metadata: { - appName: "celonis" - } - }, - { - key: "key-2-connection", - type: PackageManagerVariableType.CONNECTION, - value: "connection-id", - metadata: { - appName: "nameOfApp" - } - } - ] - }, - ]; - - mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/core/packages/export/batch?packageKeys=key-1&packageKeys=key-2&withDependencies=true", exportedPackagesZip.toBuffer()); - mockAxiosPost("https://myTeam.celonis.cloud/package-manager/api/core/packages/export/batch/variables-with-assignments", exportedVariables); - mockAxiosGet(`https://myTeam.celonis.cloud/package-manager/api/nodes/${firstPackageNode.key}/${firstPackageNode.key}`, {...firstPackageNode, spaceId: "space-1"}); - mockAxiosGet(`https://myTeam.celonis.cloud/package-manager/api/nodes/${secondPackageNode.key}/${secondPackageNode.key}`, {...secondPackageNode, spaceId: "space-2"}); - mockAxiosGet(`https://myTeam.celonis.cloud/package-manager/api/nodes/by-package-key/${firstPackageNode.key}/variables/runtime-values?appMode=VIEWER`, []); - mockAxiosGet(`https://myTeam.celonis.cloud/package-manager/api/nodes/by-package-key/${secondPackageNode.key}/variables/runtime-values?appMode=VIEWER`, []); - - await new ConfigCommand().batchExportPackages(["key-1", "key-2"], true); - - const expectedFileName = testTransport.logMessages[0].message.split(FileService.fileDownloadedMessage)[1]; - expect(fs.openSync).toHaveBeenCalledWith(expectedFileName, expect.anything(), expect.anything()); - expect(mockWriteSync).toHaveBeenCalled(); - - const fileBuffer = mockWriteSync.mock.calls[0][1]; - const actualZip = new AdmZip(fileBuffer); - - const firstPackageExportedZip = new AdmZip(actualZip.getEntry("key-1_1.0.0.zip").getData()); - const firstPackageExportedNode: NodeExportTransport = parse(firstPackageExportedZip.getEntry("package.json").getData().toString()); - expect(firstPackageExportedNode).toBeTruthy(); - const firstPackageContent: NodeConfiguration = firstPackageExportedNode.configuration; - expect(firstPackageContent.variables).toHaveLength(2); - expect(firstPackageContent.variables).toEqual([ - { - ...firstPackageVariableDefinition[0], - }, - { - ...firstPackageVariableDefinition[1], - metadata: { - appName: "celonis" - } - } - ]); - - const secondPackageExportedZip = new AdmZip(actualZip.getEntry("key-2_1.0.0.zip").getData()); - const secondPackageExportedNode: NodeExportTransport = parse(secondPackageExportedZip.getEntry("package.json").getData().toString()); - expect(secondPackageExportedNode).toBeTruthy(); - const secondPackageContent: NodeConfiguration = secondPackageExportedNode.configuration; - expect(secondPackageContent.variables).toHaveLength(2); - expect(secondPackageContent.variables).toEqual([{ - ...secondPackageVariableDefinition[0], - }, - { - ...secondPackageVariableDefinition[1], - metadata: { - appName: "nameOfApp" - } - } - ]); - }) - - it("Should export with SCENARIO nodes removed and CONNECTION variables fixed for package key with multiple underscores", async () => { - const manifest: PackageManifestTransport[] = []; - manifest.push(ConfigUtils.buildManifestForKeyAndFlavor("key_with_underscores_1", BatchExportImportConstants.STUDIO)); - - const firstPackageVariableDefinition: VariableDefinition[] = [ - { - key: "key1-var", - type: PackageManagerVariableType.DATA_MODEL, - runtime: false - }, - { - key: "key-1-connection", - type: PackageManagerVariableType.CONNECTION, - runtime: false - }, - { - key: "key-1-another-connection", - type: PackageManagerVariableType.CONNECTION, - runtime: false - } - ]; - - const firstPackageNode = ConfigUtils.buildPackageNode("key_with_underscores_1", {variables: firstPackageVariableDefinition}); - const firstPackageScenarioChild = ConfigUtils.buildChildNode("child-1-scenario", firstPackageNode.key, "SCENARIO"); - const firstPackageZip = ConfigUtils.buildExportPackageZip(firstPackageNode, [firstPackageScenarioChild], "1.0.0"); - - const exportedPackagesZip = ConfigUtils.buildBatchExportZip(manifest, [firstPackageZip]); - - const exportedVariables: VariableManifestTransport[] = [ - { - packageKey: "key_with_underscores_1", - version: "1.0.0", - variables: [ - { - key: "key1-var", - type: PackageManagerVariableType.DATA_MODEL, - value: "dm-id" as unknown as object, - metadata: {} - }, - { - key: "key-1-connection", - type: PackageManagerVariableType.CONNECTION, - value: { - appName: "celonis", - connectionId: "connection-id" - } as unknown as object, - metadata: null - }, - { - key: "key-1-another-connection", - type: PackageManagerVariableType.CONNECTION, - value: "connection-id", - metadata: { - appName: "nameOfApp" - } - } - ] - } - ]; - - mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/core/packages/export/batch?packageKeys=key_with_underscores_1&withDependencies=true", exportedPackagesZip.toBuffer()); - mockAxiosPost("https://myTeam.celonis.cloud/package-manager/api/core/packages/export/batch/variables-with-assignments", exportedVariables); - mockAxiosGet(`https://myTeam.celonis.cloud/package-manager/api/nodes/${firstPackageNode.key}/${firstPackageNode.key}`, {...firstPackageNode, spaceId: "space-1"}); - mockAxiosGet(`https://myTeam.celonis.cloud/package-manager/api/nodes/by-package-key/${firstPackageNode.key}/variables/runtime-values?appMode=VIEWER`, []); - - await new ConfigCommand().batchExportPackages(["key_with_underscores_1"], true); - - const expectedFileName = testTransport.logMessages[0].message.split(FileService.fileDownloadedMessage)[1]; - expect(fs.openSync).toHaveBeenCalledWith(expectedFileName, expect.anything(), expect.anything()); - expect(mockWriteSync).toHaveBeenCalled(); - - const fileBuffer = mockWriteSync.mock.calls[0][1]; - const actualZip = new AdmZip(fileBuffer); - - const firstPackageExportedZip = new AdmZip(actualZip.getEntry("key_with_underscores_1_1.0.0.zip").getData()); - const firstPackageExportedNode: NodeExportTransport = parse(firstPackageExportedZip.getEntry("package.json").getData().toString()); - expect(firstPackageExportedNode).toBeTruthy(); - const firstPackageContent: NodeConfiguration = firstPackageExportedNode.configuration; - expect(firstPackageContent.variables).toHaveLength(3); - expect(firstPackageContent.variables).toEqual([ - { - ...firstPackageVariableDefinition[0], - }, - { - ...firstPackageVariableDefinition[1], - metadata: { - appName: "celonis" - } - }, { - ...firstPackageVariableDefinition[2], - metadata: { - appName: "nameOfApp" - } - } - ]); - - expect(firstPackageExportedZip.getEntry("nodes/child-1-scenario.json")).toBeNull(); - }) - - it("Should export by packageKeys without dependencies", async () => { - const manifest: PackageManifestTransport[] = []; - manifest.push(ConfigUtils.buildManifestForKeyAndFlavor("key-1", "TEST")); - manifest.push(ConfigUtils.buildManifestForKeyAndFlavor("key-2", "TEST")); - const exportedPackagesZip = ConfigUtils.buildBatchExportZip(manifest, []); - - mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/core/packages/export/batch?packageKeys=key-1&packageKeys=key-2&packageKeys=key-3&withDependencies=false", exportedPackagesZip.toBuffer()); - mockAxiosPost("https://myTeam.celonis.cloud/package-manager/api/core/packages/export/batch/variables-with-assignments", []); - - await new ConfigCommand().batchExportPackages(["key-1", "key-2", "key-3"], false); - - const expectedFileName = testTransport.logMessages[0].message.split(FileService.fileDownloadedMessage)[1]; - expect(fs.openSync).toHaveBeenCalledWith(expectedFileName, expect.anything(), expect.anything()); - expect(mockWriteSync).toHaveBeenCalled(); - }) -}) \ No newline at end of file diff --git a/tests/config/config-import.spec.ts b/tests/config/config-import.spec.ts deleted file mode 100644 index e16c9640..00000000 --- a/tests/config/config-import.spec.ts +++ /dev/null @@ -1,326 +0,0 @@ -import {ConfigCommand} from "../../src/commands/config.command"; -import { - mockAxiosGet, - mockAxiosPost, - mockAxiosPut, mockedPostRequestBodyByUrl -} from "../utls/http-requests-mock"; -import { - PackageManifestTransport, - PostPackageImportData, - StudioPackageManifest -} from "../../src/interfaces/package-export-transport"; -import {ConfigUtils} from "../utls/config-utils"; -import {mockWriteFileSync, testTransport} from "../jest.setup"; -import * as path from "path"; -import {stringify} from "../../src/util/json"; -import {SpaceTransport} from "../../src/interfaces/save-space.interface"; -import {packageApi} from "../../src/api/package-api"; -import { - ContentNodeTransport, - PackageManagerVariableType, - VariablesAssignments -} from "../../src/interfaces/package-manager.interfaces"; -import {mockCreateReadStream, mockExistsSync, mockReadFileSync} from "../utls/fs-mock-utils"; -import {BatchExportImportConstants} from "../../src/interfaces/batch-export-import-constants"; - -import {spaceApi} from "../../src/api/space-api"; - -describe("Config import", () => { - - const LOG_MESSAGE: string = "Config import report file: "; - - beforeEach(() => { - mockExistsSync(); - }) - - it.each([ - true, - false - ]) ("Should batch import package configs with overwrite %p", async (overwrite: boolean) => { - const manifest: PackageManifestTransport[] = []; - manifest.push(ConfigUtils.buildManifestForKeyAndFlavor("key-1", "TEST")); - const exportedPackagesZip = ConfigUtils.buildBatchExportZip(manifest, []); - - mockReadFileSync(exportedPackagesZip.toBuffer()); - mockCreateReadStream(exportedPackagesZip.toBuffer()); - mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/packages", []); - - const importResponse: PostPackageImportData[] = [{ - packageKey: "key-1", - importedVersions: [{ - oldVersion: "1.0.2", - newVersion: "1.0.0" - }] - }]; - - mockAxiosPost("https://myTeam.celonis.cloud/package-manager/api/core/packages/import/batch", importResponse); - - await new ConfigCommand().batchImportPackages("./export_file.zip", overwrite); - - const expectedFileName = testTransport.logMessages[0].message.split(LOG_MESSAGE)[1]; - expect(mockWriteFileSync).toHaveBeenCalledWith(path.resolve(process.cwd(), expectedFileName), JSON.stringify(importResponse), {encoding: "utf-8"}); - }) - - it("Should batch import configs & map space ID as specified in manifest file for Studio Packages", async () => { - const manifest: PackageManifestTransport[] = []; - manifest.push(ConfigUtils.buildManifestForKeyAndFlavor("key-2", "STUDIO")); - - const studioManifest: StudioPackageManifest[] = []; - studioManifest.push(ConfigUtils.buildStudioManifestForKeyWithSpace("key-2", "spaceName", "space-id")); - - const firstPackageNode = ConfigUtils.buildPackageNode("key-2", {variables: []}); - const firstPackageZip = ConfigUtils.buildExportPackageZip(firstPackageNode, [], "1.0.0"); - const exportedPackagesZip = ConfigUtils.buildBatchExportZipWithStudioManifest(manifest, studioManifest,[firstPackageZip]); - - const space: SpaceTransport = { - id: "space-id", - name: "space", - iconReference: "earth" - }; - - const otherSpace: SpaceTransport = { - id: "spaceId", - name: "spaceName", - iconReference: "earth" - }; - - mockReadFileSync(exportedPackagesZip.toBuffer()); - mockCreateReadStream(exportedPackagesZip.toBuffer()); - mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/packages", []); - mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/spaces", [space, otherSpace]); - - const importResponse: PostPackageImportData[] = [{ - packageKey: "key-1", - importedVersions: [{ - oldVersion: "1.0.2", - newVersion: "1.0.0" - }] - }]; - - mockAxiosPost("https://myTeam.celonis.cloud/package-manager/api/core/packages/import/batch", importResponse); - - await new ConfigCommand().batchImportPackages("./export_file.zip", true); - - const expectedFileName = testTransport.logMessages[0].message.split(LOG_MESSAGE)[1]; - expect(mockWriteFileSync).toHaveBeenCalledWith(path.resolve(process.cwd(), expectedFileName), JSON.stringify(importResponse), {encoding: "utf-8"}); - }) - - it("Should fail to map space ID as the space id specified in manifest file cannot be found", async () => { - const manifest: PackageManifestTransport[] = []; - manifest.push(ConfigUtils.buildManifestForKeyAndFlavor("key-2", "STUDIO")); - - const studioManifest: StudioPackageManifest[] = []; - studioManifest.push(ConfigUtils.buildStudioManifestForKeyWithSpace("key-2", "spaceName", "space")); - - const firstPackageNode = ConfigUtils.buildPackageNode("key-2", {variables: []}); - const firstPackageZip = ConfigUtils.buildExportPackageZip(firstPackageNode, [], "1.0.0"); - const exportedPackagesZip = ConfigUtils.buildBatchExportZipWithStudioManifest(manifest, studioManifest,[firstPackageZip]); - - const space: SpaceTransport = { - id: "space-id", - name: "space", - iconReference: "earth" - }; - - mockReadFileSync(exportedPackagesZip.toBuffer()); - mockCreateReadStream(exportedPackagesZip.toBuffer()); - mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/packages", []); - mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/spaces", [space]); - - await expect( - new ConfigCommand().batchImportPackages("./export_file.zip", true) - ).rejects.toThrow("Provided space ID does not exist."); - }) - - it("Should batch import configs & map space ID as specified in manifest file for Studio Packages & move to space for existing packages.", async () => { - const manifest: PackageManifestTransport[] = []; - manifest.push(ConfigUtils.buildManifestForKeyAndFlavor("key-2", "STUDIO")); - - const studioManifest: StudioPackageManifest[] = []; - studioManifest.push(ConfigUtils.buildStudioManifestForKeyWithSpace("key-2", "space", "spaceId")); - - const firstPackageNode = ConfigUtils.buildPackageNode("key-2", {variables: []}); - const firstPackageZip = ConfigUtils.buildExportPackageZip(firstPackageNode, [], "1.0.0"); - const exportedPackagesZip = ConfigUtils.buildBatchExportZipWithStudioManifest(manifest, studioManifest,[firstPackageZip]); - - - const existingNode: Partial = { - id: "node-id", - key: "key-2" - } - - const space: SpaceTransport = { - id: "spaceId", - name: "space", - iconReference: "earth" - }; - - mockReadFileSync(exportedPackagesZip.toBuffer()); - mockCreateReadStream(exportedPackagesZip.toBuffer()); - mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/packages", [existingNode]); - mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/spaces", [space]); - mockAxiosPut("https://myTeam.celonis.cloud/package-manager/api/packages/node-id/move/spaceId", {}); - - const importResponse: PostPackageImportData[] = [{ - packageKey: "key-1", - importedVersions: [{ - oldVersion: "1.0.2", - newVersion: "1.0.0" - }] - }]; - - mockAxiosPost("https://myTeam.celonis.cloud/package-manager/api/core/packages/import/batch", importResponse); - const movePackageToSpaceSpy = jest.spyOn(packageApi, "movePackageToSpace"); - - await new ConfigCommand().batchImportPackages("./export_file.zip", true); - const expectedFileName = testTransport.logMessages[0].message.split(LOG_MESSAGE)[1]; - expect(mockWriteFileSync).toHaveBeenCalledWith(path.resolve(process.cwd(), expectedFileName), JSON.stringify(importResponse), {encoding: "utf-8"}); - expect(movePackageToSpaceSpy).toBeCalledTimes(1) - expect(movePackageToSpaceSpy).toHaveBeenCalledWith("node-id", "spaceId"); - }) - - it("Should batch import configs & map space ID by finding space with name as specified in manifest file", async () => { - const manifest: PackageManifestTransport[] = []; - manifest.push(ConfigUtils.buildManifestForKeyAndFlavor("key-2", "STUDIO")); - - const studioManifest: StudioPackageManifest[] = []; - studioManifest.push(ConfigUtils.buildStudioManifestForKeyWithSpace("key-2", "spaceName", null)); - - const firstPackageNode = ConfigUtils.buildPackageNode("key-2", {variables: []}); - const firstPackageZip = ConfigUtils.buildExportPackageZip(firstPackageNode, [], "1.0.0"); - const exportedPackagesZip = ConfigUtils.buildBatchExportZipWithStudioManifest(manifest, studioManifest,[firstPackageZip]); - - const space: SpaceTransport = { - id: "spaceId", - name: "spaceName", - iconReference: "earth" - }; - mockReadFileSync(exportedPackagesZip.toBuffer()); - mockCreateReadStream(exportedPackagesZip.toBuffer()); - mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/packages", []); - mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/spaces", [space]); - - const importResponse: PostPackageImportData[] = [{ - packageKey: "key-1", - importedVersions: [{ - oldVersion: "1.0.2", - newVersion: "1.0.0" - }] - }]; - - mockAxiosPost("https://myTeam.celonis.cloud/package-manager/api/core/packages/import/batch", importResponse); - const createSpaceSpy = jest.spyOn(spaceApi, "createSpace"); - - await new ConfigCommand().batchImportPackages("./export_file.zip", true); - - const expectedFileName = testTransport.logMessages[0].message.split(LOG_MESSAGE)[1]; - expect(mockWriteFileSync).toHaveBeenCalledWith(path.resolve(process.cwd(), expectedFileName), JSON.stringify(importResponse), {encoding: "utf-8"}); - expect(createSpaceSpy).toBeCalledTimes(0) - }) - - it("Should batch import configs & create new space", async () => { - const manifest: PackageManifestTransport[] = []; - manifest.push(ConfigUtils.buildManifestForKeyAndFlavor("key-2", "STUDIO")); - - const studioManifest: StudioPackageManifest[] = []; - studioManifest.push(ConfigUtils.buildStudioManifestForKeyWithSpace("key-2", "otherName", null)); - - const firstPackageNode = ConfigUtils.buildPackageNode("key-2", {variables: []}); - const firstPackageZip = ConfigUtils.buildExportPackageZip(firstPackageNode, [], "1.0.0"); - const exportedPackagesZip = ConfigUtils.buildBatchExportZipWithStudioManifest(manifest, studioManifest,[firstPackageZip]); - - const space: SpaceTransport = { - id: "space-id", - name: "space-name", - iconReference: "earth" - }; - - const newSpace: SpaceTransport = { - id: "otherId", - name: "otherName", - iconReference: "earth" - }; - - mockReadFileSync(exportedPackagesZip.toBuffer()); - mockCreateReadStream(exportedPackagesZip.toBuffer()); - mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/packages", []); - mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/spaces", [space]); - mockAxiosPost("https://myTeam.celonis.cloud/package-manager/api/spaces", [newSpace]); - - - const importResponse: PostPackageImportData[] = [{ - packageKey: "key-1", - importedVersions: [{ - oldVersion: "1.0.2", - newVersion: "1.0.0" - }] - }]; - - mockAxiosPost("https://myTeam.celonis.cloud/package-manager/api/core/packages/import/batch", importResponse); - const createSpaceSpy = jest.spyOn(spaceApi, "createSpace"); - - await new ConfigCommand().batchImportPackages("./export_file.zip", true); - - const expectedFileName = testTransport.logMessages[0].message.split(LOG_MESSAGE)[1]; - expect(mockWriteFileSync).toHaveBeenCalledWith(path.resolve(process.cwd(), expectedFileName), JSON.stringify(importResponse), {encoding: "utf-8"}); - expect(createSpaceSpy).toBeCalledTimes(1) - }) - - it("Should assign studio runtime variable values after import", async () => { - const manifest: PackageManifestTransport[] = []; - manifest.push(ConfigUtils.buildManifestForKeyAndFlavor("key-1", "STUDIO")); - const exportedPackagesZip = ConfigUtils.buildBatchExportZip(manifest, []); - const variableAssignment: VariablesAssignments = { - key: "variable-1", - type: PackageManagerVariableType.PLAIN_TEXT, - value: "some-value" as unknown as object - } - const studioManifest: StudioPackageManifest[] = [{ - packageKey: "key-1", - space: { - id: "space-id", - name: "space", - iconReference: "earth" - }, - runtimeVariableAssignments: [variableAssignment] - }]; - exportedPackagesZip.addFile(BatchExportImportConstants.STUDIO_FILE_NAME, Buffer.from(stringify(studioManifest))); - - mockReadFileSync(exportedPackagesZip.toBuffer()); - mockCreateReadStream(exportedPackagesZip.toBuffer()); - - const importResponse: PostPackageImportData[] = [{ - packageKey: "key-1", - importedVersions: [{ - oldVersion: "1.0.2", - newVersion: "1.0.0" - }] - }]; - - const node: Partial = { - id: "node-id", - key: "key-1" - } - - const space: SpaceTransport = { - id: "space-id", - name: "space", - iconReference: "earth" - }; - - const assignVariablesUrl = "https://myTeam.celonis.cloud/package-manager/api/nodes/by-package-key/key-1/variables/values"; - - mockAxiosPost("https://myTeam.celonis.cloud/package-manager/api/core/packages/import/batch", importResponse); - mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/nodes/key-1/key-1", node); - mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/spaces", [space]); - mockAxiosPut("https://myTeam.celonis.cloud/package-manager/api/packages/node-id/move/space-id", {}); - mockAxiosPost(assignVariablesUrl, {}); - mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/packages", [node]); - - await new ConfigCommand().batchImportPackages("./export_file.zip", true); - - const expectedFileName = testTransport.logMessages[0].message.split(LOG_MESSAGE)[1]; - expect(mockWriteFileSync).toHaveBeenCalledWith(path.resolve(process.cwd(), expectedFileName), JSON.stringify(importResponse), {encoding: "utf-8"}); - - expect(mockedPostRequestBodyByUrl.get(assignVariablesUrl)).toEqual(JSON.stringify([variableAssignment])); }) -}) diff --git a/tests/config/config-list-variables.spec.ts b/tests/config/config-list-variables.spec.ts deleted file mode 100644 index a63c87bf..00000000 --- a/tests/config/config-list-variables.spec.ts +++ /dev/null @@ -1,173 +0,0 @@ -import {ConfigCommand} from "../../src/commands/config.command"; -import {mockWriteFileSync, testTransport} from "../jest.setup"; -import {FileService} from "../../src/services/file-service"; -import * as path from "path"; -import {PackageKeyAndVersionPair, VariableManifestTransport} from "../../src/interfaces/package-export-transport"; -import {PackageManagerVariableType} from "../../src/interfaces/package-manager.interfaces"; -import {mockAxiosPost, mockedPostRequestBodyByUrl} from "../utls/http-requests-mock"; -import {parse} from "../../src/util/json"; -import * as fs from "fs"; - -describe("Config listVariables", () => { - - const firstManifest: VariableManifestTransport = { - packageKey: "key-1", - version: "1.0.0", - variables: [ - { - key: "key1-var", - type: PackageManagerVariableType.DATA_MODEL, - value: "dm-id" as unknown as object, - metadata: {} - }, - { - key: "key-1-connection", - type: PackageManagerVariableType.CONNECTION, - value: { - appName: "celonis", - connectionId: "connection-id" - } as unknown as object, - metadata: null - } - ] - }; - - const secondManifest: VariableManifestTransport = { - packageKey: "key-2", - version: "1.0.0", - variables: [ - { - key: "key2-var", - type: PackageManagerVariableType.CONNECTION, - value: { - appName: "celonis", - connectionId: "connection-id" - } as unknown as object, - metadata: { - appName: "celonis" - } - } - ] - }; - - const thirdManifest: VariableManifestTransport = { - packageKey: "key-3", - version: "1.0.0", - variables: [ - { - key: "key2-var", - type: PackageManagerVariableType.CONNECTION, - value: "connection-id", - metadata: { - appName: "celonis" - } - } - ] - } - - const fixedVariableManifests: VariableManifestTransport[] = [ - { - ...firstManifest, - variables: [ - { - ...firstManifest.variables[0] - }, - { - ...firstManifest.variables[1], - metadata: { - appName: "celonis" - } - } - ] - }, - { - ...secondManifest - }, - { - ...thirdManifest - } - ]; - - const packageKeyAndVersionPairs: PackageKeyAndVersionPair[] = [ - { - packageKey: "key-1", - version: "1.0.0" - }, - { - packageKey: "key-2", - version: "1.0.0" - }, - { - packageKey: "key-3", - version: "1.0.0" - } - ]; - - beforeEach(() => { - mockAxiosPost("https://myTeam.celonis.cloud/package-manager/api/core/packages/export/batch/variables-with-assignments", [{...firstManifest}, {...secondManifest}, {...thirdManifest}]); - }) - - it("Should list fixed variables for non-json response", async () => { - await new ConfigCommand().listVariables(false, ["key-1:1.0.0", "key-2:1.0.0", "key-3:1.0.0"], null); - - expect(testTransport.logMessages.length).toBe(3); - expect(testTransport.logMessages[0].message).toContain(JSON.stringify(fixedVariableManifests[0])); - expect(testTransport.logMessages[1].message).toContain(JSON.stringify(fixedVariableManifests[1])); - expect(testTransport.logMessages[2].message).toContain(JSON.stringify(fixedVariableManifests[2])); - - const variableExportRequest = parse(mockedPostRequestBodyByUrl.get("https://myTeam.celonis.cloud/package-manager/api/core/packages/export/batch/variables-with-assignments")); - expect(variableExportRequest).toEqual(packageKeyAndVersionPairs); - }) - - it("Should export fixed variables for json response", async () => { - await new ConfigCommand().listVariables(true, ["key-1:1.0.0", "key-2:1.0.0", "key-3:1.0.0"], null); - - expect(testTransport.logMessages.length).toBe(1); - expect(testTransport.logMessages[0].message).toContain(FileService.fileDownloadedMessage); - - const expectedFileName = testTransport.logMessages[0].message.split(FileService.fileDownloadedMessage)[1]; - expect(mockWriteFileSync).toHaveBeenCalledWith(path.resolve(process.cwd(), expectedFileName), JSON.stringify(fixedVariableManifests), {encoding: "utf-8"}); - - const variableExportRequest = parse(mockedPostRequestBodyByUrl.get("https://myTeam.celonis.cloud/package-manager/api/core/packages/export/batch/variables-with-assignments")); - expect(variableExportRequest).toEqual(packageKeyAndVersionPairs); - }) - - it("Should list fixed variables for non-json response and keysByVersion file mapping", async () => { - (fs.existsSync as jest.Mock).mockReturnValue(true); - (fs.readFileSync as jest.Mock).mockReturnValue(JSON.stringify(packageKeyAndVersionPairs)); - - await new ConfigCommand().listVariables(false, [], "key_version_mapping.json"); - - expect(testTransport.logMessages.length).toBe(3); - expect(testTransport.logMessages[0].message).toContain(JSON.stringify(fixedVariableManifests[0])); - expect(testTransport.logMessages[1].message).toContain(JSON.stringify(fixedVariableManifests[1])); - expect(testTransport.logMessages[2].message).toContain(JSON.stringify(fixedVariableManifests[2])); - - const variableExportRequest = parse(mockedPostRequestBodyByUrl.get("https://myTeam.celonis.cloud/package-manager/api/core/packages/export/batch/variables-with-assignments")); - expect(variableExportRequest).toEqual(packageKeyAndVersionPairs); - }) - - it("Should export fixed variables for json response and keysByVersion file mapping", async () => { - (fs.existsSync as jest.Mock).mockReturnValue(true); - (fs.readFileSync as jest.Mock).mockReturnValue(JSON.stringify(packageKeyAndVersionPairs)); - - await new ConfigCommand().listVariables(true, [], "key_version_mapping.json"); - - expect(testTransport.logMessages.length).toBe(1); - expect(testTransport.logMessages[0].message).toContain(FileService.fileDownloadedMessage); - - const expectedFileName = testTransport.logMessages[0].message.split(FileService.fileDownloadedMessage)[1]; - expect(mockWriteFileSync).toHaveBeenCalledWith(path.resolve(process.cwd(), expectedFileName), JSON.stringify(fixedVariableManifests), {encoding: "utf-8"}); - - const variableExportRequest = parse(mockedPostRequestBodyByUrl.get("https://myTeam.celonis.cloud/package-manager/api/core/packages/export/batch/variables-with-assignments")); - expect(variableExportRequest).toEqual(packageKeyAndVersionPairs); - }) - - it("Should throw error if no mapping and no file path is provided", async () => { - try { - await new ConfigCommand().listVariables(true, [], ""); - } catch (e) { - expect(e.message).toEqual("Please provide keysByVersion mappings or file path!"); - } - }) -}) \ No newline at end of file diff --git a/tests/config/config-list.spec.ts b/tests/config/config-list.spec.ts deleted file mode 100644 index d3657f8e..00000000 --- a/tests/config/config-list.spec.ts +++ /dev/null @@ -1,182 +0,0 @@ -import {PackageExportTransport} from "../../src/interfaces/package-export-transport"; -import {ConfigCommand} from "../../src/commands/config.command"; -import {mockWriteFileSync, testTransport} from "../jest.setup"; -import { - ContentNodeTransport, - PackageWithVariableAssignments, - StudioComputeNodeDescriptor -} from "../../src/interfaces/package-manager.interfaces"; -import * as path from "path"; -import {FileService} from "../../src/services/file-service"; -import {mockAxiosGet} from "../utls/http-requests-mock"; -import {PackageManagerApiUtils} from "../utls/package-manager-api.utils"; - -describe("Config list", () => { - - it.each([ - "", - "STUDIO,TEST" - ])( - "Should list all packages by key for non-json response with flavors: %p", - async (flavors: string) => { - const firstPackage = PackageManagerApiUtils.buildPackageExportTransport("key-1", "name-1"); - const secondPackage = PackageManagerApiUtils.buildPackageExportTransport("key-2", "name-2"); - - const flavorsArray = flavors !== "" ? flavors.split(",") : []; - - const urlParams = new URLSearchParams(); - urlParams.set("withDependencies", "false"); - flavorsArray.forEach(flavor => urlParams.append("flavors", flavor)); - - mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/core/packages/export/list?" + urlParams.toString(), [firstPackage, secondPackage]); - - await new ConfigCommand().listActivePackages(false, flavorsArray, false, []); - - expect(testTransport.logMessages.length).toBe(2); - expect(testTransport.logMessages[0].message).toContain(`${firstPackage.name} - Key: "${firstPackage.key}"`); - expect(testTransport.logMessages[1].message).toContain(`${secondPackage.name} - Key: "${secondPackage.key}"`); - } - ) - - it("Should export all packages for json response with spaceId set for studio packages", async () => { - const firstPackage = PackageManagerApiUtils.buildPackageExportTransport("key-1", "name-1"); - const secondPackage = PackageManagerApiUtils.buildPackageExportTransport("key-2", "name-2"); - - const studioPackage: ContentNodeTransport = PackageManagerApiUtils.buildContentNodeTransport("key-1", "spaceId-1"); - - mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/core/packages/export/list?withDependencies=false", [{...firstPackage}, {...secondPackage}]); - mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/packages/with-variable-assignments?type=DATA_MODEL", [studioPackage]); - - await new ConfigCommand().listActivePackages(true, [], false, []); - - const expectedFileName = testTransport.logMessages[0].message.split(FileService.fileDownloadedMessage)[1]; - - expect(mockWriteFileSync).toHaveBeenCalledWith(path.resolve(process.cwd(), expectedFileName), expect.any(String), {encoding: "utf-8"}); - - const exportedTransports = JSON.parse(mockWriteFileSync.mock.calls[0][1]) as PackageExportTransport[]; - expect(exportedTransports.length).toBe(2); - - const exportedFirstPackage = exportedTransports.filter(transport => transport.key === firstPackage.key)[0]; - const exportedSecondPackage = exportedTransports.filter(transport => transport.key === secondPackage.key)[0]; - - expect(exportedSecondPackage).toEqual(secondPackage); - expect(exportedFirstPackage).toEqual({...firstPackage, spaceId: "spaceId-1"}); - }) - - it("Should export all packages with dependencies and data models set for json response and withDependencies option", async () => { - const firstPackage = PackageManagerApiUtils.buildPackageExportTransport("key-1", "name-1"); - const secondPackage = PackageManagerApiUtils.buildPackageExportTransport("key-2", "name-2"); - - mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/core/packages/export/list?withDependencies=true", [{...firstPackage}, {...secondPackage}]); - mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/packages/with-variable-assignments?type=DATA_MODEL", []); - - const dataModelVariableAssignmentResponse: PackageWithVariableAssignments = { - id: "var-id", - key: firstPackage.key, - name: "var-name", - createdBy: "", - spaceId: undefined, - variableAssignments: [ - { - key: "var-key", - value: "datamodel-id" as unknown as object, - type: "DATA_MODEL" - } - ] - }; - mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/packages/with-variable-assignments?type=DATA_MODEL", [dataModelVariableAssignmentResponse]); - - const dataModelDetailResponse: StudioComputeNodeDescriptor = { - name: "pool-name", - dataModelId: "datamodel-id", - poolId: "pool-id" - }; - mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/compute-pools/data-models/details", [dataModelDetailResponse]); - - await new ConfigCommand().listActivePackages(true, [], true, []); - - const expectedFileName = testTransport.logMessages[0].message.split(FileService.fileDownloadedMessage)[1]; - - expect(mockWriteFileSync).toHaveBeenCalledWith(path.resolve(process.cwd(), expectedFileName), expect.any(String), {encoding: "utf-8"}); - - const exportedTransports = JSON.parse(mockWriteFileSync.mock.calls[0][1]) as PackageExportTransport[]; - expect(exportedTransports.length).toBe(2); - - const exportedFirstPackage = exportedTransports.filter(transport => transport.key === firstPackage.key)[0]; - const exportedSecondPackage = exportedTransports.filter(transport => transport.key === secondPackage.key)[0]; - - expect(exportedSecondPackage).toEqual(secondPackage); - expect(exportedFirstPackage).toEqual({...firstPackage, datamodels: [{...dataModelDetailResponse}]}); - }) - - it("Should export packagesByKeys with spaceId set for studio packages", async () => { - const firstPackage = PackageManagerApiUtils.buildPackageExportTransport("key-1", "name-1"); - const secondPackage = PackageManagerApiUtils.buildPackageExportTransport("key-2", "name-2"); - - const studioPackage: ContentNodeTransport = PackageManagerApiUtils.buildContentNodeTransport("key-1", "spaceId-1"); - - mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/core/packages/export/list-by-keys?packageKeys=key-1&packageKeys=key-2&withDependencies=false", [{...firstPackage}, {...secondPackage}]); - mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/packages/with-variable-assignments?type=DATA_MODEL", [studioPackage]); - - await new ConfigCommand().listActivePackages(true, [], false, [firstPackage.key, secondPackage.key]); - - const expectedFileName = testTransport.logMessages[0].message.split(FileService.fileDownloadedMessage)[1]; - - expect(mockWriteFileSync).toHaveBeenCalledWith(path.resolve(process.cwd(), expectedFileName), expect.any(String), {encoding: "utf-8"}); - - const exportedTransports = JSON.parse(mockWriteFileSync.mock.calls[0][1]) as PackageExportTransport[]; - expect(exportedTransports.length).toBe(2); - - const exportedFirstPackage = exportedTransports.filter(transport => transport.key === firstPackage.key)[0]; - const exportedSecondPackage = exportedTransports.filter(transport => transport.key === secondPackage.key)[0]; - - expect(exportedSecondPackage).toEqual(secondPackage); - expect(exportedFirstPackage).toEqual({...firstPackage, spaceId: "spaceId-1"}); - }) - - it("Should export packagesByKeys with dependencies and data models set for withDependencies option", async () => { - const firstPackage = PackageManagerApiUtils.buildPackageExportTransport("key-1", "name-1"); - const secondPackage = PackageManagerApiUtils.buildPackageExportTransport("key-2", "name-2"); - - mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/core/packages/export/list-by-keys?packageKeys=key-1&packageKeys=key-2&withDependencies=true", [{...firstPackage}, {...secondPackage}]); - mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/packages/with-variable-assignments?type=DATA_MODEL", []); - - const dataModelVariableAssignmentResponse: PackageWithVariableAssignments = { - id: "var-id", - key: firstPackage.key, - name: "var-name", - createdBy: "", - spaceId: undefined, - variableAssignments: [ - { - key: "var-key", - value: "datamodel-id" as unknown as object, - type: "DATA_MODEL" - } - ] - }; - mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/packages/with-variable-assignments?type=DATA_MODEL", [dataModelVariableAssignmentResponse]); - - const dataModelDetailResponse: StudioComputeNodeDescriptor = { - name: "pool-name", - dataModelId: "datamodel-id", - poolId: "pool-id" - }; - mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/compute-pools/data-models/details", [dataModelDetailResponse]); - - await new ConfigCommand().listActivePackages(true, [], true, [firstPackage.key, secondPackage.key]); - - const expectedFileName = testTransport.logMessages[0].message.split(FileService.fileDownloadedMessage)[1]; - - expect(mockWriteFileSync).toHaveBeenCalledWith(path.resolve(process.cwd(), expectedFileName), expect.any(String), {encoding: "utf-8"}); - - const exportedTransports = JSON.parse(mockWriteFileSync.mock.calls[0][1]) as PackageExportTransport[]; - expect(exportedTransports.length).toBe(2); - - const exportedFirstPackage = exportedTransports.filter(transport => transport.key === firstPackage.key)[0]; - const exportedSecondPackage = exportedTransports.filter(transport => transport.key === secondPackage.key)[0]; - - expect(exportedSecondPackage).toEqual(secondPackage); - expect(exportedFirstPackage).toEqual({...firstPackage, datamodels: [{...dataModelDetailResponse}]}); - }) -}) \ No newline at end of file diff --git a/tests/export/action-flows.spec.ts b/tests/export/action-flows.spec.ts deleted file mode 100644 index f0503919..00000000 --- a/tests/export/action-flows.spec.ts +++ /dev/null @@ -1,181 +0,0 @@ -import { FileService } from "../../src/services/file-service"; -import { mockWriteSync, testTransport } from "../jest.setup"; -import { mockAxiosGet } from "../utls/http-requests-mock"; -import { ActionFlowCommand } from "../../src/commands/action-flow.command"; -import * as AdmZip from "adm-zip"; -import { mockExistsSyncOnce, mockReadFileSync } from "../utls/fs-mock-utils"; -import { stringify } from "../../src/util/yaml"; -import * as fs from "fs"; -import { parse } from "yaml"; -import { metadataFileName } from "../../src/services/action-flow/action-flow-service"; - -describe("Export action-flows", () => { - - const packageId = "123-456-789"; - const actionFlowFileName = "20240711-scenario-1234.json"; - const actionFlowConfig = { - "name": "T2T - simple package Automation", - "flow": [ - { - "id": 6, - "module": "util:FunctionSleep", - "version": 1, - "parameters": {}, - "metadata": { - "expect": [ - { - "name": "duration", - "type": "uinteger", - "label": "Delay", - "required": true, - "validate": { - "max": 300, - "min": 1, - }, - }, - ], - "restore": {}, - "designer": { - "x": 300, - "y": 0, - }, - }, - "mapper": { - "duration": "1", - }, - }, - ], - "metadata": { - "instant": false, - "version": 1, - "designer": { - "orphans": [], - }, - "scenario": { - "dlq": false, - "dataloss": false, - "maxErrors": 3, - "autoCommit": true, - "roundtrips": 1, - "sequential": false, - "confidential": false, - "freshVariables": false, - "autoCommitTriggerLast": true, - }, - }, - "io": { - "output_spec": [], - "input_spec": [], - }, - }; - - beforeEach(() => { - (fs.openSync as jest.Mock).mockReturnValue(100); - }); - - it("Should call export API and return the zip response", async () => { - const zipExport = new AdmZip(); - zipExport.addFile(actionFlowFileName, Buffer.from(stringify(actionFlowConfig))); - - mockAxiosGet(`https://myTeam.celonis.cloud/ems-automation/api/root/${packageId}/export/assets`, zipExport.toBuffer()); - - await new ActionFlowCommand().exportActionFlows(packageId, null); - - expect(testTransport.logMessages.length).toBe(1); - const expectedZipFileName = testTransport.logMessages[0].message.split(FileService.fileDownloadedMessage)[1]; - expect(fs.openSync).toHaveBeenCalledWith(expectedZipFileName, expect.anything(), expect.anything()); - expect(mockWriteSync).toHaveBeenCalled(); - - const fileBuffer = mockWriteSync.mock.calls[0][1]; - const receivedZip = new AdmZip(fileBuffer); - - expect(receivedZip.getEntries().length).toBe(1); - const receivedZipEntry = receivedZip.getEntries()[0]; - const receivedActionFlowConfig = parse(receivedZipEntry.getData().toString()); - expect(receivedZipEntry.name).toEqual(actionFlowFileName); - expect(receivedActionFlowConfig).toEqual(actionFlowConfig); - }); - - it("Should call export API and attach the provided file to the zip response", async () => { - const metadata = { - "actionFlows": [ - { - "key": "123_asset_key", - "rootNodeKey": "543_root_key", - "parentNodeKey": "9099_parent_key", - "name": "T2T - simple package Automation", - "scenarioId": "1234", - "webHookUrl": null, - "version": "10", - "sensorType": null, - "schedule": { - "type": "indefinitely", - "interval": 900, - }, - "teamSpecific": { - "connections": [], - "variables": [], - "celonisApps": [], - "callingOtherAf": [], - "datastructures": [], - }, - }, - ], - "connections": [], - "dataPools": [], - "dataModels": [], - "skills": [], - "analyses": [], - "datastructures": [], - "mappings": [], - "actionFlowsTeamId": "555", - }; - - mockExistsSyncOnce(); - mockReadFileSync(stringify(metadata)); - const zipExport = new AdmZip(); - zipExport.addFile(actionFlowFileName, Buffer.from(stringify(actionFlowConfig))); - - mockAxiosGet(`https://myTeam.celonis.cloud/ems-automation/api/root/${packageId}/export/assets`, zipExport.toBuffer()); - await new ActionFlowCommand().exportActionFlows(packageId, metadataFileName); - - expect(testTransport.logMessages.length).toBe(1); - const expectedZipFileName = testTransport.logMessages[0].message.split(FileService.fileDownloadedMessage)[1]; - expect(fs.openSync).toHaveBeenCalledWith(expectedZipFileName, expect.anything(), expect.anything()); - expect(mockWriteSync).toHaveBeenCalled(); - - const fileBuffer = mockWriteSync.mock.calls[0][1]; - const receivedZip = new AdmZip(fileBuffer); - - expect(receivedZip.getEntries().length).toBe(2); - expect(receivedZip.getEntries().filter(entry => entry.name === metadataFileName).length).toBe(1); - - const receivedMetadataZipEntry = receivedZip.getEntries().filter(entry => entry.name === metadataFileName)[0]; - const receivedActionFlowZipEntry = receivedZip.getEntries().filter(entry => entry.name !== metadataFileName)[0]; - const receivedMetadataFile = parse(receivedMetadataZipEntry.getData().toString()); - const receivedActionFlowFile = parse(receivedActionFlowZipEntry.getData().toString()); - - expect(receivedActionFlowZipEntry.name).toEqual(actionFlowFileName); - expect(receivedActionFlowFile).toEqual(actionFlowConfig); - expect(receivedMetadataFile).toEqual(metadata); - }); - - it("Should throw error if metadata files does not exist", async () => { - const zipExport = new AdmZip(); - zipExport.addFile(actionFlowFileName, Buffer.from(stringify(actionFlowConfig))); - - mockAxiosGet(`https://myTeam.celonis.cloud/ems-automation/api/root/${packageId}/export/assets`, zipExport.toBuffer()); - - const error = new Error("mock exit"); - jest.spyOn(process, "exit").mockImplementation(() => { - throw error; - }); - - try { - await new ActionFlowCommand().exportActionFlows(packageId, metadataFileName); - } catch (e) { - expect(e).toBe(error); - expect(process.exit).toHaveBeenCalledWith(1); - } - }); -}); \ No newline at end of file diff --git a/tests/import/action-flows.spec.ts b/tests/import/action-flows.spec.ts deleted file mode 100644 index 3dea2474..00000000 --- a/tests/import/action-flows.spec.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { FileService } from "../../src/services/file-service"; -import * as path from "path"; -import { mockWriteFileSync, testTransport } from "../jest.setup"; -import { mockedAxiosInstance } from "../utls/http-requests-mock"; -import { ActionFlowCommand } from "../../src/commands/action-flow.command"; -import * as AdmZip from "adm-zip"; -import { mockCreateReadStream } from "../utls/fs-mock-utils"; - -describe("Import action-flows", () => { - - const packageId = "123-456-789"; - const mockImportResponse = { - "status": "SUCCESS", - "eventLog": [ - { - "status": "SUCCESS", - "assetType": "SCENARIO", - "assetId": "asset-id-automation", - "eventType": "IMPORT", - "log": "updated action flow with key [asset-id-automation]", - "mapping": null, - }, - ], - }; - - it("Should call import API and return non-json response", async () => { - const resp = { data: mockImportResponse }; - (mockedAxiosInstance.post as jest.Mock).mockResolvedValue(resp); - const zip = new AdmZip(); - mockCreateReadStream(zip.toBuffer()); - - await new ActionFlowCommand().importActionFlows(packageId, "tmp", true, false); - - expect(testTransport.logMessages.length).toBe(1); - expect(testTransport.logMessages[0].message).toContain(JSON.stringify(mockImportResponse, null, 4)); - - expect(mockedAxiosInstance.post).toHaveBeenCalledWith(`https://myTeam.celonis.cloud/ems-automation/api/root/${packageId}/import/assets`, expect.anything(), expect.anything()); - }); - - it("Should call import API and return json response", async () => { - const resp = { data: mockImportResponse }; - (mockedAxiosInstance.post as jest.Mock).mockResolvedValue(resp); - const zip = new AdmZip(); - mockCreateReadStream(zip.toBuffer()); - - await new ActionFlowCommand().importActionFlows(packageId, "tmp", true, true); - - expect(testTransport.logMessages.length).toBe(1); - expect(testTransport.logMessages[0].message).toContain(FileService.fileDownloadedMessage); - const expectedFileName = testTransport.logMessages[0].message.split(FileService.fileDownloadedMessage)[1]; - - expect(mockWriteFileSync).toHaveBeenCalledWith(path.resolve(process.cwd(), expectedFileName), JSON.stringify(mockImportResponse, null, 4), { encoding: "utf-8" }); - expect(mockedAxiosInstance.post).toHaveBeenCalledWith(`https://myTeam.celonis.cloud/ems-automation/api/root/${packageId}/import/assets`, expect.anything(), expect.anything()); - }); -}); \ No newline at end of file diff --git a/tests/jest.setup.ts b/tests/jest.setup.ts deleted file mode 100644 index 722b8fb5..00000000 --- a/tests/jest.setup.ts +++ /dev/null @@ -1,33 +0,0 @@ -import * as fs from 'fs'; -import {setDefaultProfile} from "./utls/context-mock"; -import {TestTransport} from "./utls/test-transport"; -import {logger} from "../src/util/logger"; -import { mockAxios } from "./utls/http-requests-mock"; - -// Mock the modules using Jest -mockAxios(); -jest.mock('fs'); - -const mockWriteFileSync = jest.fn(); -(fs.writeFileSync as jest.Mock).mockImplementation(mockWriteFileSync); - -const mockWriteSync = jest.fn(); -(fs.writeSync as jest.Mock).mockImplementation(mockWriteSync); - -afterEach(() => { - jest.clearAllMocks(); -}) - -beforeAll(() => { - setDefaultProfile(); -}) - -let testTransport; - -beforeEach(() => { - jest.clearAllMocks(); - testTransport = new TestTransport({}) - logger.add(testTransport); -}) - -export {testTransport, mockWriteFileSync, mockWriteSync}; \ No newline at end of file diff --git a/tests/list/assignments.spec.ts b/tests/list/assignments.spec.ts deleted file mode 100644 index f62a3998..00000000 --- a/tests/list/assignments.spec.ts +++ /dev/null @@ -1,65 +0,0 @@ -import {VariableCommand} from "../../src/commands/variable.command"; -import {FileService} from "../../src/services/file-service"; -import * as path from "path"; -import {mockWriteFileSync, testTransport} from "../jest.setup"; -import { mockedAxiosInstance } from "../utls/http-requests-mock"; - -describe("List assignments", () => { - - it("Should list assignments for supported type and non-json response", async () => { - const mockAssignmentValues = [ - {id: "id-1"}, - {id: "id-2"} - ]; - const resp = {data: mockAssignmentValues}; - (mockedAxiosInstance.get as jest.Mock).mockResolvedValue(resp); - - await new VariableCommand().listAssignments("DATA_MODEL", false, ""); - - expect(testTransport.logMessages.length).toBe(2); - expect(testTransport.logMessages[0].message).toContain('{"id":"id-1"}'); - expect(testTransport.logMessages[1].message).toContain('{"id":"id-2"}'); - - expect(mockedAxiosInstance.get).toHaveBeenCalledWith("https://myTeam.celonis.cloud/package-manager/api/compute-pools/pools-with-data-models", expect.anything()) - }) - - it("Should export assignments for supported type and json response", async () => { - const mockAssignmentValues = [ - {id: "id-1"}, - {id: "id-2"} - ]; - const resp = {data: mockAssignmentValues}; - (mockedAxiosInstance.get as jest.Mock).mockResolvedValue(resp); - - await new VariableCommand().listAssignments("DATA_MODEL", true, ""); - - expect(testTransport.logMessages.length).toBe(1); - expect(testTransport.logMessages[0].message).toContain(FileService.fileDownloadedMessage); - - const expectedFileName = testTransport.logMessages[0].message.split(FileService.fileDownloadedMessage)[1]; - - expect(mockWriteFileSync).toHaveBeenCalledWith(path.resolve(process.cwd(), expectedFileName), JSON.stringify(mockAssignmentValues), {encoding: "utf-8"}); - }) - - it("Should contain url params in the url", async () => { - const mockAssignmentValues = [{id: "id-1"}]; - const resp = {data: mockAssignmentValues}; - (mockedAxiosInstance.get as jest.Mock).mockResolvedValue(resp); - - await new VariableCommand().listAssignments("CONNECTION", false, "param1=value1,param2=value2"); - - expect(mockedAxiosInstance.get).toHaveBeenCalledWith("https://myTeam.celonis.cloud/process-automation-v2/api/connections?param1=value1¶m2=value2", expect.anything()) - }) - - it("Should throw error for unsupported variable types", async () => { - const type: string = "DUMMY_UNSUPPORTED_TYPE"; - - try { - await new VariableCommand().listAssignments(type, false, ""); - } catch (e) { - if (!(e.message === `Variable type ${type} not supported.`)) { - fail(); - } - } - }) -}) \ No newline at end of file diff --git a/tests/mocks/package.json b/tests/mocks/package.json deleted file mode 100644 index 525ca75b..00000000 --- a/tests/mocks/package.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "version": "mocked-version" -} \ No newline at end of file diff --git a/tests/utls/config-utils.ts b/tests/utls/config-utils.ts deleted file mode 100644 index e7962788..00000000 --- a/tests/utls/config-utils.ts +++ /dev/null @@ -1,107 +0,0 @@ -import AdmZip = require("adm-zip"); -import { - DependencyTransport, NodeConfiguration, - NodeExportTransport, - PackageManifestTransport, StudioPackageManifest, -} from "../../src/interfaces/package-export-transport"; -import {stringify} from "../../src/util/json"; -import {SpaceTransport} from "../../src/interfaces/save-space.interface"; - -export class ConfigUtils { - - public static buildBatchExportZipWithStudioManifest(manifest: PackageManifestTransport[], studioManifest: StudioPackageManifest[], packageZips: AdmZip[]): AdmZip { - - const zipExport = new AdmZip(); - zipExport.addFile("manifest.json", Buffer.from(stringify(manifest))); - zipExport.addFile("studio.json", Buffer.from(stringify(studioManifest))); - packageZips.forEach(packageZip => { - const fileName = `${packageZip.getZipComment()}.zip` - packageZip.addZipComment("") - zipExport.addFile(fileName, packageZip.toBuffer()); - }) - - return zipExport; - } - public static buildBatchExportZip(manifest: PackageManifestTransport[], packageZips: AdmZip[]): AdmZip { - - const zipExport = new AdmZip(); - zipExport.addFile("manifest.json", Buffer.from(stringify(manifest))); - packageZips.forEach(packageZip => { - const fileName = `${packageZip.getZipComment()}.zip` - packageZip.addZipComment("") - zipExport.addFile(fileName, packageZip.toBuffer()); - }) - - return zipExport; - } - - public static buildExportPackageZip(packageNode: NodeExportTransport, childNodes: NodeExportTransport[], version: string): AdmZip { - const zipExport = new AdmZip(); - - zipExport.addFile("package.json", Buffer.from(stringify(packageNode))); - zipExport.addFile("nodes/", Buffer.alloc(0)); - - childNodes.forEach(child => { - zipExport.addFile(`nodes/${child.key}.json`, Buffer.from(stringify(child))); - }); - - zipExport.addZipComment(`${packageNode.key}_${version}`); - - return zipExport; - } - - public static buildManifestForKeyAndFlavor(key: string, flavor: string, dependenciesByVersion?: Map): PackageManifestTransport { - return { - packageKey: key, - flavor: flavor, - activeVersion: "", - dependenciesByVersion: dependenciesByVersion ?? {} as Map - }; - } - - public static buildPackageNode(key: string, configuration: NodeConfiguration): NodeExportTransport { - return { - key, - parentNodeKey: key, - name: "name", - type: "PACKAGE", - exportSerializationType: "YAML", - configuration: configuration, - schemaVersion: 1, - invalidContent: false, - serializedDocument: null, - spaceId: null - }; - } - - public static buildChildNode(key: string, parentKey: string, type: string): NodeExportTransport { - return { - key, - parentNodeKey: parentKey, - name: "name", - type: type, - exportSerializationType: "YAML", - configuration: {}, - schemaVersion: 1, - invalidContent: false, - serializedDocument: null, - spaceId: null - }; - } - - public static buildStudioManifestForKeyWithSpace(key: string, spaceName: string, spaceId: string): StudioPackageManifest { - const space: Partial = { - name: spaceName - }; - - if (spaceId) { - space.id = spaceId; - } - - return { - packageKey: key, - space: space, - runtimeVariableAssignments: [] - }; - } -} \ No newline at end of file diff --git a/tests/utls/context-mock.ts b/tests/utls/context-mock.ts deleted file mode 100644 index 378d3276..00000000 --- a/tests/utls/context-mock.ts +++ /dev/null @@ -1,13 +0,0 @@ -import {contextService} from "../../src/services/context.service"; - -export function setDefaultProfile(): void { - contextService.setContext({ - profile: { - name: "test", - type: "Key", - team: "https://myTeam.celonis.cloud/", - apiToken: "YnQ3N2M0M2ItYzQ3OS00YzgyLTg0ODgtOWNkNzhiNzYwOTU2OlFkNnBpVCs0M0JBYm1ZWGlCZ2hPd245aldwWTNubFQyYVFOTFBUeHEwdUxM", - authenticationType: "Bearer" - } - }); -} diff --git a/tests/utls/fs-mock-utils.ts b/tests/utls/fs-mock-utils.ts deleted file mode 100644 index 2c01ceab..00000000 --- a/tests/utls/fs-mock-utils.ts +++ /dev/null @@ -1,21 +0,0 @@ -import * as fs from "fs"; -import {Readable} from "stream"; - -export function mockReadFileSync(data: any): void { - (fs.readFileSync as jest.Mock).mockReturnValue(data); -} - -export function mockCreateReadStream(data: any): void { - const stream = new Readable(); - stream.push(data); - stream.push(null); - (fs.createReadStream as jest.Mock).mockReturnValue(stream); -} - -export function mockExistsSync(): void { - (fs.existsSync as jest.Mock).mockReturnValue(true); -} - -export function mockExistsSyncOnce(): void { - (fs.existsSync as jest.Mock).mockReturnValueOnce(true); -} \ No newline at end of file diff --git a/tests/utls/http-requests-mock.ts b/tests/utls/http-requests-mock.ts deleted file mode 100644 index 3e01d5d5..00000000 --- a/tests/utls/http-requests-mock.ts +++ /dev/null @@ -1,85 +0,0 @@ -import { AxiosInstance } from "axios"; -import {Readable} from "stream"; -import { AxiosInitializer } from "../../src/util/axios-initializer"; - -const mockedAxiosInstance = {} as AxiosInstance; - - -const mockedGetResponseByUrl = new Map(); -const mockedPostResponseByUrl = new Map(); -const mockedPostRequestBodyByUrl = new Map(); - -const mockAxios = () : void => { - AxiosInitializer.initializeAxios = jest.fn().mockReturnValue(mockedAxiosInstance); - - mockedAxiosInstance.get = jest.fn(); - mockedAxiosInstance.post = jest.fn(); - mockedAxiosInstance.put = jest.fn(); -} - -const mockAxiosGet = (url: string, responseData: any) => { - mockedGetResponseByUrl.set(url, responseData); - (mockedAxiosInstance.get as jest.Mock).mockImplementation(requestUrl => { - if (mockedGetResponseByUrl.has(requestUrl)) { - const response = { data: mockedGetResponseByUrl.get(requestUrl) }; - - if (response.data instanceof Buffer) { - const readableStream = new Readable(); - readableStream.push(response.data) - readableStream.push(null); - return Promise.resolve({ - status: 200, - data: readableStream, - }); - } else { - return Promise.resolve(response); - } - } else { - fail("API call not mocked.") - } - }); -}; - -const mockAxiosPost = (url: string, responseData: any) => { - mockedPostResponseByUrl.set(url, responseData); - - (mockedAxiosInstance.post as jest.Mock).mockImplementation((requestUrl: string, data: any) => { - if (mockedPostResponseByUrl.has(requestUrl)) { - const response = { data: mockedPostResponseByUrl.get(requestUrl) }; - mockedPostRequestBodyByUrl.set(requestUrl, data); - - return Promise.resolve(response); - } else { - fail("API call not mocked.") - } - }) -} - -const mockAxiosPut = (url: string, responseData: any) => { - mockedPostResponseByUrl.set(url, responseData); - (mockedAxiosInstance.put as jest.Mock).mockImplementation((requestUrl: string, data: any) => { - if (mockedPostResponseByUrl.has(requestUrl)) { - const response = { data: mockedPostResponseByUrl.get(requestUrl) }; - mockedPostRequestBodyByUrl.set(requestUrl, data); - - return Promise.resolve(response); - } else { - fail("API call not mocked.") - } - }) -} - -afterEach(() => { - mockedGetResponseByUrl.clear(); - mockedPostResponseByUrl.clear(); - mockedPostRequestBodyByUrl.clear(); -}) - -export { - mockedAxiosInstance, - mockAxios, - mockAxiosGet, - mockAxiosPost, - mockAxiosPut, - mockedPostRequestBodyByUrl -}; diff --git a/tests/utls/package-manager-api.utils.ts b/tests/utls/package-manager-api.utils.ts deleted file mode 100644 index 163f3c31..00000000 --- a/tests/utls/package-manager-api.utils.ts +++ /dev/null @@ -1,41 +0,0 @@ -import {PackageExportTransport} from "../../src/interfaces/package-export-transport"; -import {SpaceTransport} from "../../src/interfaces/save-space.interface"; -import {ContentNodeTransport} from "../../src/interfaces/package-manager.interfaces"; - -export class PackageManagerApiUtils { - public static buildPackageExportTransport = (key: string, name: string): PackageExportTransport => { - return { - id: "", - key, - name, - changeDate: null, - activatedDraftId: "", - workingDraftId: "", - flavor: "", - version: "", - dependencies: null, - }; - } - - public static buildContentNodeTransport = (key: string, spaceId: string): ContentNodeTransport => { - return { - id: "", - key, - name: "", - rootNodeKey: "", - workingDraftId: "", - activatedDraftId: "", - rootNodeId: "", - assetMetadataTransport: null, - spaceId - } - } - - public static buildSpaceTransport = (id: string, name: string = "space-name", iconReference: string = "icon"): SpaceTransport => { - return { - id, - name, - iconReference, - }; - } -} \ No newline at end of file diff --git a/tests/utls/test-transport.ts b/tests/utls/test-transport.ts deleted file mode 100644 index a0dee9f2..00000000 --- a/tests/utls/test-transport.ts +++ /dev/null @@ -1,15 +0,0 @@ -import * as Transport from "winston-transport"; -import {LogEntry} from "winston"; - -export class TestTransport extends Transport { - public logMessages: LogEntry[] = []; - - constructor(options: any) { - super(options); - } - - public log(info: LogEntry, callback: () => void): void { - this.logMessages.push(info); - callback(); - } -} \ No newline at end of file From 4351014b4ba534f842b3c41c8fa5e7f7df7a0770 Mon Sep 17 00:00:00 2001 From: zgjimhaziri Date: Wed, 14 May 2025 11:43:46 +0200 Subject: [PATCH 19/45] TA-3749: Trigger build --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5b554afe..5e078070 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -2,7 +2,7 @@ name: Build Workflow on: pull_request: - branches: [ master ] + branches: [ master, content-cli-v2-refactoring ] jobs: build: runs-on: ubuntu-latest From 843f7b59569725d6ca0809a32dd49254a83fb138 Mon Sep 17 00:00:00 2001 From: zgjimhaziri Date: Wed, 14 May 2025 11:45:58 +0200 Subject: [PATCH 20/45] TA-3749: Comment test run --- jest.config.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/jest.config.ts b/jest.config.ts index 6b6f80d3..e3a032a2 100644 --- a/jest.config.ts +++ b/jest.config.ts @@ -9,7 +9,8 @@ const config: Config.InitialOptions = { "^\\./../package.json$": "/tests/mocks/package.json", }, setupFilesAfterEnv: [ - "/tests/jest.setup.ts", + // "/tests/jest.setup.ts", + // Uncomment after setting up tests ], globals: { "ts-jest": { From d88b9a48953f146ce02d28a804a2e40b2bb40e3d Mon Sep 17 00:00:00 2001 From: zgjimhaziri Date: Wed, 14 May 2025 17:05:24 +0200 Subject: [PATCH 21/45] TA-3750: Setup command modules --- .github/CODEOWNERS | 11 +++++++++++ src/commands/action-flows/module.ts | 15 +++++++++++++++ src/commands/analysis/module.ts | 15 +++++++++++++++ src/commands/cpm4/module.ts | 15 +++++++++++++++ src/commands/data-pipeline/module.ts | 15 +++++++++++++++ src/commands/pacman/module.ts | 15 +++++++++++++++ src/commands/studio/module.ts | 15 +++++++++++++++ 7 files changed, 101 insertions(+) create mode 100644 src/commands/action-flows/module.ts create mode 100644 src/commands/analysis/module.ts create mode 100644 src/commands/cpm4/module.ts create mode 100644 src/commands/data-pipeline/module.ts create mode 100644 src/commands/pacman/module.ts create mode 100644 src/commands/studio/module.ts diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 97e79712..814f473f 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1 +1,12 @@ * @celonis/navi @gencblakqorip9 @celonis/astro + +#/* @celonis/astro +#/src @celonis/astro +#/src/core @celonis/astro +#/src/commands/pacman/ @celonis/astro +#/src/commands/profile/ @celonis/astro +#/src/commands/action-flows/ action-flow team +#/src/commands/analysis/ filter and foundation team +#/src/commands/cpm4/ cpm4 team +#/src/commands/data-pipeline/ data-management team +#/src/commands/studio/ @celonis/navi diff --git a/src/commands/action-flows/module.ts b/src/commands/action-flows/module.ts new file mode 100644 index 00000000..32075375 --- /dev/null +++ b/src/commands/action-flows/module.ts @@ -0,0 +1,15 @@ +/** + * Commands related to the Action Flows area. + */ + +import { Configurator, IModule } from "../../core/command/module-handler"; +import { Context } from "../../core/command/cli-context"; + +class Module extends IModule { + + register(context: Context, configurator: Configurator) { + } + +} + +export = Module; \ No newline at end of file diff --git a/src/commands/analysis/module.ts b/src/commands/analysis/module.ts new file mode 100644 index 00000000..aa1d1dc3 --- /dev/null +++ b/src/commands/analysis/module.ts @@ -0,0 +1,15 @@ +/** + * Commands related to the Analysis feature. + */ + +import { Configurator, IModule } from "../../core/command/module-handler"; +import { Context } from "../../core/command/cli-context"; + +class Module extends IModule { + + register(context: Context, configurator: Configurator) { + } + +} + +export = Module; \ No newline at end of file diff --git a/src/commands/cpm4/module.ts b/src/commands/cpm4/module.ts new file mode 100644 index 00000000..32075375 --- /dev/null +++ b/src/commands/cpm4/module.ts @@ -0,0 +1,15 @@ +/** + * Commands related to the Action Flows area. + */ + +import { Configurator, IModule } from "../../core/command/module-handler"; +import { Context } from "../../core/command/cli-context"; + +class Module extends IModule { + + register(context: Context, configurator: Configurator) { + } + +} + +export = Module; \ No newline at end of file diff --git a/src/commands/data-pipeline/module.ts b/src/commands/data-pipeline/module.ts new file mode 100644 index 00000000..5f7dee8d --- /dev/null +++ b/src/commands/data-pipeline/module.ts @@ -0,0 +1,15 @@ +/** + * Commands related to the Data Pipeline area. + */ + +import { Configurator, IModule } from "../../core/command/module-handler"; +import { Context } from "../../core/command/cli-context"; + +class Module extends IModule { + + register(context: Context, configurator: Configurator) { + } + +} + +export = Module; \ No newline at end of file diff --git a/src/commands/pacman/module.ts b/src/commands/pacman/module.ts new file mode 100644 index 00000000..24e1fb34 --- /dev/null +++ b/src/commands/pacman/module.ts @@ -0,0 +1,15 @@ +/** + * Commands related to the Pacman area. + */ + +import { Configurator, IModule } from "../../core/command/module-handler"; +import { Context } from "../../core/command/cli-context"; + +class Module extends IModule { + + register(context: Context, configurator: Configurator) { + } + +} + +export = Module; \ No newline at end of file diff --git a/src/commands/studio/module.ts b/src/commands/studio/module.ts new file mode 100644 index 00000000..27f715ad --- /dev/null +++ b/src/commands/studio/module.ts @@ -0,0 +1,15 @@ +/** + * Commands related to the Studio area. + */ + +import { Configurator, IModule } from "../../core/command/module-handler"; +import { Context } from "../../core/command/cli-context"; + +class Module extends IModule { + + register(context: Context, configurator: Configurator) { + } + +} + +export = Module; \ No newline at end of file From 375f56ceb4ef4839b4296b2a15e4c30e9981851f Mon Sep 17 00:00:00 2001 From: zgjimhaziri Date: Fri, 16 May 2025 11:18:01 +0200 Subject: [PATCH 22/45] TA-3750: Rename Pacman module to Config --- src/commands/{pacman => config}/module.ts | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/commands/{pacman => config}/module.ts (100%) diff --git a/src/commands/pacman/module.ts b/src/commands/config/module.ts similarity index 100% rename from src/commands/pacman/module.ts rename to src/commands/config/module.ts From ba513253cbb7fa5aea520d863e5333bd85160ba6 Mon Sep 17 00:00:00 2001 From: zgjimhaziri Date: Fri, 16 May 2025 11:18:34 +0200 Subject: [PATCH 23/45] TA-3750: Remove non-Astro codeowners --- .github/CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 814f473f..30af423d 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,4 +1,4 @@ -* @celonis/navi @gencblakqorip9 @celonis/astro +* @celonis/astro #/* @celonis/astro #/src @celonis/astro From d2ff658280fe8e7b937f2bb317c744015bf53d02 Mon Sep 17 00:00:00 2001 From: zgjimhaziri Date: Fri, 16 May 2025 11:22:45 +0200 Subject: [PATCH 24/45] TA-3750: Update Pacman module description --- src/commands/config/module.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commands/config/module.ts b/src/commands/config/module.ts index 24e1fb34..b08cdd02 100644 --- a/src/commands/config/module.ts +++ b/src/commands/config/module.ts @@ -1,5 +1,5 @@ /** - * Commands related to the Pacman area. + * Commands related to Celonis configuration management. */ import { Configurator, IModule } from "../../core/command/module-handler"; From 633f4fd2f2743817ccb066fc326e484f060e70f4 Mon Sep 17 00:00:00 2001 From: zgjimhaziri Date: Fri, 16 May 2025 11:24:58 +0200 Subject: [PATCH 25/45] TA-3750: Fix merge conflicts --- src/commands/profile/profile-module.ts | 47 ------------------------ src/commands/profile/question.service.ts | 29 --------------- 2 files changed, 76 deletions(-) delete mode 100644 src/commands/profile/profile-module.ts delete mode 100644 src/commands/profile/question.service.ts diff --git a/src/commands/profile/profile-module.ts b/src/commands/profile/profile-module.ts deleted file mode 100644 index abc25535..00000000 --- a/src/commands/profile/profile-module.ts +++ /dev/null @@ -1,47 +0,0 @@ -/** - * Commands to create and list access profiles. - */ - -import { Command, OptionValues } from "commander"; -import { Configurator, IModule } from "../../core/command/module-handler"; -import { logger } from "../../core/utils/logger"; -import { Context } from "../../core/command/cli-context"; -import { ProfileCommandService } from "./profile-command.service"; - -class ProfileModule extends IModule { - - register(context: Context, configurator: Configurator) { - - let command = configurator.command("profile"); - command.description("Manage profiles required to access a system."); - - command.command("list") - .description("Command to list all stored profiles") - .action(this.listProfiles); - - command.command("create") - .description("Command to create a new profile") - .option("--setAsDefault", "Set this profile as default") - .action(this.createProfile); - - command.command("default ") - .description("Command to set a profile as default") - .action(this.defaultProfile); - } - - async defaultProfile(context: Context, command: Command) { - let profile = command.args[0]; - await new ProfileCommandService().makeDefaultProfile(profile); - } - - async createProfile(context: Context, command: Command, options: OptionValues) { - await new ProfileCommandService().createProfile(options.setAsDefault); - } - - async listProfiles(context: Context, command: Command) { - logger.debug(`List profiles`); - await new ProfileCommandService().listProfiles(); - } -} - -export = ProfileModule; \ No newline at end of file diff --git a/src/commands/profile/question.service.ts b/src/commands/profile/question.service.ts deleted file mode 100644 index a6281c6b..00000000 --- a/src/commands/profile/question.service.ts +++ /dev/null @@ -1,29 +0,0 @@ -import * as readline from "readline"; -import { ReadLine } from "readline"; - -/** - * Simple method to get input from the user/console using ReadLine. The - * class must be closed in order to avoid the CLI to not terminate properly. - */ -export class QuestionService { - - readLine: ReadLine; - - constructor() { - this.readLine = readline.createInterface({ - input: process.stdin, - output: process.stdout, - terminal: true, - }); - } - - async ask(question: string): Promise { - return new Promise((resolve) => { - this.readLine.question(question, input => resolve(input)); - }); - } - - close() { - this.readLine.close(); - } -} From 715fe90425404a717696ade8be0e174f759e7d5e Mon Sep 17 00:00:00 2001 From: zgjimhaziri Date: Fri, 16 May 2025 13:59:54 +0200 Subject: [PATCH 26/45] TA-3750: Rename config module --- src/commands/{config => configuration-management}/module.ts | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/commands/{config => configuration-management}/module.ts (100%) diff --git a/src/commands/config/module.ts b/src/commands/configuration-management/module.ts similarity index 100% rename from src/commands/config/module.ts rename to src/commands/configuration-management/module.ts From 14be28d7a2b5418be07e8d1f4b78bb2240f53699 Mon Sep 17 00:00:00 2001 From: zgjimhaziri Date: Fri, 16 May 2025 16:59:01 +0200 Subject: [PATCH 27/45] TA-3750: Rename config module in codeowners file --- .github/CODEOWNERS | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 30af423d..35493b46 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,12 +1,12 @@ -* @celonis/astro +* @celonis/astro -#/* @celonis/astro -#/src @celonis/astro -#/src/core @celonis/astro -#/src/commands/pacman/ @celonis/astro -#/src/commands/profile/ @celonis/astro -#/src/commands/action-flows/ action-flow team -#/src/commands/analysis/ filter and foundation team -#/src/commands/cpm4/ cpm4 team -#/src/commands/data-pipeline/ data-management team -#/src/commands/studio/ @celonis/navi +#/* @celonis/astro +#/src @celonis/astro +#/src/core @celonis/astro +#/src/commands/configuration-management/ @celonis/astro +#/src/commands/profile/ @celonis/astro +#/src/commands/action-flows/ action-flow team +#/src/commands/analysis/ filter and foundation team +#/src/commands/cpm4/ cpm4 team +#/src/commands/data-pipeline/ data-management team +#/src/commands/studio/ @celonis/navi From f9033a38939cef2373bd74322e7f915a21c53e94 Mon Sep 17 00:00:00 2001 From: zgjimhaziri Date: Mon, 19 May 2025 09:54:22 +0200 Subject: [PATCH 28/45] TA-3771: Migrate Data Pipeline commands --- .../connection/connection-command.service.ts | 23 ++++ .../connection/connection.commands.ts | 50 ++++++++ .../connection/connection.service.ts | 62 +++++++++ .../data-pipeline/data-pool/data-pool-api.ts | 55 ++++++++ .../data-pool/data-pool-command.service.ts | 49 +++++++ .../data-pool/data-pool-manager.factory.ts | 47 +++++++ .../data-pool/data-pool-manager.interfaces.ts | 30 +++++ .../data-pool/data-pool-service.ts | 71 ++++++++++ .../data-pool/data-pool.commands.ts | 90 +++++++++++++ .../data-pool/data-pool.manager.ts | 62 +++++++++ src/commands/data-pipeline/module.ts | 7 +- src/core/command/cli-context.ts | 26 ++-- src/core/http/http-shared/base.manager.ts | 121 ++++++++++++++++++ src/core/http/http-shared/content.service.ts | 64 +++++++++ .../http-shared/manager-config.interface.ts | 11 ++ src/core/profile/profile.service.ts | 11 +- 16 files changed, 760 insertions(+), 19 deletions(-) create mode 100644 src/commands/data-pipeline/connection/connection-command.service.ts create mode 100644 src/commands/data-pipeline/connection/connection.commands.ts create mode 100644 src/commands/data-pipeline/connection/connection.service.ts create mode 100644 src/commands/data-pipeline/data-pool/data-pool-api.ts create mode 100644 src/commands/data-pipeline/data-pool/data-pool-command.service.ts create mode 100644 src/commands/data-pipeline/data-pool/data-pool-manager.factory.ts create mode 100644 src/commands/data-pipeline/data-pool/data-pool-manager.interfaces.ts create mode 100644 src/commands/data-pipeline/data-pool/data-pool-service.ts create mode 100644 src/commands/data-pipeline/data-pool/data-pool.commands.ts create mode 100644 src/commands/data-pipeline/data-pool/data-pool.manager.ts create mode 100644 src/core/http/http-shared/base.manager.ts create mode 100644 src/core/http/http-shared/content.service.ts create mode 100644 src/core/http/http-shared/manager-config.interface.ts diff --git a/src/commands/data-pipeline/connection/connection-command.service.ts b/src/commands/data-pipeline/connection/connection-command.service.ts new file mode 100644 index 00000000..739e4602 --- /dev/null +++ b/src/commands/data-pipeline/connection/connection-command.service.ts @@ -0,0 +1,23 @@ +import { Context } from "../../../core/command/cli-context"; +import { ConnectionService } from "./connection.service"; + +export class ConnectionCommandService { + + private connectionService: ConnectionService; + + constructor(context: Context) { + this.connectionService = new ConnectionService(context); + } + + public async updateProperty(dataPoolId: string, connectionId: string, property: string, value: string): Promise{ + await this.connectionService.updateProperty(dataPoolId, connectionId, property, value); + } + + public async getProperties(dataPoolId: string, connectionId: string): Promise { + await this.connectionService.listProperties(dataPoolId, connectionId); + } + + public async listConnections(dataPoolId: string): Promise { + await this.connectionService.findAllConnections(dataPoolId); + } +} diff --git a/src/commands/data-pipeline/connection/connection.commands.ts b/src/commands/data-pipeline/connection/connection.commands.ts new file mode 100644 index 00000000..dc8ac493 --- /dev/null +++ b/src/commands/data-pipeline/connection/connection.commands.ts @@ -0,0 +1,50 @@ +/** + * Commands related to the Data Pool Connections. + */ + +import { Command, OptionValues } from "commander"; +import { Configurator } from "../../../core/command/module-handler"; +import { ConnectionCommandService } from "./connection-command.service"; +import { Context } from "../../../core/command/cli-context"; + +export class ConnectionCommands { + + register(context: Context, configurator: Configurator) { + const listCommand = configurator.command("list"); + listCommand.command("connection") + .description("Command to list all connections in a Data Pool") + .option("-p, --profile ", "Profile which you want to use to list connections") + .requiredOption("--dataPoolId ", "ID of the data pool") + .action(this.listConnections); + + const getCommand = configurator.command("get"); + getCommand.command("connection") + .description("Programmatically read properties of your connections") + .option("-p, --profile ", "Profile which you want to use to update the data pool configuration") + .requiredOption("--dataPoolId ", "Id of the data pool you want to update") + .requiredOption("--connectionId ", "Id of the connection you want to update") + .action(this.getCommandProperties); + + const setCommand = configurator.command("set"); + setCommand.command("connection") + .description("Programmatically update properties of your connections") + .option("-p, --profile ", "Profile which you want to use to update the data pool configuration") + .requiredOption("--dataPoolId ", "Id of the data pool you want to update") + .requiredOption("--connectionId ", "Id of the connection you want to update") + .requiredOption("--property ", "The property you want to update") + .requiredOption("--value ", "The value you want to update") + .action(this.updateConnectionProperty); + } + + async getCommandProperties(context: Context, command: Command, options: OptionValues) { + await new ConnectionCommandService(context).getProperties(options.dataPoolId, options.connectionId); + } + + async listConnections(context: Context, command: Command, options: OptionValues) { + await new ConnectionCommandService(context).listConnections(options.dataPoolId); + } + + async updateConnectionProperty(context: Context, command: Command, options: OptionValues) { + await new ConnectionCommandService(context).updateProperty(options.dataPoolId, options.connectionId, options.property, options.value); + } +} diff --git a/src/commands/data-pipeline/connection/connection.service.ts b/src/commands/data-pipeline/connection/connection.service.ts new file mode 100644 index 00000000..79165d22 --- /dev/null +++ b/src/commands/data-pipeline/connection/connection.service.ts @@ -0,0 +1,62 @@ +import { logger } from "../../../core/utils/logger"; +import { Context } from "../../../core/command/cli-context"; +import { DataPoolApi } from "../data-pool/data-pool-api"; + +export class ConnectionService { + + private dataPoolApi: DataPoolApi; + + constructor(context: Context) { + this.dataPoolApi = new DataPoolApi(context) + } + + public async findAllConnections(dataPoolId: string): Promise { + const connections = await this.dataPoolApi.listConnections(dataPoolId); + connections.forEach(connection => { + logger.info(`Connection ID: ${connection.id} - Name: ${connection.name} - Type: ${connection.type}`); + }) + return connections; + } + + public async listProperties(dataPoolId: string, connectionId: string): Promise { + const connection = await this.dataPoolApi.getConnection(dataPoolId, connectionId); + const type = connection.type; + const typedConnection = await this.dataPoolApi.getTypedConnection(dataPoolId, connectionId, type); + logger.info(`Connection ID: ${connection.id} - Name: ${connection.name} - Type: ${connection.type}`); + logger.info(`Properties:`) + for (let k in typedConnection) { + if (typeof typedConnection[k] === 'object') { + for (let o in typedConnection[k]) { + logger.info(` ${k}.${o} : ${typeof typedConnection[k][o]} := ${typedConnection[k][o]}`) + } + } else { + logger.info(` ${k} : ${typeof typedConnection[k]} := ${typedConnection[k]}`); + } + } + return typedConnection; + } + + public async updateProperty(dataPoolId: string, connectionId: string, property: string, value: string) { + const connection = await this.dataPoolApi.getConnection(dataPoolId, connectionId); + const type = connection.type; + const typedConnection = await this.dataPoolApi.getTypedConnection(dataPoolId, connectionId, type); + const parts = property.split("."); + // update the typed connection object + let currentObject = typedConnection; + for (let i = 0; i < parts.length; i++) { + const part = parts[i]; + if (currentObject.hasOwnProperty(part)) { + if (i < parts.length - 1) { + currentObject = currentObject[part]; + } else { + currentObject[part] = value; + } + } else { + logger.error(`Property ${property} not found on connection.`) + return; + } + } + await this.dataPoolApi.updateTypedConnection(dataPoolId, connectionId, type, typedConnection); + logger.info(`Property ${property} updated.`); + } +} diff --git a/src/commands/data-pipeline/data-pool/data-pool-api.ts b/src/commands/data-pipeline/data-pool/data-pool-api.ts new file mode 100644 index 00000000..77c41b1a --- /dev/null +++ b/src/commands/data-pipeline/data-pool/data-pool-api.ts @@ -0,0 +1,55 @@ +import { DataPoolInstallVersionReport, DataPoolPageTransport } from "./data-pool-manager.interfaces"; +import { FatalError } from "../../../core/utils/logger"; +import { Context } from "../../../core/command/cli-context"; +import { HttpClient } from "../../../core/http/http-client"; + +export class DataPoolApi { + + private httpClient: HttpClient; + + constructor(context: Context) { + this.httpClient = context.httpClient; + } + + public async findAllPagedPools(limit: string, page: string): Promise { + return this.httpClient.get(`/integration/api/pools/paged?limit=${limit}&page=${page}`).catch(e => { + throw new FatalError(`Problem getting data pools: : ${e}`); + }); + } + + public async executeDataPoolsBatchImport(importRequest: string): Promise { + return this.httpClient.post("/integration/api/pool/batch-import", importRequest).catch(e => { + throw new FatalError(`Data Pool batch import failed: : ${e}`); + }); + } + + public async exportDataPool(poolId: string): Promise { + return this.httpClient.get(`/integration/api/pools/${poolId}/v2/export`).catch(e => { + throw new FatalError(`Data Pool export failed: : ${e}`); + }); + } + + public async listConnections(poolId: string): Promise { + return this.httpClient.get(`/integration/api/pools/${poolId}/overviews/data-sources`).catch(e => { + throw new FatalError(`Can not list connections: : ${e}`); + }); + } + + public async getConnection(poolId: string, connectionId: string): Promise { + return this.httpClient.get(`/integration/api/pools/${poolId}/data-sources/${connectionId}`).catch(e => { + throw new FatalError(`Can not get connection: : ${e}`); + }); + } + + public async getTypedConnection(poolId: string, connectionId: string, type: string): Promise { + return this.httpClient.get(`/integration/api/datasource/${type}/${connectionId}`).catch(e => { + throw new FatalError(`Can get typed connection: : ${e}`); + }); + } + + public async updateTypedConnection(poolId: string, connectionId: string, type: string, data: any): Promise { + return this.httpClient.put(`/integration/api/datasource/${type}/${connectionId}`, data).catch(e => { + throw new FatalError(`Can not update typed connection: ${e}`); + }); + } +} diff --git a/src/commands/data-pipeline/data-pool/data-pool-command.service.ts b/src/commands/data-pipeline/data-pool/data-pool-command.service.ts new file mode 100644 index 00000000..64b03085 --- /dev/null +++ b/src/commands/data-pipeline/data-pool/data-pool-command.service.ts @@ -0,0 +1,49 @@ +import { ContentService } from "../../../core/http/http-shared/content.service"; +import { DataPoolManagerFactory } from "./data-pool-manager.factory"; +import { Context } from "../../../core/command/cli-context"; +import { DataPoolService } from "./data-pool-service"; +import { logger } from "../../../core/utils/logger"; + +export class DataPoolCommandService { + private contentService = new ContentService(); + private dataPoolManagerFactory: DataPoolManagerFactory; + private dataPoolService: DataPoolService; + + constructor(context: Context) { + this.dataPoolManagerFactory = new DataPoolManagerFactory(context); + this.dataPoolService = new DataPoolService(context); + } + + public async pullDataPool(id: string): Promise { + await this.contentService.pull(this.dataPoolManagerFactory.createManager(id, null)); + } + + public async pushDataPool(filename: string): Promise { + await this.contentService.push(this.dataPoolManagerFactory.createManager(null, filename)); + } + + public async exportDataPool(poolId: string, outputToJsonFile: boolean): Promise { + await this.dataPoolService.exportDataPool(poolId, outputToJsonFile); + } + + public async pushDataPools(): Promise { + await this.contentService.batchPush(this.dataPoolManagerFactory.createManagers()); + } + + public async batchImportDataPools(requestFile: string, outputToJsonFile: boolean): Promise { + await this.dataPoolService.batchImportDataPools(requestFile, outputToJsonFile); + } + + public async updateDataPool(id: string, filename: string): Promise { + await this.contentService.update(this.dataPoolManagerFactory.createManager(id, filename)); + } + + public async listDataPools(jsonResponse: boolean): Promise { + logger.info(jsonResponse); + if (jsonResponse) { + await this.dataPoolService.findAndExportAllPools(); + } else { + await this.dataPoolService.listDataPools(); + } + } +} diff --git a/src/commands/data-pipeline/data-pool/data-pool-manager.factory.ts b/src/commands/data-pipeline/data-pool/data-pool-manager.factory.ts new file mode 100644 index 00000000..5b733006 --- /dev/null +++ b/src/commands/data-pipeline/data-pool/data-pool-manager.factory.ts @@ -0,0 +1,47 @@ +import * as path from "path"; +import { DataPoolManager } from "./data-pool.manager"; +import { FatalError, logger } from "../../../core/utils/logger"; +import * as fs from "node:fs"; +import { Context } from "../../../core/command/cli-context"; + +export class DataPoolManagerFactory { + + private readonly context: Context; + + constructor(context: Context) { + this.context = context; + } + + public createManager(id: string, filename: string): DataPoolManager { + const dataPoolManager = new DataPoolManager(this.context); + dataPoolManager.id = id; + if (filename !== null) { + dataPoolManager.content = this.readFile(filename); + } + return dataPoolManager; + } + + public createManagers(): DataPoolManager[] { + const dataPools = fs.readdirSync(process.cwd()); + return dataPools + .filter(filePath => { + if (filePath.startsWith(DataPoolManager.DATA_POOL_FILE_NAME_PREFIX) && filePath.endsWith(".json")) { + const file = fs.lstatSync(filePath); + return file.isFile(); + } + return false; + }) + .map(dataPool => { + const dataPoolManager = new DataPoolManager(this.context); + dataPoolManager.content = this.readFile(dataPool); + return dataPoolManager; + }); + } + + private readFile(filename: string): string { + if (!fs.existsSync(path.resolve(process.cwd(), filename))) { + logger.error(new FatalError("The provided file does not exit")); + } + return fs.readFileSync(path.resolve(process.cwd(), filename), { encoding: "utf-8" }); + } +} diff --git a/src/commands/data-pipeline/data-pool/data-pool-manager.interfaces.ts b/src/commands/data-pipeline/data-pool/data-pool-manager.interfaces.ts new file mode 100644 index 00000000..71c00bff --- /dev/null +++ b/src/commands/data-pipeline/data-pool/data-pool-manager.interfaces.ts @@ -0,0 +1,30 @@ +export declare type DataPoolStatus = "CANCEL" | "FAIL" | "QUEUED" | "RUNNING" | "SKIPPED" | "SUCCESS" | "UNCONFIGURED"; +export type Tag = {}; + +export declare class DataSourceSlimTransport { + id: string; + name: string; + imported: boolean; + importedPoolId: string; +} + +export interface DataPoolSlimTransport { + id: string; + name: string; + status: DataPoolStatus; + lastExecutionStartDate: Date; + createdBy: string; + tags: Tag[]; + rawDataSize: number; + dataSources: DataSourceSlimTransport[]; +} +export declare class DataPoolPageTransport { + content: DataPoolSlimTransport[]; + pageSize: number; + pageNumber: number; + totalCount: number; +} + +export declare class DataPoolInstallVersionReport { + dataModelIdMappings: Map; +} diff --git a/src/commands/data-pipeline/data-pool/data-pool-service.ts b/src/commands/data-pipeline/data-pool/data-pool-service.ts new file mode 100644 index 00000000..38caf886 --- /dev/null +++ b/src/commands/data-pipeline/data-pool/data-pool-service.ts @@ -0,0 +1,71 @@ +import { v4 as uuidv4 } from "uuid"; +import { FileService, fileService } from "../../../core/utils/file-service"; +import { logger } from "../../../core/utils/logger"; +import { DataPoolSlimTransport } from "./data-pool-manager.interfaces"; +import { Context } from "../../../core/command/cli-context"; +import { DataPoolApi } from "./data-pool-api"; + +export class DataPoolService { + + private dataPoolApi: DataPoolApi; + + constructor(context: Context) { + this.dataPoolApi = new DataPoolApi(context); + } + + public async batchImportDataPools(requestFilePath: string, outputToJsonFile: boolean): Promise { + const requestFileContent: string = fileService.readFile(requestFilePath); + const importReport = await this.dataPoolApi.executeDataPoolsBatchImport(requestFileContent); + const importReportString = JSON.stringify(importReport, null, 4); + + if (outputToJsonFile) { + const reportFileName = "batch_import_report_" + uuidv4() + ".json"; + fileService.writeToFileWithGivenName(importReportString, reportFileName); + logger.info("Batch import report file: " + reportFileName); + } else { + logger.info("Batch import report: \n" + importReportString); + } + } + + public async exportDataPool(poolId: string, outputToJsonFile: boolean): Promise { + const exportedDataPool = await this.dataPoolApi.exportDataPool(poolId); + const exportedDataPoolString = JSON.stringify(exportedDataPool, null, 4); + + if (outputToJsonFile) { + const reportFileName = uuidv4() + "_data_pool_" + poolId + ".json"; + fileService.writeToFileWithGivenName(exportedDataPoolString, reportFileName); + logger.info(FileService.fileDownloadedMessage + reportFileName); + } else { + logger.info("Exported Data Pool: \n" + exportedDataPoolString); + } + } + + public async listDataPools(): Promise { + const dataPools = await this.findAllPools(); + dataPools.forEach(pool => { + logger.info(`Pool Id: ${pool.id} - Pool Name: ${pool.name}`); + }); + } + + public async findAndExportAllPools(): Promise { + const dataPools = await this.findAllPools(); + this.exportListOfPools(dataPools); + } + + private async findAllPools(): Promise { + let page = 0; + const dataPools: DataPoolSlimTransport[] = []; + let tmpPage = await this.dataPoolApi.findAllPagedPools("100", page.toString()); + while (tmpPage.pageNumber < tmpPage.totalCount) { + dataPools.push(...tmpPage.content); + tmpPage = await this.dataPoolApi.findAllPagedPools("100", (++page).toString()); + } + return dataPools; + } + + private exportListOfPools(nodes: DataPoolSlimTransport[]): void { + const filename = uuidv4() + ".json"; + fileService.writeToFileWithGivenName(JSON.stringify(nodes), filename); + logger.info(FileService.fileDownloadedMessage + filename); + } +} \ No newline at end of file diff --git a/src/commands/data-pipeline/data-pool/data-pool.commands.ts b/src/commands/data-pipeline/data-pool/data-pool.commands.ts new file mode 100644 index 00000000..cacc1d1b --- /dev/null +++ b/src/commands/data-pipeline/data-pool/data-pool.commands.ts @@ -0,0 +1,90 @@ +/** + * Commands related to the Data Pools. + */ +import { Context } from "../../../core/command/cli-context"; +import { Configurator } from "../../../core/command/module-handler"; +import { Command, OptionValues } from "commander"; +import { DataPoolCommandService } from "./data-pool-command.service"; + +export class DataPoolCommands { + + register(context: Context, configurator: Configurator) { + const exportCommand = configurator.command("export"); + exportCommand.command("data-pool") + .description("Command to export a data pool") + .option("-p, --profile ", "Profile which you want to use to export the data pool") + .requiredOption("--id ", "ID of the data pool you want to export") + .option("--outputToJsonFile", "Output the exported data pool to a JSON file") + .action(this.exportDataPool); + + const importCommand = configurator.command("import"); + importCommand.command("data-pools") + .description("Command to batch import multiple data pools with their objects and dependencies") + .option("-p, --profile ", "Profile which you want to use to import the data pools") + .requiredOption("-f, --jsonFile ", "The file with the JSON data pool batch import request") + .option("--outputToJsonFile", "Output the batch import result in a JSON file") + .action(this.batchImportDataPools); + + const listCommand = configurator.command("list"); + listCommand.command("data-pools") + .description("Command to list all Data Pools") + .option("-p, --profile ", "Profile which you want to use to list data pools") + .option("--json", "Return response as json type", "") + .action(this.listDataPools); + + const pullCommand = configurator.command("pull"); + pullCommand.command("data-pool") + .description("Command to pull a data pool") + .option("-p, --profile ", "Profile which you want to use to pull the data pool") + .requiredOption("--id ", "Id of the data pool you want to pull") + .action(this.pullDataPool); + + const pushCommand = configurator.command("push"); + pushCommand.command("data-pool") + .description("Command to push a data pool") + .option("-p, --profile ", "Profile which you want to use to push the data pool") + .requiredOption("-f, --file ", "The file you want to push") + .action(this.pushDataPool); + + pushCommand.command("data-pools") + .description("Command to push data pools") + .option("-p, --profile ", "Profile which you want to use to push the data pools") + .action(this.pushDataPools); + + const updateCommand = configurator.command("update"); + updateCommand.command("data-pool") + .description("Command to update a data pool using a data pool configuration file") + .option("-p, --profile ", "Profile which you want to use to update the data pool configuration") + .requiredOption("--id ", "Id of the data pool you want to update") + .requiredOption("-f, --file ", "The file you want to push") + .action(this.updateDataPool); + } + + async exportDataPool(context: Context, command: Command, options: OptionValues) { + await new DataPoolCommandService(context).exportDataPool(options.id, options.outputToJsonFile); + } + + async batchImportDataPools(context: Context, command: Command, options: OptionValues) { + await new DataPoolCommandService(context).batchImportDataPools(options.jsonFile, options.outputToJsonFile); + } + + async listDataPools(context: Context, command: Command, options: OptionValues) { + await new DataPoolCommandService(context).listDataPools(options.json); + } + + async pullDataPool(context: Context, command: Command, options: OptionValues) { + await new DataPoolCommandService(context).pullDataPool(options.id); + } + + async pushDataPool(context: Context, command: Command, options: OptionValues) { + await new DataPoolCommandService(context).pushDataPool(options.file); + } + + async pushDataPools(context: Context, command: Command, options: OptionValues) { + await new DataPoolCommandService(context).pushDataPools(); + } + + async updateDataPool(context: Context, command: Command, options: OptionValues) { + await new DataPoolCommandService(context).updateDataPool(options.id, options.file); + } +} diff --git a/src/commands/data-pipeline/data-pool/data-pool.manager.ts b/src/commands/data-pipeline/data-pool/data-pool.manager.ts new file mode 100644 index 00000000..eb0a46f4 --- /dev/null +++ b/src/commands/data-pipeline/data-pool/data-pool.manager.ts @@ -0,0 +1,62 @@ +import { BaseManager } from "../../../core/http/http-shared/base.manager"; +import { ManagerConfig } from "../../../core/http/http-shared/manager-config.interface"; +import { Context } from "../../../core/command/cli-context"; + +export class DataPoolManager extends BaseManager { + public static DATA_POOL_FILE_NAME_PREFIX = "data-pool_"; + private static API_URL = "/integration/api"; + private static DATA_POOL_PUSH_URL = DataPoolManager.API_URL + "/pool-import"; + private static DATA_POOL_ACTIONS_URL = DataPoolManager.API_URL + "/pools/{id}"; + private static DATA_POOL_PULL_URL = DataPoolManager.DATA_POOL_ACTIONS_URL + "/export"; + + private _id: string; + private _content: string; + + constructor(context: Context) { + super(context); + } + + public get content(): string { + return this._content; + } + + public set content(value: string) { + this._content = value; + } + + public get id(): string { + return this._id; + } + + public set id(value: string) { + this._id = value; + } + + public getConfig(): ManagerConfig { + return { + pushUrl: DataPoolManager.DATA_POOL_PUSH_URL, + pullUrl: DataPoolManager.DATA_POOL_PULL_URL.replace("{id}", this.id), + updateUrl: DataPoolManager.DATA_POOL_ACTIONS_URL.replace("{id}", this.id), + exportFileName: DataPoolManager.DATA_POOL_FILE_NAME_PREFIX + this.id + ".json", + onPushSuccessMessage: (data: any): string => { + return "Data Pool was pushed successfully. New ID: " + data.id; + }, + onUpdateSuccessMessage: (): string => { + return "Data Pool was updated successfully!"; + }, + }; + } + + public getBody(): any { + if (this.id != null) { + const parsedContent = JSON.parse(this.content); + return parsedContent.dataPool; + } + + return this.content; + } + + protected getSerializedFileContent(data: any): string { + return JSON.stringify(data); + } +} diff --git a/src/commands/data-pipeline/module.ts b/src/commands/data-pipeline/module.ts index 5f7dee8d..3d0ef319 100644 --- a/src/commands/data-pipeline/module.ts +++ b/src/commands/data-pipeline/module.ts @@ -4,12 +4,15 @@ import { Configurator, IModule } from "../../core/command/module-handler"; import { Context } from "../../core/command/cli-context"; +import { ConnectionCommands } from "./connection/connection.commands"; +import { DataPoolCommands } from "./data-pool/data-pool.commands"; class Module extends IModule { register(context: Context, configurator: Configurator) { + new DataPoolCommands().register(context, configurator); + new ConnectionCommands().register(context, configurator); } - } -export = Module; \ No newline at end of file +export = Module; diff --git a/src/core/command/cli-context.ts b/src/core/command/cli-context.ts index 01af2e0e..1ed54151 100644 --- a/src/core/command/cli-context.ts +++ b/src/core/command/cli-context.ts @@ -10,10 +10,12 @@ import {Profile} from "../profile/profile.interface"; */ export class Context { - log = logger; - httpClient: HttpClient; // TODO - provide access to an initialized API (http api etc.) - profile: Profile; - profileName: string | undefined; + + public httpClient: HttpClient; + public profile: Profile; + + private log = logger; + private profileName: string | undefined; private profileService = new ProfileService(); @@ -21,34 +23,30 @@ export class Context { this.profileName = options.profile; } - async init() { + public async init(): Promise { await this.loadProfile(this.profileName); if (this.profile) { // only if a profile is available, it makes sense to provide an initialized - // HttpClient API. + // HttpClient API. this.httpClient = new HttpClient(this); } } - async loadProfile(profileName: string | undefined) { + private async loadProfile(profileName: string | undefined): Promise { if (!profileName) { - this.log.debug(`Profile name not specified, using default profile name`); + this.log.debug("Profile name not specified, using default profile name"); profileName = this.profileService.getDefaultProfile(); if (!profileName) { - this.log.debug(`A default profile is not configured.`); + this.log.debug("A default profile is not configured."); } - return; } try { this.profile = await this.profileService.findProfile(profileName); this.profileName = profileName; this.log.info(`Using profile ${profileName}`); } catch (err) { - // TODO - The error message is incorrect, overriding it here for the time being. - // change it though after the ProfileService is completely migrated/fixed. - //this.log.error(err); - this.log.error(`The profile ${profileName} cannot be loaded.`); + this.log.error(err); this.profile = undefined; this.profileName = undefined; } diff --git a/src/core/http/http-shared/base.manager.ts b/src/core/http/http-shared/base.manager.ts new file mode 100644 index 00000000..f988151b --- /dev/null +++ b/src/core/http/http-shared/base.manager.ts @@ -0,0 +1,121 @@ +import * as fs from "fs"; +import * as path from "path"; +import { FatalError, logger } from "../../utils/logger"; +import { ManagerConfig } from "./manager-config.interface"; +import { HttpClient } from "../http-client"; +import { Context } from "../../command/cli-context"; + +export abstract class BaseManager { + private httpClient: HttpClient; + protected readonly fileDownloadedMessage = "File downloaded successfully. New filename: "; + + protected constructor(context: Context) { + this.httpClient = context.httpClient; + } + + public async pull(): Promise { + return new Promise((resolve, reject) => { + this.httpClient + .get(this.getConfig().pullUrl) + .then(data => { + try { + const filename = this.writeToFile(data); + logger.info(this.fileDownloadedMessage + filename); + resolve(); + } catch (e) { + logger.error(new FatalError(e)); + reject(); + } + }) + .catch(err => { + logger.error(new FatalError(err)); + reject(); + }); + }); + } + + public async pullFile(): Promise { + return new Promise((resolve, reject) => { + this.httpClient + .getFile(this.getConfig().pullUrl) + .then(data => { + const filename = this.writeStreamToFile(data); + logger.info(this.fileDownloadedMessage + filename); + resolve(); + }) + .catch(err => { + logger.error(new FatalError(err)); + reject(); + }); + }); + } + + public async push(): Promise { + return new Promise((resolve, reject) => { + this.httpClient + .post(this.getConfig().pushUrl, this.getBody()) + .then(data => { + logger.info(this.getConfig().onPushSuccessMessage(data)); + resolve(data); + }) + .catch(err => { + logger.error(new FatalError(err)); + reject(); + }); + }); + } + + public async update(): Promise { + return new Promise((resolve, reject) => { + this.httpClient + .put(this.getConfig().updateUrl, this.getBody()) + .then(data => { + logger.info(this.getConfig().onUpdateSuccessMessage()); + resolve(data); + }) + .catch(err => { + logger.error(new FatalError(err)); + reject(); + }); + }); + } + + public async findAll(): Promise { + return new Promise((resolve, reject) => { + this.httpClient + .get(this.getConfig().findAllUrl) + .then(data => { + this.getConfig().onFindAll(data); + resolve(data); + }) + .catch(err => { + logger.error(new FatalError(err)); + reject(); + }); + }); + } + + protected writeToFile(data: any): string { + const filename = this.getConfig().exportFileName; + this.writeToFileWithGivenName(data, filename); + return filename; + } + + protected writeStreamToFile(data: any): string { + const filename = this.getConfig().exportFileName; + fs.writeFileSync(filename, data); + return filename; + } + + protected writeToFileWithGivenName(data: any, filename: string): void { + fs.writeFileSync(path.resolve(process.cwd(), filename), this.getSerializedFileContent(data), { + encoding: "utf-8", + }); + } + + protected abstract getConfig(): ManagerConfig; + + protected abstract getBody(): object; + + protected abstract getSerializedFileContent(data: any): string; +} diff --git a/src/core/http/http-shared/content.service.ts b/src/core/http/http-shared/content.service.ts new file mode 100644 index 00000000..6c2a3d15 --- /dev/null +++ b/src/core/http/http-shared/content.service.ts @@ -0,0 +1,64 @@ +import { BaseManager } from "./base.manager"; + +export class ContentService { + + public async pull(baseManager: BaseManager): Promise { + return new Promise((resolve, reject) => { + baseManager.pull().then( + () => resolve(), + () => reject() + ); + }); + } + + public async pullFile(baseManager: BaseManager): Promise { + return new Promise((resolve, reject) => { + baseManager.pullFile().then( + () => resolve(), + () => reject() + ); + }); + } + + public async push(baseManager: BaseManager): Promise { + return new Promise((resolve, reject) => { + baseManager.push().then( + () => resolve(), + () => reject() + ); + }); + } + + public async batchPush(baseManagers: BaseManager[]): Promise { + return new Promise((resolve, reject) => { + const promises: Array> = []; + + baseManagers.forEach(baseManager => { + promises.push(baseManager.push()); + }); + + Promise.all(promises).then( + () => resolve(), + () => reject() + ); + }); + } + + public async update(baseManager: BaseManager): Promise { + return new Promise((resolve, reject) => { + baseManager.update().then( + () => resolve(), + () => reject() + ); + }); + } + + public async findAll(baseManager: BaseManager): Promise { + return new Promise((resolve, reject) => { + baseManager.findAll().then( + () => resolve(), + () => reject() + ); + }); + } +} diff --git a/src/core/http/http-shared/manager-config.interface.ts b/src/core/http/http-shared/manager-config.interface.ts new file mode 100644 index 00000000..abead4c4 --- /dev/null +++ b/src/core/http/http-shared/manager-config.interface.ts @@ -0,0 +1,11 @@ +export interface ManagerConfig { + pushUrl?: string; + pullUrl?: string; + updateUrl?: string; + findAllUrl?: string; + exportFileName?: string; + onPushSuccessMessage?: (data: any) => string; + onUpdateSuccessMessage?: () => string; + onFindAll?: (data: any) => void; + onFindAllAndExport?: (data: any) => void; +} diff --git a/src/core/profile/profile.service.ts b/src/core/profile/profile.service.ts index 3cfe4d62..b45a6c25 100644 --- a/src/core/profile/profile.service.ts +++ b/src/core/profile/profile.service.ts @@ -26,6 +26,7 @@ export class ProfileService { public async findProfile(profileName: string): Promise { return new Promise((resolve, reject) => { + this.checkIfMissingProfile(profileName, reject); try { if (process.env.TEAM_URL && process.env.API_TOKEN) { resolve(this.buildProfileFromEnvVariables()); @@ -39,9 +40,7 @@ export class ProfileService { .then(() => resolve(profile)); } } catch (e) { - reject( - "No profile provided. Please provide a profile or an TEAM_URL and API_TOKEN through env variables" - ); + reject(`The profile ${profileName} couldn't be resolved.`); } }); } @@ -297,6 +296,12 @@ export class ProfileService { }) }) } + + private checkIfMissingProfile(profileName: string, reject: any): void { + if (!profileName && (!process.env.TEAM_URL || !process.env.API_TOKEN)) { + reject("No profile provided. Please provide a profile or an TEAM_URL and API_TOKEN through env variables"); + } + } } export const profileService = new ProfileService(); From 3a2f976cd2a821f3baa430b4ad9dafd5ea79a30c Mon Sep 17 00:00:00 2001 From: zgjimhaziri Date: Tue, 20 May 2025 15:39:52 +0200 Subject: [PATCH 29/45] TA-3767: Migrate Studio commands --- src/commands/studio/api/asset-api.ts | 27 + .../studio/api/batch-import-export-api.ts | 79 +++ src/commands/studio/api/compute-pool-api.ts | 19 + src/commands/studio/api/node-api.ts | 26 + src/commands/studio/api/package-api.ts | 91 +++ .../studio/api/package-dependencies-api.ts | 45 ++ src/commands/studio/api/space-api.ts | 31 + src/commands/studio/api/variables-api.ts | 45 ++ .../command-service/asset-command.service.ts | 36 ++ .../package-command.service.ts | 58 ++ .../command-service/space-command.service.ts | 16 + .../studio/command-service/widget.command.ts | 38 ++ .../batch-export-import.constants.ts | 11 + .../batch-export-node.interfaces.ts | 28 + .../studio/interfaces/manifest.interface.ts | 31 + .../interfaces/package-export.interfaces.ts | 82 +++ .../interfaces/package-manager.interfaces.ts | 99 ++++ .../interfaces/save-content-node.interface.ts | 12 + .../studio/interfaces/space.interface.ts | 5 + .../variable-assignment-apis.constants.ts | 12 + .../studio/manager/asset.manager-factory.ts | 57 ++ src/commands/studio/manager/asset.manager.ts | 75 +++ .../studio/manager/package.manager-factory.ts | 79 +++ .../studio/manager/package.manager.ts | 143 +++++ .../studio/manager/space.manager-factory.ts | 21 + src/commands/studio/manager/space.manager.ts | 52 ++ .../studio/manager/widget.manager-factory.ts | 95 ++++ src/commands/studio/manager/widget.manager.ts | 67 +++ src/commands/studio/module.ts | 149 +++++ src/commands/studio/service/asset-service.ts | 33 ++ .../studio/service/data-model.service.ts | 26 + .../studio/service/package.service.ts | 537 ++++++++++++++++++ src/commands/studio/service/space.service.ts | 49 ++ src/commands/studio/service/studio.service.ts | 280 +++++++++ .../studio/service/variable.service.ts | 124 ++++ .../studio/utils/semantic-versioning.ts | 23 + src/core/utils/json.ts | 14 + src/core/utils/yaml.ts | 9 + 38 files changed, 2624 insertions(+) create mode 100644 src/commands/studio/api/asset-api.ts create mode 100644 src/commands/studio/api/batch-import-export-api.ts create mode 100644 src/commands/studio/api/compute-pool-api.ts create mode 100644 src/commands/studio/api/node-api.ts create mode 100644 src/commands/studio/api/package-api.ts create mode 100644 src/commands/studio/api/package-dependencies-api.ts create mode 100644 src/commands/studio/api/space-api.ts create mode 100644 src/commands/studio/api/variables-api.ts create mode 100644 src/commands/studio/command-service/asset-command.service.ts create mode 100644 src/commands/studio/command-service/package-command.service.ts create mode 100644 src/commands/studio/command-service/space-command.service.ts create mode 100644 src/commands/studio/command-service/widget.command.ts create mode 100644 src/commands/studio/interfaces/batch-export-import.constants.ts create mode 100644 src/commands/studio/interfaces/batch-export-node.interfaces.ts create mode 100644 src/commands/studio/interfaces/manifest.interface.ts create mode 100644 src/commands/studio/interfaces/package-export.interfaces.ts create mode 100644 src/commands/studio/interfaces/package-manager.interfaces.ts create mode 100644 src/commands/studio/interfaces/save-content-node.interface.ts create mode 100644 src/commands/studio/interfaces/space.interface.ts create mode 100644 src/commands/studio/interfaces/variable-assignment-apis.constants.ts create mode 100644 src/commands/studio/manager/asset.manager-factory.ts create mode 100644 src/commands/studio/manager/asset.manager.ts create mode 100644 src/commands/studio/manager/package.manager-factory.ts create mode 100644 src/commands/studio/manager/package.manager.ts create mode 100644 src/commands/studio/manager/space.manager-factory.ts create mode 100644 src/commands/studio/manager/space.manager.ts create mode 100644 src/commands/studio/manager/widget.manager-factory.ts create mode 100644 src/commands/studio/manager/widget.manager.ts create mode 100644 src/commands/studio/service/asset-service.ts create mode 100644 src/commands/studio/service/data-model.service.ts create mode 100644 src/commands/studio/service/package.service.ts create mode 100644 src/commands/studio/service/space.service.ts create mode 100644 src/commands/studio/service/studio.service.ts create mode 100644 src/commands/studio/service/variable.service.ts create mode 100644 src/commands/studio/utils/semantic-versioning.ts create mode 100644 src/core/utils/json.ts create mode 100644 src/core/utils/yaml.ts diff --git a/src/commands/studio/api/asset-api.ts b/src/commands/studio/api/asset-api.ts new file mode 100644 index 00000000..d8749758 --- /dev/null +++ b/src/commands/studio/api/asset-api.ts @@ -0,0 +1,27 @@ +import {SaveContentNode} from "../interfaces/save-content-node.interface"; +import { HttpClient } from "../../../core/http/http-client"; +import { Context } from "../../../core/command/cli-context"; +import { FatalError } from "../../../core/utils/logger"; + +export class AssetApi { + + private httpClient: HttpClient; + + constructor(context: Context) { + this.httpClient = context.httpClient; + } + + public async findAllAssets(assetType: string): Promise { + return this.httpClient.get(this.getFindAllAssetsUrl(assetType)).catch(e => { + throw new FatalError(`Problem getting assets: ${e}`); + }); + } + + private getFindAllAssetsUrl(assetType: string): string { + const findAllAssetsUrl = "/package-manager/api/nodes"; + if (assetType) { + return `${findAllAssetsUrl}?assetType=${assetType}`; + } + return findAllAssetsUrl; + } +} diff --git a/src/commands/studio/api/batch-import-export-api.ts b/src/commands/studio/api/batch-import-export-api.ts new file mode 100644 index 00000000..25a94617 --- /dev/null +++ b/src/commands/studio/api/batch-import-export-api.ts @@ -0,0 +1,79 @@ +import * as FormData from "form-data"; +import { HttpClient } from "../../../core/http/http-client"; +import { Context } from "../../../core/command/cli-context"; +import { FatalError } from "../../../core/utils/logger"; +import { + PackageExportTransport, + PackageKeyAndVersionPair, + PostPackageImportData, + VariableManifestTransport, +} from "../interfaces/package-export.interfaces"; + +export class BatchImportExportApi { + + private httpClient: HttpClient; + + constructor(context: Context) { + this.httpClient = context.httpClient; + } + + public findAllActivePackages(flavors: string[], withDependencies: boolean = false): Promise { + const queryParams = new URLSearchParams(); + + queryParams.set("withDependencies", withDependencies.toString()); + flavors.forEach(flavor => queryParams.append("flavors", flavor)) + + return this.httpClient.get(`/package-manager/api/core/packages/export/list?${queryParams.toString()}`).catch(e => { + throw new FatalError(`Problem getting active packages: ${e}`); + }); + } + + public findActivePackagesByVariableValue(flavors: string[], variableValue: string, variableType: string): Promise { + const queryParams = new URLSearchParams(); + + queryParams.set("variableValue", variableValue); + if (variableType) { + queryParams.set("variableType", variableType); + } + flavors.forEach(flavor => queryParams.append("flavors", flavor)) + + return this.httpClient.get(`/package-manager/api/core/packages/export/list-by-variable-value?${queryParams.toString()}`).catch(e => { + throw new FatalError(`Problem getting active packages by variable value: ${e}`); + }); + } + + public findActivePackagesByKeys(packageKeys: string[], withDependencies: boolean = false): Promise { + const queryParams = new URLSearchParams(); + + packageKeys.forEach(key => queryParams.append("packageKeys", key)) + queryParams.set("withDependencies", withDependencies.toString()); + + return this.httpClient.get(`/package-manager/api/core/packages/export/list-by-keys?${queryParams.toString()}`).catch(e => { + throw new FatalError(`Problem getting active packages by keys: ${e}`); + }); + } + + public exportPackages(packageKeys: string[], withDependencies: boolean = false): Promise { + const queryParams = new URLSearchParams(); + packageKeys.forEach(packageKey => queryParams.append("packageKeys", packageKey)); + queryParams.set("withDependencies", withDependencies.toString()); + + return this.httpClient.getFile(`/package-manager/api/core/packages/export/batch?${queryParams.toString()}`).catch(e => { + throw new FatalError(`Problem exporting packages: ${e}`); + }); + } + + public importPackages(data: FormData, overwrite: boolean): Promise { + return this.httpClient.postFile( + "/package-manager/api/core/packages/import/batch", + data, + {overwrite} + ); + } + + public findVariablesWithValuesByPackageKeysAndVersion(packagesByKeyAndVersion: PackageKeyAndVersionPair[]): Promise { + return this.httpClient.post("/package-manager/api/core/packages/export/batch/variables-with-assignments", packagesByKeyAndVersion).catch(e => { + throw new FatalError(`Problem exporting package variables: ${e}`); + }) + } +} diff --git a/src/commands/studio/api/compute-pool-api.ts b/src/commands/studio/api/compute-pool-api.ts new file mode 100644 index 00000000..35044527 --- /dev/null +++ b/src/commands/studio/api/compute-pool-api.ts @@ -0,0 +1,19 @@ +import {StudioComputeNodeDescriptor} from "../interfaces/package-manager.interfaces"; +import { HttpClient } from "../../../core/http/http-client"; +import { Context } from "../../../core/command/cli-context"; + +export class ComputePoolApi { + + private httpClient: HttpClient; + + constructor(context: Context) { + this.httpClient = context.httpClient; + } + + public async findAllDataModelsDetails(): Promise { + return this.httpClient.get("/package-manager/api/compute-pools/data-models/details") + .catch(e => { + return null; + }); + } +} diff --git a/src/commands/studio/api/node-api.ts b/src/commands/studio/api/node-api.ts new file mode 100644 index 00000000..d73be9c5 --- /dev/null +++ b/src/commands/studio/api/node-api.ts @@ -0,0 +1,26 @@ +import {ContentNodeTransport} from "../interfaces/package-manager.interfaces"; +import { HttpClient } from "../../../core/http/http-client"; +import { Context } from "../../../core/command/cli-context"; +import { FatalError } from "../../../core/utils/logger"; + +export class NodeApi { + + private httpClient: HttpClient; + + constructor(context: Context) { + this.httpClient = context.httpClient; + } + + public async findAllNodesOfType(assetType?: string): Promise { + return this.httpClient.get(`/package-manager/api/nodes?assetType=${assetType}`) + .catch(e => { + throw new FatalError(`Problem getting nodes of type ${assetType}: ${e}`); + }); + } + + public async findOneByKeyAndRootNodeKey(packageKey: string, nodeKey: string): Promise { + return this.httpClient.get(`/package-manager/api/nodes/${packageKey}/${nodeKey}`).catch(e => { + return null; + }); + } +} diff --git a/src/commands/studio/api/package-api.ts b/src/commands/studio/api/package-api.ts new file mode 100644 index 00000000..8a31097d --- /dev/null +++ b/src/commands/studio/api/package-api.ts @@ -0,0 +1,91 @@ +import { + ActivatePackageTransport, + ContentNodeTransport, + PackageHistoryTransport, PackageManagerVariableType, PackageWithVariableAssignments, +} from "../interfaces/package-manager.interfaces"; +import { HttpClient } from "../../../core/http/http-client"; +import { Context } from "../../../core/command/cli-context"; +import { FatalError } from "../../../core/utils/logger"; + +export class PackageApi { + + private httpClient: HttpClient; + + constructor(context: Context) { + this.httpClient = context.httpClient; + } + + public async findAllPackages(): Promise { + return this.httpClient.get("/package-manager/api/packages").catch(e => { + throw new FatalError(`Problem getting packages: ${e}`); + }); + } + + public async exportPackage(rootPackageKey: string, version?: string, excludeActionFlows?: boolean): Promise { + const queryParams = new URLSearchParams(); + queryParams.set("newKey", rootPackageKey); + queryParams.set("version", version ?? ""); + queryParams.set("excludeActionFlows", excludeActionFlows ? "true" : "false"); + + return await this.httpClient.downloadFile(`/package-manager/api/packages/${rootPackageKey}/export?${queryParams.toString()}`).catch(e => { + throw new FatalError(`Package ${rootPackageKey}_${version} failed to export.`) + }); + } + + public async findAllPackagesWithVariableAssignments(type: PackageManagerVariableType): Promise { + const queryParams = new URLSearchParams(); + if (type) { + queryParams.set("type", type); + } + + return this.httpClient.get(`/package-manager/api/packages/with-variable-assignments?${queryParams.toString()}`).catch(e => { + throw new FatalError(`Problem getting variables of packages: : ${e}`); + }); + } + + public async findLatestVersionById(nodeId: string): Promise { + return this.httpClient.get(`/package-manager/api/packages/${nodeId}/latest-version`).catch(e => { + throw new FatalError(`Problem getting latest version of package: ${e}`); + }); + } + + public async findActiveVersionById(nodeId: string): Promise { + return this.httpClient.get(`/package-manager/api/packages/${nodeId}/active`).catch(e => { + throw new FatalError(`Problem getting latest version of package: ${e}`); + }); + } + + public async findActiveVersionByIds(nodeIds: string[]): Promise { + return this.httpClient.post("/package-manager/api/packages/active/by-ids", nodeIds).catch(e => { + throw new FatalError(`Problem getting latest version of packages: ${e}`); + }); + } + + public async findNextVersion(nodeId: string): Promise { + return this.httpClient.get(`/package-manager/api/packages/${nodeId}/next-version`).catch(e => { + throw new FatalError(`Problem getting latest version of package: ${e}`); + }); + } + + public async importPackage(nodeContent: any, spaceId: string, overwrite: boolean, excludeActionFlows?: boolean): Promise { + await this.httpClient.postFile("/package-manager/api/packages/import", nodeContent, { + spaceId: spaceId, + overwrite: overwrite, + excludeActionFlows: excludeActionFlows + }).catch(e => { + throw new FatalError(`Problem importing package: ${e}`); + }); + } + + public async movePackageToSpace(nodeId: string, spaceId: string): Promise { + await this.httpClient.put(`/package-manager/api/packages/${nodeId}/move/${spaceId}`, {}).catch(e => { + throw new FatalError(`Problem moving package: ${e}`); + }); + } + + public async publishPackage(activatePackage: ActivatePackageTransport): Promise { + await this.httpClient.post(`/package-manager/api/packages/${activatePackage.packageKey}/activate`, activatePackage).catch(e => { + throw new FatalError(`Problem activating package with key ${activatePackage.packageKey}: ${e}`); + }); + } +} diff --git a/src/commands/studio/api/package-dependencies-api.ts b/src/commands/studio/api/package-dependencies-api.ts new file mode 100644 index 00000000..9fb84be8 --- /dev/null +++ b/src/commands/studio/api/package-dependencies-api.ts @@ -0,0 +1,45 @@ +import {PackageDependencyTransport} from "../interfaces/package-manager.interfaces"; +import { HttpClient } from "../../../core/http/http-client"; +import { Context } from "../../../core/command/cli-context"; +import { FatalError } from "../../../core/utils/logger"; + +export class PackageDependenciesApi { + + private httpClient: HttpClient; + + constructor(context: Context) { + this.httpClient = context.httpClient; + } + + public async findDependenciesOfPackage(nodeId: string, draftId: string): Promise { + return this.httpClient.get(`/package-manager/api/package-dependencies/${nodeId}/by-root-draft-id/${draftId}`) + .catch(e=> { + throw new FatalError(`Problem getting dependencies of package: ${e}`); + }); + } + + public async findPackageDependenciesByIds(nodeDraftIdMap: Map): Promise> { + return await this.httpClient.post("/package-manager/api/package-dependencies/by-ids", Object.fromEntries(nodeDraftIdMap)) + .catch(e=> { + throw new FatalError(`Problem getting dependencies of package: ${e}`); + }); + } + + public async updatePackageDependency(nodeId: string, packageDependency: PackageDependencyTransport): Promise { + await this.httpClient.put(`/package-manager/api/package-dependencies/${nodeId}/dependency/by-key/${packageDependency.key}`, packageDependency).catch(e => { + throw new FatalError(`Problem updating package dependency: ${e}`); + }); + } + + public async createDependencies(packageId: string, packageDependency: PackageDependencyTransport[]): Promise { + await this.httpClient.post(`/package-manager/api/package-dependencies/${packageId}`, packageDependency).catch(e => { + throw new FatalError(`Problem updating package dependency: ${e}`); + }); + } + + public async deleteDependency(packageId: string, packageDependencyKey: string): Promise { + await this.httpClient.delete(`/package-manager/api/package-dependencies/${packageId}/dependency/by-key/${packageDependencyKey}`).catch(e => { + throw new FatalError(`Problem updating package dependency: ${e}`); + }); + } +} diff --git a/src/commands/studio/api/space-api.ts b/src/commands/studio/api/space-api.ts new file mode 100644 index 00000000..641668cd --- /dev/null +++ b/src/commands/studio/api/space-api.ts @@ -0,0 +1,31 @@ +import { HttpClient } from "../../../core/http/http-client"; +import { Context } from "../../../core/command/cli-context"; +import { SpaceTransport } from "../interfaces/space.interface"; +import { FatalError } from "../../../core/utils/logger"; + +export class SpaceApi { + + private httpClient: HttpClient; + + constructor(context: Context) { + this.httpClient = context.httpClient; + } + + public async findOne(spaceId: string): Promise { + return this.httpClient.get(`/package-manager/api/spaces/${spaceId}`).catch(e => { + throw new FatalError(`Problem getting space: ${spaceId} ${e}`); + }); + } + + public async findAllSpaces(): Promise { + return this.httpClient.get("/package-manager/api/spaces").catch(e => { + throw new FatalError(`Problem getting spaces: ${e}`); + }); + } + + public async createSpace(space: SpaceTransport): Promise { + return this.httpClient.post("/package-manager/api/spaces", space).catch(e => { + throw new FatalError(`Problem space creation: ${e}`); + }); + } +} diff --git a/src/commands/studio/api/variables-api.ts b/src/commands/studio/api/variables-api.ts new file mode 100644 index 00000000..8f517c66 --- /dev/null +++ b/src/commands/studio/api/variables-api.ts @@ -0,0 +1,45 @@ +import { + ContentNodeTransport, + VariablesAssignments +} from "../interfaces/package-manager.interfaces"; +import {URLSearchParams} from "url"; +import { variableAssignmentApis } from "../interfaces/variable-assignment-apis.constants"; +import { HttpClient } from "../../../core/http/http-client"; +import { Context } from "../../../core/command/cli-context"; +import { FatalError } from "../../../core/utils/logger"; + +export class VariablesApi { + private static readonly ASSIGNMENT_APIS = variableAssignmentApis; + private httpClient: HttpClient; + + constructor(context: Context) { + this.httpClient = context.httpClient; + } + + public async assignVariableValues(packageKey: string, variablesAssignments: VariablesAssignments[]): Promise { + return this.httpClient.post(`/package-manager/api/nodes/by-package-key/${packageKey}/variables/values`, variablesAssignments).catch(e => { + throw new FatalError(`Problem updating variables of package ${packageKey}: ${e}`); + }); + } + + public async getCandidateAssignments(type: string, params: URLSearchParams): Promise { + if (!VariablesApi.ASSIGNMENT_APIS[type]) { + throw new FatalError(`Variable type ${type} not supported.`); + } + + const apiUrl: string = VariablesApi.ASSIGNMENT_APIS[type].url + (params.toString().length ? `?${params.toString()}` : ""); + + return this.httpClient.get(apiUrl).catch(e => { + throw new FatalError(`Problem getting variables assignment values for type ${type}: ${e}`); + }); + } + + public getRuntimeVariableValues(packageKey: string, appMode: string): Promise { + const queryParams = new URLSearchParams(); + queryParams.set("appMode", appMode); + + return this.httpClient.get(`/package-manager/api/nodes/by-package-key/${packageKey}/variables/runtime-values?${queryParams.toString()}`).catch(e => { + throw new FatalError(`Problem getting runtime variables of package ${packageKey}: ${e}`); + }); + } +} diff --git a/src/commands/studio/command-service/asset-command.service.ts b/src/commands/studio/command-service/asset-command.service.ts new file mode 100644 index 00000000..ff560e3c --- /dev/null +++ b/src/commands/studio/command-service/asset-command.service.ts @@ -0,0 +1,36 @@ +import { ContentService } from "../../../core/http/http-shared/content.service"; +import { Context } from "../../../core/command/cli-context"; +import { AssetManagerFactory } from "../manager/asset.manager-factory"; +import { AssetService } from "../service/asset-service"; + +export class AssetCommandService { + private contentService = new ContentService(); + private assetManagerFactory: AssetManagerFactory; + + private assetService: AssetService; + + constructor(context: Context) { + this.assetManagerFactory = new AssetManagerFactory(context); + this.assetService = new AssetService(context); + } + + public async pullAsset(key: string): Promise { + await this.contentService.pull(this.assetManagerFactory.createManager(key)); + } + + public async pushAsset(fileName: string, packageKey: string): Promise { + await this.contentService.push(this.assetManagerFactory.createManager(null, fileName, packageKey)); + } + + public async pushAssets(packageKey: string): Promise { + await this.contentService.batchPush(this.assetManagerFactory.createManagers(packageKey)); + } + + public async listAssets(jsonResponse: boolean, assetType: string): Promise { + if (jsonResponse) { + await this.assetService.findAndExportAllAssets(assetType); + } else { + await this.assetService.listAssets(assetType); + } + } +} diff --git a/src/commands/studio/command-service/package-command.service.ts b/src/commands/studio/command-service/package-command.service.ts new file mode 100644 index 00000000..e3b92ef7 --- /dev/null +++ b/src/commands/studio/command-service/package-command.service.ts @@ -0,0 +1,58 @@ +import { ContentService } from "../../../core/http/http-shared/content.service"; +import { Context } from "../../../core/command/cli-context"; +import { PackageManagerFactory } from "../manager/package.manager-factory"; +import { PackageService } from "../service/package.service"; + +export class PackageCommandService { + private contentService = new ContentService(); + private packageManagerFactory: PackageManagerFactory; + + private packageService: PackageService; + + constructor(context: Context) { + this.packageManagerFactory = new PackageManagerFactory(context); + this.packageService = new PackageService(context); + } + + public async pullPackage( + key: string, + store: boolean, + newKey: string, + draft: boolean + ): Promise { + await this.contentService.pullFile( + this.packageManagerFactory.createPullManager(key, store, newKey, draft) + ); + } + + public async pushPackage( + spaceKey: string, + fileName: string, + newKey: string, + overwrite: boolean + ): Promise { + await this.contentService.push( + this.packageManagerFactory.createPushManager(spaceKey, fileName, newKey, overwrite) + ); + } + + public async pushPackages(spaceKey: string): Promise { + await this.contentService.batchPush(this.packageManagerFactory.createPushManagers(spaceKey)); + } + + public async listPackages(jsonResponse: boolean, includeDependencies: boolean, packageKeys: string[]): Promise { + if (jsonResponse) { + await this.packageService.findAndExportListOfAllPackages(includeDependencies, packageKeys ?? []); + } else { + await this.packageService.listPackages(); + } + } + + public async batchExportPackages(packageKeys: string[], includeDependencies: boolean, excludeActionFlows?: boolean): Promise { + await this.packageService.batchExportPackages(packageKeys, includeDependencies, excludeActionFlows); + } + + public async batchImportPackages(spaceMappings: string[], dataModelMappingsFilePath: string, exportedPackagesFile: string, overwrite: boolean, excludeActionFlows?: boolean): Promise { + await this.packageService.batchImportPackages(spaceMappings ?? [], dataModelMappingsFilePath, exportedPackagesFile, overwrite, excludeActionFlows); + } +} diff --git a/src/commands/studio/command-service/space-command.service.ts b/src/commands/studio/command-service/space-command.service.ts new file mode 100644 index 00000000..b12c85f1 --- /dev/null +++ b/src/commands/studio/command-service/space-command.service.ts @@ -0,0 +1,16 @@ +import { ContentService } from "../../../core/http/http-shared/content.service"; +import { SpaceManagerFactory } from "../manager/space.manager-factory"; +import { Context } from "../../../core/command/cli-context"; + +export class SpaceCommandService { + private contentService = new ContentService(); + private spaceManagerFactory: SpaceManagerFactory; + + constructor(context: Context) { + this.spaceManagerFactory = new SpaceManagerFactory(context); + } + + public async listSpaces(jsonResponse: boolean): Promise { + await this.contentService.findAll(this.spaceManagerFactory.createListManager(jsonResponse)); + } +} diff --git a/src/commands/studio/command-service/widget.command.ts b/src/commands/studio/command-service/widget.command.ts new file mode 100644 index 00000000..ef58409d --- /dev/null +++ b/src/commands/studio/command-service/widget.command.ts @@ -0,0 +1,38 @@ +import { execSync } from "child_process"; +import { GracefulError, logger } from "../../../core/utils/logger"; +import * as fs from "node:fs"; +import * as path from "path"; +import { ContentService } from "../../../core/http/http-shared/content.service"; +import { Context } from "../../../core/command/cli-context"; +import { WidgetManagerFactory } from "../manager/widget.manager-factory"; + +export class WidgetCommand { + private contentService = new ContentService(); + private widgetManagerFactory: WidgetManagerFactory; + + constructor(context: Context) { + this.widgetManagerFactory = new WidgetManagerFactory(context); + } + + public async pushWidget(tenantIndependent: boolean, userSpecific: boolean): Promise { + await this.contentService.push(this.widgetManagerFactory.createManager(tenantIndependent, userSpecific)); + await this.pushToAwsIfAuthorized(); + } + + private async pushToAwsIfAuthorized(): Promise { + if (process.env.AWS_ACCESS_KEY_ID_CDN && process.env.AWS_SECRET_ACCESS_KEY_CDN) { + try { + const dir = path.resolve(process.cwd()); + const pushToS3stdout = execSync( + `aws s3 cp ${dir} s3://celonis-static-origin/static/package-manager/ --recursive --exclude="*.map" --exclude="*.yaml" --profile default` + ).toString("utf-8"); + logger.info(pushToS3stdout); + } catch (error) { + logger.error(new GracefulError(error.stderr?.toString() || error.message)); + } + } + + const zipFileName = path.resolve(process.cwd(), "output.zip"); + fs.unlinkSync(zipFileName); + } +} diff --git a/src/commands/studio/interfaces/batch-export-import.constants.ts b/src/commands/studio/interfaces/batch-export-import.constants.ts new file mode 100644 index 00000000..a0c815ea --- /dev/null +++ b/src/commands/studio/interfaces/batch-export-import.constants.ts @@ -0,0 +1,11 @@ +export enum BatchExportImportConstants { + STUDIO_FILE_NAME = "studio.json", + VARIABLES_FILE_NAME = "variables.json", + MANIFEST_FILE_NAME = "manifest.json", + STUDIO = "STUDIO", + APP_MODE_VIEWER = "VIEWER", + ZIP_EXTENSION = ".zip", + JSON_EXTENSION = ".json", + NODES_FOLDER_NAME = "nodes/", + SCENARIO_NODE = "SCENARIO" +} \ No newline at end of file diff --git a/src/commands/studio/interfaces/batch-export-node.interfaces.ts b/src/commands/studio/interfaces/batch-export-node.interfaces.ts new file mode 100644 index 00000000..65f829e9 --- /dev/null +++ b/src/commands/studio/interfaces/batch-export-node.interfaces.ts @@ -0,0 +1,28 @@ +import { + ContentNodeTransport, + PackageDependencyTransport, + PackageHistoryTransport, + StudioComputeNodeDescriptor, + VariablesAssignments +} from "./package-manager.interfaces"; +import { SpaceTransport } from "./space.interface"; + +export interface BatchExportNodeTransport extends ContentNodeTransport { + workingDraftId: string; + activatedDraftId: string; + version?: PackageHistoryTransport; + dependencies?: PackageDependencyTransport[]; + datamodels?: StudioComputeNodeDescriptor[]; + variables?: VariablesAssignments[]; + space?: SpaceTransport; +} + +export interface PackageAndAssetTransport { + rootNode: ContentNodeTransport, + nodes: ContentNodeTransport[] +} + +export interface SpaceMappingTransport { + packageKey: string, + spaceId: string +} \ No newline at end of file diff --git a/src/commands/studio/interfaces/manifest.interface.ts b/src/commands/studio/interfaces/manifest.interface.ts new file mode 100644 index 00000000..b3d42261 --- /dev/null +++ b/src/commands/studio/interfaces/manifest.interface.ts @@ -0,0 +1,31 @@ +import {VariablesAssignments} from "./package-manager.interfaces"; + +export interface ManifestNodeTransport { + packageKey: string; + packageId: string, + variables: ManifestVariable[], + space: ManifestSpace, + dependenciesByVersion: Map; +} + +export interface ManifestVariable extends VariablesAssignments { + dataPoolName?: string, + dataModelName?: string +} + +export interface ManifestSpace { + spaceName: string, + spaceIcon: string +} + +export interface ManifestDependency { + id: string; + key: string; + name: string; + version: string; + rootNodeId: string; + external: boolean + draftId: string; + updateAvailable: boolean; + deleted: boolean; +} diff --git a/src/commands/studio/interfaces/package-export.interfaces.ts b/src/commands/studio/interfaces/package-export.interfaces.ts new file mode 100644 index 00000000..be73b97b --- /dev/null +++ b/src/commands/studio/interfaces/package-export.interfaces.ts @@ -0,0 +1,82 @@ +import {StudioComputeNodeDescriptor, VariableDefinition, VariablesAssignments} from "./package-manager.interfaces"; +import { SpaceTransport } from "./space.interface"; + +export interface DependencyTransport { + key: string; + version: string; +} + +export interface PackageExportTransport { + id: string; + key: string; + name: string; + changeDate: string; + activatedDraftId: string; + workingDraftId: string; + flavor: string; + version: string; + dependencies: DependencyTransport[]; + spaceId?: string; + datamodels?: StudioComputeNodeDescriptor[]; +} + +export interface PackageManifestTransport { + packageKey: string; + flavor: string; + activeVersion: string; + dependenciesByVersion: Map; +} + +export interface VariableExportTransport { + key: string; + value: any; + type: string; + metadata: object; +} + +export interface VariableManifestTransport { + packageKey: string; + version: string; + variables?: VariableExportTransport[]; +} + +export interface PackageKeyAndVersionPair { + packageKey: string; + version: string; +} + +export interface NodeExportTransport { + key: string; + parentNodeKey: string; + name: string; + type: string; + exportSerializationType: string; + configuration: NodeConfiguration; + schemaVersion: number; + + spaceId: string; + + invalidContent?: boolean; + serializedDocument?: Buffer; +} + +export interface NodeConfiguration { + variables?: VariableDefinition[]; + [key: string]: any; +} + +export interface StudioPackageManifest { + packageKey: string; + space: Partial; + runtimeVariableAssignments: VariablesAssignments[]; +} + +export interface PackageVersionImport { + oldVersion: string; + newVersion: string; +} + +export interface PostPackageImportData { + packageKey: string; + importedVersions: PackageVersionImport[]; +} \ No newline at end of file diff --git a/src/commands/studio/interfaces/package-manager.interfaces.ts b/src/commands/studio/interfaces/package-manager.interfaces.ts new file mode 100644 index 00000000..07b3eb1c --- /dev/null +++ b/src/commands/studio/interfaces/package-manager.interfaces.ts @@ -0,0 +1,99 @@ +export interface AssetMetadataTransport { + hidden: boolean; +} + +export interface ContentNodeTransport { + id: string; + key: string; + name: string; + rootNodeKey: string; + workingDraftId: string; + activatedDraftId: string; + rootNodeId: string; + assetMetadataTransport: AssetMetadataTransport; + spaceId: string; +} + +export interface ActivatePackageTransport { + packageKey: string; + version: string; + publishMessage: string; + nodeIdsToExclude: string[]; +} + +export interface DataModelTransport { + id: string; + name: string, + poolId: string; +} + +export interface PackageDependencyTransport { + id: string; + key: string; + name: string; + version: string; + rootNodeId: string; + external: boolean + draftId: string; + updateAvailable: boolean; + deleted: boolean; +} + +export interface AssetMetadataTransport { + hidden: boolean; +} + +export interface PackageWithVariableAssignments { + id: string; + key: string; + name: string; + createdBy: string; + spaceId: string; + variableAssignments: VariablesAssignments[] +} + +export interface VariablesAssignments { + key: string; + value: object; + type: string; +} + +export interface VariableDefinition { + key: string; + type: PackageManagerVariableType; + description?: string; + source?: string; + runtime?: boolean; + metadata?: object; +} + +export enum PackageManagerVariableType { + DATA_MODEL="DATA_MODEL", + CONNECTION="CONNECTION", + ASSIGNMENT_RULE="ASSIGNMENT_RULE", + PLAIN_TEXT= "PLAIN_TEXT" +} + +export interface PackageHistoryTransport { + id: string; + key: string; + name: string; + version: string; +} + +export interface StudioDataModelTransport { + node: StudioComputeNodeDescriptor; + dataPool: ComputePoolTransport; +} + +export interface StudioComputeNodeDescriptor { + name: string; + dataModelId: string; + poolId: string; +} + +export interface ComputePoolTransport { + id: string; + name: string; +} + diff --git a/src/commands/studio/interfaces/save-content-node.interface.ts b/src/commands/studio/interfaces/save-content-node.interface.ts new file mode 100644 index 00000000..2c514d0d --- /dev/null +++ b/src/commands/studio/interfaces/save-content-node.interface.ts @@ -0,0 +1,12 @@ +import { AssetMetadataTransport } from "./package-manager.interfaces"; + +export interface SaveContentNode { + key: string; + rootNodeKey: string; + name: string; + nodeType: string; + assetType: string; + serializedContent: string; + assetMetadataTransport: AssetMetadataTransport; +} + diff --git a/src/commands/studio/interfaces/space.interface.ts b/src/commands/studio/interfaces/space.interface.ts new file mode 100644 index 00000000..bd64fc53 --- /dev/null +++ b/src/commands/studio/interfaces/space.interface.ts @@ -0,0 +1,5 @@ +export interface SpaceTransport { + id: string; + name: string; + iconReference: string +} \ No newline at end of file diff --git a/src/commands/studio/interfaces/variable-assignment-apis.constants.ts b/src/commands/studio/interfaces/variable-assignment-apis.constants.ts new file mode 100644 index 00000000..17147666 --- /dev/null +++ b/src/commands/studio/interfaces/variable-assignment-apis.constants.ts @@ -0,0 +1,12 @@ +export const variableAssignmentApis: { [key: string]: VariableAssignmentApi } = { + DATA_MODEL: { + url: "/package-manager/api/compute-pools/pools-with-data-models" + }, + CONNECTION: { + url: "/process-automation-v2/api/connections" + } +}; + +export interface VariableAssignmentApi { + url: string; +} \ No newline at end of file diff --git a/src/commands/studio/manager/asset.manager-factory.ts b/src/commands/studio/manager/asset.manager-factory.ts new file mode 100644 index 00000000..12c732b7 --- /dev/null +++ b/src/commands/studio/manager/asset.manager-factory.ts @@ -0,0 +1,57 @@ +import * as fs from "fs"; +import * as path from "path"; +import { AssetManager } from "./asset.manager"; +import { FatalError, logger } from "../../../core/utils/logger"; +import { Context } from "../../../core/command/cli-context"; + +export class AssetManagerFactory { + + private readonly context: Context; + + constructor(context: Context) { + this.context = context; + } + + public createManager(key?: string, fileName?: string, packageKey?: string): AssetManager { + const assetManager = new AssetManager(this.context); + assetManager.key = key; + + if (fileName) { + assetManager.content = this.readFile(fileName); + } + + assetManager.packageKey = packageKey; + return assetManager; + } + + public createManagers(packageKey: string): AssetManager[] { + const filePaths = fs.readdirSync(process.cwd()); + + return filePaths + .filter(filePath => { + if (!this.isAssetFilePath(filePath)) { + return false; + } + + const file = fs.lstatSync(filePath); + return file.isFile(); + }) + .map(filePath => { + return this.createManager(null, filePath, packageKey); + }); + } + + private isAssetFilePath(filePath: string): boolean { + return ( + filePath.startsWith(AssetManager.ASSET_FILE_PREFIX) && + (filePath.endsWith("yml") || filePath.endsWith("yaml")) + ); + } + + private readFile(fileName: string): string { + if (!fs.existsSync(path.resolve(process.cwd(), fileName))) { + logger.error(new FatalError("The provided file does not exit")); + } + return fs.readFileSync(path.resolve(process.cwd(), fileName), { encoding: "utf-8" }); + } +} diff --git a/src/commands/studio/manager/asset.manager.ts b/src/commands/studio/manager/asset.manager.ts new file mode 100644 index 00000000..bc8f5a7b --- /dev/null +++ b/src/commands/studio/manager/asset.manager.ts @@ -0,0 +1,75 @@ +import { BaseManager } from "../../../core/http/http-shared/base.manager"; +import { Context } from "../../../core/command/cli-context"; +import { ManagerConfig } from "../../../core/http/http-shared/manager-config.interface"; +import { SaveContentNode } from "../interfaces/save-content-node.interface"; +import { parse, stringify } from "../../../core/utils/yaml"; + +export class AssetManager extends BaseManager { + public static ASSET_FILE_PREFIX = "asset_"; + private static BASE_URL = "/package-manager/api/nodes"; + + private static PUSH_URL = `${AssetManager.BASE_URL}/asset/import`; + private static PULL_URL = `${AssetManager.BASE_URL}/asset/export`; + + private _key: string; + private _content: string; + private _packageKey: string; + + constructor(context: Context) { + super(context); + } + + public get key(): string { + return this._key; + } + + public set key(value: string) { + this._key = value; + } + + public get content(): string { + return this._content; + } + public set content(value: string) { + this._content = value; + } + + public get packageKey(): string { + return this._packageKey; + } + + public set packageKey(value: string) { + this._packageKey = value; + } + + private get onlyKey(): string { + if (this.key) { + return this.key.split(".")[1]; + } + } + + public getConfig(): ManagerConfig { + return { + pushUrl: AssetManager.PUSH_URL, + pullUrl: `${AssetManager.PULL_URL}/${this.key}`, + exportFileName: `${AssetManager.ASSET_FILE_PREFIX}${this.onlyKey}.yml`, + onPushSuccessMessage: (data: any): string => { + return "Asset was pushed successfully. New key: " + data.rootWithKey; + }, + }; + } + + public getBody(): any { + return this.toNodeTransport(); + } + + private toNodeTransport(): SaveContentNode { + const asset = parse(this.content) as SaveContentNode; + asset.rootNodeKey = this.packageKey; + return asset; + } + + protected getSerializedFileContent(data: any): string { + return stringify(data); + } +} diff --git a/src/commands/studio/manager/package.manager-factory.ts b/src/commands/studio/manager/package.manager-factory.ts new file mode 100644 index 00000000..0cfe19de --- /dev/null +++ b/src/commands/studio/manager/package.manager-factory.ts @@ -0,0 +1,79 @@ +import * as fs from "fs"; +import * as path from "path"; +import { Context } from "../../../core/command/cli-context"; +import { PackageManager } from "./package.manager"; +import { FatalError, logger } from "../../../core/utils/logger"; + +export class PackageManagerFactory { + + private readonly context: Context; + + constructor(context: Context) { + this.context = context; + } + + public createPullManager(key: string, store?: boolean, newKey?: string, draft?: boolean): PackageManager { + return this.createManager(key, null, null, store, newKey, false, draft); + } + + public createPushManager(spaceKey: string, fileName: string, newKey?: string, overwrite?: boolean): PackageManager { + return this.createManager(null, spaceKey, fileName, false, newKey, overwrite, false); + } + + public createPushManagers(spaceKey: string): PackageManager[] { + const filePaths = fs.readdirSync(process.cwd()); + + return filePaths + .filter(filePath => { + if (!this.isPackageFilePath(filePath)) { + return false; + } + + const file = fs.lstatSync(filePath); + return file.isFile(); + }) + .map(filePath => { + return this.createPushManager(spaceKey, filePath); + }); + } + + public createManager( + key?: string, + spaceKey?: string, + fileName?: string, + store?: boolean, + newKey?: string, + overwrite?: boolean, + draft?: boolean, + ): PackageManager { + const packageManager = new PackageManager(this.context); + + if (fileName) { + packageManager.fileName = this.resolvePackageFilePath(fileName); + } + + packageManager.key = key; + packageManager.spaceKey = spaceKey; + packageManager.store = store; + packageManager.newKey = newKey; + packageManager.overwrite = overwrite; + packageManager.draft = draft; + + return packageManager; + } + + private isPackageFilePath(filePath: string): boolean { + return ( + filePath.startsWith(PackageManager.PACKAGE_FILE_PREFIX) && + filePath.endsWith(PackageManager.PACKAGE_FILE_EXTENSION) + ); + } + + private resolvePackageFilePath(fileName: string): string { + if (!fs.existsSync(path.resolve(process.cwd(), fileName))) { + logger.error(new FatalError("The provided file does not exit")); + } + + return path.resolve(process.cwd(), fileName); + } +} diff --git a/src/commands/studio/manager/package.manager.ts b/src/commands/studio/manager/package.manager.ts new file mode 100644 index 00000000..46e5f750 --- /dev/null +++ b/src/commands/studio/manager/package.manager.ts @@ -0,0 +1,143 @@ +import * as fs from "fs"; +import * as FormData from "form-data"; +import { BaseManager } from "../../../core/http/http-shared/base.manager"; +import { ManagerConfig } from "../../../core/http/http-shared/manager-config.interface"; +import { SaveContentNode } from "../interfaces/save-content-node.interface"; +import { logger } from "../../../core/utils/logger"; +import { Context } from "../../../core/command/cli-context"; + +export class PackageManager extends BaseManager { + public static PACKAGE_FILE_PREFIX = "package_"; + public static PACKAGE_FILE_EXTENSION = ".zip"; + + private static BASE_URL = "/package-manager/api/packages"; + + private static IMPORT_ENDPOINT_PATH = "import"; + private static EXPORT_ENDPOINT_PATH = "export"; + + private _key: string; + private _spaceKey: string; + private _fileName: string; + private _store: boolean; + private _newKey: string; + private _overwrite: boolean; + private _draft: boolean; + + constructor(context: Context) { + super(context); + } + + public get key(): string { + return this._key; + } + + public set key(value: string) { + this._key = value; + } + + public get spaceKey(): string { + return this._spaceKey; + } + + public set spaceKey(value: string) { + this._spaceKey = value; + } + + public get fileName(): string { + return this._fileName; + } + + public set fileName(value: string) { + this._fileName = value; + } + + public get store(): boolean { + return this._store; + } + + public set store(value: boolean) { + this._store = value; + } + + public get newKey(): string { + return this._newKey; + } + + public set newKey(value: string) { + this._newKey = value; + } + + public get overwrite(): boolean { + return this._overwrite; + } + + public set overwrite(value: boolean) { + this._overwrite = value; + } + + public get draft(): boolean { + return this._draft; + } + + public set draft(value: boolean) { + this._draft = value; + } + + public getConfig(): ManagerConfig { + return { + pushUrl: this.buildPushUrl(), + pullUrl: `${PackageManager.BASE_URL}/${this.key}/${PackageManager.EXPORT_ENDPOINT_PATH}?store=${ + this.store + }&draft=${this.draft}${this.newKey ? `&newKey=${this.newKey}` : ""}`, + findAllUrl: PackageManager.BASE_URL, + exportFileName: + PackageManager.PACKAGE_FILE_PREFIX + + (this.newKey ? this.newKey : this.key) + + PackageManager.PACKAGE_FILE_EXTENSION, + onPushSuccessMessage: (): string => "Package was pushed successfully.", + onFindAll: (data: SaveContentNode[]) => this.listPackages(data), + }; + } + + public getBody(): any { + const formData = new FormData(); + formData.append("package", fs.createReadStream(this.fileName)); + return formData; + } + + protected getSerializedFileContent(data: any): string { + return data; + } + + private buildPushUrl(): string { + this.validateOptions(); + const pushUrl = `${PackageManager.BASE_URL}/${PackageManager.IMPORT_ENDPOINT_PATH}`; + return this.getPushUrlWithParams(pushUrl); + } + + private listPackages(nodes: SaveContentNode[]): void { + nodes.forEach(node => { + logger.info(`${node.name} - Key: "${node.key}"`); + }); + } + + private validateOptions(): void { + if (this.newKey && this.overwrite) { + logger.error( + "You cannot overwrite a package and set a new key at the same time. Please use only one of the options." + ); + process.exit(); + } + } + + private getPushUrlWithParams(pushUrl: string): string { + const pushUrlWithParams = `${pushUrl}?spaceId=${this.spaceKey}&`; + if (this.newKey) { + return `${pushUrlWithParams}newKey=${this.newKey}`; + } + if (this.overwrite) { + return `${pushUrlWithParams}overwrite=${this.overwrite}`; + } + return pushUrlWithParams; + } +} diff --git a/src/commands/studio/manager/space.manager-factory.ts b/src/commands/studio/manager/space.manager-factory.ts new file mode 100644 index 00000000..c6160772 --- /dev/null +++ b/src/commands/studio/manager/space.manager-factory.ts @@ -0,0 +1,21 @@ +import { SpaceManager } from "./space.manager"; +import { Context } from "../../../core/command/cli-context"; + +export class SpaceManagerFactory { + + private readonly context: Context; + + constructor(context: Context) { + this.context = context; + } + + public createListManager(jsonResponse: boolean): SpaceManager { + return this.createManager(jsonResponse); + } + + public createManager(jsonResponse?: boolean): SpaceManager { + const spaceManager = new SpaceManager(this.context); + spaceManager.jsonResponse = jsonResponse; + return spaceManager; + } +} \ No newline at end of file diff --git a/src/commands/studio/manager/space.manager.ts b/src/commands/studio/manager/space.manager.ts new file mode 100644 index 00000000..3bdeaca9 --- /dev/null +++ b/src/commands/studio/manager/space.manager.ts @@ -0,0 +1,52 @@ +import {v4 as uuidv4} from "uuid"; +import { Context } from "../../../core/command/cli-context"; +import { BaseManager } from "../../../core/http/http-shared/base.manager"; +import { ManagerConfig } from "../../../core/http/http-shared/manager-config.interface"; +import { SpaceTransport } from "../interfaces/space.interface"; +import { logger } from "../../../core/utils/logger"; + +export class SpaceManager extends BaseManager { + + private static BASE_URL = "/package-manager/api/spaces"; + + private _jsonResponse: boolean; + + constructor(context: Context) { + super(context); + } + + public get jsonResponse(): boolean { + return this._jsonResponse; + } + + public set jsonResponse(value: boolean) { + this._jsonResponse = value; + } + + public getConfig(): ManagerConfig { + return { + findAllUrl: SpaceManager.BASE_URL, + onFindAll: (data: SpaceTransport[]) => this.listSpaces(data), + }; + } + + private listSpaces(nodes: SpaceTransport[]): void { + if (this.jsonResponse) { + const filename = uuidv4() + ".json"; + this.writeToFileWithGivenName(JSON.stringify(nodes, ["id", "name"]), filename); + logger.info(this.fileDownloadedMessage + filename); + } else { + nodes.forEach(node => { + logger.info(`${node.id} - Name: "${node.name}"`); + }); + } + } + + protected getBody(): object { + return {}; + } + + protected getSerializedFileContent(data: any): string { + return data; + } +} diff --git a/src/commands/studio/manager/widget.manager-factory.ts b/src/commands/studio/manager/widget.manager-factory.ts new file mode 100644 index 00000000..f58336d6 --- /dev/null +++ b/src/commands/studio/manager/widget.manager-factory.ts @@ -0,0 +1,95 @@ +import * as fs from "fs"; +import * as path from "path"; +import { WidgetManager } from "./widget.manager"; +import { FatalError, logger } from "../../../core/utils/logger"; +import { Context } from "../../../core/command/cli-context"; +import * as AdmZip from "adm-zip"; +import { parse } from "../../../core/utils/yaml"; + +export class WidgetManagerFactory { + + private readonly context: Context; + + constructor(context: Context) { + this.context = context; + } + + public createManager(tenantIndependent: boolean = false, userSpecific: boolean = false): WidgetManager { + const widgetManager = new WidgetManager(this.context); + widgetManager.content = this.readContent(); + widgetManager.tenantIndependent = tenantIndependent; + widgetManager.userSpecific = userSpecific; + return widgetManager; + } + + private readContent(): any { + const manifest = this.fetchManifest(); + + if (!manifest) { + logger.error(new FatalError("Missing manifest file.")); + } + + this.validateManifest(manifest); + + const zip = new AdmZip(); + const zipFileName = path.resolve(process.cwd(), "output.zip"); + zip.addLocalFolder(path.resolve(process.cwd())); + zip.writeZip(zipFileName); + return fs.createReadStream(path.resolve(process.cwd(), "output.zip")); + } + + public fetchManifest(): Manifest { + if (fs.existsSync(path.resolve(process.cwd(), "manifest.yaml"))) { + return parse(fs.readFileSync(path.resolve(process.cwd(), "manifest.yaml"), { encoding: "utf-8" })); + } + + if (fs.existsSync(path.resolve(process.cwd(), "manifest.yml"))) { + return parse(fs.readFileSync(path.resolve(process.cwd(), "manifest.yml"), { encoding: "utf-8" })); + } + + return null; + } + + public validateManifest(manifest: Manifest): void { + if (!manifest.bundle) { + logger.error(new FatalError("Missing 'bundle' attribute.")); + } + + if (!manifest.name) { + logger.error(new FatalError("Missing 'name' attribute.")); + } + + if (!manifest.key) { + logger.error(new FatalError("Missing 'key' attribute.")); + } + + if (!fs.existsSync(manifest.bundle)) { + logger.error(new FatalError("Missing bundle.")); + } + + if ( + fs.existsSync(path.resolve(process.cwd(), "assets")) && + !fs.existsSync(path.resolve(process.cwd(), "assets", "widgets", manifest.key)) + ) { + logger.error( + new FatalError( + "Assets directory does not exist. Assets should live under 'assets/widgets/" + manifest.key + "'." + ) + ); + } + } +} + +interface Manifest { + key: string; + name: string; + bundle: string; + version: string; + externalResource: string; + widgets: ManifestWidget[]; +} + +interface ManifestWidget { + widgetId: string; + name: string; +} diff --git a/src/commands/studio/manager/widget.manager.ts b/src/commands/studio/manager/widget.manager.ts new file mode 100644 index 00000000..9ae32c48 --- /dev/null +++ b/src/commands/studio/manager/widget.manager.ts @@ -0,0 +1,67 @@ +import * as FormData from "form-data"; +import { BaseManager } from "../../../core/http/http-shared/base.manager"; +import { Context } from "../../../core/command/cli-context"; +import { ManagerConfig } from "../../../core/http/http-shared/manager-config.interface"; + +export class WidgetManager extends BaseManager { + private static PACKAGE_MANAGER_BASE_URL = "/package-manager"; + private static WIDGET_API = "/api/widgets/upload"; + private static WIDGET_TENANT_INDEPENDENT_API = "/api/widgets/upload-tenant-independently"; + private static WIDGET_USER_SPECIFIC_API = "/api/widgets/upload-user"; + private _content: any; + private _tenantIndependent = false; + private _userSpecific = false; + + constructor(context: Context) { + super(context); + } + + public get content(): any { + return this._content; + } + + public set content(value: any) { + this._content = value; + } + + public get tenantIndependent(): any { + return this._tenantIndependent; + } + + public set tenantIndependent(value: any) { + this._tenantIndependent = value; + } + + public get userSpecific(): any { + return this._userSpecific; + } + + public set userSpecific(value: any) { + this._userSpecific = value; + } + + public getConfig(): ManagerConfig { + const baseUrl = WidgetManager.PACKAGE_MANAGER_BASE_URL; + const widgetUrl = this.userSpecific + ? WidgetManager.WIDGET_USER_SPECIFIC_API + : this.tenantIndependent + ? WidgetManager.WIDGET_TENANT_INDEPENDENT_API + : WidgetManager.WIDGET_API; + return { + pushUrl: `${baseUrl}${widgetUrl}`, + onPushSuccessMessage: (): string => { + return "Widget was pushed successfully"; + }, + }; + } + + public getBody(): any { + const formData = new FormData(); + formData.append("file", this.content); + return formData; + } + + protected getSerializedFileContent(data: any): string { + return data; + } +} diff --git a/src/commands/studio/module.ts b/src/commands/studio/module.ts index 27f715ad..1bea8e8f 100644 --- a/src/commands/studio/module.ts +++ b/src/commands/studio/module.ts @@ -4,12 +4,161 @@ import { Configurator, IModule } from "../../core/command/module-handler"; import { Context } from "../../core/command/cli-context"; +import { Command, OptionValues } from "commander"; +import { PackageCommandService } from "./command-service/package-command.service"; +import { AssetCommandService } from "./command-service/asset-command.service"; +import { WidgetCommand } from "./command-service/widget.command"; +import { SpaceCommandService } from "./command-service/space-command.service"; class Module extends IModule { register(context: Context, configurator: Configurator) { + const exportCommand = configurator.command("export"); + exportCommand.command("packages") + .description("Command to export all given packages") + .option("-p, --profile ", "Profile which you want to use to list packages") + .requiredOption("--packageKeys ", "Exports only given package keys") + .option("--includeDependencies", "Include variables and dependencies", "") + .option("--excludeActionFlows", "Don't export action flows") + .action(this.batchExportPackages); + + const importCommand = configurator.command("import"); + importCommand.command("packages") + .description("Command to import all given packages") + .option("-p, --profile ", "Profile which you want to use to list packages") + .option( + "--spaceMappings ", + "List of mappings for importing packages to different target spaces. Mappings should follow format 'packageKey:targetSpaceKey'" + ) + .option("--overwrite", "Flag to allow overwriting of packages") + .option("--excludeActionFlows", "Skip overwrite of action flows of package") + .option("--dataModelMappingsFile ", "DataModel variable mappings file path. If missing, variables will be mapped from manifest file.") + .requiredOption("-f, --file ", "Exported packages file (relative path)") + .action(this.batchImportPackages); + + const listCommand = configurator.command("list"); + listCommand.command("packages") + .description("Command to list all packages") + .option("-p, --profile ", "Profile which you want to use to list packages") + .option("--json", "Return response as json type", "") + .option("--includeDependencies", "Include variables and dependencies", "") + .option("--packageKeys ", "Lists only given package keys") + .action(this.listPackages); + + listCommand.command("spaces") + .description("Command to list all spaces") + .option("-p, --profile ", "Profile which you want to use to list spaces") + .option("--json", "Return response as json type", "") + .action(this.listSpaces); + + listCommand.command("assets") + .description("Command to list all assets") + .option("-p, --profile ", "Profile which you want to use to list assets") + .option("--json", "Return response as json type", "") + .option("--assetType ", "type of assets") + .action(this.listAssets); + + const pullCommand = configurator.command("pull"); + pullCommand.command("asset") + .description("Command to pull an asset from Studio") + .option("-p, --profile ", "Profile which you want to use to pull the asset") + .requiredOption("--key ", "Key of asset you want to pull") + .action(this.pullAsset); + + pullCommand.command("package") + .description("Command to pull a package") + .option("-p, --profile ", "Profile which you want to use to pull the package") + .requiredOption("--key ", "Key of the package you want to pull") + .option("--store", "Pull package with store deployment metadata") + .option("--newKey ", "Define a new key for your package") + .option("--draft", "Pull draft version of package") + .action(this.pullPackage); + + const pushCommand = configurator.command("push"); + pushCommand.command("asset") + .description("Command to push an asset to Studio") + .option("-p, --profile ", "Profile which you want to use to push the asset") + .requiredOption("-f, --file ", "The file you want to push") + .requiredOption("--package ", "Key of the package you want to push asset to") + .action(this.pushAsset); + + pushCommand.command("assets") + .description("Command to push assets to Studio") + .option("-p, --profile ", "Profile which you want to use to push the assets") + .requiredOption("--package ", "Key of the package you want to push assets to") + .action(this.pushAssets); + + pushCommand.command("package") + .description("Command to push a package to Studio") + .option("-p, --profile ", "Profile which you want to use to push the package") + .option("--newKey ", "Define a new key for your package") + .option("--overwrite", "Overwrite package and its assets") + .requiredOption("-f, --file ", "The file you want to push") + .requiredOption("--spaceKey ", "The key of the destination space") + .action(this.pushPackage); + + pushCommand.command("packages") + .description("Command to push packages to Studio") + .option("-p, --profile ", "Profile which you want to use to push the packages") + .requiredOption("--spaceKey ", "The key of the destination space") + .action(this.pushPackages); + + pushCommand.command("widget") + .description("Command to push a widget") + .option("-p, --profile ", "Profile which you want to use to push the widget") + .option("--tenantIndependent", "Upload widget tenant independently") + .option("--userSpecific", "Upload widget only for the user in the provided api token") + .option("--packageManager", "Upload widget to package manager (deprecated)") // Deprecated + .action(this.pushWidget); + } + + private async batchExportPackages(context: Context, command: Command, options: OptionValues): Promise { + await new PackageCommandService(context).batchExportPackages(options.packageKeys, options.includeDependencies, options.excludeActionFlows ?? false); + } + + private async batchImportPackages(context: Context, command: Command, options: OptionValues): Promise { + await new PackageCommandService(context).batchImportPackages(options.spaceMappings, options.dataModelMappingsFile, options.file, options.overwrite, options.excludeActionFlows); + } + + private async listPackages(context: Context, command: Command, options: OptionValues): Promise { + await new PackageCommandService(context).listPackages(options.json, options.includeDependencies, options.packageKeys); } + private async listSpaces(context: Context, command: Command, options: OptionValues): Promise { + await new SpaceCommandService(context).listSpaces(options.json); + } + + private async listAssets(context: Context, command: Command, options: OptionValues): Promise { + await new AssetCommandService(context).listAssets(options.json, options.assetType); + } + + private async pullAsset(context: Context, command: Command, options: OptionValues): Promise { + await new AssetCommandService(context).pullAsset(options.key); + } + + private async pullPackage(context: Context, command: Command, options: OptionValues): Promise { + await new PackageCommandService(context).pullPackage(options.key, !!options.store, options.newKey, !!options.draft); + } + + private async pushAsset(context: Context, command: Command, options: OptionValues): Promise { + await new AssetCommandService(context).pushAsset(options.file, options.package); + } + + private async pushAssets(context: Context, command: Command, options: OptionValues): Promise { + await new AssetCommandService(context).pushAssets(options.package); + } + + private async pushPackage(context: Context, command: Command, options: OptionValues): Promise { + await new PackageCommandService(context).pushPackage(options.spaceKey, options.file, options.newKey, options.overwrite); + } + + private async pushPackages(context: Context, command: Command, options: OptionValues): Promise { + await new PackageCommandService(context).pushPackages(options.spaceKey); + } + + private async pushWidget(context: Context, command: Command, options: OptionValues): Promise { + await new WidgetCommand(context).pushWidget(!!options.tenantIndependent, !!options.userSpecific); + } } export = Module; \ No newline at end of file diff --git a/src/commands/studio/service/asset-service.ts b/src/commands/studio/service/asset-service.ts new file mode 100644 index 00000000..d5fa1c81 --- /dev/null +++ b/src/commands/studio/service/asset-service.ts @@ -0,0 +1,33 @@ +import {v4 as uuidv4} from "uuid"; +import { Context } from "../../../core/command/cli-context"; +import { SaveContentNode } from "../interfaces/save-content-node.interface"; +import { AssetApi } from "../api/asset-api"; +import { logger } from "../../../core/utils/logger"; +import { fileService } from "../../../core/utils/file-service"; + +export class AssetService { + protected readonly fileDownloadedMessage = "File downloaded successfully. New filename: "; + + private assetApi: AssetApi; + + constructor(context: Context) { + this.assetApi = new AssetApi(context); + } + + public async listAssets(assetType: string): Promise { + const nodes = await this.assetApi.findAllAssets(assetType); + nodes.forEach(node => { + logger.info(`${node.name} - Key: "${node.key}"`); + }); + } + + public async findAndExportAllAssets(assetType: string): Promise { + const fieldsToInclude = ["key", "name", "assetType", "rootNodeKey", "activatedDraftId"]; + + const nodes: SaveContentNode[] = await this.assetApi.findAllAssets(assetType); + + const filename = uuidv4() + ".json"; + fileService.writeToFileWithGivenName(JSON.stringify(nodes, fieldsToInclude), filename); + logger.info(this.fileDownloadedMessage + filename); + } +} diff --git a/src/commands/studio/service/data-model.service.ts b/src/commands/studio/service/data-model.service.ts new file mode 100644 index 00000000..3d010f94 --- /dev/null +++ b/src/commands/studio/service/data-model.service.ts @@ -0,0 +1,26 @@ +import { PackageWithVariableAssignments, StudioComputeNodeDescriptor } from "../interfaces/package-manager.interfaces"; +import { Context } from "../../../core/command/cli-context"; +import { ComputePoolApi } from "../api/compute-pool-api"; + +export class DataModelService { + + private computePoolApi: ComputePoolApi; + + constructor(context: Context) { + this.computePoolApi = new ComputePoolApi(context); + } + + public async getDataModelDetailsForPackages(packagesWithDataModelVariables: PackageWithVariableAssignments[]): Promise> { + const dataModelsMap = new Map(); + const allAvailableDataModels = await this.computePoolApi.findAllDataModelsDetails(); + + for (const node of packagesWithDataModelVariables) { + const variablesOfPackage = packagesWithDataModelVariables.find(nodeWithVariablesAssignment => nodeWithVariablesAssignment.key === node.key)?.variableAssignments; + const dataModelIds = variablesOfPackage.filter(variable => variable.value).map(variable => variable.value.toString()); + + const assignedDataModels = allAvailableDataModels.filter(dataModel => dataModelIds.includes(dataModel.dataModelId)); + dataModelsMap.set(node.key, assignedDataModels); + } + return dataModelsMap; + } +} diff --git a/src/commands/studio/service/package.service.ts b/src/commands/studio/service/package.service.ts new file mode 100644 index 00000000..a3073642 --- /dev/null +++ b/src/commands/studio/service/package.service.ts @@ -0,0 +1,537 @@ +import {v4 as uuidv4} from "uuid"; +import * as fs from "fs"; +import AdmZip = require("adm-zip"); +import * as path from "path"; +import {tmpdir} from "os"; +import * as FormData from "form-data"; +import { Context } from "../../../core/command/cli-context"; +import { PackageApi } from "../api/package-api"; +import { FatalError, logger } from "../../../core/utils/logger"; +import { + ContentNodeTransport, + PackageDependencyTransport, + PackageManagerVariableType, +} from "../interfaces/package-manager.interfaces"; +import { DataPoolInstallVersionReport } from "../../data-pipeline/data-pool/data-pool-manager.interfaces"; +import { FileService, fileService } from "../../../core/utils/file-service"; +import { BatchExportNodeTransport } from "../interfaces/batch-export-node.interfaces"; +import { ManifestDependency, ManifestNodeTransport } from "../interfaces/manifest.interface"; +import { parse } from "../../../core/utils/yaml"; +import { stringify } from "yaml"; +import { SemanticVersioning } from "../utils/semantic-versioning"; +import { SpaceTransport } from "../interfaces/space.interface"; +import { NodeApi } from "../api/node-api"; +import { PackageDependenciesApi } from "../api/package-dependencies-api"; +import { DataModelService } from "./data-model.service"; +import { VariableService } from "./variable.service"; +import { SpaceService } from "./space.service"; + +export class PackageService { + protected readonly fileDownloadedMessage = "File downloaded successfully. New filename: "; + + private packageApi: PackageApi; + private nodeApi: NodeApi; + private packageDependenciesApi: PackageDependenciesApi; + + private dataModelService: DataModelService; + private variableService: VariableService; + private spaceService: SpaceService; + + constructor(context: Context) { + this.packageApi = new PackageApi(context); + this.nodeApi = new NodeApi(context); + this.packageDependenciesApi = new PackageDependenciesApi(context); + this.dataModelService = new DataModelService(context); + this.variableService = new VariableService(context); + this.spaceService = new SpaceService(context); + } + + public async listPackages(): Promise { + const nodes = await this.packageApi.findAllPackages(); + nodes.forEach(node => { + logger.info(`${node.name} - Key: "${node.key}"`); + }); + } + + public async findAndExportListOfAllPackages(includeDependencies: boolean, packageKeys: string[]): Promise { + const fieldsToInclude = ["key", "name", "changeDate", "activatedDraftId", "workingDraftId", "spaceId"]; + + let nodesListToExport: BatchExportNodeTransport[] = await this.packageApi.findAllPackages(); + if (packageKeys.length > 0) { + nodesListToExport = nodesListToExport.filter(node => packageKeys.includes(node.rootNodeKey)); + } + + if (includeDependencies) { + fieldsToInclude.push("type", "value", "dependencies", "id", "updateAvailable", "version", "poolId", "node", "dataModelId", "dataPool", "datamodels"); + const unPublishedNodes = nodesListToExport.filter(node => !node.activatedDraftId); + let publishedNodes = nodesListToExport.filter(node => node.activatedDraftId); + publishedNodes = await this.getNodesWithActiveVersion(publishedNodes); + nodesListToExport = [...publishedNodes, ...unPublishedNodes]; + + const packageWithDataModelVariableAssignments = await this.variableService.getVariableAssignmentsForNodes(PackageManagerVariableType.DATA_MODEL); + const dataModelDetailsByNode = await this.dataModelService.getDataModelDetailsForPackages(packageWithDataModelVariableAssignments); + nodesListToExport.forEach(node => { + node.datamodels = dataModelDetailsByNode.get(node.key); + }); + + const draftIdByNodeId = new Map(); + nodesListToExport.forEach(node => draftIdByNodeId.set(node.workingDraftId, node.id)); + + const dependenciesByPackageIds = await this.getPackagesWithDependencies(draftIdByNodeId); + + nodesListToExport = nodesListToExport.map(nodeToExport => { + nodeToExport.dependencies = dependenciesByPackageIds[nodeToExport.workingDraftId] ?? []; + return nodeToExport; + }) + } + this.exportListOfPackages(nodesListToExport, fieldsToInclude); + } + + public async batchImportPackages(spaceMappings: string[], dataModelMappingsFilePath: string, exportedPackagesFile: string, overwrite: boolean, excludeActionFlows: boolean): Promise { + exportedPackagesFile = exportedPackagesFile + (exportedPackagesFile.includes(".zip") ? "" : ".zip"); + const zip = new AdmZip(exportedPackagesFile); + const importedFilePath = path.resolve(tmpdir(), "export_" + uuidv4()); + await fs.mkdirSync(importedFilePath); + await zip.extractAllTo(importedFilePath); + + const manifestNodes = await this.readManifestFile(importedFilePath); + + if (!overwrite) { + const allTargetPackages = await this.packageApi.findAllPackages(); + const manifestNodeKeys = manifestNodes.map(node => node.packageKey); + const packagesWithDraftChanges = allTargetPackages + .filter(node => manifestNodeKeys.includes(node.key) && node.workingDraftId !== node.activatedDraftId) + .map(node => node.key) + .join(", "); + if (!!packagesWithDraftChanges) { + throw new FatalError(`Failed to import. Cannot overwrite packages with key(s) ${packagesWithDraftChanges}`) + } + } + + let dmTargetIdsBySourceIds: Map = new Map(); + if (dataModelMappingsFilePath) { + const dataModelMappings: DataPoolInstallVersionReport = await fileService.readFileToJson(dataModelMappingsFilePath); + dmTargetIdsBySourceIds = new Map(Object.entries(dataModelMappings.dataModelIdMappings)); + } + + manifestNodes.map(node => node.dependenciesByVersion = new Map(Object.entries(node.dependenciesByVersion))); + + const importedVersionsByNodeKey = new Map(); + const sourceToTargetVersionsByNodeKey = new Map>(); + + for (const node of manifestNodes) { + for (const version of node.dependenciesByVersion.keys()) { + await this.checkNodeForCircularDependency(node, version, manifestNodes, []); + } + } + + const customSpacesMap: Map = new Map(); + spaceMappings.forEach(spaceMap => { + const packageAndSpaceid = spaceMap.split(":"); + customSpacesMap.set(packageAndSpaceid[0], packageAndSpaceid[1]) + }) + + const draftIdsByPackageKeyAndVersion = new Map(); + for (const node of manifestNodes) { + await this.importPackage(node, manifestNodes, sourceToTargetVersionsByNodeKey, customSpacesMap, dmTargetIdsBySourceIds, importedVersionsByNodeKey, draftIdsByPackageKeyAndVersion, importedFilePath, excludeActionFlows) + } + } + + private async checkNodeForCircularDependency(node: ManifestNodeTransport, version: string, manifestNodes: ManifestNodeTransport[], iteratedNodeAndVersions: string[]): Promise { + const dependencies = node.dependenciesByVersion.get(version); + + if (iteratedNodeAndVersions.includes(`${node.packageKey}_${version}`)) { + throw new Error("Circular dependency detected!"); + } + + iteratedNodeAndVersions.push(`${node.packageKey}_${version}`); + + for (const dependency of dependencies) { + const manifestNodeOfDependency = manifestNodes.find(manifestNode => manifestNode.packageKey === dependency.key); + + await this.checkNodeForCircularDependency(manifestNodeOfDependency, dependency.version, manifestNodes, iteratedNodeAndVersions); + } + } + + private async importPackage(packageToImport: ManifestNodeTransport, + manifestNodes: ManifestNodeTransport[], + sourceToTargetVersionsByNodeKey: Map>, + spaceMappings: Map, + dmTargetIdsBySourceIds: Map, + importedVersionsByNodeKey: Map, + draftIdsByPackageKeyAndVersion: Map, + importedFilePath: string, + excludeActionFlows?: boolean): Promise { + const importedPackageVersion = importedVersionsByNodeKey.get(packageToImport.packageKey) ?? []; + const versionsOfPackage = [...packageToImport.dependenciesByVersion.keys()].sort((k1, k2) => { + const version1 = new SemanticVersioning(k1); + const version2 = new SemanticVersioning(k1); + return version1.isGreaterThan(version2) ? 1 : -1; + }).filter(version => !importedPackageVersion.includes(version)); + + for (const version of versionsOfPackage) { + try { + await this.importPackageVersion(packageToImport, manifestNodes, sourceToTargetVersionsByNodeKey, spaceMappings, dmTargetIdsBySourceIds, importedVersionsByNodeKey, draftIdsByPackageKeyAndVersion, importedFilePath, version, excludeActionFlows); + } catch (e) { + logger.error(`Problem import package with key: ${packageToImport.packageKey} ${version} ${e}`); + } + } + } + + private async importPackageVersion(packageToImport: ManifestNodeTransport, + manifestNodes: ManifestNodeTransport[], + sourceToTargetVersionsByNodeKey: Map>, + spaceMappings: Map, + dmTargetIdsBySourceIds: Map, + importedVersionsByNodeKey: Map, + draftIdsByPackageKeyAndVersion: Map, + importedFilePath: string, + versionOfPackageBeingImported: string, + excludeActionFlows?: boolean): Promise { + if (packageToImport.dependenciesByVersion.get(versionOfPackageBeingImported).length) { + const dependenciesOfPackageVersion = packageToImport.dependenciesByVersion.get(versionOfPackageBeingImported); + await this.importDependencyPackages(dependenciesOfPackageVersion, manifestNodes, sourceToTargetVersionsByNodeKey, spaceMappings, dmTargetIdsBySourceIds, + importedVersionsByNodeKey, draftIdsByPackageKeyAndVersion, importedFilePath, excludeActionFlows + ); + } + + if (this.checkIfPackageVersionHasBeenImported(packageToImport.packageKey, versionOfPackageBeingImported, importedVersionsByNodeKey)) { + return; + } + + const targetSpace = await this.getTargetSpaceForExportedPackage(packageToImport, spaceMappings); + + let nodeInTargetTeam = await this.nodeApi.findOneByKeyAndRootNodeKey(packageToImport.packageKey, packageToImport.packageKey); + + const pathToZipFile = path.resolve(importedFilePath, packageToImport.packageKey + "_" + versionOfPackageBeingImported + ".zip"); + const packageZip = this.createBodyForImport(pathToZipFile); + + await this.packageApi.importPackage(packageZip, targetSpace.id, !!nodeInTargetTeam, excludeActionFlows); + + if (nodeInTargetTeam) { + await this.packageApi.movePackageToSpace(nodeInTargetTeam.id, targetSpace.id) + } + + nodeInTargetTeam = await this.nodeApi.findOneByKeyAndRootNodeKey(packageToImport.packageKey, packageToImport.packageKey); + + if (this.isLatestVersion(versionOfPackageBeingImported, [...packageToImport.dependenciesByVersion.keys()])) { + if (dmTargetIdsBySourceIds.size > 0) { + const variableAssignments = packageToImport.variables + .filter(variable => variable.type === PackageManagerVariableType.DATA_MODEL).map(variable => { + variable.value = dmTargetIdsBySourceIds.get(variable.value?.toString()) as unknown as object; + return variable; + }) + + await this.variableService.assignVariableValues(nodeInTargetTeam.key, variableAssignments); + } else { + await this.variableService.assignVariableValues(nodeInTargetTeam.key, packageToImport.variables); + } + } + + draftIdsByPackageKeyAndVersion.set(`${nodeInTargetTeam.key}_${versionOfPackageBeingImported}`, nodeInTargetTeam.workingDraftId); + + await this.updateDependencyVersions(packageToImport, versionOfPackageBeingImported, sourceToTargetVersionsByNodeKey, draftIdsByPackageKeyAndVersion); + await this.publishPackage(packageToImport); + + const packageVersionInTargetTeam = await this.packageApi.findActiveVersionById(nodeInTargetTeam.id); + + const mappedVersions = sourceToTargetVersionsByNodeKey.get(packageToImport.packageKey) ?? new Map(); + mappedVersions.set(versionOfPackageBeingImported, packageVersionInTargetTeam.version); + sourceToTargetVersionsByNodeKey.set(packageToImport.packageKey, mappedVersions); + + const importedVersionsOfPackage = importedVersionsByNodeKey.get(packageToImport.packageKey) ?? []; + importedVersionsOfPackage.push(versionOfPackageBeingImported); + importedVersionsByNodeKey.set(packageToImport.packageKey, importedVersionsOfPackage); + + const mappedVersion = sourceToTargetVersionsByNodeKey.get(packageToImport.packageKey).get(versionOfPackageBeingImported); + + logger.info(`Imported package with key: ${packageToImport.packageKey} ${versionOfPackageBeingImported} successfully. New version: ${mappedVersion}`) + } + + private isLatestVersion(packageVersion: string, allPackageVersions: string[]): boolean { + let isLatestVersion = true; + + for (const version of allPackageVersions) { + const version1 = new SemanticVersioning(packageVersion); + const version2 = new SemanticVersioning(version); + + if (version2.isGreaterThan(version1)) { + isLatestVersion = false; + break; + } + } + + return isLatestVersion; + } + + private async importDependencyPackages(dependenciesToImport: ManifestDependency[], + manifestNodes: ManifestNodeTransport[], + sourceToTargetVersionsByNodeKey: Map>, + spaceMappings: Map, + dmTargetIdsBySourceIds: Map, + importedVersionsByNodeKey: Map, + draftIdsByPackageKeyAndVersion: Map, + importedFilePath: string, + excludeActionFlows?: boolean): Promise { + for (const dependency of dependenciesToImport) { + if (this.checkIfPackageVersionHasBeenImported(dependency.key, dependency.version, importedVersionsByNodeKey)) { + continue; + } + + const dependentPackage = manifestNodes.find((node) => node.packageKey === dependency.key); + await this.importPackage(dependentPackage, manifestNodes, sourceToTargetVersionsByNodeKey, spaceMappings, dmTargetIdsBySourceIds, importedVersionsByNodeKey, draftIdsByPackageKeyAndVersion, importedFilePath, excludeActionFlows); + } + } + + private checkIfPackageVersionHasBeenImported(packageKey: string, + version: string, + importedVersions: Map): boolean { + const importedPackages = importedVersions.get(packageKey) ?? []; + return importedPackages.includes(version); + } + + private createBodyForImport(filename: string): FormData { + const formData = new FormData(); + formData.append("package", fs.createReadStream(filename, {encoding: null})); + + return formData; + } + + private async getTargetSpaceForExportedPackage(packageToImport: ManifestNodeTransport, spaceMappings: Map): Promise { + let targetSpace; + const allSpaces = await this.spaceService.refreshAndGetAllSpaces(); + if (spaceMappings.has(packageToImport.packageKey)) { + const customSpaceId = spaceMappings.get(packageToImport.packageKey); + const customSpace = allSpaces.find(space => space.id === customSpaceId); + + if (!customSpace) { + throw Error("Provided space id does not exist"); + } + + targetSpace = customSpace; + } else { + targetSpace = allSpaces.find(space => space.name === packageToImport.space.spaceName); + + if (!targetSpace) { + targetSpace = await this.spaceService.createSpace(packageToImport.space.spaceName, packageToImport.space.spaceIcon); + } + + } + + return targetSpace; + } + + private async updateDependencyVersions(node: ManifestNodeTransport, + versionOfPackage: string, + sourceToTargetVersionsByNodeKey: Map>, + draftIdsByPackageKeyAndVersion: Map): Promise { + + const createdNode = await this.nodeApi.findOneByKeyAndRootNodeKey(node.packageKey, node.packageKey); + const newDependencies = []; + for (const dependency of [...node.dependenciesByVersion.get(versionOfPackage)]) { + const nodeInTargetTeam = await this.nodeApi.findOneByKeyAndRootNodeKey(dependency.key, dependency.key); + const draftIdOfVersionedDependency = draftIdsByPackageKeyAndVersion.get(`${dependency.key}_${dependency.version}`); + dependency.version = sourceToTargetVersionsByNodeKey.get(dependency.key).get(dependency.version); + dependency.updateAvailable = dependency.updateAvailable; + dependency.id = nodeInTargetTeam.rootNodeId; + dependency.rootNodeId = createdNode.rootNodeId; + dependency.draftId = draftIdOfVersionedDependency; + newDependencies.push(dependency); + + await this.packageDependenciesApi.deleteDependency(createdNode.id, dependency.key); + } + + if (newDependencies.length) { + await this.packageDependenciesApi.createDependencies(createdNode.id, newDependencies); + } + } + + public async publishPackage(packageToImport: ManifestNodeTransport): Promise { + const nodeInTargetTeam = await this.nodeApi.findOneByKeyAndRootNodeKey(packageToImport.packageKey, packageToImport.packageKey); + const nextVersion = await this.packageApi.findNextVersion(nodeInTargetTeam.id); + await this.packageApi.publishPackage({ + packageKey: packageToImport.packageKey, + version: nextVersion.version, + publishMessage: "Published package after import", + nodeIdsToExclude: [] + }); + } + + public async batchExportPackages(packageKeys: string[], includeDependencies: boolean, excludeActionFlows?: boolean): Promise { + const allPackages = await this.packageApi.findAllPackages(); + let nodesListToExport: BatchExportNodeTransport[] = allPackages.filter(node => packageKeys.includes(node.key)); + + const versionsByNodeKey = new Map(); + + const allPackageKeys = allPackages.map(p => p.key); + + for (const packageKey of packageKeys) { + if (!allPackageKeys.includes(packageKey)) { + throw new Error(`Package ${packageKey} does not exist.`); + } + } + + nodesListToExport = await this.getNodesWithActiveVersion(nodesListToExport); + if (includeDependencies) { + const dependencyPackages = await this.getDependencyPackages(nodesListToExport, [], allPackages, [], versionsByNodeKey); + nodesListToExport.push(...dependencyPackages); + } + + const packagesWithVariableAssignments = await this.variableService.getVariableAssignmentsForNodes(); + + nodesListToExport.forEach(node => { + node.variables = packagesWithVariableAssignments.find(nodeWithVariablesAssignment => nodeWithVariablesAssignment.key === node.key)?.variableAssignments; + }); + + const packagesWithDataModelVariables = packagesWithVariableAssignments.map(packageWithVariablesAssignments => { + packageWithVariablesAssignments.variableAssignments = packageWithVariablesAssignments.variableAssignments.filter(variable => variable.value && variable.type === PackageManagerVariableType.DATA_MODEL); + return packageWithVariablesAssignments; + }); + const dataModelDetailsByNode = await this.dataModelService.getDataModelDetailsForPackages(packagesWithDataModelVariables); + + nodesListToExport.forEach(node => { + node.datamodels = dataModelDetailsByNode.get(node.key); + }); + + nodesListToExport = await this.spaceService.getParentSpaces(nodesListToExport); + await this.exportToZip(nodesListToExport, versionsByNodeKey, excludeActionFlows); + } + + public async getNodesWithActiveVersion(nodes: BatchExportNodeTransport[]): Promise { + const activeVersionsOfPackage = await this.packageApi.findActiveVersionByIds(nodes.map(node => node.id)); + + nodes.forEach(node => { + node.version = activeVersionsOfPackage.find(packageVersion => packageVersion.id === node.id); + }) + + return nodes; + } + + public async getPackagesWithDependencies(draftIdByNodeId: Map): Promise> { + const allPackageDependencies: Map = await this.packageDependenciesApi.findPackageDependenciesByIds(draftIdByNodeId); + return allPackageDependencies; + } + + private async getDependencyPackages(nodesToResolve: BatchExportNodeTransport[], dependencyPackages: BatchExportNodeTransport[], allPackages: ContentNodeTransport[], resolvedDependencies: string[], versionsByNodeKey: Map): Promise { + const draftIdByNodeId = new Map(); + nodesToResolve.forEach(node => draftIdByNodeId.set(node.activatedDraftId, node.id)); + + const dependenciesByPackageDraftIds = await this.getPackagesWithDependencies(draftIdByNodeId); + + const nodesWithDependencies = nodesToResolve.map(nodeToExport => { + nodeToExport.dependencies = dependenciesByPackageDraftIds[nodeToExport.activatedDraftId] ?? []; + return nodeToExport; + }); + + for (const node of nodesWithDependencies) { + node.dependencies = node.dependencies.filter(dependency => !(dependency.external || dependency.deleted)); + node.dependencies.forEach(dependency => { + const dependencyVersions = versionsByNodeKey.get(dependency.key) ?? []; + if (!dependencyVersions.includes(dependency.version)) { + dependencyVersions.push(dependency.version); + versionsByNodeKey.set(dependency.key, dependencyVersions); + } + }); + + const nodesToGetKeys = node.dependencies.filter(dependency => { + return !this.nodeHasBeenResolvedBefore(resolvedDependencies, dependency.key, dependency.version) + }).map(iteratedNode => iteratedNode.key); + + if (nodesToGetKeys.length > 0) { + const dependencyPackagesOfNode = allPackages.filter(packageNode => nodesToGetKeys.includes(packageNode.key)).map(dependency => { + const versionedDep = node.dependencies.find(dep => dependency.key === dep.key); + return { + ...dependency, + version: { + version: versionedDep.version + }, + activatedDraftId: versionedDep.draftId + } as BatchExportNodeTransport + }); + + dependencyPackages.push(...dependencyPackagesOfNode); + await this.getDependencyPackages(dependencyPackagesOfNode, dependencyPackages, allPackages, resolvedDependencies, versionsByNodeKey) + } + } + + return dependencyPackages; + } + + private nodeHasBeenResolvedBefore(nodePath: string[], nodeKey: string, version: string): boolean { + return nodePath.includes(nodeKey + ":" + version); + } + + private exportListOfPackages(nodes: BatchExportNodeTransport[], fieldsToInclude: string[]): void { + const filename = uuidv4() + ".json"; + fileService.writeToFileWithGivenName(JSON.stringify(nodes, fieldsToInclude), filename); + logger.info(FileService.fileDownloadedMessage + filename); + } + + private async exportPackagesAndAssets(nodes: BatchExportNodeTransport[], excludeActionFlows?: boolean): Promise { + const zips = []; + for (const rootPackage of nodes) { + const exportedPackage = await this.packageApi.exportPackage(rootPackage.key, rootPackage.version.version, excludeActionFlows) + zips.push({ + data: exportedPackage, + packageKey: rootPackage.key, + version: rootPackage.version.version + }); + } + return zips; + } + + private async exportToZip(nodes: BatchExportNodeTransport[], versionsByNodeKey: Map, excludeActionFlows?: boolean): Promise { + const manifestNodes = this.exportManifestOfPackages(nodes, versionsByNodeKey); + const packageZips = await this.exportPackagesAndAssets(nodes, excludeActionFlows); + + const zip = new AdmZip(); + + zip.addFile("manifest.yml", Buffer.from(stringify(manifestNodes), "utf8")); + for (const packageZip of packageZips) { + zip.addFile(`${packageZip.packageKey}_${packageZip.version}.zip`, packageZip.data) + } + + const fileName = "export_" + uuidv4() + ".zip"; + zip.writeZip(fileName); + logger.info(this.fileDownloadedMessage + fileName); + } + + private exportManifestOfPackages(nodes: BatchExportNodeTransport[], dependencyVersionsByNodeKey: Map): ManifestNodeTransport[] { + const manifestNodesByPackageKey = new Map(); + + nodes.forEach((node) => { + const manifestNode = manifestNodesByPackageKey.get(node.key) ?? {} as ManifestNodeTransport; + manifestNode.packageKey = node.key; + manifestNode.packageId = node.id; + manifestNode.space = { + spaceName: node.space.name, + spaceIcon: node.space.iconReference + } + manifestNode.variables = node.variables?.map((variable) => { + if (variable.type === PackageManagerVariableType.DATA_MODEL) { + // @ts-ignore + const dataModel = node.datamodels?.find(dataModel => dataModel.dataModelId === variable.value); + return { + key: variable.key, + type: variable.type, + value: variable.value, + dataModelName: dataModel?.name, + } + } + return variable; + }); + manifestNode.dependenciesByVersion = manifestNode.dependenciesByVersion ?? new Map(); + manifestNode.dependenciesByVersion.set(node.version.version, node.dependencies ?? []); + manifestNodesByPackageKey.set(node.key, manifestNode); + }) + + return [...manifestNodesByPackageKey.values()]; + } + + private readManifestFile(importedFileName: string): Promise { + const manifest: ManifestNodeTransport[] = parse( + fs.readFileSync(path.resolve(importedFileName + "/manifest.yml"), { encoding: "utf-8" }) + ); + return Promise.all(manifest); + } +} diff --git a/src/commands/studio/service/space.service.ts b/src/commands/studio/service/space.service.ts new file mode 100644 index 00000000..f6634232 --- /dev/null +++ b/src/commands/studio/service/space.service.ts @@ -0,0 +1,49 @@ +import {v4 as uuidv4} from "uuid"; +import { Context } from "../../../core/command/cli-context"; +import { SpaceTransport } from "../interfaces/space.interface"; +import { BatchExportNodeTransport } from "../interfaces/batch-export-node.interfaces"; +import { SpaceApi } from "../api/space-api"; + +export class SpaceService { + + private allSpaces: SpaceTransport[] = []; + + private spaceApi: SpaceApi; + + constructor(context: Context) { + this.spaceApi = new SpaceApi(context); + } + + public async getParentSpaces(nodes: BatchExportNodeTransport[]): Promise { + const promises = []; + + nodes.forEach(node => { + promises.push(new Promise(async resolve => { + node.space = await this.spaceApi.findOne(node.spaceId); + resolve(node); + })); + }) + + return Promise.all(promises); + } + + public async createSpace(spaceName: string, spaceIcon: string): Promise { + const newSpace = await this.spaceApi.createSpace({ + id: uuidv4(), + name: spaceName, + iconReference: spaceIcon + }); + + await this.refreshAndGetAllSpaces(); + this.allSpaces.push(newSpace); + return newSpace; + } + + public async refreshAndGetAllSpaces(): Promise { + if (this.allSpaces.length) { + return this.allSpaces; + } + this.allSpaces = await this.spaceApi.findAllSpaces(); + return this.allSpaces; + } +} diff --git a/src/commands/studio/service/studio.service.ts b/src/commands/studio/service/studio.service.ts new file mode 100644 index 00000000..082ea50b --- /dev/null +++ b/src/commands/studio/service/studio.service.ts @@ -0,0 +1,280 @@ +import {IZipEntry} from "adm-zip"; +import * as AdmZip from "adm-zip"; +import { + NodeConfiguration, + NodeExportTransport, + PackageExportTransport, PackageKeyAndVersionPair, + StudioPackageManifest, VariableExportTransport, + VariableManifestTransport, +} from "../interfaces/package-export.interfaces"; +import { Context } from "../../../core/command/cli-context"; +import { PackageApi } from "../api/package-api"; +import { + ContentNodeTransport, + PackageManagerVariableType, + PackageWithVariableAssignments, StudioComputeNodeDescriptor, +} from "../interfaces/package-manager.interfaces"; +import { NodeApi } from "../api/node-api"; +import { DataModelService } from "./data-model.service"; +import { SpaceTransport } from "../interfaces/space.interface"; +import { VariableService } from "./variable.service"; +import { SpaceApi } from "../api/space-api"; +import { VariablesApi } from "../api/variables-api"; +import { BatchExportImportConstants } from "../interfaces/batch-export-import.constants"; +import { SpaceService } from "./space.service"; +import { parse, stringify } from "../../../core/utils/json"; + +export class StudioService { + + private packageApi: PackageApi; + private nodeApi: NodeApi; + private spaceApi: SpaceApi; + private variablesApi: VariablesApi; + + private spaceService: SpaceService; + private dataModelService: DataModelService; + private variableService: VariableService; + + constructor(context: Context) { + this.packageApi = new PackageApi(context); + this.nodeApi = new NodeApi(context); + this.spaceApi = new SpaceApi(context); + this.variablesApi = new VariablesApi(context); + + this.spaceService = new SpaceService(context); + this.dataModelService = new DataModelService(context); + this.variableService = new VariableService(context); + } + + public async getExportPackagesWithStudioData(packagesToExport: PackageExportTransport[], withDependencies: boolean): Promise { + const studioPackagesWithDataModels = await this.packageApi.findAllPackagesWithVariableAssignments(PackageManagerVariableType.DATA_MODEL); + + packagesToExport = this.setSpaceIdForStudioPackages(packagesToExport, studioPackagesWithDataModels); + + if (withDependencies) { + const dataModelDetailsByNode = await this.dataModelService.getDataModelDetailsForPackages(studioPackagesWithDataModels); + packagesToExport = this.setDataModelsForStudioPackages(packagesToExport, studioPackagesWithDataModels, dataModelDetailsByNode); + } + + return packagesToExport; + } + + public fixConnectionVariables(variables: VariableManifestTransport[]): VariableManifestTransport[] { + return variables.map(variableManifest => ({ + ...variableManifest, + variables: variableManifest.variables.map(variable => { + if (variable.type !== PackageManagerVariableType.CONNECTION) { + return variable; + } + + return this.fixConnectionVariable(variable); + }) + })); + } + + public async getStudioPackageManifests(studioPackageKeys: string[]): Promise { + return Promise.all(studioPackageKeys.map(async packageKey => { + const node = await this.nodeApi.findOneByKeyAndRootNodeKey(packageKey, packageKey); + const nodeSpace: SpaceTransport = await this.spaceApi.findOne(node.spaceId); + const variableAssignments = await this.variablesApi.getRuntimeVariableValues(packageKey, BatchExportImportConstants.APP_MODE_VIEWER); + + return { + packageKey: packageKey, + space: { + name: nodeSpace.name, + iconReference: nodeSpace.iconReference + }, + runtimeVariableAssignments: variableAssignments + } + })); + } + + public processPackageForExport(exportedPackage: IZipEntry, exportedVariables: VariableManifestTransport[]): AdmZip { + const packageZip = new AdmZip(exportedPackage.getData()); + this.deleteScenarioAssets(packageZip); + this.fixConnectionVariablesForRootNodeFiles(packageZip, exportedPackage.name, exportedVariables); + + return packageZip; + } + + public async processImportedPackages(configs: AdmZip, existingStudioPackages: ContentNodeTransport[], studioManifests: StudioPackageManifest[]): Promise { + if(studioManifests == null) { + return; + } + for (const manifest of studioManifests) { + const existingPackage = existingStudioPackages.find(existingPackage => existingPackage.key === manifest.packageKey); + if (existingPackage) { + await this.packageApi.movePackageToSpace(existingPackage.id, manifest.space.id); + } + await this.assignRuntimeVariables(manifest); + } + } + + private setSpaceIdForStudioPackages(packages: PackageExportTransport[], studioPackages: PackageWithVariableAssignments[]): PackageExportTransport[] { + const studioPackageByKey = new Map(); + studioPackages.forEach(pkg => studioPackageByKey.set(pkg.key, pkg)); + + return packages.map(pkg => { + return studioPackageByKey.has(pkg.key) ? { + ...pkg, + spaceId: studioPackageByKey.get(pkg.key).spaceId + } : pkg; + }); + } + + private setDataModelsForStudioPackages(packages: PackageExportTransport[], + studioPackageWithDataModels: PackageWithVariableAssignments[], + dataModelDetailsByNode: Map): PackageExportTransport[] { + const studioPackageByKey = new Map(); + studioPackageWithDataModels.forEach(pkg => studioPackageByKey.set(pkg.key, pkg)); + + return packages.map(pkg => { + return studioPackageByKey.has(pkg.key) ? { + ...pkg, + datamodels: dataModelDetailsByNode.get(pkg.key) + .map(dataModel => ({ + name: dataModel.name, + poolId: dataModel.poolId, + dataModelId: dataModel.dataModelId + })) + } : pkg; + }); + } + + private fixConnectionVariable(variable: VariableExportTransport): VariableExportTransport { + if (!variable.value.appName) { + return variable; + } + + return { + ...variable, + metadata: { + ...variable.metadata, + appName: variable.value.appName + } + } + } + + private deleteScenarioAssets(packageZip: AdmZip): void { + packageZip.getEntries().filter(entry => entry.entryName.startsWith(BatchExportImportConstants.NODES_FOLDER_NAME) && entry.entryName.endsWith(BatchExportImportConstants.JSON_EXTENSION)) + .forEach(entry => { + const node: NodeExportTransport = parse(entry.getData().toString()); + if (node.type === BatchExportImportConstants.SCENARIO_NODE) { + packageZip.deleteFile(entry); + } + }); + } + + private fixConnectionVariablesForRootNodeFiles(packageZip: AdmZip, zipName: string, exportedVariables: VariableManifestTransport[]): void { + const packageKeyAndVersion = this.getPackageKeyAndVersion(zipName); + const connectionVariablesByKey = this.getConnectionVariablesByKeyForPackage(packageKeyAndVersion.packageKey, packageKeyAndVersion.version, exportedVariables); + + if (connectionVariablesByKey.size === 0) { + return; + } + + const packageEntry = packageZip.getEntry("package.json"); + + const exportedNode: NodeExportTransport = parse(packageEntry.getData().toString()); + const nodeContent: NodeConfiguration = exportedNode.configuration; + + nodeContent.variables = nodeContent.variables.map(variable => ({ + ...variable, + metadata: variable.type === PackageManagerVariableType.CONNECTION ? + connectionVariablesByKey.get(variable.key).metadata : variable.metadata + })); + + packageZip.updateFile(packageEntry, Buffer.from(stringify(exportedNode))); + } + + private getPackageKeyAndVersion(zipName: string): PackageKeyAndVersionPair { + const lastUnderscoreIndex = zipName.lastIndexOf("_"); + const packageKey = zipName.replace(BatchExportImportConstants.ZIP_EXTENSION, "").substring(0, lastUnderscoreIndex); + const packageVersion = zipName.replace(BatchExportImportConstants.ZIP_EXTENSION, "").substring(lastUnderscoreIndex + 1); + + return { + packageKey: packageKey, + version: packageVersion + } + } + + private getConnectionVariablesByKeyForPackage(packageKey: string, packageVersion: string, variables: VariableManifestTransport[]): Map { + const variablesByKey = new Map(); + const packageVariables = variables.find(exportedVariable => exportedVariable.packageKey === packageKey && exportedVariable.version === packageVersion); + + if (packageVariables && packageVariables.variables.length) { + packageVariables.variables.filter(variable => variable.type === PackageManagerVariableType.CONNECTION) + .forEach(variable => variablesByKey.set(variable.key, variable)); + } + + return variablesByKey; + } + + public async mapSpaces(exportedFiles: AdmZip, studioManifests: StudioPackageManifest[]): Promise { + if (studioManifests == null) { + return exportedFiles; + } + for (const file of exportedFiles.getEntries()) { + if(file.name.endsWith(BatchExportImportConstants.ZIP_EXTENSION)) { + const packageKey = this.getPackageKeyAndVersion(file.name).packageKey; + + if (this.isStudioPackage(studioManifests, packageKey)) { + const studioManifest = studioManifests.find(manifest => manifest.packageKey === packageKey); + + if (studioManifest) { + const spaceId = await this.findDesiredSpaceIdForPackage(studioManifest); + studioManifest.space.id = spaceId; + + const packageZip = new AdmZip(file.getData()); + packageZip.getEntries().forEach(nodeFile => { + if (nodeFile.entryName.endsWith(BatchExportImportConstants.JSON_EXTENSION)) { + const updatedNodeFile = this.updateSpaceIdForNode(nodeFile.getData().toString(), spaceId); + packageZip.updateFile(nodeFile, Buffer.from(updatedNodeFile)); + } + }); + exportedFiles.updateFile(file, packageZip.toBuffer()); + } + } + } + } + return exportedFiles; + } + + private isStudioPackage(studioManifests: StudioPackageManifest[], packageKey: string): boolean { + return studioManifests.some(manifest => manifest.packageKey === packageKey); + } + + private async findDesiredSpaceIdForPackage(studioPackageManifest: StudioPackageManifest): Promise { + const allSpaces = await this.spaceService.refreshAndGetAllSpaces(); + + if (studioPackageManifest.space.id) { + const targetSpace = allSpaces.find(space => space.id === studioPackageManifest.space.id); + if (!targetSpace) { + throw Error("Provided space ID does not exist."); + } + return targetSpace.id; + } + + const targetSpaceByName = allSpaces.find(space => space.name === studioPackageManifest.space.name); + if (targetSpaceByName) { + return targetSpaceByName.id; + } + + const spaceTransport = await this.spaceService.createSpace(studioPackageManifest.space.name, studioPackageManifest.space.iconReference); + return spaceTransport.id; + } + + private async assignRuntimeVariables(manifest: StudioPackageManifest): Promise { + if (manifest.runtimeVariableAssignments.length) { + await this.variableService.assignVariableValues(manifest.packageKey, manifest.runtimeVariableAssignments); + } + } + + private updateSpaceIdForNode(nodeContent: string, spaceId: string): string { + const exportedNode: NodeExportTransport = parse(nodeContent); + const oldSpaceId = exportedNode.spaceId; + + nodeContent = nodeContent.replace(new RegExp(oldSpaceId, "g"), spaceId); + return nodeContent; + } +} diff --git a/src/commands/studio/service/variable.service.ts b/src/commands/studio/service/variable.service.ts new file mode 100644 index 00000000..d698dbe6 --- /dev/null +++ b/src/commands/studio/service/variable.service.ts @@ -0,0 +1,124 @@ +import { v4 as uuidv4 } from "uuid"; +import {URLSearchParams} from "url"; +import { + PackageManagerVariableType, + PackageWithVariableAssignments, + VariablesAssignments, +} from "../interfaces/package-manager.interfaces"; +import { Context } from "../../../core/command/cli-context"; +import { PackageApi } from "../api/package-api"; +import { FatalError, logger } from "../../../core/utils/logger"; +import { FileService, fileService } from "../../../core/utils/file-service"; +import { VariablesApi } from "../api/variables-api"; +import { PackageKeyAndVersionPair, VariableManifestTransport } from "../interfaces/package-export.interfaces"; +import { BatchImportExportApi } from "../api/batch-import-export-api"; +import { StudioService } from "./studio.service"; + +export class VariableService { + + private packageApi: PackageApi; + private variablesApi: VariablesApi; + private batchImportExportApi: BatchImportExportApi; + + private studioService: StudioService; + + constructor(context: Context) { + this.packageApi = new PackageApi(context); + this.variablesApi = new VariablesApi(context); + this.batchImportExportApi = new BatchImportExportApi(context); + + this.studioService = new StudioService(context); + } + + public async getVariableAssignmentsForNodes(type?: PackageManagerVariableType): Promise { + return await this.packageApi.findAllPackagesWithVariableAssignments(type); + } + + public async assignVariableValues(packageKey: string, variablesAssignments: VariablesAssignments[]): Promise { + await this.variablesApi.assignVariableValues(packageKey, variablesAssignments); + } + + public async listCandidateAssignments(type: string, params: string): Promise { + const parsedParams = this.parseParams(params); + const assignments = await this.variablesApi.getCandidateAssignments(type, parsedParams); + + assignments.forEach(assignment => { + logger.info(JSON.stringify(assignment)); + }); + } + + public async findAndExportCandidateAssignments(type: string, params: string): Promise { + const parsedParams = this.parseParams(params); + const assignments = await this.variablesApi.getCandidateAssignments(type, parsedParams); + + this.exportToJson(assignments) + } + + public async listVariables(keysByVersion: string[], keysByVersionFile: string): Promise { + const variableManifests = await this.getVersionedVariablesByKeyVersionPairs(keysByVersion, keysByVersionFile); + + variableManifests.forEach(variableManifest => { + logger.info(JSON.stringify(variableManifest)); + }); + } + + public async exportVariables(keysByVersion: string[], keysByVersionFile: string): Promise { + const variableManifests = await this.getVersionedVariablesByKeyVersionPairs(keysByVersion, keysByVersionFile); + + this.exportToJson(variableManifests); + } + + private parseParams(params?: string): URLSearchParams { + const queryParams = new URLSearchParams(); + + if (params) { + try { + params.split(",").forEach((param: string) => { + const paramKeyValuePair: string[] = param.split("="); + queryParams.set(paramKeyValuePair[0], paramKeyValuePair[1]); + }) + } catch (e) { + throw new FatalError(`Problem parsing query params: ${e}`); + } + } + + return queryParams; + } + + private async getVersionedVariablesByKeyVersionPairs(keysByVersion: string[], keysByVersionFile: string): Promise { + const variablesExportRequest: PackageKeyAndVersionPair[] = await this.buildKeyVersionPairs(keysByVersion, keysByVersionFile); + + const variableManifests = await this.batchImportExportApi.findVariablesWithValuesByPackageKeysAndVersion(variablesExportRequest); + return this.studioService.fixConnectionVariables(variableManifests); + } + + private async buildKeyVersionPairs(keysByVersion: string[], keysByVersionFile: string): Promise { + let variablesExportRequest: PackageKeyAndVersionPair[] = []; + + if (keysByVersion.length !== 0) { + variablesExportRequest = this.buildKeyAndVersionPairsFromArrayInput(keysByVersion); + } else if (keysByVersion.length === 0 && keysByVersionFile !== "") { + variablesExportRequest = await fileService.readFileToJson(keysByVersionFile); + } else { + throw new FatalError("Please provide keysByVersion mappings or file path!"); + } + + return variablesExportRequest; + } + + private buildKeyAndVersionPairsFromArrayInput(keysByVersion: string[]): PackageKeyAndVersionPair[] { + return keysByVersion.map(keyAndVersion => { + const keyAndVersionSplit = keyAndVersion.split(":"); + return { + packageKey: keyAndVersionSplit[0], + version: keyAndVersionSplit[1] + }; + }); + } + + private exportToJson(data: any): void { + const filename = uuidv4() + ".json"; + fileService.writeToFileWithGivenName(JSON.stringify(data), filename); + logger.info(FileService.fileDownloadedMessage + filename); + } +} diff --git a/src/commands/studio/utils/semantic-versioning.ts b/src/commands/studio/utils/semantic-versioning.ts new file mode 100644 index 00000000..6a55d464 --- /dev/null +++ b/src/commands/studio/utils/semantic-versioning.ts @@ -0,0 +1,23 @@ +export class SemanticVersioning { + private version: string; + + constructor(version: string) { + this.version = version; + } + + public isGreaterThan(versionToCompare: SemanticVersioning): boolean { + const splitVersion1 = this.version.split("."); + const splitVersion2 = versionToCompare.version.split("."); + + const majorVersion1 = splitVersion1[0]; + const majorVersion2 = splitVersion2[0]; + + const minorVersion1 = splitVersion1[1]; + const minorVersion2 = splitVersion2[1]; + + const patchVersion1 = splitVersion1[2]; + const patchVersion2 = splitVersion2[2]; + + return (majorVersion1 > majorVersion2) || (minorVersion1 > minorVersion2) || (patchVersion1 > patchVersion2); + } +} \ No newline at end of file diff --git a/src/core/utils/json.ts b/src/core/utils/json.ts new file mode 100644 index 00000000..0a1b8eae --- /dev/null +++ b/src/core/utils/json.ts @@ -0,0 +1,14 @@ +export function stringify(data: any): string { + return JSON.stringify(data, replacer, 2); +} + +export function parse(data: string): T { + return JSON.parse(data); +} + +const replacer = (key, value) => { + if (value instanceof Map) { + return Object.fromEntries(value); + } + return value; +}; diff --git a/src/core/utils/yaml.ts b/src/core/utils/yaml.ts new file mode 100644 index 00000000..8c9c9518 --- /dev/null +++ b/src/core/utils/yaml.ts @@ -0,0 +1,9 @@ +import * as YAML from "yaml"; + +export function stringify(data: any): string { + return YAML.stringify(data, {doubleQuotedAsJSON: true, indent: 2, lineWidth: 200}); +} + +export function parse(data: string): T { + return YAML.parse(data); +} From 4165bd8d6a8244354468b53a8130630f14d56393 Mon Sep 17 00:00:00 2001 From: zgjimhaziri Date: Tue, 20 May 2025 15:56:24 +0200 Subject: [PATCH 30/45] TA-3767: Add Navi as codeowners in Studio commands --- .github/CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 35493b46..64766c14 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -9,4 +9,4 @@ #/src/commands/analysis/ filter and foundation team #/src/commands/cpm4/ cpm4 team #/src/commands/data-pipeline/ data-management team -#/src/commands/studio/ @celonis/navi +/src/commands/studio/ @celonis/navi From 7b9c2314b11e343d96796283e75f275bf78d68eb Mon Sep 17 00:00:00 2001 From: zgjimhaziri Date: Tue, 20 May 2025 16:47:07 +0200 Subject: [PATCH 31/45] TA-3769: Migrate Analysis commands --- .../analysis-bookmarks-command.service.ts | 20 ++++++ .../analysis-bookmarks.manager-factory.ts | 39 +++++++++++ .../analysis/analysis-bookmarks.manager.ts | 64 +++++++++++++++++++ src/commands/analysis/module.ts | 24 +++++++ 4 files changed, 147 insertions(+) create mode 100644 src/commands/analysis/analysis-bookmarks-command.service.ts create mode 100644 src/commands/analysis/analysis-bookmarks.manager-factory.ts create mode 100644 src/commands/analysis/analysis-bookmarks.manager.ts diff --git a/src/commands/analysis/analysis-bookmarks-command.service.ts b/src/commands/analysis/analysis-bookmarks-command.service.ts new file mode 100644 index 00000000..374e27ea --- /dev/null +++ b/src/commands/analysis/analysis-bookmarks-command.service.ts @@ -0,0 +1,20 @@ +import { ContentService } from "../../core/http/http-shared/content.service"; +import { AnalysisBookmarksManagerFactory } from "./analysis-bookmarks.manager-factory"; +import { Context } from "../../core/command/cli-context"; + +export class AnalysisBookmarksCommandService { + private contentService = new ContentService(); + private analysisBookmarksManagerFactory: AnalysisBookmarksManagerFactory; + + constructor(context: Context) { + this.analysisBookmarksManagerFactory = new AnalysisBookmarksManagerFactory(context); + } + + public async pullAnalysisBookmarks(analysisId: string, type: string): Promise { + await this.contentService.pull(this.analysisBookmarksManagerFactory.createAnalysisBookmarksManager(null, analysisId, type)); + } + + public async pushAnalysisBookmarks(analysisId: string, filename: string): Promise { + await this.contentService.push(this.analysisBookmarksManagerFactory.createAnalysisBookmarksManager(filename, analysisId)); + } +} diff --git a/src/commands/analysis/analysis-bookmarks.manager-factory.ts b/src/commands/analysis/analysis-bookmarks.manager-factory.ts new file mode 100644 index 00000000..36f84aec --- /dev/null +++ b/src/commands/analysis/analysis-bookmarks.manager-factory.ts @@ -0,0 +1,39 @@ +import * as fs from "fs"; +import * as path from "path"; +import { AnalysisBookmarksManager } from "./analysis-bookmarks.manager"; +import { FatalError, logger } from "../../core/utils/logger"; +import { Context } from "../../core/command/cli-context"; + +export class AnalysisBookmarksManagerFactory { + + private readonly context: Context; + + constructor(context: Context) { + this.context = context; + } + + public createAnalysisBookmarksManager( + filename: string, + analysisId: string, + type?: string + ): AnalysisBookmarksManager { + const analysisBookmarksManager = new AnalysisBookmarksManager(this.context); + analysisBookmarksManager.analysisId = analysisId; + if (type === undefined || type === null) { + type = "user"; + } + + analysisBookmarksManager.type = type; + if (filename !== null) { + analysisBookmarksManager.fileName = this.readFile(filename); + } + return analysisBookmarksManager; + } + + private readFile(fileName: string): string { + if (!fs.existsSync(path.resolve(process.cwd(), fileName))) { + logger.error(new FatalError("The provided file does not exist")); + } + return path.resolve(process.cwd(), fileName); + } +} diff --git a/src/commands/analysis/analysis-bookmarks.manager.ts b/src/commands/analysis/analysis-bookmarks.manager.ts new file mode 100644 index 00000000..98abfb76 --- /dev/null +++ b/src/commands/analysis/analysis-bookmarks.manager.ts @@ -0,0 +1,64 @@ +import * as fs from "fs"; +import * as FormData from "form-data"; +import { Context } from "../../core/command/cli-context"; +import { BaseManager } from "../../core/http/http-shared/base.manager"; +import { ManagerConfig } from "../../core/http/http-shared/manager-config.interface"; + +export class AnalysisBookmarksManager extends BaseManager { + private static BASE_URL = "/process-analytics/api/bookmarks"; + private static ANALYSIS_BOOKMARKS_FILE_PREFIX = "studio_analysis_bookmarks_"; + + private _analysisId: string; + private _fileName: string; + private _type: string; + + constructor(context: Context) { + super(context); + } + + public get fileName(): string { + return this._fileName; + } + + public set fileName(value: string) { + this._fileName = value; + } + + public get analysisId(): string { + return this._analysisId; + } + + public set analysisId(value: string) { + this._analysisId = value; + } + + public get type(): string { + return this._type; + } + + public set type(value: string) { + this._type = value; + } + + public getConfig(): ManagerConfig { + const pullUrl = `${AnalysisBookmarksManager.BASE_URL}/export?analysisId=${this.analysisId}&type=${this.type}`; + return { + pushUrl: `${AnalysisBookmarksManager.BASE_URL}/import?analysisId=${this.analysisId}`, + pullUrl: pullUrl, + exportFileName: `${AnalysisBookmarksManager.ANALYSIS_BOOKMARKS_FILE_PREFIX}${this.analysisId}${".json"}`, + onPushSuccessMessage: (): string => { + return "Analysis Bookmarks was pushed successfully."; + }, + }; + } + + public getBody(): any { + const formData = new FormData(); + formData.append("file", fs.createReadStream(this.fileName)); + return formData; + } + + protected getSerializedFileContent(data: any): string { + return JSON.stringify(data); + } +} diff --git a/src/commands/analysis/module.ts b/src/commands/analysis/module.ts index aa1d1dc3..487926ab 100644 --- a/src/commands/analysis/module.ts +++ b/src/commands/analysis/module.ts @@ -4,12 +4,36 @@ import { Configurator, IModule } from "../../core/command/module-handler"; import { Context } from "../../core/command/cli-context"; +import { Command, OptionValues } from "commander"; +import { AnalysisBookmarksCommandService } from "./analysis-bookmarks-command.service"; class Module extends IModule { register(context: Context, configurator: Configurator) { + const pullCommand = configurator.command("pull"); + pullCommand.command("bookmarks") + .description("Command to pull an analysis bookmarks") + .option("-p, --profile ", "Profile which you want to use to pull the analysis bookmarks") + .option("--type ", "Pull shared/all Analysis Bookmarks, else by default get user bookmarks") + .requiredOption("--id ", "Id of the analysis you want to pull") + .action(this.pullAnalysisBookmarks); + + const pushCommand = configurator.command("push"); + pushCommand.command("bookmarks") + .description("Command to push an analysis to a workspace") + .option("-p, --profile ", "Profile which you want to use to push the analysis") + .requiredOption("--id ", "Id of the Analysis to which you want to push the analysis bookmarks") + .requiredOption("-f, --file ", "The file you want to push") + .action(this.pushAnalysisBookmarks); + } + + private async pullAnalysisBookmarks(context: Context, command: Command, options: OptionValues): Promise { + await new AnalysisBookmarksCommandService(context).pullAnalysisBookmarks(options.id, options.type); } + private async pushAnalysisBookmarks(context: Context, command: Command, options: OptionValues): Promise { + await new AnalysisBookmarksCommandService(context).pushAnalysisBookmarks(options.id, options.file); + } } export = Module; \ No newline at end of file From 7b8e58a811cf8c914d41c1bd479a7f00dfb37b84 Mon Sep 17 00:00:00 2001 From: zgjimhaziri Date: Tue, 20 May 2025 18:25:59 +0200 Subject: [PATCH 32/45] TA-3769: Move Pacman related commands out of Studio module --- .../batch-export-import.constants.ts | 0 .../interfaces/package-export.interfaces.ts | 8 +- .../variable-assignment-apis.constants.ts | 0 .../studio/api/batch-import-export-api.ts | 79 ----------- ...riables-api.ts => studio-variables-api.ts} | 16 +-- .../batch-export-node.interfaces.ts | 10 -- .../studio/service/package.service.ts | 6 +- .../studio/service/studio-variable.service.ts | 27 ++++ src/commands/studio/service/studio.service.ts | 16 +-- .../studio/service/variable.service.ts | 124 ------------------ 10 files changed, 45 insertions(+), 241 deletions(-) rename src/commands/{studio => configuration-management}/interfaces/batch-export-import.constants.ts (100%) rename src/commands/{studio => configuration-management}/interfaces/package-export.interfaces.ts (88%) rename src/commands/{studio => configuration-management}/interfaces/variable-assignment-apis.constants.ts (100%) delete mode 100644 src/commands/studio/api/batch-import-export-api.ts rename src/commands/studio/api/{variables-api.ts => studio-variables-api.ts} (64%) create mode 100644 src/commands/studio/service/studio-variable.service.ts delete mode 100644 src/commands/studio/service/variable.service.ts diff --git a/src/commands/studio/interfaces/batch-export-import.constants.ts b/src/commands/configuration-management/interfaces/batch-export-import.constants.ts similarity index 100% rename from src/commands/studio/interfaces/batch-export-import.constants.ts rename to src/commands/configuration-management/interfaces/batch-export-import.constants.ts diff --git a/src/commands/studio/interfaces/package-export.interfaces.ts b/src/commands/configuration-management/interfaces/package-export.interfaces.ts similarity index 88% rename from src/commands/studio/interfaces/package-export.interfaces.ts rename to src/commands/configuration-management/interfaces/package-export.interfaces.ts index be73b97b..604de747 100644 --- a/src/commands/studio/interfaces/package-export.interfaces.ts +++ b/src/commands/configuration-management/interfaces/package-export.interfaces.ts @@ -1,5 +1,9 @@ -import {StudioComputeNodeDescriptor, VariableDefinition, VariablesAssignments} from "./package-manager.interfaces"; -import { SpaceTransport } from "./space.interface"; +import { SpaceTransport } from "../../studio/interfaces/space.interface"; +import { + StudioComputeNodeDescriptor, + VariableDefinition, + VariablesAssignments, +} from "../../studio/interfaces/package-manager.interfaces"; export interface DependencyTransport { key: string; diff --git a/src/commands/studio/interfaces/variable-assignment-apis.constants.ts b/src/commands/configuration-management/interfaces/variable-assignment-apis.constants.ts similarity index 100% rename from src/commands/studio/interfaces/variable-assignment-apis.constants.ts rename to src/commands/configuration-management/interfaces/variable-assignment-apis.constants.ts diff --git a/src/commands/studio/api/batch-import-export-api.ts b/src/commands/studio/api/batch-import-export-api.ts deleted file mode 100644 index 25a94617..00000000 --- a/src/commands/studio/api/batch-import-export-api.ts +++ /dev/null @@ -1,79 +0,0 @@ -import * as FormData from "form-data"; -import { HttpClient } from "../../../core/http/http-client"; -import { Context } from "../../../core/command/cli-context"; -import { FatalError } from "../../../core/utils/logger"; -import { - PackageExportTransport, - PackageKeyAndVersionPair, - PostPackageImportData, - VariableManifestTransport, -} from "../interfaces/package-export.interfaces"; - -export class BatchImportExportApi { - - private httpClient: HttpClient; - - constructor(context: Context) { - this.httpClient = context.httpClient; - } - - public findAllActivePackages(flavors: string[], withDependencies: boolean = false): Promise { - const queryParams = new URLSearchParams(); - - queryParams.set("withDependencies", withDependencies.toString()); - flavors.forEach(flavor => queryParams.append("flavors", flavor)) - - return this.httpClient.get(`/package-manager/api/core/packages/export/list?${queryParams.toString()}`).catch(e => { - throw new FatalError(`Problem getting active packages: ${e}`); - }); - } - - public findActivePackagesByVariableValue(flavors: string[], variableValue: string, variableType: string): Promise { - const queryParams = new URLSearchParams(); - - queryParams.set("variableValue", variableValue); - if (variableType) { - queryParams.set("variableType", variableType); - } - flavors.forEach(flavor => queryParams.append("flavors", flavor)) - - return this.httpClient.get(`/package-manager/api/core/packages/export/list-by-variable-value?${queryParams.toString()}`).catch(e => { - throw new FatalError(`Problem getting active packages by variable value: ${e}`); - }); - } - - public findActivePackagesByKeys(packageKeys: string[], withDependencies: boolean = false): Promise { - const queryParams = new URLSearchParams(); - - packageKeys.forEach(key => queryParams.append("packageKeys", key)) - queryParams.set("withDependencies", withDependencies.toString()); - - return this.httpClient.get(`/package-manager/api/core/packages/export/list-by-keys?${queryParams.toString()}`).catch(e => { - throw new FatalError(`Problem getting active packages by keys: ${e}`); - }); - } - - public exportPackages(packageKeys: string[], withDependencies: boolean = false): Promise { - const queryParams = new URLSearchParams(); - packageKeys.forEach(packageKey => queryParams.append("packageKeys", packageKey)); - queryParams.set("withDependencies", withDependencies.toString()); - - return this.httpClient.getFile(`/package-manager/api/core/packages/export/batch?${queryParams.toString()}`).catch(e => { - throw new FatalError(`Problem exporting packages: ${e}`); - }); - } - - public importPackages(data: FormData, overwrite: boolean): Promise { - return this.httpClient.postFile( - "/package-manager/api/core/packages/import/batch", - data, - {overwrite} - ); - } - - public findVariablesWithValuesByPackageKeysAndVersion(packagesByKeyAndVersion: PackageKeyAndVersionPair[]): Promise { - return this.httpClient.post("/package-manager/api/core/packages/export/batch/variables-with-assignments", packagesByKeyAndVersion).catch(e => { - throw new FatalError(`Problem exporting package variables: ${e}`); - }) - } -} diff --git a/src/commands/studio/api/variables-api.ts b/src/commands/studio/api/studio-variables-api.ts similarity index 64% rename from src/commands/studio/api/variables-api.ts rename to src/commands/studio/api/studio-variables-api.ts index 8f517c66..c08a79ae 100644 --- a/src/commands/studio/api/variables-api.ts +++ b/src/commands/studio/api/studio-variables-api.ts @@ -3,13 +3,11 @@ import { VariablesAssignments } from "../interfaces/package-manager.interfaces"; import {URLSearchParams} from "url"; -import { variableAssignmentApis } from "../interfaces/variable-assignment-apis.constants"; import { HttpClient } from "../../../core/http/http-client"; import { Context } from "../../../core/command/cli-context"; import { FatalError } from "../../../core/utils/logger"; -export class VariablesApi { - private static readonly ASSIGNMENT_APIS = variableAssignmentApis; +export class StudioVariablesApi { private httpClient: HttpClient; constructor(context: Context) { @@ -22,18 +20,6 @@ export class VariablesApi { }); } - public async getCandidateAssignments(type: string, params: URLSearchParams): Promise { - if (!VariablesApi.ASSIGNMENT_APIS[type]) { - throw new FatalError(`Variable type ${type} not supported.`); - } - - const apiUrl: string = VariablesApi.ASSIGNMENT_APIS[type].url + (params.toString().length ? `?${params.toString()}` : ""); - - return this.httpClient.get(apiUrl).catch(e => { - throw new FatalError(`Problem getting variables assignment values for type ${type}: ${e}`); - }); - } - public getRuntimeVariableValues(packageKey: string, appMode: string): Promise { const queryParams = new URLSearchParams(); queryParams.set("appMode", appMode); diff --git a/src/commands/studio/interfaces/batch-export-node.interfaces.ts b/src/commands/studio/interfaces/batch-export-node.interfaces.ts index 65f829e9..92a5122d 100644 --- a/src/commands/studio/interfaces/batch-export-node.interfaces.ts +++ b/src/commands/studio/interfaces/batch-export-node.interfaces.ts @@ -16,13 +16,3 @@ export interface BatchExportNodeTransport extends ContentNodeTransport { variables?: VariablesAssignments[]; space?: SpaceTransport; } - -export interface PackageAndAssetTransport { - rootNode: ContentNodeTransport, - nodes: ContentNodeTransport[] -} - -export interface SpaceMappingTransport { - packageKey: string, - spaceId: string -} \ No newline at end of file diff --git a/src/commands/studio/service/package.service.ts b/src/commands/studio/service/package.service.ts index a3073642..6ffae206 100644 --- a/src/commands/studio/service/package.service.ts +++ b/src/commands/studio/service/package.service.ts @@ -23,7 +23,7 @@ import { SpaceTransport } from "../interfaces/space.interface"; import { NodeApi } from "../api/node-api"; import { PackageDependenciesApi } from "../api/package-dependencies-api"; import { DataModelService } from "./data-model.service"; -import { VariableService } from "./variable.service"; +import { StudioVariableService } from "./studio-variable.service"; import { SpaceService } from "./space.service"; export class PackageService { @@ -34,7 +34,7 @@ export class PackageService { private packageDependenciesApi: PackageDependenciesApi; private dataModelService: DataModelService; - private variableService: VariableService; + private variableService: StudioVariableService; private spaceService: SpaceService; constructor(context: Context) { @@ -42,7 +42,7 @@ export class PackageService { this.nodeApi = new NodeApi(context); this.packageDependenciesApi = new PackageDependenciesApi(context); this.dataModelService = new DataModelService(context); - this.variableService = new VariableService(context); + this.variableService = new StudioVariableService(context); this.spaceService = new SpaceService(context); } diff --git a/src/commands/studio/service/studio-variable.service.ts b/src/commands/studio/service/studio-variable.service.ts new file mode 100644 index 00000000..ff325772 --- /dev/null +++ b/src/commands/studio/service/studio-variable.service.ts @@ -0,0 +1,27 @@ +import { + PackageManagerVariableType, + PackageWithVariableAssignments, + VariablesAssignments, +} from "../interfaces/package-manager.interfaces"; +import { Context } from "../../../core/command/cli-context"; +import { PackageApi } from "../api/package-api"; +import { StudioVariablesApi } from "../api/studio-variables-api"; + +export class StudioVariableService { + + private packageApi: PackageApi; + private variablesApi: StudioVariablesApi; + + constructor(context: Context) { + this.packageApi = new PackageApi(context); + this.variablesApi = new StudioVariablesApi(context); + } + + public async getVariableAssignmentsForNodes(type?: PackageManagerVariableType): Promise { + return await this.packageApi.findAllPackagesWithVariableAssignments(type); + } + + public async assignVariableValues(packageKey: string, variablesAssignments: VariablesAssignments[]): Promise { + await this.variablesApi.assignVariableValues(packageKey, variablesAssignments); + } +} diff --git a/src/commands/studio/service/studio.service.ts b/src/commands/studio/service/studio.service.ts index 082ea50b..3d6af458 100644 --- a/src/commands/studio/service/studio.service.ts +++ b/src/commands/studio/service/studio.service.ts @@ -6,7 +6,7 @@ import { PackageExportTransport, PackageKeyAndVersionPair, StudioPackageManifest, VariableExportTransport, VariableManifestTransport, -} from "../interfaces/package-export.interfaces"; +} from "../../configuration-management/interfaces/package-export.interfaces"; import { Context } from "../../../core/command/cli-context"; import { PackageApi } from "../api/package-api"; import { @@ -17,10 +17,10 @@ import { import { NodeApi } from "../api/node-api"; import { DataModelService } from "./data-model.service"; import { SpaceTransport } from "../interfaces/space.interface"; -import { VariableService } from "./variable.service"; +import { StudioVariableService } from "./studio-variable.service"; import { SpaceApi } from "../api/space-api"; -import { VariablesApi } from "../api/variables-api"; -import { BatchExportImportConstants } from "../interfaces/batch-export-import.constants"; +import { StudioVariablesApi } from "../api/studio-variables-api"; +import { BatchExportImportConstants } from "../../configuration-management/interfaces/batch-export-import.constants"; import { SpaceService } from "./space.service"; import { parse, stringify } from "../../../core/utils/json"; @@ -29,21 +29,21 @@ export class StudioService { private packageApi: PackageApi; private nodeApi: NodeApi; private spaceApi: SpaceApi; - private variablesApi: VariablesApi; + private variablesApi: StudioVariablesApi; private spaceService: SpaceService; private dataModelService: DataModelService; - private variableService: VariableService; + private variableService: StudioVariableService; constructor(context: Context) { this.packageApi = new PackageApi(context); this.nodeApi = new NodeApi(context); this.spaceApi = new SpaceApi(context); - this.variablesApi = new VariablesApi(context); + this.variablesApi = new StudioVariablesApi(context); this.spaceService = new SpaceService(context); this.dataModelService = new DataModelService(context); - this.variableService = new VariableService(context); + this.variableService = new StudioVariableService(context); } public async getExportPackagesWithStudioData(packagesToExport: PackageExportTransport[], withDependencies: boolean): Promise { diff --git a/src/commands/studio/service/variable.service.ts b/src/commands/studio/service/variable.service.ts deleted file mode 100644 index d698dbe6..00000000 --- a/src/commands/studio/service/variable.service.ts +++ /dev/null @@ -1,124 +0,0 @@ -import { v4 as uuidv4 } from "uuid"; -import {URLSearchParams} from "url"; -import { - PackageManagerVariableType, - PackageWithVariableAssignments, - VariablesAssignments, -} from "../interfaces/package-manager.interfaces"; -import { Context } from "../../../core/command/cli-context"; -import { PackageApi } from "../api/package-api"; -import { FatalError, logger } from "../../../core/utils/logger"; -import { FileService, fileService } from "../../../core/utils/file-service"; -import { VariablesApi } from "../api/variables-api"; -import { PackageKeyAndVersionPair, VariableManifestTransport } from "../interfaces/package-export.interfaces"; -import { BatchImportExportApi } from "../api/batch-import-export-api"; -import { StudioService } from "./studio.service"; - -export class VariableService { - - private packageApi: PackageApi; - private variablesApi: VariablesApi; - private batchImportExportApi: BatchImportExportApi; - - private studioService: StudioService; - - constructor(context: Context) { - this.packageApi = new PackageApi(context); - this.variablesApi = new VariablesApi(context); - this.batchImportExportApi = new BatchImportExportApi(context); - - this.studioService = new StudioService(context); - } - - public async getVariableAssignmentsForNodes(type?: PackageManagerVariableType): Promise { - return await this.packageApi.findAllPackagesWithVariableAssignments(type); - } - - public async assignVariableValues(packageKey: string, variablesAssignments: VariablesAssignments[]): Promise { - await this.variablesApi.assignVariableValues(packageKey, variablesAssignments); - } - - public async listCandidateAssignments(type: string, params: string): Promise { - const parsedParams = this.parseParams(params); - const assignments = await this.variablesApi.getCandidateAssignments(type, parsedParams); - - assignments.forEach(assignment => { - logger.info(JSON.stringify(assignment)); - }); - } - - public async findAndExportCandidateAssignments(type: string, params: string): Promise { - const parsedParams = this.parseParams(params); - const assignments = await this.variablesApi.getCandidateAssignments(type, parsedParams); - - this.exportToJson(assignments) - } - - public async listVariables(keysByVersion: string[], keysByVersionFile: string): Promise { - const variableManifests = await this.getVersionedVariablesByKeyVersionPairs(keysByVersion, keysByVersionFile); - - variableManifests.forEach(variableManifest => { - logger.info(JSON.stringify(variableManifest)); - }); - } - - public async exportVariables(keysByVersion: string[], keysByVersionFile: string): Promise { - const variableManifests = await this.getVersionedVariablesByKeyVersionPairs(keysByVersion, keysByVersionFile); - - this.exportToJson(variableManifests); - } - - private parseParams(params?: string): URLSearchParams { - const queryParams = new URLSearchParams(); - - if (params) { - try { - params.split(",").forEach((param: string) => { - const paramKeyValuePair: string[] = param.split("="); - queryParams.set(paramKeyValuePair[0], paramKeyValuePair[1]); - }) - } catch (e) { - throw new FatalError(`Problem parsing query params: ${e}`); - } - } - - return queryParams; - } - - private async getVersionedVariablesByKeyVersionPairs(keysByVersion: string[], keysByVersionFile: string): Promise { - const variablesExportRequest: PackageKeyAndVersionPair[] = await this.buildKeyVersionPairs(keysByVersion, keysByVersionFile); - - const variableManifests = await this.batchImportExportApi.findVariablesWithValuesByPackageKeysAndVersion(variablesExportRequest); - return this.studioService.fixConnectionVariables(variableManifests); - } - - private async buildKeyVersionPairs(keysByVersion: string[], keysByVersionFile: string): Promise { - let variablesExportRequest: PackageKeyAndVersionPair[] = []; - - if (keysByVersion.length !== 0) { - variablesExportRequest = this.buildKeyAndVersionPairsFromArrayInput(keysByVersion); - } else if (keysByVersion.length === 0 && keysByVersionFile !== "") { - variablesExportRequest = await fileService.readFileToJson(keysByVersionFile); - } else { - throw new FatalError("Please provide keysByVersion mappings or file path!"); - } - - return variablesExportRequest; - } - - private buildKeyAndVersionPairsFromArrayInput(keysByVersion: string[]): PackageKeyAndVersionPair[] { - return keysByVersion.map(keyAndVersion => { - const keyAndVersionSplit = keyAndVersion.split(":"); - return { - packageKey: keyAndVersionSplit[0], - version: keyAndVersionSplit[1] - }; - }); - } - - private exportToJson(data: any): void { - const filename = uuidv4() + ".json"; - fileService.writeToFileWithGivenName(JSON.stringify(data), filename); - logger.info(FileService.fileDownloadedMessage + filename); - } -} From bcaa9bfe0c47b639cc8105f888dc079a02c16cf2 Mon Sep 17 00:00:00 2001 From: zgjimhaziri Date: Tue, 20 May 2025 18:30:59 +0200 Subject: [PATCH 33/45] TA-3769: Remove Pacman interfaces from this PR --- .../batch-export-import.constants.ts | 11 - .../interfaces/package-export.interfaces.ts | 86 ------ .../variable-assignment-apis.constants.ts | 12 - src/commands/studio/service/studio.service.ts | 280 ------------------ 4 files changed, 389 deletions(-) delete mode 100644 src/commands/configuration-management/interfaces/batch-export-import.constants.ts delete mode 100644 src/commands/configuration-management/interfaces/package-export.interfaces.ts delete mode 100644 src/commands/configuration-management/interfaces/variable-assignment-apis.constants.ts delete mode 100644 src/commands/studio/service/studio.service.ts diff --git a/src/commands/configuration-management/interfaces/batch-export-import.constants.ts b/src/commands/configuration-management/interfaces/batch-export-import.constants.ts deleted file mode 100644 index a0c815ea..00000000 --- a/src/commands/configuration-management/interfaces/batch-export-import.constants.ts +++ /dev/null @@ -1,11 +0,0 @@ -export enum BatchExportImportConstants { - STUDIO_FILE_NAME = "studio.json", - VARIABLES_FILE_NAME = "variables.json", - MANIFEST_FILE_NAME = "manifest.json", - STUDIO = "STUDIO", - APP_MODE_VIEWER = "VIEWER", - ZIP_EXTENSION = ".zip", - JSON_EXTENSION = ".json", - NODES_FOLDER_NAME = "nodes/", - SCENARIO_NODE = "SCENARIO" -} \ No newline at end of file diff --git a/src/commands/configuration-management/interfaces/package-export.interfaces.ts b/src/commands/configuration-management/interfaces/package-export.interfaces.ts deleted file mode 100644 index 604de747..00000000 --- a/src/commands/configuration-management/interfaces/package-export.interfaces.ts +++ /dev/null @@ -1,86 +0,0 @@ -import { SpaceTransport } from "../../studio/interfaces/space.interface"; -import { - StudioComputeNodeDescriptor, - VariableDefinition, - VariablesAssignments, -} from "../../studio/interfaces/package-manager.interfaces"; - -export interface DependencyTransport { - key: string; - version: string; -} - -export interface PackageExportTransport { - id: string; - key: string; - name: string; - changeDate: string; - activatedDraftId: string; - workingDraftId: string; - flavor: string; - version: string; - dependencies: DependencyTransport[]; - spaceId?: string; - datamodels?: StudioComputeNodeDescriptor[]; -} - -export interface PackageManifestTransport { - packageKey: string; - flavor: string; - activeVersion: string; - dependenciesByVersion: Map; -} - -export interface VariableExportTransport { - key: string; - value: any; - type: string; - metadata: object; -} - -export interface VariableManifestTransport { - packageKey: string; - version: string; - variables?: VariableExportTransport[]; -} - -export interface PackageKeyAndVersionPair { - packageKey: string; - version: string; -} - -export interface NodeExportTransport { - key: string; - parentNodeKey: string; - name: string; - type: string; - exportSerializationType: string; - configuration: NodeConfiguration; - schemaVersion: number; - - spaceId: string; - - invalidContent?: boolean; - serializedDocument?: Buffer; -} - -export interface NodeConfiguration { - variables?: VariableDefinition[]; - [key: string]: any; -} - -export interface StudioPackageManifest { - packageKey: string; - space: Partial; - runtimeVariableAssignments: VariablesAssignments[]; -} - -export interface PackageVersionImport { - oldVersion: string; - newVersion: string; -} - -export interface PostPackageImportData { - packageKey: string; - importedVersions: PackageVersionImport[]; -} \ No newline at end of file diff --git a/src/commands/configuration-management/interfaces/variable-assignment-apis.constants.ts b/src/commands/configuration-management/interfaces/variable-assignment-apis.constants.ts deleted file mode 100644 index 17147666..00000000 --- a/src/commands/configuration-management/interfaces/variable-assignment-apis.constants.ts +++ /dev/null @@ -1,12 +0,0 @@ -export const variableAssignmentApis: { [key: string]: VariableAssignmentApi } = { - DATA_MODEL: { - url: "/package-manager/api/compute-pools/pools-with-data-models" - }, - CONNECTION: { - url: "/process-automation-v2/api/connections" - } -}; - -export interface VariableAssignmentApi { - url: string; -} \ No newline at end of file diff --git a/src/commands/studio/service/studio.service.ts b/src/commands/studio/service/studio.service.ts deleted file mode 100644 index 3d6af458..00000000 --- a/src/commands/studio/service/studio.service.ts +++ /dev/null @@ -1,280 +0,0 @@ -import {IZipEntry} from "adm-zip"; -import * as AdmZip from "adm-zip"; -import { - NodeConfiguration, - NodeExportTransport, - PackageExportTransport, PackageKeyAndVersionPair, - StudioPackageManifest, VariableExportTransport, - VariableManifestTransport, -} from "../../configuration-management/interfaces/package-export.interfaces"; -import { Context } from "../../../core/command/cli-context"; -import { PackageApi } from "../api/package-api"; -import { - ContentNodeTransport, - PackageManagerVariableType, - PackageWithVariableAssignments, StudioComputeNodeDescriptor, -} from "../interfaces/package-manager.interfaces"; -import { NodeApi } from "../api/node-api"; -import { DataModelService } from "./data-model.service"; -import { SpaceTransport } from "../interfaces/space.interface"; -import { StudioVariableService } from "./studio-variable.service"; -import { SpaceApi } from "../api/space-api"; -import { StudioVariablesApi } from "../api/studio-variables-api"; -import { BatchExportImportConstants } from "../../configuration-management/interfaces/batch-export-import.constants"; -import { SpaceService } from "./space.service"; -import { parse, stringify } from "../../../core/utils/json"; - -export class StudioService { - - private packageApi: PackageApi; - private nodeApi: NodeApi; - private spaceApi: SpaceApi; - private variablesApi: StudioVariablesApi; - - private spaceService: SpaceService; - private dataModelService: DataModelService; - private variableService: StudioVariableService; - - constructor(context: Context) { - this.packageApi = new PackageApi(context); - this.nodeApi = new NodeApi(context); - this.spaceApi = new SpaceApi(context); - this.variablesApi = new StudioVariablesApi(context); - - this.spaceService = new SpaceService(context); - this.dataModelService = new DataModelService(context); - this.variableService = new StudioVariableService(context); - } - - public async getExportPackagesWithStudioData(packagesToExport: PackageExportTransport[], withDependencies: boolean): Promise { - const studioPackagesWithDataModels = await this.packageApi.findAllPackagesWithVariableAssignments(PackageManagerVariableType.DATA_MODEL); - - packagesToExport = this.setSpaceIdForStudioPackages(packagesToExport, studioPackagesWithDataModels); - - if (withDependencies) { - const dataModelDetailsByNode = await this.dataModelService.getDataModelDetailsForPackages(studioPackagesWithDataModels); - packagesToExport = this.setDataModelsForStudioPackages(packagesToExport, studioPackagesWithDataModels, dataModelDetailsByNode); - } - - return packagesToExport; - } - - public fixConnectionVariables(variables: VariableManifestTransport[]): VariableManifestTransport[] { - return variables.map(variableManifest => ({ - ...variableManifest, - variables: variableManifest.variables.map(variable => { - if (variable.type !== PackageManagerVariableType.CONNECTION) { - return variable; - } - - return this.fixConnectionVariable(variable); - }) - })); - } - - public async getStudioPackageManifests(studioPackageKeys: string[]): Promise { - return Promise.all(studioPackageKeys.map(async packageKey => { - const node = await this.nodeApi.findOneByKeyAndRootNodeKey(packageKey, packageKey); - const nodeSpace: SpaceTransport = await this.spaceApi.findOne(node.spaceId); - const variableAssignments = await this.variablesApi.getRuntimeVariableValues(packageKey, BatchExportImportConstants.APP_MODE_VIEWER); - - return { - packageKey: packageKey, - space: { - name: nodeSpace.name, - iconReference: nodeSpace.iconReference - }, - runtimeVariableAssignments: variableAssignments - } - })); - } - - public processPackageForExport(exportedPackage: IZipEntry, exportedVariables: VariableManifestTransport[]): AdmZip { - const packageZip = new AdmZip(exportedPackage.getData()); - this.deleteScenarioAssets(packageZip); - this.fixConnectionVariablesForRootNodeFiles(packageZip, exportedPackage.name, exportedVariables); - - return packageZip; - } - - public async processImportedPackages(configs: AdmZip, existingStudioPackages: ContentNodeTransport[], studioManifests: StudioPackageManifest[]): Promise { - if(studioManifests == null) { - return; - } - for (const manifest of studioManifests) { - const existingPackage = existingStudioPackages.find(existingPackage => existingPackage.key === manifest.packageKey); - if (existingPackage) { - await this.packageApi.movePackageToSpace(existingPackage.id, manifest.space.id); - } - await this.assignRuntimeVariables(manifest); - } - } - - private setSpaceIdForStudioPackages(packages: PackageExportTransport[], studioPackages: PackageWithVariableAssignments[]): PackageExportTransport[] { - const studioPackageByKey = new Map(); - studioPackages.forEach(pkg => studioPackageByKey.set(pkg.key, pkg)); - - return packages.map(pkg => { - return studioPackageByKey.has(pkg.key) ? { - ...pkg, - spaceId: studioPackageByKey.get(pkg.key).spaceId - } : pkg; - }); - } - - private setDataModelsForStudioPackages(packages: PackageExportTransport[], - studioPackageWithDataModels: PackageWithVariableAssignments[], - dataModelDetailsByNode: Map): PackageExportTransport[] { - const studioPackageByKey = new Map(); - studioPackageWithDataModels.forEach(pkg => studioPackageByKey.set(pkg.key, pkg)); - - return packages.map(pkg => { - return studioPackageByKey.has(pkg.key) ? { - ...pkg, - datamodels: dataModelDetailsByNode.get(pkg.key) - .map(dataModel => ({ - name: dataModel.name, - poolId: dataModel.poolId, - dataModelId: dataModel.dataModelId - })) - } : pkg; - }); - } - - private fixConnectionVariable(variable: VariableExportTransport): VariableExportTransport { - if (!variable.value.appName) { - return variable; - } - - return { - ...variable, - metadata: { - ...variable.metadata, - appName: variable.value.appName - } - } - } - - private deleteScenarioAssets(packageZip: AdmZip): void { - packageZip.getEntries().filter(entry => entry.entryName.startsWith(BatchExportImportConstants.NODES_FOLDER_NAME) && entry.entryName.endsWith(BatchExportImportConstants.JSON_EXTENSION)) - .forEach(entry => { - const node: NodeExportTransport = parse(entry.getData().toString()); - if (node.type === BatchExportImportConstants.SCENARIO_NODE) { - packageZip.deleteFile(entry); - } - }); - } - - private fixConnectionVariablesForRootNodeFiles(packageZip: AdmZip, zipName: string, exportedVariables: VariableManifestTransport[]): void { - const packageKeyAndVersion = this.getPackageKeyAndVersion(zipName); - const connectionVariablesByKey = this.getConnectionVariablesByKeyForPackage(packageKeyAndVersion.packageKey, packageKeyAndVersion.version, exportedVariables); - - if (connectionVariablesByKey.size === 0) { - return; - } - - const packageEntry = packageZip.getEntry("package.json"); - - const exportedNode: NodeExportTransport = parse(packageEntry.getData().toString()); - const nodeContent: NodeConfiguration = exportedNode.configuration; - - nodeContent.variables = nodeContent.variables.map(variable => ({ - ...variable, - metadata: variable.type === PackageManagerVariableType.CONNECTION ? - connectionVariablesByKey.get(variable.key).metadata : variable.metadata - })); - - packageZip.updateFile(packageEntry, Buffer.from(stringify(exportedNode))); - } - - private getPackageKeyAndVersion(zipName: string): PackageKeyAndVersionPair { - const lastUnderscoreIndex = zipName.lastIndexOf("_"); - const packageKey = zipName.replace(BatchExportImportConstants.ZIP_EXTENSION, "").substring(0, lastUnderscoreIndex); - const packageVersion = zipName.replace(BatchExportImportConstants.ZIP_EXTENSION, "").substring(lastUnderscoreIndex + 1); - - return { - packageKey: packageKey, - version: packageVersion - } - } - - private getConnectionVariablesByKeyForPackage(packageKey: string, packageVersion: string, variables: VariableManifestTransport[]): Map { - const variablesByKey = new Map(); - const packageVariables = variables.find(exportedVariable => exportedVariable.packageKey === packageKey && exportedVariable.version === packageVersion); - - if (packageVariables && packageVariables.variables.length) { - packageVariables.variables.filter(variable => variable.type === PackageManagerVariableType.CONNECTION) - .forEach(variable => variablesByKey.set(variable.key, variable)); - } - - return variablesByKey; - } - - public async mapSpaces(exportedFiles: AdmZip, studioManifests: StudioPackageManifest[]): Promise { - if (studioManifests == null) { - return exportedFiles; - } - for (const file of exportedFiles.getEntries()) { - if(file.name.endsWith(BatchExportImportConstants.ZIP_EXTENSION)) { - const packageKey = this.getPackageKeyAndVersion(file.name).packageKey; - - if (this.isStudioPackage(studioManifests, packageKey)) { - const studioManifest = studioManifests.find(manifest => manifest.packageKey === packageKey); - - if (studioManifest) { - const spaceId = await this.findDesiredSpaceIdForPackage(studioManifest); - studioManifest.space.id = spaceId; - - const packageZip = new AdmZip(file.getData()); - packageZip.getEntries().forEach(nodeFile => { - if (nodeFile.entryName.endsWith(BatchExportImportConstants.JSON_EXTENSION)) { - const updatedNodeFile = this.updateSpaceIdForNode(nodeFile.getData().toString(), spaceId); - packageZip.updateFile(nodeFile, Buffer.from(updatedNodeFile)); - } - }); - exportedFiles.updateFile(file, packageZip.toBuffer()); - } - } - } - } - return exportedFiles; - } - - private isStudioPackage(studioManifests: StudioPackageManifest[], packageKey: string): boolean { - return studioManifests.some(manifest => manifest.packageKey === packageKey); - } - - private async findDesiredSpaceIdForPackage(studioPackageManifest: StudioPackageManifest): Promise { - const allSpaces = await this.spaceService.refreshAndGetAllSpaces(); - - if (studioPackageManifest.space.id) { - const targetSpace = allSpaces.find(space => space.id === studioPackageManifest.space.id); - if (!targetSpace) { - throw Error("Provided space ID does not exist."); - } - return targetSpace.id; - } - - const targetSpaceByName = allSpaces.find(space => space.name === studioPackageManifest.space.name); - if (targetSpaceByName) { - return targetSpaceByName.id; - } - - const spaceTransport = await this.spaceService.createSpace(studioPackageManifest.space.name, studioPackageManifest.space.iconReference); - return spaceTransport.id; - } - - private async assignRuntimeVariables(manifest: StudioPackageManifest): Promise { - if (manifest.runtimeVariableAssignments.length) { - await this.variableService.assignVariableValues(manifest.packageKey, manifest.runtimeVariableAssignments); - } - } - - private updateSpaceIdForNode(nodeContent: string, spaceId: string): string { - const exportedNode: NodeExportTransport = parse(nodeContent); - const oldSpaceId = exportedNode.spaceId; - - nodeContent = nodeContent.replace(new RegExp(oldSpaceId, "g"), spaceId); - return nodeContent; - } -} From b2a35bb7213a01f9bddcca1e91acb8494af04247 Mon Sep 17 00:00:00 2001 From: zgjimhaziri Date: Tue, 20 May 2025 18:48:29 +0200 Subject: [PATCH 34/45] TA-3770: Migrate CPM4 commands --- src/commands/cpm4/ctp-command.service.ts | 42 +++++++++++++++++ src/commands/cpm4/ctp.analysis.manager.ts | 23 ++++++++++ src/commands/cpm4/ctp.datamodel.manager.ts | 30 +++++++++++++ src/commands/cpm4/ctp.manager-factory.ts | 48 ++++++++++++++++++++ src/commands/cpm4/ctp.manager.ts | 52 ++++++++++++++++++++++ src/commands/cpm4/module.ts | 39 +++++++++++++++- 6 files changed, 233 insertions(+), 1 deletion(-) create mode 100644 src/commands/cpm4/ctp-command.service.ts create mode 100644 src/commands/cpm4/ctp.analysis.manager.ts create mode 100644 src/commands/cpm4/ctp.datamodel.manager.ts create mode 100644 src/commands/cpm4/ctp.manager-factory.ts create mode 100644 src/commands/cpm4/ctp.manager.ts diff --git a/src/commands/cpm4/ctp-command.service.ts b/src/commands/cpm4/ctp-command.service.ts new file mode 100644 index 00000000..5a5fa69f --- /dev/null +++ b/src/commands/cpm4/ctp-command.service.ts @@ -0,0 +1,42 @@ +import { Context } from "../../core/command/cli-context"; +import { ContentService } from "../../core/http/http-shared/content.service"; +import { FatalError, logger } from "../../core/utils/logger"; +import { CTPManagerFactory } from "./ctp.manager-factory"; + +export class CTPCommandService { + private contentService = new ContentService(); + private ctpManagerFactory: CTPManagerFactory; + + constructor(context: Context) { + this.ctpManagerFactory = new CTPManagerFactory(context); + } + + public async pushCTPFile( + filename: string, + password: string, + pushAnalysis: boolean, + pushDataModels: boolean, + existingPoolId: string, + globalPoolName: string, + spaceKey: string + ): Promise { + if (pushAnalysis) { + await this.contentService.push(this.ctpManagerFactory.createCtpAnalysisManager(filename, password, spaceKey)); + } + + if (pushDataModels) { + this.validateParamsForDataModelPush(existingPoolId, globalPoolName); + await this.contentService.push(this.ctpManagerFactory.createCtpDataModelManager(filename, password, existingPoolId, globalPoolName)); + } + } + + private validateParamsForDataModelPush(existingPoolId: string, globalPoolName: string): void { + if (existingPoolId != null && globalPoolName != null) { + logger.error( + new FatalError( + "You should specify only one of those options --globalPoolName, --existingPoolId, they are mutual exclusive" + ) + ); + } + } +} diff --git a/src/commands/cpm4/ctp.analysis.manager.ts b/src/commands/cpm4/ctp.analysis.manager.ts new file mode 100644 index 00000000..86fc0e70 --- /dev/null +++ b/src/commands/cpm4/ctp.analysis.manager.ts @@ -0,0 +1,23 @@ +import { CtpManager } from "./ctp.manager"; +import * as FormData from "form-data"; +import { Context } from "../../core/command/cli-context"; + +export class CtpAnalysisManager extends CtpManager { + private static BASE_URL = "/process-analytics/import/ctp"; + + constructor(context: Context) { + super(context); + } + + protected getUrl(): string { + return CtpAnalysisManager.BASE_URL; + } + + public getBody(): any { + const formData = new FormData(); + formData.append("file", this.content); + formData.append("password", this.password); + formData.append("spaceId", this.spaceKey); + return formData; + } +} diff --git a/src/commands/cpm4/ctp.datamodel.manager.ts b/src/commands/cpm4/ctp.datamodel.manager.ts new file mode 100644 index 00000000..501b2bf3 --- /dev/null +++ b/src/commands/cpm4/ctp.datamodel.manager.ts @@ -0,0 +1,30 @@ +import { CtpManager } from "./ctp.manager"; +import * as FormData from "form-data"; +import { Context } from "../../core/command/cli-context"; + +export class CtpDataModelManager extends CtpManager { + private static BASE_URL = "/cpm-ems-migrator/migration/api/ctp"; + private readonly existingPoolId: string; + private readonly globalPoolName: string; + + constructor(context: Context, existingPoolId: string, globalPoolName: string) { + super(context); + this.existingPoolId = existingPoolId; + this.globalPoolName = globalPoolName; + } + + protected getUrl(): string { + return CtpDataModelManager.BASE_URL; + } + + protected getBody(): object { + const formData = new FormData(); + formData.append("file", this.content); + formData.append("transport", JSON.stringify({ + password: this.password, + existingPoolId: this.existingPoolId, + globalPoolName: this.globalPoolName, + })); + return formData; + } +} diff --git a/src/commands/cpm4/ctp.manager-factory.ts b/src/commands/cpm4/ctp.manager-factory.ts new file mode 100644 index 00000000..085b6472 --- /dev/null +++ b/src/commands/cpm4/ctp.manager-factory.ts @@ -0,0 +1,48 @@ +import * as fs from "fs"; +import * as path from "path"; +import { ReadStream } from "fs"; +import { Context } from "../../core/command/cli-context"; +import { CtpAnalysisManager } from "./ctp.analysis.manager"; +import { CtpDataModelManager } from "./ctp.datamodel.manager"; +import { CtpManager } from "./ctp.manager"; +import { FatalError, logger } from "../../core/utils/logger"; + +export class CTPManagerFactory { + + private readonly context: Context; + + constructor(context: Context) { + this.context = context; + } + + public createCtpAnalysisManager(filename: string, password: string, spaceKey: string): CtpManager { + const ctpManager = new CtpAnalysisManager(this.context); + return this.initManager(ctpManager, filename, password, spaceKey); + } + + public createCtpDataModelManager( + filename: string, + password: string, + existingPoolId: string, + globalPoolName: string + ): CtpManager { + const ctpManager = new CtpDataModelManager(this.context, existingPoolId, globalPoolName); + return this.initManager(ctpManager, filename, password); + } + + private initManager(ctpManager: CtpManager, filename: string, password: string, spaceKey?: string): CtpManager { + ctpManager.password = password; + ctpManager.spaceKey = spaceKey; + if (filename !== null) { + ctpManager.content = this.readFile(filename); + } + return ctpManager; + } + + private readFile(filename: string): ReadStream { + if (!fs.existsSync(path.resolve(process.cwd(), filename))) { + logger.error(new FatalError("The provided file does not exit")); + } + return fs.createReadStream(path.resolve(process.cwd(), filename)); + } +} diff --git a/src/commands/cpm4/ctp.manager.ts b/src/commands/cpm4/ctp.manager.ts new file mode 100644 index 00000000..52ceb20b --- /dev/null +++ b/src/commands/cpm4/ctp.manager.ts @@ -0,0 +1,52 @@ +import { Context } from "../../core/command/cli-context"; +import { BaseManager } from "../../core/http/http-shared/base.manager"; +import { ManagerConfig } from "../../core/http/http-shared/manager-config.interface"; + +export abstract class CtpManager extends BaseManager { + protected _content: any; + protected _password: string; + protected _spaceKey?: string; + + protected constructor(context: Context) { + super(context); + } + + public get content(): any { + return this._content; + } + + public set content(value: any) { + this._content = value; + } + + public get password(): string { + return this._password; + } + + public set password(value: string) { + this._password = value; + } + + public get spaceKey(): string { + return this._spaceKey; + } + + public set spaceKey(value: string) { + this._spaceKey = value; + } + + public getConfig(): ManagerConfig { + return { + pushUrl: this.getUrl(), + onPushSuccessMessage: (): string => { + return "CTP File was pushed successfully"; + }, + }; + } + + protected getSerializedFileContent(data: any): string { + return JSON.stringify(data); + } + + protected abstract getUrl(): string; +} diff --git a/src/commands/cpm4/module.ts b/src/commands/cpm4/module.ts index 32075375..0adc981f 100644 --- a/src/commands/cpm4/module.ts +++ b/src/commands/cpm4/module.ts @@ -1,15 +1,52 @@ /** - * Commands related to the Action Flows area. + * Commands related to the CPM4 area. */ import { Configurator, IModule } from "../../core/command/module-handler"; import { Context } from "../../core/command/cli-context"; +import { Command, OptionValues } from "commander"; +import { CTPCommandService } from "./ctp-command.service"; class Module extends IModule { register(context: Context, configurator: Configurator) { + const pushCommand = configurator.command("push") + pushCommand.command("ctp") + .description("Command to push a .ctp (Celonis 4 transport file) to create a package") + .option("-p, --profile ", "Profile which you want to use to push the analysis") + .option("-a, --pushAnalysis", "Specify this option if you want to push analysis from the CTP file") + .option("-d, --pushDataModels", "Specify this option if you want to push data models from the CTP file") + .option( + "--globalPoolName ", + "Specify this option if you want to push all Data models into one newly created pool along with value to set the name of the pool to be created", + null + ) + .option( + "--existingPoolId ", + "Specify this option if you want to push all Data models into one already existing pool with provided ID", + null + ) + .option( + "-s, --spaceKey ", + "The key of the destination space where the analyses from .ctp file will be created.", + "" + ) + .requiredOption("-f, --file ", "The .ctp file you want to push") + .requiredOption("--password ", "The password used for extracting the .ctp file") + .action(this.pushCTPFile); } + private async pushCTPFile(context: Context, command: Command, options: OptionValues): Promise { + await new CTPCommandService(context).pushCTPFile( + options.file, + options.password, + options.pushAnalysis, + options.pushDataModels, + options.existingPoolId, + options.globalPoolName, + options.spaceKey + ); + } } export = Module; \ No newline at end of file From 4c022cec3d35ce8169d3e62bd8fe42d18378cec9 Mon Sep 17 00:00:00 2001 From: zgjimhaziri Date: Wed, 21 May 2025 11:07:35 +0200 Subject: [PATCH 35/45] TA-3770: Add individual codeowners --- .github/CODEOWNERS | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 35493b46..9e9f689a 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -8,5 +8,5 @@ #/src/commands/action-flows/ action-flow team #/src/commands/analysis/ filter and foundation team #/src/commands/cpm4/ cpm4 team -#/src/commands/data-pipeline/ data-management team -#/src/commands/studio/ @celonis/navi +/src/commands/data-pipeline/ @Dusan-r @IvanGandacov @EktaCelonis @gorasoCelonis +#/src/commands/studio/ @celonis/navi \ No newline at end of file From 4a9434788917bbe104cfefbb73d4a06fa37a2e1f Mon Sep 17 00:00:00 2001 From: zgjimhaziri Date: Wed, 21 May 2025 11:14:05 +0200 Subject: [PATCH 36/45] TA-3770: Add cpm4 codeowners --- .github/CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 71e8aabc..f4ba2955 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -7,6 +7,6 @@ #/src/commands/profile/ @celonis/astro #/src/commands/action-flows/ action-flow team #/src/commands/analysis/ filter and foundation team -#/src/commands/cpm4/ cpm4 team +/src/commands/cpm4/ @celonis/cpm4 /src/commands/data-pipeline/ @Dusan-r @IvanGandacov @EktaCelonis @gorasoCelonis /src/commands/studio/ @celonis/navi From 367a73fd4a2b31bd7435be9b3e20208174f03d1b Mon Sep 17 00:00:00 2001 From: zgjimhaziri Date: Wed, 21 May 2025 11:26:18 +0200 Subject: [PATCH 37/45] TA-3769: Add process-analytics team as owners --- .github/CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 71e8aabc..766d8ea6 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -6,7 +6,7 @@ #/src/commands/configuration-management/ @celonis/astro #/src/commands/profile/ @celonis/astro #/src/commands/action-flows/ action-flow team -#/src/commands/analysis/ filter and foundation team +/src/commands/analysis/ @celonis/process-analytics #/src/commands/cpm4/ cpm4 team /src/commands/data-pipeline/ @Dusan-r @IvanGandacov @EktaCelonis @gorasoCelonis /src/commands/studio/ @celonis/navi From cc86dffc84b0a0d2969b7511c1a1a00dd74d183f Mon Sep 17 00:00:00 2001 From: zgjimhaziri Date: Thu, 22 May 2025 10:31:47 +0200 Subject: [PATCH 38/45] TA-3768: Setup test config --- .github/workflows/build.yml | 4 +- tests/jest.setup.ts | 28 +++++++++++ tests/mocks/package.json | 3 ++ tests/utls/fs-mock-utils.ts | 21 ++++++++ tests/utls/http-requests-mock.ts | 84 ++++++++++++++++++++++++++++++++ tests/utls/test-context.ts | 13 +++++ tests/utls/test-transport.ts | 19 ++++++++ 7 files changed, 170 insertions(+), 2 deletions(-) create mode 100644 tests/jest.setup.ts create mode 100644 tests/mocks/package.json create mode 100644 tests/utls/fs-mock-utils.ts create mode 100644 tests/utls/http-requests-mock.ts create mode 100644 tests/utls/test-context.ts create mode 100644 tests/utls/test-transport.ts diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a8af1fd1..5e078070 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,5 +16,5 @@ jobs: run: yarn install - name: Yarn Build run: yarn build -# - name: Yarn Test -# run: yarn test + - name: Yarn Test + run: yarn test diff --git a/tests/jest.setup.ts b/tests/jest.setup.ts new file mode 100644 index 00000000..fda6a04c --- /dev/null +++ b/tests/jest.setup.ts @@ -0,0 +1,28 @@ +// Mock the modules using Jest +import * as fs from "fs"; +import { mockAxios } from "./utls/http-requests-mock"; +import { TestTransport } from "./utls/test-transport"; +import { logger } from "../src/core/utils/logger"; + +mockAxios(); +jest.mock("fs"); + +const mockWriteFileSync = jest.fn(); +(fs.writeFileSync as jest.Mock).mockImplementation(mockWriteFileSync); + +const mockWriteSync = jest.fn(); +(fs.writeSync as jest.Mock).mockImplementation(mockWriteSync); + +afterEach(() => { + jest.clearAllMocks(); +}); + +let testTransport: TestTransport; + +beforeEach(() => { + jest.clearAllMocks(); + testTransport = new TestTransport({}); + logger.add(testTransport); +}); + +export {testTransport, mockWriteFileSync, mockWriteSync}; \ No newline at end of file diff --git a/tests/mocks/package.json b/tests/mocks/package.json new file mode 100644 index 00000000..525ca75b --- /dev/null +++ b/tests/mocks/package.json @@ -0,0 +1,3 @@ +{ + "version": "mocked-version" +} \ No newline at end of file diff --git a/tests/utls/fs-mock-utils.ts b/tests/utls/fs-mock-utils.ts new file mode 100644 index 00000000..2c01ceab --- /dev/null +++ b/tests/utls/fs-mock-utils.ts @@ -0,0 +1,21 @@ +import * as fs from "fs"; +import {Readable} from "stream"; + +export function mockReadFileSync(data: any): void { + (fs.readFileSync as jest.Mock).mockReturnValue(data); +} + +export function mockCreateReadStream(data: any): void { + const stream = new Readable(); + stream.push(data); + stream.push(null); + (fs.createReadStream as jest.Mock).mockReturnValue(stream); +} + +export function mockExistsSync(): void { + (fs.existsSync as jest.Mock).mockReturnValue(true); +} + +export function mockExistsSyncOnce(): void { + (fs.existsSync as jest.Mock).mockReturnValueOnce(true); +} \ No newline at end of file diff --git a/tests/utls/http-requests-mock.ts b/tests/utls/http-requests-mock.ts new file mode 100644 index 00000000..d02d1c17 --- /dev/null +++ b/tests/utls/http-requests-mock.ts @@ -0,0 +1,84 @@ +import { AxiosInstance } from "axios"; +import {Readable} from "stream"; +import { AxiosInitializer } from "../../src/core/http/axios-initializer"; + +const mockedAxiosInstance = {} as AxiosInstance; + +const mockedGetResponseByUrl = new Map(); +const mockedPostResponseByUrl = new Map(); +const mockedPostRequestBodyByUrl = new Map(); + +const mockAxios = () : void => { + AxiosInitializer.initializeAxios = jest.fn().mockReturnValue(mockedAxiosInstance); + + mockedAxiosInstance.get = jest.fn(); + mockedAxiosInstance.post = jest.fn(); + mockedAxiosInstance.put = jest.fn(); +} + +const mockAxiosGet = (url: string, responseData: any) => { + mockedGetResponseByUrl.set(url, responseData); + (mockedAxiosInstance.get as jest.Mock).mockImplementation(requestUrl => { + if (mockedGetResponseByUrl.has(requestUrl)) { + const response = { data: mockedGetResponseByUrl.get(requestUrl) }; + + if (response.data instanceof Buffer) { + const readableStream = new Readable(); + readableStream.push(response.data) + readableStream.push(null); + return Promise.resolve({ + status: 200, + data: readableStream, + }); + } else { + return Promise.resolve(response); + } + } else { + fail("API call not mocked.") + } + }); +}; + +const mockAxiosPost = (url: string, responseData: any) => { + mockedPostResponseByUrl.set(url, responseData); + + (mockedAxiosInstance.post as jest.Mock).mockImplementation((requestUrl: string, data: any) => { + if (mockedPostResponseByUrl.has(requestUrl)) { + const response = { data: mockedPostResponseByUrl.get(requestUrl) }; + mockedPostRequestBodyByUrl.set(requestUrl, data); + + return Promise.resolve(response); + } else { + fail("API call not mocked.") + } + }) +} + +const mockAxiosPut = (url: string, responseData: any) => { + mockedPostResponseByUrl.set(url, responseData); + (mockedAxiosInstance.put as jest.Mock).mockImplementation((requestUrl: string, data: any) => { + if (mockedPostResponseByUrl.has(requestUrl)) { + const response = { data: mockedPostResponseByUrl.get(requestUrl) }; + mockedPostRequestBodyByUrl.set(requestUrl, data); + + return Promise.resolve(response); + } else { + fail("API call not mocked.") + } + }) +} + +afterEach(() => { + mockedGetResponseByUrl.clear(); + mockedPostResponseByUrl.clear(); + mockedPostRequestBodyByUrl.clear(); +}) + +export { + mockedAxiosInstance, + mockAxios, + mockAxiosGet, + mockAxiosPost, + mockAxiosPut, + mockedPostRequestBodyByUrl +}; diff --git a/tests/utls/test-context.ts b/tests/utls/test-context.ts new file mode 100644 index 00000000..376863c8 --- /dev/null +++ b/tests/utls/test-context.ts @@ -0,0 +1,13 @@ +import { Context } from "../../src/core/command/cli-context"; +import { HttpClient } from "../../src/core/http/http-client"; + +const testContext = new Context({}); +testContext.profile = { + name: "test", + type: "Key", + team: "https://myTeam.celonis.cloud/", + apiToken: "YnQ3N2M0M2ItYzQ3OS00YzgyLTg0ODgtOWNkNzhiNzYwOTU2OlFkNnBpVCs0M0JBYm1ZWGlCZ2hPd245aldwWTNubFQyYVFOTFBUeHEwdUxM", + authenticationType: "Bearer" +} +testContext.httpClient = new HttpClient(testContext); +export { testContext }; diff --git a/tests/utls/test-transport.ts b/tests/utls/test-transport.ts new file mode 100644 index 00000000..53114a06 --- /dev/null +++ b/tests/utls/test-transport.ts @@ -0,0 +1,19 @@ +import * as Transport from "winston-transport"; +import {LogEntry} from "winston"; + +export class TestTransport extends Transport { + public logMessages: LogEntry[] = []; + + constructor(options: any) { + super(options); + } + + public log(logEntry: LogEntry, callback: () => void): void { + if (logEntry.level.includes("debug")) { + callback(); + return; + } + this.logMessages.push(logEntry); + callback(); + } +} \ No newline at end of file From ed95597c7199cbd9768766a1c4472f43a563e2df Mon Sep 17 00:00:00 2001 From: zgjimhaziri Date: Thu, 22 May 2025 10:38:46 +0200 Subject: [PATCH 39/45] TA-3768: Renames and uncommenting --- jest.config.ts | 46 +++++++++---------- tests/jest.setup.ts | 6 +-- ...transport.ts => logging-test-transport.ts} | 2 +- 3 files changed, 27 insertions(+), 27 deletions(-) rename tests/utls/{test-transport.ts => logging-test-transport.ts} (88%) diff --git a/jest.config.ts b/jest.config.ts index 3e7cddc0..6b6f80d3 100644 --- a/jest.config.ts +++ b/jest.config.ts @@ -1,23 +1,23 @@ -// import type { Config } from "@jest/types" -// const config: Config.InitialOptions = { -// verbose: true, -// transform: { -// "^.+\\.tsx?$": "ts-jest", -// }, -// testMatch: ["/tests/**/*.spec.ts"], -// moduleNameMapper: { -// "^\\./../package.json$": "/tests/mocks/package.json", -// }, -// setupFilesAfterEnv: [ -// "/tests/jest.setup.ts", -// ], -// globals: { -// "ts-jest": { -// tsconfig: { -// sourceMap: true -// } -// } -// } -// } -// -// export default config \ No newline at end of file +import type { Config } from "@jest/types" +const config: Config.InitialOptions = { + verbose: true, + transform: { + "^.+\\.tsx?$": "ts-jest", + }, + testMatch: ["/tests/**/*.spec.ts"], + moduleNameMapper: { + "^\\./../package.json$": "/tests/mocks/package.json", + }, + setupFilesAfterEnv: [ + "/tests/jest.setup.ts", + ], + globals: { + "ts-jest": { + tsconfig: { + sourceMap: true + } + } + } +} + +export default config \ No newline at end of file diff --git a/tests/jest.setup.ts b/tests/jest.setup.ts index fda6a04c..7857fc00 100644 --- a/tests/jest.setup.ts +++ b/tests/jest.setup.ts @@ -1,7 +1,7 @@ // Mock the modules using Jest import * as fs from "fs"; import { mockAxios } from "./utls/http-requests-mock"; -import { TestTransport } from "./utls/test-transport"; +import { LoggingTestTransport } from "./utls/logging-test-transport"; import { logger } from "../src/core/utils/logger"; mockAxios(); @@ -17,11 +17,11 @@ afterEach(() => { jest.clearAllMocks(); }); -let testTransport: TestTransport; +let testTransport: LoggingTestTransport; beforeEach(() => { jest.clearAllMocks(); - testTransport = new TestTransport({}); + testTransport = new LoggingTestTransport({}); logger.add(testTransport); }); diff --git a/tests/utls/test-transport.ts b/tests/utls/logging-test-transport.ts similarity index 88% rename from tests/utls/test-transport.ts rename to tests/utls/logging-test-transport.ts index 53114a06..f3848e04 100644 --- a/tests/utls/test-transport.ts +++ b/tests/utls/logging-test-transport.ts @@ -1,7 +1,7 @@ import * as Transport from "winston-transport"; import {LogEntry} from "winston"; -export class TestTransport extends Transport { +export class LoggingTestTransport extends Transport { public logMessages: LogEntry[] = []; constructor(options: any) { From 1b38ca9d260af13e495b408b961352bbb83be9a8 Mon Sep 17 00:00:00 2001 From: zgjimhaziri Date: Thu, 22 May 2025 10:39:11 +0200 Subject: [PATCH 40/45] TA-3768: Comment out `yarn test` in build.yml --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5e078070..a8af1fd1 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,5 +16,5 @@ jobs: run: yarn install - name: Yarn Build run: yarn build - - name: Yarn Test - run: yarn test +# - name: Yarn Test +# run: yarn test From 1b4221ca7466389756a1b0f91a48148fea047680 Mon Sep 17 00:00:00 2001 From: zgjimhaziri Date: Thu, 22 May 2025 10:43:43 +0200 Subject: [PATCH 41/45] TA-3768: Rename variable used in logging transport --- tests/jest.setup.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/jest.setup.ts b/tests/jest.setup.ts index 7857fc00..ae3002dd 100644 --- a/tests/jest.setup.ts +++ b/tests/jest.setup.ts @@ -17,12 +17,12 @@ afterEach(() => { jest.clearAllMocks(); }); -let testTransport: LoggingTestTransport; +let loggingTestTransport: LoggingTestTransport; beforeEach(() => { jest.clearAllMocks(); - testTransport = new LoggingTestTransport({}); - logger.add(testTransport); + loggingTestTransport = new LoggingTestTransport({}); + logger.add(loggingTestTransport); }); -export {testTransport, mockWriteFileSync, mockWriteSync}; \ No newline at end of file +export {loggingTestTransport, mockWriteFileSync, mockWriteSync}; \ No newline at end of file From 5771e931c18106a9d084fbda4a82148e673e457c Mon Sep 17 00:00:00 2001 From: zgjimhaziri Date: Thu, 22 May 2025 16:19:31 +0200 Subject: [PATCH 42/45] TA-3768: Use test token --- tests/utls/test-context.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/utls/test-context.ts b/tests/utls/test-context.ts index 376863c8..e41d6937 100644 --- a/tests/utls/test-context.ts +++ b/tests/utls/test-context.ts @@ -6,7 +6,7 @@ testContext.profile = { name: "test", type: "Key", team: "https://myTeam.celonis.cloud/", - apiToken: "YnQ3N2M0M2ItYzQ3OS00YzgyLTg0ODgtOWNkNzhiNzYwOTU2OlFkNnBpVCs0M0JBYm1ZWGlCZ2hPd245aldwWTNubFQyYVFOTFBUeHEwdUxM", + apiToken: "test-token", authenticationType: "Bearer" } testContext.httpClient = new HttpClient(testContext); From c69d03079edfacd8efdd1bd773849c43134f56a7 Mon Sep 17 00:00:00 2001 From: zgjimhaziri Date: Thu, 22 May 2025 16:32:06 +0200 Subject: [PATCH 43/45] TA-3768: Make fs import consistent across projects --- .../data-pipeline/data-pool/data-pool-manager.factory.ts | 2 +- src/commands/studio/command-service/widget.command.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/commands/data-pipeline/data-pool/data-pool-manager.factory.ts b/src/commands/data-pipeline/data-pool/data-pool-manager.factory.ts index 5b733006..9a367007 100644 --- a/src/commands/data-pipeline/data-pool/data-pool-manager.factory.ts +++ b/src/commands/data-pipeline/data-pool/data-pool-manager.factory.ts @@ -1,7 +1,7 @@ import * as path from "path"; import { DataPoolManager } from "./data-pool.manager"; import { FatalError, logger } from "../../../core/utils/logger"; -import * as fs from "node:fs"; +import * as fs from "fs"; import { Context } from "../../../core/command/cli-context"; export class DataPoolManagerFactory { diff --git a/src/commands/studio/command-service/widget.command.ts b/src/commands/studio/command-service/widget.command.ts index ef58409d..cc483507 100644 --- a/src/commands/studio/command-service/widget.command.ts +++ b/src/commands/studio/command-service/widget.command.ts @@ -1,6 +1,6 @@ import { execSync } from "child_process"; import { GracefulError, logger } from "../../../core/utils/logger"; -import * as fs from "node:fs"; +import * as fs from "fs"; import * as path from "path"; import { ContentService } from "../../../core/http/http-shared/content.service"; import { Context } from "../../../core/command/cli-context"; From acebee57cfcb2816f3849dc1c27b236e16360b79 Mon Sep 17 00:00:00 2001 From: Zgjim Haziri <37958277+ZgjimHaziri@users.noreply.github.com> Date: Fri, 23 May 2025 13:29:14 +0200 Subject: [PATCH 44/45] TA-3768: Migrate Action Flows commands (#206) --- .github/CODEOWNERS | 3 +- .github/workflows/build.yml | 4 +- .../action-flow/action-flow-api.ts | 35 ++++ .../action-flow-command.service.ts | 23 +++ .../action-flow/action-flow.service.ts | 79 ++++++++ src/commands/action-flows/module.ts | 63 ++++++ .../skill/skill-command.service.ts | 20 ++ .../skill/skill.manager-factory.ts | 32 ++++ .../action-flows/skill/skill.manager.ts | 59 ++++++ .../action-flows/analyze-action-flows.spec.ts | 70 +++++++ .../action-flows/export-action-flows.spec.ts | 181 ++++++++++++++++++ .../action-flows/import-action-flows.spec.ts | 56 ++++++ 12 files changed, 622 insertions(+), 3 deletions(-) create mode 100644 src/commands/action-flows/action-flow/action-flow-api.ts create mode 100644 src/commands/action-flows/action-flow/action-flow-command.service.ts create mode 100644 src/commands/action-flows/action-flow/action-flow.service.ts create mode 100644 src/commands/action-flows/skill/skill-command.service.ts create mode 100644 src/commands/action-flows/skill/skill.manager-factory.ts create mode 100644 src/commands/action-flows/skill/skill.manager.ts create mode 100644 tests/commands/action-flows/analyze-action-flows.spec.ts create mode 100644 tests/commands/action-flows/export-action-flows.spec.ts create mode 100644 tests/commands/action-flows/import-action-flows.spec.ts diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 40d3203b..1d2cb4f8 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -5,7 +5,8 @@ #/src/core @celonis/astro #/src/commands/configuration-management/ @celonis/astro #/src/commands/profile/ @celonis/astro -#/src/commands/action-flows/ @celonis/process-automation +/src/commands/action-flows/ @celonis/process-automation +/tests/commands/action-flows/ @celonis/process-automation /src/commands/analysis/ @celonis/process-analytics /src/commands/cpm4/ @celonis/cpm4 /src/commands/data-pipeline/ @Dusan-r @IvanGandacov @EktaCelonis @gorasoCelonis diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a8af1fd1..5e078070 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,5 +16,5 @@ jobs: run: yarn install - name: Yarn Build run: yarn build -# - name: Yarn Test -# run: yarn test + - name: Yarn Test + run: yarn test diff --git a/src/commands/action-flows/action-flow/action-flow-api.ts b/src/commands/action-flows/action-flow/action-flow-api.ts new file mode 100644 index 00000000..04c90486 --- /dev/null +++ b/src/commands/action-flows/action-flow/action-flow-api.ts @@ -0,0 +1,35 @@ +import * as FormData from "form-data"; +import { HttpClient } from "../../../core/http/http-client"; +import { Context } from "../../../core/command/cli-context"; +import { FatalError } from "../../../core/utils/logger"; + +export class ActionFlowApi { + + private httpClient: HttpClient; + + constructor(context: Context) { + this.httpClient = context.httpClient; + } + + public async exportRawAssets(packageId: string): Promise { + return this.httpClient.getFile(`/ems-automation/api/root/${packageId}/export/assets`).catch(e => { + throw new FatalError(`Problem getting Action Flow assets: ${e}`); + }); + } + + public async analyzeAssets(packageId: string): Promise { + return this.httpClient.get(`/ems-automation/api/root/${packageId}/export/assets/analyze`).catch(e => { + throw new FatalError(`Problem analyzing Action Flow assets: ${e}`); + }); + } + + public async importAssets(packageId: string, data: FormData, dryRun: boolean): Promise { + const params = { + dryRun: dryRun, + }; + + return this.httpClient.postFile(`/ems-automation/api/root/${packageId}/import/assets`, data, params).catch(e => { + throw new FatalError(`Problem importing Action Flow assets: ${e}`); + }); + } +} diff --git a/src/commands/action-flows/action-flow/action-flow-command.service.ts b/src/commands/action-flows/action-flow/action-flow-command.service.ts new file mode 100644 index 00000000..1a69b678 --- /dev/null +++ b/src/commands/action-flows/action-flow/action-flow-command.service.ts @@ -0,0 +1,23 @@ +import { Context } from "../../../core/command/cli-context"; +import { ActionFlowService } from "./action-flow.service"; + +export class ActionFlowCommandService { + + private actionFlowService: ActionFlowService; + + constructor(context: Context) { + this.actionFlowService = new ActionFlowService(context); + } + + public async exportActionFlows(packageId: string, metadataFile: string): Promise { + await this.actionFlowService.exportActionFlows(packageId, metadataFile); + } + + public async analyzeActionFlows(packageId: string, outputToJsonFile: boolean): Promise { + await this.actionFlowService.analyzeActionFlows(packageId, outputToJsonFile); + } + + public async importActionFlows(packageId: string, filePath: string, dryRun: boolean, outputToJsonFile: boolean): Promise { + await this.actionFlowService.importActionFlows(packageId, filePath, dryRun, outputToJsonFile); + } +} \ No newline at end of file diff --git a/src/commands/action-flows/action-flow/action-flow.service.ts b/src/commands/action-flows/action-flow/action-flow.service.ts new file mode 100644 index 00000000..29365dc8 --- /dev/null +++ b/src/commands/action-flows/action-flow/action-flow.service.ts @@ -0,0 +1,79 @@ +import { v4 as uuidv4 } from "uuid"; +import * as AdmZip from "adm-zip"; +import * as FormData from "form-data"; +import * as fs from "fs"; +import { Context } from "../../../core/command/cli-context"; +import { ActionFlowApi } from "./action-flow-api"; +import { fileService, FileService } from "../../../core/utils/file-service"; +import { logger } from "../../../core/utils/logger"; + +export class ActionFlowService { + public static readonly METADATA_FILE_NAME = "metadata.json"; + + private actionFlowApi: ActionFlowApi; + + constructor(context: Context) { + this.actionFlowApi = new ActionFlowApi(context); + } + + public async exportActionFlows(packageId: string, metadataFilePath: string): Promise { + const exportedActionFlowsData = await this.actionFlowApi.exportRawAssets(packageId); + const tmpZip: AdmZip = new AdmZip(exportedActionFlowsData); + + const zip = new AdmZip(); + tmpZip.getEntries().forEach(entry => { + zip.addFile(entry.entryName, entry.getData()); + }); + + if (metadataFilePath) { + this.attachMetadataFile(metadataFilePath, zip); + } + + const fileName = "action-flows_export_" + uuidv4() + ".zip"; + zip.writeZip(fileName); + logger.info(FileService.fileDownloadedMessage + fileName); + } + + public async analyzeActionFlows(packageId: string, outputToJsonFile: boolean): Promise { + const actionFlowsMetadata = await this.actionFlowApi.analyzeAssets(packageId); + const actionFlowsMetadataString = JSON.stringify(actionFlowsMetadata, null, 4); + + if (outputToJsonFile) { + const metadataFileName = "action-flows_metadata_" + uuidv4() + ".json"; + fileService.writeToFileWithGivenName(actionFlowsMetadataString, metadataFileName); + logger.info(FileService.fileDownloadedMessage + metadataFileName); + } else { + logger.info("Action flows analyze metadata: \n" + actionFlowsMetadataString); + } + } + + public async importActionFlows(packageId: string, filePath: string, dryRun: boolean, outputToJsonFile: boolean): Promise { + const actionFlowsZip = this.createBodyForImport(filePath); + const eventLog = await this.actionFlowApi.importAssets(packageId, actionFlowsZip, dryRun); + const eventLogString = JSON.stringify(eventLog, null, 4); + + if (outputToJsonFile) { + const eventLogFileName = "action-flows_import_event_log_" + uuidv4() + ".json"; + fileService.writeToFileWithGivenName(eventLogString, eventLogFileName); + logger.info(FileService.fileDownloadedMessage + eventLogFileName); + } else { + logger.info("Action flows import event log: \n" + eventLogString); + } + } + + private createBodyForImport(fileName: string): FormData { + fileName = fileName + (fileName.endsWith(".zip") ? "" : ".zip"); + + const formData = new FormData(); + formData.append("file", fs.createReadStream(fileName, { encoding: null }), { filename: fileName }); + + return formData; + } + + private attachMetadataFile(fileName: string, zip: AdmZip): void { + fileName = fileName + (fileName.endsWith(".json") ? "" : ".json"); + const metadata = fileService.readFile(fileName); + + zip.addFile(ActionFlowService.METADATA_FILE_NAME, Buffer.from(metadata)); + } +} diff --git a/src/commands/action-flows/module.ts b/src/commands/action-flows/module.ts index 32075375..24653359 100644 --- a/src/commands/action-flows/module.ts +++ b/src/commands/action-flows/module.ts @@ -4,12 +4,75 @@ import { Configurator, IModule } from "../../core/command/module-handler"; import { Context } from "../../core/command/cli-context"; +import { Command, OptionValues } from "commander"; +import { ActionFlowCommandService } from "./action-flow/action-flow-command.service"; +import { SkillCommandService } from "./skill/skill-command.service"; class Module extends IModule { register(context: Context, configurator: Configurator) { + const analyzeCommand = configurator.command("analyze"); + analyzeCommand.command("action-flows") + .description("Analyze Action Flows dependencies for a certain package") + .option("-p, --profile ", "Profile which you want to use to analyze Action Flows") + .requiredOption("--packageId ", "ID of the package from which you want to export Action Flows") + .option("-o, --outputToJsonFile", "Output the analyze result in a JSON file") + .action(this.analyzeActionFlows); + + const exportCommand = configurator.command("export"); + exportCommand.command("action-flows") + .description("Command to export all Action Flows in a package with their objects and dependencies") + .option("-p, --profile ", "Profile which you want to use to export Action Flows") + .requiredOption("--packageId ", "ID of the package from which you want to export Action Flows") + .option("-f, --file ", "Action flows metadata file (relative path)") + .action(this.exportActionFlows); + + const importCommand = configurator.command("import"); + importCommand.command("action-flows") + .description("Command to import all Action Flows in a package with their objects and dependencies") + .option("-p, --profile ", "Profile which you want to use to import Action Flows") + .requiredOption("--packageId ", "ID of the package to which you want to export Action Flows") + .requiredOption("-f, --file ", "Exported Action Flows file (relative path)") + .requiredOption("-d, --dryRun ", "Execute the import on dry run mode") + .option("-o, --outputToJsonFile", "Output the import result in a JSON file") + .action(this.importActionFlows); + + const pullCommand = configurator.command("pull"); + pullCommand.command("skill") + .description("Command to pull a skill") + .option("-p, --profile ", "Profile which you want to use to pull the skill") + .requiredOption("--projectId ", "Id of the project you want to pull") + .requiredOption("--skillId ", "Id of the skill you want to pull") + .action(this.pullSkill); + + const pushCommand = configurator.command("push"); + pushCommand.command("skill") + .description("Command to push a skill to a project") + .option("-p, --profile ", "Profile which you want to use to push the skill") + .requiredOption("--projectId ", "Id of the project you want to push") + .requiredOption("-f, --file ", "The file you want to push") + .action(this.pushSkill); } + private async analyzeActionFlows(context: Context, command: Command, options: OptionValues): Promise { + await new ActionFlowCommandService(context).analyzeActionFlows(options.packageId, options.outputToJsonFile); + } + + private async exportActionFlows(context: Context, command: Command, options: OptionValues): Promise { + await new ActionFlowCommandService(context).exportActionFlows(options.packageId, options.file); + } + + private async importActionFlows(context: Context, command: Command, options: OptionValues): Promise { + await new ActionFlowCommandService(context).importActionFlows(options.packageId, options.file, options.dryRun, options.outputToJsonFile); + } + + private async pullSkill(context: Context, command: Command, options: OptionValues): Promise { + await new SkillCommandService(context).pullSkill(options.profile, options.projectId, options.skillId); + } + + private async pushSkill(context: Context, command: Command, options: OptionValues): Promise { + await new SkillCommandService(context).pushSkill(options.profile, options.projectId, options.file); + } } export = Module; \ No newline at end of file diff --git a/src/commands/action-flows/skill/skill-command.service.ts b/src/commands/action-flows/skill/skill-command.service.ts new file mode 100644 index 00000000..7a3a4c88 --- /dev/null +++ b/src/commands/action-flows/skill/skill-command.service.ts @@ -0,0 +1,20 @@ +import { ContentService } from "../../../core/http/http-shared/content.service"; +import { Context } from "../../../core/command/cli-context"; +import { SkillManagerFactory } from "./skill.manager-factory"; + +export class SkillCommandService { + private contentService = new ContentService(); + private skillManagerFactory: SkillManagerFactory; + + constructor(context: Context) { + this.skillManagerFactory = new SkillManagerFactory(context); + } + + public async pullSkill(profile: string, projectId: string, skillId: string): Promise { + await this.contentService.pull(this.skillManagerFactory.createManager(projectId, skillId, null)); + } + + public async pushSkill(profile: string, projectId: string, filename: string): Promise { + await this.contentService.push(this.skillManagerFactory.createManager(projectId, null, filename)); + } +} diff --git a/src/commands/action-flows/skill/skill.manager-factory.ts b/src/commands/action-flows/skill/skill.manager-factory.ts new file mode 100644 index 00000000..e3897494 --- /dev/null +++ b/src/commands/action-flows/skill/skill.manager-factory.ts @@ -0,0 +1,32 @@ +import * as fs from "fs"; +import * as path from "path"; +import { Stream } from "stream"; +import { Context } from "../../../core/command/cli-context"; +import { FatalError, logger } from "../../../core/utils/logger"; +import { SkillManager } from "./skill.manager"; + +export class SkillManagerFactory { + + private readonly context: Context; + + constructor(context: Context) { + this.context = context; + } + + public createManager(projectId: string, skillId: string, filename: string): SkillManager { + const skillManager = new SkillManager(this.context); + skillManager.skillId = skillId; + skillManager.projectId = projectId; + if (filename !== null) { + skillManager.content = this.readFile(filename); + } + return skillManager; + } + + private readFile(filename: string): Stream { + if (!fs.existsSync(path.resolve(process.cwd(), filename))) { + logger.error(new FatalError("The provided file does not exit")); + } + return fs.createReadStream(path.resolve(process.cwd(), filename), { encoding: "binary" }); + } +} diff --git a/src/commands/action-flows/skill/skill.manager.ts b/src/commands/action-flows/skill/skill.manager.ts new file mode 100644 index 00000000..5a83ae2e --- /dev/null +++ b/src/commands/action-flows/skill/skill.manager.ts @@ -0,0 +1,59 @@ +import * as FormData from "form-data"; +import { Context } from "../../../core/command/cli-context"; +import { BaseManager } from "../../../core/http/http-shared/base.manager"; +import { ManagerConfig } from "../../../core/http/http-shared/manager-config.interface"; + +export class SkillManager extends BaseManager { + private static BASE_URL = "/action-engine/api/projects"; + private _skillId: string; + private _projectId: string; + private _content: any; + + constructor(context: Context) { + super(context); + } + + public get content(): any { + return this._content; + } + + public set content(value: any) { + this._content = value; + } + public get skillId(): string { + return this._skillId; + } + + public set skillId(value: string) { + this._skillId = value; + } + + public get projectId(): string { + return this._projectId; + } + + public set projectId(value: string) { + this._projectId = value; + } + + public getConfig(): ManagerConfig { + return { + pushUrl: `${SkillManager.BASE_URL}/${this.projectId}/skills/import-file`, + pullUrl: `${SkillManager.BASE_URL}/${this.projectId}/skills/${this.skillId}/export`, + exportFileName: "skill_" + this.skillId + ".json", + onPushSuccessMessage: (skill: any): string => { + return "Skill was pushed successfully. New ID: " + skill.id; + }, + }; + } + + public getBody(): any { + const formData = new FormData(); + formData.append("file", this.content); + return formData; + } + + protected getSerializedFileContent(data: any): string { + return JSON.stringify(data); + } +} diff --git a/tests/commands/action-flows/analyze-action-flows.spec.ts b/tests/commands/action-flows/analyze-action-flows.spec.ts new file mode 100644 index 00000000..784f3e13 --- /dev/null +++ b/tests/commands/action-flows/analyze-action-flows.spec.ts @@ -0,0 +1,70 @@ +import * as path from "path"; +import { mockedAxiosInstance } from "../../utls/http-requests-mock"; +import { loggingTestTransport, mockWriteFileSync } from "../../jest.setup"; +import { FileService } from "../../../src/core/utils/file-service"; +import { ActionFlowCommandService } from "../../../src/commands/action-flows/action-flow/action-flow-command.service"; +import { testContext } from "../../utls/test-context"; + +describe("Analyze action-flows", () => { + + const packageId = "123-456-789"; + const mockAnalyzeResponse = { + "actionFlows": [ + { + "key": "987_asset_key", + "rootNodeKey": "123_root_key_node", + "parentNodeKey": "555_parent_node_key", + "name": "T2T - simple package Automation", + "scenarioId": "321", + "webHookUrl": null, + "version": "10", + "sensorType": null, + "schedule": { + "type": "indefinitely", + "interval": 900, + }, + "teamSpecific": { + "connections": [], + "variables": [], + "celonisApps": [], + "callingOtherAf": [], + "datastructures": [], + }, + }, + ], + "connections": [], + "dataPools": [], + "dataModels": [], + "skills": [], + "analyses": [], + "datastructures": [], + "mappings": [], + "actionFlowsTeamId": "1234", + }; + + it("Should call import API and return non-json response", async () => { + const resp = { data: mockAnalyzeResponse }; + (mockedAxiosInstance.get as jest.Mock).mockResolvedValue(resp); + + await new ActionFlowCommandService(testContext).analyzeActionFlows(packageId, false); + + expect(loggingTestTransport.logMessages.length).toBe(1); + expect(loggingTestTransport.logMessages[0].message).toContain(JSON.stringify(mockAnalyzeResponse, null, 4)); + + expect(mockedAxiosInstance.get).toHaveBeenCalledWith(`https://myTeam.celonis.cloud/ems-automation/api/root/${packageId}/export/assets/analyze`, expect.anything()); + }); + + it("Should call import API and return json response", async () => { + const resp = { data: mockAnalyzeResponse }; + (mockedAxiosInstance.get as jest.Mock).mockResolvedValue(resp); + + await new ActionFlowCommandService(testContext).analyzeActionFlows(packageId, true); + + expect(loggingTestTransport.logMessages.length).toBe(1); + expect(loggingTestTransport.logMessages[0].message).toContain(FileService.fileDownloadedMessage); + const expectedFileName = loggingTestTransport.logMessages[0].message.split(FileService.fileDownloadedMessage)[1]; + + expect(mockWriteFileSync).toHaveBeenCalledWith(path.resolve(process.cwd(), expectedFileName), JSON.stringify(mockAnalyzeResponse, null, 4), { encoding: "utf-8" }); + expect(mockedAxiosInstance.get).toHaveBeenCalledWith(`https://myTeam.celonis.cloud/ems-automation/api/root/${packageId}/export/assets/analyze`, expect.anything()); + }); +}); \ No newline at end of file diff --git a/tests/commands/action-flows/export-action-flows.spec.ts b/tests/commands/action-flows/export-action-flows.spec.ts new file mode 100644 index 00000000..09e9d9cb --- /dev/null +++ b/tests/commands/action-flows/export-action-flows.spec.ts @@ -0,0 +1,181 @@ +import * as AdmZip from "adm-zip"; +import * as fs from "fs"; +import { parse, stringify } from "yaml"; +import { mockAxiosGet } from "../../utls/http-requests-mock"; +import { ActionFlowCommandService } from "../../../src/commands/action-flows/action-flow/action-flow-command.service"; +import { loggingTestTransport, mockWriteSync } from "../../jest.setup"; +import { FileService } from "../../../src/core/utils/file-service"; +import { mockExistsSyncOnce, mockReadFileSync } from "../../utls/fs-mock-utils"; +import { ActionFlowService } from "../../../src/commands/action-flows/action-flow/action-flow.service"; +import { testContext } from "../../utls/test-context"; + +describe("Export action-flows", () => { + + const packageId = "123-456-789"; + const actionFlowFileName = "20240711-scenario-1234.json"; + const actionFlowConfig = { + "name": "T2T - simple package Automation", + "flow": [ + { + "id": 6, + "module": "util:FunctionSleep", + "version": 1, + "parameters": {}, + "metadata": { + "expect": [ + { + "name": "duration", + "type": "uinteger", + "label": "Delay", + "required": true, + "validate": { + "max": 300, + "min": 1, + }, + }, + ], + "restore": {}, + "designer": { + "x": 300, + "y": 0, + }, + }, + "mapper": { + "duration": "1", + }, + }, + ], + "metadata": { + "instant": false, + "version": 1, + "designer": { + "orphans": [], + }, + "scenario": { + "dlq": false, + "dataloss": false, + "maxErrors": 3, + "autoCommit": true, + "roundtrips": 1, + "sequential": false, + "confidential": false, + "freshVariables": false, + "autoCommitTriggerLast": true, + }, + }, + "io": { + "output_spec": [], + "input_spec": [], + }, + }; + + beforeEach(() => { + (fs.openSync as jest.Mock).mockReturnValue(100); + }); + + it("Should call export API and return the zip response", async () => { + const zipExport = new AdmZip(); + zipExport.addFile(actionFlowFileName, Buffer.from(stringify(actionFlowConfig))); + + mockAxiosGet(`https://myTeam.celonis.cloud/ems-automation/api/root/${packageId}/export/assets`, zipExport.toBuffer()); + + await new ActionFlowCommandService(testContext).exportActionFlows(packageId, null); + + expect(loggingTestTransport.logMessages.length).toBe(1); + const expectedZipFileName = loggingTestTransport.logMessages[0].message.split(FileService.fileDownloadedMessage)[1]; + expect(fs.openSync).toHaveBeenCalledWith(expectedZipFileName, expect.anything(), expect.anything()); + expect(mockWriteSync).toHaveBeenCalled(); + + const fileBuffer = mockWriteSync.mock.calls[0][1]; + const receivedZip = new AdmZip(fileBuffer); + + expect(receivedZip.getEntries().length).toBe(1); + const receivedZipEntry = receivedZip.getEntries()[0]; + const receivedActionFlowConfig = parse(receivedZipEntry.getData().toString()); + expect(receivedZipEntry.name).toEqual(actionFlowFileName); + expect(receivedActionFlowConfig).toEqual(actionFlowConfig); + }); + + it("Should call export API and attach the provided file to the zip response", async () => { + const metadata = { + "actionFlows": [ + { + "key": "123_asset_key", + "rootNodeKey": "543_root_key", + "parentNodeKey": "9099_parent_key", + "name": "T2T - simple package Automation", + "scenarioId": "1234", + "webHookUrl": null, + "version": "10", + "sensorType": null, + "schedule": { + "type": "indefinitely", + "interval": 900, + }, + "teamSpecific": { + "connections": [], + "variables": [], + "celonisApps": [], + "callingOtherAf": [], + "datastructures": [], + }, + }, + ], + "connections": [], + "dataPools": [], + "dataModels": [], + "skills": [], + "analyses": [], + "datastructures": [], + "mappings": [], + "actionFlowsTeamId": "555", + }; + + mockExistsSyncOnce(); + mockReadFileSync(stringify(metadata)); + const zipExport = new AdmZip(); + zipExport.addFile(actionFlowFileName, Buffer.from(stringify(actionFlowConfig))); + + mockAxiosGet(`https://myTeam.celonis.cloud/ems-automation/api/root/${packageId}/export/assets`, zipExport.toBuffer()); + await new ActionFlowCommandService(testContext).exportActionFlows(packageId, ActionFlowService.METADATA_FILE_NAME); + + expect(loggingTestTransport.logMessages.length).toBe(1); + const expectedZipFileName = loggingTestTransport.logMessages[0].message.split(FileService.fileDownloadedMessage)[1]; + expect(fs.openSync).toHaveBeenCalledWith(expectedZipFileName, expect.anything(), expect.anything()); + expect(mockWriteSync).toHaveBeenCalled(); + + const fileBuffer = mockWriteSync.mock.calls[0][1]; + const receivedZip = new AdmZip(fileBuffer); + + expect(receivedZip.getEntries().length).toBe(2); + expect(receivedZip.getEntries().filter(entry => entry.name === ActionFlowService.METADATA_FILE_NAME).length).toBe(1); + + const receivedMetadataZipEntry = receivedZip.getEntries().filter(entry => entry.name === ActionFlowService.METADATA_FILE_NAME)[0]; + const receivedActionFlowZipEntry = receivedZip.getEntries().filter(entry => entry.name !== ActionFlowService.METADATA_FILE_NAME)[0]; + const receivedMetadataFile = parse(receivedMetadataZipEntry.getData().toString()); + const receivedActionFlowFile = parse(receivedActionFlowZipEntry.getData().toString()); + + expect(receivedActionFlowZipEntry.name).toEqual(actionFlowFileName); + expect(receivedActionFlowFile).toEqual(actionFlowConfig); + expect(receivedMetadataFile).toEqual(metadata); + }); + + it("Should throw error if metadata files does not exist", async () => { + const zipExport = new AdmZip(); + zipExport.addFile(actionFlowFileName, Buffer.from(stringify(actionFlowConfig))); + + mockAxiosGet(`https://myTeam.celonis.cloud/ems-automation/api/root/${packageId}/export/assets`, zipExport.toBuffer()); + + const error = new Error("mock exit"); + jest.spyOn(process, "exit").mockImplementation(() => { + throw error; + }); + + try { + await new ActionFlowCommandService(testContext).exportActionFlows(packageId, ActionFlowService.METADATA_FILE_NAME); + } catch (e) { + expect(e).toBe(error); + expect(process.exit).toHaveBeenCalledWith(1); + } + }); +}); \ No newline at end of file diff --git a/tests/commands/action-flows/import-action-flows.spec.ts b/tests/commands/action-flows/import-action-flows.spec.ts new file mode 100644 index 00000000..9ff02fb8 --- /dev/null +++ b/tests/commands/action-flows/import-action-flows.spec.ts @@ -0,0 +1,56 @@ +import * as path from "path"; +import * as AdmZip from "adm-zip"; +import { mockedAxiosInstance } from "../../utls/http-requests-mock"; +import { mockCreateReadStream } from "../../utls/fs-mock-utils"; +import { ActionFlowCommandService } from "../../../src/commands/action-flows/action-flow/action-flow-command.service"; +import { loggingTestTransport, mockWriteFileSync } from "../../jest.setup"; +import { FileService } from "../../../src/core/utils/file-service"; +import { testContext } from "../../utls/test-context"; + +describe("Import action-flows", () => { + + const packageId = "123-456-789"; + const mockImportResponse = { + "status": "SUCCESS", + "eventLog": [ + { + "status": "SUCCESS", + "assetType": "SCENARIO", + "assetId": "asset-id-automation", + "eventType": "IMPORT", + "log": "updated action flow with key [asset-id-automation]", + "mapping": null, + }, + ], + }; + + it("Should call import API and return non-json response", async () => { + const resp = { data: mockImportResponse }; + (mockedAxiosInstance.post as jest.Mock).mockResolvedValue(resp); + const zip = new AdmZip(); + mockCreateReadStream(zip.toBuffer()); + + await new ActionFlowCommandService(testContext).importActionFlows(packageId, "tmp", true, false); + + expect(loggingTestTransport.logMessages.length).toBe(1); + expect(loggingTestTransport.logMessages[0].message).toContain(JSON.stringify(mockImportResponse, null, 4)); + + expect(mockedAxiosInstance.post).toHaveBeenCalledWith(`https://myTeam.celonis.cloud/ems-automation/api/root/${packageId}/import/assets`, expect.anything(), expect.anything()); + }); + + it("Should call import API and return json response", async () => { + const resp = { data: mockImportResponse }; + (mockedAxiosInstance.post as jest.Mock).mockResolvedValue(resp); + const zip = new AdmZip(); + mockCreateReadStream(zip.toBuffer()); + + await new ActionFlowCommandService(testContext).importActionFlows(packageId, "tmp", true, true); + + expect(loggingTestTransport.logMessages.length).toBe(1); + expect(loggingTestTransport.logMessages[0].message).toContain(FileService.fileDownloadedMessage); + const expectedFileName = loggingTestTransport.logMessages[0].message.split(FileService.fileDownloadedMessage)[1]; + + expect(mockWriteFileSync).toHaveBeenCalledWith(path.resolve(process.cwd(), expectedFileName), JSON.stringify(mockImportResponse, null, 4), { encoding: "utf-8" }); + expect(mockedAxiosInstance.post).toHaveBeenCalledWith(`https://myTeam.celonis.cloud/ems-automation/api/root/${packageId}/import/assets`, expect.anything(), expect.anything()); + }); +}); \ No newline at end of file From 4950af9ba371f772aadd283691de7bf6371321f9 Mon Sep 17 00:00:00 2001 From: Zgjim Haziri <37958277+ZgjimHaziri@users.noreply.github.com> Date: Fri, 23 May 2025 16:19:59 +0200 Subject: [PATCH 45/45] TA-3751: Migrate Pacman commands (#207) --- .../api/batch-import-export-api.ts | 78 +++ .../configuration-management/api/diff-api.ts | 27 + .../api/variable-assignment-candidates-api.ts | 26 + .../batch-import-export.service.ts | 184 ++++++ .../config-command.service.ts | 58 ++ .../configuration-management/diff.service.ts | 88 +++ .../batch-export-import.constants.ts | 11 + .../interfaces/diff-package.interfaces.ts | 34 ++ .../interfaces/package-export.interfaces.ts | 86 +++ .../variable-assignment-apis.constants.ts | 12 + .../configuration-management/module.ts | 81 ++- .../studio.service.ts | 280 +++++++++ .../variable-command.service.ts | 19 + .../variable.service.ts | 106 ++++ .../config-diff.spec.ts | 179 ++++++ .../config-export.spec.ts | 552 ++++++++++++++++++ .../config-import.spec.ts | 322 ++++++++++ .../config-list-variables.spec.ts | 177 ++++++ .../config-list.spec.ts | 218 +++++++ .../list-assignments.spec.ts | 66 +++ tests/utls/config-utils.ts | 107 ++++ tests/utls/pacman-api.utils.ts | 43 ++ 22 files changed, 2753 insertions(+), 1 deletion(-) create mode 100644 src/commands/configuration-management/api/batch-import-export-api.ts create mode 100644 src/commands/configuration-management/api/diff-api.ts create mode 100644 src/commands/configuration-management/api/variable-assignment-candidates-api.ts create mode 100644 src/commands/configuration-management/batch-import-export.service.ts create mode 100644 src/commands/configuration-management/config-command.service.ts create mode 100644 src/commands/configuration-management/diff.service.ts create mode 100644 src/commands/configuration-management/interfaces/batch-export-import.constants.ts create mode 100644 src/commands/configuration-management/interfaces/diff-package.interfaces.ts create mode 100644 src/commands/configuration-management/interfaces/package-export.interfaces.ts create mode 100644 src/commands/configuration-management/interfaces/variable-assignment-apis.constants.ts create mode 100644 src/commands/configuration-management/studio.service.ts create mode 100644 src/commands/configuration-management/variable-command.service.ts create mode 100644 src/commands/configuration-management/variable.service.ts create mode 100644 tests/commands/configuration-management/config-diff.spec.ts create mode 100644 tests/commands/configuration-management/config-export.spec.ts create mode 100644 tests/commands/configuration-management/config-import.spec.ts create mode 100644 tests/commands/configuration-management/config-list-variables.spec.ts create mode 100644 tests/commands/configuration-management/config-list.spec.ts create mode 100644 tests/commands/configuration-management/list-assignments.spec.ts create mode 100644 tests/utls/config-utils.ts create mode 100644 tests/utls/pacman-api.utils.ts diff --git a/src/commands/configuration-management/api/batch-import-export-api.ts b/src/commands/configuration-management/api/batch-import-export-api.ts new file mode 100644 index 00000000..2c0e4d7e --- /dev/null +++ b/src/commands/configuration-management/api/batch-import-export-api.ts @@ -0,0 +1,78 @@ +import * as FormData from "form-data"; +import { + PackageExportTransport, + PackageKeyAndVersionPair, + PostPackageImportData, VariableManifestTransport, +} from "../interfaces/package-export.interfaces"; +import { FatalError } from "../../../core/utils/logger"; +import { HttpClient } from "../../../core/http/http-client"; +import { Context } from "../../../core/command/cli-context"; + +export class BatchImportExportApi { + + private httpClient: HttpClient; + + constructor(context: Context) { + this.httpClient = context.httpClient; + } + + public async findAllActivePackages(flavors: string[], withDependencies: boolean = false): Promise { + const queryParams = new URLSearchParams(); + + queryParams.set("withDependencies", withDependencies.toString()); + flavors.forEach(flavor => queryParams.append("flavors", flavor)) + + return this.httpClient.get(`/package-manager/api/core/packages/export/list?${queryParams.toString()}`).catch(e => { + throw new FatalError(`Problem getting active packages: ${e}`); + }); + } + + public async findActivePackagesByVariableValue(flavors: string[], variableValue: string, variableType: string): Promise { + const queryParams = new URLSearchParams(); + + queryParams.set("variableValue", variableValue); + if (variableType) { + queryParams.set("variableType", variableType); + } + flavors.forEach(flavor => queryParams.append("flavors", flavor)) + + return this.httpClient.get(`/package-manager/api/core/packages/export/list-by-variable-value?${queryParams.toString()}`).catch(e => { + throw new FatalError(`Problem getting active packages by variable value: ${e}`); + }); + } + + public async findActivePackagesByKeys(packageKeys: string[], withDependencies: boolean = false): Promise { + const queryParams = new URLSearchParams(); + + packageKeys.forEach(key => queryParams.append("packageKeys", key)) + queryParams.set("withDependencies", withDependencies.toString()); + + return this.httpClient.get(`/package-manager/api/core/packages/export/list-by-keys?${queryParams.toString()}`).catch(e => { + throw new FatalError(`Problem getting active packages by keys: ${e}`); + }); + } + + public async exportPackages(packageKeys: string[], withDependencies: boolean = false): Promise { + const queryParams = new URLSearchParams(); + packageKeys.forEach(packageKey => queryParams.append("packageKeys", packageKey)); + queryParams.set("withDependencies", withDependencies.toString()); + + return this.httpClient.getFile(`/package-manager/api/core/packages/export/batch?${queryParams.toString()}`).catch(e => { + throw new FatalError(`Problem exporting packages: ${e}`); + }); + } + + public async importPackages(data: FormData, overwrite: boolean): Promise { + return this.httpClient.postFile( + "/package-manager/api/core/packages/import/batch", + data, + {overwrite} + ); + } + + public async findVariablesWithValuesByPackageKeysAndVersion(packagesByKeyAndVersion: PackageKeyAndVersionPair[]): Promise { + return this.httpClient.post("/package-manager/api/core/packages/export/batch/variables-with-assignments", packagesByKeyAndVersion).catch(e => { + throw new FatalError(`Problem exporting package variables: ${e}`); + }) + } +} diff --git a/src/commands/configuration-management/api/diff-api.ts b/src/commands/configuration-management/api/diff-api.ts new file mode 100644 index 00000000..211757f5 --- /dev/null +++ b/src/commands/configuration-management/api/diff-api.ts @@ -0,0 +1,27 @@ +import * as FormData from "form-data"; +import { HttpClient } from "../../../core/http/http-client"; +import { Context } from "../../../core/command/cli-context"; +import { PackageDiffMetadata, PackageDiffTransport } from "../interfaces/diff-package.interfaces"; + +export class DiffApi { + + private httpClient: HttpClient; + + constructor(context: Context) { + this.httpClient = context.httpClient; + } + + public async diffPackages(data: FormData): Promise { + return this.httpClient.postFile( + "/package-manager/api/core/packages/diff/configuration", + data + ); + } + + public async hasChanges(data: FormData): Promise { + return this.httpClient.postFile( + "/package-manager/api/core/packages/diff/configuration/has-changes", + data + ); + } +} diff --git a/src/commands/configuration-management/api/variable-assignment-candidates-api.ts b/src/commands/configuration-management/api/variable-assignment-candidates-api.ts new file mode 100644 index 00000000..0f44a890 --- /dev/null +++ b/src/commands/configuration-management/api/variable-assignment-candidates-api.ts @@ -0,0 +1,26 @@ +import {URLSearchParams} from "url"; +import { variableAssignmentApis } from "../interfaces/variable-assignment-apis.constants"; +import { HttpClient } from "../../../core/http/http-client"; +import { Context } from "../../../core/command/cli-context"; +import { FatalError } from "../../../core/utils/logger"; + +export class VariableAssignmentCandidatesApi { + + private httpClient: HttpClient; + + constructor(context: Context) { + this.httpClient = context.httpClient; + } + + public async getCandidateAssignments(type: string, params: URLSearchParams): Promise { + if (!variableAssignmentApis[type]) { + throw new FatalError(`Variable type ${type} not supported.`); + } + + const apiUrl: string = variableAssignmentApis[type].url + (params.toString().length ? `?${params.toString()}` : ""); + + return this.httpClient.get(apiUrl).catch(e => { + throw new FatalError(`Problem getting variables assignment values for type ${type}: ${e}`); + }); + } +} diff --git a/src/commands/configuration-management/batch-import-export.service.ts b/src/commands/configuration-management/batch-import-export.service.ts new file mode 100644 index 00000000..2b8b70ba --- /dev/null +++ b/src/commands/configuration-management/batch-import-export.service.ts @@ -0,0 +1,184 @@ +import {v4 as uuidv4} from "uuid"; +import * as FormData from "form-data"; +import {Readable} from "stream"; +import * as AdmZip from "adm-zip"; +import { Context } from "../../core/command/cli-context"; +import { + PackageExportTransport, PackageKeyAndVersionPair, + PackageManifestTransport, + StudioPackageManifest, VariableManifestTransport, +} from "./interfaces/package-export.interfaces"; +import { BatchExportImportConstants } from "./interfaces/batch-export-import.constants"; +import { fileService, FileService } from "../../core/utils/file-service"; +import { logger } from "../../core/utils/logger"; +import { parse, stringify } from "../../core/utils/json"; +import { PackageApi } from "../studio/api/package-api"; +import { BatchImportExportApi } from "./api/batch-import-export-api"; +import { StudioService } from "./studio.service"; + +export class BatchImportExportService { + + private batchImportExportApi: BatchImportExportApi; + + private studioPackageApi: PackageApi; + private studioService: StudioService; + + constructor(context: Context) { + this.batchImportExportApi = new BatchImportExportApi(context); + + this.studioPackageApi = new PackageApi(context); + this.studioService = new StudioService(context); + } + + public async listActivePackages(flavors: string[]): Promise { + const activePackages = await this.batchImportExportApi.findAllActivePackages(flavors); + activePackages.forEach(pkg => { + logger.info(`${pkg.name} - Key: "${pkg.key}"`) + }); + } + + public async findAndExportListOfActivePackages(flavors: string[], packageKeys: string[], withDependencies: boolean): Promise { + let packagesToExport: PackageExportTransport[]; + + if (packageKeys.length) { + packagesToExport = await this.batchImportExportApi.findActivePackagesByKeys(packageKeys, withDependencies); + } else { + packagesToExport = await this.batchImportExportApi.findAllActivePackages(flavors, withDependencies); + } + + packagesToExport = await this.studioService.getExportPackagesWithStudioData(packagesToExport, withDependencies); + + this.exportListOfPackages(packagesToExport); + } + + public async batchExportPackages(packageKeys: string[], withDependencies: boolean = false): Promise { + const exportedPackagesData: Buffer = await this.batchImportExportApi.exportPackages(packageKeys, withDependencies); + const exportedPackagesZip: AdmZip = new AdmZip(exportedPackagesData); + + const manifest: PackageManifestTransport[] = parse( + exportedPackagesZip.getEntry(BatchExportImportConstants.MANIFEST_FILE_NAME).getData().toString() + ); + + const versionsByPackageKey = this.getVersionsByPackageKey(manifest); + + let exportedVariables = await this.getVersionedVariablesForPackagesWithKeys(versionsByPackageKey); + exportedVariables = this.studioService.fixConnectionVariables(exportedVariables); + exportedPackagesZip.addFile(BatchExportImportConstants.VARIABLES_FILE_NAME, Buffer.from(stringify(exportedVariables), "utf8")); + + const studioPackageKeys = manifest.filter(packageManifest => packageManifest.flavor === BatchExportImportConstants.STUDIO) + .map(packageManifest => packageManifest.packageKey); + + const studioData = await this.studioService.getStudioPackageManifests(studioPackageKeys); + exportedPackagesZip.addFile(BatchExportImportConstants.STUDIO_FILE_NAME, Buffer.from(stringify(studioData), "utf8")); + + exportedPackagesZip.getEntries().forEach(entry => { + if (entry.name.endsWith(BatchExportImportConstants.ZIP_EXTENSION)) { + const lastUnderscoreIndex = entry.name.lastIndexOf("_"); + const packageKey = entry.name.substring(0, lastUnderscoreIndex); + + if (studioPackageKeys.includes(packageKey)) { + const updatedPackage = this.studioService.processPackageForExport(entry, exportedVariables); + exportedPackagesZip.updateFile(entry, updatedPackage.toBuffer()); + } + } + }); + + const fileDownloadedMessage = "File downloaded successfully. New filename: "; + const filename = `export_${uuidv4()}.zip`; + exportedPackagesZip.writeZip(filename); + logger.info(fileDownloadedMessage + filename); + } + + public async batchImportPackages(file: string, overwrite: boolean): Promise { + let configs = new AdmZip(file); + const studioManifests = this.parseEntryData(configs, BatchExportImportConstants.STUDIO_FILE_NAME) as StudioPackageManifest[]; + const variablesManifests: VariableManifestTransport[] = this.parseEntryData(configs, BatchExportImportConstants.VARIABLES_FILE_NAME) as VariableManifestTransport[]; + + configs = await this.studioService.mapSpaces(configs, studioManifests); + const existingStudioPackages = await this.studioPackageApi.findAllPackages(); + + const formData = this.buildBodyForImport(configs, variablesManifests); + const postPackageImportData = await this.batchImportExportApi.importPackages(formData, overwrite); + await this.studioService.processImportedPackages(configs, existingStudioPackages, studioManifests); + + const reportFileName = "config_import_report_" + uuidv4() + ".json"; + fileService.writeToFileWithGivenName(JSON.stringify(postPackageImportData), reportFileName); + logger.info("Config import report file: " + reportFileName); + } + + public async findAndExportListOfActivePackagesByVariableValue(flavors: string[], variableValue: string, variableType: string): Promise { + let packagesToExport = await this.batchImportExportApi.findActivePackagesByVariableValue(flavors, variableValue, variableType); + + packagesToExport = await this.studioService.getExportPackagesWithStudioData(packagesToExport, false); + + this.exportListOfPackages(packagesToExport); + } + + public async listActivePackagesByVariableValue(flavors: string[], variableValue: string, variableType: string) : Promise { + const packagesByVariableValue = await this.batchImportExportApi.findActivePackagesByVariableValue(flavors, variableValue, variableType); + packagesByVariableValue.forEach(pkg => { + logger.info(`${pkg.name} - Key: "${pkg.key}"`) + }); + } + + private exportListOfPackages(packages: PackageExportTransport[]): void { + const filename = uuidv4() + ".json"; + fileService.writeToFileWithGivenName(JSON.stringify(packages), filename); + logger.info(FileService.fileDownloadedMessage + filename); + } + + private getVersionsByPackageKey(manifests: PackageManifestTransport[]): Map { + const versionsByPackageKey = new Map(); + manifests.forEach(packageManifest => { + versionsByPackageKey.set(packageManifest.packageKey, Object.keys(packageManifest.dependenciesByVersion)); + }) + + return versionsByPackageKey; + } + + private getVersionedVariablesForPackagesWithKeys(versionsByPackageKey: Map): Promise { + const variableExportRequest: PackageKeyAndVersionPair[] = []; + versionsByPackageKey?.forEach((versions, key) => { + versions?.forEach(version => { + variableExportRequest.push({ + packageKey: key, + version: version, + }) + }) + }); + + return this.batchImportExportApi.findVariablesWithValuesByPackageKeysAndVersion(variableExportRequest) + } + + private buildBodyForImport(configs: AdmZip, variablesManifests: VariableManifestTransport[]): FormData { + const formData = new FormData(); + const readableStream = this.getReadableStream(configs); + + formData.append("file", readableStream, {filename: "configs.zip"}); + + if (variablesManifests) { + formData.append("mappedVariables", JSON.stringify(variablesManifests), { + contentType: "application/json" + }); + } + + return formData; + } + + private getReadableStream(configs: AdmZip): Readable { + return new Readable({ + read(): void { + this.push(configs.toBuffer()); + this.push(null); + }, + }); + } + + private parseEntryData(configs: AdmZip, fileName: string): any { + const entry = configs.getEntry(fileName); + if (entry) { + return (parse(entry.getData().toString())); + } + return null; + } +} diff --git a/src/commands/configuration-management/config-command.service.ts b/src/commands/configuration-management/config-command.service.ts new file mode 100644 index 00000000..b63fe82f --- /dev/null +++ b/src/commands/configuration-management/config-command.service.ts @@ -0,0 +1,58 @@ +import { Context } from "../../core/command/cli-context"; +import { BatchImportExportService } from "./batch-import-export.service"; +import { VariableService } from "./variable.service"; +import { DiffService } from "./diff.service"; + +export class ConfigCommandService { + + private batchImportExportService: BatchImportExportService; + private variableService: VariableService; + private diffService: DiffService; + + constructor(context: Context) { + this.batchImportExportService = new BatchImportExportService(context); + this.variableService = new VariableService(context); + this.diffService = new DiffService(context); + } + + public async listActivePackages(jsonResponse: boolean, flavors: string[], withDependencies: boolean, packageKeys: string[], variableValue: string, variableType: string): Promise { + if (variableValue) { + await this.listPackagesByVariableValue(jsonResponse, flavors, variableValue, variableType); + return; + } + + if (jsonResponse) { + await this.batchImportExportService.findAndExportListOfActivePackages(flavors ?? [], packageKeys ?? [], withDependencies) + } else { + await this.batchImportExportService.listActivePackages(flavors ?? []); + } + } + + public async listVariables(jsonResponse: boolean, keysByVersion: string[], keysByVersionFile: string): Promise { + if (jsonResponse) { + await this.variableService.exportVariables(keysByVersion, keysByVersionFile); + } else { + await this.variableService.listVariables(keysByVersion, keysByVersionFile); + } + } + + public batchExportPackages(packageKeys: string[], withDependencies: boolean = false): Promise { + return this.batchImportExportService.batchExportPackages(packageKeys, withDependencies); + } + + public batchImportPackages(file: string, overwrite: boolean): Promise { + return this.batchImportExportService.batchImportPackages(file, overwrite); + } + + public diffPackages(file: string, hasChanges: boolean, jsonResponse: boolean): Promise { + return this.diffService.diffPackages(file, hasChanges, jsonResponse); + } + + private async listPackagesByVariableValue(jsonResponse: boolean, flavors: string[], variableValue: string, variableType: string): Promise { + if (jsonResponse) { + await this.batchImportExportService.findAndExportListOfActivePackagesByVariableValue(flavors ?? [], variableValue, variableType ) + } else { + await this.batchImportExportService.listActivePackagesByVariableValue(flavors ?? [], variableValue, variableType); + } + } +} diff --git a/src/commands/configuration-management/diff.service.ts b/src/commands/configuration-management/diff.service.ts new file mode 100644 index 00000000..23f676e7 --- /dev/null +++ b/src/commands/configuration-management/diff.service.ts @@ -0,0 +1,88 @@ +import * as AdmZip from "adm-zip"; +import {Readable} from "stream"; +import * as FormData from "form-data"; +import {v4 as uuidv4} from "uuid"; +import { logger } from "../../core/utils/logger"; +import { fileService, FileService } from "../../core/utils/file-service"; +import { Context } from "../../core/command/cli-context"; +import { PackageDiffMetadata, PackageDiffTransport } from "./interfaces/diff-package.interfaces"; +import { DiffApi } from "./api/diff-api"; + +export class DiffService { + + private diffApi: DiffApi; + + constructor(context: Context) { + this.diffApi = new DiffApi(context); + } + + public async diffPackages(file: string, hasChanges: boolean, jsonResponse: boolean): Promise { + if (hasChanges) { + await this.hasChanges(file, jsonResponse); + } else { + await this.diffPackagesAndReturnDiff(file, jsonResponse); + } + } + + private async hasChanges(file: string, jsonResponse: boolean): Promise { + const packages = new AdmZip(file); + const formData = this.buildBodyForDiff(packages); + const returnedHasChangesData = await this.diffApi.hasChanges(formData); + + if (jsonResponse) { + this.exportListOfPackageDiffMetadata(returnedHasChangesData); + } else { + logger.info(this.buildStringResponseForPackageDiffMetadataList(returnedHasChangesData)); + } + } + + private async diffPackagesAndReturnDiff(file: string, jsonResponse: boolean): Promise { + const packages = new AdmZip(file); + const formData = this.buildBodyForDiff(packages); + const returnedHasChangesData = await this.diffApi.diffPackages(formData); + + if (jsonResponse) { + this.exportListOfPackageDiffs(returnedHasChangesData); + } else { + logger.info(this.buildStringResponseForPackageDiffs(returnedHasChangesData)); + } + } + + private buildBodyForDiff(packages: AdmZip): FormData { + const formData = new FormData(); + const readableStream = this.getReadableStream(packages); + + formData.append("file", readableStream, {filename: "packages.zip"}); + + return formData; + } + + private getReadableStream(packages: AdmZip): Readable { + return new Readable({ + read(): void { + this.push(packages.toBuffer()); + this.push(null); + } + }); + } + + private exportListOfPackageDiffs(packageDiffs: PackageDiffTransport[]): void { + const filename = uuidv4() + ".json"; + fileService.writeToFileWithGivenName(JSON.stringify(packageDiffs), filename); + logger.info(FileService.fileDownloadedMessage + filename); + } + + private exportListOfPackageDiffMetadata(packageDiffMetadata: PackageDiffMetadata[]): void { + const filename = uuidv4() + ".json"; + fileService.writeToFileWithGivenName(JSON.stringify(packageDiffMetadata), filename); + logger.info(FileService.fileDownloadedMessage + filename); + } + + private buildStringResponseForPackageDiffs(packageDiffs: PackageDiffTransport[]): string { + return "\n" + JSON.stringify(packageDiffs, null, 2); + } + + private buildStringResponseForPackageDiffMetadataList(packageDiffMetadata: PackageDiffMetadata[]): string { + return "\n" + JSON.stringify(packageDiffMetadata, null, 2); + } +} diff --git a/src/commands/configuration-management/interfaces/batch-export-import.constants.ts b/src/commands/configuration-management/interfaces/batch-export-import.constants.ts new file mode 100644 index 00000000..a0c815ea --- /dev/null +++ b/src/commands/configuration-management/interfaces/batch-export-import.constants.ts @@ -0,0 +1,11 @@ +export enum BatchExportImportConstants { + STUDIO_FILE_NAME = "studio.json", + VARIABLES_FILE_NAME = "variables.json", + MANIFEST_FILE_NAME = "manifest.json", + STUDIO = "STUDIO", + APP_MODE_VIEWER = "VIEWER", + ZIP_EXTENSION = ".zip", + JSON_EXTENSION = ".json", + NODES_FOLDER_NAME = "nodes/", + SCENARIO_NODE = "SCENARIO" +} \ No newline at end of file diff --git a/src/commands/configuration-management/interfaces/diff-package.interfaces.ts b/src/commands/configuration-management/interfaces/diff-package.interfaces.ts new file mode 100644 index 00000000..507242a1 --- /dev/null +++ b/src/commands/configuration-management/interfaces/diff-package.interfaces.ts @@ -0,0 +1,34 @@ +export interface ConfigurationChangeTransport { + op: string; + path: string; + from: string; + value: object; + fromValue: object; +} + +export enum NodeConfigurationChangeType { + ADDED = "ADDED", + DELETED = "DELETED", + CHANGED = "CHANGED", + UNCHANGED = "UNCHANGED", + INVALID = "INVALID" +} + +export interface NodeDiffTransport { + nodeKey: string; + name: string; + type: string; + changeType: NodeConfigurationChangeType; + changes: ConfigurationChangeTransport[]; +} + +export interface PackageDiffTransport { + packageKey: string; + packageChanges: ConfigurationChangeTransport[]; + nodesWithChanges: NodeDiffTransport[]; +} + +export interface PackageDiffMetadata { + packageKey: string; + hasChanges: boolean; +} \ No newline at end of file diff --git a/src/commands/configuration-management/interfaces/package-export.interfaces.ts b/src/commands/configuration-management/interfaces/package-export.interfaces.ts new file mode 100644 index 00000000..ec6d54ab --- /dev/null +++ b/src/commands/configuration-management/interfaces/package-export.interfaces.ts @@ -0,0 +1,86 @@ +import { + StudioComputeNodeDescriptor, + VariableDefinition, + VariablesAssignments, +} from "../../studio/interfaces/package-manager.interfaces"; +import { SpaceTransport } from "../../studio/interfaces/space.interface"; + +export interface DependencyTransport { + key: string; + version: string; +} + +export interface PackageExportTransport { + id: string; + key: string; + name: string; + changeDate: string; + activatedDraftId: string; + workingDraftId: string; + flavor: string; + version: string; + dependencies: DependencyTransport[]; + spaceId?: string; + datamodels?: StudioComputeNodeDescriptor[]; +} + +export interface PackageManifestTransport { + packageKey: string; + flavor: string; + activeVersion: string; + dependenciesByVersion: Map; +} + +export interface VariableExportTransport { + key: string; + value: any; + type: string; + metadata: object; +} + +export interface VariableManifestTransport { + packageKey: string; + version: string; + variables?: VariableExportTransport[]; +} + +export interface PackageKeyAndVersionPair { + packageKey: string; + version: string; +} + +export interface NodeExportTransport { + key: string; + parentNodeKey: string; + name: string; + type: string; + exportSerializationType: string; + configuration: NodeConfiguration; + schemaVersion: number; + + spaceId: string; + + invalidContent?: boolean; + serializedDocument?: Buffer; +} + +export interface NodeConfiguration { + variables?: VariableDefinition[]; + [key: string]: any; +} + +export interface StudioPackageManifest { + packageKey: string; + space: Partial; + runtimeVariableAssignments: VariablesAssignments[]; +} + +export interface PackageVersionImport { + oldVersion: string; + newVersion: string; +} + +export interface PostPackageImportData { + packageKey: string; + importedVersions: PackageVersionImport[]; +} \ No newline at end of file diff --git a/src/commands/configuration-management/interfaces/variable-assignment-apis.constants.ts b/src/commands/configuration-management/interfaces/variable-assignment-apis.constants.ts new file mode 100644 index 00000000..ae930750 --- /dev/null +++ b/src/commands/configuration-management/interfaces/variable-assignment-apis.constants.ts @@ -0,0 +1,12 @@ +export interface VariableAssignmentApi { + url: string; +} + +export const variableAssignmentApis: { [key: string]: VariableAssignmentApi } = { + DATA_MODEL: { + url: "/package-manager/api/compute-pools/pools-with-data-models" + }, + CONNECTION: { + url: "/process-automation-v2/api/connections" + } +}; \ No newline at end of file diff --git a/src/commands/configuration-management/module.ts b/src/commands/configuration-management/module.ts index b08cdd02..be6e253e 100644 --- a/src/commands/configuration-management/module.ts +++ b/src/commands/configuration-management/module.ts @@ -4,12 +4,91 @@ import { Configurator, IModule } from "../../core/command/module-handler"; import { Context } from "../../core/command/cli-context"; +import { Command, OptionValues } from "commander"; +import { ConfigCommandService } from "./config-command.service"; +import { VariableCommandService } from "./variable-command.service"; class Module extends IModule { - register(context: Context, configurator: Configurator) { + public register(context: Context, configurator: Configurator): void { + const configCommand = configurator.command("config"); + configCommand.command("list") + .description("Command to list active packages that can be exported") + .option("-p, --profile ", "Profile which you want to use to list possible variable assignments") + .option("--json", "Return response as json type", "") + .option("--flavors ", "Lists only active packages of the given flavors") + .option("--withDependencies", "Include dependencies", "") + .option("--packageKeys ", "Lists only given package keys") + .option("--variableValue ", "Variable value for filtering packages by.") + .option("--variableType ", "Variable type for filtering packages by.") + .action(this.listActivePackages); + + configCommand.command("export") + .description("Command to export package configs") + .option("-p, --profile ", "Profile which you want to use to export packages") + .requiredOption("--packageKeys ", "Keys of packages to export") + .option("--withDependencies", "Include variables and dependencies", "") + .action(this.batchExportPackages); + + configCommand.command("import") + .description("Command to import package configs") + .option("-p, --profile ", "Profile which you want to use to import packages") + .option("--overwrite", "Flag to allow overwriting of packages") + .requiredOption("-f, --file ", "Exported packages file (relative path)") + .action(this.batchImportPackages); + + configCommand.command("diff") + .description("Command to diff configs of packages") + .option("-p, --profile ", "Profile of the team/realm which you want to use to diff the packages with") + .option("--hasChanges", "Flag to return only the information if the package has changes without the actual changes") + .option("--json", "Return the response as a JSON file") + .requiredOption("-f, --file ", "Exported packages file (relative or absolute path)") + .action(this.diffPackages); + + const variablesCommand = configCommand.command("variables") + .description("Commands related to variable configs"); + + variablesCommand.command("list") + .description("Command to list versioned variables of packages") + .option("-p, --profile ", "Profile which you want to use to list packages") + .option("--json", "Return response as json type", "") + .option("--keysByVersion ", "Mapping of package keys and versions", "") + .option("--keysByVersionFile ", "Package keys by version mappings file path.", "") + .action(this.listVariables); + + const listCommand = configurator.command("list"); + listCommand.command("assignments") + .description("Command to list possible variable assignments for a type") + .option("-p, --profile ", "Profile which you want to use to list possible variable assignments") + .option("--json", "Return response as json type", "") + .requiredOption("--type ", "Type of variable") + .option("--params ", "Variable query params") + .action(this.listAssignments); + } + + private async listActivePackages(context: Context, command: Command, options: OptionValues): Promise { + await new ConfigCommandService(context).listActivePackages(options.json, options.flavors, options.withDependencies, options.packageKeys, options.variableValue, options.variableType); } + private async batchExportPackages(context: Context, command: Command, options: OptionValues): Promise { + await new ConfigCommandService(context).batchExportPackages(options.packageKeys, options.withDependencies); + } + + private async batchImportPackages(context: Context, command: Command, options: OptionValues): Promise { + await new ConfigCommandService(context).batchImportPackages(options.file, options.overwrite); + } + + private async diffPackages(context: Context, command: Command, options: OptionValues): Promise { + await new ConfigCommandService(context).diffPackages(options.file, options.hasChanges, options.json); + } + + private async listVariables(context: Context, command: Command, options: OptionValues): Promise { + await new ConfigCommandService(context).listVariables(options.json, options.keysByVersion, options.keysByVersionFile); + } + + private async listAssignments(context: Context, command: Command, options: OptionValues): Promise { + await new VariableCommandService(context).listAssignments(options.type, options.json, options.params); + } } export = Module; \ No newline at end of file diff --git a/src/commands/configuration-management/studio.service.ts b/src/commands/configuration-management/studio.service.ts new file mode 100644 index 00000000..c19c95bd --- /dev/null +++ b/src/commands/configuration-management/studio.service.ts @@ -0,0 +1,280 @@ +import {IZipEntry} from "adm-zip"; +import * as AdmZip from "adm-zip"; +import { + NodeConfiguration, + NodeExportTransport, + PackageExportTransport, PackageKeyAndVersionPair, + StudioPackageManifest, VariableExportTransport, + VariableManifestTransport, +} from "./interfaces/package-export.interfaces"; +import { + ContentNodeTransport, + PackageManagerVariableType, + PackageWithVariableAssignments, StudioComputeNodeDescriptor, +} from "../studio/interfaces/package-manager.interfaces"; +import { BatchExportImportConstants } from "./interfaces/batch-export-import.constants"; +import { SpaceTransport } from "../studio/interfaces/space.interface"; +import { parse, stringify } from "../../core/utils/json"; +import { Context } from "../../core/command/cli-context"; +import { PackageApi } from "../studio/api/package-api"; +import { DataModelService } from "../studio/service/data-model.service"; +import { NodeApi } from "../studio/api/node-api"; +import { SpaceApi } from "../studio/api/space-api"; +import { StudioVariablesApi } from "../studio/api/studio-variables-api"; +import { SpaceService } from "../studio/service/space.service"; +import { StudioVariableService } from "../studio/service/studio-variable.service"; + +export class StudioService { + + private studioPackageApi: PackageApi; + private studioNodeApi: NodeApi; + private studioSpaceApi: SpaceApi; + private studioVariablesApi: StudioVariablesApi; + + private studioDataModelService: DataModelService; + private studioSpaceService: SpaceService; + private studioVariableService: StudioVariableService; + + constructor(context: Context) { + this.studioPackageApi = new PackageApi(context); + this.studioNodeApi = new NodeApi(context); + this.studioSpaceApi = new SpaceApi(context); + this.studioVariablesApi = new StudioVariablesApi(context); + + this.studioDataModelService = new DataModelService(context); + this.studioSpaceService = new SpaceService(context); + this.studioVariableService = new StudioVariableService(context); + } + + public async getExportPackagesWithStudioData(packagesToExport: PackageExportTransport[], withDependencies: boolean): Promise { + const studioPackagesWithDataModels = await this.studioPackageApi.findAllPackagesWithVariableAssignments(PackageManagerVariableType.DATA_MODEL); + + packagesToExport = this.setSpaceIdForStudioPackages(packagesToExport, studioPackagesWithDataModels); + + if (withDependencies) { + const dataModelDetailsByNode = await this.studioDataModelService.getDataModelDetailsForPackages(studioPackagesWithDataModels); + packagesToExport = this.setDataModelsForStudioPackages(packagesToExport, studioPackagesWithDataModels, dataModelDetailsByNode); + } + + return packagesToExport; + } + + public fixConnectionVariables(variables: VariableManifestTransport[]): VariableManifestTransport[] { + return variables.map(variableManifest => ({ + ...variableManifest, + variables: variableManifest.variables.map(variable => { + if (variable.type !== PackageManagerVariableType.CONNECTION) { + return variable; + } + + return this.fixConnectionVariable(variable); + }) + })); + } + + public async getStudioPackageManifests(studioPackageKeys: string[]): Promise { + return Promise.all(studioPackageKeys.map(async packageKey => { + const node = await this.studioNodeApi.findOneByKeyAndRootNodeKey(packageKey, packageKey); + const nodeSpace: SpaceTransport = await this.studioSpaceApi.findOne(node.spaceId); + const variableAssignments = await this.studioVariablesApi.getRuntimeVariableValues(packageKey, BatchExportImportConstants.APP_MODE_VIEWER); + + return { + packageKey: packageKey, + space: { + name: nodeSpace.name, + iconReference: nodeSpace.iconReference + }, + runtimeVariableAssignments: variableAssignments + } + })); + } + + public processPackageForExport(exportedPackage: IZipEntry, exportedVariables: VariableManifestTransport[]): AdmZip { + const packageZip = new AdmZip(exportedPackage.getData()); + this.deleteScenarioAssets(packageZip); + this.fixConnectionVariablesForRootNodeFiles(packageZip, exportedPackage.name, exportedVariables); + + return packageZip; + } + + public async processImportedPackages(configs: AdmZip, existingStudioPackages: ContentNodeTransport[], studioManifests: StudioPackageManifest[]): Promise { + if(studioManifests == null) { + return; + } + for (const manifest of studioManifests) { + const existingPackage = existingStudioPackages.find(existingPackage => existingPackage.key === manifest.packageKey); + if (existingPackage) { + await this.studioPackageApi.movePackageToSpace(existingPackage.id, manifest.space.id); + } + await this.assignRuntimeVariables(manifest); + } + } + + private setSpaceIdForStudioPackages(packages: PackageExportTransport[], studioPackages: PackageWithVariableAssignments[]): PackageExportTransport[] { + const studioPackageByKey = new Map(); + studioPackages.forEach(pkg => studioPackageByKey.set(pkg.key, pkg)); + + return packages.map(pkg => { + return studioPackageByKey.has(pkg.key) ? { + ...pkg, + spaceId: studioPackageByKey.get(pkg.key).spaceId + } : pkg; + }); + } + + private setDataModelsForStudioPackages(packages: PackageExportTransport[], + studioPackageWithDataModels: PackageWithVariableAssignments[], + dataModelDetailsByNode: Map): PackageExportTransport[] { + const studioPackageByKey = new Map(); + studioPackageWithDataModels.forEach(pkg => studioPackageByKey.set(pkg.key, pkg)); + + return packages.map(pkg => { + return studioPackageByKey.has(pkg.key) ? { + ...pkg, + datamodels: dataModelDetailsByNode.get(pkg.key) + .map(dataModel => ({ + name: dataModel.name, + poolId: dataModel.poolId, + dataModelId: dataModel.dataModelId + })) + } : pkg; + }); + } + + private fixConnectionVariable(variable: VariableExportTransport): VariableExportTransport { + if (!variable.value.appName) { + return variable; + } + + return { + ...variable, + metadata: { + ...variable.metadata, + appName: variable.value.appName + } + } + } + + private deleteScenarioAssets(packageZip: AdmZip): void { + packageZip.getEntries().filter(entry => entry.entryName.startsWith(BatchExportImportConstants.NODES_FOLDER_NAME) && entry.entryName.endsWith(BatchExportImportConstants.JSON_EXTENSION)) + .forEach(entry => { + const node: NodeExportTransport = parse(entry.getData().toString()); + if (node.type === BatchExportImportConstants.SCENARIO_NODE) { + packageZip.deleteFile(entry); + } + }); + } + + private fixConnectionVariablesForRootNodeFiles(packageZip: AdmZip, zipName: string, exportedVariables: VariableManifestTransport[]): void { + const packageKeyAndVersion = this.getPackageKeyAndVersion(zipName); + const connectionVariablesByKey = this.getConnectionVariablesByKeyForPackage(packageKeyAndVersion.packageKey, packageKeyAndVersion.version, exportedVariables); + + if (connectionVariablesByKey.size === 0) { + return; + } + + const packageEntry = packageZip.getEntry("package.json"); + + const exportedNode: NodeExportTransport = parse(packageEntry.getData().toString()); + const nodeContent: NodeConfiguration = exportedNode.configuration; + + nodeContent.variables = nodeContent.variables.map(variable => ({ + ...variable, + metadata: variable.type === PackageManagerVariableType.CONNECTION ? + connectionVariablesByKey.get(variable.key).metadata : variable.metadata + })); + + packageZip.updateFile(packageEntry, Buffer.from(stringify(exportedNode))); + } + + private getPackageKeyAndVersion(zipName: string): PackageKeyAndVersionPair { + const lastUnderscoreIndex = zipName.lastIndexOf("_"); + const packageKey = zipName.replace(BatchExportImportConstants.ZIP_EXTENSION, "").substring(0, lastUnderscoreIndex); + const packageVersion = zipName.replace(BatchExportImportConstants.ZIP_EXTENSION, "").substring(lastUnderscoreIndex + 1); + + return { + packageKey: packageKey, + version: packageVersion + } + } + + private getConnectionVariablesByKeyForPackage(packageKey: string, packageVersion: string, variables: VariableManifestTransport[]): Map { + const variablesByKey = new Map(); + const packageVariables = variables.find(exportedVariable => exportedVariable.packageKey === packageKey && exportedVariable.version === packageVersion); + + if (packageVariables && packageVariables.variables.length) { + packageVariables.variables.filter(variable => variable.type === PackageManagerVariableType.CONNECTION) + .forEach(variable => variablesByKey.set(variable.key, variable)); + } + + return variablesByKey; + } + + public async mapSpaces(exportedFiles: AdmZip, studioManifests: StudioPackageManifest[]): Promise { + if (studioManifests == null) { + return exportedFiles; + } + for (const file of exportedFiles.getEntries()) { + if(file.name.endsWith(BatchExportImportConstants.ZIP_EXTENSION)) { + const packageKey = this.getPackageKeyAndVersion(file.name).packageKey; + + if (this.isStudioPackage(studioManifests, packageKey)) { + const studioManifest = studioManifests.find(manifest => manifest.packageKey === packageKey); + + if (studioManifest) { + const spaceId = await this.findDesiredSpaceIdForPackage(studioManifest); + studioManifest.space.id = spaceId; + + const packageZip = new AdmZip(file.getData()); + packageZip.getEntries().forEach(nodeFile => { + if (nodeFile.entryName.endsWith(BatchExportImportConstants.JSON_EXTENSION)) { + const updatedNodeFile = this.updateSpaceIdForNode(nodeFile.getData().toString(), spaceId); + packageZip.updateFile(nodeFile, Buffer.from(updatedNodeFile)); + } + }); + exportedFiles.updateFile(file, packageZip.toBuffer()); + } + } + } + } + return exportedFiles; + } + + private isStudioPackage(studioManifests: StudioPackageManifest[], packageKey: string): boolean { + return studioManifests.some(manifest => manifest.packageKey === packageKey); + } + + private async findDesiredSpaceIdForPackage(studioPackageManifest: StudioPackageManifest): Promise { + const allSpaces = await this.studioSpaceService.refreshAndGetAllSpaces(); + + if (studioPackageManifest.space.id) { + const targetSpace = allSpaces.find(space => space.id === studioPackageManifest.space.id); + if (!targetSpace) { + throw Error("Provided space ID does not exist."); + } + return targetSpace.id; + } + + const targetSpaceByName = allSpaces.find(space => space.name === studioPackageManifest.space.name); + if (targetSpaceByName) { + return targetSpaceByName.id; + } + + const spaceTransport = await this.studioSpaceService.createSpace(studioPackageManifest.space.name, studioPackageManifest.space.iconReference); + return spaceTransport.id; + } + + private async assignRuntimeVariables(manifest: StudioPackageManifest): Promise { + if (manifest.runtimeVariableAssignments.length) { + await this.studioVariableService.assignVariableValues(manifest.packageKey, manifest.runtimeVariableAssignments); + } + } + + private updateSpaceIdForNode(nodeContent: string, spaceId: string): string { + const exportedNode: NodeExportTransport = parse(nodeContent); + const oldSpaceId = exportedNode.spaceId; + + nodeContent = nodeContent.replace(new RegExp(oldSpaceId, "g"), spaceId); + return nodeContent; + } +} diff --git a/src/commands/configuration-management/variable-command.service.ts b/src/commands/configuration-management/variable-command.service.ts new file mode 100644 index 00000000..dbf699dd --- /dev/null +++ b/src/commands/configuration-management/variable-command.service.ts @@ -0,0 +1,19 @@ +import { VariableService } from "./variable.service"; +import { Context } from "../../core/command/cli-context"; + +export class VariableCommandService { + + private variableService: VariableService; + + constructor(context: Context) { + this.variableService = new VariableService(context); + } + + public async listAssignments(variableType: string, jsonResponse: boolean, params: string): Promise { + if (jsonResponse) { + await this.variableService.findAndExportCandidateAssignments(variableType, params); + } else { + await this.variableService.listCandidateAssignments(variableType, params); + } + } +} diff --git a/src/commands/configuration-management/variable.service.ts b/src/commands/configuration-management/variable.service.ts new file mode 100644 index 00000000..77c8b85a --- /dev/null +++ b/src/commands/configuration-management/variable.service.ts @@ -0,0 +1,106 @@ +import { v4 as uuidv4 } from "uuid"; +import { Context } from "../../core/command/cli-context"; +import { FatalError, logger } from "../../core/utils/logger"; +import { StudioService } from "./studio.service"; +import { FileService, fileService } from "../../core/utils/file-service"; +import { PackageKeyAndVersionPair, VariableManifestTransport } from "./interfaces/package-export.interfaces"; +import { BatchImportExportApi } from "./api/batch-import-export-api"; +import { URLSearchParams } from "url"; +import { VariableAssignmentCandidatesApi } from "./api/variable-assignment-candidates-api"; + +export class VariableService { + + private batchImportExportApi: BatchImportExportApi; + private variableAssignmentCandidatesApi: VariableAssignmentCandidatesApi; + private studioService: StudioService; + + constructor(context: Context) { + this.batchImportExportApi = new BatchImportExportApi(context); + this.variableAssignmentCandidatesApi = new VariableAssignmentCandidatesApi(context); + this.studioService = new StudioService(context); + } + + public async listVariables(keysByVersion: string[], keysByVersionFile: string): Promise { + const variableManifests = await this.getVersionedVariablesByKeyVersionPairs(keysByVersion, keysByVersionFile); + + variableManifests.forEach(variableManifest => { + logger.info(JSON.stringify(variableManifest)); + }); + } + + public async listCandidateAssignments(type: string, params: string): Promise { + const parsedParams = this.parseParams(params); + const assignments = await this.variableAssignmentCandidatesApi.getCandidateAssignments(type, parsedParams); + + assignments.forEach(assignment => { + logger.info(JSON.stringify(assignment)); + }); + } + + public async findAndExportCandidateAssignments(type: string, params: string): Promise { + const parsedParams = this.parseParams(params); + const assignments = await this.variableAssignmentCandidatesApi.getCandidateAssignments(type, parsedParams); + + this.exportToJson(assignments) + } + + public async exportVariables(keysByVersion: string[], keysByVersionFile: string): Promise { + const variableManifests = await this.getVersionedVariablesByKeyVersionPairs(keysByVersion, keysByVersionFile); + + this.exportToJson(variableManifests); + } + + private async getVersionedVariablesByKeyVersionPairs(keysByVersion: string[], keysByVersionFile: string): Promise { + const variablesExportRequest: PackageKeyAndVersionPair[] = await this.buildKeyVersionPairs(keysByVersion, keysByVersionFile); + + const variableManifests = await this.batchImportExportApi.findVariablesWithValuesByPackageKeysAndVersion(variablesExportRequest); + return this.studioService.fixConnectionVariables(variableManifests); + } + + private async buildKeyVersionPairs(keysByVersion: string[], keysByVersionFile: string): Promise { + let variablesExportRequest: PackageKeyAndVersionPair[] = []; + + if (keysByVersion.length !== 0) { + variablesExportRequest = this.buildKeyAndVersionPairsFromArrayInput(keysByVersion); + } else if (keysByVersion.length === 0 && keysByVersionFile !== "") { + variablesExportRequest = await fileService.readFileToJson(keysByVersionFile); + } else { + throw new FatalError("Please provide keysByVersion mappings or file path!"); + } + + return variablesExportRequest; + } + + private buildKeyAndVersionPairsFromArrayInput(keysByVersion: string[]): PackageKeyAndVersionPair[] { + return keysByVersion.map(keyAndVersion => { + const keyAndVersionSplit = keyAndVersion.split(":"); + return { + packageKey: keyAndVersionSplit[0], + version: keyAndVersionSplit[1] + }; + }); + } + + private exportToJson(data: any): void { + const filename = uuidv4() + ".json"; + fileService.writeToFileWithGivenName(JSON.stringify(data), filename); + logger.info(FileService.fileDownloadedMessage + filename); + } + + private parseParams(params?: string): URLSearchParams { + const queryParams = new URLSearchParams(); + + if (params) { + try { + params.split(",").forEach((param: string) => { + const paramKeyValuePair: string[] = param.split("="); + queryParams.set(paramKeyValuePair[0], paramKeyValuePair[1]); + }) + } catch (e) { + throw new FatalError(`Problem parsing query params: ${e}`); + } + } + + return queryParams; + } +} diff --git a/tests/commands/configuration-management/config-diff.spec.ts b/tests/commands/configuration-management/config-diff.spec.ts new file mode 100644 index 00000000..9c93cd15 --- /dev/null +++ b/tests/commands/configuration-management/config-diff.spec.ts @@ -0,0 +1,179 @@ +import * as path from "path"; +import { mockCreateReadStream, mockExistsSync, mockReadFileSync } from "../../utls/fs-mock-utils"; +import { + PackageManifestTransport +} from "../../../src/commands/configuration-management/interfaces/package-export.interfaces"; +import { + NodeConfigurationChangeType, + PackageDiffMetadata, + PackageDiffTransport, +} from "../../../src/commands/configuration-management/interfaces/diff-package.interfaces"; +import { mockAxiosPost } from "../../utls/http-requests-mock"; +import { ConfigCommandService } from "../../../src/commands/configuration-management/config-command.service"; +import { testContext } from "../../utls/test-context"; +import { loggingTestTransport, mockWriteFileSync } from "../../jest.setup"; +import { FileService } from "../../../src/core/utils/file-service"; +import { ConfigUtils } from "../../utls/config-utils"; + +describe("Config diff", () => { + + beforeEach(() => { + mockExistsSync(); + }); + + it("Should show on terminal if packages have changes with hasChanges set to true and jsonResponse false", async () => { + const manifest: PackageManifestTransport[] = []; + manifest.push(ConfigUtils.buildManifestForKeyAndFlavor("package-key", "STUDIO")); + + const firstPackageNode = ConfigUtils.buildPackageNode("package-key", {metadata: {description: "test"}, variables: [], dependencies: []}); + const firstChildNode = ConfigUtils.buildChildNode("key-1", "package-key", "TEST"); + const firstPackageZip = ConfigUtils.buildExportPackageZip(firstPackageNode, [firstChildNode], "1.0.0"); + const exportedPackagesZip = ConfigUtils.buildBatchExportZip(manifest, [firstPackageZip]); + + mockReadFileSync(exportedPackagesZip.toBuffer()); + mockCreateReadStream(exportedPackagesZip.toBuffer()); + + const diffResponse: PackageDiffMetadata[] = [{ + packageKey: "package-key", + hasChanges: true + }]; + + mockAxiosPost("https://myTeam.celonis.cloud/package-manager/api/core/packages/diff/configuration/has-changes", diffResponse); + + await new ConfigCommandService(testContext).diffPackages("./packages.zip", true, false); + + expect(loggingTestTransport.logMessages.length).toBe(1); + expect(loggingTestTransport.logMessages[0].message).toContain( + JSON.stringify(diffResponse, null, 2) + ); + }); + + it("Should show diff on terminal with hasChanges set to false and jsonResponse false", async () => { + const manifest: PackageManifestTransport[] = []; + manifest.push(ConfigUtils.buildManifestForKeyAndFlavor("package-key", "STUDIO")); + + const firstPackageNode = ConfigUtils.buildPackageNode("package-key", {metadata: {description: "test"}, variables: [], dependencies: []}); + const firstChildNode = ConfigUtils.buildChildNode("key-1", "package-key", "TEST"); + const firstPackageZip = ConfigUtils.buildExportPackageZip(firstPackageNode, [firstChildNode], "1.0.0"); + const exportedPackagesZip = ConfigUtils.buildBatchExportZip(manifest, [firstPackageZip]); + + mockReadFileSync(exportedPackagesZip.toBuffer()); + mockCreateReadStream(exportedPackagesZip.toBuffer()); + + const diffResponse: PackageDiffTransport[] = [{ + packageKey: "package-key", + packageChanges: [ + { + op: "add", + path: "/test", + from: "bbbb", + value: JSON.parse("123"), + fromValue: null + }], + nodesWithChanges: [{ + nodeKey: firstChildNode.key, + name: firstChildNode.name, + type: firstChildNode.type, + changeType: NodeConfigurationChangeType.ADDED, + changes: [{ + op: "add", + path: "/test", + from: "bbb", + value: JSON.parse("234"), + fromValue: null + }] + }] + }]; + + mockAxiosPost("https://myTeam.celonis.cloud/package-manager/api/core/packages/diff/configuration", diffResponse); + + await new ConfigCommandService(testContext).diffPackages("./packages.zip", false, false); + + expect(loggingTestTransport.logMessages.length).toBe(1); + expect(loggingTestTransport.logMessages[0].message).toContain( + JSON.stringify(diffResponse, null, 2) + ); + }); + + it("Should generate a json file with diff info when hasChanges is set to false and jsonResponse is set to true", async () => { + const manifest: PackageManifestTransport[] = []; + manifest.push(ConfigUtils.buildManifestForKeyAndFlavor("package-key", "STUDIO")); + + const firstPackageNode = ConfigUtils.buildPackageNode("package-key", {metadata: {description: "test"}, variables: [], dependencies: []}); + const firstChildNode = ConfigUtils.buildChildNode("key-1", "package-key", "TEST"); + const firstPackageZip = ConfigUtils.buildExportPackageZip(firstPackageNode, [firstChildNode], "1.0.0"); + const exportedPackagesZip = ConfigUtils.buildBatchExportZip(manifest, [firstPackageZip]); + + mockReadFileSync(exportedPackagesZip.toBuffer()); + mockCreateReadStream(exportedPackagesZip.toBuffer()); + + const diffResponse: PackageDiffTransport[] = [{ + packageKey: "package-key", + packageChanges: [ + { + op: "add", + path: "/test", + from: "bbbb", + value: JSON.parse("123"), + fromValue: null + }], + nodesWithChanges: [{ + nodeKey: firstChildNode.key, + name: firstChildNode.name, + type: firstChildNode.type, + changeType: NodeConfigurationChangeType.ADDED, + changes: [{ + op: "add", + path: "/test", + from: "bbb", + value: JSON.parse("234"), + fromValue: null + }] + }] + }]; + + mockAxiosPost("https://myTeam.celonis.cloud/package-manager/api/core/packages/diff/configuration", diffResponse); + + await new ConfigCommandService(testContext).diffPackages("./packages.zip", false, true); + + const expectedFileName = loggingTestTransport.logMessages[0].message.split(FileService.fileDownloadedMessage)[1]; + + expect(mockWriteFileSync).toHaveBeenCalledWith(path.resolve(process.cwd(), expectedFileName), expect.any(String), {encoding: "utf-8"}); + const exportedPackageDiffTransport = JSON.parse(mockWriteFileSync.mock.calls[0][1]) as PackageDiffTransport[]; + expect(exportedPackageDiffTransport.length).toBe(1); + + const exportedFirstPackageDiffTransport = exportedPackageDiffTransport.filter(diffTransport => diffTransport.packageKey === firstPackageNode.key); + expect(exportedFirstPackageDiffTransport).toEqual(diffResponse); + }); + + it("Should generate a json file with info whether packages have changes when hasChanges is set to true and jsonResponse is set to true", async () => { + const manifest: PackageManifestTransport[] = []; + manifest.push(ConfigUtils.buildManifestForKeyAndFlavor("package-key", "STUDIO")); + + const firstPackageNode = ConfigUtils.buildPackageNode("package-key", {metadata: {description: "test"}, variables: [], dependencies: []}); + const firstChildNode = ConfigUtils.buildChildNode("key-1", "package-key", "TEST"); + const firstPackageZip = ConfigUtils.buildExportPackageZip(firstPackageNode, [firstChildNode], "1.0.0"); + const exportedPackagesZip = ConfigUtils.buildBatchExportZip(manifest, [firstPackageZip]); + + mockReadFileSync(exportedPackagesZip.toBuffer()); + mockCreateReadStream(exportedPackagesZip.toBuffer()); + + const diffResponse: PackageDiffMetadata[] = [{ + packageKey: "package-key", + hasChanges: true + }]; + + mockAxiosPost("https://myTeam.celonis.cloud/package-manager/api/core/packages/diff/configuration/has-changes", diffResponse); + + await new ConfigCommandService(testContext).diffPackages("./packages.zip", true, true); + + const expectedFileName = loggingTestTransport.logMessages[0].message.split(FileService.fileDownloadedMessage)[1]; + + expect(mockWriteFileSync).toHaveBeenCalledWith(path.resolve(process.cwd(), expectedFileName), expect.any(String), {encoding: "utf-8"}); + const exportedPackageDiffTransport = JSON.parse(mockWriteFileSync.mock.calls[0][1]) as PackageDiffTransport[]; + expect(exportedPackageDiffTransport.length).toBe(1); + + const exportedFirstPackageDiffTransport = exportedPackageDiffTransport.filter(diffTransport => diffTransport.packageKey === firstPackageNode.key); + expect(exportedFirstPackageDiffTransport).toEqual(diffResponse); + }); +}); \ No newline at end of file diff --git a/tests/commands/configuration-management/config-export.spec.ts b/tests/commands/configuration-management/config-export.spec.ts new file mode 100644 index 00000000..a93c4632 --- /dev/null +++ b/tests/commands/configuration-management/config-export.spec.ts @@ -0,0 +1,552 @@ +import * as fs from "fs"; +import AdmZip = require("adm-zip"); +import { mockAxiosGet, mockAxiosPost, mockedPostRequestBodyByUrl } from "../../utls/http-requests-mock"; +import { + BatchExportImportConstants +} from "../../../src/commands/configuration-management/interfaces/batch-export-import.constants"; +import { + DependencyTransport, NodeConfiguration, NodeExportTransport, + PackageManifestTransport, StudioPackageManifest, VariableManifestTransport, +} from "../../../src/commands/configuration-management/interfaces/package-export.interfaces"; +import { + PackageManagerVariableType, VariableDefinition, + VariablesAssignments, +} from "../../../src/commands/studio/interfaces/package-manager.interfaces"; +import { loggingTestTransport, mockWriteSync } from "../../jest.setup"; +import { FileService } from "../../../src/core/utils/file-service"; +import { parse, stringify } from "../../../src/core/utils/json"; +import { ConfigCommandService } from "../../../src/commands/configuration-management/config-command.service"; +import { testContext } from "../../utls/test-context"; +import { ConfigUtils } from "../../utls/config-utils"; +import { PacmanApiUtils } from "../../utls/pacman-api.utils"; + +describe("Config export", () => { + + const firstSpace = PacmanApiUtils.buildSpaceTransport("space-1", "First space", "Icon1"); + const secondSpace = PacmanApiUtils.buildSpaceTransport("space-2", "Second space", "Icon2"); + + beforeEach(() => { + (fs.openSync as jest.Mock).mockReturnValue(100); + mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/spaces/space-1", {...firstSpace}); + mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/spaces/space-2", {...secondSpace}); + }); + + it("Should export studio file for studio packageKeys", async () => { + const manifest: PackageManifestTransport[] = []; + manifest.push(ConfigUtils.buildManifestForKeyAndFlavor("key-1", BatchExportImportConstants.STUDIO)); + manifest.push(ConfigUtils.buildManifestForKeyAndFlavor("key-2", BatchExportImportConstants.STUDIO)); + manifest.push(ConfigUtils.buildManifestForKeyAndFlavor("key-3", "TEST")); + const exportedPackagesZip = ConfigUtils.buildBatchExportZip(manifest, []); + + const firstStudioPackage = PacmanApiUtils.buildContentNodeTransport("key-1", "space-1"); + const firstPackageRuntimeVariable: VariablesAssignments = { + key: "varKey", + type: PackageManagerVariableType.PLAIN_TEXT, + value: "default-value" as unknown as object + }; + + const secondStudioPackage = PacmanApiUtils.buildContentNodeTransport("key-2", "space-2"); + + mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/core/packages/export/batch?packageKeys=key-1&packageKeys=key-2&packageKeys=key-3&withDependencies=true", exportedPackagesZip.toBuffer()); + mockAxiosPost("https://myTeam.celonis.cloud/package-manager/api/core/packages/export/batch/variables-with-assignments", []); + mockAxiosGet(`https://myTeam.celonis.cloud/package-manager/api/nodes/${firstStudioPackage.key}/${firstStudioPackage.key}`, firstStudioPackage); + mockAxiosGet(`https://myTeam.celonis.cloud/package-manager/api/nodes/${secondStudioPackage.key}/${secondStudioPackage.key}`, secondStudioPackage); + mockAxiosGet(`https://myTeam.celonis.cloud/package-manager/api/nodes/by-package-key/${firstStudioPackage.key}/variables/runtime-values?appMode=VIEWER`, [firstPackageRuntimeVariable]); + mockAxiosGet(`https://myTeam.celonis.cloud/package-manager/api/nodes/by-package-key/${secondStudioPackage.key}/variables/runtime-values?appMode=VIEWER`, []); + + await new ConfigCommandService(testContext).batchExportPackages(["key-1", "key-2", "key-3"], true); + + const expectedFileName = loggingTestTransport.logMessages[0].message.split(FileService.fileDownloadedMessage)[1]; + expect(fs.openSync).toHaveBeenCalledWith(expectedFileName, expect.anything(), expect.anything()); + expect(mockWriteSync).toHaveBeenCalled(); + + const fileBuffer = mockWriteSync.mock.calls[0][1]; + const actualZip = new AdmZip(fileBuffer); + + const studioManifest: StudioPackageManifest[] = parse(actualZip.getEntry(BatchExportImportConstants.STUDIO_FILE_NAME).getData().toString()); + expect(studioManifest).toHaveLength(2); + expect(studioManifest).toContainEqual({ + packageKey: firstStudioPackage.key, + space: { + name: firstSpace.name, + iconReference: firstSpace.iconReference + }, + runtimeVariableAssignments: [firstPackageRuntimeVariable] + }); + expect(studioManifest).toContainEqual({ + packageKey: secondStudioPackage.key, + space: { + name: secondSpace.name, + iconReference: secondSpace.iconReference + }, + runtimeVariableAssignments: [] + }); + }) + + it("Should export variables file with connection variables fixed", async () => { + const firstPackageDependencies = new Map(); + firstPackageDependencies.set("1.0.0", []); + + const secondPackageDependencies = new Map(); + secondPackageDependencies.set("1.0.0", []); + secondPackageDependencies.set("1.1.1", []); + + const manifest: PackageManifestTransport[] = []; + manifest.push(ConfigUtils.buildManifestForKeyAndFlavor("key-1", BatchExportImportConstants.STUDIO, firstPackageDependencies)); + manifest.push(ConfigUtils.buildManifestForKeyAndFlavor("key-2", BatchExportImportConstants.STUDIO, secondPackageDependencies)); + + const firstPackageVariableDefinition: VariableDefinition[] = [ + { + key: "key1-var", + type: PackageManagerVariableType.DATA_MODEL, + runtime: false + }, + { + key: "key-1-connection", + type: PackageManagerVariableType.CONNECTION, + runtime: false + } + ]; + + const firstPackageNode = ConfigUtils.buildPackageNode("key-1", {variables: firstPackageVariableDefinition}); + const firstPackageZip = ConfigUtils.buildExportPackageZip(firstPackageNode, [], "1.0.0"); + + const secondPackageVariableDefinition: VariableDefinition[] = [ + { + key: "key2-var", + type: PackageManagerVariableType.DATA_MODEL, + runtime: false + }, + { + key: "key-2-connection", + type: PackageManagerVariableType.CONNECTION, + runtime: false + } + ]; + + const secondPackageNode = ConfigUtils.buildPackageNode("key-2", {variables: secondPackageVariableDefinition}); + const secondPackageZip = ConfigUtils.buildExportPackageZip(secondPackageNode, [], "1.0.0"); + + const exportedPackagesZip = ConfigUtils.buildBatchExportZip(manifest, [firstPackageZip, secondPackageZip]); + + const exportedVariables: VariableManifestTransport[] = [ + { + packageKey: "key-1", + version: "1.0.0", + variables: [ + { + key: "key1-var", + type: PackageManagerVariableType.DATA_MODEL, + value: "dm-id" as unknown as object, + metadata: {} + }, + { + key: "key-1-connection", + type: PackageManagerVariableType.CONNECTION, + value: { + appName: "celonis", + connectionId: "connection-id" + } as unknown as object, + metadata: null + } + ] + }, + { + packageKey: "key-2", + version: "1.0.0", + variables: [ + { + key: "key2-var", + type: PackageManagerVariableType.DATA_MODEL, + value: "dm-id" as unknown as object, + metadata: {} + }, + { + key: "key-2-connection", + type: PackageManagerVariableType.CONNECTION, + value: "connection-id", + metadata: { + appName: "nameOfApp" + } + } + ] + }, + ]; + + mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/core/packages/export/batch?packageKeys=key-1&packageKeys=key-2&withDependencies=true", exportedPackagesZip.toBuffer()); + mockAxiosPost("https://myTeam.celonis.cloud/package-manager/api/core/packages/export/batch/variables-with-assignments", [...exportedVariables]); + mockAxiosGet(`https://myTeam.celonis.cloud/package-manager/api/nodes/${firstPackageNode.key}/${firstPackageNode.key}`, {...firstPackageNode, spaceId: "space-1"}); + mockAxiosGet(`https://myTeam.celonis.cloud/package-manager/api/nodes/${secondPackageNode.key}/${secondPackageNode.key}`, {...secondPackageNode, spaceId: "space-2"}); + mockAxiosGet(`https://myTeam.celonis.cloud/package-manager/api/nodes/by-package-key/${firstPackageNode.key}/variables/runtime-values?appMode=VIEWER`, []); + mockAxiosGet(`https://myTeam.celonis.cloud/package-manager/api/nodes/by-package-key/${secondPackageNode.key}/variables/runtime-values?appMode=VIEWER`, []); + + await new ConfigCommandService(testContext).batchExportPackages(["key-1", "key-2"], true); + + const expectedFileName = loggingTestTransport.logMessages[0].message.split(FileService.fileDownloadedMessage)[1]; + expect(fs.openSync).toHaveBeenCalledWith(expectedFileName, expect.anything(), expect.anything()); + expect(mockWriteSync).toHaveBeenCalled(); + + const fileBuffer = mockWriteSync.mock.calls[0][1]; + const actualZip = new AdmZip(fileBuffer); + + const exportedVariablesFileContent: VariableManifestTransport[] = parse(actualZip.getEntry(BatchExportImportConstants.VARIABLES_FILE_NAME).getData().toString()); + expect(exportedVariablesFileContent).toHaveLength(2); + expect(exportedVariablesFileContent).toContainEqual({ + packageKey: "key-1", + version: "1.0.0", + variables: [ + { + key: "key1-var", + type: PackageManagerVariableType.DATA_MODEL, + value: "dm-id", + metadata: {} + }, + { + key: "key-1-connection", + type: PackageManagerVariableType.CONNECTION, + value: { + appName: "celonis", + connectionId: "connection-id" + }, + metadata: { + appName: "celonis" + } + } + ] + }); + expect(exportedVariablesFileContent).toContainEqual({ + packageKey: "key-2", + version: "1.0.0", + variables: [ + { + key: "key2-var", + type: PackageManagerVariableType.DATA_MODEL, + value: "dm-id", + metadata: {} + }, + { + key: "key-2-connection", + type: PackageManagerVariableType.CONNECTION, + value: "connection-id", + metadata: { + appName: "nameOfApp" + } + } + ] + }); + + const variableExportRequest = parse(mockedPostRequestBodyByUrl.get("https://myTeam.celonis.cloud/package-manager/api/core/packages/export/batch/variables-with-assignments")); + expect(variableExportRequest).toBeTruthy(); + expect(variableExportRequest).toHaveLength(3); + expect(variableExportRequest).toContainEqual({ + packageKey: "key-1", + version: "1.0.0" + }); + expect(variableExportRequest).toContainEqual({ + packageKey: "key-2", + version: "1.0.0" + }); + expect(variableExportRequest).toContainEqual({ + packageKey: "key-2", + version: "1.1.1" + }); + }) + + it("Should remove SCENARIO asset files of packages", async () => { + const manifest: PackageManifestTransport[] = []; + manifest.push(ConfigUtils.buildManifestForKeyAndFlavor("key-1", BatchExportImportConstants.STUDIO)); + manifest.push(ConfigUtils.buildManifestForKeyAndFlavor("key-2", BatchExportImportConstants.STUDIO)); + + const firstPackageNode = ConfigUtils.buildPackageNode("key-1", {}); + const firstPackageScenarioChild = ConfigUtils.buildChildNode("child-1-scenario", firstPackageNode.key, "SCENARIO"); + const firstPackageTestChild = ConfigUtils.buildChildNode("child-2", firstPackageNode.key, "TEST"); + const firstPackageZip = ConfigUtils.buildExportPackageZip(firstPackageNode, [firstPackageScenarioChild, firstPackageTestChild], "1.0.0"); + + const secondPackageNode = ConfigUtils.buildPackageNode("key-2", {}); + const secondPackageScenarioChild = ConfigUtils.buildChildNode("child-3-scenario", secondPackageNode.key, "SCENARIO"); + const secondPackageTestChild = ConfigUtils.buildChildNode("child-4", secondPackageNode.key, "TEST"); + const secondPackageZip = ConfigUtils.buildExportPackageZip(secondPackageNode, [secondPackageScenarioChild, secondPackageTestChild], "1.0.0"); + + const exportedPackagesZip = ConfigUtils.buildBatchExportZip(manifest, [firstPackageZip, secondPackageZip]); + + mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/core/packages/export/batch?packageKeys=key-1&packageKeys=key-2&withDependencies=true", exportedPackagesZip.toBuffer()); + mockAxiosPost("https://myTeam.celonis.cloud/package-manager/api/core/packages/export/batch/variables-with-assignments", []); + mockAxiosGet(`https://myTeam.celonis.cloud/package-manager/api/nodes/${firstPackageNode.key}/${firstPackageNode.key}`, {...firstPackageNode, spaceId: "space-1"}); + mockAxiosGet(`https://myTeam.celonis.cloud/package-manager/api/nodes/${secondPackageNode.key}/${secondPackageNode.key}`, {...secondPackageNode, spaceId: "space-2"}); + mockAxiosGet(`https://myTeam.celonis.cloud/package-manager/api/nodes/by-package-key/${firstPackageNode.key}/variables/runtime-values?appMode=VIEWER`, []); + mockAxiosGet(`https://myTeam.celonis.cloud/package-manager/api/nodes/by-package-key/${secondPackageNode.key}/variables/runtime-values?appMode=VIEWER`, []); + + await new ConfigCommandService(testContext).batchExportPackages(["key-1", "key-2"], true); + + const expectedFileName = loggingTestTransport.logMessages[0].message.split(FileService.fileDownloadedMessage)[1]; + expect(fs.openSync).toHaveBeenCalledWith(expectedFileName, expect.anything(), expect.anything()); + expect(mockWriteSync).toHaveBeenCalled(); + + const fileBuffer = mockWriteSync.mock.calls[0][1]; + const actualZip = new AdmZip(fileBuffer); + + const firstPackageExportedZip = new AdmZip(actualZip.getEntry("key-1_1.0.0.zip").getData()); + expect(firstPackageExportedZip.getEntry("nodes/child-1-scenario.json")).toBeNull(); + expect(firstPackageExportedZip.getEntry("nodes/child-2.json").getData().toString()).toEqual(stringify(firstPackageTestChild)); + + const secondPackageExportedZip = new AdmZip(actualZip.getEntry("key-2_1.0.0.zip").getData()); + expect(secondPackageExportedZip.getEntry("nodes/child-3-scenario.json")).toBeNull(); + expect(secondPackageExportedZip.getEntry("nodes/child-4.json").getData().toString()).toEqual(stringify(secondPackageTestChild)); + }) + + it("Should add appName to metadata for CONNECTION variables of package.json files", async () => { + const manifest: PackageManifestTransport[] = []; + manifest.push(ConfigUtils.buildManifestForKeyAndFlavor("key-1", BatchExportImportConstants.STUDIO)); + manifest.push(ConfigUtils.buildManifestForKeyAndFlavor("key-2", BatchExportImportConstants.STUDIO)); + + const firstPackageVariableDefinition: VariableDefinition[] = [ + { + key: "key1-var", + type: PackageManagerVariableType.DATA_MODEL, + runtime: false + }, + { + key: "key-1-connection", + type: PackageManagerVariableType.CONNECTION, + runtime: false + } + ]; + + const firstPackageNode = ConfigUtils.buildPackageNode("key-1", {variables: firstPackageVariableDefinition}); + const firstPackageZip = ConfigUtils.buildExportPackageZip(firstPackageNode, [], "1.0.0"); + + const secondPackageVariableDefinition: VariableDefinition[] = [ + { + key: "key2-var", + type: PackageManagerVariableType.CONNECTION, + runtime: false, + metadata: { + appName: "celonis" + } + }, + { + key: "key-2-connection", + type: PackageManagerVariableType.CONNECTION, + runtime: false + } + ]; + + const secondPackageNode = ConfigUtils.buildPackageNode("key-2", {variables: secondPackageVariableDefinition}); + const secondPackageZip = ConfigUtils.buildExportPackageZip(secondPackageNode, [], "1.0.0"); + + const exportedPackagesZip = ConfigUtils.buildBatchExportZip(manifest, [firstPackageZip, secondPackageZip]); + + const exportedVariables: VariableManifestTransport[] = [ + { + packageKey: "key-1", + version: "1.0.0", + variables: [ + { + key: "key1-var", + type: PackageManagerVariableType.DATA_MODEL, + value: "dm-id" as unknown as object, + metadata: {} + }, + { + key: "key-1-connection", + type: PackageManagerVariableType.CONNECTION, + value: { + appName: "celonis", + connectionId: "connection-id" + } as unknown as object, + metadata: null + } + ] + }, + { + packageKey: "key-2", + version: "1.0.0", + variables: [ + { + key: "key2-var", + type: PackageManagerVariableType.CONNECTION, + value: { + appName: "celonis", + connectionId: "connection-id" + } as unknown as object, + metadata: { + appName: "celonis" + } + }, + { + key: "key-2-connection", + type: PackageManagerVariableType.CONNECTION, + value: "connection-id", + metadata: { + appName: "nameOfApp" + } + } + ] + }, + ]; + + mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/core/packages/export/batch?packageKeys=key-1&packageKeys=key-2&withDependencies=true", exportedPackagesZip.toBuffer()); + mockAxiosPost("https://myTeam.celonis.cloud/package-manager/api/core/packages/export/batch/variables-with-assignments", exportedVariables); + mockAxiosGet(`https://myTeam.celonis.cloud/package-manager/api/nodes/${firstPackageNode.key}/${firstPackageNode.key}`, {...firstPackageNode, spaceId: "space-1"}); + mockAxiosGet(`https://myTeam.celonis.cloud/package-manager/api/nodes/${secondPackageNode.key}/${secondPackageNode.key}`, {...secondPackageNode, spaceId: "space-2"}); + mockAxiosGet(`https://myTeam.celonis.cloud/package-manager/api/nodes/by-package-key/${firstPackageNode.key}/variables/runtime-values?appMode=VIEWER`, []); + mockAxiosGet(`https://myTeam.celonis.cloud/package-manager/api/nodes/by-package-key/${secondPackageNode.key}/variables/runtime-values?appMode=VIEWER`, []); + + await new ConfigCommandService(testContext).batchExportPackages(["key-1", "key-2"], true); + + const expectedFileName = loggingTestTransport.logMessages[0].message.split(FileService.fileDownloadedMessage)[1]; + expect(fs.openSync).toHaveBeenCalledWith(expectedFileName, expect.anything(), expect.anything()); + expect(mockWriteSync).toHaveBeenCalled(); + + const fileBuffer = mockWriteSync.mock.calls[0][1]; + const actualZip = new AdmZip(fileBuffer); + + const firstPackageExportedZip = new AdmZip(actualZip.getEntry("key-1_1.0.0.zip").getData()); + const firstPackageExportedNode: NodeExportTransport = parse(firstPackageExportedZip.getEntry("package.json").getData().toString()); + expect(firstPackageExportedNode).toBeTruthy(); + const firstPackageContent: NodeConfiguration = firstPackageExportedNode.configuration; + expect(firstPackageContent.variables).toHaveLength(2); + expect(firstPackageContent.variables).toEqual([ + { + ...firstPackageVariableDefinition[0], + }, + { + ...firstPackageVariableDefinition[1], + metadata: { + appName: "celonis" + } + } + ]); + + const secondPackageExportedZip = new AdmZip(actualZip.getEntry("key-2_1.0.0.zip").getData()); + const secondPackageExportedNode: NodeExportTransport = parse(secondPackageExportedZip.getEntry("package.json").getData().toString()); + expect(secondPackageExportedNode).toBeTruthy(); + const secondPackageContent: NodeConfiguration = secondPackageExportedNode.configuration; + expect(secondPackageContent.variables).toHaveLength(2); + expect(secondPackageContent.variables).toEqual([{ + ...secondPackageVariableDefinition[0], + }, + { + ...secondPackageVariableDefinition[1], + metadata: { + appName: "nameOfApp" + } + } + ]); + }) + + it("Should export with SCENARIO nodes removed and CONNECTION variables fixed for package key with multiple underscores", async () => { + const manifest: PackageManifestTransport[] = []; + manifest.push(ConfigUtils.buildManifestForKeyAndFlavor("key_with_underscores_1", BatchExportImportConstants.STUDIO)); + + const firstPackageVariableDefinition: VariableDefinition[] = [ + { + key: "key1-var", + type: PackageManagerVariableType.DATA_MODEL, + runtime: false + }, + { + key: "key-1-connection", + type: PackageManagerVariableType.CONNECTION, + runtime: false + }, + { + key: "key-1-another-connection", + type: PackageManagerVariableType.CONNECTION, + runtime: false + } + ]; + + const firstPackageNode = ConfigUtils.buildPackageNode("key_with_underscores_1", {variables: firstPackageVariableDefinition}); + const firstPackageScenarioChild = ConfigUtils.buildChildNode("child-1-scenario", firstPackageNode.key, "SCENARIO"); + const firstPackageZip = ConfigUtils.buildExportPackageZip(firstPackageNode, [firstPackageScenarioChild], "1.0.0"); + + const exportedPackagesZip = ConfigUtils.buildBatchExportZip(manifest, [firstPackageZip]); + + const exportedVariables: VariableManifestTransport[] = [ + { + packageKey: "key_with_underscores_1", + version: "1.0.0", + variables: [ + { + key: "key1-var", + type: PackageManagerVariableType.DATA_MODEL, + value: "dm-id" as unknown as object, + metadata: {} + }, + { + key: "key-1-connection", + type: PackageManagerVariableType.CONNECTION, + value: { + appName: "celonis", + connectionId: "connection-id" + } as unknown as object, + metadata: null + }, + { + key: "key-1-another-connection", + type: PackageManagerVariableType.CONNECTION, + value: "connection-id", + metadata: { + appName: "nameOfApp" + } + } + ] + } + ]; + + mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/core/packages/export/batch?packageKeys=key_with_underscores_1&withDependencies=true", exportedPackagesZip.toBuffer()); + mockAxiosPost("https://myTeam.celonis.cloud/package-manager/api/core/packages/export/batch/variables-with-assignments", exportedVariables); + mockAxiosGet(`https://myTeam.celonis.cloud/package-manager/api/nodes/${firstPackageNode.key}/${firstPackageNode.key}`, {...firstPackageNode, spaceId: "space-1"}); + mockAxiosGet(`https://myTeam.celonis.cloud/package-manager/api/nodes/by-package-key/${firstPackageNode.key}/variables/runtime-values?appMode=VIEWER`, []); + + await new ConfigCommandService(testContext).batchExportPackages(["key_with_underscores_1"], true); + + const expectedFileName = loggingTestTransport.logMessages[0].message.split(FileService.fileDownloadedMessage)[1]; + expect(fs.openSync).toHaveBeenCalledWith(expectedFileName, expect.anything(), expect.anything()); + expect(mockWriteSync).toHaveBeenCalled(); + + const fileBuffer = mockWriteSync.mock.calls[0][1]; + const actualZip = new AdmZip(fileBuffer); + + const firstPackageExportedZip = new AdmZip(actualZip.getEntry("key_with_underscores_1_1.0.0.zip").getData()); + const firstPackageExportedNode: NodeExportTransport = parse(firstPackageExportedZip.getEntry("package.json").getData().toString()); + expect(firstPackageExportedNode).toBeTruthy(); + const firstPackageContent: NodeConfiguration = firstPackageExportedNode.configuration; + expect(firstPackageContent.variables).toHaveLength(3); + expect(firstPackageContent.variables).toEqual([ + { + ...firstPackageVariableDefinition[0], + }, + { + ...firstPackageVariableDefinition[1], + metadata: { + appName: "celonis" + } + }, { + ...firstPackageVariableDefinition[2], + metadata: { + appName: "nameOfApp" + } + } + ]); + + expect(firstPackageExportedZip.getEntry("nodes/child-1-scenario.json")).toBeNull(); + }) + + it("Should export by packageKeys without dependencies", async () => { + const manifest: PackageManifestTransport[] = []; + manifest.push(ConfigUtils.buildManifestForKeyAndFlavor("key-1", "TEST")); + manifest.push(ConfigUtils.buildManifestForKeyAndFlavor("key-2", "TEST")); + const exportedPackagesZip = ConfigUtils.buildBatchExportZip(manifest, []); + + mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/core/packages/export/batch?packageKeys=key-1&packageKeys=key-2&packageKeys=key-3&withDependencies=false", exportedPackagesZip.toBuffer()); + mockAxiosPost("https://myTeam.celonis.cloud/package-manager/api/core/packages/export/batch/variables-with-assignments", []); + + await new ConfigCommandService(testContext).batchExportPackages(["key-1", "key-2", "key-3"], false); + + const expectedFileName = loggingTestTransport.logMessages[0].message.split(FileService.fileDownloadedMessage)[1]; + expect(fs.openSync).toHaveBeenCalledWith(expectedFileName, expect.anything(), expect.anything()); + expect(mockWriteSync).toHaveBeenCalled(); + }) +}) \ No newline at end of file diff --git a/tests/commands/configuration-management/config-import.spec.ts b/tests/commands/configuration-management/config-import.spec.ts new file mode 100644 index 00000000..bb0d0220 --- /dev/null +++ b/tests/commands/configuration-management/config-import.spec.ts @@ -0,0 +1,322 @@ +import * as path from "path"; +import { mockCreateReadStream, mockExistsSync, mockReadFileSync } from "../../utls/fs-mock-utils"; +import { + PackageManifestTransport, PostPackageImportData, StudioPackageManifest, +} from "../../../src/commands/configuration-management/interfaces/package-export.interfaces"; +import { + mockAxiosGet, + mockAxiosPost, + mockAxiosPut, + mockedAxiosInstance, + mockedPostRequestBodyByUrl, +} from "../../utls/http-requests-mock"; +import { ConfigCommandService } from "../../../src/commands/configuration-management/config-command.service"; +import { testContext } from "../../utls/test-context"; +import { loggingTestTransport, mockWriteFileSync } from "../../jest.setup"; +import { SpaceTransport } from "../../../src/commands/studio/interfaces/space.interface"; +import { + ContentNodeTransport, + PackageManagerVariableType, VariablesAssignments, +} from "../../../src/commands/studio/interfaces/package-manager.interfaces"; +import { + BatchExportImportConstants +} from "../../../src/commands/configuration-management/interfaces/batch-export-import.constants"; +import { ConfigUtils } from "../../utls/config-utils"; +import { stringify } from "../../../src/core/utils/json"; + +describe("Config import", () => { + + const LOG_MESSAGE: string = "Config import report file: "; + + beforeEach(() => { + mockExistsSync(); + }) + + it.each([ + true, + false + ]) ("Should batch import package configs with overwrite %p", async (overwrite: boolean) => { + const manifest: PackageManifestTransport[] = []; + manifest.push(ConfigUtils.buildManifestForKeyAndFlavor("key-1", "TEST")); + const exportedPackagesZip = ConfigUtils.buildBatchExportZip(manifest, []); + + mockReadFileSync(exportedPackagesZip.toBuffer()); + mockCreateReadStream(exportedPackagesZip.toBuffer()); + mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/packages", []); + + const importResponse: PostPackageImportData[] = [{ + packageKey: "key-1", + importedVersions: [{ + oldVersion: "1.0.2", + newVersion: "1.0.0" + }] + }]; + + mockAxiosPost("https://myTeam.celonis.cloud/package-manager/api/core/packages/import/batch", importResponse); + + await new ConfigCommandService(testContext).batchImportPackages("./export_file.zip", overwrite); + + const expectedFileName = loggingTestTransport.logMessages[0].message.split(LOG_MESSAGE)[1]; + expect(mockWriteFileSync).toHaveBeenCalledWith(path.resolve(process.cwd(), expectedFileName), JSON.stringify(importResponse), {encoding: "utf-8"}); + }) + + it("Should batch import configs & map space ID as specified in manifest file for Studio Packages", async () => { + const manifest: PackageManifestTransport[] = []; + manifest.push(ConfigUtils.buildManifestForKeyAndFlavor("key-2", "STUDIO")); + + const studioManifest: StudioPackageManifest[] = []; + studioManifest.push(ConfigUtils.buildStudioManifestForKeyWithSpace("key-2", "spaceName", "space-id")); + + const firstPackageNode = ConfigUtils.buildPackageNode("key-2", {variables: []}); + const firstPackageZip = ConfigUtils.buildExportPackageZip(firstPackageNode, [], "1.0.0"); + const exportedPackagesZip = ConfigUtils.buildBatchExportZipWithStudioManifest(manifest, studioManifest,[firstPackageZip]); + + const space: SpaceTransport = { + id: "space-id", + name: "space", + iconReference: "earth" + }; + + const otherSpace: SpaceTransport = { + id: "spaceId", + name: "spaceName", + iconReference: "earth" + }; + + mockReadFileSync(exportedPackagesZip.toBuffer()); + mockCreateReadStream(exportedPackagesZip.toBuffer()); + mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/packages", []); + mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/spaces", [space, otherSpace]); + + const importResponse: PostPackageImportData[] = [{ + packageKey: "key-1", + importedVersions: [{ + oldVersion: "1.0.2", + newVersion: "1.0.0" + }] + }]; + + mockAxiosPost("https://myTeam.celonis.cloud/package-manager/api/core/packages/import/batch", importResponse); + + await new ConfigCommandService(testContext).batchImportPackages("./export_file.zip", true); + + const expectedFileName = loggingTestTransport.logMessages[0].message.split(LOG_MESSAGE)[1]; + expect(mockWriteFileSync).toHaveBeenCalledWith(path.resolve(process.cwd(), expectedFileName), JSON.stringify(importResponse), {encoding: "utf-8"}); + }) + + it("Should fail to map space ID as the space id specified in manifest file cannot be found", async () => { + const manifest: PackageManifestTransport[] = []; + manifest.push(ConfigUtils.buildManifestForKeyAndFlavor("key-2", "STUDIO")); + + const studioManifest: StudioPackageManifest[] = []; + studioManifest.push(ConfigUtils.buildStudioManifestForKeyWithSpace("key-2", "spaceName", "space")); + + const firstPackageNode = ConfigUtils.buildPackageNode("key-2", {variables: []}); + const firstPackageZip = ConfigUtils.buildExportPackageZip(firstPackageNode, [], "1.0.0"); + const exportedPackagesZip = ConfigUtils.buildBatchExportZipWithStudioManifest(manifest, studioManifest,[firstPackageZip]); + + const space: SpaceTransport = { + id: "space-id", + name: "space", + iconReference: "earth" + }; + + mockReadFileSync(exportedPackagesZip.toBuffer()); + mockCreateReadStream(exportedPackagesZip.toBuffer()); + mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/packages", []); + mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/spaces", [space]); + + await expect( + new ConfigCommandService(testContext).batchImportPackages("./export_file.zip", true) + ).rejects.toThrow("Provided space ID does not exist."); + }) + + it("Should batch import configs & map space ID as specified in manifest file for Studio Packages & move to space for existing packages.", async () => { + const manifest: PackageManifestTransport[] = []; + manifest.push(ConfigUtils.buildManifestForKeyAndFlavor("key-2", "STUDIO")); + + const studioManifest: StudioPackageManifest[] = []; + studioManifest.push(ConfigUtils.buildStudioManifestForKeyWithSpace("key-2", "space", "spaceId")); + + const firstPackageNode = ConfigUtils.buildPackageNode("key-2", {variables: []}); + const firstPackageZip = ConfigUtils.buildExportPackageZip(firstPackageNode, [], "1.0.0"); + const exportedPackagesZip = ConfigUtils.buildBatchExportZipWithStudioManifest(manifest, studioManifest,[firstPackageZip]); + + + const existingNode: Partial = { + id: "node-id", + key: "key-2" + } + + const space: SpaceTransport = { + id: "spaceId", + name: "space", + iconReference: "earth" + }; + + mockReadFileSync(exportedPackagesZip.toBuffer()); + mockCreateReadStream(exportedPackagesZip.toBuffer()); + mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/packages", [existingNode]); + mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/spaces", [space]); + mockAxiosPut("https://myTeam.celonis.cloud/package-manager/api/packages/node-id/move/spaceId", {}); + + const importResponse: PostPackageImportData[] = [{ + packageKey: "key-1", + importedVersions: [{ + oldVersion: "1.0.2", + newVersion: "1.0.0" + }] + }]; + + mockAxiosPost("https://myTeam.celonis.cloud/package-manager/api/core/packages/import/batch", importResponse); + + await new ConfigCommandService(testContext).batchImportPackages("./export_file.zip", true); + const expectedFileName = loggingTestTransport.logMessages[0].message.split(LOG_MESSAGE)[1]; + expect(mockWriteFileSync).toHaveBeenCalledWith(path.resolve(process.cwd(), expectedFileName), JSON.stringify(importResponse), {encoding: "utf-8"}); + expect(mockedAxiosInstance.put).toHaveBeenCalledWith("https://myTeam.celonis.cloud/package-manager/api/packages/node-id/move/spaceId", expect.anything(), expect.anything()); + }) + + it("Should batch import configs & map space ID by finding space with name as specified in manifest file", async () => { + const manifest: PackageManifestTransport[] = []; + manifest.push(ConfigUtils.buildManifestForKeyAndFlavor("key-2", "STUDIO")); + + const studioManifest: StudioPackageManifest[] = []; + studioManifest.push(ConfigUtils.buildStudioManifestForKeyWithSpace("key-2", "spaceName", null)); + + const firstPackageNode = ConfigUtils.buildPackageNode("key-2", {variables: []}); + const firstPackageZip = ConfigUtils.buildExportPackageZip(firstPackageNode, [], "1.0.0"); + const exportedPackagesZip = ConfigUtils.buildBatchExportZipWithStudioManifest(manifest, studioManifest,[firstPackageZip]); + + const space: SpaceTransport = { + id: "spaceId", + name: "spaceName", + iconReference: "earth" + }; + mockReadFileSync(exportedPackagesZip.toBuffer()); + mockCreateReadStream(exportedPackagesZip.toBuffer()); + mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/packages", []); + mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/spaces", [space]); + + const importResponse: PostPackageImportData[] = [{ + packageKey: "key-1", + importedVersions: [{ + oldVersion: "1.0.2", + newVersion: "1.0.0" + }] + }]; + + mockAxiosPost("https://myTeam.celonis.cloud/package-manager/api/core/packages/import/batch", importResponse); + + await new ConfigCommandService(testContext).batchImportPackages("./export_file.zip", true); + + const expectedFileName = loggingTestTransport.logMessages[0].message.split(LOG_MESSAGE)[1]; + expect(mockWriteFileSync).toHaveBeenCalledWith(path.resolve(process.cwd(), expectedFileName), JSON.stringify(importResponse), {encoding: "utf-8"}); + expect(mockedAxiosInstance.put).not.toHaveBeenCalledWith("https://myTeam.celonis.cloud/package-manager/api/spaces", expect.anything(), expect.anything()); + }) + + it("Should batch import configs & create new space", async () => { + const manifest: PackageManifestTransport[] = []; + manifest.push(ConfigUtils.buildManifestForKeyAndFlavor("key-2", "STUDIO")); + + const studioManifest: StudioPackageManifest[] = []; + studioManifest.push(ConfigUtils.buildStudioManifestForKeyWithSpace("key-2", "otherName", null)); + + const firstPackageNode = ConfigUtils.buildPackageNode("key-2", {variables: []}); + const firstPackageZip = ConfigUtils.buildExportPackageZip(firstPackageNode, [], "1.0.0"); + const exportedPackagesZip = ConfigUtils.buildBatchExportZipWithStudioManifest(manifest, studioManifest,[firstPackageZip]); + + const space: SpaceTransport = { + id: "space-id", + name: "space-name", + iconReference: "earth" + }; + + const newSpace: SpaceTransport = { + id: "otherId", + name: "otherName", + iconReference: "earth" + }; + + mockReadFileSync(exportedPackagesZip.toBuffer()); + mockCreateReadStream(exportedPackagesZip.toBuffer()); + mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/packages", []); + mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/spaces", [space]); + mockAxiosPost("https://myTeam.celonis.cloud/package-manager/api/spaces", [newSpace]); + + + const importResponse: PostPackageImportData[] = [{ + packageKey: "key-1", + importedVersions: [{ + oldVersion: "1.0.2", + newVersion: "1.0.0" + }] + }]; + + mockAxiosPost("https://myTeam.celonis.cloud/package-manager/api/core/packages/import/batch", importResponse); + + await new ConfigCommandService(testContext).batchImportPackages("./export_file.zip", true); + + const expectedFileName = loggingTestTransport.logMessages[0].message.split(LOG_MESSAGE)[1]; + expect(mockWriteFileSync).toHaveBeenCalledWith(path.resolve(process.cwd(), expectedFileName), JSON.stringify(importResponse), {encoding: "utf-8"}); + expect(mockedAxiosInstance.put).not.toHaveBeenCalledWith("https://myTeam.celonis.cloud/package-manager/api/spaces", expect.anything(), expect.anything()); + }) + + it("Should assign studio runtime variable values after import", async () => { + const manifest: PackageManifestTransport[] = []; + manifest.push(ConfigUtils.buildManifestForKeyAndFlavor("key-1", "STUDIO")); + const exportedPackagesZip = ConfigUtils.buildBatchExportZip(manifest, []); + const variableAssignment: VariablesAssignments = { + key: "variable-1", + type: PackageManagerVariableType.PLAIN_TEXT, + value: "some-value" as unknown as object + } + const studioManifest: StudioPackageManifest[] = [{ + packageKey: "key-1", + space: { + id: "space-id", + name: "space", + iconReference: "earth" + }, + runtimeVariableAssignments: [variableAssignment] + }]; + exportedPackagesZip.addFile(BatchExportImportConstants.STUDIO_FILE_NAME, Buffer.from(stringify(studioManifest))); + + mockReadFileSync(exportedPackagesZip.toBuffer()); + mockCreateReadStream(exportedPackagesZip.toBuffer()); + + const importResponse: PostPackageImportData[] = [{ + packageKey: "key-1", + importedVersions: [{ + oldVersion: "1.0.2", + newVersion: "1.0.0" + }] + }]; + + const node: Partial = { + id: "node-id", + key: "key-1" + } + + const space: SpaceTransport = { + id: "space-id", + name: "space", + iconReference: "earth" + }; + + const assignVariablesUrl = "https://myTeam.celonis.cloud/package-manager/api/nodes/by-package-key/key-1/variables/values"; + + mockAxiosPost("https://myTeam.celonis.cloud/package-manager/api/core/packages/import/batch", importResponse); + mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/nodes/key-1/key-1", node); + mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/spaces", [space]); + mockAxiosPut("https://myTeam.celonis.cloud/package-manager/api/packages/node-id/move/space-id", {}); + mockAxiosPost(assignVariablesUrl, {}); + mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/packages", [node]); + + await new ConfigCommandService(testContext).batchImportPackages("./export_file.zip", true); + + const expectedFileName = loggingTestTransport.logMessages[0].message.split(LOG_MESSAGE)[1]; + expect(mockWriteFileSync).toHaveBeenCalledWith(path.resolve(process.cwd(), expectedFileName), JSON.stringify(importResponse), {encoding: "utf-8"}); + + expect(mockedPostRequestBodyByUrl.get(assignVariablesUrl)).toEqual(JSON.stringify([variableAssignment])); + }) +}) diff --git a/tests/commands/configuration-management/config-list-variables.spec.ts b/tests/commands/configuration-management/config-list-variables.spec.ts new file mode 100644 index 00000000..9c028a92 --- /dev/null +++ b/tests/commands/configuration-management/config-list-variables.spec.ts @@ -0,0 +1,177 @@ +import * as path from "path"; +import * as fs from "fs"; +import { parse } from "../../../src/core/utils/json"; +import { + PackageKeyAndVersionPair, + VariableManifestTransport, +} from "../../../src/commands/configuration-management/interfaces/package-export.interfaces"; +import { PackageManagerVariableType } from "../../../src/commands/studio/interfaces/package-manager.interfaces"; +import { mockAxiosPost, mockedPostRequestBodyByUrl } from "../../utls/http-requests-mock"; +import { ConfigCommandService } from "../../../src/commands/configuration-management/config-command.service"; +import { testContext } from "../../utls/test-context"; +import { loggingTestTransport, mockWriteFileSync } from "../../jest.setup"; +import { FileService } from "../../../src/core/utils/file-service"; + +describe("Config listVariables", () => { + + const firstManifest: VariableManifestTransport = { + packageKey: "key-1", + version: "1.0.0", + variables: [ + { + key: "key1-var", + type: PackageManagerVariableType.DATA_MODEL, + value: "dm-id" as unknown as object, + metadata: {} + }, + { + key: "key-1-connection", + type: PackageManagerVariableType.CONNECTION, + value: { + appName: "celonis", + connectionId: "connection-id" + } as unknown as object, + metadata: null + } + ] + }; + + const secondManifest: VariableManifestTransport = { + packageKey: "key-2", + version: "1.0.0", + variables: [ + { + key: "key2-var", + type: PackageManagerVariableType.CONNECTION, + value: { + appName: "celonis", + connectionId: "connection-id" + } as unknown as object, + metadata: { + appName: "celonis" + } + } + ] + }; + + const thirdManifest: VariableManifestTransport = { + packageKey: "key-3", + version: "1.0.0", + variables: [ + { + key: "key2-var", + type: PackageManagerVariableType.CONNECTION, + value: "connection-id", + metadata: { + appName: "celonis" + } + } + ] + } + + const fixedVariableManifests: VariableManifestTransport[] = [ + { + ...firstManifest, + variables: [ + { + ...firstManifest.variables[0] + }, + { + ...firstManifest.variables[1], + metadata: { + appName: "celonis" + } + } + ] + }, + { + ...secondManifest + }, + { + ...thirdManifest + } + ]; + + const packageKeyAndVersionPairs: PackageKeyAndVersionPair[] = [ + { + packageKey: "key-1", + version: "1.0.0" + }, + { + packageKey: "key-2", + version: "1.0.0" + }, + { + packageKey: "key-3", + version: "1.0.0" + } + ]; + + beforeEach(() => { + mockAxiosPost("https://myTeam.celonis.cloud/package-manager/api/core/packages/export/batch/variables-with-assignments", [{...firstManifest}, {...secondManifest}, {...thirdManifest}]); + }) + + it("Should list fixed variables for non-json response", async () => { + await new ConfigCommandService(testContext).listVariables(false, ["key-1:1.0.0", "key-2:1.0.0", "key-3:1.0.0"], null); + + expect(loggingTestTransport.logMessages.length).toBe(3); + expect(loggingTestTransport.logMessages[0].message).toContain(JSON.stringify(fixedVariableManifests[0])); + expect(loggingTestTransport.logMessages[1].message).toContain(JSON.stringify(fixedVariableManifests[1])); + expect(loggingTestTransport.logMessages[2].message).toContain(JSON.stringify(fixedVariableManifests[2])); + + const variableExportRequest = parse(mockedPostRequestBodyByUrl.get("https://myTeam.celonis.cloud/package-manager/api/core/packages/export/batch/variables-with-assignments")); + expect(variableExportRequest).toEqual(packageKeyAndVersionPairs); + }) + + it("Should export fixed variables for json response", async () => { + await new ConfigCommandService(testContext).listVariables(true, ["key-1:1.0.0", "key-2:1.0.0", "key-3:1.0.0"], null); + + expect(loggingTestTransport.logMessages.length).toBe(1); + expect(loggingTestTransport.logMessages[0].message).toContain(FileService.fileDownloadedMessage); + + const expectedFileName = loggingTestTransport.logMessages[0].message.split(FileService.fileDownloadedMessage)[1]; + expect(mockWriteFileSync).toHaveBeenCalledWith(path.resolve(process.cwd(), expectedFileName), JSON.stringify(fixedVariableManifests), {encoding: "utf-8"}); + + const variableExportRequest = parse(mockedPostRequestBodyByUrl.get("https://myTeam.celonis.cloud/package-manager/api/core/packages/export/batch/variables-with-assignments")); + expect(variableExportRequest).toEqual(packageKeyAndVersionPairs); + }) + + it("Should list fixed variables for non-json response and keysByVersion file mapping", async () => { + (fs.existsSync as jest.Mock).mockReturnValue(true); + (fs.readFileSync as jest.Mock).mockReturnValue(JSON.stringify(packageKeyAndVersionPairs)); + + await new ConfigCommandService(testContext).listVariables(false, [], "key_version_mapping.json"); + + expect(loggingTestTransport.logMessages.length).toBe(3); + expect(loggingTestTransport.logMessages[0].message).toContain(JSON.stringify(fixedVariableManifests[0])); + expect(loggingTestTransport.logMessages[1].message).toContain(JSON.stringify(fixedVariableManifests[1])); + expect(loggingTestTransport.logMessages[2].message).toContain(JSON.stringify(fixedVariableManifests[2])); + + const variableExportRequest = parse(mockedPostRequestBodyByUrl.get("https://myTeam.celonis.cloud/package-manager/api/core/packages/export/batch/variables-with-assignments")); + expect(variableExportRequest).toEqual(packageKeyAndVersionPairs); + }) + + it("Should export fixed variables for json response and keysByVersion file mapping", async () => { + (fs.existsSync as jest.Mock).mockReturnValue(true); + (fs.readFileSync as jest.Mock).mockReturnValue(JSON.stringify(packageKeyAndVersionPairs)); + + await new ConfigCommandService(testContext).listVariables(true, [], "key_version_mapping.json"); + + expect(loggingTestTransport.logMessages.length).toBe(1); + expect(loggingTestTransport.logMessages[0].message).toContain(FileService.fileDownloadedMessage); + + const expectedFileName = loggingTestTransport.logMessages[0].message.split(FileService.fileDownloadedMessage)[1]; + expect(mockWriteFileSync).toHaveBeenCalledWith(path.resolve(process.cwd(), expectedFileName), JSON.stringify(fixedVariableManifests), {encoding: "utf-8"}); + + const variableExportRequest = parse(mockedPostRequestBodyByUrl.get("https://myTeam.celonis.cloud/package-manager/api/core/packages/export/batch/variables-with-assignments")); + expect(variableExportRequest).toEqual(packageKeyAndVersionPairs); + }) + + it("Should throw error if no mapping and no file path is provided", async () => { + try { + await new ConfigCommandService(testContext).listVariables(true, [], ""); + } catch (e) { + expect(e.message).toEqual("Please provide keysByVersion mappings or file path!"); + } + }) +}) \ No newline at end of file diff --git a/tests/commands/configuration-management/config-list.spec.ts b/tests/commands/configuration-management/config-list.spec.ts new file mode 100644 index 00000000..b1c6af02 --- /dev/null +++ b/tests/commands/configuration-management/config-list.spec.ts @@ -0,0 +1,218 @@ +import * as path from "path"; +import { stringify } from "../../../src/core/utils/json"; +import { PacmanApiUtils } from "../../utls/pacman-api.utils"; +import { mockAxiosGet } from "../../utls/http-requests-mock"; +import { ConfigCommandService } from "../../../src/commands/configuration-management/config-command.service"; +import { testContext } from "../../utls/test-context"; +import { loggingTestTransport, mockWriteFileSync } from "../../jest.setup"; +import { + ContentNodeTransport, + PackageWithVariableAssignments, StudioComputeNodeDescriptor, +} from "../../../src/commands/studio/interfaces/package-manager.interfaces"; +import { FileService } from "../../../src/core/utils/file-service"; +import { + PackageExportTransport +} from "../../../src/commands/configuration-management/interfaces/package-export.interfaces"; + +describe("Config list", () => { + + it.each([ + "", + "STUDIO,TEST" + ])( + "Should list all packages by key for non-json response with flavors: %p", + async (flavors: string) => { + const firstPackage = PacmanApiUtils.buildPackageExportTransport("key-1", "name-1"); + const secondPackage = PacmanApiUtils.buildPackageExportTransport("key-2", "name-2"); + + const flavorsArray = flavors !== "" ? flavors.split(",") : []; + + const urlParams = new URLSearchParams(); + urlParams.set("withDependencies", "false"); + flavorsArray.forEach(flavor => urlParams.append("flavors", flavor)); + + mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/core/packages/export/list?" + urlParams.toString(), [firstPackage, secondPackage]); + + await new ConfigCommandService(testContext).listActivePackages(false, flavorsArray, false, [], null, null); + + expect(loggingTestTransport.logMessages.length).toBe(2); + expect(loggingTestTransport.logMessages[0].message).toContain(`${firstPackage.name} - Key: "${firstPackage.key}"`); + expect(loggingTestTransport.logMessages[1].message).toContain(`${secondPackage.name} - Key: "${secondPackage.key}"`); + } + ) + + it("Should export all packages for json response with spaceId set for studio packages", async () => { + const firstPackage = PacmanApiUtils.buildPackageExportTransport("key-1", "name-1"); + const secondPackage = PacmanApiUtils.buildPackageExportTransport("key-2", "name-2"); + + const studioPackage: ContentNodeTransport = PacmanApiUtils.buildContentNodeTransport("key-1", "spaceId-1"); + + mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/core/packages/export/list?withDependencies=false", [{...firstPackage}, {...secondPackage}]); + mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/packages/with-variable-assignments?type=DATA_MODEL", [studioPackage]); + + await new ConfigCommandService(testContext).listActivePackages(true, [], false, [], null, null); + + const expectedFileName = loggingTestTransport.logMessages[0].message.split(FileService.fileDownloadedMessage)[1]; + + expect(mockWriteFileSync).toHaveBeenCalledWith(path.resolve(process.cwd(), expectedFileName), expect.any(String), {encoding: "utf-8"}); + + const exportedTransports = JSON.parse(mockWriteFileSync.mock.calls[0][1]) as PackageExportTransport[]; + expect(exportedTransports.length).toBe(2); + + const exportedFirstPackage = exportedTransports.filter(transport => transport.key === firstPackage.key)[0]; + const exportedSecondPackage = exportedTransports.filter(transport => transport.key === secondPackage.key)[0]; + + expect(exportedSecondPackage).toEqual(secondPackage); + expect(exportedFirstPackage).toEqual({...firstPackage, spaceId: "spaceId-1"}); + }) + + it("Should export all packages with dependencies and data models set for json response and withDependencies option", async () => { + const firstPackage = PacmanApiUtils.buildPackageExportTransport("key-1", "name-1"); + const secondPackage = PacmanApiUtils.buildPackageExportTransport("key-2", "name-2"); + + mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/core/packages/export/list?withDependencies=true", [{...firstPackage}, {...secondPackage}]); + mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/packages/with-variable-assignments?type=DATA_MODEL", []); + + const dataModelVariableAssignmentResponse: PackageWithVariableAssignments = { + id: "var-id", + key: firstPackage.key, + name: "var-name", + createdBy: "", + spaceId: undefined, + variableAssignments: [ + { + key: "var-key", + value: "datamodel-id" as unknown as object, + type: "DATA_MODEL" + } + ] + }; + mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/packages/with-variable-assignments?type=DATA_MODEL", [dataModelVariableAssignmentResponse]); + + const dataModelDetailResponse: StudioComputeNodeDescriptor = { + name: "pool-name", + dataModelId: "datamodel-id", + poolId: "pool-id" + }; + mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/compute-pools/data-models/details", [dataModelDetailResponse]); + + await new ConfigCommandService(testContext).listActivePackages(true, [], true, [], null, null); + + const expectedFileName = loggingTestTransport.logMessages[0].message.split(FileService.fileDownloadedMessage)[1]; + + expect(mockWriteFileSync).toHaveBeenCalledWith(path.resolve(process.cwd(), expectedFileName), expect.any(String), {encoding: "utf-8"}); + + const exportedTransports = JSON.parse(mockWriteFileSync.mock.calls[0][1]) as PackageExportTransport[]; + expect(exportedTransports.length).toBe(2); + + const exportedFirstPackage = exportedTransports.filter(transport => transport.key === firstPackage.key)[0]; + const exportedSecondPackage = exportedTransports.filter(transport => transport.key === secondPackage.key)[0]; + + expect(exportedSecondPackage).toEqual(secondPackage); + expect(exportedFirstPackage).toEqual({...firstPackage, datamodels: [{...dataModelDetailResponse}]}); + }) + + it("Should export packagesByKeys with spaceId set for studio packages", async () => { + const firstPackage = PacmanApiUtils.buildPackageExportTransport("key-1", "name-1"); + const secondPackage = PacmanApiUtils.buildPackageExportTransport("key-2", "name-2"); + + const studioPackage: ContentNodeTransport = PacmanApiUtils.buildContentNodeTransport("key-1", "spaceId-1"); + + mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/core/packages/export/list-by-keys?packageKeys=key-1&packageKeys=key-2&withDependencies=false", [{...firstPackage}, {...secondPackage}]); + mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/packages/with-variable-assignments?type=DATA_MODEL", [studioPackage]); + + await new ConfigCommandService(testContext).listActivePackages(true, [], false, [firstPackage.key, secondPackage.key], null, null); + + const expectedFileName = loggingTestTransport.logMessages[0].message.split(FileService.fileDownloadedMessage)[1]; + + expect(mockWriteFileSync).toHaveBeenCalledWith(path.resolve(process.cwd(), expectedFileName), expect.any(String), {encoding: "utf-8"}); + + const exportedTransports = JSON.parse(mockWriteFileSync.mock.calls[0][1]) as PackageExportTransport[]; + expect(exportedTransports.length).toBe(2); + + const exportedFirstPackage = exportedTransports.filter(transport => transport.key === firstPackage.key)[0]; + const exportedSecondPackage = exportedTransports.filter(transport => transport.key === secondPackage.key)[0]; + + expect(exportedSecondPackage).toEqual(secondPackage); + expect(exportedFirstPackage).toEqual({...firstPackage, spaceId: "spaceId-1"}); + }) + + it("Should export packagesByKeys with dependencies and data models set for withDependencies option", async () => { + const firstPackage = PacmanApiUtils.buildPackageExportTransport("key-1", "name-1"); + const secondPackage = PacmanApiUtils.buildPackageExportTransport("key-2", "name-2"); + + mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/core/packages/export/list-by-keys?packageKeys=key-1&packageKeys=key-2&withDependencies=true", [{...firstPackage}, {...secondPackage}]); + mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/packages/with-variable-assignments?type=DATA_MODEL", []); + + const dataModelVariableAssignmentResponse: PackageWithVariableAssignments = { + id: "var-id", + key: firstPackage.key, + name: "var-name", + createdBy: "", + spaceId: undefined, + variableAssignments: [ + { + key: "var-key", + value: "datamodel-id" as unknown as object, + type: "DATA_MODEL" + } + ] + }; + mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/packages/with-variable-assignments?type=DATA_MODEL", [dataModelVariableAssignmentResponse]); + + const dataModelDetailResponse: StudioComputeNodeDescriptor = { + name: "pool-name", + dataModelId: "datamodel-id", + poolId: "pool-id" + }; + mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/compute-pools/data-models/details", [dataModelDetailResponse]); + + await new ConfigCommandService(testContext).listActivePackages(true, [], true, [firstPackage.key, secondPackage.key], null, null); + + const expectedFileName = loggingTestTransport.logMessages[0].message.split(FileService.fileDownloadedMessage)[1]; + + expect(mockWriteFileSync).toHaveBeenCalledWith(path.resolve(process.cwd(), expectedFileName), expect.any(String), {encoding: "utf-8"}); + + const exportedTransports = JSON.parse(mockWriteFileSync.mock.calls[0][1]) as PackageExportTransport[]; + expect(exportedTransports.length).toBe(2); + + const exportedFirstPackage = exportedTransports.filter(transport => transport.key === firstPackage.key)[0]; + const exportedSecondPackage = exportedTransports.filter(transport => transport.key === secondPackage.key)[0]; + + expect(exportedSecondPackage).toEqual(secondPackage); + expect(exportedFirstPackage).toEqual({...firstPackage, datamodels: [{...dataModelDetailResponse}]}); + }) + + it("Should list all packages filtered by variable value", async () => { + const firstPackage = PacmanApiUtils.buildPackageExportTransport("key-1", "name-1"); + const secondPackage = PacmanApiUtils.buildPackageExportTransport("key-2", "name-2"); + + + mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/core/packages/export/list-by-variable-value?variableValue=1", [{...firstPackage}, {...secondPackage}]); + mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/packages/with-variable-assignments?type=DATA_MODEL", []); + + await new ConfigCommandService(testContext).listActivePackages(false, [], false, [], "1", null); + + expect(loggingTestTransport.logMessages.length).toBe(2); + expect(loggingTestTransport.logMessages[0].message).toContain(`${firstPackage.name} - Key: "${firstPackage.key}"`); + expect(loggingTestTransport.logMessages[1].message).toContain(`${secondPackage.name} - Key: "${secondPackage.key}"`); + }) + + it("Should export all packages for json response filtered by variable value", async () => { + const firstPackage = PacmanApiUtils.buildPackageExportTransport("key-1", "name-1"); + const secondPackage = PacmanApiUtils.buildPackageExportTransport("key-2", "name-2"); + + mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/core/packages/export/list-by-variable-value?variableValue=1", [{...firstPackage}, {...secondPackage}]); + mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/packages/with-variable-assignments?type=DATA_MODEL", []); + + await new ConfigCommandService(testContext).listActivePackages(true, [], false, [], "1", null); + + const expectedFileName = loggingTestTransport.logMessages[0].message.split(FileService.fileDownloadedMessage)[1]; + + expect(mockWriteFileSync).toHaveBeenCalledWith(path.resolve(process.cwd(), expectedFileName), expect.any(String), {encoding: "utf-8"}); + + const exportedTransports = JSON.parse(mockWriteFileSync.mock.calls[0][1]) as PackageExportTransport[]; + expect(exportedTransports.length).toBe(2); + }) + +}) \ No newline at end of file diff --git a/tests/commands/configuration-management/list-assignments.spec.ts b/tests/commands/configuration-management/list-assignments.spec.ts new file mode 100644 index 00000000..1230a4f7 --- /dev/null +++ b/tests/commands/configuration-management/list-assignments.spec.ts @@ -0,0 +1,66 @@ +import * as path from "path"; +import { mockedAxiosInstance } from "../../utls/http-requests-mock"; +import { VariableCommandService } from "../../../src/commands/configuration-management/variable-command.service"; +import { testContext } from "../../utls/test-context"; +import { loggingTestTransport, mockWriteFileSync } from "../../jest.setup"; +import { FileService } from "../../../src/core/utils/file-service"; + +describe("List assignments", () => { + + it("Should list assignments for supported type and non-json response", async () => { + const mockAssignmentValues = [ + {id: "id-1"}, + {id: "id-2"} + ]; + const resp = {data: mockAssignmentValues}; + (mockedAxiosInstance.get as jest.Mock).mockResolvedValue(resp); + + await new VariableCommandService(testContext).listAssignments("DATA_MODEL", false, ""); + + expect(loggingTestTransport.logMessages.length).toBe(2); + expect(loggingTestTransport.logMessages[0].message).toContain('{"id":"id-1"}'); + expect(loggingTestTransport.logMessages[1].message).toContain('{"id":"id-2"}'); + + expect(mockedAxiosInstance.get).toHaveBeenCalledWith("https://myTeam.celonis.cloud/package-manager/api/compute-pools/pools-with-data-models", expect.anything()) + }) + + it("Should export assignments for supported type and json response", async () => { + const mockAssignmentValues = [ + {id: "id-1"}, + {id: "id-2"} + ]; + const resp = {data: mockAssignmentValues}; + (mockedAxiosInstance.get as jest.Mock).mockResolvedValue(resp); + + await new VariableCommandService(testContext).listAssignments("DATA_MODEL", true, ""); + + expect(loggingTestTransport.logMessages.length).toBe(1); + expect(loggingTestTransport.logMessages[0].message).toContain(FileService.fileDownloadedMessage); + + const expectedFileName = loggingTestTransport.logMessages[0].message.split(FileService.fileDownloadedMessage)[1]; + + expect(mockWriteFileSync).toHaveBeenCalledWith(path.resolve(process.cwd(), expectedFileName), JSON.stringify(mockAssignmentValues), {encoding: "utf-8"}); + }) + + it("Should contain url params in the url", async () => { + const mockAssignmentValues = [{id: "id-1"}]; + const resp = {data: mockAssignmentValues}; + (mockedAxiosInstance.get as jest.Mock).mockResolvedValue(resp); + + await new VariableCommandService(testContext).listAssignments("CONNECTION", false, "param1=value1,param2=value2"); + + expect(mockedAxiosInstance.get).toHaveBeenCalledWith("https://myTeam.celonis.cloud/process-automation-v2/api/connections?param1=value1¶m2=value2", expect.anything()) + }) + + it("Should throw error for unsupported variable types", async () => { + const type: string = "DUMMY_UNSUPPORTED_TYPE"; + + try { + await new VariableCommandService(testContext).listAssignments(type, false, ""); + } catch (e) { + if (!(e.message === `Variable type ${type} not supported.`)) { + fail(); + } + } + }) +}) \ No newline at end of file diff --git a/tests/utls/config-utils.ts b/tests/utls/config-utils.ts new file mode 100644 index 00000000..74825257 --- /dev/null +++ b/tests/utls/config-utils.ts @@ -0,0 +1,107 @@ +import AdmZip = require("adm-zip"); +import { + DependencyTransport, NodeConfiguration, + NodeExportTransport, + PackageManifestTransport, StudioPackageManifest, +} from "../../src/commands/configuration-management/interfaces/package-export.interfaces"; +import { stringify } from "../../src/core/utils/json"; +import { SpaceTransport } from "../../src/commands/studio/interfaces/space.interface"; + +export class ConfigUtils { + + public static buildBatchExportZipWithStudioManifest(manifest: PackageManifestTransport[], studioManifest: StudioPackageManifest[], packageZips: AdmZip[]): AdmZip { + + const zipExport = new AdmZip(); + zipExport.addFile("manifest.json", Buffer.from(stringify(manifest))); + zipExport.addFile("studio.json", Buffer.from(stringify(studioManifest))); + packageZips.forEach(packageZip => { + const fileName = `${packageZip.getZipComment()}.zip` + packageZip.addZipComment("") + zipExport.addFile(fileName, packageZip.toBuffer()); + }) + + return zipExport; + } + public static buildBatchExportZip(manifest: PackageManifestTransport[], packageZips: AdmZip[]): AdmZip { + + const zipExport = new AdmZip(); + zipExport.addFile("manifest.json", Buffer.from(stringify(manifest))); + packageZips.forEach(packageZip => { + const fileName = `${packageZip.getZipComment()}.zip` + packageZip.addZipComment("") + zipExport.addFile(fileName, packageZip.toBuffer()); + }) + + return zipExport; + } + + public static buildExportPackageZip(packageNode: NodeExportTransport, childNodes: NodeExportTransport[], version: string): AdmZip { + const zipExport = new AdmZip(); + + zipExport.addFile("package.json", Buffer.from(stringify(packageNode))); + zipExport.addFile("nodes/", Buffer.alloc(0)); + + childNodes.forEach(child => { + zipExport.addFile(`nodes/${child.key}.json`, Buffer.from(stringify(child))); + }); + + zipExport.addZipComment(`${packageNode.key}_${version}`); + + return zipExport; + } + + public static buildManifestForKeyAndFlavor(key: string, flavor: string, dependenciesByVersion?: Map): PackageManifestTransport { + return { + packageKey: key, + flavor: flavor, + activeVersion: "", + dependenciesByVersion: dependenciesByVersion ?? {} as Map + }; + } + + public static buildPackageNode(key: string, configuration: NodeConfiguration): NodeExportTransport { + return { + key, + parentNodeKey: key, + name: "name", + type: "PACKAGE", + exportSerializationType: "YAML", + configuration: configuration, + schemaVersion: 1, + invalidContent: false, + serializedDocument: null, + spaceId: null + }; + } + + public static buildChildNode(key: string, parentKey: string, type: string): NodeExportTransport { + return { + key, + parentNodeKey: parentKey, + name: "name", + type: type, + exportSerializationType: "YAML", + configuration: {}, + schemaVersion: 1, + invalidContent: false, + serializedDocument: null, + spaceId: null + }; + } + + public static buildStudioManifestForKeyWithSpace(key: string, spaceName: string, spaceId: string): StudioPackageManifest { + const space: Partial = { + name: spaceName + }; + + if (spaceId) { + space.id = spaceId; + } + + return { + packageKey: key, + space: space, + runtimeVariableAssignments: [] + }; + } +} diff --git a/tests/utls/pacman-api.utils.ts b/tests/utls/pacman-api.utils.ts new file mode 100644 index 00000000..42ba8a26 --- /dev/null +++ b/tests/utls/pacman-api.utils.ts @@ -0,0 +1,43 @@ +import { + PackageExportTransport +} from "../../src/commands/configuration-management/interfaces/package-export.interfaces"; +import { ContentNodeTransport } from "../../src/commands/studio/interfaces/package-manager.interfaces"; +import { SpaceTransport } from "../../src/commands/studio/interfaces/space.interface"; + +export class PacmanApiUtils { + public static buildPackageExportTransport = (key: string, name: string): PackageExportTransport => { + return { + id: "", + key, + name, + changeDate: null, + activatedDraftId: "", + workingDraftId: "", + flavor: "", + version: "", + dependencies: null, + }; + } + + public static buildContentNodeTransport = (key: string, spaceId: string): ContentNodeTransport => { + return { + id: "", + key, + name: "", + rootNodeKey: "", + workingDraftId: "", + activatedDraftId: "", + rootNodeId: "", + assetMetadataTransport: null, + spaceId + } + } + + public static buildSpaceTransport = (id: string, name: string = "space-name", iconReference: string = "icon"): SpaceTransport => { + return { + id, + name, + iconReference, + }; + } +}