Skip to content

Commit a740d6a

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

4 files changed

Lines changed: 147 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: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -505,7 +505,23 @@ 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+
}
508510

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(
523+
String representationHint, String assetPath, OutputStream output, int maxRetries
524+
) {
509525
List<Representation> reps = this.getInfoWithRepresentations(representationHint).getRepresentations();
510526
if (reps.size() < 1) {
511527
throw new BoxAPIException("No matching representations found for requested '" + representationHint
@@ -523,16 +539,27 @@ public void getRepresentationContent(String representationHint, String assetPath
523539
case "none":
524540

525541
String repContentURLString = null;
526-
while (repContentURLString == null) {
542+
int attemptNumber = 0;
543+
while (repContentURLString == null && attemptNumber < maxRetries) {
527544
repContentURLString = this.pollRepInfo(representation.getInfo().getUrl());
528545
try {
529546
Thread.sleep(100);
530547
} catch (InterruptedException e) {
531548
throw new RuntimeException(e);
532549
}
550+
attemptNumber++;
551+
}
552+
553+
if (repContentURLString != null) {
554+
this.makeRepresentationContentRequest(repContentURLString, assetPath, output);
555+
} else {
556+
throw new BoxAPIException(
557+
"Representation did non have a success status allowing it to be retrieved after "
558+
+ maxRetries
559+
+ " attempts"
560+
);
533561
}
534562

535-
this.makeRepresentationContentRequest(repContentURLString, assetPath, output);
536563
break;
537564
case "error":
538565
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: 86 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,91 @@ 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(
473+
apiException.getMessage(),
474+
"Representation did non have a success status allowing it to be retrieved after 5 attempts"
475+
);
476+
}
477+
}
478+
479+
@Test
480+
public void testGetRepresentationContentSuccess() {
481+
final String fileID = "12345";
482+
wireMockRule.stubFor(
483+
WireMock.get(WireMock.urlPathEqualTo("/2.0/files/" + fileID))
484+
.withQueryParam("fields", WireMock.equalTo("representations"))
485+
.willReturn(WireMock.aResponse()
486+
.withHeader("Content-Type", APPLICATION_JSON)
487+
.withBody(getFixture("BoxFile/GetFileRepresentations200", wireMockRule.httpsPort()))
488+
.withStatus(200))
489+
);
490+
wireMockRule.stubFor(
491+
WireMock.get(WireMock.urlPathEqualTo(
492+
"/2.0/internal_files/12345/versions/1116420931563/representations/jpg_thumb_32x32")
493+
)
494+
.inScenario("Get file representation status info")
495+
.willSetStateTo("pending status")
496+
.willReturn(WireMock.aResponse()
497+
.withHeader("Content-Type", APPLICATION_JSON)
498+
.withBody(getFixture("BoxFile/GetFileRepresentation200WithPending", wireMockRule.httpsPort()))
499+
.withStatus(200))
500+
);
501+
wireMockRule.stubFor(
502+
WireMock.get(WireMock.urlPathEqualTo(
503+
"/2.0/internal_files/12345/versions/1116420931563/representations/jpg_thumb_32x32")
504+
)
505+
.inScenario("Get file representation status info")
506+
.whenScenarioStateIs("pending status")
507+
.willReturn(WireMock.aResponse()
508+
.withHeader("Content-Type", APPLICATION_JSON)
509+
.withBody(getFixture("BoxFile/GetFileRepresentation200", wireMockRule.httpsPort()))
510+
.withStatus(200))
511+
);
512+
513+
wireMockRule.stubFor(
514+
WireMock.get(WireMock.urlPathEqualTo(
515+
"/2.0/internal_files/1030335435441/versions/1116437417841/representations/jpg_thumb_32x32/content/"
516+
))
517+
.willReturn(WireMock.aResponse()
518+
.withHeader("Content-Type", "image/jpg")
519+
.withBody("This is a JPG")
520+
.withStatus(200))
521+
);
522+
523+
BoxFile file = new BoxFile(this.api, fileID);
524+
OutputStream output = new ByteArrayOutputStream();
525+
file.getRepresentationContent("[jpg?dimensions=32x32]", output);
526+
assertThat(output.toString(), equalTo("This is a JPG"));
527+
}
528+
443529
@Test
444530
public void testDeletePreviousFileVersionSucceeds() {
445531
final String versionID = "12345";

0 commit comments

Comments
 (0)