Leveraging Object Store BTP Service with SAP CAP: A Guide to Storing Static Files
In today's digital landscape, businesses are constantly seeking efficient ways to manage their data, including static files such as images, documents, and multimedia content. With the advent of cloud computing, storing and accessing these files has become easier and more scalable than ever before. One such solution is leveraging the Object Store service within the SAP Business Technology Platform (BTP) in conjunction with SAP Cloud Application Programming Model (CAP).
In this blog post, we'll explore how you can seamlessly integrate Object Store BTP Service with SAP CAP to store static files, enabling efficient data management within your applications.
Introducing Object Store BTP Service
Object Store is a cloud-based storage solution offered as part of the SAP Business Technology Platform. It provides scalable and durable object storage that can be seamlessly integrated into your applications. With Object Store, you can securely store and retrieve files of any type, making it an ideal solution for managing static assets in your applications.
Benefits of Using Object Store with SAP CAP
Integrating Object Store BTP Service with SAP CAP offers several benefits:
- Scalability: Object Store provides scalable storage that can accommodate large volumes of static files, ensuring your application can handle growth in data demand.
- Durability: Files stored in Object Store are redundantly stored across multiple data centers, ensuring high durability and data resilience.
- Integration: Seamlessly integrate static file storage capabilities into your CAP applications without the need for complex configuration or custom development.
- Security: Leverage built-in security features of SAP BTP to ensure that access to Object Store resources is securely managed and controlled.
Integrating Object Store with SAP CAP
To integrate Object Store BTP Service with SAP CAP, follow these steps:
-
Set Up Object Store Service: Begin by provisioning the Object Store service within your SAP BTP account. You can do this through the SAP BTP cockpit or using the Cloud Foundry command-line interface (CF CLI).
-
Install Required Dependencies: In your CAP project, install the necessary dependencies to interact with Object Store. This may include SDKs or libraries provided by SAP for accessing Object Store APIs.
-
Configure Object Store Service Binding: Create a service binding between your CAP application and the Object Store service instance. This will allow your application to securely access the Object Store resources.
-
Implement File Upload and Download: Within your CAP application, implement functionality to upload and download static files to and from Object Store. This may involve creating RESTful endpoints or service functions to handle file operations.
-
Secure Access Control: Ensure that access to Object Store resources is securely managed within your application. Utilize authentication and authorization mechanisms provided by SAP BTP to control access to sensitive data.
What are we building?
We will create a simple CAP application with a single endpoint to upload and download files. To simplify this tutorial, we will limit the type of files to PNG images, however we can easily change this to fit any kind of file.
Object Store service exposes the access to the static storage service of the infrastructure provider. This depends on your BTP account configuration. In my BTP Account I have AWS as IaaS provider, so our storage will be an S3 bucket.
Prerequisites
To build this you need a subscription to the Object Store service in BTP with the standard
plan. More information about this service here.
It is important to understand the general concepts of Object Store service, and also to assign the corresponding quotas and entitlements for your subaccount.
Furthermore we need Node.js and a basic knowledge of SAP CAP, the Cloud Foundry CLI, and the CAP CLI.
Step 1: Create a basic CAP application
Follow the basic steps to create a CAP app in the folder of your choice.
In my case:
cds init CAP_ObjectStore
Step 2: Define the db schema and the service to expose the endpoint
In /db
folder create a schema.cds
file with the following code:
namespace media.db;
entity Pictures {
key ID : UUID;
@Core.MediaType: 'image/png'
content : LargeBinary;
}
Note that we are forcing
image/png
as Mime Type. As we said before you can easily change this to fit any other type of file
In /srv
folder create a media-service.cds
file with the following code:
using media.db as db from '../db/schema';
service MediaService {
entity Pictures as projection on db.Pictures;
}
Step 3: Create an instance of the Object Store service and a service key to create a binding with the application
To do that I use the CF CLI with the following commands:
cf create-service objectstore standard cap-objectstore-instance
cf create-service-key cap-objectstore-instance cap-objectstore-instance-key
cds bind --to cap-objectstore-instance:cap-objectstore-instance-key
Those commands create an instance of the object store and creates a binding to the local CAP application. The cds bind
command creates a file in your project folder with the credentials needed so the CAP app binds to the service instance when running it in hybrid mode.
Step 4: Create the custom handler for our service endpoint
Here is where the magic happens. We will rewrite the /Pictures
endpoint implementation so we intercept the data stream of the uploaded file and send it to the AWS S3 bucket using the native AWS SDK
First we need to install the dependency
npm i --save aws-sdk
Now in the /srv
folder, create a media-service.js
file with the following code:
const AWS = require('aws-sdk');
/* Initialize S3 bucket */
function _initializeS3Bucket(vcap_services) {
const credentials = new AWS.Credentials(
vcap_services.objectstore[0].credentials.access_key_id,
vcap_services.objectstore[0].credentials.secret_access_key
);
AWS.config.update({
region: vcap_services.objectstore[0].credentials.region,
credentials: credentials
});
return new AWS.S3({
apiVersion: '2006-03-01'
});
}
/* Get object stream from S3 */
function _getObjectStream(vcap_services, s3, objectKey) {
const params = {
Bucket: vcap_services.objectstore[0].credentials.bucket,
Key: objectKey
};
return s3.getObject(params).createReadStream();
}
module.exports = (srv) => {
const vcap_services = JSON.parse(process.env.VCAP_SERVICES);
const s3 = _initializeS3Bucket(vcap_services);
srv.on('UPDATE', 'Pictures', async (req) => {
const params = {
Bucket: vcap_services.objectstore[0].credentials.bucket,
Key: req.data.ID,
Body: req.data.content,
ContentType: 'image/png'
};
s3.upload(params, function (err, data) {
console.log(err, data);
});
});
srv.on('READ', 'Pictures', (req, next) => {
if (!req.data.ID) {
return next();
}
return {
value: _getObjectStream(vcap_services, s3, req.data.ID)
};
});
};
With this code we are reimplementing the UPDATE and READ handlers of the endpoint. In the UPDATE handler we create an upload stream to the S3 bucket to store the file. In the READ handler, we do the opposite, create a read stream to download the file from S3.
Step 5: Test the application
Let's run the CAP app in hybrid mode.
cds watch --profile hybrid
This executes the app in localhost and at the same time gets the binding credentials for the object store service we bound in Step 3, and bulk them into the VCAP_SERVICE environment variable.
Now create a /test
folder and inside create a requests.http
file and include a PNG file of your choice in the same folder. In my case test.png
Fill the requests.http
file with the following code
### Create an entry in the DB. Generates an UUID
POST http://localhost:4004/odata/v4/media/Pictures
Content-Type: application/json
{}
### Upload the file with the entry ID is key name
PUT http://localhost:4004/odata/v4/media/Pictures(368e5f5e-4744-4387-a54a-d474f82ab232)/content
Content-Type: image/png
< ./test.png
### Downloads the file by file ID
GET http://localhost:4004/odata/v4/media/Pictures(f0df9601-d362-466c-931c-f2b0087d8b01)/content
Now you can click on send request
after each comment to fire the corresponding request and test the service.
The way it works is the following:
- You have to send a POST request to create an entry in the DB for that file. This will generate a unique ID for this file, which will be used to store the file in S3 avoiding overwritting due to unexpected upload of files with the same name.
- Next, send a PUT request against the same endpoing. This PUT request include the ID we just generated and the file to upload
- Last, if we want to download the file, we just need to send a GET request agains the endpoint providing the specific ID.
Step 6: Deploy app as MTA
To deploy the CAP app to our Cloud Foundry account, we just need to define the MTA file as follows:
_schema-version: '3.1'
ID: CAP_ObjectStore
version: 1.0.0
description: 'A simple CAP project.'
parameters:
enable-parallel-deployments: true
build-parameters:
before-all:
- builder: custom
commands:
- npx cds build --production
modules:
- name: CAP_ObjectStore-srv
type: nodejs
path: gen/srv
parameters:
buildpack: nodejs_buildpack
build-parameters:
builder: npm
provides:
- name: srv-api # required by consumers of CAP services (e.g. approuter)
properties:
srv-url: ${default-url}
requires:
- name: cap-objectstore-instance
- name: CAP_ObjectStore-db
- name: CAP_ObjectStore-auth
- name: CAP_ObjectStore-db-deployer
type: hdb
path: gen/db
parameters:
buildpack: nodejs_buildpack
requires:
- name: CAP_ObjectStore-db
resources:
- name: cap-objectstore-instance
type: objectstore
parameters:
service: objectstore
service-plan: standard
service-name: cap-objectstore-instance
- name: CAP_ObjectStore-db
type: com.sap.xs.hdi-container
parameters:
service: hana
service-plan: hdi-shared
- name: CAP_ObjectStore-auth
type: org.cloudfoundry.managed-service
parameters:
service: xsuaa
service-plan: application
path: ./xs-security.json
config:
xsappname: CAP_ObjectStore-${org}-${space}
tenant-mode: dedicated
Note that I have included an XSUAA instance and a HANA HDI container, which are bound the CAP server.
Now just build and deploy:
mbt build mta.yaml
cf deploy mta_archives/CAP_ObjectStore_1.0.0.mtar
Recap
With this project we have created a CAP application exposing one endpoint to manage static BLOB files and store them in an AWS S3 bucket using the SAP BTP Object Store Service.