Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
68 changes: 68 additions & 0 deletions DOCUMENTATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -569,6 +569,74 @@ 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
Comment thread
promeris marked this conversation as resolved.
Outdated
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 --profile my-profile-name
Comment thread
ZgjimHaziri marked this conversation as resolved.
Outdated
```
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 --profile my-profile-name
```

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

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 --profile my-profile-name
```

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

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 --profile my-profile-name
```

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 --profile my-profile-name
```

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.1.4",
Comment thread
promeris marked this conversation as resolved.
Outdated
"description": "CLI Tool to help manage content in Celonis Platform",
"main": "content-cli.js",
"bin": {
Expand Down
123 changes: 123 additions & 0 deletions src/commands/deployment/deployment-api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
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> {
if (!request.packageKey || request.packageKey.trim() === "") {
throw new FatalError("Package key is required to create a deployment.");
}
if (!request.packageVersion || request.packageVersion.trim() === "") {
throw new FatalError("Package version is required to create a deployment.");
}
if (!request.deployableType || request.deployableType.trim() === "") {
throw new FatalError("Deployable type is required to create a deployment.");
}
if (!request.targetId || request.targetId.trim() === "") {
throw new FatalError("Target ID is required to create a deployment.");
}
Comment thread
ZgjimHaziri marked this conversation as resolved.
Outdated

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

public async getDeployments(filter: GetDeploymentsRequest): Promise<DeploymentTransport[]> {
const queryParams = new URLSearchParams();

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 = 100,
offset = 0
): Promise<DeploymentTransport[]> {
const queryParams = new URLSearchParams();
queryParams.set("limit", limit.toString());
queryParams.set("offset", offset.toString());

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}`);
});
}
}
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;
}
108 changes: 108 additions & 0 deletions src/commands/deployment/deployment.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
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): Promise<void> {
const filter: GetDeploymentsRequest = {
packageKey,
targetId,
deployableType,
status: fromString(status),
createdBy
};
const deployments = await this.deploymentApi.getDeployments(filter);

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?: number, offset?:number): 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