Common API - Add tests on position field validation at creation (#16630)
Co-authored-by: guillim <guigloo@msn.com>
This commit is contained in:
+17
-20
@@ -33,6 +33,7 @@ import { validateLinksFieldOrThrow } from 'src/engine/api/common/common-args-pro
|
||||
import { validateMultiSelectFieldOrThrow } from 'src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-multi-select-field-or-throw.util';
|
||||
import { validateNumberFieldOrThrow } from 'src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-number-field-or-throw.util';
|
||||
import { validateNumericFieldOrThrow } from 'src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-numeric-field-or-throw.util';
|
||||
import { validateOverriddenPositionFieldOrThrow } from 'src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-overridden-position-field-or-throw.util';
|
||||
import { validatePhonesFieldOrThrow } from 'src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-phones-field-or-throw.util';
|
||||
import { validateRatingAndSelectFieldOrThrow } from 'src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-rating-and-select-field-or-throw.util';
|
||||
import { validateRawJsonFieldOrThrow } from 'src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-raw-json-field-or-throw.util';
|
||||
@@ -44,7 +45,6 @@ import {
|
||||
CommonQueryRunnerExceptionCode,
|
||||
} from 'src/engine/api/common/common-query-runners/errors/common-query-runner.exception';
|
||||
import { AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type';
|
||||
import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service';
|
||||
import { RecordPositionService } from 'src/engine/core-modules/record-position/services/record-position.service';
|
||||
import { transformEmailsValue } from 'src/engine/core-modules/record-transformer/utils/transform-emails-value.util';
|
||||
import { transformLinksValue } from 'src/engine/core-modules/record-transformer/utils/transform-links-value.util';
|
||||
@@ -58,10 +58,7 @@ import { FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-meta
|
||||
|
||||
@Injectable()
|
||||
export class DataArgProcessor {
|
||||
constructor(
|
||||
private readonly recordPositionService: RecordPositionService,
|
||||
private readonly featureFlagService: FeatureFlagService,
|
||||
) {}
|
||||
constructor(private readonly recordPositionService: RecordPositionService) {}
|
||||
|
||||
async process({
|
||||
partialRecordInputs,
|
||||
@@ -90,9 +87,21 @@ export class DataArgProcessor {
|
||||
flatObjectMetadata,
|
||||
);
|
||||
|
||||
const overriddenPositionRecords =
|
||||
await this.recordPositionService.overridePositionOnRecords({
|
||||
partialRecordInputs: partialRecordInputs,
|
||||
workspaceId: workspace.id,
|
||||
objectMetadata: {
|
||||
isCustom: flatObjectMetadata.isCustom,
|
||||
nameSingular: flatObjectMetadata.nameSingular,
|
||||
fieldIdByName,
|
||||
},
|
||||
shouldBackfillPositionIfUndefined,
|
||||
});
|
||||
|
||||
const processedRecords: Partial<ObjectRecord>[] = [];
|
||||
|
||||
for (const record of partialRecordInputs) {
|
||||
for (const record of overriddenPositionRecords) {
|
||||
const processedRecord: Partial<ObjectRecord> = {};
|
||||
|
||||
for (const [key, value] of Object.entries(record)) {
|
||||
@@ -139,19 +148,7 @@ export class DataArgProcessor {
|
||||
processedRecords.push(processedRecord);
|
||||
}
|
||||
|
||||
const overriddenPositionRecords =
|
||||
await this.recordPositionService.overridePositionOnRecords({
|
||||
partialRecordInputs: processedRecords,
|
||||
workspaceId: workspace.id,
|
||||
objectMetadata: {
|
||||
isCustom: flatObjectMetadata.isCustom,
|
||||
nameSingular: flatObjectMetadata.nameSingular,
|
||||
fieldIdByName,
|
||||
},
|
||||
shouldBackfillPositionIfUndefined,
|
||||
});
|
||||
|
||||
return overriddenPositionRecords;
|
||||
return processedRecords;
|
||||
}
|
||||
|
||||
private async processField(
|
||||
@@ -161,7 +158,7 @@ export class DataArgProcessor {
|
||||
): Promise<unknown> {
|
||||
switch (fieldMetadata.type) {
|
||||
case FieldMetadataType.POSITION:
|
||||
return value;
|
||||
return validateOverriddenPositionFieldOrThrow(value, key);
|
||||
case FieldMetadataType.NUMERIC: {
|
||||
const validatedValue = validateNumericFieldOrThrow(value, key);
|
||||
|
||||
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
import { validateOverriddenPositionFieldOrThrow } from 'src/engine/api/common/common-args-processors/data-arg-processor/validator-utils/validate-overridden-position-field-or-throw.util';
|
||||
|
||||
describe('validateOverriddenPositionFieldOrThrow', () => {
|
||||
describe('valid inputs', () => {
|
||||
it('should return the position when value is a positive integer', () => {
|
||||
const result = validateOverriddenPositionFieldOrThrow(123, 'testField');
|
||||
|
||||
expect(result).toBe(123);
|
||||
});
|
||||
|
||||
it('should return the position when value is zero', () => {
|
||||
const result = validateOverriddenPositionFieldOrThrow(0, 'testField');
|
||||
|
||||
expect(result).toBe(0);
|
||||
});
|
||||
|
||||
it('should return the position when value is negative integer', () => {
|
||||
const result = validateOverriddenPositionFieldOrThrow(-3, 'testField');
|
||||
|
||||
expect(result).toBe(-3);
|
||||
});
|
||||
|
||||
it('should return the position when value is negative float', () => {
|
||||
const result = validateOverriddenPositionFieldOrThrow(-0.5, 'testField');
|
||||
|
||||
expect(result).toBe(-0.5);
|
||||
});
|
||||
});
|
||||
|
||||
describe('invalid inputs', () => {
|
||||
it('should throw when value is null', () => {
|
||||
expect(() =>
|
||||
validateOverriddenPositionFieldOrThrow(null, 'testField'),
|
||||
).toThrow('Invalid position value null for field "testField"');
|
||||
});
|
||||
|
||||
it('should throw when value is undefined', () => {
|
||||
expect(() =>
|
||||
validateOverriddenPositionFieldOrThrow(undefined, 'testField'),
|
||||
).toThrow('Invalid position value undefined for field "testField"');
|
||||
});
|
||||
|
||||
it('should throw when value is a string with a number', () => {
|
||||
expect(() =>
|
||||
validateOverriddenPositionFieldOrThrow('123', 'testField'),
|
||||
).toThrow('Invalid position value \'123\' for field "testField"');
|
||||
});
|
||||
|
||||
it('should throw when value is an empty string', () => {
|
||||
expect(() =>
|
||||
validateOverriddenPositionFieldOrThrow('', 'testField'),
|
||||
).toThrow('Invalid position value \'\' for field "testField"');
|
||||
});
|
||||
|
||||
it('should throw when value is a boolean (true)', () => {
|
||||
expect(() =>
|
||||
validateOverriddenPositionFieldOrThrow(true, 'testField'),
|
||||
).toThrow('Invalid position value true for field "testField"');
|
||||
});
|
||||
|
||||
it('should throw when value is a boolean (false)', () => {
|
||||
expect(() =>
|
||||
validateOverriddenPositionFieldOrThrow(false, 'testField'),
|
||||
).toThrow('Invalid position value false for field "testField"');
|
||||
});
|
||||
|
||||
it('should throw when value is an array', () => {
|
||||
expect(() =>
|
||||
validateOverriddenPositionFieldOrThrow([1, 2, 3], 'testField'),
|
||||
).toThrow('Invalid position value [ 1, 2, 3 ] for field "testField"');
|
||||
});
|
||||
|
||||
it('should throw when value is an object', () => {
|
||||
expect(() =>
|
||||
validateOverriddenPositionFieldOrThrow({ key: 'value' }, 'testField'),
|
||||
).toThrow(
|
||||
'Invalid position value { key: \'value\' } for field "testField"',
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw when value is NaN', () => {
|
||||
expect(() =>
|
||||
validateOverriddenPositionFieldOrThrow(NaN, 'testField'),
|
||||
).toThrow('Invalid position value NaN for field "testField"');
|
||||
});
|
||||
});
|
||||
});
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
import { inspect } from 'util';
|
||||
|
||||
import {
|
||||
CommonQueryRunnerException,
|
||||
CommonQueryRunnerExceptionCode,
|
||||
} from 'src/engine/api/common/common-query-runners/errors/common-query-runner.exception';
|
||||
|
||||
export const validateOverriddenPositionFieldOrThrow = (
|
||||
value: unknown,
|
||||
fieldName: string,
|
||||
): number | null => {
|
||||
if (
|
||||
typeof value !== 'number' ||
|
||||
(typeof value === 'number' &&
|
||||
(isNaN(value) || value === Infinity || value === -Infinity))
|
||||
)
|
||||
throw new CommonQueryRunnerException(
|
||||
`Invalid position value ${inspect(value)} for field "${fieldName}"`,
|
||||
CommonQueryRunnerExceptionCode.INVALID_ARGS_DATA,
|
||||
);
|
||||
|
||||
return value;
|
||||
};
|
||||
+1
-1
@@ -48,8 +48,8 @@ import {
|
||||
import { PermissionsService } from 'src/engine/metadata-modules/permissions/permissions.service';
|
||||
import { UserRoleService } from 'src/engine/metadata-modules/user-role/user-role.service';
|
||||
import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager';
|
||||
import { WorkspaceCacheService } from 'src/engine/workspace-cache/services/workspace-cache.service';
|
||||
import type { RolePermissionConfig } from 'src/engine/twenty-orm/types/role-permission-config';
|
||||
import { WorkspaceCacheService } from 'src/engine/workspace-cache/services/workspace-cache.service';
|
||||
|
||||
@Injectable()
|
||||
export abstract class CommonBaseQueryRunnerService<
|
||||
|
||||
Reference in New Issue
Block a user