Skip to content

Commit 62b6d50

Browse files
committed
feat: Overload the getRepresentationContent method with a maxRetries parameter
1 parent 39ed38b commit 62b6d50

4 files changed

Lines changed: 139 additions & 2 deletions

File tree

doc/files.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1078,4 +1078,19 @@ BoxFile file = new BoxFile(api, "12345");
10781078
file.getRepresentationContent("[png?dimensions=1024x1024]", "1.png", output);
10791079
```
10801080

1081+
Generating a representation for the selected file is an asynchronous operation and may take some time.
1082+
Therefore, by default, the `getRepresentationContent` method periodically checks the status of the generated file and downloads it when it is ready.
1083+
With the `maxRetries` parameter in [`getRepresentationContent(String representationHint, String assetPath, OutputStream output, int maxRetries)`][get-rep-content-overloaded], you can define
1084+
the number of status checks for the generated file, which will be performed at intervals of 100 ms.
1085+
1086+
If this number is exceeded, a `BoxApiException` will be thrown.
1087+
1088+
```java
1089+
FileOutputStream output = new FileOutputStream("/path/to/file.png");
1090+
BoxFile file = new BoxFile(api, "12345");
1091+
file.getRepresentationContent("[png?dimensions=1024x1024]", "1.png", output, 10);
1092+
```
1093+
1094+
10811095
[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-
1096+
[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-

src/main/java/com/box/sdk/BoxFile.java

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -505,6 +505,21 @@ public void getRepresentationContent(String representationHint, OutputStream out
505505
* @see <a href=https://developer.box.com/reference#section-x-rep-hints-header>X-Rep-Hints Header</a>
506506
*/
507507
public void getRepresentationContent(String representationHint, String assetPath, OutputStream output) {
508+
this.getRepresentationContent(representationHint, assetPath, output, Integer.MAX_VALUE);
509+
}
510+
511+
/**
512+
* Fetches the contents of a file representation with asset path and writes them to the provided output stream.
513+
*
514+
* @param representationHint the X-Rep-Hints query for the representation to fetch.
515+
* @param assetPath the path of the asset for representations containing multiple files.
516+
* @param output the output stream to write the contents to.
517+
* @param maxRetries the maximum number of attempts to call the request for retrieving status information
518+
* indicating whether the representation has been generated and is ready to fetch.
519+
* If the number of attempts is exceeded, the method will throw a BoxApiException.
520+
* @see <a href=https://developer.box.com/reference#section-x-rep-hints-header>X-Rep-Hints Header</a>
521+
*/
522+
public void getRepresentationContent(String representationHint, String assetPath, OutputStream output, int maxRetries) {
508523

509524
List<Representation> reps = this.getInfoWithRepresentations(representationHint).getRepresentations();
510525
if (reps.size() < 1) {
@@ -523,16 +538,23 @@ public void getRepresentationContent(String representationHint, String assetPath
523538
case "none":
524539

525540
String repContentURLString = null;
526-
while (repContentURLString == null) {
541+
int attemptNumber = 0;
542+
while (repContentURLString == null && attemptNumber < maxRetries) {
527543
repContentURLString = this.pollRepInfo(representation.getInfo().getUrl());
528544
try {
529545
Thread.sleep(100);
530546
} catch (InterruptedException e) {
531547
throw new RuntimeException(e);
532548
}
549+
attemptNumber++;
550+
}
551+
552+
if (repContentURLString != null) {
553+
this.makeRepresentationContentRequest(repContentURLString, assetPath, output);
554+
} else {
555+
throw new BoxAPIException("Representation did non have a success status allowing it to be retrieved after " + maxRetries + " attempts");
533556
}
534557

535-
this.makeRepresentationContentRequest(repContentURLString, assetPath, output);
536558
break;
537559
case "error":
538560
throw new BoxAPIException("Representation had error status");
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"representation": "jpg",
3+
"properties": {
4+
"dimensions": "32x32",
5+
"paged": false,
6+
"thumb": true
7+
},
8+
"info": {
9+
"url": "https://localhost:53621/2.0/internal_files/1030335435441/versions/1116437417841/representations/jpg_thumb_32x32"
10+
},
11+
"status": {
12+
"state": "pending"
13+
},
14+
"content": {
15+
"url_template": "https://localhost:53621/2.0/internal_files/1030335435441/versions/1116437417841/representations/jpg_thumb_32x32/content/{+asset_path}"
16+
}
17+
}

src/test/java/com/box/sdk/BoxFileTest.java

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import static org.junit.Assert.assertEquals;
1616
import static org.junit.Assert.assertNull;
1717
import static org.junit.Assert.assertTrue;
18+
import static org.junit.Assert.fail;
1819

1920
import com.box.sdk.sharedlink.BoxSharedLinkRequest;
2021
import com.eclipsesource.json.Json;
@@ -440,6 +441,88 @@ public void testGetThumbnailSucceeds() {
440441
assertThat(output.toString(), equalTo("This is a JPG"));
441442
}
442443

444+
@Test
445+
public void testGetRepresentationContentThrowsWhenExceedingMaxRetries() {
446+
final String fileID = "12345";
447+
wireMockRule.stubFor(
448+
WireMock.get(WireMock.urlPathEqualTo("/2.0/files/" + fileID))
449+
.withQueryParam("fields", WireMock.equalTo("representations"))
450+
.willReturn(WireMock.aResponse()
451+
.withHeader("Content-Type", APPLICATION_JSON)
452+
.withBody(getFixture("BoxFile/GetFileRepresentations200", wireMockRule.httpsPort()))
453+
.withStatus(200))
454+
);
455+
wireMockRule.stubFor(
456+
WireMock.get(WireMock.urlPathEqualTo(
457+
"/2.0/internal_files/12345/versions/1116420931563/representations/jpg_thumb_32x32")
458+
)
459+
.willReturn(WireMock.aResponse()
460+
.withHeader("Content-Type", APPLICATION_JSON)
461+
.withBody(getFixture("BoxFile/GetFileRepresentation200WithPending", wireMockRule.httpsPort()))
462+
.withStatus(200))
463+
);
464+
465+
try {
466+
BoxFile file = new BoxFile(this.api, fileID);
467+
OutputStream output = new ByteArrayOutputStream();
468+
file.getRepresentationContent("[jpg?dimensions=32x32]", "", output, 5);
469+
fail("getRepresentationContent did not fail with BoxAPIException due to pending status");
470+
assertThat(output.toString(), equalTo("This is a JPG"));
471+
} catch (BoxAPIException apiException) {
472+
assertEquals(apiException.getMessage(), "Representation did non have a success status allowing it to be retrieved after 5 attempts");
473+
}
474+
}
475+
476+
@Test
477+
public void testGetRepresentationContentSuccess() {
478+
final String fileID = "12345";
479+
wireMockRule.stubFor(
480+
WireMock.get(WireMock.urlPathEqualTo("/2.0/files/" + fileID))
481+
.withQueryParam("fields", WireMock.equalTo("representations"))
482+
.willReturn(WireMock.aResponse()
483+
.withHeader("Content-Type", APPLICATION_JSON)
484+
.withBody(getFixture("BoxFile/GetFileRepresentations200", wireMockRule.httpsPort()))
485+
.withStatus(200))
486+
);
487+
wireMockRule.stubFor(
488+
WireMock.get(WireMock.urlPathEqualTo(
489+
"/2.0/internal_files/12345/versions/1116420931563/representations/jpg_thumb_32x32")
490+
)
491+
.inScenario("Get file representation status info")
492+
.willSetStateTo("pending status")
493+
.willReturn(WireMock.aResponse()
494+
.withHeader("Content-Type", APPLICATION_JSON)
495+
.withBody(getFixture("BoxFile/GetFileRepresentation200WithPending", wireMockRule.httpsPort()))
496+
.withStatus(200))
497+
);
498+
wireMockRule.stubFor(
499+
WireMock.get(WireMock.urlPathEqualTo(
500+
"/2.0/internal_files/12345/versions/1116420931563/representations/jpg_thumb_32x32")
501+
)
502+
.inScenario("Get file representation status info")
503+
.whenScenarioStateIs("pending status")
504+
.willReturn(WireMock.aResponse()
505+
.withHeader("Content-Type", APPLICATION_JSON)
506+
.withBody(getFixture("BoxFile/GetFileRepresentation200", wireMockRule.httpsPort()))
507+
.withStatus(200))
508+
);
509+
510+
wireMockRule.stubFor(
511+
WireMock.get(WireMock.urlPathEqualTo(
512+
"/2.0/internal_files/1030335435441/versions/1116437417841/representations/jpg_thumb_32x32/content/"
513+
))
514+
.willReturn(WireMock.aResponse()
515+
.withHeader("Content-Type", "image/jpg")
516+
.withBody("This is a JPG")
517+
.withStatus(200))
518+
);
519+
520+
BoxFile file = new BoxFile(this.api, fileID);
521+
OutputStream output = new ByteArrayOutputStream();
522+
file.getRepresentationContent("[jpg?dimensions=32x32]", output);
523+
assertThat(output.toString(), equalTo("This is a JPG"));
524+
}
525+
443526
@Test
444527
public void testDeletePreviousFileVersionSucceeds() {
445528
final String versionID = "12345";

0 commit comments

Comments
 (0)