我正在使用Scala,akka-http,json4s构建一个微服务。还为我的业务bean类使用case类。我的案例类有scala Enumerations(我研究了scala Enums并了解了这些限制,但这完全适合我目前的用例)。
有了这个背景,当我尝试创建服务时,我无法理解在哪里定义
implicit val formats = DefaultFormats + new EnumNameSerializer(_ProfessionClaType) + new EnumNameSerializer(_LinkRelType)
以下是我粗略的scala类结构:
import akka.actor.ActorSystem
import akka.event.{Logging, LoggingAdapter}
import akka.http.scaladsl.Http
import akka.http.scaladsl.marshalling.ToResponseMarshallable
import akka.http.scaladsl.model.StatusCodes._
import akka.http.scaladsl.server.Directives
import akka.stream.ActorMaterializer
import com.typesafe.config.{Config, ConfigFactory}
import de.heikoseeberger.akkahttpjson4s.Json4sSupport
import gremlin.scala.ScalaVertex
import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedVertex
import org.json4s.ext.EnumNameSerializer
import org.json4s.{DefaultFormats, jackson}
import scala.concurrent.{ExecutionContext, ExecutionContextExecutor, Future}
trait Service {
implicit val system: ActorSystem
implicit def executor: ExecutionContextExecutor
implicit val materializer: ActorMaterializer
// ?? implicit val formats = DefaultFormats + new EnumNameSerializer(_ProfessionClaType) + new EnumNameSerializer(_LinkRelType)
lazy val client = TitanConnection.cluster.connect();
def config: Config
val logger: LoggingAdapter
def addPerson(p: Person): Future[Either[String, Person]] = {
try {
//Code to add person to database
val resultPerson = Person(propertyMap)
Future.successful(Right(resultPerson))
} catch {
case e :Exception => e.printStackTrace
Future.failed(new Exception("Person can't be created"))
}
}
def fetchPerson(pId: String): Future[Either[Error, Person]] = {
try {
//Code to fetch person object from database
result = results.one() //fetches the first record from results, if it exists
//Following is code for validation of the data
if(result.isNull)
Future.successful(Left(new Error("FAILED","","",s"""There is no person with requested personID:$pId""")))
else {
//Code to retrieve person and return the same as object
Future.successful(Right(resultPerson))
}
} catch {
case e :Exception => e.printStackTrace
Future.successful(Left(new Error("FATAL","","",s"""There is some exception while retrieving person with requested personID:$pId""")))
}
}
/** This is the list of operations possible on the person business entity.
*
* @param ec
* @return
*/
def routes(implicit ec: ExecutionContext) = {
import Directives._
import Json4sSupport._
implicit val serialization = jackson.Serialization // or native.Serialization
implicit val system = ActorSystem()
implicit val materializer = ActorMaterializer()
implicit val formats = DefaultFormats + new EnumNameSerializer(_ProfessionClaType) + new EnumNameSerializer(_LinkRelType)
logRequestResult("PersonMicroservice") {
pathPrefix("person") {
(get & path(Segment)) { personId =>
implicit val formats = DefaultFormats + new EnumNameSerializer(_ProfessionClaType) + new EnumNameSerializer(_LinkRelType)
complete {
fetchPerson(personId).map[ToResponseMarshallable] {
case Right(personFormat) => personFormat
case Left(errorMessage) => BadRequest -> errorMessage
}
}
}~
post { entity(as[Person]) { entity =>
implicit val formats = DefaultFormats + new EnumNameSerializer(_ProfessionClaType) + new EnumNameSerializer(_LinkRelType)
complete {
addPerson(entity).map[ToResponseMarshallable] {
case Right(personFormat) => personFormat
case Left(error) => BadRequest -> error
}
}
}
}
}
}
}
}
/** Microservice for "Person" business entity. This microservice shall handle the basic CRUD related operations
* of Person business entity.
*/
object PersonMicroService extends App with Service {
override implicit val system = ActorSystem()
override implicit val executor = system.dispatcher
override implicit val materializer = ActorMaterializer()
override val config = ConfigFactory.load()
override val logger = Logging(system, getClass)
Http().bindAndHandle(routes , config.getString("http.interface"), config.getInt("http.port"))
}
我还有一个ScalaTest规范用于单元测试服务,我也被迫在每个测试用例中定义formats
。不确定我是否做得对。因此寻求专家意见。
以下是我的测试规范:
包in.niftyride.unit
import akka.event.{Logging, LoggingAdapter}
import akka.http.scaladsl.server.Directives
import de.heikoseeberger.akkahttpjson4s.Json4sSupport
import org.json4s.{jackson, DefaultFormats}
import org.json4s.ext.EnumNameSerializer
import akka.http.scaladsl.model.ContentTypes._
import akka.http.scaladsl.model.StatusCodes._
import Json4sSupport._
class PersonEndpointSpec extends UnitServiceSpec{
override def testConfigSource = "akka.loglevel = WARNING"
override def config = testConfig
override lazy val client = TestDatabaseProvider.cluster.connect;
val logger: LoggingAdapter = Logging(system, this.getClass)
System.setProperty(DatabaseUtils.SERVER_HASH_TEXT, DatabaseUtils.RANDOM_HASH)
"Service" should "respond to single id query" in {
implicit val serialization = jackson.Serialization // or native.Serialization
implicit val formats = DefaultFormats + new EnumNameSerializer(_ProfessionClaType) + new EnumNameSerializer(_LinkRelType)
Get(s"/person/${PersonTestData.personId1}") ~> routes ~> check {
status shouldBe OK
contentType shouldBe `application/json`
responseAs[Person] shouldBe PersonTestData.minData1
}
}
it should "be possible to create a person with valid data through POST" in {
implicit val serialization = jackson.Serialization // or native.Serialization
implicit val formats = DefaultFormats + new EnumNameSerializer(_ProfessionClaType) + new EnumNameSerializer(_LinkRelType)
Post(s"/person", PersonTestData.minDataEmptyPersonId1) ~> routes ~> check {
status shouldBe OK
contentType shouldBe `application/json`
responseAs[Person] shouldBe PersonTestData.minData1
}
}
}