Skip to content

Commit 4866093

Browse files
acoburnlangsamu
andauthored
JCL-307: Returned modified object (#377)
Co-authored-by: Samu Lang <langsamu@users.noreply.github.com>
1 parent 3d4290f commit 4866093

5 files changed

Lines changed: 134 additions & 33 deletions

File tree

integration/base/src/main/java/com/inrupt/client/integration/base/SolidServerTransformer.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@
3939

4040
class SolidServerTransformer extends ResponseDefinitionTransformer {
4141

42+
private static final String SLASH = "/";
43+
4244
private final Map<String, ServerBody> storage;
4345
private String asUri;
4446

@@ -152,10 +154,10 @@ public ResponseDefinition transform(final Request request, final ResponseDefinit
152154
}
153155

154156
private void addSubContainersToStorage(final String path, final String mimeType) {
155-
final var newPath = path.startsWith("/") ? path.substring(1, path.length()) : path;
156-
var containers = newPath.split("/");
157+
final var newPath = path.startsWith(SLASH) ? path.substring(1, path.length()) : path;
158+
var containers = newPath.split(SLASH);
157159
while (containers.length > 0) {
158-
final var uri = "/" + String.join("/", containers);
160+
final var uri = SLASH + String.join(SLASH, containers) + SLASH;
159161
if (!this.storage.containsKey(uri)) {
160162
this.storage.put(uri, new ServerBody(new byte[0], mimeType));
161163
}

solid/src/main/java/com/inrupt/client/solid/SolidClient.java

Lines changed: 64 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
*/
2121
package com.inrupt.client.solid;
2222

23+
import static java.nio.charset.StandardCharsets.UTF_8;
24+
2325
import com.inrupt.client.Client;
2426
import com.inrupt.client.ClientProvider;
2527
import com.inrupt.client.Headers;
@@ -38,7 +40,9 @@
3840
import java.util.List;
3941
import java.util.Map;
4042
import java.util.Objects;
43+
import java.util.concurrent.CompletableFuture;
4144
import java.util.concurrent.CompletionStage;
45+
import java.util.function.Function;
4246

4347
import 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());

solid/src/main/java/com/inrupt/client/solid/SolidSyncClient.java

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@ public class SolidSyncClient {
3939

4040
private final SolidClient client;
4141

42-
SolidSyncClient(final Client client, final Headers headers) {
43-
this(new SolidClient(client, headers));
42+
SolidSyncClient(final Client client, final Headers headers, final boolean fetchAfterWrite) {
43+
this(new SolidClient(client, headers, fetchAfterWrite));
4444
}
4545

4646
SolidSyncClient(final SolidClient client) {
@@ -87,41 +87,40 @@ public <T extends Resource> T read(final URI identifier, final Class<T> clazz) {
8787
*
8888
* @param resource the resource
8989
* @param <T> the resource type
90+
* @return the created resource
9091
*/
91-
public <T extends Resource> void create(final T resource) {
92-
awaitAsync(client.create(resource));
92+
public <T extends Resource> T create(final T resource) {
93+
return awaitAsync(client.create(resource));
9394
}
9495

9596
/**
9697
* Update an existing Solid Resource.
9798
*
9899
* @param resource the resource
99100
* @param <T> the resource type
100-
* @return the response
101+
* @return the updated resource
101102
*/
102-
public <T extends Resource> Response<Void> update(final T resource) {
103+
public <T extends Resource> T update(final T resource) {
103104
return awaitAsync(client.update(resource));
104105
}
105106

106107
/**
107108
* Delete an existing Solid Resource.
108109
*
109110
* @param resource the resource URI
110-
* @return the next stage of completion
111111
*/
112-
public Response<Void> delete(final URI resource) {
113-
return awaitAsync(client.delete(new SolidResourceReference(resource, null)));
112+
public void delete(final URI resource) {
113+
awaitAsync(client.delete(new SolidResourceReference(resource, null)));
114114
}
115115

116116
/**
117117
* Delete an existing Solid Resource.
118118
*
119119
* @param resource the resource
120120
* @param <T> the resource type
121-
* @return the next stage of completion
122121
*/
123-
public <T extends Resource> Response<Void> delete(final T resource) {
124-
return awaitAsync(client.delete(resource));
122+
public <T extends Resource> void delete(final T resource) {
123+
awaitAsync(client.delete(resource));
125124
}
126125

127126
/**
@@ -143,6 +142,7 @@ public static Builder getClientBuilder() {
143142
public static class Builder {
144143
private Client builderClient;
145144
private Headers builderHeaders;
145+
private boolean builderFetchAfterWrite = true;
146146

147147
Builder() {
148148
}
@@ -169,6 +169,17 @@ public Builder headers(final Headers headers) {
169169
return this;
170170
}
171171

172+
/**
173+
* Set whether to fetch a resource after a write operation.
174+
*
175+
* @param fetch whether to fetch the remote resource after a write operation
176+
* @return this builder
177+
*/
178+
public Builder fetchAfterWrite(final boolean fetch) {
179+
this.builderFetchAfterWrite = fetch;
180+
return this;
181+
}
182+
172183
/**
173184
* Build the {@link SolidSyncClient}.
174185
*
@@ -177,7 +188,7 @@ public Builder headers(final Headers headers) {
177188
public SolidSyncClient build() {
178189
final Client c = builderClient == null ? ClientProvider.getClient() : builderClient;
179190
final Headers h = builderHeaders == null ? SolidClient.EMPTY_HEADERS : builderHeaders;
180-
return new SolidSyncClient(c, h);
191+
return new SolidSyncClient(c, h, builderFetchAfterWrite);
181192
}
182193
}
183194

solid/src/test/java/com/inrupt/client/solid/SolidClientTest.java

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ void testGetContainer() throws IOException, InterruptedException {
180180
}
181181

182182
@Test
183-
void testGetBinary() {
183+
void testGetBinaryUpdate() {
184184
final URI uri = URI.create(config.get("solid_resource_uri") + "/binary");
185185

186186
client.read(uri, SolidNonRDFSource.class).thenAccept(binary -> {
@@ -189,6 +189,21 @@ void testGetBinary() {
189189
assertEquals("text/plain", b.getContentType());
190190

191191
assertDoesNotThrow(client.update(b).toCompletableFuture()::join);
192+
assertDoesNotThrow(client.delete(b).toCompletableFuture()::join);
193+
assertDoesNotThrow(client.delete(b.getIdentifier()).toCompletableFuture()::join);
194+
}
195+
}).toCompletableFuture().join();
196+
}
197+
198+
@Test
199+
void testGetBinaryCreate() {
200+
final URI uri = URI.create(config.get("solid_resource_uri") + "/binary");
201+
202+
client.read(uri, SolidNonRDFSource.class).thenAccept(binary -> {
203+
try (final SolidNonRDFSource b = binary) {
204+
assertEquals(uri, b.getIdentifier());
205+
assertEquals("text/plain", b.getContentType());
206+
192207
assertDoesNotThrow(client.create(b).toCompletableFuture()::join);
193208
assertDoesNotThrow(client.delete(b).toCompletableFuture()::join);
194209
assertDoesNotThrow(client.delete(b.getIdentifier()).toCompletableFuture()::join);

0 commit comments

Comments
 (0)