Add capability to record timestamp 95/71695/1
authorKajur, Harish (vk250x) <vk250x@att.com>
Thu, 1 Nov 2018 21:46:56 +0000 (17:46 -0400)
committerKajur, Harish (vk250x) <vk250x@att.com>
Thu, 1 Nov 2018 21:47:05 +0000 (17:47 -0400)
Issue-ID: AAI-1831
Change-Id: Ifcadf1caa0f7764c759997f10f3f789b1a702872
Signed-off-by: Kajur, Harish (vk250x) <vk250x@att.com>
aai-core/src/main/java/org/onap/aai/serialization/queryformats/Format.java
aai-core/src/main/java/org/onap/aai/serialization/queryformats/FormatFactory.java
aai-core/src/main/java/org/onap/aai/serialization/queryformats/ResourceWithSoT.java [new file with mode: 0644]
aai-core/src/test/java/org/onap/aai/serialization/queryformats/ResourceWithSoTTest.java [new file with mode: 0644]

index abd261e..7e7356f 100644 (file)
@@ -31,7 +31,8 @@ public enum Format {
        resource_and_url,
        console,
        raw,
-       count;
+       count,
+    resource_with_sot;
 
        public static Format getFormat(String format) throws AAIException {
                try {
index 29ae851..f36941f 100644 (file)
@@ -83,6 +83,9 @@ public class FormatFactory {
                        case count :
                                formatter = new Formatter(inject(new Count(), params));
                                break;
+            case resource_with_sot :
+                formatter = new Formatter(inject(new ResourceWithSoT.Builder(loader, serializer, urlBuilder), params).build());
+                break;
                        default :
                                break;
 
diff --git a/aai-core/src/main/java/org/onap/aai/serialization/queryformats/ResourceWithSoT.java b/aai-core/src/main/java/org/onap/aai/serialization/queryformats/ResourceWithSoT.java
new file mode 100644 (file)
index 0000000..503f3a5
--- /dev/null
@@ -0,0 +1,166 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.aai
+ * ================================================================================
+ * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.aai.serialization.queryformats;
+
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.onap.aai.db.props.AAIProperties;
+import org.onap.aai.introspection.Loader;
+import org.onap.aai.serialization.db.DBSerializer;
+import org.onap.aai.serialization.queryformats.exceptions.AAIFormatVertexException;
+import org.onap.aai.serialization.queryformats.params.Depth;
+import org.onap.aai.serialization.queryformats.params.NodesOnly;
+import org.onap.aai.serialization.queryformats.utils.UrlBuilder;
+import org.onap.aai.util.AAIConfig;
+
+import java.util.Optional;
+
+public class ResourceWithSoT extends MultiFormatMapper {
+    protected JsonParser parser = new JsonParser();
+    protected final DBSerializer serializer;
+    protected final Loader loader;
+    protected final UrlBuilder urlBuilder;
+    protected final int depth;
+    protected final boolean nodesOnly;
+    protected ResourceWithSoT(Builder builder) {
+        this.urlBuilder = builder.getUrlBuilder();
+        this.loader = builder.getLoader();
+        this.serializer = builder.getSerializer();
+        this.depth = builder.getDepth();
+        this.nodesOnly = builder.isNodesOnly();
+    }
+
+    @Override
+    public int parallelThreshold() {
+        return 100;
+    }
+
+    public static class Builder implements NodesOnly<Builder>, Depth<Builder> {
+
+        protected final Loader loader;
+        protected final DBSerializer serializer;
+        protected final UrlBuilder urlBuilder;
+        protected boolean includeUrl = false;
+        protected boolean nodesOnly = false;
+        protected int depth = 1;
+        protected boolean modelDriven = false;
+        public Builder(Loader loader, DBSerializer serializer, UrlBuilder urlBuilder) {
+            this.loader = loader;
+            this.serializer = serializer;
+            this.urlBuilder = urlBuilder;
+        }
+
+        protected Loader getLoader() {
+            return this.loader;
+        }
+
+        protected DBSerializer getSerializer() {
+            return this.serializer;
+        }
+
+        protected UrlBuilder getUrlBuilder() {
+            return this.urlBuilder;
+        }
+
+        public Builder includeUrl() {
+            this.includeUrl = true;
+            return this;
+        }
+
+        public Builder nodesOnly(Boolean nodesOnly) {
+            this.nodesOnly = nodesOnly;
+            return this;
+        }
+        public boolean isNodesOnly() {
+            return this.nodesOnly;
+        }
+
+        public Builder depth(Integer depth) {
+            this.depth = depth;
+            return this;
+        }
+
+        public int getDepth() {
+            return this.depth;
+        }
+
+        public boolean isIncludeUrl() {
+            return this.includeUrl;
+        }
+
+        public Builder modelDriven() {
+            this.modelDriven = true;
+            return this;
+        }
+
+        public boolean getModelDriven() {
+            return this.modelDriven;
+        }
+        public ResourceWithSoT build() {
+            return new ResourceWithSoT(this);
+        }
+    }
+
+    /**
+     *
+     * Returns an Optional<JsonObject> to convert the contents from the given Vertex object into a JsonObject.
+     * The fields returned are to record the time stamp of the creation/modification of the object, the user responsible for
+     * the change, and the last http method performed on the object.
+     *
+     * @param v
+     * @return
+     * @throws AAIFormatVertexException
+     */
+    @Override
+    protected Optional<JsonObject> getJsonFromVertex(Vertex v) throws AAIFormatVertexException {
+        // Null check
+        if (v == null)
+            return null;
+
+        JsonObject json = new JsonObject();
+
+        Object createdTimestampObj = v.property(AAIProperties.CREATED_TS).value();
+        Object lastModifiedTimestampObj = v.property(AAIProperties.LAST_MOD_TS).value();
+        Object sotObj = v.property(AAIProperties.SOURCE_OF_TRUTH).value();
+        Object lastModSotObj = v.property(AAIProperties.LAST_MOD_SOURCE_OF_TRUTH).value();
+        long createdTimestamp = Long.valueOf(createdTimestampObj.toString());
+        long lastModifiedTimestamp = Long.valueOf(lastModifiedTimestampObj.toString());
+        long threshold = Long.valueOf(AAIConfig.get("aai.resource.format.threshold", "10"));
+
+        // Add to the property field of the JSON payload
+        json.addProperty("aai-created-ts", createdTimestampObj.toString());
+        json.addProperty("aai-last-mod-ts", lastModifiedTimestampObj.toString());
+        json.addProperty("source-of-truth", sotObj.toString());
+        json.addProperty("last-mod-source-of-truth", lastModSotObj.toString());
+
+        // Check if the timestamp difference between creation and last modification are greater than a certain threshold, and if the source of truth differs
+        // If the timestamp difference is marginal and the SoT (creator/modifier) is the same, the last action performed is likely to be a creation.
+        long timestampDiff = lastModifiedTimestamp - createdTimestamp;
+        boolean isSameSoT = sotObj.toString().equals(lastModSotObj.toString());
+
+        if (timestampDiff <= threshold && isSameSoT)
+            json.addProperty("last-action-performed", "Created");
+        else
+            json.addProperty("last-action-performed", "Modified");
+
+        return Optional.of(json);
+    }
+}
diff --git a/aai-core/src/test/java/org/onap/aai/serialization/queryformats/ResourceWithSoTTest.java b/aai-core/src/test/java/org/onap/aai/serialization/queryformats/ResourceWithSoTTest.java
new file mode 100644 (file)
index 0000000..ad28e69
--- /dev/null
@@ -0,0 +1,181 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.aai
+ * ================================================================================
+ * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.aai.serialization.queryformats;
+
+import com.google.gson.JsonObject;
+import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
+import org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.ReadOnlyStrategy;
+import org.apache.tinkerpop.gremlin.structure.Graph;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerGraph;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.onap.aai.AAISetup;
+import org.onap.aai.dbmap.DBConnectionType;
+import org.onap.aai.exceptions.AAIException;
+import org.onap.aai.introspection.Loader;
+import org.onap.aai.introspection.ModelType;
+import org.onap.aai.serialization.db.DBSerializer;
+import org.onap.aai.serialization.engines.JanusGraphDBEngine;
+import org.onap.aai.serialization.engines.QueryStyle;
+import org.onap.aai.serialization.engines.TransactionalGraphEngine;
+import org.onap.aai.serialization.queryformats.exceptions.AAIFormatVertexException;
+import org.onap.aai.serialization.queryformats.utils.UrlBuilder;
+import org.onap.aai.setup.SchemaVersion;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+public class ResourceWithSoTTest extends AAISetup {
+    @Mock
+    private UrlBuilder urlBuilder;
+
+    private Graph graph;
+    private Vertex putVertex;
+    private Vertex patchVertex1;
+    private Vertex patchVertex2;
+
+    private JsonObject jsonPutObj = new JsonObject() ;
+    private JsonObject jsonPatchObj1 = new JsonObject() ;
+    private JsonObject jsonPatchObj2 = new JsonObject() ;
+
+    private SchemaVersion version;
+    private ResourceWithSoT resourceWithSoT;
+
+    private TransactionalGraphEngine dbEngine;
+    private Loader loader;
+    private DBSerializer serializer;
+    private final ModelType factoryType = ModelType.MOXY;
+
+    @Before
+    public void setUp() throws Exception {
+
+        version = schemaVersions.getDefaultVersion();
+        MockitoAnnotations.initMocks(this);
+
+        graph = TinkerGraph.open();
+
+        Long currentTimeMs = System.currentTimeMillis();
+        String timeNowInMs = Long.toString(currentTimeMs);
+
+        // PUT / CREATE
+        jsonPutObj.addProperty("aai-created-ts", timeNowInMs);
+        jsonPutObj.addProperty("aai-last-mod-ts", timeNowInMs);
+        jsonPutObj.addProperty("source-of-truth", "user_a");
+        jsonPutObj.addProperty("last-mod-source-of-truth", "user_a");
+        jsonPutObj.addProperty("last-action-performed", "Created");
+
+        putVertex = graph.addVertex(
+            "aai-created-ts", timeNowInMs,
+            "aai-last-mod-ts", timeNowInMs,
+            "source-of-truth", "user_a",
+            "last-mod-source-of-truth", "user_a"
+        );
+
+        // PATCH / MODIFY with differing source of truths
+        jsonPatchObj1.addProperty("aai-created-ts", timeNowInMs);
+        jsonPatchObj1.addProperty("aai-last-mod-ts", timeNowInMs);
+        jsonPatchObj1.addProperty("source-of-truth", "user_a");
+        jsonPatchObj1.addProperty("last-mod-source-of-truth", "user_b");
+        jsonPatchObj1.addProperty("last-action-performed", "Modified");
+
+        patchVertex1 = graph.addVertex(
+            "aai-created-ts", timeNowInMs,
+            "aai-last-mod-ts", timeNowInMs,
+            "source-of-truth", "user_a",
+            "last-mod-source-of-truth", "user_b"
+        );
+
+        // PATCH / MODIFY with differing time stamps
+        jsonPatchObj2.addProperty("aai-created-ts", timeNowInMs);
+        jsonPatchObj2.addProperty("aai-last-mod-ts", Long.toString(currentTimeMs + 1000));
+        jsonPatchObj2.addProperty("source-of-truth", "user_a");
+        jsonPatchObj2.addProperty("last-mod-source-of-truth", "user_a");
+        jsonPatchObj2.addProperty("last-action-performed", "Modified");
+
+        patchVertex2 = graph.addVertex(
+            "aai-created-ts", timeNowInMs,
+            "aai-last-mod-ts", Long.toString(currentTimeMs + 1000),
+            "source-of-truth", "user_a",
+            "last-mod-source-of-truth", "user_a"
+        );
+
+        graph = TinkerGraph.open();
+        createLoaderEngineSetup();
+    }
+
+    // This test is to simulate a PUT request
+    @Test
+    public void testGetJsonFromVertexWithCreateVertex() throws AAIFormatVertexException, AAIException {
+        if (putVertex == null)
+            assertTrue("The vertex used for this test is null. Fail immediately.", false);
+
+        JsonObject json = resourceWithSoT.getJsonFromVertex(putVertex).get();
+        assertEquals(jsonPutObj, json);
+    }
+
+    // This test is to simulate PATCH requests
+    @Test
+    public void testGetJsonFromVertexWithModifyVertex() throws AAIFormatVertexException, AAIException {
+        if (patchVertex1 == null)
+            assertTrue("The vertex 1 used for this test is null. Fail immediately.", false);
+        if (patchVertex2 == null)
+            assertTrue("The vertex 2 used for this test is null. Fail immediately.", false);
+
+        // Differing Source of Truths will indicate that the action performed modified the vertex
+        JsonObject json1 = resourceWithSoT.getJsonFromVertex(patchVertex1).get();
+        assertEquals(jsonPatchObj1, json1);
+
+        // Timestamps that have a large span in time difference will (likely) indicate that the transaction was not a create (thus, modify)
+        JsonObject json2 = resourceWithSoT.getJsonFromVertex(patchVertex2).get();
+        assertEquals(jsonPatchObj2, json2);
+    }
+
+    @Test
+    public void testGetJsonFromVertexWithNullVertex() throws AAIFormatVertexException, AAIException {
+        // Null check, will return null.
+        assertNull(resourceWithSoT.getJsonFromVertex(null));
+    }
+
+    public void createLoaderEngineSetup() throws AAIException {
+
+        if (loader == null) {
+            loader = loaderFactory.createLoaderForVersion(factoryType, version);
+            //loader = LoaderFactory.createLoaderForVersion(factoryType, version);
+            dbEngine = spy(new JanusGraphDBEngine(QueryStyle.TRAVERSAL, DBConnectionType.CACHED, loader));
+            serializer = new DBSerializer(version, dbEngine, factoryType, "Junit");
+            resourceWithSoT = new ResourceWithSoT.Builder(loader, serializer, urlBuilder).build();
+
+            TransactionalGraphEngine.Admin spyAdmin = spy(dbEngine.asAdmin());
+
+            when(dbEngine.tx()).thenReturn(graph);
+            when(dbEngine.asAdmin()).thenReturn(spyAdmin);
+
+            when(spyAdmin.getReadOnlyTraversalSource())
+                .thenReturn(graph.traversal(GraphTraversalSource.build().with(ReadOnlyStrategy.instance())));
+            when(spyAdmin.getTraversalSource()).thenReturn(graph.traversal());
+        }
+    }
+}