@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
JavaScript
;
/**
* 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);
}
};