├── .babelrc ├── .gitignore ├── .travis.yml ├── README.md ├── dist └── soql-parse.js ├── index.html ├── lib └── index.js ├── package.json ├── src └── parser.pegjs ├── test ├── data │ ├── expected │ │ ├── 00-simple-01.json5 │ │ ├── 01-simple-02.json5 │ │ ├── 02-select-clause-01.json5 │ │ ├── 03-select-clause-02.json5 │ │ ├── 04-select-clause-03.json5 │ │ ├── 05-from-object-with-alias-01.json5 │ │ ├── 06-from-object-with-alias-02.json5 │ │ ├── 07-from-object-group-03.json5 │ │ ├── 08-from-object-order-04.json5 │ │ ├── 10-where-clause-01.json5 │ │ ├── 11-where-clause-02.json5 │ │ ├── 12-where-clause-03.json5 │ │ ├── 13-where-clause-04.json5 │ │ ├── 14-where-clause-05.json5 │ │ ├── 15-where-clause-06.json5 │ │ ├── 16-where-clause-07.json5 │ │ ├── 20-order-by-clause-01.json5 │ │ ├── 21-order-by-clause-02.json5 │ │ ├── 30-group-by-clause-01.json5 │ │ ├── 31-group-by-clause-02.json5 │ │ ├── 32-group-by-clause-03.json5 │ │ ├── 40-date-literal-01.json5 │ │ ├── 41-date-literal-02.json5 │ │ ├── 41-date-literal-03.json5 │ │ ├── 41-date-literal-04.json5 │ │ ├── 50-dates-01.json5 │ │ └── 90-complex-01.json5 │ └── parsable │ │ ├── 00-simple-01.soql │ │ ├── 01-simple-02.soql │ │ ├── 02-select-clause-01.soql │ │ ├── 03-select-clause-02.soql │ │ ├── 04-select-clause-03.soql │ │ ├── 05-from-object-with-alias-01.soql │ │ ├── 06-from-object-with-alias-02.soql │ │ ├── 07-from-object-group-03.soql │ │ ├── 08-from-object-order-04.soql │ │ ├── 10-where-clause-01.soql │ │ ├── 11-where-clause-02.soql │ │ ├── 12-where-clause-03.soql │ │ ├── 13-where-clause-04.soql │ │ ├── 14-where-clause-05.soql │ │ ├── 15-where-clause-06.soql │ │ ├── 16-where-clause-07.soql │ │ ├── 20-order-by-clause-01.soql │ │ ├── 21-order-by-clause-02.soql │ │ ├── 30-group-by-clause-01.soql │ │ ├── 31-group-by-clause-02.soql │ │ ├── 32-group-by-clause-03.soql │ │ ├── 40-date-literal-01.soql │ │ ├── 41-date-literal-02.soql │ │ ├── 41-date-literal-03.soql │ │ ├── 41-date-literal-04.soql │ │ ├── 50-dates-01.soql │ │ └── 90-complex-01.soql └── index.test.js └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["env"] 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### https://raw.github.com/github/gitignore/f31b319dca10213163411cb710c27dc37ed3eac5/Node.gitignore 2 | 3 | lib-cov 4 | *.seed 5 | *.log 6 | *.csv 7 | *.dat 8 | *.out 9 | *.pid 10 | *.gz 11 | 12 | pids 13 | logs 14 | results 15 | 16 | npm-debug.log 17 | node_modules 18 | 19 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - stable 4 | sudo: false 5 | cache: 6 | directories: 7 | - node_modules 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # soql-parse 2 | 3 | Parse [SOQL](https://developer.salesforce.com/docs/atlas.en-us.soql_sosl.meta/soql_sosl/sforce_api_calls_soql.htm) query string to abstract syntax tree in JavaScript. 4 | 5 | [![Build Status](https://travis-ci.org/stomita/soql-parse.svg?branch=master)](https://travis-ci.org/stomita/soql-parse) 6 | 7 | ## Online Demo 8 | 9 | https://stomita.github.io/soql-parse/ 10 | 11 | ## Install 12 | 13 | ``` 14 | npm install soql-parse 15 | ``` 16 | 17 | ## Usage 18 | 19 | ```js 20 | import { parse } from 'soql-parse'; 21 | 22 | const soql = ` 23 | Select Id, Name, toLabel(Type) from Account 24 | WHERE Name like 'A%' and Type IN ('Partner', 'Customer') 25 | ORDER by CreatedDate DESC 26 | LIMIT 10 27 | `; 28 | 29 | const parsed = parse(soql); 30 | console.log(parsed); 31 | ``` 32 | 33 | Result: 34 | 35 | ```json 36 | { 37 | "type": "Query", 38 | "fields": [ 39 | { 40 | "type": "FieldReference", 41 | "path": [ 42 | "Id" 43 | ] 44 | }, 45 | { 46 | "type": "FieldReference", 47 | "path": [ 48 | "Name" 49 | ] 50 | }, 51 | { 52 | "type": "FunctionCall", 53 | "name": "toLabel", 54 | "arguments": [ 55 | { 56 | "type": "FieldReference", 57 | "path": [ 58 | "Type" 59 | ] 60 | } 61 | ] 62 | } 63 | ], 64 | "object": { 65 | "type": "ObjectReference", 66 | "name": "Account" 67 | }, 68 | "condition": { 69 | "type": "LogicalCondition", 70 | "operator": "AND", 71 | "left": { 72 | "type": "ComparisonCondition", 73 | "field": { 74 | "type": "FieldReference", 75 | "path": [ 76 | "Name" 77 | ] 78 | }, 79 | "operator": "LIKE", 80 | "value": { 81 | "type": "string", 82 | "value": "A%" 83 | } 84 | }, 85 | "right": { 86 | "type": "ComparisonCondition", 87 | "field": { 88 | "type": "FieldReference", 89 | "path": [ 90 | "Type" 91 | ] 92 | }, 93 | "operator": "IN", 94 | "value": { 95 | "type": "list", 96 | "values": [ 97 | { 98 | "type": "string", 99 | "value": "Partner" 100 | }, 101 | { 102 | "type": "string", 103 | "value": "Customer" 104 | } 105 | ] 106 | } 107 | } 108 | }, 109 | "sort": [ 110 | { 111 | "field": { 112 | "type": "FieldReference", 113 | "path": [ 114 | "CreatedDate" 115 | ] 116 | }, 117 | "direction": "DESC" 118 | } 119 | ], 120 | "limit": { 121 | "type": "number", 122 | "value": 10 123 | } 124 | } 125 | ``` 126 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | SOQL Parse Demo 5 | 33 | 34 | 35 |
36 |

SOQL Parse Demo

37 |
38 | 39 | 44 |
45 |
46 | 47 |
48 |
49 |
50 | 51 | 56 |
57 |
58 | 59 | 64 |
65 |
66 |
67 | 68 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "soql-parse", 3 | "version": "1.1.1", 4 | "main": "lib/index.js", 5 | "license": "MIT", 6 | "scripts": { 7 | "prepare": "npm run build", 8 | "build": "npm run build:parser && npm run build:web", 9 | "build:parser": "pegjs -o lib/index.js src/parser.pegjs", 10 | "build:web": "webpack lib/index.js dist/soql-parse.js --output-library SOQLParse", 11 | "test": "ava" 12 | }, 13 | "devDependencies": { 14 | "ava": "^0.24.0", 15 | "json5": "^0.5.1", 16 | "pegjs": "^0.10.0", 17 | "webpack": "^3.10.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/parser.pegjs: -------------------------------------------------------------------------------- 1 | { 2 | function assign() { 3 | return Object.assign.apply(null, arguments); 4 | } 5 | 6 | function createLogicalConditionTree(operator, head, tail) { 7 | var result = head; 8 | for (var i = 0; i < tail.length; i++) { 9 | result = { 10 | type: 'LogicalCondition', 11 | operator: operator, 12 | left: result, 13 | right: tail[i], 14 | }; 15 | } 16 | return result; 17 | } 18 | 19 | function isReserved(word) { 20 | return /^(SELECT|FROM|AS|USING|WHERE|AND|OR|NOT|GROUP|BY|ORDER|LIMIT|OFFSET|FOR|TRUE|FALSE|NULL)$/i.test(word); 21 | } 22 | } 23 | 24 | Query = 25 | _ SELECT 26 | __ fields:QueryFieldList 27 | __ object:FromClause 28 | scope:(__ ScopeClause)? 29 | condition:(__ WhereClause)? 30 | group:(__ GroupByClause)? 31 | sort:(__ OrderByClause)? 32 | limit:(__ LimitClause)? 33 | offset:(__ OffsetClause)? 34 | selectFor:(__ SelectForClause)? 35 | _ { 36 | return assign( 37 | { 38 | type: 'Query', 39 | fields: fields, 40 | object: object, 41 | }, 42 | scope ? { scope: scope[1] } : {}, 43 | condition ? { condition: condition[1] } : {}, 44 | group ? { group: group[1] } : {}, 45 | sort ? { sort: sort[1] } : {}, 46 | limit ? { limit: limit[1] } : {}, 47 | offset ? { offset: offset[1] } : {}, 48 | selectFor ? { selectFor: selectFor[1] } : {} 49 | ); 50 | } 51 | 52 | QueryFieldList = 53 | head:QueryFieldListItem _ COMMA _ tail:QueryFieldList { 54 | return [head].concat(tail); 55 | } 56 | / field:QueryFieldListItem { 57 | return [field] 58 | } 59 | 60 | QueryFieldListItem = 61 | SubQuery 62 | / QueryField 63 | 64 | QueryField = 65 | field:FieldExpr alias:(__? Identifier)? { 66 | return ( 67 | alias ? 68 | assign({}, field, { alias: alias[1] }) : 69 | field 70 | ); 71 | } 72 | 73 | FieldExpr = 74 | FunctionCall 75 | / FieldReference 76 | 77 | FunctionCall = 78 | func:Identifier _ LPAREN _ args:FunctionArg* _ RPAREN { 79 | return { 80 | type: 'FunctionCall', 81 | name: func, 82 | arguments: args, 83 | }; 84 | } 85 | 86 | FunctionArg = 87 | FieldReference 88 | 89 | FieldReference = 90 | path:FieldPath { 91 | return { 92 | type: 'FieldReference', 93 | path: path, 94 | }; 95 | } 96 | 97 | FieldPath = 98 | head:Identifier _ DOT _ tail:FieldPath { 99 | return [head].concat(tail); 100 | } 101 | / field:Identifier { 102 | return [field]; 103 | } 104 | 105 | FromClause = 106 | FROM __ 107 | object:ObjectReference 108 | aliasObjects:(_ COMMA _ AliasObjectList)? { 109 | return ( 110 | aliasObjects ? 111 | assign({}, object, { aliasObjects: aliasObjects[3] }) : 112 | object 113 | ); 114 | } 115 | 116 | ObjectReference = 117 | name:ObjectIdentifier alias:(__ (AS __)? Identifier)? { 118 | return assign( 119 | { 120 | type: 'ObjectReference', 121 | name: name, 122 | }, 123 | alias ? { alias: alias[2] } : {} 124 | ); 125 | } 126 | 127 | AliasObjectList = 128 | head:AliasObjectReference _ COMMA _ tail:AliasObjectList { 129 | return [head].concat(tail || []); 130 | } 131 | / head:AliasObjectReference { 132 | return [head]; 133 | } 134 | 135 | AliasObjectReference = 136 | path:FieldPath alias:(__ (AS __)? Identifier)? { 137 | return assign( 138 | { 139 | type: 'AliasObjectReference', 140 | path: path 141 | }, 142 | alias ? { alias: alias[2] } : {} 143 | ); 144 | } 145 | 146 | ScopeClause = 147 | USING __ SCOPE __ scope:FilterScope { 148 | return scope; 149 | } 150 | 151 | FilterScope = 152 | "Delegated"i { return 'Delegated'; } 153 | / "Everything"i { return 'Everything'; } 154 | / "Mine"i { return 'Mine'; } 155 | / "My_Territory"i { return 'My_My_Territory'; } 156 | / "My_Team_Territory"i { return 'My_Team_Territory'; } 157 | / "Team"i { return 'Team'; } 158 | 159 | WhereClause = 160 | WHERE __ condition: Condition { 161 | return condition 162 | } 163 | 164 | Condition = 165 | OrCondition 166 | 167 | OrCondition = 168 | head:AndCondition tail:(__ OR __ condition:AndCondition { return condition; })* { 169 | return createLogicalConditionTree('OR', head, tail); 170 | } 171 | 172 | AndCondition = 173 | head:NotCondition tail:(__ AND __ condition:NotCondition { return condition; })* { 174 | return createLogicalConditionTree('AND', head, tail); 175 | } 176 | 177 | NotCondition = 178 | NOT __ condition:ParenCondition { 179 | return { 180 | type: 'NegateCondition', 181 | operator: 'NOT', 182 | condition: condition, 183 | }; 184 | } 185 | / ParenCondition 186 | 187 | ParenCondition = 188 | LPAREN _ condition:Condition _ RPAREN { 189 | return assign({}, condition, { parentheses: true }); 190 | } 191 | / ComparisonCondition 192 | 193 | ComparisonCondition = 194 | field:FieldExpr 195 | operator:( 196 | _ o:SpecialCharComparisonOperator _ { return o; } 197 | / __ o:ComparisonOperator __ { return o; } 198 | ) 199 | value:ComparisonValue { 200 | return { 201 | type: 'ComparisonCondition', 202 | field: field, 203 | operator: operator, 204 | value: value, 205 | }; 206 | } 207 | 208 | SpecialCharComparisonOperator = 209 | "=" / "!=" / "<=" / ">=" / "<" / ">" 210 | 211 | ComparisonOperator = 212 | "LIKE"i { return 'LIKE'; } 213 | / "IN"i { return 'IN'; } 214 | / "NOT"i __ "IN"i { return 'NOT IN'; } 215 | / "INCLUDES"i { return 'INCLUDES'; } 216 | / "EXCLUDES"i { return 'EXCLUDES'; } 217 | 218 | ComparisonValue = 219 | SubQuery 220 | / ListLiteral 221 | / Literal 222 | / BindVariable 223 | 224 | GroupByClause = 225 | GROUP __ BY __ ROLLUP _ LPAREN _ fields:GroupItemList _ RPAREN { 226 | return { 227 | type: 'RollupGrouping', 228 | fields: fields 229 | }; 230 | } 231 | / GROUP __ BY __ CUBE _ LPAREN _ fields:GroupItemList _ RPAREN { 232 | return { 233 | type: 'CubeGrouping', 234 | fields: fields 235 | }; 236 | } 237 | / GROUP __ BY __ fields:GroupItemList { 238 | return { 239 | type: 'Grouping', 240 | fields: fields, 241 | }; 242 | } 243 | 244 | GroupItemList = 245 | head:GroupItem _ COMMA _ tail:GroupItemList { 246 | return [head].concat(tail); 247 | } 248 | / group:GroupItem { 249 | return [group]; 250 | } 251 | 252 | GroupItem = 253 | FieldExpr 254 | 255 | OrderByClause = 256 | ORDER __ BY __ sort:SortItemList { 257 | return sort; 258 | } 259 | 260 | SortItemList = 261 | head:SortItem _ COMMA _ tail:SortItemList { 262 | return [head].concat(tail); 263 | } 264 | / sort:SortItem { 265 | return [sort]; 266 | } 267 | 268 | SortItem = 269 | field:FieldExpr 270 | direction:(__ SortDir)? 271 | nullOrder:(__ NullOrder)? { 272 | return assign( 273 | { field: field }, 274 | direction ? { direction: direction[1] } : {}, 275 | nullOrder ? { nullOrder: nullOrder[1] } : {} 276 | ); 277 | } 278 | 279 | SortDir = 280 | ASC { return 'ASC'; } 281 | / DESC { return 'DESC'; } 282 | 283 | NullOrder = 284 | NULLS __ FIRST { return 'FIRST'; } 285 | / NULLS __ LAST { return 'LAST'; } 286 | 287 | LimitClause = 288 | LIMIT __ value:LimitValue { 289 | return value; 290 | } 291 | 292 | LimitValue = 293 | NumberLiteral 294 | / BindVariable 295 | 296 | OffsetClause = 297 | OFFSET __ value:OffsetValue { 298 | return value; 299 | } 300 | 301 | OffsetValue = 302 | NumberLiteral 303 | / BindVariable 304 | 305 | SelectForClause = 306 | FOR __ VIEW { return 'VIEW'; } 307 | / FOR __ REFERENCE { return 'REFERENCE'; } 308 | 309 | SubQuery = 310 | LPAREN 311 | _ SELECT 312 | __ fields:SubQueryFieldList 313 | __ object:FromClause 314 | condition:(__ WhereClause)? 315 | sort:(__ OrderByClause)? 316 | limit:(__ LimitClause)? 317 | _ RPAREN { 318 | return assign( 319 | { 320 | type: 'Query', 321 | fields: fields, 322 | object: object, 323 | }, 324 | condition ? { condition: condition[1] } : {}, 325 | sort ? { sort: sort[1] } : {}, 326 | limit ? { limit: limit[1] } : {} 327 | ); 328 | } 329 | 330 | SubQueryFieldList = 331 | head:SubQueryFieldListItem _ COMMA _ tail:SubQueryFieldList { 332 | return [head].concat(tail); 333 | } 334 | / field:SubQueryFieldListItem { 335 | return [field] 336 | } 337 | 338 | SubQueryFieldListItem = FieldExpr 339 | 340 | Identifier = 341 | id:([a-zA-Z][0-9a-zA-Z_]* { return text() }) & { return !isReserved(id) } { return id; } 342 | 343 | // 'Group' and 'Order' are valid sobjects to query from, 344 | // as well as are part of the reserved keywords 'GROUP BY' and 'ORDER BY', 345 | // so we need this special identifier pattern for FromClause 346 | ObjectIdentifier = 347 | "GROUP"i { return text() } 348 | / "ORDER"i { return text() } 349 | / Identifier 350 | 351 | BindVariable = 352 | COLON identifier:Identifier { 353 | return { 354 | type: 'BindVariable', 355 | identifier: identifier, 356 | }; 357 | } 358 | 359 | ListLiteral = 360 | LPAREN _ values:LiteralList _ RPAREN { 361 | return { 362 | type: 'list', 363 | values: values, 364 | }; 365 | } 366 | 367 | LiteralList = 368 | head:Literal _ COMMA _ tail:LiteralList { 369 | return [head].concat(tail); 370 | } 371 | / Literal 372 | 373 | Literal = 374 | StringLiteral 375 | / ISODateLiteral 376 | / DateLiteral 377 | / NumberLiteral 378 | / BooleanLiteral 379 | / NullLiteral 380 | 381 | NumberLiteral = 382 | n:Number { 383 | return { 384 | type: 'number', 385 | value: n 386 | } 387 | } 388 | 389 | Number = 390 | int_:Int frac:Frac { return parseFloat(int_ + frac); } 391 | / int_:Int { return parseFloat(int_); } 392 | 393 | Int 394 | = digit19:Digit19 digits:Digits { return digit19 + digits; } 395 | / digit:Digit 396 | / op:[+-] digits:Digits { return op + digit19 + digits; } 397 | / op:[+-] digit:Digit { return op + digit; } 398 | 399 | Frac 400 | = "." digits:Digits { return "." + digits; } 401 | 402 | Digits 403 | = digits:Digit+ { return digits.join(""); } 404 | 405 | Integer2 406 | = $(Digit Digit) 407 | 408 | Integer4 409 | = $(Digit Digit Digit Digit) 410 | 411 | Digit = [0-9] 412 | Digit19 = [1-9] 413 | 414 | HexDigit 415 | = [0-9a-fA-F] 416 | 417 | StringLiteral = 418 | QUOTE ca:(SingleChar*) QUOTE { 419 | return { 420 | type: 'string', 421 | value: ca.join('') 422 | }; 423 | } 424 | 425 | 426 | SingleChar = 427 | [^'\\\0-\x1F\x7f] 428 | / EscapeChar 429 | 430 | EscapeChar = 431 | "\\'" { return "'"; } 432 | / '\\"' { return '"'; } 433 | / "\\\\" { return "\\"; } 434 | / "\\/" { return "/"; } 435 | / "\\b" { return "\b"; } 436 | / "\\f" { return "\f"; } 437 | / "\\n" { return "\n"; } 438 | / "\\r" { return "\r"; } 439 | / "\\t" { return "\t"; } 440 | / "\\u" h1:HexDigit h2:HexDigit h3:HexDigit h4:HexDigit { 441 | return String.fromCharCode(parseInt("0x" + h1 + h2 + h3 + h4)); 442 | } 443 | 444 | ISODate 445 | = Integer4 "-" Integer2 "-" Integer2 446 | 447 | ISOTZ 448 | = "Z" 449 | / $(("+" / "-") Integer2 ":" Integer2 ) 450 | / $(("+" / "-") Integer4 ) 451 | 452 | DateFormatLiteral = 453 | Integer4 "-" Integer2 "-" Integer2 { 454 | return { 455 | type: 'date', 456 | value: text() 457 | }; 458 | } 459 | 460 | ISOTime 461 | = $(Integer2 ":" Integer2 ":" Integer2) 462 | 463 | ISODateLiteral 464 | = d:ISODate t:$("T" ISOTime)? z:$ISOTZ? { 465 | return { 466 | type: t || z ? 'datetime' : 'date', 467 | value: text() 468 | } 469 | } 470 | 471 | DateLiteral = 472 | d:TODAY { 473 | return { 474 | type: 'dateLiteral', 475 | value: text() 476 | } 477 | } 478 | / d:YESTERDAY { 479 | return { 480 | type: 'dateLiteral', 481 | value: text() 482 | } 483 | } 484 | / d:TOMORROW { 485 | return { 486 | type: 'dateLiteral', 487 | value: text() 488 | } 489 | } 490 | / d:LAST_WEEK { 491 | return { 492 | type: 'dateLiteral', 493 | value: text() 494 | } 495 | } 496 | / d:THIS_WEEK { 497 | return { 498 | type: 'dateLiteral', 499 | value: text() 500 | } 501 | } 502 | / d:NEXT_WEEK { 503 | return { 504 | type: 'dateLiteral', 505 | value: text() 506 | } 507 | } 508 | / d:LAST_MONTH { 509 | return { 510 | type: 'dateLiteral', 511 | value: text() 512 | } 513 | } 514 | / d:THIS_MONTH { 515 | return { 516 | type: 'dateLiteral', 517 | value: text() 518 | } 519 | } 520 | / d:NEXT_MONTH { 521 | return { 522 | type: 'dateLiteral', 523 | value: text() 524 | } 525 | } 526 | / d:LAST_90_DAYS { 527 | return { 528 | type: 'dateLiteral', 529 | value: text() 530 | } 531 | } 532 | / d:NEXT_90_DAYS { 533 | return { 534 | type: 'dateLiteral', 535 | value: text() 536 | } 537 | } 538 | / d:THIS_QUARTER { 539 | return { 540 | type: 'dateLiteral', 541 | value: text() 542 | } 543 | } 544 | / d:LAST_QUARTER { 545 | return { 546 | type: 'dateLiteral', 547 | value: text() 548 | } 549 | } 550 | / d:NEXT_QUARTER { 551 | return { 552 | type: 'dateLiteral', 553 | value: text() 554 | } 555 | } 556 | / d:THIS_YEAR { 557 | return { 558 | type: 'dateLiteral', 559 | value: text() 560 | } 561 | } 562 | / d:LAST_YEAR { 563 | return { 564 | type: 'dateLiteral', 565 | value: text() 566 | } 567 | } 568 | / d:NEXT_YEAR { 569 | return { 570 | type: 'dateLiteral', 571 | value: text() 572 | } 573 | } 574 | / d:THIS_FISCAL_QUARTER { 575 | return { 576 | type: 'dateLiteral', 577 | value: text() 578 | } 579 | } 580 | / d:LAST_FISCAL_QUARTER { 581 | return { 582 | type: 'dateLiteral', 583 | value: text() 584 | } 585 | } 586 | / d:NEXT_FISCAL_QUARTER { 587 | return { 588 | type: 'dateLiteral', 589 | value: text() 590 | } 591 | } 592 | / d:THIS_FISCAL_YEAR { 593 | return { 594 | type: 'dateLiteral', 595 | value: text() 596 | } 597 | } 598 | / d:LAST_FISCAL_YEAR { 599 | return { 600 | type: 'dateLiteral', 601 | value: text() 602 | } 603 | } 604 | / d:NEXT_FISCAL_YEAR { 605 | return { 606 | type: 'dateLiteral', 607 | value: text() 608 | } 609 | } 610 | / d:LAST_N_DAYS c:_":"_ n:$(Digit+) { 611 | return { 612 | type: 'dateLiteral', 613 | value: d, 614 | argument: parseInt(n) 615 | } 616 | } 617 | / d:NEXT_N_DAYS c:_":"_ n:$(Digit+) { 618 | return { 619 | type: 'dateLiteral', 620 | value: d, 621 | argument: parseInt(n) 622 | } 623 | } 624 | / d:NEXT_N_WEEKS c:_":"_ n:$(Digit+) { 625 | return { 626 | type: 'dateLiteral', 627 | value: d, 628 | argument: parseInt(n) 629 | } 630 | } 631 | / d:LAST_N_WEEKS c:_":"_ n:$(Digit+) { 632 | return { 633 | type: 'dateLiteral', 634 | value: d, 635 | argument: parseInt(n) 636 | } 637 | } 638 | / d:NEXT_N_MONTHS c:_":"_ n:$(Digit+) { 639 | return { 640 | type: 'dateLiteral', 641 | value: d, 642 | argument: parseInt(n) 643 | } 644 | } 645 | / d:LAST_N_MONTHS c:_":"_ n:$(Digit+) { 646 | return { 647 | type: 'dateLiteral', 648 | value: d, 649 | argument: parseInt(n) 650 | } 651 | } 652 | / d:NEXT_N_QUARTERS c:_":"_ n:$(Digit+) { 653 | return { 654 | type: 'dateLiteral', 655 | value: d, 656 | argument: parseInt(n) 657 | } 658 | } 659 | / d:LAST_N_QUARTERS c:_":"_ n:$(Digit+) { 660 | return { 661 | type: 'dateLiteral', 662 | value: d, 663 | argument: parseInt(n) 664 | } 665 | } 666 | / d:NEXT_N_YEARS c:_":"_ n:$(Digit+) { 667 | return { 668 | type: 'dateLiteral', 669 | value: d, 670 | argument: parseInt(n) 671 | } 672 | } 673 | / d:LAST_N_YEARS c:_":"_ n:$(Digit+) { 674 | return { 675 | type: 'dateLiteral', 676 | value: d, 677 | argument: parseInt(n) 678 | } 679 | } 680 | / d:NEXT_N_FISCAL_QUARTERS c:_":"_ n:$(Digit+) { 681 | return { 682 | type: 'dateLiteral', 683 | value: d, 684 | argument: parseInt(n) 685 | } 686 | } 687 | / d:LAST_N_FISCAL_QUARTERS c:_":"_ n:$(Digit+) { 688 | return { 689 | type: 'dateLiteral', 690 | value: d, 691 | argument: parseInt(n) 692 | } 693 | } 694 | / d:NEXT_N_FISCAL_YEARS c:_":"_ n:$(Digit+) { 695 | return { 696 | type: 'dateLiteral', 697 | value: d, 698 | argument: parseInt(n) 699 | } 700 | } 701 | / d:LAST_N_FISCAL_YEARS c:_":"_ n:$(Digit+) { 702 | return { 703 | type: 'dateLiteral', 704 | value: d, 705 | argument: parseInt(n) 706 | } 707 | } 708 | 709 | BooleanLiteral = 710 | TRUE { 711 | return { 712 | type: 'boolean', 713 | value: true 714 | }; 715 | } 716 | / FALSE { 717 | return { 718 | type: 'boolean', 719 | value: false 720 | }; 721 | } 722 | 723 | NullLiteral = 724 | NULL { 725 | return { 726 | type: 'null', 727 | value: null 728 | }; 729 | } 730 | 731 | COMMA = "," 732 | DOT = "." 733 | LPAREN = "(" 734 | RPAREN = ")" 735 | QUOTE = "'" 736 | COLON = ":" 737 | 738 | _ "spacer" = 739 | [ \t\n\r]* 740 | 741 | __ "whitespaces" = 742 | [ \t\n\r]+ 743 | 744 | 745 | // Keywords 746 | 747 | SELECT = "SELECT"i 748 | FROM = "FROM"i 749 | AS = "AS"i 750 | USING = "USING"i 751 | SCOPE = "SCOPE"i 752 | WHERE = "WHERE"i 753 | OR = "OR"i 754 | AND = "AND"i 755 | NOT = "NOT"i 756 | GROUP = "GROUP"i 757 | BY = "BY"i 758 | ROLLUP = "ROLLUP"i 759 | CUBE = "CUBE"i 760 | ORDER = "ORDER"i 761 | ASC = "ASC"i 762 | DESC = "DESC"i 763 | NULLS = "NULLS"i 764 | FIRST = "FIRST"i 765 | LAST = "LAST"i 766 | LIMIT = "LIMIT"i 767 | OFFSET = "OFFSET"i 768 | FOR = "FOR"i 769 | VIEW = "VIEW"i 770 | REFERENCE = "REFERENCE"i 771 | TRUE = "TRUE"i 772 | FALSE = "FALSE"i 773 | NULL = "NULL"i 774 | 775 | // Date Literals 776 | 777 | YESTERDAY = "YESTERDAY"i 778 | TODAY = "TODAY"i 779 | TOMORROW = "TOMORROW"i 780 | LAST_WEEK = "LAST_WEEK"i 781 | THIS_WEEK = "THIS_WEEK"i 782 | NEXT_WEEK = "NEXT_WEEK"i 783 | LAST_MONTH = "LAST_MONTH"i 784 | THIS_MONTH = "THIS_MONTH"i 785 | NEXT_MONTH = "NEXT_MONTH"i 786 | LAST_90_DAYS = "LAST_90_DAYS"i 787 | NEXT_90_DAYS = "NEXT_90_DAYS"i 788 | THIS_QUARTER = "THIS_QUARTER"i 789 | LAST_QUARTER = "LAST_QUARTER"i 790 | NEXT_QUARTER = "NEXT_QUARTER"i 791 | THIS_YEAR = "THIS_YEAR"i 792 | LAST_YEAR = "LAST_YEAR"i 793 | NEXT_YEAR = "NEXT_YEAR"i 794 | THIS_FISCAL_QUARTER = "THIS_FISCAL_QUARTER"i 795 | LAST_FISCAL_QUARTER = "LAST_FISCAL_QUARTER"i 796 | NEXT_FISCAL_QUARTER = "NEXT_FISCAL_QUARTER"i 797 | THIS_FISCAL_YEAR = "THIS_FISCAL_YEAR"i 798 | LAST_FISCAL_YEAR = "LAST_FISCAL_YEAR"i 799 | NEXT_FISCAL_YEAR = "NEXT_FISCAL_YEAR"i 800 | LAST_N_DAYS = "LAST_N_DAYS"i 801 | NEXT_N_DAYS = "NEXT_N_DAYS"i 802 | NEXT_N_WEEKS = "NEXT_N_WEEKS"i 803 | LAST_N_WEEKS = "LAST_N_WEEKS"i 804 | NEXT_N_MONTHS = "NEXT_N_MONTHS"i 805 | LAST_N_MONTHS = "LAST_N_MONTHS"i 806 | NEXT_N_QUARTERS = "NEXT_N_QUARTERS"i 807 | LAST_N_QUARTERS = "LAST_N_QUARTERS"i 808 | NEXT_N_YEARS = "NEXT_N_YEARS"i 809 | LAST_N_YEARS = "LAST_N_YEARS"i 810 | NEXT_N_FISCAL_QUARTERS = "NEXT_N_FISCAL_QUARTERS"i 811 | LAST_N_FISCAL_QUARTERS = "LAST_N_FISCAL_QUARTERS"i 812 | NEXT_N_FISCAL_YEARS = "NEXT_N_FISCAL_YEARS"i 813 | LAST_N_FISCAL_YEARS = "LAST_N_FISCAL_YEARS"i -------------------------------------------------------------------------------- /test/data/expected/00-simple-01.json5: -------------------------------------------------------------------------------- 1 | { 2 | type: 'Query', 3 | fields: [{ 4 | type: 'FieldReference', 5 | path: ['Name'], 6 | }], 7 | object: { 8 | type: 'ObjectReference', 9 | name: 'Account', 10 | }, 11 | } 12 | -------------------------------------------------------------------------------- /test/data/expected/01-simple-02.json5: -------------------------------------------------------------------------------- 1 | { 2 | type: 'Query', 3 | fields: [{ 4 | type: 'FieldReference', 5 | path: ['Name'], 6 | }], 7 | object: { 8 | type: 'ObjectReference', 9 | name: 'Account', 10 | }, 11 | } 12 | -------------------------------------------------------------------------------- /test/data/expected/02-select-clause-01.json5: -------------------------------------------------------------------------------- 1 | { 2 | type: 'Query', 3 | fields: [{ 4 | type: 'FieldReference', 5 | path: ['Id'], 6 | }, { 7 | type: 'FieldReference', 8 | path: ['Name'], 9 | }, { 10 | type: 'FieldReference', 11 | path: ['Owner', 'Name'], 12 | }], 13 | object: { 14 | type: 'ObjectReference', 15 | name: 'Account', 16 | }, 17 | } 18 | -------------------------------------------------------------------------------- /test/data/expected/03-select-clause-02.json5: -------------------------------------------------------------------------------- 1 | { 2 | type: 'Query', 3 | fields: [{ 4 | type: 'FieldReference', 5 | path: ['Id'], 6 | }, { 7 | type: 'FieldReference', 8 | path: ['Name'], 9 | }, { 10 | type: 'FunctionCall', 11 | name: 'toLabel', 12 | arguments: [{ 13 | type: 'FieldReference', 14 | path: ['Account', 'Type'], 15 | }], 16 | }, { 17 | type: 'FunctionCall', 18 | name: 'convertCurrency', 19 | arguments: [{ 20 | type: 'FieldReference', 21 | path: ['Amount'], 22 | }], 23 | }], 24 | object: { 25 | type: 'ObjectReference', 26 | name: 'Opportunity', 27 | }, 28 | } 29 | -------------------------------------------------------------------------------- /test/data/expected/04-select-clause-03.json5: -------------------------------------------------------------------------------- 1 | { 2 | type: 'Query', 3 | fields: [{ 4 | type: 'FieldReference', 5 | path: ['Id'], 6 | }, { 7 | type: 'FieldReference', 8 | path: ['Name'], 9 | }, { 10 | type: 'Query', 11 | fields: [{ 12 | type: 'FieldReference', 13 | path: ['Id'], 14 | }, { 15 | type: 'FieldReference', 16 | path: ['Name'], 17 | }], 18 | object: { 19 | type: 'ObjectReference', 20 | name: 'Contacts', 21 | }, 22 | }], 23 | object: { 24 | type: 'ObjectReference', 25 | name: 'Account', 26 | }, 27 | } 28 | -------------------------------------------------------------------------------- /test/data/expected/05-from-object-with-alias-01.json5: -------------------------------------------------------------------------------- 1 | { 2 | type: 'Query', 3 | fields: [{ 4 | type: 'FieldReference', 5 | path: ['Id'], 6 | }, { 7 | type: 'FieldReference', 8 | path: ['Name'], 9 | }, { 10 | type: 'FieldReference', 11 | path: ['Amount'], 12 | }, { 13 | type: 'FieldReference', 14 | path: ['a', 'Id'], 15 | }, { 16 | type: 'FieldReference', 17 | path: ['a', 'Name'], 18 | }], 19 | object: { 20 | type: 'ObjectReference', 21 | name: 'Opportunity', 22 | aliasObjects: [{ 23 | type: 'AliasObjectReference', 24 | path: ['Opportunity', 'Account'], 25 | alias: 'a' 26 | }], 27 | }, 28 | } 29 | -------------------------------------------------------------------------------- /test/data/expected/06-from-object-with-alias-02.json5: -------------------------------------------------------------------------------- 1 | { 2 | type: 'Query', 3 | fields: [{ 4 | type: 'FieldReference', 5 | path: ['o', 'Id'], 6 | }, { 7 | type: 'FieldReference', 8 | path: ['o', 'Name'], 9 | }, { 10 | type: 'FieldReference', 11 | path: ['o', 'Amount'], 12 | }, { 13 | type: 'FieldReference', 14 | path: ['a', 'Id'], 15 | }, { 16 | type: 'FieldReference', 17 | path: ['a', 'Name'], 18 | }, { 19 | type: 'FieldReference', 20 | path: ['u', 'Username'], 21 | }], 22 | object: { 23 | type: 'ObjectReference', 24 | name: 'Opportunity', 25 | alias: 'o', 26 | aliasObjects: [{ 27 | type: 'AliasObjectReference', 28 | path: ['o', 'Account'], 29 | alias: 'a' 30 | }, { 31 | type: 'AliasObjectReference', 32 | path: ['a', 'Owner'], 33 | alias: 'u' 34 | }], 35 | }, 36 | } 37 | -------------------------------------------------------------------------------- /test/data/expected/07-from-object-group-03.json5: -------------------------------------------------------------------------------- 1 | { 2 | type: 'Query', 3 | fields: [{ 4 | type: 'FieldReference', 5 | path: ['Name'], 6 | }, { 7 | type: 'FunctionCall', 8 | name: 'COUNT', 9 | arguments: [{ 10 | type: 'FieldReference', 11 | path: ['Id'], 12 | }], 13 | alias: 'c' 14 | }], 15 | object: { 16 | type: 'ObjectReference', 17 | name: 'Group', 18 | }, 19 | condition: { 20 | type: 'ComparisonCondition', 21 | operator: '=', 22 | field: { 23 | type: 'FieldReference', 24 | path: ['Type'], 25 | }, 26 | value: { 27 | type: 'string', 28 | value: 'Queue', 29 | }, 30 | }, 31 | group: { 32 | type: 'Grouping', 33 | fields: [{ 34 | type: 'FieldReference', 35 | path: ['Name'], 36 | }], 37 | }, 38 | sort: [{ 39 | field: { 40 | type: 'FieldReference', 41 | path: ['Name'], 42 | }, 43 | direction: 'ASC', 44 | }], 45 | } -------------------------------------------------------------------------------- /test/data/expected/08-from-object-order-04.json5: -------------------------------------------------------------------------------- 1 | { 2 | type: 'Query', 3 | fields: [{ 4 | type: 'FieldReference', 5 | path: ['Name'], 6 | }, { 7 | type: 'FunctionCall', 8 | name: 'COUNT', 9 | arguments: [{ 10 | type: 'FieldReference', 11 | path: ['Id'], 12 | }], 13 | alias: 'c' 14 | }], 15 | object: { 16 | type: 'ObjectReference', 17 | name: 'Order', 18 | }, 19 | condition: { 20 | type: 'ComparisonCondition', 21 | operator: '=', 22 | field: { 23 | type: 'FieldReference', 24 | path: ['Type'], 25 | }, 26 | value: { 27 | type: 'string', 28 | value: 'Standard', 29 | }, 30 | }, 31 | group: { 32 | type: 'Grouping', 33 | fields: [{ 34 | type: 'FieldReference', 35 | path: ['Name'], 36 | }], 37 | }, 38 | sort: [{ 39 | field: { 40 | type: 'FieldReference', 41 | path: ['Name'], 42 | }, 43 | direction: 'ASC', 44 | }], 45 | } -------------------------------------------------------------------------------- /test/data/expected/10-where-clause-01.json5: -------------------------------------------------------------------------------- 1 | { 2 | type: 'Query', 3 | fields: [{ 4 | type: 'FieldReference', 5 | path: ['Id'], 6 | }, { 7 | type: 'FieldReference', 8 | path: ['Name'], 9 | }], 10 | object: { 11 | type: 'ObjectReference', 12 | name: 'Account', 13 | }, 14 | condition: { 15 | type: 'ComparisonCondition', 16 | operator: '=', 17 | field: { 18 | type: 'FieldReference', 19 | path: ['Name'], 20 | }, 21 | value: { 22 | type: 'string', 23 | value: 'Apple', 24 | }, 25 | }, 26 | } 27 | -------------------------------------------------------------------------------- /test/data/expected/11-where-clause-02.json5: -------------------------------------------------------------------------------- 1 | { 2 | type: 'Query', 3 | fields: [{ 4 | type: 'FieldReference', 5 | path: ['Id'], 6 | }, { 7 | type: 'FieldReference', 8 | path: ['Name'], 9 | }], 10 | object: { 11 | type: 'ObjectReference', 12 | name: 'Account', 13 | }, 14 | condition: { 15 | type: 'LogicalCondition', 16 | operator: 'AND', 17 | left: { 18 | type: 'ComparisonCondition', 19 | operator: '=', 20 | field: { 21 | type: 'FieldReference', 22 | path: ['Name'], 23 | }, 24 | value: { 25 | type: 'string', 26 | value: 'Apple', 27 | }, 28 | }, 29 | right: { 30 | type: 'ComparisonCondition', 31 | operator: '=', 32 | field: { 33 | type: 'FieldReference', 34 | path: ['Owner', 'Active'], 35 | }, 36 | value: { 37 | type: 'boolean', 38 | value: true, 39 | }, 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /test/data/expected/12-where-clause-03.json5: -------------------------------------------------------------------------------- 1 | { 2 | type: 'Query', 3 | fields: [{ 4 | type: 'FieldReference', 5 | path: ['Id'], 6 | }, { 7 | type: 'FieldReference', 8 | path: ['Name'], 9 | }], 10 | object: { 11 | type: 'ObjectReference', 12 | name: 'Account', 13 | }, 14 | condition: { 15 | type: 'LogicalCondition', 16 | operator: 'OR', 17 | left: { 18 | type: 'ComparisonCondition', 19 | operator: 'LIKE', 20 | field: { 21 | type: 'FieldReference', 22 | path: ['Name'], 23 | }, 24 | value: { 25 | type: 'string', 26 | value: 'A%', 27 | }, 28 | }, 29 | right: { 30 | type: 'LogicalCondition', 31 | operator: 'AND', 32 | left: { 33 | type: 'ComparisonCondition', 34 | operator: '=', 35 | field: { 36 | type: 'FieldReference', 37 | path: ['Type'], 38 | }, 39 | value: { 40 | type: 'string', 41 | value: 'Partner', 42 | }, 43 | }, 44 | right: { 45 | type: 'ComparisonCondition', 46 | operator: '!=', 47 | field: { 48 | type: 'FieldReference', 49 | path: ['Owner', 'Username'], 50 | }, 51 | value: { 52 | type: 'string', 53 | value: 'user01@example.com', 54 | }, 55 | }, 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /test/data/expected/13-where-clause-04.json5: -------------------------------------------------------------------------------- 1 | { 2 | type: 'Query', 3 | fields: [{ 4 | type: 'FieldReference', 5 | path: ['Id'], 6 | }, { 7 | type: 'FieldReference', 8 | path: ['Name'], 9 | }], 10 | object: { 11 | type: 'ObjectReference', 12 | name: 'Account', 13 | }, 14 | condition: { 15 | type: 'LogicalCondition', 16 | operator: 'AND', 17 | left: { 18 | type: 'LogicalCondition', 19 | operator: 'OR', 20 | left: { 21 | type: 'ComparisonCondition', 22 | operator: 'LIKE', 23 | field: { 24 | type: 'FieldReference', 25 | path: ['Name'], 26 | }, 27 | value: { 28 | type: 'string', 29 | value: 'A%', 30 | }, 31 | }, 32 | right: { 33 | type: 'ComparisonCondition', 34 | operator: '=', 35 | field: { 36 | type: 'FieldReference', 37 | path: ['Type'], 38 | }, 39 | value: { 40 | type: 'string', 41 | value: 'Partner', 42 | }, 43 | }, 44 | parentheses: true, 45 | }, 46 | right: { 47 | type: 'ComparisonCondition', 48 | operator: '!=', 49 | field: { 50 | type: 'FieldReference', 51 | path: ['Owner', 'Username'], 52 | }, 53 | value: { 54 | type: 'string', 55 | value: 'user01@example.com', 56 | }, 57 | }, 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /test/data/expected/14-where-clause-05.json5: -------------------------------------------------------------------------------- 1 | { 2 | type: 'Query', 3 | fields: [{ 4 | type: 'FieldReference', 5 | path: ['Id'], 6 | }, { 7 | type: 'FieldReference', 8 | path: ['Name'], 9 | }], 10 | object: { 11 | type: 'ObjectReference', 12 | name: 'Account', 13 | }, 14 | condition: { 15 | type: 'LogicalCondition', 16 | operator: 'OR', 17 | left: { 18 | type: 'ComparisonCondition', 19 | operator: 'IN', 20 | field: { 21 | type: 'FieldReference', 22 | path: ['Type'], 23 | }, 24 | value: { 25 | type: 'list', 26 | values: [{ 27 | type: 'string', 28 | value: 'Partner', 29 | }, { 30 | type: 'string', 31 | value: 'Customer', 32 | }], 33 | }, 34 | }, 35 | right: { 36 | type: 'LogicalCondition', 37 | operator: 'AND', 38 | left: { 39 | type: 'ComparisonCondition', 40 | operator: 'IN', 41 | field: { 42 | type: 'FunctionCall', 43 | name: 'CALENDAR_YEAR', 44 | arguments: [{ 45 | type: 'FieldReference', 46 | path: ['CreatedDate'], 47 | }], 48 | }, 49 | value: { 50 | type: 'list', 51 | values: [{ 52 | type: 'number', 53 | value: 2016, 54 | }, { 55 | type: 'number', 56 | value: 2017, 57 | }], 58 | }, 59 | }, 60 | right: { 61 | type: 'ComparisonCondition', 62 | operator: 'NOT IN', 63 | field: { 64 | type: 'FunctionCall', 65 | name: 'CALENDAR_MONTH', 66 | arguments: [{ 67 | type: 'FieldReference', 68 | path: ['CreatedDate'], 69 | }], 70 | }, 71 | value: { 72 | type: 'list', 73 | values: [{ 74 | type: 'number', 75 | value: 2, 76 | }, { 77 | type: 'number', 78 | value: 4, 79 | }, { 80 | type: 'number', 81 | value: 6, 82 | }, { 83 | type: 'number', 84 | value: 9, 85 | }, { 86 | type: 'number', 87 | value: 11, 88 | }], 89 | }, 90 | }, 91 | parentheses: true, 92 | }, 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /test/data/expected/15-where-clause-06.json5: -------------------------------------------------------------------------------- 1 | { 2 | type: 'Query', 3 | fields: [{ 4 | type: 'FieldReference', 5 | path: ['Id'], 6 | }, { 7 | type: 'FieldReference', 8 | path: ['SomeNumberField__c'], 9 | }], 10 | object: { 11 | type: 'ObjectReference', 12 | name: 'Account', 13 | }, 14 | condition: { 15 | type: 'ComparisonCondition', 16 | operator: '<=', 17 | field: { 18 | type: 'FieldReference', 19 | path: ['SomeNumberField__c'], 20 | }, 21 | value: { 22 | type: 'number', 23 | value: 10, 24 | }, 25 | }, 26 | } 27 | -------------------------------------------------------------------------------- /test/data/expected/16-where-clause-07.json5: -------------------------------------------------------------------------------- 1 | { 2 | type: 'Query', 3 | fields: [ 4 | { 5 | type: 'FieldReference', 6 | path: ['Id'] 7 | }, 8 | { 9 | type: 'FieldReference', 10 | path: ['SomeNumberField__c'] 11 | } 12 | ], 13 | object: { 14 | type: 'ObjectReference', 15 | name: 'Account' 16 | }, 17 | condition: { 18 | type: 'LogicalCondition', 19 | operator: 'OR', 20 | left: { 21 | type: 'LogicalCondition', 22 | operator: 'OR', 23 | left: { 24 | type: 'LogicalCondition', 25 | operator: 'OR', 26 | left: { 27 | type: 'LogicalCondition', 28 | operator: 'OR', 29 | left: { 30 | type: 'ComparisonCondition', 31 | field: { 32 | type: 'FieldReference', 33 | path: ['CreatedDate'] 34 | }, 35 | operator: '<', 36 | value: { 37 | type: 'datetime', 38 | value: '1999-01-01T23:01:01+01:00' 39 | } 40 | }, 41 | right: { 42 | type: 'ComparisonCondition', 43 | field: { 44 | type: 'FieldReference', 45 | path: ['CreatedDate'] 46 | }, 47 | operator: '<=', 48 | value: { 49 | type: 'datetime', 50 | value: '1999-01-01T23:01:01-08:00' 51 | } 52 | } 53 | }, 54 | right: { 55 | type: 'ComparisonCondition', 56 | field: { 57 | type: 'FieldReference', 58 | path: ['CreatedDate'] 59 | }, 60 | operator: '>=', 61 | value: { 62 | type: 'datetime', 63 | value: '1999-01-01T23:01:01Z' 64 | } 65 | } 66 | }, 67 | right: { 68 | type: 'ComparisonCondition', 69 | field: { 70 | type: 'FieldReference', 71 | path: ['CreatedDate'] 72 | }, 73 | operator: '<=', 74 | value: { 75 | type: 'date', 76 | value: '1999-01-01' 77 | } 78 | } 79 | }, 80 | right: { 81 | type: 'ComparisonCondition', 82 | field: { 83 | type: 'FieldReference', 84 | path: ['CreatedDate'] 85 | }, 86 | operator: '=', 87 | value: { 88 | type: 'datetime', 89 | value: '1999-01-01T23:01:01+01:00' 90 | } 91 | } 92 | } 93 | } -------------------------------------------------------------------------------- /test/data/expected/20-order-by-clause-01.json5: -------------------------------------------------------------------------------- 1 | { 2 | type: 'Query', 3 | fields: [{ 4 | type: 'FieldReference', 5 | path: ['Id'], 6 | }, { 7 | type: 'FieldReference', 8 | path: ['Name'], 9 | }], 10 | object: { 11 | type: 'ObjectReference', 12 | name: 'Account', 13 | }, 14 | sort: [{ 15 | field: { 16 | type: 'FieldReference', 17 | path: ['CreatedDate'], 18 | }, 19 | }], 20 | } 21 | -------------------------------------------------------------------------------- /test/data/expected/21-order-by-clause-02.json5: -------------------------------------------------------------------------------- 1 | { 2 | type: 'Query', 3 | fields: [{ 4 | type: 'FieldReference', 5 | path: ['Id'], 6 | }, { 7 | type: 'FieldReference', 8 | path: ['Name'], 9 | }], 10 | object: { 11 | type: 'ObjectReference', 12 | name: 'Account', 13 | }, 14 | sort: [{ 15 | field: { 16 | type: 'FieldReference', 17 | path: ['Type'], 18 | }, 19 | direction: 'ASC', 20 | nullOrder: 'LAST', 21 | }, { 22 | field: { 23 | type: 'FieldReference', 24 | path: ['CreatedDate'], 25 | }, 26 | direction: 'DESC', 27 | }], 28 | } 29 | -------------------------------------------------------------------------------- /test/data/expected/30-group-by-clause-01.json5: -------------------------------------------------------------------------------- 1 | { 2 | type: 'Query', 3 | fields: [{ 4 | type: 'FunctionCall', 5 | name: 'count', 6 | arguments: [{ 7 | type: 'FieldReference', 8 | path: ['Id'], 9 | }], 10 | }, { 11 | type: 'FieldReference', 12 | path: ['Type'], 13 | }], 14 | object: { 15 | type: 'ObjectReference', 16 | name: 'Account', 17 | }, 18 | group: { 19 | type: 'Grouping', 20 | fields: [{ 21 | type: 'FieldReference', 22 | path: ['Type'], 23 | }], 24 | }, 25 | } 26 | -------------------------------------------------------------------------------- /test/data/expected/31-group-by-clause-02.json5: -------------------------------------------------------------------------------- 1 | { 2 | type: 'Query', 3 | fields: [{ 4 | type: 'FunctionCall', 5 | name: 'count', 6 | arguments: [{ 7 | type: 'FieldReference', 8 | path: ['Id'], 9 | }], 10 | alias: 'Cnt', 11 | }, { 12 | type: 'FieldReference', 13 | path: ['Type'], 14 | }, { 15 | type: 'FunctionCall', 16 | name: 'CALENDAR_YEAR', 17 | arguments: [{ 18 | type: 'FieldReference', 19 | path: ['CreatedDate'], 20 | }], 21 | alias: 'Year', 22 | }], 23 | object: { 24 | type: 'ObjectReference', 25 | name: 'Account', 26 | }, 27 | group: { 28 | type: 'Grouping', 29 | fields: [{ 30 | type: 'FieldReference', 31 | path: ['Type'], 32 | }, { 33 | type: 'FunctionCall', 34 | name: 'CALENDAR_YEAR', 35 | arguments: [{ 36 | type: 'FieldReference', 37 | path: ['CreatedDate'], 38 | }], 39 | }], 40 | }, 41 | } 42 | -------------------------------------------------------------------------------- /test/data/expected/32-group-by-clause-03.json5: -------------------------------------------------------------------------------- 1 | { 2 | type: 'Query', 3 | fields: [{ 4 | type: 'FunctionCall', 5 | name: 'count', 6 | arguments: [{ 7 | type: 'FieldReference', 8 | path: ['Id'], 9 | }], 10 | alias: 'Cnt', 11 | }, { 12 | type: 'FieldReference', 13 | path: ['Type'], 14 | }, { 15 | type: 'FunctionCall', 16 | name: 'CALENDAR_YEAR', 17 | arguments: [{ 18 | type: 'FieldReference', 19 | path: ['CreatedDate'], 20 | }], 21 | alias: 'Year', 22 | }], 23 | object: { 24 | type: 'ObjectReference', 25 | name: 'Account', 26 | }, 27 | group: { 28 | type: 'Grouping', 29 | fields: [{ 30 | type: 'FieldReference', 31 | path: ['Type'], 32 | }, { 33 | type: 'FunctionCall', 34 | name: 'CALENDAR_YEAR', 35 | arguments: [{ 36 | type: 'FieldReference', 37 | path: ['CreatedDate'], 38 | }], 39 | }], 40 | }, 41 | sort: [{ 42 | field: { 43 | type: 'FieldReference', 44 | path: ['Type'], 45 | }, 46 | }, { 47 | field: { 48 | type: 'FunctionCall', 49 | name: 'count', 50 | arguments: [{ 51 | type: 'FieldReference', 52 | path: ['Id'], 53 | }], 54 | }, 55 | direction: 'ASC', 56 | }], 57 | } 58 | -------------------------------------------------------------------------------- /test/data/expected/40-date-literal-01.json5: -------------------------------------------------------------------------------- 1 | { 2 | type: 'Query', 3 | fields: [{ 4 | type: 'FieldReference', 5 | path: ['Id'], 6 | }, { 7 | type: 'FieldReference', 8 | path: ['Name'], 9 | }, { 10 | type: 'FieldReference', 11 | path: ['CreatedDate'], 12 | }], 13 | object: { 14 | type: 'ObjectReference', 15 | name: 'Account', 16 | }, 17 | condition: { 18 | type: 'ComparisonCondition', 19 | operator: '=', 20 | field: { 21 | type: 'FieldReference', 22 | path: ['CreatedDate'], 23 | }, 24 | value: { 25 | type: 'dateLiteral', 26 | value: 'TODAY', 27 | }, 28 | }, 29 | } 30 | -------------------------------------------------------------------------------- /test/data/expected/41-date-literal-02.json5: -------------------------------------------------------------------------------- 1 | { 2 | type: 'Query', 3 | fields: [{ 4 | type: 'FieldReference', 5 | path: ['Id'], 6 | }, { 7 | type: 'FieldReference', 8 | path: ['Name'], 9 | }, { 10 | type: 'FieldReference', 11 | path: ['CreatedDate'], 12 | }], 13 | object: { 14 | type: 'ObjectReference', 15 | name: 'Account', 16 | }, 17 | condition: { 18 | type: 'ComparisonCondition', 19 | operator: '>=', 20 | field: { 21 | type: 'FieldReference', 22 | path: ['CreatedDate'], 23 | }, 24 | value: { 25 | type: 'dateLiteral', 26 | value: 'LAST_N_DAYS', 27 | argument: 1, 28 | }, 29 | }, 30 | } 31 | -------------------------------------------------------------------------------- /test/data/expected/41-date-literal-03.json5: -------------------------------------------------------------------------------- 1 | { 2 | type: 'Query', 3 | fields: [ 4 | { 5 | type: 'FieldReference', 6 | path: ['Id'] 7 | }, 8 | { 9 | type: 'FieldReference', 10 | path: ['SomeNumberField__c'] 11 | } 12 | ], 13 | object: { 14 | type: 'ObjectReference', 15 | name: 'Account' 16 | }, 17 | condition: { 18 | type: 'LogicalCondition', 19 | operator: 'OR', 20 | left: { 21 | type: 'LogicalCondition', 22 | operator: 'OR', 23 | left: { 24 | type: 'LogicalCondition', 25 | operator: 'OR', 26 | left: { 27 | type: 'LogicalCondition', 28 | operator: 'OR', 29 | left: { 30 | type: 'LogicalCondition', 31 | operator: 'OR', 32 | left: { 33 | type: 'LogicalCondition', 34 | operator: 'OR', 35 | left: { 36 | type: 'LogicalCondition', 37 | operator: 'OR', 38 | left: { 39 | type: 'LogicalCondition', 40 | operator: 'OR', 41 | left: { 42 | type: 'LogicalCondition', 43 | operator: 'OR', 44 | left: { 45 | type: 'LogicalCondition', 46 | operator: 'OR', 47 | left: { 48 | type: 'LogicalCondition', 49 | operator: 'OR', 50 | left: { 51 | type: 'LogicalCondition', 52 | operator: 'OR', 53 | left: { 54 | type: 'LogicalCondition', 55 | operator: 'OR', 56 | left: { 57 | type: 'LogicalCondition', 58 | operator: 'OR', 59 | left: { 60 | type: 'LogicalCondition', 61 | operator: 'OR', 62 | left: { 63 | type: 'LogicalCondition', 64 | operator: 'OR', 65 | left: { 66 | type: 'LogicalCondition', 67 | operator: 'OR', 68 | left: { 69 | type: 'LogicalCondition', 70 | operator: 'OR', 71 | left: { 72 | type: 'LogicalCondition', 73 | operator: 'OR', 74 | left: { 75 | type: 'LogicalCondition', 76 | operator: 'OR', 77 | left: { 78 | type: 'LogicalCondition', 79 | operator: 'OR', 80 | left: { 81 | type: 'LogicalCondition', 82 | operator: 'OR', 83 | left: { 84 | type: 'LogicalCondition', 85 | operator: 'OR', 86 | left: { 87 | type: 'LogicalCondition', 88 | operator: 'OR', 89 | left: { 90 | type: 'LogicalCondition', 91 | operator: 'OR', 92 | left: { 93 | type: 'LogicalCondition', 94 | operator: 'OR', 95 | left: { 96 | type: 'LogicalCondition', 97 | operator: 'OR', 98 | left: { 99 | type: 'LogicalCondition', 100 | operator: 'OR', 101 | left: { 102 | type: 'LogicalCondition', 103 | operator: 'OR', 104 | left: { 105 | type: 'LogicalCondition', 106 | operator: 'OR', 107 | left: { 108 | type: 'LogicalCondition', 109 | operator: 'OR', 110 | left: { 111 | type: 'LogicalCondition', 112 | operator: 'OR', 113 | left: { 114 | type: 'LogicalCondition', 115 | operator: 'OR', 116 | left: { 117 | type: 'LogicalCondition', 118 | operator: 'OR', 119 | left: { 120 | type: 'LogicalCondition', 121 | operator: 'OR', 122 | left: { 123 | type: 'LogicalCondition', 124 | operator: 'OR', 125 | left: { 126 | type: 'ComparisonCondition', 127 | field: { 128 | type: 'FieldReference', 129 | path: ['CreadedDate'] 130 | }, 131 | operator: '=', 132 | value: { 133 | type: 'dateLiteral', 134 | value: 'YESTERDAY' 135 | } 136 | }, 137 | right: { 138 | type: 'ComparisonCondition', 139 | field: { 140 | type: 'FieldReference', 141 | path: ['CreadedDate'] 142 | }, 143 | operator: '=', 144 | value: { 145 | type: 'dateLiteral', 146 | value: 'TODAY' 147 | } 148 | } 149 | }, 150 | right: { 151 | type: 'ComparisonCondition', 152 | field: { 153 | type: 'FieldReference', 154 | path: ['CreadedDate'] 155 | }, 156 | operator: '=', 157 | value: { 158 | type: 'dateLiteral', 159 | value: 'TOMORROW' 160 | } 161 | } 162 | }, 163 | right: { 164 | type: 'ComparisonCondition', 165 | field: { 166 | type: 'FieldReference', 167 | path: ['CreadedDate'] 168 | }, 169 | operator: '=', 170 | value: { 171 | type: 'dateLiteral', 172 | value: 'LAST_WEEK' 173 | } 174 | } 175 | }, 176 | right: { 177 | type: 'ComparisonCondition', 178 | field: { 179 | type: 'FieldReference', 180 | path: ['CreadedDate'] 181 | }, 182 | operator: '=', 183 | value: { 184 | type: 'dateLiteral', 185 | value: 'THIS_WEEK' 186 | } 187 | } 188 | }, 189 | right: { 190 | type: 'ComparisonCondition', 191 | field: { 192 | type: 'FieldReference', 193 | path: ['CreadedDate'] 194 | }, 195 | operator: '=', 196 | value: { 197 | type: 'dateLiteral', 198 | value: 'NEXT_WEEK' 199 | } 200 | } 201 | }, 202 | right: { 203 | type: 'ComparisonCondition', 204 | field: { 205 | type: 'FieldReference', 206 | path: ['CreadedDate'] 207 | }, 208 | operator: '=', 209 | value: { 210 | type: 'dateLiteral', 211 | value: 'LAST_MONTH' 212 | } 213 | } 214 | }, 215 | right: { 216 | type: 'ComparisonCondition', 217 | field: { 218 | type: 'FieldReference', 219 | path: ['CreadedDate'] 220 | }, 221 | operator: '=', 222 | value: { 223 | type: 'dateLiteral', 224 | value: 'THIS_MONTH' 225 | } 226 | } 227 | }, 228 | right: { 229 | type: 'ComparisonCondition', 230 | field: { 231 | type: 'FieldReference', 232 | path: ['CreadedDate'] 233 | }, 234 | operator: '=', 235 | value: { 236 | type: 'dateLiteral', 237 | value: 'NEXT_MONTH' 238 | } 239 | } 240 | }, 241 | right: { 242 | type: 'ComparisonCondition', 243 | field: { 244 | type: 'FieldReference', 245 | path: ['CreadedDate'] 246 | }, 247 | operator: '=', 248 | value: { 249 | type: 'dateLiteral', 250 | value: 'LAST_90_DAYS' 251 | } 252 | } 253 | }, 254 | right: { 255 | type: 'ComparisonCondition', 256 | field: { 257 | type: 'FieldReference', 258 | path: ['CreadedDate'] 259 | }, 260 | operator: '=', 261 | value: { 262 | type: 'dateLiteral', 263 | value: 'NEXT_90_DAYS' 264 | } 265 | } 266 | }, 267 | right: { 268 | type: 'ComparisonCondition', 269 | field: { 270 | type: 'FieldReference', 271 | path: ['CreadedDate'] 272 | }, 273 | operator: '=', 274 | value: { 275 | type: 'dateLiteral', 276 | value: 'THIS_QUARTER' 277 | } 278 | } 279 | }, 280 | right: { 281 | type: 'ComparisonCondition', 282 | field: { 283 | type: 'FieldReference', 284 | path: ['CreadedDate'] 285 | }, 286 | operator: '=', 287 | value: { 288 | type: 'dateLiteral', 289 | value: 'LAST_QUARTER' 290 | } 291 | } 292 | }, 293 | right: { 294 | type: 'ComparisonCondition', 295 | field: { 296 | type: 'FieldReference', 297 | path: ['CreadedDate'] 298 | }, 299 | operator: '=', 300 | value: { 301 | type: 'dateLiteral', 302 | value: 'NEXT_QUARTER' 303 | } 304 | } 305 | }, 306 | right: { 307 | type: 'ComparisonCondition', 308 | field: { 309 | type: 'FieldReference', 310 | path: ['CreadedDate'] 311 | }, 312 | operator: '=', 313 | value: { 314 | type: 'dateLiteral', 315 | value: 'THIS_YEAR' 316 | } 317 | } 318 | }, 319 | right: { 320 | type: 'ComparisonCondition', 321 | field: { 322 | type: 'FieldReference', 323 | path: ['CreadedDate'] 324 | }, 325 | operator: '=', 326 | value: { 327 | type: 'dateLiteral', 328 | value: 'LAST_YEAR' 329 | } 330 | } 331 | }, 332 | right: { 333 | type: 'ComparisonCondition', 334 | field: { 335 | type: 'FieldReference', 336 | path: ['CreadedDate'] 337 | }, 338 | operator: '=', 339 | value: { 340 | type: 'dateLiteral', 341 | value: 'NEXT_YEAR' 342 | } 343 | } 344 | }, 345 | right: { 346 | type: 'ComparisonCondition', 347 | field: { 348 | type: 'FieldReference', 349 | path: ['CreadedDate'] 350 | }, 351 | operator: '=', 352 | value: { 353 | type: 'dateLiteral', 354 | value: 'THIS_FISCAL_QUARTER' 355 | } 356 | } 357 | }, 358 | right: { 359 | type: 'ComparisonCondition', 360 | field: { 361 | type: 'FieldReference', 362 | path: ['CreadedDate'] 363 | }, 364 | operator: '=', 365 | value: { 366 | type: 'dateLiteral', 367 | value: 'LAST_FISCAL_QUARTER' 368 | } 369 | } 370 | }, 371 | right: { 372 | type: 'ComparisonCondition', 373 | field: { 374 | type: 'FieldReference', 375 | path: ['CreadedDate'] 376 | }, 377 | operator: '=', 378 | value: { 379 | type: 'dateLiteral', 380 | value: 'NEXT_FISCAL_QUARTER' 381 | } 382 | } 383 | }, 384 | right: { 385 | type: 'ComparisonCondition', 386 | field: { 387 | type: 'FieldReference', 388 | path: ['CreadedDate'] 389 | }, 390 | operator: '=', 391 | value: { 392 | type: 'dateLiteral', 393 | value: 'THIS_FISCAL_YEAR' 394 | } 395 | } 396 | }, 397 | right: { 398 | type: 'ComparisonCondition', 399 | field: { 400 | type: 'FieldReference', 401 | path: ['CreadedDate'] 402 | }, 403 | operator: '=', 404 | value: { 405 | type: 'dateLiteral', 406 | value: 'LAST_FISCAL_YEAR' 407 | } 408 | } 409 | }, 410 | right: { 411 | type: 'ComparisonCondition', 412 | field: { 413 | type: 'FieldReference', 414 | path: ['CreadedDate'] 415 | }, 416 | operator: '=', 417 | value: { 418 | type: 'dateLiteral', 419 | value: 'NEXT_FISCAL_YEAR' 420 | } 421 | } 422 | }, 423 | right: { 424 | type: 'ComparisonCondition', 425 | field: { 426 | type: 'FieldReference', 427 | path: ['CreadedDate'] 428 | }, 429 | operator: '=', 430 | value: { 431 | type: 'dateLiteral', 432 | value: 'LAST_N_DAYS', 433 | argument: 1 434 | } 435 | } 436 | }, 437 | right: { 438 | type: 'ComparisonCondition', 439 | field: { 440 | type: 'FieldReference', 441 | path: ['CreadedDate'] 442 | }, 443 | operator: '=', 444 | value: { 445 | type: 'dateLiteral', 446 | value: 'NEXT_N_DAYS', 447 | argument: 1 448 | } 449 | } 450 | }, 451 | right: { 452 | type: 'ComparisonCondition', 453 | field: { 454 | type: 'FieldReference', 455 | path: ['CreadedDate'] 456 | }, 457 | operator: '=', 458 | value: { 459 | type: 'dateLiteral', 460 | value: 'NEXT_N_WEEKS', 461 | argument: 1 462 | } 463 | } 464 | }, 465 | right: { 466 | type: 'ComparisonCondition', 467 | field: { 468 | type: 'FieldReference', 469 | path: ['CreadedDate'] 470 | }, 471 | operator: '=', 472 | value: { 473 | type: 'dateLiteral', 474 | value: 'LAST_N_WEEKS', 475 | argument: 1 476 | } 477 | } 478 | }, 479 | right: { 480 | type: 'ComparisonCondition', 481 | field: { 482 | type: 'FieldReference', 483 | path: ['CreadedDate'] 484 | }, 485 | operator: '=', 486 | value: { 487 | type: 'dateLiteral', 488 | value: 'NEXT_N_MONTHS', 489 | argument: 1 490 | } 491 | } 492 | }, 493 | right: { 494 | type: 'ComparisonCondition', 495 | field: { 496 | type: 'FieldReference', 497 | path: ['CreadedDate'] 498 | }, 499 | operator: '=', 500 | value: { 501 | type: 'dateLiteral', 502 | value: 'LAST_N_MONTHS', 503 | argument: 1 504 | } 505 | } 506 | }, 507 | right: { 508 | type: 'ComparisonCondition', 509 | field: { 510 | type: 'FieldReference', 511 | path: ['CreadedDate'] 512 | }, 513 | operator: '=', 514 | value: { 515 | type: 'dateLiteral', 516 | value: 'NEXT_N_QUARTERS', 517 | argument: 1 518 | } 519 | } 520 | }, 521 | right: { 522 | type: 'ComparisonCondition', 523 | field: { 524 | type: 'FieldReference', 525 | path: ['CreadedDate'] 526 | }, 527 | operator: '=', 528 | value: { 529 | type: 'dateLiteral', 530 | value: 'LAST_N_QUARTERS', 531 | argument: 1 532 | } 533 | } 534 | }, 535 | right: { 536 | type: 'ComparisonCondition', 537 | field: { 538 | type: 'FieldReference', 539 | path: ['CreadedDate'] 540 | }, 541 | operator: '=', 542 | value: { 543 | type: 'dateLiteral', 544 | value: 'NEXT_N_YEARS', 545 | argument: 1 546 | } 547 | } 548 | }, 549 | right: { 550 | type: 'ComparisonCondition', 551 | field: { 552 | type: 'FieldReference', 553 | path: ['CreadedDate'] 554 | }, 555 | operator: '=', 556 | value: { 557 | type: 'dateLiteral', 558 | value: 'LAST_N_YEARS', 559 | argument: 1 560 | } 561 | } 562 | }, 563 | right: { 564 | type: 'ComparisonCondition', 565 | field: { 566 | type: 'FieldReference', 567 | path: ['CreadedDate'] 568 | }, 569 | operator: '=', 570 | value: { 571 | type: 'dateLiteral', 572 | value: 'NEXT_N_FISCAL_QUARTERS', 573 | argument: 1 574 | } 575 | } 576 | }, 577 | right: { 578 | type: 'ComparisonCondition', 579 | field: { 580 | type: 'FieldReference', 581 | path: ['CreadedDate'] 582 | }, 583 | operator: '=', 584 | value: { 585 | type: 'dateLiteral', 586 | value: 'LAST_N_FISCAL_QUARTERS', 587 | argument: 1 588 | } 589 | } 590 | }, 591 | right: { 592 | type: 'ComparisonCondition', 593 | field: { 594 | type: 'FieldReference', 595 | path: ['CreadedDate'] 596 | }, 597 | operator: '=', 598 | value: { 599 | type: 'dateLiteral', 600 | value: 'NEXT_N_FISCAL_YEARS', 601 | argument: 1 602 | } 603 | } 604 | }, 605 | right: { 606 | type: 'ComparisonCondition', 607 | field: { 608 | type: 'FieldReference', 609 | path: ['CreadedDate'] 610 | }, 611 | operator: '=', 612 | value: { 613 | type: 'dateLiteral', 614 | value: 'LAST_N_FISCAL_YEARS', 615 | argument: 1 616 | } 617 | } 618 | } 619 | } -------------------------------------------------------------------------------- /test/data/expected/41-date-literal-04.json5: -------------------------------------------------------------------------------- 1 | { 2 | type: 'Query', 3 | fields: [ 4 | { 5 | type: 'FieldReference', 6 | path: ['Id'] 7 | }, 8 | { 9 | type: 'FieldReference', 10 | path: ['SomeNumberField__c'] 11 | } 12 | ], 13 | object: { 14 | type: 'ObjectReference', 15 | name: 'Account' 16 | }, 17 | condition: { 18 | type: 'LogicalCondition', 19 | operator: 'OR', 20 | left: { 21 | type: 'LogicalCondition', 22 | operator: 'OR', 23 | left: { 24 | type: 'LogicalCondition', 25 | operator: 'OR', 26 | left: { 27 | type: 'LogicalCondition', 28 | operator: 'OR', 29 | left: { 30 | type: 'LogicalCondition', 31 | operator: 'OR', 32 | left: { 33 | type: 'LogicalCondition', 34 | operator: 'OR', 35 | left: { 36 | type: 'LogicalCondition', 37 | operator: 'OR', 38 | left: { 39 | type: 'LogicalCondition', 40 | operator: 'OR', 41 | left: { 42 | type: 'LogicalCondition', 43 | operator: 'OR', 44 | left: { 45 | type: 'LogicalCondition', 46 | operator: 'OR', 47 | left: { 48 | type: 'LogicalCondition', 49 | operator: 'OR', 50 | left: { 51 | type: 'LogicalCondition', 52 | operator: 'OR', 53 | left: { 54 | type: 'LogicalCondition', 55 | operator: 'OR', 56 | left: { 57 | type: 'LogicalCondition', 58 | operator: 'OR', 59 | left: { 60 | type: 'LogicalCondition', 61 | operator: 'OR', 62 | left: { 63 | type: 'LogicalCondition', 64 | operator: 'OR', 65 | left: { 66 | type: 'LogicalCondition', 67 | operator: 'OR', 68 | left: { 69 | type: 'LogicalCondition', 70 | operator: 'OR', 71 | left: { 72 | type: 'LogicalCondition', 73 | operator: 'OR', 74 | left: { 75 | type: 'LogicalCondition', 76 | operator: 'OR', 77 | left: { 78 | type: 'LogicalCondition', 79 | operator: 'OR', 80 | left: { 81 | type: 'LogicalCondition', 82 | operator: 'OR', 83 | left: { 84 | type: 'LogicalCondition', 85 | operator: 'OR', 86 | left: { 87 | type: 'LogicalCondition', 88 | operator: 'OR', 89 | left: { 90 | type: 'LogicalCondition', 91 | operator: 'OR', 92 | left: { 93 | type: 'LogicalCondition', 94 | operator: 'OR', 95 | left: { 96 | type: 'LogicalCondition', 97 | operator: 'OR', 98 | left: { 99 | type: 'LogicalCondition', 100 | operator: 'OR', 101 | left: { 102 | type: 'LogicalCondition', 103 | operator: 'OR', 104 | left: { 105 | type: 'LogicalCondition', 106 | operator: 'OR', 107 | left: { 108 | type: 'LogicalCondition', 109 | operator: 'OR', 110 | left: { 111 | type: 'LogicalCondition', 112 | operator: 'OR', 113 | left: { 114 | type: 'LogicalCondition', 115 | operator: 'OR', 116 | left: { 117 | type: 'LogicalCondition', 118 | operator: 'OR', 119 | left: { 120 | type: 'LogicalCondition', 121 | operator: 'OR', 122 | left: { 123 | type: 'LogicalCondition', 124 | operator: 'OR', 125 | left: { 126 | type: 'ComparisonCondition', 127 | field: { 128 | type: 'FieldReference', 129 | path: ['CreadedDate'] 130 | }, 131 | operator: '=', 132 | value: { 133 | type: 'dateLiteral', 134 | value: 'YESTERDAY' 135 | } 136 | }, 137 | right: { 138 | type: 'ComparisonCondition', 139 | field: { 140 | type: 'FieldReference', 141 | path: ['CreadedDate'] 142 | }, 143 | operator: '=', 144 | value: { 145 | type: 'dateLiteral', 146 | value: 'TODAY' 147 | } 148 | } 149 | }, 150 | right: { 151 | type: 'ComparisonCondition', 152 | field: { 153 | type: 'FieldReference', 154 | path: ['CreadedDate'] 155 | }, 156 | operator: '=', 157 | value: { 158 | type: 'dateLiteral', 159 | value: 'TOMORROW' 160 | } 161 | } 162 | }, 163 | right: { 164 | type: 'ComparisonCondition', 165 | field: { 166 | type: 'FieldReference', 167 | path: ['CreadedDate'] 168 | }, 169 | operator: '=', 170 | value: { 171 | type: 'dateLiteral', 172 | value: 'LAST_WEEK' 173 | } 174 | } 175 | }, 176 | right: { 177 | type: 'ComparisonCondition', 178 | field: { 179 | type: 'FieldReference', 180 | path: ['CreadedDate'] 181 | }, 182 | operator: '=', 183 | value: { 184 | type: 'dateLiteral', 185 | value: 'THIS_WEEK' 186 | } 187 | } 188 | }, 189 | right: { 190 | type: 'ComparisonCondition', 191 | field: { 192 | type: 'FieldReference', 193 | path: ['CreadedDate'] 194 | }, 195 | operator: '=', 196 | value: { 197 | type: 'dateLiteral', 198 | value: 'NEXT_WEEK' 199 | } 200 | } 201 | }, 202 | right: { 203 | type: 'ComparisonCondition', 204 | field: { 205 | type: 'FieldReference', 206 | path: ['CreadedDate'] 207 | }, 208 | operator: '=', 209 | value: { 210 | type: 'dateLiteral', 211 | value: 'LAST_MONTH' 212 | } 213 | } 214 | }, 215 | right: { 216 | type: 'ComparisonCondition', 217 | field: { 218 | type: 'FieldReference', 219 | path: ['CreadedDate'] 220 | }, 221 | operator: '=', 222 | value: { 223 | type: 'dateLiteral', 224 | value: 'THIS_MONTH' 225 | } 226 | } 227 | }, 228 | right: { 229 | type: 'ComparisonCondition', 230 | field: { 231 | type: 'FieldReference', 232 | path: ['CreadedDate'] 233 | }, 234 | operator: '=', 235 | value: { 236 | type: 'dateLiteral', 237 | value: 'NEXT_MONTH' 238 | } 239 | } 240 | }, 241 | right: { 242 | type: 'ComparisonCondition', 243 | field: { 244 | type: 'FieldReference', 245 | path: ['CreadedDate'] 246 | }, 247 | operator: '=', 248 | value: { 249 | type: 'dateLiteral', 250 | value: 'LAST_90_DAYS' 251 | } 252 | } 253 | }, 254 | right: { 255 | type: 'ComparisonCondition', 256 | field: { 257 | type: 'FieldReference', 258 | path: ['CreadedDate'] 259 | }, 260 | operator: '=', 261 | value: { 262 | type: 'dateLiteral', 263 | value: 'NEXT_90_DAYS' 264 | } 265 | } 266 | }, 267 | right: { 268 | type: 'ComparisonCondition', 269 | field: { 270 | type: 'FieldReference', 271 | path: ['CreadedDate'] 272 | }, 273 | operator: '=', 274 | value: { 275 | type: 'dateLiteral', 276 | value: 'THIS_QUARTER' 277 | } 278 | } 279 | }, 280 | right: { 281 | type: 'ComparisonCondition', 282 | field: { 283 | type: 'FieldReference', 284 | path: ['CreadedDate'] 285 | }, 286 | operator: '=', 287 | value: { 288 | type: 'dateLiteral', 289 | value: 'LAST_QUARTER' 290 | } 291 | } 292 | }, 293 | right: { 294 | type: 'ComparisonCondition', 295 | field: { 296 | type: 'FieldReference', 297 | path: ['CreadedDate'] 298 | }, 299 | operator: '=', 300 | value: { 301 | type: 'dateLiteral', 302 | value: 'NEXT_QUARTER' 303 | } 304 | } 305 | }, 306 | right: { 307 | type: 'ComparisonCondition', 308 | field: { 309 | type: 'FieldReference', 310 | path: ['CreadedDate'] 311 | }, 312 | operator: '=', 313 | value: { 314 | type: 'dateLiteral', 315 | value: 'THIS_YEAR' 316 | } 317 | } 318 | }, 319 | right: { 320 | type: 'ComparisonCondition', 321 | field: { 322 | type: 'FieldReference', 323 | path: ['CreadedDate'] 324 | }, 325 | operator: '=', 326 | value: { 327 | type: 'dateLiteral', 328 | value: 'LAST_YEAR' 329 | } 330 | } 331 | }, 332 | right: { 333 | type: 'ComparisonCondition', 334 | field: { 335 | type: 'FieldReference', 336 | path: ['CreadedDate'] 337 | }, 338 | operator: '=', 339 | value: { 340 | type: 'dateLiteral', 341 | value: 'NEXT_YEAR' 342 | } 343 | } 344 | }, 345 | right: { 346 | type: 'ComparisonCondition', 347 | field: { 348 | type: 'FieldReference', 349 | path: ['CreadedDate'] 350 | }, 351 | operator: '=', 352 | value: { 353 | type: 'dateLiteral', 354 | value: 'THIS_FISCAL_QUARTER' 355 | } 356 | } 357 | }, 358 | right: { 359 | type: 'ComparisonCondition', 360 | field: { 361 | type: 'FieldReference', 362 | path: ['CreadedDate'] 363 | }, 364 | operator: '=', 365 | value: { 366 | type: 'dateLiteral', 367 | value: 'LAST_FISCAL_QUARTER' 368 | } 369 | } 370 | }, 371 | right: { 372 | type: 'ComparisonCondition', 373 | field: { 374 | type: 'FieldReference', 375 | path: ['CreadedDate'] 376 | }, 377 | operator: '=', 378 | value: { 379 | type: 'dateLiteral', 380 | value: 'NEXT_FISCAL_QUARTER' 381 | } 382 | } 383 | }, 384 | right: { 385 | type: 'ComparisonCondition', 386 | field: { 387 | type: 'FieldReference', 388 | path: ['CreadedDate'] 389 | }, 390 | operator: '=', 391 | value: { 392 | type: 'dateLiteral', 393 | value: 'THIS_FISCAL_YEAR' 394 | } 395 | } 396 | }, 397 | right: { 398 | type: 'ComparisonCondition', 399 | field: { 400 | type: 'FieldReference', 401 | path: ['CreadedDate'] 402 | }, 403 | operator: '=', 404 | value: { 405 | type: 'dateLiteral', 406 | value: 'LAST_FISCAL_YEAR' 407 | } 408 | } 409 | }, 410 | right: { 411 | type: 'ComparisonCondition', 412 | field: { 413 | type: 'FieldReference', 414 | path: ['CreadedDate'] 415 | }, 416 | operator: '=', 417 | value: { 418 | type: 'dateLiteral', 419 | value: 'NEXT_FISCAL_YEAR' 420 | } 421 | } 422 | }, 423 | right: { 424 | type: 'ComparisonCondition', 425 | field: { 426 | type: 'FieldReference', 427 | path: ['CreadedDate'] 428 | }, 429 | operator: '=', 430 | value: { 431 | type: 'dateLiteral', 432 | value: 'LAST_N_DAYS', 433 | argument: 12 434 | } 435 | } 436 | }, 437 | right: { 438 | type: 'ComparisonCondition', 439 | field: { 440 | type: 'FieldReference', 441 | path: ['CreadedDate'] 442 | }, 443 | operator: '=', 444 | value: { 445 | type: 'dateLiteral', 446 | value: 'NEXT_N_DAYS', 447 | argument: 12 448 | } 449 | } 450 | }, 451 | right: { 452 | type: 'ComparisonCondition', 453 | field: { 454 | type: 'FieldReference', 455 | path: ['CreadedDate'] 456 | }, 457 | operator: '=', 458 | value: { 459 | type: 'dateLiteral', 460 | value: 'NEXT_N_WEEKS', 461 | argument: 12 462 | } 463 | } 464 | }, 465 | right: { 466 | type: 'ComparisonCondition', 467 | field: { 468 | type: 'FieldReference', 469 | path: ['CreadedDate'] 470 | }, 471 | operator: '=', 472 | value: { 473 | type: 'dateLiteral', 474 | value: 'LAST_N_WEEKS', 475 | argument: 12 476 | } 477 | } 478 | }, 479 | right: { 480 | type: 'ComparisonCondition', 481 | field: { 482 | type: 'FieldReference', 483 | path: ['CreadedDate'] 484 | }, 485 | operator: '=', 486 | value: { 487 | type: 'dateLiteral', 488 | value: 'NEXT_N_MONTHS', 489 | argument: 12 490 | } 491 | } 492 | }, 493 | right: { 494 | type: 'ComparisonCondition', 495 | field: { 496 | type: 'FieldReference', 497 | path: ['CreadedDate'] 498 | }, 499 | operator: '=', 500 | value: { 501 | type: 'dateLiteral', 502 | value: 'LAST_N_MONTHS', 503 | argument: 12 504 | } 505 | } 506 | }, 507 | right: { 508 | type: 'ComparisonCondition', 509 | field: { 510 | type: 'FieldReference', 511 | path: ['CreadedDate'] 512 | }, 513 | operator: '=', 514 | value: { 515 | type: 'dateLiteral', 516 | value: 'NEXT_N_QUARTERS', 517 | argument: 12 518 | } 519 | } 520 | }, 521 | right: { 522 | type: 'ComparisonCondition', 523 | field: { 524 | type: 'FieldReference', 525 | path: ['CreadedDate'] 526 | }, 527 | operator: '=', 528 | value: { 529 | type: 'dateLiteral', 530 | value: 'LAST_N_QUARTERS', 531 | argument: 12 532 | } 533 | } 534 | }, 535 | right: { 536 | type: 'ComparisonCondition', 537 | field: { 538 | type: 'FieldReference', 539 | path: ['CreadedDate'] 540 | }, 541 | operator: '=', 542 | value: { 543 | type: 'dateLiteral', 544 | value: 'NEXT_N_YEARS', 545 | argument: 12 546 | } 547 | } 548 | }, 549 | right: { 550 | type: 'ComparisonCondition', 551 | field: { 552 | type: 'FieldReference', 553 | path: ['CreadedDate'] 554 | }, 555 | operator: '=', 556 | value: { 557 | type: 'dateLiteral', 558 | value: 'LAST_N_YEARS', 559 | argument: 12 560 | } 561 | } 562 | }, 563 | right: { 564 | type: 'ComparisonCondition', 565 | field: { 566 | type: 'FieldReference', 567 | path: ['CreadedDate'] 568 | }, 569 | operator: '=', 570 | value: { 571 | type: 'dateLiteral', 572 | value: 'NEXT_N_FISCAL_QUARTERS', 573 | argument: 12 574 | } 575 | } 576 | }, 577 | right: { 578 | type: 'ComparisonCondition', 579 | field: { 580 | type: 'FieldReference', 581 | path: ['CreadedDate'] 582 | }, 583 | operator: '=', 584 | value: { 585 | type: 'dateLiteral', 586 | value: 'LAST_N_FISCAL_QUARTERS', 587 | argument: 12 588 | } 589 | } 590 | }, 591 | right: { 592 | type: 'ComparisonCondition', 593 | field: { 594 | type: 'FieldReference', 595 | path: ['CreadedDate'] 596 | }, 597 | operator: '=', 598 | value: { 599 | type: 'dateLiteral', 600 | value: 'NEXT_N_FISCAL_YEARS', 601 | argument: 12 602 | } 603 | } 604 | }, 605 | right: { 606 | type: 'ComparisonCondition', 607 | field: { 608 | type: 'FieldReference', 609 | path: ['CreadedDate'] 610 | }, 611 | operator: '=', 612 | value: { 613 | type: 'dateLiteral', 614 | value: 'LAST_N_FISCAL_YEARS', 615 | argument: 12 616 | } 617 | } 618 | } 619 | } -------------------------------------------------------------------------------- /test/data/expected/50-dates-01.json5: -------------------------------------------------------------------------------- 1 | { 2 | type: 'Query', 3 | fields: [ 4 | { 5 | type: 'FieldReference', 6 | path: ['Id'] 7 | }, 8 | { 9 | type: 'FieldReference', 10 | path: ['SomeNumberField__c'] 11 | } 12 | ], 13 | object: { 14 | type: 'ObjectReference', 15 | name: 'Account' 16 | }, 17 | condition: { 18 | type: 'LogicalCondition', 19 | operator: 'OR', 20 | left: { 21 | type: 'LogicalCondition', 22 | operator: 'OR', 23 | left: { 24 | type: 'LogicalCondition', 25 | operator: 'OR', 26 | left: { 27 | type: 'LogicalCondition', 28 | operator: 'OR', 29 | left: { 30 | type: 'ComparisonCondition', 31 | field: { 32 | type: 'FieldReference', 33 | path: ['CreatedDate'] 34 | }, 35 | operator: '<', 36 | value: { 37 | type: 'datetime', 38 | value: '1999-01-01T23:01:01+01:00' 39 | } 40 | }, 41 | right: { 42 | type: 'ComparisonCondition', 43 | field: { 44 | type: 'FieldReference', 45 | path: ['CreatedDate'] 46 | }, 47 | operator: '<=', 48 | value: { 49 | type: 'datetime', 50 | value: '1999-01-01T23:01:01-08:00' 51 | } 52 | } 53 | }, 54 | right: { 55 | type: 'ComparisonCondition', 56 | field: { 57 | type: 'FieldReference', 58 | path: ['CreatedDate'] 59 | }, 60 | operator: '>', 61 | value: { 62 | type: 'datetime', 63 | value: '1999-01-01T23:01:01Z' 64 | } 65 | } 66 | }, 67 | right: { 68 | type: 'ComparisonCondition', 69 | field: { 70 | type: 'FieldReference', 71 | path: ['CreatedDate'] 72 | }, 73 | operator: '>=', 74 | value: { 75 | type: 'date', 76 | value: '1999-01-01' 77 | } 78 | } 79 | }, 80 | right: { 81 | type: 'ComparisonCondition', 82 | field: { 83 | type: 'FieldReference', 84 | path: [ 85 | 'CreatedDate' 86 | ] 87 | }, 88 | operator: '=', 89 | value: { 90 | type: 'datetime', 91 | value: '1999-01-01T23:01:01+01:00' 92 | } 93 | } 94 | } 95 | } -------------------------------------------------------------------------------- /test/data/expected/90-complex-01.json5: -------------------------------------------------------------------------------- 1 | { 2 | type: 'Query', 3 | fields: [{ 4 | type: 'FieldReference', 5 | path: ['Id'], 6 | }, { 7 | type: 'FieldReference', 8 | path: ['Name'], 9 | }, { 10 | type: 'FieldReference', 11 | path: ['Owner', 'Id'], 12 | }, { 13 | type: 'FunctionCall', 14 | name: 'toLabel', 15 | arguments: [{ 16 | type: 'FieldReference', 17 | path: ['StageName'], 18 | }], 19 | }, { 20 | type: 'Query', 21 | fields: [{ 22 | type: 'FieldReference', 23 | path: ['Id'], 24 | }, { 25 | type: 'FieldReference', 26 | path: ['Contact', 'Id'], 27 | }, { 28 | type: 'FieldReference', 29 | path: ['Contact', 'Name'], 30 | }, { 31 | type: 'FunctionCall', 32 | name: 'format', 33 | arguments: [{ 34 | type: 'FieldReference', 35 | path: ['Contact', 'Account', 'NumberOfEmployees'], 36 | }], 37 | }], 38 | object: { 39 | type: 'ObjectReference', 40 | name: 'OpportunityContactRoles', 41 | }, 42 | limit: { 43 | type: 'number', 44 | value: 10, 45 | }, 46 | }], 47 | object: { 48 | type: 'ObjectReference', 49 | name: 'Opportunity', 50 | }, 51 | scope: 'Mine', 52 | condition: { 53 | type: 'LogicalCondition', 54 | operator: 'OR', 55 | left: { 56 | type: 'LogicalCondition', 57 | operator: 'OR', 58 | left: { 59 | type: 'ComparisonCondition', 60 | operator: 'LIKE', 61 | field: { 62 | type: 'FieldReference', 63 | path: ['Account', 'Name'], 64 | }, 65 | value: { 66 | type: 'string', 67 | value: "O'reilly%", 68 | }, 69 | }, 70 | right: { 71 | type: 'LogicalCondition', 72 | operator: 'AND', 73 | left: { 74 | type: 'NegateCondition', 75 | operator: 'NOT', 76 | condition: { 77 | type: 'LogicalCondition', 78 | operator: 'AND', 79 | left: { 80 | type: 'ComparisonCondition', 81 | operator: '=', 82 | field: { 83 | type: 'FunctionCall', 84 | name: 'CALENDAR_MONTH', 85 | arguments: [{ 86 | type: 'FieldReference', 87 | path: ['CreatedDate'], 88 | }], 89 | }, 90 | value: { 91 | type: 'number', 92 | value: 10, 93 | }, 94 | }, 95 | right: { 96 | type: 'ComparisonCondition', 97 | operator: '>', 98 | field: { 99 | type: 'FieldReference', 100 | path: ['CreatedDate'], 101 | }, 102 | value: { 103 | type: 'date', 104 | value: '2015-12-31', 105 | }, 106 | }, 107 | parentheses: true, 108 | }, 109 | parentheses: true, 110 | }, 111 | right: { 112 | type: 'ComparisonCondition', 113 | operator: '=', 114 | field: { 115 | type: 'FieldReference', 116 | path: ['Owner', 'Name'], 117 | }, 118 | value: { 119 | type: 'string', 120 | value: 'Hello', 121 | }, 122 | } 123 | }, 124 | }, 125 | right: { 126 | type: 'ComparisonCondition', 127 | operator: '>', 128 | field: { 129 | type: 'FieldReference', 130 | path: ['Probability'], 131 | }, 132 | value: { 133 | type: 'number', 134 | value: 99.5, 135 | }, 136 | }, 137 | }, 138 | sort: [{ 139 | field: { 140 | type: 'FieldReference', 141 | path: ['Account', 'Type'], 142 | }, 143 | direction: 'DESC', 144 | nullOrder: 'LAST', 145 | }, { 146 | field: { 147 | type: 'FieldReference', 148 | path: ['CreatedDate'], 149 | }, 150 | }], 151 | limit: { 152 | type: 'number', 153 | value: 100, 154 | }, 155 | offset: { 156 | type: 'number', 157 | value: 200, 158 | }, 159 | } 160 | -------------------------------------------------------------------------------- /test/data/parsable/00-simple-01.soql: -------------------------------------------------------------------------------- 1 | SELECT Name FROM Account 2 | -------------------------------------------------------------------------------- /test/data/parsable/01-simple-02.soql: -------------------------------------------------------------------------------- 1 | Select Name froM Account 2 | -------------------------------------------------------------------------------- /test/data/parsable/02-select-clause-01.soql: -------------------------------------------------------------------------------- 1 | SELECT Id, Name, Owner.Name 2 | FROM Account 3 | -------------------------------------------------------------------------------- /test/data/parsable/03-select-clause-02.soql: -------------------------------------------------------------------------------- 1 | SELECT 2 | Id, Name, 3 | toLabel(Account.Type), 4 | convertCurrency(Amount) 5 | FROM Opportunity 6 | -------------------------------------------------------------------------------- /test/data/parsable/04-select-clause-03.soql: -------------------------------------------------------------------------------- 1 | SELECT Id, Name, (SELECT Id, Name From Contacts) 2 | FROM Account 3 | -------------------------------------------------------------------------------- /test/data/parsable/05-from-object-with-alias-01.soql: -------------------------------------------------------------------------------- 1 | SELECT Id, Name, Amount, a.Id, a.Name 2 | FROM Opportunity, Opportunity.Account AS a 3 | -------------------------------------------------------------------------------- /test/data/parsable/06-from-object-with-alias-02.soql: -------------------------------------------------------------------------------- 1 | SELECT o.Id, o.Name, o.Amount, a.Id, a.Name, u.Username 2 | FROM Opportunity o, o.Account a, a.Owner u 3 | -------------------------------------------------------------------------------- /test/data/parsable/07-from-object-group-03.soql: -------------------------------------------------------------------------------- 1 | SELECT Name, COUNT(Id) c 2 | FROM Group 3 | WHERE Type = 'Queue' 4 | GROUP BY Name 5 | ORDER BY Name ASC -------------------------------------------------------------------------------- /test/data/parsable/08-from-object-order-04.soql: -------------------------------------------------------------------------------- 1 | SELECT Name, COUNT(Id) c 2 | FROM Order 3 | WHERE Type = 'Standard' 4 | GROUP BY Name 5 | ORDER BY Name ASC -------------------------------------------------------------------------------- /test/data/parsable/10-where-clause-01.soql: -------------------------------------------------------------------------------- 1 | SELECT Id, Name FROM Account 2 | WHERE Name='Apple' 3 | -------------------------------------------------------------------------------- /test/data/parsable/11-where-clause-02.soql: -------------------------------------------------------------------------------- 1 | SELECT Id, Name FROM Account 2 | WHERE Name='Apple' AND Owner.Active = true 3 | -------------------------------------------------------------------------------- /test/data/parsable/12-where-clause-03.soql: -------------------------------------------------------------------------------- 1 | SELECT Id, Name FROM Account 2 | WHERE Name LIKE 'A%' 3 | OR Type = 'Partner' 4 | AND Owner.Username!= 'user01@example.com' 5 | -------------------------------------------------------------------------------- /test/data/parsable/13-where-clause-04.soql: -------------------------------------------------------------------------------- 1 | SELECT Id, Name FROM Account 2 | WHERE (Name LIKE 'A%' OR Type = 'Partner') 3 | AND Owner.Username!= 'user01@example.com' 4 | -------------------------------------------------------------------------------- /test/data/parsable/14-where-clause-05.soql: -------------------------------------------------------------------------------- 1 | SELECT Id, Name FROM Account 2 | WHERE Type IN ('Partner', 'Customer') 3 | OR ( 4 | CALENDAR_YEAR(CreatedDate) IN (2016, 2017) 5 | AND CALENDAR_MONTH(CreatedDate) NOT IN (2, 4, 6, 9, 11) 6 | ) 7 | -------------------------------------------------------------------------------- /test/data/parsable/15-where-clause-06.soql: -------------------------------------------------------------------------------- 1 | SELECT Id, SomeNumberField__c FROM Account 2 | WHERE SomeNumberField__c <= 10 3 | -------------------------------------------------------------------------------- /test/data/parsable/16-where-clause-07.soql: -------------------------------------------------------------------------------- 1 | SELECT Id, SomeNumberField__c FROM Account 2 | WHERE CreatedDate < 1999-01-01T23:01:01+01:00 3 | OR CreatedDate <= 1999-01-01T23:01:01-08:00 4 | OR CreatedDate >= 1999-01-01T23:01:01Z 5 | OR CreatedDate <= 1999-01-01 6 | OR CreatedDate = 1999-01-01T23:01:01+01:00 7 | -------------------------------------------------------------------------------- /test/data/parsable/20-order-by-clause-01.soql: -------------------------------------------------------------------------------- 1 | SELECT Id, Name FROM Account 2 | ORDER BY CreatedDate 3 | -------------------------------------------------------------------------------- /test/data/parsable/21-order-by-clause-02.soql: -------------------------------------------------------------------------------- 1 | SELECT Id, Name FROM Account 2 | ORDER BY Type ASC NULLS LAST, CreatedDate DESC 3 | -------------------------------------------------------------------------------- /test/data/parsable/30-group-by-clause-01.soql: -------------------------------------------------------------------------------- 1 | SELECT count(Id), Type FROM Account 2 | GROUP BY Type 3 | -------------------------------------------------------------------------------- /test/data/parsable/31-group-by-clause-02.soql: -------------------------------------------------------------------------------- 1 | SELECT count(Id) Cnt, Type, CALENDAR_YEAR(CreatedDate)Year FROM Account 2 | GROUP BY Type, CALENDAR_YEAR(CreatedDate) 3 | -------------------------------------------------------------------------------- /test/data/parsable/32-group-by-clause-03.soql: -------------------------------------------------------------------------------- 1 | SELECT count(Id) Cnt, Type, CALENDAR_YEAR(CreatedDate) Year FROM Account 2 | GROUP BY Type, CALENDAR_YEAR(CreatedDate) 3 | ORDER BY Type, count(Id) ASC 4 | -------------------------------------------------------------------------------- /test/data/parsable/40-date-literal-01.soql: -------------------------------------------------------------------------------- 1 | SELECT Id, Name, CreatedDate 2 | FROM Account 3 | WHERE CreatedDate = TODAY -------------------------------------------------------------------------------- /test/data/parsable/41-date-literal-02.soql: -------------------------------------------------------------------------------- 1 | SELECT Id, Name, CreatedDate 2 | FROM Account 3 | WHERE CreatedDate >= LAST_N_DAYS:1 -------------------------------------------------------------------------------- /test/data/parsable/41-date-literal-03.soql: -------------------------------------------------------------------------------- 1 | SELECT Id, SomeNumberField__c 2 | FROM Account 3 | WHERE CreadedDate = YESTERDAY 4 | OR CreadedDate = TODAY 5 | OR CreadedDate = TOMORROW 6 | OR CreadedDate = LAST_WEEK 7 | OR CreadedDate = THIS_WEEK 8 | OR CreadedDate = NEXT_WEEK 9 | OR CreadedDate = LAST_MONTH 10 | OR CreadedDate = THIS_MONTH 11 | OR CreadedDate = NEXT_MONTH 12 | OR CreadedDate = LAST_90_DAYS 13 | OR CreadedDate = NEXT_90_DAYS 14 | OR CreadedDate = THIS_QUARTER 15 | OR CreadedDate = LAST_QUARTER 16 | OR CreadedDate = NEXT_QUARTER 17 | OR CreadedDate = THIS_YEAR 18 | OR CreadedDate = LAST_YEAR 19 | OR CreadedDate = NEXT_YEAR 20 | OR CreadedDate = THIS_FISCAL_QUARTER 21 | OR CreadedDate = LAST_FISCAL_QUARTER 22 | OR CreadedDate = NEXT_FISCAL_QUARTER 23 | OR CreadedDate = THIS_FISCAL_YEAR 24 | OR CreadedDate = LAST_FISCAL_YEAR 25 | OR CreadedDate = NEXT_FISCAL_YEAR 26 | OR CreadedDate = LAST_N_DAYS:1 27 | OR CreadedDate = NEXT_N_DAYS:1 28 | OR CreadedDate = NEXT_N_WEEKS:1 29 | OR CreadedDate = LAST_N_WEEKS:1 30 | OR CreadedDate = NEXT_N_MONTHS:1 31 | OR CreadedDate = LAST_N_MONTHS:1 32 | OR CreadedDate = NEXT_N_QUARTERS:1 33 | OR CreadedDate = LAST_N_QUARTERS:1 34 | OR CreadedDate = NEXT_N_YEARS:1 35 | OR CreadedDate = LAST_N_YEARS:1 36 | OR CreadedDate = NEXT_N_FISCAL_QUARTERS:1 37 | OR CreadedDate = LAST_N_FISCAL_QUARTERS:1 38 | OR CreadedDate = NEXT_N_FISCAL_YEARS:1 39 | OR CreadedDate = LAST_N_FISCAL_YEARS:1 -------------------------------------------------------------------------------- /test/data/parsable/41-date-literal-04.soql: -------------------------------------------------------------------------------- 1 | SELECT Id, SomeNumberField__c 2 | FROM Account 3 | WHERE CreadedDate = YESTERDAY 4 | OR CreadedDate = TODAY 5 | OR CreadedDate = TOMORROW 6 | OR CreadedDate = LAST_WEEK 7 | OR CreadedDate = THIS_WEEK 8 | OR CreadedDate = NEXT_WEEK 9 | OR CreadedDate = LAST_MONTH 10 | OR CreadedDate = THIS_MONTH 11 | OR CreadedDate = NEXT_MONTH 12 | OR CreadedDate = LAST_90_DAYS 13 | OR CreadedDate = NEXT_90_DAYS 14 | OR CreadedDate = THIS_QUARTER 15 | OR CreadedDate = LAST_QUARTER 16 | OR CreadedDate = NEXT_QUARTER 17 | OR CreadedDate = THIS_YEAR 18 | OR CreadedDate = LAST_YEAR 19 | OR CreadedDate = NEXT_YEAR 20 | OR CreadedDate = THIS_FISCAL_QUARTER 21 | OR CreadedDate = LAST_FISCAL_QUARTER 22 | OR CreadedDate = NEXT_FISCAL_QUARTER 23 | OR CreadedDate = THIS_FISCAL_YEAR 24 | OR CreadedDate = LAST_FISCAL_YEAR 25 | OR CreadedDate = NEXT_FISCAL_YEAR 26 | OR CreadedDate = LAST_N_DAYS : 12 27 | OR CreadedDate = NEXT_N_DAYS : 12 28 | OR CreadedDate = NEXT_N_WEEKS : 12 29 | OR CreadedDate = LAST_N_WEEKS : 12 30 | OR CreadedDate = NEXT_N_MONTHS : 12 31 | OR CreadedDate = LAST_N_MONTHS : 12 32 | OR CreadedDate = NEXT_N_QUARTERS : 12 33 | OR CreadedDate = LAST_N_QUARTERS : 12 34 | OR CreadedDate = NEXT_N_YEARS : 12 35 | OR CreadedDate = LAST_N_YEARS : 12 36 | OR CreadedDate = NEXT_N_FISCAL_QUARTERS : 12 37 | OR CreadedDate = LAST_N_FISCAL_QUARTERS : 12 38 | OR CreadedDate = NEXT_N_FISCAL_YEARS : 12 39 | OR CreadedDate = LAST_N_FISCAL_YEARS : 12 -------------------------------------------------------------------------------- /test/data/parsable/50-dates-01.soql: -------------------------------------------------------------------------------- 1 | SELECT Id, SomeNumberField__c 2 | FROM Account 3 | WHERE CreatedDate < 1999-01-01T23:01:01+01:00 4 | OR CreatedDate <= 1999-01-01T23:01:01-08:00 5 | OR CreatedDate > 1999-01-01T23:01:01Z 6 | OR CreatedDate >= 1999-01-01 7 | OR CreatedDate = 1999-01-01T23:01:01+01:00 -------------------------------------------------------------------------------- /test/data/parsable/90-complex-01.soql: -------------------------------------------------------------------------------- 1 | SELECT 2 | Id, Name, Owner.Id, toLabel(StageName), 3 | (SELECT Id, Contact.Id, Contact.Name, format(Contact.Account.NumberOfEmployees) 4 | FROM OpportunityContactRoles 5 | LIMIT 10) 6 | FROM Opportunity 7 | USING SCOPE Mine 8 | WHERE 9 | Account.Name LIKE 'O\'reilly%' 10 | OR 11 | (NOT ( 12 | CALENDAR_MONTH(CreatedDate) = 10 13 | AND 14 | CreatedDate > 2015-12-31 15 | )) 16 | AND 17 | Owner.Name = 'Hello' 18 | OR 19 | Probability > 99.5 20 | ORDER BY 21 | Account.Type DESC NULLS LAST, 22 | CreatedDate 23 | LIMIT 100 24 | OFFSET 200 25 | -------------------------------------------------------------------------------- /test/index.test.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | import fs from 'fs'; 3 | import path from 'path'; 4 | import JSON5 from 'json5'; 5 | import { parse } from '..'; 6 | 7 | function getSyntaxErrorIndicator(text, location) { 8 | const lno = location.start.line - 1; 9 | const cno = location.start.column - 1; 10 | const lines = text.split(/\n/); 11 | const indicator = Array.from(new Array(cno)).map(() => ' ').join('') + '^'; 12 | return [ 13 | ...lines.slice(0, lno + 1), 14 | indicator, 15 | ...lines.slice(lno + 1), 16 | ].slice(Math.max(lno - 2, 0), lno + 3).join('\n'); 17 | } 18 | 19 | const PARSABLE_DIR = path.join(__dirname, 'data', 'parsable'); 20 | const EXPECTED_DIR = path.join(__dirname, 'data', 'expected'); 21 | 22 | fs.readdirSync(PARSABLE_DIR) 23 | .filter((filename) => /\.soql$/.test(filename)) 24 | .forEach((filename) => { 25 | const name = path.basename(filename, '.soql'); 26 | test(`parse soql test: ${name}`, (t) => { 27 | const soql = fs.readFileSync(path.join(PARSABLE_DIR, filename), 'utf8'); 28 | const expected = JSON5.parse( 29 | fs.readFileSync(path.join(EXPECTED_DIR, `${name}.json5`), 'utf8') 30 | ); 31 | let parsed; 32 | try { 33 | parsed = parse(soql); 34 | } catch (e) { 35 | let message = e.message; 36 | if (e.location) { 37 | const indicator = getSyntaxErrorIndicator(soql, e.location); 38 | message = `${message}\n\n[${name}]\n${indicator}`; 39 | } 40 | t.fail(message); 41 | } 42 | t.deepEqual(parsed, expected); 43 | }); 44 | }); 45 | --------------------------------------------------------------------------------