Skip to content

Conversation

@KevLehman
Copy link
Member

@KevLehman KevLehman commented Jan 29, 2025

Proposed changes (including videos or screenshots)

Issue(s)

CORE-1063

Steps to test or reproduce

Further comments


This pull request focuses on migrating the User model to TypeScript in the Rocket.Chat repository. The changes span multiple files, primarily enhancing type safety and code maintainability. Key updates include:

  • Introduction of type-safe string validation for user IDs in file objects.
  • Use of FindOptions from MongoDB to type the options parameter in the findUsersToAutocomplete function.
  • Type safety improvements in various APIs, including channels, groups, and VoIP rooms, by adding types like UserStatus and optimizing query logic.
  • Replacement of string literals with enum values for user status handling in several components, such as the AppActivationBridge class and LDAP management.
  • Introduction of type guard functions and improved error handling in methods related to user keys and LDAP channel creation.
  • Updates to the IUser interface and IUsersModel interface with new properties and refined method signatures.
  • Migration from JavaScript to TypeScript in the Users model, indicated by changes in the tsconfig.json file.

Overall, these changes aim to improve type safety, consistency, and maintainability across the codebase.

@dionisio-bot
Copy link
Contributor

dionisio-bot bot commented Jan 29, 2025

Looks like this PR is not ready to merge, because of the following issues:

  • This PR is targeting the wrong base branch. It should target 7.6.0, but it targets 7.5.0

Please fix the issues and try again

If you have any trouble, please check the PR guidelines

@changeset-bot
Copy link

changeset-bot bot commented Jan 29, 2025

⚠️ No Changeset found

Latest commit: 625bc85

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@github-actions
Copy link
Contributor

github-actions bot commented Jan 29, 2025

PR Preview Action v1.6.0

🚀 View preview at
https://RocketChat.github.io/Rocket.Chat/pr-preview/pr-35056/

Built to branch gh-pages at 2025-03-18 22:25 UTC.
Preview will be ready when the GitHub Pages deployment is complete.

@codecov
Copy link

codecov bot commented Jan 29, 2025

Codecov Report

Attention: Patch coverage is 33.33333% with 4 lines in your changes missing coverage. Please review.

Project coverage is 59.66%. Comparing base (a4e4590) to head (625bc85).
Report is 24 commits behind head on develop.

Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff             @@
##           develop   #35056      +/-   ##
===========================================
- Coverage    59.66%   59.66%   -0.01%     
===========================================
  Files         2826     2826              
  Lines        68369    68397      +28     
  Branches     15148    15155       +7     
===========================================
+ Hits         40793    40809      +16     
- Misses       24962    24971       +9     
- Partials      2614     2617       +3     
Flag Coverage Δ
unit 75.86% <33.33%> (-0.01%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

🚀 New features to boost your workflow:
  • Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@KevLehman KevLehman marked this pull request as ready for review January 29, 2025 19:47
@KevLehman KevLehman requested review from a team as code owners January 29, 2025 19:47
Copy link
Contributor

@pierre-lehnen-rc pierre-lehnen-rc left a comment

Choose a reason for hiding this comment

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

32/34 files reviewed, just missing the two big ones.

@KevLehman KevLehman added this to the 7.4.0 milestone Jan 31, 2025
@KevLehman KevLehman added the stat: QA assured Means it has been tested and approved by a company insider label Feb 18, 2025
@dionisio-bot dionisio-bot bot added the stat: ready to merge PR tested and approved waiting for merge label Feb 18, 2025
@scuciatto scuciatto modified the milestones: 7.4.0, 7.5.0 Feb 21, 2025
@kodiakhq
Copy link
Contributor

kodiakhq bot commented Feb 25, 2025

This PR currently has a merge conflict. Please resolve this and then re-add the ['stat: ready to merge', 'automerge'] label.

@kodiakhq kodiakhq bot removed the stat: ready to merge PR tested and approved waiting for merge label Feb 25, 2025
@Dnouv Dnouv requested a review from Copilot March 10, 2025 06:32
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

PR Overview

This pull request updates various parts of the codebase to TypeScript by adding appropriate type casts, type imports, and minor refactoring changes for better type safety and consistency.

  • Type adjustments and cast fixes in several API routes and utility functions
  • Enhanced error handling and logging in LDAP and livechat features
  • Optional parameter changes and type guard additions for improved robustness

Reviewed Changes

File Description
apps/meteor/imports/personal-access-tokens/server/api/methods/regenerateToken.ts Added a ts-expect-error comment to flag an apparent type discrepancy in token generation
apps/meteor/app/api/server/v1/groups.ts Updated type cast for user status filter
apps/meteor/app/api/server/v1/channels.ts Added type annotations and removed unnecessary async/await
apps/meteor/ee/app/livechat-enterprise/server/lib/routing/LoadRotation.ts Enhanced check for agent existence using optional chaining on the username property
apps/meteor/app/importer/server/classes/converters/ConverterCache.ts Improved user existence check before caching username to ID mapping
apps/meteor/ee/server/lib/ldap/Manager.ts Added error logging when a required user is not found for channel ownership
apps/meteor/app/api/server/v1/users.ts Updated user lookup to include a TODO related to types and projection
apps/meteor/app/api/server/lib/users.ts Ensured options are correctly cast as FindOptions for Mongo queries
apps/meteor/app/api/server/helpers/addUserToFileObj.ts Added a helper to ensure only valid string IDs are processed
apps/meteor/app/livechat/server/lib/isMessageFromBot.ts Changed return type annotation, though the implementation remains unchanged
apps/meteor/app/livechat/server/business-hour/AbstractBusinessHour.ts Replaced string literal 'offline' with UserStatus.OFFLINE for consistency
apps/meteor/app/livechat/server/lib/contacts/getContacts.ts Ensured manager IDs are filtered for undefined values
apps/meteor/app/importer/server/classes/converters/UserConverter.ts Updated function types to better handle optional user records
apps/meteor/app/lib/server/functions/updateGroupDMsName.ts Refactored member filtering to use an explicit type guard
apps/meteor/app/api/server/v1/voip/rooms.ts Adjusted type annotations when fetching the agent object
apps/meteor/app/e2e/server/methods/setUserPublicAndPrivateKeys.ts Added a type guard to verify key pair results before proceeding
apps/meteor/app/apps/server/bridges/activation.ts Updated activation bridge to use a TS constant for user status
apps/meteor/app/api/server/v1/im.ts Added explicit FindOptions type to query options
apps/meteor/app/livechat/server/api/lib/users.ts Removed unnecessary generic parameter for agent lookup
apps/meteor/app/livechat/server/business-hour/Helper.ts Converted function parameter types to use optional parameters

Copilot reviewed 35 out of 35 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (1)

apps/meteor/imports/personal-access-tokens/server/api/methods/regenerateToken.ts:36

  • Consider addressing the underlying type mismatch to ensure consistent token generation behavior, or formally deprecate the method instead of suppressing the TypeScript error.
// @ts-expect-error - This doesn't make sense. We're querying a user and expecting Mongo to return a token. We should look into this one

@sampaiodiego
Copy link
Member

@kody start-review

@kody-ai
Copy link

kody-ai bot commented Mar 10, 2025

Code Review Completed! 🔥

The code review was successfully completed based on your current configurations.

Kody Guide: Usage and Configuration
Interacting with Kody
  • Request a Review: Ask Kody to review your PR manually by adding a comment with the @kody start-review command at the root of your PR.

  • Provide Feedback: Help Kody learn and improve by reacting to its comments with a 👍 for helpful suggestions or a 👎 if improvements are needed.

Current Kody Configuration
Review Options

The following review options are enabled or disabled:

Options Enabled
Security
Code Style
Kody Rules
Refactoring
Error Handling
Maintainability
Potential Issues
Documentation And Comments
Performance And Optimization

Access your configuration settings here.

@@ -24,9 +25,7 @@ async function getUsersWhoAreInTheSameGroupDMsAs(user: IUser) {
room.uids.forEach((uid) => uid !== user._id && userIds.add(uid));
Copy link

Choose a reason for hiding this comment

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

kody code-reviewKody Rules

room.uids.forEach((uid) => {
	if (uid !== user._id) {
		userIds.add(uid);
	}
});

Use of the comma operator in forEach loops and other expressions reduces readability and can make the code harder to debug.

This issue appears in multiple locations:

  • apps/meteor/app/lib/server/functions/updateGroupDMsName.ts: Lines 25-25
  • packages/models/src/models/Users.ts: Lines 598-598
  • packages/models/src/models/Users.ts: Lines 138-140

Please avoid using the comma operator in favor of separate statements or explicit control structures to improve code readability.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

const uids = files.map(({ userId }) => userId).filter(Boolean);
const uids = files.map(({ userId }) => userId).filter(isString);

const users = await Users.findByIds(uids, { projection: { name: 1, username: 1 } }).toArray();
Copy link

Choose a reason for hiding this comment

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

kody code-reviewError Handling

try {
  const users = await Users.findByIds(uids, { projection: { name: 1, username: 1 } }).toArray();
  return files.map((file) => {
    const user = users.find(({ _id }) => _id === file.userId);
    return { ...file, user };
  });
} catch (error) {
  console.error('Failed to fetch users:', error);
  throw new Error('Failed to fetch user data');
}

Multiple instances of missing or inadequate error handling in database queries and asynchronous operations, which could lead to unhandled promise rejections or runtime errors.

This issue appears in multiple locations:

  • apps/meteor/app/api/server/helpers/addUserToFileObj.ts: Lines 9-9
  • apps/meteor/app/api/server/v1/channels.ts: Lines 1011-1011
  • apps/meteor/app/api/server/v1/channels.ts: Lines 1050-1050
  • apps/meteor/server/lib/ldap/Manager.ts: Lines 504-511
  • packages/models/src/models/Users.ts: Lines 1588-1609

Please ensure all database queries and asynchronous operations are wrapped in try-catch blocks to handle potential errors and prevent unhandled promise rejections.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

Comment on lines 265 to 269
if (fields.userId) {
user = await Users.findOneById(fields.userId, { projection: { username: 1 } });
} else if (fields.username) {
user = await Users.findOneByUsernameIgnoringCase(fields.username, { projection: { username: 1 } });
// TODO: Fix these types. We're passing a projection but the `user` variable is expecting way more data than this projection
user = await Users.findOneByUsernameIgnoringCase<IUser>(fields.username, { projection: { username: 1 } });
Copy link

Choose a reason for hiding this comment

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

kody code-reviewPotential Issues

// TODO: Fix these types. We're passing a projection but the `user` variable is expecting way more data than this projection
const userWithUsername = await Users.findOneByUsernameIgnoringCase<Pick<IUser, 'username'>>(fields.username, { projection: { username: 1 } });
if (userWithUsername) {
  user = userWithUsername as IUser; // Temporary workaround until proper refactoring
} else {
  // Handle the case where no user is found
}

The TODO comment correctly identifies a type safety issue, but it would be better to also add a type assertion or refactor the code to ensure type safety. The current implementation could lead to runtime errors if the user object is used with the assumption of having more fields than just username. This implementation uses Pick to select only the 'username' property, ensuring type safety with the projection, and then asserts the type as IUser to maintain compatibility with the existing code. This is a temporary workaround until a proper refactoring can be done to address the underlying type mismatch.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

Comment on lines 36 to 38
// @ts-expect-error - This doesn't make sense. We're querying a user and expecting Mongo to return a token. We should look into this one
// or deprecate the method
return generatePersonalAccessTokenOfUser({ tokenName, userId, bypassTwoFactor: tokenExist.bypassTwoFactor || false });
Copy link

Choose a reason for hiding this comment

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

kody code-reviewPotential Issues

// @ts-expect-error - This doesn't make sense. We're querying a user and expecting Mongo to return a token. We should look into this one
// or deprecate the method
const newToken = await generatePersonalAccessTokenOfUser({ tokenName, userId, bypassTwoFactor: tokenExist.bypassTwoFactor || false });
if (!newToken) {
    throw new Meteor.Error('token-generation-failed', 'Failed to generate personal access token');
}
return newToken;

The @ts-expect-error comment indicates a potential type safety issue. While preserving the existing architecture and the comment indicating the need for future refactoring or deprecation, it's crucial to handle the potential for generatePersonalAccessTokenOfUser to return a falsy value, which could lead to unexpected behavior. This suggestion introduces error handling to address this issue.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

Comment on lines 303 to 307
getAgentInfo(
agentId: string,
agentId: IUser['_id'],
showAgentEmail?: boolean,
): Promise<Pick<ILivechatAgent, '_id' | 'name' | 'username' | 'phone' | 'customFields' | 'status' | 'livechat'> | null>;
): Promise<Pick<ILivechatAgent, '_id' | 'name' | 'username' | 'phone' | 'customFields' | 'status' | 'livechat' | 'emails'> | null>;
roleBaseQuery(userId: string): { _id: string };
Copy link

Choose a reason for hiding this comment

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

kody code-reviewPotential Issues

getAgentInfo(
		agentId: IUser['_id'],
		showAgentEmail?: boolean,
	): Promise<Partial<Pick<ILivechatAgent, '_id' | 'name' | 'username' | 'phone' | 'customFields' | 'status' | 'livechat' | 'emails'>> | null>;

The return type of getAgentInfo should use Partial<Pick<>> instead of Pick<> since some fields might be undefined as indicated by the optional fields in the return type.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

setE2EPublicAndPrivateKeysByUserId(userId: string, e2e: { public_key: string; private_key: string }): Promise<UpdateResult>;
rocketMailUnsubscribe(userId: string, createdAt: string): Promise<number>;
fetchKeysByUserId(userId: string): Promise<{ public_key: string; private_key: string } | Record<string, never>>;
fetchKeysByUserId(userId: string): Promise<{ public_key: string; private_key: string } | object>;
Copy link

Choose a reason for hiding this comment

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

kody code-reviewMaintainability

fetchKeysByUserId(userId: string): Promise<{ public_key: string; private_key: string } | Record<string, never>>;

The fetchKeysByUserId method's return type uses 'object' which is too broad and should be replaced with Record<string, never> for an empty object or a more specific type.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

Comment on lines +1017 to +1025
updateStatusById(
userId: IUser['_id'],
{
statusDefault,
status,
statusConnection,
statusText,
}: { statusDefault?: UserStatus; status: UserStatus; statusConnection: UserStatus; statusText?: string },
) {
Copy link

Choose a reason for hiding this comment

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

kody code-reviewMaintainability

interface IUserStatusUpdate {
  statusDefault?: UserStatus;
  status: UserStatus;
  statusConnection: UserStatus;
  statusText?: string;
}

updateStatusById(userId: IUser['_id'], statusUpdate: IUserStatusUpdate)

The updateStatusById method contains multiple status-related parameters in an object. Consider creating a dedicated interface/type for status updates to improve maintainability and type safety.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.


const user = await Users.findOneByUsernameIgnoringCase(roomOwner);
if (!user) {
logger.error(`Unable to find user '${roomOwner}' to be the owner of the channel '${channel}'.`);
Copy link
Member

Choose a reason for hiding this comment

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

not sure if this should be just a log or a throw, because on the old code if user was not found then createRoom would actually throw a Meteor.Error

@scuciatto scuciatto modified the milestones: 7.5.0, 7.6.0 Mar 20, 2025
@dionisio-bot dionisio-bot bot added the stat: ready to merge PR tested and approved waiting for merge label Mar 20, 2025
@ggazzo ggazzo merged commit 5387d91 into develop Mar 20, 2025
49 checks passed
@ggazzo ggazzo deleted the chore/users-to-ts branch March 20, 2025 14:05
abhinavkrin pushed a commit that referenced this pull request Mar 24, 2025
abhinavkrin pushed a commit that referenced this pull request Mar 24, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

stat: QA assured Means it has been tested and approved by a company insider stat: ready to merge PR tested and approved waiting for merge

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants