0
votes

We are using SAP SDK 3.25.0 and calling a batch request Read query passing some filters. I am getting the response of all the records and it can be seen that the filter is not working properly.

I have referred this blog here but I am getting the same issue of decode URL issue YY1_QuantityContractTracki?$filter=((CustomerName eq %27Ford%27) and (SalesSchedulingAgreement eq %270030000141%27)) and (PurchaseOrderByCustomer eq %27TEST%27)&$select=SalesSchedulingAgreement,PurchaseOrderByCustomer,Customer,CustomerName,SalesSchedulingAgreementItem,Material,MaterialByCustomer&$format=json

Below is the query program which I am using.

Am I missing something here. Please let us know Thanks, Arun Pai

final BatchRequestBuilder builder = BatchRequestBuilder.withService("/sap/opu/odata/sap/YY1_QUANTITYCONTRACTTRACKI_CDS");
    for (Contract contract : contracts) {
        FilterExpression mainFilter = new FilterExpression("CustomerName", "eq", ODataType.of(contract.getCustomerName()))
                .and(new FilterExpression("SalesSchedulingAgreement", "eq", ODataType.of(contract.getSchAgrmntNo())))
                .and(new FilterExpression("PurchaseOrderByCustomer", "eq", ODataType.of(contract.getCustRefNo())));
        final ODataQuery oDataQuery = ODataQueryBuilder
                .withEntity(sapConfig.getEssentialsContractServiceUrl(),
                        sapConfig.getEssentialsContractListEntity())
                .select("SalesSchedulingAgreement", "PurchaseOrderByCustomer", "Customer", "CustomerName",
                        "SalesSchedulingAgreementItem", "Material", "MaterialByCustomer")
                .filter(mainFilter)
                .build();
        builder.addQueryRequest(oDataQuery);
    }
    final BatchRequest batchRequest = builder.build();
    final BatchResult batchResult = batchRequest.execute(httpClient);

Update

I have changed the version to 3.35.0 today with connectivity version 1.40.11 but it did'nt work either. Below is the log request which gets printed in the console


2021-01-15 19:15:03.831 INFO 42640 --- [io-8084-exec-10] c.s.c.s.o.c.impl.BatchRequestImpl : --batch_123 Content-Type: application/http Content-Transfer-Encoding: binary

GET YY1_QuantityContractTracki?%24filter%3D%28%28CustomerName+eq+%2527Ford27%29+and+%28SalesSchedulingAgreement+eq+%25270030000141%2527%29%29+and+%28PurchaseOrderByCustomer+eq+%2527TEST%2527%29%26%24select%3DSalesSchedulingAgreement%2CPurchaseOrderByCustomer%2CCustomer%2CCustomerName%2CSalesSchedulingAgreementItem%2CMaterial%2CMaterialByCustomer%26%24format%3Djson HTTP/1.1 Accept: application/json;odata=verbose

--batch_123--

2
Did you test the OData URL directly with only one filter expression (e.g. CustomerName) and did it return something? If yes, what happens if you add another argument (e.g. SalesSchedulingAgreement)?Sandra Rossi
While calling the OData URL directly as below, there are no issues and can see the results in the browser YY1_QuantityContractTracki?$filter=((CustomerName eq 'Ford') and (SalesSchedulingAgreement eq '0030000141')) and (PurchaseOrderByCustomer eq 'TEST')&$select=SalesSchedulingAgreement,PurchaseOrderByCustomer,Customer,CustomerName,SalesSchedulingAgreementItem,Material,MaterialByCustomer&$format=json. I have updated the question with the SDK version and log requests which gets printed.Arun Kumar
So, you mean the issue is about how the single quotes are put in the generated URL: that fails with %27, and that succeeds with '.Sandra Rossi
I assume the problem is that ' becomes percentage-encoded to %27 and then double encoded to %2527. This double-encoding is a bug and can not be understood by the OData service.Alexander Dümont

2 Answers

2
votes

Update (22.03.2021)

With the release of SAP Cloud SDK 3.41.0 this week we'll enable support for read operations in OData batch requests on the type-safe API. Please find the chapter in the respective documentation. Example:

BusinessPartnerService service;
BusinessPartnerAddress addressToCreate1;
BusinessPartnerAddress addressToCreate2;

BusinessPartnerFluentHelper requestTenEntities = service.getAllBusinessPartner().top(10);
BusinessPartnerByKeyFluentHelper requestSingleEntity = service.getBusinessPartnerByKey("bupa9000");

BatchResponse result =
    service
        .batch()
        .addReadOperations(requestTenEntities)
        .addReadOperations(requestSingleEntity)
        .executeRequest(destination);

List<BusinessPartner> entities = result.getReadResult(requestTenEntities);
BusinessPartner entity = result.getReadResult(requestSingleEntity);

Original response:

I'm from the SAP Cloud SDK team. Generally we recommend our users to generate classes for their OData service interactions. This way you can easily make sure that requests are according to specification, while type safety is taken care of.

Unfortunately I cannot help you with the API of BatchRequestBuilder, BatchRequest or BatchResult because they are not directly a part of SAP Cloud SDK and not maintained by us. Instead we suggest our own request builders.


If the generation of classes, as linked above, is not an option for you, then I would suggest to try our expert API featuring the Generic OData Client of SAP Cloud SDK. This is the code that we would also use internally for our generated request builders:

String servicePath = "/sap/opu/odata/sap/YY1_QUANTITYCONTRACTTRACKI_CDS";
ODataRequestBatch requestBatch = new ODataRequestBatch(servicePath, ODataProtocol.V2);
Map<Contract, ODataRequestRead> batchedRequests = new HashMap<>();

// iterate over contracts, construct OData query objects and add them to the OData batch request builder
for (Contract contract : contracts) {
    String entityName = sapConfig.getEssentialsContractListEntity();
    String serviceUrl = sapConfig.getEssentialsContractServiceUrl();
    StructuredQuery structuredQuery = StructuredQuery.onEntity(entityName, ODataProtocol.V2);

    structuredQuery.select("SalesSchedulingAgreement", "PurchaseOrderByCustomer", "Customer", "CustomerName", "SalesSchedulingAgreementItem", "Material", "MaterialByCustomer");
    structuredQuery.filter(FieldReference.of("SalesSchedulingAgreement").equalTo(contract.getSchAgrmntNo()));
    structuredQuery.filter(FieldReference.of("PurchaseOrderByCustomer").equalTo(contract.getCustRefNo()));

    String encodedQuery = structuredQuery.getEncodedQueryString();
    ODataRequestRead requestRead = new ODataRequestRead(serviceUrl, entityName, encodedQuery, ODataProtocol.V2);
    batchedRequests.put(contract, requestRead);
    requestBatch.addRead(requestRead);
}

// execute the OData batch request
ODataRequestResultMultipartGeneric batchResult = requestBatch.execute(httpClient);

// extract information from batch response, by referring to the individual OData request references
for( Map.Entry<Contract, ODataRequestRead> requestMapping : batchedRequests.entrySet() ) {
    ODataRequestResultGeneric queryResult = batchResult.getResult(requestMapping.getValue());
    List<Map<String, Object>> itemsForQuery = queryResult.asListOfMaps();
}

Kind regards

Alexander

1
votes

For your information: with the release of SAP Cloud SDK 3.41.0 we enabled support for read operations in OData batch requests on the type-safe API. Please find the chapter in the respective documentation. You would no longer need to use the Generic OData Client of SAP Cloud SDK as suggested in the other response. Example:

BusinessPartnerService service;
BusinessPartnerAddress addressToCreate1;
BusinessPartnerAddress addressToCreate2;

BusinessPartnerFluentHelper requestTenEntities = service.getAllBusinessPartner().top(10);
BusinessPartnerByKeyFluentHelper requestSingleEntity = service.getBusinessPartnerByKey("bupa9000");

BatchResponse result =
    service
        .batch()
        .addReadOperations(requestTenEntities)
        .addReadOperations(requestSingleEntity)
        .executeRequest(destination);

List<BusinessPartner> entities = result.getReadResult(requestTenEntities);
BusinessPartner entity = result.getReadResult(requestSingleEntity);