aboutsummaryrefslogtreecommitdiff
path: root/src/main/scala/com
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/scala/com')
-rw-r--r--src/main/scala/com/drivergrp/core/app.scala201
-rw-r--r--src/main/scala/com/drivergrp/core/auth.scala137
-rw-r--r--src/main/scala/com/drivergrp/core/config.scala24
-rw-r--r--src/main/scala/com/drivergrp/core/core.scala46
-rw-r--r--src/main/scala/com/drivergrp/core/crypto.scala19
-rw-r--r--src/main/scala/com/drivergrp/core/database.scala52
-rw-r--r--src/main/scala/com/drivergrp/core/file.scala151
-rw-r--r--src/main/scala/com/drivergrp/core/generators.scala72
-rw-r--r--src/main/scala/com/drivergrp/core/json.scala106
-rw-r--r--src/main/scala/com/drivergrp/core/logging.scala106
-rw-r--r--src/main/scala/com/drivergrp/core/messages.scala59
-rw-r--r--src/main/scala/com/drivergrp/core/rest.scala126
-rw-r--r--src/main/scala/com/drivergrp/core/stats.scala43
-rw-r--r--src/main/scala/com/drivergrp/core/time.scala75
14 files changed, 0 insertions, 1217 deletions
diff --git a/src/main/scala/com/drivergrp/core/app.scala b/src/main/scala/com/drivergrp/core/app.scala
deleted file mode 100644
index a1e1082..0000000
--- a/src/main/scala/com/drivergrp/core/app.scala
+++ /dev/null
@@ -1,201 +0,0 @@
-package com.drivergrp.core
-
-import akka.actor.ActorSystem
-import akka.http.scaladsl.Http
-import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport
-import akka.http.scaladsl.model.StatusCodes._
-import akka.http.scaladsl.model.{HttpResponse, StatusCodes}
-import akka.http.scaladsl.server.Directives._
-import akka.http.scaladsl.server.RouteResult._
-import akka.http.scaladsl.server.{ExceptionHandler, Route, RouteConcatenation}
-import akka.stream.ActorMaterializer
-import com.drivergrp.core.logging.{Logger, TypesafeScalaLogger}
-import com.drivergrp.core.rest.Swagger
-import com.drivergrp.core.time.Time
-import com.drivergrp.core.time.provider.{SystemTimeProvider, TimeProvider}
-import com.typesafe.config.Config
-import org.slf4j.LoggerFactory
-import spray.json.DefaultJsonProtocol
-
-import scala.compat.Platform.ConcurrentModificationException
-import scala.concurrent.duration._
-import scala.concurrent.{Await, Future}
-
-object app {
-
- class DriverApp(version: String,
- gitHash: String,
- modules: Seq[Module],
- time: TimeProvider = new SystemTimeProvider(),
- log: Logger = new TypesafeScalaLogger(
- com.typesafe.scalalogging.Logger(LoggerFactory.getLogger(classOf[DriverApp]))),
- config: Config = com.drivergrp.core.config.loadDefaultConfig,
- interface: String = "::0",
- baseUrl: String = "localhost:8080",
- port: Int = 8080) {
-
- implicit private lazy val actorSystem = ActorSystem("spray-routing", config)
- implicit private lazy val executionContext = actorSystem.dispatcher
- implicit private lazy val materializer = ActorMaterializer()(actorSystem)
- private lazy val http = Http()(actorSystem)
-
- def run(): Unit = {
- activateServices(modules)
- scheduleServicesDeactivation(modules)
- bindHttp(modules)
- Console.print(s"${this.getClass.getName} App is started\n")
- }
-
- def stop(): Unit = {
- http.shutdownAllConnectionPools().onComplete { _ =>
- val _ = actorSystem.terminate()
- val terminated = Await.result(actorSystem.whenTerminated, 30.seconds)
- val addressTerminated = if (terminated.addressTerminated) "is" else "is not"
- Console.print(s"${this.getClass.getName} App $addressTerminated stopped ")
- }
- }
-
- protected def bindHttp(modules: Seq[Module]): Unit = {
- val serviceTypes = modules.flatMap(_.routeTypes)
- val swaggerService = new Swagger(baseUrl, version, actorSystem, serviceTypes, config)
- val swaggerRoutes = swaggerService.routes ~ swaggerService.swaggerUI
- val versionRt = versionRoute(version, gitHash, time.currentTime())
-
- val generalExceptionHandler = ExceptionHandler {
-
- case is: IllegalStateException =>
- extractUri { uri =>
- // TODO: extract `requestUuid` from request or thread, provided by linkerd/zipkin
- def requestUuid = java.util.UUID.randomUUID.toString
-
- log.debug(s"Request is not allowed to $uri ($requestUuid)", is)
- complete(
- HttpResponse(BadRequest,
- entity = s"""{ "requestUuid": "$requestUuid", "message": "${is.getMessage}" }"""))
- }
-
- case cm: ConcurrentModificationException =>
- extractUri { uri =>
- // TODO: extract `requestUuid` from request or thread, provided by linkerd/zipkin
- def requestUuid = java.util.UUID.randomUUID.toString
-
- log.debug(s"Concurrent modification of the resource $uri ($requestUuid)", cm)
- complete(
- HttpResponse(Conflict,
- entity = s"""{ "requestUuid": "$requestUuid", "message": "${cm.getMessage}" }"""))
- }
-
- case t: Throwable =>
- extractUri { uri =>
- // TODO: extract `requestUuid` from request or thread, provided by linkerd/zipkin
- def requestUuid = java.util.UUID.randomUUID.toString
-
- log.error(s"Request to $uri could not be handled normally ($requestUuid)", t)
- complete(
- HttpResponse(InternalServerError,
- entity = s"""{ "requestUuid": "$requestUuid", "message": "${t.getMessage}" }"""))
- }
- }
-
- val _ = Future {
- http.bindAndHandle(route2HandlerFlow(handleExceptions(generalExceptionHandler) {
- logRequestResult("log")(modules.map(_.route).foldLeft(versionRt ~ swaggerRoutes)(_ ~ _))
- }), interface, port)(materializer)
- }
- }
-
- protected def versionRoute(version: String, gitHash: String, startupTime: Time): Route = {
- import DefaultJsonProtocol._
- import SprayJsonSupport._
-
- path("version") {
- val currentTime = time.currentTime().millis
- complete(
- Map(
- "version" -> version,
- "gitHash" -> gitHash,
- "modules" -> modules.map(_.name).mkString(", "),
- "startupTime" -> startupTime.millis.toString,
- "serverTime" -> currentTime.toString,
- "uptime" -> (currentTime - startupTime.millis).toString
- ))
- }
- }
-
- /**
- * Initializes services
- */
- protected def activateServices(services: Seq[Module]): Unit = {
- services.foreach { service =>
- Console.print(s"Service ${service.name} starts ...")
- try {
- service.activate()
- } catch {
- case t: Throwable =>
- log.fatal(s"Service ${service.name} failed to activate", t)
- Console.print(" Failed! (check log)")
- }
- Console.print(" Done\n")
- }
- }
-
- /**
- * Schedules services to be deactivated on the app shutdown
- */
- protected def scheduleServicesDeactivation(services: Seq[Module]) = {
- Runtime.getRuntime.addShutdownHook(new Thread() {
- override def run(): Unit = {
- services.foreach { service =>
- Console.print(s"Service ${service.name} shutting down ...")
- try {
- service.deactivate()
- } catch {
- case t: Throwable =>
- log.fatal(s"Service ${service.name} failed to deactivate", t)
- Console.print(" Failed! (check log)")
- }
- Console.print(" Done\n")
- }
- }
- })
- }
- }
-
- import scala.reflect.runtime.universe._
-
- trait Module {
- val name: String
- def route: Route
- def routeTypes: Seq[Type]
-
- def activate(): Unit = {}
- def deactivate(): Unit = {}
- }
-
- class EmptyModule extends Module {
- val name = "Nothing"
- def route: Route = complete(StatusCodes.OK)
- def routeTypes = Seq.empty[Type]
- }
-
- class SimpleModule(val name: String, val route: Route, routeType: Type) extends Module {
- def routeTypes: Seq[Type] = Seq(routeType)
- }
-
- /**
- * Module implementation which may be used to composed a few
- *
- * @param name more general name of the composite module,
- * must be provided as there is no good way to automatically
- * generalize the name from the composed modules' names
- * @param modules modules to compose into a single one
- */
- class CompositeModule(val name: String, modules: Seq[Module]) extends Module with RouteConcatenation {
-
- def route: Route = modules.map(_.route).reduce(_ ~ _)
- def routeTypes = modules.flatMap(_.routeTypes)
-
- override def activate() = modules.foreach(_.activate())
- override def deactivate() = modules.reverse.foreach(_.deactivate())
- }
-}
diff --git a/src/main/scala/com/drivergrp/core/auth.scala b/src/main/scala/com/drivergrp/core/auth.scala
deleted file mode 100644
index 6b8cdaa..0000000
--- a/src/main/scala/com/drivergrp/core/auth.scala
+++ /dev/null
@@ -1,137 +0,0 @@
-package com.drivergrp.core
-
-import akka.http.scaladsl.model.headers.HttpChallenges
-import akka.http.scaladsl.server.AuthenticationFailedRejection.CredentialsRejected
-
-import scala.concurrent.Future
-import scala.util.{Failure, Success, Try}
-import scalaz.OptionT
-
-object auth {
-
- sealed trait Permission
- case object CanSeeUser extends Permission
- case object CanSeeAssay extends Permission
- case object CanSeeReport extends Permission
- case object CanCreateReport extends Permission
- case object CanEditReport extends Permission
- case object CanEditReviewingReport extends Permission
- case object CanSignOutReport extends Permission
- case object CanShareReportWithPatient extends Permission
- case object CanAssignRoles extends Permission
-
- trait Role {
- val id: Id[Role]
- val name: Name[Role]
- val permissions: Set[Permission]
-
- def hasPermission(permission: Permission): Boolean = permissions.contains(permission)
- }
-
- case object ObserverRole extends Role {
- val id = Id(1L)
- val name = Name("observer")
- val permissions = Set[Permission](CanSeeUser, CanSeeAssay, CanSeeReport)
- }
-
- case object PatientRole extends Role {
- val id = Id(2L)
- val name = Name("patient")
- val permissions = Set.empty[Permission]
- }
-
- case object CuratorRole extends Role {
- val id = Id(3L)
- val name = Name("curator")
- val permissions = Set[Permission](CanSeeUser, CanSeeAssay, CanSeeReport, CanEditReport)
- }
-
- case object PathologistRole extends Role {
- val id = Id(4L)
- val name = Name("pathologist")
- val permissions =
- Set[Permission](CanSeeUser, CanSeeAssay, CanSeeReport, CanEditReport, CanSignOutReport, CanEditReviewingReport)
- }
-
- case object AdministratorRole extends Role {
- val id = Id(5L)
- val name = Name("administrator")
- val permissions = Set[Permission](
- CanSeeUser,
- CanSeeAssay,
- CanSeeReport,
- CanCreateReport,
- CanEditReport,
- CanEditReviewingReport,
- CanSignOutReport,
- CanShareReportWithPatient,
- CanAssignRoles
- )
- }
-
- trait User {
- def id: Id[User]
- def roles: Set[Role]
- def permissions: Set[Permission] = roles.flatMap(_.permissions)
- }
-
- final case class Macaroon(value: String)
-
- final case class Base64[T](value: String)
-
- final case class AuthToken(value: Base64[Macaroon])
-
- final case class PasswordHash(value: String)
-
- object AuthService {
- val AuthenticationTokenHeader = "WWW-Authenticate"
- }
-
- trait AuthService[U <: User] {
-
- import akka.http.scaladsl.server._
- import Directives._
-
- protected def authStatus(authToken: AuthToken): OptionT[Future, U]
-
- def authorize(permission: Permission): Directive1[(AuthToken, U)] = {
- parameters('authToken.?).flatMap { parameterTokenValue =>
- optionalHeaderValueByName(AuthService.AuthenticationTokenHeader).flatMap { headerTokenValue =>
- verifyAuthToken(headerTokenValue.orElse(parameterTokenValue), permission)
- }
- }
- }
-
- private def verifyAuthToken(tokenOption: Option[String], permission: Permission): Directive1[(AuthToken, U)] =
- tokenOption match {
- case Some(tokenValue) =>
- val token = AuthToken(Base64[Macaroon](tokenValue))
-
- onComplete(authStatus(token).run).flatMap { tokenUserResult =>
- checkPermissions(tokenUserResult, permission, token)
- }
-
- case None =>
- reject(MissingHeaderRejection(AuthService.AuthenticationTokenHeader))
- }
-
- private def checkPermissions(userResult: Try[Option[U]],
- permission: Permission,
- token: AuthToken): Directive1[(AuthToken, U)] = {
- userResult match {
- case Success(Some(user)) =>
- if (user.roles.exists(_.hasPermission(permission))) provide(token -> user)
- else {
- val challenge = HttpChallenges.basic(s"User does not have the required permission $permission")
- reject(AuthenticationFailedRejection(CredentialsRejected, challenge))
- }
-
- case Success(None) =>
- reject(ValidationRejection(s"Wasn't able to find authenticated user for the token provided"))
-
- case Failure(t) =>
- reject(ValidationRejection(s"Wasn't able to verify token for authenticated user", Some(t)))
- }
- }
- }
-}
diff --git a/src/main/scala/com/drivergrp/core/config.scala b/src/main/scala/com/drivergrp/core/config.scala
deleted file mode 100644
index 29cd9ed..0000000
--- a/src/main/scala/com/drivergrp/core/config.scala
+++ /dev/null
@@ -1,24 +0,0 @@
-package com.drivergrp.core
-
-import java.io.File
-import com.typesafe.config.{Config, ConfigFactory}
-
-object config {
-
- def loadDefaultConfig: Config = {
- val configDefaults = ConfigFactory.load(this.getClass.getClassLoader, "application.conf")
-
- scala.sys.props.get("application.config") match {
-
- case Some(filename) =>
- val configFile = new File(filename)
- if (configFile.exists()) {
- ConfigFactory.parseFile(configFile).withFallback(configDefaults)
- } else {
- throw new IllegalStateException(s"No config found at $filename")
- }
-
- case None => configDefaults
- }
- }
-}
diff --git a/src/main/scala/com/drivergrp/core/core.scala b/src/main/scala/com/drivergrp/core/core.scala
deleted file mode 100644
index 158447f..0000000
--- a/src/main/scala/com/drivergrp/core/core.scala
+++ /dev/null
@@ -1,46 +0,0 @@
-package com.drivergrp
-
-import scalaz.Equal
-
-package object core {
- import scala.language.reflectiveCalls
-
- def make[T](v: => T)(f: T => Unit): T = {
- val value = v; f(value); value
- }
-
- def using[R <: { def close() }, P](r: => R)(f: R => P): P = {
- val resource = r
- try {
- f(resource)
- } finally {
- resource.close()
- }
- }
-
- object tagging {
- private[core] trait Tagged[+V, +Tag]
- }
- type @@[+V, +Tag] = V with tagging.Tagged[V, Tag]
-
- type Id[+Tag] = Long @@ Tag
- object Id {
- def apply[Tag](value: Long) = value.asInstanceOf[Id[Tag]]
- }
- implicit def idEqual[T]: Equal[Id[T]] = Equal.equal[Id[T]](_ == _)
- implicit def idOrdering[T]: Ordering[Id[T]] = Ordering.by(i => i: Long)
-
- type Name[+Tag] = String @@ Tag
- object Name {
- def apply[Tag](value: String) = value.asInstanceOf[Name[Tag]]
- }
-
- implicit def nameEqual[T]: Equal[Name[T]] = Equal.equal[Name[T]](_ == _)
- implicit def nameOrdering[T]: Ordering[Name[T]] = Ordering.by(n => n: String)
-
- object revision {
- final case class Revision[T](id: String)
-
- implicit def revisionEqual[T]: Equal[Revision[T]] = Equal.equal[Revision[T]](_.id == _.id)
- }
-}
diff --git a/src/main/scala/com/drivergrp/core/crypto.scala b/src/main/scala/com/drivergrp/core/crypto.scala
deleted file mode 100644
index f693fa3..0000000
--- a/src/main/scala/com/drivergrp/core/crypto.scala
+++ /dev/null
@@ -1,19 +0,0 @@
-package com.drivergrp.core
-
-import com.drivergrp.core.auth.AuthToken
-
-object crypto {
-
- final case class EncryptionKey(value: String)
-
- final case class DecryptionKey(value: String)
-
- trait Crypto {
-
- def keyForToken(authToken: AuthToken): EncryptionKey
-
- def encrypt(encryptionKey: EncryptionKey)(message: Array[Byte]): Array[Byte]
-
- def decrypt(decryptionKey: EncryptionKey)(message: Array[Byte]): Array[Byte]
- }
-}
diff --git a/src/main/scala/com/drivergrp/core/database.scala b/src/main/scala/com/drivergrp/core/database.scala
deleted file mode 100644
index 581c5de..0000000
--- a/src/main/scala/com/drivergrp/core/database.scala
+++ /dev/null
@@ -1,52 +0,0 @@
-package com.drivergrp.core
-
-import com.drivergrp.core.time.Time
-
-import scala.concurrent.Future
-import slick.backend.DatabaseConfig
-import slick.driver.JdbcProfile
-
-object database {
-
- trait Database {
- val profile: JdbcProfile
- val database: JdbcProfile#Backend#Database
- }
-
- object Database {
-
- def fromConfig(databaseName: String): Database = {
- val dbConfig: DatabaseConfig[JdbcProfile] = DatabaseConfig.forConfig(databaseName)
-
- new Database {
- val profile: JdbcProfile = dbConfig.driver
- val database: JdbcProfile#Backend#Database = dbConfig.db
- }
- }
- }
-
- trait IdColumnTypes {
- val database: Database
-
- import database.profile.api._
-
- implicit def idColumnType[T] =
- MappedColumnType.base[Id[T], Long](id => id: Long, Id[T](_))
-
- implicit def nameColumnType[T] =
- MappedColumnType.base[Name[T], String](name => name: String, Name[T](_))
-
- implicit val timeColumnType = MappedColumnType.base[Time, Long](time => time.millis, Time(_))
- }
-
- trait DatabaseObject extends IdColumnTypes {
-
- def createTables(): Future[Unit]
- def disconnect(): Unit
- }
-
- abstract class DatabaseObjectAdapter extends DatabaseObject {
- def createTables(): Future[Unit] = Future.successful(())
- def disconnect(): Unit = {}
- }
-}
diff --git a/src/main/scala/com/drivergrp/core/file.scala b/src/main/scala/com/drivergrp/core/file.scala
deleted file mode 100644
index 20bd36e..0000000
--- a/src/main/scala/com/drivergrp/core/file.scala
+++ /dev/null
@@ -1,151 +0,0 @@
-package com.drivergrp.core
-
-import java.io.File
-import java.nio.file.{Path, Paths}
-import java.util.UUID._
-
-import com.amazonaws.services.s3.AmazonS3
-import com.amazonaws.services.s3.model.{Bucket, GetObjectRequest, ListObjectsV2Request}
-import com.drivergrp.core.revision.Revision
-import com.drivergrp.core.time.Time
-
-import scala.concurrent.{ExecutionContext, Future}
-import scalaz.{ListT, OptionT}
-
-object file {
-
- final case class FileLink(
- name: Name[File],
- location: Path,
- revision: Revision[File],
- lastModificationDate: Time
- )
-
- trait FileService {
-
- def getFileLink(id: Name[File]): FileLink
-
- def getFile(fileLink: FileLink): File
- }
-
- trait FileStorage {
-
- def upload(localSource: File, destination: Path): Future[Unit]
-
- def download(filePath: Path): OptionT[Future, File]
-
- def delete(filePath: Path): Future[Unit]
-
- def list(path: Path): ListT[Future, FileLink]
-
- /** List of characters to avoid in S3 (I would say file names in general)
- *
- * @see http://stackoverflow.com/questions/7116450/what-are-valid-s3-key-names-that-can-be-accessed-via-the-s3-rest-api
- */
- private val illegalChars = "\\^`><{}][#%~|&@:,$=+?; "
-
- protected def checkSafeFileName[T](filePath: Path)(f: => T): T = {
- filePath.toString.find(c => illegalChars.contains(c)) match {
- case Some(illegalCharacter) =>
- throw new IllegalArgumentException(s"File name cannot contain character `$illegalCharacter`")
- case None => f
- }
- }
- }
-
- class S3Storage(s3: AmazonS3, bucket: Name[Bucket], executionContext: ExecutionContext) extends FileStorage {
- implicit private val execution = executionContext
-
- def upload(localSource: File, destination: Path): Future[Unit] = Future {
- checkSafeFileName(destination) {
- val _ = s3.putObject(bucket, destination.toString, localSource).getETag
- }
- }
-
- def download(filePath: Path): OptionT[Future, File] =
- OptionT.optionT(Future {
- val tempDir = System.getProperty("java.io.tmpdir")
- val randomFolderName = randomUUID().toString
- val tempDestinationFile = new File(Paths.get(tempDir, randomFolderName, filePath.toString).toString)
-
- if (!tempDestinationFile.getParentFile.mkdirs()) {
- throw new Exception(s"Failed to create temp directory to download file `$tempDestinationFile`")
- } else {
- Option(s3.getObject(new GetObjectRequest(bucket, filePath.toString), tempDestinationFile)).map { _ =>
- tempDestinationFile
- }
- }
- })
-
- def delete(filePath: Path): Future[Unit] = Future {
- s3.deleteObject(bucket, filePath.toString)
- }
-
- def list(path: Path): ListT[Future, FileLink] =
- ListT.listT(Future {
- import scala.collection.JavaConverters._
- val req = new ListObjectsV2Request().withBucketName(bucket).withPrefix(path.toString).withMaxKeys(2)
-
- def isInSubFolder(path: Path)(fileLink: FileLink) =
- fileLink.location.toString.replace(path.toString + "/", "").contains("/")
-
- Iterator.continually(s3.listObjectsV2(req)).takeWhile { result =>
- req.setContinuationToken(result.getNextContinuationToken)
- result.isTruncated
- } flatMap { result =>
- result.getObjectSummaries.asScala.toList.map { summary =>
- FileLink(Name[File](summary.getKey),
- Paths.get(path.toString + "/" + summary.getKey),
- Revision[File](summary.getETag),
- Time(summary.getLastModified.getTime))
- } filterNot isInSubFolder(path)
- } toList
- })
- }
-
- class FileSystemStorage(executionContext: ExecutionContext) extends FileStorage {
- implicit private val execution = executionContext
-
- def upload(localSource: File, destination: Path): Future[Unit] = Future {
- checkSafeFileName(destination) {
- val destinationFile = destination.toFile
-
- if (destinationFile.getParentFile.exists() || destinationFile.getParentFile.mkdirs()) {
- if (localSource.renameTo(destinationFile)) ()
- else {
- throw new Exception(
- s"Failed to move file from `${localSource.getCanonicalPath}` to `${destinationFile.getCanonicalPath}`")
- }
- } else {
- throw new Exception(s"Failed to create parent directories for file `${destinationFile.getCanonicalPath}`")
- }
- }
- }
-
- def download(filePath: Path): OptionT[Future, File] =
- OptionT.optionT(Future {
- Option(new File(filePath.toString)).filter(file => file.exists() && file.isFile)
- })
-
- def delete(filePath: Path): Future[Unit] = Future {
- val file = new File(filePath.toString)
- if (file.delete()) ()
- else {
- throw new Exception(s"Failed to delete file $file" + (if (!file.exists()) ", file does not exist." else "."))
- }
- }
-
- def list(path: Path): ListT[Future, FileLink] =
- ListT.listT(Future {
- val file = new File(path.toString)
- if (file.isDirectory) {
- file.listFiles().toList.filter(_.isFile).map { file =>
- FileLink(Name[File](file.getName),
- Paths.get(file.getPath),
- Revision[File](file.hashCode.toString),
- Time(file.lastModified()))
- }
- } else List.empty[FileLink]
- })
- }
-}
diff --git a/src/main/scala/com/drivergrp/core/generators.scala b/src/main/scala/com/drivergrp/core/generators.scala
deleted file mode 100644
index 10df7db..0000000
--- a/src/main/scala/com/drivergrp/core/generators.scala
+++ /dev/null
@@ -1,72 +0,0 @@
-package com.drivergrp.core
-
-import java.math.MathContext
-
-import com.drivergrp.core.revision.Revision
-import com.drivergrp.core.time.{Time, TimeRange}
-
-import scala.reflect.ClassTag
-import scala.util.Random
-
-object generators {
-
- private val random = new Random
- import random._
-
- private val DefaultMaxLength = 100
- private val StringLetters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ ".toSet
-
- def nextId[T](): Id[T] = Id[T](scala.math.abs(nextLong()))
-
- def nextId[T](maxValue: Int): Id[T] = Id[T](scala.math.abs(nextInt(maxValue).toLong))
-
- def nextName[T](maxLength: Int = DefaultMaxLength): Name[T] = Name[T](nextString(maxLength))
-
- def nextUuid() = java.util.UUID.randomUUID
-
- def nextRevision[T]() = Revision[T](nextUuid().toString)
-
- def nextString(maxLength: Int = DefaultMaxLength): String =
- (oneOf[Char](StringLetters) +: arrayOf(oneOf[Char](StringLetters), maxLength - 1)).mkString
-
- def nextOption[T](value: => T): Option[T] = if (nextBoolean) Option(value) else None
-
- def nextPair[L, R](left: => L, right: => R): (L, R) = (left, right)
-
- def nextTriad[F, S, T](first: => F, second: => S, third: => T): (F, S, T) = (first, second, third)
-
- def nextTime(): Time = Time(math.abs(nextLong() % System.currentTimeMillis))
-
- def nextTimeRange(): TimeRange = {
- val oneTime = nextTime()
- val anotherTime = nextTime()
-
- TimeRange(Time(scala.math.min(oneTime.millis, anotherTime.millis)),
- Time(scala.math.max(oneTime.millis, anotherTime.millis)))
- }
-
- def nextBigDecimal(multiplier: Double = 1000000.00, precision: Int = 2): BigDecimal =
- BigDecimal(multiplier * nextDouble, new MathContext(precision))
-
- def oneOf[T](items: T*): T = oneOf(items.toSet)
-
- def oneOf[T](items: Set[T]): T = items.toSeq(nextInt(items.size))
-
- def arrayOf[T: ClassTag](generator: => T, maxLength: Int = DefaultMaxLength): Array[T] =
- Array.fill(nextInt(maxLength))(generator)
-
- def seqOf[T](generator: => T, maxLength: Int = DefaultMaxLength): Seq[T] =
- Seq.fill(nextInt(maxLength))(generator)
-
- def vectorOf[T](generator: => T, maxLength: Int = DefaultMaxLength): Vector[T] =
- Vector.fill(nextInt(maxLength))(generator)
-
- def listOf[T](generator: => T, maxLength: Int = DefaultMaxLength): List[T] =
- List.fill(nextInt(maxLength))(generator)
-
- def setOf[T](generator: => T, maxLength: Int = DefaultMaxLength): Set[T] =
- seqOf(generator, maxLength).toSet
-
- def mapOf[K, V](maxLength: Int, keyGenerator: => K, valueGenerator: => V): Map[K, V] =
- seqOf(nextPair(keyGenerator, valueGenerator), maxLength).toMap
-}
diff --git a/src/main/scala/com/drivergrp/core/json.scala b/src/main/scala/com/drivergrp/core/json.scala
deleted file mode 100644
index 9a30161..0000000
--- a/src/main/scala/com/drivergrp/core/json.scala
+++ /dev/null
@@ -1,106 +0,0 @@
-package com.drivergrp.core
-
-import akka.http.scaladsl.model.Uri.Path
-import akka.http.scaladsl.server.PathMatcher.Matched
-import akka.http.scaladsl.server.{PathMatcher, _}
-import akka.http.scaladsl.unmarshalling.Unmarshaller
-import com.drivergrp.core.revision.Revision
-import com.drivergrp.core.time.Time
-import spray.json.{DeserializationException, JsNumber, _}
-
-import scala.reflect.runtime.universe._
-
-object json {
-
- def IdInPath[T]: PathMatcher1[Id[T]] =
- PathMatcher("""[+-]?\d*""".r) flatMap { string =>
- try Some(Id[T](string.toLong))
- catch { case _: IllegalArgumentException => None }
- }
-
- implicit def idFormat[T] = new RootJsonFormat[Id[T]] {
- def write(id: Id[T]) = JsNumber(id)
-
- def read(value: JsValue) = value match {
- case JsNumber(id) => Id[T](id.toLong)
- case _ => throw new DeserializationException("Id expects number")
- }
- }
-
- def NameInPath[T]: PathMatcher1[Name[T]] = new PathMatcher1[Name[T]] {
- def apply(path: Path) = Matched(Path.Empty, Tuple1(Name[T](path.toString)))
- }
-
- implicit def nameFormat[T] = new RootJsonFormat[Name[T]] {
- def write(name: Name[T]) = JsString(name)
-
- def read(value: JsValue): Name[T] = value match {
- case JsString(name) => Name[T](name)
- case _ => throw new DeserializationException("Name expects string")
- }
- }
-
- def TimeInPath: PathMatcher1[Time] =
- PathMatcher("""[+-]?\d*""".r) flatMap { string =>
- try Some(Time(string.toLong))
- catch { case _: IllegalArgumentException => None }
- }
-
- implicit val timeFormat = new RootJsonFormat[Time] {
- def write(time: Time) = JsObject("timestamp" -> JsNumber(time.millis))
-
- def read(value: JsValue): Time = value match {
- case JsObject(fields) =>
- fields
- .get("timestamp")
- .flatMap {
- case JsNumber(millis) => Some(Time(millis.toLong))
- case _ => None
- }
- .getOrElse(throw new DeserializationException("Time expects number"))
- case _ => throw new DeserializationException("Time expects number")
- }
- }
-
- def RevisionInPath[T]: PathMatcher1[Revision[T]] =
- PathMatcher("""[\da-fA-F]{8}-[\da-fA-F]{4}-[\da-fA-F]{4}-[\da-fA-F]{4}-[\da-fA-F]{12}""".r) flatMap { string =>
- Some(Revision[T](string))
- }
-
- implicit def revisionFromStringUnmarshaller[T]: Unmarshaller[String, Revision[T]] =
- Unmarshaller.strict[String, Revision[T]](Revision[T](_))
-
- implicit def revisionFormat[T] = new RootJsonFormat[Revision[T]] {
- def write(revision: Revision[T]) = JsString(revision.id.toString)
-
- def read(value: JsValue): Revision[T] = value match {
- case JsString(revision) => Revision[T](revision)
- case _ => throw new DeserializationException("Revision expects uuid string")
- }
- }
-
- class EnumJsonFormat[T](mapping: (String, T)*) extends JsonFormat[T] {
- private val map = mapping.toMap
-
- override def write(value: T): JsValue = {
- map.find(_._2 == value).map(_._1) match {
- case Some(name) => JsString(name)
- case _ => serializationError(s"Value $value is not found in the mapping $map")
- }
- }
-
- override def read(json: JsValue): T = json match {
- case JsString(name) =>
- map.getOrElse(name, throw new DeserializationException(s"Value $name is not found in the mapping $map"))
- case _ => deserializationError("Expected string as enumeration value, but got " + json)
- }
- }
-
- class ValueClassFormat[T: TypeTag](writeValue: T => BigDecimal, create: BigDecimal => T) extends JsonFormat[T] {
- def write(valueClass: T) = JsNumber(writeValue(valueClass))
- def read(json: JsValue): T = json match {
- case JsNumber(value) => create(value)
- case _ => deserializationError(s"Expected number as ${typeOf[T].getClass.getName}, but got " + json)
- }
- }
-}
diff --git a/src/main/scala/com/drivergrp/core/logging.scala b/src/main/scala/com/drivergrp/core/logging.scala
deleted file mode 100644
index 126c670..0000000
--- a/src/main/scala/com/drivergrp/core/logging.scala
+++ /dev/null
@@ -1,106 +0,0 @@
-package com.drivergrp.core
-
-import org.slf4j.Marker
-
-object logging {
-
- trait Logger {
-
- def fatal(message: String): Unit
- def fatal(message: String, cause: Throwable): Unit
- def fatal(message: String, args: AnyRef*): Unit
- def fatal(marker: Marker, message: String): Unit
- def fatal(marker: Marker, message: String, cause: Throwable): Unit
- def fatal(marker: Marker, message: String, args: AnyRef*): Unit
-
- def error(message: String): Unit
- def error(message: String, cause: Throwable): Unit
- def error(message: String, args: AnyRef*): Unit
- def error(marker: Marker, message: String): Unit
- def error(marker: Marker, message: String, cause: Throwable): Unit
- def error(marker: Marker, message: String, args: AnyRef*): Unit
-
- def audit(message: String): Unit
- def audit(message: String, cause: Throwable): Unit
- def audit(message: String, args: AnyRef*): Unit
- def audit(marker: Marker, message: String): Unit
- def audit(marker: Marker, message: String, cause: Throwable): Unit
- def audit(marker: Marker, message: String, args: AnyRef*): Unit
-
- def debug(message: String): Unit
- def debug(message: String, cause: Throwable): Unit
- def debug(message: String, args: AnyRef*): Unit
- def debug(marker: Marker, message: String): Unit
- def debug(marker: Marker, message: String, cause: Throwable): Unit
- def debug(marker: Marker, message: String, args: AnyRef*): Unit
- }
-
- /**
- * Logger implementation which uses `com.typesafe.scalalogging.Logger` on the back.
- * It redefines the meaning of logging levels to fit to the Driver infrastructure design,
- * and as using error and warn, debug and trace was always confusing and mostly done wrong.
- *
- * @param scalaLogging com.typesafe.scalalogging.Logger which logging will be delegated to
- */
- class TypesafeScalaLogger(scalaLogging: com.typesafe.scalalogging.Logger) extends Logger {
-
- def fatal(message: String): Unit = scalaLogging.error(message)
- def fatal(message: String, cause: Throwable): Unit = scalaLogging.error(message, cause)
- def fatal(message: String, args: AnyRef*): Unit = scalaLogging.error(message, args)
- def fatal(marker: Marker, message: String): Unit = scalaLogging.error(marker, message)
- def fatal(marker: Marker, message: String, cause: Throwable): Unit = scalaLogging.error(marker, message, cause)
- def fatal(marker: Marker, message: String, args: AnyRef*): Unit = scalaLogging.error(marker, message, args)
-
- def error(message: String): Unit = scalaLogging.warn(message)
- def error(message: String, cause: Throwable): Unit = scalaLogging.warn(message, cause)
- def error(message: String, args: AnyRef*): Unit = scalaLogging.warn(message, args)
- def error(marker: Marker, message: String): Unit = scalaLogging.warn(marker, message)
- def error(marker: Marker, message: String, cause: Throwable): Unit = scalaLogging.warn(marker, message, cause)
- def error(marker: Marker, message: String, args: AnyRef*): Unit = scalaLogging.warn(marker, message, args)
-
- def audit(message: String): Unit = scalaLogging.info(message)
- def audit(message: String, cause: Throwable): Unit = scalaLogging.info(message, cause)
- def audit(message: String, args: AnyRef*): Unit = scalaLogging.info(message, args)
- def audit(marker: Marker, message: String): Unit = scalaLogging.info(marker, message)
- def audit(marker: Marker, message: String, cause: Throwable): Unit = scalaLogging.info(marker, message, cause)
- def audit(marker: Marker, message: String, args: AnyRef*): Unit = scalaLogging.info(marker, message, args)
-
- def debug(message: String): Unit = scalaLogging.debug(message)
- def debug(message: String, cause: Throwable): Unit = scalaLogging.debug(message, cause)
- def debug(message: String, args: AnyRef*): Unit = scalaLogging.debug(message, args)
- def debug(marker: Marker, message: String): Unit = scalaLogging.debug(marker, message)
- def debug(marker: Marker, message: String, cause: Throwable): Unit = scalaLogging.debug(marker, message, cause)
- def debug(marker: Marker, message: String, args: AnyRef*): Unit = scalaLogging.debug(marker, message, args)
- }
-
- class NoLogger() extends Logger {
-
- def fatal(message: String): Unit = {}
- def fatal(message: String, cause: Throwable): Unit = {}
- def fatal(message: String, args: AnyRef*): Unit = {}
- def fatal(marker: Marker, message: String): Unit = {}
- def fatal(marker: Marker, message: String, cause: Throwable): Unit = {}
- def fatal(marker: Marker, message: String, args: AnyRef*): Unit = {}
-
- def error(message: String): Unit = {}
- def error(message: String, cause: Throwable): Unit = {}
- def error(message: String, args: AnyRef*): Unit = {}
- def error(marker: Marker, message: String): Unit = {}
- def error(marker: Marker, message: String, cause: Throwable): Unit = {}
- def error(marker: Marker, message: String, args: AnyRef*): Unit = {}
-
- def audit(message: String): Unit = {}
- def audit(message: String, cause: Throwable): Unit = {}
- def audit(message: String, args: AnyRef*): Unit = {}
- def audit(marker: Marker, message: String): Unit = {}
- def audit(marker: Marker, message: String, cause: Throwable): Unit = {}
- def audit(marker: Marker, message: String, args: AnyRef*): Unit = {}
-
- def debug(message: String): Unit = {}
- def debug(message: String, cause: Throwable): Unit = {}
- def debug(message: String, args: AnyRef*): Unit = {}
- def debug(marker: Marker, message: String): Unit = {}
- def debug(marker: Marker, message: String, cause: Throwable): Unit = {}
- def debug(marker: Marker, message: String, args: AnyRef*): Unit = {}
- }
-}
diff --git a/src/main/scala/com/drivergrp/core/messages.scala b/src/main/scala/com/drivergrp/core/messages.scala
deleted file mode 100644
index 3a97401..0000000
--- a/src/main/scala/com/drivergrp/core/messages.scala
+++ /dev/null
@@ -1,59 +0,0 @@
-package com.drivergrp.core
-
-import java.util.Locale
-
-import com.drivergrp.core.logging.Logger
-import com.typesafe.config.Config
-
-import scala.collection.JavaConverters._
-
-/**
- * Scala internationalization (i18n) support
- */
-object messages {
-
- object Messages {
- def messages(config: Config, log: Logger, locale: Locale = Locale.US): Messages = {
- val map = config.getConfig(locale.getLanguage).root().unwrapped().asScala.mapValues(_.toString).toMap
- Messages(map, locale, log)
- }
- }
-
- final case class Messages(map: Map[String, String], locale: Locale, log: Logger) {
-
- /**
- * Returns message for the key
- *
- * @param key key
- * @return message
- */
- def apply(key: String): String = {
- map.get(key) match {
- case Some(message) => message
- case None =>
- log.error(s"Message with key '$key' not found for locale '${locale.getLanguage}'")
- key
- }
- }
-
- /**
- * Returns message for the key and formats that with parameters
- *
- * @example "Hello {0}!" with "Joe" will be "Hello Joe!"
- *
- * @param key key
- * @param params params to be embedded
- * @return formatted message
- */
- def apply(key: String, params: Any*): String = {
-
- def format(formatString: String, params: Seq[Any]) =
- params.zipWithIndex.foldLeft(formatString) {
- case (res, (value, index)) => res.replace(s"{$index}", value.toString)
- }
-
- val template = apply(key)
- format(template, params)
- }
- }
-}
diff --git a/src/main/scala/com/drivergrp/core/rest.scala b/src/main/scala/com/drivergrp/core/rest.scala
deleted file mode 100644
index d97e13e..0000000
--- a/src/main/scala/com/drivergrp/core/rest.scala
+++ /dev/null
@@ -1,126 +0,0 @@
-package com.drivergrp.core
-
-import akka.actor.ActorSystem
-import akka.http.scaladsl.Http
-import akka.http.scaladsl.model._
-import akka.http.scaladsl.model.headers.RawHeader
-import akka.http.scaladsl.unmarshalling.Unmarshal
-import akka.stream.ActorMaterializer
-import akka.stream.scaladsl.Flow
-import akka.util.ByteString
-import com.drivergrp.core.auth.{AuthService, AuthToken}
-import com.drivergrp.core.crypto.Crypto
-import com.drivergrp.core.logging.Logger
-import com.drivergrp.core.stats.Stats
-import com.drivergrp.core.time.TimeRange
-import com.drivergrp.core.time.provider.TimeProvider
-import com.github.swagger.akka.model._
-import com.github.swagger.akka.{HasActorSystem, SwaggerHttpService}
-import com.typesafe.config.Config
-
-import scala.concurrent.{ExecutionContext, Future}
-import scala.util.{Failure, Success}
-import scalaz.{Failure => _, Success => _}
-
-object rest {
-
- trait Service
-
- trait ServiceTransport {
-
- def sendRequest(authToken: AuthToken)(requestStub: HttpRequest): Future[Unmarshal[ResponseEntity]]
- }
-
- trait ServiceDiscovery {
-
- def discover[T <: Service](serviceName: Name[Service]): T
- }
-
- class HttpRestServiceTransport(actorSystem: ActorSystem, executionContext: ExecutionContext,
- crypto: Crypto, log: Logger, stats: Stats, time: TimeProvider) extends ServiceTransport {
-
- protected implicit val materializer = ActorMaterializer()(actorSystem)
- protected implicit val execution = executionContext
-
- def sendRequest(authToken: AuthToken)(requestStub: HttpRequest): Future[Unmarshal[ResponseEntity]] = {
-
- val requestTime = time.currentTime()
- val encryptionFlow = Flow[ByteString] map { bytes =>
- ByteString(crypto.encrypt(crypto.keyForToken(authToken))(bytes.toArray))
- }
- val decryptionFlow = Flow[ByteString] map { bytes =>
- ByteString(crypto.decrypt(crypto.keyForToken(authToken))(bytes.toArray))
- }
-
- val request = requestStub
- .withEntity(requestStub.entity.transformDataBytes(encryptionFlow))
- .withHeaders(
- RawHeader(AuthService.AuthenticationTokenHeader, authToken.value.value))
-
- log.audit(s"Sending to ${request.uri} request $request")
-
- val responseEntity = Http()(actorSystem).singleRequest(request)(materializer) map { response =>
- if(response.status == StatusCodes.NotFound) {
- Unmarshal(HttpEntity.Empty: ResponseEntity)
- } else if(response.status.isFailure()) {
- throw new Exception("Http status is failure " + response.status)
- } else {
- Unmarshal(response.entity.transformDataBytes(decryptionFlow))
- }
- }
-
- responseEntity.onComplete {
- case Success(r) =>
- val responseTime = time.currentTime()
- log.audit(s"Response from ${request.uri} to request $requestStub is successful")
- stats.recordStats(Seq("request", request.uri.toString, "success"), TimeRange(requestTime, responseTime), 1)
-
- case Failure(t: Throwable) =>
- val responseTime = time.currentTime()
- log.audit(s"Failed to receive response from ${request.uri} to request $requestStub")
- log.error(s"Failed to receive response from ${request.uri} to request $requestStub", t)
- stats.recordStats(Seq("request", request.uri.toString, "fail"), TimeRange(requestTime, responseTime), 1)
- } (executionContext)
-
- responseEntity
- }
- }
-
- import scala.reflect.runtime.universe._
-
- class Swagger(override val host: String,
- version: String,
- override val actorSystem: ActorSystem,
- override val apiTypes: Seq[Type],
- val config: Config) extends SwaggerHttpService with HasActorSystem {
-
- val materializer = ActorMaterializer()(actorSystem)
-
- override val basePath = config.getString("swagger.basePath")
- override val apiDocsPath = config.getString("swagger.docsPath")
-
- override val info = Info(
- config.getString("swagger.apiInfo.description"),
- version,
- config.getString("swagger.apiInfo.title"),
- config.getString("swagger.apiInfo.termsOfServiceUrl"),
- contact = Some(Contact(
- config.getString("swagger.apiInfo.contact.name"),
- config.getString("swagger.apiInfo.contact.url"),
- config.getString("swagger.apiInfo.contact.email")
- )),
- license = Some(License(
- config.getString("swagger.apiInfo.license"),
- config.getString("swagger.apiInfo.licenseUrl")
- )),
- vendorExtensions = Map.empty[String, AnyRef])
-
- def swaggerUI = get {
- pathPrefix("") {
- pathEndOrSingleSlash {
- getFromResource("swagger-ui/index.html")
- }
- } ~ getFromResourceDirectory("swagger-ui")
- }
- }
-}
diff --git a/src/main/scala/com/drivergrp/core/stats.scala b/src/main/scala/com/drivergrp/core/stats.scala
deleted file mode 100644
index cd77f7a..0000000
--- a/src/main/scala/com/drivergrp/core/stats.scala
+++ /dev/null
@@ -1,43 +0,0 @@
-package com.drivergrp.core
-
-import com.drivergrp.core.logging.Logger
-import com.drivergrp.core.time.{Time, TimeRange}
-
-object stats {
-
- type StatsKey = String
- type StatsKeys = Seq[StatsKey]
-
- trait Stats {
-
- def recordStats(keys: StatsKeys, interval: TimeRange, value: BigDecimal): Unit
-
- def recordStats(keys: StatsKeys, interval: TimeRange, value: Int): Unit =
- recordStats(keys, interval, BigDecimal(value))
-
- def recordStats(key: StatsKey, interval: TimeRange, value: BigDecimal): Unit =
- recordStats(Vector(key), interval, value)
-
- def recordStats(key: StatsKey, interval: TimeRange, value: Int): Unit =
- recordStats(Vector(key), interval, BigDecimal(value))
-
- def recordStats(keys: StatsKeys, time: Time, value: BigDecimal): Unit =
- recordStats(keys, TimeRange(time, time), value)
-
- def recordStats(keys: StatsKeys, time: Time, value: Int): Unit =
- recordStats(keys, TimeRange(time, time), BigDecimal(value))
-
- def recordStats(key: StatsKey, time: Time, value: BigDecimal): Unit =
- recordStats(Vector(key), TimeRange(time, time), value)
-
- def recordStats(key: StatsKey, time: Time, value: Int): Unit =
- recordStats(Vector(key), TimeRange(time, time), BigDecimal(value))
- }
-
- class LogStats(log: Logger) extends Stats {
- def recordStats(keys: StatsKeys, interval: TimeRange, value: BigDecimal): Unit = {
- val valueString = value.bigDecimal.toPlainString
- log.audit(s"${keys.mkString(".")}(${interval.start.millis}-${interval.end.millis})=$valueString")
- }
- }
-}
diff --git a/src/main/scala/com/drivergrp/core/time.scala b/src/main/scala/com/drivergrp/core/time.scala
deleted file mode 100644
index b935713..0000000
--- a/src/main/scala/com/drivergrp/core/time.scala
+++ /dev/null
@@ -1,75 +0,0 @@
-package com.drivergrp.core
-
-import java.text.SimpleDateFormat
-import java.util._
-
-import scala.concurrent.duration._
-
-object time {
-
- // The most useful time units
- val Second = 1000L
- val Seconds = Second
- val Minute = 60 * Seconds
- val Minutes = Minute
- val Hour = 60 * Minutes
- val Hours = Hour
- val Day = 24 * Hours
- val Days = Day
- val Week = 7 * Days
- val Weeks = Week
-
- final case class Time(millis: Long) extends AnyVal {
-
- def isBefore(anotherTime: Time): Boolean = implicitly[Ordering[Time]].lt(this, anotherTime)
-
- def isAfter(anotherTime: Time): Boolean = implicitly[Ordering[Time]].gt(this, anotherTime)
-
- def advanceBy(duration: Duration): Time = Time(millis + duration.toMillis)
- }
-
- object Time {
-
- implicit def timeOrdering: Ordering[Time] = Ordering.by(_.millis)
- }
-
- final case class TimeRange(start: Time, end: Time) {
- def duration: Duration = FiniteDuration(end.millis - start.millis, MILLISECONDS)
- }
-
- def startOfMonth(time: Time) = {
- Time(make(new GregorianCalendar()) { cal =>
- cal.setTime(new Date(time.millis))
- cal.set(Calendar.DAY_OF_MONTH, cal.getActualMinimum(Calendar.DAY_OF_MONTH))
- }.getTime.getTime)
- }
-
- def textualDate(timezone: TimeZone)(time: Time): String =
- make(new SimpleDateFormat("MMMM d, yyyy"))(_.setTimeZone(timezone)).format(new Date(time.millis))
-
- def textualTime(timezone: TimeZone)(time: Time): String =
- make(new SimpleDateFormat("MMM dd, yyyy hh:mm:ss a"))(_.setTimeZone(timezone)).format(new Date(time.millis))
-
- object provider {
-
- /**
- * Time providers are supplying code with current times
- * and are extremely useful for testing to check how system is going
- * to behave at specific moments in time.
- *
- * All the calls to receive current time must be made using time
- * provider injected to the caller.
- */
- trait TimeProvider {
- def currentTime(): Time
- }
-
- final class SystemTimeProvider extends TimeProvider {
- def currentTime() = Time(System.currentTimeMillis())
- }
-
- final class SpecificTimeProvider(time: Time) extends TimeProvider {
- def currentTime() = time
- }
- }
-}