aboutsummaryrefslogtreecommitdiff
path: root/src/main/scala/xyz/driver/common/domain
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/scala/xyz/driver/common/domain')
-rw-r--r--src/main/scala/xyz/driver/common/domain/CaseId.scala10
-rw-r--r--src/main/scala/xyz/driver/common/domain/Category.scala21
-rw-r--r--src/main/scala/xyz/driver/common/domain/Email.scala3
-rw-r--r--src/main/scala/xyz/driver/common/domain/FuzzyValue.scala17
-rw-r--r--src/main/scala/xyz/driver/common/domain/Id.scala51
-rw-r--r--src/main/scala/xyz/driver/common/domain/Label.scala15
-rw-r--r--src/main/scala/xyz/driver/common/domain/PasswordHash.scala42
-rw-r--r--src/main/scala/xyz/driver/common/domain/RecordRequestId.scala16
-rw-r--r--src/main/scala/xyz/driver/common/domain/TextJson.scala14
-rw-r--r--src/main/scala/xyz/driver/common/domain/User.scala74
10 files changed, 263 insertions, 0 deletions
diff --git a/src/main/scala/xyz/driver/common/domain/CaseId.scala b/src/main/scala/xyz/driver/common/domain/CaseId.scala
new file mode 100644
index 0000000..bb11f90
--- /dev/null
+++ b/src/main/scala/xyz/driver/common/domain/CaseId.scala
@@ -0,0 +1,10 @@
+package xyz.driver.common.domain
+
+import java.util.UUID
+
+case class CaseId(id: String)
+
+object CaseId {
+
+ def apply() = new CaseId(UUID.randomUUID().toString)
+}
diff --git a/src/main/scala/xyz/driver/common/domain/Category.scala b/src/main/scala/xyz/driver/common/domain/Category.scala
new file mode 100644
index 0000000..e130367
--- /dev/null
+++ b/src/main/scala/xyz/driver/common/domain/Category.scala
@@ -0,0 +1,21 @@
+package xyz.driver.common.domain
+
+import xyz.driver.common.logging._
+
+case class Category(id: LongId[Category], name: String)
+
+object Category {
+ implicit def toPhiString(x: Category): PhiString = {
+ import x._
+ phi"Category(id=$id, name=${Unsafe(name)})"
+ }
+}
+
+case class CategoryWithLabels(category: Category, labels: List[Label])
+
+object CategoryWithLabels {
+ implicit def toPhiString(x: CategoryWithLabels): PhiString = {
+ import x._
+ phi"CategoryWithLabels(category=$category, labels=$labels)"
+ }
+}
diff --git a/src/main/scala/xyz/driver/common/domain/Email.scala b/src/main/scala/xyz/driver/common/domain/Email.scala
new file mode 100644
index 0000000..c3bcf3f
--- /dev/null
+++ b/src/main/scala/xyz/driver/common/domain/Email.scala
@@ -0,0 +1,3 @@
+package xyz.driver.common.domain
+
+case class Email(value: String)
diff --git a/src/main/scala/xyz/driver/common/domain/FuzzyValue.scala b/src/main/scala/xyz/driver/common/domain/FuzzyValue.scala
new file mode 100644
index 0000000..584f8f7
--- /dev/null
+++ b/src/main/scala/xyz/driver/common/domain/FuzzyValue.scala
@@ -0,0 +1,17 @@
+package xyz.driver.common.domain
+
+import xyz.driver.common.logging._
+import xyz.driver.common.utils.Utils
+
+sealed trait FuzzyValue
+object FuzzyValue {
+ case object Yes extends FuzzyValue
+ case object No extends FuzzyValue
+ case object Maybe extends FuzzyValue
+
+ val All: Set[FuzzyValue] = Set(Yes, No, Maybe)
+
+ def fromBoolean(x: Boolean): FuzzyValue = if (x) Yes else No
+
+ implicit def toPhiString(x: FuzzyValue): PhiString = Unsafe(Utils.getClassSimpleName(x.getClass))
+}
diff --git a/src/main/scala/xyz/driver/common/domain/Id.scala b/src/main/scala/xyz/driver/common/domain/Id.scala
new file mode 100644
index 0000000..9f9604e
--- /dev/null
+++ b/src/main/scala/xyz/driver/common/domain/Id.scala
@@ -0,0 +1,51 @@
+package xyz.driver.common.domain
+
+import java.util.UUID
+
+import xyz.driver.common.logging._
+
+sealed trait Id[+T]
+
+case class CompoundId[Id1 <: Id[_], Id2 <: Id[_]](part1: Id1, part2: Id2) extends Id[(Id1, Id2)]
+
+case class LongId[+T](id: Long) extends Id[T] {
+ override def toString: String = id.toString
+
+ def is(longId: Long): Boolean = {
+ id == longId
+ }
+}
+
+object LongId {
+ implicit def toPhiString[T](x: LongId[T]): PhiString = Unsafe(s"LongId(${x.id})")
+}
+
+case class StringId[+T](id: String) extends Id[T] {
+ override def toString: String = id
+
+ def is(stringId: String): Boolean = {
+ id == stringId
+ }
+}
+
+object StringId {
+ implicit def toPhiString[T](x: StringId[T]): PhiString = Unsafe(s"StringId(${x.id})")
+}
+
+case class UuidId[+T](id: UUID) extends Id[T] {
+ override def toString: String = id.toString
+}
+
+object UuidId {
+
+ /**
+ * @note May fail, if `string` is invalid UUID.
+ */
+ def apply[T](string: String): UuidId[T] = new UuidId[T](UUID.fromString(string))
+
+ def apply[T](): UuidId[T] = new UuidId[T](UUID.randomUUID())
+
+ implicit def ordering[T] = Ordering.by[UuidId[T], String](_.toString)
+
+ implicit def toPhiString[T](x: UuidId[T]): PhiString = Unsafe(s"UuidId(${x.id})")
+}
diff --git a/src/main/scala/xyz/driver/common/domain/Label.scala b/src/main/scala/xyz/driver/common/domain/Label.scala
new file mode 100644
index 0000000..2214216
--- /dev/null
+++ b/src/main/scala/xyz/driver/common/domain/Label.scala
@@ -0,0 +1,15 @@
+package xyz.driver.common.domain
+
+import xyz.driver.common.logging._
+
+case class Label(id: LongId[Label],
+ categoryId: LongId[Category],
+ name: String,
+ description: String)
+
+object Label {
+ implicit def toPhiString(x: Label): PhiString = {
+ import x._
+ phi"Label($id, categoryId=${Unsafe(categoryId)}, name=${Unsafe(name)}, description=${Unsafe(description)})"
+ }
+}
diff --git a/src/main/scala/xyz/driver/common/domain/PasswordHash.scala b/src/main/scala/xyz/driver/common/domain/PasswordHash.scala
new file mode 100644
index 0000000..7b25799
--- /dev/null
+++ b/src/main/scala/xyz/driver/common/domain/PasswordHash.scala
@@ -0,0 +1,42 @@
+package xyz.driver.common.domain
+
+import java.nio.charset.Charset
+
+import org.mindrot.jbcrypt.BCrypt
+
+case class PasswordHash(value: Array[Byte]) {
+
+ lazy val hashString: String = new String(value, Charset.forName("UTF-8"))
+
+ override def toString: String = {
+ s"${this.getClass.getSimpleName}($hashString)"
+ }
+
+ override def equals(that: Any): Boolean = {
+ that match {
+ case thatHash: PasswordHash => java.util.Arrays.equals(this.value, thatHash.value)
+ case _ => false
+ }
+ }
+
+ override def hashCode(): Int = {
+ 42 + java.util.Arrays.hashCode(this.value)
+ }
+
+ def is(password: String): Boolean = {
+ BCrypt.checkpw(password, hashString)
+ }
+
+}
+
+object PasswordHash {
+
+ def apply(password: String): PasswordHash = {
+ new PasswordHash(getHash(password))
+ }
+
+ private def getHash(str: String): Array[Byte] = {
+ BCrypt.hashpw(str, BCrypt.gensalt()).getBytes(Charset.forName("UTF-8"))
+ }
+
+}
diff --git a/src/main/scala/xyz/driver/common/domain/RecordRequestId.scala b/src/main/scala/xyz/driver/common/domain/RecordRequestId.scala
new file mode 100644
index 0000000..901ff66
--- /dev/null
+++ b/src/main/scala/xyz/driver/common/domain/RecordRequestId.scala
@@ -0,0 +1,16 @@
+package xyz.driver.common.domain
+
+import java.util.UUID
+
+import xyz.driver.common.logging._
+
+case class RecordRequestId(id: UUID) {
+ override def toString: String = id.toString
+}
+
+object RecordRequestId {
+
+ def apply() = new RecordRequestId(UUID.randomUUID())
+
+ implicit def toPhiString(x: RecordRequestId): PhiString = phi"${x.id}"
+}
diff --git a/src/main/scala/xyz/driver/common/domain/TextJson.scala b/src/main/scala/xyz/driver/common/domain/TextJson.scala
new file mode 100644
index 0000000..af18723
--- /dev/null
+++ b/src/main/scala/xyz/driver/common/domain/TextJson.scala
@@ -0,0 +1,14 @@
+package xyz.driver.common.domain
+
+import xyz.driver.common.logging._
+
+case class TextJson[+T](content: T) {
+ def map[U](f: T => U): TextJson[U] = copy(f(content))
+}
+
+object TextJson {
+
+ implicit def toPhiString[T](x: TextJson[T])(implicit inner: T => PhiString): PhiString = {
+ phi"TextJson(${x.content})"
+ }
+}
diff --git a/src/main/scala/xyz/driver/common/domain/User.scala b/src/main/scala/xyz/driver/common/domain/User.scala
new file mode 100644
index 0000000..83d861f
--- /dev/null
+++ b/src/main/scala/xyz/driver/common/domain/User.scala
@@ -0,0 +1,74 @@
+package xyz.driver.common.domain
+
+import java.time.LocalDateTime
+
+import xyz.driver.common.logging._
+import xyz.driver.common.domain.User.Role
+import xyz.driver.common.utils.Utils
+
+case class User(id: LongId[User],
+ email: Email,
+ name: String,
+ role: Role,
+ passwordHash: PasswordHash,
+ latestActivity: Option[LocalDateTime],
+ deleted: Option[LocalDateTime])
+
+object User {
+
+ sealed trait Role extends Product with Serializable {
+
+ /**
+ * Bit representation of this role
+ */
+ def bit: Int
+
+ def is(that: Role): Boolean = this == that
+
+ def oneOf(roles: Role*): Boolean = roles.contains(this)
+
+ def oneOf(roles: Set[Role]): Boolean = roles.contains(this)
+ }
+
+ object Role extends PhiLogging {
+ case object RecordAdmin extends Role {val bit = 1 << 0}
+ case object RecordCleaner extends Role {val bit = 1 << 1}
+ case object RecordOrganizer extends Role {val bit = 1 << 2}
+ case object DocumentExtractor extends Role {val bit = 1 << 3}
+ case object TrialSummarizer extends Role {val bit = 1 << 4}
+ case object CriteriaCurator extends Role {val bit = 1 << 5}
+ case object TrialAdmin extends Role {val bit = 1 << 6}
+ case object EligibilityVerifier extends Role{val bit = 1 << 7}
+ case object TreatmentMatchingAdmin extends Role{val bit = 1 << 8}
+ case object RoutesCurator extends Role{val bit = 1 << 9}
+
+ val RepRoles = Set[Role](RecordAdmin, RecordCleaner, RecordOrganizer, DocumentExtractor)
+
+ val TcRoles = Set[Role](TrialSummarizer, CriteriaCurator, TrialAdmin)
+
+ val TreatmentMatchingRoles = Set[Role](RoutesCurator, EligibilityVerifier, TreatmentMatchingAdmin)
+
+ val All = RepRoles ++ TcRoles ++ TreatmentMatchingRoles
+
+ def apply(bitMask: Int): Role = {
+ def extractRole(role: Role): Set[Role] =
+ if ((bitMask & role.bit) != 0) Set(role) else Set.empty[Role]
+
+ val roles = All.flatMap(extractRole)
+ roles.size match {
+ case 1 => roles.head
+ case _ =>
+ logger.error(phi"Can't convert a bit mask ${Unsafe(bitMask)} to any role")
+ throw new IllegalArgumentException()
+ }
+ }
+
+ implicit def toPhiString(x: Role): PhiString = Unsafe(Utils.getClassSimpleName(x.getClass))
+ }
+
+ implicit def toPhiString(x: User): PhiString = {
+ import x._
+ phi"User(id=$id, role=$role)"
+ }
+
+}