Skip to content
Merged
Show file tree
Hide file tree
Changes from 38 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
6b2d53b
Adding minor comments
lippisch Apr 17, 2025
2145ce2
Profile is now loaded as part of the context
lippisch Apr 17, 2025
97a056d
Logger now writes logs into the config directory
lippisch Apr 21, 2025
1b0bbde
fix for --debug
lippisch Apr 21, 2025
101a96c
Migrated list command as an example implementation
lippisch Apr 22, 2025
0ab31e0
modules can extend any root level command
lippisch Apr 23, 2025
7c23b97
entry point file can be of 3 variants now, supporting foldername as p…
lippisch Apr 24, 2025
9fba40f
Merge remote-tracking branch 'origin/master' into fli-contribution-mo…
lippisch Apr 29, 2025
5ab1e34
Modules are now structured by owning team, not command structure
lippisch Apr 29, 2025
5f107c1
Base API with some refactored error handling
lippisch May 2, 2025
601623d
minor tweaks to align with documentation
lippisch May 2, 2025
0b60d45
TA-3749: Define foundation of the CLI
ZgjimHaziri May 13, 2025
38f86e7
TA-3749: Revert unnecessary change
ZgjimHaziri May 13, 2025
6f4f515
TA-3749: Revert unnecessary change
ZgjimHaziri May 13, 2025
128fcab
TA-3749: Revert unnecessary change
ZgjimHaziri May 13, 2025
b624e74
TA-3749: Revert unnecessary change
ZgjimHaziri May 13, 2025
f5e25b8
TA-3749: yarn-lock update
ZgjimHaziri May 13, 2025
5b4a283
TA-3749: Rever package.json update
ZgjimHaziri May 13, 2025
7f6cc1b
TA-3749: Move code to top level
ZgjimHaziri May 14, 2025
0726a23
Merge base branch
ZgjimHaziri May 14, 2025
4351014
TA-3749: Trigger build
ZgjimHaziri May 14, 2025
843f7b5
TA-3749: Comment test run
ZgjimHaziri May 14, 2025
d88b9a4
TA-3750: Setup command modules
ZgjimHaziri May 14, 2025
e3cb10f
TA-3750: Merge base branch insto TA-3750-setup-modules
ZgjimHaziri May 16, 2025
375f56c
TA-3750: Rename Pacman module to Config
ZgjimHaziri May 16, 2025
ba51325
TA-3750: Remove non-Astro codeowners
ZgjimHaziri May 16, 2025
d2ff658
TA-3750: Update Pacman module description
ZgjimHaziri May 16, 2025
633f4fd
TA-3750: Fix merge conflicts
ZgjimHaziri May 16, 2025
715fe90
TA-3750: Rename config module
ZgjimHaziri May 16, 2025
14be28d
TA-3750: Rename config module in codeowners file
ZgjimHaziri May 16, 2025
f9033a3
TA-3771: Migrate Data Pipeline commands
ZgjimHaziri May 19, 2025
b11c573
Merge branch 'content-cli-v2-refactoring' into TA-3771-migrate-data-p…
ZgjimHaziri May 19, 2025
3a2f976
TA-3767: Migrate Studio commands
ZgjimHaziri May 20, 2025
4165bd8
TA-3767: Add Navi as codeowners in Studio commands
ZgjimHaziri May 20, 2025
7b8e58a
TA-3769: Move Pacman related commands out of Studio module
ZgjimHaziri May 20, 2025
bcaa9bf
TA-3769: Remove Pacman interfaces from this PR
ZgjimHaziri May 20, 2025
4c022ce
TA-3770: Add individual codeowners
ZgjimHaziri May 21, 2025
26260f3
Merge branch 'TA-3771-migrate-data-pipeline' into TA-3767-migrate-stu…
ZgjimHaziri May 21, 2025
71605b4
Merge branch 'content-cli-v2-refactoring' into TA-3767-migrate-studio…
ZgjimHaziri May 22, 2025
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
4 changes: 2 additions & 2 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@
#/src/commands/action-flows/ action-flow team
#/src/commands/analysis/ filter and foundation team
#/src/commands/cpm4/ cpm4 team
#/src/commands/data-pipeline/ data-management team
#/src/commands/studio/ @celonis/navi
/src/commands/data-pipeline/ @Dusan-r @IvanGandacov @EktaCelonis @gorasoCelonis
/src/commands/studio/ @celonis/navi
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { Context } from "../../../core/command/cli-context";
import { ConnectionService } from "./connection.service";

export class ConnectionCommandService {

private connectionService: ConnectionService;

constructor(context: Context) {
this.connectionService = new ConnectionService(context);
}

public async updateProperty(dataPoolId: string, connectionId: string, property: string, value: string): Promise<void>{
await this.connectionService.updateProperty(dataPoolId, connectionId, property, value);
}

public async getProperties(dataPoolId: string, connectionId: string): Promise<void> {
await this.connectionService.listProperties(dataPoolId, connectionId);
}

public async listConnections(dataPoolId: string): Promise<any> {
await this.connectionService.findAllConnections(dataPoolId);
}
}
50 changes: 50 additions & 0 deletions src/commands/data-pipeline/connection/connection.commands.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/**
* Commands related to the Data Pool Connections.
*/

import { Command, OptionValues } from "commander";
import { Configurator } from "../../../core/command/module-handler";
import { ConnectionCommandService } from "./connection-command.service";
import { Context } from "../../../core/command/cli-context";

export class ConnectionCommands {

register(context: Context, configurator: Configurator) {
const listCommand = configurator.command("list");
listCommand.command("connection")
.description("Command to list all connections in a Data Pool")
.option("-p, --profile <profile>", "Profile which you want to use to list connections")
.requiredOption("--dataPoolId <dataPoolId>", "ID of the data pool")
.action(this.listConnections);

const getCommand = configurator.command("get");
getCommand.command("connection")
.description("Programmatically read properties of your connections")
.option("-p, --profile <profile>", "Profile which you want to use to update the data pool configuration")
.requiredOption("--dataPoolId <dataPoolId>", "Id of the data pool you want to update")
.requiredOption("--connectionId <connectionId>", "Id of the connection you want to update")
.action(this.getCommandProperties);

const setCommand = configurator.command("set");
setCommand.command("connection")
.description("Programmatically update properties of your connections")
.option("-p, --profile <profile>", "Profile which you want to use to update the data pool configuration")
.requiredOption("--dataPoolId <dataPoolId>", "Id of the data pool you want to update")
.requiredOption("--connectionId <connectionId>", "Id of the connection you want to update")
.requiredOption("--property <property>", "The property you want to update")
.requiredOption("--value <value>", "The value you want to update")
.action(this.updateConnectionProperty);
}

async getCommandProperties(context: Context, command: Command, options: OptionValues) {
await new ConnectionCommandService(context).getProperties(options.dataPoolId, options.connectionId);
}

async listConnections(context: Context, command: Command, options: OptionValues) {
await new ConnectionCommandService(context).listConnections(options.dataPoolId);
}

async updateConnectionProperty(context: Context, command: Command, options: OptionValues) {
await new ConnectionCommandService(context).updateProperty(options.dataPoolId, options.connectionId, options.property, options.value);
}
}
62 changes: 62 additions & 0 deletions src/commands/data-pipeline/connection/connection.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { logger } from "../../../core/utils/logger";
import { Context } from "../../../core/command/cli-context";
import { DataPoolApi } from "../data-pool/data-pool-api";

export class ConnectionService {

private dataPoolApi: DataPoolApi;

constructor(context: Context) {
this.dataPoolApi = new DataPoolApi(context)
}

public async findAllConnections(dataPoolId: string): Promise<any[]> {
const connections = await this.dataPoolApi.listConnections(dataPoolId);
connections.forEach(connection => {
logger.info(`Connection ID: ${connection.id} - Name: ${connection.name} - Type: ${connection.type}`);
})
return connections;
}

public async listProperties(dataPoolId: string, connectionId: string): Promise<any[]> {
const connection = await this.dataPoolApi.getConnection(dataPoolId, connectionId);
const type = connection.type;
const typedConnection = await this.dataPoolApi.getTypedConnection(dataPoolId, connectionId, type);
logger.info(`Connection ID: ${connection.id} - Name: ${connection.name} - Type: ${connection.type}`);
logger.info(`Properties:`)
for (let k in typedConnection) {
if (typeof typedConnection[k] === 'object') {
for (let o in typedConnection[k]) {
logger.info(` ${k}.${o} : ${typeof typedConnection[k][o]} := ${typedConnection[k][o]}`)
}
} else {
logger.info(` ${k} : ${typeof typedConnection[k]} := ${typedConnection[k]}`);
}
}
return typedConnection;
}

public async updateProperty(dataPoolId: string, connectionId: string, property: string, value: string) {
const connection = await this.dataPoolApi.getConnection(dataPoolId, connectionId);
const type = connection.type;
const typedConnection = await this.dataPoolApi.getTypedConnection(dataPoolId, connectionId, type);
const parts = property.split(".");
// update the typed connection object
let currentObject = typedConnection;
for (let i = 0; i < parts.length; i++) {
const part = parts[i];
if (currentObject.hasOwnProperty(part)) {
if (i < parts.length - 1) {
currentObject = currentObject[part];
} else {
currentObject[part] = value;
}
} else {
logger.error(`Property ${property} not found on connection.`)
return;
}
}
await this.dataPoolApi.updateTypedConnection(dataPoolId, connectionId, type, typedConnection);
logger.info(`Property ${property} updated.`);
}
}
55 changes: 55 additions & 0 deletions src/commands/data-pipeline/data-pool/data-pool-api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { DataPoolInstallVersionReport, DataPoolPageTransport } from "./data-pool-manager.interfaces";
import { FatalError } from "../../../core/utils/logger";
import { Context } from "../../../core/command/cli-context";
import { HttpClient } from "../../../core/http/http-client";

export class DataPoolApi {

private httpClient: HttpClient;

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

public async findAllPagedPools(limit: string, page: string): Promise<DataPoolPageTransport> {
return this.httpClient.get(`/integration/api/pools/paged?limit=${limit}&page=${page}`).catch(e => {
throw new FatalError(`Problem getting data pools: : ${e}`);
});
}

public async executeDataPoolsBatchImport(importRequest: string): Promise<DataPoolInstallVersionReport> {
return this.httpClient.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 this.httpClient.get(`/integration/api/pools/${poolId}/v2/export`).catch(e => {
throw new FatalError(`Data Pool export failed: : ${e}`);
});
}

public async listConnections(poolId: string): Promise<any> {
return this.httpClient.get(`/integration/api/pools/${poolId}/overviews/data-sources`).catch(e => {
throw new FatalError(`Can not list connections: : ${e}`);
});
}

public async getConnection(poolId: string, connectionId: string): Promise<any> {
return this.httpClient.get(`/integration/api/pools/${poolId}/data-sources/${connectionId}`).catch(e => {
throw new FatalError(`Can not get connection: : ${e}`);
});
}

public async getTypedConnection(poolId: string, connectionId: string, type: string): Promise<any> {
return this.httpClient.get(`/integration/api/datasource/${type}/${connectionId}`).catch(e => {
throw new FatalError(`Can get typed connection: : ${e}`);
});
}

public async updateTypedConnection(poolId: string, connectionId: string, type: string, data: any): Promise<any> {
return this.httpClient.put(`/integration/api/datasource/${type}/${connectionId}`, data).catch(e => {
throw new FatalError(`Can not update typed connection: ${e}`);
});
}
}
49 changes: 49 additions & 0 deletions src/commands/data-pipeline/data-pool/data-pool-command.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { ContentService } from "../../../core/http/http-shared/content.service";
import { DataPoolManagerFactory } from "./data-pool-manager.factory";
import { Context } from "../../../core/command/cli-context";
import { DataPoolService } from "./data-pool-service";
import { logger } from "../../../core/utils/logger";

export class DataPoolCommandService {
private contentService = new ContentService();
private dataPoolManagerFactory: DataPoolManagerFactory;
private dataPoolService: DataPoolService;

constructor(context: Context) {
this.dataPoolManagerFactory = new DataPoolManagerFactory(context);
this.dataPoolService = new DataPoolService(context);
}

public async pullDataPool(id: string): Promise<void> {
await this.contentService.pull(this.dataPoolManagerFactory.createManager(id, null));
}

public async pushDataPool(filename: string): Promise<void> {
await this.contentService.push(this.dataPoolManagerFactory.createManager(null, filename));
}

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

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

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

public async updateDataPool(id: string, filename: string): Promise<any> {
await this.contentService.update(this.dataPoolManagerFactory.createManager(id, filename));
}

public async listDataPools(jsonResponse: boolean): Promise<any> {
logger.info(jsonResponse);
if (jsonResponse) {
await this.dataPoolService.findAndExportAllPools();
} else {
await this.dataPoolService.listDataPools();
}
}
}
47 changes: 47 additions & 0 deletions src/commands/data-pipeline/data-pool/data-pool-manager.factory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import * as path from "path";
import { DataPoolManager } from "./data-pool.manager";
import { FatalError, logger } from "../../../core/utils/logger";
import * as fs from "node:fs";
import { Context } from "../../../core/command/cli-context";

export class DataPoolManagerFactory {

private readonly context: Context;

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

public createManager(id: string, filename: string): DataPoolManager {
const dataPoolManager = new DataPoolManager(this.context);
dataPoolManager.id = id;
if (filename !== null) {
dataPoolManager.content = this.readFile(filename);
}
return dataPoolManager;
}

public createManagers(): DataPoolManager[] {
const dataPools = fs.readdirSync(process.cwd());
return dataPools
.filter(filePath => {
if (filePath.startsWith(DataPoolManager.DATA_POOL_FILE_NAME_PREFIX) && filePath.endsWith(".json")) {
const file = fs.lstatSync(filePath);
return file.isFile();
}
return false;
})
.map(dataPool => {
const dataPoolManager = new DataPoolManager(this.context);
dataPoolManager.content = this.readFile(dataPool);
return dataPoolManager;
});
}

private readFile(filename: string): string {
if (!fs.existsSync(path.resolve(process.cwd(), filename))) {
logger.error(new FatalError("The provided file does not exit"));
}
return fs.readFileSync(path.resolve(process.cwd(), filename), { encoding: "utf-8" });
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
export declare type DataPoolStatus = "CANCEL" | "FAIL" | "QUEUED" | "RUNNING" | "SKIPPED" | "SUCCESS" | "UNCONFIGURED";
export type Tag = {};

export declare class DataSourceSlimTransport {
id: string;
name: string;
imported: boolean;
importedPoolId: string;
}

export interface DataPoolSlimTransport {
id: string;
name: string;
status: DataPoolStatus;
lastExecutionStartDate: Date;
createdBy: string;
tags: Tag[];
rawDataSize: number;
dataSources: DataSourceSlimTransport[];
}
export declare class DataPoolPageTransport {
content: DataPoolSlimTransport[];
pageSize: number;
pageNumber: number;
totalCount: number;
}

export declare class DataPoolInstallVersionReport {
dataModelIdMappings: Map<string, string>;
}
Loading