sirius-framework-mcp 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +191 -0
- package/README.md +160 -0
- package/dist/grammars/tree-sitter-java.wasm +0 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +247 -0
- package/dist/index.js.map +1 -0
- package/dist/java-parser.d.ts +25 -0
- package/dist/java-parser.js +281 -0
- package/dist/java-parser.js.map +1 -0
- package/dist/prompts/index.d.ts +9 -0
- package/dist/prompts/index.js +15 -0
- package/dist/prompts/index.js.map +1 -0
- package/dist/prompts/workflows.d.ts +27 -0
- package/dist/prompts/workflows.js +317 -0
- package/dist/prompts/workflows.js.map +1 -0
- package/dist/resources/biz/analytics.md +157 -0
- package/dist/resources/biz/biz-controller.md +151 -0
- package/dist/resources/biz/codelists.md +154 -0
- package/dist/resources/biz/entity-triple.md +142 -0
- package/dist/resources/biz/importer.md +153 -0
- package/dist/resources/biz/isenguard.md +156 -0
- package/dist/resources/biz/jobs.md +145 -0
- package/dist/resources/biz/processes.md +155 -0
- package/dist/resources/biz/storage.md +149 -0
- package/dist/resources/biz/tenants.md +159 -0
- package/dist/resources/biz/testing.md +127 -0
- package/dist/resources/db/composites.md +145 -0
- package/dist/resources/db/entities.md +156 -0
- package/dist/resources/db/mixing.md +176 -0
- package/dist/resources/db/queries.md +178 -0
- package/dist/resources/db/refs.md +135 -0
- package/dist/resources/index.d.ts +27 -0
- package/dist/resources/index.js +68 -0
- package/dist/resources/index.js.map +1 -0
- package/dist/resources/kernel/async.md +189 -0
- package/dist/resources/kernel/commons.md +203 -0
- package/dist/resources/kernel/config.md +155 -0
- package/dist/resources/kernel/di.md +138 -0
- package/dist/resources/kernel/lifecycle.md +146 -0
- package/dist/resources/loader.d.ts +9 -0
- package/dist/resources/loader.js +17 -0
- package/dist/resources/loader.js.map +1 -0
- package/dist/resources/web/controllers.md +151 -0
- package/dist/resources/web/services.md +136 -0
- package/dist/resources/web/templates.md +162 -0
- package/dist/tools/index.d.ts +4 -0
- package/dist/tools/index.js +3 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/introspection.d.ts +55 -0
- package/dist/tools/introspection.js +233 -0
- package/dist/tools/introspection.js.map +1 -0
- package/dist/tools/scaffold.d.ts +64 -0
- package/dist/tools/scaffold.js +505 -0
- package/dist/tools/scaffold.js.map +1 -0
- package/dist/workspace.d.ts +37 -0
- package/dist/workspace.js +185 -0
- package/dist/workspace.js.map +1 -0
- package/package.json +41 -0
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP prompt definitions for common Sirius development workflows.
|
|
3
|
+
*
|
|
4
|
+
* Each prompt generates an initial conversation that guides an AI assistant
|
|
5
|
+
* through a specific Sirius framework task.
|
|
6
|
+
*/
|
|
7
|
+
function userMessage(text) {
|
|
8
|
+
return { role: "user", content: { type: "text", text } };
|
|
9
|
+
}
|
|
10
|
+
function assistantMessage(text) {
|
|
11
|
+
return {
|
|
12
|
+
role: "assistant",
|
|
13
|
+
content: { type: "text", text },
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
// 1. add-entity
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
export const addEntityPrompt = {
|
|
20
|
+
name: "add-entity",
|
|
21
|
+
description: "I want to add a new Sirius entity",
|
|
22
|
+
arguments: [
|
|
23
|
+
{
|
|
24
|
+
name: "name",
|
|
25
|
+
description: "The entity name (e.g. 'Product'). Used as the base for interface, SQL, and Mongo class names.",
|
|
26
|
+
required: true,
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
name: "package",
|
|
30
|
+
description: "The target Java package (e.g. 'sirius.biz.products'). If omitted, a sensible default under sirius.biz will be suggested.",
|
|
31
|
+
required: false,
|
|
32
|
+
},
|
|
33
|
+
],
|
|
34
|
+
generateMessages(args) {
|
|
35
|
+
const name = args.name;
|
|
36
|
+
const pkg = args.package ?? `sirius.biz.${name.toLowerCase()}s`;
|
|
37
|
+
return [
|
|
38
|
+
userMessage(`I want to add a new Sirius entity called "${name}" in the package "${pkg}". ` +
|
|
39
|
+
`Please guide me through the full entity-triple pattern.`),
|
|
40
|
+
assistantMessage(`Great -- adding a new entity in Sirius follows the **entity-triple** pattern ` +
|
|
41
|
+
`(see resource \`sirius://biz/entity-triple\`). Here is the step-by-step plan:\n` +
|
|
42
|
+
`\n` +
|
|
43
|
+
`## 1. Database-independent interface\n` +
|
|
44
|
+
`Create \`${pkg}.${name}\` as an interface that extends \`Entity\` (and typically \`Traced\`, \`Journaled\`).\n` +
|
|
45
|
+
`Define a \`${name}Data\` composite if the entity carries reusable field groups.\n` +
|
|
46
|
+
`Use \`Mapping.named()\` constants for every persistent field.\n` +
|
|
47
|
+
`\n` +
|
|
48
|
+
`## 2. JDBC implementation\n` +
|
|
49
|
+
`Create \`${pkg}.jdbc.SQL${name}\` extending \`SQLTenantAware\` and implementing \`${name}\`.\n` +
|
|
50
|
+
`Add appropriate \`@Index\` annotations for unique constraints and lookup fields.\n` +
|
|
51
|
+
`Add \`@Framework("${pkg.replace(/^sirius\.biz\./, "biz.")}-jdbc")\` so the entity is only loaded when the flag is enabled.\n` +
|
|
52
|
+
`\n` +
|
|
53
|
+
`## 3. MongoDB implementation\n` +
|
|
54
|
+
`Create \`${pkg}.mongo.Mongo${name}\` extending \`MongoTenantAware\` and implementing \`${name}\`.\n` +
|
|
55
|
+
`Add \`@Framework("${pkg.replace(/^sirius\.biz\./, "biz.")}-mongo")\`.\n` +
|
|
56
|
+
`\n` +
|
|
57
|
+
`## 4. Framework flags\n` +
|
|
58
|
+
`Register framework flags in your \`.conf\` file:\n` +
|
|
59
|
+
`\`\`\`hocon\n` +
|
|
60
|
+
`sirius.frameworks {\n` +
|
|
61
|
+
` "${pkg.replace(/^sirius\.biz\./, "biz.")}" = true\n` +
|
|
62
|
+
` "${pkg.replace(/^sirius\.biz\./, "biz.")}-jdbc" = true\n` +
|
|
63
|
+
` "${pkg.replace(/^sirius\.biz\./, "biz.")}-mongo" = false\n` +
|
|
64
|
+
`}\n` +
|
|
65
|
+
`\`\`\`\n` +
|
|
66
|
+
`\n` +
|
|
67
|
+
`## 5. Translation source\n` +
|
|
68
|
+
`Add \`@TranslationSource\` to provide i18n keys for the entity and its fields.\n` +
|
|
69
|
+
`Create corresponding \`.properties\` files under \`resources/\`.\n` +
|
|
70
|
+
`\n` +
|
|
71
|
+
`Shall I generate the skeleton code for each of these files?`),
|
|
72
|
+
];
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
// ---------------------------------------------------------------------------
|
|
76
|
+
// 2. add-job
|
|
77
|
+
// ---------------------------------------------------------------------------
|
|
78
|
+
export const addJobPrompt = {
|
|
79
|
+
name: "add-job",
|
|
80
|
+
description: "I want to add a new background job",
|
|
81
|
+
arguments: [
|
|
82
|
+
{
|
|
83
|
+
name: "name",
|
|
84
|
+
description: "The job name (e.g. 'ProductImportJob'). Used as the class name for the JobFactory implementation.",
|
|
85
|
+
required: true,
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
name: "type",
|
|
89
|
+
description: "The kind of job: 'batch' (long-running data processing), 'report' (generates output), or 'interactive' (user-triggered, short-lived). Determines the base class.",
|
|
90
|
+
required: false,
|
|
91
|
+
},
|
|
92
|
+
],
|
|
93
|
+
generateMessages(args) {
|
|
94
|
+
const name = args.name;
|
|
95
|
+
const type = args.type ?? "batch";
|
|
96
|
+
const baseClassMap = {
|
|
97
|
+
batch: "BatchProcessJobFactory",
|
|
98
|
+
report: "ReportJobFactory",
|
|
99
|
+
interactive: "InteractiveJobFactory",
|
|
100
|
+
};
|
|
101
|
+
const baseClass = baseClassMap[type] ?? baseClassMap.batch;
|
|
102
|
+
return [
|
|
103
|
+
userMessage(`I want to add a new ${type} background job called "${name}". ` +
|
|
104
|
+
`Please guide me through the Sirius jobs framework.`),
|
|
105
|
+
assistantMessage(`The Sirius jobs framework (see resource \`sirius://biz/jobs\`) uses a ` +
|
|
106
|
+
`\`JobFactory\` hierarchy to define background jobs. For a **${type}** job, ` +
|
|
107
|
+
`the right base class is \`${baseClass}\`.\n` +
|
|
108
|
+
`\n` +
|
|
109
|
+
`## Step-by-step plan\n` +
|
|
110
|
+
`\n` +
|
|
111
|
+
`### 1. Choose and extend the base class\n` +
|
|
112
|
+
`Create \`${name}\` extending \`${baseClass}\`.\n` +
|
|
113
|
+
`Register it with \`@Register\` so the DI framework picks it up.\n` +
|
|
114
|
+
`\n` +
|
|
115
|
+
`### 2. Define parameters\n` +
|
|
116
|
+
`Override the \`collectParameters()\` method to declare job parameters.\n` +
|
|
117
|
+
`Use parameter types like \`StringParameter\`, \`BooleanParameter\`, \`EntityParameter\`, etc.\n` +
|
|
118
|
+
`Parameters appear in the job's UI form automatically.\n` +
|
|
119
|
+
`\n` +
|
|
120
|
+
`### 3. Implement execution logic\n` +
|
|
121
|
+
(type === "batch"
|
|
122
|
+
? `Override \`execute(ProcessContext process)\` to implement the batch processing logic.\n` +
|
|
123
|
+
`Use \`process.log()\` for progress reporting and \`process.addTiming()\` for performance tracking.\n` +
|
|
124
|
+
`Handle cancellation via \`process.isActive()\`.\n`
|
|
125
|
+
: type === "report"
|
|
126
|
+
? `Override \`execute(ProcessContext process)\` and use the report output helpers.\n` +
|
|
127
|
+
`The report framework handles file generation and storage automatically.\n`
|
|
128
|
+
: `Override \`execute(ProcessContext process)\` for the interactive job logic.\n` +
|
|
129
|
+
`Interactive jobs are typically short-lived and may provide immediate feedback.\n`) +
|
|
130
|
+
`\n` +
|
|
131
|
+
`### 4. Permissions and labels\n` +
|
|
132
|
+
`Override \`getRequiredPermission()\` to restrict who can run the job.\n` +
|
|
133
|
+
`Provide a label and description via NLS keys.\n` +
|
|
134
|
+
`\n` +
|
|
135
|
+
`### 5. Optional: scheduling\n` +
|
|
136
|
+
`If the job should run on a schedule, implement the \`ScheduledJobFactory\` interface ` +
|
|
137
|
+
`or configure it via the admin UI.\n` +
|
|
138
|
+
`\n` +
|
|
139
|
+
`Shall I generate the skeleton code for \`${name}\`?`),
|
|
140
|
+
];
|
|
141
|
+
},
|
|
142
|
+
};
|
|
143
|
+
// ---------------------------------------------------------------------------
|
|
144
|
+
// 3. add-feature
|
|
145
|
+
// ---------------------------------------------------------------------------
|
|
146
|
+
export const addFeaturePrompt = {
|
|
147
|
+
name: "add-feature",
|
|
148
|
+
description: "I want to add a complete feature module",
|
|
149
|
+
arguments: [
|
|
150
|
+
{
|
|
151
|
+
name: "name",
|
|
152
|
+
description: "The feature name (e.g. 'products'). This determines the package, entity names, controller, and templates.",
|
|
153
|
+
required: true,
|
|
154
|
+
},
|
|
155
|
+
],
|
|
156
|
+
generateMessages(args) {
|
|
157
|
+
const name = args.name;
|
|
158
|
+
const singular = name.endsWith("s") && name.length > 1 ? name.slice(0, -1) : name;
|
|
159
|
+
const entityName = singular.charAt(0).toUpperCase() + singular.slice(1);
|
|
160
|
+
const pkg = `sirius.biz.${name.toLowerCase()}`;
|
|
161
|
+
return [
|
|
162
|
+
userMessage(`I want to add a complete feature module called "${name}" to a Sirius application. ` +
|
|
163
|
+
`This should be a full vertical slice: entity, controller, template, and test.`),
|
|
164
|
+
assistantMessage(`A full feature module in Sirius is a vertical slice that touches multiple layers. ` +
|
|
165
|
+
`Here is the plan for the "${name}" feature. Relevant resources:\n` +
|
|
166
|
+
`- \`sirius://biz/entity-triple\` (entity pattern)\n` +
|
|
167
|
+
`- \`sirius://biz/jobs\` (if the feature needs background processing)\n` +
|
|
168
|
+
`- \`sirius://biz/importer\` (if the feature needs data import)\n` +
|
|
169
|
+
`- \`sirius://kernel/di\` (dependency injection and registration)\n` +
|
|
170
|
+
`\n` +
|
|
171
|
+
`## 1. Entity layer\n` +
|
|
172
|
+
`Follow the entity-triple pattern to create:\n` +
|
|
173
|
+
`- \`${pkg}.${entityName}\` -- database-independent interface\n` +
|
|
174
|
+
`- \`${pkg}.jdbc.SQL${entityName}\` -- JDBC implementation (extends \`SQLTenantAware\`)\n` +
|
|
175
|
+
`- \`${pkg}.mongo.Mongo${entityName}\` -- MongoDB implementation (extends \`MongoTenantAware\`)\n` +
|
|
176
|
+
`- \`${pkg}.${entityName}Data\` -- composite for reusable field groups (if needed)\n` +
|
|
177
|
+
`\n` +
|
|
178
|
+
`## 2. Controller layer\n` +
|
|
179
|
+
`Create \`${pkg}.${entityName}Controller\` extending \`BizController\`:\n` +
|
|
180
|
+
`- Use \`@Routed\` annotations for URL mappings (e.g. \`/${name}\`, \`/${singular}/{id}\`)\n` +
|
|
181
|
+
`- Add \`@LoginRequired\` and \`@Permission\` for access control\n` +
|
|
182
|
+
`- Implement list, detail/edit, and delete endpoints\n` +
|
|
183
|
+
`- Make the controller generic over the entity interface so it works with both SQL and Mongo\n` +
|
|
184
|
+
`\n` +
|
|
185
|
+
`## 3. Template layer\n` +
|
|
186
|
+
`Create Pasta/Tagliatelle templates under \`resources/default/templates/biz/${name}/\`:\n` +
|
|
187
|
+
`- \`${singular}.html.pasta\` -- detail/edit view\n` +
|
|
188
|
+
`- \`${name}.html.pasta\` -- list view\n` +
|
|
189
|
+
`Use the Tycho UI component library (\`<t:...>\` tags) for consistent look-and-feel.\n` +
|
|
190
|
+
`\n` +
|
|
191
|
+
`## 4. Framework flags\n` +
|
|
192
|
+
`Register in your \`.conf\`:\n` +
|
|
193
|
+
`\`\`\`hocon\n` +
|
|
194
|
+
`sirius.frameworks {\n` +
|
|
195
|
+
` "biz.${name}" = true\n` +
|
|
196
|
+
` "biz.${name}-jdbc" = true\n` +
|
|
197
|
+
` "biz.${name}-mongo" = false\n` +
|
|
198
|
+
`}\n` +
|
|
199
|
+
`\`\`\`\n` +
|
|
200
|
+
`\n` +
|
|
201
|
+
`## 5. Test\n` +
|
|
202
|
+
`Create \`${entityName}Test.kt\` in \`src/test/kotlin/${pkg.replace(/\./g, "/")}/\`:\n` +
|
|
203
|
+
`- Use \`@ExtendWith(SiriusExtension::class)\`\n` +
|
|
204
|
+
`- Wait for database readiness in \`@BeforeAll\`\n` +
|
|
205
|
+
`- Test CRUD operations and any business logic\n` +
|
|
206
|
+
`\n` +
|
|
207
|
+
`## 6. i18n\n` +
|
|
208
|
+
`Add \`@TranslationSource\` to the entity and create \`.properties\` files ` +
|
|
209
|
+
`for labels and validation messages.\n` +
|
|
210
|
+
`\n` +
|
|
211
|
+
`Shall I start generating the code for each layer?`),
|
|
212
|
+
];
|
|
213
|
+
},
|
|
214
|
+
};
|
|
215
|
+
// ---------------------------------------------------------------------------
|
|
216
|
+
// 4. add-import-handler
|
|
217
|
+
// ---------------------------------------------------------------------------
|
|
218
|
+
export const addImportHandlerPrompt = {
|
|
219
|
+
name: "add-import-handler",
|
|
220
|
+
description: "I want to add a data import handler",
|
|
221
|
+
arguments: [
|
|
222
|
+
{
|
|
223
|
+
name: "entity",
|
|
224
|
+
description: "The entity name this import handler targets (e.g. 'Product'). Must correspond to an existing Sirius entity.",
|
|
225
|
+
required: true,
|
|
226
|
+
},
|
|
227
|
+
],
|
|
228
|
+
generateMessages(args) {
|
|
229
|
+
const entity = args.entity;
|
|
230
|
+
return [
|
|
231
|
+
userMessage(`I want to add a data import handler for the "${entity}" entity. ` +
|
|
232
|
+
`Please guide me through the Sirius importer framework.`),
|
|
233
|
+
assistantMessage(`The Sirius importer framework (see resource \`sirius://biz/importer\`) provides ` +
|
|
234
|
+
`\`EntityImportHandler\` classes that define how external data maps to entities. ` +
|
|
235
|
+
`Here is the plan:\n` +
|
|
236
|
+
`\n` +
|
|
237
|
+
`## 1. Choose the base class\n` +
|
|
238
|
+
`- For JDBC entities: extend \`SQLEntityImportHandler<SQL${entity}>\`\n` +
|
|
239
|
+
`- For MongoDB entities: extend \`MongoEntityImportHandler<Mongo${entity}>\`\n` +
|
|
240
|
+
`Register with \`@Register\` and annotate with the appropriate \`@Framework\`.\n` +
|
|
241
|
+
`\n` +
|
|
242
|
+
`## 2. Define find queries\n` +
|
|
243
|
+
`Override \`determineFindQuery()\` to specify how existing records are located ` +
|
|
244
|
+
`during import (usually by a unique business key like an external ID or code).\n` +
|
|
245
|
+
`This is critical for upsert behavior -- without it, imports always create new records.\n` +
|
|
246
|
+
`\n` +
|
|
247
|
+
`## 3. Map fields\n` +
|
|
248
|
+
`Override \`collectFieldMappings()\` or use the auto-mapping from \`@AutoImport\` annotations.\n` +
|
|
249
|
+
`Fields annotated with \`@AutoImport\` on the entity are automatically mapped.\n` +
|
|
250
|
+
`For custom/computed fields, add explicit mappings in the handler.\n` +
|
|
251
|
+
`\n` +
|
|
252
|
+
`## 4. Handle events\n` +
|
|
253
|
+
`Override lifecycle callbacks to add custom logic:\n` +
|
|
254
|
+
`- \`beforeImport()\` -- validate or transform data before persisting\n` +
|
|
255
|
+
`- \`afterImport()\` -- trigger side effects after a record is imported\n` +
|
|
256
|
+
`- \`collectDefaultExportableMappings()\` -- define which fields appear in exports\n` +
|
|
257
|
+
`\n` +
|
|
258
|
+
`## 5. Register and test\n` +
|
|
259
|
+
`The handler is automatically discovered by the import framework via DI.\n` +
|
|
260
|
+
`Test by creating a small CSV or XML import and running it through the import UI or jobs.\n` +
|
|
261
|
+
`\n` +
|
|
262
|
+
`Shall I generate the handler skeleton for \`${entity}\`?`),
|
|
263
|
+
];
|
|
264
|
+
},
|
|
265
|
+
};
|
|
266
|
+
// ---------------------------------------------------------------------------
|
|
267
|
+
// 5. debug-framework-flags
|
|
268
|
+
// ---------------------------------------------------------------------------
|
|
269
|
+
export const debugFrameworkFlagsPrompt = {
|
|
270
|
+
name: "debug-framework-flags",
|
|
271
|
+
description: "My entity/service isn't being picked up",
|
|
272
|
+
arguments: [],
|
|
273
|
+
generateMessages() {
|
|
274
|
+
return [
|
|
275
|
+
userMessage(`My entity or service is not being picked up by the Sirius framework. ` +
|
|
276
|
+
`I have the class annotated with @Register but it does not seem to be active. ` +
|
|
277
|
+
`Help me debug this.`),
|
|
278
|
+
assistantMessage(`This is a common issue and almost always comes down to **framework flags**. ` +
|
|
279
|
+
`Let me walk you through the diagnostic steps (see resource \`sirius://kernel/di\`).\n` +
|
|
280
|
+
`\n` +
|
|
281
|
+
`## Step 1: Check the framework flag on your class\n` +
|
|
282
|
+
`Look at the \`@Register\` annotation on your class. If it specifies a \`framework\` attribute:\n` +
|
|
283
|
+
`\`\`\`java\n` +
|
|
284
|
+
`@Register(framework = "biz.myfeature")\n` +
|
|
285
|
+
`\`\`\`\n` +
|
|
286
|
+
`...then the flag \`biz.myfeature\` must be **enabled** in the config.\n` +
|
|
287
|
+
`\n` +
|
|
288
|
+
`Also check for a \`@Framework\` annotation on the class, which serves the same purpose.\n` +
|
|
289
|
+
`\n` +
|
|
290
|
+
`## Step 2: Verify the flag is enabled in config\n` +
|
|
291
|
+
`Check your \`develop.conf\` (or \`instance.conf\`) and the component config files:\n` +
|
|
292
|
+
`\`\`\`hocon\n` +
|
|
293
|
+
`sirius.frameworks {\n` +
|
|
294
|
+
` "biz.myfeature" = true # Must be true!\n` +
|
|
295
|
+
` "biz.myfeature-jdbc" = true # If using JDBC entities\n` +
|
|
296
|
+
`}\n` +
|
|
297
|
+
`\`\`\`\n` +
|
|
298
|
+
`A missing or \`false\` flag means the class is **completely invisible** to the DI system.\n` +
|
|
299
|
+
`\n` +
|
|
300
|
+
`## Step 3: Check for typos in framework names\n` +
|
|
301
|
+
`The framework name in \`@Register(framework = "...")\` must **exactly match** ` +
|
|
302
|
+
`the key in \`sirius.frameworks\`. A typo in either place silently disables the class.\n` +
|
|
303
|
+
`\n` +
|
|
304
|
+
`## Step 4: Check the class hierarchy\n` +
|
|
305
|
+
`- Entities must extend the correct base class (\`SQLTenantAware\`, \`MongoTenantAware\`, etc.)\n` +
|
|
306
|
+
`- Services must implement an interface or extend a known base class\n` +
|
|
307
|
+
`- The class must be in a package that is scanned (under the configured base packages)\n` +
|
|
308
|
+
`\n` +
|
|
309
|
+
`## Step 5: Use the system console\n` +
|
|
310
|
+
`In dev mode, go to \`/system/console\` and use the \`frameworks\` command to see ` +
|
|
311
|
+
`which flags are active. You can also check \`/system/cluster\` for registered components.\n` +
|
|
312
|
+
`\n` +
|
|
313
|
+
`What is the exact class and its annotations? I can help pinpoint the issue.`),
|
|
314
|
+
];
|
|
315
|
+
},
|
|
316
|
+
};
|
|
317
|
+
//# sourceMappingURL=workflows.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workflows.js","sourceRoot":"","sources":["../../src/prompts/workflows.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
# Analytics
|
|
2
|
+
|
|
3
|
+
The analytics module provides scheduled metric computation, performance flags,
|
|
4
|
+
event recording, and chart visualization. Metrics are computed per entity on
|
|
5
|
+
daily or monthly schedules.
|
|
6
|
+
|
|
7
|
+
## Metric Computers
|
|
8
|
+
|
|
9
|
+
### DailyMetricComputer
|
|
10
|
+
|
|
11
|
+
Computes a metric for each entity on a daily basis. Annotated with `@AutoRegister`
|
|
12
|
+
for automatic discovery:
|
|
13
|
+
|
|
14
|
+
```java
|
|
15
|
+
@AutoRegister
|
|
16
|
+
public abstract class DailyMetricComputer<E extends BaseEntity<?>>
|
|
17
|
+
implements AnalyticalTask<E> {
|
|
18
|
+
|
|
19
|
+
@Part @Nullable
|
|
20
|
+
protected Metrics metrics;
|
|
21
|
+
|
|
22
|
+
public abstract void compute(MetricComputerContext context, E entity) throws Exception;
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Implementation example:
|
|
27
|
+
|
|
28
|
+
```java
|
|
29
|
+
public class OrderCountComputer extends DailyMetricComputer<SQLTenant> {
|
|
30
|
+
|
|
31
|
+
@Override
|
|
32
|
+
public Class<SQLTenant> getType() {
|
|
33
|
+
return SQLTenant.class;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
@Override
|
|
37
|
+
public void compute(MetricComputerContext context, SQLTenant tenant) throws Exception {
|
|
38
|
+
int count = oma.select(SQLOrder.class)
|
|
39
|
+
.eq(SQLOrder.TENANT, tenant)
|
|
40
|
+
.where(OMA.FILTERS.gte(SQLOrder.CREATED_AT, context.periodStart()))
|
|
41
|
+
.where(OMA.FILTERS.lte(SQLOrder.CREATED_AT, context.periodEnd()))
|
|
42
|
+
.count();
|
|
43
|
+
metrics.updateDailyMetric("orders", tenant, context.date(), count);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### MonthlyMetricComputer
|
|
49
|
+
|
|
50
|
+
Same pattern, but invoked monthly. Also runs daily for the current month via
|
|
51
|
+
best-effort scheduling to keep preliminary values up to date:
|
|
52
|
+
|
|
53
|
+
```java
|
|
54
|
+
public class MonthlyRevenueComputer extends MonthlyMetricComputer<SQLTenant> {
|
|
55
|
+
|
|
56
|
+
@Override
|
|
57
|
+
public void compute(MetricComputerContext context, SQLTenant tenant) throws Exception {
|
|
58
|
+
// context.periodStart() and context.periodEnd() span the full month
|
|
59
|
+
Amount revenue = computeRevenue(tenant, context.periodStart(), context.periodEnd());
|
|
60
|
+
metrics.updateMonthlyMetric("revenue", tenant, context.date(), revenue.getAmount());
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
@Override
|
|
64
|
+
public boolean suppressBestEffortScheduling() {
|
|
65
|
+
return false; // default: allow daily re-computation of current month
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### MonthlyLargeMetricComputer
|
|
71
|
+
|
|
72
|
+
For expensive computations that should only run once per month (not re-computed
|
|
73
|
+
daily via best effort).
|
|
74
|
+
|
|
75
|
+
## MetricComputerContext
|
|
76
|
+
|
|
77
|
+
Passed to every `compute()` call with pre-calculated time boundaries:
|
|
78
|
+
|
|
79
|
+
| Method | Description |
|
|
80
|
+
|---------------------------------|----------------------------------------------|
|
|
81
|
+
| `date()` | The reference date for the metric |
|
|
82
|
+
| `periodStart()` | Start of the period (LocalDateTime) |
|
|
83
|
+
| `periodEnd()` | End of the period (LocalDateTime) |
|
|
84
|
+
| `periodOutsideOfCurrentInterest()` | True if computing a historical period |
|
|
85
|
+
| `bestEffort()` | True if this is a best-effort re-computation |
|
|
86
|
+
|
|
87
|
+
## Scheduling
|
|
88
|
+
|
|
89
|
+
The analytics scheduler iterates over entities and invokes registered computers.
|
|
90
|
+
There are separate schedulers for JDBC and MongoDB entities:
|
|
91
|
+
|
|
92
|
+
- `SQLAnalyticalTaskScheduler` — schedules tasks for `SQLEntity` types
|
|
93
|
+
- `MongoAnalyticalTaskScheduler` — schedules tasks for `MongoEntity` types
|
|
94
|
+
|
|
95
|
+
Enable the appropriate framework flags:
|
|
96
|
+
|
|
97
|
+
```hocon
|
|
98
|
+
sirius.frameworks {
|
|
99
|
+
biz.analytics-metrics-jdbc = true # Enable JDBC metric computation
|
|
100
|
+
biz.analytics-metrics-mongo = false # Enable MongoDB metric computation
|
|
101
|
+
biz.scheduler-jdbc = true # Enable JDBC scheduler
|
|
102
|
+
biz.scheduler-mongo = false # Enable MongoDB scheduler
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Schedulers use batch emitters (`SQLEntityBatchEmitter`, `MongoEntityBatchEmitter`)
|
|
107
|
+
to process entities in configurable batch sizes via `DistributedTasks`.
|
|
108
|
+
|
|
109
|
+
## Execution Flags (Performance Flags)
|
|
110
|
+
|
|
111
|
+
Performance flags track boolean states per entity (e.g., "has overdue invoices",
|
|
112
|
+
"needs review"). They are stored as composites on entities:
|
|
113
|
+
|
|
114
|
+
- **`SQLPerformanceData`** — for JDBC entities
|
|
115
|
+
- **`MongoPerformanceData`** — for MongoDB entities
|
|
116
|
+
|
|
117
|
+
```java
|
|
118
|
+
@Framework("biz.tenants-jdbc")
|
|
119
|
+
public class SQLTenant extends BizEntity implements Tenant<Long>, PerformanceFlagged {
|
|
120
|
+
private final SQLPerformanceData performanceData = new SQLPerformanceData(this);
|
|
121
|
+
|
|
122
|
+
@Override
|
|
123
|
+
public SQLPerformanceData getPerformanceData() {
|
|
124
|
+
return performanceData;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Enable with:
|
|
130
|
+
|
|
131
|
+
```hocon
|
|
132
|
+
sirius.frameworks {
|
|
133
|
+
biz.analytics-execution-flags-jdbc = true
|
|
134
|
+
biz.analytics-execution-flags-mongo = false
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## AnalyticalTask Interface
|
|
139
|
+
|
|
140
|
+
Both `DailyMetricComputer` and `MonthlyMetricComputer` implement `AnalyticalTask<E>`.
|
|
141
|
+
Override `getLevel()` to control execution order (lower = earlier) when one computer
|
|
142
|
+
depends on another's results. Override `isEnabled()` to conditionally disable.
|
|
143
|
+
|
|
144
|
+
## Common Mistakes
|
|
145
|
+
|
|
146
|
+
1. **Missing scheduler framework flag** — Registering metric computers without
|
|
147
|
+
enabling `biz.scheduler-jdbc` or `biz.scheduler-mongo` means they never execute.
|
|
148
|
+
|
|
149
|
+
2. **Wrong entity type** — A `DailyMetricComputer<SQLTenant>` only runs for
|
|
150
|
+
`SQLTenant` entities. If your app uses MongoDB, you need a Mongo variant.
|
|
151
|
+
|
|
152
|
+
3. **Ignoring `periodOutsideOfCurrentInterest()`** — Use this flag to skip
|
|
153
|
+
expensive external API calls when backfilling historical data.
|
|
154
|
+
|
|
155
|
+
4. **Not using `bestEffort()` correctly** — Best-effort runs provide preliminary
|
|
156
|
+
values for the current period. Do not perform destructive operations during
|
|
157
|
+
best-effort runs.
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# BizController
|
|
2
|
+
|
|
3
|
+
`BizController` extends `BasicController` from sirius-web and serves as the base
|
|
4
|
+
class for all controllers that operate on entities in the biz layer. It provides
|
|
5
|
+
tenant-aware helpers, entity loading from web requests, and pre-injected database
|
|
6
|
+
access parts.
|
|
7
|
+
|
|
8
|
+
## Injected Parts
|
|
9
|
+
|
|
10
|
+
`BizController` comes with these fields already injected:
|
|
11
|
+
|
|
12
|
+
```java
|
|
13
|
+
@Part protected Mixing mixing; // Schema/descriptor access
|
|
14
|
+
@Part protected OMA oma; // JDBC entity operations
|
|
15
|
+
@Part protected Mango mango; // MongoDB entity operations
|
|
16
|
+
@Part protected Elastic elastic; // Elasticsearch operations
|
|
17
|
+
@Part @Nullable protected Tenants<?, ?, ?> tenants; // Tenant management
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
You can use `oma`, `mango`, and `elastic` directly in subclasses without declaring
|
|
21
|
+
your own `@Part` fields.
|
|
22
|
+
|
|
23
|
+
## Loading Entities from Requests — load()
|
|
24
|
+
|
|
25
|
+
The `load()` method reads HTTP parameters into an entity, populating all fields
|
|
26
|
+
marked with `@Autoloaded`:
|
|
27
|
+
|
|
28
|
+
```java
|
|
29
|
+
@Routed(value = "/product/:1/save", methods = HttpMethod.POST)
|
|
30
|
+
@LoginRequired
|
|
31
|
+
public void saveProduct(WebContext webContext, String productId) {
|
|
32
|
+
Product product = find(Product.class, productId);
|
|
33
|
+
|
|
34
|
+
load(webContext, product); // fills all @Autoloaded fields from the request
|
|
35
|
+
oma.update(product);
|
|
36
|
+
showSavedMessage();
|
|
37
|
+
webContext.respondWith().redirectToGet("/products");
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Overloads:
|
|
42
|
+
- `load(webContext, entity)` — loads all `@Autoloaded` properties.
|
|
43
|
+
- `load(webContext, entity, Mapping... properties)` — loads only the listed properties.
|
|
44
|
+
- `load(webContext, entity, List<Mapping> properties)` — same, with a list.
|
|
45
|
+
|
|
46
|
+
## Resolving Entities — find()
|
|
47
|
+
|
|
48
|
+
The `find()` method resolves an entity by its ID string. It also handles the special
|
|
49
|
+
value `"new"`, which creates a fresh instance instead of querying the database:
|
|
50
|
+
|
|
51
|
+
```java
|
|
52
|
+
Product product = find(Product.class, productId);
|
|
53
|
+
// If productId is "new", returns a new Product()
|
|
54
|
+
// Otherwise, looks up the entity — throws 404 if not found
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
For tenant-scoped entities, use `findForTenant()` which additionally verifies that
|
|
58
|
+
the entity belongs to the current user's tenant.
|
|
59
|
+
|
|
60
|
+
## CRUD Flow Pattern
|
|
61
|
+
|
|
62
|
+
The standard pattern for list/edit/delete:
|
|
63
|
+
|
|
64
|
+
```java
|
|
65
|
+
@Register
|
|
66
|
+
public class ProductController extends BizController {
|
|
67
|
+
|
|
68
|
+
@Routed("/products")
|
|
69
|
+
@DefaultRoute
|
|
70
|
+
@LoginRequired
|
|
71
|
+
@Permission("permission-manage-products")
|
|
72
|
+
public void products(WebContext webContext) {
|
|
73
|
+
SQLPageHelper<Product> pageHelper =
|
|
74
|
+
SQLPageHelper.withQuery(tenants.forCurrentTenant(oma.select(Product.class)));
|
|
75
|
+
pageHelper.withBasePath("/products");
|
|
76
|
+
pageHelper.withSearchFields(QueryField.contains(Product.NAME));
|
|
77
|
+
webContext.respondWith()
|
|
78
|
+
.template("/templates/products/list.html.pasta", pageHelper.asPage());
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
@Routed("/product/:1")
|
|
82
|
+
@LoginRequired
|
|
83
|
+
@Permission("permission-manage-products")
|
|
84
|
+
public void editProduct(WebContext webContext, String productId) {
|
|
85
|
+
Product product = findForTenant(Product.class, productId);
|
|
86
|
+
|
|
87
|
+
if (webContext.ensureSafePOST()) {
|
|
88
|
+
load(webContext, product);
|
|
89
|
+
oma.update(product);
|
|
90
|
+
showSavedMessage();
|
|
91
|
+
webContext.respondWith().redirectToGet("/products");
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
webContext.respondWith()
|
|
96
|
+
.template("/templates/products/edit.html.pasta", product);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
@Routed("/product/:1/delete")
|
|
100
|
+
@LoginRequired
|
|
101
|
+
@Permission("permission-manage-products")
|
|
102
|
+
public void deleteProduct(WebContext webContext, String productId) {
|
|
103
|
+
Product product = findForTenant(Product.class, productId);
|
|
104
|
+
oma.delete(product);
|
|
105
|
+
showDeletedMessage();
|
|
106
|
+
webContext.respondWith().redirectToGet("/products");
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Page Helpers
|
|
112
|
+
|
|
113
|
+
For list views with pagination, search, and filtering:
|
|
114
|
+
|
|
115
|
+
- **`SQLPageHelper`** — wraps a `SmartQuery<SQLEntity>` for JDBC entities.
|
|
116
|
+
- **`MongoPageHelper`** — wraps a `MongoQuery<MongoEntity>` for MongoDB entities.
|
|
117
|
+
- **`ElasticPageHelper`** — wraps an `ElasticQuery<ElasticEntity>` for Elasticsearch.
|
|
118
|
+
|
|
119
|
+
```java
|
|
120
|
+
SQLPageHelper<Product> pageHelper =
|
|
121
|
+
SQLPageHelper.withQuery(oma.select(Product.class).orderAsc(Product.NAME));
|
|
122
|
+
pageHelper.withBasePath("/products");
|
|
123
|
+
pageHelper.withSearchFields(QueryField.contains(Product.NAME));
|
|
124
|
+
pageHelper.addBooleanFacet(Product.ACTIVE, NLS.get("Product.active"));
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## @Autoloaded
|
|
128
|
+
|
|
129
|
+
Mark entity properties with `@Autoloaded` to have `load()` pick them up automatically
|
|
130
|
+
from HTTP request parameters. The parameter name matches the property name:
|
|
131
|
+
|
|
132
|
+
```java
|
|
133
|
+
@Autoloaded
|
|
134
|
+
@Length(150)
|
|
135
|
+
@Trim
|
|
136
|
+
private String name; // loaded from request param "name"
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## Common Mistakes
|
|
140
|
+
|
|
141
|
+
1. **Forgetting `ensureSafePOST()`** — Always check for safe POST before mutating
|
|
142
|
+
data. Without this, you are vulnerable to CSRF attacks.
|
|
143
|
+
|
|
144
|
+
2. **Not returning after redirect** — After `redirectToGet()`, always `return`.
|
|
145
|
+
Otherwise the method continues and may try to render a template too.
|
|
146
|
+
|
|
147
|
+
3. **Using `find()` for tenant-scoped entities** — Use `findForTenant()` instead.
|
|
148
|
+
Plain `find()` does not verify the tenant, which allows cross-tenant data access.
|
|
149
|
+
|
|
150
|
+
4. **Forgetting `@DefaultRoute`** — Without a default route, exceptions in other
|
|
151
|
+
routes render a generic error page instead of staying in context.
|