-
-
Notifications
You must be signed in to change notification settings - Fork 120
Refactor request validation and parsing using utility functions #168
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,66 +1,93 @@ | ||
| import AppwriteService from "./appwrite.js"; | ||
| import LivekitService from "./livekit.js"; | ||
| import { throwIfMissing } from "./utils.js"; | ||
| import { throwIfMissing, parseBody } from "./utils.js"; | ||
|
|
||
| export default async ({ req, res, log, error }) => { | ||
| throwIfMissing(process.env, [ | ||
| "APPWRITE_API_KEY", | ||
| "MASTER_DATABASE_ID", | ||
| "ROOMS_COLLECTION_ID", | ||
| "LIVEKIT_HOST", | ||
| "LIVEKIT_API_KEY", | ||
| "LIVEKIT_API_SECRET", | ||
| "LIVEKIT_SOCKET_URL", | ||
| ]); | ||
| // Validate environment variables | ||
| try { | ||
| throwIfMissing(process.env, [ | ||
| "APPWRITE_API_KEY", | ||
| "MASTER_DATABASE_ID", | ||
| "ROOMS_COLLECTION_ID", | ||
| "LIVEKIT_HOST", | ||
| "LIVEKIT_API_KEY", | ||
| "LIVEKIT_API_SECRET", | ||
| "LIVEKIT_SOCKET_URL", | ||
| ]); | ||
| } catch (err) { | ||
| error("[ENV_ERROR] " + err.message); | ||
| return res.json({ | ||
| success: false, | ||
| message: err.message, | ||
| }, 500); | ||
| } | ||
|
|
||
| const appwrite = new AppwriteService(); | ||
| const livekit = new LivekitService(); | ||
|
|
||
| let data; | ||
|
|
||
| // β Safe parsing + validation | ||
| try { | ||
| throwIfMissing(JSON.parse(req.body), ["name", "adminUid", "tags"]); | ||
| data = parseBody(req.body); | ||
| throwIfMissing(data, ["name", "adminUid", "tags"]); | ||
| } catch (err) { | ||
| error(err.message); | ||
| return res.json({ msg: err.message }, 400); | ||
| error("[VALIDATION_ERROR] " + err.message); | ||
| return res.json({ | ||
| success: false, | ||
| message: err.message, | ||
| }, 400); | ||
| } | ||
|
|
||
| const { name, description = "", adminUid, tags } = data; | ||
|
|
||
| try { | ||
| log(req); | ||
| const { name, description, adminUid, tags } = JSON.parse(req.body); | ||
| log("[CREATE_ROOM_REQUEST]", { name, adminUid, tags }); | ||
|
|
||
| // create a new room on appwrite | ||
| // Create room in Appwrite | ||
| const newRoomdata = { | ||
| name, | ||
| description, | ||
| adminUid, | ||
| tags, | ||
| totalParticipants: 1, | ||
| }; | ||
|
|
||
| const appwriteRoomId = await appwrite.createRoom(newRoomdata); | ||
| log(appwriteRoomId); | ||
| log("[APPWRITE_ROOM_CREATED]", appwriteRoomId); | ||
|
|
||
| // create a new livekit room | ||
| // Create room in LiveKit | ||
| const livekitRoomOptions = { | ||
| name: appwriteRoomId, // using appwrite room doc id as livekit room name | ||
| emptyTimeout: 300, // timeout in seconds | ||
| name: appwriteRoomId, | ||
| emptyTimeout: 300, | ||
| }; | ||
|
|
||
| const livekitRoom = await livekit.createRoom(livekitRoomOptions); | ||
| log(livekitRoom); | ||
| log("[LIVEKIT_ROOM_CREATED]", livekitRoom); | ||
|
|
||
| // Creating a token for the admin | ||
| // Generate token for admin | ||
| const accessToken = livekit.generateToken( | ||
| appwriteRoomId, | ||
| adminUid, | ||
| true | ||
| ); | ||
|
|
||
| return res.json({ | ||
| msg: "Room created Successfully", | ||
| livekit_room: livekitRoom, | ||
| livekit_socket_url: `${process.env.LIVEKIT_SOCKET_URL}`, | ||
| access_token: accessToken, | ||
| success: true, | ||
| message: "Room created successfully", | ||
| data: { | ||
| roomId: appwriteRoomId, | ||
| livekit_room: livekitRoom, | ||
| livekit_socket_url: process.env.LIVEKIT_SOCKET_URL, | ||
| access_token: accessToken, | ||
| }, | ||
| }); | ||
| } catch (e) { | ||
| error(String(e)); | ||
| return res.json({ msg: "Room creation failed" }, 500); | ||
| error("[CREATE_ROOM_ERROR] " + String(e)); | ||
|
|
||
| return res.json({ | ||
| success: false, | ||
| message: "Room creation failed", | ||
| }, 500); | ||
| } | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,11 +1,25 @@ | ||
| export const throwIfMissing = (obj, keys) => { | ||
| const missing = []; | ||
|
|
||
| for (let key of keys) { | ||
| if (!(key in obj) || !obj[key]) { | ||
| if (!(key in obj) || obj[key] === undefined || obj[key] === null) { | ||
| missing.push(key); | ||
| } | ||
| } | ||
|
|
||
| if (missing.length > 0) { | ||
| throw new Error(`Missing required fields: ${missing.join(", ")}`); | ||
| } | ||
| }; | ||
|
|
||
| export const parseBody = (body) => { | ||
| if (!body) { | ||
| throw new Error("Request body is empty"); | ||
| } | ||
|
|
||
| try { | ||
| return JSON.parse(body); | ||
| } catch (err) { | ||
| throw new Error("Invalid JSON body"); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,35 +2,55 @@ import AppwriteService from "./appwrite.js"; | |
| import { throwIfMissing } from "./utils.js"; | ||
|
|
||
| export default async (context) => { | ||
| throwIfMissing(process.env, [ | ||
| "APPWRITE_API_KEY", | ||
| "ROOMS_COLLECTION_ID", | ||
| "PARTICIPANTS_COLLECTION_ID", | ||
| "ACTIVE_PAIRS_COLLECTION_ID", | ||
| "RETENTION_PERIOD_DAYS", | ||
| "VERIFICATION_DATABASE_ID", | ||
| "OTP_COLLECTION_ID", | ||
| ]); | ||
| const { res, log, error } = context; | ||
|
|
||
| // Environment validation | ||
| try { | ||
| throwIfMissing(process.env, [ | ||
| "APPWRITE_API_KEY", | ||
| "ROOMS_COLLECTION_ID", | ||
| "PARTICIPANTS_COLLECTION_ID", | ||
| "ACTIVE_PAIRS_COLLECTION_ID", | ||
| "RETENTION_PERIOD_DAYS", | ||
| "VERIFICATION_DATABASE_ID", | ||
| "OTP_COLLECTION_ID", | ||
| ]); | ||
| } catch (err) { | ||
| error("[ENV_ERROR] " + err.message); | ||
| return res.json({ | ||
| success: false, | ||
| message: err.message, | ||
| }, 500); | ||
| } | ||
|
|
||
| const appwrite = new AppwriteService(); | ||
|
|
||
| // Cleanup Participants | ||
| try { | ||
| await appwrite.cleanParticipantsCollection(); | ||
| log("[CLEANUP] Participants collection cleaned"); | ||
| } catch (e) { | ||
| context.error(String(e)); | ||
| error("[CLEANUP_ERROR] Participants: " + String(e)); | ||
| } | ||
|
|
||
| // Cleanup Active Pairs | ||
| try { | ||
| await appwrite.cleanActivePairsCollection(); | ||
| log("[CLEANUP] Active pairs collection cleaned"); | ||
| } catch (e) { | ||
| context.error(String(e)); | ||
| error("[CLEANUP_ERROR] ActivePairs: " + String(e)); | ||
| } | ||
|
|
||
| // Cleanup OTPs | ||
| try { | ||
| await appwrite.clearOldOTPs(); | ||
| log("[CLEANUP] Old OTPs cleared"); | ||
| } catch (e) { | ||
| context.error(String(e)); | ||
| error("[CLEANUP_ERROR] OTP: " + String(e)); | ||
| } | ||
|
|
||
| return context.res.send("Database Cleanup completed. And unnecessary OTPs are also cleared."); | ||
| }; | ||
| return res.json({ | ||
| success: true, | ||
| message: "Database cleanup completed and expired OTPs cleared", | ||
| }); | ||
|
Comment on lines
+52
to
+55
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Success response returned even when individual cleanup steps fail. The function returns π‘ Suggested approach+ const errors = [];
+
// Cleanup Participants
try {
await appwrite.cleanParticipantsCollection();
log("[CLEANUP] Participants collection cleaned");
} catch (e) {
error("[CLEANUP_ERROR] Participants: " + String(e));
+ errors.push("Participants");
}
// ... similar for other phases
return res.json({
- success: true,
+ success: errors.length === 0,
message: "Database cleanup completed and expired OTPs cleared",
+ ...(errors.length > 0 && { failedPhases: errors }),
});π€ Prompt for AI Agents |
||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,18 +1,32 @@ | ||
| export const throwIfMissing = (obj, keys) => { | ||
| const missing = []; | ||
|
|
||
| for (let key of keys) { | ||
| if (!(key in obj) || (!obj[key] && obj[key] != 0)) { | ||
| if (!(key in obj) || obj[key] === undefined || obj[key] === null) { | ||
| missing.push(key); | ||
| } | ||
| } | ||
|
|
||
| if (missing.length > 0) { | ||
| throw new Error(`Missing required fields: ${missing.join(", ")}`); | ||
| } | ||
| }; | ||
|
|
||
| export const parseBody = (body) => { | ||
| if (!body) { | ||
| throw new Error("Request body is empty"); | ||
| } | ||
|
|
||
| try { | ||
| return JSON.parse(body); | ||
| } catch (err) { | ||
| throw new Error("Invalid JSON body"); | ||
| } | ||
| }; | ||
|
Comment on lines
+15
to
+25
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. π§© Analysis chainπ Script executed: #!/bin/bash
# Check if parseBody is imported/used in database-cleaner/main.js
rg -n "parseBody" functions/database-cleaner/src/Repository: AOSSIE-Org/Resonate-Backend Length of output: 150 π Script executed: # Check imports and usage of parseBody in database-cleaner main.js
rg -n "parseBody\|import.*parseBody" functions/database-cleaner/src/main.jsRepository: AOSSIE-Org/Resonate-Backend Length of output: 53 π Script executed: # Check the main.js file to understand what the database-cleaner function does
cat -n functions/database-cleaner/src/main.jsRepository: AOSSIE-Org/Resonate-Backend Length of output: 1991 π Script executed: # Verify if parseBody is called anywhere within utils.js itself
rg -n "parseBody(" functions/database-cleaner/src/utils.jsRepository: AOSSIE-Org/Resonate-Backend Length of output: 141 π Script executed: # Check if parseBody is called within utils.js (fixing regex)
rg -n "parseBody\s*\(" functions/database-cleaner/src/utils.jsRepository: AOSSIE-Org/Resonate-Backend Length of output: 53 π Script executed: # Also check the full utils.js to see what functions are defined and exported
cat -n functions/database-cleaner/src/utils.jsRepository: AOSSIE-Org/Resonate-Backend Length of output: 1110 Remove unused The π€ Prompt for AI Agents |
||
|
|
||
| export const getExpiryDate = () => { | ||
| const retentionPeriod = +(process.env.RETENTION_PERIOD_DAYS ?? 1); | ||
| const expiryDate = new Date(); | ||
| expiryDate.setDate(expiryDate.getDate() - retentionPeriod); | ||
| return expiryDate.toISOString(); | ||
| }; | ||
| }; | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Try/catch ineffective for
cleanParticipantsCollectionerrors.Per the relevant code snippet at
functions/database-cleaner/src/appwrite.js:31-46,cleanParticipantsCollectionusesforEachwith async callbacks:Errors from individual document deletions won't propagate to this try/catch, and the success log on line 31 will print even if deletions fail silently. Consider refactoring
cleanParticipantsCollectionto usePromise.alllike the other cleanup methods.π€ Prompt for AI Agents