UNPKG

generator-partha-btp-capm

Version:
760 lines (740 loc) 37.7 kB
"use strict"; const Generator = require("yeoman-generator"); const path = require("path"); const glob = require("glob"); module.exports = class extends Generator { prompting() { return this.prompt([ { type: "input", name: "projectName", message: "What project name would you like?", validate: (s) => { if (/^[a-zA-Z0-9_-]*$/g.test(s)) { return true; } return "Please only use alphanumeric characters for the project name."; }, default: "app", }, { type: "confirm", name: "newDir", message: "Would you like to create a new directory for this project?", default: true }, { type: "input", name: "displayName", message: "What is the display name of your app?", default: "App", }, { type: "input", name: "description", message: "What is the description of your app?", default: "Business Application", }, { type: "input", name: "schemaName", message: "Will you be using an existing SAP HANA Cloud schema? If so please enter the schema name here or leave blank for none. Note: schema names in mixed case are case sensitive!", default: "" }, { when: response => response.schemaName !== "", type: "input", name: "hanaEndpoint", message: "What is your SAP HANA Cloud SQL endpoint?", default: "<guid>.hana.<region>.hanacloud.ondemand.com:443" }, { when: response => response.schemaName !== "", type: "input", name: "hanaUser", message: "What is your SAP HANA Cloud user name?", default: "DBADMIN" }, { when: response => response.schemaName !== "", type: "password", name: "hanaPassword", message: "What is the password for your SAP HANA Cloud user?", mask: "*", default: "" }, { when: response => response.schemaName !== "", type: "confirm", name: "schemaUPS", message: "Would you like to create the SAP HANA Cloud technical user and User-Provided Service Instance?", default: true }, { type: "confirm", name: "hana", message: "Would you like to create an entity with SAP HANA Cloud persistence?", default: true }, { when: response => response.hana === true, type: "confirm", name: "hanaNative", message: "Would you like to use native SAP HANA Cloud artifacts?", default: true }, { type: "confirm", name: "api", message: "Would you like to use an external API?", default: false }, { when: response => response.api === true, type: "checkbox", name: "apiLoB", message: "Which external API would you like to use?", choices: ["SAP S/4HANA Cloud Sales Order (A2X)", "SAP S/4HANA Cloud Business Partner (A2X)", "SAP SuccessFactors Recruiting"], default: ["SAP S/4HANA Cloud Sales Order (A2X)"], }, { when: response => response.api === true && response.apiLoB.includes("SAP SuccessFactors Recruiting"), type: "input", name: "systemName", message: "What is the System Name for SAP SuccessFactors?", default: "", }, { when: response => response.api === true, type: "input", name: "APIKey", message: "What is your API Key for SAP API Business Hub?", default: "", }, { type: "confirm", name: "authentication", message: "Would you like authentication?", default: false }, { when: response => response.authentication === true, type: "confirm", name: "authorization", message: "Would you like authorization?", default: false }, { when: response => response.authentication === true && response.authorization === true, type: "confirm", name: "attributes", message: "Would you like to use role attributes?", default: false }, { type: "confirm", name: "v2support", message: "Would you like to enable OData v2 support?", default: false }, { type: "confirm", name: "ui", message: "Would you like a UI?", default: false }, { when: response => response.ui === true, type: "confirm", name: "html5repo", message: "Would you like to use the HTML5 Application Repository?", default: false }, { when: response => response.html5repo === true, type: "confirm", name: "managedAppRouter", message: "Would you like to use the managed application router?", default: true }, { type: "input", name: "customDomain", message: "Will you be using a wildcard custom domain (eg: apps.mydomain.com)? If so please enter the custom domain name here. Leave blank to use the platform default.", validate: (s) => { if (s === "") { return true; } if (/^[a-zA-Z0-9.-]*$/g.test(s)) { return true; } return "Please only use alphanumeric characters for the custom domain."; }, default: "", }, { when: response => response.authentication === true && response.schemaName === "" && response.html5repo === false, type: "confirm", name: "multiTenant", message: "Would you like to create a SaaS multitenant app?", default: false }, { when: response => response.multiTenant === true && response.customDomain === "", type: "confirm", name: "routes", message: "Would you like to include creation/deletion of tenant routes on subscribe/unsubscribe (via the CF API)?", default: true, }, { type: "confirm", name: "em", message: "Would you like to enable messaging with SAP Event Mesh?", default: false }, { when: response => response.em === true, type: "input", name: "emNamespace", message: "What messaging namespace would you like? Note: Namespaces must contain exactly three segments and be unique per subaccount.", validate: (s) => { if (/^[a-zA-Z0-9//]*$/g.test(s) && s.split("/").length === 3 && s.substring(0, 1) !== "/" && s.substring(s.length - 1, s.length) !== "/") { return true; } return "Please specify exactly three segments and only use alphanumeric characters for the messaging namespace."; }, default: "company/technology/events", }, { when: response => response.em === true && (response.apiLoB.includes("SAP S/4HANA Cloud Sales Order (A2X)") || response.apiLoB.includes("SAP S/4HANA Cloud Business Partner (A2X)")), type: "input", name: "emClientId", message: "What is the emClientId of your SAP S/4HANA Cloud Extensibility messaging service instance?", validate: (s) => { if (s.length >= 1 && s.length <= 4 && /^[a-zA-Z0-9]*$/g.test(s)) { return true; } return "Please use between 1 and 4 alphanumeric characters for the emClientID."; }, default: "", }, { type: "confirm", name: "cicd", message: "Would you like to enable Continuous Integration and Delivery (CI/CD)?", default: false }, { type: "confirm", name: "applicationLogging", message: "Would you like to enable Application Logging?", default: false }, { type: "confirm", name: "buildDeploy", message: "Would you like to build and deploy the project immediately?", default: false }, ]).then((answers) => { if (answers.newDir) { this.destinationRoot(`${answers.projectName}`); } if (answers.schemaName === "") { answers.hanaEndpoint = ""; answers.hanaUser = ""; answers.hanaPassword = ""; answers.schemaUPS = false; } else { answers.multiTenant = false; } if (answers.hana === false) { answers.hanaNative = false; } if (answers.api === false) { answers.apiLoB = []; answers.APIKey = ""; answers.systemName = ""; } answers.apiS4HCSO = answers.apiLoB.includes("SAP S/4HANA Cloud Sales Order (A2X)"); answers.apiS4HCBP = answers.apiLoB.includes("SAP S/4HANA Cloud Business Partner (A2X)"); answers.apiSFSFRC = answers.apiLoB.includes("SAP SuccessFactors Recruiting"); if (answers.apiSFSFRC === false) { answers.systemName = ""; } if (answers.authentication === false) { answers.authorization = false; answers.multiTenant = false; } if (answers.authentication === false || answers.authorization === false) { answers.attributes = false; } if (answers.ui === false) { answers.html5repo = false; answers.managedAppRouter = false; if (answers.authentication === true) { answers.multiTenant = false; } } if (answers.html5repo === true) { answers.srvPath = ""; answers.multiTenant = false; } else { answers.srvPath = "/"; answers.managedAppRouter = false; } if (answers.multiTenant === false) { answers.routes = false; } if (answers.em === false) { answers.emNamespace = ""; answers.emClientId = ""; } if (answers.apiS4HCSO === false && answers.apiS4HCBP === false) { answers.emClientId = ""; } if (answers.customDomain !== "") { answers.routes = false; } this.config.set(answers); }); } writing() { var answers = this.config; if (answers.get('schemaName') !== "") { // when schema name is lowercase sometimes we need to specify it in uppercase! var schemaNameAdjustedCase = answers.get('schemaName'); if (answers.get('schemaName') === answers.get('schemaName').toLowerCase()) { schemaNameAdjustedCase = answers.get('schemaName').toUpperCase(); } var pwdgen = require('generate-password'); var grantorPassword = pwdgen.generate({ length: 30, numbers: true }); if (answers.get('schemaUPS') === true) { var done = this.async(); } } if (answers.get('cicd') === true) { answers.set('cforg', 'org'); answers.set('cfspace', 'space'); answers.set('cfapi', 'https://api.cf.region.hana.ondemand.com'); // try to identify the targeted api, org & space const res = this.spawnCommandSync('cf', ['target'], { stdio: 'pipe' }); const stdout = res.stdout.toString('utf8'); var field_strings = stdout.split(/[\r\n]*---[\r\n]*/); for (var i = 0; i < field_strings.length; i++) { if (field_strings[i] == '') { continue; } var props_strings = field_strings[i].split('\n'); for (var j = 0; j < props_strings.length; j++) { var keyvalue = props_strings[j].split(':'); if (keyvalue[0].toUpperCase() === 'API ENDPOINT') { answers.set('cfapi', keyvalue[1].trim() + ':' + keyvalue[2].trim()); } else if (keyvalue[0] === 'org') { answers.set('cforg', keyvalue[1].trim()); } else if (keyvalue[0] === 'space') { answers.set('cfspace', keyvalue[1].trim()); } } } } // scaffold the project this.sourceRoot(path.join(__dirname, "templates")); glob .sync("**", { cwd: this.sourceRoot(), nodir: true, dot: true }) .forEach((file) => { if (!(file.includes('.DS_Store'))) { if (!(file === 'dotenv' && answers.get('api') === false)) { if (!(file === 'xs-security.json' && answers.get('authentication') === false && answers.get('api') === false && answers.get('html5repo') === false)) { if (!(file === 'em.json' && answers.get('em') === false)) { if (!((file === 'Jenkinsfile' || file.substring(0, 9) === '.pipeline') && answers.get('cicd') === false)) { if (!(file.substring(0, 19) === 'srv/catalog-service' && answers.get('hana') === false && answers.get('api') === false)) { if (!(file === 'srv/provisioning.js' && answers.get('multiTenant') === false)) { if (!(file === 'srv/server.js' && answers.get('v2support') === false && answers.get('multiTenant') === false)) { if (!(file.substring(0, 32) === 'srv/external/API_SALES_ORDER_SRV' && answers.get('apiS4HCSO') === false)) { if (!(file.substring(0, 33) === 'srv/external/API_BUSINESS_PARTNER' && answers.get('apiS4HCBP') === false)) { if (!((file.substring(0, 25) === 'srv/external/RCMCandidate' || file.includes('map.html')) && answers.get('apiSFSFRC') === false)) { if (!((file.substring(0, 15) === 'app/xs-app.json' || file.substring(0, 16) === 'app/package.json') && (answers.get('managedAppRouter') === true || (answers.get('authentication') === false && answers.get('ui') === false)))) { if (!((file.substring(0, 13) === 'app/resources' || file.includes('i18n') || file.includes('index.cds')) && answers.get('ui') === false)) { if (!((file.substring(0, 19) === 'app/resources/fiori' || file.includes('i18n') || file.includes('index.cds')) && answers.get('hana') === false)) { if (!((file.substring(0, 31) === 'app/resources/fiori/xs-app.json' || file.substring(0, 32) === 'app/resources/fiori/package.json') && answers.get('html5repo') === false)) { if (!((file.substring(0, 31) === 'app/resources/html5/xs-app.json' || file.substring(0, 32) === 'app/resources/html5/package.json' || file.substring(0, 33) === 'app/resources/html5/manifest.json') && answers.get('html5repo') === false)) { if (!(file.substring(0, 2) === 'db' && answers.get('hana') === false && answers.get('schemaName') === "")) { if (!((file.substring(0, 17) === 'db/data-model.cds' || file.substring(0, 7) === 'db/data') && answers.get('hana') === false)) { if (!((file.substring(0, 36) === 'db/data/_PROJECT_NAME_.db.Conditions' || file.substring(0, 34) === 'db/data/_PROJECT_NAME_.db.Customer' || file.substring(0, 32) === 'db/data/_PROJECT_NAME_.db.Status' ) && (answers.get('apiS4HCBP') === false || answers.get('em') === false))) { if (!(file.substring(0, 7) === 'db/src/' && answers.get('hanaNative') === false && answers.get('schemaName') === "")) { if (!(file.substring(0, 20) === 'db/src/_SCHEMA_NAME_' && answers.get('schemaName') === "")) { if (!((file.substring(0, 10) === 'db/src/SP_' || file.substring(0, 10) === 'db/src/TT_') && answers.get('hanaNative') === false)) { const sOrigin = this.templatePath(file); let fileDest = file; if (fileDest.includes('_PROJECT_NAME_')) { console.log('before:', fileDest); fileDest = 'db/data/' + answers.get('projectName') + '.db-' + fileDest.split(".", 3)[2] + '.csv'; console.log('after:', fileDest); } if (fileDest.includes('_SCHEMA_NAME_')) { console.log('before:', fileDest); fileDest = 'db/src/' + answers.get('schemaName') + '.' + fileDest.split(".", 3)[1]; console.log('after:', fileDest); } if (fileDest === 'dotenv') { fileDest = '.env'; } if (fileDest === 'dotgitignore') { fileDest = '.gitignore'; } const sTarget = this.destinationPath(fileDest); this.fs.copyTpl(sOrigin, sTarget, this.config.getAll()); } } } } } } } } } } } } } } } } } } } } } } }); if (answers.get('schemaName') !== "") { var fs = this.fs; var destinationRoot = this.destinationRoot(); var prefix = answers.get('projectName') + '_' + answers.get('schemaName'); var thisgen = this; // connect to HANA to obtain metadata for tables in schema, scaffold project files & create technical user and roles var hana = require('@sap/hana-client') var connOptions = { serverNode: answers.get('hanaEndpoint'), encrypt: 'true', sslValidateCertificate: 'true', ssltruststore: '-----BEGIN CERTIFICATE-----MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBhMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBDQTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsBCSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7PT19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbRTLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUwDQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/EsrhMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJFPnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0lsYSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQkCAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=-----END CERTIFICATE-----', uid: answers.get('hanaUser'), pwd: answers.get('hanaPassword') }; let connection = hana.createConnection(); connection.connect(connOptions, function (err) { if (err) { return console.error(err); } let sql = "SELECT 'T' AS object_type, table_name as object_name FROM tables WHERE schema_name='" + schemaNameAdjustedCase + "' AND is_system_table='FALSE' AND is_temporary='FALSE' UNION SELECT 'V' AS object_type, view_name as object_name FROM views WHERE schema_name='" + schemaNameAdjustedCase + "'"; connection.exec(sql, function (err, resObjects) { if (err) { return console.error(err); } if (resObjects.length < 1) { return console.error("No tables or views found in schema " + answers.get('schemaName')); } // create synonyms let hdbSynonym = "{"; let i = 0; resObjects.forEach(element => { if (i) hdbSynonym += ","; hdbSynonym += '"' + answers.get('projectName') + '.db::' + answers.get('schemaName') + '.' + element.OBJECT_NAME + '": {"target": {"object":"' + element.OBJECT_NAME + '","schema":"' + schemaNameAdjustedCase + '"}}'; i++; }); hdbSynonym += "}"; sql = "SELECT 'T' AS object_type, table_name as object_name, column_name, data_type_name, length, scale, is_nullable, default_value, position FROM table_columns WHERE schema_name='" + schemaNameAdjustedCase + "' AND table_name IN(''"; resObjects.forEach(element => { if (element.OBJECT_TYPE === "T") { sql += ",'" + element.OBJECT_NAME + "'"; } }); sql += ") UNION SELECT 'V' AS object_type, view_name as object_name, column_name, data_type_name, length, scale, is_nullable, default_value, position FROM view_columns WHERE schema_name='" + schemaNameAdjustedCase + "' AND view_name IN(''"; resObjects.forEach(element => { if (element.OBJECT_TYPE === "V") { sql += ",'" + element.OBJECT_NAME + "'"; } }); sql += ") ORDER BY object_name, position"; connection.exec(sql, function (err, resColumns) { if (err) { return console.error(err); } // create hdbview for each object var hdbViews = []; resObjects.forEach(elementO => { let hdbView = "VIEW " + answers.get('projectName').toUpperCase() + "_DB_" + answers.get('schemaName').toUpperCase() + "_" + (elementO.OBJECT_NAME).toUpperCase() + " AS SELECT"; let i = 0; resColumns.forEach(elementC => { if (elementC.OBJECT_NAME === elementO.OBJECT_NAME) { if (i) hdbView += ","; if (elementC.COLUMN_NAME !== (elementC.COLUMN_NAME).toUpperCase()) { hdbView += ' "' + elementC.COLUMN_NAME + '" AS ' + (elementC.COLUMN_NAME).toUpperCase(); } else { hdbView += " " + elementC.COLUMN_NAME; } i++; } }); hdbView += ' FROM "' + answers.get('projectName') + '.db::' + answers.get('schemaName') + '.' + elementO.OBJECT_NAME + '"'; var view = { "NAME": elementO.OBJECT_NAME, "VIEW": hdbView }; hdbViews.push(view); }); let sql = "SELECT table_name as object_name, column_name, position, is_primary_key FROM constraints WHERE schema_name='" + schemaNameAdjustedCase + "' AND table_name IN(''"; resObjects.forEach(element => { if (element.OBJECT_TYPE === "T") { sql += ",'" + element.OBJECT_NAME + "'"; } }); sql += ") ORDER BY table_name, position"; connection.exec(sql, function (err, resConstraints) { if (err) { return console.error(err); } // create facade entity for each view let schemaCDS = "namespace " + answers.get('projectName') + ".db; context " + answers.get('schemaName') + " {"; resObjects.forEach(elementO => { schemaCDS += " @cds.persistence.exists entity " + elementO.OBJECT_NAME; /* view parameters not supported yet: need to include in db/src/.hdbview, db/<schemaName>.cds and srv/<schemaName>-service.cds //let sql = "SELECT view_name as object_name, parameter_name, data_type_name, length, scale, has_default_value, position FROM view_parameters WHERE schema_name='" + schemaNameAdjustedCase + "' AND view_name IN("; // add any view parameters i = 0; resParameters.forEach(elementP => { this.log(elementP.OBJECT_NAME,elementO.OBJECT_NAME); if (elementP.OBJECT_NAME === elementO.OBJECT_NAME) { if (i) { schemaCDS += ","; } else { schemaCDS += " ("; } schemaCDS += elementP.PARAMETER_NAME + " : "; switch (elementP.DATA_TYPE_NAME) { case "BOOLEAN": schemaCDS += "Boolean"; break; case "TINYINT": case "SMALLINT": case "INTEGER": schemaCDS += "Integer"; break; case "BIGINT": schemaCDS += "Integer64"; break; case "REAL": case "DOUBLE": schemaCDS += "Double"; break; case "SMALLDECIMAL": case "DECIMAL": schemaCDS += "Decimal(" + elementP.LENGTH + "," + elementP.SCALE + ")"; break; case "DATE": schemaCDS += "Date"; break; case "TIME": schemaCDS += "Time"; break; case "TIMESTAMP": schemaCDS += "Timestamp"; break; case "CHAR": case "NCHAR": case "CLOB": case "VARCHAR": case "NVARCHAR": schemaCDS += "String(" + elementP.LENGTH + ")"; break; case "VARBINARY": case "BINARY": schemaCDS += "Binary(" + elementP.LENGTH + ")"; break; case "BLOB": schemaCDS += "LargeBinary"; break; case "NCLOB": schemaCDS += "LargeString"; break; default: schemaCDS += elementP.DATA_TYPE_NAME; } i++; } }); if (i) schemaCDS += ")"; */ schemaCDS += " {"; i = 0; resColumns.forEach(elementC => { if (elementC.OBJECT_NAME === elementO.OBJECT_NAME) { resConstraints.forEach(elementI => { if (elementI.OBJECT_NAME === elementC.OBJECT_NAME && elementI.COLUMN_NAME === elementC.COLUMN_NAME && elementI.IS_PRIMARY_KEY === "TRUE") { schemaCDS += " key"; } }); // unclear how to determine key for views - assume first column if (elementC.OBJECT_TYPE === "V" && i === 0) { schemaCDS += " key"; } i++; schemaCDS += " " + (elementC.COLUMN_NAME).toLowerCase() + " : "; switch (elementC.DATA_TYPE_NAME) { case "BOOLEAN": schemaCDS += "Boolean"; break; case "TINYINT": case "SMALLINT": case "INTEGER": schemaCDS += "Integer"; break; case "BIGINT": schemaCDS += "Integer64"; break; case "REAL": case "DOUBLE": schemaCDS += "Double"; break; case "SMALLDECIMAL": case "DECIMAL": schemaCDS += "Decimal(" + elementC.LENGTH + "," + elementC.SCALE + ")"; break; case "DATE": schemaCDS += "Date"; break; case "TIME": schemaCDS += "Time"; break; case "TIMESTAMP": schemaCDS += "Timestamp"; break; case "CHAR": case "NCHAR": case "CLOB": case "VARCHAR": case "NVARCHAR": schemaCDS += "String(" + elementC.LENGTH + ")"; break; case "VARBINARY": case "BINARY": schemaCDS += "Binary(" + elementC.LENGTH + ")"; break; case "BLOB": schemaCDS += "LargeBinary"; break; case "NCLOB": schemaCDS += "LargeString"; break; default: schemaCDS += elementC.DATA_TYPE_NAME; } if (elementC.IS_NULLABLE === "FALSE") { schemaCDS += " not null"; } if (elementC.DEFAULT_VALUE != null) { schemaCDS += " default "; switch (elementC.DATA_TYPE_NAME) { case "BOOLEAN": case "BIGINT": case "DOUBLE": case "DECIMAL": case "SMALLINT": case "TINYINT": case "SMALLDECIMAL": case "REAL": case "INTEGER": schemaCDS += elementC.DEFAULT_VALUE; break; default: schemaCDS += "'" + elementC.DEFAULT_VALUE + "'"; } } schemaCDS += ";"; } }); schemaCDS += "};"; }); schemaCDS += "}"; // create service let serviceCDS = "using {" + answers.get('projectName') + ".db." + answers.get('schemaName') + " as " + answers.get('schemaName') + "} from '../db/" + answers.get('schemaName') + "'; service " + answers.get('schemaName') + "Service @(path : '/" + answers.get('schemaName') + "')"; if (answers.get('authentication')) { serviceCDS += " @(requires:'authenticated-user')"; } serviceCDS += " {"; resObjects.forEach(element => { serviceCDS += " entity " + element.OBJECT_NAME; if (answers.get('authorization')) { serviceCDS += " @(restrict: [{ grant: 'READ', to: 'Viewer' }, { grant: 'WRITE', to: 'Admin' } ])"; } if (element.OBJECT_TYPE === "V") { serviceCDS += " as select * from " + answers.get('schemaName') + "." + element.OBJECT_NAME + ";"; } else { serviceCDS += " as select * from " + answers.get('schemaName') + "." + element.OBJECT_NAME + ";"; } }); serviceCDS += "};"; // scaffold project files fs.write(destinationRoot + "/db/src/" + answers.get('schemaName') + ".hdbsynonym", hdbSynonym); hdbViews.forEach(element => { fs.write(destinationRoot + "/db/src/" + answers.get('schemaName') + "-" + element.NAME + ".hdbview", element.VIEW); }); fs.write(destinationRoot + "/db/" + answers.get('schemaName') + ".cds", schemaCDS); fs.write(destinationRoot + "/srv/" + answers.get('schemaName') + "-service.cds", serviceCDS); // define SAP HANA Cloud technical user, roles and grants thisgen.log('Syntax to create the SAP HANA Cloud technical user, roles and grants:'); const sql1 = 'CREATE USER ' + prefix + '_GRANTOR PASSWORD ' + grantorPassword + ' NO FORCE_FIRST_PASSWORD_CHANGE'; thisgen.log(sql1 + ";"); const sql2 = 'CREATE ROLE "' + prefix + '::EXTERNAL_ACCESS_G"'; thisgen.log(sql2 + ";"); const sql3 = 'CREATE ROLE "' + prefix + '::EXTERNAL_ACCESS"'; thisgen.log(sql3 + ";"); const sql4 = 'GRANT "' + prefix + '::EXTERNAL_ACCESS_G", "' + prefix + '::EXTERNAL_ACCESS" TO ' + prefix + '_GRANTOR WITH ADMIN OPTION'; thisgen.log(sql4 + ";"); const sql5 = 'GRANT SELECT,INSERT,UPDATE,DELETE ON SCHEMA "' + schemaNameAdjustedCase + '" TO "' + prefix + '::EXTERNAL_ACCESS_G" WITH GRANT OPTION'; thisgen.log(sql5 + ";"); const sql6 = 'GRANT SELECT,INSERT,UPDATE,DELETE ON SCHEMA "' + schemaNameAdjustedCase + '" TO "' + prefix + '::EXTERNAL_ACCESS"'; thisgen.log(sql6 + ";"); thisgen.log(""); // define user-provided service instance // we take this approach instead of writing to mta.yaml to avoid the SAP HANA Cloud technical user & password being visible in project source files const cupsParams = '{"user":"' + prefix + '_GRANTOR","password":"' + grantorPassword + '","schema":"' + schemaNameAdjustedCase + '","tags":["hana"]}'; thisgen.log('Syntax to create the User-Provided Service Instance:'); thisgen.log('cf cups ' + answers.get('projectName') + '-db-' + answers.get('schemaName') + " -p '" + cupsParams + "'"); thisgen.log(""); // create HANA technical user and roles only when requested if (answers.get('schemaUPS') === true) { connection.exec(sql1, function (err, result) { if (err) { return console.error(err); } connection.exec(sql2, function (err, result) { if (err) { return console.error(err); } connection.exec(sql3, function (err, result) { if (err) { return console.error(err); } connection.exec(sql4, function (err, result) { if (err) { return console.error(err); } connection.exec(sql5, function (err, result) { if (err) { return console.error(err); } connection.exec(sql6, function (err, result) { if (err) { return console.error(err); } connection.disconnect(function (err) { done(err); thisgen.spawnCommandSync('cf', ['cups', answers.get('projectName') + '-db-' + answers.get('schemaName'), '-p', cupsParams]); }); }); }); }); }); }); }); } }); }); }); }); } answers.delete('hanaUser'); answers.delete('hanaPassword'); answers.delete('APIKey'); } install() { // build and deploy if requested var answers = this.config; var mta = "mta_archives/" + answers.get("projectName") + "_0.0.1.mtar"; if (answers.get("buildDeploy")) { let opt = { "cwd": this.destinationPath() }; let resBuild = this.spawnCommandSync("mbt", ["build"], opt); if (resBuild.status === 0) { this.spawnCommandSync("cf", ["deploy", mta], opt); } } else { this.log(""); this.log("You can build and deploy your project from the command line as follows:"); this.log(" cd " + answers.get("projectName")); this.log(" mbt build"); this.log(" cf deploy " + mta); } } end() { this.log(""); if (this.config.get('customDomain') !== "" && this.config.get('multiTenant')) { this.log("Important: The wildcard custom domain route needs be mapped via the following CF CLI command after deployment:"); this.log(" cf map-route " + this.config.get('projectName') + " " + this.config.get('customDomain') + ' --hostname "*"'); } if (this.config.get('routes')) { let projectName = this.config.get('projectName'); this.log("Important: The CF API is being used so please be sure to issue the following CF CLI commands after deployment:"); this.log(" cf set-env " + projectName + "-srv cf_api_user '<email>'"); this.log(" cf set-env " + projectName + "-srv cf_api_password '<password>'"); this.log(" cf restage " + projectName + "-srv"); } if (this.config.get('multiTenant') && this.config.get('api')) { this.log("Don't forget to configure the destination for each subscriber."); } this.log(""); } };