https://raw.githubusercontent.com/ajmaradiaga/feeds/main/scmt/topics/SAP-Cloud-SDK-blog-posts.xmlSAP Community - SAP Cloud SDK2026-02-27T12:12:16.784198+00:00python-feedgenSAP Cloud SDK blog posts in SAP Communityhttps://community.sap.com/t5/technology-blog-posts-by-sap/configuration-as-code-cac-with-destinations/ba-p/13699003Configuration as code (CaC) with destinations.2024-05-13T12:54:57.123000+02:00quovadishttps://community.sap.com/t5/user/viewprofilepage/user-id/743<H1 id="toc-hId-865494464">Configuration as code (CaC) with destinations.</H1><P>Destinations are very handy and powerful mechanism to facilitate access to target systems and devices.</P><P>When it comes to SAP BTP destinations, the idea is to manage both <STRONG>subaccount</STRONG> and <STRONG>instance</STRONG> level destinations (and/or their certificates) as<SPAN> </SPAN><STRONG>shared</STRONG><SPAN> </SPAN>configuration resources on a provider subaccount level.</P><P>That way, the destinations configurations can be stored as versioned assets in a source repository and need to be maintained only once per provider, thus, without incurring application runtime tie-in.</P><P>Last but not least, BTP destination service is used as a self-configuration tool.</P><H2 id="toc-hId-798063678"><A href="https://gist.github.com/ptesny/aa8bc30ce043e1e11c145fe15278db62#configuration-as-code-cac-quovadis-master" target="_blank" rel="noopener nofollow noreferrer">Configuration as code with SAP BTP destination service</A></H2><P> </P><TABLE border="1"><TBODY><TR><TD>Table of Contents<OL><LI><A href="https://gist.github.com/ptesny/aa8bc30ce043e1e11c145fe15278db62#cac-dest" target="_blank" rel="noopener nofollow noreferrer">Configuration as code with SAP BTP destination service.</A></LI><OL><LI><A href="https://gist.github.com/ptesny/aa8bc30ce043e1e11c145fe15278db62#create-dest-service" target="_blank" rel="noopener nofollow noreferrer">create shared destination service instance and binding</A>.</LI></OL><LI><A href="https://gist.github.com/ptesny/aa8bc30ce043e1e11c145fe15278db62#create-bootstrap" target="_blank" rel="noopener nofollow noreferrer">Provision bootstrap destinations.</A></LI><OL><LI><A href="https://gist.github.com/ptesny/aa8bc30ce043e1e11c145fe15278db62#get-dest-credentials" target="_blank" rel="noopener nofollow noreferrer">retrieve destination service credentials from binding</A>.</LI><LI><A href="https://gist.github.com/ptesny/aa8bc30ce043e1e11c145fe15278db62#create-bootstrap-payload" target="_blank" rel="noopener nofollow noreferrer">describe bootstrap destination definitions.</A></LI><LI><A href="https://gist.github.com/ptesny/aa8bc30ce043e1e11c145fe15278db62#apply-payload" target="_blank" rel="noopener nofollow noreferrer">create bootstrap destinations on subaccount</A>.</LI></OL><LI><A href="https://gist.github.com/ptesny/aa8bc30ce043e1e11c145fe15278db62#configure-dest" target="_blank" rel="noopener nofollow noreferrer">Configure destination resources.</A></LI><OL><LI><A href="https://gist.github.com/ptesny/aa8bc30ce043e1e11c145fe15278db62#dynamic_dest" target="_blank" rel="noopener nofollow noreferrer">dynamic_dest route with managed approuter</A>.</LI><LI><A href="https://gist.github.com/ptesny/aa8bc30ce043e1e11c145fe15278db62#cloud-sdk" target="_blank" rel="noopener nofollow noreferrer">SAP Cloud SDK built-in destinations</A>.</LI></OL><LI><A href="https://gist.github.com/ptesny/aa8bc30ce043e1e11c145fe15278db62#documentation" target="_blank" rel="noopener nofollow noreferrer">Documentation.</A></LI></OL></TD></TR></TBODY></TABLE><P>PS.</P><P><STRONG>Bootstrap destinations definitions. </STRONG></P><P>Even, if there is no intrinsic BTP CLI command to assist in creation of destinations from service bindings, this can be achieved quite easily with a bit of jq gimmick by applying service binding credentials to a json payload template, for instance:</P><P> </P><pre class="lia-code-sample language-json"><code>{
"init_data": {
"subaccount": {
"destinations": [
{
"Description": "dest-httpbin",
"Type": "HTTP",
"clientId": "sb-clone12847c4c89544b4f9234b26ede429f62!b282590|destination-xsappname!b62",
"HTML5.DynamicDestination": "true",
"HTML5.Timeout": "60000",
"Authentication": "OAuth2ClientCredentials",
"Name": "dest-httpbin",
"tokenServiceURL": "https://<subdomain>.authentication.us10.hana.ondemand.com/oauth/token",
"ProxyType": "Internet",
"URL": "https://httpbin.org",
"tokenServiceURLType": "Dedicated",
"clientSecret": "<clientSecret>"
},
{
"Description": "SAP Destination Service APIs",
"Type": "HTTP",
"clientId": "sb-clone12847c4c89544b4f9234b26ede429f62!b282590|destination-xsappname!b62",
"HTML5.DynamicDestination": "true",
"HTML5.Timeout": "60000",
"Authentication": "OAuth2ClientCredentials",
"Name": "destination-service",
"tokenServiceURL": "https://<subdomain>.authentication.us10.hana.ondemand.com/oauth/token",
"ProxyType": "Internet",
"URL": "https://destination-configuration.cfapps.us10.hana.ondemand.com/destination-configuration/v1",
"tokenServiceURLType": "Dedicated",
"clientSecret": "<clientSecret>"
}
],
"certificates": [
],
"existing_certificates_policy": "update",
"existing_destinations_policy": "update"
}
}
}</code></pre><P> </P><P>Alternatively, one could resort to using SAP Cloud SDK built-in <A href="https://sap.github.io/cloud-sdk/docs/js/features/connectivity/destinations#service-binding-environment-variables" target="_self" rel="nofollow noopener noreferrer">service binding destinations</A>.</P><P>The below nodejs code snippet demonstrates how to leverage SAP Cloud SDK with its service binding destinations with the likes of service manager and destinations services.</P><pre class="lia-code-sample language-yaml"><code>apiVersion: serverless.kyma-project.io/v1alpha2
kind: Function
metadata:
name: {{ .Values.services.srv.name }}
labels:
{{- include "app.labels" . | nindent 4 }}
app: {{ .Values.services.srv.name }}
spec:
runtime: {{ .Values.services.srv.runtime }}
# runtimeImageOverride: {{ .Values.services.srv.runtimeImageOverride }}
source:
inline:
dependencies: |
{
"name": "{{ .Values.services.srv.name }}",
"version": "0.0.1",
"dependencies": {
"axios":"latest"
,"debug": "latest"
,"@sap/xsenv": "latest"
,"@sap-cloud-sdk/http-client": "latest"
,"@sap-cloud-sdk/connectivity": "latest"
,"@sap-cloud-sdk/resilience": "latest"
,"async-retry": "latest"
}
}
source: |
const debug = require('debug')('{{ .Values.services.srv.name }}:function');
const NOT_FOUND = 'Not Found';
const xsenv = require('@sap/xsenv');
const services = xsenv.getServices({
sm: { label: 'service-manager', name: 'saas-sm' }
,
dest: { label: 'destination' }
});
console.log('saas-sm: ', services.sm);
const readServices = xsenv.readServices();
console.log('readServices: ', readServices);
const httpClient = require('@sap-cloud-sdk/http-client');
const cloudSdkConnectivity = require('@sap-cloud-sdk/connectivity');
const { retrieveJwt, decodeJwt, Destination } = require('@sap-cloud-sdk/connectivity');
const { setGlobalLogLevel, createLogger } = require('@sap-cloud-sdk/util');
const { retry } = require ('@sap-cloud-sdk/resilience');
const { resilience } = require ('@sap-cloud-sdk/resilience');
const ResilienceOptions = {
retry: 10,
circuitBreaker: false,
timeout: 300*1000 // 5 minutes in milliseconds
};
const retryme = require('async-retry');
setGlobalLogLevel('debug');
const logger = createLogger('http-logs');
module.exports = {
main: async function (event, context) {
const req = event.extensions.request;
const message = `Hello World`
+ ` from the Kyma Function ${context['function-name']}`
+ ` running on ${context.runtime}!`
+ ` with the request headers ${JSON.stringify(req.headers,0,2)}`;
console.log(message);
if (typeof req.path !== undefined) {
console.log('path: ', JSON.stringify(req.path,0,2))
}
if (typeof req.params !== undefined) {
console.log('params: ', JSON.stringify(req.params,0,2))
}
if (typeof req.url !== undefined) {
console.log('url: ', JSON.stringify(req.url,0,2))
}
if (typeof req.authInfo !== undefined) {
console.log('authInfo: ', JSON.stringify(req.authInfo,0,2))
}
const { pathname } = new URL(req.url || '', `https://${req.headers.host}`)
console.log('pathname: ', pathname)
const url = require("url");
var url_parts = url.parse(req.url);
console.log(url_parts);
console.log(url_parts.pathname);
// returns an array with paths
let path_array = req.url.match('^[^?]*')[0].split('/').slice(1);
console.log(path_array)
console.log(req.url.match('^[^?]*')[0])
if (!path_array?.length) return 'Please use an API verb';
const actions = [
{ name: 'offerings', verb: 'service_offerings', dest: 'saas-sm', url: '/v1/' },
{ name: 'plans', verb: 'service_plans', dest: 'saas-sm', url: '/v1/' },
{ name: 'instances', verb: 'service_instances', dest: 'saas-sm', url: '/v1/' },
{ name: 'bindings', verb: 'service_bindings', dest: 'saas-sm', url: '/v1/' },
{ name: 'instanceDestinations', verb: 'instanceDestinations', dest: 'faas-dest-x509', url: '/destination-configuration/v1/' },
{ name: 'subaccountDestinations', verb: 'subaccountDestinations', dest: 'faas-dest-x509' , url: '/destination-configuration/v1/' }
];
const action = actions.find( ({ name }) => name === path_array[1] )
console.log('action found: ', action)
if (path_array[0] == 'srv' && action !== undefined) {
path_array = req.url.match('^[^?]*')[0].split('/').slice(2);
console.log('path_array: ', path_array)
const queryString = req.query;
console.log('queryString: ', queryString)
const urlParams = new URLSearchParams(queryString);
const params = req.params;
console.log('params: ', params)
try {
// https://sap.github.io/cloud-sdk/docs/js/features/connectivity/destinations#service-binding-environment-variables
const endpoint = path_array[1] !== undefined ? '/' + path_array[1] : '';
console.log(endpoint)
let res = await httpClient.executeHttpRequest({ destinationName: action.dest }, {
method: 'GET',
url: action.url + action.verb + endpoint
});
return res.data;
} catch (err) {
console.log(err.stack);
return err.message;
}
}
}
}
scaleConfig:
maxReplicas: 5
minReplicas: 3
resourceConfiguration:
function:
profile: S
env: ## https://kyma-project.io/docs/kyma/latest/05-technical-reference/00-configuration-parameters/svls-02-environment-variables/#node-js-runtime-specific-environment-variables
- name: FUNC_TIMEOUT ## Specifies the number of seconds in which a runtime must execute the code.
value: '1800'
- name: REQ_MB_LIMIT ## payload body size limit in megabytes.
value: "10"
- name: DEBUG
value: '{{ .Values.services.srv.name }}:*'
- name: SERVICE_BINDING_ROOT
value: /bindings
secretMounts:
- secretName: {{ .Values.services.sm.bindingSecretName }}
mountPath: "/bindings/saas-sm"
- secretName: {{ .Values.services.dest.bindingSecretNamex509 }}
mountPath: "/bindings/faas-dest-x509"</code></pre><P> </P>2024-05-13T12:54:57.123000+02:00https://community.sap.com/t5/technology-blog-posts-by-sap/open-sourcing-the-sap-cloud-sdk-for-java-part-1-preparations/ba-p/13721026Open Sourcing the SAP Cloud SDK for Java: Part 1 - Preparations2024-06-05T16:33:41.214000+02:00matthiaskuhrhttps://community.sap.com/t5/user/viewprofilepage/user-id/624473<P class=""><SPAN class="">The <a href="https://community.sap.com/t5/c-khhcw49343/SAP+Cloud+SDK/pd-p/73555000100800000895" class="lia-product-mention" data-product="484-1">SAP Cloud SDK</a> is a library for developing applications on the SAP Business Technology Platform (BTP). While its <A href="https://github.com/sap/cloud-sdk-js/" target="_blank" rel="noopener nofollow noreferrer">version for JavaScript</A> has been open source since 2020, the <A href="https://github.com/SAP/cloud-sdk-java" target="_blank" rel="noopener nofollow noreferrer"><SPAN class="">Java version</SPAN></A> has been open sourced only recently with the <A href="https://community.sap.com/t5/technology-blogs-by-sap/released-sap-cloud-sdk-for-java-version-5/ba-p/13576668" target="_blank"><SPAN class="">release of version 5.0.0</SPAN></A>.<BR /><BR />This blog post is <STRONG>first in a series</STRONG> where I go over the process of how we moved the <STRONG><A href="https://sap.github.io/cloud-sdk/docs/java/overview-cloud-sdk-for-java" target="_blank" rel="noopener nofollow noreferrer">SAP Cloud SDK for Java</A></STRONG> from an internal code base and development ecosystem to an <STRONG><EM>open source project</EM></STRONG> on GitHub. This has been a journey that spun across 10 months and involved a lot of planning, coordination and technical work.</SPAN></P><P class=""><SPAN class="">We started planning the process in the beginning of 2023 and finally released version <STRONG>5.0.0</STRONG> as the first open source version of the project on December 5th, 2023. Of course, during this time we also worked on other features and improvements for the SDK, but the open source migration was a significant part of our work.</SPAN></P><P class=""><SPAN class="">This first blog post covers some of the non-technical planning and preparation that was necessary to open source the project. Parts <A href="https://community.sap.com/t5/technology-blogs-by-sap/open-sourcing-the-sap-cloud-sdk-for-java-part-2-moving-code/ba-p/13723816" target="_blank">two</A> and <A href="https://community.sap.com/t5/technology-blogs-by-sap/open-sourcing-the-sap-cloud-sdk-for-java-part-3-re-building-the-ci-cd/ba-p/13725638" target="_blank">three</A> will cover the technical migration steps of the codebase itself and the development ecosystem around it.</SPAN></P><H2 id="toc-hId-1016503424"><SPAN class=""><span class="lia-unicode-emoji" title=":thinking_face:">🤔</span>Why Open Source?</SPAN></H2><P class=""><SPAN class="">Choosing to open source the SAP Cloud SDK for Java was a strategic decision, aiming for multiple long term benefits:</SPAN></P><UL><LI><SPAN class="">First, open sourcing the project makes it easier for all developers to use the library. It allows users to quickly peek into the source code to better understand some implementation, to ask for help on the issues tab of the repository or to contribute themselves.</SPAN></LI><LI>Second, it also allowed us to modernize and simplify our tools to build the SDK.</LI><LI>Last, but not least, it allows us to engage more with the developer community and promotes transparency and trust.</LI></UL><P class=""><SPAN class="">These were some of the reasons that ultimately motivated us to take on the challenge of moving the project to open source.</SPAN></P><H2 id="toc-hId-819989919"><SPAN class=""><span class="lia-unicode-emoji" title=":magnifying_glass_tilted_right:">🔎</span> Finding a Starting Point</SPAN></H2><P class=""><SPAN class="">At SAP, we have a dedicated team that helps other teams within SAP open-source their projects: The Open Source Program Office of SAP (OSPO). You can read more about them in this blog post from Michael: <A href="https://community.sap.com/t5/open-source-blogs/managing-open-source-software-with-an-open-source-program-office/ba-p/13516761" target="_blank">Managing Open Source Software with an Open Source Program Office</A>.</SPAN></P><P class=""><SPAN class="">So, the first step was to get in touch with the OSPO and discuss our plans. They helped us understand the requirements and the necessary steps we'd have to take for going open source.</SPAN></P><H2 id="toc-hId-623476414"><SPAN class=""><span class="lia-unicode-emoji" title=":person_fencing:">🤺</span> Taking the First Steps</SPAN></H2><P class=""><SPAN class="">We started by evaluating which parts of the productive codebase were (not) eligible for open sourcing. The code base had grown since its inception in 2016 and contained some functionality that was either not central to the product, only used internally or legacy code.</SPAN></P><P class=""><SPAN class="">We knew that the core functionality of the SDK (e.g. the destination service integration, the OData APIs, the multi-tenancy and resilience features) could be moved to open source without issues. But there were other parts where it was either not immediately clear, or the functionality was completely SAP-internal and not released to Maven Central.</SPAN></P><P class=""><SPAN class="">We had to deal with two main categories of code that we had to either remove or replace:</SPAN></P><OL><LI><SPAN class="">Code that contained SAP intellectual property (IP) and was not eligible for open sourcing.</SPAN></LI><LI>Code that used SAP-internal dependencies.</LI></OL><P class=""><SPAN class="">Both categories required an extensive review of the codebase, its dependencies and the functionality provided by the code.</SPAN></P><P class=""><SPAN class="">For example, we found that the pre-generated OData clients for <a href="https://community.sap.com/t5/c-khhcw49343/SAP+S%25252F4HANA/pd-p/73554900100800000266" class="lia-product-mention" data-product="799-1">SAP S/4HANA</a> were not eligible for open sourcing. In this case we decided that we would deprecate and remove these clients. To make up for this, we instead improved the OData generator which was used to generate these clients, so that users can generate their own version of the clien</SPAN></P><P class=""><SPAN class=""><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="part1-release-notes-vdm-deprecation.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/119301iA6A8E20DCE17DAB9/image-size/large?v=v2&px=999" role="button" title="part1-release-notes-vdm-deprecation.png" alt="part1-release-notes-vdm-deprecation.png" /></span><BR /><BR />In other cases, we decided to remove modules, but ensure the existing modules of version 4 would be compatible with the new version 5 of the SDK. That way existing users could upgrade to the new version while keeping a select few modules of the old version. But since this approach has some challenges (one has to keep maintaining the old modules and continuously ensure compatibility with the new version), we only applied this for a few select cases that were small but important enough to justify the effort.</SPAN></P><P class=""><SPAN class="">Overall, we had to evaluate each impacted feature case by case. That meant getting feedback from existing stakeholders, finding and evaluating any potential alternatives and ultimately making a decision.</SPAN></P><P class=""><SPAN class=""><span class="lia-unicode-emoji" title=":light_bulb:">💡</span>Considering the relatively large code base of version 4 with well over 100 maven modules this was arguably the most challenging part of the open source migration. <STRONG>Overall it took several months until we had a clear picture of what we wanted to open source and what we had to remove or replace.</STRONG></SPAN></P><P class=""><SPAN class="">However, because this naturally is a process that takes time, we worked on other aspects of the migration in parallel to that.</SPAN></P><H2 id="toc-hId-426962909"><SPAN class="">🧹 Code Cleanup</SPAN></H2><P class=""><SPAN class="">While the future of some parts of the code base where still in discussion, we already prepared the rest of the code base to be moved to an open source repository. For example, this meant removing any internal or personal identifiable information from test code. But also inspecting internal documents and code comments, removing e.g. links to internal tools or documentation, internal IP addresses or other internal information.</SPAN></P><P class=""><SPAN class="">While not strictly necessary in all cases, it was important to us that the code is sensible to all developers, not just SAP internal developers. Having inaccessible links or references to internal tools would make it difficult for open source contributors to understand and contribute to the code.<BR /><BR /><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="part1-diff.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/119303iC2F0B3621073FE15/image-size/large?v=v2&px=999" role="button" title="part1-diff.png" alt="part1-diff.png" /></span><BR /></SPAN></P><P class=""><SPAN class="">Considering the project contained around 1800 productive classes and equally many test classes together with around 100 other files like Markdown documents, configuration files and scripts, this task also took some time. But after a few weeks and a good bit of search and replace, we were confident that the code base was ready to be open sourced.</SPAN></P><H2 id="toc-hId-230449404"><SPAN class=""><span class="lia-unicode-emoji" title=":building_construction:">🏗</span>Setting up an Open Source Repository</SPAN></H2><P class=""><SPAN class="">Now it was time to set up the open source repository. That meant creating a new repository within the <A href="https://github.com/SAP" target="_blank" rel="noopener nofollow noreferrer">SAP organization on GitHub</A>. For this process the OSPO team has a <A href="https://github.com/SAP/repository-template" target="_blank" rel="noopener nofollow noreferrer">repository template</A> prepared from which new repositories are created.</SPAN></P><P class=""><SPAN class="">As part of the process we added the necessary documents and links: We set the new license to be <STRONG>Apache-2.0</STRONG>, added the main readme file, drafted our contribution guidelines and linked the code of conduct. The OSPO template navigates you through this process and provides you with a comprehensive checklist as well as any SAP-specific standard texts or references that need to be used.</SPAN></P><P class=""><SPAN class=""><BR /><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="part1-ospo-template.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/119304i9D89EDD0114A3F10/image-size/large?v=v2&px=999" role="button" title="part1-ospo-template.png" alt="part1-ospo-template.png" /></span><BR /></SPAN></P><P class=""><SPAN class="">But, since this repository so far effectively only contained a readme, we kept it <STRONG>internal</STRONG> to the SAP organization until we were ready to open source the actual source code. The actual process of moving over the code base is what I will cover in the next part of this series.</SPAN></P><H2 id="toc-hId-33935899"><SPAN class=""><span class="lia-unicode-emoji" title=":sleeping_face:">😴</span> What we didn't have to do</SPAN></H2><P class=""><SPAN class="">Before wrapping up I think it's worth mentioning that there were also some things we didn't have to do. </SPAN><SPAN class="">For example, we didn't have to apply any larger refactorings to deal with functionality we removed or replaced. Most of the code was already structured into maven modules that were independent enough from each other, so that often we could decide on a per-module basis if and how the code should be open-sourced.</SPAN></P><P class=""><SPAN class="">In addition, we already had <STRONG>strong tooling support</STRONG> set up to ensure that the code base was in a good state overall. This included a comprehensive test suite, but also code scanning tools (e.g. Fortify, Checksytle, PMD etc.) and tools for analyzing dependencies and licenses (e.g. BlackDuck). This made identifying internal dependencies or finding license issues relatively easy.</SPAN></P><P class=""><SPAN class="">And, of course, we had experience from moving the JavaScript variant of the project into open source. We could also refer to other closely related open source projects such as the <A href="https://github.com/SAP/cloud-security-services-integration-library" target="_blank" rel="noopener nofollow noreferrer">SAP BTP Security Services Integration Libraries</A> or the <A href="https://github.com/sap/btp-environment-variable-access/" target="_blank" rel="noopener nofollow noreferrer">SAP BTP Environment Service Binding Access Library</A>. Together with the help and tools provided by the OSPO team this made the process of moving the Java variant into open source significantly easier.</SPAN></P><H2 id="toc-hId--162577606"><SPAN class=""><span class="lia-unicode-emoji" title=":open_book:">📖</span>Conclusion</SPAN></H2><P class=""><SPAN class="">Summing up, we had to do quite some planning and preparation to get the <a href="https://community.sap.com/t5/c-khhcw49343/SAP+Cloud+SDK/pd-p/73555000100800000895" class="lia-product-mention" data-product="484-2">SAP Cloud SDK</a> for Java ready for open sourcing. This was mostly due to the size of the code base and the amount of internal functionality and IP it contained</SPAN></P><P class=""><SPAN class="">But with the help of the <STRONG>OSPO</STRONG> team, the experience from open-sourcing the <STRONG>JavaScript</STRONG> variant and the support from stakeholders we were able to prepare the code base step by step. </SPAN><SPAN class="">In the <A href="https://community.sap.com/t5/technology-blogs-by-sap/open-sourcing-the-sap-cloud-sdk-for-java-part-2-moving-code/ba-p/13723816" target="_blank">next part</A> of this series I will cover the actual process of moving the code base to GitHub and the technical challenges we faced during this process.</SPAN></P><P class=""><SPAN class="">What are your experiences with open sourcing projects? Do you have any questions on the process we went through? Leave your thoughts in the comments below!</SPAN></P>2024-06-05T16:33:41.214000+02:00https://community.sap.com/t5/technology-blog-posts-by-sap/open-sourcing-the-sap-cloud-sdk-for-java-part-2-moving-code/ba-p/13723816Open Sourcing the SAP Cloud SDK for Java: Part 2 - Moving Code2024-06-07T09:21:46.497000+02:00matthiaskuhrhttps://community.sap.com/t5/user/viewprofilepage/user-id/624473<DIV><P><SPAN>The <a href="https://community.sap.com/t5/c-khhcw49343/SAP+Cloud+SDK/pd-p/73555000100800000895" class="lia-product-mention" data-product="484-1">SAP Cloud SDK</a> </SPAN><SPAN>is a library for developing applications on the SAP Business Technology Platform (BTP). While its </SPAN><A href="https://github.com/sap/cloud-sdk-js/" target="_blank" rel="noopener nofollow noreferrer">version for JavaScript</A><SPAN> has been open source since 2020, the </SPAN><A href="https://github.com/SAP/cloud-sdk-java" target="_blank" rel="noopener nofollow noreferrer"><SPAN class="">Java version</SPAN></A><SPAN> has been open sourced only recently with the </SPAN><A href="https://community.sap.com/t5/technology-blogs-by-sap/released-sap-cloud-sdk-for-java-version-5/ba-p/13576668" target="_blank"><SPAN class="">release of version 5.0.0</SPAN></A><SPAN>.</SPAN><BR /><BR />This blog post is the <STRONG>second of a series</STRONG> where I go over the process of how we moved the <A href="https://sap.github.io/cloud-sdk/docs/java/overview-cloud-sdk-for-java" target="_self" rel="nofollow noopener noreferrer">SAP Cloud SDK for Java</A> from an internal code base and development ecosystem to an <STRONG>open source project</STRONG> on GitHub.<BR /><BR /><A href="https://community.sap.com/t5/technology-blogs-by-sap/open-sourcing-the-sap-cloud-sdk-for-java-part-1-preparations/ba-p/13721026" target="_self"><SPAN>Part one</SPAN></A> covered the non-technical planning and preparation that was necessary to open source the project. Finally, <A href="https://community.sap.com/t5/technology-blogs-by-sap/open-sourcing-the-sap-cloud-sdk-for-java-part-3-re-building-the-ci-cd/ba-p/13725638" target="_blank"><SPAN>part three</SPAN></A> will cover the transformation of the CI/CD pipeline and the surrounding automations.</P><H2 id="toc-hId-1016570663"><SPAN><span class="lia-unicode-emoji" title=":chart_increasing:">📈</span> </SPAN>The Overall Strategy</H2><P>Right from the get-go it was clear that we would release a <STRONG>new major version</STRONG><SPAN> </SPAN>of the SDK once the open source migration was complete. The open source version would come with a new license and significant changes to the feature scope, dropping support for some legacy features and platforms. With the current version being 4.X at the time, we set the goal to release <STRONG>version 5.0.0</STRONG><SPAN> </SPAN>as the first open source version of the SDK.</P><P>Still, we would have to continue developing the current version 4 for some time, at least up until a few weeks before the release of version 5. And even after that, we would have to maintain version 4 and provide bug fixes and security updates for some time.</P><P>Next, we had decided to replace our existing (Jenkins) pipelines and related tooling (e.g. for static code checks, dependency updates etc.) in favor of more modern alternatives that are more suitable for open source projects. We decided to completely switch to <A href="https://github.com/features/actions" target="_self" rel="nofollow noopener noreferrer"><SPAN>GitHub Actions</SPAN></A> for our pipelines and automations, and also use other tools provided by GitHub for code quality and security checks (e.g. CodeQL, Dependabot). This would require some time to set up and test.</P><P>Finally, we wanted to have a smooth transition with as little downtime to the development process as possible. Ideally, we copy over the code to the new repository and have all the CI/CD tooling and automations in place and working, guaranteeing the same or even better code quality compared to the internal repository.</P><H2 id="toc-hId-820057158"><SPAN><span class="lia-unicode-emoji" title=":delivery_truck:">🚚</span> </SPAN>How We Moved the Code Base</H2><P>With these requirements in mind we came up with a plan to move the code base over to the open source repository. It comprised of three steps:</P><OL><LI>Copy a representative sample of the code to the open source repository and develop the CI/CD pipelines.</LI><LI>Create a v5 branch in the internal repository and develop the open source version in parallel.</LI><LI>Move the v5 branch over to the open source repository and press the release button.</LI></OL><P>Copying a sample allowed us to develop the CI/CD pipeline and all related tooling in parallel to the code development. Also, it allowed us to split the code base as late as possible, reducing the time period where we need to maintain two repositories.</P><H2 id="toc-hId-623543653"><SPAN>🧪 </SPAN>Creating a Representative Sample</H2><P>Once we had created the empty repository on GitHub we copied around 5-10 modules or roughly <STRONG>10 % of the code base</STRONG><SPAN> </SPAN>over to the open source repository. We carefully chose a subset of the code that we knew would change relatively little in the near future and that would be representative of the whole code base. The code sample was representative in the sense that it included various different kinds of Maven modules: Typical modules that would get shipped as JAR, parent and BOM modules that are used for dependency management, modules that would get shipped as a Maven plugin or Maven archetype and test modules that wouldn't get shipped.</P><P>This allowed us to implement our CI/CD pipelines on a subset of the code that would be representative of the whole code base. Also, this meant this work can be done in parallel and completely independent of the development on the internal repository. In the end, the sample code allowed us to fully prepare all CI/CD tooling with a high degree of confidence that once we move over the rest of the code base everything would work as expected.</P><H2 id="toc-hId-427030148"><SPAN><span class="lia-unicode-emoji" title=":shuffle_tracks_button:">🔀</span> </SPAN>Creating a Branch for V5</H2><P>We then created a v5 branch in the internal repository and started removing any code that would not be open sourced. We also set up an automation right away that would merge any changes done to v4 automatically into the v5 branch.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="part2-git.png" style="width: 527px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/120513i8E74D604270CF074/image-size/large?v=v2&px=999" role="button" title="part2-git.png" alt="part2-git.png" /></span></P><P>That way we could continue developing the current version 4 without the risk of forgetting to port relevant changes to the v5 branch. Still, maintaining two branches is additional work, so we pushed creating the v5 branch back as far as possible.</P><H2 id="toc-hId-230516643"><span class="lia-unicode-emoji" title=":up_right_arrow:">↗️</span>Moving to the Open Source Repository</H2><P>Finally, once all changes required for the open source migration on the v5 branch were complete and the open source repository was set up with all required CI/CD tooling, we moved over the v5 branch to the open source repository. This step almost fully replaced the existing code sample we had copied over earlier. Only the parts relevant to the CI/CD setup were kept and merged with the incoming v5 branch.</P><P>On the 2nd of October around <STRONG>2500 files</STRONG><SPAN> </SPAN>were copied over from the internal to the open source repository with <A href="https://github.com/SAP/cloud-sdk-java/pull/83" target="_self" rel="nofollow noopener noreferrer"><SPAN>this pull request</SPAN></A>. We implemented the necessary adjustments for the CI/CD pipelines to work on the full code base (e.g. adjusting the thresholds for the code scans and test coverage, adjusting the copied over pom.xml files etc.).</P><P>Copying over also meant we would lose the commit history of the code. While technically we could have kept it, it would have contained all the internal code and information we had to remove from the code base. Therefore, we decided to start with a clean commit history in the open source repository.</P><P>The process finished on the 4th of October where the pull request was merged and the internal v5 branch archived.</P><H2 id="toc-hId-34003138"><SPAN><span class="lia-unicode-emoji" title=":open_book:">📖</span></SPAN> Summing Up</H2><P>Overall, we were quite happy with how the move of the code base went. Having the work on pipelines and tooling done independently and in parallel to the development on the internal repository meant we didn't lose any time or sacrifice code quality in the process. It also made it easier to involve other from colleagues outside our development team who had more experience with GitHub Actions and helped us set up the pipelines.</P><P>Here is a timeline of the overall process:</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="part2-timeline.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/120515i852A3CBF3063AEED/image-size/large?v=v2&px=999" role="button" title="part2-timeline.png" alt="part2-timeline.png" /></span></P><P>In the <A href="https://community.sap.com/t5/technology-blogs-by-sap/open-sourcing-the-sap-cloud-sdk-for-java-part-3-re-building-the-ci-cd/ba-p/13725638" target="_blank">third and final part</A> of this series I will cover how we used GitHub Actions to implement our all our CI/CD pipelines and how we automated the release process.</P></DIV>2024-06-07T09:21:46.497000+02:00https://community.sap.com/t5/technology-blog-posts-by-sap/open-sourcing-the-sap-cloud-sdk-for-java-part-3-re-building-the-ci-cd/ba-p/13725638Open Sourcing the SAP Cloud SDK for Java: Part 3 - Re-Building the CI/CD Automations2024-06-10T14:00:23.734000+02:00matthiaskuhrhttps://community.sap.com/t5/user/viewprofilepage/user-id/624473<P><SPAN>The <A class="" href="https://community.sap.com/t5/c-khhcw49343/SAP+Cloud+SDK/pd-p/73555000100800000895" target="_blank">SAP Cloud SDK</A> </SPAN><SPAN>is a library for developing applications on the SAP Business Technology Platform (BTP). While its </SPAN><A href="https://github.com/sap/cloud-sdk-js/" target="_blank" rel="noopener nofollow noreferrer">version for JavaScript</A><SPAN> has been open source since 2020, the </SPAN><A href="https://github.com/SAP/cloud-sdk-java" target="_blank" rel="noopener nofollow noreferrer"><SPAN class="">Java version</SPAN></A><SPAN> has been open sourced only recently with the </SPAN><A href="https://community.sap.com/t5/technology-blogs-by-sap/released-sap-cloud-sdk-for-java-version-5/ba-p/13576668" target="_blank"><SPAN class="">release of version 5.0.0</SPAN></A><SPAN>.</SPAN><BR /><BR /><SPAN>This blog post is the </SPAN><STRONG>final one of a series</STRONG><SPAN> where I go over the process of how we moved the </SPAN><A href="https://sap.github.io/cloud-sdk/docs/java/overview-cloud-sdk-for-java" target="_self" rel="nofollow noopener noreferrer">SAP Cloud SDK for Java</A><SPAN> from an internal code base and development ecosystem to an </SPAN><STRONG>open source project</STRONG><SPAN> on GitHub.</SPAN><BR /><BR /><A href="https://community.sap.com/t5/technology-blogs-by-sap/open-sourcing-the-sap-cloud-sdk-for-java-part-1-preparations/ba-p/13721026" target="_blank">Part one</A> covered the non-technical preparations and planning that were necessary to open source the project. <A href="https://community.sap.com/t5/technology-blogs-by-sap/open-sourcing-the-sap-cloud-sdk-for-java-part-2-moving-code/ba-p/13723816" target="_blank">Part two</A> went into the details of how we moved the code base over from an internal repository to the open source repository on GitHub. Finally, <STRONG>this post</STRONG> will cover the <STRONG>transformation of the CI/CD pipelines</STRONG> and the surrounding automations.</P><H2 id="toc-hId-1016628387"><SPAN><span class="lia-unicode-emoji" title=":index_pointing_up:">☝️</span></SPAN>Requirements for our CI/CD Automation</H2><P>For our internal repository we were using an internal <STRONG>Jenkins</STRONG> server to run most of our automations. The pipelines had grown over the years and were quite complex:</P><UL><LI>The main Jenkins pipeline for PR and main branch builds alone had grown to over <STRONG>1k lines of Groovy</STRONG> code.</LI><LI>The release was mostly handled by Python scripts with roughly 1.5k lines of code.</LI><LI>The pipelines were integrated with other internal systems and tools.</LI></UL><P>It was clear that we had to replace the current setup with a publicly accessible and more modern alternative. We made the decision to completely move to <STRONG>GitHub Actions</STRONG>. Considering the size of the code base and the high degree of automation this was a significant challenge.</P><P>To better understand the solution we went for, let's look at some of the requirements we had for the CI/CD automation:</P><UL><LI><STRONG>Pull requests</STRONG> should automatically be built, tested and validated against quality checks (e.g. test coverage, static code checks etc.).</LI><LI><STRONG>Commits</STRONG> on the main branch should automatically be deployed to an internal Maven snapshot repository.</LI><LI>As part of this, commits on the main branch must also pass all requirements that hold for pull requests plus an additional check on licenses and security vulnerabilities on dependencies.</LI><LI>In terms of quality, any commit on the main branch might be released as a productive version.</LI><LI><STRONG>Releases to Maven Central</STRONG> should be done using a fully automated pipeline, performing several steps:</LI><LI>Release builds must pass all quality checks that hold for commits on the main branch.</LI><LI>The release pipeline should automatically increment the version number, create a release tag and publish the release notes and JavaDocs.</LI><LI>The pipeline must also distinguish between modules intended for public release and internal test modules which are not to be released.</LI><LI>Finally, the release pipeline should automatically deploy all artifacts intended for release to Maven Central.</LI></UL><P>These requirements were largely already in place for our internal repository, and it was crucial for us to have the same or even higher levels of quality and automation for the open source repository. Here is how we did it.</P><H2 id="toc-hId-820114882"><SPAN><span class="lia-unicode-emoji" title=":bookmark:">🔖</span></SPAN>Architecture of our GitHub Workflows</H2><P>We use several workflows that build on each other to achieve the requirements mentioned above. I'll go through them step by step and explain how we use them to fully automate everything from PR builds to deploying to Maven Central.</P><H3 id="toc-hId-752684096"><SPAN><span class="lia-unicode-emoji" title=":white_heavy_check_mark:">✅</span> </SPAN>The CI Workflow</H3><P>At the heart of our CI/CD automation is the <A href="https://github.com/SAP/cloud-sdk-java/blob/main/.github/workflows/continuous-integration.yaml" target="_blank" rel="noopener nofollow noreferrer"><SPAN>continuous-integration</SPAN> workflow</A>. It is triggered on every pull request and comprises multiple jobs, from building the code to running tests and quality checks.<BR /><BR />Here is a simplified excerpt from the <A href="https://github.com/SAP/cloud-sdk-java/blob/main/.github/workflows/continuous-integration.yaml#L102" target="_blank" rel="noopener nofollow noreferrer"><SPAN>build job</SPAN></A>:</P><DIV><PRE><SPAN>build:</SPAN>
<SPAN>name: "Build"</SPAN>
<SPAN>needs: [ context, check-formatting ]</SPAN>
<SPAN>runs-on: ubuntu-latest</SPAN>
<SPAN>steps:</SPAN>
<SPAN>- name: "Checkout repository"</SPAN>
<SPAN>uses: actions/checkout@v4</SPAN>
<SPAN># ...</SPAN>
<SPAN>- name: "Setup java"</SPAN>
<SPAN>uses: actions/setup-java@v4</SPAN>
<SPAN># ...</SPAN>
<SPAN>- name: "Restore Dependencies"</SPAN>
<SPAN>id: restore-dependencies</SPAN>
<SPAN>uses: actions/cache/restore@v4</SPAN>
<SPAN># ...</SPAN>
<SPAN>- name: "Build SDK"</SPAN>
r<SPAN>un: |</SPAN>
<SPAN>MVN_ARGS="${{ env.MVN_MULTI_THREADED_ARGS }} install -DskipTests -DskipFormatting"</SPAN>
<SPAN># ...</SPAN>
<SPAN>echo "[DEBUG] Running Maven with following arguments: $MVN_ARGS"</SPAN>
<SPAN>mvn $MVN_ARGS</SPAN>
<SPAN> - name: "Cache Dependencies"</SPAN>
<SPAN> if: ${{ steps.restore-dependencies.outputs.cache-hit != 'true' }}</SPAN>
<SPAN>uses: actions/cache/save@v4</SPAN>
<SPAN># ...</SPAN>
<SPAN> # ..</SPAN> </PRE></DIV><P>This is how it looks like when triggered on a pull request:</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="part3-pr-build.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/121302i3F805B11486D507D/image-size/large?v=v2&px=999" role="button" title="part3-pr-build.png" alt="part3-pr-build.png" /></span><BR />Aside from the typical <EM>build</EM><SPAN> </SPAN>and <EM>test</EM><SPAN> </SPAN>jobs you can see some of the tools we use to ensure code quality, such as CodeQL, Checkstyle, PMD and SpotBugs. Some other tools such as BlackDuck are shown but disabled for pull request builds. You'll also notice that the workflow is designed to fail fast, having fast running jobs at the beginning and more time-consuming jobs in parallel or at a later stage, wherever possible.</P><P>Finally, you'll notice the <EM>Collect Context</EM><SPAN> </SPAN>job at the start of the workflow. This becomes relevant later when we look at how we use the CI workflow for builds on the main branch and releases.</P><P>The details of the individual jobs such as <EM>build</EM><SPAN> </SPAN>and <EM>test</EM><SPAN> </SPAN>contain much more than what is shown in the diagram. I'll explain some of the tips and tricks we used to get them to work efficiently and reliably further down in this post. For now, let's move on to how we use the CI workflow for builds on the main branch and releases.</P><H3 id="toc-hId-556170591"><SPAN><span class="lia-unicode-emoji" title=":camera_with_flash:">📸</span></SPAN> The Main Branch Workflow</H3><P>The <A href="https://github.com/SAP/cloud-sdk-java/blob/main/.github/workflows/main-build.yaml" target="_blank" rel="noopener nofollow noreferrer"><SPAN>main build</SPAN></A> is triggered by commits on the main branch. In addition to running the same checks as on pull requests, it should also run a BlackDuck scan and deploy the current snapshot to an internal Maven repository.</P><P>This is how it looks like when triggered on the main branch:</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="part3-main-build.png" style="width: 964px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/121305iAA3BF2A0B6C98B92/image-size/large?v=v2&px=999" role="button" title="part3-main-build.png" alt="part3-main-build.png" /></span>As you can see, the main build triggers the CI workflow and runs the deployment afterward. When triggering the CI workflow it passes a parameter to inform the workflow it should also run the BlackDuck scan. Finally, in case the workflow fails it also sends a Slack notification to the development team.</P><H3 id="toc-hId-359657086"><SPAN><span class="lia-unicode-emoji" title=":package:">📦</span> </SPAN>The Release Workflow</H3><P>We are using a two-step process for releasing to Maven Central, comprised of two workflows: <A href="https://github.com/SAP/cloud-sdk-java/blob/main/.github/workflows/prepare-release.yaml" target="_blank" rel="noopener nofollow noreferrer"><SPAN>prepare-release</SPAN></A> and <A href="https://github.com/SAP/cloud-sdk-java/blob/main/.github/workflows/perform-release.yml" target="_blank" rel="noopener nofollow noreferrer"><SPAN>perform-release</SPAN></A>. Both workflows are triggered manually and are designed to provide as much automation while being as reliable as possible.</P><P>The <EM>prepare-release</EM><SPAN> </SPAN>workflow first creates a branch for the release and increments the projects version to the desired release version. Then it triggers the CI workflow on the commit with the release version set and with additional parameters to sign the produced artifacts with a GPG key and enable JavaDoc generation.</P><P>Once the CI build completes, it creates a release tag, a draft release and pull requests for the release notes and JavaDocs. Finally, if all steps succeeded, it increases the version number again to the next snapshot version and raises a pull request on the code base. If any of the steps failed the workflow will roll back the changes.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="part3-prepare-release.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/121310iF8ACC0BFC321D7DA/image-size/large?v=v2&px=999" role="button" title="part3-prepare-release.png" alt="part3-prepare-release.png" /></span></P><P>If everything looks good we trigger the <EM>perform-release</EM><SPAN> </SPAN>workflow which will deploy the artifacts to Maven Central and merge the open pull requests.</P><P>This two-step approach allows us to ensure everything is in place and functional before actually deploying the new version. For example, we can tweak the release notes or the GitHub release description while still having the process fully automated. And in case anything goes wrong it is much easier to fix it, compared to having one large release pipeline.</P><H2 id="toc-hId-34060862"><SPAN><span class="lia-unicode-emoji" title=":magnifying_glass_tilted_right:">🔎</span> </SPAN>The Details of GitHub Actions and our Maven Project</H2><P>Now that you have an idea of how the overall architecture of our GitHub workflows looks like, let's dive into some of the details of the workflows. Please keep in mind that this part is somewhat specific to building larger <STRONG>Java</STRONG> projects using <STRONG>Maven</STRONG>.</P><H3 id="toc-hId--33369924"><SPAN><span class="lia-unicode-emoji" title=":open_file_folder:">📂</span> </SPAN>Keeping Track of Build Artifacts</H3><P>We are using several jobs in our workflows that depend on each other. For example, the <EM>build</EM><SPAN> </SPAN>job produces the artifacts that are then used in the <EM>test</EM><SPAN> </SPAN>job.</P><P>Now, for this to work one has to share the artifacts produced in the <EM>build</EM><SPAN> </SPAN>job with the <EM>test</EM><SPAN> </SPAN>job. This turned out to not be as straightforward as one might think. Some tasks would require the compiled classes (e.g. running tests), others would require the JAR files (e.g. the signing step) and others would require the JARs to be installed in the local Maven repository (e.g. the archetype test stage).</P><P>We solved this by using the <A href="https://github.com/actions/upload-artifact" target="_blank" rel="noopener nofollow noreferrer"><SPAN>upload-artifact</SPAN></A> and <A href="https://github.com/actions/download-artifact" target="_blank" rel="noopener nofollow noreferrer"><SPAN>download-artifact</SPAN></A> actions. We configured them to upload all <FONT face="courier new,courier">./**/target/**</FONT><SPAN> </SPAN>directories produced by the <EM>build</EM><SPAN> </SPAN>job. We also upload the part of the local Maven repository that contains our own artifacts (<FONT face="courier new,courier">~/.m2/repository/com/sap/cloud/sdk/**</FONT>). Subsequent jobs that require these artifacts download them.</P><P>To speed up build times we also use a cache for our dependencies. That means dependencies don't have to be re-downloaded for every build, which speeds up the build significantly. However, because our own artifacts are also installed into the local Maven repository, we have to be careful to exclude them from this cache. Otherwise, build results from previous builds would leak into subsequent runs. So we use exclusion patterns when setting up the cache to exclude our own artifacts.</P><H3 id="toc-hId--229883429"><SPAN><span class="lia-unicode-emoji" title=":repeat_button:">🔁</span> </SPAN>Dealing with the Maven Build Lifecycle</H3><P>Maven has a well-defined <A href="https://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html" target="_blank" rel="noopener nofollow noreferrer"><SPAN>build lifecycle</SPAN></A> where each phase builds on the previous phase. This becomes a bit of a challenge if you have a larger project and want to separate out individual build steps.</P><P>For example, running <FONT face="courier new,courier">mvn compile</FONT><SPAN> </SPAN>in one job, uploading the artifacts, then downloading them and running <FONT face="courier new,courier">mvn test</FONT><SPAN> </SPAN>in another job would internally run the <EM>compile</EM><SPAN> </SPAN>phase again. This happens because uploading and downloading the artifacts affects the file timestamps and Maven considers the files to be outdated.</P><P>In addition, we are using a lot of Maven plugins to verify various aspects of our code base. We are using multiple formatting plugins, static code analysis plugins, test coverage plugins, dependency analyzers and more. To speed up the build process and to fail fast we want to run these checks as early as possible and in parallel.</P><P>To solve this, we are invoking the plugins explicitly in their dedicated jobs (e.g. formatting and code style analysis). For the <EM>build</EM><SPAN> </SPAN>job we run <FONT face="courier new,courier">mvn install</FONT><SPAN> </SPAN>and exclude tests as well as any plugins that run elsewhere via specific properties. Finally, the <EM>test</EM><SPAN> </SPAN>stage invokes the Maven surefire plugin and the code coverage plugin directly to avoid recompiling the code.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="part3-test.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/121364iA98EF721D01D68AD/image-size/large?v=v2&px=999" role="button" title="part3-test.png" alt="part3-test.png" /></span></P><P>While saving a few minutes of build time, this also gives us the flexibility to turn individual checks on or off. Last, but not least, splitting the build process into individual jobs makes it much easier to see where a build failed and why.</P><P>However, this requires a good understanding of your Maven project and how you have configured your profiles and plugins.</P><H3 id="toc-hId--426396934"><SPAN><span class="lia-unicode-emoji" title=":pushpin:">📌</span> </SPAN>Keeping Track of which Artifacts to Release</H3><P>The Cloud SDK is a multi-module Maven project with several modules that are not intended for public release. For example, we have modules like <FONT face="courier new,courier">testutil</FONT><SPAN> </SPAN>or <FONT face="courier new,courier">odata-api-sample</FONT><SPAN> </SPAN>that are only used for testing. When releasing to Maven Central we want to make sure that only modules that are intended for public release are actually deployed. This can be achieved using Maven profiles, for example by listing the test modules only under a certain profile.</P><P>But we found that this solution isn't reliable enough for our purposes. For example, a profile may be forgotten to be (de-) activated within any of the various steps in our build pipeline. Or, if a module is moved or renamed, one has to be extra careful the profile continues to apply after the refactoring.</P><P>To solve this, we are using a custom script that builds and verifies the <A href="https://github.com/SAP/cloud-sdk-java/blob/main/module-inventory.json" target="_blank" rel="noopener nofollow noreferrer"><SPAN>module-inventory.json</SPAN></A> file. It contains a list of all modules, their intended release scope as well as further information (e.g. if it requires a license and security scan, or if the module is in beta, production ready or deprecated). This information is explicitly declared via properties in each module's <FONT face="courier new,courier">pom.xml</FONT><SPAN> </SPAN>file and thus immediately visible to anyone working on the project.<BR />Any changes would be detected by the CI pipeline and the build would fail if the <FONT face="courier new,courier">module-inventory.json</FONT><SPAN> </SPAN>has not been updated accordingly.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="part3-module-inventory.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/121365i8DF7ABF880E502EF/image-size/large?v=v2&px=999" role="button" title="part3-module-inventory.png" alt="part3-module-inventory.png" /></span></P><P>Finally, the release pipeline uses the <FONT face="courier new,courier">module-inventory.json</FONT><SPAN> </SPAN>to determine which modules to release and deploys exactly those artifacts.</P><H2 id="toc-hId--751993158"><span class="lia-unicode-emoji" title=":light_bulb:">💡</span>Challenges and Learnings</H2><P>One of the challenges we faced was how to develop and test the workflows. GitHub Actions can not be easily re-ran or debugged locally, each change in a workflow file has to be committed and pushed to the repository. But we didn't want to spam our repositories with commits, tags, releases and pull requests during the development phase. Furthermore, we didn't want to publish any artifacts to Maven Central during the development and testing phase. Still, we wanted to test the release pipeline as close to the real process as possible.</P><P>To solve the first issue we used <STRONG>forks</STRONG> of the repository for developing and testing the workflows until they were close enough to the final version. Some of the larger changes required <STRONG>200-300 commits</STRONG> until we got the desired result (for example, see <A href="https://github.com/SAP/cloud-sdk-java/pull/259" target="_blank" rel="noopener nofollow noreferrer"><SPAN>this PR</SPAN></A>). When merging we squashed everything into a single commit to not clutter the version history.</P><P>Also, we limited pipeline steps to only a few lines of shell commands per step. For anything more complex (e.g. generating a JSON file, generating a test report XML file, etc.) we created <SPAN><A href="https://github.com/SAP/cloud-sdk-java/tree/main/.pipeline/scripts" target="_blank" rel="noopener nofollow noreferrer">Python scripts</A></SPAN> that could be tested independently on a local machine. Finally, we tested our release automation by deploying snapshots to the <SPAN><A href="https://s01.oss.sonatype.org/content/repositories/snapshots/" target="_blank" rel="noopener nofollow noreferrer">Sonatype snapshot repository</A></SPAN>. This also allowed us to give SAP-external developers access to a release candidate for testing.</P><H2 id="toc-hId--601252306"><SPAN><span class="lia-unicode-emoji" title=":open_book:">📖</span></SPAN> Conclusion</H2><P>After several iterations and a lot of testing we now have fully automated CI/CD pipelines that meet our requirements. Using GitHub Actions we were able to replace our complex Jenkins setup with a modern and publicly accessible alternative. That enables also SAP-external developers to contribute to the project and accelerates the development of the SAP Cloud SDK for Java.</P><P>That concludes this series on open sourcing the SAP Cloud SDK for Java. Please don't hesitate to share your thoughts on the migration process or what you may have done differently. How do you build your Java projects with GitHub Actions? Do you also use multiple workflows and jobs, do you use re-usable workflows or custom actions? Or do you have one large workflow that does everything? As usual, let me know in the comments below <SPAN><span class="lia-unicode-emoji" title=":winking_face:">😉</span></SPAN>.</P>2024-06-10T14:00:23.734000+02:00https://community.sap.com/t5/technology-blog-posts-by-members/custom-spring-boot-application-in-sap-btp-cf-integrates-with-btp-document/ba-p/13797941Custom Spring Boot application in SAP BTP CF integrates with BTP Document Management Service(DMS2024-08-17T01:58:19.050000+02:00Sudhir_Lenkahttps://community.sap.com/t5/user/viewprofilepage/user-id/208695<P><STRONG>Introduction</STRONG></P><P>The SAP BTP Document Management Service(DMS) helps in managing business documents. It's based on the OASIS (Organization for the Advancement of Structured Information Standards) industry standard CMIS (Content Management Interoperability Services) and includes features like versioning, hierarchies, access control, and document management.</P><P>In this blog, I am going to explain how to create a custom spring boot application in SAP BTP cloud foundry and integrate it with SAP BTP DMS.</P><P><STRONG>Business Scenario</STRONG></P><P>Let’s say we have a requirement where we need to replicate documents from different SAP SaaS applications(e.g. SAP SuccessFactors) to SAP BTP Document Management Service. In this case, we can create a custom Java Spring Boot application and deploy it to BTP Cloud Foundry as an MTA(multi-target application). Spring Boot application can have scheduled Cron Jobs(if there is any need for automation) or REST APIs(to be triggered from UI and any other application) to interact with DMS rest APIs and SuccessFactors.</P><P>Or if you have a custom application, and you want to use SAP BTP DMS for document management then this example will be helpful.</P><P><STRONG>Technical Details</STRONG></P><P> In this blog, I will create a maven-based spring boot application(MTA) and will expose a REST API that will upload a document in the DMS. Spring Boot application will interact with DMS REST API through BTP destinations. The main reason for using BTP destinations is the DMS authentication will be taken care of by BTP destinations. I will be using SAP Cloud SDK to interact with BTP Destinations from Spring Boot.</P><P><STRONG>Architecture</STRONG></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="sudhiranjan_lenka_0-1723849924131.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/153370iF60976361FC98564/image-size/medium?v=v2&px=400" role="button" title="sudhiranjan_lenka_0-1723849924131.png" alt="sudhiranjan_lenka_0-1723849924131.png" /></span></P><P><STRONG>Prerequisite </STRONG></P><OL><LI>Access to SAP BTP Cloud Foundry, and org & space are created.</LI><LI>Access to BTP destination.</LI><LI>Access to SAP BTP DMS service and DMS repository is created.</LI><LI>Eclipse(with Spring tool suite and Lombok plugin) and JDK(1.8) are installed in the development system.</LI><LI>Cloud Foundry command line interface (CF CLI) is installed, and the path is set in the environment variable.</LI><LI>Cloud MTA build tool (MBT) is installed, and the path is set in the environment variable.</LI><LI>Make tool is installed, and the path is set in the environment variable.</LI></OL><P><STRONG>Create a destination in SAP BTP for DMS service</STRONG></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="sudhiranjan_lenka_1-1723849924142.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/153368iEFDD16C8913FC300/image-size/medium?v=v2&px=400" role="button" title="sudhiranjan_lenka_1-1723849924142.png" alt="sudhiranjan_lenka_1-1723849924142.png" /></span></P><P> </P><UL><LI>Please make sure the URL contains the repository ID and folder name(if no folder is created in the repository, then use root)</LI></UL><P><STRONG>Steps to create the Application</STRONG></P><OL><LI>Create a maven based spring boot project in Eclipse.</LI></OL><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="sudhiranjan_lenka_2-1723849924148.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/153369i4E4B038C9156DD53/image-size/medium?v=v2&px=400" role="button" title="sudhiranjan_lenka_2-1723849924148.png" alt="sudhiranjan_lenka_2-1723849924148.png" /></span></P><OL><LI>Open the pom.xml file and replace it with the below code.</LI></OL><P> </P><pre class="lia-code-sample language-markup"><code><?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.0.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.sl</groupId>
<artifactId>SampleSAPBTPApplication</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>SampleSAPBTPApplication</name>
<description>Sample SAP BTP App with Spring Boot</description>
<url/>
<licenses>
<license/>
</licenses>
<developers>
<developer/>
</developers>
<scm>
<connection/>
<developerConnection/>
<tag/>
<url/>
</scm>
<properties>
<java.version>1.8</java.version>
<swagger.version>2.9.2</swagger.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.sap.cloud.sdk.cloudplatform</groupId>
<artifactId>scp-cf</artifactId>
<version>3.46.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web-services</artifactId>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.10</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpmime</artifactId>
<version>4.5.10</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>${swagger.version}</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>${swagger.version}</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-bean-validators</artifactId>
<version>${swagger.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<packaging>war</packaging>
</project></code></pre><P> </P><P> </P><OL><LI>Create a Java class and replace the code with the below code (please check the package and class name).</LI></OL><P> </P><pre class="lia-code-sample language-java"><code>package com.sl.demo.api;
import java.io.IOException;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import com.sap.cloud.sdk.cloudplatform.connectivity.DestinationAccessor;
import com.sap.cloud.sdk.cloudplatform.connectivity.HttpClientAccessor;
import com.sap.cloud.sdk.cloudplatform.connectivity.HttpDestination;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
@RestController
public class BTPDMSService {
private final Logger log = LoggerFactory.getLogger(this.getClass());
/**
* Attaches a generic file to an existing work.
*/
@RequestMapping(method = RequestMethod.POST, path = "/uploadFile", produces = "application/json", consumes = "multipart/form-data")
@ApiOperation(value = "Attaches a generic file to an existing work.")
@ApiResponses({ @ApiResponse(code = 200, message = "OK", responseContainer = "Map", response = Long.class),
@ApiResponse(code = 400, message = "Bad request"),
@ApiResponse(code = 401, message = "Unauthorized, basic auth required"),
@ApiResponse(code = 403, message = "Forbidden") })
public String uploadFile(@RequestParam("attachment") MultipartFile file) throws IOException {
log.info("Upload file started");
String DESTINATION_NAME = "MyDestinationForDMS";
byte[] content = file.getBytes();
String filename = file.getOriginalFilename();
String contentType = file.getContentType();
HttpDestination destination = DestinationAccessor.getDestination(DESTINATION_NAME).asHttp();
HttpClient httpclient = HttpClientAccessor.getHttpClient(destination);
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
log.info("Uploading file : {}", filename);
builder.addTextBody("cmisaction", "createDocument");
builder.addTextBody("propertyId[0]", "cmis:name");
builder.addTextBody("propertyValue[0]", "testSudhir01.json");
builder.addTextBody("propertyId[1]", "cmis:objectTypeId");
builder.addTextBody("propertyValue[1]", "cmis:document");
builder.addTextBody("filename", "testSudhir01");
builder.addTextBody("_charset_", "UTF-8");
builder.addTextBody("includeAllowableAction", "true");
builder.addTextBody("succinct", "true");
builder.addBinaryBody("media", content, ContentType.create(contentType), filename);
HttpEntity postData = builder.build();
HttpPost httpPost = new HttpPost();
httpPost.setEntity(postData);
HttpResponse response = httpclient.execute(httpPost);
HttpEntity entity = response.getEntity();
log.info("Response from post File: {}", response.getStatusLine().getStatusCode());
log.info("******** Response Body **********: {}", EntityUtils.toString(entity));
return "Executed with status Code:" + response.getStatusLine().getStatusCode();
}
}</code></pre><P> </P><OL><LI>Create a Java class for swagger config and replace the code with the below code.</LI></OL><P> </P><pre class="lia-code-sample language-java"><code>package com.sl.demo.swagger;
import java.util.Collections;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import springfox.bean.validators.configuration.BeanValidatorPluginsConfiguration;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
@Configuration
@EnableSwagger2
@Import(BeanValidatorPluginsConfiguration.class)
public class SpringFoxConfig {
String appTitle = "DMS Integration";
@Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo()).select()
.apis(RequestHandlerSelectors.basePackage(parentPackageName())).paths(PathSelectors.ant("/**")).build();
}
private String parentPackageName() {
String thisPackageName = getClass().getPackage().getName();
int lastDotPos = thisPackageName.lastIndexOf('.');
String parentPackageName = thisPackageName.substring(0, lastDotPos);
return parentPackageName;
}
private ApiInfo apiInfo() {
return new ApiInfo(appTitle + " API", "This is the API for the " + appTitle + " application.", "v1", null,
new Contact("SL", "", "test@sl.com") , null, null,
Collections.EMPTY_LIST);
}
}</code></pre><P> </P><OL><LI>Create a mta.yaml file in the workspace (where the project is created) and paste the below code into the mta.yaml file.</LI></OL><P> </P><pre class="lia-code-sample language-yaml"><code>_schema-version: '3.0.0'
ID: com.sl.sampleApplication
description: Sample Application
version: 1.0.0
modules:
- name: SampleSAPBTPApplication
type: java
path: SampleSAPBTPApplication
parameters:
stack: cflinuxfs4
memory: 2048M
instances: 1
buildpacks:
- java_buildpack
build-parameters:
builder: custom
timeout: 10m
build-result: target/SampleSAPBTPApplication-*.jar
commands:
- mvn clean install -Dcheckstyle.skip -DskipTests=true -Pdev
requires:
- name: BTPDestinationResource
# Binding services
resources:
- name: BTPDestinationResource
type: org.cloudfoundry.managed-service
parameters:
service-name: MyDestinationInstance
service-plan: lite
service: destination</code></pre><P> </P><OL><LI>Select the project in Eclipse and update(Maven) the project, please make sure there is no compilation error.</LI></OL><P><STRONG>Build and Deployment</STRONG></P><OL><LI>Open the command prompt from the workspace(where the mta.yaml file is placed) folder.</LI></OL><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="sudhiranjan_lenka_3-1723849924151.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/153371iEE9D70672D4FCF75/image-size/medium?v=v2&px=400" role="button" title="sudhiranjan_lenka_3-1723849924151.png" alt="sudhiranjan_lenka_3-1723849924151.png" /></span></P><OL><LI>Build the project to generate the “.tar” file by using the below command in the command prompt.</LI></OL><P> </P><pre class="lia-code-sample language-bash"><code>mbt build -t SampleSAPBTPApplication/generatedTAR --mtar SampleApplication.tar</code></pre><P> </P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="sudhiranjan_lenka_14-1723850889798.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/153382iF4367952BF1FBC95/image-size/medium?v=v2&px=400" role="button" title="sudhiranjan_lenka_14-1723850889798.png" alt="sudhiranjan_lenka_14-1723850889798.png" /></span></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="sudhiranjan_lenka_15-1723850889809.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/153383i2512DA499064AB0D/image-size/medium?v=v2&px=400" role="button" title="sudhiranjan_lenka_15-1723850889809.png" alt="sudhiranjan_lenka_15-1723850889809.png" /></span></P><OL><LI>If the build is successful, then SampleApplication.tat file will be created in the generatedTAR folder.</LI></OL><P> </P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="sudhiranjan_lenka_16-1723850889814.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/153385i0024FDD45B525F61/image-size/medium?v=v2&px=400" role="button" title="sudhiranjan_lenka_16-1723850889814.png" alt="sudhiranjan_lenka_16-1723850889814.png" /></span></P><P> </P><OL><LI>Use the below commands in the command prompt to log in to the BTP cloud foundry and deploy the project. (It will prompt you to enter the username and password for BTP. it will prompt you to select org and space if there is more than one)</LI></OL><P> </P><pre class="lia-code-sample language-bash"><code>cf login -a https://api.<CF API host>.hana.ondemand.com</code></pre><pre class="lia-code-sample language-bash"><code>cf deploy SampleSAPBTPApplication/generatedTAR/SampleApplication.tar</code></pre><P> </P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="sudhiranjan_lenka_17-1723850889821.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/153384iDDAE77DDA558A756/image-size/medium?v=v2&px=400" role="button" title="sudhiranjan_lenka_17-1723850889821.png" alt="sudhiranjan_lenka_17-1723850889821.png" /></span></P><OL><LI>Once the deployment is successful, it will be shown in the applications under the space.</LI></OL><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="sudhiranjan_lenka_18-1723850889828.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/153386i996A0A5DB7EA93C7/image-size/medium?v=v2&px=400" role="button" title="sudhiranjan_lenka_18-1723850889828.png" alt="sudhiranjan_lenka_18-1723850889828.png" /></span></P><OL><LI>If everything is fine, then the status will be showing as started and the application can be accessed by using the URL shown under application routes.</LI></OL><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="sudhiranjan_lenka_19-1723850889838.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/153387iC5D3BE1A8AFC232D/image-size/medium?v=v2&px=400" role="button" title="sudhiranjan_lenka_19-1723850889838.png" alt="sudhiranjan_lenka_19-1723850889838.png" /></span></P><P><STRONG>Testing</STRONG></P><OL><LI>The application can be tested by using the swagger URL from the application as mentioned below.</LI></OL><P><A target="_self">https://<app URL-dev-samplesapbtpapplication.hana.ondemand.com>/swagger-ui.html</A> </P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="sudhiranjan_lenka_20-1723850889848.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/153388i95EB14867AFC8AD3/image-size/medium?v=v2&px=400" role="button" title="sudhiranjan_lenka_20-1723850889848.png" alt="sudhiranjan_lenka_20-1723850889848.png" /></span></P><OL><LI>Click on “/uploadFile” API to test the file upload functionality.</LI></OL><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="sudhiranjan_lenka_21-1723850889852.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/153389i4B18B07BD432EA0E/image-size/medium?v=v2&px=400" role="button" title="sudhiranjan_lenka_21-1723850889852.png" alt="sudhiranjan_lenka_21-1723850889852.png" /></span></P><OL><LI>Choose any file and click on “Execute”.</LI><LI>If everything is fine, then the file will be uploaded to DMS and can be found in DMS.</LI></OL><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="sudhiranjan_lenka_22-1723850889857.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/153390i775033C99615072B/image-size/medium?v=v2&px=400" role="button" title="sudhiranjan_lenka_22-1723850889857.png" alt="sudhiranjan_lenka_22-1723850889857.png" /></span></P><P><STRONG>Conclusion</STRONG></P><P>This blog is written based on my learning and understnading, please feel free to comment for the improvement. Same thing can be achieved by creating a CAP appllication, in the next blog I will explain how to achieve the same requirement through a CAP (Java) application.</P><P>If anybody is interested for the code, please feel free to pull it from git.</P><P><A title="MTASpringBootIntegratesDMS" href="https://github.com/sudhirlenkagit/MTASpringBootIntegratesDMS.git" target="_blank" rel="noopener nofollow noreferrer">https://github.com/sudhirlenkagit/MTASpringBootIntegratesDMS.git</A> </P><P>Thanks,</P><P>Sudhir Lenka.</P><P> </P><P> </P>2024-08-17T01:58:19.050000+02:00https://community.sap.com/t5/technology-blog-posts-by-members/sap-cloud-integration-api-collection-of-odata-clients-using-js-ts-and-sap/ba-p/13810288SAP Cloud Integration API - Collection of OData Clients using JS/TS and SAP Cloud SDK2024-09-02T15:20:00.706000+02:00GiuseppeMPhttps://community.sap.com/t5/user/viewprofilepage/user-id/1645393<H1 id="toc-hId-915098654">TL;DR</H1><P>Hey everyone, I'm a very new developer in the SAP ecosystem and I want to share some stuff.</P><P>To consume the SAP Cloud Integration APIs from JS/TS, you must install the SAP Cloud SDK and utilize the generated clients. It is necessary to properly configure tsconfig when using Typescript, which can be challenging as it may not always be compatible with your project.</P><P>(Trying to write down a entire client using axios is really painful)</P><P>Additionally, these files begin to appear in every project, leading to unnecessary boilerplate in many repositories within SAP projects.</P><P>Now you can simply run `npm install ts-sap-cloud-odata-api-v2-client` and start using the generated clients immediately without any stress.</P><P>Read more: <A href="https://sap.github.io/cloud-sdk/docs/js/getting-started" target="_blank" rel="noopener nofollow noreferrer">https://sap.github.io/cloud-sdk/docs/js/getting-started</A></P><P>Process followed: <A href="https://sap.github.io/cloud-sdk/docs/js/features/odata/generate-client" target="_blank" rel="noopener nofollow noreferrer">https://sap.github.io/cloud-sdk/docs/js/features/odata/generate-client</A></P><H2 id="toc-hId-847667868">Features</H2><P>I've just generated clients for the <A href="https://api.sap.com/package/CloudIntegrationAPI/odata" target="_blank" rel="noopener noreferrer">https://api.sap.com/package/CloudIntegrationAPI/odata</A></P><P>Before moving forward, please pay attention to this: <A href="https://github.com/GiuseppeMP/sap-cloud-integration-odata-api-v2-clients?tab=readme-ov-file#-disclaimer-" target="_blank" rel="noopener nofollow noreferrer">https://github.com/GiuseppeMP/sap-cloud-integration-odata-api-v2-clients?tab=readme-ov-file#-disclaimer-</A> <span class="lia-unicode-emoji" title=":folded_hands:">🙏</span></P><H2 id="toc-hId-651154363">Install</H2><P> </P><P> </P><P> </P><P> </P><P> </P><pre class="lia-code-sample language-bash"><code>npm i ts-sap-cloud-odata-api-v2-client</code></pre><P> </P><P> </P><P> </P><P> </P><P> </P><P><BR /><BR /></P><H2 id="toc-hId-454640858">Dependencies</H2><P> </P><P> </P><P> </P><P> </P><P> </P><P> </P><pre class="lia-code-sample language-bash"><code>npm i -cloud-sdk/connectivity #required
npm i -cloud-sdk/odata-v2 #optional</code></pre><P> </P><P> </P><P> </P><P> </P><P> </P><P><BR /><BR /></P><H2 id="toc-hId-258127353">Usage</H2><P>Now you can simply use the generated clients in your code. The entire interface was kept from the SDK, and you can refer to the <A href="https://sap.github.io/cloud-sdk/docs/js/getting-started" target="_blank" rel="noopener nofollow noreferrer">https://sap.github.io/cloud-sdk/docs/js/getting-started</A> to learn more.</P><P><A href="https://sap.github.io/cloud-sdk/docs/js/features/odata/execute-request" target="_blank" rel="noopener nofollow noreferrer">https://sap.github.io/cloud-sdk/docs/js/features/odata/execute-request</A></P><P><BR />Each client is isolated in a module that can be imported using `'ts-sap-cloud-odata-api-v2-client/{APINAME}'`.<BR />This is necessary to avoid conflicts with other modules as they are all generated separately.</P><P><STRONG>Example that how I'm using</STRONG></P><P> </P><P> </P><P> </P><P> </P><P> </P><pre class="lia-code-sample language-javascript"><code>import {messageProcessingLogs, MessageProcessingLogsType } from 'ts-sap-cloud-odata-api-v2-client/MessageProcessingLogs'
import { registerDestination } from '@sap-cloud-sdk/connectivity'
async function example(name: string, url: string) {
await registerDestination(
{
name: name, // choose name
url: url // add url to your tenant
},
);
const basePath = "/api/v1" // change if needed
const requestBuilder = messageProcessingLogs<MessageProcessingLogsType>().messageProcessingLogsApi.requestBuilder().getAll()
const getAll = requestBuilder.setBasePath(basePath).addCustomHeaders({ 'authorization': `Bearer ...` })
return getAll.execute({ destinationName: name })
}</code></pre><P> </P><P> </P><P> </P><P> </P><P> </P><H3 id="toc-hId-190696567">Show your support</H3><P>Give a <span class="lia-unicode-emoji" title=":star:">⭐</span>️ if this project helped you!</P><P>If you would like to ask about features, other API clients, report bugs, or seek help, please feel free to create an issue in the GitHub repository.</P><P><A href="https://github.com/GiuseppeMP/sap-cloud-integration-odata-api-v2-clients/issues/new" target="_blank" rel="noopener nofollow noreferrer">https://github.com/GiuseppeMP/sap-cloud-integration-odata-api-v2-clients/issues/new</A><BR /><BR />It's a very new repository, I really appreciate feedback and collaborations folks!<BR /><BR /><BR />See you!</P>2024-09-02T15:20:00.706000+02:00https://community.sap.com/t5/technology-blog-posts-by-members/build-a-java-spring-boot-middleware-application-to-consume-sap-odata/ba-p/13858588Build a Java Spring boot middleware application to consume SAP OData service with SAP Cloud SDK2024-09-08T01:45:30.799000+02:00Sudhir_Lenkahttps://community.sap.com/t5/user/viewprofilepage/user-id/208695<P><STRONG>Introduction</STRONG></P><P>We had a requirement to consume a SAP OData service in an existing spring boot application running on SAP BTP Cloud Foundry. There are many ways to consume an OData service in a Java application, but we found an easier way to consume the OData service by using SAP Cloud SDK. There are many business use cases where we need to develop an application to run on SAP BTP or any cloud and consume OData service. In this blog, I will share the steps to consume an OData service in a maven-based Java Spring boot application with the help of SAP Cloud SDK.</P><P><STRONG>Understanding SAP Cloud SDK</STRONG></P><P>SAP provides a software development kit (SDK) called the SAP Cloud SDK to make developers' jobs easier. The SDK contains a set of libraries that simplify interacting with APIs at a higher level by hiding technical communication details.</P><P>The SAP Cloud SDK was developed to reduce the effort of building extension applications for SAP S/4HANA Cloud. It provides Java and JavaScript libraries, as well as a set of tools for developers, such as fault tolerance, cache management, tutorials, and project templates.</P><P>The SAP Cloud SDK enables partners, customers to easily consume OData services from SAP S/4HANA Cloud, discover existing OData services, and use built-in developer tools such as cache management, API metering, latency, and fault tolerance.</P><P>SAP Cloud SDK also allows you to generate the Virtual Data Model (VDM) of custom OData Services as well as standard SAP OData Services. This VDM can then be readily consumed by your application</P><P>SAP CAP Model uses SAP Cloud SDK behind the scenes to perform a variety of tasks.</P><P><STRONG>Business use cases</STRONG></P><UL><LI><STRONG>Scenario 1</STRONG>:- If you are planning to build your application using MongoDB, then you will not be able to use the CAP Model (since it only supports HANA for production), so you can create an application(MTA) in Java Spring boot and make use of SAP Cloud SDK for connecting to remote systems, consuming APIs in a type-safe manner, securing your application and providing multi-tenant support, etc.</LI><LI><STRONG>Scenario 2</STRONG>: - If you already have an existing Java application that was built without using the SAP CAP Model and you want to add more features like consuming an OData service.</LI><LI><STRONG>Scenario 3</STRONG>: - If you need advanced features in your app (currently, not provided by CAP Model) - for example, Resilience. </LI><LI><STRONG>Scenario 4</STRONG>: - You Are Extending an SAP Product or Service, building a Middle-Ware, Publishing a Cloud App</LI></UL><P><STRONG>Steps at a high level</STRONG></P><UL><LI>Create a maven-based Java Spring boot application/project.</LI><LI>Download the metadata of the service and save it as “serviceName.edmx”.</LI><LI>Put the EDMX file in the resource folder of the application/project.</LI><LI>(optional) Create a service naming properties file in the resource folder.</LI><LI>Add the dependencies (SAP Cloud SDK OData generator and others) in the pom.xml file.</LI><LI>Add the OData generator maven plugin in the pom.xml file.</LI><LI>Compile the application to generate the type-safe Java client library from the EDMX of the service, which gives you programmatic access to all the entities, fields, and structures of the OData service</LI><LI>Create the controller class and use the generated classes to interact with the OData service.</LI><LI>Create the destination in the environment variable for local testing and in the BTP for productive use.</LI><LI>Run the application and test it locally.</LI><LI>Deploy the application to BTP Cloud Foundry as an MTA application.</LI></UL><P><STRONG>Prerequisite </STRONG></P><OL><LI>Eclipse(with Spring tool suite and Lombok plugin)</LI><LI>JDK(1.8).</LI><LI>Access to any OData service (I will be using Business Partner API from <A href="https://api.sap.com/" target="_blank" rel="noopener noreferrer">SAP Business Accelerator Hub</A>) </LI></OL><P>(In this blog I will not cover the BTP deployment, I will explain how to test the application locally. Please refer to my other <A href="https://community.sap.com/t5/technology-blogs-by-members/custom-spring-boot-application-in-sap-btp-cf-integrates-with-btp-document/ba-p/13797941" target="_blank">blog</A> for deployment to BTP Cloud Foundry)</P><P><STRONG>Steps</STRONG></P><OL><LI>Create a new spring boot project in Eclipse as mentioned below.</LI></OL><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Sudhir_Lenka_0-1725750408309.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/163589iA78BFB12928184A3/image-size/medium?v=v2&px=400" role="button" title="Sudhir_Lenka_0-1725750408309.png" alt="Sudhir_Lenka_0-1725750408309.png" /></span></P><OL><LI>The project structure in eclipse will look like below.</LI></OL><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Sudhir_Lenka_1-1725750408314.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/163588i110159FFEEB9B563/image-size/medium?v=v2&px=400" role="button" title="Sudhir_Lenka_1-1725750408314.png" alt="Sudhir_Lenka_1-1725750408314.png" /></span></P><OL><LI>Open the pom.xml file and replace the below code.</LI></OL><P> </P><P> </P><P> </P><P> </P><pre class="lia-code-sample language-markup"><code><?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.0.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<groupId>com.sl</groupId>
<artifactId>SpringBootToConsumeODataThroughCloudSDK</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>SpringBootToConsumeODataThroughCloudSDK</name>
<description>Sample Spring Boot App to consume OData through Cloud SDK</description>
<url />
<licenses>
<license />
</licenses>
<developers>
<developer />
</developers>
<scm>
<connection />
<developerConnection />
<tag />
<url />
</scm>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web-services</artifactId>
</dependency>
<dependency>
<groupId>com.sap.cloud.sdk.datamodel</groupId>
<artifactId>odata-core</artifactId>
<version>4.15.0</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>com.sap.cloud.sdk.datamodel</groupId>
<artifactId>odata-generator-maven-plugin</artifactId>
<!-- Please use the latest version here -->
<version>4.15.0</version>
<executions>
<execution>
<id>generate-consumption</id>
<phase>generate-sources</phase>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<inputDirectory>${project.basedir}/src/main/resources/edmx</inputDirectory>
<outputDirectory>${project.build.sourceDirectory}</outputDirectory>
<serviceNameMappingFile>${project.basedir}/src/main/resources/serviceNameMappings.properties</serviceNameMappingFile>
<deleteOutputDirectory>false</deleteOutputDirectory>
<packageName>com.sl.vdm</packageName>
<defaultBasePath>odata/v2/</defaultBasePath>
<compileScope>COMPILE</compileScope>
<serviceMethodsPerEntitySet>true</serviceMethodsPerEntitySet>
<overwriteFiles>true</overwriteFiles>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project></code></pre><P> </P><P> </P><P>(In this blog we will be using OData V2 and SAP Cloud SDK version 4, if you want to consume OData V4 then the dependency version needs to be changed in the pom.xml file)</P><OL><LI>Go to <A href="https://api.sap.com/products/SAPS4HANACloud/apis/ODATA" target="_blank" rel="noopener noreferrer"><STRONG>SAP Business Accelerator Hub</STRONG></A> to download the metadata of the OData service.</LI><LI>Search for “business” in the search box for business partner OData service (In this blog we will be using OData V2).</LI></OL><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Sudhir_Lenka_2-1725750408350.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/163590iF2D123A056A9A5E5/image-size/medium?v=v2&px=400" role="button" title="Sudhir_Lenka_2-1725750408350.png" alt="Sudhir_Lenka_2-1725750408350.png" /></span></P><P> </P><P>(Please login with your personal(trial) SAP user account)</P><OL><LI>Click on “Business Partner (A2X)” to navigate to business partner API.</LI><LI>Click on “API Specification” and download the <A href="https://api.sap.com/api/API_BUSINESS_PARTNER/overview" target="_blank" rel="noopener noreferrer">EDMX</A> (service metadata).</LI></OL><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Sudhir_Lenka_3-1725750408362.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/163592i76EC5B90533F4EED/image-size/medium?v=v2&px=400" role="button" title="Sudhir_Lenka_3-1725750408362.png" alt="Sudhir_Lenka_3-1725750408362.png" /></span></P><P> </P><P>(If you want to consume an OData service that is not available in the Business Accelerator Hub like On-promise ECC/S4HANA OData service then you can fetch the metadata of the service by using <STRONG>$metadata</STRONG> and save the content in a .edmx file{<EM>any name</EM>})</P><OL><LI>Create a folder called “edmx” in the resource folder of the project and copy the downloaded/created edmx file.</LI><LI>Also create a properties file “serviceNameMappings.properties” in the resource folder.</LI></OL><P> </P><P> </P><pre class="lia-code-sample language-java"><code># API BUSINESS PARTNER
API_BUSINESS_PARTNER.className = APIBusinessPartner
API_BUSINESS_PARTNER.packageName = businesspartner</code></pre><P> </P><P> </P><OL><LI> Now the folder structure in Eclipse should look like below.</LI></OL><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Sudhir_Lenka_4-1725750408365.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/163591i8C5B65D8584025F8/image-size/medium?v=v2&px=400" role="button" title="Sudhir_Lenka_4-1725750408365.png" alt="Sudhir_Lenka_4-1725750408365.png" /></span></P><OL><LI>Now compile the project by using the “<EM>maven install</EM>” command.</LI></OL><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Sudhir_Lenka_5-1725750408379.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/163593iB526834AB6EE84C4/image-size/medium?v=v2&px=400" role="button" title="Sudhir_Lenka_5-1725750408379.png" alt="Sudhir_Lenka_5-1725750408379.png" /></span></P><OL><LI>Once the project is compiled successfully it will generate the classes for the OData service which will be used in our business logic for integration with the OData service.</LI><LI>Now the folder structure should look like below in Eclipse.</LI></OL><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Sudhir_Lenka_6-1725750408383.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/163594iA4A0057296A42A7E/image-size/medium?v=v2&px=400" role="button" title="Sudhir_Lenka_6-1725750408383.png" alt="Sudhir_Lenka_6-1725750408383.png" /></span></P><P>(Sometimes it doesn’t create the correct URL in the generated <STRONG>service interface (<EM>APIBusinessPartnerService,java</EM>)</STRONG>, so please verify and replace it with the correct URL as mentioned below. You can find the correct URL when you click “Code Snippet” in the “Try Out” tab in Business Accelerator Hub)</P><P> </P><P> </P><pre class="lia-code-sample language-java"><code>String DEFAULT_SERVICE_PATH = "odata/sap/API_BUSINESS_PARTNER";</code></pre><P> </P><P> </P><OL><LI>Now create a controller class (<STRONG><EM>BusinessPartnerController.java</EM></STRONG>) to fetch business partners.</LI><LI>Replace the below code in the BusinessPartnerController class.</LI></OL><P> </P><P> </P><pre class="lia-code-sample language-java"><code>package com.sl.api;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import com.google.gson.Gson;
import com.sap.cloud.sdk.cloudplatform.connectivity.Destination;
import com.sap.cloud.sdk.cloudplatform.connectivity.DestinationAccessor;
import com.sap.cloud.sdk.cloudplatform.connectivity.HttpDestination;
import com.sl.vdm.namespaces.businesspartner.BusinessPartner;
import com.sl.vdm.services.DefaultAPIBusinessPartnerService;
@RestController
public class BusinessPartnerController {
private final Logger log = LoggerFactory.getLogger(this.getClass());
private static final String APIKEY_HEADER = "apikey";
private static final String SANDBOX_APIKEY = "---Generated API Key---";
@GetMapping("/GetBusinessPartners")
public ResponseEntity<?> getBusinessPartners() {
String DESTINATION_NAME = "MyDestination"; // in productive code, use constants file to
// retrieve destination name
HttpDestination destination =
DestinationAccessor.getDestination(DESTINATION_NAME).asHttp();
final List<BusinessPartner> businessPartners = new DefaultAPIBusinessPartnerService()
.getAllBusinessPartner().select(BusinessPartner.BUSINESS_PARTNER, BusinessPartner.LAST_NAME,
BusinessPartner.FIRST_NAME, BusinessPartner.CREATED_ON)
.filter(BusinessPartner.MALE.eq(true))
.top(200)
.withHeader(APIKEY_HEADER, SANDBOX_APIKEY)
.executeRequest(destination);
return ResponseEntity.ok( new Gson().toJson(businessPartners));
}
}</code></pre><P> </P><P> </P><OL><LI>Generate the API key from <A href="https://api.sap.com/api/API_BUSINESS_PARTNER/overview" target="_blank" rel="noopener noreferrer">Business Accelerator Hub</A> and copy it in the variable <STRONG><EM>SANDBOX_APIKEY </EM></STRONG>in BusinessPartnerController class.</LI></OL><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Sudhir_Lenka_7-1725750408415.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/163596iEF4796D7F6F96650/image-size/medium?v=v2&px=400" role="button" title="Sudhir_Lenka_7-1725750408415.png" alt="Sudhir_Lenka_7-1725750408415.png" /></span></P><P>(If the OData service is from other system like ECC or S4HANA then this step is not required, and the authentication will be set in the destination which will be automatically picked up by the SAP Cloud SDK library)</P><OL><LI> Now we need to create a destination in the environment variable to configure the OData service URL and authentication details (in this case authentication is not required).</LI><LI>Right-click on the project and select the run configuration, select the “Environment” tab in the dialogue box.</LI><LI>Click on “Add” to create an environment variable for the destination. Keep the name as “destinations” and value as mentioned below.</LI></OL><P> </P><P> </P><pre class="lia-code-sample language-json"><code>[{"name": "MyDestination", "url": "https://sandbox.api.sap.com/s4hanacloud/sap/opu/"}]</code></pre><P> </P><P> </P><P><EM><<sample destination with basic authentication>></EM></P><P> </P><P> </P><pre class="lia-code-sample language-json"><code>[{"name": "MyDestination", "url": "--OData service base URL--", "user": "---user name---", "password": "--password for the given user--" }]</code></pre><P> </P><P> </P><P>(You can add multiple destinations to connect to different systems)</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Sudhir_Lenka_8-1725750408430.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/163595iE0A1DE35A97B5BEB/image-size/medium?v=v2&px=400" role="button" title="Sudhir_Lenka_8-1725750408430.png" alt="Sudhir_Lenka_8-1725750408430.png" /></span></P><OL><LI>Click on apply and run.</LI><LI>If everything is fine, then it will be a success and show the port number for the App.</LI><LI>Now you can test it from your browser by using the application URL</LI></OL><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Sudhir_Lenka_9-1725750408440.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/163597i6C65E4B5B39E3C7D/image-size/medium?v=v2&px=400" role="button" title="Sudhir_Lenka_9-1725750408440.png" alt="Sudhir_Lenka_9-1725750408440.png" /></span></P><P>Note :- This blog is written based on my personal learning and experience, please feel free to commnet for improvement.</P><P>Happy learning!!</P><P>Best Regards,</P><P>Sudhir.</P>2024-09-08T01:45:30.799000+02:00https://community.sap.com/t5/technology-blog-posts-by-members/integrating-with-sap-datasphere-api-through-btp-destination-using-oauth/ba-p/13877833Integrating with SAP Datasphere API through BTP Destination Using OAuth SAML Bearer Assertion2024-09-30T10:39:53.533000+02:00thomasswolfshttps://community.sap.com/t5/user/viewprofilepage/user-id/13757<P>For our proof of concept, we aimed to access the SAP Datasphere API via SAP BTP, utilizing the OAuth2 SAML Bearer authentication flow. Ideally, we wanted to connect directly to this destination through a route in our Approuter. </P><P>We found this blog post helpful as a starting point: <A href="https://community.sap.com/t5/technology-blogs-by-sap/integrating-with-sap-datasphere-consumption-apis-using-saml-bearer/ba-p/13647905" target="_new"><SPAN>Integrating</SPAN><SPAN> with</SPAN><SPAN> SAP</SPAN><SPAN> Datasphere</SPAN><SPAN> Consumption</SPAN><SPAN> APIs</SPAN><SPAN> Using</SPAN><SPAN> SAML</SPAN><SPAN> Bearer</SPAN></A>. Thanks to <a href="https://community.sap.com/t5/user/viewprofilepage/user-id/513684">@gustavokath</a> </P><P>However, the solution required an additional (NodeJS) middleware. Our goal was to achieve this both with and without a Node.js middleware, leveraging the destination service and default approuter.</P><P>We encountered some issues that I would like to document. If our experience can help anyone facing similar challenges along the way, that would be wonderful! <span class="lia-unicode-emoji" title=":slightly_smiling_face:">🙂</span></P><P><STRONG>Prerequisite: </STRONG>Make sure to check out the blog I mentioned earlier, as I won’t be going over all the steps again. I’ll just point out the differences we made.</P><P><STRONG><U>With NodeJS middleware:</U></STRONG></P><P>In the scenario where we used a Node.js middleware, we opted for the SAP Cloud SDK instead of handling HTTP requests with Axios. The SAP Cloud SDK simplifies integration with SAP services, providing built-in tools for handling authentication and API interactions. You can find more details in the <A href="https://sap.github.io/cloud-sdk/docs/js/getting-started" target="_new" rel="noopener nofollow noreferrer"><SPAN>SAP</SPAN><SPAN> Cloud</SPAN><SPAN> SDK</SPAN><SPAN> documentation</SPAN></A>.</P><P>The following code does the trick:</P><P> </P><P> </P><P> </P><pre class="lia-code-sample language-javascript"><code>//Imports
const connectivity = require("@sap-cloud-sdk/connectivity");
const httpclient = require('@sap-cloud-sdk/http-client');
//Simple example method to call datasphere destination via Cloud SDK
//Assuming req is a Request object coming from ExpressJS
const callDatasphere = (req, path, params) => {
const jwt = connectivity.retrieveJwt(req);
const destination = await connectivity.getDestination({ destinationName: "datasphere_destination", jwt: jwt });
let url = `${dest.url}${path}`;
let result = await httpclient.executeHttpRequest(dest, {
url: url,
method: "get",
headers: { "Content-Type": "application/xml" },
params: params
});
return result.data;
}</code></pre><P> </P><P> </P><P> </P><P><U><STRONG>Without NodeJS middleware</STRONG></U></P><P>The above example worked perfectly for us. However, we also wanted to explore an implementation without using a Node.js middleware. In this case, only a managed or custom approuter would be utilized.</P><P>For this we added a router to the approuter xs-ap.json file:</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="thomasswolfs_1-1727279041911.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/171316i53CC75E77F099ADE/image-size/medium?v=v2&px=400" role="button" title="thomasswolfs_1-1727279041911.png" alt="thomasswolfs_1-1727279041911.png" /></span></P><P>While testing this setup, we encountered an unexpected "unauthorized" error. This was puzzling because the destination worked flawlessly when used with the Cloud SDK. To investigate further, we fetched the destination data using Postman. The response confirmed that the tokens were generated correctly, indicating that the OAuth2 SAML Bearer flow was functioning as expected.</P><P>While debugging the Approuter, we discovered that it was adding additional headers to the request being sent to the Datasphere API.</P><P>One of these headers was "x-forwarded-host", which contained the source domain. By adding an additional property to the destination configuration, we were able to overwrite this header successfully:</P><P>uRL.headers.x-forwarded-host: "your-datasphere-domain.com"</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="thomasswolfs_0-1727278965038.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/171312i5DF54906DDFF9ABC/image-size/medium?v=v2&px=400" role="button" title="thomasswolfs_0-1727278965038.png" alt="thomasswolfs_0-1727278965038.png" /></span></P><P> </P><P> </P><P>From this point on, we were able to make calls to Datasphere through the Approuter without needing an additional Node.js middleware. This approach also works with a managed Approuter, allowing you to achieve the integration without requiring a Cloud Foundry runtime.</P><P> </P><P>Ideally, Datasphere would offer the ability to allowlist our source domains in a way, eliminating the need for the above change in the destination configuration. If you have any ideas on how to achieve this, feel free to share! For now, we will go forward with this work around :-).</P><P><a href="https://community.sap.com/t5/c-khhcw49343/SAP+Datasphere/pd-p/73555000100800002141" class="lia-product-mention" data-product="16-1">SAP Datasphere</a> <a href="https://community.sap.com/t5/c-khhcw49343/SAP+Business+Technology+Platform/pd-p/73555000100700000172" class="lia-product-mention" data-product="1215-1">SAP Business Technology Platform</a> </P><P> </P><P> </P><P> </P><P> </P><P> </P><P> </P><DIV> </DIV><P> </P><P> </P><P> </P><P> </P><P> </P>2024-09-30T10:39:53.533000+02:00https://community.sap.com/t5/technology-blog-posts-by-sap/introducing-the-sap-cloud-sdk-for-ai-javascript-typescript/ba-p/13892856Introducing the SAP Cloud SDK for AI (JavaScript/TypeScript) 🎉2024-10-28T17:57:54.632000+01:00TomFrenkenhttps://community.sap.com/t5/user/viewprofilepage/user-id/1456478<DIV><SPAN>We are excited to announce the initial release of the </SPAN><A href="https://github.com/SAP/ai-sdk-js#readme" target="_self" rel="nofollow noopener noreferrer"><SPAN>SAP Cloud SDK for AI</SPAN></A><SPAN>.</SPAN></DIV><DIV><SPAN>Integrate generative AI capabilities into your SAP Business Technology Platform (BTP) applications and</SPAN><SPAN> leverage the <A href="https://help.sap.com/docs/sap-ai-core/sap-ai-core-service-guide/generative-ai-hub-in-sap-ai-core" target="_self" rel="noopener noreferrer">Generative AI Hub</A> in </SPAN><A href="https://help.sap.com/docs/sap-ai-core/sap-ai-core-service-guide/what-is-sap-ai-core" target="_self" rel="noopener noreferrer"><SPAN>SAP AI Core</SPAN></A>.</DIV><DIV> </DIV><DIV>If you’re developing on SAP BTP, the SAP Cloud SDK for AI is designed to make AI integration more accessible and impactful in your applications. With features that streamline deployment management, enhance content safety, and simplify model orchestration, this SDK allows you to quickly incorporate advanced AI capabilities without extensive setup. Whether you need flexible workflows, secure data handling, or seamless integration with generative models, this SDK offers powerful tools to bring AI-driven functionality to your SAP BTP solutions.</DIV><DIV> </DIV><DIV><SPAN>In this blog post, we will introduce you to the key packages and their features.</SPAN></DIV><H3 id="toc-hId-1180717513"><FONT size="5"><SPAN>AI Orchestration with @sap-ai-sdk/orchestration</SPAN></FONT></H3><DIV> </DIV><DIV><SPAN>Leverage the generative AI Hub orchestration service and configure templating, content filtering, and data masking for your applications with the <STRONG><A href="https://github.com/SAP/ai-sdk-js/tree/main/packages/orchestration#readme" target="_self" rel="nofollow noopener noreferrer">@sap-ai-sdk/orchestration</A></STRONG> </SPAN><SPAN>package.</SPAN></DIV><DIV><SPAN>With the orchestration service, you can streamline AI interactions and ensure compliance with content safety guidelines.</SPAN></DIV><UL><LI><A href="https://github.com/SAP/ai-sdk-js/blob/main/packages/orchestration/README.md#templating" target="_self" rel="nofollow noopener noreferrer"><STRONG>Templating</STRONG></A><SPAN>: Create dynamic prompts with placeholders to customize AI interactions based on user inputs.</SPAN></LI><LI><A href="https://github.com/SAP/ai-sdk-js/blob/main/packages/orchestration/README.md#content-filtering" target="_self" rel="nofollow noopener noreferrer"><STRONG>Content Filtering</STRONG></A><SPAN>: Apply filters to ensure input and output adhere to content safety guidelines.</SPAN></LI><LI><STRONG><A href="https://github.com/SAP/ai-sdk-js/blob/main/packages/orchestration/README.md#data-masking" target="_self" rel="nofollow noopener noreferrer">Data Masking</A></STRONG><SPAN>: <SPAN class="">Anonymize</SPAN> <SPAN class="">and</SPAN> <SPAN class="">pseudonymize</SPAN> <SPAN class="">sensitive</SPAN> information.</SPAN></LI><LI><SPAN><STRONG>Grounding</STRONG>: Integrate <SPAN class="">external</SPAN> <SPAN class="">data</SPAN> sources to provide contextually relevant information (planned for Q4 <SPAN class="">2024</SPAN>).</SPAN></LI></UL><DIV><H3 id="toc-hId-984204008"><FONT size="5">AI Management with @sap-ai-sdk/ai-api</FONT></H3></DIV><DIV> </DIV><DIV><SPAN>Automate processes such as creating artifacts, configurations, and deployments, executing batch inference jobs, as well as managing Docker registries and object storage for training data.</SPAN></DIV><DIV><SPAN>The <STRONG><A href="https://github.com/SAP/ai-sdk-js/tree/main/packages/ai-api#readme" target="_self" rel="nofollow noopener noreferrer">@sap-ai-sdk/ai-api</A></STRONG><STRONG> </STRONG>package provides comprehensive tools for managing scenarios and workflows in SAP AI Core.</SPAN></DIV><UL><LI><STRONG><A href="https://github.com/SAP/ai-sdk-js/tree/main/packages/ai-api#create-an-artifact" target="_self" rel="nofollow noopener noreferrer">Artifact Management</A></STRONG><SPAN>: Register and manage datasets and other model artifacts.</SPAN></LI><LI><STRONG><A href="https://github.com/SAP/ai-sdk-js/tree/main/packages/ai-api#create-a-configuration" target="_self" rel="nofollow noopener noreferrer">Configuration Management</A></STRONG><SPAN>: Set up configurations for different models and use cases.</SPAN></LI><LI><A href="https://github.com/SAP/ai-sdk-js/tree/main/packages/ai-api#create-a-deployment" target="_self" rel="nofollow noopener noreferrer"><SPAN><STRONG>Deployment Management</STRONG></SPAN></A><SPAN>: Deploy AI models and manage their lifecycle within SAP AI Core.</SPAN></LI></UL><H3 id="toc-hId-787690503"><FONT size="5"><SPAN>LangChain Integration with @sap-ai-sdk/langchai</SPAN></FONT></H3><DIV> </DIV><DIV><SPAN>The </SPAN><STRONG><A href="https://github.com/SAP/ai-sdk-js/tree/main/packages/langchain#readme" target="_self" rel="nofollow noopener noreferrer">@sap-ai-sdk/langchain</A></STRONG><SPAN> package provides LangChain-compatible clients for Azure OpenAI models deployed in SAP AI Core, enabling sophisticated AI pipelines within your SAP BTP applications.</SPAN></DIV><UL><LI><SPAN><STRONG>Seamless Integration</STRONG></SPAN><SPAN>: Built on SAP Cloud SDK for AI's foundation model clients, ensuring compatibility with SAP AI Core and LangChain's ecosystem.</SPAN></LI><LI><SPAN><STRONG>Flexible Clients</STRONG></SPAN><SPAN>: Easily initialize chat and embedding clients with support for advanced features like templating and output parsing.</SPAN></LI><LI><SPAN><STRONG>RAG Support</STRONG></SPAN><SPAN>: Implement </SPAN><A href="https://github.com/SAP/ai-sdk-js/blob/main/sample-code/src/langchain-azure-openai.ts#L65" target="_self" rel="nofollow noopener noreferrer"><SPAN>Retrieval-Augmented Generation workflows</SPAN></A><SPAN> by combining embedding capabilities with LangChain's text splitters and vector stores.</SPAN></LI></UL><H3 id="toc-hId-591176998"><FONT size="5"><SPAN>Generative AI with @</SPAN></FONT><FONT size="5"><SPAN>sap-ai-sdk/foundation-models</SPAN></FONT></H3><DIV> </DIV><DIV><SPAN>The </SPAN><A href="https://github.com/SAP/ai-sdk-js/tree/main/packages/foundation-models#readme" target="_self" rel="nofollow noopener noreferrer"><SPAN><STRONG>@sap-ai-sdk/foundation-models</STRONG></SPAN></A><SPAN> package offers streamlined access to specific generative AI models accessible via Generative AI Hub.</SPAN></DIV><DIV><SPAN>It provides a more focused interface compared to the full orchestration capabilities, concentrating on direct model interactions.<BR /><BR /></SPAN></DIV><DIV><SPAN>This package is ideal for developers who need direct access to foundation models for inference and embedding requests without the additional layers of templating, content filtering, or data masking provided by the orchestration service.</SPAN></DIV><H3 id="toc-hId-394663493"><FONT size="5">Getting Started</FONT></H3><DIV> </DIV><DIV><SPAN>You will need Node v20 or higher and an ESM (ECMAScript Modules) compatible application to use the packages. <BR /></SPAN><SPAN>To explore these packages further, check out our </SPAN><A href="https://github.com/SAP/ai-sdk-js/tree/main/sample-code#readme" target="_self" rel="nofollow noopener noreferrer"><SPAN>sample code</SPAN></A><SPAN>, which shows the usage of the various SDK packages.</SPAN></DIV><H3 id="toc-hId-198149988"><FONT size="5"><SPAN>Support and Feedback</SPAN></FONT></H3><DIV> </DIV><DIV><SPAN>We value your feedback on this initial release! </SPAN></DIV><DIV><SPAN>If you need support or want to share your thoughts and ideas, go ahead and open an issue on </SPAN><A href="https://github.com/SAP/ai-sdk-js/issues" target="_self" rel="nofollow noopener noreferrer"><SPAN>GitHub</SPAN></A>.</DIV><H3 id="toc-hId-1636483"><FONT size="5">Latest News</FONT></H3><UL><LI><FONT size="3">Visit <A href="https://www.sap.com/products/artificial-intelligence.html" target="_self" rel="noopener noreferrer">sap.com/ai</A> and explore our portfolio</FONT></LI><LI><FONT size="3">Explore the available AI capabilities on <A href="https://discovery-center.cloud.sap/serviceCatalog/sap-ai-core/?region=all" target="_self" rel="nofollow noopener noreferrer">SAP Discovery Center</A>.</FONT></LI><LI><FONT size="3">Discover the latest announcements in the <A href="https://news.sap.com/?p=228310" target="_self" rel="noopener noreferrer">SAP TechEd Press Release</A> and the <A href="https://www.sap.com/events/teched/news-guide.html"%20\t "_blank" target="_self" rel="noopener noreferrer">SAP TechEd News Guide</A>.</FONT></LI><LI><FONT size="3">Review the <A href="https://roadmaps.sap.com/board?range=FIRST-LAST&PRODUCT=73554900100800003641&PRODUCT=73555000100800003283"%20\l "Q3%202024" \t "_blank" target="_self" rel="noopener noreferrer">SAP Road Map Explorer</A> for a detailed view of upcoming product innovations.</FONT></LI><LI><FONT size="3">Join the <A href="https://pages.community.sap.com/topics/ai-core-artificial-intelligence"%20\t "_blank" target="_self" rel="noopener noreferrer">SAP Community</A> page to connect with experts and share knowledge.</FONT></LI></UL>2024-10-28T17:57:54.632000+01:00https://community.sap.com/t5/technology-blog-posts-by-sap/introducing-the-sap-cloud-sdk-for-ai-java/ba-p/13956162Introducing the SAP Cloud SDK for AI (Java) 🎉2024-12-06T18:26:11.077000+01:00newtorkhttps://community.sap.com/t5/user/viewprofilepage/user-id/1384533<DIV class=""><DIV class=""><P>We’re excited to announce the initial release of the<SPAN> </SPAN><A href="https://github.com/SAP/ai-sdk-java#readme" target="_blank" rel="noopener nofollow noreferrer">SAP Cloud SDK for AI for Java</A>! Only a few weeks ago we already<SPAN> </SPAN><A href="https://community.sap.com/t5/technology-blogs-by-sap/introducing-the-sap-cloud-sdk-for-ai-javascript-typescript/ba-p/13892856" target="_blank">announced the release</A><SPAN> </SPAN>of the JavaScript/TypeScript variant. Similarly, this SDK for Java enables convenient integration of generative AI capabilities within your SAP Business Technology Platform (BTP) applications and allows you to utilize the<SPAN> </SPAN><A href="https://help.sap.com/docs/sap-ai-core/sap-ai-core-service-guide/generative-ai-hub-in-sap-ai-core" target="_blank" rel="noopener noreferrer">Generative AI Hub</A><SPAN> </SPAN>in<SPAN> </SPAN><A href="https://help.sap.com/docs/sap-ai-core/sap-ai-core-service-guide/what-is-sap-ai-core" target="_blank" rel="noopener noreferrer">SAP AI Core</A>.</P><P>For<SPAN> </SPAN><A href="https://help.sap.com/docs/btp?locale=en-US" target="_blank" rel="noopener noreferrer">SAP BTP</A><SPAN> </SPAN>developers, the AI SDK is crafted to simplify AI integration and enhance application impact. With features that optimize deployment, improve content safety, and facilitate model orchestration, the SDK lets you bring advanced AI functionality to your applications swiftly and with minimal setup. Whether you need adaptable workflows, secure data handling, or smooth generative model integration, the SDK equips you with robust tools to embed AI-powered features in your SAP BTP solutions.</P><P>This post introduces the main modules and their features.</P><DIV class=""><DIV class=""> </DIV></DIV><H2 id="toc-hId-1076682325">AI Core - Setup and Usage</H2></DIV><DIV class=""><PRE><<SPAN class="">dependency</SPAN>>
<<SPAN class="">groupId</SPAN>>com.sap.ai.sdk</<SPAN class="">groupId</SPAN>>
<<SPAN class="">artifactId</SPAN>>core</<SPAN class="">artifactId</SPAN>>
<<SPAN class="">version</SPAN>>1.0.0</<SPAN class="">version</SPAN>>
</<SPAN class="">dependency</SPAN>></PRE></DIV><P>Automate tasks such as creating AI Core artifacts, configurations, and deployments, executing batch inference jobs, as well as managing Docker registries and object storage for training data. The<SPAN> </SPAN><FONT face="courier new,courier">core</FONT><SPAN> </SPAN>module provides tools for workflow and scenario management within SAP AI Core.</P><UL><LI>Artifact management: register and organize datasets and model artifacts.</LI><LI>Configuration management: set up configurations for various models and use cases.</LI><LI>Deployment management: deploy AI models and manage their lifecycle within SAP AI Core.</LI></UL><P><STRONG>Example SDK code:</STRONG><SPAN> </SPAN>Create a deployment in SAP AI Core.</P><DIV class=""><PRE><SPAN class="">var</SPAN> <SPAN class="">api</SPAN> = <SPAN class="">new</SPAN> <SPAN class="">DeploymentApi</SPAN>();
<SPAN class="">var</SPAN> <SPAN class="">resourceGroupId</SPAN> = <SPAN class="">"default"</SPAN>;
<SPAN class="">var</SPAN> <SPAN class="">request</SPAN> =<SPAN class=""> AiDeploymentCreationRequest</SPAN>.<SPAN class="">create</SPAN>().<SPAN class="">configurationId</SPAN>(<SPAN class="">"12345-123-123-123-123456abcdefg"</SPAN>);
<SPAN class="">AiDeploymentCreationResponse</SPAN> <SPAN class="">deployment</SPAN> = <SPAN class="">api</SPAN>.<SPAN class="">create</SPAN>(<SPAN class="">resourceGroupId</SPAN>, <SPAN class="">request</SPAN>);
<SPAN class="">String</SPAN> <SPAN class="">id</SPAN> = <SPAN class="">deployment</SPAN>.<SPAN class="">getId</SPAN>();
<SPAN class="">AiExecutionStatus</SPAN> <SPAN class="">status</SPAN> = <SPAN class="">deployment</SPAN>.<SPAN class="">getStatus</SPAN>();</PRE><DIV class=""><SPAN>You can learn more about the SDK's capabilities for SAP AI Core</SPAN><SPAN> </SPAN><A href="https://github.com/SAP/ai-sdk-java/blob/main/docs/guides/AI_CORE_DEPLOYMENT.md" target="_blank" rel="noopener nofollow noreferrer">in the public repository guide</A><SPAN><SPAN><SPAN>.</SPAN></SPAN></SPAN><DIV class=""><DIV class=""><SPAN> </SPAN></DIV></DIV></DIV></DIV><H2 id="toc-hId-880168820">AI Core - Orchestration</H2></DIV><DIV class=""><PRE><<SPAN class="">dependency</SPAN>>
<<SPAN class="">groupId</SPAN>>com.sap.ai.sdk</<SPAN class="">groupId</SPAN>>
<<SPAN class="">artifactId</SPAN>>orchestration</<SPAN class="">artifactId</SPAN>>
<<SPAN class="">version</SPAN>>1.0.0</<SPAN class="">version</SPAN>>
</<SPAN class="">dependency</SPAN>></PRE><DIV class=""><SPAN>This</SPAN><SPAN> </SPAN><FONT face="courier new,courier"><SPAN>orchestration</SPAN></FONT><SPAN> </SPAN><SPAN>module lets you use the</SPAN><SPAN> </SPAN><A href="https://help.sap.com/docs/sap-ai-core/sap-ai-core-service-guide/orchestration" target="_blank" rel="noopener noreferrer">Generative AI Hub Orchestration Service</A><SPAN> </SPAN><SPAN>with templating, content filtering, and data masking from within your applications. The Orchestration Service streamlines AI interactions while ensuring adherence to content safety guidelines.</SPAN></DIV></DIV><UL><LI>Templating: Build dynamic prompts with placeholders to tailor AI interactions to user inputs.</LI><LI>Content Filtering: Apply filters to maintain compliance with content safety guidelines.</LI><LI>Data Masking: Anonymize and pseudonymize sensitive data.</LI><LI>Grounding: Add external data sources for contextually relevant information (planned for Q1 2025).</LI></UL><P><STRONG>Example SDK code:</STRONG><SPAN> </SPAN>Write a simple chat completion.</P><DIV class=""><PRE><SPAN class="">var</SPAN> <SPAN class="">client</SPAN> = <SPAN class="">new</SPAN> <SPAN class="">OrchestrationClient</SPAN>();
<SPAN class="">var</SPAN> <SPAN class="">config</SPAN> = <SPAN class="">new</SPAN> <SPAN class="">OrchestrationModuleConfig</SPAN>().<SPAN class="">withLlmConfig</SPAN>(<SPAN class="">OrchestrationAiModel</SPAN>.<SPAN class="">GPT_4O</SPAN>);
<SPAN class="">var</SPAN> <SPAN class="">prompt</SPAN> = <SPAN class="">new</SPAN> <SPAN class="">OrchestrationPrompt</SPAN>(<SPAN class="">"Hello world! Why is this phrase so famous?"</SPAN>);
<SPAN class="">var</SPAN> <SPAN class="">result</SPAN> = <SPAN class="">client</SPAN>.<SPAN class="">chatCompletion</SPAN>(<SPAN class="">prompt</SPAN>, <SPAN class="">config</SPAN>);
<SPAN class="">String</SPAN> <SPAN class="">messageResult</SPAN> = <SPAN class="">result</SPAN>.<SPAN class="">getContent</SPAN>();</PRE><DIV class=""><SPAN>You can learn more about the SDK's capabilities for Orchestration Service</SPAN><SPAN> </SPAN><A href="https://github.com/SAP/ai-sdk-java/blob/main/docs/guides/ORCHESTRATION_CHAT_COMPLETION.md" target="_blank" rel="noopener nofollow noreferrer">in the public repository guide</A><SPAN>.</SPAN></DIV><DIV class=""> </DIV></DIV><DIV class=""><H2 id="toc-hId-683655315">AI Core - Foundation Models</H2></DIV><DIV class=""><PRE><<SPAN class="">dependency</SPAN>>
<<SPAN class="">groupId</SPAN>>com.sap.ai.sdk.foundationmodels</<SPAN class="">groupId</SPAN>>
<<SPAN class="">artifactId</SPAN>>openai</<SPAN class="">artifactId</SPAN>>
<<SPAN class="">version</SPAN>>1.0.0</<SPAN class="">version</SPAN>>
</<SPAN class="">dependency</SPAN>></PRE><DIV class=""><SPAN>The</SPAN><SPAN> </SPAN><FONT face="courier new,courier"><SPAN>openai</SPAN></FONT><SPAN> </SPAN><SPAN>module, along with other modules in the</SPAN><SPAN> </SPAN><SPAN>com.sap.ai.sdk.foundationmodels</SPAN><SPAN> </SPAN><SPAN>group, enables streamlined access to specific generative AI models available through the Generative AI Hub. The module provides a simplified interface focused on direct model interactions, ideal for developers who require direct access to foundation models for inference and embedding requests without additional orchestration features.</SPAN></DIV></DIV><P>Currently only<SPAN> </SPAN><FONT face="courier new,courier">openai</FONT><SPAN> </SPAN>is supported. Please open a<SPAN> </SPAN><A href="https://github.com/SAP/ai-sdk-java/issues/new/choose" target="_blank" rel="noopener nofollow noreferrer">feature request</A>, if you need direct LLM access for other foundation models.</P><P><STRONG>Example SDK code:</STRONG><SPAN> </SPAN>Write a simple chat completion.</P><DIV class=""><PRE><SPAN class="">var</SPAN> <SPAN class="">result</SPAN> =
<SPAN class="">OpenAiClient</SPAN>.<SPAN class="">forModel</SPAN>(<SPAN class="">GPT_35_TURBO</SPAN>)
.<SPAN class="">withSystemPrompt</SPAN>(<SPAN class="">"You are a helpful AI"</SPAN>)
.<SPAN class="">chatCompletion</SPAN>(<SPAN class="">"Hello World! Why is this phrase so famous?"</SPAN>);
<SPAN class="">String</SPAN> <SPAN class="">resultMessage</SPAN> = <SPAN class="">result</SPAN>.<SPAN class="">getContent</SPAN>();</PRE><DIV class=""><SPAN>You can learn more about the SDK's capabilities for foundation models and OpenAI specific features</SPAN><SPAN> </SPAN><A href="https://github.com/SAP/ai-sdk-java/blob/main/docs/guides/OPENAI_CHAT_COMPLETION.md" target="_blank" rel="noopener nofollow noreferrer">in the public repository guide</A><SPAN>.</SPAN></DIV><DIV class=""> </DIV></DIV><DIV class=""><H2 id="toc-hId-487141810">Getting Started</H2></DIV><P>You will need<SPAN> </SPAN><EM>Java 17</EM><SPAN> </SPAN>or higher.<SPAN> </SPAN><EM>Spring Boot</EM><SPAN> </SPAN>or<SPAN> </SPAN><A href="https://cap.cloud.sap/docs/" target="_blank" rel="nofollow noopener noreferrer"><EM>SAP Cloud Application Programming Model (CAP)</EM></A><SPAN> </SPAN>as a framework is recommended, but not required. To explore these packages further, check out<SPAN> </SPAN><A href="https://github.com/SAP/ai-sdk-java/tree/main/sample-code/spring-app" target="_blank" rel="noopener nofollow noreferrer">our sample project</A>, which shows the usage of the various SDK packages.</P><P> </P><DIV class=""><H2 id="toc-hId-290628305">Support and Feedback</H2></DIV><P>We’d love your feedback on this first release! For support or to share your ideas, feel free to open an issue on<SPAN> </SPAN><A href="https://github.com/SAP/ai-sdk-java/issues/new/choose" target="_blank" rel="noopener nofollow noreferrer">GitHub</A>.</P><P> </P><DIV class=""><H2 id="toc-hId-94114800">Latest News</H2></DIV><UL><LI><A href="https://community.sap.com/t5/technology-blogs-by-sap/introducing-the-sap-cloud-sdk-for-ai-javascript-typescript/ba-p/13892856" target="_blank">SAP Cloud SDK for AI for JavaScript</A><SPAN> </SPAN>released in October 2024.</LI><LI>Visit<SPAN> </SPAN><A href="https://www.sap.com/products/artificial-intelligence.html" target="_blank" rel="noopener noreferrer">sap.com/ai</A><SPAN> </SPAN>and explore our portfolio</LI><LI>Explore the available AI capabilities on<SPAN> </SPAN><A href="https://discovery-center.cloud.sap/serviceCatalog/sap-ai-core/?region=all" target="_blank" rel="nofollow noopener noreferrer">SAP Discovery Center</A>.</LI><LI>Discover the latest announcements in the<SPAN> </SPAN><A href="https://news.sap.com/?p=228310" target="_blank" rel="noopener noreferrer">SAP TechEd Press Release</A><SPAN> </SPAN>and the<SPAN> </SPAN><A href="https://www.sap.com/events/teched/news-guide.html" target="_blank" rel="noopener noreferrer">SAP TechEd News Guide</A>.</LI><LI>Review the<SPAN> </SPAN><A href="https://roadmaps.sap.com/board?PRODUCT=73554900100800003641&PRODUCT=73555000100800003283&range=FIRST-LAST" target="_blank" rel="noopener noreferrer">SAP Road Map Explorer</A><SPAN> </SPAN>for a detailed view of upcoming product innovations.</LI><LI>Join the<SPAN> </SPAN><A href="https://pages.community.sap.com/topics/ai-core-artificial-intelligence" target="_blank" rel="noopener noreferrer">SAP Community</A><SPAN> </SPAN>page to connect with experts and share knowledge.</LI></UL>2024-12-06T18:26:11.077000+01:00https://community.sap.com/t5/product-lifecycle-management-blog-posts-by-members/sending-custom-emails-in-digital-manufacturing-using-sap-cloud-sdk/ba-p/13792568Sending Custom Emails in Digital Manufacturing Using SAP Cloud SDK2025-01-10T14:22:58.291000+01:00egeAksoyekhttps://community.sap.com/t5/user/viewprofilepage/user-id/879950<H4 id="toc-hId-1281168231"><STRONG>Introduction</STRONG></H4><P>In SAP DM, email notifications can be sent to one or multiple users using the Create & Update Alert Services. To do this, it is sufficient to provide our data to the AlertNotification field, apart from 'IN_APP'. With a recent update to DM, our existing mail destination was automatically brought into the Destinations section inside our BTP account.</P><P>When creating custom emails, this destination can be utilized via the Cloud SDK. The reason for leveraging the Cloud SDK for sending custom emails is that the styles and content of emails sent through alert services within DM cannot be customized. If there is a need to send information beyond the fixed email text, an alternative method outside of alert services is required.</P><H4 id="toc-hId-1084654726"><STRONG>Sending Mails with Create Alert Service</STRONG></H4><P>In standard DM developments, when using the Create Alert service, we can send the alert to the specified email by setting the ActionType value to SAP_MAIL in the AlertNotification body parameter.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="["Figure 1- Alert Notification Body Parameter"]" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/210027iAE5109EBB652B6CA/image-size/medium?v=v2&px=400" role="button" title="alertNotificationBodyParams.png" alt="["Figure 1- Alert Notification Body Parameter"]" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">["Figure 1- Alert Notification Body Parameter"]</span></span></P><P>Additionally, to utilize the mail service, it is necessary to verify that a destination of type 'Mail' is configured in the 'Destinations' section of SAP BTP.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="["Figure 2- Destination Configuration of the Mail Server"]" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/210759iE1C6456FD329AF7D/image-size/large?v=v2&px=999" role="button" title="blogeditted.png" alt="["Figure 2- Destination Configuration of the Mail Server"]" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">["Figure 2- Destination Configuration of the Mail Server"]</span></span></P><P>Once the necessary connections are established and the Create Alert service is triggered with the provided parameters, the email notification is received as shown below.<span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="["Figure 3-Example Alert Mail"]" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/210072iF739031906178607/image-size/large?v=v2&px=999" role="button" title="alertMailExample.png" alt="["Figure 3-Example Alert Mail"]" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">["Figure 3-Example Alert Mail"]</span></span></P><P>However, as can be seen, these template email notifications offer limited capabilities in terms of both content and scenario diversity. </P><P>This blog will provide examples of integrating a custom email service into DM as a third party using the mail client feature in Cloud SDK, and utilizing the existing MAIL destination for our processes.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="["Figure 4- Basic Send Mail Service"]" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/210144i8521A234EB23D1C8/image-size/large?v=v2&px=999" role="button" title="newAlertServiceCode.png" alt="["Figure 4- Basic Send Mail Service"]" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">["Figure 4- Basic Send Mail Service"]</span></span></P><P> <span class="lia-inline-image-display-wrapper lia-image-align-left" image-alt="["Figure 5- Receiced Basic Mail"]" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/210143i3E98DF78480FA135/image-size/medium?v=v2&px=400" role="button" title="incomingMailExample.png" alt="["Figure 5- Receiced Basic Mail"]" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">["Figure 5- Receiced Basic Mail"]</span></span></P><P>In addition to the simple email shown in the image, the main additional features that can be added and used are as follows: cc, replyTo, html (the message to be sent can be designed and shared in HTML format), attachments, and priority.</P><P> </P><P> </P><P> </P><P> </P><P><span class="lia-inline-image-display-wrapper lia-image-align-left" image-alt="["Figure 6- Mail Attachment Examples"]" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/210512iFECC78CFC2085478/image-size/medium?v=v2&px=400" role="button" title="attachmentObj.png" alt="["Figure 6- Mail Attachment Examples"]" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">["Figure 6- Mail Attachment Examples"]</span></span></P><P>Images can be embedded within the page, and they can also be sent as attachments. The key point here is that the name of the image used must match the provided CID correctly.</P><P> </P><P> </P><P> </P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="["Figure 7- Received Mail with Attachments and Images"]" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/210603iDCD346E46050D19E/image-size/large?v=v2&px=999" role="button" title="sapMailSample.png" alt="["Figure 7- Received Mail with Attachments and Images"]" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">["Figure 7- Received Mail with Attachments and Images"]</span></span></P><H4 id="toc-hId-888141221"><STRONG>Usage of the Mail Service as a Third Party in DM Processes</STRONG></H4><P>By triggering our service via Postman, we are now ready to integrate it as a third party within DM for future process designs. To achieve this, we navigate to 'Manage Service Registry' in DM and create a new service under 'API Services,' assigning our defined path to the URL path.</P><P>After configuring our method and defining the request and response structures, we save the service and proceed to 'Design Production Process' for testing.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-left" image-alt="["Figure 8- Service API Template of Mail Service"]" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/210614i82C71D6D716B3899/image-size/large?v=v2&px=999" role="button" title="Ekran görüntüsü 2025-01-10 143909.png" alt="["Figure 8- Service API Template of Mail Service"]" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">["Figure 8- Service API Template of Mail Service"]</span></span></P><P> </P><P> </P><P>After opening our design, we navigate to 'Services and Process' and select our mail service from the group where it was added under 'Select Services.' Once selected, we save the configuration. This setup allows us to tailor our setups and introduce variety to our developments based on customer requirements.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="["Figure 9a- Custom Service Selection in Process Designer"]" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/210625iB17948E8139F7CB6/image-size/medium?v=v2&px=400" role="button" title="Ekran görüntüsü 2025-01-10 144350.png" alt="["Figure 9a- Custom Service Selection in Process Designer"]" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">["Figure 9a- Custom Service Selection in Process Designer"]</span></span><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="["Figure 9b- Basic Process Template with Third Party Service"]" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/210626iEA75AC4CA65508E1/image-size/medium?v=v2&px=400" role="button" title="Ekran görüntüsü 2025-01-10 144622.png" alt="["Figure 9b- Basic Process Template with Third Party Service"]" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">["Figure 9b- Basic Process Template with Third Party Service"]</span></span></P><H4 id="toc-hId-691627716"><STRONG>Future Directions & Possibilities</STRONG></H4><P>"Examples of scenario customizations and the variety of responses to customer requests enabled by the added email service include:</P><UL><LI><STRONG>Automatically sending data via email</STRONG>: Instead of downloading filtered tables from report pages as Excel files, the data can be automatically sent to predetermined or input-specified email accounts after being retrieved.</LI><LI><STRONG>Sending critical information with CCs</STRONG>: Critical information can be sent to necessary groups, including CCs, to ensure the information reaches all relevant recipients.</LI><LI><STRONG>Creating internal email handling pages</STRONG>: Developing pages within DM for internal email receipt and response, facilitating better internal communication.</LI></UL><P>If you like this post or have any questions, please let us know in the comments.<BR />All questions are highly appreciated.</P><P><STRONG> Ege Aksöyek</STRONG> </P><P> </P><H4 id="toc-hId-495114211"><STRONG>Reference Documents</STRONG></H4><P><A href="https://help.sap.com/docs/sap-digital-manufacturing/operations-guide/configuring-email-notifications" target="_blank" rel="noopener noreferrer">Configure Email Notifications | SAP Help Portal</A></P>2025-01-10T14:22:58.291000+01:00https://community.sap.com/t5/application-development-and-automation-blog-posts/sap-developer-news-january-23rd-2025/ba-p/13994568SAP Developer News, January 23rd, 20252025-01-23T21:10:00.156000+01:00thomas_junghttps://community.sap.com/t5/user/viewprofilepage/user-id/139<P><div class="video-embed-center video-embed"><iframe class="embedly-embed" src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2FBO4qZLxBPG4%3Ffeature%3Doembed&display_name=YouTube&url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DBO4qZLxBPG4&image=https%3A%2F%2Fi.ytimg.com%2Fvi%2FBO4qZLxBPG4%2Fhqdefault.jpg&type=text%2Fhtml&schema=youtube" width="400" height="225" scrolling="no" title="Dev Survey, Cloud SDK for AI, CodeConnect, Wait in BPA, Integration Group | SAP Developer News" frameborder="0" allow="autoplay; fullscreen; encrypted-media; picture-in-picture;" allowfullscreen="true"></iframe></div></P><H3 id="toc-hId-1209403396">DESCRIPTION</H3><P><STRONG>Podcast: <A href="https://podcast.opensap.info/sap-developers/2025/01/23/sap-developer-news-january-23rd-2025/" target="_blank" rel="noopener nofollow noreferrer">https://podcast.opensap.info/sap-developers/2025/01/23/sap-developer-news-january-23rd-2025/</A></STRONG></P><P><STRONG>Developer Insight Survey </STRONG></P><UL><LI>Blog Post <A href="https://community.sap.com/t5/technology-blogs-by-sap/the-2025-sap-developer-survey-is-live-contribute-now/ba-p/13993753" target="_blank">https://community.sap.com/t5/technology-blogs-by-sap/the-2025-sap-developer-survey-is-live-contribute-now/ba-p/13993753</A></LI><LI>Survey: <A href="https://sapinsights.eu.qualtrics.com/jfe/form/SV_6AxTBFzfczfuoJg" target="_blank" rel="noopener nofollow noreferrer">https://sapinsights.eu.qualtrics.com/jfe/form/SV_6AxTBFzfczfuoJg</A></LI></UL><P><STRONG>New Release of the SAP Cloud SDK for AI </STRONG></P><UL><LI>SDK Release Notes <A href="https://github.com/SAP/ai-sdk-js/releases/tag/v1.6.0" target="_blank" rel="noopener nofollow noreferrer">https://github.com/SAP/ai-sdk-js/releases/tag/v1.6.0</A></LI></UL><P><STRONG>Code Connect 2025</STRONG></P><UL><LI>Conference website <A href="https://code-connect.dev/" target="_blank" rel="noopener nofollow noreferrer">https://code-connect.dev/</A></LI><LI>Storm Éowyn <A href="https://www.bbc.co.uk/weather/articles/cr46z2dv606o" target="_blank" rel="noopener nofollow noreferrer">https://www.bbc.co.uk/weather/articles/cr46z2dv606o</A></LI><LI>My post-news-item run route <A href="https://www.strava.com/activities/13430153502" target="_blank" rel="noopener nofollow noreferrer">https://www.strava.com/activities/13430153502</A></LI></UL><P><STRONG>“Wait for an API Call” Step in SAP Build Process Automation</STRONG></P><UL><LI>Daniel’s blog post on the new process step: <A href="https://community.sap.com/t5/technology-blogs-by-sap/new-quot-wait-for-api-call-quot-integrates-processes-with-external-systems/ba-p/13992848" target="_blank">https://community.sap.com/t5/technology-blogs-by-sap/new-quot-wait-for-api-call-quot-integrates-processes-with-external-systems/ba-p/13992848</A></LI></UL><P><STRONG>New SAP Community interest group - Integration</STRONG></P><UL><LI>Integration interest group: <A href="https://community.sap.com/t5/integration/gh-p/integration" target="_blank">https://community.sap.com/t5/integration/gh-p/integration</A></LI></UL><P><STRONG>New Certification Prerequisite for SAP Build Low-Code Certification</STRONG></P><UL><LI>Description: <SPAN><A href="https://www.linkedin.com/posts/sabrina-brueck_sapcertification-sapbuild-lowcodenocode-activity-7287391385630982145-4G-c" target="_blank" rel="noopener nofollow noreferrer">https://www.linkedin.com/posts/sabrina-brueck_sapcertification-sapbuild-lowcodenocode-activity-7287391385630982145-4G-c</A></SPAN></LI></UL><H3 id="toc-hId-1012889891">CHAPTER TITLES</H3><P>0:00 Intro</P><P>0:10 2025 Developer Insight Survey</P><P>1:14 Code Connect 2025</P><P>2:32 New Release of the SAP Cloud SDK for AI</P><P>3:17 “Wait for an API Call” Step in SAP Build Process Automation</P><P>4:23 New SAP Community interest group - Integration</P><P>4:47 New Certification Prerequisite for SAP Build Low-Code Certification</P><H3 id="toc-hId-816376386">Transcription</H3><P><STRONG>[Intro]</STRONG> This is the SAP Developer News for January 23rd, 2025.</P><P><STRONG>[Riley]</STRONG> The annual SAP Developer Insight Survey is live now. This is the sixth annual running of the survey for us. If you've taken it before, you'll see many familiar questions, things that we use to track trends over time. There's also a deeper exploration this year of topics like generative AI for developers and also developer enablement. In the description section below, you're going to do that. to find two links. The first is actually a link to last year's report. If you haven't looked at that yet, I think you'll find the results interesting. The second link, of course, is to this year's survey. It will take about 12 to 15 minutes of your time to complete. The survey is going to be open for about six weeks. At the end of that time, we'll go off and analyze the data and produce a report that we'll share with you. So I look forward to seeing your comments at the point that we do that. In the meantime, dive into the links, explore it, and we appreciate your feedback. It's important because it helps us shape the programs that we use within the community for 2025. Thank you.</P><P><STRONG>[DJ]</STRONG> Good morning. Storm Éowyn is on its way. It's already bitterly cold. It's starting to rain and the winds are gathering. But I've just got time before I got on my run to tell you about the grassroots annual SAP Tech Conference of the year. That is, of course, Code Connect. Happy way again this year, in the usual place, certainly unruped, in Germany, near Walldorf. In the usual time of year, it's the first full week in July, and we have two usual suspect one-day conferences, the awesome recap and the amazing UI5con, and Code Connect is joined this by a new conference HANA TechCon. So we're also planning to run a couple of CodeJams on the Monday, the first day of the Code Connect week. So let us know if you're interested in those too. For those traveling from afar, there's also an early bird ticket application process. Anyway, all the links are in the description. I'm going to be there and I hope to see you there too.</P><P><STRONG>[Thomas]</STRONG> Hey, I wanted to tell you about the new release of the SAP Cloud SDK for AI. This is a great tool for JavaScript developers to be able to build your own custom AI orchestration services and interactive applications. In this new 1.6 release, we see the inclusion of image recognition in the orchestration service, as well as screaming. capabilities in the orchestration client. So if your JavaScript developer looking to get started with SAP AI capabilities, be sure to check out this new release of the SAP Cloud SDK for AI. AI.</P><P><STRONG>[Daniel]</STRONG> SAP Build Process Automation is introducing a new type of process step. Wait for an API call. The step lets you pause a process automation. and then let an external system call an API to resume it. When you add this type of step to your process, you are creating a new trigger. But instead of this trigger creating a new process instance, it merely resumes an existing instance that had already been paused by the step. The idea is to allow process automation to integrate with external systems and let those systems take over part of the process and then signal back to the process instance that it has finished. It seems like it'll open up a world of possible integration scenarios with process automation. I've written a blog on how to use it, step by step, and I will leave a link to the blog post in the description.</P><P><STRONG>[Antonio]</STRONG> Holla, SAP developers. Did you know that there is a new interest group in SAP community which focuses completely on the integration topic? This is our place to share and connect with a collaborative community of integration developers. Now, what are you waiting for? Go and join a new group. I'm including the link in the description.</P><P><STRONG>[Thomas]</STRONG> I'd like to tell you about a new edition into the certification experience, particularly for SAP build low code no code certification. SAP is adding a new hands-on practical portion of the certification experience. This is a free learning course that comes with system access to allow you to execute exercises to get hands-on practical experience. This is part of a new effort by SAP to bring more practical capabilities and testing as part of the certification experience. So check out the links in the show notes for the addition to the certification, the particular certification that's involved, as well as the free experience learning journey that you can take to get ready for that certification.</P><P> </P>2025-01-23T21:10:00.156000+01:00https://community.sap.com/t5/technology-blog-posts-by-members/sap-btp-accessing-feature-flag-using-clientcredentials-destination-part-1/ba-p/14008014SAP BTP Accessing Feature Flag using ClientCredentials Destination. Part - 12025-02-05T13:14:44.184000+01:00DebashishDashttps://community.sap.com/t5/user/viewprofilepage/user-id/121928<P><STRONG>Introduction to SAP Feature Flags</STRONG></P><P>The SAP Feature Flags service is designed to help developers manage feature toggles efficiently. By using feature flags, you can control the availability of new features in your application dynamically. This blog post will walk you through a simple application that accesses a feature flag from the Feature Flag dashboard and uses its return value to control application behavior based on different requirements.</P><P>Let's not discuss more about it, rather jump into the action. You can find the more details in sap documentation - <A href="https://discovery-center.cloud.sap/serviceCatalog/feature-flags-service?region=all" target="_blank" rel="noopener nofollow noreferrer">link</A></P><P><STRONG>Pre-Requisites</STRONG></P><P>Before diving into the implementation, ensure you have the following:</P><UL><LI>SAP BTP Feature Flag subscription</LI><LI>Feature Flag Instance</LI><LI>Flag created in the Feature Flag Dashboard</LI><LI>Destination instance</LI></UL><P>For this demo, we have set up the SAP BTP Feature Flag service, created an instance of the feature flag service, and a destination named featureflagdest.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="DebashishDas_1-1738757091797.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/222231i3BBF726955FD079B/image-size/large?v=v2&px=999" role="button" title="DebashishDas_1-1738757091797.png" alt="DebashishDas_1-1738757091797.png" /></span></P><P><STRONG>Setting Up the Environment</STRONG></P><P><STRONG>Creating the Certificate</STRONG></P><P>First, create the necessary certificates for OAuth2ClientCredentials authentication.</P><P>Use the following commands in the BTP command prompt:</P><P> </P><P> </P><P> </P><pre class="lia-code-sample language-bash"><code>cf service-key feature-flag flag-key | sed '/Getting key/d' | jq --raw-output .credentials.x509.certificate > mtls-certificate.pem
cf service-key feature-flag flag-key | sed '/Getting key/d' | jq --raw-output .credentials.x509.key > mtls-private-key.pem</code></pre><P> </P><P> </P><P> </P><P>Both of these commands will extract certificate details and store in .pem files.</P><P>Combine the contents of mtls-certificate.pem and mtls-private-key.pem into a single certificate.pem file and upload it to the certificates section inside the destination service featureflagdest.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="DebashishDas_2-1738757091801.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/222233i9491B8B3EEE65C24/image-size/large?v=v2&px=999" role="button" title="DebashishDas_2-1738757091801.png" alt="DebashishDas_2-1738757091801.png" /></span></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="DebashishDas_3-1738757091802.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/222232iF51753D68DC12756/image-size/large?v=v2&px=999" role="button" title="DebashishDas_3-1738757091802.png" alt="DebashishDas_3-1738757091802.png" /></span></P><P><STRONG>Creating the Destination</STRONG></P><P>Create a destination named FeatureDest with the following details:</P><UL><LI><STRONG>Name</STRONG>: FeatureDest</LI><LI><STRONG>Type</STRONG>: HTTP</LI><LI><STRONG>URL</STRONG>: <A href="https://feature-flags.cfapps" target="_blank" rel="noopener nofollow noreferrer">https://feature-flags.cfapps</A>.<region>.hana.ondemand.com</LI><LI><STRONG>Proxy Type</STRONG>: Internet</LI><LI><STRONG>Authentication</STRONG>: OAuth2ClientCredentials</LI><LI><STRONG>Use mTLS for token retrieval</STRONG>: Checked</LI><LI><STRONG>Client ID</STRONG>: <Service Key Credential-> x509-> client_id ></LI><LI><STRONG>Token Service Key Store Location</STRONG>: certificate.pem</LI><LI><STRONG>Token Service Key Store Password</STRONG>: Auto-generated from certificate.pem</LI><LI><STRONG>Token Service URL Type</STRONG>: Dedicated</LI><LI><STRONG>Token Service URL</STRONG>: <A href="https://trial-" target="_blank" rel="noopener nofollow noreferrer">https://trial-</A><subdomain>.authentication.cert.<region>.hana.ondemand.com/oauth/token</LI></UL><P>Most of the inputs we will get from the flag-key credentials –</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="DebashishDas_4-1738757091816.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/222234iE8C7EC1C009E0452/image-size/large?v=v2&px=999" role="button" title="DebashishDas_4-1738757091816.png" alt="DebashishDas_4-1738757091816.png" /></span></P><P> </P><P>We are done creating our destination. We could use Basic Authentication method but thought to use certificate-based ClientCredentials authentication and how to create the .pem file out of the service key.</P><P>In the next part we will develop a small application to access the Feature Flag.</P><P>--> <A href="https://community.sap.com/t5/technology-blogs-by-members/sap-btp-accessing-feature-flag-using-clientcredentials-destination-part-2/ba-p/14008046" target="_self">SAP BTP Accessing Feature Flag using ClientCredentials Destination. Part - 2</A> </P>2025-02-05T13:14:44.184000+01:00https://community.sap.com/t5/technology-blog-posts-by-members/sap-btp-accessing-feature-flag-using-clientcredentials-destination-part-2/ba-p/14008046SAP BTP Accessing Feature Flag using ClientCredentials Destination. Part - 22025-02-05T13:39:46.438000+01:00DebashishDashttps://community.sap.com/t5/user/viewprofilepage/user-id/121928<P>In Previous <A href="https://community.sap.com/t5/technology-blogs-by-members/sap-btp-accessing-feature-flag-using-clientcredentials-destination-part-1/ba-p/14008014" target="_self">Part-1</A> we configured the destination through which we will access feature flags. Now we will develop an application. We could use a Simple NodeJs application to access BTP instance, but again thought of developing using sap cloud sdk as described by SAP.</P><P><FONT size="2"><EM>How to develop a simple nodeJS application, you can follow one of my existing blog posts or any other out there in internet.</EM></FONT></P><P><FONT size="2"><A href="https://community.sap.com/t5/technology-blogs-by-members/run-nodejs-in-sap-btp-and-locally-part-1/ba-p/13552757" target="_blank"><EM>Run NodeJs in SAP BTP and Locally, Part - 1</EM></A></FONT></P><P><FONT size="2">(** Note: The blog was written earlier, you may have to adjust the npm packages and little modifications in the code.)</FONT></P><P><STRONG> </STRONG><STRONG>Let’s Start with some command to install packages –</STRONG></P><P><STRONG>Setting Up the Project</STRONG></P><P><STRONG>1. Install NestJS</STRONG>: (documentation on NestJS - <A href="https://docs.nestjs.com/" target="_blank" rel="noopener nofollow noreferrer">link</A> )</P><P> </P><pre class="lia-code-sample language-bash"><code>npm i -g @nestjs/cli</code></pre><P> </P><P><STRONG>2</STRONG>. <STRONG>Scaffold an Application</STRONG>:</P><P> </P><pre class="lia-code-sample language-bash"><code>nest new featureflagsapcloudsdk
cd featureflagsapcloudsdk</code></pre><P> </P><P>3. <STRONG>Install OpenAPI Generator</STRONG>:</P><P> </P><pre class="lia-code-sample language-bash"><code>npm install -D -cloud-sdk/openapi-generator</code></pre><P> </P><P><STRONG>4.</STRONG> <STRONG>Download and Set Up FeatureFlagsAPI.yaml</STRONG>:</P><OL><UL><LI>Download from SAP Business Accelerator Hub.</LI><LI>Create folders resources/service-specs and upload FeatureFlagsAPI.yaml. <A href="https://api.sap.com/api/FeatureFlagsAPI/overview" target="_blank" rel="noopener noreferrer">https://api.sap.com/api/FeatureFlagsAPI/overview</A></LI></UL></OL><P> <span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="DebashishDas_1-1738757949656.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/222238i6B6583189AF1254E/image-size/large?v=v2&px=999" role="button" title="DebashishDas_1-1738757949656.png" alt="DebashishDas_1-1738757949656.png" /></span></P><P><STRONG>5. Create a file options-per-service.json and store in the same folder ‘resources/service-specs’.</STRONG></P><P> </P><pre class="lia-code-sample language-json"><code>{
"resources/service-specs/FeatureFlagsAPI.yaml": {}
}</code></pre><P> </P><P><STRONG>6. Install SAP Cloud SDK OpenAPI Package</STRONG>:</P><P> </P><pre class="lia-code-sample language-bash"><code>npm i -cloud-sdk/openapi</code></pre><P> </P><P>7. <STRONG>Generate Typed Client</STRONG>:</P><P> </P><pre class="lia-code-sample language-bash"><code>npx openapi-generator --input resources/service-specs --outputDir src/generated</code></pre><P> </P><P>This will generate the below like folders and files –</P><P> <span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="DebashishDas_2-1738757949657.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/222239i4DFDF9FDCC6AAC5F/image-size/large?v=v2&px=999" role="button" title="DebashishDas_2-1738757949657.png" alt="DebashishDas_2-1738757949657.png" /></span></P><P>All the calls or routing will be navigating from app.controller.ts file. These files will be generated by-default.</P><P> <span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="DebashishDas_3-1738757949658.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/222237iF4FE959A048FB6D9/image-size/large?v=v2&px=999" role="button" title="DebashishDas_3-1738757949658.png" alt="DebashishDas_3-1738757949658.png" /></span></P><P><STRONG>8. Implementing the Application</STRONG></P><P>Modify app.controller.ts and app.service.ts to include methods for accessing the feature flag.</P><P>These applications are based on typescript which generated by sap cloud sdk, but for the demo purposes we are avoiding the types. Mostly we are providing <any> as assigning and return type.</P><P><STRONG>app.controller.ts</STRONG>:</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="DebashishDas_4-1738757949659.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/222241i89204DBE5452C642/image-size/large?v=v2&px=999" role="button" title="DebashishDas_4-1738757949659.png" alt="DebashishDas_4-1738757949659.png" /></span></P><P><STRONG>app.service.ts</STRONG>:</P><P>Add “import { EvaluateV2Api } from './generated/FeatureFlagsAPI';”</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="DebashishDas_5-1738757949660.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/222242i0C7CF2D8033FFE10/image-size/large?v=v2&px=999" role="button" title="DebashishDas_5-1738757949660.png" alt="DebashishDas_5-1738757949660.png" /></span></P><P><STRONG>9. Deploying the Application</STRONG></P><P>Now let’s create a manifest file to deploy and bind with other resources –</P><P>Destination Featureflagdest already</P><P><FONT size="2">** I am not very much worried about the authentication here now. So only using the Destination service.</FONT></P><P>manifest.yaml will look like below –</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="DebashishDas_6-1738757949661.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/222240iA4FA1F1DF6AC5D8D/image-size/large?v=v2&px=999" role="button" title="DebashishDas_6-1738757949661.png" alt="DebashishDas_6-1738757949661.png" /></span></P><P>Before deploying use the below Command, which is must each time you make any changes to any files.</P><P> </P><pre class="lia-code-sample language-bash"><code>nest build</code></pre><P> </P><P>Now deploy manifest file to BTP Cloud foundry using Command</P><P> </P><pre class="lia-code-sample language-bash"><code>cf push</code></pre><P> </P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="DebashishDas_7-1738757949662.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/222245i849D8C098C12A793/image-size/large?v=v2&px=999" role="button" title="DebashishDas_7-1738757949662.png" alt="DebashishDas_7-1738757949662.png" /></span></P><P><STRONG>10. Run the Application</STRONG></P><P>In BTP, Application is in running state and started.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="DebashishDas_8-1738757949664.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/222246i7EB766E241F92ED9/image-size/large?v=v2&px=999" role="button" title="DebashishDas_8-1738757949664.png" alt="DebashishDas_8-1738757949664.png" /></span></P><P>Execute the module with the path /feature-flag</P><P>Here is the output when flag is set to true</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="DebashishDas_9-1738757949665.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/222243i6D68A4AC414C1468/image-size/large?v=v2&px=999" role="button" title="DebashishDas_9-1738757949665.png" alt="DebashishDas_9-1738757949665.png" /></span></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="DebashishDas_10-1738757949666.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/222248i18FE098649903404/image-size/large?v=v2&px=999" role="button" title="DebashishDas_10-1738757949666.png" alt="DebashishDas_10-1738757949666.png" /></span></P><P>When the flag is false</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="DebashishDas_11-1738757949666.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/222249i98B75D141020B941/image-size/large?v=v2&px=999" role="button" title="DebashishDas_11-1738757949666.png" alt="DebashishDas_11-1738757949666.png" /></span></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="DebashishDas_12-1738757949667.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/222247i3954091E7101F90F/image-size/large?v=v2&px=999" role="button" title="DebashishDas_12-1738757949667.png" alt="DebashishDas_12-1738757949667.png" /></span></P><P><U><STRONG>Code Snippet –</STRONG></U></P><P>app.controller.ts</P><P> </P><pre class="lia-code-sample language-javascript"><code>import { Controller, Get, HttpException } from '@nestjs/common';
import { AppService } from './app.service';
@Controller()
export class AppController {
constructor(private readonly appService: AppService) { }
@Get('/feature-flag')
getFeatureFlag(): any {
return this.appService.getFlag()
.then(response => {
return response;
})
.catch(error => {
throw new HttpException(
`Failed to get Feature Flags - ${error.message}`,
500
)
})
}
}</code></pre><P> </P><P>app.service.ts</P><P> </P><pre class="lia-code-sample language-javascript"><code>import { Injectable, Get ,HttpException } from '@nestjs/common';
import { EvaluateV2Api } from './generated/FeatureFlagsAPI';
@Injectable()
export class AppService {
async getFlag(): Promise<any> {
return EvaluateV2Api
.getV2EvaluateById('<feature_flag_name>', { identifier: '<tenant_id>'})
.setBasePath('https://feature-flags.cfapps.<region>.hana.ondemand.com/api')
.execute({ destinationName: '<destination_name>' });
}
}</code></pre><P> </P><P>manifest.yaml</P><P> </P><pre class="lia-code-sample language-yaml"><code>applications:
- name: featureflagsdk
path: .
buildpacks:
- nodejs_buildpack
memory: 256M
command: npm run start:prod
random-route: true
services:
- featureflagdest</code></pre><P> </P><H4 id="toc-hId-1960130043">Conclusion: </H4><P><SPAN>By following this posts, you will get the guidance of how to access the BTP Feature flags and use in your different scenarios.</SPAN></P><P><SPAN>If you have any questions or faced challenges, feel free to drop a comment below.</SPAN></P><P> </P><P><U><STRONG>References –</STRONG></U></P><OL><LI><A href="https://help.sap.com/docs/feature-flags-service/sap-feature-flags-service/what-is-sap-feature-flags-service" target="_blank" rel="noopener noreferrer">https://help.sap.com/docs/feature-flags-service/sap-feature-flags-service/what-is-sap-feature-flags-service</A></LI><LI><A href="https://api.sap.com/api/FeatureFlagsAPI/overview" target="_blank" rel="noopener noreferrer">https://api.sap.com/api/FeatureFlagsAPI/overview</A></LI><LI><A href="https://sap.github.io/cloud-sdk/" target="_blank" rel="noopener nofollow noreferrer">https://sap.github.io/cloud-sdk/</A></LI></OL>2025-02-05T13:39:46.438000+01:00https://community.sap.com/t5/technology-blog-posts-by-members/sap-btp-cloud-application-programming-model/ba-p/14032929SAP BTP: Cloud Application Programming Model2025-03-04T07:09:41.141000+01:00SekhuteTKhttps://community.sap.com/t5/user/viewprofilepage/user-id/15314<P>SAP Cloud Application Programming (CAP) Model exposes the ability to extend standard Cloud, on-premises and hybrid application functionality with flexible custom business logic scripted using the developer’s choice of programing language (SQL, SQL Script, Python, Node.js and JavaScript) and exposing the functionality to various user interfaces (UIs) e.g. Microsoft Power Bi, SAP Fiori, Dataiku and Google Big Query using various integration frameworks e.g. OData v4,JDBC/ODBC depending on the case study.</P><P>The developer takes pride in delivering a scalable extended functionality, embedding robust real-time monitoring capabilities to ensure a health life cycle and protecting the functionality with secure authentication protocols e.g. OAuth 2.0, JWT Bearer, SAML version 2.0 and RFC Specification.</P><P>Furthermore, the developer ensures a reliable and robust solutions by implementing continuous integration and continuous delivery CI/CD pipelines that automates testing, building and deployment of code changes this strategy ensures a speedy development and delivery cycle.</P><P>Traditionally developers extended business logic directly on-premises on the ABAP application server. With the ever-changing world of technology, digital transformation has become essential for organization to say afloat of competitors and therefore the programming model paradigms and capabilities of extending SAP solutions have evolved drastically and provides a seamless experience into the cloud.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Programming model.png" style="width: 626px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/232631i560CE0B01EB4E6CB/image-size/large?v=v2&px=999" role="button" title="Programming model.png" alt="Programming model.png" /></span></P><P class="lia-align-center" style="text-align: center;"><EM>Figure 1: </EM></P><P class="lia-align-center" style="text-align: center;"><EM>Architectural Paradigm Shift from Classic ABAP App server to CAP App server</EM></P><P class="lia-align-center" style="text-align: center;"><EM>Source: Thomas Jung, <A href="https://community.sap.com/t5/technology-blogs-by-sap/sap-hana-extended-application-services/ba-p/12963426" target="_blank"><STRONG>SAP HANA Extended Application Services</STRONG></A> blog, URL: <A href="https://community.sap.com/t5/technology-blogs-by-sap/sap-hana-extended-application-services/ba-p/12963426/page/4" target="_blank">https://community.sap.com/t5/technology-blogs-by-sap/sap-hana-extended-application-services/ba-p/12963426/page/4</A></EM></P><P data-unlink="true">The SAP Cloud Application model comes with a rich set of languages, libraries, and tools for building enterprise-grade services and applications. It guides developers along a 'golden path' of proven best practices and a great wealth of out-of-the-box solutions to recurring tasks:</P><P data-unlink="true"><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="cap.png" style="width: 626px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/232633i1EC9379A4BB0DAA1/image-size/large?v=v2&px=999" role="button" title="cap.png" alt="cap.png" /></span></P><P class="lia-align-center" style="text-align: center;"><EM>Figure 2: </EM></P><P class="lia-align-center" style="text-align: center;"><EM>SAP Cloud Application Model Architecture</EM></P><P class="lia-align-center" style="text-align: center;"><EM>Source <A href="HTTPS://Cap.cloud.sap" target="_blank" rel="nofollow noopener noreferrer">HTTPS://Cap.cloud.sap</A> , U</EM><EM>RL:</EM> <EM><A href="https://cap.cloud.sap/docs/about/" target="_blank" rel="nofollow noopener noreferrer">https://cap.cloud.sap/docs/about/</A></EM></P><P>The below diagram categories the skill set of SAP Cloud Developer based on the scope of the development either being ABAP or Non-ABAP. Each with its unique set of tools and methodologies:</P><P><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="ABAP VS NON.png" style="width: 626px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/232635i61C63058B7D72485/image-size/large?v=v2&px=999" role="button" title="ABAP VS NON.png" alt="ABAP VS NON.png" /></span></P><P class="lia-align-center" style="text-align: center;"><EM>Figure 3:</EM></P><P class="lia-align-center" style="text-align: center;"><EM> SAP Cloud Developer skill set</EM></P><P class="lia-align-center" style="text-align: center;"><EM>Source: Building side-by-side extensions on SAP BTP, URL: <A href="https://learning.sap.com/learning-journeys/build-side-by-side-extensions-on-sap-btp/identifying-the-need-for-side-by-side-extensibility_f1e838f0-f02a-43b4-8896-cedc25a7d5d0" target="_blank" rel="noopener noreferrer">https://learning.sap.com/learning-journeys/build-side-by-side-extensions-on-sap-btp/identifying-the-need-for-side-by-side-extensibility_f1e838f0-f02a-43b4-8896-cedc25a7d5d0</A></EM></P><P>The Cloud Application Programming model enables a collaboration of agile cross functional teams inclusive of but not limited, depending on the scope of the development: citizen developers (low-code/no-code), Professional developers (Scripting, OOP), business analysts, data engineers and application specialists to model robust data models:</P><P><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Cross functional.png" style="width: 626px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/232636i237D8D5BBD7CB069/image-size/large?v=v2&px=999" role="button" title="Cross functional.png" alt="Cross functional.png" /></span></P><P class="lia-align-center" style="text-align: center;"><EM>Figure 4:</EM></P><P class="lia-align-center" style="text-align: center;"><EM>Agile cross functional team</EM></P><P class="lia-align-center" style="text-align: center;"><EM>Source: <STRONG>Investigating the Impact of Cross-Functional Teams on Business Success</STRONG></EM></P><P class="lia-align-center" style="text-align: center;"><EM>URL:</EM> <EM><A href="https://www.iienstitu.com/en/blog/cross-functional-teams" target="_blank" rel="nofollow noopener noreferrer">https://www.iienstitu.com/en/blog/cross-functional-teams</A></EM></P><P>SAP Cloud Application Programming (CAP) Model enables a culture where people regardless of their title or background, work together to imagine, develop, deploy, and operate a solution. This strengthens the trust between employees and authentic healthy work relationships.</P><P>Thank you all for taking a glimpse into the above Content. Please don’t for get to like, comment and share.</P><P><STRONG>Abbreviations:</STRONG></P><P><STRONG>APP: </STRONG>Application</P><P><STRONG>CAP: </STRONG>Cloud Application Programing</P><P><STRONG>CDS</STRONG>: Core Data Services</P><P><STRONG>CI/CD:</STRONG> Continuous Integration and Continuous Delivery</P><P><STRONG>JWT</STRONG>: JSON Web token</P><P><STRONG>OData: </STRONG>Open Data Protocol</P><P><STRONG>RFC</STRONG>: Remote Function Call</P><P><STRONG>SAML</STRONG>: Security Assertion Markup Language</P><P><STRONG>SQL:</STRONG> Structured Query Language</P><P><STRONG>UIs:</STRONG> User Interfaces</P>2025-03-04T07:09:41.141000+01:00https://community.sap.com/t5/technology-blog-posts-by-members/autoloading-data-in-sap-fiori-apps-using-sap-capm-backend/ba-p/14052254Autoloading Data in SAP Fiori Apps Using SAP CAPM Backend2025-04-10T16:54:38.645000+02:00Guru_Charanhttps://community.sap.com/t5/user/viewprofilepage/user-id/1451110<P><STRONG><U>Step-1</U></STRONG> : <STRONG>Locate </STRONG>the <STRONG>Manifest.json </STRONG>in your application.</P><P>Step-2 : State the changes as shown below in the code.</P><pre class="lia-code-sample language-javascript"><code> "targets": {
"RecordList": {
"type": "Component",
"id": "RecordList",
"name": "sap.fe.templates.ListReport",
"options": {
"settings": {
"contextPath": "/Record",
"variantManagement": "Page",
"initialLoad":true, //Make the intial-load to true
"navigation": {
"Record": {
"detail": {
"route": "RecordObjectPage"
}
}
},</code></pre><P><STRONG> Step-3</STRONG> : Make the i<STRONG>ntialLoad : true, </STRONG></P><P><STRONG>contextPath: Refers to the entity set exposed by your CAP service (e.g., /Record).</STRONG></P><P><STRONG>Step-4</STRONG> : Run your your <STRONG>sap fiori-application & </STRONG>See the magic that happens.</P><P><U><STRONG>Final Verdict:</STRONG></U> </P><P>Enabling initialLoad: true is a subtle but impactful enhancement that can significantly improve user experience in SAP Fiori List Report applications. However, like any performance-related tweak, it should be applied thoughtfully based on the app’s business purpose, dataset size, and user expectations.</P><P> </P><div class="lia-spoiler-container"><a class="lia-spoiler-link" href="#" rel="nofollow noopener noreferrer">Spoiler</a><noscript> (Highlight to read)</noscript><div class="lia-spoiler-border"><div class="lia-spoiler-content"><span class="lia-unicode-emoji" title=":megaphone:">📣</span>I will give only the conditions when the initial-load is not to be used & consider using in other conditions <span class="lia-unicode-emoji" title=":smiling_face_with_smiling_eyes:">😊</span></div><noscript><div class="lia-spoiler-noscript-container"><div class="lia-spoiler-noscript-content">I will give only the conditions when the initial-load is not to be used & consider using in other conditions </div></div></noscript></div></div><P> </P><H3 id="toc-hId-1835488134"><span class="lia-unicode-emoji" title=":cross_mark:">❌</span> When <EM>Not</EM> to Use initialLoad: true?</H3><P class="">1)Your app handles large datasets or complex queries without filtering.<BR />2) Real-time, always-fresh data is mandatory (e.g., live inventory counts).<BR />3) You rely heavily on dynamic filters that users apply manually before loading.<BR />4) There's a risk of backend performance bottlenecks on auto-load.<BR />5) You're building an app with heavy personalization or variant management that changes frequently.</P><P> </P>2025-04-10T16:54:38.645000+02:00https://community.sap.com/t5/technology-blog-posts-by-members/using-the-sap-cloud-javascript-sdk-for-ai/ba-p/14140582Using the SAP Cloud Javascript SDK for AI2025-07-02T14:15:32.107000+02:00aliulashayirhttps://community.sap.com/t5/user/viewprofilepage/user-id/2035254<H1 id="toc-hId-1604971718"><STRONG>Using the SAP Cloud Javascript SDK for AI </STRONG></H1><DIV><H1 id="toc-hId-1408458213">Introduction</H1><P>In this blog, We will build a Node.js Service with authenticaton that Talks to SAP Generative AI Hub. We’ll spin up a tiny Express server, protect it with XSUAA, call an OpenAI model (gpt-4o-mini) deployed in the Generative AI Hub, and push the whole thing to Cloud Foundry.</P><P>Source code is ~70 lines; everything else is service wiring.</P><HR /><H2 id="toc-hId-1341027427">Table of Contents</H2><OL><LI><A href="#h_9151997441751361469182" target="_self" rel="nofollow noopener noreferrer">Prerequisites</A></LI></OL><OL><LI><A href="#h_830909271151751361475377" target="_self" rel="nofollow noopener noreferrer">Step 1 — Destination services</A></LI></OL><OL><LI><A href="#h_839652719261751361482623" target="_self" rel="nofollow noopener noreferrer">Step 2 — Create the XSUAA instance</A></LI></OL><OL><LI><A href="#h_716093466351751361488585" target="_self" rel="nofollow noopener noreferrer">Step 3 — Scaffold the Node project</A></LI></OL><OL><LI><A href="#h_991084637431751361494195" target="_self" rel="nofollow noopener noreferrer">Step 4 — Deploy to BTP</A></LI></OL><OL><LI><A href="#h_102502829501751361501241" target="_self" rel="nofollow noopener noreferrer">Step 5 — Test with Postman</A></LI></OL><OL><LI><A href="#h_578059860561751361515951" target="_self" rel="nofollow noopener noreferrer">Useful Links</A></LI></OL><HR /><H2 id="toc-hId-1144513922">1 · Prerequisites</H2><UL><LI><STRONG>BTP sub-account</STRONG> (any region)</LI></UL><UL><LI><STRONG>Generative AI Hub</STRONG> enabled in BTP Cockpit</LI></UL><UL><LI><STRONG>Node.js ≥ 18</STRONG> & <STRONG>cf CLI v8</STRONG> - <A href="https://nodejs.org/" target="_blank" rel="noopener nofollow noreferrer">https://nodejs.org</A></LI></UL><UL><LI><STRONG>Postman</STRONG> - <A href="https://www.postman.com/" target="_blank" rel="noopener nofollow noreferrer">https://www.postman.com/</A></LI></UL><UL><LI><STRONG>VSCode</STRONG> - <A href="https://code.visualstudio.com/" target="_blank" rel="noopener nofollow noreferrer">https://code.visualstudio.com/</A></LI></UL><UL><LI><STRONG>AI Core Instance & Service Key</STRONG> created from BTP</LI></UL><HR /><H2 id="toc-hId-948000417">2 · Step 1 — Create Destination Service</H2><P>Creation of a Destination Service is needed as we will be using it to connect to our GenAI hub instance. There is a way to define custom destination programatically mentioned here.</P><P><A href="https://sap.github.io/cloud-sdk/docs/js/features/connectivity/destinations#register-destination" target="_blank" rel="noopener nofollow noreferrer">https://sap.github.io/cloud-sdk/docs/js/features/connectivity/destinations#register-destination</A></P><P>However from my testing I just couldn’t got it to connect to my genai instance. So I will be creating a destination following the documentation</P><P><A href="https://sap.github.io/ai-sdk/docs/js/connecting-to-ai-core" target="_blank" rel="noopener nofollow noreferrer">https://sap.github.io/ai-sdk/docs/js/connecting-to-ai-core</A></P><OL><LI><STRONG>Create the Destination Service</STRONG><P>Go to the Instances and Subscriptions and click the create button to the right</P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Ekran_Resmi_2025-06-30_15.15.36.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/281149i8979DCAA79E66865/image-size/large?v=v2&px=999" role="button" title="Ekran_Resmi_2025-06-30_15.15.36.png" alt="Ekran_Resmi_2025-06-30_15.15.36.png" /></span></LI></OL><DIV><A href="https://community.sap.com/Using%20the%20SAP%20Cloud%20Javascript%20SDK%20for%20AI%2021f49ed7814680169bebea93fbfb20ae/Ekran_Resmi_2025-06-30_15.15.36.png" target="_blank" rel="noopener nofollow noreferrer"><IMG src="https://community.sap.com/Using%20the%20SAP%20Cloud%20Javascript%20SDK%20for%20AI%2021f49ed7814680169bebea93fbfb20ae/Ekran_Resmi_2025-06-30_15.15.36.png" border="0" alt="" /></A><P>Select the Destination Service and name it whatever you like</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="image.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/281151i87CC394DEAA07B53/image-size/large?v=v2&px=999" role="button" title="image.png" alt="image.png" /></span></P><A href="https://community.sap.com/Using%20the%20SAP%20Cloud%20Javascript%20SDK%20for%20AI%2021f49ed7814680169bebea93fbfb20ae/image.png" target="_blank" rel="noopener nofollow noreferrer"><IMG src="https://community.sap.com/Using%20the%20SAP%20Cloud%20Javascript%20SDK%20for%20AI%2021f49ed7814680169bebea93fbfb20ae/image.png" border="0" alt="" /></A></DIV><P>Click on the newly created Destination Service Instance and create a Service Key for it</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Ekran_Resmi_2025-06-30_15.25.51.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/281152iA1A748EEB9B3CFCA/image-size/large?v=v2&px=999" role="button" title="Ekran_Resmi_2025-06-30_15.25.51.png" alt="Ekran_Resmi_2025-06-30_15.25.51.png" /></span></P><A href="https://community.sap.com/Using%20the%20SAP%20Cloud%20Javascript%20SDK%20for%20AI%2021f49ed7814680169bebea93fbfb20ae/Ekran_Resmi_2025-06-30_15.25.51.png" target="_blank" rel="noopener nofollow noreferrer"><IMG src="https://community.sap.com/Using%20the%20SAP%20Cloud%20Javascript%20SDK%20for%20AI%2021f49ed7814680169bebea93fbfb20ae/Ekran_Resmi_2025-06-30_15.25.51.png" border="0" alt="" /></A><P>And then click the 3 dots up on the right corner and go to option View Dashboard here we will be creating a destination. Create a HTTP destination and select Authentication type as “OAuth2ClientCredentials” paste-in the values you have beforehand from the AI Core Service service key.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="image 1.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/281155iA5F9BCED8B239FDC/image-size/large?v=v2&px=999" role="button" title="image 1.png" alt="image 1.png" /></span></P><H3 id="toc-hId-880569631">Important!</H3><P>Make sure to add /oauht/token to the end of your Token Service URL if there is none.</P><A href="https://community.sap.com/Using%20the%20SAP%20Cloud%20Javascript%20SDK%20for%20AI%2021f49ed7814680169bebea93fbfb20ae/image%201.png" target="_blank" rel="noopener nofollow noreferrer"><IMG src="https://community.sap.com/Using%20the%20SAP%20Cloud%20Javascript%20SDK%20for%20AI%2021f49ed7814680169bebea93fbfb20ae/image%201.png" border="0" alt="" /></A><P>Click Check Connection. It will give a message;</P><BLOCKQUOTE><span class="lia-unicode-emoji" title=":white_heavy_check_mark:">✅</span>Connection to "my-aicore" established. Response returned: "404: Not Found”.</BLOCKQUOTE><P>This is fine as we are not sending a request to the destination yet.</P><HR /><H2 id="toc-hId-554973407">3 · Step 2 — Create XSUAA Service and Service Key</H2><P>Just like the steps we did when creating our Destination Service create an Authorization and Trust Management (XSUAA) service instance and then create a service key for it.</P><P>Only thing to pay attention is to select plan “broker” when creating the service</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="image 2.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/281153i157063A01410E110/image-size/large?v=v2&px=999" role="button" title="image 2.png" alt="image 2.png" /></span></P><A href="https://community.sap.com/Using%20the%20SAP%20Cloud%20Javascript%20SDK%20for%20AI%2021f49ed7814680169bebea93fbfb20ae/image%202.png" target="_blank" rel="noopener nofollow noreferrer"><IMG src="https://community.sap.com/Using%20the%20SAP%20Cloud%20Javascript%20SDK%20for%20AI%2021f49ed7814680169bebea93fbfb20ae/image%202.png" border="0" alt="" /></A><P>Then just create the service keys for this service aswell.</P><H2 id="toc-hId-358459902">4 · Step 3 — Scaffold the Node project</H2></DIV><pre class="lia-code-sample language-bash"><code>mkdir genai-node && cd genai-node
npm init -y
npm i express -ai-sdk/foundation-models
npm i cfenv /xssec
npm i -D typescript rimraf
npm i -D @types/express @types/cfenv to the
npx tsc --init --outDir dist --rootDir src \
--target es2022 --module es2022 \
--moduleResolution node --esModuleInterop tru
mkdir src
touch .cfignore #here put the paths of the files you don't want to deploy to BTP</code></pre><BLOCKQUOTE><span class="lia-unicode-emoji" title=":warning:">⚠️</span>TypeScript typings & Express version<UL><LI><STRONG>This tutorial uses </STRONG><STRONG>express@5.1</STRONG> (bundles its own .d.ts files).<P><STRONG>Do </STRONG><EM><STRONG>not</STRONG></EM><STRONG> install </STRONG><STRONG>@types/express</STRONG><STRONG> with Express 5</STRONG> – VS Code will show<BR /><BR /><EM>“Duplicate identifier / Cannot redeclare …”</EM> errors because two sets of<BR />typings clash.</P></LI></UL><UL><LI>If you decide to downgrade to <STRONG>express@4.x</STRONG>, then you <STRONG>must</STRONG> add<P>npm i -D @types/express so the compiler can find the declarations.</P></LI></UL><P>In both cases you can safely install the helpers below:</P></BLOCKQUOTE><pre class="lia-code-sample language-bash"><code># always safe
npm i -D @types/node @types/cfenv</code></pre><P>They silence any remaining red squiggles without affecting runtime.</P><P>Complete tsconfig.json</P><pre class="lia-code-sample language-json"><code>{
"compilerOptions": {
"target": "es2022",
"module": "es2022",
"moduleResolution": "node",
"rootDir": "src",
"outDir": "dist",
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"skipLibCheck": true // turns off deep type-checking in node_modules
},
"include": ["src/**/*.ts", "*.ts"]
}</code></pre><P>Complete package.json</P><pre class="lia-code-sample language-json"><code>{
"name": "genai-node",
"version": "1.0.0",
"type": "module",
"scripts": {
"build": "rimraf dist && tsc",
"start": "npm run build && node dist/server.js"
},
"dependencies": {
"@sap-ai-sdk/ai-api": "^1.15.0",
"@sap-ai-sdk/foundation-models": "^1.15.0",
"@sap-ai-sdk/langchain": "^1.15.0",
"@sap-ai-sdk/orchestration": "^1.15.0",
"@sap-cloud-sdk/connectivity": "^4.0.2",
"@sap/xssec": "^4.8.0",
"cfenv": "^1.2.4",
"dotenv": "^16.5.0",
"express": "^5.1.0"
},
"devDependencies": {
"rimraf": "^6.0.1",
"ts-node": "^10.9.2",
"tsx": "^4.20.3",
"typescript": "^5.8.3"
}
}</code></pre><P>We compile on CF during npm start; the buildpack installs devDeps by default, so no extra steps required.</P><P>Create src/server.ts:</P><pre class="lia-code-sample language-javascript"><code>import express, { Request, Response, NextFunction } from 'express';
import { AzureOpenAiChatClient } from '@sap-ai-sdk/foundation-models';
import xssec from '@sap/xssec';
import cfenv from 'cfenv';
const app = express();
const port = process.env.PORT || 3000;
const XSUAA_SERVICE_NAME = process.env.XSUAA_SERVICE_NAME;
const appEnv = cfenv.getAppEnv();
const xsuaaService = appEnv.getService(XSUAA_SERVICE_NAME as any);
if (!xsuaaService) {
throw new Error(`XSUAA service "${XSUAA_SERVICE_NAME}" not bound!`);
}
const uaaCreds = xsuaaService.credentials;
function verifyJwt(req: Request, _res: Response, next: NextFunction) {
const auth = req.headers.authorization;
if (!auth?.startsWith('Bearer ')) {
return _res.status(403).send('Missing bearer token');
}
const token = auth.slice(7);
xssec.createSecurityContext(token, uaaCreds)
.then(() => next())
.catch((e: any) => {
console.error('JWT rejected →', e.innerError ?? e.message ?? e);
_res.status(403).send(String(e.innerError?.message ?? e.message));
});
}
const chat = new AzureOpenAiChatClient(
{
modelName: 'gpt-4o-mini' ,
resourceGroup: 'your-resource-group'
},
{
destinationName: 'my-aicore'
}
);
app.post('/chat', verifyJwt express.json(), async (req, res) => {
const userMsg = String(req.body.message ?? '');
try {
const resp = await chat.run({
messages: [{ role: 'user', content: userMsg }]
});
res.json({ answer: resp.getContent() });
} catch (e: any) {
console.error(e);
res.status(500).json({ error: e?.message ?? 'server error' });
}
});
app.listen(port, () => console.log('⇢ listening on', port));</code></pre><H2 id="toc-hId-161946397">5 · Step 4 — Deploy to BTP</H2><P>manifest.yaml</P><pre class="lia-code-sample language-yaml"><code>---
applications:
- name: genai-node
buildpack: nodejs_buildpack
command: npm start
memory: 256M
env:
XSUAA_SERVICE_NAME: xsuaa-blog #Name of your service
services:
- destination-service
- xsuaa-blog </code></pre><P>Push the codebase to the CloudFoundry and check if it is running</P><pre class="lia-code-sample language-bash"><code>cf push
cf logs genai-node --recent</code></pre><H2 id="toc-hId--34567108">5 · Step 6—Test with Postman</H2><P>After you deploy the application click on it to get the custom link to the app.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="image 3.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/281156iBA221A5F6E3CEC69/image-size/large?v=v2&px=999" role="button" title="image 3.png" alt="image 3.png" /></span><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="image 4.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/281157i71068D51A25A8C18/image-size/large?v=v2&px=999" role="button" title="image 4.png" alt="image 4.png" /></span></P><P><A href="https://community.sap.com/Using%20the%20SAP%20Cloud%20Javascript%20SDK%20for%20AI%2021f49ed7814680169bebea93fbfb20ae/image%203.png" target="_blank" rel="noopener nofollow noreferrer"><IMG src="https://community.sap.com/Using%20the%20SAP%20Cloud%20Javascript%20SDK%20for%20AI%2021f49ed7814680169bebea93fbfb20ae/image%203.png" border="0" alt="" /></A><A href="https://community.sap.com/Using%20the%20SAP%20Cloud%20Javascript%20SDK%20for%20AI%2021f49ed7814680169bebea93fbfb20ae/image%204.png" target="_blank" rel="noopener nofollow noreferrer"><IMG src="https://community.sap.com/Using%20the%20SAP%20Cloud%20Javascript%20SDK%20for%20AI%2021f49ed7814680169bebea93fbfb20ae/image%204.png" border="0" alt="" /></A></P><UL><LI>Open up Postman and write your request with your endpoint</LI></UL><UL><LI>Go to the <STRONG>Authorization</STRONG> tab → Select OAuth 2.0</LI></UL><UL><LI>Enter credentials<BR /><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="image 5.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/281154i9FD71599B30C3E13/image-size/large?v=v2&px=999" role="button" title="image 5.png" alt="image 5.png" /></span></LI></UL><P><A href="https://community.sap.com/Using%20the%20SAP%20Cloud%20Javascript%20SDK%20for%20AI%2021f49ed7814680169bebea93fbfb20ae/image%205.png" target="_blank" rel="noopener nofollow noreferrer"><IMG src="https://community.sap.com/Using%20the%20SAP%20Cloud%20Javascript%20SDK%20for%20AI%2021f49ed7814680169bebea93fbfb20ae/image%205.png" border="0" alt="" /></A></P><UL><LI>Scroll down to the “<STRONG>Get New Access Token</STRONG>” and then “<STRONG>Use Token</STRONG>”.</LI></UL><P>Now you are good to send the request and get a result</P><pre class="lia-code-sample language-json"><code>{
"answer": "Hello! How can I assist you today?"
}</code></pre><H2 id="toc-hId-116173744">7 · Useful Links</H2><UL><LI><A href="https://www.sap.com/events/teched/virtual/flow/sap/te24/catalog/page/catalog/session/1722557682293001fTqe" target="_blank" rel="noopener noreferrer">https://www.sap.com/events/teched/virtual/flow/sap/te24/catalog/page/catalog/session/1722557682293001fTqe</A></LI></UL><UL><LI><A href="https://github.com/SAP-samples/teched2024-AI180" target="_blank" rel="noopener nofollow noreferrer">https://github.com/SAP-samples/teched2024-AI180</A></LI></UL><UL><LI><A href="https://sap.github.io/ai-sdk/docs/js/connecting-to-ai-core" target="_blank" rel="noopener nofollow noreferrer">https://sap.github.io/ai-sdk/docs/js/connecting-to-ai-core</A></LI></UL><UL><LI><A href="https://sap.github.io/cloud-sdk/docs/js/tutorials/getting-started/introduction" target="_blank" rel="noopener nofollow noreferrer">https://sap.github.io/cloud-sdk/docs/js/tutorials/getting-started/introduction</A></LI></UL>2025-07-02T14:15:32.107000+02:00https://community.sap.com/t5/technology-blog-posts-by-sap/how-to-use-cloud-sdk-for-ai-in-an-existing-cap-nodejs-project-in-commonjs/ba-p/14169098How to use Cloud SDK for AI in an existing CAP NodeJS Project in CommonJS2025-08-01T10:03:01.242000+02:00Jacky_Liu1https://community.sap.com/t5/user/viewprofilepage/user-id/132085<P>I have an existing SAP Cloud Application Program Project in CommonJs. I want to add custom AI function in it . So need to use <A href="https://sap.github.io/ai-sdk/docs/js/overview-cloud-sdk-for-ai-js" target="_self" rel="nofollow noopener noreferrer">SAP Cloud SDK for AI </A>in my project. But SAP Cloud SDK for AI only support native ESM. Convert my existing project into ESM? I think it is too risky. I need to use the ESM module in the CommonJs project. At last, it works. Let me share the steps which maybe be helpful for you.</P><H4 id="toc-hId-1994330268">Steps:</H4><H5 id="toc-hId-1926899482">1, Install <SPAN>esbuild in the root path of my project:</SPAN></H5><pre class="lia-code-sample language-bash"><code>npm i esbuild</code></pre><H5 id="toc-hId-1730385977">2. Create folder named <U><EM>esm</EM></U> under <U><EM>srv</EM></U>, then create a file named <U><EM>index.js</EM></U> under folder <U><EM>esm</EM></U> with the following code .</H5><pre class="lia-code-sample language-javascript"><code>import { OrchestrationClient, buildAzureContentSafetyFilter } from '@sap-ai-sdk/orchestration';
import { AzureOpenAiEmbeddingClient } from '@sap-ai-sdk/langchain'
export { OrchestrationClient, buildAzureContentSafetyFilter, AzureOpenAiEmbeddingClient };</code></pre><H5 id="toc-hId-1533872472">3. Add following script in my package.json file in project root path.</H5><pre class="lia-code-sample language-javascript"><code> "deps": "esbuild --platform=node srv/esm/index.js --bundle --outfile=srv/esm/bundle.js"</code></pre><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Jacky_Liu1_0-1754034389278.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/295028iDF153B0500979B79/image-size/medium?v=v2&px=400" role="button" title="Jacky_Liu1_0-1754034389278.png" alt="Jacky_Liu1_0-1754034389278.png" /></span></P><H5 id="toc-hId-1337358967">4. Run the following command in the project root path in terminal.</H5><pre class="lia-code-sample language-bash"><code>npm run deps</code></pre><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Jacky_Liu1_0-1754035312369.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/295041i01BDB7AACC94EFE6/image-size/medium?v=v2&px=400" role="button" title="Jacky_Liu1_0-1754035312369.png" alt="Jacky_Liu1_0-1754035312369.png" /></span></P><H5 id="toc-hId-1140845462">5. Adjust the code of importing the module of <U><EM>SAP Cloud SDK for AI </EM></U>as the following.</H5><pre class="lia-code-sample language-javascript"><code>// const { OrchestrationClient, buildAzureContentSafetyFilter } = require('@sap-ai-sdk/orchestration');
const { OrchestrationClient, buildAzureContentSafetyFilter } = require('../esm/bundle');
// const { AzureOpenAiEmbeddingClient } = require('@sap-ai-sdk/langchain');
const { AzureOpenAiEmbeddingClient } = require('../esm/bundle');</code></pre><P> </P><P> </P><P>The ends!</P><P>Thanks for your time!</P><P>Jacky Liu</P><P> </P>2025-08-01T10:03:01.242000+02:00https://community.sap.com/t5/technology-blog-posts-by-sap/build-a-code-based-agent-using-sap-ai-core-with-next-js-and-the-vercel-ai/ba-p/14230640Build a Code-based Agent using SAP AI Core with Next.js and the Vercel AI SDK2025-09-29T16:43:22.762000+02:00florian-richterhttps://community.sap.com/t5/user/viewprofilepage/user-id/2124549<H1 id="toc-hId-1632678183">Build a Code-based Agent using SAP AI Core with Next.JS and the Vercel AI SDK</H1><H2 id="toc-hId-1565247397">TL;DR</H2><P>If you need to create a code-based agent and want to use Next.js with the Vercel AI SDK, you can connect to SAP AI Core through the LangChain compatibility layer.</P><H2 id="toc-hId-1368733892">Use Case</H2><P>I was recently looking into building an application which gives users access to a highly specialized AI agent. To make this application, I chose Next.js and created a code-based agent which will be handled by the Next.js server runtime through the Vercel AI SDK. Even though this is not the traditional way to <A href="https://help.sap.com/docs/btp/btp-developers-guide/what-is-btp-developers-guide" target="_blank" rel="noopener noreferrer">create web applications at SAP</A>, it was a very efficient way to create this application for me.</P><H2 id="toc-hId-1172220387">Why Next.js?</H2><P>Modern web applications have come a long way since the times of single-page apps (SPA) which brought dynamic and reactive applications to the web. React is the most popular web UI library, but "Create React App" is deprecated. Instead the React team <A href="https://react.dev/learn/creating-a-react-app#nextjs-app-router" target="_blank" rel="noopener nofollow noreferrer">recommends</A> the meta-framework <A href="https://nextjs.org/docs" target="_blank" rel="noopener nofollow noreferrer">Next.js</A>. Both React and Next.js dominate the popularity charts as you can see in the annual <A href="https://2024.stateofjs.com/en-US/libraries/meta-frameworks/" target="_blank" rel="noopener nofollow noreferrer">State of JS survey</A>. While popularity is not the only consideration when picking a framework, it often suggests good documentation and widespread support.</P><P>Meta-frameworks focus on rendering and serving the application in an efficient way, using static-site generation (SSG), server-side rendering (SSR) and other techniques to improve load times and responsiveness. To fully utilize these advantages, Next.js runs its own server and introduces server routes and server components, breaking down the traditional frontend-backend split.</P><P>If you are interested in learning about Next.js, feel free to check out one of the many available tutorials:</P><UL><LI><A href="https://nextjs.org/docs" target="_blank" rel="noopener nofollow noreferrer">Next.js Getting Started</A></LI><LI><A href="https://nextjs.org/learn/dashboard-app" target="_blank" rel="noopener nofollow noreferrer">Learn Next.js</A></LI><LI><A href="https://www.youtube.com/watch?v=d5x0JCZbAJs" target="_blank" rel="noopener nofollow noreferrer">From 0 to Production (long video)</A></LI></UL><H2 id="toc-hId-975706882">Why a Code-based Agent?</H2><P>SAP offers different options to create a custom agent based on your needs. Many cases are already covered by existing agents integrated with Joule or can be created without writing code in <A href="https://www.sap.com/products/artificial-intelligence/joule-studio.html" target="_blank" rel="noopener noreferrer">Joule Studio</A>. These are the recommended ways to consume AI services in a safe and compliant way, but highly specialized agents may need additional capabilities only available through code-based agents.</P><P>Code-based agents can use SAP AI Core services to provide streamlined access to a large number of frontier models through SAP Business Technology Platform. This allows you to create a bespoke agent using popular open-source tools together with SAP integrations. But beware: Creating a code-based agent requires additional development capacity, specialized skills and significant ongoing maintenance.</P><H2 id="toc-hId-779193377">Connect Vercel AI SDK with SAP Cloud SDK for AI</H2><P>The best way to connect your application to SAP AI Core is the <A href="https://sap.github.io/ai-sdk/" target="_blank" rel="noopener nofollow noreferrer">SAP Cloud SDK for AI</A> (often called SAP AI SDK). It supports Python, Java and JavaScript. To use it with Next.js, the JavaScript version is the correct choice.</P><P>Unless you want to build your code-based agent from the ground up, you'll need to choose one of the many available frameworks. The common choices in JavaScript are LangChain, Mastra and Vercel AI SDK. I chose the <A href="https://ai-sdk.dev/docs/introduction" target="_blank" rel="noopener nofollow noreferrer">Vercel AI SDK</A> as it makes the integration with Next.js super easy. While the Vercel AI SDK supports many model providers out-of-the-box, the SAP AI Core is not included. Luckily, both the <A href="https://sap.github.io/ai-sdk/docs/js/langchain" target="_blank" rel="noopener nofollow noreferrer">SAP AI SDK</A> and the <A href="https://ai-sdk.dev/docs/reference/stream-helpers/langchain-adapter" target="_blank" rel="noopener nofollow noreferrer">Vercel AI SDK</A> have LangChain compatibility layers.</P><P>I started by generating a new Next.js application with <CODE>npx create-next-app@latest</CODE> and installed the required dependencies. Please excuse the image for the instructions, SAP Community makes it really hard to post this command without linking to a user when using the @ symbol.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Screenshot 2025-09-30 at 09.00.44.png" style="width: 959px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/321480iA2BFBE0B02CB7574/image-size/large?v=v2&px=999" role="button" title="Screenshot 2025-09-30 at 09.00.44.png" alt="Screenshot 2025-09-30 at 09.00.44.png" /></span></P><P>The SAP AI SDK needs an environment variable called <CODE>AICORE_SERVICE_KEY</CODE> which contains the service key from your SAP AI Core instance on SAP Business Technology Platform. It should look something like this (but as a string):</P><pre class="lia-code-sample language-markup"><code>{
"clientid": "...",
"clientsecret": "...",
"url": "https://...sap.hana.ondemand.com",
"identityzone": "...",
"identityzoneid": "...",
"appname": "...",
"credential-type": "binding-secret",
"serviceurls": {
"AI_API_URL": "https://...ml.hana.ondemand.com"
}
}</code></pre><P>There seems to be an <A href="https://github.com/SAP/ai-sdk-js/issues/1119" target="_blank" rel="noopener nofollow noreferrer">issue</A> in how Next.js parses the <CODE>.env</CODE> file, so I recommend you load the environment variable manually before starting the Next.js server or with <A href="https://direnv.net/" target="_blank" rel="noopener nofollow noreferrer">direnv</A>.</P><P>Now you need to create the route which calls the model (e.g. <CODE>src/app/api/chat/route.ts</CODE>).</P><pre class="lia-code-sample language-javascript"><code>import { OrchestrationClient } from "@sap-ai-sdk/langchain";
import { toUIMessageStream } from "@ai-sdk/langchain";
import { UIMessage, createUIMessageStreamResponse } from "ai";
export async function POST(req: Request) {
const { messages }: { messages: UIMessage[] } = await req.json();
const model = new OrchestrationClient({
promptTemplating: {
model: {
name: "gpt-4o-mini",
},
},
});
const stream = await model.stream(
messages.map((m) => ({
role: m.role ?? "user",
content: m.parts.map((p) => (p.type === "text" ? p.text : "")).join(""),
}))
);
return createUIMessageStreamResponse({ stream: toUIMessageStream(stream) });
}</code></pre><P>Next, add the page which displays the chat (e.g. <CODE>src/app/page.tsx</CODE>).</P><pre class="lia-code-sample language-javascript"><code>"use client";
import { useChat } from "@ai-sdk/react";
import { useState } from "react";
export default function Chat() {
const [input, setInput] = useState("");
const { messages, sendMessage } = useChat();
return (
<div className="flex flex-col w-full max-w-md py-24 mx-auto stretch">
{messages.map((message) => (
<div key={message.id} className="whitespace-pre-wrap">
{message.role === "user" ? "User: " : "AI: "}
{message.parts.map((part, i) => {
switch (part.type) {
case "text":
return <div key={`${message.id}-${i}`}>{part.text}</div>;
}
})}
</div>
))}
<form
onSubmit={(e) => {
e.preventDefault();
sendMessage({ text: input });
setInput("");
}}
>
<input
className="fixed dark:bg-zinc-900 bottom-0 w-full max-w-md p-2 mb-8 border border-zinc-300 dark:border-zinc-800 rounded shadow-xl"
value={input}
placeholder="Say something..."
onChange={(e) => setInput(e.currentTarget.value)}
/>
</form>
</div>
);
}</code></pre><P>This creates a new SAP AI SDK <CODE>OrchestrationClient</CODE> using the SAP AI SDK LangChain compatibility package, creates a new stream and wraps it with the <CODE>toUIMessageStream</CODE> compatibility function and <CODE>createUIMessageStreamResponse</CODE> function. This way it can be consumed with the <CODE>useChat</CODE> hook from the Vercel AI SDK React package.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="demo.gif" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/321482i5AAE6724017124D2/image-size/medium?v=v2&px=400" role="button" title="demo.gif" alt="demo.gif" /></span></P><P> There is still a long way from this demo to the finished application. But I hope you can see how easy it is to connect open-source tools with your SAP AI Core instance and get started.</P>2025-09-29T16:43:22.762000+02:00https://community.sap.com/t5/integration-blog-posts/consuming-dpi-nextgen-services-using-client-certificate-authentication/ba-p/14270871Consuming DPI NextGen Services using Client Certificate Authentication2025-11-20T11:01:31.311000+01:00shifa_tarannumhttps://community.sap.com/t5/user/viewprofilepage/user-id/855627<P>This blog provides a step-by-step walk-through of using client-certificate authentication to connect to DPI Next Gen (Data Privacy Integration - NextGen) services via destination certificates. You’ll also find practical guidance for testing your setup ensuring secure communication between services via MTLS. Even if you're new to certificate authentication, or just looking for a quick reference, this guide offers an approach to getting it right.</P><P> </P><H2 id="toc-hId-1765457002"><STRONG>Introduction</STRONG></H2><P>Client Certificate Authentication is a secure method to verify the identity of an application making a request to a service. Instead of relying on passwords or OAuth2 tokens, the calling client presents a certificate during the TLS handshake. The service then validates this certificate against trusted authorities before allowing access.</P><P>In the SAP BTP landscape, this authentication mechanism is used when applications communicate with either a platform service (such as Destination Service), or custom apps deployed on Cloud Foundry. By configuring a certificate as a service key, SAP BTP ensures that only verified clients can call endpoints.</P><P>This method offers stronger security for communication because certificates are harder to compromise, can be automatically rotated, and can be scoped strictly to the required service.<BR /><BR /></P><H2 id="toc-hId-1568943497"><STRONG>Accessing Data Privacy Integration (DPI Next Gen) Service</STRONG></H2><P data-unlink="true">DPI Services provides unified data privacy capabilities for applications on SAP BTP. You can explore more about it in the <A href="https://help.sap.com/docs/data-privacy-integration/end-user-information/what-is-data-privacy-integration-nextgen" target="_self" rel="noopener noreferrer">help guide</A>. It also provide <A href="https://api.sap.com/api/ImplementArchivingandDestructionAPIs_NG/resource/Data_Archiving" target="_self" rel="noopener noreferrer">API endpoints</A> that you need to implement in your application.</P><P>To consume the services, create a service instance of <EM>data-privacy-integration-service</EM> with the <EM>data-privacy-internal</EM> plan. Depending on the use-case (for example, archiving, retention, etc.) a set of configurations parameters are expected during service instance creation, which are documented <A href="https://help.sap.com/docs/data-privacy-integration/development/getting-started-data-privacy-integration-nextgen" target="_blank" rel="noopener noreferrer">here</A>.</P><H3 id="toc-hId-1501512711"><STRONG>Generating Client Certificates</STRONG></H3><P>The Destination Certificates UI on the BTP Cockpit provides a way to create and maintain X.509 Certificates, either self-signed or generated via external services.</P><OL><LI>Create a new certificate. Make sure to choose the file-extension as P12 extension, which allows retrieve both the certificate and the corresponding private key. Choose the validity based on your requirement. Password can be left empty.<BR />(<STRONG>Note:</STRONG> whatever you input in the "name" field will end up being the Common Name in the certificate, once generated.)<span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Certificate Creation on BTP" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/342169i5E7479F688C21380/image-size/large?v=v2&px=999" role="button" title="4.jpg" alt="Certificate Creation on BTP" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Certificate Creation on BTP</span></span><BR /><BR /></LI><LI>Once submitted, the UI shows the certificate subject and the issuer details. They are needed during the creation of a service-key. <BR /><BR /><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Destination Certificate Details" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/343538i1C6FA68A21FA77BC/image-size/large?v=v2&px=999" role="button" title="2.jpg" alt="Destination Certificate Details" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Destination Certificate Details</span></span></LI></OL><H3 id="toc-hId-1304999206">Creating a Service Key for DPI NextGen Service</H3><P>Creating a service key without additional parameters typically indicates that the service uses the Client-Credentials flow, where authentication is handled through the client ID and client secret.<BR /><BR />To consume the DPI NextGen service via client-certificates, we'll need to specify our intent via a few parameters when creating a service-key:<BR /><BR /></P><pre class="lia-code-sample language-json"><code>{
"credentials": {
"certSubject": ".....",
"certIssuer": "....."
}
}</code></pre><P>The values for <FONT face="andale mono,times">certSubject</FONT> and <FONT face="andale mono,times">certIssuer</FONT> fields are strings, and can be obtained from the certificate details page as shown previously. For example:<BR /><BR /><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Service Key Configuration Parameters" style="width: 597px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/343545iCCC866C437F85860/image-size/large?v=v2&px=999" role="button" title="5.jpg" alt="Service Key Configuration Parameters" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Service Key Configuration Parameters</span></span></P><P>Click on Create to start the service-key creation. Once the service key is successfully created, you can fetch the callback URL. We will create a destination by providing this URL.<BR /><BR /></P><H3 id="toc-hId-1108485701">Creating a Destination to access DPI NextGen Services</H3><P><SPAN>Now, we create a destination to the DPI NextGen service. </SPAN></P><OL><LI><SPAN>Create a new destination via the BTP Cockpit. For authentication, choose the option <EM>ClientCertificateAuthentication</EM> from the drop-down. In the <EM>Key Store Location</EM> select the certificate that you created in the previous step. The URL can be obtained from the service-key itself.<BR /><BR /><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Creation of a new Destination" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/343542i6DAC3763903DD195/image-size/large?v=v2&px=999" role="button" title="3.jpg" alt="Creation of a new Destination" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Creation of a new Destination</span></span></SPAN><SPAN><BR /><BR /></SPAN></LI><LI><SPAN><SPAN>Click on Save to create the destination. Verify the authentication parameters and the URL.<BR /><BR /><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Destination Details" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/343544i3393520EF4E76DE5/image-size/large?v=v2&px=999" role="button" title="4.jpg" alt="Destination Details" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Destination Details</span></span></SPAN></SPAN></LI></OL><P><SPAN><SPAN>Once this is done, the connectivity setup is complete for any application to consume the DPI NextGen services. In the next few sections, we'll show how to test the connectivity via a REST Client, and also via a deployed application.<BR /><BR /></SPAN></SPAN></P><H3 id="toc-hId-911972196">Getting Client Certificate and Key via Destination Service API<BR /><BR /></H3><TABLE border="1"><TBODY><TR><TD width="100%"><STRONG>To test connectivity via a REST client, we'll need access to both the certificate and the private key.</STRONG> Currently, the "Export" button on the Destination Certificates UI provides only the public certificate. Private keys are only accessible via the <A href="https://api.sap.com/api/SAP_CP_CF_Connectivity_Destination/resource/Certificates_on_Subaccount_Level" target="_blank" rel="noopener noreferrer">Destination Service API</A>.</TD></TR></TBODY></TABLE><P>All destination certificates and private keys can be obtained by sending a GET request to the following endpoint:<BR /><BR /></P><PRE><A href="https://destination-configuration.cfapps.eu12.hana.ondemand.com/destination-configuration/v1/subaccountCertificates" target="_blank" rel="noopener nofollow noreferrer">https://<domain>/destination-configuration/v1/subaccountCertificates</A></PRE><P>The response body will have the following format:</P><pre class="lia-code-sample language-json"><code>{
"Name": "trial.p12",
"Type": "CERTIFICATE",
"Content": "<base64-encoded content>"
}</code></pre><P>The certificate and private-key are packed together into a binary, and then encoded via base64, which is what the API response carries. In the next set of steps, we'll be decoding this base64-encoded content and extract the certificate and private-key as two separate files.<BR /><BR />Copy the value for the "Content" attribute and save it in a <SPAN><SPAN><SPAN>file, for example <EM>Trial.txt</EM>. Since the content is base64-encoded, use the following command to decode it and save it in a proper format:</SPAN></SPAN></SPAN></P><pre class="lia-code-sample language-bash"><code>cat Trial.txt | base64 -d > Trial.p12</code></pre><P><EM>For the subsequent operations, the <FONT face="andale mono,times">openssl</FONT> binary is needed. For macOS, you can install it via <A href="https://formulae.brew.sh/formula/openssl@3" target="_self" rel="nofollow noopener noreferrer">homebrew</A>. </EM><BR /><BR />Next, we'll use this command to display the certificate chain and the private key. This is to verify if the response was properly decoded or not.</P><pre class="lia-code-sample language-bash"><code>openssl pkcs12 -in Trial.p12 -nodes</code></pre><P>There will be prompt to enter a password. It would be the same as the one chosen while creating the certificate. In case you didn't enter a password, just press the enter key to continue.<BR /><BR />Next, we need to save certificate and key into two separate files.</P><P>To save the certificate to a file <EM>TrialCert.crt:</EM></P><pre class="lia-code-sample language-yaml"><code>openssl pkcs12 -in Trial.p12 -nodes -out TrialCert.crt -nokeys </code></pre><P>To save the private key as <EM>TrialKey.key</EM> </P><pre class="lia-code-sample language-yaml"><code>openssl pkcs12 -in Trial.p12 -nodes -out TrialKey.key -nocerts</code></pre><P>There may be some sections with “Bag Attributes”. We can manually correct both the certificate and key files by removing these sections.<BR /><BR /></P><H2 id="toc-hId-586375972">Example: Use DRM Service via REST Client</H2><P>In this section, we'll test by sending calls to the Data Retention Manager (DRM) API which is a part of the DPI NextGen service, by using client-certificate we obtained in the previous step. Any REST client can be used, Bruno is used in these examples.</P><P>In Bruno, create a new collection. On the collection settings page, choose the certificate and key that was created, For domain, specify the root URL for the DPI service.<span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Add Certificate in Bruno" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/343548i11CF870AA9DEB60D/image-size/large?v=v2&px=999" role="button" title="7h(1).jpg" alt="Add Certificate in Bruno" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Add Certificate in Bruno</span></span><BR />Now, within the collection, any request that is created to an endpoint which matches the domain selected above, Bruno will perform authentication via the chosen client-certificate:</P><P><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Send Request to DRM Endpoint" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/343550i70217C5E4B60A823/image-size/large?v=v2&px=999" role="button" title="7h(2).jpg" alt="Send Request to DRM Endpoint" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Send Request to DRM Endpoint</span></span></P><H2 id="toc-hId-389862467"> </H2><H2 id="toc-hId-193348962">Example: Use DRM Service via SAP Cloud SDK</H2><P>For a backend application to consume any external service, SAP recommends using the <STRONG>SAP Cloud SDK</STRONG> for both Java and JavaScript applications. This blog post assumes you're using a CAP application. Follow these steps here to setup Cloud SDK integration: <A href="https://cap.cloud.sap/docs/java/cqn-services/remote-services#cloud-sdk-integration" target="_self" rel="nofollow noopener noreferrer">https://cap.cloud.sap/docs/java/cqn-services/remote-services#cloud-sdk-integration</A><BR /><BR />Also, this blog post uses code-snippets written in Java, but Cloud SDK for JavaScript has equivalent methods and interfaces for everything used here.</P><H3 id="toc-hId-125918176"><STRONG>Setup a Destination Instance</STRONG></H3><P>SAP Cloud SDK provides the <FONT face="andale mono,times">DestinationService</FONT> and <FONT face="andale mono,times">DestinationOptions</FONT> interfaces. This is an example to connect to a destination via name:</P><pre class="lia-code-sample language-java"><code>var service = new DestinationService();
DestinationOptions options;
Try<Destination> destination = service.tryGetDestination("destination-name", options);</code></pre><P>By default, in case of a multi-tenant setup, the Cloud SDK will look-up the destination in a consumer subaccount first. Since, the “TRIAL_AUTH” destination is maintained in the provider subaccount, the destination lookup (a.k.a "retrieval") strategy needs to be changed.<BR /><BR />Here, we explicitly specify the retrieval strategy <EM>ALWAYS_PROVIDER </EM>to make sure the destination is fetched directly from the provider subaccount:</P><pre class="lia-code-sample language-java"><code>DestinationService service = new DestinationService();
DestinationOptions options = DestinationOptions.builder()
.augmentBuilder(DestinationServiceOptionsAugmenter
.augmenter()
.retrievalStrategy(DestinationServiceRetrievalStrategy.ALWAYS_PROVIDER))
.build();
Try<Destination> destinationTry = service.tryGetDestination(“TRIAL_AUTH”, options);
Destination destination = destinationTry.get();</code></pre><P> </P><H3 id="toc-hId--145826698"><STRONG>Setup an HTTP Client using the resolved Destination</STRONG></H3><P>Once the destination has been resolved, the next step is to create an instance of <FONT face="andale mono,times">HTTPClient</FONT>. This instance already has the authentication credentials, headers, etc. defined as set in the destination, which it will be apply for every request:<BR /><BR /></P><pre class="lia-code-sample language-java"><code>// Convert the destination to an HTTP destination
HttpDestination httpDestination = destination.asHttp();
// Obtain an HTTP client instance
HttpClient client = HttpClientAccessor.getHttpClient(destination);
// Create a generic HTTP request (e.g., HttpPut / HttpPost / HttpGet / tec)
HttpRequestBase request = new HttpPut(httpDestination.getUri().toString());
// Example: Replace with new HttpPost(...) or new HttpGet(...)or others as needed
// --- Set headers (example) ---
request.setHeader("Content-Type", "application/json");
request.setHeader("Accept", "application/json");
// Add any additional headers here...
// --- Set payload (only applies to entity-enclosing requests like PUT/POST/others) ---
StringEntity requestBody = new StringEntity(payload, "UTF-8");
if (request instanceof HttpEntityEnclosingRequestBase) {
((HttpEntityEnclosingRequestBase) request).setEntity(requestBody);
}
// Execute the HTTP request
HttpResponse response = client.execute(request);</code></pre><P><BR />To verify this, we setup a dummy backend application with this code, along with an endpoint to trigger it. Again, we setup Bruno to send a request to this custom endpoint and validate the destination-based connectivity. This time, the client-certificates need not be added, since the destination service will provide those:<BR /><BR /><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Send Request to DRM via Bruno" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/343552i99996FD79D268E6D/image-size/large?v=v2&px=999" role="button" title="8(2).jpg" alt="Send Request to DRM via Bruno" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Send Request to DRM via Bruno</span></span></P><H2 id="toc-hId--48937196"><BR />Conclusion</H2><P>This post walked through configuring client-certificate (mTLS) authentication for DPI Next Gen: from creating destination certificates and service keys to wiring a destination for testing both using a REST client and the SAP Cloud SDK. The following is the summary of what we've covered:</P><UL><LI>Generate a P12 certificate in the Destination Certificates UI.</LI><LI>Create the DPI-NextGen service-instance and configure a service-key for client-certificate authentication.</LI><LI>Create a destination using <EM>ClientCertificateAuthentication</EM>.</LI><LI>Retrieve and decode the certificate-key pair via the Destination Service API endpoint.</LI><LI>Test with a REST client to call DRM/DPI endpoints.</LI><LI>Integrate and test from an application using the SAP Cloud SDK.</LI></UL><P> </P>2025-11-20T11:01:31.311000+01:00