mirror of
https://github.com/twentyhq/twenty.git
synced 2026-05-19 05:53:23 -04:00
## Summary Aligns **workspace member** editing and **onboarding** with how the product is actually used: profile and other “settings” fields go through **`updateWorkspaceMemberSettings`**, while **`/graphql`** record APIs follow **object-level** permissions for the `workspaceMember` object. ## Product behaviour ### Completing “Create profile” onboarding Users who must create a profile (empty name at sign-up) get `ONBOARDING_CREATE_PROFILE_PENDING` set. The onboarding UI saves the name with **`updateWorkspaceMemberSettings`**, not with a workspace record **`updateOne`**. **Before:** The server only cleared the pending flag on **`workspaceMember.updateOne`**, so the flag could stay set and onboarding appeared stuck. **After:** Clearing the profile step runs when **`updateWorkspaceMemberSettings`** persists an update that includes a **name** (same rules as before: non-empty name parts). Onboarding can advance normally after **Continue** on Create profile. ### Two ways to change workspace member data | Path | Typical use | Who can change what | |------|----------------|---------------------| | **`updateWorkspaceMemberSettings`** (metadata API) | Standard member fields the app treats as “my profile / preferences” (name, avatar-related settings, locale, time zone, etc.) | **Always** your **own** workspace member. Changing **another** member still requires **Workspace members** in role settings (`WORKSPACE_MEMBERS`). Custom fields are **not** allowed on this endpoint (unchanged). | | **`/graphql`** record mutations on **`workspaceMember`** | Custom fields, integrations, anything that goes through the generic record API | **`WorkspaceMember`** is special-cased in permissions: **read** stays **on** for everyone, but **update / create / delete** require **`WORKSPACE_MEMBERS`**, including updating **your own** row via `/graphql`. So a **Member** without that permission cannot fix their name through **`updateWorkspaceMember`**; they use **Settings** / **`updateWorkspaceMemberSettings`** instead. | This matches **`WorkspaceRolesPermissionsCacheService`**: for the workspace member object, `canReadObjectRecords` is always true; `canUpdateObjectRecords` (and delete-related flags) follow **`WORKSPACE_MEMBERS`**. ### Hooks and delete side-effects - Removed **`workspaceMember.updateOne`** pre-query hook and **`WorkspaceMemberPreQueryHookService`**: they duplicated the same rules the permission cache already enforces for `/graphql`. - **`WorkspaceMember.deleteOne`** pre-hook still tells users to remove members via the dedicated flow; the post-hook only runs the **`deleteUserWorkspace`** side-effect when a member row is actually removed—**no** extra settings-permission check there, since only callers that already passed **object** delete permission can remove the row. ## Tests - **`workspace-members.integration-spec.ts`**: clarifies and extends coverage so **`/graphql`** **`updateOne`** is denied for **own** record on a **standard** name field and on a **custom** field when the role lacks **`WORKSPACE_MEMBERS`**. ## Implementation notes - **`OnboardingService.completeOnboardingProfileStepIfNameProvided`** centralises the “clear profile pending if name present” logic; **`UserResolver.updateWorkspaceMemberSettings`** calls it after save, using the typed update payload’s **`name`** (no cast). - **`UserWorkspaceService.updateUserWorkspaceLocaleForUserWorkspace`**: drops a redundant **`coreEntityCacheService.invalidate`**; **`updateWorkspaceMemberSettings`** still invalidates the user-workspace cache after the mutation.