chore: Update coerceTypes to false and modify mocharc settings#39559
chore: Update coerceTypes to false and modify mocharc settings#39559
Conversation
|
Looks like this PR is not ready to merge, because of the following issues:
Please fix the issues and try again If you have any trouble, please check the PR guidelines |
🦋 Changeset detectedLatest commit: 3950b2e The changes in this PR will be included in the next version bump. This PR includes changesets to release 41 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## develop #39559 +/- ##
===========================================
- Coverage 71.01% 70.89% -0.12%
===========================================
Files 3199 3209 +10
Lines 113466 113892 +426
Branches 20574 20664 +90
===========================================
+ Hits 80576 80746 +170
- Misses 30845 31088 +243
- Partials 2045 2058 +13
Flags with carried forward coverage won't be shown. Click here to find out more. 🚀 New features to boost your workflow:
|
…hema With coerceTypes set to false, Ajv now strictly validates response shapes. Users without statusText were failing validation since it was marked as required. Update both the TypeScript type and the JSON schema to reflect that statusText and avatarETag are optional fields. Co-Authored-By: Claude Opus 4.6 <[email protected]>
…lues for group and section This change prepares for the migration of settings by allowing group and section properties to be null, ensuring compatibility with future updates. The upsertPermissions function has been adjusted to handle these changes accordingly.
…alidation Introduce a new AJV instance (ajvQuery) in @rocket.chat/rest-typings with coerceTypes: true, and use it for all query parameter validation across the REST API. URL query strings always arrive as strings; with coercion enabled, values like count=50 or offset=0 are coerced to numbers before validation, so endpoints accept typical query shapes without failing. - Add ajvQuery in packages/rest-typings (same options/formats/keywords as ajv, but coerceTypes: true) and export it. - Use ajvQuery for inline query schemas in rooms, oauthapps, permissions, federation (ee), and for app-defined query validators in call-history, commands, custom-user-status, e2e. - Switch all query-only validators in rest-typings to compile with ajvQuery (banners, customSounds, cloud, roles, autotranslate, integrations, import, calendar, videoConference, settings, moderation, email-inbox, misc, server-events, apps). Body and response validation continue to use the original ajv instance (no coercion). Made-with: Cursor
0005782 to
fcf39f2
Compare
Updated the ICalendarEvent interface to permit meetingUrl to be explicitly set to null, enhancing flexibility in event data handling.
fcf39f2 to
eb12ef5
Compare
- apps/meteor app/api/server ajv.ts and v1/roles.ts - rest-typings channels, chat, dm, groups, users query schemas using ajvQuery Made-with: Cursor
Enhanced the BannerView type definition to allow the title property to accept either a plain string or a UiKit text object, improving flexibility in banner content representation.
Added a check in the UiKitBanner component to return null if the title is not a string, addressing potential issues with title rendering and ensuring proper type handling.
b98800a to
f49acdb
Compare
Changed the expected error property in subscription tests from 'error-invalid-subscription' to 'invalid-params' and ensured the presence of a generic 'error' property for better clarity in error responses.
…ation Updated the validation for GETLivechatRoomsParams to use the ajvQuery instance, enhancing query parameter validation with type coercion capabilities. This change ensures better handling of query parameters in the omnichannel API.
Made-with: Cursor
Made-with: Cursor
Made-with: Cursor
6b85726 to
30ee1ed
Compare
There was a problem hiding this comment.
12 issues found across 86 files
Note: This PR contains a large number of files. cubic only reviews up to 75 files per PR, so some files may not have been reviewed.
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="packages/rest-typings/src/v1/subscriptionsEndpoints.ts">
<violation number="1" location="packages/rest-typings/src/v1/subscriptionsEndpoints.ts:107">
P1: Use the body Ajv instance here. `ajvQuery` enables type coercion for query strings, so this POST validator will start accepting numeric `roomId`/`_id` values by coercing them to strings.</violation>
</file>
<file name="packages/rest-typings/src/v1/chat.ts">
<violation number="1" location="packages/rest-typings/src/v1/chat.ts:3">
P2: This migration leaves three paginated GET validators on `ajv`, so `count`/`offset` query strings will now fail validation for those chat endpoints.</violation>
</file>
<file name="apps/meteor/app/authorization/server/functions/upsertPermissions.ts">
<violation number="1" location="apps/meteor/app/authorization/server/functions/upsertPermissions.ts:57">
P2: Omitting `group` here does not clear existing `group: null` / `section: null` values from stored permissions, so the intended normalization will be skipped.</violation>
</file>
<file name="packages/core-typings/src/ISetting.ts">
<violation number="1" location="packages/core-typings/src/ISetting.ts:56">
P2: Widen `section` as well; the new TODO says legacy settings can still contain `section: null`, so leaving it as `string` keeps this shared type unsound.</violation>
</file>
<file name="packages/rest-typings/src/v1/omnichannel.ts">
<violation number="1" location="packages/rest-typings/src/v1/omnichannel.ts:2353">
P2: This is a response schema, so switching it to `ajvQuery` weakens the contract by coercing mistyped output instead of failing validation.</violation>
<violation number="2" location="packages/rest-typings/src/v1/omnichannel.ts:2793">
P2: This POST body validator should keep `ajv`, not `ajvQuery`; using the query validator here will coerce invalid body fields instead of rejecting them.</violation>
</file>
<file name="apps/meteor/app/livechat/server/api/v1/integration.ts">
<violation number="1" location="apps/meteor/app/livechat/server/api/v1/integration.ts:31">
P2: This coercion runs too late to help clients affected by `coerceTypes: false`; schema validation will reject mistyped payloads before the handler executes.</violation>
<violation number="2" location="apps/meteor/app/livechat/server/api/v1/integration.ts:39">
P1: `null` timeout payloads now get stored as `undefined`, which breaks webhook retry timing and timeout handling downstream.</violation>
</file>
<file name="packages/rest-typings/src/v1/Ajv.ts">
<violation number="1" location="packages/rest-typings/src/v1/Ajv.ts:28">
P2: The format/keyword registrations are fully duplicated between `ajv` and `ajvQuery`. Extract the shared setup into a helper to avoid silent divergence when future formats or keywords are added to only one instance.
```ts
function configureAjv(instance: Ajv) {
addFormats(instance);
instance.addFormat('basic_email', /^[^@]+@[^@]+$/);
instance.addFormat(
'rfc_email',
/^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/,
);
instance.addKeyword({
keyword: 'isNotEmpty',
type: 'string',
validate: (_schema: unknown, data: unknown): boolean => typeof data === 'string' && !!data.trim(),
});
}
configureAjv(ajv);
configureAjv(ajvQuery);
```</violation>
</file>
<file name="apps/meteor/client/views/banners/UiKitBanner.tsx">
<violation number="1" location="apps/meteor/client/views/banners/UiKitBanner.tsx:56">
P2: This drops the entire banner for valid `TextObject` titles. `BannerView.title` explicitly allows `string | TextObject`, so apps sending structured titles will now lose the whole banner instead of just falling back on title rendering.</violation>
</file>
<file name="apps/meteor/server/services/calendar/service.ts">
<violation number="1" location="apps/meteor/server/services/calendar/service.ts:180">
P2: This makes it impossible to reliably clear an existing `meetingUrl`, because explicit clearing values are collapsed to `undefined` before the update payload is built.</violation>
</file>
<file name="packages/rest-typings/src/v1/misc.ts">
<violation number="1" location="packages/rest-typings/src/v1/misc.ts:98">
P2: `isDirectoryProps` is compiled against a type that incorrectly requires optional query fields.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| group?: string | null; | ||
| section?: string; |
There was a problem hiding this comment.
P2: Widen section as well; the new TODO says legacy settings can still contain section: null, so leaving it as string keeps this shared type unsound.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/core-typings/src/ISetting.ts, line 56:
<comment>Widen `section` as well; the new TODO says legacy settings can still contain `section: null`, so leaving it as `string` keeps this shared type unsound.</comment>
<file context>
@@ -52,7 +52,8 @@ export interface ISettingBase extends IRocketChatRecord {
env: boolean;
- group?: string;
+ // TODO: migrate settings with group and section with null to undefined
+ group?: string | null;
section?: string;
tab?: string;
</file context>
| group?: string | null; | |
| section?: string; | |
| group?: string | null; | |
| section?: string | null; |
| typeof LivechatHttpTimeout !== 'undefined' && { _id: 'Livechat_http_timeout', value: LivechatHttpTimeout }, | ||
| typeof LivechatWebhookUrl !== 'undefined' && { | ||
| _id: 'Livechat_webhookUrl', | ||
| value: trim(LivechatWebhookUrl != null ? String(LivechatWebhookUrl) : ''), |
There was a problem hiding this comment.
P2: This coercion runs too late to help clients affected by coerceTypes: false; schema validation will reject mistyped payloads before the handler executes.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/meteor/app/livechat/server/api/v1/integration.ts, line 31:
<comment>This coercion runs too late to help clients affected by `coerceTypes: false`; schema validation will reject mistyped payloads before the handler executes.</comment>
<file context>
@@ -26,9 +26,18 @@ API.v1.addRoute(
- typeof LivechatHttpTimeout !== 'undefined' && { _id: 'Livechat_http_timeout', value: LivechatHttpTimeout },
+ typeof LivechatWebhookUrl !== 'undefined' && {
+ _id: 'Livechat_webhookUrl',
+ value: trim(LivechatWebhookUrl != null ? String(LivechatWebhookUrl) : ''),
+ },
+ typeof LivechatSecretToken !== 'undefined' && {
</file context>
| 'rfc_email', | ||
| /^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/, | ||
| ); | ||
| ajvQuery.addFormat('basic_email', /^[^@]+@[^@]+$/); |
There was a problem hiding this comment.
P2: The format/keyword registrations are fully duplicated between ajv and ajvQuery. Extract the shared setup into a helper to avoid silent divergence when future formats or keywords are added to only one instance.
function configureAjv(instance: Ajv) {
addFormats(instance);
instance.addFormat('basic_email', /^[^@]+@[^@]+$/);
instance.addFormat(
'rfc_email',
/^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/,
);
instance.addKeyword({
keyword: 'isNotEmpty',
type: 'string',
validate: (_schema: unknown, data: unknown): boolean => typeof data === 'string' && !!data.trim(),
});
}
configureAjv(ajv);
configureAjv(ajvQuery);Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/rest-typings/src/v1/Ajv.ts, line 28:
<comment>The format/keyword registrations are fully duplicated between `ajv` and `ajvQuery`. Extract the shared setup into a helper to avoid silent divergence when future formats or keywords are added to only one instance.
```ts
function configureAjv(instance: Ajv) {
addFormats(instance);
instance.addFormat('basic_email', /^[^@]+@[^@]+$/);
instance.addFormat(
'rfc_email',
/^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/,
);
instance.addKeyword({
keyword: 'isNotEmpty',
type: 'string',
validate: (_schema: unknown, data: unknown): boolean => typeof data === 'string' && !!data.trim(),
});
}
configureAjv(ajv);
configureAjv(ajvQuery);
```</comment>
<file context>
@@ -10,18 +18,29 @@ const ajv = new Ajv({
'rfc_email',
/^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/,
);
+ajvQuery.addFormat('basic_email', /^[^@]+@[^@]+$/);
+ajvQuery.addFormat(
+ 'rfc_email',
</file context>
| }); | ||
|
|
||
| // TODO: check why we are not considering TextObject as title | ||
| if (view.title && typeof view.title !== 'string') { |
There was a problem hiding this comment.
P2: This drops the entire banner for valid TextObject titles. BannerView.title explicitly allows string | TextObject, so apps sending structured titles will now lose the whole banner instead of just falling back on title rendering.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/meteor/client/views/banners/UiKitBanner.tsx, line 56:
<comment>This drops the entire banner for valid `TextObject` titles. `BannerView.title` explicitly allows `string | TextObject`, so apps sending structured titles will now lose the whole banner instead of just falling back on title rendering.</comment>
<file context>
@@ -52,6 +52,10 @@ const UiKitBanner = ({ initialView }: UiKitBannerProps) => {
});
+ // TODO: check why we are not considering TextObject as title
+ if (view.title && typeof view.title !== 'string') {
+ return null;
+ }
</file context>
| private async getMeetingUrl(eventData: Partial<ICalendarEvent>): Promise<string | undefined> { | ||
| if (eventData.meetingUrl !== undefined) { | ||
| return eventData.meetingUrl; | ||
| return eventData.meetingUrl || undefined; |
There was a problem hiding this comment.
P2: This makes it impossible to reliably clear an existing meetingUrl, because explicit clearing values are collapsed to undefined before the update payload is built.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/meteor/server/services/calendar/service.ts, line 180:
<comment>This makes it impossible to reliably clear an existing `meetingUrl`, because explicit clearing values are collapsed to `undefined` before the update payload is built.</comment>
<file context>
@@ -177,7 +177,7 @@ export class CalendarService extends ServiceClassInternal implements ICalendarSe
private async getMeetingUrl(eventData: Partial<ICalendarEvent>): Promise<string | undefined> {
if (eventData.meetingUrl !== undefined) {
- return eventData.meetingUrl;
+ return eventData.meetingUrl || undefined;
}
</file context>
| }; | ||
|
|
||
| export const isDirectoryProps = ajv.compile<Directory>(DirectorySchema); | ||
| export const isDirectoryProps = ajvQuery.compile<Directory>(DirectorySchema); |
There was a problem hiding this comment.
P2: isDirectoryProps is compiled against a type that incorrectly requires optional query fields.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/rest-typings/src/v1/misc.ts, line 98:
<comment>`isDirectoryProps` is compiled against a type that incorrectly requires optional query fields.</comment>
<file context>
@@ -95,7 +95,7 @@ const DirectorySchema = {
};
-export const isDirectoryProps = ajv.compile<Directory>(DirectorySchema);
+export const isDirectoryProps = ajvQuery.compile<Directory>(DirectorySchema);
type MethodCall = { method: string; params: unknown[]; id: string; msg: 'string' };
</file context>
| export const isDirectoryProps = ajvQuery.compile<Directory>(DirectorySchema); | |
| export const isDirectoryProps = ajvQuery.compile<PaginatedRequest<{ text?: string; type?: string; workspace?: string }>>(DirectorySchema); |
|
/jira ARCH-1464 |
…s and their usage
There was a problem hiding this comment.
2 issues found across 3 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/app/livechat/server/api/v1/integration.ts">
<violation number="1" location="apps/meteor/app/livechat/server/api/v1/integration.ts:39">
P2: This truthiness check drops a valid timeout of `0`; use a null/undefined check so provided zero values are still saved.</violation>
</file>
<file name="packages/rest-typings/src/v1/omnichannel.ts">
<violation number="1" location="packages/rest-typings/src/v1/omnichannel.ts:3955">
P2: The new `| undefined` fields no longer match the Ajv schema, which still allows omitted and `null` values. That makes `isPOSTomnichannelIntegrations` narrow some valid payloads to the wrong TypeScript shape.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
6c2e307 to
c6ddf34
Compare
This change introduces two separate AJV instances for validating request bodies and query parameters, enhancing type safety. The new setup prevents silent coercion of incorrect types in request bodies, ensuring that numeric and boolean values are sent with the correct types. This may break existing API consumers who rely on previous coercion behavior.
Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: Guilherme Gazzo <[email protected]> Co-authored-by: Guilherme Gazzo <[email protected]> Co-authored-by: Claude Opus 4.6 <[email protected]>
Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: Guilherme Gazzo <[email protected]> Co-authored-by: Guilherme Gazzo <[email protected]> Co-authored-by: Claude Opus 4.6 <[email protected]>
We identified that all current validators in the system were allowing data coercion, which caused inconsistencies, especially in the OpenAPI. In some cases, coercion was being accepted, while in others, explicit tests rejected it. This ambiguity caused confusion, both in the documentation and in the code behavior. Since coercion is necessary for GET parameters (HTTP), as everything comes as a string, we created a new validator instance, specifically for these cases. In all other flows, we do not accept coercion and keep the types explicitly declared, ensuring greater predictability and consistency.
📱 Kick off Copilot coding agent tasks wherever you are with GitHub Mobile, available on iOS and Android.
Task: ARCH-2057