Skip to content

Getting Started

Angel Sanadinov edited this page Apr 12, 2017 · 6 revisions

core3 is based on Play 2.5.x and Akka 2.4.x, so some familiarity with them is essential (especially Play). Here are some resources that can help you get started:

Before writing anything it might be a good idea to get to decide on your application's structure. Also, there are two example projects that can be used as a more hands-on starting point:

Add dependencies

libraryDependencies += "com.interelgroup" %% "core3" % "1.0.0"

See Additional Dependencies for other libraries you may need.

Create containers

You don't have to use containers if your application does not need to store anything or if persistence is handled in a different way.

The basic approach is to define all the fields, decide on the supported DALs and implement the appropriate container 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

case class TestContainer(
  test: String,
  created: Timestamp,
  var updated: Timestamp,
  var updatedBy: String,
  id: ObjectID,
  var revision: RevisionID,
  var revisionNumber: RevisionSequenceNumber)
  extends MutableContainer {
  override val objectType: ContainerType = "TestContainer"
}

object TestContainer
  //with JSON support (CouchDB, Redis, Elasticsearch)
  extends JSONContainerCompanion
    //with Slick support (MariaDB)
    with SlickContainerCompanionImpl[(/*... def ...*/)] {
  private type ContainerTupleDef = (/*... def ...*/)

  // ... implementation ...
}

Warning: Each container must have a unique objectType for your application! This means that defining containers called TransactionLog, LocalUser or Group can cause issues. You can go around that by not using transaction logs, not using local auth or not using groups.

Check out the containers page for more details.

Create workflows

You don't have to use workflows if they don't make sense for your application or if they are too restrictive; the data layer can be used directly through the DatabaseAbstractionLayer interface (see Provide DB).

The easiest way to go about it is to define a list of actions that your users will be performing (for example: create project, update article, lock user, etc) and create a workflow per action. For each workflow you will need to define and implement the following:

  • Workflow parameters (if any) - the parameters supplied by the user (or some other external entity)
  • Input data (if any) - the data retrieved from the DB
  • Action - the actual work to be done

Check out the workflows page for more details.

Provide DB

In your application's Module.scala (example with Redis):

import akka.actor.ActorSystem
import akka.util.Timeout
import com.google.inject.{AbstractModule, Provides, Singleton}
import core3.config.StaticConfig
import core3.database._
import core3.database.containers.{JSONContainerCompanion, JSONConverter, core}
import core3.database.dals.json.Redis
import core3.database.dals.{Core, DatabaseAbstractionLayer}
import net.codingwell.scalaguice.ScalaModule
import play.api.{Environment, Mode}

import scala.concurrent.ExecutionContext
import scala.concurrent.duration._

class Module extends AbstractModule with ScalaModule {

  ...

  @Provides
  @Singleton
  def provideDB(
    environment: Environment
  )(
    implicit system: ActorSystem, ec: ExecutionContext
  ): DatabaseAbstractionLayer = {
    //creates the JSON container companion descriptor
    val storeCompanions = Map[ContainerType, JSONContainerCompanion](
      "Group" -> core.Group,
      "TransactionLog" -> core.TransactionLog,
      "LocalUser" -> core.LocalUser
    )

    //ensures that the JSON converted is
    //properly initialized on Play reloads (in dev)
    environment.mode match {
      case Mode.Dev =>
        if (!JSONConverter.isInitialized)
          JSONConverter.initialize(storeCompanions)

      case _ =>
        JSONConverter.initialize(storeCompanions)
    }

    val storeConfig = StaticConfig.get.getConfig("database.redis")
    implicit val timeout = Timeout(
      StaticConfig.get.getInt("database.requestTimeout").seconds
    )

    //initializes the Redis DAL
    val storeActor = system.actorOf(
      Redis.props(
        storeConfig.getString("hostname"),
        storeConfig.getInt("port"),
        storeConfig.getString("secret"),
        storeConfig.getInt("connectionTimeout"),
        storeCompanions,
        storeConfig.getInt("databaseID"),
        storeConfig.getInt("scanCount")
      )
    )

    //builds the Core DAL
    new DatabaseAbstractionLayer(
      system.actorOf(
        Core.props(
          Map(
            "Group" -> Vector(storeActor),
            "TransactionLog" -> Vector(storeActor),
            "LocalUser" -> Vector(storeActor)
          )
        )
      )
    )
  }

  ...

}

Check out the databases page for more details and config.

Provide Engine

In your application's Module.scala (example with all core workflows):

import akka.actor.ActorSystem
import akka.util.Timeout
import com.google.inject.{AbstractModule, Provides, Singleton}
import core3.config.StaticConfig
import core3.database.dals.DatabaseAbstractionLayer
import core3.workflows._
import net.codingwell.scalaguice.ScalaModule

import scala.concurrent.ExecutionContext
import scala.concurrent.duration._

class Module extends AbstractModule with ScalaModule {

  ...

  @Provides
  @Singleton
  def provideEngine(system: ActorSystem, db: DatabaseAbstractionLayer)
    (implicit ec: ExecutionContext): WorkflowEngine = {
    val workflows = Seq(
      definitions.SystemCreateGroup,
      definitions.SystemCreateLocalUser,
      definitions.SystemDeleteGroup,
      definitions.SystemDeleteLocalUser,
      definitions.SystemQueryGroups,
      definitions.SystemQueryLocalUsers,
      definitions.SystemQueryTransactionLogs,
      definitions.SystemUpdateGroup,
      definitions.SystemUpdateLocalUserMetadata,
      definitions.SystemUpdateLocalUserPassword,
      definitions.SystemUpdateLocalUserPermissions
    )

    val engineConfig = StaticConfig.get.getConfig("engine")
    implicit val timeout = Timeout(
      engineConfig.getInt("requestTimeout").seconds
    )

    new WorkflowEngine(
      system.actorOf(
        WorkflowEngineComponent.props(
          workflows,
          db,
          StoreTransactionLogs.fromString(
            engineConfig.getString("storeLogs")
          )
        )
      )
    )
  }

  ...
}

Check out the Workflow Engine page for more details and config.

Create controllers

You don't have to use controllers if communication and security is handled in a different way.

This will be the code that communicates with the outside world: renders pages, parses parameters, makes service requests, etc. You can start with the basics then move on to how to use and configure them.

Create UI

Play's template engine is one way to go but this part is entirely up to you.

Run It

PlayConsole

Clone this wiki locally