Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
1 change: 1 addition & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
/src/core @celonis/astro
/src/commands/configuration-management/ @celonis/astro
/src/commands/profile/ @celonis/astro
/src/commands/deployment/ @celonis/astro
/src/commands/action-flows/ @celonis/process-automation
/tests/commands/action-flows/ @celonis/process-automation
/src/commands/analysis/ @celonis/process-analytics
Expand Down
70 changes: 70 additions & 0 deletions DOCUMENTATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -569,6 +569,76 @@ This mapping should be saved and then used during import.
Since the format of the variables.json file on import is the same JSON structure as the list variables result, you can either map the values to the variables.json file for each variable, or replace the variables.json file with the result of the listing & mapping altogether.
If the mapping of variables is skipped, you should delete the variables.json file before importing.

### Deployment commands (beta)
The **deployment** command group allows you to create deployments, list their history, check active deployments, and retrieve deployables and targets.

#### Create Deployment
Create a new deployment for a given package version in a target, based on the deployable type.
```
content-cli deployment create --packageKey myPackage --packageVersion 1.0.0 --deployableType app-package --targetId targetId
```
The command will return the created deployment, or an error if the deployment could not be created.
It is also possible to use the `--json` option for writing the extended response to a file that gets created in the working directory.

#### List Deployment History
List the deployment history, optionally filtered by package, target, type, status, or creator.
```
content-cli deployment list history
```

Additional filtering options:
- `--packageKey <packageKey>` – Filter deployment history by package key
- `--targetId <targetId>` – Filter deployment history by target ID
- `--deployableType <deployableType>` – Filter deployment history by deployable type
- `--status <status>` – Filter deployment history by status
- `--createdBy <createdBy>` – Filter deployment history by creator

The response is paginated, and the page size can be controlled with the `--limit` and `--offset` options (defaults to 100 and 0 respectively).

It is also possible to use the '--json' option for writing the extended response to a file that gets created in the working directory.

#### List Active Deployments
Get the active deployment(s) for a given target or package.

For getting the active deployment for a target the `--targetId` option should be used:
```
content-cli deployment list active --targetId targetId
```

For getting the active deployment for a package the `--packageKey` option should be used:
```
content-cli deployment list active --packageKey myPackage
```

The `--targetIds` option can be used to filter the response by multiple target IDs. The option should be used with a comma-separated list of target IDs.

The response of listing active deployments for a package will return all active deployments for that package across all targets.
The response is paginated, and the page size can be controlled with the `--limit` and `--offset` options (defaults to 100 and 0 respectively).

For both options, it is also possible to use the `--json` option for writing the extended response to a file that gets created in the working directory.

Note: You must provide either `--packageKey` or `--targetId`, not both.

#### List Deployables

List all available deployables.

```
content-cli deployment list deployables --flavor STUDIO
```

The `--flavor` option can be used to filter the deployables by flavor. The available flavors are: **STUDIO** and **OCDM**.
It is also possible to use the `--json` option for writing the extended response to a file that gets created in the working directory.

#### List Targets
List all targets for a given deployable type (and optionally a package key).

```
content-cli deployment list targets --deployableType app-package
```

The `--packageKey` option can be used to get targets for a specific package.
It is also possible to use the `--json` option for writing the extended response to a file that gets created in the working directory.

### Action Flows commands

Expand Down
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": "1.1.3",
"version": "1.2.0",
"description": "CLI Tool to help manage content in Celonis Platform",
"main": "content-cli.js",
"bin": {
Expand Down
121 changes: 121 additions & 0 deletions src/commands/deployment/deployment-api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import { HttpClient } from "../../core/http/http-client";
import { Context } from "../../core/command/cli-context";
import {
CreateDeploymentRequest,
DeployableTransport,
DeploymentStatus,
DeploymentTransport,
GetDeploymentsRequest,
TargetTransport,
} from "./deployment.interfaces";
import { FatalError } from "../../core/utils/logger";

export class DeploymentApi {
private httpClient: () => HttpClient;

constructor(context: Context) {
this.httpClient = () => context.httpClient;
}

public async createDeployment(request: CreateDeploymentRequest): Promise<DeploymentTransport> {
return this.httpClient()
.post("/pacman/api/deployments", request)
.catch((e) => {
throw new FatalError(`Problem creating deployment: ${e}`);
});
}

public async getDeployments(filter: GetDeploymentsRequest, limit?: string, offset?: string): Promise<DeploymentTransport[]> {
const queryParams = new URLSearchParams();
this.addPaginationParams(queryParams, limit, offset);

if (filter.packageKey) {
queryParams.set("packageKey", filter.packageKey);
}
if (filter.targetId) {
queryParams.set("targetId", filter.targetId);
}
if (filter.deployableType) {
queryParams.set("deployableType", filter.deployableType);
}
if (filter.status && filter.status !== DeploymentStatus.UNKNOWN) {
queryParams.set("status", filter.status);
}
if (filter.createdBy) {
queryParams.set("createdBy", filter.createdBy);
}

return this.httpClient()
.get(`/pacman/api/deployments/history?${queryParams.toString()}`)
.catch((e) => {
throw new FatalError(`Problem getting deployments: ${e}`);
});
}

public async getActiveDeploymentsForPackage(
packageKey: string,
targetIds?: string[],
limit?: string,
offset?: string
): Promise<DeploymentTransport[]> {
const queryParams = new URLSearchParams();
this.addPaginationParams(queryParams, limit, offset);

if (targetIds && targetIds.length > 0) {
queryParams.set("targetIds", targetIds.join(","));
}

return this.httpClient()
.get(`/pacman/api/deployments/packages/${packageKey}/active?${queryParams.toString()}`)
.catch((e) => {
throw new FatalError(`Problem getting active deployments for package ${packageKey}: ${e}`);
});
}

public async getActiveDeploymentForTarget(targetId: string): Promise<DeploymentTransport> {
return this.httpClient()
.get(`/pacman/api/deployments/targets/${targetId}/active`)
.catch((e) => {
throw new FatalError(`Problem getting active deployment for target ${targetId}: ${e}`);
});
}

public async getTargets(deployableType: string, packageKey?: string): Promise<TargetTransport[]> {
const queryParams = new URLSearchParams();
queryParams.set("deployableType", deployableType);

if (packageKey) {
queryParams.set("packageKey", packageKey);
}

return this.httpClient()
.get(`/pacman/api/deployments/targets?${queryParams.toString()}`)
.catch((e) => {
throw new FatalError(`Problem getting targets: ${e}`);
});
}

public async getDeployables(flavor?: string): Promise<DeployableTransport[]> {
const queryParams = new URLSearchParams();
if (flavor) {
queryParams.set("flavor", flavor);
}

return this.httpClient()
.get(`/pacman/api/deployments/deployables?${queryParams.toString()}`)
.catch((e) => {
throw new FatalError(`Problem getting deployables: ${e}`);
});
}

private addPaginationParams(queryParams: URLSearchParams, limit?: string, offset?: string): void {
const defaultLimit = 100;
const defaultOffset = 0;

const parsedLimit = limit !== undefined && !isNaN(Number(limit)) ? Number(limit) : defaultLimit;
queryParams.set("limit", parsedLimit.toString());

const parsedOffset = offset !== undefined && !isNaN(Number(offset)) ? Number(offset) : defaultOffset;
queryParams.set("offset", parsedOffset.toString());
}
}
53 changes: 53 additions & 0 deletions src/commands/deployment/deployment.interfaces.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
export interface DeploymentTransport {
id: string;
packageKey: string;
packageVersion: string;
deployableType: string;
target: TargetTransport;
status: DeploymentStatus;
statusMessage: string;
createdBy: string;
createdAt: string;
deployedAt: string;
}

export interface TargetTransport {
id: string;
name: string;
}

export interface DeployableTransport {
name: string;
type: string;
targetType: string;
flavor: string;
}

export enum DeploymentStatus {
UNKNOWN = "unknown",
PENDING = "PENDING",
IN_PROGRESS = "IN_PROGRESS",
SUCCESS = "SUCCESS",
FAILED = "FAILED",
}

export interface CreateDeploymentRequest {
packageKey: string;
packageVersion: string;
deployableType: string;
targetId: string;
}

export interface GetDeploymentsRequest {
packageKey: string;
targetId: string;
deployableType: string;
status: DeploymentStatus;
createdBy: string;
}

export function fromString(value: string): DeploymentStatus {
return (Object.values(DeploymentStatus) as string[]).includes(value)
? (value as DeploymentStatus)
: DeploymentStatus.UNKNOWN;
}
115 changes: 115 additions & 0 deletions src/commands/deployment/deployment.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import { DeploymentApi } from "./deployment-api";
import { CreateDeploymentRequest, fromString, GetDeploymentsRequest } from "./deployment.interfaces";
import { Context } from "../../core/command/cli-context";
import { fileService, FileService } from "../../core/utils/file-service";
import { logger } from "../../core/utils/logger";
import { v4 as uuidv4 } from "uuid";

export class DeploymentService {
private deploymentApi: DeploymentApi;

constructor(context: Context) {
this.deploymentApi = new DeploymentApi(context);
}

public async createDeployment(packageKey: string, packageVersion: string, deployableType: string, targetId: string, jsonResponse: boolean): Promise<void> {
const request: CreateDeploymentRequest = {
packageKey,
packageVersion,
deployableType,
targetId
};

const deployment = await this.deploymentApi.createDeployment(request);

if (jsonResponse) {
const filename = uuidv4() + ".json";
fileService.writeToFileWithGivenName(JSON.stringify(deployment), filename);
logger.info(FileService.fileDownloadedMessage + filename);
} else {
logger.info(`Deployment created with ID: ${deployment.id}, Status: ${deployment.status}`);
}
}

public async getDeployments(jsonResponse: boolean
,packageKey?: string
,targetId?: string
,deployableType?: string
,status?: string
,createdBy?: string
,limit?: string
,offset?: string): Promise<void> {
const filter: GetDeploymentsRequest = {
packageKey,
targetId,
deployableType,
status: fromString(status),
createdBy
};
const deployments = await this.deploymentApi.getDeployments(filter, limit, offset);

if (jsonResponse) {
const filename = uuidv4() + ".json";
fileService.writeToFileWithGivenName(JSON.stringify(deployments), filename);
logger.info(FileService.fileDownloadedMessage + filename);
} else {
deployments.forEach(deployment => {
logger.info(`ID: ${deployment.id}, Package: ${deployment.packageKey}, Version: ${deployment.packageVersion}, Status: ${deployment.status}, Created at: ${new Date(deployment.createdAt).toISOString()}`);
});
}
}

public async getActiveDeploymentForTarget(targetId: string, jsonResponse: boolean): Promise<void> {
const deployment = await this.deploymentApi.getActiveDeploymentForTarget(targetId);

if (jsonResponse) {
const filename = uuidv4() + ".json";
fileService.writeToFileWithGivenName(JSON.stringify(deployment), filename);
logger.info(FileService.fileDownloadedMessage + filename);
} else {
logger.info(`ID: ${deployment.id}, Package: ${deployment.packageKey}, Version: ${deployment.packageVersion}, Status: ${deployment.status}, Deployed at: ${new Date(deployment.deployedAt).toISOString()}`);
}
}

public async getActiveDeploymentsForPackage(packageKey: string, jsonResponse: boolean, targetIds?: string[], limit?: string, offset?: string): Promise<void> {
const deployments = await this.deploymentApi.getActiveDeploymentsForPackage(packageKey, targetIds, limit, offset);

if (jsonResponse) {
const filename = uuidv4() + ".json";
fileService.writeToFileWithGivenName(JSON.stringify(deployments), filename);
logger.info(FileService.fileDownloadedMessage + filename);
} else {
deployments.forEach(deployment => {
logger.info(`ID: ${deployment.id}, Package: ${deployment.packageKey}, Version: ${deployment.packageVersion}, Status: ${deployment.status}, Deployed at: ${deployment.deployedAt}`);
});
}
}

public async getTargets(jsonResponse: boolean, deployableType: string, packageKey?: string): Promise<void> {
const targets = await this.deploymentApi.getTargets(deployableType, packageKey);

if (jsonResponse) {
const filename = uuidv4() + ".json";
fileService.writeToFileWithGivenName(JSON.stringify(targets), filename);
logger.info(FileService.fileDownloadedMessage + filename);
} else {
targets.forEach(target => {
logger.info(`ID: ${target.id}, Name: ${target.name}`);
});
}
}

public async getDeployables(jsonResponse: boolean, flavor?: string): Promise<void> {
const deployables = await this.deploymentApi.getDeployables(flavor);

if (jsonResponse) {
const filename = uuidv4() + ".json";
fileService.writeToFileWithGivenName(JSON.stringify(deployables), filename);
logger.info(FileService.fileDownloadedMessage + filename);
} else {
deployables.forEach(deployable => {
logger.info(`Name: ${deployable.name}, Type: ${deployable.type}`);
});
}
}
}
Loading