## Context
Add an eventEmitter instance to twenty datasources so we can emit DB
events.
Add input and output formatting to twenty orm (formatData, formatResult)
Those 2 elements simplified existing logic when we interact with the
ORM, input will be formatted by the ORM so we can directly use
field-like structure instead of column-like. The output will be
formatted, for builder queries it will be in `result.generatedMaps`
where `result.raw` preserves the previous column-like structure.
Important change: We now have an authContext that we can pass when we
get a repository, this will be used for the different events emitted in
the ORM. We also removed the caching for repositories as it was not
scaling well and not necessary imho
Note: An upcoming PR should handle the onDelete: cascade behavior where
we send DESTROY events in cascade when there is an onDelete: CASCADE on
the FK.
---------
Co-authored-by: Charles Bochet <charles@twenty.com>
Fixes https://github.com/twentyhq/twenty/issues/12867
Issue:
when you have a variable `toto` which is: `Record<string, MyType>` and
you do toto['xxx'], this will be typed as `MyType` instead of `MyType |
undefined`
Solutions:
- activate `noUncheckedIndexedAccess` check in tsconfig, this is the
preferred solution but will take time to get there (this raises 600+
errors)
- use a Map: cf https://github.com/twentyhq/twenty/pull/13125/files
- set the type to Partial<Record<string, MyType>>. Drawback is that when
you do Object.values(toto), you'll get `Array<MyType | undefined>`.
Hence why we have to filter these behind
<img width="1512" alt="image"
src="https://github.com/user-attachments/assets/d0a0bfed-c441-4e53-84c2-2da98ccbcf50"
/>
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.`
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
After release 55, we found out that CRON job monitor was red for
CronTriggerCronJob
While only 1 workspace was not in the appropriate state, meaning the
whole command was probably failing for only 1 workspace failing.
We suggest here to catch errors per worksspace and simply push to sentry
the error of the errored workspace relative to workflow trigger.
In this PR
- Determine object record permissions on workflows objects (workflow,
workflowVersion, workflowRun) base on settings permissions @Weiko
- Add Workflow permission guards on workflow resolvers @thomtrp . **Any
method within a resolver that has the SettingsPermission Guard is only
callable by a apiKey or a user that has the permission** (so not by
external parties).
- Add checks bypass in workflow services since 1) for actions gated by
settings permissions, the gate should be done at resolver level, so it
will have been done before the call to the service 2) some service
methods may be called by workflowTriggerController which is callable by
external parties without permissions (ex:
workflowCommonWorkspaceService.getWorkflowVersionOrFail). This is
something we may want to change in the future (still to discuss), by
removing the guard at resolver-level and relying on
shouldBypassPermissionChecks at getRepository and made in a way that we
only bypass for external parties.
- Add checks bypass for actions performed by workflows since they should
not be restricted in our current vision
- Add tests
# What
Fully deprecate old relations because we have one bug tied to it and it
make the codebase complex
# How I've made this PR:
1. remove metadata datasource (we only keep 'core') => this was causing
extra complexity in the refactor + flaky reset
2. merge dev and demo datasets => as I needed to update the tests which
is very painful, I don't want to do it twice
3. remove all code tied to RELATION_METADATA /
relation-metadata.resolver, or anything tied to the old relation system
4. Remove ONE_TO_ONE and MANY_TO_MANY that are not supported
5. fix impacts on the different areas : see functional testing below
# Functional testing
## Functional testing from the front-end:
1. Database Reset ✅
2. Sign In ✅
3. Workspace sign-up ✅
5. Browsing table / kanban / show ✅
6. Assigning a record in a one to many / in a many to one ✅
7. Deleting a record involved in a relation ✅ => broken but not tied to
this PR
8. "Add new" from relation picker ✅ => broken but not tied to this PR
9. Creating a Task / Note, Updating a Task / Note relations, Deleting a
Task / Note (from table, show page, right drawer) ✅ => broken but not
tied to this PR
10. creating a relation from settings (custom / standard x oneToMany /
manyToOne) ✅
11. updating a relation from settings should not be possible ✅
12. deleting a relation from settings (custom / standard x oneToMany /
manyToOne) ✅
13. Make sure timeline activity still work (relation were involved
there), espacially with Task / Note => to be double checked ✅ => Cannot
convert undefined or null to object
14. Workspace deletion / User deletion ✅
15. CSV Import should keep working ✅
16. Permissions: I have tested without permissions V2 as it's still hard
to test v2 work and it's not in prod yet ✅
17. Workflows global test ✅
## From the API:
1. Review open-api documentation (REST) ✅
2. Make sure REST Api are still able to fetch relations ==> won't do, we
have a coupling Get/Update/Create there, this requires refactoring
3. Make sure REST Api is still able to update / remove relation => won't
do same
## Automated tests
1. lint + typescript ✅
2. front unit tests: ✅
3. server unit tests 2 ✅
4. front stories: ✅
5. server integration: ✅
6. chromatic check : expected 0
7. e2e check : expected no more that current failures
## Remove // Todos
1. All are captured by functional tests above, nothing additional to do
## (Un)related regressions
1. Table loading state is not working anymore, we see the empty state
before table content
2. Filtering by Creator Tim Ap return empty results
3. Not possible to add Tasks / Notes / Files from show page
# Result
## New seeds that can be easily extended
<img width="1920" alt="image"
src="https://github.com/user-attachments/assets/d290d130-2a5f-44e6-b419-7e42a89eec4b"
/>
## -5k lines of code
## No more 'metadata' dataSource (we only have 'core)
## No more relationMetadata (I haven't drop the table yet it's not
referenced in the code anymore)
## We are ready to fix the 6 months lag between current API results and
our mocked tests
## No more bug on relation creation / deletion
---------
Co-authored-by: Weiko <corentin@twenty.com>
Co-authored-by: Félix Malfait <felix@twenty.com>
Backend part of https://github.com/twentyhq/core-team-issues/issues/928
- Add fields to database event settings
- If not set, match all automated triggers with the right event name
- If set, event needs at least one updated field listened to be treated
# Introduction
Added a no-explicit-any rule to the twenty-server, not applicable to
tests and integration tests folder
Related to https://github.com/twentyhq/core-team-issues/issues/975
Discussed with Charles
## In case of conflicts
Until this is approved I won't rebased and handle conflict, just need to
drop two latest commits and re run the scripts etc
## Legacy
We decided not to handle the existing lint error occurrences and
programmatically ignored them through a disable next line rule comment
## Open question
We might wanna activate the
[no-explicit-any](https://typescript-eslint.io/rules/no-explicit-any/)
`ignoreRestArgs` for our use case ?
```
ignoreRestArgs?: boolean;
```
---------
Co-authored-by: etiennejouan <jouan.etienne@gmail.com>
This PR attemps at improving sentry grouping and filtering by
- Using the exceptionCode as the fingerprint when the error is a
customException. For this to work in this PR we are now throwing
customExceptions instead of internalServerError deprived of their code.
They will still be converted to Internal server errors when sent back as
response
- Filtering 4xx issues where it was missing (for emailVerification
because errors were not handled, for invalid captcha and billing errors
because they are httpErrors and not graphqlErrors)
---------
Co-authored-by: Félix Malfait <felix@twenty.com>
First and main step of
https://github.com/twentyhq/core-team-issues/issues/747
We are implementing a permission check layer in our custom
WorkspaceEntityManager by overriding all the db-executing methods (this
PR only overrides some as a POC, the rest will be done in the next PR).
Our custom repositories call entity managers under the hood to interact
with the db so this solves the repositories case too.
This is still behind the feature flag IsPermissionsV2Enabled.
In the next PR
- finish overriding all the methods required in WorkspaceEntityManager
- add tests
- create a form filler component
- send the response on submit
- put back a field name. We need it for the step output
- validate a form is well set before activation
TODO:
- we need to refresh to see the form submitted. We need to discuss about
a strategy
- the response is not saved in the step settings. We need a new endpoint
to update workflow run step
https://github.com/user-attachments/assets/0f34a6cd-ed8c-4d9a-a1d4-51455cc83443
We are starting to put too many services in common folder. Version and
step building should be separated into different services and go to the
builder folder. Today builder folder only manage schema.
We should:
- keep services responsible for only one action
- keep modules based on the actual action these provide rather than
having common module
This PR:
- creates a service for workflow version builder
- moves version and step builders to workflow builder folder rather than
commun
- creates separated folders for schema, version and steps
No logic has been added. Only modules created and functions moved.
## Context
In some CustomException exceptions, we were instantiating a code without
initializing it which was overriding the parent code and it was then
lost when retrieving it in filters.
Removing them to make sure we don't reproduce this pattern
In this PR:
- Add support for manual triggers in the backend
- Add a right drawer to let users select the type of trigger they want
- Create a specific right drawer for database event triggers
- Create a right drawer for manual triggers; let the user select where
the manual trigger should be made available
- Create a default trigger as soon as the user selects the type of
trigger they want. It prevents the user to see empty selects for record
type and event type. By default, the database event trigger will be set
to "company.created". It should be visible enough for users to
understand what happens and choose another record type or event type.
https://github.com/user-attachments/assets/29a21985-1823-4890-9eb3-e4f876459c7a
## Setup
This PR can be tested only if some feature flags have specific values:
- `IsWorkflowEnabled` equals `true`
- `IsQueryRunnerTwentyORMEnabled` equals `false`
These feature flags weren't committed to don't break other branches.
## What this PR brings
- Display buttons to activate and deactivate a workflow version and a
button to discard the current draft version. I also scaffolded a "Test"
button, which doesn't do anything for now.
- Wired the activate, deactivate and discard draft buttons to the
backend.
- Made it possible to "edit" active and deactivated versions by
automatically creating a new draft version when the user tries to edit
the version.
- Hide the "Discard Draft", button if the current version is not a draft
or is the first version ever created.
- On the backend, don't consider discarded drafts when checking if a new
draft version can be created.
- On the backend, disallow deleting the first created workflow version.
Otherwise, we will end up with a blank canvas in the front end, and it
will be impossible to recover from it.
- On the backend, disallow running deactivation steps if the workflow
version is not currently active. Previously, we were throwing, which is
unnecessary as it's a valid case.
## Spotted bugs that we must dive into
### Duplicate workflow versions in Apollo cache
https://github.com/user-attachments/assets/7cfffd06-11e0-417a-8da0-f9a5f43b84e2
---------
Co-authored-by: Charles Bochet <charles@twenty.com>
- Create a workflow version when the user visits an empty workflow.
- If the trigger is not defined yet and the user selects either the
standard object type or the event type first, we automatically select
the first option of the other value. Indeed, every state update is
automatically saved on the backend and we need both standard object and
event types to save the event name.
- Introduces a change in the backend. I removed the assertions that
throw when a workflow version is not complete, that is, when it doesn't
have a defined trigger, which is the case when scaffolding a new
workflow with a first empty workflow version.
- We should keep validating the workflow versions, at least when we
publish them. That should be done in a second step.
Workflow version :
- prevent status to be update manually
- prevent creation / deletion in another status than draft
- prevent creation if a draft already exists
Workflow:
- prevent statuses to set manually
WorkflowRun:
- prevent all manual operations
Add listener to keep status on workflows up to date:
- version draft => statuses should contain draft
- version active => statuses should contain active
- version deactivated => if no version active, statuses should contain
deactivated
Renaming also the endpoints because it was not reflecting the full
behaviour.
Finally, adding a new status Archived for versions. Will be used when a
version is deactivated, but is not the last published version anymore.
It means this version cannot be re-activated.