@friggframework/core 2.0.0-next.85 → 2.0.0-next.87
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.
|
@@ -25,7 +25,8 @@ const ERROR_CODE_MAP = {
|
|
|
25
25
|
ENTITY_NOT_FOUND: 401,
|
|
26
26
|
ENTITY_USER_NOT_FOUND: 401,
|
|
27
27
|
INTEGRATION_NOT_FOUND: 404,
|
|
28
|
-
|
|
28
|
+
EXTERNAL_ID_REQUIRED: 400,
|
|
29
|
+
TYPE_REQUIRED: 400,
|
|
29
30
|
INTEGRATION_RECORD_NOT_FOUND: 404,
|
|
30
31
|
};
|
|
31
32
|
|
|
@@ -83,14 +84,20 @@ function createIntegrationCommands({ integrationClass }) {
|
|
|
83
84
|
});
|
|
84
85
|
|
|
85
86
|
return {
|
|
86
|
-
|
|
87
|
+
/**
|
|
88
|
+
* Find integration context by external entity ID and type
|
|
89
|
+
* @param {Object} params
|
|
90
|
+
* @param {string} params.externalId - External ID of the entity
|
|
91
|
+
* @param {string} params.type - Integration type (config.type)
|
|
92
|
+
* @returns {Promise<Object>} Integration context, entity, and record
|
|
93
|
+
*/
|
|
94
|
+
async findIntegrationContextByExternalEntityId({ externalId, type }) {
|
|
87
95
|
try {
|
|
88
|
-
const
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
return { context };
|
|
96
|
+
const result = await findByExternalEntityIdUseCase.execute({
|
|
97
|
+
externalId,
|
|
98
|
+
type,
|
|
99
|
+
});
|
|
100
|
+
return result;
|
|
94
101
|
} catch (error) {
|
|
95
102
|
return mapErrorToResponse(error);
|
|
96
103
|
}
|
|
@@ -197,11 +204,12 @@ function createIntegrationCommands({ integrationClass }) {
|
|
|
197
204
|
|
|
198
205
|
async function findIntegrationContextByExternalEntityId({
|
|
199
206
|
integrationClass,
|
|
200
|
-
|
|
207
|
+
externalId,
|
|
208
|
+
type,
|
|
201
209
|
} = {}) {
|
|
202
210
|
const commands = createIntegrationCommands({ integrationClass });
|
|
203
211
|
|
|
204
|
-
return commands.findIntegrationContextByExternalEntityId(
|
|
212
|
+
return commands.findIntegrationContextByExternalEntityId({ externalId, type });
|
|
205
213
|
}
|
|
206
214
|
|
|
207
215
|
module.exports = {
|
|
@@ -19,39 +19,43 @@ class FindIntegrationContextByExternalEntityIdUseCase {
|
|
|
19
19
|
this.loadIntegrationContextUseCase = loadIntegrationContextUseCase;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
async execute({
|
|
23
|
-
if (!
|
|
24
|
-
const error = new Error('
|
|
25
|
-
error.code = '
|
|
22
|
+
async execute({ externalId, type }) {
|
|
23
|
+
if (!externalId) {
|
|
24
|
+
const error = new Error('externalId is required');
|
|
25
|
+
error.code = 'EXTERNAL_ID_REQUIRED';
|
|
26
|
+
throw error;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (!type) {
|
|
30
|
+
const error = new Error('type is required');
|
|
31
|
+
error.code = 'TYPE_REQUIRED';
|
|
26
32
|
throw error;
|
|
27
33
|
}
|
|
28
34
|
|
|
29
35
|
const entity = await this.moduleRepository.findEntity({
|
|
30
|
-
externalId
|
|
36
|
+
externalId,
|
|
31
37
|
});
|
|
32
38
|
|
|
33
39
|
if (!entity) {
|
|
34
40
|
const error = new Error(
|
|
35
|
-
`Entity not found for externalId: ${
|
|
41
|
+
`Entity not found for externalId: ${externalId}`
|
|
36
42
|
);
|
|
37
43
|
error.code = 'ENTITY_NOT_FOUND';
|
|
38
44
|
throw error;
|
|
39
45
|
}
|
|
40
46
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
throw error;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const integrationRecord =
|
|
48
|
-
await this.integrationRepository.findIntegrationByUserId(
|
|
49
|
-
entity.userId
|
|
47
|
+
const integrations =
|
|
48
|
+
await this.integrationRepository.findIntegrationsByEntityId(
|
|
49
|
+
entity.id
|
|
50
50
|
);
|
|
51
51
|
|
|
52
|
+
const integrationRecord = integrations?.find(
|
|
53
|
+
(i) => i.config?.type === type
|
|
54
|
+
);
|
|
55
|
+
|
|
52
56
|
if (!integrationRecord) {
|
|
53
57
|
const error = new Error(
|
|
54
|
-
`Integration not found for
|
|
58
|
+
`Integration of type '${type}' not found for entity: ${entity.id}`
|
|
55
59
|
);
|
|
56
60
|
error.code = 'INTEGRATION_NOT_FOUND';
|
|
57
61
|
throw error;
|
|
@@ -28,8 +28,9 @@
|
|
|
28
28
|
* const updateMetrics = new UpdateProcessMetrics({ processRepository, websocketService });
|
|
29
29
|
* await updateMetrics.execute(processId, {
|
|
30
30
|
* processed: 100,
|
|
31
|
-
* success:
|
|
31
|
+
* success: 92,
|
|
32
32
|
* errors: 5,
|
|
33
|
+
* skipped: 3,
|
|
33
34
|
* errorDetails: [{ contactId: 'abc', error: 'Missing email', timestamp: '...' }]
|
|
34
35
|
* });
|
|
35
36
|
*/
|
|
@@ -54,6 +55,11 @@ class UpdateProcessMetrics {
|
|
|
54
55
|
* @param {number} [metricsUpdate.processed=0] - Records processed in this batch
|
|
55
56
|
* @param {number} [metricsUpdate.success=0] - Successful records
|
|
56
57
|
* @param {number} [metricsUpdate.errors=0] - Failed records
|
|
58
|
+
* @param {number} [metricsUpdate.skipped=0] - Intentionally-skipped
|
|
59
|
+
* records (hash-match, dedupe, loop protection, etc.). Increments
|
|
60
|
+
* `results.aggregateData.totalSkipped`. Distinct from errors so the
|
|
61
|
+
* UI can show `processed = synced + failed + skipped` without
|
|
62
|
+
* conflating intentional skips with failures.
|
|
57
63
|
* @param {Array} [metricsUpdate.errorDetails=[]] - Error details array
|
|
58
64
|
* @returns {Promise<Object>} Updated process record
|
|
59
65
|
* @throws {Error} If process not found or update fails
|
|
@@ -71,9 +77,11 @@ class UpdateProcessMetrics {
|
|
|
71
77
|
const processed = metricsUpdate.processed || 0;
|
|
72
78
|
const success = metricsUpdate.success || 0;
|
|
73
79
|
const errors = metricsUpdate.errors || 0;
|
|
80
|
+
const skipped = metricsUpdate.skipped || 0;
|
|
74
81
|
if (processed) increment['context.processedRecords'] = processed;
|
|
75
82
|
if (success) increment['results.aggregateData.totalSynced'] = success;
|
|
76
83
|
if (errors) increment['results.aggregateData.totalFailed'] = errors;
|
|
84
|
+
if (skipped) increment['results.aggregateData.totalSkipped'] = skipped;
|
|
77
85
|
|
|
78
86
|
const pushSlice = {};
|
|
79
87
|
if (
|
|
@@ -191,6 +199,7 @@ class UpdateProcessMetrics {
|
|
|
191
199
|
total: context.totalRecords || 0,
|
|
192
200
|
successCount: aggregateData.totalSynced || 0,
|
|
193
201
|
errorCount: aggregateData.totalFailed || 0,
|
|
202
|
+
skippedCount: aggregateData.totalSkipped || 0,
|
|
194
203
|
recordsPerSecond: aggregateData.recordsPerSecond || 0,
|
|
195
204
|
estimatedCompletion: context.estimatedCompletion || null,
|
|
196
205
|
timestamp: new Date().toISOString(),
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@friggframework/core",
|
|
3
3
|
"prettier": "@friggframework/prettier-config",
|
|
4
|
-
"version": "2.0.0-next.
|
|
4
|
+
"version": "2.0.0-next.87",
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"@aws-sdk/client-apigatewaymanagementapi": "^3.588.0",
|
|
7
7
|
"@aws-sdk/client-kms": "^3.588.0",
|
|
@@ -38,9 +38,9 @@
|
|
|
38
38
|
}
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
|
-
"@friggframework/eslint-config": "2.0.0-next.
|
|
42
|
-
"@friggframework/prettier-config": "2.0.0-next.
|
|
43
|
-
"@friggframework/test": "2.0.0-next.
|
|
41
|
+
"@friggframework/eslint-config": "2.0.0-next.87",
|
|
42
|
+
"@friggframework/prettier-config": "2.0.0-next.87",
|
|
43
|
+
"@friggframework/test": "2.0.0-next.87",
|
|
44
44
|
"@prisma/client": "^6.17.0",
|
|
45
45
|
"@types/lodash": "4.17.15",
|
|
46
46
|
"@typescript-eslint/eslint-plugin": "^8.0.0",
|
|
@@ -80,5 +80,5 @@
|
|
|
80
80
|
"publishConfig": {
|
|
81
81
|
"access": "public"
|
|
82
82
|
},
|
|
83
|
-
"gitHead": "
|
|
83
|
+
"gitHead": "ee72b6f5349ee764da595f67cc6f184a42f763f1"
|
|
84
84
|
}
|