Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DP-1602 Add data pool export / batch import commands #133

Merged
merged 18 commits into from
Aug 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 70 additions & 0 deletions DOCUMENTATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -376,4 +376,74 @@ the same command as with pushing other assets to Studio:
// Push analysis to Studio
content-cli push bookmarks -p my-profile-name --id 73d39112-73ae-4bbe-8051-3c0f14e065ec --file studio_analysis_bookmarks_39c5bb7b-b486-4230-ab01-854a17ddbff2.json
```

### Data Pool export / import commands

#### Export Data Pool

In order to pull a Data Pool you can execute the following command:

```content-cli export data-pool --id <replace-with-pool-id> --profile local --outputToJsonFile```

_Note_: The ```--outputToJsonFile``` is optional. If specified, the exported data pool is saved in a JSON file. The
command output will give you all the details.

#### Batch Import multiple Data Pools

In order to batch push a list of data pools use the following command:

```content-cli import data-pools --jsonFile ./request.json --profile dev1 --outputToJsonFile```

#### Input

* The ```request.json``` file contains the batch import JSON request.
* The ```--batchImport``` flag enables import of multiple Data Pools together with data shared accross Data Pools
(for example, imported data connections, cross-pool scheduling triggers). If this flag
isn't used, then the old implementation is used which doesn't support the import of shared
objects. Thus, for the old implementation, the imported Data Pools that rely on shared objects
will not be functional.

* The JSON request looks the following way:

```
{
"targetTeamDomain": "dev1",
"dataPoolImports": [
{
"sourcePoolId": "850728cc-c679-4925-954a-87fb39abb12b",
"targetPoolId": "80a1389d-50c5-4976-ad6e-fb5b7a2b5517",
"dataSourceMappings": {
"69e7c6b8-a36c-48ee-8dba-9bb89baf41dd": "98b4b2d9-898d-4b72-aeb9-ebd87c097cb3",
"2ec72366-c84f-4896-9f7a-9db1891aeb54": "082a754f-e971-44d8-993a-053707e4a307"
},
"dataPool": {...}
},
{
"sourcePoolId": "510fde4e-4a53-4a4e-9331-31038bcaf891",
"targetPoolId": "1b9b368b-e0df-4e74-99e8-59e2febe9687",
"dataSourceMappings": {
"e9359d63-5ccf-4f0d-8da3-24cda8a42c01": "096c0280-4cb9-4279-a003-b77698287aba",
"8a1b2a1e-1015-4c7b-8cdc-b8efea1ad894": "e971e8d1-96d4-488a-9e1d-6cff0c4a9813"
},
"dataPool": {...}
}
]
}
```

In the above JSON:
1. ```targetTeamDomain```: the destination team domain, into which the data pools data is pushed.
2. ```sourcePoolId```: the source Data Pool ID.
2. ```targetPoolId```: the target Data Pool ID to which the source Data Pool ID should be mapped to.
3. ```dataSourceMappings```: the source Data Source ID to destination Data Source ID mappings.
4. ```dataPool```: the Data Pool data exported via the `export data-pool` command

#### Output

The command outputs an import report.

If the `--outputToJsonFile` option is specified, the import report will be written to a JSON file.
The command output will give you all the details.


|--------------------------------------------------------------------------------------------------------------------------------|
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@celonis/content-cli",
"version": "0.4.2",
"version": "0.4.3",
"description": "CLI Tool to help manage content in Celonis EMS",
"main": "content-cli.js",
"bin": {
Expand Down
19 changes: 15 additions & 4 deletions src/api/data-pool-api.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import {httpClientV2} from "../services/http-client-service.v2";
import {FatalError, logger} from "../util/logger";
import {DataPoolPageTransport} from "../interfaces/data-pool-manager.interfaces";

import { httpClientV2 } from "../services/http-client-service.v2";
import { FatalError } from "../util/logger";
import { DataPoolInstallVersionReport, DataPoolPageTransport } from "../interfaces/data-pool-manager.interfaces";

class DataPoolApi {
public static readonly INSTANCE = new DataPoolApi();
Expand All @@ -11,6 +10,18 @@ class DataPoolApi {
throw new FatalError(`Problem getting data pools: : ${e}`);
});
}

public async executeDataPoolsBatchImport(importRequest: string): Promise<DataPoolInstallVersionReport> {
return httpClientV2.post("/integration/api/pool/batch-import", importRequest).catch(e => {
throw new FatalError(`Data Pool batch import failed: : ${e}`);
});
}

public async exportDataPool(poolId: string): Promise<string> {
return httpClientV2.get(`/integration/api/pools/${poolId}/v2/export`).catch(e => {
throw new FatalError(`Data Pool export failed: : ${e}`);
});
}
}

export const dataPoolApi = DataPoolApi.INSTANCE;
15 changes: 11 additions & 4 deletions src/commands/data-pool.command.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {ContentService} from "../services/content.service";
import {DataPoolManagerFactory} from "../content/factory/data-pool-manager.factory";
import {dataPoolService} from "../services/data-pool/data-pool-service";
import { ContentService } from "../services/content.service";
import { DataPoolManagerFactory } from "../content/factory/data-pool-manager.factory";
import { dataPoolService } from "../services/data-pool/data-pool-service";

export class DataPoolCommand {
private contentService = new ContentService();
Expand All @@ -14,10 +14,18 @@ export class DataPoolCommand {
await this.contentService.push(profile, this.dataPoolManagerFactory.createManager(null, filename));
}

public async exportDataPool(poolId: string, outputToJsonFile: boolean): Promise<void> {
await dataPoolService.exportDataPool(poolId, outputToJsonFile);
}

public async pushDataPools(profile: string): Promise<void> {
await this.contentService.batchPush(profile, this.dataPoolManagerFactory.createManagers());
}

public async batchImportDataPools(requestFile: string, outputToJsonFile: boolean): Promise<void> {
await dataPoolService.batchImportDataPools(requestFile, outputToJsonFile);
}

public async updateDataPool(profile: string, id: string, filename: string): Promise<any> {
await this.contentService.update(profile, this.dataPoolManagerFactory.createManager(id, filename));
}
Expand All @@ -29,5 +37,4 @@ export class DataPoolCommand {
await dataPoolService.listDataPools();
}
}

}
50 changes: 31 additions & 19 deletions src/content-cli-export.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import {CommanderStatic} from "commander";
import {PackageCommand} from "./commands/package.command";
import {logger} from "./util/logger";
import {contextService} from "./services/context.service";
import * as commander from "commander";
import { CommanderStatic } from "commander";
import { PackageCommand } from "./commands/package.command";
import { logger } from "./util/logger";
import { DataPoolCommand } from "./commands/data-pool.command";
import { ContextInitializer } from "./util/context-initializer";

export class Export {
public static packages(program: CommanderStatic): CommanderStatic {
Expand All @@ -13,36 +14,47 @@ export class Export {
.requiredOption("--packageKeys <packageKeys...>", "Exports only given package keys")
.option("--includeDependencies", "Include variables and dependencies", "")
.action(async cmd => {
await new PackageCommand().batchExportPackages(cmd.packageKeys, cmd.includeDependencies)
await new PackageCommand().batchExportPackages(cmd.packageKeys, cmd.includeDependencies);
process.exit();
});

return program;
}
}

const options = commander.parseOptions(process.argv)
const indexOfProfileOption = options.unknown.indexOf('-p') !== -1 ? options.unknown.indexOf('-p') : options.unknown.indexOf('--profile');
public static dataPool(program: CommanderStatic): CommanderStatic {
program
.command("data-pool")
.description("Command to export a data pool")
.option("-p, --profile <profile>", "Profile which you want to use to export the data pool")
.requiredOption("--id <id>", "ID of the data pool you want to export")
.option("--outputToJsonFile", "Output the exported data pool to a JSON file")
.action(async cmd => {
await new DataPoolCommand().exportDataPool(cmd.id, cmd.outputToJsonFile);
process.exit();
});

process.on("unhandledRejection", (e, promise) => {
logger.error(e.toString());
})
return program;
}
}

contextService.resolveProfile(options.unknown[indexOfProfileOption + 1]).then(() => {
getAllCommands();
}, ()=> {
const loadCommands = () => {
getAllCommands();
}).catch(e => {
console.log(e)
});
};

ContextInitializer.initContext()
.then(loadCommands, loadCommands)
.catch(e => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we calling the loadCommands twice here? I haven't seen this syntax before/

Copy link
Contributor Author

@IvanGandacov IvanGandacov Aug 3, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's exactly as it was before, just instead of writting the () => { getCommands(); } twice, I put it into a variable.

logger.error(e);
});

if (!process.argv.slice(2).length) {
commander.outputHelp();
process.exit(1);
}

function getAllCommands() {
function getAllCommands(): void {
Export.packages(commander);
Export.dataPool(commander);

commander.parse(process.argv);
}
}
56 changes: 35 additions & 21 deletions src/content-cli-import.ts
Original file line number Diff line number Diff line change
@@ -1,49 +1,63 @@
import {CommanderStatic} from "commander";
import {PackageCommand} from "./commands/package.command";
import {logger} from "./util/logger";
import {contextService} from "./services/context.service";
import * as commander from "commander";
import { CommanderStatic } from "commander";
import { PackageCommand } from "./commands/package.command";
import { DataPoolCommand } from "./commands/data-pool.command";
import { ContextInitializer } from "./util/context-initializer";
import { logger } from "./util/logger";

export class Import {

public static packages(program: CommanderStatic): CommanderStatic {
program
.command("packages")
.description("Command to import all given packages")
.option("-p, --profile <profile>", "Profile which you want to use to list packages")
.option("--spaceMappings <spaceMappings...>", "List of mappings for importing packages to different target spaces. Mappings should follow format 'packageKey:targetSpaceKey'")
.option(
"--spaceMappings <spaceMappings...>",
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just prettifying

"List of mappings for importing packages to different target spaces. Mappings should follow format 'packageKey:targetSpaceKey'"
)
.requiredOption("-f, --file <file>", "Exported packages file (relative path)")
.action(async cmd => {
await new PackageCommand().batchImportPackages(cmd.spaceMappings, cmd.file)
await new PackageCommand().batchImportPackages(cmd.spaceMappings, cmd.file);
process.exit();
});

return program;
}
}

const options = commander.parseOptions(process.argv)
const indexOfProfileOption = options.unknown.indexOf('-p') !== -1 ? options.unknown.indexOf('-p') : options.unknown.indexOf('--profile');
public static dataPools(program: CommanderStatic): CommanderStatic {
program
.command("data-pools")
.description("Command to batch import multiple data pools with their objects and dependencies")
.option("-p, --profile <profile>", "Profile which you want to use to import the data pools")
.requiredOption("-f, --jsonFile <file>", "The file with the JSON data pool batch import request")
.option("--outputToJsonFile", "Output the batch import result in a JSON file")
.action(async cmd => {
await new DataPoolCommand().batchImportDataPools(cmd.jsonFile, cmd.outputToJsonFile);
process.exit();
});

process.on("unhandledRejection", (e, promise) => {
logger.error(e.toString());
})
return program;
}
}

contextService.resolveProfile(options.unknown[indexOfProfileOption + 1]).then(() => {
const loadCommands = () => {
getAllCommands();
}, ()=> {
getAllCommands();
}).catch(e => {
console.log(e)
});
};

ContextInitializer.initContext()
.then(loadCommands, loadCommands)
.catch(e => {
logger.error(e);
});

if (!process.argv.slice(2).length) {
commander.outputHelp();
process.exit(1);
}

function getAllCommands() {
function getAllCommands(): void {
Import.packages(commander);
Import.dataPools(commander);

commander.parse(process.argv);
}
}
1 change: 0 additions & 1 deletion src/content-cli-pull.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,4 +129,3 @@ if (!process.argv.slice(2).length) {
commander.outputHelp();
process.exit(1);
}

1 change: 0 additions & 1 deletion src/content-cli-push.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,6 @@ class Push {
await new DataPoolCommand().pushDataPool(cmd.profile, cmd.file);
process.exit();
});

return program;
}

Expand Down
4 changes: 2 additions & 2 deletions src/content/manager/base.manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ export abstract class BaseManager {

protected writeToFile(data: any): string {
const filename = this.getConfig().exportFileName;
this.writeToFileWithGivenName(data, filename)
this.writeToFileWithGivenName(data, filename);
return filename;
}

Expand All @@ -114,7 +114,7 @@ export abstract class BaseManager {
return filename;
}

protected writeToFileWithGivenName(data: any, filename:string): void {
protected writeToFileWithGivenName(data: any, filename: string): void {
fs.writeFileSync(path.resolve(process.cwd(), filename), this.getSerializedFileContent(data), {
encoding: "utf-8",
});
Expand Down
6 changes: 5 additions & 1 deletion src/interfaces/data-pool-manager.interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,8 @@ export declare class DataPoolPageTransport {
pageSize: number;
pageNumber: number;
totalCount: number;
}
}

export declare class DataPoolInstallVersionReport {
dataModelIdMappings: Map<String, String>;
}
2 changes: 1 addition & 1 deletion src/interfaces/manager-config.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ export interface ManagerConfig {
onPushSuccessMessage?: (data: any) => string;
onUpdateSuccessMessage?: () => string;
onFindAll?: (data: any) => void;
onFindAllAndExport?: (data: any) => void
onFindAllAndExport?: (data: any) => void;
}
Loading
Loading