diff --git a/build.sbt b/build.sbt index 004d2eda..e1c8effc 100644 --- a/build.sbt +++ b/build.sbt @@ -14,7 +14,7 @@ lazy val root = (project in file(".")).enablePlugins(PlayJava) commonSettings, name := "ground" ) - .aggregate(common, postgres) + .aggregate(common, postgres, cassandra) lazy val common = (project in file("modules/common")) @@ -48,6 +48,20 @@ lazy val postgres = (project in file("modules/postgres")) ).dependsOn(common) +lazy val cassandra = (project in file("modules/cassandra")) + .enablePlugins(PlayJava, JavaAppPackaging) + .settings( + commonSettings, + name := "ground-cassandra", + libraryDependencies += javaJdbc, + libraryDependencies += cache, + libraryDependencies += "com.datastax.cassandra" % "cassandra-driver-core" % "3.0.0", + libraryDependencies += "commons-beanutils" % "commons-beanutils-core" % "1.8.3", + jacoco.settings, + parallelExecution in jacoco.Config := false, + Keys.fork in jacoco.Config := true, + jacoco.reportFormats in jacoco.Config := Seq(XMLReport(encoding = "utf-8")) + ).dependsOn(common) jacoco.settings parallelExecution in jacoco.Config := false diff --git a/modules/cassandra/app/Module.java b/modules/cassandra/app/Module.java new file mode 100644 index 00000000..1b26cfa9 --- /dev/null +++ b/modules/cassandra/app/Module.java @@ -0,0 +1,12 @@ +import com.google.inject.AbstractModule; +import edu.berkeley.ground.cassandra.start.ApplicationStart; +import java.time.Clock; + +public class Module extends AbstractModule { + + @Override + public void configure() { + bind(Clock.class).toInstance(Clock.systemDefaultZone()); + bind(ApplicationStart.class).asEagerSingleton(); + } +} diff --git a/modules/cassandra/app/edu/berkeley/ground/cassandra/controllers/EdgeController.java b/modules/cassandra/app/edu/berkeley/ground/cassandra/controllers/EdgeController.java new file mode 100644 index 00000000..61933b7f --- /dev/null +++ b/modules/cassandra/app/edu/berkeley/ground/cassandra/controllers/EdgeController.java @@ -0,0 +1,120 @@ +package edu.berkeley.ground.cassandra.controllers; + +import akka.actor.ActorSystem; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import edu.berkeley.ground.common.exception.GroundException; +import edu.berkeley.ground.common.model.core.Edge; +import edu.berkeley.ground.common.model.core.EdgeVersion; +import edu.berkeley.ground.common.util.IdGenerator; +import edu.berkeley.ground.cassandra.dao.core.CassandraEdgeDao; +import edu.berkeley.ground.cassandra.dao.core.CassandraEdgeVersionDao; +import edu.berkeley.ground.cassandra.util.GroundUtils; +import edu.berkeley.ground.cassandra.util.CassandraDatabase; +import edu.berkeley.ground.cassandra.util.CassandraUtils; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; +import java.util.concurrent.CompletionStage; +import javax.inject.Inject; +import play.cache.CacheApi; +import play.libs.Json; +import play.mvc.BodyParser; +import play.mvc.Controller; +import play.mvc.Result; +import play.mvc.Results; + +public class EdgeController extends Controller { + + private CacheApi cache; + private ActorSystem actorSystem; + + private CassandraEdgeDao cassandraEdgeDao; + private CassandraEdgeVersionDao cassandraEdgeVersionDao; + + @Inject + final void injectUtils(final CacheApi cache, final CassandraDatabase dbSource, final ActorSystem actorSystem, final IdGenerator idGenerator) { + this.actorSystem = actorSystem; + this.cache = cache; + + this.cassandraEdgeDao = new CassandraEdgeDao(dbSource, idGenerator); + this.cassandraEdgeVersionDao = new CassandraEdgeVersionDao(dbSource, idGenerator); + } + + public final CompletionStage getEdge(final String sourceKey) { + return CompletableFuture.supplyAsync( + () -> { + try { + return this.cache.getOrElse( + "edges", + () -> Json.toJson(this.cassandraEdgeDao.retrieveFromDatabase(sourceKey)), + Integer.parseInt(System.getProperty("ground.cache.expire.secs"))); + } catch (Exception e) { + throw new CompletionException(e); + } + }, + CassandraUtils.getDbSourceHttpContext(this.actorSystem)) + .thenApply(Results::ok) + .exceptionally(e -> GroundUtils.handleException(e, request())); + } + + @BodyParser.Of(BodyParser.Json.class) + public final CompletionStage addEdge() { + return CompletableFuture.supplyAsync( + () -> { + JsonNode json = request().body().asJson(); + Edge edge = Json.fromJson(json, Edge.class); + + try { + edge = this.cassandraEdgeDao.create(edge); + } catch (GroundException e) { + throw new CompletionException(e); + } + + return Json.toJson(edge); + }, + CassandraUtils.getDbSourceHttpContext(this.actorSystem)) + .thenApply(Results::created) + .exceptionally(e -> GroundUtils.handleException(e, request())); + } + + public final CompletionStage getEdgeVersion(Long id) { + return CompletableFuture.supplyAsync( + () -> { + try { + return this.cache.getOrElse( + "edge_versions", + () -> Json.toJson(this.cassandraEdgeVersionDao.retrieveFromDatabase(id)), + Integer.parseInt(System.getProperty("ground.cache.expire.secs"))); + } catch (Exception e) { + throw new CompletionException(e); + } + }, + CassandraUtils.getDbSourceHttpContext(actorSystem)) + .thenApply(Results::ok) + .exceptionally(e -> GroundUtils.handleException(e, request())); + } + + @BodyParser.Of(BodyParser.Json.class) + public final CompletionStage addEdgeVersion() { + return CompletableFuture.supplyAsync( + () -> { + JsonNode json = request().body().asJson(); + List parentIds = GroundUtils.getListFromJson(json, "parentIds"); + + ((ObjectNode) json).remove("parentIds"); + EdgeVersion edgeVersion = Json.fromJson(json, EdgeVersion.class); + + try { + edgeVersion = this.cassandraEdgeVersionDao.create(edgeVersion, parentIds); + } catch (GroundException e) { + throw new CompletionException(e); + } + + return Json.toJson(edgeVersion); + }, + CassandraUtils.getDbSourceHttpContext(actorSystem)) + .thenApply(Results::created) + .exceptionally(e -> GroundUtils.handleException(e, request())); + } +} diff --git a/modules/cassandra/app/edu/berkeley/ground/cassandra/controllers/GraphController.java b/modules/cassandra/app/edu/berkeley/ground/cassandra/controllers/GraphController.java new file mode 100644 index 00000000..c241b3c0 --- /dev/null +++ b/modules/cassandra/app/edu/berkeley/ground/cassandra/controllers/GraphController.java @@ -0,0 +1,129 @@ +/** + * 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. + */ +package edu.berkeley.ground.cassandra.controllers; + +import akka.actor.ActorSystem; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import edu.berkeley.ground.common.exception.GroundException; +import edu.berkeley.ground.common.model.core.Graph; +import edu.berkeley.ground.common.model.core.GraphVersion; +import edu.berkeley.ground.common.util.IdGenerator; +import edu.berkeley.ground.cassandra.dao.core.CassandraGraphDao; +import edu.berkeley.ground.cassandra.dao.core.CassandraGraphVersionDao; +import edu.berkeley.ground.cassandra.util.GroundUtils; +import edu.berkeley.ground.cassandra.util.CassandraDatabase; +import edu.berkeley.ground.cassandra.util.CassandraUtils; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; +import java.util.concurrent.CompletionStage; +import javax.inject.Inject; +import play.cache.CacheApi; +import play.libs.Json; +import play.mvc.BodyParser; +import play.mvc.Controller; +import play.mvc.Result; +import play.mvc.Results; + +public class GraphController extends Controller { + + private CacheApi cache; + private ActorSystem actorSystem; + + private CassandraGraphDao cassandraGraphDao; + private CassandraGraphVersionDao cassandraGraphVersionDao; + + @Inject + final void injectUtils(final CacheApi cache, final CassandraDatabase dbSource, final ActorSystem actorSystem, final IdGenerator idGenerator) { + this.actorSystem = actorSystem; + this.cache = cache; + + this.cassandraGraphDao = new CassandraGraphDao(dbSource, idGenerator); + + this.cassandraGraphVersionDao = new CassandraGraphVersionDao(dbSource, idGenerator); + } + + public final CompletionStage getGraph(String sourceKey) { + return CompletableFuture.supplyAsync( + () -> { + try { + return this.cache.getOrElse( + "graphs", + () -> Json.toJson(this.cassandraGraphDao.retrieveFromDatabase(sourceKey)), + Integer.parseInt(System.getProperty("ground.cache.expire.secs"))); + } catch (Exception e) { + throw new CompletionException(e); + } + }, + CassandraUtils.getDbSourceHttpContext(this.actorSystem)) + .thenApply(Results::ok) + .exceptionally(e -> GroundUtils.handleException(e, request())); + } + + public final CompletionStage getGraphVersion(Long id) { + return CompletableFuture.supplyAsync( + () -> { + try { + return this.cache.getOrElse("graph_versions", + () -> Json.toJson(this.cassandraGraphVersionDao.retrieveFromDatabase(id)), + Integer.parseInt(System.getProperty("ground.cache.expire.secs"))); + } catch (Exception e) { + throw new CompletionException(e); + } + }, + CassandraUtils.getDbSourceHttpContext(this.actorSystem)) + .thenApply(Results::ok) + .exceptionally(e -> GroundUtils.handleException(e, request())); + } + + @BodyParser.Of(BodyParser.Json.class) + public final CompletionStage addGraph() { + return CompletableFuture.supplyAsync( + () -> { + JsonNode json = request().body().asJson(); + Graph graph = Json.fromJson(json, Graph.class); + + try { + graph = this.cassandraGraphDao.create(graph); + } catch (GroundException e) { + throw new CompletionException(e); + } + + return Json.toJson(graph); + }, + CassandraUtils.getDbSourceHttpContext(actorSystem)) + .thenApply(Results::created) + .exceptionally(e -> GroundUtils.handleException(e, request())); + } + + @BodyParser.Of(BodyParser.Json.class) + public final CompletionStage addGraphVersion() { + return CompletableFuture.supplyAsync( + () -> { + JsonNode json = request().body().asJson(); + List parentIds = GroundUtils.getListFromJson(json, "parentIds"); + ((ObjectNode) json).remove("parentIds"); + GraphVersion graphVersion = Json.fromJson(json, GraphVersion.class); + + try { + graphVersion = this.cassandraGraphVersionDao.create(graphVersion, parentIds); + } catch (GroundException e) { + throw new CompletionException(e); + } + return Json.toJson(graphVersion); + }, + CassandraUtils.getDbSourceHttpContext(actorSystem)) + .thenApply(Results::ok) + .exceptionally(e -> GroundUtils.handleException(e, request())); + } +} diff --git a/modules/cassandra/app/edu/berkeley/ground/cassandra/controllers/HomeController.java b/modules/cassandra/app/edu/berkeley/ground/cassandra/controllers/HomeController.java new file mode 100644 index 00000000..17863b02 --- /dev/null +++ b/modules/cassandra/app/edu/berkeley/ground/cassandra/controllers/HomeController.java @@ -0,0 +1,17 @@ +package edu.berkeley.ground.cassandra.controllers; + +import play.mvc.*; +import views.html.*; + +/** This controller contains an action to handle HTTP requests to the application's home page. */ +public class HomeController extends Controller { + + /** + * An action that renders an HTML page with a welcome message. The configuration in the + * routes file means that this method will be called when the application receives a + * GET request with a path of /. + */ + public Result index() { + return ok(index.render("Your new application is ready.")); + } +} diff --git a/modules/cassandra/app/edu/berkeley/ground/cassandra/controllers/LineageEdgeController.java b/modules/cassandra/app/edu/berkeley/ground/cassandra/controllers/LineageEdgeController.java new file mode 100644 index 00000000..02659a37 --- /dev/null +++ b/modules/cassandra/app/edu/berkeley/ground/cassandra/controllers/LineageEdgeController.java @@ -0,0 +1,117 @@ +package edu.berkeley.ground.cassandra.controllers; + +import akka.actor.ActorSystem; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import edu.berkeley.ground.common.exception.GroundException; +import edu.berkeley.ground.common.model.usage.LineageEdge; +import edu.berkeley.ground.common.model.usage.LineageEdgeVersion; +import edu.berkeley.ground.common.util.IdGenerator; +import edu.berkeley.ground.cassandra.dao.usage.CassandraLineageEdgeDao; +import edu.berkeley.ground.cassandra.dao.usage.CassandraLineageEdgeVersionDao; +import edu.berkeley.ground.cassandra.util.GroundUtils; +import edu.berkeley.ground.cassandra.util.CassandraDatabase; +import edu.berkeley.ground.cassandra.util.CassandraUtils; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; +import java.util.concurrent.CompletionStage; +import javax.inject.Inject; +import play.cache.CacheApi; +import play.libs.Json; +import play.mvc.BodyParser; +import play.mvc.Controller; +import play.mvc.Result; +import play.mvc.Results; + +public class LineageEdgeController extends Controller { + + private CacheApi cache; + private ActorSystem actorSystem; + + private CassandraLineageEdgeDao cassandraLineageEdgeDao; + private CassandraLineageEdgeVersionDao cassandraLineageEdgeVersionDao; + + @Inject + final void injectUtils(final CacheApi cache, final CassandraDatabase dbSource, final ActorSystem actorSystem, final IdGenerator idGenerator) { + this.actorSystem = actorSystem; + this.cache = cache; + + this.cassandraLineageEdgeDao = new CassandraLineageEdgeDao(dbSource, idGenerator); + this.cassandraLineageEdgeVersionDao = new CassandraLineageEdgeVersionDao(dbSource, idGenerator); + } + + public final CompletionStage getLineageEdge(String sourceKey) { + return CompletableFuture.supplyAsync( + () -> { + try { + return this.cache.getOrElse( + "lineage_edges", + () -> Json.toJson(this.cassandraLineageEdgeDao.retrieveFromDatabase(sourceKey)), + Integer.parseInt(System.getProperty("ground.cache.expire.secs"))); + } catch (Exception e) { + throw new CompletionException(e); + } + }, + CassandraUtils.getDbSourceHttpContext(actorSystem)) + .thenApply(Results::ok) + .exceptionally(e -> GroundUtils.handleException(e, request())); + } + + public final CompletionStage getLineageEdgeVersion(Long id) { + return CompletableFuture.supplyAsync( + () -> { + try { + return this.cache.getOrElse( + "lineage_edge_versions", + () -> Json.toJson(this.cassandraLineageEdgeVersionDao.retrieveFromDatabase(id)), + Integer.parseInt(System.getProperty("ground.cache.expire.secs"))); + } catch (Exception e) { + throw new CompletionException(e); + } + }, + CassandraUtils.getDbSourceHttpContext(actorSystem)) + .thenApply(Results::ok) + .exceptionally(e -> GroundUtils.handleException(e, request())); + } + + @BodyParser.Of(BodyParser.Json.class) + public final CompletionStage createLineageEdge() { + return CompletableFuture.supplyAsync( + () -> { + JsonNode json = request().body().asJson(); + LineageEdge lineageEdge = Json.fromJson(json, LineageEdge.class); + try { + lineageEdge = this.cassandraLineageEdgeDao.create(lineageEdge); + } catch (GroundException e) { + throw new CompletionException(e); + } + return Json.toJson(lineageEdge); + }, + CassandraUtils.getDbSourceHttpContext(this.actorSystem)) + .thenApply(Results::created) + .exceptionally(e -> GroundUtils.handleException(e, request())); + } + + public final CompletionStage createLineageEdgeVersion() { + return CompletableFuture.supplyAsync( + () -> { + JsonNode json = request().body().asJson(); + + List parentIds = GroundUtils.getListFromJson(json, "parentIds"); + ((ObjectNode) json).remove("parentIds"); + + LineageEdgeVersion lineageEdgeVersion = Json.fromJson(json, LineageEdgeVersion.class); + + try { + lineageEdgeVersion = this.cassandraLineageEdgeVersionDao.create(lineageEdgeVersion, parentIds); + } catch (GroundException e) { + throw new CompletionException(e); + } + return Json.toJson(lineageEdgeVersion); + }, + CassandraUtils.getDbSourceHttpContext(actorSystem)) + .thenApply(Results::created) + .exceptionally(e -> GroundUtils.handleException(e, request())); + } +} diff --git a/modules/cassandra/app/edu/berkeley/ground/cassandra/controllers/LineageGraphController.java b/modules/cassandra/app/edu/berkeley/ground/cassandra/controllers/LineageGraphController.java new file mode 100644 index 00000000..df5e5e97 --- /dev/null +++ b/modules/cassandra/app/edu/berkeley/ground/cassandra/controllers/LineageGraphController.java @@ -0,0 +1,118 @@ +package edu.berkeley.ground.cassandra.controllers; + +import akka.actor.ActorSystem; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import edu.berkeley.ground.common.exception.GroundException; +import edu.berkeley.ground.common.model.usage.LineageGraph; +import edu.berkeley.ground.common.model.usage.LineageGraphVersion; +import edu.berkeley.ground.common.util.IdGenerator; +import edu.berkeley.ground.cassandra.dao.usage.CassandraLineageGraphDao; +import edu.berkeley.ground.cassandra.dao.usage.CassandraLineageGraphVersionDao; +import edu.berkeley.ground.cassandra.util.GroundUtils; +import edu.berkeley.ground.cassandra.util.CassandraDatabase; +import edu.berkeley.ground.cassandra.util.CassandraUtils; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; +import java.util.concurrent.CompletionStage; +import javax.inject.Inject; +import play.cache.CacheApi; +import play.libs.Json; +import play.mvc.BodyParser; +import play.mvc.Controller; +import play.mvc.Result; +import play.mvc.Results; + +public class LineageGraphController extends Controller { + + private CacheApi cache; + private ActorSystem actorSystem; + + private CassandraLineageGraphDao cassandraLineageGraphDao; + private CassandraLineageGraphVersionDao cassandraLineageGraphVersionDao; + + @Inject + final void injectUtils(final CacheApi cache, final CassandraDatabase dbSource, + final ActorSystem actorSystem, final IdGenerator idGenerator) { + this.actorSystem = actorSystem; + this.cache = cache; + + this.cassandraLineageGraphDao = new CassandraLineageGraphDao(dbSource, idGenerator); + this.cassandraLineageGraphVersionDao = new CassandraLineageGraphVersionDao(dbSource, idGenerator); + } + + public final CompletionStage getLineageGraph(String sourceKey) { + return CompletableFuture.supplyAsync( + () -> { + try { + return this.cache.getOrElse( + "lineage_graphs", + () -> Json.toJson(this.cassandraLineageGraphDao.retrieveFromDatabase(sourceKey)), + Integer.parseInt(System.getProperty("ground.cache.expire.secs"))); + } catch (Exception e) { + throw new CompletionException(e); + } + }, + CassandraUtils.getDbSourceHttpContext(actorSystem)) + .thenApply(Results::ok) + .exceptionally(e -> GroundUtils.handleException(e, request())); + } + + public final CompletionStage getLineageGraphVersion(Long id) { + return CompletableFuture.supplyAsync( + () -> { + try { + return this.cache.getOrElse( + "lineage_graph_versions", + () -> Json.toJson(this.cassandraLineageGraphVersionDao.retrieveFromDatabase(id)), + Integer.parseInt(System.getProperty("ground.cache.expire.secs"))); + } catch (Exception e) { + throw new CompletionException(e); + } + }, + CassandraUtils.getDbSourceHttpContext(actorSystem)) + .thenApply(Results::ok) + .exceptionally(e -> GroundUtils.handleException(e, request())); + } + + @BodyParser.Of(BodyParser.Json.class) + public final CompletionStage createLineageGraph() { + return CompletableFuture.supplyAsync( + () -> { + JsonNode json = request().body().asJson(); + LineageGraph lineageGraph = Json.fromJson(json, LineageGraph.class); + try { + lineageGraph = this.cassandraLineageGraphDao.create(lineageGraph); + } catch (GroundException e) { + throw new CompletionException(e); + } + return Json.toJson(lineageGraph); + }, + CassandraUtils.getDbSourceHttpContext(this.actorSystem)) + .thenApply(Results::created) + .exceptionally(e -> GroundUtils.handleException(e, request())); + } + + public final CompletionStage createLineageGraphVersion() { + return CompletableFuture.supplyAsync( + () -> { + JsonNode json = request().body().asJson(); + + List parentIds = GroundUtils.getListFromJson(json, "parentIds"); + ((ObjectNode) json).remove("parentIds"); + + LineageGraphVersion lineageGraphVersion = Json.fromJson(json, LineageGraphVersion.class); + + try { + lineageGraphVersion = this.cassandraLineageGraphVersionDao.create(lineageGraphVersion, parentIds); + } catch (GroundException e) { + throw new CompletionException(e); + } + return Json.toJson(lineageGraphVersion); + }, + CassandraUtils.getDbSourceHttpContext(actorSystem)) + .thenApply(Results::created) + .exceptionally(e -> GroundUtils.handleException(e, request())); + } +} diff --git a/modules/cassandra/app/edu/berkeley/ground/cassandra/controllers/NodeController.java b/modules/cassandra/app/edu/berkeley/ground/cassandra/controllers/NodeController.java new file mode 100644 index 00000000..eb17e0af --- /dev/null +++ b/modules/cassandra/app/edu/berkeley/ground/cassandra/controllers/NodeController.java @@ -0,0 +1,118 @@ +package edu.berkeley.ground.cassandra.controllers; + +import akka.actor.ActorSystem; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import edu.berkeley.ground.common.exception.GroundException; +import edu.berkeley.ground.common.model.core.Node; +import edu.berkeley.ground.common.model.core.NodeVersion; +import edu.berkeley.ground.common.util.IdGenerator; +import edu.berkeley.ground.cassandra.dao.core.CassandraNodeDao; +import edu.berkeley.ground.cassandra.dao.core.CassandraNodeVersionDao; +import edu.berkeley.ground.cassandra.util.GroundUtils; +import edu.berkeley.ground.cassandra.util.CassandraDatabase; +import edu.berkeley.ground.cassandra.util.CassandraUtils; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; +import java.util.concurrent.CompletionStage; +import javax.inject.Inject; +import play.cache.CacheApi; +import play.libs.Json; +import play.mvc.BodyParser; +import play.mvc.Controller; +import play.mvc.Result; +import play.mvc.Results; + +public class NodeController extends Controller { + + private CacheApi cache; + private ActorSystem actorSystem; + + private CassandraNodeDao cassandraNodeDao; + private CassandraNodeVersionDao cassandraNodeVersionDao; + + @Inject + final void injectUtils(final CacheApi cache, final CassandraDatabase dbSource, final ActorSystem actorSystem, final IdGenerator idGenerator) { + this.actorSystem = actorSystem; + this.cache = cache; + + this.cassandraNodeDao = new CassandraNodeDao(dbSource, idGenerator); + this.cassandraNodeVersionDao = new CassandraNodeVersionDao(dbSource, idGenerator); + } + + public final CompletionStage getNode(String sourceKey) { + return CompletableFuture.supplyAsync( + () -> { + try { + return this.cache.getOrElse( + "nodes", + () -> Json.toJson(this.cassandraNodeDao.retrieveFromDatabase(sourceKey)), + Integer.parseInt(System.getProperty("ground.cache.expire.secs"))); + } catch (Exception e) { + throw new CompletionException(e); + } + }, + CassandraUtils.getDbSourceHttpContext(this.actorSystem)) + .thenApply(Results::ok) + .exceptionally(e -> GroundUtils.handleException(e, request())); + } + + @BodyParser.Of(BodyParser.Json.class) + public final CompletionStage addNode() { + return CompletableFuture.supplyAsync( + () -> { + JsonNode json = request().body().asJson(); + Node node = Json.fromJson(json, Node.class); + try { + node = this.cassandraNodeDao.create(node); + } catch (GroundException e) { + throw new CompletionException(e); + } + return Json.toJson(node); + }, + CassandraUtils.getDbSourceHttpContext(this.actorSystem)) + .thenApply(Results::created) + .exceptionally(e -> GroundUtils.handleException(e, request())); + } + + public final CompletionStage getNodeVersion(Long id) { + return CompletableFuture.supplyAsync( + () -> { + try { + return this.cache.getOrElse( + "node_versions", + () -> Json.toJson(this.cassandraNodeVersionDao.retrieveFromDatabase(id)), + Integer.parseInt(System.getProperty("ground.cache.expire.secs"))); + } catch (Exception e) { + throw new CompletionException(e); + } + }, + CassandraUtils.getDbSourceHttpContext(this.actorSystem)) + .thenApply(Results::ok) + .exceptionally(e -> GroundUtils.handleException(e, request())); + } + + @BodyParser.Of(BodyParser.Json.class) + public final CompletionStage addNodeVersion() { + return CompletableFuture.supplyAsync( + () -> { + JsonNode json = request().body().asJson(); + + List parentIds = GroundUtils.getListFromJson(json, "parentIds"); + ((ObjectNode) json).remove("parentIds"); + NodeVersion nodeVersion = Json.fromJson(json, NodeVersion.class); + + try { + nodeVersion = this.cassandraNodeVersionDao.create(nodeVersion, parentIds); + } catch (GroundException e) { + e.printStackTrace(); + throw new CompletionException(e); + } + return Json.toJson(nodeVersion); + }, + CassandraUtils.getDbSourceHttpContext(this.actorSystem)) + .thenApply(Results::created) + .exceptionally(e -> GroundUtils.handleException(e, request())); + } +} diff --git a/modules/cassandra/app/edu/berkeley/ground/cassandra/controllers/StructureController.java b/modules/cassandra/app/edu/berkeley/ground/cassandra/controllers/StructureController.java new file mode 100644 index 00000000..9f0cea8c --- /dev/null +++ b/modules/cassandra/app/edu/berkeley/ground/cassandra/controllers/StructureController.java @@ -0,0 +1,129 @@ +/** + * 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. + */ +package edu.berkeley.ground.cassandra.controllers; + +import akka.actor.ActorSystem; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import edu.berkeley.ground.common.exception.GroundException; +import edu.berkeley.ground.common.model.core.Structure; +import edu.berkeley.ground.common.model.core.StructureVersion; +import edu.berkeley.ground.common.util.IdGenerator; +import edu.berkeley.ground.cassandra.dao.core.CassandraStructureDao; +import edu.berkeley.ground.cassandra.dao.core.CassandraStructureVersionDao; +import edu.berkeley.ground.cassandra.util.GroundUtils; +import edu.berkeley.ground.cassandra.util.CassandraDatabase; +import edu.berkeley.ground.cassandra.util.CassandraUtils; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; +import java.util.concurrent.CompletionStage; +import javax.inject.Inject; +import play.cache.CacheApi; +import play.libs.Json; +import play.mvc.BodyParser; +import play.mvc.Controller; +import play.mvc.Result; +import play.mvc.Results; + +public class StructureController extends Controller { + + private CacheApi cache; + private ActorSystem actorSystem; + + private CassandraStructureDao cassandraStructureDao; + private CassandraStructureVersionDao cassandraStructureVersionDao; + + @Inject + final void injectUtils(final CacheApi cache, final CassandraDatabase dbSource, final ActorSystem actorSystem, final IdGenerator idGenerator) { + this.actorSystem = actorSystem; + this.cache = cache; + + this.cassandraStructureDao = new CassandraStructureDao(dbSource, idGenerator); + this.cassandraStructureVersionDao = new CassandraStructureVersionDao(dbSource, idGenerator); + } + + public final CompletionStage getStructure(String sourceKey) { + return CompletableFuture.supplyAsync( + () -> { + try { + return this.cache.getOrElse( + "structures", + () -> Json.toJson(this.cassandraStructureDao.retrieveFromDatabase(sourceKey)), + Integer.parseInt(System.getProperty("ground.cache.expire.secs"))); + } catch (Exception e) { + throw new CompletionException(e); + } + }, CassandraUtils.getDbSourceHttpContext(this.actorSystem)) + .thenApply(Results::ok) + .exceptionally(e -> GroundUtils.handleException(e, request())); + } + + public final CompletionStage getStructureVersion(Long id) { + return CompletableFuture.supplyAsync( + () -> { + try { + return this.cache.getOrElse( + "structure_versions", + () -> Json.toJson(this.cassandraStructureVersionDao.retrieveFromDatabase(id)), + Integer.parseInt(System.getProperty("ground.cache.expire.secs"))); + } catch (Exception e) { + throw new CompletionException(e); + } + }, CassandraUtils.getDbSourceHttpContext(this.actorSystem)) + .thenApply(Results::ok) + .exceptionally(e -> GroundUtils.handleException(e, request())); + } + + @BodyParser.Of(BodyParser.Json.class) + public final CompletionStage addStructure() { + return CompletableFuture.supplyAsync( + () -> { + JsonNode json = request().body().asJson(); + Structure structure = Json.fromJson(json, Structure.class); + + try { + structure = this.cassandraStructureDao.create(structure); + } catch (GroundException e) { + throw new CompletionException(e); + } + return Json.toJson(structure); + }, + + CassandraUtils.getDbSourceHttpContext(this.actorSystem)) + .thenApply(Results::created) + .exceptionally(e -> GroundUtils.handleException(e, request())); + } + + @BodyParser.Of(BodyParser.Json.class) + public final CompletionStage addStructureVersion() { + return CompletableFuture.supplyAsync( + () -> { + JsonNode json = request().body().asJson(); + + List parentIds = GroundUtils.getListFromJson(json, "parentIds"); + ((ObjectNode) json).remove("parentIds"); + + StructureVersion structureVersion = Json.fromJson(json, StructureVersion.class); + + try { + structureVersion = this.cassandraStructureVersionDao.create(structureVersion, parentIds); + } catch (GroundException e) { + throw new CompletionException(e); + } + return Json.toJson(structureVersion); + }, + CassandraUtils.getDbSourceHttpContext(this.actorSystem)) + .thenApply(Results::created) + .exceptionally(e -> GroundUtils.handleException(e, request())); + } +} diff --git a/modules/cassandra/app/edu/berkeley/ground/cassandra/dao/CqlConstants.java b/modules/cassandra/app/edu/berkeley/ground/cassandra/dao/CqlConstants.java new file mode 100644 index 00000000..47f84239 --- /dev/null +++ b/modules/cassandra/app/edu/berkeley/ground/cassandra/dao/CqlConstants.java @@ -0,0 +1,90 @@ +package edu.berkeley.ground.cassandra.dao; + +public class CqlConstants { + + /* General insert statements */ + public static final String INSERT_GENERIC_ITEM_WITH_NAME = "INSERT INTO %s (item_id, source_key, name) VALUES (%d, \'%s\', \'%s\');"; + public static final String INSERT_GENERIC_ITEM_WITHOUT_NAME = "INSERT INTO %s (item_id, source_key, name) VALUES (%d, \'%s\', null);"; + + /* General select statements */ + public static final String SELECT_STAR_BY_SOURCE_KEY = "SELECT * FROM %s WHERE source_key = \'%s\' ALLOW FILTERING;"; + public static final String SELECT_STAR_ITEM_BY_ID = "SELECT * FROM %s WHERE item_id = %d ALLOW FILTERING;"; + public static final String SELECT_STAR_BY_ID = "SELECT * FROM %s WHERE id = %d ALLOW FILTERING;"; + public static final String DELETE_BY_ID = "DELETE FROM %s WHERE id = %d;"; + + /* Version-specific statements */ + public static final String INSERT_VERSION = "INSERT INTO version (id) VALUES (%d);"; + + /* Version Successor-specific statements */ + public static final String INSERT_VERSION_SUCCESSOR = "INSERT INTO version_successor (id, from_version_id, to_version_id) VALUES (%d, %d, %d);"; + public static final String SELECT_VERSION_SUCCESSOR = "SELECT * FROM version_successor where id = %d ALLOW FILTERING;"; + public static final String SELECT_VERSION_SUCCESSOR_BY_ENDPOINT = "SELECT * FROM version_successor WHERE to_version_id = %d ALLOW FILTERING;"; + public static final String DELETE_VERSION_SUCCESSOR = "DELETE FROM version_successor WHERE id = %d;"; + + /* Version History DAG-specific statements */ + public static final String INSERT_VERSION_HISTORY_DAG_EDGE = "INSERT INTO version_history_dag (item_id, version_successor_id) VALUES (%d, %d);"; + public static final String SELECT_VERSION_HISTORY_DAG = "SELECT * FROM version_history_dag WHERE item_id = %d ALLOW FILTERING;"; + public static final String DELETE_SUCCESSOR_FROM_DAG = "DELETE FROM version_history_dag WHERE item_id = %d AND version_successor_id = %d;"; + + /* Item-specific statements */ + public static final String INSERT_ITEM = "INSERT INTO item (id) VALUES (%d);"; + public static final String INSERT_ITEM_TAG_WITH_VALUE = + "INSERT INTO item_tag (item_id, key, value, type) VALUES (%d, " + "\'%s\', \'%s\', \'%s\');"; + public static final String INSERT_ITEM_TAG_NO_VALUE = "INSERT INTO item_tag (item_id, key, value, type) VALUES (%d, \'%s\', null, null);"; + public static final String SELECT_ITEM_TAGS = "SELECT * FROM item_tag WHERE item_id = %d ALLOW FILTERING;"; + public static final String SELECT_ITEM_TAGS_BY_KEY = "SELECT * FROM item_tag WHERE key = \'%s\' ALLOW FILTERING;"; + + /* Edge-specific statements */ + public static final String INSERT_EDGE_WITH_NAME = + "INSERT INTO edge (item_id, source_key, from_node_id, to_node_id, name) VALUES (%d, \'%s\', %d, %d, \'%s\');"; + public static final String INSERT_EDGE_WITHOUT_NAME = + "INSERT INTO edge (item_id, source_key, from_node_id, to_node_id, name) VALUES (%d, \'%s\', %d, %d, null);"; + public static final String INSERT_EDGE_VERSION = "INSERT INTO edge_version (id, edge_id, from_node_version_start_id, from_node_version_end_id, " + + "to_node_version_start_id, to_node_version_end_id) VALUES (%d, %d, %d, %d, %d, %d);"; + public static final String UPDATE_EDGE_VERSION = "UPDATE edge_version SET from_node_version_end_id = %d, to_node_version_end_id = %d WHERE id = " + + "%d;"; + + /* Graph-specific statements */ + public static final String INSERT_GRAPH_VERSION = "INSERT INTO graph_version (id, graph_id) VALUES (%d, %d);"; + public static final String INSERT_GRAPH_VERSION_EDGE = "INSERT INTO graph_version_edge (graph_version_id, edge_version_id) VALUES (%d, %d);"; + public static final String SELECT_GRAPH_VERSION_EDGES = "SELECT * FROM graph_version_edge WHERE graph_version_id = %d ALLOW FILTERING;"; + public static final String DELETE_ALL_GRAPH_VERSION_EDGES = "DELETE FROM %s WHERE %s_version_id = %d;"; + + /* Node-specific statements */ + public static final String INSERT_NODE_VERSION = "INSERT INTO node_version (id, node_id) VALUES (%d, %d);"; + public static final String SELECT_NODE_VERSION_ADJACENT_LINEAGE = "SELECT * FROM lineage_edge_version WHERE from_rich_version_id = %d;"; + + /* Rich Version-specific statements */ + public static final String INSERT_RICH_VERSION_WITH_REFERENCE = "INSERT INTO rich_version (id, structure_version_id, reference) VALUES (%d, %d, " + + "\'%s\');"; + public static final String INSERT_RICH_VERSION_WITHOUT_REFERENCE = "INSERT INTO rich_version (id, structure_version_id, reference) VALUES (%d, %d, " + + "null);"; + public static final String INSERT_RICH_VERSION_TAG_WITH_VALUE = "INSERT INTO rich_version_tag (rich_version_id, key, value, type) VALUES (%d, " + + "\'%s\', \'%s\', \'%s\');"; + public static final String INSERT_RICH_VERSION_TAG_NO_VALUE = "INSERT INTO rich_version_tag (rich_version_id, key, value, type) VALUES (%d, " + + "\'%s\', null, null);"; + public static final String INSERT_RICH_VERSION_EXTERNAL_PARAMETER = "INSERT INTO rich_version_external_parameter (rich_version_id, key, value) " + + "VALUES (%d, \'%s\', \'%s\');"; + public static final String SELECT_RICH_VERSION_EXTERNAL_PARAMETERS = "SELECT * FROM rich_version_external_parameter WHERE rich_version_id = %d ALLOW FILTERING;"; + public static final String SELECT_RICH_VERSION_TAGS = "SELECT * FROM rich_version_tag WHERE rich_version_id = %d ALLOW FILTERING;"; + public static final String SELECT_RICH_VERSION_TAGS_BY_KEY = "SELECT * FROM rich_version_tag WHERE key = \'%s\' ALLOW FILTERING;"; + public static final String DELETE_RICH_VERSION_TAGS = "DELETE FROM rich_version_tag WHERE rich_version_id = %d;"; + public static final String DELETE_RICH_EXTERNAL_PARAMETERS = "DELETE FROM rich_version_external_parameter WHERE rich_version_id = %d"; + + /* Structure-specific statements */ + public static final String INSERT_STRUCTURE_VERSION = "INSERT INTO structure_version (id, structure_id) VALUES (%d, %d);"; + public static final String INSERT_STRUCTURE_VERSION_ATTRIBUTE = "INSERT INTO structure_version_attribute (structure_version_id, key, type) " + + "VALUES (%d, \'%s\', \'%s\');"; + public static final String SELECT_STRUCTURE_VERSION_ATTRIBUTES = "SELECT * FROM structure_version_attribute WHERE structure_version_id = %d ALLOW FILTERING;"; + public static final String DELETE_STRUCTURE_VERSION_ATTRIBUTES = "DELETE FROM structure_version_attribute WHERE structure_version_id = %d"; + + /* Lineage Edge-specific statements */ + public static final String INSERT_LINEAGE_EDGE_VERSION = "INSERT INTO lineage_edge_version (id, lineage_edge_id, from_rich_version_id, " + + "to_rich_version_id, principal_id) VALUES (%d, %d, %d, %d, %d);"; + + /* Lineage Graph-specific statements */ + public static final String INSERT_LINEAGE_GRAPH_VERSION = "INSERT INTO lineage_graph_version (id, lineage_graph_id) VALUES (%d, %d);"; + public static final String INSERT_LINEAGE_GRAPH_VERSION_EDGE = "INSERT INTO lineage_graph_version_edge (lineage_graph_version_id, " + + "lineage_edge_version_id) VALUES (%d, %d);"; + public static final String SELECT_LINEAGE_GRAPH_VERSION_EDGES = "SELECT * FROM lineage_graph_version_edge WHERE lineage_graph_version_id = %d ALLOW FILTERING;"; +} diff --git a/modules/cassandra/app/edu/berkeley/ground/cassandra/dao/core/CassandraEdgeDao.java b/modules/cassandra/app/edu/berkeley/ground/cassandra/dao/core/CassandraEdgeDao.java new file mode 100644 index 00000000..c4a9d14d --- /dev/null +++ b/modules/cassandra/app/edu/berkeley/ground/cassandra/dao/core/CassandraEdgeDao.java @@ -0,0 +1,99 @@ +/** + * 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. + */ +package edu.berkeley.ground.cassandra.dao.core; + +import com.fasterxml.jackson.databind.JsonNode; +import edu.berkeley.ground.common.dao.core.EdgeDao; +import edu.berkeley.ground.common.exception.GroundException; +import edu.berkeley.ground.common.exception.GroundException.ExceptionType; +import edu.berkeley.ground.common.model.core.Edge; +import edu.berkeley.ground.common.util.IdGenerator; +import edu.berkeley.ground.cassandra.dao.CqlConstants; +import edu.berkeley.ground.cassandra.dao.version.CassandraItemDao; +import edu.berkeley.ground.cassandra.util.CassandraStatements; +import edu.berkeley.ground.cassandra.util.CassandraUtils; +import java.util.List; +import java.util.Map; +import edu.berkeley.ground.cassandra.util.CassandraDatabase; +import play.libs.Json; + + +public class CassandraEdgeDao extends CassandraItemDao implements EdgeDao { + + public CassandraEdgeDao(CassandraDatabase dbSource, IdGenerator idGenerator) { + super(dbSource, idGenerator); + } + + @Override + public Class getType() { + return Edge.class; + } + + @Override + public Edge create(Edge edge) throws GroundException { + super.verifyItemNotExists(edge.getSourceKey()); + + CassandraStatements statements; + long uniqueId = idGenerator.generateItemId(); + + Edge newEdge = new Edge(uniqueId, edge); + try { + statements = super.insert(newEdge); + + String name = edge.getName(); + + if (name != null) { + statements.append(String.format(CqlConstants.INSERT_EDGE_WITH_NAME, uniqueId, edge.getSourceKey(), edge.getFromNodeId(), + edge.getToNodeId(), name)); + } else { + statements.append(String.format(CqlConstants.INSERT_EDGE_WITHOUT_NAME, uniqueId, edge.getSourceKey(), edge.getFromNodeId(), + edge.getToNodeId())); + } + + } catch (Exception e) { + throw new GroundException(e); + } + CassandraUtils.executeCqlList(dbSource, statements); + return newEdge; + } + + @Override + protected Edge retrieve(String cql, Object field) throws GroundException { + JsonNode json = Json.parse(CassandraUtils.executeQueryToJson(dbSource, cql)); + + if (json.size() == 0) { + throw new GroundException(ExceptionType.ITEM_NOT_FOUND, this.getType().getSimpleName(), field.toString()); + } + + Edge edge = Json.fromJson(json.get(0), Edge.class); + long id = edge.getId(); + return new Edge(id, edge.getName(), edge.getSourceKey(), edge.getFromNodeId(), edge.getToNodeId(), + super.cassandraTagDao.retrieveFromDatabaseByItemId(id)); + } + + @Override + public List getLeaves(String sourceKey) throws GroundException { + Edge edge = retrieveFromDatabase(sourceKey); + return super.getLeaves(edge.getId()); + } + + @Override + public Map getHistory(String sourceKey) throws GroundException { + Edge edge = retrieveFromDatabase(sourceKey); + return super.getHistory(edge.getId()); + } + + @Override + public void truncate(long itemId, int numLevels) throws GroundException { + super.truncate(itemId, numLevels); + } +} diff --git a/modules/cassandra/app/edu/berkeley/ground/cassandra/dao/core/CassandraEdgeVersionDao.java b/modules/cassandra/app/edu/berkeley/ground/cassandra/dao/core/CassandraEdgeVersionDao.java new file mode 100644 index 00000000..20abd8c7 --- /dev/null +++ b/modules/cassandra/app/edu/berkeley/ground/cassandra/dao/core/CassandraEdgeVersionDao.java @@ -0,0 +1,136 @@ +package edu.berkeley.ground.cassandra.dao.core; + +import com.fasterxml.jackson.databind.JsonNode; +import edu.berkeley.ground.common.dao.core.EdgeVersionDao; +import edu.berkeley.ground.common.exception.GroundException; +import edu.berkeley.ground.common.exception.GroundException.ExceptionType; +import edu.berkeley.ground.common.model.core.Edge; +import edu.berkeley.ground.common.model.core.EdgeVersion; +import edu.berkeley.ground.common.model.core.RichVersion; +import edu.berkeley.ground.common.model.version.VersionHistoryDag; +import edu.berkeley.ground.common.util.IdGenerator; +import edu.berkeley.ground.cassandra.dao.CqlConstants; +import edu.berkeley.ground.cassandra.dao.version.CassandraVersionHistoryDagDao; +import edu.berkeley.ground.cassandra.util.CassandraDatabase; +import edu.berkeley.ground.cassandra.util.CassandraStatements; +import edu.berkeley.ground.cassandra.util.CassandraUtils; +import java.util.List; +import play.libs.Json; + + +public class CassandraEdgeVersionDao extends CassandraRichVersionDao implements EdgeVersionDao { + + private CassandraEdgeDao cassandraEdgeDao; + + public CassandraEdgeVersionDao(CassandraDatabase dbSource, IdGenerator idGenerator) { + super(dbSource, idGenerator); + this.cassandraEdgeDao = new CassandraEdgeDao(dbSource, idGenerator); + } + + @Override + public EdgeVersion create(final EdgeVersion edgeVersion, List parentIds) throws GroundException { + final long uniqueId = this.idGenerator.generateVersionId(); + EdgeVersion newEdgeVersion = new EdgeVersion(uniqueId, edgeVersion); + + CassandraStatements updateVersionList = this.cassandraEdgeDao.update(newEdgeVersion.getEdgeId(), newEdgeVersion.getId(), parentIds); + + for (long parentId : parentIds) { + if (parentId != 0) { + updateVersionList.merge(this.updatePreviousVersion(newEdgeVersion, newEdgeVersion.getEdgeId(), parentId)); + } + } + + try { + CassandraStatements statements = super.insert(newEdgeVersion); + Long fromEndId = edgeVersion.getFromNodeVersionEndId(); + Long toEndId = edgeVersion.getToNodeVersionEndId(); + + if (fromEndId == -1) { + fromEndId = null; + } + + if (toEndId == -1) { + toEndId = null; + } + + statements.append(String.format(CqlConstants.INSERT_EDGE_VERSION, uniqueId, edgeVersion.getEdgeId(), edgeVersion.getFromNodeVersionStartId(), + fromEndId, edgeVersion.getToNodeVersionStartId(), toEndId)); + + statements.merge(updateVersionList); + + CassandraUtils.executeCqlList(dbSource, statements); + } catch (Exception e) { + throw new GroundException(e); + } + + return newEdgeVersion; + } + + @Override + public CassandraStatements delete(long id) { + CassandraStatements statements = new CassandraStatements(); + statements.append(String.format(CqlConstants.DELETE_BY_ID, "edge_version", id)); + + CassandraStatements superStatements = super.delete(id); + superStatements.merge(statements); + return superStatements; + } + + /** + * Set the from and to end versions of a previous edge version. + * + * @param currentVersion the new version created + * @param edgeId the id of the edge we're updating + * @param parentId the id of the parent we're updating + * @return a set of statements to set the end versions + */ + private CassandraStatements updatePreviousVersion(EdgeVersion currentVersion, long edgeId, long parentId) throws GroundException { + CassandraStatements statements = new CassandraStatements(); + + CassandraVersionHistoryDagDao versionHistoryDagDao = + new CassandraVersionHistoryDagDao(this.dbSource, this.idGenerator); + + EdgeVersion parentVersion = this.retrieveFromDatabase(parentId); + Edge edge = this.cassandraEdgeDao.retrieveFromDatabase(edgeId); + + long fromNodeId = edge.getFromNodeId(); + long toNodeId = edge.getToNodeId(); + + long fromEndId = -1; + long toEndId = -1; + + if (parentVersion.getFromNodeVersionEndId() == -1) { + // update from end id + VersionHistoryDag dag = versionHistoryDagDao.retrieveFromDatabase(fromNodeId); + fromEndId = dag.getParent(currentVersion.getFromNodeVersionStartId()).get(0); + } + + if (parentVersion.getToNodeVersionEndId() == -1) { + // update to end id + VersionHistoryDag dag = versionHistoryDagDao.retrieveFromDatabase(toNodeId); + toEndId = dag.getParent(currentVersion.getToNodeVersionStartId()).get(0); + } + + if (fromEndId != -1 || toEndId != -1) { + statements.append(String.format(CqlConstants.UPDATE_EDGE_VERSION, fromEndId, toEndId, parentId)); + } + + return statements; + } + + @Override + public EdgeVersion retrieveFromDatabase(long id) throws GroundException { + String cql = String.format(CqlConstants.SELECT_STAR_BY_ID, "edge_version", id); + JsonNode json = Json.parse(CassandraUtils.executeQueryToJson(dbSource, cql)); + + if (json.size() == 0) { + throw new GroundException(ExceptionType.VERSION_NOT_FOUND, this.getType().getSimpleName(), String.format("%d", id)); + } + + json = json.get(0); + EdgeVersion edgeVersion = Json.fromJson(json, EdgeVersion.class); + RichVersion richVersion = super.retrieveFromDatabase(id); + + return new EdgeVersion(id, richVersion, edgeVersion); + } +} diff --git a/modules/cassandra/app/edu/berkeley/ground/cassandra/dao/core/CassandraGraphDao.java b/modules/cassandra/app/edu/berkeley/ground/cassandra/dao/core/CassandraGraphDao.java new file mode 100644 index 00000000..9a6d5568 --- /dev/null +++ b/modules/cassandra/app/edu/berkeley/ground/cassandra/dao/core/CassandraGraphDao.java @@ -0,0 +1,79 @@ +/** + * 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. + */ +package edu.berkeley.ground.cassandra.dao.core; + +import edu.berkeley.ground.common.dao.core.GraphDao; +import edu.berkeley.ground.common.exception.GroundException; +import edu.berkeley.ground.common.model.core.Graph; +import edu.berkeley.ground.common.util.IdGenerator; +import edu.berkeley.ground.cassandra.dao.CqlConstants; +import edu.berkeley.ground.cassandra.dao.version.CassandraItemDao; +import edu.berkeley.ground.cassandra.util.CassandraDatabase; +import edu.berkeley.ground.cassandra.util.CassandraStatements; +import edu.berkeley.ground.cassandra.util.CassandraUtils; +import java.util.List; +import java.util.Map; + + +public class CassandraGraphDao extends CassandraItemDao implements GraphDao { + + public CassandraGraphDao(CassandraDatabase dbSource, IdGenerator idGenerator) { + super(dbSource, idGenerator); + } + + @Override + public Class getType() { + return Graph.class; + } + + @Override + public Graph create(Graph graph) throws GroundException { + super.verifyItemNotExists(graph.getSourceKey()); + + CassandraStatements statements; + long uniqueId = idGenerator.generateItemId(); + Graph newGraph = new Graph(uniqueId, graph); + + try { + statements = super.insert(newGraph); + + String name = graph.getName(); + if (name != null) { + statements.append(String.format(CqlConstants.INSERT_GENERIC_ITEM_WITH_NAME, "graph", uniqueId, graph.getSourceKey(), name)); + } else { + statements.append(String.format(CqlConstants.INSERT_GENERIC_ITEM_WITHOUT_NAME, "graph", uniqueId, graph.getSourceKey())); + } + } catch (Exception e) { + throw new GroundException(e); + } + + CassandraUtils.executeCqlList(dbSource, statements); + return newGraph; + } + + @Override + public List getLeaves(String sourceKey) throws GroundException { + Graph graph = retrieveFromDatabase(sourceKey); + return super.getLeaves(graph.getId()); + } + + @Override + public Map getHistory(String sourceKey) throws GroundException { + Graph graph = retrieveFromDatabase(sourceKey); + return super.getHistory(graph.getId()); + } + + @Override + public void truncate(long itemId, int numLevels) throws GroundException { + super.truncate(itemId, numLevels); + } +} diff --git a/modules/cassandra/app/edu/berkeley/ground/cassandra/dao/core/CassandraGraphVersionDao.java b/modules/cassandra/app/edu/berkeley/ground/cassandra/dao/core/CassandraGraphVersionDao.java new file mode 100644 index 00000000..a53f8ce1 --- /dev/null +++ b/modules/cassandra/app/edu/berkeley/ground/cassandra/dao/core/CassandraGraphVersionDao.java @@ -0,0 +1,87 @@ +package edu.berkeley.ground.cassandra.dao.core; + +import com.fasterxml.jackson.databind.JsonNode; +import edu.berkeley.ground.common.dao.core.GraphVersionDao; +import edu.berkeley.ground.common.exception.GroundException; +import edu.berkeley.ground.common.exception.GroundException.ExceptionType; +import edu.berkeley.ground.common.model.core.GraphVersion; +import edu.berkeley.ground.common.model.core.RichVersion; +import edu.berkeley.ground.common.util.IdGenerator; +import edu.berkeley.ground.cassandra.dao.CqlConstants; +import edu.berkeley.ground.cassandra.util.CassandraDatabase; +import edu.berkeley.ground.cassandra.util.CassandraStatements; +import edu.berkeley.ground.cassandra.util.CassandraUtils; +import java.util.ArrayList; +import java.util.List; +import play.libs.Json; + + +public class CassandraGraphVersionDao extends CassandraRichVersionDao implements GraphVersionDao { + + private CassandraGraphDao cassandraGraphDao; + + public CassandraGraphVersionDao(CassandraDatabase dbSource, IdGenerator idGenerator) { + super(dbSource, idGenerator); + this.cassandraGraphDao = new CassandraGraphDao(dbSource, idGenerator); + } + + @Override + public GraphVersion create(final GraphVersion graphVersion, List parentIds) + throws GroundException { + + final long uniqueId = idGenerator.generateVersionId(); + GraphVersion newGraphVersion = new GraphVersion(uniqueId, graphVersion); + + CassandraStatements updateVersionList = this.cassandraGraphDao.update(newGraphVersion.getGraphId(), newGraphVersion.getId(), parentIds); + + try { + CassandraStatements statements = super.insert(newGraphVersion); + statements.append(String.format(CqlConstants.INSERT_GRAPH_VERSION, uniqueId, graphVersion.getGraphId())); + statements.merge(updateVersionList); + + for (Long id : newGraphVersion.getEdgeVersionIds()) { + statements.append(String.format(CqlConstants.INSERT_GRAPH_VERSION_EDGE, newGraphVersion.getId(), id)); + } + + CassandraUtils.executeCqlList(dbSource, statements); + } catch (Exception e) { + throw new GroundException(e); + } + return newGraphVersion; + } + + @Override + public CassandraStatements delete(long id) { + CassandraStatements statements = new CassandraStatements(); + statements.append(String.format(CqlConstants.DELETE_ALL_GRAPH_VERSION_EDGES, "graph_version_edge", "graph_version_id", id)); + statements.append(String.format(CqlConstants.DELETE_BY_ID, "graph_version", id)); + + CassandraStatements superStatements = super.delete(id); + superStatements.merge(statements); + return superStatements; + } + + @Override + public GraphVersion retrieveFromDatabase(long id) throws GroundException { + String cql = String.format(CqlConstants.SELECT_STAR_BY_ID, "graph_version", id); + JsonNode json = Json.parse(CassandraUtils.executeQueryToJson(dbSource, cql)); + + if (json.size() == 0) { + throw new GroundException(ExceptionType.VERSION_NOT_FOUND, this.getType().getSimpleName(), String.format("%d", id)); + } + + GraphVersion graphVersion = Json.fromJson(json.get(0), GraphVersion.class); + List edgeIds = new ArrayList<>(); + cql = String.format(CqlConstants.SELECT_GRAPH_VERSION_EDGES, id); + + JsonNode edgeJson = Json.parse(CassandraUtils.executeQueryToJson(dbSource, cql)); + for (JsonNode edge : edgeJson) { + edgeIds.add(edge.get("edgeVersionId").asLong()); + } + + RichVersion richVersion = super.retrieveFromDatabase(id); + return new GraphVersion(id, richVersion.getTags(), richVersion.getStructureVersionId(), richVersion.getReference(), richVersion.getParameters(), + graphVersion.getGraphId(), edgeIds); + } +} + diff --git a/modules/cassandra/app/edu/berkeley/ground/cassandra/dao/core/CassandraNodeDao.java b/modules/cassandra/app/edu/berkeley/ground/cassandra/dao/core/CassandraNodeDao.java new file mode 100644 index 00000000..f3818d97 --- /dev/null +++ b/modules/cassandra/app/edu/berkeley/ground/cassandra/dao/core/CassandraNodeDao.java @@ -0,0 +1,70 @@ +package edu.berkeley.ground.cassandra.dao.core; + +import edu.berkeley.ground.common.dao.core.NodeDao; +import edu.berkeley.ground.common.exception.GroundException; +import edu.berkeley.ground.common.model.core.Node; +import edu.berkeley.ground.common.util.IdGenerator; +import edu.berkeley.ground.cassandra.dao.CqlConstants; +import edu.berkeley.ground.cassandra.dao.version.CassandraItemDao; +import edu.berkeley.ground.cassandra.util.CassandraDatabase; +import edu.berkeley.ground.cassandra.util.CassandraStatements; +import edu.berkeley.ground.cassandra.util.CassandraUtils; +import java.util.List; +import java.util.Map; + + +public class CassandraNodeDao extends CassandraItemDao implements NodeDao { + + public CassandraNodeDao(CassandraDatabase dbSource, IdGenerator idGenerator) { + super(dbSource, idGenerator); + } + + @Override + public Class getType() { + return Node.class; + } + + @Override + public Node create(Node node) throws GroundException { + super.verifyItemNotExists(node.getSourceKey()); + + CassandraStatements statements; + long uniqueId = idGenerator.generateItemId(); + + Node newNode = new Node(uniqueId, node); + try { + statements = super.insert(newNode); + + String name = node.getName(); + + if (name != null) { + statements.append(String.format(CqlConstants.INSERT_GENERIC_ITEM_WITH_NAME, "node", uniqueId, node.getSourceKey(), name)); + } else { + statements.append(String.format(CqlConstants.INSERT_GENERIC_ITEM_WITHOUT_NAME, "node", uniqueId, node.getSourceKey())); + } + } catch (Exception e) { + throw new GroundException(e); + } + + CassandraUtils.executeCqlList(dbSource, statements); + return newNode; + } + + @Override + public List getLeaves(String sourceKey) throws GroundException { + Node node = this.retrieveFromDatabase(sourceKey); + return super.getLeaves(node.getId()); + } + + @Override + public Map getHistory(String sourceKey) throws GroundException { + Node node = retrieveFromDatabase(sourceKey); + return super.getHistory(node.getId()); + } + + @Override + public void truncate(long itemId, int numLevels) throws GroundException { + super.truncate(itemId, numLevels); + } + +} diff --git a/modules/cassandra/app/edu/berkeley/ground/cassandra/dao/core/CassandraNodeVersionDao.java b/modules/cassandra/app/edu/berkeley/ground/cassandra/dao/core/CassandraNodeVersionDao.java new file mode 100644 index 00000000..63b1ecfb --- /dev/null +++ b/modules/cassandra/app/edu/berkeley/ground/cassandra/dao/core/CassandraNodeVersionDao.java @@ -0,0 +1,84 @@ +package edu.berkeley.ground.cassandra.dao.core; + +import com.fasterxml.jackson.databind.JsonNode; +import edu.berkeley.ground.common.dao.core.NodeVersionDao; +import edu.berkeley.ground.common.exception.GroundException; +import edu.berkeley.ground.common.exception.GroundException.ExceptionType; +import edu.berkeley.ground.common.model.core.NodeVersion; +import edu.berkeley.ground.common.model.core.RichVersion; +import edu.berkeley.ground.common.util.IdGenerator; +import edu.berkeley.ground.cassandra.dao.CqlConstants; +import edu.berkeley.ground.cassandra.util.CassandraDatabase; +import edu.berkeley.ground.cassandra.util.CassandraStatements; +import edu.berkeley.ground.cassandra.util.CassandraUtils; +import java.util.ArrayList; +import java.util.List; +import play.libs.Json; + +public class CassandraNodeVersionDao extends CassandraRichVersionDao implements NodeVersionDao { + + private CassandraNodeDao cassandraNodeDao; + + public CassandraNodeVersionDao(CassandraDatabase dbSource, IdGenerator idGenerator) { + super(dbSource, idGenerator); + this.cassandraNodeDao = new CassandraNodeDao(dbSource, idGenerator); + } + + @Override + public NodeVersion create(final NodeVersion nodeVersion, List parentIds) + throws GroundException { + + final long uniqueId = idGenerator.generateVersionId(); + NodeVersion newNodeVersion = new NodeVersion(uniqueId, nodeVersion); + + CassandraStatements updateVersionList = this.cassandraNodeDao.update(newNodeVersion.getNodeId(), newNodeVersion.getId(), parentIds); + + try { + CassandraStatements statements = super.insert(newNodeVersion); + statements.append(String.format(CqlConstants.INSERT_NODE_VERSION, uniqueId, nodeVersion.getNodeId())); + statements.merge(updateVersionList); + + CassandraUtils.executeCqlList(this.dbSource, statements); + } catch (Exception e) { + e.printStackTrace(); + throw new GroundException(e); + } + return newNodeVersion; + } + + @Override + public CassandraStatements delete(long id) { + CassandraStatements statements = new CassandraStatements(); + statements.append(String.format(CqlConstants.DELETE_BY_ID, "node_version", id)); + + CassandraStatements superStatements = super.delete(id); + superStatements.merge(statements); + return superStatements; + } + + @Override + public NodeVersion retrieveFromDatabase(long id) throws GroundException { + String cql = String.format(CqlConstants.SELECT_STAR_BY_ID, "node_version", id); + JsonNode json = Json.parse(CassandraUtils.executeQueryToJson(this.dbSource, cql)); + + if (json.size() == 0) { + throw new GroundException(ExceptionType.VERSION_NOT_FOUND, this.getType().getSimpleName(), String.format("%d", id)); + } + + NodeVersion nodeVersion = Json.fromJson(json.get(0), NodeVersion.class); + RichVersion richVersion = super.retrieveFromDatabase(id); + + return new NodeVersion(id, richVersion, nodeVersion); + } + + @Override + public List retrieveAdjacentLineageEdgeVersion(long startId) throws GroundException { + String cql = String.format(CqlConstants.SELECT_NODE_VERSION_ADJACENT_LINEAGE, startId); + JsonNode json = Json.parse(CassandraUtils.executeQueryToJson(this.dbSource, cql)); + + List result = new ArrayList<>(); + json.forEach(x -> result.add(x.get("id").asLong())); + + return result; + } +} diff --git a/modules/cassandra/app/edu/berkeley/ground/cassandra/dao/core/CassandraRichVersionDao.java b/modules/cassandra/app/edu/berkeley/ground/cassandra/dao/core/CassandraRichVersionDao.java new file mode 100644 index 00000000..6a9ca2c6 --- /dev/null +++ b/modules/cassandra/app/edu/berkeley/ground/cassandra/dao/core/CassandraRichVersionDao.java @@ -0,0 +1,182 @@ +/** + * 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. + */ +package edu.berkeley.ground.cassandra.dao.core; + +import edu.berkeley.ground.common.dao.core.RichVersionDao; +import edu.berkeley.ground.common.exception.GroundException; +import edu.berkeley.ground.common.exception.GroundException.ExceptionType; +import edu.berkeley.ground.common.model.core.RichVersion; +import edu.berkeley.ground.common.model.core.StructureVersion; +import edu.berkeley.ground.common.model.version.GroundType; +import edu.berkeley.ground.common.model.version.Tag; +import edu.berkeley.ground.common.util.IdGenerator; +import edu.berkeley.ground.cassandra.dao.CqlConstants; +import edu.berkeley.ground.cassandra.dao.version.CassandraTagDao; +import edu.berkeley.ground.cassandra.dao.version.CassandraVersionDao; +import edu.berkeley.ground.cassandra.util.CassandraDatabase; +import edu.berkeley.ground.cassandra.util.CassandraStatements; +import com.datastax.driver.core.Cluster; +import com.datastax.driver.core.ResultSet; +import com.datastax.driver.core.Row; +import com.datastax.driver.core.Session; +import com.datastax.driver.core.exceptions.QueryExecutionException; +import java.util.HashMap; +import java.util.Map; + + +public abstract class CassandraRichVersionDao extends CassandraVersionDao implements RichVersionDao { + + private CassandraTagDao cassandraTagDao; + + public CassandraRichVersionDao(CassandraDatabase dbSource, IdGenerator idGenerator) { + super(dbSource, idGenerator); + this.cassandraTagDao = new CassandraTagDao(dbSource); + } + + @Override + public CassandraStatements insert(final T richVersion) throws GroundException { + long id = richVersion.getId(); + Long structureVersionId; + + if (richVersion.getStructureVersionId() == -1) { + structureVersionId = null; + } else { + CassandraStructureVersionDao cassandraStructureVersionDao = new CassandraStructureVersionDao(dbSource, idGenerator); + StructureVersion structureVersion = cassandraStructureVersionDao.retrieveFromDatabase(richVersion.getStructureVersionId()); + structureVersionId = richVersion.getStructureVersionId(); + checkStructureTags(structureVersion, richVersion.getTags()); + } + + CassandraStatements statements = super.insert(richVersion); + + String reference = richVersion.getReference(); + if (reference != null) { + statements.append(String.format(CqlConstants.INSERT_RICH_VERSION_WITH_REFERENCE, id, structureVersionId, reference)); + } else { + statements.append(String.format(CqlConstants.INSERT_RICH_VERSION_WITHOUT_REFERENCE, id, structureVersionId)); + } + + final Map tags = richVersion.getTags(); + for (String tagKey : tags.keySet()) { + Tag tag = tags.get(tagKey); + + statements.merge(this.cassandraTagDao.insertRichVersionTag(new Tag(id, tag.getKey(), tag.getValue(), tag.getValueType()))); + } + + Map parameters = richVersion.getParameters(); + if (!parameters.isEmpty()) { + for (String key : parameters.keySet()) { + statements.append(String.format(CqlConstants.INSERT_RICH_VERSION_EXTERNAL_PARAMETER, richVersion.getId(), key, parameters.get(key))); + } + } + + return statements; + } + + @Override + public CassandraStatements delete(long id) { + CassandraStatements statements = new CassandraStatements(); + + statements.append(String.format(CqlConstants.DELETE_RICH_VERSION_TAGS, id)); + statements.append(String.format(CqlConstants.DELETE_RICH_EXTERNAL_PARAMETERS, id)); + statements.append(String.format(CqlConstants.DELETE_BY_ID, "rich_version", id)); + + CassandraStatements superStatements = super.delete(id); + superStatements.merge(statements); + return superStatements; + } + + + @Override + public RichVersion retrieveFromDatabase(long id) throws GroundException { + String cql = String.format(CqlConstants.SELECT_STAR_BY_ID, "rich_version", id); + + String reference; + long structureVersionId; + + Session session = this.dbSource.getSession(); + + + try { + ResultSet resultSet = session.execute(cql); + + if (resultSet.isExhausted()) { + throw new GroundException(ExceptionType.VERSION_NOT_FOUND, RichVersion.class.getSimpleName(), String.format("%d", id)); + } + + Row nextRow = resultSet.one(); + + reference = nextRow.getString("reference"); + structureVersionId = nextRow.getLong("structure_version_id"); + + } catch (QueryExecutionException e) { + throw new GroundException(e); + } + + Map tags = this.cassandraTagDao.retrieveFromDatabaseByVersionId(id); + Map referenceParams = getReferenceParameters(id); + structureVersionId = structureVersionId == 0 ? -1 : structureVersionId; + + return new RichVersion(id, tags, structureVersionId, reference, referenceParams); + } + + private Map getReferenceParameters(long id) throws GroundException { + String cql = String.format(CqlConstants.SELECT_RICH_VERSION_EXTERNAL_PARAMETERS, id); + Map referenceParameters = new HashMap<>(); + + Session session = this.dbSource.getSession(); + + ResultSet parameterSet = session.execute(cql); + if (parameterSet.isExhausted()) { + return referenceParameters; + } + + for (Row row: parameterSet.all()) { + referenceParameters.put(row.getString("key"), row.getString("value")); + } + + return referenceParameters; + } + + + /** + * Validate that the given Tags satisfy the StructureVersion's requirements. + * + * @param structureVersion the StructureVersion to check against + * @param tags the provided tags + */ + private static void checkStructureTags(StructureVersion structureVersion, Map tags) + throws GroundException { + + Map structureVersionAttributes = structureVersion.getAttributes(); + + if (tags.isEmpty()) { + throw new GroundException(ExceptionType.OTHER, String.format("No tags were specified even though a StructureVersion (%d) was.", + structureVersion.getId())); + } + + for (String key : structureVersionAttributes.keySet()) { + if (!tags.keySet().contains(key)) { + // check if such a tag exists + throw new GroundException(ExceptionType.OTHER, String.format("No tag with key %s was specified.", key)); + } else if (tags.get(key).getValueType() == null) { + // check that value type is specified + throw new GroundException(ExceptionType.OTHER, String.format("Tag with key %s did not have a value.", key)); + } else if (!tags.get(key).getValueType().equals(structureVersionAttributes.get(key))) { + // check that the value type is the same + throw new GroundException(ExceptionType.OTHER, String.format( + "Tag with key %s did not have a value of the correct type: expected [%s] but found [%s].", key, structureVersionAttributes.get(key), + tags.get(key).getValueType())); + } + } + } +} diff --git a/modules/cassandra/app/edu/berkeley/ground/cassandra/dao/core/CassandraStructureDao.java b/modules/cassandra/app/edu/berkeley/ground/cassandra/dao/core/CassandraStructureDao.java new file mode 100644 index 00000000..dfb8b516 --- /dev/null +++ b/modules/cassandra/app/edu/berkeley/ground/cassandra/dao/core/CassandraStructureDao.java @@ -0,0 +1,83 @@ +/** + * 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. + */ +package edu.berkeley.ground.cassandra.dao.core; + +import edu.berkeley.ground.common.dao.core.StructureDao; +import edu.berkeley.ground.common.exception.GroundException; +import edu.berkeley.ground.common.model.core.Structure; +import edu.berkeley.ground.common.util.IdGenerator; +import edu.berkeley.ground.cassandra.dao.CqlConstants; +import edu.berkeley.ground.cassandra.dao.version.CassandraItemDao; +import edu.berkeley.ground.cassandra.util.CassandraDatabase; +import edu.berkeley.ground.cassandra.util.CassandraStatements; +import edu.berkeley.ground.cassandra.util.CassandraUtils; +import java.util.List; +import java.util.Map; + + +public class CassandraStructureDao extends CassandraItemDao implements StructureDao { + + public CassandraStructureDao(CassandraDatabase dbSource, IdGenerator idGenerator) { + super(dbSource, idGenerator); + } + + @Override + public Class getType() { + return Structure.class; + } + + @Override + public Structure create(Structure structure) throws GroundException { + super.verifyItemNotExists(structure.getSourceKey()); + + CassandraStatements cassandraStatements; + long uniqueId = idGenerator.generateItemId(); + + Structure newStructure = new Structure(uniqueId, structure); + + try { + cassandraStatements = super.insert(newStructure); + + String name = structure.getName(); + + if (name != null) { + cassandraStatements.append(String.format(CqlConstants.INSERT_GENERIC_ITEM_WITH_NAME, "structure", uniqueId, structure.getSourceKey(), name)); + } else { + cassandraStatements.append(String.format(CqlConstants.INSERT_GENERIC_ITEM_WITHOUT_NAME, "structure", uniqueId, structure.getSourceKey())); + } + } catch (GroundException e) { + throw e; + } catch (Exception e) { + throw new GroundException(e); + } + + CassandraUtils.executeCqlList(dbSource, cassandraStatements); + return newStructure; + } + + @Override + public List getLeaves(String sourceKey) throws GroundException { + Structure structure = retrieveFromDatabase(sourceKey); + return super.getLeaves(structure.getId()); + } + + @Override + public Map getHistory(String sourceKey) throws GroundException { + Structure structure = retrieveFromDatabase(sourceKey); + return super.getHistory(structure.getId()); + } + + @Override + public void truncate(long itemId, int numLevels) throws GroundException { + super.truncate(itemId, numLevels); + } +} diff --git a/modules/cassandra/app/edu/berkeley/ground/cassandra/dao/core/CassandraStructureVersionDao.java b/modules/cassandra/app/edu/berkeley/ground/cassandra/dao/core/CassandraStructureVersionDao.java new file mode 100644 index 00000000..55a6addf --- /dev/null +++ b/modules/cassandra/app/edu/berkeley/ground/cassandra/dao/core/CassandraStructureVersionDao.java @@ -0,0 +1,106 @@ +/** + * 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. + */ +package edu.berkeley.ground.cassandra.dao.core; + +import com.fasterxml.jackson.databind.JsonNode; +import edu.berkeley.ground.common.dao.core.StructureVersionDao; +import edu.berkeley.ground.common.exception.GroundException; +import edu.berkeley.ground.common.exception.GroundException.ExceptionType; +import edu.berkeley.ground.common.model.core.StructureVersion; +import edu.berkeley.ground.common.model.version.GroundType; +import edu.berkeley.ground.common.util.IdGenerator; +import edu.berkeley.ground.cassandra.dao.CqlConstants; +import edu.berkeley.ground.cassandra.dao.version.CassandraVersionDao; +import edu.berkeley.ground.cassandra.util.CassandraDatabase; +import edu.berkeley.ground.cassandra.util.CassandraStatements; +import edu.berkeley.ground.cassandra.util.CassandraUtils; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import play.libs.Json; + +public class CassandraStructureVersionDao extends CassandraVersionDao implements StructureVersionDao { + + private CassandraStructureDao cassandraStructureDao; + + public CassandraStructureVersionDao(CassandraDatabase dbSource, IdGenerator idGenerator) { + super(dbSource, idGenerator); + this.cassandraStructureDao = new CassandraStructureDao(dbSource, idGenerator); + } + + @Override + public final StructureVersion create(final StructureVersion structureVersion, List parentIds) throws GroundException { + + long uniqueId = idGenerator.generateItemId(); + StructureVersion newStructureVersion = new StructureVersion(uniqueId, structureVersion); + CassandraStatements updateVersionList = this.cassandraStructureDao + .update(newStructureVersion.getStructureId(), newStructureVersion.getId(), parentIds); + + try { + CassandraStatements statements = super.insert(newStructureVersion); + statements.append(String.format(CqlConstants.INSERT_STRUCTURE_VERSION, uniqueId, structureVersion.getStructureId())); + + for (Map.Entry attribute : structureVersion.getAttributes().entrySet()) { + statements.append(String.format(CqlConstants.INSERT_STRUCTURE_VERSION_ATTRIBUTE, uniqueId, attribute.getKey(), attribute.getValue())); + } + + statements.merge(updateVersionList); + + CassandraUtils.executeCqlList(dbSource, statements); + } catch (Exception e) { + throw new GroundException(e); + } + + return newStructureVersion; + } + + @Override + public CassandraStatements delete(long id) { + CassandraStatements statements = new CassandraStatements(); + statements.append(String.format(CqlConstants.DELETE_STRUCTURE_VERSION_ATTRIBUTES, id)); + statements.append(String.format(CqlConstants.DELETE_BY_ID, "structure_version", id)); + + CassandraStatements superStatements = super.delete(id); + superStatements.merge(statements); + return superStatements; + } + + @Override + public StructureVersion retrieveFromDatabase(final long id) throws GroundException { + HashMap attributes; + try { + String resultQuery = String.format(CqlConstants.SELECT_STAR_BY_ID, "structure_version", id); + JsonNode resultJson = Json.parse(CassandraUtils.executeQueryToJson(dbSource, resultQuery)); + + if (resultJson.size() == 0) { + throw new GroundException(ExceptionType.VERSION_NOT_FOUND, this.getType().getSimpleName(), String.format("%d", id)); + } + + StructureVersion structureVersion = Json.fromJson(resultJson.get(0), StructureVersion.class); + + String attributeQuery = String.format(CqlConstants.SELECT_STRUCTURE_VERSION_ATTRIBUTES, id); + JsonNode attributeJson = Json.parse(CassandraUtils.executeQueryToJson(dbSource, attributeQuery)); + + attributes = new HashMap<>(); + + for (JsonNode attribute : attributeJson) { + GroundType type = GroundType.fromString(attribute.get("type").asText()); + attributes.put(attribute.get("key").asText(), type); + } + + structureVersion = new StructureVersion(structureVersion.getId(), structureVersion.getStructureId(), attributes); + return structureVersion; + } catch (Exception e) { + throw new GroundException(e); + } + } +} diff --git a/modules/cassandra/app/edu/berkeley/ground/cassandra/dao/usage/CassandraLineageEdgeDao.java b/modules/cassandra/app/edu/berkeley/ground/cassandra/dao/usage/CassandraLineageEdgeDao.java new file mode 100644 index 00000000..30d0d0d4 --- /dev/null +++ b/modules/cassandra/app/edu/berkeley/ground/cassandra/dao/usage/CassandraLineageEdgeDao.java @@ -0,0 +1,82 @@ +/** + * 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. + */ +package edu.berkeley.ground.cassandra.dao.usage; + +import edu.berkeley.ground.common.dao.usage.LineageEdgeDao; +import edu.berkeley.ground.common.exception.GroundException; +import edu.berkeley.ground.common.model.usage.LineageEdge; +import edu.berkeley.ground.common.util.IdGenerator; +import edu.berkeley.ground.cassandra.dao.CqlConstants; +import edu.berkeley.ground.cassandra.dao.version.CassandraItemDao; +import edu.berkeley.ground.cassandra.util.CassandraDatabase; +import edu.berkeley.ground.cassandra.util.CassandraStatements; +import edu.berkeley.ground.cassandra.util.CassandraUtils; +import java.util.List; +import java.util.Map; + + +public class CassandraLineageEdgeDao extends CassandraItemDao implements LineageEdgeDao { + + public CassandraLineageEdgeDao(CassandraDatabase dbSource, IdGenerator idGenerator) { + super(dbSource, idGenerator); + } + + @Override + public Class getType() { + return LineageEdge.class; + } + + @Override + public LineageEdge create(LineageEdge lineageEdge) throws GroundException { + super.verifyItemNotExists(lineageEdge.getSourceKey()); + + long uniqueId = this.idGenerator.generateItemId(); + LineageEdge newLineageEdge = new LineageEdge(uniqueId, lineageEdge); + CassandraStatements statements = super.insert(newLineageEdge); + + String name = lineageEdge.getName(); + + if (name != null) { + statements.append(String.format(CqlConstants.INSERT_GENERIC_ITEM_WITH_NAME, "lineage_edge", newLineageEdge.getId(), + newLineageEdge.getSourceKey(), name)); + } else { + statements.append(String.format(CqlConstants.INSERT_GENERIC_ITEM_WITHOUT_NAME, "lineage_edge", newLineageEdge.getId(), + newLineageEdge.getSourceKey())); + } + + try { + CassandraUtils.executeCqlList(this.dbSource, statements); + return newLineageEdge; + } catch (Exception e) { + throw new GroundException(e); + } + } + + @Override + public List getLeaves(String sourceKey) throws GroundException { + LineageEdge lineageEdge = retrieveFromDatabase(sourceKey); + return super.getLeaves(lineageEdge.getId()); + } + + @Override + public Map getHistory(String sourceKey) throws GroundException { + LineageEdge lineageEdge = retrieveFromDatabase(sourceKey); + return super.getHistory(lineageEdge.getId()); + } + + @Override + public void truncate(long itemId, int numLevels) throws GroundException { + super.truncate(itemId, numLevels); + } + +} + diff --git a/modules/cassandra/app/edu/berkeley/ground/cassandra/dao/usage/CassandraLineageEdgeVersionDao.java b/modules/cassandra/app/edu/berkeley/ground/cassandra/dao/usage/CassandraLineageEdgeVersionDao.java new file mode 100644 index 00000000..fda425ec --- /dev/null +++ b/modules/cassandra/app/edu/berkeley/ground/cassandra/dao/usage/CassandraLineageEdgeVersionDao.java @@ -0,0 +1,87 @@ +/** + * 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. + */ +package edu.berkeley.ground.cassandra.dao.usage; + +import com.fasterxml.jackson.databind.JsonNode; +import edu.berkeley.ground.common.dao.usage.LineageEdgeVersionDao; +import edu.berkeley.ground.common.exception.GroundException; +import edu.berkeley.ground.common.exception.GroundException.ExceptionType; +import edu.berkeley.ground.common.model.core.RichVersion; +import edu.berkeley.ground.common.model.usage.LineageEdgeVersion; +import edu.berkeley.ground.common.util.IdGenerator; +import edu.berkeley.ground.cassandra.dao.CqlConstants; +import edu.berkeley.ground.cassandra.dao.core.CassandraRichVersionDao; +import edu.berkeley.ground.cassandra.util.CassandraDatabase; +import edu.berkeley.ground.cassandra.util.CassandraStatements; +import edu.berkeley.ground.cassandra.util.CassandraUtils; +import java.util.List; +import play.libs.Json; + + +public class CassandraLineageEdgeVersionDao extends CassandraRichVersionDao implements LineageEdgeVersionDao { + + private CassandraLineageEdgeDao cassandraLineageEdgeDao; + + public CassandraLineageEdgeVersionDao(CassandraDatabase dbSource, IdGenerator idGenerator) { + super(dbSource, idGenerator); + this.cassandraLineageEdgeDao = new CassandraLineageEdgeDao(dbSource, idGenerator); + } + + @Override + public LineageEdgeVersion create(final LineageEdgeVersion lineageEdgeVersion, + List parentIds) throws GroundException { + final long uniqueId = idGenerator.generateVersionId(); + + LineageEdgeVersion newLineageEdgeVersion = new LineageEdgeVersion(uniqueId, lineageEdgeVersion); + + CassandraStatements updateVersionList = this.cassandraLineageEdgeDao + .update(newLineageEdgeVersion.getLineageEdgeId(), newLineageEdgeVersion.getId(), parentIds); + + try { + CassandraStatements statements = super.insert(newLineageEdgeVersion); + statements.append(String.format(CqlConstants.INSERT_LINEAGE_EDGE_VERSION, uniqueId, newLineageEdgeVersion.getLineageEdgeId(), + newLineageEdgeVersion.getFromId(), newLineageEdgeVersion.getToId(), null)); + statements.merge(updateVersionList); + + CassandraUtils.executeCqlList(dbSource, statements); + } catch (Exception e) { + throw new GroundException(e); + } + return newLineageEdgeVersion; + } + + @Override + public CassandraStatements delete(long id) { + CassandraStatements statements = new CassandraStatements(); + statements.append(String.format(CqlConstants.DELETE_BY_ID, "lineage_edge_version", id)); + + CassandraStatements superStatements = super.delete(id); + superStatements.merge(statements); + return superStatements; + } + + + @Override + public LineageEdgeVersion retrieveFromDatabase(long id) throws GroundException { + String cql = String.format(CqlConstants.SELECT_STAR_BY_ID, "lineage_edge_version", id); + JsonNode json = Json.parse(CassandraUtils.executeQueryToJson(dbSource, cql)); + + if (json.size() == 0) { + throw new GroundException(ExceptionType.VERSION_NOT_FOUND, this.getType().getSimpleName(), String.format("%d", id)); + } + + LineageEdgeVersion lineageEdgeVersion = Json.fromJson(json.get(0), LineageEdgeVersion.class); + RichVersion richVersion = super.retrieveFromDatabase(id); + + return new LineageEdgeVersion(id, richVersion, lineageEdgeVersion); + } +} diff --git a/modules/cassandra/app/edu/berkeley/ground/cassandra/dao/usage/CassandraLineageGraphDao.java b/modules/cassandra/app/edu/berkeley/ground/cassandra/dao/usage/CassandraLineageGraphDao.java new file mode 100644 index 00000000..5e917359 --- /dev/null +++ b/modules/cassandra/app/edu/berkeley/ground/cassandra/dao/usage/CassandraLineageGraphDao.java @@ -0,0 +1,80 @@ +/** + * 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. + */ +package edu.berkeley.ground.cassandra.dao.usage; + +import edu.berkeley.ground.common.dao.usage.LineageGraphDao; +import edu.berkeley.ground.common.exception.GroundException; +import edu.berkeley.ground.common.model.usage.LineageGraph; +import edu.berkeley.ground.common.util.IdGenerator; +import edu.berkeley.ground.cassandra.dao.CqlConstants; +import edu.berkeley.ground.cassandra.dao.version.CassandraItemDao; +import edu.berkeley.ground.cassandra.util.CassandraDatabase; +import edu.berkeley.ground.cassandra.util.CassandraStatements; +import edu.berkeley.ground.cassandra.util.CassandraUtils; +import java.util.List; +import java.util.Map; + + +public class CassandraLineageGraphDao extends CassandraItemDao implements LineageGraphDao { + + public CassandraLineageGraphDao(CassandraDatabase dbSource, IdGenerator idGenerator) { + super(dbSource, idGenerator); + } + + @Override + public Class getType() { + return LineageGraph.class; + } + + @Override + public LineageGraph create(LineageGraph lineageGraph) throws GroundException { + super.verifyItemNotExists(lineageGraph.getSourceKey()); + + long uniqueId = idGenerator.generateItemId(); + LineageGraph newLineageGraph = new LineageGraph(uniqueId, lineageGraph); + + CassandraStatements statements = super.insert(newLineageGraph); + + String name = lineageGraph.getName(); + if (name != null) { + statements.append(String.format(CqlConstants.INSERT_GENERIC_ITEM_WITH_NAME, "lineage_graph", newLineageGraph.getId(), + newLineageGraph.getSourceKey(), name)); + } else { + statements.append(String.format(CqlConstants.INSERT_GENERIC_ITEM_WITHOUT_NAME, "lineage_graph", newLineageGraph.getId(), + newLineageGraph.getSourceKey())); + } + + try { + CassandraUtils.executeCqlList(dbSource, statements); + return newLineageGraph; + } catch (Exception e) { + throw new GroundException(e); + } + } + + @Override + public List getLeaves(String sourceKey) throws GroundException { + LineageGraph lineageGraph = retrieveFromDatabase(sourceKey); + return super.getLeaves(lineageGraph.getId()); + } + + @Override + public Map getHistory(String sourceKey) throws GroundException { + LineageGraph lineageGraph = retrieveFromDatabase(sourceKey); + return super.getHistory(lineageGraph.getId()); + } + + @Override + public void truncate(long itemId, int numLevels) throws GroundException { + super.truncate(itemId, numLevels); + } +} diff --git a/modules/cassandra/app/edu/berkeley/ground/cassandra/dao/usage/CassandraLineageGraphVersionDao.java b/modules/cassandra/app/edu/berkeley/ground/cassandra/dao/usage/CassandraLineageGraphVersionDao.java new file mode 100644 index 00000000..8c5b2f06 --- /dev/null +++ b/modules/cassandra/app/edu/berkeley/ground/cassandra/dao/usage/CassandraLineageGraphVersionDao.java @@ -0,0 +1,102 @@ +/** + * 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. + */ +package edu.berkeley.ground.cassandra.dao.usage; + +import com.fasterxml.jackson.databind.JsonNode; +import edu.berkeley.ground.common.dao.usage.LineageGraphVersionDao; +import edu.berkeley.ground.common.exception.GroundException; +import edu.berkeley.ground.common.exception.GroundException.ExceptionType; +import edu.berkeley.ground.common.model.core.RichVersion; +import edu.berkeley.ground.common.model.usage.LineageGraphVersion; +import edu.berkeley.ground.common.util.IdGenerator; +import edu.berkeley.ground.cassandra.dao.CqlConstants; +import edu.berkeley.ground.cassandra.dao.core.CassandraRichVersionDao; +import edu.berkeley.ground.cassandra.util.CassandraDatabase; +import edu.berkeley.ground.cassandra.util.CassandraStatements; +import edu.berkeley.ground.cassandra.util.CassandraUtils; +import java.util.ArrayList; +import java.util.List; +import play.libs.Json; + + +public class CassandraLineageGraphVersionDao extends CassandraRichVersionDao implements LineageGraphVersionDao { + + private CassandraLineageGraphDao cassandraLineageGraphDao; + + public CassandraLineageGraphVersionDao(CassandraDatabase dbSource, IdGenerator idGenerator) { + super(dbSource, idGenerator); + this.cassandraLineageGraphDao = new CassandraLineageGraphDao(dbSource, idGenerator); + } + + @Override + public LineageGraphVersion create(LineageGraphVersion lineageGraphVersion, List parentIds) + throws GroundException { + final long uniqueId = idGenerator.generateVersionId(); + LineageGraphVersion newLineageGraphVersion = new LineageGraphVersion(uniqueId, lineageGraphVersion); + + CassandraStatements updateVersionList = this.cassandraLineageGraphDao + .update(newLineageGraphVersion.getLineageGraphId(), newLineageGraphVersion.getId(), + parentIds); + + try { + CassandraStatements statements = super.insert(newLineageGraphVersion); + statements.append(String.format(CqlConstants.INSERT_LINEAGE_GRAPH_VERSION, uniqueId, newLineageGraphVersion.getLineageGraphId())); + + statements.merge(updateVersionList); + + for (Long id : newLineageGraphVersion.getLineageEdgeVersionIds()) { + statements.append(String.format(CqlConstants.INSERT_LINEAGE_GRAPH_VERSION_EDGE, newLineageGraphVersion.getId(), id)); + } + + CassandraUtils.executeCqlList(dbSource, statements); + } catch (Exception e) { + throw new GroundException(e); + } + + return newLineageGraphVersion; + } + + @Override + public CassandraStatements delete(long id) { + CassandraStatements statements = new CassandraStatements(); + statements.append(String.format(CqlConstants.DELETE_ALL_GRAPH_VERSION_EDGES, "lineage_graph_version_edge", "lineage_graph_version_id", id)); + statements.append(String.format(CqlConstants.DELETE_BY_ID, "graph_version", id)); + + CassandraStatements superStatements = super.delete(id); + superStatements.merge(statements); + return superStatements; + } + + @Override + public LineageGraphVersion retrieveFromDatabase(long id) throws GroundException { + String cql = String.format(CqlConstants.SELECT_STAR_BY_ID, "lineage_graph_version", id); + JsonNode json = Json.parse(CassandraUtils.executeQueryToJson(dbSource, cql)); + + if (json.size() == 0) { + throw new GroundException(ExceptionType.VERSION_NOT_FOUND, this.getType().getSimpleName(), String.format("%d", id)); + } + + LineageGraphVersion lineageGraphVersion = Json.fromJson(json.get(0), LineageGraphVersion.class); + + List edgeIds = new ArrayList<>(); + cql = String.format(CqlConstants.SELECT_LINEAGE_GRAPH_VERSION_EDGES, id); + + JsonNode edgeJson = Json.parse(CassandraUtils.executeQueryToJson(dbSource, cql)); + for (JsonNode edge : edgeJson) { + edgeIds.add(edge.get("lineageEdgeVersionId").asLong()); + } + + RichVersion richVersion = super.retrieveFromDatabase(id); + return new LineageGraphVersion(id, richVersion.getTags(), richVersion.getStructureVersionId(), richVersion.getReference(), + richVersion.getParameters(), lineageGraphVersion.getLineageGraphId(), edgeIds); + } +} diff --git a/modules/cassandra/app/edu/berkeley/ground/cassandra/dao/version/CassandraItemDao.java b/modules/cassandra/app/edu/berkeley/ground/cassandra/dao/version/CassandraItemDao.java new file mode 100644 index 00000000..1b1a906a --- /dev/null +++ b/modules/cassandra/app/edu/berkeley/ground/cassandra/dao/version/CassandraItemDao.java @@ -0,0 +1,178 @@ +/** + * 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. + */ +package edu.berkeley.ground.cassandra.dao.version; + +import com.fasterxml.jackson.databind.JsonNode; +import com.google.common.base.CaseFormat; +import edu.berkeley.ground.common.dao.version.ItemDao; +import edu.berkeley.ground.common.exception.GroundException; +import edu.berkeley.ground.common.exception.GroundException.ExceptionType; +import edu.berkeley.ground.common.model.version.Item; +import edu.berkeley.ground.common.model.version.Tag; +import edu.berkeley.ground.common.model.version.VersionHistoryDag; +import edu.berkeley.ground.common.util.IdGenerator; +import edu.berkeley.ground.cassandra.dao.CqlConstants; +import edu.berkeley.ground.cassandra.util.CassandraDatabase; +import edu.berkeley.ground.cassandra.util.CassandraStatements; +import edu.berkeley.ground.cassandra.util.CassandraUtils; +import java.lang.reflect.Constructor; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import play.libs.Json; + + +public abstract class CassandraItemDao implements ItemDao { + + private CassandraVersionHistoryDagDao cassandraVersionHistoryDagDao; + protected CassandraTagDao cassandraTagDao; + protected CassandraDatabase dbSource; + protected IdGenerator idGenerator; + + public CassandraItemDao(CassandraDatabase dbSource, IdGenerator idGenerator) { + this.dbSource = dbSource; + this.idGenerator = idGenerator; + this.cassandraVersionHistoryDagDao = new CassandraVersionHistoryDagDao(dbSource, idGenerator); + this.cassandraTagDao = new CassandraTagDao(dbSource); + } + + @Override + public CassandraStatements insert(final T item) throws GroundException { + long id = item.getId(); + + final List cqlList = new ArrayList<>(); + cqlList.add(String.format(CqlConstants.INSERT_ITEM, id)); + + final Map tags = item.getTags(); + CassandraStatements statements = new CassandraStatements(cqlList); + + if (tags != null) { + for (String key : tags.keySet()) { + Tag tag = tags.get(key); + statements.merge(this.cassandraTagDao.insertItemTag(new Tag(id, tag.getKey(), tag.getValue(), tag.getValueType()))); + } + } + + return new CassandraStatements(cqlList); + } + + @Override + public T retrieveFromDatabase(String sourceKey) throws GroundException { + return this.retrieve(String.format(CqlConstants.SELECT_STAR_BY_SOURCE_KEY, CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, + this.getType().getSimpleName()), sourceKey), sourceKey); + } + + @Override + public T retrieveFromDatabase(long id) throws GroundException { + return this.retrieve(String.format(CqlConstants.SELECT_STAR_ITEM_BY_ID, CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, + this.getType().getSimpleName()), id), id); + } + + @Override + public List getLeaves(long itemId) throws GroundException { + try { + VersionHistoryDag dag = this.cassandraVersionHistoryDagDao.retrieveFromDatabase(itemId); + + return dag.getLeaves(); + } catch (GroundException e) { + if (!e.getMessage().contains("No results found for query:")) { + throw e; + } + + return new ArrayList<>(); + } + } + + @Override + public Map getHistory(long itemId) throws GroundException { + try { + VersionHistoryDag dag = this.cassandraVersionHistoryDagDao.retrieveFromDatabase(itemId); + + return dag.getParentChildPairs(); + } catch (GroundException e) { + if (!e.getMessage().contains("No results found for query:")) { + throw e; + } + + return new HashMap<>(); + } + } + + /** + * Add a new Version to this Item. The provided parentIds will be the parents of this particular + * version. What's provided in the default case varies based on which database we are writing + * into. + * + * @param itemId the id of the Item we're updating + * @param childId the new version's id + * @param parentIds the ids of the parents of the child + */ + @Override + public CassandraStatements update(long itemId, long childId, List parentIds) throws GroundException { + + if (parentIds.isEmpty()) { + parentIds.add(0L); + } + + VersionHistoryDag dag = this.cassandraVersionHistoryDagDao.retrieveFromDatabase(itemId); + CassandraStatements statements = new CassandraStatements(); + + for (long parentId : parentIds) { + if (parentId != 0L && !dag.checkItemInDag(parentId)) { + throw new GroundException(ExceptionType.OTHER, String.format("Parent %d is not in Item %d.", parentId, itemId)); + } + + statements.merge(this.cassandraVersionHistoryDagDao.addEdge(dag, parentId, childId, itemId)); + } + + return statements; + } + + /** + * Truncate the item to only have the most recent levels. + * + * @param numLevels the levels to keep + * @throws GroundException an error while removing versions + */ + @Override + public void truncate(long itemId, int numLevels) throws GroundException { + VersionHistoryDag dag; + dag = cassandraVersionHistoryDagDao.retrieveFromDatabase(itemId); + this.cassandraVersionHistoryDagDao.truncate(dag, numLevels, this.getType()); + } + + protected T retrieve(String cql, Object field) throws GroundException { + JsonNode json = Json.parse(CassandraUtils.executeQueryToJson(dbSource, cql)); + + if (json.size() == 0) { + throw new GroundException(ExceptionType.ITEM_NOT_FOUND, this.getType().getSimpleName(), field.toString()); + } + + Class type = this.getType(); + JsonNode itemJson = json.get(0); + long id = itemJson.get("itemId").asLong(); + String name = itemJson.get("name").asText(); + String sourceKey = itemJson.get("sourceKey").asText(); + + Object[] args = {id, name, sourceKey, this.cassandraTagDao.retrieveFromDatabaseByItemId(id)}; + + Constructor constructor; + try { + constructor = type.getConstructor(long.class, String.class, String.class, Map.class); + return constructor.newInstance(args); + } catch (Exception e) { + throw new GroundException(ExceptionType.OTHER, String.format("Catastrophic failure: Unable to instantiate Item.\n%s: %s.", + e.getClass().getName(), e.getMessage())); + } + } +} diff --git a/modules/cassandra/app/edu/berkeley/ground/cassandra/dao/version/CassandraTagDao.java b/modules/cassandra/app/edu/berkeley/ground/cassandra/dao/version/CassandraTagDao.java new file mode 100644 index 00000000..ca4c926b --- /dev/null +++ b/modules/cassandra/app/edu/berkeley/ground/cassandra/dao/version/CassandraTagDao.java @@ -0,0 +1,145 @@ +/** + * 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. + */ +package edu.berkeley.ground.cassandra.dao.version; + +import edu.berkeley.ground.common.dao.version.TagDao; +import edu.berkeley.ground.common.exception.GroundException; +import edu.berkeley.ground.common.exception.GroundException.ExceptionType; +import edu.berkeley.ground.common.model.version.GroundType; +import edu.berkeley.ground.common.model.version.Tag; +import edu.berkeley.ground.cassandra.dao.CqlConstants; +import edu.berkeley.ground.cassandra.util.CassandraDatabase; +import edu.berkeley.ground.cassandra.util.CassandraStatements; +import com.datastax.driver.core.Cluster; +import com.datastax.driver.core.ResultSet; +import com.datastax.driver.core.Row; +import com.datastax.driver.core.Session; +import com.datastax.driver.core.exceptions.QueryExecutionException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class CassandraTagDao implements TagDao { + + private CassandraDatabase dbSource; + + public CassandraTagDao(CassandraDatabase dbSource) { + this.dbSource = dbSource; + } + + @Override + public CassandraStatements insertItemTag(final Tag tag) { + List cqlList = new ArrayList<>(); + if (tag.getValue() != null) { + cqlList.add(String.format(CqlConstants.INSERT_ITEM_TAG_WITH_VALUE, tag.getId(), tag.getKey(), tag.getValue(), tag.getValueType())); + } else { + cqlList.add( + String.format(CqlConstants.INSERT_ITEM_TAG_NO_VALUE, tag.getId(), tag.getKey())); + } + return new CassandraStatements(cqlList); + } + + @Override + public CassandraStatements insertRichVersionTag(final Tag tag) { + List cqlList = new ArrayList<>(); + if (tag.getValue() != null) { + cqlList.add(String.format(CqlConstants.INSERT_RICH_VERSION_TAG_WITH_VALUE, tag.getId(), tag.getKey(), tag.getValue(), tag.getValueType())); + } else { + cqlList.add(String.format(CqlConstants.INSERT_RICH_VERSION_TAG_NO_VALUE, tag.getId(), tag.getKey())); + } + + return new CassandraStatements(cqlList); + } + + @Override + public Map retrieveFromDatabaseByVersionId(long id) throws GroundException { + String cql = String.format(CqlConstants.SELECT_RICH_VERSION_TAGS, id); + return this.retrieveFromDatabaseById(id, cql); + } + + @Override + public Map retrieveFromDatabaseByItemId(long id) throws GroundException { + String cql = String.format(CqlConstants.SELECT_ITEM_TAGS, id); + return this.retrieveFromDatabaseById(id, cql); + } + + private Map retrieveFromDatabaseById(long id, String cql) throws GroundException { + Map results = new HashMap<>(); + + Session session = this.dbSource.getSession(); + + ResultSet resultSet = session.execute(cql); + for (Row row: resultSet.all()) { + String key = row.getString("key"); + + // these methods will return null if the input is null, so there's no need to check + GroundType type = GroundType.fromString(row.getString("type")); + Object value = this.getValue(type, row, "value"); + + results.put(key, new Tag(id, key, value, type)); + } + + return results; + } + + @Override + public List getVersionIdsByTag(String tag) throws GroundException { + String cql = String.format(CqlConstants.SELECT_RICH_VERSION_TAGS_BY_KEY, tag); + return this.getIdsByTag(cql, "rich_version_id"); + } + + @Override + public List getItemIdsByTag(String tag) throws GroundException { + String cql = String.format(CqlConstants.SELECT_ITEM_TAGS_BY_KEY, tag); + return this.getIdsByTag(cql, "item_id"); + } + + private List getIdsByTag(String cql, String idColumn) throws GroundException { + List result = new ArrayList<>(); + + Session session = this.dbSource.getSession(); + + try { + ResultSet resultSet = session.execute(cql); + for (Row row: resultSet.all()) { + result.add(row.getLong(idColumn)); + } + } catch (QueryExecutionException e) { + throw new GroundException(e); + } + + return result; + } + + private Object getValue(GroundType type, Row row, String columnName) throws GroundException { + + if (type == null) { + return null; + } + + + switch (type) { + case STRING: + return row.getString(columnName); + case INTEGER: + return Integer.valueOf(row.getString(columnName)); + case LONG: + return Long.valueOf(row.getString(columnName)); + case BOOLEAN: + return Boolean.valueOf(row.getString(columnName)); + default: + // this should never happen because we've listed all types + throw new GroundException(ExceptionType.OTHER, String.format("Unidentified type: %s", type)); + } + } +} diff --git a/modules/cassandra/app/edu/berkeley/ground/cassandra/dao/version/CassandraVersionDao.java b/modules/cassandra/app/edu/berkeley/ground/cassandra/dao/version/CassandraVersionDao.java new file mode 100644 index 00000000..a4aad143 --- /dev/null +++ b/modules/cassandra/app/edu/berkeley/ground/cassandra/dao/version/CassandraVersionDao.java @@ -0,0 +1,47 @@ +/** + * 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. + */ +package edu.berkeley.ground.cassandra.dao.version; + +import edu.berkeley.ground.common.dao.version.VersionDao; +import edu.berkeley.ground.common.exception.GroundException; +import edu.berkeley.ground.common.model.version.Version; +import edu.berkeley.ground.common.util.IdGenerator; +import edu.berkeley.ground.cassandra.dao.CqlConstants; +import edu.berkeley.ground.cassandra.util.CassandraDatabase; +import edu.berkeley.ground.cassandra.util.CassandraStatements; + +public abstract class CassandraVersionDao implements VersionDao { + + protected CassandraDatabase dbSource; + protected IdGenerator idGenerator; + + public CassandraVersionDao(CassandraDatabase dbSource, IdGenerator idGenerator) { + this.dbSource = dbSource; + this.idGenerator = idGenerator; + } + + @Override + public CassandraStatements insert(T version) throws GroundException { + CassandraStatements statements = new CassandraStatements(); + + statements.append(String.format(CqlConstants.INSERT_VERSION, version.getId())); + return statements; + } + + @Override + public CassandraStatements delete(long id) { + CassandraStatements statements = new CassandraStatements(); + statements.append(String.format(CqlConstants.DELETE_BY_ID, "version", id)); + + return statements; + } +} diff --git a/modules/cassandra/app/edu/berkeley/ground/cassandra/dao/version/CassandraVersionHistoryDagDao.java b/modules/cassandra/app/edu/berkeley/ground/cassandra/dao/version/CassandraVersionHistoryDagDao.java new file mode 100644 index 00000000..5ba9a9c8 --- /dev/null +++ b/modules/cassandra/app/edu/berkeley/ground/cassandra/dao/version/CassandraVersionHistoryDagDao.java @@ -0,0 +1,164 @@ +/** + * 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. + */ +package edu.berkeley.ground.cassandra.dao.version; + +import edu.berkeley.ground.common.dao.version.VersionHistoryDagDao; +import edu.berkeley.ground.common.exception.GroundException; +import edu.berkeley.ground.common.model.version.Item; +import edu.berkeley.ground.common.model.version.VersionHistoryDag; +import edu.berkeley.ground.common.model.version.VersionSuccessor; +import edu.berkeley.ground.common.util.IdGenerator; +import edu.berkeley.ground.cassandra.dao.CqlConstants; +import edu.berkeley.ground.cassandra.util.GroundUtils; +import edu.berkeley.ground.cassandra.util.CassandraDatabase; +import edu.berkeley.ground.cassandra.util.CassandraStatements; +import edu.berkeley.ground.cassandra.util.CassandraUtils; +import com.datastax.driver.core.Cluster; +import com.datastax.driver.core.ResultSet; +import com.datastax.driver.core.Row; +import com.datastax.driver.core.Session; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class CassandraVersionHistoryDagDao implements VersionHistoryDagDao { + + private CassandraVersionSuccessorDao cassandraVersionSuccessorDao; + private CassandraDatabase dbSource; + private IdGenerator idGenerator; + + public CassandraVersionHistoryDagDao(CassandraDatabase dbSource, IdGenerator idGenerator) { + this.dbSource = dbSource; + this.idGenerator = idGenerator; + + this.cassandraVersionSuccessorDao = new CassandraVersionSuccessorDao(this.dbSource, this.idGenerator); + } + + @Override + public VersionHistoryDag create(long itemId) throws GroundException { + return new VersionHistoryDag(itemId, new ArrayList<>()); + } + + /** + * Retrieve a DAG from the database. + * + * @param itemId the id of the item whose dag we are retrieving + * @return the retrieved DAG + * @throws GroundException an error retrieving the DAG + */ + @Override + public VersionHistoryDag retrieveFromDatabase(long itemId) throws GroundException { + String cql = String.format(CqlConstants.SELECT_VERSION_HISTORY_DAG, itemId); + + List edges = new ArrayList<>(); + + Session session = this.dbSource.getSession(); + + final ResultSet resultSet = session.execute(cql); + List successors = new ArrayList<>(); + for (Row row: resultSet.all()) { + successors.add(row.getLong("version_successor_id")); + } + + for (Long versionSuccessorId : successors) { + VersionSuccessor versionSuccessor = cassandraVersionSuccessorDao.retrieveFromDatabase(versionSuccessorId); + edges.add(versionSuccessor); + } + + return new VersionHistoryDag(itemId, edges); + } + + /** + * Add an edge to the DAG. + * + * @param dag the DAG to update + * @param parentId the parent's id + * @param childId the child's id + * @param itemId the id of the Item whose DAG we're updating + * @throws GroundException an error adding the edge + */ + @Override + public CassandraStatements addEdge(VersionHistoryDag dag, long parentId, long childId, long itemId) throws GroundException { + VersionSuccessor successor = this.cassandraVersionSuccessorDao.instantiateVersionSuccessor(parentId, childId); + dag.addEdge(parentId, childId, successor.getId()); + + CassandraStatements statements = cassandraVersionSuccessorDao.insert(successor); + statements.append(String.format(CqlConstants.INSERT_VERSION_HISTORY_DAG_EDGE, itemId, successor.getId())); + return statements; + } + + + /** + * Truncate the DAG to only have a certain number of levels, removing everything before that. + * + * @param dag the DAG to truncate + * @param numLevels the number of levels to keep + */ + @Override + public void truncate(VersionHistoryDag dag, int numLevels, Class itemType) throws GroundException { + + int keptLevels = 1; + List lastLevel = new ArrayList<>(); + List previousLevel = dag.getLeaves(); + + while (keptLevels <= numLevels) { + List currentLevel = new ArrayList<>(); + + previousLevel.forEach(id -> currentLevel.addAll(dag.getParent(id))); + + lastLevel = previousLevel; + previousLevel = currentLevel; + + keptLevels++; + } + + List deleteQueue = new ArrayList<>(new HashSet<>(previousLevel)); + Set deleted = new HashSet<>(); + + CassandraStatements statements = new CassandraStatements(); + + // delete the version successors between the last kept level and the first deleted level + for (long id : lastLevel) { + this.cassandraVersionSuccessorDao.deleteFromDestination(statements, id, dag.getItemId()); + this.addEdge(dag, 0, id, dag.getItemId()); + } + + while (deleteQueue.size() > 0) { + long id = deleteQueue.get(0); + + if (id != 0) { + this.cassandraVersionSuccessorDao.deleteFromDestination(statements, id, dag.getItemId()); + GroundUtils.getVersionDaoFromItemType(itemType, this.dbSource, this.idGenerator).delete(id); + + deleted.add(id); + List parents = dag.getParent(id); + + parents.forEach(parentId -> { + if (!deleted.contains(parentId)) { + deleteQueue.add(parentId); + } + }); + } + + deleteQueue.remove(0); + } + + for (long id : lastLevel) { + statements.merge(this.addEdge(dag, 0, id, dag.getItemId())); + } + + CassandraUtils.executeCqlList(dbSource, statements); + } +} diff --git a/modules/cassandra/app/edu/berkeley/ground/cassandra/dao/version/CassandraVersionSuccessorDao.java b/modules/cassandra/app/edu/berkeley/ground/cassandra/dao/version/CassandraVersionSuccessorDao.java new file mode 100644 index 00000000..30ffa231 --- /dev/null +++ b/modules/cassandra/app/edu/berkeley/ground/cassandra/dao/version/CassandraVersionSuccessorDao.java @@ -0,0 +1,145 @@ +/** + * 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. + */ + +package edu.berkeley.ground.cassandra.dao.version; + +import com.fasterxml.jackson.databind.JsonNode; +import edu.berkeley.ground.common.dao.version.VersionSuccessorDao; +import edu.berkeley.ground.common.exception.GroundException; +import edu.berkeley.ground.common.exception.GroundException.ExceptionType; +import edu.berkeley.ground.common.model.version.VersionSuccessor; +import edu.berkeley.ground.common.util.DbStatements; +import edu.berkeley.ground.common.util.IdGenerator; +import edu.berkeley.ground.cassandra.dao.CqlConstants; +import edu.berkeley.ground.cassandra.util.CassandraDatabase; +import edu.berkeley.ground.cassandra.util.CassandraStatements; +import edu.berkeley.ground.cassandra.util.CassandraUtils; +import com.datastax.driver.core.Cluster; +import com.datastax.driver.core.ResultSet; +import com.datastax.driver.core.Row; +import com.datastax.driver.core.Session; +import com.datastax.driver.core.exceptions.QueryExecutionException; +import play.libs.Json; + + +public class CassandraVersionSuccessorDao { + + private final IdGenerator idGenerator; + private final CassandraDatabase dbSource; + + public CassandraVersionSuccessorDao(CassandraDatabase dbSource, IdGenerator idGenerator) { + this.dbSource = dbSource; + this.idGenerator = idGenerator; + } + + /** + * Create a cqlList containing commands that will persist a new version successor + * + * @param successor the successor to insert + * @return List of CQL expressions to insert version successor + */ + public CassandraStatements insert(VersionSuccessor successor) throws GroundException { + // Check to see if both are valid ids since we don't have foreign key constraints + verifyVersion(successor.getFromId()); + verifyVersion(successor.getFromId()); + + CassandraStatements statements = new CassandraStatements(); + String cql = String.format(CqlConstants.INSERT_VERSION_SUCCESSOR, successor.getId(), successor.getFromId(), successor.getToId()); + statements.append(cql); + + return statements; + } + + /** + * Create and persist a version successor. + * + * @param fromId the id of the parent version + * @param toId the id of the child version + * @return the created version successor + */ + VersionSuccessor instantiateVersionSuccessor(long fromId, long toId) throws GroundException { + long dbId = idGenerator.generateSuccessorId(); + return new VersionSuccessor(dbId, fromId, toId); + } + + /** + * Retrieve a version successor from the database. + * + * @param dbId the id of the successor to retrieve + * @return the retrieved version successor + * @throws GroundException either the successor didn't exist or couldn't be retrieved + */ + public VersionSuccessor retrieveFromDatabase(long dbId) throws GroundException { + try { + String cql = String.format(CqlConstants.SELECT_VERSION_SUCCESSOR, dbId); + JsonNode json = Json.parse(CassandraUtils.executeQueryToJson(dbSource, cql)); + + if (json.size() == 0) { + throw new GroundException(ExceptionType.OTHER, String.format("Version Successor with id %d does not exist.", dbId)); + } + + json = json.get(0); + return new VersionSuccessor(dbId, json.get("fromVersionId").asLong(), json.get("toVersionId").asLong()); + } catch (Exception e) { + throw new GroundException(e); + } + + } + + /** + * Delete a version successor from the database. + * + * @param statementsPointer the list of DB statements to append to + * @param toId the destination version + * @param itemId the id of the item we are deleting from + * @throws GroundException an unexpected error while retrieving the version successors to delete + */ + public void deleteFromDestination(DbStatements statementsPointer, long toId, long itemId) throws GroundException { + CassandraStatements statements = (CassandraStatements) statementsPointer; + + try { + String cql = String.format(CqlConstants.SELECT_VERSION_SUCCESSOR_BY_ENDPOINT, toId); + JsonNode json = Json.parse(CassandraUtils.executeQueryToJson(dbSource, cql)); + + for (JsonNode result : json) { + Long dbId = result.get("id").asLong(); + + statements.append(String.format(CqlConstants.DELETE_SUCCESSOR_FROM_DAG, itemId, dbId)); + statements.append(String.format(CqlConstants.DELETE_VERSION_SUCCESSOR, dbId)); + } + } catch (Exception e) { + throw new GroundException(e); + } + } + + /** + * Verify that id is a valid id since foreign key constraints don't exist. + * + * @param id an idea of a version + */ + private void verifyVersion(long id) throws GroundException { + if (id == 0L ) { + return; + } + + Session session = this.dbSource.getSession(); + + String cql = String.format(CqlConstants.SELECT_STAR_BY_ID, "version", id); + ResultSet resultSet = session.execute(cql); + + if (resultSet.isExhausted()) { + throw new GroundException(ExceptionType.VERSION_NOT_FOUND, "version", String.valueOf(id)); + } + } +} diff --git a/modules/cassandra/app/edu/berkeley/ground/cassandra/filters/GroundFilter.java b/modules/cassandra/app/edu/berkeley/ground/cassandra/filters/GroundFilter.java new file mode 100644 index 00000000..ddc29ea7 --- /dev/null +++ b/modules/cassandra/app/edu/berkeley/ground/cassandra/filters/GroundFilter.java @@ -0,0 +1,37 @@ +package edu.berkeley.ground.cassandra.filters; + +import akka.stream.Materializer; +import java.util.concurrent.CompletionStage; +import java.util.concurrent.Executor; +import java.util.function.Function; +import javax.inject.Inject; +import javax.inject.Singleton; +import play.mvc.Filter; +import play.mvc.Http.RequestHeader; +import play.mvc.Result; + +/** + * This is a simple filter that adds a header to all requests. + */ +@Singleton +public class GroundFilter extends Filter { + + private final Executor exec; + + /** + * @param mat This object is needed to handle streaming of requests and responses. + * @param exec This class is needed to execute code asynchronously. It is used below by the thenAsyncApply method. + */ + @Inject + public GroundFilter(Materializer mat, Executor exec) { + super(mat); + this.exec = exec; + } + + @Override + public CompletionStage apply( + Function> next, RequestHeader requestHeader) { + + return next.apply(requestHeader).thenApplyAsync(result -> result.withHeader("X-ExampleFilter", "foo"), exec); + } +} diff --git a/modules/cassandra/app/edu/berkeley/ground/cassandra/start/ApplicationStart.java b/modules/cassandra/app/edu/berkeley/ground/cassandra/start/ApplicationStart.java new file mode 100644 index 00000000..2097842a --- /dev/null +++ b/modules/cassandra/app/edu/berkeley/ground/cassandra/start/ApplicationStart.java @@ -0,0 +1,38 @@ +package edu.berkeley.ground.cassandra.start; + +import edu.berkeley.ground.common.exception.GroundException; +import edu.berkeley.ground.cassandra.util.CassandraDatabase; +import java.time.Clock; +import java.time.Instant; +import java.util.concurrent.CompletableFuture; +import javax.inject.Inject; +import javax.inject.Singleton; +import play.Logger; +import play.api.Configuration; +// import play.db.Database; +import play.inject.ApplicationLifecycle; + +@Singleton +public class ApplicationStart { + + private final Instant start; + + @Inject + public ApplicationStart(Clock clock, ApplicationLifecycle appLifecycle, final Configuration configuration, final CassandraDatabase dbSource) + throws GroundException { + + this.start = clock.instant(); + Logger.info("Ground Cassandra: Starting application at " + this.start); + + Logger.info("Queries will Cache for {} seconds.", configuration.underlying().getString("ground.cache.expire.secs")); + System.setProperty("ground.cache.expire.secs", configuration.underlying().getString("ground.cache.expire.secs")); + + appLifecycle.addStopHook( + () -> { + Instant stop = clock.instant(); + Long runningTime = stop.getEpochSecond() - this.start.getEpochSecond(); + Logger.info("Ground Cassandra: Stopping application at " + clock.instant() + " after " + runningTime + "s."); + return CompletableFuture.completedFuture(null); + }); + } +} diff --git a/modules/cassandra/app/edu/berkeley/ground/cassandra/util/CassandraDatabase.java b/modules/cassandra/app/edu/berkeley/ground/cassandra/util/CassandraDatabase.java new file mode 100644 index 00000000..2f4823d1 --- /dev/null +++ b/modules/cassandra/app/edu/berkeley/ground/cassandra/util/CassandraDatabase.java @@ -0,0 +1,126 @@ +/** + * 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. + */ + +package edu.berkeley.ground.cassandra.util; + + +import com.datastax.driver.core.BoundStatement; +import com.datastax.driver.core.Cluster; +import com.datastax.driver.core.PlainTextAuthProvider; +import com.datastax.driver.core.PreparedStatement; +import com.datastax.driver.core.ResultSet; +import com.datastax.driver.core.Session; +import edu.berkeley.ground.cassandra.util.CassandraDatabase; + + +public class CassandraDatabase { +// private static final Logger LOGGER = LoggerFactory.getLogger(CassandraClient.class); + + private final String host; + private final int port; + private final String keyspace; + private final String username; + private final String password; + + private final Cluster cluster; + private final Session session; + + /** + * Constructor for the Cassandra client. + * + * @param host the host address for Cassandra + * @param port the Cassandra port + * @param keyspace the name of the keyspace we're using + * @param username the login username + * @param password the login password + */ + public CassandraDatabase(String host, int port, String keyspace, String username, String password) { + this.host = host; + this.port = port; + this.keyspace = keyspace; + this.username = username; + this.password = password; + + this.cluster = this.makeCluster(); + this.session = this.makeSession(); + } + + /** + * Returns the host of this Cassandra Database + * @return host + */ + public String getHost() { + return this.host; + } + + /** + * Returns the port of this Cassandra Database + * @return port + */ + public int getPort() { + return this.port; + } + + /** + * Returns the keyspace of this Cassandra Database + * @return keyspace + */ + public String getKeyspace() { + return this.keyspace; + } + + /** + * Returns the username of this Cassandra Database + * @return username + */ + public String getUsername() { + return this.username; + } + + /** + * Returns the password of this Cassandra Database + * @return password + */ + public String getPassword() { + return this.password; + } + + /** + * Returns a new Session for this Cassandra Database + * @return session + */ + private Cluster makeCluster() { + return Cluster.builder() + .addContactPoint(this.host) + .withPort(this.port) + .withAuthProvider(new PlainTextAuthProvider(this.username, this.password)) + .build(); + } + + /** + * Returns a new Session for a given cluster + * @param cluster + * @return session + */ + private Session makeSession() { + return this.cluster.connect(this.keyspace); + } + + public Session getSession() { + return this.session; + } + + public void shutdown() { + this.session.close(); + this.cluster.close(); + } +} diff --git a/modules/cassandra/app/edu/berkeley/ground/cassandra/util/CassandraStatements.java b/modules/cassandra/app/edu/berkeley/ground/cassandra/util/CassandraStatements.java new file mode 100644 index 00000000..a8528ebd --- /dev/null +++ b/modules/cassandra/app/edu/berkeley/ground/cassandra/util/CassandraStatements.java @@ -0,0 +1,33 @@ +package edu.berkeley.ground.cassandra.util; + +import edu.berkeley.ground.common.util.DbStatements; +import java.util.ArrayList; +import java.util.List; + +public class CassandraStatements implements DbStatements { + + List statements; + + public CassandraStatements() { + this.statements = new ArrayList<>(); + } + + public CassandraStatements(List statements) { + this.statements = statements; + } + + @Override + public void append(String statement) { + this.statements.add(statement); + } + + @Override + public void merge(DbStatements other) { + this.statements.addAll(other.getAllStatements()); + } + + @Override + public List getAllStatements() { + return this.statements; + } +} diff --git a/modules/cassandra/app/edu/berkeley/ground/cassandra/util/CassandraUtils.java b/modules/cassandra/app/edu/berkeley/ground/cassandra/util/CassandraUtils.java new file mode 100644 index 00000000..93eb0846 --- /dev/null +++ b/modules/cassandra/app/edu/berkeley/ground/cassandra/util/CassandraUtils.java @@ -0,0 +1,80 @@ +/** + * 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. + */ + +package edu.berkeley.ground.cassandra.util; + +import akka.actor.ActorSystem; +import com.google.common.base.CaseFormat; +import edu.berkeley.ground.common.exception.GroundException; +import com.datastax.driver.core.Cluster; +import com.datastax.driver.core.ResultSet; +import com.datastax.driver.core.Row; +import com.datastax.driver.core.Session; +import com.datastax.driver.core.exceptions.QueryExecutionException; +import edu.berkeley.ground.cassandra.util.CassandraDatabase; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Executor; +import play.Logger; +import play.libs.concurrent.HttpExecution; + +public final class CassandraUtils { + + private CassandraUtils() { + } + + public static Executor getDbSourceHttpContext(final ActorSystem actorSystem) { + return HttpExecution.fromThread((Executor) actorSystem.dispatchers().lookup("ground.db.context")); + } + + public static String executeQueryToJson(CassandraDatabase dbSource, String cql) throws GroundException { + Logger.debug("executeQueryToJson: {}", cql); + + final List> objList = new ArrayList<>(); + Session session = dbSource.getSession(); + + try { + final ResultSet resultSet = session.execute(cql); + + for (Row row: resultSet.all()) { + final Map rowData = new HashMap<>(); + + for (int column = 0; column < resultSet.getColumnDefinitions().size(); column++) { + String key = CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, resultSet.getColumnDefinitions().getName(column)); + rowData.put(key, row.getObject(column)); + } + + objList.add(rowData); + } + } catch (QueryExecutionException e) { + throw new GroundException(e); + } + + return GroundUtils.listToJson(objList); + } + + public static void executeCqlList(final CassandraDatabase dbSource, final CassandraStatements statements) throws GroundException { + Session session = dbSource.getSession(); + + try { + for (final String cql : statements.getAllStatements()) { + Logger.debug("executeCqlList cql : {}", cql); + session.execute(cql); + } + } catch (QueryExecutionException e) { + Logger.error("error: Message: {} Trace: {}", e.getMessage(), e.getStackTrace()); + throw new GroundException(e); + } + } +} diff --git a/modules/cassandra/app/edu/berkeley/ground/cassandra/util/GroundUtils.java b/modules/cassandra/app/edu/berkeley/ground/cassandra/util/GroundUtils.java new file mode 100644 index 00000000..6cd61c06 --- /dev/null +++ b/modules/cassandra/app/edu/berkeley/ground/cassandra/util/GroundUtils.java @@ -0,0 +1,110 @@ +package edu.berkeley.ground.cassandra.util; + +import static play.mvc.Results.badRequest; +import static play.mvc.Results.internalServerError; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import edu.berkeley.ground.common.dao.version.VersionDao; +import edu.berkeley.ground.common.exception.GroundException; +import edu.berkeley.ground.common.exception.GroundException.ExceptionType; +import edu.berkeley.ground.common.model.core.Edge; +import edu.berkeley.ground.common.model.core.Graph; +import edu.berkeley.ground.common.model.core.Node; +import edu.berkeley.ground.common.model.core.Structure; +import edu.berkeley.ground.common.model.usage.LineageEdge; +import edu.berkeley.ground.common.model.usage.LineageGraph; +import edu.berkeley.ground.common.util.IdGenerator; +import edu.berkeley.ground.cassandra.dao.core.CassandraEdgeVersionDao; +import edu.berkeley.ground.cassandra.dao.core.CassandraGraphVersionDao; +import edu.berkeley.ground.cassandra.dao.core.CassandraNodeVersionDao; +import edu.berkeley.ground.cassandra.dao.core.CassandraStructureVersionDao; +import edu.berkeley.ground.cassandra.dao.usage.CassandraLineageEdgeVersionDao; +import edu.berkeley.ground.cassandra.dao.usage.CassandraLineageGraphVersionDao; +import edu.berkeley.ground.cassandra.util.CassandraDatabase; +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import play.Logger; +import play.libs.Json; +import play.mvc.Http.Request; +import play.mvc.Result; + +public final class GroundUtils { + + private GroundUtils() { + } + + public static Result handleException(Throwable e, Request request) { + if (e.getCause().getCause() instanceof GroundException) { + return badRequest(GroundUtils.getClientError(request, e.getCause().getCause(), ExceptionType.ITEM_NOT_FOUND)); + } else { + return internalServerError(GroundUtils.getServerError(request, e.getCause().getCause())); + } + } + + private static ObjectNode getServerError(final Request request, final Throwable e) { + Logger.error("Error! Request Path: {}\nError Message: {}\n Stack Trace: {}", request.path(), e.getMessage(), e.getStackTrace()); + + ObjectNode result = Json.newObject(); + result.put("Error", "Unexpected error while processing request."); + result.put("Request Path", request.path()); + + StringWriter sw = new StringWriter(); + e.printStackTrace(new PrintWriter(sw)); + result.put("Stack Trace", sw.toString()); + return result; + } + + private static ObjectNode getClientError(final Request request, final Throwable e, ExceptionType type) { + Logger.error("Error! Request Path: {}\nError Message: {}\n Stack Trace: {}", request.path(), e.getMessage(), e.getStackTrace()); + + ObjectNode result = Json.newObject(); + + result.put("Error", String.format("The request to %s was invalid.", request.path())); + result.put("Message", String.format("%s", e.getMessage())); + + return result; + } + + static String listToJson(final List> objList) { + try { + return new ObjectMapper().writeValueAsString(objList); + } catch (IOException e) { + throw new RuntimeException("ERROR : listToJson Converting List to JSON." + e.getMessage(), e); + } + } + + public static List getListFromJson(JsonNode jsonNode, String fieldName) { + List parents = new ArrayList<>(); + JsonNode listNode = jsonNode.get(fieldName); + + if (listNode != null) { + listNode.forEach(node -> parents.add(node.asLong())); + } + + return parents; + } + + public static VersionDao getVersionDaoFromItemType(Class klass, CassandraDatabase dbSource, IdGenerator idGenerator) throws GroundException { + if (klass.equals(Node.class)) { + return new CassandraNodeVersionDao(dbSource, idGenerator); + } else if (klass.equals(Edge.class)) { + return new CassandraEdgeVersionDao(dbSource, idGenerator); + } else if (klass.equals(Graph.class)) { + return new CassandraGraphVersionDao(dbSource, idGenerator); + } else if (klass.equals(Structure.class)) { + return new CassandraStructureVersionDao(dbSource, idGenerator); + } else if (klass.equals(LineageEdge.class)) { + return new CassandraLineageEdgeVersionDao(dbSource, idGenerator); + } else if (klass.equals(LineageGraph.class)) { + return new CassandraLineageGraphVersionDao(dbSource, idGenerator); + } else { + throw new GroundException(ExceptionType.OTHER, String.format("Unknown class :%s.", klass.getSimpleName())); + } + } +} diff --git a/modules/cassandra/app/views/index.scala.html b/modules/cassandra/app/views/index.scala.html new file mode 100644 index 00000000..f61f6ee4 --- /dev/null +++ b/modules/cassandra/app/views/index.scala.html @@ -0,0 +1,3 @@ +@(message: String) +@main("Welcome to Ground - Cassandra Plugin") { +} diff --git a/modules/cassandra/app/views/main.scala.html b/modules/cassandra/app/views/main.scala.html new file mode 100644 index 00000000..db7da9a1 --- /dev/null +++ b/modules/cassandra/app/views/main.scala.html @@ -0,0 +1,16 @@ +@(title: String)(content: Html) + + + + + @title + + + + + + @* And here's where we render the `Html` object containing + * the page content. *@ + @content + + diff --git a/modules/cassandra/conf/application.conf b/modules/cassandra/conf/application.conf new file mode 100644 index 00000000..1d9f631f --- /dev/null +++ b/modules/cassandra/conf/application.conf @@ -0,0 +1,45 @@ +# This is the main configuration file for the Ground Postgres. + + +play.crypto.secret = "groundpostgres" +ground.cache.expire.secs = 5 + +ground.db.context { + fork-join-executor { + parallelism-min = 1 + parallelism-factor = 4 + parallelism-max = 16 + } +} + +## Modules +play.modules { +} + + +## Internationalisation +play.i18n { + # The application languages + langs = ["en"] +} + +## Database Connection Pool +play.db { + # The combination of these two settings results in "db.default" as the + # default JDBC pool: + config = "db" + default = "default" + + prototype { + hikaricp.minimumIdle = 50 + hikaricp.maximumPoolSize = 20 + } +} + +## JDBC Datasource +db { + default.driver = org.postgresql.Driver + default.url = "jdbc:postgresql://localhost:5432/ground" + default.username = ground + default.password = metadata +} diff --git a/modules/cassandra/conf/logback.xml b/modules/cassandra/conf/logback.xml new file mode 100644 index 00000000..86ec12c0 --- /dev/null +++ b/modules/cassandra/conf/logback.xml @@ -0,0 +1,41 @@ + + + + + + + ${application.home:-.}/logs/application.log + + %date [%level] from %logger in %thread - %message%n%xException + + + + + + %coloredLevel %logger{15} - %message%n%xException{10} + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/cassandra/conf/routes b/modules/cassandra/conf/routes new file mode 100644 index 00000000..948ea720 --- /dev/null +++ b/modules/cassandra/conf/routes @@ -0,0 +1,45 @@ +# Routes +# This file defines all application routes (Higher priority routes first) +# ~~~~ + +# An example controller showing a sample home page +GET / edu.berkeley.ground.cassandra.controllers.HomeController.index + +# Map static resources from the /public folder to the /assets URL path +GET /assets/*file controllers.Assets.versioned(path="/public", file: Asset) + +# node endpoints +POST /nodes edu.berkeley.ground.cassandra.controllers.NodeController.addNode() +GET /nodes/:sourceKey edu.berkeley.ground.cassandra.controllers.NodeController.getNode(sourceKey: String) +POST /versions/nodes edu.berkeley.ground.cassandra.controllers.NodeController.addNodeVersion() +GET /versions/nodes/:id edu.berkeley.ground.cassandra.controllers.NodeController.getNodeVersion(id: Long) + +# graph endpoints +POST /graphs edu.berkeley.ground.cassandra.controllers.GraphController.addGraph() +GET /graphs/:sourceKey edu.berkeley.ground.cassandra.controllers.GraphController.getGraph(sourceKey: String) +POST /versions/graphs edu.berkeley.ground.cassandra.controllers.GraphController.addGraphVersion() +GET /versions/graphs/:id edu.berkeley.ground.cassandra.controllers.GraphController.getGraphVersion(id: Long) + +# structure endpoints +POST /structures edu.berkeley.ground.cassandra.controllers.StructureController.addStructure() +GET /structures/:sourceKey edu.berkeley.ground.cassandra.controllers.StructureController.getStructure(sourceKey: String) +POST /versions/structures edu.berkeley.ground.cassandra.controllers.StructureController.addStructureVersion() +GET /versions/structures/:id edu.berkeley.ground.cassandra.controllers.StructureController.getStructureVersion(id: Long) + +# edge endpoints +POST /edges edu.berkeley.ground.cassandra.controllers.EdgeController.addEdge() +GET /edges/:sourceKey edu.berkeley.ground.cassandra.controllers.EdgeController.getEdge(sourceKey: String) +POST /versions/edges edu.berkeley.ground.cassandra.controllers.EdgeController.addEdgeVersion() +GET /versions/edges/:id edu.berkeley.ground.cassandra.controllers.EdgeController.getEdgeVersion(id: Long) + +# lineage edge endpoints +POST /lineage_edges edu.berkeley.ground.cassandra.controllers.LineageEdgeController.createLineageEdge() +GET /lineage_edges/:sourceKey edu.berkeley.ground.cassandra.controllers.LineageEdgeController.getLineageEdge(sourceKey: String) +POST /versions/lineage_edges edu.berkeley.ground.cassandra.controllers.LineageEdgeController.createLineageEdgeVersion() +GET /versions/lineage_edges/:id edu.berkeley.ground.cassandra.controllers.LineageEdgeController.getLineageEdgeVersion(id: Long) + +# lineage graph endpoints +POST /lineage_graphs edu.berkeley.ground.cassandra.controllers.LineageGraphController.createLineageGraph() +GET /lineage_graphs/:sourceKey edu.berkeley.ground.cassandra.controllers.LineageGraphController.getLineageGraph(sourceKey: String) +POST /versions/lineage_graphs edu.berkeley.ground.cassandra.controllers.LineageGraphController.createLineageGraphVersion() +GET /versions/lineage_graphs/:id edu.berkeley.ground.cassandra.controllers.LineageGraphController.getLineageGraphVersion(id: Long) diff --git a/modules/cassandra/conf/swagger.yml b/modules/cassandra/conf/swagger.yml new file mode 100644 index 00000000..58840364 --- /dev/null +++ b/modules/cassandra/conf/swagger.yml @@ -0,0 +1,10 @@ +--- + swagger: "2.0" + info: + title: "cassandra Ground API" + description: "cassandra APIs" + version: "0.1" + consumes: + - application/json + produces: + - application/json diff --git a/modules/cassandra/project/build.properties b/modules/cassandra/project/build.properties new file mode 100644 index 00000000..a279d8ae --- /dev/null +++ b/modules/cassandra/project/build.properties @@ -0,0 +1,4 @@ +#Activator-generated Properties +#Tue Oct 04 16:51:22 CDT 2016 +template.uuid=26c759a5-daf0-4e02-bcfa-ac69725267c0 +sbt.version=0.13.15 diff --git a/modules/cassandra/project/plugins.sbt b/modules/cassandra/project/plugins.sbt new file mode 100644 index 00000000..0b0e11c1 --- /dev/null +++ b/modules/cassandra/project/plugins.sbt @@ -0,0 +1,6 @@ +// The Play plugin +addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.5.13") +addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "4.0.0") +addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "1.0.0") + +addSbtPlugin("de.johoop" % "jacoco4sbt" % "2.2.0") diff --git a/modules/cassandra/public/images/favicon.png b/modules/cassandra/public/images/favicon.png new file mode 100644 index 00000000..483081da Binary files /dev/null and b/modules/cassandra/public/images/favicon.png differ diff --git a/modules/cassandra/public/javascripts/hello.js b/modules/cassandra/public/javascripts/hello.js new file mode 100644 index 00000000..02ee13c7 --- /dev/null +++ b/modules/cassandra/public/javascripts/hello.js @@ -0,0 +1,3 @@ +if (window.console) { + console.log("Welcome to your Play application's JavaScript!"); +} diff --git a/modules/cassandra/public/stylesheets/main.css b/modules/cassandra/public/stylesheets/main.css new file mode 100644 index 00000000..e69de29b diff --git a/modules/cassandra/test/edu/berkeley/ground/cassandra/dao/CassandraTest.java b/modules/cassandra/test/edu/berkeley/ground/cassandra/dao/CassandraTest.java new file mode 100644 index 00000000..8f092a7a --- /dev/null +++ b/modules/cassandra/test/edu/berkeley/ground/cassandra/dao/CassandraTest.java @@ -0,0 +1,96 @@ +package edu.berkeley.ground.cassandra.dao; + +import com.google.common.collect.ImmutableMap; +import edu.berkeley.ground.common.exception.GroundException; +import edu.berkeley.ground.common.util.IdGenerator; +import edu.berkeley.ground.cassandra.dao.core.CassandraEdgeDao; +import edu.berkeley.ground.cassandra.dao.core.CassandraEdgeVersionDao; +import edu.berkeley.ground.cassandra.dao.core.CassandraGraphDao; +import edu.berkeley.ground.cassandra.dao.core.CassandraGraphVersionDao; +import edu.berkeley.ground.cassandra.dao.core.CassandraNodeDao; +import edu.berkeley.ground.cassandra.dao.core.CassandraNodeVersionDao; +import edu.berkeley.ground.cassandra.dao.core.CassandraStructureDao; +import edu.berkeley.ground.cassandra.dao.core.CassandraStructureVersionDao; +import edu.berkeley.ground.cassandra.dao.usage.CassandraLineageEdgeDao; +import edu.berkeley.ground.cassandra.dao.usage.CassandraLineageEdgeVersionDao; +import edu.berkeley.ground.cassandra.dao.usage.CassandraLineageGraphDao; +import edu.berkeley.ground.cassandra.dao.usage.CassandraLineageGraphVersionDao; +import edu.berkeley.ground.cassandra.dao.version.CassandraTagDao; +import edu.berkeley.ground.cassandra.dao.version.CassandraVersionHistoryDagDao; +import edu.berkeley.ground.cassandra.dao.version.CassandraVersionSuccessorDao; +import edu.berkeley.ground.cassandra.dao.version.mock.TestCassandraItemDao; +import edu.berkeley.ground.cassandra.dao.version.mock.TestCassandraRichVersionDao; +import edu.berkeley.ground.cassandra.dao.version.mock.TestCassandraVersionDao; +import edu.berkeley.ground.cassandra.util.CassandraDatabase; +import java.io.IOException; +import java.util.function.Function; +import com.datastax.driver.core.Cluster; +import com.datastax.driver.core.Session; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.nio.file.Paths; +import org.junit.After; +import org.junit.Before; + + +public class CassandraTest extends DaoTest { + + private static final String TRUNCATE_SCRIPT = "../../resources/scripts/cassandra/truncate.cql"; + private static final String CREATE_SCHEMA_SCRIPT = "../../resources/scripts/cassandra/cassandra.cql"; + private static final Function CREATE_KEYSPACE_CQL = keyspace->"create keyspace IF NOT EXISTS " + + keyspace + " with replication = { 'class' : 'SimpleStrategy', 'replication_factor' : 1 };"; + + public CassandraTest() throws GroundException { + + } + + @Before + public void setup() throws IOException, InterruptedException, GroundException { + CassandraDatabase dbSource = new CassandraDatabase( + "localhost", + 9042, + "test", + "user", + "password" + ); + + IdGenerator idGenerator = new IdGenerator(0, 1, false); + + CassandraTest.dbSource = dbSource; + CassandraTest.idGenerator = idGenerator; + + CassandraTest.cassandraVersionDao = new TestCassandraVersionDao(dbSource, idGenerator); + CassandraTest.versionSuccessorDao = new CassandraVersionSuccessorDao(dbSource, idGenerator); + CassandraTest.versionHistoryDagDao = new CassandraVersionHistoryDagDao(dbSource, idGenerator); + CassandraTest.cassandraItemDao = new TestCassandraItemDao(dbSource, idGenerator); + CassandraTest.tagDao = new CassandraTagDao(dbSource); + + CassandraTest.cassandraRichVersionDao = new TestCassandraRichVersionDao(dbSource, idGenerator); + CassandraTest.structureDao = new CassandraStructureDao(dbSource, idGenerator); + CassandraTest.structureVersionDao = new CassandraStructureVersionDao(dbSource, idGenerator); + CassandraTest.edgeDao = new CassandraEdgeDao(dbSource, idGenerator); + CassandraTest.edgeVersionDao = new CassandraEdgeVersionDao(dbSource, idGenerator); + CassandraTest.graphDao = new CassandraGraphDao(dbSource, idGenerator); + CassandraTest.graphVersionDao = new CassandraGraphVersionDao(dbSource, idGenerator); + CassandraTest.nodeDao = new CassandraNodeDao(dbSource, idGenerator); + CassandraTest.nodeVersionDao = new CassandraNodeVersionDao(dbSource, idGenerator); + CassandraTest.lineageEdgeDao = new CassandraLineageEdgeDao(dbSource, idGenerator); + CassandraTest.lineageEdgeVersionDao = new CassandraLineageEdgeVersionDao(dbSource, idGenerator); + CassandraTest.lineageGraphDao = new CassandraLineageGraphDao(dbSource, idGenerator); + CassandraTest.lineageGraphVersionDao = new CassandraLineageGraphVersionDao(dbSource, idGenerator); + + runScript(TRUNCATE_SCRIPT); + runScript(CREATE_SCHEMA_SCRIPT); + } + + @After + public void tearDown() throws IOException, InterruptedException, GroundException { + dbSource.shutdown(); + } + + private static void runScript(String script) { + // Can wrap around with try-catch for a QueryExecutionException if wanted (like in PostgresTest.java for Postgres Connector) + Session session = dbSource.getSession(); + DaoTest.runScript(script, session::execute); + } +} diff --git a/modules/cassandra/test/edu/berkeley/ground/cassandra/dao/DaoTest.java b/modules/cassandra/test/edu/berkeley/ground/cassandra/dao/DaoTest.java new file mode 100644 index 00000000..04970820 --- /dev/null +++ b/modules/cassandra/test/edu/berkeley/ground/cassandra/dao/DaoTest.java @@ -0,0 +1,261 @@ +package edu.berkeley.ground.cassandra.dao; + +import edu.berkeley.ground.common.dao.core.EdgeDao; +import edu.berkeley.ground.common.dao.core.EdgeVersionDao; +import edu.berkeley.ground.common.dao.core.GraphDao; +import edu.berkeley.ground.common.dao.core.GraphVersionDao; +import edu.berkeley.ground.common.dao.core.NodeDao; +import edu.berkeley.ground.common.dao.core.NodeVersionDao; +import edu.berkeley.ground.common.dao.core.RichVersionDao; +import edu.berkeley.ground.common.dao.core.StructureDao; +import edu.berkeley.ground.common.dao.core.StructureVersionDao; +import edu.berkeley.ground.common.dao.usage.LineageEdgeDao; +import edu.berkeley.ground.common.dao.usage.LineageEdgeVersionDao; +import edu.berkeley.ground.common.dao.usage.LineageGraphDao; +import edu.berkeley.ground.common.dao.usage.LineageGraphVersionDao; +import edu.berkeley.ground.common.dao.version.ItemDao; +import edu.berkeley.ground.common.dao.version.TagDao; +import edu.berkeley.ground.common.dao.version.VersionDao; +import edu.berkeley.ground.common.dao.version.VersionHistoryDagDao; +// import edu.berkeley.ground.common.dao.version.VersionSuccessorDao; +import edu.berkeley.ground.cassandra.dao.version.CassandraVersionSuccessorDao; +import edu.berkeley.ground.common.exception.GroundException; +import edu.berkeley.ground.common.model.core.Edge; +import edu.berkeley.ground.common.model.core.EdgeVersion; +import edu.berkeley.ground.common.model.core.Graph; +import edu.berkeley.ground.common.model.core.GraphVersion; +import edu.berkeley.ground.common.model.core.Node; +import edu.berkeley.ground.common.model.core.NodeVersion; +import edu.berkeley.ground.common.model.core.RichVersion; +import edu.berkeley.ground.common.model.core.Structure; +import edu.berkeley.ground.common.model.core.StructureVersion; +import edu.berkeley.ground.common.model.usage.LineageEdge; +import edu.berkeley.ground.common.model.usage.LineageEdgeVersion; +import edu.berkeley.ground.common.model.usage.LineageGraph; +import edu.berkeley.ground.common.model.usage.LineageGraphVersion; +import edu.berkeley.ground.common.model.version.GroundType; +import edu.berkeley.ground.common.model.version.Item; +import edu.berkeley.ground.common.model.version.Tag; +import edu.berkeley.ground.common.model.version.Version; +import edu.berkeley.ground.common.util.IdGenerator; +import edu.berkeley.ground.cassandra.util.CassandraDatabase; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; +import java.util.stream.Collectors; +import java.util.stream.Stream; +// import play.db.Database; + + +public class DaoTest { + + protected static CassandraDatabase dbSource; + protected static IdGenerator idGenerator; + // protected static VersionSuccessorDao versionSuccessorDao; + protected static CassandraVersionSuccessorDao versionSuccessorDao; + protected static VersionHistoryDagDao versionHistoryDagDao; + protected static TagDao tagDao; + protected static EdgeDao edgeDao; + protected static GraphDao graphDao; + protected static LineageEdgeDao lineageEdgeDao; + protected static LineageGraphDao lineageGraphDao; + protected static NodeDao nodeDao; + protected static StructureDao structureDao; + protected static EdgeVersionDao edgeVersionDao; + protected static GraphVersionDao graphVersionDao; + protected static LineageEdgeVersionDao lineageEdgeVersionDao; + protected static LineageGraphVersionDao lineageGraphVersionDao; + protected static NodeVersionDao nodeVersionDao; + protected static StructureVersionDao structureVersionDao; + protected static RichVersionDao cassandraRichVersionDao; + protected static ItemDao cassandraItemDao; + protected static VersionDao cassandraVersionDao; + + public static Node createNode(String sourceKey) throws GroundException { + // id should be replaced by output of IdGenerator + return nodeDao.create(new Node(0L, null, sourceKey, new HashMap())); + } + + public static NodeVersion createNodeVersion(long nodeId) throws GroundException { + return createNodeVersion(nodeId, new ArrayList<>()); + } + + public static NodeVersion createNodeVersion(long nodeId, List parents) + throws GroundException { + NodeVersion nodeVersion = new NodeVersion(0L, new HashMap<>(), -1, null, new HashMap<>(), + nodeId); + return nodeVersionDao.create(nodeVersion, parents); + } + + public static Edge createEdge(String sourceKey, String fromNode, String toNode) + throws GroundException { + + long fromNodeId = nodeDao.retrieveFromDatabase(fromNode).getId(); + long toNodeId = nodeDao.retrieveFromDatabase(toNode).getId(); + Edge edge = new Edge(0L, null, sourceKey, fromNodeId, toNodeId, new HashMap<>()); + return edgeDao.create(edge); + } + + public static EdgeVersion createEdgeVersion(long edgeId, long fromStart, long toStart) + throws GroundException { + + return createEdgeVersion(edgeId, fromStart, toStart, new ArrayList<>()); + } + + public static EdgeVersion createEdgeVersion(long edgeId, + long fromStart, + long toStart, + List parents) + throws GroundException { + EdgeVersion edgeVersion = new EdgeVersion(0L, new HashMap<>(), -1, null, new HashMap<>(), + edgeId, fromStart, -1, toStart, -1); + return edgeVersionDao.create(edgeVersion, parents); + } + + public static EdgeVersion createEdgeVersion(long edgeId, + long fromStart, + long fromEnd, + long toStart, + long toEnd, + List parents) + throws GroundException { + EdgeVersion edgeVersion = new EdgeVersion(0L, new HashMap<>(), -1, null, new HashMap<>(), + edgeId, fromStart, fromEnd, toStart, toEnd); + return edgeVersionDao.create(edgeVersion, parents); + } + + public static Graph createGraph(String sourceKey) throws GroundException { + Graph graph = new Graph(0L, null, sourceKey, new HashMap<>()); + return graphDao.create(graph); + } + + public static GraphVersion createGraphVersion(long graphId, List edgeVersionIds) + throws GroundException { + + return createGraphVersion(graphId, edgeVersionIds, new ArrayList<>()); + } + + public static GraphVersion createGraphVersion(long graphId, + List edgeVersionIds, + List parents) throws GroundException { + + GraphVersion graphVersion = new GraphVersion(0L, new HashMap<>(), -1, null, new HashMap<>(), graphId, edgeVersionIds); + return graphVersionDao.create(graphVersion, parents); + } + + public static LineageEdge createLineageEdge(String sourceKey) throws GroundException { + LineageEdge lineageEdge = new LineageEdge(0L, null, sourceKey, new HashMap<>()); + return lineageEdgeDao.create(lineageEdge); + } + + public static LineageEdgeVersion createLineageEdgeVersion(long lineageEdgeId, + long fromId, + long toId) throws GroundException { + + return createLineageEdgeVersion(lineageEdgeId, fromId, toId, new ArrayList<>()); + } + + public static LineageEdgeVersion createLineageEdgeVersion(long lineageEdgeId, + long fromId, + long toId, + List parents) + throws GroundException { + LineageEdgeVersion lineageEdgeVersion = new LineageEdgeVersion(0L, new HashMap(), + (long) -1, + null, new HashMap<>(), fromId, toId, lineageEdgeId); + return lineageEdgeVersionDao.create(lineageEdgeVersion, parents); + } + + public static LineageGraph createLineageGraph(String sourceKey) throws GroundException { + LineageGraph lineageGraph = new LineageGraph(0L, null, sourceKey, new HashMap<>()); + return lineageGraphDao.create(lineageGraph); + } + + public static LineageGraphVersion createLineageGraphVersion(long lineageGraphId, + List lineageEdgeVersionIds) + throws GroundException { + + return createLineageGraphVersion(lineageGraphId, lineageEdgeVersionIds, new ArrayList<>()); + } + + public static LineageGraphVersion createLineageGraphVersion(long lineageGraphId, + List lineageEdgeVersionIds, + List parents) + throws GroundException { + + LineageGraphVersion lineageGraphVersion = new LineageGraphVersion(0L, new HashMap<>(), -1, null, + new HashMap<>(), lineageGraphId, lineageEdgeVersionIds); + return lineageGraphVersionDao.create(lineageGraphVersion, parents); + } + + + public static Structure createStructure(String sourceKey) throws GroundException { + Structure structure = new Structure(0L, null, sourceKey, new HashMap<>()); + return structureDao.create(structure); + } + + public static StructureVersion createStructureVersion(long structureId) throws GroundException { + return createStructureVersion(structureId, new ArrayList<>()); + } + + public static StructureVersion createStructureVersion(long structureId, List parents) + throws GroundException { + + Map structureVersionAttributes = new HashMap<>(); + structureVersionAttributes.put("intfield", GroundType.INTEGER); + structureVersionAttributes.put("boolfield", GroundType.BOOLEAN); + structureVersionAttributes.put("strfield", GroundType.STRING); + + StructureVersion structureVersion = new StructureVersion(0L, structureId, + structureVersionAttributes); + return structureVersionDao.create(structureVersion, parents); + } + + public static Map createTags() throws GroundException { + Map tags = new HashMap<>(); + tags.put("intfield", new Tag(-1, "intfield", 1, GroundType.INTEGER)); + tags.put("strfield", new Tag(-1, "strfield", "1", GroundType.STRING)); + tags.put("boolfield", new Tag(-1, "boolfield", true, GroundType.BOOLEAN)); + + return tags; + } + + public static long createTwoNodesAndEdge() throws GroundException { + String firstTestNode = "firstTestNode"; + long firstTestNodeId = DaoTest.createNode(firstTestNode).getId(); + long firstNodeVersionId = DaoTest.createNodeVersion(firstTestNodeId).getId(); + + String secondTestNode = "secondTestNode"; + long secondTestNodeId = DaoTest.createNode(secondTestNode).getId(); + long secondNodeVersionId = DaoTest.createNodeVersion(secondTestNodeId).getId(); + + String edgeName = "testEdge"; + long edgeId = DaoTest.createEdge(edgeName, firstTestNode, secondTestNode).getId(); + + long edgeVersionId = DaoTest.createEdgeVersion(edgeId, firstNodeVersionId, + secondNodeVersionId).getId(); + + return edgeVersionId; + } + + protected static void runScript(String scriptFile, Consumer executor) { + final String SQL_COMMENT_START = "--"; + + try (Stream lines = Files.lines(Paths.get(scriptFile))) { + String data = lines.filter(line -> !line.startsWith(SQL_COMMENT_START)) + .collect(Collectors.joining()); + Arrays.stream(data.split(";")) + .map(chunk -> chunk + ";") + .forEach(statement -> executor.accept(statement)); + } catch (IOException e) { + throw new RuntimeException("Unable to read script file: " + scriptFile); + } + } + +} diff --git a/modules/cassandra/test/edu/berkeley/ground/cassandra/dao/core/CassandraEdgeDaoTest.java b/modules/cassandra/test/edu/berkeley/ground/cassandra/dao/core/CassandraEdgeDaoTest.java new file mode 100644 index 00000000..9757a9f6 --- /dev/null +++ b/modules/cassandra/test/edu/berkeley/ground/cassandra/dao/core/CassandraEdgeDaoTest.java @@ -0,0 +1,131 @@ +package edu.berkeley.ground.cassandra.dao.core; + +/** + * 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. + */ + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import edu.berkeley.ground.common.exception.GroundException; +import edu.berkeley.ground.common.model.core.Edge; +import edu.berkeley.ground.common.model.version.VersionHistoryDag; +import edu.berkeley.ground.common.model.version.VersionSuccessor; +import edu.berkeley.ground.cassandra.dao.CassandraTest; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import org.junit.Test; + +public class CassandraEdgeDaoTest extends CassandraTest { + + public CassandraEdgeDaoTest() throws GroundException { + super(); + } + + @Test + public void testEdgeCreation() throws GroundException { + String testName = "test"; + String sourceKey = "testKey"; + + String fromNodeName = "firstTestNode"; + String toNodeName = "secondTestNode"; + long fromNodeId = CassandraTest.createNode(fromNodeName).getId(); + long toNodeId = CassandraTest.createNode(toNodeName).getId(); + + CassandraTest.edgeDao.create(new Edge(0L, testName, sourceKey, fromNodeId, toNodeId, new HashMap<>())); + + Edge edge = CassandraTest.edgeDao.retrieveFromDatabase(sourceKey); + + assertEquals(testName, edge.getName()); + assertEquals(fromNodeId, edge.getFromNodeId()); + assertEquals(toNodeId, edge.getToNodeId()); + assertEquals(sourceKey, edge.getSourceKey()); + } + + @Test(expected = GroundException.class) + public void testRetrieveBadEdge() throws GroundException { + String sourceKey = "test"; + + try { + CassandraTest.edgeDao.retrieveFromDatabase(sourceKey); + } catch (GroundException e) { + assertEquals(GroundException.class, e.getClass()); + + throw e; + } + } + + @Test(expected = GroundException.class) + public void testCreateDuplicateEdge() throws GroundException { + String edgeName = "edgeName"; + String edgeKey = "edgeKey"; + String fromNode = "fromNode"; + String toNode = "toNode"; + + long fromNodeId = -1; + long toNodeId = -1; + + try { + fromNodeId = CassandraTest.createNode(fromNode).getId(); + toNodeId = CassandraTest.createNode(toNode).getId(); + + CassandraTest.edgeDao + .create(new Edge(0L, edgeName, edgeKey, fromNodeId, toNodeId, new HashMap<>())); + } catch (GroundException e) { + fail(e.getMessage()); + } + + CassandraTest.edgeDao + .create(new Edge(0L, edgeName, edgeKey, fromNodeId, toNodeId, new HashMap<>())); + } + + @Test + public void testTruncation() throws GroundException { + String firstTestNode = "firstTestNode"; + long firstTestNodeId = CassandraTest.createNode(firstTestNode).getId(); + long firstNodeVersionId = CassandraTest.createNodeVersion(firstTestNodeId).getId(); + + String secondTestNode = "secondTestNode"; + long secondTestNodeId = CassandraTest.createNode(secondTestNode).getId(); + long secondNodeVersionId = CassandraTest.createNodeVersion(secondTestNodeId).getId(); + + String edgeName = "testEdge"; + long edgeId = CassandraTest.createEdge(edgeName, firstTestNode, secondTestNode).getId(); + + long edgeVersionId = CassandraTest.createEdgeVersion(edgeId, firstNodeVersionId, secondNodeVersionId).getId(); + + // create new node versions in each of the nodes + List parents = new ArrayList<>(); + parents.add(firstNodeVersionId); + long newFirstNodeVersionId = CassandraTest.createNodeVersion(firstTestNodeId, parents).getId(); + + parents.clear(); + parents.add(secondNodeVersionId); + long newSecondNodeVersionId = CassandraTest.createNodeVersion(secondTestNodeId, parents).getId(); + + parents.clear(); + parents.add(edgeVersionId); + long newEdgeVersionId = CassandraTest.createEdgeVersion(edgeId, newFirstNodeVersionId, newSecondNodeVersionId, parents).getId(); + + CassandraTest.edgeDao.truncate(edgeId, 1); + + VersionHistoryDag dag = CassandraTest.versionHistoryDagDao.retrieveFromDatabase(edgeId); + + assertEquals(1, dag.getEdgeIds().size()); + VersionSuccessor successor = CassandraTest.versionSuccessorDao.retrieveFromDatabase(dag.getEdgeIds().get(0)); + + assertEquals(0, successor.getFromId()); + assertEquals(newEdgeVersionId, successor.getToId()); + } +} diff --git a/modules/cassandra/test/edu/berkeley/ground/cassandra/dao/core/CassandraEdgeVersionDaoTest.java b/modules/cassandra/test/edu/berkeley/ground/cassandra/dao/core/CassandraEdgeVersionDaoTest.java new file mode 100644 index 00000000..5782f3a1 --- /dev/null +++ b/modules/cassandra/test/edu/berkeley/ground/cassandra/dao/core/CassandraEdgeVersionDaoTest.java @@ -0,0 +1,166 @@ +package edu.berkeley.ground.cassandra.dao.core; + +/** + * 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. + */ + +import static org.junit.Assert.assertEquals; + +import edu.berkeley.ground.common.exception.GroundException; +import edu.berkeley.ground.common.model.core.EdgeVersion; +import edu.berkeley.ground.common.model.version.Tag; +import edu.berkeley.ground.cassandra.dao.CassandraTest; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.junit.Test; + +public class CassandraEdgeVersionDaoTest extends CassandraTest { + + public CassandraEdgeVersionDaoTest() throws GroundException { + super(); + } + + @Test + public void testEdgeVersionCreation() throws GroundException { + String firstTestNode = "firstTestNode"; + long firstTestNodeId = CassandraTest.createNode(firstTestNode).getId(); + long firstNodeVersionId = CassandraTest.createNodeVersion(firstTestNodeId).getId(); + + String secondTestNode = "secondTestNode"; + long secondTestNodeId = CassandraTest.createNode(secondTestNode).getId(); + long secondNodeVersionId = CassandraTest.createNodeVersion(secondTestNodeId).getId(); + + String edgeName = "testEdge"; + long edgeId = CassandraTest.createEdge(edgeName, firstTestNode, secondTestNode).getId(); + + String structureName = "testStructure"; + long structureId = CassandraTest.createStructure(structureName).getId(); + long structureVersionId = CassandraTest.createStructureVersion(structureId).getId(); + + Map tags = CassandraTest.createTags(); + + String testReference = "http://www.google.com"; + Map parameters = new HashMap<>(); + parameters.put("http", "GET"); + + EdgeVersion edgeVersion = new EdgeVersion(0L, tags, structureVersionId, testReference, + parameters, edgeId, firstNodeVersionId, -1, secondNodeVersionId, -1); + long edgeVersionId = CassandraTest.edgeVersionDao.create(edgeVersion, new ArrayList<>()) + .getId(); + + EdgeVersion retrieved = CassandraTest.edgeVersionDao.retrieveFromDatabase(edgeVersionId); + + assertEquals(edgeId, retrieved.getEdgeId()); + assertEquals(structureVersionId, (long) retrieved.getStructureVersionId()); + assertEquals(testReference, retrieved.getReference()); + assertEquals(firstNodeVersionId, retrieved.getFromNodeVersionStartId()); + assertEquals(secondNodeVersionId, retrieved.getToNodeVersionStartId()); + assertEquals(-1, retrieved.getFromNodeVersionEndId()); + assertEquals(-1, retrieved.getToNodeVersionEndId()); + + //assertEquals(parameters.size(), retrieved.getParameters().size()); + assertEquals(tags.size(), retrieved.getTags().size()); + + Map retrievedParameters = retrieved.getParameters(); + Map retrievedTags = retrieved.getTags(); + + for (String key : parameters.keySet()) { + assert (retrievedParameters).containsKey(key); + assertEquals(parameters.get(key), retrievedParameters.get(key)); + } + + for (String key : tags.keySet()) { + assert (retrievedTags).containsKey(key); + assertEquals(tags.get(key), retrievedTags.get(key)); + } + } + + @Test + public void testCorrectEndVersion() throws GroundException { + String firstTestNode = "firstTestNode"; + long firstTestNodeId = CassandraTest.createNode(firstTestNode).getId(); + long firstNodeVersionId = CassandraTest.createNodeVersion(firstTestNodeId).getId(); + + String secondTestNode = "secondTestNode"; + long secondTestNodeId = CassandraTest.createNode(secondTestNode).getId(); + long secondNodeVersionId = CassandraTest.createNodeVersion(secondTestNodeId).getId(); + + String edgeName = "testEdge"; + long edgeId = CassandraTest.createEdge(edgeName, firstTestNode, secondTestNode).getId(); + + long edgeVersionId = CassandraTest.createEdgeVersion(edgeId, firstNodeVersionId, secondNodeVersionId).getId(); + + EdgeVersion retrieved = CassandraTest.edgeVersionDao.retrieveFromDatabase(edgeVersionId); + + assertEquals(edgeId, retrieved.getEdgeId()); + assertEquals(-1, (long) retrieved.getStructureVersionId()); + assertEquals(null, retrieved.getReference()); + assertEquals(firstNodeVersionId, retrieved.getFromNodeVersionStartId()); + assertEquals(secondNodeVersionId, retrieved.getToNodeVersionStartId()); + assertEquals(-1, retrieved.getFromNodeVersionEndId()); + assertEquals(-1, retrieved.getToNodeVersionEndId()); + + // create two new node versions in each of the nodes + List parents = new ArrayList<>(); + parents.add(firstNodeVersionId); + long fromEndId = CassandraTest.createNodeVersion(firstTestNodeId, parents).getId(); + + parents.clear(); + parents.add(fromEndId); + long newFirstNodeVersionId = CassandraTest.createNodeVersion(firstTestNodeId, parents).getId(); + + parents.clear(); + parents.add(secondNodeVersionId); + long toEndId = CassandraTest.createNodeVersion(secondTestNodeId, parents).getNodeId(); + + parents.clear(); + parents.add(toEndId); + long newSecondNodeVersionId = CassandraTest.createNodeVersion(secondTestNodeId, parents).getId(); + + parents.clear(); + parents.add(edgeVersionId); + long newEdgeVersionId = CassandraTest.createEdgeVersion(edgeId, newFirstNodeVersionId, newSecondNodeVersionId, parents).getId(); + + EdgeVersion parent = CassandraTest.edgeVersionDao.retrieveFromDatabase(edgeVersionId); + EdgeVersion child = CassandraTest.edgeVersionDao.retrieveFromDatabase(newEdgeVersionId); + + assertEquals(edgeId, child.getEdgeId()); + assertEquals(-1, (long) child.getStructureVersionId()); + assertEquals(null, child.getReference()); + assertEquals(newFirstNodeVersionId, child.getFromNodeVersionStartId()); + assertEquals(newSecondNodeVersionId, child.getToNodeVersionStartId()); + assertEquals(-1, child.getFromNodeVersionEndId()); + assertEquals(-1, child.getToNodeVersionEndId()); + + // Make sure that the end versions were set correctly + assertEquals(firstNodeVersionId, parent.getFromNodeVersionStartId()); + assertEquals(secondNodeVersionId, parent.getToNodeVersionStartId()); + assertEquals(fromEndId, parent.getFromNodeVersionEndId()); + assertEquals(toEndId, parent.getToNodeVersionEndId()); + } + + @Test(expected = GroundException.class) + public void testBadEdgeVersion() throws GroundException { + long id = 1; + + try { + CassandraTest.edgeVersionDao.retrieveFromDatabase(id); + } catch (GroundException e) { + assertEquals(GroundException.class, e.getClass()); + + throw e; + } + } +} diff --git a/modules/cassandra/test/edu/berkeley/ground/cassandra/dao/core/CassandraGraphDaoTest.java b/modules/cassandra/test/edu/berkeley/ground/cassandra/dao/core/CassandraGraphDaoTest.java new file mode 100644 index 00000000..ccefb796 --- /dev/null +++ b/modules/cassandra/test/edu/berkeley/ground/cassandra/dao/core/CassandraGraphDaoTest.java @@ -0,0 +1,103 @@ +/** + * 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. + */ + +package edu.berkeley.ground.cassandra.dao.core; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import edu.berkeley.ground.common.exception.GroundException; +import edu.berkeley.ground.common.model.core.Graph; +import edu.berkeley.ground.common.model.version.VersionHistoryDag; +import edu.berkeley.ground.common.model.version.VersionSuccessor; +import edu.berkeley.ground.cassandra.dao.CassandraTest; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import org.junit.Test; + +public class CassandraGraphDaoTest extends CassandraTest { + + public CassandraGraphDaoTest() throws GroundException { + super(); + } + + + @Test + public void testGraphCreation() throws GroundException { + String testName = "test"; + String sourceKey = "testKey"; + + CassandraTest.graphDao.create(new Graph(0L, testName, sourceKey, new HashMap<>())); + Graph graph = CassandraTest.graphDao.retrieveFromDatabase(sourceKey); + + assertEquals(testName, graph.getName()); + assertEquals(sourceKey, graph.getSourceKey()); + } + + @Test(expected = GroundException.class) + public void testRetrieveBadGraph() throws GroundException { + String sourceKey = "test"; + + try { + CassandraTest.graphDao.retrieveFromDatabase(sourceKey); + } catch (GroundException e) { + assertEquals(GroundException.class, e.getClass()); + + throw e; + } + } + + @Test(expected = GroundException.class) + public void testCreateDuplicateGraph() throws GroundException { + String graphName = "graphName"; + String graphKey = "graphKey"; + + try { + CassandraTest.graphDao.create(new Graph(0L, graphName, graphKey, new HashMap<>())); + } catch (GroundException e) { + fail(e.getMessage()); + } + + CassandraTest.graphDao.create(new Graph(0L, graphName, graphKey, new HashMap<>())); + } + + @Test + public void testTruncate() throws GroundException { + long edgeVersionId = CassandraTest.createTwoNodesAndEdge(); + + List edgeVersionIds = new ArrayList<>(); + edgeVersionIds.add(edgeVersionId); + + String graphName = "testGraph"; + long graphId = CassandraTest.createGraph(graphName).getId(); + + long graphVersionId = CassandraTest.createGraphVersion(graphId, edgeVersionIds).getId(); + + List parents = new ArrayList<>(); + parents.add(graphVersionId); + long newGraphVersionId = CassandraTest.createGraphVersion(graphId, edgeVersionIds, parents).getId(); + + CassandraTest.graphDao.truncate(graphId, 1); + + VersionHistoryDag dag = CassandraTest.versionHistoryDagDao.retrieveFromDatabase(graphId); + + assertEquals(1, dag.getEdgeIds().size()); + + VersionSuccessor successor = CassandraTest.versionSuccessorDao.retrieveFromDatabase(dag.getEdgeIds().get(0)); + + assertEquals(0, successor.getFromId()); + assertEquals(newGraphVersionId, successor.getToId()); + } +} diff --git a/modules/cassandra/test/edu/berkeley/ground/cassandra/dao/core/CassandraGraphVersionDaoTest.java b/modules/cassandra/test/edu/berkeley/ground/cassandra/dao/core/CassandraGraphVersionDaoTest.java new file mode 100644 index 00000000..f77c7087 --- /dev/null +++ b/modules/cassandra/test/edu/berkeley/ground/cassandra/dao/core/CassandraGraphVersionDaoTest.java @@ -0,0 +1,118 @@ +/** + * 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. + */ + +package edu.berkeley.ground.cassandra.dao.core; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import edu.berkeley.ground.common.exception.GroundException; +import edu.berkeley.ground.common.model.core.GraphVersion; +import edu.berkeley.ground.common.model.version.Tag; +import edu.berkeley.ground.cassandra.dao.CassandraTest; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.junit.Test; + +public class CassandraGraphVersionDaoTest extends CassandraTest { + + public CassandraGraphVersionDaoTest() throws GroundException { + super(); + } + + @Test + public void testGraphVersionCreation() throws GroundException { + long edgeVersionId = CassandraTest.createTwoNodesAndEdge(); + + List edgeVersionIds = new ArrayList<>(); + edgeVersionIds.add(edgeVersionId); + + String graphName = "testGraph"; + long graphId = CassandraTest.createGraph(graphName).getId(); + + String structureName = "testStructure"; + long structureId = CassandraTest.createStructure(structureName).getId(); + + long structureVersionId = CassandraTest.createStructureVersion(structureId).getId(); + + Map tags = CassandraTest.createTags(); + + String testReference = "http://www.google.com"; + Map parameters = new HashMap<>(); + parameters.put("http", "GET"); + + GraphVersion graphVersion = new GraphVersion(0L, tags, structureVersionId, testReference, parameters, graphId, edgeVersionIds); + long graphVersionId = CassandraTest.graphVersionDao.create(graphVersion, new ArrayList<>()).getId(); + + GraphVersion retrieved = CassandraTest.graphVersionDao.retrieveFromDatabase(graphVersionId); + + assertEquals(graphId, retrieved.getGraphId()); + assertEquals(structureVersionId, (long) retrieved.getStructureVersionId()); + assertEquals(testReference, retrieved.getReference()); + assertEquals(edgeVersionIds.size(), retrieved.getEdgeVersionIds().size()); + + List retrievedEdgeVersionIds = retrieved.getEdgeVersionIds(); + + for (long id : edgeVersionIds) { + assert (retrievedEdgeVersionIds).contains(id); + } + + assertEquals(parameters.size(), retrieved.getParameters().size()); + assertEquals(tags.size(), retrieved.getTags().size()); + + Map retrievedParameters = retrieved.getParameters(); + Map retrievedTags = retrieved.getTags(); + + for (String key : parameters.keySet()) { + assert (retrievedParameters).containsKey(key); + assertEquals(parameters.get(key), retrievedParameters.get(key)); + } + + for (String key : tags.keySet()) { + assert (retrievedTags).containsKey(key); + assertEquals(tags.get(key), retrievedTags.get(key)); + } + } + + @Test + public void testCreateEmptyGraph() throws GroundException { + String graphName = "testGraph"; + long graphId = CassandraTest.createGraph(graphName).getId(); + + GraphVersion graphVersion = new GraphVersion(0L, new HashMap<>(), -1, null, + new HashMap<>(), graphId, new ArrayList<>()); + long graphVersionId = CassandraTest.graphVersionDao.create(graphVersion, new ArrayList<>()) + .getId(); + + GraphVersion retrieved = CassandraTest.graphVersionDao + .retrieveFromDatabase(graphVersionId); + + assertTrue(retrieved.getEdgeVersionIds().isEmpty()); + } + + @Test(expected = GroundException.class) + public void testBadGraphVersion() throws GroundException { + long id = 1; + + try { + CassandraTest.graphVersionDao.retrieveFromDatabase(id); + } catch (GroundException e) { + assertEquals(GroundException.class, e.getClass()); + + throw e; + } + } +} diff --git a/modules/cassandra/test/edu/berkeley/ground/cassandra/dao/core/CassandraNodeDaoTest.java b/modules/cassandra/test/edu/berkeley/ground/cassandra/dao/core/CassandraNodeDaoTest.java new file mode 100644 index 00000000..237d8c50 --- /dev/null +++ b/modules/cassandra/test/edu/berkeley/ground/cassandra/dao/core/CassandraNodeDaoTest.java @@ -0,0 +1,160 @@ +/** + * 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. + */ + +package edu.berkeley.ground.cassandra.dao.core; + +import static junit.framework.TestCase.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import edu.berkeley.ground.common.exception.GroundException; +import edu.berkeley.ground.common.model.core.Node; +import edu.berkeley.ground.common.model.version.GroundType; +import edu.berkeley.ground.common.model.version.Tag; +import edu.berkeley.ground.common.model.version.VersionHistoryDag; +import edu.berkeley.ground.common.model.version.VersionSuccessor; +import edu.berkeley.ground.cassandra.dao.CassandraTest; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import org.junit.Test; + +public class CassandraNodeDaoTest extends CassandraTest { + + public CassandraNodeDaoTest() throws GroundException { + super(); + } + + @Test + public void testNodeCreation() throws GroundException { + Map tagsMap = new HashMap<>(); + tagsMap.put("testtag", new Tag(1, "testtag", "tag", GroundType.STRING)); + + String testName = "test"; + String sourceKey = "testKey"; + + CassandraTest.nodeDao.create(new Node(0L, testName, sourceKey, tagsMap)); + + Node node = CassandraTest.nodeDao.retrieveFromDatabase(sourceKey); + assertEquals(testName, node.getName()); + assertEquals(tagsMap, node.getTags()); + assertEquals(sourceKey, node.getSourceKey()); + } + + @Test + public void testLeafRetrieval() throws GroundException { + String sourceKey = "testNode1"; + long nodeId = CassandraTest.createNode(sourceKey).getId(); + + long nodeVersionId = CassandraTest.createNodeVersion(nodeId).getId(); + long secondNodeVersionId = CassandraTest.createNodeVersion(nodeId).getId(); + + List leaves = CassandraTest.nodeDao.getLeaves(sourceKey); + + assertTrue(leaves.contains(nodeVersionId)); + assertTrue(leaves.contains(secondNodeVersionId)); + } + + @Test(expected = GroundException.class) + public void testRetrieveBadNode() throws GroundException { + String sourceKey = "test"; + + try { + CassandraTest.nodeDao.retrieveFromDatabase(sourceKey); + } catch (GroundException e) { + assertEquals(GroundException.class, e.getClass()); + + throw e; + } + } + + @Test(expected = GroundException.class) + public void testCreateDuplicateNode() throws GroundException { + String nodeName = "nodeName"; + String nodeKey = "nodeKey"; + + try { + CassandraTest.nodeDao.create(new Node(0L, nodeName, nodeKey, new HashMap<>())); + } catch (GroundException e) { + fail(e.getMessage()); + } + + CassandraTest.nodeDao.create(new Node(0L, nodeName, nodeKey, new HashMap<>())); + } + + @Test + public void testTruncation() throws GroundException { + String testNode = "testNode"; + long testNodeId = CassandraTest.createNode(testNode).getId(); + long firstNodeVersionId = CassandraTest.createNodeVersion(testNodeId).getId(); + + List parents = new ArrayList<>(); + parents.add(firstNodeVersionId); + long newNodeVersionId = CassandraTest.createNodeVersion(testNodeId, parents).getId(); + + CassandraTest.nodeDao.truncate(testNodeId, 1); + + VersionHistoryDag dag = CassandraTest.versionHistoryDagDao + .retrieveFromDatabase(testNodeId); + assertEquals(1, dag.getEdgeIds().size()); + + VersionSuccessor successor = CassandraTest.versionSuccessorDao.retrieveFromDatabase( + dag.getEdgeIds().get(0)); + + assertEquals(0, successor.getFromId()); + assertEquals(newNodeVersionId, successor.getToId()); + } + + @Test + public void testBranchTruncation() throws GroundException { + String testNode = "testNode"; + long testNodeId = CassandraTest.createNode(testNode).getId(); + long originalId = CassandraTest.createNodeVersion(testNodeId).getId(); + + List parents = new ArrayList<>(); + parents.add(originalId); + long firstParentId = CassandraTest.createNodeVersion(testNodeId, parents).getId(); + long secondParentId = CassandraTest.createNodeVersion(testNodeId, parents).getId(); + + parents.clear(); + parents.add(firstParentId); + parents.add(secondParentId); + long childId = CassandraTest.createNodeVersion(testNodeId, parents).getId(); + + CassandraTest.nodeDao.truncate(testNodeId, 2); + + VersionHistoryDag dag = CassandraTest.versionHistoryDagDao + .retrieveFromDatabase(testNodeId); + + assertEquals(4, dag.getEdgeIds().size()); + + Set> correctSuccessors = new HashSet<>(); + correctSuccessors.add(Arrays.asList(0L, firstParentId)); + correctSuccessors.add(Arrays.asList(0L, secondParentId)); + correctSuccessors.add(Arrays.asList(firstParentId, childId)); + correctSuccessors.add(Arrays.asList(secondParentId, childId)); + + for (long successorId : dag.getEdgeIds()) { + VersionSuccessor successor = CassandraTest.versionSuccessorDao + .retrieveFromDatabase(successorId); + correctSuccessors.remove(Arrays.asList(successor.getFromId(), successor.getToId())); + } + + assertTrue(correctSuccessors.isEmpty()); + } +} diff --git a/modules/cassandra/test/edu/berkeley/ground/cassandra/dao/core/CassandraNodeVersionDaoTest.java b/modules/cassandra/test/edu/berkeley/ground/cassandra/dao/core/CassandraNodeVersionDaoTest.java new file mode 100644 index 00000000..bb227b22 --- /dev/null +++ b/modules/cassandra/test/edu/berkeley/ground/cassandra/dao/core/CassandraNodeVersionDaoTest.java @@ -0,0 +1,94 @@ +/** + * 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. + */ + +package edu.berkeley.ground.cassandra.dao.core; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import edu.berkeley.ground.common.exception.GroundException; +import edu.berkeley.ground.common.model.core.NodeVersion; +import edu.berkeley.ground.common.model.version.Tag; +import edu.berkeley.ground.cassandra.dao.CassandraTest; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.junit.Test; + +public class CassandraNodeVersionDaoTest extends CassandraTest { + + public CassandraNodeVersionDaoTest() throws GroundException { + super(); + } + + @Test + public void testNodeVersionCreation() throws GroundException { + String nodeName = "testNode"; + long nodeId = CassandraTest.createNode(nodeName).getId(); + + String structureName = "testStructure"; + long structureId = CassandraTest.createStructure(structureName).getId(); + long structureVersionId = CassandraTest.createStructureVersion(structureId).getId(); + + Map tags = CassandraTest.createTags(); + + String testReference = "http://www.google.com"; + Map parameters = new HashMap<>(); + parameters.put("http", "GET"); + + NodeVersion nodeVersion = new NodeVersion(0L, tags, structureVersionId, testReference, parameters, nodeId); + long nodeVersionId = CassandraTest.nodeVersionDao.create(nodeVersion, new ArrayList<>()).getId(); + + NodeVersion retrieved = CassandraTest.nodeVersionDao.retrieveFromDatabase(nodeVersionId); + + assertEquals(nodeId, retrieved.getNodeId()); + assertEquals(structureVersionId, (long) retrieved.getStructureVersionId()); + assertEquals(testReference, retrieved.getReference()); + + assertEquals(parameters.size(), retrieved.getParameters().size()); + assertEquals(tags.size(), retrieved.getTags().size()); + + Map retrievedParameters = retrieved.getParameters(); + Map retrievedTags = retrieved.getTags(); + + for (String key : parameters.keySet()) { + assert (retrievedParameters).containsKey(key); + assertEquals(parameters.get(key), retrievedParameters.get(key)); + } + + for (String key : tags.keySet()) { + assert (retrievedTags).containsKey(key); + assertEquals(tags.get(key), retrievedTags.get(key)); + } + + List leaves = CassandraTest.nodeDao.getLeaves(nodeName); + + assertTrue(leaves.contains(nodeVersionId)); + assertTrue(1 == leaves.size()); + } + + @Test(expected = GroundException.class) + public void testBadNodeVersion() throws GroundException { + long id = 1; + + try { + CassandraTest.nodeVersionDao.retrieveFromDatabase(id); + } catch (GroundException e) { + assertEquals(GroundException.class, e.getClass()); + + throw e; + } + } +} diff --git a/modules/cassandra/test/edu/berkeley/ground/cassandra/dao/core/CassandraRichVersionDaoTest.java b/modules/cassandra/test/edu/berkeley/ground/cassandra/dao/core/CassandraRichVersionDaoTest.java new file mode 100644 index 00000000..12781e0e --- /dev/null +++ b/modules/cassandra/test/edu/berkeley/ground/cassandra/dao/core/CassandraRichVersionDaoTest.java @@ -0,0 +1,128 @@ +/** + * 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. + */ + +package edu.berkeley.ground.cassandra.dao.core; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import edu.berkeley.ground.common.exception.GroundException; +import edu.berkeley.ground.common.model.core.RichVersion; +import edu.berkeley.ground.common.model.version.GroundType; +import edu.berkeley.ground.common.model.version.Tag; +import edu.berkeley.ground.cassandra.dao.CassandraTest; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; +import org.junit.Test; + +public class CassandraRichVersionDaoTest extends CassandraTest { + + public CassandraRichVersionDaoTest() throws GroundException { + super(); + } + + @Test + public void testReference() throws GroundException { + long id = 1; + String testReference = "http://www.google.com"; + Map parameters = new HashMap<>(); + parameters.put("http", "GET"); + parameters.put("ftp", "test"); + + RichVersion richVersion = new RichVersion(id, new HashMap(), -1L, testReference, + parameters); + CassandraTest.cassandraRichVersionDao.create(richVersion, new ArrayList<>()); + + RichVersion retrieved = CassandraTest.cassandraRichVersionDao.retrieveFromDatabase(id); + + assertEquals(id, retrieved.getId()); + assertEquals(testReference, retrieved.getReference()); + + Map retrievedParams = retrieved.getParameters(); + for (String key : parameters.keySet()) { + assert (retrievedParams).containsKey(key); + assertEquals(parameters.get(key), retrievedParams.get(key)); + } + } + + @Test + public void testTags() throws GroundException { + long id = 1; + + Map tags = new HashMap<>(); + tags.put("justkey", new Tag(-1, "justkey", null, null)); + tags.put("withintvalue", new Tag(-1, "withintvalue", 1, GroundType.INTEGER)); + tags.put("withstringvalue", new Tag(-1, "withstringvalue", "1", GroundType.STRING)); + tags.put("withboolvalue", new Tag(-1, "withboolvalue", true, GroundType.BOOLEAN)); + + RichVersion richVersion = new RichVersion(id, tags, -1L, null, new HashMap<>()); + CassandraTest.cassandraRichVersionDao.create(richVersion, new ArrayList<>()); + + RichVersion retrieved = CassandraTest.cassandraRichVersionDao.retrieveFromDatabase(id); + + assertEquals(id, retrieved.getId()); + assertEquals(tags.size(), retrieved.getTags().size()); + + Map retrievedTags = retrieved.getTags(); + for (String key : tags.keySet()) { + assert (retrievedTags).containsKey(key); + assertEquals(tags.get(key), retrievedTags.get(key)); + assertEquals(retrieved.getId(), retrievedTags.get(key).getId()); + } + } + + @Test + public void testStructureVersionConformation() throws GroundException { + long id = 10; + + String structureName = "testStructure"; + long structureId = CassandraTest.createStructure(structureName).getId(); + long structureVersionId = CassandraTest.createStructureVersion(structureId).getId(); + + Map tags = CassandraTest.createTags(); + + RichVersion richVersion = new RichVersion(id, tags, structureVersionId, null, + new HashMap<>()); + CassandraTest.cassandraRichVersionDao.create(richVersion, new ArrayList<>()); + + RichVersion retrieved = CassandraTest.cassandraRichVersionDao.retrieveFromDatabase(id); + assertEquals((long) retrieved.getStructureVersionId(), structureVersionId); + } + + @Test(expected = GroundException.class) + public void testStructureVersionFails() throws GroundException { + long structureVersionId = -1; + long id = 1; + + // none of these operations should fail + try { + String structureName = "testStructure"; + long structureId = CassandraTest.createStructure(structureName).getId(); + structureVersionId = CassandraTest.createStructureVersion(structureId).getId(); + } catch (GroundException ge) { + fail(ge.getMessage()); + } + + Map tags = new HashMap<>(); + tags.put("intfield", new Tag(-1, "intfield", 1, GroundType.INTEGER)); + tags.put("intfield", new Tag(-1, "strfield", "1", GroundType.STRING)); + tags.put("intfield", new Tag(-1, "boolfield", true, GroundType.BOOLEAN)); + + // this should fail + RichVersion richVersion = new RichVersion(id, tags, structureVersionId, null, + new HashMap<>()); + CassandraTest.cassandraRichVersionDao.create(richVersion, new ArrayList<>()); + } +} diff --git a/modules/cassandra/test/edu/berkeley/ground/cassandra/dao/core/CassandraStructureDaoTest.java b/modules/cassandra/test/edu/berkeley/ground/cassandra/dao/core/CassandraStructureDaoTest.java new file mode 100644 index 00000000..6aad1acd --- /dev/null +++ b/modules/cassandra/test/edu/berkeley/ground/cassandra/dao/core/CassandraStructureDaoTest.java @@ -0,0 +1,118 @@ +package edu.berkeley.ground.cassandra.dao.core; + +/** + * 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. + */ + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import edu.berkeley.ground.common.exception.GroundException; +import edu.berkeley.ground.common.model.core.Structure; +import edu.berkeley.ground.common.model.version.VersionHistoryDag; +import edu.berkeley.ground.common.model.version.VersionSuccessor; +import edu.berkeley.ground.cassandra.dao.CassandraTest; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import org.junit.Test; + +public class CassandraStructureDaoTest extends CassandraTest { + + public CassandraStructureDaoTest() throws GroundException { + super(); + } + + + @Test + public void testStructureCreation() throws GroundException { + String testName = "test"; + String sourceKey = "testKey"; + + Structure insertStructure = new Structure(0L, testName, sourceKey, new HashMap<>()); + + CassandraTest.structureDao.create(insertStructure); + Structure structure = CassandraTest.structureDao.retrieveFromDatabase(sourceKey); + + assertEquals(testName, structure.getName()); + assertEquals(sourceKey, structure.getSourceKey()); + } + + @Test + public void testLeafRetrieval() throws GroundException { + String sourceKey = "testStructure"; + long structureId = CassandraTest.createStructure(sourceKey).getId(); + + long structureVersionId = CassandraTest.createStructureVersion(structureId).getId(); + long secondStructureVersionId = CassandraTest.createStructureVersion(structureId).getId(); + + List leaves = CassandraTest.structureDao.getLeaves(sourceKey); + + assertTrue(leaves.contains(structureVersionId)); + assertTrue(leaves.contains(secondStructureVersionId)); + } + + @Test(expected = GroundException.class) + public void testRetrieveBadStructure() throws GroundException { + String sourceKey = "test"; + + try { + CassandraTest.structureDao.retrieveFromDatabase(sourceKey); + } catch (GroundException e) { + assertEquals(GroundException.class, e.getClass()); + + throw e; + } + } + + @Test(expected = GroundException.class) + public void testCreateDuplicateStructure() throws GroundException { + String structureName = "structureName"; + String structureKey = "structureKey"; + + try { + Structure structure = new Structure(0L, structureName, structureKey, new HashMap<>()); + CassandraTest.structureDao.create(structure); + } catch (GroundException e) { + fail(e.getMessage()); + } + + Structure sameStructure = new Structure(0L, structureName, structureKey, new HashMap<>()); + CassandraTest.structureDao.create(sameStructure); + } + + + @Test + public void testTruncate() throws GroundException { + String structureName = "testStructure1"; + long structureId = CassandraTest.createStructure(structureName).getId(); + + long structureVersionId = CassandraTest.createStructureVersion(structureId).getId(); + + List parents = new ArrayList<>(); + parents.add(structureVersionId); + long newStructureVersionId = CassandraTest.createStructureVersion(structureId, parents).getId(); + + CassandraTest.structureDao.truncate(structureId, 1); + + VersionHistoryDag dag = CassandraTest.versionHistoryDagDao.retrieveFromDatabase(structureId); + + assertEquals(1, dag.getEdgeIds().size()); + VersionSuccessor successor = CassandraTest.versionSuccessorDao.retrieveFromDatabase( + dag.getEdgeIds().get(0)); + + assertEquals(0, successor.getFromId()); + assertEquals(newStructureVersionId, successor.getToId()); + } +} diff --git a/modules/cassandra/test/edu/berkeley/ground/cassandra/dao/core/CassandraStructureVersionDaoTest.java b/modules/cassandra/test/edu/berkeley/ground/cassandra/dao/core/CassandraStructureVersionDaoTest.java new file mode 100644 index 00000000..213fb083 --- /dev/null +++ b/modules/cassandra/test/edu/berkeley/ground/cassandra/dao/core/CassandraStructureVersionDaoTest.java @@ -0,0 +1,74 @@ +/** + * 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. + */ + +package edu.berkeley.ground.cassandra.dao.core; + +import static org.junit.Assert.assertEquals; + +import edu.berkeley.ground.common.exception.GroundException; +import edu.berkeley.ground.common.model.core.StructureVersion; +import edu.berkeley.ground.common.model.version.GroundType; +import edu.berkeley.ground.cassandra.dao.CassandraTest; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; +import org.junit.Test; + +public class CassandraStructureVersionDaoTest extends CassandraTest { + + public CassandraStructureVersionDaoTest() throws GroundException { + super(); + } + + + @Test + public void testStructureVersionCreation() throws GroundException { + String structureName = "testStructure"; + long structureId = CassandraTest.createStructure(structureName).getId(); + + Map structureVersionAttributes = new HashMap<>(); + structureVersionAttributes.put("intfield", GroundType.INTEGER); + structureVersionAttributes.put("boolfield", GroundType.BOOLEAN); + structureVersionAttributes.put("strfield", GroundType.STRING); + + StructureVersion structureVersion = new StructureVersion(0L, structureId, + structureVersionAttributes); + long structureVersionId = CassandraTest.structureVersionDao + .create(structureVersion, new ArrayList<>()).getId(); + + StructureVersion retrieved = CassandraTest.structureVersionDao + .retrieveFromDatabase(structureVersionId); + + assertEquals(structureId, retrieved.getStructureId()); + Map retrievedAttributes = retrieved.getAttributes(); + + for (String key : structureVersionAttributes.keySet()) { + assert (retrievedAttributes).containsKey(key); + assertEquals(structureVersionAttributes.get(key), retrievedAttributes.get(key)); + } + } + + @Test(expected = GroundException.class) + public void testBadStructureVersion() throws GroundException { + long id = 1; + + try { + CassandraTest.structureVersionDao.retrieveFromDatabase(id); + } catch (GroundException e) { + assertEquals(GroundException.class, e.getClass()); + + throw e; + } + } +} diff --git a/modules/cassandra/test/edu/berkeley/ground/cassandra/dao/core/CassandraTagDaoTest.java b/modules/cassandra/test/edu/berkeley/ground/cassandra/dao/core/CassandraTagDaoTest.java new file mode 100644 index 00000000..e5e3b953 --- /dev/null +++ b/modules/cassandra/test/edu/berkeley/ground/cassandra/dao/core/CassandraTagDaoTest.java @@ -0,0 +1,69 @@ +/** + * 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. + */ +package edu.berkeley.ground.cassandra.dao.core; + +import static org.junit.Assert.assertTrue; + +import edu.berkeley.ground.common.exception.GroundException; +import edu.berkeley.ground.common.model.core.Node; +import edu.berkeley.ground.common.model.core.NodeVersion; +import edu.berkeley.ground.common.model.version.GroundType; +import edu.berkeley.ground.common.model.version.Tag; +import edu.berkeley.ground.cassandra.dao.CassandraTest; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.junit.Test; + +public class CassandraTagDaoTest extends CassandraTest { + + public CassandraTagDaoTest() throws GroundException { + super(); + } + + @Test + public void testGetItemIdsByTag() throws GroundException { + Map tagsMap = new HashMap<>(); + tagsMap.put("testtag", new Tag(1, "testtag", "tag", GroundType.STRING)); + + long nodeId1 = CassandraTest.nodeDao.create(new Node(0L, null, "test1", tagsMap)).getId(); + long nodeId2 = CassandraTest.nodeDao.create(new Node(0L, null, "test2", tagsMap)).getId(); + + List ids = CassandraTest.tagDao.getItemIdsByTag("testtag"); + + assertTrue(ids.contains(nodeId1)); + assertTrue(ids.contains(nodeId2)); + } + + @Test + public void testGetVersionIdsByTag() throws GroundException { + Map tagsMap = new HashMap<>(); + tagsMap.put("testtag", new Tag(1, "testtag", "tag", GroundType.STRING)); + + long nodeId = CassandraTest.createNode("testNode").getId(); + + NodeVersion nodeVersion1 = new NodeVersion(0L, tagsMap, -1, null, new HashMap<>(), nodeId); + long nodeVersionId1 = CassandraTest.nodeVersionDao.create(nodeVersion1, new ArrayList<>()) + .getId(); + NodeVersion nodeVersion2 = new NodeVersion(0L, tagsMap, -1, null, new HashMap<>(), nodeId); + long nodeVersionId2 = CassandraTest.nodeVersionDao.create(nodeVersion2, new ArrayList<>()) + .getId(); + + List ids = CassandraTest.tagDao.getVersionIdsByTag("testtag"); + + assertTrue(ids.contains(nodeVersionId1)); + assertTrue(ids.contains(nodeVersionId2)); + } +} diff --git a/modules/cassandra/test/edu/berkeley/ground/cassandra/dao/usage/CassandraLineageEdgeDaoTest.java b/modules/cassandra/test/edu/berkeley/ground/cassandra/dao/usage/CassandraLineageEdgeDaoTest.java new file mode 100644 index 00000000..d2a06585 --- /dev/null +++ b/modules/cassandra/test/edu/berkeley/ground/cassandra/dao/usage/CassandraLineageEdgeDaoTest.java @@ -0,0 +1,99 @@ +package edu.berkeley.ground.cassandra.dao.usage; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import edu.berkeley.ground.common.exception.GroundException; +import edu.berkeley.ground.common.model.usage.LineageEdge; +import edu.berkeley.ground.common.model.version.VersionHistoryDag; +import edu.berkeley.ground.common.model.version.VersionSuccessor; +import edu.berkeley.ground.cassandra.dao.CassandraTest; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import org.junit.Test; + +public class CassandraLineageEdgeDaoTest extends CassandraTest { + + public CassandraLineageEdgeDaoTest() throws GroundException { + super(); + } + + @Test + public void testLineageEdgeCreation() throws GroundException { + String testName = "test"; + String sourceKey = "testKey"; + + LineageEdge insertLineageEdge = new LineageEdge(0L, testName, sourceKey, new HashMap<>()); + CassandraTest.lineageEdgeDao.create(insertLineageEdge); + LineageEdge lineageEdge = CassandraTest.lineageEdgeDao.retrieveFromDatabase(sourceKey); + + assertEquals(testName, lineageEdge.getName()); + assertEquals(sourceKey, lineageEdge.getSourceKey()); + } + + @Test(expected = GroundException.class) + public void testRetrieveBadLineageEdge() throws GroundException { + String sourceKey = "test"; + + try { + CassandraTest.lineageEdgeDao.retrieveFromDatabase(sourceKey); + } catch (GroundException e) { + assertEquals(GroundException.class, e.getClass()); + + throw e; + } + } + + @Test(expected = GroundException.class) + public void testCreateDuplicateLineageEdge() throws GroundException { + String lineageEdgeName = "lineageEdgeName"; + String lineageEdgeKey = "lineageEdgeKey"; + + try { + LineageEdge lineageEdge = new LineageEdge(0L, lineageEdgeName, lineageEdgeKey, + new HashMap<>()); + CassandraTest.lineageEdgeDao.create(lineageEdge); + } catch (GroundException e) { + fail(e.getMessage()); + } + + LineageEdge duplicateEdge = new LineageEdge(0L, lineageEdgeName, lineageEdgeKey, + new HashMap<>()); + CassandraTest.lineageEdgeDao.create(duplicateEdge); + } + + @Test + public void testTruncate() throws GroundException { + String firstTestNode = "firstTestNode"; + long firstTestNodeId = CassandraTest.createNode(firstTestNode).getId(); + long firstNodeVersionId = CassandraTest.createNodeVersion(firstTestNodeId).getId(); + + String secondTestNode = "secondTestNode"; + long secondTestNodeId = CassandraTest.createNode(secondTestNode).getId(); + long secondNodeVersionId = CassandraTest.createNodeVersion(secondTestNodeId).getId(); + + String lineageEdgeName = "testLineageEdge"; + long lineageEdgeId = CassandraTest.createLineageEdge(lineageEdgeName).getId(); + long lineageEdgeVersionId = CassandraTest.createLineageEdgeVersion(lineageEdgeId, + firstNodeVersionId, secondNodeVersionId).getId(); + + List parents = new ArrayList<>(); + parents.add(lineageEdgeVersionId); + long newLineageEdgeVersionId = CassandraTest.createLineageEdgeVersion(lineageEdgeId, + firstNodeVersionId, secondNodeVersionId, parents).getId(); + + CassandraTest.lineageEdgeDao.truncate(lineageEdgeId, 1); + + VersionHistoryDag dag = CassandraTest.versionHistoryDagDao + .retrieveFromDatabase(lineageEdgeId); + + assertEquals(1, dag.getEdgeIds().size()); + + VersionSuccessor successor = CassandraTest.versionSuccessorDao.retrieveFromDatabase( + dag.getEdgeIds().get(0)); + + assertEquals(0, successor.getFromId()); + assertEquals(newLineageEdgeVersionId, successor.getToId()); + } +} diff --git a/modules/cassandra/test/edu/berkeley/ground/cassandra/dao/usage/CassandraLineageEdgeVersionDaoTest.java b/modules/cassandra/test/edu/berkeley/ground/cassandra/dao/usage/CassandraLineageEdgeVersionDaoTest.java new file mode 100644 index 00000000..94e465b0 --- /dev/null +++ b/modules/cassandra/test/edu/berkeley/ground/cassandra/dao/usage/CassandraLineageEdgeVersionDaoTest.java @@ -0,0 +1,88 @@ +package edu.berkeley.ground.cassandra.dao.usage; + +import static org.junit.Assert.assertEquals; + +import edu.berkeley.ground.common.exception.GroundException; +import edu.berkeley.ground.common.model.usage.LineageEdgeVersion; +import edu.berkeley.ground.common.model.version.Tag; +import edu.berkeley.ground.cassandra.dao.CassandraTest; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; +import org.junit.Test; + +public class CassandraLineageEdgeVersionDaoTest extends CassandraTest { + + public CassandraLineageEdgeVersionDaoTest() throws GroundException { + super(); + } + + @Test + public void testLineageEdgeVersionCreation() throws GroundException { + String firstTestNode = "firstTestNode"; + long firstTestNodeId = CassandraTest.createNode(firstTestNode).getId(); + long firstNodeVersionId = CassandraTest.createNodeVersion(firstTestNodeId).getId(); + + String secondTestNode = "secondTestNode"; + long secondTestNodeId = CassandraTest.createNode(secondTestNode).getId(); + long secondNodeVersionId = CassandraTest.createNodeVersion(secondTestNodeId).getId(); + + String lineageEdgeName = "testLineageEdge"; + long lineageEdgeId = CassandraTest.createLineageEdge(lineageEdgeName).getId(); + + String structureName = "testStructure"; + long structureId = CassandraTest.createStructure(structureName).getId(); + long structureVersionId = CassandraTest.createStructureVersion(structureId).getId(); + + Map tags = CassandraTest.createTags(); + + String testReference = "http://www.google.com"; + Map parameters = new HashMap<>(); + parameters.put("http", "GET"); + + LineageEdgeVersion lineageEdgeVersion = new LineageEdgeVersion(0L, tags, structureVersionId, + testReference, parameters, firstNodeVersionId, + secondNodeVersionId, lineageEdgeId); + + long lineageEdgeVersionId = CassandraTest.lineageEdgeVersionDao + .create(lineageEdgeVersion, new ArrayList<>()).getId(); + + LineageEdgeVersion retrieved = CassandraTest.lineageEdgeVersionDao + .retrieveFromDatabase(lineageEdgeVersionId); + + assertEquals(lineageEdgeId, retrieved.getLineageEdgeId()); + assertEquals(structureVersionId, (long) retrieved.getStructureVersionId()); + assertEquals(testReference, retrieved.getReference()); + assertEquals(retrieved.getFromId(), firstNodeVersionId); + assertEquals(retrieved.getToId(), secondNodeVersionId); + + assertEquals(parameters.size(), retrieved.getParameters().size()); + assertEquals(tags.size(), retrieved.getTags().size()); + + Map retrievedParameters = retrieved.getParameters(); + Map retrievedTags = retrieved.getTags(); + + for (String key : parameters.keySet()) { + assert (retrievedParameters).containsKey(key); + assertEquals(parameters.get(key), retrievedParameters.get(key)); + } + + for (String key : tags.keySet()) { + assert (retrievedTags).containsKey(key); + assertEquals(tags.get(key), retrievedTags.get(key)); + } + } + + @Test(expected = GroundException.class) + public void testBadLineageEdgeVersion() throws GroundException { + long id = 1; + + try { + CassandraTest.lineageEdgeVersionDao.retrieveFromDatabase(id); + } catch (GroundException e) { + assertEquals(GroundException.class, e.getClass()); + + throw e; + } + } +} diff --git a/modules/cassandra/test/edu/berkeley/ground/cassandra/dao/usage/CassandraLineageGraphDaoTest.java b/modules/cassandra/test/edu/berkeley/ground/cassandra/dao/usage/CassandraLineageGraphDaoTest.java new file mode 100644 index 00000000..0889a84c --- /dev/null +++ b/modules/cassandra/test/edu/berkeley/ground/cassandra/dao/usage/CassandraLineageGraphDaoTest.java @@ -0,0 +1,104 @@ +package edu.berkeley.ground.cassandra.dao.usage; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import edu.berkeley.ground.common.exception.GroundException; +import edu.berkeley.ground.common.model.usage.LineageGraph; +import edu.berkeley.ground.common.model.version.VersionHistoryDag; +import edu.berkeley.ground.common.model.version.VersionSuccessor; +import edu.berkeley.ground.cassandra.dao.CassandraTest; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import org.junit.Test; + +public class CassandraLineageGraphDaoTest extends CassandraTest { + + public CassandraLineageGraphDaoTest() throws GroundException { + super(); + } + + @Test + public void testLineageGraphCreation() throws GroundException { + String testName = "test"; + String sourceKey = "testKey"; + + LineageGraph lineageGraph = new LineageGraph(0L, testName, sourceKey, new HashMap<>()); + CassandraTest.lineageGraphDao.create(lineageGraph); + LineageGraph graph = CassandraTest.lineageGraphDao.retrieveFromDatabase(sourceKey); + + assertEquals(testName, graph.getName()); + assertEquals(sourceKey, graph.getSourceKey()); + } + + @Test(expected = GroundException.class) + public void testRetrieveBadLineageGraph() throws GroundException { + String sourceKey = "test"; + + try { + CassandraTest.lineageGraphDao.retrieveFromDatabase(sourceKey); + } catch (GroundException e) { + assertEquals(GroundException.class, e.getClass()); + + throw e; + } + } + + @Test(expected = GroundException.class) + public void testCreateDuplicateLineageGraph() throws GroundException { + String lineageGraphName = "lineageGraphName"; + String lineageGraphKey = "lineageGraphKey"; + + try { + LineageGraph lineageGraph = new LineageGraph(0L, lineageGraphName, lineageGraphKey, + new HashMap<>()); + CassandraTest.lineageGraphDao.create(lineageGraph); + } catch (GroundException e) { + fail(e.getMessage()); + } + + LineageGraph duplicateGraph = new LineageGraph(0L, lineageGraphName, lineageGraphKey, + new HashMap<>()); + CassandraTest.lineageGraphDao.create(duplicateGraph); + } + + @Test + public void testTruncate() throws GroundException { + String firstTestNode = "firstTestNode"; + long firstTestNodeId = CassandraTest.createNode(firstTestNode).getId(); + long firstNodeVersionId = CassandraTest.createNodeVersion(firstTestNodeId).getId(); + + String secondTestNode = "secondTestNode"; + long secondTestNodeId = CassandraTest.createNode(secondTestNode).getId(); + long secondNodeVersionId = CassandraTest.createNodeVersion(secondTestNodeId).getId(); + + String lineageEdgeName = "testLineageEdge"; + long lineageEdgeId = CassandraTest.createLineageEdge(lineageEdgeName).getId(); + long lineageEdgeVersionId = CassandraTest.createLineageEdgeVersion(lineageEdgeId, firstNodeVersionId, secondNodeVersionId).getId(); + + List lineageEdgeVersionIds = new ArrayList<>(); + lineageEdgeVersionIds.add(lineageEdgeVersionId); + + String lineageGraphName = "testLineageGraph"; + long lineageGraphId = CassandraTest.createLineageGraph(lineageGraphName).getId(); + long lineageGraphVersionId = CassandraTest.createLineageGraphVersion(lineageGraphId, lineageEdgeVersionIds).getId(); + + List parents = new ArrayList<>(); + parents.add(lineageGraphVersionId); + long newLineageGraphVersionId = CassandraTest.createLineageGraphVersion(lineageGraphId, lineageEdgeVersionIds, parents).getId(); + + CassandraTest.lineageGraphDao.truncate(lineageGraphId, 1); + + VersionHistoryDag dag = CassandraTest.versionHistoryDagDao + .retrieveFromDatabase(lineageGraphId); + + assertEquals(1, dag.getEdgeIds().size()); + + VersionSuccessor successor = CassandraTest.versionSuccessorDao.retrieveFromDatabase( + dag.getEdgeIds().get(0)); + + assertEquals(0, successor.getFromId()); + assertEquals(newLineageGraphVersionId, successor.getToId()); + } +} diff --git a/modules/cassandra/test/edu/berkeley/ground/cassandra/dao/usage/CassandraLineageGraphVersionDaoTest.java b/modules/cassandra/test/edu/berkeley/ground/cassandra/dao/usage/CassandraLineageGraphVersionDaoTest.java new file mode 100644 index 00000000..e5c3e838 --- /dev/null +++ b/modules/cassandra/test/edu/berkeley/ground/cassandra/dao/usage/CassandraLineageGraphVersionDaoTest.java @@ -0,0 +1,116 @@ +package edu.berkeley.ground.cassandra.dao.usage; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import edu.berkeley.ground.common.exception.GroundException; +import edu.berkeley.ground.common.model.usage.LineageGraphVersion; +import edu.berkeley.ground.common.model.version.Tag; +import edu.berkeley.ground.cassandra.dao.CassandraTest; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.junit.Test; + +public class CassandraLineageGraphVersionDaoTest extends CassandraTest { + + public CassandraLineageGraphVersionDaoTest() throws GroundException { + super(); + } + + @Test + public void testLineageGraphVersionCreation() throws GroundException { + String firstTestNode = "firstTestNode"; + long firstTestNodeId = CassandraTest.createNode(firstTestNode).getId(); + long firstNodeVersionId = CassandraTest.createNodeVersion(firstTestNodeId).getId(); + + String secondTestNode = "secondTestNode"; + long secondTestNodeId = CassandraTest.createNode(secondTestNode).getId(); + long secondNodeVersionId = CassandraTest.createNodeVersion(secondTestNodeId).getId(); + + String lineageEdgeName = "testLineageEdge"; + long lineageEdgeId = CassandraTest.createLineageEdge(lineageEdgeName).getId(); + + long lineageEdgeVersionId = CassandraTest.createLineageEdgeVersion(lineageEdgeId, + firstNodeVersionId, secondNodeVersionId).getId(); + + List lineageEdgeVersionIds = new ArrayList<>(); + lineageEdgeVersionIds.add(lineageEdgeVersionId); + + String lineageGraphName = "testLineageGraph"; + long lineageGraphId = CassandraTest.createLineageGraph(lineageGraphName).getId(); + + String structureName = "testStructure"; + long structureId = CassandraTest.createStructure(structureName).getId(); + long structureVersionId = CassandraTest.createStructureVersion(structureId).getId(); + + Map tags = CassandraTest.createTags(); + + String testReference = "http://www.google.com"; + Map parameters = new HashMap<>(); + parameters.put("http", "GET"); + + long lineageGraphVersionId = CassandraTest.lineageGraphVersionDao + .create(new LineageGraphVersion(0L, tags, structureVersionId, testReference, parameters, + lineageGraphId, + lineageEdgeVersionIds), new ArrayList<>()).getId(); + + LineageGraphVersion retrieved = CassandraTest.lineageGraphVersionDao + .retrieveFromDatabase(lineageGraphVersionId); + + assertEquals(lineageGraphId, retrieved.getLineageGraphId()); + assertEquals(structureVersionId, (long) retrieved.getStructureVersionId()); + assertEquals(testReference, retrieved.getReference()); + assertEquals(lineageEdgeVersionIds.size(), retrieved.getLineageEdgeVersionIds().size()); + + List retrievedLineageEdgeVersionIds = retrieved.getLineageEdgeVersionIds(); + + for (long id : lineageEdgeVersionIds) { + assert (retrievedLineageEdgeVersionIds).contains(id); + } + + assertEquals(parameters.size(), retrieved.getParameters().size()); + assertEquals(tags.size(), retrieved.getTags().size()); + + Map retrievedParameters = retrieved.getParameters(); + Map retrievedTags = retrieved.getTags(); + + for (String key : parameters.keySet()) { + assert (retrievedParameters).containsKey(key); + assertEquals(parameters.get(key), retrievedParameters.get(key)); + } + + for (String key : tags.keySet()) { + assert (retrievedTags).containsKey(key); + assertEquals(tags.get(key), retrievedTags.get(key)); + } + } + + @Test(expected = GroundException.class) + public void testBadLineageGraphVersion() throws GroundException { + long id = 1; + + try { + CassandraTest.lineageGraphVersionDao.retrieveFromDatabase(id); + } catch (GroundException e) { + assertEquals(GroundException.class, e.getClass()); + + throw e; + } + } + + @Test + public void testCreateEmptyLineageGraph() throws GroundException { + String lineageGraphName = "testGraph"; + long lineageGraphId = CassandraTest.createLineageGraph(lineageGraphName).getId(); + + long lineageGraphVersionId = CassandraTest.createLineageGraphVersion(lineageGraphId, + new ArrayList<>()).getId(); + + LineageGraphVersion retrieved = CassandraTest.lineageGraphVersionDao + .retrieveFromDatabase(lineageGraphVersionId); + + assertTrue(retrieved.getLineageEdgeVersionIds().isEmpty()); + } +} diff --git a/modules/cassandra/test/edu/berkeley/ground/cassandra/dao/version/CassandraItemDaoTest.java b/modules/cassandra/test/edu/berkeley/ground/cassandra/dao/version/CassandraItemDaoTest.java new file mode 100644 index 00000000..fd712204 --- /dev/null +++ b/modules/cassandra/test/edu/berkeley/ground/cassandra/dao/version/CassandraItemDaoTest.java @@ -0,0 +1,258 @@ +/** + * 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. + */ + +package edu.berkeley.ground.cassandra.dao.version; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import edu.berkeley.ground.common.exception.GroundException; +import edu.berkeley.ground.common.model.version.GroundType; +import edu.berkeley.ground.common.model.version.Item; +import edu.berkeley.ground.common.model.version.Tag; +import edu.berkeley.ground.common.model.version.Version; +import edu.berkeley.ground.common.model.version.VersionHistoryDag; +import edu.berkeley.ground.common.model.version.VersionSuccessor; +import edu.berkeley.ground.cassandra.dao.CassandraTest; +import edu.berkeley.ground.cassandra.util.CassandraStatements; +import edu.berkeley.ground.cassandra.util.CassandraUtils; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.junit.Test; + +public class CassandraItemDaoTest extends CassandraTest { + + public CassandraItemDaoTest() throws GroundException { + super(); + } + + @Test + public void testCorrectUpdateWithParent() throws GroundException { + long testId = 1; + + Item item = new Item(testId, new HashMap<>()); + CassandraTest.cassandraItemDao.create(item); + + long fromId = 2; + long toId = 3; + + CassandraUtils.executeCqlList(CassandraTest.dbSource, (CassandraStatements) CassandraTest.cassandraVersionDao.insert(new Version(fromId))); + CassandraUtils.executeCqlList(CassandraTest.dbSource, (CassandraStatements) CassandraTest.cassandraVersionDao.insert(new Version(toId))); + + List parentIds = new ArrayList<>(); + CassandraUtils.executeCqlList(dbSource, (CassandraStatements) CassandraTest.cassandraItemDao.update(testId, fromId, parentIds)); + + parentIds.clear(); + parentIds.add(fromId); + CassandraUtils.executeCqlList(dbSource, (CassandraStatements) CassandraTest.cassandraItemDao.update(testId, toId, parentIds)); + + VersionHistoryDag dag = CassandraTest.versionHistoryDagDao.retrieveFromDatabase(testId); + + assertEquals(2, dag.getEdgeIds().size()); + assertEquals(toId, (long) dag.getLeaves().get(0)); + + VersionSuccessor successor = null; + for (long id : dag.getEdgeIds()) { + successor = CassandraTest.versionSuccessorDao.retrieveFromDatabase(id); + + if (successor.getFromId() != 0) { + break; + } + } + + if (successor != null) { + assertEquals(fromId, successor.getFromId()); + assertEquals(toId, successor.getToId()); + + return; + } + + fail(); + } + + @Test + public void testCorrectUpdateWithoutParent() throws GroundException { + long testId = 1; + + CassandraTest.cassandraItemDao.create(new Item(testId, new HashMap<>())); + long toId = 2; + CassandraUtils.executeCqlList(CassandraTest.dbSource, (CassandraStatements) CassandraTest.cassandraVersionDao.insert(new Version(toId))); + + List parentIds = new ArrayList<>(); + + // No parent is specified, and there is no other version in this Item, we should + // automatically make this a child of EMPTY + CassandraUtils.executeCqlList(dbSource, (CassandraStatements) CassandraTest.cassandraItemDao.update(testId, toId, parentIds)); + + VersionHistoryDag dag = CassandraTest.versionHistoryDagDao.retrieveFromDatabase(testId); + + assertEquals(1, dag.getEdgeIds().size()); + assertEquals(toId, (long) dag.getLeaves().get(0)); + + VersionSuccessor successor = CassandraTest.versionSuccessorDao.retrieveFromDatabase( + dag.getEdgeIds().get(0)); + + assertEquals(0, successor.getFromId()); + assertEquals(toId, successor.getToId()); + } + + @Test + public void testCorrectUpdateWithLinearHistory() throws GroundException { + long testId = 1; + + CassandraTest.cassandraItemDao.create(new Item(testId, new HashMap<>())); + + long fromId = 2; + long toId = 3; + + CassandraUtils.executeCqlList(CassandraTest.dbSource, (CassandraStatements) CassandraTest.cassandraVersionDao.insert(new Version(fromId))); + CassandraUtils.executeCqlList(CassandraTest.dbSource, (CassandraStatements) CassandraTest.cassandraVersionDao.insert(new Version(toId))); + List parentIds = new ArrayList<>(); + + // first, make from a child of EMPTY + CassandraUtils.executeCqlList(dbSource, (CassandraStatements) CassandraTest.cassandraItemDao.update(testId, fromId, parentIds)); + + // then, add to as a child and make sure that it becomes a child of from + parentIds.clear(); + parentIds.add(fromId); + CassandraUtils.executeCqlList(dbSource, (CassandraStatements) CassandraTest.cassandraItemDao.update(testId, toId, parentIds)); + + VersionHistoryDag dag = CassandraTest.versionHistoryDagDao.retrieveFromDatabase(testId); + + assertEquals(2, dag.getEdgeIds().size()); + assertEquals(toId, (long) dag.getLeaves().get(0)); + + VersionSuccessor fromSuccessor = CassandraTest.versionSuccessorDao.retrieveFromDatabase( + dag.getEdgeIds().get(0)); + + VersionSuccessor toSuccessor = CassandraTest.versionSuccessorDao.retrieveFromDatabase( + dag.getEdgeIds().get(1)); + + if (fromSuccessor.getFromId() != 0) { + VersionSuccessor tmp = fromSuccessor; + fromSuccessor = toSuccessor; + toSuccessor = tmp; + } + + assertEquals(0, fromSuccessor.getFromId()); + assertEquals(fromId, fromSuccessor.getToId()); + + assertEquals(fromId, toSuccessor.getFromId()); + assertEquals(toId, toSuccessor.getToId()); + } + + @Test(expected = GroundException.class) + public void testIncorrectUpdate() throws GroundException { + long testId = 1; + long fromId = 2; + long toId = 3; + + try { + CassandraTest.cassandraItemDao.create(new Item(testId, new HashMap<>())); + + CassandraUtils.executeCqlList(CassandraTest.dbSource, (CassandraStatements) CassandraTest.cassandraVersionDao.insert(new Version(toId))); + } catch (GroundException ge) { + fail(ge.getMessage()); + } + + List parentIds = new ArrayList<>(); + parentIds.add(fromId); + + // this should fail because fromId is not a valid version + CassandraUtils.executeCqlList(dbSource, (CassandraStatements) CassandraTest.cassandraItemDao.update(testId, toId, parentIds)); + } + + @Test + public void testMultipleParents() throws GroundException { + long testId = 1; + + CassandraTest.cassandraItemDao.create(new Item(testId, new HashMap<>())); + + long parentOne = 2; + long parentTwo = 3; + long child = 4; + + CassandraUtils.executeCqlList(CassandraTest.dbSource, (CassandraStatements) CassandraTest.cassandraVersionDao.insert(new Version(parentOne))); + CassandraUtils.executeCqlList(CassandraTest.dbSource, (CassandraStatements) CassandraTest.cassandraVersionDao.insert(new Version(parentTwo))); + CassandraUtils.executeCqlList(CassandraTest.dbSource, (CassandraStatements) CassandraTest.cassandraVersionDao.insert(new Version(child))); + List parentIds = new ArrayList<>(); + + // first, make the parents children of EMPTY) + CassandraUtils.executeCqlList(CassandraTest.dbSource, (CassandraStatements) CassandraTest.cassandraItemDao.update(testId, parentOne, parentIds)); + CassandraUtils.executeCqlList(CassandraTest.dbSource, (CassandraStatements) CassandraTest.cassandraItemDao.update(testId, parentTwo, parentIds)); + + // then, add to as a child and make sure that it becomes a child of from + parentIds.clear(); + parentIds.add(parentOne); + parentIds.add(parentTwo); + CassandraUtils.executeCqlList(dbSource, (CassandraStatements) CassandraTest.cassandraItemDao.update(testId, child, parentIds)); + + VersionHistoryDag dag = CassandraTest.versionHistoryDagDao.retrieveFromDatabase(testId); + + assertEquals(4, dag.getEdgeIds().size()); + assertEquals(child, (long) dag.getLeaves().get(0)); + + // Retrieve all the version successors and check that they have the correct data. + VersionSuccessor parentOneSuccessor = CassandraTest.versionSuccessorDao + .retrieveFromDatabase( + dag.getEdgeIds().get(0)); + + VersionSuccessor parentTwoSuccessor = CassandraTest.versionSuccessorDao + .retrieveFromDatabase( + dag.getEdgeIds().get(1)); + + VersionSuccessor childOneSuccessor = CassandraTest.versionSuccessorDao.retrieveFromDatabase( + dag.getEdgeIds().get(2)); + + VersionSuccessor childTwoSuccessor = CassandraTest.versionSuccessorDao.retrieveFromDatabase( + dag.getEdgeIds().get(3)); + + assertEquals(0, parentOneSuccessor.getFromId()); + assertEquals(parentOne, parentOneSuccessor.getToId()); + + assertEquals(0, parentTwoSuccessor.getFromId()); + assertEquals(parentTwo, parentTwoSuccessor.getToId()); + + assertEquals(parentOne, childOneSuccessor.getFromId()); + assertEquals(child, childOneSuccessor.getToId()); + + assertEquals(parentTwo, childTwoSuccessor.getFromId()); + assertEquals(child, childTwoSuccessor.getToId()); + assertEquals(child, childTwoSuccessor.getToId()); + } + + @Test + public void testTags() throws GroundException { + long testId = 1; + Map tags = new HashMap<>(); + tags.put("justkey", new Tag(testId, "justkey", null, null)); + tags.put("withintvalue", new Tag(testId, "withintvalue", 1, GroundType.INTEGER)); + tags.put("withstringvalue", new Tag(testId, "withstringvalue", "1", GroundType.STRING)); + tags.put("withboolvalue", new Tag(testId, "withboolvalue", true, GroundType.BOOLEAN)); + + long itemId = CassandraTest.cassandraItemDao.create(new Item(testId, tags)).getId(); + + Map retrievedTags = CassandraTest.tagDao.retrieveFromDatabaseByItemId(itemId); + + assertEquals(tags.size(), retrievedTags.size()); + + for (String key : tags.keySet()) { + assert (retrievedTags).containsKey(key); + assertEquals(tags.get(key), retrievedTags.get(key)); + assertEquals(itemId, retrievedTags.get(key).getId()); + } + } +} diff --git a/modules/cassandra/test/edu/berkeley/ground/cassandra/dao/version/CassandraVersionHistoryDagDaoTest.java b/modules/cassandra/test/edu/berkeley/ground/cassandra/dao/version/CassandraVersionHistoryDagDaoTest.java new file mode 100644 index 00000000..e2f25560 --- /dev/null +++ b/modules/cassandra/test/edu/berkeley/ground/cassandra/dao/version/CassandraVersionHistoryDagDaoTest.java @@ -0,0 +1,39 @@ +package edu.berkeley.ground.cassandra.dao.version; + +/** + * 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. + */ + +import static org.junit.Assert.assertEquals; + +import edu.berkeley.ground.common.exception.GroundException; +import edu.berkeley.ground.common.model.version.VersionHistoryDag; +import edu.berkeley.ground.cassandra.dao.CassandraTest; +import org.junit.Test; + +public class CassandraVersionHistoryDagDaoTest extends CassandraTest { + + public CassandraVersionHistoryDagDaoTest() throws GroundException { + super(); + } + + @Test + public void testVersionHistoryDAGCreation() throws GroundException { + long testId = 1; + CassandraTest.versionHistoryDagDao.create(testId); + + VersionHistoryDag dag = CassandraTest.versionHistoryDagDao.retrieveFromDatabase(testId); + + assertEquals(0, dag.getEdgeIds().size()); + } +} diff --git a/modules/cassandra/test/edu/berkeley/ground/cassandra/dao/version/CassandraVersionSuccessorDaoTest.java b/modules/cassandra/test/edu/berkeley/ground/cassandra/dao/version/CassandraVersionSuccessorDaoTest.java new file mode 100644 index 00000000..71f7c662 --- /dev/null +++ b/modules/cassandra/test/edu/berkeley/ground/cassandra/dao/version/CassandraVersionSuccessorDaoTest.java @@ -0,0 +1,81 @@ +/** + * 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. + */ + +package edu.berkeley.ground.cassandra.dao.version; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import edu.berkeley.ground.common.exception.GroundException; +import edu.berkeley.ground.common.model.version.Version; +import edu.berkeley.ground.common.model.version.VersionSuccessor; +import edu.berkeley.ground.cassandra.dao.CassandraTest; +import edu.berkeley.ground.cassandra.util.CassandraStatements; +import edu.berkeley.ground.cassandra.util.CassandraUtils; +import org.junit.Test; + +public class CassandraVersionSuccessorDaoTest extends CassandraTest { + + public CassandraVersionSuccessorDaoTest() throws GroundException { + super(); + } + + @Test + public void testVersionSuccessorCreation() throws GroundException { + long fromId = 1; + long toId = 2; + + CassandraUtils.executeCqlList(CassandraTest.dbSource, (CassandraStatements) CassandraTest.cassandraVersionDao.insert(new Version(fromId))); + CassandraUtils.executeCqlList(CassandraTest.dbSource, (CassandraStatements) CassandraTest.cassandraVersionDao.insert(new Version(toId))); + + VersionSuccessor successor = ((CassandraVersionSuccessorDao) CassandraTest.versionSuccessorDao).instantiateVersionSuccessor(fromId, toId); + CassandraUtils.executeCqlList(CassandraTest.dbSource, (CassandraStatements) CassandraTest.versionSuccessorDao.insert(successor)); + + VersionSuccessor retrieved = CassandraTest.versionSuccessorDao.retrieveFromDatabase(successor.getId()); + + assertEquals(fromId, retrieved.getFromId()); + assertEquals(toId, retrieved.getToId()); + } + + @Test(expected = GroundException.class) + public void testBadVersionSuccessorCreation() throws GroundException { + long fromId = 123; + long toId = 456; + + try { + // the main difference is that we're not creating a Version for the toId + CassandraTest.cassandraVersionDao.insert(new Version(fromId)); + } catch (GroundException ge) { + fail(ge.getMessage()); + } + + // This statement should cause a GroundException because toId is not in the database + VersionSuccessor successor = ((CassandraVersionSuccessorDao) CassandraTest.versionSuccessorDao).instantiateVersionSuccessor(fromId, toId); + CassandraUtils.executeCqlList(CassandraTest.dbSource, (CassandraStatements) CassandraTest.versionSuccessorDao.insert(successor)); + } + + @Test(expected = GroundException.class) + public void testBadVersionSuccessorRetrieval() throws GroundException { + try { + CassandraTest.versionSuccessorDao.retrieveFromDatabase(10); + } catch (GroundException e) { + + if (!e.getMessage().contains("Version Successor with id 10 does not exist.")) { + fail(); + } + + throw e; + } + } +} diff --git a/modules/cassandra/test/edu/berkeley/ground/cassandra/dao/version/mock/TestCassandraItemDao.java b/modules/cassandra/test/edu/berkeley/ground/cassandra/dao/version/mock/TestCassandraItemDao.java new file mode 100644 index 00000000..fe759d29 --- /dev/null +++ b/modules/cassandra/test/edu/berkeley/ground/cassandra/dao/version/mock/TestCassandraItemDao.java @@ -0,0 +1,40 @@ +package edu.berkeley.ground.cassandra.dao.version.mock; + +import edu.berkeley.ground.common.exception.GroundException; +import edu.berkeley.ground.common.exception.GroundException.ExceptionType; +import edu.berkeley.ground.common.model.version.Item; +import edu.berkeley.ground.common.util.IdGenerator; +import edu.berkeley.ground.cassandra.dao.version.CassandraItemDao; +import edu.berkeley.ground.cassandra.dao.version.CassandraTagDao; +import edu.berkeley.ground.cassandra.util.CassandraDatabase; +import edu.berkeley.ground.cassandra.util.CassandraUtils; +// import play.db.Database; + +public class TestCassandraItemDao extends CassandraItemDao { + + public TestCassandraItemDao(CassandraDatabase dbSource, IdGenerator idGenerator) { + super(dbSource, idGenerator); + } + + @Override + public Class getType() { + return Item.class; + } + + @Override + public Item retrieveFromDatabase(long id) throws GroundException { + return new Item(id, new CassandraTagDao(dbSource).retrieveFromDatabaseByItemId(id)); + } + + @Override + public Item retrieveFromDatabase(String sourceKey) throws GroundException { + throw new GroundException(ExceptionType.OTHER, "This method should never be called."); + } + + @Override + public Item create(Item item) throws GroundException { + CassandraUtils.executeCqlList(this.dbSource, this.insert(new Item(item.getId(), item.getTags()))); + + return item; + } +} diff --git a/modules/cassandra/test/edu/berkeley/ground/cassandra/dao/version/mock/TestCassandraRichVersionDao.java b/modules/cassandra/test/edu/berkeley/ground/cassandra/dao/version/mock/TestCassandraRichVersionDao.java new file mode 100644 index 00000000..ac8085a2 --- /dev/null +++ b/modules/cassandra/test/edu/berkeley/ground/cassandra/dao/version/mock/TestCassandraRichVersionDao.java @@ -0,0 +1,29 @@ +package edu.berkeley.ground.cassandra.dao.version.mock; + +import edu.berkeley.ground.common.exception.GroundException; +import edu.berkeley.ground.common.model.core.RichVersion; +import edu.berkeley.ground.common.util.IdGenerator; +import edu.berkeley.ground.cassandra.dao.core.CassandraRichVersionDao; +import edu.berkeley.ground.cassandra.util.CassandraDatabase; +import edu.berkeley.ground.cassandra.util.CassandraUtils; +import java.util.List; +// import play.db.Database; + +public class TestCassandraRichVersionDao extends CassandraRichVersionDao { + + public TestCassandraRichVersionDao(CassandraDatabase dbSource, IdGenerator idGenerator) { + super(dbSource, idGenerator); + } + + @Override + public RichVersion create(RichVersion richVersion, List parents) throws GroundException { + CassandraUtils.executeCqlList(this.dbSource, super.insert(richVersion)); + + return richVersion; + } + + @Override + public Class getType() { + return RichVersion.class; + } +} diff --git a/modules/cassandra/test/edu/berkeley/ground/cassandra/dao/version/mock/TestCassandraVersionDao.java b/modules/cassandra/test/edu/berkeley/ground/cassandra/dao/version/mock/TestCassandraVersionDao.java new file mode 100644 index 00000000..c80da51b --- /dev/null +++ b/modules/cassandra/test/edu/berkeley/ground/cassandra/dao/version/mock/TestCassandraVersionDao.java @@ -0,0 +1,23 @@ +package edu.berkeley.ground.cassandra.dao.version.mock; + +import edu.berkeley.ground.common.model.version.Version; +import edu.berkeley.ground.common.util.IdGenerator; +import edu.berkeley.ground.cassandra.dao.version.CassandraVersionDao; +import edu.berkeley.ground.cassandra.util.CassandraDatabase; +// import play.db.Database; + +public class TestCassandraVersionDao extends CassandraVersionDao { + + public TestCassandraVersionDao(CassandraDatabase dbSource, IdGenerator idGenerator) { + super(dbSource, idGenerator); + } + + public Class getType() { + return Version.class; + } + + @Override + public Version retrieveFromDatabase(long id) { + return new Version(id); + } +} diff --git a/resources/scripts/cassandra/cassandra.cql b/resources/scripts/cassandra/cassandra.cql new file mode 100644 index 00000000..4a80153f --- /dev/null +++ b/resources/scripts/cassandra/cassandra.cql @@ -0,0 +1,179 @@ + +-- 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. + + +-- VERSIONS + +CREATE TABLE IF NOT EXISTS version ( + id bigint PRIMARY KEY +); + +CREATE TABLE IF NOT EXISTS version_successor ( + id bigint PRIMARY KEY, + from_version_id bigint, + to_version_id bigint +); + +CREATE TABLE IF NOT EXISTS item ( + id bigint PRIMARY KEY +); + +CREATE TABLE IF NOT EXISTS item_tag ( + item_id bigint, + key varchar, + value varchar, + type varchar, + PRIMARY KEY (item_id, key) +); + +CREATE TABLE IF NOT EXISTS version_history_dag ( + item_id bigint, + version_successor_id bigint, + PRIMARY KEY(item_id, version_successor_id) +); + +-- MODELS + +CREATE TABLE IF NOT EXISTS structure ( + item_id bigint, + source_key varchar, + name varchar, + PRIMARY KEY (item_id, source_key) +); + +CREATE TABLE IF NOT EXISTS structure_version ( + id bigint PRIMARY KEY, + structure_id bigint +); + +CREATE TABLE IF NOT EXISTS structure_version_attribute ( + structure_version_id bigint, + key varchar, + type varchar, + PRIMARY KEY (structure_version_id, key) +); + +CREATE TABLE IF NOT EXISTS rich_version ( + id bigint PRIMARY KEY, + structure_version_id bigint, + reference varchar +); + +CREATE TABLE IF NOT EXISTS rich_version_external_parameter ( + rich_version_id bigint, + key varchar, + value varchar, + PRIMARY KEY (rich_version_id, key) +); + +CREATE TABLE IF NOT EXISTS rich_version_tag ( + rich_version_id bigint, + key varchar, + value varchar, + type varchar, + PRIMARY KEY (rich_version_id, key) +); + +CREATE TABLE IF NOT EXISTS edge ( + item_id bigint, + source_key varchar, + from_node_id bigint, + to_node_id bigint, + name varchar, + PRIMARY KEY (item_id, source_key) +); + +CREATE TABLE IF NOT EXISTS node ( + item_id bigint, + source_key varchar, + name varchar, + PRIMARY KEY (item_id, source_key) +); + +CREATE TABLE IF NOT EXISTS graph ( + item_id bigint, + source_key varchar, + name varchar, + PRIMARY KEY (item_id, source_key) +); + +CREATE TABLE IF NOT EXISTS node_version ( + id bigint PRIMARY KEY, + node_id bigint +); + +CREATE TABLE IF NOT EXISTS edge_version ( + id bigint PRIMARY KEY, + edge_id bigint, + from_node_version_start_id bigint, + from_node_version_end_id bigint, + to_node_version_start_id bigint, + to_node_version_end_id bigint +); + +CREATE TABLE IF NOT EXISTS graph_version ( + id bigint PRIMARY KEY, + graph_id bigint +); + +CREATE TABLE IF NOT EXISTS graph_version_edge ( + graph_version_id bigint, + edge_version_id bigint, + PRIMARY KEY (graph_version_id, edge_version_id) +); + +-- USAGE + +CREATE TABLE IF NOT EXISTS principal ( + node_id bigint, + source_key varchar, + name varchar, + PRIMARY KEY (node_id, source_key) +); + +CREATE TABLE IF NOT EXISTS lineage_edge ( + item_id bigint, + source_key varchar, + name varchar, + PRIMARY KEY (item_id, source_key) +); + +CREATE TABLE IF NOT EXISTS lineage_edge_version ( + id bigint PRIMARY KEY, + lineage_edge_id bigint, + from_rich_version_id bigint, + to_rich_version_id bigint, + principal_id bigint, +); + +CREATE TABLE IF NOT EXISTS lineage_graph ( + item_id bigint, + source_key varchar, + name varchar, + PRIMARY KEY (item_id, source_key) +); + +CREATE TABLE IF NOT EXISTS lineage_graph_version ( + id bigint PRIMARY KEY, + lineage_graph_id bigint +); + +CREATE TABLE IF NOT EXISTS lineage_graph_version_edge ( + lineage_graph_version_id bigint, + lineage_edge_version_id bigint, + PRIMARY KEY (lineage_graph_version_id, lineage_edge_version_id) +); + +-- CREATE EMPTY VERSION + +INSERT INTO version(id) values (0); diff --git a/resources/scripts/cassandra/cassandra_setup.py b/resources/scripts/cassandra/cassandra_setup.py new file mode 100644 index 00000000..1414abe1 --- /dev/null +++ b/resources/scripts/cassandra/cassandra_setup.py @@ -0,0 +1,29 @@ +''' +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. +''' + +import sys, os + +assert (len(sys.argv) >= 2) +dbname = sys.argv[1] + +drop = len(sys.argv) == 3 + +if drop: + command_string = "cqlsh -k " + str(dbname) + " -f drop_cassandra.cql" + os.system(command_string) + +command_string = "cqlsh -k " + str(dbname) + " -f cassandra.cql" +os.system(command_string) + +print "Successfully reset Cassandra." diff --git a/resources/scripts/cassandra/drop_cassandra.cql b/resources/scripts/cassandra/drop_cassandra.cql new file mode 100644 index 00000000..4d273e0d --- /dev/null +++ b/resources/scripts/cassandra/drop_cassandra.cql @@ -0,0 +1,38 @@ +/** + * 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. + */ + +DROP TABLE lineage_graph_version_edge; +DROP TABLE lineage_graph_version; +DROP TABLE lineage_graph; +DROP TABLE lineage_edge_version; +DROP TABLE lineage_edge; +DROP TABLE principal; +DROP TABLE graph_version_edge; +DROP TABLE graph_version; +DROP TABLE edge_version; +DROP TABLE node_version; +DROP TABLE graph; +DROP TABLE node; +DROP TABLE edge; +DROP TABLE rich_version_tag; +DROP TABLE rich_version_external_parameter; +DROP TABLE rich_version; +DROP TABLE structure_version_attribute; +DROP TABLE structure_version; +DROP TABLE structure; +DROP TABLE version_history_dag; +DROP TABLE item_tag; +DROP TABLE item; +DROP TABLE version_successor; +DROP TABLE version; diff --git a/resources/scripts/cassandra/truncate.cql b/resources/scripts/cassandra/truncate.cql new file mode 100644 index 00000000..4c175ce1 --- /dev/null +++ b/resources/scripts/cassandra/truncate.cql @@ -0,0 +1,40 @@ + +-- 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. + + +TRUNCATE lineage_graph_version_edge; +TRUNCATE lineage_graph_version; +TRUNCATE lineage_graph; +TRUNCATE lineage_edge_version; +TRUNCATE lineage_edge; +TRUNCATE principal; +TRUNCATE graph_version_edge; +TRUNCATE graph_version; +TRUNCATE edge_version; +TRUNCATE node_version; +TRUNCATE graph; +TRUNCATE node; +TRUNCATE edge; +TRUNCATE rich_version_tag; +TRUNCATE rich_version_external_parameter; +TRUNCATE rich_version; +TRUNCATE structure_version_attribute; +TRUNCATE structure_version; +TRUNCATE structure; +TRUNCATE version_history_dag; +TRUNCATE item_tag; +TRUNCATE item; +TRUNCATE version_successor; +TRUNCATE version; + +INSERT into version(id) values (0); diff --git a/resources/scripts/format-code.sh b/resources/scripts/format-code.sh new file mode 100755 index 00000000..e9a502a9 --- /dev/null +++ b/resources/scripts/format-code.sh @@ -0,0 +1,11 @@ +#!/bin/bash +# Auto format changed java files using google-java-format. +# TODO: make this a pre commit hook, putting it in $repo/.git/hooks + +cd `git rev-parse --show-toplevel` + +java -jar resources/scripts/google-java-format-1.3-all-deps.jar --replace `find . -name "*.java" -type f -printf " %p"` + + + + diff --git a/resources/scripts/postgres/drop_postgres.sql b/resources/scripts/postgres/drop_postgres.sql new file mode 100644 index 00000000..932e8c72 --- /dev/null +++ b/resources/scripts/postgres/drop_postgres.sql @@ -0,0 +1,37 @@ +-- 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. + +DROP TABLE lineage_graph_version_edge; +DROP TABLE lineage_graph_version; +DROP TABLE lineage_graph; +DROP TABLE lineage_edge_version; +DROP TABLE lineage_edge; +DROP TABLE principal; +DROP TABLE graph_version_edge; +DROP TABLE graph_version; +DROP TABLE edge_version; +DROP TABLE node_version; +DROP TABLE graph; +DROP TABLE edge; +DROP TABLE node; +DROP TABLE rich_version_tag; +DROP TABLE rich_version_external_parameter; +DROP TABLE rich_version; +DROP TABLE structure_version_attribute; +DROP TABLE structure_version; +DROP TABLE structure; +DROP TABLE version_history_dag; +DROP TABLE item_tag; +DROP TABLE item; +DROP TABLE version_successor; +DROP TABLE version; +DROP TYPE data_type; diff --git a/resources/scripts/postgres/postgres.sql b/resources/scripts/postgres/postgres.sql new file mode 100644 index 00000000..85447a9f --- /dev/null +++ b/resources/scripts/postgres/postgres.sql @@ -0,0 +1,177 @@ +-- noinspection SqlDialectInspectionForFile + +-- noinspection SqlNoDataSourceInspectionForFile + +-- 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. + +-- VERSIONS +CREATE TYPE data_type as enum ('integer', 'string', 'boolean'); + +CREATE TABLE IF NOT EXISTS version ( + id bigint NOT NULL PRIMARY KEY +); + +CREATE TABLE IF NOT EXISTS version_successor ( + id bigint NOT NULL PRIMARY KEY, + from_version_id bigint NOT NULL REFERENCES version(id), + to_version_id bigint NOT NULL REFERENCES version(id), + CONSTRAINT version_successor_unique_endpoints UNIQUE (from_version_id, to_version_id) +); + +CREATE TABLE IF NOT EXISTS item ( + id bigint NOT NULL PRIMARY KEY +); + +CREATE TABLE IF NOT EXISTS item_tag ( + item_id bigint NOT NULL REFERENCES item(id), + key varchar NOT NULL, + value varchar, + type data_type, + CONSTRAINT item_tag_pkey PRIMARY KEY (item_id, key) +); + +CREATE TABLE IF NOT EXISTS version_history_dag ( + item_id bigint NOT NULL REFERENCES item(id), + version_successor_id bigint NOT NULL REFERENCES version_successor(id), + CONSTRAINT version_history_dag_pkey PRIMARY KEY (item_id, version_successor_id) +); + +-- MODELS + +CREATE TABLE IF NOT EXISTS structure ( + item_id bigint NOT NULL PRIMARY KEY REFERENCES item(id), + source_key varchar UNIQUE, + name varchar +); + +CREATE TABLE IF NOT EXISTS structure_version ( + id bigint NOT NULL PRIMARY KEY REFERENCES version(id), + structure_id bigint NOT NULL REFERENCES structure(item_id) +); + +CREATE TABLE IF NOT EXISTS structure_version_attribute ( + structure_version_id bigint NOT NULL REFERENCES structure_version(id), + key varchar NOT NULL, + type varchar NOT NULL, + CONSTRAINT structure_version_attribute_pkey PRIMARY KEY(structure_version_id, key) +); + +CREATE TABLE IF NOT EXISTS rich_version ( + id bigint NOT NULL PRIMARY KEY REFERENCES version(id), + structure_version_id bigint REFERENCES structure_version(id), + reference varchar +); + +CREATE TABLE IF NOT EXISTS rich_version_external_parameter ( + rich_version_id bigint NOT NULL REFERENCES rich_version(id), + key varchar NOT NULL, + value varchar NOT NULL, + CONSTRAINT rich_version_external_parameter_pkey PRIMARY KEY (rich_version_id, key) +); + +CREATE TABLE IF NOT EXISTS rich_version_tag ( + rich_version_id bigint REFERENCES rich_version(id), + key varchar NOT NULL, + value varchar, + type data_type, + CONSTRAINT rich_version_tag_pkey PRIMARY KEY (rich_version_id, key) +); + +CREATE TABLE IF NOT EXISTS node ( + item_id bigint NOT NULL PRIMARY KEY REFERENCES item(id), + source_key varchar UNIQUE, + name varchar +); + +CREATE TABLE IF NOT EXISTS edge ( + item_id bigint NOT NULL PRIMARY KEY REFERENCES item(id), + source_key varchar UNIQUE, + from_node_id bigint NOT NULL REFERENCES node(item_id), + to_node_id bigint NOT NULL REFERENCES node(item_id), + name varchar +); + + +CREATE TABLE IF NOT EXISTS graph ( + item_id bigint NOT NULL PRIMARY KEY REFERENCES item(id), + source_key varchar UNIQUE, + name varchar +); + +CREATE TABLE IF NOT EXISTS node_version ( + id bigint NOT NULL PRIMARY KEY REFERENCES rich_version(id), + node_id bigint NOT NULL REFERENCES node(item_id) +); + +CREATE TABLE IF NOT EXISTS edge_version ( + id bigint NOT NULL PRIMARY KEY REFERENCES rich_version(id), + edge_id bigint NOT NULL REFERENCES edge(item_id), + from_node_version_start_id bigint NOT NULL REFERENCES node_version(id), + from_node_version_end_id bigint REFERENCES node_version(id), + to_node_version_start_id bigint NOT NULL REFERENCES node_version(id), + to_node_version_end_id bigint REFERENCES node_version(id) +); + +CREATE TABLE IF NOT EXISTS graph_version ( + id bigint NOT NULL PRIMARY KEY REFERENCES rich_version(id), + graph_id bigint NOT NULL REFERENCES graph(item_id) +); + +CREATE TABLE IF NOT EXISTS graph_version_edge ( + graph_version_id bigint NOT NULL REFERENCES graph_version(id), + edge_version_id bigint NOT NULL REFERENCES edge_version(id), + CONSTRAINT graph_version_edge_pkey PRIMARY KEY (graph_version_id, edge_version_id) +); + +-- USAGE + +CREATE TABLE IF NOT EXISTS principal ( + node_id bigint NOT NULL PRIMARY KEY REFERENCES node(item_id), + source_key varchar UNIQUE, + name varchar +); + +CREATE TABLE IF NOT EXISTS lineage_edge ( + item_id bigint NOT NULL PRIMARY KEY REFERENCES item(id), + source_key varchar UNIQUE, + name varchar +); + +CREATE TABLE IF NOT EXISTS lineage_edge_version ( + id bigint NOT NULL PRIMARY KEY REFERENCES rich_version(id), + lineage_edge_id bigint NOT NULL REFERENCES lineage_edge(item_id), + from_rich_version_id bigint NOT NULL REFERENCES rich_version(id), + to_rich_version_id bigint NOT NULL REFERENCES rich_version(id), + principal_id bigint REFERENCES node_version(id) +); + +CREATE TABLE IF NOT EXISTS lineage_graph ( + item_id bigint NOT NULL PRIMARY KEY REFERENCES item(id), + source_key varchar UNIQUE, + name varchar +); + +CREATE TABLE IF NOT EXISTS lineage_graph_version ( + id bigint NOT NULL PRIMARY KEY REFERENCES rich_version(id), + lineage_graph_id bigint NOT NULL REFERENCES lineage_graph(item_id) +); + +CREATE TABLE IF NOT EXISTS lineage_graph_version_edge ( + lineage_graph_version_id bigint NOT NULL REFERENCES lineage_graph_version(id), + lineage_edge_version_id bigint NOT NULL REFERENCES lineage_edge_version(id), + CONSTRAINT lineage_graph_version_edge_pkey PRIMARY KEY (lineage_graph_version_id, lineage_edge_version_id) +); + +-- CREATE EMPTY VERSION + +INSERT INTO version(id) values (0); diff --git a/resources/scripts/postgres/postgres_setup.py b/resources/scripts/postgres/postgres_setup.py new file mode 100644 index 00000000..1bd03df9 --- /dev/null +++ b/resources/scripts/postgres/postgres_setup.py @@ -0,0 +1,28 @@ +''' +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. +''' + +import os, sys + +assert (len(sys.argv) >= 3) +user = sys.argv[1] +dbname = sys.argv[2] + +drop = len(sys.argv) == 4 + +if drop: + delete_string = "psql -U " + str(user) + " -d " + str(dbname) + " -f drop_postgres.sql" + os.system(delete_string) + +create_string = "psql -U " + str(user) + " -d " + str(dbname) + " -f postgres.sql" +os.system(create_string)