Technical Articles
Integrating SAP Document Management in CAP Application
Previous Post
In the previous blog post, I’ve shown how to create a basic ui5 app and integrated it with document management reuse ui.
https://blogs.sap.com/2020/10/22/integrating-sap-cp-document-management-service-ui-in-the-fiori-app/
This Blog Post
Now in this blog post, I will show a simple use case of integrating document management service with your CAP Based UI5 app.
We will reuse most of the stuff that was created from the previous blog like repositoryId, service instance name..,
Final Application Demo
GIT Repo URL
https://github.com/mahesh0431/CommunityDMCAPApp
Steps:
Step 1: Create a CAP Application in the CAP Space using Business Application Studio.
There are lot of tutorials to create a CAP Application, so I am skipping that part.
Just create a basic CAP app with one entity. and use the GIT Repo that I mentioned earlier to adjust any missing code. I will now only add the information that is only needed to show the Document Management Reuse UI in a CAP App.
I generated the CAP app by selecting the below options.
Then delete the data folder in the db folder.
Open the data-model.cds and add repositoryId and folderId. Then change the entity to UUID based as shown below.
namespace my.bookshop;
using { managed ,cuid} from '@sap/cds/common';
entity Books: cuid,managed {
booknumber : Integer;
title : String;
stock : Integer;
repositoryId : String;
folderId: String;
}
Now update the srv/cat-service.cds by removing the “@readonly”.
using my.bookshop as my from '../db/data-model';
service CatalogService {
entity Books as projection on my.Books;
}
Add index.cds file to srv folder and add the below content.
using {CatalogService as my} from './cat-service';
annotate my.Books with @odata.draft.enabled;
annotate my.Books with @(UI : {
SelectionFields : [ booknumber, title ],
LineItem : [
{
Value : booknumber,
Label : 'ID'
},
{
Value : title,
Label : 'Title'
}
],
Facets : [{
$Type : 'UI.CollectionFacet',
ID : 'General',
Label : 'Book Info',
Facets : [{
$Type : 'UI.ReferenceFacet',
Target : '@UI.FieldGroup#Main',
Label : 'Main Facet'
}]
}],
FieldGroup #Main : {Data : [
{Value : booknumber},
{Value : title}
]}
});
Now create cat-service.js file in the srv folder and add below code. I’ve added enough comments to understand the business logic here.
const cds = require('@sap/cds')
const axios = require('axios').default;
const FormData = require('form-data');
const VCAP_SERVICES = JSON.parse(process.env.VCAP_SERVICES);
// Access the sdm credentials (Document management, Integration option instance)
const sdmCredentials = VCAP_SERVICES.sdm[0].credentials
const _fetchJwtToken = async function (oauthUrl, oauthClient, oauthSecret) {
// This is to get the oauth token , which is used to create the folder ID
return new Promise((resolve, reject) => {
const tokenUrl = oauthUrl + '/oauth/token?grant_type=client_credentials&response_type=token'
const config = {
headers: {
Authorization: "Basic " + Buffer.from(oauthClient + ':' + oauthSecret).toString("base64")
}
}
axios.get(tokenUrl, config)
.then(response => {
resolve(response.data.access_token)
})
.catch(error => {
reject(error)
})
})
}
// This is to create a folder in the repository for every new book that is getting created.
// So basically we create a new folder for every book id and user can add their respective attachments in that folder.
const _createFolder = async function (sdmUrl, jwtToken, repositoryId, rootFolderId, forlderName) {
return new Promise((resolve, reject) => {
const folderCreateURL = sdmUrl + "browser/" + repositoryId + "/root"
const formData = new FormData();
formData.append("objectId", rootFolderId);
formData.append("cmisaction", "createFolder");
formData.append("propertyId[0]", "cmis:name");
formData.append("propertyValue[0]", forlderName);
formData.append("propertyId[1]", "cmis:objectTypeId");
formData.append("propertyValue[1]", "cmis:folder");
formData.append("succinct", 'true');
let headers = formData.getHeaders();
headers["Authorization"] = "Bearer " + jwtToken;
const config = {
headers: headers
}
axios.post(folderCreateURL, formData, config)
.then(response => {
resolve(response.data.succinctProperties["cmis:objectId"])
})
.catch(error => {
reject(error)
})
})
}
module.exports = cds.service.impl(async (service) => {
// This will be called whenever a new draft book is getting created
service.before("NEW", 'Books', async (context) => {
// Fill the repositoryId
context.data.repositoryId = "3a6fbabb-1c19-4014-80cd-e9d4443fd311";
const connJwtToken = await _fetchJwtToken(sdmCredentials.uaa.url, sdmCredentials.uaa.clientid, sdmCredentials.uaa.clientsecret)
// Creating the folder id and fill it
context.data.folderId = await _createFolder(sdmCredentials.endpoints.ecmservice.url, connJwtToken, context.data.repositoryId, "SYNsY7aoCEVirXnHScSBm3SQsSAvCy8zsAkZJjAjUE8", context.data.ID);
});
});
In the above code, the repositoryId will be same as the one we created in the earlier blog post.
for the folderId, use the earlier post admin ui5 app and create a dummy folder (CAP Books App folder), so now to get that folderId, we need to use postman query. Highlighted is the CAP BooksApp document folder id.
query I’ve used is:
https://api-sdm-di.cfapps.eu10.hana.ondemand.com/browser/3a6fbabb-1c19-4014-80cd-e9d4443fd311/root?objectId=cec9627000c878b437e809f5&cmisSelector=children
objectId is nothing but the root folderId that we have seen in the previous blogpost.
Now create server.js file in srv folder, this is because we will use OData V2 service for our Fiori app.
"use strict";
const cds = require("@sap/cds");
const proxy = require("@sap/cds-odata-v2-adapter-proxy");
cds.on("bootstrap", app => app.use(proxy()));
module.exports = cds.server;
add these entries to package.json dependencies in the root folder
"@sap/cds-odata-v2-adapter-proxy": "^1.4.48",
"axios": "^0.20.0",
"form-data": "^3.0.0",
Any missing things, you can refer to git hub project.
Step 2: Add a Fiori Element Application
Download the ui5 app(booksapp) that is the git link and paste it in the app folder, just update the appid in your manifest.json to your desired app id. Make sure you update this ide in the package.json file from app/booksapp folder.
This is basically a V2 Fiori element template application. I’ve extended the object page(added a new Facet and controller) as you can see in the below manfest.json
In the attachment fragment, i will use the component container to show the document management reuseui, as shown the previous blogpost.
The additional thing here is, I written a code to manually pass the repositoryId and folderId to the DocumentManagement UI. as shown below ( the data here is the one we created in the cap handler)
Step 3: Create AppRouter
let’s create a approuter folder, you can copy the package.json and xs-app.json files from the git url.
approuter/package.json
{
"name": "approuter",
"description": "Node.js based application router service for html5-apps",
"engines": {
"node": "^8.0.0 || ^10.0.0"
},
"dependencies": {
"@sap/approuter": "8.5.1"
},
"devDependencies": {
"@sap/html5-repo-mock": "1.6.0"
},
"scripts": {
"start": "node node_modules/@sap/approuter/approuter.js"
}
}
approuter/xs-app.json
{
"welcomeFile": "/communitydmcapbooksapp/index.html",
"authenticationMethod": "route",
"routes": [
{
"source": "^/v2/catalog/(.*)$",
"authenticationType": "xsuaa",
"destination": "srv-api",
"csrfProtection": false
}
]
}
Step 4: Update MTA.YAML
You can refer to this section from the git project mta.yaml file for more info.
add approuter module
We will reuse the same SDM (Document Management IntegrationOption) instance here in this project in resources:
We will also add another module to get the ui5 app and to deploy it to cf.
The remaining modules and resources are pretty straightforward, which you can get from the git project.
Step 5: Deploy and Run
BUILD MTA and Deploy.
Access the approuter url
Open the URL:
That’s it folks, remaining part you can check the video at the top šĀ Let me know your thoughts or any improvements needed here.
PS: I’ve used many of the community blogs and the one I referred mostly to do this is Mio Yasutake blog, check it out.
https://blogs.sap.com/2020/09/06/developing-a-fiori-elements-app-with-cap-and-fiori-tools/
Thanks
Mahesh
Thanks for sharing this document.
Hello Mahesh Kumar Palavalli,
Is there any other way to upload and get documents from a Sap Document Management in Cloud Foundry environment?
I need to upload docs automatically (without any other interface), when an user is using a html5 app, which has been deployed on cloud foundry.
Regards.
Hi Pablo,
As Manesh described in this blog post (_createFolder function), you can access SAP Document Management services using the CMIS Rest API, more information in:
Content Management Interoperability Services (CMIS) Version 1.1 (oasis-open.org)
Hello,
Thanks for your informative Blog. Was Curious to know if we decide not to leverage document management service, is there any other way we can connect CMIS complaint external repositories to BTP and access them in CAP applications?
Hi Mahesh Kumar Palavalli,
Thank you for the bog. I'm working on sdm integration also.
My scenario is central app router instead of standalone app router. And it always return 500 error for document service request.
So, do you have some examples about sdm integration with central app router?
Regards
Zhenkun
Hi Mahesh,
thank you for sharing this example. Have you thought about usingĀ CmisJS library instead of your plain axios calls? TheĀ setToken method would also allow you to set the Bearer Token.
Best Regards
Gregor
Hi Mahesh,
very good blog for DMS.
One question, how to integrate the DMS UI in SAP OData V4 instead of V2?
Best Regards,
Ali