Open your SAP OData APIs for some swagger – or how to make friends with the other kids from the API block
📢Note: Before implementing data extraction or write back to/from SAP systems please verify your licensing agreement. SAP Principal Propagation eases that hurdle for read operations.
Find the first post in this series regarding .NET with OData and tackling monday-morning-blues here.
Me and Will Eastbury have been busy building upon the work of the Oasis Foundation in the OData + OpenAPI space, where SAP and Microsoft are large contributors. Today is the day of easily converting your OData metadata into the widely spread OpenAPI definition and making that available to external consumers. Hence, opening your platform to a wide eco system of APIs and developer expertise. 🌎 This becomes more and more relevant due to SAP’s initiative to “keep the ABAP core clean”, “side-by-side-extension” pattern and the race for developers.
Have a look at our webcast session about this topic or follow along the blog post at your leisure.
We will walk you through the steps using the SAP Gateway hosted OData service GW_SAMPLE_BASIC exposing it to Microsoft Power Automate as a connector to prove the point. SAP Gateway is available for ECC and S/4HANA by the way.
Cherry on the cake🍰: we will be applying SAP Principal Propagation under the hood in Azure API Management (APIM). This concept empowers all your client apps to primarily deal with business logic rather than SAP authentication specifics. Azure AD secures access to the APIs while honouring the SAP OAuth server as final validator.
Fig.1 API interface definition flow overview
Once the APIs are registered in APIM, we are ready to enrich them with sophisticated functionalities like request-throttling, user mapping (aka. SAP Principal Propagation via OAuth2SAMLBearerAssertion flow) and token caching.
|🛈 Note: Every OData service exposed via the SAP Gateway could potentially be added to APIM without additional involvement by the NetWeaver Administrators. However, for SAP Principal Propagation as described in this blog the services need to be activated for OAuth. Enablement can be done from transaction /IWFND/MAINT_SERVICE on the SAP Gateway.|
Exporting the API from APIM to Power Automate enables a low code environment to profit from this generalized and central setup. All your client apps may use the same API. Authorization is delegated to Azure AD.
Let’s look at the moving parts 🏊
See below the specifics for Power Platform but keep in mind it is not limited to that.
Fig.2 Data flow after API is introduced in Power Automate
The official docs for SAP APIs in Azure get you started fine, but there are some tweaks required to be plug & play for restrictive external environments. We supply switches to adhere to the OpenAPI policies of our consumer. Find the open-source web-based converter here and the associated GitHub repos here.
Fig.3 Screenshot of OData to OpenAPI converter
Paste your OData definition and check the “Optional Parameters” Section to maintain your service specific parameters.
If you don’t want to put your potentially sensitive connection information on the webapp despite being open-source and no data storage beyond processing, maintain manually afterwards.
Fig.4 Screenshot of conversion parameters
To support exportability and expose OData update capabilities (e.g., ETags) from Azure APIM to Power Automate we need to switch on post-processing features as shown above. For direct import into Power Automate (without Azure APIM in between) switch the swagger override too. In that case OAuth2 and Azure AD tenant id will come handy as well.
We will continue the narrative via APIM and therefore discard the swagger override and maintain OAuth2 manually.
Our target OData service lives on this endpoint:
https://<your domain or ip>:<ssl port>/sap/opu/odata/iwbep/gwsample_basic/
Fig.5 Screenshot of conversion result
Once you hit the convert button from the “OData Definition” section you get your conversion result. Oasis maintains different approaches for the different versions of OData. To support “conversion clarity” we display the version, that was anticipated based on the input schema.
So, let’s save the new OpenAPI spec for our OData service and upload into Azure APIM. Therefore, navigate to your APIM instance.
Fig.6 Screenshot of API create from OpenAPI
Fig.7 Screenshot of Import-Wizard
We recommend supplying the original SAP path as URL suffix to support BTP clients. Some apps anticipate the standard paths (e.g., the OData discovery service in SAP Business Application Studio). Those would break otherwise. We keep “test” as an example.
Verify the newly created API and adjust the required subscription setting. We will rely on AAD and SAP OAuth server instead😊
Fig.8 Screenshot of unchecked subscription requirement
Before we export the definition, we finish the API setup to enable SAP Principal Propagation, X-CSRF-Token handling, and JSON response formatting. We offer a pre-defined Azure APIM policy for that purpose here. In case you are looking for CSRF token handling only, have a look here.
Be aware that this configuration is independent of the “exporting” to Power Automate. The policies remain in Azure APIM and the custom connector simply “points” towards them. Imagine the APIM team overseeing SAP APIs while the client apps only get authorization to interact with them. Basically, full control and freedom for your developers of SAP Integration challenges.
Exporting to your client app with a couple of clicks 🖱️
Find the export function via the three dots and maintain your target environment details.
Fig.9 Screenshot of export process
Before we finish the OAuth settings on Power Automate (or any client app really), we need to create an Application Registration on Azure AD to handle the authentication flow for this new client.
Take note of the application ID from the Overview pane, create a secret (Manage -> Certificates & Secrets) and maintain the API permission exposed by your API Management middle tier as described on our prior blog and GitHub repos.
Fig.10 Screenshot of API permission for APIM middle tier
Use the noted info to fill the Security tab of your custom connector on Power Automate (navigate Data -> Custom Connectors). You can choose Azure AD or Generic OAuth2 as identity providers. I picked generic due to its greater familiarity with the SAP terms regarding OAuth2.
Fig.11 Screenshot of custom connector OAuth2 settings
Re-use the Token URL for the refresh token URL endpoint, put your APIM middle tier scope (if you follow our posts and guides it will be something like: api://your-app-id/user_impersonation) under Scope. Once you save, copy the Redirect URL field at the bottom to finish the setup on the app registration for this custom connector. That will likely be “https://global.consent.azure-apim.net/redirect”.
Fig.12 Screenshot of redirect URL setting after custom connector update
That leaves the Connection setting as final step before we can start testing. There are two options. Maintain the connection from the flow designer or on custom connector level. For the latter navigate to the Test pane on the setup screen and add a connection. The user needs to be allowed to call the front-end application. If you prefer the flow designer, the creation wizard will create the connection during the design process.
Ready to see some action? 🎬
Create a new flow and choose your new shiny custom connector as flow step. That brings up all the SAP API endpoints hosted by APIM 😊
Fig.13 list of all SAP OData operations exposed by imported API
We will choose “Get entity from ProductSet by key”, to prove principal propagation and data write back to the SAP backend. So, pick “Update Entity in ProductSet” next. Supply the product ID from your first GET operation alongside the ETag for the if-match header. OData relies on the ETag concept for update concurrency. Every update operation requires it and the converter presented in this blog post ensures it gets surfaced to the API user via the response header and __metadata property. I will retrieve it from the metadata using an expression like so:
Now, let’s raise the product price and enjoy the SAP data update from your client app, that doesn’t need to care about SAP Principal Propagation, CSRF-tokens, and backend server resource consumption anymore. All that gets taken care of by Azure APIM 😊
Fig.14 Raise price in SAP from client app
Hit the test button and marvel at the simplicity from the Developers point of view 🪄.
Fig.15 price change request result
Find more request details via the “Show raw inputs/outputs” button. It contains the http headers, metadata etc.
What about other popular software runtimes? 💕
Great, we can enable client applications via OpenAPI definitions in various flavours but what about SDKs for full-code developers? We are glad you are asking. The converter is currently being integrated with the Kiota framework to generate SDKs for your OData service out of the box. Kiota covers Ruby, Python (soon), Java, Go, .NET and TypeScript. Enhance SDKs with SAP Cloud Application Programming Model and SAP Cloud SDK libs in a second step.
You get the programming language dependent scaffolding as a package download, so you can hit the ground running. Not too bad, huh? 😉
Stay tuned for more updates regarding this topic.
Production Readiness 🏭
The UI-based flow introduced in this blog is nice for a small number of integration endeavours and to get started. But of course, for enterprise scale, we need to employ APIOps practices to be able to maintain thousands of APIs with different configurations and automatically govern them and their lifecycle in your API Management solution.
A first step could be adding the Azure function powering the UI and flow described in this post to your CD pipeline, so you can run it automatically on every new OData API that you add to your landscape. The Azure Function code is open-source and shared as part of the GitHub repos.
Further Reading 📰
- Microsoft Docs for SAP OData import into Azure APIM
- Principal Propagation policy
- CSRF-Token policy
- DotNET project implementing APIM policy and corresponding blog post
- Exposing your SAP APIs with SAP Graph
- Hybrid API Management for SAP
- Hands-On Lab: Combining the Microsoft-Graph and SAP-Graph in a Microsoft Teams and Azure Bot scenario
Final Words 🥇
Uhh, what a ride. We converted SAP OData APIs into OpenAPI definitions to make them compatible with other open standards widely used in the application development industries. This approach eases the race for rare ABAP developers and addresses SAP’s initiative to avoid extensions within the ABAP core.
As an example, to act upon the alternative API definition we implemented Power Automate. Fronting those APIs via an API Management solution such as Azure APIM brings the additional benefit that client applications and developers can be freed of the burden of SAP specific authentication mechanisms and profit from native security mechanisms on Azure.
SAP Admins get piece of mind due to service throttling capabilities and complete coverage of SAP Principal Propagation all governed in one single place. APIOps and SDK generation based upon the SAP OData services top off the approach.
As always feel free to ask lots of follow-up questions.
Will and Martin
Well done, Martin Pankraz . This is a great way to deal with oData metadata by having other tools (and not SAP APIM) and landscapes in use. Great approach - thanks for sharing and all the tough work on that!
Excellent Blog! Good to see the use case of Power Automate at this level esp. with the OData service handshake! Love it!
Good blog but it raises a question to me...
Why is Azure APIM not interpreting a native OData definition from a (any) backend service? I mean: OData has basically been initiated by Microsoft in 2007 and even the SAP API Management supports OData natively (converts it internally to OpenAPI - you will be able to download the spec in the future).
Anyhow: thanks for sharing your knowledge, Martin! Always appreciated!
Excelllent Martin Pankraz .
For those companies using both SAP and Azure tools/services this is a great integration example. Please keep those inspiring examples flowing.
Great blog, thanks for sharing. We have been using the same approach for some time to expose OData APIs in SAP to external clients. But when using PowerApps to access the APIs we face a huge problem - PowerApps platform only supports OpenAPI specification v2.0, while all our APIs are published on v3.0. This means we have to downgrade the specification in PowerApps to make it work. Do you have a workaround/solution for this problem?
we have added a switch to the converter to create the OpenAPI spec from OData in v2 if required. Check the optional parameters as in fig.4.
When imported into Azure APIM in OpenAPI v3 it gets converted automatically for PowerAutomate when exported.
So, check the switch if you want to use with PowerAutomate directly or don't if you have Azure APIM in between.
Thanks Martin, I will try these options
Frank, please let us know how you get on with the converter, any feedback would be much appreciated!
thank you for this blog. Does the online converter work with an Odata service v2 definition or does it only work for Odata service v4?
the example on the blog is OData v2 too 😉 the conversion logic provided by Oasis covers all OData versions and we re-tested with their examples. Did you hit an issue? Or where is your question coming from?
I tested it and got an empty result with Odata v2 but now 1 hour later it works ;).
Great blog post.
Thanks to Geert-Jan Klaps the OpenAPI definition can also be generated inside the SAP system by implementing his project "ABAP OpenAPI UI" which you can find in the following blog posts:
ABAP OpenAPI UI v1 released! | SAP Blogs
ABAP OpenAPI UI v2, a long overdue update! | SAP Blogs
hi, thank you for such a clean explanation on the openAPI conversion.... it came in handy and i was to convert sap odata metadata to openapi and adjust them as per need via swagger editor.
Thanks a lot.
We are running a S/4HANA system with only Basic Autehntification (No SAML, no SSO, no AAD , no OAuth) I have converted and then imported the Odata metadata definition into Azure APIM.
But then, I keep running into 401 errors
HTTP/1.1 401 Unauthorized content-encoding: br content-length: 6337 content-type: text/html; charset=utf-8 date: Tue, 07 Mar 2023 15:49:19 GMT sap-perf-fesrec: 7354.000000 sap-server: true sap-system: S4H vary: Origin www-authenticate: Basic realm="SAP NetWeaver Application Server [XXX/400]"
I could not find a way to provide the crendentials.
I found the Perform basic authentication.policy.xml, but not sure if it should be used in my case and how ? Inbound ? All operations ?
I would highly discourage exposing APIs with Basic Auth, even if you injected the Azure APIM into a private VNet. Zero-trust policies go a long way reducing blast radius of security incidents.
Regarding your error:
How are you calling that API? Postman for testing or any REST client? You need to provide the credentials there. They are translated into an http Authorization header. It will read Authorization: Basic abcedeh something. Since you have the API key checkbox marked you also need to provide the header Ocp-Apim-Subscription-Key with your generated api key. Retrieve it from the Test pane or the section APIs -> Subscriptions -> Primary Key
Or simply uncheck the API key required checkbox to get started. Let us know how it went.
Works fine, thank you very much ! ( it is a demo system, with test data)
So, you were able to make the OData request successfully now, right?
Yes. for some reasons, Postman was giving me the 401 errors, even with basic authentification filled.
I restarted from scratch, works perfectly fine now. I am actually using SAP Build Apps to retrieve SAP Odata services through Azure APIM, since direct BTP destinations call via SAP Cloud Connector is not an option at the moment (https://launchpad.support.sap.com/#/notes/3300760)
Via Azure APIM, GET operations work fine, but I am facing the dreaded CORS errors for anything else (DELETE, UPDATE ...) I tried to use the CORS policies .. but no success so far
CORS is a browser security topic. When you use have other consumers or API pass through scenarios with APIM it doesn't apply. Or is it maybe a CSRF token issue instead?