Metadata api create entity in workspace custom app (#15911)

# Introduction
Cleaner and fewer scope version of
https://github.com/twentyhq/twenty/pull/15745 ( removed sync-metadata
hack through, too ambitious migration and upgrade )

Please note that this PR won't have any interaction with the existing
sync-metadata
Which mean that the sync metadata does not update the standard entities
applicationId and universalIdentifier, and it won't we will deprecate it
on favor of a workspace migration aka twenty-standard app installation

## API Metadata
Any operation going through the api metadata nows automatically scope
the related entity to the workspace custom application instance. (
optionally passing an applicationId to allow current hacky implem of app
sync service )

We need to either ignore the tests or remove the cli status check from
the blocking status badges for a PR to be merged

## New workspace
Already handled in previous
https://github.com/twentyhq/twenty/pull/15625, when a workspace is
created it gets created a twenty standard and custom workspace instance

All his views and permissions will be prefilled to the its twenty
standard app instance with a specific universalIdentifier

## New universalIdentifier
At the contrary as before with standardIds, universalIdentifier are
unique for a given workspace
This means that createdAt field of both object company and opportunity
will have a unique universalIdentifier whereas they share the same
standardId

## FlatApplication
Introduced the flatApplication and cache. Will migrate existing
`MetadataName` to be `SyncableMetadataName` in a following PR

## What's next
Next we will describe a twenty standard app configuration as json that
will be used to generate a workspace migration that will be run instead
of the sync metadata, in a nutshell we aim to deprecated the sync
metadata
So we can standardize any entity to have a non nullable applicationId
and universalIdentifier

## Upgrade command
Introduced an upgrade command that will create a custom workspace
instance for any workspace that do not have one in order to align with
the new behavior when creating a new workspace
This commit is contained in:
Paul Rastoin
2025-11-23 22:35:17 +01:00
committed by GitHub
parent 4848bc03f3
commit f9ab09c404
99 changed files with 3458 additions and 730 deletions
@@ -5,6 +5,8 @@ import { isDefined } from 'twenty-shared/utils';
import { DataSource } from 'typeorm';
import { ApplicationService } from 'src/engine/core-modules/application/application.service';
import { TWENTY_STANDARD_APPLICATION } from 'src/engine/core-modules/application/constants/twenty-standard-applications';
import { WorkspaceFlatApplicationMapCacheService } from 'src/engine/core-modules/application/services/workspace-flat-application-map-cache.service';
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service';
import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service';
@@ -21,7 +23,6 @@ import { seedPageLayoutWidgets } from 'src/engine/workspace-manager/dev-seeder/c
import { seedPageLayouts } from 'src/engine/workspace-manager/dev-seeder/core/utils/seed-page-layouts.util';
import { DevSeederDataService } from 'src/engine/workspace-manager/dev-seeder/data/services/dev-seeder-data.service';
import { DevSeederMetadataService } from 'src/engine/workspace-manager/dev-seeder/metadata/services/dev-seeder-metadata.service';
import { TWENTY_STANDARD_APPLICATION } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/twenty-standard-applications';
import { WorkspaceSyncMetadataService } from 'src/engine/workspace-manager/workspace-sync-metadata/workspace-sync-metadata.service';
@Injectable()
@@ -38,6 +39,7 @@ export class DevSeederService {
private readonly flatEntityMapsCacheService: WorkspaceManyOrAllFlatEntityMapsCacheService,
private readonly devSeederDataService: DevSeederDataService,
private readonly applicationService: ApplicationService,
private readonly workspaceFlatApplicationMapCacheService: WorkspaceFlatApplicationMapCacheService,
@InjectDataSource()
private readonly coreDataSource: DataSource,
) {}
@@ -59,6 +61,10 @@ export class DevSeederService {
workspaceId,
);
await this.workspaceFlatApplicationMapCacheService.invalidateCache({
workspaceId,
});
const dataSourceMetadata =
await this.dataSourceService.createDataSourceMetadata(
workspaceId,
@@ -80,6 +86,13 @@ export class DevSeederService {
);
}
const { twentyStandardFlatApplication } =
await this.applicationService.findWorkspaceTwentyStandardAndCustomApplicationOrThrow(
{
workspaceId,
},
);
await this.workspaceSyncMetadataService.synchronize({
workspaceId: workspaceId,
dataSourceId: dataSourceMetadata.id,
@@ -90,13 +103,17 @@ export class DevSeederService {
dataSourceMetadata,
workspaceId,
featureFlags,
twentyStandardFlatApplication,
});
await this.devSeederMetadataService.seedRelations({
workspaceId,
});
await this.devSeederPermissionsService.initPermissions(workspaceId);
await this.devSeederPermissionsService.initPermissions({
workspaceId,
twentyStandardApplication,
});
await seedPageLayouts(this.coreDataSource, 'core', workspaceId);
await seedPageLayoutTabs(this.coreDataSource, 'core', workspaceId);