Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Streaming Rest Call - returning the response input stream without… #379

Merged
merged 1 commit into from
Sep 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 23 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -851,16 +851,37 @@ org.apache.http.Header[] headers = response.getAllHeaders();
org.apache.http.StatusLine statusLine = response.getStatusLine();

// A convenience method for verifying success
assert response.isSuccessResponse()
assert response.isSuccessResponse();

// Get the response raw body
String rawBody = response.rawBody();
String rawBody = response.getRawBody();

// If the the response raw body has a JSON format, populate an object with the body content,
// by providing a object's class.
List<Map<String, String>> parsedBody = response.parseBody(List.class);
```

Executing an Artifactory streaming REST API

```groovy
ArtifactoryRequest repositoryRequest = new ArtifactoryRequestImpl().apiUrl("api/repositories")
.method(ArtifactoryRequest.Method.GET)
.responseType(ArtifactoryRequest.ContentType.JSON);
ArtifactoryStreamingResponse response = artifactory.streamingRestCall(repositoryRequest);

// Get the response headers
org.apache.http.Header[] headers = response.getAllHeaders();

// Get the response status information
org.apache.http.StatusLine statusLine = response.getStatusLine();

// A convenience method for verifying success
assert response.isSuccessResponse();

// Get the response raw body using input stream
String rawBody = IOUtils.toString(response.getInputStream(), StandardCharsets.UTF_8);
```

## Building and Testing the Sources

The code is built using Gradle and includes integration tests.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package org.jfrog.artifactory.client;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpUriRequest;

import java.io.IOException;
import java.io.InputStream;
Expand Down Expand Up @@ -42,10 +44,14 @@ public interface Artifactory extends ApiInterface, AutoCloseable {

ArtifactoryResponse restCall(ArtifactoryRequest artifactoryRequest) throws IOException;

ArtifactoryStreamingResponse streamingRestCall(ArtifactoryRequest artifactoryRequest) throws IOException;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's document it to the README

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done and tested


InputStream getInputStream(String path) throws IOException;

InputStream getInputStreamWithHeaders(String path, Map<String, String> headers) throws IOException;

HttpResponse execute(HttpUriRequest request) throws IOException;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it really needed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think so


default public <T> T get(String path, Class<? extends T> object, Class<T> interfaceObject) throws IOException {
return null;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,16 @@
package org.jfrog.artifactory.client;

import org.apache.http.Header;
import org.apache.http.StatusLine;

import java.io.IOException;

/**
* ArtifactoryResponse object returned from {@link Artifactory#restCall(ArtifactoryRequest)}.
* acts as a wrapper for {@link org.apache.http.HttpResponse} but removes the need to handle response stream.
*/
public interface ArtifactoryResponse {

Header[] getAllHeaders();

StatusLine getStatusLine();
public interface ArtifactoryResponse extends BaseArtifactoryResponse {

String getRawBody();

<T> T parseBody(Class<T> toType) throws IOException;

boolean isSuccessResponse();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package org.jfrog.artifactory.client;

import java.io.IOException;
import java.io.InputStream;


/**
* ArtifactoryStreamingResponse object returned from {@link Artifactory#streamingRestCall(ArtifactoryRequest)}.
* acts as a wrapper for {@link org.apache.http.HttpResponse}.
*/
public interface ArtifactoryStreamingResponse extends BaseArtifactoryResponse, AutoCloseable {
InputStream getInputStream() throws IOException;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package org.jfrog.artifactory.client;
import org.apache.http.Header;
import org.apache.http.StatusLine;

public interface BaseArtifactoryResponse {

Header[] getAllHeaders();

StatusLine getStatusLine();

boolean isSuccessResponse();

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package org.jfrog.artifactory.client.impl;

import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;

public abstract class AbstractArtifactoryResponseImpl {

private final HttpResponse httpResponse;

public AbstractArtifactoryResponseImpl(HttpResponse httpResponse) {
this.httpResponse = httpResponse;
}

public HttpResponse getHttpResponse() {
return httpResponse;
}

public Header[] getAllHeaders() {
return this.httpResponse.getAllHeaders();
}

public StatusLine getStatusLine() {
return this.httpResponse.getStatusLine();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,24 @@ public ArtifactorySystem system() {
*/
@Override
public ArtifactoryResponse restCall(ArtifactoryRequest artifactoryRequest) throws IOException {
HttpResponse httpResponse = handleArtifactoryRequest(artifactoryRequest);
return new ArtifactoryResponseImpl(httpResponse);
}

/**
* Create a REST call to artifactory with a generic request
*
* @param artifactoryRequest that should be sent to artifactory
* @return {@link ArtifactoryStreamingResponse} Artifactory response in accordance with the request,
* which includes a reference to the inputStream.
*/
@Override
public ArtifactoryStreamingResponse streamingRestCall(ArtifactoryRequest artifactoryRequest) throws IOException {
HttpResponse httpResponse = handleArtifactoryRequest(artifactoryRequest);
return new ArtifactoryStreamingResponseImpl(httpResponse);
}

private HttpResponse handleArtifactoryRequest(ArtifactoryRequest artifactoryRequest) throws IOException {
HttpRequestBase httpRequest;

String requestPath = "/" + artifactoryRequest.getApiUrl();
Expand Down Expand Up @@ -194,7 +212,7 @@ public ArtifactoryResponse restCall(ArtifactoryRequest artifactoryRequest) throw
}

HttpResponse httpResponse = execute(httpRequest);
return new ArtifactoryResponseImpl(httpResponse);
return httpResponse;
}

private void setEntity(HttpEntityEnclosingRequestBase httpRequest, Object body, ContentType contentType) throws JsonProcessingException {
Expand Down Expand Up @@ -369,6 +387,7 @@ public String delete(String path) throws IOException {
return Util.responseToString(httpResponse);
}

@Override
public HttpResponse execute(HttpUriRequest request) throws IOException {
HttpClientContext clientContext = HttpClientContext.create();
if (clientContext.getAttribute(PreemptiveAuthInterceptor.ORIGINAL_HOST_CONTEXT_PARAM) == null) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,22 @@
package org.jfrog.artifactory.client.impl;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
import org.apache.http.util.EntityUtils;
import org.jfrog.artifactory.client.ArtifactoryResponse;
import org.jfrog.artifactory.client.impl.util.Util;

import java.io.IOException;

public class ArtifactoryResponseImpl implements ArtifactoryResponse {
public class ArtifactoryResponseImpl extends AbstractArtifactoryResponseImpl implements ArtifactoryResponse {

private static final ObjectMapper objectMapper = new ObjectMapper();

private HttpResponse httpResponse;
private String rawBody;

ArtifactoryResponseImpl(HttpResponse httpResponse) throws IOException {
this.httpResponse = httpResponse;
super(httpResponse);

HttpEntity entity = httpResponse.getEntity();

Expand All @@ -34,16 +31,6 @@ public class ArtifactoryResponseImpl implements ArtifactoryResponse {
}
}

@Override
public Header[] getAllHeaders() {
return this.httpResponse.getAllHeaders();
}

@Override
public StatusLine getStatusLine() {
return this.httpResponse.getStatusLine();
}

@Override
public String getRawBody() {
return this.rawBody;
Expand All @@ -62,7 +49,6 @@ public <T> T parseBody(Class<T> toType) throws IOException {
@Override
public boolean isSuccessResponse() {
int status = getStatusLine().getStatusCode();

return status >= 200 && status < 300;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package org.jfrog.artifactory.client.impl;

import org.apache.commons.io.IOUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.jfrog.artifactory.client.ArtifactoryStreamingResponse;

import java.io.IOException;
import java.io.InputStream;

public class ArtifactoryStreamingResponseImpl extends AbstractArtifactoryResponseImpl implements ArtifactoryStreamingResponse {

public ArtifactoryStreamingResponseImpl(HttpResponse httpResponse) {
super(httpResponse);
}

@Override
public InputStream getInputStream() throws IOException {
InputStream is = null;
HttpEntity entity = getHttpResponse().getEntity();
if (entity != null) {
is = entity.getContent();
}
return is;
}

@Override
public boolean isSuccessResponse() {
int status = getStatusLine().getStatusCode();
return (status == HttpStatus.SC_OK ||
status == HttpStatus.SC_PARTIAL_CONTENT);
}

@Override
public void close() throws Exception {
IOUtils.close(getInputStream());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package org.jfrog.artifactory.client;

import org.apache.commons.io.IOUtils;
import org.jfrog.artifactory.client.impl.ArtifactoryRequestImpl;
import org.testng.annotations.Test;

import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;

import static org.testng.Assert.*;

public class StreamingRestCallTest extends ArtifactoryTestsBase {

@Test
public void testDownloadWithHeadersByStreamingRestCall() throws IOException {
InputStream inputStream = this.getClass().getResourceAsStream("/sample.txt");
assertNotNull(inputStream);
artifactory.repository(localRepository.getKey()).upload(PATH, inputStream).withProperty("color", "blue")
.withProperty("color", "red").doUpload();

Map<String, String> headers = new HashMap<>();
headers.put("Range", "bytes=0-10");
ArtifactoryRequest request = new ArtifactoryRequestImpl()
.apiUrl(localRepository.getKey() + "/" + PATH)
.method(ArtifactoryRequest.Method.GET)
.setHeaders(headers)
.requestType(ArtifactoryRequest.ContentType.JSON);

ArtifactoryStreamingResponse response = artifactory.streamingRestCall(request);
assertTrue(response.isSuccessResponse());

inputStream = response.getInputStream();
String actual = textFrom(inputStream);
assertEquals(actual, textFrom(this.getClass().getResourceAsStream("/sample.txt")).substring(0, 11));
}

@Test
public void testErrorStreamingRestCall() throws IOException {
ArtifactoryRequest request = new ArtifactoryRequestImpl()
.apiUrl(localRepository.getKey() + "/" + PATH + "shouldNotExist")
.method(ArtifactoryRequest.Method.GET)
.requestType(ArtifactoryRequest.ContentType.JSON);
ArtifactoryStreamingResponse response = artifactory.streamingRestCall(request);
assertFalse(response.isSuccessResponse());
assertEquals(response.getStatusLine().getStatusCode(), 404);
String raw = IOUtils.toString(response.getInputStream(), StandardCharsets.UTF_8);
assertEquals(raw, "{\n" +
" \"errors\" : [ {\n" +
" \"status\" : 404,\n" +
" \"message\" : \"File not found.\"\n" +
" } ]\n" +
"}");
}
}
Loading