diff --git a/jena/src/main/java/com/inrupt/client/jena/JenaBodyHandlers.java b/jena/src/main/java/com/inrupt/client/jena/JenaBodyHandlers.java index 697e0f82f2b..7232cc00c7c 100644 --- a/jena/src/main/java/com/inrupt/client/jena/JenaBodyHandlers.java +++ b/jena/src/main/java/com/inrupt/client/jena/JenaBodyHandlers.java @@ -49,7 +49,7 @@ public final class JenaBodyHandlers { private static void throwOnError(final Response.ResponseInfo responseInfo) { if (!Response.isSuccess(responseInfo.statusCode())) { throw new ClientHttpException( - "Could not map to a Jena entity.", + "An HTTP error was encountered mapping to a Jena entity.", responseInfo.uri(), responseInfo.statusCode(), responseInfo.headers(), diff --git a/jena/src/test/java/com/inrupt/client/jena/JenaBodyHandlersTest.java b/jena/src/test/java/com/inrupt/client/jena/JenaBodyHandlersTest.java index 712541142b2..ee87d0e0e92 100644 --- a/jena/src/test/java/com/inrupt/client/jena/JenaBodyHandlersTest.java +++ b/jena/src/test/java/com/inrupt/client/jena/JenaBodyHandlersTest.java @@ -79,6 +79,31 @@ void testOfJenaModelHandler() throws IOException, ); } + /** + * @deprecated covers the deprecated JenaBodyHandlers::ofModel function. To be removed when removing the function + * from the API. + */ + @Test + void testOfModelHandler() throws IOException, + InterruptedException { + final Request request = Request.newBuilder() + .uri(URI.create(config.get("rdf_uri") + "/oneTriple")) + .GET() + .build(); + + final var response = client.send(request, JenaBodyHandlers.ofModel()) + .toCompletableFuture().join(); + + assertEquals(200, response.statusCode()); + final var responseBody = response.body(); + assertEquals(1, responseBody.size()); + assertTrue(responseBody.contains( + null, + null, + ResourceFactory.createResource("http://example.test/o")) + ); + } + @Test void testOfJenaModelHandlerAsync() throws IOException, InterruptedException, ExecutionException { @@ -102,6 +127,33 @@ void testOfJenaModelHandlerAsync() throws IOException, ); } + /** + * @deprecated covers the deprecated JenaBodyHandlers::ofModel function. To be removed when removing the function + * from the API. + */ + @Test + void testOfModelHandlerAsync() throws IOException, + InterruptedException, ExecutionException { + final Request request = Request.newBuilder() + .uri(URI.create(config.get("rdf_uri") + "/oneTriple")) + .header("Accept", "text/turtle") + .GET() + .build(); + + final var asyncResponse = client.send(request, JenaBodyHandlers.ofModel()); + + final int statusCode = asyncResponse.thenApply(Response::statusCode).toCompletableFuture().join(); + assertEquals(200, statusCode); + + final var responseBody = asyncResponse.thenApply(Response::body).toCompletableFuture().join(); + assertEquals(1, responseBody.size()); + assertTrue(responseBody.contains( + null, + null, + ResourceFactory.createResource("http://example.test/o")) + ); + } + @Test void testOfJenaModelHandlerWithURL() throws IOException, InterruptedException { final Request request = Request.newBuilder() @@ -121,6 +173,29 @@ void testOfJenaModelHandlerWithURL() throws IOException, InterruptedException { ); } + /** + * @deprecated covers the deprecated JenaBodyHandlers::ofModel function. To be removed when removing the function + * from the API. + */ + @Test + void testOfModelHandlerWithURL() throws IOException, InterruptedException { + final Request request = Request.newBuilder() + .uri(URI.create(config.get("rdf_uri") + "/example")) + .GET() + .build(); + + final var response = client.send(request, JenaBodyHandlers.ofModel()) + .toCompletableFuture().join(); + + assertEquals(200, response.statusCode()); + final var responseBody = response.body(); + assertEquals(7, responseBody.size()); + assertTrue(responseBody.contains( + null, + ResourceFactory.createProperty("http://www.w3.org/ns/pim/space#preferencesFile")) + ); + } + @Test void testOfJenaModelHandlerError() throws IOException, InterruptedException { @@ -152,16 +227,42 @@ void testOfJenaDatasetHandler() throws IOException, .build(); final var response = client.send(request, JenaBodyHandlers.ofJenaDataset()) - .toCompletableFuture().join(); + .toCompletableFuture().join(); assertEquals(200, response.statusCode()); final var responseBody = response.body(); assertEquals(1, responseBody.asDatasetGraph().stream().count()); assertTrue(responseBody.asDatasetGraph().contains( - null, - NodeFactory.createURI("http://example.test/s"), - null, - null) + null, + NodeFactory.createURI("http://example.test/s"), + null, + null) + ); + } + + /** + * @deprecated covers the deprecated JenaBodyHandlers::ofDataset function. To be removed when removing the function + * from the API. + */ + @Test + void testOfDatasetHandler() throws IOException, + InterruptedException { + final Request request = Request.newBuilder() + .uri(URI.create(config.get("rdf_uri") + "/oneTriple")) + .GET() + .build(); + + final var response = client.send(request, JenaBodyHandlers.ofDataset()) + .toCompletableFuture().join(); + + assertEquals(200, response.statusCode()); + final var responseBody = response.body(); + assertEquals(1, responseBody.asDatasetGraph().stream().count()); + assertTrue(responseBody.asDatasetGraph().contains( + null, + NodeFactory.createURI("http://example.test/s"), + null, + null) ); } @@ -186,6 +287,31 @@ void testOfJenaDatasetHandlerWithURL() throws IOException, InterruptedException ); } + /** + * @deprecated covers the deprecated JenaBodyHandlers::ofDataset function. To be removed when removing the function + * from the API. + */ + @Test + void testOfDatasetHandlerWithURL() throws IOException, InterruptedException { + final Request request = Request.newBuilder() + .uri(URI.create(config.get("rdf_uri") + "/example")) + .GET() + .build(); + + final var response = client.send(request, JenaBodyHandlers.ofDataset()) + .toCompletableFuture().join(); + + assertEquals(200, response.statusCode()); + final var responseBody = response.body(); + assertEquals(7, responseBody.asDatasetGraph().stream().count()); + assertTrue(responseBody.asDatasetGraph().contains( + null, + null, + NodeFactory.createURI("http://www.w3.org/ns/pim/space#preferencesFile"), + null) + ); + } + @Test void testOfJenaDatasetHandlerError() throws IOException, InterruptedException { @@ -231,6 +357,33 @@ void testOfJenaGraphHandlerAsync() throws IOException, ); } + /** + * @deprecated covers the deprecated JenaBodyHandlers::ofGraph function. To be removed when removing the function + * from the API. + */ + @Test + void testOfGraphHandlerAsync() throws IOException, + InterruptedException, ExecutionException { + final Request request = Request.newBuilder() + .uri(URI.create(config.get("rdf_uri") + "/oneTriple")) + .header("Accept", "text/turtle") + .GET() + .build(); + + final var asyncResponse = client.send(request, JenaBodyHandlers.ofGraph()); + + final int statusCode = asyncResponse.thenApply(Response::statusCode).toCompletableFuture().join(); + assertEquals(200, statusCode); + + final var responseBody = asyncResponse.thenApply(Response::body).toCompletableFuture().join(); + assertEquals(1, responseBody.size()); + assertTrue(responseBody.contains( + NodeFactory.createURI("http://example.test/s"), + null, + null) + ); + } + @Test void testOfJenaGraphHandler() throws IOException, InterruptedException { @@ -252,6 +405,31 @@ void testOfJenaGraphHandler() throws IOException, ); } + /** + * @deprecated covers the deprecated JenaBodyHandlers::ofGraph function. To be removed when removing the function + * from the API. + */ + @Test + void testOfGraphHandler() throws IOException, + InterruptedException { + final Request request = Request.newBuilder() + .uri(URI.create(config.get("rdf_uri") + "/oneTriple")) + .GET() + .build(); + + final var response = client.send(request, JenaBodyHandlers.ofGraph()) + .toCompletableFuture().join(); + + assertEquals(200, response.statusCode()); + final var responseBody = response.body(); + assertEquals(1, responseBody.size()); + assertTrue(responseBody.contains( + NodeFactory.createURI("http://example.test/s"), + null, + null) + ); + } + @Test void testOfJenaGraphHandlerWithURL() throws IOException, InterruptedException { final Request request = Request.newBuilder() @@ -272,6 +450,30 @@ void testOfJenaGraphHandlerWithURL() throws IOException, InterruptedException { ); } + /** + * @deprecated covers the deprecated JenaBodyHandlers::ofGraph function. To be removed when removing the function + * from the API. + */ + @Test + void testOfGraphHandlerWithURL() throws IOException, InterruptedException { + final Request request = Request.newBuilder() + .uri(URI.create(config.get("rdf_uri") + "/example")) + .GET() + .build(); + + final var response = client.send(request, JenaBodyHandlers.ofGraph()) + .toCompletableFuture().join(); + + assertEquals(200, response.statusCode()); + final var responseBody = response.body(); + assertEquals(7, responseBody.size()); + assertTrue(responseBody.contains( + null, + NodeFactory.createURI("http://www.w3.org/ns/pim/space#preferencesFile"), + null) + ); + } + @Test void testOfJenaGraphHandlerError() throws IOException, InterruptedException { diff --git a/rdf4j/src/main/java/com/inrupt/client/rdf4j/RDF4JBodyHandlers.java b/rdf4j/src/main/java/com/inrupt/client/rdf4j/RDF4JBodyHandlers.java index 3273b22e477..bba3ac04680 100644 --- a/rdf4j/src/main/java/com/inrupt/client/rdf4j/RDF4JBodyHandlers.java +++ b/rdf4j/src/main/java/com/inrupt/client/rdf4j/RDF4JBodyHandlers.java @@ -20,12 +20,14 @@ */ package com.inrupt.client.rdf4j; +import com.inrupt.client.ClientHttpException; import com.inrupt.client.Response; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.UncheckedIOException; +import java.nio.charset.StandardCharsets; import org.eclipse.rdf4j.model.Model; import org.eclipse.rdf4j.model.impl.DynamicModelFactory; @@ -41,45 +43,91 @@ */ public final class RDF4JBodyHandlers { + private static void throwOnError(final Response.ResponseInfo responseInfo) { + if (!Response.isSuccess(responseInfo.statusCode())) { + throw new ClientHttpException( + "An HTTP error was encountered mapping to an RDF4J entity.", + responseInfo.uri(), + responseInfo.statusCode(), + responseInfo.headers(), + new String(responseInfo.body().array(), StandardCharsets.UTF_8) + ); + } + } + + private static Model responseToModel(final Response.ResponseInfo responseInfo) { + return responseInfo.headers().firstValue("Content-Type") + .map(RDF4JBodyHandlers::toRDF4JFormat).map(format -> { + try (final InputStream stream = new ByteArrayInputStream(responseInfo.body().array())) { + return Rio.parse(stream, responseInfo.uri().toString(), format); + } catch (final IOException ex) { + throw new UncheckedIOException( + "An I/O error occurred while data was read from the InputStream", ex); + } + }) + .orElseGet(() -> new DynamicModelFactory().createEmptyModel()); + } + /** * Populate a RDF4J {@link Model} with an HTTP response. * * @return an HTTP body handler + * @deprecated Use {@link RDF4JBodyHandlers#ofRDF4JModel} instead for consistent HTTP error handling. */ public static Response.BodyHandler ofModel() { - return responseInfo -> responseInfo.headers().firstValue("Content-Type") - .map(RDF4JBodyHandlers::toRDF4JFormat).map(format -> { - try (final InputStream stream = new ByteArrayInputStream(responseInfo.body().array())) { - return Rio.parse(stream, responseInfo.uri().toString(), format); - } catch (final IOException ex) { - throw new UncheckedIOException( - "An I/O error occurred while data was read from the InputStream", ex); - } - }) - .orElseGet(() -> new DynamicModelFactory().createEmptyModel()); + return RDF4JBodyHandlers::responseToModel; } /** - * Populate a RDF4J {@link Repository} with an HTTP response. + * Populate a RDF4J {@link Model} with an HTTP response. * * @return an HTTP body handler */ - public static Response.BodyHandler ofRepository() { - return responseInfo -> responseInfo.headers().firstValue("Content-Type") + public static Response.BodyHandler ofRDF4JModel() { + return responseInfo -> { + RDF4JBodyHandlers.throwOnError(responseInfo); + return RDF4JBodyHandlers.responseToModel(responseInfo); + }; + } + + private static Repository responseToRepository(final Response.ResponseInfo responseInfo) { + return responseInfo.headers().firstValue("Content-Type") .map(RDF4JBodyHandlers::toRDF4JFormat).map(format -> { final Repository repository = new SailRepository(new MemoryStore()); try (final InputStream stream = new ByteArrayInputStream(responseInfo.body().array()); - final RepositoryConnection conn = repository.getConnection()) { + final RepositoryConnection conn = repository.getConnection()) { conn.add(stream, responseInfo.uri().toString(), format); } catch (final IOException ex) { throw new UncheckedIOException( - "An I/O error occurred while data was read from the InputStream", ex); + "An I/O error occurred while data was read from the InputStream", ex); } return repository; }) .orElseGet(() -> new SailRepository(new MemoryStore())); } + /** + * Populate a RDF4J {@link Repository} with an HTTP response. + * + * @return an HTTP body handler + * @deprecated Use {@link RDF4JBodyHandlers#ofRDF4JRepository} instead for consistent HTTP error handling. + */ + public static Response.BodyHandler ofRepository() { + return RDF4JBodyHandlers::responseToRepository; + } + + /** + * Populate a RDF4J {@link Repository} with an HTTP response. + * + * @return an HTTP body handler + */ + public static Response.BodyHandler ofRDF4JRepository() { + return responseInfo -> { + RDF4JBodyHandlers.throwOnError(responseInfo); + return RDF4JBodyHandlers.responseToRepository(responseInfo); + }; + } + static RDFFormat toRDF4JFormat(final String mediaType) { return Rio.getParserFormatForMIMEType(mediaType).orElse(RDFFormat.TURTLE); } diff --git a/rdf4j/src/test/java/com/inrupt/client/rdf4j/RDF4JBodyHandlersTest.java b/rdf4j/src/test/java/com/inrupt/client/rdf4j/RDF4JBodyHandlersTest.java index 668d42ba8ac..600688acfcf 100644 --- a/rdf4j/src/test/java/com/inrupt/client/rdf4j/RDF4JBodyHandlersTest.java +++ b/rdf4j/src/test/java/com/inrupt/client/rdf4j/RDF4JBodyHandlersTest.java @@ -22,6 +22,7 @@ import static org.junit.jupiter.api.Assertions.*; +import com.inrupt.client.ClientHttpException; import com.inrupt.client.Request; import com.inrupt.client.Response; import com.inrupt.client.spi.HttpService; @@ -32,6 +33,7 @@ import java.net.URI; import java.util.HashMap; import java.util.Map; +import java.util.concurrent.CompletionException; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeoutException; @@ -61,14 +63,14 @@ static void teardown() { } @Test - void testOfModelHandlerAsync() throws IOException, + void testOfRDF4JModelHandlerAsync() throws IOException, InterruptedException, ExecutionException, TimeoutException { final Request request = Request.newBuilder() .uri(URI.create(config.get("rdf_uri") + "/oneTriple")) .GET() .build(); - final Response res = client.send(request, RDF4JBodyHandlers.ofModel()).toCompletableFuture().join(); + final Response res = client.send(request, RDF4JBodyHandlers.ofRDF4JModel()).toCompletableFuture().join(); final int statusCode = res.statusCode(); final Model responseBody = res.body(); @@ -83,15 +85,42 @@ void testOfModelHandlerAsync() throws IOException, ); } + /** + * @deprecated covers the deprecated RDF4JBodyHandlers::ofModel function. To be removed when removing the function + * from the API. + */ @Test - void testOfModelHandler() throws IOException, + void testOfModelHandlerAsync() throws IOException, InterruptedException, ExecutionException, TimeoutException { final Request request = Request.newBuilder() .uri(URI.create(config.get("rdf_uri") + "/oneTriple")) .GET() .build(); - final Response response = client.send(request, RDF4JBodyHandlers.ofModel()) + final Response res = client.send(request, RDF4JBodyHandlers.ofModel()).toCompletableFuture().join(); + + final int statusCode = res.statusCode(); + final Model responseBody = res.body(); + + assertEquals(200, statusCode); + assertEquals(1, responseBody.size()); + assertTrue(responseBody.contains( + (Resource)SimpleValueFactory.getInstance().createIRI("http://example.test/s"), + null, + null, + (Resource)null) + ); + } + + @Test + void testOfRDF4JModelHandler() throws IOException, + InterruptedException, ExecutionException, TimeoutException { + final Request request = Request.newBuilder() + .uri(URI.create(config.get("rdf_uri") + "/oneTriple")) + .GET() + .build(); + + final Response response = client.send(request, RDF4JBodyHandlers.ofRDF4JModel()) .toCompletableFuture().join(); assertEquals(200, response.statusCode()); @@ -105,14 +134,40 @@ void testOfModelHandler() throws IOException, ); } + /** + * @deprecated covers the deprecated RDF4JBodyHandlers::ofModel function. To be removed when removing the function + * from the API. + */ @Test - void testOfModelHandlerWithURL() throws IOException, InterruptedException { + void testOfModelHandler() throws IOException, + InterruptedException, ExecutionException, TimeoutException { final Request request = Request.newBuilder() - .uri(URI.create(config.get("rdf_uri") + "/example")) + .uri(URI.create(config.get("rdf_uri") + "/oneTriple")) .GET() .build(); final Response response = client.send(request, RDF4JBodyHandlers.ofModel()) + .toCompletableFuture().join(); + + assertEquals(200, response.statusCode()); + final Model responseBody = response.body(); + assertEquals(1, responseBody.size()); + assertTrue(responseBody.contains( + (Resource)SimpleValueFactory.getInstance().createIRI("http://example.test/s"), + null, + null, + (Resource)null) + ); + } + + @Test + void testOfRDF4JModelHandlerWithURL() throws IOException, InterruptedException { + final Request request = Request.newBuilder() + .uri(URI.create(config.get("rdf_uri") + "/example")) + .GET() + .build(); + + final Response response = client.send(request, RDF4JBodyHandlers.ofRDF4JModel()) .toCompletableFuture().join(); assertEquals(200, response.statusCode()); @@ -127,6 +182,85 @@ void testOfModelHandlerWithURL() throws IOException, InterruptedException { ); } + /** + * @deprecated covers the deprecated RDF4JBodyHandlers::ofModel function. To be removed when removing the function + * from the API. + */ + @Test + void testOfModelHandlerWithURL() throws IOException, InterruptedException { + final Request request = Request.newBuilder() + .uri(URI.create(config.get("rdf_uri") + "/example")) + .GET() + .build(); + + final Response response = client.send(request, RDF4JBodyHandlers.ofModel()) + .toCompletableFuture().join(); + + assertEquals(200, response.statusCode()); + final Model responseBody = response.body(); + assertEquals(7, responseBody.size()); + assertTrue(responseBody.contains( + null, + SimpleValueFactory.getInstance().createIRI("http://www.w3.org/ns/pim/space#preferencesFile"), + null, + (Resource)null + ) + ); + } + + @Test + void testOfRDF4JModelHandlerError() throws IOException, + InterruptedException, ExecutionException, TimeoutException { + final Request request = Request.newBuilder() + .uri(URI.create(config.get("rdf_uri") + "/error")) + .GET() + .build(); + + final CompletionException completionException = assertThrows( + CompletionException.class, + () -> client.send(request, RDF4JBodyHandlers.ofRDF4JModel()).toCompletableFuture().join() + ); + + final ClientHttpException httpException = (ClientHttpException) completionException.getCause(); + + assertEquals(429, httpException.getProblemDetails().getStatus()); + assertEquals("Too Many Requests", httpException.getProblemDetails().getTitle()); + assertEquals("Some details", httpException.getProblemDetails().getDetails()); + assertEquals("https://example.org/type", httpException.getProblemDetails().getType().toString()); + assertEquals("https://example.org/instance", httpException.getProblemDetails().getInstance().toString()); + } + + @Test + void testOfRDF4JRepositoryHandlerAsync() throws IOException, + InterruptedException, ExecutionException, TimeoutException { + final Request request = Request.newBuilder() + .uri(URI.create(config.get("rdf_uri") + "/oneTriple")) + .GET() + .build(); + + final Response res = client.send(request, RDF4JBodyHandlers.ofRDF4JRepository()) + .toCompletableFuture().join(); + + final int statusCode = res.statusCode(); + assertEquals(200, statusCode); + + final Repository responseBody = res.body(); + try (final RepositoryConnection conn = responseBody.getConnection()) { + assertTrue(conn.hasStatement( + (Resource)SimpleValueFactory.getInstance().createIRI("http://example.test/s"), + null, + null, + false, + (Resource)null + ) + ); + } + } + + /** + * @deprecated covers the deprecated RDF4JBodyHandlers::ofRepository function. + * To be removed when removing the function from the API. + */ @Test void testOfRepositoryHandlerAsync() throws IOException, InterruptedException, ExecutionException, TimeoutException { @@ -136,12 +270,37 @@ void testOfRepositoryHandlerAsync() throws IOException, .build(); final Response res = client.send(request, RDF4JBodyHandlers.ofRepository()) - .toCompletableFuture().join(); + .toCompletableFuture().join(); final int statusCode = res.statusCode(); assertEquals(200, statusCode); final Repository responseBody = res.body(); + try (final RepositoryConnection conn = responseBody.getConnection()) { + assertTrue(conn.hasStatement( + (Resource)SimpleValueFactory.getInstance().createIRI("http://example.test/s"), + null, + null, + false, + (Resource)null + ) + ); + } + } + + @Test + void testOfRDF4JRepositoryHandler() throws IOException, + InterruptedException, ExecutionException, TimeoutException { + final Request request = Request.newBuilder() + .uri(URI.create(config.get("rdf_uri") + "/oneTriple")) + .GET() + .build(); + + final Response response = client.send(request, RDF4JBodyHandlers.ofRDF4JRepository()) + .toCompletableFuture().join(); + assertEquals(200, response.statusCode()); + + final Repository responseBody = response.body(); try (final RepositoryConnection conn = responseBody.getConnection()) { assertTrue(conn.hasStatement( (Resource)SimpleValueFactory.getInstance().createIRI("http://example.test/s"), @@ -154,6 +313,10 @@ void testOfRepositoryHandlerAsync() throws IOException, } } + /** + * @deprecated covers the deprecated RDF4JBodyHandlers::ofRepository function. + * To be removed when removing the function from the API. + */ @Test void testOfRepositoryHandler() throws IOException, InterruptedException, ExecutionException, TimeoutException { @@ -163,14 +326,38 @@ void testOfRepositoryHandler() throws IOException, .build(); final Response response = client.send(request, RDF4JBodyHandlers.ofRepository()) + .toCompletableFuture().join(); + assertEquals(200, response.statusCode()); + + final Repository responseBody = response.body(); + try (final RepositoryConnection conn = responseBody.getConnection()) { + assertTrue(conn.hasStatement( + (Resource)SimpleValueFactory.getInstance().createIRI("http://example.test/s"), + null, + null, + false, + (Resource)null + ) + ); + } + } + + @Test + void testOfRDF4JRepositoryHandlerWithURL() throws IOException, InterruptedException { + final Request request = Request.newBuilder() + .uri(URI.create(config.get("rdf_uri") + "/example")) + .GET() + .build(); + + final Response response = client.send(request, RDF4JBodyHandlers.ofRDF4JRepository()) .toCompletableFuture().join(); assertEquals(200, response.statusCode()); final Repository responseBody = response.body(); try (final RepositoryConnection conn = responseBody.getConnection()) { assertTrue(conn.hasStatement( - (Resource)SimpleValueFactory.getInstance().createIRI("http://example.test/s"), null, + SimpleValueFactory.getInstance().createIRI("http://www.w3.org/ns/pim/space#preferencesFile"), null, false, (Resource)null @@ -179,6 +366,10 @@ void testOfRepositoryHandler() throws IOException, } } + /** + * @deprecated covers the deprecated RDF4JBodyHandlers::ofRepository function. + * To be removed when removing the function from the API. + */ @Test void testOfRepositoryHandlerWithURL() throws IOException, InterruptedException { final Request request = Request.newBuilder() @@ -187,19 +378,41 @@ void testOfRepositoryHandlerWithURL() throws IOException, InterruptedException { .build(); final Response response = client.send(request, RDF4JBodyHandlers.ofRepository()) - .toCompletableFuture().join(); + .toCompletableFuture().join(); assertEquals(200, response.statusCode()); final Repository responseBody = response.body(); try (final RepositoryConnection conn = responseBody.getConnection()) { assertTrue(conn.hasStatement( - null, - SimpleValueFactory.getInstance().createIRI("http://www.w3.org/ns/pim/space#preferencesFile"), - null, - false, - (Resource)null - ) + null, + SimpleValueFactory.getInstance().createIRI("http://www.w3.org/ns/pim/space#preferencesFile"), + null, + false, + (Resource)null + ) ); } } + + @Test + void testOfRDF4JRepositoryHandlerError() throws IOException, + InterruptedException, ExecutionException, TimeoutException { + final Request request = Request.newBuilder() + .uri(URI.create(config.get("rdf_uri") + "/error")) + .GET() + .build(); + + final CompletionException completionException = assertThrows( + CompletionException.class, + () -> client.send(request, RDF4JBodyHandlers.ofRDF4JRepository()).toCompletableFuture().join() + ); + + final ClientHttpException httpException = (ClientHttpException) completionException.getCause(); + + assertEquals(429, httpException.getProblemDetails().getStatus()); + assertEquals("Too Many Requests", httpException.getProblemDetails().getTitle()); + assertEquals("Some details", httpException.getProblemDetails().getDetails()); + assertEquals("https://example.org/type", httpException.getProblemDetails().getType().toString()); + assertEquals("https://example.org/instance", httpException.getProblemDetails().getInstance().toString()); + } }