diff --git a/packages/twenty-server/src/database/typeorm/core/migrations/common/1771499112046-change-navigation-menu-item-position-to-double-precision.ts b/packages/twenty-server/src/database/typeorm/core/migrations/common/1771499112046-change-navigation-menu-item-position-to-double-precision.ts new file mode 100644 index 0000000000..7eb60943f3 --- /dev/null +++ b/packages/twenty-server/src/database/typeorm/core/migrations/common/1771499112046-change-navigation-menu-item-position-to-double-precision.ts @@ -0,0 +1,19 @@ +import { type MigrationInterface, type QueryRunner } from 'typeorm'; + +export class ChangeNavigationMenuItemPositionToDoublePrecision1771499112046 + implements MigrationInterface +{ + name = 'ChangeNavigationMenuItemPositionToDoublePrecision1771499112046'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "core"."navigationMenuItem" ALTER COLUMN "position" TYPE double precision`, + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "core"."navigationMenuItem" ALTER COLUMN "position" TYPE integer`, + ); + } +} diff --git a/packages/twenty-server/src/engine/metadata-modules/navigation-menu-item/entities/navigation-menu-item.entity.ts b/packages/twenty-server/src/engine/metadata-modules/navigation-menu-item/entities/navigation-menu-item.entity.ts index 91ec722f85..0270f7d45a 100644 --- a/packages/twenty-server/src/engine/metadata-modules/navigation-menu-item/entities/navigation-menu-item.entity.ts +++ b/packages/twenty-server/src/engine/metadata-modules/navigation-menu-item/entities/navigation-menu-item.entity.ts @@ -97,7 +97,7 @@ export class NavigationMenuItemEntity @Column({ nullable: true, type: 'uuid' }) folderId: string | null; - @Column({ nullable: false }) + @Column({ nullable: false, type: 'double precision' }) position: number; @CreateDateColumn({ type: 'timestamptz' }) diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-builder/validators/services/flat-navigation-menu-item-validator.service.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-builder/validators/services/flat-navigation-menu-item-validator.service.ts index d4d1ed63dd..aff504c140 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-builder/validators/services/flat-navigation-menu-item-validator.service.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration/workspace-migration-builder/validators/services/flat-navigation-menu-item-validator.service.ts @@ -177,13 +177,12 @@ export class FlatNavigationMenuItemValidatorService { if ( isDefined(flatNavigationMenuItem.position) && - (!Number.isInteger(flatNavigationMenuItem.position) || - flatNavigationMenuItem.position < 0) + !Number.isFinite(flatNavigationMenuItem.position) ) { validationResult.errors.push({ code: NavigationMenuItemExceptionCode.INVALID_NAVIGATION_MENU_ITEM_INPUT, - message: t`Position must be a non-negative integer`, - userFriendlyMessage: msg`Position must be a non-negative integer`, + message: t`Position must be a finite number`, + userFriendlyMessage: msg`Position must be a finite number`, }); } @@ -307,14 +306,11 @@ export class FlatNavigationMenuItemValidatorService { const positionUpdate = flatEntityUpdate.position; - if ( - isDefined(positionUpdate) && - (!Number.isInteger(positionUpdate) || positionUpdate < 0) - ) { + if (isDefined(positionUpdate) && !Number.isFinite(positionUpdate)) { validationResult.errors.push({ code: NavigationMenuItemExceptionCode.INVALID_NAVIGATION_MENU_ITEM_INPUT, - message: t`Position must be a non-negative integer`, - userFriendlyMessage: msg`Position must be a non-negative integer`, + message: t`Position must be a finite number`, + userFriendlyMessage: msg`Position must be a finite number`, }); } diff --git a/packages/twenty-server/test/integration/metadata/suites/navigation-menu-item/__snapshots__/failing-navigation-menu-item-creation.integration-spec.ts.snap b/packages/twenty-server/test/integration/metadata/suites/navigation-menu-item/__snapshots__/failing-navigation-menu-item-creation.integration-spec.ts.snap index 53dca50fe8..0f6d137bfb 100644 --- a/packages/twenty-server/test/integration/metadata/suites/navigation-menu-item/__snapshots__/failing-navigation-menu-item-creation.integration-spec.ts.snap +++ b/packages/twenty-server/test/integration/metadata/suites/navigation-menu-item/__snapshots__/failing-navigation-menu-item-creation.integration-spec.ts.snap @@ -142,14 +142,3 @@ exports[`NavigationMenuItem creation should fail when creating with missing targ } `; -exports[`NavigationMenuItem creation should fail when creating with negative position 1`] = ` -{ - "extensions": { - "code": "NOT_FOUND", - "subCode": "RELATION_UNIVERSAL_IDENTIFIER_NOT_FOUND", - "userFriendlyMessage": "An error occurred.", - }, - "message": "Could not find objectMetadata for given targetObjectMetadataId", - "name": "NotFoundError", -} -`; diff --git a/packages/twenty-server/test/integration/metadata/suites/navigation-menu-item/__snapshots__/failing-navigation-menu-item-update.integration-spec.ts.snap b/packages/twenty-server/test/integration/metadata/suites/navigation-menu-item/__snapshots__/failing-navigation-menu-item-update.integration-spec.ts.snap index 1249756e8b..8fb0046a7b 100644 --- a/packages/twenty-server/test/integration/metadata/suites/navigation-menu-item/__snapshots__/failing-navigation-menu-item-update.integration-spec.ts.snap +++ b/packages/twenty-server/test/integration/metadata/suites/navigation-menu-item/__snapshots__/failing-navigation-menu-item-update.integration-spec.ts.snap @@ -71,38 +71,3 @@ exports[`NavigationMenuItem update should fail when updating with missing id 1`] "name": "ValidationError", } `; - -exports[`NavigationMenuItem update should fail when updating with negative position 1`] = ` -{ - "extensions": { - "code": "METADATA_VALIDATION_FAILED", - "errors": { - "navigationMenuItem": [ - { - "errors": [ - { - "code": "INVALID_NAVIGATION_MENU_ITEM_INPUT", - "message": "Position must be a non-negative integer", - "userFriendlyMessage": "Position must be a non-negative integer", - }, - ], - "flatEntityMinimalInformation": { - "universalIdentifier": Any, - }, - "metadataName": "navigationMenuItem", - "status": "fail", - "type": "update", - }, - ], - }, - "message": "Validation failed for 1 navigationMenuItem", - "summary": { - "navigationMenuItem": 1, - "totalErrors": 1, - }, - "userFriendlyMessage": "Metadata validation failed", - }, - "message": "Multiple validation errors occurred while updating navigation menu item", - "name": "GraphQLError", -} -`; diff --git a/packages/twenty-server/test/integration/metadata/suites/navigation-menu-item/failing-navigation-menu-item-creation.integration-spec.ts b/packages/twenty-server/test/integration/metadata/suites/navigation-menu-item/failing-navigation-menu-item-creation.integration-spec.ts index 6f170f6622..5455107371 100644 --- a/packages/twenty-server/test/integration/metadata/suites/navigation-menu-item/failing-navigation-menu-item-creation.integration-spec.ts +++ b/packages/twenty-server/test/integration/metadata/suites/navigation-menu-item/failing-navigation-menu-item-creation.integration-spec.ts @@ -86,16 +86,6 @@ const failingNavigationMenuItemCreationTestCases: EachTestingContext { diff --git a/packages/twenty-server/test/integration/metadata/suites/navigation-menu-item/failing-navigation-menu-item-update.integration-spec.ts b/packages/twenty-server/test/integration/metadata/suites/navigation-menu-item/failing-navigation-menu-item-update.integration-spec.ts index f26b32e63e..42a23a6da6 100644 --- a/packages/twenty-server/test/integration/metadata/suites/navigation-menu-item/failing-navigation-menu-item-update.integration-spec.ts +++ b/packages/twenty-server/test/integration/metadata/suites/navigation-menu-item/failing-navigation-menu-item-update.integration-spec.ts @@ -130,17 +130,6 @@ describe('NavigationMenuItem update should fail', () => { }), }, }, - { - title: 'when updating with negative position', - context: { - input: (testSetup) => ({ - id: testSetup.testNavigationMenuItemId, - update: { - position: -1, - }, - }), - }, - }, ]; it.each(eachTestingContextFilter(failingNavigationMenuItemUpdateTestCases))(