-
Notifications
You must be signed in to change notification settings - Fork 13
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
Release 1.0.0 #110
base: main
Are you sure you want to change the base?
Release 1.0.0 #110
Conversation
PS-2108 : Multi year mapping with cohort Create Function to add academic year along with cohort creation
PS-2150 -> MutltiyearSupport for create cohortMember API
PS- PS-2151 multiYear-support in search cohortMember API
PS-2111 Search API to search cohort with academic year
PS-2163 Cohort hierarchy by academic year and PS-2164 My cohort by academic year
PS-2169 changes in bulk creation API for multiyear
PS- 2170 get user by cohortId with year
password reset link username changes
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.
Actionable comments posted: 1
♻️ Duplicate comments (2)
src/adapters/postgres/user-adapter.ts (2)
1414-1418
:⚠️ Potential issueFix SQL injection vulnerability.
The query string is constructed using string interpolation, which is vulnerable to SQL injection attacks. Use parameterized queries instead.
-let query = `SELECT * FROM public."CohortAcademicYear" WHERE "cohortId"= '${cohortIds}' AND "academicYearId" = '${academicYearId}'`; -let getCohortAcademicYearId = await this.usersRepository.query(query); +const query = `SELECT * FROM public."CohortAcademicYear" WHERE "cohortId"= $1 AND "academicYearId" = $2`; +const getCohortAcademicYearId = await this.usersRepository.query(query, [cohortIds, academicYearId]);🧰 Tools
🪛 eslint
[error] 1414-1414: 'query' is never reassigned. Use 'const' instead.
(prefer-const)
[error] 1416-1416: 'getCohortAcademicYearId' is never reassigned. Use 'const' instead.
(prefer-const)
2000-2000
:⚠️ Potential issueAvoid using
isNaN
; useNumber.isNaN
instead.Using the global
isNaN
function can lead to unexpected results due to type coercion.-if (!hashValue || !expires || isNaN(parseInt(expires))) { +if (!hashValue || !expires || Number.isNaN(parseInt(expires))) {🧰 Tools
🪛 Biome (1.9.4)
[error] 2000-2000: isNaN is unsafe. It attempts a type coercion. Use Number.isNaN instead.
See the MDN documentation for more details.
Unsafe fix: Use Number.isNaN instead.(lint/suspicious/noGlobalIsNan)
🧹 Nitpick comments (2)
src/adapters/postgres/user-adapter.ts (2)
Line range hint
57-100
: Add validation for injected dependencies and configuration values.Consider adding validation for required dependencies and configuration values to fail fast if any required dependency is missing. Also, consider providing default values for all configuration settings.
constructor( @InjectRepository(User) private usersRepository: Repository<User>, // ... other dependencies ) { + // Validate required dependencies + if (!this.usersRepository || !this.fieldsService || !this.notificationRequest) { + throw new Error('Required dependencies are not provided'); + } + this.jwt_secret = this.configService.get<string>("RBAC_JWT_SECRET"); + if (!this.jwt_secret) { + throw new Error('RBAC_JWT_SECRET is required'); + } this.jwt_password_reset_expires_In = this.configService.get<string>( "PASSWORD_RESET_JWT_EXPIRES_IN" ); + if (!this.jwt_password_reset_expires_In) { + this.jwt_password_reset_expires_In = '1h'; // Default value + } // ... other config }🧰 Tools
🪛 Biome (1.9.4)
[error] 574-574: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
[error] 607-607: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
🪛 eslint
[error] 97-97: Replace
'OTP_EXPIRY'
with"OTP_EXPIRY"
(prettier/prettier)
[error] 98-98: Replace
'OTP_DIGITS'
with"OTP_DIGITS"
(prettier/prettier)
[error] 99-99: Replace
'SMS_KEY'
with"SMS_KEY"
(prettier/prettier)
[error] 385-385: 'filters' is never reassigned. Use 'const' instead.
(prefer-const)
[error] 385-385: 'exclude' is never reassigned. Use 'const' instead.
(prefer-const)
[error] 385-385: 'sort' is never reassigned. Use 'const' instead.
(prefer-const)
[error] 478-478: Insert
⏎········
(prettier/prettier)
[error] 479-479: Delete
··
(prettier/prettier)
1574-1585
: Standardize error handling and improve logging.Error handling is inconsistent across methods. Consider:
- Creating a centralized error handling utility
- Adding structured logging with correlation IDs
- Standardizing error response format
// Create an error handling utility class ErrorHandler { static handleError(error: Error, apiId: string, response: Response) { LoggerUtil.error( API_RESPONSES.SERVER_ERROR, `Error: ${error.message}`, apiId, { correlationId: uuid() } ); return APIResponse.error( response, apiId, API_RESPONSES.SERVER_ERROR, error.message, HttpStatus.INTERNAL_SERVER_ERROR ); } }
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
-
src/adapters/postgres/user-adapter.ts
(10 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
src/adapters/postgres/user-adapter.ts (1)
Pattern **/*.ts
: "Review the JavaScript code for conformity with the Google JavaScript style guide, highlighting any deviations. Ensure that:
- The code adheres to best practices associated with nodejs.
- The code adheres to best practices associated with nestjs framework.
- The code adheres to best practices recommended for performance.
- The code adheres to similar naming conventions for controllers, models, services, methods, variables."
🪛 Biome (1.9.4)
src/adapters/postgres/user-adapter.ts
[error] 574-574: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
[error] 607-607: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
[error] 689-689: Avoid the delete operator which can impact performance.
Unsafe fix: Use an undefined assignment instead.
(lint/performance/noDelete)
[error] 1261-1261: Unsafe usage of optional chaining.
If it short-circuits with 'undefined' the evaluation will throw TypeError here:
(lint/correctness/noUnsafeOptionalChaining)
[error] 1147-1147: Avoid the use of spread (...
) syntax on accumulators.
Spread syntax should be avoided on accumulators (like those in .reduce
) because it causes a time complexity of O(n^2)
.
Consider methods such as .splice or .push instead.
(lint/performance/noAccumulatingSpread)
[error] 1753-1753: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
[error] 2000-2000: isNaN is unsafe. It attempts a type coercion. Use Number.isNaN instead.
See the MDN documentation for more details.
Unsafe fix: Use Number.isNaN instead.
(lint/suspicious/noGlobalIsNan)
🪛 eslint
src/adapters/postgres/user-adapter.ts
[error] 97-97: Replace 'OTP_EXPIRY'
with "OTP_EXPIRY"
(prettier/prettier)
[error] 98-98: Replace 'OTP_DIGITS'
with "OTP_DIGITS"
(prettier/prettier)
[error] 99-99: Replace 'SMS_KEY'
with "SMS_KEY"
(prettier/prettier)
[error] 385-385: 'filters' is never reassigned. Use 'const' instead.
(prefer-const)
[error] 385-385: 'exclude' is never reassigned. Use 'const' instead.
(prefer-const)
[error] 385-385: 'sort' is never reassigned. Use 'const' instead.
(prefer-const)
[error] 478-478: Insert ⏎········
(prettier/prettier)
[error] 479-479: Delete ··
(prettier/prettier)
[error] 779-779: Replace ·where:·{·userId:·userDto.userId·}
with ⏎········where:·{·userId:·userDto.userId·},⏎·····
(prettier/prettier)
[error] 795-795: Replace userDto.userData.deviceId,·userDto.userId,·user.deviceId)
with ⏎············userDto.userData.deviceId,⏎············userDto.userId,⏎············user.deviceId⏎··········);
(prettier/prettier)
[error] 796-797: Delete ⏎
(prettier/prettier)
[error] 800-800: Replace userDto.userData.deviceId,·userDto.userId,·user.deviceId)
with ⏎············userDto.userData.deviceId,⏎············userDto.userId,⏎············user.deviceId⏎··········);
(prettier/prettier)
[error] 812-812: Replace keycloakReqBody
with ⏎············keycloakReqBody⏎··········
(prettier/prettier)
[error] 814-814: Replace 'exists'
with "exists"
(prettier/prettier)
[error] 931-931: Replace updateField:·UpdateField):·Promise<'exists'
with ⏎····updateField:·UpdateField⏎··):·Promise<"exists"
(prettier/prettier)
[error] 932-933: Delete ⏎
(prettier/prettier)
[error] 938-938: Replace updateField
with ⏎········updateField⏎······
(prettier/prettier)
[error] 940-940: Replace 'exists'
with "exists"
(prettier/prettier)
[error] 944-944: 'updateResult' is never reassigned. Use 'const' instead.
(prefer-const)
[error] 944-944: Insert ;
(prettier/prettier)
[error] 949-950: Delete ⏎
(prettier/prettier)
[error] 953-953: Delete ,
(prettier/prettier)
[error] 959-959: Replace userDeviceId:·string,·userId:·string,·existingDeviceId:·string[]
with ⏎····userDeviceId:·string,⏎····userId:·string,⏎····existingDeviceId:·string[]⏎··
(prettier/prettier)
[error] 960-960: 'deviceIds' is never reassigned. Use 'const' instead.
(prefer-const)
[error] 974-974: Replace deviceIdforRemove:·string,·userId:·string,·existingDeviceId:·string[]
with ⏎····deviceIdforRemove:·string,⏎····userId:·string,⏎····existingDeviceId:·string[]⏎··
(prettier/prettier)
[error] 981-981: Replace id
with (id)
(prettier/prettier)
[error] 985-985: Replace userId:·string,·userData:·Partial<User>
with ⏎····userId:·string,⏎····userData:·Partial<User>⏎··
(prettier/prettier)
[error] 986-986: Delete ······
(prettier/prettier)
[error] 996-997: Delete ⏎
(prettier/prettier)
[error] 999-999: Replace 'An·error·occurred·while·updating·user·details'
with "An·error·occurred·while·updating·user·details"
(prettier/prettier)
[error] 1001-1002: Delete ⏎
(prettier/prettier)
[error] 1062-1062: 'errKeycloak' is never reassigned. Use 'const' instead.
(prefer-const)
[error] 1081-1081: 'resKeycloak' is never reassigned. Use 'const' instead.
(prefer-const)
[error] 1081-1082: Replace ⏎
with ;
(prettier/prettier)
[error] 1107-1108: Delete ⏎
(prettier/prettier)
[error] 1147-1147: Insert ··
(prettier/prettier)
[error] 1148-1148: Insert ··
(prettier/prettier)
[error] 1149-1149: Replace ················
with ··················
(prettier/prettier)
[error] 1395-1395: Replace user.userId·=·userCreateDto?.userId
with (user.userId·=·userCreateDto?.userId)
(prettier/prettier)
[error] 1396-1396: Replace user.username·=·userCreateDto?.username
with (user.username·=·userCreateDto?.username)
(prettier/prettier)
[error] 1397-1397: Replace user.firstName·=·userCreateDto?.firstName
with (user.firstName·=·userCreateDto?.firstName)
(prettier/prettier)
[error] 1398-1398: Replace user.middleName·=·userCreateDto?.middleName
with (user.middleName·=·userCreateDto?.middleName)
(prettier/prettier)
[error] 1399-1399: Replace user.lastName·=·userCreateDto?.lastName
with (user.lastName·=·userCreateDto?.lastName)
(prettier/prettier)
[error] 1400-1400: Replace user.gender·=·userCreateDto?.gender
with (user.gender·=·userCreateDto?.gender)
(prettier/prettier)
[error] 1401-1401: Replace user.email·=·userCreateDto?.email
with (user.email·=·userCreateDto?.email)
(prettier/prettier)
[error] 1402-1402: Replace user.mobile·=·Number(userCreateDto?.mobile)·||·null
with (user.mobile·=·Number(userCreateDto?.mobile)·||·null)
(prettier/prettier)
[error] 1403-1403: Replace user.createdBy·=·userCreateDto?.createdBy·||·userCreateDto?.createdBy
with (user.createdBy·=·userCreateDto?.createdBy·||·userCreateDto?.createdBy)
(prettier/prettier)
[error] 1414-1414: 'query' is never reassigned. Use 'const' instead.
(prefer-const)
[error] 1416-1416: 'getCohortAcademicYearId' is never reassigned. Use 'const' instead.
(prefer-const)
[error] 1421-1421: 'cohortData' is never reassigned. Use 'const' instead.
(prefer-const)
[error] 1648-1648: Insert ··
(prettier/prettier)
[error] 1755-1755: Insert ··
(prettier/prettier)
[error] 1756-1756: Insert ··
(prettier/prettier)
[error] 1876-1876: Replace mobileOrUsername:·string,·otp:·string,·reason:·string
with ⏎····mobileOrUsername:·string,⏎····otp:·string,⏎····reason:·string⏎··
(prettier/prettier)
[error] 1900-1900: Replace mobile,·reason
with ⏎········mobile,⏎········reason⏎······
(prettier/prettier)
[error] 1906-1906: Insert ,
(prettier/prettier)
[error] 1908-1908: Insert ,
(prettier/prettier)
[error] 1917-1918: Delete ⏎···
(prettier/prettier)
[error] 1939-1939: Replace mobileWithCode,·otp,·reason
with ⏎········mobileWithCode,⏎········otp,⏎········reason⏎······
(prettier/prettier)
[error] 1942-1942: Insert ,
(prettier/prettier)
[error] 1945-1945: Replace "OTP",·"SEND_OTP",·replacements,·[mobile]
with ⏎········"OTP",⏎········"SEND_OTP",⏎········replacements,⏎········[mobile]⏎······
(prettier/prettier)
[error] 1947-1948: Delete ⏎···
(prettier/prettier)
[error] 1968-1968: Replace 'signup'
with "signup"
(prettier/prettier)
[error] 1979-1979: Replace 'forgot'
with "forgot"
(prettier/prettier)
[error] 1999-1999: Replace '.'
with "."
(prettier/prettier)
[error] 2056-2056: Replace context:·string,·key:·string,·replacements:·object,·receipients:·string[]
with ⏎····context:·string,⏎····key:·string,⏎····replacements:·object,⏎····receipients:·string[]⏎··
(prettier/prettier)
[error] 2073-2073: Replace mailSend?.result?.sms?.errors·&&·mailSend.result.sms.errors.length·>·0
with ⏎········mailSend?.result?.sms?.errors·&&⏎········mailSend.result.sms.errors.length·>·0⏎······
(prettier/prettier)
[error] 2074-2074: Replace (error:·{·error:·string;·})·=>·error.error
with ⏎··········(error:·{·error:·string·})·=>·error.error⏎········
(prettier/prettier)
[error] 2079-2080: Delete ⏎···
(prettier/prettier)
[error] 2082-2082: Replace ${API_RESPONSES.SMS_NOTIFICATION_ERROR}:··${error.message}
with ⏎········
${API_RESPONSES.SMS_NOTIFICATION_ERROR}:··${error.message}⏎······
(prettier/prettier)
[error] 2087-2087: Replace body:·SendPasswordResetOTPDto,·response:·Response
with ⏎····body:·SendPasswordResetOTPDto,⏎····response:·Response⏎··
(prettier/prettier)
[error] 2091-2091: 'error' is never reassigned. Use 'const' instead.
(prefer-const)
[error] 2092-2092: 'success' is never reassigned. Use 'const' instead.
(prefer-const)
[error] 2113-2113: Replace ''
with ""
(prettier/prettier)
[error] 2116-2116: Replace username,·otp,·reason
with ⏎········username,⏎········otp,⏎········reason⏎······
(prettier/prettier)
[error] 2120-2120: Insert ,
(prettier/prettier)
[error] 2123-2123: Replace userData.mobile
with ⏎············userData.mobile,⏎··········
(prettier/prettier)
[error] 2124-2124: Replace 'SMS'
with "SMS"
(prettier/prettier)
[error] 2126-2126: Replace ·type:·'SMS',·message:·
${API_RESPONSES.MOBILE_OTP_SEND_FAILED}·${e.message}·})
with ⏎············type:·"SMS",⏎············message:·
${API_RESPONSES.MOBILE_OTP_SEND_FAILED}·${e.message},⏎··········});
(prettier/prettier)
[error] 2134-2134: Insert ,
(prettier/prettier)
[error] 2137-2137: Replace userData.email
with ⏎············userData.email,⏎··········
(prettier/prettier)
[error] 2138-2138: Replace ·type:·'Email',·message:·API_RESPONSES.EMAIL_SENT_OTP·})
with ⏎············type:·"Email",⏎············message:·API_RESPONSES.EMAIL_SENT_OTP,⏎··········});
(prettier/prettier)
[error] 2140-2140: Replace ·type:·'Email',·message:·
${API_RESPONSES.EMAIL_OTP_SEND_FAILED}:·${e.message}·})
with ⏎············type:·"Email",⏎············message:·
${API_RESPONSES.EMAIL_OTP_SEND_FAILED}:·${e.message},⏎··········});
(prettier/prettier)
[error] 2143-2143: Delete ·
(prettier/prettier)
[error] 2144-2144: Insert ⏎·······
(prettier/prettier)
[error] 2145-2145: Replace ''
with ""
(prettier/prettier)
[error] 2146-2146: Replace e·=>·e.type·===·'SMS'
with (e)·=>·e.type·===·"SMS"
(prettier/prettier)
[error] 2147-2147: Replace .filter(e·=>·e.type·===·'SMS').map(e·=>·e.message)
with ⏎············.filter((e)·=>·e.type·===·"SMS")⏎············.map((e)·=>·e.message)⏎············
(prettier/prettier)
[error] 2149-2149: Replace e·=>·e.type·===·'Email'
with (e)·=>·e.type·===·"Email"
(prettier/prettier)
[error] 2150-2150: Replace .filter(e·=>·e.type·===·'Email').map(e·=>·e.message)
with ⏎············.filter((e)·=>·e.type·===·"Email")⏎············.map((e)·=>·e.message)⏎············
(prettier/prettier)
[error] 2164-2164: Insert ,
(prettier/prettier)
[error] 2165-2165: Insert ;
(prettier/prettier)
[error] 2173-2174: Delete ⏎···
(prettier/prettier)
[error] 2183-2184: Delete ⏎
(prettier/prettier)
[error] 2187-2187: Replace context:·string,·key:·string,·replacements:·object,·emailReceipt
with ⏎····context:·string,⏎····key:·string,⏎····replacements:·object,⏎····emailReceipt⏎··
(prettier/prettier)
[error] 2202-2202: Replace mailSend?.result?.email?.errors·&&·mailSend.result.email.errors.length·>·0
with ⏎········mailSend?.result?.email?.errors·&&⏎········mailSend.result.email.errors.length·>·0⏎······
(prettier/prettier)
[error] 2203-2203: Replace (error:·{·error:·string;·})·=>·error.error
with ⏎··········(error:·{·error:·string·})·=>·error.error⏎········
(prettier/prettier)
[error] 2208-2209: Delete ⏎···
(prettier/prettier)
[error] 2211-2211: Replace ${API_RESPONSES.EMAIL_NOTIFICATION_ERROR}:··${e.message}
with ⏎········
${API_RESPONSES.EMAIL_NOTIFICATION_ERROR}:··${e.message}⏎······
(prettier/prettier)
[error] 2213-2214: Delete ⏎
(prettier/prettier)
const apiId = APIID.VERIFY_OTP; | ||
try { | ||
const { mobile, otp, hash, reason, username } = body; | ||
if (!otp || !hash || !reason) { | ||
return APIResponse.error( | ||
response, | ||
apiId, | ||
API_RESPONSES.BAD_REQUEST, | ||
API_RESPONSES.OTP_VALIDED_REQUIRED_KEY, | ||
HttpStatus.BAD_REQUEST | ||
); | ||
} | ||
// Determine the value to use for verification based on the reason | ||
let identifier: string; | ||
if (reason === 'signup') { | ||
if (!mobile) { | ||
return APIResponse.error( | ||
response, | ||
apiId, | ||
API_RESPONSES.BAD_REQUEST, | ||
API_RESPONSES.MOBILE_REQUIRED, | ||
HttpStatus.BAD_REQUEST | ||
); | ||
} | ||
identifier = this.formatMobileNumber(mobile); // Assuming this formats the mobile number | ||
} else if (reason === 'forgot') { | ||
if (!username) { | ||
return APIResponse.error( | ||
response, | ||
apiId, | ||
API_RESPONSES.BAD_REQUEST, | ||
API_RESPONSES.USERNAME_REQUIRED, | ||
HttpStatus.BAD_REQUEST | ||
); | ||
} | ||
identifier = username; | ||
} else { | ||
return APIResponse.error( | ||
response, | ||
apiId, | ||
API_RESPONSES.BAD_REQUEST, | ||
API_RESPONSES.INVALID_REASON, | ||
HttpStatus.BAD_REQUEST | ||
); | ||
} | ||
const [hashValue, expires] = hash.split('.'); | ||
if (!hashValue || !expires || isNaN(parseInt(expires))) { | ||
return APIResponse.error( | ||
response, | ||
apiId, | ||
API_RESPONSES.BAD_REQUEST, | ||
API_RESPONSES.INVALID_HASH_FORMAT, | ||
HttpStatus.BAD_REQUEST | ||
); | ||
} | ||
|
||
if (Date.now() > parseInt(expires)) { | ||
return APIResponse.error( | ||
response, | ||
apiId, | ||
API_RESPONSES.OTP_EXPIRED, | ||
API_RESPONSES.OTP_EXPIRED, | ||
HttpStatus.BAD_REQUEST | ||
); | ||
} | ||
const data = `${identifier}.${otp}.${reason}.${expires}`; | ||
const calculatedHash = this.authUtils.calculateHash(data, this.smsKey); // Create hash | ||
|
||
if (calculatedHash === hashValue) { | ||
return await APIResponse.success( | ||
response, | ||
apiId, | ||
{ success: true }, | ||
HttpStatus.OK, | ||
API_RESPONSES.OTP_VALID | ||
); | ||
} else { | ||
return APIResponse.error( | ||
response, | ||
apiId, | ||
API_RESPONSES.OTP_INVALID, | ||
API_RESPONSES.OTP_INVALID, | ||
HttpStatus.BAD_REQUEST | ||
); | ||
} | ||
} catch (e) { | ||
LoggerUtil.error( | ||
`${API_RESPONSES.SERVER_ERROR}`, | ||
`Error during OTP verification: ${e.message}`, | ||
apiId | ||
); | ||
return APIResponse.error( | ||
response, | ||
apiId, | ||
API_RESPONSES.SERVER_ERROR, | ||
`Error : ${e?.message}`, | ||
HttpStatus.INTERNAL_SERVER_ERROR | ||
); | ||
} | ||
} |
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.
🛠️ Refactor suggestion
Enhance OTP security implementation.
The OTP implementation should include:
- Rate limiting for OTP attempts
- OTP cleanup mechanism
- Secure storage of OTP hashes
// Add rate limiting
private async checkRateLimit(identifier: string): Promise<void> {
const attempts = await this.getAttempts(identifier);
if (attempts >= MAX_ATTEMPTS) {
throw new TooManyAttemptsException();
}
}
// Add OTP cleanup
private async cleanupExpiredOTPs(): Promise<void> {
// Implementation to clean up expired OTPs
// This should be called periodically
}
🧰 Tools
🪛 Biome (1.9.4)
[error] 2000-2000: isNaN is unsafe. It attempts a type coercion. Use Number.isNaN instead.
See the MDN documentation for more details.
Unsafe fix: Use Number.isNaN instead.
(lint/suspicious/noGlobalIsNan)
🪛 eslint
[error] 1900-1900: Replace mobile,·reason
with ⏎········mobile,⏎········reason⏎······
(prettier/prettier)
[error] 1906-1906: Insert ,
(prettier/prettier)
[error] 1908-1908: Insert ,
(prettier/prettier)
[error] 1917-1918: Delete ⏎···
(prettier/prettier)
[error] 1939-1939: Replace mobileWithCode,·otp,·reason
with ⏎········mobileWithCode,⏎········otp,⏎········reason⏎······
(prettier/prettier)
[error] 1942-1942: Insert ,
(prettier/prettier)
[error] 1945-1945: Replace "OTP",·"SEND_OTP",·replacements,·[mobile]
with ⏎········"OTP",⏎········"SEND_OTP",⏎········replacements,⏎········[mobile]⏎······
(prettier/prettier)
[error] 1947-1948: Delete ⏎···
(prettier/prettier)
[error] 1968-1968: Replace 'signup'
with "signup"
(prettier/prettier)
[error] 1979-1979: Replace 'forgot'
with "forgot"
(prettier/prettier)
[error] 1999-1999: Replace '.'
with "."
(prettier/prettier)
PS-3610: User Search with Custom Combinations for Learner Creation
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.
Actionable comments posted: 3
🧹 Nitpick comments (3)
src/adapters/userservicelocator.ts (1)
Line range hint
1-47
: Improve interface organization.Consider grouping related methods together for better maintainability:
- User CRUD operations
- Authentication/Password operations
- OTP operations
- Tenant/Role operations
export interface IServicelocator { // User CRUD operations createUser(...); updateUser(...); deleteUserById(...); // Authentication/Password operations resetUserPassword(...); forgotPassword(...); // OTP operations sendOtp(...); verifyOtp(...); sendPasswordResetOTP(...); // Tenant/Role operations checkUser(...); findUserRoles(...); }🧰 Tools
🪛 ESLint
[error] 21-21: Replace
request:·any,·userDto:·UserCreateDto,·academicYearId:·string,·response:·Response
with⏎····request:·any,⏎····userDto:·UserCreateDto,⏎····academicYearId:·string,⏎····response:·Response⏎··
(prettier/prettier)
[error] 37-37: Replace
request:·any,·username:·string,·redirectUrl:·string,·response:·Response
with⏎····request:·any,⏎····username:·string,⏎····redirectUrl:·string,⏎····response:·Response⏎··
(prettier/prettier)
[error] 41-41: Replace
body:·SendPasswordResetOTPDto,·response:·Response
with⏎····body:·SendPasswordResetOTPDto,⏎····response:·Response⏎··
(prettier/prettier)
src/adapters/postgres/user-adapter.ts (2)
1856-1858
: Improve mobile number formatting.The mobile number formatting is hardcoded for a specific country code (+91). Consider making it configurable:
private formatMobileNumber(mobile: string): string { const countryCode = this.configService.get<string>('COUNTRY_CODE') || '+91'; return `${countryCode}${mobile}`; }
Line range hint
1-2272
: Consider breaking down the large service class.The
PostgresUserService
class is too large (2000+ lines) and handles multiple responsibilities. Consider splitting it into smaller, focused services:
- UserAuthenticationService
- OtpService
- UserProfileService
- UserTenantService
This will improve maintainability, testability, and adherence to the Single Responsibility Principle.
🧰 Tools
🪛 Biome (1.9.4)
[error] 689-689: Avoid the delete operator which can impact performance.
Unsafe fix: Use an undefined assignment instead.
(lint/performance/noDelete)
[error] 1261-1261: Unsafe usage of optional chaining.
If it short-circuits with 'undefined' the evaluation will throw TypeError here:
(lint/correctness/noUnsafeOptionalChaining)
[error] 1147-1147: Avoid the use of spread (
...
) syntax on accumulators.Spread syntax should be avoided on accumulators (like those in
.reduce
) because it causes a time complexity ofO(n^2)
.
Consider methods such as .splice or .push instead.(lint/performance/noAccumulatingSpread)
🪛 ESLint
[error] 779-779: Replace
·where:·{·userId:·userDto.userId·}
with⏎········where:·{·userId:·userDto.userId·},⏎·····
(prettier/prettier)
[error] 795-795: Replace
userDto.userData.deviceId,·userDto.userId,·user.deviceId)
with⏎············userDto.userData.deviceId,⏎············userDto.userId,⏎············user.deviceId⏎··········);
(prettier/prettier)
[error] 796-797: Delete
⏎
(prettier/prettier)
[error] 800-800: Replace
userDto.userData.deviceId,·userDto.userId,·user.deviceId)
with⏎············userDto.userData.deviceId,⏎············userDto.userId,⏎············user.deviceId⏎··········);
(prettier/prettier)
[error] 812-812: Replace
keycloakReqBody
with⏎············keycloakReqBody⏎··········
(prettier/prettier)
[error] 814-814: Replace
'exists'
with"exists"
(prettier/prettier)
[error] 931-931: Replace
updateField:·UpdateField):·Promise<'exists'
with⏎····updateField:·UpdateField⏎··):·Promise<"exists"
(prettier/prettier)
[error] 932-933: Delete
⏎
(prettier/prettier)
[error] 938-938: Replace
updateField
with⏎········updateField⏎······
(prettier/prettier)
[error] 940-940: Replace
'exists'
with"exists"
(prettier/prettier)
[error] 944-944: 'updateResult' is never reassigned. Use 'const' instead.
(prefer-const)
[error] 944-944: Insert
;
(prettier/prettier)
[error] 948-949: Delete
⏎
(prettier/prettier)
[error] 953-953: Delete
,
(prettier/prettier)
[error] 959-959: Replace
userDeviceId:·string,·userId:·string,·existingDeviceId:·string[]
with⏎····userDeviceId:·string,⏎····userId:·string,⏎····existingDeviceId:·string[]⏎··
(prettier/prettier)
[error] 960-960: 'deviceIds' is never reassigned. Use 'const' instead.
(prefer-const)
[error] 974-974: Replace
deviceIdforRemove:·string,·userId:·string,·existingDeviceId:·string[]
with⏎····deviceIdforRemove:·string,⏎····userId:·string,⏎····existingDeviceId:·string[]⏎··
(prettier/prettier)
[error] 981-981: Replace
id
with(id)
(prettier/prettier)
[error] 985-985: Replace
userId:·string,·userData:·Partial<User>
with⏎····userId:·string,⏎····userData:·Partial<User>⏎··
(prettier/prettier)
[error] 995-996: Delete
⏎
(prettier/prettier)
[error] 999-999: Replace
'An·error·occurred·while·updating·user·details'
with"An·error·occurred·while·updating·user·details"
(prettier/prettier)
[error] 1001-1002: Delete
⏎
(prettier/prettier)
[error] 1062-1062: 'errKeycloak' is never reassigned. Use 'const' instead.
(prefer-const)
[error] 1081-1081: 'resKeycloak' is never reassigned. Use 'const' instead.
(prefer-const)
[error] 1081-1082: Replace
⏎
with;
(prettier/prettier)
[error] 1107-1108: Delete
⏎
(prettier/prettier)
[error] 1147-1147: Insert
··
(prettier/prettier)
[error] 1148-1148: Insert
··
(prettier/prettier)
[error] 1149-1149: Insert
··
(prettier/prettier)
[error] 1346-1347: Delete
⏎
(prettier/prettier)
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
-
src/adapters/postgres/user-adapter.ts
(9 hunks) -
src/adapters/userservicelocator.ts
(2 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
src/adapters/postgres/user-adapter.ts (1)
Pattern **/*.ts
: "Review the JavaScript code for conformity with the Google JavaScript style guide, highlighting any deviations. Ensure that:
- The code adheres to best practices associated with nodejs.
- The code adheres to best practices associated with nestjs framework.
- The code adheres to best practices recommended for performance.
- The code adheres to similar naming conventions for controllers, models, services, methods, variables."
src/adapters/userservicelocator.ts (1)
Pattern **/*.ts
: "Review the JavaScript code for conformity with the Google JavaScript style guide, highlighting any deviations. Ensure that:
- The code adheres to best practices associated with nodejs.
- The code adheres to best practices associated with nestjs framework.
- The code adheres to best practices recommended for performance.
- The code adheres to similar naming conventions for controllers, models, services, methods, variables."
🪛 Biome (1.9.4)
src/adapters/postgres/user-adapter.ts
[error] 574-574: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
[error] 607-607: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
[error] 689-689: Avoid the delete operator which can impact performance.
Unsafe fix: Use an undefined assignment instead.
(lint/performance/noDelete)
[error] 1261-1261: Unsafe usage of optional chaining.
If it short-circuits with 'undefined' the evaluation will throw TypeError here:
(lint/correctness/noUnsafeOptionalChaining)
[error] 1147-1147: Avoid the use of spread (...
) syntax on accumulators.
Spread syntax should be avoided on accumulators (like those in .reduce
) because it causes a time complexity of O(n^2)
.
Consider methods such as .splice or .push instead.
(lint/performance/noAccumulatingSpread)
[error] 1738-1738: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
[error] 1985-1985: isNaN is unsafe. It attempts a type coercion. Use Number.isNaN instead.
See the MDN documentation for more details.
Unsafe fix: Use Number.isNaN instead.
(lint/suspicious/noGlobalIsNan)
🪛 ESLint
src/adapters/postgres/user-adapter.ts
[error] 97-97: Replace 'OTP_EXPIRY'
with "OTP_EXPIRY"
(prettier/prettier)
[error] 98-98: Replace 'OTP_DIGITS'
with "OTP_DIGITS"
(prettier/prettier)
[error] 99-99: Replace 'SMS_KEY'
with "SMS_KEY"
(prettier/prettier)
[error] 385-385: 'filters' is never reassigned. Use 'const' instead.
(prefer-const)
[error] 385-385: 'exclude' is never reassigned. Use 'const' instead.
(prefer-const)
[error] 385-385: 'sort' is never reassigned. Use 'const' instead.
(prefer-const)
[error] 478-478: Insert ⏎········
(prettier/prettier)
[error] 479-479: Delete ··
(prettier/prettier)
[error] 779-779: Replace ·where:·{·userId:·userDto.userId·}
with ⏎········where:·{·userId:·userDto.userId·},⏎·····
(prettier/prettier)
[error] 795-795: Replace userDto.userData.deviceId,·userDto.userId,·user.deviceId)
with ⏎············userDto.userData.deviceId,⏎············userDto.userId,⏎············user.deviceId⏎··········);
(prettier/prettier)
[error] 796-797: Delete ⏎
(prettier/prettier)
[error] 800-800: Replace userDto.userData.deviceId,·userDto.userId,·user.deviceId)
with ⏎············userDto.userData.deviceId,⏎············userDto.userId,⏎············user.deviceId⏎··········);
(prettier/prettier)
[error] 812-812: Replace keycloakReqBody
with ⏎············keycloakReqBody⏎··········
(prettier/prettier)
[error] 814-814: Replace 'exists'
with "exists"
(prettier/prettier)
[error] 931-931: Replace updateField:·UpdateField):·Promise<'exists'
with ⏎····updateField:·UpdateField⏎··):·Promise<"exists"
(prettier/prettier)
[error] 932-933: Delete ⏎
(prettier/prettier)
[error] 938-938: Replace updateField
with ⏎········updateField⏎······
(prettier/prettier)
[error] 940-940: Replace 'exists'
with "exists"
(prettier/prettier)
[error] 944-944: 'updateResult' is never reassigned. Use 'const' instead.
(prefer-const)
[error] 944-944: Insert ;
(prettier/prettier)
[error] 948-949: Delete ⏎
(prettier/prettier)
[error] 953-953: Delete ,
(prettier/prettier)
[error] 959-959: Replace userDeviceId:·string,·userId:·string,·existingDeviceId:·string[]
with ⏎····userDeviceId:·string,⏎····userId:·string,⏎····existingDeviceId:·string[]⏎··
(prettier/prettier)
[error] 960-960: 'deviceIds' is never reassigned. Use 'const' instead.
(prefer-const)
[error] 974-974: Replace deviceIdforRemove:·string,·userId:·string,·existingDeviceId:·string[]
with ⏎····deviceIdforRemove:·string,⏎····userId:·string,⏎····existingDeviceId:·string[]⏎··
(prettier/prettier)
[error] 981-981: Replace id
with (id)
(prettier/prettier)
[error] 985-985: Replace userId:·string,·userData:·Partial<User>
with ⏎····userId:·string,⏎····userData:·Partial<User>⏎··
(prettier/prettier)
[error] 995-996: Delete ⏎
(prettier/prettier)
[error] 999-999: Replace 'An·error·occurred·while·updating·user·details'
with "An·error·occurred·while·updating·user·details"
(prettier/prettier)
[error] 1001-1002: Delete ⏎
(prettier/prettier)
[error] 1062-1062: 'errKeycloak' is never reassigned. Use 'const' instead.
(prefer-const)
[error] 1081-1081: 'resKeycloak' is never reassigned. Use 'const' instead.
(prefer-const)
[error] 1081-1082: Replace ⏎
with ;
(prettier/prettier)
[error] 1107-1108: Delete ⏎
(prettier/prettier)
[error] 1147-1147: Insert ··
(prettier/prettier)
[error] 1148-1148: Insert ··
(prettier/prettier)
[error] 1149-1149: Insert ··
(prettier/prettier)
[error] 1346-1347: Delete ⏎
(prettier/prettier)
[error] 1380-1380: Replace user.userId·=·userCreateDto?.userId
with (user.userId·=·userCreateDto?.userId)
(prettier/prettier)
[error] 1381-1381: Replace user.username·=·userCreateDto?.username
with (user.username·=·userCreateDto?.username)
(prettier/prettier)
[error] 1382-1382: Replace user.firstName·=·userCreateDto?.firstName
with (user.firstName·=·userCreateDto?.firstName)
(prettier/prettier)
[error] 1383-1383: Replace user.middleName·=·userCreateDto?.middleName
with (user.middleName·=·userCreateDto?.middleName)
(prettier/prettier)
[error] 1384-1384: Replace user.lastName·=·userCreateDto?.lastName
with (user.lastName·=·userCreateDto?.lastName)
(prettier/prettier)
[error] 1385-1385: Replace user.gender·=·userCreateDto?.gender
with (user.gender·=·userCreateDto?.gender)
(prettier/prettier)
[error] 1386-1386: Replace user.email·=·userCreateDto?.email
with (user.email·=·userCreateDto?.email)
(prettier/prettier)
[error] 1387-1387: Replace user.mobile·=·Number(userCreateDto?.mobile)·||·null
with (user.mobile·=·Number(userCreateDto?.mobile)·||·null)
(prettier/prettier)
[error] 1388-1388: Replace user.createdBy·=·userCreateDto?.createdBy·||·userCreateDto?.createdBy
with (user.createdBy·=·userCreateDto?.createdBy·||·userCreateDto?.createdBy)
(prettier/prettier)
[error] 1399-1399: 'query' is never reassigned. Use 'const' instead.
(prefer-const)
[error] 1401-1401: 'getCohortAcademicYearId' is never reassigned. Use 'const' instead.
(prefer-const)
[error] 1406-1406: 'cohortData' is never reassigned. Use 'const' instead.
(prefer-const)
[error] 1633-1633: Insert ··
(prettier/prettier)
[error] 1740-1740: Insert ··
(prettier/prettier)
[error] 1741-1741: Insert ··
(prettier/prettier)
[error] 1861-1861: Replace mobileOrUsername:·string,·otp:·string,·reason:·string
with ⏎····mobileOrUsername:·string,⏎····otp:·string,⏎····reason:·string⏎··
(prettier/prettier)
[error] 1885-1885: Replace mobile,·reason
with ⏎········mobile,⏎········reason⏎······
(prettier/prettier)
[error] 1891-1891: Insert ,
(prettier/prettier)
[error] 1893-1893: Insert ,
(prettier/prettier)
[error] 1902-1903: Delete ⏎···
(prettier/prettier)
[error] 1924-1924: Replace mobileWithCode,·otp,·reason
with ⏎········mobileWithCode,⏎········otp,⏎········reason⏎······
(prettier/prettier)
[error] 1927-1927: Insert ,
(prettier/prettier)
[error] 1930-1930: Replace "OTP",·"SEND_OTP",·replacements,·[mobile]
with ⏎········"OTP",⏎········"SEND_OTP",⏎········replacements,⏎········[mobile]⏎······
(prettier/prettier)
[error] 1932-1933: Delete ⏎···
(prettier/prettier)
[error] 1953-1953: Replace 'signup'
with "signup"
(prettier/prettier)
[error] 1964-1964: Replace 'forgot'
with "forgot"
(prettier/prettier)
[error] 1984-1984: Replace '.'
with "."
(prettier/prettier)
[error] 2041-2041: Replace context:·string,·key:·string,·replacements:·object,·receipients:·string[]
with ⏎····context:·string,⏎····key:·string,⏎····replacements:·object,⏎····receipients:·string[]⏎··
(prettier/prettier)
[error] 2058-2058: Replace mailSend?.result?.sms?.errors·&&·mailSend.result.sms.errors.length·>·0
with ⏎········mailSend?.result?.sms?.errors·&&⏎········mailSend.result.sms.errors.length·>·0⏎······
(prettier/prettier)
[error] 2059-2059: Replace (error:·{·error:·string;·})·=>·error.error
with ⏎··········(error:·{·error:·string·})·=>·error.error⏎········
(prettier/prettier)
[error] 2064-2065: Delete ⏎···
(prettier/prettier)
[error] 2067-2067: Replace ${API_RESPONSES.SMS_NOTIFICATION_ERROR}:··${error.message}
with ⏎········
${API_RESPONSES.SMS_NOTIFICATION_ERROR}:··${error.message}⏎······
(prettier/prettier)
[error] 2072-2072: Replace body:·SendPasswordResetOTPDto,·response:·Response
with ⏎····body:·SendPasswordResetOTPDto,⏎····response:·Response⏎··
(prettier/prettier)
[error] 2076-2076: 'error' is never reassigned. Use 'const' instead.
(prefer-const)
[error] 2077-2077: 'success' is never reassigned. Use 'const' instead.
(prefer-const)
[error] 2098-2098: Replace ''
with ""
(prettier/prettier)
[error] 2101-2101: Replace username,·otp,·reason
with ⏎········username,⏎········otp,⏎········reason⏎······
(prettier/prettier)
[error] 2105-2105: Insert ,
(prettier/prettier)
[error] 2108-2108: Replace userData.mobile
with ⏎············userData.mobile,⏎··········
(prettier/prettier)
[error] 2109-2109: Replace 'SMS'
with "SMS"
(prettier/prettier)
[error] 2111-2111: Replace ·type:·'SMS',·message:·
${API_RESPONSES.MOBILE_OTP_SEND_FAILED}·${e.message}·})
with ⏎············type:·"SMS",⏎············message:·
${API_RESPONSES.MOBILE_OTP_SEND_FAILED}·${e.message},⏎··········});
(prettier/prettier)
[error] 2119-2119: Insert ,
(prettier/prettier)
[error] 2122-2122: Replace userData.email
with ⏎············userData.email,⏎··········
(prettier/prettier)
[error] 2123-2123: Replace ·type:·'Email',·message:·API_RESPONSES.EMAIL_SENT_OTP·})
with ⏎············type:·"Email",⏎············message:·API_RESPONSES.EMAIL_SENT_OTP,⏎··········});
(prettier/prettier)
[error] 2125-2125: Replace ·type:·'Email',·message:·
${API_RESPONSES.EMAIL_OTP_SEND_FAILED}:·${e.message}·})
with ⏎············type:·"Email",⏎············message:·
${API_RESPONSES.EMAIL_OTP_SEND_FAILED}:·${e.message},⏎··········});
(prettier/prettier)
[error] 2128-2128: Delete ·
(prettier/prettier)
[error] 2129-2129: Insert ⏎·······
(prettier/prettier)
[error] 2130-2130: Replace ''
with ""
(prettier/prettier)
[error] 2131-2131: Replace e·=>·e.type·===·'SMS'
with (e)·=>·e.type·===·"SMS"
(prettier/prettier)
[error] 2132-2132: Replace .filter(e·=>·e.type·===·'SMS').map(e·=>·e.message)
with ⏎············.filter((e)·=>·e.type·===·"SMS")⏎············.map((e)·=>·e.message)⏎············
(prettier/prettier)
[error] 2134-2134: Replace (e·=>·e.type·===·'Email'
with ((e)·=>·e.type·===·"Email"
(prettier/prettier)
[error] 2135-2135: Replace .filter(e·=>·e.type·===·'Email').map(e·=>·e.message)
with ⏎············.filter((e)·=>·e.type·===·"Email")⏎············.map((e)·=>·e.message)⏎············
(prettier/prettier)
[error] 2149-2149: Insert ,
(prettier/prettier)
[error] 2150-2150: Insert ;
(prettier/prettier)
[error] 2158-2159: Delete ⏎···
(prettier/prettier)
[error] 2167-2168: Delete ⏎
(prettier/prettier)
[error] 2172-2172: Replace context:·string,·key:·string,·replacements:·object,·emailReceipt
with ⏎····context:·string,⏎····key:·string,⏎····replacements:·object,⏎····emailReceipt⏎··
(prettier/prettier)
[error] 2187-2187: Replace mailSend?.result?.email?.errors·&&·mailSend.result.email.errors.length·>·0
with ⏎········mailSend?.result?.email?.errors·&&⏎········mailSend.result.email.errors.length·>·0⏎······
(prettier/prettier)
[error] 2188-2188: Replace (error:·{·error:·string;·})·=>·error.error
with ⏎··········(error:·{·error:·string·})·=>·error.error⏎········
(prettier/prettier)
[error] 2193-2194: Delete ⏎···
(prettier/prettier)
[error] 2196-2196: Replace ${API_RESPONSES.EMAIL_NOTIFICATION_ERROR}:··${e.message}
with ⏎········
${API_RESPONSES.EMAIL_NOTIFICATION_ERROR}:··${e.message}⏎······
(prettier/prettier)
[error] 2198-2199: Delete ⏎
(prettier/prettier)
[error] 2201-2205: Replace ⏎····request:·any,⏎····response:·any,⏎····filters:·ExistUserDto⏎··
with request:·any,·response:·any,·filters:·ExistUserDto
(prettier/prettier)
[error] 2213-2213: Replace key·===·'firstName'·||·key·===·'middleName'·||·key·===·'lastName'
with ⏎··············key·===·"firstName"·||⏎··············key·===·"middleName"·||⏎··············key·===·"lastName"⏎············
(prettier/prettier)
[error] 2222-2223: Delete ······⏎··
(prettier/prettier)
[error] 2227-2227: Replace 'username',·'firstName',·'middleName',·'lastName'
with "username",·"firstName",·"middleName",·"lastName"
(prettier/prettier)
[error] 2265-2265: Replace 'string'
with "string"
(prettier/prettier)
[error] 2267-2267: Replace '\\$&'
with "\\$&"
(prettier/prettier)
[error] 2271-2273: Delete ⏎··⏎
(prettier/prettier)
src/adapters/userservicelocator.ts
[error] 21-21: Replace request:·any,·userDto:·UserCreateDto,·academicYearId:·string,·response:·Response
with ⏎····request:·any,⏎····userDto:·UserCreateDto,⏎····academicYearId:·string,⏎····response:·Response⏎··
(prettier/prettier)
[error] 37-37: Replace request:·any,·username:·string,·redirectUrl:·string,·response:·Response
with ⏎····request:·any,⏎····username:·string,⏎····redirectUrl:·string,⏎····response:·Response⏎··
(prettier/prettier)
[error] 41-41: Replace body:·SendPasswordResetOTPDto,·response:·Response
with ⏎····body:·SendPasswordResetOTPDto,⏎····response:·Response⏎··
(prettier/prettier)
🔇 Additional comments (5)
src/adapters/userservicelocator.ts (2)
37-38
: Add request body type definition.The
forgotPassword
method usesany
type for the body parameter, which could lead to runtime errors.🧰 Tools
🪛 ESLint
[error] 37-37: Replace
request:·any,·username:·string,·redirectUrl:·string,·response:·Response
with⏎····request:·any,⏎····username:·string,⏎····redirectUrl:·string,⏎····response:·Response⏎··
(prettier/prettier)
39-41
: Add rate limiting for OTP endpoints.The OTP-related methods (
sendOtp
,verifyOtp
,sendPasswordResetOTP
) should include rate limiting to prevent abuse.🧰 Tools
🪛 ESLint
[error] 41-41: Replace
body:·SendPasswordResetOTPDto,·response:·Response
with⏎····body:·SendPasswordResetOTPDto,⏎····response:·Response⏎··
(prettier/prettier)
src/adapters/postgres/user-adapter.ts (3)
1399-1403
: Fix SQL injection vulnerability.The query string is constructed using string interpolation, which is vulnerable to SQL injection attacks.
🧰 Tools
🪛 ESLint
[error] 1399-1399: 'query' is never reassigned. Use 'const' instead.
(prefer-const)
[error] 1401-1401: 'getCohortAcademicYearId' is never reassigned. Use 'const' instead.
(prefer-const)
1379-1388
: Fix object property assignments.The properties of the
user
object are being assigned using commas, which can lead to unexpected behavior.🧰 Tools
🪛 ESLint
[error] 1380-1380: Replace
user.userId·=·userCreateDto?.userId
with(user.userId·=·userCreateDto?.userId)
(prettier/prettier)
[error] 1381-1381: Replace
user.username·=·userCreateDto?.username
with(user.username·=·userCreateDto?.username)
(prettier/prettier)
[error] 1382-1382: Replace
user.firstName·=·userCreateDto?.firstName
with(user.firstName·=·userCreateDto?.firstName)
(prettier/prettier)
[error] 1383-1383: Replace
user.middleName·=·userCreateDto?.middleName
with(user.middleName·=·userCreateDto?.middleName)
(prettier/prettier)
[error] 1384-1384: Replace
user.lastName·=·userCreateDto?.lastName
with(user.lastName·=·userCreateDto?.lastName)
(prettier/prettier)
[error] 1385-1385: Replace
user.gender·=·userCreateDto?.gender
with(user.gender·=·userCreateDto?.gender)
(prettier/prettier)
[error] 1386-1386: Replace
user.email·=·userCreateDto?.email
with(user.email·=·userCreateDto?.email)
(prettier/prettier)
[error] 1387-1387: Replace
user.mobile·=·Number(userCreateDto?.mobile)·||·null
with(user.mobile·=·Number(userCreateDto?.mobile)·||·null)
(prettier/prettier)
[error] 1388-1388: Replace
user.createdBy·=·userCreateDto?.createdBy·||·userCreateDto?.createdBy
with(user.createdBy·=·userCreateDto?.createdBy·||·userCreateDto?.createdBy)
(prettier/prettier)
1984-1993
: 🛠️ Refactor suggestionUse Number.isNaN instead of isNaN.
The global
isNaN
function performs type coercion which can lead to unexpected results.- if (!hashValue || !expires || isNaN(parseInt(expires))) { + if (!hashValue || !expires || Number.isNaN(parseInt(expires))) {Likely invalid or redundant comment.
🧰 Tools
🪛 Biome (1.9.4)
[error] 1985-1985: isNaN is unsafe. It attempts a type coercion. Use Number.isNaN instead.
See the MDN documentation for more details.
Unsafe fix: Use Number.isNaN instead.(lint/suspicious/noGlobalIsNan)
🪛 ESLint
[error] 1984-1984: Replace
'.'
with"."
(prettier/prettier)
getUsersDetailsById(userData: UserData, response: any); | ||
updateUser(userDto?: UserUpdateDTO, response?: Response): Promise<void>; |
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.
🛠️ Refactor suggestion
Improve type safety and consistency in method signatures.
The method signatures have inconsistent return type declarations and use of any
types:
- getUsersDetailsById(userData: UserData, response: any);
- updateUser(userDto?: UserUpdateDTO, response?: Response): Promise<void>;
- createUser(request: any, userDto: UserCreateDto, academicYearId: string, response: Response);
+ getUsersDetailsById(userData: UserData, response: Response): Promise<void>;
+ updateUser(userDto: UserUpdateDTO, response: Response): Promise<void>;
+ createUser(request: Request, userDto: UserCreateDto, academicYearId: string, response: Response): Promise<void>;
Also applies to: 21-22
const apiId = APIID.VERIFY_OTP; | ||
try { | ||
const { mobile, otp, hash, reason, username } = body; | ||
if (!otp || !hash || !reason) { | ||
return APIResponse.error( | ||
response, | ||
apiId, | ||
API_RESPONSES.BAD_REQUEST, | ||
API_RESPONSES.OTP_VALIDED_REQUIRED_KEY, | ||
HttpStatus.BAD_REQUEST | ||
); | ||
} | ||
// Determine the value to use for verification based on the reason | ||
let identifier: string; | ||
if (reason === 'signup') { | ||
if (!mobile) { | ||
return APIResponse.error( | ||
response, | ||
apiId, | ||
API_RESPONSES.BAD_REQUEST, | ||
API_RESPONSES.MOBILE_REQUIRED, | ||
HttpStatus.BAD_REQUEST | ||
); | ||
} | ||
identifier = this.formatMobileNumber(mobile); // Assuming this formats the mobile number | ||
} else if (reason === 'forgot') { | ||
if (!username) { | ||
return APIResponse.error( | ||
response, | ||
apiId, | ||
API_RESPONSES.BAD_REQUEST, | ||
API_RESPONSES.USERNAME_REQUIRED, | ||
HttpStatus.BAD_REQUEST | ||
); | ||
} | ||
identifier = username; | ||
} else { | ||
return APIResponse.error( | ||
response, | ||
apiId, | ||
API_RESPONSES.BAD_REQUEST, | ||
API_RESPONSES.INVALID_REASON, | ||
HttpStatus.BAD_REQUEST | ||
); | ||
} | ||
const [hashValue, expires] = hash.split('.'); | ||
if (!hashValue || !expires || isNaN(parseInt(expires))) { | ||
return APIResponse.error( | ||
response, | ||
apiId, | ||
API_RESPONSES.BAD_REQUEST, | ||
API_RESPONSES.INVALID_HASH_FORMAT, | ||
HttpStatus.BAD_REQUEST | ||
); | ||
} | ||
|
||
if (Date.now() > parseInt(expires)) { | ||
return APIResponse.error( | ||
response, | ||
apiId, | ||
API_RESPONSES.OTP_EXPIRED, | ||
API_RESPONSES.OTP_EXPIRED, | ||
HttpStatus.BAD_REQUEST | ||
); | ||
} | ||
const data = `${identifier}.${otp}.${reason}.${expires}`; | ||
const calculatedHash = this.authUtils.calculateHash(data, this.smsKey); // Create hash | ||
|
||
if (calculatedHash === hashValue) { | ||
return await APIResponse.success( | ||
response, | ||
apiId, | ||
{ success: true }, | ||
HttpStatus.OK, | ||
API_RESPONSES.OTP_VALID | ||
); | ||
} else { | ||
return APIResponse.error( | ||
response, | ||
apiId, | ||
API_RESPONSES.OTP_INVALID, | ||
API_RESPONSES.OTP_INVALID, | ||
HttpStatus.BAD_REQUEST | ||
); | ||
} | ||
} catch (e) { | ||
LoggerUtil.error( | ||
`${API_RESPONSES.SERVER_ERROR}`, | ||
`Error during OTP verification: ${e.message}`, | ||
apiId | ||
); | ||
return APIResponse.error( | ||
response, | ||
apiId, | ||
API_RESPONSES.SERVER_ERROR, | ||
`Error : ${e?.message}`, | ||
HttpStatus.INTERNAL_SERVER_ERROR | ||
); | ||
} | ||
} |
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.
🛠️ Refactor suggestion
Enhance OTP verification security.
The OTP verification implementation needs additional security measures:
- Rate limiting for verification attempts
- Prevention of timing attacks
- Secure comparison of hashes
private async validateOtpAttempts(identifier: string): Promise<void> {
const attempts = await this.getVerificationAttempts(identifier);
if (attempts >= MAX_VERIFY_ATTEMPTS) {
throw new TooManyAttemptsException();
}
}
private compareHashes(hash1: string, hash2: string): boolean {
return crypto.timingSafeEqual(
Buffer.from(hash1, 'utf8'),
Buffer.from(hash2, 'utf8')
);
}
🧰 Tools
🪛 Biome (1.9.4)
[error] 1985-1985: isNaN is unsafe. It attempts a type coercion. Use Number.isNaN instead.
See the MDN documentation for more details.
Unsafe fix: Use Number.isNaN instead.
(lint/suspicious/noGlobalIsNan)
🪛 ESLint
[error] 1885-1885: Replace mobile,·reason
with ⏎········mobile,⏎········reason⏎······
(prettier/prettier)
[error] 1891-1891: Insert ,
(prettier/prettier)
[error] 1893-1893: Insert ,
(prettier/prettier)
[error] 1902-1903: Delete ⏎···
(prettier/prettier)
[error] 1924-1924: Replace mobileWithCode,·otp,·reason
with ⏎········mobileWithCode,⏎········otp,⏎········reason⏎······
(prettier/prettier)
[error] 1927-1927: Insert ,
(prettier/prettier)
[error] 1930-1930: Replace "OTP",·"SEND_OTP",·replacements,·[mobile]
with ⏎········"OTP",⏎········"SEND_OTP",⏎········replacements,⏎········[mobile]⏎······
(prettier/prettier)
[error] 1932-1933: Delete ⏎···
(prettier/prettier)
[error] 1953-1953: Replace 'signup'
with "signup"
(prettier/prettier)
[error] 1964-1964: Replace 'forgot'
with "forgot"
(prettier/prettier)
[error] 1984-1984: Replace '.'
with "."
(prettier/prettier)
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.
Actionable comments posted: 7
♻️ Duplicate comments (4)
src/adapters/userservicelocator.ts (1)
39-39
: 🛠️ Refactor suggestionDefine request body types for better type safety.
The
forgotPassword
method usesany
type for the body parameter.-forgotPassword(request: any, body: any, response: Response); +forgotPassword(request: Request, body: { token: string; newPassword: string }, response: Response): Promise<void>;src/adapters/postgres/user-adapter.ts (3)
1400-1404
:⚠️ Potential issueFix SQL injection vulnerability.
The query string is constructed using string interpolation, which is vulnerable to SQL injection attacks.
-let query = `SELECT * FROM public."CohortAcademicYear" WHERE "cohortId"= '${cohortIds}' AND "academicYearId" = '${academicYearId}'`; -let getCohortAcademicYearId = await this.usersRepository.query(query); +const query = `SELECT * FROM public."CohortAcademicYear" WHERE "cohortId" = $1 AND "academicYearId" = $2`; +const getCohortAcademicYearId = await this.usersRepository.query(query, [cohortIds, academicYearId]);🧰 Tools
🪛 ESLint
[error] 1400-1400: 'query' is never reassigned. Use 'const' instead.
(prefer-const)
[error] 1402-1402: 'getCohortAcademicYearId' is never reassigned. Use 'const' instead.
(prefer-const)
1381-1389
: 🛠️ Refactor suggestionUse object property assignments instead of comma-separated expressions.
The current syntax for assigning object properties is error-prone.
-user.userId = userCreateDto?.userId, -user.username = userCreateDto?.username, -user.firstName = userCreateDto?.firstName, -user.middleName = userCreateDto?.middleName, -user.lastName = userCreateDto?.lastName, -user.gender = userCreateDto?.gender, -user.email = userCreateDto?.email, -user.mobile = Number(userCreateDto?.mobile) || null, -user.createdBy = userCreateDto?.createdBy || userCreateDto?.createdBy; +Object.assign(user, { + userId: userCreateDto?.userId, + username: userCreateDto?.username, + firstName: userCreateDto?.firstName, + middleName: userCreateDto?.middleName, + lastName: userCreateDto?.lastName, + gender: userCreateDto?.gender, + email: userCreateDto?.email, + mobile: Number(userCreateDto?.mobile) || null, + createdBy: userCreateDto?.createdBy +});🧰 Tools
🪛 ESLint
[error] 1381-1381: Replace
user.userId·=·userCreateDto?.userId
with(user.userId·=·userCreateDto?.userId)
(prettier/prettier)
[error] 1382-1382: Replace
user.username·=·userCreateDto?.username
with(user.username·=·userCreateDto?.username)
(prettier/prettier)
[error] 1383-1383: Replace
user.firstName·=·userCreateDto?.firstName
with(user.firstName·=·userCreateDto?.firstName)
(prettier/prettier)
[error] 1384-1384: Replace
user.middleName·=·userCreateDto?.middleName
with(user.middleName·=·userCreateDto?.middleName)
(prettier/prettier)
[error] 1385-1385: Replace
user.lastName·=·userCreateDto?.lastName
with(user.lastName·=·userCreateDto?.lastName)
(prettier/prettier)
[error] 1386-1386: Replace
user.gender·=·userCreateDto?.gender
with(user.gender·=·userCreateDto?.gender)
(prettier/prettier)
[error] 1387-1387: Replace
user.email·=·userCreateDto?.email
with(user.email·=·userCreateDto?.email)
(prettier/prettier)
[error] 1388-1388: Replace
user.mobile·=·Number(userCreateDto?.mobile)·||·null
with(user.mobile·=·Number(userCreateDto?.mobile)·||·null)
(prettier/prettier)
[error] 1389-1389: Replace
user.createdBy·=·userCreateDto?.createdBy·||·userCreateDto?.createdBy
with(user.createdBy·=·userCreateDto?.createdBy·||·userCreateDto?.createdBy)
(prettier/prettier)
2263-2270
: 🛠️ Refactor suggestionEnhance input sanitization.
The current sanitization only escapes SQL special characters.
private sanitizeInput(value: unknown): string | unknown { if (typeof value === 'string') { return value .replace(/[<>]/g, '') // Remove < and > .replace(/javascript:/gi, '') // Remove javascript: protocol .replace(/on\w+=/gi, ''); // Remove event handlers } return value; }🧰 Tools
🪛 ESLint
[error] 2264-2264: Replace
'string'
with"string"
(prettier/prettier)
[error] 2266-2266: Replace
'\\$&'
with"\\$&"
(prettier/prettier)
🧹 Nitpick comments (11)
src/common/utils/api-id.config.ts (3)
46-51
: Inconsistent API path pattern for academic year endpoints.The academic year endpoints use inconsistent naming patterns:
api.academicyear.*
(one word)api.create.cohortAcademicYear
(camelCase with different structure)Consider standardizing to either:
api.academic-year.*
andapi.cohort-academic-year.*
(kebab-case)api.academicYear.*
andapi.cohortAcademicYear.*
(camelCase)
23-27
: Inconsistent search endpoint naming.The cohort member endpoints use
list
instead ofsearch
:COHORT_MEMBER_SEARCH: "api.cohortmember.list"
This is inconsistent with other search endpoints like
ROLE_SEARCH
,FIELDS_SEARCH
, etc.Consider renaming to maintain consistency:
- COHORT_MEMBER_SEARCH: "api.cohortmember.list", + COHORT_MEMBER_LIST: "api.cohortmember.list",
30-34
: Inconsistent read/get endpoint naming.The cohort endpoints use
read
instead ofget
:COHORT_READ: "api.cohort.read"
This is inconsistent with other similar endpoints that use
get
(e.g.,USER_GET
,ROLE_GET
).Consider standardizing to use
get
:- COHORT_READ: "api.cohort.read", + COHORT_GET: "api.cohort.get",src/adapters/postgres/fields-adapter.ts (2)
1422-1454
: Improve query construction in findDynamicOptionsThe method uses confusing string concatenation and assignments within expressions.
Refactor for better readability:
async findDynamicOptions(tableName, whereClause?: any, offset?: any, limit?: any, order?: any, optionName?: any) { const conditions = []; if (whereClause) { conditions.push(whereClause); } if (optionName) { conditions.push(`"name" ILike '%${optionName}%'`); } const wherePart = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : ''; const orderPart = order || ''; const offsetPart = offset ? `offset ${offset}` : ''; const limitPart = limit ? `limit ${limit}` : ''; const query = ` SELECT *, COUNT(*) OVER() AS total_count FROM public."${tableName}" ${wherePart} ${orderPart} ${offsetPart} ${limitPart} `.trim(); return await this.fieldsRepository.query(query); }🧰 Tools
🪛 Biome (1.9.4)
[error] 1434-1434: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
1-1772
: Consider splitting the large service classThe
PostgresFieldsService
class is too large and handles multiple responsibilities, making it harder to maintain and test.Consider splitting it into smaller, focused services:
- FieldsQueryService: For field-related queries
- FieldValuesService: For field values operations
- FieldValidationService: For field validation logic
- FieldOptionsService: For managing field options
This would improve:
- Code organization
- Testability
- Maintainability
- Single Responsibility Principle compliance
🧰 Tools
🪛 Biome (1.9.4)
[error] 438-438: Avoid the delete operator which can impact performance.
Unsafe fix: Use an undefined assignment instead.
(lint/performance/noDelete)
[error] 575-575: Avoid the delete operator which can impact performance.
Unsafe fix: Use an undefined assignment instead.
(lint/performance/noDelete)
[error] 1434-1434: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.(lint/suspicious/noAssignInExpressions)
[error] 1752-1752: Avoid the delete operator which can impact performance.
Unsafe fix: Use an undefined assignment instead.
(lint/performance/noDelete)
[error] 1753-1753: Avoid the delete operator which can impact performance.
Unsafe fix: Use an undefined assignment instead.
(lint/performance/noDelete)
🪛 ESLint
[error] 34-34: Delete
·
(prettier/prettier)
[error] 104-104: Insert
;
(prettier/prettier)
[error] 388-388: Insert
;
(prettier/prettier)
[error] 460-460: Insert
;
(prettier/prettier)
[error] 509-509: Replace
·fieldsData.fieldParams·&&
with⏎········fieldsData.fieldParams·&&⏎·······
(prettier/prettier)
[error] 635-635: Insert
;
(prettier/prettier)
[error] 670-673: Replace
⏎········
${API_RESPONSES.SERVER_ERROR},⏎········
Error:·${e.message},⏎······)
with ``${API_RESPONSES.SERVER_ERROR},·
Error:·${e.message}`);`(prettier/prettier)
[error] 758-758: Delete
····
(prettier/prettier)
[error] 769-772: Replace
⏎········
${API_RESPONSES.SERVER_ERROR},⏎········
Error:·${e.message},⏎······)
with ``${API_RESPONSES.SERVER_ERROR},·
Error:·${e.message}`);`(prettier/prettier)
[error] 793-793: 'tenantCond' is never reassigned. Use 'const' instead.
(prefer-const)
[error] 793-793: Replace
?·
"tenantId"·=·'${tenantId}'·:
"tenantId"·IS·NULL`` with⏎········?·
"tenantId"·=·'${tenantId}'`⏎········:·`"tenantId"·IS·NULL`;`(prettier/prettier)
[error] 840-840: Insert
;
(prettier/prettier)
[error] 903-903: Insert
;
(prettier/prettier)
[error] 950-950: Insert
;
(prettier/prettier)
[error] 1052-1055: Replace
⏎········
${API_RESPONSES.SERVER_ERROR},⏎········
Error:·${e.message},⏎······)
with ``${API_RESPONSES.SERVER_ERROR},·
Error:·${e.message}`);`(prettier/prettier)
[error] 1281-1284: Replace
⏎········
${API_RESPONSES.SERVER_ERROR},⏎········
Error:·${e.message},⏎······)
with ``${API_RESPONSES.SERVER_ERROR},·
Error:·${e.message}`);`(prettier/prettier)
[error] 1407-1410: Replace
⏎········
${API_RESPONSES.SERVER_ERROR},⏎········
Error:·${e.message},⏎······)
with ``${API_RESPONSES.SERVER_ERROR},·
Error:·${e.message}`);`(prettier/prettier)
[error] 1468-1468: Delete
··
(prettier/prettier)
[error] 1469-1469: Delete
··
(prettier/prettier)
[error] 1653-1656: Replace
⏎········
${API_RESPONSES.SERVER_ERROR},⏎········
Error:·${e.message},⏎······)
with ``${API_RESPONSES.SERVER_ERROR},·
Error:·${e.message}`);`(prettier/prettier)
src/common/utils/response.messages.ts (4)
1-189
: Improve code organization with TypeScript enums and message categorization.The current implementation using a plain object could benefit from TypeScript's type safety and organization features.
Consider restructuring the messages using TypeScript enums and nested objects for better organization and type safety:
-export const API_RESPONSES = { +export enum ApiResponseCategory { + AUTH = 'auth', + USER = 'user', + ACADEMIC_YEAR = 'academic_year', + COHORT = 'cohort', + TENANT = 'tenant', + OTP = 'otp' +} + +export const API_RESPONSES = { + [ApiResponseCategory.AUTH]: { + UNAUTHORIZED: "Unauthorized", + INVALID_TOKEN: "Token Invalid", + }, + [ApiResponseCategory.USER]: { + NOT_FOUND: "User does not exist", + UPDATED: "User updated successfully", + // ... other user-related messages + }, + // ... other categories +}🧰 Tools
🪛 ESLint
[error] 65-65: Insert
⏎···
(prettier/prettier)
[error] 66-66: Insert
⏎···
(prettier/prettier)
[error] 67-67: Insert
⏎···
(prettier/prettier)
[error] 68-68: Insert
⏎···
(prettier/prettier)
[error] 69-69: Insert
⏎···
(prettier/prettier)
[error] 70-70: Insert
⏎···
(prettier/prettier)
[error] 71-71: Insert
⏎···
(prettier/prettier)
[error] 72-72: Insert
⏎···
(prettier/prettier)
[error] 73-73: Replace
'Unauthorized'
with"Unauthorized"
(prettier/prettier)
[error] 74-74: Replace
'Token·Invalid'
with"Token·Invalid"
(prettier/prettier)
[error] 77-77: Replace
'User·updated·successfully.'
with"User·updated·successfully."
(prettier/prettier)
[error] 78-78: Replace
'User·does·not·exist.'
with"User·does·not·exist."
(prettier/prettier)
[error] 79-79: Replace
'User·already·exist.'
with"User·already·exist."
(prettier/prettier)
[error] 80-80: Replace
'Duplicate·data.'
with"Duplicate·data."
(prettier/prettier)
[error] 81-81: Replace
'User·already·exists.'
with"User·already·exists."
(prettier/prettier)
[error] 82-82: Replace
'Internal·server·error'
with"Internal·server·error"
(prettier/prettier)
[error] 83-84: Replace
'User·service',⏎
with"User·service",
(prettier/prettier)
[error] 86-86: Replace
'User·not·found·for·delete.'
with"User·not·found·for·delete."
(prettier/prettier)
[error] 87-87: Replace
'User·not·found·for·password·reset.'
with"User·not·found·for·password·reset."
(prettier/prettier)
[error] 90-90: Replace
'User·details·fetched·successfully.'
with"User·details·fetched·successfully."
(prettier/prettier)
[error] 91-91: Replace
'User·details·fetched·successfully·by·email'
with"User·details·fetched·successfully·by·email"
(prettier/prettier)
[error] 92-92: Replace
'User·details·fetched·successfully·by·phone'
with"User·details·fetched·successfully·by·phone"
(prettier/prettier)
[error] 93-93: Replace
·'User·details·fetched·successfully·by·username'
with⏎····"User·details·fetched·successfully·by·username"
(prettier/prettier)
[error] 94-94: Replace
·'User·details·fetched·successfully·by·tenantId'
with⏎····"User·details·fetched·successfully·by·tenantId"
(prettier/prettier)
[error] 95-95: Replace
·'User·details·fetched·successfully·by·userId'
with⏎····"User·details·fetched·successfully·by·userId"
(prettier/prettier)
[error] 96-96: Replace
·'User·details·fetched·successfully·by·userId·and·tenantId'
with⏎····"User·details·fetched·successfully·by·userId·and·tenantId"
(prettier/prettier)
[error] 97-97: Replace
·'User·details·fetched·successfully·by·email·and·tenantId'
with⏎····"User·details·fetched·successfully·by·email·and·tenantId"
(prettier/prettier)
[error] 98-98: Replace
'User·created·successfully·on·keycloak'
with"User·created·successfully·on·keycloak"
(prettier/prettier)
[error] 99-99: Replace
'Username·is·already·exists·in·keycloak'
with"Username·is·already·exists·in·keycloak"
(prettier/prettier)
[error] 100-100: Replace
'Failed·to·update·username·details·in·Keycloak.'
with·"Failed·to·update·username·details·in·Keycloak."
(prettier/prettier)
[error] 101-101: Replace
'Username·is·already·taken.·Suggested·a·new·unique·username.'
with⏎····"Username·is·already·taken.·Suggested·a·new·unique·username."
(prettier/prettier)
[error] 105-105: Replace
'User·created·in·user·table·successfully'
with"User·created·in·user·table·successfully"
(prettier/prettier)
[error] 106-106: Replace
'User·creation·failed'
with"User·creation·failed"
(prettier/prettier)
[error] 107-107: Insert
⏎···
(prettier/prettier)
[error] 108-108: Insert
⏎···
(prettier/prettier)
[error] 109-109: Insert
⏎···
(prettier/prettier)
[error] 110-110: Insert
⏎···
(prettier/prettier)
[error] 114-115: Delete
⏎
(prettier/prettier)
[error] 116-116: Replace
'Please·enter·valid·UUID'
with"Please·enter·valid·UUID"
(prettier/prettier)
[error] 118-118: Insert
⏎···
(prettier/prettier)
[error] 123-123: Replace
'Academic·year·not·found·for·tenant'
with"Academic·year·not·found·for·tenant"
(prettier/prettier)
[error] 124-124: Insert
⏎···
(prettier/prettier)
[error] 125-125: Replace
·'Invalid·parameters·provided.·Please·ensure·that·tenantId,·roleId,·and·cohortId·(if·applicable)·are·correctly·provided.'
with⏎····"Invalid·parameters·provided.·Please·ensure·that·tenantId,·roleId,·and·cohortId·(if·applicable)·are·correctly·provided."
(prettier/prettier)
[error] 126-126: Insert
⏎···
(prettier/prettier)
[error] 128-128: Insert
⏎···
(prettier/prettier)
[error] 130-130: Insert
⏎···
(prettier/prettier)
[error] 131-131: Insert
⏎···
(prettier/prettier)
[error] 132-132: Replace
'Field·not·found'
with"Field·not·found"
(prettier/prettier)
[error] 133-133: Replace
'Password·reset·successful!'
with"Password·reset·successful!"
(prettier/prettier)
[error] 140-140: Replace
'Cohort·name·already·exist.Please·provide·another·name.'
with"Cohort·name·already·exist.Please·provide·another·name."
(prettier/prettier)
[error] 144-144: Replace
'Cohort·already·exists'
with"Cohort·already·exists"
(prettier/prettier)
[error] 146-146: Replace
'Fetch·cohort·custom·field·details'
with"Fetch·cohort·custom·field·details"
(prettier/prettier)
[error] 147-147: Replace
'Get·all·child·data·response'
with"Get·all·child·data·response"
(prettier/prettier)
[error] 148-148: Replace
'Fetch·cohort·data·response'
with"Fetch·cohort·data·response"
(prettier/prettier)
[error] 149-149: Replace
'Cohort·updated·successfully.'
with"Cohort·updated·successfully."
(prettier/prettier)
[error] 150-150: Replace
'Tenant·not·found'
with"Tenant·not·found"
(prettier/prettier)
[error] 164-165: Delete
⏎
(prettier/prettier)
[error] 166-166: Insert
⏎···
(prettier/prettier)
[error] 168-168: Replace
'OTP·has·expired'
with"OTP·has·expired"
(prettier/prettier)
[error] 170-170: Replace
'OTP·validation·Sucessfully'
with"OTP·validation·Sucessfully"
(prettier/prettier)
[error] 171-171: Replace
'Invalid·mobile·number.·Must·be·10·digits.'
with"Invalid·mobile·number.·Must·be·10·digits."
(prettier/prettier)
[error] 172-172: Replace
'Missing·required·fields'
with"Missing·required·fields"
(prettier/prettier)
[error] 173-173: Replace
'Invalid·hash·format'
with"Invalid·hash·format"
(prettier/prettier)
[error] 174-174: Replace
'SMS·notification·failed'
with"SMS·notification·failed"
(prettier/prettier)
[error] 175-175: Replace
'Failed·to·send·SMS·notification:'
with"Failed·to·send·SMS·notification:"
(prettier/prettier)
[error] 176-176: Replace
'Username·Required'
with"Username·Required"
(prettier/prettier)
[error] 177-177: Replace
'Invalid·Reason'
with"Invalid·Reason"
(prettier/prettier)
[error] 178-178: Replace
'MObile·Required'
with"MObile·Required"
(prettier/prettier)
[error] 179-179: Replace
'Invalid·hash·format'
with"Invalid·hash·format"
(prettier/prettier)
[error] 180-180: Replace
·'Notification·not·send·due·to·getting·from·notification·API'
with⏎····"Notification·not·send·due·to·getting·from·notification·API"
(prettier/prettier)
[error] 181-181: Replace
·'Mobile·number·and·email·ID·not·found·for·sending·OTP'
with⏎····"Mobile·number·and·email·ID·not·found·for·sending·OTP"
(prettier/prettier)
[error] 182-182: Replace
'OTP·sent·successfully·to·mobile'
with"OTP·sent·successfully·to·mobile"
(prettier/prettier)
[error] 183-183: Replace
'Failed·to·send·OTP·to·mobile'
with"Failed·to·send·OTP·to·mobile"
(prettier/prettier)
[error] 184-184: Replace
'OTP·sent·successfully·to·email'
with"OTP·sent·successfully·to·email"
(prettier/prettier)
[error] 185-185: Replace
'Failed·to·send·OTP·to·email'
with"Failed·to·send·OTP·to·email"
(prettier/prettier)
[error] 186-186: Replace
'OTP·sent·successfully'
with"OTP·sent·successfully"
(prettier/prettier)
[error] 187-187: Replace
'Failed·to·send·Email·notification:'
with"Failed·to·send·Email·notification:"
(prettier/prettier)
[error] 188-188: Replace
'Email·notification·failed'
with"Email·notification·failed",
(prettier/prettier)
178-178
: Fix typo in mobile required message.The message contains a typo in the word "Mobile".
- MOBILE_REQUIRED: 'MObile Required', + MOBILE_REQUIRED: "Mobile number is required",🧰 Tools
🪛 ESLint
[error] 178-178: Replace
'MObile·Required'
with"MObile·Required"
(prettier/prettier)
67-72
: Improve error message templates.The template strings for error messages could be more consistent and informative.
Consider standardizing the error message format:
- MAPPING_EXIST_BW_USER_AND_COHORT: (userId, cohortId) => `Mapping already exists for userId ${userId} and cohortId ${cohortId} for this academic year`, + MAPPING_EXISTS: (userId, cohortId) => `User (${userId}) is already mapped to cohort (${cohortId}) for this academic year`, - USER_CREATE_FAILED_WITH_ERROR: (error) => `User creation failed with error: ${error}`, - USER_CREATE_FAILED_WITH_ERROR_AND_EMAIL: (error, email) => `User creation failed with error: ${error}. Email: ${email}`, + USER_CREATE_FAILED: (error: string, context?: { email?: string; phone?: string; username?: string }) => { + let message = `Failed to create user: ${error}`; + if (context?.email) message += ` (Email: ${context.email})`; + if (context?.phone) message += ` (Phone: ${context.phone})`; + if (context?.username) message += ` (Username: ${context.username})`; + return message; + },Also applies to: 107-110
🧰 Tools
🪛 ESLint
[error] 67-67: Insert
⏎···
(prettier/prettier)
[error] 68-68: Insert
⏎···
(prettier/prettier)
[error] 69-69: Insert
⏎···
(prettier/prettier)
[error] 70-70: Insert
⏎···
(prettier/prettier)
[error] 71-71: Insert
⏎···
(prettier/prettier)
[error] 72-72: Insert
⏎···
(prettier/prettier)
1-189
: Add JSDoc comments for better documentation.The file lacks documentation explaining the purpose and usage of these messages.
Add JSDoc comments at the top of the file:
+/** + * @fileoverview Centralized storage for all API response messages used across the application. + * Messages are categorized by their domain (auth, user, academic year, etc.) and include + * both static messages and dynamic message templates. + * + * @example + * import { API_RESPONSES } from './response.messages'; + * + * // Static message + * throw new Error(API_RESPONSES.USER_NOT_FOUND); + * + * // Dynamic message + * throw new Error(API_RESPONSES.INVALID_EMAIL(emailId)); + */ export const API_RESPONSES = {🧰 Tools
🪛 ESLint
[error] 65-65: Insert
⏎···
(prettier/prettier)
[error] 66-66: Insert
⏎···
(prettier/prettier)
[error] 67-67: Insert
⏎···
(prettier/prettier)
[error] 68-68: Insert
⏎···
(prettier/prettier)
[error] 69-69: Insert
⏎···
(prettier/prettier)
[error] 70-70: Insert
⏎···
(prettier/prettier)
[error] 71-71: Insert
⏎···
(prettier/prettier)
[error] 72-72: Insert
⏎···
(prettier/prettier)
[error] 73-73: Replace
'Unauthorized'
with"Unauthorized"
(prettier/prettier)
[error] 74-74: Replace
'Token·Invalid'
with"Token·Invalid"
(prettier/prettier)
[error] 77-77: Replace
'User·updated·successfully.'
with"User·updated·successfully."
(prettier/prettier)
[error] 78-78: Replace
'User·does·not·exist.'
with"User·does·not·exist."
(prettier/prettier)
[error] 79-79: Replace
'User·already·exist.'
with"User·already·exist."
(prettier/prettier)
[error] 80-80: Replace
'Duplicate·data.'
with"Duplicate·data."
(prettier/prettier)
[error] 81-81: Replace
'User·already·exists.'
with"User·already·exists."
(prettier/prettier)
[error] 82-82: Replace
'Internal·server·error'
with"Internal·server·error"
(prettier/prettier)
[error] 83-84: Replace
'User·service',⏎
with"User·service",
(prettier/prettier)
[error] 86-86: Replace
'User·not·found·for·delete.'
with"User·not·found·for·delete."
(prettier/prettier)
[error] 87-87: Replace
'User·not·found·for·password·reset.'
with"User·not·found·for·password·reset."
(prettier/prettier)
[error] 90-90: Replace
'User·details·fetched·successfully.'
with"User·details·fetched·successfully."
(prettier/prettier)
[error] 91-91: Replace
'User·details·fetched·successfully·by·email'
with"User·details·fetched·successfully·by·email"
(prettier/prettier)
[error] 92-92: Replace
'User·details·fetched·successfully·by·phone'
with"User·details·fetched·successfully·by·phone"
(prettier/prettier)
[error] 93-93: Replace
·'User·details·fetched·successfully·by·username'
with⏎····"User·details·fetched·successfully·by·username"
(prettier/prettier)
[error] 94-94: Replace
·'User·details·fetched·successfully·by·tenantId'
with⏎····"User·details·fetched·successfully·by·tenantId"
(prettier/prettier)
[error] 95-95: Replace
·'User·details·fetched·successfully·by·userId'
with⏎····"User·details·fetched·successfully·by·userId"
(prettier/prettier)
[error] 96-96: Replace
·'User·details·fetched·successfully·by·userId·and·tenantId'
with⏎····"User·details·fetched·successfully·by·userId·and·tenantId"
(prettier/prettier)
[error] 97-97: Replace
·'User·details·fetched·successfully·by·email·and·tenantId'
with⏎····"User·details·fetched·successfully·by·email·and·tenantId"
(prettier/prettier)
[error] 98-98: Replace
'User·created·successfully·on·keycloak'
with"User·created·successfully·on·keycloak"
(prettier/prettier)
[error] 99-99: Replace
'Username·is·already·exists·in·keycloak'
with"Username·is·already·exists·in·keycloak"
(prettier/prettier)
[error] 100-100: Replace
'Failed·to·update·username·details·in·Keycloak.'
with·"Failed·to·update·username·details·in·Keycloak."
(prettier/prettier)
[error] 101-101: Replace
'Username·is·already·taken.·Suggested·a·new·unique·username.'
with⏎····"Username·is·already·taken.·Suggested·a·new·unique·username."
(prettier/prettier)
[error] 105-105: Replace
'User·created·in·user·table·successfully'
with"User·created·in·user·table·successfully"
(prettier/prettier)
[error] 106-106: Replace
'User·creation·failed'
with"User·creation·failed"
(prettier/prettier)
[error] 107-107: Insert
⏎···
(prettier/prettier)
[error] 108-108: Insert
⏎···
(prettier/prettier)
[error] 109-109: Insert
⏎···
(prettier/prettier)
[error] 110-110: Insert
⏎···
(prettier/prettier)
[error] 114-115: Delete
⏎
(prettier/prettier)
[error] 116-116: Replace
'Please·enter·valid·UUID'
with"Please·enter·valid·UUID"
(prettier/prettier)
[error] 118-118: Insert
⏎···
(prettier/prettier)
[error] 123-123: Replace
'Academic·year·not·found·for·tenant'
with"Academic·year·not·found·for·tenant"
(prettier/prettier)
[error] 124-124: Insert
⏎···
(prettier/prettier)
[error] 125-125: Replace
·'Invalid·parameters·provided.·Please·ensure·that·tenantId,·roleId,·and·cohortId·(if·applicable)·are·correctly·provided.'
with⏎····"Invalid·parameters·provided.·Please·ensure·that·tenantId,·roleId,·and·cohortId·(if·applicable)·are·correctly·provided."
(prettier/prettier)
[error] 126-126: Insert
⏎···
(prettier/prettier)
[error] 128-128: Insert
⏎···
(prettier/prettier)
[error] 130-130: Insert
⏎···
(prettier/prettier)
[error] 131-131: Insert
⏎···
(prettier/prettier)
[error] 132-132: Replace
'Field·not·found'
with"Field·not·found"
(prettier/prettier)
[error] 133-133: Replace
'Password·reset·successful!'
with"Password·reset·successful!"
(prettier/prettier)
[error] 140-140: Replace
'Cohort·name·already·exist.Please·provide·another·name.'
with"Cohort·name·already·exist.Please·provide·another·name."
(prettier/prettier)
[error] 144-144: Replace
'Cohort·already·exists'
with"Cohort·already·exists"
(prettier/prettier)
[error] 146-146: Replace
'Fetch·cohort·custom·field·details'
with"Fetch·cohort·custom·field·details"
(prettier/prettier)
[error] 147-147: Replace
'Get·all·child·data·response'
with"Get·all·child·data·response"
(prettier/prettier)
[error] 148-148: Replace
'Fetch·cohort·data·response'
with"Fetch·cohort·data·response"
(prettier/prettier)
[error] 149-149: Replace
'Cohort·updated·successfully.'
with"Cohort·updated·successfully."
(prettier/prettier)
[error] 150-150: Replace
'Tenant·not·found'
with"Tenant·not·found"
(prettier/prettier)
[error] 164-165: Delete
⏎
(prettier/prettier)
[error] 166-166: Insert
⏎···
(prettier/prettier)
[error] 168-168: Replace
'OTP·has·expired'
with"OTP·has·expired"
(prettier/prettier)
[error] 170-170: Replace
'OTP·validation·Sucessfully'
with"OTP·validation·Sucessfully"
(prettier/prettier)
[error] 171-171: Replace
'Invalid·mobile·number.·Must·be·10·digits.'
with"Invalid·mobile·number.·Must·be·10·digits."
(prettier/prettier)
[error] 172-172: Replace
'Missing·required·fields'
with"Missing·required·fields"
(prettier/prettier)
[error] 173-173: Replace
'Invalid·hash·format'
with"Invalid·hash·format"
(prettier/prettier)
[error] 174-174: Replace
'SMS·notification·failed'
with"SMS·notification·failed"
(prettier/prettier)
[error] 175-175: Replace
'Failed·to·send·SMS·notification:'
with"Failed·to·send·SMS·notification:"
(prettier/prettier)
[error] 176-176: Replace
'Username·Required'
with"Username·Required"
(prettier/prettier)
[error] 177-177: Replace
'Invalid·Reason'
with"Invalid·Reason"
(prettier/prettier)
[error] 178-178: Replace
'MObile·Required'
with"MObile·Required"
(prettier/prettier)
[error] 179-179: Replace
'Invalid·hash·format'
with"Invalid·hash·format"
(prettier/prettier)
[error] 180-180: Replace
·'Notification·not·send·due·to·getting·from·notification·API'
with⏎····"Notification·not·send·due·to·getting·from·notification·API"
(prettier/prettier)
[error] 181-181: Replace
·'Mobile·number·and·email·ID·not·found·for·sending·OTP'
with⏎····"Mobile·number·and·email·ID·not·found·for·sending·OTP"
(prettier/prettier)
[error] 182-182: Replace
'OTP·sent·successfully·to·mobile'
with"OTP·sent·successfully·to·mobile"
(prettier/prettier)
[error] 183-183: Replace
'Failed·to·send·OTP·to·mobile'
with"Failed·to·send·OTP·to·mobile"
(prettier/prettier)
[error] 184-184: Replace
'OTP·sent·successfully·to·email'
with"OTP·sent·successfully·to·email"
(prettier/prettier)
[error] 185-185: Replace
'Failed·to·send·OTP·to·email'
with"Failed·to·send·OTP·to·email"
(prettier/prettier)
[error] 186-186: Replace
'OTP·sent·successfully'
with"OTP·sent·successfully"
(prettier/prettier)
[error] 187-187: Replace
'Failed·to·send·Email·notification:'
with"Failed·to·send·Email·notification:"
(prettier/prettier)
[error] 188-188: Replace
'Email·notification·failed'
with"Email·notification·failed",
(prettier/prettier)
src/adapters/userservicelocator.ts (1)
38-42
: Implement rate limiting for OTP and password reset endpoints.These sensitive endpoints should be protected against abuse:
sendPasswordResetLink
forgotPassword
sendOtp
verifyOtp
sendPasswordResetOTP
Consider implementing rate limiting using NestJS ThrottlerModule to prevent:
- Brute force attempts
- OTP flooding
- Password reset abuse
🧰 Tools
🪛 ESLint
[error] 38-38: Replace
request:·any,·username:·string,·redirectUrl:·string,·response:·Response
with⏎····request:·any,⏎····username:·string,⏎····redirectUrl:·string,⏎····response:·Response⏎··
(prettier/prettier)
[error] 42-42: Replace
body:·SendPasswordResetOTPDto,·response:·Response
with⏎····body:·SendPasswordResetOTPDto,⏎····response:·Response⏎··
(prettier/prettier)
src/adapters/postgres/user-adapter.ts (1)
83-101
: Consider organizing dependencies using a configuration service.The constructor has many dependencies and configuration values. Consider:
- Moving configuration to a dedicated config service
- Grouping related dependencies
- Using dependency interfaces for better testability
interface OtpConfig { otpExpiry: number; otpDigits: number; smsKey: string; } interface JwtConfig { secret: string; passwordResetExpiresIn: string; resetFrontendUrl: string; } @Injectable() class ConfigService { getOtpConfig(): OtpConfig; getJwtConfig(): JwtConfig; }🧰 Tools
🪛 ESLint
[error] 98-98: Replace
'OTP_EXPIRY'
with"OTP_EXPIRY"
(prettier/prettier)
[error] 99-99: Replace
'OTP_DIGITS'
with"OTP_DIGITS"
(prettier/prettier)
[error] 100-100: Replace
'SMS_KEY'
with"SMS_KEY"
(prettier/prettier)
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
-
src/adapters/postgres/fields-adapter.ts
(1 hunks) -
src/adapters/postgres/user-adapter.ts
(9 hunks) -
src/adapters/userservicelocator.ts
(2 hunks) -
src/common/utils/api-id.config.ts
(1 hunks) -
src/common/utils/response.messages.ts
(1 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
src/common/utils/response.messages.ts (1)
Pattern **/*.ts
: "Review the JavaScript code for conformity with the Google JavaScript style guide, highlighting any deviations. Ensure that:
- The code adheres to best practices associated with nodejs.
- The code adheres to best practices associated with nestjs framework.
- The code adheres to best practices recommended for performance.
- The code adheres to similar naming conventions for controllers, models, services, methods, variables."
src/common/utils/api-id.config.ts (1)
Pattern **/*.ts
: "Review the JavaScript code for conformity with the Google JavaScript style guide, highlighting any deviations. Ensure that:
- The code adheres to best practices associated with nodejs.
- The code adheres to best practices associated with nestjs framework.
- The code adheres to best practices recommended for performance.
- The code adheres to similar naming conventions for controllers, models, services, methods, variables."
src/adapters/postgres/user-adapter.ts (1)
Pattern **/*.ts
: "Review the JavaScript code for conformity with the Google JavaScript style guide, highlighting any deviations. Ensure that:
- The code adheres to best practices associated with nodejs.
- The code adheres to best practices associated with nestjs framework.
- The code adheres to best practices recommended for performance.
- The code adheres to similar naming conventions for controllers, models, services, methods, variables."
src/adapters/userservicelocator.ts (1)
Pattern **/*.ts
: "Review the JavaScript code for conformity with the Google JavaScript style guide, highlighting any deviations. Ensure that:
- The code adheres to best practices associated with nodejs.
- The code adheres to best practices associated with nestjs framework.
- The code adheres to best practices recommended for performance.
- The code adheres to similar naming conventions for controllers, models, services, methods, variables."
src/adapters/postgres/fields-adapter.ts (1)
Pattern **/*.ts
: "Review the JavaScript code for conformity with the Google JavaScript style guide, highlighting any deviations. Ensure that:
- The code adheres to best practices associated with nodejs.
- The code adheres to best practices associated with nestjs framework.
- The code adheres to best practices recommended for performance.
- The code adheres to similar naming conventions for controllers, models, services, methods, variables."
🪛 ESLint
src/common/utils/response.messages.ts
[error] 65-65: Insert ⏎···
(prettier/prettier)
[error] 66-66: Insert ⏎···
(prettier/prettier)
[error] 67-67: Insert ⏎···
(prettier/prettier)
[error] 68-68: Insert ⏎···
(prettier/prettier)
[error] 69-69: Insert ⏎···
(prettier/prettier)
[error] 70-70: Insert ⏎···
(prettier/prettier)
[error] 71-71: Insert ⏎···
(prettier/prettier)
[error] 72-72: Insert ⏎···
(prettier/prettier)
[error] 73-73: Replace 'Unauthorized'
with "Unauthorized"
(prettier/prettier)
[error] 74-74: Replace 'Token·Invalid'
with "Token·Invalid"
(prettier/prettier)
[error] 77-77: Replace 'User·updated·successfully.'
with "User·updated·successfully."
(prettier/prettier)
[error] 78-78: Replace 'User·does·not·exist.'
with "User·does·not·exist."
(prettier/prettier)
[error] 79-79: Replace 'User·already·exist.'
with "User·already·exist."
(prettier/prettier)
[error] 80-80: Replace 'Duplicate·data.'
with "Duplicate·data."
(prettier/prettier)
[error] 81-81: Replace 'User·already·exists.'
with "User·already·exists."
(prettier/prettier)
[error] 82-82: Replace 'Internal·server·error'
with "Internal·server·error"
(prettier/prettier)
[error] 83-84: Replace 'User·service',⏎
with "User·service",
(prettier/prettier)
[error] 86-86: Replace 'User·not·found·for·delete.'
with "User·not·found·for·delete."
(prettier/prettier)
[error] 87-87: Replace 'User·not·found·for·password·reset.'
with "User·not·found·for·password·reset."
(prettier/prettier)
[error] 90-90: Replace 'User·details·fetched·successfully.'
with "User·details·fetched·successfully."
(prettier/prettier)
[error] 91-91: Replace 'User·details·fetched·successfully·by·email'
with "User·details·fetched·successfully·by·email"
(prettier/prettier)
[error] 92-92: Replace 'User·details·fetched·successfully·by·phone'
with "User·details·fetched·successfully·by·phone"
(prettier/prettier)
[error] 93-93: Replace ·'User·details·fetched·successfully·by·username'
with ⏎····"User·details·fetched·successfully·by·username"
(prettier/prettier)
[error] 94-94: Replace ·'User·details·fetched·successfully·by·tenantId'
with ⏎····"User·details·fetched·successfully·by·tenantId"
(prettier/prettier)
[error] 95-95: Replace ·'User·details·fetched·successfully·by·userId'
with ⏎····"User·details·fetched·successfully·by·userId"
(prettier/prettier)
[error] 96-96: Replace ·'User·details·fetched·successfully·by·userId·and·tenantId'
with ⏎····"User·details·fetched·successfully·by·userId·and·tenantId"
(prettier/prettier)
[error] 97-97: Replace ·'User·details·fetched·successfully·by·email·and·tenantId'
with ⏎····"User·details·fetched·successfully·by·email·and·tenantId"
(prettier/prettier)
[error] 98-98: Replace 'User·created·successfully·on·keycloak'
with "User·created·successfully·on·keycloak"
(prettier/prettier)
[error] 99-99: Replace 'Username·is·already·exists·in·keycloak'
with "Username·is·already·exists·in·keycloak"
(prettier/prettier)
[error] 100-100: Replace 'Failed·to·update·username·details·in·Keycloak.'
with ·"Failed·to·update·username·details·in·Keycloak."
(prettier/prettier)
[error] 101-101: Replace 'Username·is·already·taken.·Suggested·a·new·unique·username.'
with ⏎····"Username·is·already·taken.·Suggested·a·new·unique·username."
(prettier/prettier)
[error] 105-105: Replace 'User·created·in·user·table·successfully'
with "User·created·in·user·table·successfully"
(prettier/prettier)
[error] 106-106: Replace 'User·creation·failed'
with "User·creation·failed"
(prettier/prettier)
[error] 107-107: Insert ⏎···
(prettier/prettier)
[error] 108-108: Insert ⏎···
(prettier/prettier)
[error] 109-109: Insert ⏎···
(prettier/prettier)
[error] 110-110: Insert ⏎···
(prettier/prettier)
[error] 114-115: Delete ⏎
(prettier/prettier)
[error] 116-116: Replace 'Please·enter·valid·UUID'
with "Please·enter·valid·UUID"
(prettier/prettier)
[error] 118-118: Insert ⏎···
(prettier/prettier)
[error] 123-123: Replace 'Academic·year·not·found·for·tenant'
with "Academic·year·not·found·for·tenant"
(prettier/prettier)
[error] 124-124: Insert ⏎···
(prettier/prettier)
[error] 125-125: Replace ·'Invalid·parameters·provided.·Please·ensure·that·tenantId,·roleId,·and·cohortId·(if·applicable)·are·correctly·provided.'
with ⏎····"Invalid·parameters·provided.·Please·ensure·that·tenantId,·roleId,·and·cohortId·(if·applicable)·are·correctly·provided."
(prettier/prettier)
[error] 126-126: Insert ⏎···
(prettier/prettier)
[error] 128-128: Insert ⏎···
(prettier/prettier)
[error] 130-130: Insert ⏎···
(prettier/prettier)
[error] 131-131: Insert ⏎···
(prettier/prettier)
[error] 132-132: Replace 'Field·not·found'
with "Field·not·found"
(prettier/prettier)
[error] 133-133: Replace 'Password·reset·successful!'
with "Password·reset·successful!"
(prettier/prettier)
[error] 140-140: Replace 'Cohort·name·already·exist.Please·provide·another·name.'
with "Cohort·name·already·exist.Please·provide·another·name."
(prettier/prettier)
[error] 144-144: Replace 'Cohort·already·exists'
with "Cohort·already·exists"
(prettier/prettier)
[error] 146-146: Replace 'Fetch·cohort·custom·field·details'
with "Fetch·cohort·custom·field·details"
(prettier/prettier)
[error] 147-147: Replace 'Get·all·child·data·response'
with "Get·all·child·data·response"
(prettier/prettier)
[error] 148-148: Replace 'Fetch·cohort·data·response'
with "Fetch·cohort·data·response"
(prettier/prettier)
[error] 149-149: Replace 'Cohort·updated·successfully.'
with "Cohort·updated·successfully."
(prettier/prettier)
[error] 150-150: Replace 'Tenant·not·found'
with "Tenant·not·found"
(prettier/prettier)
[error] 164-165: Delete ⏎
(prettier/prettier)
[error] 166-166: Insert ⏎···
(prettier/prettier)
[error] 168-168: Replace 'OTP·has·expired'
with "OTP·has·expired"
(prettier/prettier)
[error] 170-170: Replace 'OTP·validation·Sucessfully'
with "OTP·validation·Sucessfully"
(prettier/prettier)
[error] 171-171: Replace 'Invalid·mobile·number.·Must·be·10·digits.'
with "Invalid·mobile·number.·Must·be·10·digits."
(prettier/prettier)
[error] 172-172: Replace 'Missing·required·fields'
with "Missing·required·fields"
(prettier/prettier)
[error] 173-173: Replace 'Invalid·hash·format'
with "Invalid·hash·format"
(prettier/prettier)
[error] 174-174: Replace 'SMS·notification·failed'
with "SMS·notification·failed"
(prettier/prettier)
[error] 175-175: Replace 'Failed·to·send·SMS·notification:'
with "Failed·to·send·SMS·notification:"
(prettier/prettier)
[error] 176-176: Replace 'Username·Required'
with "Username·Required"
(prettier/prettier)
[error] 177-177: Replace 'Invalid·Reason'
with "Invalid·Reason"
(prettier/prettier)
[error] 178-178: Replace 'MObile·Required'
with "MObile·Required"
(prettier/prettier)
[error] 179-179: Replace 'Invalid·hash·format'
with "Invalid·hash·format"
(prettier/prettier)
[error] 180-180: Replace ·'Notification·not·send·due·to·getting·from·notification·API'
with ⏎····"Notification·not·send·due·to·getting·from·notification·API"
(prettier/prettier)
[error] 181-181: Replace ·'Mobile·number·and·email·ID·not·found·for·sending·OTP'
with ⏎····"Mobile·number·and·email·ID·not·found·for·sending·OTP"
(prettier/prettier)
[error] 182-182: Replace 'OTP·sent·successfully·to·mobile'
with "OTP·sent·successfully·to·mobile"
(prettier/prettier)
[error] 183-183: Replace 'Failed·to·send·OTP·to·mobile'
with "Failed·to·send·OTP·to·mobile"
(prettier/prettier)
[error] 184-184: Replace 'OTP·sent·successfully·to·email'
with "OTP·sent·successfully·to·email"
(prettier/prettier)
[error] 185-185: Replace 'Failed·to·send·OTP·to·email'
with "Failed·to·send·OTP·to·email"
(prettier/prettier)
[error] 186-186: Replace 'OTP·sent·successfully'
with "OTP·sent·successfully"
(prettier/prettier)
[error] 187-187: Replace 'Failed·to·send·Email·notification:'
with "Failed·to·send·Email·notification:"
(prettier/prettier)
[error] 188-188: Replace 'Email·notification·failed'
with "Email·notification·failed",
(prettier/prettier)
src/common/utils/api-id.config.ts
[error] 59-59: Replace 'api.send.reset.otp'
with "api.send.reset.otp",
(prettier/prettier)
src/adapters/postgres/user-adapter.ts
[error] 19-19: Replace ·ExistUserDto,·SuggestUserDto,·UserSearchDto·
with ⏎··ExistUserDto,⏎··SuggestUserDto,⏎··UserSearchDto,⏎
(prettier/prettier)
[error] 47-47: Replace 'crypto'
with "crypto"
(prettier/prettier)
[error] 98-98: Replace 'OTP_EXPIRY'
with "OTP_EXPIRY"
(prettier/prettier)
[error] 99-99: Replace 'OTP_DIGITS'
with "OTP_DIGITS"
(prettier/prettier)
[error] 100-100: Replace 'SMS_KEY'
with "SMS_KEY"
(prettier/prettier)
[error] 386-386: 'filters' is never reassigned. Use 'const' instead.
(prefer-const)
[error] 386-386: 'exclude' is never reassigned. Use 'const' instead.
(prefer-const)
[error] 386-386: 'sort' is never reassigned. Use 'const' instead.
(prefer-const)
[error] 479-479: Insert ⏎········
(prettier/prettier)
[error] 480-480: Delete ··
(prettier/prettier)
[error] 780-780: Replace ·where:·{·userId:·userDto.userId·}
with ⏎········where:·{·userId:·userDto.userId·},⏎·····
(prettier/prettier)
[error] 796-796: Replace userDto.userData.deviceId,·userDto.userId,·user.deviceId)
with ⏎············userDto.userData.deviceId,⏎············userDto.userId,⏎············user.deviceId⏎··········);
(prettier/prettier)
[error] 797-798: Delete ⏎
(prettier/prettier)
[error] 801-801: Replace userDto.userData.deviceId,·userDto.userId,·user.deviceId)
with ⏎············userDto.userData.deviceId,⏎············userDto.userId,⏎············user.deviceId⏎··········);
(prettier/prettier)
[error] 813-813: Replace keycloakReqBody
with ⏎············keycloakReqBody⏎··········
(prettier/prettier)
[error] 815-815: Replace 'exists'
with "exists"
(prettier/prettier)
[error] 932-932: Replace updateField:·UpdateField):·Promise<'exists'
with ⏎····updateField:·UpdateField⏎··):·Promise<"exists"
(prettier/prettier)
[error] 933-934: Delete ⏎
(prettier/prettier)
[error] 939-939: Replace updateField
with ⏎········updateField⏎······
(prettier/prettier)
[error] 941-941: Replace 'exists'
with "exists"
(prettier/prettier)
[error] 945-945: 'updateResult' is never reassigned. Use 'const' instead.
(prefer-const)
[error] 945-945: Insert ;
(prettier/prettier)
[error] 949-950: Delete ⏎
(prettier/prettier)
[error] 954-954: Delete ,
(prettier/prettier)
[error] 960-960: Replace userDeviceId:·string,·userId:·string,·existingDeviceId:·string[]
with ⏎····userDeviceId:·string,⏎····userId:·string,⏎····existingDeviceId:·string[]⏎··
(prettier/prettier)
[error] 961-961: 'deviceIds' is never reassigned. Use 'const' instead.
(prefer-const)
[error] 975-975: Replace deviceIdforRemove:·string,·userId:·string,·existingDeviceId:·string[]
with ⏎····deviceIdforRemove:·string,⏎····userId:·string,⏎····existingDeviceId:·string[]⏎··
(prettier/prettier)
[error] 982-982: Replace id
with (id)
(prettier/prettier)
[error] 986-986: Replace userId:·string,·userData:·Partial<User>
with ⏎····userId:·string,⏎····userData:·Partial<User>⏎··
(prettier/prettier)
[error] 996-997: Delete ⏎
(prettier/prettier)
[error] 1000-1000: Replace 'An·error·occurred·while·updating·user·details'
with "An·error·occurred·while·updating·user·details"
(prettier/prettier)
[error] 1002-1003: Delete ⏎
(prettier/prettier)
[error] 1063-1063: 'errKeycloak' is never reassigned. Use 'const' instead.
(prefer-const)
[error] 1082-1082: 'resKeycloak' is never reassigned. Use 'const' instead.
(prefer-const)
[error] 1082-1083: Replace ⏎
with ;
(prettier/prettier)
[error] 1108-1109: Delete ⏎
(prettier/prettier)
[error] 1148-1148: Insert ··
(prettier/prettier)
[error] 1149-1149: Insert ··
(prettier/prettier)
[error] 1150-1150: Insert ··
(prettier/prettier)
[error] 1347-1348: Delete ⏎
(prettier/prettier)
[error] 1381-1381: Replace user.userId·=·userCreateDto?.userId
with (user.userId·=·userCreateDto?.userId)
(prettier/prettier)
[error] 1382-1382: Replace user.username·=·userCreateDto?.username
with (user.username·=·userCreateDto?.username)
(prettier/prettier)
[error] 1383-1383: Replace user.firstName·=·userCreateDto?.firstName
with (user.firstName·=·userCreateDto?.firstName)
(prettier/prettier)
[error] 1384-1384: Replace user.middleName·=·userCreateDto?.middleName
with (user.middleName·=·userCreateDto?.middleName)
(prettier/prettier)
[error] 1385-1385: Replace user.lastName·=·userCreateDto?.lastName
with (user.lastName·=·userCreateDto?.lastName)
(prettier/prettier)
[error] 1386-1386: Replace user.gender·=·userCreateDto?.gender
with (user.gender·=·userCreateDto?.gender)
(prettier/prettier)
[error] 1387-1387: Replace user.email·=·userCreateDto?.email
with (user.email·=·userCreateDto?.email)
(prettier/prettier)
[error] 1388-1388: Replace user.mobile·=·Number(userCreateDto?.mobile)·||·null
with (user.mobile·=·Number(userCreateDto?.mobile)·||·null)
(prettier/prettier)
[error] 1389-1389: Replace user.createdBy·=·userCreateDto?.createdBy·||·userCreateDto?.createdBy
with (user.createdBy·=·userCreateDto?.createdBy·||·userCreateDto?.createdBy)
(prettier/prettier)
[error] 1400-1400: 'query' is never reassigned. Use 'const' instead.
(prefer-const)
[error] 1402-1402: 'getCohortAcademicYearId' is never reassigned. Use 'const' instead.
(prefer-const)
[error] 1407-1407: 'cohortData' is never reassigned. Use 'const' instead.
(prefer-const)
[error] 1634-1634: Insert ··
(prettier/prettier)
[error] 1741-1741: Insert ··
(prettier/prettier)
[error] 1742-1742: Replace ········
with ··········
(prettier/prettier)
[error] 1767-1767: Delete ······
(prettier/prettier)
[error] 1862-1862: Replace mobileOrUsername:·string,·otp:·string,·reason:·string
with ⏎····mobileOrUsername:·string,⏎····otp:·string,⏎····reason:·string⏎··
(prettier/prettier)
[error] 1886-1886: Replace mobile,·reason
with ⏎········mobile,⏎········reason⏎······
(prettier/prettier)
[error] 1892-1892: Insert ,
(prettier/prettier)
[error] 1894-1894: Insert ,
(prettier/prettier)
[error] 1903-1904: Delete ⏎···
(prettier/prettier)
[error] 1925-1925: Replace mobileWithCode,·otp,·reason
with ⏎········mobileWithCode,⏎········otp,⏎········reason⏎······
(prettier/prettier)
[error] 1928-1928: Insert ,
(prettier/prettier)
[error] 1931-1931: Replace "OTP",·"SEND_OTP",·replacements,·[mobile]
with ⏎········"OTP",⏎········"SEND_OTP",⏎········replacements,⏎········[mobile]⏎······
(prettier/prettier)
[error] 1933-1934: Delete ⏎···
(prettier/prettier)
[error] 1954-1954: Replace 'signup'
with "signup"
(prettier/prettier)
[error] 1965-1965: Replace 'forgot'
with "forgot"
(prettier/prettier)
[error] 1985-1985: Replace '.'
with "."
(prettier/prettier)
[error] 2042-2042: Replace context:·string,·key:·string,·replacements:·object,·receipients:·string[]
with ⏎····context:·string,⏎····key:·string,⏎····replacements:·object,⏎····receipients:·string[]⏎··
(prettier/prettier)
[error] 2059-2059: Replace mailSend?.result?.sms?.errors·&&·mailSend.result.sms.errors.length·>·0
with ⏎········mailSend?.result?.sms?.errors·&&⏎········mailSend.result.sms.errors.length·>·0⏎······
(prettier/prettier)
[error] 2060-2060: Replace (error:·{·error:·string;·})·=>·error.error
with ⏎··········(error:·{·error:·string·})·=>·error.error⏎········
(prettier/prettier)
[error] 2065-2066: Delete ⏎···
(prettier/prettier)
[error] 2068-2068: Replace ${API_RESPONSES.SMS_NOTIFICATION_ERROR}:··${error.message}
with ⏎········
${API_RESPONSES.SMS_NOTIFICATION_ERROR}:··${error.message}⏎······
(prettier/prettier)
[error] 2073-2073: Replace body:·SendPasswordResetOTPDto,·response:·Response
with ⏎····body:·SendPasswordResetOTPDto,⏎····response:·Response⏎··
(prettier/prettier)
[error] 2077-2077: 'error' is never reassigned. Use 'const' instead.
(prefer-const)
[error] 2078-2078: 'success' is never reassigned. Use 'const' instead.
(prefer-const)
[error] 2099-2099: Replace ''
with ""
(prettier/prettier)
[error] 2102-2102: Replace username,·otp,·reason
with ⏎········username,⏎········otp,⏎········reason⏎······
(prettier/prettier)
[error] 2106-2106: Insert ,
(prettier/prettier)
[error] 2109-2109: Replace userData.mobile
with ⏎············userData.mobile,⏎··········
(prettier/prettier)
[error] 2110-2110: Replace 'SMS'
with "SMS"
(prettier/prettier)
[error] 2112-2112: Replace ·type:·'SMS',·message:·
${API_RESPONSES.MOBILE_OTP_SEND_FAILED}·${e.message}·})
with ⏎············type:·"SMS",⏎············message:·
${API_RESPONSES.MOBILE_OTP_SEND_FAILED}·${e.message},⏎··········});
(prettier/prettier)
[error] 2120-2120: Insert ,
(prettier/prettier)
[error] 2123-2123: Replace userData.email
with ⏎············userData.email,⏎··········
(prettier/prettier)
[error] 2124-2124: Replace ·type:·'Email',·message:·API_RESPONSES.EMAIL_SENT_OTP·})
with ⏎············type:·"Email",⏎············message:·API_RESPONSES.EMAIL_SENT_OTP,⏎··········});
(prettier/prettier)
[error] 2126-2126: Replace ·type:·'Email',·message:·
${API_RESPONSES.EMAIL_OTP_SEND_FAILED}:·${e.message}·})
with ⏎············type:·"Email",⏎············message:·
${API_RESPONSES.EMAIL_OTP_SEND_FAILED}:·${e.message},⏎··········});
(prettier/prettier)
[error] 2129-2129: Delete ·
(prettier/prettier)
[error] 2130-2130: Insert ⏎·······
(prettier/prettier)
[error] 2131-2131: Replace ''
with ""
(prettier/prettier)
[error] 2132-2132: Replace e·=>·e.type·===·'SMS'
with (e)·=>·e.type·===·"SMS"
(prettier/prettier)
[error] 2133-2133: Replace .filter(e·=>·e.type·===·'SMS').map(e·=>·e.message)
with ⏎············.filter((e)·=>·e.type·===·"SMS")⏎············.map((e)·=>·e.message)⏎············
(prettier/prettier)
[error] 2135-2135: Replace (e·=>·e.type·===·'Email'
with ((e)·=>·e.type·===·"Email"
(prettier/prettier)
[error] 2136-2136: Replace .filter(e·=>·e.type·===·'Email').map(e·=>·e.message)
with ⏎············.filter((e)·=>·e.type·===·"Email")⏎············.map((e)·=>·e.message)⏎············
(prettier/prettier)
[error] 2150-2150: Insert ,
(prettier/prettier)
[error] 2151-2151: Insert ;
(prettier/prettier)
[error] 2159-2160: Delete ⏎···
(prettier/prettier)
[error] 2169-2170: Delete ⏎
(prettier/prettier)
[error] 2173-2173: Replace context:·string,·key:·string,·replacements:·object,·emailReceipt
with ⏎····context:·string,⏎····key:·string,⏎····replacements:·object,⏎····emailReceipt⏎··
(prettier/prettier)
[error] 2188-2188: Replace mailSend?.result?.email?.errors·&&·mailSend.result.email.errors.length·>·0
with ⏎········mailSend?.result?.email?.errors·&&⏎········mailSend.result.email.errors.length·>·0⏎······
(prettier/prettier)
[error] 2189-2189: Replace (error:·{·error:·string;·})·=>·error.error
with ⏎··········(error:·{·error:·string·})·=>·error.error⏎········
(prettier/prettier)
[error] 2194-2195: Delete ⏎···
(prettier/prettier)
[error] 2197-2197: Replace ${API_RESPONSES.EMAIL_NOTIFICATION_ERROR}:··${e.message}
with ⏎········
${API_RESPONSES.EMAIL_NOTIFICATION_ERROR}:··${e.message}⏎······
(prettier/prettier)
[error] 2199-2200: Delete ⏎
(prettier/prettier)
[error] 2202-2206: Replace ⏎····request:·any,⏎····response:·any,⏎····filters:·ExistUserDto⏎··
with request:·any,·response:·any,·filters:·ExistUserDto
(prettier/prettier)
[error] 2214-2214: Replace key·===·'firstName'·||·key·===·'middleName'·||·key·===·'lastName'
with ⏎··············key·===·"firstName"·||⏎··············key·===·"middleName"·||⏎··············key·===·"lastName"⏎············
(prettier/prettier)
[error] 2226-2226: Replace 'username',·'firstName',·'middleName',·'lastName'
with "username",·"firstName",·"middleName",·"lastName"
(prettier/prettier)
[error] 2264-2264: Replace 'string'
with "string"
(prettier/prettier)
[error] 2266-2266: Replace '\\$&'
with "\\$&"
(prettier/prettier)
[error] 2271-2272: Delete ··⏎
(prettier/prettier)
[error] 2273-2273: Replace request:·Request,·response:·Response,·suggestUserDto:·SuggestUserDto
with ⏎····request:·Request,⏎····response:·Response,⏎····suggestUserDto:·SuggestUserDto⏎··
(prettier/prettier)
[error] 2280-2280: Delete ··
(prettier/prettier)
[error] 2282-2282: Delete ··
(prettier/prettier)
[error] 2287-2287: Delete ········
(prettier/prettier)
[error] 2291-2291: Delete ··
(prettier/prettier)
[error] 2296-2296: Delete ··
(prettier/prettier)
[error] 2304-2304: Delete ··
(prettier/prettier)
[error] 2309-2309: Replace suggestedUsername:·newUsername
with ·suggestedUsername:·newUsername·
(prettier/prettier)
[error] 2314-2314: Delete ··
(prettier/prettier)
[error] 2323-2324: Delete ····⏎····
(prettier/prettier)
[error] 2335-2336: Delete ⏎··
(prettier/prettier)
src/adapters/userservicelocator.ts
[error] 4-4: Replace ·ExistUserDto,·SuggestUserDto,·UserSearchDto·
with ⏎··ExistUserDto,⏎··SuggestUserDto,⏎··UserSearchDto,⏎
(prettier/prettier)
[error] 21-21: Replace request:·any,·userDto:·UserCreateDto,·academicYearId:·string,·response:·Response
with ⏎····request:·any,⏎····userDto:·UserCreateDto,⏎····academicYearId:·string,⏎····response:·Response⏎··
(prettier/prettier)
[error] 36-36: Replace request:·Request,·response:Response,·suggestUserDto:·SuggestUserDto
with ⏎····request:·Request,⏎····response:·Response,⏎····suggestUserDto:·SuggestUserDto⏎··
(prettier/prettier)
[error] 38-38: Replace request:·any,·username:·string,·redirectUrl:·string,·response:·Response
with ⏎····request:·any,⏎····username:·string,⏎····redirectUrl:·string,⏎····response:·Response⏎··
(prettier/prettier)
[error] 42-42: Replace body:·SendPasswordResetOTPDto,·response:·Response
with ⏎····body:·SendPasswordResetOTPDto,⏎····response:·Response⏎··
(prettier/prettier)
src/adapters/postgres/fields-adapter.ts
[error] 34-34: Delete ·
(prettier/prettier)
[error] 104-104: Insert ;
(prettier/prettier)
[error] 388-388: Insert ;
(prettier/prettier)
[error] 460-460: Insert ;
(prettier/prettier)
[error] 509-509: Replace ·fieldsData.fieldParams·&&
with ⏎········fieldsData.fieldParams·&&⏎·······
(prettier/prettier)
[error] 635-635: Insert ;
(prettier/prettier)
[error] 670-673: Replace ⏎········
${API_RESPONSES.SERVER_ERROR},⏎········
Error:·${e.message},⏎······)
with ``${API_RESPONSES.SERVER_ERROR},·
Error:·${e.message}`);`
(prettier/prettier)
[error] 758-758: Delete ····
(prettier/prettier)
[error] 769-772: Replace ⏎········
${API_RESPONSES.SERVER_ERROR},⏎········
Error:·${e.message},⏎······)
with ``${API_RESPONSES.SERVER_ERROR},·
Error:·${e.message}`);`
(prettier/prettier)
[error] 793-793: 'tenantCond' is never reassigned. Use 'const' instead.
(prefer-const)
[error] 793-793: Replace ?·
"tenantId"·=·'${tenantId}'·:
"tenantId"·IS·NULL`` with ⏎········?·
"tenantId"·=·'${tenantId}'`⏎········:·`"tenantId"·IS·NULL`;`
(prettier/prettier)
[error] 840-840: Insert ;
(prettier/prettier)
[error] 903-903: Insert ;
(prettier/prettier)
[error] 950-950: Insert ;
(prettier/prettier)
[error] 1052-1055: Replace ⏎········
${API_RESPONSES.SERVER_ERROR},⏎········
Error:·${e.message},⏎······)
with ``${API_RESPONSES.SERVER_ERROR},·
Error:·${e.message}`);`
(prettier/prettier)
[error] 1281-1284: Replace ⏎········
${API_RESPONSES.SERVER_ERROR},⏎········
Error:·${e.message},⏎······)
with ``${API_RESPONSES.SERVER_ERROR},·
Error:·${e.message}`);`
(prettier/prettier)
[error] 1407-1410: Replace ⏎········
${API_RESPONSES.SERVER_ERROR},⏎········
Error:·${e.message},⏎······)
with ``${API_RESPONSES.SERVER_ERROR},·
Error:·${e.message}`);`
(prettier/prettier)
[error] 1468-1468: Delete ··
(prettier/prettier)
[error] 1469-1469: Delete ··
(prettier/prettier)
[error] 1653-1656: Replace ⏎········
${API_RESPONSES.SERVER_ERROR},⏎········
Error:·${e.message},⏎······)
with ``${API_RESPONSES.SERVER_ERROR},·
Error:·${e.message}`);`
(prettier/prettier)
🪛 Biome (1.9.4)
src/adapters/postgres/user-adapter.ts
[error] 575-575: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
[error] 608-608: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
[error] 690-690: Avoid the delete operator which can impact performance.
Unsafe fix: Use an undefined assignment instead.
(lint/performance/noDelete)
[error] 1262-1262: Unsafe usage of optional chaining.
If it short-circuits with 'undefined' the evaluation will throw TypeError here:
(lint/correctness/noUnsafeOptionalChaining)
[error] 1148-1148: Avoid the use of spread (...
) syntax on accumulators.
Spread syntax should be avoided on accumulators (like those in .reduce
) because it causes a time complexity of O(n^2)
.
Consider methods such as .splice or .push instead.
(lint/performance/noAccumulatingSpread)
[error] 1739-1739: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
[error] 1986-1986: isNaN is unsafe. It attempts a type coercion. Use Number.isNaN instead.
See the MDN documentation for more details.
Unsafe fix: Use Number.isNaN instead.
(lint/suspicious/noGlobalIsNan)
src/adapters/postgres/fields-adapter.ts
[error] 438-438: Avoid the delete operator which can impact performance.
Unsafe fix: Use an undefined assignment instead.
(lint/performance/noDelete)
[error] 575-575: Avoid the delete operator which can impact performance.
Unsafe fix: Use an undefined assignment instead.
(lint/performance/noDelete)
[error] 1434-1434: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 1752-1752: Avoid the delete operator which can impact performance.
Unsafe fix: Use an undefined assignment instead.
(lint/performance/noDelete)
[error] 1753-1753: Avoid the delete operator which can impact performance.
Unsafe fix: Use an undefined assignment instead.
(lint/performance/noDelete)
🔇 Additional comments (4)
src/adapters/postgres/fields-adapter.ts (1)
67-73
:⚠️ Potential issueCritical: SQL injection vulnerability in whereClause construction
The code constructs SQL queries by directly concatenating user input (
requiredData.context
andrequiredData.contextType
), making it vulnerable to SQL injection attacks.Use parameterized queries instead:
-whereClause += ` OR context = '${requiredData.context}' AND "contextType" IS NULL`; +whereClause += ` OR context = $1 AND "contextType" IS NULL`; -whereClause += ` OR "contextType" = '${requiredData.contextType}'`; +whereClause += ` OR "contextType" = $2`; -const data = await this.getFieldData(whereClause); +const data = await this.getFieldData(whereClause, [requiredData.context, requiredData.contextType]);Likely invalid or redundant comment.
src/adapters/userservicelocator.ts (2)
1-8
: LGTM! Well-organized imports.The import statements are properly organized and follow consistent conventions.
🧰 Tools
🪛 ESLint
[error] 4-4: Replace
·ExistUserDto,·SuggestUserDto,·UserSearchDto·
with⏎··ExistUserDto,⏎··SuggestUserDto,⏎··UserSearchDto,⏎
(prettier/prettier)
19-22
: 🛠️ Refactor suggestionImprove type safety in method signatures.
The method signatures have inconsistent return type declarations and use of
any
types:-getUsersDetailsById(userData: UserData, response: any); -findUserDetails(userID: any, username: string, tenantId?: string); +getUsersDetailsById(userData: UserData, response: Response): Promise<void>; +findUserDetails(userID: string, username: string, tenantId?: string): Promise<User | false>;Likely invalid or redundant comment.
🧰 Tools
🪛 ESLint
[error] 21-21: Replace
request:·any,·userDto:·UserCreateDto,·academicYearId:·string,·response:·Response
with⏎····request:·any,⏎····userDto:·UserCreateDto,⏎····academicYearId:·string,⏎····response:·Response⏎··
(prettier/prettier)
src/adapters/postgres/user-adapter.ts (1)
1986-1987
:⚠️ Potential issueReplace
isNaN
withNumber.isNaN
.Using the global
isNaN
function can lead to unexpected results due to type coercion.-if (!hashValue || !expires || isNaN(parseInt(expires))) { +if (!hashValue || !expires || Number.isNaN(parseInt(expires))) {Likely invalid or redundant comment.
🧰 Tools
🪛 Biome (1.9.4)
[error] 1986-1986: isNaN is unsafe. It attempts a type coercion. Use Number.isNaN instead.
See the MDN documentation for more details.
Unsafe fix: Use Number.isNaN instead.(lint/suspicious/noGlobalIsNan)
src/common/utils/api-id.config.ts
Outdated
SEND_OTP: "api.send.OTP", | ||
VERIFY_OTP: "api.verify.OTP", | ||
SEND_RESET_OTP: 'api.send.reset.otp' |
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.
🛠️ Refactor suggestion
Fix formatting issues and standardize OTP endpoint patterns.
- Line 59 uses single quotes instead of double quotes
- Missing trailing comma after the last property
- Inconsistent casing in OTP endpoints (
OTP
vsotp
)
Apply this diff to fix the issues:
SEND_OTP: "api.send.OTP",
VERIFY_OTP: "api.verify.OTP",
- SEND_RESET_OTP: 'api.send.reset.otp'
+ SEND_RESET_OTP: "api.send.reset.otp",
Consider standardizing the OTP casing:
- SEND_OTP: "api.send.OTP",
- VERIFY_OTP: "api.verify.OTP",
- SEND_RESET_OTP: "api.send.reset.otp",
+ SEND_OTP: "api.send.otp",
+ VERIFY_OTP: "api.verify.otp",
+ SEND_RESET_OTP: "api.send.reset.otp",
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
SEND_OTP: "api.send.OTP", | |
VERIFY_OTP: "api.verify.OTP", | |
SEND_RESET_OTP: 'api.send.reset.otp' | |
SEND_OTP: "api.send.otp", | |
VERIFY_OTP: "api.verify.otp", | |
SEND_RESET_OTP: "api.send.reset.otp", |
🧰 Tools
🪛 ESLint
[error] 59-59: Replace 'api.send.reset.otp'
with "api.send.reset.otp",
(prettier/prettier)
let createSourceFields = `INSERT INTO public.${tableName} ("name", "value", "createdBy"`; | ||
|
||
// Add controllingfieldfk to the columns if it is defined | ||
if (controllingfieldfk !== undefined && controllingfieldfk !== "") { | ||
createSourceFields += `, controllingfieldfk`; | ||
} | ||
|
||
createSourceFields += `) VALUES ('${name}', '${value}', '${createdBy}'`; |
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.
Critical: SQL injection vulnerabilities in table operations
Both createSourceDetailsTableFields
and updateSourceDetailsTableFields
methods construct SQL queries by directly concatenating user inputs, making them vulnerable to SQL injection.
Use TypeORM's QueryBuilder or parameterized queries:
// For createSourceDetailsTableFields
const queryBuilder = this.fieldsValuesRepository.createQueryBuilder()
.insert()
.into(tableName)
.values({
name: name,
value: value,
createdBy: createdBy,
...(controllingfieldfk && { controllingfieldfk })
});
// For updateSourceDetailsTableFields
const queryBuilder = this.fieldsValuesRepository.createQueryBuilder()
.update(tableName)
.set({
name: name,
updatedBy: updatedBy,
...(controllingfieldfk && { controllingfieldfk })
})
.where("value = :value", { value });
Also applies to: 723-729
} | ||
delete fieldsData.fieldParams; |
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.
🛠️ Refactor suggestion
Improve performance by avoiding the delete operator
Using the delete
operator can impact performance as it forces the JavaScript engine to reoptimize the object structure.
Replace delete operations with undefined assignments:
-delete fieldsData.fieldParams;
+fieldsData.fieldParams = undefined;
-delete data.fieldParams;
-delete data.sourceDetails;
+data.fieldParams = undefined;
+data.sourceDetails = undefined;
Also applies to: 575-575, 1752-1753
🧰 Tools
🪛 Biome (1.9.4)
[error] 438-438: Avoid the delete operator which can impact performance.
Unsafe fix: Use an undefined assignment instead.
(lint/performance/noDelete)
} catch (e) { | ||
LoggerUtil.error( | ||
`${API_RESPONSES.SERVER_ERROR}`, | ||
`Error: ${e.message}`, | ||
) | ||
return new ErrorResponse({ | ||
errorCode: "400", | ||
errorMessage: e, | ||
}); | ||
} |
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.
🛠️ Refactor suggestion
Standardize error handling patterns
The file uses inconsistent error handling patterns. Some methods return ErrorResponse
while others use APIResponse.error()
.
Standardize error handling across all methods:
try {
// Method logic
} catch (error) {
LoggerUtil.error(
API_RESPONSES.SERVER_ERROR,
`Error: ${error.message}`,
apiId
);
return APIResponse.error(
response,
apiId,
API_RESPONSES.SERVER_ERROR,
error.message || API_RESPONSES.SERVER_ERROR,
HttpStatus.INTERNAL_SERVER_ERROR
);
}
Also applies to: 873-912
🧰 Tools
🪛 ESLint
[error] 1052-1055: Replace ⏎········
${API_RESPONSES.SERVER_ERROR},⏎········
Error:·${e.message},⏎······)
with ``${API_RESPONSES.SERVER_ERROR},·
Error:·${e.message}`);`
(prettier/prettier)
ACADEMICYEAR_EXIST: "Academic Year Already Exist", | ||
ACADEMICYEAR_YEAR: "Already Exist", |
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.
🛠️ Refactor suggestion
Fix inconsistent academic year messages.
There are duplicate and inconsistent messages for academic year not found scenarios.
Consolidate the duplicate messages:
- ACADEMICYEAR_EXIST: "Academic Year Already Exist",
- ACADEMICYEAR_YEAR: "Already Exist",
- ACADEMICYEAR_NOTFOUND: "Academic Year Not Found",
+ ACADEMIC_YEAR_EXISTS: "Academic Year already exists",
+ ACADEMIC_YEAR_NOT_FOUND: "Academic Year not found",
Also applies to: 26-26
USERNAME_REQUIRED: 'Username Required', | ||
INVALID_REASON: 'Invalid Reason', | ||
MOBILE_REQUIRED: 'MObile Required', | ||
INVALID_HASH_FORMAT: 'Invalid hash format', |
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.
Remove duplicate message definition.
The INVALID_HASH_FORMAT
message is defined twice with different casing.
INVALID_HASH_FORMATE: 'Invalid hash format',
- INVALID_HASH_FORMAT: 'Invalid hash format',
+ INVALID_HASH_FORMAT: "Invalid hash format",
Also applies to: 173-173
🧰 Tools
🪛 ESLint
[error] 179-179: Replace 'Invalid·hash·format'
with "Invalid·hash·format"
(prettier/prettier)
//get User Details | ||
USER_GET_SUCCESSFULLY: 'User details fetched successfully.', | ||
USER_GET_BY_EMAIL_SUCCESSFULLY: 'User details fetched successfully by email', | ||
USER_GET_BY_PHONE_SUCCESSFULLY: 'User details fetched successfully by phone', | ||
USER_GET_BY_USERNAME_SUCCESSFULLY: 'User details fetched successfully by username', | ||
USER_GET_BY_TENANT_ID_SUCCESSFULLY: 'User details fetched successfully by tenantId', | ||
USER_GET_BY_USER_ID_SUCCESSFULLY: 'User details fetched successfully by userId', | ||
USER_GET_BY_USER_ID_AND_TENANT_ID_SUCCESSFULLY: 'User details fetched successfully by userId and tenantId', | ||
USER_GET_BY_EMAIL_AND_TENANT_ID_SUCCESSFULLY: 'User details fetched successfully by email and tenantId', | ||
USER_CREATE_KEYCLOAK: 'User created successfully on keycloak', | ||
USERNAME_EXISTS_KEYCLOAK: 'Username is already exists in keycloak', | ||
UPDATE_USER_KEYCLOAK_ERROR:'Failed to update username details in Keycloak.', | ||
USERNAME_SUGGEST_SUCCESSFULLY:'Username is already taken. Suggested a new unique username.', | ||
|
||
//Create user | ||
USER_CREATE_SUCCESSFULLY: `User created successfully`, | ||
USER_CREATE_IN_DB: 'User created in user table successfully', | ||
USER_CREATE_FAILED: 'User creation failed', | ||
USER_CREATE_FAILED_WITH_ERROR: (error) => `User creation failed with error: ${error}`, | ||
USER_CREATE_FAILED_WITH_ERROR_AND_EMAIL: (error, email) => `User creation failed with error: ${error}. Email: ${email}`, | ||
USER_CREATE_FAILED_WITH_ERROR_AND_PHONE: (error, phone) => `User creation failed with error: ${error}. Phone: ${phone}`, | ||
USER_CREATE_FAILED_WITH_ERROR_AND_USERNAME: (error, username) => `User creation failed with error: ${error}. Username: ${username}`, | ||
USERID_NOT_FOUND: (userId) => `User Id '${userId}' does not exist.`, | ||
TENANTID_NOT_FOUND: (tenantId) => `Tenant Id '${tenantId}' does not exist.`, | ||
|
||
|
||
//UUID constants | ||
UUID_VALIDATION: 'Please enter valid UUID', | ||
INVALID_EMAIL: (emailId) => `Invalid email address: ${emailId}`, | ||
MOBILE_NO_CHECK: (mobileNo) => `Mobile number must be 10 digits long: ${mobileNo}`, | ||
DOB_FORMAT: (dob) => `Date of birth must be in the format yyyy-mm-dd: ${dob}`, | ||
INVALID_USERNAME_EMAIL: `Invalid Username Or Email`, | ||
USER_RELATEDENTITY_DELETE: `User and related entries deleted Successfully.`, | ||
|
||
ACADEMIC_YEAR_NOT_FOUND: 'Academic year not found for tenant', | ||
DUPLICAT_TENANTID: "Duplicate tenantId detected. Please ensure each tenantId is unique and correct your data.", | ||
INVALID_PARAMETERS: 'Invalid parameters provided. Please ensure that tenantId, roleId, and cohortId (if applicable) are correctly provided.', | ||
COHORT_NOT_FOUND_IN_TENANT_ID: (cohortId, TenantId) => `Cohort Id '${cohortId}' does not exist for this tenant '${TenantId}'.`, | ||
|
||
ROLE_NOT_FOUND_IN_TENANT: (roleId, tenantId) => `Role Id '${roleId}' does not exist for this tenant '${tenantId}'.`, | ||
USER_EXISTS_SEND_MAIL: "User Exists. Proceed with Sending Email.", | ||
INVALID_FIELD: (invalidateFields) => `Invalid fields found: ${invalidateFields}`, | ||
DUPLICATE_FIELD: (duplicateFieldKeys) => `Duplicate fieldId detected: ${duplicateFieldKeys}`, | ||
FIELD_NOT_FOUND: 'Field not found', | ||
PASSWORD_RESET: 'Password reset successful!', | ||
|
||
SOMETHING_WRONG: "Something went wrong", | ||
USER_PASSWORD_UPDATE: "User Password Updated Successfully", | ||
USER_BASIC_DETAILS_UPDATE: "User basic details updated successfully", | ||
USER_TENANT: "User tenant mapping successfully", | ||
USER_COHORT: "User cohort mapping successfully", | ||
COHORT_NAME_EXIST: 'Cohort name already exist.Please provide another name.', | ||
|
||
COHORT_LIST: "Cohort list fetched successfully", | ||
COHORT_HIERARCHY: "Cohort hierarchy fetched successfully", | ||
COHORT_EXISTS: 'Cohort already exists', | ||
CREATE_COHORT: "Cohort Created Successfully.", | ||
COHORT_FIELD_DETAILS: 'Fetch cohort custom field details', | ||
CHILD_DATA: 'Get all child data response', | ||
COHORT_DATA_RESPONSE: 'Fetch cohort data response', | ||
COHORT_UPDATED_SUCCESSFULLY: 'Cohort updated successfully.', | ||
TENANT_NOTFOUND: 'Tenant not found', | ||
COHORTMEMBER_UPDATE_SUCCESSFULLY: "Cohort Member updated Successfully", | ||
|
||
//Tenant | ||
TENANT_GET: "Tenant fetched successfully.", | ||
TENANT_NOT_FOUND: "No tenants found matching the specified criteria.", | ||
TENANT_EXISTS: "Tenant already exists", | ||
TENANT_CREATE: "Tenant created successfully", | ||
TENANT_UPDATE: "Tenant updated successfully", | ||
TENANT_DELETE: "Tenant deleted successfully", | ||
TENANT_SEARCH_SUCCESS: "Tenant search successfully", | ||
TENANT_CREATE_FAILED: "Failed to create tenant, please try again.", | ||
REQUIRED_AND_UUID: "tenantId is required and it's must be a valid UUID.", | ||
|
||
|
||
//OTP | ||
NOTIFICATION_FAIL_DURING_OTP_SEND: "Send SMS notification failed duing OTP send", | ||
OTP_SEND_SUCCESSFULLY: "OTP sent successfully", | ||
OTP_EXPIRED: 'OTP has expired', | ||
OTP_INVALID: "OTP invalid", | ||
OTP_VALID: 'OTP validation Sucessfully', | ||
MOBILE_VALID: 'Invalid mobile number. Must be 10 digits.', | ||
OTP_VALIDED_REQUIRED_KEY: 'Missing required fields', | ||
INVALID_HASH_FORMATE: 'Invalid hash format', | ||
SMS_ERROR: 'SMS notification failed', | ||
SMS_NOTIFICATION_ERROR: 'Failed to send SMS notification:', | ||
USERNAME_REQUIRED: 'Username Required', | ||
INVALID_REASON: 'Invalid Reason', | ||
MOBILE_REQUIRED: 'MObile Required', | ||
INVALID_HASH_FORMAT: 'Invalid hash format', | ||
NOTIFICATION_ERROR: 'Notification not send due to getting from notification API', | ||
MOBILE_EMAIL_NOT_FOUND: 'Mobile number and email ID not found for sending OTP', | ||
MOBILE_SENT_OTP: 'OTP sent successfully to mobile', | ||
MOBILE_OTP_SEND_FAILED: 'Failed to send OTP to mobile', | ||
EMAIL_SENT_OTP: 'OTP sent successfully to email', | ||
EMAIL_OTP_SEND_FAILED: 'Failed to send OTP to email', | ||
SEND_OTP: 'OTP sent successfully', | ||
EMAIL_NOTIFICATION_ERROR: 'Failed to send Email notification:', | ||
EMAIL_ERROR: 'Email notification failed' |
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.
🛠️ Refactor suggestion
Fix inconsistent string quotes usage.
The file mixes single and double quotes for string literals. According to the static analysis hints, double quotes are preferred.
Replace all single quotes with double quotes for consistency. Here's an example of the changes needed:
- UNAUTHORIZED: 'Unauthorized',
+ UNAUTHORIZED: "Unauthorized",
- USER_UPDATED_SUCCESSFULLY: 'User updated successfully.',
+ USER_UPDATED_SUCCESSFULLY: "User updated successfully.",
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
UNAUTHORIZED: 'Unauthorized', | |
INVALID_TOKEN: 'Token Invalid', | |
//User Api messages | |
USER_UPDATED_SUCCESSFULLY: 'User updated successfully.', | |
USER_NOT_EXISTS: 'User does not exist.', | |
USER_EXISTS: 'User already exist.', | |
DUPLICATE_DATA: 'Duplicate data.', | |
USER_ALREADY_EXISTS: 'User already exists.', | |
SERVER_ERROR: 'Internal server error', | |
SERVICE_NAME: 'User service', | |
USER_NOT_FOUND_FOR_DELETE: 'User not found for delete.', | |
USER_NOT_FOUND_FOR_PASSWORD_RESET: 'User not found for password reset.', | |
//get User Details | |
USER_GET_SUCCESSFULLY: 'User details fetched successfully.', | |
USER_GET_BY_EMAIL_SUCCESSFULLY: 'User details fetched successfully by email', | |
USER_GET_BY_PHONE_SUCCESSFULLY: 'User details fetched successfully by phone', | |
USER_GET_BY_USERNAME_SUCCESSFULLY: 'User details fetched successfully by username', | |
USER_GET_BY_TENANT_ID_SUCCESSFULLY: 'User details fetched successfully by tenantId', | |
USER_GET_BY_USER_ID_SUCCESSFULLY: 'User details fetched successfully by userId', | |
USER_GET_BY_USER_ID_AND_TENANT_ID_SUCCESSFULLY: 'User details fetched successfully by userId and tenantId', | |
USER_GET_BY_EMAIL_AND_TENANT_ID_SUCCESSFULLY: 'User details fetched successfully by email and tenantId', | |
USER_CREATE_KEYCLOAK: 'User created successfully on keycloak', | |
USERNAME_EXISTS_KEYCLOAK: 'Username is already exists in keycloak', | |
UPDATE_USER_KEYCLOAK_ERROR:'Failed to update username details in Keycloak.', | |
USERNAME_SUGGEST_SUCCESSFULLY:'Username is already taken. Suggested a new unique username.', | |
//Create user | |
USER_CREATE_SUCCESSFULLY: `User created successfully`, | |
USER_CREATE_IN_DB: 'User created in user table successfully', | |
USER_CREATE_FAILED: 'User creation failed', | |
USER_CREATE_FAILED_WITH_ERROR: (error) => `User creation failed with error: ${error}`, | |
USER_CREATE_FAILED_WITH_ERROR_AND_EMAIL: (error, email) => `User creation failed with error: ${error}. Email: ${email}`, | |
USER_CREATE_FAILED_WITH_ERROR_AND_PHONE: (error, phone) => `User creation failed with error: ${error}. Phone: ${phone}`, | |
USER_CREATE_FAILED_WITH_ERROR_AND_USERNAME: (error, username) => `User creation failed with error: ${error}. Username: ${username}`, | |
USERID_NOT_FOUND: (userId) => `User Id '${userId}' does not exist.`, | |
TENANTID_NOT_FOUND: (tenantId) => `Tenant Id '${tenantId}' does not exist.`, | |
//UUID constants | |
UUID_VALIDATION: 'Please enter valid UUID', | |
INVALID_EMAIL: (emailId) => `Invalid email address: ${emailId}`, | |
MOBILE_NO_CHECK: (mobileNo) => `Mobile number must be 10 digits long: ${mobileNo}`, | |
DOB_FORMAT: (dob) => `Date of birth must be in the format yyyy-mm-dd: ${dob}`, | |
INVALID_USERNAME_EMAIL: `Invalid Username Or Email`, | |
USER_RELATEDENTITY_DELETE: `User and related entries deleted Successfully.`, | |
ACADEMIC_YEAR_NOT_FOUND: 'Academic year not found for tenant', | |
DUPLICAT_TENANTID: "Duplicate tenantId detected. Please ensure each tenantId is unique and correct your data.", | |
INVALID_PARAMETERS: 'Invalid parameters provided. Please ensure that tenantId, roleId, and cohortId (if applicable) are correctly provided.', | |
COHORT_NOT_FOUND_IN_TENANT_ID: (cohortId, TenantId) => `Cohort Id '${cohortId}' does not exist for this tenant '${TenantId}'.`, | |
ROLE_NOT_FOUND_IN_TENANT: (roleId, tenantId) => `Role Id '${roleId}' does not exist for this tenant '${tenantId}'.`, | |
USER_EXISTS_SEND_MAIL: "User Exists. Proceed with Sending Email.", | |
INVALID_FIELD: (invalidateFields) => `Invalid fields found: ${invalidateFields}`, | |
DUPLICATE_FIELD: (duplicateFieldKeys) => `Duplicate fieldId detected: ${duplicateFieldKeys}`, | |
FIELD_NOT_FOUND: 'Field not found', | |
PASSWORD_RESET: 'Password reset successful!', | |
SOMETHING_WRONG: "Something went wrong", | |
USER_PASSWORD_UPDATE: "User Password Updated Successfully", | |
USER_BASIC_DETAILS_UPDATE: "User basic details updated successfully", | |
USER_TENANT: "User tenant mapping successfully", | |
USER_COHORT: "User cohort mapping successfully", | |
COHORT_NAME_EXIST: 'Cohort name already exist.Please provide another name.', | |
COHORT_LIST: "Cohort list fetched successfully", | |
COHORT_HIERARCHY: "Cohort hierarchy fetched successfully", | |
COHORT_EXISTS: 'Cohort already exists', | |
CREATE_COHORT: "Cohort Created Successfully.", | |
COHORT_FIELD_DETAILS: 'Fetch cohort custom field details', | |
CHILD_DATA: 'Get all child data response', | |
COHORT_DATA_RESPONSE: 'Fetch cohort data response', | |
COHORT_UPDATED_SUCCESSFULLY: 'Cohort updated successfully.', | |
TENANT_NOTFOUND: 'Tenant not found', | |
COHORTMEMBER_UPDATE_SUCCESSFULLY: "Cohort Member updated Successfully", | |
//Tenant | |
TENANT_GET: "Tenant fetched successfully.", | |
TENANT_NOT_FOUND: "No tenants found matching the specified criteria.", | |
TENANT_EXISTS: "Tenant already exists", | |
TENANT_CREATE: "Tenant created successfully", | |
TENANT_UPDATE: "Tenant updated successfully", | |
TENANT_DELETE: "Tenant deleted successfully", | |
TENANT_SEARCH_SUCCESS: "Tenant search successfully", | |
TENANT_CREATE_FAILED: "Failed to create tenant, please try again.", | |
REQUIRED_AND_UUID: "tenantId is required and it's must be a valid UUID.", | |
//OTP | |
NOTIFICATION_FAIL_DURING_OTP_SEND: "Send SMS notification failed duing OTP send", | |
OTP_SEND_SUCCESSFULLY: "OTP sent successfully", | |
OTP_EXPIRED: 'OTP has expired', | |
OTP_INVALID: "OTP invalid", | |
OTP_VALID: 'OTP validation Sucessfully', | |
MOBILE_VALID: 'Invalid mobile number. Must be 10 digits.', | |
OTP_VALIDED_REQUIRED_KEY: 'Missing required fields', | |
INVALID_HASH_FORMATE: 'Invalid hash format', | |
SMS_ERROR: 'SMS notification failed', | |
SMS_NOTIFICATION_ERROR: 'Failed to send SMS notification:', | |
USERNAME_REQUIRED: 'Username Required', | |
INVALID_REASON: 'Invalid Reason', | |
MOBILE_REQUIRED: 'MObile Required', | |
INVALID_HASH_FORMAT: 'Invalid hash format', | |
NOTIFICATION_ERROR: 'Notification not send due to getting from notification API', | |
MOBILE_EMAIL_NOT_FOUND: 'Mobile number and email ID not found for sending OTP', | |
MOBILE_SENT_OTP: 'OTP sent successfully to mobile', | |
MOBILE_OTP_SEND_FAILED: 'Failed to send OTP to mobile', | |
EMAIL_SENT_OTP: 'OTP sent successfully to email', | |
EMAIL_OTP_SEND_FAILED: 'Failed to send OTP to email', | |
SEND_OTP: 'OTP sent successfully', | |
EMAIL_NOTIFICATION_ERROR: 'Failed to send Email notification:', | |
EMAIL_ERROR: 'Email notification failed' | |
UNAUTHORIZED: "Unauthorized", | |
INVALID_TOKEN: "Token Invalid", | |
//User Api messages | |
USER_UPDATED_SUCCESSFULLY: "User updated successfully.", | |
USER_NOT_EXISTS: "User does not exist.", | |
USER_EXISTS: "User already exist.", | |
DUPLICATE_DATA: "Duplicate data.", | |
USER_ALREADY_EXISTS: "User already exists.", | |
SERVER_ERROR: "Internal server error", | |
SERVICE_NAME: "User service", | |
USER_NOT_FOUND_FOR_DELETE: "User not found for delete.", | |
USER_NOT_FOUND_FOR_PASSWORD_RESET: "User not found for password reset.", | |
//get User Details | |
USER_GET_SUCCESSFULLY: "User details fetched successfully.", | |
USER_GET_BY_EMAIL_SUCCESSFULLY: "User details fetched successfully by email", | |
USER_GET_BY_PHONE_SUCCESSFULLY: "User details fetched successfully by phone", | |
USER_GET_BY_USERNAME_SUCCESSFULLY: "User details fetched successfully by username", | |
USER_GET_BY_TENANT_ID_SUCCESSFULLY: "User details fetched successfully by tenantId", | |
USER_GET_BY_USER_ID_SUCCESSFULLY: "User details fetched successfully by userId", | |
USER_GET_BY_USER_ID_AND_TENANT_ID_SUCCESSFULLY: "User details fetched successfully by userId and tenantId", | |
USER_GET_BY_EMAIL_AND_TENANT_ID_SUCCESSFULLY: "User details fetched successfully by email and tenantId", | |
USER_CREATE_KEYCLOAK: "User created successfully on keycloak", | |
USERNAME_EXISTS_KEYCLOAK: "Username is already exists in keycloak", | |
UPDATE_USER_KEYCLOAK_ERROR: "Failed to update username details in Keycloak.", | |
USERNAME_SUGGEST_SUCCESSFULLY: "Username is already taken. Suggested a new unique username.", | |
//Create user | |
USER_CREATE_SUCCESSFULLY: "User created successfully", | |
USER_CREATE_IN_DB: "User created in user table successfully", | |
USER_CREATE_FAILED: "User creation failed", | |
USER_CREATE_FAILED_WITH_ERROR: (error) => `User creation failed with error: ${error}`, | |
USER_CREATE_FAILED_WITH_ERROR_AND_EMAIL: (error, email) => `User creation failed with error: ${error}. Email: ${email}`, | |
USER_CREATE_FAILED_WITH_ERROR_AND_PHONE: (error, phone) => `User creation failed with error: ${error}. Phone: ${phone}`, | |
USER_CREATE_FAILED_WITH_ERROR_AND_USERNAME: (error, username) => `User creation failed with error: ${error}. Username: ${username}`, | |
USERID_NOT_FOUND: (userId) => `User Id '${userId}' does not exist.`, | |
TENANTID_NOT_FOUND: (tenantId) => `Tenant Id '${tenantId}' does not exist.`, | |
//UUID constants | |
UUID_VALIDATION: "Please enter valid UUID", | |
INVALID_EMAIL: (emailId) => `Invalid email address: ${emailId}`, | |
MOBILE_NO_CHECK: (mobileNo) => `Mobile number must be 10 digits long: ${mobileNo}`, | |
DOB_FORMAT: (dob) => `Date of birth must be in the format yyyy-mm-dd: ${dob}`, | |
INVALID_USERNAME_EMAIL: "Invalid Username Or Email", | |
USER_RELATEDENTITY_DELETE: "User and related entries deleted Successfully.", | |
ACADEMIC_YEAR_NOT_FOUND: "Academic year not found for tenant", | |
DUPLICAT_TENANTID: "Duplicate tenantId detected. Please ensure each tenantId is unique and correct your data.", | |
INVALID_PARAMETERS: "Invalid parameters provided. Please ensure that tenantId, roleId, and cohortId (if applicable) are correctly provided.", | |
COHORT_NOT_FOUND_IN_TENANT_ID: (cohortId, TenantId) => `Cohort Id '${cohortId}' does not exist for this tenant '${TenantId}'.`, | |
ROLE_NOT_FOUND_IN_TENANT: (roleId, tenantId) => `Role Id '${roleId}' does not exist for this tenant '${tenantId}'.`, | |
USER_EXISTS_SEND_MAIL: "User Exists. Proceed with Sending Email.", | |
INVALID_FIELD: (invalidateFields) => `Invalid fields found: ${invalidateFields}`, | |
DUPLICATE_FIELD: (duplicateFieldKeys) => `Duplicate fieldId detected: ${duplicateFieldKeys}`, | |
FIELD_NOT_FOUND: "Field not found", | |
PASSWORD_RESET: "Password reset successful!", | |
SOMETHING_WRONG: "Something went wrong", | |
USER_PASSWORD_UPDATE: "User Password Updated Successfully", | |
USER_BASIC_DETAILS_UPDATE: "User basic details updated successfully", | |
USER_TENANT: "User tenant mapping successfully", | |
USER_COHORT: "User cohort mapping successfully", | |
COHORT_NAME_EXIST: "Cohort name already exist.Please provide another name.", | |
COHORT_LIST: "Cohort list fetched successfully", | |
COHORT_HIERARCHY: "Cohort hierarchy fetched successfully", | |
COHORT_EXISTS: "Cohort already exists", | |
CREATE_COHORT: "Cohort Created Successfully.", | |
COHORT_FIELD_DETAILS: "Fetch cohort custom field details", | |
CHILD_DATA: "Get all child data response", | |
COHORT_DATA_RESPONSE: "Fetch cohort data response", | |
COHORT_UPDATED_SUCCESSFULLY: "Cohort updated successfully.", | |
TENANT_NOTFOUND: "Tenant not found", | |
COHORTMEMBER_UPDATE_SUCCESSFULLY: "Cohort Member updated Successfully", | |
//Tenant | |
TENANT_GET: "Tenant fetched successfully.", | |
TENANT_NOT_FOUND: "No tenants found matching the specified criteria.", | |
TENANT_EXISTS: "Tenant already exists", | |
TENANT_CREATE: "Tenant created successfully", | |
TENANT_UPDATE: "Tenant updated successfully", | |
TENANT_DELETE: "Tenant deleted successfully", | |
TENANT_SEARCH_SUCCESS: "Tenant search successfully", | |
TENANT_CREATE_FAILED: "Failed to create tenant, please try again.", | |
REQUIRED_AND_UUID: "tenantId is required and it's must be a valid UUID.", | |
//OTP | |
NOTIFICATION_FAIL_DURING_OTP_SEND: "Send SMS notification failed duing OTP send", | |
OTP_SEND_SUCCESSFULLY: "OTP sent successfully", | |
OTP_EXPIRED: "OTP has expired", | |
OTP_INVALID: "OTP invalid", | |
OTP_VALID: "OTP validation Sucessfully", | |
MOBILE_VALID: "Invalid mobile number. Must be 10 digits.", | |
OTP_VALIDED_REQUIRED_KEY: "Missing required fields", | |
INVALID_HASH_FORMATE: "Invalid hash format", | |
SMS_ERROR: "SMS notification failed", | |
SMS_NOTIFICATION_ERROR: "Failed to send SMS notification:", | |
USERNAME_REQUIRED: "Username Required", | |
INVALID_REASON: "Invalid Reason", | |
MOBILE_REQUIRED: "MObile Required", | |
INVALID_HASH_FORMAT: "Invalid hash format", | |
NOTIFICATION_ERROR: "Notification not send due to getting from notification API", | |
MOBILE_EMAIL_NOT_FOUND: "Mobile number and email ID not found for sending OTP", | |
MOBILE_SENT_OTP: "OTP sent successfully to mobile", | |
MOBILE_OTP_SEND_FAILED: "Failed to send OTP to mobile", | |
EMAIL_SENT_OTP: "OTP sent successfully to email", | |
EMAIL_OTP_SEND_FAILED: "Failed to send OTP to email", | |
SEND_OTP: "OTP sent successfully", | |
EMAIL_NOTIFICATION_ERROR: "Failed to send Email notification:", | |
EMAIL_ERROR: "Email notification failed" |
🧰 Tools
🪛 ESLint
[error] 73-73: Replace 'Unauthorized'
with "Unauthorized"
(prettier/prettier)
[error] 74-74: Replace 'Token·Invalid'
with "Token·Invalid"
(prettier/prettier)
[error] 77-77: Replace 'User·updated·successfully.'
with "User·updated·successfully."
(prettier/prettier)
[error] 78-78: Replace 'User·does·not·exist.'
with "User·does·not·exist."
(prettier/prettier)
[error] 79-79: Replace 'User·already·exist.'
with "User·already·exist."
(prettier/prettier)
[error] 80-80: Replace 'Duplicate·data.'
with "Duplicate·data."
(prettier/prettier)
[error] 81-81: Replace 'User·already·exists.'
with "User·already·exists."
(prettier/prettier)
[error] 82-82: Replace 'Internal·server·error'
with "Internal·server·error"
(prettier/prettier)
[error] 83-84: Replace 'User·service',⏎
with "User·service",
(prettier/prettier)
[error] 86-86: Replace 'User·not·found·for·delete.'
with "User·not·found·for·delete."
(prettier/prettier)
[error] 87-87: Replace 'User·not·found·for·password·reset.'
with "User·not·found·for·password·reset."
(prettier/prettier)
[error] 90-90: Replace 'User·details·fetched·successfully.'
with "User·details·fetched·successfully."
(prettier/prettier)
[error] 91-91: Replace 'User·details·fetched·successfully·by·email'
with "User·details·fetched·successfully·by·email"
(prettier/prettier)
[error] 92-92: Replace 'User·details·fetched·successfully·by·phone'
with "User·details·fetched·successfully·by·phone"
(prettier/prettier)
[error] 93-93: Replace ·'User·details·fetched·successfully·by·username'
with ⏎····"User·details·fetched·successfully·by·username"
(prettier/prettier)
[error] 94-94: Replace ·'User·details·fetched·successfully·by·tenantId'
with ⏎····"User·details·fetched·successfully·by·tenantId"
(prettier/prettier)
[error] 95-95: Replace ·'User·details·fetched·successfully·by·userId'
with ⏎····"User·details·fetched·successfully·by·userId"
(prettier/prettier)
[error] 96-96: Replace ·'User·details·fetched·successfully·by·userId·and·tenantId'
with ⏎····"User·details·fetched·successfully·by·userId·and·tenantId"
(prettier/prettier)
[error] 97-97: Replace ·'User·details·fetched·successfully·by·email·and·tenantId'
with ⏎····"User·details·fetched·successfully·by·email·and·tenantId"
(prettier/prettier)
[error] 98-98: Replace 'User·created·successfully·on·keycloak'
with "User·created·successfully·on·keycloak"
(prettier/prettier)
[error] 99-99: Replace 'Username·is·already·exists·in·keycloak'
with "Username·is·already·exists·in·keycloak"
(prettier/prettier)
[error] 100-100: Replace 'Failed·to·update·username·details·in·Keycloak.'
with ·"Failed·to·update·username·details·in·Keycloak."
(prettier/prettier)
[error] 101-101: Replace 'Username·is·already·taken.·Suggested·a·new·unique·username.'
with ⏎····"Username·is·already·taken.·Suggested·a·new·unique·username."
(prettier/prettier)
[error] 105-105: Replace 'User·created·in·user·table·successfully'
with "User·created·in·user·table·successfully"
(prettier/prettier)
[error] 106-106: Replace 'User·creation·failed'
with "User·creation·failed"
(prettier/prettier)
[error] 107-107: Insert ⏎···
(prettier/prettier)
[error] 108-108: Insert ⏎···
(prettier/prettier)
[error] 109-109: Insert ⏎···
(prettier/prettier)
[error] 110-110: Insert ⏎···
(prettier/prettier)
[error] 114-115: Delete ⏎
(prettier/prettier)
[error] 116-116: Replace 'Please·enter·valid·UUID'
with "Please·enter·valid·UUID"
(prettier/prettier)
[error] 118-118: Insert ⏎···
(prettier/prettier)
[error] 123-123: Replace 'Academic·year·not·found·for·tenant'
with "Academic·year·not·found·for·tenant"
(prettier/prettier)
[error] 124-124: Insert ⏎···
(prettier/prettier)
[error] 125-125: Replace ·'Invalid·parameters·provided.·Please·ensure·that·tenantId,·roleId,·and·cohortId·(if·applicable)·are·correctly·provided.'
with ⏎····"Invalid·parameters·provided.·Please·ensure·that·tenantId,·roleId,·and·cohortId·(if·applicable)·are·correctly·provided."
(prettier/prettier)
[error] 126-126: Insert ⏎···
(prettier/prettier)
[error] 128-128: Insert ⏎···
(prettier/prettier)
[error] 130-130: Insert ⏎···
(prettier/prettier)
[error] 131-131: Insert ⏎···
(prettier/prettier)
[error] 132-132: Replace 'Field·not·found'
with "Field·not·found"
(prettier/prettier)
[error] 133-133: Replace 'Password·reset·successful!'
with "Password·reset·successful!"
(prettier/prettier)
[error] 140-140: Replace 'Cohort·name·already·exist.Please·provide·another·name.'
with "Cohort·name·already·exist.Please·provide·another·name."
(prettier/prettier)
[error] 144-144: Replace 'Cohort·already·exists'
with "Cohort·already·exists"
(prettier/prettier)
[error] 146-146: Replace 'Fetch·cohort·custom·field·details'
with "Fetch·cohort·custom·field·details"
(prettier/prettier)
[error] 147-147: Replace 'Get·all·child·data·response'
with "Get·all·child·data·response"
(prettier/prettier)
[error] 148-148: Replace 'Fetch·cohort·data·response'
with "Fetch·cohort·data·response"
(prettier/prettier)
[error] 149-149: Replace 'Cohort·updated·successfully.'
with "Cohort·updated·successfully."
(prettier/prettier)
[error] 150-150: Replace 'Tenant·not·found'
with "Tenant·not·found"
(prettier/prettier)
[error] 164-165: Delete ⏎
(prettier/prettier)
[error] 166-166: Insert ⏎···
(prettier/prettier)
[error] 168-168: Replace 'OTP·has·expired'
with "OTP·has·expired"
(prettier/prettier)
[error] 170-170: Replace 'OTP·validation·Sucessfully'
with "OTP·validation·Sucessfully"
(prettier/prettier)
[error] 171-171: Replace 'Invalid·mobile·number.·Must·be·10·digits.'
with "Invalid·mobile·number.·Must·be·10·digits."
(prettier/prettier)
[error] 172-172: Replace 'Missing·required·fields'
with "Missing·required·fields"
(prettier/prettier)
[error] 173-173: Replace 'Invalid·hash·format'
with "Invalid·hash·format"
(prettier/prettier)
[error] 174-174: Replace 'SMS·notification·failed'
with "SMS·notification·failed"
(prettier/prettier)
[error] 175-175: Replace 'Failed·to·send·SMS·notification:'
with "Failed·to·send·SMS·notification:"
(prettier/prettier)
[error] 176-176: Replace 'Username·Required'
with "Username·Required"
(prettier/prettier)
[error] 177-177: Replace 'Invalid·Reason'
with "Invalid·Reason"
(prettier/prettier)
[error] 178-178: Replace 'MObile·Required'
with "MObile·Required"
(prettier/prettier)
[error] 179-179: Replace 'Invalid·hash·format'
with "Invalid·hash·format"
(prettier/prettier)
[error] 180-180: Replace ·'Notification·not·send·due·to·getting·from·notification·API'
with ⏎····"Notification·not·send·due·to·getting·from·notification·API"
(prettier/prettier)
[error] 181-181: Replace ·'Mobile·number·and·email·ID·not·found·for·sending·OTP'
with ⏎····"Mobile·number·and·email·ID·not·found·for·sending·OTP"
(prettier/prettier)
[error] 182-182: Replace 'OTP·sent·successfully·to·mobile'
with "OTP·sent·successfully·to·mobile"
(prettier/prettier)
[error] 183-183: Replace 'Failed·to·send·OTP·to·mobile'
with "Failed·to·send·OTP·to·mobile"
(prettier/prettier)
[error] 184-184: Replace 'OTP·sent·successfully·to·email'
with "OTP·sent·successfully·to·email"
(prettier/prettier)
[error] 185-185: Replace 'Failed·to·send·OTP·to·email'
with "Failed·to·send·OTP·to·email"
(prettier/prettier)
[error] 186-186: Replace 'OTP·sent·successfully'
with "OTP·sent·successfully"
(prettier/prettier)
[error] 187-187: Replace 'Failed·to·send·Email·notification:'
with "Failed·to·send·Email·notification:"
(prettier/prettier)
[error] 188-188: Replace 'Email·notification·failed'
with "Email·notification·failed",
(prettier/prettier)
PS-3707 : Implement addition of custom role in keycloak token
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.
Actionable comments posted: 2
♻️ Duplicate comments (7)
src/common/utils/keycloak.adapter.util.ts (2)
1-4
: 🛠️ Refactor suggestionUse consistent ES6 import syntax
The mix of ES6 imports and CommonJS require statements should be standardized to use ES6 imports throughout.
-import { AxiosError, AxiosRequestConfig, AxiosResponse } from "axios"; +import axios, { AxiosError, AxiosRequestConfig, AxiosResponse } from "axios"; -const axios = require("axios");🧰 Tools
🪛 ESLint
[error] 4-4: Require statement not part of import statement.
(@typescript-eslint/no-var-requires)
104-106
: 🛠️ Refactor suggestionUse LoggerUtil consistently for error logging
Replace console.error calls with LoggerUtil for consistent error handling across the application.
- console.error("Error Response Status:", error.response.status); - console.error("Error Response Data:", error.response.data); - console.error("Error Response Headers:", error.response.headers); + LoggerUtil.error( + `${API_RESPONSES.SERVER_ERROR}`, + `Error Response Status: ${error.response.status}, Data: ${JSON.stringify(error.response.data)}` + );Also applies to: 114-114, 121-121
src/adapters/postgres/user-adapter.ts (5)
1399-1403
:⚠️ Potential issueFix SQL injection vulnerability in query construction.
The query is vulnerable to SQL injection attacks. Use parameterized queries instead:
- let query = `SELECT * FROM public."CohortAcademicYear" WHERE "cohortId"= '${cohortIds}' AND "academicYearId" = '${academicYearId}'`; - let getCohortAcademicYearId = await this.usersRepository.query(query); + const query = `SELECT * FROM public."CohortAcademicYear" WHERE "cohortId" = $1 AND "academicYearId" = $2`; + const getCohortAcademicYearId = await this.usersRepository.query(query, [cohortIds, academicYearId]);🧰 Tools
🪛 ESLint
[error] 1399-1399: 'query' is never reassigned. Use 'const' instead.
(prefer-const)
[error] 1401-1401: 'getCohortAcademicYearId' is never reassigned. Use 'const' instead.
(prefer-const)
1871-1917
: 🛠️ Refactor suggestionEnhance OTP security with rate limiting.
The OTP implementation should include rate limiting to prevent brute force attacks:
private async validateOtpAttempts(identifier: string): Promise<void> { const attempts = await this.getVerificationAttempts(identifier); if (attempts >= MAX_VERIFY_ATTEMPTS) { throw new TooManyAttemptsException(); } }🧰 Tools
🪛 ESLint
[error] 1885-1885: Replace
mobile,·reason
with⏎········mobile,⏎········reason⏎······
(prettier/prettier)
[error] 1891-1891: Insert
,
(prettier/prettier)
[error] 1893-1893: Insert
,
(prettier/prettier)
[error] 1902-1903: Delete
⏎···
(prettier/prettier)
1985-1985
: 🛠️ Refactor suggestionReplace
isNaN
withNumber.isNaN
.Using the global
isNaN
function can lead to unexpected results due to type coercion:- if (!hashValue || !expires || isNaN(parseInt(expires))) { + if (!hashValue || !expires || Number.isNaN(parseInt(expires))) {🧰 Tools
🪛 Biome (1.9.4)
[error] 1985-1985: isNaN is unsafe. It attempts a type coercion. Use Number.isNaN instead.
See the MDN documentation for more details.
Unsafe fix: Use Number.isNaN instead.(lint/suspicious/noGlobalIsNan)
1380-1388
:⚠️ Potential issueFix object property assignments.
The current property assignments use the comma operator which can lead to unexpected behavior:
- user.userId = userCreateDto?.userId, - user.username = userCreateDto?.username, - user.firstName = userCreateDto?.firstName, - user.middleName = userCreateDto?.middleName, - user.lastName = userCreateDto?.lastName, - user.gender = userCreateDto?.gender, - user.email = userCreateDto?.email, - user.mobile = Number(userCreateDto?.mobile) || null, - user.createdBy = userCreateDto?.createdBy || userCreateDto?.createdBy; + user.userId = userCreateDto?.userId; + user.username = userCreateDto?.username; + user.firstName = userCreateDto?.firstName; + user.middleName = userCreateDto?.middleName; + user.lastName = userCreateDto?.lastName; + user.gender = userCreateDto?.gender; + user.email = userCreateDto?.email; + user.mobile = Number(userCreateDto?.mobile) || null; + user.createdBy = userCreateDto?.createdBy;🧰 Tools
🪛 ESLint
[error] 1380-1380: Replace
user.userId·=·userCreateDto?.userId
with(user.userId·=·userCreateDto?.userId)
(prettier/prettier)
[error] 1381-1381: Replace
user.username·=·userCreateDto?.username
with(user.username·=·userCreateDto?.username)
(prettier/prettier)
[error] 1382-1382: Replace
user.firstName·=·userCreateDto?.firstName
with(user.firstName·=·userCreateDto?.firstName)
(prettier/prettier)
[error] 1383-1383: Replace
user.middleName·=·userCreateDto?.middleName
with(user.middleName·=·userCreateDto?.middleName)
(prettier/prettier)
[error] 1384-1384: Replace
user.lastName·=·userCreateDto?.lastName
with(user.lastName·=·userCreateDto?.lastName)
(prettier/prettier)
[error] 1385-1385: Replace
user.gender·=·userCreateDto?.gender
with(user.gender·=·userCreateDto?.gender)
(prettier/prettier)
[error] 1386-1386: Replace
user.email·=·userCreateDto?.email
with(user.email·=·userCreateDto?.email)
(prettier/prettier)
[error] 1387-1387: Replace
user.mobile·=·Number(userCreateDto?.mobile)·||·null
with(user.mobile·=·Number(userCreateDto?.mobile)·||·null)
(prettier/prettier)
[error] 1388-1388: Replace
user.createdBy·=·userCreateDto?.createdBy·||·userCreateDto?.createdBy
with(user.createdBy·=·userCreateDto?.createdBy·||·userCreateDto?.createdBy)
(prettier/prettier)
2262-2269
: 🛠️ Refactor suggestionEnhance input sanitization.
The current sanitization only escapes SQL special characters:
private sanitizeInput(value: unknown): string | unknown { if (typeof value === 'string') { // Remove potential XSS vectors return value .replace(/[<>]/g, '') // Remove < and > .replace(/javascript:/gi, '') // Remove javascript: protocol .replace(/on\w+=/gi, ''); // Remove event handlers } return value; }🧰 Tools
🪛 ESLint
[error] 2263-2263: Replace
'string'
with"string"
(prettier/prettier)
[error] 2265-2265: Replace
'\\$&'
with"\\$&"
(prettier/prettier)
🧹 Nitpick comments (1)
src/common/utils/keycloak.adapter.util.ts (1)
229-232
: Fix missing semicolons in error logging statementsAdd missing semicolons to maintain consistent code formatting.
LoggerUtil.error( `${API_RESPONSES.SERVER_ERROR}`, `Error: "Keycloak error - email" ${e.message},` - ) + );Also applies to: 257-260
🧰 Tools
🪛 ESLint
[error] 232-232: Insert
;
(prettier/prettier)
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
-
src/adapters/postgres/user-adapter.ts
(9 hunks) -
src/common/utils/keycloak.adapter.util.ts
(5 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
src/common/utils/keycloak.adapter.util.ts (1)
Pattern **/*.ts
: "Review the JavaScript code for conformity with the Google JavaScript style guide, highlighting any deviations. Ensure that:
- The code adheres to best practices associated with nodejs.
- The code adheres to best practices associated with nestjs framework.
- The code adheres to best practices recommended for performance.
- The code adheres to similar naming conventions for controllers, models, services, methods, variables."
src/adapters/postgres/user-adapter.ts (1)
Pattern **/*.ts
: "Review the JavaScript code for conformity with the Google JavaScript style guide, highlighting any deviations. Ensure that:
- The code adheres to best practices associated with nodejs.
- The code adheres to best practices associated with nestjs framework.
- The code adheres to best practices recommended for performance.
- The code adheres to similar naming conventions for controllers, models, services, methods, variables."
🪛 ESLint
src/common/utils/keycloak.adapter.util.ts
[error] 4-4: Require statement not part of import statement.
(@typescript-eslint/no-var-requires)
[error] 53-53: Insert ;
(prettier/prettier)
[error] 57-58: Delete ⏎
(prettier/prettier)
[error] 78-78: Delete ·
(prettier/prettier)
[error] 80-80: Replace ·
with ,
(prettier/prettier)
[error] 81-81: Insert ,
(prettier/prettier)
[error] 100-100: Replace ·statusCode:·response.status,·message:·"User·created·successfully",·userId·:·userId
with ⏎······statusCode:·response.status,⏎······message:·"User·created·successfully",⏎······userId:·userId,⏎···
(prettier/prettier)
[error] 110-110: Replace ·error.response.data.errorMessage·||
with ⏎··········error.response.data.errorMessage·||⏎·········
(prettier/prettier)
[error] 129-130: Delete ⏎
(prettier/prettier)
[error] 203-203: Insert ⏎·····
(prettier/prettier)
[error] 211-212: Delete ⏎
(prettier/prettier)
[error] 232-232: Insert ;
(prettier/prettier)
[error] 260-260: Insert ;
(prettier/prettier)
src/adapters/postgres/user-adapter.ts
[error] 19-19: Replace ·ExistUserDto,·SuggestUserDto,·UserSearchDto·
with ⏎··ExistUserDto,⏎··SuggestUserDto,⏎··UserSearchDto,⏎
(prettier/prettier)
[error] 47-47: Replace 'crypto'
with "crypto"
(prettier/prettier)
[error] 98-98: Replace 'OTP_EXPIRY'
with "OTP_EXPIRY"
(prettier/prettier)
[error] 99-99: Replace 'OTP_DIGITS'
with "OTP_DIGITS"
(prettier/prettier)
[error] 100-100: Replace 'SMS_KEY'
with "SMS_KEY"
(prettier/prettier)
[error] 386-386: 'filters' is never reassigned. Use 'const' instead.
(prefer-const)
[error] 386-386: 'exclude' is never reassigned. Use 'const' instead.
(prefer-const)
[error] 386-386: 'sort' is never reassigned. Use 'const' instead.
(prefer-const)
[error] 479-479: Insert ⏎········
(prettier/prettier)
[error] 480-480: Replace ········
with ······
(prettier/prettier)
[error] 780-780: Replace ·where:·{·userId:·userDto.userId·}
with ⏎········where:·{·userId:·userDto.userId·},⏎·····
(prettier/prettier)
[error] 796-796: Replace userDto.userData.deviceId,·userDto.userId,·user.deviceId)
with ⏎············userDto.userData.deviceId,⏎············userDto.userId,⏎············user.deviceId⏎··········);
(prettier/prettier)
[error] 797-798: Delete ⏎
(prettier/prettier)
[error] 801-801: Replace userDto.userData.deviceId,·userDto.userId,·user.deviceId)
with ⏎············userDto.userData.deviceId,⏎············userDto.userId,⏎············user.deviceId⏎··········);
(prettier/prettier)
[error] 813-813: Replace keycloakReqBody
with ⏎············keycloakReqBody⏎··········
(prettier/prettier)
[error] 815-815: Replace 'exists'
with "exists"
(prettier/prettier)
[error] 932-932: Replace updateField:·UpdateField):·Promise<'exists'
with ⏎····updateField:·UpdateField⏎··):·Promise<"exists"
(prettier/prettier)
[error] 933-934: Delete ⏎
(prettier/prettier)
[error] 939-939: Replace updateField
with ⏎········updateField⏎······
(prettier/prettier)
[error] 941-941: Replace 'exists'
with "exists"
(prettier/prettier)
[error] 945-945: 'updateResult' is never reassigned. Use 'const' instead.
(prefer-const)
[error] 945-945: Insert ;
(prettier/prettier)
[error] 949-950: Delete ⏎
(prettier/prettier)
[error] 954-954: Delete ,
(prettier/prettier)
[error] 960-960: Replace userDeviceId:·string,·userId:·string,·existingDeviceId:·string[]
with ⏎····userDeviceId:·string,⏎····userId:·string,⏎····existingDeviceId:·string[]⏎··
(prettier/prettier)
[error] 961-961: 'deviceIds' is never reassigned. Use 'const' instead.
(prefer-const)
[error] 975-975: Replace deviceIdforRemove:·string,·userId:·string,·existingDeviceId:·string[]
with ⏎····deviceIdforRemove:·string,⏎····userId:·string,⏎····existingDeviceId:·string[]⏎··
(prettier/prettier)
[error] 982-982: Replace id
with (id)
(prettier/prettier)
[error] 986-986: Replace userId:·string,·userData:·Partial<User>
with ⏎····userId:·string,⏎····userData:·Partial<User>⏎··
(prettier/prettier)
[error] 997-998: Delete ⏎
(prettier/prettier)
[error] 1000-1000: Replace 'An·error·occurred·while·updating·user·details'
with "An·error·occurred·while·updating·user·details"
(prettier/prettier)
[error] 1002-1003: Delete ⏎
(prettier/prettier)
[error] 1082-1082: 'resKeycloak' is never reassigned. Use 'const' instead.
(prefer-const)
[error] 1082-1082: Replace userSchema,·token,·validatedRoles[0]?.title)
with ⏎········userSchema,⏎········token,⏎········validatedRoles[0]?.title⏎······);
(prettier/prettier)
[error] 1107-1108: Delete ⏎
(prettier/prettier)
[error] 1147-1147: Insert ··
(prettier/prettier)
[error] 1148-1148: Insert ··
(prettier/prettier)
[error] 1149-1149: Insert ··
(prettier/prettier)
[error] 1346-1347: Delete ⏎
(prettier/prettier)
[error] 1380-1380: Replace user.userId·=·userCreateDto?.userId
with (user.userId·=·userCreateDto?.userId)
(prettier/prettier)
[error] 1381-1381: Replace user.username·=·userCreateDto?.username
with (user.username·=·userCreateDto?.username)
(prettier/prettier)
[error] 1382-1382: Replace user.firstName·=·userCreateDto?.firstName
with (user.firstName·=·userCreateDto?.firstName)
(prettier/prettier)
[error] 1383-1383: Replace user.middleName·=·userCreateDto?.middleName
with (user.middleName·=·userCreateDto?.middleName)
(prettier/prettier)
[error] 1384-1384: Replace user.lastName·=·userCreateDto?.lastName
with (user.lastName·=·userCreateDto?.lastName)
(prettier/prettier)
[error] 1385-1385: Replace user.gender·=·userCreateDto?.gender
with (user.gender·=·userCreateDto?.gender)
(prettier/prettier)
[error] 1386-1386: Replace user.email·=·userCreateDto?.email
with (user.email·=·userCreateDto?.email)
(prettier/prettier)
[error] 1387-1387: Replace user.mobile·=·Number(userCreateDto?.mobile)·||·null
with (user.mobile·=·Number(userCreateDto?.mobile)·||·null)
(prettier/prettier)
[error] 1388-1388: Replace user.createdBy·=·userCreateDto?.createdBy·||·userCreateDto?.createdBy
with (user.createdBy·=·userCreateDto?.createdBy·||·userCreateDto?.createdBy)
(prettier/prettier)
[error] 1399-1399: 'query' is never reassigned. Use 'const' instead.
(prefer-const)
[error] 1401-1401: 'getCohortAcademicYearId' is never reassigned. Use 'const' instead.
(prefer-const)
[error] 1406-1406: 'cohortData' is never reassigned. Use 'const' instead.
(prefer-const)
[error] 1633-1633: Insert ··
(prettier/prettier)
[error] 1740-1740: Replace ··········
with ············
(prettier/prettier)
[error] 1741-1741: Insert ··
(prettier/prettier)
[error] 1861-1861: Replace mobileOrUsername:·string,·otp:·string,·reason:·string
with ⏎····mobileOrUsername:·string,⏎····otp:·string,⏎····reason:·string⏎··
(prettier/prettier)
[error] 1885-1885: Replace mobile,·reason
with ⏎········mobile,⏎········reason⏎······
(prettier/prettier)
[error] 1891-1891: Insert ,
(prettier/prettier)
[error] 1893-1893: Insert ,
(prettier/prettier)
[error] 1902-1903: Delete ⏎···
(prettier/prettier)
[error] 1924-1924: Replace mobileWithCode,·otp,·reason
with ⏎········mobileWithCode,⏎········otp,⏎········reason⏎······
(prettier/prettier)
[error] 1927-1927: Insert ,
(prettier/prettier)
[error] 1930-1930: Replace "OTP",·"SEND_OTP",·replacements,·[mobile]
with ⏎········"OTP",⏎········"SEND_OTP",⏎········replacements,⏎········[mobile]⏎······
(prettier/prettier)
[error] 1932-1933: Delete ⏎···
(prettier/prettier)
[error] 1953-1953: Replace 'signup'
with "signup"
(prettier/prettier)
[error] 1964-1964: Replace 'forgot'
with "forgot"
(prettier/prettier)
[error] 1984-1984: Replace '.'
with "."
(prettier/prettier)
[error] 2041-2041: Replace context:·string,·key:·string,·replacements:·object,·receipients:·string[]
with ⏎····context:·string,⏎····key:·string,⏎····replacements:·object,⏎····receipients:·string[]⏎··
(prettier/prettier)
[error] 2058-2058: Replace mailSend?.result?.sms?.errors·&&·mailSend.result.sms.errors.length·>·0
with ⏎········mailSend?.result?.sms?.errors·&&⏎········mailSend.result.sms.errors.length·>·0⏎······
(prettier/prettier)
[error] 2059-2059: Replace (error:·{·error:·string;·})·=>·error.error
with ⏎··········(error:·{·error:·string·})·=>·error.error⏎········
(prettier/prettier)
[error] 2064-2065: Delete ⏎···
(prettier/prettier)
[error] 2067-2067: Replace ${API_RESPONSES.SMS_NOTIFICATION_ERROR}:··${error.message}
with ⏎········
${API_RESPONSES.SMS_NOTIFICATION_ERROR}:··${error.message}⏎······
(prettier/prettier)
[error] 2072-2072: Replace body:·SendPasswordResetOTPDto,·response:·Response
with ⏎····body:·SendPasswordResetOTPDto,⏎····response:·Response⏎··
(prettier/prettier)
[error] 2076-2076: 'error' is never reassigned. Use 'const' instead.
(prefer-const)
[error] 2077-2077: 'success' is never reassigned. Use 'const' instead.
(prefer-const)
[error] 2098-2098: Replace ''
with ""
(prettier/prettier)
[error] 2101-2101: Replace username,·otp,·reason
with ⏎········username,⏎········otp,⏎········reason⏎······
(prettier/prettier)
[error] 2105-2105: Insert ,
(prettier/prettier)
[error] 2108-2108: Replace userData.mobile
with ⏎············userData.mobile,⏎··········
(prettier/prettier)
[error] 2109-2109: Replace 'SMS'
with "SMS"
(prettier/prettier)
[error] 2111-2111: Replace ·type:·'SMS',·message:·
${API_RESPONSES.MOBILE_OTP_SEND_FAILED}·${e.message}·})
with ⏎············type:·"SMS",⏎············message:·
${API_RESPONSES.MOBILE_OTP_SEND_FAILED}·${e.message},⏎··········});
(prettier/prettier)
[error] 2119-2119: Insert ,
(prettier/prettier)
[error] 2122-2122: Replace userData.email
with ⏎············userData.email,⏎··········
(prettier/prettier)
[error] 2123-2123: Replace ·type:·'Email',·message:·API_RESPONSES.EMAIL_SENT_OTP·})
with ⏎············type:·"Email",⏎············message:·API_RESPONSES.EMAIL_SENT_OTP,⏎··········});
(prettier/prettier)
[error] 2125-2125: Replace ·type:·'Email',·message:·
${API_RESPONSES.EMAIL_OTP_SEND_FAILED}:·${e.message}·})
with ⏎············type:·"Email",⏎············message:·
${API_RESPONSES.EMAIL_OTP_SEND_FAILED}:·${e.message},⏎··········});
(prettier/prettier)
[error] 2128-2128: Delete ·
(prettier/prettier)
[error] 2129-2129: Insert ⏎·······
(prettier/prettier)
[error] 2130-2130: Replace ''
with ""
(prettier/prettier)
[error] 2131-2131: Replace e·=>·e.type·===·'SMS'
with (e)·=>·e.type·===·"SMS"
(prettier/prettier)
[error] 2132-2132: Replace .filter(e·=>·e.type·===·'SMS').map(e·=>·e.message)
with ⏎············.filter((e)·=>·e.type·===·"SMS")⏎············.map((e)·=>·e.message)⏎············
(prettier/prettier)
[error] 2134-2134: Replace e·=>·e.type·===·'Email'
with (e)·=>·e.type·===·"Email"
(prettier/prettier)
[error] 2135-2135: Replace .filter(e·=>·e.type·===·'Email').map(e·=>·e.message)
with ⏎············.filter((e)·=>·e.type·===·"Email")⏎············.map((e)·=>·e.message)⏎············
(prettier/prettier)
[error] 2149-2149: Insert ,
(prettier/prettier)
[error] 2150-2150: Insert ;
(prettier/prettier)
[error] 2158-2159: Delete ⏎···
(prettier/prettier)
[error] 2168-2169: Delete ⏎
(prettier/prettier)
[error] 2172-2172: Replace context:·string,·key:·string,·replacements:·object,·emailReceipt
with ⏎····context:·string,⏎····key:·string,⏎····replacements:·object,⏎····emailReceipt⏎··
(prettier/prettier)
[error] 2187-2187: Replace mailSend?.result?.email?.errors·&&·mailSend.result.email.errors.length·>·0
with ⏎········mailSend?.result?.email?.errors·&&⏎········mailSend.result.email.errors.length·>·0⏎······
(prettier/prettier)
[error] 2188-2188: Replace (error:·{·error:·string;·})·=>·error.error
with ⏎··········(error:·{·error:·string·})·=>·error.error⏎········
(prettier/prettier)
[error] 2193-2194: Delete ⏎···
(prettier/prettier)
[error] 2196-2196: Replace ${API_RESPONSES.EMAIL_NOTIFICATION_ERROR}:··${e.message}
with ⏎········
${API_RESPONSES.EMAIL_NOTIFICATION_ERROR}:··${e.message}⏎······
(prettier/prettier)
[error] 2198-2199: Delete ⏎
(prettier/prettier)
[error] 2201-2205: Replace ⏎····request:·any,⏎····response:·any,⏎····filters:·ExistUserDto⏎··
with request:·any,·response:·any,·filters:·ExistUserDto
(prettier/prettier)
[error] 2213-2213: Replace key·===·'firstName'·||·key·===·'middleName'·||·key·===·'lastName'
with ⏎··············key·===·"firstName"·||⏎··············key·===·"middleName"·||⏎··············key·===·"lastName"⏎············
(prettier/prettier)
[error] 2225-2225: Replace 'username',·'firstName',·'middleName',·'lastName'
with "username",·"firstName",·"middleName",·"lastName"
(prettier/prettier)
[error] 2263-2263: Replace 'string'
with "string"
(prettier/prettier)
[error] 2265-2265: Replace '\\$&'
with "\\$&"
(prettier/prettier)
[error] 2271-2272: Replace ⏎··async·suggestUsername(request:·Request,·response:·Response,·suggestUserDto:·SuggestUserDto
with ··async·suggestUsername(⏎····request:·Request,⏎····response:·Response,⏎····suggestUserDto:·SuggestUserDto⏎··
(prettier/prettier)
[error] 2281-2281: Delete ··
(prettier/prettier)
[error] 2322-2323: Delete ⏎
(prettier/prettier)
[error] 2334-2335: Delete ⏎
(prettier/prettier)
🪛 Biome (1.9.4)
src/adapters/postgres/user-adapter.ts
[error] 575-575: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
[error] 608-608: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
[error] 690-690: Avoid the delete operator which can impact performance.
Unsafe fix: Use an undefined assignment instead.
(lint/performance/noDelete)
[error] 1261-1261: Unsafe usage of optional chaining.
If it short-circuits with 'undefined' the evaluation will throw TypeError here:
(lint/correctness/noUnsafeOptionalChaining)
[error] 1147-1147: Avoid the use of spread (...
) syntax on accumulators.
Spread syntax should be avoided on accumulators (like those in .reduce
) because it causes a time complexity of O(n^2)
.
Consider methods such as .splice or .push instead.
(lint/performance/noAccumulatingSpread)
[error] 1738-1738: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
[error] 1985-1985: isNaN is unsafe. It attempts a type coercion. Use Number.isNaN instead.
See the MDN documentation for more details.
Unsafe fix: Use Number.isNaN instead.
(lint/suspicious/noGlobalIsNan)
🔇 Additional comments (2)
src/common/utils/keycloak.adapter.util.ts (2)
78-81
: LGTM! Good improvement in user roles handlingThe addition of user roles in attributes is a good practice for role-based access control.
🧰 Tools
🪛 ESLint
[error] 78-78: Delete
·
(prettier/prettier)
[error] 80-80: Replace
·
with,
(prettier/prettier)
[error] 81-81: Insert
,
(prettier/prettier)
133-146
: Well-structured implementation of user update functionalityExcellent use of TypeScript features:
- Clear interface definitions
- Proper type safety
- Comprehensive error handling
- Good use of optional parameters
Also applies to: 148-211
this.jwt_secret = this.configService.get<string>("RBAC_JWT_SECRET"); | ||
this.jwt_password_reset_expires_In = this.configService.get<string>( | ||
"PASSWORD_RESET_JWT_EXPIRES_IN" | ||
); | ||
this.reset_frontEnd_url = | ||
this.configService.get<string>("RESET_FRONTEND_URL"); | ||
this.otpExpiry = this.configService.get<number>('OTP_EXPIRY') || 10; // default: 10 minutes | ||
this.otpDigits = this.configService.get<number>('OTP_DIGITS') || 6; | ||
this.smsKey = this.configService.get<string>('SMS_KEY'); | ||
} |
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.
🛠️ Refactor suggestion
Enhance configuration value handling with validation and type safety.
Add validation and type assertions for configuration values to prevent runtime errors:
- this.jwt_secret = this.configService.get<string>("RBAC_JWT_SECRET");
+ this.jwt_secret = this.configService.get<string>("RBAC_JWT_SECRET");
+ if (!this.jwt_secret) {
+ throw new Error("RBAC_JWT_SECRET is not configured");
+ }
this.jwt_password_reset_expires_In = this.configService.get<string>(
"PASSWORD_RESET_JWT_EXPIRES_IN"
);
+ if (!this.jwt_password_reset_expires_In) {
+ throw new Error("PASSWORD_RESET_JWT_EXPIRES_IN is not configured");
+ }
this.reset_frontEnd_url =
this.configService.get<string>("RESET_FRONTEND_URL");
+ if (!this.reset_frontEnd_url) {
+ throw new Error("RESET_FRONTEND_URL is not configured");
+ }
- this.otpExpiry = this.configService.get<number>('OTP_EXPIRY') || 10;
- this.otpDigits = this.configService.get<number>('OTP_DIGITS') || 6;
- this.smsKey = this.configService.get<string>('SMS_KEY');
+ this.otpExpiry = this.validateNumber(this.configService.get<number>('OTP_EXPIRY'), 10, 'OTP_EXPIRY');
+ this.otpDigits = this.validateNumber(this.configService.get<number>('OTP_DIGITS'), 6, 'OTP_DIGITS');
+ this.smsKey = this.configService.get<string>('SMS_KEY');
+ if (!this.smsKey) {
+ throw new Error("SMS_KEY is not configured");
+ }
Add this helper method:
private validateNumber(value: number | undefined, defaultValue: number, configKey: string): number {
if (value === undefined) {
return defaultValue;
}
if (typeof value !== 'number' || isNaN(value) || value <= 0) {
throw new Error(`Invalid ${configKey} configuration value`);
}
return value;
}
🧰 Tools
🪛 ESLint
[error] 98-98: Replace 'OTP_EXPIRY'
with "OTP_EXPIRY"
(prettier/prettier)
[error] 99-99: Replace 'OTP_DIGITS'
with "OTP_DIGITS"
(prettier/prettier)
[error] 100-100: Replace 'SMS_KEY'
with "SMS_KEY"
(prettier/prettier)
const user = await this.usersRepository.findOne({ | ||
where: { userId: userId }, | ||
}); | ||
if (!user) { | ||
return APIResponse.error(response, apiId, "Not Found", `User not found in user table.`, HttpStatus.NOT_FOUND); | ||
return APIResponse.error( | ||
response, | ||
apiId, | ||
API_RESPONSES.NOT_FOUND, | ||
API_RESPONSES.USERNAME_NOT_FOUND, | ||
HttpStatus.NOT_FOUND | ||
); | ||
} | ||
|
||
|
||
// Delete from User table | ||
const userResult = await this.usersRepository.delete(userId); | ||
|
||
// Delete from CohortMembers table | ||
const cohortMembersResult = await this.cohortMemberRepository.delete({ userId: userId }); | ||
const cohortMembersResult = await this.cohortMemberRepository.delete({ | ||
userId: userId, | ||
}); | ||
|
||
// Delete from UserTenantMapping table | ||
const userTenantMappingResult = await this.userTenantMappingRepository.delete({ userId: userId }); | ||
const userTenantMappingResult = | ||
await this.userTenantMappingRepository.delete({ userId: userId }); | ||
|
||
// Delete from UserRoleMapping table | ||
const userRoleMappingResult = await this.userRoleMappingRepository.delete({ userId: userId }); | ||
const userRoleMappingResult = await this.userRoleMappingRepository.delete( | ||
{ userId: userId } | ||
); | ||
|
||
// Delete from FieldValues table where ItemId matches userId | ||
const fieldValuesResult = await this.fieldsValueRepository.delete({ itemId: userId }); | ||
const fieldValuesResult = await this.fieldsValueRepository.delete({ | ||
itemId: userId, | ||
}); |
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.
🛠️ Refactor suggestion
Wrap multiple database operations in a transaction.
The delete operations should be wrapped in a transaction to maintain data consistency:
+ const queryRunner = this.usersRepository.manager.connection.createQueryRunner();
+ await queryRunner.connect();
+ await queryRunner.startTransaction();
try {
- const userResult = await this.usersRepository.delete(userId);
- const cohortMembersResult = await this.cohortMemberRepository.delete({
- userId: userId,
- });
- const userTenantMappingResult =
- await this.userTenantMappingRepository.delete({ userId: userId });
- const userRoleMappingResult = await this.userRoleMappingRepository.delete(
- { userId: userId }
- );
- const fieldValuesResult = await this.fieldsValueRepository.delete({
- itemId: userId,
- });
+ await queryRunner.manager.delete(User, userId);
+ await queryRunner.manager.delete(CohortMembers, { userId });
+ await queryRunner.manager.delete(UserTenantMapping, { userId });
+ await queryRunner.manager.delete(UserRoleMapping, { userId });
+ await queryRunner.manager.delete(FieldValues, { itemId: userId });
+ await queryRunner.commitTransaction();
} catch (error) {
+ await queryRunner.rollbackTransaction();
throw error;
+ } finally {
+ await queryRunner.release();
}
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
const user = await this.usersRepository.findOne({ | |
where: { userId: userId }, | |
}); | |
if (!user) { | |
return APIResponse.error(response, apiId, "Not Found", `User not found in user table.`, HttpStatus.NOT_FOUND); | |
return APIResponse.error( | |
response, | |
apiId, | |
API_RESPONSES.NOT_FOUND, | |
API_RESPONSES.USERNAME_NOT_FOUND, | |
HttpStatus.NOT_FOUND | |
); | |
} | |
// Delete from User table | |
const userResult = await this.usersRepository.delete(userId); | |
// Delete from CohortMembers table | |
const cohortMembersResult = await this.cohortMemberRepository.delete({ userId: userId }); | |
const cohortMembersResult = await this.cohortMemberRepository.delete({ | |
userId: userId, | |
}); | |
// Delete from UserTenantMapping table | |
const userTenantMappingResult = await this.userTenantMappingRepository.delete({ userId: userId }); | |
const userTenantMappingResult = | |
await this.userTenantMappingRepository.delete({ userId: userId }); | |
// Delete from UserRoleMapping table | |
const userRoleMappingResult = await this.userRoleMappingRepository.delete({ userId: userId }); | |
const userRoleMappingResult = await this.userRoleMappingRepository.delete( | |
{ userId: userId } | |
); | |
// Delete from FieldValues table where ItemId matches userId | |
const fieldValuesResult = await this.fieldsValueRepository.delete({ itemId: userId }); | |
const fieldValuesResult = await this.fieldsValueRepository.delete({ | |
itemId: userId, | |
}); | |
const user = await this.usersRepository.findOne({ | |
where: { userId: userId }, | |
}); | |
if (!user) { | |
return APIResponse.error( | |
response, | |
apiId, | |
API_RESPONSES.NOT_FOUND, | |
API_RESPONSES.USERNAME_NOT_FOUND, | |
HttpStatus.NOT_FOUND | |
); | |
} | |
const queryRunner = this.usersRepository.manager.connection.createQueryRunner(); | |
await queryRunner.connect(); | |
await queryRunner.startTransaction(); | |
try { | |
await queryRunner.manager.delete(User, userId); | |
await queryRunner.manager.delete(CohortMembers, { userId }); | |
await queryRunner.manager.delete(UserTenantMapping, { userId }); | |
await queryRunner.manager.delete(UserRoleMapping, { userId }); | |
await queryRunner.manager.delete(FieldValues, { itemId: userId }); | |
await queryRunner.commitTransaction(); | |
} catch (error) { | |
await queryRunner.rollbackTransaction(); | |
throw error; | |
} finally { | |
await queryRunner.release(); | |
} |
Added field-value-delete API
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.
Actionable comments posted: 6
♻️ Duplicate comments (1)
src/adapters/postgres/fields-adapter.ts (1)
687-714
:⚠️ Potential issueCritical: SQL injection vulnerability in
createSourceDetailsTableFields
.The method constructs SQL queries by directly concatenating user inputs.
Use parameterized queries or TypeORM's QueryBuilder:
const queryBuilder = this.fieldsValuesRepository .createQueryBuilder() .insert() .into(tableName) .values({ name, value, createdBy, ...(controllingfieldfk && { controllingfieldfk }) }); await queryBuilder.execute();
🧹 Nitpick comments (2)
src/common/utils/api-id.config.ts (1)
35-35
: Standardize API endpoint design patterns.The API endpoints have inconsistent verb placement and resource naming:
api.create.cohortAcademicYear
differs from the pattern used in other endpoints likeapi.cohort.create
api.assigntenant.create
could be better structured as a sub-resourceConsider applying this diff to improve consistency:
- ASSIGN_TENANT_CREATE: "api.assigntenant.create", + ASSIGN_TENANT_CREATE: "api.tenant.assign.create", - ADD_COHORT_TO_ACADEMIC_YEAR: "api.create.cohortAcademicYear", + ADD_COHORT_TO_ACADEMIC_YEAR: "api.academic-year.cohort.create",Also applies to: 52-52
src/adapters/postgres/fields-adapter.ts (1)
439-439
: Improve performance by avoiding the delete operator.Using the
delete
operator can impact performance as it forces the JavaScript engine to reoptimize the object structure.Replace delete operations with undefined assignments:
- delete fieldsData.fieldParams; + fieldsData.fieldParams = undefined; - delete fieldsData.fieldParams; + fieldsData.fieldParams = undefined; - delete data.fieldParams; - delete data.sourceDetails; + data.fieldParams = undefined; + data.sourceDetails = undefined;Also applies to: 578-578, 1739-1740
🧰 Tools
🪛 Biome (1.9.4)
[error] 439-439: Avoid the delete operator which can impact performance.
Unsafe fix: Use an undefined assignment instead.
(lint/performance/noDelete)
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
src/adapters/fieldsservicelocator.ts
(1 hunks)src/adapters/postgres/fields-adapter.ts
(1 hunks)src/common/utils/api-id.config.ts
(1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
src/common/utils/api-id.config.ts (1)
Pattern **/*.ts
: "Review the JavaScript code for conformity with the Google JavaScript style guide, highlighting any deviations. Ensure that:
- The code adheres to best practices associated with nodejs.
- The code adheres to best practices associated with nestjs framework.
- The code adheres to best practices recommended for performance.
- The code adheres to similar naming conventions for controllers, models, services, methods, variables."
src/adapters/fieldsservicelocator.ts (1)
Pattern **/*.ts
: "Review the JavaScript code for conformity with the Google JavaScript style guide, highlighting any deviations. Ensure that:
- The code adheres to best practices associated with nodejs.
- The code adheres to best practices associated with nestjs framework.
- The code adheres to best practices recommended for performance.
- The code adheres to similar naming conventions for controllers, models, services, methods, variables."
src/adapters/postgres/fields-adapter.ts (1)
Pattern **/*.ts
: "Review the JavaScript code for conformity with the Google JavaScript style guide, highlighting any deviations. Ensure that:
- The code adheres to best practices associated with nodejs.
- The code adheres to best practices associated with nestjs framework.
- The code adheres to best practices recommended for performance.
- The code adheres to similar naming conventions for controllers, models, services, methods, variables."
🪛 Biome (1.9.4)
src/adapters/postgres/fields-adapter.ts
[error] 439-439: Avoid the delete operator which can impact performance.
Unsafe fix: Use an undefined assignment instead.
(lint/performance/noDelete)
[error] 578-578: Avoid the delete operator which can impact performance.
Unsafe fix: Use an undefined assignment instead.
(lint/performance/noDelete)
[error] 1424-1424: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
[error] 1739-1739: Avoid the delete operator which can impact performance.
Unsafe fix: Use an undefined assignment instead.
(lint/performance/noDelete)
[error] 1740-1740: Avoid the delete operator which can impact performance.
Unsafe fix: Use an undefined assignment instead.
(lint/performance/noDelete)
🪛 ESLint
src/adapters/postgres/fields-adapter.ts
[error] 790-790: 'tenantCond' is never reassigned. Use 'const' instead.
(prefer-const)
🔇 Additional comments (3)
src/common/utils/api-id.config.ts (2)
60-61
: Add trailing comma after the last property.
7-9
: Verify security measures for sensitive endpoints.Several sensitive operations are exposed through these endpoints. Ensure that:
- Password reset and OTP endpoints are rate-limited
- Authentication endpoints have proper session management
- Token endpoints implement security best practices
Run the following script to check for rate limiting implementation:
Also applies to: 42-45, 58-60
src/adapters/fieldsservicelocator.ts (1)
47-48
: Add type definitions for method parameters.The
requiredData
parameter lacks type definition in bothdeleteFieldOptions
andgetFormCustomField
methods.- deleteFieldOptions(requiredData, response); - getFormCustomField(requiredData, response); + deleteFieldOptions(requiredData: { [key: string]: any }, response: Response); + getFormCustomField(requiredData: { [key: string]: any }, response: Response);
PRIVILEGE_BYROLEID: "api.privilegebyRoleId.get", | ||
PRIVILEGE_BYPRIVILEGEID: "api.privilegebyPrivilegeId.get", |
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.
🛠️ Refactor suggestion
Standardize API endpoint naming patterns.
There are inconsistencies in the casing and word separation patterns:
privilegebyRoleId
vsprivilege
fieldValues
vsfields
OTP
vsotp
Apply this diff to standardize the patterns:
- PRIVILEGE_BYROLEID: "api.privilegebyRoleId.get",
- PRIVILEGE_BYPRIVILEGEID: "api.privilegebyPrivilegeId.get",
+ PRIVILEGE_BY_ROLE_ID: "api.privilege.by-role-id.get",
+ PRIVILEGE_BY_PRIVILEGE_ID: "api.privilege.by-privilege-id.get",
- FIELDVALUES_CREATE: "api.fieldValues.create",
- FIELDVALUES_SEARCH: "api.fieldValues.search",
- FIELDVALUES_DELETE: "api.fieldValues.delete",
+ FIELD_VALUES_CREATE: "api.field-values.create",
+ FIELD_VALUES_SEARCH: "api.field-values.search",
+ FIELD_VALUES_DELETE: "api.field-values.delete",
- SEND_OTP: "api.send.OTP",
- VERIFY_OTP: "api.verify.OTP",
- SEND_RESET_OTP: "api.send.reset.otp",
+ SEND_OTP: "api.send.otp",
+ VERIFY_OTP: "api.verify.otp",
+ SEND_RESET_OTP: "api.send.reset.otp",
Also applies to: 38-39, 58-60
updateFields( | ||
fieldId: any, | ||
request: any, | ||
fieldsUpdateDto: FieldsUpdateDto, | ||
response: Response | ||
); |
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.
🛠️ Refactor suggestion
Replace any
type with a more specific type.
Using any
type reduces type safety. Consider using a more specific type for the fieldId
parameter.
updateFields(
- fieldId: any,
+ fieldId: string,
request: any,
fieldsUpdateDto: FieldsUpdateDto,
response: Response
);
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
updateFields( | |
fieldId: any, | |
request: any, | |
fieldsUpdateDto: FieldsUpdateDto, | |
response: Response | |
); | |
updateFields( | |
fieldId: string, | |
request: any, | |
fieldsUpdateDto: FieldsUpdateDto, | |
response: Response | |
); |
searchFields( | ||
tenantid, | ||
request: any, | ||
fieldsSearchDto: FieldsSearchDto, | ||
response: Response | ||
); |
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.
🛠️ Refactor suggestion
Add type definition for tenantid
parameter.
The tenantid
parameter should have a type definition for better type safety.
searchFields(
- tenantid,
+ tenantid: string,
request: any,
fieldsSearchDto: FieldsSearchDto,
response: Response
);
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
searchFields( | |
tenantid, | |
request: any, | |
fieldsSearchDto: FieldsSearchDto, | |
response: Response | |
); | |
searchFields( | |
tenantid: string, | |
request: any, | |
fieldsSearchDto: FieldsSearchDto, | |
response: Response | |
); |
const offsetCond = offset ? `offset ${offset}` : ""; | ||
const limitCond = limit ? `limit ${limit}` : ""; | ||
let whereCond = `WHERE `; | ||
whereCond = whereClause ? (whereCond += `${whereClause}`) : ""; |
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.
🛠️ Refactor suggestion
Critical: SQL injection vulnerability and confusing assignment.
The method has two issues:
- SQL injection vulnerability due to unparameterized queries
- Confusing assignment within expression
Fix the assignment:
- whereCond = whereClause ? (whereCond += `${whereClause}`) : "";
+ if (whereClause) {
+ whereCond += whereClause;
+ } else {
+ whereCond = "";
+ }
Use parameterized queries:
- const query = `SELECT *,COUNT(*) OVER() AS total_count FROM public."${tableName}" ${whereCond} ${orderCond} ${offsetCond} ${limitCond}`;
+ const query = `SELECT *,COUNT(*) OVER() AS total_count FROM public.$1 WHERE $2 $3 $4 $5`;
+ const result = await this.fieldsRepository.query(query, [tableName, whereCond, orderCond, offsetCond, limitCond]);
Also applies to: 1436-1436
🧰 Tools
🪛 Biome (1.9.4)
[error] 1424-1424: The assignment should not be in an expression.
The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.
(lint/suspicious/noAssignInExpressions)
const fileread = readFileSync( | ||
join(process.cwd(), "src/utils/corefield.json"), | ||
"utf8" | ||
); |
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.
🛠️ Refactor suggestion
Use asynchronous file reading.
Synchronous file reading can block the event loop. Use fs.promises.readFile
instead.
- const fileread = readFileSync(
- join(process.cwd(), "src/utils/corefield.json"),
- "utf8"
- );
+ const fileread = await fs.promises.readFile(
+ join(process.cwd(), "src/utils/corefield.json"),
+ "utf8"
+ );
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
const fileread = readFileSync( | |
join(process.cwd(), "src/utils/corefield.json"), | |
"utf8" | |
); | |
const fileread = await fs.promises.readFile( | |
join(process.cwd(), "src/utils/corefield.json"), | |
"utf8" | |
); |
if (requiredData.context) { | ||
whereClause += ` OR context = '${requiredData.context}' AND "contextType" IS NULL`; | ||
} | ||
|
||
if (requiredData.contextType) { | ||
whereClause += ` OR "contextType" = '${requiredData.contextType}'`; | ||
} |
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.
Critical: SQL injection vulnerability in getFormCustomField
.
The method constructs SQL queries by directly concatenating user inputs (requiredData.context
and requiredData.contextType
).
Use parameterized queries:
- whereClause += ` OR context = '${requiredData.context}' AND "contextType" IS NULL`;
+ whereClause += ` OR context = $1 AND "contextType" IS NULL`;
- whereClause += ` OR "contextType" = '${requiredData.contextType}'`;
+ whereClause += ` OR "contextType" = $2`;
- const data = await this.getFieldData(whereClause);
+ const data = await this.getFieldData(whereClause, [requiredData.context, requiredData.contextType]);
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
if (requiredData.context) { | |
whereClause += ` OR context = '${requiredData.context}' AND "contextType" IS NULL`; | |
} | |
if (requiredData.contextType) { | |
whereClause += ` OR "contextType" = '${requiredData.contextType}'`; | |
} | |
if (requiredData.context) { | |
- whereClause += ` OR context = '${requiredData.context}' AND "contextType" IS NULL`; | |
+ whereClause += ` OR context = $1 AND "contextType" IS NULL`; | |
} | |
if (requiredData.contextType) { | |
- whereClause += ` OR "contextType" = '${requiredData.contextType}'`; | |
+ whereClause += ` OR "contextType" = $2`; | |
} | |
- const data = await this.getFieldData(whereClause); | |
+ const data = await this.getFieldData(whereClause, [requiredData.context, requiredData.contextType]); |
Allow Updating Cohort with Existing Name for the Same Cohort ID
|
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.
Actionable comments posted: 5
🧹 Nitpick comments (1)
src/adapters/postgres/cohort-adapter.ts (1)
1220-1323
: Enhance input validation securityThe validation logic could be strengthened by:
- Adding input sanitization for all string inputs
- Implementing rate limiting for validation requests
- Adding validation for maximum allowed values
private sanitizeInput(value: string): string { return value.replace(/[<>{}]/g, ''); // Remove potential XSS vectors } // Add this to validateRequestBody if (userCreateDto.tenantCohortRoleMapping?.length > MAX_MAPPINGS) { errorCollector.addError(`Exceeded maximum allowed mappings (${MAX_MAPPINGS})`); }
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
-
src/adapters/postgres/cohort-adapter.ts
(3 hunks) -
src/adapters/postgres/user-adapter.ts
(9 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
`**/*.ts`: "Review the JavaScript code for conformity with t...
**/*.ts
: "Review the JavaScript code for conformity with the Google JavaScript style guide, highlighting any deviations. Ensure that:
- The code adheres to best practices associated with nodejs.
- The code adheres to best practices associated with nestjs framework.
- The code adheres to best practices recommended for performance.
- The code adheres to similar naming conventions for controllers, models, services, methods, variables."
src/adapters/postgres/user-adapter.ts
src/adapters/postgres/cohort-adapter.ts
🪛 ESLint
src/adapters/postgres/user-adapter.ts
[error] 19-19: Replace ·ExistUserDto,·SuggestUserDto,·UserSearchDto·
with ⏎··ExistUserDto,⏎··SuggestUserDto,⏎··UserSearchDto,⏎
(prettier/prettier)
[error] 47-47: Replace 'crypto'
with "crypto"
(prettier/prettier)
[error] 98-98: Replace 'OTP_EXPIRY'
with "OTP_EXPIRY"
(prettier/prettier)
[error] 99-99: Replace 'OTP_DIGITS'
with "OTP_DIGITS"
(prettier/prettier)
[error] 100-100: Replace 'SMS_KEY'
with "SMS_KEY"
(prettier/prettier)
[error] 386-386: 'filters' is never reassigned. Use 'const' instead.
(prefer-const)
[error] 386-386: 'exclude' is never reassigned. Use 'const' instead.
(prefer-const)
[error] 386-386: 'sort' is never reassigned. Use 'const' instead.
(prefer-const)
[error] 479-479: Insert ⏎········
(prettier/prettier)
[error] 480-480: Replace ········
with ······
(prettier/prettier)
[error] 780-780: Replace ·where:·{·userId:·userDto.userId·}
with ⏎········where:·{·userId:·userDto.userId·},⏎·····
(prettier/prettier)
[error] 796-796: Replace userDto.userData.deviceId,·userDto.userId,·user.deviceId)
with ⏎············userDto.userData.deviceId,⏎············userDto.userId,⏎············user.deviceId⏎··········);
(prettier/prettier)
[error] 797-798: Delete ⏎
(prettier/prettier)
[error] 801-801: Replace userDto.userData.deviceId,·userDto.userId,·user.deviceId)
with ⏎············userDto.userData.deviceId,⏎············userDto.userId,⏎············user.deviceId⏎··········);
(prettier/prettier)
[error] 813-813: Replace keycloakReqBody
with ⏎············keycloakReqBody⏎··········
(prettier/prettier)
[error] 815-815: Replace 'exists'
with "exists"
(prettier/prettier)
[error] 932-932: Replace updateField:·UpdateField):·Promise<'exists'
with ⏎····updateField:·UpdateField⏎··):·Promise<"exists"
(prettier/prettier)
[error] 933-934: Delete ⏎
(prettier/prettier)
[error] 939-939: Replace updateField
with ⏎········updateField⏎······
(prettier/prettier)
[error] 941-941: Replace 'exists'
with "exists"
(prettier/prettier)
[error] 945-945: 'updateResult' is never reassigned. Use 'const' instead.
(prefer-const)
[error] 945-945: Insert ;
(prettier/prettier)
[error] 949-950: Delete ⏎
(prettier/prettier)
[error] 954-954: Delete ,
(prettier/prettier)
[error] 960-960: Replace userDeviceId:·string,·userId:·string,·existingDeviceId:·string[]
with ⏎····userDeviceId:·string,⏎····userId:·string,⏎····existingDeviceId:·string[]⏎··
(prettier/prettier)
[error] 961-961: 'deviceIds' is never reassigned. Use 'const' instead.
(prefer-const)
[error] 975-975: Replace deviceIdforRemove:·string,·userId:·string,·existingDeviceId:·string[]
with ⏎····deviceIdforRemove:·string,⏎····userId:·string,⏎····existingDeviceId:·string[]⏎··
(prettier/prettier)
[error] 982-982: Replace id
with (id)
(prettier/prettier)
[error] 986-986: Replace userId:·string,·userData:·Partial<User>
with ⏎····userId:·string,⏎····userData:·Partial<User>⏎··
(prettier/prettier)
[error] 997-998: Delete ⏎
(prettier/prettier)
[error] 1000-1000: Replace 'An·error·occurred·while·updating·user·details'
with "An·error·occurred·while·updating·user·details"
(prettier/prettier)
[error] 1002-1003: Delete ⏎
(prettier/prettier)
[error] 1082-1082: 'resKeycloak' is never reassigned. Use 'const' instead.
(prefer-const)
[error] 1082-1082: Replace userSchema,·token,·validatedRoles[0]?.title)
with ⏎········userSchema,⏎········token,⏎········validatedRoles[0]?.title⏎······);
(prettier/prettier)
[error] 1107-1108: Delete ⏎
(prettier/prettier)
[error] 1147-1147: Insert ··
(prettier/prettier)
[error] 1148-1148: Insert ··
(prettier/prettier)
[error] 1149-1149: Insert ··
(prettier/prettier)
[error] 1346-1347: Delete ⏎
(prettier/prettier)
[error] 1380-1380: Replace user.userId·=·userCreateDto?.userId
with (user.userId·=·userCreateDto?.userId)
(prettier/prettier)
[error] 1381-1381: Replace user.username·=·userCreateDto?.username
with (user.username·=·userCreateDto?.username)
(prettier/prettier)
[error] 1382-1382: Replace user.firstName·=·userCreateDto?.firstName
with (user.firstName·=·userCreateDto?.firstName)
(prettier/prettier)
[error] 1383-1383: Replace user.middleName·=·userCreateDto?.middleName
with (user.middleName·=·userCreateDto?.middleName)
(prettier/prettier)
[error] 1384-1384: Replace user.lastName·=·userCreateDto?.lastName
with (user.lastName·=·userCreateDto?.lastName)
(prettier/prettier)
[error] 1385-1385: Replace user.gender·=·userCreateDto?.gender
with (user.gender·=·userCreateDto?.gender)
(prettier/prettier)
[error] 1386-1386: Replace user.email·=·userCreateDto?.email
with (user.email·=·userCreateDto?.email)
(prettier/prettier)
[error] 1387-1387: Replace user.mobile·=·Number(userCreateDto?.mobile)·||·null
with (user.mobile·=·Number(userCreateDto?.mobile)·||·null)
(prettier/prettier)
[error] 1388-1388: Replace user.createdBy·=·userCreateDto?.createdBy·||·userCreateDto?.createdBy
with (user.createdBy·=·userCreateDto?.createdBy·||·userCreateDto?.createdBy)
(prettier/prettier)
[error] 1399-1399: 'query' is never reassigned. Use 'const' instead.
(prefer-const)
[error] 1401-1401: 'getCohortAcademicYearId' is never reassigned. Use 'const' instead.
(prefer-const)
[error] 1406-1406: 'cohortData' is never reassigned. Use 'const' instead.
(prefer-const)
[error] 1633-1633: Insert ··
(prettier/prettier)
[error] 1740-1740: Replace ··········
with ············
(prettier/prettier)
[error] 1741-1741: Insert ··
(prettier/prettier)
[error] 1861-1861: Replace mobileOrUsername:·string,·otp:·string,·reason:·string
with ⏎····mobileOrUsername:·string,⏎····otp:·string,⏎····reason:·string⏎··
(prettier/prettier)
[error] 1885-1885: Replace mobile,·reason
with ⏎········mobile,⏎········reason⏎······
(prettier/prettier)
[error] 1891-1891: Insert ,
(prettier/prettier)
[error] 1893-1893: Insert ,
(prettier/prettier)
[error] 1902-1903: Delete ⏎···
(prettier/prettier)
[error] 1924-1924: Replace mobileWithCode,·otp,·reason
with ⏎········mobileWithCode,⏎········otp,⏎········reason⏎······
(prettier/prettier)
[error] 1927-1927: Insert ,
(prettier/prettier)
[error] 1930-1930: Replace "OTP",·"SEND_OTP",·replacements,·[mobile]
with ⏎········"OTP",⏎········"SEND_OTP",⏎········replacements,⏎········[mobile]⏎······
(prettier/prettier)
[error] 1932-1933: Delete ⏎···
(prettier/prettier)
[error] 1953-1953: Replace 'signup'
with "signup"
(prettier/prettier)
[error] 1964-1964: Replace 'forgot'
with "forgot"
(prettier/prettier)
[error] 1984-1984: Replace '.'
with "."
(prettier/prettier)
[error] 2041-2041: Replace context:·string,·key:·string,·replacements:·object,·receipients:·string[]
with ⏎····context:·string,⏎····key:·string,⏎····replacements:·object,⏎····receipients:·string[]⏎··
(prettier/prettier)
[error] 2058-2058: Replace mailSend?.result?.sms?.errors·&&·mailSend.result.sms.errors.length·>·0
with ⏎········mailSend?.result?.sms?.errors·&&⏎········mailSend.result.sms.errors.length·>·0⏎······
(prettier/prettier)
[error] 2059-2059: Replace (error:·{·error:·string;·})·=>·error.error
with ⏎··········(error:·{·error:·string·})·=>·error.error⏎········
(prettier/prettier)
[error] 2064-2065: Delete ⏎···
(prettier/prettier)
[error] 2067-2067: Replace ${API_RESPONSES.SMS_NOTIFICATION_ERROR}:··${error.message}
with ⏎········
${API_RESPONSES.SMS_NOTIFICATION_ERROR}:··${error.message}⏎······
(prettier/prettier)
[error] 2072-2072: Replace body:·SendPasswordResetOTPDto,·response:·Response
with ⏎····body:·SendPasswordResetOTPDto,⏎····response:·Response⏎··
(prettier/prettier)
[error] 2076-2076: 'error' is never reassigned. Use 'const' instead.
(prefer-const)
[error] 2077-2077: 'success' is never reassigned. Use 'const' instead.
(prefer-const)
[error] 2098-2098: Replace ''
with ""
(prettier/prettier)
[error] 2101-2101: Replace username,·otp,·reason
with ⏎········username,⏎········otp,⏎········reason⏎······
(prettier/prettier)
[error] 2105-2105: Insert ,
(prettier/prettier)
[error] 2108-2108: Replace userData.mobile
with ⏎············userData.mobile,⏎··········
(prettier/prettier)
[error] 2109-2109: Replace 'SMS'
with "SMS"
(prettier/prettier)
[error] 2111-2111: Replace ·type:·'SMS',·message:·
${API_RESPONSES.MOBILE_OTP_SEND_FAILED}·${e.message}·})
with ⏎············type:·"SMS",⏎············message:·
${API_RESPONSES.MOBILE_OTP_SEND_FAILED}·${e.message},⏎··········});
(prettier/prettier)
[error] 2119-2119: Insert ,
(prettier/prettier)
[error] 2122-2122: Replace userData.email
with ⏎············userData.email,⏎··········
(prettier/prettier)
[error] 2123-2123: Replace ·type:·'Email',·message:·API_RESPONSES.EMAIL_SENT_OTP·})
with ⏎············type:·"Email",⏎············message:·API_RESPONSES.EMAIL_SENT_OTP,⏎··········});
(prettier/prettier)
[error] 2125-2125: Replace ·type:·'Email',·message:·
${API_RESPONSES.EMAIL_OTP_SEND_FAILED}:·${e.message}·})
with ⏎············type:·"Email",⏎············message:·
${API_RESPONSES.EMAIL_OTP_SEND_FAILED}:·${e.message},⏎··········});
(prettier/prettier)
[error] 2128-2128: Delete ·
(prettier/prettier)
[error] 2129-2129: Insert ⏎·······
(prettier/prettier)
[error] 2130-2130: Replace ''
with ""
(prettier/prettier)
[error] 2131-2131: Replace e·=>·e.type·===·'SMS'
with (e)·=>·e.type·===·"SMS"
(prettier/prettier)
[error] 2132-2132: Replace .filter(e·=>·e.type·===·'SMS').map(e·=>·e.message)
with ⏎············.filter((e)·=>·e.type·===·"SMS")⏎············.map((e)·=>·e.message)⏎············
(prettier/prettier)
[error] 2134-2134: Replace e·=>·e.type·===·'Email'
with (e)·=>·e.type·===·"Email"
(prettier/prettier)
[error] 2135-2135: Replace .filter(e·=>·e.type·===·'Email').map(e·=>·e.message)
with ⏎············.filter((e)·=>·e.type·===·"Email")⏎············.map((e)·=>·e.message)⏎············
(prettier/prettier)
[error] 2149-2149: Insert ,
(prettier/prettier)
[error] 2150-2150: Insert ;
(prettier/prettier)
[error] 2158-2159: Delete ⏎···
(prettier/prettier)
[error] 2168-2169: Delete ⏎
(prettier/prettier)
[error] 2172-2172: Replace context:·string,·key:·string,·replacements:·object,·emailReceipt
with ⏎····context:·string,⏎····key:·string,⏎····replacements:·object,⏎····emailReceipt⏎··
(prettier/prettier)
[error] 2187-2187: Replace mailSend?.result?.email?.errors·&&·mailSend.result.email.errors.length·>·0
with ⏎········mailSend?.result?.email?.errors·&&⏎········mailSend.result.email.errors.length·>·0⏎······
(prettier/prettier)
[error] 2188-2188: Replace (error:·{·error:·string;·})·=>·error.error
with ⏎··········(error:·{·error:·string·})·=>·error.error⏎········
(prettier/prettier)
[error] 2193-2194: Delete ⏎···
(prettier/prettier)
[error] 2196-2196: Replace ${API_RESPONSES.EMAIL_NOTIFICATION_ERROR}:··${e.message}
with ⏎········
${API_RESPONSES.EMAIL_NOTIFICATION_ERROR}:··${e.message}⏎······
(prettier/prettier)
[error] 2198-2199: Delete ⏎
(prettier/prettier)
[error] 2201-2205: Replace ⏎····request:·any,⏎····response:·any,⏎····filters:·ExistUserDto⏎··
with request:·any,·response:·any,·filters:·ExistUserDto
(prettier/prettier)
[error] 2213-2213: Replace key·===·'firstName'·||·key·===·'middleName'·||·key·===·'lastName'
with ⏎··············key·===·"firstName"·||⏎··············key·===·"middleName"·||⏎··············key·===·"lastName"⏎············
(prettier/prettier)
[error] 2225-2225: Replace 'username',·'firstName',·'middleName',·'lastName'
with "username",·"firstName",·"middleName",·"lastName"
(prettier/prettier)
[error] 2263-2263: Replace 'string'
with "string"
(prettier/prettier)
[error] 2265-2265: Replace '\\$&'
with "\\$&"
(prettier/prettier)
[error] 2271-2272: Replace ⏎··async·suggestUsername(request:·Request,·response:·Response,·suggestUserDto:·SuggestUserDto
with ··async·suggestUsername(⏎····request:·Request,⏎····response:·Response,⏎····suggestUserDto:·SuggestUserDto⏎··
(prettier/prettier)
[error] 2281-2281: Delete ··
(prettier/prettier)
[error] 2322-2323: Delete ⏎
(prettier/prettier)
[error] 2334-2335: Delete ⏎
(prettier/prettier)
src/adapters/postgres/cohort-adapter.ts
[error] 45-45: Delete ·
(prettier/prettier)
[error] 93-93: Insert ;
(prettier/prettier)
[error] 118-120: Replace ⏎······API_RESPONSES.COHORT_DATA_RESPONSE,⏎····)
with API_RESPONSES.COHORT_DATA_RESPONSE);
(prettier/prettier)
[error] 126-126: Delete ,
(prettier/prettier)
[error] 150-152: Replace ⏎········API_RESPONSES.CHILD_DATA,⏎······)
with API_RESPONSES.CHILD_DATA);
(prettier/prettier)
[error] 160-160: Delete ,
(prettier/prettier)
[error] 172-173: Delete ⏎
(prettier/prettier)
[error] 284-286: Replace ⏎······API_RESPONSES.COHORT_FIELD_DETAILS,⏎····)
with API_RESPONSES.COHORT_FIELD_DETAILS);
(prettier/prettier)
[error] 312-312: Insert ;
(prettier/prettier)
[error] 421-423: Replace ⏎········API_RESPONSES.CREATE_COHORT,⏎······)
with API_RESPONSES.CREATE_COHORT);
(prettier/prettier)
[error] 436-436: Insert ;
(prettier/prettier)
[error] 508-508: Insert ⏎···············
(prettier/prettier)
[error] 553-553: Delete ··
(prettier/prettier)
[error] 554-554: Delete ··
(prettier/prettier)
[error] 566-566: Insert ··
(prettier/prettier)
[error] 567-567: Insert ··
(prettier/prettier)
[error] 568-568: Insert ··
(prettier/prettier)
[error] 608-610: Replace ⏎··········API_RESPONSES.COHORT_UPDATED_SUCCESSFULLY,⏎········)
with API_RESPONSES.COHORT_UPDATED_SUCCESSFULLY);
(prettier/prettier)
[error] 632-632: Insert ;
(prettier/prettier)
[error] 915-915: Insert ;
(prettier/prettier)
[error] 973-973: Insert ;
(prettier/prettier)
[error] 1029-1031: Replace ⏎······API_RESPONSES.COHORT_HIERARCHY,⏎····)
with API_RESPONSES.COHORT_HIERARCHY);
(prettier/prettier)
[error] 1058-1058: Replace requiredData.userId,·requiredData?.academicYearId
with ⏎··········requiredData.userId,⏎··········requiredData?.academicYearId⏎········
(prettier/prettier)
[error] 1102-1102: Insert ;
(prettier/prettier)
[error] 1115-1115: Replace requiredData.userId,·requiredData?.academicYearId
with ⏎··········requiredData.userId,⏎··········requiredData?.academicYearId⏎········
(prettier/prettier)
[error] 1165-1165: Insert ;
(prettier/prettier)
🪛 Biome (1.9.4)
src/adapters/postgres/user-adapter.ts
[error] 575-575: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
[error] 608-608: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
[error] 690-690: Avoid the delete operator which can impact performance.
Unsafe fix: Use an undefined assignment instead.
(lint/performance/noDelete)
[error] 1261-1261: Unsafe usage of optional chaining.
If it short-circuits with 'undefined' the evaluation will throw TypeError here:
(lint/correctness/noUnsafeOptionalChaining)
[error] 1147-1147: Avoid the use of spread (...
) syntax on accumulators.
Spread syntax should be avoided on accumulators (like those in .reduce
) because it causes a time complexity of O(n^2)
.
Consider methods such as .splice or .push instead.
(lint/performance/noAccumulatingSpread)
[error] 1738-1738: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
[error] 1985-1985: isNaN is unsafe. It attempts a type coercion. Use Number.isNaN instead.
See the MDN documentation for more details.
Unsafe fix: Use Number.isNaN instead.
(lint/suspicious/noGlobalIsNan)
src/adapters/postgres/cohort-adapter.ts
[error] 274-274: Avoid the delete operator which can impact performance.
Unsafe fix: Use an undefined assignment instead.
(lint/performance/noDelete)
[error] 275-275: Avoid the delete operator which can impact performance.
Unsafe fix: Use an undefined assignment instead.
(lint/performance/noDelete)
[error] 528-528: Do not access Object.prototype method 'hasOwnProperty' from target object.
It's recommended using Object.hasOwn() instead of using Object.hasOwnProperty().
See MDN web docs for more details.
(lint/suspicious/noPrototypeBuiltins)
[error] 566-566: Avoid the use of spread (...
) syntax on accumulators.
Spread syntax should be avoided on accumulators (like those in .reduce
) because it causes a time complexity of O(n^2)
.
Consider methods such as .splice or .push instead.
(lint/performance/noAccumulatingSpread)
⏰ Context from checks skipped due to timeout of 90000ms (1)
- GitHub Check: Deployment
🔇 Additional comments (2)
src/adapters/postgres/cohort-adapter.ts (2)
118-120
: Fix syntax inLoggerUtil.log
callsThe
LoggerUtil.log
function is called with a trailing comma and missing semicolon, which may cause a syntax error.🧰 Tools
🪛 ESLint
[error] 118-120: Replace
⏎······API_RESPONSES.COHORT_DATA_RESPONSE,⏎····)
withAPI_RESPONSES.COHORT_DATA_RESPONSE);
(prettier/prettier)
399-403
:⚠️ Potential issueFix SQL injection vulnerability in query construction
The query is constructed using string interpolation, which is vulnerable to SQL injection attacks. Use parameterized queries instead:
-let query = `SELECT * FROM public."CohortAcademicYear" WHERE "cohortId"= '${cohortIds}' AND "academicYearId" = '${academicYearId}'`; +let query = `SELECT * FROM public."CohortAcademicYear" WHERE "cohortId"= $1 AND "academicYearId" = $2`; +let getCohortAcademicYearId = await this.usersRepository.query(query, [cohortIds, academicYearId]);Likely invalid or redundant comment.
private async getCohortHierarchy( | ||
parentId: string, | ||
customField?: boolean | ||
): Promise<any> { | ||
const childData = await this.cohortRepository.find({ where: { parentId } }); | ||
const hierarchy = []; | ||
let customFieldDetails; | ||
let childHierarchy; | ||
for (const data of childData) { | ||
if (customField) { | ||
childHierarchy = await this.getCohortHierarchy( | ||
data.cohortId, | ||
customField | ||
); | ||
customFieldDetails = await this.getCohortCustomFieldDetails( | ||
data.cohortId | ||
); | ||
} else { | ||
childHierarchy = await this.getCohortHierarchy(data.cohortId); | ||
} | ||
hierarchy.push({ | ||
cohortId: data.cohortId, | ||
name: data.name, | ||
parentId: data.parentId, | ||
type: data.type, | ||
status: data.status, | ||
customField: customFieldDetails || [], | ||
childData: childHierarchy, | ||
}); | ||
} | ||
|
||
LoggerUtil.log( | ||
API_RESPONSES.COHORT_HIERARCHY, | ||
) | ||
|
||
return hierarchy; | ||
} |
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.
🛠️ Refactor suggestion
Enhance error handling and prevent stack overflow in recursive method
The recursive implementation of getCohortHierarchy
could lead to stack overflow for deep hierarchies. Consider:
- Adding a depth limit parameter
- Implementing error handling for database queries
- Using an iterative approach for better performance
-private async getCohortHierarchy(parentId: string, customField?: boolean): Promise<any> {
+private async getCohortHierarchy(parentId: string, customField?: boolean, maxDepth: number = 10): Promise<any> {
+ if (maxDepth <= 0) {
+ return [];
+ }
try {
const childData = await this.cohortRepository.find({ where: { parentId } });
const hierarchy = [];
let customFieldDetails;
let childHierarchy;
for (const data of childData) {
if (customField) {
childHierarchy = await this.getCohortHierarchy(
data.cohortId,
customField,
+ maxDepth - 1
);
customFieldDetails = await this.getCohortCustomFieldDetails(
data.cohortId
);
} else {
- childHierarchy = await this.getCohortHierarchy(data.cohortId);
+ childHierarchy = await this.getCohortHierarchy(data.cohortId, customField, maxDepth - 1);
}
hierarchy.push({
cohortId: data.cohortId,
name: data.name,
parentId: data.parentId,
type: data.type,
status: data.status,
customField: customFieldDetails || [],
childData: childHierarchy,
});
}
return hierarchy;
+ } catch (error) {
+ LoggerUtil.error(
+ API_RESPONSES.SERVER_ERROR,
+ `Error in getCohortHierarchy: ${error.message}`
+ );
+ throw error;
+ }
}
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
private async getCohortHierarchy( | |
parentId: string, | |
customField?: boolean | |
): Promise<any> { | |
const childData = await this.cohortRepository.find({ where: { parentId } }); | |
const hierarchy = []; | |
let customFieldDetails; | |
let childHierarchy; | |
for (const data of childData) { | |
if (customField) { | |
childHierarchy = await this.getCohortHierarchy( | |
data.cohortId, | |
customField | |
); | |
customFieldDetails = await this.getCohortCustomFieldDetails( | |
data.cohortId | |
); | |
} else { | |
childHierarchy = await this.getCohortHierarchy(data.cohortId); | |
} | |
hierarchy.push({ | |
cohortId: data.cohortId, | |
name: data.name, | |
parentId: data.parentId, | |
type: data.type, | |
status: data.status, | |
customField: customFieldDetails || [], | |
childData: childHierarchy, | |
}); | |
} | |
LoggerUtil.log( | |
API_RESPONSES.COHORT_HIERARCHY, | |
) | |
return hierarchy; | |
} | |
private async getCohortHierarchy( | |
parentId: string, | |
customField?: boolean, | |
maxDepth: number = 10 | |
): Promise<any> { | |
if (maxDepth <= 0) { | |
return []; | |
} | |
try { | |
const childData = await this.cohortRepository.find({ where: { parentId } }); | |
const hierarchy = []; | |
let customFieldDetails; | |
let childHierarchy; | |
for (const data of childData) { | |
if (customField) { | |
childHierarchy = await this.getCohortHierarchy( | |
data.cohortId, | |
customField, | |
maxDepth - 1 | |
); | |
customFieldDetails = await this.getCohortCustomFieldDetails( | |
data.cohortId | |
); | |
} else { | |
childHierarchy = await this.getCohortHierarchy( | |
data.cohortId, | |
customField, | |
maxDepth - 1 | |
); | |
} | |
hierarchy.push({ | |
cohortId: data.cohortId, | |
name: data.name, | |
parentId: data.parentId, | |
type: data.type, | |
status: data.status, | |
customField: customFieldDetails || [], | |
childData: childHierarchy, | |
}); | |
} | |
return hierarchy; | |
} catch (error) { | |
LoggerUtil.error( | |
API_RESPONSES.SERVER_ERROR, | |
`Error in getCohortHierarchy: ${error.message}` | |
); | |
throw error; | |
} | |
} |
🧰 Tools
🪛 ESLint
[error] 1029-1031: Replace ⏎······API_RESPONSES.COHORT_HIERARCHY,⏎····)
with API_RESPONSES.COHORT_HIERARCHY);
(prettier/prettier)
const apiId = APIID.VERIFY_OTP; | ||
try { | ||
const { mobile, otp, hash, reason, username } = body; | ||
if (!otp || !hash || !reason) { | ||
return APIResponse.error( | ||
response, | ||
apiId, | ||
API_RESPONSES.BAD_REQUEST, | ||
API_RESPONSES.OTP_VALIDED_REQUIRED_KEY, | ||
HttpStatus.BAD_REQUEST | ||
); | ||
} | ||
// Determine the value to use for verification based on the reason | ||
let identifier: string; | ||
if (reason === 'signup') { | ||
if (!mobile) { | ||
return APIResponse.error( | ||
response, | ||
apiId, | ||
API_RESPONSES.BAD_REQUEST, | ||
API_RESPONSES.MOBILE_REQUIRED, | ||
HttpStatus.BAD_REQUEST | ||
); | ||
} | ||
identifier = this.formatMobileNumber(mobile); // Assuming this formats the mobile number | ||
} else if (reason === 'forgot') { | ||
if (!username) { | ||
return APIResponse.error( | ||
response, | ||
apiId, | ||
API_RESPONSES.BAD_REQUEST, | ||
API_RESPONSES.USERNAME_REQUIRED, | ||
HttpStatus.BAD_REQUEST | ||
); | ||
} | ||
identifier = username; | ||
} else { | ||
return APIResponse.error( | ||
response, | ||
apiId, | ||
API_RESPONSES.BAD_REQUEST, | ||
API_RESPONSES.INVALID_REASON, | ||
HttpStatus.BAD_REQUEST | ||
); | ||
} | ||
const [hashValue, expires] = hash.split('.'); | ||
if (!hashValue || !expires || isNaN(parseInt(expires))) { | ||
return APIResponse.error( | ||
response, | ||
apiId, | ||
API_RESPONSES.BAD_REQUEST, | ||
API_RESPONSES.INVALID_HASH_FORMAT, | ||
HttpStatus.BAD_REQUEST | ||
); | ||
} | ||
|
||
if (Date.now() > parseInt(expires)) { | ||
return APIResponse.error( | ||
response, | ||
apiId, | ||
API_RESPONSES.OTP_EXPIRED, | ||
API_RESPONSES.OTP_EXPIRED, | ||
HttpStatus.BAD_REQUEST | ||
); | ||
} | ||
const data = `${identifier}.${otp}.${reason}.${expires}`; | ||
const calculatedHash = this.authUtils.calculateHash(data, this.smsKey); // Create hash | ||
|
||
if (calculatedHash === hashValue) { | ||
return await APIResponse.success( | ||
response, | ||
apiId, | ||
{ success: true }, | ||
HttpStatus.OK, | ||
API_RESPONSES.OTP_VALID | ||
); | ||
} else { | ||
return APIResponse.error( | ||
response, | ||
apiId, | ||
API_RESPONSES.OTP_INVALID, | ||
API_RESPONSES.OTP_INVALID, | ||
HttpStatus.BAD_REQUEST | ||
); | ||
} | ||
} catch (e) { | ||
LoggerUtil.error( | ||
`${API_RESPONSES.SERVER_ERROR}`, | ||
`Error during OTP verification: ${e.message}`, | ||
apiId | ||
); | ||
return APIResponse.error( | ||
response, | ||
apiId, | ||
API_RESPONSES.SERVER_ERROR, | ||
`Error : ${e?.message}`, | ||
HttpStatus.INTERNAL_SERVER_ERROR | ||
); | ||
} | ||
} |
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.
🛠️ Refactor suggestion
Enhance OTP security implementation
The OTP implementation should include:
- Rate limiting for OTP attempts
- OTP cleanup mechanism
- Secure storage of OTP hashes
private async validateOtpAttempts(identifier: string): Promise<void> {
const attempts = await this.getAttempts(identifier);
if (attempts >= MAX_ATTEMPTS) {
throw new TooManyAttemptsException();
}
}
private async cleanupExpiredOTPs(): Promise<void> {
// Implementation to clean up expired OTPs
// This should be called periodically
}
🧰 Tools
🪛 Biome (1.9.4)
[error] 1985-1985: isNaN is unsafe. It attempts a type coercion. Use Number.isNaN instead.
See the MDN documentation for more details.
Unsafe fix: Use Number.isNaN instead.
(lint/suspicious/noGlobalIsNan)
🪛 ESLint
[error] 1861-1861: Replace mobileOrUsername:·string,·otp:·string,·reason:·string
with ⏎····mobileOrUsername:·string,⏎····otp:·string,⏎····reason:·string⏎··
(prettier/prettier)
[error] 1885-1885: Replace mobile,·reason
with ⏎········mobile,⏎········reason⏎······
(prettier/prettier)
[error] 1891-1891: Insert ,
(prettier/prettier)
[error] 1893-1893: Insert ,
(prettier/prettier)
[error] 1902-1903: Delete ⏎···
(prettier/prettier)
[error] 1924-1924: Replace mobileWithCode,·otp,·reason
with ⏎········mobileWithCode,⏎········otp,⏎········reason⏎······
(prettier/prettier)
[error] 1927-1927: Insert ,
(prettier/prettier)
[error] 1930-1930: Replace "OTP",·"SEND_OTP",·replacements,·[mobile]
with ⏎········"OTP",⏎········"SEND_OTP",⏎········replacements,⏎········[mobile]⏎······
(prettier/prettier)
[error] 1932-1933: Delete ⏎···
(prettier/prettier)
[error] 1953-1953: Replace 'signup'
with "signup"
(prettier/prettier)
[error] 1964-1964: Replace 'forgot'
with "forgot"
(prettier/prettier)
[error] 1984-1984: Replace '.'
with "."
(prettier/prettier)
} | ||
// Determine email address | ||
let emailOfUser = userData?.email; | ||
if (!emailOfUser) { | ||
const createdByUser = await this.usersRepository.findOne({ | ||
where: { userId: userData.createdBy }, | ||
}); | ||
emailOfUser = createdByUser?.email; | ||
} | ||
if (!emailOfUser) { | ||
return APIResponse.error( | ||
response, | ||
apiId, | ||
API_RESPONSES.BAD_REQUEST, | ||
API_RESPONSES.EMAIL_NOT_FOUND_FOR_RESET, | ||
HttpStatus.BAD_REQUEST | ||
); | ||
} | ||
|
||
//Generate Token for password Reset | ||
const tokenPayload = { | ||
sub: userData.userId, | ||
email: emailOfUser, | ||
}; | ||
const jwtExpireTime = this.jwt_password_reset_expires_In; | ||
const jwtSecretKey = this.jwt_secret; | ||
const frontEndUrl = `${this.reset_frontEnd_url}/reset-password`; | ||
const resetToken = await this.jwtUtil.generateTokenForForgotPassword( | ||
tokenPayload, | ||
jwtExpireTime, | ||
jwtSecretKey | ||
); | ||
|
||
// Format expiration time | ||
const time = formatTime(jwtExpireTime); | ||
const programName = userData?.tenantData[0]?.tenantName; | ||
const capilatizeFirstLettterOfProgram = programName | ||
? programName.charAt(0).toUpperCase() + programName.slice(1) | ||
: "Learner Account"; | ||
|
||
//Send Notification | ||
const notificationPayload = { | ||
isQueue: false, | ||
context: "USER", | ||
key: "OnForgotPasswordReset", | ||
replacements: { | ||
"{username}": userData?.firstName + " " + userData?.lastName, | ||
"{resetToken}": resetToken, | ||
"{programName}": capilatizeFirstLettterOfProgram, | ||
"{expireTime}": time, | ||
"{frontEndUrl}": frontEndUrl, | ||
"{redirectUrl}": redirectUrl, | ||
}, | ||
email: { | ||
receipients: [emailOfUser], | ||
}, | ||
}; | ||
|
||
const mailSend = await this.notificationRequest.sendNotification( | ||
notificationPayload | ||
); | ||
|
||
if (mailSend?.result?.email?.errors.length > 0) { | ||
LoggerUtil.error( | ||
`${API_RESPONSES.BAD_REQUEST}`, | ||
`Error: ${API_RESPONSES.RESET_PASSWORD_LINK_FAILED}`, | ||
apiId | ||
); | ||
return APIResponse.error( | ||
response, | ||
apiId, | ||
mailSend?.result?.email?.errors, | ||
API_RESPONSES.RESET_PASSWORD_LINK_FAILED, | ||
HttpStatus.BAD_REQUEST | ||
); | ||
} | ||
|
||
async searchUser(tenantId: string, | ||
return await APIResponse.success( | ||
response, | ||
apiId, | ||
{ email: emailOfUser }, | ||
HttpStatus.OK, | ||
API_RESPONSES.RESET_PASSWORD_LINK_SUCCESS | ||
); | ||
} catch (e) { | ||
LoggerUtil.error( | ||
`${API_RESPONSES.INTERNAL_SERVER_ERROR}`, | ||
`Error: ${e.message}`, | ||
apiId | ||
); | ||
return APIResponse.error( | ||
response, | ||
apiId, | ||
API_RESPONSES.INTERNAL_SERVER_ERROR, | ||
`Error : ${e.message}`, | ||
HttpStatus.INTERNAL_SERVER_ERROR | ||
); | ||
} | ||
} |
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.
🛠️ Refactor suggestion
Enhance password reset security
The password reset implementation should include:
- Password complexity validation
- Token revocation after reset
- Rate limiting for reset attempts
private async validatePasswordResetAttempts(username: string): Promise<void> {
const attempts = await this.getResetAttempts(username);
if (attempts >= MAX_RESET_ATTEMPTS) {
throw new TooManyAttemptsException();
}
}
private async validatePasswordComplexity(password: string): Promise<void> {
const complexityRules = {
minLength: 8,
requireNumbers: true,
requireSpecialChars: true
};
// Implementation
}
let query = `SELECT * FROM public."CohortAcademicYear" WHERE "cohortId"= '${cohortIds}' AND "academicYearId" = '${academicYearId}'`; | ||
|
||
let getCohortAcademicYearId = await this.usersRepository.query( | ||
query | ||
); |
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.
Fix SQL injection vulnerability
The query string is constructed using string interpolation, which is vulnerable to SQL injection attacks.
Use parameterized queries instead:
let query = `SELECT * FROM public."CohortAcademicYear" WHERE "cohortId"= $1 AND "academicYearId" = $2`;
let getCohortAcademicYearId = await this.usersRepository.query(query, [cohortIds, academicYearId]);
🧰 Tools
🪛 ESLint
[error] 1399-1399: 'query' is never reassigned. Use 'const' instead.
(prefer-const)
[error] 1401-1401: 'getCohortAcademicYearId' is never reassigned. Use 'const' instead.
(prefer-const)
sanitizeInput(value) { | ||
if (typeof value === 'string') { | ||
// Escape special characters for SQL | ||
return value.replace(/[%_\\]/g, '\\$&'); | ||
} | ||
// For other types, return the value as is or implement specific sanitization logic | ||
return value; | ||
} |
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.
🛠️ Refactor suggestion
Enhance input sanitization
The current sanitization only escapes SQL special characters. Consider using parameterized queries and implementing additional sanitization:
private sanitizeInput(value: unknown): string | unknown {
if (typeof value === 'string') {
// Remove potential XSS vectors
return value
.replace(/[<>]/g, '') // Remove < and >
.replace(/javascript:/gi, '') // Remove javascript: protocol
.replace(/on\w+=/gi, ''); // Remove event handlers
}
return value;
}
🧰 Tools
🪛 ESLint
[error] 2263-2263: Replace 'string'
with "string"
(prettier/prettier)
[error] 2265-2265: Replace '\\$&'
with "\\$&"
(prettier/prettier)
Summary by CodeRabbit
Release Notes
New Features
Improvements
Breaking Changes
Bug Fixes