Skip to content

Containers

Angel Sanadinov edited this page Apr 11, 2017 · 11 revisions

Overview

The data management of a core3 application is based around two main concepts: database abstraction layers and containers. In addition to that, there are views which can be used to further process and group data coming from the databases.

Note: Implicit parameters have been removed for brevity.

Example containers

All core containers can be found here.

Containers

Containers represent data going in and out of the data layer and, usually, each one maps to a distinct entity in one or more DBs (for example, table row in MariaDB or a document in CouchDB). Each container has at least an id and a containerType, used for identifying an object across all DBs.

LocalUser and Container Traits

//Base container trait
trait Container {
  val id: ObjectID
  val objectType: ContainerType
}

Immutable

Useful for storing data that does not change, such as logs or data snapshots.

trait ImmutableContainer extends Container

Mutable

Used for everything else and has the following additional pieces of information:

  • created - Data and time of the object's creation
  • updated - Date and time of the object's last update
  • updatedBy - ID of the user that performed the last update
  • revision - Unique ID used for ensuring data integrity
  • revisionNumber - Sequential ID used for ensuring data integrity and tracking the number of changes to an object
trait MutableContainer extends Container {
  val created: Timestamp
  var updated: Timestamp
  var updatedBy: String
  var revision: RevisionID
  var revisionNumber: RevisionSequenceNumber
}

Companion Objects

A container class' companion object is used for converting the container to and from the format a DB expects.

BasicContainerCompanion

Defines the basic methods required from all companion objects:

trait BasicContainerCompanion {
  def getDatabaseName(dataType: DataType): String
  def matchCustomQuery(queryName: String, queryParams: Map[String, String], container: Container): Boolean
}
JSONContainerCompanion

Defines the methods required to convert a container object to/from JSON:

trait JSONContainerCompanion extends BasicContainerCompanion {
  def toJsonData(container: Container, format: JsonDataFormat): JsValue
  def fromJsonData(data: JsValue): Container
}
SlickContainerCompanion and SlickContainerCompanionImpl

Defines the methods required to convert containers to/from SQL DBs:

trait SlickContainerCompanion extends BasicContainerCompanion {
  def runCreateSchema(db: DatabaseDef): Future[Boolean]
  def runDropSchema(db: DatabaseDef): Future[Boolean]
  def runGenericQuery(query: SQLActionBuilder, db: DatabaseDef): Future[Vector[Container]]
  def runGet(objectID: ObjectID, db: DatabaseDef): Future[Container]
  def runCreate(container: Container, db: DatabaseDef): Future[Boolean]
  def runUpdate(container: MutableContainer, db: DatabaseDef): Future[Boolean]
  def runDelete(objectID: ObjectID, db: DatabaseDef): Future[Boolean]
  def runCustomQuery(queryName: String, queryParams: Map[String, String], db: DatabaseDef): Future[Vector[Container]]
}

trait SlickContainerCompanionImpl[ContainerTupleDef] extends SlickContainerCompanion {
  protected def convertToTuple(container: Container): ContainerTupleDef
  protected def convertFromTuple(tuple: ContainerTupleDef): Container
}

Note: SlickContainerCompanionImpl is the trait to be extended by container companions. SlickContainerCompanion is used by the DALs.

SearchContainerCompanion

Defines the methods required to convert a container to a searchable entity:

trait SearchContainerCompanion extends JSONContainerCompanion {
  def getSearchFields: Map[String, String]
}

Note: Normally, a container cannot be reconstructed from search data.

Core containers

  • Group - Allows grouping of other containers
  • LocalUser - Basic user container; used for local authentication
  • TransactionLog - Used for storing data about the operations the workflow engine is performing

Defining your own

  1. Decide on whether the container is going to be mutable or not

  2. Extend the appropriate trait (MutableContainer or ImmutableContainer) and define all container fields

import core3.database
import core3.database.containers._
import core3.database._
import core3.utils.Time._
import core3.utils._
import play.api.libs.json._
import slick.jdbc.MySQLProfile.api._
import slick.jdbc.MySQLProfile.backend.DatabaseDef
import slick.jdbc.SQLActionBuilder

import scala.concurrent.{ExecutionContext, Future}

case class Group(
  shortName: String,
  var name: String,
  var items: Vector[ObjectID],
  itemsType: ContainerType,
  created: Timestamp,
  var updated: Timestamp,
  var updatedBy: String,
  id: ObjectID,
  var revision: RevisionID,
  var revisionNumber: RevisionSequenceNumber
)
  extends MutableContainer {
  override val objectType: ContainerType = "Group"

  //... additional methods and/or constructors ...

}
  1. Decide on the container's target DBs and extend the appropriate companion traits:
import core3.database
import core3.database.containers._
import core3.database._
import core3.utils.Time._
import core3.utils._
import play.api.libs.json._
import slick.jdbc.MySQLProfile.api._
import slick.jdbc.MySQLProfile.backend.DatabaseDef
import slick.jdbc.SQLActionBuilder

import scala.concurrent.{ExecutionContext, Future}

object Group
  extends JSONContainerCompanion        //adds support for JSON-based DBs
    with SlickContainerCompanionImpl[(  //adds support for SQL DBs
    //Slick table row / tuple definition
    String, String, String, String, String, java.sql.Timestamp, java.sql.Timestamp, String, String, Int
    )] {
  private type ContainerTupleDef = (
    //Slick table row / tuple definition
    String, String, String, String, String, java.sql.Timestamp, java.sql.Timestamp, String, String, Int
    )

  //... trait method implementation ...

}
  1. Done

Clone this wiki locally