Skip to content

Commit 74dd715

Browse files
committed
JCL-474: Introduce filter API
1 parent a2a5eb1 commit 74dd715

File tree

5 files changed

+786
-4
lines changed

5 files changed

+786
-4
lines changed

access-grant/src/main/java/com/inrupt/client/accessgrant/AccessGrantClient.java

Lines changed: 108 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import com.inrupt.client.Client;
2626
import com.inrupt.client.ClientCache;
2727
import com.inrupt.client.ClientProvider;
28+
import com.inrupt.client.Headers;
2829
import com.inrupt.client.Request;
2930
import com.inrupt.client.Response;
3031
import com.inrupt.client.auth.Session;
@@ -347,6 +348,110 @@ public CompletionStage<AccessCredentialVerification> verify(final AccessCredenti
347348
});
348349
}
349350

351+
/**
352+
* Perform an Access Credentials query and return a page of access credentials.
353+
*
354+
* @param <T> the credential type
355+
* @param filter the query filter
356+
* @return the page of query results
357+
*/
358+
public <T extends AccessCredential> CompletionStage<CredentialResult<T>> query(final CredentialFilter<T> filter) {
359+
final Class<T> clazz = filter.getCredentialType();
360+
final Set<String> supportedTypes;
361+
if (AccessGrant.class.isAssignableFrom(clazz)) {
362+
supportedTypes = ACCESS_GRANT_TYPES;
363+
} else if (AccessRequest.class.isAssignableFrom(clazz)) {
364+
supportedTypes = ACCESS_REQUEST_TYPES;
365+
} else if (AccessDenial.class.isAssignableFrom(clazz)) {
366+
supportedTypes = ACCESS_DENIAL_TYPES;
367+
} else {
368+
throw new AccessGrantException("Unsupported type " + clazz + " in query request");
369+
}
370+
371+
return v1Metadata().thenCompose(metadata -> {
372+
// TODO check that query endpoint is nonnull
373+
final Request req = Request.newBuilder(filter.asURI(metadata.queryEndpoint)).GET().build();
374+
return client.send(req, Response.BodyHandlers.ofInputStream()).thenApply(response -> {
375+
try (final InputStream input = response.body()) {
376+
if (isSuccess(response.statusCode())) {
377+
final Map<String, CredentialFilter<T>> links = processFilterResponseHeaders(response.headers(),
378+
filter);
379+
final List<T> items = processFilterResponseBody(input, supportedTypes, clazz);
380+
return new CredentialResult<>(items, links.get("first"), links.get("prev"),
381+
links.get("next"), links.get("last"));
382+
} else {
383+
throw new AccessGrantException("Error querying access grant: HTTP response " +
384+
response.statusCode());
385+
}
386+
} catch (final IOException ex) {
387+
throw new AccessGrantException(
388+
"Unexpected I/O exception while processing Access Grant query", ex);
389+
}
390+
});
391+
});
392+
}
393+
394+
<T extends AccessCredential> Map<String, CredentialFilter<T>> processFilterResponseHeaders(final Headers headers,
395+
final CredentialFilter<T> filter) {
396+
final Map<String, CredentialFilter<T>> links = new HashMap<>();
397+
final List<String> linkHeaders = headers.allValues("Link");
398+
if (!linkHeaders.isEmpty()) {
399+
Headers.Link.parse(linkHeaders.toArray(linkHeaders.toArray(new String[0])))
400+
.forEach(link -> {
401+
final String rel = link.getParameter("rel");
402+
final URI uri = link.getUri();
403+
if (rel != null && uri != null) {
404+
final String page = getPageQueryParam(uri);
405+
links.put(rel, CredentialFilter.newBuilder(filter).page(page)
406+
.build(filter.getCredentialType()));
407+
}
408+
});
409+
}
410+
return links;
411+
}
412+
413+
static String getPageQueryParam(final URI uri) {
414+
final String params = uri.getQuery();
415+
if (params != null) {
416+
for (final String param : params.split("&")) {
417+
final String parts[] = param.split("=", 2);
418+
if (parts.length == 2 && "page".equals(parts[0])) {
419+
return parts[1];
420+
}
421+
}
422+
}
423+
return null;
424+
}
425+
426+
@SuppressWarnings("unchecked")
427+
<T extends AccessCredential> List<T> processFilterResponseBody(final InputStream input,
428+
final Set<String> validTypes, final Class<T> clazz) throws IOException {
429+
430+
final List<T> items = new ArrayList<>();
431+
final List<Object> data = jsonService.fromJson(input,
432+
new ArrayList<Object>(){}.getClass().getGenericSuperclass());
433+
for (final Object item : data) {
434+
Utils.asMap(item).ifPresent(credential ->
435+
Utils.asSet(credential.get(TYPE)).ifPresent(types -> {
436+
types.retainAll(validTypes);
437+
if (!types.isEmpty()) {
438+
final Map<String, Object> presentation = new HashMap<>();
439+
presentation.put(CONTEXT, Arrays.asList(VC_CONTEXT_URI));
440+
presentation.put(TYPE, Arrays.asList("VerifiablePresentation"));
441+
presentation.put(VERIFIABLE_CREDENTIAL, Arrays.asList(credential));
442+
if (AccessGrant.class.equals(clazz)) {
443+
items.add((T) AccessGrant.of(new String(serialize(presentation), UTF_8)));
444+
} else if (AccessRequest.class.equals(clazz)) {
445+
items.add((T) AccessRequest.of(new String(serialize(presentation), UTF_8)));
446+
} else if (AccessDenial.class.equals(clazz)) {
447+
items.add((T) AccessDenial.of(new String(serialize(presentation), UTF_8)));
448+
}
449+
}
450+
}));
451+
}
452+
return items;
453+
}
454+
350455
/**
351456
* Perform an Access Credentials query and returns 0 to N matching access credentials.
352457
*
@@ -404,7 +509,7 @@ private <T extends AccessCredential> CompletionStage<List<T>> query(final URI re
404509
final List<T> responses = new ArrayList<>();
405510
for (final Map<String, Object> data :
406511
buildQuery(config.getIssuer(), type, resource, creator, recipient, purposes, modes)) {
407-
final Request req = Request.newBuilder(metadata.queryEndpoint)
512+
final Request req = Request.newBuilder(metadata.deriveEndpoint)
408513
.header(CONTENT_TYPE, APPLICATION_JSON)
409514
.POST(Request.BodyPublishers.ofByteArray(serialize(data))).build();
410515
final Response<InputStream> response = client.send(req, Response.BodyHandlers.ofInputStream())
@@ -572,7 +677,8 @@ CompletionStage<Metadata> v1Metadata() {
572677
})
573678
.thenApply(metadata -> {
574679
final Metadata m = new Metadata();
575-
m.queryEndpoint = asUri(metadata.get("derivationService"));
680+
m.deriveEndpoint = asUri(metadata.get("derivationService"));
681+
m.queryEndpoint = asUri(metadata.get("queryService"));
576682
m.issueEndpoint = asUri(metadata.get("issuerService"));
577683
m.verifyEndpoint = asUri(metadata.get("verifierService"));
578684
m.statusEndpoint = asUri(metadata.get("statusService"));

0 commit comments

Comments
 (0)