Skip to Content
Technical Articles
Author's profile photo Ali Asad

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.

  1. 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.
  2. 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.html

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

 

Assigned Tags

      5 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Mustafa Bensan
      Mustafa Bensan

      Hi Ali,

      Thanks for this real-world use case for a multitenant application.  I have a couple of questions as follows:

      1. How would the configuration you have specified differ in the case of using the Launchpad Service instead of the Portal Service?
      2. How is a user store implemented in this scenario?  Would we need to implement IAS as a dependency?

      Regards,

      Mustafa.

      Author's profile photo Gregor Wolf
      Gregor Wolf

      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

      Author's profile photo Mustafa Bensan
      Mustafa Bensan

      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.

      Author's profile photo Gregor Wolf
      Gregor Wolf

      Correct.

      Author's profile photo Gregor Wolf
      Gregor Wolf

      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