From 57aa3f7c5216807450adece9f3362eb513b779f1 Mon Sep 17 00:00:00 2001 From: Alexander Probst Date: Thu, 7 May 2026 11:55:01 +0200 Subject: [PATCH 1/3] SP-160: added optional includeBranches parameter to API methods --- .../configuration-management/api/batch-import-export-api.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/commands/configuration-management/api/batch-import-export-api.ts b/src/commands/configuration-management/api/batch-import-export-api.ts index e262e1a..c485859 100644 --- a/src/commands/configuration-management/api/batch-import-export-api.ts +++ b/src/commands/configuration-management/api/batch-import-export-api.ts @@ -16,10 +16,11 @@ export class BatchImportExportApi { this.httpClient = () => context.httpClient; } - public async findAllActivePackages(flavors: string[], withDependencies: boolean = false): Promise { + public async findAllActivePackages(flavors: string[], withDependencies: boolean = false, includeBranches: boolean = false): Promise { const queryParams = new URLSearchParams(); queryParams.set("withDependencies", withDependencies.toString()); + queryParams.set("includeBranches", includeBranches.toString()); flavors.forEach(flavor => queryParams.append("flavors", flavor)) return this.httpClient().get(`/package-manager/api/core/packages/export/list?${queryParams.toString()}`).catch(e => { @@ -27,13 +28,14 @@ export class BatchImportExportApi { }); } - public async findActivePackagesByVariableValue(flavors: string[], variableValue: string, variableType: string): Promise { + public async findActivePackagesByVariableValue(flavors: string[], variableValue: string, variableType: string, includeBranches: boolean = false): Promise { const queryParams = new URLSearchParams(); queryParams.set("variableValue", variableValue); if (variableType) { queryParams.set("variableType", variableType); } + queryParams.set("includeBranches", includeBranches.toString()); flavors.forEach(flavor => queryParams.append("flavors", flavor)) return this.httpClient().get(`/package-manager/api/core/packages/export/list-by-variable-value?${queryParams.toString()}`).catch(e => { From dd03ef5baf3b932ff15c534fedc58c6740ea9408 Mon Sep 17 00:00:00 2001 From: Alexander Probst Date: Thu, 7 May 2026 16:12:04 +0200 Subject: [PATCH 2/3] SP-160: added branch awareness for listing packages by introducing --branches option --- .../batch-import-export.service.ts | 16 +++---- .../config-command.service.ts | 14 +++--- .../configuration-management/module.ts | 22 ++++++++- .../config-list.spec.ts | 29 +++++------ .../configuration-management/module.spec.ts | 48 +++++++++++++++++++ 5 files changed, 99 insertions(+), 30 deletions(-) diff --git a/src/commands/configuration-management/batch-import-export.service.ts b/src/commands/configuration-management/batch-import-export.service.ts index e5c570a..a9bd5ce 100644 --- a/src/commands/configuration-management/batch-import-export.service.ts +++ b/src/commands/configuration-management/batch-import-export.service.ts @@ -35,14 +35,14 @@ export class BatchImportExportService { this.gitService = new GitService(context); } - public async listActivePackages(flavors: string[]): Promise { - const activePackages = await this.batchImportExportApi.findAllActivePackages(flavors); + public async listActivePackages(flavors: string[], includeBranches: boolean): Promise { + const activePackages = await this.batchImportExportApi.findAllActivePackages(flavors, false, includeBranches); activePackages.forEach(pkg => { logger.info(`${pkg.name} - Key: "${pkg.key}"`) }); } - public async findAndExportListOfPackages(flavors: string[], packageKeys: string[], keysByVersion: string[], withDependencies: boolean): Promise { + public async findAndExportListOfPackages(flavors: string[], packageKeys: string[], keysByVersion: string[], withDependencies: boolean, includeBranches: boolean): Promise { let packagesToExport: PackageExportTransport[]; if (keysByVersion.length) { @@ -50,7 +50,7 @@ export class BatchImportExportService { } else if (packageKeys.length) { packagesToExport = await this.batchImportExportApi.findActivePackagesByKeys(packageKeys, withDependencies); } else { - packagesToExport = await this.batchImportExportApi.findAllActivePackages(flavors, withDependencies); + packagesToExport = await this.batchImportExportApi.findAllActivePackages(flavors, withDependencies, includeBranches); } packagesToExport = await this.studioService.getExportPackagesWithStudioData(packagesToExport, withDependencies); @@ -159,16 +159,16 @@ export class BatchImportExportService { logger.info("Config import report file: " + reportFileName); } - public async findAndExportListOfActivePackagesByVariableValue(flavors: string[], variableValue: string, variableType: string): Promise { - let packagesToExport = await this.batchImportExportApi.findActivePackagesByVariableValue(flavors, variableValue, variableType); + public async findAndExportListOfActivePackagesByVariableValue(flavors: string[], variableValue: string, variableType: string, includeBranches: boolean): Promise { + let packagesToExport = await this.batchImportExportApi.findActivePackagesByVariableValue(flavors, variableValue, variableType, includeBranches); packagesToExport = await this.studioService.getExportPackagesWithStudioData(packagesToExport, false); this.exportListOfPackages(packagesToExport); } - public async listActivePackagesByVariableValue(flavors: string[], variableValue: string, variableType: string) : Promise { - const packagesByVariableValue = await this.batchImportExportApi.findActivePackagesByVariableValue(flavors, variableValue, variableType); + public async listActivePackagesByVariableValue(flavors: string[], variableValue: string, variableType: string, includeBranches: boolean) : Promise { + const packagesByVariableValue = await this.batchImportExportApi.findActivePackagesByVariableValue(flavors, variableValue, variableType, includeBranches); packagesByVariableValue.forEach(pkg => { logger.info(`${pkg.name} - Key: "${pkg.key}"`) }); diff --git a/src/commands/configuration-management/config-command.service.ts b/src/commands/configuration-management/config-command.service.ts index 5f595a6..ee0a31a 100644 --- a/src/commands/configuration-management/config-command.service.ts +++ b/src/commands/configuration-management/config-command.service.ts @@ -16,18 +16,18 @@ export class ConfigCommandService { this.diffService = new DiffService(context); } - public async listPackages(jsonResponse: boolean, flavors: string[], withDependencies: boolean, packageKeys: string[], keysByVersion: string[], variableValue: string, variableType: string): Promise { + public async listPackages(jsonResponse: boolean, flavors: string[], withDependencies: boolean, packageKeys: string[], keysByVersion: string[], variableValue: string, variableType: string, includeBranches: boolean): Promise { if (variableValue) { - await this.listPackagesByVariableValue(jsonResponse, flavors, variableValue, variableType); + await this.listPackagesByVariableValue(jsonResponse, flavors, variableValue, variableType, includeBranches); return; } if (jsonResponse) { - await this.batchImportExportService.findAndExportListOfPackages(flavors ?? [], packageKeys ?? [], keysByVersion ?? [], withDependencies); + await this.batchImportExportService.findAndExportListOfPackages(flavors ?? [], packageKeys ?? [], keysByVersion ?? [], withDependencies, includeBranches); } else if (keysByVersion) { await this.batchImportExportService.listPackagesByKeysWithVersion(keysByVersion, withDependencies); } else { - await this.batchImportExportService.listActivePackages(flavors ?? []); + await this.batchImportExportService.listActivePackages(flavors ?? [], includeBranches); } } @@ -82,11 +82,11 @@ export class ConfigCommandService { return this.diffService.diffPackages(file, hasChanges, jsonResponse); } - private async listPackagesByVariableValue(jsonResponse: boolean, flavors: string[], variableValue: string, variableType: string): Promise { + private async listPackagesByVariableValue(jsonResponse: boolean, flavors: string[], variableValue: string, variableType: string, includeBranches: boolean): Promise { if (jsonResponse) { - await this.batchImportExportService.findAndExportListOfActivePackagesByVariableValue(flavors ?? [], variableValue, variableType ) + await this.batchImportExportService.findAndExportListOfActivePackagesByVariableValue(flavors ?? [], variableValue, variableType, includeBranches) } else { - await this.batchImportExportService.listActivePackagesByVariableValue(flavors ?? [], variableValue, variableType); + await this.batchImportExportService.listActivePackagesByVariableValue(flavors ?? [], variableValue, variableType, includeBranches); } } } diff --git a/src/commands/configuration-management/module.ts b/src/commands/configuration-management/module.ts index 4562a4c..ddb1c48 100644 --- a/src/commands/configuration-management/module.ts +++ b/src/commands/configuration-management/module.ts @@ -26,6 +26,7 @@ class Module extends IModule { .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) .action(this.listPackages); configCommand.command("export") @@ -185,7 +186,26 @@ class Module extends IModule { if (options.packageKeys && options.keysByVersion) { throw new Error("Please provide either --packageKeys or --keysByVersion, but not both."); } - await new ConfigCommandService(context).listPackages(options.json, options.flavors, options.withDependencies, options.packageKeys, options.keysByVersion, options.variableValue, options.variableType); + + if (options.branches) { + if (options.packageKeys) { + throw new Error("Please provide either --packageKeys or --branches, but not both."); + } + + if (options.keysByVersion) { + throw new Error("Please provide either --keysByVersion or --branches, but not both."); + } + } + + await new ConfigCommandService(context).listPackages( + options.json, + options.flavors, + options.withDependencies, + options.packageKeys, + options.keysByVersion, + options.variableValue, + options.variableType, + options.branches); } private async batchExportPackages(context: Context, command: Command, options: OptionValues): Promise { diff --git a/tests/commands/configuration-management/config-list.spec.ts b/tests/commands/configuration-management/config-list.spec.ts index 6a2c77e..53e93a0 100644 --- a/tests/commands/configuration-management/config-list.spec.ts +++ b/tests/commands/configuration-management/config-list.spec.ts @@ -28,11 +28,12 @@ describe("Config list", () => { const urlParams = new URLSearchParams(); urlParams.set("withDependencies", "false"); + urlParams.set("includeBranches", "false"); flavorsArray.forEach(flavor => urlParams.append("flavors", flavor)); mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/core/packages/export/list?" + urlParams.toString(), [firstPackage, secondPackage]); - await new ConfigCommandService(testContext).listPackages(false, flavorsArray, false, [], undefined, null, null); + await new ConfigCommandService(testContext).listPackages(false, flavorsArray, false, [], undefined, null, null, false); expect(loggingTestTransport.logMessages.length).toBe(2); expect(loggingTestTransport.logMessages[0].message).toContain(`${firstPackage.name} - Key: "${firstPackage.key}"`); @@ -46,10 +47,10 @@ describe("Config list", () => { const studioPackage: ContentNodeTransport = PacmanApiUtils.buildContentNodeTransport("key-1", "spaceId-1"); - mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/core/packages/export/list?withDependencies=false", [{...firstPackage}, {...secondPackage}]); + mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/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); + await new ConfigCommandService(testContext).listPackages(true, [], false, [], undefined, null, null, false); const expectedFileName = loggingTestTransport.logMessages[0].message.split(FileService.fileDownloadedMessage)[1]; @@ -69,7 +70,7 @@ describe("Config list", () => { const firstPackage = PacmanApiUtils.buildPackageExportTransport("key-1", "name-1"); const secondPackage = PacmanApiUtils.buildPackageExportTransport("key-2", "name-2"); - mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/core/packages/export/list?withDependencies=true", [{...firstPackage}, {...secondPackage}]); + mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/core/packages/export/list?withDependencies=true&includeBranches=false", [{...firstPackage}, {...secondPackage}]); mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/packages/with-variable-assignments?type=DATA_MODEL", []); const dataModelVariableAssignmentResponse: PackageWithVariableAssignments = { @@ -95,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); + await new ConfigCommandService(testContext).listPackages(true, [], true, [], undefined, null, null, false); const expectedFileName = loggingTestTransport.logMessages[0].message.split(FileService.fileDownloadedMessage)[1]; @@ -120,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); + await new ConfigCommandService(testContext).listPackages(true, [], false, [firstPackage.key, secondPackage.key], undefined, null, null, false); const expectedFileName = loggingTestTransport.logMessages[0].message.split(FileService.fileDownloadedMessage)[1]; @@ -166,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); + await new ConfigCommandService(testContext).listPackages(true, [], true, [firstPackage.key, secondPackage.key], undefined, null, null, false); const expectedFileName = loggingTestTransport.logMessages[0].message.split(FileService.fileDownloadedMessage)[1]; @@ -187,10 +188,10 @@ describe("Config list", () => { const secondPackage = PacmanApiUtils.buildPackageExportTransport("key-2", "name-2"); - mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/core/packages/export/list-by-variable-value?variableValue=1", [{...firstPackage}, {...secondPackage}]); + mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/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); + await new ConfigCommandService(testContext).listPackages(false, [], false, [], undefined, "1", null, false); expect(loggingTestTransport.logMessages.length).toBe(2); expect(loggingTestTransport.logMessages[0].message).toContain(`${firstPackage.name} - Key: "${firstPackage.key}"`); @@ -201,10 +202,10 @@ describe("Config list", () => { const firstPackage = PacmanApiUtils.buildPackageExportTransport("key-1", "name-1"); const secondPackage = PacmanApiUtils.buildPackageExportTransport("key-2", "name-2"); - mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/core/packages/export/list-by-variable-value?variableValue=1", [{...firstPackage}, {...secondPackage}]); + mockAxiosGet("https://myTeam.celonis.cloud/package-manager/api/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); + await new ConfigCommandService(testContext).listPackages(true, [], false, [], undefined, "1", null, false); const expectedFileName = loggingTestTransport.logMessages[0].message.split(FileService.fileDownloadedMessage)[1]; @@ -224,7 +225,7 @@ describe("Config list", () => { [firstPackage, secondPackage] ); - await new ConfigCommandService(testContext).listPackages(false, [], false, undefined, keysByVersion, null, null); + await new ConfigCommandService(testContext).listPackages(false, [], false, undefined, keysByVersion, null, null, false); expect(loggingTestTransport.logMessages.length).toBe(2); expect(loggingTestTransport.logMessages[0].message).toContain(`${firstPackage.name} - Key: "${firstPackage.key}"`); @@ -241,7 +242,7 @@ describe("Config list", () => { [firstPackage, secondPackage] ); - await new ConfigCommandService(testContext).listPackages(false, [], true, undefined, keysByVersion, null, null); + await new ConfigCommandService(testContext).listPackages(false, [], true, undefined, keysByVersion, null, null, false); expect(loggingTestTransport.logMessages.length).toBe(2); }) @@ -259,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); + await new ConfigCommandService(testContext).listPackages(true, [], false, [], keysByVersion, null, null, false); 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 214b38f..9d0c175 100644 --- a/tests/commands/configuration-management/module.spec.ts +++ b/tests/commands/configuration-management/module.spec.ts @@ -57,6 +57,32 @@ describe("Configuration Management Module - Action Validations", () => { expect(mockConfigCommandService.listPackages).not.toHaveBeenCalled(); }); + it("should throw error when both packageKeys and branches are provided", async () => { + const options: OptionValues = { + packageKeys: ["package1", "package2"], + branches: true + }; + + await expect((module as any).listPackages(testContext, mockCommand, options)).rejects.toThrow( + "Please provide either --packageKeys or --branches, but not both." + ); + + expect(mockConfigCommandService.listPackages).not.toHaveBeenCalled(); + }); + + it("should throw error when both keysByVersion and branches are provided", async () => { + const options: OptionValues = { + keysByVersion: ["package3.1.0.0", "package4.1.0.0"], + branches: true + }; + + await expect((module as any).listPackages(testContext, mockCommand, options)).rejects.toThrow( + "Please provide either --keysByVersion or --branches, but not both." + ); + + expect(mockConfigCommandService.listPackages).not.toHaveBeenCalled(); + }); + it("should pass validation when only packageKeys is provided", async () => { const options: OptionValues = { packageKeys: ["package1", "package2"], @@ -72,6 +98,7 @@ describe("Configuration Management Module - Action Validations", () => { ["package1", "package2"], undefined, undefined, + undefined, undefined ); }); @@ -91,9 +118,30 @@ describe("Configuration Management Module - Action Validations", () => { undefined, ["package3.1.0.0", "package4.1.0.0"], undefined, + undefined, undefined ); }); + + it("should pass validation when only branches is provided", async () => { + const options: OptionValues = { + branches: true, + json: true, + }; + + await (module as any).listPackages(testContext, mockCommand, options); + + expect(mockConfigCommandService.listPackages).toHaveBeenCalledWith( + true, + undefined, + undefined, + undefined, + undefined, + undefined, + undefined, + true + ); + }); }); }); From a09015c8a7e97138c64e01248104fdc3f0b760f7 Mon Sep 17 00:00:00 2001 From: Alexander Probst Date: Fri, 8 May 2026 10:50:52 +0200 Subject: [PATCH 3/3] SP-160: removed restriction for combining --branches parameter --- .../configuration-management/module.ts | 10 ------ .../configuration-management/module.spec.ts | 31 ++----------------- 2 files changed, 3 insertions(+), 38 deletions(-) diff --git a/src/commands/configuration-management/module.ts b/src/commands/configuration-management/module.ts index ddb1c48..00aaf7f 100644 --- a/src/commands/configuration-management/module.ts +++ b/src/commands/configuration-management/module.ts @@ -187,16 +187,6 @@ class Module extends IModule { throw new Error("Please provide either --packageKeys or --keysByVersion, but not both."); } - if (options.branches) { - if (options.packageKeys) { - throw new Error("Please provide either --packageKeys or --branches, but not both."); - } - - if (options.keysByVersion) { - throw new Error("Please provide either --keysByVersion or --branches, but not both."); - } - } - await new ConfigCommandService(context).listPackages( options.json, options.flavors, diff --git a/tests/commands/configuration-management/module.spec.ts b/tests/commands/configuration-management/module.spec.ts index 9d0c175..b87f0a2 100644 --- a/tests/commands/configuration-management/module.spec.ts +++ b/tests/commands/configuration-management/module.spec.ts @@ -57,32 +57,6 @@ describe("Configuration Management Module - Action Validations", () => { expect(mockConfigCommandService.listPackages).not.toHaveBeenCalled(); }); - it("should throw error when both packageKeys and branches are provided", async () => { - const options: OptionValues = { - packageKeys: ["package1", "package2"], - branches: true - }; - - await expect((module as any).listPackages(testContext, mockCommand, options)).rejects.toThrow( - "Please provide either --packageKeys or --branches, but not both." - ); - - expect(mockConfigCommandService.listPackages).not.toHaveBeenCalled(); - }); - - it("should throw error when both keysByVersion and branches are provided", async () => { - const options: OptionValues = { - keysByVersion: ["package3.1.0.0", "package4.1.0.0"], - branches: true - }; - - await expect((module as any).listPackages(testContext, mockCommand, options)).rejects.toThrow( - "Please provide either --keysByVersion or --branches, but not both." - ); - - expect(mockConfigCommandService.listPackages).not.toHaveBeenCalled(); - }); - it("should pass validation when only packageKeys is provided", async () => { const options: OptionValues = { packageKeys: ["package1", "package2"], @@ -122,8 +96,9 @@ describe("Configuration Management Module - Action Validations", () => { undefined ); }); - - it("should pass validation when only branches is provided", async () => { + }); + describe("branches validation", () => { + it("should pass validation when branches is provided", async () => { const options: OptionValues = { branches: true, json: true,