@koloseum/utils 0.2.14 → 0.2.16
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/dist/utils.d.ts +12 -1
- package/dist/utils.js +77 -42
- package/package.json +2 -2
package/dist/utils.d.ts
CHANGED
|
@@ -223,9 +223,10 @@ export declare const Utility: {
|
|
|
223
223
|
/**
|
|
224
224
|
* Parses a `PostgrestError` object and returns a custom error object if any has occurred.
|
|
225
225
|
* @param {PostgrestError | null} postgrestError - The `PostgrestError` object, or `null` if no error occurred
|
|
226
|
+
* @param {boolean} clientSafe - Whether to clamp 5xx errors down to 422 to prevent Sentry from capturing them as unhandled; defaults to `true`
|
|
226
227
|
* @returns An object with an `error` if any has occurred
|
|
227
228
|
*/
|
|
228
|
-
parsePostgrestError: (postgrestError: PostgrestError | null) => {
|
|
229
|
+
parsePostgrestError: (postgrestError: PostgrestError | null, clientSafe?: boolean) => {
|
|
229
230
|
error?: CustomError;
|
|
230
231
|
};
|
|
231
232
|
/**
|
|
@@ -249,6 +250,16 @@ export declare const Utility: {
|
|
|
249
250
|
* @returns A sanitised string, or an empty string if the input is invalid
|
|
250
251
|
*/
|
|
251
252
|
sanitiseHtml: (input: string) => string;
|
|
253
|
+
/**
|
|
254
|
+
* Sends a welcome notification to a user.
|
|
255
|
+
* @param {SupabaseClient<Database>} supabase - The Supabase client
|
|
256
|
+
* @param {UserWithCustomMetadata} user - The user to send the notification to
|
|
257
|
+
* @param {MicroserviceGroup} microserviceGroup - The microservice group the user belongs to
|
|
258
|
+
* @returns An object with an `error` if any has occurred
|
|
259
|
+
*/
|
|
260
|
+
sendWelcomeNotification: (supabase: SupabaseClient<Database>, user: UserWithCustomMetadata, microserviceGroup: MicroserviceGroup) => Promise<{
|
|
261
|
+
error?: CustomError;
|
|
262
|
+
}>;
|
|
252
263
|
/**
|
|
253
264
|
* A regular expression for social media handles, without a leading slash or @ character.
|
|
254
265
|
*
|
package/dist/utils.js
CHANGED
|
@@ -703,6 +703,9 @@ export const Utility = {
|
|
|
703
703
|
// Return true if microservice group is public
|
|
704
704
|
if (microserviceGroup === "public")
|
|
705
705
|
return { isAuthorised: true };
|
|
706
|
+
// Validate user metadata and app metadata
|
|
707
|
+
if (!user.user_metadata || !user.app_metadata)
|
|
708
|
+
return { error: Utility.customError(400, "User metadata is required.") };
|
|
706
709
|
// Get user's roles and the role prefix
|
|
707
710
|
const roles = [];
|
|
708
711
|
const rolePrefix = microserviceGroup === "backroom" ? "backroom" : microserviceGroup.slice(0, -1);
|
|
@@ -748,43 +751,9 @@ export const Utility = {
|
|
|
748
751
|
if (validationError)
|
|
749
752
|
return { error: Utility.customError(code, validationError) };
|
|
750
753
|
// Send welcome notification if needed
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
// Update user metadata in database
|
|
755
|
-
const { error: updateError } = await supabase.auth.updateUser({
|
|
756
|
-
data: { backroom: { welcome_notification_sent: false } }
|
|
757
|
-
});
|
|
758
|
-
if (updateError)
|
|
759
|
-
return { error: Utility.customError(updateError.status ?? 500, updateError.message) };
|
|
760
|
-
// Update user object in memory
|
|
761
|
-
user.user_metadata.backroom = { welcome_notification_sent: false };
|
|
762
|
-
}
|
|
763
|
-
// Define SuprSend workflow body
|
|
764
|
-
const workflowBody = {
|
|
765
|
-
name: "welcome-to-backroom",
|
|
766
|
-
template: "welcome-to-backroom",
|
|
767
|
-
notification_category: "system",
|
|
768
|
-
users: [
|
|
769
|
-
{
|
|
770
|
-
distinct_id: user.id,
|
|
771
|
-
$skip_create: true
|
|
772
|
-
}
|
|
773
|
-
]
|
|
774
|
-
};
|
|
775
|
-
// Send welcome notification
|
|
776
|
-
const { code, error: workflowError } = await Utility.callEdgeFunction(false, supabase, `suprsend/trigger-workflow?user-id=${user.id}`, { method: "POST", body: workflowBody });
|
|
777
|
-
if (workflowError)
|
|
778
|
-
return { error: Utility.customError(code, workflowError) };
|
|
779
|
-
// Update user metadata in database
|
|
780
|
-
const { error: updateError } = await supabase.auth.updateUser({
|
|
781
|
-
data: { backroom: { welcome_notification_sent: true } }
|
|
782
|
-
});
|
|
783
|
-
if (updateError)
|
|
784
|
-
return { error: Utility.customError(updateError.status ?? 500, updateError.message) };
|
|
785
|
-
// Update user object in memory
|
|
786
|
-
user.user_metadata.backroom.welcome_notification_sent = true;
|
|
787
|
-
}
|
|
754
|
+
const { error: welcomeError } = await Utility.sendWelcomeNotification(supabase, user, microserviceGroup);
|
|
755
|
+
if (welcomeError)
|
|
756
|
+
return { error: welcomeError };
|
|
788
757
|
// Return result
|
|
789
758
|
return { isAuthorised };
|
|
790
759
|
},
|
|
@@ -1268,17 +1237,21 @@ export const Utility = {
|
|
|
1268
1237
|
/**
|
|
1269
1238
|
* Parses a `PostgrestError` object and returns a custom error object if any has occurred.
|
|
1270
1239
|
* @param {PostgrestError | null} postgrestError - The `PostgrestError` object, or `null` if no error occurred
|
|
1240
|
+
* @param {boolean} clientSafe - Whether to clamp 5xx errors down to 422 to prevent Sentry from capturing them as unhandled; defaults to `true`
|
|
1271
1241
|
* @returns An object with an `error` if any has occurred
|
|
1272
1242
|
*/
|
|
1273
|
-
parsePostgrestError: (postgrestError) => {
|
|
1243
|
+
parsePostgrestError: (postgrestError, clientSafe = true) => {
|
|
1274
1244
|
// Return undefined if no error occurred
|
|
1275
1245
|
let error;
|
|
1276
1246
|
if (!postgrestError)
|
|
1277
1247
|
return { error };
|
|
1278
|
-
//
|
|
1248
|
+
// Get custom error code from hint
|
|
1279
1249
|
const customErrorCode = Number(postgrestError.hint);
|
|
1250
|
+
// Clamp 5xx errors down to 422 to prevent Sentry from capturing them as unhandled; see https://koloseum-technologies.sentry.io/issues/6766267685/
|
|
1251
|
+
let statusCode = clientSafe ? (customErrorCode >= 500 ? 422 : customErrorCode) : customErrorCode;
|
|
1252
|
+
// Return custom error if hint is a number between 400 and 599
|
|
1280
1253
|
if (!isNaN(customErrorCode) && customErrorCode >= 400 && customErrorCode <= 599) {
|
|
1281
|
-
error = Utility.customError(
|
|
1254
|
+
error = Utility.customError(statusCode, postgrestError.message);
|
|
1282
1255
|
return { error };
|
|
1283
1256
|
}
|
|
1284
1257
|
// Map Postgrest error codes to custom error codes
|
|
@@ -1316,11 +1289,12 @@ export const Utility = {
|
|
|
1316
1289
|
// Return custom error if Postgrest error code is found
|
|
1317
1290
|
for (const { code, status } of errorMap)
|
|
1318
1291
|
if (postgrestError.code === code || postgrestError.code.startsWith(code)) {
|
|
1319
|
-
|
|
1292
|
+
statusCode = clientSafe ? (status >= 500 ? 422 : status) : status;
|
|
1293
|
+
error = Utility.customError(statusCode, Status.ERROR);
|
|
1320
1294
|
return { error };
|
|
1321
1295
|
}
|
|
1322
1296
|
// Return generic error
|
|
1323
|
-
error = Utility.customError(500, Status.ERROR);
|
|
1297
|
+
error = Utility.customError(clientSafe ? 422 : 500, Status.ERROR);
|
|
1324
1298
|
return { error };
|
|
1325
1299
|
},
|
|
1326
1300
|
/**
|
|
@@ -1352,6 +1326,67 @@ export const Utility = {
|
|
|
1352
1326
|
* @returns A sanitised string, or an empty string if the input is invalid
|
|
1353
1327
|
*/
|
|
1354
1328
|
sanitiseHtml: (input) => typeof input !== "string" ? "" : sanitize(input, { allowedTags: [], allowedAttributes: {} }),
|
|
1329
|
+
/**
|
|
1330
|
+
* Sends a welcome notification to a user.
|
|
1331
|
+
* @param {SupabaseClient<Database>} supabase - The Supabase client
|
|
1332
|
+
* @param {UserWithCustomMetadata} user - The user to send the notification to
|
|
1333
|
+
* @param {MicroserviceGroup} microserviceGroup - The microservice group the user belongs to
|
|
1334
|
+
* @returns An object with an `error` if any has occurred
|
|
1335
|
+
*/
|
|
1336
|
+
sendWelcomeNotification: async (supabase, user, microserviceGroup) => {
|
|
1337
|
+
// Backroom
|
|
1338
|
+
if (microserviceGroup === "backroom" && !user.user_metadata.backroom?.welcome_notification_sent) {
|
|
1339
|
+
// Use atomic update with timestamp to prevent race conditions
|
|
1340
|
+
const currentTime = new Date().toISOString();
|
|
1341
|
+
const { data: updatedUser, error: updateError } = await supabase.auth.updateUser({
|
|
1342
|
+
data: {
|
|
1343
|
+
backroom: {
|
|
1344
|
+
...user.user_metadata.backroom,
|
|
1345
|
+
welcome_notification_sent: true,
|
|
1346
|
+
welcome_notification_timestamp: currentTime
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1349
|
+
});
|
|
1350
|
+
if (updateError)
|
|
1351
|
+
return { error: Utility.customError(updateError.status ?? 500, updateError.message) };
|
|
1352
|
+
// Only send notification if flag was successfully updated
|
|
1353
|
+
const updatedBackroom = updatedUser.user?.user_metadata?.backroom;
|
|
1354
|
+
if (updatedBackroom?.welcome_notification_sent === true &&
|
|
1355
|
+
updatedBackroom?.welcome_notification_timestamp === currentTime) {
|
|
1356
|
+
// Define SuprSend workflow body
|
|
1357
|
+
const workflowBody = {
|
|
1358
|
+
name: "welcome-to-backroom",
|
|
1359
|
+
template: "welcome-to-backroom",
|
|
1360
|
+
notification_category: "system",
|
|
1361
|
+
users: [
|
|
1362
|
+
{
|
|
1363
|
+
distinct_id: user.id,
|
|
1364
|
+
$skip_create: true
|
|
1365
|
+
}
|
|
1366
|
+
]
|
|
1367
|
+
};
|
|
1368
|
+
// Send welcome notification
|
|
1369
|
+
const { code, error: workflowError } = await Utility.callEdgeFunction(false, supabase, `suprsend/trigger-workflow?user-id=${user.id}`, { method: "POST", body: workflowBody });
|
|
1370
|
+
// If notification fails, revert the flag and return error
|
|
1371
|
+
if (workflowError) {
|
|
1372
|
+
await supabase.auth.updateUser({
|
|
1373
|
+
data: {
|
|
1374
|
+
backroom: {
|
|
1375
|
+
...user.user_metadata.backroom,
|
|
1376
|
+
welcome_notification_sent: false,
|
|
1377
|
+
welcome_notification_timestamp: undefined
|
|
1378
|
+
}
|
|
1379
|
+
}
|
|
1380
|
+
});
|
|
1381
|
+
return { error: Utility.customError(code, workflowError) };
|
|
1382
|
+
}
|
|
1383
|
+
// Update user object in memory
|
|
1384
|
+
user.user_metadata.backroom = updatedBackroom;
|
|
1385
|
+
}
|
|
1386
|
+
}
|
|
1387
|
+
// Return empty object
|
|
1388
|
+
return {};
|
|
1389
|
+
},
|
|
1355
1390
|
/**
|
|
1356
1391
|
* A regular expression for social media handles, without a leading slash or @ character.
|
|
1357
1392
|
*
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@koloseum/utils",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.16",
|
|
4
4
|
"author": "Koloseum Technologies Limited",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Utility logic for use across Koloseum web apps (TypeScript)",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"validator": "^13.15.15"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
|
-
"@koloseum/types": "^0.2.
|
|
41
|
+
"@koloseum/types": "^0.2.5",
|
|
42
42
|
"@playwright/test": "^1.53.1",
|
|
43
43
|
"@suprsend/web-components": "^0.2.1",
|
|
44
44
|
"@types/sanitize-html": "^2.16.0",
|