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

feat: Overload the getRepresentationContent method with a maxRetries parameter #1251

Merged
merged 3 commits into from
Jun 6, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
15 changes: 15 additions & 0 deletions doc/files.md
Original file line number Diff line number Diff line change
Expand Up @@ -1078,4 +1078,19 @@ BoxFile file = new BoxFile(api, "12345");
file.getRepresentationContent("[png?dimensions=1024x1024]", "1.png", output);
```

Generating a representation for the selected file is an asynchronous operation and may take some time.
Therefore, by default, the `getRepresentationContent` method periodically checks the status of the generated file and downloads it when it is ready.
With the `maxRetries` parameter in [`getRepresentationContent(String representationHint, String assetPath, OutputStream output, int maxRetries)`][get-rep-content-overloaded], you can define
the number of status checks for the generated file, which will be performed at intervals of 100 ms.

If this number is exceeded, a `BoxApiException` will be thrown.

```java
FileOutputStream output = new FileOutputStream("/path/to/file.png");
BoxFile file = new BoxFile(api, "12345");
file.getRepresentationContent("[png?dimensions=1024x1024]", "1.png", output, 10);
```


[get-rep-content]: http://opensource.box.com/box-java-sdk/javadoc/com/box/sdk/BoxFile.html#getRepresentationContent-java.lang.String-java.lang.String-java.io.OutputStream-
[get-rep-content-overloaded]: http://opensource.box.com/box-java-sdk/javadoc/com/box/sdk/BoxFile.html#getRepresentationContent-java.lang.String-java.lang.String-java.io.OutputStream-int-
31 changes: 29 additions & 2 deletions src/main/java/com/box/sdk/BoxFile.java
Original file line number Diff line number Diff line change
Expand Up @@ -505,7 +505,23 @@ public void getRepresentationContent(String representationHint, OutputStream out
* @see <a href=https://developer.box.com/reference#section-x-rep-hints-header>X-Rep-Hints Header</a>
*/
public void getRepresentationContent(String representationHint, String assetPath, OutputStream output) {
this.getRepresentationContent(representationHint, assetPath, output, Integer.MAX_VALUE);
}

/**
* Fetches the contents of a file representation with asset path and writes them to the provided output stream.
*
* @param representationHint the X-Rep-Hints query for the representation to fetch.
* @param assetPath the path of the asset for representations containing multiple files.
* @param output the output stream to write the contents to.
* @param maxRetries the maximum number of attempts to call the request for retrieving status information
* indicating whether the representation has been generated and is ready to fetch.
* If the number of attempts is exceeded, the method will throw a BoxApiException.
* @see <a href=https://developer.box.com/reference#section-x-rep-hints-header>X-Rep-Hints Header</a>
*/
public void getRepresentationContent(
String representationHint, String assetPath, OutputStream output, int maxRetries
) {
List<Representation> reps = this.getInfoWithRepresentations(representationHint).getRepresentations();
if (reps.size() < 1) {
throw new BoxAPIException("No matching representations found for requested '" + representationHint
Expand All @@ -523,16 +539,27 @@ public void getRepresentationContent(String representationHint, String assetPath
case "none":

String repContentURLString = null;
while (repContentURLString == null) {
int attemptNumber = 0;
while (repContentURLString == null && attemptNumber < maxRetries) {
repContentURLString = this.pollRepInfo(representation.getInfo().getUrl());
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
attemptNumber++;
}

if (repContentURLString != null) {
this.makeRepresentationContentRequest(repContentURLString, assetPath, output);
} else {
throw new BoxAPIException(
"Representation did non have a success status allowing it to be retrieved after "
arjankowski marked this conversation as resolved.
Show resolved Hide resolved
+ maxRetries
+ " attempts"
);
}

this.makeRepresentationContentRequest(repContentURLString, assetPath, output);
break;
case "error":
throw new BoxAPIException("Representation had error status");
Expand Down
17 changes: 17 additions & 0 deletions src/test/Fixtures/BoxFile/GetFileRepresentation200WithPending.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"representation": "jpg",
"properties": {
"dimensions": "32x32",
"paged": false,
"thumb": true
},
"info": {
"url": "https://localhost:53621/2.0/internal_files/1030335435441/versions/1116437417841/representations/jpg_thumb_32x32"
},
"status": {
"state": "pending"
},
"content": {
"url_template": "https://localhost:53621/2.0/internal_files/1030335435441/versions/1116437417841/representations/jpg_thumb_32x32/content/{+asset_path}"
}
}
86 changes: 86 additions & 0 deletions src/test/java/com/box/sdk/BoxFileTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import com.box.sdk.sharedlink.BoxSharedLinkRequest;
import com.eclipsesource.json.Json;
Expand Down Expand Up @@ -440,6 +441,91 @@ public void testGetThumbnailSucceeds() {
assertThat(output.toString(), equalTo("This is a JPG"));
}

@Test
public void testGetRepresentationContentThrowsWhenExceedingMaxRetries() {
final String fileID = "12345";
wireMockRule.stubFor(
WireMock.get(WireMock.urlPathEqualTo("/2.0/files/" + fileID))
.withQueryParam("fields", WireMock.equalTo("representations"))
.willReturn(WireMock.aResponse()
.withHeader("Content-Type", APPLICATION_JSON)
.withBody(getFixture("BoxFile/GetFileRepresentations200", wireMockRule.httpsPort()))
.withStatus(200))
);
wireMockRule.stubFor(
WireMock.get(WireMock.urlPathEqualTo(
"/2.0/internal_files/12345/versions/1116420931563/representations/jpg_thumb_32x32")
)
.willReturn(WireMock.aResponse()
.withHeader("Content-Type", APPLICATION_JSON)
.withBody(getFixture("BoxFile/GetFileRepresentation200WithPending", wireMockRule.httpsPort()))
.withStatus(200))
);

try {
BoxFile file = new BoxFile(this.api, fileID);
OutputStream output = new ByteArrayOutputStream();
file.getRepresentationContent("[jpg?dimensions=32x32]", "", output, 5);
fail("getRepresentationContent did not fail with BoxAPIException due to pending status");
assertThat(output.toString(), equalTo("This is a JPG"));
arjankowski marked this conversation as resolved.
Show resolved Hide resolved
} catch (BoxAPIException apiException) {
assertEquals(
apiException.getMessage(),
"Representation did non have a success status allowing it to be retrieved after 5 attempts"
);
}
}

@Test
public void testGetRepresentationContentSuccess() {
final String fileID = "12345";
wireMockRule.stubFor(
WireMock.get(WireMock.urlPathEqualTo("/2.0/files/" + fileID))
.withQueryParam("fields", WireMock.equalTo("representations"))
.willReturn(WireMock.aResponse()
.withHeader("Content-Type", APPLICATION_JSON)
.withBody(getFixture("BoxFile/GetFileRepresentations200", wireMockRule.httpsPort()))
.withStatus(200))
);
wireMockRule.stubFor(
WireMock.get(WireMock.urlPathEqualTo(
"/2.0/internal_files/12345/versions/1116420931563/representations/jpg_thumb_32x32")
)
.inScenario("Get file representation status info")
.willSetStateTo("pending status")
.willReturn(WireMock.aResponse()
.withHeader("Content-Type", APPLICATION_JSON)
.withBody(getFixture("BoxFile/GetFileRepresentation200WithPending", wireMockRule.httpsPort()))
.withStatus(200))
);
wireMockRule.stubFor(
WireMock.get(WireMock.urlPathEqualTo(
"/2.0/internal_files/12345/versions/1116420931563/representations/jpg_thumb_32x32")
)
.inScenario("Get file representation status info")
.whenScenarioStateIs("pending status")
.willReturn(WireMock.aResponse()
.withHeader("Content-Type", APPLICATION_JSON)
.withBody(getFixture("BoxFile/GetFileRepresentation200", wireMockRule.httpsPort()))
.withStatus(200))
);

wireMockRule.stubFor(
WireMock.get(WireMock.urlPathEqualTo(
"/2.0/internal_files/1030335435441/versions/1116437417841/representations/jpg_thumb_32x32/content/"
))
.willReturn(WireMock.aResponse()
.withHeader("Content-Type", "image/jpg")
.withBody("This is a JPG")
.withStatus(200))
);

BoxFile file = new BoxFile(this.api, fileID);
OutputStream output = new ByteArrayOutputStream();
file.getRepresentationContent("[jpg?dimensions=32x32]", output);
assertThat(output.toString(), equalTo("This is a JPG"));
}

@Test
public void testDeletePreviousFileVersionSucceeds() {
final String versionID = "12345";
Expand Down
Loading