Note: Application is built in CAP Java Stack along with DWC framework. Technical user is configurated in destination service for making an API call.
Flow :
- API call gets initiated from UI.
- The application layer receives the request and respond with 204
- Then starts the async process explicitly via spring events and adds custom header as ‘sap-language’ based on user's locale before making the call to S4 backend.
httpHeaders.put(DefaultErpHttpDestination.LOCALE_HEADER_NAME, dwcHeaderContainer.getHeader(HttpHeaders.ACCEPT_LANGUAGE));
ModificationResponse<PurchaseRequisition> s4ReqResponse =
s4opRequistionService.createPurchaseRequisition(s4opRequistion).withHeaders(httpHeaders).executeRequest(destinationProvider.getDestination());
However, Observed the underlying cloud SDK layer which 1st makes the call to destination service and retrieves the provided headers and then gets system current locale based on some constraint and adds a header again for ‘sap-language’ with the default system locale as 'en' which leads to problem in our case.
Calling stack trace :
Thread [SimpleAsyncTaskExecutor-60] (Suspended)
CdsRequestHeaderFacade.tryGetRequestHeaders() line: 28
RequestHeaderAccessor.tryGetHeaderContainer() line: 89
DefaultLocaleFacade.getLocalesByHeaders() line: 48
DefaultLocaleFacade.getCurrentLocales() line: 36
DefaultLocaleFacade(LocaleFacade).getCurrentLocale() line: 27
LocaleAccessor.getCurrentLocale() line: 53
371621826.get() line: not available
Option$None<T>(Option<T>).getOrElse(Supplier<? extends T>) line: 336
DefaultErpHttpDestination(ErpHttpDestinationProperties).getLocale() line: 51
DefaultErpHttpDestination.getHeadersToAdd() line: 188
DefaultErpHttpDestination.getHeaders(URI) line: 169
HttpClientWrapper.wrapRequest(HttpUriRequest) line: 98
HttpClientWrapper.execute(HttpUriRequest) line: 116
HttpClientWrapper.execute(HttpUriRequest) line: 35
DefaultCsrfTokenRetriever.retrieveCsrfTokenResponseHeader(HttpClient, String, Map<String,String>) line: 91
DefaultCsrfTokenRetriever.retrieveCsrfToken(HttpClient, String, Map<String,String>) line: 54
ODataRequestCreate(ODataRequestGeneric).lambda$tryGetCsrfToken$5ab307ff$1(CsrfTokenRetriever, HttpClient) line: 266
659039215.apply() line: not available
Try<T>.of(CheckedFunction0<? extends T>) line: 75
ODataRequestCreate(ODataRequestGeneric).tryGetCsrfToken(HttpClient, CsrfTokenRetriever) line: 266
ODataRequestCreate(ODataRequestGeneric).tryExecuteWithCsrfToken(HttpClient, Supplier<HttpResponse>) line: 239
ODataRequestCreate.execute(HttpClient) line: 93
PurchaseRequisitionCreateFluentHelper(FluentHelperCreate<FluentHelperT,EntityT>).executeRequest(HttpDestinationProperties) line: 246
xxxxxx.copyPR(Map<String,Object>) line: 176
xxxxxx.s4AdapterResponseToxxxxxx(xxxxxx) line: 69
xxxxxx.onApplicationEvent(xxxxxx) line: 58
xxxxxxSpringListener.onApplicationEvent(ApplicationEvent) line: 1
SimpleApplicationEventMulticaster.doInvokeListener(ApplicationListener, ApplicationEvent)
SimpleApplicationEventMulticaster.invokeListener(ApplicationListener<?>, ApplicationEvent)
SimpleApplicationEventMulticaster.lambda$multicastEvent$0(ApplicationListener, ApplicationEvent)
1824178544.run() line: not available
DwcContextTaskDecorator.lambda$decorate$0(Map, Runnable) line: 33
1126784716.run() line: not available
Thread.run() line: 829
Sample Http Out Going request and response looks like :
http-outgoing-13 >> "POST /sap/opu/odata/sap/API_PURCHASEREQ_PROCESS_SRV/A_PurchaseRequisitionHeader HTTP/1.1[\r][\n]"
http-outgoing-13 >> "sap-language: de[\r][\n]"
http-outgoing-13 >> "Accept: application/json[\r][\n]"
http-outgoing-13 >> "RequestID: xxxxx[\r][\n]"
http-outgoing-13 >> "RepeatabilityCreation: 2022-09-19T08:50:00.889315900Z[\r][\n]"
http-outgoing-13 >> "X-CorrelationID: [\r][\n]"
http-outgoing-13 >> "x-csrf-token: xxxxx==[\r][\n]"
http-outgoing-13 >> "Content-Type: application/json[\r][\n]"
http-outgoing-13 >> "Authorization: Basic XXXX[\r][\n]"
http-outgoing-13 >> "sap-language: en[\r][\n]"
http-outgoing-13 >> "Content-Length: 1383[\r][\n]"
http-outgoing-13 >> "Host: XXXX[\r][\n]"
http-outgoing-13 >> "Connection: Keep-Alive[\r][\n]"
http-outgoing-13 >> "User-Agent: Apache-HttpClient/4.5.13 (Java/11.0.16)[\r][\n]"
http-outgoing-13 >> "Cookie: XXX%3d; sap-usercontext=sap-language=en&sap-client=xxx[\r][\n]"
http-outgoing-13 >> "Accept-Encoding: gzip,deflate[\r][\n]"
http-outgoing-13 >> "[\r][\n]"
http-outgoing-13 >> "body_xxx"
http-outgoing-13 << "HTTP/1.1 400 Bad Request[\r][\n]"
http-outgoing-13 << {"error":{"code":"06/101","message":{"lang":"en","value":"No master record exists for supplier EPRINT"}"}
If you notice, there are two 'sap-language' header goes to backend as mentioned above and S4 backend layer considers the last header attribute and returns the error message always in English. Our expectation is error messages should come based on user’s locale…
Following queries :
Is there any way by which application layer can instruct to Cloud SDK like Not to append again 'sap-language' header and give preference to what is being passed by the custom header as ‘sap-language’ As ‘de’ in our case?
Is it the bug from Cloud SDK?
Any recommendation or inputs to address this issue..