UNPKG

@sap/xsodata

Version:

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

245 lines (206 loc) 8.76 kB
'use strict'; const typeConverter = require('./../utils/typeConverter'); const dbs = require('./../db/dbSegment'); const NotFound = require('./../utils/errors/http/notFound'); const InternalError = require('./../utils/errors/internalError'); const configuration = require('./../configuration'); const model = require('../model/model.js'); exports.JsonBuilder = JsonBuilder; function JsonBuilder(context) { this._ns = context.gModel.getNamespace(); this._baseUrl = context.uriTree.baseUrl; this._context = context; } JsonBuilder.prototype.createConverterArray = function (dbSeg) { return dbSeg.getSelectedPropsWithGenKey().map(function toConverter(property) { return dbSeg.entityType.converterMapToJsonPayload[property]; }); }; JsonBuilder.prototype.serializeEntity = function (dbSegment) { var entity = this._serializeEntity(dbSegment); return toODataJson(this._context, entity); }; JsonBuilder.prototype._serializeEntity = function (dbSeg) { var rows = dbSeg.getRowsWithGenKey(); if (rows.length === 0) { if (dbSeg.entityType._entityType.parameters && dbSeg.entityType._entityType.parameters.viaKey === true) { throw new NotFound('Entity with provided input parameter and key not found.'); } else { throw new NotFound('Resource not found.'); } } var converter = this.createConverterArray(dbSeg); return this.serializeRow(this._context, dbSeg, rows[0], converter); }; JsonBuilder.prototype.serializeProperty = function (dbSeg) { if (dbSeg.getRowsWithGenKey().length > 1) { throw new InternalError('Too many rows for serializing a property.'); } var property = dbSeg.singleProperty; var converter; var result = {}; converter = dbSeg.entityType.converterMapToJsonPayload[property]; result[property] = converter(dbSeg.getRowsWithGenKey()[0][property]); return toODataJson(this._context, result); }; JsonBuilder.prototype.serializeFeed = function (dbSegment, inlineCount) { var results = this._serializeFeed(dbSegment, inlineCount); var feed = { "__count": inlineCount, "results": results }; return toODataJson(this._context, feed); }; JsonBuilder.prototype._serializeFeed = function (dbSeg) { var rows = dbSeg.getRowsWithGenKey(); var results = []; var converter = this.createConverterArray(dbSeg); for (var i = 0; i < rows.length; i++) { var line = this.serializeRow(this._context, dbSeg, rows[i], converter); results.push(line); } return results; }; JsonBuilder.prototype.serializeNavMany = function (context, dbSeg, parent1row) { var rows = dbSeg.getRowsWithGenKey(); var results = []; var row = rows[dbSeg.sql.readPosition]; var converter = this.createConverterArray(dbSeg); while (row && (row['0row'] === parent1row)) { var line = this.serializeRow(context, dbSeg, row, converter); results.push(line); //next dbSeg.sql.readPosition++; row = rows[dbSeg.sql.readPosition]; } return results; }; JsonBuilder.prototype.serializeNavOne = function (context, dbSeg, parent1row) { var rows = dbSeg.getRowsWithGenKey(); var results = []; var row = rows[dbSeg.sql.readPosition]; var converter = this.createConverterArray(dbSeg); while (row && (row['0row'] === parent1row)) { var line = this.serializeRow(context, dbSeg, row, converter); results.push(line); //next dbSeg.sql.readPosition++; row = rows[dbSeg.sql.readPosition]; } if (results.length > 1) { context.logger.silly("json", "Error: Too many records for nav to one: " + results.length + ', ' + parent1row); throw new InternalError('Too many records for nav to one'); } return results[0]; }; JsonBuilder.prototype.createAbsUrl = function (dbSeg, row) { var ret = this._baseUrl + dbSeg.entityType.name; var keysProperties = dbSeg.getKeysProperties(); ret += '('; ret += keysProperties.map(toValues).filter(v => v !== undefined).join(','); return ret + ')'; function toValues(keyProperty, index, array) { var value; if (dbSeg.entityType.kind === model.entityKind.calculationView && dbSeg.entityType._entityType.parameters.viaKey === true) { if (row[keyProperty.COLUMN_NAME] === null) { value = null; } else if (row[keyProperty.COLUMN_NAME] !== undefined) { // although no input parameter given from client the application MUST have filled the input parameters as key (undefined via db is not possible) value = typeConverter.serializeDbValueToUriLiteral(row[keyProperty.COLUMN_NAME], keyProperty); } else { if (dbSeg.hasAliasedKeyPropertiesOnCalcView()) { let foundKeyFieldAlias = null; for (let i = 0; i < dbSeg._aliasedKeyPropertiesOnCalcView.length; i++) { if (dbSeg._aliasedKeyPropertiesOnCalcView[i].property === keyProperty.COLUMN_NAME && dbSeg._aliasedKeyPropertiesOnCalcView[i].table === dbSeg._Alias) { foundKeyFieldAlias = dbSeg._aliasedKeyPropertiesOnCalcView[i].alias; break; // there must not be more than one entry! } } if (foundKeyFieldAlias !== null && row[foundKeyFieldAlias] !== undefined) { value = typeConverter.serializeDbValueToUriLiteral(row[foundKeyFieldAlias], keyProperty); } else { value = null; } } else { value = null; } } } else { value = typeConverter.serializeDbValueToUriLiteral(row[keyProperty.COLUMN_NAME], keyProperty); } if (array.length === 1) { return value; } return keyProperty.COLUMN_NAME + '=' + value; } }; JsonBuilder.prototype.serializeRow = function (context, dbSeg, row, converter) { var ret = {}; var url = this.createAbsUrl(dbSeg, row); if (context.oData.dbSegment.isLinks) { ret.uri = url; return ret; } ret.__metadata = { uri: url, type: this._ns + '.' + dbSeg.entityType.name + 'Type' }; if (row.__etag) { ret.__metadata.etag = 'W/"' + row.__etag + '"'; } if (!dbSeg.hasAliasedKeyPropertiesOnCalcView()) { dbSeg.getSelectedPropsWithGenKey().forEach(function toConverter(property, index) { ret[property] = converter[index](row[property]); }); } else { let selectedProperties = dbSeg.getSelectedPropsWithGenKey(); for (let i = 0; i < selectedProperties.length; i++) { if (!row[selectedProperties[i]]) { let aliasedKeyProperty = dbSeg.getAliasedKeyPropertyOnCalcView(selectedProperties[i]); if (aliasedKeyProperty !== null && aliasedKeyProperty.alias) { ret[selectedProperties[i]] = converter[i](row[aliasedKeyProperty.alias]); } else { ret[selectedProperties[i]] = converter[i](row[selectedProperties[i]]); // keeps null } } else { ret[selectedProperties[i]] = converter[i](row[selectedProperties[i]]); } } } for (let i = 0; i < dbSeg._SelectedNavigations.length; i++) { var sn = dbSeg._SelectedNavigations[i]; var navDbSeg = dbSeg.getRelevantNavigationSegments()[sn]; if (navDbSeg && navDbSeg.isExpand) { ret[sn] = this.serializeDbSeg(context, navDbSeg, row['1row']); } else { ret[sn] = { __deferred: { uri: url + '/' + sn } }; } } return ret; }; JsonBuilder.prototype.serializeDbSeg = function (context, dbSeg, parent1row) { var d; if (dbSeg.kind === dbs.DBS_Entity) { if (dbSeg.isCollection) { d = { results: this._serializeFeed(context, dbSeg) }; } else { d = this._serializeEntity(context, dbSeg); } } else if (dbSeg.kind === dbs.DBS_Navigation) { if (dbSeg.isCollection) { d = { results: this.serializeNavMany(context, dbSeg, parent1row) }; } else { d = this.serializeNavOne(context, dbSeg, parent1row); } } return d; }; function toODataJson(context, object) { var response = { d: object }; if (context.mode === configuration.modes.development) { return JSON.stringify(response, null, 4); } else { return JSON.stringify(response, null, 0); } }