Skip to content

OData V4 CountRequestBuilder.toRequest() is stateful and mutates the resource path #1120

@Louis-Stegra

Description

@Louis-Stegra

Describe the Bug

Calling OData V4 CountRequestBuilder.toRequest() multiple times on the same builder mutates the underlying ODataResourcePath and appends /$count repeatedly.

Observed behavior

Given a CountRequestBuilder for a collection such as:

/People

the first call to toRequest() produces:

/People/$count

but subsequent calls on the same builder can produce:

/People/$count/$count
/People/$count/$count/$count

This happens because ODataRequestCount mutates the ODataResourcePath instance it receives.

Root cause

In ODataRequestCount:

super(servicePath, resourcePath.addSegment("$count"), encodedQuery, protocol);

ODataResourcePath.addSegment(...) is mutating, so any shared path object is modified in place.

This makes toRequest() have side effects, which is surprising for SDK request builders and breaks our integration that inspect a request before executing it.

Proposed fix

Treat the incoming ODataResourcePath as immutable from ODataRequestCount's:

  • copy it before mutating it when constructing a new ODataRequestCount instance

There seems to be a similar setup in ODataRequestFunction.

I have drafted a PR with the suggested fix, feel free to disregard if you consider a different fix, or if you don't believe this is a bug.

Steps to Reproduce

import com.sap.cloud.sdk.datamodel.odatav4.core.CountRequestBuilder;
import com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Person;
import com.sap.cloud.sdk.datamodel.odatav4.referenceservice.namespaces.trippin.Trip;
import com.sap.cloud.sdk.datamodel.odatav4.referenceservice.services.DefaultTrippinService;

public class Repro {
    public static void main(String[] args) {
        Person person = Person.builder().userName("russellwhyte").build();

        CountRequestBuilder<Trip> countBuilder =
            new DefaultTrippinService()
                .forEntity(person)
                .navigateTo(Person.TO_TRIPS)
                .count();

        System.out.println(countBuilder.toRequest().getRelativeUri());
        System.out.println(countBuilder.toRequest().getRelativeUri());
    }
}

Current behaviour, outputs:

/TripPinServiceRW/People('russellwhyte')/Trips/$count
/TripPinServiceRW/People('russellwhyte')/Trips/$count/$count

Expected Behavior

calling toRequest() should not have a side effect on the resource path.

Screenshots

No response

Used Versions

  • Java: 17.0.17
  • SAP Cloud SDK version: 5.26.0

Code Examples

// Your code here

Stack Trace

No response

Log File

No response

Affected Development Phase

Development

Impact

Inconvenience

Timeline

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions