From cc0901f6bd6a544bfa1366cb68c1b58d13837fbd Mon Sep 17 00:00:00 2001 From: Aaron Coburn Date: Tue, 29 Sep 2020 15:31:58 -0400 Subject: [PATCH] Make Metadata class more extensible (#1085) Resolves #1084 --- .../java/org/trellisldp/api/Metadata.java | 55 ++++++++++++++++++- .../java/org/trellisldp/api/MetadataTest.java | 6 ++ .../http/impl/MutatingLdpHandler.java | 3 +- 3 files changed, 60 insertions(+), 4 deletions(-) diff --git a/core/api/src/main/java/org/trellisldp/api/Metadata.java b/core/api/src/main/java/org/trellisldp/api/Metadata.java index af00616d0..24cbb94ff 100644 --- a/core/api/src/main/java/org/trellisldp/api/Metadata.java +++ b/core/api/src/main/java/org/trellisldp/api/Metadata.java @@ -16,9 +16,12 @@ package org.trellisldp.api; import static java.util.Collections.emptySet; +import static java.util.Collections.unmodifiableMap; import static java.util.Objects.requireNonNull; import static java.util.Optional.ofNullable; +import java.util.HashMap; +import java.util.Map; import java.util.Optional; import java.util.Set; @@ -36,9 +39,11 @@ public final class Metadata { private final IRI membershipResource; private final IRI memberOfRelation; private final IRI insertedContentRelation; + private final IRI agent; private final BinaryMetadata binary; private final Set graphNames; private final String revision; + private final Map properties; /** * A Metadata-bearing data structure for use with resource manipulation. @@ -50,16 +55,19 @@ public final class Metadata { * @param memberRelation an LDP hasMemberRelation predicate, may be {@code null} * @param memberOfRelation an LDP isMemberOfRelation predicate, may be {@code null} * @param insertedContentRelation an LDP insertedContentRelation, may be {@code null} + * @param agent the agent associated with the operation, may be {@code null} * @param binary metadata about a BinaryMetadata, may be {@code null} * @param revision a revision value, may be {@code null}. This value may be used by a * {@link ResourceService} implementation for additional concurrency control. * This value would typically be used in tandem with the {@link Resource#getRevision} * method. * @param graphNames a collection of metadata graphNames + * @param properties a collection of additional properties */ private Metadata(final IRI identifier, final IRI ixnModel, final IRI container, final IRI membershipResource, - final IRI memberRelation, final IRI memberOfRelation, final IRI insertedContentRelation, - final BinaryMetadata binary, final String revision, final Set graphNames) { + final IRI memberRelation, final IRI memberOfRelation, final IRI insertedContentRelation, final IRI agent, + final BinaryMetadata binary, final String revision, final Set graphNames, + final Map properties) { this.identifier = requireNonNull(identifier, "Identifier cannot be null!"); this.ixnModel = requireNonNull(ixnModel, "Interaction model cannot be null!"); this.container = container; @@ -67,9 +75,11 @@ private Metadata(final IRI identifier, final IRI ixnModel, final IRI container, this.memberRelation = memberRelation; this.memberOfRelation = memberOfRelation; this.insertedContentRelation = insertedContentRelation; + this.agent = agent; this.binary = binary; this.revision = revision; this.graphNames = graphNames; + this.properties = properties; } /** @@ -129,6 +139,14 @@ public Optional getContainer() { return ofNullable(container); } + /** + * Retrieve the agent associated with this opertation, if known. + * @return the agent identified with a WebID + */ + public Optional getAgent() { + return ofNullable(agent); + } + /** * Retrieve the membership resource if this is an LDP Direct or Indirect container. * @@ -194,6 +212,14 @@ public Optional getRevision() { return ofNullable(revision); } + /** + * Retrieve any additional properties. + * @return an immutable collection of properties + */ + public Map getProperties() { + return properties; + } + /** * A mutable builder for a {@link Metadata} object. */ @@ -205,9 +231,11 @@ public static final class Builder { private IRI membershipResource; private IRI memberOfRelation; private IRI insertedContentRelation; + private IRI agent; private BinaryMetadata binary; private String revision; private Set graphNames = emptySet(); + private Map properties = new HashMap<>(); /** * Create a Metadata builder with the provided identifier. @@ -277,6 +305,16 @@ public Builder insertedContentRelation(final IRI insertedContentRelation) { return this; } + /** + * Set the agent value. + * @param agent the agent associated with the operation + * @return this builder + */ + public Builder agent(final IRI agent) { + this.agent = agent; + return this; + } + /** * Set the binary metadata. * @param binary the binary metadata @@ -307,13 +345,24 @@ public Builder revision(final String revision) { return this; } + /** + * Set a property on the resource metadata. + * @param key the property key + * @param value the property value + * @return this builder + */ + public Builder property(final String key, final String value) { + this.properties.put(key, value); + return this; + } + /** * Build the Metadata object, transitioning this builder to the built state. * @return the built Metadata */ public Metadata build() { return new Metadata(identifier, ixnModel, container, membershipResource, memberRelation, memberOfRelation, - insertedContentRelation, binary, revision, graphNames); + insertedContentRelation, agent, binary, revision, graphNames, unmodifiableMap(properties)); } } } diff --git a/core/api/src/test/java/org/trellisldp/api/MetadataTest.java b/core/api/src/test/java/org/trellisldp/api/MetadataTest.java index 2381e63c3..06acea9bd 100644 --- a/core/api/src/test/java/org/trellisldp/api/MetadataTest.java +++ b/core/api/src/test/java/org/trellisldp/api/MetadataTest.java @@ -36,6 +36,7 @@ class MetadataTest { private static final IRI identifier = rdf.createIRI("trellis:data/resource"); private static final IRI member = rdf.createIRI("trellis:data/member"); private static final IRI root = rdf.createIRI("trellis:data/"); + private static final IRI agent = rdf.createIRI("https://example.com/agent#i"); @Test void testMetadataIndirectContainer() { @@ -44,6 +45,7 @@ void testMetadataIndirectContainer() { .container(root).memberRelation(LDP.member) .membershipResource(member) .insertedContentRelation(FOAF.primaryTopic) + .property("key", "value") .revision("blahblahblah").build(); assertEquals(identifier, metadata.getIdentifier()); assertEquals(LDP.IndirectContainer, metadata.getInteractionModel()); @@ -54,13 +56,16 @@ void testMetadataIndirectContainer() { assertFalse(metadata.getMemberOfRelation().isPresent()); assertTrue(metadata.getMetadataGraphNames().isEmpty()); assertFalse(metadata.getBinary().isPresent()); + assertFalse(metadata.getAgent().isPresent()); assertEquals(of("blahblahblah"), metadata.getRevision()); + assertEquals("value", metadata.getProperties().get("key")); } @Test void testMetadataDirectContainer() { final Metadata metadata = Metadata.builder(identifier) .interactionModel(LDP.DirectContainer) + .agent(agent) .container(root).memberOfRelation(DC.isPartOf) .membershipResource(member).metadataGraphNames(singleton(Trellis.PreferAccessControl)).build(); assertEquals(identifier, metadata.getIdentifier()); @@ -68,6 +73,7 @@ void testMetadataDirectContainer() { assertEquals(of(root), metadata.getContainer()); assertEquals(of(member), metadata.getMembershipResource()); assertEquals(of(DC.isPartOf), metadata.getMemberOfRelation()); + assertEquals(of(agent), metadata.getAgent()); assertFalse(metadata.getInsertedContentRelation().isPresent()); assertFalse(metadata.getMemberRelation().isPresent()); assertFalse(metadata.getBinary().isPresent()); diff --git a/core/http/src/main/java/org/trellisldp/http/impl/MutatingLdpHandler.java b/core/http/src/main/java/org/trellisldp/http/impl/MutatingLdpHandler.java index 508f45faf..80beae388 100644 --- a/core/http/src/main/java/org/trellisldp/http/impl/MutatingLdpHandler.java +++ b/core/http/src/main/java/org/trellisldp/http/impl/MutatingLdpHandler.java @@ -258,7 +258,8 @@ protected CompletionStage persistBinaryContent(final BinaryMetadata metada } protected Metadata.Builder metadataBuilder(final IRI identifier, final IRI ixnModel, final Dataset mutable) { - final Metadata.Builder builder = Metadata.builder(identifier).interactionModel(ixnModel); + final Metadata.Builder builder = Metadata.builder(identifier).interactionModel(ixnModel) + .agent(getSession().getAgent()); mutable.getGraph(Trellis.PreferUserManaged).ifPresent(graph -> { graph.stream(null, LDP.membershipResource, null) .filter(triple -> matchIdentifier(triple.getSubject(), identifier)).findFirst().map(Triple::getObject)