mirror of
https://github.com/n8n-io/n8n.git
synced 2025-03-05 20:50:17 -08:00
fix: Allow Date/Luxon objects and additional formats in DateTime validation (#8525)
This commit is contained in:
parent
76cdf75fb9
commit
c419c8592f
|
@ -52,6 +52,17 @@ export const tryToParseBoolean = (value: unknown): value is boolean => {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const tryToParseDateTime = (value: unknown): DateTime => {
|
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();
|
const dateString = String(value).trim();
|
||||||
|
|
||||||
// Rely on luxon to parse different date formats
|
// Rely on luxon to parse different date formats
|
||||||
|
@ -72,6 +83,11 @@ export const tryToParseDateTime = (value: unknown): DateTime => {
|
||||||
return sqlDate;
|
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 } });
|
throw new ApplicationError('Value is not a valid date', { extra: { dateString } });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { executeFilter } from '@/NodeParameters/FilterParameter';
|
import { executeFilter } from '@/NodeParameters/FilterParameter';
|
||||||
import type { FilterConditionValue, FilterValue } from '@/Interfaces';
|
import type { FilterConditionValue, FilterValue } from '@/Interfaces';
|
||||||
import merge from 'lodash/merge';
|
import merge from 'lodash/merge';
|
||||||
|
import { DateTime } from 'luxon';
|
||||||
|
|
||||||
type DeepPartial<T> = {
|
type DeepPartial<T> = {
|
||||||
[P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
|
[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: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-11-15T17:12:49.113Z', expected: false },
|
||||||
{ left: '2023-11-15T17:10:49.113Z', right: '2023-01-01T00:00:00.000Z', expected: true },
|
{ 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 }) => {
|
])('dateTime:after("$left", "$right") === $expected', ({ left, right, expected }) => {
|
||||||
const result = executeFilter(
|
const result = executeFilter(
|
||||||
filterFactory({
|
filterFactory({
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { validateFieldType } from '@/TypeValidation';
|
import { validateFieldType } from '@/TypeValidation';
|
||||||
import type { DateTime } from 'luxon';
|
import { DateTime } from 'luxon';
|
||||||
|
|
||||||
const VALID_ISO_DATES = [
|
const VALID_ISO_DATES = [
|
||||||
'1994-11-05T08:15:30-05:00',
|
'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 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 = [
|
const INVALID_DATES = [
|
||||||
'1994-11-05M08:15:30-05:00',
|
'1994-11-05M08:15:30-05:00',
|
||||||
'18-05-2020',
|
'18-05-2020',
|
||||||
'',
|
'',
|
||||||
'Wed, 17 May 2023 10:52:32',
|
|
||||||
'SMT, 17 May 2023 10:52:32',
|
|
||||||
'1685084980', // We are not supporting timestamps
|
'1685084980', // We are not supporting timestamps
|
||||||
'1685085012135',
|
'1685085012135',
|
||||||
1685084980,
|
1685084980,
|
||||||
|
@ -45,40 +51,38 @@ const INVALID_DATES = [
|
||||||
];
|
];
|
||||||
|
|
||||||
describe('Type Validation', () => {
|
describe('Type Validation', () => {
|
||||||
it('should validate and cast ISO dates', () => {
|
describe('Dates', () => {
|
||||||
VALID_ISO_DATES.forEach((date) => {
|
test.each(VALID_ISO_DATES)('should validate and cast ISO date "%s"', (date) => {
|
||||||
const validationResult = validateFieldType('date', date, 'dateTime');
|
const validationResult = validateFieldType('date', date, 'dateTime');
|
||||||
expect(validationResult.valid).toBe(true);
|
expect(validationResult.valid).toBe(true);
|
||||||
expect((validationResult.newValue as DateTime).isValid).toBe(true);
|
expect((validationResult.newValue as DateTime).isValid).toBe(true);
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
it('should validate and cast RFC 2822 dates', () => {
|
test.each(VALID_RFC_DATES)('should validate and cast RFC2822 date "%s"', (date) => {
|
||||||
VALID_RFC_DATES.forEach((date) => {
|
|
||||||
const validationResult = validateFieldType('date', date, 'dateTime');
|
const validationResult = validateFieldType('date', date, 'dateTime');
|
||||||
expect(validationResult.valid).toBe(true);
|
expect(validationResult.valid).toBe(true);
|
||||||
expect((validationResult.newValue as DateTime).isValid).toBe(true);
|
expect((validationResult.newValue as DateTime).isValid).toBe(true);
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
it('should validate and cast HTTP dates', () => {
|
test.each(VALID_HTTP_DATES)('should validate and cast HTTP date "%s"', (date) => {
|
||||||
VALID_HTTP_DATES.forEach((date) => {
|
|
||||||
const validationResult = validateFieldType('date', date, 'dateTime');
|
const validationResult = validateFieldType('date', date, 'dateTime');
|
||||||
expect(validationResult.valid).toBe(true);
|
expect(validationResult.valid).toBe(true);
|
||||||
expect((validationResult.newValue as DateTime).isValid).toBe(true);
|
expect((validationResult.newValue as DateTime).isValid).toBe(true);
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
it('should validate and cast SQL dates', () => {
|
test.each(VALID_SQL_DATES)('should validate and cast SQL date "%s"', (date) => {
|
||||||
VALID_SQL_DATES.forEach((date) => {
|
|
||||||
const validationResult = validateFieldType('date', date, 'dateTime');
|
const validationResult = validateFieldType('date', date, 'dateTime');
|
||||||
expect(validationResult.valid).toBe(true);
|
expect(validationResult.valid).toBe(true);
|
||||||
expect((validationResult.newValue as DateTime).isValid).toBe(true);
|
expect((validationResult.newValue as DateTime).isValid).toBe(true);
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
it('should not validate invalid dates', () => {
|
test.each(OTHER_VALID_DATES)('should validate and cast date "%s"', (date) => {
|
||||||
INVALID_DATES.forEach((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');
|
const validationResult = validateFieldType('date', date, 'dateTime');
|
||||||
expect(validationResult.valid).toBe(false);
|
expect(validationResult.valid).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue