├── .npmrc ├── tsconfig.json ├── lib ├── index.js.map ├── index.js ├── index.d.ts ├── FilterVisitor.d.ts ├── FilterVisitor.js.map └── FilterVisitor.js ├── .gitignore ├── package.json ├── src ├── index.ts └── FilterVisitor.ts ├── README.md └── test └── filterVisitor.test.js /.npmrc: -------------------------------------------------------------------------------- 1 | registry=https://registry.npmjs.org 2 | @types:registry=https://registry.npmjs.org 3 | loglevel="warn" 4 | //SECRET -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "sourceMap": true, 4 | "target": "es5", 5 | "declaration": true, 6 | "outDir": "lib" 7 | }, 8 | "exclude": [ 9 | "node_modules" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /lib/index.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA,8BAA8B,iBAC9B,CAAC,CAD8C;AAC/C,gCAAsC,iBACtC,CAAC,CADsD;AAWvD,IAAM,aAAa,GAAG,IAAI,6BAAa,EAAE,CAAC;AAc1C,sBAA6B,MAAsB;IACjD,IAAI,GAAG,GAAgB,CAAC,OAAO,MAAM,IAAI,QAAQ,GAAG,wBAAW,CAAS,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC;IAC1F,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;AACtC,CAAC;AAHe,oBAAY,eAG3B,CAAA;AAcD,2BAAkC,UAA0B;IAC1D,IAAI,GAAG,GAAgB,CAAC,OAAO,UAAU,IAAI,QAAQ,GAAG,wBAAW,CAAS,UAAU,CAAC,GAAG,UAAU,CAAC,CAAC;IACtG,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,GAAG,EAAE,EAAE,CAAC,CAAA;AACrC,CAAC;AAHe,yBAAiB,oBAGhC,CAAA"} -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var FilterVisitor_1 = require('./FilterVisitor'); 3 | var odata_v4_parser_1 = require('odata-v4-parser'); 4 | var filterVisitor = new FilterVisitor_1.FilterVisitor(); 5 | function createFilter(filter) { 6 | var ast = (typeof filter == "string" ? odata_v4_parser_1.filter(filter) : filter); 7 | return filterVisitor.Visit(ast, {}); 8 | } 9 | exports.createFilter = createFilter; 10 | function compileExpression(expression) { 11 | var ast = (typeof expression == "string" ? odata_v4_parser_1.filter(expression) : expression); 12 | return filterVisitor.Visit(ast, {}); 13 | } 14 | exports.compileExpression = compileExpression; 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 18 | .grunt 19 | 20 | # node-waf configuration 21 | .lock-wscript 22 | 23 | # Compiled binary addons (http://nodejs.org/api/addons.html) 24 | build/Release 25 | 26 | # Dependency directory 27 | node_modules 28 | 29 | # Optional npm cache directory 30 | .npm 31 | 32 | # Optional REPL history 33 | .node_repl_history 34 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "odata-v4-inmemory", 3 | "version": "0.1.9", 4 | "description": "Service OData requests from an inmemory data store", 5 | "main": "lib/index.js", 6 | "typings": "lib/index", 7 | "directories": { 8 | "test": "test" 9 | }, 10 | "scripts": { 11 | "build": "tsc src/index.ts --outDir lib -d", 12 | "pretest": "npm run build", 13 | "test": "mocha" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "git+https://github.com/jaystack/odata-v4-inmemory.git" 18 | }, 19 | "keywords": [ 20 | "OData", 21 | "server", 22 | "V4", 23 | "parser" 24 | ], 25 | "author": "JayStack", 26 | "license": "MIT", 27 | "bugs": { 28 | "url": "https://github.com/jaystack/odata-v4-inmemory/issues" 29 | }, 30 | "homepage": "https://github.com/jaystack/odata-v4-inmemory#readme", 31 | "dependencies": { 32 | "odata-v4-parser": "^0.1.9" 33 | }, 34 | "devDependencies": { 35 | "chai": "^3.5.0", 36 | "mocha": "^2.4.5", 37 | "typescript": "^1.8.9" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /lib/index.d.ts: -------------------------------------------------------------------------------- 1 | import { Token } from 'odata-v4-parser/lib/lexer'; 2 | export interface ExpressionFunction { 3 | (entity: any): any; 4 | } 5 | export interface FilterFunction { 6 | (entity: any): boolean; 7 | } 8 | /** 9 | * Creates a filter function from an OData filter expression string 10 | * @param {string} odataFilter - A filter expression in OData $filter format 11 | * @return {FilterFunction} JavaScript function that implements the filter predicate 12 | * @example 13 | * const filterFn = createFilter("Size eq 4 and startswith(Name,'Ch')") 14 | * const items = [{Size:1, Name:'Chai'}, {Size:4, Name:'Childrens book' }] 15 | * console.log(items.filter(filterFn)) 16 | * >> [{Size:4, Name:'Childrens book'}] 17 | */ 18 | export declare function createFilter(filter: string): any; 19 | export declare function createFilter(filter: Token): any; 20 | /** 21 | * Compiles a value returning function from an OData expression string 22 | * @param {string} odataExpression - An expression in OData expression format 23 | * @return {ExpressionFunction} JavaScript function that implements the expression 24 | * @example 25 | * const expression = compileExpression("concat((Size add 12) mul 3,Name)") 26 | * const item = {Size:1, Name:'Chai'} 27 | * console.log(expression(item)) 28 | * >> 39Chai 29 | */ 30 | export declare function compileExpression(expression: string): any; 31 | export declare function compileExpression(expression: Token): any; 32 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import { FilterVisitor } from './FilterVisitor' 2 | import { filter as parseFilter } from 'odata-v4-parser' 3 | import { Token } from 'odata-v4-parser/lib/lexer' 4 | 5 | export interface ExpressionFunction { 6 | (entity: any): any 7 | } 8 | 9 | export interface FilterFunction { 10 | (entity: any): boolean 11 | } 12 | 13 | const filterVisitor = new FilterVisitor(); 14 | 15 | /** 16 | * Creates a filter function from an OData filter expression string 17 | * @param {string} odataFilter - A filter expression in OData $filter format 18 | * @return {FilterFunction} JavaScript function that implements the filter predicate 19 | * @example 20 | * const filterFn = createFilter("Size eq 4 and startswith(Name,'Ch')") 21 | * const items = [{Size:1, Name:'Chai'}, {Size:4, Name:'Childrens book' }] 22 | * console.log(items.filter(filterFn)) 23 | * >> [{Size:4, Name:'Childrens book'}] 24 | */ 25 | export function createFilter(filter:string); 26 | export function createFilter(filter:Token); 27 | export function createFilter(filter: string | Token): FilterFunction { 28 | let ast:Token = (typeof filter == "string" ? parseFilter(filter) : filter); 29 | return filterVisitor.Visit(ast, {}); 30 | } 31 | 32 | /** 33 | * Compiles a value returning function from an OData expression string 34 | * @param {string} odataExpression - An expression in OData expression format 35 | * @return {ExpressionFunction} JavaScript function that implements the expression 36 | * @example 37 | * const expression = compileExpression("concat((Size add 12) mul 3,Name)") 38 | * const item = {Size:1, Name:'Chai'} 39 | * console.log(expression(item)) 40 | * >> 39Chai 41 | */ 42 | export function compileExpression(expression:string); 43 | export function compileExpression(expression:Token); 44 | export function compileExpression(expression: string | Token): ExpressionFunction { 45 | let ast:Token = (typeof expression == "string" ? parseFilter(expression) : expression); 46 | return filterVisitor.Visit(ast, {}) 47 | } -------------------------------------------------------------------------------- /lib/FilterVisitor.d.ts: -------------------------------------------------------------------------------- 1 | import { Token } from 'odata-v4-parser/lib/lexer'; 2 | export interface VisitorFuncRes { 3 | (a: any): any; 4 | } 5 | export interface VisitorFunc { 6 | (node: Token, context: any): VisitorFuncRes | Array; 7 | } 8 | export interface VisitorMap { 9 | [key: string]: VisitorFunc; 10 | } 11 | export declare const ODataMethodMap: { 12 | round: (v: any) => number; 13 | indexof: (v: any, i: any) => any; 14 | substring: (v: any, i: any) => any; 15 | contains: (v: any, i: any) => any; 16 | endswith: (v: any, i: any) => any; 17 | startswith: (v: any, i: any) => any; 18 | length: (v: any) => any; 19 | tolower: (v: any) => any; 20 | toupper: (v: any) => any; 21 | trim: (v: any) => any; 22 | concat: (v: any, i: any) => any; 23 | year: (v: any) => any; 24 | month: (v: any) => any; 25 | day: (v: any) => any; 26 | hour: (v: any) => any; 27 | minute: (v: any) => any; 28 | second: (v: any) => any; 29 | now: () => Date; 30 | maxdatetime: () => Date; 31 | mindatetime: () => Date; 32 | floor: (v: any) => number; 33 | ceiling: (v: any) => number; 34 | }; 35 | export declare class FilterVisitor implements VisitorMap { 36 | [k: string]: VisitorFunc; 37 | Visit(node: Token, context: any): any; 38 | private VisitFirstMemberExpression(node, context); 39 | private VisitBinaryExpression(node, context); 40 | protected VisitBoolParenExpression(node: Token, context: any): (a: any) => boolean; 41 | protected VisitLambdaVariableExpression(node: Token, context: any): (a: any) => any; 42 | protected VisitCountExpression(node: Token, context: any): (a: any) => any; 43 | protected VisitAllExpression(node: Token, context: any): (a: any) => any; 44 | protected VisitAnyExpression(node: Token, context: any): (a: any) => any; 45 | protected VisitFilter(node: Token, context: any): (a: any) => boolean; 46 | protected VisitNotExpression(node: Token, context: any): (a: any) => boolean; 47 | protected VisitEqualsExpression(node: Token, context: any): (a: any) => boolean; 48 | protected VisitNotEqualsExpression(node: Token, context: any): (a: any) => boolean; 49 | protected VisitGreaterThanExpression(node: Token, context: any): (a: any) => boolean; 50 | protected VisitLesserThanExpression(node: Token, context: any): (a: any) => boolean; 51 | protected VisitLesserOrEqualsExpression(node: Token, context: any): (a: any) => boolean; 52 | protected VisitGreaterOrEqualsExpression(node: Token, context: any): (a: any) => boolean; 53 | protected VisitImplicitVariableExpression(node: Token, context: any): (a: any) => any; 54 | protected VisitAndExpression(node: Token, context: any): (a: any) => any; 55 | protected VisitAddExpression(node: Token, context: any): (a: any) => any; 56 | protected VisitSubExpression(node: Token, context: any): (a: any) => number; 57 | protected VisitMulExpression(node: Token, context: any): (a: any) => number; 58 | protected VisitDivExpression(node: Token, context: any): (a: any) => number; 59 | protected VisitModExpression(node: Token, context: any): (a: any) => number; 60 | protected VisitParenExpression(node: Token, context: any): (a: any) => any; 61 | protected getLiteral(node: Token): any; 62 | protected VisitLiteral(node: Token, context: any): (a: any) => any; 63 | protected VisitMethodCallExpression(node: Token, context: any): (a: any) => any; 64 | protected VisitPropertyPathExpression(node: Token, context: any): any; 65 | protected VisitODataIdentifier(node: Token, context: any): (a: any) => any; 66 | protected VisitIsOfExpression(node: Token, context: any): (a: any) => boolean; 67 | protected VisitQualifiedEntityTypeName(node: Token, context: any): (a: any) => any; 68 | protected VisitEntityTypeName(node: Token, context: any): (a: any) => any; 69 | protected VisitOrExpression(node: Token, context: any): (a: any) => any; 70 | private resolveIdentifier(node); 71 | protected VisitIdentifier(node: any, context: any): (a: any) => any; 72 | protected VisitArray(node: Token, context: any): (a: any) => any[]; 73 | protected VisitNegateExpression(node: Token, context: any): (a: any) => number; 74 | } 75 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OData V4 Service modules - InMemory Connector 2 | 3 | Service OData v4 requests from an in memory data store. 4 | 5 | ## Synopsis 6 | The OData V4 InMemory Connector provides functionality to convert the varios types of OData segments 7 | into runnable JavaScript functions, that you can execute over an inmemory graph of objects. 8 | 9 | In most cases servicing data from memory is not possible - in this case you can use the 10 | [OData V4 MongoDB Connector](https://github.com/jaystack/odata-v4-mongodb) 11 | 12 | The InMemory connector can be safely used in IoT devices. 13 | 14 | ## Potential usage scenarios 15 | 16 | - Server: Create high speed, standard compliant data sharing apis 17 | - Server: Use as a mock or swap-in for acceptance tests in place of the mongodb connector 18 | - Client: use the OData syntax to define data queries or value returning expressions as strings (stored in a configuration) and compile and execute later as JS functions 19 | 20 | 21 | ## Usage as server - TypeScript 22 | ```javascript 23 | import { createFilter } from 'odata-v4-inmemory' 24 | 25 | //example request: GET /api/products?$filter=category/id eq 5 or color eq 'Red' 26 | app.get("/api/products", (req: Request, res: Response) => { 27 | const data = getYourProductArray() //or similar :) 28 | const filterFn = createFilter(req.query.$filter) 29 | res.json(data.filter(filterFn)) 30 | }) 31 | ``` 32 | 33 | ## Usage ES5 34 | ```javascript 35 | var createFilter = require('odata-v4-inmemory').createFilter 36 | 37 | app.get("/api/products", function(req, res) { 38 | var data = getYourProductArray() //or similar :) 39 | var filterFn = createFilter(req.query.$filter) 40 | res.json(data.filter(filterFn)) 41 | }) 42 | ``` 43 | 44 | ## Using as expression engine 45 | ```javascript 46 | import { compileExpression } from 'odata-v4-inmemory' 47 | 48 | const expression = compileExpression("concat((Size add 12) mul 3,Name)") 49 | const item = {Size:1, Name:'Chai'} 50 | console.log(expression(item)) 51 | >> 39Chai 52 | ``` 53 | 54 | ## Supported OData segments 55 | 56 | 57 | For now **$filter** 58 | 59 | Support for **$select** and **$expand** is next. 60 | 61 | 62 | ### Supported $filter expressions 63 | The [OData v4 Parser](https://www.npmjs.com/package/odata-v4-parser) layer supports 100% of the specification. 64 | The Connector is about 90% ready on filters. Except for date arithmetic, and geo.* everything is supported. 65 | 66 | *We are into creating a comprehensive feature availability chart for V1 release* 67 | 68 | ✓ expression 5.1.1.6.3: null 69 | ✓ expression 5.1.1.6.1: NullValue eq null 70 | ✓ expression 5.1.1.6.1: duration'P12DT23H59M59.999999999999S' 71 | ✓ expression 5.1.1.6.1: A eq INF 72 | ✓ expression 5.1.1.6.1: A eq 0.31415926535897931e1 73 | ✓ expression 5.1.1.6.1: A add B eq hour(07:59:59.999) 74 | ✓ expression 5.1.1.6.2: ["a","b"] 75 | ✓ expression 5.1.1.1.1: 1 eq 1 76 | ✓ expression 5.1.1.1.1: null eq null 77 | ✓ expression 5.1.1.1.2: A ne 1 78 | ✓ expression 5.1.1.1.3: A gt 2 79 | ✓ expression 5.1.1.1.4: A ge 3 80 | ✓ expression 5.1.1.1.5: A lt 2 81 | ✓ expression 5.1.1.1.6: A le 2 82 | ✓ expression 5.1.1.2.3: -A eq 1 83 | ✓ expression: A 84 | ✓ expression: A/b 85 | ✓ expression: A/b eq 1 86 | ✓ expression: A/b eq A/b 87 | ✓ expression 5.1.1.3: (A/b eq B/a) or (B/c lt 4) and ((E add 2) gt B add A) 88 | ✓ expression 5.1.1.1.9: not A ne 1 89 | - expression 5.1.1.1.10: A has enum'b 90 | ✓ expression 4.8: A/$count 91 | ✓ expression: A/$count eq 3 92 | ✓ expression: A le B 93 | ✓ expression: A ge B 94 | ✓ expression 5.1.1.1.7: A and B 95 | ✓ expression 5.1.1.1.8: A or B 96 | ✓ expression: (A and B) 97 | ✓ expression: A/$count gt 2 and A/$count lt 4 98 | ✓ expression: (A/$count gt 2) and A/$count lt 3 99 | ✓ expression 5.1.1.2.1: A add B 100 | - expression 5.1.1.2.1: '2016-01-01' add 'P4DT15H' 101 | - expression 5.1.1.2.1: '2016-01-01T12:00:00Z' add 'P4DT15H' 102 | - expression 5.1.1.2.1: duration'P4DT15H' add duration'P4DT15H' 103 | ✓ expression 5.1.1.2.1: A sub B 104 | - expression 5.1.1.2.1: '2016-01-01T12:00:00Z' sub 'P4DT15H' 105 | ✓ expression: 5 sub 10 106 | ✓ expression 5.1.1.2.4: A mul B 107 | ✓ expression 5.1.1.2.4: 5 mul 10 108 | ✓ expression 5.1.1.2.5: 10 div 3 109 | ✓ expression 5.1.1.2.5: (A mul B) div 10 110 | ✓ expression 5.1.1.2.6: (A mod B) div 10 111 | ✓ expression: A add 'B' 112 | ✓ expression: 'A' add 'B' 113 | ✓ expression: A/$count add B/$count eq 7 114 | ✓ expression: $it eq 5 115 | ✓ expression: true eq false 116 | ✓ expression: true eq false 117 | ✓ expression: A/all(i:i eq 1) 118 | ✓ expression: A/all(i:$it eq 1) 119 | ✓ expression 5.1.1.5.2: A/all(i: i/a eq 1) 120 | ✓ expression 5.1.1.5.1: A/any(i: i/a eq 1) 121 | ✓ expression 5.1.1.4.6: substring(A, 2) eq 'BC' 122 | ✓ expression: substring('ABC', D) eq 'BC' 123 | ✓ expression: substring('ABC', 2) eq 'BC' 124 | ✓ expression: A eq 1.5 125 | ✓ expression: A eq year(2016-01-01) 126 | ✓ expression 5.1.1.4.1: contains(A, 'BC') 127 | ✓ expression 5.1.1.4.2: endswith(A, 'CD') 128 | ✓ expression 5.1.1.4.3: startswith(A, 'CD') 129 | ✓ expression 5.1.1.4.4: length(A) eq 3 130 | ✓ expression 5.1.1.4.5: indexof(A, 'DE') 131 | ✓ expression 5.1.1.4.7: tolower(A) eq 'abc' 132 | ✓ expression 5.1.1.4.8: toupper(A) eq 'ABC' 133 | ✓ expression 5.1.1.4.9: trim(A) eq 'abc' 134 | ✓ expression 5.1.1.4.10: concat(A,B) eq 'fubar' 135 | ✓ expression 5.1.1.4.10: concat(A,concat(B,C)) eq 'fubardoh' 136 | ✓ expression 5.1.1.4.11: A eq year(2016-01-01T13:00Z) 137 | ✓ expression 5.1.1.4.12: A eq month(2016-01-01T13:00Z) 138 | ✓ expression 5.1.1.4.13: A eq day(2016-01-01T13:00Z) 139 | ✓ expression 5.1.1.4.14: A eq hour(2016-01-01T13:00Z) 140 | ✓ expression 5.1.1.4.15: A eq minute(2016-01-01T13:00Z) 141 | ✓ expression 5.1.1.4.16: A eq second(2016-01-01T13:00:02Z) 142 | ✓ expression 5.1.1.4.21: year(now()) 143 | ✓ expression 5.1.1.4.22: year(maxdatetime()) 144 | ✓ expression 5.1.1.4.23: year(mindatetime()) 145 | - expression 5.1.1.4.24: totalseconds(A) 146 | ✓ expression 5.1.1.4.25: round(A) eq 42 147 | ✓ expression 5.1.1.4.26: floor(A) eq 42 148 | ✓ expression 5.1.1.4.27: ceiling(A) eq 42 149 | ✓ expression 5.1.1.4.28: isof(Model.Order) 150 | ✓ expression 5.1.1.4.28: isof($it, Model.Order) 151 | ✓ expression 5.1.1.4.28: Orders/all(item: isof(item, Model.Order)) 152 | - expression 5.1.1.4.29: cast(A, Edm.String) 153 | - expression 5.1.1.4.30: geo.distance(A, B) 154 | - expression 5.1.1.4.31: geo.intersects(A, B) 155 | - expression 5.1.1.4.31: geo.length(A) 156 | -------------------------------------------------------------------------------- /src/FilterVisitor.ts: -------------------------------------------------------------------------------- 1 | import { TokenType, Token } from 'odata-v4-parser/lib/lexer' 2 | 3 | 4 | 5 | export interface VisitorFuncRes { 6 | (a: any): any 7 | } 8 | 9 | export interface VisitorFunc { 10 | (node: Token, context: any): VisitorFuncRes | Array 11 | } 12 | 13 | export interface VisitorMap { 14 | [key: string]: VisitorFunc 15 | } 16 | 17 | const minDateTime = new Date(-8640000000000000) 18 | const maxDateTime = new Date(8640000000000000) 19 | export const ODataMethodMap = { 20 | round: v => Math.round(v), 21 | indexof: (v, i) => v.indexOf && v.indexOf(i), 22 | substring: (v, i) => v.substr(i - 1), 23 | contains: (v, i) => v.includes(i), 24 | endswith: (v, i) => v.endsWith(i), 25 | startswith: (v, i) => v.startsWith(i), 26 | length: v => v.length, 27 | tolower: v => v.toLowerCase(), 28 | toupper: v => v.toUpperCase(), 29 | trim: v => v.trim(), 30 | concat: (v,i) => typeof v == 'number' ? v.toString().concat(i) : v.concat(i), 31 | year: v => v.getFullYear(), 32 | month: v => v.getMonth() + 1, 33 | day: v => v.getDate(), 34 | hour: v => v.getHours(), 35 | minute: v => v.getMinutes(), 36 | second: v => v.getSeconds(), 37 | now: () => new Date(), 38 | maxdatetime: () => maxDateTime, 39 | mindatetime: () => minDateTime, 40 | floor: v => Math.floor(v), 41 | ceiling: v => Math.ceil(v), 42 | //isof: (v, i) => return v 43 | } 44 | 45 | export class FilterVisitor implements VisitorMap { 46 | [k: string]: VisitorFunc; 47 | 48 | 49 | Visit(node: Token, context: any) { 50 | //console.log("Visiting: ", node.type) 51 | if (!node) { 52 | throw new Error("Node cannot be empty") 53 | } 54 | switch (node.type) { 55 | //these are auto handled by visitor bubbling 56 | case TokenType.CollectionPathExpression: 57 | case TokenType.LambdaPredicateExpression: 58 | case TokenType.MemberExpression: 59 | case TokenType.SingleNavigationExpression: 60 | case TokenType.CommonExpression: 61 | case TokenType.ArrayOrObject: 62 | case undefined: 63 | break; 64 | default: 65 | const fun = this[`Visit${node.type}`] 66 | if (fun) { 67 | return fun.call(this, node, context) 68 | } 69 | console.log(`Unhandled node type, falling back: ${node.type}`) 70 | } 71 | return this.Visit(node.value, context) 72 | } 73 | 74 | //todo fix AST so that we dont need this 75 | private VisitFirstMemberExpression(node: Token, context: any) { 76 | if (Array.isArray(node.value)) { 77 | const [current, next] = node.value 78 | return this.VisitODataIdentifier({value:{ current, next}}, context) 79 | } 80 | return this.Visit(node.value, context) 81 | } 82 | 83 | private VisitBinaryExpression(node: Token, context: any) { 84 | return [this.Visit(node.value.left, context), this.Visit(node.value.right, context)] 85 | } 86 | 87 | protected VisitBoolParenExpression(node: Token, context: any) { 88 | var inner = this.Visit(node.value, context) 89 | return a => !!inner(a) 90 | } 91 | 92 | protected VisitLambdaVariableExpression(node: Token, context: any) { 93 | return a => a 94 | } 95 | 96 | protected VisitCountExpression(node: Token, context: any) { 97 | return a => (a && a.length) || 0 98 | } 99 | 100 | protected VisitAllExpression(node: Token, context: any) { 101 | const predicate = this.Visit(node.value.predicate, context) 102 | return a => a.every && a.every(predicate) 103 | } 104 | 105 | protected VisitAnyExpression(node: Token, context: any) { 106 | const predicate = this.Visit(node.value.predicate, context) 107 | return a => a.some && a.some(predicate) 108 | } 109 | 110 | 111 | protected VisitFilter(node: Token, context: any) { 112 | var predicate = this.Visit(node.value, context) 113 | return a => !!predicate(a) 114 | } 115 | 116 | protected VisitNotExpression(node: Token, context: any) { 117 | var expression = this.Visit(node.value, context) 118 | return a => !expression(a) 119 | } 120 | 121 | protected VisitEqualsExpression(node: Token, context: any) { 122 | var [left, right] = this.VisitBinaryExpression(node, context) 123 | return a => left(a) === right(a) 124 | } 125 | 126 | protected VisitNotEqualsExpression(node: Token, context: any) { 127 | var [left, right] = this.VisitBinaryExpression(node, context) 128 | return a => left(a) !== right(a) 129 | } 130 | 131 | protected VisitGreaterThanExpression(node: Token, context: any) { 132 | var [left, right] = this.VisitBinaryExpression(node, context) 133 | return a => left(a) > right(a) 134 | } 135 | 136 | protected VisitLesserThanExpression(node: Token, context: any) { 137 | var [left, right] = this.VisitBinaryExpression(node, context) 138 | return a => left(a) < right(a) 139 | } 140 | 141 | protected VisitLesserOrEqualsExpression(node: Token, context: any) { 142 | var [left, right] = this.VisitBinaryExpression(node, context) 143 | return a => left(a) <= right(a) 144 | } 145 | 146 | protected VisitGreaterOrEqualsExpression(node: Token, context: any) { 147 | var [left, right] = this.VisitBinaryExpression(node, context) 148 | return a => left(a) >= right(a) 149 | } 150 | 151 | protected VisitImplicitVariableExpression(node: Token, context: any) { 152 | return a => a 153 | } 154 | protected VisitAndExpression(node: Token, context: any) { 155 | var [left, right] = this.VisitBinaryExpression(node, context) 156 | return a => left(a) && right(a) 157 | } 158 | 159 | protected VisitAddExpression(node: Token, context: any) { 160 | var [left, right] = this.VisitBinaryExpression(node, context) 161 | return a => left(a) + right(a) 162 | } 163 | protected VisitSubExpression(node: Token, context: any) { 164 | var [left, right] = this.VisitBinaryExpression(node, context) 165 | return a => left(a) - right(a) 166 | } 167 | protected VisitMulExpression(node: Token, context: any) { 168 | var [left, right] = this.VisitBinaryExpression(node, context) 169 | return a => left(a) * right(a) 170 | } 171 | protected VisitDivExpression(node: Token, context: any) { 172 | var [left, right] = this.VisitBinaryExpression(node, context) 173 | return a => left(a) / right(a) 174 | } 175 | protected VisitModExpression(node: Token, context: any) { 176 | var [left, right] = this.VisitBinaryExpression(node, context) 177 | return a => left(a) % right(a) 178 | } 179 | 180 | protected VisitParenExpression(node: Token, context: any) { 181 | var fn = this.Visit(node.value, context) 182 | return a => fn(a) 183 | } 184 | 185 | protected getLiteral(node: Token): any { 186 | switch(node.value) { 187 | case 'null': return null 188 | case "Edm.SByte": return parseInt(node.raw) 189 | case "Edm.Decimal": return parseFloat(node.raw) 190 | case "Edm.Double": switch(node.raw) { 191 | case 'INF': return Infinity 192 | default: return parseFloat(node.raw) 193 | } 194 | case "Edm.Boolean": return node.raw === "true" 195 | //todo: check if string is actually a valid literal type 196 | case "string": 197 | case "Edm.String": 198 | return decodeURIComponent(node.raw).replace(/'/g,'').replace(/\"/g,""); 199 | case "Edm.DateTimeOffset": 200 | case "Edm.Date": 201 | return new Date(node.raw) 202 | case "Edm.TimeOfDay": 203 | return new Date(`1970-01-01T${node.raw}Z`) 204 | case "Edm.Duration": 205 | var m = node.raw.match(/P([0-9]*D)?T?([0-9]{1,2}H)?([0-9]{1,2}M)?([\.0-9]*S)?/) 206 | if (m) { 207 | var d = new Date(0); 208 | for(var i = 1; i < m.length; i++) { 209 | switch(m[i].slice(-1)) { 210 | case 'D': d.setDate(parseInt(m[i])); continue; 211 | case 'H': d.setHours(parseInt(m[i])); continue; 212 | case 'M': d.setMinutes(parseInt(m[i])); continue; 213 | case 'S': d.setSeconds(parseInt(m[i])); continue; 214 | } 215 | } 216 | return d; 217 | } 218 | default: 219 | console.log("unknown value type:" + node.value) 220 | } 221 | return node.raw 222 | } 223 | protected VisitLiteral(node: Token, context: any) { 224 | return a => this.getLiteral(node) 225 | } 226 | 227 | protected VisitMethodCallExpression(node: Token, context: any) { 228 | var method = ODataMethodMap[node.value.method] 229 | var params = (node.value.parameters || []).map(p => this.Visit(p, context)) 230 | if (!method) { 231 | console.log(`Unknown method ${node.value.method}`) 232 | return a => a 233 | } 234 | return a => method.apply(this, params.map(p => p(a))) 235 | } 236 | 237 | protected VisitPropertyPathExpression(node: Token, context: any) { 238 | if (node.value.name) { 239 | return a => a[node.value.name] 240 | } 241 | if (node.value.current && node.value.next){ 242 | const current = this.Visit(node.value.current, context) 243 | const next = this.Visit(node.value.next, context) 244 | return a => next(current(a) || {}) 245 | } 246 | return this.Visit(node.value, context); 247 | } 248 | 249 | protected VisitODataIdentifier(node: Token, context: any) { 250 | if (node.value.name) { 251 | return a => a[node.value.name] 252 | } 253 | const current = this.Visit(node.value.current, context) 254 | const next = this.Visit(node.value.next, context) 255 | return a => next(current(a) || {}) 256 | } 257 | 258 | protected VisitIsOfExpression(node: Token, context: any) { 259 | var type = this.Visit(node.value.typename, context) 260 | if (!node.value.target) { 261 | return a => { 262 | return a.constructor.name === type(a) 263 | } 264 | } 265 | var target = this.Visit(node.value.target, context) 266 | return a => target(a).constructor.name === type(a) 267 | } 268 | 269 | protected VisitQualifiedEntityTypeName(node: Token, context: any) { 270 | return a => this.Visit(node.value, context)(a); 271 | } 272 | 273 | protected VisitEntityTypeName(node: Token, context: any) { 274 | return a => node.value.name; 275 | } 276 | 277 | protected VisitOrExpression(node: Token, context: any) { 278 | var [left, right] = this.VisitBinaryExpression(node, context) 279 | return a => left(a) || right(a) 280 | } 281 | 282 | private resolveIdentifier(node) { 283 | switch(node.value) { 284 | case 'EntityTypeName': 285 | return node.raw.split(".").slice(-1)[0] 286 | } 287 | } 288 | //this needs double check. why is model type model as 'identifier' 289 | protected VisitIdentifier(node, context) { 290 | return a => this.resolveIdentifier(node) 291 | } 292 | 293 | 294 | protected VisitArray(node: Token, context: any) { 295 | var items = (node.value.items || []).map( item => this.Visit(item, context)) 296 | return a => [...items.map(item => item(a))] 297 | } 298 | 299 | protected VisitNegateExpression(node: Token, context: any) { 300 | var exp = this.Visit(node.value, context) 301 | return a => -exp(a) 302 | } 303 | } 304 | -------------------------------------------------------------------------------- /test/filterVisitor.test.js: -------------------------------------------------------------------------------- 1 | var createFilter = require('../lib').createFilter 2 | var expect = require('chai').expect 3 | 4 | describe("inmemory visitor", () => { 5 | var f 6 | var e 7 | beforeEach(function() { 8 | var match 9 | if (match = this.currentTest.title.match(/expression[^\:]*\: ?(.*)/)) { 10 | e = f = createFilter(match[1]) 11 | } 12 | }) 13 | 14 | //all numbers are referencing this: 15 | //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 16 | 17 | it("expression 5.1.1.6.3: null", () => { 18 | expect(e()).to.be.null 19 | }) 20 | 21 | it("expression 5.1.1.6.1: NullValue eq null", () => { 22 | expect(f({NullValue: null})).to.be.true 23 | }) 24 | 25 | it("expression 5.1.1.6.1: duration'P12DT23H59M59.999999999999S'", () => { 26 | expect(e({}).getTime()).to.eql(1036799000) 27 | }) 28 | 29 | it("expression 5.1.1.6.1: A eq INF", () => { 30 | expect(f({A: Infinity})).to.be.true 31 | }) 32 | 33 | it("expression 5.1.1.6.1: A eq 0.31415926535897931e1", () => { 34 | expect(f({A: 0.31415926535897931e1})).to.be.true 35 | }) 36 | 37 | it("expression 5.1.1.6.1: A add B eq hour(07:59:59.999)", () => { 38 | expect(f({A: 3, B: 4})).to.be.true 39 | }) 40 | 41 | it('expression 5.1.1.6.2: ["a","b"]', () => { 42 | expect(e({A: 3, B: 4})).to.eql(['a','b']) 43 | }) 44 | 45 | it("expression 5.1.1.1.1: 1 eq 1", () => { 46 | expect(f()).to.be.true 47 | }) 48 | 49 | it("expression 5.1.1.1.1: null eq null", () => { 50 | expect(f()).to.be.true 51 | }) 52 | 53 | it("expression 5.1.1.1.2: A ne 1", () => { 54 | expect(f({A:1})).to.equal(false) 55 | expect(f({A:2})).to.equal(true) 56 | }) 57 | 58 | it("expression 5.1.1.1.3: A gt 2", () => { 59 | expect(f({A: 3})).to.equal(true) 60 | }) 61 | 62 | it("expression 5.1.1.1.4: A ge 3", () => { 63 | expect(f({A: 3})).to.equal(true) 64 | }) 65 | 66 | it("expression 5.1.1.1.5: A lt 2", () => { 67 | expect(f({A: 1})).to.equal(true) 68 | }) 69 | 70 | it("expression 5.1.1.1.6: A le 2", () => { 71 | expect(f({A: 2})).to.equal(true) 72 | }) 73 | 74 | it("expression 5.1.1.2.3: -A eq 1", () => { 75 | expect(f({A:-1})).to.be.true 76 | }) 77 | 78 | it("expression: A", () => { 79 | expect(f({A:1})).to.equal(1) 80 | }) 81 | 82 | it("expression: A/b", () => { 83 | expect(f({A:{b:42}})).to.equal(42) 84 | }) 85 | 86 | it("expression: A/b eq 1", () => { 87 | expect(f({A:{b:1}})).to.equal(true) 88 | expect(f({A:{b:2}})).to.equal(false) 89 | }) 90 | 91 | it("expression: A/b eq A/b", () => { 92 | expect(f({A:{b:1}})).to.equal(true) 93 | }) 94 | 95 | it("expression 5.1.1.3: (A/b eq B/a) or (B/c lt 4) and ((E add 2) gt B add A)", () => { 96 | expect(f({A:{b:1}})).to.equal(false) 97 | }) 98 | 99 | it("expression 5.1.1.1.9: not A ne 1", () => { 100 | expect(f({A:1})).to.equal(true) 101 | expect(f({A:2})).to.equal(false) 102 | }) 103 | 104 | xit("expression 5.1.1.1.10: A has enum'b", () => { 105 | 106 | }) 107 | 108 | it("expression 4.8: A/$count", () => { 109 | expect(f({A:[1,2,3]})).to.equal(3) 110 | }) 111 | 112 | it("expression: A/$count eq 3", () => { 113 | expect(f({A:[1,2,3]})).to.equal(true) 114 | }) 115 | 116 | it("expression: A le B", () => { 117 | expect(f({A:1, B:2})).to.equal(true) 118 | expect(f({A:2, B:2})).to.equal(true) 119 | expect(f({A:3, B:2})).to.equal(false) 120 | }) 121 | 122 | it("expression: A ge B", () => { 123 | expect(f({A:1, B:2})).to.equal(false) 124 | expect(f({A:2, B:2})).to.equal(true) 125 | expect(f({A:3, B:2})).to.equal(true) 126 | }) 127 | 128 | it("expression 5.1.1.1.7: A and B", () => { 129 | expect(f({A:1, B:2})).to.equal(2) 130 | }) 131 | 132 | it("expression 5.1.1.1.8: A or B", () => { 133 | expect(e({A:1, B:2})).to.equal(1) 134 | }) 135 | 136 | it("expression: (A and B)", () => { 137 | expect(f({A:1, B:2})).to.equal(true) 138 | }) 139 | 140 | 141 | it("expression: A/$count gt 2 and A/$count lt 4", () => { 142 | expect(f({A:[1,2,3]})).to.equal(true) 143 | }) 144 | 145 | it("expression: (A/$count gt 2) and A/$count lt 3", () => { 146 | expect(f({A:[1,2,3]})).to.equal(false) 147 | }) 148 | 149 | it("expression 5.1.1.2.1: A add B", () => { 150 | expect(f({A:1, B:2})).to.equal(3) 151 | }) 152 | 153 | //undone 154 | xit("expression 5.1.1.2.1: '2016-01-01' add 'P4DT15H'", () => { 155 | expect(f({A:1, B:2})).to.equal(3) 156 | }) 157 | 158 | //undone 159 | xit("expression 5.1.1.2.1: '2016-01-01T12:00:00Z' add 'P4DT15H'", () => { 160 | expect(f({A:1, B:2})).to.equal(3) 161 | }) 162 | 163 | //undone 164 | xit("expression 5.1.1.2.1: duration'P4DT15H' add duration'P4DT15H'", () => { 165 | expect(f({A:1, B:2})).to.equal(3) 166 | }) 167 | 168 | 169 | it("expression 5.1.1.2.1: A sub B", () => { 170 | expect(f({A:1, B:2})).to.equal(-1) 171 | }) 172 | 173 | xit("expression 5.1.1.2.1: '2016-01-01T12:00:00Z' sub 'P4DT15H'", () => { 174 | expect(f({A:1, B:2})).to.equal(3) 175 | }) 176 | 177 | it("expression: 5 sub 10", () => { 178 | expect(f({A:1, B:2})).to.equal(-5) 179 | }) 180 | 181 | it("expression 5.1.1.2.4: A mul B", () => { 182 | expect(f({A:1, B:2})).to.equal(2) 183 | }) 184 | 185 | it("expression 5.1.1.2.4: 5 mul 10", () => { 186 | expect(f({A:1, B:2})).to.equal(50) 187 | }) 188 | 189 | it("expression 5.1.1.2.5: 10 div 3", () => { 190 | expect(f({A:1, B:2})).to.equal(3.3333333333333335) 191 | }) 192 | 193 | it("expression 5.1.1.2.5: (A mul B) div 10", () => { 194 | expect(f({A:1, B:2})).to.equal(0.2) 195 | }) 196 | it("expression 5.1.1.2.6: (A mod B) div 10", () => { 197 | expect(f({A:3, B:2})).to.equal(0.1) 198 | }) 199 | 200 | it("expression: A add 'B'", () => { 201 | expect(f({A:1, B:2})).to.equal('1B') 202 | }) 203 | 204 | it("expression: 'A' add 'B'", () => { 205 | expect(f({A:1, B:2})).to.equal('AB') 206 | }) 207 | 208 | it("expression: A/$count add B/$count eq 7", () => { 209 | expect(f({A:[1,2,3], B:[1,2,3,4]})).to.equal(true) 210 | }) 211 | 212 | it("expression: $it eq 5", () => { 213 | expect(f(5)).to.equal(true) 214 | }) 215 | 216 | it("expression: true eq false", () => { 217 | expect(f(5)).to.equal(false) 218 | }) 219 | 220 | it("expression: true eq false", () => { 221 | expect(f(5)).to.equal(false) 222 | }) 223 | 224 | it("expression: A/all(i:i eq 1)", () => { 225 | expect(f({A: [1,1,1,1]})).to.equal(true) 226 | expect(f({A: [1,1,1,2]})).to.equal(false) 227 | }) 228 | 229 | it("expression: A/all(i:$it eq 1)", () => { 230 | expect(f({A: [1,1,1,1]})).to.equal(true) 231 | }) 232 | 233 | it("expression 5.1.1.5.2: A/all(i: i/a eq 1)", () => { 234 | expect(f({A: [{a:1},{a:1}]})).to.equal(true) 235 | expect(f({A: [{a:1},{a:2}]})).to.equal(false) 236 | }) 237 | 238 | it("expression 5.1.1.5.1: A/any(i: i/a eq 1)", () => { 239 | expect(f({A: [{a:1},{a:2}]})).to.equal(true) 240 | expect(f({A: [{a:3},{a:2}]})).to.equal(false) 241 | }) 242 | 243 | it("expression 5.1.1.4.6: substring(A, 2) eq 'BC'", () => { 244 | expect(f({A:'ABC'})).to.equal(true) 245 | }) 246 | 247 | it("expression: substring('ABC', D) eq 'BC'", () => { 248 | expect(f({D:2})).to.equal(true) 249 | }) 250 | 251 | it("expression: substring('ABC', 2) eq 'BC'", () => { 252 | expect(f({})).to.equal(true) 253 | }) 254 | 255 | it("expression: A eq 1.5", () => { 256 | expect(f({A: 1.5})).to.equal(true) 257 | }) 258 | 259 | it("expression: A eq year(2016-01-01)", () => { 260 | expect(f({A: 2016})).to.equal(true) 261 | }) 262 | 263 | it("expression 5.1.1.4.1: contains(A, 'BC')", () => { 264 | expect(f({A: 'ABCD'})).to.equal(true) 265 | }) 266 | 267 | it("expression 5.1.1.4.2: endswith(A, 'CD')", () => { 268 | expect(f({A: 'ABCD'})).to.equal(true) 269 | expect(f({A: 'ABCDE'})).to.equal(false) 270 | }) 271 | 272 | it("expression 5.1.1.4.3: startswith(A, 'CD')", () => { 273 | expect(f({A: 'CDE'})).to.equal(true) 274 | expect(f({A: 'ABCDE'})).to.equal(false) 275 | }) 276 | 277 | it("expression 5.1.1.4.4: length(A) eq 3", () => { 278 | expect(f({A: 'CDE'})).to.equal(true) 279 | expect(f({A: 'ABCDE'})).to.equal(false) 280 | }) 281 | it("expression 5.1.1.4.5: indexof(A, 'DE')", () => { 282 | expect(f({A: 'CDE'})).to.equal(1) 283 | expect(f({A: 'ABCDE'})).to.equal(3) 284 | }) 285 | 286 | it("expression 5.1.1.4.7: tolower(A) eq 'abc'", () => { 287 | expect(f({A: 'ABC'})).to.equal(true) 288 | expect(f({A: 'DEF'})).to.equal(false) 289 | }) 290 | 291 | it("expression 5.1.1.4.8: toupper(A) eq 'ABC'", () => { 292 | expect(f({A: 'abc'})).to.equal(true) 293 | expect(f({A: 'def'})).to.equal(false) 294 | }) 295 | 296 | it("expression 5.1.1.4.9: trim(A) eq 'abc'", () => { 297 | expect(f({A: ' abc '})).to.equal(true) 298 | }) 299 | 300 | it("expression 5.1.1.4.10: concat(A,B) eq 'fubar'", () => { 301 | expect(f({A:'fu',B:'bar'})).to.equal(true) 302 | }) 303 | 304 | it("expression 5.1.1.4.10: concat(A,concat(B,C)) eq 'fubardoh'", () => { 305 | expect(f({A:'fu',B:'bar', C:'doh'})).to.equal(true) 306 | }) 307 | 308 | it("expression 5.1.1.4.11: A eq year(2016-01-01T13:00Z)", () => { 309 | expect(f({A: 2016})).to.equal(true) 310 | }) 311 | 312 | it("expression 5.1.1.4.12: A eq month(2016-01-01T13:00Z)", () => { 313 | expect(f({A: 1})).to.equal(true) 314 | }) 315 | 316 | it("expression 5.1.1.4.13: A eq day(2016-01-01T13:00Z)", () => { 317 | expect(f({A: 1})).to.equal(true) 318 | }) 319 | 320 | it("expression 5.1.1.4.14: A eq hour(2016-01-01T13:00Z)", () => { 321 | expect(f({A: 13})).to.equal(true) 322 | }) 323 | 324 | it("expression 5.1.1.4.15: A eq minute(2016-01-01T13:00Z)", () => { 325 | expect(f({A: 0})).to.equal(true) 326 | }) 327 | 328 | it("expression 5.1.1.4.16: A eq second(2016-01-01T13:00:02Z)", () => { 329 | expect(f({A: 02})).to.equal(true) 330 | }) 331 | 332 | it("expression 5.1.1.4.21: year(now())", () => { 333 | expect(f({})).to.equal(new Date().getFullYear()) 334 | }) 335 | 336 | it("expression 5.1.1.4.22: year(maxdatetime())", () => { 337 | expect(f({})).to.equal(275760) 338 | }) 339 | 340 | it("expression 5.1.1.4.23: year(mindatetime())", () => { 341 | expect(f({})).to.equal(-271821) 342 | }) 343 | 344 | //parser ok 345 | //undone 346 | xit("expression 5.1.1.4.24: totalseconds(A)", () => { 347 | expect(f({A: 'P6DT23H59M59.9999S'})).to.equal(-271821) 348 | }) 349 | 350 | it("expression 5.1.1.4.25: round(A) eq 42", () => { 351 | expect(f({A: 41.9})).to.equal(true) 352 | }) 353 | 354 | it("expression 5.1.1.4.26: floor(A) eq 42", () => { 355 | expect(f({A: 42.9})).to.equal(true) 356 | }) 357 | 358 | it("expression 5.1.1.4.27: ceiling(A) eq 42", () => { 359 | expect(f({A: 41.3})).to.equal(true) 360 | }) 361 | 362 | it("expression 5.1.1.4.28: isof(Model.Order)", () => { 363 | function Order() { } 364 | expect(f(new Order())).to.equal(true) 365 | }) 366 | 367 | it("expression 5.1.1.4.28: isof($it, Model.Order)", () => { 368 | function Order() { } 369 | expect(f(new Order())).to.equal(true) 370 | }) 371 | 372 | it("expression 5.1.1.4.28: Orders/all(item: isof(item, Model.Order))", () => { 373 | function Order() { } 374 | expect(f({Orders: [new Order()]})).to.equal(true) 375 | expect(f({Orders: [new Order(),{}]})).to.equal(false) 376 | }) 377 | 378 | 379 | //parser error 380 | //undone 381 | xit("expression 5.1.1.4.29: cast(A, Edm.String)", () => { 382 | expect(f({A: 41.3})).to.equal(true) 383 | }) 384 | 385 | //parser ok 386 | //undone 387 | xit("expression 5.1.1.4.30: geo.distance(A, B)", () => { 388 | expect(f({A: 41.3})).to.equal(true) 389 | }) 390 | 391 | //parser ok 392 | //undone 393 | xit("expression 5.1.1.4.31: geo.intersects(A, B)", () => { 394 | expect(f({A: 41.3})).to.equal(true) 395 | }) 396 | 397 | //parser ok 398 | //undone 399 | xit("expression 5.1.1.4.31: geo.length(A)", () => { 400 | expect(f({A: 41.3})).to.equal(true) 401 | }) 402 | 403 | 404 | 405 | }) 406 | -------------------------------------------------------------------------------- /lib/FilterVisitor.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"FilterVisitor.js","sourceRoot":"","sources":["../src/FilterVisitor.ts"],"names":[],"mappings":"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"} -------------------------------------------------------------------------------- /lib/FilterVisitor.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var lexer_1 = require('odata-v4-parser/lib/lexer'); 3 | var minDateTime = new Date(-8640000000000000); 4 | var maxDateTime = new Date(8640000000000000); 5 | exports.ODataMethodMap = { 6 | round: function (v) { return Math.round(v); }, 7 | indexof: function (v, i) { return v.indexOf && v.indexOf(i); }, 8 | substring: function (v, i) { return v.substr(i - 1); }, 9 | contains: function (v, i) { return v.includes(i); }, 10 | endswith: function (v, i) { return v.endsWith(i); }, 11 | startswith: function (v, i) { return v.startsWith(i); }, 12 | length: function (v) { return v.length; }, 13 | tolower: function (v) { return v.toLowerCase(); }, 14 | toupper: function (v) { return v.toUpperCase(); }, 15 | trim: function (v) { return v.trim(); }, 16 | concat: function (v, i) { return typeof v == 'number' ? v.toString().concat(i) : v.concat(i); }, 17 | year: function (v) { return v.getFullYear(); }, 18 | month: function (v) { return v.getMonth() + 1; }, 19 | day: function (v) { return v.getDate(); }, 20 | hour: function (v) { return v.getHours(); }, 21 | minute: function (v) { return v.getMinutes(); }, 22 | second: function (v) { return v.getSeconds(); }, 23 | now: function () { return new Date(); }, 24 | maxdatetime: function () { return maxDateTime; }, 25 | mindatetime: function () { return minDateTime; }, 26 | floor: function (v) { return Math.floor(v); }, 27 | ceiling: function (v) { return Math.ceil(v); } 28 | }; 29 | var FilterVisitor = (function () { 30 | function FilterVisitor() { 31 | } 32 | FilterVisitor.prototype.Visit = function (node, context) { 33 | //console.log("Visiting: ", node.type) 34 | if (!node) { 35 | throw new Error("Node cannot be empty"); 36 | } 37 | switch (node.type) { 38 | //these are auto handled by visitor bubbling 39 | case lexer_1.TokenType.CollectionPathExpression: 40 | case lexer_1.TokenType.LambdaPredicateExpression: 41 | case lexer_1.TokenType.MemberExpression: 42 | case lexer_1.TokenType.SingleNavigationExpression: 43 | case lexer_1.TokenType.CommonExpression: 44 | case lexer_1.TokenType.ArrayOrObject: 45 | case undefined: 46 | break; 47 | default: 48 | var fun = this[("Visit" + node.type)]; 49 | if (fun) { 50 | return fun.call(this, node, context); 51 | } 52 | console.log("Unhandled node type, falling back: " + node.type); 53 | } 54 | return this.Visit(node.value, context); 55 | }; 56 | //todo fix AST so that we dont need this 57 | FilterVisitor.prototype.VisitFirstMemberExpression = function (node, context) { 58 | if (Array.isArray(node.value)) { 59 | var _a = node.value, current = _a[0], next = _a[1]; 60 | return this.VisitODataIdentifier({ value: { current: current, next: next } }, context); 61 | } 62 | return this.Visit(node.value, context); 63 | }; 64 | FilterVisitor.prototype.VisitBinaryExpression = function (node, context) { 65 | return [this.Visit(node.value.left, context), this.Visit(node.value.right, context)]; 66 | }; 67 | FilterVisitor.prototype.VisitBoolParenExpression = function (node, context) { 68 | var inner = this.Visit(node.value, context); 69 | return function (a) { return !!inner(a); }; 70 | }; 71 | FilterVisitor.prototype.VisitLambdaVariableExpression = function (node, context) { 72 | return function (a) { return a; }; 73 | }; 74 | FilterVisitor.prototype.VisitCountExpression = function (node, context) { 75 | return function (a) { return (a && a.length) || 0; }; 76 | }; 77 | FilterVisitor.prototype.VisitAllExpression = function (node, context) { 78 | var predicate = this.Visit(node.value.predicate, context); 79 | return function (a) { return a.every && a.every(predicate); }; 80 | }; 81 | FilterVisitor.prototype.VisitAnyExpression = function (node, context) { 82 | var predicate = this.Visit(node.value.predicate, context); 83 | return function (a) { return a.some && a.some(predicate); }; 84 | }; 85 | FilterVisitor.prototype.VisitFilter = function (node, context) { 86 | var predicate = this.Visit(node.value, context); 87 | return function (a) { return !!predicate(a); }; 88 | }; 89 | FilterVisitor.prototype.VisitNotExpression = function (node, context) { 90 | var expression = this.Visit(node.value, context); 91 | return function (a) { return !expression(a); }; 92 | }; 93 | FilterVisitor.prototype.VisitEqualsExpression = function (node, context) { 94 | var _a = this.VisitBinaryExpression(node, context), left = _a[0], right = _a[1]; 95 | return function (a) { return left(a) === right(a); }; 96 | }; 97 | FilterVisitor.prototype.VisitNotEqualsExpression = function (node, context) { 98 | var _a = this.VisitBinaryExpression(node, context), left = _a[0], right = _a[1]; 99 | return function (a) { return left(a) !== right(a); }; 100 | }; 101 | FilterVisitor.prototype.VisitGreaterThanExpression = function (node, context) { 102 | var _a = this.VisitBinaryExpression(node, context), left = _a[0], right = _a[1]; 103 | return function (a) { return left(a) > right(a); }; 104 | }; 105 | FilterVisitor.prototype.VisitLesserThanExpression = function (node, context) { 106 | var _a = this.VisitBinaryExpression(node, context), left = _a[0], right = _a[1]; 107 | return function (a) { return left(a) < right(a); }; 108 | }; 109 | FilterVisitor.prototype.VisitLesserOrEqualsExpression = function (node, context) { 110 | var _a = this.VisitBinaryExpression(node, context), left = _a[0], right = _a[1]; 111 | return function (a) { return left(a) <= right(a); }; 112 | }; 113 | FilterVisitor.prototype.VisitGreaterOrEqualsExpression = function (node, context) { 114 | var _a = this.VisitBinaryExpression(node, context), left = _a[0], right = _a[1]; 115 | return function (a) { return left(a) >= right(a); }; 116 | }; 117 | FilterVisitor.prototype.VisitImplicitVariableExpression = function (node, context) { 118 | return function (a) { return a; }; 119 | }; 120 | FilterVisitor.prototype.VisitAndExpression = function (node, context) { 121 | var _a = this.VisitBinaryExpression(node, context), left = _a[0], right = _a[1]; 122 | return function (a) { return left(a) && right(a); }; 123 | }; 124 | FilterVisitor.prototype.VisitAddExpression = function (node, context) { 125 | var _a = this.VisitBinaryExpression(node, context), left = _a[0], right = _a[1]; 126 | return function (a) { return left(a) + right(a); }; 127 | }; 128 | FilterVisitor.prototype.VisitSubExpression = function (node, context) { 129 | var _a = this.VisitBinaryExpression(node, context), left = _a[0], right = _a[1]; 130 | return function (a) { return left(a) - right(a); }; 131 | }; 132 | FilterVisitor.prototype.VisitMulExpression = function (node, context) { 133 | var _a = this.VisitBinaryExpression(node, context), left = _a[0], right = _a[1]; 134 | return function (a) { return left(a) * right(a); }; 135 | }; 136 | FilterVisitor.prototype.VisitDivExpression = function (node, context) { 137 | var _a = this.VisitBinaryExpression(node, context), left = _a[0], right = _a[1]; 138 | return function (a) { return left(a) / right(a); }; 139 | }; 140 | FilterVisitor.prototype.VisitModExpression = function (node, context) { 141 | var _a = this.VisitBinaryExpression(node, context), left = _a[0], right = _a[1]; 142 | return function (a) { return left(a) % right(a); }; 143 | }; 144 | FilterVisitor.prototype.VisitParenExpression = function (node, context) { 145 | var fn = this.Visit(node.value, context); 146 | return function (a) { return fn(a); }; 147 | }; 148 | FilterVisitor.prototype.getLiteral = function (node) { 149 | switch (node.value) { 150 | case 'null': return null; 151 | case "Edm.SByte": return parseInt(node.raw); 152 | case "Edm.Decimal": return parseFloat(node.raw); 153 | case "Edm.Double": switch (node.raw) { 154 | case 'INF': return Infinity; 155 | default: return parseFloat(node.raw); 156 | } 157 | case "Edm.Boolean": return node.raw === "true"; 158 | //todo: check if string is actually a valid literal type 159 | case "string": 160 | case "Edm.String": 161 | return decodeURIComponent(node.raw).replace(/'/g, '').replace(/\"/g, ""); 162 | case "Edm.DateTimeOffset": 163 | case "Edm.Date": 164 | return new Date(node.raw); 165 | case "Edm.TimeOfDay": 166 | return new Date("1970-01-01T" + node.raw + "Z"); 167 | case "Edm.Duration": 168 | var m = node.raw.match(/P([0-9]*D)?T?([0-9]{1,2}H)?([0-9]{1,2}M)?([\.0-9]*S)?/); 169 | if (m) { 170 | var d = new Date(0); 171 | for (var i = 1; i < m.length; i++) { 172 | switch (m[i].slice(-1)) { 173 | case 'D': 174 | d.setDate(parseInt(m[i])); 175 | continue; 176 | case 'H': 177 | d.setHours(parseInt(m[i])); 178 | continue; 179 | case 'M': 180 | d.setMinutes(parseInt(m[i])); 181 | continue; 182 | case 'S': 183 | d.setSeconds(parseInt(m[i])); 184 | continue; 185 | } 186 | } 187 | return d; 188 | } 189 | default: 190 | console.log("unknown value type:" + node.value); 191 | } 192 | return node.raw; 193 | }; 194 | FilterVisitor.prototype.VisitLiteral = function (node, context) { 195 | var _this = this; 196 | return function (a) { return _this.getLiteral(node); }; 197 | }; 198 | FilterVisitor.prototype.VisitMethodCallExpression = function (node, context) { 199 | var _this = this; 200 | var method = exports.ODataMethodMap[node.value.method]; 201 | var params = (node.value.parameters || []).map(function (p) { return _this.Visit(p, context); }); 202 | if (!method) { 203 | console.log("Unknown method " + node.value.method); 204 | return function (a) { return a; }; 205 | } 206 | return function (a) { return method.apply(_this, params.map(function (p) { return p(a); })); }; 207 | }; 208 | FilterVisitor.prototype.VisitPropertyPathExpression = function (node, context) { 209 | if (node.value.name) { 210 | return function (a) { return a[node.value.name]; }; 211 | } 212 | if (node.value.current && node.value.next) { 213 | var current_1 = this.Visit(node.value.current, context); 214 | var next_1 = this.Visit(node.value.next, context); 215 | return function (a) { return next_1(current_1(a) || {}); }; 216 | } 217 | return this.Visit(node.value, context); 218 | }; 219 | FilterVisitor.prototype.VisitODataIdentifier = function (node, context) { 220 | if (node.value.name) { 221 | return function (a) { return a[node.value.name]; }; 222 | } 223 | var current = this.Visit(node.value.current, context); 224 | var next = this.Visit(node.value.next, context); 225 | return function (a) { return next(current(a) || {}); }; 226 | }; 227 | FilterVisitor.prototype.VisitIsOfExpression = function (node, context) { 228 | var type = this.Visit(node.value.typename, context); 229 | if (!node.value.target) { 230 | return function (a) { 231 | return a.constructor.name === type(a); 232 | }; 233 | } 234 | var target = this.Visit(node.value.target, context); 235 | return function (a) { return target(a).constructor.name === type(a); }; 236 | }; 237 | FilterVisitor.prototype.VisitQualifiedEntityTypeName = function (node, context) { 238 | var _this = this; 239 | return function (a) { return _this.Visit(node.value, context)(a); }; 240 | }; 241 | FilterVisitor.prototype.VisitEntityTypeName = function (node, context) { 242 | return function (a) { return node.value.name; }; 243 | }; 244 | FilterVisitor.prototype.VisitOrExpression = function (node, context) { 245 | var _a = this.VisitBinaryExpression(node, context), left = _a[0], right = _a[1]; 246 | return function (a) { return left(a) || right(a); }; 247 | }; 248 | FilterVisitor.prototype.resolveIdentifier = function (node) { 249 | switch (node.value) { 250 | case 'EntityTypeName': 251 | return node.raw.split(".").slice(-1)[0]; 252 | } 253 | }; 254 | //this needs double check. why is model type model as 'identifier' 255 | FilterVisitor.prototype.VisitIdentifier = function (node, context) { 256 | var _this = this; 257 | return function (a) { return _this.resolveIdentifier(node); }; 258 | }; 259 | FilterVisitor.prototype.VisitArray = function (node, context) { 260 | var _this = this; 261 | var items = (node.value.items || []).map(function (item) { return _this.Visit(item, context); }); 262 | return function (a) { return items.map(function (item) { return item(a); }).slice(); }; 263 | }; 264 | FilterVisitor.prototype.VisitNegateExpression = function (node, context) { 265 | var exp = this.Visit(node.value, context); 266 | return function (a) { return -exp(a); }; 267 | }; 268 | return FilterVisitor; 269 | }()); 270 | exports.FilterVisitor = FilterVisitor; 271 | --------------------------------------------------------------------------------