Uniphar.Sap.OData.Client
7.0.0
dotnet add package Uniphar.Sap.OData.Client --version 7.0.0
NuGet\Install-Package Uniphar.Sap.OData.Client -Version 7.0.0
<PackageReference Include="Uniphar.Sap.OData.Client" Version="7.0.0" />
<PackageVersion Include="Uniphar.Sap.OData.Client" Version="7.0.0" />
<PackageReference Include="Uniphar.Sap.OData.Client" />
paket add Uniphar.Sap.OData.Client --version 7.0.0
#r "nuget: Uniphar.Sap.OData.Client, 7.0.0"
#addin nuget:?package=Uniphar.Sap.OData.Client&version=7.0.0
#tool nuget:?package=Uniphar.Sap.OData.Client&version=7.0.0
sap-odata-client
NuGet package repository for Sap OData Client connection
Purpose
This library is for making calls to SAP (running in AZ). Calls to SAP require a base url. Full path will be decorated with the namespace of the service we call.
Calls to the SAP instance requires authentication with Basic auth
- (username
and password
). These are stored in a
secret storage and you do not need to define these. The secret storage can be AZ Keyvault
, AWS Secrets Manager
or
others. Any token
expiry is automatically handled by the SAP client and is not something to be concerned with either.
Custom Events
The library also publishes some custom telemetry events. This is because there has been several issues with the SAP Rise environments. We publish the following events
Event | Description | Remarks |
---|---|---|
SapTokenExpiredEvent | When x-csrf-token has expired, and we get a 403 back when calling the SAP Rise instance |
|
SapTokenUnauthorizedFetchEvent | When requesting a new x-csrf-token SAP returns a 401 instead of the expected 403 |
The system will throw an UnauthorizedAccessException |
SapTokenRefreshedFetchEvent | When requesting a new x-csrf-token , and SAP returns anything but 401 , we notify it was part of the Response headers |
|
SapTokenNotFoundEvent | When requesting a new x-csrf-token , and SAP returns anything but 401 . However, response headers do not contain x-csrf-token |
Setup your project to use SapClient package
When using this NuGet package you need to setup a few things in your project
SapClientOptions
Property | IsRequired | Description | Source |
---|---|---|---|
UserName | True | The SAP user account name | KeyVault |
Password | True | The SAP user account password | KeyVault |
FetchTokenStatusCodeTriggers | False | The set of HttpStatus codes that should trigger renewal of sap token fetching. Default is 401. | appsettings |
The secrets name follow the patter SapClientOptions--{sapApp}--{UserName|Password}
:
SapClientOptions--EWM--UserName
SapClientOptions--EWM--Password
SapClientOptions--S4--UserName
SapClientOptions--S4--Password
NOTE: :
FetchTokenStatusCodeTriggers
is a list of HttpCodes we test against to trigger fetching a new token. We predefine the status code to check for asForbidden (403)
. If the issue we encountered before (returning401
instead of403
) starts again, you need to update the appsettings.json to handle both status codes. You should do that in the project that consumes this NuGet package.Significant changes with version 2.0.2
UseHttpClientHandler
andPooledConnectionLifetime
have been retired with version 2.0.2, as we now use short-living http client instances created by HttpClientFactory. These are no longer part ofSapClientOptions
either.
Program.cs
Add the following to Program.cs
Use
SapClientKeyVaultSecretManager
to map specific credentials to the base configurationbuilder.Configuration.AddAzureKeyVault( new Uri(kvUri), tokenCredential, new AzureKeyVaultConfigurationOptions { ReloadInterval = TimeSpan.FromMinutes(30), Manager = new SapClientKeyVaultSecretManager("S4") // or "EWM" });
Bind
SapClientOptions
so it can used inOptions
pattern:builder.Services.Configure<SapClientOptions>( builder.Configuration.GetSection(nameof(SapClientOptions)));
Register a singleton instance of
HttpClient
, asSapClient
makes calls to SAP over HTTP:// If we need to use NullProxy if (bool.Parse(builder.Configuration["InstanceConfig:EnableNullProxy"]!)) { builder.Services.AddTransient<ISapClient, NullSapClient>(); } else { builder.Services.AddTransient<ISapClient, SapClient>(); builder.Services.AddHttpClient(nameof(SapClient), client => { client.BaseAddress = new Uri(builder.Configuration["SapClientOptions:BaseUrl"]!); client.Timeout = TimeSpan.Parse(builder.Configuration["SapClientOptions:Timeout"]!); }); }
NOTE: : In the example above we have registered a named client as we intend to use same configuration for all delegated clients when making request to SAP.
Using SapClient library
We have a couple of predefined (and custom) JsonSerialisers
we need to use when calling SAP. This is handled by class
SapJsonSerializer
. It exposes a static method:
public static JsonSerializerOptions GetJsonSerializerOptions()
{
// preliminary code logic
jsonSerializerOptions.Converters.Add(new SapDateTimeConverter());
jsonSerializerOptions.Converters.Add(new SapDateTimeOffsetConverter());
jsonSerializerOptions.Converters.Add(new SapDecimalConverter());
return jsonSerializerOptions;
}
The SAP client package exposes two methods
Task<ContentResult> GetAsync<T>(string path, CancellationToken cancellationToken)
Task<ContentResult> PostAsync<T>(string path, T message, CancellationToken cancellationToken)
Both methods return a ContentResult
, which means we can handle based on the Http status code in the response.
Example usage:
public async Task<ActionResult<SalesOrderResponse>> CreateToDoList([FromBody] CreateToDoList createToDoList, CancellationToken cancellationToken)
{
// Call SAP Client to get Serializer Options
var serializerOptions = SapJsonSerializer.GetJsonSerializerOptions();
// Make call to SAP Client POST method
var createdTodoListContentResult = await _sapClient.PostAsync("TO_DO_LIST_API", createToDoList.MapToSapToDoListRequest(), cancellationToken);
switch(createdTodoListContentResult.StatusCode)
{
case 201:
var createdSapTodoListResult = JsonSerializer.Deserialize<OData<ToDoList>>(createdTodoListContentResult.Content!, serializerOptions);
// Make call to SAP Client GET method
var sapToDoListWithItems = await _sapClient.GetAsync<OData<ToDoList>>(
$"TO_DO_LIST_API/TO_DO_LIST_ITEM('{createdSapTodoListResult!.Data!.Id}')?$expand=to_Item", cancellationToken);
var sapToDoList = JsonSerializer.Deserialize<OData<ToDoList>>(sapToDoListWithItems.Content!, serializerOptions);
var newSapToDoList = sapToDoList!.Data!.MapToSapToDoListResponse();
return CreatedAtAction(nameof(CreateToDoList), new { id = newSalesOrder.Id }, newSalesOrder);
case 400:
var sapErrorResponse = JsonSerializer.Deserialize<SapErrorResponse>(createdTodoListContentResult.Content!, options);
return BadRequest(sapErrorResponse!.MapToProblemDetails());
default:
return new StatusCodeResult((int)createdTodoListContentResult.StatusCode!);
}
}
OData Batch Query
The Sap Client supports the ability to execute simple batch queries against a SAP OData endpoint.
To execute a batch query against an OData endpoint the ExecuteBatchQueryAsync method can be used to submit and retrieve the result of the batch query.
BatchQueryResponse batchQueryResponse = await sapClient.ExecuteBatchQueryAsync(
"API_PRODUCT_SRV/$batch?sap-client=200",
new List<BatchQueryRequest>
{
new BatchQueryRequest("A_Product(Product='131187')"),
new BatchQueryRequest("A_Product(Product='386110')")
});
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net8.0
- Azure.Extensions.AspNetCore.Configuration.Secrets (>= 1.4.0)
- Microsoft.ApplicationInsights (>= 2.22.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last updated | |
---|---|---|---|
7.0.0 | 1,834 | 2/19/2025 | |
6.0.0 | 382 | 1/28/2025 | |
5.0.3 | 264 | 12/10/2024 | |
5.0.2 | 110 | 12/10/2024 | |
5.0.1 | 157 | 12/2/2024 | |
5.0.0 | 145 | 11/26/2024 | |
4.1.0 | 185 | 11/14/2024 | |
4.0.3 | 151 | 11/1/2024 | |
4.0.2 | 121 | 10/31/2024 | |
4.0.1 | 116 | 10/31/2024 | |
4.0.0 | 121 | 10/30/2024 | |
3.1.0 | 121 | 10/29/2024 | |
3.0.0 | 121 | 10/25/2024 | |
2.0.4 | 398 | 10/1/2024 | |
2.0.3 | 156 | 9/25/2024 | |
2.0.2 | 149 | 9/24/2024 | |
2.0.1 | 509 | 5/16/2024 | |
2.0.0 | 134 | 5/14/2024 | |
1.0.6 | 616 | 4/8/2024 | |
1.0.5 | 133 | 4/5/2024 | |
1.0.4 | 190 | 4/2/2024 | |
1.0.3 | 150 | 4/2/2024 | |
1.0.2 | 133 | 3/29/2024 | |
1.0.0 | 870 | 12/15/2023 | |
0.4.1 | 159 | 12/15/2023 | |
0.3.0 | 167 | 12/14/2023 | |
0.1.1 | 141 | 12/14/2023 | |
0.1.0 | 151 | 12/14/2023 |