odatafy-parser
Version:
generate odatafy ast from odata query parameters
228 lines (213 loc) • 9.61 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.processExpandOptionsUnprocessedNode = void 0;
const peggy_1 = __importDefault(require("peggy"));
const querystring_1 = __importDefault(require("querystring"));
const errors_1 = require("../types/errors");
const nodes_1 = require("../types/nodes");
const utils_1 = require("../utils");
const computeParser_1 = __importDefault(require("./computeParser"));
const filterParser_1 = __importDefault(require("./filterParser"));
const levelsParser_1 = __importDefault(require("./levelsParser"));
const orderbyParser_1 = __importDefault(require("./orderbyParser"));
const searchParser_1 = __importDefault(require("./searchParser"));
const selectParser_1 = __importDefault(require("./selectParser"));
const skipParser_1 = __importDefault(require("./skipParser"));
const topParser_1 = __importDefault(require("./topParser"));
const expandParser = peggy_1.default.generate(`
{
function ExpandNodeHelper(value) {
return {
nodeType: "ExpandNode",
value: value.filter(expandItem => expandItem != undefined)
}
}
function ExpandPathNodeHelper(value, expOps) {
return expOps && expOps.value != ""?{
nodeType: "ExpandPathNodeWithOptions",
value: value.filter(expandPathItem => expandPathItem != undefined),
options:expOps
} : {
nodeType: "ExpandPathNode",
value: value.filter(expandPathItem => expandPathItem != undefined)
}
}
function ExpandIdentifierNodeHelper(value, flag) {
return {
nodeType: "ExpandIdentifierNode",
value: value,
...(flag && {flag: flag})
}
}
function ExpandFunctionNodeHelper(func, args) {
return {
nodeType: "ExpandFunctionNode",
func: func,
args: args
}
}
function ExpandOptionsUnprocessedNodeHelper(expandOptionsString, type) {
return {
nodeType: "ExpandOptionsUnprocessedNode",
value: expandOptionsString,
type: type
}
}
function ExpandStarNodeHelper(options) {
return {
nodeType: "ExpandStarNode",
...(options && {options: options})
}
}
function ExpandValueNodeHelper() {
return {
nodeType: "ExpandValueNode"
}
}
}
start = head:expandItem tail:(COMMA @expandItem)* {return ExpandNodeHelper([head, ...tail])}
expandItem = "$value" {return ExpandPathNodeHelper([ExpandValueNodeHelper()])}
/ identNode:odataIdentifierWithNamespace !("/" / "(") {return ExpandPathNodeHelper([identNode])}
/ identNode:odataIdentifier !("/" / "." / "(") {return ExpandPathNodeHelper([identNode])}
/ expandPath
//if there is a dollar sign, dont read the ident here so that it can be read later
expandPath = path1: ( @( odataIdentifierWithNamespace / odataIdentifier ) "/" !"$")*
path2: ( STAR options:( ref {return {ref: true}} / OPEN levels:levels CLOSE {return {levels: levels}} )? {return {path: [ExpandStarNodeHelper(options)]}}
/ ident1:(odataIdentifier / odataAnnotation) ident2:( "/" @(odataIdentifierWithNamespace/odataIdentifier) )?
expOps:(
type:(ref {return "ref"} / count {return "count"} / "" &OPEN {return "default"}) optionString:expandOptions? {return ExpandOptionsUnprocessedNodeHelper(optionString, type)}
)?
{return {path: [ident1, ident2], expOps: expOps}}
)
{return ExpandPathNodeHelper([...path1, ...path2.path], path2.expOps)}
count = "/$count"
ref = "/$ref"
odataAnnotation = AT identNode:(odataIdentifierWithNamespace / odataIdentifier) {return {...identNode, flag: "Annotation"}}
expandOptions = OPEN expandOptionString:$textUntilTerminator CLOSE {return expandOptionString}
textUntilTerminator = ( &haveTerminatorAhead .)*
haveTerminatorAhead = . ( !")" . )* ")"
levels = ( "$levels" / "levels" ) EQ value:( oneToNine DIGIT* / "max" ) {return value}
odataIdentifierWithNamespace = value:$(odataIdentifier ( "." odataIdentifier )+) {return ExpandIdentifierNodeHelper(value)}
odataIdentifier = value:$(identifierLeadingCharacter identifierCharacter*) {return ExpandIdentifierNodeHelper(value)}
identifierLeadingCharacter = ALPHA / "_"
identifierCharacter = ALPHA / "_" / DIGIT
oneToNine = "1" / "2" / "3" / "4" / "5" / "6" / "7" / "8" / "9"
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 = ' '
`, { trace: false });
function parseExpand(expr) {
let ast;
try {
ast = expandParser.parse(expr);
}
catch (e) {
throw (0, utils_1.getOdatafyParserError)('malformed expand expression', errors_1.OdatafyQueryOptions.Expand);
}
try {
for (const expandItem of ast.value) {
if (expandItem.nodeType == nodes_1.NodeTypes.ExpandPathNodeWithOptions) {
const expandOptions = processExpandOptionsUnprocessedNode(expandItem.options);
expandItem.options = expandOptions.value;
expandItem.optionType = expandOptions.type;
}
}
return ast;
}
catch (e) {
throw (0, utils_1.getOdatafyParserError)('malformed expand options', errors_1.OdatafyQueryOptions.Expand);
}
}
function processExpandOptionsUnprocessedNode(expandOptionsUnprocessedNode) {
const parsedOptions = querystring_1.default.parse(expandOptionsUnprocessedNode.value, ';');
const options = {};
//parse options with $
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 = selectParser_1.default.parse(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 = parseExpand(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);
}
if (parsedOptions.$levels && typeof parsedOptions.$levels == 'string') {
options.levels = levelsParser_1.default.parse(parsedOptions.$levels);
}
//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 = selectParser_1.default.parse(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 = parseExpand(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);
}
if (parsedOptions.levels && typeof parsedOptions.levels == 'string') {
options.levels = levelsParser_1.default.parse(parsedOptions.levels);
}
//TODO search
return {
value: options,
type: expandOptionsUnprocessedNode.type
};
}
exports.processExpandOptionsUnprocessedNode = processExpandOptionsUnprocessedNode;
exports.default = {
/**
* Parser for expand expressions
* @param expr expand expression as string
* @example expandParser.parse("Items/$ref")
* @returns Abstract Syntax Tree (AST) of type ExpandNode
*/
parse: parseExpand
};