Technical Articles
Multitenant MTA: providing a Cloud Portal Service with FIORI apps to subscriber accounts
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/
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
- html5-host
- Portal Deployer
- xsuaa, Html5-host, Saas
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-cap/
Github repo used to create project scaffolding: https://github.com/sbarzaghialteaup/cap-multitenant-portal
Hi Ali,
Thanks for this real-world use case for a multitenant application. I have a couple of questions as follows:
Regards,
Mustafa.
Hi Mustafa,
reagarding 1):
Please check out my question Launchpad Module also with Launchpad Service?. Right now it seems not to be possible to use the Launchpad Service for a Launchpad Module.
regarding 2):
The users are managed in the subscriber subaccounts. You can configure your own IdP there. IAS helps especially with Azure AD (and also Azurea AD B2C) as it makes the integration a lot easier.
Best Regards
Gregor
Hi Gregor,
Thanks for your feedback. About Point 2, if there is no Corporate IdP like Azure AD, then I assume IAS can be used directly as an IdP and user store rather than just as a proxy to a Corporate IdP like Azure AD?
Regards,
Mustafa.
Correct.
Hi Ali,
thank you for this post. Maybe you find way how I've implemented the content for the destinations call interresting: Documentation: Deploy SaaS Solutions (MTX) - Dependencies to Destination / Connectivity Service.
CU
Gregor