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. -->
This commit is contained in:
+211
-2
@@ -1,3 +1,5 @@
|
||||
import { ViewKey } from 'twenty-shared/types';
|
||||
|
||||
import { type FlatPageLayoutWidgetMaps } from 'src/engine/metadata-modules/flat-page-layout-widget/types/flat-page-layout-widget-maps.type';
|
||||
import { type FlatViewFieldGroupMaps } from 'src/engine/metadata-modules/flat-view-field-group/types/flat-view-field-group-maps.type';
|
||||
import { DEFAULT_VIEW_FIELD_SIZE } from 'src/engine/metadata-modules/flat-view-field/constants/default-view-field-size.constant';
|
||||
@@ -21,13 +23,30 @@ const buildEmptyFlatEntityMaps = () => ({
|
||||
});
|
||||
|
||||
const buildFlatViewMaps = (
|
||||
entries: { id: string; universalIdentifier: string }[] = [],
|
||||
entries: {
|
||||
id: string;
|
||||
universalIdentifier: string;
|
||||
key?: ViewKey | null;
|
||||
isActive?: boolean;
|
||||
isSystemSideEffect?: boolean;
|
||||
objectMetadataUniversalIdentifier?: string | null;
|
||||
deletedAt?: string | null;
|
||||
}[] = [],
|
||||
): FlatViewMaps =>
|
||||
({
|
||||
byUniversalIdentifier: Object.fromEntries(
|
||||
entries.map((entry) => [
|
||||
entry.universalIdentifier,
|
||||
{ universalIdentifier: entry.universalIdentifier, id: entry.id },
|
||||
{
|
||||
universalIdentifier: entry.universalIdentifier,
|
||||
id: entry.id,
|
||||
key: entry.key ?? null,
|
||||
isActive: entry.isActive ?? true,
|
||||
isSystemSideEffect: entry.isSystemSideEffect ?? false,
|
||||
objectMetadataUniversalIdentifier:
|
||||
entry.objectMetadataUniversalIdentifier ?? null,
|
||||
deletedAt: entry.deletedAt ?? null,
|
||||
},
|
||||
]),
|
||||
),
|
||||
universalIdentifierById: Object.fromEntries(
|
||||
@@ -713,6 +732,196 @@ describe('computeFlatViewFieldsFromFieldsWidgets', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('isSystemSideEffect inheritance from parent view', () => {
|
||||
it('should flag the created view field when the parent view is a system side effect', () => {
|
||||
const result = computeFlatViewFieldsFromFieldsWidgets({
|
||||
fieldsToCreate: [
|
||||
{
|
||||
objectMetadataUniversalIdentifier:
|
||||
OBJECT_METADATA_UNIVERSAL_IDENTIFIER,
|
||||
fieldMetadataUniversalIdentifier: 'field-uid-1',
|
||||
},
|
||||
],
|
||||
flatPageLayoutWidgetMaps: buildFlatPageLayoutWidgetMaps([
|
||||
buildFieldsWidget(),
|
||||
]),
|
||||
flatViewFieldMaps: buildFlatViewFieldMaps(),
|
||||
flatViewMaps: buildFlatViewMaps([
|
||||
{
|
||||
id: VIEW_ID,
|
||||
universalIdentifier: VIEW_UNIVERSAL_IDENTIFIER,
|
||||
isSystemSideEffect: true,
|
||||
},
|
||||
]),
|
||||
flatViewFieldGroupMaps: buildFlatViewFieldGroupMaps(),
|
||||
applicationUniversalIdentifier: APPLICATION_UNIVERSAL_IDENTIFIER,
|
||||
});
|
||||
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result[0].isSystemSideEffect).toBe(true);
|
||||
});
|
||||
|
||||
it('should not flag the created view field when the parent view is not a system side effect', () => {
|
||||
const result = computeFlatViewFieldsFromFieldsWidgets({
|
||||
fieldsToCreate: [
|
||||
{
|
||||
objectMetadataUniversalIdentifier:
|
||||
OBJECT_METADATA_UNIVERSAL_IDENTIFIER,
|
||||
fieldMetadataUniversalIdentifier: 'field-uid-1',
|
||||
},
|
||||
],
|
||||
flatPageLayoutWidgetMaps: buildFlatPageLayoutWidgetMaps([
|
||||
buildFieldsWidget(),
|
||||
]),
|
||||
flatViewFieldMaps: buildFlatViewFieldMaps(),
|
||||
flatViewMaps: buildFlatViewMaps([
|
||||
{
|
||||
id: VIEW_ID,
|
||||
universalIdentifier: VIEW_UNIVERSAL_IDENTIFIER,
|
||||
isSystemSideEffect: false,
|
||||
},
|
||||
]),
|
||||
flatViewFieldGroupMaps: buildFlatViewFieldGroupMaps(),
|
||||
applicationUniversalIdentifier: APPLICATION_UNIVERSAL_IDENTIFIER,
|
||||
});
|
||||
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result[0].isSystemSideEffect).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('INDEX view propagation', () => {
|
||||
it('should add a hidden, flagged view field to the object INDEX view even without a fields widget', () => {
|
||||
const result = computeFlatViewFieldsFromFieldsWidgets({
|
||||
fieldsToCreate: [
|
||||
{
|
||||
objectMetadataUniversalIdentifier:
|
||||
OBJECT_METADATA_UNIVERSAL_IDENTIFIER,
|
||||
fieldMetadataUniversalIdentifier: 'field-uid-1',
|
||||
},
|
||||
],
|
||||
flatPageLayoutWidgetMaps:
|
||||
buildEmptyFlatEntityMaps() as unknown as FlatPageLayoutWidgetMaps,
|
||||
flatViewFieldMaps: buildFlatViewFieldMaps(),
|
||||
flatViewMaps: buildFlatViewMaps([
|
||||
{
|
||||
id: 'index-view-db-id',
|
||||
universalIdentifier: 'index-view-uid',
|
||||
key: ViewKey.INDEX,
|
||||
isSystemSideEffect: true,
|
||||
objectMetadataUniversalIdentifier:
|
||||
OBJECT_METADATA_UNIVERSAL_IDENTIFIER,
|
||||
},
|
||||
]),
|
||||
flatViewFieldGroupMaps: buildFlatViewFieldGroupMaps(),
|
||||
applicationUniversalIdentifier: APPLICATION_UNIVERSAL_IDENTIFIER,
|
||||
});
|
||||
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result[0]).toMatchObject({
|
||||
viewUniversalIdentifier: 'index-view-uid',
|
||||
fieldMetadataUniversalIdentifier: 'field-uid-1',
|
||||
isVisible: false,
|
||||
isSystemSideEffect: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('should not target a standalone non-INDEX view without a widget', () => {
|
||||
const result = computeFlatViewFieldsFromFieldsWidgets({
|
||||
fieldsToCreate: [
|
||||
{
|
||||
objectMetadataUniversalIdentifier:
|
||||
OBJECT_METADATA_UNIVERSAL_IDENTIFIER,
|
||||
fieldMetadataUniversalIdentifier: 'field-uid-1',
|
||||
},
|
||||
],
|
||||
flatPageLayoutWidgetMaps:
|
||||
buildEmptyFlatEntityMaps() as unknown as FlatPageLayoutWidgetMaps,
|
||||
flatViewFieldMaps: buildFlatViewFieldMaps(),
|
||||
flatViewMaps: buildFlatViewMaps([
|
||||
{
|
||||
id: 'plain-view-db-id',
|
||||
universalIdentifier: 'plain-view-uid',
|
||||
key: null,
|
||||
objectMetadataUniversalIdentifier:
|
||||
OBJECT_METADATA_UNIVERSAL_IDENTIFIER,
|
||||
},
|
||||
]),
|
||||
flatViewFieldGroupMaps: buildFlatViewFieldGroupMaps(),
|
||||
applicationUniversalIdentifier: APPLICATION_UNIVERSAL_IDENTIFIER,
|
||||
});
|
||||
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
|
||||
it('should skip an inactive or soft-deleted INDEX view', () => {
|
||||
const result = computeFlatViewFieldsFromFieldsWidgets({
|
||||
fieldsToCreate: [
|
||||
{
|
||||
objectMetadataUniversalIdentifier:
|
||||
OBJECT_METADATA_UNIVERSAL_IDENTIFIER,
|
||||
fieldMetadataUniversalIdentifier: 'field-uid-1',
|
||||
},
|
||||
],
|
||||
flatPageLayoutWidgetMaps:
|
||||
buildEmptyFlatEntityMaps() as unknown as FlatPageLayoutWidgetMaps,
|
||||
flatViewFieldMaps: buildFlatViewFieldMaps(),
|
||||
flatViewMaps: buildFlatViewMaps([
|
||||
{
|
||||
id: 'inactive-index-view-db-id',
|
||||
universalIdentifier: 'inactive-index-view-uid',
|
||||
key: ViewKey.INDEX,
|
||||
isActive: false,
|
||||
objectMetadataUniversalIdentifier:
|
||||
OBJECT_METADATA_UNIVERSAL_IDENTIFIER,
|
||||
},
|
||||
{
|
||||
id: 'deleted-index-view-db-id',
|
||||
universalIdentifier: 'deleted-index-view-uid',
|
||||
key: ViewKey.INDEX,
|
||||
deletedAt: '2024-01-01T00:00:00.000Z',
|
||||
objectMetadataUniversalIdentifier:
|
||||
OBJECT_METADATA_UNIVERSAL_IDENTIFIER,
|
||||
},
|
||||
]),
|
||||
flatViewFieldGroupMaps: buildFlatViewFieldGroupMaps(),
|
||||
applicationUniversalIdentifier: APPLICATION_UNIVERSAL_IDENTIFIER,
|
||||
});
|
||||
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
|
||||
it('should not duplicate a field when a fields widget and the INDEX view share the same view', () => {
|
||||
const result = computeFlatViewFieldsFromFieldsWidgets({
|
||||
fieldsToCreate: [
|
||||
{
|
||||
objectMetadataUniversalIdentifier:
|
||||
OBJECT_METADATA_UNIVERSAL_IDENTIFIER,
|
||||
fieldMetadataUniversalIdentifier: 'field-uid-1',
|
||||
},
|
||||
],
|
||||
flatPageLayoutWidgetMaps: buildFlatPageLayoutWidgetMaps([
|
||||
buildFieldsWidget({ viewId: 'index-view-db-id', isVisible: true }),
|
||||
]),
|
||||
flatViewFieldMaps: buildFlatViewFieldMaps(),
|
||||
flatViewMaps: buildFlatViewMaps([
|
||||
{
|
||||
id: 'index-view-db-id',
|
||||
universalIdentifier: 'index-view-uid',
|
||||
key: ViewKey.INDEX,
|
||||
objectMetadataUniversalIdentifier:
|
||||
OBJECT_METADATA_UNIVERSAL_IDENTIFIER,
|
||||
},
|
||||
]),
|
||||
flatViewFieldGroupMaps: buildFlatViewFieldGroupMaps(),
|
||||
applicationUniversalIdentifier: APPLICATION_UNIVERSAL_IDENTIFIER,
|
||||
});
|
||||
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result[0].isVisible).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('unique universal identifiers', () => {
|
||||
it('should generate unique universalIdentifier for each created view field', () => {
|
||||
const result = computeFlatViewFieldsFromFieldsWidgets({
|
||||
|
||||
+77
-26
@@ -1,8 +1,8 @@
|
||||
import { ViewKey } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { type FlatPageLayoutWidgetMaps } from 'src/engine/metadata-modules/flat-page-layout-widget/types/flat-page-layout-widget-maps.type';
|
||||
import { type FlatPageLayoutWidget } from 'src/engine/metadata-modules/flat-page-layout-widget/types/flat-page-layout-widget.type';
|
||||
import { type FlatViewFieldGroupMaps } from 'src/engine/metadata-modules/flat-view-field-group/types/flat-view-field-group-maps.type';
|
||||
import { DEFAULT_VIEW_FIELD_SIZE } from 'src/engine/metadata-modules/flat-view-field/constants/default-view-field-size.constant';
|
||||
import { type FlatViewFieldMaps } from 'src/engine/metadata-modules/flat-view-field/types/flat-view-field-maps.type';
|
||||
@@ -19,6 +19,11 @@ type FieldToCreateInfo = {
|
||||
fieldMetadataUniversalIdentifier: string;
|
||||
};
|
||||
|
||||
type FieldViewTarget = {
|
||||
viewId: string;
|
||||
isVisible: boolean;
|
||||
};
|
||||
|
||||
const isFieldsWidgetConfiguration = (
|
||||
configuration: AllPageLayoutWidgetConfiguration,
|
||||
): configuration is FieldsConfigurationDTO => {
|
||||
@@ -28,25 +33,65 @@ const isFieldsWidgetConfiguration = (
|
||||
);
|
||||
};
|
||||
|
||||
const getMatchingFieldsWidgets = ({
|
||||
const getFieldViewTargets = ({
|
||||
objectMetadataUniversalIdentifier,
|
||||
flatPageLayoutWidgetMaps,
|
||||
flatViewMaps,
|
||||
}: {
|
||||
objectMetadataUniversalIdentifier: string;
|
||||
flatPageLayoutWidgetMaps: FlatPageLayoutWidgetMaps;
|
||||
}): FlatPageLayoutWidget[] =>
|
||||
Object.values(flatPageLayoutWidgetMaps.byUniversalIdentifier)
|
||||
.filter(isDefined)
|
||||
.filter(
|
||||
(widget) =>
|
||||
widget.isActive &&
|
||||
widget.type === WidgetType.FIELDS &&
|
||||
widget.objectMetadataUniversalIdentifier ===
|
||||
objectMetadataUniversalIdentifier &&
|
||||
isFieldsWidgetConfiguration(widget.configuration) &&
|
||||
isDefined(widget.configuration.viewId) &&
|
||||
isDefined(widget.configuration.newFieldDefaultVisibility),
|
||||
);
|
||||
flatViewMaps: FlatViewMaps;
|
||||
}): FieldViewTarget[] => {
|
||||
const targets: FieldViewTarget[] = [];
|
||||
const seenViewIds = new Set<string>();
|
||||
|
||||
for (const widget of Object.values(
|
||||
flatPageLayoutWidgetMaps.byUniversalIdentifier,
|
||||
).filter(isDefined)) {
|
||||
if (
|
||||
!widget.isActive ||
|
||||
widget.type !== WidgetType.FIELDS ||
|
||||
widget.objectMetadataUniversalIdentifier !==
|
||||
objectMetadataUniversalIdentifier ||
|
||||
!isFieldsWidgetConfiguration(widget.configuration)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const { viewId, newFieldDefaultVisibility } = widget.configuration;
|
||||
|
||||
if (
|
||||
!isDefined(viewId) ||
|
||||
!isDefined(newFieldDefaultVisibility) ||
|
||||
seenViewIds.has(viewId)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
seenViewIds.add(viewId);
|
||||
targets.push({ viewId, isVisible: newFieldDefaultVisibility });
|
||||
}
|
||||
|
||||
for (const view of Object.values(flatViewMaps.byUniversalIdentifier).filter(
|
||||
isDefined,
|
||||
)) {
|
||||
if (
|
||||
view.key !== ViewKey.INDEX ||
|
||||
!view.isActive ||
|
||||
isDefined(view.deletedAt) ||
|
||||
view.objectMetadataUniversalIdentifier !==
|
||||
objectMetadataUniversalIdentifier ||
|
||||
seenViewIds.has(view.id)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
seenViewIds.add(view.id);
|
||||
targets.push({ viewId: view.id, isVisible: false });
|
||||
}
|
||||
|
||||
return targets;
|
||||
};
|
||||
|
||||
const findLastViewFieldGroupId = ({
|
||||
viewId,
|
||||
@@ -130,11 +175,13 @@ export const computeFlatViewFieldsFromFieldsWidgets = ({
|
||||
];
|
||||
|
||||
const nextPositionByKey = new Map<string, number>();
|
||||
const queuedViewFieldKeys = new Set<string>();
|
||||
|
||||
for (const objectMetadataUniversalIdentifier of objectMetadataUniversalIdentifiers) {
|
||||
const matchingWidgets = getMatchingFieldsWidgets({
|
||||
const targets = getFieldViewTargets({
|
||||
objectMetadataUniversalIdentifier,
|
||||
flatPageLayoutWidgetMaps,
|
||||
flatViewMaps,
|
||||
});
|
||||
|
||||
const fieldsForObject = fieldsToCreate.filter(
|
||||
@@ -143,16 +190,7 @@ export const computeFlatViewFieldsFromFieldsWidgets = ({
|
||||
objectMetadataUniversalIdentifier,
|
||||
);
|
||||
|
||||
for (const widget of matchingWidgets) {
|
||||
if (!isFieldsWidgetConfiguration(widget.configuration)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const configuration = widget.configuration;
|
||||
|
||||
const viewId = configuration.viewId!;
|
||||
const isVisible = configuration.newFieldDefaultVisibility!;
|
||||
|
||||
for (const { viewId, isVisible } of targets) {
|
||||
const viewUniversalIdentifier =
|
||||
flatViewMaps.universalIdentifierById[viewId] ?? null;
|
||||
|
||||
@@ -160,6 +198,10 @@ export const computeFlatViewFieldsFromFieldsWidgets = ({
|
||||
continue;
|
||||
}
|
||||
|
||||
const isSystemSideEffect =
|
||||
flatViewMaps.byUniversalIdentifier[viewUniversalIdentifier]
|
||||
?.isSystemSideEffect ?? false;
|
||||
|
||||
const viewFieldGroupId = findLastViewFieldGroupId({
|
||||
viewId,
|
||||
flatViewFieldGroupMaps,
|
||||
@@ -184,6 +226,14 @@ export const computeFlatViewFieldsFromFieldsWidgets = ({
|
||||
}
|
||||
|
||||
for (const field of fieldsForObject) {
|
||||
const dedupKey = `${viewId}:${field.fieldMetadataUniversalIdentifier}`;
|
||||
|
||||
if (queuedViewFieldKeys.has(dedupKey)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
queuedViewFieldKeys.add(dedupKey);
|
||||
|
||||
const position = nextPositionByKey.get(positionKey)!;
|
||||
|
||||
nextPositionByKey.set(positionKey, position + 1);
|
||||
@@ -200,6 +250,7 @@ export const computeFlatViewFieldsFromFieldsWidgets = ({
|
||||
position,
|
||||
aggregateOperation: null,
|
||||
isActive: true,
|
||||
isSystemSideEffect,
|
||||
universalOverrides: null,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
|
||||
+1
@@ -67,6 +67,7 @@ export const fromCreateViewFieldInputToFlatViewFieldToCreate = ({
|
||||
position: createViewFieldInput.position ?? 0,
|
||||
aggregateOperation: createViewFieldInput.aggregateOperation ?? null,
|
||||
isActive: true,
|
||||
isSystemSideEffect: false,
|
||||
universalOverrides: null,
|
||||
viewFieldGroupUniversalIdentifier,
|
||||
applicationUniversalIdentifier: flatApplication.universalIdentifier,
|
||||
|
||||
+1
@@ -64,6 +64,7 @@ export const fromUpdateViewFieldInputToFlatViewFieldToUpdateOrThrow = ({
|
||||
entityApplicationUniversalIdentifier:
|
||||
existingFlatViewFieldToUpdate.applicationUniversalIdentifier,
|
||||
workspaceCustomApplicationUniversalIdentifier,
|
||||
isSystemSideEffect: existingFlatViewFieldToUpdate.isSystemSideEffect,
|
||||
});
|
||||
|
||||
const { overrides, updatedEditableProperties } =
|
||||
|
||||
Reference in New Issue
Block a user