f86bf637d1097a75d339222c82f67c86cded65e7
15 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
3ee93b5ec9 |
feat(server): add isSystemSideEffect & merge createOneObject/createOneField side-effect migrations (#21673)
## Context When an object is created via the metadata API, `createOneObject` creates its side-effect entities (INDEX view + viewFields, indexes, navigation menu item, "go to" command menu item, record-page fields view, page layout/tabs/widgets) across **three separate `validateBuildAndRunWorkspaceMigration` calls**, purely because the protection behavior (mutations → overrides, delete → deactivate, reset → reactivate) was keyed on *"owned by the standard app"*, forcing the side effects into batches with different application owners. This misrepresents ownership and breaks atomicity. This PR separates two orthogonal concepts: - **Ownership** (`applicationId`), the true owner: the caller's application (the workspace custom app today, 3rd-party apps later). - **Protection** (`isSystemSideEffect`), the row was generated by the system, so user mutations route to overrides, deletion becomes deactivation, and reset restores defaults. Once side effects are re-owned to the caller, the old `applicationId === standardApp` check can no longer tell an original side-effect row from a user-added one so a dedicated `isSystemSideEffect` flag carries the protection instead. This is **PR 1 of 2** (forward-only). It makes newly created objects and fields correct; existing workspaces are handled by a follow-up backfill (see *Out of scope*). ## What this PR does - **`isSystemSideEffect` column** on the 8 affected entities (`view`, `viewField`, `indexMetadata`, `commandMenuItem`, `pageLayout`, `pageLayoutTab`, `pageLayoutWidget`, `fieldMetadata`), with `@WasIntroducedInUpgrade` + an entry in the flat-entity property configuration (`toCompare: true`, read-only). - **Single atomic migration in `createOneObject`**: the three `validateBuildAndRunWorkspaceMigration` calls are merged into one, owned by the caller (`resolvedOwnerFlatApplication`) and the record-page view/fields, page layout, and navigation command item are re-owned to the caller and flagged `isSystemSideEffect: true`. `buildNavigationFlatCommandMenuItem` is parameterized with `applicationUniversalIdentifier` (no longer hardcoded to the standard app). - **Field-creation side effects** (`createManyFields`/`createOneField` already run as a single caller-owned migration, so no re-ownership/merge was needed): the auto-created viewField is flagged `isSystemSideEffect: true`, and a new field now also propagates to the object's **INDEX/table view** (added there as a **hidden** column, `isVisible: false`) in addition to the record-page FIELDS widget. The INDEX view is targeted directly by `key = INDEX` (it is not a page-layout widget), de-duplicated per `(viewId, fieldMetadataUniversalIdentifier)` to respect the per-view unique index. The unique-field index is likewise flagged the inverse relation field stays unflagged (`isSystem: false`). - **Protection predicate** extended: `isCallerOverridingEntity` and the removal/reset split strategies now treat `isSystemSideEffect` rows as protected even when caller-owned (route to overrides / deactivate / reset) and the page-layout-reset guards allow resetting flagged entities. - **Standard compute maps** set the flag consistently so a re-sync produces no diff (standard-object side effects stay `false`; per-object nav command items and custom-object base fields are `true`). - **Read-only GraphQL exposure** of `isSystemSideEffect` on the view / view-field / page-layout / tab / widget / command-menu-item DTOs (not exposed on create/update inputs). => Todo: needs to take this new flag into account. This is fine for now because isSystem remains on object/field. - **Fast instance command** (`2-14`) adding the 8 columns (`NOT NULL DEFAULT false`). ## Scope decisions - **`pageLayout` is not an `OverridableEntity`**, its own row has nothing user-overridable (all customization lives on tabs/widgets). It's dual-purpose (`RECORD_PAGE` side-effect vs. user `DASHBOARD`), so it gets `isSystemSideEffect` for protection only, no `overrides` jsonb. - **`navigationMenuItem` is out of scope.**: Those are side effects only for the metadata API and not marked as "system" (they can be deleted/updated etc...) - **`viewFieldGroup` is not a side effect**, it's only created via the explicit view-field-group API, never by object/field creation, so it gets no flag. ## Out of scope (follow-ups) **PR 2** — slow per-workspace backfill (re-own + flag existing side effects, recreate missing ones) and deterministic v5 identifiers for base fields / pageLayout / tab. <!-- This is an auto-generated description by cubic. --> <a href="https://cubic.dev/pr/twentyhq/twenty/pull/21673?utm_source=github" target="_blank" rel="noopener noreferrer" data-no-image-dialog="true"><picture><source media="(prefers-color-scheme: dark)" srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img alt="Review in cubic" src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a> <!-- End of auto-generated description by cubic. --> |
||
|
|
f4ead89956 |
refactor(twenty-orm): migrate 23 grandfathered entities to WorkspaceScopedRepository (#20987)
## Summary Follow-up to #20953. Migrates 23 of the 30 entities that were left in `WORKSPACE_SCOPED_EXEMPTIONS` last time, so the lint rule's workspaceId-enforcement default now covers most of the core/metadata schema. ### Migrated (23 entities, 88 files, 22 commits) | Family | Entities | |---|---| | Trivial caches | `NavigationMenuItem`, `Skill`, `DataSource`, `Webhook`, `CommandMenuItem`, `IndexMetadata` | | Views | `View`, `ViewField`, `ViewFieldGroup`, `ViewFilter`, `ViewFilterGroup`, `ViewGroup`, `ViewSort` | | Layouts | `PageLayout`, `PageLayoutTab`, `PageLayoutWidget` | | Roles & permissions | `Role`, `RoleTarget`, `PermissionFlag`, `ObjectPermission`, `FieldPermission`, `RowLevelPermissionPredicate`, `RowLevelPermissionPredicateGroup` | For each entity: swap `@InjectRepository(X)` → `@InjectWorkspaceScopedRepository(X)` (and the field type → `WorkspaceScopedRepository<X>`); rewrite every call site to pass `workspaceId` as the first arg (stripped from `where`/criteria — the wrapper throws if you include it now); register `provideWorkspaceScopedRepository(X)` in every owning NestJS module; update affected spec providers to `getWorkspaceScopedRepositoryToken(X)`. ### Rule update - `ApplicationRegistrationVariableEntity` was misclassified — moved to `STRUCTURAL_EXEMPTIONS` (no `workspaceId` column; it's keyed on `applicationRegistrationId` at the instance level). - 22 of the 23 migrated entities removed from `WORKSPACE_SCOPED_EXEMPTIONS` entirely (zero remaining raw `@InjectRepository` sites). - `RoleTargetEntity` also removed; one call site in `user-workspace.service.ts` keeps a raw injection with an `eslint-disable` + reason because `softRemove(...)` is not on the wrapper API yet (the migration would require threading `workspaceId` through `deleteUserWorkspace`'s three callers). ### Still exempted (7 entities, follow-up PRs) | Entity | Why deferred | |---|---| | `ApplicationEntity` | ~50 sites with several cross-workspace lookups by id (auth, OAuth, file-storage, cleanup) | | `CalendarChannelEntity` / `MessageChannelEntity` | Use `.increment(...)` (not on wrapper) and `repository.manager.transaction(...)` — wrapper needs to grow `.increment` + the transaction sites need `withManager` or dual-inject | | `FieldMetadataEntity` / `ObjectMetadataEntity` | The metadata services `extends TypeOrmQueryService<X>` and `super(rawRepo)` — requires dual-inject or reworking the inheritance | | `KeyValuePairEntity` | Allows `workspaceId: IsNull()` for instance-level config; wrapper rejects null | | `UpgradeMigrationEntity` | Same — instance-level + cross-workspace ledger | ## Test plan - [x] `npx nx typecheck twenty-server` — clean - [x] `npx nx lint twenty-server` — clean (0/0) - [x] All 10 affected unit specs pass (115 tests) — api-key, agent-role, permissions, workspace-roles-permissions-cache, view-filter-group, workflow-version-step-operations, two-factor-authentication (service + resolver), user-workspace, file - [ ] Server integration tests in CI |
||
|
|
6d3ff4c9ce |
Translate standard page layouts (#19890)
## Context Standard page layout tabs, page layout widgets, and view field group titles were hardcoded English in the backend. This PR brings them under the same translation pipeline as views. Notes: Once a standard widget/tab/section title is overriden, the backend returns its value without translation |
||
|
|
47bdcb11d8 |
Move is active to fe (#19649)
## Context Moving isActive filtering to the frontend for page layout tabs and widgets, hiding inactive entities from the UI while keeping them in state for future reactivation Next we will implement deactivated standard tab re-activation during tab creation (cc @Devessier) <img width="234" height="303" alt="📋 Menu (Slots)" src="https://github.com/user-attachments/assets/17a25ac6-55e2-4778-b7f0-e7554ed69704" /> |
||
|
|
238018dc7b |
Reset Tab Page Layout (#19453)
## Context - Add "Reset to default" for page layout tabs and widgets — backend mutation resets overrides, reactivates deactivated children, and deletes custom children - Fix override write bug where mutating an overridable property (e.g. widget title) on a standard-app entity incorrectly overwrote the base column instead of writing to the overrides JSONB — PageLayoutUpdateService now uses resolveFlatEntityOverridableProperties for accurate diffing and routes properties through sanitizeOverridableEntityInput - Deprecate isOverridden - We need more time to think about this feature. Currently this adds too much complexity for a very small benefit https://github.com/user-attachments/assets/a84546c8-1e15-4d9e-a489-0825cf8b8ed2 |
||
|
|
8aa208fc93 |
Fix overridable entities logic for SSE (#19433)
## Context SSE metadata events for overridable entities (viewField, viewFieldGroup, pageLayoutWidget, pageLayoutTab) were sending raw flat entity data without resolving overrides into base properties or filtering isActive: false entities. This caused the frontend to display stale/incorrect values (e.g., a hidden viewField still appearing visible). ## Implementation Add a sanitization step in MetadataEventPublisher.enrichMetadataEventBatch that resolves overrides and converts isActive transitions into the appropriate event types (deactivated -> delete, reactivated -> create), matching what GraphQL resolvers already do |
||
|
|
c1e4756f9c |
Add is active to overridable entities and deactivation logic for page layouts (#19200)
- Replace soft-deletion (deletedAt) with isActive boolean for overridable entities (tabs, widgets, viewFieldGroups, viewFields). Standard entities are deactivated (isActive: false) when removed from update payloads, while custom entities are hard-deleted. - When a viewFieldGroup is deactivated/deleted, its viewFields are reassigned to the next section by position (or null if none remain). - Add isActive: true filters to viewFieldGroup and viewField API queries so deactivated entities are excluded from responses. Next: - fields-widget-upsert.service.ts should be refactored a bit - Add restore logic |
||
|
|
c11e4ece39 |
Fallback to field metadata (#19131)
Rely on the field metadata items to always display all object's fields in the fields widget configuration editor. If fields are missing in the returned view fields, we add the missing fields through object metadata. https://github.com/user-attachments/assets/3c4d45e8-05d0-4943-be4b-bcf1e310155c |
||
|
|
5c745059ad |
refactor: remove "core" naming from views and eliminate converter layer (#18667)
## Summary
- **Remove all "core" prefixes** from the views system — the
metadata-based storage migration is complete, so `CoreView`,
`coreViewsSelector`, `getCoreViews`, etc. are now just `View`,
`viewsSelector`, `getViews`
- **Eliminate the entire converter layer** (15 files, ~850 lines
deleted) — `convertCoreViewToView` and all sub-converters were either
no-ops or trivially adding `__typename` / mapping identical enum values.
Local enums now re-export from generated GraphQL types directly (single
source of truth)
- **Unify `View` and `ViewWithRelations`** into one type —
`ViewWithRelations` is now a type alias for `View`, selectors return
data directly without conversion
### Backend
- Rename `@ObjectType('CoreView')` → `@ObjectType('View')` (and all
sub-entities)
- Rename resolver methods: `getCoreViews` → `getViews`, `createCoreView`
→ `createView`, etc.
- Rename `FIND_ALL_CORE_VIEWS_GRAPHQL_OPERATION` →
`FIND_ALL_VIEWS_GRAPHQL_OPERATION`
### Frontend
- Delete 15 converter files (`convertGqlView*ToView*`,
`convertView*ToGql`, `convertViewWithRelationsToView`)
- Re-export `ViewType`, `ViewKey`, `ViewFilterGroupLogicalOperator` from
generated enums (no more duplicate enum definitions with different
casing)
- Replace `ViewOpenRecordInType` with `ViewOpenRecordIn` from generated
- Remove `__typename` from all local view sub-types
- Remove unused `variant` from `ViewFilter`, make `displayValue` and
`definition` optional
- Rename ~45 GraphQL query/mutation files and all selectors to drop
"core" prefix
- Delete unused `viewsWithRelationsSelector`
|
||
|
|
ba9aa41bba |
refactor: metadata store cleanup, SSE unification, mock metadata loading & login redirect fix (#18651)
## Summary - **SSE unification**: Replaced 11 individual SSE effect components with a single generic `MetadataStoreSSEEffect` - **Metadata store cleanup**: Merged `metadataCollectionHashesState` into `metadataStoreState` (currentCollectionHash / draftCollectionHash per entity), moved `objectMetadataItemsSelector` to `object-metadata` domain, converted `navigationMenuItemsState` to a derived selector - **Naming clarity**: Renamed `isAppMetadataReadyState` → `isMinimalMetadataReadyState`, `MetadataGater` → `MinimalMetadataGater`, `useIsLogged` → `useHasAccessTokenPair`, `patchMetadataStoreFromSSEEvent` now takes named object params - **Mock metadata loading**: Added `generate-navigation-menu-items.ts` script, rewrote `useLoadMockedMinimalMetadata` to load full objects/fields/indexes/views/navItems from generated mock data, enabling proper sign-in background rendering (table columns, view picker, navigation) - **Login/logout transitions**: `MinimalMetadataLoadEffect` manages mocked↔real metadata transitions based on auth state, `MainContextStoreProvider` computes context on auth pages for view picker support - **Login redirect fix**: `handleLoadWorkspaceAfterAuthentication` now re-enables `isAppEffectRedirectEnabled` after `loadCurrentUser()` completes, fixing the blocked post-login navigation - **Dead code removal**: Deleted `useRefreshPageLayouts`, `useApplyPageLayouts`, `useStaleMetadataEntities`, `metadataCollectionHashesState`, and all individual SSE effects ## Test plan - [x] Login from welcome page redirects to companies page - [x] Logout transitions cleanly to mocked metadata on welcome page - [x] Sign-in background shows table columns, view picker, and navigation items - [x] SSE events still update metadata store entries correctly - [x] Navigation menu items persist across page refreshes - [ ] CI: lint, typecheck, tests pass |
||
|
|
48172d60fd |
View field override (#18572)
## Context This PR introduces overrides for view fields which will be useful for page layout FIELDS widgets fields position/groups/visibility override + restore logic. |
||
|
|
0e89c96170 |
feat: add npm and tarball app distribution with upgrade mechanism (#18358)
## Summary - **npm + tarball app distribution**: Apps can be installed from the npm registry (public or private) or uploaded as `.tar.gz` tarballs, with `AppRegistrationSourceType` tracking the origin - **Upgrade mechanism**: `AppUpgradeService` checks for newer versions, supports rollback for npm-sourced apps, and a cron job runs every 6 hours to update `latestAvailableVersion` on registrations - **Security hardening**: Tarball extraction uses path traversal protection, and `enableScripts: false` in `.yarnrc.yml` disables all lifecycle scripts during `yarn install` to prevent RCE - **Frontend**: "Install from npm" and "Upload tarball" modals, upgrade button on app detail page, blue "Update" badge on installed apps table when a newer version is available - **Marketplace catalog sync**: Hourly cron job syncs a hardcoded catalog index into `ApplicationRegistration` entities - **Integration tests**: Coverage for install, upgrade, tarball upload, and catalog sync flows ## Backend changes | Area | Files | |------|-------| | Entity & migration | `ApplicationRegistrationEntity` (sourceType, sourcePackage, latestAvailableVersion), `ApplicationEntity` (applicationRegistrationId), migration | | Services | `AppPackageResolverService`, `ApplicationInstallService`, `AppUpgradeService`, `MarketplaceCatalogSyncService` | | Cron jobs | `MarketplaceCatalogSyncCronJob` (hourly), `AppVersionCheckCronJob` (every 6h) | | REST endpoint | `AppRegistrationUploadController` — tarball upload with secure extraction | | Resolver | `MarketplaceResolver` — simplified `installMarketplaceApp` (removed redundant `sourcePackage` arg) | | Security | `.yarnrc.yml` — `enableScripts: false` to block postinstall RCE | ## Frontend changes | Area | Files | |------|-------| | Modals | `SettingsInstallNpmAppModal`, `SettingsUploadTarballModal`, `SettingsAppModalLayout` | | Hooks | `useUploadAppTarball`, `useInstallMarketplaceApp` (cleaned up) | | Upgrade UI | `SettingsApplicationVersionContainer`, `SettingsApplicationDetailAboutTab` | | Badge | `SettingsApplicationTableRow` — blue "Update" tag, `SettingsApplicationsInstalledTab` — fetches registrations for version comparison | | Styling | Migrated to Linaria (matching main) | ## Test plan - [ ] Install an app from npm via the "Install from npm" modal - [ ] Upload a `.tar.gz` tarball via the "Upload tarball" modal - [ ] Verify upgrade badge appears when `latestAvailableVersion > version` - [ ] Verify upgrade flow from app detail page - [ ] Run integration tests: `app-distribution.integration-spec.ts`, `marketplace-catalog-sync.integration-spec.ts` - [ ] Verify `enableScripts: false` blocks postinstall scripts during yarn install Made with [Cursor](https://cursor.com) |
||
|
|
9a0295fd3d |
feat: add atomic upsertFieldsWidget mutation to replace multiple view field group/field API calls (#18137)
The `useSaveFieldsWidgetGroups` hook was making multiple sequential API
calls per widget (create groups, delete groups, update groups, update
fields) — non-atomic, chatty, and with heavy diff computation on the
frontend.
## Backend
- **New input DTOs**: `UpsertFieldsWidgetInput` /
`UpsertFieldsWidgetGroupInput` / `UpsertFieldsWidgetFieldInput` — caller
passes the full desired state of groups + fields for a widget
- **`FieldsWidgetUpsertService`**: looks up widget → resolves `viewId`,
diffs against existing flat entity maps, builds optimistic group maps so
newly-created groups can be referenced by field updates in the same
pass, then runs creates/updates/deletes in a single
`validateBuildAndRunWorkspaceMigration` call
- **`upsertFieldsWidget` mutation** added to `ViewFieldGroupResolver`;
new `FIELDS_WIDGET_NOT_FOUND` exception code added and wired into the
GraphQL exception filter
## Frontend
- New `UPSERT_FIELDS_WIDGET` GQL document
- `useSaveFieldsWidgetGroups` replaces all per-operation calls with a
single mutation per widget, passing the full draft state — diff logic
moves entirely to the backend
```graphql
mutation UpsertFieldsWidget($input: UpsertFieldsWidgetInput!) {
upsertFieldsWidget(input: $input) {
id
name
position
isVisible
viewId
viewFields { id isVisible position ... }
}
}
```
Widget IDs are collected from both draft **and** persisted state so
widgets whose draft groups were cleared still trigger deletion of their
server-side groups.
<!-- START COPILOT ORIGINAL PROMPT -->
<details>
<summary>Original prompt</summary>
Start implementation
The user has attached the following file paths as relevant context:
- CLAUDE.md
<analysis>
[Chronological Review: The conversation began with the user requesting a
review of how updates on views are stored in the frontend of record page
layouts. The user expressed a desire to migrate heavy computations and
multiple requests from the frontend to the backend. They proposed
creating a custom upsertFieldsWidget mutation to handle updates
atomically. The conversation has focused on this migration strategy and
its implementation.]
[Intent Mapping: The user explicitly requested to "check how we store
updates on views in the frontend of record page layouts" and indicated a
need to "migrate this task to the backend" by creating a custom mutation
for atomic updates.]
[Technical Inventory: The conversation involves the following technical
concepts:
- Frontend and backend architecture for handling page layout updates.
- Custom GraphQL mutation (upsertFieldsWidget) for atomic updates.
- The file in focus is page-layout-update.service.ts, which is likely
responsible for handling updates related to page layouts in the
backend.]
[Code Archaeology: The specific file being worked on is
/Users/devessier/Web/twenty/packages/twenty-server/src/engine/metadata-modules/page-layout/services/page-layout-update.service.ts.
The user is likely looking to modify or enhance this service to
accommodate the new mutation for handling updates.]
[Progress Assessment: The user has identified the need for a backend
migration strategy but has not yet implemented any changes. The focus is
on planning the transition from frontend to backend processing.]
[Context Validation: All critical information for continuation is
captured, including the user's intent to migrate computations to the
backend and the specific file being modified.]
[Recent Commands Analysis: The last agent commands involved checking the
current file context and the user's request to summarize the
conversation. No specific tool results were generated as the focus was
on summarizing the conversation history. The agent was actively working
on summarizing the conversation when the token budget was exceeded,
which was triggered by the user's request for a summary.]
</analysis>
<summary>
1. Conversation Overview:
- Primary Objectives: The user requested to "check how we store updates
on views in the frontend of record page layouts" and expressed a desire
to "migrate this task to the backend" by creating a custom
upsertFieldsWidget mutation for atomic updates.
- Session Context: The conversation has focused on the need to shift
heavy computations from the frontend to the backend, with an emphasis on
implementing a new mutation to streamline updates.
- User Intent Evolution: The user's needs have remained consistent,
focusing on backend migration for efficiency and atomicity in updates.
2. Technical Foundation:
- Core Technology: The conversation involves a backend service likely
built with Node.js and TypeScript, as indicated by the file path.
- Framework/Library: The use of GraphQL for handling mutations is
implied through the mention of a custom mutation.
- Architectural Pattern: The user is looking to implement a
service-oriented architecture where the backend handles complex
computations instead of the frontend.
- Environment Detail: The current working file is located in a server
package of the twenty repository.
3. Codebase Status:
- File Name: page-layout-update.service.ts
- Purpose: This file is responsible for managing updates related to page
layouts in the backend.
- Current State: The user is considering modifications to implement a
new mutation for handling updates.
- Key Code Segments: Specific functions or classes have not been
detailed yet, as the focus is on planning changes.
- Dependencies: This service likely interacts with other components in
the metadata-modules related to page layouts.
4. Problem Resolution:
- Issues Encountered: The current challenge is the inefficiency of
handling updates in the frontend.
- Solutions Implemented: The proposed solution is to create a custom
mutation to handle updates atomically in the backend.
- Debugging Context: No ongoing troubleshooting efforts have been
mentioned yet.
- Lessons Learned: The need for backend processing to improve
performance has been highlighted.
5. Progress Tracking:
- Completed Tasks: No tasks have been completed yet; the user is in the
planning phase.
- Partially Complete Work: The user is preparing to implement a new
mutation for backend updates.
- Validated Outcomes: No features have been confirmed working through
testing at this stage.
6. Active Work State:
- Current Focus: The user is focused on modifying the
page-layout-update.service.ts to implement the new mutation.
- Recent Context: The last few exchanges involved discussing the
migration of update tasks from the frontend to the backend.
- Working Code: No specific code snippets have been modified yet, as the
co...
</details>
<!-- START COPILOT CODING AGENT SUFFIX -->
Created from [VS
Code](https://code.visualstudio.com/docs/copilot/copilot-coding-agent).
<!-- START COPILOT CODING AGENT TIPS -->
---
💡 You can make Copilot smarter by setting up custom instructions,
customizing its development environment and configuring Model Context
Protocol (MCP) servers. Learn more [Copilot coding agent
tips](https://gh.io/copilot-coding-agent-tips) in the docs.
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Devessier <29370468+Devessier@users.noreply.github.com>
Co-authored-by: Baptiste Devessier <baptiste@devessier.fr>
|
||
|
|
2b7b05de2e |
[OBJECT_MANIFEST_BREAKING_CHANGE] Sync returns workspace migration (#17918)
# Introduction In this PR we start returning a workspace migration post sync so it can committed and provided within the tarball ## Universal aggregators utils Created two utils ### deleteUniversalFlatEntityForeignKeyAggregators Used when building a universal create action, a newly created actions should not contain any aggregated foreign key so they won't be codegen in the workspace migration but also they are overriden at uninversal to flat transpilation anw ### resetUniversalFlatEntityForeignKeyAggregators Used before validating a new flat entity creation, some validator will consume the fk aggregator in order to validate integrity, but of optimstically provided it can result to errors. To avoid caller responsability we override them here ## create-field-action refactor Refactored the universal and flat field create action to be following the base actions in order to ease typing Also it was tailored to handle unlimited amount of flat field metadata in the same actions whereas in the reality we were always only sending at max 2 ( for relation fields ) Note: relation field has to be provided at the same as if not optimistic would fail to retrieve circular universal identifiers ## ObjectManifest Now always expect a `labelIdentifierFieldMetadataUniversalIdentifier` ## Integration test Created an integration test that creates an app, sync a first manifest and a second implying update workspace migration action generation |
||
|
|
6aca1dd013 |
Introducing view field group syncable entity (#17867)
## Context Introduces a new viewFieldGroup entity that allows grouping view fields into sections (e.g. "General", "Additional", "Other") within a view. The page layout fields widget needs a way to organize fields into sections. Today, views have no concept of field grouping. This PR introduces the viewFieldGroup entity which sits between a view and its viewFields, enabling section-based organization. <img width="401" height="724" alt="Layout - V2 (customize visibility)" src="https://github.com/user-attachments/assets/6376e2ab-44db-42bf-9d2c-758f56f6b548" /> --------- Co-authored-by: Charles Bochet <charles@twenty.com> |