Skip to content

Commit

Permalink
feat: Overload the getRepresentationContent method with a `maxRetri…
Browse files Browse the repository at this point in the history
…es` parameter (#1251)
  • Loading branch information
arjankowski authored Jun 6, 2024
1 parent 39ed38b commit d26bd4f
Show file tree
Hide file tree
Showing 4 changed files with 146 additions and 2 deletions.
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 not have a success status allowing it to be retrieved after "
+ 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}"
}
}
85 changes: 85 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,90 @@ 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");
} catch (BoxAPIException apiException) {
assertEquals(
apiException.getMessage(),
"Representation did not 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

0 comments on commit d26bd4f

Please sign in to comment.