Skip to content

Commit 2652a08

Browse files
committed
add flexible date comparison for conditions
1 parent 76f39e9 commit 2652a08

File tree

3 files changed

+121
-8
lines changed

3 files changed

+121
-8
lines changed

packages/backend/src/apps/toolbox/__tests__/common/condition-is-true.test.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,41 @@ describe('Condition is true', () => {
9494
expect(result).toEqual(expectedResult)
9595
})
9696

97+
/**
98+
* Typical datetime formats:
99+
* yyyy-MM-dd HH:mm
100+
* dd MMM yyyy
101+
* dd/MM/yyyy HH:mm:ss
102+
* ISO format
103+
*/
104+
it.each([
105+
{ text: '2024-11-06 12:00', expectedResult: true },
106+
{ text: '03/11/2024 12:00:30', expectedResult: false },
107+
])('supports before', ({ text, expectedResult }) => {
108+
const result = conditionIsTrue({
109+
field: '04 Nov 2024',
110+
is: 'is',
111+
condition: 'before',
112+
text,
113+
})
114+
115+
expect(result).toEqual(expectedResult)
116+
})
117+
118+
it.each([
119+
{ text: '2024-11-06T12:00:00.500+08:00', expectedResult: false },
120+
{ text: '03 Nov 2024 23:59', expectedResult: true },
121+
])('supports after', ({ text, expectedResult }) => {
122+
const result = conditionIsTrue({
123+
field: '04 Nov 2024',
124+
is: 'is',
125+
condition: 'after',
126+
text,
127+
})
128+
129+
expect(result).toEqual(expectedResult)
130+
})
131+
97132
it.each([
98133
{ field: 'hello', text: 9.9, condition: 'equals', expectedResult: true },
99134
{ field: 10, text: 10, condition: 'gte', expectedResult: false },
@@ -135,6 +170,25 @@ describe('Condition is true', () => {
135170
},
136171
)
137172

173+
it.each([
174+
{ field: 10, condition: 'gte', text: 'abc' },
175+
{ field: 'abc', condition: 'lt', text: 10 },
176+
{ field: '04 Nov 2024', condition: 'before', text: 'abc' },
177+
{ field: 'abc', condition: 'before', text: '04 Nov 2024' },
178+
])(
179+
'throws an error for invalid field or value for comparison',
180+
({ field, condition, text }) => {
181+
expect(() =>
182+
conditionIsTrue({
183+
field,
184+
is: 'is',
185+
condition,
186+
text,
187+
}),
188+
).toThrowError()
189+
},
190+
)
191+
138192
it('throws an error for unsupported conditions', () => {
139193
expect(() =>
140194
conditionIsTrue({

packages/backend/src/apps/toolbox/common/condition-is-true.ts

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,14 @@ function compareNumbers(
66
value: IJSONValue,
77
): boolean {
88
// WARNING: can only compare safely up till Number.MAX_SAFE_INTEGER but BigInt cannot compare floats...
9-
if (isNaN(Number(field)) || isNaN(Number(value))) {
10-
throw new Error('Non-number used in field or value for comparison')
9+
if (isNaN(Number(field))) {
10+
throw new Error('Non-number used in field for comparison')
1111
}
12+
13+
if (isNaN(Number(value))) {
14+
throw new Error('Non-number used in value for comparison')
15+
}
16+
1217
switch (condition) {
1318
case 'gte':
1419
return Number(field) >= Number(value)
@@ -21,6 +26,29 @@ function compareNumbers(
2126
}
2227
}
2328

29+
function compareDates(
30+
field: IJSONValue,
31+
condition: 'before' | 'after',
32+
value: IJSONValue,
33+
) {
34+
const fieldTimestamp = new Date(field as string).getTime()
35+
if (isNaN(fieldTimestamp)) {
36+
throw new Error('Invalid date used in field for comparison')
37+
}
38+
39+
const valueTimestamp = new Date(value as string).getTime()
40+
if (isNaN(valueTimestamp)) {
41+
throw new Error('Invalid date used in value for comparison')
42+
}
43+
44+
switch (condition) {
45+
case 'before':
46+
return fieldTimestamp < valueTimestamp
47+
case 'after':
48+
return fieldTimestamp > valueTimestamp
49+
}
50+
}
51+
2452
export default function conditionIsTrue(conditionArgs: IJSONObject): boolean {
2553
// `value` is named `text` for legacy reasons.
2654
const { field, is, condition, text: value } = conditionArgs
@@ -30,6 +58,10 @@ export default function conditionIsTrue(conditionArgs: IJSONObject): boolean {
3058
case 'equals':
3159
result = field === value
3260
break
61+
case 'before':
62+
case 'after':
63+
result = compareDates(field, condition, value)
64+
break
3365
case 'gte':
3466
case 'gt':
3567
case 'lte':

packages/backend/src/apps/toolbox/common/get-condition-args.ts

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,39 @@ export default function getConditionArgs({
3939
variables: false,
4040
showOptionValue: false,
4141
options: [
42-
{ label: 'Equals to', value: 'equals' },
43-
{ label: 'Greater than ', value: 'gt' },
44-
{ label: 'Greater than or equals to', value: 'gte' },
45-
{ label: 'Less than', value: 'lt' },
46-
{ label: 'Less than or equals to', value: 'lte' },
47-
{ label: 'Contains', value: 'contains' },
42+
{
43+
label: 'Equals to',
44+
value: 'equals',
45+
description: 'Used for text values',
46+
},
47+
{
48+
label: 'Contains',
49+
value: 'contains',
50+
description: 'Used for text values',
51+
},
52+
53+
{ label: 'Greater than', value: 'gt', description: 'Used for numbers' },
54+
{
55+
label: 'Greater than or equals to',
56+
value: 'gte',
57+
description: 'Used for numbers',
58+
},
59+
{ label: 'Less than', value: 'lt', description: 'Used for numbers' },
60+
{
61+
label: 'Less than or equals to',
62+
value: 'lte',
63+
description: 'Used for numbers',
64+
},
65+
{
66+
label: 'Before',
67+
value: 'before',
68+
description: 'Used for dates',
69+
},
70+
{
71+
label: 'After',
72+
value: 'after',
73+
description: 'Used for dates',
74+
},
4875
{ label: 'Empty', value: 'empty' },
4976
],
5077
},

0 commit comments

Comments
 (0)