Commit Graph

540 Commits

Author SHA1 Message Date
Paul Rastoin 9e81618773 Refactor morph field name and morph data loader (#14299)
# Introduction
Storing morph relation field names directly in database, using morphId
to aggregate them
Removing dynamic morph field metadata computation in schemas and data
loader

Will add integration tests on morph data loader entry

closes https://github.com/twentyhq/core-team-issues/issues/1425 
closes https://github.com/twentyhq/core-team-issues/issues/1424 
closes https://github.com/twentyhq/core-team-issues/issues/1423
2025-09-08 17:20:18 +02:00
Félix Malfait 502bd07db9 Fix wrong path used by backend (#14352)
After moving a section on the frontend, this broke the path that was
sent by email on the backend.
This kind of error comes back every ~2-3 month under different forms so
we need a more robust solution: I moved routes to the shared folder,
that way we will share one common source of truth between the frontend
and the backend.

Fixes #14343
2025-09-08 16:07:13 +02:00
Vinay Arvind Badgujar 5824637d1a fix - newly added object not visible in left menu (#14202)
resolves #14190 

added refreshCoreViews() call after object creation to immediately
update core views state, ensuring new objects appear in the navigation
drawer without requiring a refresh

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
Co-authored-by: prastoin <paul@twenty.com>
2025-09-05 10:43:03 +02:00
Marie 40251d34ec [permissions] Deprecate ObjectsPermissionsDeprecated (1/3) (#14306)
We want to update UserWorkspacePermissions from 
```
export type UserWorkspacePermissions = {
  permissionFlags: Record<PermissionFlagType, boolean>;
  objectRecordsPermissions: Record<PermissionsOnAllObjectRecords, boolean>;
  objectPermissions: ObjectsPermissionsDeprecated;
};
```

to
```
export type UserWorkspacePermissions = {
  permissionFlags: Record<PermissionFlagType, boolean>;
  objectsPermissions: ObjectsPermissions;
};
```

`ObjectsPermissionsDeprecated` and `ObjectsPermissions` are actually
very similar, they only have different key names (`canRead` vs
`canReadObjectRecords`)

To avoid brutal breaking changes, we will proceed in multiple steps: 

1. This PR: adapt FE so it does not call objectRecordsPermissions
anymore + add the new objectsPermissions to UserWorkspacePermissions
without calling it yet in the FE
2. Remove objectRecordsPermissions in BE + use objectsPermissions in FE
instead of objectPermissions
3. Remove objectPermissions
2025-09-04 14:03:33 +00:00
Abdul Rahman 79bcd90d8d Feat: role applicability controls (#14239)
Closes [#1404](https://github.com/twentyhq/core-team-issues/issues/1404)

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2025-09-04 08:58:50 +05:30
Marie 052f91cff1 Adapt field permissions to label identifier (#14287)
Closes https://github.com/twentyhq/core-team-issues/issues/1316

As discussed with @Bonapara, the behaviour is the following:

1 if there is a read restriction on a field that is or becomes the label
identifier, this restriction is actually overriden to allow any user who
has read rights on the object to also be able to read the values on the
label identifier field. This restriction is overriden in the values
stored in the cache but not in the db.
2 in the UI it is not possible to add a field permission to restrict
read rights on the field that is the label identifier. It is still
possible to update the label identifier for it to be a field that
previously had a restrictive field permission on a role though, but then
1. has our back.
2025-09-03 19:37:06 +02:00
Paul Rastoin 4fa114b3ec Add MorphId column to MORPH_RELATION field metadata (#14285)
close https://github.com/twentyhq/core-team-issues/issues/1426
2025-09-03 18:33:17 +02:00
Etienne 6ee41f33c6 fix - remove Untitled default value on standard name field (#14282)
Enable unique constraint creation on name standard field. 
Null values are handled in front with 'Untitled'.

Need to migrate fieldMetadata default value on all custom objects ?

Tested : 
- Toggle on/off uniqueness on standard name field of standard/custom
object
- Create new custom text field and toggle on/off uniqueness
2025-09-03 14:45:34 +02:00
Paul Rastoin 6522498df0 Failing morph relation creation v2 integration testing & fixes (#14253)
# Introduction
closes https://github.com/twentyhq/core-team-issues/issues/1408
2025-09-03 12:14:43 +02:00
Thomas Trompette 2e7fe238ed Clean step and edge errors (#14261)
- add filters for both resolvers
- map to gql errors

Should fix https://github.com/twentyhq/core-team-issues/issues/996
2025-09-03 10:00:02 +02:00
martmull aae99482a1 Remove workspaceId from path parameter for route endpoints (#14258)
- remove workspaceId from path parameter for route endpoints
- use host to infer workspaceId
2025-09-02 16:38:29 +02:00
Paul Rastoin 6d6ee939cf Morph relation field v2 fixes (#14216)
Fixes, depending on the contextual current operation in order to
retrieve relation target and all morph related fields to be delete:
- DELETE Field,
- DELETE Object, 
- UPDATE Field, that affects all others related fields
2025-09-02 12:17:20 +00:00
Weiko ce3fcb295d Add traces to transaction rollback exceptions (#14246) 2025-09-02 13:58:37 +02:00
martmull a722d97724 14066 extensibility add coretriggerroute table (#14241)
This Pr 
- adds a `route` table to the core schema
- adds a controller to trigger route

For now, we need to add a `/s/<workspace_id>` prefix to all routes

We plan to create a custom domain table in order to let the users create
subdomains for their workspace, and so to link their routes to a
subdomain. Thank to that, we will be able to identify workspace_id from
domain name, and the prefix could be `/s`. If we create a native
dedicated subdomain for routes for each workspaces, the prefix could be
completely removed!

Here the follow up ticket to do that ->
https://github.com/twentyhq/twenty/issues/14240
2025-09-02 11:24:59 +02:00
Marie 0d5fedfb0a Fix user friendly message for non available object name (#14210)
Fixes https://github.com/twentyhq/twenty/issues/13577.

When naming or renaming an object, we are already checking that the name
was available compared to other existing objects.
But we also need to check that the relation fields that will be created
or updated are available as well: we create relation fields on
Attachment, Note, Favorite, Task and TimelineActivites, that bear the
name of the object. Thus, it is not possible to create an object that
has the same name as one of the fields on Attachment, Note etc: name,
createdAt, ... are not valid names.
2025-09-01 16:35:40 +00:00
Weiko 80347b7b38 Rename uniqueIdentifier to universalIdentifier (#14201) 2025-09-01 12:33:59 +02:00
Antoine Moreaux 1c4568c8b1 feat(pricing/ai): improve billing metered pricing + add pricing on ai chat (#14092)
## TODO:

- [x] display "yearly" or "monthly" wording everywhere it's needed
- [ ] Add button with "downgrade" or "upgrade" to save the change of
credits price + modal to validate
- [x] Add renewal date 
- [ ] Implement
https://docs.stripe.com/billing/subscriptions/subscription-schedules for
`switchFromYearlyToMonthly` and decrease number of credits
2025-08-29 16:23:07 +00:00
Paul Rastoin 7df9094939 RelationFieldMetadataType to MorphOrRelationFieldMetadataType and so on (#14160) 2025-08-29 15:25:12 +00:00
Paul Rastoin a2296c02ec Delete morph v2 adjustements (#14158)
we need to handle both:

- morph relation field deletion ( implies every related morph and target
deletion )
- target RELATION only the pair that could contain a morph or not we
don't care


close https://github.com/twentyhq/core-team-issues/issues/1411
2025-08-29 17:11:59 +02:00
Paul Rastoin 6397a4d906 Update object handle morph join col rename (#14153)
# Introduction
When udpating a flat object metadata name we need to search for RELATION
field that has a MORPH RELATION target flat field metadata with
MANY_TO_ONE relationType as its settings are binded to the updated flat
object metadata name

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

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

We use the ObjectMetadataMaps in order to fasten the process

Fixes https://github.com/twentyhq/core-team-issues/issues/1179
2025-08-29 15:04:08 +02:00
Paul Rastoin b5114a5fa3 Morph relation v2 delete: retrieve related field metadata to delete (#14142)
close https://github.com/twentyhq/core-team-issues/issues/1411
2025-08-29 11:59:32 +02:00
martmull 71d97c5398 14064 extensibility add coretriggereventlistener table (#14138)
This Pr continues the extensibility journey
- adds a `core.databaseEventTrigger` table
- add a oneToMany relation between `core.serverlessFunction` and
`core.databaseEventTrigger`
- add a job `CallDatabaseEventTriggerJobsJob` triggered by
`EntityEventsToDbListener` triggering `serverlessFunction` based on the
`core.databaseEventTrigger.settings.eventName`
- add a new `trigger-queue` to carry this job
- renamed `DatabaseEventTriggerListener` into
`WorkflowDatabaseEventTriggerListener`
2025-08-29 08:11:35 +02:00
Paul Rastoin b364acf5bb Morph relation validation v2 (#14141)
## Introduction
Binding morph relation validation to relation validation code flow
Please note that ALL create field will go through basic atomic
validation, which involve name availability and so on

close https://github.com/twentyhq/core-team-issues/issues/1407
2025-08-28 16:05:58 +00:00
Paul Rastoin 7c481abf0c Handle input transpilation morph relation v2 (#14124)
# Introduction
From `morphRelationCreationPayload` to `flatFieldMetadatas`

close https://github.com/twentyhq/core-team-issues/issues/1406

## Unit testing
I'm not a fan of covering things this way, where we could have some
strong integration testing tests in the first place
But I wanted to freeze the optimistic path computation, also covered the
exception because it was cheap, doesn't mean I won't cover them through
integration tests later anw
2025-08-28 17:22:03 +02:00
Félix Malfait e264d7f32b fix: Enable Lingui recommended rules and fix all translation violations (#14133)
- Enable lingui/no-single-variables-to-translate and all other
recommended Lingui rules
- Fix single variable translation patterns (t`${variable}` → variable)
- Fix expression-in-message violations by extracting variables
- Fix t-call-in-function violations by moving translations inside
functions
- Update ESLint configs to use linguiPlugin.configs['flat/recommended']
- Clean up unused imports and improve translation patterns
2025-08-28 15:12:38 +02:00
martmull 9b41a3be54 Add cron trigger table (#14110)
This Pr begins the extensibility journey
- adds a `core.cronTrigger` table
- add a oneToMany relation between core.serverlessFunction and
`core.cronTrigger` (one serverlessFunction can be triggered by multiple
cronTriggers)
- add a job to trigger a serverless function
- adds a cron to trigger serverlessFunction (via the trigger job) based
on the core.cronTrigger.setting.pattern
- adds a command to register the cron
- add the command in `cron-register-all.command.ts`
2025-08-28 14:47:48 +02:00
Weiko dec2239ae7 Remove typeorm service (#14116)
## Context
To simplify the way we inject our default datasource, I've recently
removed the token injection that was confusion since we only had once
configured on the module level. Now I'm removing TypeORM service which
allows us to instantiate a new Datasource with the same parameters as
the default one, it was redundant and confusing.
2025-08-28 13:21:26 +02:00
Paul Rastoin 59d90bedaa Integration testing v2 enum field types fail and success path (#14010)
# Introduction
Migrating and improving performances of field enum integrations tests
success and failing tests cases to be using the new v2 api

## Discovered issue
When deleting an object in v1 it will leave related enums until the
object is re-created
Something not done anymore within the create in v2 but in the delete
operation
We should implem an upgrade command to remove such relicas

## Bugs
- Update/create default value multi select runner wrong sql query -> FIX
- Update default value multi select regression, we should allow option
without an id to be inserted -> FIX
- default value compare dynamic json stringify convertion or not in
compare tools for object and fields
2025-08-28 10:23:04 +00:00
Weiko 533d7fe49a Deprecate legacy core datasource token (#14096) 2025-08-27 20:09:54 +02:00
Abdul Rahman 9098df7ddf Add preconfigured Workflow creation agent (#13855)
Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2025-08-27 14:20:38 +02:00
Weiko f06786d603 Add missing FK on view tables (#14084)
## Context
View tables were missing some FK, we are also introducing delete cascade
on those tables when needed to leverage pg cascade deletion instead of
having to implement it.
Also renaming those classes with Entity suffix to follow repo
guidelines.
2025-08-26 18:59:20 +02:00
Weiko d433a576d7 Create related records after object creation with migration v2 (view, view filters, favorite) (#14068)
## Context
This PR introduces view actions for the migration v2. We should be able
to create, update and delete views through the migration runner
independently from object actions (this is not 100% true in this PR
though as I'm still passing the object metadata to build the view)
Note: Once favorite are moved out of the workspace schema, we will have
favorite actions as well, in the meantime I have to inject some code at
the end of the createOne method from the object metadata service
2025-08-26 10:54:03 +02:00
Guillim 35df56e7d8 morph fix for release 1.4 (#14029)
DataLoader Specific update du to Morph relations that have many rows

Fixes https://github.com/twentyhq/twenty/issues/14018
2025-08-21 14:43:14 +00:00
Weiko 707043c3ba Rollout field permissions (#14024)
Rolling out field permissions feature
2025-08-21 16:17:28 +02:00
Antoine Moreaux 809fe5699f feat(ai): enhance metadata handling and add support for tools, prompt… (#14002)
…s, and resources

- Removed unused `ObjectMetadataStandardIdToIdMap`.
- Updated Vite config with additional allowed hosts.
- Improved MCP service to handle `ping` method and lists for tools,
prompts, and resources.
- Refactored utility function `isFieldMetadataEntityOfType` for improved
type handling.
- Expanded MCP metadata service to include tools, prompts, and resources
support.
2025-08-21 08:45:46 +00:00
Paul Rastoin e99c67d6b5 Fail slow + structured validator error response scoped by object and fields (#13980)
# Introduction
Example of returned response: ( snapshot copy don't mind jest expect any
values )
```json
{
  "extensions": {
    "code": "BAD_USER_INPUT",
    "errors": {
      "fieldMetadata": [
        {
          "errors": [
            {
              "code": "INVALID_FIELD_INPUT",
              "message": "Name is too long",
              "userFriendlyMessage": "Name is too long",
              "value": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
            },
          ],
          "id": Any<String>,
          "name": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
          "objectMetadataId": Any<String>,
          "operation": "create_field",
        },
        {
          "errors": [
            {
              "code": "INVALID_FIELD_INPUT",
              "message": "Name is too long",
              "userFriendlyMessage": "Name is too long",
              "value": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
            },
          ],
          "id": Any<String>,
          "name": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
          "objectMetadataId": Any<String>,
          "operation": "create_field",
        },
        {
          "errors": [
            {
              "code": "INVALID_FIELD_INPUT",
              "message": "Name is too long",
              "userFriendlyMessage": "Name is too long",
              "value": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
            },
          ],
          "id": Any<String>,
          "name": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
          "objectMetadataId": Any<String>,
          "operation": "create_field",
        },
        {
          "errors": [
            {
              "code": "INVALID_FIELD_INPUT",
              "message": "Name is too long",
              "userFriendlyMessage": "Name is too long",
              "value": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
            },
          ],
          "id": Any<String>,
          "name": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
          "objectMetadataId": Any<String>,
          "operation": "create_field",
        },
        {
          "errors": [
            {
              "code": "INVALID_FIELD_INPUT",
              "message": "Name is too long",
              "userFriendlyMessage": "Name is too long",
              "value": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
            },
          ],
          "id": Any<String>,
          "name": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
          "objectMetadataId": Any<String>,
          "operation": "create_field",
        },
      ],
      "objectMetadata": [
        {
          "errors": [
            {
              "code": "INVALID_OBJECT_INPUT",
              "message": "Name is too long",
              "value": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
            },
          ],
          "fields": [],
          "id": Any<String>,
          "namePlural": "listingas",
          "nameSingular": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
          "operation": "create_object",
        },
      ],
    },
    "message": "Validation failed for 1 object(s) and 5 field(s)",
    "summary": {
      "invalidFields": 5,
      "invalidObjects": 1,
      "totalErrors": 6,
    },
    "userFriendlyMessage": "Validation failed for 1 object(s) and 5 field(s)",
  },
  "message": "Multiple validation errors occurred while creating object",
  "name": "GraphQLError",
}
```

## Note
We should put in place integrations tests on REST API too, in order to
ensure we receive the same response error
2025-08-20 17:33:15 +00:00
Weiko deec9c96da Refactor schema migration runner service discovery (#13988)
## Context
This refactoring improves scalability in the workspace migration runner
in terms of actions. Instead of relying on a switch case and multiple
changes and services + duplicate things between metadata and workspace
schema, we now have a unique module for all kind of actions and a
registry pattern to register them and run them dynamically.

Example on how to add a new "create_role" action with the future design:
```typescript
// packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/role/services/create-role-action-handler.service.ts
export class CreateRoleActionHandlerService extends WorkspaceMigrationRunnerActionHandler(
  'create_role',
) {
  constructor(
    private readonly workspaceSchemaManagerService: WorkspaceSchemaManagerService,
    private readonly roleRepository: Repository<Role>,
  ) {}

  async executeForMetadata(
    context: WorkspaceMigrationActionRunnerArgs<CreateRoleAction>,
  ): Promise<void> {
      this.roleRepository.save({...}); // technically could use the queryRunner from context param directly instead of injecting the repository
  }

  async executeForWorkspaceSchema(
    context: WorkspaceMigrationActionRunnerArgs<CreateRoleAction>,
  ): Promise<void> {
      // this.workspaceSchemaManagerService.tableManager... -> Nothing to do in the workspace schema in the role case
  }
}
```
```typescript
// packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/workspace-schema-migration-runner-action-handlers.module.ts
@Module({
  imports: [WorkspaceSchemaManagerModule, +TypeOrmModule.forFeature([Role]),
  providers: [
     ...
     +RoleCreateActionService
  ],
})
export class WorkspaceSchemaMigrationRunnerActionHandlersModule {}
```
```typescript
export type WorkspaceMigrationAction=
  | WorkspaceMigrationObjectAction
  ...
  +| WorkspaceMigrationRoleAction;

export type CreateRoleAction = {
   type = 'create_role',
   role: RoleEntity
};

export type WorkspaceMigrationRoleAction = CreateRoleAction;
```
2025-08-20 18:30:40 +02:00
neo773 3ef94f6e8c Refactor read only object and fields (#13936)
Co-authored-by: Charles Bochet <charles@twenty.com>
2025-08-19 18:10:35 +02:00
Guillim 90c237a185 Morph-front-settings (#13872)
# Implement Morph Frontend Settings

Possiblity to create a morh relation from the Settings !

## Overview
Morph object system with new frontend settings UI for configuring morph
relations.

## What Changed
- **New Components**: `SettingsDataModelFieldMorphRelationForm`,
`SettingsMorphRelationMultiSelect`
- **Form System**: React Hook Form + Zod validation for morph relation
configuration
- **Preview System**: Live preview of field configurations with
validation
- **State Management**: Enhanced component state handling for complex
form interactions

## Technical Details
- **Architecture**: Restructured field input/display components for
better maintainability
- **Types**: Added comprehensive TypeScript types for morph relations
(`isFieldMorphRelation`, etc.)
- **Validation**: Dynamic validation rules based on field types and
relation patterns
- **Performance**: Optimized rendering with proper state management

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
2025-08-19 16:37:26 +02:00
Paul Rastoin 9eaede6e1e Fix default relation standard field deletion (#13975)
Authorize relation standard field deletion if targetObjectMetadata is
not present in existingFlatObjectMetadataMaps
2025-08-19 16:12:04 +02:00
Paul Rastoin 86170c705e Standard relation field for custom objects (#13968)
## Introduction
Introducing default standard relation field for custom objects
2025-08-19 13:53:46 +02:00
Weiko aa51ecb9d2 Fix relations creation with metadata v2 (#13967) 2025-08-18 21:48:39 +02:00
Paul Rastoin 6c23c9c46b fix(server): field metadata creation service v2 (#13963) 2025-08-18 17:50:33 +02:00
Weiko 43bb8d6043 Workspace schema migration runner v2 - Fix Enums and Create TsVector (#13955)
## Context
- Adding ts-vector generatedType/asExpression as TS_VECTOR settings
- Using those settings to setup properly tsVector searchVector column
through the new migration runner
- Fix enum creation/suppression

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

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

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

## TODO
- improve `otherFlatObjectMetadataMapsToValidate` naming too vague

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

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