├── .gitignore ├── .npmignore ├── .npmrc ├── README.md ├── package.json ├── src ├── index.ts └── visitor.ts ├── test ├── mssql.spec.js ├── oracle.spec.js ├── query.spec.js ├── where-parameters.spec.js └── where.spec.js ├── tsconfig.json └── yarn.lock /.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 | 35 | .vscode 36 | lib -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 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 | 35 | .vscode -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | @types:registry=https://registry.npmjs.org 2 | loglevel="warn" 3 | //SECRET -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OData V4 Service modules - SQL Connector 2 | 3 | Service OData v4 requests from an SQL data store. 4 | 5 | ## Synopsis 6 | The OData V4 SQL Connector provides functionality to convert the various types of OData segments 7 | into SQL query statements, that you can execute over an SQL database. 8 | 9 | ## Potential usage scenarios 10 | 11 | - Create high speed, standard compliant data sharing APIs 12 | 13 | ## Usage as server - TypeScript 14 | ```javascript 15 | import { createFilter } from 'odata-v4-sql' 16 | 17 | //example request: GET /api/Users?$filter=Id eq 42 18 | app.get("/api/Users", (req: Request, res: Response) => { 19 | const filter = createFilter(req.query.$filter); 20 | // request instance from mssql module 21 | request.query(`SELECT * FROM Users WHERE ${filter.where}`, function(err, data){ 22 | res.json({ 23 | '@odata.context': req.protocol + '://' + req.get('host') + '/api/$metadata#Users', 24 | value: data 25 | }); 26 | }); 27 | }); 28 | ``` 29 | 30 | Advanced TypeScript example available [here](https://raw.githubusercontent.com/jaystack/odata-v4-sql/master/src/example/sql.ts). 31 | 32 | ## Usage ES5 33 | ```javascript 34 | var createFilter = require('odata-v4-sql').createFilter; 35 | 36 | app.get("/api/Users", function(req, res) { 37 | var filter = createFilter(req.query.$filter); 38 | // request instance from mssql module 39 | request.query(filter.from("Users"), function(err, data){ 40 | res.json({ 41 | '@odata.context': req.protocol + '://' + req.get('host') + '/api/$metadata#Users', 42 | value: data 43 | }); 44 | }); 45 | }) 46 | ``` 47 | 48 | ## Supported OData segments 49 | 50 | * $filter 51 | * $select 52 | * $skip 53 | * $top 54 | * $orderby 55 | * $expand -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "odata-v4-sql", 3 | "version": "0.1.2", 4 | "description": "OData to SQL query compiler", 5 | "main": "lib/index.js", 6 | "typings": "lib/index", 7 | "directories": { 8 | "test": "test" 9 | }, 10 | "scripts": { 11 | "build": "tsc", 12 | "pretest": "npm run build", 13 | "test": "mocha", 14 | "pretdd": "npm run build", 15 | "tdd": "mocha -w", 16 | "prepare": "npm run build" 17 | }, 18 | "repository": { 19 | "type": "git", 20 | "url": "git+https://github.com/jaystack/odata-v4-sql.git" 21 | }, 22 | "keywords": [ 23 | "OData", 24 | "V4", 25 | "sql" 26 | ], 27 | "author": "JayStack", 28 | "license": "MIT", 29 | "bugs": { 30 | "url": "https://github.com/jaystack/odata-v4-sql/issues" 31 | }, 32 | "homepage": "https://github.com/jaystack/odata-v4-sql#readme", 33 | "dependencies": { 34 | "odata-v4-literal": "^0.1.0", 35 | "odata-v4-parser": "0.1.13" 36 | }, 37 | "devDependencies": { 38 | "chai": "^3.5.0", 39 | "mocha": "^3.1.2", 40 | "typescript": "^2.5.1" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import { Visitor, SQLLang } from "./visitor"; 2 | export { SQLLang } from "./visitor"; 3 | import { filter, query } from "odata-v4-parser"; 4 | import { Token } from "odata-v4-parser/lib/lexer"; 5 | 6 | export interface SqlOptions{ 7 | useParameters?:boolean 8 | type?:SQLLang 9 | } 10 | 11 | /** 12 | * Creates an SQL query descriptor from an OData query string 13 | * @param {string} odataQuery - An OData query string 14 | * @return {string} SQL query descriptor 15 | * @example 16 | * const filter = createQuery("$filter=Size eq 4 and Age gt 18"); 17 | * let sqlQuery = `SELECT * FROM table WHERE ${filter.where}`; 18 | */ 19 | export function createQuery(odataQuery:string, options?:SqlOptions):Visitor; 20 | export function createQuery(odataQuery:string, options?:SqlOptions, type?:SQLLang):Visitor; 21 | export function createQuery(odataQuery:Token, options?:SqlOptions):Visitor; 22 | export function createQuery(odataQuery:Token, options?:SqlOptions, type?:SQLLang):Visitor; 23 | export function createQuery(odataQuery:string | Token, options = {}, type?:SQLLang):Visitor{ 24 | if (typeof type != "undefined" && type) options.type = type; 25 | let ast:Token = (typeof odataQuery == "string" ? query(odataQuery) : odataQuery); 26 | return new Visitor(options).Visit(ast).asType(); 27 | } 28 | 29 | /** 30 | * Creates an SQL WHERE clause from an OData filter expression string 31 | * @param {string} odataFilter - A filter expression in OData $filter format 32 | * @return {string} SQL WHERE clause 33 | * @example 34 | * const filter = createFilter("Size eq 4 and Age gt 18"); 35 | * let sqlQuery = `SELECT * FROM table WHERE ${filter}`; 36 | */ 37 | export function createFilter(odataFilter:string, options?:SqlOptions):Visitor; 38 | export function createFilter(odataFilter:string, options?:SqlOptions, type?:SQLLang):Visitor; 39 | export function createFilter(odataFilter:Token, options?:SqlOptions):Visitor; 40 | export function createFilter(odataFilter:Token, options?:SqlOptions, type?:SQLLang):Visitor; 41 | export function createFilter(odataFilter:string | Token, options = {}, type?:SQLLang):Visitor{ 42 | if (typeof type != "undefined" && type) options.type = type; 43 | let ast:Token = (typeof odataFilter == "string" ? filter(odataFilter) : odataFilter); 44 | return new Visitor(options).Visit(ast).asType(); 45 | } -------------------------------------------------------------------------------- /src/visitor.ts: -------------------------------------------------------------------------------- 1 | import { Token } from "odata-v4-parser/lib/lexer"; 2 | import { Literal } from "odata-v4-literal"; 3 | import { SqlOptions } from "./index"; 4 | 5 | export class SQLLiteral extends Literal{ 6 | static convert(type:string, value:string):any { 7 | return (new SQLLiteral(type, value)).valueOf(); 8 | } 9 | 'Edm.String'(value:string){ return "'" + decodeURIComponent(value).slice(1, -1).replace(/''/g, "'") + "'"; } 10 | 'Edm.Guid'(value:string){ return "'" + decodeURIComponent(value) + "'"; } 11 | 'Edm.Date'(value:string){ return "'" + value + "'"; } 12 | 'Edm.DateTimeOffset'(value:string):any{ return "'" + value.replace("T", " ").replace("Z", " ").trim() + "'"; } 13 | 'Edm.Boolean'(value:string):any{ 14 | value = value || ''; 15 | switch (value.toLowerCase()){ 16 | case 'true': return 1; 17 | case 'false': return 0; 18 | default: return "NULL"; 19 | } 20 | } 21 | 'null'(value:string){ return "NULL"; } 22 | } 23 | 24 | export enum SQLLang{ 25 | ANSI, 26 | MsSql, 27 | MySql, 28 | PostgreSql, 29 | Oracle 30 | } 31 | 32 | export class Visitor{ 33 | protected options:SqlOptions 34 | type:SQLLang; 35 | select:string = ""; 36 | where:string = ""; 37 | orderby:string = ""; 38 | skip:number 39 | limit:number 40 | inlinecount:boolean 41 | navigationProperty:string 42 | includes:Visitor[] = []; 43 | parameters:any = new Map(); 44 | protected parameterSeed:number = 0; 45 | protected originalWhere:string; 46 | ast:Token 47 | 48 | constructor(options = {}){ 49 | this.options = options; 50 | if (this.options.useParameters != false) this.options.useParameters = true; 51 | this.type = options.type || SQLLang.ANSI; 52 | } 53 | 54 | from(table:string){ 55 | let sql = `SELECT ${this.select} FROM [${table}] WHERE ${this.where} ORDER BY ${this.orderby}`; 56 | switch (this.type){ 57 | case SQLLang.Oracle: 58 | case SQLLang.MsSql: 59 | if (typeof this.skip == "number") sql += ` OFFSET ${this.skip} ROWS`; 60 | if (typeof this.limit == "number"){ 61 | if (typeof this.skip != "number") sql += " OFFSET 0 ROWS"; 62 | sql += ` FETCH NEXT ${this.limit} ROWS ONLY`; 63 | } 64 | break; 65 | case SQLLang.MySql: 66 | case SQLLang.PostgreSql: 67 | default: 68 | if (typeof this.limit == "number") sql += ` LIMIT ${this.limit}`; 69 | if (typeof this.skip == "number") sql += ` OFFSET ${this.skip}`; 70 | break; 71 | } 72 | return sql; 73 | } 74 | 75 | asMsSql(){ 76 | this.type = SQLLang.MsSql; 77 | let rx = new RegExp("\\?", "g"); 78 | let keys = this.parameters.keys(); 79 | this.originalWhere = this.where; 80 | this.where = this.where.replace(rx, () => `@${keys.next().value}`); 81 | this.includes.forEach((item) => item.asMsSql()); 82 | return this; 83 | } 84 | 85 | asOracleSql(){ 86 | this.type = SQLLang.Oracle; 87 | let rx = new RegExp("\\?", "g"); 88 | let keys = this.parameters.keys(); 89 | this.originalWhere = this.where; 90 | this.where = this.where.replace(rx, () => `:${keys.next().value}`); 91 | this.includes.forEach((item) => item.asOracleSql()); 92 | return this; 93 | } 94 | 95 | asAnsiSql(){ 96 | this.type = SQLLang.ANSI; 97 | this.where = this.originalWhere || this.where; 98 | this.includes.forEach((item) => item.asAnsiSql()); 99 | return this; 100 | } 101 | 102 | asType(){ 103 | switch (this.type){ 104 | case SQLLang.MsSql: return this.asMsSql(); 105 | case SQLLang.ANSI: 106 | case SQLLang.MySql: 107 | case SQLLang.PostgreSql: return this.asAnsiSql(); 108 | case SQLLang.Oracle: return this.asOracleSql(); 109 | default: return this; 110 | } 111 | } 112 | 113 | Visit(node:Token, context?:any){ 114 | this.ast = this.ast || node; 115 | context = context || { target: "where" }; 116 | 117 | if (node){ 118 | var visitor = this[`Visit${node.type}`]; 119 | if (visitor) visitor.call(this, node, context); 120 | else console.log(`Unhandled node type: ${node.type}`, node); 121 | } 122 | 123 | if (node == this.ast){ 124 | if (!this.select) this.select = `*`; 125 | if (!this.where) this.where = "1 = 1"; 126 | if (!this.orderby) this.orderby = "1"; 127 | } 128 | return this; 129 | } 130 | 131 | protected VisitODataUri(node:Token, context:any){ 132 | this.Visit(node.value.resource, context); 133 | this.Visit(node.value.query, context); 134 | } 135 | 136 | protected VisitExpand(node: Token, context: any) { 137 | node.value.items.forEach((item) => { 138 | let expandPath = item.value.path.raw; 139 | let visitor = this.includes.filter(v => v.navigationProperty == expandPath)[0]; 140 | if (!visitor){ 141 | visitor = new Visitor(this.options); 142 | visitor.parameterSeed = this.parameterSeed; 143 | this.includes.push(visitor); 144 | } 145 | visitor.Visit(item); 146 | this.parameterSeed = visitor.parameterSeed; 147 | }); 148 | } 149 | 150 | protected VisitExpandItem(node: Token, context: any) { 151 | this.Visit(node.value.path, context); 152 | if (node.value.options) node.value.options.forEach((item) => this.Visit(item, context)); 153 | } 154 | 155 | protected VisitExpandPath(node: Token, context: any) { 156 | this.navigationProperty = node.raw; 157 | } 158 | 159 | protected VisitQueryOptions(node:Token, context:any){ 160 | node.value.options.forEach((option) => this.Visit(option, context)); 161 | } 162 | 163 | protected VisitInlineCount(node:Token, context:any){ 164 | this.inlinecount = Literal.convert(node.value.value, node.value.raw); 165 | } 166 | 167 | protected VisitFilter(node:Token, context:any){ 168 | context.target = "where"; 169 | this.Visit(node.value, context); 170 | if (!this.where) this.where = "1 = 1"; 171 | } 172 | 173 | protected VisitOrderBy(node:Token, context:any){ 174 | context.target = "orderby"; 175 | node.value.items.forEach((item, i) => { 176 | this.Visit(item, context); 177 | if (i < node.value.items.length - 1) this.orderby += ", "; 178 | }); 179 | } 180 | 181 | protected VisitOrderByItem(node:Token, context:any){ 182 | this.Visit(node.value.expr, context); 183 | this.orderby += node.value.direction > 0 ? " ASC" : " DESC"; 184 | } 185 | 186 | protected VisitSkip(node:Token, context:any){ 187 | this.skip = +node.value.raw; 188 | } 189 | 190 | protected VisitTop(node:Token, context:any){ 191 | this.limit = +node.value.raw; 192 | } 193 | 194 | protected VisitSelect(node:Token, context:any){ 195 | context.target = "select"; 196 | node.value.items.forEach((item, i) => { 197 | this.Visit(item, context); 198 | if (i < node.value.items.length - 1) this.select += ", "; 199 | }); 200 | } 201 | 202 | protected VisitSelectItem(node:Token, context:any){ 203 | let item = node.raw.replace(/\//g, '.'); 204 | this.select += `[${item}]`; 205 | } 206 | 207 | protected VisitAndExpression(node:Token, context:any){ 208 | this.Visit(node.value.left, context); 209 | this.where += " AND "; 210 | this.Visit(node.value.right, context); 211 | } 212 | 213 | protected VisitOrExpression(node:Token, context:any){ 214 | this.Visit(node.value.left, context); 215 | this.where += " OR "; 216 | this.Visit(node.value.right, context); 217 | } 218 | 219 | protected VisitBoolParenExpression(node:Token, context:any){ 220 | this.where += "("; 221 | this.Visit(node.value, context); 222 | this.where += ")"; 223 | } 224 | 225 | protected VisitCommonExpression(node:Token, context:any){ 226 | this.Visit(node.value, context); 227 | } 228 | 229 | protected VisitFirstMemberExpression(node:Token, context:any){ 230 | this.Visit(node.value, context); 231 | } 232 | 233 | protected VisitMemberExpression(node:Token, context:any){ 234 | this.Visit(node.value, context); 235 | } 236 | 237 | protected VisitPropertyPathExpression(node:Token, context:any){ 238 | if (node.value.current && node.value.next){ 239 | this.Visit(node.value.current, context); 240 | context.identifier += "."; 241 | this.Visit(node.value.next, context); 242 | }else this.Visit(node.value, context); 243 | } 244 | 245 | protected VisitSingleNavigationExpression(node:Token, context:any){ 246 | if (node.value.current && node.value.next){ 247 | this.Visit(node.value.current, context); 248 | this.Visit(node.value.next, context); 249 | }else this.Visit(node.value, context); 250 | } 251 | 252 | protected VisitODataIdentifier(node:Token, context:any){ 253 | this[context.target] += `[${node.value.name}]`; 254 | context.identifier = node.value.name; 255 | } 256 | 257 | protected VisitEqualsExpression(node:Token, context:any){ 258 | this.Visit(node.value.left, context); 259 | this.where += " = "; 260 | this.Visit(node.value.right, context); 261 | if (this.options.useParameters && context.literal == null){ 262 | this.where = this.where.replace(/= \?$/, "IS NULL").replace(new RegExp(`\\? = \\[${context.identifier}\\]$`), `[${context.identifier}] IS NULL`); 263 | }else if (context.literal == "NULL"){ 264 | this.where = this.where.replace(/= NULL$/, "IS NULL").replace(new RegExp(`NULL = \\[${context.identifier}\\]$`), `[${context.identifier}] IS NULL`); 265 | } 266 | } 267 | 268 | protected VisitNotEqualsExpression(node:Token, context:any){ 269 | this.Visit(node.value.left, context); 270 | this.where += " <> "; 271 | this.Visit(node.value.right, context); 272 | if (this.options.useParameters && context.literal == null){ 273 | this.where = this.where.replace(/<> \?$/, "IS NOT NULL").replace(new RegExp(`\\? <> \\[${context.identifier}\\]$`), `[${context.identifier}] IS NOT NULL`); 274 | }else if (context.literal == "NULL"){ 275 | this.where = this.where.replace(/<> NULL$/, "IS NOT NULL").replace(new RegExp(`NULL <> \\[${context.identifier}\\]$`), `[${context.identifier}] IS NOT NULL`); 276 | } 277 | } 278 | 279 | protected VisitLesserThanExpression(node:Token, context:any){ 280 | this.Visit(node.value.left, context); 281 | this.where += " < "; 282 | this.Visit(node.value.right, context); 283 | } 284 | 285 | protected VisitLesserOrEqualsExpression(node:Token, context:any){ 286 | this.Visit(node.value.left, context); 287 | this.where += " <= "; 288 | this.Visit(node.value.right, context); 289 | } 290 | 291 | protected VisitGreaterThanExpression(node:Token, context:any){ 292 | this.Visit(node.value.left, context); 293 | this.where += " > "; 294 | this.Visit(node.value.right, context); 295 | } 296 | 297 | protected VisitGreaterOrEqualsExpression(node:Token, context:any){ 298 | this.Visit(node.value.left, context); 299 | this.where += " >= "; 300 | this.Visit(node.value.right, context); 301 | } 302 | 303 | protected VisitLiteral(node:Token, context:any){ 304 | if (this.options.useParameters){ 305 | let name = `p${this.parameterSeed++}`; 306 | let value = Literal.convert(node.value, node.raw); 307 | context.literal = value; 308 | this.parameters.set(name, value); 309 | this.where += "?"; 310 | }else this.where += (context.literal = SQLLiteral.convert(node.value, node.raw)); 311 | } 312 | 313 | protected VisitMethodCallExpression(node:Token, context:any){ 314 | var method = node.value.method; 315 | var params = node.value.parameters || []; 316 | switch (method){ 317 | case "contains": 318 | this.Visit(params[0], context); 319 | if (this.options.useParameters){ 320 | let name = `p${this.parameterSeed++}`; 321 | let value = Literal.convert(params[1].value, params[1].raw); 322 | this.parameters.set(name, `%${value}%`); 323 | this.where += " like ?"; 324 | }else this.where += ` like '%${SQLLiteral.convert(params[1].value, params[1].raw).slice(1, -1)}%'`; 325 | break; 326 | case "endswith": 327 | this.Visit(params[0], context); 328 | if (this.options.useParameters){ 329 | let name = `p${this.parameterSeed++}`; 330 | let value = Literal.convert(params[1].value, params[1].raw); 331 | this.parameters.set(name, `%${value}`); 332 | this.where += " like ?"; 333 | }else this.where += ` like '%${SQLLiteral.convert(params[1].value, params[1].raw).slice(1, -1)}'`; 334 | break; 335 | case "startswith": 336 | this.Visit(params[0], context); 337 | if (this.options.useParameters){ 338 | let name = `p${this.parameterSeed++}`; 339 | let value = Literal.convert(params[1].value, params[1].raw); 340 | this.parameters.set(name, `${value}%`); 341 | this.where += " like ?"; 342 | }else this.where += ` like '${SQLLiteral.convert(params[1].value, params[1].raw).slice(1, -1)}%'`; 343 | break; 344 | case "indexof": 345 | let fn = ""; 346 | switch (this.type) { 347 | case SQLLang.MsSql: 348 | fn = "CHARINDEX"; 349 | break; 350 | case SQLLang.ANSI: 351 | case SQLLang.MySql: 352 | case SQLLang.PostgreSql: 353 | default: 354 | fn = "INSTR"; 355 | break; 356 | } 357 | if (fn === "CHARINDEX"){ 358 | const tmp = params[0]; 359 | params[0] = params[1]; 360 | params[1] = tmp; 361 | } 362 | this.where += `${fn}(`; 363 | this.Visit(params[0], context); 364 | this.where += ', '; 365 | this.Visit(params[1], context); 366 | this.where += ") - 1"; 367 | break; 368 | case "round": 369 | this.where += "ROUND("; 370 | this.Visit(params[0], context); 371 | this.where += ")"; 372 | break; 373 | case "length": 374 | this.where += "LEN("; 375 | this.Visit(params[0], context); 376 | this.where += ")"; 377 | break; 378 | case "tolower": 379 | this.where += "LCASE("; 380 | this.Visit(params[0], context); 381 | this.where += ")"; 382 | break; 383 | case "toupper": 384 | this.where += "UCASE("; 385 | this.Visit(params[0], context); 386 | this.where += ")"; 387 | break; 388 | case "floor": 389 | case "ceiling": 390 | case "year": 391 | case "month": 392 | case "day": 393 | case "hour": 394 | case "minute": 395 | case "second": 396 | this.where += `${method.toUpperCase()}(`; 397 | this.Visit(params[0], context); 398 | this.where += ")"; 399 | break; 400 | case "now": 401 | this.where += "NOW()"; 402 | break; 403 | case "trim": 404 | this.where += "TRIM(' ' FROM "; 405 | this.Visit(params[0], context); 406 | this.where += ")"; 407 | break; 408 | } 409 | } 410 | 411 | } 412 | -------------------------------------------------------------------------------- /test/mssql.spec.js: -------------------------------------------------------------------------------- 1 | var createFilter = require('../lib').createFilter 2 | var expect = require('chai').expect 3 | 4 | describe("SQL WHERE useParameters (MS-SQL)", () => { 5 | var f; 6 | beforeEach(function() { 7 | var match; 8 | if (match = this.currentTest.title.match(/expression[^\:]*\: ?(.*)/)) { 9 | f = createFilter(match[1], { 10 | useParameters: true, 11 | type: 1 12 | }); 13 | } 14 | }); 15 | 16 | //all numbers are referencing this: 17 | //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 18 | 19 | it("expression 5.1.1.6.1: NullValue eq null", () => { 20 | expect(f.where).to.equal("[NullValue] IS NULL") 21 | }) 22 | 23 | it("expression 5.1.1.6.1: TrueValue eq true", () => { 24 | expect(f.where).to.equal("[TrueValue] = @p0") 25 | }) 26 | 27 | it("expression 5.1.1.6.1: FalseValue eq false", () => { 28 | expect(f.where).to.equal("[FalseValue] = @p0") 29 | }) 30 | 31 | it("expression 5.1.1.6.1: IntegerValue lt -128", () => { 32 | expect(f.where).to.equal("[IntegerValue] < @p0") 33 | }) 34 | 35 | it("expression 5.1.1.6.1: DecimalValue eq 34.95", () => { 36 | expect(f.where).to.equal("[DecimalValue] = @p0") 37 | }) 38 | 39 | it("expression 5.1.1.6.1: StringValue eq 'Say Hello,then go'", () => { 40 | expect(f.where).to.equal("[StringValue] = @p0") 41 | }) 42 | 43 | it("expression 5.1.1.6.1: DurationValue eq duration'P12DT23H59M59.999999999999S'", () => { 44 | expect(f.where).to.equal("[DurationValue] = @p0") 45 | }) 46 | 47 | it("expression 5.1.1.6.1: DateValue eq 2012-12-03", () => { 48 | expect(f.where).to.equal("[DateValue] = @p0") 49 | }) 50 | 51 | it("expression 5.1.1.6.1: DateTimeOffsetValue eq 2012-12-03T07:16:23Z", () => { 52 | expect(f.where).to.equal("[DateTimeOffsetValue] = @p0") 53 | }) 54 | 55 | it("expression 5.1.1.6.1: GuidValue eq 01234567-89ab-cdef-0123-456789abcdef", () => { 56 | expect(f.where).to.equal("[GuidValue] = @p0") 57 | }) 58 | 59 | it("expression 5.1.1.6.1: Int64Value eq 0", () => { 60 | expect(f.where).to.equal("[Int64Value] = @p0") 61 | }) 62 | 63 | it("expression 5.1.1.6.1: A eq 0.31415926535897931e1", () => { 64 | expect(f.where).to.equal("[A] = @p0") 65 | }) 66 | 67 | it("expression 5.1.1.1.2: A ne 1", () => { 68 | expect(f.where).to.equal("[A] <> @p0") 69 | }) 70 | 71 | it("expression 5.1.1.1.3: A gt 2", () => { 72 | expect(f.where).to.equal("[A] > @p0") 73 | }) 74 | 75 | it("expression 5.1.1.1.4: A ge 3", () => { 76 | expect(f.where).to.equal("[A] >= @p0") 77 | }) 78 | 79 | it("expression 5.1.1.1.5: A lt 2", () => { 80 | expect(f.where).to.equal("[A] < @p0") 81 | }) 82 | 83 | it("expression 5.1.1.1.6: A le 2", () => { 84 | expect(f.where).to.equal("[A] <= @p0") 85 | }) 86 | 87 | it("expression 5.1.1.3: (A eq 2) or (B lt 4) and ((E gt 5) or (E lt -1))", () => { 88 | expect(f.where).to.equal("([A] = @p0) OR ([B] < @p1) AND (([E] > @p2) OR ([E] < @p3))") 89 | }) 90 | 91 | it("expression 5.1.1.4.1: contains(A, 'BC')", () => { 92 | expect(f.where).to.equal("[A] like @p0"); 93 | }) 94 | 95 | it("expression 5.1.1.4.2: endswith(A, 'CD')", () => { 96 | expect(f.where).to.equal("[A] like @p0"); 97 | }) 98 | 99 | it("expression 5.1.1.4.3: startswith(A, 'CD')", () => { 100 | expect(f.where).to.equal("[A] like @p0"); 101 | }) 102 | 103 | it("expression 5.1.1.4.4: length(A) eq 3", () => { 104 | expect(f.where).to.equal("LEN([A]) = @p0") 105 | }) 106 | 107 | it("expression 5.1.1.4.5: indexof(A, 'BC') eq 1", () => { 108 | expect(f.where).to.equal("CHARINDEX(@p0, [A]) - 1 = @p1") 109 | }) 110 | 111 | it("expression 5.1.1.4.7: tolower(A) eq 'abc'", () => { 112 | expect(f.where).to.equal("LCASE([A]) = @p0") 113 | }) 114 | 115 | it("expression 5.1.1.4.8: toupper(A) eq 'ABC'", () => { 116 | expect(f.where).to.equal("UCASE([A]) = @p0") 117 | }) 118 | 119 | it("expression 5.1.1.4.9: trim(A) eq 'abc'", () => { 120 | expect(f.where).to.equal("TRIM(' ' FROM [A]) = @p0") 121 | }) 122 | 123 | it("expression 5.1.1.4.11: A eq year(2016-01-01T13:00Z)", () => { 124 | expect(f.where).to.equal("[A] = YEAR(@p0)") 125 | }) 126 | 127 | it("expression 5.1.1.4.21: year(now())", () => { 128 | expect(f.where).to.equal("YEAR(NOW())") 129 | }) 130 | 131 | it("expression 5.1.1.4.25: round(A) eq 42", () => { 132 | expect(f.where).to.equal("ROUND([A]) = @p0") 133 | }) 134 | 135 | it("expression 5.1.1.4.26: floor(A) eq 42", () => { 136 | expect(f.where).to.equal("FLOOR([A]) = @p0") 137 | }) 138 | 139 | it("expression 5.1.1.4.27: ceiling(A) eq 42", () => { 140 | expect(f.where).to.equal("CEILING([A]) = @p0") 141 | }) 142 | }) 143 | -------------------------------------------------------------------------------- /test/oracle.spec.js: -------------------------------------------------------------------------------- 1 | var createFilter = require('../lib').createFilter 2 | var expect = require('chai').expect 3 | 4 | describe("SQL WHERE useParameters (ORACLE-SQL)", () => { 5 | var f; 6 | beforeEach(function() { 7 | var match; 8 | if (match = this.currentTest.title.match(/expression[^\:]*\: ?(.*)/)) { 9 | f = createFilter(match[1], { 10 | useParameters: true, 11 | type: 4 12 | }); 13 | } 14 | }); 15 | 16 | //all numbers are referencing this: 17 | //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 18 | 19 | it("expression 5.1.1.6.1: NullValue eq null", () => { 20 | expect(f.where).to.equal("[NullValue] IS NULL"); 21 | }) 22 | 23 | it("expression 5.1.1.6.1: TrueValue eq true", () => { 24 | expect(f.where).to.equal("[TrueValue] = :p0"); 25 | }) 26 | 27 | it("expression 5.1.1.6.1: FalseValue eq false", () => { 28 | expect(f.where).to.equal("[FalseValue] = :p0"); 29 | }) 30 | 31 | it("expression 5.1.1.6.1: IntegerValue lt -128", () => { 32 | expect(f.where).to.equal("[IntegerValue] < :p0"); 33 | }) 34 | 35 | it("expression 5.1.1.6.1: DecimalValue eq 34.95", () => { 36 | expect(f.where).to.equal("[DecimalValue] = :p0"); 37 | }) 38 | 39 | it("expression 5.1.1.6.1: StringValue eq 'Say Hello,then go'", () => { 40 | expect(f.where).to.equal("[StringValue] = :p0"); 41 | }) 42 | 43 | it("expression 5.1.1.6.1: DurationValue eq duration'P12DT23H59M59.999999999999S'", () => { 44 | expect(f.where).to.equal("[DurationValue] = :p0"); 45 | }) 46 | 47 | it("expression 5.1.1.6.1: DateValue eq 2012-12-03", () => { 48 | expect(f.where).to.equal("[DateValue] = :p0"); 49 | }) 50 | 51 | it("expression 5.1.1.6.1: DateTimeOffsetValue eq 2012-12-03T07:16:23Z", () => { 52 | expect(f.where).to.equal("[DateTimeOffsetValue] = :p0"); 53 | }) 54 | 55 | it("expression 5.1.1.6.1: GuidValue eq 01234567-89ab-cdef-0123-456789abcdef", () => { 56 | expect(f.where).to.equal("[GuidValue] = :p0"); 57 | }) 58 | 59 | it("expression 5.1.1.6.1: Int64Value eq 0", () => { 60 | expect(f.where).to.equal("[Int64Value] = :p0"); 61 | }) 62 | 63 | it("expression 5.1.1.6.1: A eq 0.31415926535897931e1", () => { 64 | expect(f.where).to.equal("[A] = :p0"); 65 | }) 66 | 67 | it("expression 5.1.1.1.2: A ne 1", () => { 68 | expect(f.where).to.equal("[A] <> :p0"); 69 | }) 70 | 71 | it("expression 5.1.1.1.3: A gt 2", () => { 72 | expect(f.where).to.equal("[A] > :p0"); 73 | }) 74 | 75 | it("expression 5.1.1.1.4: A ge 3", () => { 76 | expect(f.where).to.equal("[A] >= :p0"); 77 | }) 78 | 79 | it("expression 5.1.1.1.5: A lt 2", () => { 80 | expect(f.where).to.equal("[A] < :p0"); 81 | }) 82 | 83 | it("expression 5.1.1.1.6: A le 2", () => { 84 | expect(f.where).to.equal("[A] <= :p0"); 85 | }) 86 | 87 | it("expression 5.1.1.3: (A eq 2) or (B lt 4) and ((E gt 5) or (E lt -1))", () => { 88 | expect(f.where).to.equal("([A] = :p0) OR ([B] < :p1) AND (([E] > :p2) OR ([E] < :p3))"); 89 | }) 90 | 91 | it("expression 5.1.1.4.1: contains(A, 'BC')", () => { 92 | expect(f.where).to.equal("[A] like :p0"); 93 | }) 94 | 95 | it("expression 5.1.1.4.2: endswith(A, 'CD')", () => { 96 | expect(f.where).to.equal("[A] like :p0"); 97 | }) 98 | 99 | it("expression 5.1.1.4.3: startswith(A, 'CD')", () => { 100 | expect(f.where).to.equal("[A] like :p0"); 101 | }) 102 | 103 | it("expression 5.1.1.4.4: length(A) eq 3", () => { 104 | expect(f.where).to.equal("LEN([A]) = :p0"); 105 | }) 106 | 107 | it("expression 5.1.1.4.7: tolower(A) eq 'abc'", () => { 108 | expect(f.where).to.equal("LCASE([A]) = :p0"); 109 | }) 110 | 111 | it("expression 5.1.1.4.8: toupper(A) eq 'ABC'", () => { 112 | expect(f.where).to.equal("UCASE([A]) = :p0"); 113 | }) 114 | 115 | it("expression 5.1.1.4.9: trim(A) eq 'abc'", () => { 116 | expect(f.where).to.equal("TRIM(' ' FROM [A]) = :p0"); 117 | }) 118 | 119 | it("expression 5.1.1.4.11: A eq year(2016-01-01T13:00Z)", () => { 120 | expect(f.where).to.equal("[A] = YEAR(:p0)") 121 | }) 122 | 123 | it("expression 5.1.1.4.21: year(now())", () => { 124 | expect(f.where).to.equal("YEAR(NOW())") 125 | }) 126 | 127 | it("expression 5.1.1.4.25: round(A) eq 42", () => { 128 | expect(f.where).to.equal("ROUND([A]) = :p0") 129 | }) 130 | 131 | it("expression 5.1.1.4.26: floor(A) eq 42", () => { 132 | expect(f.where).to.equal("FLOOR([A]) = :p0") 133 | }) 134 | 135 | it("expression 5.1.1.4.27: ceiling(A) eq 42", () => { 136 | expect(f.where).to.equal("CEILING([A]) = :p0") 137 | }) 138 | }) 139 | -------------------------------------------------------------------------------- /test/query.spec.js: -------------------------------------------------------------------------------- 1 | var createQuery = require('../lib').createQuery 2 | var expect = require('chai').expect 3 | 4 | describe("SQL SELECT", () => { 5 | var f; 6 | beforeEach(function() { 7 | var match; 8 | if (match = this.currentTest.title.match(/expression[^\:]*\: ?(.*)/)) { 9 | f = createQuery(match[1]); 10 | } 11 | }); 12 | 13 | it("expression: $select=Id", () => { 14 | expect(f.select).to.equal("[Id]"); 15 | }); 16 | 17 | it("expression: $select=Id,Value", () => { 18 | expect(f.select).to.equal("[Id], [Value]"); 19 | }); 20 | }); 21 | 22 | describe("SQL ORDER BY", () => { 23 | var f; 24 | beforeEach(function() { 25 | var match; 26 | if (match = this.currentTest.title.match(/expression[^\:]*\: ?(.*)/)) { 27 | f = createQuery(match[1]); 28 | } 29 | }); 30 | 31 | it("expression: $orderby=Id", () => { 32 | expect(f.orderby).to.equal("[Id] ASC"); 33 | }); 34 | 35 | it("expression: $orderby=Id asc", () => { 36 | expect(f.orderby).to.equal("[Id] ASC"); 37 | }); 38 | 39 | it("expression: $orderby=Id desc", () => { 40 | expect(f.orderby).to.equal("[Id] DESC"); 41 | }); 42 | 43 | it("expression: $orderby=Id,Value desc", () => { 44 | expect(f.orderby).to.equal("[Id] ASC, [Value] DESC"); 45 | }); 46 | 47 | it("expression: $orderby=Id asc,Value desc", () => { 48 | expect(f.orderby).to.equal("[Id] ASC, [Value] DESC"); 49 | }); 50 | }); 51 | 52 | describe("OData $expand", () => { 53 | var f; 54 | beforeEach(function() { 55 | var match; 56 | if (match = this.currentTest.title.match(/expression[^\:]*\: ?(.*)/)) { 57 | f = createQuery(match[1], { 58 | useParameters: false 59 | }); 60 | } 61 | }); 62 | 63 | it("expression: $expand=Child", () => { 64 | expect(f.includes).to.be.an.array; 65 | expect(f.includes.length).to.equal(1); 66 | expect(f.includes[0].navigationProperty).to.equal("Child"); 67 | }); 68 | 69 | it("expression: $expand=Child($filter=Id eq 1)", () => { 70 | expect(f.includes).to.be.an.array; 71 | expect(f.includes.length).to.equal(1); 72 | expect(f.includes[0].navigationProperty).to.equal("Child"); 73 | expect(f.includes[0].where).to.equal("[Id] = 1"); 74 | }); 75 | }); 76 | 77 | describe("SQL statement", () => { 78 | var f; 79 | beforeEach(function() { 80 | var match; 81 | if (match = this.currentTest.title.match(/expression[^\:]*\: ?(.*)/)) { 82 | f = createQuery(match[1]); 83 | } 84 | }); 85 | 86 | it("expression: $top=2", () => { 87 | expect(f.from("dummy")).to.equal("SELECT * FROM [dummy] WHERE 1 = 1 ORDER BY 1 LIMIT 2"); 88 | }); 89 | 90 | it("expression: $skip=1&$top=2", () => { 91 | expect(f.from("dummy")).to.equal("SELECT * FROM [dummy] WHERE 1 = 1 ORDER BY 1 LIMIT 2 OFFSET 1"); 92 | }); 93 | }); -------------------------------------------------------------------------------- /test/where-parameters.spec.js: -------------------------------------------------------------------------------- 1 | var createFilter = require('../lib').createFilter 2 | var expect = require('chai').expect 3 | 4 | describe("SQL WHERE useParameters", () => { 5 | var f; 6 | beforeEach(function() { 7 | var match; 8 | if (match = this.currentTest.title.match(/expression[^\:]*\: ?(.*)/)) { 9 | f = createFilter(match[1], { 10 | useParameters: true 11 | }); 12 | } 13 | }); 14 | 15 | //all numbers are referencing this: 16 | //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 17 | 18 | it("expression 5.1.1.6.1: NullValue eq null", () => { 19 | expect(f.where).to.equal("[NullValue] IS NULL") 20 | }) 21 | 22 | it("expression 5.1.1.6.1: TrueValue eq true", () => { 23 | expect(f.where).to.equal("[TrueValue] = ?") 24 | }) 25 | 26 | it("expression 5.1.1.6.1: FalseValue eq false", () => { 27 | expect(f.where).to.equal("[FalseValue] = ?") 28 | }) 29 | 30 | it("expression 5.1.1.6.1: IntegerValue lt -128", () => { 31 | expect(f.where).to.equal("[IntegerValue] < ?") 32 | }) 33 | 34 | it("expression 5.1.1.6.1: DecimalValue eq 34.95", () => { 35 | expect(f.where).to.equal("[DecimalValue] = ?") 36 | }) 37 | 38 | it("expression 5.1.1.6.1: StringValue eq 'Say Hello,then go'", () => { 39 | expect(f.where).to.equal("[StringValue] = ?") 40 | }) 41 | 42 | it("expression 5.1.1.6.1: DurationValue eq duration'P12DT23H59M59.999999999999S'", () => { 43 | expect(f.where).to.equal("[DurationValue] = ?") 44 | }) 45 | 46 | it("expression 5.1.1.6.1: DateValue eq 2012-12-03", () => { 47 | expect(f.where).to.equal("[DateValue] = ?") 48 | }) 49 | 50 | it("expression 5.1.1.6.1: DateTimeOffsetValue eq 2012-12-03T07:16:23Z", () => { 51 | expect(f.where).to.equal("[DateTimeOffsetValue] = ?") 52 | }) 53 | 54 | it("expression 5.1.1.6.1: GuidValue eq 01234567-89ab-cdef-0123-456789abcdef", () => { 55 | expect(f.where).to.equal("[GuidValue] = ?") 56 | }) 57 | 58 | it("expression 5.1.1.6.1: Int64Value eq 0", () => { 59 | expect(f.where).to.equal("[Int64Value] = ?") 60 | }) 61 | 62 | it("expression 5.1.1.6.1: A eq 0.31415926535897931e1", () => { 63 | expect(f.where).to.equal("[A] = ?") 64 | }) 65 | 66 | it("expression 5.1.1.1.2: A ne 1", () => { 67 | expect(f.where).to.equal("[A] <> ?") 68 | }) 69 | 70 | it("expression 5.1.1.1.3: A gt 2", () => { 71 | expect(f.where).to.equal("[A] > ?") 72 | }) 73 | 74 | it("expression 5.1.1.1.4: A ge 3", () => { 75 | expect(f.where).to.equal("[A] >= ?") 76 | }) 77 | 78 | it("expression 5.1.1.1.5: A lt 2", () => { 79 | expect(f.where).to.equal("[A] < ?") 80 | }) 81 | 82 | it("expression 5.1.1.1.6: A le 2", () => { 83 | expect(f.where).to.equal("[A] <= ?") 84 | }) 85 | 86 | it("expression 5.1.1.3: (A eq 2) or (B lt 4) and ((E gt 5) or (E lt -1))", () => { 87 | expect(f.where).to.equal("([A] = ?) OR ([B] < ?) AND (([E] > ?) OR ([E] < ?))") 88 | }) 89 | 90 | it("expression 5.1.1.4.1: contains(A, 'BC')", () => { 91 | expect(f.where).to.equal("[A] like ?"); 92 | }) 93 | 94 | it("expression 5.1.1.4.2: endswith(A, 'CD')", () => { 95 | expect(f.where).to.equal("[A] like ?"); 96 | }) 97 | 98 | it("expression 5.1.1.4.3: startswith(A, 'CD')", () => { 99 | expect(f.where).to.equal("[A] like ?"); 100 | }) 101 | 102 | it("expression 5.1.1.4.4: length(A) eq 3", () => { 103 | expect(f.where).to.equal("LEN([A]) = ?") 104 | }) 105 | 106 | it("expression 5.1.1.4.5: indexof(A, 'BC') eq 1", () => { 107 | expect(f.where).to.equal("INSTR([A], ?) - 1 = ?") 108 | }) 109 | 110 | it("expression 5.1.1.4.7: tolower(A) eq 'abc'", () => { 111 | expect(f.where).to.equal("LCASE([A]) = ?") 112 | }) 113 | 114 | it("expression 5.1.1.4.8: toupper(A) eq 'ABC'", () => { 115 | expect(f.where).to.equal("UCASE([A]) = ?") 116 | }) 117 | 118 | it("expression 5.1.1.4.9: trim(A) eq 'abc'", () => { 119 | expect(f.where).to.equal("TRIM(' ' FROM [A]) = ?") 120 | }) 121 | 122 | it("expression 5.1.1.4.11: A eq year(2016-01-01T13:00Z)", () => { 123 | expect(f.where).to.equal("[A] = YEAR(?)") 124 | }) 125 | 126 | it("expression 5.1.1.4.21: year(now())", () => { 127 | expect(f.where).to.equal("YEAR(NOW())") 128 | }) 129 | 130 | it("expression 5.1.1.4.25: round(A) eq 42", () => { 131 | expect(f.where).to.equal("ROUND([A]) = ?") 132 | }) 133 | 134 | it("expression 5.1.1.4.26: floor(A) eq 42", () => { 135 | expect(f.where).to.equal("FLOOR([A]) = ?") 136 | }) 137 | 138 | it("expression 5.1.1.4.27: ceiling(A) eq 42", () => { 139 | expect(f.where).to.equal("CEILING([A]) = ?") 140 | }) 141 | }) 142 | -------------------------------------------------------------------------------- /test/where.spec.js: -------------------------------------------------------------------------------- 1 | var createFilter = require('../lib').createFilter 2 | var expect = require('chai').expect 3 | 4 | describe("SQL WHERE", () => { 5 | var f; 6 | beforeEach(function() { 7 | var match; 8 | if (match = this.currentTest.title.match(/expression[^\:]*\: ?(.*)/)) { 9 | f = createFilter(match[1], { 10 | useParameters: false 11 | }).where; 12 | } 13 | }); 14 | 15 | //all numbers are referencing this: 16 | //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 17 | 18 | it("expression 5.1.1.6.1: 1 eq 1", () => { 19 | expect(f).to.equal("1 = 1") 20 | }) 21 | 22 | it("expression 5.1.1.6.1: NullValue eq null", () => { 23 | expect(f).to.equal("[NullValue] IS NULL") 24 | }) 25 | 26 | it("expression 5.1.1.6.1: null eq NullValue", () => { 27 | expect(f).to.equal("[NullValue] IS NULL") 28 | }) 29 | 30 | it("expression 5.1.1.6.1: TrueValue eq true", () => { 31 | expect(f).to.equal("[TrueValue] = 1") 32 | }) 33 | 34 | it("expression 5.1.1.6.1: FalseValue eq false", () => { 35 | expect(f).to.equal("[FalseValue] = 0") 36 | }) 37 | 38 | it("expression 5.1.1.6.1: IntegerValue lt -128", () => { 39 | expect(f).to.equal("[IntegerValue] < -128") 40 | }) 41 | 42 | it("expression 5.1.1.6.1: DecimalValue eq 34.95", () => { 43 | expect(f).to.equal("[DecimalValue] = 34.95") 44 | }) 45 | 46 | it("expression 5.1.1.6.1: StringValue eq 'Say Hello,then go'", () => { 47 | expect(f).to.equal("[StringValue] = 'Say Hello,then go'") 48 | }) 49 | 50 | xit("expression 5.1.1.6.1: DurationValue eq duration'P12DT23H59M59.999999999999S'", () => { 51 | expect(f).to.equal("[DurationValue] = 1033199000") 52 | }) 53 | 54 | it("expression 5.1.1.6.1: DateValue eq 2012-12-03", () => { 55 | expect(f).to.equal("[DateValue] = '2012-12-03'") 56 | }) 57 | 58 | it("expression 5.1.1.6.1: DateTimeOffsetValue eq 2012-12-03T07:16:23Z", () => { 59 | expect(f).to.equal("[DateTimeOffsetValue] = '2012-12-03 07:16:23'") 60 | }) 61 | 62 | it("expression 5.1.1.6.1: GuidValue eq 01234567-89ab-cdef-0123-456789abcdef", () => { 63 | expect(f).to.equal("[GuidValue] = '01234567-89ab-cdef-0123-456789abcdef'") 64 | }) 65 | 66 | it("expression 5.1.1.6.1: Int64Value eq 0", () => { 67 | expect(f).to.equal("[Int64Value] = 0") 68 | }) 69 | 70 | it("expression 5.1.1.6.1: A eq 0.31415926535897931e1", () => { 71 | expect(f).to.equal("[A] = 3.141592653589793") 72 | }) 73 | 74 | it("expression 5.1.1.1.2: A ne 1", () => { 75 | expect(f).to.equal("[A] <> 1") 76 | }) 77 | 78 | it("expression 5.1.1.1.3: A gt 2", () => { 79 | expect(f).to.equal("[A] > 2") 80 | }) 81 | 82 | it("expression 5.1.1.1.4: A ge 3", () => { 83 | expect(f).to.equal("[A] >= 3") 84 | }) 85 | 86 | it("expression 5.1.1.1.5: A lt 2", () => { 87 | expect(f).to.equal("[A] < 2") 88 | }) 89 | 90 | it("expression 5.1.1.1.6: A le 2", () => { 91 | expect(f).to.equal("[A] <= 2") 92 | }) 93 | 94 | it("expression 5.1.1.3: (A eq 2) or (B lt 4) and ((E gt 5) or (E lt -1))", () => { 95 | expect(f).to.equal("([A] = 2) OR ([B] < 4) AND (([E] > 5) OR ([E] < -1))") 96 | }) 97 | 98 | it("expression 5.1.1.4.1: contains(A, 'BC')", () => { 99 | expect(f).to.equal("[A] like '%BC%'"); 100 | }) 101 | 102 | it("expression 5.1.1.4.2: endswith(A, 'CD')", () => { 103 | expect(f).to.equal("[A] like '%CD'"); 104 | }) 105 | 106 | it("expression 5.1.1.4.3: startswith(A, 'CD')", () => { 107 | expect(f).to.equal("[A] like 'CD%'"); 108 | }) 109 | 110 | it("expression 5.1.1.4.4: length(A) eq 3", () => { 111 | expect(f).to.equal("LEN([A]) = 3") 112 | }) 113 | 114 | it("expression 5.1.1.4.5: indexof(A, 'BC') eq 1", () => { 115 | expect(f).to.equal("INSTR([A], 'BC') - 1 = 1") 116 | }) 117 | 118 | it("expression 5.1.1.4.7: tolower(A) eq 'abc'", () => { 119 | expect(f).to.equal("LCASE([A]) = 'abc'") 120 | }) 121 | 122 | it("expression 5.1.1.4.8: toupper(A) eq 'ABC'", () => { 123 | expect(f).to.equal("UCASE([A]) = 'ABC'") 124 | }) 125 | 126 | it("expression 5.1.1.4.9: trim(A) eq 'abc'", () => { 127 | expect(f).to.equal("TRIM(' ' FROM [A]) = 'abc'") 128 | }) 129 | 130 | it("expression 5.1.1.4.11: A eq year(2016-01-01T13:00Z)", () => { 131 | expect(f).to.equal("[A] = YEAR('2016-01-01 13:00')") 132 | }) 133 | 134 | it("expression 5.1.1.4.21: year(now())", () => { 135 | expect(f).to.equal("YEAR(NOW())") 136 | }) 137 | 138 | it("expression 5.1.1.4.25: round(A) eq 42", () => { 139 | expect(f).to.equal("ROUND([A]) = 42") 140 | }) 141 | 142 | it("expression 5.1.1.4.26: floor(A) eq 42", () => { 143 | expect(f).to.equal("FLOOR([A]) = 42") 144 | }) 145 | 146 | it("expression 5.1.1.4.27: ceiling(A) eq 42", () => { 147 | expect(f).to.equal("CEILING([A]) = 42") 148 | }) 149 | }) 150 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "sourceMap": true, 4 | "target": "es6", 5 | "module": "commonjs", 6 | "declaration": true, 7 | "experimentalDecorators": true, 8 | "outDir": "lib" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | "@types/body-parser@0.0.33": 4 | version "0.0.33" 5 | resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-0.0.33.tgz#33ca1498fc37e51c5df0c81cae34569e7041e025" 6 | dependencies: 7 | "@types/express" "*" 8 | 9 | "@types/config@^0.0.30": 10 | version "0.0.30" 11 | resolved "https://registry.yarnpkg.com/@types/config/-/config-0.0.30.tgz#784879a1f2f674fa52b80fc43957e4bffc24aadf" 12 | 13 | "@types/cors@0.0.33": 14 | version "0.0.33" 15 | resolved "https://registry.yarnpkg.com/@types/cors/-/cors-0.0.33.tgz#05dc50760b93201b81c54a94fb757f21144f5e61" 16 | dependencies: 17 | "@types/express" "*" 18 | 19 | "@types/express-serve-static-core@*": 20 | version "4.0.39" 21 | resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.0.39.tgz#45157f96480d46f254648f45b2c6d70bd9fc9f54" 22 | dependencies: 23 | "@types/node" "*" 24 | 25 | "@types/express@*", "@types/express@^4.0.33": 26 | version "4.0.33" 27 | resolved "https://registry.yarnpkg.com/@types/express/-/express-4.0.33.tgz#9212b6c67e02e09ee9f80740ded04306050739ab" 28 | dependencies: 29 | "@types/express-serve-static-core" "*" 30 | "@types/serve-static" "*" 31 | 32 | "@types/extend@^2.0.30": 33 | version "2.0.30" 34 | resolved "https://registry.yarnpkg.com/@types/extend/-/extend-2.0.30.tgz#58cc93f621d0a358d3fec9bd6e739bc60ec7e275" 35 | 36 | "@types/mime@*": 37 | version "0.0.29" 38 | resolved "https://registry.yarnpkg.com/@types/mime/-/mime-0.0.29.tgz#fbcfd330573b912ef59eeee14602bface630754b" 39 | 40 | "@types/mssql@^3.1.29": 41 | version "3.1.29" 42 | resolved "https://registry.yarnpkg.com/@types/mssql/-/mssql-3.1.29.tgz#78a815c15e3e85e8614a9ebd4b5d030d06e88a93" 43 | dependencies: 44 | "@types/node" "*" 45 | 46 | "@types/node@*": 47 | version "6.0.46" 48 | resolved "https://registry.yarnpkg.com/@types/node/-/node-6.0.46.tgz#8d9e48572831f05b11cc4c793754d43437219d62" 49 | 50 | "@types/qs@^6.2.30": 51 | version "6.2.30" 52 | resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.2.30.tgz#139e247511ee3b9be539cdd553c8fdb33c1f624e" 53 | 54 | "@types/serve-static@*": 55 | version "1.7.31" 56 | resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.7.31.tgz#15456de8d98d6b4cff31be6c6af7492ae63f521a" 57 | dependencies: 58 | "@types/express-serve-static-core" "*" 59 | "@types/mime" "*" 60 | 61 | accepts@~1.3.3: 62 | version "1.3.3" 63 | resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.3.tgz#c3ca7434938648c3e0d9c1e328dd68b622c284ca" 64 | dependencies: 65 | mime-types "~2.1.11" 66 | negotiator "0.6.1" 67 | 68 | array-flatten@1.1.1: 69 | version "1.1.1" 70 | resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" 71 | 72 | asap@~2.0.3: 73 | version "2.0.5" 74 | resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.5.tgz#522765b50c3510490e52d7dcfe085ef9ba96958f" 75 | 76 | assertion-error@^1.0.1: 77 | version "1.0.2" 78 | resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.0.2.tgz#13ca515d86206da0bac66e834dd397d87581094c" 79 | 80 | babel-runtime@^5.8.19: 81 | version "5.8.38" 82 | resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-5.8.38.tgz#1c0b02eb63312f5f087ff20450827b425c9d4c19" 83 | dependencies: 84 | core-js "^1.0.0" 85 | 86 | balanced-match@^0.4.1: 87 | version "0.4.2" 88 | resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.4.2.tgz#cb3f3e3c732dc0f01ee70b403f302e61d7709838" 89 | 90 | big-number@0.3.1: 91 | version "0.3.1" 92 | resolved "https://registry.yarnpkg.com/big-number/-/big-number-0.3.1.tgz#ac73020c0a59bb79eb17c2ce2db77f77d974e013" 93 | 94 | bl@^1.0.0: 95 | version "1.1.2" 96 | resolved "https://registry.yarnpkg.com/bl/-/bl-1.1.2.tgz#fdca871a99713aa00d19e3bbba41c44787a65398" 97 | dependencies: 98 | readable-stream "~2.0.5" 99 | 100 | body-parser@^1.15.2: 101 | version "1.15.2" 102 | resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.15.2.tgz#d7578cf4f1d11d5f6ea804cef35dc7a7ff6dae67" 103 | dependencies: 104 | bytes "2.4.0" 105 | content-type "~1.0.2" 106 | debug "~2.2.0" 107 | depd "~1.1.0" 108 | http-errors "~1.5.0" 109 | iconv-lite "0.4.13" 110 | on-finished "~2.3.0" 111 | qs "6.2.0" 112 | raw-body "~2.1.7" 113 | type-is "~1.6.13" 114 | 115 | brace-expansion@^1.0.0: 116 | version "1.1.6" 117 | resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.6.tgz#7197d7eaa9b87e648390ea61fc66c84427420df9" 118 | dependencies: 119 | balanced-match "^0.4.1" 120 | concat-map "0.0.1" 121 | 122 | browser-stdout@1.3.0: 123 | version "1.3.0" 124 | resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.0.tgz#f351d32969d32fa5d7a5567154263d928ae3bd1f" 125 | 126 | buffer-shims@^1.0.0: 127 | version "1.0.0" 128 | resolved "https://registry.yarnpkg.com/buffer-shims/-/buffer-shims-1.0.0.tgz#9978ce317388c649ad8793028c3477ef044a8b51" 129 | 130 | bytes@2.4.0: 131 | version "2.4.0" 132 | resolved "https://registry.yarnpkg.com/bytes/-/bytes-2.4.0.tgz#7d97196f9d5baf7f6935e25985549edd2a6c2339" 133 | 134 | chai@^3.5.0: 135 | version "3.5.0" 136 | resolved "https://registry.yarnpkg.com/chai/-/chai-3.5.0.tgz#4d02637b067fe958bdbfdd3a40ec56fef7373247" 137 | dependencies: 138 | assertion-error "^1.0.1" 139 | deep-eql "^0.1.3" 140 | type-detect "^1.0.0" 141 | 142 | commander@2.9.0: 143 | version "2.9.0" 144 | resolved "https://registry.yarnpkg.com/commander/-/commander-2.9.0.tgz#9c99094176e12240cb22d6c5146098400fe0f7d4" 145 | dependencies: 146 | graceful-readlink ">= 1.0.0" 147 | 148 | concat-map@0.0.1: 149 | version "0.0.1" 150 | resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" 151 | 152 | config@^1.21.0: 153 | version "1.24.0" 154 | resolved "https://registry.yarnpkg.com/config/-/config-1.24.0.tgz#553bc4d77637d6630a305b52c33cb6da2b9f42f0" 155 | dependencies: 156 | json5 "0.4.0" 157 | 158 | content-disposition@0.5.1: 159 | version "0.5.1" 160 | resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.1.tgz#87476c6a67c8daa87e32e87616df883ba7fb071b" 161 | 162 | content-type@~1.0.2: 163 | version "1.0.2" 164 | resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.2.tgz#b7d113aee7a8dd27bd21133c4dc2529df1721eed" 165 | 166 | cookie-signature@1.0.6: 167 | version "1.0.6" 168 | resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" 169 | 170 | cookie@0.3.1: 171 | version "0.3.1" 172 | resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb" 173 | 174 | core-js@^1.0.0: 175 | version "1.2.7" 176 | resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636" 177 | 178 | core-util-is@~1.0.0: 179 | version "1.0.2" 180 | resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" 181 | 182 | cors@^2.8.0: 183 | version "2.8.1" 184 | resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.1.tgz#6181aa56abb45a2825be3304703747ae4e9d2383" 185 | dependencies: 186 | vary "^1" 187 | 188 | debug@~2.2.0, debug@2.2.0: 189 | version "2.2.0" 190 | resolved "https://registry.yarnpkg.com/debug/-/debug-2.2.0.tgz#f87057e995b1a1f6ae6a4960664137bc56f039da" 191 | dependencies: 192 | ms "0.7.1" 193 | 194 | deep-eql@^0.1.3: 195 | version "0.1.3" 196 | resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-0.1.3.tgz#ef558acab8de25206cd713906d74e56930eb69f2" 197 | dependencies: 198 | type-detect "0.1.1" 199 | 200 | depd@~1.1.0: 201 | version "1.1.0" 202 | resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.0.tgz#e1bd82c6aab6ced965b97b88b17ed3e528ca18c3" 203 | 204 | destroy@~1.0.4: 205 | version "1.0.4" 206 | resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" 207 | 208 | diff@1.4.0: 209 | version "1.4.0" 210 | resolved "https://registry.yarnpkg.com/diff/-/diff-1.4.0.tgz#7f28d2eb9ee7b15a97efd89ce63dcfdaa3ccbabf" 211 | 212 | ee-first@1.1.1: 213 | version "1.1.1" 214 | resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" 215 | 216 | encodeurl@~1.0.1: 217 | version "1.0.1" 218 | resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.1.tgz#79e3d58655346909fe6f0f45a5de68103b294d20" 219 | 220 | escape-html@~1.0.3: 221 | version "1.0.3" 222 | resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" 223 | 224 | escape-string-regexp@1.0.5: 225 | version "1.0.5" 226 | resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" 227 | 228 | etag@~1.7.0: 229 | version "1.7.0" 230 | resolved "https://registry.yarnpkg.com/etag/-/etag-1.7.0.tgz#03d30b5f67dd6e632d2945d30d6652731a34d5d8" 231 | 232 | express@^4.14.0: 233 | version "4.14.0" 234 | resolved "https://registry.yarnpkg.com/express/-/express-4.14.0.tgz#c1ee3f42cdc891fb3dc650a8922d51ec847d0d66" 235 | dependencies: 236 | accepts "~1.3.3" 237 | array-flatten "1.1.1" 238 | content-disposition "0.5.1" 239 | content-type "~1.0.2" 240 | cookie "0.3.1" 241 | cookie-signature "1.0.6" 242 | debug "~2.2.0" 243 | depd "~1.1.0" 244 | encodeurl "~1.0.1" 245 | escape-html "~1.0.3" 246 | etag "~1.7.0" 247 | finalhandler "0.5.0" 248 | fresh "0.3.0" 249 | merge-descriptors "1.0.1" 250 | methods "~1.1.2" 251 | on-finished "~2.3.0" 252 | parseurl "~1.3.1" 253 | path-to-regexp "0.1.7" 254 | proxy-addr "~1.1.2" 255 | qs "6.2.0" 256 | range-parser "~1.2.0" 257 | send "0.14.1" 258 | serve-static "~1.11.1" 259 | type-is "~1.6.13" 260 | utils-merge "1.0.0" 261 | vary "~1.1.0" 262 | 263 | extend@^3.0.0: 264 | version "3.0.0" 265 | resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.0.tgz#5a474353b9f3353ddd8176dfd37b91c83a46f1d4" 266 | 267 | finalhandler@0.5.0: 268 | version "0.5.0" 269 | resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-0.5.0.tgz#e9508abece9b6dba871a6942a1d7911b91911ac7" 270 | dependencies: 271 | debug "~2.2.0" 272 | escape-html "~1.0.3" 273 | on-finished "~2.3.0" 274 | statuses "~1.3.0" 275 | unpipe "~1.0.0" 276 | 277 | forwarded@~0.1.0: 278 | version "0.1.0" 279 | resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.0.tgz#19ef9874c4ae1c297bcf078fde63a09b66a84363" 280 | 281 | fresh@0.3.0: 282 | version "0.3.0" 283 | resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.3.0.tgz#651f838e22424e7566de161d8358caa199f83d4f" 284 | 285 | fs.realpath@^1.0.0: 286 | version "1.0.0" 287 | resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" 288 | 289 | generic-pool@^2.2.0: 290 | version "2.4.4" 291 | resolved "https://registry.yarnpkg.com/generic-pool/-/generic-pool-2.4.4.tgz#49d90c5e8d138adef0a12780a86e4acba48892d2" 292 | 293 | glob@7.0.5: 294 | version "7.0.5" 295 | resolved "https://registry.yarnpkg.com/glob/-/glob-7.0.5.tgz#b4202a69099bbb4d292a7c1b95b6682b67ebdc95" 296 | dependencies: 297 | fs.realpath "^1.0.0" 298 | inflight "^1.0.4" 299 | inherits "2" 300 | minimatch "^3.0.2" 301 | once "^1.3.0" 302 | path-is-absolute "^1.0.0" 303 | 304 | "graceful-readlink@>= 1.0.0": 305 | version "1.0.1" 306 | resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725" 307 | 308 | growl@1.9.2: 309 | version "1.9.2" 310 | resolved "https://registry.yarnpkg.com/growl/-/growl-1.9.2.tgz#0ea7743715db8d8de2c5ede1775e1b45ac85c02f" 311 | 312 | has-flag@^1.0.0: 313 | version "1.0.0" 314 | resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa" 315 | 316 | http-errors@~1.5.0: 317 | version "1.5.0" 318 | resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.5.0.tgz#b1cb3d8260fd8e2386cad3189045943372d48211" 319 | dependencies: 320 | inherits "2.0.1" 321 | setprototypeof "1.0.1" 322 | statuses ">= 1.3.0 < 2" 323 | 324 | iconv-lite@^0.4.11, iconv-lite@0.4.13: 325 | version "0.4.13" 326 | resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.13.tgz#1f88aba4ab0b1508e8312acc39345f36e992e2f2" 327 | 328 | inflight@^1.0.4: 329 | version "1.0.6" 330 | resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" 331 | dependencies: 332 | once "^1.3.0" 333 | wrappy "1" 334 | 335 | inherits@~2.0.1, inherits@2: 336 | version "2.0.3" 337 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" 338 | 339 | inherits@2.0.1: 340 | version "2.0.1" 341 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1" 342 | 343 | ipaddr.js@1.1.1: 344 | version "1.1.1" 345 | resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.1.1.tgz#c791d95f52b29c1247d5df80ada39b8a73647230" 346 | 347 | isarray@~1.0.0: 348 | version "1.0.0" 349 | resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" 350 | 351 | json3@3.3.2: 352 | version "3.3.2" 353 | resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.2.tgz#3c0434743df93e2f5c42aee7b19bcb483575f4e1" 354 | 355 | json5@0.4.0: 356 | version "0.4.0" 357 | resolved "https://registry.yarnpkg.com/json5/-/json5-0.4.0.tgz#054352e4c4c80c86c0923877d449de176a732c8d" 358 | 359 | lodash._baseassign@^3.0.0: 360 | version "3.2.0" 361 | resolved "https://registry.yarnpkg.com/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz#8c38a099500f215ad09e59f1722fd0c52bfe0a4e" 362 | dependencies: 363 | lodash._basecopy "^3.0.0" 364 | lodash.keys "^3.0.0" 365 | 366 | lodash._basecopy@^3.0.0: 367 | version "3.0.1" 368 | resolved "https://registry.yarnpkg.com/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz#8da0e6a876cf344c0ad8a54882111dd3c5c7ca36" 369 | 370 | lodash._basecreate@^3.0.0: 371 | version "3.0.3" 372 | resolved "https://registry.yarnpkg.com/lodash._basecreate/-/lodash._basecreate-3.0.3.tgz#1bc661614daa7fc311b7d03bf16806a0213cf821" 373 | 374 | lodash._getnative@^3.0.0: 375 | version "3.9.1" 376 | resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5" 377 | 378 | lodash._isiterateecall@^3.0.0: 379 | version "3.0.9" 380 | resolved "https://registry.yarnpkg.com/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz#5203ad7ba425fae842460e696db9cf3e6aac057c" 381 | 382 | lodash.create@3.1.1: 383 | version "3.1.1" 384 | resolved "https://registry.yarnpkg.com/lodash.create/-/lodash.create-3.1.1.tgz#d7f2849f0dbda7e04682bb8cd72ab022461debe7" 385 | dependencies: 386 | lodash._baseassign "^3.0.0" 387 | lodash._basecreate "^3.0.0" 388 | lodash._isiterateecall "^3.0.0" 389 | 390 | lodash.isarguments@^3.0.0: 391 | version "3.1.0" 392 | resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a" 393 | 394 | lodash.isarray@^3.0.0: 395 | version "3.0.4" 396 | resolved "https://registry.yarnpkg.com/lodash.isarray/-/lodash.isarray-3.0.4.tgz#79e4eb88c36a8122af86f844aa9bcd851b5fbb55" 397 | 398 | lodash.keys@^3.0.0: 399 | version "3.1.2" 400 | resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-3.1.2.tgz#4dbc0472b156be50a0b286855d1bd0b0c656098a" 401 | dependencies: 402 | lodash._getnative "^3.0.0" 403 | lodash.isarguments "^3.0.0" 404 | lodash.isarray "^3.0.0" 405 | 406 | media-typer@0.3.0: 407 | version "0.3.0" 408 | resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" 409 | 410 | merge-descriptors@1.0.1: 411 | version "1.0.1" 412 | resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" 413 | 414 | methods@~1.1.2: 415 | version "1.1.2" 416 | resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" 417 | 418 | mime-db@~1.24.0: 419 | version "1.24.0" 420 | resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.24.0.tgz#e2d13f939f0016c6e4e9ad25a8652f126c467f0c" 421 | 422 | mime-types@~2.1.11: 423 | version "2.1.12" 424 | resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.12.tgz#152ba256777020dd4663f54c2e7bc26381e71729" 425 | dependencies: 426 | mime-db "~1.24.0" 427 | 428 | mime@1.3.4: 429 | version "1.3.4" 430 | resolved "https://registry.yarnpkg.com/mime/-/mime-1.3.4.tgz#115f9e3b6b3daf2959983cb38f149a2d40eb5d53" 431 | 432 | minimatch@^3.0.2: 433 | version "3.0.3" 434 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.3.tgz#2a4e4090b96b2db06a9d7df01055a62a77c9b774" 435 | dependencies: 436 | brace-expansion "^1.0.0" 437 | 438 | minimist@0.0.8: 439 | version "0.0.8" 440 | resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" 441 | 442 | mkdirp@0.5.1: 443 | version "0.5.1" 444 | resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" 445 | dependencies: 446 | minimist "0.0.8" 447 | 448 | mocha@^3.1.2: 449 | version "3.1.2" 450 | resolved "https://registry.yarnpkg.com/mocha/-/mocha-3.1.2.tgz#51f93b432bf7e1b175ffc22883ccd0be32dba6b5" 451 | dependencies: 452 | browser-stdout "1.3.0" 453 | commander "2.9.0" 454 | debug "2.2.0" 455 | diff "1.4.0" 456 | escape-string-regexp "1.0.5" 457 | glob "7.0.5" 458 | growl "1.9.2" 459 | json3 "3.3.2" 460 | lodash.create "3.1.1" 461 | mkdirp "0.5.1" 462 | supports-color "3.1.2" 463 | 464 | ms@0.7.1: 465 | version "0.7.1" 466 | resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098" 467 | 468 | mssql@^3.3.0: 469 | version "3.3.0" 470 | resolved "https://registry.yarnpkg.com/mssql/-/mssql-3.3.0.tgz#b6e6337ff123e87bf8aee1e6c8344b53ca5da856" 471 | dependencies: 472 | generic-pool "^2.2.0" 473 | promise "^7.0.1" 474 | tedious "~1.14.0" 475 | 476 | negotiator@0.6.1: 477 | version "0.6.1" 478 | resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9" 479 | 480 | odata-v4-literal@^0.1.0: 481 | version "0.1.0" 482 | resolved "https://registry.yarnpkg.com/odata-v4-literal/-/odata-v4-literal-0.1.0.tgz#b52c44e112a0c918d4af6cbb5ad51f6d484305e7" 483 | 484 | odata-v4-metadata@^0.1.2, odata-v4-metadata@^0.1.3: 485 | version "0.1.3" 486 | resolved "https://registry.yarnpkg.com/odata-v4-metadata/-/odata-v4-metadata-0.1.3.tgz#22fb5d080289a195fa2aa36393c94cd8d3105711" 487 | 488 | odata-v4-parser@^0.1.12, odata-v4-parser@0.1.13: 489 | version "0.1.13" 490 | resolved "https://registry.yarnpkg.com/odata-v4-parser/-/odata-v4-parser-0.1.13.tgz#4b5cfef0381cc39c1ca0d53c0bbc825cc655b492" 491 | 492 | odata-v4-server@rc: 493 | version "0.1.5" 494 | resolved "https://registry.yarnpkg.com/odata-v4-server/-/odata-v4-server-0.1.5.tgz#526ca6b420c76c3769fe02c8729c1f87e8c60f00" 495 | dependencies: 496 | "@types/body-parser" "0.0.33" 497 | "@types/cors" "0.0.33" 498 | "@types/express" "^4.0.33" 499 | "@types/extend" "^2.0.30" 500 | "@types/qs" "^6.2.30" 501 | body-parser "^1.15.2" 502 | cors "^2.8.0" 503 | express "^4.14.0" 504 | extend "^3.0.0" 505 | odata-v4-metadata "^0.1.3" 506 | odata-v4-parser "^0.1.12" 507 | odata-v4-service-document "0.0.3" 508 | odata-v4-service-metadata "^0.1.5" 509 | qs "^6.3.0" 510 | reflect-metadata "^0.1.8" 511 | 512 | odata-v4-service-document@0.0.3: 513 | version "0.0.3" 514 | resolved "https://registry.yarnpkg.com/odata-v4-service-document/-/odata-v4-service-document-0.0.3.tgz#0cd900b5a2e078e02d3383c14d51c48de67caa8a" 515 | dependencies: 516 | extend "^3.0.0" 517 | odata-v4-metadata "^0.1.2" 518 | odata-v4-service-metadata "^0.1.4" 519 | 520 | odata-v4-service-metadata@^0.1.4, odata-v4-service-metadata@^0.1.5: 521 | version "0.1.5" 522 | resolved "https://registry.yarnpkg.com/odata-v4-service-metadata/-/odata-v4-service-metadata-0.1.5.tgz#d26a799d454acae606e8ee742adca0fbb60d5ff1" 523 | dependencies: 524 | extend "^3.0.0" 525 | odata-v4-metadata "^0.1.2" 526 | 527 | on-finished@~2.3.0: 528 | version "2.3.0" 529 | resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" 530 | dependencies: 531 | ee-first "1.1.1" 532 | 533 | once@^1.3.0: 534 | version "1.4.0" 535 | resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" 536 | dependencies: 537 | wrappy "1" 538 | 539 | parseurl@~1.3.1: 540 | version "1.3.1" 541 | resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.1.tgz#c8ab8c9223ba34888aa64a297b28853bec18da56" 542 | 543 | path-is-absolute@^1.0.0: 544 | version "1.0.1" 545 | resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" 546 | 547 | path-to-regexp@0.1.7: 548 | version "0.1.7" 549 | resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" 550 | 551 | process-nextick-args@~1.0.6: 552 | version "1.0.7" 553 | resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3" 554 | 555 | promise@^7.0.1: 556 | version "7.1.1" 557 | resolved "https://registry.yarnpkg.com/promise/-/promise-7.1.1.tgz#489654c692616b8aa55b0724fa809bb7db49c5bf" 558 | dependencies: 559 | asap "~2.0.3" 560 | 561 | proxy-addr@~1.1.2: 562 | version "1.1.2" 563 | resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-1.1.2.tgz#b4cc5f22610d9535824c123aef9d3cf73c40ba37" 564 | dependencies: 565 | forwarded "~0.1.0" 566 | ipaddr.js "1.1.1" 567 | 568 | qs@^6.3.0: 569 | version "6.3.0" 570 | resolved "https://registry.yarnpkg.com/qs/-/qs-6.3.0.tgz#f403b264f23bc01228c74131b407f18d5ea5d442" 571 | 572 | qs@6.2.0: 573 | version "6.2.0" 574 | resolved "https://registry.yarnpkg.com/qs/-/qs-6.2.0.tgz#3b7848c03c2dece69a9522b0fae8c4126d745f3b" 575 | 576 | range-parser@~1.2.0: 577 | version "1.2.0" 578 | resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e" 579 | 580 | raw-body@~2.1.7: 581 | version "2.1.7" 582 | resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.1.7.tgz#adfeace2e4fb3098058014d08c072dcc59758774" 583 | dependencies: 584 | bytes "2.4.0" 585 | iconv-lite "0.4.13" 586 | unpipe "1.0.0" 587 | 588 | readable-stream@^2.0.2: 589 | version "2.1.5" 590 | resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.1.5.tgz#66fa8b720e1438b364681f2ad1a63c618448c9d0" 591 | dependencies: 592 | buffer-shims "^1.0.0" 593 | core-util-is "~1.0.0" 594 | inherits "~2.0.1" 595 | isarray "~1.0.0" 596 | process-nextick-args "~1.0.6" 597 | string_decoder "~0.10.x" 598 | util-deprecate "~1.0.1" 599 | 600 | readable-stream@~2.0.5: 601 | version "2.0.6" 602 | resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.0.6.tgz#8f90341e68a53ccc928788dacfcd11b36eb9b78e" 603 | dependencies: 604 | core-util-is "~1.0.0" 605 | inherits "~2.0.1" 606 | isarray "~1.0.0" 607 | process-nextick-args "~1.0.6" 608 | string_decoder "~0.10.x" 609 | util-deprecate "~1.0.1" 610 | 611 | reflect-metadata@^0.1.8: 612 | version "0.1.8" 613 | resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.8.tgz#72426d570b60776e3688968bd5ab9537a15cecf6" 614 | 615 | semver@^5.1.0: 616 | version "5.3.0" 617 | resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" 618 | 619 | send@0.14.1: 620 | version "0.14.1" 621 | resolved "https://registry.yarnpkg.com/send/-/send-0.14.1.tgz#a954984325392f51532a7760760e459598c89f7a" 622 | dependencies: 623 | debug "~2.2.0" 624 | depd "~1.1.0" 625 | destroy "~1.0.4" 626 | encodeurl "~1.0.1" 627 | escape-html "~1.0.3" 628 | etag "~1.7.0" 629 | fresh "0.3.0" 630 | http-errors "~1.5.0" 631 | mime "1.3.4" 632 | ms "0.7.1" 633 | on-finished "~2.3.0" 634 | range-parser "~1.2.0" 635 | statuses "~1.3.0" 636 | 637 | serve-static@~1.11.1: 638 | version "1.11.1" 639 | resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.11.1.tgz#d6cce7693505f733c759de57befc1af76c0f0805" 640 | dependencies: 641 | encodeurl "~1.0.1" 642 | escape-html "~1.0.3" 643 | parseurl "~1.3.1" 644 | send "0.14.1" 645 | 646 | setprototypeof@1.0.1: 647 | version "1.0.1" 648 | resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.0.1.tgz#52009b27888c4dc48f591949c0a8275834c1ca7e" 649 | 650 | sprintf@0.1.5: 651 | version "0.1.5" 652 | resolved "https://registry.yarnpkg.com/sprintf/-/sprintf-0.1.5.tgz#8f83e39a9317c1a502cb7db8050e51c679f6edcf" 653 | 654 | "statuses@>= 1.3.0 < 2", statuses@~1.3.0: 655 | version "1.3.0" 656 | resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.3.0.tgz#8e55758cb20e7682c1f4fce8dcab30bf01d1e07a" 657 | 658 | string_decoder@~0.10.x: 659 | version "0.10.31" 660 | resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" 661 | 662 | supports-color@3.1.2: 663 | version "3.1.2" 664 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.1.2.tgz#72a262894d9d408b956ca05ff37b2ed8a6e2a2d5" 665 | dependencies: 666 | has-flag "^1.0.0" 667 | 668 | tedious@~1.14.0: 669 | version "1.14.0" 670 | resolved "https://registry.yarnpkg.com/tedious/-/tedious-1.14.0.tgz#c05c303b8668cf91e55a6493b744866af61987de" 671 | dependencies: 672 | babel-runtime "^5.8.19" 673 | big-number "0.3.1" 674 | bl "^1.0.0" 675 | iconv-lite "^0.4.11" 676 | readable-stream "^2.0.2" 677 | semver "^5.1.0" 678 | sprintf "0.1.5" 679 | 680 | type-detect@^1.0.0: 681 | version "1.0.0" 682 | resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-1.0.0.tgz#762217cc06db258ec48908a1298e8b95121e8ea2" 683 | 684 | type-detect@0.1.1: 685 | version "0.1.1" 686 | resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-0.1.1.tgz#0ba5ec2a885640e470ea4e8505971900dac58822" 687 | 688 | type-is@~1.6.13: 689 | version "1.6.13" 690 | resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.13.tgz#6e83ba7bc30cd33a7bb0b7fb00737a2085bf9d08" 691 | dependencies: 692 | media-typer "0.3.0" 693 | mime-types "~2.1.11" 694 | 695 | typescript@^2.0.6: 696 | version "2.0.7" 697 | resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.0.7.tgz#efc39e8822e240d0b741d06ff86708137bcdb5e4" 698 | 699 | unpipe@~1.0.0, unpipe@1.0.0: 700 | version "1.0.0" 701 | resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" 702 | 703 | util-deprecate@~1.0.1: 704 | version "1.0.2" 705 | resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" 706 | 707 | utils-merge@1.0.0: 708 | version "1.0.0" 709 | resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.0.tgz#0294fb922bb9375153541c4f7096231f287c8af8" 710 | 711 | vary@^1, vary@~1.1.0: 712 | version "1.1.0" 713 | resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.0.tgz#e1e5affbbd16ae768dd2674394b9ad3022653140" 714 | 715 | wrappy@1: 716 | version "1.0.2" 717 | resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" 718 | 719 | --------------------------------------------------------------------------------