Search

Multitenancy

How to implement multitenant aware applications (SaaS applications) with CAP.

Content

How It Works

SAP Cloud Platform exposes a method for implementing and provisioning SaaS applications on Cloud Foundry. To effectively operate and run SaaS applications and to make them economically successful (TCO), such applications can be deployed once by the application provider, and then be shared by many customers. Such sharing of applications and related compute resources between many tenants is addressed by the multitenancy capabilities of SAP Cloud Platform. One important aspect is to guarantee separation between different tenants, so that a tenant isn’t allowed to access any data of other tenants. Apart from data separation, Identity and Access Management must be isolated between tenants.

These SAP Cloud Platform concepts are described here:

CAP supports application developers in implementing the APIs required by SAP Cloud Platform. If the application is using SAP HANA database, multitenancy can come out of the box. CAP features provided:

  • Expose endpoints for un-/subscribing tenants, for returning subscription dependencies and for tenant database updates
  • Out of the box default implementation for un-/subscribing using SAP HANA HDI containers
  • Out of the box default implementation for tenant database updates
  • Application can implement handlers for its own un-/subscribing logic
  • Tenant-specific routing of service requests → use tenant-specific metadata and tenant-specific database connection

The cds-mtx Module

CAP provides the npm module for Node.js published as @sap/cds-mtx on npmjs.com.:

Java SaaS application deployment

It provides a number of APIs for implementing SaaS applications on SAP Cloud Platform. All APIs are based on CDS. They can be exposed through plain REST, and/or consumed by other Node.js modules when running on the same server as cds-mtx.

  • provisioning: implements the subscription callback API as required by SAP Cloud Platform. If a tenant is subscribing to the SaaS application, the onboarding request is handled. cds-mtx is contacting the SAP HANA Service Manager service to create a new HDI container for the tenant. Then, database artifacts get deployed into this HDI container. In addition, the unsubscribe operation and the “get dependencies” operations are supported.

  • metadata: can be used to get CSN- and EDMX-models, get a list of available services and languages.

  • model: is used to extend existing CDS models and to perform tenant model upgrades after having pushed a new version of the SaaS application.

To expose these APIs as plain REST API, embed the cds-mtx module into a Node.js server together with cds. Define a server start script (for example, server.js) as follows:

const cds = require ('@sap/cds')

cds.on('bootstrap',(app) => {
    cds.mtx.in(app)            // serve cds-mtx APIs
});

// Delegate bootstrapping to built-in server.js
module.exports = cds.server

Configuration

The cds-mtx module follows the CDS configuration method for the CAP Node.js stack. This means, configuration can be put into the package.json file, or into .cdsrc.json. The following example shows a typical .cdsrc.json file with cds-mtx relevant configuration:

{
    "mtx": {
        "element-prefix": ["Z_", "ZZ_"],
        "namespace-blacklist": ["com.sap.", "sap."],
        "entity-whitelist": ["my.bookshop.Books", "<another entity>", ...],
        "service-whitelist": ["AdminService", "<another service>", ...]
    },
    "odata": {"version": "v4"},
    "requires": {
        "db": {
            "kind": "hana",
            "model": ["db", "srv"],
            "multiTenant": true,
            "vcap": {"label": "service-manager"}
        },
        "uaa": {
            "kind": "xsuaa"
        }
    }
}

In the mtx section, an array of allowed prefixes for CDS element names (table or service field names) and a list of blocked CDS namespaces for new entities and services can be configured. Field extensions can only be performed with those prefixes. Similarly, new entities and services can’t be created within the blocked CDS namespaces. Namespace restrictions are “inherited”, that is, protecting “com.sap.” also protects “com.sap.*”


As an SAP developer, you should protect ["com.sap.", "sap."].


The mtx section also lists the entities and services that can be extended. The entity-whitelist and service-whitelist are listing the fully qualified names of the entities or services.

If these lists aren’t provided, all entities or services can be extended. A warning is logged by cds build in this case.

If entries of the lists can’t be resolved, cds build fails with an error.

The section for datasource db shows how to configure for a bound Service Manager service for HDI containers.

REST APIs

All APIs receive and respond with JSON payloads. Application-specific logic (for example, scope checks) can be added using event handlers.

Tenant Provisioning API

  • Subscribe tenant

    PUT /mtx/v1/provisioning/tenant/<tenantId>
    

    Minimal request body:

    {
      "subscribedSubdomain": "<subdomain>",
      "eventType": "CREATE"
    }
    

    An application can mix in application-specific parameters into this payload, which it can interpret within application handlers. Use the _application_ object to specify those parameters. There’s one predefined sap object, which is interpreted by cds-mtx default handlers. With that object, it’s possible to set service creation parameters to be used by the service manager service when creating HDI container service instances. A typical use case is to provide the database_id to distinguish between multiple SAP HANA DBs mapped to one Cloud Foundry space.

    {
      "subscribedSubdomain": "<subdomain>",
      "eventType": "CREATE",
      "_application_": {
        "sap": {
          "service-manager": {
            "provisioning_parameters": { "database_id" : "<HANA DB GUID>" },
            "binding_parameters": {"<key>" : "<value>"}
        }
      }
    }
    

    Having two SAP HANA databases mapped to one space, subscription doesn’t work out of the box, unless you’ve specified a default database. This can be achieved by specifying a default when mapping a database to a space. See Share an Instance with another Cloud Foundry Space for more details.


  • Unsubscribe tenant

    DELETE /mtx/v1/provisioning/tenant/<tenantId>
    
  • Subscription dependencies

    GET /mtx/v1/provisioning/dependencies
    

    Response body: Array of String. The default implementation returns an empty array.

  • GET subscribed tenants

    GET /mtx/v1/provisioning/tenant/
    

    Returns the list of subscribed tenants. For each tenant, the request body is returned which have been used for subscribing the tenant.

Model API

  • Get cds model content

    GET mtx/v1/model/content/<tenantId>
    

    Returns the two objects base and extension in response body:

    {
      "base": "<base model cds files>",
      "extension": "<extension cds files>"
    }
    
  • Activate extensions

    POST mtx/v1/model/activate
    

    Request body (example):

    {
      "tenant": "tenant1",
      "extension": "<cds extension files>",
      "undeployExtension": false
    }
    

    The extension element must be a JSON array of arrays. Each first-level array element corresponds to a CDS file containing CDS extensions. Each second-level array element must be a two entry array. The first entry specifies the file name. The second entry specifies the file content. Extension files for data models must be placed into a db-folder. Extensions for services must be placed into srv-folder. Entities of the base model (the nonextended model) are imported by using ... from '_base/...'.

    If the undeployExtension flag is set, all extensions are undeployed from the database that are no longer part of the extensions in the current activation call.


    undeployExtension has to be used with care as it potentially removes tables and their content from the database.


    Request body detailed sample:

    {
    "tenant": "6c07c584-f463-46e5-9340-51958c51dd0a",
    "extension": [
      [
      "db/ext-entities.cds",
      "using my.bookshop from '_base/db/data-model'; \n extend entity bookshop.Books with { \n ISBN: String; \n rating: Integer \n  }"
      ],
      [
      "db/new-entities.cds",
      "namespace com.acme.ext; \n entity Categories { \n key ID: String; \n description: String; \n }"
      ],
      [
      "srv/ext-service.cds",
      "using CatalogService from '_base/srv/cat-service'; \n using com.acme.ext from '../db/new-entities'; \n extend service CatalogService with { \n  @insertonly entity Categories as projection on ext.Categories; \n }"
      ]
      ],
    "undeployExtension": false
    }
    
  • Upgrade base model from filesystem (asynchronous)

    POST mtx/v1/model/asyncUpgrade
    

    Request body:

    {
      "tenants": ["tenantId1", "tenantId2", ...],
      "autoUndeploy": <boolean>
    }
    

    Upgrade all tenants with request body { "tenants": ["all"] }.

    If autoUndeploy is set to true, the auto-undeploy mode of the HDI deployer is used. See HDI Delta Deployment and Undeploy Whitelist for more details.

    Response (example):

    { "jobID": "iy5u935lgaq" }
    

    The jobID can be used to query the status of the upgrade process:

    GET /mtx/v1/model/status/<jobID>
    

    During processing, the response can look like:

    {
      "error": null,
      "status": "RUNNING",
      "result": null
    }
    

    Once a job is finished, the collective status is reported like this:

    {
      "error": null,
      "status": "FINISHED",
      "result": {
          "tenants": {
              "<tenantId1>": {
                  "status": "SUCCESS",
                  "message": "",
                  "buildLogs": "<build logs>"
              },
              "<tenantId2>": {
                  "status": "FAILURE",
                  "message": "<some error log output>",
                  "buildLogs": "<build logs>"
              }
          }
      }
    }
    

The status of a job can be QUEUED (not started yet), RUNNING, FINISHED, and FAILED.

The result status of the upgrade operation per tenant can be RUNNING, SUCCESS, or FAILURE.


Logs are persisted for a period of 30 minutes before they get deleted automatically. If you’re requesting the job status “too late”, you get a 404 Not Found response.


Metadata API

All metadata APIs support eTags. By setting the corresponding header, you can check for model updates.

  • GET edmx

    GET /mtx/v1/metadata/edmx/<tenantId>
    

    Returns the edmx metadata of the (extended) model of the application.

    Optional URL parameters

    name=<service-name>
    language=<language-code>
    
  • GET csn

    GET /mtx/v1/metadata/csn/<tenantId>
    

    Returns the compiled (extended) model of the application.

  • GET languages

    GET /mtx/v1/metadata/languages/<tenantId>
    

    Returns the supported languages of the (extended) model of the application.

  • GET services

    GET /mtx/v1/metadata/services/<tenantId>
    

    Returns the services of the (extended) model of the application.

Deployment Structure

CAP-based SaaS applications must contain the cds-mtx library, which runs in a Node.js server environment. If the application’s business logic has been implemented in JavaScript, then cds-mtx runs on the same Node.js server as the business logic. In this case, the server exposes the business APIs as well as the APIs of cds-mtx. This is different, if the business logic has been implemented in Java. Then, cds-mtx needs to be deployed as a “sidecar” to the Java server and bound to it via the environment variable CDS_MTX_SDC_URL. In this scenario, the CAP Java libraries are exposing the tenant lifecycle APIs. API calls are forwarded to the sidecar. However, any application-specific tenant lifecycle logic can be implemented in Java.

The following diagram illustrates the deployment structure for the Java case. The scenario is described for the Java case, but transfers to the simpler, pure Node.js case, with the difference that there’s just one server instead of two (CAP server and sidecar coincide).

Java SaaS application deployment

Prerequisites:

To make a SaaS application available for subscription to SaaS consumer tenants, the application provider must register the application in the Cloud Foundry environment through the SaaS Manager (a.k.a. “SaaS Provisioning service”).

Each SaaS application must at least bind to two SAP Cloud Platform service instances:

  1. User Account and Authentication Service (service xsuaa): Binding information contains the OAuth client ID and client credentials. The XSUAA security library can be used to validate JWT token from requests and to retrieve the tenant context from the JWT.
  2. Service Manager for SAP HANA (service service-manager): CAP is using this service for creating a new SAP HANA Deployment Infrastructure (HDI) container for each tenant and for retrieving the tenant-specific database connection.


Tenant Lifecycle

If a tenant is subscribing to the SaaS application, the SaaS Manager issues an onboarding request to the SaaS application. CAP triggers the generation of a new tenant database container and will deploy the database artifacts.

After running an application upgrade, the application has to trigger the “tenant upgrade” endpoint, if new or changed database artifacts shall become effective.

An “unsubscribe” event triggers the tenant offboarding endpoint. CAP deletes the corresponding database container (data loss!).

The application can influence the CAP out-of-the-box behavior by implementing own request handlers.

Runtime

Requests to CAP business services must contain valid JWT tokens which proof the authentication of the requesting user and which contains application scopes. Further, the requesting tenant ID is contained in this JWT. CAP is extracting the tenant context from the JWT and retrieves the correct database connection from the Service Manager.

Tenant-Specific Extensibility

Customers of a SaaS application can extend data and service models in a tenant-specific way. See section Extensibility for more details.

Event Handlers for cds-mtx APIs


If you’re using a CAP Java server, it re-exposes the APIs required by SAP Cloud Platform’s SaaS Manager (the Provisioning API). We recommended leveraging the corresponding Java-based mechanisms to add handlers to these APIs. Handlers for the Model-API of cds-mtx must always be implemented on the Node.js server, because this API isn’t re-exposed by the Java stack.


cds-mtx serves its APIs by using cds technology. Therefore, any additional application logic can be implemented as cds event handlers. Event handlers can be implemented in JavaScript files, which are named according to the service name. Those files can be placed into the folder containing your CDS service definition files (for example, the srv/ folder, but they can be placed anywhere where models are loaded from). In case of the cds-mtx APIs, use the files provisioning.js, model.js, and metadata.js. See the following use cases for examples.


Use Case: Return Application URL After Subscription

When connecting the tenant provisioning API to SAP Cloud Platform’s SaaS Manager, a handler for ProvisioningService must be implemented, which returns the application URL (to be used by subscribers) for displaying it in SAP Cloud Platform Cockpit. The following example assumes that the application sets an environment variable APP_URLPART. The task of the handler is to prefix the content of the variable APP_URLPART with the tenant subdomain contained in the onboarding request. It returns the complete, tenant-specific application URL:

provisioning.js

module.exports = (service) => {
  // event handler for returning the tenant specific application URL as a response to an onboarding request
  service.on('UPDATE', 'tenant', async (req, next) => {
    const res = await next();          // IMPORTANT: call default implementation which is doing the HDI container creation
    let c = cds.env.for('app');        // use cds config framework to read app specific config node
    let appuri = typeof c.urlpart === "undefined" ? ' ' : c.urlpart;
    if (appuri === ' ') {
      console.log('[INFO ][ON_UPDATE_TENANT] Application URI for subscriptions is not configured.');
      return '';
    } else {
      let url = 'https://' + req.data.subscribedSubdomain + appuri;
      console.log('[INFO ][ON_UPDATE_TENANT] ' + 'Application URL is ' + url);
      return url;
    }
  });
}

A handler is registered which replaces the default handler for the provisioning service. UPDATE is relevant here, because SAP Cloud Platform sends a PUT request for onboarding. The environment variable isn’t read-in directly. Instead, the cds configuration framework is used, which allows configuring in various ways.


Use Case: Application-Specific Scope Checks

Another common task is to implement application-specific scope checks for the subscription requests. This scope check can be implemented by the “before - UPDATE -tenant” event handler within provisioning.js:

module.exports = (service) => {
  // event handler for doing some application specific scope checks
  service.before ('UPDATE', 'tenant', async (req) => {
    console.log('[INFO ][BEFORE_UPDATE_TENANT] ' + req.data.subscribedTenantId);
    if (!req.user.is('Callback')) {     // check for the scope "Callback"
      console.log('[ERROR][BEFORE_UPDATE_TENANT]: JWT does not contain required scope.');
      // Reject request
      const e = new Error('Forbidden');
      e.code = 403;
      return req.reject(e);
    } else {
      console.log('[INFO ][BEFORE_UPDATE_TENANT]: JWT contains relevant scope');
    }
  });
...
}


Use Case: Conditional Offboarding

Applications may want to prevent the deletion of a tenant database container, if certain conditions are met (for instance, there are still subscribers accessing this database). This behavior can be implemented by replacing the default “DELETE tenant” implementation:

module.exports = (service) => {
  ...
  // Event handler checking a condition before deleting the tenant DB container as a response to an offboarding request.
  // This handler overwrites the default handler!
  service.on ('DELETE', 'tenant', async (req, next) => {
    console.log('[INFO ][ON_DELETE_TENANT] ' + req.data.subscribedTenantId);
    if (1 === 2) {    // put your condition here
      console.log('[INFO ][ON_DELETE_TENANT]: Offboarding will not be executed');
    } else {
       await next();  // call default implementation which is doing regular offboarding
    }
  });
  ...
}


Use Case: Return Subscription Dependencies

If SAP Cloud Platform’s SaaS Manager requests an array of subscription dependencies (“GET DEPENDENCIES callback”), register an ON-handler for the GET mtx/v1/provisioning/dependencies endpoint.

module.exports = (service) => {
...
  // event handler returning an array of dependencies as a response to the GET DEPENDENCIES request
    service.on('dependencies', async (req) => {
    const deps = ['first_dependency', 'second_dependency'];
    console.log('[INFO ][ON_GET_DEPENDENCIES] Dependent applications/services: ' + JSON.stringify(deps) );
    return deps;
  });
...
}

Node.js Stack

CAP provides the npm module @sap/cds-mtx to expose the APIs required to build multitenant applications. This module also offers features for performing tenant-specific database and service extensions.

Deploy as Multitenant Application

This tutorial extends the Deploying to Cloud tutorial but can easily be applied to your own scenario.

  1. Enhance and add required services:
    In package.json, add the multiTenant property to the db datasource and add hint to find the service-manager. Further, add the requirement for the authentication service xsuaa of SAP Cloud Platform.

     "cds": {
       "requires": {
         "db": {
           "kind": "hana",
           "model": ["db", "srv"],
           "multiTenant": true,
           "vcap": {"label": "service-manager"}
         },
         "uaa": {"kind": "xsuaa"}
       }
     }
    
  2. Add npm dependencies:
    Add the following entries to the dependencies section in package.json:

       "@sap/cds-mtx": "^1",
       "@sap/hdi-deploy": "^3.11.4",
       "@sap/instance-manager": "^2",
       "passport": "^0.4.0",
       "@sap/xssec": "^2.1.16"
    
  3. Add server start script:

    • Add the file server.js to the project root folder:

       const cds = require ('@sap/cds')
      
       cds.on('bootstrap',(app) => {
         cds.mtx.in(app)            // serve cds-mtx APIs
       });
      
       // Delegate bootstrapping to built-in server.js
       module.exports = cds.server
      
    • Make sure to adapt the server start command in package.json:

       ...
       "scripts": {"start": "cds run"},
       ...
      

      Alternatively, you can remove the start script declaration, because server.js is started by default.

  4. Enable authentication for the Bookshop services:

    Add one of the following alternative annotations to the AdminService (admin-service.cds) and the CatalogService (cat-service.cds) of the bookshop app.

    • If you intend to use the Postman collection for testing (see Testing with Postman), enable access by technical users:
       @requires: 'system-user'
      
    • Otherwise, enable access by authenticated natural persons:
       @requires: 'authenticated-user'
      
  5. Deploy to Cloud as described in Deploy using MTA.

Testing with Postman

Import this Collection into Postman. It contains a set of variables, which you have to set.

  • From XSUAA binding information, extract clientid, clientsecret, paasTenantId (= tenantid), and paasSubdomain (= identityzone).
  • appurl is the URL of the bookshop-srv application.

Send requests:

  1. Fetch a JWT token with request “Authenticate –> Get Token w/ Client Credentials”. Please check whether the test results are ok, indicating that the JWT token has been stored.
  2. Subscribe the PaaS tenant with request “mtx API –> Onboard PaaS tenant
  3. Try all other requests …


Testing with Server Running Locally

You can run the bookshop server (bookshop-srv) locally and have it connected to the remote database. To achieve this, you need to provide database connection details, which you can obtain as follows:

  1. Deploy the MTA as described previously.
  2. Run cf env bookshop-srv to obtain output similar to the following:
     [...]
     {
      "VCAP_SERVICES": {
       "service-manager": [
        {
         "binding_name": null,
         "credentials": {
           [...]
         },
         [...]
        }
       ],
       "xsuaa": [
        [...]
       ]
      }
     }
    
     [...]
    
  3. Copy the VCAP_SERVICES definition, that is, the part between and including the outermost curly braces.
  4. Create a file default-env.json in the db subfolder of your app.
  5. Paste the copied data.

To start the server, run npm run start in the root directory of bookshop-srv. By default, the server listens on localhost:4004. (The port can be changed through the PORT environment variable.)

This address can be used as server URL in the remainder of this documentation. You can also run the Postman requests against it by simply adjusting the appurl accordingly.


Register the Bookshop SaaS App for Subscription

To enable subscription of tenants to the bookshop SaaS application using SAP Cloud Platform Cockpit, it must be registered with SAP Cloud Platform. This registration can be accomplished by creating an instance of the SaaS Registry Service. See section Register the Multitenant Application to the SaaS Provisioning Service in SAP Cloud Platform documentation for more details.

  • Connect to the Cloud Foundry space to which you’ve deployed the bookshop application.
  • Create a file regconfig.json with the following content:

    {
     "xsappname":"bookshop-mt-${space}",
     "appUrls": {
        "onSubscription" : "<bookshop-srv-url>/mtx/v1/provisioning/tenant/{tenantId}"
     },
     "displayName" : "CAP Bookshop SaaS App",
     "description" : "A Bookshop SaaS application made with CAP",
     "category": "CAP"
    }
    

    In this JSON file, two placeholders must be replaced with actual values:

    • ${space}: this is your deployment target space. Compare also the value of xsappname at the bookshop-uaa-mt resource in the generated mta.yaml file.
    • <bookshop-srv-url>: this is the URL of your bookshop-srv application on Cloud Foundry. This “onSubscription” URL is used by SAP Cloud Platform to perform the actual subscription. It’s a “callback” into your application to perform the actual tenant provisioning.


  • Create an instance of the service saas-registry in your Cloud Foundry space:

    cf create-service saas-registry application bookshopreg -c regconfig.json
    

    Alternatively, you can use SAP Cloud Platform Cockpit to create this service instance.


Implement a Tenant Provisioning Handler

To be finally able to subscribe using SAP Cloud Platform Cockpit, the subscription call must return a URL for an application user, which points to the tenant-specific application entry point (usually a UI). A detailed description of how to implement handlers for cds-mtx can be found here

  • Create the file provisioning.js in folder srv/:

    module.exports = (service) => {
      service.on('UPDATE', 'tenant', async (req, next) => {
        const res = await next();     // first call default implementation which is doing the HDI container creation
        let url = '<bookshop-srv-url>/admin';
        console.log('[INFO ][ON_UPDATE_TENANT] ' + 'Application URL is ' + url);
        return url;
      } );
    }
    

    In the provided code sample, you have to replace <bookshop-srv-url> with the URL of your bookshop-srv application on Cloud Foundry. The /admin endpoint is returned then. It’s important that this endpoint isn’t protected (doesn’t require a JWT token to be called).


  • Rebuild (mbt build -t ./) and redeploy (cf deploy bookshop_1.0.0.mtar) your application.


Subscribe to the SaaS Application

Your application is now ready for subscription from another tenant (= Subaccount on SAP Cloud Platform):

  • Using SAP Cloud Platform Cockpit, switch to, or create another subaccount within your global account.
  • Switch to the Subscriptions section of the subaccount.
  • Click on the tile in category CAP corresponding to your registered bookshop application.
  • Click on the Subscribe button. Now, the tenant onboarding API of the bookshop application is called to provision a new SAP HANA database schema for this tenant.
  • The tenant-specific application URL is returned to the SAP Cloud Platform Cockpit and put behind the Go to Application link. Usually, this URL directs a user to an application UI to log on and use the application. In our case, it points to the URL coded into the handler (provisioning.js) and it just brings up some service metadata.

Extend Tenant Data and Service Models

cds-mtx provides an API to extend data and service models in a tenant-specific way. The CDS Software Development Kit, @sap/cds-dk, contains a command line client with which an extension developer can maintain and activate cds model extensions files. See section Extending SaaS Applications for more details.

Perform the following steps to enable and perform extensions of the Getting Started Bookshop SaaS application:

  1. Add required scopes to XSUAA configuration.
  2. Authorize Extension Developer on SAP Cloud Platform.
  3. Set up an Extension Project.
  4. Create extension cds files and activate.

Add Required Scopes

Tenant-specific cds model extensions are performed by an extension developer, which is a customer role. Therefore, those activities are protected by XSUAA scopes. It’s possible to update your already existing XSUAA instance using the cf update-service command. However, we demonstrate how to extend the generated mta.yaml file appropriately. This has the advantage, that you don’t need to modify the generated mta.yaml file, so that this can be regenerated without loosing your scope definitions.

Create the MTA extension descriptor file scopes.mtaext:

_schema-version: '3'
ID: bookshop.scopes
extends: bookshop

resources:
- name: bookshop-uaa-mt
  parameters:
    config:
      scopes:
      - name: $XSAPPNAME.ExtendCDS
      role-templates:
      - name: ExtensionDeveloper
        description: CDS Extension Developer
        scope-references:
        - $XSAPPNAME.ExtendCDS
      role-collections:
      - name: CAP-Getting-Started
        description: CAP Getting Started
        role-template-references:
        - $XSAPPNAME.ExtensionDeveloper

Rebuild the application while applying the extension descriptor with the command:

   mbt build -t ./ -e scopes.mtaext

Then redeploy while just updating the bookshop-uaa-mt service instance:

   cf deploy bookshop_1.0.0.mtar -r bookshop-uaa-mt

You need to have the User & Role Administrator role to be able to define role templates. If the previous command gives you a related error message, please have this role assigned to you.



Authorize Extension Developer

  • Open the overview screen of a Subaccount in SAP Cloud Platform Cockpit. This can be any Subaccount, which is already subscribed to your application. For first testing, you can choose your Paas/Provider Subaccount into which you’ve deployed your application. Note down the value for “Subdomain”. In the following, we refer to it as <subdomain>. It represents the tenant.

  • Choose Trust Configuration and click on the active default identity provider name, for instance, SAP ID Service. Trust Configuration

  • Enter your E-Mail address and choose Show Assignments.
  • Choose Assign Role Collection.
  • From the popup, assign CAP-Getting-Started.

You’re now authorized to use the extension API to extend cds models for your PaaS/Provider tenant.


Set Up an Extension Project

Initialize a new extension project for tenant <subdomain>:

  • Execute cds extend <bookshop-srv-url> -s <subdomain>
  • The system will respond with the message “Invalid passcode”.
  • Fetch a one-time passcode used for authentication by following the link given in the system message. Log on at the browser logon screen. Note down the shown authentication code <passcode>.
  • Execute cds extend <bookshop-srv-url> -s <subdomain> -p <passcode>. A project structure will be generated in a new folder with name <subdomain>.

Create and Activate an Extension

  • In the db/ folder of this project, create the new file ext.cds:
    using sap.capire.bookshop from '_base/db/schema.cds';
    extend entity bookshop.Books with {
      ISBN : String;
    }
    
  • Execute cds activate <subdomain> (from the folder one above the folder <subdomain>). For the tenant <subdomain>, the new field ISBN gets activated on the database.
  • Verify that the new field is exposed for Books. Use Postman to fetch a JWT for the extended tenant and send a GET request to <bookshop-srv-url>/browse/$metadata.


Java Stack

How to enable multitenancy for a CAP Java application is described in Java > Multitenancy.