Skip to content

Commit

Permalink
add generics support
Browse files Browse the repository at this point in the history
  • Loading branch information
regevbr committed Jun 9, 2018
1 parent 41364a7 commit 29d700f
Show file tree
Hide file tree
Showing 4 changed files with 158 additions and 102 deletions.
21 changes: 9 additions & 12 deletions example/lib/functionsFactory.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
'use strict';
Object.defineProperty(exports, "__esModule", { value: true });
const fs = require("fs");
const path = require("path");
const functionsDir = path.join(__dirname, '/functions');
const userFunc_1 = require("./functions/userFunc");
const counterFunc_1 = require("./functions/counterFunc");
const functionsTable = {};
exports.functionsTable = functionsTable;
fs.readdirSync(functionsDir).forEach((file) => {
if (file.endsWith('.js')) {
const data = require(path.join(functionsDir, file)).factory;
const description = data();
if (functionsTable[description.name]) {
throw new Error(`Function with name ${description.name} already exists`);
}
functionsTable[description.name] = description.evaluate;
const _addFunction = (description) => {
if (functionsTable[description.name]) {
throw new Error(`Function with name ${description.name} already exists`);
}
});
functionsTable[description.name] = description.evaluate;
};
_addFunction(userFunc_1.factory());
_addFunction(counterFunc_1.factory());
19 changes: 8 additions & 11 deletions example/lib/functionsFactory.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
'use strict';

import * as fs from 'fs';
import * as path from 'path';
import {Func, FunctionsTable} from "../../dist";
import {ExpressionContext} from "./evaluator";
import {factory as userFuncFactory} from './functions/userFunc';
import {factory as counterFuncFactory} from './functions/counterFunc';

export interface FunctionDescription {
name: string;
Expand All @@ -12,19 +12,16 @@ export interface FunctionDescription {

export type FuncFactory = () => FunctionDescription;

const functionsDir = path.join(__dirname, '/functions');

const functionsTable: FunctionsTable<ExpressionContext> = {};

fs.readdirSync(functionsDir).forEach((file) => {
if (file.endsWith('.js')) {
const data = require(path.join(functionsDir, file)).factory as FuncFactory;
const description = data();
const _addFunction = (description: FunctionDescription) : void => {
if (functionsTable[description.name]) {
throw new Error(`Function with name ${description.name} already exists`);
throw new Error(`Function with name ${description.name} already exists`);
}
functionsTable[description.name] = description.evaluate;
}
});
};

_addFunction(userFuncFactory());
_addFunction(counterFuncFactory());

export { functionsTable };
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "json-expression-eval",
"version": "1.1.1",
"version": "1.1.2",
"description": "evaluate a json described boolean expression using dynamic functions",
"main": "dist/index.js",
"types": "dist/index.d.ts",
Expand Down
218 changes: 140 additions & 78 deletions test/index.spec.ts
Original file line number Diff line number Diff line change
@@ -1,161 +1,223 @@
'use strict';

import { expect } from 'chai';
import { evaluate } from '../dist/index';
import {expect} from 'chai';
import {evaluate} from '../dist';

describe('evaluator', function() {
interface Context {
userId: string;
timesCounter: number;
}

describe('evaluator', function () {

const functionsTable = {
user: (user: string, context: {userId: string}): boolean => {
user: (user: string, context: { userId: string }): boolean => {
return context.userId === user;
}
};

it('should evaluate short eq compare op to true', function() {
expect(evaluate({ timesCounter: 5 }, { timesCounter: 5 }, functionsTable)).to.eql(true);
it('should evaluate short eq compare op to true', function () {
expect(evaluate<Context>({timesCounter: 5}, {timesCounter: 5, userId: 'a'}, functionsTable)).to.eql(true);
});

it('should evaluate short eq compare op to false', function() {
expect(evaluate({ timesCounter: 5 }, { timesCounter: 7 }, functionsTable)).to.eql(false);
it('should evaluate short eq compare op to false', function () {
expect(evaluate<Context>({timesCounter: 5}, {timesCounter: 7, userId: 'a'}, functionsTable)).to.eql(false);
});

it('should evaluate eq compare op to true', function() {
expect(evaluate({ timesCounter: {eq: 5} }, { timesCounter: 5 }, functionsTable)).to.eql(true);
it('should evaluate eq compare op to true', function () {
expect(evaluate<Context>({timesCounter: {eq: 5}}, {timesCounter: 5, userId: 'a'}, functionsTable)).to.eql(true);
});

it('should evaluate eq compare op to false', function() {
expect(evaluate({ timesCounter: {eq: 5} }, { timesCounter: 7 }, functionsTable)).to.eql(false);
it('should evaluate eq compare op to false', function () {
expect(evaluate<Context>({timesCounter: {eq: 5}}, {
timesCounter: 7,
userId: 'a'
}, functionsTable)).to.eql(false);
});

it('should evaluate neq compare op to true', function() {
expect(evaluate({ timesCounter: {neq: 5} }, { timesCounter: 8 }, functionsTable)).to.eql(true);
it('should evaluate neq compare op to true', function () {
expect(evaluate<Context>({timesCounter: {neq: 5}}, {
timesCounter: 8,
userId: 'a'
}, functionsTable)).to.eql(true);
});

it('should evaluate eq compare op to false', function() {
expect(evaluate({ timesCounter: {neq: 5} }, { timesCounter: 5 }, functionsTable)).to.eql(false);
it('should evaluate eq compare op to false', function () {
expect(evaluate<Context>({timesCounter: {neq: 5}}, {
timesCounter: 5,
userId: 'a'
}, functionsTable)).to.eql(false);
});

it('should evaluate gt compare op to true', function() {
expect(evaluate({ timesCounter: {gt: 5} }, { timesCounter: 8 }, functionsTable)).to.eql(true);
it('should evaluate gt compare op to true', function () {
expect(evaluate<Context>({timesCounter: {gt: 5}}, {timesCounter: 8, userId: 'a'}, functionsTable)).to.eql(true);
});

it('should evaluate gt compare op to false', function() {
expect(evaluate({ timesCounter: {gt: 5} }, { timesCounter: 3 }, functionsTable)).to.eql(false);
it('should evaluate gt compare op to false', function () {
expect(evaluate<Context>({timesCounter: {gt: 5}}, {
timesCounter: 3,
userId: 'a'
}, functionsTable)).to.eql(false);
});

it('should evaluate gt compare op to false 2', function() {
expect(evaluate({ timesCounter: {gt: 5} }, { timesCounter: 5 }, functionsTable)).to.eql(false);
it('should evaluate gt compare op to false 2', function () {
expect(evaluate<Context>({timesCounter: {gt: 5}}, {
timesCounter: 5,
userId: 'a'
}, functionsTable)).to.eql(false);
});

it('should evaluate gte compare op to true', function() {
expect(evaluate({ timesCounter: {gte: 5} }, { timesCounter: 8 }, functionsTable)).to.eql(true);
it('should evaluate gte compare op to true', function () {
expect(evaluate<Context>({timesCounter: {gte: 5}}, {
timesCounter: 8,
userId: 'a'
}, functionsTable)).to.eql(true);
});

it('should evaluate gte compare op to true 2', function() {
expect(evaluate({ timesCounter: {gte: 5} }, { timesCounter: 5 }, functionsTable)).to.eql(true);
it('should evaluate gte compare op to true 2', function () {
expect(evaluate<Context>({timesCounter: {gte: 5}}, {
timesCounter: 5,
userId: 'a'
}, functionsTable)).to.eql(true);
});

it('should evaluate gte compare op to false', function() {
expect(evaluate({ timesCounter: {gte: 5} }, { timesCounter: 3 }, functionsTable)).to.eql(false);
it('should evaluate gte compare op to false', function () {
expect(evaluate<Context>({timesCounter: {gte: 5}}, {
timesCounter: 3,
userId: 'a'
}, functionsTable)).to.eql(false);
});

it('should evaluate lt compare op to true', function() {
expect(evaluate({ timesCounter: {lt: 5} }, { timesCounter: 3 }, functionsTable)).to.eql(true);
it('should evaluate lt compare op to true', function () {
expect(evaluate<Context>({timesCounter: {lt: 5}}, {timesCounter: 3, userId: 'a'}, functionsTable)).to.eql(true);
});

it('should evaluate gt compare lt to false', function() {
expect(evaluate({ timesCounter: {lt: 5} }, { timesCounter: 8 }, functionsTable)).to.eql(false);
it('should evaluate gt compare lt to false', function () {
expect(evaluate<Context>({timesCounter: {lt: 5}}, {
timesCounter: 8,
userId: 'a'
}, functionsTable)).to.eql(false);
});

it('should evaluate lt compare op to false 2', function() {
expect(evaluate({ timesCounter: {lt: 5} }, { timesCounter: 5 }, functionsTable)).to.eql(false);
it('should evaluate lt compare op to false 2', function () {
expect(evaluate<Context>({timesCounter: {lt: 5}}, {
timesCounter: 5,
userId: 'a'
}, functionsTable)).to.eql(false);
});

it('should evaluate lte compare op to true', function() {
expect(evaluate({ timesCounter: {lte: 5} }, { timesCounter: 3 }, functionsTable)).to.eql(true);
it('should evaluate lte compare op to true', function () {
expect(evaluate<Context>({timesCounter: {lte: 5}}, {
timesCounter: 3,
userId: 'a'
}, functionsTable)).to.eql(true);
});

it('should evaluate lte compare op to true 2', function() {
expect(evaluate({ timesCounter: {lte: 5} }, { timesCounter: 5 }, functionsTable)).to.eql(true);
it('should evaluate lte compare op to true 2', function () {
expect(evaluate<Context>({timesCounter: {lte: 5}}, {
timesCounter: 5,
userId: 'a'
}, functionsTable)).to.eql(true);
});

it('should evaluate lte compare op to false', function() {
expect(evaluate({ timesCounter: {lte: 5} }, { timesCounter: 8 }, functionsTable)).to.eql(false);
it('should evaluate lte compare op to false', function () {
expect(evaluate<Context>({timesCounter: {lte: 5}}, {
timesCounter: 8,
userId: 'a'
}, functionsTable)).to.eql(false);
});

it('should evaluate a single function', function() {
expect(evaluate({ user: 'r@a.com' }, { userId: 'r@a.com' }, functionsTable)).to.eql(true);
it('should evaluate a single function', function () {
expect(evaluate<Context>({user: 'r@a.com'}, {userId: 'r@a.com', timesCounter: 8}, functionsTable)).to.eql(true);
});

it('should evaluate a single not function to false', function() {
expect(evaluate({ not: { user: 'r@a.com' } }, { userId: 'r@a.com' }, functionsTable)).to.eql(false);
it('should evaluate a single not function to false', function () {
expect(evaluate<Context>({not: {user: 'r@a.com'}}, {
userId: 'r@a.com',
timesCounter: 8
}, functionsTable)).to.eql(false);
});

it('should evaluate a single not function to true', function() {
expect(evaluate({ not: { user: 'a@a.com' } }, { userId: 'r@a.com' }, functionsTable)).to.eql(true);
it('should evaluate a single not function to true', function () {
expect(evaluate<Context>({not: {user: 'a@a.com'}}, {
userId: 'r@a.com',
timesCounter: 8
}, functionsTable)).to.eql(true);
});

it('should evaluate a single and function to true', function() {
expect(evaluate({ and: [{ user: 'r@a.com' }] }, { userId: 'r@a.com' }, functionsTable)).to.eql(true);
it('should evaluate a single and function to true', function () {
expect(evaluate<Context>({and: [{user: 'r@a.com'}]}, {
userId: 'r@a.com',
timesCounter: 8
}, functionsTable)).to.eql(true);
});

it('should evaluate a single or function to true', function() {
expect(evaluate({ or: [{ user: 'r@a.com' }] }, { userId: 'r@a.com' }, functionsTable)).to.eql(true);
it('should evaluate a single or function to true', function () {
expect(evaluate<Context>({or: [{user: 'r@a.com'}]}, {
userId: 'r@a.com',
timesCounter: 8
}, functionsTable)).to.eql(true);
});

it('should evaluate a single and function to false', function() {
expect(evaluate({ and: [{ user: 't@a.com' }] }, { userId: 'r@a.com' }, functionsTable)).to.eql(false);
it('should evaluate a single and function to false', function () {
expect(evaluate<Context>({and: [{user: 't@a.com'}]}, {
userId: 'r@a.com',
timesCounter: 8
}, functionsTable)).to.eql(false);
});

it('should evaluate a single or function to false', function() {
expect(evaluate({ or: [{ user: 't@a.com' }] }, { userId: 'r@a.com' }, functionsTable)).to.eql(false);
it('should evaluate a single or function to false', function () {
expect(evaluate<Context>({or: [{user: 't@a.com'}]}, {
userId: 'r@a.com',
timesCounter: 8
}, functionsTable)).to.eql(false);
});

it('should fail on empty or op', function() {
expect(function() {
evaluate({ or: [] }, { userId: 'r@a.com' }, functionsTable);
it('should fail on empty or op', function () {
expect(function () {
evaluate<Context>({or: []}, {userId: 'r@a.com', timesCounter: 8}, functionsTable);
}).to.throw('Invalid expression - or operator must have at least one expression');
});

it('should fail on empty and op', function() {
expect(function() {
evaluate({ and: [] }, { userId: 'r@a.com' }, functionsTable);
it('should fail on empty and op', function () {
expect(function () {
evaluate<Context>({and: []}, {userId: 'r@a.com', timesCounter: 8}, functionsTable);
}).to.throw('Invalid expression - and operator must have at least one expression');
});

it('should fail on non existing function', function() {
expect(function() {
evaluate({ and: [{ dummy: 1 }] }, { userId: 'r@a.com' }, functionsTable);
it('should fail on non existing function', function () {
expect(function () {
evaluate<Context>({and: [{dummy: 1}]}, {userId: 'r@a.com', timesCounter: 8}, functionsTable);
}).to.throw('Invalid expression - unknown function dummy');
});

it('should fail on non existing function 2', function() {
expect(function() {
evaluate({ dummy: 1 }, { userId: 'r@a.com' }, functionsTable);
it('should fail on non existing function 2', function () {
expect(function () {
evaluate<Context>({dummy: 1}, {userId: 'r@a.com', timesCounter: 8}, functionsTable);
}).to.throw('Invalid expression - unknown function dummy');
});

it('should fail on non existing op', function() {
expect(function() {
evaluate({ userId: {bk : 1} }, { userId: 'r@a.com' }, functionsTable);
it('should fail on non existing op', function () {
expect(function () {
evaluate<Context>({userId: {bk: 1}}, {userId: 'r@a.com', timesCounter: 8}, functionsTable);
}).to.throw('Invalid expression - unknown op bk');
});

it('should fail too many keys to op', function() {
expect(function() {
evaluate({ userId: {eq : 1, nq: 2} }, { userId: 'r@a.com' }, functionsTable);
it('should fail too many keys to op', function () {
expect(function () {
evaluate<Context>({userId: {eq: 1, nq: 2}}, {userId: 'r@a.com', timesCounter: 8}, functionsTable);
}).to.throw('Invalid expression - too may keys');
});

it('should fail too many keys', function() {
expect(function() {
evaluate({ dummy: 1, user: 'a' }, { userId: 'r@a.com' }, functionsTable);
it('should fail too many keys', function () {
expect(function () {
evaluate<Context>({dummy: 1, user: 'a'}, {userId: 'r@a.com', timesCounter: 8}, functionsTable);
}).to.throw('Invalid expression - too may keys');
});

it('should fail too many keys 2', function() {
expect(function() {
evaluate({ or: [{ dummy: 1, user: 'a' }] }, { userId: 'r@a.com' }, functionsTable);
it('should fail too many keys 2', function () {
expect(function () {
evaluate<Context>({or: [{dummy: 1, user: 'a'}]}, {userId: 'r@a.com', timesCounter: 8}, functionsTable);
}).to.throw('Invalid expression - too may keys');
});
});

0 comments on commit 29d700f

Please sign in to comment.