Skip to content

Commit

Permalink
Merge pull request #32 from time-loop/mike-m/CLK-578056/improve-no-da…
Browse files Browse the repository at this point in the history
…te-handling
  • Loading branch information
mysza authored Dec 17, 2024
2 parents 070d787 + 68788da commit 5d0adc3
Show file tree
Hide file tree
Showing 14 changed files with 70 additions and 34 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@time-loop/hot-formula-parser",
"version": "4.2.0",
"version": "4.3.0",
"description": "Formula parser",
"type": "commonjs",
"main": "dist/index.js",
Expand Down
13 changes: 5 additions & 8 deletions src/clickup/formulajsProxy.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
import * as formulajs from '@formulajs/formulajs';

const hasNil = (...args: unknown[]) =>
args.some((arg) => arg === null || arg === undefined || arg === '' || arg === false);
const nullToZero = (arg: unknown) => (hasNil(arg) ? 0 : arg);
import { hasNil, nilToZero } from './utils';

const overrides = {
DATE: (year: unknown, month: unknown, day: unknown) =>
Expand All @@ -14,11 +11,11 @@ const overrides = {
DAYS360: (startDate: unknown, endDate: unknown, method: unknown) =>
hasNil(startDate, endDate) ? Number.NaN : formulajs.DAYS360(startDate, endDate, method),
EDATE: (startDate: unknown, months: unknown) =>
hasNil(startDate) ? Number.NaN : formulajs.EDATE(startDate, nullToZero(months)),
hasNil(startDate) ? Number.NaN : formulajs.EDATE(startDate, nilToZero(months)),
EOMONTH: (startDate: unknown, months: unknown) =>
hasNil(startDate) ? Number.NaN : formulajs.EOMONTH(startDate, nullToZero(months)),
hasNil(startDate) ? Number.NaN : formulajs.EOMONTH(startDate, nilToZero(months)),
HOUR: (date: unknown) => (hasNil(date) ? Number.NaN : formulajs.HOUR(date)),
INTERVAL: (seconds: unknown) => formulajs.INTERVAL(nullToZero(seconds)),
INTERVAL: (seconds: unknown) => formulajs.INTERVAL(nilToZero(seconds)),
ISOWEEKNUM: (date: unknown) => (hasNil(date) ? Number.NaN : formulajs.ISOWEEKNUM(date)),
MINUTE: (serialNumber: unknown) => (hasNil(serialNumber) ? Number.NaN : formulajs.MINUTE(serialNumber)),
MONTH: (date: unknown) => (hasNil(date) ? Number.NaN : formulajs.MONTH(date)),
Expand All @@ -34,7 +31,7 @@ const overrides = {
YEARFRAC: (startDate: unknown, endDate: unknown, basis: unknown) =>
hasNil(startDate, endDate) ? Number.NaN : formulajs.YEARFRAC(startDate, endDate, basis),
WORKDAY: (startDate: unknown, days: unknown, holidays: unknown) =>
hasNil(startDate) ? Number.NaN : formulajs.WORKDAY(startDate, nullToZero(days), holidays),
hasNil(startDate) ? Number.NaN : formulajs.WORKDAY(startDate, nilToZero(days), holidays),
NETWORKDAYS: (startDate: unknown, endDate: unknown, holidays: unknown) =>
hasNil(startDate, endDate) ? Number.NaN : formulajs.NETWORKDAYS(startDate, endDate, holidays),
};
Expand Down
4 changes: 4 additions & 0 deletions src/clickup/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export const hasNil = (...args: unknown[]) =>
args.some((arg) => arg === null || arg === undefined || arg === '' || arg === false);

export const nilToZero = (arg: unknown) => (hasNil(arg) ? 0 : arg);
4 changes: 3 additions & 1 deletion src/evaluate-by-operator/operator/add.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { toNumber } from '../../helper/number';
import { ERROR_VALUE } from '../../error';
import { nilToZero } from '../../clickup/utils';

export const SYMBOL = '+';

export default function func(first, ...rest) {
const result = rest.reduce((acc, value) => acc + toNumber(value), toNumber(first));
// in addition, we convert unset values to zero
const result = rest.reduce((acc, value) => acc + toNumber(nilToZero(value)), toNumber(nilToZero(first)));

if (isNaN(result)) {
throw Error(ERROR_VALUE);
Expand Down
6 changes: 6 additions & 0 deletions src/evaluate-by-operator/operator/divide.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
import { toNumber } from '../../helper/number';
import { ERROR_DIV_ZERO, ERROR_VALUE } from '../../error';
import { hasNil } from '../../clickup/utils';

export const SYMBOL = '/';

export default function func(first, ...rest) {
if (hasNil(first, ...rest)) {
// some of the arguments are unset, return NaN
return Number.NaN;
}

const result = rest.reduce((acc, value) => acc / Number(toNumber(value)), Number(toNumber(first)));

if (result === Infinity) {
Expand Down
15 changes: 12 additions & 3 deletions src/evaluate-by-operator/operator/minus.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { toNumber } from '../../helper/number';
import { ERROR_VALUE } from '../../error';
import ClickUpConfiguration from '../../clickup.config';
import { ERROR_VALUE } from '../../error';
import { hasNil } from '../../clickup/utils';

export const SYMBOL = '-';

Expand All @@ -10,12 +11,20 @@ export default function func(first, ...rest) {
convertFormulasInNumbers: ClickUpConfiguration.ConvertFormulasInNumbers,
};

if (hasNil(first, ...rest)) {
// some of the arguments are unset, return NaN
return Number.NaN;
}

const result = rest.reduce((acc, value) => {
const subtrahend = toNumber(value, toNumberConfig);
return acc - (subtrahend || 0);
if (Number.isNaN(subtrahend)) {
return Number.NaN;
}
return acc - subtrahend;
}, toNumber(first, toNumberConfig));

if (isNaN(result)) {
if (Number.isNaN(result)) {
throw Error(ERROR_VALUE);
}

Expand Down
6 changes: 6 additions & 0 deletions src/evaluate-by-operator/operator/multiply.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
import { toNumber } from '../../helper/number';
import { ERROR_VALUE } from '../../error';
import { hasNil } from '../../clickup/utils';

export const SYMBOL = '*';

export default function func(first, ...rest) {
if (hasNil(first, ...rest)) {
// some of the arguments are unset, return NaN
return Number.NaN;
}

const result = rest.reduce((acc, value) => {
const num = toNumber(value);
if (num === undefined) {
Expand Down
6 changes: 6 additions & 0 deletions src/evaluate-by-operator/operator/power.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
import { toNumber } from '../../helper/number';
import { ERROR_VALUE } from '../../error';
import { hasNil } from '../../clickup/utils';

export const SYMBOL = '^';

export default function func(exp1, exp2) {
if (hasNil(exp1, exp2)) {
// some of the arguments are unset, return NaN
return Number.NaN;
}

const exp1Number = toNumber(exp1);
const exp2Number = toNumber(exp2);
if (exp1Number === undefined || exp2Number === undefined) {
Expand Down
2 changes: 1 addition & 1 deletion src/helper/number.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export function toNumber(
return getNumberOfDaysSinceEpoch(value);
}

return undefined;
return Number.NaN;
}

/**
Expand Down
4 changes: 2 additions & 2 deletions test/unit/evaluate-by-operator/operator/add.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ describe('add operator', () => {
expect(func(2, undefined)).toBe(2);
expect(func(null, 2)).toBe(2);
expect(func(undefined, 2)).toBe(2);
expect(func(2, '')).toBe(2);
expect(func('', 2)).toBe(2);
expect(() => func('foo', ' ', 'bar', ' baz')).toThrow('VALUE');
expect(() => func('foo', 2)).toThrow('VALUE');
expect(() => func(2, '')).toThrow('VALUE');
expect(() => func('', 2)).toThrow('VALUE');
});

describe('ClickUp Overrides', () => {
Expand Down
12 changes: 6 additions & 6 deletions test/unit/evaluate-by-operator/operator/divide.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ describe('divide operator', () => {
expect(func('2', 8.8)).toBe(0.22727272727272727);
expect(func('2', '-8.8', 6, 0.4)).toBe(-0.0946969696969697);
expect(func(0, 1)).toBe(0);
expect(func(null, 2)).toBe(0);
expect(func(undefined, 2)).toBe(0);
expect(func(null, 2)).toBe(Number.NaN);
expect(func(undefined, 2)).toBe(Number.NaN);
expect(func('', 2)).toBe(Number.NaN);
expect(func(2, '')).toBe(Number.NaN);
expect(func(2, null)).toBe(Number.NaN);
expect(func(2, undefined)).toBe(Number.NaN);
expect(() => func(1, 0)).toThrow('DIV/0');
expect(() => func('foo', ' ', 'bar', ' baz')).toThrow('VALUE');
expect(() => func('', 2)).toThrow('VALUE');
expect(() => func(2, null)).toThrow('DIV/0');
expect(() => func(2, undefined)).toThrow('DIV/0');
expect(() => func(2, '')).toThrow('VALUE');
});
});
6 changes: 6 additions & 0 deletions test/unit/evaluate-by-operator/operator/minus.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ describe('minus operator', () => {
expect(func('2', 8.8)).toBe(-6.800000000000001);
expect(func('2', '8.8')).toBe(-6.800000000000001);
expect(func('2', '-8.8', 6, 0.4)).toBe(4.4);
expect(func(null, null)).toBe(Number.NaN);
expect(func(null, false)).toBe(Number.NaN);
expect(func(false, false)).toBe(Number.NaN);
expect(func(false, null)).toBe(Number.NaN);
expect(func(null, 'foo')).toBe(Number.NaN);
expect(func('foo', null)).toBe(Number.NaN);
expect(() => func('foo', ' ', 'bar', ' baz')).toThrow('VALUE');
expect(() => func('foo', 2)).toThrow('VALUE');
});
Expand Down
12 changes: 6 additions & 6 deletions test/unit/evaluate-by-operator/operator/multiply.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ describe('multiply operator', () => {
expect(func('2', 8.8)).toBe(17.6);
expect(func('2', '8.8')).toBe(17.6);
expect(func('2', '-8.8', 6, 0.4)).toBe(-42.24000000000001);
expect(func(2, null)).toBe(0);
expect(func(2, undefined)).toBe(0);
expect(func(null, 2)).toBe(0);
expect(func(undefined, 2)).toBe(0);
expect(func(2, null)).toBe(Number.NaN);
expect(func(2, undefined)).toBe(Number.NaN);
expect(func(null, 2)).toBe(Number.NaN);
expect(func(undefined, 2)).toBe(Number.NaN);
expect(func(2, '')).toBe(Number.NaN);
expect(func('', 2)).toBe(Number.NaN);
expect(() => func('foo', ' ', 'bar', ' baz')).toThrow('VALUE');
expect(() => func('foo', 2)).toThrow('VALUE');
expect(() => func(2, '')).toThrow('VALUE');
expect(() => func('', 2)).toThrow('VALUE');
});
});
12 changes: 6 additions & 6 deletions test/unit/evaluate-by-operator/operator/power.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ describe('power operator', () => {
expect(func('2', 8.8)).toBe(445.7218884076158);
expect(func('2', '8.8')).toBe(445.7218884076158);
expect(func('2', '8.8')).toBe(445.7218884076158);
expect(func(2, null)).toBe(1);
expect(func(2, undefined)).toBe(1);
expect(func(null, 2)).toBe(0);
expect(func(undefined, 2)).toBe(0);
expect(func(2, null)).toBe(Number.NaN);
expect(func(2, undefined)).toBe(Number.NaN);
expect(func(null, 2)).toBe(Number.NaN);
expect(func(undefined, 2)).toBe(Number.NaN);
expect(func(2, '')).toBe(Number.NaN);
expect(func('', 2)).toBe(Number.NaN);
expect(() => func('foo', ' ')).toThrow('VALUE');
expect(() => func('foo', 2)).toThrow('VALUE');
expect(() => func(2, '')).toThrow('VALUE');
expect(() => func('', 2)).toThrow('VALUE');
});
});

0 comments on commit 5d0adc3

Please sign in to comment.