@fluentcommerce/fc-connect-sdk 0.1.48 → 0.1.52
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/CHANGELOG.md +506 -379
- package/README.md +343 -0
- package/dist/cjs/clients/fluent-client.js +110 -14
- package/dist/cjs/data-sources/s3-data-source.js +1 -1
- package/dist/cjs/data-sources/sftp-data-source.js +1 -1
- package/dist/cjs/index.d.ts +1 -1
- package/dist/cjs/services/extraction/extraction-orchestrator.d.ts +4 -1
- package/dist/cjs/services/extraction/extraction-orchestrator.js +84 -11
- package/dist/cjs/types/index.d.ts +79 -10
- package/dist/cjs/versori/fluent-versori-client.d.ts +4 -1
- package/dist/cjs/versori/fluent-versori-client.js +131 -13
- package/dist/esm/clients/fluent-client.js +110 -14
- package/dist/esm/data-sources/s3-data-source.js +1 -1
- package/dist/esm/data-sources/sftp-data-source.js +1 -1
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/services/extraction/extraction-orchestrator.d.ts +4 -1
- package/dist/esm/services/extraction/extraction-orchestrator.js +84 -11
- package/dist/esm/types/index.d.ts +79 -10
- package/dist/esm/versori/fluent-versori-client.d.ts +4 -1
- package/dist/esm/versori/fluent-versori-client.js +131 -13
- package/dist/tsconfig.esm.tsbuildinfo +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/tsconfig.types.tsbuildinfo +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/services/extraction/extraction-orchestrator.d.ts +4 -1
- package/dist/types/types/index.d.ts +79 -10
- package/dist/types/versori/fluent-versori-client.d.ts +4 -1
- package/docs/02-CORE-GUIDES/api-reference/event-api-input-output-reference.md +478 -18
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-01-client-api.md +83 -0
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-08-types.md +52 -0
- package/docs/02-CORE-GUIDES/api-reference/modules/api-reference-12-partial-responses.md +212 -0
- package/docs/02-CORE-GUIDES/api-reference/readme.md +1 -1
- package/docs/02-CORE-GUIDES/extraction/modules/02-core-guides-extraction-08-extraction-orchestrator.md +68 -4
- package/docs/02-CORE-GUIDES/mapping/modules/mapping-01-foundations.md +450 -448
- package/docs/02-CORE-GUIDES/mapping/modules/mapping-02-quick-start.md +476 -474
- package/docs/02-CORE-GUIDES/mapping/modules/mapping-03-schema-validation.md +464 -462
- package/docs/02-CORE-GUIDES/mapping/modules/mapping-05-advanced-patterns.md +1366 -1364
- package/docs/readme.md +245 -245
- package/package.json +17 -6
- package/docs/versori-apis/ACTIVATIONS-AND-VARIABLES-GUIDE.md +0 -60
- package/docs/versori-apis/JWT-GENERATION-GUIDE.md +0 -94
- package/docs/versori-apis/QUICK-WORKFLOW.md +0 -293
- package/docs/versori-apis/README.md +0 -73
- package/docs/versori-apis/VERSORI-PLATFORM-ARCHITECTURE.md +0 -880
- package/docs/versori-apis/Versori-Platform-API.postman_collection.json +0 -2925
- package/docs/versori-apis/Versori-Platform-API.postman_environment.example.json +0 -62
- package/docs/versori-apis/Versori-Platform-API.postman_environment.json +0 -178
|
@@ -80,8 +80,9 @@ export class FluentVersoriClient {
|
|
|
80
80
|
});
|
|
81
81
|
const paginationVars = detectPaginationVariables(payload.query);
|
|
82
82
|
const paginationEnabled = !isMutation && (payload.pagination?.enabled ?? paginationVars.hasPagination);
|
|
83
|
+
const errorHandling = payload.errorHandling ?? 'throw';
|
|
83
84
|
if (!paginationEnabled) {
|
|
84
|
-
return this.executeSinglePage(payload);
|
|
85
|
+
return this.executeSinglePage(payload, errorHandling);
|
|
85
86
|
}
|
|
86
87
|
const direction = payload.pagination?.direction ??
|
|
87
88
|
paginationVars.direction ??
|
|
@@ -97,10 +98,11 @@ export class FluentVersoriClient {
|
|
|
97
98
|
abortSignal: payload.pagination?.abortSignal,
|
|
98
99
|
onProgress: payload.pagination?.onProgress ?? (() => { }),
|
|
99
100
|
onWarning: payload.pagination?.onWarning ?? ((msg) => this.ctx.log?.warn?.(msg)),
|
|
101
|
+
errorHandling,
|
|
100
102
|
};
|
|
101
103
|
return this.executeWithAutoPagination(payload, paginationVars, config);
|
|
102
104
|
}
|
|
103
|
-
async executeSinglePage(payload) {
|
|
105
|
+
async executeSinglePage(payload, errorHandling = 'throw') {
|
|
104
106
|
const operationName = payload.operationName || 'unnamed';
|
|
105
107
|
try {
|
|
106
108
|
const apiPayload = {
|
|
@@ -139,6 +141,18 @@ export class FluentVersoriClient {
|
|
|
139
141
|
throw new Error('Failed to parse GraphQL response');
|
|
140
142
|
}
|
|
141
143
|
if (parsedResponse.errors && parsedResponse.errors.length > 0) {
|
|
144
|
+
if (errorHandling === 'partial') {
|
|
145
|
+
this.ctx.log.warn(`[fc-connect-sdk:graphql] GraphQL operation "${operationName}" returned partial data with ${parsedResponse.errors.length} error(s)`, {
|
|
146
|
+
operationName,
|
|
147
|
+
errors: parsedResponse.errors,
|
|
148
|
+
errorCount: parsedResponse.errors.length,
|
|
149
|
+
hasData: !!parsedResponse.data,
|
|
150
|
+
});
|
|
151
|
+
return {
|
|
152
|
+
...parsedResponse,
|
|
153
|
+
hasPartialData: true,
|
|
154
|
+
};
|
|
155
|
+
}
|
|
142
156
|
this.ctx.log.error(`[fc-connect-sdk:graphql] GraphQL operation "${operationName}" returned ${parsedResponse.errors.length} error(s)`, {
|
|
143
157
|
operationName,
|
|
144
158
|
errors: parsedResponse.errors,
|
|
@@ -175,6 +189,7 @@ export class FluentVersoriClient {
|
|
|
175
189
|
let currentVariables = { ...(payload.variables || {}) };
|
|
176
190
|
let lastCursor = null;
|
|
177
191
|
let emptyPageCount = 0;
|
|
192
|
+
const accumulatedErrors = [];
|
|
178
193
|
while (true) {
|
|
179
194
|
if (config.abortSignal?.aborted) {
|
|
180
195
|
truncated = true;
|
|
@@ -197,17 +212,29 @@ export class FluentVersoriClient {
|
|
|
197
212
|
const response = await this.executeSinglePage({
|
|
198
213
|
query: payload.query,
|
|
199
214
|
variables: currentVariables,
|
|
200
|
-
});
|
|
215
|
+
}, 'partial');
|
|
201
216
|
lastExtensions = response.extensions;
|
|
202
217
|
if (response.errors && response.errors.length > 0) {
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
218
|
+
if (config.errorHandling === 'partial') {
|
|
219
|
+
this.ctx.log.warn(`[fc-connect-sdk:pagination] Page ${pageNumber + 1} returned partial data with ${response.errors.length} error(s)`, {
|
|
220
|
+
errors: response.errors,
|
|
221
|
+
pagesCompleted: pageNumber,
|
|
222
|
+
totalRecordsSoFar: totalRecords,
|
|
223
|
+
errorCount: response.errors.length,
|
|
224
|
+
hasData: !!response.data,
|
|
225
|
+
});
|
|
226
|
+
accumulatedErrors.push(...response.errors);
|
|
227
|
+
}
|
|
228
|
+
else {
|
|
229
|
+
this.ctx.log.error(`[fc-connect-sdk:pagination] GraphQL errors during pagination (page ${pageNumber + 1}, ${totalRecords} records fetched so far)`, {
|
|
230
|
+
errors: response.errors,
|
|
231
|
+
pagesCompleted: pageNumber,
|
|
232
|
+
totalRecordsBeforeError: totalRecords,
|
|
233
|
+
errorCount: response.errors.length,
|
|
234
|
+
});
|
|
235
|
+
const firstError = response.errors[0];
|
|
236
|
+
throw new GraphQLExecutionError(`GraphQL error during pagination (page ${pageNumber}): ${firstError.message || 'Unknown error'}`, response.errors, payload.query, currentVariables);
|
|
237
|
+
}
|
|
211
238
|
}
|
|
212
239
|
pageNumber++;
|
|
213
240
|
const connection = extractConnection(response.data, config.connectionPath);
|
|
@@ -310,14 +337,17 @@ export class FluentVersoriClient {
|
|
|
310
337
|
await new Promise(resolve => setTimeout(resolve, config.delayMs));
|
|
311
338
|
}
|
|
312
339
|
}
|
|
313
|
-
|
|
340
|
+
const hasPartialErrors = accumulatedErrors.length > 0;
|
|
341
|
+
this.ctx.log?.info?.(`[fc-connect-sdk:pagination] Pagination complete (${pageNumber} pages, ${totalRecords} records${truncated ? `, truncated: ${truncationReason}` : ''}${hasPartialErrors ? `, ${accumulatedErrors.length} errors` : ''})`, {
|
|
314
342
|
totalPages: pageNumber,
|
|
315
343
|
totalRecords,
|
|
316
344
|
truncated,
|
|
317
345
|
truncationReason,
|
|
318
346
|
duration: Date.now() - startTime,
|
|
347
|
+
hasPartialErrors,
|
|
348
|
+
errorCount: accumulatedErrors.length,
|
|
319
349
|
});
|
|
320
|
-
|
|
350
|
+
const response = {
|
|
321
351
|
data: allData,
|
|
322
352
|
extensions: {
|
|
323
353
|
...(lastExtensions || {}),
|
|
@@ -330,6 +360,11 @@ export class FluentVersoriClient {
|
|
|
330
360
|
},
|
|
331
361
|
},
|
|
332
362
|
};
|
|
363
|
+
if (hasPartialErrors) {
|
|
364
|
+
response.errors = accumulatedErrors;
|
|
365
|
+
response.hasPartialData = true;
|
|
366
|
+
}
|
|
367
|
+
return response;
|
|
333
368
|
}
|
|
334
369
|
async sendEvent(event, mode = 'async') {
|
|
335
370
|
this.ctx.log.info(`[fc-connect-sdk:event] Sending event "${event.name}" (${event.entityType}:${event.entityRef}, mode: ${mode})`, {
|
|
@@ -389,6 +424,79 @@ export class FluentVersoriClient {
|
|
|
389
424
|
return { success: true, statusCode: response.status, message: text };
|
|
390
425
|
}
|
|
391
426
|
}
|
|
427
|
+
async getEvents(params = {}) {
|
|
428
|
+
const query = this.buildEventQueryString(params);
|
|
429
|
+
const endpoint = query ? `/api/v4.1/event?${query}` : '/api/v4.1/event';
|
|
430
|
+
this.ctx.log.info('[fc-connect-sdk:event] Searching event logs', {
|
|
431
|
+
endpoint,
|
|
432
|
+
filterCount: Object.keys(params).length,
|
|
433
|
+
hasDateRange: !!(params.from || params.to),
|
|
434
|
+
entityType: params['context.entityType'],
|
|
435
|
+
rootEntityType: params['context.rootEntityType'],
|
|
436
|
+
eventStatus: params.eventStatus,
|
|
437
|
+
});
|
|
438
|
+
const response = await this.fetchWithRetry(endpoint, {
|
|
439
|
+
method: 'GET',
|
|
440
|
+
headers: {
|
|
441
|
+
'Content-Type': 'application/json',
|
|
442
|
+
},
|
|
443
|
+
});
|
|
444
|
+
const text = response.text;
|
|
445
|
+
if (!response.ok) {
|
|
446
|
+
this.ctx.log.error('[fc-connect-sdk:event] Failed to search events', {
|
|
447
|
+
status: response.status,
|
|
448
|
+
body: text,
|
|
449
|
+
});
|
|
450
|
+
this.throwApiError('Get events failed', response.status, text);
|
|
451
|
+
}
|
|
452
|
+
try {
|
|
453
|
+
const result = JSON.parse(text);
|
|
454
|
+
this.ctx.log.info('[fc-connect-sdk:event] Event search completed', {
|
|
455
|
+
resultCount: result.count,
|
|
456
|
+
hasMore: result.hasMore,
|
|
457
|
+
start: result.start,
|
|
458
|
+
});
|
|
459
|
+
return result;
|
|
460
|
+
}
|
|
461
|
+
catch {
|
|
462
|
+
throw new FluentAPIError('Failed to parse event log response', response.status, text);
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
async getEventById(eventId) {
|
|
466
|
+
if (!eventId) {
|
|
467
|
+
throw new FluentValidationError('eventId is required');
|
|
468
|
+
}
|
|
469
|
+
this.ctx.log.info(`[fc-connect-sdk:event] Getting event by ID: ${eventId}`, { eventId });
|
|
470
|
+
const response = await this.fetchWithRetry(`/api/v4.1/event/${eventId}`, {
|
|
471
|
+
method: 'GET',
|
|
472
|
+
headers: {
|
|
473
|
+
'Content-Type': 'application/json',
|
|
474
|
+
},
|
|
475
|
+
});
|
|
476
|
+
const text = response.text;
|
|
477
|
+
if (!response.ok) {
|
|
478
|
+
this.ctx.log.error(`[fc-connect-sdk:event] Failed to get event ${eventId}`, {
|
|
479
|
+
eventId,
|
|
480
|
+
status: response.status,
|
|
481
|
+
body: text,
|
|
482
|
+
});
|
|
483
|
+
this.throwApiError(`Get event ${eventId} failed`, response.status, text);
|
|
484
|
+
}
|
|
485
|
+
try {
|
|
486
|
+
const event = JSON.parse(text);
|
|
487
|
+
this.ctx.log.info(`[fc-connect-sdk:event] Event retrieved: ${event.name}`, {
|
|
488
|
+
eventId: event.id,
|
|
489
|
+
name: event.name,
|
|
490
|
+
type: event.type,
|
|
491
|
+
eventStatus: event.eventStatus,
|
|
492
|
+
entityType: event.context?.entityType,
|
|
493
|
+
});
|
|
494
|
+
return event;
|
|
495
|
+
}
|
|
496
|
+
catch {
|
|
497
|
+
throw new FluentAPIError('Failed to parse event response', response.status, text);
|
|
498
|
+
}
|
|
499
|
+
}
|
|
392
500
|
async createJob(payload) {
|
|
393
501
|
this.ctx.log.info(`[fc-connect-sdk:job] Creating job "${payload.name}"`, {
|
|
394
502
|
name: payload.name,
|
|
@@ -536,6 +644,16 @@ export class FluentVersoriClient {
|
|
|
536
644
|
const result = await this.graphql(payload);
|
|
537
645
|
return result.data;
|
|
538
646
|
}
|
|
647
|
+
buildEventQueryString(params) {
|
|
648
|
+
const searchParams = new URLSearchParams();
|
|
649
|
+
for (const [key, value] of Object.entries(params)) {
|
|
650
|
+
if (value === undefined || value === null || value === '') {
|
|
651
|
+
continue;
|
|
652
|
+
}
|
|
653
|
+
searchParams.append(key, String(value));
|
|
654
|
+
}
|
|
655
|
+
return searchParams.toString();
|
|
656
|
+
}
|
|
539
657
|
throwApiError(message, status, responseText) {
|
|
540
658
|
if (status === 401) {
|
|
541
659
|
throw new AuthenticationError(`${message}: ${responseText}`, { response: responseText });
|