-
Notifications
You must be signed in to change notification settings - Fork 1
Macro Annotations
It is often useful to use enums for container fields and they require custom serialization.
That often is done by converting the enum to/from strings and this basic functionality can be achieved by using the
DatabaseEnum macro annotation:
import core3.meta.enums._
//... container definition ...
object Organization {
@DatabaseEnum
sealed trait OrganizationType
object OrganizationType {
case object Client extends OrganizationType
case object InternalTeam extends OrganizationType
}
}On its own, this annotation only generates a fromString method; however, when generating container definitions via
the appropriate container annotations, extra code is generated for the enum to ensure proper (de)serialization:
//Slick: WithSlickContainerDefinition
implicit val OrganizationTypeColumnType =
MappedColumnType.base[OrganizationType, String](
{ enum => enum.toString },
{ string => OrganizationType.fromString(string) }
)//Json: WithJsonContainerDefinition
implicit val OrganizationTypeReads: Reads[OrganizationType] = Reads {
json => json.validate[String].map(OrganizationType.fromString)
}
implicit val OrganizationTypeWrites: Writes[OrganizationType] = Writes {
enum => JsString(enum.toString)
} @DatabaseEnum
sealed trait SomeType1
object SomeType1 {
case object EnumOne extends SomeType1
case object EnumTwo extends SomeType1
}
sealed trait SomeType2
@DatabaseEnum
object SomeType2 {
case object EnumOne extends SomeType2
case object EnumTwo extends SomeType2
}
@DatabaseEnum
sealed class SomeType3
object SomeType3 {
case object EnumOne extends SomeType3
case object EnumTwo extends SomeType3
}Container conversion boilerplate can be easily generated:
-
override def getDatabaseName: String = "<some name>"- the name is generated based on the container class name and it attempts to split any words (camel case is assumed); for example:SomeClassbecomessome_class -
override def matchCustomQuery(queryName: String, queryParams: Map[String, String], container: core3.database.containers.Container): Boolean = ???- throws aNotImplementedError
-
matchCustomQuery- normally, this method will be overridden to provide actual query functionality (see the examples below)
- enum reads/writes - See enumerations above
-
implicit val SomeContainerWrites: play.api.libs.json.Writes[SomeContainer]- for converting a container to a Json object -
implicit val SomeContainerReads: play.api.libs.json.Reads[SomeContainer]- for converting a Json object to a container -
override def toJsonData(container: Container)- helper method -
override def fromJsonData(data: .JsValue)- helper method
Nothing
- Slick Imports - all standard imports (Slick profile, shapeless, slickless)
-
Slick Table Definition - the standard table definition for Slick; column names are based on the container field names (camel case is assumed); for example:
someIntFieldwill becomeSOME_INT_FIELD - Slick Queries - default query definition (get object by ID)
-
Slick Actions - all standard actions plus a
customQueryActionthat throws aNotImplementedError
-
customQueryAction- normally, this method will be overridden to provide actual query functionality (see the examples below) - additional queries - any additional queries need to be define explicitly (see the examples below)
Multiple annotations can be applied to the same container.
import core3.database
import core3.database.{ContainerType, ObjectID, RevisionID, RevisionSequenceNumber}
import core3.database.containers._
import core3.utils._
import core3.meta.containers._
import core3.meta.enums._
@WithBasicContainerDefinition
@WithJsonContainerDefinition
@WithSlickContainerDefinition
case class Organization(
var name: String,
var description: String,
organizationType: Organization.OrganizationType,
created: Timestamp,
var updated: Timestamp,
var updatedBy: String,
id: ObjectID,
var revision: RevisionID,
var revisionNumber: RevisionSequenceNumber
) extends MutableContainer {
override val objectType: ContainerType = "Organization"
}
object Organization {
@DatabaseEnum
sealed trait OrganizationType
object OrganizationType {
case object Internal extends OrganizationType
case object External extends OrganizationType
}
trait BasicDefinition extends BasicContainerDefinition {
//basic custom query
override def matchCustomQuery(...): Boolean = {
queryName match {
case "getByName" =>
queryParams("name") == container.asInstanceOf[Organization].name
case _ => throw new IllegalArgumentException(
s"Organization::matchCustomQuery > Query [$queryName] is not supported."
)
}
}
}
trait SlickDefinition extends SlickContainerDefinition {
//custom slick query
private val compiledGetByName =
Compiled((name: Rep[String]) => query.filter(_.name === name))
//custom slick query action
override def customQueryAction(...): ... = {
queryName match {
case "getByName" => compiledGetByName(queryParams("name")).result
case _ => throw new IllegalArgumentException(
s"Organization::matchCustomQuery > Query [$queryName] is not supported."
)
}
}
}
}Before being supplied to a DAL or a Workflow Engine the definition needs to be initialized:
// initializes the organization definitions (see above) with MariaDB support
val organizationDefinitions =
new core.Group.BasicDefinition
with core.Group.JsonDefinition
with core.Group.SlickDefinition {
override protected def withProfile: JdbcProfile = slick.jdbc.MySQLProfile
}Normally, all queries will be defined in the container traits/definitions, however, any definition method can be overridden when specifying the initialization:
// initializes the organization definitions (see above) with MariaDB support
val organizationDefinitions =
new core.Group.BasicDefinition
with core.Group.JsonDefinition
with core.Group.SlickDefinition {
override protected def withProfile: JdbcProfile = slick.jdbc.MySQLProfile
//overrides the method specified in `BasicDefinition`
override def matchCustomQuery(...): Boolean = ???
}Home | Getting Started | Structure | Containers | Workflows | Controllers