001/*
002 * Licensed to DuraSpace under one or more contributor license agreements.
003 * See the NOTICE file distributed with this work for additional information
004 * regarding copyright ownership.
005 *
006 * DuraSpace licenses this file to you under the Apache License,
007 * Version 2.0 (the "License"); you may not use this file except in
008 * compliance with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.fcrepo.event.serialization;
019
020import static org.apache.jena.rdf.model.ModelFactory.createDefaultModel;
021import static org.apache.jena.rdf.model.ResourceFactory.createProperty;
022import static org.apache.jena.rdf.model.ResourceFactory.createResource;
023import static org.apache.jena.vocabulary.DCTerms.identifier;
024import static org.apache.jena.vocabulary.DCTerms.isPartOf;
025import static org.apache.jena.vocabulary.RDF.type;
026import static java.nio.charset.StandardCharsets.UTF_8;
027import static java.time.Instant.ofEpochMilli;
028import static org.fcrepo.kernel.api.RdfLexicon.EVENT_NAMESPACE;
029import static org.fcrepo.kernel.api.RdfLexicon.PROV_NAMESPACE;
030import static org.fcrepo.kernel.api.RdfLexicon.REPOSITORY_NAMESPACE;
031import static org.fcrepo.kernel.api.observer.OptionalValues.BASE_URL;
032import static org.fcrepo.kernel.api.observer.OptionalValues.USER_AGENT;
033import static org.junit.Assert.assertEquals;
034import static org.junit.Assert.assertTrue;
035import static org.mockito.Mockito.when;
036
037import java.io.ByteArrayInputStream;
038import java.io.IOException;
039import java.time.Instant;
040import java.util.ArrayList;
041import java.util.HashMap;
042import java.util.HashSet;
043import java.util.List;
044import java.util.Map;
045import java.util.Set;
046import java.util.concurrent.atomic.AtomicInteger;
047
048import com.fasterxml.jackson.databind.JsonNode;
049import com.fasterxml.jackson.databind.ObjectMapper;
050import org.apache.jena.rdf.model.Model;
051import org.apache.jena.rdf.model.Resource;
052import org.apache.jena.rdf.model.SimpleSelector;
053import org.fcrepo.kernel.api.observer.EventType;
054import org.fcrepo.kernel.api.observer.FedoraEvent;
055import org.junit.Before;
056import org.junit.Test;
057import org.junit.runner.RunWith;
058import org.mockito.Mock;
059import org.mockito.runners.MockitoJUnitRunner;
060
061/**
062 * <p>FedoraEventTest class.</p>
063 *
064 * @author acoburn
065 */
066@RunWith(MockitoJUnitRunner.class)
067public class JsonLdEventTest {
068
069    private static String FOAF_NAMESPACE = "http://xmlns.com/foaf/0.1/";
070
071    @Mock
072    private FedoraEvent mockEvent;
073
074    private String baseUrl = "http://localhost:8080/fcrepo/rest";
075
076    private String path = "/path/to/resource";
077
078    private Instant timestamp = ofEpochMilli(1465919304000L);
079
080    @Before
081    public void setUp() {
082        final Set<EventType> typeSet = new HashSet<>();
083        typeSet.add(EventType.RESOURCE_MODIFICATION);
084        final Set<String> resourceTypeSet = new HashSet<>();
085        resourceTypeSet.add(REPOSITORY_NAMESPACE + "Resource");
086        resourceTypeSet.add(REPOSITORY_NAMESPACE + "Container");
087        resourceTypeSet.add("http://example.com/SampleType");
088        final Map<String, String> auxInfo = new HashMap<>();
089        auxInfo.put(BASE_URL, baseUrl);
090        auxInfo.put(USER_AGENT, "fcrepo-client/1.0");
091        when(mockEvent.getTypes()).thenReturn(typeSet);
092        when(mockEvent.getResourceTypes()).thenReturn(resourceTypeSet);
093        when(mockEvent.getPath()).thenReturn(path);
094        when(mockEvent.getUserID()).thenReturn("fedo raadmin");
095        when(mockEvent.getDate()).thenReturn(timestamp);
096        when(mockEvent.getEventID()).thenReturn("urn:uuid:some-event");
097        when(mockEvent.getInfo()).thenReturn(auxInfo);
098    }
099
100    @Test
101    public void testJsonSerializationAsModel() {
102        final EventSerializer serializer = new JsonLDSerializer();
103        final String json = serializer.serialize(mockEvent);
104        final Model model = createDefaultModel();
105        model.read(new ByteArrayInputStream(json.getBytes(UTF_8)), baseUrl + path, "JSON-LD");
106
107        final Resource subject = createResource(baseUrl + path);
108        final Resource blankNode = null;
109
110        assertTrue(model.contains(subject, type, createResource(REPOSITORY_NAMESPACE + "Resource")));
111        assertTrue(model.contains(subject, type, createResource(REPOSITORY_NAMESPACE + "Container")));
112        assertTrue(model.contains(subject, type, createResource(PROV_NAMESPACE + "Entity")));
113        assertTrue(model.contains(subject, type, createResource("http://example.com/SampleType")));
114        assertTrue(model.contains(subject, isPartOf, createResource(baseUrl)));
115        assertTrue(model.contains(subject, createProperty(PROV_NAMESPACE + "wasGeneratedBy")));
116        assertTrue(model.contains(subject, createProperty(PROV_NAMESPACE + "wasAttributedTo")));
117
118        final AtomicInteger activities = new AtomicInteger();
119        model.listStatements(new SimpleSelector(subject, createProperty(PROV_NAMESPACE + "wasGeneratedBy"), blankNode))
120            .forEachRemaining(statement -> {
121                final Resource r = statement.getResource();
122                assertTrue(r.hasProperty(type, createResource(EVENT_NAMESPACE + "ResourceModification")));
123                assertTrue(r.hasProperty(type, createResource(PROV_NAMESPACE + "Activity")));
124                assertTrue(r.hasProperty(identifier, createResource("urn:uuid:some-event")));
125                activities.incrementAndGet();
126            });
127        assertEquals(activities.get(), 1);
128
129        final AtomicInteger agents = new AtomicInteger();
130        model.listStatements(new SimpleSelector(subject, createProperty(PROV_NAMESPACE + "wasAttributedTo"), blankNode))
131            .forEachRemaining(statement -> {
132                final Resource r = statement.getResource();
133                if (r.hasProperty(type, createResource(PROV_NAMESPACE + "Person"))) {
134                    assertTrue(r.hasProperty(type, createResource(PROV_NAMESPACE + "Person")));
135                    assertTrue(r.hasProperty(createProperty(FOAF_NAMESPACE + "name"), "fedo raadmin"));
136                } else {
137                    assertTrue(r.hasProperty(type, createResource(PROV_NAMESPACE + "SoftwareAgent")));
138                    assertTrue(r.hasProperty(createProperty(FOAF_NAMESPACE + "name"), "fcrepo-client/1.0"));
139                }
140                agents.incrementAndGet();
141            });
142        assertEquals(agents.get(), 2);
143        assertEquals(1, 1);
144    }
145
146    @Test
147    public void testJsonSerializationAsJson() throws IOException {
148        final EventSerializer serializer = new JsonLDSerializer();
149        final String json = serializer.serialize(mockEvent);
150
151        final ObjectMapper mapper = new ObjectMapper();
152        final JsonNode node = mapper.readTree(json);
153        assertTrue(node.has("@context"));
154        assertTrue(node.has("id"));
155        assertEquals(node.get("id").textValue(), baseUrl + path);
156        final List<String> types = new ArrayList<>();
157        node.get("type").elements().forEachRemaining(n -> {
158            types.add(n.textValue());
159        });
160        assertEquals(types.size(), 4);
161        assertTrue(types.contains(REPOSITORY_NAMESPACE + "Resource"));
162        assertTrue(types.contains(REPOSITORY_NAMESPACE + "Container"));
163        assertTrue(types.contains(PROV_NAMESPACE + "Entity"));
164        assertTrue(types.contains("http://example.com/SampleType"));
165        assertTrue(node.has("isPartOf"));
166        assertEquals(node.get("isPartOf").textValue(), baseUrl);
167
168        // verify prov:Activity node
169        assertTrue(node.has("wasGeneratedBy"));
170        final JsonNode activity = node.get("wasGeneratedBy");
171        final List<String> activityTypes = new ArrayList<>();
172        assertTrue(activity.has("type"));
173        activity.get("type").elements().forEachRemaining(n -> {
174            activityTypes.add(n.textValue());
175        });
176        assertEquals(activityTypes.size(), 2);
177        assertTrue(activityTypes.contains(EVENT_NAMESPACE + "ResourceModification"));
178        assertTrue(activityTypes.contains(PROV_NAMESPACE + "Activity"));
179        assertTrue(activity.has("atTime"));
180        assertTrue(activity.has("identifier"));
181        assertEquals(activity.get("atTime").textValue(), timestamp.toString());
182        assertEquals(activity.get("identifier").textValue(), "urn:uuid:some-event");
183
184        // verify prov:Agent node
185        assertTrue(node.has("wasAttributedTo"));
186        final AtomicInteger agents = new AtomicInteger();
187        node.get("wasAttributedTo").elements().forEachRemaining(n -> {
188            assertTrue(n.has("type"));
189            assertTrue(n.has("name"));
190            if (n.get("type").textValue().equals(PROV_NAMESPACE + "Person")) {
191                assertEquals(n.get("type").textValue(), PROV_NAMESPACE + "Person");
192                assertEquals(n.get("name").textValue(), "fedo raadmin");
193            } else {
194                assertEquals(n.get("type").textValue(), PROV_NAMESPACE + "SoftwareAgent");
195                assertEquals(n.get("name").textValue(), "fcrepo-client/1.0");
196            }
197            agents.incrementAndGet();
198        });
199        assertEquals(agents.get(), 2);
200    }
201}