2020 */
2121package com .inrupt .client .solid ;
2222
23+ import static java .nio .charset .StandardCharsets .UTF_8 ;
24+
2325import com .inrupt .client .Client ;
2426import com .inrupt .client .ClientProvider ;
2527import com .inrupt .client .Headers ;
3840import java .util .List ;
3941import java .util .Map ;
4042import java .util .Objects ;
43+ import java .util .concurrent .CompletableFuture ;
4144import java .util .concurrent .CompletionStage ;
45+ import java .util .function .Function ;
4246
4347import org .apache .commons .rdf .api .Dataset ;
4448
@@ -59,10 +63,12 @@ public class SolidClient {
5963
6064 private final Client client ;
6165 private final Headers defaultHeaders ;
66+ private final boolean fetchAfterWrite ;
6267
63- SolidClient (final Client client , final Headers headers ) {
68+ SolidClient (final Client client , final Headers headers , final boolean fetchAfterWrite ) {
6469 this .client = Objects .requireNonNull (client , "Client may not be null!" );
6570 this .defaultHeaders = Objects .requireNonNull (headers , "Headers may not be null!" );
71+ this .fetchAfterWrite = fetchAfterWrite ;
6672 }
6773
6874 /**
@@ -72,7 +78,7 @@ public class SolidClient {
7278 * @return a session-scoped client
7379 */
7480 public SolidClient session (final Session session ) {
75- return new SolidClient (client .session (session ), defaultHeaders );
81+ return new SolidClient (client .session (session ), defaultHeaders , fetchAfterWrite );
7682 }
7783
7884 /**
@@ -164,7 +170,7 @@ public <T extends Resource> CompletionStage<T> read(final URI identifier, final
164170 * @param <T> the resource type
165171 * @return the next stage of completion
166172 */
167- public <T extends Resource > CompletionStage <Response < Void > > create (final T resource ) {
173+ public <T extends Resource > CompletionStage <T > create (final T resource ) {
168174 return create (resource , EMPTY_HEADERS );
169175 }
170176
@@ -176,7 +182,7 @@ public <T extends Resource> CompletionStage<Response<Void>> create(final T resou
176182 * @param <T> the resource type
177183 * @return the next stage of completion
178184 */
179- public <T extends Resource > CompletionStage <Response < Void > > create (final T resource , final Headers headers ) {
185+ public <T extends Resource > CompletionStage <T > create (final T resource , final Headers headers ) {
180186 final Request .Builder builder = Request .newBuilder (resource .getIdentifier ()).PUT (cast (resource ));
181187
182188 decorateHeaders (builder , defaultHeaders );
@@ -186,7 +192,8 @@ public <T extends Resource> CompletionStage<Response<Void>> create(final T resou
186192 defaultHeaders .firstValue (USER_AGENT ).ifPresent (agent -> builder .setHeader (USER_AGENT , agent ));
187193 headers .firstValue (USER_AGENT ).ifPresent (agent -> builder .setHeader (USER_AGENT , agent ));
188194
189- return client .send (builder .build (), Response .BodyHandlers .discarding ());
195+ return client .send (builder .build (), Response .BodyHandlers .ofByteArray ())
196+ .thenCompose (handleResponse (resource , headers , "Unable to create resource" ));
190197 }
191198
192199 /**
@@ -196,7 +203,7 @@ public <T extends Resource> CompletionStage<Response<Void>> create(final T resou
196203 * @param <T> the resource type
197204 * @return the next stage of completion
198205 */
199- public <T extends Resource > CompletionStage <Response < Void > > update (final T resource ) {
206+ public <T extends Resource > CompletionStage <T > update (final T resource ) {
200207 return update (resource , EMPTY_HEADERS );
201208 }
202209
@@ -208,7 +215,7 @@ public <T extends Resource> CompletionStage<Response<Void>> update(final T resou
208215 * @param <T> the resource type
209216 * @return the next stage of completion
210217 */
211- public <T extends Resource > CompletionStage <Response < Void > > update (final T resource , final Headers headers ) {
218+ public <T extends Resource > CompletionStage <T > update (final T resource , final Headers headers ) {
212219 final Request .Builder builder = Request .newBuilder (resource .getIdentifier ()).PUT (cast (resource ));
213220
214221 decorateHeaders (builder , defaultHeaders );
@@ -218,7 +225,8 @@ public <T extends Resource> CompletionStage<Response<Void>> update(final T resou
218225 defaultHeaders .firstValue (USER_AGENT ).ifPresent (agent -> builder .setHeader (USER_AGENT , agent ));
219226 headers .firstValue (USER_AGENT ).ifPresent (agent -> builder .setHeader (USER_AGENT , agent ));
220227
221- return client .send (builder .build (), Response .BodyHandlers .discarding ());
228+ return client .send (builder .build (), Response .BodyHandlers .ofByteArray ())
229+ .thenCompose (handleResponse (resource , headers , "Unable to update resource" ));
222230 }
223231
224232 /**
@@ -227,7 +235,7 @@ public <T extends Resource> CompletionStage<Response<Void>> update(final T resou
227235 * @param resource the resource URI
228236 * @return the next stage of completion
229237 */
230- public CompletionStage <Response < Void > > delete (final URI resource ) {
238+ public CompletionStage <Void > delete (final URI resource ) {
231239 return delete (new SolidResourceReference (resource , null ));
232240 }
233241
@@ -238,7 +246,7 @@ public CompletionStage<Response<Void>> delete(final URI resource) {
238246 * @param headers headers to add to this request
239247 * @return the next stage of completion
240248 */
241- public CompletionStage <Response < Void > > delete (final URI resource , final Headers headers ) {
249+ public CompletionStage <Void > delete (final URI resource , final Headers headers ) {
242250 return delete (new SolidResourceReference (resource , null ), headers );
243251 }
244252
@@ -249,7 +257,7 @@ public CompletionStage<Response<Void>> delete(final URI resource, final Headers
249257 * @param <T> the resource type
250258 * @return the next stage of completion
251259 */
252- public <T extends Resource > CompletionStage <Response < Void > > delete (final T resource ) {
260+ public <T extends Resource > CompletionStage <Void > delete (final T resource ) {
253261 return delete (resource , EMPTY_HEADERS );
254262 }
255263
@@ -261,7 +269,7 @@ public <T extends Resource> CompletionStage<Response<Void>> delete(final T resou
261269 * @param <T> the resource type
262270 * @return the next stage of completion
263271 */
264- public <T extends Resource > CompletionStage <Response < Void > > delete (final T resource , final Headers headers ) {
272+ public <T extends Resource > CompletionStage <Void > delete (final T resource , final Headers headers ) {
265273 final Request .Builder builder = Request .newBuilder (resource .getIdentifier ()).DELETE ();
266274
267275 decorateHeaders (builder , defaultHeaders );
@@ -270,7 +278,14 @@ public <T extends Resource> CompletionStage<Response<Void>> delete(final T resou
270278 defaultHeaders .firstValue (USER_AGENT ).ifPresent (agent -> builder .setHeader (USER_AGENT , agent ));
271279 headers .firstValue (USER_AGENT ).ifPresent (agent -> builder .setHeader (USER_AGENT , agent ));
272280
273- return client .send (builder .build (), Response .BodyHandlers .discarding ());
281+ return client .send (builder .build (), Response .BodyHandlers .ofByteArray ()).thenApply (res -> {
282+ if (isSuccess (res .statusCode ())) {
283+ return null ;
284+ } else {
285+ throw new SolidClientException ("Unable to delete resource" , resource .getIdentifier (),
286+ res .statusCode (), res .headers (), new String (res .body (), UTF_8 ));
287+ }
288+ });
274289 }
275290
276291 /**
@@ -297,6 +312,7 @@ public static SolidClient.Builder getClientBuilder() {
297312 public static class Builder {
298313 private Client builderClient ;
299314 private Headers builderHeaders ;
315+ private boolean builderFetchAfterWrite = true ;
300316
301317 Builder () {
302318 }
@@ -323,6 +339,17 @@ public Builder headers(final Headers headers) {
323339 return this ;
324340 }
325341
342+ /**
343+ * Set whether to fetch a resource after a write operation.
344+ *
345+ * @param fetch whether to fetch the remote resource after a write operation
346+ * @return this builder
347+ */
348+ public Builder fetchAfterWrite (final boolean fetch ) {
349+ this .builderFetchAfterWrite = fetch ;
350+ return this ;
351+ }
352+
326353 /**
327354 * Build the {@link SolidClient}.
328355 *
@@ -331,10 +358,29 @@ public Builder headers(final Headers headers) {
331358 public SolidClient build () {
332359 final Client c = builderClient == null ? ClientProvider .getClient () : builderClient ;
333360 final Headers h = builderHeaders == null ? EMPTY_HEADERS : builderHeaders ;
334- return new SolidClient (c , h );
361+ return new SolidClient (c , h , builderFetchAfterWrite );
335362 }
336363 }
337364
365+ <T extends Resource > Function <Response <byte []>, CompletionStage <T >> handleResponse (final T resource ,
366+ final Headers headers , final String message ) {
367+ return res -> {
368+ if (!isSuccess (res .statusCode ())) {
369+ throw new SolidClientException (message , resource .getIdentifier (),
370+ res .statusCode (), res .headers (), new String (res .body (), UTF_8 ));
371+ }
372+
373+ if (!fetchAfterWrite ) {
374+ return CompletableFuture .completedFuture (resource );
375+ }
376+
377+ @ SuppressWarnings ("unchecked" )
378+ final Class <T > clazz = (Class <T >) resource .getClass ();
379+ return read (resource .getIdentifier (), headers , clazz );
380+
381+ };
382+ }
383+
338384 static <T extends Resource > T construct (final URI identifier , final Class <T > clazz ,
339385 final Dataset dataset , final Metadata metadata ) throws ReflectiveOperationException {
340386 try {
@@ -370,6 +416,10 @@ static void decorateHeaders(final Request.Builder builder, final Headers headers
370416 }
371417 }
372418
419+ static boolean isSuccess (final int statusCode ) {
420+ return statusCode >= 200 && statusCode < 300 ;
421+ }
422+
373423 static Request .BodyPublisher cast (final Resource resource ) {
374424 try {
375425 return Request .BodyPublishers .ofInputStream (resource .getEntity ());
0 commit comments