5555import java .util .Set ;
5656import java .util .concurrent .CompletableFuture ;
5757import java .util .concurrent .CompletionStage ;
58+ import java .util .function .Function ;
5859
5960import org .slf4j .Logger ;
6061import org .slf4j .LoggerFactory ;
@@ -105,6 +106,7 @@ public class AccessGrantClient {
105106 private static final String IS_PROVIDED_TO = "isProvidedTo" ;
106107 private static final String IS_CONSENT_FOR_DATA_SUBJECT = "isConsentForDataSubject" ;
107108 private static final String FOR_PERSONAL_DATA = "forPersonalData" ;
109+ private static final String TEMPLATE = "template" ;
108110 private static final String HAS_STATUS = "hasStatus" ;
109111 private static final String REQUEST = "request" ;
110112 private static final String VERIFIED_REQUEST = "verifiedRequest" ;
@@ -183,7 +185,10 @@ private AccessGrantClient(final Client client, final ClientCache<URI, Metadata>
183185 this .config = Objects .requireNonNull (config , "config may not be null!" );
184186 this .metadataCache = Objects .requireNonNull (metadataCache , "metadataCache may not be null!" );
185187 this .jsonService = ServiceProvider .getJsonService ();
186- LOGGER .debug ("Initializing Access Grant client with issuer: {}" , config .getIssuer ());
188+ LOGGER .atDebug ()
189+ .setMessage ("Initializing Access Grant client with issuer: {}" )
190+ .addArgument (config ::getIssuer )
191+ .log ();
187192 }
188193
189194 /**
@@ -204,7 +209,7 @@ public AccessGrantClient session(final Session session) {
204209 * @return the next stage of completion containing the resulting access request
205210 */
206211 public CompletionStage <AccessRequest > requestAccess (final AccessRequest .RequestParameters request ) {
207- return requestAccess (request .getRecipient (), request .getResources (),
212+ return requestAccess (request .getRecipient (), request .getResources (), request . getTemplates (),
208213 request .getModes (), request .getPurposes (), request .getExpiration (), request .getIssuedAt ());
209214 }
210215
@@ -220,16 +225,23 @@ public CompletionStage<AccessRequest> requestAccess(final AccessRequest.RequestP
220225 */
221226 public CompletionStage <AccessRequest > requestAccess (final URI recipient , final Set <URI > resources ,
222227 final Set <String > modes , final Set <URI > purposes , final Instant expiration ) {
223- return requestAccess (recipient , resources , modes , purposes , expiration , null );
228+ return requestAccess (recipient , resources , Collections . emptySet (), modes , purposes , expiration , null );
224229 }
225230
226231 private CompletionStage <AccessRequest > requestAccess (final URI recipient , final Set <URI > resources ,
227- final Set <String > modes , final Set <URI > purposes , final Instant expiration , final Instant issuance ) {
232+ final Set <String > templates , final Set <String > modes , final Set <URI > purposes , final Instant expiration ,
233+ final Instant issuance ) {
228234 Objects .requireNonNull (resources , "Resources may not be null!" );
235+ Objects .requireNonNull (templates , "Templates may not be null!" );
229236 Objects .requireNonNull (modes , "Access modes may not be null!" );
237+ if (templates .isEmpty () && resources .isEmpty ()) {
238+ LOGGER .warn ("Both resources and templates are empty in access request" );
239+ } else if (!templates .isEmpty () && !resources .isEmpty ()) {
240+ LOGGER .warn ("Both resources and templates are non-empty in access request" );
241+ }
230242 return v1Metadata ().thenCompose (metadata -> {
231- final Map <String , Object > data = buildAccessRequestv1 (recipient , resources , modes , purposes , expiration ,
232- issuance );
243+ final Map <String , Object > data = buildAccessRequestv1 (recipient , resources , templates , modes , purposes ,
244+ expiration , issuance );
233245
234246 final Request req = Request .newBuilder (metadata .issueEndpoint )
235247 .header (CONTENT_TYPE , APPLICATION_JSON )
@@ -253,26 +265,79 @@ private CompletionStage<AccessRequest> requestAccess(final URI recipient, final
253265 }
254266
255267 /**
256- * Issue an access grant based on an access request. The access request is not verified.
268+ * Issue an access grant based on an access request.
269+ *
270+ * <p>
271+ * The access request is not verified.
272+ * Any templated URLs are ignored.
257273 *
258274 * @param request the access request
259275 * @return the next stage of completion containing the issued access grant
260276 */
261277 public CompletionStage <AccessGrant > grantAccess (final AccessRequest request ) {
262- return grantAccess (request , false );
278+ return grantAccess (request , templates -> Collections .emptySet (), false );
279+ }
280+
281+ /**
282+ * Issue an access grant based on an access request.
283+ *
284+ * <p>
285+ * The access request is verified.
286+ * Any templated URLs are processed according to the provided mapping function.
287+ *
288+ * @param request the access request
289+ * @param mapping a mapping function for template URLs
290+ * @return the next stage of completion containing the issued access grant
291+ */
292+ public CompletionStage <AccessGrant > grantAccess (final AccessRequest request ,
293+ final Function <Set <String >, Set <URI >> mapping ) {
294+ return grantAccess (request , mapping , true );
263295 }
264296
265297 /**
266298 * Issue an access grant based on an access request.
267299 *
300+ * <p>
301+ * Any templated URLs are ignored.
302+ *
268303 * @param request the access request
269304 * @param verifyRequest whether the request should be verified before issuing the access grant
270305 * @return the next stage of completion containing the issued access grant
271306 */
272307 public CompletionStage <AccessGrant > grantAccess (final AccessRequest request , final boolean verifyRequest ) {
308+ return grantAccess (request , templates -> Collections .emptySet (), verifyRequest );
309+ }
310+
311+ /**
312+ * Issue an access grant based on an access request.
313+ *
314+ * @param request the access request
315+ * @param mapping a mapping function for template URLs
316+ * @param verifyRequest whether the request should be verified before issuing the access grant
317+ * @return the next stage of completion containing the issued access grant
318+ */
319+ public CompletionStage <AccessGrant > grantAccess (final AccessRequest request ,
320+ final Function <Set <String >, Set <URI >> mapping , final boolean verifyRequest ) {
273321 Objects .requireNonNull (request , "Request may not be null!" );
322+ final var templated = mapping .apply (request .getTemplates ());
323+ if (templated .size () != request .getTemplates ().size ()) {
324+ LOGGER .atDebug ()
325+ .setMessage ("Unexpected number of mapped template values, found ({}) expected ({})" )
326+ .addArgument (templated ::size )
327+ .addArgument (() -> request .getTemplates ().size ())
328+ .log ();
329+ }
330+ final var resources = new HashSet <URI >(request .getResources ());
331+ resources .addAll (templated );
332+ if (resources .isEmpty ()) {
333+ LOGGER .atWarn ()
334+ .setMessage ("No data URLs supplied: {} resource URLs and {} mapped templates" )
335+ .addArgument (() -> request .getResources ().size ())
336+ .addArgument (templated ::size )
337+ .log ();
338+ }
274339 return v1Metadata ().thenCompose (metadata -> {
275- final Map <String , Object > data = buildAccessGrantv1 (request .getCreator (), request . getResources () ,
340+ final Map <String , Object > data = buildAccessGrantv1 (request .getCreator (), resources ,
276341 request .getModes (), request .getPurposes (), request .getExpiration (), request .getIssuedAt (),
277342 request .getIdentifier (), verifyRequest );
278343 final Request req = Request .newBuilder (metadata .issueEndpoint )
@@ -815,12 +880,17 @@ static Map<String, Object> buildAccessGrantv1(
815880 return data ;
816881 }
817882
818- static Map <String , Object > buildAccessRequestv1 (final URI agent , final Set <URI > resources , final Set <String > modes ,
819- final Set <URI > purposes , final Instant expiration , final Instant issuance ) {
883+ static Map <String , Object > buildAccessRequestv1 (final URI agent , final Set <URI > resources ,
884+ final Set <String > templates , final Set <String > modes , final Set <URI > purposes ,
885+ final Instant expiration , final Instant issuance ) {
820886 final Map <String , Object > consent = new HashMap <>();
821887 consent .put (HAS_STATUS , "https://w3id.org/GConsent#ConsentStatusRequested" );
822888 consent .put (MODE , modes );
823- consent .put (FOR_PERSONAL_DATA , resources );
889+ if (!resources .isEmpty ()) {
890+ consent .put (FOR_PERSONAL_DATA , resources );
891+ } else {
892+ consent .put (TEMPLATE , templates );
893+ }
824894 if (agent != null ) {
825895 consent .put (IS_CONSENT_FOR_DATA_SUBJECT , agent );
826896 }
0 commit comments