Recompute cached permissions at feature flag update (#12554)
If permissionsV2 feature flag is toggled, we should recompute the permissions. We decided to make each WorkspaceXxCacheService Xx-specific (feature flag, permissions...), so we are not recomputing permission cache from workspaceFeatureFlagCacheService where feature flags are recomputed, even if that would be a lower level than FeatureFlagService. This allows to avoid complex circuclar dependency and keeps a clear purpose for each service.
This commit is contained in:
@ -7,6 +7,7 @@ import { TypeORMModule } from 'src/database/typeorm/typeorm.module';
|
|||||||
import { FeatureFlag } from 'src/engine/core-modules/feature-flag/feature-flag.entity';
|
import { FeatureFlag } from 'src/engine/core-modules/feature-flag/feature-flag.entity';
|
||||||
import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service';
|
import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service';
|
||||||
import { WorkspaceFeatureFlagsMapCacheModule } from 'src/engine/metadata-modules/workspace-feature-flags-map-cache/workspace-feature-flags-map-cache.module';
|
import { WorkspaceFeatureFlagsMapCacheModule } from 'src/engine/metadata-modules/workspace-feature-flags-map-cache/workspace-feature-flags-map-cache.module';
|
||||||
|
import { WorkspacePermissionsCacheModule } from 'src/engine/metadata-modules/workspace-permissions-cache/workspace-permissions-cache.module';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
@ -17,6 +18,7 @@ import { WorkspaceFeatureFlagsMapCacheModule } from 'src/engine/metadata-modules
|
|||||||
resolvers: [],
|
resolvers: [],
|
||||||
}),
|
}),
|
||||||
WorkspaceFeatureFlagsMapCacheModule,
|
WorkspaceFeatureFlagsMapCacheModule,
|
||||||
|
WorkspacePermissionsCacheModule,
|
||||||
],
|
],
|
||||||
exports: [FeatureFlagService],
|
exports: [FeatureFlagService],
|
||||||
providers: [FeatureFlagService],
|
providers: [FeatureFlagService],
|
||||||
|
|||||||
@ -11,6 +11,7 @@ import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/service
|
|||||||
import { featureFlagValidator } from 'src/engine/core-modules/feature-flag/validates/feature-flag.validate';
|
import { featureFlagValidator } from 'src/engine/core-modules/feature-flag/validates/feature-flag.validate';
|
||||||
import { publicFeatureFlagValidator } from 'src/engine/core-modules/feature-flag/validates/is-public-feature-flag.validate';
|
import { publicFeatureFlagValidator } from 'src/engine/core-modules/feature-flag/validates/is-public-feature-flag.validate';
|
||||||
import { WorkspaceFeatureFlagsMapCacheService } from 'src/engine/metadata-modules/workspace-feature-flags-map-cache/workspace-feature-flags-map-cache.service';
|
import { WorkspaceFeatureFlagsMapCacheService } from 'src/engine/metadata-modules/workspace-feature-flags-map-cache/workspace-feature-flags-map-cache.service';
|
||||||
|
import { WorkspacePermissionsCacheService } from 'src/engine/metadata-modules/workspace-permissions-cache/workspace-permissions-cache.service';
|
||||||
|
|
||||||
jest.mock(
|
jest.mock(
|
||||||
'src/engine/core-modules/feature-flag/validates/is-public-feature-flag.validate',
|
'src/engine/core-modules/feature-flag/validates/is-public-feature-flag.validate',
|
||||||
@ -35,6 +36,10 @@ describe('FeatureFlagService', () => {
|
|||||||
recomputeFeatureFlagsMapCache: jest.fn(),
|
recomputeFeatureFlagsMapCache: jest.fn(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const mockWorkspacePermissionsCacheService = {
|
||||||
|
recomputeRolesPermissionsCache: jest.fn(),
|
||||||
|
};
|
||||||
|
|
||||||
const workspaceId = 'workspace-id';
|
const workspaceId = 'workspace-id';
|
||||||
const featureFlag = FeatureFlagKey.IS_WORKFLOW_ENABLED;
|
const featureFlag = FeatureFlagKey.IS_WORKFLOW_ENABLED;
|
||||||
|
|
||||||
@ -57,6 +62,10 @@ describe('FeatureFlagService', () => {
|
|||||||
provide: WorkspaceFeatureFlagsMapCacheService,
|
provide: WorkspaceFeatureFlagsMapCacheService,
|
||||||
useValue: mockWorkspaceFeatureFlagsMapCacheService,
|
useValue: mockWorkspaceFeatureFlagsMapCacheService,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
provide: WorkspacePermissionsCacheService,
|
||||||
|
useValue: mockWorkspacePermissionsCacheService,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
}).compile();
|
}).compile();
|
||||||
|
|
||||||
|
|||||||
@ -15,6 +15,7 @@ import {
|
|||||||
import { featureFlagValidator } from 'src/engine/core-modules/feature-flag/validates/feature-flag.validate';
|
import { featureFlagValidator } from 'src/engine/core-modules/feature-flag/validates/feature-flag.validate';
|
||||||
import { publicFeatureFlagValidator } from 'src/engine/core-modules/feature-flag/validates/is-public-feature-flag.validate';
|
import { publicFeatureFlagValidator } from 'src/engine/core-modules/feature-flag/validates/is-public-feature-flag.validate';
|
||||||
import { WorkspaceFeatureFlagsMapCacheService } from 'src/engine/metadata-modules/workspace-feature-flags-map-cache/workspace-feature-flags-map-cache.service';
|
import { WorkspaceFeatureFlagsMapCacheService } from 'src/engine/metadata-modules/workspace-feature-flags-map-cache/workspace-feature-flags-map-cache.service';
|
||||||
|
import { WorkspacePermissionsCacheService } from 'src/engine/metadata-modules/workspace-permissions-cache/workspace-permissions-cache.service';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class FeatureFlagService {
|
export class FeatureFlagService {
|
||||||
@ -22,6 +23,7 @@ export class FeatureFlagService {
|
|||||||
@InjectRepository(FeatureFlag, 'core')
|
@InjectRepository(FeatureFlag, 'core')
|
||||||
private readonly featureFlagRepository: Repository<FeatureFlag>,
|
private readonly featureFlagRepository: Repository<FeatureFlag>,
|
||||||
private readonly workspaceFeatureFlagsMapCacheService: WorkspaceFeatureFlagsMapCacheService,
|
private readonly workspaceFeatureFlagsMapCacheService: WorkspaceFeatureFlagsMapCacheService,
|
||||||
|
private readonly workspacePermissionsCacheService: WorkspacePermissionsCacheService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public async isFeatureEnabled(
|
public async isFeatureEnabled(
|
||||||
@ -71,9 +73,15 @@ export class FeatureFlagService {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (keys.includes(FeatureFlagKey.IS_PERMISSIONS_V2_ENABLED)) {
|
||||||
|
await this.workspacePermissionsCacheService.recomputeRolesPermissionsCache(
|
||||||
|
{ workspaceId, ignoreLock: true },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
await this.workspaceFeatureFlagsMapCacheService.recomputeFeatureFlagsMapCache(
|
await this.workspaceFeatureFlagsMapCacheService.recomputeFeatureFlagsMapCache(
|
||||||
{
|
{
|
||||||
workspaceId: workspaceId,
|
workspaceId,
|
||||||
ignoreLock: true,
|
ignoreLock: true,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -136,6 +144,12 @@ export class FeatureFlagService {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (featureFlag === FeatureFlagKey.IS_PERMISSIONS_V2_ENABLED) {
|
||||||
|
await this.workspacePermissionsCacheService.recomputeRolesPermissionsCache(
|
||||||
|
{ workspaceId, ignoreLock: true },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
import { Module } from '@nestjs/common';
|
import { Module } from '@nestjs/common';
|
||||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||||
|
|
||||||
import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module';
|
|
||||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||||
import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
|
import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
|
||||||
import { UserWorkspaceRoleEntity } from 'src/engine/metadata-modules/role/user-workspace-role.entity';
|
import { UserWorkspaceRoleEntity } from 'src/engine/metadata-modules/role/user-workspace-role.entity';
|
||||||
|
import { WorkspaceFeatureFlagsMapCacheModule } from 'src/engine/metadata-modules/workspace-feature-flags-map-cache/workspace-feature-flags-map-cache.module';
|
||||||
import { WorkspacePermissionsCacheStorageService } from 'src/engine/metadata-modules/workspace-permissions-cache/workspace-permissions-cache-storage.service';
|
import { WorkspacePermissionsCacheStorageService } from 'src/engine/metadata-modules/workspace-permissions-cache/workspace-permissions-cache-storage.service';
|
||||||
import { WorkspaceCacheStorageModule } from 'src/engine/workspace-cache-storage/workspace-cache-storage.module';
|
import { WorkspaceCacheStorageModule } from 'src/engine/workspace-cache-storage/workspace-cache-storage.module';
|
||||||
|
|
||||||
@ -19,7 +19,7 @@ import { WorkspacePermissionsCacheService } from './workspace-permissions-cache.
|
|||||||
'core',
|
'core',
|
||||||
),
|
),
|
||||||
WorkspaceCacheStorageModule,
|
WorkspaceCacheStorageModule,
|
||||||
FeatureFlagModule,
|
WorkspaceFeatureFlagsMapCacheModule,
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
WorkspacePermissionsCacheService,
|
WorkspacePermissionsCacheService,
|
||||||
|
|||||||
@ -9,16 +9,15 @@ import { isDefined } from 'twenty-shared/utils';
|
|||||||
import { In, Repository } from 'typeorm';
|
import { In, Repository } from 'typeorm';
|
||||||
|
|
||||||
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
|
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 { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||||
import { SettingPermissionType } from 'src/engine/metadata-modules/permissions/constants/setting-permission-type.constants';
|
import { SettingPermissionType } from 'src/engine/metadata-modules/permissions/constants/setting-permission-type.constants';
|
||||||
import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
|
import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
|
||||||
import { UserWorkspaceRoleEntity } from 'src/engine/metadata-modules/role/user-workspace-role.entity';
|
import { UserWorkspaceRoleEntity } from 'src/engine/metadata-modules/role/user-workspace-role.entity';
|
||||||
|
import { WorkspaceFeatureFlagsMapCacheService } from 'src/engine/metadata-modules/workspace-feature-flags-map-cache/workspace-feature-flags-map-cache.service';
|
||||||
import { UserWorkspaceRoleMap } from 'src/engine/metadata-modules/workspace-permissions-cache/types/user-workspace-role-map.type';
|
import { UserWorkspaceRoleMap } from 'src/engine/metadata-modules/workspace-permissions-cache/types/user-workspace-role-map.type';
|
||||||
import { WorkspacePermissionsCacheStorageService } from 'src/engine/metadata-modules/workspace-permissions-cache/workspace-permissions-cache-storage.service';
|
import { WorkspacePermissionsCacheStorageService } from 'src/engine/metadata-modules/workspace-permissions-cache/workspace-permissions-cache-storage.service';
|
||||||
import { TwentyORMExceptionCode } from 'src/engine/twenty-orm/exceptions/twenty-orm.exception';
|
import { TwentyORMExceptionCode } from 'src/engine/twenty-orm/exceptions/twenty-orm.exception';
|
||||||
import { getFromCacheWithRecompute } from 'src/engine/utils/get-data-from-cache-with-recompute.util';
|
import { getFromCacheWithRecompute } from 'src/engine/utils/get-data-from-cache-with-recompute.util';
|
||||||
import { WorkspaceCacheStorageService } from 'src/engine/workspace-cache-storage/workspace-cache-storage.service';
|
|
||||||
import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids';
|
import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids';
|
||||||
|
|
||||||
type CacheResult<T, U> = {
|
type CacheResult<T, U> = {
|
||||||
@ -41,8 +40,7 @@ export class WorkspacePermissionsCacheService {
|
|||||||
@InjectRepository(UserWorkspaceRoleEntity, 'core')
|
@InjectRepository(UserWorkspaceRoleEntity, 'core')
|
||||||
private readonly userWorkspaceRoleRepository: Repository<UserWorkspaceRoleEntity>,
|
private readonly userWorkspaceRoleRepository: Repository<UserWorkspaceRoleEntity>,
|
||||||
private readonly workspacePermissionsCacheStorageService: WorkspacePermissionsCacheStorageService,
|
private readonly workspacePermissionsCacheStorageService: WorkspacePermissionsCacheStorageService,
|
||||||
private readonly workspaceCacheStorageService: WorkspaceCacheStorageService,
|
private readonly workspaceFeatureFlagsMapCacheService: WorkspaceFeatureFlagsMapCacheService,
|
||||||
private readonly featureFlagService: FeatureFlagService,
|
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async recomputeRolesPermissionsCache({
|
async recomputeRolesPermissionsCache({
|
||||||
@ -87,12 +85,14 @@ export class WorkspacePermissionsCacheService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const isPermissionsV2Enabled =
|
const workspaceFeatureFlagsMap =
|
||||||
await this.featureFlagService.isFeatureEnabled(
|
await this.workspaceFeatureFlagsMapCacheService.getWorkspaceFeatureFlagsMap(
|
||||||
FeatureFlagKey.IS_PERMISSIONS_V2_ENABLED,
|
{ workspaceId },
|
||||||
workspaceId,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const isPermissionsV2Enabled =
|
||||||
|
!!workspaceFeatureFlagsMap[FeatureFlagKey.IS_PERMISSIONS_V2_ENABLED];
|
||||||
|
|
||||||
const recomputedRolesPermissions =
|
const recomputedRolesPermissions =
|
||||||
await this.getObjectRecordPermissionsForRoles({
|
await this.getObjectRecordPermissionsForRoles({
|
||||||
workspaceId,
|
workspaceId,
|
||||||
|
|||||||
Reference in New Issue
Block a user