diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 1d39dc37..4bb97347 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -5,6 +5,8 @@ /src/core @celonis/studio-platform /src/commands/configuration-management/ @celonis/astro /tests/commands/configuration-management/ @celonis/astro +/src/commands/t2tc/ @celonis/studio-platform +/tests/commands/t2tc/ @celonis/studio-platform /src/commands/profile/ @celonis/studio-platform /src/commands/asset-registry/ @celonis/astro /tests/commands/asset-registry/ @celonis/astro diff --git a/docs/user-guide/agentic-development-guide.md b/docs/user-guide/agentic-development-guide.md index 59d80704..f01eb6af 100644 --- a/docs/user-guide/agentic-development-guide.md +++ b/docs/user-guide/agentic-development-guide.md @@ -87,10 +87,10 @@ Or validate during import with the `--validate` flag — note that this runs the content-cli config import -d --validate --overwrite ``` -To also run business-layer rules (PQL parsing, data-model availability, KPI uniqueness, …) and package-settings checks (dependencies, variables, and flavor-specific package settings), run `config validate` against the just-imported staging version: +To also run business-layer rules (PQL parsing, data-model availability, KPI uniqueness, …) and package-settings checks (dependencies, variables, and flavor-specific package settings), run `config package validate` against the just-imported staging version: ```bash -content-cli config validate --packageKey --layers SCHEMA BUSINESS PACKAGE_SETTINGS +content-cli config package validate --packageKey --layers SCHEMA BUSINESS PACKAGE_SETTINGS ``` If validation returns errors, fix the issues before importing. diff --git a/docs/user-guide/config-commands.md b/docs/user-guide/config-commands.md index 52d4efb6..7b0cd599 100644 --- a/docs/user-guide/config-commands.md +++ b/docs/user-guide/config-commands.md @@ -1,40 +1,32 @@ # Configuration Management Commands -The `config` command group manages package configurations. Most of its commands work with a package and its resources — importing a package, working with nodes, versions, or variables, and validating configurations. Separately, it includes a set of **batch commands** built for specific bulk use-cases, such as moving many packages at once between teams and realms. +The `config` command group manages a package and its resources — importing a single package, working with nodes, versions, or variables, reading metadata, and validating configurations. -The batch commands are **batch-specific**: they use their own archive format that only the batch commands understand, so their artifacts are **not interchangeable** with the package commands (see [Two command families](#two-command-families)). +> **Bulk Team-to-Team Copy commands have moved.** `config list`, `config export`, `config import`, and `config diff` are **deprecated** and now live under the [`t2tc package`](./t2tc-commands.md) command group. `config validate` is deprecated too and has moved to [`config package validate`](#validate-package-configurations-config-package-validate). The deprecated commands still work for now but print a deprecation notice — switch to the new commands at your earliest convenience. -- **Package commands** — work with a package and its resources: - - [`config package import`](#package-commands-config-package) — import a package +- **Package & resource commands** — work with a package and its contents: + - [`config package import`](#package-commands-config-package) — import a single package + - [`config package validate`](#validate-package-configurations-config-package-validate) — validate a package's staging version + - [`config package list`](#list-packages-config-package-list) — list packages (staging by default) - [`config nodes …`](#finding-nodes) — work with individual nodes - [`config versions …`](#package-version) — read and create package versions - [`config variables list`](#listing--mapping-variables) — read package variables - - [`config validate`](#validate-package-configurations) — validate a package's staging version -- **Batch commands** — bulk multi-package transport for specific use-cases: - - [`config list`](#list-packages), [`config export`](#batch-export-packages), [`config import`](#batch-import-packages), [`config diff`](#diff-local-zip-with-deployed-versionspecific-versionstaging), [`config metadata export`](#batch-export-packages) + - `config metadata export` — check whether packages have unpublished changes +- **Team-to-Team Copy (batch) commands** — bulk multi-package transport, now under [`t2tc package`](./t2tc-commands.md): + - [`t2tc package list`](./t2tc-commands.md#list-packages), [`t2tc package export`](./t2tc-commands.md#export-packages), [`t2tc package import`](./t2tc-commands.md#import-packages), [`t2tc package diff`](./t2tc-commands.md#diff-local-zip-with-deployed-versionspecific-versionstaging) -## Two command families +## Package vs. batch artifact format -The batch commands are a **self-contained, batch-specific set**. `config export` produces a multi-package **batch artifact** — a top-level `manifest.json`, `variables.json`, `studio.json`, and one nested `_.zip` per package — that is produced and consumed **only** by other batch commands. `config package import`, by contrast, works with a plain **package zip** (a `package.json`, an optional `variables.json`, and a `nodes/` folder). The two formats are **not interchangeable**: +`config package import` works with a plain **package zip** (a `package.json`, an optional `variables.json`, and a `nodes/` folder). The Team-to-Team Copy commands instead use a multi-package **batch artifact** (a top-level `manifest.json`, `variables.json`, `studio.json`, and a nested `_.zip` per package). The two formats are **not interchangeable**: -- An archive from `config export` can be imported with `config import` or inspected with `config diff` — but **not** with `config package import`. -- A package zip used by `config package import` **cannot** be imported with `config import` or diffed with `config diff`. +- An archive from `t2tc package export` can be imported with `t2tc package import` or inspected with `t2tc package diff` — but **not** with `config package import`. +- A package zip used by `config package import` **cannot** be imported with `t2tc package import` or diffed with `t2tc package diff`. -| Command | Group | Artifact it reads / writes | -|---|---|---| -| `config package import` | Package commands | Package zip/dir (`package.json`, optional `variables.json`, `nodes/`) | -| `config nodes …` | Package commands | Node JSON payload | -| `config validate`, `config versions …`, `config variables list` | Package commands | — (operate directly on the platform) | -| `config export` | Batch commands | Batch archive (multi-package) | -| `config import` | Batch commands | Batch archive (multi-package) | -| `config diff` | Batch commands | Batch archive (multi-package) | -| `config list`, `config metadata export` | Batch commands | — (JSON list output) | - -**Which should I use?** Reach for the batch commands only for their specific bulk use-cases — moving a set of packages together (for example, a migration between teams). For everything else, use the package commands. +For the full Team-to-Team Copy reference, see [T2TC Commands](./t2tc-commands.md). ## Permissions -All `config` commands run against the Pacman API and are subject to the same permission checks the platform applies in the UI. The required permission depends on the **flavor** of the target package: +All `config` commands run against the Pacman API and are subject to the same permission checks the platform applies in the UI. The same permissions apply to the [`t2tc package`](./t2tc-commands.md) commands. The required permission depends on the **flavor** of the target package: | Package flavor | Required permission | |---|---| @@ -47,181 +39,68 @@ This applies to every command that reads or modifies a single package or its nod - `config nodes create`, `config nodes update`, `config nodes archive` - `config variables list` - `config versions get`, `config versions create` -- `config validate`, `config diff` -- `config export`, `config import`, `config package import`, `config metadata export` +- `config package validate`, `config package import`, `config metadata export` +- `t2tc package export`, `t2tc package import`, `t2tc package diff` If the authenticated profile does not have the required permission, the command fails with `Access is Denied`. -`config list` is the one exception: instead of failing, it **filters out packages the profile does not have permission to access**. If a package you expect to see is missing from the list, the most likely cause is missing edit permission on the package (Studio) or on its connected data pool (OCDM). +The listing commands (`config package list` and `t2tc package list`) are the one exception: instead of failing, they **filter out packages the profile does not have permission to access**. If a package you expect to see is missing from the list, the most likely cause is missing edit permission on the package (Studio) or on its connected data pool (OCDM). -## List Packages +## List Packages (`config package list`) -> **Batch command.** Part of the [batch family](#two-command-families) — a bulk listing utility typically used to discover packages before a `config export`. +> The deprecated `config list` command has moved to [`t2tc package list`](./t2tc-commands.md#list-packages) (bulk listing for the Team-to-Team Copy workflow, with `--withDependencies` / `--packageKeys` / `--keysByVersion` filtering). For day-to-day listing, use `config package list` below. `config list` still works but prints a deprecation notice. -Packages can be listed using the following command: +`config package list` lists packages in the target team. **By default it lists staging packages.** ```bash -content-cli config list -p +content-cli config package list -p ``` -The result will be printed in the console containing only the package name and key: +The result is printed in the console containing the package name and key: ```bash info: Package1 - Key: "package-1" ``` -By using the `--json` option, packages can be exported (saved) in an extended form as a json file in the current working directory. +Use `--json` to export the list as a JSON file in the current working directory: ```bash -content-cli config list -p --json +content-cli config package list -p --json ``` -The name of the file will be printed in the console with the following format: - ```bash info: File downloaded successfully. New filename: 9560f81f-f746-4117-83ee-dd1f614ad624.json ``` -By using the `--flavors` option, you can filter which packages to list. The available flavors are: **STUDIO** and **OCDM**. - -To list staging packages instead of deployed packages use the `--staging` option. Please note that this flag is not compatible with the below options. - -### List Packages with Dependencies - -When using the listing command with the `--json` option, two additional options are available: - -- **--withDependencies**: This option will include the dependencies of the packages in the output. - -```bash -content-cli config list -p --withDependencies -``` - -- **--packageKeys**: This option allows you to filter the packages by their keys. You can specify multiple package keys separated by spaces. - -```bash -content-cli config list -p --packageKeys key1 ... keyN -[optional] –-withDependencies -``` - -## Batch Export Packages - -> **Batch command.** Part of the [batch family](#two-command-families). It produces a multi-package **batch artifact** that can only be re-imported with [`config import`](#batch-import-packages) or inspected with [`config diff`](#diff-local-zip-with-deployed-versionspecific-versionstaging) — **not** with `config package import`. To work with one package, use [`config package import`](#package-commands-config-package). - -Packages can be exported using the following command: - -```bash -content-cli config export -p --packageKeys key1 ... keyN -``` - -The `--keysByVersion` option can be used to export packages by specific version. You can specify multiple packages with version separated by spaces, in the format of 'packageKey.version'. -The `--withDependencies` option can be used to also export dependencies of the given packages. -The `--unzip` option can be used to unzip the exported packages into the current working directory. - -Depending on the `--unzip` option used, a zip file, or a directory containing the exported packages, will be created in the current working directory containing: - -```bash -exported_package_random_uuid/ -├─ manifest.json -├─ variable.json -├─ studio.json -├─ package_key1-version.zip -├─ ... -├─ package_keyN-version.zip -``` - -### Git Integration for Export - -The following **Git options** are available: - -- `--gitProfile ` – specifies the Git profile to use for exporting directly to a repository. - If not specified, the default profile will be used. -- `--gitBranch ` – specifies the branch in the Github repository where the export will be pushed. - -Example exporting to Git: - -```bash -content-cli config export -p --packageKeys key1 key2 --gitProfile myGitProfile --gitBranch feature-branch -``` - -### Export Directory Structure - -- manifest.json - File which contains the metadata of the exported packages. -- studio.json - File which contains the metadata of the exported packages in a format compatible with Studio. -- variables.json - File which contains the variables of the exported packages. -- exported packages directories - Directories containing the exported package files, each directory is named after the package key and the version. - -Inside each exported package directory, the following files will be present: - -- package.json - File which contains the configuration of the exported package. -- nodes/ - Directory containing the nodes of the exported package. - -Inside the nodes directory, a file for each node will be present: - -- node_key.json - File which contains the configuration of the exported node. - -## Batch Import Packages - -> **Batch command.** Part of the [batch family](#two-command-families). It expects a multi-package **batch artifact** produced by [`config export`](#batch-export-packages). To import one package from a package zip, use [`config package import`](#package-commands-config-package) instead. - -Packages can be imported using the following commands, if importing from a zip file: - -```bash -content-cli config import -p -f -``` - -Where `-f` is the short hand operation for `--file`. -If importing from a directory containing the exported packages, the following command can be used: +Use `--flavors` to filter which packages to list. The available flavors are **STUDIO** and **OCDM**: ```bash -content-cli config import -p -d +content-cli config package list -p --flavors STUDIO ``` -Where `-d` is the shorthand operation for `--directory`. -When packages with the same keys exist in the target team, the --overwrite option can be used for allowing overwriting of those packages. If the package in target environment contains unpublished changes, they are automatically saved under a new version. This allows you to audit, compare, or roll back to your previous state via the version history if needed. +> Need active (deployed) versions, dependency expansion, or `--packageKeys` / `--keysByVersion` filtering? Those belong to the bulk copy workflow — use [`t2tc package list`](./t2tc-commands.md#list-packages). -```bash -content-cli config import -p -f --overwrite -``` +## Bulk Export / Import / Diff (moved to `t2tc package`) -### Git Integration for Import - -The following **Git options** are available: - -- `--gitProfile ` – specifies the Git profile to use for importing directly from a repository. - If not specified, the default profile will be used. -- `--gitBranch ` – specifies the branch in the Github repository from which to import. - -Example importing from Git: - -```bash -content-cli config import -p --gitProfile myGitProfile --gitBranch feature-branch -``` - -Finally, the result of this command will be a list of PostPackageImportData exported as a json file. The file name will be printed with the following message format: - -```bash -info: Config import report file: 9560f81f-f746-4117-83ee-dd1f614ad624.json -``` - -### Validate During Import - -Add `--validate` to `config import` to run validation against each node **before** the import is committed: - -```bash -content-cli config import -p -d --validate --overwrite -``` - -`config import --validate` runs the **SCHEMA** layer only. It does **not** run BUSINESS-layer checks (PQL parsing, data-model availability, KPI uniqueness, etc.) or PACKAGE_SETTINGS checks (package dependencies, variables, and flavor-specific package settings). To run those validations, use [`config validate`](#validate-package-configurations) after the import. +> **Deprecated and moved.** The bulk multi-package commands have moved to the [`t2tc package`](./t2tc-commands.md) group: +> +> | Old (deprecated) | New | +> |---|---| +> | `config export` | [`t2tc package export`](./t2tc-commands.md#export-packages) | +> | `config import` | [`t2tc package import`](./t2tc-commands.md#import-packages) | +> | `config diff` | [`t2tc package diff`](./t2tc-commands.md#diff-local-zip-with-deployed-versionspecific-versionstaging) | +> +> The flags and behaviour are unchanged — only the command path moved. The old commands still work but print a deprecation notice. See [T2TC Commands](./t2tc-commands.md) for the full reference (batch artifact format, Git integration, validation during import, and more). To import a **single** package from a package zip, use [`config package import`](#package-commands-config-package). ## Package Commands (`config package`) -The `config package` command group works with a package and its contents. It is **not** part of the batch-specific set — it uses the plain package format described below, which is not interchangeable with the batch artifact (see [Two command families](#two-command-families)). +The `config package` command group works with a package and its contents. It uses the plain package format described below, which is not interchangeable with the batch artifact used by the Team-to-Team Copy commands (see [Package vs. batch artifact format](#package-vs-batch-artifact-format)). ### Import a Package -`config package import` imports a package from a package zip (or directory). Unlike [`config import`](#batch-import-packages) — which performs a **batch** import and expects the multi-package batch artifact (`manifest.json`, a top-level `variables.json`, `studio.json`, and a nested `_.zip` per package) — `config package import` takes a plain, flat package layout and imports it on its own. +`config package import` imports a package from a package zip (or directory). Unlike [`t2tc package import`](./t2tc-commands.md#import-packages) — which performs a **batch** import and expects the multi-package batch artifact (`manifest.json`, a top-level `variables.json`, `studio.json`, and a nested `_.zip` per package) — `config package import` takes a plain, flat package layout and imports it on its own. -> A zip produced by `config export` is a **batch artifact** and cannot be imported with `config package import`. Likewise, a package zip cannot be imported with `config import`. Use the command that matches how the artifact was produced. +> A zip produced by `t2tc package export` is a **batch artifact** and cannot be imported with `config package import`. Likewise, a package zip cannot be imported with `t2tc package import`. Use the command that matches how the artifact was produced. ```bash content-cli config package import -p -f @@ -288,14 +167,16 @@ content-cli config package import -p -f --json info: File downloaded successfully. New filename: 9560f81f-f746-4117-83ee-dd1f614ad624.json ``` -## Validate Package Configurations +## Validate Package Configurations (`config package validate`) -The `config validate` command validates the **staging (draft) version** of a package by sending its nodes through one or more validation layers. The command runs against the Pacman validate API and returns a structured report of errors, warnings, and info findings. +> **Renamed.** This command moved from `config validate` to `config package validate`. The old `config validate` still works but prints a deprecation notice; switch to `config package validate`. + +The `config package validate` command validates the **staging (draft) version** of a package by sending its nodes through one or more validation layers. The command runs against the Pacman validate API and returns a structured report of errors, warnings, and info findings. This command requires **edit permission** on the target package (see [Permissions](#permissions)). ```bash -content-cli config validate --packageKey +content-cli config package validate --packageKey ``` By default, only the `SCHEMA` layer is run. The console output looks like: @@ -329,7 +210,7 @@ Currently `SCHEMA`, `BUSINESS`, and `PACKAGE_SETTINGS` are the layers accepted b To run all layers: ```bash -content-cli config validate --packageKey --layers SCHEMA BUSINESS PACKAGE_SETTINGS +content-cli config package validate --packageKey --layers SCHEMA BUSINESS PACKAGE_SETTINGS ``` Use `PACKAGE_SETTINGS` when you need to verify that the package's own settings are usable in the destination team before continuing authoring or import work. It reports issues such as missing dependency versions, duplicate dependency or variable keys, blank variable keys/types, missing Studio data model assignments, and OCDM package-settings problems when the corresponding backend validation is enabled. @@ -339,7 +220,7 @@ Use `PACKAGE_SETTINGS` when you need to verify that the package's own settings a By default, every node in the package's staging version is validated. To restrict the scope to a subset of nodes, use `--nodeKeys`: ```bash -content-cli config validate --packageKey --nodeKeys node-key-1 node-key-2 +content-cli config package validate --packageKey --nodeKeys node-key-1 node-key-2 ``` ### Export Validation Report as JSON @@ -347,7 +228,7 @@ content-cli config validate --packageKey --nodeKeys node-key-1 node Use `--json` to write the full validation report to a JSON file in the current working directory instead of printing it to the console: ```bash -content-cli config validate --packageKey --layers SCHEMA BUSINESS PACKAGE_SETTINGS --json +content-cli config package validate --packageKey --layers SCHEMA BUSINESS PACKAGE_SETTINGS --json ``` The filename is printed on success: @@ -885,7 +766,7 @@ To diff a local node JSON file (the compare side) against a version of the node content-cli config nodes diff --packageKey --nodeKey --baseVersion --file ``` -The file must follow the `NodeExportTransport` shape — the format produced by `config export --unzip` under `_/nodes/.json`. `--baseVersion` accepts either `STAGING` or a specific package version. `--file` and `--compareVersion` are mutually exclusive; exactly one must be provided. +The file must follow the `NodeExportTransport` shape — the format produced by `t2tc package export --unzip` under `_/nodes/.json`. `--baseVersion` accepts either `STAGING` or a specific package version. `--file` and `--compareVersion` are mutually exclusive; exactly one must be provided. If no node with the given key exists for the resolved base version, the file is diffed against an empty configuration `{}`. @@ -943,23 +824,4 @@ content-cli config nodes dependencies list --packageKey --nodeKey < ## Diff local zip with deployed version/specific version/staging -> **Batch command.** Part of the [batch family](#two-command-families). It expects a multi-package **batch artifact** produced by [`config export`](#batch-export-packages); a package zip is not supported here. - -To compare local zipped packages with online packages use: -```bash -content-cli config diff --file -``` - -As with other commands, use `--json` to export the diff to a file. -To diff against a specific version use the `--baseVersion` parameter. When omitted it will diff against the current deployed version. -To diff against staging use `--baseVersion STAGING`. - -```bash -content-cli config diff --file --baseVersion -``` - -To diff against the current deployed version and only return whether there are any changes, use the `--hasChanges` flag. - -```bash -content-cli config diff --file --hasChanges -``` \ No newline at end of file +> **Deprecated and moved.** `config diff` has moved to [`t2tc package diff`](./t2tc-commands.md#diff-local-zip-with-deployed-versionspecific-versionstaging). The flags and behaviour are unchanged — only the command path moved. The old command still works but prints a deprecation notice. See [T2TC Commands](./t2tc-commands.md#diff-local-zip-with-deployed-versionspecific-versionstaging) for the full reference. \ No newline at end of file diff --git a/docs/user-guide/t2tc-commands.md b/docs/user-guide/t2tc-commands.md new file mode 100644 index 00000000..115e4fe5 --- /dev/null +++ b/docs/user-guide/t2tc-commands.md @@ -0,0 +1,211 @@ +# Team-to-Team Copy (T2TC) Commands + +The `t2tc package` command group moves whole packages — and, optionally, their dependencies and variables — between teams and realms. These commands power the **Team-to-Team Copy** feature: the T2TC backend drives the copy by exporting from a source team and importing into a target team through this CLI. + +These commands are a **self-contained, batch-specific set**. `t2tc package export` produces a multi-package **batch archive**, and only the other `t2tc package` commands (`import`, `diff`) understand that archive. It is **not** interchangeable with the package format used by [`config package import`](./config-commands.md#package-commands-config-package) (see [Batch archive vs. package format](#batch-archive-vs-package-format)). + +> **Migrating from `config`?** `t2tc package list/export/import/diff` replace the deprecated `config list/export/import/diff` commands. The flags and behaviour are identical — only the command path changed. + +| Old (deprecated) | New | +|---|---| +| `config list` | `t2tc package list` | +| `config export` | `t2tc package export` | +| `config import` | `t2tc package import` | +| `config diff` | `t2tc package diff` | + +## Permissions + +`t2tc package` commands run against the Pacman API and are subject to the same permission checks the platform applies in the UI. The required permission depends on the **flavor** of the target package: + +| Package flavor | Required permission | +|---|---| +| **Studio** (Studio packages and their assets) | **Edit package** permission on the package | +| **OCDM** (OCDM packages and their assets) | **Edit** (admin) permission on the **data pool** the OCDM package is connected to | + +`t2tc package list` is the one exception: instead of failing, it **filters out packages the profile does not have permission to access**. If a package you expect to see is missing from the list, the most likely cause is missing edit permission on the package (Studio) or on its connected data pool (OCDM). + +## Batch archive vs. package format + +`t2tc package export` produces a multi-package **batch archive** — a top-level `manifest.json`, `variables.json`, `studio.json`, and one nested `_.zip` per package — that is produced and consumed **only** by the `t2tc package` commands. [`config package import`](./config-commands.md#package-commands-config-package), by contrast, works with a plain **package zip** (a `package.json`, an optional `variables.json`, and a `nodes/` folder). The two formats are **not interchangeable**: + +- An archive from `t2tc package export` can be imported with `t2tc package import` or inspected with `t2tc package diff` — but **not** with `config package import`. +- A package zip used by `config package import` **cannot** be imported with `t2tc package import` or diffed with `t2tc package diff`. + +Reach for `t2tc package` only for its specific bulk use-case — moving a set of packages together (for example, a migration between teams). To work with a single package, use [`config package import`](./config-commands.md#package-commands-config-package). + +## List Packages + +Packages can be listed using the following command: + +```bash +content-cli t2tc package list -p +``` + +The result will be printed in the console containing only the package name and key: + +```bash +info: Package1 - Key: "package-1" +``` + +By using the `--json` option, packages can be exported (saved) in an extended form as a json file in the current working directory. + +```bash +content-cli t2tc package list -p --json +``` + +The name of the file will be printed in the console with the following format: + +```bash +info: File downloaded successfully. New filename: 9560f81f-f746-4117-83ee-dd1f614ad624.json +``` + +By using the `--flavors` option, you can filter which packages to list. The available flavors are: **STUDIO** and **OCDM**. + +To list staging packages instead of deployed packages use the `--staging` option. Please note that this flag is not compatible with the below options. + +> For day-to-day staging listing outside the copy workflow, prefer [`config package list`](./config-commands.md#list-packages-config-package-list), which lists staging packages by default. + +### List Packages with Dependencies + +When using the listing command with the `--json` option, two additional options are available: + +- **--withDependencies**: This option will include the dependencies of the packages in the output. + +```bash +content-cli t2tc package list -p --withDependencies +``` + +- **--packageKeys**: This option allows you to filter the packages by their keys. You can specify multiple package keys separated by spaces. + +```bash +content-cli t2tc package list -p --packageKeys key1 ... keyN +[optional] --withDependencies +``` + +## Export Packages + +Packages can be exported using the following command: + +```bash +content-cli t2tc package export -p --packageKeys key1 ... keyN +``` + +The `--keysByVersion` option can be used to export packages by specific version. You can specify multiple packages with version separated by spaces, in the format of 'packageKey.version'. +The `--withDependencies` option can be used to also export dependencies of the given packages. +The `--unzip` option can be used to unzip the exported packages into the current working directory. + +Depending on the `--unzip` option used, a zip file, or a directory containing the exported packages, will be created in the current working directory containing: + +```bash +exported_package_random_uuid/ +├─ manifest.json +├─ variable.json +├─ studio.json +├─ package_key1-version.zip +├─ ... +├─ package_keyN-version.zip +``` + +### Git Integration for Export + +The following **Git options** are available: + +- `--gitProfile ` – specifies the Git profile to use for exporting directly to a repository. + If not specified, the default profile will be used. +- `--gitBranch ` – specifies the branch in the Github repository where the export will be pushed. + +Example exporting to Git: + +```bash +content-cli t2tc package export -p --packageKeys key1 key2 --gitProfile myGitProfile --gitBranch feature-branch +``` + +### Export Directory Structure + +- manifest.json - File which contains the metadata of the exported packages. +- studio.json - File which contains the metadata of the exported packages in a format compatible with Studio. +- variables.json - File which contains the variables of the exported packages. +- exported packages directories - Directories containing the exported package files, each directory is named after the package key and the version. + +Inside each exported package directory, the following files will be present: + +- package.json - File which contains the configuration of the exported package. +- nodes/ - Directory containing the nodes of the exported package. + +Inside the nodes directory, a file for each node will be present: + +- node_key.json - File which contains the configuration of the exported node. + +## Import Packages + +Packages can be imported using the following commands, if importing from a zip file: + +```bash +content-cli t2tc package import -p -f +``` + +Where `-f` is the short hand operation for `--file`. +If importing from a directory containing the exported packages, the following command can be used: + +```bash +content-cli t2tc package import -p -d +``` + +Where `-d` is the shorthand operation for `--directory`. +When packages with the same keys exist in the target team, the `--overwrite` option can be used for allowing overwriting of those packages. If the package in target environment contains unpublished changes, they are automatically saved under a new version. This allows you to audit, compare, or roll back to your previous state via the version history if needed. + +```bash +content-cli t2tc package import -p -f --overwrite +``` + +### Git Integration for Import + +The following **Git options** are available: + +- `--gitProfile ` – specifies the Git profile to use for importing directly from a repository. + If not specified, the default profile will be used. +- `--gitBranch ` – specifies the branch in the Github repository from which to import. + +Example importing from Git: + +```bash +content-cli t2tc package import -p --gitProfile myGitProfile --gitBranch feature-branch +``` + +Finally, the result of this command will be a list of PostPackageImportData exported as a json file. The file name will be printed with the following message format: + +```bash +info: Config import report file: 9560f81f-f746-4117-83ee-dd1f614ad624.json +``` + +### Validate During Import + +Add `--validate` to `t2tc package import` to run validation against each node **before** the import is committed: + +```bash +content-cli t2tc package import -p -d --validate --overwrite +``` + +`t2tc package import --validate` runs the **SCHEMA** layer only. It does **not** run BUSINESS-layer checks (PQL parsing, data-model availability, KPI uniqueness, etc.) or PACKAGE_SETTINGS checks (package dependencies, variables, and flavor-specific package settings). To run those validations, use [`config package validate`](./config-commands.md#validate-package-configurations-config-package-validate) after the import. + +## Diff local zip with deployed version/specific version/staging + +To compare local zipped packages with online packages use: + +```bash +content-cli t2tc package diff --file +``` + +As with other commands, use `--json` to export the diff to a file. +To diff against a specific version use the `--baseVersion` parameter. When omitted it will diff against the current deployed version. +To diff against staging use `--baseVersion STAGING`. + +```bash +content-cli t2tc package diff --file --baseVersion +``` + +To diff against the current deployed version and only return whether there are any changes, use the `--hasChanges` flag. + +```bash +content-cli t2tc package diff --file --hasChanges +``` diff --git a/mkdocs.yaml b/mkdocs.yaml index 15cf1760..f0b07a7f 100644 --- a/mkdocs.yaml +++ b/mkdocs.yaml @@ -13,6 +13,7 @@ nav: - Overview: './user-guide/index.md' - Studio Commands: './user-guide/studio-commands.md' - Config Commands: './user-guide/config-commands.md' + - T2TC Commands: './user-guide/t2tc-commands.md' - Deployment Commands: './user-guide/deployment-commands.md' - Asset Registry Commands: './user-guide/asset-registry-commands.md' - Data Pool Commands: './user-guide/data-pool-commands.md' diff --git a/src/commands/configuration-management/api/metadata-api.ts b/src/commands/configuration-management/api/metadata-api.ts new file mode 100644 index 00000000..9c842b9f --- /dev/null +++ b/src/commands/configuration-management/api/metadata-api.ts @@ -0,0 +1,22 @@ +import { PackageMetadataExportTransport } 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 MetadataApi { + + private readonly httpClient: () => HttpClient; + + constructor(context: Context) { + this.httpClient = () => context.httpClient; + } + + public async exportPackagesMetadata(packageKeys: string[]): Promise { + const queryParams = new URLSearchParams(); + packageKeys.forEach(packageKey => queryParams.append("packageKeys", packageKey)); + + return this.httpClient().get(`/package-manager/api/core/packages/metadata/export?${queryParams.toString()}`).catch(e => { + throw new FatalError(`Problem exporting packages metadata: ${e}`); + }) + } +} diff --git a/src/commands/configuration-management/api/staging-package-api.ts b/src/commands/configuration-management/api/staging-package-api.ts new file mode 100644 index 00000000..2bb7d8c4 --- /dev/null +++ b/src/commands/configuration-management/api/staging-package-api.ts @@ -0,0 +1,24 @@ +import { PackageExportTransport } 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 StagingPackageApi { + + private readonly httpClient: () => HttpClient; + + constructor(context: Context) { + this.httpClient = () => context.httpClient; + } + + public async findAllStagingPackages(flavors: string[], includeBranches: boolean = false): Promise { + const queryParams = new URLSearchParams(); + + queryParams.set("includeBranches", includeBranches.toString()); + flavors.forEach(flavor => queryParams.append("flavors", flavor)); + + return this.httpClient().get(`/pacman/api/core/staging/packages/export/list?${queryParams.toString()}`).catch(e => { + throw new FatalError(`Problem getting staging packages: ${e}`); + }); + } +} diff --git a/src/commands/configuration-management/api/variable-api.ts b/src/commands/configuration-management/api/variable-api.ts new file mode 100644 index 00000000..2b730632 --- /dev/null +++ b/src/commands/configuration-management/api/variable-api.ts @@ -0,0 +1,19 @@ +import { PackageKeyAndVersionPair, 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 VariableApi { + + private readonly httpClient: () => HttpClient; + + constructor(context: Context) { + this.httpClient = () => context.httpClient; + } + + 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/config-command.service.ts b/src/commands/configuration-management/config-command.service.ts index 510d44cd..30178581 100644 --- a/src/commands/configuration-management/config-command.service.ts +++ b/src/commands/configuration-management/config-command.service.ts @@ -1,42 +1,12 @@ import { Context } from "../../core/command/cli-context"; -import { BatchImportExportService } from "./batch-import-export.service"; import { VariableService } from "./variable.service"; -import { DiffService } from "./diff.service"; -import { fileService } from "../../core/utils/file-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 listPackages( - jsonResponse: boolean, - flavors: string[], - withDependencies: boolean, - packageKeys: string[], - keysByVersion: string[], - variableValue: string, - variableType: string, - includeBranches: boolean, - staging: boolean): Promise { - if (staging) { - await this.batchImportExportService.listStagingPackages(flavors ?? [], includeBranches, jsonResponse); - } else if (variableValue) { - await this.listPackagesByVariableValue(jsonResponse, flavors, variableValue, variableType, includeBranches); - } else if (jsonResponse) { - await this.batchImportExportService.findAndExportListOfPackages(flavors ?? [], packageKeys ?? [], keysByVersion ?? [], withDependencies, includeBranches); - } else if (keysByVersion) { - await this.batchImportExportService.listPackagesByKeysWithVersion(keysByVersion, withDependencies); - } else { - await this.batchImportExportService.listActivePackages(flavors ?? [], includeBranches); - } } public async listVariables( @@ -57,44 +27,4 @@ export class ConfigCommandService { await this.variableService.listVariables(keysByVersion, keysByVersionFile); } } - - public batchExportPackages(packageKeys: string[], packageKeysByVersion: string[], withDependencies: boolean, gitBranch: string, unzip: boolean): Promise { - return this.batchImportExportService.batchExportPackages(packageKeys, packageKeysByVersion, withDependencies, gitBranch, unzip); - } - - public batchExportPackagesMetadata(packageKeys: string[], jsonResponse: boolean): Promise { - return this.batchImportExportService.batchExportPackagesMetadata(packageKeys, jsonResponse); - } - - public batchImportPackages(file: string, directory: string, overwrite: boolean, gitBranch: string, performValidation: boolean = false): Promise { - if ((directory || file) && gitBranch) { - throw new Error("You cannot use both file/directory and gitBranch options at the same time. Only one import source can be defined."); - } - if (!directory && !file && !gitBranch) { - throw new Error("You must provide either a file/directory or a gitBranch option to import packages."); - } - if (file && directory) { - throw new Error("You cannot use both file and directory options at the same time. Only one import source can be defined."); - } - if (file && fileService.isDirectory(file)) { - throw new Error("The file option accepts only zip files."); - } - if (directory && !fileService.isDirectory(directory)) { - throw new Error("The directory option accepts only directories."); - } - const sourcePath = file ?? directory; - return this.batchImportExportService.batchImportPackages(sourcePath, overwrite, gitBranch, performValidation); - } - - public diffPackages(file: string, hasChanges: boolean, baseVersion: string, jsonResponse: boolean): Promise { - return this.diffService.diffPackages(file, hasChanges, baseVersion, jsonResponse); - } - - private async listPackagesByVariableValue(jsonResponse: boolean, flavors: string[], variableValue: string, variableType: string, includeBranches: boolean): Promise { - if (jsonResponse) { - await this.batchImportExportService.findAndExportListOfActivePackagesByVariableValue(flavors ?? [], variableValue, variableType, includeBranches) - } else { - await this.batchImportExportService.listActivePackagesByVariableValue(flavors ?? [], variableValue, variableType, includeBranches); - } - } } diff --git a/src/commands/configuration-management/connection-variable.helper.ts b/src/commands/configuration-management/connection-variable.helper.ts new file mode 100644 index 00000000..3f1708b2 --- /dev/null +++ b/src/commands/configuration-management/connection-variable.helper.ts @@ -0,0 +1,29 @@ +import { PackageManagerVariableType } from "../studio/interfaces/package-manager.interfaces"; +import { VariableExportTransport, VariableManifestTransport } from "./interfaces/package-export.interfaces"; + +export function fixConnectionVariables(variables: VariableManifestTransport[]): VariableManifestTransport[] { + return variables.map(variableManifest => ({ + ...variableManifest, + variables: variableManifest.variables.map(variable => { + if (variable.type !== PackageManagerVariableType.CONNECTION) { + return variable; + } + + return fixConnectionVariable(variable); + }) + })); +} + +function fixConnectionVariable(variable: VariableExportTransport): VariableExportTransport { + if (!variable.value?.appName) { + return variable; + } + + return { + ...variable, + metadata: { + ...variable.metadata, + appName: variable.value.appName + } + }; +} diff --git a/src/commands/configuration-management/metadata.service.ts b/src/commands/configuration-management/metadata.service.ts new file mode 100644 index 00000000..3d098f75 --- /dev/null +++ b/src/commands/configuration-management/metadata.service.ts @@ -0,0 +1,33 @@ +import { v4 as uuidv4 } from "uuid"; +import { Context } from "../../core/command/cli-context"; +import { PackageMetadataExportTransport } from "./interfaces/package-export.interfaces"; +import { fileService, FileService } from "../../core/utils/file-service"; +import { logger } from "../../core/utils/logger"; +import { MetadataApi } from "./api/metadata-api"; + +export class MetadataService { + + private metadataApi: MetadataApi; + + constructor(context: Context) { + this.metadataApi = new MetadataApi(context); + } + + public async exportPackagesMetadata(packageKeys: string[], jsonResponse: boolean): Promise { + const exportedPackagesMetadata: PackageMetadataExportTransport[] = await this.metadataApi.exportPackagesMetadata(packageKeys); + + if (jsonResponse) { + this.exportListOfPackagesMetadata(exportedPackagesMetadata); + } else { + exportedPackagesMetadata.forEach(pkg => { + logger.info(`${pkg.key} - Has Unpublished Changes: ${pkg.hasUnpublishedChanges}`); + }); + } + } + + private exportListOfPackagesMetadata(packagesMetadata: PackageMetadataExportTransport[]): void { + const filename = uuidv4() + ".json"; + fileService.writeToFileWithGivenName(JSON.stringify(packagesMetadata), filename); + logger.info(FileService.fileDownloadedMessage + filename); + } +} diff --git a/src/commands/configuration-management/module.ts b/src/commands/configuration-management/module.ts index fd70a6cd..2d599b29 100644 --- a/src/commands/configuration-management/module.ts +++ b/src/commands/configuration-management/module.ts @@ -6,6 +6,9 @@ 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 { StagingPackageService } from "./staging-package.service"; +import { MetadataService } from "./metadata.service"; +import { T2tcCommandService } from "../t2tc/t2tc-command.service"; import { VariableCommandService } from "./variable-command.service"; import { NodeService } from "./node.service"; import { NodeDiffService } from "./node-diff.service"; @@ -18,9 +21,10 @@ class Module extends IModule { public register(context: Context, configurator: Configurator): void { const configCommand = configurator.command("config") - .description("Manage package configurations. Most commands work with a package and its resources. The batch commands (list, export, import, diff, metadata) are a separate, batch-specific set for bulk multi-package transport: their archive format is only understood by other batch commands and is not interchangeable with the package commands."); + .description("Manage package configurations and their resources (package, nodes, versions, variables, metadata). Note: 'config list/export/import/diff' are deprecated — bulk Team-to-Team Copy operations have moved to the 't2tc package' group, and single-package operations live under 'config package'."); configCommand.command("list") - .description("[Batch] List packages in the target team. Part of the batch export/import workflow.") + .description("[Deprecated] Use 't2tc package list' instead. List packages in the target team.") + .deprecationNotice("'config list' is deprecated and will be removed in a future release. Use 't2tc package list' instead.") .option("--json", "Return response as json type", "") .option("--flavors ", "Lists only packages of the given flavors") .option("--withDependencies", "Include dependencies", "") @@ -33,7 +37,8 @@ class Module extends IModule { .action(this.listPackages); configCommand.command("export") - .description("[Batch] Export one or more packages into a batch archive. The archive only works with the batch commands ('config import' / 'config diff') and cannot be imported with 'config package import'.") + .description("[Deprecated] Use 't2tc package export' instead. Export one or more packages into a Team-to-Team Copy archive.") + .deprecationNotice("'config export' is deprecated and will be removed in a future release. Use 't2tc package export' instead.") .option("--packageKeys ", "Keys of packages to export. Exports the latest deployed version only") .option("--keysByVersion ", "Keys of packages to export by version") .option("--withDependencies", "Include variables and dependencies", "") @@ -47,13 +52,14 @@ class Module extends IModule { metadataCommand .command("export") - .description("[Batch] Show whether multiple packages have unpublished changes (bulk metadata export).") + .description("Show whether multiple packages have unpublished changes (bulk metadata export).") .requiredOption("--packageKeys ", "Keys of packages to find the metadata of") .option("--json", "Return response as json type", "") - .action(this.batchExportPackagesMetadata); + .action(this.exportPackagesMetadata); configCommand.command("import") - .description("[Batch] Import packages from a batch archive produced by 'config export'. To import one package, use 'config package import' instead.") + .description("[Deprecated] Use 't2tc package import' (batch) or 'config package import' (single package) instead. Import packages from a Team-to-Team Copy archive.") + .deprecationNotice("'config import' is deprecated and will be removed in a future release. Use 't2tc package import' (batch archive) or 'config package import' (single package) instead.") .option("--overwrite", "Flag to allow overwriting of packages") .option("--validate", "Validate node configurations before import", false) .option("--gitProfile ", "Git profile which you want to use for the Git operations") @@ -66,14 +72,14 @@ class Module extends IModule { .description("Commands for working with a single package."); packageCommand.command("import") - .description("Import a package from a zip file or directory. Uses the package format, which is not interchangeable with the batch 'config export' / 'config import' archive.") + .description("Import a package from a zip file or directory. Uses the package format, which is not interchangeable with the 't2tc package export' / 't2tc package import' batch archive.") .option("-f, --file ", "Package zip file (relative path)") .option("-d, --directory ", "Package directory (relative path)") .option("--overwrite", "Flag to allow overwriting an existing package with the same key") .option("--json", "Return the response as a JSON file") .action(this.importSinglePackage); - configCommand.command("validate") + packageCommand.command("validate") .description("Validate package node configurations") .requiredOption("--packageKey ", "Key of the package to validate") .option( @@ -85,8 +91,28 @@ class Module extends IModule { .option("--json", "Return the response as a JSON file") .action(this.validatePackage); + packageCommand.command("list") + .description("List packages in the target team. Lists staging packages by default.") + .option("--json", "Return response as json type", "") + .option("--flavors ", "Lists only packages of the given flavors") + .action(this.listStagingPackages); + + configCommand.command("validate") + .description("[Deprecated] Use 'config package validate' instead. Validate package node configurations.") + .deprecationNotice("'config validate' is deprecated and will be removed in a future release. Use 'config package validate' instead.") + .requiredOption("--packageKey ", "Key of the package to validate") + .option( + "--layers ", + "Validation layers to run. Allowed values: SCHEMA, BUSINESS, PACKAGE_SETTINGS (can be combined, e.g. --layers SCHEMA BUSINESS PACKAGE_SETTINGS). Defaults to SCHEMA.", + ["SCHEMA"] + ) + .option("--nodeKeys ", "Specific node keys to validate (default: all nodes)") + .option("--json", "Return the response as a JSON file") + .action(this.validatePackage); + configCommand.command("diff") - .description("[Batch] Diff a local batch archive (from 'config export') against deployed or staging packages.") + .description("[Deprecated] Use 't2tc package diff' instead. Diff a local Team-to-Team Copy archive against deployed or staging packages.") + .deprecationNotice("'config diff' is deprecated and will be removed in a future release. Use 't2tc package diff' instead.") .option("--hasChanges", "Flag to return only the information if the package has changes without the actual changes") .option("--baseVersion ", "Compare against a given version or STAGING") .option("--json", "Return the response as a JSON file") @@ -210,7 +236,7 @@ class Module extends IModule { throw new Error("Please provide either --packageKeys or --keysByVersion, but not both."); } - await new ConfigCommandService(context).listPackages( + await new T2tcCommandService(context).listPackages( options.json, options.flavors, options.withDependencies, @@ -222,6 +248,10 @@ class Module extends IModule { options.staging); } + private async listStagingPackages(context: Context, command: Command, options: OptionValues): Promise { + await new StagingPackageService(context).listStagingPackages(options.flavors ?? [], false, options.json); + } + private async batchExportPackages(context: Context, command: Command, options: OptionValues): Promise { if ((options.packageKeys && options.keysByVersion) || (!options.packageKeys && !options.keysByVersion)) { throw new Error("Please provide either --packageKeys or --keysByVersion, but not both."); @@ -230,11 +260,11 @@ class Module extends IModule { throw new Error("Please specify a branch using --gitBranch when using a Git profile."); } options.withDependencies = options.withDependencies ?? false; - await new ConfigCommandService(context).batchExportPackages(options.packageKeys, options.keysByVersion, options.withDependencies, options.gitBranch, options.unzip); + await new T2tcCommandService(context).batchExportPackages(options.packageKeys, options.keysByVersion, options.withDependencies, options.gitBranch, options.unzip); } - private async batchExportPackagesMetadata(context: Context, command: Command, options: OptionValues): Promise { - await new ConfigCommandService(context).batchExportPackagesMetadata(options.packageKeys, options.json); + private async exportPackagesMetadata(context: Context, command: Command, options: OptionValues): Promise { + await new MetadataService(context).exportPackagesMetadata(options.packageKeys, options.json); } private async getPackageVersion(context: Context, command: Command, options: OptionValues): Promise { @@ -266,7 +296,7 @@ class Module extends IModule { if (options.gitProfile && !options.gitBranch) { throw new Error("Please specify a branch using --gitBranch when using a Git profile."); } - await new ConfigCommandService(context).batchImportPackages(options.file, options.directory, options.overwrite, options.gitBranch, options.validate); + await new T2tcCommandService(context).batchImportPackages(options.file, options.directory, options.overwrite, options.gitBranch, options.validate); } private async importSinglePackage(context: Context, command: Command, options: OptionValues): Promise { @@ -274,7 +304,7 @@ class Module extends IModule { } private async diffPackages(context: Context, command: Command, options: OptionValues): Promise { - await new ConfigCommandService(context).diffPackages(options.file, options.hasChanges, options.baseVersion, options.json); + await new T2tcCommandService(context).diffPackages(options.file, options.hasChanges, options.baseVersion, options.json); } private async validatePackage(context: Context, command: Command, options: OptionValues): Promise { diff --git a/src/commands/configuration-management/staging-package.service.ts b/src/commands/configuration-management/staging-package.service.ts new file mode 100644 index 00000000..16c60ebf --- /dev/null +++ b/src/commands/configuration-management/staging-package.service.ts @@ -0,0 +1,32 @@ +import { v4 as uuidv4 } from "uuid"; +import { Context } from "../../core/command/cli-context"; +import { PackageExportTransport } from "./interfaces/package-export.interfaces"; +import { fileService, FileService } from "../../core/utils/file-service"; +import { logger } from "../../core/utils/logger"; +import { StagingPackageApi } from "./api/staging-package-api"; + +export class StagingPackageService { + + private stagingPackageApi: StagingPackageApi; + + constructor(context: Context) { + this.stagingPackageApi = new StagingPackageApi(context); + } + + public async listStagingPackages(flavors: string[], includeBranches: boolean, jsonResponse: boolean): Promise { + const stagingPackages = await this.stagingPackageApi.findAllStagingPackages(flavors, includeBranches); + if (jsonResponse) { + this.exportListOfPackages(stagingPackages); + } else { + stagingPackages.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); + } +} diff --git a/src/commands/configuration-management/variable.service.ts b/src/commands/configuration-management/variable.service.ts index 2b636298..08945abd 100644 --- a/src/commands/configuration-management/variable.service.ts +++ b/src/commands/configuration-management/variable.service.ts @@ -1,26 +1,24 @@ 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 { fixConnectionVariables } from "./connection-variable.helper"; import { FileService, fileService } from "../../core/utils/file-service"; import { PackageKeyAndVersionPair, StagingVariableManifestTransport, VariableManifestTransport } from "./interfaces/package-export.interfaces"; -import { BatchImportExportApi } from "./api/batch-import-export-api"; +import { VariableApi } from "./api/variable-api"; import { URLSearchParams } from "url"; import { VariableAssignmentCandidatesApi } from "./api/variable-assignment-candidates-api"; import { StagingPackageVariablesApi } from "./api/staging-package-variables-api"; export class VariableService { - private batchImportExportApi: BatchImportExportApi; + private variableApi: VariableApi; private variableAssignmentCandidatesApi: VariableAssignmentCandidatesApi; private readonly stagingPackageVariablesApi: StagingPackageVariablesApi; - private studioService: StudioService; constructor(context: Context) { - this.batchImportExportApi = new BatchImportExportApi(context); + this.variableApi = new VariableApi(context); this.variableAssignmentCandidatesApi = new VariableAssignmentCandidatesApi(context); this.stagingPackageVariablesApi = new StagingPackageVariablesApi(context); - this.studioService = new StudioService(context); } public async listVariables(keysByVersion: string[], keysByVersionFile: string): Promise { @@ -74,8 +72,8 @@ export class VariableService { 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); + const variableManifests = await this.variableApi.findVariablesWithValuesByPackageKeysAndVersion(variablesExportRequest); + return fixConnectionVariables(variableManifests); } private async buildKeyVersionPairs(keysByVersion: string[], keysByVersionFile: string): Promise { diff --git a/src/commands/configuration-management/api/diff-api.ts b/src/commands/t2tc/api/diff-api.ts similarity index 85% rename from src/commands/configuration-management/api/diff-api.ts rename to src/commands/t2tc/api/diff-api.ts index e3739e91..1999d4b5 100644 --- a/src/commands/configuration-management/api/diff-api.ts +++ b/src/commands/t2tc/api/diff-api.ts @@ -1,11 +1,11 @@ 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"; +import { PackageDiffMetadata, PackageDiffTransport } from "../../configuration-management/interfaces/diff-package.interfaces"; export class DiffApi { - private httpClient: () => HttpClient; + private readonly httpClient: () => HttpClient; constructor(context: Context) { this.httpClient = () => context.httpClient; diff --git a/src/commands/configuration-management/api/batch-import-export-api.ts b/src/commands/t2tc/api/t2tc-package-api.ts similarity index 72% rename from src/commands/configuration-management/api/batch-import-export-api.ts rename to src/commands/t2tc/api/t2tc-package-api.ts index 12983ca3..27e4cf0b 100644 --- a/src/commands/configuration-management/api/batch-import-export-api.ts +++ b/src/commands/t2tc/api/t2tc-package-api.ts @@ -1,16 +1,15 @@ import * as FormData from "form-data"; import { PackageExportTransport, - PackageKeyAndVersionPair, PackageMetadataExportTransport, - PostPackageImportData, VariableManifestTransport, -} from "../interfaces/package-export.interfaces"; + PostPackageImportData, +} from "../../configuration-management/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 { +export class T2tcPackageApi { - private httpClient: () => HttpClient; + private readonly httpClient: () => HttpClient; constructor(context: Context) { this.httpClient = () => context.httpClient; @@ -28,17 +27,6 @@ export class BatchImportExportApi { }); } - public async findAllStagingPackages(flavors: string[], includeBranches: boolean = false): Promise { - const queryParams = new URLSearchParams(); - - queryParams.set("includeBranches", includeBranches.toString()); - flavors.forEach(flavor => queryParams.append("flavors", flavor)); - - return this.httpClient().get(`/pacman/api/core/staging/packages/export/list?${queryParams.toString()}`).catch(e => { - throw new FatalError(`Problem getting staging packages: ${e}`); - }); - } - public async findActivePackagesByVariableValue(flavors: string[], variableValue: string, variableType: string, includeBranches: boolean = false): Promise { const queryParams = new URLSearchParams(); @@ -96,15 +84,6 @@ export class BatchImportExportApi { }); } - public async batchExportPackagesMetadata(packageKeys: string[]): Promise { - const queryParams = new URLSearchParams(); - packageKeys.forEach(packageKey => queryParams.append("packageKeys", packageKey)); - - return this.httpClient().get(`/package-manager/api/core/packages/metadata/export?${queryParams.toString()}`).catch(e => { - throw new FatalError(`Problem exporting packages metadata: ${e}`); - }) - } - public async importPackages(data: FormData, overwrite: boolean, performValidation: boolean): Promise { return this.httpClient().postFile( "/package-manager/api/core/packages/import/batch", @@ -112,10 +91,4 @@ export class BatchImportExportApi { {overwrite, performValidation} ); } - - 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/interfaces/batch-export-import.constants.ts b/src/commands/t2tc/batch-export-import.constants.ts similarity index 99% rename from src/commands/configuration-management/interfaces/batch-export-import.constants.ts rename to src/commands/t2tc/batch-export-import.constants.ts index a0c815ea..534543c6 100644 --- a/src/commands/configuration-management/interfaces/batch-export-import.constants.ts +++ b/src/commands/t2tc/batch-export-import.constants.ts @@ -8,4 +8,4 @@ export enum BatchExportImportConstants { JSON_EXTENSION = ".json", NODES_FOLDER_NAME = "nodes/", SCENARIO_NODE = "SCENARIO" -} \ No newline at end of file +} diff --git a/src/commands/configuration-management/diff.service.ts b/src/commands/t2tc/diff.service.ts similarity index 96% rename from src/commands/configuration-management/diff.service.ts rename to src/commands/t2tc/diff.service.ts index d0246bd5..c07f03ba 100644 --- a/src/commands/configuration-management/diff.service.ts +++ b/src/commands/t2tc/diff.service.ts @@ -5,7 +5,7 @@ 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 { PackageDiffMetadata, PackageDiffTransport } from "../configuration-management/interfaces/diff-package.interfaces"; import { DiffApi } from "./api/diff-api"; export class DiffService { diff --git a/src/commands/t2tc/module.ts b/src/commands/t2tc/module.ts new file mode 100644 index 00000000..fc3ede07 --- /dev/null +++ b/src/commands/t2tc/module.ts @@ -0,0 +1,100 @@ +import { Configurator, IModule } from "../../core/command/module-handler"; +import { Context } from "../../core/command/cli-context"; +import { Command, OptionValues } from "commander"; +import { T2tcCommandService } from "./t2tc-command.service"; + +class Module extends IModule { + + public register(context: Context, configurator: Configurator): void { + const t2tcCommand = configurator.command("t2tc") + .description("Team-to-Team Copy (T2TC) commands for moving whole packages and their dependencies between teams using the batch transport archive format."); + + const t2tcPackageCommand = t2tcCommand.command("package") + .description("Team-to-Team Copy package commands: list, export, import and diff packages using the batch transport archive."); + + t2tcPackageCommand.command("list") + .description("List packages in the target team. Part of the Team-to-Team Copy export/import workflow.") + .option("--json", "Return response as json type", "") + .option("--flavors ", "Lists only packages of the given flavors") + .option("--withDependencies", "Include dependencies", "") + .option("--packageKeys ", "Lists only active versions of given package keys") + .option("--keysByVersion ", "Lists packages by given key and version [packageKey.version]") + .option("--variableValue ", "Variable value for filtering packages by.") + .option("--variableType ", "Variable type for filtering packages by.") + .option("--branches", "Include branches", false) + .option("--staging", "List staging packages instead", false) + .action(this.listPackages); + + t2tcPackageCommand.command("export") + .description("Export one or more packages into a Team-to-Team Copy archive. The archive is consumed by 't2tc package import' / 't2tc package diff'.") + .option("--packageKeys ", "Keys of packages to export. Exports the latest deployed version only") + .option("--keysByVersion ", "Keys of packages to export by version") + .option("--withDependencies", "Include variables and dependencies", "") + .option("--unzip", "Unzip the exported file", "") + .option("--gitProfile ", "Git profile which you want to use for the Git operations") + .option("--gitBranch ", "Git branch in which you want to push the exported file") + .action(this.batchExportPackages); + + t2tcPackageCommand.command("import") + .description("Import packages from a Team-to-Team Copy archive produced by 't2tc package export'. To import a single standalone package, use 'config package import'.") + .option("--overwrite", "Flag to allow overwriting of packages") + .option("--validate", "Validate node configurations before import", false) + .option("--gitProfile ", "Git profile which you want to use for the Git operations") + .option("--gitBranch ", "Git branch from which you want to pull the exported file and import") + .option("-f, --file ", "Exported packages file (relative path)") + .option("-d, --directory ", "Exported packages directory (relative path)") + .action(this.batchImportPackages); + + t2tcPackageCommand.command("diff") + .description("Diff a local Team-to-Team Copy archive (from 't2tc package export') against deployed or staging packages.") + .option("--hasChanges", "Flag to return only the information if the package has changes without the actual changes") + .option("--baseVersion ", "Compare against a given version or STAGING") + .option("--json", "Return the response as a JSON file") + .requiredOption("-f, --file ", "Exported packages file (relative or absolute path)") + .action(this.diffPackages); + } + + private async listPackages(context: Context, command: Command, options: OptionValues): Promise { + if (options.staging && (options.withDependencies || options.packageKeys || options.keysByVersion || options.variableValue || options.variableType)) { + throw new Error("Staging parameter is not compatible with --withDependencies, --packageKeys, --keysByVersion, --variableValue, --variableType"); + } + if (options.packageKeys && options.keysByVersion) { + throw new Error("Please provide either --packageKeys or --keysByVersion, but not both."); + } + + await new T2tcCommandService(context).listPackages( + options.json, + options.flavors, + options.withDependencies, + options.packageKeys, + options.keysByVersion, + options.variableValue, + options.variableType, + options.branches, + options.staging); + } + + private async batchExportPackages(context: Context, command: Command, options: OptionValues): Promise { + if ((options.packageKeys && options.keysByVersion) || (!options.packageKeys && !options.keysByVersion)) { + throw new Error("Please provide either --packageKeys or --keysByVersion, but not both."); + } + if (options.gitProfile && !options.gitBranch) { + throw new Error("Please specify a branch using --gitBranch when using a Git profile."); + } + options.withDependencies = options.withDependencies ?? false; + await new T2tcCommandService(context).batchExportPackages(options.packageKeys, options.keysByVersion, options.withDependencies, options.gitBranch, options.unzip); + } + + private async batchImportPackages(context: Context, command: Command, options: OptionValues): Promise { + if (options.gitProfile && !options.gitBranch) { + throw new Error("Please specify a branch using --gitBranch when using a Git profile."); + } + await new T2tcCommandService(context).batchImportPackages(options.file, options.directory, options.overwrite, options.gitBranch, options.validate); + } + + private async diffPackages(context: Context, command: Command, options: OptionValues): Promise { + await new T2tcCommandService(context).diffPackages(options.file, options.hasChanges, options.baseVersion, options.json); + } +} + +export = Module; diff --git a/src/commands/configuration-management/studio.service.ts b/src/commands/t2tc/studio.service.ts similarity index 92% rename from src/commands/configuration-management/studio.service.ts rename to src/commands/t2tc/studio.service.ts index b729460b..e99a9db2 100644 --- a/src/commands/configuration-management/studio.service.ts +++ b/src/commands/t2tc/studio.service.ts @@ -6,13 +6,13 @@ import { PackageExportTransport, PackageKeyAndVersionPair, StudioPackageManifest, VariableExportTransport, VariableManifestTransport, -} from "./interfaces/package-export.interfaces"; +} from "../configuration-management/interfaces/package-export.interfaces"; import { ContentNodeTransport, PackageManagerVariableType, PackageWithVariableAssignments, StudioComputeNodeDescriptor, } from "../studio/interfaces/package-manager.interfaces"; -import { BatchExportImportConstants } from "./interfaces/batch-export-import.constants"; +import { BatchExportImportConstants } from "./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"; @@ -60,19 +60,6 @@ export class StudioService { 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); @@ -142,20 +129,6 @@ export class StudioService { }); } - 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 => { diff --git a/src/commands/t2tc/t2tc-command.service.ts b/src/commands/t2tc/t2tc-command.service.ts new file mode 100644 index 00000000..eb75e313 --- /dev/null +++ b/src/commands/t2tc/t2tc-command.service.ts @@ -0,0 +1,77 @@ +import { Context } from "../../core/command/cli-context"; +import { fileService } from "../../core/utils/file-service"; +import { T2tcPackageService } from "./t2tc-package.service"; +import { DiffService } from "./diff.service"; +import { StagingPackageService } from "../configuration-management/staging-package.service"; + +export class T2tcCommandService { + + private t2tcPackageService: T2tcPackageService; + private diffService: DiffService; + private stagingPackageService: StagingPackageService; + + constructor(context: Context) { + this.t2tcPackageService = new T2tcPackageService(context); + this.diffService = new DiffService(context); + this.stagingPackageService = new StagingPackageService(context); + } + + public async listPackages( + jsonResponse: boolean, + flavors: string[], + withDependencies: boolean, + packageKeys: string[], + keysByVersion: string[], + variableValue: string, + variableType: string, + includeBranches: boolean, + staging: boolean): Promise { + if (staging) { + await this.stagingPackageService.listStagingPackages(flavors ?? [], includeBranches, jsonResponse); + } else if (variableValue) { + await this.listPackagesByVariableValue(jsonResponse, flavors, variableValue, variableType, includeBranches); + } else if (jsonResponse) { + await this.t2tcPackageService.findAndExportListOfPackages(flavors ?? [], packageKeys ?? [], keysByVersion ?? [], withDependencies, includeBranches); + } else if (keysByVersion) { + await this.t2tcPackageService.listPackagesByKeysWithVersion(keysByVersion, withDependencies); + } else { + await this.t2tcPackageService.listActivePackages(flavors ?? [], includeBranches); + } + } + + public batchExportPackages(packageKeys: string[], packageKeysByVersion: string[], withDependencies: boolean, gitBranch: string, unzip: boolean): Promise { + return this.t2tcPackageService.batchExportPackages(packageKeys, packageKeysByVersion, withDependencies, gitBranch, unzip); + } + + public batchImportPackages(file: string, directory: string, overwrite: boolean, gitBranch: string, performValidation: boolean = false): Promise { + if ((directory || file) && gitBranch) { + throw new Error("You cannot use both file/directory and gitBranch options at the same time. Only one import source can be defined."); + } + if (!directory && !file && !gitBranch) { + throw new Error("You must provide either a file/directory or a gitBranch option to import packages."); + } + if (file && directory) { + throw new Error("You cannot use both file and directory options at the same time. Only one import source can be defined."); + } + if (file && fileService.isDirectory(file)) { + throw new Error("The file option accepts only zip files."); + } + if (directory && !fileService.isDirectory(directory)) { + throw new Error("The directory option accepts only directories."); + } + const sourcePath = file ?? directory; + return this.t2tcPackageService.batchImportPackages(sourcePath, overwrite, gitBranch, performValidation); + } + + public diffPackages(file: string, hasChanges: boolean, baseVersion: string, jsonResponse: boolean): Promise { + return this.diffService.diffPackages(file, hasChanges, baseVersion, jsonResponse); + } + + private async listPackagesByVariableValue(jsonResponse: boolean, flavors: string[], variableValue: string, variableType: string, includeBranches: boolean): Promise { + if (jsonResponse) { + await this.t2tcPackageService.findAndExportListOfActivePackagesByVariableValue(flavors ?? [], variableValue, variableType, includeBranches) + } else { + await this.t2tcPackageService.listActivePackagesByVariableValue(flavors ?? [], variableValue, variableType, includeBranches); + } + } +} diff --git a/src/commands/configuration-management/batch-import-export.service.ts b/src/commands/t2tc/t2tc-package.service.ts similarity index 75% rename from src/commands/configuration-management/batch-import-export.service.ts rename to src/commands/t2tc/t2tc-package.service.ts index 037daf1b..aed32676 100644 --- a/src/commands/configuration-management/batch-import-export.service.ts +++ b/src/commands/t2tc/t2tc-package.service.ts @@ -5,30 +5,34 @@ import * as AdmZip from "adm-zip"; import { Context } from "../../core/command/cli-context"; import { PackageExportTransport, PackageKeyAndVersionPair, - PackageManifestTransport, PackageMetadataExportTransport, + PackageManifestTransport, StudioPackageManifest, VariableManifestTransport, -} from "./interfaces/package-export.interfaces"; -import { BatchExportImportConstants } from "./interfaces/batch-export-import.constants"; +} from "../configuration-management/interfaces/package-export.interfaces"; +import { BatchExportImportConstants } from "./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 { T2tcPackageApi } from "./api/t2tc-package-api"; +import { VariableApi } from "../configuration-management/api/variable-api"; +import { fixConnectionVariables } from "../configuration-management/connection-variable.helper"; import { StudioService } from "./studio.service"; import { GitService } from "../../core/git-profile/git/git.service"; import * as fs from "fs"; import { FileConstants } from "../../core/utils/file.constants"; -export class BatchImportExportService { +export class T2tcPackageService { - private batchImportExportApi: BatchImportExportApi; + private t2tcPackageApi: T2tcPackageApi; + private variableApi: VariableApi; private studioPackageApi: PackageApi; private studioService: StudioService; private gitService: GitService; constructor(context: Context) { - this.batchImportExportApi = new BatchImportExportApi(context); + this.t2tcPackageApi = new T2tcPackageApi(context); + this.variableApi = new VariableApi(context); this.studioPackageApi = new PackageApi(context); this.studioService = new StudioService(context); @@ -36,32 +40,21 @@ export class BatchImportExportService { } public async listActivePackages(flavors: string[], includeBranches: boolean): Promise { - const activePackages = await this.batchImportExportApi.findAllActivePackages(flavors, false, includeBranches); + const activePackages = await this.t2tcPackageApi.findAllActivePackages(flavors, false, includeBranches); activePackages.forEach(pkg => { logger.info(`${pkg.name} - Key: "${pkg.key}"`) }); } - public async listStagingPackages(flavors: string[], includeBranches: boolean, jsonResponse: boolean): Promise { - const stagingPackages = await this.batchImportExportApi.findAllStagingPackages(flavors, includeBranches); - if (jsonResponse) { - this.exportListOfPackages(stagingPackages); - } else { - stagingPackages.forEach(pkg => { - logger.info(`${pkg.name} - Key: "${pkg.key}"`); - }); - } - } - public async findAndExportListOfPackages(flavors: string[], packageKeys: string[], keysByVersion: string[], withDependencies: boolean, includeBranches: boolean): Promise { let packagesToExport: PackageExportTransport[]; if (keysByVersion.length) { - packagesToExport = await this.batchImportExportApi.findPackagesByKeysAndVersion(keysByVersion, withDependencies); + packagesToExport = await this.t2tcPackageApi.findPackagesByKeysAndVersion(keysByVersion, withDependencies); } else if (packageKeys.length) { - packagesToExport = await this.batchImportExportApi.findActivePackagesByKeys(packageKeys, withDependencies); + packagesToExport = await this.t2tcPackageApi.findActivePackagesByKeys(packageKeys, withDependencies); } else { - packagesToExport = await this.batchImportExportApi.findAllActivePackages(flavors, withDependencies, includeBranches); + packagesToExport = await this.t2tcPackageApi.findAllActivePackages(flavors, withDependencies, includeBranches); } packagesToExport = await this.studioService.getExportPackagesWithStudioData(packagesToExport, withDependencies); @@ -70,7 +63,7 @@ export class BatchImportExportService { } public async listPackagesByKeysWithVersion(keysByVersion: string[], withDependencies: boolean): Promise { - const exportedPackages = await this.batchImportExportApi.findPackagesByKeysAndVersion(keysByVersion, withDependencies); + const exportedPackages = await this.t2tcPackageApi.findPackagesByKeysAndVersion(keysByVersion, withDependencies); exportedPackages.forEach(pkg => { logger.info(`${pkg.name} - Key: "${pkg.key}"`); }); @@ -79,9 +72,9 @@ export class BatchImportExportService { public async batchExportPackages(packageKeys: string[], packageKeysByVersion: string[], withDependencies: boolean, gitBranch: string, unzip: boolean): Promise { let exportedPackagesData: Buffer; if (packageKeys) { - exportedPackagesData = await this.batchImportExportApi.exportPackages(packageKeys, withDependencies); + exportedPackagesData = await this.t2tcPackageApi.exportPackages(packageKeys, withDependencies); } else { - exportedPackagesData = await this.batchImportExportApi.exportPackagesByVersions(packageKeysByVersion, withDependencies); + exportedPackagesData = await this.t2tcPackageApi.exportPackagesByVersions(packageKeysByVersion, withDependencies); } const exportedPackagesZip: AdmZip = new AdmZip(exportedPackagesData); @@ -93,7 +86,7 @@ export class BatchImportExportService { const versionsByPackageKey = this.getVersionsByPackageKey(manifest); let exportedVariables = await this.getVersionedVariablesForPackagesWithKeys(versionsByPackageKey); - exportedVariables = this.studioService.fixConnectionVariables(exportedVariables); + exportedVariables = fixConnectionVariables(exportedVariables); exportedPackagesZip.addFile(BatchExportImportConstants.VARIABLES_FILE_NAME, Buffer.from(stringify(exportedVariables), "utf8"), "", FileConstants.DEFAULT_FILE_PERMISSIONS); const studioPackageKeys = manifest.filter(packageManifest => packageManifest.flavor === BatchExportImportConstants.STUDIO) @@ -124,18 +117,6 @@ export class BatchImportExportService { } } - public async batchExportPackagesMetadata(packageKeys: string[], jsonResponse: boolean): Promise { - const exportedPackagesMetadata: PackageMetadataExportTransport[] = await this.batchImportExportApi.batchExportPackagesMetadata(packageKeys); - - if (jsonResponse) { - this.exportListOfPackagesMetadata(exportedPackagesMetadata); - } else { - exportedPackagesMetadata.forEach(pkg => { - logger.info(`${pkg.key} - Has Unpublished Changes: ${pkg.hasUnpublishedChanges}`); - }); - } - } - public async batchImportPackages(sourcePath: string, overwrite: boolean, gitBranch: string, performValidation: boolean = false): Promise { let sourceToBeImported: string; let temporaryGitFolder: string; @@ -157,7 +138,7 @@ export class BatchImportExportService { const existingStudioPackages = await this.studioPackageApi.findAllPackages(); const formData = this.buildBodyForImport(configs, sourceToBeImported, variablesManifests); - const postPackageImportData = await this.batchImportExportApi.importPackages(formData, overwrite, performValidation); + const postPackageImportData = await this.t2tcPackageApi.importPackages(formData, overwrite, performValidation); await this.studioService.processImportedPackages(configs, existingStudioPackages, studioManifests); if (gitBranch) { @@ -171,7 +152,7 @@ export class BatchImportExportService { } public async findAndExportListOfActivePackagesByVariableValue(flavors: string[], variableValue: string, variableType: string, includeBranches: boolean): Promise { - let packagesToExport = await this.batchImportExportApi.findActivePackagesByVariableValue(flavors, variableValue, variableType, includeBranches); + let packagesToExport = await this.t2tcPackageApi.findActivePackagesByVariableValue(flavors, variableValue, variableType, includeBranches); packagesToExport = await this.studioService.getExportPackagesWithStudioData(packagesToExport, false); @@ -179,7 +160,7 @@ export class BatchImportExportService { } public async listActivePackagesByVariableValue(flavors: string[], variableValue: string, variableType: string, includeBranches: boolean) : Promise { - const packagesByVariableValue = await this.batchImportExportApi.findActivePackagesByVariableValue(flavors, variableValue, variableType, includeBranches); + const packagesByVariableValue = await this.t2tcPackageApi.findActivePackagesByVariableValue(flavors, variableValue, variableType, includeBranches); packagesByVariableValue.forEach(pkg => { logger.info(`${pkg.name} - Key: "${pkg.key}"`) }); @@ -211,13 +192,7 @@ export class BatchImportExportService { }) }); - return this.batchImportExportApi.findVariablesWithValuesByPackageKeysAndVersion(variableExportRequest) - } - - private exportListOfPackagesMetadata(packagesMetadata: PackageMetadataExportTransport[]): void { - const filename = uuidv4() + ".json"; - fileService.writeToFileWithGivenName(JSON.stringify(packagesMetadata), filename); - logger.info(FileService.fileDownloadedMessage + filename); + return this.variableApi.findVariablesWithValuesByPackageKeysAndVersion(variableExportRequest) } private buildBodyForImport(configs: AdmZip, sourcePath: string, variablesManifests: VariableManifestTransport[]): FormData { diff --git a/tests/commands/configuration-management/config-metadata-export.spec.ts b/tests/commands/configuration-management/config-metadata-export.spec.ts index 543d84cd..2b6e5f6f 100644 --- a/tests/commands/configuration-management/config-metadata-export.spec.ts +++ b/tests/commands/configuration-management/config-metadata-export.spec.ts @@ -3,7 +3,7 @@ import { mockAxiosGet } from "../../utls/http-requests-mock"; import { PackageMetadataExportTransport } from "../../../src/commands/configuration-management/interfaces/package-export.interfaces"; -import { ConfigCommandService } from "../../../src/commands/configuration-management/config-command.service"; +import { MetadataService } from "../../../src/commands/configuration-management/metadata.service"; import { testContext } from "../../utls/test-context"; import { loggingTestTransport, mockWriteFileSync } from "../../jest.setup"; import { FileService } from "../../../src/core/utils/file-service"; @@ -28,7 +28,7 @@ describe("Config metadata export", () => { mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/core/packages/metadata/export?packageKeys=package-key-1&packageKeys=package-key-2", response); - await new ConfigCommandService(testContext).batchExportPackagesMetadata(packageKeys, false); + await new MetadataService(testContext).exportPackagesMetadata(packageKeys, false); expect(loggingTestTransport.logMessages.length).toBe(2); expect(loggingTestTransport.logMessages[0].message).toContain( @@ -49,7 +49,7 @@ describe("Config metadata export", () => { mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/core/packages/metadata/export?packageKeys=package-key-1&packageKeys=package-key-2", response); - await new ConfigCommandService(testContext).batchExportPackagesMetadata(packageKeys, true); + await new MetadataService(testContext).exportPackagesMetadata(packageKeys, true); const expectedFileName = loggingTestTransport.logMessages[0].message.split(FileService.fileDownloadedMessage)[1]; diff --git a/tests/commands/configuration-management/module.spec.ts b/tests/commands/configuration-management/module.spec.ts index 80ef7eab..e24159cd 100644 --- a/tests/commands/configuration-management/module.spec.ts +++ b/tests/commands/configuration-management/module.spec.ts @@ -1,6 +1,9 @@ import Module = require("../../../src/commands/configuration-management/module"); import { Command, OptionValues } from "commander"; import { ConfigCommandService } from "../../../src/commands/configuration-management/config-command.service"; +import { StagingPackageService } from "../../../src/commands/configuration-management/staging-package.service"; +import { MetadataService } from "../../../src/commands/configuration-management/metadata.service"; +import { T2tcCommandService } from "../../../src/commands/t2tc/t2tc-command.service"; import { NodeDependencyService } from "../../../src/commands/configuration-management/node-dependency.service"; import { PackageVersionCommandService } from "../../../src/commands/configuration-management/package-version-command.service"; import { NodeDiffService } from "../../../src/commands/configuration-management/node-diff.service"; @@ -9,6 +12,9 @@ import { testContext } from "../../utls/test-context"; import { createMockConfigurator } from "../../utls/configurator-mock"; jest.mock("../../../src/commands/configuration-management/config-command.service"); +jest.mock("../../../src/commands/configuration-management/staging-package.service"); +jest.mock("../../../src/commands/configuration-management/metadata.service"); +jest.mock("../../../src/commands/t2tc/t2tc-command.service"); jest.mock("../../../src/commands/configuration-management/node-dependency.service"); jest.mock("../../../src/commands/configuration-management/node-diff.service"); jest.mock("../../../src/commands/configuration-management/package-version-command.service"); @@ -25,6 +31,9 @@ describe("Configuration Management Module - Action Validations", () => { let module: Module; let mockCommand: Command; let mockConfigCommandService: jest.Mocked; + let mockStagingPackageService: jest.Mocked; + let mockMetadataService: jest.Mocked; + let mockT2tcCommandService: jest.Mocked; let mockNodeDependencyService: jest.Mocked; let mockNodeDiffService: jest.Mocked; let mockSinglePackageImportService: jest.Mocked; @@ -35,8 +44,19 @@ describe("Configuration Management Module - Action Validations", () => { mockCommand = {} as Command; mockConfigCommandService = { - listPackages: jest.fn().mockResolvedValue(undefined), listVariables: jest.fn().mockResolvedValue(undefined), + } as any; + + mockStagingPackageService = { + listStagingPackages: jest.fn().mockResolvedValue(undefined), + } as any; + + mockMetadataService = { + exportPackagesMetadata: jest.fn().mockResolvedValue(undefined), + } as any; + + mockT2tcCommandService = { + listPackages: jest.fn().mockResolvedValue(undefined), batchExportPackages: jest.fn().mockResolvedValue(undefined), batchImportPackages: jest.fn().mockResolvedValue(undefined), diffPackages: jest.fn().mockResolvedValue(undefined), @@ -56,6 +76,9 @@ describe("Configuration Management Module - Action Validations", () => { } as any; (ConfigCommandService as jest.MockedClass).mockImplementation(() => mockConfigCommandService); + (StagingPackageService as jest.MockedClass).mockImplementation(() => mockStagingPackageService); + (MetadataService as jest.MockedClass).mockImplementation(() => mockMetadataService); + (T2tcCommandService as jest.MockedClass).mockImplementation(() => mockT2tcCommandService); (NodeDependencyService as jest.MockedClass).mockImplementation(() => mockNodeDependencyService); (NodeDiffService as jest.MockedClass).mockImplementation(() => mockNodeDiffService); (SinglePackageImportService as jest.MockedClass).mockImplementation(() => mockSinglePackageImportService); @@ -73,7 +96,7 @@ describe("Configuration Management Module - Action Validations", () => { (module as any).listPackages(testContext, mockCommand, options) ).rejects.toThrow("Please provide either --packageKeys or --keysByVersion, but not both."); - expect(mockConfigCommandService.listPackages).not.toHaveBeenCalled(); + expect(mockT2tcCommandService.listPackages).not.toHaveBeenCalled(); }); it("should pass validation when only packageKeys is provided", async () => { @@ -84,7 +107,7 @@ describe("Configuration Management Module - Action Validations", () => { await (module as any).listPackages(testContext, mockCommand, options); - expect(mockConfigCommandService.listPackages).toHaveBeenCalledWith( + expect(mockT2tcCommandService.listPackages).toHaveBeenCalledWith( true, undefined, undefined, @@ -105,7 +128,7 @@ describe("Configuration Management Module - Action Validations", () => { await (module as any).listPackages(testContext, mockCommand, options); - expect(mockConfigCommandService.listPackages).toHaveBeenCalledWith( + expect(mockT2tcCommandService.listPackages).toHaveBeenCalledWith( true, undefined, undefined, @@ -127,7 +150,7 @@ describe("Configuration Management Module - Action Validations", () => { await (module as any).listPackages(testContext, mockCommand, options); - expect(mockConfigCommandService.listPackages).toHaveBeenCalledWith( + expect(mockT2tcCommandService.listPackages).toHaveBeenCalledWith( true, undefined, undefined, @@ -152,7 +175,7 @@ describe("Configuration Management Module - Action Validations", () => { await (module as any).listPackages(testContext, mockCommand, options); - expect(mockConfigCommandService.listPackages).toHaveBeenCalledWith( + expect(mockT2tcCommandService.listPackages).toHaveBeenCalledWith( true, undefined, undefined, @@ -221,6 +244,38 @@ describe("Configuration Management Module - Action Validations", () => { }); }); + describe("config package list handler", () => { + it("should list staging packages with json and flavors", async () => { + const options: OptionValues = { + json: true, + flavors: ["APP"], + }; + + await (module as any).listStagingPackages(testContext, mockCommand, options); + + expect(mockStagingPackageService.listStagingPackages).toHaveBeenCalledWith(["APP"], false, true); + }); + + it("should default flavors to an empty list and json to undefined when not provided", async () => { + const options: OptionValues = {}; + + await (module as any).listStagingPackages(testContext, mockCommand, options); + + expect(mockStagingPackageService.listStagingPackages).toHaveBeenCalledWith([], false, undefined); + }); + + it("should not pass legacy listPackages options", async () => { + const options: OptionValues = { + flavors: ["APP", "ANALYSIS"], + }; + + await (module as any).listStagingPackages(testContext, mockCommand, options); + + expect(mockStagingPackageService.listStagingPackages).toHaveBeenCalledWith(["APP", "ANALYSIS"], false, undefined); + expect(mockT2tcCommandService.listPackages).not.toHaveBeenCalled(); + }); + }); + describe("batchExportPackages validation", () => { describe("packageKeys and keysByVersion validation", () => { it("should throw error when both packageKeys and keysByVersion are provided", async () => { @@ -233,7 +288,7 @@ describe("Configuration Management Module - Action Validations", () => { (module as any).batchExportPackages(testContext, mockCommand, options) ).rejects.toThrow("Please provide either --packageKeys or --keysByVersion, but not both."); - expect(mockConfigCommandService.batchExportPackages).not.toHaveBeenCalled(); + expect(mockT2tcCommandService.batchExportPackages).not.toHaveBeenCalled(); }); it("should throw error when neither packageKeys nor keysByVersion are provided", async () => { @@ -243,7 +298,7 @@ describe("Configuration Management Module - Action Validations", () => { (module as any).batchExportPackages(testContext, mockCommand, options) ).rejects.toThrow("Please provide either --packageKeys or --keysByVersion, but not both."); - expect(mockConfigCommandService.batchExportPackages).not.toHaveBeenCalled(); + expect(mockT2tcCommandService.batchExportPackages).not.toHaveBeenCalled(); }); it("should pass validation when only packageKeys is provided", async () => { @@ -253,7 +308,7 @@ describe("Configuration Management Module - Action Validations", () => { await (module as any).batchExportPackages(testContext, mockCommand, options); - expect(mockConfigCommandService.batchExportPackages).toHaveBeenCalledWith( + expect(mockT2tcCommandService.batchExportPackages).toHaveBeenCalledWith( ["package1", "package2"], undefined, false, @@ -269,7 +324,7 @@ describe("Configuration Management Module - Action Validations", () => { await (module as any).batchExportPackages(testContext, mockCommand, options); - expect(mockConfigCommandService.batchExportPackages).toHaveBeenCalledWith( + expect(mockT2tcCommandService.batchExportPackages).toHaveBeenCalledWith( undefined, ["package3:v1", "package4:v2"], false, @@ -290,7 +345,7 @@ describe("Configuration Management Module - Action Validations", () => { (module as any).batchExportPackages(testContext, mockCommand, options) ).rejects.toThrow("Please specify a branch using --gitBranch when using a Git profile."); - expect(mockConfigCommandService.batchExportPackages).not.toHaveBeenCalled(); + expect(mockT2tcCommandService.batchExportPackages).not.toHaveBeenCalled(); }); it("should pass validation when gitProfile provided with gitBranch option", async () => { @@ -302,7 +357,7 @@ describe("Configuration Management Module - Action Validations", () => { await (module as any).batchExportPackages(testContext, mockCommand, options); - expect(mockConfigCommandService.batchExportPackages).toHaveBeenCalledWith( + expect(mockT2tcCommandService.batchExportPackages).toHaveBeenCalledWith( ["package1"], undefined, false, @@ -319,7 +374,7 @@ describe("Configuration Management Module - Action Validations", () => { await (module as any).batchExportPackages(testContext, mockCommand, options); - expect(mockConfigCommandService.batchExportPackages).toHaveBeenCalledWith( + expect(mockT2tcCommandService.batchExportPackages).toHaveBeenCalledWith( ["package1"], undefined, false, @@ -335,7 +390,7 @@ describe("Configuration Management Module - Action Validations", () => { await (module as any).batchExportPackages(testContext, mockCommand, options); - expect(mockConfigCommandService.batchExportPackages).toHaveBeenCalled(); + expect(mockT2tcCommandService.batchExportPackages).toHaveBeenCalled(); }); }); @@ -347,7 +402,7 @@ describe("Configuration Management Module - Action Validations", () => { await (module as any).batchExportPackages(testContext, mockCommand, options); - expect(mockConfigCommandService.batchExportPackages).toHaveBeenCalledWith( + expect(mockT2tcCommandService.batchExportPackages).toHaveBeenCalledWith( ["package1"], undefined, false, @@ -364,7 +419,7 @@ describe("Configuration Management Module - Action Validations", () => { await (module as any).batchExportPackages(testContext, mockCommand, options); - expect(mockConfigCommandService.batchExportPackages).toHaveBeenCalledWith( + expect(mockT2tcCommandService.batchExportPackages).toHaveBeenCalledWith( ["package1"], undefined, true, @@ -381,7 +436,7 @@ describe("Configuration Management Module - Action Validations", () => { await (module as any).batchExportPackages(testContext, mockCommand, options); - expect(mockConfigCommandService.batchExportPackages).toHaveBeenCalledWith( + expect(mockT2tcCommandService.batchExportPackages).toHaveBeenCalledWith( ["package1"], undefined, false, @@ -403,7 +458,7 @@ describe("Configuration Management Module - Action Validations", () => { (module as any).batchExportPackages(testContext, mockCommand, options) ).rejects.toThrow("Please provide either --packageKeys or --keysByVersion, but not both."); - expect(mockConfigCommandService.batchExportPackages).not.toHaveBeenCalled(); + expect(mockT2tcCommandService.batchExportPackages).not.toHaveBeenCalled(); }); }); }); @@ -420,7 +475,7 @@ describe("Configuration Management Module - Action Validations", () => { (module as any).batchImportPackages(testContext, mockCommand, options) ).rejects.toThrow("Please specify a branch using --gitBranch when using a Git profile."); - expect(mockConfigCommandService.batchImportPackages).not.toHaveBeenCalled(); + expect(mockT2tcCommandService.batchImportPackages).not.toHaveBeenCalled(); }); it("should pass validation when gitProfile is provided with gitBranch option", async () => { @@ -432,7 +487,7 @@ describe("Configuration Management Module - Action Validations", () => { await (module as any).batchImportPackages(testContext, mockCommand, options); - expect(mockConfigCommandService.batchImportPackages).toHaveBeenCalledWith( + expect(mockT2tcCommandService.batchImportPackages).toHaveBeenCalledWith( "export.zip", undefined, undefined, @@ -449,7 +504,7 @@ describe("Configuration Management Module - Action Validations", () => { await (module as any).batchImportPackages(testContext, mockCommand, options); - expect(mockConfigCommandService.batchImportPackages).toHaveBeenCalledWith( + expect(mockT2tcCommandService.batchImportPackages).toHaveBeenCalledWith( undefined, "./exported", undefined, @@ -465,7 +520,7 @@ describe("Configuration Management Module - Action Validations", () => { await (module as any).batchImportPackages(testContext, mockCommand, options); - expect(mockConfigCommandService.batchImportPackages).toHaveBeenCalledWith( + expect(mockT2tcCommandService.batchImportPackages).toHaveBeenCalledWith( "export.zip", undefined, undefined, @@ -483,7 +538,7 @@ describe("Configuration Management Module - Action Validations", () => { await (module as any).batchImportPackages(testContext, mockCommand, options); - expect(mockConfigCommandService.batchImportPackages).toHaveBeenCalledWith( + expect(mockT2tcCommandService.batchImportPackages).toHaveBeenCalledWith( "my-export.zip", undefined, undefined, @@ -499,7 +554,7 @@ describe("Configuration Management Module - Action Validations", () => { await (module as any).batchImportPackages(testContext, mockCommand, options); - expect(mockConfigCommandService.batchImportPackages).toHaveBeenCalledWith( + expect(mockT2tcCommandService.batchImportPackages).toHaveBeenCalledWith( undefined, "./my-exports", undefined, @@ -516,7 +571,7 @@ describe("Configuration Management Module - Action Validations", () => { await (module as any).batchImportPackages(testContext, mockCommand, options); - expect(mockConfigCommandService.batchImportPackages).toHaveBeenCalledWith( + expect(mockT2tcCommandService.batchImportPackages).toHaveBeenCalledWith( "export.zip", undefined, true, @@ -534,7 +589,7 @@ describe("Configuration Management Module - Action Validations", () => { await (module as any).batchImportPackages(testContext, mockCommand, options); - expect(mockConfigCommandService.batchImportPackages).toHaveBeenCalledWith( + expect(mockT2tcCommandService.batchImportPackages).toHaveBeenCalledWith( undefined, "./exports", true, @@ -770,9 +825,11 @@ describe("Configuration Management Module - Action Validations", () => { expect(() => new Module().register(testContext, mockConfigurator)).not.toThrow(); - // Top-level groups attached to the root configurator + // Top-level groups attached to the root configurator. The 't2tc' group + // lives in its own module (tests/commands/t2tc/module.spec.ts). expect(mockConfigurator.command).toHaveBeenCalledWith("config"); expect(mockConfigurator.command).toHaveBeenCalledWith("list"); + expect(mockConfigurator.command).toHaveBeenCalledWith("package"); }); it("wires an action handler for every leaf subcommand", () => { @@ -782,12 +839,27 @@ describe("Configuration Management Module - Action Validations", () => { // Each leaf command terminates the fluent chain with .action(handler). // Keep this count in sync when adding or removing commands in module.ts. - const expectedLeafCommands = 18; + // The 4 't2tc package' leaf commands moved to their own module. + const expectedLeafCommands = 20; expect(mockConfigurator.action).toHaveBeenCalledTimes(expectedLeafCommands); for (const call of mockConfigurator.action.mock.calls) { expect(typeof call[0]).toBe("function"); } }); + + it("marks the moved config commands as deprecated", () => { + const mockConfigurator = createMockConfigurator(); + + new Module().register(testContext, mockConfigurator); + + // config list/export/import/diff/validate are duplicated under t2tc package / + // config package and the originals carry a deprecation notice. + const expectedDeprecatedCommands = 5; + expect(mockConfigurator.deprecationNotice).toHaveBeenCalledTimes(expectedDeprecatedCommands); + for (const call of mockConfigurator.deprecationNotice.mock.calls) { + expect(typeof call[0]).toBe("string"); + } + }); }); describe("listNodeDependencies", () => { @@ -872,7 +944,7 @@ describe("Configuration Management Module - Action Validations", () => { await (module as any).diffPackages(testContext, mockCommand, options); - expect(mockConfigCommandService.diffPackages).toHaveBeenCalledWith( + expect(mockT2tcCommandService.diffPackages).toHaveBeenCalledWith( "package.zip", undefined, undefined, undefined ); }); @@ -885,7 +957,7 @@ describe("Configuration Management Module - Action Validations", () => { await (module as any).diffPackages(testContext, mockCommand, options); - expect(mockConfigCommandService.diffPackages).toHaveBeenCalledWith( + expect(mockT2tcCommandService.diffPackages).toHaveBeenCalledWith( "package.zip", undefined, undefined, true ); }); @@ -898,7 +970,7 @@ describe("Configuration Management Module - Action Validations", () => { await (module as any).diffPackages(testContext, mockCommand, options); - expect(mockConfigCommandService.diffPackages).toHaveBeenCalledWith( + expect(mockT2tcCommandService.diffPackages).toHaveBeenCalledWith( "package.zip", true, undefined, undefined ); }); @@ -911,7 +983,7 @@ describe("Configuration Management Module - Action Validations", () => { await (module as any).diffPackages(testContext, mockCommand, options); - expect(mockConfigCommandService.diffPackages).toHaveBeenCalledWith( + expect(mockT2tcCommandService.diffPackages).toHaveBeenCalledWith( "package.zip", undefined, "1.0.0", undefined ); }); @@ -925,7 +997,7 @@ describe("Configuration Management Module - Action Validations", () => { await (module as any).diffPackages(testContext, mockCommand, options); - expect(mockConfigCommandService.diffPackages).toHaveBeenCalledWith( + expect(mockT2tcCommandService.diffPackages).toHaveBeenCalledWith( "package.zip", true, "STAGING", undefined ); }); diff --git a/tests/commands/t2tc/module.spec.ts b/tests/commands/t2tc/module.spec.ts new file mode 100644 index 00000000..270adfdb --- /dev/null +++ b/tests/commands/t2tc/module.spec.ts @@ -0,0 +1,213 @@ +import Module = require("../../../src/commands/t2tc/module"); +import { Command, OptionValues } from "commander"; +import { T2tcCommandService } from "../../../src/commands/t2tc/t2tc-command.service"; +import { testContext } from "../../utls/test-context"; +import { createMockConfigurator } from "../../utls/configurator-mock"; + +jest.mock("../../../src/commands/t2tc/t2tc-command.service"); + +describe("T2TC Module", () => { + let module: Module; + let mockCommand: Command; + let mockT2tcCommandService: jest.Mocked; + + beforeEach(() => { + jest.clearAllMocks(); + module = new Module(); + mockCommand = {} as Command; + + mockT2tcCommandService = { + listPackages: jest.fn().mockResolvedValue(undefined), + batchExportPackages: jest.fn().mockResolvedValue(undefined), + batchImportPackages: jest.fn().mockResolvedValue(undefined), + diffPackages: jest.fn().mockResolvedValue(undefined), + } as any; + + (T2tcCommandService as jest.MockedClass).mockImplementation(() => mockT2tcCommandService); + }); + + describe("register", () => { + it("registers the t2tc and package command groups without throwing", () => { + const mockConfigurator = createMockConfigurator(); + + expect(() => new Module().register(testContext, mockConfigurator)).not.toThrow(); + + expect(mockConfigurator.command).toHaveBeenCalledWith("t2tc"); + expect(mockConfigurator.command).toHaveBeenCalledWith("package"); + expect(mockConfigurator.command).toHaveBeenCalledWith("list"); + expect(mockConfigurator.command).toHaveBeenCalledWith("export"); + expect(mockConfigurator.command).toHaveBeenCalledWith("import"); + expect(mockConfigurator.command).toHaveBeenCalledWith("diff"); + }); + + it("wires an action handler for every leaf subcommand", () => { + const mockConfigurator = createMockConfigurator(); + + new Module().register(testContext, mockConfigurator); + + // t2tc package list / export / import / diff + const expectedLeafCommands = 4; + expect(mockConfigurator.action).toHaveBeenCalledTimes(expectedLeafCommands); + for (const call of mockConfigurator.action.mock.calls) { + expect(typeof call[0]).toBe("function"); + } + }); + }); + + describe("listPackages", () => { + it("throws when both --packageKeys and --keysByVersion are provided", async () => { + const options: OptionValues = { + packageKeys: ["package1"], + keysByVersion: ["package2.1.0.0"], + }; + + await expect( + (module as any).listPackages(testContext, mockCommand, options) + ).rejects.toThrow("Please provide either --packageKeys or --keysByVersion, but not both."); + + expect(mockT2tcCommandService.listPackages).not.toHaveBeenCalled(); + }); + + it("throws when --staging is combined with an incompatible filter", async () => { + const options: OptionValues = { + staging: true, + packageKeys: ["package1"], + }; + + await expect( + (module as any).listPackages(testContext, mockCommand, options) + ).rejects.toThrow("Staging parameter is not compatible with"); + + expect(mockT2tcCommandService.listPackages).not.toHaveBeenCalled(); + }); + + it("delegates to T2tcCommandService with the provided options", async () => { + const options: OptionValues = { + json: true, + flavors: ["STUDIO"], + packageKeys: ["package1"], + }; + + await (module as any).listPackages(testContext, mockCommand, options); + + expect(mockT2tcCommandService.listPackages).toHaveBeenCalledWith( + true, + ["STUDIO"], + undefined, + ["package1"], + undefined, + undefined, + undefined, + undefined, + undefined + ); + }); + }); + + describe("batchExportPackages", () => { + it("throws when both --packageKeys and --keysByVersion are provided", async () => { + const options: OptionValues = { + packageKeys: ["package1"], + keysByVersion: ["package2.1.0.0"], + }; + + await expect( + (module as any).batchExportPackages(testContext, mockCommand, options) + ).rejects.toThrow("Please provide either --packageKeys or --keysByVersion, but not both."); + + expect(mockT2tcCommandService.batchExportPackages).not.toHaveBeenCalled(); + }); + + it("throws when neither --packageKeys nor --keysByVersion is provided", async () => { + const options: OptionValues = {}; + + await expect( + (module as any).batchExportPackages(testContext, mockCommand, options) + ).rejects.toThrow("Please provide either --packageKeys or --keysByVersion, but not both."); + + expect(mockT2tcCommandService.batchExportPackages).not.toHaveBeenCalled(); + }); + + it("throws when a Git profile is provided without a Git branch", async () => { + const options: OptionValues = { + packageKeys: ["package1"], + gitProfile: "my-git-profile", + }; + + await expect( + (module as any).batchExportPackages(testContext, mockCommand, options) + ).rejects.toThrow("Please specify a branch using --gitBranch when using a Git profile."); + + expect(mockT2tcCommandService.batchExportPackages).not.toHaveBeenCalled(); + }); + + it("delegates to T2tcCommandService and defaults withDependencies to false", async () => { + const options: OptionValues = { + packageKeys: ["package1"], + unzip: true, + }; + + await (module as any).batchExportPackages(testContext, mockCommand, options); + + expect(mockT2tcCommandService.batchExportPackages).toHaveBeenCalledWith( + ["package1"], + undefined, + false, + undefined, + true + ); + }); + }); + + describe("batchImportPackages", () => { + it("throws when a Git profile is provided without a Git branch", async () => { + const options: OptionValues = { + gitProfile: "my-git-profile", + }; + + await expect( + (module as any).batchImportPackages(testContext, mockCommand, options) + ).rejects.toThrow("Please specify a branch using --gitBranch when using a Git profile."); + + expect(mockT2tcCommandService.batchImportPackages).not.toHaveBeenCalled(); + }); + + it("delegates to T2tcCommandService with the provided options", async () => { + const options: OptionValues = { + file: "export.zip", + overwrite: true, + validate: true, + }; + + await (module as any).batchImportPackages(testContext, mockCommand, options); + + expect(mockT2tcCommandService.batchImportPackages).toHaveBeenCalledWith( + "export.zip", + undefined, + true, + undefined, + true + ); + }); + }); + + describe("diffPackages", () => { + it("delegates to T2tcCommandService with the provided options", async () => { + const options: OptionValues = { + file: "export.zip", + hasChanges: true, + baseVersion: "STAGING", + json: true, + }; + + await (module as any).diffPackages(testContext, mockCommand, options); + + expect(mockT2tcCommandService.diffPackages).toHaveBeenCalledWith( + "export.zip", + true, + "STAGING", + true + ); + }); + }); +}); diff --git a/tests/commands/configuration-management/config-diff.spec.ts b/tests/commands/t2tc/t2tc-package-diff.spec.ts similarity index 92% rename from tests/commands/configuration-management/config-diff.spec.ts rename to tests/commands/t2tc/t2tc-package-diff.spec.ts index 93f9f604..f7d5a4ef 100644 --- a/tests/commands/configuration-management/config-diff.spec.ts +++ b/tests/commands/t2tc/t2tc-package-diff.spec.ts @@ -9,7 +9,7 @@ import { 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 { T2tcCommandService } from "../../../src/commands/t2tc/t2tc-command.service"; import { testContext } from "../../utls/test-context"; import { loggingTestTransport, mockWriteFileSync } from "../../jest.setup"; import { FileService } from "../../../src/core/utils/file-service"; @@ -81,7 +81,7 @@ describe("Config diff", () => { mockAxiosPost("https://myTeam.celonis.cloud/package-manager/api/core/packages/diff/configuration/has-changes", diffResponse); - await new ConfigCommandService(testContext).diffPackages("./packages.zip", true, null, false); + await new T2tcCommandService(testContext).diffPackages("./packages.zip", true, null, false); expect(loggingTestTransport.logMessages.length).toBe(1); expect(loggingTestTransport.logMessages[0].message).toContain( @@ -92,7 +92,7 @@ describe("Config diff", () => { it("Should show diff on terminal with hasChanges set to false and jsonResponse false", async () => { const diffResponse = mockZipDiff("https://myTeam.celonis.cloud/package-manager/api/core/packages/diff/configuration"); - await new ConfigCommandService(testContext).diffPackages("./packages.zip", false, null, false); + await new T2tcCommandService(testContext).diffPackages("./packages.zip", false, null, false); expect(loggingTestTransport.logMessages.length).toBe(1); expect(loggingTestTransport.logMessages[0].message).toContain( @@ -103,7 +103,7 @@ describe("Config diff", () => { it("Should compare with specified version", async () => { const diffResponse = mockZipDiff("https://myTeam.celonis.cloud/package-manager/api/core/packages/diff/configuration?baseVersion=1.0.0"); - await new ConfigCommandService(testContext).diffPackages("./packages.zip", false, "1.0.0", false); + await new T2tcCommandService(testContext).diffPackages("./packages.zip", false, "1.0.0", false); expect(loggingTestTransport.logMessages.length).toBe(1); expect(loggingTestTransport.logMessages[0].message).toContain( @@ -114,7 +114,7 @@ describe("Config diff", () => { it("Should generate a json file with diff info when hasChanges is set to false and jsonResponse is set to true", async () => { const diffResponse = mockZipDiff("https://myTeam.celonis.cloud/package-manager/api/core/packages/diff/configuration"); - await new ConfigCommandService(testContext).diffPackages("./packages.zip", false, null, true); + await new T2tcCommandService(testContext).diffPackages("./packages.zip", false, null, true); const expectedFileName = loggingTestTransport.logMessages[0].message.split(FileService.fileDownloadedMessage)[1]; @@ -145,7 +145,7 @@ describe("Config diff", () => { mockAxiosPost("https://myTeam.celonis.cloud/package-manager/api/core/packages/diff/configuration/has-changes", diffResponse); - await new ConfigCommandService(testContext).diffPackages("./packages.zip", true, null, true); + await new T2tcCommandService(testContext).diffPackages("./packages.zip", true, null, true); const expectedFileName = loggingTestTransport.logMessages[0].message.split(FileService.fileDownloadedMessage)[1]; diff --git a/tests/commands/configuration-management/config-export.spec.ts b/tests/commands/t2tc/t2tc-package-export.spec.ts similarity index 96% rename from tests/commands/configuration-management/config-export.spec.ts rename to tests/commands/t2tc/t2tc-package-export.spec.ts index 58f64b75..1c58008c 100644 --- a/tests/commands/configuration-management/config-export.spec.ts +++ b/tests/commands/t2tc/t2tc-package-export.spec.ts @@ -3,7 +3,7 @@ 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"; +} from "../../../src/commands/t2tc/batch-export-import.constants"; import { DependencyTransport, NodeConfiguration, NodeExportTransport, PackageManifestTransport, StudioPackageManifest, VariableManifestTransport, @@ -15,7 +15,7 @@ import { 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 { T2tcCommandService } from "../../../src/commands/t2tc/t2tc-command.service"; import { testContext } from "../../utls/test-context"; import { ConfigUtils } from "../../utls/config-utils"; import { PacmanApiUtils } from "../../utls/pacman-api.utils"; @@ -60,7 +60,7 @@ describe("Config export", () => { 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"], undefined, true, null, null); + await new T2tcCommandService(testContext).batchExportPackages(["key-1", "key-2", "key-3"], undefined, true, null, null); const expectedFileName = loggingTestTransport.logMessages[0].message.split(FileService.fileDownloadedMessage)[1]; expect(fs.openSync).toHaveBeenCalledWith(expectedFileName, expect.anything(), expect.anything()); @@ -122,7 +122,7 @@ describe("Config export", () => { mockAxiosGet(`https://myTeam.celonis.cloud/package-manager/api/nodes/by-package-key/${secondStudioPackage.key}/variables/runtime-values?appMode=VIEWER`, []); const branchName = "my-branch"; - await new ConfigCommandService(testContext).batchExportPackages(["key-1", "key-2", "key-3"], undefined, true, branchName, null); + await new T2tcCommandService(testContext).batchExportPackages(["key-1", "key-2", "key-3"], undefined, true, branchName, null); expect(mockGitServicePushToBranch).toHaveBeenCalledTimes(1); expect(mockGitServicePushToBranch).toHaveBeenCalledWith( @@ -160,7 +160,7 @@ describe("Config export", () => { 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(undefined, ["key-1.1.0.1", "key-2.1.0.4", "key-3.1.2.0"], true, null, null); + await new T2tcCommandService(testContext).batchExportPackages(undefined, ["key-1.1.0.1", "key-2.1.0.4", "key-3.1.2.0"], true, null, null); const expectedFileName = loggingTestTransport.logMessages[0].message.split(FileService.fileDownloadedMessage)[1]; expect(fs.openSync).toHaveBeenCalledWith(expectedFileName, expect.anything(), expect.anything()); @@ -286,7 +286,7 @@ describe("Config export", () => { 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"], undefined, true, null, null); + await new T2tcCommandService(testContext).batchExportPackages(["key-1", "key-2"], undefined, true, null, null); const expectedFileName = loggingTestTransport.logMessages[0].message.split(FileService.fileDownloadedMessage)[1]; expect(fs.openSync).toHaveBeenCalledWith(expectedFileName, expect.anything(), expect.anything()); @@ -450,7 +450,7 @@ describe("Config export", () => { 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"], undefined, true, null, null); + await new T2tcCommandService(testContext).batchExportPackages(["key-1", "key-2"], undefined, true, null, null); const expectedFileName = loggingTestTransport.logMessages[0].message.split(FileService.fileDownloadedMessage)[1]; expect(fs.openSync).toHaveBeenCalledWith(expectedFileName, expect.anything(), expect.anything()); @@ -611,7 +611,7 @@ describe("Config export", () => { 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(undefined, ["key-1.1.0.2", "key-2.1.0.3"], true, null, null); + await new T2tcCommandService(testContext).batchExportPackages(undefined, ["key-1.1.0.2", "key-2.1.0.3"], true, null, null); const expectedFileName = loggingTestTransport.logMessages[0].message.split(FileService.fileDownloadedMessage)[1]; expect(fs.openSync).toHaveBeenCalledWith(expectedFileName, expect.anything(), expect.anything()); @@ -703,7 +703,7 @@ describe("Config export", () => { 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"], undefined, true, null, null); + await new T2tcCommandService(testContext).batchExportPackages(["key-1", "key-2"], undefined, true, null, null); const expectedFileName = loggingTestTransport.logMessages[0].message.split(FileService.fileDownloadedMessage)[1]; expect(fs.openSync).toHaveBeenCalledWith(expectedFileName, expect.anything(), expect.anything()); @@ -745,7 +745,7 @@ describe("Config export", () => { 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(undefined, ["key-1.1.0.2", "key-2.1.0.3"], true, null, null); + await new T2tcCommandService(testContext).batchExportPackages(undefined, ["key-1.1.0.2", "key-2.1.0.3"], true, null, null); const expectedFileName = loggingTestTransport.logMessages[0].message.split(FileService.fileDownloadedMessage)[1]; expect(fs.openSync).toHaveBeenCalledWith(expectedFileName, expect.anything(), expect.anything()); @@ -861,7 +861,7 @@ describe("Config export", () => { 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"], undefined, true, null, null); + await new T2tcCommandService(testContext).batchExportPackages(["key-1", "key-2"], undefined, true, null, null); const expectedFileName = loggingTestTransport.logMessages[0].message.split(FileService.fileDownloadedMessage)[1]; expect(fs.openSync).toHaveBeenCalledWith(expectedFileName, expect.anything(), expect.anything()); @@ -1002,7 +1002,7 @@ describe("Config export", () => { 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(undefined, ["key-1.1.0.4", "key-2.1.0.5"], true, null, null); + await new T2tcCommandService(testContext).batchExportPackages(undefined, ["key-1.1.0.4", "key-2.1.0.5"], true, null, null); const expectedFileName = loggingTestTransport.logMessages[0].message.split(FileService.fileDownloadedMessage)[1]; expect(fs.openSync).toHaveBeenCalledWith(expectedFileName, expect.anything(), expect.anything()); @@ -1110,7 +1110,7 @@ describe("Config export", () => { 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"], undefined, true, null, null); + await new T2tcCommandService(testContext).batchExportPackages(["key_with_underscores_1"], undefined, true, null, null); const expectedFileName = loggingTestTransport.logMessages[0].message.split(FileService.fileDownloadedMessage)[1]; expect(fs.openSync).toHaveBeenCalledWith(expectedFileName, expect.anything(), expect.anything()); @@ -1209,7 +1209,7 @@ describe("Config export", () => { 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(undefined, ["key_with_underscores_1.1.0.6"], true, null, null); + await new T2tcCommandService(testContext).batchExportPackages(undefined, ["key_with_underscores_1.1.0.6"], true, null, null); const expectedFileName = loggingTestTransport.logMessages[0].message.split(FileService.fileDownloadedMessage)[1]; expect(fs.openSync).toHaveBeenCalledWith(expectedFileName, expect.anything(), expect.anything()); @@ -1252,7 +1252,7 @@ describe("Config export", () => { 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"], undefined, false, null, null); + await new T2tcCommandService(testContext).batchExportPackages(["key-1", "key-2", "key-3"], undefined, false, null, null); const expectedFileName = loggingTestTransport.logMessages[0].message.split(FileService.fileDownloadedMessage)[1]; expect(fs.openSync).toHaveBeenCalledWith(expectedFileName, expect.anything(), expect.anything()); @@ -1268,7 +1268,7 @@ describe("Config export", () => { mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/core/packages/versions/export/batch?packageKeysWithVersion=key-1.1.0.3&packageKeysWithVersion=key-2.1.0.4&packageKeysWithVersion=key-3.1.0.5&withDependencies=false", exportedPackagesZip.toBuffer()); mockAxiosPost("https://myTeam.celonis.cloud/package-manager/api/core/packages/export/batch/variables-with-assignments", []); - await new ConfigCommandService(testContext).batchExportPackages(undefined, ["key-1.1.0.3", "key-2.1.0.4", "key-3.1.0.5"], false, null, null); + await new T2tcCommandService(testContext).batchExportPackages(undefined, ["key-1.1.0.3", "key-2.1.0.4", "key-3.1.0.5"], false, null, null); const expectedFileName = loggingTestTransport.logMessages[0].message.split(FileService.fileDownloadedMessage)[1]; expect(fs.openSync).toHaveBeenCalledWith(expectedFileName, expect.anything(), expect.anything()); diff --git a/tests/commands/configuration-management/config-import.spec.ts b/tests/commands/t2tc/t2tc-package-import.spec.ts similarity index 93% rename from tests/commands/configuration-management/config-import.spec.ts rename to tests/commands/t2tc/t2tc-package-import.spec.ts index 8bb9e80e..f5430f63 100644 --- a/tests/commands/configuration-management/config-import.spec.ts +++ b/tests/commands/t2tc/t2tc-package-import.spec.ts @@ -16,7 +16,7 @@ import { mockedAxiosInstance, mockedPostRequestBodyByUrl, } from "../../utls/http-requests-mock"; -import { ConfigCommandService } from "../../../src/commands/configuration-management/config-command.service"; +import { T2tcCommandService } from "../../../src/commands/t2tc/t2tc-command.service"; import { testContext } from "../../utls/test-context"; import { loggingTestTransport, mockWriteFileSync } from "../../jest.setup"; import { SpaceTransport } from "../../../src/commands/studio/interfaces/space.interface"; @@ -26,7 +26,7 @@ import { } from "../../../src/commands/studio/interfaces/package-manager.interfaces"; import { BatchExportImportConstants -} from "../../../src/commands/configuration-management/interfaces/batch-export-import.constants"; +} from "../../../src/commands/t2tc/batch-export-import.constants"; import { ConfigUtils } from "../../utls/config-utils"; import { stringify } from "../../../src/core/utils/json"; import { GitService } from "../../../src/core/git-profile/git/git.service"; @@ -62,7 +62,7 @@ describe("Config import", () => { mockAxiosPost("https://myTeam.celonis.cloud/package-manager/api/core/packages/import/batch", importResponse); - await new ConfigCommandService(testContext).batchImportPackages("./export_file.zip", null, overwrite, null); + await new T2tcCommandService(testContext).batchImportPackages("./export_file.zip", null, overwrite, null); const expectedFileName = loggingTestTransport.logMessages[0].message.split(LOG_MESSAGE)[1]; expect(mockWriteFileSync).toHaveBeenCalledWith(path.resolve(process.cwd(), expectedFileName), JSON.stringify(importResponse), {encoding: "utf-8", mode: 0o600}); @@ -95,7 +95,7 @@ describe("Config import", () => { mockAxiosPost("https://myTeam.celonis.cloud/package-manager/api/core/packages/import/batch", importResponse); - await new ConfigCommandService(testContext).batchImportPackages(null, null, overwrite, branchName); + await new T2tcCommandService(testContext).batchImportPackages(null, null, overwrite, branchName); const expectedFileName = loggingTestTransport.logMessages[0].message.split(LOG_MESSAGE)[1]; expect(mockWriteFileSync).toHaveBeenCalledWith(path.resolve(process.cwd(), expectedFileName), JSON.stringify(importResponse), {encoding: "utf-8", mode: 0o600}); @@ -134,7 +134,7 @@ describe("Config import", () => { mockAxiosPost("https://myTeam.celonis.cloud/package-manager/api/core/packages/import/batch", importResponse); - await new ConfigCommandService(testContext).batchImportPackages(null, null, overwrite, branchName); + await new T2tcCommandService(testContext).batchImportPackages(null, null, overwrite, branchName); const expectedFileName = loggingTestTransport.logMessages[0].message.split(LOG_MESSAGE)[1]; expect(mockWriteFileSync).toHaveBeenCalledWith(path.resolve(process.cwd(), expectedFileName), JSON.stringify(importResponse), {encoding: "utf-8", mode: 0o600}); @@ -178,7 +178,7 @@ describe("Config import", () => { mockAxiosPost("https://myTeam.celonis.cloud/package-manager/api/core/packages/import/batch", importResponse); - await new ConfigCommandService(testContext).batchImportPackages("./export_file.zip", null, true, null); + await new T2tcCommandService(testContext).batchImportPackages("./export_file.zip", null, true, null); const expectedFileName = loggingTestTransport.logMessages[0].message.split(LOG_MESSAGE)[1]; expect(mockWriteFileSync).toHaveBeenCalledWith(path.resolve(process.cwd(), expectedFileName), JSON.stringify(importResponse), {encoding: "utf-8", mode: 0o600}); @@ -207,7 +207,7 @@ describe("Config import", () => { mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/spaces", [space]); await expect( - new ConfigCommandService(testContext).batchImportPackages("./export_file.zip", null, true, null) + new T2tcCommandService(testContext).batchImportPackages("./export_file.zip", null, true, null) ).rejects.toThrow("Provided space ID does not exist."); }) @@ -250,7 +250,7 @@ describe("Config import", () => { mockAxiosPost("https://myTeam.celonis.cloud/package-manager/api/core/packages/import/batch", importResponse); - await new ConfigCommandService(testContext).batchImportPackages("./export_file.zip", null, true, null); + await new T2tcCommandService(testContext).batchImportPackages("./export_file.zip", null, true, null); const expectedFileName = loggingTestTransport.logMessages[0].message.split(LOG_MESSAGE)[1]; expect(mockWriteFileSync).toHaveBeenCalledWith(path.resolve(process.cwd(), expectedFileName), JSON.stringify(importResponse), {encoding: "utf-8", mode: 0o600}); expect(mockedAxiosInstance.put).toHaveBeenCalledWith("https://myTeam.celonis.cloud/package-manager/api/packages/node-id/move/spaceId", expect.anything(), expect.anything()); @@ -287,7 +287,7 @@ describe("Config import", () => { mockAxiosPost("https://myTeam.celonis.cloud/package-manager/api/core/packages/import/batch", importResponse); - await new ConfigCommandService(testContext).batchImportPackages("./export_file.zip", null, true, null); + await new T2tcCommandService(testContext).batchImportPackages("./export_file.zip", null, true, null); const expectedFileName = loggingTestTransport.logMessages[0].message.split(LOG_MESSAGE)[1]; expect(mockWriteFileSync).toHaveBeenCalledWith(path.resolve(process.cwd(), expectedFileName), JSON.stringify(importResponse), {encoding: "utf-8", mode: 0o600}); @@ -334,7 +334,7 @@ describe("Config import", () => { mockAxiosPost("https://myTeam.celonis.cloud/package-manager/api/core/packages/import/batch", importResponse); - await new ConfigCommandService(testContext).batchImportPackages("./export_file.zip", null, true, null); + await new T2tcCommandService(testContext).batchImportPackages("./export_file.zip", null, true, null); const expectedFileName = loggingTestTransport.logMessages[0].message.split(LOG_MESSAGE)[1]; expect(mockWriteFileSync).toHaveBeenCalledWith(path.resolve(process.cwd(), expectedFileName), JSON.stringify(importResponse), {encoding: "utf-8", mode: 0o600}); @@ -358,7 +358,7 @@ describe("Config import", () => { }); await expect( - new ConfigCommandService(testContext).batchImportPackages("./export_file.zip", null, false, null) + new T2tcCommandService(testContext).batchImportPackages("./export_file.zip", null, false, null) ).rejects.toThrow('Failed to handle zip file "./export_file.zip": uncompressed size 5.00 GB exceeds the 4 GB limit.'); }) @@ -413,7 +413,7 @@ describe("Config import", () => { mockAxiosPost(assignVariablesUrl, {}); mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/packages", [node]); - await new ConfigCommandService(testContext).batchImportPackages("./export_file.zip", null, true, null); + await new T2tcCommandService(testContext).batchImportPackages("./export_file.zip", null, true, null); const expectedFileName = loggingTestTransport.logMessages[0].message.split(LOG_MESSAGE)[1]; expect(mockWriteFileSync).toHaveBeenCalledWith(path.resolve(process.cwd(), expectedFileName), JSON.stringify(importResponse), {encoding: "utf-8", mode: 0o600}); @@ -440,7 +440,7 @@ describe("Config import", () => { mockAxiosPost("https://myTeam.celonis.cloud/package-manager/api/core/packages/import/batch", importResponse); - await new ConfigCommandService(testContext).batchImportPackages("./export_file.zip", null, false, null, true); + await new T2tcCommandService(testContext).batchImportPackages("./export_file.zip", null, false, null, true); expect(mockedAxiosInstance.post).toHaveBeenCalledWith( expect.stringContaining("/package-manager/api/core/packages/import/batch"), diff --git a/tests/commands/configuration-management/config-list.spec.ts b/tests/commands/t2tc/t2tc-package-list.spec.ts similarity index 90% rename from tests/commands/configuration-management/config-list.spec.ts rename to tests/commands/t2tc/t2tc-package-list.spec.ts index d09ec265..3af2ec6c 100644 --- a/tests/commands/configuration-management/config-list.spec.ts +++ b/tests/commands/t2tc/t2tc-package-list.spec.ts @@ -1,7 +1,7 @@ import * as path from "path"; 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 { T2tcCommandService } from "../../../src/commands/t2tc/t2tc-command.service"; import { testContext } from "../../utls/test-context"; import { loggingTestTransport, mockWriteFileSync } from "../../jest.setup"; import { @@ -33,7 +33,7 @@ describe("Config list", () => { mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/core/packages/export/list?" + urlParams.toString(), [firstPackage, secondPackage]); - await new ConfigCommandService(testContext).listPackages(false, flavorsArray, false, [], undefined, null, null, false, false); + await new T2tcCommandService(testContext).listPackages(false, flavorsArray, false, [], undefined, null, null, false, false); expect(loggingTestTransport.logMessages.length).toBe(2); expect(loggingTestTransport.logMessages[0].message).toContain(`${firstPackage.name} - Key: "${firstPackage.key}"`); @@ -50,7 +50,7 @@ describe("Config list", () => { mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/core/packages/export/list?withDependencies=false&includeBranches=false", [{...firstPackage}, {...secondPackage}]); mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/packages/with-variable-assignments?type=DATA_MODEL", [studioPackage]); - await new ConfigCommandService(testContext).listPackages(true, [], false, [], undefined, null, null, false, false); + await new T2tcCommandService(testContext).listPackages(true, [], false, [], undefined, null, null, false, false); const expectedFileName = loggingTestTransport.logMessages[0].message.split(FileService.fileDownloadedMessage)[1]; @@ -96,7 +96,7 @@ describe("Config list", () => { }; mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/compute-pools/data-models/details", [dataModelDetailResponse]); - await new ConfigCommandService(testContext).listPackages(true, [], true, [], undefined, null, null, false, false); + await new T2tcCommandService(testContext).listPackages(true, [], true, [], undefined, null, null, false, false); const expectedFileName = loggingTestTransport.logMessages[0].message.split(FileService.fileDownloadedMessage)[1]; @@ -121,7 +121,7 @@ describe("Config list", () => { 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).listPackages(true, [], false, [firstPackage.key, secondPackage.key], undefined, null, null, false, false); + await new T2tcCommandService(testContext).listPackages(true, [], false, [firstPackage.key, secondPackage.key], undefined, null, null, false, false); const expectedFileName = loggingTestTransport.logMessages[0].message.split(FileService.fileDownloadedMessage)[1]; @@ -167,7 +167,7 @@ describe("Config list", () => { }; mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/compute-pools/data-models/details", [dataModelDetailResponse]); - await new ConfigCommandService(testContext).listPackages(true, [], true, [firstPackage.key, secondPackage.key], undefined, null, null, false, false); + await new T2tcCommandService(testContext).listPackages(true, [], true, [firstPackage.key, secondPackage.key], undefined, null, null, false, false); const expectedFileName = loggingTestTransport.logMessages[0].message.split(FileService.fileDownloadedMessage)[1]; @@ -191,7 +191,7 @@ describe("Config list", () => { mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/core/packages/export/list-by-variable-value?variableValue=1&includeBranches=false", [{...firstPackage}, {...secondPackage}]); mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/packages/with-variable-assignments?type=DATA_MODEL", []); - await new ConfigCommandService(testContext).listPackages(false, [], false, [], undefined, "1", null, false, false); + await new T2tcCommandService(testContext).listPackages(false, [], false, [], undefined, "1", null, false, false); expect(loggingTestTransport.logMessages.length).toBe(2); expect(loggingTestTransport.logMessages[0].message).toContain(`${firstPackage.name} - Key: "${firstPackage.key}"`); @@ -205,7 +205,7 @@ describe("Config list", () => { mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/core/packages/export/list-by-variable-value?variableValue=1&includeBranches=false", [{...firstPackage}, {...secondPackage}]); mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/packages/with-variable-assignments?type=DATA_MODEL", []); - await new ConfigCommandService(testContext).listPackages(true, [], false, [], undefined, "1", null, false, false); + await new T2tcCommandService(testContext).listPackages(true, [], false, [], undefined, "1", null, false, false); const expectedFileName = loggingTestTransport.logMessages[0].message.split(FileService.fileDownloadedMessage)[1]; @@ -225,7 +225,7 @@ describe("Config list", () => { [firstPackage, secondPackage] ); - await new ConfigCommandService(testContext).listPackages(false, [], false, undefined, keysByVersion, null, null, false, false); + await new T2tcCommandService(testContext).listPackages(false, [], false, undefined, keysByVersion, null, null, false, false); expect(loggingTestTransport.logMessages.length).toBe(2); expect(loggingTestTransport.logMessages[0].message).toContain(`${firstPackage.name} - Key: "${firstPackage.key}"`); @@ -242,7 +242,7 @@ describe("Config list", () => { [firstPackage, secondPackage] ); - await new ConfigCommandService(testContext).listPackages(false, [], true, undefined, keysByVersion, null, null, false, false); + await new T2tcCommandService(testContext).listPackages(false, [], true, undefined, keysByVersion, null, null, false, false); expect(loggingTestTransport.logMessages.length).toBe(2); }) @@ -260,7 +260,7 @@ describe("Config list", () => { ); mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/packages/with-variable-assignments?type=DATA_MODEL", [studioPackage]); - await new ConfigCommandService(testContext).listPackages(true, [], false, [], keysByVersion, null, null, false, false); + await new T2tcCommandService(testContext).listPackages(true, [], false, [], keysByVersion, null, null, false, false); const expectedFileName = loggingTestTransport.logMessages[0].message.split(FileService.fileDownloadedMessage)[1]; @@ -286,7 +286,7 @@ describe("Config list", () => { mockAxiosGet("https://myTeam.celonis.cloud/pacman/api/core/staging/packages/export/list?" + urlParams.toString(), [firstPackage, secondPackage]); - await new ConfigCommandService(testContext).listPackages(false, ["STUDIO"], false, [], undefined, null, null, false, true); + await new T2tcCommandService(testContext).listPackages(false, ["STUDIO"], false, [], undefined, null, null, false, true); expect(loggingTestTransport.logMessages.length).toBe(2); expect(loggingTestTransport.logMessages[0].message).toContain(`${firstPackage.name} - Key: "${firstPackage.key}"`); @@ -302,7 +302,7 @@ describe("Config list", () => { mockAxiosGet("https://myTeam.celonis.cloud/pacman/api/core/staging/packages/export/list?" + urlParams.toString(), [firstPackage, secondPackage]); - await new ConfigCommandService(testContext).listPackages(false, null, false, [], undefined, null, null, false, true); + await new T2tcCommandService(testContext).listPackages(false, null, false, [], undefined, null, null, false, true); expect(loggingTestTransport.logMessages.length).toBe(2); expect(loggingTestTransport.logMessages[0].message).toContain(`${firstPackage.name} - Key: "${firstPackage.key}"`); @@ -319,7 +319,7 @@ describe("Config list", () => { mockAxiosGet("https://myTeam.celonis.cloud/pacman/api/core/staging/packages/export/list?" + urlParams.toString(), [firstPackage, secondPackage]); - await new ConfigCommandService(testContext).listPackages(true, ["STUDIO"], false, [], undefined, null, null, false, true); + await new T2tcCommandService(testContext).listPackages(true, ["STUDIO"], false, [], undefined, null, null, false, true); const expectedFileName = loggingTestTransport.logMessages[0].message.split(FileService.fileDownloadedMessage)[1];