|
| 1 | +# **Content CLI – Architecture & Inner Workings** |
| 2 | + |
| 3 | +## **Overview** |
| 4 | + |
| 5 | +The Content CLI is a TypeScript based CLI that allows interaction with the Celonis EMS. Its core functionality includes extracting, pushing, and managing EMS content. |
| 6 | + |
| 7 | +**Key Dependencies** |
| 8 | + |
| 9 | +| **Dependency** | **Purpose** | |
| 10 | +| --- | --- | |
| 11 | +| commander | CLI parsing and command configuration and routing | |
| 12 | +| axios | HTTP client for API calls | |
| 13 | +| form-data | Multipart file uploads (e.g. zip files) | |
| 14 | + |
| 15 | +## **Entry Point** |
| 16 | + |
| 17 | +The CLI starts at the `content-cli.ts` file, which: |
| 18 | + |
| 19 | +- Loads and registers all command modules. |
| 20 | +- Parses user input from the command line. |
| 21 | +- Initializes the shared context object. |
| 22 | +- Passes execution to the appropriate command. |
| 23 | + |
| 24 | +When built, it produces a `dist/content-cli.js`, which users execute with: `node content-cli.js [commands] [options]` |
| 25 | + |
| 26 | +The **help** dialog is shown when: |
| 27 | + |
| 28 | +- The user passes -h or --help |
| 29 | +- The user runs an incomplete or invalid command |
| 30 | + |
| 31 | +Help output includes descriptions, options and subcommands (if available) for the called command. It is auto-generated from the Commander configuration. |
| 32 | + |
| 33 | +## **Context Passing** |
| 34 | + |
| 35 | +The context object is passed to all commands and services. It includes: |
| 36 | + |
| 37 | +- **Profile**: The profile which is used for each command execution as authentication. |
| 38 | +- **Logger**: The global logger, with optional debug logging. |
| 39 | +- **HttpClient**: Authenticated EMS API client. |
| 40 | + |
| 41 | +This object is passed into command callbacks, ensuring that state and services are shared cleanly: |
| 42 | +``` |
| 43 | +private async commandHandler(context: Context): Promise<void> { |
| 44 | + await context.httpClient.get('...'); |
| 45 | +} |
| 46 | +``` |
| 47 | + |
| 48 | +## **Profile management** |
| 49 | + |
| 50 | +Every command in the Content CLI runs within the context of a profile. A profile contains information about the target Celonis team, including the base URL and authentication credentials. |
| 51 | + |
| 52 | +There are two ways a profile is used: |
| 53 | + |
| 54 | +- You can pass it directly to any command using the `--profile` flag. |
| 55 | +- Or you can set a default profile so it's used automatically without passing the flag every time. |
| 56 | + |
| 57 | +To manage profiles, use the content-cli profile command group. This allows you to: |
| 58 | + |
| 59 | +- Create a new profile |
| 60 | +- List existing profiles |
| 61 | +- Set a profile as the default |
| 62 | +- Delete unused profiles |
| 63 | + |
| 64 | +## **API communication** |
| 65 | + |
| 66 | +The **HttpClient** class is a centralized client for interacting with the EMS API. It handles: |
| 67 | + |
| 68 | +- **Auth**: Authenticates by sending the token in the provided profile. |
| 69 | +- **URL building**: Prefixing all API paths with the correct base (e.g. realm), inferred from the profile |
| 70 | +- **Request execution** |
| 71 | + - Uses axios under the hood, for HTTP API call executions. |
| 72 | + - Handles GET, POST, PUT, and DELETE methods. |
| 73 | + - Returns parsed responses to commands. |
| 74 | +- **Error handling**: Mapping error codes to readable messages. |
| 75 | + |
| 76 | +This prevents duplication of HTTP logic in individual modules. |
| 77 | + |
| 78 | +## **Managing Command Modules** |
| 79 | + |
| 80 | +### **Modules** |
| 81 | + |
| 82 | +Each common functionality group is implemented as a separate module, which is represented by a directory in the src/command folder, and is enabled as a module by a module.ts class which implements the IModule. |
| 83 | + |
| 84 | +The implementation consists of a register() method, which utilizes the passed Configurator object, which supports interactions with [Commander’s Command](https://www.npmjs.com/package/commander) API. The API gets used to: |
| 85 | + |
| 86 | +- Register commands |
| 87 | +- Define their description, options and action which will be taken when the command is executed. |
| 88 | + |
| 89 | +### **Module Handler** |
| 90 | + |
| 91 | +These modules get loaded by the **module handler**, which: |
| 92 | + |
| 93 | +- Scans available modules (like configuration-management, studio). |
| 94 | +- Calls each module’s registration function. |
| 95 | +- Registers the command using Commander.js. |
| 96 | + |
| 97 | +This means that the registration of a new module is automatically handled after module creation is done as described above. |
| 98 | + |
| 99 | +## **Configurator Wrapper** |
| 100 | + |
| 101 | +In order to provide more control over what is provided and what happens when interacting with Commander, there is a Configurator abstraction that wraps the underlying CLI command configuration. Its purpose is to: |
| 102 | + |
| 103 | +- Enforce validation rules. |
| 104 | +- Unify help text and usage patterns. (e.g. providing deprecation notice helper) |
| 105 | + |
| 106 | +## **Error Handling** |
| 107 | + |
| 108 | +### **General Error Handling** |
| 109 | + |
| 110 | +- CLI argument parsing logic is wrapped in a try/catch. |
| 111 | +- On failure, a clean message is shown and the process exits with a non-zero code. |
| 112 | +- Unhandled errors are [caught globally](https://github.com/celonis/content-cli/blob/b76187e76e1f50d41a149bfe90d6ddead568c853/src/content-cli.ts#L74) to avoid raw stack traces. |
| 113 | + |
| 114 | +### **HTTP Error Handling** |
| 115 | + |
| 116 | +- All API calls go through a centralized HttpClient. |
| 117 | +- HTTP call failures are caught and wrapped inside the HttpClient. |
| 118 | +- Custom error messages (e.g. "Unauthorized" or "Server failed") are shown to the user. |
| 119 | + |
| 120 | +## **Legacy BaseManager – Deprecated** |
| 121 | + |
| 122 | +The project historically used a BaseManager pattern where each content type extended a shared manager class. |
| 123 | + |
| 124 | +### **Why it's discouraged now:** |
| 125 | + |
| 126 | +- Cluttered many functionalities in one place. |
| 127 | +- Wasn’t reusable for new API endpoints |
| 128 | + - E.g.. config export/import commands. |
| 129 | +- Harder developer experience and readability. |
| 130 | + |
| 131 | +### **New approach:** |
| 132 | + |
| 133 | +Custom API service classes are built around the HttpClient and context, which get used . These are: |
| 134 | + |
| 135 | +- More testable |
| 136 | +- Easier to extend |
| 137 | +- More readable and explicit |
| 138 | + |
| 139 | +We avoid using BaseManager in new modules and aim to migrate old ones when possible. |
0 commit comments