Commit Graph

50 Commits

Author SHA1 Message Date
Paul Rastoin f9ab09c404 Metadata api create entity in workspace custom app (#15911)
# Introduction
Cleaner and fewer scope version of
https://github.com/twentyhq/twenty/pull/15745 ( removed sync-metadata
hack through, too ambitious migration and upgrade )

Please note that this PR won't have any interaction with the existing
sync-metadata
Which mean that the sync metadata does not update the standard entities
applicationId and universalIdentifier, and it won't we will deprecate it
on favor of a workspace migration aka twenty-standard app installation

## API Metadata
Any operation going through the api metadata nows automatically scope
the related entity to the workspace custom application instance. (
optionally passing an applicationId to allow current hacky implem of app
sync service )

We need to either ignore the tests or remove the cli status check from
the blocking status badges for a PR to be merged

## New workspace
Already handled in previous
https://github.com/twentyhq/twenty/pull/15625, when a workspace is
created it gets created a twenty standard and custom workspace instance

All his views and permissions will be prefilled to the its twenty
standard app instance with a specific universalIdentifier

## New universalIdentifier
At the contrary as before with standardIds, universalIdentifier are
unique for a given workspace
This means that createdAt field of both object company and opportunity
will have a unique universalIdentifier whereas they share the same
standardId

## FlatApplication
Introduced the flatApplication and cache. Will migrate existing
`MetadataName` to be `SyncableMetadataName` in a following PR

## What's next
Next we will describe a twenty standard app configuration as json that
will be used to generate a workspace migration that will be run instead
of the sync metadata, in a nutshell we aim to deprecated the sync
metadata
So we can standardize any entity to have a non nullable applicationId
and universalIdentifier

## Upgrade command
Introduced an upgrade command that will create a custom workspace
instance for any workspace that do not have one in order to align with
the new behavior when creating a new workspace
2025-11-23 22:35:17 +01:00
Guillim 4848bc03f3 Update Name of relation fieldMetadata (#15749)
UpdateOne of a Relation that involves a CustomObject, because the
nameSingular needs to be updated in the fieldMetadata
- nameSingular and namePlural must be provided since they are necessary
for morph name computation
  - label sync should be false


Interesting files to look at:

-
packages/twenty-server/src/engine/metadata-modules/object-metadata/utils/build-default-relation-flat-field-metadatas-for-custom-object.util.ts

- UPDATE =>
packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/utils/rename-related-morph-field-on-object-names-update.util.ts
( also update relation indexes ) needs v2 refactor to handle field
relation name update
- CREATE =>
packages/twenty-server/src/engine/metadata-modules/object-metadata/utils/build-default-relation-flat-field-metadatas-for-custom-object.util.ts
( handle morph instead of previous classic relation )
- DELETE => DONE

Edit:
closes https://github.com/twentyhq/core-team-issues/issues/1897

---------

Co-authored-by: prastoin <paul@twenty.com>
2025-11-23 21:45:30 +01:00
Paul Rastoin a39efeb1ab [BREAKING_CHANGE/GRAPHQL/OBJECT_METADATA_CREATE_ONE] Remove object/fields/view-fields v1 implementation (#15823)
# Introduction
Remove the v2 feature flag for view-field field-metadata and
object-metadata metadata entities

## Some details
- Disabled nestjs-query for object metadata creation and explicitly
calling it
- removed all v1 integration tests files

## Remarks
Not remove v2 referencing in both filenaming right now will handle that
globally later

## Breaking change
Due to object metadata resolver createOne standardization had to rename
the input from `CreateObjectInput` to `CreateOneObjectInput`
2025-11-17 10:55:19 +01:00
Paul Rastoin d640b93096 Improve v2 and cache invalidation perfs (#15467)
# Introduction
Log are debug logs of
`packages/twenty-server/test/integration/metadata/suites/object-metadata/create-delete-and-create-object-metadata-v2.integration-spec.ts`run
ten times in a row on clean db reset

## Next
Will improve cache computation to lighter invalidation.
RelationLoad `query` does not seem to work with typeorm so I'll continue
the custom integration i've started in
https://github.com/twentyhq/twenty/tree/optimize-cache-read-v2

## Integration tests duration
Significant test duration improvement too
### Before
<img width="2632" height="1402" alt="image"
src="https://github.com/user-attachments/assets/2f1f0ccf-44de-4856-bfe1-4f45a351763a"
/>

### After
<img width="2632" height="1402" alt="image"
src="https://github.com/user-attachments/assets/4bc9e6db-3046-48b9-b903-1464053936c4"
/>


## What's next
- The legacy cache invalidation removal
- Factorizing redis calls in only one operation

## Autogenerated performance comparison ( including mutation refactor
too )

[Before](https://gist.github.com/prastoin/3c1e21fa9e3b3ce4b0716902ff4a2dd6)

[After](https://gist.github.com/prastoin/7bfddd14bfded2e4991a9378970a026d)

The optimized implementation shows **dramatic performance improvements**
across all metrics:

- 🚀 **Cache Invalidation**: 156.3ms → 76.8ms (**50.9% faster**)
- 🚀 **Builder Operations**: 21.3ms → 14.2ms (**33.3% faster**)
-  **Consistency**: 16.4% more predictable performance

---

## 1. Overall Performance Summary

| Component | Before (avg) | After (avg) | Best (After) | Worst (After)
| Improvement |

|-----------|-------------|-------------|--------------|---------------|-------------|
| **Total Execution Time** | 180.2ms | 110.5ms | 52.3ms | 585.9ms |
**38.7% faster**  |
| **Cache Invalidation** | 156.3ms | 76.8ms | 47.8ms | 285.1ms | **50.9%
faster**  |
| **Transaction Execution** | 22.4ms | 22.1ms | 0.99ms | 314.2ms |
Similar |
| **Initial Cache Retrieval** | 0.81ms | 2.08ms | 0.21ms | 8.24ms |
Similar |
| **Entity Builder (total)** | 21.3ms | 14.2ms | 0.36ms | 42.5ms |
**33.3% faster**  |

### Total Execution Time Distribution

#### Before (Legacy Sequential)
```
Time (ms)    Count  Percentage  Visualization
< 150         18      16%       ████
150-180       32      29%       ███████
180-210       35      32%       ████████
210-250       17      15%       ████
250-350        6       5%       █
> 350          2       2%       ▌
```

#### After (Optimized Parallel)
```
Time (ms)    Count  Percentage  Visualization
< 70          28      31%       ████████
70-100        31      34%       █████████
100-150       18      20%       █████
150-200        8       9%       ██
200-300        4       4%       █
> 300          2       2%       ▌
```
---

## 2. Builder Performance Breakdown

### Field Metadata Builder

| Operation | Before (avg) | After (avg) | Improvement |
|-----------|-------------|-------------|-------------|
| Matrix computation | 3.5ms | 3.4ms | Similar |
| Creation validation | 15.2ms | 2.1ms | **86% faster**  |
| Deletion validation | 0.08ms | 0.06ms | Similar |
| Update validation | 1.3ms | 0.09ms | **93% faster**  |
| Entity processing | 18.6ms | 11.8ms | **37% faster**  |
| **Total validateAndBuild** | **21.3ms** | **14.2ms** | **33% faster**
 |

#### Performance Distribution
```
Before:  ▁▂▄█████▆▄▂▁     (wide spread, 15-28ms range)
After:   ▁▁▃█████▃▁▁       (tight clustering, 10-18ms range)
```

## 4. Cache Invalidation Performance Breakdown

### Cache Invalidation Summary

| Metric | Before (Legacy) | After (Optimized) | Improvement |
|--------|-----------------|-------------------|-------------|
| **Best Time** | 131.965ms | 47.833ms | **63.7% faster**  |
| **10th Percentile** | 140.2ms | 51.7ms | **63.1% faster**  |
| **25th Percentile** | 146.1ms | 54.4ms | **62.7% faster**  |
| **Median (50th)** | 155.1ms | 63.4ms | **59.1% faster**  |
| **Average** | 156.3ms | 76.8ms | **50.9% faster**  |
| **75th Percentile** | 160.2ms | 90.2ms | **43.7% faster**  |
| **90th Percentile** | 191.7ms | 100.2ms | **47.7% faster**  |
| **95th Percentile** | 235.6ms | 110.3ms | **53.2% faster**  |
| **99th Percentile** | 278.2ms | 224.9ms | **19.1% faster**  |
| **Worst Time** | 383.914ms | 285.102ms | **25.7% faster**  |

### Cache Invalidation Time Distribution

#### Before (Legacy Sequential)
```
Time (ms)    Count  Percentage  Visualization
130-140       3       3%        ▊
140-150      15      14%        ████
150-160      48      44%        ███████████
160-180      31      28%        ███████
180-220       8       7%        ██
220-280       3       3%        ▊
> 280         2       2%        ▌
```

#### After (Optimized Parallel + Intersection)
```
Time (ms)    Count  Percentage  Visualization
< 50          3       3%        ▊
50-60        27      25%        ███████
60-70        25      23%        ██████
70-90        25      23%        ██████
90-100       15      14%        ████
100-120       8       7%        ██
120-150       3       3%        ▊
> 150         4       4%        █
```

## 6. Performance Consistency Analysis

### Standard Deviation & Variance

| Metric | Before | After | Improvement |
|--------|--------|-------|-------------|
| **Cache Invalidation Std Dev** | 42.1ms | 35.2ms | **16.4% more
consistent**  |
| **Total Execution Std Dev** | 68.3ms | 89.1ms | Slightly more variable
|
| **Coefficient of Variation (Cache)** | 26.9% | 45.8% | More variance |
| **Outliers (> 2σ)** | 5 cases | 3 cases | **40% fewer outliers**  |

---
2025-10-31 16:20:51 +00:00
Paul Rastoin 1bf40d9dca Fix v2 self relation field creation (#15382)
# Introduction
Fixing self relation field creation in v2
- When computing related flat field to delete on object metadata
deletion that has self relation fields
- On self relation creation validation name availability not searching
for the relation target field of the current object field if it's the
field being validated

## Coverage
- added CUD integration testing on self relation fields

close https://github.com/twentyhq/twenty/issues/15153
2025-10-27 16:07:58 +01:00
Paul Rastoin 2e84c11eae [v2_FIX] Update standard object/field (#15233)
# Introduction
Refactoring the standard overrides dispatcher to only pass over fields
to has to be dispatched in the standardOverrides entry and let the other
side effects resulting from out of standard overrides mutation trigger

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

## This allows
- standard field settings, options etc updates and so on

## Remark
- Determine what we should do on object deactivation ( right now in
production we can still access deactivated object relation properties
and so on e.g deactivate opportunities still accessible from a view
field on company ( still have to re-create it as it has been deleted )
=> decided to leave as it is right now, `isActive` could be considered
as uiDeactivated in the end
- We should also add forbidden standard field mutations validation
inside the builder itself ( here we want to early return in the api
input transpiler too as we don't want to spread invalid side effects )
=> or in the end we could just centralize both but it will generate
several errors

## Coverage
```ts
 PASS  test/integration/metadata/suites/object-metadata/successful-update-one-standard-object-metadata.integration-spec.ts
 PASS  test/integration/metadata/suites/field-metadata/successful-update-one-standard-field-metadata.integration-spec.ts
 PASS  test/integration/metadata/suites/object-metadata/failing-update-one-standard-object-metadata.integration-spec.ts
 PASS  test/integration/metadata/suites/field-metadata/failing-update-one-standard-field-metadata.integration-spec.ts

Test Suites: 4 passed, 4 total
Tests:       18 passed, 18 total
Snapshots:   16 passed, 16 total
Time:        8.721 s, estimated 10 s
```

## Update post review
Faced a behavior where updating back the company label to its original
value would result in storing this value in the standard overrides
Refactored both field and object transpilation behavior to rather remove
the standard override value instead and let fallback on original value

Yes it's quite duplicated will factorize once we move this inside the
builder
2025-10-23 11:02:18 +02:00
Weiko cceeb6ed4d Add applicationId to syncableEntity and fix syncApp deletion (#15170)
## Context
- All flatEntity should extend SyncableEntity
- SyncableEntity should now have applicationId and application relation
- Fix syncApp deletion, should now properly use migration v2 to delete
syncable entities
2025-10-17 17:23:00 +00:00
Paul Rastoin 6188c72f74 Simplify and enhance v2 type devxp (#15032)
# Introduction
This PR introduces a huge type refactor that will leverage dynamic intra
entity optimistic flat maps update in the future and also a more
granular cache invalidation enhancing performances

close https://github.com/twentyhq/core-team-issues/issues/1717
close https://github.com/twentyhq/core-team-issues/issues/1716
close https://github.com/twentyhq/core-team-issues/issues/1643

## What's done

### Comparators centralization
Comparator is now done through global configuration as const for each
metadata names
Thanks to 

Note:
Definition of standard is evolving, standard is now scoped to an app.
Meaning that a manifest should be able to update its own standards
objects but on other app standards ones ? Each synchronizable entities
will have a standardOverrides ?

## Typing refactor

### `AllFlatEntityTypesByMetadataName`

**Single source of truth for the complete type ecosystem**, mapping each
metadata name to its entity types, flat entities, and migration actions:

```typescript
export type AllFlatEntityTypesByMetadataName = {
  fieldMetadata: {
    actions: { created: CreateFieldAction; updated: UpdateFieldAction; deleted: DeleteFieldAction; };
    flatEntity: FlatFieldMetadata;
    entity: FieldMetadataEntity;
  };
  objectMetadata: { /* ... */ };
  // ... all 10 metadata types
};
```

### `ALL_METADATA_NAME_MANY_TO_ONE_RELATIONS`

**Explicitly declares database relationships** between entities with
compile-time validation:

```typescript
export const ALL_METADATA_NAME_MANY_TO_ONE_RELATIONS = {
  viewField: {
    view: 'viewId',
    fieldMetadata: 'fieldMetadataId',
  },
  cronTrigger: {
    serverlessFunction: 'serverlessFunctionId',
  },
  // ... all relations
} as const satisfies MetadataNameAndRelations;
```

### `ALL_FLAT_ENTITY_CONFIGURATION`

**Centralizes comparison and serialization logic** for each metadata
type:

```typescript
export const ALL_FLAT_ENTITY_CONFIGURATION = {
  fieldMetadata: {
    propertiesToCompare: ['name', 'type', 'label', 'defaultValue', /* ... */],
    propertiesToStringify: ['options', 'settings', 'defaultValue'],
  },
  objectMetadata: {
    propertiesToCompare: ['nameSingular', 'namePlural', 'isActive', /* ... */],
    propertiesToStringify: [],
  },
  // ... all metadata types
} as const satisfies AllFlatEntityConfiguration;
```

## Combined Impact

These three configurations work together to create a **strongly-typed,
centrally-managed metadata system**:

1. **`AllFlatEntityTypesByMetadataName`** defines *what exists*
2. **`ALL_METADATA_NAME_MANY_TO_ONE_RELATIONS`** defines *how they
relate*
3. **`ALL_FLAT_ENTITY_CONFIGURATION`** defines *how to compare and
serialize them*

**Result:** Builders and validators become thin wrappers around
type-safe, configuration-driven logic instead of containing scattered,
error-prone manual implementations.

## What's next

### StandardOverrides standardization
Every metadata entity can be a standard one for a workspace if it's an
installed app, which means it might not expose the whole entity api to
be editable through an import dynamically
The standard overrides logic should not be applied to Fields and Objects
but to every entities

At the moment we have a logic of `EDITABLE_PROPERTIES` through the api,
and also `STANDARD_OVERRIDEDABLE_PROPERTIES`
This should be configuration centered like `propertiesToCompare` and
`propertiesToStringify`.
Scoping this PR to two last for the moment. As update dispatch to
standardOverrides could be considered as a side effect prefer waiting to
start the side effect refactor

### Granular Optimistic deprecation
With this new grain at runtime we will be able to add a flat entity and
dispatch its addition to related flat maps, so we don't have to describe
an optimistic method for each flat entity operations

See `addFlatEntityToFlatEntityAndRelatedEntityMapsOrThrow`

Note: Still in wip and included in this PR but about to create a new one
to integrate these utils and remove existing methods

### ValidateBuildAndRun dynamic args typed defintion
We should restrain the devxp to send expected flat maps entity as at
least from to or dependency as we now have the grain both a type lvl and
runtime to do so
It should not be possible in the devxp to forgot adding the views to the
v2 builder when passing the view field anymore ( that would lead to
permanent validation error in view field integrity checks )

## Conclusion
Thanks for reading and reviewing !
Any suggestions are more than welcomed ! ( same as for questions too ! )
2025-10-13 10:31:34 +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
Paul Rastoin 59fbe35a8c Move view in metadata-modules/ and create atomic folder + module for each view entity (#14990)
# Introduction
Preparing view-filter and view-group introduction in v2 core engine
Moving view from `core-modules` to `metadata-modules`

## What happened

### Created dedicated modules for each view entity:
- ViewFieldModule
- ViewFilterModule
- ViewFilterGroupModule
- ViewGroupModule
- ViewSortModule

### Each module is now completely independent with its own:
- Controller
- Resolver
- Service
- Entity

### Created dedicated abstraction metadata module folder for:
- flat-view-field
- flat-view

### Dependencies 
- Eleminated circular dep on ViewModule to all others ones
- Granular import not importing the whole viewModule anymore everywhere


close https://github.com/twentyhq/core-team-issues/issues/1703
2025-10-09 15:18:15 +02:00
Paul Rastoin 4ecc9c622d [WHEN_RELEASED_REQUIRES_CACHE_FLUSH] Object related record logic in v2 (#14937)
# Introduction
Initial motivation here was to migrate the object related records logic
from v1 to v2, please note that now in v2 views aren't records anymore
but core engine entities

## What's done
- Added specific label identifier targeting view field logic
- Handled side effects on viewField creation with lowest position on
object label identifier mutation
- Added viewField relations in field metadate entity + handled
optimistic in builder v2
- Added view relations in object metadata entity + handled optimistic in
builder v2
- Added integration tests covering the side effects and new validation
exceptions
- Sandardized cache computation
- Coverage on object metadata creation side effect on views and view
fields

## Coverage
```ts
 PASS  test/integration/graphql/suites/view/view-field/object-identifier-update-side-effect-on-view-field.integration-spec.ts
  View Field Resolver - Successful object metadata identifier update side effect on view field
    ✓ should create a view field on label identifier object metadata update if it does not exist on view (7 ms)
    ✓ Should not allow deleting a label identifier view field (17 ms)
    ✓ Should not allow destroying a label identifier view field (6 ms)
    ✓ Should not allow updating a label identifier view field visibility to false (8 ms)
    ✓ Should not allow creating a view field with a position lower than the label idenfitier view field (180 ms)
    ✓ Should not allow updated labelIdentifier view field with a position higher than existing other view field (346 ms)
    ✓ Should allow updated labelIdentifier view field with a position higher than existing other view field (434 ms)

Test Suites: 1 passed, 1 total
Tests:       7 passed, 7 total
Snapshots:   5 passed, 5 total
Time:        4.571 s, estimated 5 s
```

close https://github.com/twentyhq/core-team-issues/issues/1664
2025-10-08 16:23:37 +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
Paul Rastoin 23e21cbeea Label identifier validation v2 (#14867)
# Introduction
Adding a hacky way to validate object against fields before fields
validation ( bi-directional validation process )
If you encounter an identical setup we will add a specific devXp as
cleanup validation but for the moment this seems enough

close https://github.com/twentyhq/core-team-issues/issues/1639
2025-10-07 13:27:21 +02:00
Paul Rastoin 170f6d27c5 Extract field metadata build out of object metadata build (#14763)
# Introduction

Extracting the legacy fields build and dispatch out of the object one to
follow the generic flat entity build, also update caches entries for
object

## Main tasks
- flat field map cache
- flat field builder
- refactored the dispatch matrix to return flat entity maps instead of
flat entity arrays
- orchestrator aggregator
- removing legacy code
- making universal identifier aka standardId of standard field for
custom object deterministically dynamic
- perfs debug logs for v2

## TODO
- [x] Refactor the generic entity builder to be dependency flat maps in
order to main foreign keys list in flat parent
- [x] Refactor the flat object metadata to contain the array of related
fields and avoid costy find object fields
- [ ] Improve the create field handler to handle multiple field at once
- [ ] Refactor the dispatch to embbed the comparison
- [ ] Improve perf by extracting from elements out of existing
- [ ] Fix the labelIdentifierId validators on object before field
creation ( integ tests are in failing mode )

## Debug logs snippet

```ts
[EntityBuilder fieldMetadata] matrix computation: 0.027ms
[EntityBuilder fieldMetadata] creation validation: 0.001ms
[EntityBuilder fieldMetadata] deletion validation: 0.293ms
[EntityBuilder fieldMetadata] update validation: 0.006ms
[EntityBuilder fieldMetadata] entity processing: 0.363ms
[EntityBuilder fieldMetadata] validateAndBuild: 0.455ms
[EntityBuilder index] matrix computation: 0.005ms
[EntityBuilder index] creation validation: 0.001ms
[EntityBuilder index] deletion validation: 0.146ms
[EntityBuilder index] update validation: 0.004ms
[EntityBuilder index] entity processing: 0.199ms
[EntityBuilder index] validateAndBuild: 0.228ms
[Runner] Initial cache retrieval: 0.549ms
[BaseWorkspaceMigrationRunnerActionHandlerService] delete_index executeForWorkspaceSchema: 11.665ms
[BaseWorkspaceMigrationRunnerActionHandlerService] delete_index executeForMetadata: 12.864ms
[BaseWorkspaceMigrationRunnerActionHandlerService] delete_field executeForWorkspaceSchema: 1.476ms
[BaseWorkspaceMigrationRunnerActionHandlerService] delete_field executeForMetadata: 6.816ms
[BaseWorkspaceMigrationRunnerActionHandlerService] delete_field executeForWorkspaceSchema: 0.062ms
[BaseWorkspaceMigrationRunnerActionHandlerService] delete_field executeForMetadata: 0.889ms
[Runner] Transaction execution: 23.434ms
[Runner] Cache invalidation: 316.662ms
[Runner] Total execution: 340.767ms
```
As you can see cache invalidation is way to long, we could replace the
cache by the optimistic in the end
2025-10-02 16:07:34 +00:00
martmull 1938202780 1573 extensibility twenty cli handle custom layers for serverless functions of applications (#14779)
- allow specific layers for serverless functions
- add a serverlessFunctionLayer table
- sync application layer
2025-09-30 16:47:49 +02:00
Paul Rastoin 4fbdfb6abc Activate v2 default seed (#14660)
## Introduction
After enabling flag by default got following errors:
```ts
Test Suites: 48 failed, 1 skipped, 97 passed, 145 of 146 total
Tests:       499 failed, 1 skipped, 644 passed, 1144 total
Snapshots:   61 failed, 133 passed, 194 total
Time:        363.226 s
Ran all test suites.
```

## From
<img width="2952" height="1510" alt="image"
src="https://github.com/user-attachments/assets/7e3b20c6-2552-40a7-90bb-2d7b3002c895"
/>

## To
<img width="3134" height="1510" alt="image"
src="https://github.com/user-attachments/assets/4fc9ada4-3c14-4333-a1db-11daf87db8d6"
/>

There's a huge test bundle in the latest shard that we could split up

## Notes
- Set as failing morph relation field rename as for the moment we do not
handle relation field mutation
- fixed the object update and creation validation adding label
identifier field metadata id checks
- and more

Some integrations tests are still on the v1 ( they have before and after
all disabling and re-enabling the flat ) but mainly we now have more
coverage on the v2 than the v1.
Mainly related records, uniqueness have to be migrated the v2 and so
tests too
2025-09-26 16:05:09 +02:00
martmull edb331d68b 1541 extensibility twenty cli use workspace migration v2 to synchronize application objects fields views (#14706)
- synchronize objects


https://github.com/user-attachments/assets/257317bc-2881-4b98-a3d4-6ae52bd72aa0
2025-09-25 11:22:11 +02:00
Paul Rastoin fbd3286792 Index v2 side effects (#14567)
# Introduction
Honestly this implem is a mess, discussing a potential side effect
handler with @weiko before the build and run that would handle each side
effect per entity and operation

Handling:
- [ ] unique index is generated when a field is updated with the
`isUnique`
- [x] an index is generated when a relation is created
- [x] search vector index creation on custom object creation
- [x] renaming a field metadata or an object should re-create all
related indexes which are composed by their namings
- [x] delete object should remove any related indexes
- [x] delete field should update related indexes ( if index ends up
empty it should be removed )
- [ ] on object renaming that contains morph fields -> triggers update
field -> trigger index recompute
- [x] on update name renaming should recompute all related indexes

## TODO
- [x] Integration testing
- [ ] Refactor the index maps cache to be storing a
`idsByObjectMetadataId`
- [x] Refactor deterministic name to use order sorting
- [x] Remove flat index from flat object

## What's next

Will handle morph indexes in a new dedicated PR for the moment will
stick to this
Same for the cache improvement and uniqueness
2025-09-23 10:57:11 +02:00
Paul Rastoin 371c26bc9b Flat entity maps cache generic service + runner dynamically retrieving invalidating update cache + view service v2 refactor (#14508)
# Introduction
Migrate previous runner only iterating on `flatObjectMetadataMaps` to
`allFlatEntityMaps`.
Refactored the optimistic to be handled inside the actions handler

## Workspace flat map cache
Introducing a new service and registry, that will dynamically retrieve
and or recompute requested cache when called

## Runner refactor
Runner now dynamically invalidate updated cache at the end of the
transaction

close
https://github.com/orgs/twentyhq/projects/1/views/8?pane=issue&itemId=129136356&issue=twentyhq%7Ccore-team-issues%7C1492
close
https://github.com/orgs/twentyhq/projects/1/views/8?pane=issue&itemId=129136210&issue=twentyhq%7Ccore-team-issues%7C1494
2025-09-16 14:39:42 +02:00
Paul Rastoin 579f704baa Plugging + improving the new orchestrator (#14430)
# Introduction
Initially this PR was about introducing integration coverage for view
field v2 tests
But feature itself wasn't finished, so ended up in a TDD style
devlopment

## The new orchestrator
Orchestrator will organize each flat entity diffing inferring either
validation errors or actions sequentially.
A new builder has been introduced when extended requires to define flat
entity validation utils and action generation for `delete` `update`
`create` operations. It's highly typed using generics

## View field integration testing
Introduce coverage on failing basic tests cases for view field
operations, successfull create is also covered. We could still add more
coverage, will be done later in following PRs

## Remaining tasks for upcoming PRs:
- [ ] rename builder methods names to add generate action in their names
- [x] refactor view validation to handle field and view uniqueness index
combination
- [ ] implem strict update validator on view and view field
- [ ] dynamic cache invalidation post run
- [ ] Add coverage to successful and failing view field operations
- [ ] error formatting summary computation
- [ ] extract types out of the workspace entity builder

## Some vision
Currently flat field metadata maps is nested in the flat object metadata
maps, making its build nested too. ( not extending the new generic
builder )
We will refactor this part for both to be stored extra flat such as the
other existing flat entities.
It will require a small refactor to the generic builder that will not
only have impact on the currently built flat entity optimistic cache but
to an other one ( for example when building a create object action it
will also have an impact flat field maps )
2025-09-15 14:32:57 +02:00
Paul Rastoin ebc48e3bb2 Remove unused utils from v2 (#14356) 2025-09-08 18:16:50 +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 6522498df0 Failing morph relation creation v2 integration testing & fixes (#14253)
# Introduction
closes https://github.com/twentyhq/core-team-issues/issues/1408
2025-09-03 12:14:43 +02:00
Paul Rastoin 6d6ee939cf Morph relation field v2 fixes (#14216)
Fixes, depending on the contextual current operation in order to
retrieve relation target and all morph related fields to be delete:
- DELETE Field,
- DELETE Object, 
- UPDATE Field, that affects all others related fields
2025-09-02 12:17:20 +00:00
Weiko 80347b7b38 Rename uniqueIdentifier to universalIdentifier (#14201) 2025-09-01 12:33:59 +02:00
Paul Rastoin 7df9094939 RelationFieldMetadataType to MorphOrRelationFieldMetadataType and so on (#14160) 2025-08-29 15:25:12 +00:00
Paul Rastoin 6397a4d906 Update object handle morph join col rename (#14153)
# Introduction
When udpating a flat object metadata name we need to search for RELATION
field that has a MORPH RELATION target flat field metadata with
MANY_TO_ONE relationType as its settings are binded to the updated flat
object metadata name

In the best of the world we would remove this complexity to be computed
at runtime only and deprecate implemented logic here

close https://github.com/twentyhq/core-team-issues/issues/1412
2025-08-29 16:46:15 +02:00
Guillim f6843fcdef Seed morph metadata (#14149)
in thie PR we create the tooling to allow the relation and morph
relation creation during the seed process.

We use the ObjectMetadataMaps in order to fasten the process

Fixes https://github.com/twentyhq/core-team-issues/issues/1179
2025-08-29 15:04:08 +02:00
Félix Malfait e264d7f32b fix: Enable Lingui recommended rules and fix all translation violations (#14133)
- Enable lingui/no-single-variables-to-translate and all other
recommended Lingui rules
- Fix single variable translation patterns (t`${variable}` → variable)
- Fix expression-in-message violations by extracting variables
- Fix t-call-in-function violations by moving translations inside
functions
- Update ESLint configs to use linguiPlugin.configs['flat/recommended']
- Clean up unused imports and improve translation patterns
2025-08-28 15:12:38 +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
neo773 3ef94f6e8c Refactor read only object and fields (#13936)
Co-authored-by: Charles Bochet <charles@twenty.com>
2025-08-19 18:10:35 +02:00
Paul Rastoin 9eaede6e1e Fix default relation standard field deletion (#13975)
Authorize relation standard field deletion if targetObjectMetadata is
not present in existingFlatObjectMetadataMaps
2025-08-19 16:12:04 +02:00
Paul Rastoin 86170c705e Standard relation field for custom objects (#13968)
## Introduction
Introducing default standard relation field for custom objects
2025-08-19 13:53:46 +02:00
Weiko 43bb8d6043 Workspace schema migration runner v2 - Fix Enums and Create TsVector (#13955)
## Context
- Adding ts-vector generatedType/asExpression as TS_VECTOR settings
- Using those settings to setup properly tsVector searchVector column
through the new migration runner
- Fix enum creation/suppression

Note: regarding the new tsVector, we should implement a command to
update existing fields

TODO: 
- TS_VECTOR search vector column update (note: should be properly
updated whenever the object labelIdentifier is updated or a new TEXT
field is added to the object to follow the current logic)
- relation type fields and columns are not implemented yet 
- index migrations
2025-08-18 16:33:51 +02:00
Paul Rastoin 3d3191425e Workspace migration v2 builder embed field metadata validation (#13960)
# Introduction
Following https://github.com/twentyhq/twenty/pull/13934
Finalizing object migration
Also refactored existing to follow same for const loop pattern
2025-08-18 16:00:58 +02:00
Paul Rastoin 6629092645 Refactor builder to embed Object validation create/delete/update (#13934)
# Introduction
Moving validation directly in the builder that has the perfect
granularity to do it.
When importing we won't have to infer and dispatch on the operation
nature ( update delete create ) and validate accordingly

## Objects
Only migrated object validation for the moment even though create object
involves a validate flat field metadata creation call too

## TODO
- improve `otherFlatObjectMetadataMapsToValidate` naming too vague

## Next
- handle fields validation within fields actions build
- Unit test coverage validation issue on builder and validate
- integration test plugging with new feature flag

## Manual tested
- Update
- Delete
- Create TODO
2025-08-18 11:16:16 +02:00
Paul Rastoin ad77d2f462 ObjectMetadataServiceV2.updateOne (#13891)
# Introduction
Last service to get migrated, `updateOne` object metadata service v2
2025-08-14 09:37:52 +00:00
Paul Rastoin e8e81957a6 ObjectMetadataServiceV2.deleteOne (#13871)
# Introduction
- Implementing the delete one for the new object metadata service v2.
- Handling relation fields and fields in the first place to finally
remove the object
- puting back updatedAt and createdAt in flat metadatas
- duplicate criteria addition for flat object
- removing datasource id from object metadata dto
2025-08-13 09:20:17 +00:00
Paul Rastoin de0468d104 ObjectMetadataServiceV2.createOne (#13849)
# Introduction

Introducing v2 object metadata service create one handler v2, not
overkilling the flatObjectMetadata validation for the moment that will
require sequential validation for the import in order to handle
relations and morph relations
2025-08-12 16:49:35 +02:00
Paul Rastoin 28e5186f23 FieldMetadataServiceV2.deleteOneField (#13744)
# Introduction
- Migrated the `deleteOneField` handler to new workspace migration v2
style
- Refactored the build to expect `flatObjectMetadataMaps` that he
instantly translate at the be beginning
Commented `ObjectMetadataServiceV2` as it's still not implemented
neither used
2025-08-08 15:11:47 +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
Paul Rastoin 83a560c936 Refactor FieldMetadataService and validation to consume FlatObjectMetadataMaps (#13719)
# Introduction
Following recent `FlatObjectMetadataMaps` manipulation, transpilers
utils introduction in https://github.com/twentyhq/twenty/pull/13620
Refactored the field metadata service and validator to iterate over
`FlatObjectMetadataMaps` instead of `FlatObjectMetadata[]`.

The object metadata service v2 is shadow coding
2025-08-07 17:19:07 +02:00
Paul Rastoin b71e6b0609 FlatObject/FieldMetadata and FlatObjectMetadataMaps mocks (#13691)
# Introduction
As discussed with Charles we will introduce a new command to convert
seeded twenty instance into `flatObjectMetadatas` `flatFieldMetadatas`
mocks but later.
We can think of this PR as a partial freeze of this command result run
even tho the template might not be strictly the same in the end.
For the moment we only extracted few objects with their fields in order
to start testing newly introduced tools

used scripts ( not committed and written on the fly ):
https://gist.github.com/prastoin/bdfae0da1e3e9c66ae21c972afbd0d1e
https://gist.github.com/prastoin/30029989bf57de9303e3f7ec235271ee
2025-08-06 17:10:09 +02:00
Paul Rastoin ca3383cd86 Feat workspace migration maintains flat object metadata maps (#13620)
# Introduction
The workspace migration runner v2 now computes the next
flatObjectMetadataMaps post current ws migration action has been run
So the following one have an up to date informations

## Still TODO
~~Remove any contextual information from workspace migration v2 and
consume the optimistic cache to retrieve them~~

## Next steps
- Create flat-object-metadata-maps testing toolbox and flat-object/field
testing toolbox
- Implement strong coverage on every created utils/transpiler and
applyWorkspaceMigrationAction method
2025-08-06 13:10:14 +00:00
Paul Rastoin 6af99ab1ea FlatObjectMetadataMaps and non FP style refactor (#13590)
# Introduction
- Retrieveing flat-object-metadata-maps for input transpilation, we
decided not to introduce map merge for remaining validation operation
for the moment
- Refactored the field metadata service v2 to sequentially
optimistically render on each iteration
2025-08-05 08:46:28 +00:00
Paul Rastoin d8ec2e3959 Field metadata v2 relation (#13562)
# Introduction
- deferrable constraint on foreignKey definition
- fix relation creation input transpilation to flat field metadata
- fix validation to allow contextual validation on not already created
field metadata ( usefull for relation, and will be necessary for the
import )
2025-08-04 09:30:13 +00:00
Paul Rastoin b98d8c4d39 Fix flatFieldMetadata transpiler to fit real cache typing (#13529)
# Introduction
The cache is not accurately typed regarding the `fieldMetadataMaps` they
do not contain relations
In this way we:
- Created dedicated transpilation tools for cache manipulation
- Avoided recursivity in transpilation tools
- Fix and finalized the createMany fields metadata service v2
- Refactor the validator to handle validation :)

## What's next:
- Implem integration tests to make things run 🙃 
- migrate existing settings valdiation
- Finish the create object metadata service
- Handle update input transpilation and validation
2025-08-01 18:39:11 +02: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