Fixed plain object in field value for workflow (#17470)
This PR fixes a bug that arises in `formatResult` following up the recent refactor for DATE_TIME : https://github.com/twentyhq/twenty/pull/17407 In the case of workflows, we pass a plain object to `formatResult` : ```ts { before: null, after: '2026-01-27T10:59:15.525Z' } ``` So a case has been added to handle this. @Weiko are we ok with this more restrictive else-if part in this util ? We could also just put a `continue` for unknown shapes.
This commit is contained in:
@@ -6,7 +6,7 @@ import {
|
||||
FieldMetadataType,
|
||||
compositeTypeDefinitions,
|
||||
} from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { isDefined, stringifySafely } from 'twenty-shared/utils';
|
||||
|
||||
import {
|
||||
DEFAULT_ARRAY_FIELD_NULL_EQUIVALENT_VALUE,
|
||||
@@ -194,7 +194,8 @@ export function formatResult<T>(
|
||||
| string
|
||||
| Date
|
||||
| null
|
||||
| undefined;
|
||||
| undefined
|
||||
| Record<string, unknown>;
|
||||
|
||||
if (!isDefined(rawUpdatedDateTime)) {
|
||||
continue;
|
||||
@@ -208,9 +209,16 @@ export function formatResult<T>(
|
||||
|
||||
// @ts-expect-error legacy noImplicitAny
|
||||
newData[dateTimeField.name] = dateIsoString;
|
||||
} else if (isPlainObject(rawUpdatedDateTime)) {
|
||||
const plainObjectValue = rawUpdatedDateTime;
|
||||
|
||||
// @ts-expect-error legacy noImplicitAny
|
||||
newData[dateTimeField.name] = plainObjectValue;
|
||||
} else {
|
||||
const stringifiedUnknownValue = stringifySafely(rawUpdatedDateTime);
|
||||
|
||||
throw new Error(
|
||||
`Invalid DATE_TIME field "${dateTimeField.name}", value: "${rawUpdatedDateTime}", it should be a string or Date instance, (current type : ${typeof rawUpdatedDateTime}).`,
|
||||
`Invalid DATE_TIME field "${dateTimeField.name}", value: "${stringifiedUnknownValue}", it should be a string, Date instance or plain object, (current type : ${typeof rawUpdatedDateTime}).`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -6,7 +6,7 @@ exports[`Create input validation - DATE_TIME Gql create input - failure DATE_TIM
|
||||
|
||||
exports[`Create input validation - DATE_TIME Gql create input - failure DATE_TIME - should fail with : {"dateTimeField":{}} 1`] = `"Invalid value {} for date or date-time field "dateTimeField""`;
|
||||
|
||||
exports[`Create input validation - DATE_TIME Gql create input - failure DATE_TIME - should fail with : {"dateTimeField":1} 1`] = `"Invalid DATE_TIME field "dateTimeField", value: "1", it should be a string or Date instance, (current type : number)."`;
|
||||
exports[`Create input validation - DATE_TIME Gql create input - failure DATE_TIME - should fail with : {"dateTimeField":1} 1`] = `"Invalid DATE_TIME field "dateTimeField", value: "1", it should be a string, Date instance or plain object, (current type : number)."`;
|
||||
|
||||
exports[`Create input validation - DATE_TIME Gql create input - failure DATE_TIME - should fail with : {"dateTimeField":true} 1`] = `"Invalid value true for date or date-time field "dateTimeField""`;
|
||||
|
||||
@@ -16,6 +16,6 @@ exports[`Create input validation - DATE_TIME Rest create input - failure DATE_TI
|
||||
|
||||
exports[`Create input validation - DATE_TIME Rest create input - failure DATE_TIME - should fail with : {"dateTimeField":{}} 1`] = `"["Invalid value {} for date or date-time field \\"dateTimeField\\""]"`;
|
||||
|
||||
exports[`Create input validation - DATE_TIME Rest create input - failure DATE_TIME - should fail with : {"dateTimeField":1} 1`] = `"["Invalid DATE_TIME field \\"dateTimeField\\", value: \\"1\\", it should be a string or Date instance, (current type : number)."]"`;
|
||||
exports[`Create input validation - DATE_TIME Rest create input - failure DATE_TIME - should fail with : {"dateTimeField":1} 1`] = `"["Invalid DATE_TIME field \\"dateTimeField\\", value: \\"1\\", it should be a string, Date instance or plain object, (current type : number)."]"`;
|
||||
|
||||
exports[`Create input validation - DATE_TIME Rest create input - failure DATE_TIME - should fail with : {"dateTimeField":true} 1`] = `"["Invalid value true for date or date-time field \\"dateTimeField\\""]"`;
|
||||
|
||||
@@ -146,6 +146,7 @@ export { getHumanReadableNameFromCode } from './sentry/getHumanReadableNameFromC
|
||||
export { appendCopySuffix } from './strings/appendCopySuffix';
|
||||
export { capitalize } from './strings/capitalize';
|
||||
export { pascalCase } from './strings/pascalCase';
|
||||
export { stringifySafely } from './strings/stringifySafely';
|
||||
export { uncapitalize } from './strings/uncapitalize';
|
||||
export type {
|
||||
TipTapMarkType,
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
import { stringifySafely } from '../stringifySafely';
|
||||
|
||||
describe('stringifySafely', () => {
|
||||
it('should stringify a simple object', () => {
|
||||
expect(stringifySafely({ foo: 'bar' })).toBe('{"foo":"bar"}');
|
||||
});
|
||||
|
||||
it('should stringify an array', () => {
|
||||
expect(stringifySafely([1, 2, 3])).toBe('[1,2,3]');
|
||||
});
|
||||
|
||||
it('should stringify a string', () => {
|
||||
expect(stringifySafely('hello')).toBe('"hello"');
|
||||
});
|
||||
|
||||
it('should stringify a number', () => {
|
||||
expect(stringifySafely(42)).toBe('42');
|
||||
});
|
||||
|
||||
it('should stringify null', () => {
|
||||
expect(stringifySafely(null)).toBe('null');
|
||||
});
|
||||
|
||||
it('should stringify undefined', () => {
|
||||
expect(stringifySafely(undefined)).toBe('undefined');
|
||||
});
|
||||
|
||||
it('should stringify a boolean', () => {
|
||||
expect(stringifySafely(true)).toBe('true');
|
||||
expect(stringifySafely(false)).toBe('false');
|
||||
});
|
||||
|
||||
it('should stringify +Infinity', () => {
|
||||
expect(stringifySafely(+Infinity)).toBe('Infinity');
|
||||
});
|
||||
|
||||
it('should stringify Infinity', () => {
|
||||
expect(stringifySafely(Infinity)).toBe('Infinity');
|
||||
});
|
||||
|
||||
it('should stringify -Infinity', () => {
|
||||
expect(stringifySafely(-Infinity)).toBe('-Infinity');
|
||||
});
|
||||
|
||||
it('should stringify NaN', () => {
|
||||
expect(stringifySafely(NaN)).toBe('NaN');
|
||||
});
|
||||
|
||||
it('should fall back to String() for circular references', () => {
|
||||
const circularObj: Record<string, unknown> = { foo: 'bar' };
|
||||
circularObj.self = circularObj;
|
||||
|
||||
expect(stringifySafely(circularObj)).toBe('[object Object]');
|
||||
});
|
||||
|
||||
it('should fall back to String() for BigInt values', () => {
|
||||
const bigIntValue = BigInt(9007199254740991);
|
||||
|
||||
expect(stringifySafely(bigIntValue)).toBe('9007199254740991');
|
||||
});
|
||||
|
||||
it('should fall back to String() for functions', () => {
|
||||
// eslint-disable-next-line func-style, prefer-arrow/prefer-arrow-functions
|
||||
const namedFunction = function myFunction() {
|
||||
return 'test';
|
||||
};
|
||||
|
||||
expect(stringifySafely(namedFunction)).toBe(namedFunction.toString());
|
||||
});
|
||||
|
||||
it('should fall back to String() for arrow functions', () => {
|
||||
const arrowFunction = () => 'test';
|
||||
|
||||
expect(stringifySafely(arrowFunction)).toBe(arrowFunction.toString());
|
||||
});
|
||||
|
||||
it('should fall back to String() for symbols', () => {
|
||||
const symbol = Symbol('testSymbol');
|
||||
|
||||
expect(stringifySafely(symbol)).toBe('Symbol(testSymbol)');
|
||||
});
|
||||
|
||||
it('should fall back to String() for symbols without description', () => {
|
||||
const symbol = Symbol();
|
||||
|
||||
expect(stringifySafely(symbol)).toBe('Symbol()');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,25 @@
|
||||
export const stringifySafely = (value: unknown): string => {
|
||||
try {
|
||||
if (value === undefined) {
|
||||
return 'undefined';
|
||||
} else if (value === null) {
|
||||
return 'null';
|
||||
} else if (value === Infinity) {
|
||||
return 'Infinity';
|
||||
} else if (value === -Infinity) {
|
||||
return '-Infinity';
|
||||
} else if (typeof value === 'number' && isNaN(value)) {
|
||||
return 'NaN';
|
||||
}
|
||||
|
||||
const stringifiedValue = JSON.stringify(value);
|
||||
|
||||
if (stringifiedValue === undefined) {
|
||||
return String(value);
|
||||
}
|
||||
|
||||
return stringifiedValue;
|
||||
} catch {
|
||||
return String(value);
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user