Technology Blogs by SAP
Learn how to extend and personalize SAP applications. Follow the SAP technology blog for insights into SAP BTP, ABAP, SAP Analytics Cloud, SAP HANA, and more.
cancel
Showing results for 
Search instead for 
Did you mean: 
gopalanand
Product and Topic Expert
Product and Topic Expert
In this blog you will see how to implement the following concepts of Multitenancy:

  • Creating tenants

  • Deleting tenant

  • Dependency callbacks

  • Creating and updating the database schema and deploying the artefacts for the tenant.


Our reference application has a microservice to handle onboarding requests. This helps in isolating the responsibilities and scaling the application as per demand.

Onboarding service

SAP Software-as-a-Service Provisioning service


This service helps you manage your subscriptions to multitenant applications and their dependent services. Operations include getting registration details, subscribing to an application, unsubscribing applications, retrieving all your application subscriptions, and updating subscription dependencies.

SaaS provisioning service requires Tenant Registration callbacks to be implemented along with a dependency callback. Additionally, a tenant schema must be created. In case of an update, the tables in the database should be updated for each of these tenants.


The Onboarding service has the following features:




  • Tenant Registry callback

  • getDependencies callback

  • Creating HDI Container and deploying the database artefacts

  • Creating the approuter


Tenant Registry Callback


The Tenant Registry callback is used to register the tenant with the SaaS Provisioning service. Once registered, the application's URL is returned to the subscriber account. You can find the Tenant Registry callback logic in the saasRoute.js#L20-L105 file.



/callback/v1.0/tenants/*

This endpoint is implemented and called when the Create Subscription button is clicked.

The above implementation creates HDI-Container for the subscribing tenant using Service Manager and fills the tenant information table with subscription information which can be later used for operational use cases

getDependencies Callback


If the application uses services such as the Destination and the Connectivity services, the getDependencies callback is used to declare those dependencies in the SaaS provisioning service. Any URL that the application exposes for GET dependencies. It should not be declared if the application does not have dependencies and the callback is not implemented. Note: The JSON response of the callback must be encoded either with UTF8, UTF16, or UTF32. Otherwise, an error is returned. Important: You can either provide your getDependencies callback or use the default implementation of the approuter (recommended if no special logic is needed). If you provide your own implementation, you have to make sure that the ABAP Solution instance is returned as a dependency. The path is: /callback/v1.0/dependencies You can find the code in the dependencyRouter.js#L13-L31 file.



  saasRoute.route('/callback/v1.0/dependencies')
.get((req, res) => {
const logger = req.loggingContext.getLogger('/Application');
try {
const dependencies = [];
dependencies.push({ xsappname: services.connectivity.xsappname });
dependencies.push({ xsappname: services.destination.xsappname });
dependencies.push({ xsappname: services.jobscheduler.uaa.xsappname });
// Creating dependency to be injected into saas provisioning service
logger.info(dependencies);
res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify(dependencies));
} catch (err) {
logger.error(err);
res.status(500).send(err);
}
});

Creating HDI Containers


For a multitenant application, secure segregation of data is critical. This application uses HANA database with HDI container-based data isolation.
Each tenant has its own HDI container which is created and deployed during the provisioning. In this application, the @sap/instance-manager package is used to create and manage the HDI container. The instance manager package provides credentials caching which helps in quick access of HDI credentials when the tenants are trying to access data.
The instance manager logic to create HDI containers is implemented in the instancemanager file.



const createHDI = (tenantId, logger) => new Promise((resolve, reject) => {
createInstanceManager(options, async (err, instanceManager) => {
if (err) {
logger.error(err);
reject(err);
}
instanceManager.create(tenantId, {}, async (err2, instance) => {
if (err2) {
logger.error('Create error:', err2.message);
reject(err2)
}
if(!err2) {
resolve(instance);
}
});
});
});

Deploying the Database Artifacts (Tables)


The database artefacts are deployed in  HDI container during the provisioning, after the HDI container creation has been finished. The database artefacts are part of the  Database Microservice. The Database service requires basic authentication along with the HDI container credentials to deploy tenant-specific artefacts. You can find the code in the dbHandler.js file. Here's the function which is used to deploy the database artefacts:




async (dbCredentials, tenantId, logger) => {
try {
Object.assign(dbCredentials, { id: tenantId });
const options = {
method: 'POST',
data: dbCredentials,
url: `${process.env.db_api_url}/v1/deploy/to/instance`,
headers: {
Authorization: `Basic ${Buffer.from(`${process.env.db_api_user}:${process.env.db_api_password}`).toString('base64')}`,
'Content-Type': 'application/json',
},
};
const res = await axios(options);
logger.info(`HDI Container created for Tenant ${tenantId}`);
return res;
} catch (err) {
logger.error(err);
return err;
}


🎉 Well now the tenant onboarding has been completed, and tenant-specific schema has been created. Dependent services are also added as part of the subscription. Now let's see how Business Services work in a multi-tenant application.

Part 3: Accessing tenant-specific data and fetching S/4HANA backend data from subscriber subaccounts...


Further Read:


Other multitenant applications and Missions: