@friggframework/core 2.0.0-next.105 → 2.0.0-next.107
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.
|
@@ -383,10 +383,7 @@ class IntegrationBase {
|
|
|
383
383
|
this.id,
|
|
384
384
|
'errors',
|
|
385
385
|
'Authentication Error',
|
|
386
|
-
|
|
387
|
-
module
|
|
388
|
-
].getName()} Entity.
|
|
389
|
-
Please reconnect/re-authenticate, or reach out to Support for assistance.`,
|
|
386
|
+
this._authErrorMessage(this[module].getName()),
|
|
390
387
|
Date.now()
|
|
391
388
|
);
|
|
392
389
|
}
|
|
@@ -395,6 +392,16 @@ class IntegrationBase {
|
|
|
395
392
|
return didAuthPass;
|
|
396
393
|
}
|
|
397
394
|
|
|
395
|
+
/**
|
|
396
|
+
* @param {string} [moduleName] - The module whose credentials failed.
|
|
397
|
+
* @param {number} [statusCode] - HTTP status the module rejected us with.
|
|
398
|
+
* @returns {string} A user-facing message.
|
|
399
|
+
*/
|
|
400
|
+
_authErrorMessage(moduleName, statusCode) {
|
|
401
|
+
const status = statusCode ? ` (HTTP ${statusCode})` : '';
|
|
402
|
+
return `There was an error with your ${moduleName} Entity${status}. Please reconnect/re-authenticate, or reach out to Support for assistance.`;
|
|
403
|
+
}
|
|
404
|
+
|
|
398
405
|
/**
|
|
399
406
|
* Reconcile the auth-health axis (ERROR ↔ ENABLED) from a testAuth result.
|
|
400
407
|
* On success it never clears DISABLED — a user pause is not an auth-health
|
|
@@ -848,6 +855,9 @@ class IntegrationBase {
|
|
|
848
855
|
if (!this.id) return;
|
|
849
856
|
|
|
850
857
|
if (delegateString === 'CREDENTIAL_INVALIDATED') {
|
|
858
|
+
if (this.status === 'ERROR') return;
|
|
859
|
+
|
|
860
|
+
const moduleName = notifier?.name;
|
|
851
861
|
const detail =
|
|
852
862
|
object?.reason || object?.statusCode
|
|
853
863
|
? ` (status ${object?.statusCode ?? '?'}: ${
|
|
@@ -856,11 +866,15 @@ class IntegrationBase {
|
|
|
856
866
|
: '';
|
|
857
867
|
console.log(
|
|
858
868
|
`[Frigg] Module ${
|
|
859
|
-
|
|
869
|
+
moduleName || '?'
|
|
860
870
|
} reported invalid credentials for integration ${
|
|
861
871
|
this.id
|
|
862
872
|
} — marking ERROR${detail}`
|
|
863
873
|
);
|
|
874
|
+
await this._recordCredentialRejection(
|
|
875
|
+
moduleName,
|
|
876
|
+
object?.statusCode
|
|
877
|
+
);
|
|
864
878
|
await this.persistStatus('ERROR');
|
|
865
879
|
return;
|
|
866
880
|
}
|
|
@@ -877,6 +891,30 @@ class IntegrationBase {
|
|
|
877
891
|
await this.persistStatus('ENABLED');
|
|
878
892
|
}
|
|
879
893
|
}
|
|
894
|
+
|
|
895
|
+
/**
|
|
896
|
+
* Takes no `reason`: the delegate's is a FetchError message echoing the
|
|
897
|
+
* request, Authorization header included outside prod, and this is shown to
|
|
898
|
+
* end users. Best-effort so it cannot block the caller's status flip.
|
|
899
|
+
* @param {string} [moduleName] - The module that reported the rejection.
|
|
900
|
+
* @param {number} [statusCode] - HTTP status the module rejected us with.
|
|
901
|
+
*/
|
|
902
|
+
async _recordCredentialRejection(moduleName, statusCode) {
|
|
903
|
+
try {
|
|
904
|
+
await this.updateIntegrationMessages.execute(
|
|
905
|
+
this.id,
|
|
906
|
+
'errors',
|
|
907
|
+
'Authentication Error',
|
|
908
|
+
this._authErrorMessage(moduleName, statusCode),
|
|
909
|
+
Date.now()
|
|
910
|
+
);
|
|
911
|
+
} catch (error) {
|
|
912
|
+
console.error(
|
|
913
|
+
`[Frigg] Failed to record credential rejection for integration ${this.id}:`,
|
|
914
|
+
error
|
|
915
|
+
);
|
|
916
|
+
}
|
|
917
|
+
}
|
|
880
918
|
}
|
|
881
919
|
|
|
882
920
|
module.exports = { IntegrationBase };
|
|
@@ -16,11 +16,13 @@
|
|
|
16
16
|
*
|
|
17
17
|
* 2. Derived-fields phase — duration, recordsPerSecond,
|
|
18
18
|
* estimatedCompletion. Computed from the post-atomic snapshot and
|
|
19
|
-
* written
|
|
20
|
-
*
|
|
21
|
-
* "whichever handler wrote last"
|
|
22
|
-
*
|
|
23
|
-
*
|
|
19
|
+
* written through the same atomic primitive, scoped to only those
|
|
20
|
+
* three paths. The values themselves stay best-effort (under
|
|
21
|
+
* concurrent writers they reflect "whichever handler wrote last"),
|
|
22
|
+
* but the write can no longer clobber another caller's counters.
|
|
23
|
+
* Writing the full context/results blob here — as the legacy
|
|
24
|
+
* `update()` path did — replayed phase 1's snapshot over the row and
|
|
25
|
+
* permanently discarded increments landed in between.
|
|
24
26
|
*
|
|
25
27
|
* Optionally broadcasts progress via WebSocket service if provided.
|
|
26
28
|
*
|
|
@@ -124,44 +126,48 @@ class UpdateProcessMetrics {
|
|
|
124
126
|
throw processNotFound(`Process not found: ${processId}`);
|
|
125
127
|
}
|
|
126
128
|
|
|
127
|
-
// Phase 2: derived metrics
|
|
128
|
-
//
|
|
129
|
-
//
|
|
129
|
+
// Phase 2: derived metrics. Written through the SAME atomic path
|
|
130
|
+
// as phase 1, touching ONLY the paths this phase owns. Writing the
|
|
131
|
+
// whole context/results blob here (as the legacy `update()` did)
|
|
132
|
+
// replayed phase 1's snapshot over the row and silently discarded
|
|
133
|
+
// any increment a concurrent caller had landed in between.
|
|
130
134
|
const context = updatedProcess.context || {};
|
|
131
|
-
const results = updatedProcess.results || { aggregateData: {} };
|
|
132
|
-
if (!results.aggregateData) results.aggregateData = {};
|
|
133
135
|
|
|
134
136
|
if (context.processedRecords > 0 || context.totalRecords > 0) {
|
|
135
137
|
const startTime = new Date(
|
|
136
138
|
context.startTime || updatedProcess.createdAt
|
|
137
139
|
);
|
|
138
140
|
const elapsed = Date.now() - startTime.getTime();
|
|
139
|
-
results.aggregateData.duration = elapsed;
|
|
140
141
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
context.processedRecords / (elapsed / 1000)
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
142
|
+
const recordsPerSecond =
|
|
143
|
+
elapsed > 0 && context.processedRecords > 0
|
|
144
|
+
? context.processedRecords / (elapsed / 1000)
|
|
145
|
+
: 0;
|
|
146
|
+
|
|
147
|
+
const set = {
|
|
148
|
+
'results.aggregateData.duration': elapsed,
|
|
149
|
+
'results.aggregateData.recordsPerSecond': recordsPerSecond,
|
|
150
|
+
};
|
|
147
151
|
|
|
148
|
-
if (
|
|
152
|
+
if (
|
|
153
|
+
context.totalRecords > 0 &&
|
|
154
|
+
context.processedRecords > 0 &&
|
|
155
|
+
recordsPerSecond > 0
|
|
156
|
+
) {
|
|
149
157
|
const remaining =
|
|
150
158
|
context.totalRecords - context.processedRecords;
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
const eta = new Date(Date.now() + etaMs);
|
|
156
|
-
context.estimatedCompletion = eta.toISOString();
|
|
157
|
-
}
|
|
159
|
+
const etaMs = (remaining / recordsPerSecond) * 1000;
|
|
160
|
+
set['context.estimatedCompletion'] = new Date(
|
|
161
|
+
Date.now() + etaMs
|
|
162
|
+
).toISOString();
|
|
158
163
|
}
|
|
159
164
|
|
|
160
165
|
try {
|
|
161
|
-
|
|
162
|
-
processId,
|
|
163
|
-
|
|
164
|
-
|
|
166
|
+
const withDerived =
|
|
167
|
+
await this.processRepository.applyProcessUpdate(processId, {
|
|
168
|
+
set,
|
|
169
|
+
});
|
|
170
|
+
if (withDerived) updatedProcess = withDerived;
|
|
165
171
|
} catch (error) {
|
|
166
172
|
// Derived-field write failures are NON-FATAL — atomic
|
|
167
173
|
// counters from phase 1 already landed. Log and return the
|
|
@@ -322,6 +322,11 @@ class OAuth2Requester extends Requester {
|
|
|
322
322
|
response_status: error?.response?.status,
|
|
323
323
|
response_data: error?.response?.data,
|
|
324
324
|
});
|
|
325
|
+
// Status only: the refresh body carries client_secret, and
|
|
326
|
+
// FetchError embeds the body in its message outside prod.
|
|
327
|
+
await this.notify(this.DLGT_INVALID_AUTH, {
|
|
328
|
+
statusCode: error?.statusCode,
|
|
329
|
+
});
|
|
325
330
|
return false;
|
|
326
331
|
}
|
|
327
332
|
}
|
|
@@ -353,8 +358,13 @@ class OAuth2Requester extends Requester {
|
|
|
353
358
|
|
|
354
359
|
await this.setTokens(tokenRes);
|
|
355
360
|
return tokenRes;
|
|
356
|
-
} catch {
|
|
357
|
-
|
|
361
|
+
} catch (error) {
|
|
362
|
+
// Status only. This request's body holds the password or client
|
|
363
|
+
// secret, and FetchError embeds the body in its message outside
|
|
364
|
+
// prod, so forwarding the error itself would log the credential.
|
|
365
|
+
await this.notify(this.DLGT_INVALID_AUTH, {
|
|
366
|
+
statusCode: error?.statusCode,
|
|
367
|
+
});
|
|
358
368
|
}
|
|
359
369
|
}
|
|
360
370
|
|
|
@@ -387,8 +397,13 @@ class OAuth2Requester extends Requester {
|
|
|
387
397
|
|
|
388
398
|
await this.setTokens(tokenRes);
|
|
389
399
|
return tokenRes;
|
|
390
|
-
} catch {
|
|
391
|
-
|
|
400
|
+
} catch (error) {
|
|
401
|
+
// Status only. This request's body holds the password or client
|
|
402
|
+
// secret, and FetchError embeds the body in its message outside
|
|
403
|
+
// prod, so forwarding the error itself would log the credential.
|
|
404
|
+
await this.notify(this.DLGT_INVALID_AUTH, {
|
|
405
|
+
statusCode: error?.statusCode,
|
|
406
|
+
});
|
|
392
407
|
}
|
|
393
408
|
}
|
|
394
409
|
}
|
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.107",
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"@aws-sdk/client-apigatewaymanagementapi": "^3.588.0",
|
|
7
7
|
"@aws-sdk/client-kms": "^3.588.0",
|
|
@@ -48,9 +48,9 @@
|
|
|
48
48
|
}
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
|
-
"@friggframework/eslint-config": "2.0.0-next.
|
|
52
|
-
"@friggframework/prettier-config": "2.0.0-next.
|
|
53
|
-
"@friggframework/test": "2.0.0-next.
|
|
51
|
+
"@friggframework/eslint-config": "2.0.0-next.107",
|
|
52
|
+
"@friggframework/prettier-config": "2.0.0-next.107",
|
|
53
|
+
"@friggframework/test": "2.0.0-next.107",
|
|
54
54
|
"@prisma/client": "^6.19.3",
|
|
55
55
|
"@types/lodash": "4.17.15",
|
|
56
56
|
"@typescript-eslint/eslint-plugin": "^8.0.0",
|
|
@@ -90,5 +90,5 @@
|
|
|
90
90
|
"publishConfig": {
|
|
91
91
|
"access": "public"
|
|
92
92
|
},
|
|
93
|
-
"gitHead": "
|
|
93
|
+
"gitHead": "27ac136ac4fc2da4c9d9806ee548a8f8c49eaa15"
|
|
94
94
|
}
|