refactor(server): unify the two metadata override mechanisms into one (#22417)

## Unify the two metadata override mechanisms into one

Twenty had **two** override mechanisms:

- **`standardOverrides`** — a bespoke JSONB column on
`objectMetadata`/`fieldMetadata` with typed DTOs and a per-locale
`translations` map, resolved by two i18n-aware resolvers.
- **`OverridableEntity.overrides`** — a flat, registry-driven JSONB blob
on view / view-field / view-field-group / command-menu-item /
page-layout-tab / page-layout-widget, resolved by a plain spread.

This PR collapses them into **one** concept: a single `overrides` blob,
one registry-driven overridable set, one i18n-aware read path, and one
write path (`computeMetadataOverridesBlob`, extracted in #22404).

Object/field **stay on `SyncableEntity`** (not reparented to
`OverridableEntity`) so their `isActive` default stays **FALSE** — this
sidesteps the `isActive` default conflict entirely.

### GraphQL breaking change (accepted)

The `standardOverrides` field is **removed** with no deprecation alias —
`overrides` (a `JSON` scalar) is exposed instead on `Object` and
`Field`. Product confirmed negligible external usage; the front-end has
no hand-written consumer (only generated types), which are regenerated
here.

### Commit structure (reviewable commit-by-commit)

1. **Unified resolver + parity harness** —
`resolveEffectiveEntityProperty` is a strict superset of the three
legacy resolvers; a corpus parity spec compares it against a *frozen
reference* of the old logic across every locale, `isStandardApp` branch
and override shape.
2. **Registry-driven** — object/field presentation props tagged
`isOverridable` + `translatable`; the overridable/translatable sets are
derived from the registry (a test asserts they equal the legacy
hardcoded lists).
3. **Rename + swap + delete** — `standardOverrides` → `overrides` across
entities, DTOs, flat/universal types, producers, the ~12
resolve/write/create/sync call sites, mocks and specs; the reconciler's
two compare entries collapse to one; the three legacy resolvers, both
DTOs and the hardcoded constants/types are deleted.
4. **Migration (zero-downtime, two-phase)** — split across two releases
so a rolling deploy never drops a column a previous-release pod still
`SELECT`s:
   - **2.19 fast** — add the `overrides` column (schema only).
- **2.19 slow** — backfill `overrides` from `standardOverrides` in
`runDataMigration` (kept out of the schema transaction so the bulk write
doesn't hold the ACCESS EXCLUSIVE lock; skipped on fresh installs, which
have no data to copy).
- **2.20 fast** — drop the legacy `standardOverrides` column (gated by
`TWENTY_NEXT_VERSIONS`, so it stays dormant until the instance reaches
2.20).
5. **Front/client-SDK regen** — regenerated metadata GraphQL types.
6. **Integration specs + i18n** — updated the standard object/field
update integration specs + snapshots, and the reworded validator message
catalog entry.

### Rolling-deploy safety

`standardOverrides` is retained through 2.19 and only dropped in 2.20,
mirroring the codebase's deferred-drop convention
(`isUIReadOnly`/`isCustom`). During the 2.19 rollout both columns exist,
so old and new pods coexist without "column does not exist" errors. The
backfill lives in a slow `runDataMigration` (per the
`no-data-mutation-in-fast-instance-command` rule) so it doesn't stall
reads.

### `isActive` guard

The migration never reads or writes `isActive`; the backfill asserts the
active-row count is unchanged and aborts otherwise. Verified on a real
DB: apply + revert preserves the blob **and** the nested `translations`
map, with `isActive` counts identical before/after.

### Verification (local)

- `nx typecheck twenty-server` + `nx typecheck twenty-front` — green
- `nx lint:diff-with-main twenty-server` (oxlint `--type-aware` + oxfmt)
— green
- `nx test twenty-server` — green (unit + parity + registry + migration
tests)
- `nx run twenty-server:test:integration:with-db-reset` — green
- `database:reset` applies the 2.19 phases and leaves **both** columns
present (2.20 drop stays dormant); backfill + revert round-trip verified
on a real DB
- Metadata integration suites (standard object/field update, application
sync) pass end-to-end against the two-column schema
- Metadata GraphQL types regenerated against a booted server; zero
`standardOverrides` references remain in application code (only the
migration commands + the legacy schema baseline)

---------

Co-authored-by: prastoin <paul@twenty.com>
This commit is contained in:
Félix Malfait
2026-07-02 12:01:15 +02:00
committed by GitHub
parent 63d092a31b
commit 5a4ebca226
121 changed files with 2394 additions and 3109 deletions
@@ -464,13 +464,6 @@ enum LogicFunctionExecutionMode {
PREBUILT
}
type StandardOverrides {
label: String
description: String
icon: String
translations: JSON
}
type Field {
id: UUID!
universalIdentifier: String!
@@ -479,7 +472,6 @@ type Field {
label: String!
description: String
icon: String
standardOverrides: StandardOverrides
isActive: Boolean
isSystem: Boolean
isUIEditable: Boolean
@@ -606,15 +598,6 @@ input BooleanFieldComparison {
isNot: Boolean
}
type ObjectStandardOverrides {
labelSingular: String
labelPlural: String
description: String
icon: String
color: String
translations: JSON
}
type Object {
id: UUID!
universalIdentifier: String!
@@ -624,7 +607,6 @@ type Object {
labelPlural: String!
description: String
icon: String
standardOverrides: ObjectStandardOverrides
shortcut: String
color: String
isRemote: Boolean!
@@ -344,14 +344,6 @@ export interface LogicFunction {
export type LogicFunctionExecutionMode = 'LIVE' | 'PREBUILT'
export interface StandardOverrides {
label?: Scalars['String']
description?: Scalars['String']
icon?: Scalars['String']
translations?: Scalars['JSON']
__typename: 'StandardOverrides'
}
export interface Field {
id: Scalars['UUID']
universalIdentifier: Scalars['String']
@@ -360,7 +352,6 @@ export interface Field {
label: Scalars['String']
description?: Scalars['String']
icon?: Scalars['String']
standardOverrides?: StandardOverrides
isActive?: Scalars['Boolean']
isSystem?: Scalars['Boolean']
isUIEditable?: Scalars['Boolean']
@@ -405,16 +396,6 @@ export interface Index {
/** Type of the index */
export type IndexType = 'BTREE' | 'GIN'
export interface ObjectStandardOverrides {
labelSingular?: Scalars['String']
labelPlural?: Scalars['String']
description?: Scalars['String']
icon?: Scalars['String']
color?: Scalars['String']
translations?: Scalars['JSON']
__typename: 'ObjectStandardOverrides'
}
export interface Object {
id: Scalars['UUID']
universalIdentifier: Scalars['String']
@@ -424,7 +405,6 @@ export interface Object {
labelPlural: Scalars['String']
description?: Scalars['String']
icon?: Scalars['String']
standardOverrides?: ObjectStandardOverrides
shortcut?: Scalars['String']
color?: Scalars['String']
isRemote: Scalars['Boolean']
@@ -3350,15 +3330,6 @@ export interface LogicFunctionGenqlSelection{
__scalar?: boolean | number
}
export interface StandardOverridesGenqlSelection{
label?: boolean | number
description?: boolean | number
icon?: boolean | number
translations?: boolean | number
__typename?: boolean | number
__scalar?: boolean | number
}
export interface FieldGenqlSelection{
id?: boolean | number
universalIdentifier?: boolean | number
@@ -3367,7 +3338,6 @@ export interface FieldGenqlSelection{
label?: boolean | number
description?: boolean | number
icon?: boolean | number
standardOverrides?: StandardOverridesGenqlSelection
isActive?: boolean | number
isSystem?: boolean | number
isUIEditable?: boolean | number
@@ -3426,17 +3396,6 @@ export interface UUIDFilterComparison {is?: (Scalars['Boolean'] | null),isNot?:
export interface BooleanFieldComparison {is?: (Scalars['Boolean'] | null),isNot?: (Scalars['Boolean'] | null)}
export interface ObjectStandardOverridesGenqlSelection{
labelSingular?: boolean | number
labelPlural?: boolean | number
description?: boolean | number
icon?: boolean | number
color?: boolean | number
translations?: boolean | number
__typename?: boolean | number
__scalar?: boolean | number
}
export interface ObjectGenqlSelection{
id?: boolean | number
universalIdentifier?: boolean | number
@@ -3446,7 +3405,6 @@ export interface ObjectGenqlSelection{
labelPlural?: boolean | number
description?: boolean | number
icon?: boolean | number
standardOverrides?: ObjectStandardOverridesGenqlSelection
shortcut?: boolean | number
color?: boolean | number
isRemote?: boolean | number
@@ -6790,14 +6748,6 @@ export interface LogicFunctionLogsInput {applicationId?: (Scalars['UUID'] | null
const StandardOverrides_possibleTypes: string[] = ['StandardOverrides']
export const isStandardOverrides = (obj?: { __typename?: any } | null): obj is StandardOverrides => {
if (!obj?.__typename) throw new Error('__typename is missing in "isStandardOverrides"')
return StandardOverrides_possibleTypes.includes(obj.__typename)
}
const Field_possibleTypes: string[] = ['Field']
export const isField = (obj?: { __typename?: any } | null): obj is Field => {
if (!obj?.__typename) throw new Error('__typename is missing in "isField"')
@@ -6814,14 +6764,6 @@ export interface LogicFunctionLogsInput {applicationId?: (Scalars['UUID'] | null
const ObjectStandardOverrides_possibleTypes: string[] = ['ObjectStandardOverrides']
export const isObjectStandardOverrides = (obj?: { __typename?: any } | null): obj is ObjectStandardOverrides => {
if (!obj?.__typename) throw new Error('__typename is missing in "isObjectStandardOverrides"')
return ObjectStandardOverrides_possibleTypes.includes(obj.__typename)
}
const Object_possibleTypes: string[] = ['Object']
export const isObject = (obj?: { __typename?: any } | null): obj is Object => {
if (!obj?.__typename) throw new Error('__typename is missing in "isObject"')
File diff suppressed because it is too large Load Diff
@@ -1767,7 +1767,6 @@ export type Field = {
options?: Maybe<Scalars['JSON']['output']>;
relation?: Maybe<Relation>;
settings?: Maybe<Scalars['JSON']['output']>;
standardOverrides?: Maybe<StandardOverrides>;
type: FieldMetadataType;
universalIdentifier: Scalars['String']['output'];
updatedAt: Scalars['DateTime']['output'];
@@ -3849,7 +3848,6 @@ export type Object = {
nameSingular: Scalars['String']['output'];
searchFieldMetadataList: Array<SearchField>;
shortcut?: Maybe<Scalars['String']['output']>;
standardOverrides?: Maybe<ObjectStandardOverrides>;
universalIdentifier: Scalars['String']['output'];
updatedAt: Scalars['DateTime']['output'];
};
@@ -3979,16 +3977,6 @@ export enum ObjectRecordGroupByDateGranularity {
YEAR = 'YEAR'
}
export type ObjectStandardOverrides = {
__typename?: 'ObjectStandardOverrides';
color?: Maybe<Scalars['String']['output']>;
description?: Maybe<Scalars['String']['output']>;
icon?: Maybe<Scalars['String']['output']>;
labelPlural?: Maybe<Scalars['String']['output']>;
labelSingular?: Maybe<Scalars['String']['output']>;
translations?: Maybe<Scalars['JSON']['output']>;
};
/** Onboarding status */
export enum OnboardingStatus {
APPS_INSTALLATION = 'APPS_INSTALLATION',
@@ -5100,14 +5088,6 @@ export type StandaloneRichTextConfiguration = {
configurationType: WidgetConfigurationType;
};
export type StandardOverrides = {
__typename?: 'StandardOverrides';
description?: Maybe<Scalars['String']['output']>;
icon?: Maybe<Scalars['String']['output']>;
label?: Maybe<Scalars['String']['output']>;
translations?: Maybe<Scalars['JSON']['output']>;
};
export type SubdomainAvailabilityDto = {
__typename?: 'SubdomainAvailabilityDTO';
available: Scalars['Boolean']['output'];
@@ -127,7 +127,7 @@ const buildLegacyCalendarEventRecordingPreferenceFieldMetadata = ({
isUnique: false,
isUIEditable: true,
isLabelSyncedWithName: false,
standardOverrides: null,
overrides: null,
defaultValue: "'AUTO'",
settings: null,
options: [
@@ -0,0 +1,32 @@
import { QueryRunner } from 'typeorm';
import { RegisteredInstanceCommand } from 'src/engine/core-modules/upgrade/decorators/registered-instance-command.decorator';
import { FastInstanceCommand } from 'src/engine/core-modules/upgrade/interfaces/fast-instance-command.interface';
const TABLES = ['objectMetadata', 'fieldMetadata'] as const;
@RegisteredInstanceCommand('2.19.0', 1820000100000)
export class AddMetadataOverridesColumnFastInstanceCommand
implements FastInstanceCommand
{
public async up(queryRunner: QueryRunner): Promise<void> {
for (const table of TABLES) {
await queryRunner.query(
`ALTER TABLE "core"."${table}" ADD COLUMN IF NOT EXISTS "overrides" jsonb`,
);
}
}
public async down(queryRunner: QueryRunner): Promise<void> {
for (const table of TABLES) {
// Unconditional copy: a WHERE "overrides" IS NOT NULL guard would leave a
// stale value in standardOverrides and resurrect a cleared override.
await queryRunner.query(
`UPDATE "core"."${table}" SET "standardOverrides" = "overrides"`,
);
await queryRunner.query(
`ALTER TABLE "core"."${table}" DROP COLUMN IF EXISTS "overrides"`,
);
}
}
}
@@ -0,0 +1,48 @@
import { DataSource, QueryRunner } from 'typeorm';
import { RegisteredInstanceCommand } from 'src/engine/core-modules/upgrade/decorators/registered-instance-command.decorator';
import { SlowInstanceCommand } from 'src/engine/core-modules/upgrade/interfaces/slow-instance-command.interface';
const TABLES = ['objectMetadata', 'fieldMetadata'] as const;
@RegisteredInstanceCommand('2.19.0', 1820000110000, { type: 'slow' })
export class BackfillMetadataOverridesSlowInstanceCommand
implements SlowInstanceCommand
{
async runDataMigration(dataSource: DataSource): Promise<void> {
for (const table of TABLES) {
const activeCountBefore = await this.getActiveCount(dataSource, table);
await dataSource.query(
`UPDATE "core"."${table}" SET "overrides" = "standardOverrides" WHERE "standardOverrides" IS NOT NULL AND "overrides" IS NULL`,
);
const activeCountAfter = await this.getActiveCount(dataSource, table);
if (activeCountBefore !== activeCountAfter) {
throw new Error(
`BackfillMetadataOverrides: "isActive" changed on "core"."${table}" (${activeCountBefore} -> ${activeCountAfter}), aborting.`,
);
}
}
}
public async up(_queryRunner: QueryRunner): Promise<void> {
return;
}
public async down(_queryRunner: QueryRunner): Promise<void> {
return;
}
private async getActiveCount(
dataSource: DataSource,
table: (typeof TABLES)[number],
): Promise<number> {
const [{ count }] = await dataSource.query(
`SELECT count(*)::int AS count FROM "core"."${table}" WHERE "isActive" = true`,
);
return count;
}
}
@@ -0,0 +1,51 @@
import { type QueryRunner } from 'typeorm';
import { AddMetadataOverridesColumnFastInstanceCommand } from 'src/database/commands/upgrade-version-command/2-19/2-19-instance-command-fast-1820000100000-add-metadata-overrides-column';
describe('AddMetadataOverridesColumnFastInstanceCommand', () => {
let command: AddMetadataOverridesColumnFastInstanceCommand;
beforeEach(() => {
command = new AddMetadataOverridesColumnFastInstanceCommand();
});
describe('up', () => {
it('adds the overrides column to both tables without mutating data or dropping standardOverrides', async () => {
const query = jest.fn().mockResolvedValue(undefined);
const queryRunner = { query } as unknown as QueryRunner;
await command.up(queryRunner);
const statements = query.mock.calls.map((call) => call[0] as string);
expect(statements).toEqual([
'ALTER TABLE "core"."objectMetadata" ADD COLUMN IF NOT EXISTS "overrides" jsonb',
'ALTER TABLE "core"."fieldMetadata" ADD COLUMN IF NOT EXISTS "overrides" jsonb',
]);
expect(
statements.some(
(statement) =>
statement.includes('UPDATE') || statement.includes('DROP COLUMN'),
),
).toBe(false);
});
});
describe('down', () => {
it('copies overrides back into standardOverrides before dropping the column', async () => {
const query = jest.fn().mockResolvedValue(undefined);
const queryRunner = { query } as unknown as QueryRunner;
await command.down(queryRunner);
const statements = query.mock.calls.map((call) => call[0] as string);
expect(statements).toEqual([
'UPDATE "core"."objectMetadata" SET "standardOverrides" = "overrides"',
'ALTER TABLE "core"."objectMetadata" DROP COLUMN IF EXISTS "overrides"',
'UPDATE "core"."fieldMetadata" SET "standardOverrides" = "overrides"',
'ALTER TABLE "core"."fieldMetadata" DROP COLUMN IF EXISTS "overrides"',
]);
});
});
});
@@ -0,0 +1,45 @@
import { type DataSource } from 'typeorm';
import { BackfillMetadataOverridesSlowInstanceCommand } from 'src/database/commands/upgrade-version-command/2-19/2-19-instance-command-slow-1820000110000-backfill-metadata-overrides';
describe('BackfillMetadataOverridesSlowInstanceCommand', () => {
let command: BackfillMetadataOverridesSlowInstanceCommand;
beforeEach(() => {
command = new BackfillMetadataOverridesSlowInstanceCommand();
});
describe('runDataMigration', () => {
it('copies standardOverrides into overrides for both tables', async () => {
const query = jest.fn().mockResolvedValue([{ count: 3 }]);
const dataSource = { query } as unknown as DataSource;
await command.runDataMigration(dataSource);
const statements = query.mock.calls.map((call) => call[0] as string);
for (const table of ['objectMetadata', 'fieldMetadata']) {
expect(statements).toEqual(
expect.arrayContaining([
expect.stringContaining(
`UPDATE "core"."${table}" SET "overrides" = "standardOverrides"`,
),
]),
);
}
});
it('aborts when the isActive row count changes', async () => {
const query = jest
.fn()
.mockResolvedValueOnce([{ count: 5 }]) // objectMetadata before
.mockResolvedValueOnce(undefined) // UPDATE backfill
.mockResolvedValueOnce([{ count: 4 }]); // objectMetadata after
const dataSource = { query } as unknown as DataSource;
await expect(command.runDataMigration(dataSource)).rejects.toThrow(
/"isActive" changed on "core"\."objectMetadata"/,
);
});
});
});
@@ -0,0 +1,4 @@
// Referenced by @WasIntroducedInUpgrade on the "overrides" column so pre-2.19
// upgrade steps don't SELECT it before this command adds it.
export const ADD_METADATA_OVERRIDES_COLUMN_UPGRADE_COMMAND_NAME =
'2.19.0_AddMetadataOverridesColumnFastInstanceCommand_1820000100000';
@@ -0,0 +1,134 @@
# 2.20 — pending metadata-override cleanup
The `standardOverrides` column on `objectMetadata` and `fieldMetadata` is a
**deferred drop**. As of 2.19 the column is:
- superseded by `overrides` (backfilled by
`../2-19/2-19-instance-command-slow-1820000110000-backfill-metadata-overrides.ts`),
- excluded from the flat-entity / registry via the `WasRemovedInUpgrade<T>` type
wrapper on both entities,
- still physically present in the database, so a 2.18 → 2.19 rolling deploy never
breaks the previous release's pods, which still `SELECT "standardOverrides"`.
The physical `DROP COLUMN` is intentionally deferred to the next release (2.20):
by the time it runs, every pod is on a release that reads/writes `overrides`
only. This folder holds the ready-to-wire drop command so whoever opens the 2.20
upgrade work can drop it in.
## Why it isn't wired up yet
`2.20.0` is not in `TWENTY_CROSS_UPGRADE_SUPPORTED_VERSIONS` (currently
`[...TWENTY_PREVIOUS_VERSIONS, TWENTY_CURRENT_VERSION]`, and current is 2.19.0).
Registering a 2.20 instance command — or pointing the entity's
`@WasRemovedInUpgrade` decorator at a 2.20 command name — would fail boot
validation (`unknown-step-name`) until 2.20 is the current version. So the
command lives here as documentation, not as compiled code.
## Activation checklist (when 2.20 becomes the current version)
1. Create `2-20-instance-command-fast-<timestamp>-drop-metadata-standard-overrides-column.ts`
in this folder with the command below. Regenerate `<timestamp>` (the value
here is a placeholder) so ordering against other 2.20 commands is correct.
2. Register it in
`../instance-commands.constant.ts` (import + add to the array).
3. Add the drop decorator to `standardOverrides` on **both**
`object-metadata.entity.ts` and `field-metadata.entity.ts`:
`@WasRemovedInUpgrade({ upgradeCommandName: DROP_METADATA_STANDARD_OVERRIDES_COLUMN_UPGRADE_COMMAND_NAME })`,
and add the matching constant
(`'2.20.0_DropMetadataStandardOverridesColumnFastInstanceCommand_<timestamp>'`),
mirroring how `isCustom` pairs its decorator with a command name.
4. Add the test below under `2-20/__tests__/`.
5. Run `database:migrate:generate --name pending-migration-check` and confirm no
drift, then run the integration suite.
## Command
```ts
import { QueryRunner } from 'typeorm';
import { RegisteredInstanceCommand } from 'src/engine/core-modules/upgrade/decorators/registered-instance-command.decorator';
import { FastInstanceCommand } from 'src/engine/core-modules/upgrade/interfaces/fast-instance-command.interface';
// Phase 2 of unifying the metadata override mechanisms. Drops the legacy
// "standardOverrides" column now that every pod runs a release which reads and
// writes "overrides" (backfilled in 2.19). Runs only once the instance reaches
// 2.20, so the 2.19 rolling deploy keeps the column available for older pods.
// down() restores the column and copies the blob back from "overrides".
const TABLES = ['objectMetadata', 'fieldMetadata'] as const;
@RegisteredInstanceCommand('2.20.0', 1825000000000)
export class DropMetadataStandardOverridesColumnFastInstanceCommand
implements FastInstanceCommand
{
public async up(queryRunner: QueryRunner): Promise<void> {
for (const table of TABLES) {
await queryRunner.query(
`ALTER TABLE "core"."${table}" DROP COLUMN IF EXISTS "standardOverrides"`,
);
}
}
public async down(queryRunner: QueryRunner): Promise<void> {
for (const table of TABLES) {
await queryRunner.query(
`ALTER TABLE "core"."${table}" ADD COLUMN IF NOT EXISTS "standardOverrides" jsonb`,
);
await queryRunner.query(
`UPDATE "core"."${table}" SET "standardOverrides" = "overrides"`,
);
}
}
}
```
## Test
```ts
import { type QueryRunner } from 'typeorm';
import { DropMetadataStandardOverridesColumnFastInstanceCommand } from 'src/database/commands/upgrade-version-command/2-20/2-20-instance-command-fast-1825000000000-drop-metadata-standard-overrides-column';
describe('DropMetadataStandardOverridesColumnFastInstanceCommand', () => {
let command: DropMetadataStandardOverridesColumnFastInstanceCommand;
beforeEach(() => {
command = new DropMetadataStandardOverridesColumnFastInstanceCommand();
});
describe('up', () => {
it('drops standardOverrides from both tables', async () => {
const query = jest.fn().mockResolvedValue(undefined);
const queryRunner = { query } as unknown as QueryRunner;
await command.up(queryRunner);
const statements = query.mock.calls.map((call) => call[0] as string);
expect(statements).toEqual([
'ALTER TABLE "core"."objectMetadata" DROP COLUMN IF EXISTS "standardOverrides"',
'ALTER TABLE "core"."fieldMetadata" DROP COLUMN IF EXISTS "standardOverrides"',
]);
});
});
describe('down', () => {
it('recreates and backfills standardOverrides from overrides', async () => {
const query = jest.fn().mockResolvedValue(undefined);
const queryRunner = { query } as unknown as QueryRunner;
await command.down(queryRunner);
const statements = query.mock.calls.map((call) => call[0] as string);
for (const table of ['objectMetadata', 'fieldMetadata']) {
expect(statements).toEqual(
expect.arrayContaining([
`ALTER TABLE "core"."${table}" ADD COLUMN IF NOT EXISTS "standardOverrides" jsonb`,
`UPDATE "core"."${table}" SET "standardOverrides" = "overrides"`,
]),
);
}
});
});
});
```
@@ -41,6 +41,8 @@ import { CreateDpaAgreementCoreTableFastInstanceCommand } from 'src/database/com
import { CreateApplicationTranslationCoreTableFastInstanceCommand } from 'src/database/commands/upgrade-version-command/2-17/2-17-instance-command-fast-1801000100000-create-application-translation-core-table';
import { AddTsVectorFieldMetadataIdToSearchFieldMetadataFastInstanceCommand } from 'src/database/commands/upgrade-version-command/2-18/2-18-instance-command-fast-1810000001000-add-ts-vector-field-metadata-id-to-search-field-metadata';
import { BackfillTsVectorFieldMetadataIdOnSearchFieldMetadataSlowInstanceCommand } from 'src/database/commands/upgrade-version-command/2-18/2-18-instance-command-slow-1810000003000-backfill-ts-vector-field-metadata-id-on-search-field-metadata';
import { AddMetadataOverridesColumnFastInstanceCommand } from 'src/database/commands/upgrade-version-command/2-19/2-19-instance-command-fast-1820000100000-add-metadata-overrides-column';
import { BackfillMetadataOverridesSlowInstanceCommand } from 'src/database/commands/upgrade-version-command/2-19/2-19-instance-command-slow-1820000110000-backfill-metadata-overrides';
import { AddCacheTokensToAgentChatThreadFastInstanceCommand } from 'src/database/commands/upgrade-version-command/2-2/2-2-instance-command-fast-1777455269302-add-cache-tokens-to-agent-chat-thread';
import { AddLogoToApplicationFastInstanceCommand } from 'src/database/commands/upgrade-version-command/2-2/2-2-instance-command-fast-1777539664664-add-logo-to-application';
import { AddSubFieldNameToViewSortEarlyFastInstanceCommand } from 'src/database/commands/upgrade-version-command/2-3/2-3-instance-command-fast-1747234200000-add-sub-field-name-to-view-sort';
@@ -170,4 +172,6 @@ export const INSTANCE_COMMANDS = [
CreateApplicationTranslationCoreTableFastInstanceCommand,
AddTsVectorFieldMetadataIdToSearchFieldMetadataFastInstanceCommand,
BackfillTsVectorFieldMetadataIdOnSearchFieldMetadataSlowInstanceCommand,
AddMetadataOverridesColumnFastInstanceCommand,
BackfillMetadataOverridesSlowInstanceCommand,
];
@@ -163,7 +163,7 @@ export const mockPersonFlatObjectMetadata = (
updatedAt: new Date().toISOString(),
shortcut: null,
description: null,
standardOverrides: null,
overrides: null,
isUIEditable: true,
isUICreatable: true,
applicationUniversalIdentifier: 'test-application-id',
@@ -88,7 +88,7 @@ export const fromFieldManifestToUniversalFlatFieldMetadata = ({
label: fieldManifest.label,
description: fieldManifest.description ?? null,
icon: fieldManifest.icon ?? null,
standardOverrides: null,
overrides: null,
options: fieldManifest.options ?? null,
defaultValue,
universalSettings: fieldManifest.universalSettings ?? null,
@@ -21,7 +21,7 @@ export const fromObjectManifestToUniversalFlatObjectMetadata = ({
color: null,
description: objectManifest.description ?? null,
icon: objectManifest.icon ?? null,
standardOverrides: null,
overrides: null,
targetTableName: 'DEPRECATED',
isRemote: false,
isActive: true,
@@ -36,7 +36,7 @@ const mockObjectMetadata: FlatObjectMetadata = {
updatedAt: new Date().toISOString(),
shortcut: null,
description: null,
standardOverrides: null,
overrides: null,
isUIEditable: true,
isUICreatable: true,
labelIdentifierFieldMetadataId: null,
@@ -714,7 +714,7 @@ export class SearchService {
recordId: record.id,
objectNameSingular: objectMetadataItem.nameSingular,
objectLabelSingular:
objectMetadataItem.standardOverrides?.labelSingular ??
objectMetadataItem.overrides?.labelSingular ??
objectMetadataItem.labelSingular,
label: this.getLabelIdentifierValue(
record,
@@ -13,11 +13,11 @@ import { type FlatApplicationCacheMaps } from 'src/engine/core-modules/applicati
import { I18nService } from 'src/engine/core-modules/i18n/i18n.service';
import { type IDataloaders } from 'src/engine/dataloaders/dataloader.interface';
import { filterMorphRelationDuplicateFields } from 'src/engine/dataloaders/utils/filter-morph-relation-duplicate-fields.util';
import { FIELD_METADATA_STANDARD_OVERRIDES_PROPERTIES } from 'src/engine/metadata-modules/field-metadata/constants/field-metadata-standard-overrides-properties.constant';
import { type FieldMetadataDTO } from 'src/engine/metadata-modules/field-metadata/dtos/field-metadata.dto';
import { RelationDTO } from 'src/engine/metadata-modules/field-metadata/dtos/relation.dto';
import { type FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
import { resolveFieldMetadataStandardOverride } from 'src/engine/metadata-modules/field-metadata/utils/resolve-field-metadata-standard-override.util';
import { ALL_OVERRIDABLE_PROPERTIES_BY_METADATA_NAME } from 'src/engine/metadata-modules/flat-entity/constant/all-overridable-properties-by-metadata-name.constant';
import { resolveEffectiveEntityProperty } from 'src/engine/metadata-modules/utils/resolve-effective-entity-property.util';
import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.service';
import { findFlatEntityByIdInFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps.util';
import { findFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
@@ -419,23 +419,24 @@ export class DataloaderService {
)
: undefined;
return FIELD_METADATA_STANDARD_OVERRIDES_PROPERTIES.reduce(
const overrides = flatFieldMetadata.overrides ?? undefined;
const i18nContext = {
locale,
i18nInstance,
isStandardApp: belongsToTwentyStandardApp(flatFieldMetadata),
applicationCatalog,
};
return ALL_OVERRIDABLE_PROPERTIES_BY_METADATA_NAME.fieldMetadata.reduce(
(acc, property) => ({
...acc,
[property]: resolveFieldMetadataStandardOverride(
{
label: flatFieldMetadata.label,
description: flatFieldMetadata.description ?? undefined,
icon: flatFieldMetadata.icon ?? undefined,
standardOverrides:
flatFieldMetadata.standardOverrides ?? undefined,
},
[property]: resolveEffectiveEntityProperty({
metadataName: 'fieldMetadata',
baseValue: flatFieldMetadata[property],
overrides,
property,
locale,
i18nInstance,
belongsToTwentyStandardApp(flatFieldMetadata),
applicationCatalog,
),
i18nContext,
}),
}),
flatFieldMetadata,
);
@@ -20,7 +20,7 @@ const mockObjectMetadata = {
labelSingular: 'Person',
description: 'A person',
icon: 'IconUser',
standardOverrides: undefined,
overrides: undefined,
} as unknown as ObjectMetadataDTO;
const baseCommandMenuItem = {
@@ -1,15 +1,15 @@
import { type I18n } from '@lingui/core';
import { type APP_LOCALES } from 'twenty-shared/translations';
import { type ObjectStandardOverridesDTO } from 'src/engine/metadata-modules/object-metadata/dtos/object-standard-overrides.dto';
import { resolveObjectMetadataStandardOverride } from 'src/engine/metadata-modules/object-metadata/utils/resolve-object-metadata-standard-override.util';
import { type ObjectMetadataOverrides } from 'src/engine/metadata-modules/object-metadata/types/object-metadata-overrides.type';
import { resolveEffectiveEntityProperty } from 'src/engine/metadata-modules/utils/resolve-effective-entity-property.util';
export type NavigationInterpolationObjectMetadata = {
labelPlural: string;
labelSingular: string;
description?: string | null;
icon?: string | null;
standardOverrides?: ObjectStandardOverridesDTO | null;
overrides?: ObjectMetadataOverrides | null;
};
export const buildNavigationInterpolationContext = ({
@@ -25,31 +25,29 @@ export const buildNavigationInterpolationContext = ({
i18nInstance: I18n;
applicationCatalog?: Record<string, string>;
}): Record<string, unknown> => {
const overrideInput = {
labelPlural: objectMetadata.labelPlural,
labelSingular: objectMetadata.labelSingular,
description: objectMetadata.description ?? undefined,
icon: objectMetadata.icon ?? undefined,
standardOverrides: objectMetadata.standardOverrides ?? undefined,
const overrides = objectMetadata.overrides ?? undefined;
const i18nContext = {
locale,
i18nInstance,
isStandardApp,
applicationCatalog,
};
const resolvedLabelPlural = resolveObjectMetadataStandardOverride(
overrideInput,
'labelPlural',
locale,
i18nInstance,
isStandardApp,
applicationCatalog,
);
const resolvedLabelPlural = resolveEffectiveEntityProperty({
metadataName: 'objectMetadata',
baseValue: objectMetadata.labelPlural,
overrides,
property: 'labelPlural',
i18nContext,
});
const resolvedIcon = resolveObjectMetadataStandardOverride(
overrideInput,
'icon',
locale,
i18nInstance,
isStandardApp,
applicationCatalog,
);
const resolvedIcon = resolveEffectiveEntityProperty({
metadataName: 'objectMetadata',
baseValue: objectMetadata.icon,
overrides,
property: 'icon',
i18nContext,
});
return {
navigateToObjectMetadataItem: {
@@ -1,7 +0,0 @@
import { type MetadataUniversalFlatEntityPropertiesToCompare } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/metadata-universal-flat-entity-properties-to-compare.type';
export const FIELD_METADATA_STANDARD_OVERRIDES_PROPERTIES = [
'label',
'description',
'icon',
] as const satisfies MetadataUniversalFlatEntityPropertiesToCompare<'fieldMetadata'>[];
@@ -15,7 +15,7 @@ export class CreateFieldInput extends OmitType(
'id',
'createdAt',
'updatedAt',
'standardOverrides',
'overrides',
'applicationId',
'morphId',
'universalIdentifier',
@@ -32,7 +32,7 @@ import {
import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
import { IsValidMetadataName } from 'src/engine/decorators/metadata/is-valid-metadata-name.decorator';
import { FieldStandardOverridesDTO } from 'src/engine/metadata-modules/field-metadata/dtos/field-standard-overrides.dto';
import { type FieldMetadataOverrides } from 'src/engine/metadata-modules/field-metadata/types/field-metadata-overrides.type';
import { type FieldMetadataDefaultOption } from 'src/engine/metadata-modules/field-metadata/dtos/options.input';
import { ObjectMetadataDTO } from 'src/engine/metadata-modules/object-metadata/dtos/object-metadata.dto';
import { transformEnumValue } from 'src/engine/utils/transform-enum-value';
@@ -94,9 +94,8 @@ export class FieldMetadataDTO<T extends FieldMetadataType = FieldMetadataType> {
@Field({ nullable: true })
icon?: string;
@IsOptional()
@Field(() => FieldStandardOverridesDTO, { nullable: true })
standardOverrides?: FieldStandardOverridesDTO;
@HideField()
overrides?: FieldMetadataOverrides | null;
@IsBoolean()
@IsOptional()
@@ -1,42 +0,0 @@
import { Field, ObjectType } from '@nestjs/graphql';
import { IsJSON, IsOptional, IsString } from 'class-validator';
import { GraphQLJSON } from 'graphql-type-json';
import { type APP_LOCALES } from 'twenty-shared/translations';
import { FieldMetadataStandardOverridesProperties } from 'src/engine/metadata-modules/field-metadata/types/field-metadata-standard-overrides-properties.type';
@ObjectType('StandardOverrides')
export class FieldStandardOverridesDTO implements Partial<
Record<FieldMetadataStandardOverridesProperties, unknown>
> {
@IsString()
@IsOptional()
@Field(() => String, { nullable: true })
label?: string | null;
@IsString()
@IsOptional()
@Field(() => String, { nullable: true })
description?: string | null;
@IsString()
@IsOptional()
@Field(() => String, { nullable: true })
icon?: string | null;
@IsJSON()
@IsOptional()
@Field(() => GraphQLJSON, {
nullable: true,
})
translations?: Partial<
Record<
keyof typeof APP_LOCALES,
{
label?: string | null;
description?: string | null;
}
>
> | null;
}
@@ -27,7 +27,7 @@ export class UpdateFieldInput extends OmitType(
'type',
'createdAt',
'updatedAt',
'standardOverrides',
'overrides',
'applicationId',
'morphId',
] as const,
@@ -23,8 +23,9 @@ import {
import { WasIntroducedInUpgrade } from 'src/engine/core-modules/upgrade/decorators/was-introduced-in-upgrade.decorator';
import { WasRemovedInUpgrade } from 'src/engine/core-modules/upgrade/decorators/was-removed-in-upgrade.decorator';
import { ADD_IS_SYSTEM_SIDE_EFFECT_UPGRADE_COMMAND_NAME } from 'src/database/commands/upgrade-version-command/2-15/is-system-side-effect-upgrade-command-name.constant';
import { ADD_METADATA_OVERRIDES_COLUMN_UPGRADE_COMMAND_NAME } from 'src/database/commands/upgrade-version-command/2-19/add-metadata-overrides-column-upgrade-command-name.constant';
import { RENAME_IS_UI_READ_ONLY_TO_IS_UI_EDITABLE_UPGRADE_COMMAND_NAME } from 'src/engine/metadata-modules/object-metadata/constants/rename-is-ui-read-only-to-is-ui-editable-upgrade-command-name.constant';
import { type FieldStandardOverridesDTO } from 'src/engine/metadata-modules/field-metadata/dtos/field-standard-overrides.dto';
import { type FieldMetadataOverrides } from 'src/engine/metadata-modules/field-metadata/types/field-metadata-overrides.type';
import { AssignIfIsGivenFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/types/assign-if-is-given-field-metadata-type.type';
import { AssignTypeIfIsMorphOrRelationFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/types/assign-type-if-is-morph-or-relation-field-metadata-type.type';
import { IndexFieldMetadataEntity } from 'src/engine/metadata-modules/index-metadata/index-field-metadata.entity';
@@ -102,8 +103,18 @@ export class FieldMetadataEntity<
@Column({ nullable: true, type: 'varchar' })
icon: string | null;
@WasIntroducedInUpgrade({
upgradeCommandName: ADD_METADATA_OVERRIDES_COLUMN_UPGRADE_COMMAND_NAME,
})
@Column({ type: 'jsonb', nullable: true })
standardOverrides: JsonbProperty<FieldStandardOverridesDTO> | null;
overrides: JsonbProperty<FieldMetadataOverrides> | null;
/**
* @deprecated Superseded by `overrides`; kept readable for pods on the
* previous release during a rolling deploy. Drop deferred to 2-20/README.md.
*/
@Column({ type: 'jsonb', nullable: true })
standardOverrides: WasRemovedInUpgrade<JsonbProperty<FieldMetadataOverrides> | null>;
@Column('jsonb', { nullable: true })
options: JsonbProperty<FieldMetadataOptions<TFieldMetadataType>>;
@@ -22,16 +22,16 @@ import { RelationDTO } from 'src/engine/metadata-modules/field-metadata/dtos/rel
import { UpdateOneFieldMetadataInput } from 'src/engine/metadata-modules/field-metadata/dtos/update-field.input';
import { FieldMetadataService } from 'src/engine/metadata-modules/field-metadata/services/field-metadata.service';
import { fieldMetadataGraphqlApiExceptionHandler } from 'src/engine/metadata-modules/field-metadata/utils/field-metadata-graphql-api-exception-handler.util';
import { resolveFieldMetadataStandardOverride } from 'src/engine/metadata-modules/field-metadata/utils/resolve-field-metadata-standard-override.util';
import { fromFlatFieldMetadataToFieldMetadataDto } from 'src/engine/metadata-modules/flat-field-metadata/utils/from-flat-field-metadata-to-field-metadata-dto.util';
import { resolveEffectiveEntityProperty } from 'src/engine/metadata-modules/utils/resolve-effective-entity-property.util';
import { PermissionsGraphqlApiExceptionFilter } from 'src/engine/metadata-modules/permissions/utils/permissions-graphql-api-exception.filter';
// Keep @Parent() structurally typed so ResolverValidationPipe does not validate
// FieldMetadataDTO date decorators on already-loaded parent records.
type FieldMetadataStandardOverrideParent = Parameters<
typeof resolveFieldMetadataStandardOverride
>[0] &
Pick<FieldMetadataDTO, 'applicationId'>;
type FieldMetadataStandardOverrideParent = Pick<
FieldMetadataDTO,
'label' | 'description' | 'icon' | 'overrides' | 'applicationId'
>;
@UseGuards(WorkspaceAuthGuard)
@UsePipes(ResolverValidationPipe)
@@ -76,14 +76,18 @@ export class FieldMetadataResolver {
locale: context.req.locale,
});
return resolveFieldMetadataStandardOverride(
fieldMetadata,
labelKey,
context.req.locale,
i18n,
isStandardApp,
applicationCatalog,
);
return resolveEffectiveEntityProperty({
metadataName: 'fieldMetadata',
baseValue: fieldMetadata[labelKey],
overrides: fieldMetadata.overrides,
property: labelKey,
i18nContext: {
locale: context.req.locale,
i18nInstance: i18n,
isStandardApp,
applicationCatalog,
},
});
}
@ResolveField(() => String, { nullable: true })
@@ -0,0 +1,16 @@
import { type APP_LOCALES } from 'twenty-shared/translations';
export type FieldMetadataOverrides = {
label?: string | null;
description?: string | null;
icon?: string | null;
translations?: Partial<
Record<
keyof typeof APP_LOCALES,
{
label?: string | null;
description?: string | null;
}
>
> | null;
};
@@ -1,4 +0,0 @@
import { type FIELD_METADATA_STANDARD_OVERRIDES_PROPERTIES } from 'src/engine/metadata-modules/field-metadata/constants/field-metadata-standard-overrides-properties.constant';
export type FieldMetadataStandardOverridesProperties =
(typeof FIELD_METADATA_STANDARD_OVERRIDES_PROPERTIES)[number];
@@ -1,511 +0,0 @@
import { type I18n } from '@lingui/core';
import { SOURCE_LOCALE } from 'twenty-shared/translations';
import { generateMessageId } from 'src/engine/core-modules/i18n/utils/generateMessageId';
import { resolveFieldMetadataStandardOverride } from 'src/engine/metadata-modules/field-metadata/utils/resolve-field-metadata-standard-override.util';
jest.mock('src/engine/core-modules/i18n/utils/generateMessageId');
const mockGenerateMessageId = generateMessageId as jest.MockedFunction<
typeof generateMessageId
>;
describe('resolveFieldMetadataStandardOverride', () => {
let mockI18n: jest.Mocked<I18n>;
beforeEach(() => {
jest.clearAllMocks();
mockI18n = {
_: jest.fn(),
} as unknown as jest.Mocked<I18n>;
});
describe('Custom fields', () => {
it('should return the field value for custom label field', () => {
const fieldMetadata = {
label: 'Custom Label',
description: 'Custom Description',
icon: 'custom-icon',
isCustom: true,
standardOverrides: undefined,
};
const result = resolveFieldMetadataStandardOverride(
fieldMetadata,
'label',
'fr-FR',
mockI18n,
!fieldMetadata.isCustom,
);
expect(result).toBe('Custom Label');
});
it('should never translate a custom label even when it matches a standard catalog entry', () => {
const fieldMetadata = {
label: 'Status',
description: 'Custom Description',
icon: 'custom-icon',
isCustom: true,
standardOverrides: undefined,
};
mockGenerateMessageId.mockReturnValue('status.message.id');
mockI18n._.mockReturnValue('Statut');
const result = resolveFieldMetadataStandardOverride(
fieldMetadata,
'label',
'fr-FR',
mockI18n,
!fieldMetadata.isCustom,
);
expect(result).toBe('Status');
expect(mockGenerateMessageId).not.toHaveBeenCalled();
expect(mockI18n._).not.toHaveBeenCalled();
});
it('should return the field value for custom description field', () => {
const fieldMetadata = {
label: 'Custom Label',
description: 'Custom Description',
icon: 'custom-icon',
isCustom: true,
standardOverrides: undefined,
};
const result = resolveFieldMetadataStandardOverride(
fieldMetadata,
'description',
undefined,
mockI18n,
!fieldMetadata.isCustom,
);
expect(result).toBe('Custom Description');
});
it('should return the field value for custom icon field', () => {
const fieldMetadata = {
label: 'Custom Label',
description: 'Custom Description',
icon: 'custom-icon',
isCustom: true,
standardOverrides: undefined,
};
const result = resolveFieldMetadataStandardOverride(
fieldMetadata,
'icon',
SOURCE_LOCALE,
mockI18n,
!fieldMetadata.isCustom,
);
expect(result).toBe('custom-icon');
});
});
describe('Standard fields - Icon overrides', () => {
it('should return override icon when available for standard field', () => {
const fieldMetadata = {
label: 'Standard Label',
description: 'Standard Description',
icon: 'default-icon',
isCustom: false,
standardOverrides: {
icon: 'override-icon',
},
};
const result = resolveFieldMetadataStandardOverride(
fieldMetadata,
'icon',
'fr-FR',
mockI18n,
!fieldMetadata.isCustom,
);
expect(result).toBe('override-icon');
});
});
describe('Standard fields - Translation overrides', () => {
it('should return translation override when available for non-icon fields', () => {
const fieldMetadata = {
label: 'Standard Label',
description: 'Standard Description',
icon: 'default-icon',
isCustom: false,
standardOverrides: {
translations: {
'fr-FR': {
label: 'Libellé traduit',
description: 'Description traduite',
},
},
},
};
expect(
resolveFieldMetadataStandardOverride(
fieldMetadata,
'label',
'fr-FR',
mockI18n,
!fieldMetadata.isCustom,
),
).toBe('Libellé traduit');
expect(
resolveFieldMetadataStandardOverride(
fieldMetadata,
'description',
'fr-FR',
mockI18n,
!fieldMetadata.isCustom,
),
).toBe('Description traduite');
});
it('should fallback when translation override is not available for the locale', () => {
const fieldMetadata = {
label: 'Standard Label',
description: 'Standard Description',
icon: 'default-icon',
isCustom: false,
standardOverrides: {
translations: {
'es-ES': {
label: 'Etiqueta en español',
},
},
},
};
mockGenerateMessageId.mockReturnValue('generated-message-id');
mockI18n._.mockReturnValue('generated-message-id');
const result = resolveFieldMetadataStandardOverride(
fieldMetadata,
'label',
'fr-FR',
mockI18n,
!fieldMetadata.isCustom,
);
expect(result).toBe('Standard Label');
});
it('should fallback when translation override is not available for the labelKey', () => {
const fieldMetadata = {
label: 'Standard Label',
description: 'Standard Description',
icon: 'default-icon',
isCustom: false,
standardOverrides: {
translations: {
'fr-FR': {
label: 'Libellé traduit',
},
},
},
};
mockGenerateMessageId.mockReturnValue('generated-message-id');
mockI18n._.mockReturnValue('generated-message-id');
const result = resolveFieldMetadataStandardOverride(
fieldMetadata,
'description',
'fr-FR',
mockI18n,
!fieldMetadata.isCustom,
);
expect(result).toBe('Standard Description');
});
it('should not use translation overrides when locale is undefined', () => {
const fieldMetadata = {
label: 'Standard Label',
description: 'Standard Description',
icon: 'default-icon',
isCustom: false,
standardOverrides: {
translations: {
'fr-FR': {
label: 'Libellé traduit',
},
},
},
};
mockGenerateMessageId.mockReturnValue('generated-message-id');
mockI18n._.mockReturnValue('generated-message-id');
const result = resolveFieldMetadataStandardOverride(
fieldMetadata,
'label',
undefined,
mockI18n,
!fieldMetadata.isCustom,
);
expect(result).toBe('Standard Label');
});
});
describe('Standard fields - SOURCE_LOCALE overrides', () => {
it('should return direct override for SOURCE_LOCALE when available', () => {
const fieldMetadata = {
label: 'Standard Label',
description: 'Standard Description',
icon: 'default-icon',
isCustom: false,
standardOverrides: {
label: 'Overridden Label',
description: 'Overridden Description',
icon: 'overridden-icon',
},
};
expect(
resolveFieldMetadataStandardOverride(
fieldMetadata,
'label',
SOURCE_LOCALE,
mockI18n,
!fieldMetadata.isCustom,
),
).toBe('Overridden Label');
expect(
resolveFieldMetadataStandardOverride(
fieldMetadata,
'description',
SOURCE_LOCALE,
mockI18n,
!fieldMetadata.isCustom,
),
).toBe('Overridden Description');
expect(
resolveFieldMetadataStandardOverride(
fieldMetadata,
'icon',
SOURCE_LOCALE,
mockI18n,
!fieldMetadata.isCustom,
),
).toBe('overridden-icon');
});
it('should use direct override for non-SOURCE_LOCALE when translation override is missing', () => {
const fieldMetadata = {
label: 'Standard Label',
description: 'Standard Description',
icon: 'default-icon',
isCustom: false,
standardOverrides: {
label: 'Overridden Label',
},
};
mockGenerateMessageId.mockReturnValue('generated-message-id');
mockI18n._.mockReturnValue('generated-message-id');
const result = resolveFieldMetadataStandardOverride(
fieldMetadata,
'label',
'fr-FR',
mockI18n,
!fieldMetadata.isCustom,
);
expect(result).toBe('Overridden Label');
expect(mockGenerateMessageId).not.toHaveBeenCalled();
expect(mockI18n._).not.toHaveBeenCalled();
});
it('should not use empty string override for SOURCE_LOCALE', () => {
const fieldMetadata = {
label: 'Standard Label',
description: 'Standard Description',
icon: 'default-icon',
isCustom: false,
standardOverrides: {
label: '',
},
};
mockGenerateMessageId.mockReturnValue('generated-message-id');
mockI18n._.mockReturnValue('generated-message-id');
const result = resolveFieldMetadataStandardOverride(
fieldMetadata,
'label',
SOURCE_LOCALE,
mockI18n,
!fieldMetadata.isCustom,
);
expect(result).toBe('Standard Label');
});
it('should not use undefined override for SOURCE_LOCALE', () => {
const fieldMetadata = {
label: 'Standard Label',
description: 'Standard Description',
icon: 'default-icon',
isCustom: false,
standardOverrides: {
label: undefined,
},
};
mockGenerateMessageId.mockReturnValue('generated-message-id');
mockI18n._.mockReturnValue('generated-message-id');
const result = resolveFieldMetadataStandardOverride(
fieldMetadata,
'label',
SOURCE_LOCALE,
mockI18n,
!fieldMetadata.isCustom,
);
expect(result).toBe('Standard Label');
});
});
describe('Standard fields - Auto translation fallback', () => {
it('should return translated message when translation is available', () => {
const fieldMetadata = {
label: 'Standard Label',
description: 'Standard Description',
icon: 'default-icon',
isCustom: false,
standardOverrides: undefined,
};
mockGenerateMessageId.mockReturnValue('standard.label.message.id');
mockI18n._.mockReturnValue('Libellé traduit automatiquement');
const result = resolveFieldMetadataStandardOverride(
fieldMetadata,
'label',
'fr-FR',
mockI18n,
!fieldMetadata.isCustom,
);
expect(mockGenerateMessageId).toHaveBeenCalledWith('Standard Label');
expect(mockI18n._).toHaveBeenCalledWith('standard.label.message.id');
expect(result).toBe('Libellé traduit automatiquement');
});
it('should return original field value when no translation is found', () => {
const fieldMetadata = {
label: 'Standard Label',
description: 'Standard Description',
icon: 'default-icon',
isCustom: false,
standardOverrides: undefined,
};
const messageId = 'standard.label.message.id';
mockGenerateMessageId.mockReturnValue(messageId);
mockI18n._.mockReturnValue(messageId);
const result = resolveFieldMetadataStandardOverride(
fieldMetadata,
'label',
'fr-FR',
mockI18n,
!fieldMetadata.isCustom,
);
expect(result).toBe('Standard Label');
});
});
describe('Priority order - Standard fields', () => {
it('should prioritize translation override over SOURCE_LOCALE override for non-SOURCE_LOCALE', () => {
const fieldMetadata = {
label: 'Standard Label',
description: 'Standard Description',
icon: 'default-icon',
isCustom: false,
standardOverrides: {
label: 'Source Override',
translations: {
'fr-FR': {
label: 'Translation Override',
},
},
},
};
const result = resolveFieldMetadataStandardOverride(
fieldMetadata,
'label',
'fr-FR',
mockI18n,
!fieldMetadata.isCustom,
);
expect(result).toBe('Translation Override');
expect(mockGenerateMessageId).not.toHaveBeenCalled();
expect(mockI18n._).not.toHaveBeenCalled();
});
it('should prioritize SOURCE_LOCALE override over auto translation for SOURCE_LOCALE', () => {
const fieldMetadata = {
label: 'Standard Label',
description: 'Standard Description',
icon: 'default-icon',
isCustom: false,
standardOverrides: {
label: 'Source Override',
},
};
const result = resolveFieldMetadataStandardOverride(
fieldMetadata,
'label',
SOURCE_LOCALE,
mockI18n,
!fieldMetadata.isCustom,
);
expect(result).toBe('Source Override');
expect(mockGenerateMessageId).not.toHaveBeenCalled();
expect(mockI18n._).not.toHaveBeenCalled();
});
it('should use auto translation when no overrides are available', () => {
const fieldMetadata = {
label: 'Standard Label',
description: 'Standard Description',
icon: 'default-icon',
isCustom: false,
standardOverrides: {},
};
mockGenerateMessageId.mockReturnValue('auto.translation.id');
mockI18n._.mockReturnValue('Auto Translated Label');
const result = resolveFieldMetadataStandardOverride(
fieldMetadata,
'label',
'de-DE',
mockI18n,
!fieldMetadata.isCustom,
);
expect(result).toBe('Auto Translated Label');
expect(mockGenerateMessageId).toHaveBeenCalledWith('Standard Label');
expect(mockI18n._).toHaveBeenCalledWith('auto.translation.id');
});
});
});
@@ -17,7 +17,7 @@ export const fromFieldMetadataEntityToFieldMetadataDto = (
label: entity.label,
description: entity.description ?? undefined,
icon: entity.icon ?? undefined,
standardOverrides: entity.standardOverrides ?? undefined,
overrides: entity.overrides ?? undefined,
isActive: entity.isActive,
isSystem: entity.isSystem,
isUIEditable: entity.isUIEditable,
@@ -1,52 +0,0 @@
import { type I18n } from '@lingui/core';
import { isNonEmptyString } from '@sniptt/guards';
import { type APP_LOCALES, SOURCE_LOCALE } from 'twenty-shared/translations';
import { isDefined } from 'twenty-shared/utils';
import { translateStandardLabel } from 'src/engine/core-modules/i18n/utils/translate-standard-label.util';
import { type FieldMetadataDTO } from 'src/engine/metadata-modules/field-metadata/dtos/field-metadata.dto';
export const resolveFieldMetadataStandardOverride = (
fieldMetadata: Pick<
FieldMetadataDTO,
'label' | 'description' | 'icon' | 'standardOverrides'
>,
labelKey: 'label' | 'description' | 'icon',
locale: keyof typeof APP_LOCALES | undefined,
i18nInstance: I18n,
isStandardApp: boolean,
applicationCatalog?: Record<string, string>,
): string => {
const safeLocale = locale ?? SOURCE_LOCALE;
if (!isStandardApp && !isDefined(applicationCatalog)) {
return fieldMetadata[labelKey] ?? '';
}
if (labelKey === 'icon' && isDefined(fieldMetadata.standardOverrides?.icon)) {
return fieldMetadata.standardOverrides.icon;
}
if (
isDefined(fieldMetadata.standardOverrides?.translations) &&
labelKey !== 'icon'
) {
const translationValue =
fieldMetadata.standardOverrides.translations[safeLocale]?.[labelKey];
if (isDefined(translationValue)) {
return translationValue;
}
}
if (isNonEmptyString(fieldMetadata.standardOverrides?.[labelKey])) {
return fieldMetadata.standardOverrides[labelKey] ?? '';
}
return translateStandardLabel({
sourceValue: fieldMetadata[labelKey] ?? '',
isStandardApp,
applicationCatalog,
i18nInstance,
});
};
@@ -72,7 +72,7 @@ exports[`ALL_UNIVERSAL_FLAT_ENTITY_PROPERTIES_TO_COMPARE_AND_STRINGIFY should ma
"label",
"name",
"options",
"standardOverrides",
"overrides",
"universalSettings",
"isUIEditable",
"isNullable",
@@ -80,7 +80,7 @@ exports[`ALL_UNIVERSAL_FLAT_ENTITY_PROPERTIES_TO_COMPARE_AND_STRINGIFY should ma
"propertiesToStringify": [
"defaultValue",
"options",
"standardOverrides",
"overrides",
"universalSettings",
],
},
@@ -171,14 +171,14 @@ exports[`ALL_UNIVERSAL_FLAT_ENTITY_PROPERTIES_TO_COMPARE_AND_STRINGIFY should ma
"namePlural",
"nameSingular",
"labelIdentifierFieldMetadataUniversalIdentifier",
"standardOverrides",
"overrides",
"isUIEditable",
"isUICreatable",
"isSearchable",
"imageIdentifierFieldMetadataUniversalIdentifier",
],
"propertiesToStringify": [
"standardOverrides",
"overrides",
],
},
"objectPermission": {
@@ -0,0 +1,137 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`registry-derived override property maps derives the overridable properties for every metadata entity 1`] = `
{
"agent": [],
"applicationVariable": [],
"commandMenuItem": [
"label",
"icon",
"shortLabel",
"position",
"isPinned",
"availabilityType",
"availabilityObjectMetadataId",
"engineComponentKey",
"hotKeys",
"pageLayoutId",
],
"connectionProvider": [],
"fieldMetadata": [
"description",
"icon",
"label",
],
"fieldPermission": [],
"frontComponent": [],
"index": [],
"logicFunction": [],
"navigationMenuItem": [],
"objectMetadata": [
"color",
"description",
"icon",
"labelPlural",
"labelSingular",
],
"objectPermission": [],
"pageLayout": [],
"pageLayoutTab": [
"title",
"position",
"icon",
],
"pageLayoutWidget": [
"title",
"position",
"pageLayoutTabId",
"conditionalDisplay",
"conditionalAvailabilityExpression",
],
"permissionFlag": [],
"role": [],
"rolePermissionFlag": [],
"roleTarget": [],
"rowLevelPermissionPredicate": [],
"rowLevelPermissionPredicateGroup": [],
"searchFieldMetadata": [],
"skill": [],
"view": [
"name",
"type",
"icon",
"position",
"isCompact",
"openRecordIn",
"kanbanAggregateOperation",
"kanbanAggregateOperationFieldMetadataId",
"anyFieldFilterValue",
"calendarLayout",
"calendarFieldMetadataId",
"visibility",
"mainGroupByFieldMetadataId",
"shouldHideEmptyGroups",
"kanbanColumnWidth",
],
"viewField": [
"isVisible",
"size",
"position",
"aggregateOperation",
"viewFieldGroupId",
],
"viewFieldGroup": [
"name",
"position",
"isVisible",
],
"viewFilter": [],
"viewFilterGroup": [],
"viewGroup": [],
"viewSort": [],
"webhook": [],
}
`;
exports[`registry-derived override property maps derives the translatable properties for every metadata entity 1`] = `
{
"agent": [],
"applicationVariable": [],
"commandMenuItem": [],
"connectionProvider": [],
"fieldMetadata": [
"description",
"label",
],
"fieldPermission": [],
"frontComponent": [],
"index": [],
"logicFunction": [],
"navigationMenuItem": [],
"objectMetadata": [
"description",
"labelPlural",
"labelSingular",
],
"objectPermission": [],
"pageLayout": [],
"pageLayoutTab": [],
"pageLayoutWidget": [],
"permissionFlag": [],
"role": [],
"rolePermissionFlag": [],
"roleTarget": [],
"rowLevelPermissionPredicate": [],
"rowLevelPermissionPredicateGroup": [],
"searchFieldMetadata": [],
"skill": [],
"view": [],
"viewField": [],
"viewFieldGroup": [],
"viewFilter": [],
"viewFilterGroup": [],
"viewGroup": [],
"viewSort": [],
"webhook": [],
}
`;
@@ -0,0 +1,12 @@
import { ALL_OVERRIDABLE_PROPERTIES_BY_METADATA_NAME } from 'src/engine/metadata-modules/flat-entity/constant/all-overridable-properties-by-metadata-name.constant';
import { ALL_TRANSLATABLE_PROPERTIES_BY_METADATA_NAME } from 'src/engine/metadata-modules/flat-entity/constant/all-translatable-properties-by-metadata-name.constant';
describe('registry-derived override property maps', () => {
it('derives the overridable properties for every metadata entity', () => {
expect(ALL_OVERRIDABLE_PROPERTIES_BY_METADATA_NAME).toMatchSnapshot();
});
it('derives the translatable properties for every metadata entity', () => {
expect(ALL_TRANSLATABLE_PROPERTIES_BY_METADATA_NAME).toMatchSnapshot();
});
});
@@ -40,6 +40,7 @@ type MetadataEntityPropertyConfiguration<
: boolean;
toCompare: boolean;
isOverridable?: boolean;
translatable?: boolean;
};
};
@@ -59,8 +60,15 @@ export const ALL_ENTITY_PROPERTIES_CONFIGURATION_BY_METADATA_NAME = {
toCompare: true,
toStringify: false,
universalProperty: undefined,
isOverridable: true,
translatable: true,
},
icon: {
toCompare: true,
toStringify: false,
universalProperty: undefined,
isOverridable: true,
},
icon: { toCompare: true, toStringify: false, universalProperty: undefined },
isActive: {
toCompare: true,
toStringify: false,
@@ -86,6 +94,8 @@ export const ALL_ENTITY_PROPERTIES_CONFIGURATION_BY_METADATA_NAME = {
toCompare: true,
toStringify: false,
universalProperty: undefined,
isOverridable: true,
translatable: true,
},
name: { toCompare: true, toStringify: false, universalProperty: undefined },
options: {
@@ -93,7 +103,7 @@ export const ALL_ENTITY_PROPERTIES_CONFIGURATION_BY_METADATA_NAME = {
toStringify: true,
universalProperty: undefined,
},
standardOverrides: {
overrides: {
toCompare: true,
toStringify: true,
universalProperty: undefined,
@@ -164,13 +174,21 @@ export const ALL_ENTITY_PROPERTIES_CONFIGURATION_BY_METADATA_NAME = {
toCompare: true,
toStringify: false,
universalProperty: undefined,
isOverridable: true,
},
description: {
toCompare: true,
toStringify: false,
universalProperty: undefined,
isOverridable: true,
translatable: true,
},
icon: {
toCompare: true,
toStringify: false,
universalProperty: undefined,
isOverridable: true,
},
icon: { toCompare: true, toStringify: false, universalProperty: undefined },
isActive: {
toCompare: true,
toStringify: false,
@@ -185,11 +203,15 @@ export const ALL_ENTITY_PROPERTIES_CONFIGURATION_BY_METADATA_NAME = {
toCompare: true,
toStringify: false,
universalProperty: undefined,
isOverridable: true,
translatable: true,
},
labelSingular: {
toCompare: true,
toStringify: false,
universalProperty: undefined,
isOverridable: true,
translatable: true,
},
namePlural: {
toCompare: true,
@@ -207,7 +229,7 @@ export const ALL_ENTITY_PROPERTIES_CONFIGURATION_BY_METADATA_NAME = {
// @ts-expect-error remove once https://github.com/twentyhq/core-team-issues/issues/2172 has been resolved
universalProperty: 'labelIdentifierFieldMetadataUniversalIdentifier',
},
standardOverrides: {
overrides: {
toCompare: true,
toStringify: true,
universalProperty: undefined,
@@ -1830,3 +1852,12 @@ export type MetadataEntityOverridablePropertyName<T extends AllMetadataName> =
FilterOverridableKeys<
(typeof ALL_ENTITY_PROPERTIES_CONFIGURATION_BY_METADATA_NAME)[T]
>;
type FilterTranslatableKeys<TConfig> = {
[P in keyof TConfig]: TConfig[P] extends { translatable: true } ? P : never;
}[keyof TConfig];
export type MetadataEntityTranslatablePropertyName<T extends AllMetadataName> =
FilterTranslatableKeys<
(typeof ALL_ENTITY_PROPERTIES_CONFIGURATION_BY_METADATA_NAME)[T]
>;
@@ -0,0 +1,32 @@
import {
ALL_METADATA_NAME,
type AllMetadataName,
} from 'twenty-shared/metadata';
import {
ALL_ENTITY_PROPERTIES_CONFIGURATION_BY_METADATA_NAME,
type MetadataEntityTranslatablePropertyName,
} from 'src/engine/metadata-modules/flat-entity/constant/all-entity-properties-configuration-by-metadata-name.constant';
const computeTranslatableProperties = <T extends AllMetadataName>(
metadataName: T,
): MetadataEntityTranslatablePropertyName<T>[] => {
const config =
ALL_ENTITY_PROPERTIES_CONFIGURATION_BY_METADATA_NAME[metadataName];
return (Object.entries(config) as [string, { translatable?: boolean }][])
.filter(([_, conf]) => conf.translatable === true)
.map(([property]) => property as MetadataEntityTranslatablePropertyName<T>);
};
export const ALL_TRANSLATABLE_PROPERTIES_BY_METADATA_NAME = Object.values(
ALL_METADATA_NAME,
).reduce(
(acc, metadataName) => ({
...acc,
[metadataName]: computeTranslatableProperties(metadataName),
}),
{} as {
[P in AllMetadataName]: MetadataEntityTranslatablePropertyName<P>[];
},
);
@@ -17,7 +17,7 @@ type Assertions = [
| 'description'
| 'isActive'
| 'defaultValue'
| 'standardOverrides'
| 'overrides'
| 'options'
| 'settings'
| 'isUnique'
@@ -43,7 +43,7 @@ type Assertions = [
| 'color'
| 'description'
| 'isActive'
| 'standardOverrides'
| 'overrides'
| 'isLabelSyncedWithName'
| 'nameSingular'
| 'namePlural'
@@ -19,7 +19,7 @@ export const ATTACHMENT_FLAT_FIELDS_MOCK = {
defaultValue: "''",
description: 'Attachment name',
icon: 'IconFileUpload',
standardOverrides: null,
overrides: null,
options: null,
settings: null,
isActive: true,
@@ -41,7 +41,7 @@ export const ATTACHMENT_FLAT_FIELDS_MOCK = {
defaultValue: "''",
description: 'Attachment full path',
icon: 'IconLink',
standardOverrides: null,
overrides: null,
options: null,
settings: null,
isActive: true,
@@ -63,7 +63,7 @@ export const ATTACHMENT_FLAT_FIELDS_MOCK = {
defaultValue: "''",
description: 'Attachment type',
icon: 'IconList',
standardOverrides: null,
overrides: null,
options: null,
settings: null,
isActive: true,
@@ -85,7 +85,7 @@ export const ATTACHMENT_FLAT_FIELDS_MOCK = {
defaultValue: 'uuid',
description: 'Id',
icon: 'Icon123',
standardOverrides: null,
overrides: null,
options: null,
settings: null,
isActive: true,
@@ -107,7 +107,7 @@ export const ATTACHMENT_FLAT_FIELDS_MOCK = {
defaultValue: 'now',
description: 'Creation date',
icon: 'IconCalendar',
standardOverrides: null,
overrides: null,
options: null,
settings: { displayFormat: DateDisplayFormat.RELATIVE },
isActive: true,
@@ -129,7 +129,7 @@ export const ATTACHMENT_FLAT_FIELDS_MOCK = {
defaultValue: 'now',
description: 'Last time the record was changed',
icon: 'IconCalendarClock',
standardOverrides: null,
overrides: null,
options: null,
settings: { displayFormat: DateDisplayFormat.RELATIVE },
isActive: true,
@@ -151,7 +151,7 @@ export const ATTACHMENT_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'Date when the record was deleted',
icon: 'IconCalendarMinus',
standardOverrides: null,
overrides: null,
options: null,
settings: { displayFormat: DateDisplayFormat.RELATIVE },
isActive: true,
@@ -173,7 +173,7 @@ export const ATTACHMENT_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'Attachment author',
icon: 'IconCircleUser',
standardOverrides: null,
overrides: null,
options: null,
settings: {
onDelete: RelationOnDeleteAction.SET_NULL,
@@ -199,7 +199,7 @@ export const ATTACHMENT_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'Attachment task',
icon: 'IconNotes',
standardOverrides: null,
overrides: null,
options: null,
settings: {
onDelete: RelationOnDeleteAction.SET_NULL,
@@ -225,7 +225,7 @@ export const ATTACHMENT_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'Attachment note',
icon: 'IconNotes',
standardOverrides: null,
overrides: null,
options: null,
settings: {
onDelete: RelationOnDeleteAction.SET_NULL,
@@ -251,7 +251,7 @@ export const ATTACHMENT_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'Attachment person',
icon: 'IconUser',
standardOverrides: null,
overrides: null,
options: null,
settings: {
onDelete: RelationOnDeleteAction.CASCADE,
@@ -277,7 +277,7 @@ export const ATTACHMENT_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'Attachment company',
icon: 'IconBuildingSkyscraper',
standardOverrides: null,
overrides: null,
options: null,
settings: {
onDelete: RelationOnDeleteAction.CASCADE,
@@ -303,7 +303,7 @@ export const ATTACHMENT_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'Attachment opportunity',
icon: 'IconBuildingSkyscraper',
standardOverrides: null,
overrides: null,
options: null,
settings: {
onDelete: RelationOnDeleteAction.CASCADE,
@@ -329,7 +329,7 @@ export const ATTACHMENT_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'Attachments Rocket',
icon: 'IconBuildingSkyscraper',
standardOverrides: null,
overrides: null,
options: null,
settings: {
onDelete: RelationOnDeleteAction.CASCADE,
@@ -355,7 +355,7 @@ export const ATTACHMENT_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'Attachments Pet',
icon: 'IconBuildingSkyscraper',
standardOverrides: null,
overrides: null,
options: null,
settings: {
onDelete: RelationOnDeleteAction.CASCADE,
@@ -381,7 +381,7 @@ export const ATTACHMENT_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'Attachments Survey result',
icon: 'IconBuildingSkyscraper',
standardOverrides: null,
overrides: null,
options: null,
settings: {
onDelete: RelationOnDeleteAction.CASCADE,
@@ -19,7 +19,7 @@ export const COMPANY_FLAT_FIELDS_MOCK = {
defaultValue: "''",
description: 'The company name',
icon: 'IconBuildingSkyscraper',
standardOverrides: null,
overrides: null,
options: null,
settings: null,
isActive: true,
@@ -46,7 +46,7 @@ export const COMPANY_FLAT_FIELDS_MOCK = {
description:
'The company website URL. We use this url to fetch the company icon',
icon: 'IconLink',
standardOverrides: null,
overrides: null,
options: null,
settings: null,
isActive: true,
@@ -72,7 +72,7 @@ export const COMPANY_FLAT_FIELDS_MOCK = {
},
description: 'The company Linkedin account',
icon: 'IconBrandLinkedin',
standardOverrides: null,
overrides: null,
options: null,
settings: null,
isActive: true,
@@ -94,7 +94,7 @@ export const COMPANY_FLAT_FIELDS_MOCK = {
defaultValue: { amountMicros: null, currencyCode: "''" },
description: "The company's total annual revenue",
icon: 'IconMoneybag',
standardOverrides: null,
overrides: null,
options: null,
settings: null,
isActive: true,
@@ -125,7 +125,7 @@ export const COMPANY_FLAT_FIELDS_MOCK = {
},
description: 'Address of the company',
icon: 'IconMap',
standardOverrides: null,
overrides: null,
options: null,
settings: null,
isActive: true,
@@ -147,7 +147,7 @@ export const COMPANY_FLAT_FIELDS_MOCK = {
defaultValue: 0,
description: 'Company record position',
icon: 'IconHierarchy2',
standardOverrides: null,
overrides: null,
options: null,
settings: null,
isActive: true,
@@ -173,7 +173,7 @@ export const COMPANY_FLAT_FIELDS_MOCK = {
},
description: 'The creator of the record',
icon: 'IconCreativeCommonsSa',
standardOverrides: null,
overrides: null,
options: null,
settings: null,
isActive: true,
@@ -195,7 +195,7 @@ export const COMPANY_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'Field used for full-text search',
icon: 'IconUser',
standardOverrides: null,
overrides: null,
options: null,
settings: null,
isActive: true,
@@ -217,7 +217,7 @@ export const COMPANY_FLAT_FIELDS_MOCK = {
defaultValue: 'uuid',
description: 'Id',
icon: 'Icon123',
standardOverrides: null,
overrides: null,
options: null,
settings: null,
isActive: true,
@@ -239,7 +239,7 @@ export const COMPANY_FLAT_FIELDS_MOCK = {
defaultValue: 'now',
description: 'Creation date',
icon: 'IconCalendar',
standardOverrides: null,
overrides: null,
options: null,
settings: { displayFormat: DateDisplayFormat.RELATIVE },
isActive: true,
@@ -261,7 +261,7 @@ export const COMPANY_FLAT_FIELDS_MOCK = {
defaultValue: 'now',
description: 'Last time the record was changed',
icon: 'IconCalendarClock',
standardOverrides: null,
overrides: null,
options: null,
settings: { displayFormat: DateDisplayFormat.RELATIVE },
isActive: true,
@@ -283,7 +283,7 @@ export const COMPANY_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'Date when the record was deleted',
icon: 'IconCalendarMinus',
standardOverrides: null,
overrides: null,
options: null,
settings: { displayFormat: DateDisplayFormat.RELATIVE },
isActive: true,
@@ -305,7 +305,7 @@ export const COMPANY_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'People linked to the company.',
icon: 'IconUsers',
standardOverrides: null,
overrides: null,
options: null,
settings: { relationType: RelationType.ONE_TO_MANY },
isActive: true,
@@ -328,7 +328,7 @@ export const COMPANY_FLAT_FIELDS_MOCK = {
description:
'Your team member responsible for managing the company account',
icon: 'IconUserCircle',
standardOverrides: null,
overrides: null,
options: null,
settings: {
onDelete: RelationOnDeleteAction.SET_NULL,
@@ -354,7 +354,7 @@ export const COMPANY_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'Tasks tied to the company',
icon: 'IconCheckbox',
standardOverrides: null,
overrides: null,
options: null,
settings: { relationType: RelationType.ONE_TO_MANY },
isActive: true,
@@ -376,7 +376,7 @@ export const COMPANY_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'Notes tied to the company',
icon: 'IconNotes',
standardOverrides: null,
overrides: null,
options: null,
settings: { relationType: RelationType.ONE_TO_MANY },
isActive: true,
@@ -398,7 +398,7 @@ export const COMPANY_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'Opportunities linked to the company.',
icon: 'IconTargetArrow',
standardOverrides: null,
overrides: null,
options: null,
settings: { relationType: RelationType.ONE_TO_MANY },
isActive: true,
@@ -420,7 +420,7 @@ export const COMPANY_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'Attachments linked to the company',
icon: 'IconFileImport',
standardOverrides: null,
overrides: null,
options: null,
settings: { relationType: RelationType.ONE_TO_MANY },
isActive: true,
@@ -442,7 +442,7 @@ export const COMPANY_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'Timeline Activities linked to the company',
icon: 'IconTimelineEvent',
standardOverrides: null,
overrides: null,
options: null,
settings: { relationType: RelationType.ONE_TO_MANY },
isActive: true,
@@ -464,7 +464,7 @@ export const COMPANY_FLAT_FIELDS_MOCK = {
defaultValue: "''",
description: null,
icon: 'IconAdCircle',
standardOverrides: null,
overrides: null,
options: null,
settings: null,
isActive: true,
@@ -490,7 +490,7 @@ export const COMPANY_FLAT_FIELDS_MOCK = {
},
description: null,
icon: 'IconVideo',
standardOverrides: null,
overrides: null,
options: null,
settings: null,
isActive: true,
@@ -512,7 +512,7 @@ export const COMPANY_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: null,
icon: 'IconHome',
standardOverrides: null,
overrides: null,
options: [
{
id: '325240d6-8c67-4fb2-bb02-51e9de4cd6be',
@@ -556,7 +556,7 @@ export const COMPANY_FLAT_FIELDS_MOCK = {
defaultValue: false,
description: null,
icon: 'IconBrandVisa',
standardOverrides: null,
overrides: null,
options: null,
settings: null,
isActive: true,
@@ -40,7 +40,7 @@ export const getFlatFieldMetadataMock = <T extends FieldMetadataType>(
isUIEditable: true,
isLabelSyncedWithName: false,
isSystem: false,
standardOverrides: null,
overrides: null,
workspaceId: faker.string.uuid(),
applicationId: faker.string.uuid(),
relationTargetFieldMetadataId: null,
@@ -68,7 +68,7 @@ export const getStandardFlatFieldMetadataMock = (
overrides: Omit<FlatFieldMetadataOverrides, 'isCustom' | 'isSystem'>,
) => {
return getFlatFieldMetadataMock({
standardOverrides: {},
overrides: {},
isSystem: true,
...overrides,
});
@@ -53,7 +53,7 @@ export const getRelationTargetFlatFieldMetadataMock = ({
isUIEditable: true,
isLabelSyncedWithName: false,
isSystem: false,
standardOverrides: null,
overrides: null,
workspaceId: faker.string.uuid(),
objectMetadataId,
type,
@@ -15,7 +15,7 @@ export const NOTE_FLAT_FIELDS_MOCK = {
defaultValue: 0,
description: 'Note record position',
icon: 'IconHierarchy2',
standardOverrides: null,
overrides: null,
options: null,
settings: null,
isActive: true,
@@ -37,7 +37,7 @@ export const NOTE_FLAT_FIELDS_MOCK = {
defaultValue: "''",
description: 'Note title',
icon: 'IconNotes',
standardOverrides: null,
overrides: null,
options: null,
settings: null,
isActive: true,
@@ -59,7 +59,7 @@ export const NOTE_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'Note body',
icon: 'IconFilePencil',
standardOverrides: null,
overrides: null,
options: null,
settings: null,
isActive: true,
@@ -85,7 +85,7 @@ export const NOTE_FLAT_FIELDS_MOCK = {
},
description: 'The creator of the record',
icon: 'IconCreativeCommonsSa',
standardOverrides: null,
overrides: null,
options: null,
settings: null,
isActive: true,
@@ -107,7 +107,7 @@ export const NOTE_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'Field used for full-text search',
icon: 'IconUser',
standardOverrides: null,
overrides: null,
options: null,
settings: null,
isActive: true,
@@ -129,7 +129,7 @@ export const NOTE_FLAT_FIELDS_MOCK = {
defaultValue: 'uuid',
description: 'Id',
icon: 'Icon123',
standardOverrides: null,
overrides: null,
options: null,
settings: null,
isActive: true,
@@ -151,7 +151,7 @@ export const NOTE_FLAT_FIELDS_MOCK = {
defaultValue: 'now',
description: 'Creation date',
icon: 'IconCalendar',
standardOverrides: null,
overrides: null,
options: null,
settings: { displayFormat: DateDisplayFormat.RELATIVE },
isActive: true,
@@ -173,7 +173,7 @@ export const NOTE_FLAT_FIELDS_MOCK = {
defaultValue: 'now',
description: 'Last time the record was changed',
icon: 'IconCalendarClock',
standardOverrides: null,
overrides: null,
options: null,
settings: { displayFormat: DateDisplayFormat.RELATIVE },
isActive: true,
@@ -195,7 +195,7 @@ export const NOTE_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'Date when the record was deleted',
icon: 'IconCalendarMinus',
standardOverrides: null,
overrides: null,
options: null,
settings: { displayFormat: DateDisplayFormat.RELATIVE },
isActive: true,
@@ -217,7 +217,7 @@ export const NOTE_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'Note targets',
icon: 'IconArrowUpRight',
standardOverrides: null,
overrides: null,
options: null,
settings: { relationType: RelationType.ONE_TO_MANY },
isActive: true,
@@ -239,7 +239,7 @@ export const NOTE_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'Note attachments',
icon: 'IconFileImport',
standardOverrides: null,
overrides: null,
options: null,
settings: { relationType: RelationType.ONE_TO_MANY },
isActive: true,
@@ -261,7 +261,7 @@ export const NOTE_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'Timeline Activities linked to the note.',
icon: 'IconTimelineEvent',
standardOverrides: null,
overrides: null,
options: null,
settings: { relationType: RelationType.ONE_TO_MANY },
isActive: true,
@@ -19,7 +19,7 @@ export const NOTETARGET_FLAT_FIELDS_MOCK = {
defaultValue: 'uuid',
description: 'Id',
icon: 'Icon123',
standardOverrides: null,
overrides: null,
options: null,
settings: null,
isActive: true,
@@ -41,7 +41,7 @@ export const NOTETARGET_FLAT_FIELDS_MOCK = {
defaultValue: 'now',
description: 'Creation date',
icon: 'IconCalendar',
standardOverrides: null,
overrides: null,
options: null,
settings: { displayFormat: DateDisplayFormat.RELATIVE },
isActive: true,
@@ -63,7 +63,7 @@ export const NOTETARGET_FLAT_FIELDS_MOCK = {
defaultValue: 'now',
description: 'Last time the record was changed',
icon: 'IconCalendarClock',
standardOverrides: null,
overrides: null,
options: null,
settings: { displayFormat: DateDisplayFormat.RELATIVE },
isActive: true,
@@ -85,7 +85,7 @@ export const NOTETARGET_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'Date when the record was deleted',
icon: 'IconCalendarMinus',
standardOverrides: null,
overrides: null,
options: null,
settings: { displayFormat: DateDisplayFormat.RELATIVE },
isActive: true,
@@ -107,7 +107,7 @@ export const NOTETARGET_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'NoteTarget note',
icon: 'IconNotes',
standardOverrides: null,
overrides: null,
options: null,
settings: {
onDelete: RelationOnDeleteAction.SET_NULL,
@@ -133,7 +133,7 @@ export const NOTETARGET_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'NoteTarget person',
icon: 'IconUser',
standardOverrides: null,
overrides: null,
options: null,
settings: {
onDelete: RelationOnDeleteAction.CASCADE,
@@ -159,7 +159,7 @@ export const NOTETARGET_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'NoteTarget company',
icon: 'IconBuildingSkyscraper',
standardOverrides: null,
overrides: null,
options: null,
settings: {
onDelete: RelationOnDeleteAction.CASCADE,
@@ -185,7 +185,7 @@ export const NOTETARGET_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'NoteTarget opportunity',
icon: 'IconTargetArrow',
standardOverrides: null,
overrides: null,
options: null,
settings: {
onDelete: RelationOnDeleteAction.CASCADE,
@@ -211,7 +211,7 @@ export const NOTETARGET_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'NoteTargets Rocket',
icon: 'IconBuildingSkyscraper',
standardOverrides: null,
overrides: null,
options: null,
settings: {
onDelete: RelationOnDeleteAction.CASCADE,
@@ -237,7 +237,7 @@ export const NOTETARGET_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'NoteTargets Pet',
icon: 'IconBuildingSkyscraper',
standardOverrides: null,
overrides: null,
options: null,
settings: {
onDelete: RelationOnDeleteAction.CASCADE,
@@ -263,7 +263,7 @@ export const NOTETARGET_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'NoteTargets Survey result',
icon: 'IconBuildingSkyscraper',
standardOverrides: null,
overrides: null,
options: null,
settings: {
onDelete: RelationOnDeleteAction.CASCADE,
@@ -19,7 +19,7 @@ export const OPPORTUNITY_FLAT_FIELDS_MOCK = {
defaultValue: "''",
description: 'The opportunity name',
icon: 'IconTargetArrow',
standardOverrides: null,
overrides: null,
options: null,
settings: null,
isActive: true,
@@ -41,7 +41,7 @@ export const OPPORTUNITY_FLAT_FIELDS_MOCK = {
defaultValue: { amountMicros: null, currencyCode: "''" },
description: 'Opportunity amount',
icon: 'IconCurrencyDollar',
standardOverrides: null,
overrides: null,
options: null,
settings: null,
isActive: true,
@@ -63,7 +63,7 @@ export const OPPORTUNITY_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'Opportunity close date',
icon: 'IconCalendarEvent',
standardOverrides: null,
overrides: null,
options: null,
settings: null,
isActive: true,
@@ -85,7 +85,7 @@ export const OPPORTUNITY_FLAT_FIELDS_MOCK = {
defaultValue: "'NEW'",
description: 'Opportunity stage',
icon: 'IconProgressCheck',
standardOverrides: null,
overrides: null,
options: [
{
id: 'fdf5cede-2b0e-44d0-adbe-3bc7e613efc9',
@@ -143,7 +143,7 @@ export const OPPORTUNITY_FLAT_FIELDS_MOCK = {
defaultValue: 0,
description: 'Opportunity record position',
icon: 'IconHierarchy2',
standardOverrides: null,
overrides: null,
options: null,
settings: null,
isActive: true,
@@ -169,7 +169,7 @@ export const OPPORTUNITY_FLAT_FIELDS_MOCK = {
},
description: 'The creator of the record',
icon: 'IconCreativeCommonsSa',
standardOverrides: null,
overrides: null,
options: null,
settings: null,
isActive: true,
@@ -191,7 +191,7 @@ export const OPPORTUNITY_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'Field used for full-text search',
icon: 'IconUser',
standardOverrides: null,
overrides: null,
options: null,
settings: null,
isActive: true,
@@ -213,7 +213,7 @@ export const OPPORTUNITY_FLAT_FIELDS_MOCK = {
defaultValue: 'uuid',
description: 'Id',
icon: 'Icon123',
standardOverrides: null,
overrides: null,
options: null,
settings: null,
isActive: true,
@@ -235,7 +235,7 @@ export const OPPORTUNITY_FLAT_FIELDS_MOCK = {
defaultValue: 'now',
description: 'Creation date',
icon: 'IconCalendar',
standardOverrides: null,
overrides: null,
options: null,
settings: { displayFormat: DateDisplayFormat.RELATIVE },
isActive: true,
@@ -257,7 +257,7 @@ export const OPPORTUNITY_FLAT_FIELDS_MOCK = {
defaultValue: 'now',
description: 'Last time the record was changed',
icon: 'IconCalendarClock',
standardOverrides: null,
overrides: null,
options: null,
settings: { displayFormat: DateDisplayFormat.RELATIVE },
isActive: true,
@@ -279,7 +279,7 @@ export const OPPORTUNITY_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'Date when the record was deleted',
icon: 'IconCalendarMinus',
standardOverrides: null,
overrides: null,
options: null,
settings: { displayFormat: DateDisplayFormat.RELATIVE },
isActive: true,
@@ -301,7 +301,7 @@ export const OPPORTUNITY_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'Opportunity point of contact',
icon: 'IconUser',
standardOverrides: null,
overrides: null,
options: null,
settings: {
onDelete: RelationOnDeleteAction.SET_NULL,
@@ -327,7 +327,7 @@ export const OPPORTUNITY_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'Opportunity company',
icon: 'IconBuildingSkyscraper',
standardOverrides: null,
overrides: null,
options: null,
settings: {
onDelete: RelationOnDeleteAction.SET_NULL,
@@ -353,7 +353,7 @@ export const OPPORTUNITY_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'Tasks tied to the opportunity',
icon: 'IconCheckbox',
standardOverrides: null,
overrides: null,
options: null,
settings: { relationType: RelationType.ONE_TO_MANY },
isActive: true,
@@ -375,7 +375,7 @@ export const OPPORTUNITY_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'Notes tied to the opportunity',
icon: 'IconNotes',
standardOverrides: null,
overrides: null,
options: null,
settings: { relationType: RelationType.ONE_TO_MANY },
isActive: true,
@@ -397,7 +397,7 @@ export const OPPORTUNITY_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'Attachments linked to the opportunity',
icon: 'IconFileImport',
standardOverrides: null,
overrides: null,
options: null,
settings: { relationType: RelationType.ONE_TO_MANY },
isActive: true,
@@ -419,7 +419,7 @@ export const OPPORTUNITY_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'Timeline Activities linked to the opportunity.',
icon: 'IconTimelineEvent',
standardOverrides: null,
overrides: null,
options: null,
settings: { relationType: RelationType.ONE_TO_MANY },
isActive: true,
@@ -19,7 +19,7 @@ export const PERSON_FLAT_FIELDS_MOCK = {
defaultValue: { lastName: "''", firstName: "''" },
description: 'Contacts name',
icon: 'IconUser',
standardOverrides: null,
overrides: null,
options: null,
settings: null,
isActive: true,
@@ -41,7 +41,7 @@ export const PERSON_FLAT_FIELDS_MOCK = {
defaultValue: { primaryEmail: "''", additionalEmails: null },
description: 'Contacts Emails',
icon: 'IconMail',
standardOverrides: null,
overrides: null,
options: null,
settings: null,
isActive: true,
@@ -67,7 +67,7 @@ export const PERSON_FLAT_FIELDS_MOCK = {
},
description: 'Contacts Linkedin account',
icon: 'IconBrandLinkedin',
standardOverrides: null,
overrides: null,
options: null,
settings: null,
isActive: true,
@@ -89,7 +89,7 @@ export const PERSON_FLAT_FIELDS_MOCK = {
defaultValue: "''",
description: 'Contacts job title',
icon: 'IconBriefcase',
standardOverrides: null,
overrides: null,
options: null,
settings: null,
isActive: true,
@@ -116,7 +116,7 @@ export const PERSON_FLAT_FIELDS_MOCK = {
},
description: 'Contacts phone numbers',
icon: 'IconPhone',
standardOverrides: null,
overrides: null,
options: null,
settings: null,
isActive: true,
@@ -138,7 +138,7 @@ export const PERSON_FLAT_FIELDS_MOCK = {
defaultValue: "''",
description: 'Contacts avatar',
icon: 'IconFileUpload',
standardOverrides: null,
overrides: null,
options: null,
settings: null,
isActive: true,
@@ -160,7 +160,7 @@ export const PERSON_FLAT_FIELDS_MOCK = {
defaultValue: 0,
description: 'Person record Position',
icon: 'IconHierarchy2',
standardOverrides: null,
overrides: null,
options: null,
settings: null,
isActive: true,
@@ -186,7 +186,7 @@ export const PERSON_FLAT_FIELDS_MOCK = {
},
description: 'The creator of the record',
icon: 'IconCreativeCommonsSa',
standardOverrides: null,
overrides: null,
options: null,
settings: null,
isActive: true,
@@ -208,7 +208,7 @@ export const PERSON_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'Field used for full-text search',
icon: 'IconUser',
standardOverrides: null,
overrides: null,
options: null,
settings: null,
isActive: true,
@@ -230,7 +230,7 @@ export const PERSON_FLAT_FIELDS_MOCK = {
defaultValue: 'uuid',
description: 'Id',
icon: 'Icon123',
standardOverrides: null,
overrides: null,
options: null,
settings: null,
isActive: true,
@@ -252,7 +252,7 @@ export const PERSON_FLAT_FIELDS_MOCK = {
defaultValue: 'now',
description: 'Creation date',
icon: 'IconCalendar',
standardOverrides: null,
overrides: null,
options: null,
settings: { displayFormat: DateDisplayFormat.RELATIVE },
isActive: true,
@@ -274,7 +274,7 @@ export const PERSON_FLAT_FIELDS_MOCK = {
defaultValue: 'now',
description: 'Last time the record was changed',
icon: 'IconCalendarClock',
standardOverrides: null,
overrides: null,
options: null,
settings: { displayFormat: DateDisplayFormat.RELATIVE },
isActive: true,
@@ -296,7 +296,7 @@ export const PERSON_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'Date when the record was deleted',
icon: 'IconCalendarMinus',
standardOverrides: null,
overrides: null,
options: null,
settings: { displayFormat: DateDisplayFormat.RELATIVE },
isActive: true,
@@ -318,7 +318,7 @@ export const PERSON_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'Contacts company',
icon: 'IconBuildingSkyscraper',
standardOverrides: null,
overrides: null,
options: null,
settings: {
onDelete: RelationOnDeleteAction.SET_NULL,
@@ -345,7 +345,7 @@ export const PERSON_FLAT_FIELDS_MOCK = {
description:
'List of opportunities for which that person is the point of contact',
icon: 'IconTargetArrow',
standardOverrides: null,
overrides: null,
options: null,
settings: { relationType: RelationType.ONE_TO_MANY },
isActive: true,
@@ -367,7 +367,7 @@ export const PERSON_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'Tasks tied to the contact',
icon: 'IconCheckbox',
standardOverrides: null,
overrides: null,
options: null,
settings: { relationType: RelationType.ONE_TO_MANY },
isActive: true,
@@ -389,7 +389,7 @@ export const PERSON_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'Notes tied to the contact',
icon: 'IconNotes',
standardOverrides: null,
overrides: null,
options: null,
settings: { relationType: RelationType.ONE_TO_MANY },
isActive: true,
@@ -411,7 +411,7 @@ export const PERSON_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'Attachments linked to the contact.',
icon: 'IconFileImport',
standardOverrides: null,
overrides: null,
options: null,
settings: { relationType: RelationType.ONE_TO_MANY },
isActive: true,
@@ -433,7 +433,7 @@ export const PERSON_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'Message Participants',
icon: 'IconUserCircle',
standardOverrides: null,
overrides: null,
options: null,
settings: { relationType: RelationType.ONE_TO_MANY },
isActive: true,
@@ -455,7 +455,7 @@ export const PERSON_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'Calendar Event Participants',
icon: 'IconCalendar',
standardOverrides: null,
overrides: null,
options: null,
settings: { relationType: RelationType.ONE_TO_MANY },
isActive: true,
@@ -477,7 +477,7 @@ export const PERSON_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'Events linked to the person',
icon: 'IconTimelineEvent',
standardOverrides: null,
overrides: null,
options: null,
settings: { relationType: RelationType.ONE_TO_MANY },
isActive: true,
@@ -499,7 +499,7 @@ export const PERSON_FLAT_FIELDS_MOCK = {
defaultValue: "''",
description: null,
icon: 'IconNote',
standardOverrides: null,
overrides: null,
options: null,
settings: null,
isActive: true,
@@ -526,7 +526,7 @@ export const PERSON_FLAT_FIELDS_MOCK = {
},
description: null,
icon: 'IconBrandWhatsapp',
standardOverrides: null,
overrides: null,
options: null,
settings: null,
isActive: true,
@@ -548,7 +548,7 @@ export const PERSON_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: null,
icon: 'IconHome',
standardOverrides: null,
overrides: null,
options: [
{
id: 'c33d02fd-0bd9-4769-bd32-036d9ff645a5',
@@ -592,7 +592,7 @@ export const PERSON_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: null,
icon: 'IconStars',
standardOverrides: null,
overrides: null,
options: [
{
id: 'e6b4b1bb-c707-41cb-8f30-418a81ae3299',
@@ -15,7 +15,7 @@ export const PET_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'NoteTargets tied to the Pet',
icon: 'IconCheckbox',
standardOverrides: null,
overrides: null,
options: null,
settings: { relationType: RelationType.ONE_TO_MANY },
isActive: true,
@@ -37,7 +37,7 @@ export const PET_FLAT_FIELDS_MOCK = {
defaultValue: 'uuid',
description: 'Id',
icon: 'Icon123',
standardOverrides: null,
overrides: null,
options: null,
settings: null,
isActive: true,
@@ -59,7 +59,7 @@ export const PET_FLAT_FIELDS_MOCK = {
defaultValue: "'Untitled'",
description: 'Name',
icon: 'IconAbc',
standardOverrides: null,
overrides: null,
options: null,
settings: null,
isActive: true,
@@ -81,7 +81,7 @@ export const PET_FLAT_FIELDS_MOCK = {
defaultValue: 'now',
description: 'Creation date',
icon: 'IconCalendar',
standardOverrides: null,
overrides: null,
options: null,
settings: null,
isActive: true,
@@ -103,7 +103,7 @@ export const PET_FLAT_FIELDS_MOCK = {
defaultValue: 'now',
description: 'Last time the record was changed',
icon: 'IconCalendarClock',
standardOverrides: null,
overrides: null,
options: null,
settings: null,
isActive: true,
@@ -125,7 +125,7 @@ export const PET_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'Deletion date',
icon: 'IconCalendarClock',
standardOverrides: null,
overrides: null,
options: null,
settings: null,
isActive: true,
@@ -147,7 +147,7 @@ export const PET_FLAT_FIELDS_MOCK = {
defaultValue: { name: "''", source: "'MANUAL'" },
description: 'The creator of the record',
icon: 'IconCreativeCommonsSa',
standardOverrides: null,
overrides: null,
options: null,
settings: null,
isActive: true,
@@ -169,7 +169,7 @@ export const PET_FLAT_FIELDS_MOCK = {
defaultValue: 0,
description: 'Position',
icon: 'IconHierarchy2',
standardOverrides: null,
overrides: null,
options: null,
settings: null,
isActive: true,
@@ -191,7 +191,7 @@ export const PET_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'TimelineActivities tied to the Pet',
icon: 'IconTimelineEvent',
standardOverrides: null,
overrides: null,
options: null,
settings: { relationType: RelationType.ONE_TO_MANY },
isActive: true,
@@ -213,7 +213,7 @@ export const PET_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'Attachments tied to the Pet',
icon: 'IconFileImport',
standardOverrides: null,
overrides: null,
options: null,
settings: { relationType: RelationType.ONE_TO_MANY },
isActive: true,
@@ -235,7 +235,7 @@ export const PET_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'TaskTargets tied to the Pet',
icon: 'IconCheckbox',
standardOverrides: null,
overrides: null,
options: null,
settings: { relationType: RelationType.ONE_TO_MANY },
isActive: true,
@@ -257,7 +257,7 @@ export const PET_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'Field used for full-text search',
icon: null,
standardOverrides: null,
overrides: null,
options: null,
settings: null,
isActive: false,
@@ -279,7 +279,7 @@ export const PET_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: null,
icon: null,
standardOverrides: null,
overrides: null,
options: [
{
id: '23a75463-ca50-4b33-bfd8-701e2944c76c',
@@ -344,7 +344,7 @@ export const PET_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: null,
icon: null,
standardOverrides: null,
overrides: null,
options: [
{
id: '5bb37088-b364-44c5-a984-54b01aa241ce',
@@ -409,7 +409,7 @@ export const PET_FLAT_FIELDS_MOCK = {
defaultValue: "''",
description: null,
icon: null,
standardOverrides: null,
overrides: null,
options: null,
settings: null,
isActive: true,
@@ -431,7 +431,7 @@ export const PET_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: null,
icon: null,
standardOverrides: null,
overrides: null,
options: null,
settings: null,
isActive: true,
@@ -462,7 +462,7 @@ export const PET_FLAT_FIELDS_MOCK = {
},
description: null,
icon: null,
standardOverrides: null,
overrides: null,
options: null,
settings: null,
isActive: true,
@@ -489,7 +489,7 @@ export const PET_FLAT_FIELDS_MOCK = {
},
description: null,
icon: null,
standardOverrides: null,
overrides: null,
options: null,
settings: null,
isActive: true,
@@ -511,7 +511,7 @@ export const PET_FLAT_FIELDS_MOCK = {
defaultValue: { primaryEmail: "''", additionalEmails: null },
description: null,
icon: null,
standardOverrides: null,
overrides: null,
options: null,
settings: null,
isActive: true,
@@ -533,7 +533,7 @@ export const PET_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: null,
icon: null,
standardOverrides: null,
overrides: null,
options: null,
settings: null,
isActive: true,
@@ -555,7 +555,7 @@ export const PET_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: null,
icon: null,
standardOverrides: null,
overrides: null,
options: null,
settings: null,
isActive: true,
@@ -581,7 +581,7 @@ export const PET_FLAT_FIELDS_MOCK = {
},
description: null,
icon: null,
standardOverrides: null,
overrides: null,
options: null,
settings: null,
isActive: true,
@@ -603,7 +603,7 @@ export const PET_FLAT_FIELDS_MOCK = {
defaultValue: { amountMicros: null, currencyCode: "''" },
description: null,
icon: null,
standardOverrides: null,
overrides: null,
options: null,
settings: null,
isActive: true,
@@ -625,7 +625,7 @@ export const PET_FLAT_FIELDS_MOCK = {
defaultValue: { lastName: "''", firstName: "''" },
description: null,
icon: null,
standardOverrides: null,
overrides: null,
options: null,
settings: null,
isActive: true,
@@ -647,7 +647,7 @@ export const PET_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: null,
icon: null,
standardOverrides: null,
overrides: null,
options: [
{
id: '4a3ed94d-30db-4231-8c2e-5893d55df860',
@@ -700,7 +700,7 @@ export const PET_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: null,
icon: null,
standardOverrides: null,
overrides: null,
options: null,
settings: null,
isActive: true,
@@ -722,7 +722,7 @@ export const PET_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: null,
icon: null,
standardOverrides: null,
overrides: null,
options: null,
settings: null,
isActive: true,
@@ -744,7 +744,7 @@ export const PET_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: null,
icon: null,
standardOverrides: null,
overrides: null,
options: null,
settings: null,
isActive: true,
@@ -15,7 +15,7 @@ export const ROCKET_FLAT_FIELDS_MOCK = {
defaultValue: 'uuid',
description: 'Id',
icon: 'Icon123',
standardOverrides: null,
overrides: null,
options: null,
settings: null,
isActive: true,
@@ -37,7 +37,7 @@ export const ROCKET_FLAT_FIELDS_MOCK = {
defaultValue: "'Untitled'",
description: 'Name',
icon: 'IconAbc',
standardOverrides: null,
overrides: null,
options: null,
settings: null,
isActive: true,
@@ -59,7 +59,7 @@ export const ROCKET_FLAT_FIELDS_MOCK = {
defaultValue: 'now',
description: 'Creation date',
icon: 'IconCalendar',
standardOverrides: null,
overrides: null,
options: null,
settings: null,
isActive: true,
@@ -81,7 +81,7 @@ export const ROCKET_FLAT_FIELDS_MOCK = {
defaultValue: 'now',
description: 'Last time the record was changed',
icon: 'IconCalendarClock',
standardOverrides: null,
overrides: null,
options: null,
settings: null,
isActive: true,
@@ -103,7 +103,7 @@ export const ROCKET_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'Deletion date',
icon: 'IconCalendarClock',
standardOverrides: null,
overrides: null,
options: null,
settings: null,
isActive: true,
@@ -125,7 +125,7 @@ export const ROCKET_FLAT_FIELDS_MOCK = {
defaultValue: { name: "''", source: "'MANUAL'" },
description: 'The creator of the record',
icon: 'IconCreativeCommonsSa',
standardOverrides: null,
overrides: null,
options: null,
settings: null,
isActive: true,
@@ -147,7 +147,7 @@ export const ROCKET_FLAT_FIELDS_MOCK = {
defaultValue: 0,
description: 'Position',
icon: 'IconHierarchy2',
standardOverrides: null,
overrides: null,
options: null,
settings: null,
isActive: true,
@@ -169,7 +169,7 @@ export const ROCKET_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'TimelineActivities tied to the Rocket',
icon: 'IconTimelineEvent',
standardOverrides: null,
overrides: null,
options: null,
settings: { relationType: RelationType.ONE_TO_MANY },
isActive: true,
@@ -191,7 +191,7 @@ export const ROCKET_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'Attachments tied to the Rocket',
icon: 'IconFileImport',
standardOverrides: null,
overrides: null,
options: null,
settings: { relationType: RelationType.ONE_TO_MANY },
isActive: true,
@@ -213,7 +213,7 @@ export const ROCKET_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'NoteTargets tied to the Rocket',
icon: 'IconCheckbox',
standardOverrides: null,
overrides: null,
options: null,
settings: { relationType: RelationType.ONE_TO_MANY },
isActive: true,
@@ -235,7 +235,7 @@ export const ROCKET_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'TaskTargets tied to the Rocket',
icon: 'IconCheckbox',
standardOverrides: null,
overrides: null,
options: null,
settings: { relationType: RelationType.ONE_TO_MANY },
isActive: true,
@@ -257,7 +257,7 @@ export const ROCKET_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'Field used for full-text search',
icon: null,
standardOverrides: null,
overrides: null,
options: null,
settings: null,
isActive: false,
@@ -19,7 +19,7 @@ export const TASK_FLAT_FIELDS_MOCK = {
defaultValue: 0,
description: 'Task record position',
icon: 'IconHierarchy2',
standardOverrides: null,
overrides: null,
options: null,
settings: null,
isActive: true,
@@ -41,7 +41,7 @@ export const TASK_FLAT_FIELDS_MOCK = {
defaultValue: "''",
description: 'Task title',
icon: 'IconNotes',
standardOverrides: null,
overrides: null,
options: null,
settings: null,
isActive: true,
@@ -63,7 +63,7 @@ export const TASK_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'Task body',
icon: 'IconFilePencil',
standardOverrides: null,
overrides: null,
options: null,
settings: null,
isActive: true,
@@ -85,7 +85,7 @@ export const TASK_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'Task due date',
icon: 'IconCalendarEvent',
standardOverrides: null,
overrides: null,
options: null,
settings: null,
isActive: true,
@@ -107,7 +107,7 @@ export const TASK_FLAT_FIELDS_MOCK = {
defaultValue: "'TODO'",
description: 'Task status',
icon: 'IconCheck',
standardOverrides: null,
overrides: null,
options: [
{
id: '178a8a6f-6411-4731-a417-36e85e5526a3',
@@ -155,7 +155,7 @@ export const TASK_FLAT_FIELDS_MOCK = {
},
description: 'The creator of the record',
icon: 'IconCreativeCommonsSa',
standardOverrides: null,
overrides: null,
options: null,
settings: null,
isActive: true,
@@ -177,7 +177,7 @@ export const TASK_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'Field used for full-text search',
icon: 'IconUser',
standardOverrides: null,
overrides: null,
options: null,
settings: null,
isActive: true,
@@ -199,7 +199,7 @@ export const TASK_FLAT_FIELDS_MOCK = {
defaultValue: 'uuid',
description: 'Id',
icon: 'Icon123',
standardOverrides: null,
overrides: null,
options: null,
settings: null,
isActive: true,
@@ -221,7 +221,7 @@ export const TASK_FLAT_FIELDS_MOCK = {
defaultValue: 'now',
description: 'Creation date',
icon: 'IconCalendar',
standardOverrides: null,
overrides: null,
options: null,
settings: { displayFormat: DateDisplayFormat.RELATIVE },
isActive: true,
@@ -243,7 +243,7 @@ export const TASK_FLAT_FIELDS_MOCK = {
defaultValue: 'now',
description: 'Last time the record was changed',
icon: 'IconCalendarClock',
standardOverrides: null,
overrides: null,
options: null,
settings: { displayFormat: DateDisplayFormat.RELATIVE },
isActive: true,
@@ -265,7 +265,7 @@ export const TASK_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'Date when the record was deleted',
icon: 'IconCalendarMinus',
standardOverrides: null,
overrides: null,
options: null,
settings: { displayFormat: DateDisplayFormat.RELATIVE },
isActive: true,
@@ -287,7 +287,7 @@ export const TASK_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'Task targets',
icon: 'IconArrowUpRight',
standardOverrides: null,
overrides: null,
options: null,
settings: { relationType: RelationType.ONE_TO_MANY },
isActive: true,
@@ -309,7 +309,7 @@ export const TASK_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'Task attachments',
icon: 'IconFileImport',
standardOverrides: null,
overrides: null,
options: null,
settings: { relationType: RelationType.ONE_TO_MANY },
isActive: true,
@@ -331,7 +331,7 @@ export const TASK_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'Task assignee',
icon: 'IconUserCircle',
standardOverrides: null,
overrides: null,
options: null,
settings: {
onDelete: RelationOnDeleteAction.SET_NULL,
@@ -357,7 +357,7 @@ export const TASK_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'Timeline Activities linked to the task.',
icon: 'IconTimelineEvent',
standardOverrides: null,
overrides: null,
options: null,
settings: { relationType: RelationType.ONE_TO_MANY },
isActive: true,
@@ -19,7 +19,7 @@ export const TASKTARGET_FLAT_FIELDS_MOCK = {
defaultValue: 'uuid',
description: 'Id',
icon: 'Icon123',
standardOverrides: null,
overrides: null,
options: null,
settings: null,
isActive: true,
@@ -41,7 +41,7 @@ export const TASKTARGET_FLAT_FIELDS_MOCK = {
defaultValue: 'now',
description: 'Creation date',
icon: 'IconCalendar',
standardOverrides: null,
overrides: null,
options: null,
settings: { displayFormat: DateDisplayFormat.RELATIVE },
isActive: true,
@@ -63,7 +63,7 @@ export const TASKTARGET_FLAT_FIELDS_MOCK = {
defaultValue: 'now',
description: 'Last time the record was changed',
icon: 'IconCalendarClock',
standardOverrides: null,
overrides: null,
options: null,
settings: { displayFormat: DateDisplayFormat.RELATIVE },
isActive: true,
@@ -85,7 +85,7 @@ export const TASKTARGET_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'Date when the record was deleted',
icon: 'IconCalendarMinus',
standardOverrides: null,
overrides: null,
options: null,
settings: { displayFormat: DateDisplayFormat.RELATIVE },
isActive: true,
@@ -107,7 +107,7 @@ export const TASKTARGET_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'TaskTarget task',
icon: 'IconCheckbox',
standardOverrides: null,
overrides: null,
options: null,
settings: {
onDelete: RelationOnDeleteAction.SET_NULL,
@@ -133,7 +133,7 @@ export const TASKTARGET_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'TaskTarget person',
icon: 'IconUser',
standardOverrides: null,
overrides: null,
options: null,
settings: {
onDelete: RelationOnDeleteAction.CASCADE,
@@ -159,7 +159,7 @@ export const TASKTARGET_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'TaskTarget company',
icon: 'IconBuildingSkyscraper',
standardOverrides: null,
overrides: null,
options: null,
settings: {
onDelete: RelationOnDeleteAction.CASCADE,
@@ -185,7 +185,7 @@ export const TASKTARGET_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'TaskTarget opportunity',
icon: 'IconTargetArrow',
standardOverrides: null,
overrides: null,
options: null,
settings: {
onDelete: RelationOnDeleteAction.CASCADE,
@@ -211,7 +211,7 @@ export const TASKTARGET_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'TaskTargets Rocket',
icon: 'IconBuildingSkyscraper',
standardOverrides: null,
overrides: null,
options: null,
settings: {
onDelete: RelationOnDeleteAction.CASCADE,
@@ -237,7 +237,7 @@ export const TASKTARGET_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'TaskTargets Pet',
icon: 'IconBuildingSkyscraper',
standardOverrides: null,
overrides: null,
options: null,
settings: {
onDelete: RelationOnDeleteAction.CASCADE,
@@ -263,7 +263,7 @@ export const TASKTARGET_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'TaskTargets Survey result',
icon: 'IconBuildingSkyscraper',
standardOverrides: null,
overrides: null,
options: null,
settings: {
onDelete: RelationOnDeleteAction.CASCADE,
@@ -19,7 +19,7 @@ export const TIMELINEACTIVITY_FLAT_FIELDS_MOCK = {
defaultValue: 'now',
description: 'Creation date',
icon: 'IconCalendar',
standardOverrides: null,
overrides: null,
options: null,
settings: null,
isActive: true,
@@ -41,7 +41,7 @@ export const TIMELINEACTIVITY_FLAT_FIELDS_MOCK = {
defaultValue: "''",
description: 'Event name',
icon: 'IconAbc',
standardOverrides: null,
overrides: null,
options: null,
settings: null,
isActive: true,
@@ -63,7 +63,7 @@ export const TIMELINEACTIVITY_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'Json value for event details',
icon: 'IconListDetails',
standardOverrides: null,
overrides: null,
options: null,
settings: null,
isActive: true,
@@ -85,7 +85,7 @@ export const TIMELINEACTIVITY_FLAT_FIELDS_MOCK = {
defaultValue: "''",
description: 'Cached record name',
icon: 'IconAbc',
standardOverrides: null,
overrides: null,
options: null,
settings: null,
isActive: true,
@@ -107,7 +107,7 @@ export const TIMELINEACTIVITY_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'Linked Record id',
icon: 'IconAbc',
standardOverrides: null,
overrides: null,
options: null,
settings: null,
isActive: true,
@@ -129,7 +129,7 @@ export const TIMELINEACTIVITY_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'Linked Object Metadata Id',
icon: 'IconAbc',
standardOverrides: null,
overrides: null,
options: null,
settings: null,
isActive: true,
@@ -151,7 +151,7 @@ export const TIMELINEACTIVITY_FLAT_FIELDS_MOCK = {
defaultValue: 'uuid',
description: 'Id',
icon: 'Icon123',
standardOverrides: null,
overrides: null,
options: null,
settings: null,
isActive: true,
@@ -173,7 +173,7 @@ export const TIMELINEACTIVITY_FLAT_FIELDS_MOCK = {
defaultValue: 'now',
description: 'Creation date',
icon: 'IconCalendar',
standardOverrides: null,
overrides: null,
options: null,
settings: { displayFormat: DateDisplayFormat.RELATIVE },
isActive: true,
@@ -195,7 +195,7 @@ export const TIMELINEACTIVITY_FLAT_FIELDS_MOCK = {
defaultValue: 'now',
description: 'Last time the record was changed',
icon: 'IconCalendarClock',
standardOverrides: null,
overrides: null,
options: null,
settings: { displayFormat: DateDisplayFormat.RELATIVE },
isActive: true,
@@ -217,7 +217,7 @@ export const TIMELINEACTIVITY_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'Date when the record was deleted',
icon: 'IconCalendarMinus',
standardOverrides: null,
overrides: null,
options: null,
settings: { displayFormat: DateDisplayFormat.RELATIVE },
isActive: true,
@@ -239,7 +239,7 @@ export const TIMELINEACTIVITY_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'Event workspace member',
icon: 'IconCircleUser',
standardOverrides: null,
overrides: null,
options: null,
settings: {
onDelete: RelationOnDeleteAction.CASCADE,
@@ -265,7 +265,7 @@ export const TIMELINEACTIVITY_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'Event person',
icon: 'IconUser',
standardOverrides: null,
overrides: null,
options: null,
settings: {
onDelete: RelationOnDeleteAction.CASCADE,
@@ -291,7 +291,7 @@ export const TIMELINEACTIVITY_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'Event company',
icon: 'IconBuildingSkyscraper',
standardOverrides: null,
overrides: null,
options: null,
settings: {
onDelete: RelationOnDeleteAction.CASCADE,
@@ -317,7 +317,7 @@ export const TIMELINEACTIVITY_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'Event opportunity',
icon: 'IconTargetArrow',
standardOverrides: null,
overrides: null,
options: null,
settings: {
onDelete: RelationOnDeleteAction.SET_NULL,
@@ -343,7 +343,7 @@ export const TIMELINEACTIVITY_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'Event note',
icon: 'IconTargetArrow',
standardOverrides: null,
overrides: null,
options: null,
settings: {
onDelete: RelationOnDeleteAction.SET_NULL,
@@ -369,7 +369,7 @@ export const TIMELINEACTIVITY_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'Event task',
icon: 'IconTargetArrow',
standardOverrides: null,
overrides: null,
options: null,
settings: {
onDelete: RelationOnDeleteAction.SET_NULL,
@@ -395,7 +395,7 @@ export const TIMELINEACTIVITY_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'Event workflow',
icon: 'IconTargetArrow',
standardOverrides: null,
overrides: null,
options: null,
settings: {
onDelete: RelationOnDeleteAction.CASCADE,
@@ -421,7 +421,7 @@ export const TIMELINEACTIVITY_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'Event workflow version',
icon: 'IconTargetArrow',
standardOverrides: null,
overrides: null,
options: null,
settings: {
onDelete: RelationOnDeleteAction.CASCADE,
@@ -447,7 +447,7 @@ export const TIMELINEACTIVITY_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'Event workflow run',
icon: 'IconTargetArrow',
standardOverrides: null,
overrides: null,
options: null,
settings: {
onDelete: RelationOnDeleteAction.CASCADE,
@@ -473,7 +473,7 @@ export const TIMELINEACTIVITY_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'TimelineActivities Rocket',
icon: 'IconBuildingSkyscraper',
standardOverrides: null,
overrides: null,
options: null,
settings: {
onDelete: RelationOnDeleteAction.CASCADE,
@@ -499,7 +499,7 @@ export const TIMELINEACTIVITY_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'TimelineActivities Pet',
icon: 'IconBuildingSkyscraper',
standardOverrides: null,
overrides: null,
options: null,
settings: {
onDelete: RelationOnDeleteAction.CASCADE,
@@ -525,7 +525,7 @@ export const TIMELINEACTIVITY_FLAT_FIELDS_MOCK = {
defaultValue: null,
description: 'TimelineActivities Survey result',
icon: 'IconBuildingSkyscraper',
standardOverrides: null,
overrides: null,
options: null,
settings: {
onDelete: RelationOnDeleteAction.CASCADE,
@@ -137,10 +137,10 @@ exports[`fromCreateFieldInputToFlatFieldMetadatasToCreate MORPH_RELATION test su
"name": "newFieldPets",
"objectMetadataUniversalIdentifier": Any<String>,
"options": null,
"overrides": null,
"relationTargetFieldMetadataUniversalIdentifier": Any<String>,
"relationTargetObjectMetadataUniversalIdentifier": Any<String>,
"searchFieldMetadataUniversalIdentifiers": [],
"standardOverrides": null,
"type": "MORPH_RELATION",
"universalIdentifier": Any<String>,
"universalSettings": {
@@ -173,10 +173,10 @@ exports[`fromCreateFieldInputToFlatFieldMetadatasToCreate MORPH_RELATION test su
"name": "pet",
"objectMetadataUniversalIdentifier": Any<String>,
"options": null,
"overrides": null,
"relationTargetFieldMetadataUniversalIdentifier": Any<String>,
"relationTargetObjectMetadataUniversalIdentifier": Any<String>,
"searchFieldMetadataUniversalIdentifiers": [],
"standardOverrides": null,
"type": "RELATION",
"universalIdentifier": Any<String>,
"universalSettings": {
@@ -211,10 +211,10 @@ exports[`fromCreateFieldInputToFlatFieldMetadatasToCreate MORPH_RELATION test su
"name": "newFieldCompanies",
"objectMetadataUniversalIdentifier": Any<String>,
"options": null,
"overrides": null,
"relationTargetFieldMetadataUniversalIdentifier": Any<String>,
"relationTargetObjectMetadataUniversalIdentifier": Any<String>,
"searchFieldMetadataUniversalIdentifiers": [],
"standardOverrides": null,
"type": "MORPH_RELATION",
"universalIdentifier": Any<String>,
"universalSettings": {
@@ -247,10 +247,10 @@ exports[`fromCreateFieldInputToFlatFieldMetadatasToCreate MORPH_RELATION test su
"name": "company",
"objectMetadataUniversalIdentifier": Any<String>,
"options": null,
"overrides": null,
"relationTargetFieldMetadataUniversalIdentifier": Any<String>,
"relationTargetObjectMetadataUniversalIdentifier": Any<String>,
"searchFieldMetadataUniversalIdentifiers": [],
"standardOverrides": null,
"type": "RELATION",
"universalIdentifier": Any<String>,
"universalSettings": {
@@ -4,7 +4,7 @@ export const FLAT_FIELD_METADATA_RELATION_PROPERTIES_TO_COMPARE = [
'label',
'description',
'isActive',
'standardOverrides',
'overrides',
'icon',
'name',
'universalSettings',
@@ -44,7 +44,7 @@ export const computeFlatFieldToUpdateAndRelatedFlatFieldToUpdate = ({
flatObjectMetadata,
isSystemBuild,
}: ComputeFlatFieldToUpdateAndRelatedFlatFieldToUpdateArgs): ComputeFlatFieldToUpdateAndRelatedFlatFieldToUpdateReturnType => {
const { standardOverrides, updatedEditableFieldProperties } =
const { overrides, updatedEditableFieldProperties } =
sanitizeRawUpdateFieldInput({
existingFlatFieldMetadata: fromFlatFieldMetadata,
rawUpdateFieldInput,
@@ -62,7 +62,7 @@ export const computeFlatFieldToUpdateAndRelatedFlatFieldToUpdate = ({
],
update: updatedEditableFieldProperties,
}),
standardOverrides,
overrides,
};
if (updatedEditableFieldProperties.settings !== undefined) {
@@ -161,7 +161,7 @@ export const computeFlatFieldToUpdateAndRelatedFlatFieldToUpdate = ({
properties: relatedMorphPropertiesToUpdateTo,
update: updatedEditableFieldProperties,
}),
standardOverrides,
overrides,
};
return {
@@ -9,7 +9,7 @@ export const fromFlatFieldMetadataToFieldMetadataDto = (
updatedAt,
description,
icon,
standardOverrides,
overrides,
isNullable,
isUnique,
settings,
@@ -49,7 +49,7 @@ export const fromFlatFieldMetadataToFieldMetadataDto = (
updatedAt: new Date(updatedAt),
description: description ?? undefined,
icon: icon ?? undefined,
standardOverrides: standardOverrides ?? undefined,
overrides: overrides ?? undefined,
isNullable: isNullable ?? false,
isUnique: isUnique ?? false,
settings: settings ?? undefined,
@@ -45,7 +45,7 @@ export const getDefaultFlatFieldMetadata = ({
isUnique: createFieldInput.isUnique ?? false,
label: createFieldInput.label,
name: createFieldInput.name,
standardOverrides: null,
overrides: null,
type: createFieldInput.type,
universalIdentifier: createFieldInput.universalIdentifier ?? v4(),
options: createFieldInput.options ?? null,
@@ -4,12 +4,12 @@ import {
} from 'twenty-shared/utils';
import { v4 } from 'uuid';
import { FIELD_METADATA_STANDARD_OVERRIDES_PROPERTIES } from 'src/engine/metadata-modules/field-metadata/constants/field-metadata-standard-overrides-properties.constant';
import { type UpdateFieldInput } from 'src/engine/metadata-modules/field-metadata/dtos/update-field.input';
import {
FieldMetadataException,
FieldMetadataExceptionCode,
} from 'src/engine/metadata-modules/field-metadata/field-metadata.exception';
import { ALL_OVERRIDABLE_PROPERTIES_BY_METADATA_NAME } from 'src/engine/metadata-modules/flat-entity/constant/all-overridable-properties-by-metadata-name.constant';
import { FLAT_FIELD_METADATA_EDITABLE_PROPERTIES } from 'src/engine/metadata-modules/flat-field-metadata/constants/flat-field-metadata-editable-properties.constant';
import { type FlatFieldMetadataEditableProperties } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata-editable-properties.constant';
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
@@ -62,7 +62,7 @@ export const sanitizeRawUpdateFieldInput = ({
if (!isStandardField || isSystemBuild) {
return {
updatedEditableFieldProperties,
standardOverrides: null,
overrides: null,
};
}
@@ -82,16 +82,16 @@ export const sanitizeRawUpdateFieldInput = ({
);
}
const { overrides: standardOverrides, remainingProperties } =
computeMetadataOverridesBlob({
overridableProperties: FIELD_METADATA_STANDARD_OVERRIDES_PROPERTIES,
updatedProperties: updatedEditableFieldProperties,
existingEntity: existingFlatFieldMetadata,
existingOverrides: existingFlatFieldMetadata.standardOverrides,
});
const { overrides, remainingProperties } = computeMetadataOverridesBlob({
overridableProperties:
ALL_OVERRIDABLE_PROPERTIES_BY_METADATA_NAME.fieldMetadata,
updatedProperties: updatedEditableFieldProperties,
existingEntity: existingFlatFieldMetadata,
existingOverrides: existingFlatFieldMetadata.overrides,
});
return {
standardOverrides,
overrides,
updatedEditableFieldProperties: remainingProperties,
};
};
@@ -12,7 +12,7 @@ export const ATTACHMENT_FLAT_OBJECT_MOCK = getFlatObjectMetadataMock({
labelPlural: 'Attachments',
description: 'An attachment',
icon: 'IconFileImport',
standardOverrides: null,
overrides: null,
targetTableName: 'DEPRECATED',
isRemote: false,
isActive: true,
@@ -12,7 +12,7 @@ export const COMPANY_FLAT_OBJECT_MOCK = getFlatObjectMetadataMock({
labelPlural: 'Companies',
description: 'A company',
icon: 'IconBuildingSkyscraper',
standardOverrides: null,
overrides: null,
targetTableName: 'DEPRECATED',
isRemote: false,
isActive: true,
@@ -44,7 +44,7 @@ export const getFlatObjectMetadataMock = (
nameSingular: 'defaultflatObjectMetadataNameSingular',
shortcut: 'shortcut',
applicationId,
standardOverrides: null,
overrides: null,
targetTableName: '',
workspaceId: faker.string.uuid(),
createdAt,
@@ -69,7 +69,7 @@ export const getStandardFlatObjectMetadataMock = (
overrides: Omit<FlatObjectMetadataOverrides, 'isCustom' | 'isSystem'>,
) => {
return getFlatObjectMetadataMock({
standardOverrides: {},
overrides: {},
isSystem: true,
applicationUniversalIdentifier:
TWENTY_STANDARD_APPLICATION_UNIVERSAL_IDENTIFIER,
@@ -12,7 +12,7 @@ export const NOTE_FLAT_OBJECT_MOCK = getFlatObjectMetadataMock({
labelPlural: 'Notes',
description: 'A note',
icon: 'IconNotes',
standardOverrides: null,
overrides: null,
targetTableName: 'DEPRECATED',
isRemote: false,
isActive: true,
@@ -12,7 +12,7 @@ export const NOTE_TARGET_FLAT_OBJECT_MOCK = getFlatObjectMetadataMock({
labelPlural: 'Note Targets',
description: 'A note target',
icon: 'IconCheckbox',
standardOverrides: null,
overrides: null,
targetTableName: 'DEPRECATED',
isRemote: false,
isActive: true,
@@ -12,7 +12,7 @@ export const OPPORTUNITY_FLAT_OBJECT_MOCK = getFlatObjectMetadataMock({
labelPlural: 'Opportunities',
description: 'An opportunity',
icon: 'IconTargetArrow',
standardOverrides: null,
overrides: null,
targetTableName: 'DEPRECATED',
isRemote: false,
isActive: true,
@@ -12,7 +12,7 @@ export const PERSON_FLAT_OBJECT_MOCK = getFlatObjectMetadataMock({
labelPlural: 'People',
description: 'A person',
icon: 'IconUser',
standardOverrides: null,
overrides: null,
targetTableName: 'DEPRECATED',
isRemote: false,
isActive: true,
@@ -8,7 +8,7 @@ export const PET_FLAT_OBJECT_MOCK = getFlatObjectMetadataMock({
labelPlural: 'Pets',
description: null,
icon: 'IconCat',
standardOverrides: null,
overrides: null,
targetTableName: 'DEPRECATED',
isRemote: false,
isActive: true,
@@ -8,7 +8,7 @@ export const ROCKET_FLAT_OBJECT_MOCK = getFlatObjectMetadataMock({
labelPlural: 'Rockets',
description: 'A rocket',
icon: 'IconRocket',
standardOverrides: null,
overrides: null,
targetTableName: 'DEPRECATED',
isRemote: false,
isActive: true,
@@ -12,7 +12,7 @@ export const TASK_FLAT_OBJECT_MOCK = getFlatObjectMetadataMock({
labelPlural: 'Tasks',
description: 'A task',
icon: 'IconCheckbox',
standardOverrides: null,
overrides: null,
targetTableName: 'DEPRECATED',
isRemote: false,
isActive: true,
@@ -12,7 +12,7 @@ export const TASK_TARGET_FLAT_OBJECT_MOCK = getFlatObjectMetadataMock({
labelPlural: 'Task Targets',
description: 'A task target',
icon: 'IconCheckbox',
standardOverrides: null,
overrides: null,
targetTableName: 'DEPRECATED',
isRemote: false,
isActive: true,
@@ -12,7 +12,7 @@ export const TIMELINE_ACTIVITY_FLAT_OBJECT_MOCK = getFlatObjectMetadataMock({
labelPlural: 'Timeline Activities',
description: 'Aggregated / filtered event to be displayed on the timeline',
icon: 'IconTimelineEvent',
standardOverrides: null,
overrides: null,
targetTableName: 'DEPRECATED',
isRemote: false,
isActive: true,
@@ -89,7 +89,7 @@ export const fromCreateObjectInputToFlatObjectMetadataAndFlatFieldMetadatasToCre
namePlural: createObjectInput.namePlural,
nameSingular: createObjectInput.nameSingular,
shortcut: createObjectInput.shortcut ?? null,
standardOverrides: null,
overrides: null,
targetTableName: 'DEPRECATED',
applicationUniversalIdentifier: flatApplication.universalIdentifier,
fieldUniversalIdentifiers: [],
@@ -10,7 +10,7 @@ export const fromFlatObjectMetadataToObjectMetadataDto = (
color,
description,
icon,
standardOverrides,
overrides,
shortcut,
duplicateCriteria,
id,
@@ -55,7 +55,7 @@ export const fromFlatObjectMetadataToObjectMetadataDto = (
color: color ?? undefined,
description: description ?? undefined,
icon: icon ?? undefined,
standardOverrides: standardOverrides ?? undefined,
overrides: overrides ?? undefined,
shortcut: shortcut ?? undefined,
duplicateCriteria: duplicateCriteria ?? undefined,
applicationId,
@@ -66,7 +66,7 @@ export const fromUpdateObjectInputToFlatObjectMetadataAndRelatedFlatEntities =
const isStandardObject = belongsToTwentyStandardApp(
existingFlatObjectMetadata,
);
const { standardOverrides, updatedEditableObjectProperties } =
const { overrides, updatedEditableObjectProperties } =
sanitizeRawUpdateObjectInput({
existingFlatObjectMetadata,
rawUpdateObjectInput,
@@ -81,7 +81,7 @@ export const fromUpdateObjectInputToFlatObjectMetadataAndRelatedFlatEntities =
],
update: updatedEditableObjectProperties,
}),
standardOverrides,
overrides,
};
if (
@@ -1,14 +1,13 @@
import { extractAndSanitizeObjectStringFields } from 'twenty-shared/utils';
import { ALL_OVERRIDABLE_PROPERTIES_BY_METADATA_NAME } from 'src/engine/metadata-modules/flat-entity/constant/all-overridable-properties-by-metadata-name.constant';
import { FLAT_OBJECT_METADATA_EDITABLE_PROPERTIES } from 'src/engine/metadata-modules/flat-object-metadata/constants/flat-object-metadata-editable-properties.constant';
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
import { OBJECT_METADATA_STANDARD_OVERRIDES_PROPERTIES } from 'src/engine/metadata-modules/object-metadata/constants/object-metadata-standard-overrides-properties.constant';
import { type UpdateOneObjectInput } from 'src/engine/metadata-modules/object-metadata/dtos/update-object.input';
import {
ObjectMetadataException,
ObjectMetadataExceptionCode,
} from 'src/engine/metadata-modules/object-metadata/object-metadata.exception';
import { type ObjectMetadataStandardOverridesProperties } from 'src/engine/metadata-modules/object-metadata/types/object-metadata-standard-overrides-properties.types';
import { belongsToTwentyStandardApp } from 'src/engine/metadata-modules/utils/belongs-to-twenty-standard-app.util';
import { computeMetadataOverridesBlob } from 'src/engine/metadata-modules/utils/compute-metadata-overrides-blob.util';
@@ -37,7 +36,7 @@ export const sanitizeRawUpdateObjectInput = ({
if (!isStandardObject) {
return {
updatedEditableObjectProperties,
standardOverrides: null,
overrides: null,
};
}
@@ -46,7 +45,7 @@ export const sanitizeRawUpdateObjectInput = ({
).filter(
(property) =>
!FLAT_OBJECT_METADATA_EDITABLE_PROPERTIES.standard.includes(
property as ObjectMetadataStandardOverridesProperties,
property as (typeof FLAT_OBJECT_METADATA_EDITABLE_PROPERTIES.standard)[number],
),
);
@@ -57,16 +56,16 @@ export const sanitizeRawUpdateObjectInput = ({
);
}
const { overrides: standardOverrides, remainingProperties } =
computeMetadataOverridesBlob({
overridableProperties: OBJECT_METADATA_STANDARD_OVERRIDES_PROPERTIES,
updatedProperties: updatedEditableObjectProperties,
existingEntity: existingFlatObjectMetadata,
existingOverrides: existingFlatObjectMetadata.standardOverrides,
});
const { overrides, remainingProperties } = computeMetadataOverridesBlob({
overridableProperties:
ALL_OVERRIDABLE_PROPERTIES_BY_METADATA_NAME.objectMetadata,
updatedProperties: updatedEditableObjectProperties,
existingEntity: existingFlatObjectMetadata,
existingOverrides: existingFlatObjectMetadata.overrides,
});
return {
standardOverrides,
overrides,
updatedEditableObjectProperties: remainingProperties,
};
};
@@ -15,8 +15,8 @@ import { type CollectionHashDTO } from 'src/engine/metadata-modules/minimal-meta
import { MinimalMetadataDTO } from 'src/engine/metadata-modules/minimal-metadata/dtos/minimal-metadata.dto';
import { MinimalObjectMetadataDTO } from 'src/engine/metadata-modules/minimal-metadata/dtos/minimal-object-metadata.dto';
import { MinimalViewDTO } from 'src/engine/metadata-modules/minimal-metadata/dtos/minimal-view.dto';
import { resolveObjectMetadataStandardOverride } from 'src/engine/metadata-modules/object-metadata/utils/resolve-object-metadata-standard-override.util';
import { belongsToTwentyStandardApp } from 'src/engine/metadata-modules/utils/belongs-to-twenty-standard-app.util';
import { resolveEffectiveEntityProperty } from 'src/engine/metadata-modules/utils/resolve-effective-entity-property.util';
import { WorkspaceCacheService } from 'src/engine/workspace-cache/services/workspace-cache.service';
import { type WorkspaceCacheKeyName } from 'src/engine/workspace-cache/types/workspace-cache-key.type';
@@ -80,33 +80,31 @@ export class MinimalMetadataService {
.map((flatObjectMetadata) => {
const isStandardApp = belongsToTwentyStandardApp(flatObjectMetadata);
const objectMetadataForOverride = {
labelPlural: flatObjectMetadata.labelPlural,
labelSingular: flatObjectMetadata.labelSingular,
description: flatObjectMetadata.description ?? undefined,
icon: flatObjectMetadata.icon ?? undefined,
color: flatObjectMetadata.color ?? undefined,
standardOverrides: flatObjectMetadata.standardOverrides ?? undefined,
const overrides = flatObjectMetadata.overrides ?? undefined;
const i18nContext = {
locale: safeLocale,
i18nInstance,
isStandardApp,
};
return {
id: flatObjectMetadata.id,
nameSingular: flatObjectMetadata.nameSingular,
namePlural: flatObjectMetadata.namePlural,
labelSingular: resolveObjectMetadataStandardOverride(
objectMetadataForOverride,
'labelSingular',
safeLocale,
i18nInstance,
isStandardApp,
),
labelPlural: resolveObjectMetadataStandardOverride(
objectMetadataForOverride,
'labelPlural',
safeLocale,
i18nInstance,
isStandardApp,
),
labelSingular: resolveEffectiveEntityProperty({
metadataName: 'objectMetadata',
baseValue: flatObjectMetadata.labelSingular,
overrides,
property: 'labelSingular',
i18nContext,
}),
labelPlural: resolveEffectiveEntityProperty({
metadataName: 'objectMetadata',
baseValue: flatObjectMetadata.labelPlural,
overrides,
property: 'labelPlural',
i18nContext,
}),
icon: flatObjectMetadata.icon ?? undefined,
isActive: flatObjectMetadata.isActive,
isSystem: flatObjectMetadata.isSystem,
@@ -1,9 +0,0 @@
import { type MetadataUniversalFlatEntityPropertiesToCompare } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/metadata-universal-flat-entity-properties-to-compare.type';
export const OBJECT_METADATA_STANDARD_OVERRIDES_PROPERTIES = [
'color',
'labelSingular',
'labelPlural',
'description',
'icon',
] as const satisfies MetadataUniversalFlatEntityPropertiesToCompare<'objectMetadata'>[];
@@ -28,7 +28,7 @@ const PARTIAL_ID_FIELD = {
isLabelSyncedWithName: false,
defaultValue: 'uuid',
options: null,
standardOverrides: null,
overrides: null,
morphId: null,
universalSettings: null,
relationTargetObjectMetadataUniversalIdentifier: null,
@@ -58,7 +58,7 @@ const PARTIAL_CREATED_AT_FIELD = {
isLabelSyncedWithName: false,
defaultValue: 'now',
options: null,
standardOverrides: null,
overrides: null,
morphId: null,
universalSettings: null,
relationTargetObjectMetadataUniversalIdentifier: null,
@@ -88,7 +88,7 @@ const PARTIAL_UPDATED_AT_FIELD = {
isLabelSyncedWithName: false,
defaultValue: 'now',
options: null,
standardOverrides: null,
overrides: null,
morphId: null,
universalSettings: null,
relationTargetObjectMetadataUniversalIdentifier: null,
@@ -118,7 +118,7 @@ const PARTIAL_DELETED_AT_FIELD = {
isLabelSyncedWithName: false,
defaultValue: null,
options: null,
standardOverrides: null,
overrides: null,
morphId: null,
universalSettings: null,
relationTargetObjectMetadataUniversalIdentifier: null,
@@ -148,7 +148,7 @@ const PARTIAL_CREATED_BY_FIELD = {
isLabelSyncedWithName: false,
defaultValue: { name: "''", source: "'MANUAL'" },
options: null,
standardOverrides: null,
overrides: null,
morphId: null,
universalSettings: null,
relationTargetObjectMetadataUniversalIdentifier: null,
@@ -178,7 +178,7 @@ const PARTIAL_UPDATED_BY_FIELD = {
isLabelSyncedWithName: false,
defaultValue: { name: "''", source: "'MANUAL'" },
options: null,
standardOverrides: null,
overrides: null,
morphId: null,
universalSettings: null,
relationTargetObjectMetadataUniversalIdentifier: null,
@@ -208,7 +208,7 @@ const PARTIAL_POSITION_FIELD = {
isLabelSyncedWithName: false,
defaultValue: 0,
options: null,
standardOverrides: null,
overrides: null,
morphId: null,
universalSettings: null,
relationTargetObjectMetadataUniversalIdentifier: null,
@@ -238,7 +238,7 @@ const PARTIAL_SEARCH_VECTOR_FIELD = {
isLabelSyncedWithName: false,
defaultValue: null,
options: null,
standardOverrides: null,
overrides: null,
morphId: null,
// universalSettings for searchVector is computed at runtime
// based on the name field (getTsVectorColumnExpressionFromFields)
@@ -12,7 +12,7 @@ import { type WorkspaceEntityDuplicateCriteria } from 'src/engine/api/graphql/wo
import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
import { FieldMetadataDTO } from 'src/engine/metadata-modules/field-metadata/dtos/field-metadata.dto';
import { IndexMetadataDTO } from 'src/engine/metadata-modules/index-metadata/dtos/index-metadata.dto';
import { ObjectStandardOverridesDTO } from 'src/engine/metadata-modules/object-metadata/dtos/object-standard-overrides.dto';
import { type ObjectMetadataOverrides } from 'src/engine/metadata-modules/object-metadata/types/object-metadata-overrides.type';
@ObjectType('Object')
@Authorize({
@@ -53,8 +53,8 @@ export class ObjectMetadataDTO {
@Field({ nullable: true })
icon?: string;
@Field(() => ObjectStandardOverridesDTO, { nullable: true })
standardOverrides?: ObjectStandardOverridesDTO;
@HideField()
overrides?: ObjectMetadataOverrides | null;
@Field({ nullable: true })
shortcut?: string;
@@ -1,49 +0,0 @@
import { Field, ObjectType } from '@nestjs/graphql';
import { IsJSON, IsOptional, IsString } from 'class-validator';
import { GraphQLJSON } from 'graphql-type-json';
import { type APP_LOCALES } from 'twenty-shared/translations';
@ObjectType('ObjectStandardOverrides')
export class ObjectStandardOverridesDTO {
@IsString()
@IsOptional()
@Field(() => String, { nullable: true })
labelSingular?: string | null;
@IsString()
@IsOptional()
@Field(() => String, { nullable: true })
labelPlural?: string | null;
@IsString()
@IsOptional()
@Field(() => String, { nullable: true })
description?: string | null;
@IsString()
@IsOptional()
@Field(() => String, { nullable: true })
icon?: string | null;
@IsString()
@IsOptional()
@Field(() => String, { nullable: true })
color?: string | null;
@IsJSON()
@IsOptional()
@Field(() => GraphQLJSON, {
nullable: true,
})
translations?: Partial<
Record<
keyof typeof APP_LOCALES,
{
labelSingular?: string | null;
labelPlural?: string | null;
description?: string | null;
}
>
> | null;
}
@@ -13,12 +13,13 @@ import { type WorkspaceEntityDuplicateCriteria } from 'src/engine/api/graphql/wo
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
import { IndexMetadataEntity } from 'src/engine/metadata-modules/index-metadata/index-metadata.entity';
import { SearchFieldMetadataEntity } from 'src/engine/metadata-modules/search-field-metadata/search-field-metadata.entity';
import { type ObjectStandardOverridesDTO } from 'src/engine/metadata-modules/object-metadata/dtos/object-standard-overrides.dto';
import { type ObjectMetadataOverrides } from 'src/engine/metadata-modules/object-metadata/types/object-metadata-overrides.type';
import { FieldPermissionEntity } from 'src/engine/metadata-modules/object-permission/field-permission/field-permission.entity';
import { ObjectPermissionEntity } from 'src/engine/metadata-modules/object-permission/object-permission.entity';
import { ViewEntity } from 'src/engine/metadata-modules/view/entities/view.entity';
import { WasIntroducedInUpgrade } from 'src/engine/core-modules/upgrade/decorators/was-introduced-in-upgrade.decorator';
import { WasRemovedInUpgrade } from 'src/engine/core-modules/upgrade/decorators/was-removed-in-upgrade.decorator';
import { ADD_METADATA_OVERRIDES_COLUMN_UPGRADE_COMMAND_NAME } from 'src/database/commands/upgrade-version-command/2-19/add-metadata-overrides-column-upgrade-command-name.constant';
import { RENAME_IS_UI_READ_ONLY_TO_IS_UI_EDITABLE_UPGRADE_COMMAND_NAME } from 'src/engine/metadata-modules/object-metadata/constants/rename-is-ui-read-only-to-is-ui-editable-upgrade-command-name.constant';
import { SyncableEntity } from 'src/engine/workspace-manager/types/syncable-entity.interface';
import { type JsonbProperty } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/jsonb-property.type';
@@ -64,8 +65,18 @@ export class ObjectMetadataEntity
@Column({ nullable: true, type: 'text' })
color: string | null;
@WasIntroducedInUpgrade({
upgradeCommandName: ADD_METADATA_OVERRIDES_COLUMN_UPGRADE_COMMAND_NAME,
})
@Column({ type: 'jsonb', nullable: true })
standardOverrides: JsonbProperty<ObjectStandardOverridesDTO> | null;
overrides: JsonbProperty<ObjectMetadataOverrides> | null;
/**
* @deprecated Superseded by `overrides`; kept readable for pods on the
* previous release during a rolling deploy. Drop deferred to 2-20/README.md.
*/
@Column({ type: 'jsonb', nullable: true })
standardOverrides: WasRemovedInUpgrade<JsonbProperty<ObjectMetadataOverrides> | null>;
/**
* @deprecated
@@ -32,7 +32,7 @@ import { ObjectMetadataService } from 'src/engine/metadata-modules/object-metada
import { ObjectRecordCountService } from 'src/engine/metadata-modules/object-metadata/object-record-count.service';
import { SearchFieldMetadataDTO } from 'src/engine/metadata-modules/search-field-metadata/dtos/search-field-metadata.dto';
import { objectMetadataGraphqlApiExceptionHandler } from 'src/engine/metadata-modules/object-metadata/utils/object-metadata-graphql-api-exception-handler.util';
import { resolveObjectMetadataStandardOverride } from 'src/engine/metadata-modules/object-metadata/utils/resolve-object-metadata-standard-override.util';
import { resolveEffectiveEntityProperty } from 'src/engine/metadata-modules/utils/resolve-effective-entity-property.util';
import { PermissionsGraphqlApiExceptionFilter } from 'src/engine/metadata-modules/permissions/utils/permissions-graphql-api-exception.filter';
@UseGuards(WorkspaceAuthGuard)
@@ -92,14 +92,18 @@ export class ObjectMetadataResolver {
locale: context.req.locale,
});
return resolveObjectMetadataStandardOverride(
objectMetadata,
labelKey,
context.req.locale,
i18n,
isStandardApp,
applicationCatalog,
);
return resolveEffectiveEntityProperty({
metadataName: 'objectMetadata',
baseValue: objectMetadata[labelKey],
overrides: objectMetadata.overrides,
property: labelKey,
i18nContext: {
locale: context.req.locale,
i18nInstance: i18n,
isStandardApp,
applicationCatalog,
},
});
}
@ResolveField(() => String, { nullable: true })
@@ -10,7 +10,7 @@ import { ObjectMetadataService } from 'src/engine/metadata-modules/object-metada
import { WorkspaceMigrationBuilderException } from 'src/engine/workspace-manager/workspace-migration/exceptions/workspace-migration-builder-exception';
const OBJECT_STRIP_WHEN_NULLISH = [
'standardOverrides',
'overrides',
'color',
'duplicateCriteria',
'shortcut',
@@ -0,0 +1,19 @@
import { type APP_LOCALES } from 'twenty-shared/translations';
export type ObjectMetadataOverrides = {
labelSingular?: string | null;
labelPlural?: string | null;
description?: string | null;
icon?: string | null;
color?: string | null;
translations?: Partial<
Record<
keyof typeof APP_LOCALES,
{
labelSingular?: string | null;
labelPlural?: string | null;
description?: string | null;
}
>
> | null;
};
@@ -1,4 +0,0 @@
import { type OBJECT_METADATA_STANDARD_OVERRIDES_PROPERTIES } from 'src/engine/metadata-modules/object-metadata/constants/object-metadata-standard-overrides-properties.constant';
export type ObjectMetadataStandardOverridesProperties =
(typeof OBJECT_METADATA_STANDARD_OVERRIDES_PROPERTIES)[number];
@@ -31,7 +31,7 @@ const makeFieldMetadata = (
icon: null,
options: null,
settings: null,
standardOverrides: null,
overrides: null,
relationTargetObjectMetadataUniversalIdentifier: null,
createdAt: '2026-01-01T00:00:00.000Z',
updatedAt: '2026-01-01T00:00:00.000Z',
@@ -1,737 +0,0 @@
import { type I18n } from '@lingui/core';
import { SOURCE_LOCALE } from 'twenty-shared/translations';
import { generateMessageId } from 'src/engine/core-modules/i18n/utils/generateMessageId';
import { resolveObjectMetadataStandardOverride } from 'src/engine/metadata-modules/object-metadata/utils/resolve-object-metadata-standard-override.util';
jest.mock('src/engine/core-modules/i18n/utils/generateMessageId');
const mockGenerateMessageId = generateMessageId as jest.MockedFunction<
typeof generateMessageId
>;
describe('resolveObjectMetadataStandardOverride', () => {
let mockI18n: jest.Mocked<I18n>;
beforeEach(() => {
jest.clearAllMocks();
mockI18n = {
_: jest.fn(),
} as unknown as jest.Mocked<I18n>;
});
describe('Custom objects', () => {
it('should return the object value for custom labelSingular object', () => {
const objectMetadata = {
labelSingular: 'My Custom',
labelPlural: 'My Customs',
description: 'Custom Description',
icon: 'custom-icon',
color: 'blue',
isCustom: true,
standardOverrides: undefined,
};
const result = resolveObjectMetadataStandardOverride(
objectMetadata,
'labelSingular',
'fr-FR',
mockI18n,
!objectMetadata.isCustom,
);
expect(result).toBe('My Custom');
});
it('should never translate a custom label even when it matches a standard catalog entry', () => {
const objectMetadata = {
labelSingular: 'Company',
labelPlural: 'Companies',
description: 'Custom Description',
icon: 'custom-icon',
color: 'blue',
isCustom: true,
standardOverrides: undefined,
};
mockGenerateMessageId.mockReturnValue('company.message.id');
mockI18n._.mockReturnValue('Entreprise');
const result = resolveObjectMetadataStandardOverride(
objectMetadata,
'labelSingular',
'fr-FR',
mockI18n,
!objectMetadata.isCustom,
);
expect(result).toBe('Company');
expect(mockGenerateMessageId).not.toHaveBeenCalled();
expect(mockI18n._).not.toHaveBeenCalled();
});
it('should return the object value for custom description object', () => {
const objectMetadata = {
labelSingular: 'My Custom',
labelPlural: 'My Customs',
description: 'Custom Description',
icon: 'custom-icon',
color: 'blue',
isCustom: true,
standardOverrides: undefined,
};
const result = resolveObjectMetadataStandardOverride(
objectMetadata,
'description',
undefined,
mockI18n,
!objectMetadata.isCustom,
);
expect(result).toBe('Custom Description');
});
it('should return the object value for custom icon object', () => {
const objectMetadata = {
labelSingular: 'My Custom',
labelPlural: 'My Customs',
description: 'Custom Description',
icon: 'custom-icon',
color: 'blue',
isCustom: true,
standardOverrides: undefined,
};
const result = resolveObjectMetadataStandardOverride(
objectMetadata,
'icon',
SOURCE_LOCALE,
mockI18n,
!objectMetadata.isCustom,
);
expect(result).toBe('custom-icon');
});
it('should return the object value for custom color object', () => {
const objectMetadata = {
labelSingular: 'My Custom',
labelPlural: 'My Customs',
description: 'Custom Description',
icon: 'custom-icon',
color: 'green',
isCustom: true,
standardOverrides: undefined,
};
const result = resolveObjectMetadataStandardOverride(
objectMetadata,
'color',
SOURCE_LOCALE,
mockI18n,
!objectMetadata.isCustom,
);
expect(result).toBe('green');
});
});
describe('Standard objects - Icon overrides', () => {
it('should return override icon when available for standard object', () => {
const objectMetadata = {
labelSingular: 'My Custom',
labelPlural: 'My Customs',
description: 'Standard Description',
icon: 'default-icon',
isCustom: false,
standardOverrides: {
icon: 'override-icon',
},
};
const result = resolveObjectMetadataStandardOverride(
objectMetadata,
'icon',
'fr-FR',
mockI18n,
!objectMetadata.isCustom,
);
expect(result).toBe('override-icon');
});
});
describe('Standard objects - Color overrides', () => {
it('should return override color when available for standard object', () => {
const objectMetadata = {
labelSingular: 'Company',
labelPlural: 'Companies',
description: 'Standard Description',
icon: 'default-icon',
color: 'blue',
isCustom: false,
standardOverrides: {
color: 'red',
},
};
const result = resolveObjectMetadataStandardOverride(
objectMetadata,
'color',
'fr-FR',
mockI18n,
!objectMetadata.isCustom,
);
expect(result).toBe('red');
});
it('should return base color when no override exists for standard object', () => {
const objectMetadata = {
labelSingular: 'Company',
labelPlural: 'Companies',
description: 'Standard Description',
icon: 'default-icon',
color: 'blue',
isCustom: false,
standardOverrides: undefined,
};
mockGenerateMessageId.mockReturnValue('generated-message-id');
mockI18n._.mockReturnValue('generated-message-id');
const result = resolveObjectMetadataStandardOverride(
objectMetadata,
'color',
SOURCE_LOCALE,
mockI18n,
!objectMetadata.isCustom,
);
expect(result).toBe('blue');
});
});
describe('Standard objects - Translation overrides', () => {
it('should return translation override when available for non-icon objects', () => {
const objectMetadata = {
labelSingular: 'Standard Label',
labelPlural: 'Standard Labels',
description: 'Standard Description',
icon: 'default-icon',
isCustom: false,
standardOverrides: {
translations: {
'fr-FR': {
labelSingular: 'Libellé traduit',
labelPlural: 'Libellés traduits',
description: 'Description traduite',
},
},
},
};
expect(
resolveObjectMetadataStandardOverride(
objectMetadata,
'labelSingular',
'fr-FR',
mockI18n,
!objectMetadata.isCustom,
),
).toBe('Libellé traduit');
expect(
resolveObjectMetadataStandardOverride(
objectMetadata,
'labelPlural',
'fr-FR',
mockI18n,
!objectMetadata.isCustom,
),
).toBe('Libellés traduits');
expect(
resolveObjectMetadataStandardOverride(
objectMetadata,
'description',
'fr-FR',
mockI18n,
!objectMetadata.isCustom,
),
).toBe('Description traduite');
});
it('should fallback when translation override is not available for the locale', () => {
const objectMetadata = {
labelSingular: 'Standard Label',
labelPlural: 'Standard Labels',
description: 'Standard Description',
icon: 'default-icon',
isCustom: false,
standardOverrides: {
translations: {
'es-ES': {
labelSingular: 'Etiqueta en español',
labelPlural: 'Etiquetas en español',
description: 'Descripción en español',
},
},
},
};
mockGenerateMessageId.mockReturnValue('generated-message-id');
mockI18n._.mockReturnValue('generated-message-id');
const result = resolveObjectMetadataStandardOverride(
objectMetadata,
'labelSingular',
'fr-FR',
mockI18n,
!objectMetadata.isCustom,
);
expect(result).toBe('Standard Label');
});
it('should fallback when translation override is not available for the labelKey', () => {
const objectMetadata = {
labelSingular: 'Standard Label',
labelPlural: 'Standard Labels',
description: 'Standard Description',
icon: 'default-icon',
isCustom: false,
standardOverrides: {
translations: {
'fr-FR': {
labelPlural: 'Libellés traduits',
labelSingular: 'Libellé traduit',
},
},
},
};
mockGenerateMessageId.mockReturnValue('generated-message-id');
mockI18n._.mockReturnValue('generated-message-id');
const result = resolveObjectMetadataStandardOverride(
objectMetadata,
'description',
'fr-FR',
mockI18n,
!objectMetadata.isCustom,
);
expect(result).toBe('Standard Description');
});
it('should not use translation overrides when locale is undefined', () => {
const objectMetadata = {
labelSingular: 'Standard Label',
labelPlural: 'Standard Labels',
description: 'Standard Description',
icon: 'default-icon',
isCustom: false,
standardOverrides: {
translations: {
'fr-FR': {
labelSingular: 'Libellé traduit',
labelPlural: 'Libellés traduits',
description: 'Description traduite',
},
},
},
};
mockGenerateMessageId.mockReturnValue('generated-message-id');
mockI18n._.mockReturnValue('generated-message-id');
const result = resolveObjectMetadataStandardOverride(
objectMetadata,
'labelSingular',
undefined,
mockI18n,
!objectMetadata.isCustom,
);
expect(result).toBe('Standard Label');
});
});
describe('Standard objects - SOURCE_LOCALE overrides', () => {
it('should return direct override for SOURCE_LOCALE when available', () => {
const objectMetadata = {
labelSingular: 'Standard Label',
labelPlural: 'Standard Labels',
description: 'Standard Description',
icon: 'default-icon',
isCustom: false,
standardOverrides: {
labelSingular: 'Overridden Label',
labelPlural: 'Overridden Labels',
description: 'Overridden Description',
icon: 'overridden-icon',
},
};
expect(
resolveObjectMetadataStandardOverride(
objectMetadata,
'labelSingular',
SOURCE_LOCALE,
mockI18n,
!objectMetadata.isCustom,
),
).toBe('Overridden Label');
expect(
resolveObjectMetadataStandardOverride(
objectMetadata,
'labelPlural',
SOURCE_LOCALE,
mockI18n,
!objectMetadata.isCustom,
),
).toBe('Overridden Labels');
expect(
resolveObjectMetadataStandardOverride(
objectMetadata,
'description',
SOURCE_LOCALE,
mockI18n,
!objectMetadata.isCustom,
),
).toBe('Overridden Description');
expect(
resolveObjectMetadataStandardOverride(
objectMetadata,
'icon',
SOURCE_LOCALE,
mockI18n,
!objectMetadata.isCustom,
),
).toBe('overridden-icon');
});
it('should use direct override for non-SOURCE_LOCALE', () => {
const objectMetadata = {
labelSingular: 'Standard Label',
labelPlural: 'Standard Labels',
description: 'Standard Description',
icon: 'default-icon',
isCustom: false,
standardOverrides: {
labelSingular: 'Overridden Label',
labelPlural: 'Overridden Labels',
},
};
const result = resolveObjectMetadataStandardOverride(
objectMetadata,
'labelSingular',
'fr-FR',
mockI18n,
!objectMetadata.isCustom,
);
expect(result).toBe('Overridden Label');
});
it('should not use undefined override for SOURCE_LOCALE', () => {
const objectMetadata = {
labelSingular: 'Standard Label',
labelPlural: 'Standard Labels',
description: 'Standard Description',
icon: 'default-icon',
isCustom: false,
standardOverrides: {
labelSingular: undefined,
},
};
mockGenerateMessageId.mockReturnValue('generated-message-id');
mockI18n._.mockReturnValue('generated-message-id');
const result = resolveObjectMetadataStandardOverride(
objectMetadata,
'labelSingular',
SOURCE_LOCALE,
mockI18n,
!objectMetadata.isCustom,
);
expect(result).toBe('Standard Label');
});
});
describe('Standard objects - Auto translation fallback', () => {
it('should return translated message when translation is available', () => {
const objectMetadata = {
labelSingular: 'Standard Label',
labelPlural: 'Standard Labels',
description: 'Standard Description',
icon: 'default-icon',
isCustom: false,
standardOverrides: undefined,
};
mockGenerateMessageId.mockReturnValue('standard.label.message.id');
mockI18n._.mockReturnValue('Libellé traduit automatiquement');
const result = resolveObjectMetadataStandardOverride(
objectMetadata,
'labelSingular',
'fr-FR',
mockI18n,
!objectMetadata.isCustom,
);
expect(mockGenerateMessageId).toHaveBeenCalledWith('Standard Label');
expect(mockI18n._).toHaveBeenCalledWith('standard.label.message.id');
expect(result).toBe('Libellé traduit automatiquement');
});
it('should return original object value when no translation is found', () => {
const objectMetadata = {
labelSingular: 'Standard Label',
labelPlural: 'Standard Labels',
description: 'Standard Description',
icon: 'default-icon',
isCustom: false,
standardOverrides: undefined,
};
const messageId = 'standard.label.message.id';
mockGenerateMessageId.mockReturnValue(messageId);
mockI18n._.mockReturnValue(messageId);
const result = resolveObjectMetadataStandardOverride(
objectMetadata,
'labelSingular',
'fr-FR',
mockI18n,
!objectMetadata.isCustom,
);
expect(result).toBe('Standard Label');
});
});
describe('Priority order - Standard objects', () => {
it('should prioritize translation override over SOURCE_LOCALE override for non-SOURCE_LOCALE', () => {
const objectMetadata = {
labelSingular: 'Standard Label',
labelPlural: 'Standard Labels',
description: 'Standard Description',
icon: 'default-icon',
isCustom: false,
standardOverrides: {
labelSingular: 'Source Override',
labelPlural: 'Source Overrides',
translations: {
'fr-FR': {
labelSingular: 'Translation Override',
labelPlural: 'Translation Overrides',
},
},
},
};
const result = resolveObjectMetadataStandardOverride(
objectMetadata,
'labelSingular',
'fr-FR',
mockI18n,
!objectMetadata.isCustom,
);
expect(result).toBe('Translation Override');
expect(mockGenerateMessageId).not.toHaveBeenCalled();
expect(mockI18n._).not.toHaveBeenCalled();
});
it('should prioritize SOURCE_LOCALE override over auto translation for SOURCE_LOCALE', () => {
const objectMetadata = {
labelSingular: 'Standard Label',
labelPlural: 'Standard Labels',
description: 'Standard Description',
icon: 'default-icon',
isCustom: false,
standardOverrides: {
labelSingular: 'Source Override',
labelPlural: 'Source Overrides',
},
};
const result = resolveObjectMetadataStandardOverride(
objectMetadata,
'labelSingular',
SOURCE_LOCALE,
mockI18n,
!objectMetadata.isCustom,
);
expect(result).toBe('Source Override');
expect(mockGenerateMessageId).not.toHaveBeenCalled();
expect(mockI18n._).not.toHaveBeenCalled();
});
it('should use auto translation when no overrides are available', () => {
const objectMetadata = {
labelSingular: 'Standard Label',
labelPlural: 'Standard Labels',
description: 'Standard Description',
icon: 'default-icon',
isCustom: false,
standardOverrides: {},
};
mockGenerateMessageId.mockReturnValue('auto.translation.id');
mockI18n._.mockReturnValue('Auto Translated Label');
const result = resolveObjectMetadataStandardOverride(
objectMetadata,
'labelSingular',
'de-DE',
mockI18n,
!objectMetadata.isCustom,
);
expect(result).toBe('Auto Translated Label');
expect(mockGenerateMessageId).toHaveBeenCalledWith('Standard Label');
expect(mockI18n._).toHaveBeenCalledWith('auto.translation.id');
});
});
describe('Undefined locale handling', () => {
it('should use SOURCE_LOCALE fallback when locale is undefined for standard object', () => {
const objectMetadata = {
labelSingular: 'Standard Label',
labelPlural: 'Standard Labels',
description: 'Standard Description',
icon: 'default-icon',
isCustom: false,
standardOverrides: {
labelSingular: 'Source Override',
},
};
const result = resolveObjectMetadataStandardOverride(
objectMetadata,
'labelSingular',
undefined,
mockI18n,
!objectMetadata.isCustom,
);
expect(result).toBe('Source Override');
expect(mockGenerateMessageId).not.toHaveBeenCalled();
expect(mockI18n._).not.toHaveBeenCalled();
});
it('should fall back to auto translation when locale is undefined and no SOURCE_LOCALE override exists', () => {
mockI18n._.mockReturnValue('Auto Translated Label');
mockGenerateMessageId.mockReturnValue('auto.translation.id');
const objectMetadata = {
labelSingular: 'Standard Label',
labelPlural: 'Standard Labels',
description: 'Standard Description',
icon: 'default-icon',
isCustom: false,
standardOverrides: undefined,
};
const result = resolveObjectMetadataStandardOverride(
objectMetadata,
'labelSingular',
undefined,
mockI18n,
!objectMetadata.isCustom,
);
expect(result).toBe('Auto Translated Label');
expect(mockGenerateMessageId).toHaveBeenCalledWith('Standard Label');
expect(mockI18n._).toHaveBeenCalledWith('auto.translation.id');
});
});
describe('Application objects - catalog translations', () => {
it('should translate an application object label from its catalog', () => {
mockGenerateMessageId.mockReturnValue('app.label.id');
const objectMetadata = {
labelSingular: 'Property',
labelPlural: 'Properties',
description: 'A property',
icon: 'IconBuilding',
isCustom: false,
standardOverrides: undefined,
};
const result = resolveObjectMetadataStandardOverride(
objectMetadata,
'labelSingular',
'fr-FR',
mockI18n,
false,
{ 'app.label.id': 'Bien' },
);
expect(result).toBe('Bien');
expect(mockI18n._).not.toHaveBeenCalled();
});
it('should fall back to the source value when the catalog has no entry', () => {
mockGenerateMessageId.mockReturnValue('missing.id');
const objectMetadata = {
labelSingular: 'Property',
labelPlural: 'Properties',
description: 'A property',
icon: 'IconBuilding',
isCustom: false,
standardOverrides: undefined,
};
const result = resolveObjectMetadataStandardOverride(
objectMetadata,
'labelSingular',
'fr-FR',
mockI18n,
false,
{},
);
expect(result).toBe('Property');
});
it('should prioritize a workspace translation override over the catalog', () => {
const objectMetadata = {
labelSingular: 'Property',
labelPlural: 'Properties',
description: 'A property',
icon: 'IconBuilding',
isCustom: false,
standardOverrides: {
translations: {
'fr-FR': {
labelSingular: 'Bien immobilier',
},
},
},
};
const result = resolveObjectMetadataStandardOverride(
objectMetadata,
'labelSingular',
'fr-FR',
mockI18n,
false,
{ 'app.label.id': 'Bien' },
);
expect(result).toBe('Bien immobilier');
expect(mockGenerateMessageId).not.toHaveBeenCalled();
});
});
});
@@ -139,7 +139,7 @@ export const buildDefaultFlatFieldMetadatasForCustomObject = ({
createdAt: now,
updatedAt: now,
options: null,
standardOverrides: null,
overrides: null,
morphId: null,
applicationUniversalIdentifier,
objectMetadataUniversalIdentifier,
@@ -15,7 +15,7 @@ export const fromObjectMetadataEntityToObjectMetadataDto = (
icon: entity.icon ?? undefined,
color: entity.color ?? undefined,
shortcut: entity.shortcut ?? undefined,
standardOverrides: entity.standardOverrides ?? undefined,
overrides: entity.overrides ?? undefined,
isRemote: entity.isRemote,
isActive: entity.isActive,
isSystem: entity.isSystem,
@@ -1,61 +0,0 @@
import { type I18n } from '@lingui/core';
import { isNonEmptyString } from '@sniptt/guards';
import { type APP_LOCALES, SOURCE_LOCALE } from 'twenty-shared/translations';
import { isDefined } from 'twenty-shared/utils';
import { translateStandardLabel } from 'src/engine/core-modules/i18n/utils/translate-standard-label.util';
import { type ObjectMetadataDTO } from 'src/engine/metadata-modules/object-metadata/dtos/object-metadata.dto';
export const resolveObjectMetadataStandardOverride = (
objectMetadata: Pick<
ObjectMetadataDTO,
| 'color'
| 'labelPlural'
| 'labelSingular'
| 'description'
| 'icon'
| 'standardOverrides'
>,
labelKey: 'color' | 'labelPlural' | 'labelSingular' | 'description' | 'icon',
locale: keyof typeof APP_LOCALES | undefined,
i18nInstance: I18n,
isStandardApp: boolean,
applicationCatalog?: Record<string, string>,
): string => {
const safeLocale = locale ?? SOURCE_LOCALE;
if (!isStandardApp && !isDefined(applicationCatalog)) {
return objectMetadata[labelKey] ?? '';
}
if (
(labelKey === 'icon' || labelKey === 'color') &&
isDefined(objectMetadata.standardOverrides?.[labelKey])
) {
return objectMetadata.standardOverrides[labelKey];
}
if (
isDefined(objectMetadata.standardOverrides?.translations) &&
labelKey !== 'icon' &&
labelKey !== 'color'
) {
const translationValue =
objectMetadata.standardOverrides.translations[safeLocale]?.[labelKey];
if (isDefined(translationValue)) {
return translationValue;
}
}
if (isNonEmptyString(objectMetadata.standardOverrides?.[labelKey])) {
return objectMetadata.standardOverrides[labelKey] ?? '';
}
return translateStandardLabel({
sourceValue: objectMetadata[labelKey] ?? '',
isStandardApp,
applicationCatalog,
i18nInstance,
});
};
@@ -36,7 +36,7 @@ import {
} from 'src/engine/metadata-modules/page-layout/exceptions/page-layout.exception';
import { fromFlatPageLayoutWithTabsAndWidgetsToPageLayoutDto } from 'src/engine/metadata-modules/page-layout/utils/from-flat-page-layout-with-tabs-and-widgets-to-page-layout-dto.util';
import { isCallerOverridingEntity } from 'src/engine/metadata-modules/utils/is-caller-overriding-entity.util';
import { resolveFlatEntityOverridableProperties } from 'src/engine/metadata-modules/utils/resolve-flat-entity-overridable-properties.util';
import { resolveEffectiveEntity } from 'src/engine/metadata-modules/utils/resolve-effective-entity.util';
import { sanitizeOverridableEntityInput } from 'src/engine/metadata-modules/utils/sanitize-overridable-entity-input.util';
import { ViewService } from 'src/engine/metadata-modules/view/services/view.service';
import { WorkspaceMigrationBuilderException } from 'src/engine/workspace-manager/workspace-migration/exceptions/workspace-migration-builder-exception';
@@ -283,9 +283,7 @@ export class PageLayoutUpdateService {
.filter(isDefined)
.filter((tab) => tab.pageLayoutId === existingPageLayout.id);
const resolvedExistingTabs = existingTabs.map(
resolveFlatEntityOverridableProperties,
);
const resolvedExistingTabs = existingTabs.map(resolveEffectiveEntity);
const {
toCreate: entitiesToCreate,
@@ -561,9 +559,7 @@ export class PageLayoutUpdateService {
flatPageLayoutWidgetMaps,
});
const resolvedExistingWidgets = existingWidgets.map(
resolveFlatEntityOverridableProperties,
);
const resolvedExistingWidgets = existingWidgets.map(resolveEffectiveEntity);
const {
toCreate: entitiesToCreate,
@@ -0,0 +1,292 @@
import { type I18n } from '@lingui/core';
import { isNonEmptyString } from '@sniptt/guards';
import { type APP_LOCALES, SOURCE_LOCALE } from 'twenty-shared/translations';
import { isDefined } from 'twenty-shared/utils';
import { translateStandardLabel } from 'src/engine/core-modules/i18n/utils/translate-standard-label.util';
import { resolveEffectiveEntityProperty } from 'src/engine/metadata-modules/utils/resolve-effective-entity-property.util';
import { resolveEffectiveEntity } from 'src/engine/metadata-modules/utils/resolve-effective-entity.util';
// Frozen reference implementations of the three resolvers that existed before
// unification (resolve-object-metadata-standard-override,
// resolve-field-metadata-standard-override,
// resolve-flat-entity-overridable-properties), with standardOverrides renamed
// to overrides. The unified resolver must reproduce these byte-for-byte across
// the corpus below — this is the safety net for the whole override unification.
type Locale = keyof typeof APP_LOCALES | undefined;
// oxlint-disable-next-line no-explicit-any
type AnyOverrides = any;
const frozenResolveObjectOverride = (
objectMetadata: AnyOverrides,
labelKey: 'color' | 'labelPlural' | 'labelSingular' | 'description' | 'icon',
locale: Locale,
i18nInstance: I18n,
isStandardApp: boolean,
applicationCatalog?: Record<string, string>,
): string => {
const safeLocale = locale ?? SOURCE_LOCALE;
if (!isStandardApp && !isDefined(applicationCatalog)) {
return objectMetadata[labelKey] ?? '';
}
if (
(labelKey === 'icon' || labelKey === 'color') &&
isDefined(objectMetadata.overrides?.[labelKey])
) {
return objectMetadata.overrides[labelKey];
}
if (
isDefined(objectMetadata.overrides?.translations) &&
labelKey !== 'icon' &&
labelKey !== 'color'
) {
const translationValue =
objectMetadata.overrides.translations[safeLocale]?.[labelKey];
if (isDefined(translationValue)) {
return translationValue;
}
}
if (isNonEmptyString(objectMetadata.overrides?.[labelKey])) {
return objectMetadata.overrides[labelKey] ?? '';
}
return translateStandardLabel({
sourceValue: objectMetadata[labelKey] ?? '',
isStandardApp,
applicationCatalog,
i18nInstance,
});
};
const frozenResolveFieldOverride = (
fieldMetadata: AnyOverrides,
labelKey: 'label' | 'description' | 'icon',
locale: Locale,
i18nInstance: I18n,
isStandardApp: boolean,
applicationCatalog?: Record<string, string>,
): string => {
const safeLocale = locale ?? SOURCE_LOCALE;
if (!isStandardApp && !isDefined(applicationCatalog)) {
return fieldMetadata[labelKey] ?? '';
}
if (labelKey === 'icon' && isDefined(fieldMetadata.overrides?.icon)) {
return fieldMetadata.overrides.icon;
}
if (isDefined(fieldMetadata.overrides?.translations) && labelKey !== 'icon') {
const translationValue =
fieldMetadata.overrides.translations[safeLocale]?.[labelKey];
if (isDefined(translationValue)) {
return translationValue;
}
}
if (isNonEmptyString(fieldMetadata.overrides?.[labelKey])) {
return fieldMetadata.overrides[labelKey] ?? '';
}
return translateStandardLabel({
sourceValue: fieldMetadata[labelKey] ?? '',
isStandardApp,
applicationCatalog,
i18nInstance,
});
};
const frozenResolveFlat = (flatEntity: AnyOverrides): AnyOverrides => {
if (!isDefined(flatEntity.overrides)) {
return flatEntity;
}
return {
...flatEntity,
...flatEntity.overrides,
};
};
const mockI18n = {
_: (id: string) => `translated:${id}`,
} as unknown as I18n;
const LOCALES: Locale[] = [undefined, SOURCE_LOCALE, 'fr-FR'];
const BOOLS = [true, false];
const OBJECT_KEYS = [
'labelSingular',
'labelPlural',
'description',
'icon',
'color',
] as const;
const FIELD_KEYS = ['label', 'description', 'icon'] as const;
const buildObjectBase = () => ({
labelSingular: 'Company',
labelPlural: 'Companies',
description: 'A company',
icon: 'IconBuilding',
color: 'blue',
});
const buildFieldBase = () => ({
label: 'Name',
description: 'The name',
icon: 'IconAbc',
});
const OBJECT_OVERRIDES_CORPUS: AnyOverrides[] = [
undefined,
null,
{},
{ labelSingular: 'Org' },
{ labelSingular: '' },
{ labelPlural: 'Orgs', description: 'custom' },
{ icon: 'IconStar' },
{ color: 'red' },
{ icon: 'IconStar', color: 'red', labelSingular: 'Org' },
{ translations: { 'fr-FR': { labelSingular: 'Société' } } },
{ translations: { en: { labelPlural: 'Companies EN' } } },
{
labelSingular: 'Org',
translations: {
'fr-FR': { labelSingular: 'Société', description: 'desc fr' },
},
},
{ translations: {} },
{ translations: { 'fr-FR': {} } },
];
const FIELD_OVERRIDES_CORPUS: AnyOverrides[] = [
undefined,
null,
{},
{ label: 'Full name' },
{ label: '' },
{ description: 'custom desc' },
{ icon: 'IconStar' },
{ icon: 'IconStar', label: 'Full name' },
{ translations: { 'fr-FR': { label: 'Nom' } } },
{
label: 'Full name',
translations: { 'fr-FR': { label: 'Nom', description: 'desc fr' } },
},
{ translations: {} },
];
const CATALOGS: (Record<string, string> | undefined)[] = [
undefined,
{ 'some.id': 'From catalog' },
];
describe('resolveEffectiveEntityProperty (parity with legacy resolvers)', () => {
it('matches the frozen object resolver across the corpus', () => {
for (const overrides of OBJECT_OVERRIDES_CORPUS) {
for (const key of OBJECT_KEYS) {
for (const locale of LOCALES) {
for (const isStandardApp of BOOLS) {
for (const applicationCatalog of CATALOGS) {
const entity = { ...buildObjectBase(), overrides };
const expected = frozenResolveObjectOverride(
entity,
key,
locale,
mockI18n,
isStandardApp,
applicationCatalog,
);
const actual = resolveEffectiveEntityProperty({
metadataName: 'objectMetadata',
baseValue: entity[key as keyof typeof entity] as string,
overrides: entity.overrides,
property: key,
i18nContext: {
locale,
i18nInstance: mockI18n,
isStandardApp,
applicationCatalog,
},
});
expect(actual).toBe(expected);
}
}
}
}
}
});
it('matches the frozen field resolver across the corpus', () => {
for (const overrides of FIELD_OVERRIDES_CORPUS) {
for (const key of FIELD_KEYS) {
for (const locale of LOCALES) {
for (const isStandardApp of BOOLS) {
for (const applicationCatalog of CATALOGS) {
const entity = { ...buildFieldBase(), overrides };
const expected = frozenResolveFieldOverride(
entity,
key,
locale,
mockI18n,
isStandardApp,
applicationCatalog,
);
const actual = resolveEffectiveEntityProperty({
metadataName: 'fieldMetadata',
baseValue: entity[key as keyof typeof entity] as string,
overrides: entity.overrides,
property: key,
i18nContext: {
locale,
i18nInstance: mockI18n,
isStandardApp,
applicationCatalog,
},
});
expect(actual).toBe(expected);
}
}
}
}
}
});
});
describe('resolveEffectiveEntity (parity with legacy flat spread)', () => {
it('matches the frozen flat spread across the corpus', () => {
const flatCorpus: AnyOverrides[] = [
{ name: 'View', position: 1, overrides: undefined },
{ name: 'View', position: 1, overrides: null },
{ name: 'View', position: 1, overrides: {} },
{ name: 'View', position: 1, overrides: { name: 'Overridden' } },
{
name: 'View',
position: 1,
icon: 'IconList',
overrides: { name: 'Overridden', position: 2 },
},
];
for (const flatEntity of flatCorpus) {
expect(resolveEffectiveEntity(flatEntity)).toEqual(
frozenResolveFlat(flatEntity),
);
}
});
});
@@ -0,0 +1,9 @@
import { type I18n } from '@lingui/core';
import { type APP_LOCALES } from 'twenty-shared/translations';
export type EffectiveEntityI18nContext = {
locale: keyof typeof APP_LOCALES | undefined;
i18nInstance: I18n;
isStandardApp: boolean;
applicationCatalog?: Record<string, string>;
};
@@ -0,0 +1,19 @@
import { type AllMetadataName } from 'twenty-shared/metadata';
import { type APP_LOCALES } from 'twenty-shared/translations';
import {
type MetadataEntityOverridablePropertyName,
type MetadataEntityTranslatablePropertyName,
} from 'src/engine/metadata-modules/flat-entity/constant/all-entity-properties-configuration-by-metadata-name.constant';
type OverridesTranslationEntry<T extends AllMetadataName> = {
[P in MetadataEntityTranslatablePropertyName<T>]?: string | null;
};
export type MetadataPresentationOverrides<T extends AllMetadataName> = {
[P in MetadataEntityOverridablePropertyName<T>]?: string | null;
} & {
translations?: Partial<
Record<keyof typeof APP_LOCALES, OverridesTranslationEntry<T>>
> | null;
};
@@ -0,0 +1,70 @@
import { isNonEmptyString } from '@sniptt/guards';
import { type AllMetadataName } from 'twenty-shared/metadata';
import { SOURCE_LOCALE } from 'twenty-shared/translations';
import { isDefined } from 'twenty-shared/utils';
import { translateStandardLabel } from 'src/engine/core-modules/i18n/utils/translate-standard-label.util';
import { type MetadataEntityOverridablePropertyName } from 'src/engine/metadata-modules/flat-entity/constant/all-entity-properties-configuration-by-metadata-name.constant';
import { ALL_TRANSLATABLE_PROPERTIES_BY_METADATA_NAME } from 'src/engine/metadata-modules/flat-entity/constant/all-translatable-properties-by-metadata-name.constant';
import { type EffectiveEntityI18nContext } from 'src/engine/metadata-modules/utils/effective-entity-i18n-context.type';
import { type MetadataPresentationOverrides } from 'src/engine/metadata-modules/utils/metadata-presentation-overrides.type';
export const resolveEffectiveEntityProperty = <T extends AllMetadataName>({
metadataName,
baseValue,
overrides,
property,
i18nContext,
}: {
metadataName: T;
baseValue: string | null | undefined;
overrides: MetadataPresentationOverrides<T> | null | undefined;
property: MetadataEntityOverridablePropertyName<T> & string;
i18nContext: EffectiveEntityI18nContext;
}): string => {
const isTranslatable = (
ALL_TRANSLATABLE_PROPERTIES_BY_METADATA_NAME[metadataName] as string[]
).includes(property);
const overrideValue = (
overrides as Record<string, unknown> | null | undefined
)?.[property];
const { locale, i18nInstance, isStandardApp, applicationCatalog } =
i18nContext;
const safeLocale = locale ?? SOURCE_LOCALE;
const safeBaseValue = baseValue ?? '';
// Custom (non-standard) entities without a catalog have no standard label to
// resolve or translate, and never carry overrides.
if (!isStandardApp && !isDefined(applicationCatalog)) {
return safeBaseValue;
}
if (!isTranslatable && isDefined(overrideValue)) {
return overrideValue as string;
}
if (isTranslatable && isDefined(overrides?.translations)) {
const translationValue = (
overrides.translations[safeLocale] as
| Record<string, string | null | undefined>
| undefined
)?.[property];
if (isDefined(translationValue)) {
return translationValue;
}
}
if (isNonEmptyString(overrideValue)) {
return overrideValue;
}
return translateStandardLabel({
sourceValue: safeBaseValue,
isStandardApp,
applicationCatalog,
i18nInstance,
});
};
@@ -5,9 +5,7 @@ type FlatEntityWithOverrides = {
overrides: Record<string, unknown> | null;
};
export const resolveFlatEntityOverridableProperties = <
T extends FlatEntityWithOverrides,
>(
export const resolveEffectiveEntity = <T extends FlatEntityWithOverrides>(
flatEntity: T,
): T => {
if (!isDefined(flatEntity.overrides)) {
@@ -24,7 +24,7 @@ import { NoPermissionGuard } from 'src/engine/guards/no-permission.guard';
import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard';
import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.service';
import { findFlatEntityByIdInFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps.util';
import { resolveObjectMetadataStandardOverride } from 'src/engine/metadata-modules/object-metadata/utils/resolve-object-metadata-standard-override.util';
import { resolveEffectiveEntityProperty } from 'src/engine/metadata-modules/utils/resolve-effective-entity-property.util';
import { belongsToTwentyStandardApp } from 'src/engine/metadata-modules/utils/belongs-to-twenty-standard-app.util';
import { CreateViewPermissionGuard } from 'src/engine/metadata-modules/view-permissions/guards/create-view-permission.guard';
import { DeleteViewPermissionGuard } from 'src/engine/metadata-modules/view-permissions/guards/delete-view-permission.guard';
@@ -211,19 +211,17 @@ export class ViewController {
if (objectMetadata) {
const i18n = this.i18nService.getI18nInstance(locale ?? 'en');
const translatedObjectLabel = resolveObjectMetadataStandardOverride(
{
labelPlural: objectMetadata.labelPlural,
labelSingular: objectMetadata.labelSingular,
description: objectMetadata.description ?? undefined,
icon: objectMetadata.icon ?? undefined,
standardOverrides: objectMetadata.standardOverrides ?? undefined,
const translatedObjectLabel = resolveEffectiveEntityProperty({
metadataName: 'objectMetadata',
baseValue: objectMetadata.labelPlural,
overrides: objectMetadata.overrides ?? undefined,
property: 'labelPlural',
i18nContext: {
locale,
i18nInstance: i18n,
isStandardApp: belongsToTwentyStandardApp(objectMetadata),
},
'labelPlural',
locale,
i18n,
belongsToTwentyStandardApp(objectMetadata),
);
});
processedName = this.viewService.processViewNameWithTemplate(
view.name,
@@ -24,7 +24,7 @@ import { CustomPermissionGuard } from 'src/engine/guards/custom-permission.guard
import { NoPermissionGuard } from 'src/engine/guards/no-permission.guard';
import { SettingsPermissionGuard } from 'src/engine/guards/settings-permission.guard';
import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard';
import { resolveObjectMetadataStandardOverride } from 'src/engine/metadata-modules/object-metadata/utils/resolve-object-metadata-standard-override.util';
import { resolveEffectiveEntityProperty } from 'src/engine/metadata-modules/utils/resolve-effective-entity-property.util';
import { ViewFieldGroupDTO } from 'src/engine/metadata-modules/view-field-group/dtos/view-field-group.dto';
import { ViewFieldDTO } from 'src/engine/metadata-modules/view-field/dtos/view-field.dto';
import { ViewFilterGroupDTO } from 'src/engine/metadata-modules/view-filter-group/dtos/view-filter-group.dto';
@@ -80,20 +80,18 @@ export class ViewResolver {
workspaceId: workspace.id,
locale: context.req.locale,
});
const translatedObjectLabel = resolveObjectMetadataStandardOverride(
{
labelPlural: objectMetadata.labelPlural,
labelSingular: objectMetadata.labelSingular,
description: objectMetadata.description ?? undefined,
icon: objectMetadata.icon ?? undefined,
standardOverrides: objectMetadata.standardOverrides ?? undefined,
const translatedObjectLabel = resolveEffectiveEntityProperty({
metadataName: 'objectMetadata',
baseValue: objectMetadata.labelPlural,
overrides: objectMetadata.overrides ?? undefined,
property: 'labelPlural',
i18nContext: {
locale: context.req.locale,
i18nInstance: i18n,
isStandardApp,
applicationCatalog,
},
'labelPlural',
context.req.locale,
i18n,
isStandardApp,
applicationCatalog,
);
});
return this.viewService.processViewNameWithTemplate(
view.name,
@@ -6,8 +6,7 @@ import { isDefined, isNonEmptyArray } from 'twenty-shared/utils';
import { I18nService } from 'src/engine/core-modules/i18n/i18n.service';
import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.service';
import { NavigationMenuItemRecordIdentifierService } from 'src/engine/metadata-modules/navigation-menu-item/services/navigation-menu-item-record-identifier.service';
import { FIELD_METADATA_STANDARD_OVERRIDES_PROPERTIES } from 'src/engine/metadata-modules/field-metadata/constants/field-metadata-standard-overrides-properties.constant';
import { OBJECT_METADATA_STANDARD_OVERRIDES_PROPERTIES } from 'src/engine/metadata-modules/object-metadata/constants/object-metadata-standard-overrides-properties.constant';
import { ALL_OVERRIDABLE_PROPERTIES_BY_METADATA_NAME } from 'src/engine/metadata-modules/flat-entity/constant/all-overridable-properties-by-metadata-name.constant';
import { type MetadataEventBatch } from 'src/engine/subscriptions/metadata-event/types/metadata-event-batch.type';
import { type FlatCommandMenuItem } from 'src/engine/metadata-modules/flat-command-menu-item/types/flat-command-menu-item.type';
import { enrichCommandMenuItemEventWithResolvedNavigation } from 'src/engine/subscriptions/metadata-event/utils/enrich-command-menu-item-event-with-resolved-navigation.util';
@@ -61,7 +60,7 @@ export class MetadataEventPublisher {
metadataEventBatch as MetadataEventBatch<'commandMenuItem'>,
);
case 'objectMetadata':
return this.resolveObjectMetadataStandardOverrides(
return this.resolveObjectMetadataOverrides(
metadataEventBatch as MetadataEventBatch<'objectMetadata'>,
);
default:
@@ -87,9 +86,9 @@ export class MetadataEventPublisher {
'before' in enrichedProperties &&
isDefined(enrichedProperties.before)
) {
enrichedProperties.before = this.applyStandardOverridesToMetadataRecord(
enrichedProperties.before = this.applyOverridesToMetadataRecord(
enrichedProperties.before as Record<string, unknown>,
FIELD_METADATA_STANDARD_OVERRIDES_PROPERTIES,
ALL_OVERRIDABLE_PROPERTIES_BY_METADATA_NAME.fieldMetadata,
) as typeof enrichedProperties.before;
}
@@ -103,9 +102,9 @@ export class MetadataEventPublisher {
flatObjectMetadataMaps,
});
enrichedProperties.after = this.applyStandardOverridesToMetadataRecord(
enrichedProperties.after = this.applyOverridesToMetadataRecord(
enrichedAfter,
FIELD_METADATA_STANDARD_OVERRIDES_PROPERTIES,
ALL_OVERRIDABLE_PROPERTIES_BY_METADATA_NAME.fieldMetadata,
) as typeof enrichedProperties.after;
}
@@ -207,7 +206,7 @@ export class MetadataEventPublisher {
return { ...metadataEventBatch, events: enrichedEvents };
}
private resolveObjectMetadataStandardOverrides(
private resolveObjectMetadataOverrides(
metadataEventBatch: MetadataEventBatch<'objectMetadata'>,
): MetadataEventBatch<'objectMetadata'> {
const enrichedEvents = metadataEventBatch.events.map((event) => {
@@ -217,9 +216,9 @@ export class MetadataEventPublisher {
'before' in enrichedProperties &&
isDefined(enrichedProperties.before)
) {
enrichedProperties.before = this.applyStandardOverridesToMetadataRecord(
enrichedProperties.before = this.applyOverridesToMetadataRecord(
enrichedProperties.before as Record<string, unknown>,
OBJECT_METADATA_STANDARD_OVERRIDES_PROPERTIES,
ALL_OVERRIDABLE_PROPERTIES_BY_METADATA_NAME.objectMetadata,
) as typeof enrichedProperties.before;
}
@@ -227,9 +226,9 @@ export class MetadataEventPublisher {
'after' in enrichedProperties &&
isDefined(enrichedProperties.after)
) {
enrichedProperties.after = this.applyStandardOverridesToMetadataRecord(
enrichedProperties.after = this.applyOverridesToMetadataRecord(
enrichedProperties.after as Record<string, unknown>,
OBJECT_METADATA_STANDARD_OVERRIDES_PROPERTIES,
ALL_OVERRIDABLE_PROPERTIES_BY_METADATA_NAME.objectMetadata,
) as typeof enrichedProperties.after;
}
@@ -239,24 +238,24 @@ export class MetadataEventPublisher {
return { ...metadataEventBatch, events: enrichedEvents };
}
private applyStandardOverridesToMetadataRecord(
private applyOverridesToMetadataRecord(
record: Record<string, unknown>,
standardOverrideProperties: readonly string[],
overridableProperties: readonly string[],
): Record<string, unknown> {
const standardOverrides = record.standardOverrides as
const overrides = record.overrides as
| Record<string, unknown>
| null
| undefined;
if (!isDefined(standardOverrides)) {
if (!isDefined(overrides)) {
return record;
}
const resolved = { ...record };
for (const key of standardOverrideProperties) {
if (isDefined(standardOverrides[key])) {
resolved[key] = standardOverrides[key];
for (const key of overridableProperties) {
if (isDefined(overrides[key])) {
resolved[key] = overrides[key];
}
}
@@ -32,7 +32,7 @@ const makeFlatObjectMetadata = (
labelPlural: 'People',
labelSingular: 'Person',
icon: 'IconUser',
standardOverrides: null,
overrides: null,
...overrides,
}) as unknown as FlatObjectMetadata;
@@ -148,10 +148,10 @@ describe('enrichCommandMenuItemEventWithResolvedNavigation', () => {
const flatObjectMetadata = makeFlatObjectMetadata({
labelPlural: 'People',
icon: 'IconUser',
standardOverrides: {
overrides: {
labelPlural: 'Contacts',
icon: 'IconContacts',
} as unknown as FlatObjectMetadata['standardOverrides'],
} as unknown as FlatObjectMetadata['overrides'],
});
const flatObjectMetadataMaps =
makeFlatObjectMetadataMaps(flatObjectMetadata);
@@ -174,7 +174,7 @@ describe('enrichCommandMenuItemEventWithResolvedNavigation', () => {
const flatObjectMetadata = makeFlatObjectMetadata({
labelPlural: 'Companies',
icon: 'IconBuilding',
standardOverrides: null,
overrides: null,
});
const flatObjectMetadataMaps =
makeFlatObjectMetadataMaps(flatObjectMetadata);
@@ -118,7 +118,7 @@ describe('WorkspaceEntityManager', () => {
imageIdentifierFieldMetadataId: null,
labelIdentifierFieldMetadataId: null,
shortcut: null,
standardOverrides: null,
overrides: null,
applicationId: 'test-application-id',
isLabelSyncedWithName: false,
isUIEditable: true,
@@ -162,7 +162,7 @@ describe('WorkspaceEntityManager', () => {
isUnique: false,
options: null,
settings: null,
standardOverrides: null,
overrides: null,
workspaceId: 'test-workspace-id',
viewFieldIds: [],
viewFilterIds: [],
@@ -87,7 +87,7 @@ describe('WorkspaceRepository', () => {
options: null,
settings: null,
morphId: null,
standardOverrides: null,
overrides: null,
applicationId: 'application-id',
relationTargetFieldMetadataId: null,
relationTargetObjectMetadataId: null,

Some files were not shown because too many files have changed in this diff Show More