New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

odata-v4-inmemory

Package Overview
Dependencies
Maintainers
1
Versions
10
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

odata-v4-inmemory - npm Package Compare versions

Comparing version 0.1.3 to 0.1.4

32

lib/FilterVisitor.d.ts

@@ -15,2 +15,21 @@ import { Token } from 'odata-v4-parser/lib/lexer';

substring: (v: any, i: any) => any;
contains: (v: any, i: any) => any;
endswith: (v: any, i: any) => any;
startswith: (v: any, i: any) => any;
length: (v: any) => any;
tolower: (v: any) => any;
toupper: (v: any) => any;
trim: (v: any) => any;
concat: (v: any, i: any) => any;
year: (v: any) => any;
month: (v: any) => any;
day: (v: any) => any;
hour: (v: any) => any;
minute: (v: any) => any;
second: (v: any) => any;
now: () => Date;
maxdatetime: () => Date;
mindatetime: () => Date;
floor: (v: any) => number;
ceiling: (v: any) => number;
};

@@ -28,8 +47,16 @@ export declare class FilterVisitor implements VisitorMap {

protected VisitFilter(node: Token, context: any): (a: any) => boolean;
protected VisitNotExpression(node: Token, context: any): (a: any) => boolean;
protected VisitEqualsExpression(node: Token, context: any): (a: any) => boolean;
protected VisitNotEqualsExpression(node: Token, context: any): (a: any) => boolean;
protected VisitGreaterThanExpression(node: Token, context: any): (a: any) => boolean;
protected VisitLesserThanExpression(node: Token, context: any): (a: any) => boolean;
protected VisitLesserOrEqualsExpression(node: Token, context: any): (a: any) => boolean;
protected VisitGreaterOrEqualsExpression(node: Token, context: any): (a: any) => boolean;
protected VisitImplicitVariableExpression(node: Token, context: any): (a: any) => any;
protected VisitAndExpression(node: Token, context: any): (a: any) => any;
protected VisitAddExpression(node: Token, context: any): (a: any) => any;
protected VisitSubExpression(node: Token, context: any): (a: any) => number;
protected VisitMulExpression(node: Token, context: any): (a: any) => number;
protected VisitDivExpression(node: Token, context: any): (a: any) => number;
protected VisitModExpression(node: Token, context: any): (a: any) => number;
protected VisitParenExpression(node: Token, context: any): (a: any) => any;

@@ -40,3 +67,8 @@ protected getLiteral(node: Token): any;

protected VisitODataIdentifier(node: Token, context: any): (a: any) => any;
protected VisitIsOfExpression(node: Token, context: any): (a: any) => boolean;
protected VisitOrExpression(node: Token, context: any): (a: any) => any;
private resolveIdentifier(node);
protected VisitIdentifier(node: any, context: any): (a: any) => any;
protected VisitArray(node: Token, context: any): (a: any) => any[];
protected VisitNegateExpression(node: Token, context: any): (a: any) => number;
}

133

lib/FilterVisitor.js
"use strict";
var lexer_1 = require('odata-v4-parser/lib/lexer');
var minDateTime = new Date(-8640000000000000);
var maxDateTime = new Date(8640000000000000);
exports.ODataMethodMap = {
round: function (v) { return Math.round(v); },
indexof: function (v, i) { return v.indexOf && v.indexOf(i); },
substring: function (v, i) { return v.substr(i - 1); }
substring: function (v, i) { return v.substr(i - 1); },
contains: function (v, i) { return v.includes(i); },
endswith: function (v, i) { return v.endsWith(i); },
startswith: function (v, i) { return v.startsWith(i); },
length: function (v) { return v.length; },
tolower: function (v) { return v.toLowerCase(); },
toupper: function (v) { return v.toUpperCase(); },
trim: function (v) { return v.trim(); },
concat: function (v, i) { return v.concat(i); },
year: function (v) { return v.getFullYear(); },
month: function (v) { return v.getMonth() + 1; },
day: function (v) { return v.getDate(); },
hour: function (v) { return v.getHours(); },
minute: function (v) { return v.getMinutes(); },
second: function (v) { return v.getSeconds(); },
now: function () { return new Date(); },
maxdatetime: function () { return maxDateTime; },
mindatetime: function () { return minDateTime; },
floor: function (v) { return Math.floor(v); },
ceiling: function (v) { return Math.ceil(v); }
};

@@ -13,2 +34,5 @@ var FilterVisitor = (function () {

//console.log("Visiting: ", node.type)
if (!node) {
throw new Error("Node cannot be empty");
}
switch (node.type) {

@@ -22,2 +46,3 @@ //these are auto handled by visitor bubbling

case lexer_1.TokenType.CommonExpression:
case lexer_1.TokenType.ArrayOrObject:
case undefined:

@@ -67,2 +92,6 @@ break;

};
FilterVisitor.prototype.VisitNotExpression = function (node, context) {
var expression = this.Visit(node.value, context);
return function (a) { return !expression(a); };
};
FilterVisitor.prototype.VisitEqualsExpression = function (node, context) {

@@ -72,2 +101,6 @@ var _a = this.VisitBinaryExpression(node, context), left = _a[0], right = _a[1];

};
FilterVisitor.prototype.VisitNotEqualsExpression = function (node, context) {
var _a = this.VisitBinaryExpression(node, context), left = _a[0], right = _a[1];
return function (a) { return left(a) !== right(a); };
};
FilterVisitor.prototype.VisitGreaterThanExpression = function (node, context) {

@@ -81,2 +114,10 @@ var _a = this.VisitBinaryExpression(node, context), left = _a[0], right = _a[1];

};
FilterVisitor.prototype.VisitLesserOrEqualsExpression = function (node, context) {
var _a = this.VisitBinaryExpression(node, context), left = _a[0], right = _a[1];
return function (a) { return left(a) <= right(a); };
};
FilterVisitor.prototype.VisitGreaterOrEqualsExpression = function (node, context) {
var _a = this.VisitBinaryExpression(node, context), left = _a[0], right = _a[1];
return function (a) { return left(a) >= right(a); };
};
FilterVisitor.prototype.VisitImplicitVariableExpression = function (node, context) {

@@ -93,2 +134,18 @@ return function (a) { return a; };

};
FilterVisitor.prototype.VisitSubExpression = function (node, context) {
var _a = this.VisitBinaryExpression(node, context), left = _a[0], right = _a[1];
return function (a) { return left(a) - right(a); };
};
FilterVisitor.prototype.VisitMulExpression = function (node, context) {
var _a = this.VisitBinaryExpression(node, context), left = _a[0], right = _a[1];
return function (a) { return left(a) * right(a); };
};
FilterVisitor.prototype.VisitDivExpression = function (node, context) {
var _a = this.VisitBinaryExpression(node, context), left = _a[0], right = _a[1];
return function (a) { return left(a) / right(a); };
};
FilterVisitor.prototype.VisitModExpression = function (node, context) {
var _a = this.VisitBinaryExpression(node, context), left = _a[0], right = _a[1];
return function (a) { return left(a) % right(a); };
};
FilterVisitor.prototype.VisitParenExpression = function (node, context) {

@@ -100,5 +157,41 @@ var fn = this.Visit(node.value, context);

switch (node.value) {
case 'null': return null;
case "Edm.SByte": return parseInt(node.raw);
case "Edm.Decimal": return parseFloat(node.raw);
case "Edm.Double": switch (node.raw) {
case 'INF': return Infinity;
default: return parseFloat(node.raw);
}
case "Edm.Boolean": return node.raw === "true";
case "Edm.String": return node.raw.replace(/'/g, '');
//todo: check if string is actually a valid literal type
case "string":
case "Edm.String":
return node.raw.replace(/'/g, '').replace(/\"/g, "");
case "Edm.DateTimeOffset":
case "Edm.Date":
return new Date(node.raw);
case "Edm.TimeOfDay":
return new Date("1970-01-01T" + node.raw + "Z");
case "Edm.Duration":
var m = node.raw.match(/P([0-9]*D)?T?([0-9]{1,2}H)?([0-9]{1,2}M)?([\.0-9]*S)?/);
if (m) {
var d = new Date(0);
for (var i = 1; i < m.length; i++) {
switch (m[i].slice(-1)) {
case 'D':
d.setDate(parseInt(m[i]));
continue;
case 'H':
d.setHours(parseInt(m[i]));
continue;
case 'M':
d.setMinutes(parseInt(m[i]));
continue;
case 'S':
d.setSeconds(parseInt(m[i]));
continue;
}
}
return d;
}
default:

@@ -116,3 +209,7 @@ console.log("unknown value type:" + node.value);

var method = exports.ODataMethodMap[node.value.method];
var params = node.value.parameters.map(function (p) { return _this.Visit(p, context); });
var params = (node.value.parameters || []).map(function (p) { return _this.Visit(p, context); });
if (!method) {
console.log("Unknown method " + node.value.method);
return function (a) { return a; };
}
return function (a) { return method.apply(_this, params.map(function (p) { return p(a); })); };

@@ -128,2 +225,12 @@ };

};
FilterVisitor.prototype.VisitIsOfExpression = function (node, context) {
var type = this.Visit(node.value.typename, context);
if (!node.value.target) {
return function (a) {
return a.constructor.name === type(a);
};
}
var target = this.Visit(node.value.target, context);
return function (a) { return target(a).constructor.name === type(a); };
};
FilterVisitor.prototype.VisitOrExpression = function (node, context) {

@@ -133,4 +240,24 @@ var _a = this.VisitBinaryExpression(node, context), left = _a[0], right = _a[1];

};
FilterVisitor.prototype.resolveIdentifier = function (node) {
switch (node.value) {
case 'EntityTypeName':
return node.raw.split(".").slice(-1)[0];
}
};
//this needs double check. why is model type model as 'identifier'
FilterVisitor.prototype.VisitIdentifier = function (node, context) {
var _this = this;
return function (a) { return _this.resolveIdentifier(node); };
};
FilterVisitor.prototype.VisitArray = function (node, context) {
var _this = this;
var items = (node.value.items || []).map(function (item) { return _this.Visit(item, context); });
return function (a) { return items.map(function (item) { return item(a); }).slice(); };
};
FilterVisitor.prototype.VisitNegateExpression = function (node, context) {
var exp = this.Visit(node.value, context);
return function (a) { return -exp(a); };
};
return FilterVisitor;
}());
exports.FilterVisitor = FilterVisitor;
import { Token } from 'odata-v4-parser/lib/lexer';
export { Token } from 'odata-v4-parser/lib/lexer';
export interface ExpressionFunction {
(entity: any): any;
}
export interface FilterFunction {

@@ -14,3 +17,2 @@ (entity: any): boolean;

* @example
* //return true
* const filterFn = createFilter("Size eq 4 and startswith(Name,'Ch')")

@@ -22,1 +24,12 @@ * const items = [{Size:1, Name:'Chai'}, {Size:4, Name:'Childrens book' }]

export declare function createFilter(odataFilter: string): FilterFunction;
/**
* Compiles a value returning function from an OData expression string
* @param {string} odataExpression - An expression in OData expression format
* @return {ExpressionFunction} JavaScript function that implements the expression
* @example
* const expression = compileExpression("concat((Size add 12) mul 3,Name)")
* const item = {Size:1, Name:'Chai'}
* console.log(expression(item))
* >> 39Chai
*/
export declare function compileExpression(odataExpression: string): ExpressionFunction;

@@ -22,3 +22,2 @@ "use strict";

* @example
* //return true
* const filterFn = createFilter("Size eq 4 and startswith(Name,'Ch')")

@@ -33,1 +32,15 @@ * const items = [{Size:1, Name:'Chai'}, {Size:4, Name:'Childrens book' }]

exports.createFilter = createFilter;
/**
* Compiles a value returning function from an OData expression string
* @param {string} odataExpression - An expression in OData expression format
* @return {ExpressionFunction} JavaScript function that implements the expression
* @example
* const expression = compileExpression("concat((Size add 12) mul 3,Name)")
* const item = {Size:1, Name:'Chai'}
* console.log(expression(item))
* >> 39Chai
*/
function compileExpression(odataExpression) {
return filterVisitor.Visit(infrastructure.createFilterAst(odataExpression), {});
}
exports.compileExpression = compileExpression;

2

package.json
{
"name": "odata-v4-inmemory",
"version": "0.1.3",
"version": "0.1.4",
"description": "Service OData requests from an inmemory data store",

@@ -5,0 +5,0 @@ "main": "lib/index.js",

@@ -21,3 +21,3 @@ # OData V4 Service modules - InMemory Connector

## Usage TS
## Usage as server - TypeScript
```javascript

@@ -45,17 +45,41 @@ import { createFilter } from 'odata-v4-inmemory'

## Using as expression engine
```javascript
import { compileExpression } from 'odata-v4-inmemory'
const expression = compileExpression("concat((Size add 12) mul 3,Name)")
const item = {Size:1, Name:'Chai'}
console.log(expression(item))
>> 39Chai
```
## Supported OData segments
For now **$filter**, support for **$select** and **$expand** is next.
For now **$filter**
Support for **$select** and **$expand** is next.
### Supported $filter expressions
The [OData v4 Parser](https://www.npmjs.com/package/odata-v4-parser) layer supports 100% of the specification.
The InMemory Connector is about 80% ready.
The Connector is about 90% ready on filters. Except for date arithmetic, and geo.* everything is supported.
*We are into creating a comprehensive feature availability chart for V1 release*
✓ expression: 1 eq 1
✓ expression: A eq 1
✓ expression 5.1.1.6.3: null
✓ expression 5.1.1.6.1: NullValue eq null
✓ expression 5.1.1.6.1: duration'P12DT23H59M59.999999999999S'
✓ expression 5.1.1.6.1: A eq INF
✓ expression 5.1.1.6.1: A eq 0.31415926535897931e1
✓ expression 5.1.1.6.1: A add B eq hour(07:59:59.999)
✓ expression 5.1.1.6.2: ["a","b"]
✓ expression 5.1.1.1.1: 1 eq 1
✓ expression 5.1.1.1.1: null eq null
✓ expression 5.1.1.1.2: A ne 1
✓ expression 5.1.1.1.3: A gt 2
✓ expression 5.1.1.1.4: A ge 3
✓ expression 5.1.1.1.5: A lt 2
✓ expression 5.1.1.1.6: A le 2
✓ expression 5.1.1.2.3: -A eq 1
✓ expression: A

@@ -65,11 +89,26 @@ ✓ expression: A/b

✓ expression: A/b eq A/b
✓ expression: (A/b eq B/a) or (B/c lt 4) and ((E add 2) gt B add A)
✓ expression: A/$count
✓ expression 5.1.1.3: (A/b eq B/a) or (B/c lt 4) and ((E add 2) gt B add A)
✓ expression 5.1.1.1.9: not A ne 1
- expression 5.1.1.1.10: A has enum'b
✓ expression 4.8: A/$count
✓ expression: A/$count eq 3
✓ expression: A/$count gt 2
✓ expression: A and B
✓ expression: A le B
✓ expression: A ge B
✓ expression 5.1.1.1.7: A and B
✓ expression 5.1.1.1.8: A or B
✓ expression: (A and B)
✓ expression: A/$count gt 2 and A/$count lt 4
✓ expression: (A/$count gt 2) and A/$count lt 3
✓ expression: A add B
✓ expression 5.1.1.2.1: A add B
- expression 5.1.1.2.1: '2016-01-01' add 'P4DT15H'
- expression 5.1.1.2.1: '2016-01-01T12:00:00Z' add 'P4DT15H'
- expression 5.1.1.2.1: duration'P4DT15H' add duration'P4DT15H'
✓ expression 5.1.1.2.1: A sub B
- expression 5.1.1.2.1: '2016-01-01T12:00:00Z' sub 'P4DT15H'
✓ expression: 5 sub 10
✓ expression 5.1.1.2.4: A mul B
✓ expression 5.1.1.2.4: 5 mul 10
✓ expression 5.1.1.2.5: 10 div 3
✓ expression 5.1.1.2.5: (A mul B) div 10
✓ expression 5.1.1.2.6: (A mod B) div 10
✓ expression: A add 'B'

@@ -83,7 +122,39 @@ ✓ expression: 'A' add 'B'

✓ expression: A/all(i:$it eq 1)
✓ expression: A/all(i: i/a eq 1)
✓ expression: A/any(i: i/a eq 1)
✓ expression: substring(A, 2) eq 'BC'
✓ expression 5.1.1.5.2: A/all(i: i/a eq 1)
✓ expression 5.1.1.5.1: A/any(i: i/a eq 1)
✓ expression 5.1.1.4.6: substring(A, 2) eq 'BC'
✓ expression: substring('ABC', D) eq 'BC'
✓ expression: substring('ABC', 2) eq 'BC'
✓ expression: A eq 1.5
✓ expression: A eq year(2016-01-01)
✓ expression 5.1.1.4.1: contains(A, 'BC')
✓ expression 5.1.1.4.2: endswith(A, 'CD')
✓ expression 5.1.1.4.3: startswith(A, 'CD')
✓ expression 5.1.1.4.4: length(A) eq 3
✓ expression 5.1.1.4.5: indexof(A, 'DE')
✓ expression 5.1.1.4.7: tolower(A) eq 'abc'
✓ expression 5.1.1.4.8: toupper(A) eq 'ABC'
✓ expression 5.1.1.4.9: trim(A) eq 'abc'
✓ expression 5.1.1.4.10: concat(A,B) eq 'fubar'
✓ expression 5.1.1.4.10: concat(A,concat(B,C)) eq 'fubardoh'
✓ expression 5.1.1.4.11: A eq year(2016-01-01T13:00Z)
✓ expression 5.1.1.4.12: A eq month(2016-01-01T13:00Z)
✓ expression 5.1.1.4.13: A eq day(2016-01-01T13:00Z)
✓ expression 5.1.1.4.14: A eq hour(2016-01-01T13:00Z)
✓ expression 5.1.1.4.15: A eq minute(2016-01-01T13:00Z)
✓ expression 5.1.1.4.16: A eq second(2016-01-01T13:00:02Z)
✓ expression 5.1.1.4.21: year(now())
✓ expression 5.1.1.4.22: year(maxdatetime())
✓ expression 5.1.1.4.23: year(mindatetime())
- expression 5.1.1.4.24: totalseconds(A)
✓ expression 5.1.1.4.25: round(A) eq 42
✓ expression 5.1.1.4.26: floor(A) eq 42
✓ expression 5.1.1.4.27: ceiling(A) eq 42
✓ expression 5.1.1.4.28: isof(Model.Order)
✓ expression 5.1.1.4.28: isof($it, Model.Order)
✓ expression 5.1.1.4.28: Orders/all(item: isof(item, Model.Order))
- expression 5.1.1.4.29: cast(A, Edm.String)
- expression 5.1.1.4.30: geo.distance(A, B)
- expression 5.1.1.4.31: geo.intersects(A, B)
- expression 5.1.1.4.31: geo.length(A)

@@ -93,1 +164,3 @@

@@ -17,6 +17,28 @@ import { TokenType, Token } from 'odata-v4-parser/lib/lexer'

const minDateTime = new Date(-8640000000000000)
const maxDateTime = new Date(8640000000000000)
export const ODataMethodMap = {
round: v => Math.round(v),
indexof: (v, i) => v.indexOf && v.indexOf(i),
substring: (v, i) => v.substr(i - 1)
substring: (v, i) => v.substr(i - 1),
contains: (v, i) => v.includes(i),
endswith: (v, i) => v.endsWith(i),
startswith: (v, i) => v.startsWith(i),
length: v => v.length,
tolower: v => v.toLowerCase(),
toupper: v => v.toUpperCase(),
trim: v => v.trim(),
concat: (v,i) => v.concat(i),
year: v => v.getFullYear(),
month: v => v.getMonth() + 1,
day: v => v.getDate(),
hour: v => v.getHours(),
minute: v => v.getMinutes(),
second: v => v.getSeconds(),
now: () => new Date(),
maxdatetime: () => maxDateTime,
mindatetime: () => minDateTime,
floor: v => Math.floor(v),
ceiling: v => Math.ceil(v),
//isof: (v, i) => return v
}

@@ -30,2 +52,5 @@

//console.log("Visiting: ", node.type)
if (!node) {
throw new Error("Node cannot be empty")
}
switch (node.type) {

@@ -39,2 +64,3 @@ //these are auto handled by visitor bubbling

case TokenType.CommonExpression:
case TokenType.ArrayOrObject:
case undefined:

@@ -94,2 +120,7 @@ break;

protected VisitNotExpression(node: Token, context: any) {
var expression = this.Visit(node.value, context)
return a => !expression(a)
}
protected VisitEqualsExpression(node: Token, context: any) {

@@ -100,2 +131,7 @@ var [left, right] = this.VisitBinaryExpression(node, context)

protected VisitNotEqualsExpression(node: Token, context: any) {
var [left, right] = this.VisitBinaryExpression(node, context)
return a => left(a) !== right(a)
}
protected VisitGreaterThanExpression(node: Token, context: any) {

@@ -111,2 +147,12 @@ var [left, right] = this.VisitBinaryExpression(node, context)

protected VisitLesserOrEqualsExpression(node: Token, context: any) {
var [left, right] = this.VisitBinaryExpression(node, context)
return a => left(a) <= right(a)
}
protected VisitGreaterOrEqualsExpression(node: Token, context: any) {
var [left, right] = this.VisitBinaryExpression(node, context)
return a => left(a) >= right(a)
}
protected VisitImplicitVariableExpression(node: Token, context: any) {

@@ -124,2 +170,18 @@ return a => a

}
protected VisitSubExpression(node: Token, context: any) {
var [left, right] = this.VisitBinaryExpression(node, context)
return a => left(a) - right(a)
}
protected VisitMulExpression(node: Token, context: any) {
var [left, right] = this.VisitBinaryExpression(node, context)
return a => left(a) * right(a)
}
protected VisitDivExpression(node: Token, context: any) {
var [left, right] = this.VisitBinaryExpression(node, context)
return a => left(a) / right(a)
}
protected VisitModExpression(node: Token, context: any) {
var [left, right] = this.VisitBinaryExpression(node, context)
return a => left(a) % right(a)
}

@@ -130,7 +192,36 @@ protected VisitParenExpression(node: Token, context: any) {

}
protected getLiteral(node: Token): any {
switch(node.value) {
case 'null': return null
case "Edm.SByte": return parseInt(node.raw)
case "Edm.Decimal": return parseFloat(node.raw)
case "Edm.Double": switch(node.raw) {
case 'INF': return Infinity
default: return parseFloat(node.raw)
}
case "Edm.Boolean": return node.raw === "true"
case "Edm.String": return node.raw.replace(/'/g,'')
//todo: check if string is actually a valid literal type
case "string":
case "Edm.String":
return node.raw.replace(/'/g,'').replace(/\"/g,"")
case "Edm.DateTimeOffset":
case "Edm.Date":
return new Date(node.raw)
case "Edm.TimeOfDay":
return new Date(`1970-01-01T${node.raw}Z`)
case "Edm.Duration":
var m = node.raw.match(/P([0-9]*D)?T?([0-9]{1,2}H)?([0-9]{1,2}M)?([\.0-9]*S)?/)
if (m) {
var d = new Date(0);
for(var i = 1; i < m.length; i++) {
switch(m[i].slice(-1)) {
case 'D': d.setDate(parseInt(m[i])); continue;
case 'H': d.setHours(parseInt(m[i])); continue;
case 'M': d.setMinutes(parseInt(m[i])); continue;
case 'S': d.setSeconds(parseInt(m[i])); continue;
}
}
return d;
}
default:

@@ -147,3 +238,7 @@ console.log("unknown value type:" + node.value)

var method = ODataMethodMap[node.value.method]
var params = node.value.parameters.map(p => this.Visit(p, context))
var params = (node.value.parameters || []).map(p => this.Visit(p, context))
if (!method) {
console.log(`Unknown method ${node.value.method}`)
return a => a
}
return a => method.apply(this, params.map(p => p(a)))

@@ -161,2 +256,12 @@ }

protected VisitIsOfExpression(node: Token, context: any) {
var type = this.Visit(node.value.typename, context)
if (!node.value.target) {
return a => {
return a.constructor.name === type(a)
}
}
var target = this.Visit(node.value.target, context)
return a => target(a).constructor.name === type(a)
}
protected VisitOrExpression(node: Token, context: any) {

@@ -167,3 +272,23 @@ var [left, right] = this.VisitBinaryExpression(node, context)

private resolveIdentifier(node) {
switch(node.value) {
case 'EntityTypeName':
return node.raw.split(".").slice(-1)[0]
}
}
//this needs double check. why is model type model as 'identifier'
protected VisitIdentifier(node, context) {
return a => this.resolveIdentifier(node)
}
protected VisitArray(node: Token, context: any) {
var items = (node.value.items || []).map( item => this.Visit(item, context))
return a => [...items.map(item => item(a))]
}
protected VisitNegateExpression(node: Token, context: any) {
var exp = this.Visit(node.value, context)
return a => -exp(a)
}
}

@@ -170,0 +295,0 @@

@@ -6,3 +6,3 @@ import { FilterVisitor } from './FilterVisitor'

interface ExpressionFunction {
export interface ExpressionFunction {
(entity: any): any

@@ -32,3 +32,2 @@ }

* @example
* //return true
* const filterFn = createFilter("Size eq 4 and startswith(Name,'Ch')")

@@ -43,1 +42,14 @@ * const items = [{Size:1, Name:'Chai'}, {Size:4, Name:'Childrens book' }]

/**
* Compiles a value returning function from an OData expression string
* @param {string} odataExpression - An expression in OData expression format
* @return {ExpressionFunction} JavaScript function that implements the expression
* @example
* const expression = compileExpression("concat((Size add 12) mul 3,Name)")
* const item = {Size:1, Name:'Chai'}
* console.log(expression(item))
* >> 39Chai
*/
export function compileExpression(odataExpression: string): ExpressionFunction {
return filterVisitor.Visit(infrastructure.createFilterAst(odataExpression), {})
}

@@ -6,19 +6,74 @@ var createFilter = require('../lib').createFilter

var f
var e
beforeEach(function() {
var match
if (match = this.currentTest.title.match(/expression\: ?(.*)/)) {
f = createFilter(match[1])
if (match = this.currentTest.title.match(/expression[^\:]*\: ?(.*)/)) {
e = f = createFilter(match[1])
}
})
it("expression: 1 eq 1", () => {
//all numbers are referencing this:
//http://docs.oasis-open.org/odata/odata/v4.0/errata02/os/complete/part2-url-conventions/odata-v4.0-errata02-os-part2-url-conventions-complete.html#_Toc406398116
it("expression 5.1.1.6.3: null", () => {
expect(e()).to.be.null
})
it("expression 5.1.1.6.1: NullValue eq null", () => {
expect(f({NullValue: null})).to.be.true
})
it("expression 5.1.1.6.1: duration'P12DT23H59M59.999999999999S'", () => {
expect(e({}).getTime()).to.eql(1036799000)
})
it("expression 5.1.1.6.1: A eq INF", () => {
expect(f({A: Infinity})).to.be.true
})
it("expression 5.1.1.6.1: A eq 0.31415926535897931e1", () => {
expect(f({A: 0.31415926535897931e1})).to.be.true
})
it("expression 5.1.1.6.1: A add B eq hour(07:59:59.999)", () => {
expect(f({A: 3, B: 4})).to.be.true
})
it('expression 5.1.1.6.2: ["a","b"]', () => {
expect(e({A: 3, B: 4})).to.eql(['a','b'])
})
it("expression 5.1.1.1.1: 1 eq 1", () => {
expect(f()).to.be.true
})
it("expression: A eq 1", () => {
expect(f({A:1})).to.be.true
it("expression 5.1.1.1.1: null eq null", () => {
expect(f()).to.be.true
})
it("expression 5.1.1.1.2: A ne 1", () => {
expect(f({A:1})).to.equal(false)
expect(f({A:2})).to.equal(true)
})
it("expression 5.1.1.1.3: A gt 2", () => {
expect(f({A: 3})).to.equal(true)
})
it("expression 5.1.1.1.4: A ge 3", () => {
expect(f({A: 3})).to.equal(true)
})
it("expression 5.1.1.1.5: A lt 2", () => {
expect(f({A: 1})).to.equal(true)
})
it("expression 5.1.1.1.6: A le 2", () => {
expect(f({A: 2})).to.equal(true)
})
it("expression 5.1.1.2.3: -A eq 1", () => {
expect(f({A:-1})).to.be.true
})
it("expression: A", () => {

@@ -41,7 +96,16 @@ expect(f({A:1})).to.equal(1)

it("expression: (A/b eq B/a) or (B/c lt 4) and ((E add 2) gt B add A)", () => {
it("expression 5.1.1.3: (A/b eq B/a) or (B/c lt 4) and ((E add 2) gt B add A)", () => {
expect(f({A:{b:1}})).to.equal(false)
})
it("expression: A/$count", () => {
it("expression 5.1.1.1.9: not A ne 1", () => {
expect(f({A:1})).to.equal(true)
expect(f({A:2})).to.equal(false)
})
xit("expression 5.1.1.1.10: A has enum'b", () => {
})
it("expression 4.8: A/$count", () => {
expect(f({A:[1,2,3]})).to.equal(3)

@@ -54,10 +118,22 @@ })

it("expression: A/$count gt 2", () => {
expect(f({A:[1,2,3]})).to.equal(true)
it("expression: A le B", () => {
expect(f({A:1, B:2})).to.equal(true)
expect(f({A:2, B:2})).to.equal(true)
expect(f({A:3, B:2})).to.equal(false)
})
it("expression: A and B", () => {
it("expression: A ge B", () => {
expect(f({A:1, B:2})).to.equal(false)
expect(f({A:2, B:2})).to.equal(true)
expect(f({A:3, B:2})).to.equal(true)
})
it("expression 5.1.1.1.7: A and B", () => {
expect(f({A:1, B:2})).to.equal(2)
})
it("expression 5.1.1.1.8: A or B", () => {
expect(e({A:1, B:2})).to.equal(1)
})
it("expression: (A and B)", () => {

@@ -76,6 +152,53 @@ expect(f({A:1, B:2})).to.equal(true)

it("expression: A add B", () => {
it("expression 5.1.1.2.1: A add B", () => {
expect(f({A:1, B:2})).to.equal(3)
})
//undone
xit("expression 5.1.1.2.1: '2016-01-01' add 'P4DT15H'", () => {
expect(f({A:1, B:2})).to.equal(3)
})
//undone
xit("expression 5.1.1.2.1: '2016-01-01T12:00:00Z' add 'P4DT15H'", () => {
expect(f({A:1, B:2})).to.equal(3)
})
//undone
xit("expression 5.1.1.2.1: duration'P4DT15H' add duration'P4DT15H'", () => {
expect(f({A:1, B:2})).to.equal(3)
})
it("expression 5.1.1.2.1: A sub B", () => {
expect(f({A:1, B:2})).to.equal(-1)
})
xit("expression 5.1.1.2.1: '2016-01-01T12:00:00Z' sub 'P4DT15H'", () => {
expect(f({A:1, B:2})).to.equal(3)
})
it("expression: 5 sub 10", () => {
expect(f({A:1, B:2})).to.equal(-5)
})
it("expression 5.1.1.2.4: A mul B", () => {
expect(f({A:1, B:2})).to.equal(2)
})
it("expression 5.1.1.2.4: 5 mul 10", () => {
expect(f({A:1, B:2})).to.equal(50)
})
it("expression 5.1.1.2.5: 10 div 3", () => {
expect(f({A:1, B:2})).to.equal(3.3333333333333335)
})
it("expression 5.1.1.2.5: (A mul B) div 10", () => {
expect(f({A:1, B:2})).to.equal(0.2)
})
it("expression 5.1.1.2.6: (A mod B) div 10", () => {
expect(f({A:3, B:2})).to.equal(0.1)
})
it("expression: A add 'B'", () => {

@@ -114,3 +237,3 @@ expect(f({A:1, B:2})).to.equal('1B')

it("expression: A/all(i: i/a eq 1)", () => {
it("expression 5.1.1.5.2: A/all(i: i/a eq 1)", () => {
expect(f({A: [{a:1},{a:1}]})).to.equal(true)

@@ -120,3 +243,3 @@ expect(f({A: [{a:1},{a:2}]})).to.equal(false)

it("expression: A/any(i: i/a eq 1)", () => {
it("expression 5.1.1.5.1: A/any(i: i/a eq 1)", () => {
expect(f({A: [{a:1},{a:2}]})).to.equal(true)

@@ -126,3 +249,3 @@ expect(f({A: [{a:3},{a:2}]})).to.equal(false)

it("expression: substring(A, 2) eq 'BC'", () => {
it("expression 5.1.1.4.6: substring(A, 2) eq 'BC'", () => {
expect(f({A:'ABC'})).to.equal(true)

@@ -138,2 +261,153 @@ })

})
it("expression: A eq 1.5", () => {
expect(f({A: 1.5})).to.equal(true)
})
it("expression: A eq year(2016-01-01)", () => {
expect(f({A: 2016})).to.equal(true)
})
it("expression 5.1.1.4.1: contains(A, 'BC')", () => {
expect(f({A: 'ABCD'})).to.equal(true)
})
it("expression 5.1.1.4.2: endswith(A, 'CD')", () => {
expect(f({A: 'ABCD'})).to.equal(true)
expect(f({A: 'ABCDE'})).to.equal(false)
})
it("expression 5.1.1.4.3: startswith(A, 'CD')", () => {
expect(f({A: 'CDE'})).to.equal(true)
expect(f({A: 'ABCDE'})).to.equal(false)
})
it("expression 5.1.1.4.4: length(A) eq 3", () => {
expect(f({A: 'CDE'})).to.equal(true)
expect(f({A: 'ABCDE'})).to.equal(false)
})
it("expression 5.1.1.4.5: indexof(A, 'DE')", () => {
expect(f({A: 'CDE'})).to.equal(1)
expect(f({A: 'ABCDE'})).to.equal(3)
})
it("expression 5.1.1.4.7: tolower(A) eq 'abc'", () => {
expect(f({A: 'ABC'})).to.equal(true)
expect(f({A: 'DEF'})).to.equal(false)
})
it("expression 5.1.1.4.8: toupper(A) eq 'ABC'", () => {
expect(f({A: 'abc'})).to.equal(true)
expect(f({A: 'def'})).to.equal(false)
})
it("expression 5.1.1.4.9: trim(A) eq 'abc'", () => {
expect(f({A: ' abc '})).to.equal(true)
})
it("expression 5.1.1.4.10: concat(A,B) eq 'fubar'", () => {
expect(f({A:'fu',B:'bar'})).to.equal(true)
})
it("expression 5.1.1.4.10: concat(A,concat(B,C)) eq 'fubardoh'", () => {
expect(f({A:'fu',B:'bar', C:'doh'})).to.equal(true)
})
it("expression 5.1.1.4.11: A eq year(2016-01-01T13:00Z)", () => {
expect(f({A: 2016})).to.equal(true)
})
it("expression 5.1.1.4.12: A eq month(2016-01-01T13:00Z)", () => {
expect(f({A: 1})).to.equal(true)
})
it("expression 5.1.1.4.13: A eq day(2016-01-01T13:00Z)", () => {
expect(f({A: 1})).to.equal(true)
})
it("expression 5.1.1.4.14: A eq hour(2016-01-01T13:00Z)", () => {
expect(f({A: 13})).to.equal(true)
})
it("expression 5.1.1.4.15: A eq minute(2016-01-01T13:00Z)", () => {
expect(f({A: 0})).to.equal(true)
})
it("expression 5.1.1.4.16: A eq second(2016-01-01T13:00:02Z)", () => {
expect(f({A: 02})).to.equal(true)
})
it("expression 5.1.1.4.21: year(now())", () => {
expect(f({})).to.equal(new Date().getFullYear())
})
it("expression 5.1.1.4.22: year(maxdatetime())", () => {
expect(f({})).to.equal(275760)
})
it("expression 5.1.1.4.23: year(mindatetime())", () => {
expect(f({})).to.equal(-271821)
})
//parser ok
//undone
xit("expression 5.1.1.4.24: totalseconds(A)", () => {
expect(f({A: 'P6DT23H59M59.9999S'})).to.equal(-271821)
})
it("expression 5.1.1.4.25: round(A) eq 42", () => {
expect(f({A: 41.9})).to.equal(true)
})
it("expression 5.1.1.4.26: floor(A) eq 42", () => {
expect(f({A: 42.9})).to.equal(true)
})
it("expression 5.1.1.4.27: ceiling(A) eq 42", () => {
expect(f({A: 41.3})).to.equal(true)
})
it("expression 5.1.1.4.28: isof(Model.Order)", () => {
function Order() { }
expect(f(new Order())).to.equal(true)
})
it("expression 5.1.1.4.28: isof($it, Model.Order)", () => {
function Order() { }
expect(f(new Order())).to.equal(true)
})
it("expression 5.1.1.4.28: Orders/all(item: isof(item, Model.Order))", () => {
function Order() { }
expect(f({Orders: [new Order()]})).to.equal(true)
expect(f({Orders: [new Order(),{}]})).to.equal(false)
})
//parser error
//undone
xit("expression 5.1.1.4.29: cast(A, Edm.String)", () => {
expect(f({A: 41.3})).to.equal(true)
})
//parser ok
//undone
xit("expression 5.1.1.4.30: geo.distance(A, B)", () => {
expect(f({A: 41.3})).to.equal(true)
})
//parser ok
//undone
xit("expression 5.1.1.4.31: geo.intersects(A, B)", () => {
expect(f({A: 41.3})).to.equal(true)
})
//parser ok
//undone
xit("expression 5.1.1.4.31: geo.length(A)", () => {
expect(f({A: 41.3})).to.equal(true)
})
})
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc