Address PR review comments: consolidate view tool providers, add validations
- Consolidate ViewFilterToolProvider and ViewSortToolProvider into ViewToolProvider - Remove VIEW_FILTER and VIEW_SORT tool categories - Remove NavigationMenuItemObjectDeactivationListener (prefer display-time filtering) - Validate calendarFieldName is DATE/DATE_TIME field type - Enforce mainGroupByFieldName for KANBAN views - Enforce calendarFieldName + calendarLayout for CALENDAR views - Fix "navigate tool" -> "navigate_app tool" in skill metadata - Fix ViewFilterValue type assertions (was incorrectly cast as string) - Add type property to mock field metadata fixtures in tests - Add validation tests for KANBAN and CALENDAR view requirements
This commit is contained in:
-2
@@ -6,8 +6,6 @@ export enum ToolCategory {
|
||||
NATIVE_MODEL = 'NATIVE_MODEL',
|
||||
VIEW = 'VIEW',
|
||||
VIEW_FIELD = 'VIEW_FIELD',
|
||||
VIEW_FILTER = 'VIEW_FILTER',
|
||||
VIEW_SORT = 'VIEW_SORT',
|
||||
DASHBOARD = 'DASHBOARD',
|
||||
LOGIC_FUNCTION = 'LOGIC_FUNCTION',
|
||||
}
|
||||
|
||||
+41
-19
@@ -16,6 +16,8 @@ import {
|
||||
} from 'src/engine/core-modules/tool-provider/types/tool-descriptor.type';
|
||||
import { toolSetToDescriptors } from 'src/engine/core-modules/tool-provider/utils/tool-set-to-descriptors.util';
|
||||
import { PermissionsService } from 'src/engine/metadata-modules/permissions/permissions.service';
|
||||
import { ViewFilterToolsFactory } from 'src/engine/metadata-modules/view-filter/tools/view-filter-tools.factory';
|
||||
import { ViewSortToolsFactory } from 'src/engine/metadata-modules/view-sort/tools/view-sort-tools.factory';
|
||||
import { ViewToolsFactory } from 'src/engine/metadata-modules/view/tools/view-tools.factory';
|
||||
|
||||
@Injectable()
|
||||
@@ -24,23 +26,31 @@ export class ViewToolProvider implements ToolProvider, OnModuleInit {
|
||||
|
||||
constructor(
|
||||
private readonly viewToolsFactory: ViewToolsFactory,
|
||||
private readonly viewFilterToolsFactory: ViewFilterToolsFactory,
|
||||
private readonly viewSortToolsFactory: ViewSortToolsFactory,
|
||||
private readonly permissionsService: PermissionsService,
|
||||
private readonly toolExecutorService: ToolExecutorService,
|
||||
) {}
|
||||
|
||||
onModuleInit(): void {
|
||||
const factory = this.viewToolsFactory;
|
||||
const viewFactory = this.viewToolsFactory;
|
||||
const filterFactory = this.viewFilterToolsFactory;
|
||||
const sortFactory = this.viewSortToolsFactory;
|
||||
|
||||
this.toolExecutorService.registerCategoryGenerator(
|
||||
ToolCategory.VIEW,
|
||||
async (context) => {
|
||||
const workspaceMemberId = context.actorContext?.workspaceMemberId;
|
||||
|
||||
const readTools = factory.generateReadTools(
|
||||
context.workspaceId,
|
||||
workspaceMemberId ?? undefined,
|
||||
workspaceMemberId ?? undefined,
|
||||
);
|
||||
const readTools = {
|
||||
...viewFactory.generateReadTools(
|
||||
context.workspaceId,
|
||||
workspaceMemberId ?? undefined,
|
||||
workspaceMemberId ?? undefined,
|
||||
),
|
||||
...filterFactory.generateReadTools(context.workspaceId),
|
||||
...sortFactory.generateReadTools(context.workspaceId),
|
||||
};
|
||||
|
||||
const hasViewPermission =
|
||||
await this.permissionsService.checkRolesPermissions(
|
||||
@@ -50,10 +60,14 @@ export class ViewToolProvider implements ToolProvider, OnModuleInit {
|
||||
);
|
||||
|
||||
if (hasViewPermission) {
|
||||
const writeTools = factory.generateWriteTools(
|
||||
context.workspaceId,
|
||||
workspaceMemberId ?? undefined,
|
||||
);
|
||||
const writeTools = {
|
||||
...viewFactory.generateWriteTools(
|
||||
context.workspaceId,
|
||||
workspaceMemberId ?? undefined,
|
||||
),
|
||||
...filterFactory.generateWriteTools(context.workspaceId),
|
||||
...sortFactory.generateWriteTools(context.workspaceId),
|
||||
};
|
||||
|
||||
return { ...readTools, ...writeTools };
|
||||
}
|
||||
@@ -76,11 +90,15 @@ export class ViewToolProvider implements ToolProvider, OnModuleInit {
|
||||
includeSchemas: options?.includeSchemas ?? true,
|
||||
};
|
||||
|
||||
const readTools = this.viewToolsFactory.generateReadTools(
|
||||
context.workspaceId,
|
||||
workspaceMemberId ?? undefined,
|
||||
workspaceMemberId ?? undefined,
|
||||
);
|
||||
const readTools = {
|
||||
...this.viewToolsFactory.generateReadTools(
|
||||
context.workspaceId,
|
||||
workspaceMemberId ?? undefined,
|
||||
workspaceMemberId ?? undefined,
|
||||
),
|
||||
...this.viewFilterToolsFactory.generateReadTools(context.workspaceId),
|
||||
...this.viewSortToolsFactory.generateReadTools(context.workspaceId),
|
||||
};
|
||||
|
||||
const hasViewPermission =
|
||||
await this.permissionsService.checkRolesPermissions(
|
||||
@@ -90,10 +108,14 @@ export class ViewToolProvider implements ToolProvider, OnModuleInit {
|
||||
);
|
||||
|
||||
if (hasViewPermission) {
|
||||
const writeTools = this.viewToolsFactory.generateWriteTools(
|
||||
context.workspaceId,
|
||||
workspaceMemberId ?? undefined,
|
||||
);
|
||||
const writeTools = {
|
||||
...this.viewToolsFactory.generateWriteTools(
|
||||
context.workspaceId,
|
||||
workspaceMemberId ?? undefined,
|
||||
),
|
||||
...this.viewFilterToolsFactory.generateWriteTools(context.workspaceId),
|
||||
...this.viewSortToolsFactory.generateWriteTools(context.workspaceId),
|
||||
};
|
||||
|
||||
return toolSetToDescriptors(
|
||||
{ ...readTools, ...writeTools },
|
||||
|
||||
@@ -11,8 +11,6 @@ import { LogicFunctionToolProvider } from 'src/engine/core-modules/tool-provider
|
||||
import { MetadataToolProvider } from 'src/engine/core-modules/tool-provider/providers/metadata-tool.provider';
|
||||
import { NativeModelToolProvider } from 'src/engine/core-modules/tool-provider/providers/native-model-tool.provider';
|
||||
import { ViewFieldToolProvider } from 'src/engine/core-modules/tool-provider/providers/view-field-tool.provider';
|
||||
import { ViewFilterToolProvider } from 'src/engine/core-modules/tool-provider/providers/view-filter-tool.provider';
|
||||
import { ViewSortToolProvider } from 'src/engine/core-modules/tool-provider/providers/view-sort-tool.provider';
|
||||
import { ViewToolProvider } from 'src/engine/core-modules/tool-provider/providers/view-tool.provider';
|
||||
import { WorkflowToolProvider } from 'src/engine/core-modules/tool-provider/providers/workflow-tool.provider';
|
||||
import { ToolExecutorService } from 'src/engine/core-modules/tool-provider/services/tool-executor.service';
|
||||
@@ -70,8 +68,6 @@ import { ToolRegistryService } from './services/tool-registry.service';
|
||||
NativeModelToolProvider,
|
||||
LogicFunctionToolProvider,
|
||||
ViewFieldToolProvider,
|
||||
ViewFilterToolProvider,
|
||||
ViewSortToolProvider,
|
||||
ViewToolProvider,
|
||||
WorkflowToolProvider,
|
||||
{
|
||||
@@ -85,8 +81,6 @@ import { ToolRegistryService } from './services/tool-registry.service';
|
||||
metadataProvider: MetadataToolProvider,
|
||||
logicFunctionProvider: LogicFunctionToolProvider,
|
||||
viewFieldProvider: ViewFieldToolProvider,
|
||||
viewFilterProvider: ViewFilterToolProvider,
|
||||
viewSortProvider: ViewSortToolProvider,
|
||||
viewProvider: ViewToolProvider,
|
||||
workflowProvider: WorkflowToolProvider,
|
||||
) => [
|
||||
@@ -96,8 +90,6 @@ import { ToolRegistryService } from './services/tool-registry.service';
|
||||
metadataProvider,
|
||||
logicFunctionProvider,
|
||||
viewFieldProvider,
|
||||
viewFilterProvider,
|
||||
viewSortProvider,
|
||||
viewProvider,
|
||||
workflowProvider,
|
||||
],
|
||||
@@ -108,8 +100,6 @@ import { ToolRegistryService } from './services/tool-registry.service';
|
||||
MetadataToolProvider,
|
||||
LogicFunctionToolProvider,
|
||||
ViewFieldToolProvider,
|
||||
ViewFilterToolProvider,
|
||||
ViewSortToolProvider,
|
||||
ViewToolProvider,
|
||||
WorkflowToolProvider,
|
||||
],
|
||||
|
||||
-2
@@ -7,7 +7,6 @@ import { FlatNavigationMenuItemModule } from 'src/engine/metadata-modules/flat-n
|
||||
import { NavigationMenuItemGraphqlApiExceptionInterceptor } from 'src/engine/metadata-modules/navigation-menu-item/interceptors/navigation-menu-item-graphql-api-exception.interceptor';
|
||||
import { NavigationMenuItemDeletionJob } from 'src/engine/metadata-modules/navigation-menu-item/jobs/navigation-menu-item-deletion.job';
|
||||
import { NavigationMenuItemDeletionListener } from 'src/engine/metadata-modules/navigation-menu-item/listeners/navigation-menu-item-deletion.listener';
|
||||
import { NavigationMenuItemObjectDeactivationListener } from 'src/engine/metadata-modules/navigation-menu-item/listeners/navigation-menu-item-object-deactivation.listener';
|
||||
import { NavigationMenuItemResolver } from 'src/engine/metadata-modules/navigation-menu-item/navigation-menu-item.resolver';
|
||||
import { NavigationMenuItemService } from 'src/engine/metadata-modules/navigation-menu-item/navigation-menu-item.service';
|
||||
import { NavigationMenuItemAccessService } from 'src/engine/metadata-modules/navigation-menu-item/services/navigation-menu-item-access.service';
|
||||
@@ -31,7 +30,6 @@ import { WorkspaceMigrationModule } from 'src/engine/workspace-manager/workspace
|
||||
NavigationMenuItemAccessService,
|
||||
NavigationMenuItemDeletionService,
|
||||
NavigationMenuItemDeletionListener,
|
||||
NavigationMenuItemObjectDeactivationListener,
|
||||
NavigationMenuItemDeletionJob,
|
||||
NavigationMenuItemResolver,
|
||||
NavigationMenuItemRecordIdentifierService,
|
||||
|
||||
-78
@@ -1,6 +1,5 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { NavigationMenuItemType } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { ApplicationService } from 'src/engine/core-modules/application/application.service';
|
||||
@@ -23,14 +22,6 @@ const isNavigationMenuItemForDeletedView = (
|
||||
deletedIdsSet: Set<string>,
|
||||
): boolean => isDefined(item.viewId) && deletedIdsSet.has(item.viewId);
|
||||
|
||||
const isNavigationMenuItemForDeactivatedObject = (
|
||||
item: FlatNavigationMenuItem,
|
||||
deactivatedObjectMetadataIdsSet: Set<string>,
|
||||
): boolean =>
|
||||
item.type === NavigationMenuItemType.OBJECT &&
|
||||
isDefined(item.targetObjectMetadataId) &&
|
||||
deactivatedObjectMetadataIdsSet.has(item.targetObjectMetadataId);
|
||||
|
||||
@Injectable()
|
||||
export class NavigationMenuItemDeletionService {
|
||||
constructor(
|
||||
@@ -103,73 +94,4 @@ export class NavigationMenuItemDeletionService {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async deleteNavigationMenuItemsForDeactivatedObjects(
|
||||
deactivatedObjectMetadataIds: string[],
|
||||
workspaceId: string,
|
||||
): Promise<void> {
|
||||
const { workspaceCustomFlatApplication } =
|
||||
await this.applicationService.findWorkspaceTwentyStandardAndCustomApplicationOrThrow(
|
||||
{ workspaceId },
|
||||
);
|
||||
|
||||
const { flatNavigationMenuItemMaps } =
|
||||
await this.workspaceManyOrAllFlatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId,
|
||||
flatMapsKeys: ['flatNavigationMenuItemMaps'],
|
||||
},
|
||||
);
|
||||
|
||||
const deactivatedObjectMetadataIdsSet = new Set(
|
||||
deactivatedObjectMetadataIds,
|
||||
);
|
||||
|
||||
const navigationMenuItemsToDelete = Object.values(
|
||||
flatNavigationMenuItemMaps.byUniversalIdentifier,
|
||||
).filter(
|
||||
(item): item is NonNullable<typeof item> =>
|
||||
isDefined(item) &&
|
||||
isNavigationMenuItemForDeactivatedObject(
|
||||
item,
|
||||
deactivatedObjectMetadataIdsSet,
|
||||
),
|
||||
);
|
||||
|
||||
if (navigationMenuItemsToDelete.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const flatNavigationMenuItemsToDelete = navigationMenuItemsToDelete.map(
|
||||
(item) =>
|
||||
fromDeleteNavigationMenuItemInputToFlatNavigationMenuItemOrThrow({
|
||||
flatNavigationMenuItemMaps,
|
||||
navigationMenuItemId: item.id,
|
||||
}),
|
||||
);
|
||||
|
||||
const validateAndBuildResult =
|
||||
await this.workspaceMigrationValidateBuildAndRunService.validateBuildAndRunWorkspaceMigration(
|
||||
{
|
||||
allFlatEntityOperationByMetadataName: {
|
||||
navigationMenuItem: {
|
||||
flatEntityToCreate: [],
|
||||
flatEntityToDelete: flatNavigationMenuItemsToDelete,
|
||||
flatEntityToUpdate: [],
|
||||
},
|
||||
},
|
||||
workspaceId,
|
||||
isSystemBuild: false,
|
||||
applicationUniversalIdentifier:
|
||||
workspaceCustomFlatApplication.universalIdentifier,
|
||||
},
|
||||
);
|
||||
|
||||
if (validateAndBuildResult.status === 'fail') {
|
||||
throw new WorkspaceMigrationBuilderException(
|
||||
validateAndBuildResult,
|
||||
'Multiple validation errors occurred while deleting navigation menu items for deactivated objects',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user