@d19n/youfibre-odin-sdk 1.0.293 → 1.0.294
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/README.md +109 -18
- package/dist/runner.js +105 -28
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -52,11 +52,18 @@ await cboRec
|
|
|
52
52
|
})
|
|
53
53
|
.execute();
|
|
54
54
|
|
|
55
|
-
// Create a new record with links
|
|
55
|
+
// Create a new record with generic links
|
|
56
56
|
const note = odin.notes.new();
|
|
57
57
|
await note
|
|
58
58
|
.link({ id: orderId, entity: "OrderModule:Order" })
|
|
59
59
|
.createNote({ Body: "Customer called about billing" });
|
|
60
|
+
|
|
61
|
+
// Create a record with typed associations (builder pattern)
|
|
62
|
+
await odin.referrals.new()
|
|
63
|
+
.createOrderReferral({})
|
|
64
|
+
.linkCampaignReferrals(campaign) // Typed association method
|
|
65
|
+
.linkOrderReferral({ id: orderId, entity: "OrderModule:Order" })
|
|
66
|
+
.execute();
|
|
60
67
|
```
|
|
61
68
|
|
|
62
69
|
### DB Client (Backend Services)
|
|
@@ -146,7 +153,7 @@ const order = await odin.orders.get(orderId);
|
|
|
146
153
|
// Access typed properties
|
|
147
154
|
console.log(order.id); // string
|
|
148
155
|
console.log(order.properties); // OrderProperties
|
|
149
|
-
console.log(order.
|
|
156
|
+
console.log(order.record); // Full OdinRecord
|
|
150
157
|
|
|
151
158
|
// Check if record is new (unsaved)
|
|
152
159
|
if (order.isNew) {
|
|
@@ -160,6 +167,8 @@ if (order.isNew) {
|
|
|
160
167
|
|
|
161
168
|
### Link Management
|
|
162
169
|
|
|
170
|
+
#### Generic Links (.link())
|
|
171
|
+
|
|
163
172
|
```typescript
|
|
164
173
|
// Queue links to be sent with next action
|
|
165
174
|
const note = odin.notes.new();
|
|
@@ -172,6 +181,19 @@ await note
|
|
|
172
181
|
await order.unlink(contactId);
|
|
173
182
|
```
|
|
174
183
|
|
|
184
|
+
#### Named Associations (Builder Pattern)
|
|
185
|
+
|
|
186
|
+
Actions with association fields generate typed `.link*()` methods:
|
|
187
|
+
|
|
188
|
+
```typescript
|
|
189
|
+
// IDE autocomplete shows available association methods
|
|
190
|
+
await odin.referrals.new()
|
|
191
|
+
.createOrderReferral({})
|
|
192
|
+
.linkCampaignReferrals({ id: campaignId, entity: "CrmModule:Campaign" })
|
|
193
|
+
.linkOrderReferral(order) // Pass OdinRecord directly
|
|
194
|
+
.execute();
|
|
195
|
+
```
|
|
196
|
+
|
|
175
197
|
### STEP_FLOW Actions (Multi-Step Workflows)
|
|
176
198
|
|
|
177
199
|
STEP_FLOW actions allow executing multiple steps in a single transaction using a FlowBuilder pattern.
|
|
@@ -232,14 +254,14 @@ await workOrderRec
|
|
|
232
254
|
.execute();
|
|
233
255
|
```
|
|
234
256
|
|
|
235
|
-
#### With Associations
|
|
257
|
+
#### With Associations (Generic .link())
|
|
236
258
|
|
|
237
259
|
```typescript
|
|
238
|
-
//
|
|
260
|
+
// Queue generic links to be sent with next action
|
|
239
261
|
const invoiceRec = await odin.invoices.get(invoiceId);
|
|
240
262
|
|
|
241
263
|
await invoiceRec
|
|
242
|
-
.link({ id: contactId, entity: "CrmModule:Contact" }) //
|
|
264
|
+
.link({ id: contactId, entity: "CrmModule:Contact" }) // Generic link
|
|
243
265
|
.processInvoiceFlow({ journeyId: orderId })
|
|
244
266
|
.approveInvoice(invoiceRec.id, {
|
|
245
267
|
ApprovedBy: userId,
|
|
@@ -248,25 +270,94 @@ await invoiceRec
|
|
|
248
270
|
.execute(); // Links are sent with the action
|
|
249
271
|
```
|
|
250
272
|
|
|
273
|
+
#### With Named Associations (Builder Pattern)
|
|
274
|
+
|
|
275
|
+
Actions with association fields generate typed `.link*()` methods:
|
|
276
|
+
|
|
277
|
+
```typescript
|
|
278
|
+
// Typed association methods - IDE autocomplete shows available links
|
|
279
|
+
await odin.referrals.new()
|
|
280
|
+
.createOrderReferral({})
|
|
281
|
+
.linkCampaignReferrals({ id: campaignId, entity: "CrmModule:Campaign" })
|
|
282
|
+
.linkOrderReferral(order) // Can pass OdinRecord directly
|
|
283
|
+
.execute();
|
|
284
|
+
|
|
285
|
+
// Mix generic .link() with named associations
|
|
286
|
+
await odin.referrals.new()
|
|
287
|
+
.link({ id: accountId, entity: "CrmModule:Account" }) // Generic link
|
|
288
|
+
.createOrderReferral({})
|
|
289
|
+
.linkCampaignReferrals(campaign) // Named association
|
|
290
|
+
.execute();
|
|
291
|
+
|
|
292
|
+
// Inspect payload before executing
|
|
293
|
+
const builder = odin.referrals.new()
|
|
294
|
+
.createOrderReferral({})
|
|
295
|
+
.linkCampaignReferrals({ id: campaignId, entity: "CrmModule:Campaign" });
|
|
296
|
+
|
|
297
|
+
console.log(builder.getData()); // View what will be sent
|
|
298
|
+
await builder.execute(); // Then execute
|
|
299
|
+
```
|
|
300
|
+
|
|
251
301
|
## Events
|
|
252
302
|
|
|
253
|
-
Subscribe to RabbitMQ events using
|
|
303
|
+
Subscribe to RabbitMQ events using `@RabbitSubscribe` with SDK routing keys:
|
|
254
304
|
|
|
255
305
|
```typescript
|
|
256
|
-
import {
|
|
257
|
-
|
|
306
|
+
import {
|
|
307
|
+
WORK_ORDER,
|
|
308
|
+
SUB_DB_RECORD_UPDATED,
|
|
309
|
+
SUB_DB_RECORD_CREATED,
|
|
310
|
+
} from '@d19n/youfibre-odin-sdk/entities';
|
|
311
|
+
import { IDbRecordUpdated, IDbRecordCreated } from '@d19n/odin-types/dist/core';
|
|
312
|
+
import { LogsConstants } from '@d19n/logs/dist/constants';
|
|
313
|
+
|
|
314
|
+
// Subscribe to record updated events
|
|
315
|
+
@RabbitSubscribe({
|
|
316
|
+
exchange: process.env.ODIN_ORG_ID,
|
|
317
|
+
routingKey: `FieldServiceModule.${WORK_ORDER}.${SUB_DB_RECORD_UPDATED}`,
|
|
318
|
+
queue: `${process.env.DOMAIN}.WorkOrderHandler.handleRecordUpdatedEvent`,
|
|
319
|
+
errorHandler: SubscriberErrorHandler.handleError,
|
|
320
|
+
})
|
|
321
|
+
private async handleRecordUpdatedEvent(msg: IDbRecordUpdated) {
|
|
322
|
+
try {
|
|
323
|
+
// Check for stage changes
|
|
324
|
+
if (msg.event === LogsConstants.DB_RECORD_STAGE_UPDATED) {
|
|
325
|
+
const { id, entity, properties, stage } = msg;
|
|
326
|
+
// Handle stage transition
|
|
327
|
+
}
|
|
328
|
+
} catch (e) {
|
|
329
|
+
// Error handling
|
|
330
|
+
}
|
|
331
|
+
}
|
|
258
332
|
|
|
259
|
-
//
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
}
|
|
333
|
+
// Subscribe to record created events
|
|
334
|
+
@RabbitSubscribe({
|
|
335
|
+
exchange: process.env.ODIN_ORG_ID,
|
|
336
|
+
routingKey: `FieldServiceModule.${WORK_ORDER}.${SUB_DB_RECORD_CREATED}`,
|
|
337
|
+
queue: `${process.env.DOMAIN}.WorkOrderHandler.handleRecordCreatedEvent`,
|
|
338
|
+
errorHandler: SubscriberErrorHandler.handleError,
|
|
339
|
+
})
|
|
340
|
+
private async handleRecordCreatedEvent(msg: IDbRecordCreated) {
|
|
341
|
+
try {
|
|
342
|
+
const { id, entity, properties } = msg;
|
|
343
|
+
// Handle new record
|
|
344
|
+
} catch (e) {
|
|
345
|
+
// Error handling
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
```
|
|
264
349
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
350
|
+
### Common Event Constants
|
|
351
|
+
|
|
352
|
+
```typescript
|
|
353
|
+
// Record lifecycle events
|
|
354
|
+
SUB_DB_RECORD_CREATED // New record created
|
|
355
|
+
SUB_DB_RECORD_UPDATED // Record updated (includes stage changes)
|
|
356
|
+
SUB_DB_RECORD_DELETED // Record deleted
|
|
357
|
+
|
|
358
|
+
// Check event type in handler
|
|
359
|
+
if (msg.event === LogsConstants.DB_RECORD_STAGE_UPDATED) { ... }
|
|
360
|
+
if (msg.event === LogsConstants.DB_RECORD_UPDATED) { ... }
|
|
270
361
|
```
|
|
271
362
|
|
|
272
363
|
## Migration Guide
|
package/dist/runner.js
CHANGED
|
@@ -63,6 +63,73 @@ const odin_sdk_generator_1 = require("@d19n/odin-sdk-generator");
|
|
|
63
63
|
const dotenv = __importStar(require("dotenv"));
|
|
64
64
|
const fs = __importStar(require("fs"));
|
|
65
65
|
const path = __importStar(require("path"));
|
|
66
|
+
/**
|
|
67
|
+
* Custom error class for SDK generation failures
|
|
68
|
+
*/
|
|
69
|
+
class SdkGenerationError extends Error {
|
|
70
|
+
constructor(step, reason, missingPaths) {
|
|
71
|
+
super(`SDK Generation failed at step "${step}": ${reason}`);
|
|
72
|
+
this.step = step;
|
|
73
|
+
this.reason = reason;
|
|
74
|
+
this.missingPaths = missingPaths;
|
|
75
|
+
this.name = 'SdkGenerationError';
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Verify that a directory exists and contains at least one file
|
|
80
|
+
*/
|
|
81
|
+
function verifyDirectoryOutput(dir, stepName, minFiles = 1) {
|
|
82
|
+
if (!fs.existsSync(dir)) {
|
|
83
|
+
throw new SdkGenerationError(stepName, `Output directory does not exist`, [dir]);
|
|
84
|
+
}
|
|
85
|
+
const files = fs.readdirSync(dir).filter(f => f.endsWith('.ts'));
|
|
86
|
+
if (files.length < minFiles) {
|
|
87
|
+
throw new SdkGenerationError(stepName, `Output directory has insufficient files (expected at least ${minFiles}, found ${files.length})`, [dir]);
|
|
88
|
+
}
|
|
89
|
+
console.log(` ✓ Verified: ${dir} (${files.length} files)`);
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Verify that specific files exist
|
|
93
|
+
*/
|
|
94
|
+
function verifyFilesExist(files, stepName) {
|
|
95
|
+
const missing = [];
|
|
96
|
+
for (const file of files) {
|
|
97
|
+
if (!fs.existsSync(file)) {
|
|
98
|
+
missing.push(file);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
if (missing.length > 0) {
|
|
102
|
+
throw new SdkGenerationError(stepName, `Expected output files are missing`, missing);
|
|
103
|
+
}
|
|
104
|
+
for (const file of files) {
|
|
105
|
+
console.log(` ✓ Verified: ${file}`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Run a generation step with error handling and verification
|
|
110
|
+
*/
|
|
111
|
+
function runStep(stepNumber, totalSteps, stepName, generatorFn, verifyFn) {
|
|
112
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
113
|
+
console.log(`\n[${stepNumber}/${totalSteps}] ${stepName}...`);
|
|
114
|
+
try {
|
|
115
|
+
const result = yield generatorFn();
|
|
116
|
+
// Run verification if provided
|
|
117
|
+
if (verifyFn) {
|
|
118
|
+
verifyFn();
|
|
119
|
+
}
|
|
120
|
+
return result;
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
// If it's already our error type, rethrow
|
|
124
|
+
if (error instanceof SdkGenerationError) {
|
|
125
|
+
throw error;
|
|
126
|
+
}
|
|
127
|
+
// Wrap other errors
|
|
128
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
129
|
+
throw new SdkGenerationError(stepName, message);
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
}
|
|
66
133
|
function cleanDirectory(dir) {
|
|
67
134
|
if (fs.existsSync(dir)) {
|
|
68
135
|
console.log(`Cleaning ${dir}...`);
|
|
@@ -77,6 +144,7 @@ const entitiesToExclude = ['Dashboard'];
|
|
|
77
144
|
const packageName = '@d19n/youfibre-odin-sdk';
|
|
78
145
|
function generate() {
|
|
79
146
|
return __awaiter(this, void 0, void 0, function* () {
|
|
147
|
+
const totalSteps = 11;
|
|
80
148
|
console.log('='.repeat(60));
|
|
81
149
|
console.log('ODIN SDK Generation');
|
|
82
150
|
console.log('='.repeat(60));
|
|
@@ -106,61 +174,47 @@ function generate() {
|
|
|
106
174
|
// ============================================
|
|
107
175
|
// STEP 1: Generate Actions (MUST be first)
|
|
108
176
|
// ============================================
|
|
109
|
-
|
|
110
|
-
yield (0, odin_sdk_generator_1.generateActions)();
|
|
177
|
+
yield runStep(1, totalSteps, 'Generating actions', () => (0, odin_sdk_generator_1.generateActions)(), () => verifyDirectoryOutput('./actions-v2', 'Generate Actions'));
|
|
111
178
|
// ============================================
|
|
112
179
|
// STEP 2: Generate Entities
|
|
113
180
|
// ============================================
|
|
114
|
-
|
|
115
|
-
yield (0, odin_sdk_generator_1.generateEntities)({ entitiesToExclude });
|
|
181
|
+
yield runStep(2, totalSteps, 'Generating entities', () => (0, odin_sdk_generator_1.generateEntities)({ entitiesToExclude }), () => verifyDirectoryOutput('./entities-v2', 'Generate Entities'));
|
|
116
182
|
// ============================================
|
|
117
183
|
// STEP 3: Generate Legacy API SDK (deprecated)
|
|
118
184
|
// ============================================
|
|
119
|
-
|
|
120
|
-
yield (0, odin_sdk_generator_1.generateApiSdk)({ entitiesToExclude });
|
|
185
|
+
yield runStep(3, totalSteps, 'Generating legacy API SDK (deprecated)', () => (0, odin_sdk_generator_1.generateApiSdk)({ entitiesToExclude }), () => verifyDirectoryOutput('./api-sdk-v2', 'Generate API SDK'));
|
|
121
186
|
// ============================================
|
|
122
187
|
// STEP 4: Generate Legacy DB SDK (deprecated)
|
|
123
188
|
// ============================================
|
|
124
|
-
|
|
125
|
-
yield (0, odin_sdk_generator_1.generateDbSdk)({ entitiesToExclude });
|
|
189
|
+
yield runStep(4, totalSteps, 'Generating legacy DB SDK (deprecated)', () => (0, odin_sdk_generator_1.generateDbSdk)({ entitiesToExclude }), () => verifyDirectoryOutput('./db-sdk-v2', 'Generate DB SDK'));
|
|
126
190
|
// ============================================
|
|
127
191
|
// STEP 5: Generate Record Wrappers
|
|
128
192
|
// ============================================
|
|
129
|
-
|
|
130
|
-
yield (0, odin_sdk_generator_1.generateRecordWrappers)({ entitiesToExclude });
|
|
193
|
+
yield runStep(5, totalSteps, 'Generating record wrappers', () => (0, odin_sdk_generator_1.generateRecordWrappers)({ entitiesToExclude }), () => verifyDirectoryOutput('./records-v2', 'Generate Record Wrappers'));
|
|
131
194
|
// ============================================
|
|
132
195
|
// STEP 6: Generate API Accessors
|
|
133
196
|
// ============================================
|
|
134
|
-
|
|
135
|
-
yield (0, odin_sdk_generator_1.generateEntityAccessors)({ entitiesToExclude });
|
|
197
|
+
yield runStep(6, totalSteps, 'Generating API accessors', () => (0, odin_sdk_generator_1.generateEntityAccessors)({ entitiesToExclude }), () => verifyDirectoryOutput('./accessors-v2', 'Generate API Accessors'));
|
|
136
198
|
// ============================================
|
|
137
199
|
// STEP 7: Generate DB Accessors
|
|
138
200
|
// ============================================
|
|
139
|
-
|
|
140
|
-
yield (0, odin_sdk_generator_1.generateDbAccessors)({ entitiesToExclude });
|
|
201
|
+
yield runStep(7, totalSteps, 'Generating DB accessors', () => (0, odin_sdk_generator_1.generateDbAccessors)({ entitiesToExclude }), () => verifyDirectoryOutput('./db-accessors-v2', 'Generate DB Accessors'));
|
|
141
202
|
// ============================================
|
|
142
203
|
// STEP 8: Generate Unified Clients
|
|
143
204
|
// ============================================
|
|
144
|
-
|
|
145
|
-
yield (0, odin_sdk_generator_1.generateTypedClient)();
|
|
205
|
+
yield runStep(8, totalSteps, 'Generating unified clients', () => (0, odin_sdk_generator_1.generateTypedClient)(), () => verifyFilesExist(['./OdinApiClient.ts', './OdinDbClient.ts'], 'Generate Unified Clients'));
|
|
146
206
|
// ============================================
|
|
147
207
|
// STEP 9: Generate Entity Registry
|
|
148
208
|
// ============================================
|
|
149
|
-
|
|
150
|
-
yield (0, odin_sdk_generator_1.generateEntityRegistry)();
|
|
209
|
+
yield runStep(9, totalSteps, 'Generating entity registry', () => (0, odin_sdk_generator_1.generateEntityRegistry)(), () => verifyFilesExist(['./EntityRegistry.ts'], 'Generate Entity Registry'));
|
|
151
210
|
// ============================================
|
|
152
211
|
// STEP 10: Generate Root Barrel Exports
|
|
153
212
|
// ============================================
|
|
154
|
-
|
|
155
|
-
yield (0, odin_sdk_generator_1.generateBarrelExports)();
|
|
213
|
+
yield runStep(10, totalSteps, 'Generating root barrel exports', () => (0, odin_sdk_generator_1.generateBarrelExports)(), () => verifyFilesExist(['./index.ts'], 'Generate Barrel Exports'));
|
|
156
214
|
// ============================================
|
|
157
215
|
// STEP 11: Generate README Documentation
|
|
158
216
|
// ============================================
|
|
159
|
-
|
|
160
|
-
yield (0, odin_sdk_generator_1.generateReadme)({
|
|
161
|
-
packageName,
|
|
162
|
-
entitiesToExclude,
|
|
163
|
-
});
|
|
217
|
+
yield runStep(11, totalSteps, 'Generating README documentation', () => (0, odin_sdk_generator_1.generateReadme)({ packageName, entitiesToExclude }), () => verifyFilesExist(['./README.md'], 'Generate README'));
|
|
164
218
|
// NOTE: entities-v2 and actions-v2 barrel exports are handled by
|
|
165
219
|
// steps 1 and 2 (generateActions, generateEntities). They are NOT
|
|
166
220
|
// re-exported from the root index.ts due to export name conflicts.
|
|
@@ -169,7 +223,7 @@ function generate() {
|
|
|
169
223
|
// DONE
|
|
170
224
|
// ============================================
|
|
171
225
|
console.log('\n' + '='.repeat(60));
|
|
172
|
-
console.log('Generation complete!');
|
|
226
|
+
console.log('✓ Generation complete!');
|
|
173
227
|
console.log('='.repeat(60));
|
|
174
228
|
console.log(`
|
|
175
229
|
Generated directories:
|
|
@@ -190,5 +244,28 @@ Generated files:
|
|
|
190
244
|
`);
|
|
191
245
|
});
|
|
192
246
|
}
|
|
193
|
-
// Run generation
|
|
194
|
-
generate().catch(
|
|
247
|
+
// Run generation with proper error handling
|
|
248
|
+
generate().catch((error) => {
|
|
249
|
+
var _a;
|
|
250
|
+
console.error('\n' + '='.repeat(60));
|
|
251
|
+
console.error('✗ SDK GENERATION FAILED');
|
|
252
|
+
console.error('='.repeat(60));
|
|
253
|
+
if (error instanceof SdkGenerationError) {
|
|
254
|
+
console.error(`\nStep: ${error.step}`);
|
|
255
|
+
console.error(`Reason: ${error.reason}`);
|
|
256
|
+
if ((_a = error.missingPaths) === null || _a === void 0 ? void 0 : _a.length) {
|
|
257
|
+
console.error('\nMissing paths:');
|
|
258
|
+
for (const p of error.missingPaths) {
|
|
259
|
+
console.error(` - ${p}`);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
else {
|
|
264
|
+
console.error(`\nError: ${error.message || error}`);
|
|
265
|
+
}
|
|
266
|
+
console.error('\n' + '='.repeat(60));
|
|
267
|
+
console.error('Build aborted. No SDK version will be published.');
|
|
268
|
+
console.error('='.repeat(60));
|
|
269
|
+
// Exit with error code to fail CI/CD pipeline
|
|
270
|
+
process.exit(1);
|
|
271
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@d19n/youfibre-odin-sdk",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.294",
|
|
4
4
|
"description": "",
|
|
5
5
|
"author": "@d19n",
|
|
6
6
|
"license": "UNLICENSED",
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"typescript": "4.8.4"
|
|
12
12
|
},
|
|
13
13
|
"dependencies": {
|
|
14
|
-
"@d19n/odin-sdk-generator": "^3.0.
|
|
14
|
+
"@d19n/odin-sdk-generator": "^3.0.188",
|
|
15
15
|
"@d19n/odin-types": "^3.0.18",
|
|
16
16
|
"@d19n/youfibre-odin-sdk": "^1.0.219"
|
|
17
17
|
},
|