Commit Graph
2875 Commits
Author SHA1 Message Date
Abdul RahmanandGitHub dfb796ec5e Improvement AI chat error handling (#15035)
- Fixed AI chat failures when tool calls were present in conversation
history
- Improved error handling and user feedback for AI streaming errors



https://github.com/user-attachments/assets/ca85820f-32c0-4f42-86ee-98f4543e6038
2025-10-15 10:31:18 +02:00
d750df7fff Automatically clean up soft-deleted records after X days. (#14862)
Closes #14726

### Added
- `trashRetentionDays` field to workspace entity (default: 14 days)  
- Automated trash cleanup using BullMQ jobs  
- Daily cron (00:10 UTC) that enqueues cleanup jobs for all active
workspaces
- Per-workspace limit: 100k records deleted per day  
- Calendar-based retention: records deleted on day X are cleaned up X+14
days later (at midnight UTC boundaries)

### Architecture
- **Cron (WorkspaceTrashCleanupCronJob):** Runs daily, enqueues jobs in
parallel for all workspaces
- **Job (WorkspaceTrashCleanupJob):** Processes individual workspace
cleanup
- **Service (WorkspaceTrashCleanupService):** Discovers tables with
`deletedAt`, deletes old records with quota enforcement
- **Command:** `npx nx run twenty-server:command
cron:workspace:cleanup-trash` to register the cron

### Testing
- Unit tests for service with 100% coverage of public API  
- Tested quota enforcement, error handling, and edge cases

---------

Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
2025-10-14 18:49:40 +02:00
Félix MalfaitandGitHub 9495d07566 Fix: Correct inverted logic in signUpWithoutWorkspace causing 'User already exists' error (#15086)
## Problem

When trying to signup on localhost:3001 with a new random email, users
were getting an error message saying 'User already exists', even though
they were new users.

## Root Cause

The `signUpWithoutWorkspace` method in `sign-in-up.service.ts` had
inverted logic when checking if a user exists. It was using
`findUserByEmailOrThrow` which:
- **Returns the user** if found
- **Throws the provided error** if NOT found

This caused the opposite behavior:
-  **New user (doesn't exist)**: Threw 'User already exists' error
-  **Existing user**: Continued to create duplicate user

## Solution

Changed to use `findUserByEmail` and explicitly check if the user exists
before throwing the appropriate error:

```typescript
const existingUser = await this.userService.findUserByEmail(newUserParams.email);

if (existingUser) {
  throw new AuthException(
    'User already exist',
    AuthExceptionCode.USER_ALREADY_EXIST,
    { userFriendlyMessage: msg`User already exists` },
  );
}
```

This matches the correct pattern already used in `signUpInWorkspace`
(line 431-441 in auth.resolver.ts).

## Changes
- Fixed inverted logic in `signUpWithoutWorkspace` method
- Now correctly validates that user does NOT exist before creating new
user
- Matches the pattern used in `signUpInWorkspace`

## Testing
The fix corrects the logic so that:
-  New users can sign up successfully
-  Existing users get the correct 'User already exists' error
2025-10-14 18:06:24 +02:00
Thomas TrompetteandGitHub 5f1878c96f Simple design for empty node (#15087)
I did an acceptable design for empty node and iterators for release:
- use array field for iterator node. Added an util to stringify arrays
for backward compatibily
- remove icon for empty node
- allow to select a node on empty node selection


https://github.com/user-attachments/assets/b00037a8-aa1d-4784-b973-05973649b46e
2025-10-14 16:58:53 +02:00
Paul RastoinandGitHub 7d747c9876 [REQUIRES_CACHE_FLUSH][GQL_VIEW_GROUP_API_BREAKING_CHANGE] ViewGroup in v2 (#15052)
# Introduction
Adding view-group to core engine v2
Following https://github.com/twentyhq/twenty/pull/15010 ( same pattern )

## What's done
- Created flat-view-group
- flat view group runner
- flat view group builder
- create view group service v2 and input transpilers
- refactor the existing view group resolver to fix standard (
BREAKING_CHANGE on graphql api update especially ) REST stays the same
- refactored the front to consume the mutations autogenerated

close https://github.com/twentyhq/core-team-issues/issues/1665
2025-10-14 15:55:50 +02:00
nitinandGitHub 5fec6a9afc Fix group by y axis "stacked" behaviour (#15081)
also improved seeds
2025-10-14 15:06:50 +02:00
Thomas TrompetteandGitHub 863e3902af Enqueue a new job every 20 step executions (#15068)
To avoid huge workflows to block the worker, we will enqueue a new job
every 20 steps.
This could be more than 20 if there are branches but I think this is
fine, the goal is only to have a limit set.

Also cleaning a bit the code to mark running steps as failed when
workflow fails.

I tested it on a huge workflow:


https://github.com/user-attachments/assets/d7b8e345-d1a1-4467-96fd-92117b500120
2025-10-14 14:20:18 +02:00
Paul RastoinandGitHub bce83c52f0 Increment metadata version only if schema changes v2 (#15064)
# Introduction
After updating a `viewField` in a view the frontend receives a missmatch
metadata version.

That's because the `flatFieldMetadata` needs to be invalidated on a
viewField addition as it contains its primary key in its cache.
Before we would be checking updated flat entity maps, meaning that on a
view field update the flat field metadata maps would also get updated,
but we also invalidate the old v1 cache at the same time. Resuling in a
metadata version missmatch that's not really relevant for the gql schema
integrity

Now we only check if a object or field actions has been processed, if
yes increment the metadata version.
We should deco-relate the v1 object and fields cache from the metadata
version that should only serve as a "Please refresh browser state
because gql schema has mutated"
2025-10-13 16:09:10 +02:00
EtienneandGitHub fc7304bc5a Common api - findMany query (#15004)
Done in this PR : 
- simplify rest input parsing by removing metadata validation (for
filter and orderBy - to add in common) (1st commit)
- simplify result getter handlers signature (array of objectRecord only
for input)


closes https://github.com/twentyhq/core-team-issues/issues/1614
closes https://github.com/twentyhq/core-team-issues/issues/1615
closes https://github.com/twentyhq/core-team-issues/issues/1616
2025-10-13 12:48:50 +00:00
MarieandGitHub 4c7e8444cf Inter-groups orderBy on groupByDimension values (#15024)
Closes https://github.com/twentyhq/core-team-issues/issues/1563.

We now have two inter-group orderBy criteria: one on the aggregate
values, the other on the dimension values.
Ex: I am grouping companies by city and querying the average number of
employees for each group. I can either order the groups by the city
name, or by the average number of employees, or by one then the other.
I could actually also order groups by an aggregated value that I did not
ask for (ex: average ARR), but I cannot order groups by a field value I
did not group records by (ex: country).
[See discord
discussion](https://discord.com/channels/1130383047699738754/1425438213555753050/1425438223097921659)

An example of query variables: 
```
{
  "groupBy": [
    {
      "createdAt": {
        "granularity": "QUARTER_OF_THE_YEAR"
      }
    },
    {"city": true}
  ],
  "orderBy": [
    { 
      "aggregate": {
        "avgEmployees": "DescNullsLast"
      }
    },
    { 
      "aggregate": {
        "percentyEmptyEmployees": "DescNullsLast"
      }
    },
    {
      "city": "AscNullsLast"
    },
    {
      "createdAt": {
        "orderBy": "AscNullsLast",
        "granularity": "QUARTER_OF_THE_YEAR",
      }
    }
  ]
}
```

The aggregate orderBy criteria had already been implemented, but I
updated the implementation to add an "aggregate" key to prefix them, in
order to avoid confusion between the two + for the schema generation not
to break (otherwise we would have an issue when generating
OrderByWithGroupByInput if a user has created a field that has the same
name as an aggregate field, such as avgEmployees).
2025-10-13 09:51:42 +00:00
Paul RastoinandGitHub 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
Abdul RahmanandGitHub 3a6621ef6c Fix agent sync failing due to non-existent AgentEntity property (#15051) 2025-10-13 10:03:27 +02:00
d6526af302 Move search vector command from 1-7 to 1-10 and make it less verbose. (#14892)
Made the command more explicit for idempotency.

- Drop index explicitly (even though PostgreSQL does it automatically
when a column is dropped, but it might be good practice to account for
any unforeseen failures).
- Drop column.
- Create column.
- Create index (if we reach this point, column has already been created
and no error was thrown).
 
Figured we do not need extra queries to check ObjectMetadata for the
existence of person table on a workspace - we can use the IF EXISTS
syntax instead.

---------

Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
2025-10-12 18:25:45 +02:00
7b84db4708 Only fetch steps and trigger for the current workflow version (#15003)
Avoid fetching full steps and trigger for versions that are not the
current version. Because those won't be used anyway. Better for
performances.

Only difficulty was for the `createDraft` mutation. I needed to return
the full created version so I can store it in cache and use it as new
`currentVersion`. Otherwise the current version is considered as
incomplete for a short time, since workflow is fetched separately from
the current version.

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
2025-10-12 18:06:12 +02:00
neo773andGitHub 6f49fd1911 Message channel change 1 (#14942) 2025-10-11 18:18:44 +02:00
560e24fa4b Update unique fields on standard field - include soft deleted records (#14562)
Done : 
- migrate standard unique fields (add : -old)
- deactivate defaultWhereClause
- restore soft deleted if upserted (same for contact creation in mailing
sync)

fixes https://github.com/twentyhq/twenty/issues/14443

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
2025-10-11 15:50:29 +02:00
c681fb7fb6 1658 post mortem 0710 send batch events in webhook (#15022)
Use concurrent calls

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
2025-10-11 12:48:40 +02:00
68c86871dd 🦣🦣🦣 Table virtualization (#14743)
This big PR implements table virtualization with an offset paging,
allowing a way more fluid UX.

It is a v1 that should be improved in the future with partial data
loading and optimization of the browser display performance of a row.

But with this PR we have the solid enough technical foundation, both
frontend and backend, to get to a smooth table UX.

Fixes and improvements after first successful round of development
(needed to have main clean) :

- [x] Delete should refresh virtualized portion only and reset all table
- [x] Fix add new : top and bottom
- [x] Table empty shouldn’t show when first loading
- [x] Fix d&d
- [x]  Fix sorts
- [x] Fix drag when scrolling after a full virtual page (it throws an
error)
- [x] Si update mais qu’on a un sort ou filter, alors il faut trigger le
refresh
- [x]  Reset scroll position between tables
- [x]  Reset scroll shadows between tables
- [x] Setup d&n for virtual list :
https://github.com/hello-pangea/dnd/blob/main/docs/patterns/virtual-lists.md
- [x]  Full table re-render when entering edit mode
- [x] Clean code and prepare for merge

Fixes https://github.com/twentyhq/core-team-issues/issues/1613 that
contains other bugs to be fixed before merge

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
2025-10-11 12:08:10 +02:00
Thomas TrompetteandGitHub d4b81dd20f When workflow is completed, mark as failed steps still running (#15030)
As title.
2025-10-10 17:44:12 +02:00
5013222db4 i18n - translations (#15025)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2025-10-10 15:14:18 +02:00
Paul RastoinandGitHub 651ab184a7 [GQL_VIEW_FILTER_API_BREAKING_CHANGE][WHEN_RELEASED_REQUIRES_CACHE_FLUSH] ViewFilter migration to workspace migration v2 (#15010)
# Introduction
Migrating `viewFilter` to v2 in order to migrate later the field update
side effect on view to v2 too

## What's done
- Created flat-view-filter
- flat view filter runner
- flat view filter builder
- create view filter service v2 and input transpilers
- refactor the existing view filter resolver to fix standard (
BREAKING_CHANGE on graphql api update especially ) REST stays the same
- refactored the front to consume the mutations autogenerated

## New generic tools
### Compare two flat entity
Introducing a new util to compare two flat entity, it's strictly typed
and will be added to the generic builder in a following PR
This will ease flat entity addition as won't required to create a
specific abstraction for comparison
Generic builder will expect specific constant: properties to compare and
properties to stringify

### Transform flat entity for comparison
Forked and refactor the initial existing method for flat entity business
scope and type safety

## Coverage
Migrated existing integration tests to fit new contract API
This PR does not add strong coverage on validation exceptions
Deadlines are too short

close https://github.com/twentyhq/core-team-issues/issues/1666
2025-10-10 15:02:14 +02:00
Thomas TrompetteandGitHub cbfd73cbd8 Enable filters in iterators (#15017)
Filters should not cut the whole workflow. These should only stop the
branch. This PR:
- adds a new skipped status 
- when a filter stops, it still goes to the next step
- the next step will execute if there is at least a successful step
- if only skipped step, it will be skipped as well

It allows to use filters in iterators.


https://github.com/user-attachments/assets/1cfca052-55c0-4ce5-9eb8-63736618d082
2025-10-09 23:14:54 +02:00
nitinandGitHub 44f97058ac Tidy up validation for configuration (#14939)
closes https://github.com/twentyhq/core-team-issues/issues/1605

TODO:  
~~- add stories~~
~~- add base graph on companies when creating a new graph widget~~
2025-10-09 20:34:51 +00:00
Abdul RahmanandGitHub 5adc9fe9b2 feat: mutualize CRUD tools between workflows and AI (#14996)
Closes [#1662](https://github.com/twentyhq/core-team-issues/issues/1662)
2025-10-10 00:55:34 +05:30
b508bd8f9d i18n - translations (#15016)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2025-10-09 18:38:16 +02:00
Félix MalfaitandGitHub 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
martmullandGitHub 660cd38f35 Add timeoutMs to webhooks calls (#15012)
5 second timeout
tested with an application serverlessFunction that delays
2025-10-09 15:54:44 +00:00
d2ed2ad7b9 i18n - translations (#15009)
Created by Github action

Co-authored-by: github-actions <github-actions@twenty.com>
2025-10-09 15:23:25 +02:00
Paul RastoinandGitHub 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
875dc1a6b8 Connect the bar chart to the group by resolver (#14885)
Closes https://github.com/twentyhq/core-team-issues/issues/1535

Video QA:


https://github.com/user-attachments/assets/153fa3f1-08d9-4952-9456-635c602e1f57



https://github.com/user-attachments/assets/d3111afb-4c84-4f57-8a56-5cf666748c86

There are still some formatting and design issues (the labels which are
on top of each other and the values which should be formatted) that will
be fixed in a future PR

---------

Co-authored-by: ehconitin <nitinkoche03@gmail.com>
2025-10-09 14:54:33 +02:00
martmullandGitHub 973b4fef90 Fix Weiko code review returns (#15006)
as title
2025-10-09 14:51:23 +02:00
martmullandGitHub 5252384f14 1588 serverless follow ups 2 (#14998) 2025-10-09 12:56:59 +02:00
MarieandGitHub ebc6bbabaf [Fix] Command to migrate operand values for workflows (#14849)
In [this PR](https://github.com/twentyhq/twenty/pull/14785) we got rid
of what we now call ViewFilterOperandDeprecated, a camelCase version of
ViewFilterOperand, which we thought we only used in the FE. We did not
notice that this enum was used to persist filters used in workflows,
reflected in workflowVersion and workflowRun.
As a result workflow runs were broken. [In this mitigation
PR](https://github.com/twentyhq/twenty/pull/14837) (and [this
one](https://github.com/twentyhq/twenty/pull/14841)) we updated the code
handle both enum values from ViewFilterOperandDeprecated and
ViewFilterOperand, but we still want to get rid of
ViewFilterOperandDeprecated.

the command in this PR replaces the occurences of enum values of
ViewFilterOperandDeprecated.
When this has been merged, deployed and run on the workspaces, we will
be able to remove ViewFilterOperandDeprecated altogether; that will have
to be done in 1.10 though not before.
2025-10-09 09:45:44 +00:00
martmullandGitHub 920ad4c3f2 Return data or raise error in serverless controller (#14989)
as title
TODO validate we do not need to add a new column
2025-10-08 17:09:25 +00:00
EtienneandGitHub 3b992ed549 Rest/Gql - Filter input - Integration tests (#14836) 2025-10-08 18:42:22 +02:00
9fceb2354e i18n - translations (#14988)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2025-10-08 16:35:40 +02:00
Paul RastoinandGitHub 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
1c1739ec80 i18n - translations (#14986)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2025-10-08 16:01:09 +02:00
Antoine MoreauxGitHubgreptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
8e83be43fe feat(auth): enhance error handling for sign-up and existing user checks (#14953)
Refactored error handling in `useSignUpInNewWorkspace` and `useSignInUp`
hooks to provide user-friendly messages. Added validation for existing
users during sign-up, updating server-side logic to throw specific
exceptions (`USER_ALREADY_EXIST`).

Fix
https://twenty-v7.sentry.io/issues/6686753138/events/latest/?environment=prod&project=4507072499810304&query=is%3Aunresolved%20issue.priority%3A%5Bhigh%2C%20medium%5D%20QueryFailedError&referrer=latest-event&sort=date

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2025-10-08 13:43:43 +00:00
WeikoandGitHub a8fd6a0cab Patch yoga cache key (#14981) 2025-10-08 14:37:50 +02:00
1d09cb949a Feat/multivalue limit (#14961)
## Summary
1. This change introduces the ability to limit how many values a
multi-value field can contain (for example: maximum number of emails,
phone numbers, links, or array items).
2. It centralizes the limit as a shared constant and type, validates
settings on the server, updates front-end types and components to
consume the setting, and adds a small settings UI so workspace admins
can change the limit per field.
3. Default behavior is preserved: if no max is configured, the existing
default (10) is used.


## **Fixes Issue:** [#14740
](https://github.com/twentyhq/twenty/issues/14740)

## Manual Test Screenshot
<img width="383" height="451" alt="Screenshot 2025-10-08 002413"
src="https://github.com/user-attachments/assets/a7704af6-10ef-4d10-b8c9-a9eeca03bfd9"
/>


### Values in fields


https://github.com/user-attachments/assets/7f59c4f7-3aca-4f83-8c04-3d44988316e3


Let me know if any changes are needed

---------

Co-authored-by: Félix Malfait <felix@twenty.com>
Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
2025-10-08 12:09:38 +00:00
EtienneandGitHub c693c4d9cf Common API - create common find one query (#14720)
closes https://github.com/twentyhq/core-team-issues/issues/1418

Tested : 
- findOne on Rest and Gql

### Vision
#### Common
- Common is kind of renamed Gql base resolver
- Common handles args (filter, values, ...) validation
- Common accepts depth or raw gql selected fields to compute
selectedFields
- Common is directly called by each CommonQueries (findOne, ...)
service, which extend CommonBaseQuery service
- Common sequence : 
| - Parse & Validate args (args-handlers, to create)
| - Build query (query-parsers : currently in gql-query-parsers, to
move)
| - Execute query
| - Fetch relation + format
#### Rest
- Simple parsing (without metadata validation)
- Calling Common API
- Simple rest response formatting
#### Gql
- Calling Common API
2025-10-08 10:18:42 +00:00
dd4cc98b8a Update Error Handling for 2FA Requirements in Impersonation (#14963)
# Fix 2FA Error Handling in Impersonation

**Related Task:** https://twill.ai/twentyhq/ENG/tasks/2

## Summary

This PR improves error handling for two-factor authentication (2FA)
requirements during user impersonation by enhancing the GraphQL error
utilities.

## Changes Made

- Enhanced GraphQL error handling utilities to properly manage 2FA
requirement errors
- Added improved error messaging and handling for impersonation
scenarios where 2FA is required

## Why This Change?

Fixes #14962 - Users were experiencing unclear error messages when
attempting impersonation without proper 2FA setup. This update ensures
more descriptive error handling and better user experience during the
impersonation flow.

Co-authored-by: Twill <agent@twill.ai>
Co-authored-by: Claude <noreply@anthropic.com>
2025-10-07 22:45:20 +02:00
94546d7c09 Add composites subfield selection in the graph editor (#14931)
Closes https://github.com/twentyhq/core-team-issues/issues/1638
Closes https://github.com/twentyhq/core-team-issues/issues/1623



https://github.com/user-attachments/assets/2dddadaa-781b-497d-b454-0066d599bda3

---------

Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
2025-10-07 18:33:07 +02:00
238f0c0ce2 i18n - translations (#14955)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2025-10-07 18:01:11 +02:00
MarieandGitHub 0474f00b5f [groupBy] Date ranges (#14923)
Closes https://github.com/twentyhq/core-team-issues/issues/1559

2 kinds of date ranges: absolute (day, month, quarter, year) and
"cyclic" (day of the year, month of the year, quarter of the year. ex
dates in january 2024 and january 2025 will be grouped together in
"Q1").

Absolute dates are expressed in datetime format to ease their handling,
while cyclic dates are expressed with more human readable labels ("Q1")
because datetime does not make sense anyway

<img width="851" height="722" alt="Capture d’écran 2025-10-06 à 18 13
29"
src="https://github.com/user-attachments/assets/44cce55d-a363-4bfa-a13d-e385ce5d227e"
/>

<img width="854" height="716" alt="Capture d’écran 2025-10-06 à 18 14
02"
src="https://github.com/user-attachments/assets/c1c1f176-c5b6-441c-b419-7e1692600b65"
/>

<img width="878" height="721" alt="Capture d’écran 2025-10-06 à 18 14
14"
src="https://github.com/user-attachments/assets/76318c53-e1cd-4e73-aa56-9e705ea40596"
/>
2025-10-07 15:52:24 +00:00
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
WeikoandGitHub 9aaed28dcf Fix app deletion missing cache invalidation (#14944)
App deletion currently deletes all related entities through postgres
cascade delete meaning we never run actual migrations ourself and never
trigger cache invalidation (part of migration engine)
2025-10-07 16:57:22 +02:00
Antoine MoreauxGitHubCopilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
f8f113b82e Potential fix for code scanning alert no. 173: Uncontrolled data used in path expression (#14924)
Potential fix for
[https://github.com/twentyhq/twenty/security/code-scanning/173](https://github.com/twentyhq/twenty/security/code-scanning/173)

To fix this vulnerability, ensure that any file path constructed from
untrusted user input is strictly confined to the intended storage
directory. This is best done through path normalization and validation.
Before reading the file, the following steps should be taken:
1. **Resolve the file path**: Use `path.resolve` to normalize the
resulting path, removing any ".." segments or symbolic links.
2. **Check the parent directory**: Ensure that the resolved path starts
with the intended storage directory (`this.options.storagePath`).
3. **Throw an exception**: If the resolved path does not start with the
storage root, throw an exception or otherwise deny the operation.

This check should be placed in all methods that build a file path from
user input in `local.driver.ts`, with at least the `read` method
addressed. The index for this fix will be on lines that construct and
use `filePath`, specifically in the `read` method.

You will need to:
- Import `realpathSync` from `fs` (standard library).
- Add safe path resolution and containment check around file access.

---


_Suggested fixes powered by Copilot Autofix. Review carefully before
merging._

---------

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
2025-10-07 14:33:34 +02:00
f935dd44d7 i18n - translations (#14936)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2025-10-07 13:45:43 +02:00