Skip to content
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

Open
wants to merge 640 commits into
base: main
Choose a base branch
from
Open

Release 1.0.0 #110

wants to merge 640 commits into from

Conversation

Sourav-Tekdi
Copy link
Contributor

@Sourav-Tekdi Sourav-Tekdi commented Dec 5, 2024

Summary by CodeRabbit

Release Notes

  • New Features

    • Added Academic Years management functionality.
    • Introduced Cohort Academic Year tracking.
    • Enhanced user authentication with OTP and password reset capabilities.
    • Added custom field management for cohorts.
    • Implemented new API response messaging structure.
    • Added new user update functionality in Keycloak integration.
    • Introduced new methods for cohort members management.
    • Expanded API identifiers for various operations.
  • Improvements

    • Streamlined cohort search and filtering.
    • Improved error handling across services.
    • Updated authentication and authorization mechanisms.
    • Enhanced user and tenant management.
    • Enhanced logging for error handling in Keycloak operations.
    • Refined method signatures and parameter handling for better clarity.
  • Breaking Changes

    • Removed Hasura-related modules and services.
    • Discontinued attendance tracking functionality.
    • Refactored service adapters to focus on PostgreSQL.
  • Bug Fixes

    • Corrected import paths and module dependencies.
    • Fixed validation and error response handling.
    • Improved data consistency across services.
    • Enhanced response structures for API operations.

Xitija and others added 30 commits October 7, 2024 16:09
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
Copy link

@coderabbitai coderabbitai bot left a 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 issue

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"= '${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 issue

Avoid using isNaN; use Number.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:

  1. Creating a centralized error handling utility
  2. Adding structured logging with correlation IDs
  3. 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

📥 Commits

Reviewing files that changed from the base of the PR and between 71fc7b5 and 6b38b77.

📒 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)

Comment on lines +1885 to +2053
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
);
}
}
Copy link

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:

  1. Rate limiting for OTP attempts
  2. OTP cleanup mechanism
  3. 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)

Copy link

@coderabbitai coderabbitai bot left a 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:

  1. User CRUD operations
  2. Authentication/Password operations
  3. OTP operations
  4. 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:

  1. UserAuthenticationService
  2. OtpService
  3. UserProfileService
  4. 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 of O(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

📥 Commits

Reviewing files that changed from the base of the PR and between 6b38b77 and fd2b4c4.

📒 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 uses any 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 suggestion

Use 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)

Comment on lines +19 to +20
getUsersDetailsById(userData: UserData, response: any);
updateUser(userDto?: UserUpdateDTO, response?: Response): Promise<void>;
Copy link

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

Comment on lines 1871 to 2038
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
);
}
}
Copy link

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:

  1. Rate limiting for verification attempts
  2. Prevention of timing attacks
  3. 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)

Copy link

@coderabbitai coderabbitai bot left a 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 suggestion

Define request body types for better type safety.

The forgotPassword method uses any 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 issue

Fix 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 suggestion

Use 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 suggestion

Enhance 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.* and api.cohort-academic-year.* (kebab-case)
  • api.academicYear.* and api.cohortAcademicYear.* (camelCase)

23-27: Inconsistent search endpoint naming.

The cohort member endpoints use list instead of search:

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 of get:

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 findDynamicOptions

The 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 class

The 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:

  1. Moving configuration to a dedicated config service
  2. Grouping related dependencies
  3. 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

📥 Commits

Reviewing files that changed from the base of the PR and between fd2b4c4 and 48bf2e8.

📒 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 issue

Critical: SQL injection vulnerability in whereClause construction

The code constructs SQL queries by directly concatenating user input (requiredData.context and requiredData.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 suggestion

Improve 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 issue

Replace isNaN with Number.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)

Comment on lines 57 to 59
SEND_OTP: "api.send.OTP",
VERIFY_OTP: "api.verify.OTP",
SEND_RESET_OTP: 'api.send.reset.otp'
Copy link

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.

  1. Line 59 uses single quotes instead of double quotes
  2. Missing trailing comma after the last property
  3. Inconsistent casing in OTP endpoints (OTP vs otp)

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.

Suggested change
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)

Comment on lines +687 to +694
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}'`;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

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;
Copy link

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)

Comment on lines 1051 to 1060
} catch (e) {
LoggerUtil.error(
`${API_RESPONSES.SERVER_ERROR}`,
`Error: ${e.message}`,
)
return new ErrorResponse({
errorCode: "400",
errorMessage: e,
});
}
Copy link

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)

Comment on lines +23 to +24
ACADEMICYEAR_EXIST: "Academic Year Already Exist",
ACADEMICYEAR_YEAR: "Already Exist",
Copy link

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',
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

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)

Comment on lines +73 to +188
//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'
Copy link

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.

Suggested change
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)

Copy link

@coderabbitai coderabbitai bot left a 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 suggestion

Use 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 suggestion

Use 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 issue

Fix 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 suggestion

Enhance 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 suggestion

Replace isNaN with Number.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 issue

Fix 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 suggestion

Enhance 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 statements

Add 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

📥 Commits

Reviewing files that changed from the base of the PR and between 48bf2e8 and 1d4e6c5.

📒 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 handling

The 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 functionality

Excellent use of TypeScript features:

  • Clear interface definitions
  • Proper type safety
  • Comprehensive error handling
  • Good use of optional parameters

Also applies to: 148-211

Comment on lines +92 to +101
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');
}
Copy link

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)

Comment on lines +1790 to +1823
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,
});
Copy link

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.

Suggested change
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();
}

Copy link

@coderabbitai coderabbitai bot left a 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 issue

Critical: 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:

  1. api.create.cohortAcademicYear differs from the pattern used in other endpoints like api.cohort.create
  2. api.assigntenant.create could be better structured as a sub-resource

Consider 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

📥 Commits

Reviewing files that changed from the base of the PR and between 1d4e6c5 and a405005.

📒 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:

  1. Password reset and OTP endpoints are rate-limited
  2. Authentication endpoints have proper session management
  3. 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 both deleteFieldOptions and getFormCustomField methods.

- deleteFieldOptions(requiredData, response);
- getFormCustomField(requiredData, response);
+ deleteFieldOptions(requiredData: { [key: string]: any }, response: Response);
+ getFormCustomField(requiredData: { [key: string]: any }, response: Response);

Comment on lines +16 to +17
PRIVILEGE_BYROLEID: "api.privilegebyRoleId.get",
PRIVILEGE_BYPRIVILEGEID: "api.privilegebyPrivilegeId.get",
Copy link

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:

  1. privilegebyRoleId vs privilege
  2. fieldValues vs fields
  3. OTP vs otp

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

Comment on lines +16 to +21
updateFields(
fieldId: any,
request: any,
fieldsUpdateDto: FieldsUpdateDto,
response: Response
);
Copy link

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.

Suggested change
updateFields(
fieldId: any,
request: any,
fieldsUpdateDto: FieldsUpdateDto,
response: Response
);
updateFields(
fieldId: string,
request: any,
fieldsUpdateDto: FieldsUpdateDto,
response: Response
);

Comment on lines +23 to +28
searchFields(
tenantid,
request: any,
fieldsSearchDto: FieldsSearchDto,
response: Response
);
Copy link

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.

Suggested change
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}`) : "";
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

⚠️ Potential issue

Critical: SQL injection vulnerability and confusing assignment.

The method has two issues:

  1. SQL injection vulnerability due to unparameterized queries
  2. 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)

Comment on lines +41 to +44
const fileread = readFileSync(
join(process.cwd(), "src/utils/corefield.json"),
"utf8"
);
Copy link

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.

Suggested change
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"
);

Comment on lines +68 to +74
if (requiredData.context) {
whereClause += ` OR context = '${requiredData.context}' AND "contextType" IS NULL`;
}

if (requiredData.contextType) {
whereClause += ` OR "contextType" = '${requiredData.contextType}'`;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

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.

Suggested change
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]);

Copy link

sonarqubecloud bot commented Feb 5, 2025

Quality Gate Failed Quality Gate failed

Failed conditions
1 Security Hotspot
C Security Rating on New Code (required ≥ A)
C Reliability Rating on New Code (required ≥ A)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE

Copy link

@coderabbitai coderabbitai bot left a 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 security

The validation logic could be strengthened by:

  1. Adding input sanitization for all string inputs
  2. Implementing rate limiting for validation requests
  3. 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

📥 Commits

Reviewing files that changed from the base of the PR and between a405005 and 5c01276.

📒 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 in LoggerUtil.log calls

The 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,⏎····) with API_RESPONSES.COHORT_DATA_RESPONSE);

(prettier/prettier)


399-403: ⚠️ Potential issue

Fix 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.

Comment on lines +998 to +1034
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;
}
Copy link

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:

  1. Adding a depth limit parameter
  2. Implementing error handling for database queries
  3. 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.

Suggested change
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)

Comment on lines +1861 to +2038
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
);
}
}
Copy link

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:

  1. Rate limiting for OTP attempts
  2. OTP cleanup mechanism
  3. 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)

Comment on lines +103 to +219
}
// 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
);
}
}
Copy link

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:

  1. Password complexity validation
  2. Token revocation after reset
  3. 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
}

Comment on lines +1399 to +1403
let query = `SELECT * FROM public."CohortAcademicYear" WHERE "cohortId"= '${cohortIds}' AND "academicYearId" = '${academicYearId}'`;

let getCohortAcademicYearId = await this.usersRepository.query(
query
);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

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)

Comment on lines +2262 to +2269
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;
}
Copy link

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)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants