Skip to content

Conversation

@iamanishx
Copy link

@iamanishx iamanishx commented Oct 25, 2025

What kind of change does this PR introduce?

Issue Number:

Fixes #

Snapshots/Videos:

If relevant, did you update the documentation?

Summary

	[key: `chats.${PrimaryKey}:chat_messages::create`]: ChatMessage;
	[key: `chats.${PrimaryKey}:chat_messages::create::system`]: ChatMessage;
	[key: `chats.${PrimaryKey}:chat_messages::update`]: ChatMessage;
	[key: `chats.${PrimaryKey}:chat_messages::update::system`]: ChatMessage;
	[key: `chats.${PrimaryKey}:chat_messages::delete`]: ChatMessage;
	[key: `chats.${PrimaryKey}:chat_messages::delete::system`]: ChatMessage;

implemented separate subscription topics for each events and implemented hashing and isGrroup filed to control direct and group chat workflow

Does this PR introduce a breaking change?

Checklist

CodeRabbit AI Review

  • I have reviewed and addressed all critical issues flagged by CodeRabbit AI
  • I have implemented or provided justification for each non-critical suggestion
  • I have documented my reasoning in the PR comments where CodeRabbit AI suggestions were not implemented

Test Coverage

  • I have written tests for all new changes/features
  • I have verified that test coverage meets or exceeds 95%
  • I have run the test suite locally and all tests pass

Other information

Have you read the contributing guide?

Summary by CodeRabbit

  • New Features

    • Introduced chat types: direct (1:1) and group; chat creation now requires a type and supports participants.
    • Direct chats enforce exactly 2 participants, prevent duplicates, and reuse existing conversations when appropriate.
    • Added real-time subscriptions for chat message updates and deletions.
  • Bug Fixes / Behavior

    • Membership edits are now restricted for direct (1:1) chats to preserve conversation integrity.

Signed-off-by: imanishx <[email protected]>
Signed-off-by: imanishx <[email protected]>
Signed-off-by: imanishx <[email protected]>
Signed-off-by: imanishx <[email protected]>
Signed-off-by: imanishx <[email protected]>
Signed-off-by: imanishx <[email protected]>
Signed-off-by: imanishx <[email protected]>
Signed-off-by: imanishx <[email protected]>
Signed-off-by: imanishx <[email protected]>
Signed-off-by: imanishx <[email protected]>
Signed-off-by: imanishx <[email protected]>
Signed-off-by: imanishx <[email protected]>
Signed-off-by: imanishx <[email protected]>
Signed-off-by: imanishx <[email protected]>
Signed-off-by: imanishx <[email protected]>
Signed-off-by: imanishx <[email protected]>
Signed-off-by: imanishx <[email protected]>
Signed-off-by: imanishx <[email protected]>
Signed-off-by: imanishx <[email protected]>
Signed-off-by: imanishx <[email protected]>
Signed-off-by: imanishx <[email protected]>
Signed-off-by: imanishx <[email protected]>
Signed-off-by: imanishx <[email protected]>
Signed-off-by: imanishx <[email protected]>
Signed-off-by: imanishx <[email protected]>
@keploy
Copy link

keploy bot commented Oct 25, 2025

To generate Unit Tests for this PR, please click here.

@coderabbitai
Copy link

coderabbitai bot commented Oct 25, 2025

Walkthrough

This PR adds chat typing (direct vs group) to the system: DB schema adds type and direct_participants_hash; GraphQL schema gains ChatType, new inputs, and subscriptions; createChat and membership mutations are refactored to handle direct/group flows; pubsub topics updated; tests adjusted and expanded.

Changes

Cohort / File(s) Summary
Database Schema
drizzle_migrations/20251015160719_perpetual_wasp.sql, drizzle_migrations/meta/_journal.json
Added type (text, NOT NULL, default 'group') and direct_participants_hash (text) to chats via a new migration and updated migration journal.
GraphQL Schema Definition
schema.graphql
Added ChatType enum (direct, group), Chat.type field, extended MutationCreateChatInput with participants and type, and added SubscriptionChatMessageDeleteInput / SubscriptionChatMessageUpdateInput plus new subscription fields.
Runtime Enum & Drizzle Table
src/drizzle/enums/chatType.ts, src/drizzle/tables/chats.ts
New Zod enum chatTypeEnum (`"direct"
GraphQL Enum Wiring
src/graphql/enums/ChatType.ts, src/graphql/enums/index.ts
Created and exported GraphQL ChatType enum wired to runtime enum; added import to enums index.
GraphQL Inputs
src/graphql/inputs/MutationCreateChatInput.ts, src/graphql/inputs/SubscriptionChatMessageDeleteInput.ts, src/graphql/inputs/SubscriptionChatMessageUpdateInput.ts
MutationCreateChatInput now accepts participants and required type. Added two subscription input types with UUID id validation.
PubSub Types
src/graphql/pubsub.ts
Extended PubSubPublishArgsByKey type to include `chats.{PK}:chat_messages::create
Chat GraphQL Type
src/graphql/types/Chat/Chat.ts
Added exported type field to Chat type (describes 'group' or 'direct').
createChat Mutation
src/graphql/types/Mutation/createChat.ts
Refactored to branch on chat type: direct chats validate exactly 2 distinct participants, compute canonical hash, ensure idempotent creation and memberships; group chats follow creation + admin membership flow; avatar handling retained; transactions and error handling added.
Membership Mutations
src/graphql/types/Mutation/createChatMembership.ts, src/graphql/types/Mutation/updateChatMembership.ts
createChatMembership rewritten to centralized args/authorization and simplified flow; updateChatMembership added guard forbidding modifications for direct chats.
Chat Message Mutations
src/graphql/types/Mutation/deleteChatMessage.ts, src/graphql/types/Mutation/updateChatMessage.ts
PubSub topic strings updated: deletes publish to ::delete, updates to ::update.
Subscriptions
src/graphql/types/Subscription/chatMessageCreate.ts, src/graphql/types/Subscription/chatMessageDelete.ts, src/graphql/types/Subscription/chatMessageUpdate.ts, src/graphql/types/Subscription/index.ts
Removed comment header in create; added chatMessageDelete and chatMessageUpdate subscriptions with input validation, auth/membership checks, and pubsub channel subscriptions. Updated subscription index imports.
Type Introspection
test/graphql/types/gql.tada.d.ts
Updated introspection types: Chat.type field, ChatType enum, added participants and type to MutationCreateChatInput, and new subscription input types.
Tests — general adjustments
test/graphql/types/Chat/*.test.ts, test/graphql/types/Query/*.test.ts, test/graphql/types/Mutation/*.test.ts
Numerous tests updated to use optional chaining (?.) for nested user fields; many test cases added/expanded for createChat (direct/group validation, idempotency, roles); createChatMembership tests converted toward integration flows; UnreadChat.test.ts removed.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant GraphQL as GraphQL API
    participant Validator as Input Validation
    participant DB as Database
    participant PubSub

    Client->>GraphQL: mutation createChat(type, participants?, avatar?)
    GraphQL->>Validator: validate input (type, participants)
    alt type == "direct"
        Validator->>GraphQL: ensure exactly 2 distinct participant IDs
        GraphQL->>DB: compute sorted hash & query by (org, hash, type)
        alt existing direct chat found
            DB-->>GraphQL: return existing chat
            GraphQL->>DB: ensure memberships exist (create if missing)
        else no existing chat
            GraphQL->>DB: create chat (type='direct', hash), create 2 memberships (transaction)
        end
    else type == "group"
        GraphQL->>DB: create chat (type='group')
        alt avatar provided
            GraphQL->>DB: upload avatar and attach
        end
        GraphQL->>DB: create admin membership for creator
        alt participants provided
            GraphQL->>DB: create regular memberships for participants
        end
    end
    GraphQL->>PubSub: publish chat event (created)
    DB-->>GraphQL: commit transaction
    GraphQL-->>Client: return Chat
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Areas needing extra attention:

  • createChat mutation branching, transaction boundaries, and race conditions around direct chat hashing and uniqueness.
  • Correctness of direct participants hash generation and uniqueness constraints.
  • createChatMembership refactor: authorization logic and parity with previous behavior.
  • Subscription auth/membership checks (chatMessageDelete, chatMessageUpdate) and pubsub topic consistency.
  • Test changes: ensure removed assertions and optional chaining do not mask regressions or permit invalid inputs.

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Description Check ⚠️ Warning The PR description is largely incomplete and fails to meet the repository's template requirements. While a brief summary exists (a code snippet and one sentence about implementing separate subscription topics and hashing), the description is missing nearly all critical sections: "What kind of change," "Issue Number," "Snapshots/Videos," documentation updates, breaking change analysis, and all checklist items remain unchecked or unfilled. Most of the template consists of placeholder text that was not replaced with actual information, leaving reviewers without essential context about motivation, scope, testing, and potential impact. Complete the PR description by filling in all required template sections: specify the change type (e.g., feature, refactoring), reference the related issue number, provide a detailed summary explaining the motivation for separating subscription topics and implementing direct/group chat distinction, clarify any breaking changes and migration impact, link to documentation updates if applicable, and check the test coverage items confirming that tests for the new direct chat validation, type handling, and subscription functionality have been written and pass with adequate coverage.
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check name Status Explanation
Title Check ✅ Passed The title "[GSoC-25]; Implemented required changes for subscription at scale" is partially related to the changeset. It accurately describes the subscription enhancements (separate topics for chatMessageDelete and chatMessageUpdate, PubSub infrastructure changes), which are present and significant in the PR. However, the title does not capture the equally important changes for implementing direct versus group chat distinction through type fields and participant hashing, which represents a substantial portion of the implementation (including database schema changes, mutation refactoring, and authorization logic). The title refers to a real aspect of the change but does not fully represent the main scope of the implementation.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link

Our Pull Request Approval Process

Thanks for contributing!

Testing Your Code

Remember, your PRs won't be reviewed until these criteria are met:

  1. We don't merge PRs with poor code quality.
    1. Follow coding best practices such that CodeRabbit.ai approves your PR.
  2. We don't merge PRs with failed tests.
    1. When tests fail, click on the Details link to learn more.
    2. Write sufficient tests for your changes (CodeCov Patch Test). Your testing level must be better than the target threshold of the repository
    3. Tests may fail if you edit sensitive files. Ask to add the ignore-sensitive-files-pr label if the edits are necessary.
  3. We cannot merge PRs with conflicting files. These must be fixed.

Our policies make our code better.

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

@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: 29

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
test/graphql/types/Chat/members.test.ts (1)

181-182: Major: Test assertions checking for potentially undefined values are less meaningful.

The assertions expect(ids).toContain(memberA.user?.id) will pass if memberA.user?.id is undefined and the array doesn't contain undefined, or if both contain undefined. This reduces the effectiveness of the test validation.

After adding null checks for user creation, use direct access in assertions:

-expect(ids).toContain(memberA.user?.id);
-expect(ids).toContain(memberB.user?.id);
+expect(ids).toContain(memberA.user.id);
+expect(ids).toContain(memberB.user.id);
-for (const id of [memberA.user?.id, memberB.user?.id])
+for (const id of [memberA.user.id, memberB.user.id])
   expect(combined).toContain(id);

Also applies to: 209-210

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2dc8264 and fd70199.

📒 Files selected for processing (31)
  • drizzle_migrations/20251015160719_perpetual_wasp.sql (1 hunks)
  • drizzle_migrations/meta/_journal.json (1 hunks)
  • schema.graphql (6 hunks)
  • src/drizzle/enums/chatType.ts (1 hunks)
  • src/drizzle/tables/chats.ts (1 hunks)
  • src/graphql/enums/ChatType.ts (1 hunks)
  • src/graphql/enums/index.ts (1 hunks)
  • src/graphql/inputs/MutationCreateChatInput.ts (3 hunks)
  • src/graphql/inputs/SubscriptionChatMessageDeleteInput.ts (1 hunks)
  • src/graphql/inputs/SubscriptionChatMessageUpdateInput.ts (1 hunks)
  • src/graphql/pubsub.ts (1 hunks)
  • src/graphql/types/Chat/Chat.ts (1 hunks)
  • src/graphql/types/Mutation/createChat.ts (2 hunks)
  • src/graphql/types/Mutation/createChatMembership.ts (1 hunks)
  • src/graphql/types/Mutation/deleteChatMessage.ts (1 hunks)
  • src/graphql/types/Mutation/updateChatMembership.ts (1 hunks)
  • src/graphql/types/Mutation/updateChatMessage.ts (1 hunks)
  • src/graphql/types/Subscription/chatMessageCreate.ts (0 hunks)
  • src/graphql/types/Subscription/chatMessageDelete.ts (1 hunks)
  • src/graphql/types/Subscription/chatMessageUpdate.ts (1 hunks)
  • src/graphql/types/Subscription/index.ts (1 hunks)
  • test/graphql/types/Chat/Chat.test.ts (6 hunks)
  • test/graphql/types/Chat/creator.test.ts (0 hunks)
  • test/graphql/types/Chat/members.test.ts (14 hunks)
  • test/graphql/types/Mutation/MarkChatAsRead.test.ts (14 hunks)
  • test/graphql/types/Mutation/createChat.test.ts (11 hunks)
  • test/graphql/types/Mutation/createChatMembership.test.ts (2 hunks)
  • test/graphql/types/Mutation/updateChatMembership.test.ts (16 hunks)
  • test/graphql/types/Query/UnreadChat.test.ts (0 hunks)
  • test/graphql/types/Query/UnreadChats.test.ts (12 hunks)
  • test/graphql/types/gql.tada.d.ts (2 hunks)
💤 Files with no reviewable changes (3)
  • src/graphql/types/Subscription/chatMessageCreate.ts
  • test/graphql/types/Chat/creator.test.ts
  • test/graphql/types/Query/UnreadChat.test.ts
🧰 Additional context used
🧠 Learnings (7)
📚 Learning: 2025-02-10T18:57:27.311Z
Learnt from: shivasankaran18
PR: PalisadoesFoundation/talawa-api#3175
File: test/graphql/types/Mutation/updateAgendaItem.test.ts:33-49
Timestamp: 2025-02-10T18:57:27.311Z
Learning: In test/graphql/types/Mutation/updateAgendaItem.test.ts, while a global mock setup is used for the drizzle client, an alternative approach is to use specialized mocks initialized in beforeEach for each test scenario to reduce global state and ensure test isolation. However, this requires careful type handling to maintain type safety.

Applied to files:

  • test/graphql/types/Query/UnreadChats.test.ts
  • test/graphql/types/Mutation/updateChatMembership.test.ts
  • test/graphql/types/Mutation/createChat.test.ts
  • test/graphql/types/Mutation/createChatMembership.test.ts
📚 Learning: 2025-03-14T03:48:58.166Z
Learnt from: Piyushk8
PR: PalisadoesFoundation/talawa-api#3319
File: test/graphql/types/Advertisement/updatedAt.test.ts:172-172
Timestamp: 2025-03-14T03:48:58.166Z
Learning: In test/graphql/types/Advertisement/updatedAt.test.ts, variables initialized in beforeEach blocks are recreated before each test, making it safe to modify their values directly within test cases without causing side effects in subsequent tests.

Applied to files:

  • test/graphql/types/Query/UnreadChats.test.ts
📚 Learning: 2025-02-18T15:03:52.111Z
Learnt from: yugal07
PR: PalisadoesFoundation/talawa-api#3218
File: test/routes/graphql/Query/event.test.ts:107-140
Timestamp: 2025-02-18T15:03:52.111Z
Learning: In test/routes/graphql/Query/event.test.ts, the createAndDeleteTestUser function intentionally combines user creation and deletion in a single function to ensure proper cleanup after test execution, as every user creation needs to be cleaned up.

Applied to files:

  • test/graphql/types/Query/UnreadChats.test.ts
  • test/graphql/types/Chat/Chat.test.ts
  • test/graphql/types/Chat/members.test.ts
  • test/graphql/types/Mutation/updateChatMembership.test.ts
  • test/graphql/types/Mutation/createChat.test.ts
  • test/graphql/types/Mutation/MarkChatAsRead.test.ts
📚 Learning: 2024-09-22T09:37:15.069Z
Learnt from: disha1202
PR: PalisadoesFoundation/talawa-api#2466
File: src/types/generatedGraphQLTypes.ts:3096-3101
Timestamp: 2024-09-22T09:37:15.069Z
Learning: Direct chats do not need to be associated with any organization, so the `organizationId` field in `ChatInput` remains optional.

Applied to files:

  • test/graphql/types/Mutation/createChat.test.ts
  • schema.graphql
📚 Learning: 2024-10-12T09:54:09.642Z
Learnt from: syedali237
PR: PalisadoesFoundation/talawa-api#2582
File: src/resolvers/Mutation/sendMembershipRequest.ts:156-170
Timestamp: 2024-10-12T09:54:09.642Z
Learning: In the `sendMembershipRequest` mutation within `src/resolvers/Mutation/sendMembershipRequest.ts`, the team prefers not to introduce transactions for simple operations when existing error handling is sufficient, to avoid unnecessary overhead.

Applied to files:

  • src/graphql/types/Mutation/createChatMembership.ts
  • src/graphql/types/Mutation/updateChatMembership.ts
  • test/graphql/types/Mutation/createChatMembership.test.ts
📚 Learning: 2025-01-29T17:47:13.159Z
Learnt from: PurnenduMIshra129th
PR: PalisadoesFoundation/talawa-api#3094
File: src/types/generatedGraphQLTypes.ts:5118-5122
Timestamp: 2025-01-29T17:47:13.159Z
Learning: The file src/types/generatedGraphQLTypes.ts is auto-generated and should not be modified directly. Any changes needed should be made to the GraphQL schema instead.

Applied to files:

  • src/graphql/enums/index.ts
📚 Learning: 2025-09-29T02:49:37.032Z
Learnt from: BharathC0
PR: PalisadoesFoundation/talawa-api#0
File: :0-0
Timestamp: 2025-09-29T02:49:37.032Z
Learning: For comprehensive test coverage in talawa-api mutation tests, it's essential to test all error scenarios (unauthenticated, invalid arguments, resource not found, business logic violations) and success scenarios using the mercuriusClient pattern with proper GraphQL document nodes and suite/test structure from Vitest.

Applied to files:

  • test/graphql/types/Mutation/createChatMembership.test.ts
🧬 Code graph analysis (13)
test/graphql/types/Query/UnreadChats.test.ts (2)
test/graphql/types/documentNodes.ts (1)
  • Query_signIn (191-218)
test/routes/graphql/documentNodes.ts (1)
  • Query_signIn (191-218)
test/graphql/types/Chat/members.test.ts (1)
test/graphql/types/documentNodes.ts (1)
  • Query_signIn (191-218)
test/graphql/types/Mutation/updateChatMembership.test.ts (1)
test/graphql/types/documentNodes.ts (1)
  • Mutation_deleteUser (49-74)
src/graphql/types/Subscription/chatMessageUpdate.ts (2)
src/graphql/inputs/SubscriptionChatMessageUpdateInput.ts (2)
  • subscriptionChatMessageUpdateInputSchema (4-6)
  • SubscriptionChatMessageUpdateInput (8-20)
src/utilities/TalawaGraphQLError.ts (1)
  • TalawaGraphQLError (264-277)
test/graphql/types/Mutation/createChat.test.ts (2)
test/graphql/types/documentNodes.ts (1)
  • Mutation_createChat (835-842)
src/utilities/TalawaGraphQLError.ts (1)
  • TalawaGraphQLFormattedError (282-284)
src/graphql/enums/ChatType.ts (1)
src/drizzle/enums/chatType.ts (1)
  • chatTypeEnum (6-6)
src/graphql/types/Subscription/chatMessageDelete.ts (2)
src/graphql/inputs/SubscriptionChatMessageDeleteInput.ts (2)
  • subscriptionChatMessageDeleteInputSchema (4-6)
  • SubscriptionChatMessageDeleteInput (8-20)
src/utilities/TalawaGraphQLError.ts (1)
  • TalawaGraphQLError (264-277)
src/graphql/types/Mutation/createChatMembership.ts (3)
src/graphql/types/Chat/Chat.ts (2)
  • Chat (25-25)
  • Chat (27-27)
src/utilities/TalawaGraphQLError.ts (1)
  • TalawaGraphQLError (264-277)
src/drizzle/tables/chatMemberships.ts (1)
  • chatMembershipsTable (18-99)
src/graphql/types/Mutation/createChat.ts (2)
src/utilities/TalawaGraphQLError.ts (1)
  • TalawaGraphQLError (264-277)
src/drizzle/tables/chats.ts (1)
  • chatsTable (14-101)
src/graphql/types/Mutation/updateChatMembership.ts (1)
src/utilities/TalawaGraphQLError.ts (1)
  • TalawaGraphQLError (264-277)
test/graphql/types/Mutation/MarkChatAsRead.test.ts (1)
test/graphql/types/documentNodes.ts (3)
  • Mutation_createOrganizationMembership (469-474)
  • Mutation_deleteChatMembership (913-919)
  • Mutation_createChatMembership (899-911)
src/graphql/inputs/MutationCreateChatInput.ts (2)
src/drizzle/tables/chats.ts (1)
  • chatsTableInsertSchema (142-146)
src/graphql/enums/ChatType.ts (1)
  • ChatType (4-8)
test/graphql/types/Mutation/createChatMembership.test.ts (1)
test/graphql/types/documentNodes.ts (9)
  • Query_signIn (191-218)
  • Mutation_createUser (12-40)
  • Mutation_deleteUser (49-74)
  • Mutation_createOrganization (415-423)
  • Mutation_deleteOrganization (503-510)
  • Mutation_createOrganizationMembership (469-474)
  • Mutation_createChat (835-842)
  • Mutation_deleteChat (1336-1343)
  • Mutation_createChatMembership (899-911)
🪛 GitHub Actions: Pull request workflow
test/graphql/types/Query/UnreadChats.test.ts

[error] 187-187: TypeScript error TS7006: Parameter 'c' implicitly has an 'any' type.


[error] 207-207: TypeScript error TS7006: Parameter 'c' implicitly has an 'any' type.


[error] 337-337: TypeScript error TS7006: Parameter 'c' implicitly has an 'any' type.

test/graphql/types/Mutation/createChat.test.ts

[error] 693-696: Formatter would have printed the following content: extra whitespace/newline difference near end of test block (formatting issue detected by biome format:check)

src/graphql/enums/ChatType.ts

[error] 5-6: Formatter would have printed the following content: description: "Possible variants of the type of a chat." (formatting issue detected by biome format:check)

src/graphql/types/Mutation/createChat.ts

[error] 341-341: Formatter would have printed the following content: unintended formatting change detected (formatting issue detected by biome format:check)

src/graphql/inputs/MutationCreateChatInput.ts

[error] 29-33: Formatter would have printed the following content: incorrect description formatting for participants field (formatting issue detected by biome format:check)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyse Code With CodeQL (typescript)
🔇 Additional comments (18)
src/drizzle/enums/chatType.ts (1)

1-6: LGTM!

Clean runtime-validated enum definition using Zod. This will provide type safety across the database and GraphQL layers.

src/graphql/inputs/SubscriptionChatMessageDeleteInput.ts (1)

1-20: LGTM!

Clean implementation of the subscription input with proper UUID validation and clear documentation.

drizzle_migrations/20251015160719_perpetual_wasp.sql (1)

1-2: Migration is correct, but consider unique constraint in follow-up.

The migration successfully adds the required columns. However, as noted in the review of src/drizzle/tables/chats.ts, direct_participants_hash should have a unique constraint to prevent duplicate direct chats between the same pair of users. Consider creating a follow-up migration to add this constraint.

src/graphql/types/Subscription/index.ts (1)

3-4: LGTM! New subscription imports added.

The new subscription imports follow the existing pattern and properly register the chat message update and delete subscriptions.

src/graphql/types/Mutation/deleteChatMessage.ts (1)

156-156: Critical fix: Corrected PubSub topic for delete events.

The delete mutation now publishes to the correct ::delete topic instead of incorrectly publishing to ::create. This ensures subscribers receive delete events on the appropriate channel.

src/graphql/enums/index.ts (1)

4-4: LGTM! ChatType enum properly registered.

The import follows the established pattern and maintains alphabetical ordering.

src/graphql/types/Mutation/updateChatMessage.ts (1)

161-161: Critical fix: Corrected PubSub topic for update events.

The update mutation now publishes to the correct ::update topic instead of incorrectly publishing to ::create. This ensures subscribers receive update events on the appropriate channel.

test/graphql/types/gql.tada.d.ts (1)

25-25: Auto-generated introspection updated correctly.

The introspection types reflect the new schema changes (Chat.type field and MutationCreateChatInput.participants field). As this is an auto-generated file, these changes are expected and correct.

Based on learnings

Also applies to: 105-105

src/graphql/types/Mutation/updateChatMembership.ts (1)

140-152: Guard correctly enforces direct chat membership immutability.

The guard properly prevents membership modifications for direct (1:1) chats. The placement in the control flow is correct, and the error code is appropriate.

Consider whether the default error message for unauthorized_action_on_arguments_associated_resources clearly communicates to API consumers that direct chat memberships cannot be modified. If not, you might want to provide a custom message parameter to make the intent more explicit.

src/graphql/inputs/SubscriptionChatMessageUpdateInput.ts (1)

1-20: LGTM! Subscription input follows established patterns.

The input type is well-structured with proper validation (UUID) and clear descriptions. It mirrors the pattern used in SubscriptionChatMessageDeleteInput.

src/graphql/types/Subscription/chatMessageUpdate.ts (1)

9-9: The import path is correct. Ignore this review comment.

The file actually exists as graphqLimits.ts, not graphqlLimits.ts. The import statement import envConfig from "~/src/utilities/graphqLimits" matches the actual filename and is correct. The suggested change would break the build.

Likely an incorrect or invalid review comment.

test/graphql/types/Mutation/updateChatMembership.test.ts (1)

58-63: Fail fast on missing IDs; reduce boilerplate with a helper.

  • Add explicit assertions for *.user?.id (and tokens where used) right after creation; avoid passing undefined to DB filters/cleanup.
  • Consider a createAndDeleteTestUser-style helper to encapsulate create/sign-in/cleanup to cut repetition and ensure cleanup parity. Based on learnings.

Patch pattern:

@@
 assertToBeNonNullish(creatorRes.data?.createUser);
 const creator = creatorRes.data.createUser;
+assertToBeNonNullish(creator.user?.id);
@@
 assertToBeNonNullish(actorRes.data?.createUser);
 const actor = actorRes.data.createUser;
+assertToBeNonNullish(actor.user?.id);
@@
 assertToBeNonNullish(targetRes.data?.createUser);
 const target = targetRes.data.createUser;
+assertToBeNonNullish(target.user?.id);

Additionally, in beforeAll (Lines 731-734), check password too:

- if (!server.envConfig.API_ADMINISTRATOR_USER_EMAIL_ADDRESS) {
+ if (
+   !server.envConfig.API_ADMINISTRATOR_USER_EMAIL_ADDRESS ||
+   !server.envConfig.API_ADMINISTRATOR_USER_PASSWORD
+ ) {

Also applies to: 83-84, 104-105, 177-178, 202-202, 259-260, 281-281, 407-407, 428-428, 657-657, 780-780, 829-829, 851-851, 987-987, 1009-1009, 1140-1140

⛔ Skipped due to learnings
Learnt from: meetulr
PR: PalisadoesFoundation/talawa-api#2635
File: tests/resolvers/UserTag/usersAssignedTo.spec.ts:81-124
Timestamp: 2024-11-02T04:54:41.376Z
Learning: In test files such as `tests/resolvers/UserTag/usersAssignedTo.spec.ts` in the Talawa API project, the team prefers to prioritize code clarity over database query optimization. Separate database queries are acceptable for the sake of readability in tests.
src/graphql/pubsub.ts (1)

33-38: Clarify or remove the unused ::system variant keys before refactoring.

The ::system keys (lines 34, 36, 38) are defined in the type but never published or subscribed to. Current code only emits and listens to base topics:

  • Publishers: chats.${chatId}:chat_messages::create|update|delete
  • Subscribers: chats.${id}:chat_messages::create|update|delete

The mapped type refactor is syntactically valid and would reduce duplication, but it would preserve these unused keys. Either remove the ::system variants if they're not needed, or clarify their purpose before refactoring.

src/graphql/inputs/MutationCreateChatInput.ts (1)

5-17: Ignore this review comment—the suggested validation already exists in the resolver.

The createChat resolver (src/graphql/types/Mutation/createChat.ts) already enforces the business logic you've highlighted:

  • Direct chats require exactly 2 participants and validates they are different (lines 130–155)
  • Group chats allow optional participants (line 229)
  • A participants hash is calculated for direct chats to ensure uniqueness

Adding the suggested superRefine validation to the schema would duplicate this logic. The Biome formatting complaint is not a file issue but a configuration error in biome.jsonc (unknown keys "ignore" and "organizeImports").

The review comment is based on incomplete information about the resolver's existing validation and a misconfigured Biome setup. The code as written is correct and handles the participant requirements appropriately.

Likely an incorrect or invalid review comment.

schema.graphql (3)

431-436: Enum addition looks good.

ChatType enum values align with usage in inputs and mutations.

Ensure all resolvers and DB enums map 1:1 to these values (no casing/drift).


2012-2013: Minor doc tweak OK.

No functional change; description is clearer.


4842-4856: New subscriptions read well and match topic taxonomy.

Descriptions and field wiring are consistent with create/update/delete events.

Do we also need a parallel “::system” subscription variant or a flag to include system topics?

test/graphql/types/Mutation/createChat.test.ts (1)

181-189: Good: tests now pass required type.

Supplying type: "group" aligns with the new input contract.

Also applies to: 211-217, 247-251, 319-323, 347-349, 371-373

Comment on lines +356 to +358
"""Type of the chat: 'group' or 'direct'."""
type: 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 | 🟠 Major

Use the new ChatType enum for Chat.type (and make it non-null).

Exposing Chat.type as String weakens the contract and diverges from the new ChatType enum. Switch to ChatType! for consistency and safer client typing.

Apply this schema diff:

-  """Type of the chat: 'group' or 'direct'."""
-  type: String
+  """Type of the chat."""
+  type: ChatType!
📝 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
"""Type of the chat: 'group' or 'direct'."""
type: String
"""Type of the chat."""
type: ChatType!
🤖 Prompt for AI Agents
In schema.graphql around lines 356 to 358, the Chat.type field is declared as a
String; change it to use the existing ChatType enum and make it non-nullable by
updating the field to ChatType! so the schema reads that Chat.type: ChatType!
ensuring stronger typing and consistency with the ChatType enum throughout the
schema.

Comment on lines +4864 to +4874
"""Input for subscribing to chat message delete events."""
input SubscriptionChatMessageDeleteInput {
"""Global identifier of the chat to subscribe to."""
id: ID!
}

"""Input for subscribing to chat message update events."""
input SubscriptionChatMessageUpdateInput {
"""Global identifier of the chat to subscribe to."""
id: ID!
}
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Unify ID types across subscription inputs.

Here id: ID! is used, but SubscriptionChatMessageCreateInput still exposes id: String!. Align all three to ID! for consistency.

🤖 Prompt for AI Agents
In schema.graphql around lines 4864 to 4874, the subscription input types are
inconsistent: SubscriptionChatMessageDeleteInput and
SubscriptionChatMessageUpdateInput use id: ID! while
SubscriptionChatMessageCreateInput (elsewhere) uses id: String!; change the id
field on SubscriptionChatMessageCreateInput to id: ID! to match the others,
update any related schema documentation/comment if present, and run schema
validation and GraphQL codegen/tests to ensure no downstream breakage.

Comment on lines +17 to +27
/**
* Type of the chat: 'group' or 'direct'.
*/
type: text("type").notNull().default("group"),
/**
* Canonicalized participants hash for direct chats (e.g. "smallerId:largerId").
* Used to ensure a single direct chat exists for a pair of users.
*/
directParticipantsHash: text("direct_participants_hash").$defaultFn(
() => sql`${null}`,
),
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Missing unique constraint on directParticipantsHash.

The directParticipantsHash field is described as being "Used to ensure a single direct chat exists for a pair of users." However, there is no unique constraint or index on this column. Without a unique constraint, multiple direct chats can be created between the same pair of users, especially under concurrent operations.

Add a unique index in the table definition:

(self) => [
	index().on(self.creatorId),
	index().on(self.name),
	index().on(self.organizationId),
	index().on(self.updaterId),
	unique().on(self.directParticipantsHash),
],

You'll also need a corresponding migration to add the unique constraint to existing tables.

🤖 Prompt for AI Agents
In src/drizzle/tables/chats.ts around lines 17 to 27, add a unique index on
directParticipantsHash to enforce one direct chat per participant-pair: update
the table's index list to include a unique().on(self.directParticipantsHash)
(preferably as a partial/filtered unique index limited to type='direct' and
direct_participants_hash IS NOT NULL to avoid affecting group chats), and create
a migration that adds this unique constraint to the existing table — the
migration must handle existing duplicate direct hashes (either deduplicate or
fail with a clear remediation step) and ensure the index semantics match the DB
(e.g., partial index for Postgres) so group chats with NULL hashes are
unaffected.

Comment on lines +25 to +27
directParticipantsHash: text("direct_participants_hash").$defaultFn(
() => sql`${null}`,
),
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Simplify nullable default value.

The use of .$defaultFn(() => sql\${null}`)` is unnecessarily complex for a simple null default. Drizzle ORM allows nullable columns without explicit default functions.

Simplify to:

-		directParticipantsHash: text("direct_participants_hash").$defaultFn(
-			() => sql`${null}`,
-		),
+		directParticipantsHash: text("direct_participants_hash"),
📝 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
directParticipantsHash: text("direct_participants_hash").$defaultFn(
() => sql`${null}`,
),
directParticipantsHash: text("direct_participants_hash"),
🤖 Prompt for AI Agents
In src/drizzle/tables/chats.ts around lines 25 to 27, replace the current
default function that returns sql`${null}` with a plain nullable column
declaration; remove the .$defaultFn(() => sql`${null}`) and declare the column
as text("direct_participants_hash").nullable() so the column is nullable without
an explicit default function.

await mercuriusClient.mutate(Mutation_deleteUser, {
headers: { authorization: `bearer ${adminToken}` },
variables: { input: { id: user1Id } },
variables: { input: { id: user1.user?.id } },
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Optional chaining after non-nullish assertion is inconsistent.

Line 69 asserts that user1Res.data?.createUser is non-nullish, yet Line 74 uses optional chaining (user1.user?.id). If the assertion passes, user1.user should be non-null. Consider using non-null assertion instead: user1.user!.id.

This pattern repeats throughout the file (Lines 95, 118, 129, 137, 148, 170, 175, 180, 185, 315, 338, 365, 370, 426, 469, 490, 513, 540).

Apply this pattern to be more explicit:

-				variables: { input: { id: user1.user?.id } },
+				variables: { input: { id: user1.user!.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
variables: { input: { id: user1.user?.id } },
variables: { input: { id: user1.user!.id } },
🤖 Prompt for AI Agents
In test/graphql/types/Mutation/MarkChatAsRead.test.ts around line 74 (and also
at lines 95, 118, 129, 137, 148, 170, 175, 180, 185, 315, 338, 365, 370, 426,
469, 490, 513, 540), the test first asserts that user1Res.data?.createUser is
non-nullish but then uses optional chaining (e.g., user1.user?.id); replace
these optional chains with non-null assertions (e.g., user1.user!.id) so the
code consistently reflects the prior non-null assertion and avoids redundant
optional chaining.

await mercuriusClient.mutate(Mutation_deleteUser, {
headers: { authorization: `bearer ${adminToken}` },
variables: { input: { id: memberId } },
variables: { input: { id: member.user?.id } },
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Optional chaining after non-nullish assertion is inconsistent.

Line 82 asserts that memberRes.data?.createUser is non-nullish, yet Line 87 uses optional chaining (member.user?.id). Consider using non-null assertion instead: member.user!.id.

This pattern repeats throughout the file.

🤖 Prompt for AI Agents
In test/graphql/types/Query/UnreadChats.test.ts around line 87 (and other places
in the file where optional chaining follows an earlier non-nullish assertion of
the same value), replace uses of optional chaining like member.user?.id with
non-null assertion member.user!.id to reflect the prior guarantee
(memberRes.data?.createUser) and keep type consistency; update all repeated
occurrences in the file and run TypeScript checks to ensure no remaining
optional-chaining-after-assertion instances remain.

assertToBeNonNullish(unreadRes.data?.unreadChats);
const unreadList = unreadRes.data.unreadChats;
expect(unreadList.some((c: { id: string }) => c.id === chatId)).toBe(true);
expect(unreadList.some((c) => c.id === chatId)).toBe(true);
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

Fix TypeScript error: implicit any type.

The callback parameter c has an implicit any type, causing a TypeScript compilation error (confirmed by pipeline failures).

Add a type annotation to the callback parameter:

-		expect(unreadList.some((c) => c.id === chatId)).toBe(true);
+		expect(unreadList.some((c: { id: string }) => c.id === chatId)).toBe(true);
📝 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
expect(unreadList.some((c) => c.id === chatId)).toBe(true);
expect(unreadList.some((c: { id: string }) => c.id === chatId)).toBe(true);
🧰 Tools
🪛 GitHub Actions: Pull request workflow

[error] 187-187: TypeScript error TS7006: Parameter 'c' implicitly has an 'any' type.

🤖 Prompt for AI Agents
In test/graphql/types/Query/UnreadChats.test.ts around line 187, the arrow
callback parameter `c` in unreadList.some(...) has an implicit any causing
TypeScript errors; add an explicit type annotation for `c` (for example use the
project's chat/unread type like `UnreadChat` or a minimal shape such as `(c: {
id: string })`) and import the proper type if needed so the callback becomes
typed (e.g., `(c: UnreadChat)` or `(c: { id: string }) => c.id === chatId`).

expect(outsiderList.some((c: { id: string }) => c.id === chatId)).toBe(
false,
);
expect(outsiderList.some((c) => c.id === chatId)).toBe(false);
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

Fix TypeScript error: implicit any type.

The callback parameter c has an implicit any type, causing a TypeScript compilation error (confirmed by pipeline failures).

Add a type annotation to the callback parameter:

-		expect(outsiderList.some((c) => c.id === chatId)).toBe(false);
+		expect(outsiderList.some((c: { id: string }) => c.id === chatId)).toBe(false);
📝 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
expect(outsiderList.some((c) => c.id === chatId)).toBe(false);
expect(outsiderList.some((c: { id: string }) => c.id === chatId)).toBe(false);
🧰 Tools
🪛 GitHub Actions: Pull request workflow

[error] 207-207: TypeScript error TS7006: Parameter 'c' implicitly has an 'any' type.

🤖 Prompt for AI Agents
In test/graphql/types/Query/UnreadChats.test.ts around line 207, the
arrow-function callback c in outsiderList.some(...) has an implicit any type
causing a TypeScript error; add an explicit type annotation for the callback
parameter (use the actual chat element type used in tests, e.g. Chat or the
array element type via typeof outsiderList[number]) and import or reference that
type as needed so the callback becomes typed and the compilation error is
resolved.

assertToBeNonNullish(res.data?.unreadChats);
const afterList = res.data.unreadChats;
expect(afterList.some((c: { id: string }) => c.id === chatId)).toBe(false);
expect(afterList.some((c) => c.id === chatId)).toBe(false);
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

Fix TypeScript error: implicit any type.

The callback parameter c has an implicit any type, causing a TypeScript compilation error (confirmed by pipeline failures).

Add a type annotation to the callback parameter:

-		expect(afterList.some((c) => c.id === chatId)).toBe(false);
+		expect(afterList.some((c: { id: string }) => c.id === chatId)).toBe(false);
📝 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
expect(afterList.some((c) => c.id === chatId)).toBe(false);
expect(afterList.some((c: { id: string }) => c.id === chatId)).toBe(false);
🧰 Tools
🪛 GitHub Actions: Pull request workflow

[error] 337-337: TypeScript error TS7006: Parameter 'c' implicitly has an 'any' type.

🤖 Prompt for AI Agents
In test/graphql/types/Query/UnreadChats.test.ts around line 337, the arrow
callback passed to afterList.some has an implicit any for parameter c; update
the parameter to include an explicit type (e.g., (c: Chat) or (c: { id: string
}) depending on available types) and import or reference the correct
Chat/interface type at the top of the file so TypeScript no longer infers any.

@iamanishx iamanishx closed this Oct 25, 2025
@iamanishx iamanishx reopened this Oct 25, 2025
@iamanishx
Copy link
Author

i will rebase the branch later as of now we are not proceeding with changes cause this will break our flutter application

Signed-off-by: imanishx <[email protected]>
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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/graphql/types/Mutation/createChat.ts (1)

153-159: Fix possibly-uninitialized avatar fields.

avatarName/avatarMimeType may be undefined at runtime; declare as possibly undefined or conditionally spread into .values.

-  let avatarMimeType: z.infer<typeof imageMimeTypeEnum>;
-  let avatarName: string;
+  let avatarMimeType: z.infer<typeof imageMimeTypeEnum> | undefined;
+  let avatarName: string | undefined;

Optionally, use conditional spread in insert values:

-  .values({
-    avatarMimeType,
-    avatarName,
+  .values({
+    ...(avatarName && avatarMimeType
+      ? { avatarName, avatarMimeType }
+      : {}),
     creatorId: currentUserId,
     ...
   })

Also applies to: 201-210, 291-299

♻️ Duplicate comments (4)
src/graphql/types/Mutation/createChat.ts (3)

164-179: Direct chats: require the acting user to be one of the two participants.

Prevents creating chats between two third parties. This was previously requested.

   if (chatType === "direct") {
     if (!Array.isArray(participants) || participants.length !== 2) {
       ...
     }
     const [a, b] = participants as [string, string];
+    if (a !== currentUserId && b !== currentUserId) {
+      throw new TalawaGraphQLError({
+        extensions: {
+          code: "invalid_arguments",
+          issues: [
+            {
+              argumentPath: ["input", "participants"],
+              message: "Acting user must be one of the participants in a direct chat.",
+            },
+          ],
+        },
+      });
+    }
     if (a === b) {
       ...
     }

198-213: Make direct-chat creation race-safe (onConflict + unique index).

Without onConflict, concurrent requests can create duplicates. Use composite unique key and do-nothing fallback.

   const [maybeCreated] = await tx
     .insert(chatsTable)
     .values({
       ...(avatarName && avatarMimeType ? { avatarName, avatarMimeType } : {}),
       creatorId: currentUserId,
       description: parsedArgs.input.description,
       name: parsedArgs.input.name,
       organizationId: parsedArgs.input.organizationId,
       type: chatType,
       directParticipantsHash: participantsHash,
     })
+    .onConflictDoNothing({
+      target: [chatsTable.organizationId, chatsTable.directParticipantsHash, chatsTable.type],
+    })
     .returning();

Also add a DB unique partial index in a migration:

  • Unique (organization_id, direct_participants_hash, type) WHERE type='direct'.

332-344: Group path: validate participants are members of the organization.

Currently inserts memberships blindly; enforce org membership first.

- if (Array.isArray(participants) && participants.length > 0) {
-   await tx.insert(chatMembershipsTable)...
- }
+ if (Array.isArray(participants) && participants.length > 0) {
+   const orgMembers = await tx.query.organizationMembershipsTable.findMany({
+     columns: { memberId: true },
+     where: (f, ops) =>
+       ops.and(
+         ops.eq(f.organizationId, parsedArgs.input.organizationId),
+         ops.inArray(f.memberId, participants),
+       ),
+   });
+   if (orgMembers.length !== participants.length) {
+     throw new TalawaGraphQLError({
+       extensions: {
+         code: "invalid_arguments",
+         issues: [{ argumentPath: ["input","participants"], message: "All participants must be organization members." }],
+       },
+     });
+   }
+   await tx.insert(chatMembershipsTable)...
+ }
test/graphql/types/Mutation/createChat.test.ts (1)

159-160: Replace empty catches and run formatter.

Use a comment to satisfy linters and avoid swallowing unexpected errors silently.

-      } catch (error) {}
+      } catch {
+        // ignore cleanup errors
+      }

Also applies to: 168-169, 177-178

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fd70199 and f275885.

📒 Files selected for processing (5)
  • src/graphql/enums/ChatType.ts (1 hunks)
  • src/graphql/inputs/MutationCreateChatInput.ts (3 hunks)
  • src/graphql/types/Mutation/createChat.ts (2 hunks)
  • test/graphql/types/Mutation/createChat.test.ts (11 hunks)
  • test/graphql/types/gql.tada.d.ts (3 hunks)
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2025-02-10T18:57:27.311Z
Learnt from: shivasankaran18
PR: PalisadoesFoundation/talawa-api#3175
File: test/graphql/types/Mutation/updateAgendaItem.test.ts:33-49
Timestamp: 2025-02-10T18:57:27.311Z
Learning: In test/graphql/types/Mutation/updateAgendaItem.test.ts, while a global mock setup is used for the drizzle client, an alternative approach is to use specialized mocks initialized in beforeEach for each test scenario to reduce global state and ensure test isolation. However, this requires careful type handling to maintain type safety.

Applied to files:

  • test/graphql/types/Mutation/createChat.test.ts
📚 Learning: 2025-02-18T15:03:52.111Z
Learnt from: yugal07
PR: PalisadoesFoundation/talawa-api#3218
File: test/routes/graphql/Query/event.test.ts:107-140
Timestamp: 2025-02-18T15:03:52.111Z
Learning: In test/routes/graphql/Query/event.test.ts, the createAndDeleteTestUser function intentionally combines user creation and deletion in a single function to ensure proper cleanup after test execution, as every user creation needs to be cleaned up.

Applied to files:

  • test/graphql/types/Mutation/createChat.test.ts
📚 Learning: 2024-09-22T09:37:15.069Z
Learnt from: disha1202
PR: PalisadoesFoundation/talawa-api#2466
File: src/types/generatedGraphQLTypes.ts:3096-3101
Timestamp: 2024-09-22T09:37:15.069Z
Learning: Direct chats do not need to be associated with any organization, so the `organizationId` field in `ChatInput` remains optional.

Applied to files:

  • test/graphql/types/Mutation/createChat.test.ts
🧬 Code graph analysis (4)
src/graphql/enums/ChatType.ts (1)
src/drizzle/enums/chatType.ts (1)
  • chatTypeEnum (6-6)
test/graphql/types/Mutation/createChat.test.ts (2)
test/graphql/types/documentNodes.ts (1)
  • Mutation_createChat (835-842)
src/utilities/TalawaGraphQLError.ts (1)
  • TalawaGraphQLFormattedError (282-284)
src/graphql/types/Mutation/createChat.ts (2)
src/utilities/TalawaGraphQLError.ts (1)
  • TalawaGraphQLError (264-277)
src/drizzle/tables/chats.ts (1)
  • chatsTable (14-101)
src/graphql/inputs/MutationCreateChatInput.ts (2)
src/drizzle/tables/chats.ts (1)
  • chatsTableInsertSchema (142-146)
src/graphql/enums/ChatType.ts (1)
  • ChatType (4-7)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyse Code With CodeQL (typescript)
🔇 Additional comments (2)
src/graphql/enums/ChatType.ts (1)

4-7: LGTM — enum wiring and formatting are correct.

src/graphql/inputs/MutationCreateChatInput.ts (1)

7-17: Confirm: should direct chats require organizationId?

Current schema/flow require orgId for direct chats; earlier guidance indicated direct chats didn’t need an org. Intentional change? If not, we should make organizationId optional for direct and branch validation accordingly. Based on learnings.

Comment on lines 15 to 17
avatar: z.custom<Promise<FileUpload>>().nullish(),
participants: z.array(z.string()).optional(),
});
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Enforce “participants required for direct” at schema-level.

Add a zod superRefine so validation fails early (exactly 2 distinct IDs when type='direct').

 export const mutationCreateChatInputSchema = chatsTableInsertSchema
   .pick({
     description: true,
     name: true,
     organizationId: true,
     type: true,
   })
   .extend({
     avatar: z.custom<Promise<FileUpload>>().nullish(),
     participants: z.array(z.string()).optional(),
-  });
+  })
+  .superRefine((i, ctx) => {
+    if (i.type === "direct") {
+      if (!Array.isArray(i.participants) || i.participants.length !== 2) {
+        ctx.addIssue({
+          code: z.ZodIssueCode.custom,
+          path: ["participants"],
+          message: "Direct chats must have exactly 2 participants.",
+        });
+      } else if (i.participants[0] === i.participants[1]) {
+        ctx.addIssue({
+          code: z.ZodIssueCode.custom,
+          path: ["participants"],
+          message: "Participants must be two distinct users.",
+        });
+      }
+    }
+  });

Also applies to: 30-35, 47-51

Comment on lines +195 to +199
const [p1, p2] = a < b ? [a, b] : [b, a];
const participantsHash = `${p1}:${p2}`;

return await ctx.drizzleClient.transaction(async (tx) => {
const [maybeCreated] = await tx
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Validate direct-chat participants are org members before insert.

Avoids adding non-members as chat members.

- const [p1, p2] = a < b ? [a, b] : [b, a];
+ const [p1, p2] = a < b ? [a, b] : [b, a];
+ const orgMembers = await ctx.drizzleClient.query.organizationMembershipsTable.findMany({
+   columns: { memberId: true },
+   where: (f, ops) =>
+     ops.and(
+       ops.eq(f.organizationId, parsedArgs.input.organizationId),
+       ops.inArray(f.memberId, [p1, p2]),
+     ),
+ });
+ if (orgMembers.length !== 2) {
+   throw new TalawaGraphQLError({
+     extensions: {
+       code: "invalid_arguments",
+       issues: [{ argumentPath: ["input", "participants"], message: "All participants must be organization members." }],
+     },
+   });
+ }

Add import:

+import { organizationMembershipsTable } from "~/src/drizzle/tables/organizationMemberships";

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/graphql/types/Mutation/createChat.ts around lines 195 to 199, before
creating the direct-chat record, validate that both participants are members of
the target organization and add the missing import for the org-members table;
query the org-members table (or use the existing isOrgMember helper) for the
provided orgId and both user IDs, throw a user-facing error if either is not a
member, and only then proceed with the tx insert; also add the import statement
for the org-members table/helper at the top of the file (e.g. import {
orgMembers } from 'src/db/schema' or import { isOrgMember } from
'src/lib/auth').

Comment on lines +215 to +231
.insert(chatMembershipsTable)
.values([
{
chatId: maybeCreated.id,
memberId: p1,
creatorId: currentUserId,
role: "regular",
},
{
chatId: maybeCreated.id,
memberId: p2,
creatorId: currentUserId,
role: "regular",
},
])
.onConflictDoNothing();

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

🧩 Analysis chain

Confirm unique constraint for chatMemberships and target it in onConflict.

Ensure a UNIQUE(chat_id, member_id) exists; if not, duplicates can still be inserted. Then target it explicitly in .onConflictDoNothing.

Also applies to: 324-344


🏁 Script executed:

#!/bin/bash
# Verify unique constraints for chat memberships and direct chats.

echo "== Locate chatMembershipsTable definition and unique constraints =="
rg -nP '(chatMembershipsTable|unique|index)\(' -C3

echo "== Check chats unique/partial index for direct participants hash =="
rg -nP 'direct_participants_hash|unique|index|partial' src/drizzle -n -C3

Length of output: 121240


🏁 Script executed:

#!/bin/bash
# Get the createChat.ts file content around the two mentioned line ranges
echo "=== Lines 215-231 in createChat.ts ==="
sed -n '210,235p' src/graphql/types/Mutation/createChat.ts

echo ""
echo "=== Lines 324-344 in createChat.ts ==="
sed -n '320,350p' src/graphql/types/Mutation/createChat.ts

echo ""
echo "=== Check for onConflictDoNothing calls ==="
grep -n "onConflictDoNothing" src/graphql/types/Mutation/createChat.ts

Length of output: 1588


🏁 Script executed:

#!/bin/bash
# Get more context around line 280
echo "=== Context around line 280 ==="
sed -n '270,290p' src/graphql/types/Mutation/createChat.ts

# Check if there are any explicit onConflictDoNothing with target() calls elsewhere
echo ""
echo "=== Search for onConflict patterns with explicit targets ==="
rg -n "onConflict.*target|\.target\(" src/graphql/types/Mutation/createChat.ts

# Check the full file for any conflict handling patterns
echo ""
echo "=== Check for unique constraint definitions or mentions ==="
rg -n "unique|constraint|conflict" src/drizzle/tables/chatMemberships.ts -i

Length of output: 608


Add explicit ON CONFLICT target to chatMemberships inserts and ensure directParticipantsHash is uniquely constrained.

The primaryKey on (chatId, memberId) exists but onConflictDoNothing() calls at lines 230, 280, 331, and 343 must explicitly target it. Additionally, chats.directParticipantsHash (line 25 in schema) lacks a unique index to enforce single direct chat per user pair.

Fix chatMemberships inserts by adding target:

.onConflictDoNothing({ target: [chatMembershipsTable.chatId, chatMembershipsTable.memberId] })

Add to chats table indexes:

uniqueIndex("unique_direct_participants").on(self.directParticipantsHash).where(sql`${chatsTable.directParticipantsHash} IS NOT NULL`)
🤖 Prompt for AI Agents
In src/graphql/types/Mutation/createChat.ts around lines 215-231 (and similarly
update the other onConflictDoNothing calls at ~lines 230, 280, 331, 343), the
.onConflictDoNothing() calls must explicitly target the composite primary key to
avoid ambiguous conflict resolution; change them to call onConflictDoNothing
with target set to [chatMembershipsTable.chatId, chatMembershipsTable.memberId].
Also update the chats table schema (around the chats schema definition, ~line
25) to add a unique index on directParticipantsHash
(uniqueIndex("unique_direct_participants").on(self.directParticipantsHash).where(sql`${chatsTable.directParticipantsHash}
IS NOT NULL`)) so directParticipantsHash is uniquely constrained for non-null
values, ensuring only one direct chat per user pair.

'CancelMembershipResponse': { kind: 'OBJECT'; name: 'CancelMembershipResponse'; fields: { 'message': { name: 'message'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; } }; 'success': { name: 'success'; type: { kind: 'SCALAR'; name: 'Boolean'; ofType: null; } }; }; };
'CategoriesByIdsInput': { kind: 'INPUT_OBJECT'; name: 'CategoriesByIdsInput'; isOneOf: false; inputFields: [{ name: 'ids'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; }; }; }; defaultValue: null }]; };
'Chat': { kind: 'OBJECT'; name: 'Chat'; fields: { 'avatarMimeType': { name: 'avatarMimeType'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; } }; 'avatarURL': { name: 'avatarURL'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; } }; 'createdAt': { name: 'createdAt'; type: { kind: 'SCALAR'; name: 'DateTime'; ofType: null; } }; 'creator': { name: 'creator'; type: { kind: 'OBJECT'; name: 'User'; ofType: null; } }; 'description': { name: 'description'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; } }; 'firstUnreadMessageId': { name: 'firstUnreadMessageId'; type: { kind: 'SCALAR'; name: 'ID'; ofType: null; } }; 'hasUnread': { name: 'hasUnread'; type: { kind: 'SCALAR'; name: 'Boolean'; ofType: null; } }; 'id': { name: 'id'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; } }; 'lastMessage': { name: 'lastMessage'; type: { kind: 'OBJECT'; name: 'ChatMessage'; ofType: null; } }; 'members': { name: 'members'; type: { kind: 'OBJECT'; name: 'ChatMembersConnection'; ofType: null; } }; 'messages': { name: 'messages'; type: { kind: 'OBJECT'; name: 'ChatMessagesConnection'; ofType: null; } }; 'name': { name: 'name'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; } }; 'organization': { name: 'organization'; type: { kind: 'OBJECT'; name: 'Organization'; ofType: null; } }; 'unreadMessagesCount': { name: 'unreadMessagesCount'; type: { kind: 'SCALAR'; name: 'Int'; ofType: null; } }; 'updatedAt': { name: 'updatedAt'; type: { kind: 'SCALAR'; name: 'DateTime'; ofType: null; } }; 'updater': { name: 'updater'; type: { kind: 'OBJECT'; name: 'User'; ofType: null; } }; }; };
'Chat': { kind: 'OBJECT'; name: 'Chat'; fields: { 'avatarMimeType': { name: 'avatarMimeType'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; } }; 'avatarURL': { name: 'avatarURL'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; } }; 'createdAt': { name: 'createdAt'; type: { kind: 'SCALAR'; name: 'DateTime'; ofType: null; } }; 'creator': { name: 'creator'; type: { kind: 'OBJECT'; name: 'User'; ofType: null; } }; 'description': { name: 'description'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; } }; 'firstUnreadMessageId': { name: 'firstUnreadMessageId'; type: { kind: 'SCALAR'; name: 'ID'; ofType: null; } }; 'hasUnread': { name: 'hasUnread'; type: { kind: 'SCALAR'; name: 'Boolean'; ofType: null; } }; 'id': { name: 'id'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; } }; 'lastMessage': { name: 'lastMessage'; type: { kind: 'OBJECT'; name: 'ChatMessage'; ofType: null; } }; 'members': { name: 'members'; type: { kind: 'OBJECT'; name: 'ChatMembersConnection'; ofType: null; } }; 'messages': { name: 'messages'; type: { kind: 'OBJECT'; name: 'ChatMessagesConnection'; ofType: null; } }; 'name': { name: 'name'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; } }; 'organization': { name: 'organization'; type: { kind: 'OBJECT'; name: 'Organization'; ofType: null; } }; 'type': { name: 'type'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; } }; 'unreadMessagesCount': { name: 'unreadMessagesCount'; type: { kind: 'SCALAR'; name: 'Int'; ofType: null; } }; 'updatedAt': { name: 'updatedAt'; type: { kind: 'SCALAR'; name: 'DateTime'; ofType: null; } }; 'updater': { name: 'updater'; type: { kind: 'OBJECT'; name: 'User'; ofType: null; } }; }; };
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Prefer Chat.type: ChatType over String.

Expose Chat.type as the ChatType enum to align API and client typing; update the Chat builder accordingly so introspection reflects the enum.

🤖 Prompt for AI Agents
In test/graphql/types/gql.tada.d.ts around line 25, the Chat.type field is
currently typed as a SCALAR String in the generated introspection; change it to
use the ChatType enum so the field shows kind: 'SCALAR' -> kind: 'ENUM' with
name: 'ChatType'. Update the Chat schema/builder where the field is defined to
reference the ChatType enum (not a plain string), then regenerate the TypeScript
GraphQL types so introspection and client typings reflect Chat.type as ChatType.

Comment on lines +270 to +273
'Subscription': { kind: 'OBJECT'; name: 'Subscription'; fields: { 'chatMessageCreate': { name: 'chatMessageCreate'; type: { kind: 'OBJECT'; name: 'ChatMessage'; ofType: null; } }; 'chatMessageDelete': { name: 'chatMessageDelete'; type: { kind: 'OBJECT'; name: 'ChatMessage'; ofType: null; } }; 'chatMessageUpdate': { name: 'chatMessageUpdate'; type: { kind: 'OBJECT'; name: 'ChatMessage'; ofType: null; } }; }; };
'SubscriptionChatMessageCreateInput': { kind: 'INPUT_OBJECT'; name: 'SubscriptionChatMessageCreateInput'; isOneOf: false; inputFields: [{ name: 'id'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; }; defaultValue: null }]; };
'SubscriptionChatMessageDeleteInput': { kind: 'INPUT_OBJECT'; name: 'SubscriptionChatMessageDeleteInput'; isOneOf: false; inputFields: [{ name: 'id'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; }; defaultValue: null }]; };
'SubscriptionChatMessageUpdateInput': { kind: 'INPUT_OBJECT'; name: 'SubscriptionChatMessageUpdateInput'; isOneOf: false; inputFields: [{ name: 'id'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; }; defaultValue: null }]; };
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Normalize subscription input IDs (String vs ID).

SubscriptionChatMessageCreateInput.id is String while delete/update use ID; please standardize on ID for all three.

🤖 Prompt for AI Agents
In test/graphql/types/gql.tada.d.ts around lines 270 to 273, the
SubscriptionChatMessageCreateInput.id is typed as String while
SubscriptionChatMessageDeleteInput.id and SubscriptionChatMessageUpdateInput.id
are typed as ID; update
SubscriptionChatMessageCreateInput.inputFields[0].type.ofType.name from 'String'
to 'ID' so all three subscription input IDs consistently use the ID scalar, and
run any typegen/compile step to verify the change.

Comment on lines +455 to +505
test("creating the same direct chat twice returns the existing chat and does not duplicate memberships", async () => {
// create a fresh second user and add to organization
const secondUser = await createTestUser(adminAuthToken, "regular");
createdUserIds.push(secondUser.userId);

await createOrganizationMembership(
adminAuthToken,
secondUser.userId,
organizationId,
"regular",
);

const input = {
name: `Direct Idempotent ${faker.string.uuid()}`,
participants: [regularUserId, secondUser.userId],
organizationId,
type: "direct" as const,
};

const first = await mercuriusClient.mutate(Mutation_createChat, {
headers: { authorization: `bearer ${regularUserAuthToken}` },
variables: { input },
});

expect(first.errors).toBeUndefined();
expect(first.data?.createChat).not.toBeNull();
assertToBeNonNullish(first.data?.createChat?.id);
createdChatIds.push(first.data.createChat.id);

const second = await mercuriusClient.mutate(Mutation_createChat, {
headers: { authorization: `bearer ${regularUserAuthToken}` },
variables: { input },
});

expect(second.errors).toBeUndefined();
expect(second.data?.createChat).not.toBeNull();
assertToBeNonNullish(second.data?.createChat?.id);

const memberships =
await server.drizzleClient.query.chatMembershipsTable.findMany({
where: (f, ops) => ops.eq(f.chatId, first.data.createChat.id),
});
const memberIds = new Set(memberships.map((m) => m.memberId));
expect(memberIds.has(regularUserId)).toBe(true);
expect(memberIds.has(secondUser.userId)).toBe(true);
// ensure no duplicate rows per member
for (const id of memberIds) {
expect(memberships.filter((m) => m.memberId === id).length).toBe(1);
}
});

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Strengthen idempotency test: assert same chat ID and no dup memberships.

Currently doesn’t prove the second call reuses the same chat.

-  expect(second.errors).toBeUndefined();
-  expect(second.data?.createChat).not.toBeNull();
-  assertToBeNonNullish(second.data?.createChat?.id);
+  expect(second.errors).toBeUndefined();
+  expect(second.data?.createChat).not.toBeNull();
+  assertToBeNonNullish(second.data?.createChat?.id);
+  expect(second.data!.createChat!.id).toBe(first.data!.createChat!.id);
🤖 Prompt for AI Agents
In test/graphql/types/Mutation/createChat.test.ts around lines 455 to 505, the
test does not assert that the second createChat call returned the same chat id
as the first, and it may push the same id twice into createdChatIds; add an
assertion that second.data?.createChat?.id === first.data?.createChat?.id (e.g.
expect(second.data?.createChat?.id).toEqual(first.data?.createChat?.id)), and
only push the chat id into createdChatIds if it isn't already present (or avoid
pushing after the second call) so the cleanup list doesn't contain duplicates.

Comment on lines +506 to +536
test("creator is administrator for created group chats", async () => {
const chatName = `CreatorAdmin ${faker.string.uuid()}`;
const result = await mercuriusClient.mutate(Mutation_createChat, {
headers: { authorization: `bearer ${regularUserAuthToken}` },
variables: {
input: {
name: chatName,
organizationId: organizationId,
type: "group",
},
},
});

expect(result.errors).toBeUndefined();
expect(result.data?.createChat).not.toBeNull();
assertToBeNonNullish(result.data?.createChat?.id);
createdChatIds.push(result.data.createChat.id);

const membership =
await server.drizzleClient.query.chatMembershipsTable.findFirst({
where: (f, ops) =>
ops.and(
ops.eq(f.chatId, result.data.createChat.id),
ops.eq(f.memberId, regularUserId),
),
});

expect(membership).not.toBeUndefined();
expect(membership?.role).toBe("administrator");
});

Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Add missing negative tests (actor must be a participant; all participants must be org members).

Covers critical validations in the resolver.

// Append near other direct-chat tests

test("direct chat requires acting user to be one of the participants", async () => {
  const outsider = await createTestUser(adminAuthToken, "regular");
  createdUserIds.push(outsider.userId);
  await createOrganizationMembership(adminAuthToken, outsider.userId, organizationId, "regular");

  const result = await mercuriusClient.mutate(Mutation_createChat, {
    headers: { authorization: `bearer ${regularUserAuthToken}` },
    variables: {
      input: {
        name: `Bad Direct ${faker.string.uuid()}`,
        participants: [outsider.userId, adminUserId], // excludes regularUserId
        organizationId,
        type: "direct",
      },
    },
  });

  expect(result.data?.createChat).toBeNull();
  expect(result.errors?.[0]?.extensions?.code).toBe("invalid_arguments");
});

test("rejects participants who are not members of the organization", async () => {
  const nonMember = await createTestUser(adminAuthToken, "regular");
  createdUserIds.push(nonMember.userId);
  // intentionally do NOT add org membership

  const result = await mercuriusClient.mutate(Mutation_createChat, {
    headers: { authorization: `bearer ${regularUserAuthToken}` },
    variables: {
      input: {
        name: `Group With NonMember ${faker.string.uuid()}`,
        organizationId,
        type: "group",
        participants: [nonMember.userId],
      },
    },
  });

  expect(result.data?.createChat).toBeNull();
  expect(result.errors?.[0]?.extensions?.code).toBe("invalid_arguments");
});
🤖 Prompt for AI Agents
test/graphql/types/Mutation/createChat.test.ts around lines 506-536: the suite
is missing two negative tests that validate important resolver checks; add one
test that verifies creating a direct chat fails if the acting user is not listed
as a participant (create a new outsider user, optionally add org membership for
them, call the Mutation_createChat with participants that exclude regularUserId,
assert createChat is null and the first error extensions.code equals
"invalid_arguments"), and add a second test that verifies creating a group chat
fails when any participant is not a member of the organization (create a
nonMember user without adding org membership, call the mutation with that user
in participants, assert createChat is null and error code is
"invalid_arguments"); ensure new users are pushed to createdUserIds for cleanup
and reuse existing helpers (createTestUser, createOrganizationMembership,
mercuriusClient.mutate) and follow the project test patterns for headers and
variables.

@github-actions
Copy link

github-actions bot commented Nov 5, 2025

This pull request did not get any activity in the past 10 days and will be closed in 360 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 5, 2025
@palisadoes
Copy link
Contributor

FYI: We have implemented automatic issue assignments

@github-actions github-actions bot removed the no-pr-activity No pull request activity label Nov 14, 2025
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