Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/twenty orm #5153

Merged
merged 5 commits into from
Apr 29, 2024
Merged

Feat/twenty orm #5153

merged 5 commits into from
Apr 29, 2024

Conversation

magrinj
Copy link
Member

@magrinj magrinj commented Apr 24, 2024

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

@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

@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:

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.

@Weiko Weiko self-requested a review April 24, 2024 16:04
@magrinj magrinj removed the -PR: wip label Apr 26, 2024
@magrinj magrinj marked this pull request as ready for review April 26, 2024 15:05
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
import { DataSourceStorage } from 'src/engine/twenty-orm/storage/data-source.storage';

@Injectable({ scope: Scope.REQUEST })
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DataSource it automatically created based on each request, a storage is system is in place to avoid creating too many data sources.

// logging: this.environmentService.get('DEBUG_MODE')
// ? ['query', 'error']
// : ['error'],
logging: 'all',
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Loggin all for now, as this shouldn't be used in production yet

): string => {
if (!isCompositeFieldMetadataType(fieldMetadata.type)) {
): string;
export function computeCompositeColumnName<
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: I feel like this might get complex over time if this can be used with either FieldMetadata or string which are quite different 🤔

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's like that for now since some previous PR, because I'm not yet sure on which constructor I should choose

propertyKey.toString(),
);

metadataArgsStorage.fields.push({
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer pure functions if possible to avoid side effects and will be easier to debug, wdyt?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes you're right, I've done that at first to create a quick class

);
const objectName = convertClassNameToObjectMetadataName(target.name);

metadataArgsStorage.objects.push({
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here

@@ -0,0 +1,17 @@
import { DataSource } from 'typeorm';

export class DataSourceStorage {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the impact on performance if we have many datasources with opened connections? Should we provide TTL and destroy the datasource after some time?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually it's storing data source the same way it was done before, but we can of course optimize this with TTL if needed, but I'm not sure it's really needed

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, let's discuss later if we need to improve that part 👍

import { WorkspaceIsSystem } from 'src/engine/twenty-orm/decorators/workspace-is-system.decorator';
import { WorkspaceIsNotAuditLogged } from 'src/engine/twenty-orm/decorators/workspace-is-not-audit-logged.decorator';

@WorkspaceObject({
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we keeping those names or is it temporary before removing ObjectMetadata decorator?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a proposal of new name, as params will now differ over what really ObjectMetadata entities are

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes makes sense 👍

Copy link
Member

@Weiko Weiko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we can add some tests + 1 test using 1 repository? I'm ok to merge it like this since it's still not fully rolled-out

) {}

getRepository<T extends ObjectLiteral>(
entityClass: Type<T>,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we call it ObjectMetadataClass?

Copy link
Member

@Weiko Weiko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we can add some tests + 1 test using 1 repository? I'm ok to merge it like this since it's still not fully rolled-out

/**
* Create providers for the given entities.
*/
export function createTwemtyORMProviders(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo

Copy link
Member

@Weiko Weiko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, great job @magrinj! 👏

@Weiko Weiko merged commit e218544 into main Apr 29, 2024
5 checks passed
@Weiko Weiko deleted the feat/twenty-orm branch April 29, 2024 14:47
@magrinj magrinj mentioned this pull request May 2, 2024
Weiko pushed a commit that referenced this pull request May 6, 2024
This PR is a follow up of PR #5153.
This one introduce some changes on how we're querying composite fields.
We can do:

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

  async companies(): Promise<CompanyObjectMetadata[]> {
    // Old way
    // const companiesFilteredByLinkLabel = await this.companyObjectMetadataRepository.find({
    //   where: { xLinkLabel: 'MyLabel' },
    // });
    // Result will return xLinkLabel property

    // New way
    const companiesFilteredByLinkLabel = await this.companyObjectMetadataRepository.find({
      where: { xLink: { label:  'MyLabel' } },
    });
    // Result will return { xLink: { label: 'MyLabel' } } property instead of  { xLinkLabel: 'MyLabel' }

    return companiesFilteredByLinkLabel;
  }
}
```

Also we can now inject `TwentyORMManage` class to manually create a
repository based on a given `workspaceId` using
`getRepositoryForWorkspace` function that way:

```typescript
export class CompanyService {
  constructor(
    // TwentyORMModule should be initialized
    private readonly twentyORMManager,
  ) {}

  async companies(): Promise<CompanyObjectMetadata[]> {
    const repository = await this.twentyORMManager.getRepositoryForWorkspace(
      '8bb6e872-a71f-4341-82b5-6b56fa81cd77',
      CompanyObjectMetadata,
    );

    const companies = await repository.find();

    return companies;
  }
}
```
srp-pawar added a commit to synapsenet-arena/lead360 that referenced this pull request May 15, 2024
commit 09d4bdb
Merge: bc86ac7 0af86ea
Author: Shubham Pawar <[email protected]>
Date:   Wed May 15 10:33:40 2024 +0530

    Merge branch 'main' of https://github.com/synapsenet-arena/lead360

commit 0af86ea
Author: Rob Luke <[email protected]>
Date:   Wed May 15 10:52:33 2024 +1000

    docs: fix calendar enable environmental variable (twentyhq#5417)

    Hi twenty team,
    Thanks for making such a great product, it's a pleasure to use and see
    the rapid development.
    @charlesBochet helped me find this error in my setup

commit 8842292
Author: Vinod Rathod <[email protected]>
Date:   Tue May 14 21:58:13 2024 +0530

    Fixed left padding for switcher icon on the table checkboxes twentyhq#4351 (twentyhq#4963)

    Closes twentyhq#4351  - Fixed spacing issue in TopBar file.

    ---------

    Co-authored-by: Félix Malfait <[email protected]>

commit 0b1f646
Author: Ady Beraud <[email protected]>
Date:   Tue May 14 18:59:48 2024 +0300

    Added loader to Download Image + modified GitHub stars (twentyhq#5407)

    - Added loader to download image in contributor page:

    https://github.com/twentyhq/twenty/assets/102751374/a6db1d80-01ed-4b07-9a57-e533012f5aa9

    - Modified GitHub stars - rounded to the nearest integer

commit 1a61405
Author: Thomas des Francs <[email protected]>
Date:   Tue May 14 17:36:00 2024 +0200

    User guide images (twentyhq#5410)

    Updated 2 illustrations & added colors on filter illustration

    ---------

    Co-authored-by: Weiko <[email protected]>

commit 53b9505
Author: Thomas des Francs <[email protected]>
Date:   Tue May 14 17:23:54 2024 +0200

    Added the illustrations for the user guide (twentyhq#5409)

    Added illustrations for all user-guide articles

commit ce19582
Author: Anchit Sinha <[email protected]>
Date:   Tue May 14 20:32:53 2024 +0530

    4599-feat(front): Add Copy Button to Floating Inputs (twentyhq#4789)

    Closes twentyhq#4599

    **Changes:**
    - Added copy button to floating inputs of Text, Number, Phone, Link and
    Email fields.

    ---------

    Co-authored-by: Félix Malfait <[email protected]>
    Co-authored-by: Lucas Bordeau <[email protected]>
    Co-authored-by: Weiko <[email protected]>
    Co-authored-by: Charles Bochet <[email protected]>

commit a53ce1c
Author: Félix Malfait <[email protected]>
Date:   Tue May 14 16:42:28 2024 +0200

    Track backend events (twentyhq#5405)

    Add tracking to backend events, we might disable frontend tracking which
    doesn't bring much value to improve the product

commit ffdd3a7
Author: martmull <[email protected]>
Date:   Tue May 14 13:21:55 2024 +0200

    Return graphql errors when exists (twentyhq#5389)

    - throw badRequest with graphql error messages when graphql request
    fails
    - clean some code

    Before
    <img width="1470" alt="image"
    src="https://github.com/twentyhq/twenty/assets/29927851/0b700d9a-2bbe-41f7-84a9-981dc7dd5344">

    After

    ![image](https://github.com/twentyhq/twenty/assets/29927851/6bbaaf7c-1244-473d-9ae5-4fefc6a1b994)

commit 1bc9b78
Author: gitstart-twenty <[email protected]>
Date:   Tue May 14 10:58:30 2024 +0100

    Show Data Skeleton Loading (twentyhq#5328)

    ### Description

    Show Data Skeleton loading

    ### Refs

    twentyhq#4460

    ### Demo

    Figma:
    https://www.figma.com/file/xt8O9mFeLl46C5InWwoMrN/Twenty?type=design&node-id=25429-70096&mode=design&t=VRxtgYCKnJkl2zpt-0

    https://jam.dev/c/178878cb-e600-4370-94d5-c8c12c8fe0d5

    Fixes twentyhq#4460

    ---------

    Co-authored-by: gitstart-twenty <[email protected]>
    Co-authored-by: v1b3m <[email protected]>
    Co-authored-by: Matheus <[email protected]>

commit de438b0
Author: Thomas Trompette <[email protected]>
Date:   Mon May 13 18:00:13 2024 +0200

    Add stripe connection option (twentyhq#5372)

    - Refactor creation and edition form so it handles stripe integration
    and not only postgres
    - Add a hook `useIsSettingsIntegrationEnabled` to avoid checking feature
    flags everywhere
    - Add zod schema for stripe

    <img width="250" alt="Capture d’écran 2024-05-13 à 12 41 52"
    src="https://github.com/twentyhq/twenty/assets/22936103/a77e7278-5d79-4f95-bddb-ae9ddd1426eb">
    <img width="250" alt="Capture d’écran 2024-05-13 à 12 41 59"
    src="https://github.com/twentyhq/twenty/assets/22936103/d617dc6a-31a4-43c8-8192-dbfb7157de1c">
    <img width="250" alt="Capture d’écran 2024-05-13 à 12 42 08"
    src="https://github.com/twentyhq/twenty/assets/22936103/c4e2d0e4-f826-436d-89be-4d1679a27861">

    ---------

    Co-authored-by: Thomas Trompette <[email protected]>

commit b9154f3
Author: Marie <[email protected]>
Date:   Mon May 13 17:43:51 2024 +0200

    Enable deletion of relation fields (twentyhq#5338)

    In this PR
    1. Enable deletion of relation fields in the product and via the api
    (migration part was missing in the api)
    3. Change wording, only use "deactivate" and "delete" everywhere (and
    not a mix of the two + "disable", "erase")

commit 0018ec7
Author: martmull <[email protected]>
Date:   Mon May 13 14:50:27 2024 +0200

    4840 multi workspace update user userworkspace inconsistent on delete set null constraint (twentyhq#5373)

commit 8576127
Author: martmull <[email protected]>
Date:   Mon May 13 14:18:45 2024 +0200

    Add migration to restrict users without workspaces (twentyhq#5369)

    - update set null ON DELETE constraint to RESTRICT
    - update missing updates

commit 1ac8abb
Author: martmull <[email protected]>
Date:   Mon May 13 10:23:32 2024 +0200

    5188 bug some canceled subscriptions are billed (twentyhq#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

commit 92acfe5
Author: Thomas Hillesøy <[email protected]>
Date:   Mon May 13 09:55:57 2024 +0200

    feat: Currencies NOK and SEK (twentyhq#5359)

    Related to twentyhq#5351 and twentyhq#5353

    Adding both currencies NOK and SEK, using icon
    https://tabler.io/icons/icon/currency-krone-swedish

    ---------

    Co-authored-by: Félix Malfait <[email protected]>

commit 4a7aabd
Author: Ady Beraud <[email protected]>
Date:   Mon May 13 10:55:30 2024 +0300

    Added GitHub init (twentyhq#5317)

    - Added github:init to allow full import, as opposed to gitHub:sync
    which allows partial sync and therefore respecting Github API Limit
    quota.

    ---------

    Co-authored-by: Ady Beraud <[email protected]>

commit 321ce72
Author: Mohamed Muhsin <[email protected]>
Date:   Mon May 13 08:16:51 2024 +0200

    Add new Currencies with Corresponding Icons (twentyhq#5353)

    ### **Description**
    Add 3 new currencies

    ### **Refs**
    twentyhq#5351

    ### **Demo**
    <img width="678" alt="Screenshot 2024-05-11 at 1 30 55 AM"
    src="https://github.com/twentyhq/twenty/assets/62111075/cc88ba46-cc5a-41d6-baf8-c28851c629ae">

commit eb2b896
Author: brendanlaschke <[email protected]>
Date:   Mon May 13 08:14:47 2024 +0200

    Releases page (twentyhq#5346)

    closes twentyhq#4103

    <img width="696" alt="Bildschirmfoto 2024-05-10 um 08 16 19"
    src="https://github.com/twentyhq/twenty/assets/48770548/e34cd348-2522-408c-886c-636595292e0f">

commit 86caf00
Author: Brandon Yee <[email protected]>
Date:   Sat May 11 03:30:03 2024 -0400

    Update README.md (twentyhq#5345)

    Fixed Grammar

commit 0fb416d
Author: Thaïs <[email protected]>
Date:   Fri May 10 18:31:22 2024 +0200

    fix: fix field select options positions after option removal (twentyhq#5350)

    - Adds an util `toSpliced`. We cannot used the native Javascript
    `Array.prototype.toSpliced` method as Chromatic servers don't support
    it.
    - Makes sure Select field options have sequential positions after
    removing an option (form validation schema checks that positions are
    sequential and considers options invalid otherwise).

commit 72521d5
Author: Marie <[email protected]>
Date:   Fri May 10 18:18:39 2024 +0200

    Disable save button while submitting form in settings  (twentyhq#5352)

    as per title

commit ae0e31a
Author: Marie <[email protected]>
Date:   Fri May 10 13:56:41 2024 +0200

    Bump versions to 0.11.2 (twentyhq#5349)

commit 999a668
Author: Marie <[email protected]>
Date:   Fri May 10 13:00:24 2024 +0200

    [fix] Do not stringify json field value if null (twentyhq#5348)

    as per title

    https://github.com/twentyhq/twenty/assets/51697796/74ff0185-f20c-4ff1-9d89-3078063f23e1

commit 8590bd7
Author: Charles Bochet <[email protected]>
Date:   Fri May 10 10:26:46 2024 +0200

    Refactor default value for select (twentyhq#5343)

    In this PR, we are refactoring two things:
    - leverage field.defaultValue for Select and MultiSelect settings form
    (instead of option.isDefault)
    - use quoted string (ex: "'USD'") for string default values to embrace
    backend format

    ---------

    Co-authored-by: Thaïs Guigon <[email protected]>

commit 7728c09
Author: Thaïs <[email protected]>
Date:   Thu May 9 01:56:15 2024 +0200

    fix: fix several field bugs (twentyhq#5339)

    After discussing with @charlesBochet, several fixes are needed on
    fields:
    - [x] Disable Boolean field `defaultValue` edition for now (On
    `defaultValue` update, newly created records are not taking the updated
    `defaultValue` into account. Setting the `defaultValue` on creation is
    fine.)
    - [x] Disable Phone field creation for now
    - [x] For the Person object, display the "Phone" field as a field of
    type Phone (right now its type is Text; later we'll migrate it to a
    proper Phone field).
    - [x] Fix RawJson field display (displaying `[object Object]` in Record
    Table cells).
    - [x] In Settings/Data Model, on Relation field creation/edition,
    "Object destination" select is not working properly if an object was not
    manually selected (displays Companies by default but creates a relation
    to another random object than Companies).

commit 005045c
Author: Thaïs <[email protected]>
Date:   Wed May 8 12:13:34 2024 +0200

    fix: fix Settings field form validation for certain field types (twentyhq#5335)

    Related to twentyhq#4295

    Following twentyhq#5326, field types other than:
    - `FieldMetadataType.Boolean`
    - `FieldMetadataType.Currency`
    - `FieldMetadataType.Relation`
    - `FieldMetadataType.Select`
    - `FieldMetadataType.MultiSelect`

    Cannot be saved as they are not included in the form validation schema.
    This PR makes sure they are included and can therefore be
    created/edited.

commit 8c85e7b
Author: Thaïs <[email protected]>
Date:   Wed May 8 11:51:09 2024 +0200

    fix: fix storybook:build cache output path (twentyhq#5336)

commit 863554b
Author: Charles Bochet <[email protected]>
Date:   Wed May 8 09:28:28 2024 +0200

    Fix storybook (twentyhq#5334)

    Fixing the last broken stories, tests should be back to green!

commit 770ee11
Author: Indrakant D <[email protected]>
Date:   Wed May 8 12:23:26 2024 +0530

    fix: Blue Button Secondary Color Issue in Dark Mode (twentyhq#5333)

    fixes
    [twentyhq#5305](twentyhq#5305 (comment))

    light mode:

    <img width="738" alt="Screenshot 2024-05-08 at 2 24 41 AM"
    src="https://github.com/twentyhq/twenty/assets/60315832/de01bbfa-6b54-4149-9930-b38840483ddf">

    <br>
    <br>

    dark mode

    <img width="735" alt="Screenshot 2024-05-08 at 2 24 55 AM"
    src="https://github.com/twentyhq/twenty/assets/60315832/7c2bbc3e-e999-42ff-a320-8bf84bce8384">

commit eef497c
Author: Charles Bochet <[email protected]>
Date:   Wed May 8 01:49:49 2024 +0200

    Fix front jest tests (twentyhq#5331)

commit bb995d5
Author: Thaïs <[email protected]>
Date:   Tue May 7 21:07:56 2024 +0200

    refactor: use react-hook-form for Field type config forms (twentyhq#5326)

    Closes twentyhq#4295

    Note: for the sake of an easier code review, I did not rename/move some
    files and added "todo" comments instead so Github is able to match those
    files with their previous version.

commit b7a2e72
Author: Thaïs <[email protected]>
Date:   Tue May 7 21:05:45 2024 +0200

    fix: fix storybook pages tests coverage (twentyhq#5319)

commit ce4e78a
Author: Charles Bochet <[email protected]>
Date:   Tue May 7 21:04:45 2024 +0200

    Fix Rest API id UUID error (twentyhq#5321)

    A user has reported an issue with REST API.
    We have recently migrated the graphql IDs from UUID to ID type. As Rest
    API is leveraging the graphql API under the hood, the Rest API query
    builder should be updated accordingly

commit b691894
Author: Weiko <[email protected]>
Date:   Tue May 7 21:03:15 2024 +0200

    Fix query runner throwing 500 when pg_graphql detects unique constraint (twentyhq#5323)

    ## Context
    Since pg_graphql does not return specific error/exception, we have to
    map the error message and throw validation errors when needed
    This PR adds a check on unicity constraint error returned by pg_graphql
    when we are trying to insert duplicate records and returns a 400 instead
    of being handled by the exceptionHandler as a 500.

commit e802cef
Author: Weiko <[email protected]>
Date:   Tue May 7 20:54:10 2024 +0200

    Fix 400 yoga errors being sent to exception handlers (twentyhq#5322)

    ## Context
    Yoga can catch its own errors and we don't want to convert them again.
    Moreover those errors don't have an "originalError" property and should
    be schema related only (400 validation) so we only want to send them
    back to the API caller without going through the exception handler.

    Also fixed an issue in the createMany which was throwing a 500 when id
    was missing from the creation payload. It seems the FE is always sending
    an ID but it should actually be optional since the DB can generate one.
    This is a regression from the new UUID validation introduced a few weeks
    ago.

commit 6edecf7
Author: Jeet Desai <[email protected]>
Date:   Wed May 8 00:22:25 2024 +0530

    Fix: Icon position alignment right to left in chip (twentyhq#5330)

    Fixes twentyhq#5298

    ![image](https://github.com/twentyhq/twenty/assets/52026385/6cfcc380-bdd1-4d7b-a0c7-58434d610ace)

commit 7c3e828
Author: Marie <[email protected]>
Date:   Tue May 7 16:30:25 2024 +0200

    [fix] Increment cache version after object/field/relation update (twentyhq#5316)

    Fixes twentyhq#5276.

    Updates were not triggering a cache version incrementation because they
    do not trigger migrations while that is where the caching version logic
    was.
    We have decided to move the cache incrementation logic to the services.

commit b0d1cc9
Author: Thaïs <[email protected]>
Date:   Tue May 7 15:05:18 2024 +0200

    feat: add links to Links field (twentyhq#5223)

    Closes twentyhq#5115, Closes twentyhq#5116

    <img width="242" alt="image"
    src="https://github.com/twentyhq/twenty/assets/3098428/ab78495a-4216-4243-8de3-53720818a09b">

    ---------

    Co-authored-by: Jérémy Magrin <[email protected]>

commit 8074aae
Author: Weiko <[email protected]>
Date:   Tue May 7 14:08:20 2024 +0200

    Split job modules (twentyhq#5318)

    ## Context
    JobsModule is hard to maintain because we provide all the jobs there,
    including their dependencies. This PR aims to split jobs in dedicated
    modules.

commit d10efb1
Author: gitstart-twenty <[email protected]>
Date:   Tue May 7 20:04:55 2024 +0800

    Add unit tests on object record mutation and query hooks (twentyhq#5014)

    ### Description
    Add unit tests on object record mutation and query hooks

    ### Refs
    twentyhq#4884

    ### Demo
    ![Screenshot 2024-04-18 at 15 16
    19](https://github.com/twentyhq/twenty/assets/140154534/c75f716a-725e-43eb-a703-3db29065fb18)

    Fixes twentyhq#4884

    ---------

    Co-authored-by: gitstart-twenty <[email protected]>
    Co-authored-by: v1b3m <[email protected]>
    Co-authored-by: Thiago Nascimbeni <[email protected]>
    Co-authored-by: Toledodev <[email protected]>
    Co-authored-by: Lucas Bordeau <[email protected]>

commit d0759ad
Author: Thaïs <[email protected]>
Date:   Tue May 7 11:44:46 2024 +0200

    refactor: use react-hook-form to validate Settings/DataModel/Field (twentyhq#4916)

    Closes twentyhq#4295

commit 9c25c1b
Author: Thomas des Francs <[email protected]>
Date:   Tue May 7 11:08:25 2024 +0200

    Added the 0.11 release changelog (twentyhq#5300)

    Added illustrations & descriptions

commit a10290e
Author: Sean Hellwig <[email protected]>
Date:   Tue May 7 03:07:51 2024 -0600

    Adds no-debugger rule to root eslint config file (twentyhq#5312)

commit ffd804d
Author: Weiko <[email protected]>
Date:   Tue May 7 11:07:20 2024 +0200

    Fix convertRecordPositionsToIntegers command for camelCase tables (twentyhq#5315)

    ## Context
    Per title, postgresql will use lowercase if not surrounded by quotes

commit 3052b49
Author: Ady Beraud <[email protected]>
Date:   Tue May 7 09:59:56 2024 +0300

    Fixed incomplete GitHub sync (twentyhq#5310)

    - Added await when fetching Github data to prevent the process from
    exiting before saving to database

    Co-authored-by: Ady Beraud <[email protected]>

commit 43cd8cc
Author: Ady Beraud <[email protected]>
Date:   Tue May 7 09:59:03 2024 +0300

    Modified URLs and added button to share on LinkedIn (twentyhq#5306)

    - Removed the env variable and added the current URL in contributor's
    page
    - Added button to share on LinkedIn on contributor's profile
    - Fixed absolute image URL for release API

    ---------

    Co-authored-by: Ady Beraud <[email protected]>

commit b438fc2
Author: Ady Beraud <[email protected]>
Date:   Tue May 7 09:35:54 2024 +0300

    Fix github stars endpoint (twentyhq#5301)

    - Encapsulated GitHub star response in an object
    - Fixed rounding of Github stars to align with Github convention
    - Fixed CORS issue so that endpoint can be called from twenty.com and
    app.twenty.com

    Co-authored-by: Ady Beraud <[email protected]>

commit a2017ea
Author: Charles Bochet <[email protected]>
Date:   Mon May 6 23:43:18 2024 +0200

    Improve messaging/calendar create contact performance (twentyhq#5314)

    In this PR, I'm refactoring the way we associate messageParticipant post
    person/company creation. Instead of looking a all person without
    participant, we are passing the one that were just created.

    Also, I'm making sure the message and messageParticipant creation
    transaction is commited before creating person/company creation (and
    then messageParticipant association)

commit 5f467ab
Author: Sean Hellwig <[email protected]>
Date:   Mon May 6 11:45:14 2024 -0600

    Removes erroneous debugger call in twenty-front (twentyhq#5311)

    Erroneous debugger call throws an error in twenty-front app console

    ---------

    Co-authored-by: Weiko <[email protected]>

commit 16ae34d
Author: Thaïs <[email protected]>
Date:   Mon May 6 17:49:53 2024 +0200

    fix: fix Chromatic script memory allocation in CI (twentyhq#5299)

commit 2c9f50e
Author: Charles Bochet <[email protected]>
Date:   Mon May 6 17:00:38 2024 +0200

    Fix enum defaultValue issues (twentyhq#5307)

    This PR fixes several issues:
    - enum naming should be: {tableName}_{fieldName}_enum and respecting the
    case
    - defaultValue format handled in the FE should respect the one in the BE

    In my opinion we should refactor the defaultValue:
    - we should respect backend format: "'myDefault'" for constant default
    and "0" for float, "now" for expressions, "true" for booleans. we can
    rename it to defaultValueExpression if it is more clear but we should
    not maintain a parallel system
    - we should deprecate option: isDefaultValue which is confusing
    - we should re-work backend to have a more unified approach between
    fields and avoid having if everywhere about select, multiselect, and
    currency cases. one unified "computeDefaultValue" function should do the
    job

    What is still broken:
    - currency default Value on creation. I think we should do the refactor
    first
    - select default value edition.
    These cases do not break the schema but are ignored currently

commit ff77a4e
Author: Pacifique LINJANJA <[email protected]>
Date:   Mon May 6 15:30:03 2024 +0200

    Feat/migrate password reset token to app token table (twentyhq#5051)

    # This PR

    - Fix twentyhq#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 <[email protected]>

commit b207d10
Author: Jérémy M <[email protected]>
Date:   Mon May 6 14:12:11 2024 +0200

    feat: extend twenty orm (twentyhq#5238)

    This PR is a follow up of PR twentyhq#5153.
    This one introduce some changes on how we're querying composite fields.
    We can do:

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

      async companies(): Promise<CompanyObjectMetadata[]> {
        // Old way
        // const companiesFilteredByLinkLabel = await this.companyObjectMetadataRepository.find({
        //   where: { xLinkLabel: 'MyLabel' },
        // });
        // Result will return xLinkLabel property

        // New way
        const companiesFilteredByLinkLabel = await this.companyObjectMetadataRepository.find({
          where: { xLink: { label:  'MyLabel' } },
        });
        // Result will return { xLink: { label: 'MyLabel' } } property instead of  { xLinkLabel: 'MyLabel' }

        return companiesFilteredByLinkLabel;
      }
    }
    ```

    Also we can now inject `TwentyORMManage` class to manually create a
    repository based on a given `workspaceId` using
    `getRepositoryForWorkspace` function that way:

    ```typescript
    export class CompanyService {
      constructor(
        // TwentyORMModule should be initialized
        private readonly twentyORMManager,
      ) {}

      async companies(): Promise<CompanyObjectMetadata[]> {
        const repository = await this.twentyORMManager.getRepositoryForWorkspace(
          '8bb6e872-a71f-4341-82b5-6b56fa81cd77',
          CompanyObjectMetadata,
        );

        const companies = await repository.find();

        return companies;
      }
    }
    ```

commit 154ae99
Author: Weiko <[email protected]>
Date:   Mon May 6 13:44:40 2024 +0200

    [flexible-schema] Add reserved keyword check on object creation (twentyhq#5303)

    ## Context
    Because creating an object in metadata also generates a graphql type and
    because graphql does not allow 2 types with the same name, we have to
    manage a list of reserved keywords that can't be used as object names.

    Currently we were maintaining a list of the core objects but we also
    have to introduce composite fields that are also generated as gql types.

commit 2828492
Author: Aditya Pimpalkar <[email protected]>
Date:   Mon May 6 10:33:48 2024 +0100

    chore: add nx/project.json to twenty-chrome-extension (twentyhq#5217)

    Fix for `build` CI on `twenty-chrome-extension`

    ---------

    Co-authored-by: Thaïs Guigon <[email protected]>

commit a1c95b9
Author: Orinami Olatunji <[email protected]>
Date:   Mon May 6 09:48:34 2024 +0100

    feat: add sign out and book a call buttons to "Choose your plan" page (twentyhq#5292)

    Resolves twentyhq#5281

    <img width="399" alt="buttions-light"
    src="https://github.com/twentyhq/twenty/assets/16918891/d1a0ba4e-696e-476b-a792-01ae19a06a55">
    <img width="390" alt="buttons-dark"
    src="https://github.com/twentyhq/twenty/assets/16918891/40bea83b-bc32-45ea-a522-ecf8239cfe51">

commit 77c0dee
Author: martmull <[email protected]>
Date:   Sat May 4 15:21:37 2024 +0200

    Add missing info from verify mutation (twentyhq#5283)

    Fix wrong error billing message

commit fc87a51
Author: Thaïs <[email protected]>
Date:   Fri May 3 19:19:21 2024 +0200

    fix: fix storybook:build memory allocation error in CI (twentyhq#5284)

commit 839a7e2
Author: Charles Bochet <[email protected]>
Date:   Fri May 3 19:11:03 2024 +0200

    Bump versions to 0.11 (twentyhq#5289)

    As per title!
    Bumping to 0.11.1 as we have already merged a few minor upgrades on top
    of 0.11

commit 6fda556
Author: Charles Bochet <[email protected]>
Date:   Fri May 3 19:10:55 2024 +0200

    Fix Filtered index view infinite re-render (twentyhq#5286)

    The whole viewBar component was re-rendered on view changes which was
    introducing performance issue. The need was to compute page title, this
    should be done in a lower level component

commit a750901
Author: Charles Bochet <[email protected]>
Date:   Fri May 3 19:10:33 2024 +0200

    Remove Feature Flag on Calendar (twentyhq#5288)

    Remove Calendar feature Flag!

commit 381bf0f
Author: Weiko <[email protected]>
Date:   Fri May 3 19:05:56 2024 +0200

    Create convert record positions to integers command (twentyhq#5287)

    ## Context
    Positions are used within a view to display and sort the different
    records of standard/custom object.
    When we add a new record and want to put it before the existing first
    record, we have to use float values to insert them in the DB and respect
    the desired order. We are adding a new command that can be executed to
    flatten those positions.

    ---------

    Co-authored-by: bosiraphael <[email protected]>

commit abf0f46
Author: Weiko <[email protected]>
Date:   Fri May 3 18:47:31 2024 +0200

    Fix yoga patch user id cache (twentyhq#5285)

    Co-authored-by: Charles Bochet <[email protected]>

commit 2067069
Author: Ady Beraud <[email protected]>
Date:   Fri May 3 17:38:41 2024 +0300

    Added OG Image (twentyhq#5251)

    - Added dynamic OG Image to share and download in contributors page

    <img width="1176" alt="Screenshot 2024-05-02 at 16 24 00"
    src="https://github.com/twentyhq/twenty/assets/102751374/0579454b-ccc7-46ba-9875-52458f06ee82">

    - Added dynamic metadata

    - Added design to contributor page

    - Added a NEXT_PUBLIC_HOST_URL in the .env file

    Co-authored-by: Ady Beraud <[email protected]>

commit a5a9e0e
Author: Thomas Trompette <[email protected]>
Date:   Fri May 3 16:30:58 2024 +0200

    Remove isMultiSelect feature flag (twentyhq#5280)

    As title

    Co-authored-by: Thomas Trompette <[email protected]>

commit 5285a42
Author: Thomas Trompette <[email protected]>
Date:   Fri May 3 16:14:37 2024 +0200

    Fix export with relations (twentyhq#5279)

    As title. Only relations are exported right now

    Co-authored-by: Thomas Trompette <[email protected]>

commit 1d9cd23
Author: Charles Bochet <[email protected]>
Date:   Fri May 3 15:35:49 2024 +0200

    Fix white screen on token expire (twentyhq#5271)

    While using middleware (executed pre-graphql) for graphql endpoint, we
    need to swallow exception and return errors with a 200. Otherwise it's
    not a valid graphql response

commit 2a0c74a
Author: Weiko <[email protected]>
Date:   Fri May 3 15:32:34 2024 +0200

    [calendar] Fix calendar sync status (twentyhq#5272)

    ## Context
    There is no calendarChannel syncStatus column compared to the
    messageChannel table. In the meantime, we are trying to infer its status
    based on the fact that the connection hasn't failed and the sync is
    enabled

commit 87994c2
Author: martmull <[email protected]>
Date:   Fri May 3 15:03:06 2024 +0200

    4900 multi select field front implement expanded cells (twentyhq#5151)

    Add expanded cell

    https://github.com/twentyhq/twenty/assets/29927851/363f2b44-7b3c-4771-a651-dfc4014da6ac

    ![image](https://github.com/twentyhq/twenty/assets/29927851/741bb0f9-fd1e-4a38-8b0e-71e144376876)

commit 1351a95
Author: Thaïs <[email protected]>
Date:   Fri May 3 14:59:09 2024 +0200

    fix: fix storybook coverage task (twentyhq#5256)

    - Fixes storybook coverage command: the coverage directory path was
    incorrect, but instead of failing `storybook:test --configuration=ci`,
    it was hanging indefinitely.
    - Switches back to `concurrently` to launch `storybook:static` and
    `storybook:test` in parallel, which allows to use options to explicitly
    kill `storybook:static` when `storybook:test` fails.
    - Moves `storybook:test --configuration=ci` to its own command
    `storybook:static:test`: used in the CI, and can be used locally to run
    storybook tests without having to launch `storybook:dev` first.
    - Creates command `storybook:coverage` and enables cache for this
    command.
    - Fixes Jest tests that were failing.
    - Improves caching conditions for some tasks (for instance, no need to
    invalidate Jest test cache if only Storybook story files were modified).

commit 5042186
Author: Weiko <[email protected]>
Date:   Fri May 3 14:52:20 2024 +0200

    Fix filter transform with logic operators (twentyhq#5269)

    Various fixes

    - Remote objects are read-only for now, we already hide and block most
    of the write actions but the button that allows you to add a new record
    in an empty collection was still visible.
    - CreatedAt is not mandatory on remote objects (at least for now) so it
    was breaking the show page, it now checks if createdAt exists and is not
    null before trying to display the human readable format `Added x days
    ago`
    - The filters are overwritten in query-runner-args.factory.ts to handle
    NUMBER field type, this was only working with filters like
    ```
          {
            "id": {
              "in": [
                1
              ]
            }
    ```
    but not with more depth such as
    ```
        "and": [
          {},
          {
            "id": {
              "in": [
                1
              ]
            }
          }
        ]
     ```
    - Fixes CREATE FOREIGN TABLE raw query which was missing ",".

commit 30ffe01
Author: Weiko <[email protected]>
Date:   Fri May 3 10:30:47 2024 +0200

    Fix token validation on graphql IntrospectionQuery (twentyhq#5255)

    ## Context
    We recently introduced a change that now throws a 401 if the token is
    invalid or expired.
    The first implementation is using an allow list and 'IntrospectionQuery'
    was missing so the playground was broken.

    The check has been updated and we now only check the excludedOperations
    list if a token is not present. This is because some operations can be
    both used as loggedIn and loggedOut so we want to validate the token for
    those sometimes (and set the workspace, user, cache version, etc). Still
    not a very clean solution imho.

commit 1430a67
Author: Félix Malfait <[email protected]>
Date:   Fri May 3 09:38:03 2024 +0200

    Quick job update (twentyhq#5265)

commit fe758e1
Author: Weiko <[email protected]>
Date:   Thu May 2 17:36:57 2024 +0200

    fix workspace-member deletion with existing attachments/documents (twentyhq#5232)

    ## Context
    We have a non-nullable constraint on authorId in attachments and
    documents, until we have soft-deletion we need to handle deletion of
    workspace-members and their attachments/documents.
    This PR introduces pre-hooks to deleteOne/deleteMany
    This is called when a user deletes a workspace-member from the members
    page

    Next: needs to be done on user level as well. This is called when users
    try to delete their own accounts. I've seen other issues such as
    re-creating a user with a previously used email failing.

commit f9c19c8
Author: Thomas Trompette <[email protected]>
Date:   Thu May 2 17:13:15 2024 +0200

    Build stripe integration on backend side (twentyhq#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 <[email protected]>

commit 5128ea3
Author: Thaïs <[email protected]>
Date:   Thu May 2 16:15:36 2024 +0200

    fix: fix storybook build script not found by Chromatic (twentyhq#5235)

commit f802964
Author: Charles Bochet <[email protected]>
Date:   Thu May 2 15:55:11 2024 +0200

    Bump to 0.10.6

commit 3015f4c
Author: Charles Bochet <[email protected]>
Date:   Thu May 2 15:50:40 2024 +0200

    Fix sync metadata script (twentyhq#5253)

    While troubleshooting self-hosting migration, we run into issues with
    sync-metadata script introduced by recent changes

commit 8d90c60
Author: Weiko <[email protected]>
Date:   Thu May 2 15:47:43 2024 +0200

    [calendar] hide calendar settings until implemented (twentyhq#5252)

    ## Context
    Those settings are not implemented yet, we would like to move them to a
    different page as well.
    In the meantime, we are hiding them since we plan to launch calendar in
    the next release and this won't be implemented before.

    We will implement it in this
    twentyhq#5140

commit 1da64c7
Author: Marie <[email protected]>
Date:   Thu May 2 15:25:54 2024 +0200

    [feat] Minor updates to the edit db connection page (twentyhq#5250)

    - Add placeholders in db connection edit page
    - Fix icon alignement and size (should not change) in Info banner

commit 05a90d6
Author: brendanlaschke <[email protected]>
Date:   Thu May 2 14:21:19 2024 +0200

    Constant api version (twentyhq#5248)

    closes twentyhq#5206
charlesBochet added a commit that referenced this pull request Jul 19, 2024
### Overview

This PR builds upon #5153, adding the ability to get a repository for
custom objects. The `entitySchema` is now generated for both standard
and custom objects based on metadata stored in the database instead of
the decorated `WorkspaceEntity` in the code. This change ensures that
standard objects with custom fields and relations can also support
custom objects.

### Implementation Details

#### Key Changes:

- **Dynamic Schema Generation:** The `entitySchema` for standard and
custom objects is now dynamically generated from the metadata stored in
the database. This shift allows for greater flexibility and
adaptability, particularly for standard objects with custom fields and
relations.
  
- **Custom Object Repository Retrieval:** A repository for a custom
object can be retrieved using `TwentyORMManager` based on the object's
name. Here's an example of how this can be achieved:

  ```typescript
const repository = await this.twentyORMManager.getRepository('custom');
  /*
* `repository` variable will be typed as follows, ensuring that standard
fields and relations are properly typed:
   * const repository: WorkspaceRepository<CustomWorkspaceEntity & {
   *    [key: string]: any;
   * }>
   */
  const res = await repository.find({});
  ```

Fix #6179

---------

Co-authored-by: Charles Bochet <[email protected]>
Co-authored-by: Weiko <[email protected]>
arnavsaxena17 pushed a commit to arnavsaxena17/twenty that referenced this pull request Oct 6, 2024
## 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 <[email protected]>
arnavsaxena17 pushed a commit to arnavsaxena17/twenty that referenced this pull request Oct 6, 2024
This PR is a follow up of PR twentyhq#5153.
This one introduce some changes on how we're querying composite fields.
We can do:

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

  async companies(): Promise<CompanyObjectMetadata[]> {
    // Old way
    // const companiesFilteredByLinkLabel = await this.companyObjectMetadataRepository.find({
    //   where: { xLinkLabel: 'MyLabel' },
    // });
    // Result will return xLinkLabel property

    // New way
    const companiesFilteredByLinkLabel = await this.companyObjectMetadataRepository.find({
      where: { xLink: { label:  'MyLabel' } },
    });
    // Result will return { xLink: { label: 'MyLabel' } } property instead of  { xLinkLabel: 'MyLabel' }

    return companiesFilteredByLinkLabel;
  }
}
```

Also we can now inject `TwentyORMManage` class to manually create a
repository based on a given `workspaceId` using
`getRepositoryForWorkspace` function that way:

```typescript
export class CompanyService {
  constructor(
    // TwentyORMModule should be initialized
    private readonly twentyORMManager,
  ) {}

  async companies(): Promise<CompanyObjectMetadata[]> {
    const repository = await this.twentyORMManager.getRepositoryForWorkspace(
      '8bb6e872-a71f-4341-82b5-6b56fa81cd77',
      CompanyObjectMetadata,
    );

    const companies = await repository.find();

    return companies;
  }
}
```
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants