From 3bd981fb848c9ead92ef19e3980a70bc4d0519a5 Mon Sep 17 00:00:00 2001 From: Heryan Djaruma Date: Fri, 23 May 2025 00:02:30 +0700 Subject: [PATCH 1/4] fix validation logic flaw --- .../src/controllers/application_controller.ts | 56 ++++++++++++++++--- 1 file changed, 49 insertions(+), 7 deletions(-) diff --git a/functions/src/controllers/application_controller.ts b/functions/src/controllers/application_controller.ts index da9d151..4c10b65 100644 --- a/functions/src/controllers/application_controller.ts +++ b/functions/src/controllers/application_controller.ts @@ -290,6 +290,14 @@ async function validateFileUploaded( return errors; } + // skip validation if not required and value is empty + if ( + validation.required !== true && + (fieldValue === undefined || fieldValue === "" || fieldValue === null) + ) { + return errors; + } + try { // check in firebase storage const fileName = `${uid}_${question.id}.${fieldValue.split(".").pop()}`; @@ -324,7 +332,7 @@ async function validateFileUploaded( // eslint-disable-next-line require-jsdoc function validateDropdownValue(fieldValue: string | any, question: Question) { - const errors = []; + const errors: { field_id: string; message: string; }[] = []; const validation = question.validation as DropdownValidation; @@ -340,6 +348,14 @@ function validateDropdownValue(fieldValue: string | any, question: Question) { return errors; } + // skip validation if not required and value is empty + if ( + validation.required !== true && + (fieldValue === undefined || fieldValue === "" || fieldValue === null) + ) { + return errors; + } + // check valid value const options = question.options; if (options && !options.includes(fieldValue)) { @@ -353,7 +369,7 @@ function validateDropdownValue(fieldValue: string | any, question: Question) { // eslint-disable-next-line require-jsdoc function validateDatetimeValue(fieldValue: string, question: Question) { - const errors = []; + const errors: { field_id: string; message: string; }[] = []; const validation = question.validation as DatetimeValidation; @@ -369,6 +385,14 @@ function validateDatetimeValue(fieldValue: string, question: Question) { return errors; } + // skip validation if not required and value is empty + if ( + validation.required !== true && + (fieldValue === undefined || fieldValue === "" || fieldValue === null) + ) { + return errors; + } + // check valid date if (!validator.isISO8601(fieldValue)) { errors.push({ @@ -381,7 +405,7 @@ function validateDatetimeValue(fieldValue: string, question: Question) { // eslint-disable-next-line require-jsdoc function validateNumberValue(fieldValue: number | any, question: Question) { - const errors = []; + const errors: { field_id: string; message: string; }[] = []; const validation = question.validation as NumberValidation; @@ -397,6 +421,14 @@ function validateNumberValue(fieldValue: number | any, question: Question) { return errors; } + // skip validation if not required and value is empty + if ( + validation.required !== true && + (fieldValue === undefined || fieldValue === "" || fieldValue === null) + ) { + return errors; + } + // check type if (typeof fieldValue !== "number") { errors.push({ @@ -410,14 +442,16 @@ function validateNumberValue(fieldValue: number | any, question: Question) { if (validation.minValue && fieldValue < validation.minValue) { errors.push({ field_id: `${question.id}`, - message: `Must be more than equals ${validation.minValue}`, + message: `Must be more than or equal to ${validation.minValue}`, }); - } else if (validation.maxValue && fieldValue > validation.maxValue) { + } + if (validation.maxValue && fieldValue > validation.maxValue) { errors.push({ field_id: `${question.id}`, - message: `Must be less than equals ${validation.maxValue}`, + message: `Must be less than or equal to ${validation.maxValue}`, }); } + return errors; } @@ -425,7 +459,7 @@ function validateNumberValue(fieldValue: number | any, question: Question) { * Validate string value. Also works for textarea. */ function validateStringValue(fieldValue: string | any, question: Question) { - const errors = []; + const errors: { field_id: string; message: string; }[] = []; const validation = question.validation as StringValidation; @@ -441,6 +475,14 @@ function validateStringValue(fieldValue: string | any, question: Question) { return errors; } + // skip validation if not required and value is empty + if ( + validation.required !== true && + (fieldValue === undefined || fieldValue === "" || fieldValue === null) + ) { + return errors; + } + // check type if (typeof fieldValue !== "string") { errors.push({ From d9c54082798e26e87396a2c838b507c1dca82911 Mon Sep 17 00:00:00 2001 From: Heryan Djaruma Date: Fri, 23 May 2025 00:26:56 +0700 Subject: [PATCH 2/4] improve file upload validation --- functions/src/config/firebase.ts | 1 + .../src/controllers/application_controller.ts | 78 +++++++++++-------- 2 files changed, 45 insertions(+), 34 deletions(-) diff --git a/functions/src/config/firebase.ts b/functions/src/config/firebase.ts index c4b779a..3220171 100644 --- a/functions/src/config/firebase.ts +++ b/functions/src/config/firebase.ts @@ -11,6 +11,7 @@ admin.initializeApp({ }); const db = admin.firestore(); +db.settings({ ignoreUndefinedProperties: true }); const auth = admin.auth(); /** diff --git a/functions/src/controllers/application_controller.ts b/functions/src/controllers/application_controller.ts index 4c10b65..7419d02 100644 --- a/functions/src/controllers/application_controller.ts +++ b/functions/src/controllers/application_controller.ts @@ -162,7 +162,8 @@ async function constructDataToSave( for (const question of questions) { if (question.id === undefined || question.id === null) continue; const fieldValue = req.body[question.id]; - if (question.type === QUESTION_TYPE.FILE) { + // rewrite file path + if (question.type === QUESTION_TYPE.FILE && !(fieldValue === undefined || fieldValue === "" || fieldValue === null)) { dataToSave[ question.id ] = `${STORAGE_BASE_LINK}${USER_UPLOAD_PATH}${UID}_${ @@ -274,10 +275,19 @@ async function validateFileUploaded( question: Question, uid: string ) { - const errors = []; + const errors: { field_id: string; message: string; }[] = []; const validation = question.validation as FileValidation; + + // skip validation if not required and value is empty + if ( + validation.required !== true && + (fieldValue === undefined || fieldValue === "" || fieldValue === null) + ) { + return errors; + } + // required if ( validation.required === true && @@ -336,23 +346,23 @@ function validateDropdownValue(fieldValue: string | any, question: Question) { const validation = question.validation as DropdownValidation; - // required + // skip validation if not required and value is empty if ( - validation.required === true && + validation.required !== true && (fieldValue === undefined || fieldValue === "" || fieldValue === null) ) { - errors.push({ - field_id: `${question.id}`, - message: `This field is required`, - }); return errors; } - // skip validation if not required and value is empty + // required if ( - validation.required !== true && + validation.required === true && (fieldValue === undefined || fieldValue === "" || fieldValue === null) ) { + errors.push({ + field_id: `${question.id}`, + message: `This field is required`, + }); return errors; } @@ -373,23 +383,23 @@ function validateDatetimeValue(fieldValue: string, question: Question) { const validation = question.validation as DatetimeValidation; - // required + // skip validation if not required and value is empty if ( - validation.required === true && + validation.required !== true && (fieldValue === undefined || fieldValue === "" || fieldValue === null) ) { - errors.push({ - field_id: `${question.id}`, - message: `This field is required`, - }); return errors; } - // skip validation if not required and value is empty + // required if ( - validation.required !== true && + validation.required === true && (fieldValue === undefined || fieldValue === "" || fieldValue === null) ) { + errors.push({ + field_id: `${question.id}`, + message: `This field is required`, + }); return errors; } @@ -409,23 +419,23 @@ function validateNumberValue(fieldValue: number | any, question: Question) { const validation = question.validation as NumberValidation; - // required + // skip validation if not required and value is empty if ( - validation.required === true && + validation.required !== true && (fieldValue === undefined || fieldValue === "" || fieldValue === null) ) { - errors.push({ - field_id: `${question.id}`, - message: `This field is required`, - }); return errors; } - // skip validation if not required and value is empty + // required if ( - validation.required !== true && + validation.required === true && (fieldValue === undefined || fieldValue === "" || fieldValue === null) ) { + errors.push({ + field_id: `${question.id}`, + message: `This field is required`, + }); return errors; } @@ -463,23 +473,23 @@ function validateStringValue(fieldValue: string | any, question: Question) { const validation = question.validation as StringValidation; - // required + // skip validation if not required and value is empty if ( - validation.required === true && + validation.required !== true && (fieldValue === undefined || fieldValue === "" || fieldValue === null) ) { - errors.push({ - field_id: `${question.id}`, - message: `This field is required`, - }); return errors; } - // skip validation if not required and value is empty + // required if ( - validation.required !== true && + validation.required === true && (fieldValue === undefined || fieldValue === "" || fieldValue === null) ) { + errors.push({ + field_id: `${question.id}`, + message: `This field is required`, + }); return errors; } From 184bd44bd8142391e85e207d4a462b873ba60c4a Mon Sep 17 00:00:00 2001 From: Heryan Djaruma Date: Fri, 23 May 2025 01:19:52 +0700 Subject: [PATCH 3/4] remove duplicate --- functions/src/controllers/application_controller.ts | 8 -------- 1 file changed, 8 deletions(-) diff --git a/functions/src/controllers/application_controller.ts b/functions/src/controllers/application_controller.ts index 7419d02..4c27663 100644 --- a/functions/src/controllers/application_controller.ts +++ b/functions/src/controllers/application_controller.ts @@ -300,14 +300,6 @@ async function validateFileUploaded( return errors; } - // skip validation if not required and value is empty - if ( - validation.required !== true && - (fieldValue === undefined || fieldValue === "" || fieldValue === null) - ) { - return errors; - } - try { // check in firebase storage const fileName = `${uid}_${question.id}.${fieldValue.split(".").pop()}`; From 6eef7e1c3a9c0ae5313f7e2389bc56f6d53dac05 Mon Sep 17 00:00:00 2001 From: Heryan Djaruma Date: Fri, 23 May 2025 12:24:00 +0700 Subject: [PATCH 4/4] fix incorrect lookup collection for google login --- functions/src/controllers/auth_controller.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/functions/src/controllers/auth_controller.ts b/functions/src/controllers/auth_controller.ts index 8ba6210..dfa2a55 100644 --- a/functions/src/controllers/auth_controller.ts +++ b/functions/src/controllers/auth_controller.ts @@ -282,7 +282,7 @@ export const sessionLogin = async ( user = await auth.getUserByEmail(decodedIdToken.email); // update user record for first time - const docRef = await db.collection("questions").doc(user.uid).get(); + const docRef = await db.collection("users").doc(user.uid).get(); if (!docRef.exists) { const userData: User = formatUser({ email: user.email ?? "",