Commit Graph

89 Commits

Author SHA1 Message Date
Paul Rastoin fbbd8fe967 [REQUIRES_CACHE_FLUSH_FOR_FIELD_AND_OBJECT]FlatFieldMetadata and FlatObjectMetadata required universal (#17557)
# Introduction
In this PR we're migrating both the `field` and `object` metadata to be
using the new `FlatEntityFromV2` that requires all the
`UniversalFlatEntityExtraProperties` to be spread at the flat entity
root.

This means that we have to update all of their flat declaration

This type swap allows to isole a specific entity migration into his own
type scope and avoid to have everything handled at once

```ts
/**
 * Currently under migration but aims to replace FlatEntity afterwards
 */
export type FlatEntityFromV2<
  TEntity,
  TMetadataName extends AllMetadataName | undefined = undefined,
  TInnerFlatEntity extends { __universal?: unknown } = FlatEntityFrom<
    TEntity,
    TMetadataName
  >,
> = Omit<TInnerFlatEntity, '__universal'> & TInnerFlatEntity['__universal'];

```

## Impact
Both object and field:
- Create input transpilation utils
- from entity to flat tools
- mocks

## Note
Removed from the universal extra properties the jsonb properties that do
not contain a serialized

## Next
Next step is to incrementally make the builder and runner expect
`UniversalFlatEntity` for both of these metadata
This way we will be able to fully migrate an entity e2e typesafely
2026-01-29 18:11:19 +00:00
Paul Rastoin fe9d6f34ff [REQUIRES_FULL_CACHE_FLUSH_WHEN_RELEASED] Refactor FlatEntity to be UniversalFlatEntity superset (#17452)
# Introduction
In this PR we're refactoring the `FlatEntity` type to become a superset
of the `UniversalFlatEntity`.
Right now we're storing all the extra properties in `__universal`
property, at some point it might just be sibling to other entity and we
might rely on the `propertiesToCompare` constants and TypeScript
allowing passing a superset type into a smaller subset type

## FromTo utils
The entity to flat entity method now computes the universal information,
standardized a typing and pattern to do

## Example
Also strictly type
```ts
    "bbb019ea-6205-498c-aea5-67bc53bce8a9": {
      "workspaceId": "20202020-1c25-4d02-bf25-6aeccf7ea419",
      "universalIdentifier": "20202020-d111-4d11-8d11-da5ab0a11002",
      "applicationId": "d01b010d-b984-465b-b40b-370e954e5188",
      "id": "bbb019ea-6205-498c-aea5-67bc53bce8a9",
      "pageLayoutTabId": "791a512f-169f-4209-b731-aa86716668c6",
      "title": "Deals by Company",
      "type": "GRAPH",
      "objectMetadataId": "9e14efea-df5b-4c0e-aba9-cfe455f32397",
      "gridPosition": { "row": 0, "column": 6, "rowSpan": 6, "columnSpan": 6 },
      "configuration": {
        "color": "orange",
        "orderBy": "FIELD_ASC",
        "timezone": "UTC",
        "displayLegend": true,
        "displayDataLabel": false,
        "showCenterMetric": true,
        "configurationType": "PIE_CHART",
        "firstDayOfTheWeek": 0,
        "aggregateOperation": "COUNT",
        "groupBySubFieldName": "name",
        "groupByFieldMetadataId": "6673ff18-63d2-47a1-8f85-2b9b09ca27a5",
        "aggregateFieldMetadataId": "8d64ee41-5dd4-4de6-945a-7c0c18399715"
      },
      "createdAt": "2026-01-28T14:08:52.140Z",
      "updatedAt": "2026-01-28T14:08:52.140Z",
      "deletedAt": null,
      "__universal": {
        "universalIdentifier": "20202020-d111-4d11-8d11-da5ab0a11002",
        "applicationUniversalIdentifier": "20202020-64aa-4b6f-b003-9c74b97cee20",
        "pageLayoutTabUniversalIdentifier": "20202020-d011-4d11-8d11-da5ab0a01001",
        "objectMetadataUniversalIdentifier": "20202020-9549-49dd-b2b2-883999db8938",
        "gridPosition": {
          "row": 0,
          "column": 6,
          "rowSpan": 6,
          "columnSpan": 6
        },
        "configuration": {
          "color": "orange",
          "orderBy": "FIELD_ASC",
          "timezone": "UTC",
          "displayLegend": true,
          "displayDataLabel": false,
          "showCenterMetric": true,
          "configurationType": "PIE_CHART",
          "firstDayOfTheWeek": 0,
          "aggregateOperation": "COUNT",
          "groupBySubFieldName": "name",
          "aggregateFieldMetadataUniversalIdentifier": "20202020-d01a-4131-8a31-f123456789ab",
          "groupByFieldMetadataUniversalIdentifier": "20202020-cbac-457e-b565-adece5fc815f"
        }
      }
    },
```
2026-01-28 16:46:34 +00:00
Paul Rastoin 44202668fd [TYPES] UniversalEntity JsonbProperty and SerializedRelation (#17396)
# Introduction

In this PR we're introducing mainly two branded type signatures for both
`JsonbProperty` entities properties and `SerializedRelation` (jsonb
serialized property storing another entity id).

Allowing to dynamically map over them later in order to build universal
`jsonb` `serialized` relations.

## `JsonbProperty`

A branded wrapper type that marks entity properties stored as PostgreSQL
JSONB columns. It adds a phantom brand `__JsonbPropertyBrand__` to
object types while leaving primitives unchanged. The branded key is
optional and typed as never, also omitted when transpiled to
`UniversalFlat`

**Should be used at entities lvl only:**
```typescript
@Column({ type: 'jsonb', nullable: false })
gridPosition: JsonbProperty<GridPosition>;

@Column({ nullable: false, type: 'jsonb', default: [] })
publishedVersions: JsonbProperty<string[]>;
```

## `SerializedRelation`

A branded string type that marks foreign key IDs stored inside JSONB
objects. These are entity references serialized within a JSONB column
rather than being a regular database foreign key.

**Usage in jsonb property generic***
```ts
type FieldMetadataRelationSettings = {
  relationType: RelationType;
  onDelete?: RelationOnDeleteAction;
  joinColumnName?: string | null;
  junctionTargetFieldId?: SerializedRelation;
};
```

## `FormatJsonbSerializedRelation<T>`

A transformation type that processes JSONB properties for universal
entity mapping. It:
1. Detects properties with the `JsonbProperty` brand
2. Finds `SerializedRelation` properties
3. Renames them from `*Id` to `*UniversalIdentifier`
4. Removes the brand from the output type ( optional though )

```typescript
// Input: JsonbProperty<{ targetFieldMetadataId: SerializedRelation }>
// Output: { targetFieldMetadataUniversalIdentifier: SerializedRelation }
```

## Result
An example of the dynamic type mapping, through a type-test example
```ts
type SettingsTestCase = UniversalFlatFieldMetadata<
    | FieldMetadataType.RELATION
    | FieldMetadataType.NUMBER
    | FieldMetadataType.TEXT
  >['settings']

type SettingsExpectedResult =
  | {
      relationType: RelationType;
      onDelete?: RelationOnDeleteAction | undefined;
      joinColumnName?: string | null | undefined;
      junctionTargetFieldUniversalIdentifier?: SerializedRelation | undefined;
    }
  | {
      dataType?: NumberDataType | undefined;
      decimals?: number | undefined;
      type?: FieldNumberVariant | undefined;
    }
  | {
      displayedMaxRows?: number | undefined;
    }
  | null;

type Assertions = [
  Expect<Equal<SettingsTestCase, SettingsExpectedResult>>,
]
```

## Remarks

- Removed duplicated twenty-server and twenty-shared typed
- Removed class validator instances for default value that were not used
at runtime, we will refactor that to add validation across all entities
following a same pattern
2026-01-26 14:25:43 +00:00
martmull 0091ef5f6c Sync built files (#17379)
as title, upload built files to local storage

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
2026-01-23 13:43:53 +01:00
Paul Rastoin 942d2fef83 Remove sync-metadata and IS_WORKSPACE_CREATION_V2_ENABLED feature flag (#16997)
# Introduction
Followup of
https://github.com/twentyhq/twenty/pull/17001#pullrequestreview-3638508738
close https://github.com/twentyhq/core-team-issues/issues/1910

We've completely decom the `sync-metadata` in production. We're now then
removing its implementation in favor of the v2.

## TODO:
- [x] Remove sync-metadata implem and commands
- [x] Remove workspace decorators 
- [x] Type each deprecated field to deprecated on their workspaceEntity
- [x] Remove the `workspace-sync-metadata` folder entirely
- [x] remove workspace migration
- [x] workspace migration removal migration
- [x] remove the `v2` references from workspace manager file names
- [x] remove the `v2` references from workspace manager modules
- [ ] Double check impact on translation file path updates


## Note
- Removed the gate logic
- Remains some service v2 naming, serverless needs to be migrated on v2
fully
- Removed workspaceMigration service app health consumption, making it
always returning up ( no more down ) cc @FelixMalfait ( quite obsolete
health check now, will require complete refactor once we introduce inter
app dependency etc )
2026-01-08 15:45:12 +00:00
Paul Rastoin 1128331cc1 Fix field item type tag (#16860)
# Introduction
This has been fixed on main already 1 hour ago, this PR now only passes
the field application id instead of the object applicationId that could
be different for example when creating a custom field on a standard
object

For the moment not introducing any logic around application integrity
directly and still relying on the isCustom and standardId definition
This will have to be refactored once we deprecate the standardId
2025-12-30 15:02:07 +00:00
Paul Rastoin eedb163131 Field metadata and object metadata v1 relicas (#16230)
# Introduction

Related https://github.com/twentyhq/core-team-issues/issues/1911

Nearly done with all functional v1 implemen removal
Will remain dead code methods that I will detect using knip
2025-12-02 12:06:28 +01:00
Etienne ca5bd76c6a Null equivalence - migration command (#16018)
Awaiting https://github.com/twentyhq/twenty/pull/15926 approval, before
un-drafting it

---------

Co-authored-by: prastoin <paul@twenty.com>
2025-11-27 13:18:56 +00:00
Weiko 1607aebcc6 Deprecate object metadata maps in favor of flat entities (#16080)
## Context
Deprecating the old objectMetadataMap type in favour of split flat
entities to match with our new caching.
In the long run, trying to achieve:
- Better performance through caching
- Consistent data access patterns across the codebase
- Reduced database queries

Now that everything is based on flat entities, which are cached, we can
finish the refactoring of workspace context cache which should already
improve performances.
Then the last step will be to consume that new cache in the new global
datasource to get rid of the many workspace datasources stored in the
server
2025-11-27 13:43:34 +01:00
Etienne 208c0857ee common api - null equivalence (#15926)
closes https://github.com/twentyhq/core-team-issues/issues/1629

To do before requesting review : 
- filter update

Migration to come in an other PR


Strat : 
1/  Null transformation

- [x] Transform NULL equivalent value to NULL in field validation in
common api - pre-query - with feature flag
- [ ] Same logic in ORM (Not done, complex to handle feature flag here)
- [x] Transform NULL value to equivalent in data formatting in ORM -
post-query

2/ Migration (in other PR) for fieldMetadata not nullable with default
defaultValue (empty string, ...)

- [ ] Remove NOT NULL db constraint
- [ ] Update record value to NULL
- [ ] Update field metadata : isNullable:true
- [ ] Update uniqueIndex whereClause (also for standard uniqueIndex) 
- [ ] Activate feature flag

3/ Update metadata creation

- [x] No more default default value
- [x] Update standard field nullability
- [x] Remove index default whereClause for standard field

4/ Update filter

- [x] When filtering on NULL or empty string, be sure all records are
returned (the one with NULL + the one with "")

5/ Test

- [ ] Strat. to do
2025-11-24 18:57:47 +01:00
martmull 9880f192a5 Move composite types to twenty-shared (#15741) 2025-11-10 22:11:52 +01:00
martmull abde3c04ac 1630 extensibility twenty cli ability to create edit and delete fields (#15501)
As title

- adds decorators in twenty-sdk
- update twenty-cli load-manifest to it gets @FieldMetadata infos +
testing
- update twenty-server so it CRUD fields properly, using
universalIdentifier
- Fix UI so we can update managed objects records
- move FieldMetadata items from twenty-server to twenty-shared
2025-11-05 17:50:06 +01:00
martmull b16ab1b7c9 1518 extensibility front add an application section in settings (#15056)
Protected by IS_APPLICATION_ENABLED featureFlag

Add `Application` section in settings

<img width="301" height="137" alt="image"
src="https://github.com/user-attachments/assets/ee53bdd2-36f6-45c6-8646-17b1e08abf00"
/>


A `settings/applications` route listing all installed applications

<img width="661" height="428" alt="image"
src="https://github.com/user-attachments/assets/69d534c4-4e9e-452a-a3d9-ded0223bb457"
/>

Introduce a new Tag for application managed items

<img width="885" height="759" alt="image"
src="https://github.com/user-attachments/assets/19767be5-61e5-4bd2-a51d-54ed9bfb1923"
/>



A `settings/applications/<application_id>` details setting page listing
all objects, serverlessFunctions and agents created by the application:

<img width="917" height="778" alt="image"
src="https://github.com/user-attachments/assets/7fc056a6-1d73-4242-b2eb-6f8955d8597d"
/>

A `settings/applications/<application_id>/<serverless_function_id>`

<img width="905" height="652" alt="image"
src="https://github.com/user-attachments/assets/56ca0021-26bf-42cb-9abf-34879f16050a"
/>

Add trigger tab in serverless function details (readonly for now)

<img width="899" height="724" alt="image"
src="https://github.com/user-attachments/assets/5eeefa35-f2a4-4fd8-a640-7b5c5891f226"
/>

Set object, serverless and agent setting detail pages readonly for
managed items
<img width="1075" height="859" alt="image"
src="https://github.com/user-attachments/assets/57c73d69-4980-47a2-b752-8dc5ab494530"
/>
<img width="648" height="582" alt="image"
src="https://github.com/user-attachments/assets/5ad5f3f7-3bc3-4e40-870a-4981c6492524"
/>
<img width="982" height="692" alt="image"
src="https://github.com/user-attachments/assets/7ad756c4-5d33-4a0a-9eb8-416c040362b9"
/>
<img width="1077" height="647" alt="image"
src="https://github.com/user-attachments/assets/e086b9f5-4062-4d10-82a9-4023de3cad3f"
/>
2025-10-15 17:13:29 +02:00
Félix Malfait e577c2d746 Update user friendly errors for translations (#15000)
Force msg typing instead of string for user friendly errors
2025-10-09 18:17:32 +02:00
Guillim f60817a1e1 Morph many to one picker (#14155)
This PR follows the multiSelect PR merged previously. It will enable
morph relation Many to One to be handled from the table, using a
singleSelect picker

Main point : I decided to change the singleSelect API to take an array
of **objectMetadataName** instead of only one to deal with both our
usecases.

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
2025-10-07 17:25:11 +02:00
nitin 666b99524a remove singleton usage of i18n (#14422)
closes https://github.com/twentyhq/core-team-issues/issues/1480

---------

Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
2025-09-17 19:01:43 +02:00
Paul Rastoin 9e81618773 Refactor morph field name and morph data loader (#14299)
# Introduction
Storing morph relation field names directly in database, using morphId
to aggregate them
Removing dynamic morph field metadata computation in schemas and data
loader

Will add integration tests on morph data loader entry

closes https://github.com/twentyhq/core-team-issues/issues/1425 
closes https://github.com/twentyhq/core-team-issues/issues/1424 
closes https://github.com/twentyhq/core-team-issues/issues/1423
2025-09-08 17:20:18 +02:00
Paul Rastoin 7c481abf0c Handle input transpilation morph relation v2 (#14124)
# Introduction
From `morphRelationCreationPayload` to `flatFieldMetadatas`

close https://github.com/twentyhq/core-team-issues/issues/1406

## Unit testing
I'm not a fan of covering things this way, where we could have some
strong integration testing tests in the first place
But I wanted to freeze the optimistic path computation, also covered the
exception because it was cheap, doesn't mean I won't cover them through
integration tests later anw
2025-08-28 17:22:03 +02:00
Paul Rastoin 59d90bedaa Integration testing v2 enum field types fail and success path (#14010)
# Introduction
Migrating and improving performances of field enum integrations tests
success and failing tests cases to be using the new v2 api

## Discovered issue
When deleting an object in v1 it will leave related enums until the
object is re-created
Something not done anymore within the create in v2 but in the delete
operation
We should implem an upgrade command to remove such relicas

## Bugs
- Update/create default value multi select runner wrong sql query -> FIX
- Update default value multi select regression, we should allow option
without an id to be inserted -> FIX
- default value compare dynamic json stringify convertion or not in
compare tools for object and fields
2025-08-28 10:23:04 +00:00
Paul Rastoin e99c67d6b5 Fail slow + structured validator error response scoped by object and fields (#13980)
# Introduction
Example of returned response: ( snapshot copy don't mind jest expect any
values )
```json
{
  "extensions": {
    "code": "BAD_USER_INPUT",
    "errors": {
      "fieldMetadata": [
        {
          "errors": [
            {
              "code": "INVALID_FIELD_INPUT",
              "message": "Name is too long",
              "userFriendlyMessage": "Name is too long",
              "value": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
            },
          ],
          "id": Any<String>,
          "name": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
          "objectMetadataId": Any<String>,
          "operation": "create_field",
        },
        {
          "errors": [
            {
              "code": "INVALID_FIELD_INPUT",
              "message": "Name is too long",
              "userFriendlyMessage": "Name is too long",
              "value": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
            },
          ],
          "id": Any<String>,
          "name": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
          "objectMetadataId": Any<String>,
          "operation": "create_field",
        },
        {
          "errors": [
            {
              "code": "INVALID_FIELD_INPUT",
              "message": "Name is too long",
              "userFriendlyMessage": "Name is too long",
              "value": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
            },
          ],
          "id": Any<String>,
          "name": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
          "objectMetadataId": Any<String>,
          "operation": "create_field",
        },
        {
          "errors": [
            {
              "code": "INVALID_FIELD_INPUT",
              "message": "Name is too long",
              "userFriendlyMessage": "Name is too long",
              "value": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
            },
          ],
          "id": Any<String>,
          "name": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
          "objectMetadataId": Any<String>,
          "operation": "create_field",
        },
        {
          "errors": [
            {
              "code": "INVALID_FIELD_INPUT",
              "message": "Name is too long",
              "userFriendlyMessage": "Name is too long",
              "value": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
            },
          ],
          "id": Any<String>,
          "name": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
          "objectMetadataId": Any<String>,
          "operation": "create_field",
        },
      ],
      "objectMetadata": [
        {
          "errors": [
            {
              "code": "INVALID_OBJECT_INPUT",
              "message": "Name is too long",
              "value": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
            },
          ],
          "fields": [],
          "id": Any<String>,
          "namePlural": "listingas",
          "nameSingular": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
          "operation": "create_object",
        },
      ],
    },
    "message": "Validation failed for 1 object(s) and 5 field(s)",
    "summary": {
      "invalidFields": 5,
      "invalidObjects": 1,
      "totalErrors": 6,
    },
    "userFriendlyMessage": "Validation failed for 1 object(s) and 5 field(s)",
  },
  "message": "Multiple validation errors occurred while creating object",
  "name": "GraphQLError",
}
```

## Note
We should put in place integrations tests on REST API too, in order to
ensure we receive the same response error
2025-08-20 17:33:15 +00:00
Weiko 1707e00ad4 Workspace schema migration runner v2 (#13899) 2025-08-14 15:58:25 +02:00
Etienne 6936993557 Unique fields - fixes (#13848)
- Enable update to unique for composite field with defaultValue
different from default defaultValue on subfield not included in unique
constraint
- Enable update to unique for standard field + Disable update to
non-unique for standard index
- Fix typo

Fixes https://github.com/twentyhq/core-team-issues/issues/1360
2025-08-13 08:40:46 +02:00
Guillim 63c8bb9530 morph backend CORE api (#13517)
# Morph Relation needs some work on the core API

Especially on the Morph types

<img width="682" height="394" alt="Screenshot 2025-07-31 at 12 19 40"
src="https://github.com/user-attachments/assets/8b185ae3-3fe7-4886-97b1-ec143bb1b0f4"
/>


## Focus Areas:
- GraphQL schema generation
- Query parsing and field selection
- Morph relation field support
- Input type definition preparation
2025-08-12 11:47:02 +02:00
Charles Bochet b14063fe06 Re-add prettier (#13812)
Re add prettier in eslint.configs
2025-08-11 14:10:04 +02:00
Félix Malfait 464a480043 Continue ESLINT9 Migration (#13795)
Might already fix #13793
2025-08-10 23:25:58 +02:00
Etienne 7ce96049b8 Unique field - add unique property creation/deletion on field (#13539)
Done : 
- add isUnique prop availability on gql update/create fieldMetadata
resolvers
- add unique index creation logic at update & creation
- add unique index deletion logic at update
- update unique index if field name updated
- edge cases : can't have default value and unique fields / standard
default value excluded from index (where clause) / can't have composite
unique fields / can't create unique fields on MORPH


closes https://github.com/twentyhq/core-team-issues/issues/1222
2025-08-08 17:42:50 +02:00
Paul Rastoin 4523ee45bc Multiple errors on field input transpilation improvement (#13774)
# Introduction
- ~~returning deleted field on deleteOneField~~ postponed for later
cannot build dto from flat atm
- refactor createFieldInput transpilation to handle multiple errors
instead of throwing
- renamed extract function
2025-08-08 16:24:47 +02:00
Félix Malfait 0236f79ccf Fix assert unreachable, remove unused variables (#13753)
Small code cleanup
2025-08-08 09:04:45 +02:00
Félix Malfait 8b4b9ef8da Change type import rule (#13751)
Forcing "type" to be explicit, works best will rollup on the frontend to
exclude depdendencies
2025-08-08 01:27:05 +02:00
Charles Bochet fecf8ca913 Fix i18n issues on datamodel translation (#13710)
As per title, I believe calling i18n is bad because it's a singleton.

This singleton is being set back and force by users in
i18n.middleware.ts

Strategy:
we need to load the translations dynamically! Should be straight
forward:
- singleton (i18nService) containing all translationMessages
- singleton also containing a map of i18n instances loaded for each
locale => I've checked it's not heavy on RAM at all
- some methods to get the translation for a messageId x locale or the
whole i18n instance

==> use it everywhere, this PR only takes care of data model translation
2025-08-07 01:28:40 +02:00
Paul Rastoin eab72d8e67 FlatObjectMetadataMaps utils coverage (#13695)
# Introduction
```ts
 PASS   twenty-server  src/engine/metadata-modules/flat-object-metadata-maps/utils/__tests__/add-flat-field-metadata-in-flat-object-metadata-maps-or-throw.spec.ts
 PASS   twenty-server  src/engine/metadata-modules/flat-object-metadata-maps/utils/__tests__/delete-field-from-flat-object-metadata-maps-or-throw.spec.ts
 PASS   twenty-server  src/engine/metadata-modules/flat-object-metadata-maps/utils/__tests__/find-flat-field-metadata-in-flat-object-metadata-maps.spec.ts
 PASS   twenty-server  src/engine/metadata-modules/flat-object-metadata-maps/utils/__tests__/replace-flat-object-metadata-in-flat-object-metadata-maps-or-throw.spec.ts
 PASS   twenty-server  src/engine/metadata-modules/flat-object-metadata-maps/utils/__tests__/replace-flat-field-metadata-in-flat-object-metadata-maps-or-throw.spec.ts
 PASS   twenty-server  src/engine/metadata-modules/flat-object-metadata-maps/utils/__tests__/find-flat-object-metadata-in-flat-object-metadata-maps.spec.ts
 PASS   twenty-server  src/engine/metadata-modules/flat-object-metadata-maps/utils/__tests__/add-flat-object-metadata-to-flat-object-metadata-maps-or-throw.spec.ts
 PASS   twenty-server  src/engine/metadata-modules/flat-object-metadata-maps/utils/__tests__/delete-object-from-flat-object-metadata-maps-or-throw.spec.ts

=============================== Coverage summary ===============================
Statements   : 0.43% ( 159/36564 )
Branches     : 0.16% ( 26/16182 )
Functions    : 0.27% ( 25/9110 )
Lines        : 0.4% ( 142/35006 )
================================================================================

Test Suites: 8 passed, 8 total
Tests:       20 passed, 20 total
Snapshots:   9 passed, 9 total
Time:        7.639 s
```

## Tests utils
Introduced three tests utils:
- expect-flat-field-metadata-to-be-in-flat-object-metadata-maps.util
-
expect-flat-object-metadata-to-strictly-be-in-flat-object-metadata-maps.util
- expect-to-be-defined.util
2025-08-06 17:27:36 +00:00
Paul Rastoin 29a4f4d685 CreateFieldInput transpilation to FlatFieldMetadata, FlatFieldMetadata validation (#13493)
# Introduction
Following https://github.com/twentyhq/twenty/pull/13420

What has been done:
- `CreateFieldInput` transpilation to `FlatFieldMetadata`
- `FlatFieldMetadata` validator service
- A lof of transpilation utils from `input` to `flatObject` or
`flatField`
- Created dedicated v2 api metadata services
- Introducing `inferDeletionFromMissingObjectFieldIndex` in the builder,
to avoid diffing every object and field of the current workspace we
allow only generating create/update migration operations, usefull when
passing by the api metadata


## We still need to in another PR:
- Implement a strong unit test coverage and critical functions and
services
- Finalize flat field metadata validation exception for `options`
`defaultValue` `settings` and `relations`
- Finalize `flatObjectMetadata` validation and v2 service refactor
- Plug the new service when feature flag is enabled
2025-07-30 15:08:11 +02:00
Paul Rastoin c1bf0a1fbf Object metadata API create one using workspace migration v2 (#13420)
# Introduction
In this PR we create basic transpilation methods and utils to handle
input to flat, entity to flat, object maps to flat. In order to
transpile everything into a common validation that will be implemented
in another PR

## FieldMetadataEntity typing
Added `never | null` to fields that should never be in order to ease
general abstracted method to pass null, as anw it's what is in the
database

## Todo
- ~~Create a feature flag~~
- Integration test for object creation through metadata api + pg col
introspection and snapshoting
2025-07-29 17:47:28 +02:00
Guillim 1ea451c8be Morph Relations : deleteOneField (#13349)
This PR adapts teh deleteOneField to make sure morph relations can be
deleted, either
- from a relation type, 
- or from a morph relation type (which is the most obvious case).

This PR covers 
- the deletion of fieldMetadata 
- and the migrationof workspace schemas, by using the already existing
definition of relation migrations

This PR implements a new test suite: "Delete Object metadata with morph
relation" and completes the other test suites for FieldMetadata and
ObjectMetadata creation/deletion.

Last, we added a nitpick from @paul I forgot on a previous PR on
process-nested-realtion-v2

Fixes https://github.com/twentyhq/core-team-issues/issues/1197
2025-07-24 12:40:14 +00:00
Paul Rastoin a0a575fa0b Improve FieldMetadataEntity defaultValue, settings and options typing (#13320)
# Introduction
Following https://github.com/twentyhq/twenty/pull/13264, this PR
introduces several `fieldMetadataEntity` typing enhancement suggestions.

Mainly any nullable field metadata entity properties are now either
nullable or defined.
Or never if field is dynamically required or not depending on the field
metadata type

This enhance DevX

## Standards

- field enum ( `MULTI_SELECT`, `SELECT`, `RATING` ) will never have
`options` set to `NULL` in db
- field `RELATION` or `MORH_RELATION` won't ever have its relation
fields set to `NULL` in db
- field of any type `settings`, even if possibly defined, can still be
`NULL` in db
- field of any type `defaultValue`, even if possibly defined, can still
be `NULL` in db

It coud be interesting to guard these standards by adding dedicated pg
constraints on each field

## TypesScript type tests
added coverage for each `settings`, `defaultValue`, and `options`
depending on the current `fieldMetadata`
Honestly I don' know if this typescript assertions test file is not
overkill, but regarding metadata staticness it might be very interesting
to have this guard

## Possible improvements
- We could type as `unknown` instead of "all" on `FieldMetadataType`
inferrance
- We still need to deprecate remaining duplicated entities such as
`Index/Field/MetadataInterface` etc not a huge refactor neither urgent
2025-07-23 13:43:27 +02:00
Paul Rastoin 1536ed3434 Deprecate ObjectMetadataInterface and improve entity typing (#13310)
# Introduction
Following `FieldMetadataInterface` deprecation in
https://github.com/twentyhq/twenty/pull/13264
As for the previous PR will rename and remove all the file in a
secondary PR to avoid conflicts and over loading this one

## Improvements
Removed optional properties from the `objectMetadataEntity` model and
added utils to retrieve test data

## Notes
By touching to `ObjectMetadataDTO` I would have expected a twenty-front
codegenerated types mutation, but it does not seem to be granular enough
to null/undefined coercion
2025-07-21 15:53:17 +00:00
Paul Rastoin 1dee9bc800 Remove field metadata interface references (#13305)
# Introduction
Following https://github.com/twentyhq/twenty/pull/13264
> After this PR merge will create a new one removing the type and
replacing it to FieldMetadataEntity.

This is it !
2025-07-21 13:57:14 +02:00
Paul Rastoin 47b60bd49f Deprecate FieldMetadataInterface (#13264)
# Introduction

From the moment replaced the FieldMetadataInterface definition to:
```ts
import { FieldMetadataType } from 'twenty-shared/types';

import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';

export type FieldMetadataInterface<
  T extends FieldMetadataType = FieldMetadataType,
> = FieldMetadataEntity<T>;
```
After this PR merge will create a new one removing the type and
replacing it to `FieldMetadataEntity`.
Did not renamed it here to avoid conflicts on naming + type issues fixs
within the same PR

## Field metadata entity RELATION or MORPH
Relations fields cannot be null for those field metadata entity instance
anymore, but are never for the others see
`packages/twenty-server/src/engine/metadata-modules/field-metadata/types/field-metadata-entity-test.type.ts`
( introduced TypeScript tests )

## Concerns
- TS_VECTOR is the most at risk with the `generatedType` and
`asExpression` removal from interface

## What's next
- `FielMetadataInterface` removal and rename ( see introduction )
- Depcrecating `ObjectMetadataInterface`
- Refactor `FieldMetadataEntity` optional fiels to be nullable only
- TO DIG `never` occurences on settings, defaultValue etc
- Some interfaces will be replaced by the `FlatFieldMetadata` when
deprecating the current sync and comparators tools
2025-07-21 11:30:18 +02:00
Charles Bochet 191bbb9e12 Prevent field name conflicts (#13280)
Fixes https://github.com/twentyhq/twenty/issues/13184
2025-07-18 21:38:36 +02:00
Guillim 530a7dea86 Morph relation : migration builder (#13173)
This PR will create the migration to be run for the morph relations

- We created a dedicated util to generate the column name and refactored
a little the code in order to have less dependencies and a clearer devX
(updated the snapshot that changed because of this)
- Moved the `createMigrationActions` to its own util as well
- Created the `MorphRelationColumnActionFactory` based on the relation
one

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
2025-07-17 11:39:45 +02:00
Nabhag Motivaras ffcbfa6215 fix: standard object metadata override (#13215)
# Issue

- fix #13156, related #13105

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
2025-07-16 08:50:14 +02:00
Marie c13bc60dad Improve error handling (#13130)
In the BE we throw custom errors with precise error codes (e.g.
"LABEL_ALREADY_EXISTS") before catching them in filters and rethrowing
BaseGraphQLErrors (standard errors such as NotFoundError, UserInputError
etc.).
In the FE we were grouping sentries based on the error codes but we were
actually grouping by very broad codes such as "NOT_FOUND" or
"BAD_USER_INPUT", extracted from the BaseGraphQLErrors.

To fix that, we update the BaseGraphQLError constructor api to allow to
pass on the CustomError directly and retrieve from it the original code
and store it in existing property `subCode` that we will use in the FE
to send errors to sentry.
This new api also eases usage of `userFriendlyMessage` that is passed on
to the api response and therefore to the FE when CustomError is passed
on directly to the BaseGraphQLError constructor.
2025-07-09 17:13:44 +02:00
Charles Bochet 39f6f3c4bb Prevent relation update from settings (#13099)
## Expected behavior

Described behavior regarding: (update | create) x (custom | standard) x
(icon, label, name, isSynced)

**Custom:**
- Field RELATION create: name, label, isSynced, icon should be editable
- Field RELATION update: name should not, icon label, isSynced should
- For other fields, icon, label, name, isSynced should be editable at
field creation | update

To simplify: Field RELATION name should not be editable at update

**Standards**
- Field: create does not makes sense
- Field: name should not, icon label, isSynced should (this will end up
in overrides)

To simplify, no Field RELATION edge case, name should not be editable at
update

**Note:** the FE logic is quite different as the UI is hiding some
details behind the syncWithLabel. See my comments and TODO there


## What I've tested:
(update | create) x (custom | standard) x (icon, label, name, isSynced,
description)
2025-07-08 21:03:38 +02:00
Guillim a5deddaffd fieldmetadatatype + featurelfag creation (#13021)
Co-authored-by: Charles Bochet <charles@twenty.com>
2025-07-08 12:23:28 +02:00
Marie 288f0919db Define server error messages to display in FE from the server (#12973)
Currently, when a server query or mutation from the front-end fails, the
error message defined server-side is displayed in a snackbar in the
front-end.
These error messages usually contain technical details that don't belong
to the user interface, such as "ObjectMetadataCollection not found" or
"invalid ENUM value for ...".

**BE**
In addition to the original error message that is still needed (for the
request response, debugging, sentry monitoring etc.), we add a
`displayedErrorMessage` that will be used in the snackbars. It's only
relevant to add it for the messages that will reach the FE (ie. not in
jobs or in rest api for instance) and if it can help the user sort out /
fix things (ie. we do add displayedErrorMessage for "Cannot create
multiple draft versions for the same workflow" or "Cannot delete
[field], please update the label identifier field first", but not
"Object metadata does not exist"), even if in practice in the FE users
should not be able to perform an action that will not work (ie should
not be able to save creation of multiple draft versions of the same
workflows).

**FE**
To ease the usage we replaced enqueueSnackBar with enqueueErrorSnackBar
and enqueueSuccessSnackBar with an api that only requires to pass on the
error.
If no displayedErrorMessage is specified then the default error message
is `An error occured.`
2025-07-03 12:42:10 +00:00
Abdul Rahman 74b6466a57 feat: Add agent role assignment and database CRUD tools for AI agent nodes (#12888)
This PR introduces a significant enhancement to the role-based
permission system by extending it to support AI agents, enabling them to
perform database operations based on assigned permissions.

## Key Changes

### 1. Database Schema Migration
- **Table Rename**: `userWorkspaceRole` → `roleTargets` to better
reflect its expanded purpose
- **New Column**: Added `agentId` (UUID, nullable) to support AI agent
role assignments
- **Constraint Updates**: 
- Made `userWorkspaceId` nullable to accommodate agent-only role
assignments
- Added check constraint `CHK_role_targets_either_agent_or_user`
ensuring either `agentId` OR `userWorkspaceId` is set (not both)

### 2. Entity & Service Layer Updates
- **RoleTargetsEntity**: Updated with new `agentId` field and constraint
validation
- **AgentRoleService**: New service for managing agent role assignments
with validation
- **AgentService**: Enhanced to include role information when retrieving
agents
- **RoleResolver**: Added GraphQL mutations for `assignRoleToAgent` and
`removeRoleFromAgent`

### 3. AI Agent CRUD Operations
- **Permission-Based Tool Generation**: AI agents now receive database
tools based on their assigned role permissions
- **Dynamic Tool Creation**: The `AgentToolService` generates CRUD tools
(`create_*`, `find_*`, `update_*`, `soft_delete_*`, `destroy_*`) for
each object based on role permissions
- **Granular Permissions**: Supports both global role permissions
(`canReadAllObjectRecords`) and object-specific permissions
(`canReadObjectRecords`)

### 4. Frontend Integration
- **Role Assignment UI**: Added hooks and components for
assigning/removing roles from agents

## Demo


https://github.com/user-attachments/assets/41732267-742e-416c-b423-b687c2614c82

---------

Co-authored-by: Antoine Moreaux <moreaux.antoine@gmail.com>
Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
Co-authored-by: Charles Bochet <charles@twenty.com>
Co-authored-by: Guillim <guillim@users.noreply.github.com>
Co-authored-by: Charles Bochet <charlesBochet@users.noreply.github.com>
Co-authored-by: Weiko <corentin@twenty.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: github-actions <github-actions@twenty.com>
Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
Co-authored-by: Marie <51697796+ijreilly@users.noreply.github.com>
Co-authored-by: martmull <martmull@hotmail.fr>
Co-authored-by: Thomas Trompette <thomas.trompette@sfr.fr>
Co-authored-by: Etienne <45695613+etiennejouan@users.noreply.github.com>
Co-authored-by: Baptiste Devessier <baptiste@devessier.fr>
Co-authored-by: nitin <142569587+ehconitin@users.noreply.github.com>
Co-authored-by: Paul Rastoin <45004772+prastoin@users.noreply.github.com>
Co-authored-by: prastoin <paul@twenty.com>
Co-authored-by: Vicky Wang <157669812+vickywxng@users.noreply.github.com>
Co-authored-by: Vicky Wang <vw92@cornell.edu>
Co-authored-by: Raphaël Bosi <71827178+bosiraphael@users.noreply.github.com>
2025-06-29 22:18:14 +02:00
Charles Bochet d5c974054d Improve performance on metadata computation (#12785)
In this PR:

## Improve recompute metadata cache performance. We are aiming for
~100ms

Deleting relationMetadata table and FKs pointing on it
Fetching indexMetadata and indexFieldMetadata in a separate query as
typeorm is suboptimizing

## Remove caching lock

As recomputing the metadata cache is lighter, we try to stop preventing
multiple concurrent computations. This also simplifies interfaces

## Introduce self recovery mecanisms to recompute cache automatically if
corrupted

Aka getFreshObjectMetadataMaps

## custom object resolver performance improvement:  1sec to 200ms

Double check queries and indexes used while creating a custom object
Remove the queries to db to use the cached objectMetadataMap

## reduce objectMetadataMaps to 500kb
<img width="222" alt="image"
src="https://github.com/user-attachments/assets/2370dc80-49b6-4b63-8d5e-30c5ebdaa062"
/>

We used to stored 3 fieldMetadataMaps (byId, byName, byJoinColumnName).
While this is great for devXP, this is not great for performances.
Using the same mecanisme as for objectMetadataMap: we only keep byIdMap
and introduce two otherMaps to idByName, idByJoinColumnName to make the
bridge

## Add dataloader on IndexMetadata (aka indexMetadataList in the API)

## Improve field resolver performances too

## Deprecate ClientConfig
2025-06-23 21:06:17 +02:00
Abdul Rahman eb7556e333 Fix: multi-select default values validation (#12271)
https://github.com/user-attachments/assets/3bea63cc-b098-4252-8787-fc6263f01e8d


Closes #12277

---------

Co-authored-by: prastoin <paul@twenty.com>
Co-authored-by: Charles Bochet <charles@twenty.com>
2025-06-03 15:01:58 +02:00
Paul Rastoin 97d4ec96af Fix view filter update and deletion propagation (#12082)
# Introduction

Diff description: ~500 tests and +500 additions

close https://github.com/twentyhq/core-team-issues/issues/731

## What has been done here
In a nutshell on a field metadata type ( `SELECT MULTI_SELECT` ) update,
we will be browsing all `ViewFilters` in a post hook searching for some
referencing related updated `fieldMetadata` select. In order to update
or delete the `viewFilter` depending on the associated mutations.

## How to test:
- Add FieldMetadata `SELECT | MULTI_SELECT` to an existing or a new
`objectMetadata`
- Create a filtered view on created `fieldMetadata` with any options you
would like
- Remove some options ( in the best of the world some that are selected
by the filter ) from the `fieldMetadata` settings page
- Go back to the filtered view, removed or updated options should have
been hydrated in the `displayValue` and the filtered data should make
sense

## All filtered options are deleted edge case
If an update implies that a viewFilter does not have any existing
related options anymore, then we remove the viewFilter

## Testing
```sh 
PASS  test/integration/metadata/suites/field-metadata/update-one-field-metadata-related-record.integration-spec.ts (27 s)
  update-one-field-metadata-related-record
    SELECT
      ✓ should delete related view filter if all select field options got deleted (2799 ms)
      ✓ should update related multi selected options view filter (1244 ms)
      ✓ should update related solo selected option view filter (1235 ms)
      ✓ should handle partial deletion of selected options in view filter (1210 ms)
      ✓ should handle reordering of options while maintaining view filter values (1487 ms)
      ✓ should handle no changes update of options while maintaining existing view filter values (1174 ms)
      ✓ should handle adding new options while maintaining existing view filter (1174 ms)
      ✓ should update display value with options label if less than 3 options are selected (1249 ms)
      ✓ should throw error if view filter value is not a stringified JSON array (1300 ms)
    MULTI_SELECT
      ✓ should delete related view filter if all select field options got deleted (1127 ms)
      ✓ should update related multi selected options view filter (1215 ms)
      ✓ should update related solo selected option view filter (1404 ms)
      ✓ should handle partial deletion of selected options in view filter (1936 ms)
      ✓ should handle reordering of options while maintaining view filter values (1261 ms)
      ✓ should handle no changes update of options while maintaining existing view filter values (1831 ms)
      ✓ should handle adding new options while maintaining existing view filter (1610 ms)
      ✓ should update display value with options label if less than 3 options are selected (1889 ms)
      ✓ should throw error if view filter value is not a stringified JSON array (1365 ms)

Test Suites: 1 passed, 1 total
Tests:       18 passed, 18 total
Snapshots:   18 passed, 18 total
Time:        27.039 s
```
## Out of scope
- We should handle ViewFilter validation when extracting its definition
from the metadata
https://github.com/twentyhq/core-team-issues/issues/1009

## Concerns
- Are we able through the api to update an RATING fieldMetadata ? ( if
yes than that's an issue and we should handle RATING the same way than
for SELECT and MULTI_SELECT )
- It's not possible to group a view from a MULTI_SELECT field

The above points create a double nor a triple "lecture" to the post hook
effect:
- ViewGroup -> only SELECT
- VIewFilter -> only SELECT || MULTI_SELECT
- Rating nothing
I think we should determine the scope of all of that

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
2025-05-28 10:22:28 +00:00
Paul Rastoin 442f8dbe3c [QRQC_2] No implicitAny in twenty-server (#12075)
# Introduction
Following https://github.com/twentyhq/twenty/pull/12068
Related with https://github.com/twentyhq/core-team-issues/issues/975

We're enabling `noImplicitAny` handled few use case manually, added a
`ts-expect-error` to the others, we should plan to handle them in the
future
2025-05-15 18:23:22 +02:00