Skip to content

Commit e452c0c

Browse files
authored
TA-3755: Persist the Internal Architecture document in the repo (#223)
1 parent 5a0581d commit e452c0c

File tree

1 file changed

+139
-0
lines changed

1 file changed

+139
-0
lines changed

docs/internal-architecture.md

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
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

Comments
 (0)