SAP / cloud-sdk

The SAP Cloud SDK documentation and support repository.
https://sap.github.io/cloud-sdk/
Apache License 2.0
45 stars 43 forks source link

Direct call of stream (/$value) is not supported in generated v2 client and response header missing #517

Open datze opened 3 years ago

datze commented 3 years ago

Issue Description

When I generate v2 clients out of the EDMX file, I have no chance to request the binary data stream without requesting the entity first.

Important information: for example to receive a files content with a "file-service", I have to call this:

File file= fileService.withServicePath("FILE_SRV").getFileByKey("abc").executeRequest(sapHttpDestination);
InputStream i = file.fetchAsStream();

This are two HTTP requests, one to get the file, and one to get the /$value.

In my case, the getFileByKey() (FILESET_GET_ENTITY) is not implemented and I have to use this workaround to receive the files content:

    final var file = new File("abc");
    file.attachToService("FILE_SRV", sapHttpDestination);
    InputStream i = file.fetchAsStream();

This is not very convenient and I would prefer to receive the fileService via injection and receive the stream like this:

InputStream i = fileService.withServicePath("FILE_SRV").getFileByKey("abc").executeRequestForStream(sapHttpDestination);

Further, I have no chance to receive the HTTP response headers from the file /$value request. I miss something like

file.getReponseHeaders()

Impact / Priority

My solution will be to not use the generated client but to code the HTTP request on my own. :-( (Only for this request.)

Error Message

(see above, something like Method FILESET_GET_ENTITY not implemented. if I call executeRequest(...))

Project Details

Dependencies and configuration

        <dependency>
            <groupId>com.sap.cloud.sdk.datamodel</groupId>
            <artifactId>odata-core</artifactId>
            <version>3.52.0</version>
        </dependency>
        <dependency>
            <groupId>javax.inject</groupId>
            <artifactId>javax.inject</artifactId>
            <version>1</version>
            <scope>provided</scope>
        </dependency>

            <plugin>
                <groupId>com.sap.cloud.sdk.datamodel</groupId>
                <artifactId>odata-generator-maven-plugin</artifactId>
                <version>3.52.0</version>
                <executions>
                    <execution>
                        <id>generate-consumption</id>
                        <phase>generate-sources</phase>
                        <goals>
                            <goal>generate</goal>
                        </goals>
                        <configuration>
                            <inputDirectory>${project.basedir}/sap/</inputDirectory>
                            <outputDirectory>${project.build.directory}/generated-sources/sap/</outputDirectory>
                            <deleteOutputDirectory>true</deleteOutputDirectory>
                            <packageName>com.abc.sap</packageName>
                            <defaultBasePath>sap/opu/odata/SAP/</defaultBasePath>
                            <compileScope>COMPILE</compileScope>
                            <serviceMethodsPerEntitySet>false</serviceMethodsPerEntitySet>
                            <nameSource>NAME</nameSource>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

Checklist

newtork commented 3 years ago

Hi @datze

You can get fileStream and responseHeaders in a single request by using the low-level Generic OData Client: (ex. /FileService/FileCollection(id='abc')/$value?foo=bar)

HttpDestination destination;

ODataEntityKey entityKey = new ODataEntityKey(ODataProtocol.V2).addKeyProperty("id", "abc")
ODataResourcePath resource = ODataResourcePath.of("FileCollection", entityKey).addSegment("$value");
ODataRequestReadByKey request = new ODataRequestReadByKey("FileService", resource, "?foo=bar", ODataProtocol.V2);

ODataRequestResultGeneric result = request.execute(HttpClientAccessor.getHttpClient(destination));
HttpResponse httpResponse = result.getHttpResponse();
Header[] responseHeaders = httpResponse .getAllHeaders();
try( InputStream fileStream = httpResponse.getEntity().getContent(); ) {
  // do thing
}

The code is perfectly fine if used internally. Since it's a bit verbose we are currently not advertising it as much.

In case you prefer a one-liner / convenience API for your code fileService.withServicePath("FILE_SRV"), then we'll consider a feature request. In that case please let us know the urgency, whether it's considered a blocker and when to expect such changes, if there is a schedule for you.

Alternatively you could just run the HTTP request yourself:

HttpDestination destination;

HttpClient httpClient = HttpClientAccessor.getHttpClient(destination);
HttpResponse httpResponse = httpClient.execute(new HttpGet("/FileService/FileCollection(id='abc')/$value"));
Header[] responseHeaders = httpResponse.getAllHeaders();
try( InputStream fileStream = httpResponse.getEntity().getContent(); ) {
  // do thing
}

Kind regards Alexander