UNPKG

@sap/xsodata

Version:

Expose data from a HANA database as OData V2 service with help of .xsodata files.

163 lines (140 loc) 5.79 kB
'use strict'; /** * Add <odata> to <context>> * Uses <context>.<uriTree> * * @type {exports} */ //Includes var oDataSegmentParser = require('../parsers/jison_segment_parser'); var queryParameterParser = require('./queryParameterParser'); var resourcePathParser = require('./resourcePathParser'); var uriSegmentTypes = require('./uriSegmentTypes'); var BadRequest = require('./../utils/errors/http/badRequest'); var Measurement = require('./../utils/measurement'); var typedObjects = require("./../utils/typedObjects"); var BadRequestError = require("./../utils/errors/http/badRequest"); // Values of the constants below correspond to the OData URI types exports.URI_KIND_Service = 0; exports.URI_KIND_MetaData = 8; exports.URI_KIND_Batch = 9; exports.URI_KIND_Resource = 4; // unknown position indicator on URI parsing failure const UNKNOWN_POSITION = -1; function parseODataSegments(segments) { var segmentsParsed = []; if (segments.decoded.length === 0) { segmentsParsed.push(new uriSegmentTypes.ServiceSegment('')); } else { for (var i = 0; i < segments.decoded.length; i++) { try { var tmp = oDataSegmentParser.parse(segments.decoded[i]); segmentsParsed.push(tmp); } catch (err) { let position = (err.hash && err.hash.loc && err.hash.loc.last_column && Number.isInteger(err.hash.loc.last_column)) ? err.hash.loc.last_column : UNKNOWN_POSITION; if (position === UNKNOWN_POSITION) { throw new BadRequest(`Syntax error in resource path. Character position could not be determined.`, err); } else { throw new BadRequest(`Syntax error in resource path at position ${position+1}`, err); } } } } return segmentsParsed; } function parseQueryParameter(name, value) { if (name === '$filter') { return queryParameterParser.parseFilter(value); } else if (name === '$orderby') { return queryParameterParser.parseOrderBy(value); } else if (name === '$expand') { return queryParameterParser.parseExpand(value); } else if (name === '$select') { return queryParameterParser.parseSelect(value); } else if (name === '$top') { return queryParameterParser.parseTop(value); } else if (name === '$skip') { return queryParameterParser.parseSkip(value); } else if (name === '$format') { return queryParameterParser.parseFormat(value); } else if (name === '$inlinecount') { return queryParameterParser.parseInlineCount(value); } return null; } function parseSystemQueryParameters(queries) { var queryParameter = {}; for (var prop in queries) { if (Object.prototype.hasOwnProperty.call(queries, prop)) { if (prop.substr(0, 1) === '$') { var name = prop.substr(1); queryParameter[name] = parseQueryParameter(prop, queries[prop]); } } } return queryParameter; } exports.parseODataUri = function (context, asyncDone) { context.logger.silly('uri', 'parse OData uri'); var oData = {}; try { context.logger.silly('uri', 'parse OData segments'); oData.segments = Measurement.measureSync(parseODataSegments, context.uriTree.segments, 'parseODataSegments'); } catch (err) { return asyncDone(err,context); } try { context.logger.silly('uri', 'parse OData system query parameters'); oData.systemQueryParameters = Measurement.measureSync(parseSystemQueryParameters, context.uriTree.rawUrlParts.query, 'parseSystemQueryParameters'); exports.validateQueryParameters(oData.systemQueryParameters, context); } catch (err) { return asyncDone(err,context); } var segment = oData.segments[0]; if (segment instanceof uriSegmentTypes.ServiceSegment) { oData.kind = exports.URI_KIND_Service; } else if (segment instanceof uriSegmentTypes.BatchSegment) { oData.kind = exports.URI_KIND_Batch; } else if (segment instanceof uriSegmentTypes.MetadataSegment) { oData.kind = exports.URI_KIND_MetaData; } else if (segment instanceof uriSegmentTypes.ResourceSegment) { oData.kind = exports.URI_KIND_Resource; try { resourcePathParser.parseResourcePath(context, oData); } catch (err) { return asyncDone(err,context); } } context.oData = oData; return asyncDone(null, context); }; /** * Validates url query parameters. This method triggers validation of $filter query parameter * * @param queryParameters {Object} The query parameters * @param context {Object} The xsodata context */ exports.validateQueryParameters = function (queryParameters, context) { var query = context.uriTree.rawUrlParts.query; if (queryParameters.filter || (query && query['$filter'])) { exports.validate$filterExpression(queryParameters, context); } }; /** * Validates url $filter expression. * * @param queryParameters {Object} The query parameters * @param context {Object} The xsodata context */ exports.validate$filterExpression = function (queryParameters, context) { var filter = queryParameters.filter; var query = context.uriTree.rawUrlParts.query; // Invalid $filter expression --> $filter=Id if (filter && (filter instanceof typedObjects.Property) === true) { throw new BadRequestError("Provided $filter expression is not valid", context); } // Invalid $filter expression --> $filter=Id$abc%20eq%20'1' --> with $ char if (!filter && query && query['$filter']) { throw new BadRequestError("Provided $filter expression is not valid", context); } };