UNPKG

@sap/xsodata

Version:

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

375 lines (314 loc) 10.8 kB
'use strict'; const dbutils = require('./../utils/typeConverter'); const typConv = require('./../utils/typeConverter'); const XMLWriter = require('xml-writer'); const contentTypeTools = require('./../utils/checkContentType'); const associationsUtil = require('./../utils/associations'); const Measurement = require('./../utils/measurement'); function writeNavProperty( xw, navigation, associations, entityType, areAnnotationsEnabled ) { xw.startElement('NavigationProperty'); xw.writeAttribute('Name', navigation.name); const assocName = navigation.association; const association = associations[assocName]; // xw.writeAttribute('Relationship', xw._ns + '.' + association.name + 'Type'); xw.writeAttribute( 'FromRole', associationsUtil.createFromRoleString( navigation, association, entityType ) ); xw.writeAttribute( 'ToRole', associationsUtil.createToRoleString(navigation, association, entityType) ); if (areAnnotationsEnabled) { writeAnnotationAttributes( entityType.getNavigationPropertyAnnotations(navigation.name), xw ); } xw.endElement(); } function writeProperty( xw, propertyName, col, entityType, areAnnotationsEnabled ) { xw.startElement('Property'); xw.writeAttribute('Name', propertyName); if (entityType.isConcurrentProperty(propertyName)) { xw.writeAttribute('ConcurrencyMode', 'Fixed'); } const oOataType = dbutils.dbTypeNameToODataTypeID(col.DATA_TYPE_NAME); xw.writeAttribute('Type', typConv.oDataTypeIDs[oOataType]); if (col.IS_NULLABLE !== 'TRUE') { xw.writeAttribute('Nullable', 'false'); } if (oOataType === 9) { //String xw.writeAttribute('MaxLength', col.LENGTH); } else if (oOataType === 6) { //Decimal xw.writeAttribute('Precision', col.LENGTH); xw.writeAttribute('Scale', col.SCALE); } if (areAnnotationsEnabled) { writeAnnotationAttributes( entityType.getPropertyAnnotations(propertyName), xw ); } xw.endElement(); } function writeAssociationTypes(xw, associations) { // Associations for (const assocName in associations) { if (associations.hasOwnProperty(assocName)) { const assoc = associations[assocName]; xw.startElement('Association'); xw.writeAttribute('Name', assocName + 'Type'); xw.startElement('End'); xw.writeAttribute( 'Type', xw._ns + '.' + assoc.principal.type + 'Type' ); xw.writeAttribute('Role', assoc.principal.type + 'Principal'); xw.writeAttribute('Multiplicity', assoc.principal.multiplicity); xw.endElement(); xw.startElement('End'); xw.writeAttribute( 'Type', xw._ns + '.' + assoc.dependent.type + 'Type' ); xw.writeAttribute('Role', assoc.dependent.type + 'Dependent'); xw.writeAttribute('Multiplicity', assoc.dependent.multiplicity); xw.endElement(); if (assoc.referentialConstraint) { xw.startElement('ReferentialConstraint'); xw.startElement('Principal'); xw.writeAttribute('Role', assoc.principal.type + 'Principal'); xw.startElement('PropertyRef'); xw.writeAttribute('Name', assoc.principal.joinproperties[0]); xw.endElement(); xw.endElement(); //Principal xw.startElement('Dependent'); xw.writeAttribute('Role', assoc.dependent.type + 'Dependent'); xw.startElement('PropertyRef'); xw.writeAttribute('Name', assoc.dependent.joinproperties[0]); xw.endElement(); xw.endElement(); //Dependent xw.endElement(); //ReferentialConstraint } xw.endElement(); //Association } } } function writeEntityTypes( xw, entityTypes, associations, areAnnotationsEnabled ) { let i, ii, j; /** * @type {EntityType} * */ let entityType = null; for (i = 0; i < entityTypes.length; i++) { entityType = entityTypes[i]; xw.startElement('EntityType'); xw.writeAttribute('Name', entityType.name + 'Type'); if (areAnnotationsEnabled) { writeAnnotationAttributes( entityType.getEntityTypeAnnotations(), xw ); } //keys xw.startElement('Key'); entityType.getKeysOrGenKey().forEach(writeKey); xw.endElement(); const props = entityType.getPropertiesWithGenKey(); for (j = 0; j < props.length; j++) { writeProp(props[j], entityType); } //nav properties for (ii = 0; ii < entityType.navProperties.length; ii++) { const navigation = entityType.navProperties[ii]; writeNavProperty( xw, navigation, associations, entityType, areAnnotationsEnabled ); } xw.endElement(); } function writeKey(keyName) { xw.startElement('PropertyRef'); xw.writeAttribute('Name', keyName); xw.endElement(); } function writeProp(property, entityType) { writeProperty( xw, property.COLUMN_NAME, property, entityType, areAnnotationsEnabled ); } } function writeEntitySets(xw, entityTypes, areAnnotationsEnabled) { for (const entityType of entityTypes) { xw.startElement('EntitySet'); xw.writeAttribute('Name', entityType.name); xw.writeAttribute( 'EntityType', xw._ns + '.' + entityType.name + 'Type' ); if (areAnnotationsEnabled) { writeAnnotationAttributes(entityType.getEntitySetAnnotations(), xw); } xw.endElement(); } } function writeAssociations(xw, associations, areAnnotationsEnabled) { for (const association of associations) { xw.startElement('AssociationSet'); xw.writeAttribute('Name', association.name); xw.writeAttribute( 'Association', xw._ns + '.' + association.name + 'Type' ); if (areAnnotationsEnabled) { writeAnnotationAttributes( association.getAssociationSetAnnotations(), xw ); } xw.startElement('End'); xw.writeAttribute('Role', association.principal.type + 'Principal'); xw.writeAttribute('EntitySet', association.principal.type); xw.endElement(); xw.startElement('End'); xw.writeAttribute('Role', association.dependent.type + 'Dependent'); xw.writeAttribute('EntitySet', association.dependent.type); xw.endElement(); xw.endElement(); //AssociationSet } } exports.process = function (context, asyncDone) { const response = context.response, format = context.oData.systemQueryParameters.format, areAnnotationsEnabled = context.gModel.areAnnotationsEnabled(); context.logger.silly('metadataprocessor', 'process'); context.payloadType = Measurement.measureSync( contentTypeTools.checkServiceMetadata, context.request, format, 'contentTypeTools.checkServiceMetadata' ); const header = { 'Content-Type': context.payloadType + ';charset=utf-8', }; const cacheControl = context.gModel.getSetting('metadata', 'cache-control'); if (cacheControl) { header['cache-control'] = cacheControl; } response.writeHead(200, header); const writer = function writer(string) { response.write(string); }; const xw = new XMLWriter(true, writer); //change to false for unformatted output //xsodata.scenario xw._ns = context.gModel.getNamespace(); xw.startDocument('1.0', 'utf-8', true); xw.startElement('edmx:Edmx'); xw.writeAttribute('Version', '1.0'); xw.writeAttribute( 'xmlns:edmx', 'http://schemas.microsoft.com/ado/2007/06/edmx' ); if (areAnnotationsEnabled) { xw.writeAttribute('xmlns:sap', 'https://www.sap.com/Protocols/SAPData'); } xw.startElement('edmx:DataServices'); xw.writeAttribute('m:DataServiceVersion', '2.0'); xw.writeAttribute( 'xmlns:m', 'http://schemas.microsoft.com/ado/2007/08/dataservices/metadata' ); xw.startElement('Schema'); xw.writeAttribute('Namespace', xw._ns); xw.writeAttribute( 'xmlns:d', 'http://schemas.microsoft.com/ado/2007/08/dataservices' ); xw.writeAttribute( 'xmlns:m', 'http://schemas.microsoft.com/ado/2007/08/dataservices/metadata' ); xw.writeAttribute('xmlns', 'http://schemas.microsoft.com/ado/2008/09/edm'); const entityTypes = context.gModel.getEntityTypes(); const associations = context.gModel.getAssociationsMap(); Measurement.measureSync( writeEntityTypes, xw, entityTypes, associations, areAnnotationsEnabled, 'writeEntityTypes' ); Measurement.measureSync( writeAssociationTypes, xw, associations, 'writeAssociationTypes' ); //Entity Container xw.startElement('EntityContainer'); xw.writeAttribute('Name', 'v2'); xw.writeAttribute('m:IsDefaultEntityContainer', 'true'); writeEntitySets(xw, entityTypes, areAnnotationsEnabled); writeAssociations( xw, context.gModel.getAssociations(), areAnnotationsEnabled ); xw.endElement(); //EntityContainer xw.endDocument(); response.write(xw.toString()); return asyncDone(null, context); }; exports._writeAnnotationAttributes = writeAnnotationAttributes; /** * Writes CSDL annotation attributes to the current EDM element. * * @param {Object} annotations - object with the properties representing the annotation attributes * @param {Object} xmlWriter - XMLWriter instance, which is used for the metadata serialization */ function writeAnnotationAttributes(annotations, xmlWriter) { let annotation; if (!annotations) { return; } for (annotation in annotations) { if (annotations.hasOwnProperty(annotation)) { xmlWriter.writeAttribute(annotation, annotations[annotation]); } } } exports.writeEntityTypes = writeEntityTypes;