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

feat: functionality to view unread messages in chat #2611

Open
wants to merge 118 commits into
base: develop
Choose a base branch
from

Conversation

disha1202
Copy link

@disha1202 disha1202 commented Oct 25, 2024

What kind of change does this PR introduce?

Issue Number:

Fixes #2610

Did you add tests for your changes?

Snapshots/Videos:

If relevant, did you update the documentation?

Summary

Does this PR introduce a breaking change?

Other information

Have you read the contributing guide?

Summary by CodeRabbit

Release Notes

  • New Features

    • Introduced a new field to track unseen messages for users in chats.
    • Added a mutation to allow users to mark chat messages as read, enhancing user interaction.
  • Bug Fixes

    • Improved error handling for non-existent chats and users in message-related operations.
  • Tests

    • Enhanced test coverage for the new unseen messages functionality and the marking of messages as read.

Copy link

coderabbitai bot commented Oct 25, 2024

Walkthrough

The pull request introduces a new field unseenMessagesByUsers to the Chat type and a mutation markChatMessagesAsRead. These changes enhance the chat functionality by enabling the tracking of unseen messages for users and allowing users to mark messages as read. Additionally, modifications to the chat schema and related resolvers support these new features, ensuring proper error handling and data management.

Changes

File Change Summary
src/models/Chat.ts Added unseenMessagesByUsers: JSON to InterfaceChat and chatSchema. Updated timestamps option to false.
src/resolvers/Mutation/createChat.ts Introduced unseenMessagesByUsers variable in createChat function, modifying user existence checks.
src/resolvers/Mutation/index.ts Added import and integration of markChatMessagesAsRead function into Mutation object.
src/resolvers/Mutation/markChatMessagesAsRead.ts Created a new resolver for marking messages as read, including error handling for chat and user existence.
src/resolvers/Mutation/sendMessageToChat.ts Updated to handle unseen messages, modifying the unseenMessagesByUsers field after sending a message.
src/resolvers/Query/chatsByUserId.ts Added sorting functionality to chatsByUserId resolver based on updatedAt.
src/typeDefs/mutations.ts Added mutation markChatMessagesAsRead(chatId: ID!, userId: ID!): Chat @auth.
src/typeDefs/types.ts Added field unseenMessagesByUsers: JSON to Chat type.
src/types/generatedGraphQLTypes.ts Added optional field unseenMessagesByUsers and mutation markChatMessagesAsRead to GraphQL types.
tests/helpers/chat.ts Updated createTestChat function to include unseenMessagesByUsers property.
tests/resolvers/Mutation/markChatMessagesAsRead.spec.ts Added tests for markChatMessagesAsRead mutation, including error handling and successful execution.
tests/resolvers/Mutation/sendMessageToChat.spec.ts Enhanced tests for sendMessageToChat to account for unseen messages.

Assessment against linked issues

Objective Addressed Explanation
Add feature to view unread messages and mark messages as read in chat (#2610)

Possibly related PRs

Suggested reviewers

  • palisadoes
  • rishav-jha-mech

Poem

🐇 In the chat where messages hide,
Unseen whispers side by side.
With a click, they’re marked as read,
No more secrets left unsaid!
Hopping through the code so bright,
We bring the chats to new delight! 🌟


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

Our Pull Request Approval Process

We have these basic policies to make the approval process smoother for our volunteer team.

Testing Your Code

Please make sure your code passes all tests. Our test code coverage system will fail if these conditions occur:

  1. The overall code coverage drops below the target threshold of the repository
  2. Any file in the pull request has code coverage levels below the repository threshold
  3. Merge conflicts

The process helps maintain the overall reliability of the code base and is a prerequisite for getting your PR approved. Assigned reviewers regularly review the PR queue and tend to focus on PRs that are passing.

Reviewers

Do not assign reviewers. Our Queue Monitors will review your PR and assign them.
When your PR has been assigned reviewers contact them to get your code reviewed and approved via:

  1. comments in this PR or
  2. our slack channel

Reviewing Your Code

Your reviewer(s) will have the following roles:

  1. arbitrators of future discussions with other contributors about the validity of your changes
  2. point of contact for evaluating the validity of your work
  3. person who verifies matching issues by others that should be closed.
  4. person who gives general guidance in fixing your tests

CONTRIBUTING.md

Read our CONTRIBUTING.md file. Most importantly:

  1. PRs with issues not assigned to you will be closed by the reviewer
  2. Fix the first comment in the PR so that each issue listed automatically closes

Other

  1. 🎯 Please be considerate of our volunteers' time. Contacting the person who assigned the reviewers is not advised unless they ask for your input. Do not @ the person who did the assignment otherwise.
  2. Read the CONTRIBUTING.md file make

Copy link

codecov bot commented Oct 25, 2024

Codecov Report

Attention: Patch coverage is 98.52941% with 1 line in your changes missing coverage. Please review.

Project coverage is 98.44%. Comparing base (42024f6) to head (40df8f8).
Report is 23 commits behind head on develop.

Files with missing lines Patch % Lines
src/resolvers/Mutation/createChat.ts 87.50% 1 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff            @@
##           develop    #2611   +/-   ##
========================================
  Coverage    98.44%   98.44%           
========================================
  Files          342      343    +1     
  Lines        16734    16800   +66     
  Branches      2408     2414    +6     
========================================
+ Hits         16473    16538   +65     
- Misses         258      259    +1     
  Partials         3        3           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.


🚨 Try these New Features:

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

🧹 Outside diff range and nitpick comments (10)
src/resolvers/Query/chatsByUserId.ts (1)

25-30: Consider adding index for chat sorting.

Since we're sorting on updatedAt, consider adding an index to optimize query performance, especially as the number of chats grows.

Add this index to your Chat model:

// In your Chat model definition
{
  timestamps: true,
  indexes: [
    { updatedAt: -1 }
  ]
}
src/resolvers/Mutation/sendMessageToChat.ts (1)

Line range hint 1-92: Consider architectural improvements for better scalability.

The current implementation of storing unseen message counts as a JSON string has several scalability concerns:

  1. JSON parsing/stringifying on every message operation is inefficient
  2. Storing counts as a JSON string prevents using MongoDB's native querying and indexing
  3. No apparent limit on message history or unseen count size

Consider these architectural improvements:

  1. Store unseen messages as native MongoDB fields:
interface Chat {
  unseenMessages: {
    [userId: string]: number;
  };
  // ... other fields
}
  1. Use MongoDB schema validation to ensure data integrity:
unseenMessages: {
  bsonType: "object",
  patternProperties: {
    "^[0-9a-fA-F]{24}$": { bsonType: "int" }
  }
}
  1. Add indexes for common queries:
db.chats.createIndex({ "unseenMessages.$**": 1 });

Would you like me to create a separate issue to track these architectural improvements?

src/resolvers/Mutation/createChat.ts (1)

Line range hint 39-47: Enhance error reporting for missing users.

While the user existence check is efficient, consider enhancing the error message to specify which user IDs weren't found. This would improve debugging and user experience.

 const userExists = await User.find({
   _id: { $in: args.data.userIds },
 }).lean();

 if (userExists && userExists.length !== args.data.userIds.length) {
+  const foundUserIds = userExists.map(user => user._id.toString());
+  const missingUserIds = args.data.userIds.filter(id => !foundUserIds.includes(id));
   throw new errors.NotFoundError(
-    requestContext.translate(USER_NOT_FOUND_ERROR.MESSAGE),
+    requestContext.translate(USER_NOT_FOUND_ERROR.MESSAGE) + `: ${missingUserIds.join(', ')}`,
     USER_NOT_FOUND_ERROR.CODE,
     USER_NOT_FOUND_ERROR.PARAM,
   );
 }
src/models/Chat.ts (1)

Line range hint 1-108: Consider implementing a message tracking strategy.

The current implementation tracks unseen messages at the chat level. For better scalability and performance, consider:

  1. Adding an index on unseenMessagesByUsers if you'll be querying by this field
  2. Implementing a cleanup strategy to prevent the JSON object from growing indefinitely
  3. Adding a maximum limit to the number of unseen messages tracked per user
tests/resolvers/Mutation/sendMessageToChat.spec.ts (1)

Line range hint 142-185: Add test cases for unseen messages functionality.

The successful message sending test should verify that the unseenMessagesByUsers count is incremented correctly. Consider adding assertions to verify:

  1. The count increases for the recipient but not the sender
  2. The JSON structure remains valid after updates

Here's a suggested addition to the existing test:

    const sendMessageToChatPayload = await sendMessageToChatResolver?.(
      {},
      args,
      context,
    );

+   // Verify the message payload
    expect(sendMessageToChatPayload).toEqual(
      expect.objectContaining({
        chatMessageBelongsTo: testChat._id,
        sender: testUsers[0]?._id,
        messageContent: "messageContent",
      }),
    );

+   // Verify unseen messages count
+   const updatedChat = await Chat.findById(testChat._id);
+   const unseenMessages = JSON.parse(updatedChat.unseenMessagesByUsers);
+   expect(unseenMessages[testUsers[0]?.id]).toBe(0); // Sender's count unchanged
+   expect(unseenMessages[testUsers[1]?.id]).toBe(1); // Recipient's count increased

Would you like me to help implement these test cases?

tests/resolvers/Mutation/markChatMessagesAsRead.spec.ts (2)

65-66: Replace hardcoded timestamps with dynamic values.

Using hardcoded timestamps makes the tests less maintainable and could cause issues if time-based logic is added later.

-    createdAt: "23456789",
-    updatedAt: "23456789",
+    createdAt: new Date().toISOString(),
+    updatedAt: new Date().toISOString(),

78-169: Add test cases for edge scenarios.

The test suite is missing important edge cases:

  • Marking messages as read for a user not in the chat
  • Handling invalid unseenMessagesByUsers data
  • Concurrent marking of messages

Would you like me to help generate these additional test cases?

src/typeDefs/mutations.ts (1)

253-254: The mutation definition looks good but could be enhanced.

The implementation follows the project's conventions and includes proper authentication. However, consider these enhancements:

  1. Define specific error types for better error handling:
-    markChatMessagesAsRead(chatId: ID!, userId: ID!): Chat @auth
+    markChatMessagesAsRead(chatId: ID!, userId: ID!): MarkChatMessagesAsReadResponse @auth

+ type MarkChatMessagesAsReadResponse {
+   chat: Chat
+   success: Boolean!
+   error: MarkChatMessagesAsReadError
+ }

+ type MarkChatMessagesAsReadError {
+   code: MarkChatMessagesAsReadErrorCode!
+   message: String!
+ }

+ enum MarkChatMessagesAsReadErrorCode {
+   CHAT_NOT_FOUND
+   USER_NOT_IN_CHAT
+   UNAUTHORIZED
+ }
  1. Consider adding rate limiting to prevent abuse:
-    markChatMessagesAsRead(chatId: ID!, userId: ID!): Chat @auth
+    markChatMessagesAsRead(chatId: ID!, userId: ID!): Chat @auth @rateLimit(window: "5m", max: 100)
src/typeDefs/types.ts (1)

745-745: Add documentation for the unseenMessagesByUsers field.

The field purpose and structure are not immediately clear. Please add GraphQL documentation using triple quotes to explain:

  • The expected JSON structure
  • How the field is used to track unread messages
  • Any constraints or assumptions

Example documentation:

+  """
+  Tracks the number of unseen messages per user in the chat.
+  Structure: { userId: number of unseen messages }
+  """
   unseenMessagesByUsers: JSON
schema.graphql (1)

209-209: Add descriptions to the new chat-related schema elements.

Missing descriptions make it harder for other developers to understand the purpose and behavior of these fields.

Add descriptions using triple-quote blocks:

type Chat {
  """
  Tracks which messages are unseen by which users in the chat.
  The structure maps user IDs to their last seen message timestamp.
  """
  unseenMessagesByUsers: JSON
}

type Mutation {
  """
  Marks all messages in a chat as read for a specific user.
  Returns the updated chat or error if the operation fails.
  """
  markChatMessagesAsRead(chatId: ID!, userId: ID!): Chat
}

Also applies to: 1118-1118

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 2c372d9 and 6ef15db.

📒 Files selected for processing (13)
  • schema.graphql (2 hunks)
  • src/models/Chat.ts (2 hunks)
  • src/resolvers/Mutation/createChat.ts (2 hunks)
  • src/resolvers/Mutation/index.ts (2 hunks)
  • src/resolvers/Mutation/markChatMessagesAsRead.ts (1 hunks)
  • src/resolvers/Mutation/sendMessageToChat.ts (2 hunks)
  • src/resolvers/Query/chatsByUserId.ts (2 hunks)
  • src/typeDefs/mutations.ts (1 hunks)
  • src/typeDefs/types.ts (1 hunks)
  • src/types/generatedGraphQLTypes.ts (5 hunks)
  • tests/helpers/chat.ts (1 hunks)
  • tests/resolvers/Mutation/markChatMessagesAsRead.spec.ts (1 hunks)
  • tests/resolvers/Mutation/sendMessageToChat.spec.ts (1 hunks)
🔇 Additional comments (19)
src/resolvers/Query/chatsByUserId.ts (2)

3-3: LGTM!

The type import is correctly added and properly scoped.


25-30: Verify the impact on unread messages feature.

The query changes look good, but let's ensure the sorting doesn't affect the unread messages functionality.

Let's verify the chat model structure and related tests:

✅ Verification successful

No impact on unread messages functionality

The sorting of chats doesn't affect the unread messages feature because:

  1. The unseenMessagesByUsers is stored as a JSON field in the Chat model and is managed independently through separate mutations:
    • sendMessageToChat: Increments unread count for other users
    • markChatMessagesAsRead: Resets unread count for the current user
  2. The sorting only affects the order of returned chats and doesn't modify any chat data
  3. The test coverage confirms the query returns the same data with or without sorting

The changes are safe to proceed with.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify chat model structure and related test coverage

# Check if Chat model includes unseenMessagesByUsers field
echo "Checking Chat model structure..."
rg -A 5 "unseenMessagesByUsers" "src/models/"

# Check if there are tests covering the interaction between sorting and unread messages
echo "Checking test coverage..."
rg -A 5 "sort.*unread|unread.*sort" "tests/"

Length of output: 668


Script:

#!/bin/bash
# Let's check how unseenMessagesByUsers is used in the codebase
echo "Checking usage of unseenMessagesByUsers..."
rg -A 5 "unseenMessagesByUsers" "src/"

# Let's also check if there are any specific unread message related functions
echo "Checking unread message related functions..."
ast-grep --pattern 'function $FUNC($$$) {
  $$$
  unseenMessagesByUsers
  $$$
}'

# Check if there are any tests for chatsByUserId
echo "Checking chatsByUserId tests..."
rg -A 10 "chatsByUserId" "tests/"

Length of output: 8904

src/resolvers/Mutation/markChatMessagesAsRead.ts (1)

1-63: Verify related schema changes for unseenMessagesByUsers.

The implementation assumes the existence of unseenMessagesByUsers in the Chat model. Let's verify the related schema changes.

✅ Verification successful

The schema verification shows that unseenMessagesByUsers is properly defined in both the Mongoose model (src/models/Chat.ts) and GraphQL type definitions (src/typeDefs/types.ts). Let me verify one more aspect - the GraphQL mutation definition to ensure it's properly exposed in the API.


Schema implementation is correctly aligned with the functionality

The verification confirms that:

  • unseenMessagesByUsers is properly defined in the Chat model schema as a required JSON field
  • GraphQL type definitions include the field in the Chat type
  • The mutation markChatMessagesAsRead is properly exposed in the GraphQL API with authentication
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the Chat model schema and GraphQL type definitions include unseenMessagesByUsers

# Check Chat model schema
echo "Checking Chat model schema..."
rg -A 5 "unseenMessagesByUsers" "src/models/"

# Check GraphQL type definitions
echo "Checking GraphQL type definitions..."
rg -A 5 "unseenMessagesByUsers" "src/typeDefs/"

Length of output: 913


Script:

#!/bin/bash
# Check GraphQL mutation definition
echo "Checking GraphQL mutation definition..."
rg "markChatMessagesAsRead" "src/typeDefs/"

Length of output: 221

src/resolvers/Mutation/createChat.ts (2)

Line range hint 53-79: Consider performance implications of JSON storage.

The current implementation stores unseenMessagesByUsers as a JSON string:

  1. Performance Considerations:

    • JSON parsing/stringifying overhead on every read/write
    • Limited query capabilities on the JSON field
    • Potential atomicity issues with concurrent updates
  2. Alternative Approaches:

    • Consider using a separate collection for unread message counts
    • Use MongoDB's native object storage instead of JSON strings
    • Implement optimistic locking for concurrent updates
#!/bin/bash
# Check if sendMessageToChat updates unseenMessagesByUsers
rg -A 5 "sendMessageToChat.*unseenMessagesByUsers"

# Check for potential race condition handling
rg -A 5 "findOneAndUpdate.*unseenMessagesByUsers"

71-71: Verify unseenMessagesByUsers is defined before creating chat.

Add a validation check to ensure the unseenMessagesByUsers field is properly initialized before creating the chat.

Also applies to: 79-79

src/models/Chat.ts (2)

108-108: ⚠️ Potential issue

Resolve timestamp handling inconsistency.

The schema has timestamps: false but includes required createdAt and updatedAt fields. This creates a potential risk as timestamps won't be automatically managed by Mongoose anymore.

Consider either:

  1. Keeping timestamps: true to let Mongoose handle these fields automatically, or
  2. Removing the timestamp fields if they're not needed

Let's verify timestamp usage in mutations:

#!/bin/bash
# Search for places where Chat documents are created or updated
ast-grep --pattern 'Chat.create|Chat.update|Chat.findOneAndUpdate'

102-105: Verify migration strategy for existing chat documents.

Adding a required field without a default value could cause issues with existing chat documents in the database.

Let's check if there are existing chat documents that need migration:

tests/helpers/chat.ts (1)

Line range hint 127-137: Verify the impact of missing unseenMessagesByUsers in chat creation helpers.

The createTestChatwithUsers and createTestMessageForMultipleUser functions don't initialize the unseenMessagesByUsers field. This might affect tests related to unread messages functionality.

Let's check if any tests rely on these helpers:

After verifying the usage, update these helpers to include unseenMessagesByUsers initialization for consistency with the new chat schema.

Also applies to: 139-149

tests/resolvers/Mutation/sendMessageToChat.spec.ts (1)

68-71: LGTM: Proper initialization of unseenMessagesByUsers.

The initialization of unseenMessagesByUsers with zero counts for both users is correct and matches the expected initial state.

tests/resolvers/Mutation/markChatMessagesAsRead.spec.ts (3)

1-28: LGTM! Imports and type declarations are well-structured.

The imports cover all necessary dependencies and the type declarations are properly defined.


74-82: LGTM! Test cleanup is properly implemented.

The afterAll and afterEach hooks ensure proper cleanup of database connections and mocks.


67-70: Consider storing unseenMessagesByUsers as a Map or plain object.

Storing the unseenMessagesByUsers as stringified JSON could lead to parsing/stringifying overhead and potential errors. Consider using a native MongoDB data structure.

Let's check how unseenMessagesByUsers is used across the codebase:

src/resolvers/Mutation/index.ts (2)

88-88: LGTM! Clean import statement.

The import follows the established pattern and is correctly placed alphabetically.


241-241: LGTM! Mutation resolver properly registered.

The mutation resolver is correctly added to the Mutation object and follows the alphabetical ordering convention.

Let's verify the mutation implementation and its integration:

✅ Verification successful

Mutation implementation and integration verified successfully

The verification confirms:

  • Mutation implementation exists with proper error handling for chat and user existence
  • Schema defines the mutation with correct parameters (chatId, userId)
  • Comprehensive test coverage including error cases and successful execution
  • Implementation correctly updates the unseenMessagesByUsers count for the current user
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the mutation implementation and schema definition

# Test 1: Check if the mutation implementation file exists
echo "Checking mutation implementation..."
fd "markChatMessagesAsRead.ts" src/resolvers/Mutation

# Test 2: Verify the mutation is properly defined in the schema
echo "Checking GraphQL schema definition..."
rg "markChatMessagesAsRead.*:.*Chat.*@auth" -g "*.graphql"

# Test 3: Check for test coverage
echo "Checking test coverage..."
fd "markChatMessagesAsRead.spec.ts" tests/

Length of output: 489


Script:

#!/bin/bash
# Let's verify the implementation details and schema definition

# Check the mutation implementation
echo "=== Mutation Implementation ==="
cat src/resolvers/Mutation/markChatMessagesAsRead.ts

# Check the schema definition
echo -e "\n=== Schema Definition ==="
rg "markChatMessagesAsRead" -A 3 -g "*.graphql"

# Check the test implementation
echo -e "\n=== Test Implementation ==="
cat tests/resolvers/Mutation/markChatMessagesAsRead.spec.ts

Length of output: 7392

src/typeDefs/mutations.ts (1)

253-254: Verify Chat type integration.

Let's ensure the Chat type properly defines the unseenMessagesByUsers field mentioned in the AI summary.

✅ Verification successful

Chat type integration is properly defined

The unseenMessagesByUsers field is correctly defined in the Chat type as a JSON scalar type, which aligns with its usage in the mutation. The field is consistently used across the codebase in relevant files including models, resolvers, and tests.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify Chat type definition includes unseenMessagesByUsers field
# Expected: Find the Chat type definition with the unseenMessagesByUsers field

echo "Searching for Chat type definition..."
rg -l 'type\s+Chat\s*\{' --type ts

echo "Checking unseenMessagesByUsers field in Chat type..."
rg 'unseenMessagesByUsers.*:.*JSON' --type ts

Length of output: 1234


Script:

#!/bin/bash
# Let's check the complete Chat type definition
echo "Checking complete Chat type definition..."
rg -A 20 'type\s+Chat\s*\{' src/typeDefs/types.ts

Length of output: 573

src/typeDefs/types.ts (1)

745-745: Verify the implementation details across the codebase.

Let's ensure the field is properly integrated with the chat system.

✅ Verification successful

Let me gather more specific information about the implementation details.


Field is properly integrated with the chat system

The unseenMessagesByUsers field is correctly implemented across the codebase with proper:

  • Initialization in createChat.ts with zero unread messages for all users
  • Updates in sendMessageToChat.ts to increment counters for recipients
  • Reset functionality in markChatMessagesAsRead.ts when messages are read
  • Schema definitions in models and types
  • Test coverage for both sending messages and marking as read
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the implementation of unseenMessagesByUsers across the codebase

# Test 1: Check how the field is initialized in chat creation
echo "Checking chat creation..."
rg -l "unseenMessagesByUsers.*createChat"

# Test 2: Check how the field is updated when messages are sent
echo "Checking message handling..."
rg -l "unseenMessagesByUsers.*sendMessage"

# Test 3: Check how the field is updated when messages are read
echo "Checking mark as read functionality..."
rg -l "unseenMessagesByUsers.*markChatMessagesAsRead"

# Test 4: Look for any test coverage
echo "Checking test coverage..."
fd -e spec.ts -e test.ts | xargs rg "unseenMessagesByUsers"

Length of output: 779


Script:

#!/bin/bash
# Let's examine the actual implementation in the test files and related resolvers

# Check the test implementations
echo "=== Test Implementation Details ==="
rg -A 10 "unseenMessagesByUsers" tests/resolvers/Mutation/sendMessageToChat.spec.ts
echo "\n=== Mark as Read Test Details ==="
rg -A 10 "unseenMessagesByUsers" tests/resolvers/Mutation/markChatMessagesAsRead.spec.ts

# Look for resolver implementations
echo "\n=== Resolver Implementations ==="
fd -e ts | grep -v "test\|spec" | xargs rg -A 5 "unseenMessagesByUsers"

# Check for any GraphQL schema definitions
echo "\n=== Schema Definitions ==="
fd -e graphql | xargs rg "unseenMessagesByUsers"

Length of output: 6561

src/types/generatedGraphQLTypes.ts (3)

282-282: LGTM! Added unseenMessagesByUsers field to Chat type

The field type Maybe<JSON> is appropriate for storing a dynamic mapping of user IDs to unseen message counts.


1571-1574: LGTM! Added markChatMessagesAsRead mutation arguments

The required chatId and userId arguments of type ID are appropriate for identifying the chat and user.


4290-4290: LGTM! Added markChatMessagesAsRead resolver

The resolver return type Maybe<Chat> is appropriate as it may return null if the chat is not found.

Comment on lines +17 to +24
const sort = {
updatedAt: -1,
} as
| string
| { [key: string]: SortOrder | { $meta: unknown } }
| [string, SortOrder][]
| null
| undefined;
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider simplifying the sort configuration.

While the current implementation works, we can make it more maintainable and readable.

Consider this simplified version:

-  const sort = {
-    updatedAt: -1,
-  } as
-    | string
-    | { [key: string]: SortOrder | { $meta: unknown } }
-    | [string, SortOrder][]
-    | null
-    | undefined;
+  const sort: { [key: string]: SortOrder } = {
+    updatedAt: SortOrder.DESC
+  };

This change:

  1. Uses the SortOrder enum for better readability
  2. Simplifies the type definition to match the actual usage
  3. Maintains the same functionality while being more explicit
📝 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 sort = {
updatedAt: -1,
} as
| string
| { [key: string]: SortOrder | { $meta: unknown } }
| [string, SortOrder][]
| null
| undefined;
const sort: { [key: string]: SortOrder } = {
updatedAt: SortOrder.DESC
};

src/resolvers/Mutation/markChatMessagesAsRead.ts Outdated Show resolved Hide resolved
src/resolvers/Mutation/markChatMessagesAsRead.ts Outdated Show resolved Hide resolved
Comment on lines +29 to +39
const currentUserExists = await User.exists({
_id: context.userId,
}).lean();

if (!currentUserExists) {
throw new errors.NotFoundError(
requestContext.translate(USER_NOT_FOUND_ERROR.MESSAGE),
USER_NOT_FOUND_ERROR.CODE,
USER_NOT_FOUND_ERROR.PARAM,
);
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Verify user's authorization to access the chat.

The code only checks if the user exists but doesn't verify if they are a member of the chat. This could lead to unauthorized access.

Add authorization check:

     if (!currentUserExists) {
       throw new errors.NotFoundError(
         requestContext.translate(USER_NOT_FOUND_ERROR.MESSAGE),
         USER_NOT_FOUND_ERROR.CODE,
         USER_NOT_FOUND_ERROR.PARAM,
       );
     }
+
+    // Verify user is a member of the chat
+    if (!chat.users?.includes(context.userId)) {
+      throw new errors.UnauthorizedError(
+        requestContext.translate("User is not a member of this chat"),
+        "UNAUTHORIZED_CHAT_ACCESS"
+      );
+    }

Committable suggestion was skipped due to low confidence.

Comment on lines +41 to +44
const unseenMessagesByUsers = JSON.parse(
chat.unseenMessagesByUsers as unknown as string,
);

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add error handling for JSON parsing.

The JSON.parse operation could throw if the data is malformed. Also, the type casting looks suspicious.

Add error handling and proper typing:

-    const unseenMessagesByUsers = JSON.parse(
-      chat.unseenMessagesByUsers as unknown as string,
-    );
+    let unseenMessagesByUsers: Record<string, number>;
+    try {
+      unseenMessagesByUsers = JSON.parse(
+        typeof chat.unseenMessagesByUsers === 'string' 
+          ? chat.unseenMessagesByUsers 
+          : JSON.stringify(chat.unseenMessagesByUsers)
+      );
+    } catch (error) {
+      throw new errors.ValidationError(
+        requestContext.translate("Invalid chat data format"),
+        "INVALID_CHAT_DATA"
+      );
+    }
📝 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 unseenMessagesByUsers = JSON.parse(
chat.unseenMessagesByUsers as unknown as string,
);
let unseenMessagesByUsers: Record<string, number>;
try {
unseenMessagesByUsers = JSON.parse(
typeof chat.unseenMessagesByUsers === 'string'
? chat.unseenMessagesByUsers
: JSON.stringify(chat.unseenMessagesByUsers)
);
} catch (error) {
throw new errors.ValidationError(
requestContext.translate("Invalid chat data format"),
"INVALID_CHAT_DATA"
);
}

}
});

it(`creates the directChatMessage and returns it`, async () => {
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 misleading test description.

The test description "creates the directChatMessage" is incorrect as this test verifies marking messages as read.

-  it(`creates the directChatMessage and returns it`, async () => {
+  it(`marks messages as read and returns updated chat`, async () => {
📝 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
it(`creates the directChatMessage and returns it`, async () => {
it(`marks messages as read and returns updated chat`, async () => {

Comment on lines +131 to +141
it(`creates the directChatMessage and returns it`, async () => {
await Chat.updateOne(
{
_id: testChat._id,
},
{
$push: {
users: testUsers[0]?._id,
},
},
);
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 unnecessary chat update in the success test case.

The test unnecessarily adds a user that should already be present in the chat from the setup. This makes the test harder to understand and could mask potential issues.

-    await Chat.updateOne(
-      {
-        _id: testChat._id,
-      },
-      {
-        $push: {
-          users: testUsers[0]?._id,
-        },
-      },
-    );
📝 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
it(`creates the directChatMessage and returns it`, async () => {
await Chat.updateOne(
{
_id: testChat._id,
},
{
$push: {
users: testUsers[0]?._id,
},
},
);
it(`creates the directChatMessage and returns it`, async () => {

@@ -742,6 +742,7 @@ export const types = gql`
admins: [User]
lastMessageId: String
image: String
unseenMessagesByUsers: JSON
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider using a strict type instead of JSON.

Using the JSON scalar type loses GraphQL's type safety benefits. Consider creating a dedicated type to represent unseen messages:

+ type UnseenMessages {
+   userId: ID!
+   count: Int!
+ }

  type Chat {
    # ... other fields ...
-   unseenMessagesByUsers: JSON
+   unseenMessagesByUsers: [UnseenMessages!]
  }

This provides several benefits:

  • Type safety and validation at the GraphQL schema level
  • Better documentation through the type system
  • Improved developer experience with IDE support

Committable suggestion was skipped due to low confidence.

@@ -206,6 +206,7 @@ type Chat {
messages: [ChatMessage]
name: String
organization: Organization
unseenMessagesByUsers: JSON
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider using a more type-safe structure for unseenMessagesByUsers.

The JSON type provides flexibility but lacks type safety. Consider creating a dedicated type to represent unseen messages data.

-  unseenMessagesByUsers: JSON
+  unseenMessagesByUsers: UnseenMessages

Add these type definitions:

type UnseenMessages {
  """Map of user IDs to their last seen message timestamp"""
  userToLastSeen: [UserLastSeen!]
}

type UserLastSeen {
  userId: ID!
  lastSeenAt: DateTime!
}

@@ -1114,6 +1115,7 @@
likePost(id: ID!): Post
login(data: LoginInput!): AuthData!
logout: Boolean!
markChatMessagesAsRead(chatId: ID!, userId: ID!): Chat
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add error handling to markChatMessagesAsRead mutation.

The mutation should handle cases like invalid chat/user IDs and unauthorized access.

-  markChatMessagesAsRead(chatId: ID!, userId: ID!): Chat
+  markChatMessagesAsRead(chatId: ID!, userId: ID!): MarkChatMessagesAsReadPayload!

Add these type definitions:

type MarkChatMessagesAsReadPayload {
  chat: Chat
  userErrors: [MarkChatMessagesAsReadError!]!
}

union MarkChatMessagesAsReadError = ChatNotFoundError | UserNotFoundError | UserNotAuthorizedError

type ChatNotFoundError implements Error {
  message: String!
}

disha1202 and others added 2 commits October 29, 2024 10:45
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
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

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 6ef15db and e638d96.

📒 Files selected for processing (1)
  • src/resolvers/Mutation/markChatMessagesAsRead.ts (1 hunks)

Comment on lines +46 to +48
if (unseenMessagesByUsers[context.userId] !== undefined) {
unseenMessagesByUsers[context.userId] = 0;
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add type validation for unseenMessagesByUsers structure.

The code assumes unseenMessagesByUsers is a valid object with number values, but there's no validation of its structure.

Add type validation:

+    // Validate unseenMessagesByUsers structure
+    if (typeof unseenMessagesByUsers !== 'object' || unseenMessagesByUsers === null) {
+      throw new errors.ValidationError(
+        requestContext.translate('Invalid unseenMessagesByUsers format'),
+        'INVALID_MESSAGE_COUNT_FORMAT'
+      );
+    }
+
+    // Validate the specific user's message count
+    if (unseenMessagesByUsers[context.userId] !== undefined && 
+        typeof unseenMessagesByUsers[context.userId] !== 'number') {
+      throw new errors.ValidationError(
+        requestContext.translate('Invalid message count format'),
+        'INVALID_MESSAGE_COUNT_TYPE'
+      );
+    }
+
     if (unseenMessagesByUsers[context.userId] !== undefined) {
📝 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 (unseenMessagesByUsers[context.userId] !== undefined) {
unseenMessagesByUsers[context.userId] = 0;
}
// Validate unseenMessagesByUsers structure
if (typeof unseenMessagesByUsers !== 'object' || unseenMessagesByUsers === null) {
throw new errors.ValidationError(
requestContext.translate('Invalid unseenMessagesByUsers format'),
'INVALID_MESSAGE_COUNT_FORMAT'
);
}
// Validate the specific user's message count
if (unseenMessagesByUsers[context.userId] !== undefined &&
typeof unseenMessagesByUsers[context.userId] !== 'number') {
throw new errors.ValidationError(
requestContext.translate('Invalid message count format'),
'INVALID_MESSAGE_COUNT_TYPE'
);
}
if (unseenMessagesByUsers[context.userId] !== undefined) {
unseenMessagesByUsers[context.userId] = 0;
}

Comment on lines +50 to +59
const updatedChat = await Chat.findByIdAndUpdate(
{
_id: chat._id,
},
{
$set: {
unseenMessagesByUsers: JSON.stringify(unseenMessagesByUsers),
},
},
);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add optimistic concurrency control to prevent race conditions.

Multiple concurrent updates to unseenMessagesByUsers could lead to lost updates.

Add version control to prevent race conditions:

     const updatedChat = await Chat.findByIdAndUpdate(
-      {
-        _id: chat._id,
-      },
+      chat._id,
       {
         $set: {
           unseenMessagesByUsers: JSON.stringify(unseenMessagesByUsers),
         },
       },
+      {
+        new: true,
+        // Add version control
+        __v: chat.__v,
+      }
     );

+    if (!updatedChat) {
+      throw new errors.ConflictError(
+        requestContext.translate('Chat was updated by another operation'),
+        'CONCURRENT_MODIFICATION'
+      );
+    }

Committable suggestion was skipped due to low confidence.

Copy link

github-actions bot commented Nov 9, 2024

This pull request did not get any activity in the past 10 days and will be closed in 180 days if no update occurs. Please verify it has no conflicts with the develop branch and rebase if needed. Mention it now if you need help or give permission to other people to finish your work.

@github-actions github-actions bot added the no-pr-activity No pull request activity label Nov 9, 2024
@palisadoes
Copy link
Contributor

Please fix the conflicting files and make code rabbit approve your changes

@palisadoes
Copy link
Contributor

During the week of November 11, 2024 we will start a code freeze on the develop branches in Talawa, Talawa Admin and Talawa-API.

We have completed a project to convert the Talawa-API backend to use PostgreSQL. Work will then begin with us merging code in the develop branches to a new develop-postrgres branch in each repository.

Planning activities for this will be managed in our #talawa-projects slack channel. A GitHub project will be created to track specially labeled issues. We completed a similar exercise last year using a similar methodology.

Starting November 12, California time no new PRs will be accepted against the develop branch. They must be applied to the develop-postrgres branch.

There are some GSoC project features that will need to be merged into develop. These will be the only exceptions.

This activity and the post GSoC 2024 start date was announced in our #general Slack channel last month as a pinned post.

@github-actions github-actions bot removed the no-pr-activity No pull request activity label Nov 11, 2024
Copy link

This pull request did not get any activity in the past 10 days and will be closed in 180 days if no update occurs. Please verify it has no conflicts with the develop branch and rebase if needed. Mention it now if you need help or give permission to other people to finish your work.

@github-actions github-actions bot added no-pr-activity No pull request activity and removed no-pr-activity No pull request activity labels Nov 21, 2024
@palisadoes
Copy link
Contributor

@disha1202 Are you still working on this?

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.

2 participants