Skip to content

chore(api): convert user apis#39843

Merged
ggazzo merged 27 commits intochore/apisfrom
chore/users-endpoint-missing-tests
Mar 25, 2026
Merged

chore(api): convert user apis#39843
ggazzo merged 27 commits intochore/apisfrom
chore/users-endpoint-missing-tests

Conversation

@ggazzo
Copy link
Copy Markdown
Member

@ggazzo ggazzo commented Mar 24, 2026

…ge parameter

Proposed changes (including videos or screenshots)

Issue(s)

Steps to test or reproduce

Further comments

…ge parameter

Summary by CodeRabbit

  • Refactor

    • Restructured user API endpoints with standardized validation and response schemas for improved consistency.
  • Bug Fixes

    • Enhanced error handling and request validation across user endpoints; invalid requests now return clearer error messages with proper HTTP status codes.
  • Documentation

    • Added post-migration cleanup checklist for API endpoint improvements and best practices.
  • Tests

    • Expanded end-to-end test coverage for user endpoints, including authentication and validation scenarios.

@dionisio-bot
Copy link
Copy Markdown
Contributor

dionisio-bot bot commented Mar 24, 2026

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

  • This PR is missing the 'stat: QA assured' label
  • This PR is missing the required milestone or project

Please fix the issues and try again

If you have any trouble, please check the PR guidelines

@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Mar 24, 2026

⚠️ No Changeset found

Latest commit: 7971cbb

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

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 24, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 81930348-f915-4553-aaed-ee9754f18b90

📥 Commits

Reviewing files that changed from the base of the PR and between 932aa61 and 7971cbb.

📒 Files selected for processing (13)
  • apps/meteor/app/api/server/definition.ts
  • apps/meteor/app/api/server/lib/users.ts
  • apps/meteor/app/api/server/v1/users.ts
  • apps/meteor/tests/end-to-end/api/users.ts
  • docs/api-endpoint-migration.md
  • packages/rest-typings/src/index.ts
  • packages/rest-typings/src/v1/Ajv.ts
  • packages/rest-typings/src/v1/users/UsersGetAvatarParamsGET.ts
  • packages/rest-typings/src/v1/users/UsersGetPresenceParamsGET.ts
  • packages/rest-typings/src/v1/users/UsersGetStatusParamsGET.ts
  • packages/rest-typings/src/v1/users/UsersListParamsGET.ts
  • packages/rest-typings/src/v1/users/UsersPresenceParamsGET.ts
  • packages/rest-typings/src/v1/users/UsersRequestDataDownloadParamsGET.ts

Walkthrough

The PR refactors user API routes to employ explicit schema validation through post/get handlers with AJV-compiled validators, extends API context types with connection and two-factor metadata, makes parameter properties optional in user query helpers, adds comprehensive E2E test coverage for validation scenarios, and introduces new parameter validator modules for users endpoints.

Changes

Cohort / File(s) Summary
API Context & Type Definitions
apps/meteor/app/api/server/definition.ts
Extended TypedThis context type with queryOperations, queryFields, connection object (token, id, close, clientAddress, httpHeaders), and twoFactorChecked boolean properties.
Users API Route Migration
apps/meteor/app/api/server/v1/users.ts
Major refactor converting routes from addRoute with inline handlers to API.v1.post/get with explicit body/query schemas and response metadata. Added AJV-compiled response schemas (voidSuccessResponse, userObjectResponse), registered new presence/status endpoints, enhanced validation across users.delete, users.logout, users.setStatus, and others. Updated error handling with explicit 403 status and modified runtime details for IP audit logging and status lookups.
Users Query Support
apps/meteor/app/api/server/lib/users.ts
Updated FindPaginatedUsersByStatusProps to make status, roles, searchTerm, hasLoggedIn, and type optional with conditional handling logic.
Parameter Validators
packages/rest-typings/src/v1/users/UsersGetAvatarParamsGET.ts, UsersGetPresenceParamsGET.ts, UsersGetStatusParamsGET.ts, UsersListParamsGET.ts, UsersPresenceParamsGET.ts, UsersRequestDataDownloadParamsGET.ts
Added six new validator modules defining query parameter types with AJV-compiled schema validators. Each module constrains optional query fields (userId, username, user, from, ids, fullExport, fields, query) with JSON schema enforcement and disallows additional properties.
Error Response Type Enhancement
packages/rest-typings/src/v1/Ajv.ts
Widened BadRequestErrorResponse type error field from string to unknown and expanded details to accept `string
Type Exports
packages/rest-typings/src/index.ts
Added six new re-exports from ./v1/users: UsersGetAvatarParamsGET, UsersListParamsGET, UsersPresenceParamsGET, UsersRequestDataDownloadParamsGET, UsersGetPresenceParamsGET, UsersGetStatusParamsGET.
End-to-End Tests
apps/meteor/tests/end-to-end/api/users.ts
Added extensive E2E test assertions covering missing-authentication (HTTP 401), invalid request validation (HTTP 400), empty/invalid request bodies, and updated error message assertions. Adjusted personal access token test data by removing loginToken from requests.
Migration Documentation
docs/api-endpoint-migration.md
Added post-migration cleanup checklist covering schema reuse, tightening schema definitions, setting authRequired: false for public endpoints, adding OpenAPI tags, creating changesets for errorType changes, and verifying validator and test coverage.

Sequence Diagram(s)

sequenceDiagram
    participant Client as Client
    participant API as API.v1.post/get
    participant Validator as AJV Validator
    participant Handler as Route Handler
    participant Response as Response Builder

    Client->>API: POST/GET request with body/query
    API->>Validator: Validate against compiled schema
    
    alt Validation Success
        Validator-->>API: ✓ Validation passed
        API->>Handler: Execute handler with validated params
        Handler->>Response: Generate success response
        Response-->>API: Apply response schema
        API-->>Client: 200 with typed response
    else Validation Failed
        Validator-->>API: ✗ Validation error
        API-->>Client: 400 BadRequestErrorResponse
    end
    
    alt Authorization Check
        Handler->>Handler: Check connection.token
        alt Authorized
            Handler->>Handler: Process request
        else Forbidden
            Handler-->>API: 403 response
            API-->>Client: 403 with error details
        end
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Suggested labels

type: chore


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.

@ggazzo ggazzo force-pushed the chore/users-endpoint-missing-tests branch from 95df55e to 6bea966 Compare March 24, 2026 18:10
@codecov
Copy link
Copy Markdown

codecov bot commented Mar 24, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 70.56%. Comparing base (932aa61) to head (7971cbb).
⚠️ Report is 1 commits behind head on chore/apis.

Additional details and impacted files

Impacted file tree graph

@@              Coverage Diff               @@
##           chore/apis   #39843      +/-   ##
==============================================
- Coverage       70.57%   70.56%   -0.01%     
==============================================
  Files            3256     3256              
  Lines          115791   115791              
  Branches        21039    21074      +35     
==============================================
- Hits            81716    81710       -6     
- Misses          32009    32020      +11     
+ Partials         2066     2061       -5     
Flag Coverage Δ
e2e 60.41% <ø> (-0.02%) ⬇️
e2e-api 48.09% <ø> (+0.01%) ⬆️
unit 71.12% <ø> (-0.01%) ⬇️

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

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

ggazzo and others added 22 commits March 24, 2026 20:07
…ernameSuggestion

- Add test for forgotPassword with empty body expecting 400 with error-invalid-params
- Add test for getUsernameSuggestion without auth expecting 401

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Migrate the following endpoints from addRoute to TypedAction:
- users.deleteOwnAccount
- users.sendWelcomeEmail
- users.getPreferences
- users.2fa.enableEmail
- users.2fa.disableEmail
- users.2fa.sendEmailCode
- users.sendConfirmationEmail
- users.removeOtherTokens
- users.resetE2EKey
- users.resetTOTP

All endpoints now have response schemas, use body/query instead of
validateParams, and include proper error response validators.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Migrate the following endpoints from addRoute to TypedAction:
- users.delete
- users.deactivateIdle
- users.resetAvatar
- users.checkUsernameAvailability
- users.generatePersonalAccessToken
- users.regeneratePersonalAccessToken
- users.getPersonalAccessTokens
- users.removePersonalAccessToken
- users.requestDataDownload
- users.logoutOtherClients
- users.listTeams
- users.logout

All endpoints now have strict response schemas, body/query validators,
and proper error response validators. Shared schemas extracted for
reuse (tokenNameBodySchema, tokenResponseSchema).

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Fixes temporal dead zone error where voidSuccessResponse was used
in users.resetAvatar and users.removePersonalAccessToken before
its const declaration.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
…edAction

Migrate the final 16 endpoints from addRoute to TypedAction:
- users.getAvatar, users.update, users.updateOwnBasicInfo
- users.setPreferences, users.setAvatar, users.create
- users.setActiveStatus, users.info, users.list
- users.listByStatus, users.register, users.presence
- users.autocomplete, users.getPresence, users.setStatus
- users.getStatus

users.ts is now fully migrated (40/40 endpoints). Shared schemas
extracted: userObjectResponse, statusType. All endpoints have
response schemas and proper body/query validators.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
After migrating to TypedAction with body/query validators, the error
type changes from 'invalid-params' to 'error-invalid-params'.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
…red fields

- Move voidSuccessResponse declaration before first usage (fixes TDZ crash)
- Add 403 validateForbiddenErrorResponse to endpoints using forbidden():
  setAvatar, list, listByStatus, logout, setStatus
- Add authToken to required array in createToken response schema
- Import validateForbiddenErrorResponse

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Document pending improvements for after endpoint migration:
shared schema extraction, relaxed schema strengthening, explicit
authRequired, OpenAPI tags, changesets, and test coverage.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Add tests for endpoints that had no e2e coverage after migration:
- users.createToken: 401 + 400 (missing required fields)
- users.resetE2EKey: 401 + 400 (invalid properties)
- users.resetTOTP: 401 + 400 (invalid properties)
- users.2fa.enableEmail: 401
- users.2fa.disableEmail: 401
- users.2fa.sendEmailCode: 400 (missing emailOrUsername)

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Add 24 unauthorized (401) tests and 11 invalid-params (400) tests
across existing describe blocks for migrated user endpoints.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Create AJV query validators for endpoints that read from queryParams
but had no query schema defined:
- UsersGetAvatarParamsGET
- UsersListParamsGET
- UsersPresenceParamsGET
- UsersRequestDataDownloadParamsGET
- UsersGetPresenceParamsGET
- UsersGetStatusParamsGET

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
These properties were available in SharedOptions and ActionThis but
missing from TypedThis, causing TS errors when migrated endpoints
accessed this.queryOperations.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
These properties were available in ActionThis but missing from
TypedThis, causing TS errors when migrated endpoints like
updateOwnBasicInfo called executeSaveUserProfile.call(this).

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
The 307 redirect return type is not supported by TypedAction's
Results type. Reverting to addRoute until the framework supports
custom status codes.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
…onse error type

- Add failure(result: unknown) overload to APIClass for catch blocks
- Change BadRequestErrorResponse.error from string to unknown to
  support API.v1.failure(e) with unknown error values

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
- W1: Remove unused isUsersGetAvatarParamsGET import
- W2: Add comments explaining relaxed schemas (user object, preferences,
  exportOperation, array items) that vary by projection/permissions
- W4: Add anyOf required constraint to users.delete body schema
  requiring at least one of userId, username, or user
- W5: Make FindPaginatedUsersByStatusProps fields optional to match
  query params and remove unnecessary type casts

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
- Adjusted indentation for 'users' and 'preferences' properties in various API response schemas to ensure consistent formatting.
- Updated the condition in the action function to correctly check user parameters.
…amsGET

AJV 2020 does not allow 'nullable' without 'type'. The 'ids' field
uses anyOf which has no top-level type, so nullable is invalid.
The field is already optional via the TypeScript type.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
The unknown overload was intercepting failure('string') calls before
the generic failure<T>. Removing it works because the generic already
accepts unknown and BadRequestErrorResponse.error is now typed as
unknown.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
…ions

- Revert users.list to addRoute: accepts arbitrary query filter fields
  that cannot be statically defined in a schema
- Revert 3 POST endpoint test assertions back to 'invalid-params':
  POST body validation keeps the original errorType, only GET query
  validation changes to 'error-invalid-params'
- Remove unused isUsersListParamsGET import

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
@ggazzo ggazzo force-pushed the chore/users-endpoint-missing-tests branch from 01b6943 to 39c51ed Compare March 24, 2026 23:08
ggazzo and others added 2 commits March 24, 2026 21:08
Password policy errors return details as an array of objects, but
the schema only accepted string or object. Add array as a valid type.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
ggazzo and others added 3 commits March 24, 2026 21:44
…response

The handler was passing the MongoDB UpdateResult directly to
API.v1.success(), exposing internal fields (acknowledged, modifiedCount,
etc.) and failing response validation with additionalProperties: false.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
- Changed errorType assertion from 'error-invalid-status' to 'invalid-params' for consistency in error handling.
- Updated error message to remove redundant information while maintaining clarity.
- Marked the '/users.removeOtherTokens' test suite as exclusive for focused testing.
AJV body validator now rejects invalid status values before the
handler runs, so the error message is from AJV ('must be equal to
one of the allowed values') not from the handler.
@ggazzo ggazzo force-pushed the chore/users-endpoint-missing-tests branch from 2ba4258 to 7971cbb Compare March 25, 2026 01:27
@ggazzo ggazzo marked this pull request as ready for review March 25, 2026 01:47
@ggazzo ggazzo requested review from a team as code owners March 25, 2026 01:47
@ggazzo ggazzo merged commit 85d0087 into chore/apis Mar 25, 2026
42 of 43 checks passed
@ggazzo ggazzo deleted the chore/users-endpoint-missing-tests branch March 25, 2026 01:47
Copy link
Copy Markdown
Contributor

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

Choose a reason for hiding this comment

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

3 issues found across 13 files

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="docs/api-endpoint-migration.md">

<violation number="1" location="docs/api-endpoint-migration.md:770">
P2: The new paginated response example uses `{ type: 'array' }` without `items`, which weakens runtime validation and contradicts the checklist guidance in the same section.</violation>
</file>

<file name="packages/rest-typings/src/v1/users/UsersPresenceParamsGET.ts">

<violation number="1" location="packages/rest-typings/src/v1/users/UsersPresenceParamsGET.ts:11">
P2: `from` is typed as `string | undefined` but the schema allows `null`, creating a type/validation mismatch in the compiled type guard.</violation>
</file>

<file name="packages/rest-typings/src/v1/Ajv.ts">

<violation number="1" location="packages/rest-typings/src/v1/Ajv.ts:47">
P2: `error` type was widened to `unknown`, but the AJV schema still restricts it to `string`, causing inconsistent compile-time vs runtime validation.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

const paginatedUsersResponse = ajv.compile<{ users: IUser[]; count: number; offset: number; total: number }>({
type: 'object',
properties: {
users: { type: 'array' },
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot Mar 25, 2026

Choose a reason for hiding this comment

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

P2: The new paginated response example uses { type: 'array' } without items, which weakens runtime validation and contradicts the checklist guidance in the same section.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At docs/api-endpoint-migration.md, line 770:

<comment>The new paginated response example uses `{ type: 'array' }` without `items`, which weakens runtime validation and contradicts the checklist guidance in the same section.</comment>

<file context>
@@ -745,3 +745,95 @@ Weak types detected:
+const paginatedUsersResponse = ajv.compile<{ users: IUser[]; count: number; offset: number; total: number }>({
+  type: 'object',
+  properties: {
+    users: { type: 'array' },
+    count: { type: 'number' },
+    offset: { type: 'number' },
</file context>
Suggested change
users: { type: 'array' },
users: { type: 'array', items: { $ref: '#/components/schemas/IUser' } },
Fix with Cubic

const UsersPresenceParamsGetSchema = {
type: 'object',
properties: {
from: { type: 'string', nullable: true },
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot Mar 25, 2026

Choose a reason for hiding this comment

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

P2: from is typed as string | undefined but the schema allows null, creating a type/validation mismatch in the compiled type guard.

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/users/UsersPresenceParamsGET.ts, line 11:

<comment>`from` is typed as `string | undefined` but the schema allows `null`, creating a type/validation mismatch in the compiled type guard.</comment>

<file context>
@@ -0,0 +1,19 @@
+const UsersPresenceParamsGetSchema = {
+	type: 'object',
+	properties: {
+		from: { type: 'string', nullable: true },
+		ids: {
+			anyOf: [{ type: 'string' }, { type: 'array', items: { type: 'string' } }],
</file context>
Fix with Cubic

type BadRequestErrorResponse = {
success: false;
error?: string;
error?: unknown;
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot Mar 25, 2026

Choose a reason for hiding this comment

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

P2: error type was widened to unknown, but the AJV schema still restricts it to string, causing inconsistent compile-time vs runtime validation.

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

<comment>`error` type was widened to `unknown`, but the AJV schema still restricts it to `string`, causing inconsistent compile-time vs runtime validation.</comment>

<file context>
@@ -44,10 +44,10 @@ export { ajv, ajvQuery };
 type BadRequestErrorResponse = {
 	success: false;
-	error?: string;
+	error?: unknown;
 	errorType?: string;
 	stack?: string;
</file context>
Suggested change
error?: unknown;
error?: string;
Fix with Cubic

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant