If you operate a multitenant application in SAP BTP's Cloud Foundry environment, you're probably familiar with the Saas Provisioning service. This service allows you, the application operator, to control customer subscriptions by specifying endpoints that SAP BTP will call during subscription events. Tasks like setting up database artifacts, mapping routes and initializing default configuration can be defined in these callbacks allowing for automation of certain aspects of customer onboarding. In fact, the
CAP Framework has standardized part of this onboarding processes including the provisioning of HDI containers.
But beyond subscription management, there is a third aspect to the SaaS Provisioning service responsible for allowing subscribers to access services instantiated in your provider account. This is the governed by the
getDependencies
callback and is the focus of this post.
Get Dependencies
When do you need to use
getDependencies
? The
official documentation gives us a hint:
If your application consumes any reuse services provided by SAP, you must implement the getDependencies callback to return the service dependencies of the application. The callback must return a 200 response code and a JSON file with the dependent services appName and appId, or just the xsappname.
[emphasis mine]
The key term from that snippet is
reuse services. If you use a reuse service and want your subscribers to have access,
getDependencies
must return it. But how do you know if a service is a
reuse service, is there a list somewhere? As far as I know, the answer is
no. Still, experience has shown these services fit the description:
- UI Theme Designer
- Job Scheduling Service
- Cloud Portal Service
- Connectivity/Destinations
- UI5 Flexibility for Key Users
[Note: this is not an exhaustive list]
What these services have in common is that they have a certain amount of customer specificity. Each customer could have their own defined themes or scheduled jobs or destinations. Absent a complete list or even an indicator in the service documentation itself, this is the primary signal we have to include a service in the callback response.
Dependency Relevant Events
Now that we have an understanding of when we need to define
getDependencies
and what services we need to included in its response, the next step is to understand when SAP BTP will attempt to
getDependencies
. As you might expect, it accesses this endpoint during subscription creation and update events.
Creating a Subscription
The create event is an obvious place to retrieve a list of dependencies that need to be accessible by a new customer. Indeed, in addition to the
onSubscription
endpoint, the
getDependencies
endpoint is also accessed. So when activating the new subscription from the customer subaccount, SAP BTP calls both
onSubscription
and
getDependencies
to complete the subscription.
Update Subscription
The update event is naturally the next place that needs access to a list of dependencies. In the operation of any application, there's a good chance you'll want to add new services to enhance existing functionality. In these cases, you need a mechanism to re-trigger the
getDependencies
call so SAP BTP can retrieve an updated list of services needed by the subscribed customer.
While the initial subscription has an obvious mechanism to trigger the create event (creating a subscription within the subaccount cockpit), there is no corresponding obvious mechanism to trigger an update. It's at this point that we need to consult the
SaaS Provisioning API.
In the section labeled
Application Opeartions for App Providers, we find this endpoint:
PATCH /saas-manager/v1/application/tenants/{tenantId}/subscriptions
With the description:
Update the dependencies of a multitenant application.
This is precisely what we're looking for. To refresh the dependencies, all we need to do is update the response to the
getDependencies
endpoint to include the new service and then execute that
PATCH
request against the tenant to update.
But there is a wrinkle to this endpoint that is not documented. While it does indeed trigger a new call to
getDependencies
it
also triggers a new call to
onSubcription
. This can be problematic if you assumed
onSubscription
will only be accessed during the subscription creation.
Luckily, there is a field included in the
onSubscription
request body that allows us to distinguish which event this is, the
eventType
field. For new subscriptions, the value is
CREATE
while in subscription updates the value is
UPDATE
. So, if you have logic in
onSubscription
you only wanted executed on subscription creation, just check the
eventType
.
Summary
The main takeaway here is that there are two places
getDependencies
is relevant: during subscription creations and updates. Both events result in calls to both
onSubscription
and
getDependencies
so it is important that your
onSubscription
logic can account for this.
Create
Triggered by creating a subscription in the customer subaccount. Results in these callbacks:
PUT {onSubscription}/{tenantId}
{
...
"eventType": "CREATE"
}
GET {getDependencies}?tenantId={tenantId}
Update
Triggered by accessing the
PATCH
endpoint in the
SaaS Provisioning API. Results in these callbacks:
PUT {onSubscription}/{tenantId}
{
...
"eventType": "UPDATE"
}
GET {getDependencies}?tenantId={tenantId}
Closing Thoughts
Part of my motivation for this post was my inability to find a centralized source of documentation on the specifics of how
getDependencies
works within the Saas Provisioning service. Maybe this documentation exists and I'm just not good enough to find it and if that's the case, I invite any readers to share where I can find that. If it does not actually exists, I hope this post can help others that have struggled with the same issues I have.
Thanks!