-
Notifications
You must be signed in to change notification settings - Fork 13k
feat: Replace old voice extension assingment flow #37245
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
Looks like this PR is ready to merge! 🎉 |
🦋 Changeset detectedLatest commit: cee38de 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 |
WalkthroughReplaces modal-based VoIP extension assignment with an inline Voice_call_extension input across admin/user forms; propagates Changes
Sequence Diagram(s)sequenceDiagram
participant Admin
participant UI as AdminUserForm (client)
participant API as REST API (/v1/users.update)
participant Server as saveUser & validateUserEditing
rect rgba(46,125,50,0.06)
Note over Admin,UI: Inline extension edit flow
Admin->>UI: Open edit form (canManageVoipExtension)
UI-->>Admin: Show Voice_call_extension input
Admin->>UI: Enter/modify extension
Admin->>UI: Click Save
UI->>API: POST /v1/users.update (payload includes freeSwitchExtension)
API->>Server: validateUserEditing -> canEditExtension
alt allowed
Server->>API: validation OK
API->>Server: saveUser (persist freeSwitchExtension)
Server-->>API: Success
API-->>UI: 200 OK
UI-->>Admin: Success
else denied / conflict
Server-->>API: MeteorError (action not allowed / extension in use)
API-->>UI: Error
UI-->>Admin: Error
end
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 inconclusive)
✅ Passed checks (4 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro Disabled knowledge base sources:
📒 Files selected for processing (1)
💤 Files with no reviewable changes (1)
⏰ 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). (2)
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. Comment |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## develop #37245 +/- ##
===========================================
- Coverage 67.69% 67.61% -0.09%
===========================================
Files 3342 3338 -4
Lines 114030 113711 -319
Branches 20678 20656 -22
===========================================
- Hits 77197 76889 -308
+ Misses 34158 34140 -18
- Partials 2675 2682 +7
Flags with carried forward coverage won't be shown. Click here to find out more. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 6
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/meteor/client/views/admin/users/AdminUserForm.tsx (1)
175-183: Do not send freeSwitchExtension when empty or when user lacks permissionCurrently the payload always includes freeSwitchExtension (possibly ''), which may clear the value or violate the server regex. Strip it when the field is not visible (no permission/setting) or when it’s empty/whitespace.
const handleSaveUser = useEffectEvent(async (userFormPayload: UserFormProps) => { - const { avatar, passwordConfirmation, ...userFormData } = userFormPayload; + const { avatar, passwordConfirmation, ...userFormData } = userFormPayload; + + // sanitize optional voice extension + if (!canManageVoipExtension || !userFormData.freeSwitchExtension?.trim()) { + // eslint-disable-next-line @typescript-eslint/no-dynamic-delete + delete (userFormData as Partial<UserFormProps>).freeSwitchExtension; + }
🧹 Nitpick comments (9)
apps/meteor/app/lib/server/functions/saveUser/validateUserEditing.ts (1)
1-1: Optional refactor: Consider breaking down validation complexity.The
eslint-disable complexitydirective suggests this function may benefit from decomposition. While the current implementation is acceptable, consider extracting validation groups into helper functions if complexity increases further.apps/meteor/tests/end-to-end/api/users.ts (6)
581-589: Consolidate test hooks to avoid linter errors and reduce redundancyBiome flags duplicate setup/teardown hooks. You can drop this after() entirely; beforeEach already enforces the desired baseline per test. Alternatively, convert both to beforeEach/afterEach if you want symmetry.
Suggested minimal change: remove the after() here.
- after(async () => { - await updateSetting('VoIP_TeamCollab_Enabled', true); - await updatePermission('manage-voip-extensions', ['admin']); - });
592-614: Make identifiers unique to avoid test pollution across runsHardcoding email/username can collide on retries or parallelization. Generate unique values per run.
- email: 'success_extension_user@rocket.chat', - name: 'success_extension_user', - username: 'success_extension_user', + email: `success_extension_user_${Date.now()}@rocket.chat`, + name: `success_extension_user_${Date.now()}`, + username: `success_extension_user_${Date.now()}`,
616-619: Minor: prefer idiomatic null-checkuser! is unnecessary; just check truthiness.
- if (user!) { + if (user) { await deleteUser(user); }
621-646: Add a regex-negative test to cover invalid extensionsThe issue requires validating extensions against a RegEx. Please add a failing case (e.g., non‑digits or wrong length) to assert 400 with the expected errorType.
I can draft an additional it(...) that posts freeSwitchExtension: '12ab' and expects errorType like 'invalid-params' or 'error-extension-invalid' (confirm the exact error key).
2417-2434: Avoid duplicate hooks and tighten isolationThis describe has before, beforeEach, and after. The linter complains about duplicate hooks across the suite. Also, using beforeEach to set settings/permissions is enough; consider removing the after() here, and if you need a fresh user per test, switch to beforeEach to create/delete it per test for isolation.
- let user: TestUser<IUser>; - - before(async () => { - user = await createUser(); - }); - - after(async () => { - await Promise.all([ - deleteUser(user), - updateSetting('VoIP_TeamCollab_Enabled', true), - updatePermission('manage-voip-extensions', ['admin']), - ]); - }); + let user: TestUser<IUser>; + beforeEach(async () => { + user = await createUser(); + }); + afterEach(async () => { + await deleteUser(user); + });Then keep the existing beforeEach that sets permissions/settings.
2484-2500: Assert the exact error payload for permission and setting checksAdd explicit expectations for errorType to strengthen assertions and maintain consistency with the create‑flow tests.
- .expect(400) + .expect(400) .expect((res) => { - expect(res.body).to.have.property('success', false); + expect(res.body).to.have.property('success', false); + expect(res.body).to.have.property('errorType', 'error-action-not-allowed'); });Similarly for the permission case above, assert errorType 'error-action-not-allowed'.
apps/meteor/client/views/admin/users/AdminUserForm.tsx (2)
58-61: Remove duplicated type field; rely on REST typingsfreeSwitchExtension is already in UserCreateParamsPOST. Duplicating it in the intersection risks drift.
-export type UserFormProps = Omit< - UserCreateParamsPOST & { avatar: AvatarObject; passwordConfirmation: string; freeSwitchExtension?: string }, - 'fields' ->; +export type UserFormProps = Omit<UserCreateParamsPOST & { avatar: AvatarObject; passwordConfirmation: string }, 'fields'>;
345-356: Client-side input hints for better UX (numeric only, optional pattern)Add inputMode='numeric' and optionally a basic pattern to guide users. Keep server as source of truth.
- render={({ field }) => <TextInput {...field} id={voiceExtensionId} flexGrow={1} />} + render={({ field }) => ( + <TextInput + {...field} + id={voiceExtensionId} + flexGrow={1} + inputMode='numeric' + autoComplete='off' + /> + )}If there is a configurable regex in settings, consider rendering a FieldHint with that pattern.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Jira integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (10)
apps/meteor/app/api/server/v1/users.ts(2 hunks)apps/meteor/app/lib/server/functions/saveUser/saveNewUser.ts(1 hunks)apps/meteor/app/lib/server/functions/saveUser/saveUser.ts(2 hunks)apps/meteor/app/lib/server/functions/saveUser/validateUserEditing.ts(3 hunks)apps/meteor/client/views/admin/users/AdminUserForm.tsx(6 hunks)apps/meteor/client/views/admin/users/UsersTable/UsersTable.spec.tsx(1 hunks)apps/meteor/tests/data/users.helper.ts(1 hunks)apps/meteor/tests/end-to-end/api/users.ts(2 hunks)packages/rest-typings/src/v1/users/UserCreateParamsPOST.ts(2 hunks)packages/rest-typings/src/v1/users/UsersUpdateParamsPOST.ts(2 hunks)
🧰 Additional context used
🧬 Code graph analysis (3)
apps/meteor/tests/end-to-end/api/users.ts (3)
apps/meteor/tests/data/users.helper.ts (3)
TestUser(8-8)deleteUser(55-62)createUser(10-37)packages/core-typings/src/IUser.ts (1)
IUser(186-255)apps/meteor/tests/data/api-data.ts (2)
request(10-10)credentials(39-42)
apps/meteor/client/views/admin/users/AdminUserForm.tsx (3)
packages/rest-typings/src/v1/users/UserCreateParamsPOST.ts (1)
UserCreateParamsPOST(8-28)packages/core-typings/src/IUser.ts (1)
AvatarObject(317-317)apps/meteor/client/views/admin/users/useVoipExtensionPermission.tsx (1)
useVoipExtensionPermission(3-8)
apps/meteor/app/api/server/v1/users.ts (1)
apps/meteor/app/lib/server/functions/saveUser/validateUserEditing.ts (1)
canEditExtension(15-29)
🪛 Biome (2.1.2)
apps/meteor/tests/end-to-end/api/users.ts
[error] 586-589: Disallow duplicate setup and teardown hooks.
Disallow after duplicacy inside the describe function.
(lint/suspicious/noDuplicateTestHooks)
[error] 2420-2422: Disallow duplicate setup and teardown hooks.
Disallow before duplicacy inside the describe function.
(lint/suspicious/noDuplicateTestHooks)
[error] 2424-2430: Disallow duplicate setup and teardown hooks.
Disallow after duplicacy inside the describe function.
(lint/suspicious/noDuplicateTestHooks)
⏰ 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). (3)
- GitHub Check: 📦 Build Packages
- GitHub Check: CodeQL-Build
- GitHub Check: CodeQL-Build
🔇 Additional comments (9)
apps/meteor/client/views/admin/users/UsersTable/UsersTable.spec.tsx (1)
1-21: LGTM! Test simplification aligns with UI refactoring.The removal of VoIP-specific tests and retention of story-based accessibility tests is appropriate given the broader refactoring that moves extension assignment to the user edit form.
apps/meteor/tests/data/users.helper.ts (1)
21-21: LGTM! Test helper properly extended.The addition of the optional
freeSwitchExtensionparameter is clean and will be properly included in the request payload via the spread operator.apps/meteor/app/lib/server/functions/saveUser/saveNewUser.ts (1)
50-52: LGTM! Extension assignment logic is sound.The check properly ensures that
freeSwitchExtensionis only set when it's a non-empty string, maintaining data consistency.apps/meteor/app/api/server/v1/users.ts (2)
59-59: Import added for extension validation.The
canEditExtensionfunction is properly imported for use in the user creation flow.
329-331: The race condition risk is already mitigated by an existing database unique index.The verification confirms that a unique index on
freeSwitchExtensionalready exists in the Users model schema (packages/models/src/models/Users.ts:96), configured as{ key: { freeSwitchExtension: 1 }, sparse: true, unique: true }. This provides atomic enforcement at the database level, which is the preferred mitigation option mentioned in the review comment. Even if a concurrent request creates a gap between thecanEditExtensioncheck and the save operation, the database constraint will prevent duplicate extensions from being persisted.No code changes are required.
apps/meteor/app/lib/server/functions/saveUser/saveUser.ts (2)
57-57: LGTM! Type definition properly extended.The addition of
freeSwitchExtensiontoSaveUserDatais clean and consistent with other optional fields.
167-173: LGTM! Extension update logic handles all cases correctly.The logic properly:
- Only processes changes when the value differs from the current extension
- Unsets the field when an empty/whitespace value is provided
- Sets the new value for valid extensions
- Relies on
validateUserEditing(line 105) for permission and uniqueness checks within the transactionapps/meteor/app/lib/server/functions/saveUser/validateUserEditing.ts (2)
15-29: LGTM! Extension validation logic is well-structured.The
canEditExtensionfunction properly validates:
- VoIP feature enablement
- User permissions
- Extension uniqueness
The uniqueness check at line 24 will throw an error if the extension is already assigned, preventing duplicate assignments.
However, runtime uniqueness checks are vulnerable to race conditions without database-level enforcement. Run the verification script from the comment on
users.ts:329-331to confirm a unique index exists onfreeSwitchExtension.
118-126: LGTM! Proper validation for extension editing.The validation correctly:
- Uses
isEditingFieldto detect actual changes (line 119), preventing unnecessary validation when a user retains their current extension- Calls
canEditExtensionwith the new extension value for permission and uniqueness checks
Proposed changes (including videos or screenshots)
Extension assignment should now be available in the user edit form.
Issue(s)
VGA-4
Steps to test or reproduce
Further comments
Summary by CodeRabbit
New Features
Refactor
Tests
Chores