Compare commits

...

5 Commits

Author SHA1 Message Date
neo773 0b13c39e02 Fix participant matching failing due to updating all columns instead of only changed fields (#18105)
The updateMany in matchParticipants was spreading the entire participant
object into the SET clause, which included generated columns
(searchVector) and composite field columns (createdBySource) that can't
be written to. Narrowed the update to only set personId and
workspaceMemberId.
```
[1] query failed: UPDATE "workspace_3ixj3i1a5avy16ptijtb3lae3"."calendarEventParticipant" SET "id" = $1, "createdAt" = $2, "updatedAt" = $3, "deletedAt" = $4, "createdBySource" = $5, "createdByWorkspaceMemberId" = $6, "createdByName" = $7, "createdByContext" = $8, "updatedBySource" = $9, "updatedByWorkspaceMemberId" = $10, "updatedByName" = $11, "updatedByContext" = $12, "position" = $13, "searchVector" = $14, "handle" = $15, "displayName" = $16, "isOrganizer" = $17, "responseStatus" = $18, "calendarEventId" = $19, "personId" = $20, "workspaceMemberId" = $21 WHERE "id" = $22 RETURNING * -- PARAMETERS: ["f1526103-451a-429f-9743-3a81c3a3e3aa","2026-02-19T22:23:32.354Z","2026-02-19T22:23:32.354Z",null,"MANUAL",null,"System",null,"MANUAL",null,"System",null,0,"'test@test.com':1","test@test.com","",false,"NEEDS_ACTION","fb1892a8-6daf-435b-a43b-e8fe8693eb99","60bf9e82-f1b7-4bed-a1af-7039fb9c32f5",null,"f1526103-451a-429f-9743-3a81c3a3e3aa"]
[1] error: error: column "searchVector" can only be updated to DEFAULT
[1] Exception Captured
[1]   undefined
[1]   [
[1]     PostgresException [Error]: Data validation error.
[1]         at computeTwentyORMException (/Users/huzef/Documents/bounties/twenty/packages/twenty-server/dist/engine/twenty-orm/error-handling/compute-twenty-orm-exception.js:35:19)
[1]         at WorkspaceUpdateQueryBuilder.executeMany (/Users/huzef/Documents/bounties/twenty/packages/twenty-server/dist/engine/twenty-orm/repository/workspace-update-query-builder.js:269:82)
[1]         at process.processTicksAndRejections (node:internal/process/task_queues:105:5)
[1]         at async WorkspaceRepository.updateMany (/Users/huzef/Documents/bounties/twenty/packages/twenty-server/dist/engine/twenty-orm/repository/workspace.repository.js:234:25)
[1]         at async MatchParticipantService.matchParticipants (/Users/huzef/Documents/bounties/twenty/packages/twenty-server/dist/modules/match-participant/match-participant.service.js:93:13)
[1]         at async /Users/huzef/Documents/bounties/twenty/packages/twenty-server/dist/modules/match-participant/match-participant.service.js:160:13
[1]         at async MatchParticipantService.matchParticipantsForPeople (/Users/huzef/Documents/bounties/twenty/packages/twenty-server/dist/modules/match-participant/match-participant.service.js:130:9)
[1]         at async CalendarEventParticipantMatchParticipantJob.handle (/Users/huzef/Documents/bounties/twenty/packages/twenty-server/dist/modules/calendar/calendar-event-participant-manager/jobs/calendar-event-participant-match-participant.job.js:45:13)
[1]         at async MessageQueueExplorer.invokeProcessMethods (/Users/huzef/Documents/bounties/twenty/packages/twenty-server/dist/engine/core-modules/message-queue/message-queue.explorer.js:113:17)
[1]         at async MessageQueueExplorer.handleProcessor (/Users/huzef/Documents/bounties/twenty/packages/twenty-server/dist/engine/core-modules/message-queue/message-queue.explorer.js:104:13) {
[1]       code: '428C9'
[1]     }
[1]   ]
```
2026-02-20 10:27:36 +01:00
Etienne 38a49650a3 FILES field - Attachment name display fix (#18073)
with new 'file' FILES field on attachment, UI should display attachment
name from file field value.
Issue with API users updating only 'file' FILES field (and not name
field anymore)
2026-02-19 16:07:18 +01:00
Etienne 35c4ec0d44 Remove non positive integer constraint on Nav Menu Item 2/2 (#18090)
Follow up https://github.com/twentyhq/twenty/pull/18081
2026-02-19 15:20:29 +01:00
Weiko 791462bab0 Fix google compose scope not gated by feature flag (#18093)
## Context
New google api scope has been introduced in
https://github.com/twentyhq/twenty/pull/17793 without being gated behind
a feature flag

Microsoft is using pre-existing Mail.ReadWrite scope there is nothing to
gate

## Before
<img width="553" height="445" alt="Screenshot 2026-02-19 at 14 57 58"
src="https://github.com/user-attachments/assets/59a4f76b-d38d-492f-b013-b6cad4091a7f"
/>


## After
<img width="535" height="392" alt="Screenshot 2026-02-19 at 14 58 44"
src="https://github.com/user-attachments/assets/0337bf15-ec30-4549-bb9d-571a982dffd8"
/>
2026-02-19 15:20:19 +01:00
Etienne 78aad6733f Remove non positive integer constraint on Nav Menu Item (#18081)
To fit with position typed field logic + Ease favorite to nav menu item
migration (which have negative and float position)
2026-02-19 14:40:00 +01:00
22 changed files with 136 additions and 109 deletions
@@ -1045,7 +1045,7 @@ export type CreateNavigationMenuItemInput = {
icon?: InputMaybe<Scalars['String']>;
link?: InputMaybe<Scalars['String']>;
name?: InputMaybe<Scalars['String']>;
position?: InputMaybe<Scalars['Int']>;
position?: InputMaybe<Scalars['Float']>;
targetObjectMetadataId?: InputMaybe<Scalars['UUID']>;
targetRecordId?: InputMaybe<Scalars['UUID']>;
userWorkspaceId?: InputMaybe<Scalars['UUID']>;
@@ -4583,7 +4583,7 @@ export type UpdateNavigationMenuItemInput = {
icon?: InputMaybe<Scalars['String']>;
link?: InputMaybe<Scalars['String']>;
name?: InputMaybe<Scalars['String']>;
position?: InputMaybe<Scalars['Int']>;
position?: InputMaybe<Scalars['Float']>;
};
export type UpdateObjectPayload = {
@@ -22,8 +22,8 @@ import { useHasPermissionFlag } from '@/settings/roles/hooks/useHasPermissionFla
import { IconCalendar, OverflowingTextWithTooltip } from 'twenty-ui/display';
import { isNavigationModifierPressed } from 'twenty-ui/utilities';
import {
PermissionFlagType,
FeatureFlagKey,
PermissionFlagType,
} from '~/generated-metadata/graphql';
import { formatToHumanReadableDate } from '~/utils/date-utils';
import { getFileNameAndExtension } from '~/utils/file/getFileNameAndExtension';
@@ -96,7 +96,11 @@ export const AttachmentRow = ({
);
const { name: originalFileName, extension: attachmentFileExtension } =
getFileNameAndExtension(attachment.name);
getFileNameAndExtension(
isFilesFieldMigrated
? (attachment.file?.[0]?.label as string)
: attachment.name,
);
const [attachmentFileName, setAttachmentFileName] =
useState(originalFileName);
@@ -206,7 +210,9 @@ export const AttachmentRow = ({
target="_blank"
rel="noopener noreferrer"
>
<OverflowingTextWithTooltip text={attachment.name} />
<OverflowingTextWithTooltip
text={`${attachmentFileName}${attachmentFileExtension}`}
/>
</StyledLink>
</StyledLinkContainer>
)}
@@ -0,0 +1,19 @@
import { type MigrationInterface, type QueryRunner } from 'typeorm';
export class ChangeNavigationMenuItemPositionToDoublePrecision1771499112046
implements MigrationInterface
{
name = 'ChangeNavigationMenuItemPositionToDoublePrecision1771499112046';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "core"."navigationMenuItem" ALTER COLUMN "position" TYPE double precision`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "core"."navigationMenuItem" ALTER COLUMN "position" TYPE integer`,
);
}
}
@@ -12,6 +12,8 @@ import { GoogleAPIsOauthExchangeCodeForTokenStrategy } from 'src/engine/core-mod
import { TransientTokenService } from 'src/engine/core-modules/auth/token/services/transient-token.service';
import { setRequestExtraParams } from 'src/engine/core-modules/auth/utils/google-apis-set-request-extra-params.util';
import { WorkspaceDomainsService } from 'src/engine/core-modules/domain/workspace-domains/services/workspace-domains.service';
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service';
import { GuardRedirectService } from 'src/engine/core-modules/guard-redirect/services/guard-redirect.service';
import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service';
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
@@ -27,6 +29,7 @@ export class GoogleAPIsOauthExchangeCodeForTokenGuard extends AuthGuard(
@InjectRepository(WorkspaceEntity)
private readonly workspaceRepository: Repository<WorkspaceEntity>,
private readonly workspaceDomainsService: WorkspaceDomainsService,
private readonly featureFlagService: FeatureFlagService,
) {
super();
}
@@ -46,7 +49,21 @@ export class GoogleAPIsOauthExchangeCodeForTokenGuard extends AuthGuard(
);
}
new GoogleAPIsOauthExchangeCodeForTokenStrategy(this.twentyConfigService);
const { workspaceId } =
await this.transientTokenService.verifyTransientToken(
state.transientToken,
);
const isDraftEmailEnabled =
await this.featureFlagService.isFeatureEnabled(
FeatureFlagKey.IS_DRAFT_EMAIL_ENABLED,
workspaceId,
);
new GoogleAPIsOauthExchangeCodeForTokenStrategy(
this.twentyConfigService,
isDraftEmailEnabled,
);
setRequestExtraParams(request, {
transientToken: state.transientToken,
@@ -12,6 +12,8 @@ import { GoogleAPIsOauthRequestCodeStrategy } from 'src/engine/core-modules/auth
import { TransientTokenService } from 'src/engine/core-modules/auth/token/services/transient-token.service';
import { setRequestExtraParams } from 'src/engine/core-modules/auth/utils/google-apis-set-request-extra-params.util';
import { WorkspaceDomainsService } from 'src/engine/core-modules/domain/workspace-domains/services/workspace-domains.service';
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service';
import { GuardRedirectService } from 'src/engine/core-modules/guard-redirect/services/guard-redirect.service';
import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service';
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
@@ -25,6 +27,7 @@ export class GoogleAPIsOauthRequestCodeGuard extends AuthGuard('google-apis') {
@InjectRepository(WorkspaceEntity)
private readonly workspaceRepository: Repository<WorkspaceEntity>,
private readonly workspaceDomainsService: WorkspaceDomainsService,
private readonly featureFlagService: FeatureFlagService,
) {
super({
prompt: 'select_account',
@@ -68,7 +71,16 @@ export class GoogleAPIsOauthRequestCodeGuard extends AuthGuard('google-apis') {
);
}
new GoogleAPIsOauthRequestCodeStrategy(this.twentyConfigService);
const isDraftEmailEnabled =
await this.featureFlagService.isFeatureEnabled(
FeatureFlagKey.IS_DRAFT_EMAIL_ENABLED,
workspaceId,
);
new GoogleAPIsOauthRequestCodeStrategy(
this.twentyConfigService,
isDraftEmailEnabled,
);
return (await super.canActivate(context)) as boolean;
} catch (err) {
@@ -92,8 +92,24 @@ describe('GoogleAPIScopesService', () => {
expect(result).toBe(false);
});
it('should work with the current Google API scopes', () => {
// What is currently returned by Google
it('should work with the current Google API scopes when draft email is disabled', () => {
const actualGoogleScopes = [
'https://www.googleapis.com/auth/calendar.events',
'https://www.googleapis.com/auth/gmail.readonly',
'https://www.googleapis.com/auth/gmail.send',
'https://www.googleapis.com/auth/profile.emails.read',
'https://www.googleapis.com/auth/userinfo.email',
'https://www.googleapis.com/auth/userinfo.profile',
'openid',
];
const expectedScopes = getGoogleApisOauthScopes(false);
const result = includesExpectedScopes(actualGoogleScopes, expectedScopes);
expect(result).toBe(true);
});
it('should work with the current Google API scopes when draft email is enabled', () => {
const actualGoogleScopes = [
'https://www.googleapis.com/auth/calendar.events',
'https://www.googleapis.com/auth/gmail.readonly',
@@ -104,7 +120,7 @@ describe('GoogleAPIScopesService', () => {
'https://www.googleapis.com/auth/userinfo.profile',
'openid',
];
const expectedScopes = getGoogleApisOauthScopes();
const expectedScopes = getGoogleApisOauthScopes(true);
const result = includesExpectedScopes(actualGoogleScopes, expectedScopes);
@@ -30,6 +30,7 @@ export class GoogleAPIScopesService {
public async getScopesFromGoogleAccessTokenAndCheckIfExpectedScopesArePresent(
accessToken: string,
isDraftEmailEnabled = false,
): Promise<{ scopes: string[]; isValid: boolean }> {
try {
const httpClient = this.secureHttpClientService.getHttpClient();
@@ -40,7 +41,7 @@ export class GoogleAPIScopesService {
);
const scopes = response.data.scope.split(' ');
const expectedScopes = getGoogleApisOauthScopes();
const expectedScopes = getGoogleApisOauthScopes(isDraftEmailEnabled);
return {
scopes,
@@ -10,6 +10,7 @@ import { GoogleAPIScopesService } from 'src/engine/core-modules/auth/services/go
import { GoogleApisServiceAvailabilityService } from 'src/engine/core-modules/auth/services/google-apis-service-availability.service';
import { GoogleAPIsService } from 'src/engine/core-modules/auth/services/google-apis.service';
import { UpdateConnectedAccountOnReconnectService } from 'src/engine/core-modules/auth/services/update-connected-account-on-reconnect.service';
import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service';
import { MessageQueue } from 'src/engine/core-modules/message-queue/message-queue.constants';
import { getQueueToken } from 'src/engine/core-modules/message-queue/utils/get-queue-token.util';
import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service';
@@ -181,6 +182,12 @@ describe('GoogleAPIsService', () => {
provide: getQueueToken(MessageQueue.calendarQueue),
useValue: mockCalendarQueueService,
},
{
provide: FeatureFlagService,
useValue: {
isFeatureEnabled: jest.fn().mockResolvedValue(false),
},
},
],
}).compile();
@@ -13,6 +13,8 @@ import { CreateMessageChannelService } from 'src/engine/core-modules/auth/servic
import { GoogleAPIScopesService } from 'src/engine/core-modules/auth/services/google-apis-scopes';
import { GoogleApisServiceAvailabilityService } from 'src/engine/core-modules/auth/services/google-apis-service-availability.service';
import { UpdateConnectedAccountOnReconnectService } from 'src/engine/core-modules/auth/services/update-connected-account-on-reconnect.service';
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service';
import { InjectMessageQueue } from 'src/engine/core-modules/message-queue/decorators/message-queue.decorator';
import { MessageQueue } from 'src/engine/core-modules/message-queue/message-queue.constants';
import { MessageQueueService } from 'src/engine/core-modules/message-queue/services/message-queue.service';
@@ -63,6 +65,7 @@ export class GoogleAPIsService {
private readonly updateConnectedAccountOnReconnectService: UpdateConnectedAccountOnReconnectService,
private readonly googleAPIScopesService: GoogleAPIScopesService,
private readonly googleApisServiceAvailabilityService: GoogleApisServiceAvailabilityService,
private readonly featureFlagService: FeatureFlagService,
) {}
async refreshGoogleRefreshToken(input: {
@@ -92,9 +95,15 @@ export class GoogleAPIsService {
'MESSAGING_PROVIDER_GMAIL_ENABLED',
);
const isDraftEmailEnabled = await this.featureFlagService.isFeatureEnabled(
FeatureFlagKey.IS_DRAFT_EMAIL_ENABLED,
workspaceId,
);
const { scopes, isValid } =
await this.googleAPIScopesService.getScopesFromGoogleAccessTokenAndCheckIfExpectedScopesArePresent(
input.accessToken,
isDraftEmailEnabled,
);
if (!isValid) {
@@ -14,8 +14,11 @@ export abstract class GoogleAPIsOauthCommonStrategy extends PassportStrategy(
Strategy,
'google-apis',
) {
constructor(twentyConfigService: TwentyConfigService) {
const scopes = getGoogleApisOauthScopes();
constructor(
twentyConfigService: TwentyConfigService,
isDraftEmailEnabled = false,
) {
const scopes = getGoogleApisOauthScopes(isDraftEmailEnabled);
super({
clientID: twentyConfigService.get('AUTH_GOOGLE_CLIENT_ID'),
@@ -12,8 +12,11 @@ export type GoogleAPIScopeConfig = {
@Injectable()
export class GoogleAPIsOauthExchangeCodeForTokenStrategy extends GoogleAPIsOauthCommonStrategy {
constructor(twentyConfigService: TwentyConfigService) {
super(twentyConfigService);
constructor(
twentyConfigService: TwentyConfigService,
isDraftEmailEnabled = false,
) {
super(twentyConfigService, isDraftEmailEnabled);
}
async validate(
@@ -12,8 +12,11 @@ export type GoogleAPIScopeConfig = {
@Injectable()
export class GoogleAPIsOauthRequestCodeStrategy extends GoogleAPIsOauthCommonStrategy {
constructor(twentyConfigService: TwentyConfigService) {
super(twentyConfigService);
constructor(
twentyConfigService: TwentyConfigService,
isDraftEmailEnabled = false,
) {
super(twentyConfigService, isDraftEmailEnabled);
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -1,7 +1,7 @@
/** email, profile and openid permission can be called without the https://www.googleapis.com/auth/ prefix
* see https://developers.google.com/identity/protocols/oauth2/scopes
*/
export const getGoogleApisOauthScopes = () => {
export const getGoogleApisOauthScopes = (isDraftEmailEnabled = false) => {
return [
'email',
'profile',
@@ -9,6 +9,8 @@ export const getGoogleApisOauthScopes = () => {
'https://www.googleapis.com/auth/calendar.events',
'https://www.googleapis.com/auth/profile.emails.read',
'https://www.googleapis.com/auth/gmail.send',
'https://www.googleapis.com/auth/gmail.compose',
...(isDraftEmailEnabled
? ['https://www.googleapis.com/auth/gmail.compose']
: []),
];
};
@@ -1,6 +1,6 @@
import { Field, InputType, Int } from '@nestjs/graphql';
import { Field, InputType } from '@nestjs/graphql';
import { IsInt, IsOptional, IsString, IsUUID, Min } from 'class-validator';
import { IsNumber, IsOptional, IsString, IsUUID } from 'class-validator';
import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
@@ -46,9 +46,8 @@ export class CreateNavigationMenuItemInput {
@Field(() => UUIDScalarType, { nullable: true })
folderId?: string;
@IsInt()
@Min(0)
@IsNumber()
@IsOptional()
@Field(() => Int, { nullable: true })
@Field({ nullable: true })
position?: number;
}
@@ -1,13 +1,12 @@
import { Field, InputType, Int } from '@nestjs/graphql';
import { Field, InputType } from '@nestjs/graphql';
import { Type } from 'class-transformer';
import {
IsInt,
IsNotEmpty,
IsNumber,
IsOptional,
IsString,
IsUUID,
Min,
ValidateNested,
} from 'class-validator';
@@ -20,10 +19,9 @@ export class UpdateNavigationMenuItemInput {
@Field(() => UUIDScalarType, { nullable: true })
folderId?: string | null;
@IsInt()
@Min(0)
@IsNumber()
@IsOptional()
@Field(() => Int, { nullable: true })
@Field({ nullable: true })
position?: number;
@IsOptional()
@@ -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' })
@@ -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`,
});
}
@@ -186,7 +186,10 @@ export class MatchParticipantService<
await participantRepository.updateMany(
partipantsToBeUpdated.map((participant) => ({
criteria: participant.id,
partialEntity: participant,
partialEntity: {
personId: participant.personId,
workspaceMemberId: participant.workspaceMemberId,
},
})),
);
@@ -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",
}
`;
@@ -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<String>,
},
"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",
}
`;
@@ -86,16 +86,6 @@ const failingNavigationMenuItemCreationTestCases: EachTestingContext<TestContext
},
},
},
{
title: 'when creating with negative position',
context: {
input: {
targetRecordId: faker.string.uuid(),
targetObjectMetadataId: faker.string.uuid(),
position: -1,
},
},
},
];
describe('NavigationMenuItem creation should fail', () => {
@@ -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))(