For multitenant application, there is usually a requirement to enable full-fledged solution of Fiori apps in portal or launchpad service. Information available in various documentations and help seems to be scattered, and therefore I decided to provide a one place solution to the approach.
CAPM model and the basic file structure is from the following GitHub repository:
https://github.com/sbarzaghialteaup/cap-multitenant-portal
Project structure
Following is the project structure.
Multitenant SaaS Application
for SaaS applications, first step is to add dependency in package.json of the root folder.
After dependencies have been added, server.js file has to be created in the root folder which will basically attach custom implementation of provisioning service as shown below.
const cds = require("@sap/cds");
cds.on("bootstrap", async (app) => {
await cds.mtx.in(app);
const provisioning = await cds.connect.to("ProvisioningService");
provisioning.impl(require("./srv/saas-provisioning/provisioning"));
});
module.exports = cds.server;
within the implementation of provisioning service, there are two event handlers.
- triggered during the onboarding process of new tenant, and it will create tenant specific HDI container and return the URL of the app-router with tenant specific domain.
- Triggered also during the onboarding process and it will be used to return a list of re-use services (in our case Cloud Portal) to the Provisioning service in JSON format. For further information, please refer to the following blog post and to the documentation
https://blogs.sap.com/2021/02/18/understanding-dependencies-in-saas-provisioning/
https://help.sap.com/products/BTP/65de2977205c403bbc107264b8eccf4b/2cd8913a50bc4d3e8172f84bb4bfba20....
Following code snippet is for provisioning.js file served from the path ./srv/saas-provisioning/provisioning
module.exports = (service) => {
service.on("UPDATE", "tenant", async (req, next) => {
const res = await next();
console.log(JSON.stringify(req.data));
//TODO
let url = `https://${req.data.subscribedSubdomain}-portall-${req.data.subscriptionAppName}.cfapps.eu10.hana.ondemand.com`; //replace with space name of your provider subaccount
return url;
});
service.on("dependencies", (req) => {
const xsenv = require("@sap/xsenv");
const services = xsenv.getServices({
portal: {
tag: "portal" // to ensure this works, make sure to bind portal service to CAP service instance
}
});
const dependencies = [{
xsappname: services.portal.uaa.xsappname
}];
return dependencies;
});
};
please note that "portall" is the name of space, used as a hardcoded string in the above snippet. This however needs to be replaced with space name where the application is deployed. It in-turn is needed because tenant host pattern configured in mta.yaml has the following pattern.
"^(.*)-${space}-${app-name}.${default-domain}"
Portal Deployer
portal deployer module needs to be created that will have a portal site which basically contains configuration for the portal in CommonDataModel.json file along with i18n translations. Note that no package.json file is needed here, as this will be deployed using GACD module.
MTA Deployment Descriptor
To make the whole process working, mta.yaml file needs to have following modules:
- CAP service
- provides: cap-service
- requires: Service-Manager, Saas, Portal, Xsuaa
- App router
- requires: html5-runtime, Portal, Saas, Xsuaa, Cap Service.
- HTML5 Deployer
- Portal Deployer
with following resources:
- saas registry
- xsuaa
- service manager
- html5 runtime & host
- Portal service
Wrap up & key takeaways
Now you just need to deploy the application in provider subaccount. In subscriber account, user needs to create a subscription to the app. After subscription is completed, you would need to create a route for customer subdomain and bind it the approuter. This step is completed in the space where application is deployed. You can also create this route in provisioning event handler to reduce manual step but for understanding purpose, it hasn't been done yet.
All the necessary steps are now completed to make a multitenant portal available in subscriber account and key takeaways are:
- cds-mtx libraries needs to be added as depedency.
- Server.js file is needed to attach provisioning service endpoints.
- Event handler getDependencies and onSubscription needs to be implemented.
- Saas Registry service needs to be binded to approuter, portal and cap service.
- launchpad module needs to be created using fiori tools which has configuration for needed Fiori apps
Complete github repo of the project is available on the following link:
https://github.com/aliasad466/Multitenant-cap.git
References
To understand depedencies:
https://blogs.sap.com/2021/02/18/understanding-dependencies-in-saas-provisioning/
To understand Multitenant Saas Application:
https://blogs.sap.com/2021/05/19/multitenant-application-using-cloud-application-programming-model-c...
Github repo used to create project scaffolding:
https://github.com/sbarzaghialteaup/cap-multitenant-portal