@sap/xsodata
Version:
Expose data from a HANA database as OData V2 service with help of .xsodata files.
284 lines (218 loc) • 9.77 kB
JavaScript
;
var dbutils = require('./../utils/typeConverter');
var typConv = require('./../utils/typeConverter');
var XMLWriter = require('xml-writer');
var contentTypeTools = require('./../utils/checkContentType');
var associationsUtil = require('./../utils/associations');
var Measurement = require('./../utils/measurement');
function writeNavProperty(xw, navigation, associations, entityType, areAnnotationsEnabled) {
xw.startElement('NavigationProperty');
xw.writeAttribute('Name', navigation.name);
var assocName = navigation.association;
var 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');
}
var 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 (var assocName in associations) {
if (associations.hasOwnProperty(assocName)) {
var 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) {
var i, ii, j;
/**
* @type {EntityType}
* */
var 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();
var props = entityType.getPropertiesWithGenKey();
for (j = 0; j < props.length; j++) {
writeProp(props[j], entityType);
}
//nav properties
for (ii = 0; ii < entityType.navProperties.length; ii++) {
var 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 (var i = 0; i < entityTypes.length; i++) {
var entityType = entityTypes[i];
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 (var i = 0; i < associations.length; i++) {
var assocication = associations[i];
xw.startElement('AssociationSet');
xw.writeAttribute('Name', assocication.name);
xw.writeAttribute('Association', xw._ns + '.' + assocication.name + 'Type');
if (areAnnotationsEnabled) {
writeAnnotationAttributes(assocication.getAssociationSetAnnotations(), xw);
}
xw.startElement('End');
xw.writeAttribute('Role', assocication.principal.type + 'Principal');
xw.writeAttribute('EntitySet', assocication.principal.type);
xw.endElement();
xw.startElement('End');
xw.writeAttribute('Role', assocication.dependent.type + 'Dependent');
xw.writeAttribute('EntitySet', assocication.dependent.type);
xw.endElement();
xw.endElement();//AssociationSet
}
}
exports.process = function (context, asyncDone) {
var 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');
var header = {
'Content-Type': context.payloadType + ';charset=utf-8'
};
var cacheControl = context.gModel.getSetting('metadata','cache-control');
if (cacheControl) {
header['cache-control'] = cacheControl;
}
response.writeHead(200, header);
var writer = function writer(string) {
response.write(string);
};
var 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', 'http://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');
var entityTypes = context.gModel.getEntityTypes();
var 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) {
var annotation;
if (!annotations) {
return;
}
for (annotation in annotations) {
if (annotations.hasOwnProperty(annotation)) {
xmlWriter.writeAttribute(annotation, annotations[annotation]);
}
}
}
exports.writeEntityTypes = writeEntityTypes;