Compare commits

...

21 Commits

Author SHA1 Message Date
Abdul Rahman 2f53487c49 Merge branch 'migrate-permission-flag-to-syncable-entity' into object-permission-syncable-entity-migration 2026-03-12 16:59:21 +05:30
Abdul Rahman 498a0197c9 Merge branch 'main' into migrate-permission-flag-to-syncable-entity 2026-03-12 16:51:27 +05:30
Abdul Rahman 194ca85578 Refactor successful permission flag upsert test for clarity
- Removed unused `isDefined` utility and simplified the role ID assignment in the successful permission flag upsert integration test.
- This change enhances code readability and maintains the integrity of the test logic.

This update aligns with ongoing efforts to improve test clarity and maintainability.
2026-03-12 07:45:45 +05:30
Abdul Rahman e5defd686c Add duplicate permission flag validation in FlatPermissionFlagValidatorService
- Implemented a check for duplicate permission flags for the same role within the FlatPermissionFlagValidatorService.
- Added error handling to ensure that attempts to set a duplicate permission flag result in appropriate validation errors.

This update enhances the validation logic for permission flags, improving the integrity of permission management within the workspace migration process.
2026-03-12 07:44:11 +05:30
Abdul Rahman 38532ee0c3 Add objectPermission support across metadata modules
- Introduced `objectPermission` properties in various metadata-related constants and types to enhance permission management capabilities.
- Updated relevant utility functions and entity definitions to accommodate the new `objectPermission` structure.
- Added new types and constants for `FlatObjectPermission` and its editable properties, improving integration with the overall metadata framework.

This update aligns with ongoing enhancements to the permission management system, ensuring comprehensive support for object-level permissions.
2026-03-12 07:13:28 +05:30
Abdul Rahman 30a51ae98e Add objectPermission to useMetadataErrorHandler hook
- Included a new `objectPermission` property in the `useMetadataErrorHandler` hook to enhance metadata handling capabilities.
- This addition aligns with ongoing improvements to the metadata structure and permission management functionalities.

This update supports better integration of object permissions within the system.
2026-03-12 07:10:59 +05:30
Abdul Rahman ef99ec66a7 Update Jest snapshots to include permissionFlag in related tests
- Added `permissionFlag` to the snapshots for `getMetadataRelatedMetadataNames` and `sortMetadataNamesChildrenFirst` utilities, ensuring comprehensive test coverage for permission-related functionalities.
- Updated the snapshot for `ALL_UNIVERSAL_FLAT_ENTITY_FOREIGN_KEY_AGGREGATOR_PROPERTIES` to reflect the inclusion of `permissionFlag` and its associated properties.

This change enhances the accuracy of tests related to permission management, aligning with recent updates to the metadata structure.
2026-03-12 03:24:11 +05:30
Abdul Rahman 7af8fdb7a3 Add permissionFlag to AllMetadataName enum in GraphQL schema
- Included 'permissionFlag' in the AllMetadataName enum to enhance metadata structure.
- Removed unused InstallApplication mutation and related types to streamline the codebase.

This update improves integration with permission management functionalities and cleans up the GraphQL schema by eliminating unnecessary code.
2026-03-12 03:13:38 +05:30
Abdul Rahman 0b0ef20193 Add permissionFlag properties to Jest snapshot for comparison and stringification
- Updated the Jest snapshot for `ALL_UNIVERSAL_FLAT_ENTITY_PROPERTIES_TO_COMPARE_AND_STRINGIFY` to include the new `permissionFlag` property.
- Defined `propertiesToCompare` for `permissionFlag`, enhancing the test coverage for permission flag functionalities.

This change ensures that the snapshot accurately reflects the current state of the permission flag properties, improving the reliability of tests related to permission management.
2026-03-12 03:07:22 +05:30
Abdul Rahman ee8c74fba6 Update snapshot for failing permission flag upsert test to correct message formatting
- Adjusted the formatting of the error message in the Jest snapshot for the failing permission flag upsert integration test.
- This change ensures consistency in the error message output, improving clarity in test results.

This update aligns with ongoing efforts to enhance the accuracy and readability of test outputs.
2026-03-12 03:00:23 +05:30
Abdul Rahman 6d69808a4a Add permissionFlag to AllMetadataName type and GraphQL schema
- Updated the AllMetadataName type in schema.ts to include 'permissionFlag'.
- Added 'permissionFlag' to the GraphQL enum definition in schema.graphql.

This change enhances the metadata structure by incorporating permission flags, improving integration with permission management functionalities.
2026-03-12 02:44:55 +05:30
Abdul Rahman 411cb7332b Add Jest snapshots for failing permission flag upsert tests
- Created a new snapshot file for integration tests related to the permission flag upsert functionality.
- The snapshots cover various failure scenarios, including invalid enum values, non-editable roles, non-existent role IDs, and invalid UUIDs.

This addition enhances the test coverage for permission flag operations, ensuring accurate validation of error handling during upsert operations.
2026-03-12 02:36:53 +05:30
Abdul Rahman 100962e67b Add permission flag to metadata error handler
- Updated the `useMetadataErrorHandler` hook to include a new `permissionFlag` property in the metadata structure.
- This addition enhances the metadata handling capabilities related to permission flags, improving integration with permission management processes.

This update aligns with ongoing improvements to permission flag functionalities within the system.
2026-03-12 02:26:38 +05:30
Abdul Rahman 9bc64ae4d9 Refactor permission flag utility and update workspace migration service
- Cleaned up the `fromPermissionFlagEntityToFlatPermissionFlag` utility by adjusting the formatting of the `universalIdentifier` property assignment for better readability.
- Added import for `WorkspaceMigrationViewSortActionsBuilderService` in the `workspace-migration-build-orchestrator.service.ts` to enhance the workspace migration capabilities.

This update improves code clarity and integrates additional functionality for workspace migration processes.
2026-03-12 02:26:24 +05:30
Abdul Rahman 9483ba37b9 Add integration tests for permission flag upsert functionality
- Introduced two new test files: `failing-permission-flag-upsert.integration-spec.ts` and `successful-permission-flag-upsert.integration-spec.ts`.
- The failing tests cover scenarios such as invalid UUIDs, non-existent roles, and invalid permission flag keys.
- The successful tests validate the upsert functionality with various permission flag inputs, including empty and multiple flags.
- Added utility functions for constructing GraphQL queries related to permission flag upserts.

This update enhances the test coverage for permission flag operations, ensuring robust validation of both success and failure cases during upsert operations.
2026-03-12 02:14:14 +05:30
Abdul Rahman e35ef97e78 Refactor permission flag module and service for workspace migration
- Updated `PermissionFlagModule` to include new dependencies: `ApplicationModule`, `WorkspaceMigrationModule`, and `WorkspaceManyOrAllFlatEntityMapsCacheModule`.
- Enhanced `PermissionFlagService` to integrate with `WorkspaceMigrationValidateBuildAndRunService` and `WorkspaceManyOrAllFlatEntityMapsCacheService`, improving permission flag handling during workspace migrations.
- Introduced utility function `fromFlatPermissionFlagToPermissionFlagDto` for converting flat permission flags to DTOs.

This update streamlines permission flag management and enhances integration with workspace migration processes.
2026-03-12 02:09:07 +05:30
Abdul Rahman 4e8d2c706b Add permission flag action handlers for workspace migration
- Introduced `CreatePermissionFlagActionHandlerService`, `DeletePermissionFlagActionHandlerService`, and `UpdatePermissionFlagActionHandlerService` to manage permission flag actions during workspace migration.
- Implemented methods for transpiling actions and executing database operations for creating, deleting, and updating permission flags.
- Enhanced the workspace migration utilities to support the new permission flag action handlers.

This update improves the handling of permission flags within the workspace migration process, ensuring robust action management and integration.
2026-03-12 01:55:15 +05:30
Abdul Rahman bc1570810a Add permission flag actions and validation services
- Introduced `WorkspaceMigrationPermissionFlagActionsBuilderService` for handling permission flag actions during workspace migration.
- Added `FlatPermissionFlagValidatorService` to validate creation, update, and deletion of permission flags.
- Updated `workspace-migration-builder` module to include new services and validators for permission flags.
- Enhanced existing migration utilities to support permission flag integration.

This update improves the management of permission flags, ensuring robust validation and action handling within the workspace migration process.
2026-03-12 01:27:51 +05:30
Abdul Rahman 27f2a81207 Add permission flag handling and related utilities
- Introduced `WorkspaceFlatPermissionFlagMapCacheService` for managing permission flag mappings in the workspace cache.
- Added utility functions for converting between permission flag entities and flat permission flags.
- Updated role and application metadata to include permission flag identifiers.
- Enhanced workspace migration utilities to support permission flag actions.

This update improves the management and integration of permission flags within the system, facilitating better access control and permissions handling.
2026-03-11 18:55:58 +05:30
Abdul Rahman 23c2449d86 Add migration to include universalIdentifier and applicationId in permissionFlag table
- Introduced a new migration that adds `universalIdentifier` and `applicationId` columns to the `permissionFlag` table.
- Created a unique index on the combination of `workspaceId` and `universalIdentifier`.
- Established a foreign key constraint for `applicationId` referencing the `application` table.

This update enhances the permission flag structure, allowing for better identification and association with applications.
2026-03-11 18:04:26 +05:30
Abdul Rahman b2beefb6d7 Add permission flag metadata and related types
- Introduced `permissionFlag` entity and its configuration across various constants.
- Added editable properties for `permissionFlag` and defined its flat entity types.
- Updated related metadata structures to include `permissionFlag` in serialization and validation.
- Created new types for handling permission flag actions in workspace migration.

This update enhances the metadata handling for permission flags, allowing for better integration and management within the system.
2026-03-11 17:48:13 +05:30
66 changed files with 1691 additions and 168 deletions
@@ -261,6 +261,7 @@ export enum AllMetadataName {
pageLayout = 'pageLayout',
pageLayoutTab = 'pageLayoutTab',
pageLayoutWidget = 'pageLayoutWidget',
permissionFlag = 'permissionFlag',
role = 'role',
roleTarget = 'roleTarget',
rowLevelPermissionPredicate = 'rowLevelPermissionPredicate',
@@ -30,6 +30,8 @@ export const useMetadataErrorHandler = () => {
viewFilter: t`view filter`,
index: t`index`,
logicFunction: t`logic function`,
objectPermission: t`object permission`,
permissionFlag: t`permission flag`,
role: t`role`,
roleTarget: t`role target`,
agent: t`agent`,
@@ -4157,6 +4157,7 @@ enum AllMetadataName {
pageLayoutTab
commandMenuItem
navigationMenuItem
permissionFlag
frontComponent
webhook
}
@@ -2809,7 +2809,7 @@ export type AnalyticsType = 'PAGEVIEW' | 'TRACK'
export type WorkspaceMigrationActionType = 'delete' | 'create' | 'update'
export type AllMetadataName = 'fieldMetadata' | 'objectMetadata' | 'view' | 'viewField' | 'viewFieldGroup' | 'viewGroup' | 'viewSort' | 'rowLevelPermissionPredicate' | 'rowLevelPermissionPredicateGroup' | 'viewFilterGroup' | 'index' | 'logicFunction' | 'viewFilter' | 'role' | 'roleTarget' | 'agent' | 'skill' | 'pageLayout' | 'pageLayoutWidget' | 'pageLayoutTab' | 'commandMenuItem' | 'navigationMenuItem' | 'frontComponent' | 'webhook'
export type AllMetadataName = 'fieldMetadata' | 'objectMetadata' | 'view' | 'viewField' | 'viewFieldGroup' | 'viewGroup' | 'viewSort' | 'rowLevelPermissionPredicate' | 'rowLevelPermissionPredicateGroup' | 'viewFilterGroup' | 'index' | 'logicFunction' | 'viewFilter' | 'role' | 'roleTarget' | 'agent' | 'skill' | 'pageLayout' | 'pageLayoutWidget' | 'pageLayoutTab' | 'commandMenuItem' | 'navigationMenuItem' | 'permissionFlag' | 'frontComponent' | 'webhook'
export type FileFolder = 'ProfilePicture' | 'WorkspaceLogo' | 'Attachment' | 'PersonPicture' | 'CorePicture' | 'File' | 'AgentChat' | 'BuiltLogicFunction' | 'BuiltFrontComponent' | 'PublicAsset' | 'Source' | 'FilesField' | 'Dependencies' | 'Workflow' | 'AppTarball'
@@ -8685,6 +8685,7 @@ export const enumAllMetadataName = {
pageLayoutTab: 'pageLayoutTab' as const,
commandMenuItem: 'commandMenuItem' as const,
navigationMenuItem: 'navigationMenuItem' as const,
permissionFlag: 'permissionFlag' as const,
frontComponent: 'frontComponent' as const,
webhook: 'webhook' as const
}
@@ -0,0 +1,37 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class AddUniversalIdentifierAndApplicationIdToPermissionFlag1773232418467
implements MigrationInterface
{
name = 'AddUniversalIdentifierAndApplicationIdToPermissionFlag1773232418467';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "core"."permissionFlag" ADD "universalIdentifier" uuid NOT NULL`,
);
await queryRunner.query(
`ALTER TABLE "core"."permissionFlag" ADD "applicationId" uuid NOT NULL`,
);
await queryRunner.query(
`CREATE UNIQUE INDEX "IDX_da8ffd3c24b4a819430a861067" ON "core"."permissionFlag" ("workspaceId", "universalIdentifier") `,
);
await queryRunner.query(
`ALTER TABLE "core"."permissionFlag" ADD CONSTRAINT "FK_b26a9d39a88d0e72373c677c6c5" FOREIGN KEY ("applicationId") REFERENCES "core"."application"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "core"."permissionFlag" DROP CONSTRAINT "FK_b26a9d39a88d0e72373c677c6c5"`,
);
await queryRunner.query(
`DROP INDEX "core"."IDX_da8ffd3c24b4a819430a861067"`,
);
await queryRunner.query(
`ALTER TABLE "core"."permissionFlag" DROP COLUMN "applicationId"`,
);
await queryRunner.query(
`ALTER TABLE "core"."permissionFlag" DROP COLUMN "universalIdentifier"`,
);
}
}
@@ -30,6 +30,8 @@ export const fromRoleManifestToUniversalFlatRole = ({
canBeAssignedToAgents: roleManifest.canBeAssignedToAgents ?? true,
canBeAssignedToApiKeys: roleManifest.canBeAssignedToApiKeys ?? true,
roleTargetUniversalIdentifiers: [],
objectPermissionUniversalIdentifiers: [],
permissionFlagUniversalIdentifiers: [],
rowLevelPermissionPredicateUniversalIdentifiers: [],
rowLevelPermissionPredicateGroupUniversalIdentifiers: [],
createdAt: now,
@@ -172,6 +172,13 @@ exports[`ALL_UNIVERSAL_FLAT_ENTITY_PROPERTIES_TO_COMPARE_AND_STRINGIFY should ma
"overrides",
],
},
"permissionFlag": {
"propertiesToCompare": [
"flag",
"roleUniversalIdentifier",
],
"propertiesToStringify": [],
},
"role": {
"propertiesToCompare": [
"label",
@@ -1088,6 +1088,70 @@ export const ALL_ENTITY_PROPERTIES_CONFIGURATION_BY_METADATA_NAME = {
universalProperty: 'targetObjectMetadataUniversalIdentifier',
},
},
objectPermission: {
roleId: {
toCompare: true,
toStringify: false,
universalProperty: 'roleUniversalIdentifier',
},
objectMetadataId: {
toCompare: true,
toStringify: false,
universalProperty: 'objectMetadataUniversalIdentifier',
},
canReadObjectRecords: {
toCompare: true,
toStringify: false,
universalProperty: undefined,
},
canUpdateObjectRecords: {
toCompare: true,
toStringify: false,
universalProperty: undefined,
},
canSoftDeleteObjectRecords: {
toCompare: true,
toStringify: false,
universalProperty: undefined,
},
canDestroyObjectRecords: {
toCompare: true,
toStringify: false,
universalProperty: undefined,
},
createdAt: {
toCompare: false,
toStringify: false,
universalProperty: undefined,
},
updatedAt: {
toCompare: false,
toStringify: false,
universalProperty: undefined,
},
},
permissionFlag: {
flag: {
toCompare: true,
toStringify: false,
universalProperty: undefined,
},
roleId: {
toCompare: true,
toStringify: false,
universalProperty: 'roleUniversalIdentifier',
},
createdAt: {
toCompare: false,
toStringify: false,
universalProperty: undefined,
},
updatedAt: {
toCompare: false,
toStringify: false,
universalProperty: undefined,
},
},
rowLevelPermissionPredicate: {
fieldMetadataId: {
toCompare: true,
@@ -163,6 +163,23 @@ export const ALL_MANY_TO_ONE_METADATA_FOREIGN_KEY = {
workspace: null,
application: null,
},
permissionFlag: {
workspace: null,
application: null,
role: {
foreignKey: 'roleId',
},
},
objectPermission: {
workspace: null,
application: null,
role: {
foreignKey: 'roleId',
},
objectMetadata: {
foreignKey: 'objectMetadataId',
},
},
pageLayout: {
workspace: null,
objectMetadata: {
@@ -280,6 +280,35 @@ export const ALL_MANY_TO_ONE_METADATA_RELATIONS = {
workspace: null,
application: null,
},
permissionFlag: {
workspace: null,
application: null,
role: {
metadataName: 'role',
foreignKey: 'roleId',
inverseOneToManyProperty: 'permissionFlags',
isNullable: false,
universalForeignKey: 'roleUniversalIdentifier',
},
},
objectPermission: {
workspace: null,
application: null,
role: {
metadataName: 'role',
foreignKey: 'roleId',
inverseOneToManyProperty: 'objectPermissions',
isNullable: false,
universalForeignKey: 'roleUniversalIdentifier',
},
objectMetadata: {
metadataName: 'objectMetadata',
foreignKey: 'objectMetadataId',
inverseOneToManyProperty: 'objectPermissions',
isNullable: false,
universalForeignKey: 'objectMetadataUniversalIdentifier',
},
},
pageLayout: {
workspace: null,
objectMetadata: {
@@ -8,10 +8,12 @@ import { FrontComponentEntity } from 'src/engine/metadata-modules/front-componen
import { IndexMetadataEntity } from 'src/engine/metadata-modules/index-metadata/index-metadata.entity';
import { WebhookEntity } from 'src/engine/metadata-modules/webhook/entities/webhook.entity';
import { NavigationMenuItemEntity } from 'src/engine/metadata-modules/navigation-menu-item/entities/navigation-menu-item.entity';
import { ObjectPermissionEntity } from 'src/engine/metadata-modules/object-permission/object-permission.entity';
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
import { PageLayoutTabEntity } from 'src/engine/metadata-modules/page-layout-tab/entities/page-layout-tab.entity';
import { PageLayoutWidgetEntity } from 'src/engine/metadata-modules/page-layout-widget/entities/page-layout-widget.entity';
import { PageLayoutEntity } from 'src/engine/metadata-modules/page-layout/entities/page-layout.entity';
import { PermissionFlagEntity } from 'src/engine/metadata-modules/permission-flag/permission-flag.entity';
import { RoleTargetEntity } from 'src/engine/metadata-modules/role-target/role-target.entity';
import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
import { RowLevelPermissionPredicateGroupEntity } from 'src/engine/metadata-modules/row-level-permission-predicate/entities/row-level-permission-predicate-group.entity';
@@ -45,10 +47,12 @@ export const ALL_METADATA_ENTITY_BY_METADATA_NAME = {
skill: SkillEntity,
logicFunction: LogicFunctionEntity,
objectMetadata: ObjectMetadataEntity,
objectPermission: ObjectPermissionEntity,
role: RoleEntity,
agent: AgentEntity,
commandMenuItem: CommandMenuItemEntity,
navigationMenuItem: NavigationMenuItemEntity,
permissionFlag: PermissionFlagEntity,
webhook: WebhookEntity,
viewSort: ViewSortEntity,
} as const satisfies Record<AllMetadataName, EntityTarget<ObjectLiteral>>;
@@ -74,6 +74,13 @@ export const ALL_METADATA_REQUIRED_METADATA_FOR_VALIDATION = {
objectMetadata: true,
view: true,
},
objectPermission: {
role: true,
objectMetadata: true,
},
permissionFlag: {
role: true,
},
pageLayout: {
objectMetadata: true,
pageLayoutTab: true,
@@ -33,6 +33,8 @@ export const ALL_METADATA_SERIALIZED_RELATION = {
logicFunction: {},
role: {},
roleTarget: {},
objectPermission: {},
permissionFlag: {},
pageLayout: {},
pageLayoutTab: {},
pageLayoutWidget: {
@@ -85,7 +85,12 @@ export const ALL_ONE_TO_MANY_METADATA_RELATIONS = {
universalFlatEntityForeignKeyAggregator:
'indexMetadataUniversalIdentifiers',
},
objectPermissions: null,
objectPermissions: {
metadataName: 'objectPermission',
flatEntityForeignKeyAggregator: 'objectPermissionIds',
universalFlatEntityForeignKeyAggregator:
'objectPermissionUniversalIdentifiers',
},
fieldPermissions: null,
views: {
metadataName: 'view',
@@ -144,8 +149,18 @@ export const ALL_ONE_TO_MANY_METADATA_RELATIONS = {
flatEntityForeignKeyAggregator: 'roleTargetIds',
universalFlatEntityForeignKeyAggregator: 'roleTargetUniversalIdentifiers',
},
objectPermissions: null,
permissionFlags: null,
objectPermissions: {
metadataName: 'objectPermission',
flatEntityForeignKeyAggregator: 'objectPermissionIds',
universalFlatEntityForeignKeyAggregator:
'objectPermissionUniversalIdentifiers',
},
permissionFlags: {
metadataName: 'permissionFlag',
flatEntityForeignKeyAggregator: 'permissionFlagIds',
universalFlatEntityForeignKeyAggregator:
'permissionFlagUniversalIdentifiers',
},
fieldPermissions: null,
rowLevelPermissionPredicates: {
metadataName: 'rowLevelPermissionPredicate',
@@ -161,6 +176,8 @@ export const ALL_ONE_TO_MANY_METADATA_RELATIONS = {
},
},
roleTarget: {},
objectPermission: {},
permissionFlag: {},
pageLayout: {
tabs: {
metadataName: 'pageLayoutTab',
@@ -25,6 +25,7 @@ import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadat
import { PageLayoutTabEntity } from 'src/engine/metadata-modules/page-layout-tab/entities/page-layout-tab.entity';
import { PageLayoutWidgetEntity } from 'src/engine/metadata-modules/page-layout-widget/entities/page-layout-widget.entity';
import { PageLayoutEntity } from 'src/engine/metadata-modules/page-layout/entities/page-layout.entity';
import { PermissionFlagEntity } from 'src/engine/metadata-modules/permission-flag/permission-flag.entity';
import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
import { RowLevelPermissionPredicateGroupEntity } from 'src/engine/metadata-modules/row-level-permission-predicate/entities/row-level-permission-predicate-group.entity';
import { RowLevelPermissionPredicateEntity } from 'src/engine/metadata-modules/row-level-permission-predicate/entities/row-level-permission-predicate.entity';
@@ -36,6 +37,7 @@ import { ViewGroupEntity } from 'src/engine/metadata-modules/view-group/entities
import { ViewEntity } from 'src/engine/metadata-modules/view/entities/view.entity';
import { WorkspaceCacheModule } from 'src/engine/workspace-cache/workspace-cache.module';
import { ViewSortEntity } from 'src/engine/metadata-modules/view-sort/entities/view-sort.entity';
import { WorkspaceFlatPermissionFlagMapCacheService } from 'src/engine/metadata-modules/flat-permission-flag/services/workspace-flat-permission-flag-map-cache.service';
import { WorkspaceFlatViewSortMapCacheService } from 'src/engine/metadata-modules/flat-view-sort/services/workspace-flat-view-sort-map-cache.service';
@Module({
@@ -56,6 +58,7 @@ import { WorkspaceFlatViewSortMapCacheService } from 'src/engine/metadata-module
PageLayoutEntity,
PageLayoutTabEntity,
PageLayoutWidgetEntity,
PermissionFlagEntity,
RowLevelPermissionPredicateEntity,
RowLevelPermissionPredicateGroupEntity,
ApplicationEntity,
@@ -74,6 +77,7 @@ import { WorkspaceFlatViewSortMapCacheService } from 'src/engine/metadata-module
WorkspaceFlatIndexMapCacheService,
WorkspaceFlatFieldMetadataMapCacheService,
WorkspaceFlatViewGroupMapCacheService,
WorkspaceFlatPermissionFlagMapCacheService,
WorkspaceFlatViewSortMapCacheService,
WorkspaceFlatPageLayoutMapCacheService,
WorkspaceFlatPageLayoutTabMapCacheService,
@@ -92,6 +96,7 @@ import { WorkspaceFlatViewSortMapCacheService } from 'src/engine/metadata-module
WorkspaceFlatIndexMapCacheService,
WorkspaceFlatFieldMetadataMapCacheService,
WorkspaceFlatViewGroupMapCacheService,
WorkspaceFlatPermissionFlagMapCacheService,
WorkspaceFlatViewSortMapCacheService,
WorkspaceFlatPageLayoutMapCacheService,
WorkspaceFlatPageLayoutTabMapCacheService,
@@ -12,12 +12,16 @@ import { type FlatIndexMetadata } from 'src/engine/metadata-modules/flat-index-m
import { type FlatNavigationMenuItemMaps } from 'src/engine/metadata-modules/flat-navigation-menu-item/types/flat-navigation-menu-item-maps.type';
import { type FlatNavigationMenuItem } from 'src/engine/metadata-modules/flat-navigation-menu-item/types/flat-navigation-menu-item.type';
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
import { type FlatObjectPermissionMaps } from 'src/engine/metadata-modules/flat-object-permission/types/flat-object-permission-maps.type';
import { type FlatObjectPermission } from 'src/engine/metadata-modules/flat-object-permission/types/flat-object-permission.type';
import { type FlatPageLayoutTabMaps } from 'src/engine/metadata-modules/flat-page-layout-tab/types/flat-page-layout-tab-maps.type';
import { type FlatPageLayoutTab } from 'src/engine/metadata-modules/flat-page-layout-tab/types/flat-page-layout-tab.type';
import { type FlatPageLayoutWidgetMaps } from 'src/engine/metadata-modules/flat-page-layout-widget/types/flat-page-layout-widget-maps.type';
import { type FlatPageLayoutWidget } from 'src/engine/metadata-modules/flat-page-layout-widget/types/flat-page-layout-widget.type';
import { type FlatPageLayoutMaps } from 'src/engine/metadata-modules/flat-page-layout/types/flat-page-layout-maps.type';
import { type FlatPageLayout } from 'src/engine/metadata-modules/flat-page-layout/types/flat-page-layout.type';
import { type FlatPermissionFlagMaps } from 'src/engine/metadata-modules/flat-permission-flag/types/flat-permission-flag-maps.type';
import { type FlatPermissionFlag } from 'src/engine/metadata-modules/flat-permission-flag/types/flat-permission-flag.type';
import { type FlatRoleTargetMaps } from 'src/engine/metadata-modules/flat-role-target/types/flat-role-target-maps.type';
import { type FlatRoleTarget } from 'src/engine/metadata-modules/flat-role-target/types/flat-role-target.type';
import { type FlatRole } from 'src/engine/metadata-modules/flat-role/types/flat-role.type';
@@ -53,21 +57,23 @@ import { type UniversalFlatIndexMetadata } from 'src/engine/workspace-manager/wo
import { type UniversalFlatLogicFunction } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-logic-function.type';
import { type UniversalFlatNavigationMenuItem } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-navigation-menu-item.type';
import { type UniversalFlatObjectMetadata } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-object-metadata.type';
import { type UniversalFlatPageLayout } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-page-layout.type';
import { type UniversalFlatObjectPermission } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-object-permission.type';
import { type UniversalFlatPageLayoutTab } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-page-layout-tab.type';
import { type UniversalFlatPageLayoutWidget } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-page-layout-widget.type';
import { type UniversalFlatRole } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-role.type';
import { type UniversalFlatPageLayout } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-page-layout.type';
import { type UniversalFlatPermissionFlag } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-permission-flag.type';
import { type UniversalFlatRoleTarget } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-role-target.type';
import { type UniversalFlatRowLevelPermissionPredicate } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-row-level-permission-predicate.type';
import { type UniversalFlatRole } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-role.type';
import { type UniversalFlatRowLevelPermissionPredicateGroup } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-row-level-permission-predicate-group.type';
import { type UniversalFlatRowLevelPermissionPredicate } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-row-level-permission-predicate.type';
import { type UniversalFlatSkill } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-skill.type';
import { type UniversalFlatViewFieldGroup } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-view-field-group.type';
import { type UniversalFlatViewField } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-view-field.type';
import { type UniversalFlatViewFilterGroup } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-view-filter-group.type';
import { type UniversalFlatViewFilter } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-view-filter.type';
import { type UniversalFlatViewGroup } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-view-group.type';
import { type UniversalFlatView } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-view.type';
import { type UniversalFlatViewSort } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-view-sort.type';
import { type UniversalFlatView } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-view.type';
import { type UniversalFlatWebhook } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-webhook.type';
import {
type FlatCreateAgentAction,
@@ -102,10 +108,10 @@ import {
type UniversalUpdateFrontComponentAction,
} from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/front-component/types/workspace-migration-front-component-action.type';
import {
type UniversalCreateIndexAction,
type FlatCreateIndexAction,
type FlatDeleteIndexAction,
type FlatUpdateIndexAction,
type UniversalCreateIndexAction,
type UniversalDeleteIndexAction,
type UniversalUpdateIndexAction,
} from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/index/types/workspace-migration-index-action';
@@ -125,6 +131,14 @@ import {
type UniversalDeleteNavigationMenuItemAction,
type UniversalUpdateNavigationMenuItemAction,
} from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/navigation-menu-item/types/workspace-migration-navigation-menu-item-action.type';
import {
type FlatCreateObjectPermissionAction,
type FlatDeleteObjectPermissionAction,
type FlatUpdateObjectPermissionAction,
type UniversalCreateObjectPermissionAction,
type UniversalDeleteObjectPermissionAction,
type UniversalUpdateObjectPermissionAction,
} from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/object-permission/types/workspace-migration-object-permission-action.type';
import {
type FlatCreateObjectAction,
type FlatDeleteObjectAction,
@@ -157,6 +171,14 @@ import {
type UniversalDeletePageLayoutAction,
type UniversalUpdatePageLayoutAction,
} from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/page-layout/types/workspace-migration-page-layout-action.type';
import {
type FlatCreatePermissionFlagAction,
type FlatDeletePermissionFlagAction,
type FlatUpdatePermissionFlagAction,
type UniversalCreatePermissionFlagAction,
type UniversalDeletePermissionFlagAction,
type UniversalUpdatePermissionFlagAction,
} from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/permission-flag/types/workspace-migration-permission-flag-action.type';
import {
type FlatCreateRoleTargetAction,
type FlatDeleteRoleTargetAction,
@@ -197,14 +219,6 @@ import {
type UniversalDeleteSkillAction,
type UniversalUpdateSkillAction,
} from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/skill/types/workspace-migration-skill-action.type';
import {
type FlatCreateViewFieldAction,
type FlatDeleteViewFieldAction,
type FlatUpdateViewFieldAction,
type UniversalCreateViewFieldAction,
type UniversalDeleteViewFieldAction,
type UniversalUpdateViewFieldAction,
} from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/view-field/types/workspace-migration-view-field-action.type';
import {
type FlatCreateViewFieldGroupAction,
type FlatDeleteViewFieldGroupAction,
@@ -213,6 +227,14 @@ import {
type UniversalDeleteViewFieldGroupAction,
type UniversalUpdateViewFieldGroupAction,
} from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/view-field-group/types/workspace-migration-view-field-group-action.type';
import {
type FlatCreateViewFieldAction,
type FlatDeleteViewFieldAction,
type FlatUpdateViewFieldAction,
type UniversalCreateViewFieldAction,
type UniversalDeleteViewFieldAction,
type UniversalUpdateViewFieldAction,
} from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/view-field/types/workspace-migration-view-field-action.type';
import {
type FlatCreateViewFilterGroupAction,
type FlatDeleteViewFilterGroupAction,
@@ -237,6 +259,14 @@ import {
type UniversalDeleteViewGroupAction,
type UniversalUpdateViewGroupAction,
} from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/view-group/types/workspace-migration-view-group-action.type';
import {
type FlatCreateViewSortAction,
type FlatDeleteViewSortAction,
type FlatUpdateViewSortAction,
type UniversalCreateViewSortAction,
type UniversalDeleteViewSortAction,
type UniversalUpdateViewSortAction,
} from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/view-sort/types/workspace-migration-view-sort-action.type';
import {
type FlatCreateViewAction,
type FlatDeleteViewAction,
@@ -253,14 +283,6 @@ import {
type UniversalDeleteWebhookAction,
type UniversalUpdateWebhookAction,
} from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/webhook/types/workspace-migration-webhook-action.type';
import {
type FlatCreateViewSortAction,
type FlatDeleteViewSortAction,
type FlatUpdateViewSortAction,
type UniversalCreateViewSortAction,
type UniversalDeleteViewSortAction,
type UniversalUpdateViewSortAction,
} from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/view-sort/types/workspace-migration-view-sort-action.type';
export type AllFlatEntityTypesByMetadataName = {
fieldMetadata: {
@@ -298,6 +320,22 @@ export type AllFlatEntityTypesByMetadataName = {
universalFlatEntity: UniversalFlatObjectMetadata;
entity: MetadataEntity<'objectMetadata'>;
};
objectPermission: {
flatEntityMaps: FlatObjectPermissionMaps;
universalActions: {
create: UniversalCreateObjectPermissionAction;
update: UniversalUpdateObjectPermissionAction;
delete: UniversalDeleteObjectPermissionAction;
};
flatActions: {
create: FlatCreateObjectPermissionAction;
update: FlatUpdateObjectPermissionAction;
delete: FlatDeleteObjectPermissionAction;
};
flatEntity: FlatObjectPermission;
universalFlatEntity: UniversalFlatObjectPermission;
entity: MetadataEntity<'objectPermission'>;
};
view: {
flatEntityMaps: FlatViewMaps;
universalActions: {
@@ -570,6 +608,22 @@ export type AllFlatEntityTypesByMetadataName = {
universalFlatEntity: UniversalFlatNavigationMenuItem;
entity: NavigationMenuItemEntity;
};
permissionFlag: {
flatEntityMaps: FlatPermissionFlagMaps;
universalActions: {
create: UniversalCreatePermissionFlagAction;
update: UniversalUpdatePermissionFlagAction;
delete: UniversalDeletePermissionFlagAction;
};
flatActions: {
create: FlatCreatePermissionFlagAction;
update: FlatUpdatePermissionFlagAction;
delete: FlatDeletePermissionFlagAction;
};
flatEntity: FlatPermissionFlag;
universalFlatEntity: UniversalFlatPermissionFlag;
entity: MetadataEntity<'permissionFlag'>;
};
pageLayout: {
flatEntityMaps: FlatPageLayoutMaps;
universalActions: {
@@ -67,9 +67,16 @@ exports[`getMetadataRelatedMetadataNames should return related metadata names fo
]
`;
exports[`getMetadataRelatedMetadataNames should return related metadata names for permissionFlag 1`] = `
[
"role",
]
`;
exports[`getMetadataRelatedMetadataNames should return related metadata names for role 1`] = `
[
"roleTarget",
"permissionFlag",
"rowLevelPermissionPredicate",
"rowLevelPermissionPredicateGroup",
]
@@ -11,6 +11,7 @@ exports[`sortMetadataNamesChildrenFirst should return metadata names sorted with
"viewSort",
"index",
"pageLayout",
"permissionFlag",
"roleTarget",
"rowLevelPermissionPredicateGroup",
"viewGroup",
@@ -0,0 +1,10 @@
import { type MetadataEntityPropertyName } from 'src/engine/metadata-modules/flat-entity/constant/all-entity-properties-configuration-by-metadata-name.constant';
export const FLAT_OBJECT_PERMISSION_EDITABLE_PROPERTIES = [
'roleId',
'objectMetadataId',
'canReadObjectRecords',
'canUpdateObjectRecords',
'canSoftDeleteObjectRecords',
'canDestroyObjectRecords',
] as const satisfies MetadataEntityPropertyName<'objectPermission'>[];
@@ -0,0 +1,4 @@
import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
import { type FlatObjectPermission } from 'src/engine/metadata-modules/flat-object-permission/types/flat-object-permission.type';
export type FlatObjectPermissionMaps = FlatEntityMaps<FlatObjectPermission>;
@@ -0,0 +1,4 @@
import { type FlatEntityFrom } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-from.type';
import { type ObjectPermissionEntity } from 'src/engine/metadata-modules/object-permission/object-permission.entity';
export type FlatObjectPermission = FlatEntityFrom<ObjectPermissionEntity>;
@@ -0,0 +1,6 @@
import { type MetadataEntityPropertyName } from 'src/engine/metadata-modules/flat-entity/constant/all-entity-properties-configuration-by-metadata-name.constant';
export const FLAT_PERMISSION_FLAG_EDITABLE_PROPERTIES = [
'flag',
'roleId',
] as const satisfies MetadataEntityPropertyName<'permissionFlag'>[];
@@ -0,0 +1,71 @@
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { ApplicationEntity } from 'src/engine/core-modules/application/application.entity';
import { createEmptyFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/constant/create-empty-flat-entity-maps.constant';
import { type FlatPermissionFlagMaps } from 'src/engine/metadata-modules/flat-permission-flag/types/flat-permission-flag-maps.type';
import { fromPermissionFlagEntityToFlatPermissionFlag } from 'src/engine/metadata-modules/flat-permission-flag/utils/from-permission-flag-entity-to-flat-permission-flag.util';
import { PermissionFlagEntity } from 'src/engine/metadata-modules/permission-flag/permission-flag.entity';
import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
import { WorkspaceCacheProvider } from 'src/engine/workspace-cache/interfaces/workspace-cache-provider.service';
import { WorkspaceCache } from 'src/engine/workspace-cache/decorators/workspace-cache.decorator';
import { createIdToUniversalIdentifierMap } from 'src/engine/workspace-cache/utils/create-id-to-universal-identifier-map.util';
import { addFlatEntityToFlatEntityMapsThroughMutationOrThrow } from 'src/engine/workspace-manager/workspace-migration/utils/add-flat-entity-to-flat-entity-maps-through-mutation-or-throw.util';
@Injectable()
@WorkspaceCache('flatPermissionFlagMaps')
export class WorkspaceFlatPermissionFlagMapCacheService extends WorkspaceCacheProvider<FlatPermissionFlagMaps> {
constructor(
@InjectRepository(PermissionFlagEntity)
private readonly permissionFlagRepository: Repository<PermissionFlagEntity>,
@InjectRepository(ApplicationEntity)
private readonly applicationRepository: Repository<ApplicationEntity>,
@InjectRepository(RoleEntity)
private readonly roleRepository: Repository<RoleEntity>,
) {
super();
}
async computeForCache(workspaceId: string): Promise<FlatPermissionFlagMaps> {
const [permissionFlags, applications, roles] = await Promise.all([
this.permissionFlagRepository.find({
where: { workspaceId },
withDeleted: true,
}),
this.applicationRepository.find({
where: { workspaceId },
select: ['id', 'universalIdentifier'],
withDeleted: true,
}),
this.roleRepository.find({
where: { workspaceId },
select: ['id', 'universalIdentifier'],
withDeleted: true,
}),
]);
const applicationIdToUniversalIdentifierMap =
createIdToUniversalIdentifierMap(applications);
const roleIdToUniversalIdentifierMap =
createIdToUniversalIdentifierMap(roles);
const flatPermissionFlagMaps = createEmptyFlatEntityMaps();
for (const permissionFlagEntity of permissionFlags) {
const flatPermissionFlag = fromPermissionFlagEntityToFlatPermissionFlag({
entity: permissionFlagEntity,
applicationIdToUniversalIdentifierMap,
roleIdToUniversalIdentifierMap,
});
addFlatEntityToFlatEntityMapsThroughMutationOrThrow({
flatEntity: flatPermissionFlag,
flatEntityMapsToMutate: flatPermissionFlagMaps,
});
}
return flatPermissionFlagMaps;
}
}
@@ -0,0 +1,4 @@
import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
import { type FlatPermissionFlag } from 'src/engine/metadata-modules/flat-permission-flag/types/flat-permission-flag.type';
export type FlatPermissionFlagMaps = FlatEntityMaps<FlatPermissionFlag>;
@@ -0,0 +1,4 @@
import { type FlatEntityFrom } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-from.type';
import { type PermissionFlagEntity } from 'src/engine/metadata-modules/permission-flag/permission-flag.entity';
export type FlatPermissionFlag = FlatEntityFrom<PermissionFlagEntity>;
@@ -0,0 +1,39 @@
import { v4 } from 'uuid';
import { type FlatApplication } from 'src/engine/core-modules/application/types/flat-application.type';
import { type AllFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/all-flat-entity-maps.type';
import { resolveEntityRelationUniversalIdentifiers } from 'src/engine/metadata-modules/flat-entity/utils/resolve-entity-relation-universal-identifiers.util';
import { type CreatePermissionFlagInput } from 'src/engine/metadata-modules/permission-flag/dtos/create-permission-flag.input';
import { type UniversalFlatPermissionFlag } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-permission-flag.type';
export const fromCreatePermissionFlagInputToFlatPermissionFlagToCreate = ({
createPermissionFlagInput,
flatApplication,
flatRoleMaps,
}: {
createPermissionFlagInput: CreatePermissionFlagInput;
flatApplication: FlatApplication;
} & Pick<AllFlatEntityMaps, 'flatRoleMaps'>): UniversalFlatPermissionFlag & {
id: string;
} => {
const { roleId, flag, universalIdentifier } = createPermissionFlagInput;
const now = new Date().toISOString();
const { roleUniversalIdentifier } = resolveEntityRelationUniversalIdentifiers(
{
metadataName: 'permissionFlag',
foreignKeyValues: { roleId },
flatEntityMaps: { flatRoleMaps },
},
);
return {
id: v4(),
flag,
universalIdentifier: universalIdentifier ?? v4(),
applicationUniversalIdentifier: flatApplication.universalIdentifier,
roleUniversalIdentifier,
createdAt: now,
updatedAt: now,
};
};
@@ -0,0 +1,53 @@
import { isDefined, removePropertiesFromRecord } from 'twenty-shared/utils';
import {
FlatEntityMapsException,
FlatEntityMapsExceptionCode,
} from 'src/engine/metadata-modules/flat-entity/exceptions/flat-entity-maps.exception';
import { getMetadataEntityRelationProperties } from 'src/engine/metadata-modules/flat-entity/utils/get-metadata-entity-relation-properties.util';
import { type FlatPermissionFlag } from 'src/engine/metadata-modules/flat-permission-flag/types/flat-permission-flag.type';
import { type FromEntityToFlatEntityArgs } from 'src/engine/workspace-cache/types/from-entity-to-flat-entity-args.type';
export const fromPermissionFlagEntityToFlatPermissionFlag = ({
entity: permissionFlagEntity,
applicationIdToUniversalIdentifierMap,
roleIdToUniversalIdentifierMap,
}: FromEntityToFlatEntityArgs<'permissionFlag'>): FlatPermissionFlag => {
const permissionFlagEntityWithoutRelations = removePropertiesFromRecord(
permissionFlagEntity,
getMetadataEntityRelationProperties('permissionFlag'),
);
const applicationUniversalIdentifier =
applicationIdToUniversalIdentifierMap.get(
permissionFlagEntity.applicationId,
);
if (!isDefined(applicationUniversalIdentifier)) {
throw new FlatEntityMapsException(
`Application with id ${permissionFlagEntity.applicationId} not found for permissionFlag ${permissionFlagEntity.id}`,
FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND,
);
}
const roleUniversalIdentifier = roleIdToUniversalIdentifierMap.get(
permissionFlagEntity.roleId,
);
if (!isDefined(roleUniversalIdentifier)) {
throw new FlatEntityMapsException(
`Role with id ${permissionFlagEntity.roleId} not found for permissionFlag ${permissionFlagEntity.id}`,
FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND,
);
}
return {
...permissionFlagEntityWithoutRelations,
createdAt: permissionFlagEntity.createdAt.toISOString(),
updatedAt: permissionFlagEntity.updatedAt.toISOString(),
universalIdentifier:
permissionFlagEntityWithoutRelations.universalIdentifier,
applicationUniversalIdentifier,
roleUniversalIdentifier,
};
};
@@ -54,7 +54,9 @@ export const fromCreateRoleInputToFlatRoleToCreate = ({
roleTargetIds: [],
roleTargetUniversalIdentifiers: [],
objectPermissionIds: [],
objectPermissionUniversalIdentifiers: [],
permissionFlagIds: [],
permissionFlagUniversalIdentifiers: [],
fieldPermissionIds: [],
rowLevelPermissionPredicateIds: [],
rowLevelPermissionPredicateUniversalIdentifiers: [],
@@ -68,6 +68,12 @@ export const fromRoleEntityToFlatRole = ({
roleTargetUniversalIdentifiers: roleEntity.roleTargets.map(
({ universalIdentifier }) => universalIdentifier,
),
objectPermissionUniversalIdentifiers: roleEntity.objectPermissions.map(
({ universalIdentifier }) => universalIdentifier,
),
permissionFlagUniversalIdentifiers: roleEntity.permissionFlags.map(
({ universalIdentifier }) => universalIdentifier,
),
rowLevelPermissionPredicateUniversalIdentifiers:
roleEntity.rowLevelPermissionPredicates.map(
({ universalIdentifier }) => universalIdentifier,
@@ -13,7 +13,7 @@ import {
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
import { WorkspaceRelatedEntity } from 'src/engine/workspace-manager/types/workspace-related-entity';
import { SyncableEntity } from 'src/engine/workspace-manager/types/syncable-entity.interface';
@Entity('objectPermission')
@Unique('IDX_OBJECT_PERMISSION_OBJECT_METADATA_ID_ROLE_ID_UNIQUE', [
@@ -21,7 +21,7 @@ import { WorkspaceRelatedEntity } from 'src/engine/workspace-manager/types/works
'roleId',
])
@Index('IDX_OBJECT_PERMISSION_WORKSPACE_ID_ROLE_ID', ['workspaceId', 'roleId'])
export class ObjectPermissionEntity extends WorkspaceRelatedEntity {
export class ObjectPermissionEntity extends SyncableEntity {
@PrimaryGeneratedColumn('uuid')
id: string;
@@ -0,0 +1,7 @@
import { PermissionFlagType } from 'twenty-shared/constants';
export type CreatePermissionFlagInput = {
roleId: string;
flag: PermissionFlagType;
universalIdentifier?: string;
};
@@ -12,11 +12,11 @@ import {
} from 'typeorm';
import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
import { WorkspaceRelatedEntity } from 'src/engine/workspace-manager/types/workspace-related-entity';
import { SyncableEntity } from 'src/engine/workspace-manager/types/syncable-entity.interface';
@Entity('permissionFlag')
@Unique('IDX_PERMISSION_FLAG_FLAG_ROLE_ID_UNIQUE', ['flag', 'roleId'])
export class PermissionFlagEntity extends WorkspaceRelatedEntity {
export class PermissionFlagEntity extends SyncableEntity {
@PrimaryGeneratedColumn('uuid')
id: string;
@@ -1,17 +1,18 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { PermissionFlagEntity } from 'src/engine/metadata-modules/permission-flag/permission-flag.entity';
import { ApplicationModule } from 'src/engine/core-modules/application/application.module';
import { WorkspaceManyOrAllFlatEntityMapsCacheModule } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.module';
import { PermissionFlagService } from 'src/engine/metadata-modules/permission-flag/permission-flag.service';
import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
import { WorkspaceCacheModule } from 'src/engine/workspace-cache/workspace-cache.module';
import { WorkspaceMigrationModule } from 'src/engine/workspace-manager/workspace-migration/workspace-migration.module';
@Module({
imports: [
TypeOrmModule.forFeature([PermissionFlagEntity, RoleEntity]),
WorkspaceCacheModule,
ApplicationModule,
WorkspaceMigrationModule,
WorkspaceManyOrAllFlatEntityMapsCacheModule,
],
providers: [PermissionFlagService],
exports: [PermissionFlagService],
})
@@ -1,26 +1,31 @@
import { InjectDataSource, InjectRepository } from '@nestjs/typeorm';
import { Injectable } from '@nestjs/common';
import { msg } from '@lingui/core/macro';
import { isDefined } from 'twenty-shared/utils';
import { DataSource, In, Repository } from 'typeorm';
import { PermissionFlagType } from 'twenty-shared/constants';
import { isDefined } from 'twenty-shared/utils';
import { ApplicationService } from 'src/engine/core-modules/application/application.service';
import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.service';
import { type FlatPermissionFlag } from 'src/engine/metadata-modules/flat-permission-flag/types/flat-permission-flag.type';
import { fromCreatePermissionFlagInputToFlatPermissionFlagToCreate } from 'src/engine/metadata-modules/flat-permission-flag/utils/from-create-permission-flag-input-to-flat-permission-flag-to-create.util';
import { type PermissionFlagDTO } from 'src/engine/metadata-modules/permission-flag/dtos/permission-flag.dto';
import { type UpsertPermissionFlagsInput } from 'src/engine/metadata-modules/permission-flag/dtos/upsert-permission-flag-input';
import { PermissionFlagEntity } from 'src/engine/metadata-modules/permission-flag/permission-flag.entity';
import { fromFlatPermissionFlagToPermissionFlagDto } from 'src/engine/metadata-modules/permission-flag/utils/from-flat-permission-flag-to-permission-flag-dto.util';
import {
PermissionsException,
PermissionsExceptionCode,
PermissionsExceptionMessage,
} from 'src/engine/metadata-modules/permissions/permissions.exception';
import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
import { WorkspaceCacheService } from 'src/engine/workspace-cache/services/workspace-cache.service';
import { WorkspaceMigrationBuilderException } from 'src/engine/workspace-manager/workspace-migration/exceptions/workspace-migration-builder-exception';
import { WorkspaceMigrationValidateBuildAndRunService } from 'src/engine/workspace-manager/workspace-migration/services/workspace-migration-validate-build-and-run-service';
@Injectable()
export class PermissionFlagService {
constructor(
@InjectRepository(RoleEntity)
private readonly roleRepository: Repository<RoleEntity>,
@InjectDataSource()
private readonly coreDataSource: DataSource,
private readonly workspaceMigrationValidateBuildAndRunService: WorkspaceMigrationValidateBuildAndRunService,
private readonly workspaceManyOrAllFlatEntityMapsCacheService: WorkspaceManyOrAllFlatEntityMapsCacheService,
private readonly applicationService: ApplicationService,
private readonly workspaceCacheService: WorkspaceCacheService,
) {}
@@ -30,11 +35,39 @@ export class PermissionFlagService {
}: {
workspaceId: string;
input: UpsertPermissionFlagsInput;
}): Promise<PermissionFlagEntity[]> {
await this.validateRoleIsEditableOrThrow({
roleId: input.roleId,
workspaceId,
});
}): Promise<PermissionFlagDTO[]> {
const { flatPermissionFlagMaps, flatRoleMaps } =
await this.workspaceManyOrAllFlatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
{
workspaceId,
flatMapsKeys: ['flatPermissionFlagMaps', 'flatRoleMaps'],
},
);
const roleUniversalId = flatRoleMaps.universalIdentifierById[input.roleId];
const roleById = isDefined(roleUniversalId)
? flatRoleMaps.byUniversalIdentifier[roleUniversalId]
: undefined;
if (!isDefined(roleById)) {
throw new PermissionsException(
PermissionsExceptionMessage.ROLE_NOT_FOUND,
PermissionsExceptionCode.ROLE_NOT_FOUND,
{
userFriendlyMessage: msg`The role you are trying to modify could not be found.`,
},
);
}
if (!roleById.isEditable) {
throw new PermissionsException(
PermissionsExceptionMessage.ROLE_NOT_EDITABLE,
PermissionsExceptionCode.ROLE_NOT_EDITABLE,
{
userFriendlyMessage: msg`This role cannot be modified because it is a system role. Only custom roles can be edited.`,
},
);
}
const invalidFlags = input.permissionFlagKeys.filter(
(flag) => !Object.values(PermissionFlagType).includes(flag),
@@ -50,112 +83,94 @@ export class PermissionFlagService {
);
}
const queryRunner = this.coreDataSource.createQueryRunner();
const roleUniversalIdentifier = roleById.universalIdentifier;
await queryRunner.connect();
await queryRunner.startTransaction();
const currentPermissionFlagsForRole = Object.values(
flatPermissionFlagMaps.byUniversalIdentifier,
).filter(
(pf): pf is FlatPermissionFlag =>
isDefined(pf) && pf.roleUniversalIdentifier === roleUniversalIdentifier,
);
try {
const existingPermissions = await queryRunner.manager.find(
PermissionFlagEntity,
const inputSet = new Set(input.permissionFlagKeys);
const existingSet = new Set(
currentPermissionFlagsForRole.map((pf) => pf.flag),
);
const flatApplication =
await this.getFlatApplicationForWorkspace(workspaceId);
const flatEntityToCreate = input.permissionFlagKeys
.filter((flag) => !existingSet.has(flag))
.map((flag) =>
fromCreatePermissionFlagInputToFlatPermissionFlagToCreate({
createPermissionFlagInput: { roleId: input.roleId, flag },
flatApplication,
flatRoleMaps,
}),
);
const flatEntityToDelete = currentPermissionFlagsForRole.filter(
(pf) => !inputSet.has(pf.flag),
);
if (flatEntityToCreate.length === 0 && flatEntityToDelete.length === 0) {
const unchanged = currentPermissionFlagsForRole.filter((pf) =>
inputSet.has(pf.flag),
);
return unchanged.map(fromFlatPermissionFlagToPermissionFlagDto);
}
const buildAndRunResult =
await this.workspaceMigrationValidateBuildAndRunService.validateBuildAndRunWorkspaceMigration(
{
where: {
roleId: input.roleId,
workspaceId,
allFlatEntityOperationByMetadataName: {
permissionFlag: {
flatEntityToCreate,
flatEntityToDelete,
flatEntityToUpdate: [],
},
},
workspaceId,
isSystemBuild: false,
applicationUniversalIdentifier: flatApplication.universalIdentifier,
},
);
const existingSettings = new Set(existingPermissions.map((p) => p.flag));
const inputSettings = new Set(input.permissionFlagKeys);
const flagsToAdd = input.permissionFlagKeys.filter(
(setting) => !existingSettings.has(setting),
if (buildAndRunResult.status === 'fail') {
throw new WorkspaceMigrationBuilderException(
buildAndRunResult,
'Validation errors occurred while upserting permission flags',
);
const permissionsToRemove = existingPermissions.filter(
(permission) => !inputSettings.has(permission.flag),
);
if (permissionsToRemove.length > 0) {
await queryRunner.manager.delete(PermissionFlagEntity, {
id: In(permissionsToRemove.map((p) => p.id)),
});
}
if (flagsToAdd.length > 0) {
const newPermissions = flagsToAdd.map((flag) =>
queryRunner.manager.create(PermissionFlagEntity, {
workspaceId,
roleId: input.roleId,
flag,
}),
);
await queryRunner.manager.save(PermissionFlagEntity, newPermissions);
}
await queryRunner.commitTransaction();
return queryRunner.manager.find(PermissionFlagEntity, {
where: { roleId: input.roleId, workspaceId },
order: { flag: 'ASC' },
});
} catch (error) {
if (queryRunner.isTransactionActive) {
try {
await queryRunner.rollbackTransaction();
} catch (error) {
// oxlint-disable-next-line no-console
console.trace(`Failed to rollback transaction: ${error.message}`);
}
}
if (error.message.includes('violates foreign key constraint')) {
const role = await this.roleRepository.findOne({
where: { id: input.roleId },
});
if (!isDefined(role)) {
throw new PermissionsException(
PermissionsExceptionMessage.ROLE_NOT_FOUND,
PermissionsExceptionCode.ROLE_NOT_FOUND,
{
userFriendlyMessage: msg`The role you are trying to modify could not be found. It may have been deleted or you may not have access to it.`,
},
);
}
}
throw error;
} finally {
await queryRunner.release();
await this.workspaceCacheService.invalidateAndRecompute(workspaceId, [
'rolesPermissions',
]);
}
await this.workspaceCacheService.invalidateAndRecompute(workspaceId, [
'rolesPermissions',
]);
const { flatPermissionFlagMaps: freshFlatPermissionFlagMaps } =
await this.workspaceManyOrAllFlatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
{
workspaceId,
flatMapsKeys: ['flatPermissionFlagMaps'],
},
);
const resultFlags = Object.values(
freshFlatPermissionFlagMaps.byUniversalIdentifier,
).filter(
(pf): pf is FlatPermissionFlag =>
isDefined(pf) && pf.roleUniversalIdentifier === roleUniversalIdentifier,
);
return resultFlags.map(fromFlatPermissionFlagToPermissionFlagDto);
}
private async validateRoleIsEditableOrThrow({
roleId,
workspaceId,
}: {
roleId: string;
workspaceId: string;
}) {
const role = await this.roleRepository.findOne({
where: {
id: roleId,
workspaceId,
},
});
if (!role?.isEditable) {
throw new PermissionsException(
PermissionsExceptionMessage.ROLE_NOT_EDITABLE,
PermissionsExceptionCode.ROLE_NOT_EDITABLE,
{
userFriendlyMessage: msg`This role cannot be modified because it is a system role. Only custom roles can be edited.`,
},
private async getFlatApplicationForWorkspace(workspaceId: string) {
const { workspaceCustomFlatApplication } =
await this.applicationService.findWorkspaceTwentyStandardAndCustomApplicationOrThrow(
{ workspaceId },
);
}
return workspaceCustomFlatApplication;
}
}
@@ -0,0 +1,10 @@
import { type FlatPermissionFlag } from 'src/engine/metadata-modules/flat-permission-flag/types/flat-permission-flag.type';
import { type PermissionFlagDTO } from 'src/engine/metadata-modules/permission-flag/dtos/permission-flag.dto';
export const fromFlatPermissionFlagToPermissionFlagDto = (
flatPermissionFlag: FlatPermissionFlag,
): PermissionFlagDTO => ({
id: flatPermissionFlag.id,
roleId: flatPermissionFlag.roleId,
flag: flatPermissionFlag.flag,
});
@@ -32,6 +32,7 @@ export const WORKSPACE_CACHE_KEYS_V2 = {
flatApplicationMaps: 'flat-maps:flatApplicationMaps',
flatRoleMaps: 'flat-maps:role',
flatRoleTargetMaps: 'flat-maps:role-target',
flatPermissionFlagMaps: 'flat-maps:permission-flag',
ORMEntityMetadatas: 'orm:entity-metadatas',
flatAgentMaps: 'flat-maps:agent',
flatSkillMaps: 'flat-maps:skill',
@@ -73,8 +73,10 @@ export const createStandardRoleFlatMetadata = ({
createdAt: now,
updatedAt: now,
permissionFlagIds: [],
permissionFlagUniversalIdentifiers: [],
fieldPermissionIds: [],
objectPermissionIds: [],
objectPermissionUniversalIdentifiers: [],
roleTargetIds: [],
roleTargetUniversalIdentifiers: [],
rowLevelPermissionPredicateIds: [],
@@ -167,6 +167,7 @@ export const enrichCreateWorkspaceMigrationActionsWithIds = ({
case 'navigationMenuItem':
case 'frontComponent':
case 'viewSort':
case 'permissionFlag':
case 'webhook': {
if (!isDefined(idByUniversalIdentifier)) {
return action;
@@ -24,6 +24,7 @@ import { WorkspaceMigrationObjectActionsBuilderService } from 'src/engine/worksp
import { WorkspaceMigrationPageLayoutTabActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/page-layout-tab/workspace-migration-page-layout-tab-actions-builder.service';
import { WorkspaceMigrationPageLayoutWidgetActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/page-layout-widget/workspace-migration-page-layout-widget-actions-builder.service';
import { WorkspaceMigrationPageLayoutActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/page-layout/workspace-migration-page-layout-actions-builder.service';
import { WorkspaceMigrationPermissionFlagActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/permission-flag/workspace-migration-permission-flag-actions-builder.service';
import { WorkspaceMigrationRoleTargetActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/role-target/workspace-migration-role-target-actions-builder.service';
import { WorkspaceMigrationRoleActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/role/workspace-migration-role-actions-builder.service';
import { WorkspaceMigrationRowLevelPermissionPredicateGroupActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/row-level-permission-predicate-group/workspace-migration-row-level-permission-predicate-group-actions-builder.service';
@@ -34,9 +35,9 @@ import { WorkspaceMigrationViewFieldActionsBuilderService } from 'src/engine/wor
import { WorkspaceMigrationViewFilterGroupActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/view-filter-group/workspace-migration-view-filter-group-actions-builder.service';
import { WorkspaceMigrationViewFilterActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/view-filter/workspace-migration-view-filter-actions-builder.service';
import { WorkspaceMigrationViewGroupActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/view-group/workspace-migration-view-group-actions-builder.service';
import { WorkspaceMigrationViewSortActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/view-sort/workspace-migration-view-sort-actions.builder.service';
import { WorkspaceMigrationViewActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/view/workspace-migration-view-actions-builder.service';
import { WorkspaceMigrationWebhookActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/webhook/workspace-migration-webhook-actions-builder.service';
import { WorkspaceMigrationViewSortActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/view-sort/workspace-migration-view-sort-actions.builder.service';
@Injectable()
export class WorkspaceMigrationBuildOrchestratorService {
@@ -50,6 +51,7 @@ export class WorkspaceMigrationBuildOrchestratorService {
private readonly workspaceMigrationViewGroupActionsBuilderService: WorkspaceMigrationViewGroupActionsBuilderService,
private readonly workspaceMigrationViewFieldGroupActionsBuilderService: WorkspaceMigrationViewFieldGroupActionsBuilderService,
private readonly workspaceMigrationViewSortActionsBuilderService: WorkspaceMigrationViewSortActionsBuilderService,
private readonly workspaceMigrationPermissionFlagActionsBuilderService: WorkspaceMigrationPermissionFlagActionsBuilderService,
private readonly workspaceMigrationLogicFunctionActionsBuilderService: WorkspaceMigrationLogicFunctionActionsBuilderService,
private readonly workspaceMigrationRoleTargetActionsBuilderService: WorkspaceMigrationRoleTargetActionsBuilderService,
private readonly workspaceMigrationFieldActionsBuilderService: WorkspaceMigrationFieldActionsBuilderService,
@@ -140,6 +142,7 @@ export class WorkspaceMigrationBuildOrchestratorService {
flatRowLevelPermissionPredicateMaps,
flatRowLevelPermissionPredicateGroupMaps,
flatRoleMaps,
flatPermissionFlagMaps,
flatRoleTargetMaps,
flatAgentMaps,
flatSkillMaps,
@@ -481,6 +484,31 @@ export class WorkspaceMigrationBuildOrchestratorService {
}
}
if (isDefined(flatPermissionFlagMaps)) {
const { from: fromFlatPermissionFlagMaps, to: toFlatPermissionFlagMaps } =
flatPermissionFlagMaps;
const permissionFlagResult =
await this.workspaceMigrationPermissionFlagActionsBuilderService.validateAndBuild(
{
additionalCacheDataMaps,
from: fromFlatPermissionFlagMaps,
to: toFlatPermissionFlagMaps,
buildOptions,
dependencyOptimisticFlatEntityMaps: optimisticAllFlatEntityMaps,
workspaceId,
},
);
if (permissionFlagResult.status === 'fail') {
orchestratorFailureReport.permissionFlag.push(
...permissionFlagResult.errors,
);
} else {
orchestratorActionsReport.permissionFlag = permissionFlagResult.actions;
}
}
if (isDefined(flatRoleTargetMaps)) {
const { from: fromFlatRoleTargetMaps, to: toFlatRoleTargetMaps } =
flatRoleTargetMaps;
@@ -811,6 +839,12 @@ export class WorkspaceMigrationBuildOrchestratorService {
...aggregatedOrchestratorActionsReport.roleTarget.update,
///
// Permission flags
...aggregatedOrchestratorActionsReport.permissionFlag.delete,
...aggregatedOrchestratorActionsReport.permissionFlag.create,
...aggregatedOrchestratorActionsReport.permissionFlag.update,
///
// Agents
...aggregatedOrchestratorActionsReport.agent.delete,
...aggregatedOrchestratorActionsReport.agent.create,
@@ -28,8 +28,10 @@ exports[`ALL_UNIVERSAL_FLAT_ENTITY_FOREIGN_KEY_AGGREGATOR_PROPERTIES should matc
"widgetUniversalIdentifiers",
],
"pageLayoutWidget": [],
"permissionFlag": [],
"role": [
"roleTargetUniversalIdentifiers",
"permissionFlagUniversalIdentifiers",
"rowLevelPermissionPredicateUniversalIdentifiers",
"rowLevelPermissionPredicateGroupUniversalIdentifiers",
],
@@ -35,6 +35,8 @@ export const ALL_JSONB_PROPERTIES_WITH_SERIALIZED_RELATION_BY_METADATA_NAME = {
},
commandMenuItem: {},
navigationMenuItem: {},
objectPermission: {},
permissionFlag: {},
frontComponent: {},
} as const satisfies {
[P in AllMetadataName]: Partial<{
@@ -0,0 +1,7 @@
import { type ObjectPermissionEntity } from 'src/engine/metadata-modules/object-permission/object-permission.entity';
import { type UniversalFlatEntityFrom } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-entity-from.type';
export type UniversalFlatObjectPermission = UniversalFlatEntityFrom<
ObjectPermissionEntity,
'objectPermission'
>;
@@ -0,0 +1,7 @@
import { type PermissionFlagEntity } from 'src/engine/metadata-modules/permission-flag/permission-flag.entity';
import { type UniversalFlatEntityFrom } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-entity-from.type';
export type UniversalFlatPermissionFlag = UniversalFlatEntityFrom<
PermissionFlagEntity,
'permissionFlag'
>;
@@ -0,0 +1,24 @@
import { type BaseFlatCreateWorkspaceMigrationAction } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/types/base-flat-create-workspace-migration-action.type';
import { type BaseFlatDeleteWorkspaceMigrationAction } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/types/base-flat-delete-workspace-migration-action.type';
import { type BaseFlatUpdateWorkspaceMigrationAction } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/types/base-flat-update-workspace-migration-action.type';
import { type BaseUniversalCreateWorkspaceMigrationAction } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/types/base-universal-create-workspace-migration-action.type';
import { type BaseUniversalDeleteWorkspaceMigrationAction } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/types/base-universal-delete-workspace-migration-action.type';
import { type BaseUniversalUpdateWorkspaceMigrationAction } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/types/base-universal-update-workspace-migration-action.type';
export type FlatCreateObjectPermissionAction =
BaseFlatCreateWorkspaceMigrationAction<'objectPermission'>;
export type FlatUpdateObjectPermissionAction =
BaseFlatUpdateWorkspaceMigrationAction<'objectPermission'>;
export type FlatDeleteObjectPermissionAction =
BaseFlatDeleteWorkspaceMigrationAction<'objectPermission'>;
export type UniversalCreateObjectPermissionAction =
BaseUniversalCreateWorkspaceMigrationAction<'objectPermission'>;
export type UniversalUpdateObjectPermissionAction =
BaseUniversalUpdateWorkspaceMigrationAction<'objectPermission'>;
export type UniversalDeleteObjectPermissionAction =
BaseUniversalDeleteWorkspaceMigrationAction<'objectPermission'>;
@@ -0,0 +1,24 @@
import { type BaseFlatCreateWorkspaceMigrationAction } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/types/base-flat-create-workspace-migration-action.type';
import { type BaseFlatDeleteWorkspaceMigrationAction } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/types/base-flat-delete-workspace-migration-action.type';
import { type BaseFlatUpdateWorkspaceMigrationAction } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/types/base-flat-update-workspace-migration-action.type';
import { type BaseUniversalCreateWorkspaceMigrationAction } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/types/base-universal-create-workspace-migration-action.type';
import { type BaseUniversalDeleteWorkspaceMigrationAction } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/types/base-universal-delete-workspace-migration-action.type';
import { type BaseUniversalUpdateWorkspaceMigrationAction } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/types/base-universal-update-workspace-migration-action.type';
export type FlatCreatePermissionFlagAction =
BaseFlatCreateWorkspaceMigrationAction<'permissionFlag'>;
export type FlatUpdatePermissionFlagAction =
BaseFlatUpdateWorkspaceMigrationAction<'permissionFlag'>;
export type FlatDeletePermissionFlagAction =
BaseFlatDeleteWorkspaceMigrationAction<'permissionFlag'>;
export type UniversalCreatePermissionFlagAction =
BaseUniversalCreateWorkspaceMigrationAction<'permissionFlag'>;
export type UniversalUpdatePermissionFlagAction =
BaseUniversalUpdateWorkspaceMigrationAction<'permissionFlag'>;
export type UniversalDeletePermissionFlagAction =
BaseUniversalDeleteWorkspaceMigrationAction<'permissionFlag'>;
@@ -0,0 +1,120 @@
import { Injectable } from '@nestjs/common';
import { ALL_METADATA_NAME } from 'twenty-shared/metadata';
import { WorkspaceEntityMigrationBuilderService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/services/workspace-entity-migration-builder.service';
import { UniversalUpdatePermissionFlagAction } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/permission-flag/types/workspace-migration-permission-flag-action.type';
import { FlatPermissionFlagValidatorService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/validators/services/flat-permission-flag-validator.service';
import { UniversalFlatEntityValidationArgs } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/types/universal-flat-entity-validation-args.type';
import { UniversalFlatEntityValidationReturnType } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/types/universal-flat-entity-validation-result.type';
import { FlatEntityUpdateValidationArgs } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/types/universal-flat-entity-update-validation-args.type';
@Injectable()
export class WorkspaceMigrationPermissionFlagActionsBuilderService extends WorkspaceEntityMigrationBuilderService<
typeof ALL_METADATA_NAME.permissionFlag
> {
constructor(
private readonly flatPermissionFlagValidatorService: FlatPermissionFlagValidatorService,
) {
super(ALL_METADATA_NAME.permissionFlag);
}
protected validateFlatEntityCreation(
args: UniversalFlatEntityValidationArgs<
typeof ALL_METADATA_NAME.permissionFlag
>,
): UniversalFlatEntityValidationReturnType<
typeof ALL_METADATA_NAME.permissionFlag,
'create'
> {
const validationResult =
this.flatPermissionFlagValidatorService.validateFlatPermissionFlagCreation(
args,
);
if (validationResult.errors.length > 0) {
return {
status: 'fail',
...validationResult,
};
}
const { flatEntityToValidate: flatPermissionFlagToValidate } = args;
return {
status: 'success',
action: {
type: 'create',
metadataName: 'permissionFlag',
flatEntity: flatPermissionFlagToValidate,
},
};
}
protected validateFlatEntityDeletion(
args: UniversalFlatEntityValidationArgs<
typeof ALL_METADATA_NAME.permissionFlag
>,
): UniversalFlatEntityValidationReturnType<
typeof ALL_METADATA_NAME.permissionFlag,
'delete'
> {
const validationResult =
this.flatPermissionFlagValidatorService.validateFlatPermissionFlagDeletion(
args,
);
if (validationResult.errors.length > 0) {
return {
status: 'fail',
...validationResult,
};
}
const { flatEntityToValidate: flatPermissionFlagToValidate } = args;
return {
status: 'success',
action: {
type: 'delete',
metadataName: 'permissionFlag',
universalIdentifier: flatPermissionFlagToValidate.universalIdentifier,
},
};
}
protected validateFlatEntityUpdate(
args: FlatEntityUpdateValidationArgs<
typeof ALL_METADATA_NAME.permissionFlag
>,
): UniversalFlatEntityValidationReturnType<
typeof ALL_METADATA_NAME.permissionFlag,
'update'
> {
const validationResult =
this.flatPermissionFlagValidatorService.validateFlatPermissionFlagUpdate(
args,
);
if (validationResult.errors.length > 0) {
return {
status: 'fail',
...validationResult,
};
}
const { universalIdentifier, flatEntityUpdate } = args;
const updatePermissionFlagAction: UniversalUpdatePermissionFlagAction = {
type: 'update',
metadataName: 'permissionFlag',
universalIdentifier,
update: flatEntityUpdate,
};
return {
status: 'success',
action: updatePermissionFlagAction,
};
}
}
@@ -0,0 +1,219 @@
import { Injectable } from '@nestjs/common';
import { msg, t } from '@lingui/core/macro';
import { PermissionFlagType } from 'twenty-shared/constants';
import { ALL_METADATA_NAME } from 'twenty-shared/metadata';
import { isDefined } from 'twenty-shared/utils';
import { findFlatEntityByUniversalIdentifier } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-universal-identifier.util';
import { PermissionsExceptionCode } from 'src/engine/metadata-modules/permissions/permissions.exception';
import { FailedFlatEntityValidation } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/types/failed-flat-entity-validation.type';
import { getEmptyFlatEntityValidationError } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/utils/get-flat-entity-validation-error.util';
import { FlatEntityUpdateValidationArgs } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/types/universal-flat-entity-update-validation-args.type';
import { UniversalFlatEntityValidationArgs } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/types/universal-flat-entity-validation-args.type';
@Injectable()
export class FlatPermissionFlagValidatorService {
validateFlatPermissionFlagCreation({
flatEntityToValidate: flatPermissionFlagToValidate,
optimisticFlatEntityMapsAndRelatedFlatEntityMaps: {
flatPermissionFlagMaps: optimisticFlatPermissionFlagMaps,
flatRoleMaps,
},
}: UniversalFlatEntityValidationArgs<
typeof ALL_METADATA_NAME.permissionFlag
>): FailedFlatEntityValidation<'permissionFlag', 'create'> {
const validationResult = getEmptyFlatEntityValidationError({
flatEntityMinimalInformation: {
universalIdentifier: flatPermissionFlagToValidate.universalIdentifier,
roleUniversalIdentifier:
flatPermissionFlagToValidate.roleUniversalIdentifier,
},
metadataName: 'permissionFlag',
type: 'create',
});
const existingByUniversalId = findFlatEntityByUniversalIdentifier({
universalIdentifier: flatPermissionFlagToValidate.universalIdentifier,
flatEntityMaps: optimisticFlatPermissionFlagMaps,
});
if (isDefined(existingByUniversalId)) {
validationResult.errors.push({
code: PermissionsExceptionCode.INVALID_SETTING,
message: t`Permission flag with universal identifier ${flatPermissionFlagToValidate.universalIdentifier} already exists`,
userFriendlyMessage: msg`Permission flag already exists`,
});
}
const referencedRole = findFlatEntityByUniversalIdentifier({
universalIdentifier: flatPermissionFlagToValidate.roleUniversalIdentifier,
flatEntityMaps: flatRoleMaps,
});
if (!isDefined(referencedRole)) {
validationResult.errors.push({
code: PermissionsExceptionCode.ROLE_NOT_FOUND,
message: t`Role not found`,
userFriendlyMessage: msg`Role not found`,
});
}
const isValidFlag =
isDefined(flatPermissionFlagToValidate.flag) &&
Object.values(PermissionFlagType).includes(
flatPermissionFlagToValidate.flag,
);
if (!isValidFlag) {
validationResult.errors.push({
code: PermissionsExceptionCode.INVALID_SETTING,
message: t`Invalid permission flag value`,
userFriendlyMessage: msg`Invalid permission setting`,
});
}
const duplicateForSameRole = Object.values(
optimisticFlatPermissionFlagMaps.byUniversalIdentifier,
).filter(
(pf) =>
isDefined(pf) &&
pf.roleUniversalIdentifier ===
flatPermissionFlagToValidate.roleUniversalIdentifier &&
pf.flag === flatPermissionFlagToValidate.flag &&
pf.universalIdentifier !==
flatPermissionFlagToValidate.universalIdentifier,
);
if (duplicateForSameRole.length > 0) {
validationResult.errors.push({
code: PermissionsExceptionCode.INVALID_SETTING,
message: t`Permission flag for this role and setting already exists`,
userFriendlyMessage: msg`This permission is already set for the role`,
});
}
return validationResult;
}
validateFlatPermissionFlagUpdate({
universalIdentifier,
flatEntityUpdate,
optimisticFlatEntityMapsAndRelatedFlatEntityMaps: {
flatPermissionFlagMaps: optimisticFlatPermissionFlagMaps,
flatRoleMaps,
},
}: FlatEntityUpdateValidationArgs<
typeof ALL_METADATA_NAME.permissionFlag
>): FailedFlatEntityValidation<'permissionFlag', 'update'> {
const existingFlatPermissionFlag = findFlatEntityByUniversalIdentifier({
universalIdentifier,
flatEntityMaps: optimisticFlatPermissionFlagMaps,
});
const validationResult = getEmptyFlatEntityValidationError({
flatEntityMinimalInformation: {
universalIdentifier,
},
metadataName: 'permissionFlag',
type: 'update',
});
if (!isDefined(existingFlatPermissionFlag)) {
validationResult.errors.push({
code: PermissionsExceptionCode.PERMISSION_NOT_FOUND,
message: t`Permission flag to update not found`,
userFriendlyMessage: msg`Permission flag not found`,
});
return validationResult;
}
const updatedFlatPermissionFlag = {
...existingFlatPermissionFlag,
...flatEntityUpdate,
};
if (isDefined(flatEntityUpdate.roleUniversalIdentifier)) {
const referencedRole = findFlatEntityByUniversalIdentifier({
universalIdentifier: updatedFlatPermissionFlag.roleUniversalIdentifier,
flatEntityMaps: flatRoleMaps,
});
if (!isDefined(referencedRole)) {
validationResult.errors.push({
code: PermissionsExceptionCode.ROLE_NOT_FOUND,
message: t`Role not found`,
userFriendlyMessage: msg`Role not found`,
});
}
}
if (isDefined(flatEntityUpdate.flag)) {
const isValidFlag = Object.values(PermissionFlagType).includes(
flatEntityUpdate.flag as PermissionFlagType,
);
if (!isValidFlag) {
validationResult.errors.push({
code: PermissionsExceptionCode.INVALID_SETTING,
message: t`Invalid permission flag value`,
userFriendlyMessage: msg`Invalid permission setting`,
});
}
}
const duplicateForSameRole = Object.values(
optimisticFlatPermissionFlagMaps.byUniversalIdentifier,
).filter(
(pf) =>
isDefined(pf) &&
pf.roleUniversalIdentifier ===
updatedFlatPermissionFlag.roleUniversalIdentifier &&
pf.flag === updatedFlatPermissionFlag.flag &&
pf.universalIdentifier !== universalIdentifier,
);
if (duplicateForSameRole.length > 0) {
validationResult.errors.push({
code: PermissionsExceptionCode.INVALID_SETTING,
message: t`Permission flag for this role and setting already exists`,
userFriendlyMessage: msg`This permission is already set for the role`,
});
}
return validationResult;
}
validateFlatPermissionFlagDeletion({
flatEntityToValidate: { universalIdentifier },
optimisticFlatEntityMapsAndRelatedFlatEntityMaps: {
flatPermissionFlagMaps: optimisticFlatPermissionFlagMaps,
},
}: UniversalFlatEntityValidationArgs<
typeof ALL_METADATA_NAME.permissionFlag
>): FailedFlatEntityValidation<'permissionFlag', 'delete'> {
const validationResult = getEmptyFlatEntityValidationError({
flatEntityMinimalInformation: {
universalIdentifier,
},
metadataName: 'permissionFlag',
type: 'delete',
});
const existingFlatPermissionFlag = findFlatEntityByUniversalIdentifier({
universalIdentifier,
flatEntityMaps: optimisticFlatPermissionFlagMaps,
});
if (!isDefined(existingFlatPermissionFlag)) {
validationResult.errors.push({
code: PermissionsExceptionCode.PERMISSION_NOT_FOUND,
message: t`Permission flag to delete not found`,
userFriendlyMessage: msg`Permission flag not found`,
});
}
return validationResult;
}
}
@@ -8,25 +8,26 @@ import { FlatCommandMenuItemValidatorService } from 'src/engine/workspace-manage
import { FlatFieldMetadataValidatorService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/validators/services/flat-field-metadata-validator.service';
import { FlatFrontComponentValidatorService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/validators/services/flat-front-component-validator.service';
import { FlatIndexValidatorService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/validators/services/flat-index-metadata-validator.service';
import { FlatLogicFunctionValidatorService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/validators/services/flat-logic-function-validator.service';
import { FlatNavigationMenuItemValidatorService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/validators/services/flat-navigation-menu-item-validator.service';
import { FlatObjectMetadataValidatorService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/validators/services/flat-object-metadata-validator.service';
import { FlatPageLayoutTabValidatorService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/validators/services/flat-page-layout-tab-validator.service';
import { FlatPageLayoutValidatorService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/validators/services/flat-page-layout-validator.service';
import { FlatPageLayoutWidgetValidatorService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/validators/services/flat-page-layout-widget-validator.service';
import { FlatPermissionFlagValidatorService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/validators/services/flat-permission-flag-validator.service';
import { FlatRoleTargetValidatorService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/validators/services/flat-role-target-validator.service';
import { FlatRoleValidatorService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/validators/services/flat-role-validator.service';
import { FlatRowLevelPermissionPredicateGroupValidatorService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/validators/services/flat-row-level-permission-predicate-group-validator.service';
import { FlatRowLevelPermissionPredicateValidatorService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/validators/services/flat-row-level-permission-predicate-validator.service';
import { FlatLogicFunctionValidatorService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/validators/services/flat-logic-function-validator.service';
import { FlatSkillValidatorService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/validators/services/flat-skill-validator.service';
import { FlatViewFieldGroupValidatorService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/validators/services/flat-view-field-group-validator.service';
import { FlatViewFieldValidatorService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/validators/services/flat-view-field-validator.service';
import { FlatViewFilterGroupValidatorService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/validators/services/flat-view-filter-group-validator.service';
import { FlatViewFilterValidatorService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/validators/services/flat-view-filter-validator.service';
import { FlatViewGroupValidatorService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/validators/services/flat-view-group-validator.service';
import { FlatViewSortValidatorService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/validators/services/flat-view-sort-validator.service';
import { FlatViewValidatorService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/validators/services/flat-view-validator.service';
import { FlatWebhookValidatorService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/validators/services/flat-webhook-validator.service';
import { FlatViewSortValidatorService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/validators/services/flat-view-sort-validator.service';
@Module({
imports: [FeatureFlagModule],
@@ -38,6 +39,7 @@ import { FlatViewSortValidatorService } from 'src/engine/workspace-manager/works
FlatViewGroupValidatorService,
FlatViewFieldGroupValidatorService,
FlatViewSortValidatorService,
FlatPermissionFlagValidatorService,
FlatIndexValidatorService,
FlatFieldMetadataValidatorService,
FlatObjectMetadataValidatorService,
@@ -66,6 +68,7 @@ import { FlatViewSortValidatorService } from 'src/engine/workspace-manager/works
FlatViewGroupValidatorService,
FlatViewFieldGroupValidatorService,
FlatViewSortValidatorService,
FlatPermissionFlagValidatorService,
FlatIndexValidatorService,
FlatFieldMetadataValidatorService,
FlatObjectMetadataValidatorService,
@@ -4,29 +4,30 @@ import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-
import { FlatFieldMetadataTypeValidatorService } from 'src/engine/metadata-modules/flat-field-metadata/services/flat-field-metadata-type-validator.service';
import { WorkspaceMigrationAgentActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/agent/workspace-migration-agent-actions-builder.service';
import { WorkspaceMigrationCommandMenuItemActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/command-menu-item/workspace-migration-command-menu-item-actions-builder.service';
import { WorkspaceMigrationNavigationMenuItemActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/navigation-menu-item/workspace-migration-navigation-menu-item-actions-builder.service';
import { WorkspaceMigrationFieldActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/field/workspace-migration-field-actions-builder.service';
import { WorkspaceMigrationFrontComponentActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/front-component/workspace-migration-front-component-actions-builder.service';
import { WorkspaceMigrationIndexActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/index/workspace-migration-index-actions-builder.service';
import { WorkspaceMigrationLogicFunctionActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/logic-function/workspace-migration-logic-function-actions-builder.service';
import { WorkspaceMigrationNavigationMenuItemActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/navigation-menu-item/workspace-migration-navigation-menu-item-actions-builder.service';
import { WorkspaceMigrationObjectActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/object/workspace-migration-object-actions-builder.service';
import { WorkspaceMigrationPageLayoutTabActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/page-layout-tab/workspace-migration-page-layout-tab-actions-builder.service';
import { WorkspaceMigrationPageLayoutWidgetActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/page-layout-widget/workspace-migration-page-layout-widget-actions-builder.service';
import { WorkspaceMigrationPageLayoutActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/page-layout/workspace-migration-page-layout-actions-builder.service';
import { WorkspaceMigrationPermissionFlagActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/permission-flag/workspace-migration-permission-flag-actions-builder.service';
import { WorkspaceMigrationRoleTargetActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/role-target/workspace-migration-role-target-actions-builder.service';
import { WorkspaceMigrationRoleActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/role/workspace-migration-role-actions-builder.service';
import { WorkspaceMigrationRowLevelPermissionPredicateGroupActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/row-level-permission-predicate-group/workspace-migration-row-level-permission-predicate-group-actions-builder.service';
import { WorkspaceMigrationRowLevelPermissionPredicateActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/row-level-permission-predicate/workspace-migration-row-level-permission-predicate-actions-builder.service';
import { WorkspaceMigrationLogicFunctionActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/logic-function/workspace-migration-logic-function-actions-builder.service';
import { WorkspaceMigrationSkillActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/skill/workspace-migration-skill-actions-builder.service';
import { WorkspaceMigrationFrontComponentActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/front-component/workspace-migration-front-component-actions-builder.service';
import { WorkspaceMigrationViewFieldActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/view-field/workspace-migration-view-field-actions-builder.service';
import { WorkspaceMigrationViewFieldGroupActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/view-field-group/workspace-migration-view-field-group-actions-builder.service';
import { WorkspaceMigrationWebhookActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/webhook/workspace-migration-webhook-actions-builder.service';
import { WorkspaceMigrationViewFieldActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/view-field/workspace-migration-view-field-actions-builder.service';
import { WorkspaceMigrationViewFilterGroupActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/view-filter-group/workspace-migration-view-filter-group-actions-builder.service';
import { WorkspaceMigrationViewFilterActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/view-filter/workspace-migration-view-filter-actions-builder.service';
import { WorkspaceMigrationViewGroupActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/view-group/workspace-migration-view-group-actions-builder.service';
import { WorkspaceMigrationViewActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/view/workspace-migration-view-actions-builder.service';
import { WorkspaceMigrationBuilderValidatorsModule } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/validators/workspace-migration-builder-validators.module';
import { WorkspaceMigrationViewSortActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/view-sort/workspace-migration-view-sort-actions.builder.service';
import { WorkspaceMigrationViewActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/view/workspace-migration-view-actions-builder.service';
import { WorkspaceMigrationWebhookActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/webhook/workspace-migration-webhook-actions-builder.service';
import { WorkspaceMigrationBuilderValidatorsModule } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/validators/workspace-migration-builder-validators.module';
@Module({
imports: [FeatureFlagModule, WorkspaceMigrationBuilderValidatorsModule],
@@ -40,6 +41,7 @@ import { WorkspaceMigrationViewSortActionsBuilderService } from 'src/engine/work
WorkspaceMigrationViewGroupActionsBuilderService,
WorkspaceMigrationViewFieldGroupActionsBuilderService,
WorkspaceMigrationViewSortActionsBuilderService,
WorkspaceMigrationPermissionFlagActionsBuilderService,
WorkspaceMigrationIndexActionsBuilderService,
WorkspaceMigrationLogicFunctionActionsBuilderService,
WorkspaceMigrationFieldActionsBuilderService,
@@ -67,6 +69,7 @@ import { WorkspaceMigrationViewSortActionsBuilderService } from 'src/engine/work
WorkspaceMigrationViewGroupActionsBuilderService,
WorkspaceMigrationViewFieldGroupActionsBuilderService,
WorkspaceMigrationViewSortActionsBuilderService,
WorkspaceMigrationPermissionFlagActionsBuilderService,
WorkspaceMigrationFieldActionsBuilderService,
WorkspaceMigrationLogicFunctionActionsBuilderService,
WorkspaceMigrationRoleActionsBuilderService,
@@ -0,0 +1,62 @@
import { Injectable } from '@nestjs/common';
import { v4 } from 'uuid';
import { WorkspaceMigrationRunnerActionHandler } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/interfaces/workspace-migration-runner-action-handler-service.interface';
import { resolveUniversalRelationIdentifiersToIds } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/utils/resolve-universal-relation-identifiers-to-ids.util';
import {
FlatCreatePermissionFlagAction,
UniversalCreatePermissionFlagAction,
} from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/permission-flag/types/workspace-migration-permission-flag-action.type';
import {
WorkspaceMigrationActionRunnerArgs,
WorkspaceMigrationActionRunnerContext,
} from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/types/workspace-migration-action-runner-args.type';
@Injectable()
export class CreatePermissionFlagActionHandlerService extends WorkspaceMigrationRunnerActionHandler(
'create',
'permissionFlag',
) {
override async transpileUniversalActionToFlatAction({
action,
allFlatEntityMaps,
flatApplication,
workspaceId,
}: WorkspaceMigrationActionRunnerArgs<UniversalCreatePermissionFlagAction>): Promise<FlatCreatePermissionFlagAction> {
const { roleId } = resolveUniversalRelationIdentifiersToIds({
flatEntityMaps: allFlatEntityMaps,
metadataName: action.metadataName,
universalForeignKeyValues: action.flatEntity,
});
return {
...action,
flatEntity: {
...action.flatEntity,
roleId,
applicationId: flatApplication.id,
id: action.id ?? v4(),
workspaceId,
},
};
}
async executeForMetadata(
context: WorkspaceMigrationActionRunnerContext<FlatCreatePermissionFlagAction>,
): Promise<void> {
const { flatAction, queryRunner } = context;
const { flatEntity } = flatAction;
await this.insertFlatEntitiesInRepository({
queryRunner,
flatEntities: [flatEntity],
});
}
async executeForWorkspaceSchema(
_context: WorkspaceMigrationActionRunnerContext<FlatCreatePermissionFlagAction>,
): Promise<void> {
return;
}
}
@@ -0,0 +1,50 @@
import { Injectable } from '@nestjs/common';
import { WorkspaceMigrationRunnerActionHandler } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/interfaces/workspace-migration-runner-action-handler-service.interface';
import { PermissionFlagEntity } from 'src/engine/metadata-modules/permission-flag/permission-flag.entity';
import {
FlatDeletePermissionFlagAction,
UniversalDeletePermissionFlagAction,
} from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/permission-flag/types/workspace-migration-permission-flag-action.type';
import {
WorkspaceMigrationActionRunnerArgs,
WorkspaceMigrationActionRunnerContext,
} from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/types/workspace-migration-action-runner-args.type';
@Injectable()
export class DeletePermissionFlagActionHandlerService extends WorkspaceMigrationRunnerActionHandler(
'delete',
'permissionFlag',
) {
constructor() {
super();
}
override async transpileUniversalActionToFlatAction(
context: WorkspaceMigrationActionRunnerArgs<UniversalDeletePermissionFlagAction>,
): Promise<FlatDeletePermissionFlagAction> {
return this.transpileUniversalDeleteActionToFlatDeleteAction(context);
}
async executeForMetadata(
context: WorkspaceMigrationActionRunnerContext<FlatDeletePermissionFlagAction>,
): Promise<void> {
const { flatAction, queryRunner, workspaceId } = context;
const permissionFlagRepository =
queryRunner.manager.getRepository<PermissionFlagEntity>(
PermissionFlagEntity,
);
await permissionFlagRepository.delete({
id: flatAction.entityId,
workspaceId,
});
}
async executeForWorkspaceSchema(
_context: WorkspaceMigrationActionRunnerContext<FlatDeletePermissionFlagAction>,
): Promise<void> {
return;
}
}
@@ -0,0 +1,71 @@
import { Injectable } from '@nestjs/common';
import { WorkspaceMigrationRunnerActionHandler } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/interfaces/workspace-migration-runner-action-handler-service.interface';
import { findFlatEntityByUniversalIdentifierOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-universal-identifier-or-throw.util';
import { PermissionFlagEntity } from 'src/engine/metadata-modules/permission-flag/permission-flag.entity';
import { resolveUniversalUpdateRelationIdentifiersToIds } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/utils/resolve-universal-update-relation-identifiers-to-ids.util';
import {
FlatUpdatePermissionFlagAction,
UniversalUpdatePermissionFlagAction,
} from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/permission-flag/types/workspace-migration-permission-flag-action.type';
import {
WorkspaceMigrationActionRunnerArgs,
WorkspaceMigrationActionRunnerContext,
} from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/types/workspace-migration-action-runner-args.type';
@Injectable()
export class UpdatePermissionFlagActionHandlerService extends WorkspaceMigrationRunnerActionHandler(
'update',
'permissionFlag',
) {
constructor() {
super();
}
override async transpileUniversalActionToFlatAction(
context: WorkspaceMigrationActionRunnerArgs<UniversalUpdatePermissionFlagAction>,
): Promise<FlatUpdatePermissionFlagAction> {
const { action, allFlatEntityMaps } = context;
const flatPermissionFlag = findFlatEntityByUniversalIdentifierOrThrow({
flatEntityMaps: allFlatEntityMaps.flatPermissionFlagMaps,
universalIdentifier: action.universalIdentifier,
});
const update = resolveUniversalUpdateRelationIdentifiersToIds({
metadataName: 'permissionFlag',
universalUpdate: action.update,
allFlatEntityMaps,
});
return {
type: 'update',
metadataName: 'permissionFlag',
entityId: flatPermissionFlag.id,
update,
};
}
async executeForMetadata(
context: WorkspaceMigrationActionRunnerContext<FlatUpdatePermissionFlagAction>,
): Promise<void> {
const { flatAction, queryRunner, workspaceId } = context;
const { entityId, update } = flatAction;
const permissionFlagRepository =
queryRunner.manager.getRepository<PermissionFlagEntity>(
PermissionFlagEntity,
);
await permissionFlagRepository.update(
{ id: entityId, workspaceId },
update,
);
}
async executeForWorkspaceSchema(
_context: WorkspaceMigrationActionRunnerContext<FlatUpdatePermissionFlagAction>,
): Promise<void> {
return;
}
}
@@ -9,15 +9,21 @@ import { UpdateAgentActionHandlerService } from 'src/engine/workspace-manager/wo
import { CreateCommandMenuItemActionHandlerService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/command-menu-item/services/create-command-menu-item-action-handler.service';
import { DeleteCommandMenuItemActionHandlerService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/command-menu-item/services/delete-command-menu-item-action-handler.service';
import { UpdateCommandMenuItemActionHandlerService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/command-menu-item/services/update-command-menu-item-action-handler.service';
import { CreateNavigationMenuItemActionHandlerService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/navigation-menu-item/services/create-navigation-menu-item-action-handler.service';
import { DeleteNavigationMenuItemActionHandlerService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/navigation-menu-item/services/delete-navigation-menu-item-action-handler.service';
import { UpdateNavigationMenuItemActionHandlerService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/navigation-menu-item/services/update-navigation-menu-item-action-handler.service';
import { CreateFieldActionHandlerService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/field/services/create-field-action-handler.service';
import { DeleteFieldActionHandlerService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/field/services/delete-field-action-handler.service';
import { UpdateFieldActionHandlerService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/field/services/update-field-action-handler.service';
import { CreateFrontComponentActionHandlerService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/front-component/services/create-front-component-action-handler.service';
import { DeleteFrontComponentActionHandlerService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/front-component/services/delete-front-component-action-handler.service';
import { UpdateFrontComponentActionHandlerService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/front-component/services/update-front-component-action-handler.service';
import { CreateIndexActionHandlerService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/index/services/create-index-action-handler.service';
import { DeleteIndexActionHandlerService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/index/services/delete-index-action-handler.service';
import { UpdateIndexActionHandlerService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/index/services/update-index-action-handler.service';
import { CreateLogicFunctionActionHandlerService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/logic-function/services/create-logic-function-action-handler.service';
import { DeleteLogicFunctionActionHandlerService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/logic-function/services/delete-logic-function-action-handler.service';
import { UpdateLogicFunctionActionHandlerService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/logic-function/services/update-logic-function-action-handler.service';
import { CreateNavigationMenuItemActionHandlerService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/navigation-menu-item/services/create-navigation-menu-item-action-handler.service';
import { DeleteNavigationMenuItemActionHandlerService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/navigation-menu-item/services/delete-navigation-menu-item-action-handler.service';
import { UpdateNavigationMenuItemActionHandlerService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/navigation-menu-item/services/update-navigation-menu-item-action-handler.service';
import { CreateObjectActionHandlerService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/object/services/create-object-action-handler.service';
import { DeleteObjectActionHandlerService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/object/services/delete-object-action-handler.service';
import { UpdateObjectActionHandlerService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/object/services/update-object-action-handler.service';
@@ -30,6 +36,9 @@ import { UpdatePageLayoutWidgetActionHandlerService } from 'src/engine/workspace
import { CreatePageLayoutActionHandlerService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/page-layout/services/create-page-layout-action-handler.service';
import { DeletePageLayoutActionHandlerService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/page-layout/services/delete-page-layout-action-handler.service';
import { UpdatePageLayoutActionHandlerService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/page-layout/services/update-page-layout-action-handler.service';
import { CreatePermissionFlagActionHandlerService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/permission-flag/services/create-permission-flag-action-handler.service';
import { DeletePermissionFlagActionHandlerService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/permission-flag/services/delete-permission-flag-action-handler.service';
import { UpdatePermissionFlagActionHandlerService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/permission-flag/services/update-permission-flag-action-handler.service';
import { CreateRoleTargetActionHandlerService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/role-target/services/create-role-target-action-handler.service';
import { DeleteRoleTargetActionHandlerService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/role-target/services/delete-role-target-action-handler.service';
import { UpdateRoleTargetActionHandlerService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/role-target/services/update-role-target-action-handler.service';
@@ -42,18 +51,9 @@ import { UpdateRowLevelPermissionPredicateGroupActionHandlerService } from 'src/
import { CreateRowLevelPermissionPredicateActionHandlerService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/row-level-permission-predicate/services/create-row-level-permission-predicate-action-handler.service';
import { DeleteRowLevelPermissionPredicateActionHandlerService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/row-level-permission-predicate/services/delete-row-level-permission-predicate-action-handler.service';
import { UpdateRowLevelPermissionPredicateActionHandlerService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/row-level-permission-predicate/services/update-row-level-permission-predicate-action-handler.service';
import { CreateLogicFunctionActionHandlerService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/logic-function/services/create-logic-function-action-handler.service';
import { DeleteLogicFunctionActionHandlerService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/logic-function/services/delete-logic-function-action-handler.service';
import { UpdateLogicFunctionActionHandlerService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/logic-function/services/update-logic-function-action-handler.service';
import { CreateSkillActionHandlerService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/skill/services/create-skill-action-handler.service';
import { DeleteSkillActionHandlerService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/skill/services/delete-skill-action-handler.service';
import { CreateFrontComponentActionHandlerService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/front-component/services/create-front-component-action-handler.service';
import { UpdateFrontComponentActionHandlerService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/front-component/services/update-front-component-action-handler.service';
import { DeleteFrontComponentActionHandlerService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/front-component/services/delete-front-component-action-handler.service';
import { UpdateSkillActionHandlerService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/skill/services/update-skill-action-handler.service';
import { CreateWebhookActionHandlerService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/webhook/services/create-webhook-action-handler.service';
import { DeleteWebhookActionHandlerService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/webhook/services/delete-webhook-action-handler.service';
import { UpdateWebhookActionHandlerService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/webhook/services/update-webhook-action-handler.service';
import { CreateViewFieldGroupActionHandlerService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/view-field-group/services/create-view-field-group-action-handler.service';
import { DeleteViewFieldGroupActionHandlerService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/view-field-group/services/delete-view-field-group-action-handler.service';
import { UpdateViewFieldGroupActionHandlerService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/view-field-group/services/update-view-field-group-action-handler.service';
@@ -69,12 +69,15 @@ import { UpdateViewFilterActionHandlerService } from 'src/engine/workspace-manag
import { CreateViewGroupActionHandlerService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/view-group/services/create-view-group-action-handler.service';
import { DeleteViewGroupActionHandlerService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/view-group/services/delete-view-group-action-handler.service';
import { UpdateViewGroupActionHandlerService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/view-group/services/update-view-group-action-handler.service';
import { CreateViewSortActionHandlerService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/view-sort/services/create-view-sort-action-handler.service';
import { DeleteViewSortActionHandlerService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/view-sort/services/delete-view-sort-action-handler.service';
import { UpdateViewSortActionHandlerService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/view-sort/services/update-view-sort-action-handler.service';
import { CreateViewActionHandlerService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/view/services/create-view-action-handler.service';
import { DeleteViewActionHandlerService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/view/services/delete-view-action-handler.service';
import { UpdateViewActionHandlerService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/view/services/update-view-action-handler.service';
import { CreateViewSortActionHandlerService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/view-sort/services/create-view-sort-action-handler.service';
import { UpdateViewSortActionHandlerService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/view-sort/services/update-view-sort-action-handler.service';
import { DeleteViewSortActionHandlerService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/view-sort/services/delete-view-sort-action-handler.service';
import { CreateWebhookActionHandlerService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/webhook/services/create-webhook-action-handler.service';
import { DeleteWebhookActionHandlerService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/webhook/services/delete-webhook-action-handler.service';
import { UpdateWebhookActionHandlerService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-runner/action-handlers/webhook/services/update-webhook-action-handler.service';
@Module({
imports: [
@@ -134,6 +137,10 @@ import { DeleteViewSortActionHandlerService } from 'src/engine/workspace-manager
DeleteRoleTargetActionHandlerService,
UpdateRoleTargetActionHandlerService,
CreatePermissionFlagActionHandlerService,
UpdatePermissionFlagActionHandlerService,
DeletePermissionFlagActionHandlerService,
CreateAgentActionHandlerService,
UpdateAgentActionHandlerService,
DeleteAgentActionHandlerService,
@@ -19,6 +19,7 @@ export const METADATA_EVENTS_TO_EMIT = {
pageLayoutTab: true,
commandMenuItem: true,
navigationMenuItem: true,
permissionFlag: true,
rowLevelPermissionPredicate: true,
rowLevelPermissionPredicateGroup: true,
index: true,
@@ -89,6 +89,7 @@ const deriveAllMetadataEventsFromCreateAction = (
case 'commandMenuItem':
case 'frontComponent':
case 'navigationMenuItem':
case 'permissionFlag':
case 'viewSort':
case 'webhook': {
return [
@@ -53,6 +53,7 @@ const deriveAllMetadataEventsFromDeleteAction = ({
case 'commandMenuItem':
case 'frontComponent':
case 'navigationMenuItem':
case 'permissionFlag':
case 'viewSort':
case 'webhook': {
const flatEntityToDelete = findFlatEntityByIdInFlatEntityMapsOrThrow<
@@ -131,6 +131,7 @@ const deriveAllMetadataEventsFromUpdateAction = ({
case 'commandMenuItem':
case 'frontComponent':
case 'navigationMenuItem':
case 'permissionFlag':
case 'viewSort':
case 'webhook': {
const flatEntityMapsKey = getMetadataFlatEntityMapsKey(
@@ -68,6 +68,7 @@ export const optimisticallyApplyCreateActionOnAllFlatEntityMaps = ({
case 'commandMenuItem':
case 'frontComponent':
case 'navigationMenuItem':
case 'permissionFlag':
case 'webhook': {
addFlatEntityToFlatEntityAndRelatedEntityMapsThroughMutationOrThrow({
flatEntity: flatAction.flatEntity,
@@ -40,6 +40,7 @@ export const optimisticallyApplyDeleteActionOnAllFlatEntityMaps = ({
case 'commandMenuItem':
case 'frontComponent':
case 'navigationMenuItem':
case 'permissionFlag':
case 'webhook': {
const flatEntityToDelete = findFlatEntityByIdInFlatEntityMapsOrThrow<
MetadataFlatEntity<typeof flatAction.metadataName>
@@ -61,6 +61,7 @@ export const optimisticallyApplyUpdateActionOnAllFlatEntityMaps = ({
case 'commandMenuItem':
case 'frontComponent':
case 'navigationMenuItem':
case 'permissionFlag':
case 'webhook': {
const flatEntityMapsKey = getMetadataFlatEntityMapsKey(
flatAction.metadataName,
@@ -0,0 +1,54 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`Permission flag upsert should fail when permissionFlagKeys contains invalid enum value 1`] = `
{
"extensions": {
"code": "BAD_USER_INPUT",
"http": {
"status": 400,
},
"userFriendlyMessage": "An error occurred.",
},
"message": "Value \"INVALID_FLAG\" does not exist in \"PermissionFlagType\" enum.",
"name": "GraphQLError",
}
`;
exports[`Permission flag upsert should fail when role is not editable (system role) 1`] = `
{
"extensions": {
"code": "FORBIDDEN",
"subCode": "ROLE_NOT_EDITABLE",
"userFriendlyMessage": "This role cannot be modified because it is a system role. Only custom roles can be edited.",
},
"message": "Role is not editable",
"name": "ForbiddenError",
}
`;
exports[`Permission flag upsert should fail when roleId does not exist 1`] = `
{
"extensions": {
"code": "NOT_FOUND",
"subCode": "ROLE_NOT_FOUND",
"userFriendlyMessage": "The role you are trying to modify could not be found.",
},
"message": "Role not found",
"name": "NotFoundError",
}
`;
exports[`Permission flag upsert should fail when roleId is not a valid UUID 1`] = `
{
"extensions": {
"code": "GRAPHQL_VALIDATION_FAILED",
"http": {
"status": 400,
},
"userFriendlyMessage": "An error occurred.",
"value": "invalid-uuid",
},
"message": "Invalid UUID: 'invalid-uuid'",
"name": "ValidationError",
}
`;
@@ -0,0 +1,138 @@
import { expectOneNotInternalServerErrorSnapshot } from 'test/integration/graphql/utils/expect-one-not-internal-server-error-snapshot.util';
import { upsertPermissionFlags } from 'test/integration/metadata/suites/permission-flag/utils/upsert-permission-flags.util';
import { createOneRole } from 'test/integration/metadata/suites/role/utils/create-one-role.util';
import { deleteOneRole } from 'test/integration/metadata/suites/role/utils/delete-one-role.util';
import { findRoles } from 'test/integration/metadata/suites/role/utils/find-roles.util';
import { jestExpectToBeDefined } from 'test/utils/jest-expect-to-be-defined.util.test';
import { PermissionFlagType } from 'twenty-shared/constants';
import {
eachTestingContextFilter,
type EachTestingContext,
} from 'twenty-shared/testing';
import { isDefined } from 'twenty-shared/utils';
import { v4 } from 'uuid';
import { type UpsertPermissionFlagsInput } from 'src/engine/metadata-modules/permission-flag/dtos/upsert-permission-flag-input';
type GlobalTestContext = {
editableRoleId: string;
nonEditableRoleId: string;
};
type TestContext = {
input: (globalContext: GlobalTestContext) => UpsertPermissionFlagsInput;
};
const failingPermissionFlagUpsertTestCases: EachTestingContext<TestContext>[] =
[
{
title: 'when roleId is not a valid UUID',
context: {
input: () => ({
roleId: 'invalid-uuid',
permissionFlagKeys: [PermissionFlagType.DATA_MODEL],
}),
},
},
{
title: 'when roleId does not exist',
context: {
input: () => ({
roleId: v4(),
permissionFlagKeys: [PermissionFlagType.DATA_MODEL],
}),
},
},
{
title: 'when role is not editable (system role)',
context: {
input: (globalContext) => ({
roleId: globalContext.nonEditableRoleId,
permissionFlagKeys: [PermissionFlagType.DATA_MODEL],
}),
},
},
{
title: 'when permissionFlagKeys contains invalid enum value',
context: {
input: (globalContext) => ({
roleId: globalContext.editableRoleId,
permissionFlagKeys: ['INVALID_FLAG' as PermissionFlagType],
}),
},
},
];
describe('Permission flag upsert should fail', () => {
let editableRoleId: string;
let nonEditableRoleId: string;
beforeAll(async () => {
const { data: roleData } = await createOneRole({
expectToFail: false,
input: {
label: 'Test Role For Permission Flag Failing',
description: 'Role for permission flag failing tests',
icon: 'IconSettings',
canUpdateAllSettings: false,
canAccessAllTools: true,
canReadAllObjectRecords: true,
canUpdateAllObjectRecords: false,
canSoftDeleteAllObjectRecords: false,
canDestroyAllObjectRecords: false,
canBeAssignedToUsers: true,
canBeAssignedToAgents: false,
canBeAssignedToApiKeys: false,
},
});
editableRoleId = roleData?.createOneRole?.id;
jestExpectToBeDefined(editableRoleId);
const { data: rolesData } = await findRoles({
expectToFail: false,
gqlFields: `
id
label
isEditable
`,
});
jestExpectToBeDefined(rolesData?.getRoles);
const adminRole = rolesData.getRoles.find(
(role: { label: string; isEditable: boolean }) =>
role.label === 'Admin' && role.isEditable === false,
);
jestExpectToBeDefined(adminRole);
nonEditableRoleId = adminRole.id;
});
afterAll(async () => {
if (isDefined(editableRoleId)) {
await deleteOneRole({
expectToFail: false,
input: { idToDelete: editableRoleId },
});
}
});
it.each(eachTestingContextFilter(failingPermissionFlagUpsertTestCases))(
'$title',
async ({ context }) => {
const globalContext: GlobalTestContext = {
editableRoleId: editableRoleId ?? '',
nonEditableRoleId: nonEditableRoleId ?? '',
};
const input = context.input(globalContext);
const { errors } = await upsertPermissionFlags({
expectToFail: true,
input,
});
expectOneNotInternalServerErrorSnapshot({
errors,
});
},
);
});
@@ -0,0 +1,118 @@
import { upsertPermissionFlags } from 'test/integration/metadata/suites/permission-flag/utils/upsert-permission-flags.util';
import { createOneRole } from 'test/integration/metadata/suites/role/utils/create-one-role.util';
import { deleteOneRole } from 'test/integration/metadata/suites/role/utils/delete-one-role.util';
import { jestExpectToBeDefined } from 'test/utils/jest-expect-to-be-defined.util.test';
import { PermissionFlagType } from 'twenty-shared/constants';
describe('Permission flag upsert should succeed', () => {
let createdRoleId: string;
beforeAll(async () => {
const { data: roleData } = await createOneRole({
expectToFail: false,
input: {
label: 'Test Role For Permission Flag Success',
description: 'Role for permission flag successful tests',
icon: 'IconSettings',
canUpdateAllSettings: false,
canAccessAllTools: true,
canReadAllObjectRecords: true,
canUpdateAllObjectRecords: false,
canSoftDeleteAllObjectRecords: false,
canDestroyAllObjectRecords: false,
canBeAssignedToUsers: true,
canBeAssignedToAgents: false,
canBeAssignedToApiKeys: false,
},
});
const roleId = roleData?.createOneRole?.id;
jestExpectToBeDefined(roleId);
createdRoleId = roleId;
});
afterAll(async () => {
if (createdRoleId) {
await deleteOneRole({
expectToFail: false,
input: { idToDelete: createdRoleId },
});
}
});
it('should upsert with empty permissionFlagKeys', async () => {
const { data } = await upsertPermissionFlags({
expectToFail: false,
input: {
roleId: createdRoleId,
permissionFlagKeys: [],
},
});
expect(data?.upsertPermissionFlags).toEqual([]);
});
it('should upsert with one permission flag', async () => {
const { data } = await upsertPermissionFlags({
expectToFail: false,
input: {
roleId: createdRoleId,
permissionFlagKeys: [PermissionFlagType.DATA_MODEL],
},
});
expect(data?.upsertPermissionFlags).toHaveLength(1);
expect(data?.upsertPermissionFlags?.[0]).toMatchObject({
roleId: createdRoleId,
flag: PermissionFlagType.DATA_MODEL,
});
expect(data?.upsertPermissionFlags?.[0].id).toBeDefined();
});
it('should upsert with multiple permission flags', async () => {
const { data } = await upsertPermissionFlags({
expectToFail: false,
input: {
roleId: createdRoleId,
permissionFlagKeys: [
PermissionFlagType.DATA_MODEL,
PermissionFlagType.ROLES,
PermissionFlagType.VIEWS,
],
},
});
expect(data?.upsertPermissionFlags).toHaveLength(3);
const flags = data?.upsertPermissionFlags?.map((pf) => pf.flag) ?? [];
expect(flags).toContain(PermissionFlagType.DATA_MODEL);
expect(flags).toContain(PermissionFlagType.ROLES);
expect(flags).toContain(PermissionFlagType.VIEWS);
});
it('should upsert to remove some flags (replace with subset)', async () => {
const { data } = await upsertPermissionFlags({
expectToFail: false,
input: {
roleId: createdRoleId,
permissionFlagKeys: [PermissionFlagType.DATA_MODEL],
},
});
expect(data?.upsertPermissionFlags).toHaveLength(1);
expect(data?.upsertPermissionFlags?.[0].flag).toBe(
PermissionFlagType.DATA_MODEL,
);
});
it('should upsert back to empty', async () => {
const { data } = await upsertPermissionFlags({
expectToFail: false,
input: {
roleId: createdRoleId,
permissionFlagKeys: [],
},
});
expect(data?.upsertPermissionFlags).toEqual([]);
});
});
@@ -0,0 +1,28 @@
import gql from 'graphql-tag';
import { type PerformMetadataQueryParams } from 'test/integration/metadata/types/perform-metadata-query.type';
import { type UpsertPermissionFlagsInput } from 'src/engine/metadata-modules/permission-flag/dtos/upsert-permission-flag-input';
export type UpsertPermissionFlagsFactoryInput = UpsertPermissionFlagsInput;
const DEFAULT_PERMISSION_FLAG_GQL_FIELDS = `
id
roleId
flag
`;
export const upsertPermissionFlagsQueryFactory = ({
input,
gqlFields = DEFAULT_PERMISSION_FLAG_GQL_FIELDS,
}: PerformMetadataQueryParams<UpsertPermissionFlagsFactoryInput>) => ({
query: gql`
mutation UpsertPermissionFlags($upsertPermissionFlagsInput: UpsertPermissionFlagsInput!) {
upsertPermissionFlags(upsertPermissionFlagsInput: $upsertPermissionFlagsInput) {
${gqlFields}
}
}
`,
variables: {
upsertPermissionFlagsInput: input,
},
});
@@ -0,0 +1,43 @@
import {
type UpsertPermissionFlagsFactoryInput,
upsertPermissionFlagsQueryFactory,
} from 'test/integration/metadata/suites/permission-flag/utils/upsert-permission-flags-query-factory.util';
import { makeMetadataAPIRequest } from 'test/integration/metadata/suites/utils/make-metadata-api-request.util';
import { type CommonResponseBody } from 'test/integration/metadata/types/common-response-body.type';
import { type PerformMetadataQueryParams } from 'test/integration/metadata/types/perform-metadata-query.type';
import { warnIfErrorButNotExpectedToFail } from 'test/integration/metadata/utils/warn-if-error-but-not-expected-to-fail.util';
import { warnIfNoErrorButExpectedToFail } from 'test/integration/metadata/utils/warn-if-no-error-but-expected-to-fail.util';
import { type PermissionFlagDTO } from 'src/engine/metadata-modules/permission-flag/dtos/permission-flag.dto';
export const upsertPermissionFlags = async ({
input,
gqlFields,
expectToFail = false,
token,
}: PerformMetadataQueryParams<UpsertPermissionFlagsFactoryInput>): CommonResponseBody<{
upsertPermissionFlags: PermissionFlagDTO[];
}> => {
const graphqlOperation = upsertPermissionFlagsQueryFactory({
input,
gqlFields,
});
const response = await makeMetadataAPIRequest(graphqlOperation, token);
if (expectToFail === true) {
warnIfNoErrorButExpectedToFail({
response,
errorMessage: 'Permission flags upsert should have failed but did not',
});
}
if (expectToFail === false) {
warnIfErrorButNotExpectedToFail({
response,
errorMessage: 'Permission flags upsert has failed but should not',
});
}
return { data: response.body.data, errors: response.body.errors };
};
@@ -21,6 +21,8 @@ export const ALL_METADATA_NAME = {
pageLayoutTab: 'pageLayoutTab',
commandMenuItem: 'commandMenuItem',
navigationMenuItem: 'navigationMenuItem',
objectPermission: 'objectPermission',
permissionFlag: 'permissionFlag',
frontComponent: 'frontComponent',
webhook: 'webhook',
} as const;