Skip to content

Commit 368633e

Browse files
authored
JCL-435: Add headers data to core API (#774)
1 parent 5d6b00a commit 368633e

File tree

23 files changed

+454
-96
lines changed

23 files changed

+454
-96
lines changed

api/src/main/java/com/inrupt/client/Headers.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,15 @@ public static Headers of(final Map<String, List<String>> headers) {
9090
return new Headers(Objects.requireNonNull(headers));
9191
}
9292

93+
/**
94+
* Create an empty headers object.
95+
*
96+
* @return the new Headers object
97+
*/
98+
public static Headers empty() {
99+
return new Headers(new HashMap<>());
100+
}
101+
93102
private Headers(final Map<String, List<String>> headers) {
94103
this.data.putAll(headers);
95104
}

api/src/main/java/com/inrupt/client/NonRDFSource.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ public class NonRDFSource implements Resource {
3636
private final URI identifier;
3737
private final String contentType;
3838
private final InputStream entity;
39+
private final Headers headers;
3940

4041
/**
4142
* Create a new non-RDF-bearing resource.
@@ -47,9 +48,25 @@ public class NonRDFSource implements Resource {
4748
* @param entity the resource entity
4849
*/
4950
protected NonRDFSource(final URI identifier, final String contentType, final InputStream entity) {
51+
this(identifier, contentType, entity, null);
52+
}
53+
54+
/**
55+
* Create a new non-RDF-bearing resource.
56+
*
57+
* <p>Subclasses should have the same constructor signature to work with the provided object mapping mechanism.
58+
*
59+
* @param identifier the resource identifier
60+
* @param contentType the content type of the resource
61+
* @param entity the resource entity
62+
* @param headers header values associated with the resource, may be {@code null}
63+
*/
64+
protected NonRDFSource(final URI identifier, final String contentType, final InputStream entity,
65+
final Headers headers) {
5066
this.identifier = Objects.requireNonNull(identifier, "identifier may not be null!");
5167
this.contentType = Objects.requireNonNull(contentType, "contentType may not be null!");
5268
this.entity = Objects.requireNonNull(entity, "entity may not be null!");
69+
this.headers = headers == null ? Headers.empty() : headers;
5370
}
5471

5572
@Override
@@ -62,6 +79,11 @@ public String getContentType() {
6279
return contentType;
6380
}
6481

82+
@Override
83+
public Headers getHeaders() {
84+
return headers;
85+
}
86+
6587
@Override
6688
public InputStream getEntity() throws IOException {
6789
return entity;

api/src/main/java/com/inrupt/client/RDFSource.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ public class RDFSource extends WrapperDataset implements Resource {
4949

5050
private final URI identifier;
5151
private final RDFSyntax syntax;
52+
private final Headers headers;
5253

5354
/**
5455
* Create a new RDF-bearing resource.
@@ -62,6 +63,19 @@ protected RDFSource(final URI identifier, final Dataset dataset) {
6263
this(identifier, RDFSyntax.TURTLE, dataset);
6364
}
6465

66+
/**
67+
* Create a new RDF-bearing resource.
68+
*
69+
* <p>Subclasses should have the same constructor signature to work with the provided object mapping mechanism.
70+
*
71+
* @param identifier the resource identifier
72+
* @param dataset the dataset corresponding to this resource, may be {@code null}
73+
* @param headers header values associated with the resource, may be {@code null}
74+
*/
75+
protected RDFSource(final URI identifier, final Dataset dataset, final Headers headers) {
76+
this(identifier, RDFSyntax.TURTLE, dataset, headers);
77+
}
78+
6579
/**
6680
* Create a new RDF-bearing resource.
6781
*
@@ -72,7 +86,22 @@ protected RDFSource(final URI identifier, final Dataset dataset) {
7286
* @param dataset the dataset corresponding to this resource, may be {@code null}
7387
*/
7488
protected RDFSource(final URI identifier, final RDFSyntax syntax, final Dataset dataset) {
89+
this(identifier, syntax, dataset, null);
90+
}
91+
92+
/**
93+
* Create a new RDF-bearing resource.
94+
*
95+
* <p>Subclasses should have the same constructor signature to work with the provided object mapping mechanism.
96+
*
97+
* @param identifier the resource identifier
98+
* @param syntax the original RDF syntax in use
99+
* @param dataset the dataset corresponding to this resource, may be {@code null}
100+
* @param headers header values associated with the resource, may be {@code null}
101+
*/
102+
protected RDFSource(final URI identifier, final RDFSyntax syntax, final Dataset dataset, final Headers headers) {
75103
super(dataset == null ? rdf.createDataset() : dataset);
104+
this.headers = headers == null ? Headers.empty() : headers;
76105
this.identifier = identifier;
77106
this.syntax = syntax;
78107
}
@@ -87,6 +116,11 @@ public String getContentType() {
87116
return syntax.mediaType();
88117
}
89118

119+
@Override
120+
public Headers getHeaders() {
121+
return headers;
122+
}
123+
90124
@Override
91125
public InputStream getEntity() throws IOException {
92126
try (final ByteArrayOutputStream out = new ByteArrayOutputStream()) {

api/src/main/java/com/inrupt/client/Resource.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,13 @@ public interface Resource extends AutoCloseable {
4545
*/
4646
String getContentType();
4747

48+
/**
49+
* The resource headers.
50+
*
51+
* @return the resource headers
52+
*/
53+
Headers getHeaders();
54+
4855
/**
4956
* The resource entity.
5057
*

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

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ static void setup() throws IOException {
165165
.build();
166166
//create test file in access grant enabled container
167167
try (final InputStream is = new ByteArrayInputStream(StandardCharsets.UTF_8.encode("Test text").array())) {
168-
final SolidNonRDFSource testResource = new SolidNonRDFSource(sharedTextFileURI, Utils.PLAIN_TEXT, is, null);
168+
final SolidNonRDFSource testResource = new SolidNonRDFSource(sharedTextFileURI, Utils.PLAIN_TEXT, is);
169169
assertDoesNotThrow(() -> authResourceOwnerClient.create(testResource));
170170
prepareAcpOfResource(authResourceOwnerClient, sharedTextFileURI, SolidNonRDFSource.class);
171171
}
@@ -460,7 +460,7 @@ void accessGrantGetRdfTest(final Session resourceOwnerSession, final Session req
460460
.path("resource-accessGrantGetRdfTest.ttl")
461461
.build();
462462

463-
try (final SolidRDFSource resource = new SolidRDFSource(testRDFresourceURI, null, null)) {
463+
try (final SolidRDFSource resource = new SolidRDFSource(testRDFresourceURI)) {
464464
assertDoesNotThrow(() -> resourceOwnerClient.create(resource));
465465

466466
prepareAcpOfResource(resourceOwnerClient, testRDFresourceURI, SolidRDFSource.class);
@@ -613,7 +613,7 @@ void accessGrantGetNonRdfTest(final Session resourceOwnerSession, final Session
613613
try (final InputStream is = new ByteArrayInputStream(
614614
StandardCharsets.UTF_8.encode("Test test test text").array())) {
615615
final SolidNonRDFSource testResource =
616-
new SolidNonRDFSource(newTestFileURI, Utils.PLAIN_TEXT, is, null);
616+
new SolidNonRDFSource(newTestFileURI, Utils.PLAIN_TEXT, is);
617617
assertDoesNotThrow(() -> resourceOwnerClient.create(testResource));
618618
}
619619

@@ -662,7 +662,7 @@ void accessGrantSetNonRdfTest(final Session resourceOwnerSession, final Session
662662
try (final InputStream is = new ByteArrayInputStream(
663663
StandardCharsets.UTF_8.encode("Test test test text").array())) {
664664
final SolidNonRDFSource testResource =
665-
new SolidNonRDFSource(newTestFileURI, Utils.PLAIN_TEXT, is, null);
665+
new SolidNonRDFSource(newTestFileURI, Utils.PLAIN_TEXT, is);
666666
assertDoesNotThrow(() -> resourceOwnerClient.create(testResource));
667667
}
668668

@@ -744,7 +744,7 @@ void accessGrantCreateNonRdfTest(final Session resourceOwnerSession, final Sessi
744744
try (final InputStream is = new ByteArrayInputStream(
745745
StandardCharsets.UTF_8.encode("Test test test text").array())) {
746746
final SolidNonRDFSource testResource =
747-
new SolidNonRDFSource(newTestFileURI, Utils.PLAIN_TEXT, is, null);
747+
new SolidNonRDFSource(newTestFileURI, Utils.PLAIN_TEXT, is);
748748
assertDoesNotThrow(() -> requesterAuthClient.create(testResource));
749749
}
750750

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

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ static void teardown() {
166166
void fetchPublicResourceUnauthenticatedTest() {
167167
LOGGER.info("Integration Test - Unauthenticated fetch of public resource");
168168
//create a public resource
169-
try (final SolidRDFSource testResource = new SolidRDFSource(publicResourceURI, null, null)) {
169+
try (final SolidRDFSource testResource = new SolidRDFSource(publicResourceURI)) {
170170
final SolidSyncClient client = SolidSyncClient.getClient();
171171
assertDoesNotThrow(() -> client.create(testResource));
172172
assertDoesNotThrow(() -> client.read(publicResourceURI, SolidRDFSource.class));
@@ -183,7 +183,7 @@ void fetchPrivateResourceUnauthenticatedTest(final Session session) {
183183
//create private resource
184184
final SolidSyncClient authClient = SolidSyncClient.getClient().session(session);
185185

186-
try (final SolidRDFSource testResource = new SolidRDFSource(privateResourceURI, null, null)) {
186+
try (final SolidRDFSource testResource = new SolidRDFSource(privateResourceURI)) {
187187
assertDoesNotThrow(() -> authClient.create(testResource));
188188

189189
final SolidSyncClient client = SolidSyncClient.getClient();
@@ -203,7 +203,7 @@ void fetchPublicResourceAuthenticatedTest(final Session session) {
203203
LOGGER.info("Integration Test - Authenticated fetch of public resource");
204204
//create public resource
205205
final SolidSyncClient client = SolidSyncClient.getClient();
206-
try (final SolidRDFSource testResource = new SolidRDFSource(publicResourceURI, null, null)) {
206+
try (final SolidRDFSource testResource = new SolidRDFSource(publicResourceURI)) {
207207
assertDoesNotThrow(() -> client.create(testResource));
208208

209209
final SolidSyncClient authClient = SolidSyncClient.getClient().session(session);
@@ -221,7 +221,7 @@ void fetchPrivateResourceAuthenticatedTest(final Session session) {
221221
LOGGER.info("Integration Test - Authenticated fetch of private resource");
222222
//create private resource
223223
final SolidSyncClient authClient = SolidSyncClient.getClient().session(session);
224-
try (final SolidRDFSource testResource = new SolidRDFSource(privateResourceURI, null, null)) {
224+
try (final SolidRDFSource testResource = new SolidRDFSource(privateResourceURI)) {
225225
assertDoesNotThrow(() -> authClient.create(testResource));
226226

227227
assertDoesNotThrow(() -> authClient.read(privateResourceURI, SolidRDFSource.class));
@@ -238,7 +238,7 @@ void fetchPrivateResourceUnauthAuthTest(final Session session) {
238238
LOGGER.info("Integration Test - Unauthenticated, then auth fetch of private resource");
239239
//create private resource
240240
final SolidSyncClient authClient = SolidSyncClient.getClient().session(session);
241-
try (final SolidRDFSource testResource = new SolidRDFSource(privateResourceURI, null, null)) {
241+
try (final SolidRDFSource testResource = new SolidRDFSource(privateResourceURI)) {
242242
assertDoesNotThrow(() -> authClient.create(testResource));
243243

244244
final SolidSyncClient client = SolidSyncClient.getClient();
@@ -260,15 +260,15 @@ void fetchPrivateResourceUnauthAuthTest(final Session session) {
260260
void multiSessionTest(final Session session) {
261261
LOGGER.info("Integration Test - Multiple sessions authenticated in parallel");
262262
//create private resource
263-
try (final SolidRDFSource testResource = new SolidRDFSource(privateResourceURI, null, null)) {
263+
try (final SolidRDFSource testResource = new SolidRDFSource(privateResourceURI)) {
264264
final SolidSyncClient authClient1 = SolidSyncClient.getClient().session(session);
265265
assertDoesNotThrow(() -> authClient1.create(testResource));
266266

267267
//create another private resource with another client
268268
final URI privateResourceURL2 = URIBuilder.newBuilder(privateContainerURI)
269269
.path("resource2.ttl")
270270
.build();
271-
try (final SolidRDFSource testResource2 = new SolidRDFSource(privateResourceURL2, null, null)) {
271+
try (final SolidRDFSource testResource2 = new SolidRDFSource(privateResourceURL2)) {
272272
final SolidSyncClient authClient2 =
273273
SolidSyncClient.getClient().session(session);
274274
assertDoesNotThrow(() -> authClient2.create(testResource2));

integration/openid/src/test/java/com/inrupt/client/integration/openid/OpenIdAuthenticationScenariosTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ void openIdAuthenticationTest(final Session session) {
4444
LOGGER.info("Integration Test - Authenticated fetch of private resource uses OpenID authenticator");
4545
//create private resource
4646
final SolidSyncClient authClient = SolidSyncClient.getClient().session(session);
47-
try (final SolidRDFSource testResource = new SolidRDFSource(privateResourceURI, null, null)) {
47+
try (final SolidRDFSource testResource = new SolidRDFSource(privateResourceURI)) {
4848
assertDoesNotThrow(() -> authClient.create(testResource));
4949

5050
assertDoesNotThrow(() -> authClient.read(privateResourceURI, SolidRDFSource.class));

performance/base/src/main/java/com/inrupt/client/performance/base/GetResourcesWithGrantCachingTokenScenario.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,7 @@ private static void createResources(final SolidSyncClient client, final URI priv
242242
try (final InputStream is =
243243
new ByteArrayInputStream(StandardCharsets.UTF_8.encode("Test text").array())) {
244244
final SolidNonRDFSource testResource =
245-
new SolidNonRDFSource(resourceURI, Utils.PLAIN_TEXT, is, null);
245+
new SolidNonRDFSource(resourceURI, Utils.PLAIN_TEXT, is);
246246
assertDoesNotThrow(() -> client.create(testResource));
247247
} catch (IOException e) {
248248
LOGGER.warn("Could not create performance test resource " + resourceURI);

performance/base/src/main/java/com/inrupt/client/performance/base/GetResourcesWithGrantScenario.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ private static void createResources(final SolidSyncClient client, final URI priv
239239
try (final InputStream is =
240240
new ByteArrayInputStream(StandardCharsets.UTF_8.encode("Test text").array())) {
241241
final SolidNonRDFSource testResource =
242-
new SolidNonRDFSource(resourceURI, Utils.PLAIN_TEXT, is, null);
242+
new SolidNonRDFSource(resourceURI, Utils.PLAIN_TEXT, is);
243243
assertDoesNotThrow(() -> client.create(testResource));
244244
} catch (IOException e) {
245245
LOGGER.warn("Could not create performance test resource " + resourceURI);

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

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,11 @@
2020
*/
2121
package com.inrupt.client.solid;
2222

23+
import com.inrupt.client.Headers;
24+
import com.inrupt.client.vocabulary.PIM;
25+
2326
import java.net.URI;
27+
import java.util.Arrays;
2428
import java.util.HashMap;
2529
import java.util.HashSet;
2630
import java.util.Map;
@@ -32,6 +36,9 @@
3236
*/
3337
public class Metadata {
3438

39+
private static final URI STORAGE = URI.create(PIM.getNamespace() + "Storage");
40+
private static final String CONTENT_TYPE = "Content-Type";
41+
3542
private final URI acl;
3643
private final URI storage;
3744
private final Set<URI> types = new HashSet<>();
@@ -294,5 +301,54 @@ public Metadata build() {
294301
// Prevent external instantiation
295302
}
296303
}
304+
305+
public static Metadata of(final URI identifier, final Headers headers) {
306+
// Gather metadata from HTTP headers
307+
final Metadata.Builder metadata = Metadata.newBuilder();
308+
headers.allValues("Link").stream()
309+
.flatMap(l -> Headers.Link.parse(l).stream())
310+
.forEach(link -> {
311+
if (link.getParameter("rel").contains("type")) {
312+
if ((link.getUri().equals(STORAGE))) {
313+
metadata.storage(identifier);
314+
}
315+
metadata.type(link.getUri());
316+
} else if (link.getParameter("rel").contains("acl")) {
317+
metadata.acl(link.getUri());
318+
} else if (link.getParameter("rel").contains(PIM.storage.toString())) {
319+
metadata.storage(link.getUri());
320+
}
321+
});
322+
323+
headers.allValues("WAC-Allow").stream()
324+
.map(Headers.WacAllow::parse)
325+
.map(Headers.WacAllow::getAccessParams)
326+
.flatMap(p -> p.entrySet().stream())
327+
.forEach(metadata::wacAllow);
328+
329+
headers.allValues("Allow").stream()
330+
.flatMap(s -> Arrays.stream(s.split(",")))
331+
.map(String::trim)
332+
.forEach(metadata::allowedMethod);
333+
334+
headers.allValues("Accept-Post").stream()
335+
.flatMap(s -> Arrays.stream(s.split(",")))
336+
.map(String::trim)
337+
.forEach(metadata::allowedPostSyntax);
338+
339+
headers.allValues("Accept-Patch").stream()
340+
.flatMap(s -> Arrays.stream(s.split(",")))
341+
.map(String::trim)
342+
.forEach(metadata::allowedPatchSyntax);
343+
344+
headers.allValues("Accept-Put").stream()
345+
.flatMap(s -> Arrays.stream(s.split(",")))
346+
.map(String::trim)
347+
.forEach(metadata::allowedPutSyntax);
348+
349+
metadata.contentType(headers.firstValue(CONTENT_TYPE).orElse("application/octet-stream"));
350+
351+
return metadata.build();
352+
}
297353
}
298354

0 commit comments

Comments
 (0)