UNPKG

@sap/cds-dk

Version:

Command line client and development toolkit for the SAP Cloud Application Programming Model

154 lines (138 loc) 5.94 kB
"use strict"; let edmxv2CSN = require("./v2parser"); let edmxv4CSN = require("./v4parser"); const messages = require("../message").getMessages(); let common = require("../common"); const { path } = require('../../cds').utils; let namespaceFileObject = {}; function getNamespaces(context) { if (context.include_namespaces === "*") context.include_all_namespaces = true; else if (typeof context.include_namespaces === 'string' && !context.include_namespaces.startsWith('-')) context.namespaces = context.include_namespaces.replace(/ /g, "").split(','); else throw new Error(messages.INCORRECT_INPUT_NAMESPACES); } function _convertEDMXWithMultipleSchemas(edmx, multipleSchemas, namespace) { /* This is a quick hack only handling the following case we saw from SFSF: <edmx:DataServices> <Schema Namespace="SFODataSet"> <EntityContainer> <EntitySet Name="Foos" EntityType="SFOData.Foo"> ... </EntitySet> </EntityContainer> </Schema> <Schema Namespace="SFOData"> <EntityType Name="Foo"> ... </EntityType> </Schema> </edmx:DataServices> which is turned into that: <edmx:DataServices> <Schema Namespace="SFOData"> <EntityContainer> <EntitySet Name="Foos" EntityType="SFOData.Foo"> ... </EntitySet> </EntityContainer> <EntityType Name="Foo"> ... </EntityType> </Schema> </edmx:DataServices> */ // Remove the ending and beginning of the first and second schema tag edmx = edmx.replace(multipleSchemas, ''); // Now capture the schema tag details let schemaDetails = edmx.match(/(<Schema [^>]*>)/); // Replace the Namespace value with the correct one schemaDetails = schemaDetails[0].replace(/Namespace="[^"]+"/, `Namespace="${namespace}"`); // Place the above computed schema details in the edmx content edmx = edmx.replace(/(<Schema [^>]*>)/, `${schemaDetails}`); return edmx; } async function edmx2csn(edmx, filename, context) { let namespace; // we might get aliased namespace value here const result = edmx.match(/EntityType="([^"]+)/); const multipleSchemas = /(\s*<\/Schema>\s*<Schema [^>]*>)\s*/m; if (result) { const index = result[1].lastIndexOf("."); namespace = (index > 0) ? result[1].substring(0, index) : result[1]; } else { // to support edmx without entities - unbound actions and functions only console.warn('There are no entities in the OData model'); let schema = edmx.match(/(<Schema [^>]*>)/); if (!schema) throw new Error(messages.INVALID_EDMX_METADATA); schema = schema[0].match(/Namespace="[^"]+"/); namespace = schema[0].slice(schema[0].lastIndexOf('Namespace') + 11, schema[0].lastIndexOf('"')); } // this hack is only used for OData V2, for V4 we support multiple schema scenario if (context.odataVersion === 'V2' && multipleSchemas.test(edmx)) { edmx = _convertEDMXWithMultipleSchemas(edmx, multipleSchemas, namespace); } let csn = await _generateCSN(edmx, false, true, context); if (filename) { // Regex checks for this condition “A-B” must start with a letter or underscore, followed by at most 127 letters, underscores or digits in the fileName const regex = /^[\p{Letter}\p{Nl}_]{1}[_\p{Letter}\p{Nl}\p{Nd}\p{Mn}\p{Mc}\p{Pc}\p{Cf}]{0,127}$/gu /** * V4: <Schema Namespace="Full.Schema.Name" xmlns="http://docs.oasis-open.org/odata/ns/edm" Alias="aliasedValue"> * V2: <Schema Namespace="Full.Schema.Name" xmlns="http://schemas.microsoft.com/ado/2008/09/edm" Alias="aliasedValue"> * * context.schemaNamespace is set to correct namespace value by the respective parsers */ if (context.schemaNamespace) { namespaceFileObject[context.schemaNamespace] = filename; } else { namespaceFileObject[namespace] = filename; } for (const [namespace, filename] of Object.entries(namespaceFileObject)) { // Replace namespace by given filename in the csn file instead of edmx file const re = RegExp(`([("/])${namespace}([/").])`, 'g'); let name = path.parse(filename).name; if (!(name.match(regex))) { // check if the filename is in the right format with the regex if not replace unaccepted characters with underscore (_) name = name.replace(/^[^a-z\p{Nl}_]/gui, '_'); name = name.replace(/[^a-z0-9_\p{Nl}\p{Nd}\p{Mn}\p{Mc}\p{Pc}\p{Cf}]/gui, '_'); } const fn = `$1${name}$2`; csn = csn.replace(re, fn); } } return JSON.parse(csn); } async function _generateCSN(edmx, ignorePersistenceSkip, mockServerUc, context) { let csn; let edmx2jsonModel; let defaultNamespaces; let namespaces = []; if (context.namespaces) namespaces.push(...(context.namespaces)); if (context.odataVersion === "V2") { // default namespaces to be present if (!context.include_all_namespaces) { defaultNamespaces = ['edmx', 'sap', 'm']; namespaces = namespaces.concat(defaultNamespaces); namespaces = namespaces.filter((item, pos) => namespaces.indexOf(item) === pos); //removing duplicate values } context.namespaces = namespaces; edmx2jsonModel = await common.generateEDMX2JSON(edmx); return new Promise(function getCsn(resolve, reject) { csn = edmxv2CSN.getEdmxv2CSN(edmx2jsonModel, ignorePersistenceSkip, mockServerUc, context); if (csn) resolve(csn); else reject("Error Occurred"); }); } else if (context.odataVersion === 'V4') { // default namespaces to be present if (!context.include_all_namespaces) { defaultNamespaces = ['edmx']; namespaces = namespaces.concat(defaultNamespaces); namespaces = namespaces.filter((item, pos) => namespaces.indexOf(item) === pos); //removing duplicate values } context.namespaces = namespaces; return new Promise(function(resolve, reject) { csn = edmxv4CSN.getEdmxv4CSN(edmx, ignorePersistenceSkip, mockServerUc, context); if (csn) resolve(csn); else reject("Error Occurred"); }); } return new Promise(function invalidFile(resolve, reject) { reject(new Error(messages.INVALID_EDMX_METADATA)); }); } module.exports = { getNamespaces, edmx2csn };