UNPKG

@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
'use strict'; 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; }