Skip to content

chore(api): migrate channels.ts to typed HTTP methods - #41415

Merged
ggazzo merged 30 commits into
developfrom
chore/api-migrate-channels
Jul 29, 2026
Merged

chore(api): migrate channels.ts to typed HTTP methods#41415
ggazzo merged 30 commits into
developfrom
chore/api-migrate-channels

Conversation

@ggazzo

@ggazzo ggazzo commented Jul 16, 2026

Copy link
Copy Markdown
Member

Proposed changes

Continues the API endpoint migration (docs/api-endpoint-migration.md): migrates apps/meteor/server/api/v1/channels.ts from the legacy API.v1.addRoute() pattern to the typed API.v1.get/post pattern with AJV request/response validation.

WIP / incremental — migrating the file in batches. channels.* endpoints are already declared in ChannelsEndpoints (rest-typings), so this follows the moderation pattern: convert the registration and add response schemas, keeping the manual Endpoints entry (no ExtractRoutesFromAPI augmentation, avoids TS2717).

Migrated so far

  • channels.addAll, channels.archive, channels.unarchive, channels.join, channels.kick, channels.leave

Notes

  • Shared channelResponseSchema ($ref IRoom) + successResponseSchema.
  • findChannelByIdOrName throws Meteor.Error for client errors (room-not-found / archived). The typed router does not convert thrown errors to 400 (the legacy addRoute wrapper did), so each handler catches and returns API.v1.failure(message, errorType) to preserve the previous behavior.
  • No behavior change intended — pure refactor/typing; no changeset needed.
  • Remaining channels.* endpoints (and groups.ts) will follow in subsequent batches; messages/history/files return IMessage and may need relaxed schemas (attachment oneOf validation limitation).

Testing

  • tsc --noEmit, eslint and prettier clean for the migrated file.
  • Response schemas validate only under TEST_MODE; e2e channels suite will exercise them.

Review in cubic

Task: ARCH-2262

Summary by CodeRabbit

  • Bug Fixes

    • Improved consistency of channel-related API responses by standardizing typed success and failure payloads across many actions (e.g., join/leave, moderation, announcements, and room settings).
    • Added stricter request/response validation for channel operations to return more predictable, client-friendly HTTP status codes on failures.
  • Documentation

    • Updated the API endpoint migration guide with new guidance on typed-route error handling: use try/catch and return API.v1.failure(...) to avoid 500 responses from uncaught thrown errors.

WIP migrating channels.ts from addRoute to typed API.v1 (moderation pattern —
kept manual ChannelsEndpoints entry, no augment). Batch 1: addAll, archive,
unarchive, join, kick, leave.

- Shared channelResponseSchema ($ref IRoom) + successResponseSchema.
- findChannelByIdOrName throws Meteor.Error; the typed router does not map
  throws to 400, so each handler catches and returns API.v1.failure(msg,
  errorType) to preserve the previous behavior.
@changeset-bot

changeset-bot Bot commented Jul 16, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 5210034

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

This PR includes no changesets

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

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

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

@dionisio-bot

dionisio-bot Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

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

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

Please fix the issues and try again

If you have any trouble, please check the PR guidelines

@coderabbitai

coderabbitai Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Channel management endpoints are migrated from legacy routes to typed API.v1 handlers with AJV request/query and response schemas, standardized failure mapping, and explicit error responses. The migration guide documents thrown-error handling and cross-service error-shape extraction.

Changes

Channel API route migration

Layer / File(s) Summary
Route contracts and channel status handling
apps/meteor/server/api/v1/channels.ts
Adds reusable AJV schemas and error normalization while migrating channels.addAll, channels.archive, and channels.unarchive.
Membership route migration
apps/meteor/server/api/v1/channels.ts
Migrates channels.join, channels.kick, and channels.leave to typed POST handlers with validated bodies and explicit responses.
Moderation and leadership route migration
apps/meteor/server/api/v1/channels.ts
Migrates moderator, owner, and leader management endpoints with standardized caught-error conversion.
Channel information and listing routes
apps/meteor/server/api/v1/channels.ts
Migrates channels.info and channels.list to typed GET handlers with AJV query validation and response contracts.
Channel settings route migration
apps/meteor/server/api/v1/channels.ts
Migrates channel status and setting operations with reusable request schemas, typed responses, and normalized failures.
Typed router error guidance
docs/api-endpoint-migration.md
Documents explicit failure responses, shape-based errorType extraction, and inline failure returns.

Estimated code review effort: 4 (Complex) | ~45 minutes

Suggested labels: type: chore

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: migrating channels.ts to typed HTTP methods.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Warning

Review ran into problems

🔥 Problems

Errors were encountered while retrieving linked issues.

Errors (1)
  • ARCH-2262: Request failed with status code 401

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.

@codecov

codecov Bot commented Jul 16, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 77.82910% with 96 lines in your changes missing coverage. Please review.
✅ Project coverage is 68.71%. Comparing base (cff23f9) to head (5210034).
⚠️ Report is 105 commits behind head on develop.

Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff             @@
##           develop   #41415      +/-   ##
===========================================
+ Coverage    68.48%   68.71%   +0.23%     
===========================================
  Files         4092     4151      +59     
  Lines       158216   159516    +1300     
  Branches     28678    28203     -475     
===========================================
+ Hits        108351   109612    +1261     
+ Misses       44827    44731      -96     
- Partials      5038     5173     +135     
Flag Coverage Δ
e2e 58.84% <100.00%> (-0.45%) ⬇️
e2e-api 45.70% <77.72%> (+0.19%) ⬆️
unit 70.66% <0.00%> (+0.32%) ⬆️

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

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

Errors from core-services (e.g. Room.join) cross a service boundary and are not
instanceof the local Meteor.Error, so the catch dropped their errorType and the
channels.join e2e assertions (`expected ... to have property 'errorType'`)
failed. Extract message/errorType by shape instead of instanceof, matching the
legacy addRoute behavior.
@ggazzo ggazzo added this to the 8.7.0 milestone Jul 16, 2026
@ggazzo
ggazzo marked this pull request as ready for review July 16, 2026 14:30
@ggazzo
ggazzo requested a review from a team as a code owner July 16, 2026 14:30
@ggazzo

ggazzo commented Jul 16, 2026

Copy link
Copy Markdown
Member Author

/jira ARCH-1464

KevLehman
KevLehman previously approved these changes Jul 16, 2026

@KevLehman KevLehman left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

didn't know we dropped the "global" try/catch behavior for the api routes.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
apps/meteor/server/api/v1/channels.ts (1)

158-161: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider a higher-order function to DRY up repetitive error handling.

Since this migration is incremental and many more channels.* and groups.* endpoints are planned for subsequent batches, wrapping handlers in a higher-order function can eliminate the identical try/catch boilerplate across all endpoints.

💡 Example wrapper implementation
function withErrorHandler<T>(action: (this: T) => Promise<any>) {
	return async function(this: T) {
		try {
			return await action.call(this);
		} catch (error) {
			const [message, errorType] = errorToFailureArgs(error);
			return API.v1.failure(message, errorType);
		}
	};
}

// Usage example:
API.v1.post(
	'channels.addAll',
	{ /* ... */ },
	withErrorHandler(async function() {
		// Handler logic without try/catch
	})
);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/meteor/server/api/v1/channels.ts` around lines 158 - 161, Introduce a
reusable higher-order error-handling wrapper near the affected channel endpoint
handlers, using the existing errorToFailureArgs and API.v1.failure behavior.
Apply it to the handlers currently containing identical try/catch blocks,
including the affected channels.* endpoints, and remove their inline
error-handling boilerplate while preserving handler this-context and responses.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/meteor/server/api/v1/channels.ts`:
- Around line 132-135: Update errorToFailureArgs to read the Meteor.Error reason
before message: include an optional reason field in the extracted error shape
and use it when it is a string, falling back to the existing
message/String(error) behavior otherwise. Preserve the current extraction of the
string error code for the second tuple value.

---

Nitpick comments:
In `@apps/meteor/server/api/v1/channels.ts`:
- Around line 158-161: Introduce a reusable higher-order error-handling wrapper
near the affected channel endpoint handlers, using the existing
errorToFailureArgs and API.v1.failure behavior. Apply it to the handlers
currently containing identical try/catch blocks, including the affected
channels.* endpoints, and remove their inline error-handling boilerplate while
preserving handler this-context and responses.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 75387af3-ccf1-4e18-82ad-b7b2bd692be0

📥 Commits

Reviewing files that changed from the base of the PR and between cff23f9 and d9d142a.

📒 Files selected for processing (1)
  • apps/meteor/server/api/v1/channels.ts
📜 Review details
⏰ Context from checks skipped due to timeout. (10)
  • GitHub Check: cubic · AI code reviewer
  • GitHub Check: Hacktron Security Check
  • GitHub Check: 🔨 Test UI (EE) / MongoDB 8.0 coverage (4/5)
  • GitHub Check: 🔨 Test UI (EE) / MongoDB 8.0 coverage (5/5)
  • GitHub Check: 🔨 Test UI (EE) / MongoDB 8.0 coverage (2/5)
  • GitHub Check: 🔨 Test UI (EE) / MongoDB 8.0 coverage (1/5)
  • GitHub Check: 🔨 Test UI (CE) / MongoDB 8.0 (1/4)
  • GitHub Check: 🔨 Test UI (EE) / MongoDB 8.0 coverage (3/5)
  • GitHub Check: 🔨 Test UI (CE) / MongoDB 8.0 (4/4)
  • GitHub Check: 🔨 Test UI (CE) / MongoDB 8.0 (2/4)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (.cursor/rules/playwright.mdc)

**/*.{ts,tsx,js}: Write concise, technical TypeScript/JavaScript with accurate typing in Playwright tests
Avoid code comments in the implementation

Files:

  • apps/meteor/server/api/v1/channels.ts
🧠 Learnings (3)
📚 Learning: 2026-02-26T19:25:44.063Z
Learnt from: gabriellsh
Repo: RocketChat/Rocket.Chat PR: 38778
File: packages/ui-voip/src/providers/useMediaSession.ts:192-192
Timestamp: 2026-02-26T19:25:44.063Z
Learning: In the Rocket.Chat repository, do not reference Biome lint rules in code review feedback. Biome is not used even if biome.json exists; only reference Biome rules if there is explicit, project-wide usage documented. For TypeScript files, review lint implications without Biome guidance unless the project enables Biome rules.

Applied to files:

  • apps/meteor/server/api/v1/channels.ts
📚 Learning: 2026-02-26T19:25:44.063Z
Learnt from: gabriellsh
Repo: RocketChat/Rocket.Chat PR: 38778
File: packages/ui-voip/src/providers/useMediaSession.ts:192-192
Timestamp: 2026-02-26T19:25:44.063Z
Learning: In this repository (RocketChat/Rocket.Chat), Biome lint rules are not used even if a biome.json exists. When reviewing TypeScript files (e.g., packages/ui-voip/src/providers/useMediaSession.ts), ensure lint suggestions do not reference Biome-specific rules. Rely on general ESLint/TypeScript lint rules and project conventions instead.

Applied to files:

  • apps/meteor/server/api/v1/channels.ts
📚 Learning: 2026-05-06T12:21:44.083Z
Learnt from: juliajforesti
Repo: RocketChat/Rocket.Chat PR: 40256
File: apps/meteor/client/components/CreateDiscussion/CreateDiscussion.tsx:121-149
Timestamp: 2026-05-06T12:21:44.083Z
Learning: Field wrappers in rocket.chat/fuselage-forms (Field, FieldLabel, FieldRow, FieldError, FieldHint) auto-create htmlFor/id associations, aria-describedby, and role="alert" for errors. Do not manually set htmlFor, id, aria-describedby, or role attributes when using these wrappers. This automatic wiring does not apply to plain rocket.chat/fuselage components, which require explicit ID wiring per the accessibility docs. In code reviews, prefer using fuselage-forms wrappers for form fields and verify there is no unnecessary manual ID/aria wiring in files that use these wrappers. If a component uses plain fuselage components, ensure proper id wiring as per docs.

Applied to files:

  • apps/meteor/server/api/v1/channels.ts

Comment thread apps/meteor/server/api/v1/channels.ts Outdated

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

All reported issues were addressed across 1 file

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread apps/meteor/server/api/v1/channels.ts Outdated
ggazzo added 2 commits July 16, 2026 11:42
The typed router has no global error handler, so thrown errors (incl. Meteor.Error)
become 500 — handlers must catch client-error throws and return API.v1.failure.
core-services errors are not instanceof the local Meteor.Error, so extract
message/errorType by shape.
Migrate rename, setCustomFields, setDefault, setDescription, setPurpose,
setTopic, setType. These addRoute endpoints had no validateParams, so use inline
ajv body validators (roomSettingBody helper: room target + setting field),
matching the inline-validator style used in rooms.ts/users.ts. Responses:
channelResponseSchema ($ref IRoom) or the {field} schema; findChannelByIdOrName
throws are caught → API.v1.failure.
Migrate addModerator, addOwner, removeModerator, removeOwner, addLeader,
removeLeader (all void, body: isChannelsModeratorsProps). findChannelByIdOrName
throws caught -> API.v1.failure.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
apps/meteor/server/api/v1/channels.ts (1)

152-152: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Remove the implementation comment.

The helper is self-explanatory from its name and signature. As per coding guidelines, “Avoid code comments in the implementation.” <coding_guidelines>

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/meteor/server/api/v1/channels.ts` at line 152, Remove the implementation
comment above the channels.set* body validator helper in channels.ts, leaving
the helper and its behavior unchanged.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/meteor/server/api/v1/channels.ts`:
- Around line 153-163: Update the shared roomSettingBody validator to require
the setting field together with at least one room target, roomId or roomName, so
requests cannot validate without a channel identifier; preserve the existing
field schema and additionalProperties restrictions.

In `@docs/api-endpoint-migration.md`:
- Around line 568-576: Update the action example’s catch block to handle only
known client-signaling errors via errorToFailureArgs and API.v1.failure; rethrow
all unexpected exceptions so they retain server-error handling and avoid
exposing internal messages. Keep the successful findChannelByIdOrName response
unchanged.

---

Nitpick comments:
In `@apps/meteor/server/api/v1/channels.ts`:
- Line 152: Remove the implementation comment above the channels.set* body
validator helper in channels.ts, leaving the helper and its behavior unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 112c3443-1bf2-490a-b272-aa44bbb7566b

📥 Commits

Reviewing files that changed from the base of the PR and between d9d142a and 90af3f4.

📒 Files selected for processing (2)
  • apps/meteor/server/api/v1/channels.ts
  • docs/api-endpoint-migration.md
📜 Review details
⏰ Context from checks skipped due to timeout. (3)
  • GitHub Check: CodeQL-Build
  • GitHub Check: cubic · AI code reviewer
  • GitHub Check: CodeQL-Build
⚠️ CI failures not shown inline (2)

GitHub Check: Dionisio QA: Some checks did not pass

Conclusion: failure

View job details

**Conclusion:** failure
### Steps
- ✅ **No merge conflicts**
- ❌ **QA assured** — This PR is missing the 'stat: QA assured' label
- ✅ **Mergeable**
- ✅ **Has milestone or project**
- ✅ **Valid PR title**
- ✅ **Correct target version**

GitHub Check: Dionisio QA: Some checks did not pass

Conclusion: failure

View job details

**Conclusion:** failure
### Steps
- ✅ **No merge conflicts**
- ❌ **QA assured** — This PR is missing the 'stat: QA assured' label
- ✅ **Mergeable**
- ✅ **Has milestone or project**
- ✅ **Valid PR title**
- ✅ **Correct target version**
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (.cursor/rules/playwright.mdc)

**/*.{ts,tsx,js}: Write concise, technical TypeScript/JavaScript with accurate typing in Playwright tests
Avoid code comments in the implementation

Files:

  • apps/meteor/server/api/v1/channels.ts
🧠 Learnings (3)
📚 Learning: 2026-02-26T19:25:44.063Z
Learnt from: gabriellsh
Repo: RocketChat/Rocket.Chat PR: 38778
File: packages/ui-voip/src/providers/useMediaSession.ts:192-192
Timestamp: 2026-02-26T19:25:44.063Z
Learning: In the Rocket.Chat repository, do not reference Biome lint rules in code review feedback. Biome is not used even if biome.json exists; only reference Biome rules if there is explicit, project-wide usage documented. For TypeScript files, review lint implications without Biome guidance unless the project enables Biome rules.

Applied to files:

  • apps/meteor/server/api/v1/channels.ts
📚 Learning: 2026-02-26T19:25:44.063Z
Learnt from: gabriellsh
Repo: RocketChat/Rocket.Chat PR: 38778
File: packages/ui-voip/src/providers/useMediaSession.ts:192-192
Timestamp: 2026-02-26T19:25:44.063Z
Learning: In this repository (RocketChat/Rocket.Chat), Biome lint rules are not used even if a biome.json exists. When reviewing TypeScript files (e.g., packages/ui-voip/src/providers/useMediaSession.ts), ensure lint suggestions do not reference Biome-specific rules. Rely on general ESLint/TypeScript lint rules and project conventions instead.

Applied to files:

  • apps/meteor/server/api/v1/channels.ts
📚 Learning: 2026-05-06T12:21:44.083Z
Learnt from: juliajforesti
Repo: RocketChat/Rocket.Chat PR: 40256
File: apps/meteor/client/components/CreateDiscussion/CreateDiscussion.tsx:121-149
Timestamp: 2026-05-06T12:21:44.083Z
Learning: Field wrappers in rocket.chat/fuselage-forms (Field, FieldLabel, FieldRow, FieldError, FieldHint) auto-create htmlFor/id associations, aria-describedby, and role="alert" for errors. Do not manually set htmlFor, id, aria-describedby, or role attributes when using these wrappers. This automatic wiring does not apply to plain rocket.chat/fuselage components, which require explicit ID wiring per the accessibility docs. In code reviews, prefer using fuselage-forms wrappers for form fields and verify there is no unnecessary manual ID/aria wiring in files that use these wrappers. If a component uses plain fuselage components, ensure proper id wiring as per docs.

Applied to files:

  • apps/meteor/server/api/v1/channels.ts
🔇 Additional comments (2)
docs/api-endpoint-migration.md (1)

561-567: LGTM!

Also applies to: 577-594

apps/meteor/server/api/v1/channels.ts (1)

137-150: LGTM!

Also applies to: 668-718, 1324-1414, 1416-1447, 1449-1490, 1492-1525, 1527-1560, 1562-1595, 1597-1636, 1638-1688

Comment thread apps/meteor/server/api/v1/channels.ts
Comment thread docs/api-endpoint-migration.md Outdated

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

1 issue found across 2 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/meteor/server/api/v1/channels.ts">

<violation number="1" location="apps/meteor/server/api/v1/channels.ts:1453">
P3: `channels.setDefault` now publishes a string-or-boolean request schema while the exported REST client contract permits only booleans. Align `ChannelsSetDefaultProps` (and its validator schema) with this route, or restrict the route schema to the published contract, so generated and typed consumers agree.</violation>
</file>

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread docs/api-endpoint-migration.md Outdated
Comment thread apps/meteor/server/api/v1/channels.ts
Comment thread apps/meteor/server/api/v1/channels.ts
Comment thread apps/meteor/server/api/v1/channels.ts
Add an anyOf(roomId, roomName) constraint to the shared roomSettingBody
validator so channels.set*/rename bodies must carry a channel identifier,
matching the exported REST contracts, and drop the redundant helper comment.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Caution

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

⚠️ Outside diff range comments (2)
apps/meteor/server/api/v1/channels.ts (2)

152-162: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Reject requests containing both room identifiers. anyOf allows both roomId and roomName, and findChannelByIdOrName will silently prefer roomId when both are sent. Use oneOf if the API should accept exactly one identifier.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/meteor/server/api/v1/channels.ts` around lines 152 - 162, Update the
roomSettingBody schema to use oneOf instead of anyOf for the roomId and roomName
requirements, ensuring requests must provide exactly one identifier and cannot
silently prioritize roomId. Keep the existing required field and
additionalProperties constraints unchanged.

531-539: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Include success: true in the response generic.
API.v1.success() adds success to the body, so the response schema should be typed as { announcement?: string; success: true }.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/meteor/server/api/v1/channels.ts` around lines 531 - 539, Update the
response generic passed to ajv.compile for announcementResponseSchema to include
success: true alongside the optional announcement field, matching the success
property added by API.v1.success().
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@apps/meteor/server/api/v1/channels.ts`:
- Around line 152-162: Update the roomSettingBody schema to use oneOf instead of
anyOf for the roomId and roomName requirements, ensuring requests must provide
exactly one identifier and cannot silently prioritize roomId. Keep the existing
required field and additionalProperties constraints unchanged.
- Around line 531-539: Update the response generic passed to ajv.compile for
announcementResponseSchema to include success: true alongside the optional
announcement field, matching the success property added by API.v1.success().

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f61ce0dd-712d-4de3-8660-649dc3805d3f

📥 Commits

Reviewing files that changed from the base of the PR and between 7dca3ed and 352041a.

📒 Files selected for processing (1)
  • apps/meteor/server/api/v1/channels.ts
📜 Review details
⏰ Context from checks skipped due to timeout. (5)
  • GitHub Check: ⚙️ Variables Setup
  • GitHub Check: cubic · AI code reviewer
  • GitHub Check: CodeQL-Build
  • GitHub Check: Hacktron Security Check
  • GitHub Check: CodeQL-Build
⚠️ CI failures not shown inline (4)

GitHub Check: Dionisio QA: Some checks did not pass

Conclusion: failure

View job details

**Conclusion:** failure
### Steps
- ✅ **No merge conflicts**
- ❌ **QA assured** — This PR is missing the 'stat: QA assured' label
- ✅ **Mergeable**
- ✅ **Has milestone or project**
- ✅ **Valid PR title**
- ✅ **Correct target version**

GitHub Check: Dionisio QA: Some checks did not pass

Conclusion: failure

View job details

**Conclusion:** failure
### Steps
- ✅ **No merge conflicts**
- ❌ **QA assured** — This PR is missing the 'stat: QA assured' label
- ✅ **Mergeable**
- ✅ **Has milestone or project**
- ✅ **Valid PR title**
- ✅ **Correct target version**

GitHub Check: Dionisio QA: Some checks did not pass

Conclusion: failure

View job details

**Conclusion:** failure
### Steps
- ✅ **No merge conflicts**
- ❌ **QA assured** — This PR is missing the 'stat: QA assured' label
- ✅ **Mergeable**
- ✅ **Has milestone or project**
- ✅ **Valid PR title**
- ✅ **Correct target version**

GitHub Check: Dionisio QA: Some checks did not pass

Conclusion: failure

View job details

**Conclusion:** failure
### Steps
- ✅ **No merge conflicts**
- ❌ **QA assured** — This PR is missing the 'stat: QA assured' label
- ✅ **Mergeable**
- ✅ **Has milestone or project**
- ✅ **Valid PR title**
- ✅ **Correct target version**
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (.cursor/rules/playwright.mdc)

**/*.{ts,tsx,js}: Write concise, technical TypeScript/JavaScript with accurate typing in Playwright tests
Avoid code comments in the implementation

Files:

  • apps/meteor/server/api/v1/channels.ts
🧠 Learnings (3)
📚 Learning: 2026-02-26T19:25:44.063Z
Learnt from: gabriellsh
Repo: RocketChat/Rocket.Chat PR: 38778
File: packages/ui-voip/src/providers/useMediaSession.ts:192-192
Timestamp: 2026-02-26T19:25:44.063Z
Learning: In the Rocket.Chat repository, do not reference Biome lint rules in code review feedback. Biome is not used even if biome.json exists; only reference Biome rules if there is explicit, project-wide usage documented. For TypeScript files, review lint implications without Biome guidance unless the project enables Biome rules.

Applied to files:

  • apps/meteor/server/api/v1/channels.ts
📚 Learning: 2026-02-26T19:25:44.063Z
Learnt from: gabriellsh
Repo: RocketChat/Rocket.Chat PR: 38778
File: packages/ui-voip/src/providers/useMediaSession.ts:192-192
Timestamp: 2026-02-26T19:25:44.063Z
Learning: In this repository (RocketChat/Rocket.Chat), Biome lint rules are not used even if a biome.json exists. When reviewing TypeScript files (e.g., packages/ui-voip/src/providers/useMediaSession.ts), ensure lint suggestions do not reference Biome-specific rules. Rely on general ESLint/TypeScript lint rules and project conventions instead.

Applied to files:

  • apps/meteor/server/api/v1/channels.ts
📚 Learning: 2026-05-06T12:21:44.083Z
Learnt from: juliajforesti
Repo: RocketChat/Rocket.Chat PR: 40256
File: apps/meteor/client/components/CreateDiscussion/CreateDiscussion.tsx:121-149
Timestamp: 2026-05-06T12:21:44.083Z
Learning: Field wrappers in rocket.chat/fuselage-forms (Field, FieldLabel, FieldRow, FieldError, FieldHint) auto-create htmlFor/id associations, aria-describedby, and role="alert" for errors. Do not manually set htmlFor, id, aria-describedby, or role attributes when using these wrappers. This automatic wiring does not apply to plain rocket.chat/fuselage components, which require explicit ID wiring per the accessibility docs. In code reviews, prefer using fuselage-forms wrappers for form fields and verify there is no unnecessary manual ID/aria wiring in files that use these wrappers. If a component uses plain fuselage components, ensure proper id wiring as per docs.

Applied to files:

  • apps/meteor/server/api/v1/channels.ts
🔇 Additional comments (1)
apps/meteor/server/api/v1/channels.ts (1)

186-189: LGTM!

Also applies to: 193-215, 218-230, 306-318, 524-529, 541-553, 702-727, 729-752

ggazzo added 3 commits July 16, 2026 15:38
- info: GET, roomTargetQuery (ajvQuery), returns { channel } ($ref IRoom); 403 for canAccessRoom.
- list: GET, isChannelsListProps query, PaginatedResult<{ channels: IRoom[] }>; typed ourQuery as Filter<IRoom>.
Adds roomTargetQuery + channelsListResponseSchema helpers.
- roles: GET, returns { roles: RoomRoles[] } (schema includes runtime _id the type omits).
- moderators: GET, returns { moderators: sub.u[] }; 403 for canAccessRoom.
- delete: POST, void.
- close: POST void (roomTargetBody helper added).
- counters: GET, nullable numeric/date fields, userId query param, 403.
@ggazzo

ggazzo commented Jul 16, 2026

Copy link
Copy Markdown
Member Author

🤝 Handoff — continuing this migration in a fresh session

State: channels.ts is 28/41 endpoints migrated (last commit 76db06f69c). All tsc/eslint/prettier clean. Worktree: .claude/worktrees/api-channels, branch chore/api-migrate-channels (off develop). Groups mirror is in #41422.

How to work

  • Migrate a small batch → ./node_modules/.bin/prettier --write apps/meteor/server/api/v1/channels.ts./node_modules/.bin/eslint <file> (0 errors) → NODE_OPTIONS="--max-old-space-size=8192" ./node_modules/.bin/tsc -p apps/meteor/tsconfig.json --noEmit --skipLibCheck 2>&1 | grep 'v1/channels.ts(' (must be empty; ignore unrelated mongodb dual-package noise) → commit → push. No changeset (pure refactor).

Established conventions (reuse the helpers at the top of the file)

  • Pre-existing typed endpoints → convert + add response schema, keep the manual ChannelsEndpoints entry, NO ExtractRoutesFromAPI augmentation (avoids TS2717).
  • addRoute('x', opts, {async get/post(){}})API.v1.get/post('x', {...opts, query|body, response}, async function action(){}). Remember to change the API.v1.addRoute( line too — a frequent miss that leaves this untyped / overload errors.
  • Helpers: channelResponseSchema ($ref IRoom), successResponseSchema (void), stringFieldResponseSchema<T>(field) (description/purpose/topic), roomSettingBody<T>(field, schema) (set* bodies), roomTargetBody<T>() (POST room-target only), roomTargetQuery (GET room-target), channelsListResponseSchema, errorToFailureArgs(error).
  • Endpoints without validateParamsinline ajv.compile<T>() body validators (do NOT try to import new is*Props from rest-typings — new exports aren't picked up by the local meteor tsc; inline is idiomatic, rooms.ts/users.ts do it). GET validators use ajvQuery (coercion).
  • Throws → 500 (no global onError). findChannelByIdOrName and core-services (Room.join, Team.*) throw; wrap handler in try/catchconst [message, errorType] = errorToFailureArgs(error); return API.v1.failure(message, errorType);. Declare 400 in response. canAccessRoom failure → API.v1.forbidden() → declare 403.
  • Type ≠ DB reality: schemas must match actual runtime output, not the TS type (e.g. getRoomRoles projects without _id:0 so items carry _id the RoomRoles type omits — schema includes _id, but the ajv.compile<T> generic must stay = the TS type so success() type-checks; generic and JSON schema may legitimately differ).
  • this.user.username / this.requestIp are string | undefined?? ''.

Remaining (14) — with notes

Endpoint Method Return / note
members GET paginated { members: IUser[] }$ref IUser (verify projected fields)
online GET { online: [{ _id, username }] }
getAllUserMentionsByChannel GET paginated { mentions, count, offset, total }
list.joined GET paginated { channels: IRoom[] } — reuse channelsListResponseSchema
anonymousread GET { messages: IMessage[] } ⚠️ IMessage attachment oneOf → relax with TODO
getIntegrations GET { integrations: IIntegration[] } — IIntegration is a union ($ref oneOf incoming/outgoing)
messages GET { messages: IMessage[] } ⚠️ relax (attachment oneOf)
history GET { messages: IMessage[] } ⚠️ relax; returns getChannelHistory result, forbidden() on null
files GET paginated { files: IUpload[] }
create POST { channel: IRoom } — has createChannelValidator throwing; wrap
invite POST { channel: IRoom }channelResponseSchema
convertToTeam POST { team: ITeam } — check ITeam typia registration or write schema
setJoinCode POST check return; roomSettingBody('joinCode', ...)

⚠️ IMessage endpoints (messages/history/anonymousread): $ref IMessage trips the attachment oneOf (documented pitfall) — use a relaxed { type: 'array', items: { type: 'object' } } with a TODO, per the guide's known-limitation exception.

Full remaining tracker (channels + groups): the plan artifact / ARCH-1464 handoff comment.

isChannelsListProps (additionalProperties:false, no roomId) rejected the
roomId that existing clients/tests pass to list.joined even though the
endpoint ignores it. Use a tolerant inline query that accepts and ignores
roomId/roomName. Also loosen members item validation (additionalProperties)
to avoid rejecting unforeseen projected fields.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@hacktron-app hacktron-app Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

1 issue found across 1 file

Severity Count
MEDIUM 1

View full scan results

Comment thread apps/meteor/server/api/v1/channels.ts Outdated
Comment on lines +140 to +151
function errorToFailureArgs(error: unknown): [string, string | undefined] {
const e = error as { reason?: unknown; message?: unknown; error?: unknown };
// Prefer `reason` so the `[error-code]` suffix that Meteor.Error appends to `message` does not leak to clients.
let message = String(error);
if (typeof e?.message === 'string') {
message = e.message;
}
if (typeof e?.reason === 'string') {
message = e.reason;
}
return [message, typeof e?.error === 'string' ? e.error : undefined];
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

MEDIUM Information Disclosure via Unfiltered Internal Error Messages in errorToFailureArgs

The custom error-handling utility errorToFailureArgs in channels.ts catches any thrown error and extracts its raw message property to return directly to the client. When standard JavaScript exceptions (e.g., TypeError) or database driver errors (e.g., MongoDB write conflicts or query execution errors) occur, they do not contain a reason property (which is specific to Meteor.Error). Consequently, the raw, unfiltered error message is returned to the client, potentially leaking sensitive information such as database collection names, field schemas, query structures, or internal variable names.

Steps to Reproduce
  1. Authenticate as a regular user.
  2. Invoke an API endpoint that uses errorToFailureArgs (e.g., channels.create or channels.rename) with parameters designed to trigger a database or runtime exception (such as a duplicate key error or an unexpected type that bypasses validation but fails in business logic).
  3. Observe the server's response, which contains the raw database or runtime error message.
Fix with AI

Open in Cursor Open in Claude

A security vulnerability was found by Hacktron.

File: apps/meteor/server/api/v1/channels.ts
Lines: 140-151
Severity: medium

Vulnerability: Information Disclosure via Unfiltered Internal Error Messages in errorToFailureArgs

Description:
The custom error-handling utility `errorToFailureArgs` in `channels.ts` catches any thrown error and extracts its raw `message` property to return directly to the client. When standard JavaScript exceptions (e.g., `TypeError`) or database driver errors (e.g., MongoDB write conflicts or query execution errors) occur, they do not contain a `reason` property (which is specific to `Meteor.Error`). Consequently, the raw, unfiltered error message is returned to the client, potentially leaking sensitive information such as database collection names, field schemas, query structures, or internal variable names.

Proof of Concept:
**Steps to Reproduce**

1. Authenticate as a regular user.
2. Invoke an API endpoint that uses `errorToFailureArgs` (e.g., `channels.create` or `channels.rename`) with parameters designed to trigger a database or runtime exception (such as a duplicate key error or an unexpected type that bypasses validation but fails in business logic).
3. Observe the server's response, which contains the raw database or runtime error message.

Affected Code:
function errorToFailureArgs(error: unknown): [string, string | undefined] {
	const e = error as { reason?: unknown; message?: unknown; error?: unknown };
	// Prefer `reason` so the `[error-code]` suffix that Meteor.Error appends to `message` does not leak to clients.
	let message = String(error);
	if (typeof e?.message === 'string') {
		message = e.message;
	}
	if (typeof e?.reason === 'string') {
		message = e.reason;
	}
	return [message, typeof e?.error === 'string' ? e.error : undefined];
}

Acceptance criteria:
- Acceptance is defined by the **actual reported behavior**, not by tests passing.
- Reproduce the issue, or narrow the exact code path that produces it, *before* changing code. State what you confirmed.
- Fix the underlying cause. Mitigations that paper over the reported behavior do not count as a fix.
- Add a regression test that fails on the unpatched code and passes on the fix. If a regression test is genuinely impractical (e.g. race condition, infra-level issue), say so and explain why.
- Existing tests passing is **not** the bar. Do not declare done on tests-pass theatre.

Only change what is necessary to fix this vulnerability. Do not refactor adjacent code or modify unrelated files.

Triage: Reply !fp <reason> (false positive), !valid (confirmed), !accepted_risk <reason>, or !fixed (resolved). Any other reply is saved as a triage note.
Reason is optional but improves future scans — e.g. !fp internal endpoint, not user-facing.

View finding in Hacktron

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

errorToFailureArgs was removed (596e008). The raw-message-for-non-Meteor-errors behavior now only exists in the global ApiClass wrapper (default -> api.failure(e.message)), which is pre-existing and framework-wide. Leaving open as a separate follow-up (classify unexpected errors -> generic 500).

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

All reported issues were addressed across 2 files (changes from recent commits).

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread apps/meteor/server/api/v1/channels.ts
ggazzo and others added 2 commits July 17, 2026 16:03
Revert errorToFailureArgs to use Meteor.Error.message (with the [error-code]
suffix) instead of .reason. Existing e2e tests assert the code is present in
the response `error` field (channels dup-name, teams.delete room-not-found),
matching legacy addRoute behavior. The earlier .reason change (CodeRabbit
suggestion) is incompatible with that contract.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- create: the create modal sends extraData.{topic,federated} (spread verbatim
  into createRoom), but the schema's extraData was additionalProperties:false
  without those keys, so UI channel creation 400'd and hung. Allow arbitrary
  extraData (add topic/federated, additionalProperties:true).
- info: channels.info also serves omnichannel ('l') rooms (livechat test
  helpers call it) which lack the owner `u` that IRoom requires. Use a
  dedicated schema accepting a full IRoom or any room-shaped object.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@hacktron-app hacktron-app Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

1 issue found across 1 file

Severity Count
HIGH 1

View full scan results

Comment thread packages/rest-typings/src/v1/channels/ChannelsCreateProps.ts Outdated

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

All reported issues were addressed across 2 files (changes from recent commits).

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread apps/meteor/server/api/v1/channels.ts
ggazzo and others added 2 commits July 17, 2026 22:47
The create modal sends broadcast:false, which createRoom stores verbatim, so
rooms genuinely carry broadcast:false. IRoom typed it as `true`, making typia
emit enum:[true], which failed response validation (400) on channels.create
and hung UI channel/team-channel creation. Widen to boolean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
IRoom.broadcast widened to boolean, but ISubscription.broadcast is `true |
undefined`. Coerce with `|| undefined` (false -> undefined) at the two cached
store copy sites so the assignment stays `true | undefined` without widening
the subscription type.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@ggazzo
ggazzo requested a review from a team as a code owner July 18, 2026 03:47
@ggazzo ggazzo modified the milestones: 8.7.0, 8.8.0 Jul 22, 2026
Comment thread docs/api-endpoint-migration.md Outdated
ggazzo and others added 5 commits July 28, 2026 16:55
Security:
- channels.create: close extraData (additionalProperties: false) so callers
  can't inject arbitrary room fields (default/featured/retention/abacAttributes);
  the create modal only sends the declared fields (mass-assignment fix).
- channels.getIntegrations / channels.files: apply the user-supplied query
  first and overlay the trusted permission/rid filters last so a crafted query
  can't override the ownership scope or room id.

Correctness:
- errorToFailureArgs now also forwards stack (TEST_MODE) and details, and each
  catch passes them to API.v1.failure — restores the legacy addRoute error body.
- channels.create: type members/teams items as strings; drop stray nullable on
  readOnly/customFields/excludeSelf/extraData (contract now matches the type).
- Remove dead 'bodyParam X is required' branches in set{Description,Purpose,
  Topic} — the body schema already marks the field required.

Left as follow-up (pre-existing, not this migration): raw error-message
disclosure in errorToFailureArgs for non-Meteor errors, and the anyOf vs oneOf
integrations response shape (anyOf validates correctly; oneOf risks overlap).
…piClass error wrapper

The global ApiClass wrapper (_internalRouteActionHandler) already catches thrown
Meteor.Errors and maps them: error-too-many-requests->429, unauthorized->401/403,
forbidden->403/400, default->api.failure(...)=400. The per-handler errorToFailureArgs
catch-all was therefore redundant, and worse, it flattened 401/403/429 responses into
400. Removed the helper and all 41 per-handler try/catch wrappers so handlers just
throw and let the wrapper map the status. Kept the channels.create validator's custom
catch (unauthorized->forbidden), which is not the errorToFailureArgs pattern.

Per review.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- channels.create: throw Meteor.Error('unauthorized') from the create.validate
  permission gate (was a plain Error) and remove the handler's try/catch, so the
  global ApiClass wrapper maps it (403, or 401 under breaking-changes) like every
  other endpoint — instead of forcing 403 locally.
- docs/api-endpoint-migration.md: correct the 'Error Handling' section. The typed
  router DOES map thrown errors: ApiClass wraps every route (incl. typed) in a
  try/catch that maps Meteor.Error -> 429/401/403/400. Handlers should just throw;
  the per-handler catch-all + errorToFailureArgs were redundant.
IIncomingIntegration/IOutgoingIntegration are discriminated by a required
`type` enum (webhook-incoming vs webhook-outgoing), so they are disjoint and
oneOf matches exactly one — no overlap risk. Matches the IIntegration contract
convention.
The create.validate permission gate throws a plain Error('unauthorized'); the
global wrapper would surface its raw message. Restore the local catch that maps
it to API.v1.forbidden() so the response keeps the stable { error: 'unauthorized' }
(403) contract the e2e test asserts. Reverts the over-eager catch removal in b4312d9.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

stat: QA assured Means it has been tested and approved by a company insider type: chore

Projects

Development

Successfully merging this pull request may close these issues.

3 participants