Commit Graph

108 Commits

Author SHA1 Message Date
b8f33f6f59 5095 move onboardingstatus computation from frontend to backend (#5954)
- move front `onboardingStatus` computing to server side
- add logic to `useSetNextOnboardingStatus`
- update some missing redirections in
`usePageChangeEffectNavigateLocation`
- separate subscriptionStatus from onboardingStatus
2024-06-28 17:32:02 +02:00
95c5602a4e feat: manually implement joinColumn (#6022)
This PR introduce a new decorator named `@WorkspaceJoinColumn`, the goal
of this one is to manually declare the join columns inside the workspace
entities, so we don't have to rely on `ObjectRecord` type.

This decorator can be used that way:

```typescript
  @WorkspaceRelation({
    standardId: ACTIVITY_TARGET_STANDARD_FIELD_IDS.company,
    type: RelationMetadataType.MANY_TO_ONE,
    label: 'Company',
    description: 'ActivityTarget company',
    icon: 'IconBuildingSkyscraper',
    inverseSideTarget: () => CompanyWorkspaceEntity,
    inverseSideFieldKey: 'activityTargets',
  })
  @WorkspaceIsNullable()
  company: Relation<CompanyWorkspaceEntity> | null;

  // The argument is the name of the relation above
  @WorkspaceJoinColumn('company')
  companyId: string | null;
```
2024-06-27 11:41:22 +02:00
1eb9c582f3 Rename mutation maximum affected records (#6042)
As per my last comment on https://github.com/twentyhq/twenty/pull/6039,
we decided to rename this var
2024-06-26 18:00:25 +02:00
6599bc136e Add mutationMaximumRecordAffected to clientConfig (#6039)
We are exposing the server mutationMaximumRecordAffected value via
clientConfig so it can be used by the FE.

This is a first step for https://github.com/twentyhq/twenty/issues/6025

<img width="610" alt="Screenshot 2024-06-26 at 14 58 26"
src="https://github.com/twentyhq/twenty/assets/1834158/9d192976-fd22-45d2-bdaa-a8ff6bb90ca2">
2024-06-26 15:21:13 +02:00
cf67ed09d0 Upsert endpoint and CSV import upsert (#5970)
This PR introduces an `upsert` parameter (along the existing `data`
param) for `createOne` and `createMany` mutations.

When upsert is set to `true`, the function will look for records with
the same id if an id was passed. If not id was passed, it will leverage
the existing duplicate check mechanism to find a duplicate. If a record
is found, then the function will perform an update instead of a create.

Unfortunately I had to remove some nice tests that existing on the args
factory. Those tests where mostly testing the duplication rule
generation logic but through a GraphQL angle. Since I moved the
duplication rule logic to a dedicated service, if I kept the tests but
mocked the service we wouldn't really be testing anything useful. The
right path would be to create new tests for this service that compare
the JSON output and not the GraphQL output but I chose not to work on
this as it's equivalent to rewriting the tests from scratch and I have
other competing priorities.
2024-06-26 11:39:16 +02:00
78865ee73e Fix billing signup when workspace does not exist (#6018) 2024-06-25 15:51:01 +02:00
77f9f6473b Create feature flag for calendar V2 (#5998)
Create feature flag for calendar V2
2024-06-24 13:54:52 +02:00
0b4bfce324 feat: drop calendar repository (#5824)
This PR is replacing and removing all the raw queries and repositories
with the new `TwentyORM` and injection system using
`@InjectWorkspaceRepository`.
Some logic that was contained inside repositories has been moved to the
services.
In this PR we're only replacing repositories for calendar feature.

---------

Co-authored-by: Weiko <corentin@twenty.com>
Co-authored-by: bosiraphael <raphael.bosi@gmail.com>
Co-authored-by: Charles Bochet <charles@twenty.com>
2024-06-22 09:26:58 +02:00
6fd8dab552 5582 get httpsapitwentycomrestmetadata objects filters dont work (#5906)
- Remove filters from metadata rest api
- add limite before and after parameters for metadata
- remove update from metadata relations
- fix typing issue
- fix naming
- fix before parameter

---------

Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
2024-06-18 18:55:13 +02:00
1ba7037fdc 5581 get httpsapitwentycomrestmetadata relations not working (#5867)
Filtering relations is not allowed
(see`packages/twenty-server/src/engine/metadata-modules/relation-metadata/dtos/relation-metadata.dto.ts`)
so we remove filtering for find many relation

we also fixed some bug in result structure and metadata open-api schema
2024-06-17 10:59:29 +02:00
d99b9d1d6b feat: Enhancements to MessageQueue Module with Decorators (#5657)
### Overview

This PR introduces significant enhancements to the MessageQueue module
by integrating `@Processor`, `@Process`, and `@InjectMessageQueue`
decorators. These changes streamline the process of defining and
managing queue processors and job handlers, and also allow for
request-scoped handlers, improving compatibility with services that rely
on scoped providers like TwentyORM repositories.

### Key Features

1. **Decorator-based Job Handling**: Use `@Processor` and `@Process`
decorators to define job handlers declaratively.
2. **Request Scope Support**: Job handlers can be scoped per request,
enhancing integration with request-scoped services.

### Usage

#### Defining Processors and Job Handlers

The `@Processor` decorator is used to define a class that processes jobs
for a specific queue. The `@Process` decorator is applied to methods
within this class to define specific job handlers.

##### Example 1: Specific Job Handlers

```typescript
import { Processor, Process, InjectMessageQueue } from 'src/engine/integrations/message-queue';

@Processor('taskQueue')
export class TaskProcessor {

  @Process('taskA')
  async handleTaskA(job: { id: string, data: any }) {
    console.log(`Handling task A with data:`, job.data);
    // Logic for task A
  }

  @Process('taskB')
  async handleTaskB(job: { id: string, data: any }) {
    console.log(`Handling task B with data:`, job.data);
    // Logic for task B
  }
}
```

In the example above, `TaskProcessor` is responsible for processing jobs
in the `taskQueue`. The `handleTaskA` method will only be called for
jobs with the name `taskA`, while `handleTaskB` will be called for
`taskB` jobs.

##### Example 2: General Job Handler

```typescript
import { Processor, Process, InjectMessageQueue } from 'src/engine/integrations/message-queue';

@Processor('generalQueue')
export class GeneralProcessor {

  @Process()
  async handleAnyJob(job: { id: string, name: string, data: any }) {
    console.log(`Handling job ${job.name} with data:`, job.data);
    // Logic for any job
  }
}
```

In this example, `GeneralProcessor` handles all jobs in the
`generalQueue`, regardless of the job name. The `handleAnyJob` method
will be invoked for every job added to the `generalQueue`.

#### Adding Jobs to a Queue

You can use the `@InjectMessageQueue` decorator to inject a queue into a
service and add jobs to it.

##### Example:

```typescript
import { Injectable } from '@nestjs/common';
import { InjectMessageQueue, MessageQueue } from 'src/engine/integrations/message-queue';

@Injectable()
export class TaskService {
  constructor(
    @InjectMessageQueue('taskQueue') private readonly taskQueue: MessageQueue,
  ) {}

  async addTaskA(data: any) {
    await this.taskQueue.add('taskA', data);
  }

  async addTaskB(data: any) {
    await this.taskQueue.add('taskB', data);
  }
}
```

In this example, `TaskService` adds jobs to the `taskQueue`. The
`addTaskA` and `addTaskB` methods add jobs named `taskA` and `taskB`,
respectively, to the queue.

#### Using Scoped Job Handlers

To utilize request-scoped job handlers, specify the scope in the
`@Processor` decorator. This is particularly useful for services that
use scoped repositories like those in TwentyORM.

##### Example:

```typescript
import { Processor, Process, InjectMessageQueue, Scope } from 'src/engine/integrations/message-queue';

@Processor({ name: 'scopedQueue', scope: Scope.REQUEST })
export class ScopedTaskProcessor {

  @Process('scopedTask')
  async handleScopedTask(job: { id: string, data: any }) {
    console.log(`Handling scoped task with data:`, job.data);
    // Logic for scoped task, which might use request-scoped services
  }
}
```

Here, the `ScopedTaskProcessor` is associated with `scopedQueue` and
operates with request scope. This setup is essential when the job
handler relies on services that need to be instantiated per request,
such as scoped repositories.

### Migration Notes

- **Decorators**: Refactor job handlers to use `@Processor` and
`@Process` decorators.
- **Request Scope**: Utilize the scope option in `@Processor` if your
job handlers depend on request-scoped services.

Fix #5628

---------

Co-authored-by: Weiko <corentin@twenty.com>
2024-06-17 09:49:37 +02:00
be18ee4d7d Fix sentry error (#5848)
Fixes
https://twenty-v7.sentry.io/issues/5363536663/?environment=prod&project=4507072499810304&query=is%3Aunresolved+issue.priority%3A%5Bhigh%2C+medium%5D&referrer=issue-stream&statsPeriod=7d&stream_index=0

- handle error properly in twenty-server
- display backend error message
2024-06-14 12:41:55 +02:00
28202cc9e0 Fix workspaceLogo in invite-email (#5865)
## Fixes wrong image url in email 

![image](https://github.com/twentyhq/twenty/assets/29927851/5fb1524b-874d-4723-8450-0284382bbeb3)

## Done
- duplicates and adapt `getImageAbsoluteURIOrBase64` from `twenty-front`
in `twenty-email`
- send `SERVER_URL` to email builder
2024-06-14 12:36:24 +02:00
374237a988 Refacto rest api, fix graphl playground, improve analytics (#5844)
- Improve the rest api by introducing startingAfter/endingBefore (we
previously had lastCursor), and moving pageInfo/totalCount outside of
the data object.
- Fix broken GraphQL playground on website
- Improve analytics by sending server url
2024-06-12 21:54:33 +02:00
3986824017 5623 add an inviteteam onboarding step (#5769)
## Changes
- add a new invite Team onboarding step
- update currentUser.state to currentUser.onboardingStep

## Edge cases
We will never display invite team onboarding step 
- if number of workspaceMember > 1
- if a workspaceMember as been deleted

## Important changes
Update typeorm package version to 0.3.20 because we needed a fix on
`indexPredicates` pushed in 0.3.20 version
(https://github.com/typeorm/typeorm/issues/10191)

## Result
<img width="844" alt="image"
src="https://github.com/twentyhq/twenty/assets/29927851/0dab54cf-7c66-4c64-b0c9-b0973889a148">



https://github.com/twentyhq/twenty/assets/29927851/13268d0a-cfa7-42a4-84c6-9e1fbbe48912
2024-06-12 21:13:18 +02:00
30d3ebc68a Fix missing cursor on rest api (#5841)
## Before

![image](https://github.com/twentyhq/twenty/assets/29927851/fc3bad2d-5238-4afa-b528-409fbff3902c)

## After

![image](https://github.com/twentyhq/twenty/assets/29927851/418174c1-aafb-4ea2-a936-50c03ea17764)

![image](https://github.com/twentyhq/twenty/assets/29927851/03439033-db6b-44b0-9613-f766babc1d2d)

![image](https://github.com/twentyhq/twenty/assets/29927851/f0e5e998-3c61-437d-863f-7289609d0d30)
2024-06-12 16:25:04 +02:00
bd22bfce2e Push event for user signup (#5837)
Need to setup Twenty as a CRM for Twenty the Twenty team
2024-06-12 12:35:46 +02:00
a0d9fdb3de Fix bugs and telemetry (#5832)
Bugfix 1:
<img width="491" alt="Screenshot 2024-06-12 at 07 19 42"
src="https://github.com/twentyhq/twenty/assets/6399865/e3ad2771-4edd-453d-9d85-f429177dfd15">

Bugfix 2:
<img width="259" alt="Screenshot 2024-06-12 at 07 47 02"
src="https://github.com/twentyhq/twenty/assets/6399865/2f82c90e-2180-4290-b12e-e72910fb108c">

Change 3:
I remove the "telemetry anonymization enabled" parameter as it was
misleading, we were anonymization ids but still forwarding the workspace
name which is imo more sensitive than an ID
2024-06-12 08:11:48 +02:00
4994a9c3a9 Api docs remove Relations from Post & Patch (#5817)
* Remove relations where they cannot be used
* Removed duplicated schema for findMany
* Reuse schema for Relation variant to reduce size of sent json object

closes #5778

---------

Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
Co-authored-by: martmull <martmull@hotmail.fr>
2024-06-11 15:31:48 +02:00
5c15fcd249 5805 typing issue in rest api (#5818)
Fixed types for:
- uuid
- email
- datetime
- date
- number
- currency.amountMicros
- select
- multiSelect

## Before

![image](https://github.com/twentyhq/twenty/assets/29927851/4bfa3a6d-a26f-47e4-a46f-7a5582825482)


## After

![image](https://github.com/twentyhq/twenty/assets/29927851/0bbab32f-4172-4525-91d1-76c37f299ac0)
2024-06-11 14:54:02 +02:00
7f9fdf3ff6 Fix performance issue mail (#5780)
In this PR, I'm mainly doing two things:
- uniformizing messaging-messages-import and
messaging-message-list-fetch behaviors (cron.job and job)
- improving performances of these cron.jobs by not triggering the jobs
if the stage is not relevant
- making sure these jobs have same signature (workspaceId +
messageChannelId)
2024-06-08 11:07:36 +02:00
1208fed7b3 Add endpoint to create postgres credentials (#5767)
First step for creating credentials for database proxy.

In next PRs:
- When calling endpoint, create database Postgres on proxy server 
- Setup user on database using postgresCredentials
- Build remote server on DB to access workspace data
2024-06-07 14:45:24 +02:00
9f6a6c3282 5622 add a syncemail onboarding step (#5689)
- add sync email onboarding step
- refactor calendar and email visibility enums
- add a new table `keyValuePair` in `core` schema
- add a new resolved boolean field `skipSyncEmail` in current user




https://github.com/twentyhq/twenty/assets/29927851/de791475-5bfe-47f9-8e90-76c349fba56f
2024-06-05 18:16:53 +02:00
e9d3ed99ca 5078 ability to invite team members (#5750)
## Added features
- update team member setting page
- add a section to send invitation by email
- add a new invitation email
- update email font to 'Trebuchet MS' as Google Inter font is not
working, we need to use a web safe font
https://templates.mailchimp.com/design/typography/

## Demo

https://github.com/twentyhq/twenty/assets/29927851/c731d883-1599-4281-87e3-0671f36994ae

## Invitation Email

![image](https://github.com/twentyhq/twenty/assets/29927851/d569fc64-fa0c-4769-a3dd-1193a12b495c)
2024-06-05 16:35:14 +02:00
eab8deb211 Rework messaging modules (#5710)
In this PR, I'm refactoring the messaging module into smaller pieces
that have **ONE** responsibility: import messages, clean messages,
handle message participant creation, instead of having ~30 modules (1
per service, jobs, cron, ...). This is mandatory to start introducing
drivers (gmails, office365, ...) IMO. It is too difficult to enforce
common interfaces as we have too many interfaces (30 modules...). All
modules should not be exposed

Right now, we have services that are almost functions:
do-that-and-this.service.ts / do-that-and-this.module.ts
I believe we should have something more organized at a high level and it
does not matter that much if we have a bit of code duplicates.

Note that the proposal is not fully implemented in the current PR that
has only focused on messaging folder (biggest part)

Here is the high level proposal:
- connected-account: token-refresher
- blocklist
- messaging: message-importer, message-cleaner, message-participants,
... (right now I'm keeping a big messaging-common but this will
disappear see below)
- calendar: calendar-importer, calendar-cleaner, ...

Consequences:
1) It's OK to re-implement several times some things. Example:
- error handling in connected-account, messaging, and calendar instead
of trying to unify. They are actually different error handling. The only
things that might be in common is the GmailError => CommonError parsing
and I'm not even sure it makes a lot of sense as these 3 apis might have
different format actually
- auto-creation. Calendar and Messaging could actually have different
rules

2) **We should not have circular dependencies:** 
- I believe this was the reason why we had so many modules, to be able
to cherry pick the one we wanted to avoid circular deps. This is not the
right approach IMO, we need architect the whole messaging by defining
high level blocks that won't have circular dependencies by design. If we
encounter one, we should rethink and break the block in a way that makes
sense.
- ex: connected-account.resolver is not in the same module as
token-refresher. ==> connected-account.resolver => message-importer (as
we trigger full sync job when we connect an account) => token-refresher
(as we refresh token on message import).

connected-account.resolver and token-refresher both in connected-account
folder but should be in different modules. Otherwise it's a circular
dependency. It does not mean that we should create 1 module per service
as it was done before

In a nutshell: The code needs to be thought in term of reponsibilities
and in a way that enforce high level interfaces (and avoid circular
dependencies)

Bonus: As you can see, this code is also removing a lot of code because
of the removal of many .module.ts (also because I'm removing the sync
scripts v2 feature flag end removing old code)
Bonus: I have prefixed services name with Messaging to improve dev xp.
GmailErrorHandler could be different between MessagingGmailErrorHandler
and CalendarGmailErrorHandler for instance
2024-06-03 11:16:05 +02:00
f166171a1c 5531 update gmail full sync to v2 (#5674)
Closes #5531

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
2024-05-31 13:29:58 +02:00
a12c1aad5e fix: user has to login every time chrome sidepanel is opened (#5544)
We can pass the auth tokens to our front app via post message, which
will also allow us to pass route names to navigate on it
2024-05-30 12:58:45 +02:00
f58c961d98 Remove feature flag for Links field (#5606) 2024-05-27 16:05:22 +02:00
936ac4027a Introduce a new feature flag for contact creation (#5570)
Introduce new feature flag
`IS_CONTACT_CREATION_FOR_SENT_AND_RECEIVED_EMAILS_ENABLED` to allow
contacts to be created for sent and received emails.
2024-05-24 18:55:21 +02:00
87465b13ee 5507 modify the partial sync cron to work with the new statuses (#5512)
Closes #5507
2024-05-24 18:27:54 +02:00
04bf697b25 feat: add feature flag to activate Links field creation (#5535)
Related issue: #3607
2024-05-22 18:06:32 +02:00
beaaf33544 5498 create a feature flag is gmail sync v2 enabled (#5501)
Closes #5498
2024-05-22 09:25:06 +02:00
ec248f8605 Remove dumb code placement (#5494)
Fix a bug introduced in [this
PR](https://github.com/twentyhq/twenty/pull/5254/files)

When a subscription is created, we need to create the subscription,
#5254 return if no subscription is created so the sub can never be
created at all

This PR fixes that
2024-05-21 12:08:51 +02:00
88f5eb669e 4689 multi workspace i should be able to accept an invite if im already logged in (#5454)
- split signInUp to separate Invitation from signInUp
- update redirection logic
- add a resolver for userWorkspace
- add a mutation to add a user to a workspace
- authorize /invite/hash while loggedIn
- add a button to join a workspace

### Base functionnality

https://github.com/twentyhq/twenty/assets/29927851/a1075a4e-a2af-4184-aa3e-e163711277a1

### Error handling

https://github.com/twentyhq/twenty/assets/29927851/1bdd78ce-933a-4860-a87a-3f1f7bda389e
2024-05-20 12:11:38 +02:00
8b5f79ddbf fix: multiple twenty orm issues & show an example of use (#5439)
This PR is fixing some issues and adding enhancement in TwentyORM:

- [x] Composite fields in nested relations are not formatted properly
- [x] Passing operators like `Any` in `where` condition is breaking the
query
- [x] Ability to auto load workspace-entities based on a regex path

I've also introduced an example of use for `CalendarEventService`:


https://github.com/twentyhq/twenty/pull/5439/files#diff-3a7dffc0dea57345d10e70c648e911f98fe237248bcea124dafa9c8deb1db748R15
2024-05-20 11:01:47 +02:00
fdf10f17e2 4655 batch endpoints on the rest api (#5411)
- add POST rest/batch/<OBJECT> endpoint
- rearrange rest api code with Twenty quality standard
- unify REST API error format
- Added PATCH verb to update objects
- In openapi schema, we replaced PUT with PATCH verb to comply with REST
standard
- fix openApi schema to match the REST api

### Batch Create

![image](https://github.com/twentyhq/twenty/assets/29927851/fe8cd91d-7b35-477f-9077-3477b57b054c)

### Replace PUT by PATCH in open Api

![image](https://github.com/twentyhq/twenty/assets/29927851/9a95060d-0b21-4a04-a3fa-c53390897b5b)

### Error format unification

![image](https://github.com/twentyhq/twenty/assets/29927851/f47dfcef-a4f8-4f93-8504-22f82a8d8057)

![image](https://github.com/twentyhq/twenty/assets/29927851/d76a87e2-2bf6-4ed9-a142-71ad7c123beb)

![image](https://github.com/twentyhq/twenty/assets/29927851/6db59ad3-0ba7-4390-a02d-be15884e2516)
2024-05-16 14:15:49 +02:00
ea5a7ba70e feat: add renew token query for apollo client (chrome-extension) (#5200)
fixes - #5203
2024-05-16 10:21:16 +02:00
f0383e3147 feat: twenty orm sync (#5266)
This PR is updating all object metadata entities with the new
decorators, and deleting the old ones.
This way we can use the new TwentyORM with all the standard objects.

---------

Co-authored-by: Weiko <corentin@twenty.com>
2024-05-15 16:58:47 +02:00
a53ce1c488 Track backend events (#5405)
Add tracking to backend events, we might disable frontend tracking which
doesn't bring much value to improve the product
2024-05-14 16:42:28 +02:00
0018ec78b0 4840 multi workspace update user userworkspace inconsistent on delete set null constraint (#5373) 2024-05-13 14:50:27 +02:00
8576127b47 Add migration to restrict users without workspaces (#5369)
- update set null ON DELETE constraint to RESTRICT
- update missing updates
2024-05-13 14:18:45 +02:00
1ac8abb118 5188 bug some canceled subscriptions are billed (#5254)
When user is deleting its account on a specific workspace, we remove it
as if it was a workspaceMember, and if no workspaceMember remains, we
delete the workspace and the associated stripe subscription
2024-05-13 10:23:32 +02:00
ff77a4ee21 Feat/migrate password reset token to app token table (#5051)
# This PR

- Fix #5021 
- Migrates `passwordResetToken` and `passwordResetTokenExpiresAt` fields
from `core.users` to `core.appToken`
- Marks those fields as `deprecated` so we can remove them later if we
are happy with the transition -- I took this decision on my own,
@FellipeMTX let me know what you think about it, we can also remove them
straight away if you think it's better
- Fixed the `database:migration` script from the `twenty-server` to:
```json
    "database:migrate": {
      "executor": "nx:run-commands",
      "dependsOn": ["build"], // added this line
      "options": {
        "cwd": "packages/twenty-server",
        "commands": [
          "nx typeorm -- migration:run -d src/database/typeorm/metadata/metadata.datasource",
          "nx typeorm -- migration:run -d src/database/typeorm/core/core.datasource"
        ],
        "parallel": false
      }
    },
```
The migration script wasn't running because the builds were not executed
- [x] Added unit tests for the token.service file's changes

Looking forward to hearing feedback from you

cc: @charlesBochet

---------

Co-authored-by: Weiko <corentin@twenty.com>
2024-05-06 15:30:03 +02:00
a750901582 Remove Feature Flag on Calendar (#5288)
Remove Calendar feature Flag!
2024-05-03 19:10:33 +02:00
a5a9e0e238 Remove isMultiSelect feature flag (#5280)
As title

Co-authored-by: Thomas Trompette <thomast@twenty.com>
2024-05-03 16:30:58 +02:00
f9c19c839b Build stripe integration on backend side (#5246)
Adding stripe integration by making the server logic independent of the
input fields:
- query factories (remote server, foreign data wrapper, foreign table)
to loop on fields and values without hardcoding the names of the fields
- adding stripe input and type
- add the logic to handle static schema. Simply creating a big object to
store into the server

Additional work:
- rename username field to user. This is the input intended for postgres
user mapping and we now need a matching by name

---------

Co-authored-by: Thomas Trompette <thomast@twenty.com>
2024-05-02 17:13:15 +02:00
05a90d6153 Constant api version (#5248)
closes #5206
2024-05-02 14:21:19 +02:00
8853226d17 feat: add Links field type (#5176)
Closes #5113

---------

Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
2024-05-01 11:56:14 +02:00
e2185448ed Feat/twenty orm (#5153)
## Introduction

This PR introduces "TwentyORM," a custom ORM module designed to
streamline database interactions within our workspace schema, reducing
the need for raw SQL queries. The API mirrors TypeORM's to provide a
familiar interface while integrating enhancements specific to our
project's needs.

To facilitate this integration, new decorators prefixed with `Workspace`
have been implemented. These decorators are used to define entity
metadata more explicitly and are critical in constructing our schema
dynamically.

## New Features

- **Custom ORM System**: Named "TwentyORM," which aligns closely with
TypeORM for ease of use but is tailored to our application's specific
requirements.
- **Decorator-Driven Configuration**: Entities are now configured with
`Workspace`-prefixed decorators that clearly define schema mappings and
relationships directly within the entity classes.
- **Injectable Repositories**: Repositories can be injected similarly to
TypeORM, allowing for flexible and straightforward data management.

## Example Implementations

### Decorated Entity Definitions

Entities are defined with new decorators that outline table and field
metadata, relationships, and constraints. Here are examples of these
implementations:

#### Company Metadata Object

```typescript
@WorkspaceObject({
  standardId: STANDARD_OBJECT_IDS.company,
  namePlural: 'companies',
  labelSingular: 'Company',
  labelPlural: 'Companies',
  description: 'A company',
  icon: 'IconBuildingSkyscraper',
})
export class CompanyObjectMetadata extends BaseObjectMetadata {
  @WorkspaceField({
    standardId: COMPANY_STANDARD_FIELD_IDS.name,
    type: FieldMetadataType.TEXT,
    label: 'Name',
    description: 'The company name',
    icon: 'IconBuildingSkyscraper',
  })
  name: string;

  @WorkspaceField({
    standardId: COMPANY_STANDARD_FIELD_IDS.xLink,
    type: FieldMetadataType.LINK,
    label: 'X',
    description: 'The company Twitter/X account',
    icon: 'IconBrandX',
  })
  @WorkspaceIsNullable()
  xLink: LinkMetadata;

  @WorkspaceField({
    standardId: COMPANY_STANDARD_FIELD_IDS.position,
    type: FieldMetadataType.POSITION,
    label: 'Position',
    description: 'Company record position',
    icon: 'IconHierarchy2',
  })
  @WorkspaceIsSystem()
  @WorkspaceIsNullable()
  position: number;

  @WorkspaceRelation({
    standardId: COMPANY_STANDARD_FIELD_IDS.accountOwner,
    label: 'Account Owner',
    description: 'Your team member responsible for managing the company account',
    type: RelationMetadataType.MANY_TO_ONE,
    inverseSideTarget: () => WorkspaceMemberObjectMetadata,
    inverseSideFieldKey: 'accountOwnerForCompanies',
    onDelete: RelationOnDeleteAction.SET_NULL,
  })
  @WorkspaceIsNullable()
  accountOwner: WorkspaceMemberObjectMetadata;
}
```

#### Workspace Member Metadata Object

```typescript
@WorkspaceObject({
  standardId: STANDARD_OBJECT_IDS.workspaceMember,
  namePlural: 'workspaceMembers',
  labelSingular: 'Workspace Member',
  labelPlural: 'Workspace Members',
  description: 'A workspace member',
  icon: 'IconUserCircle',
})
@WorkspaceIsSystem()
@WorkspaceIsNotAuditLogged()
export class WorkspaceMemberObjectMetadata extends BaseObjectMetadata {
  @WorkspaceField({
    standardId: WORKSPACE_MEMBER_STANDARD_FIELD_IDS.name,
    type: FieldMetadataType.FULL_NAME,
    label: 'Name',
    description: 'Workspace member name',
    icon: 'IconCircleUser',
  })
  name: FullNameMetadata;

  @WorkspaceRelation({
    standardId: WORKSPACE_MEMBER_STANDARD_FIELD_IDS.accountOwnerForCompanies,
    label: 'Account Owner For Companies',
    description: 'Account owner for companies',
    icon: 'IconBriefcase',
    type: RelationMetadataType.ONE_TO_MANY,
    inverseSideTarget: () => CompanyObjectMetadata,
    inverseSideFieldKey: 'accountOwner',
    onDelete: RelationOnDeleteAction.SET_NULL,
  })
  accountOwnerForCompanies: Relation

<CompanyObjectMetadata[]>;
}
```

### Injectable Repository Usage

Repositories can be directly injected into services, allowing for
streamlined query operations:

```typescript
export class CompanyService {
  constructor(
    @InjectWorkspaceRepository(CompanyObjectMetadata)
    private readonly companyObjectMetadataRepository: WorkspaceRepository<CompanyObjectMetadata>,
  ) {}

  async companies(): Promise<CompanyObjectMetadata[]> {
    // Example queries demonstrating simple and relation-loaded operations
    const simpleCompanies = await this.companyObjectMetadataRepository.find({});
    const companiesWithOwners = await this.companyObjectMetadataRepository.find({
      relations: ['accountOwner'],
    });
    const companiesFilteredByLinkLabel = await this.companyObjectMetadataRepository.find({
      where: { xLinkLabel: 'MyLabel' },
    });

    return companiesFilteredByLinkLabel;
  }
}
```

## Conclusions

This PR sets the foundation for a decorator-driven ORM layer that
simplifies data interactions and supports complex entity relationships
while maintaining clean and manageable code architecture. This is not
finished yet, and should be extended.
All the standard objects needs to be migrated and all the module using
the old decorators too.

---------

Co-authored-by: Weiko <corentin@twenty.com>
2024-04-29 16:47:42 +02:00
224c8d361b Setup relations for remote objects (#5149)
New strategy:
- add settings field on FieldMetadata. Contains a boolean isIdField and
for numbers, a precision
- if idField, the graphql scalar returned will be a GraphQL id. This
will allow the app to work even for ids that are not uuid
- remove globals dateScalar and numberScalar modes. These were not used
- set limit as Integer
- check manually in query runner mutations that we send a valid id

Todo left:
- remove WorkspaceBuildSchemaOptions since this is not used anymore.
Will do in another PR

---------

Co-authored-by: Thomas Trompette <thomast@twenty.com>
Co-authored-by: Weiko <corentin@twenty.com>
2024-04-26 14:37:34 +02:00