UNPKG

odatafy-parser

Version:

generate odatafy ast from odata query parameters

199 lines (186 loc) 8.47 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.processSelectOptionsUnprocessedNode = void 0; const peggy_1 = __importDefault(require("peggy")); const nodes_1 = require("../types/nodes"); const querystring_1 = __importDefault(require("querystring")); const filterParser_1 = __importDefault(require("./filterParser")); const orderbyParser_1 = __importDefault(require("./orderbyParser")); const skipParser_1 = __importDefault(require("./skipParser")); const topParser_1 = __importDefault(require("./topParser")); const computeParser_1 = __importDefault(require("./computeParser")); const expandParser_1 = __importDefault(require("./expandParser")); const searchParser_1 = __importDefault(require("./searchParser")); const utils_1 = require("../utils"); const errors_1 = require("../types/errors"); //TODO add annotations to path const selectParser = peggy_1.default.generate(` { function SelectNodeHelper(value) { return { nodeType: "SelectNode", value: value.filter(selectItem => selectItem != undefined) } } function SelectPathNodeHelper(value, options) { return options?{ nodeType: "SelectPathNodeWithOptions", value: value, options: options } : { nodeType: "SelectPathNode", value: value } } function SelectIdentifierNodeHelper(value, flag) { return { nodeType: "SelectIdentifierNode", value: value, ...(flag && {flag: flag}) } } function SelectFunctionNodeHelper(func, args) { return { nodeType: "SelectFunctionNode", func: func, args: args } } function SelectOptionsUnprocessedNodeHelper(selectOptionsString) { return { nodeType: "SelectOptionsUnprocessedNode", value: selectOptionsString } } } //functionname and selectoption are not clashing, because function params need to have odataIdentifiers as params and selectoptions always contain EQs, which are not allowed in odataIdents select = head:selectItem tail:( COMMA BWS @selectItem )* {return SelectNodeHelper([head, ...tail])} selectItem = STAR {return undefined} / identNode:allOperationsInSchema {return SelectPathNodeHelper([identNode])} / ( identNode:qualifiedFunctionName {return SelectPathNodeHelper([identNode])} / selectPath / identNode:(selectPathPart) selOps:selectOptions? {return SelectPathNodeHelper([identNode], selOps) } ) selectPath = head:(selectPathPart) tail:( "/" @(identNode: selectPathPart ) )* selOps:selectOptions? {return SelectPathNodeHelper([head, ...tail], selOps)} selectPathPart = odataIdentifierWithNamespace / odataIdentifier / odataAnnotation selectOptions = OPEN selectOptionString:$textUntilTerminator CLOSE {return SelectOptionsUnprocessedNodeHelper(selectOptionString)} textUntilTerminator = ( &haveTerminatorAhead .)* haveTerminatorAhead = . ( !")" . )* ")" //avoid the matching with qualifiedFunctionName if the expression is actually an Identifier with selectOptions qualifiedFunctionName = func:$odataIdentifierWithNamespace OPEN args:parameterNames CLOSE {return SelectFunctionNodeHelper(func, args)} //selectOptionStart = ("$"? ("filter" / "search" / "count" / "orderby" / "skip" / "top" / "compute" / "select" / "expand") EQ .*) parameterNames = head:odataIdentifier tail:( COMMA @odataIdentifier )* {return [head, ...tail]} allOperationsInSchema = value:$(odataIdentifier ( "." odataIdentifier )*) "." STAR {return SelectIdentifierNodeHelper(value, "AllOperationsInSchema")} odataAnnotation = AT identNode:(odataIdentifierWithNamespace / odataIdentifier) {return {...identNode, flag: "Annotation"}} odataIdentifierWithNamespace = value:$(odataIdentifier ( "." odataIdentifier )+) {return SelectIdentifierNodeHelper(value)} odataIdentifier = value:$(identifierLeadingCharacter identifierCharacter*) {return SelectIdentifierNodeHelper(value)} identifierLeadingCharacter = ALPHA / "_" identifierCharacter = ALPHA / "_" / DIGIT AT = "@" / "%40" ALPHA = [a-zA-Z] DIGIT = [0-9] COMMA = "," / "%2C" STAR = "*" / "%2A" EQ = "=" OPEN = "(" / "%28" CLOSE = ")" / "%29" RWS = ( SP / HTAB / "%20" / "%09" )+ BWS = ( SP / HTAB / "%20" / "%09" )* SP = ' ' HTAB = ' ' `); function parseSelect(expr) { let ast; try { ast = selectParser.parse(expr); } catch (e) { throw (0, utils_1.getOdatafyParserError)('malformed select expression', errors_1.OdatafyQueryOptions.Select); } try { for (const selectPath of ast.value) { if (selectPath.nodeType == nodes_1.NodeTypes.SelectPathNodeWithOptions) { selectPath.options = processSelectOptionsUnprocessedNode(selectPath.options); } } return ast; } catch (e) { throw (0, utils_1.getOdatafyParserError)('malformed select options', errors_1.OdatafyQueryOptions.Select); } } function processSelectOptionsUnprocessedNode(SelectOptionsUnprocessedNode) { const parsedOptions = querystring_1.default.parse(SelectOptionsUnprocessedNode.value, ';'); const options = {}; //parse options wíth $ if (parsedOptions.$filter && typeof parsedOptions.$filter == 'string') { options.filter = filterParser_1.default.parse(parsedOptions.$filter); } if (parsedOptions.$orderby && typeof parsedOptions.$orderby == 'string') { options.orderby = orderbyParser_1.default.parse(parsedOptions.$orderby); } if (parsedOptions.$skip && typeof parsedOptions.$skip == 'string') { options.skip = skipParser_1.default.parse(parsedOptions.$skip); } if (parsedOptions.$top && typeof parsedOptions.$top == 'string') { options.top = topParser_1.default.parse(parsedOptions.$top); } if (parsedOptions.$select && typeof parsedOptions.$select == 'string') { options.select = parseSelect(parsedOptions.$select); } if (parsedOptions.$compute && typeof parsedOptions.$compute == 'string') { options.compute = computeParser_1.default.parse(parsedOptions.$compute); } if (parsedOptions.$expand && typeof parsedOptions.$expand == 'string') { options.expand = expandParser_1.default.parse(parsedOptions.$expand); } if (parsedOptions.$count && typeof parsedOptions.$count == 'string') { options.count = true; } if (parsedOptions.$search && typeof parsedOptions.$search == 'string') { options.search = searchParser_1.default.parse(parsedOptions.$search); } // parse options without $ if (parsedOptions.filter && typeof parsedOptions.filter == 'string') { options.filter = filterParser_1.default.parse(parsedOptions.filter); } if (parsedOptions.orderby && typeof parsedOptions.orderby == 'string') { options.orderby = orderbyParser_1.default.parse(parsedOptions.orderby); } if (parsedOptions.skip && typeof parsedOptions.skip == 'string') { options.skip = skipParser_1.default.parse(parsedOptions.skip); } if (parsedOptions.top && typeof parsedOptions.top == 'string') { options.top = topParser_1.default.parse(parsedOptions.top); } if (parsedOptions.select && typeof parsedOptions.select == 'string') { options.select = parseSelect(parsedOptions.select); } if (parsedOptions.compute && typeof parsedOptions.compute == 'string') { options.compute = computeParser_1.default.parse(parsedOptions.compute); } if (parsedOptions.expand && typeof parsedOptions.expand == 'string') { options.expand = expandParser_1.default.parse(parsedOptions.expand); } if (parsedOptions.count && typeof parsedOptions.count == 'string') { options.count = true; } if (parsedOptions.search && typeof parsedOptions.search == 'string') { options.search = searchParser_1.default.parse(parsedOptions.search); } return options; } exports.processSelectOptionsUnprocessedNode = processSelectOptionsUnprocessedNode; exports.default = { /** * Parser for select expressions * @param expr select expression as string * @example selectParser.parse("Name,Age") * @returns Abstract Syntax Tree (AST) of type SelectNode */ parse: parseSelect };