Reputation: 11
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 :
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..
Upvotes: 1
Views: 188
Reputation: 11
@Alexander, Tried with the following code, however did not work as expected.
Could you plz check once and suggest if i am missing something or anything w.r.t version which needs to be upgraded in order to adopt the recommendation
ModificationResponse<PurchaseRequisition> s4ReqResponse =
RequestHeaderAccessor.executeWithHeaderContainer(dwcHeaderContainer.getHeaders(), () -> {
ModificationResponse<PurchaseRequisition> s4Response = null;¬
try {
s4Response = s4opRequistionService.createPurchaseRequisition(s4opRequistion)
.executeRequest(destinationProvider.getDestination());
return s4Response;
} catch (ODataException e) {
logger.error(EXCEPTION_FROM_COPYPR);
logAndSendErrorMessageToCore(e, requisition);
}
return s4Response;
}
);
StackTrace:
Thread [SimpleAsyncTaskExecutor-41] (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
2065479632.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
1607932978.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
S4Adapter.lambda$0(PurchaseRequisition, Map) line: 177
64962500.call() line: not available
ThreadContextCallable<T>.call() line: 229
ThreadContextExecutor(AbstractThreadContextExecutor<ExecutorT>).execute(Callable<T>) line: 320
RequestHeaderAccessor.executeWithHeaderContainer(RequestHeaderContainer, Callable<T>) line: 185
RequestHeaderAccessor.executeWithHeaderContainer(Map<String,String>, Callable<T>) line: 160
S4Adapter.copyPR(Map<String,Object>) line: 173
Upvotes: 0
Reputation: 938
Is it the bug from Cloud SDK?
Not really, to me this looks like an incorrect API usage:
Your Destination is of type DefaultErpHttpDestination
. It is significantly different from regular destination types, because it automatically adds headers sap-client
and sap-language
. It seems like the class cannot resolve the request headers at runtime, therefore falls back to system default en
.
You are adding headers via OData API, while technically possible it's unreasonable for your actual call. As it results in duplicate header entry.
Is there any way for Cloud SDK not to append again 'sap-language' header?
Yes, do not cast to ErpHttpDestination
explicitly.
Instead of fiddling with the destination or odata request, I would suggest to use the following code:
ModificationResponse<PurchaseRequisition> s4ReqResponse =
RequestHeaderAccessor.executeWithHeaderContainer(
dwcHeaderContainer.getHeaders(),
() -> s4opRequistionService
.createPurchaseRequisition(s4opRequistion)
.executeRequest(destinationProvider.getDestination()));
Reason:
ErpHttpDestination
ThreadContext
.Upvotes: 0