@fluentcommerce/fc-connect-sdk 0.1.51 → 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 CHANGED
@@ -7,6 +7,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ _No unreleased changes_
11
+
12
+ ## [0.1.52] - 2026-02-15
13
+
14
+ ### Added - Event API Log Query (REST GET Endpoints)
15
+ - Added `getEvents(params)` to `FluentClient` and `FluentVersoriClient` for `GET /api/v4.1/event`
16
+ - Search/query events with comprehensive filtering (entity type, status, date range, etc.)
17
+ - Supports pagination with `start`/`count` parameters (max 5000 per page)
18
+ - Full documentation with JSDoc examples for common use cases
19
+ - Added `getEventById(eventId)` to `FluentClient` and `FluentVersoriClient` for `GET /api/v4.1/event/{eventId}`
20
+ - Retrieve complete event details by unique identifier
21
+ - Returns event metadata, context, attributes, and source event IDs
22
+ - Useful for event tracing and debugging workflows
23
+ - Added typed Event Log models: `FluentEventQueryParams`, `FluentEventLogResponse`, `FluentEventLogItem`, `FluentEventLogContext`, and `FluentEventLogAttribute`
24
+ - Added typed Event constants for query safety: `FluentEventType`, `FluentEventStatus`, `FluentEventCategory`, `FluentEventRootEntityType`, and `FluentEventEntityType`
25
+ - Added unit test coverage for query-string building, empty-filter handling, success parsing, and error paths
26
+ - Updated API docs with `getEvents()` and `getEventById()` usage examples
27
+
10
28
  ### Added - Security & Compliance
11
29
  - **Security Audit Infrastructure** - Comprehensive security audit tools and scripts
12
30
  - `security-audit/security-audit.cjs` - Automated vulnerability scanning
package/README.md CHANGED
@@ -147,6 +147,7 @@ Choose the right Fluent Commerce API for your use case:
147
147
  | **Location setup** (stores, warehouses) | **Event API** | `sendEvent()` | Requires workflow orchestration |
148
148
  | **Order creation** (one-time orders) | **GraphQL** | `client.graphql()` with `createOrder` mutation | Triggers Order CREATED event, full control |
149
149
  | **Order updates/events** (status changes) | **Event API** | `sendEvent()` | Triggers workflows for order state changes |
150
+ | **Audit/search event history** (trace flows, investigate failures) | **Event API (GET)** | `getEvents()` + `getEventById()` | Read-only event/audit retrieval with filters and pagination |
150
151
  | **Customer data** (registration, profiles) | **GraphQL** | `client.graphql()` with mutations | No Rubix workflow support for customers |
151
152
  | **Data extraction** (export to S3/SFTP) | **GraphQL** | `client.graphql()` + `ExtractionOrchestrator` | Query with auto-pagination, extract thousands of records |
152
153
  | **Single operations** (create one product, update one location) | **GraphQL** | `client.graphql()` | Direct control, immediate feedback |
@@ -1217,6 +1218,222 @@ console.log('✅ Event sent successfully');
1217
1218
  - **Event API Templates:** `docs/01-TEMPLATES/versori/workflows/ingestion/event-api/` (8 templates for S3/SFTP with CSV, XML, JSON, Parquet)
1218
1219
  - **Batch API Guide:** `docs/02-CORE-GUIDES/ingestion/modules/02-CORE-GUIDES-ingestion-06-batch-api.md`
1219
1220
 
1221
+ ### Event Log API (Search + Audit)
1222
+
1223
+ Query and retrieve events/audit logs from the Fluent Commerce REST Event API. Use these **read-only** methods for troubleshooting workflows, tracing event chains, monitoring orchestration, and auditing batch processing.
1224
+
1225
+ **Methods:**
1226
+ - `getEvents(params)` → `GET /api/v4.1/event` - Search/filter events with flexible query parameters
1227
+ - `getEventById(eventId)` → `GET /api/v4.1/event/{eventId}` - Get a single event by ID
1228
+
1229
+ **Important:** These methods query the Event Log (audit trail). They do NOT trigger workflows — use `sendEvent()` for that.
1230
+
1231
+ #### Basic Usage
1232
+
1233
+ ```typescript
1234
+ import { createClient } from '@fluentcommerce/fc-connect-sdk';
1235
+
1236
+ const client = await createClient({
1237
+ config: { baseUrl, clientId, clientSecret, username, password },
1238
+ });
1239
+
1240
+ // 1) Search for failed orchestration audit events on orders
1241
+ const logs = await client.getEvents({
1242
+ 'context.rootEntityType': 'ORDER',
1243
+ eventType: 'ORCHESTRATION_AUDIT',
1244
+ eventStatus: 'FAILED',
1245
+ count: 100,
1246
+ });
1247
+
1248
+ console.log(`Found ${logs.results.length} events (hasMore=${logs.hasMore})`);
1249
+
1250
+ for (const event of logs.results) {
1251
+ console.log(` ${event.name} [${event.eventStatus}] on ${event.context?.entityType}:${event.context?.entityRef}`);
1252
+ }
1253
+
1254
+ // 2) Get full details for a specific event by ID
1255
+ if (logs.results[0]?.id) {
1256
+ const event = await client.getEventById(logs.results[0].id);
1257
+ console.log(`Event: ${event.name} - Status: ${event.eventStatus}`);
1258
+ console.log(` Entity: ${event.context?.entityType}:${event.context?.entityRef}`);
1259
+ console.log(` Root: ${event.context?.rootEntityType}:${event.context?.rootEntityRef}`);
1260
+ console.log(` Generated: ${event.generatedOn} by ${event.generatedBy}`);
1261
+
1262
+ // Trace parent events (for debugging event chains)
1263
+ if (event.context?.sourceEvents?.length) {
1264
+ for (const parentId of event.context.sourceEvents) {
1265
+ const parent = await client.getEventById(parentId);
1266
+ console.log(` Parent: ${parent.name} (${parent.eventStatus})`);
1267
+ }
1268
+ }
1269
+ }
1270
+ ```
1271
+
1272
+ #### Response Shape
1273
+
1274
+ `getEvents()` returns `FluentEventLogResponse`:
1275
+
1276
+ ```typescript
1277
+ {
1278
+ start: 1, // Pagination offset
1279
+ count: 1000, // Results in this page
1280
+ hasMore: false, // More pages available?
1281
+ results: [ // Array of FluentEventLogItem
1282
+ {
1283
+ id: "e2cc5040-...", // UUID
1284
+ name: "BATCH_COMPLETE", // Event/ruleset name (can be null)
1285
+ type: "ORCHESTRATION_AUDIT",
1286
+ accountId: "MYACCOUNT",
1287
+ retailerId: "5", // String in response (not number)
1288
+ category: "BATCH",
1289
+ context: {
1290
+ sourceEvents: ["babc5f37-..."], // Parent event IDs for tracing
1291
+ entityType: "BATCH",
1292
+ entityId: "12",
1293
+ entityRef: "12",
1294
+ rootEntityType: "JOB",
1295
+ rootEntityId: "13",
1296
+ rootEntityRef: "13"
1297
+ },
1298
+ eventStatus: "COMPLETE",
1299
+ attributes: null, // null OR array of { name, value, type }
1300
+ source: null,
1301
+ generatedBy: "Rubix User",
1302
+ generatedOn: "2026-02-05T06:31:56.895+00:00"
1303
+ }
1304
+ ]
1305
+ }
1306
+ ```
1307
+
1308
+ `getEventById(id)` returns a single `FluentEventLogItem` (same shape as each item in `results`).
1309
+
1310
+ #### Query Parameters
1311
+
1312
+ All parameters are optional. Context filters use flat dot-notation keys:
1313
+
1314
+ | Parameter | Type | Description | Example |
1315
+ |-----------|------|-------------|---------|
1316
+ | `context.rootEntityType` | string | Root entity type | `'ORDER'`, `'JOB'`, `'LOCATION'` |
1317
+ | `context.rootEntityId` | string | Root entity ID | `'12345'` |
1318
+ | `context.rootEntityRef` | string | Root entity reference | `'ORD-12345'` |
1319
+ | `context.entityType` | string | Sub-entity type | `'FULFILMENT'`, `'BATCH'`, `'ARTICLE'` |
1320
+ | `context.entityId` | string | Sub-entity ID | `'67890'` |
1321
+ | `context.entityRef` | string | Sub-entity reference | `'FUL-67890'` |
1322
+ | `eventType` | string | Event type filter | `'ORCHESTRATION_AUDIT'` |
1323
+ | `eventStatus` | string | Status filter | `'FAILED'`, `'COMPLETE'` |
1324
+ | `name` | string | Event/ruleset name | `'BATCH_COMPLETE'` |
1325
+ | `category` | string | Category filter | `'ruleSet'`, `'ACTION'` |
1326
+ | `from` | string | Start date (UTC ISO 8601) | `'2026-02-01T00:00:00.000Z'` |
1327
+ | `to` | string | End date (UTC ISO 8601) | `'2026-02-15T23:59:59.999Z'` |
1328
+ | `retailerId` | string/number | Retailer filter (falls back to client config) | `'5'` |
1329
+ | `start` | number | Pagination offset (default: `0`) | `0` |
1330
+ | `count` | number | Results per page (default: `100`, max: `5000`) | `1000` |
1331
+
1332
+ #### Valid Values Reference
1333
+
1334
+ | Parameter | Valid Values |
1335
+ |-----------|-------------|
1336
+ | **eventType** | `ORCHESTRATION`, `ORCHESTRATION_AUDIT`, `API`, `INTEGRATION`, `SECURITY`, `GENERAL` |
1337
+ | **eventStatus** | `PENDING`, `SCHEDULED`, `NO_MATCH`, `SUCCESS`, `FAILED`, `COMPLETE` |
1338
+ | **category** | `snapshot`, `ruleSet`, `rule`, `ACTION`, `CUSTOM`, `exception`, `ORDER_WORKFLOW`, `BATCH` |
1339
+ | **rootEntityType** | `ORDER`, `LOCATION`, `FULFILMENT_OPTIONS`, `PRODUCT_CATALOGUE`, `INVENTORY_CATALOGUE`, `VIRTUAL_CATALOGUE`, `CONTROL_GROUP`, `RETURN_ORDER`, `BILLING_ACCOUNT`, `JOB` |
1340
+ | **entityType** | `ORDER`, `FULFILMENT`, `ARTICLE`, `CONSIGNMENT`, `LOCATION`, `WAVE`, `FULFILMENT_OPTIONS`, `FULFILMENT_PLAN`, `PRODUCT_CATALOGUE`, `CATEGORY`, `PRODUCT`, `INVENTORY_CATALOGUE`, `INVENTORY_POSITION`, `INVENTORY_QUANTITY`, `VIRTUAL_CATALOGUE`, `VIRTUAL_POSITION`, `CONTROL_GROUP`, `CONTROL`, `RETURN_ORDER`, `RETURN_FULFILMENT`, `BILLING_ACCOUNT`, `CREDIT_MEMO`, `BATCH` |
1341
+
1342
+ #### Common Use Cases
1343
+
1344
+ ```typescript
1345
+ // 1. Find failed events in the last 24 hours
1346
+ const failedEvents = await client.getEvents({
1347
+ eventStatus: 'FAILED',
1348
+ from: new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString(),
1349
+ to: new Date().toISOString(),
1350
+ count: 1000,
1351
+ });
1352
+
1353
+ // 2. Track batch job processing (all events for a specific job)
1354
+ const batchEvents = await client.getEvents({
1355
+ 'context.rootEntityType': 'JOB',
1356
+ 'context.rootEntityId': '13',
1357
+ eventType: 'ORCHESTRATION_AUDIT',
1358
+ count: 500,
1359
+ });
1360
+
1361
+ // 3. Paginate through large result sets
1362
+ let start = 0;
1363
+ const allEvents: any[] = [];
1364
+ let hasMore = true;
1365
+
1366
+ while (hasMore) {
1367
+ const page = await client.getEvents({
1368
+ 'context.rootEntityType': 'ORDER',
1369
+ start,
1370
+ count: 1000,
1371
+ });
1372
+ allEvents.push(...page.results);
1373
+ hasMore = page.hasMore;
1374
+ start += page.count;
1375
+ }
1376
+ console.log(`Total events collected: ${allEvents.length}`);
1377
+
1378
+ // 4. Extract performance timing from event attributes
1379
+ const auditEvents = await client.getEvents({
1380
+ eventType: 'ORCHESTRATION_AUDIT',
1381
+ 'context.rootEntityType': 'JOB',
1382
+ count: 100,
1383
+ });
1384
+
1385
+ for (const event of auditEvents.results) {
1386
+ if (Array.isArray(event.attributes)) {
1387
+ const startTimer = event.attributes.find(a => a.name === 'startTimer');
1388
+ const stopTimer = event.attributes.find(a => a.name === 'stopTimer');
1389
+ if (startTimer && stopTimer) {
1390
+ const duration = Number(stopTimer.value) - Number(startTimer.value);
1391
+ console.log(`${event.name}: ${duration}ms`);
1392
+ }
1393
+ }
1394
+ }
1395
+
1396
+ // 5. Find events for a specific order by reference
1397
+ const orderEvents = await client.getEvents({
1398
+ 'context.rootEntityType': 'ORDER',
1399
+ 'context.rootEntityRef': 'HD_12345',
1400
+ eventType: 'ORCHESTRATION_AUDIT',
1401
+ });
1402
+ ```
1403
+
1404
+ #### When to Use Which Method
1405
+
1406
+ | Method | Use When | Returns | Example |
1407
+ |--------|----------|---------|---------|
1408
+ | `sendEvent()` | **Trigger** a workflow or business action | Event creation response | Product upsert, order cancel, location update |
1409
+ | `getEvents()` | **Search/filter** historical events | `{ results[], hasMore, start, count }` | Find failed events, audit batch processing |
1410
+ | `getEventById()` | **Get full details** for one event by ID | Single event object | Drill into context, attributes, trace sourceEvents |
1411
+
1412
+ #### Limitations & Operational Notes
1413
+
1414
+ | Constraint | Value | Notes |
1415
+ |------------|-------|-------|
1416
+ | **Max count per request** | `5000` | Use pagination (`start`/`count`/`hasMore`) for larger result sets |
1417
+ | **Time range (from)** | 4 months back max | API rejects queries older than ~4 months |
1418
+ | **Time range (to)** | 1 month forward max | API rejects future dates beyond ~1 month |
1419
+ | **Default time window** | Past 30 days | Applied when `from` is omitted |
1420
+ | **Date format** | UTC ISO 8601 | `YYYY-MM-DDTHH:mm:ss.SSSZ` |
1421
+ | **retailerId fallback** | Client config | If not in params, uses `client.config.retailerId` |
1422
+ | **attributes field** | Nullable | Can be `null` (not empty array) — always check before iterating |
1423
+ | **name field** | Nullable | Can be `null` for some system events |
1424
+ | **GraphQL** | Not available | Event queries use REST only — no GraphQL `events` root field exists |
1425
+
1426
+ **Key points:**
1427
+ - `getEvents()` and `getEventById()` are **read-only** — they do NOT trigger workflows
1428
+ - Empty `results: []` is valid (not an error) — your filter may simply match zero events
1429
+ - Always use bounded time ranges (`from`/`to`) for predictable performance
1430
+ - Use `eventType: 'ORCHESTRATION_AUDIT'` for workflow execution history
1431
+ - Use `context.sourceEvents` array on each event to trace parent event chains
1432
+ - The `attributes` field is `null` for most events; only rule/ruleset events populate it
1433
+ - `retailerId` in responses is a **string** (e.g., `"5"`) even though you can pass a number in params
1434
+
1435
+ **📚 Detailed Guide:** `docs/02-CORE-GUIDES/api-reference/event-api-input-output-reference.md`
1436
+
1220
1437
  ---
1221
1438
 
1222
1439
  ### Error Classification
@@ -513,6 +513,56 @@ class FluentClient {
513
513
  throw error;
514
514
  }
515
515
  }
516
+ async getEvents(params = {}) {
517
+ const queryString = this.buildEventQueryString(params);
518
+ const endpoint = queryString ? `/api/v4.1/event?${queryString}` : '/api/v4.1/event';
519
+ this.log('info', '[FluentClient:event] Searching event logs', {
520
+ endpoint,
521
+ filterCount: Object.keys(params).length,
522
+ hasDateRange: !!(params.from || params.to),
523
+ entityType: params['context.entityType'],
524
+ rootEntityType: params['context.rootEntityType'],
525
+ eventStatus: params.eventStatus,
526
+ });
527
+ try {
528
+ const response = await this.request(endpoint, {
529
+ method: 'GET',
530
+ });
531
+ this.log('info', '[FluentClient:event] Event search completed', {
532
+ resultCount: response.data.count,
533
+ hasMore: response.data.hasMore,
534
+ start: response.data.start,
535
+ });
536
+ return response.data;
537
+ }
538
+ catch (error) {
539
+ this.log('error', '[FluentClient:event] Failed to search events', error);
540
+ throw error;
541
+ }
542
+ }
543
+ async getEventById(eventId) {
544
+ if (!eventId) {
545
+ throw new types_1.FluentValidationError('eventId is required');
546
+ }
547
+ this.log('info', `[FluentClient:event] Getting event by ID: ${eventId}`, { eventId });
548
+ try {
549
+ const response = await this.request(`/api/v4.1/event/${eventId}`, {
550
+ method: 'GET',
551
+ });
552
+ this.log('info', `[FluentClient:event] Event retrieved: ${response.data.name}`, {
553
+ eventId: response.data.id,
554
+ name: response.data.name,
555
+ type: response.data.type,
556
+ eventStatus: response.data.eventStatus,
557
+ entityType: response.data.context?.entityType,
558
+ });
559
+ return response.data;
560
+ }
561
+ catch (error) {
562
+ this.log('error', `[FluentClient:event] Failed to get event ${eventId}`, error);
563
+ throw error;
564
+ }
565
+ }
516
566
  async validateWebhook(payload, signature, rawPayload) {
517
567
  this.log('info', `[FluentClient:webhook] Validating webhook for event "${payload.name || 'unknown'}"`, {
518
568
  hasSignature: !!signature,
@@ -592,6 +642,16 @@ class FluentClient {
592
642
  const result = await this.graphql(payload);
593
643
  return result.data;
594
644
  }
645
+ buildEventQueryString(params) {
646
+ const searchParams = new URLSearchParams();
647
+ for (const [key, value] of Object.entries(params)) {
648
+ if (value === undefined || value === null || value === '') {
649
+ continue;
650
+ }
651
+ searchParams.append(key, String(value));
652
+ }
653
+ return searchParams.toString();
654
+ }
595
655
  async request(endpoint, config = {}) {
596
656
  const isVersoriContext = this.context?.fetch && this.context?.fetch !== globalThis.fetch;
597
657
  this.log('debug', 'Request mode detection', {
@@ -34,7 +34,7 @@ export { GraphQLTemplateGenerator } from './services/mapping/query/graphql-templ
34
34
  export { MappingError, ResolverError } from './errors';
35
35
  export type { NodeConfig, NodesConfig, FieldConfig, FieldsConfig, NodesContext, MappingContext, ResolverFunction, ResolverHelpers, ResolverContext, ResolversMap, MapResult, MapWithNodesResult, NodeValidationResult, MappingOptions, } from './services/mapping/types';
36
36
  export { CsvDelimiter, FileEncoding, JobStrategy, FileType, BatchAction, EntityType, AwsRegion, ValidationMode, ProcessingStatus, } from './types/enums';
37
- export type { FluentClientConfig, FluentBatchPayload, FluentInventoryBatchEntity, FluentInventoryBatchRequest, FluentBatchResponse, FluentBatchStatus, FluentJobPayload, FluentJobResponse, FluentJobMetadata, FluentJobStatus, FluentJobResults, FluentEvent, FluentEventMode, CreateEventOptions, GraphQLPayload, GraphQLResponse, GraphQLErrorMode, GraphQLError, PaginationConfig, PageInfo, PaginatedResponse, FluentWebhookPayload, WebhookRequestContext, GraphQLValidationResult, BatchConfiguration, DataSourceConfig, FileMetadata, Logger, StructuredLogger, LogContext, LoggerConfig, PerformanceMetrics, InventoryDataRecord, ParquetDataRecord, FieldMappingConfig, FieldMappingRule, ProcessingMetadata, AttributeValue, } from './types';
37
+ export type { FluentClientConfig, FluentBatchPayload, FluentInventoryBatchEntity, FluentInventoryBatchRequest, FluentBatchResponse, FluentBatchStatus, FluentJobPayload, FluentJobResponse, FluentJobMetadata, FluentJobStatus, FluentJobResults, FluentEvent, FluentEventMode, CreateEventOptions, FluentEventRootEntityType, FluentEventEntityType, FluentEventCategory, FluentEventType, FluentEventStatus, FluentEventQueryParamValue, FluentEventQueryParams, FluentEventLogAttribute, FluentEventLogContext, FluentEventLogItem, FluentEventLogResponse, GraphQLPayload, GraphQLResponse, GraphQLErrorMode, GraphQLError, PaginationConfig, PageInfo, PaginatedResponse, FluentWebhookPayload, WebhookRequestContext, GraphQLValidationResult, BatchConfiguration, DataSourceConfig, FileMetadata, Logger, StructuredLogger, LogContext, LoggerConfig, PerformanceMetrics, InventoryDataRecord, ParquetDataRecord, FieldMappingConfig, FieldMappingRule, ProcessingMetadata, AttributeValue, } from './types';
38
38
  export type { VersoriContext, WebhookContext, DirectContext } from './types';
39
39
  export { S3SDKTester, S3PresignedTester, S3ComparisonTester, FluentConnectionTester, type TestResult, type FluentTestConfig, type S3TestConfig, } from './testing/index';
40
40
  export { S3Service, S3ServiceError, type S3ServiceConfig, type S3Config, type S3Object, type ListOptions, type PutOptions, type CopyOptions, type S3Location, } from './services/s3/index';
@@ -95,6 +95,70 @@ export interface FluentEvent {
95
95
  export interface CreateEventOptions {
96
96
  mode?: FluentEventMode;
97
97
  }
98
+ export type FluentEventRootEntityType = 'ORDER' | 'LOCATION' | 'FULFILMENT_OPTIONS' | 'PRODUCT_CATALOGUE' | 'INVENTORY_CATALOGUE' | 'VIRTUAL_CATALOGUE' | 'CONTROL_GROUP' | 'RETURN_ORDER' | 'BILLING_ACCOUNT' | 'JOB';
99
+ export type FluentEventEntityType = 'ORDER' | 'FULFILMENT' | 'ARTICLE' | 'CONSIGNMENT' | 'LOCATION' | 'WAVE' | 'FULFILMENT_OPTIONS' | 'FULFILMENT_PLAN' | 'PRODUCT_CATALOGUE' | 'CATEGORY' | 'PRODUCT' | 'INVENTORY_CATALOGUE' | 'INVENTORY_POSITION' | 'INVENTORY_QUANTITY' | 'VIRTUAL_CATALOGUE' | 'VIRTUAL_POSITION' | 'CONTROL_GROUP' | 'CONTROL' | 'RETURN_ORDER' | 'RETURN_FULFILMENT' | 'BILLING_ACCOUNT' | 'CREDIT_MEMO' | 'BATCH';
100
+ export type FluentEventCategory = 'snapshot' | 'ruleSet' | 'rule' | 'ACTION' | 'CUSTOM' | 'exception' | 'ORDER_WORKFLOW';
101
+ export type FluentEventType = 'ORCHESTRATION' | 'ORCHESTRATION_AUDIT' | 'API' | 'INTEGRATION' | 'SECURITY' | 'GENERAL';
102
+ export type FluentEventStatus = 'PENDING' | 'SCHEDULED' | 'NO_MATCH' | 'SUCCESS' | 'FAILED' | 'COMPLETE';
103
+ export type FluentEventQueryParamValue = string | number | boolean;
104
+ export interface FluentEventQueryParams {
105
+ start?: number;
106
+ count?: number;
107
+ from?: string;
108
+ to?: string;
109
+ name?: string;
110
+ category?: FluentEventCategory | string;
111
+ retailerId?: string | number;
112
+ eventType?: FluentEventType | string;
113
+ eventStatus?: FluentEventStatus | string;
114
+ eventId?: string;
115
+ 'context.rootEntityType'?: FluentEventRootEntityType | string;
116
+ 'context.rootEntityId'?: string | number;
117
+ 'context.rootEntityRef'?: string;
118
+ 'context.entityType'?: FluentEventEntityType | string;
119
+ 'context.entityId'?: string | number;
120
+ 'context.entityRef'?: string;
121
+ 'context.sourceEvents'?: string;
122
+ [key: string]: FluentEventQueryParamValue | undefined;
123
+ }
124
+ export interface FluentEventLogContext {
125
+ sourceEvents?: string[];
126
+ entityType?: FluentEventEntityType | string;
127
+ entityId?: string;
128
+ entityRef?: string;
129
+ rootEntityType?: FluentEventRootEntityType | string;
130
+ rootEntityId?: string;
131
+ rootEntityRef?: string;
132
+ [key: string]: JsonValue | undefined;
133
+ }
134
+ export interface FluentEventLogAttribute {
135
+ name: string;
136
+ value: string;
137
+ type?: string;
138
+ [key: string]: unknown;
139
+ }
140
+ export interface FluentEventLogItem {
141
+ id: string;
142
+ name: string;
143
+ type?: FluentEventType | string;
144
+ accountId?: string;
145
+ retailerId?: string;
146
+ category?: FluentEventCategory | string;
147
+ context?: FluentEventLogContext;
148
+ eventStatus?: FluentEventStatus | string;
149
+ attributes?: FluentEventLogAttribute[] | Record<string, JsonValue> | null;
150
+ source?: string | null;
151
+ generatedBy?: string;
152
+ generatedOn?: string;
153
+ [key: string]: unknown;
154
+ }
155
+ export interface FluentEventLogResponse {
156
+ start: number;
157
+ count: number;
158
+ hasMore: boolean;
159
+ results: FluentEventLogItem[];
160
+ [key: string]: unknown;
161
+ }
98
162
  export type GraphQLErrorMode = 'throw' | 'partial';
99
163
  export interface GraphQLPayload<T = Record<string, JsonValue>> {
100
164
  query: string;
@@ -1,4 +1,4 @@
1
- import type { FluentEvent, GraphQLPayload, GraphQLResponse, FluentEventMode, JsonValue, FluentBatchPayload, FluentBatchResponse, FluentBatchStatus, FluentJobPayload, FluentJobResponse, FluentJobStatus, FluentJobResults } from '../types';
1
+ import type { FluentEvent, GraphQLPayload, GraphQLResponse, FluentEventMode, FluentEventLogResponse, FluentEventLogItem, FluentEventQueryParams, JsonValue, FluentBatchPayload, FluentBatchResponse, FluentBatchStatus, FluentJobPayload, FluentJobResponse, FluentJobStatus, FluentJobResults } from '../types';
2
2
  import type { FluentWebhookPayload, EventResponse } from '../types';
3
3
  export declare class FluentVersoriClient {
4
4
  private ctx;
@@ -13,6 +13,8 @@ export declare class FluentVersoriClient {
13
13
  private executeSinglePage;
14
14
  private executeWithAutoPagination;
15
15
  sendEvent(event: FluentEvent, mode?: FluentEventMode): Promise<EventResponse>;
16
+ getEvents(params?: FluentEventQueryParams): Promise<FluentEventLogResponse>;
17
+ getEventById(eventId: string): Promise<FluentEventLogItem>;
16
18
  createJob(payload: FluentJobPayload): Promise<FluentJobResponse>;
17
19
  sendBatch(jobId: string, payload: FluentBatchPayload): Promise<FluentBatchResponse>;
18
20
  getJobStatus(jobId: string): Promise<FluentJobStatus>;
@@ -20,6 +22,7 @@ export declare class FluentVersoriClient {
20
22
  getJobResults(jobId: string): Promise<FluentJobResults>;
21
23
  query<T = JsonValue>(queryOrPayload: string | GraphQLPayload, variables?: Record<string, any>): Promise<T | undefined>;
22
24
  mutate<T = JsonValue>(mutationOrPayload: string | GraphQLPayload, variables?: Record<string, any>): Promise<T | undefined>;
25
+ private buildEventQueryString;
23
26
  private throwApiError;
24
27
  private fetchWithRetry;
25
28
  validateWebhook(payload: FluentWebhookPayload, signature?: string, rawPayload?: string, publicKey?: string): Promise<boolean>;
@@ -427,6 +427,79 @@ class FluentVersoriClient {
427
427
  return { success: true, statusCode: response.status, message: text };
428
428
  }
429
429
  }
430
+ async getEvents(params = {}) {
431
+ const query = this.buildEventQueryString(params);
432
+ const endpoint = query ? `/api/v4.1/event?${query}` : '/api/v4.1/event';
433
+ this.ctx.log.info('[fc-connect-sdk:event] Searching event logs', {
434
+ endpoint,
435
+ filterCount: Object.keys(params).length,
436
+ hasDateRange: !!(params.from || params.to),
437
+ entityType: params['context.entityType'],
438
+ rootEntityType: params['context.rootEntityType'],
439
+ eventStatus: params.eventStatus,
440
+ });
441
+ const response = await this.fetchWithRetry(endpoint, {
442
+ method: 'GET',
443
+ headers: {
444
+ 'Content-Type': 'application/json',
445
+ },
446
+ });
447
+ const text = response.text;
448
+ if (!response.ok) {
449
+ this.ctx.log.error('[fc-connect-sdk:event] Failed to search events', {
450
+ status: response.status,
451
+ body: text,
452
+ });
453
+ this.throwApiError('Get events failed', response.status, text);
454
+ }
455
+ try {
456
+ const result = JSON.parse(text);
457
+ this.ctx.log.info('[fc-connect-sdk:event] Event search completed', {
458
+ resultCount: result.count,
459
+ hasMore: result.hasMore,
460
+ start: result.start,
461
+ });
462
+ return result;
463
+ }
464
+ catch {
465
+ throw new types_1.FluentAPIError('Failed to parse event log response', response.status, text);
466
+ }
467
+ }
468
+ async getEventById(eventId) {
469
+ if (!eventId) {
470
+ throw new types_1.FluentValidationError('eventId is required');
471
+ }
472
+ this.ctx.log.info(`[fc-connect-sdk:event] Getting event by ID: ${eventId}`, { eventId });
473
+ const response = await this.fetchWithRetry(`/api/v4.1/event/${eventId}`, {
474
+ method: 'GET',
475
+ headers: {
476
+ 'Content-Type': 'application/json',
477
+ },
478
+ });
479
+ const text = response.text;
480
+ if (!response.ok) {
481
+ this.ctx.log.error(`[fc-connect-sdk:event] Failed to get event ${eventId}`, {
482
+ eventId,
483
+ status: response.status,
484
+ body: text,
485
+ });
486
+ this.throwApiError(`Get event ${eventId} failed`, response.status, text);
487
+ }
488
+ try {
489
+ const event = JSON.parse(text);
490
+ this.ctx.log.info(`[fc-connect-sdk:event] Event retrieved: ${event.name}`, {
491
+ eventId: event.id,
492
+ name: event.name,
493
+ type: event.type,
494
+ eventStatus: event.eventStatus,
495
+ entityType: event.context?.entityType,
496
+ });
497
+ return event;
498
+ }
499
+ catch {
500
+ throw new types_1.FluentAPIError('Failed to parse event response', response.status, text);
501
+ }
502
+ }
430
503
  async createJob(payload) {
431
504
  this.ctx.log.info(`[fc-connect-sdk:job] Creating job "${payload.name}"`, {
432
505
  name: payload.name,
@@ -574,6 +647,16 @@ class FluentVersoriClient {
574
647
  const result = await this.graphql(payload);
575
648
  return result.data;
576
649
  }
650
+ buildEventQueryString(params) {
651
+ const searchParams = new URLSearchParams();
652
+ for (const [key, value] of Object.entries(params)) {
653
+ if (value === undefined || value === null || value === '') {
654
+ continue;
655
+ }
656
+ searchParams.append(key, String(value));
657
+ }
658
+ return searchParams.toString();
659
+ }
577
660
  throwApiError(message, status, responseText) {
578
661
  if (status === 401) {
579
662
  throw new types_1.AuthenticationError(`${message}: ${responseText}`, { response: responseText });
@@ -510,6 +510,56 @@ export class FluentClient {
510
510
  throw error;
511
511
  }
512
512
  }
513
+ async getEvents(params = {}) {
514
+ const queryString = this.buildEventQueryString(params);
515
+ const endpoint = queryString ? `/api/v4.1/event?${queryString}` : '/api/v4.1/event';
516
+ this.log('info', '[FluentClient:event] Searching event logs', {
517
+ endpoint,
518
+ filterCount: Object.keys(params).length,
519
+ hasDateRange: !!(params.from || params.to),
520
+ entityType: params['context.entityType'],
521
+ rootEntityType: params['context.rootEntityType'],
522
+ eventStatus: params.eventStatus,
523
+ });
524
+ try {
525
+ const response = await this.request(endpoint, {
526
+ method: 'GET',
527
+ });
528
+ this.log('info', '[FluentClient:event] Event search completed', {
529
+ resultCount: response.data.count,
530
+ hasMore: response.data.hasMore,
531
+ start: response.data.start,
532
+ });
533
+ return response.data;
534
+ }
535
+ catch (error) {
536
+ this.log('error', '[FluentClient:event] Failed to search events', error);
537
+ throw error;
538
+ }
539
+ }
540
+ async getEventById(eventId) {
541
+ if (!eventId) {
542
+ throw new FluentValidationError('eventId is required');
543
+ }
544
+ this.log('info', `[FluentClient:event] Getting event by ID: ${eventId}`, { eventId });
545
+ try {
546
+ const response = await this.request(`/api/v4.1/event/${eventId}`, {
547
+ method: 'GET',
548
+ });
549
+ this.log('info', `[FluentClient:event] Event retrieved: ${response.data.name}`, {
550
+ eventId: response.data.id,
551
+ name: response.data.name,
552
+ type: response.data.type,
553
+ eventStatus: response.data.eventStatus,
554
+ entityType: response.data.context?.entityType,
555
+ });
556
+ return response.data;
557
+ }
558
+ catch (error) {
559
+ this.log('error', `[FluentClient:event] Failed to get event ${eventId}`, error);
560
+ throw error;
561
+ }
562
+ }
513
563
  async validateWebhook(payload, signature, rawPayload) {
514
564
  this.log('info', `[FluentClient:webhook] Validating webhook for event "${payload.name || 'unknown'}"`, {
515
565
  hasSignature: !!signature,
@@ -589,6 +639,16 @@ export class FluentClient {
589
639
  const result = await this.graphql(payload);
590
640
  return result.data;
591
641
  }
642
+ buildEventQueryString(params) {
643
+ const searchParams = new URLSearchParams();
644
+ for (const [key, value] of Object.entries(params)) {
645
+ if (value === undefined || value === null || value === '') {
646
+ continue;
647
+ }
648
+ searchParams.append(key, String(value));
649
+ }
650
+ return searchParams.toString();
651
+ }
592
652
  async request(endpoint, config = {}) {
593
653
  const isVersoriContext = this.context?.fetch && this.context?.fetch !== globalThis.fetch;
594
654
  this.log('debug', 'Request mode detection', {
@@ -34,7 +34,7 @@ export { GraphQLTemplateGenerator } from './services/mapping/query/graphql-templ
34
34
  export { MappingError, ResolverError } from './errors/index.js';
35
35
  export type { NodeConfig, NodesConfig, FieldConfig, FieldsConfig, NodesContext, MappingContext, ResolverFunction, ResolverHelpers, ResolverContext, ResolversMap, MapResult, MapWithNodesResult, NodeValidationResult, MappingOptions, } from './services/mapping/types/index.js';
36
36
  export { CsvDelimiter, FileEncoding, JobStrategy, FileType, BatchAction, EntityType, AwsRegion, ValidationMode, ProcessingStatus, } from './types/enums.js';
37
- export type { FluentClientConfig, FluentBatchPayload, FluentInventoryBatchEntity, FluentInventoryBatchRequest, FluentBatchResponse, FluentBatchStatus, FluentJobPayload, FluentJobResponse, FluentJobMetadata, FluentJobStatus, FluentJobResults, FluentEvent, FluentEventMode, CreateEventOptions, GraphQLPayload, GraphQLResponse, GraphQLErrorMode, GraphQLError, PaginationConfig, PageInfo, PaginatedResponse, FluentWebhookPayload, WebhookRequestContext, GraphQLValidationResult, BatchConfiguration, DataSourceConfig, FileMetadata, Logger, StructuredLogger, LogContext, LoggerConfig, PerformanceMetrics, InventoryDataRecord, ParquetDataRecord, FieldMappingConfig, FieldMappingRule, ProcessingMetadata, AttributeValue, } from './types/index.js';
37
+ export type { FluentClientConfig, FluentBatchPayload, FluentInventoryBatchEntity, FluentInventoryBatchRequest, FluentBatchResponse, FluentBatchStatus, FluentJobPayload, FluentJobResponse, FluentJobMetadata, FluentJobStatus, FluentJobResults, FluentEvent, FluentEventMode, CreateEventOptions, FluentEventRootEntityType, FluentEventEntityType, FluentEventCategory, FluentEventType, FluentEventStatus, FluentEventQueryParamValue, FluentEventQueryParams, FluentEventLogAttribute, FluentEventLogContext, FluentEventLogItem, FluentEventLogResponse, GraphQLPayload, GraphQLResponse, GraphQLErrorMode, GraphQLError, PaginationConfig, PageInfo, PaginatedResponse, FluentWebhookPayload, WebhookRequestContext, GraphQLValidationResult, BatchConfiguration, DataSourceConfig, FileMetadata, Logger, StructuredLogger, LogContext, LoggerConfig, PerformanceMetrics, InventoryDataRecord, ParquetDataRecord, FieldMappingConfig, FieldMappingRule, ProcessingMetadata, AttributeValue, } from './types/index.js';
38
38
  export type { VersoriContext, WebhookContext, DirectContext } from './types/index.js';
39
39
  export { S3SDKTester, S3PresignedTester, S3ComparisonTester, FluentConnectionTester, type TestResult, type FluentTestConfig, type S3TestConfig, } from './testing/index.js';
40
40
  export { S3Service, S3ServiceError, type S3ServiceConfig, type S3Config, type S3Object, type ListOptions, type PutOptions, type CopyOptions, type S3Location, } from './services/s3/index.js';