fix: Allow Date/Luxon objects and additional formats in DateTime validation (#8525)

This commit is contained in:
Elias Meire 2024-02-02 11:27:31 +01:00 committed by GitHub
parent 76cdf75fb9
commit c419c8592f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 44 additions and 17 deletions

View file

@ -52,6 +52,17 @@ export const tryToParseBoolean = (value: unknown): value is boolean => {
};
export const tryToParseDateTime = (value: unknown): DateTime => {
if (value instanceof DateTime && value.isValid) {
return value;
}
if (value instanceof Date) {
const fromJSDate = DateTime.fromJSDate(value);
if (fromJSDate.isValid) {
return fromJSDate;
}
}
const dateString = String(value).trim();
// Rely on luxon to parse different date formats
@ -72,6 +83,11 @@ export const tryToParseDateTime = (value: unknown): DateTime => {
return sqlDate;
}
const parsedDateTime = DateTime.fromMillis(Date.parse(dateString));
if (parsedDateTime.isValid) {
return parsedDateTime;
}
throw new ApplicationError('Value is not a valid date', { extra: { dateString } });
};

View file

@ -1,6 +1,7 @@
import { executeFilter } from '@/NodeParameters/FilterParameter';
import type { FilterConditionValue, FilterValue } from '@/Interfaces';
import merge from 'lodash/merge';
import { DateTime } from 'luxon';
type DeepPartial<T> = {
[P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
@ -676,6 +677,12 @@ describe('FilterParameter', () => {
{ left: '2023-11-15T17:10:49.113Z', right: '2023-11-15T17:10:49.113Z', expected: false },
{ left: '2023-11-15T17:10:49.113Z', right: '2023-11-15T17:12:49.113Z', expected: false },
{ left: '2023-11-15T17:10:49.113Z', right: '2023-01-01T00:00:00.000Z', expected: true },
{ left: '2024-01-01', right: new Date('2023-01-01T00:00:00.000Z'), expected: true },
{
left: DateTime.fromFormat('2024-01-01', 'yyyy-MM-dd'),
right: '1-Feb-2024',
expected: false,
},
])('dateTime:after("$left", "$right") === $expected', ({ left, right, expected }) => {
const result = executeFilter(
filterFactory({

View file

@ -1,5 +1,5 @@
import { validateFieldType } from '@/TypeValidation';
import type { DateTime } from 'luxon';
import { DateTime } from 'luxon';
const VALID_ISO_DATES = [
'1994-11-05T08:15:30-05:00',
@ -30,12 +30,18 @@ const VALID_RFC_DATES = [
const VALID_SQL_DATES = ['2008-11-11', '2008-11-11 13:23:44'];
const OTHER_VALID_DATES = [
'Wed, 17 May 2023 10:52:32',
'SMT, 17 May 2023 10:52:32',
'1-Feb-2024',
new Date(),
DateTime.now(),
];
const INVALID_DATES = [
'1994-11-05M08:15:30-05:00',
'18-05-2020',
'',
'Wed, 17 May 2023 10:52:32',
'SMT, 17 May 2023 10:52:32',
'1685084980', // We are not supporting timestamps
'1685085012135',
1685084980,
@ -45,40 +51,38 @@ const INVALID_DATES = [
];
describe('Type Validation', () => {
it('should validate and cast ISO dates', () => {
VALID_ISO_DATES.forEach((date) => {
describe('Dates', () => {
test.each(VALID_ISO_DATES)('should validate and cast ISO date "%s"', (date) => {
const validationResult = validateFieldType('date', date, 'dateTime');
expect(validationResult.valid).toBe(true);
expect((validationResult.newValue as DateTime).isValid).toBe(true);
});
});
it('should validate and cast RFC 2822 dates', () => {
VALID_RFC_DATES.forEach((date) => {
test.each(VALID_RFC_DATES)('should validate and cast RFC2822 date "%s"', (date) => {
const validationResult = validateFieldType('date', date, 'dateTime');
expect(validationResult.valid).toBe(true);
expect((validationResult.newValue as DateTime).isValid).toBe(true);
});
});
it('should validate and cast HTTP dates', () => {
VALID_HTTP_DATES.forEach((date) => {
test.each(VALID_HTTP_DATES)('should validate and cast HTTP date "%s"', (date) => {
const validationResult = validateFieldType('date', date, 'dateTime');
expect(validationResult.valid).toBe(true);
expect((validationResult.newValue as DateTime).isValid).toBe(true);
});
});
it('should validate and cast SQL dates', () => {
VALID_SQL_DATES.forEach((date) => {
test.each(VALID_SQL_DATES)('should validate and cast SQL date "%s"', (date) => {
const validationResult = validateFieldType('date', date, 'dateTime');
expect(validationResult.valid).toBe(true);
expect((validationResult.newValue as DateTime).isValid).toBe(true);
});
});
it('should not validate invalid dates', () => {
INVALID_DATES.forEach((date) => {
test.each(OTHER_VALID_DATES)('should validate and cast date "%s"', (date) => {
const validationResult = validateFieldType('date', date, 'dateTime');
expect(validationResult.valid).toBe(true);
expect((validationResult.newValue as DateTime).isValid).toBe(true);
});
test.each(INVALID_DATES)('should not validate invalid date "%s"', (date) => {
const validationResult = validateFieldType('date', date, 'dateTime');
expect(validationResult.valid).toBe(false);
});