Migrate metadata cache (#16030)
## Context Deprecating legacy ObjectMetadata from cache in favor of flat entities. Introducing utils to build byName/byNameSingular/byNamePlural in isolated cases ## Next - I had to introduce a util to build from flat to legacy objectMetadataMaps, we should instead use flat maps directly when needed (datasource, schema generation, etc) - Deprecate metadata version in the cache - Use the new cache strategy for flat entities with permissions and feature flags and inject in the global datasource context
This commit is contained in:
-2
@@ -33,7 +33,6 @@ import { ViewFieldModule } from 'src/engine/metadata-modules/view-field/view-fie
|
||||
import { ViewFilterModule } from 'src/engine/metadata-modules/view-filter/view-filter.module';
|
||||
import { ViewGroupModule } from 'src/engine/metadata-modules/view-group/view-group.module';
|
||||
import { ViewModule } from 'src/engine/metadata-modules/view/view.module';
|
||||
import { WorkspaceMetadataCacheModule } from 'src/engine/metadata-modules/workspace-metadata-cache/workspace-metadata-cache.module';
|
||||
import { WorkspaceMetadataVersionModule } from 'src/engine/metadata-modules/workspace-metadata-version/workspace-metadata-version.module';
|
||||
import { WorkspaceMigrationModule } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.module';
|
||||
import { WorkspaceCacheStorageModule } from 'src/engine/workspace-cache-storage/workspace-cache-storage.module';
|
||||
@@ -68,7 +67,6 @@ import { UpdateFieldInput } from './dtos/update-field.input';
|
||||
ViewFilterModule,
|
||||
ViewGroupModule,
|
||||
PermissionsModule,
|
||||
WorkspaceMetadataCacheModule,
|
||||
WorkspaceMigrationV2Module,
|
||||
FlatFieldMetadataModule,
|
||||
IndexMetadataModule,
|
||||
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { isMorphOrRelationFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-morph-or-relation-flat-field-metadata.util';
|
||||
|
||||
export type FieldMapsForObject = {
|
||||
fieldIdByName: Record<string, string>;
|
||||
fieldIdByJoinColumnName: Record<string, string>;
|
||||
};
|
||||
|
||||
export const buildFieldMapsForObject = (
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>,
|
||||
objectMetadataId: string,
|
||||
): FieldMapsForObject => {
|
||||
const fieldIdByName: Record<string, string> = {};
|
||||
const fieldIdByJoinColumnName: Record<string, string> = {};
|
||||
|
||||
for (const [fieldId, field] of Object.entries(flatFieldMetadataMaps.byId)) {
|
||||
if (!isDefined(field)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (field.objectMetadataId !== objectMetadataId) {
|
||||
continue;
|
||||
}
|
||||
|
||||
fieldIdByName[field.name] = fieldId;
|
||||
|
||||
if (isMorphOrRelationFlatFieldMetadata(field)) {
|
||||
const joinColumnName = (
|
||||
field.settings as { joinColumnName?: string } | undefined
|
||||
)?.joinColumnName;
|
||||
|
||||
if (joinColumnName) {
|
||||
fieldIdByJoinColumnName[joinColumnName] = fieldId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
fieldIdByName,
|
||||
fieldIdByJoinColumnName,
|
||||
};
|
||||
};
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
|
||||
export const buildObjectIdByNameMaps = (
|
||||
flatObjectMetadataMaps: FlatEntityMaps<FlatObjectMetadata>,
|
||||
): {
|
||||
idByNameSingular: Record<string, string>;
|
||||
idByNamePlural: Record<string, string>;
|
||||
} => {
|
||||
const idByNameSingular: Record<string, string> = {};
|
||||
const idByNamePlural: Record<string, string> = {};
|
||||
|
||||
for (const [objectId, objectMetadata] of Object.entries(
|
||||
flatObjectMetadataMaps.byId,
|
||||
)) {
|
||||
if (!isDefined(objectMetadata)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
idByNameSingular[objectMetadata.nameSingular] = objectId;
|
||||
idByNamePlural[objectMetadata.namePlural] = objectId;
|
||||
}
|
||||
|
||||
return { idByNameSingular, idByNamePlural };
|
||||
};
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { buildFieldMapsForObject } from 'src/engine/metadata-modules/flat-field-metadata/utils/build-field-maps-for-object.util';
|
||||
import { type FlatIndexMetadata } from 'src/engine/metadata-modules/flat-index-metadata/types/flat-index-metadata.type';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { type ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps';
|
||||
|
||||
export const buildObjectMetadataItemWithFieldMaps = (
|
||||
flatObjectMetadata: FlatObjectMetadata,
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>,
|
||||
flatIndexMaps: FlatEntityMaps<FlatIndexMetadata>,
|
||||
): ObjectMetadataItemWithFieldMaps => {
|
||||
const fieldsById: Record<string, FlatFieldMetadata> = {};
|
||||
|
||||
for (const fieldId of flatObjectMetadata.fieldMetadataIds) {
|
||||
const field = flatFieldMetadataMaps.byId[fieldId];
|
||||
|
||||
if (isDefined(field)) {
|
||||
fieldsById[fieldId] = field;
|
||||
}
|
||||
}
|
||||
|
||||
const { fieldIdByName, fieldIdByJoinColumnName } = buildFieldMapsForObject(
|
||||
flatFieldMetadataMaps,
|
||||
flatObjectMetadata.id,
|
||||
);
|
||||
|
||||
const indexMetadatas = flatObjectMetadata.indexMetadataIds
|
||||
.map((indexId) => flatIndexMaps.byId[indexId])
|
||||
.filter(isDefined)
|
||||
.map((flatIndex) => ({
|
||||
...flatIndex,
|
||||
indexFieldMetadatas: flatIndex.flatIndexFieldMetadatas,
|
||||
}));
|
||||
|
||||
return {
|
||||
...flatObjectMetadata,
|
||||
fieldsById,
|
||||
fieldIdByName,
|
||||
fieldIdByJoinColumnName,
|
||||
indexMetadatas,
|
||||
} as unknown as ObjectMetadataItemWithFieldMaps;
|
||||
};
|
||||
-2
@@ -31,7 +31,6 @@ import { RemoteTableRelationsModule } from 'src/engine/metadata-modules/remote-s
|
||||
import { ViewFieldModule } from 'src/engine/metadata-modules/view-field/view-field.module';
|
||||
import { ViewEntity } from 'src/engine/metadata-modules/view/entities/view.entity';
|
||||
import { ViewModule } from 'src/engine/metadata-modules/view/view.module';
|
||||
import { WorkspaceMetadataCacheModule } from 'src/engine/metadata-modules/workspace-metadata-cache/workspace-metadata-cache.module';
|
||||
import { WorkspaceMetadataVersionModule } from 'src/engine/metadata-modules/workspace-metadata-version/workspace-metadata-version.module';
|
||||
import { WorkspaceMigrationModule } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.module';
|
||||
import { WorkspacePermissionsCacheModule } from 'src/engine/metadata-modules/workspace-permissions-cache/workspace-permissions-cache.module';
|
||||
@@ -61,7 +60,6 @@ import { WorkspaceMigrationV2Module } from 'src/engine/workspace-manager/workspa
|
||||
PermissionsModule,
|
||||
WorkspacePermissionsCacheModule,
|
||||
WorkspaceCacheStorageModule,
|
||||
WorkspaceMetadataCacheModule,
|
||||
WorkspaceDataSourceModule,
|
||||
FeatureFlagModule,
|
||||
WorkspaceMigrationV2Module,
|
||||
|
||||
+51
-31
@@ -3,6 +3,7 @@ import { getRepositoryToken } from '@nestjs/typeorm';
|
||||
|
||||
import { type Repository } from 'typeorm';
|
||||
|
||||
import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.service';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { type UpsertObjectPermissionsInput } from 'src/engine/metadata-modules/object-permission/dtos/upsert-object-permissions.input';
|
||||
import { ObjectPermissionEntity } from 'src/engine/metadata-modules/object-permission/object-permission.entity';
|
||||
@@ -13,9 +14,7 @@ import {
|
||||
PermissionsExceptionMessage,
|
||||
} from 'src/engine/metadata-modules/permissions/permissions.exception';
|
||||
import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
|
||||
import { type ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps';
|
||||
import { WorkspacePermissionsCacheService } from 'src/engine/metadata-modules/workspace-permissions-cache/workspace-permissions-cache.service';
|
||||
import { WorkspaceCacheStorageService } from 'src/engine/workspace-cache-storage/workspace-cache-storage.service';
|
||||
|
||||
describe('ObjectPermissionService', () => {
|
||||
let service: ObjectPermissionService;
|
||||
@@ -24,7 +23,7 @@ describe('ObjectPermissionService', () => {
|
||||
>;
|
||||
let roleRepository: jest.Mocked<Repository<RoleEntity>>;
|
||||
let workspacePermissionsCacheService: jest.Mocked<WorkspacePermissionsCacheService>;
|
||||
let workspaceCacheStorageService: jest.Mocked<WorkspaceCacheStorageService>;
|
||||
let workspaceManyOrAllFlatEntityMapsCacheService: jest.Mocked<WorkspaceManyOrAllFlatEntityMapsCacheService>;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
@@ -56,9 +55,9 @@ describe('ObjectPermissionService', () => {
|
||||
},
|
||||
},
|
||||
{
|
||||
provide: WorkspaceCacheStorageService,
|
||||
provide: WorkspaceManyOrAllFlatEntityMapsCacheService,
|
||||
useValue: {
|
||||
getObjectMetadataMapsOrThrow: jest.fn(),
|
||||
getOrRecomputeManyOrAllFlatEntityMaps: jest.fn(),
|
||||
},
|
||||
},
|
||||
],
|
||||
@@ -72,7 +71,9 @@ describe('ObjectPermissionService', () => {
|
||||
workspacePermissionsCacheService = module.get(
|
||||
WorkspacePermissionsCacheService,
|
||||
);
|
||||
workspaceCacheStorageService = module.get(WorkspaceCacheStorageService);
|
||||
workspaceManyOrAllFlatEntityMapsCacheService = module.get(
|
||||
WorkspaceManyOrAllFlatEntityMapsCacheService,
|
||||
);
|
||||
});
|
||||
|
||||
describe('upsertObjectPermissions', () => {
|
||||
@@ -106,18 +107,26 @@ describe('ObjectPermissionService', () => {
|
||||
],
|
||||
};
|
||||
|
||||
// Mock object metadata maps with a system object
|
||||
workspaceCacheStorageService.getObjectMetadataMapsOrThrow.mockResolvedValue(
|
||||
// Mock flat object metadata maps with a system object
|
||||
workspaceManyOrAllFlatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps.mockResolvedValue(
|
||||
{
|
||||
byId: {
|
||||
[systemObjectMetadataId]: {
|
||||
id: systemObjectMetadataId,
|
||||
isSystem: true,
|
||||
workspaceId,
|
||||
} as ObjectMetadataItemWithFieldMaps,
|
||||
flatObjectMetadataMaps: {
|
||||
byId: {
|
||||
[systemObjectMetadataId]: {
|
||||
id: systemObjectMetadataId,
|
||||
isSystem: true,
|
||||
workspaceId,
|
||||
fieldMetadataIds: [],
|
||||
indexMetadataIds: [],
|
||||
viewIds: [],
|
||||
universalIdentifier: systemObjectMetadataId,
|
||||
applicationId: null,
|
||||
} as any,
|
||||
},
|
||||
idByUniversalIdentifier: {},
|
||||
universalIdentifiersByApplicationId: {},
|
||||
},
|
||||
idByNameSingular: {},
|
||||
},
|
||||
} as any,
|
||||
);
|
||||
|
||||
// Act & Assert
|
||||
@@ -155,18 +164,26 @@ describe('ObjectPermissionService', () => {
|
||||
],
|
||||
};
|
||||
|
||||
// Mock object metadata maps with a custom object
|
||||
workspaceCacheStorageService.getObjectMetadataMapsOrThrow.mockResolvedValue(
|
||||
// Mock flat object metadata maps with a custom object
|
||||
workspaceManyOrAllFlatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps.mockResolvedValue(
|
||||
{
|
||||
byId: {
|
||||
[customObjectMetadataId]: {
|
||||
id: customObjectMetadataId,
|
||||
isSystem: false,
|
||||
workspaceId,
|
||||
} as ObjectMetadataItemWithFieldMaps,
|
||||
flatObjectMetadataMaps: {
|
||||
byId: {
|
||||
[customObjectMetadataId]: {
|
||||
id: customObjectMetadataId,
|
||||
isSystem: false,
|
||||
workspaceId,
|
||||
fieldMetadataIds: [],
|
||||
indexMetadataIds: [],
|
||||
viewIds: [],
|
||||
universalIdentifier: customObjectMetadataId,
|
||||
applicationId: null,
|
||||
} as any,
|
||||
},
|
||||
idByUniversalIdentifier: {},
|
||||
universalIdentifiersByApplicationId: {},
|
||||
},
|
||||
idByNameSingular: {},
|
||||
},
|
||||
} as any,
|
||||
);
|
||||
|
||||
// Mock successful upsert
|
||||
@@ -236,12 +253,15 @@ describe('ObjectPermissionService', () => {
|
||||
],
|
||||
};
|
||||
|
||||
// Mock empty object metadata maps
|
||||
workspaceCacheStorageService.getObjectMetadataMapsOrThrow.mockResolvedValue(
|
||||
// Mock empty flat object metadata maps
|
||||
workspaceManyOrAllFlatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps.mockResolvedValue(
|
||||
{
|
||||
byId: {},
|
||||
idByNameSingular: {},
|
||||
},
|
||||
flatObjectMetadataMaps: {
|
||||
byId: {},
|
||||
idByUniversalIdentifier: {},
|
||||
universalIdentifiersByApplicationId: {},
|
||||
},
|
||||
} as any,
|
||||
);
|
||||
|
||||
// Act & Assert
|
||||
|
||||
+97
-63
@@ -10,6 +10,7 @@ import {
|
||||
objectMetadataItemMock,
|
||||
} from 'src/engine/api/__mocks__/object-metadata-item.mock';
|
||||
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.service';
|
||||
import { type UpsertFieldPermissionsInput } from 'src/engine/metadata-modules/object-permission/dtos/upsert-field-permissions.input';
|
||||
import { FieldPermissionEntity } from 'src/engine/metadata-modules/object-permission/field-permission/field-permission.entity';
|
||||
import { FieldPermissionService } from 'src/engine/metadata-modules/object-permission/field-permission/field-permission.service';
|
||||
@@ -20,7 +21,6 @@ import {
|
||||
} from 'src/engine/metadata-modules/permissions/permissions.exception';
|
||||
import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
|
||||
import { WorkspacePermissionsCacheService } from 'src/engine/metadata-modules/workspace-permissions-cache/workspace-permissions-cache.service';
|
||||
import { WorkspaceCacheStorageService } from 'src/engine/workspace-cache-storage/workspace-cache-storage.service';
|
||||
import { getMockFieldMetadataEntity } from 'src/utils/__test__/get-field-metadata-entity.mock';
|
||||
|
||||
describe('FieldPermissionService', () => {
|
||||
@@ -31,7 +31,7 @@ describe('FieldPermissionService', () => {
|
||||
let roleRepository: jest.Mocked<Repository<RoleEntity>>;
|
||||
let fieldMetadataRepository: jest.Mocked<Repository<FieldMetadataEntity>>;
|
||||
let workspacePermissionsCacheService: jest.Mocked<WorkspacePermissionsCacheService>;
|
||||
let workspaceCacheStorageService: jest.Mocked<WorkspaceCacheStorageService>;
|
||||
let workspaceManyOrAllFlatEntityMapsCacheService: jest.Mocked<WorkspaceManyOrAllFlatEntityMapsCacheService>;
|
||||
|
||||
const testWorkspaceId = '20202020-0000-0000-0000-000000000000';
|
||||
const testRoleId = '20202020-0000-0000-0000-000000000001';
|
||||
@@ -98,9 +98,9 @@ describe('FieldPermissionService', () => {
|
||||
},
|
||||
},
|
||||
{
|
||||
provide: WorkspaceCacheStorageService,
|
||||
provide: WorkspaceManyOrAllFlatEntityMapsCacheService,
|
||||
useValue: {
|
||||
getObjectMetadataMapsOrThrow: jest.fn(),
|
||||
getOrRecomputeManyOrAllFlatEntityMaps: jest.fn(),
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -123,7 +123,9 @@ describe('FieldPermissionService', () => {
|
||||
workspacePermissionsCacheService = module.get(
|
||||
WorkspacePermissionsCacheService,
|
||||
);
|
||||
workspaceCacheStorageService = module.get(WorkspaceCacheStorageService);
|
||||
workspaceManyOrAllFlatEntityMapsCacheService = module.get(
|
||||
WorkspaceManyOrAllFlatEntityMapsCacheService,
|
||||
);
|
||||
|
||||
// Setup default mocks
|
||||
roleRepository.findOne.mockResolvedValue(mockRole);
|
||||
@@ -137,39 +139,49 @@ describe('FieldPermissionService', () => {
|
||||
data: mockRolesPermissions,
|
||||
},
|
||||
);
|
||||
workspaceCacheStorageService.getObjectMetadataMapsOrThrow.mockResolvedValue(
|
||||
const testFieldMetadata = getMockFieldMetadataEntity({
|
||||
...fieldTextMock,
|
||||
label: 'Test Field',
|
||||
objectMetadataId: testObjectMetadataId,
|
||||
workspaceId: testWorkspaceId,
|
||||
id: testFieldMetadataId,
|
||||
});
|
||||
|
||||
workspaceManyOrAllFlatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps.mockResolvedValue(
|
||||
{
|
||||
byId: {
|
||||
[testObjectMetadataId]: {
|
||||
...objectMetadataItemMock,
|
||||
fieldsById: {
|
||||
[fieldTextMock.id]: getMockFieldMetadataEntity({
|
||||
...fieldTextMock,
|
||||
label: 'Test Field',
|
||||
objectMetadataId: testObjectMetadataId,
|
||||
workspaceId: testWorkspaceId,
|
||||
id: '20202020-0000-0000-0000-000000000003',
|
||||
}),
|
||||
},
|
||||
fieldIdByJoinColumnName: {},
|
||||
fieldIdByName: {},
|
||||
indexMetadatas: [],
|
||||
flatObjectMetadataMaps: {
|
||||
byId: {
|
||||
[testObjectMetadataId]: {
|
||||
...objectMetadataItemMock,
|
||||
id: testObjectMetadataId,
|
||||
fieldMetadataIds: [testFieldMetadataId],
|
||||
indexMetadataIds: [],
|
||||
viewIds: [],
|
||||
universalIdentifier: testObjectMetadataId,
|
||||
applicationId: null,
|
||||
} as any,
|
||||
[fieldRelationMock.objectMetadataId]: {
|
||||
...objectMetadataItemMock,
|
||||
id: fieldRelationMock.objectMetadataId,
|
||||
fieldMetadataIds: [fieldRelationMock.id],
|
||||
indexMetadataIds: [],
|
||||
viewIds: [],
|
||||
universalIdentifier: fieldRelationMock.objectMetadataId,
|
||||
applicationId: null,
|
||||
} as any,
|
||||
},
|
||||
[fieldRelationMock.objectMetadataId]: {
|
||||
...objectMetadataItemMock,
|
||||
id: fieldRelationMock.objectMetadataId,
|
||||
fieldsById: {
|
||||
[fieldRelationMock.id]: fieldRelationMock,
|
||||
},
|
||||
fieldIdByJoinColumnName: {},
|
||||
fieldIdByName: {},
|
||||
indexMetadatas: [],
|
||||
idByUniversalIdentifier: {},
|
||||
universalIdentifiersByApplicationId: {},
|
||||
},
|
||||
flatFieldMetadataMaps: {
|
||||
byId: {
|
||||
[testFieldMetadataId]: testFieldMetadata as any,
|
||||
[fieldRelationMock.id]: fieldRelationMock as any,
|
||||
},
|
||||
idByUniversalIdentifier: {},
|
||||
universalIdentifiersByApplicationId: {},
|
||||
},
|
||||
idByNameSingular: {
|
||||
testObject: testObjectMetadataId,
|
||||
},
|
||||
},
|
||||
} as any,
|
||||
);
|
||||
fieldPermissionsRepository.find.mockResolvedValue([]);
|
||||
fieldPermissionsRepository.upsert.mockResolvedValue({} as any);
|
||||
@@ -386,11 +398,19 @@ describe('FieldPermissionService', () => {
|
||||
});
|
||||
|
||||
it('should throw error when object metadata is not found', async () => {
|
||||
workspaceCacheStorageService.getObjectMetadataMapsOrThrow.mockResolvedValue(
|
||||
workspaceManyOrAllFlatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps.mockResolvedValue(
|
||||
{
|
||||
byId: {},
|
||||
idByNameSingular: {},
|
||||
},
|
||||
flatObjectMetadataMaps: {
|
||||
byId: {},
|
||||
idByUniversalIdentifier: {},
|
||||
universalIdentifiersByApplicationId: {},
|
||||
},
|
||||
flatFieldMetadataMaps: {
|
||||
byId: {},
|
||||
idByUniversalIdentifier: {},
|
||||
universalIdentifiersByApplicationId: {},
|
||||
},
|
||||
} as any,
|
||||
);
|
||||
|
||||
const input = createUpsertInput([
|
||||
@@ -420,21 +440,29 @@ describe('FieldPermissionService', () => {
|
||||
isSystem: true,
|
||||
};
|
||||
|
||||
workspaceCacheStorageService.getObjectMetadataMapsOrThrow.mockResolvedValue(
|
||||
workspaceManyOrAllFlatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps.mockResolvedValue(
|
||||
{
|
||||
byId: {
|
||||
[testObjectMetadataId]: {
|
||||
...systemObjectMetadata,
|
||||
fieldsById: {},
|
||||
fieldIdByJoinColumnName: {},
|
||||
fieldIdByName: {},
|
||||
indexMetadatas: [],
|
||||
flatObjectMetadataMaps: {
|
||||
byId: {
|
||||
[testObjectMetadataId]: {
|
||||
...systemObjectMetadata,
|
||||
id: testObjectMetadataId,
|
||||
fieldMetadataIds: [],
|
||||
indexMetadataIds: [],
|
||||
viewIds: [],
|
||||
universalIdentifier: testObjectMetadataId,
|
||||
applicationId: null,
|
||||
} as any,
|
||||
},
|
||||
idByUniversalIdentifier: {},
|
||||
universalIdentifiersByApplicationId: {},
|
||||
},
|
||||
idByNameSingular: {
|
||||
testObject: testObjectMetadataId,
|
||||
flatFieldMetadataMaps: {
|
||||
byId: {},
|
||||
idByUniversalIdentifier: {},
|
||||
universalIdentifiersByApplicationId: {},
|
||||
},
|
||||
},
|
||||
} as any,
|
||||
);
|
||||
|
||||
const input = createUpsertInput([
|
||||
@@ -458,23 +486,29 @@ describe('FieldPermissionService', () => {
|
||||
});
|
||||
|
||||
it('should throw error when field metadata is not found', async () => {
|
||||
const objectMetadataWithoutField = {
|
||||
...objectMetadataItemMock,
|
||||
fieldsById: {},
|
||||
fieldIdByJoinColumnName: {},
|
||||
fieldIdByName: {},
|
||||
indexMetadatas: [],
|
||||
};
|
||||
|
||||
workspaceCacheStorageService.getObjectMetadataMapsOrThrow.mockResolvedValue(
|
||||
workspaceManyOrAllFlatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps.mockResolvedValue(
|
||||
{
|
||||
byId: {
|
||||
[testObjectMetadataId]: objectMetadataWithoutField,
|
||||
flatObjectMetadataMaps: {
|
||||
byId: {
|
||||
[testObjectMetadataId]: {
|
||||
...objectMetadataItemMock,
|
||||
id: testObjectMetadataId,
|
||||
fieldMetadataIds: [],
|
||||
indexMetadataIds: [],
|
||||
viewIds: [],
|
||||
universalIdentifier: testObjectMetadataId,
|
||||
applicationId: null,
|
||||
} as any,
|
||||
},
|
||||
idByUniversalIdentifier: {},
|
||||
universalIdentifiersByApplicationId: {},
|
||||
},
|
||||
idByNameSingular: {
|
||||
testObject: testObjectMetadataId,
|
||||
flatFieldMetadataMaps: {
|
||||
byId: {},
|
||||
idByUniversalIdentifier: {},
|
||||
universalIdentifiersByApplicationId: {},
|
||||
},
|
||||
},
|
||||
} as any,
|
||||
);
|
||||
|
||||
const input = createUpsertInput([
|
||||
|
||||
+22
-5
@@ -14,6 +14,9 @@ import {
|
||||
} from 'src/engine/core-modules/graphql/utils/graphql-errors.util';
|
||||
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { isFieldMetadataTypeRelation } from 'src/engine/metadata-modules/field-metadata/utils/is-field-metadata-type-relation.util';
|
||||
import { createEmptyFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/constant/create-empty-flat-entity-maps.constant';
|
||||
import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.service';
|
||||
import { buildObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/flat-object-metadata/utils/build-object-metadata-item-with-field-maps.util';
|
||||
import { type UpsertFieldPermissionsInput } from 'src/engine/metadata-modules/object-permission/dtos/upsert-field-permissions.input';
|
||||
import { FieldPermissionEntity } from 'src/engine/metadata-modules/object-permission/field-permission/field-permission.entity';
|
||||
import {
|
||||
@@ -24,7 +27,6 @@ import {
|
||||
import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
|
||||
import { type ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
|
||||
import { WorkspacePermissionsCacheService } from 'src/engine/metadata-modules/workspace-permissions-cache/workspace-permissions-cache.service';
|
||||
import { WorkspaceCacheStorageService } from 'src/engine/workspace-cache-storage/workspace-cache-storage.service';
|
||||
|
||||
@Injectable()
|
||||
export class FieldPermissionService {
|
||||
@@ -36,7 +38,7 @@ export class FieldPermissionService {
|
||||
@InjectRepository(FieldPermissionEntity)
|
||||
private readonly fieldPermissionsRepository: Repository<FieldPermissionEntity>,
|
||||
private readonly workspacePermissionsCacheService: WorkspacePermissionsCacheService,
|
||||
private readonly workspaceCacheStorageService: WorkspaceCacheStorageService,
|
||||
private readonly workspaceManyOrAllFlatEntityMapsCacheService: WorkspaceManyOrAllFlatEntityMapsCacheService,
|
||||
) {}
|
||||
|
||||
public async upsertFieldPermissions({
|
||||
@@ -60,11 +62,26 @@ export class FieldPermissionService {
|
||||
role,
|
||||
});
|
||||
|
||||
const { byId: objectMetadataMapsById } =
|
||||
await this.workspaceCacheStorageService.getObjectMetadataMapsOrThrow(
|
||||
workspaceId,
|
||||
const { flatObjectMetadataMaps, flatFieldMetadataMaps } =
|
||||
await this.workspaceManyOrAllFlatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId,
|
||||
flatMapsKeys: ['flatObjectMetadataMaps', 'flatFieldMetadataMaps'],
|
||||
},
|
||||
);
|
||||
|
||||
const objectMetadataMapsById: ObjectMetadataMaps['byId'] = {};
|
||||
|
||||
for (const [id, flatObj] of Object.entries(flatObjectMetadataMaps.byId)) {
|
||||
if (isDefined(flatObj)) {
|
||||
objectMetadataMapsById[id] = buildObjectMetadataItemWithFieldMaps(
|
||||
flatObj,
|
||||
flatFieldMetadataMaps,
|
||||
createEmptyFlatEntityMaps(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const existingFieldPermissions = await this.fieldPermissionsRepository.find(
|
||||
{
|
||||
where: {
|
||||
|
||||
+2
-2
@@ -2,6 +2,7 @@ import { Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
|
||||
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { WorkspaceManyOrAllFlatEntityMapsCacheModule } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.module';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { FieldPermissionEntity } from 'src/engine/metadata-modules/object-permission/field-permission/field-permission.entity';
|
||||
import { FieldPermissionService } from 'src/engine/metadata-modules/object-permission/field-permission/field-permission.service';
|
||||
@@ -9,7 +10,6 @@ import { ObjectPermissionEntity } from 'src/engine/metadata-modules/object-permi
|
||||
import { ObjectPermissionService } from 'src/engine/metadata-modules/object-permission/object-permission.service';
|
||||
import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
|
||||
import { WorkspacePermissionsCacheModule } from 'src/engine/metadata-modules/workspace-permissions-cache/workspace-permissions-cache.module';
|
||||
import { WorkspaceCacheStorageModule } from 'src/engine/workspace-cache-storage/workspace-cache-storage.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
@@ -20,8 +20,8 @@ import { WorkspaceCacheStorageModule } from 'src/engine/workspace-cache-storage/
|
||||
FieldPermissionEntity,
|
||||
FieldMetadataEntity,
|
||||
]),
|
||||
WorkspaceCacheStorageModule,
|
||||
WorkspacePermissionsCacheModule,
|
||||
WorkspaceManyOrAllFlatEntityMapsCacheModule,
|
||||
],
|
||||
providers: [ObjectPermissionService, FieldPermissionService],
|
||||
exports: [ObjectPermissionService, FieldPermissionService],
|
||||
|
||||
+9
-6
@@ -4,6 +4,7 @@ import { msg } from '@lingui/core/macro';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { In, Repository } from 'typeorm';
|
||||
|
||||
import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.service';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import {
|
||||
type ObjectPermissionInput,
|
||||
@@ -17,7 +18,6 @@ import {
|
||||
} from 'src/engine/metadata-modules/permissions/permissions.exception';
|
||||
import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
|
||||
import { WorkspacePermissionsCacheService } from 'src/engine/metadata-modules/workspace-permissions-cache/workspace-permissions-cache.service';
|
||||
import { WorkspaceCacheStorageService } from 'src/engine/workspace-cache-storage/workspace-cache-storage.service';
|
||||
|
||||
export class ObjectPermissionService {
|
||||
constructor(
|
||||
@@ -28,7 +28,7 @@ export class ObjectPermissionService {
|
||||
@InjectRepository(ObjectMetadataEntity)
|
||||
private readonly objectMetadataRepository: Repository<ObjectMetadataEntity>,
|
||||
private readonly workspacePermissionsCacheService: WorkspacePermissionsCacheService,
|
||||
private readonly workspaceCacheStorageService: WorkspaceCacheStorageService,
|
||||
private readonly workspaceManyOrAllFlatEntityMapsCacheService: WorkspaceManyOrAllFlatEntityMapsCacheService,
|
||||
) {}
|
||||
|
||||
public async upsertObjectPermissions({
|
||||
@@ -53,14 +53,17 @@ export class ObjectPermissionService {
|
||||
roleWithObjectPermissions: role,
|
||||
});
|
||||
|
||||
const { byId: objectMetadataMapsById } =
|
||||
await this.workspaceCacheStorageService.getObjectMetadataMapsOrThrow(
|
||||
workspaceId,
|
||||
const { flatObjectMetadataMaps } =
|
||||
await this.workspaceManyOrAllFlatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId,
|
||||
flatMapsKeys: ['flatObjectMetadataMaps'],
|
||||
},
|
||||
);
|
||||
|
||||
input.objectPermissions.forEach((objectPermission) => {
|
||||
const objectMetadataForObjectPermission =
|
||||
objectMetadataMapsById[objectPermission.objectMetadataId];
|
||||
flatObjectMetadataMaps.byId[objectPermission.objectMetadataId];
|
||||
|
||||
if (!isDefined(objectMetadataForObjectPermission)) {
|
||||
throw new PermissionsException(
|
||||
|
||||
+10
-6
@@ -24,6 +24,7 @@ import { RequestLocale } from 'src/engine/decorators/locale/request-locale.decor
|
||||
import { CustomPermissionGuard } from 'src/engine/guards/custom-permission.guard';
|
||||
import { NoPermissionGuard } from 'src/engine/guards/no-permission.guard';
|
||||
import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard';
|
||||
import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.service';
|
||||
import { resolveObjectMetadataStandardOverride } from 'src/engine/metadata-modules/object-metadata/utils/resolve-object-metadata-standard-override.util';
|
||||
import { CreateViewPermissionGuard } from 'src/engine/metadata-modules/view-permissions/guards/create-view-permission.guard';
|
||||
import { DeleteViewPermissionGuard } from 'src/engine/metadata-modules/view-permissions/guards/delete-view-permission.guard';
|
||||
@@ -41,7 +42,6 @@ import {
|
||||
import { ViewRestApiExceptionFilter } from 'src/engine/metadata-modules/view/filters/view-rest-api-exception.filter';
|
||||
import { ViewV2Service } from 'src/engine/metadata-modules/view/services/view-v2.service';
|
||||
import { ViewService } from 'src/engine/metadata-modules/view/services/view.service';
|
||||
import { WorkspaceMetadataCacheService } from 'src/engine/metadata-modules/workspace-metadata-cache/services/workspace-metadata-cache.service';
|
||||
|
||||
@Controller('rest/metadata/views')
|
||||
@UseGuards(WorkspaceAuthGuard)
|
||||
@@ -51,7 +51,7 @@ export class ViewController {
|
||||
private readonly viewService: ViewService,
|
||||
private readonly viewV2Service: ViewV2Service,
|
||||
private readonly featureFlagService: FeatureFlagService,
|
||||
private readonly workspaceMetadataCacheService: WorkspaceMetadataCacheService,
|
||||
private readonly flatEntityMapsCacheService: WorkspaceManyOrAllFlatEntityMapsCacheService,
|
||||
private readonly i18nService: I18nService,
|
||||
) {}
|
||||
|
||||
@@ -226,16 +226,20 @@ export class ViewController {
|
||||
return views;
|
||||
}
|
||||
|
||||
const { objectMetadataMaps } =
|
||||
await this.workspaceMetadataCacheService.getExistingOrRecomputeMetadataMaps(
|
||||
{ workspaceId },
|
||||
const { flatObjectMetadataMaps } =
|
||||
await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId,
|
||||
flatMapsKeys: ['flatObjectMetadataMaps'],
|
||||
},
|
||||
);
|
||||
|
||||
return views.map((view) => {
|
||||
let processedName = view.name;
|
||||
|
||||
if (view.name.includes('{objectLabelPlural}')) {
|
||||
const objectMetadata = objectMetadataMaps.byId[view.objectMetadataId];
|
||||
const objectMetadata =
|
||||
flatObjectMetadataMaps.byId[view.objectMetadataId];
|
||||
|
||||
if (objectMetadata) {
|
||||
const i18n = this.i18nService.getI18nInstance(locale ?? 'en');
|
||||
|
||||
@@ -76,7 +76,7 @@ export class ViewResolver {
|
||||
workspaceId: workspace.id,
|
||||
});
|
||||
|
||||
if (objectMetadata) {
|
||||
if (isDefined(objectMetadata)) {
|
||||
const i18n = this.i18nService.getI18nInstance(context.req.locale);
|
||||
const translatedObjectLabel = resolveObjectMetadataStandardOverride(
|
||||
{
|
||||
|
||||
@@ -19,7 +19,6 @@ import { ViewEntity } from 'src/engine/metadata-modules/view/entities/view.entit
|
||||
import { ViewResolver } from 'src/engine/metadata-modules/view/resolvers/view.resolver';
|
||||
import { ViewV2Service } from 'src/engine/metadata-modules/view/services/view-v2.service';
|
||||
import { ViewService } from 'src/engine/metadata-modules/view/services/view.service';
|
||||
import { WorkspaceMetadataCacheModule } from 'src/engine/metadata-modules/workspace-metadata-cache/workspace-metadata-cache.module';
|
||||
import { WorkspaceCacheStorageModule } from 'src/engine/workspace-cache-storage/workspace-cache-storage.module';
|
||||
import { WorkspaceMigrationV2Module } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-v2.module';
|
||||
|
||||
@@ -37,7 +36,6 @@ import { WorkspaceMigrationV2Module } from 'src/engine/workspace-manager/workspa
|
||||
FeatureFlagModule,
|
||||
PermissionsModule,
|
||||
UserRoleModule,
|
||||
WorkspaceMetadataCacheModule,
|
||||
WorkspaceCacheStorageModule,
|
||||
WorkspaceMigrationV2Module,
|
||||
FlatViewModule,
|
||||
|
||||
-10
@@ -1,10 +0,0 @@
|
||||
import { CustomException } from 'src/utils/custom-exception';
|
||||
|
||||
export class WorkspaceMetadataCacheException extends CustomException<WorkspaceMetadataCacheExceptionCode> {}
|
||||
|
||||
export enum WorkspaceMetadataCacheExceptionCode {
|
||||
OBJECT_METADATA_MAP_NOT_FOUND = 'Object Metadata map not found',
|
||||
FIELD_METADATA_NOT_FOUND = 'Field Metadata not found',
|
||||
FIELD_METADATA_INVALID = 'Field Metadata is invalid',
|
||||
OBJECT_METADATA_COLLECTION_NOT_FOUND = 'Object Metadata collection not found',
|
||||
}
|
||||
-162
@@ -1,162 +0,0 @@
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { In, Repository } from 'typeorm';
|
||||
|
||||
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { IndexMetadataEntity } from 'src/engine/metadata-modules/index-metadata/index-metadata.entity';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { type ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
|
||||
import { generateObjectMetadataMaps } from 'src/engine/metadata-modules/utils/generate-object-metadata-maps.util';
|
||||
import {
|
||||
WorkspaceMetadataVersionException,
|
||||
WorkspaceMetadataVersionExceptionCode,
|
||||
} from 'src/engine/metadata-modules/workspace-metadata-version/exceptions/workspace-metadata-version.exception';
|
||||
import { WorkspaceCacheStorageService } from 'src/engine/workspace-cache-storage/workspace-cache-storage.service';
|
||||
|
||||
type GetExistingOrRecomputeMetadataMapsResult = {
|
||||
objectMetadataMaps: ObjectMetadataMaps;
|
||||
metadataVersion: number;
|
||||
};
|
||||
|
||||
@Injectable()
|
||||
export class WorkspaceMetadataCacheService {
|
||||
logger = new Logger(WorkspaceMetadataCacheService.name);
|
||||
|
||||
constructor(
|
||||
@InjectRepository(WorkspaceEntity)
|
||||
private readonly workspaceRepository: Repository<WorkspaceEntity>,
|
||||
private readonly workspaceCacheStorageService: WorkspaceCacheStorageService,
|
||||
@InjectRepository(ObjectMetadataEntity)
|
||||
private readonly objectMetadataRepository: Repository<ObjectMetadataEntity>,
|
||||
@InjectRepository(IndexMetadataEntity)
|
||||
private readonly indexMetadataRepository: Repository<IndexMetadataEntity>,
|
||||
) {}
|
||||
|
||||
async getExistingOrRecomputeMetadataMaps({
|
||||
workspaceId,
|
||||
}: {
|
||||
workspaceId: string;
|
||||
}): Promise<GetExistingOrRecomputeMetadataMapsResult> {
|
||||
const currentCacheVersion =
|
||||
await this.getMetadataVersionFromCache(workspaceId);
|
||||
|
||||
const currentDatabaseVersion =
|
||||
await this.getMetadataVersionFromDatabase(workspaceId);
|
||||
|
||||
if (!isDefined(currentDatabaseVersion)) {
|
||||
throw new WorkspaceMetadataVersionException(
|
||||
'Metadata version not found in the database',
|
||||
WorkspaceMetadataVersionExceptionCode.METADATA_VERSION_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
const shouldRecompute =
|
||||
!isDefined(currentCacheVersion) ||
|
||||
currentCacheVersion !== currentDatabaseVersion;
|
||||
|
||||
const existingObjectMetadataMaps =
|
||||
await this.workspaceCacheStorageService.getObjectMetadataMaps(
|
||||
workspaceId,
|
||||
currentDatabaseVersion,
|
||||
);
|
||||
|
||||
if (isDefined(existingObjectMetadataMaps) && !shouldRecompute) {
|
||||
return {
|
||||
objectMetadataMaps: existingObjectMetadataMaps,
|
||||
metadataVersion: currentDatabaseVersion,
|
||||
};
|
||||
}
|
||||
|
||||
const { objectMetadataMaps, metadataVersion } =
|
||||
await this.recomputeMetadataCache({
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
return {
|
||||
objectMetadataMaps,
|
||||
metadataVersion,
|
||||
};
|
||||
}
|
||||
|
||||
async recomputeMetadataCache({
|
||||
workspaceId,
|
||||
}: {
|
||||
workspaceId: string;
|
||||
}): Promise<GetExistingOrRecomputeMetadataMapsResult> {
|
||||
const currentDatabaseVersion =
|
||||
await this.getMetadataVersionFromDatabase(workspaceId);
|
||||
|
||||
if (!isDefined(currentDatabaseVersion)) {
|
||||
throw new WorkspaceMetadataVersionException(
|
||||
'Metadata version not found in the database',
|
||||
WorkspaceMetadataVersionExceptionCode.METADATA_VERSION_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
await this.workspaceCacheStorageService.flushVersionedMetadata(workspaceId);
|
||||
|
||||
const objectMetadataItems = await this.objectMetadataRepository.find({
|
||||
where: { workspaceId },
|
||||
relations: ['fields'],
|
||||
});
|
||||
|
||||
const objectMetadataItemsIds = objectMetadataItems.map(
|
||||
(objectMetadataItem) => objectMetadataItem.id,
|
||||
);
|
||||
|
||||
const indexMetadataItems = await this.indexMetadataRepository.find({
|
||||
where: { objectMetadataId: In(objectMetadataItemsIds) },
|
||||
relations: ['indexFieldMetadatas'],
|
||||
});
|
||||
|
||||
const objectMetadataItemsWithIndexMetadatas = objectMetadataItems.map(
|
||||
(objectMetadataItem) => ({
|
||||
...objectMetadataItem,
|
||||
indexMetadatas: indexMetadataItems.filter(
|
||||
(indexMetadataItem) =>
|
||||
indexMetadataItem.objectMetadataId === objectMetadataItem.id,
|
||||
),
|
||||
}),
|
||||
);
|
||||
|
||||
const freshObjectMetadataMaps = generateObjectMetadataMaps(
|
||||
objectMetadataItemsWithIndexMetadatas,
|
||||
);
|
||||
|
||||
await this.workspaceCacheStorageService.setObjectMetadataMaps(
|
||||
workspaceId,
|
||||
currentDatabaseVersion,
|
||||
freshObjectMetadataMaps,
|
||||
);
|
||||
|
||||
await this.workspaceCacheStorageService.setMetadataVersion(
|
||||
workspaceId,
|
||||
currentDatabaseVersion,
|
||||
);
|
||||
|
||||
return {
|
||||
objectMetadataMaps: freshObjectMetadataMaps,
|
||||
metadataVersion: currentDatabaseVersion,
|
||||
};
|
||||
}
|
||||
|
||||
private async getMetadataVersionFromDatabase(
|
||||
workspaceId: string,
|
||||
): Promise<number | undefined> {
|
||||
const workspace = await this.workspaceRepository.findOne({
|
||||
where: { id: workspaceId },
|
||||
});
|
||||
|
||||
return workspace?.metadataVersion;
|
||||
}
|
||||
|
||||
private async getMetadataVersionFromCache(
|
||||
workspaceId: string,
|
||||
): Promise<number | undefined> {
|
||||
return await this.workspaceCacheStorageService.getMetadataVersion(
|
||||
workspaceId,
|
||||
);
|
||||
}
|
||||
}
|
||||
-22
@@ -1,22 +0,0 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
|
||||
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { IndexMetadataEntity } from 'src/engine/metadata-modules/index-metadata/index-metadata.entity';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { WorkspaceMetadataCacheService } from 'src/engine/metadata-modules/workspace-metadata-cache/services/workspace-metadata-cache.service';
|
||||
import { WorkspaceCacheStorageModule } from 'src/engine/workspace-cache-storage/workspace-cache-storage.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
TypeOrmModule.forFeature([
|
||||
WorkspaceEntity,
|
||||
ObjectMetadataEntity,
|
||||
IndexMetadataEntity,
|
||||
]),
|
||||
WorkspaceCacheStorageModule,
|
||||
],
|
||||
exports: [WorkspaceMetadataCacheService],
|
||||
providers: [WorkspaceMetadataCacheService],
|
||||
})
|
||||
export class WorkspaceMetadataCacheModule {}
|
||||
+16
-4
@@ -5,20 +5,22 @@ import { isDefined } from 'twenty-shared/utils';
|
||||
import { Repository } from 'typeorm';
|
||||
|
||||
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { WorkspaceMetadataCacheService } from 'src/engine/metadata-modules/workspace-metadata-cache/services/workspace-metadata-cache.service';
|
||||
import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.service';
|
||||
import {
|
||||
WorkspaceMetadataVersionException,
|
||||
WorkspaceMetadataVersionExceptionCode,
|
||||
} from 'src/engine/metadata-modules/workspace-metadata-version/exceptions/workspace-metadata-version.exception';
|
||||
import { WorkspaceCacheStorageService } from 'src/engine/workspace-cache-storage/workspace-cache-storage.service';
|
||||
|
||||
@Injectable()
|
||||
export class WorkspaceMetadataVersionService {
|
||||
logger = new Logger(WorkspaceMetadataCacheService.name);
|
||||
logger = new Logger(WorkspaceMetadataVersionService.name);
|
||||
|
||||
constructor(
|
||||
@InjectRepository(WorkspaceEntity)
|
||||
private readonly workspaceRepository: Repository<WorkspaceEntity>,
|
||||
private readonly workspaceMetadataCacheService: WorkspaceMetadataCacheService,
|
||||
private readonly flatEntityMapsCacheService: WorkspaceManyOrAllFlatEntityMapsCacheService,
|
||||
private readonly workspaceCacheStorageService: WorkspaceCacheStorageService,
|
||||
) {}
|
||||
|
||||
async incrementMetadataVersion(workspaceId: string): Promise<void> {
|
||||
@@ -42,8 +44,18 @@ export class WorkspaceMetadataVersionService {
|
||||
{ metadataVersion: newMetadataVersion },
|
||||
);
|
||||
|
||||
await this.workspaceMetadataCacheService.recomputeMetadataCache({
|
||||
await this.workspaceCacheStorageService.setMetadataVersion(
|
||||
workspaceId,
|
||||
newMetadataVersion,
|
||||
);
|
||||
|
||||
await this.flatEntityMapsCacheService.invalidateFlatEntityMaps({
|
||||
workspaceId,
|
||||
flatMapsKeys: [
|
||||
'flatObjectMetadataMaps',
|
||||
'flatFieldMetadataMaps',
|
||||
'flatIndexMaps',
|
||||
],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -2,7 +2,7 @@ import { Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
|
||||
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { WorkspaceMetadataCacheModule } from 'src/engine/metadata-modules/workspace-metadata-cache/workspace-metadata-cache.module';
|
||||
import { WorkspaceManyOrAllFlatEntityMapsCacheModule } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.module';
|
||||
import { WorkspaceMetadataVersionService } from 'src/engine/metadata-modules/workspace-metadata-version/services/workspace-metadata-version.service';
|
||||
import { WorkspaceCacheStorageModule } from 'src/engine/workspace-cache-storage/workspace-cache-storage.module';
|
||||
|
||||
@@ -10,7 +10,7 @@ import { WorkspaceCacheStorageModule } from 'src/engine/workspace-cache-storage/
|
||||
imports: [
|
||||
TypeOrmModule.forFeature([WorkspaceEntity]),
|
||||
WorkspaceCacheStorageModule,
|
||||
WorkspaceMetadataCacheModule,
|
||||
WorkspaceManyOrAllFlatEntityMapsCacheModule,
|
||||
],
|
||||
exports: [WorkspaceMetadataVersionService],
|
||||
providers: [WorkspaceMetadataVersionService],
|
||||
|
||||
Reference in New Issue
Block a user