@sap/xsodata
Version:
Expose data from a HANA database as OData V2 service with help of .xsodata files.
228 lines (199 loc) • 7.57 kB
JavaScript
;
const jsonBuilder = require('./../serializer/json');
const contentTypeTools = require('./../utils/checkContentType');
const InternalError = require('./../utils/errors/internalError');
const AtomSerializer = require('./../serializer/atomSerializer');
const NotImplemented = require('./../utils/errors/http/notImplemented');
const odataUriType = require('../uri/uriType');
// Name of the URL parameter, which indicates that the service response should be in Atom format.
// This is a temporary solution, which is used until the OData content type negotiation is not implemented.
const CUSTOM_FORMAT_PARAMETER = 'format';
// Value of the custom 'format' parameter, which indicates that the service response should be in Atom format.
const CUSTOM_ATOM_FORMAT_NAME = 'atomXml';
exports.serializeNoContent = function (context, asyncDone) {
const responseHeaders = {};
addETagHeader(context, responseHeaders);
context.response.writeHead(204, responseHeaders);
return asyncDone(null, context);
};
//Code
exports.serializeData = function (context, asyncDone) {
try {
const uriType = odataUriType.determineUriType(context);
const requestedType = getRequestedType(context);
const responseContentType = getResponseContentType(
requestedType,
context,
uriType
);
const serializer = getSerializer(requestedType, context);
const responseBody = createResponseBody(context, uriType, serializer);
const responseHeaders = createResponseHeaders(
context,
responseContentType
);
writeResponse(context, responseBody, responseHeaders);
return asyncDone(null, context);
} catch (err) {
return asyncDone(err, context);
}
};
function createResponseHeaders(context, responseContentType) {
const headers = {
'Content-Type': responseContentType,
};
const cacheControl = getCacheControlHeader(context);
if (cacheControl) {
headers['cache-control'] = cacheControl;
}
addETagHeader(context, headers);
return headers;
}
function addETagHeader(context, headers) {
const dbSegment = context.oData.dbSegmentLast;
if (dbSegment.etagHeader) {
headers.ETag = 'W/"' + dbSegment.etagHeader + '"';
}
}
function getCacheControlHeader(context) {
if (context.gModel) {
return context.gModel.getSetting('content', 'cache-control');
}
}
function writeResponse(context, responseBody, responseHeaders) {
context.response.writeHead(200, responseHeaders);
context.response.write(responseBody);
}
function getInlineCount(context) {
if (context.oData.systemQueryParameters.inlinecount === true) {
const dbSegment = context.oData.dbSegmentLast;
const rows = dbSegment.getRowsWithGenKey();
const count =
(rows[0] && rows[0]['0count']) ||
(dbSegment.sql.countRows[0] && dbSegment.sql.countRows[0]['c']);
return String(count);
}
}
/**
* Serializes the last DB segment from the specified context using the specified serializer in accordance with the
* specified uriType.
* @returns HTTP response body containing the serialized data.
*/
function createResponseBody(context, uriType, serializer) {
const dbSegment = context.oData.dbSegmentLast;
switch (uriType) {
case odataUriType.URI1:
case odataUriType.URI6B:
return serializer.serializeFeed(dbSegment, getInlineCount(context));
case odataUriType.URI2:
case odataUriType.URI6A:
return serializer.serializeEntity(dbSegment);
case odataUriType.URI5A:
return serializer.serializeProperty(dbSegment);
case odataUriType.URI5B:
return serializePropertyValue(dbSegment);
case odataUriType.URI7A:
return serializer.serializeEntity(dbSegment);
case odataUriType.URI7B:
return serializer.serializeFeed(dbSegment, getInlineCount(context));
case odataUriType.URI15:
case odataUriType.URI16:
return serializeCountValue(context, dbSegment);
default:
throw new NotImplemented('Request URI is not supported.', context);
}
}
function getSerializer(contentType, context) {
if (contentType === CUSTOM_ATOM_FORMAT_NAME) {
return new AtomSerializer(context);
} else {
return new jsonBuilder.JsonBuilder(context);
}
}
function getRequestedType(context) {
const customFormat = getCustomFormat(context);
if (customFormat && customFormat === CUSTOM_ATOM_FORMAT_NAME) {
return CUSTOM_ATOM_FORMAT_NAME;
}
return context.oData.systemQueryParameters.format;
}
function getResponseContentType(requestedType, context, uriType) {
let contentType;
switch (uriType) {
case odataUriType.URI1:
case odataUriType.URI7B:
case odataUriType.URI6B:
// custom 'format' parameter takes precedence over the $format option and the Accept header.
if (requestedType === CUSTOM_ATOM_FORMAT_NAME) {
contentType = contentTypeTools.cTypes.ctAtomFeed;
} else {
contentType = contentTypeTools.checkFeed(
context.request,
requestedType
);
}
break;
case odataUriType.URI2:
case odataUriType.URI7A:
case odataUriType.URI6A:
// custom 'format' parameter takes precedence over the $format option and the Accept header.
if (requestedType === CUSTOM_ATOM_FORMAT_NAME) {
contentType = contentTypeTools.cTypes.ctAtomEntry;
} else {
contentType = contentTypeTools.checkEntry(
context.request,
requestedType
);
}
break;
case odataUriType.URI5A:
contentType = contentTypeTools.checkProperty(
context.request,
requestedType
);
break;
case odataUriType.URI5B:
contentType = contentTypeTools.checkValue(
context.request,
requestedType
);
break;
case odataUriType.URI15:
case odataUriType.URI16:
contentType = contentTypeTools.checkCount(
context.request,
requestedType
);
break;
default:
throw new NotImplemented('Request URI is not supported.', context);
}
return contentType + ';charset=utf-8';
}
/**
* Returns value of the custom 'format' URL parameter.
*/
function getCustomFormat(context) {
const queryParameters = context.uriTree.queryParameters;
if (queryParameters) {
return queryParameters[CUSTOM_FORMAT_PARAMETER];
}
}
function serializePropertyValue(dbSeg) {
if (dbSeg.sql.rows.length > 1) {
throw new InternalError('To many rows for serializing a property');
}
const property = dbSeg.singleProperty;
let converter;
converter = dbSeg.entityType.__metadata.converterMapToXMLPayload[property];
return converter(dbSeg.sql.rows[0][property]);
}
function serializeCountValue(context, dbSeg) {
context.logger.silly('resourceprocessor', 'serializeCountValue');
if (dbSeg.sql.rows.length > 1) {
throw new InternalError(
'To many rows for serializing a $count response'
);
}
return dbSeg.sql.rows[0].c;
}