diff --git a/src/main/scala/com/fluency03/blockchain/Util.scala b/src/main/scala/com/fluency03/blockchain/Util.scala index 8c02304..8eb9b2a 100644 --- a/src/main/scala/com/fluency03/blockchain/Util.scala +++ b/src/main/scala/com/fluency03/blockchain/Util.scala @@ -2,6 +2,9 @@ package com.fluency03.blockchain import java.security.MessageDigest import java.time.Instant + +import akka.http.scaladsl.model.{StatusCode, StatusCodes} +import com.fluency03.blockchain.api.{FailureMsg, Message, SuccessMsg} import org.bouncycastle.util.encoders.Base64 object Util { @@ -60,4 +63,22 @@ object Util { */ def fromBase64(base64: String): String = new String(Base64.decode(base64), "UTF-8") + /** + * Return either SuccessMsg (if fun successfully returned a String) or FailureMsg (if fun failed). + */ + def failsafeMsg(fun: => String): Message = + try { SuccessMsg(fun) } + catch { + case e: Exception => FailureMsg(e.getMessage) + } + + /** + * Return either SuccessMsg (if fun successfully returned a String) or FailureMsg (if fun failed). + */ + def failsafeResp(fun: => String): (StatusCode, Message) = + try { (StatusCodes.OK, SuccessMsg(fun)) } + catch { + case e: Exception => (StatusCodes.InternalServerError, FailureMsg(e.getMessage)) + } + } diff --git a/src/main/scala/com/fluency03/blockchain/api/Message.scala b/src/main/scala/com/fluency03/blockchain/api/Message.scala index 010bc45..009208a 100644 --- a/src/main/scala/com/fluency03/blockchain/api/Message.scala +++ b/src/main/scala/com/fluency03/blockchain/api/Message.scala @@ -3,5 +3,5 @@ package com.fluency03.blockchain.api final case class Input(content: String) sealed trait Message -final case class SuccessMsg(content: String) extends Message -final case class FailureMsg(content: String) extends Message +final case class SuccessMsg(message: String) extends Message +final case class FailureMsg(error: String) extends Message diff --git a/src/main/scala/com/fluency03/blockchain/api/package.scala b/src/main/scala/com/fluency03/blockchain/api/package.scala index 2b57685..6fdf96d 100644 --- a/src/main/scala/com/fluency03/blockchain/api/package.scala +++ b/src/main/scala/com/fluency03/blockchain/api/package.scala @@ -16,4 +16,7 @@ package object api { val PARENT_UP = "../" + + + } diff --git a/src/main/scala/com/fluency03/blockchain/api/routes/BlockRoutes.scala b/src/main/scala/com/fluency03/blockchain/api/routes/BlockRoutes.scala index 66c8080..f504c22 100644 --- a/src/main/scala/com/fluency03/blockchain/api/routes/BlockRoutes.scala +++ b/src/main/scala/com/fluency03/blockchain/api/routes/BlockRoutes.scala @@ -33,8 +33,8 @@ trait BlockRoutes extends RoutesSupport { entity(as[Block]) { block => val blockCreated: Future[Message] = (blocksActor ? CreateBlock(block)).mapTo[Message] onSuccess(blockCreated) { - case SuccessMsg(content) => complete((StatusCodes.Created, content)) - case FailureMsg(content) => complete((StatusCodes.Conflict, content)) + case s: SuccessMsg => complete((StatusCodes.Created, s)) + case f: FailureMsg => complete((StatusCodes.Conflict, f)) } } } @@ -47,8 +47,8 @@ trait BlockRoutes extends RoutesSupport { delete { val blockDeleted: Future[Message] = (blocksActor ? DeleteBlock(hash)).mapTo[Message] onSuccess(blockDeleted) { - case SuccessMsg(content) => complete((StatusCodes.OK, content)) - case FailureMsg(content) => complete((StatusCodes.NotFound, content)) + case s: SuccessMsg => complete((StatusCodes.OK, s)) + case f: FailureMsg => complete((StatusCodes.NotFound, f)) } } } diff --git a/src/main/scala/com/fluency03/blockchain/api/routes/BlockchainRoutes.scala b/src/main/scala/com/fluency03/blockchain/api/routes/BlockchainRoutes.scala index 7da7ad7..753f7a6 100644 --- a/src/main/scala/com/fluency03/blockchain/api/routes/BlockchainRoutes.scala +++ b/src/main/scala/com/fluency03/blockchain/api/routes/BlockchainRoutes.scala @@ -31,16 +31,16 @@ trait BlockchainRoutes extends RoutesSupport { entity(as[JValue]) { _ => val blockchainCreated: Future[Message] = (blockchainActor ? CreateBlockchain).mapTo[Message] onSuccess(blockchainCreated) { - case SuccessMsg(content) => complete((StatusCodes.Created, content)) - case FailureMsg(content) => complete((StatusCodes.Conflict, content)) + case s: SuccessMsg => complete((StatusCodes.Created, s)) + case f: FailureMsg => complete((StatusCodes.Conflict, f)) } } } ~ delete { val blockchainDeleted: Future[Message] = (blockchainActor ? DeleteBlockchain).mapTo[Message] onSuccess(blockchainDeleted) { - case SuccessMsg(content) => complete((StatusCodes.OK, content)) - case FailureMsg(content) => complete((StatusCodes.NotFound, content)) + case s: SuccessMsg => complete((StatusCodes.OK, s)) + case f: FailureMsg => complete((StatusCodes.NotFound, f)) } } } diff --git a/src/main/scala/com/fluency03/blockchain/api/routes/GenericRoutes.scala b/src/main/scala/com/fluency03/blockchain/api/routes/GenericRoutes.scala index a0a87d7..5001766 100644 --- a/src/main/scala/com/fluency03/blockchain/api/routes/GenericRoutes.scala +++ b/src/main/scala/com/fluency03/blockchain/api/routes/GenericRoutes.scala @@ -10,43 +10,41 @@ import akka.http.scaladsl.server.Route import akka.http.scaladsl.server.directives.MethodDirectives.post import akka.http.scaladsl.server.directives.RouteDirectives.complete import com.fluency03.blockchain.Util._ -import com.fluency03.blockchain.api.Input +import com.fluency03.blockchain.api.{FailureMsg, Input, SuccessMsg} trait GenericRoutes extends RoutesSupport { lazy val log = Logging(system, classOf[GenericRoutes]) - - lazy val genericRoutes: Route = pathSingleSlash { get { - complete("Welcome to Blockchain in Scala!") + complete(SuccessMsg(SLOGAN)) } } ~ - pathPrefix("generic") { - path("toSha256") { + pathPrefix(GENERIC) { + path(TO_SHA256) { post { - entity(as[Input]) { in => complete((StatusCodes.Created, in.content.toSha256)) } + entity(as[Input]) { in => complete( failsafeResp { in.content.toSha256 } ) } } } ~ - path("toBase64") { + path(TO_BASE64) { post { - entity(as[Input]) { in => complete((StatusCodes.Created, in.content.toBase64)) } + entity(as[Input]) { in => complete( failsafeResp { in.content.toBase64 } ) } } } ~ - path("fromBase64") { + path(FROM_BASE64) { post { - entity(as[Input]) { in => complete((StatusCodes.Created, fromBase64(in.content))) } + entity(as[Input]) { in => complete( failsafeResp { fromBase64(in.content) } ) } } } ~ - path("epoch-time") { + path(TO_EPOCH_TIME) { post { - entity(as[Input]) { in => complete((StatusCodes.Created, epochTimeOf(in.content))) } + entity(as[Input]) { in => complete( failsafeResp { epochTimeOf(in.content).toString } ) } } } ~ - path("time-of-epoch") { + path(TIME_FROM_EPOCH) { post { - entity(as[Input]) { in => complete((StatusCodes.Created, Instant.ofEpochSecond(in.content.toLong))) } + entity(as[Input]) { in => complete( failsafeResp { Instant.ofEpochSecond(in.content.toLong).toString } ) } } } } diff --git a/src/main/scala/com/fluency03/blockchain/api/routes/NetworkRoutes.scala b/src/main/scala/com/fluency03/blockchain/api/routes/NetworkRoutes.scala index 9e0d9ff..e885914 100644 --- a/src/main/scala/com/fluency03/blockchain/api/routes/NetworkRoutes.scala +++ b/src/main/scala/com/fluency03/blockchain/api/routes/NetworkRoutes.scala @@ -33,8 +33,8 @@ trait NetworkRoutes extends RoutesSupport { entity(as[PeerSimple]) { peer => val peerCreated: Future[Message] = (networkActor ? CreatePeer(peer.name)).mapTo[Message] onSuccess(peerCreated) { - case SuccessMsg(content) => complete((StatusCodes.Created, content)) - case FailureMsg(content) => complete((StatusCodes.Conflict, content)) + case s: SuccessMsg => complete((StatusCodes.Created, s)) + case f: FailureMsg => complete((StatusCodes.Conflict, f)) } } } @@ -47,8 +47,8 @@ trait NetworkRoutes extends RoutesSupport { delete { val peerDeleted: Future[Message] = (networkActor ? DeletePeer(name)).mapTo[Message] onSuccess(peerDeleted) { - case SuccessMsg(content) => complete((StatusCodes.OK, content)) - case FailureMsg(content) => complete((StatusCodes.NotFound, content)) + case s: SuccessMsg => complete((StatusCodes.OK, s)) + case f: FailureMsg => complete((StatusCodes.NotFound, f)) } } } diff --git a/src/main/scala/com/fluency03/blockchain/api/routes/TransactionRoutes.scala b/src/main/scala/com/fluency03/blockchain/api/routes/TransactionRoutes.scala index a9dc8c0..64e8553 100644 --- a/src/main/scala/com/fluency03/blockchain/api/routes/TransactionRoutes.scala +++ b/src/main/scala/com/fluency03/blockchain/api/routes/TransactionRoutes.scala @@ -33,8 +33,8 @@ trait TransactionRoutes extends RoutesSupport { entity(as[Transaction]) { tx => val txCreated: Future[Message] = (transActor ? CreateTransaction(tx)).mapTo[Message] onSuccess(txCreated) { - case SuccessMsg(content) => complete((StatusCodes.Created, content)) - case FailureMsg(content) => complete((StatusCodes.Conflict, content)) + case s: SuccessMsg => complete((StatusCodes.Created, s)) + case f: FailureMsg => complete((StatusCodes.Conflict, f)) } } } @@ -47,8 +47,8 @@ trait TransactionRoutes extends RoutesSupport { delete { val txDeleted: Future[Message] = (transActor ? DeleteTransaction(id)).mapTo[Message] onSuccess(txDeleted) { - case SuccessMsg(content) => complete((StatusCodes.OK, content)) - case FailureMsg(content) => complete((StatusCodes.NotFound, content)) + case s: SuccessMsg => complete((StatusCodes.OK, s)) + case f: FailureMsg => complete((StatusCodes.NotFound, f)) } } } diff --git a/src/main/scala/com/fluency03/blockchain/api/routes/package.scala b/src/main/scala/com/fluency03/blockchain/api/routes/package.scala new file mode 100644 index 0000000..18ac220 --- /dev/null +++ b/src/main/scala/com/fluency03/blockchain/api/routes/package.scala @@ -0,0 +1,16 @@ +package com.fluency03.blockchain.api + +package object routes { + + // generics + val SLASH = "/" + val GENERIC = "generic" + val TO_SHA256 = "to-sha256" + val TO_BASE64 = "to-base64" + val FROM_BASE64 = "from-base64" + val TO_EPOCH_TIME = "to-epoch-time" + val TIME_FROM_EPOCH = "time-from-epoch" + + def pathOf(seg: String*): String = SLASH + seg.mkString(SLASH) + +} diff --git a/src/test/scala/com/fluency03/blockchain/api/MessageTest.scala b/src/test/scala/com/fluency03/blockchain/api/MessageTest.scala index e8de957..fc6c8db 100644 --- a/src/test/scala/com/fluency03/blockchain/api/MessageTest.scala +++ b/src/test/scala/com/fluency03/blockchain/api/MessageTest.scala @@ -6,7 +6,7 @@ class MessageTest extends FlatSpec with Matchers { "A Message" should "contain valid content." in { SuccessMsg("Response test.") shouldBe a[Message] - SuccessMsg("Response test.").content shouldEqual "Response test." + SuccessMsg("Response test.").message shouldEqual "Response test." } "A Input" should "contain valid content." in { @@ -15,7 +15,7 @@ class MessageTest extends FlatSpec with Matchers { "A Fail" should "contain valid content." in { FailureMsg("Error") shouldBe a[Message] - FailureMsg("Error").content shouldEqual "Error" + FailureMsg("Error").error shouldEqual "Error" } } diff --git a/src/test/scala/com/fluency03/blockchain/api/routes/BlockRoutesTest.scala b/src/test/scala/com/fluency03/blockchain/api/routes/BlockRoutesTest.scala index 1496b08..486af66 100644 --- a/src/test/scala/com/fluency03/blockchain/api/routes/BlockRoutesTest.scala +++ b/src/test/scala/com/fluency03/blockchain/api/routes/BlockRoutesTest.scala @@ -1,5 +1,8 @@ package com.fluency03.blockchain.api.routes -class BlockRoutesTest { +import akka.http.scaladsl.testkit.ScalatestRouteTest +import org.scalatest.{Matchers, WordSpec} + +class BlockRoutesTest extends WordSpec with Matchers with ScalatestRouteTest { } diff --git a/src/test/scala/com/fluency03/blockchain/api/routes/BlockchainRoutesTest.scala b/src/test/scala/com/fluency03/blockchain/api/routes/BlockchainRoutesTest.scala index e4e2a6f..2b8a8a1 100644 --- a/src/test/scala/com/fluency03/blockchain/api/routes/BlockchainRoutesTest.scala +++ b/src/test/scala/com/fluency03/blockchain/api/routes/BlockchainRoutesTest.scala @@ -1,5 +1,12 @@ package com.fluency03.blockchain.api.routes -class BlockchainRoutesTest { +import akka.http.scaladsl.testkit.ScalatestRouteTest +import org.scalatest.{Matchers, WordSpec} + +class BlockchainRoutesTest extends WordSpec with Matchers with ScalatestRouteTest { + + + + } diff --git a/src/test/scala/com/fluency03/blockchain/api/routes/GenericRoutesTest.scala b/src/test/scala/com/fluency03/blockchain/api/routes/GenericRoutesTest.scala index b2c4a9f..7b3e445 100644 --- a/src/test/scala/com/fluency03/blockchain/api/routes/GenericRoutesTest.scala +++ b/src/test/scala/com/fluency03/blockchain/api/routes/GenericRoutesTest.scala @@ -1,5 +1,52 @@ package com.fluency03.blockchain.api.routes -class GenericRoutesTest { +import akka.http.scaladsl.model.StatusCodes +import akka.http.scaladsl.testkit.ScalatestRouteTest +import com.fluency03.blockchain.api.{Input, JsonSupport, SuccessMsg} +import org.scalatest.{Matchers, WordSpec} +import com.fluency03.blockchain.api.Server.genericRoutes + +class GenericRoutesTest extends WordSpec with Matchers with ScalatestRouteTest with JsonSupport { + + "GenericRoutes" should { + "return a greeting for GET requests to the root path." in { + Get() ~> genericRoutes ~> check { + status shouldEqual StatusCodes.OK + responseAs[SuccessMsg] shouldEqual SuccessMsg("Welcome to Blockchain in Scala!") + } + } + + "return corresponding value." in { + Post(pathOf(GENERIC, TO_SHA256), Input("open sesame")) ~> genericRoutes ~> check { + status shouldEqual StatusCodes.OK + responseAs[SuccessMsg] shouldEqual SuccessMsg("41ef4bb0b23661e66301aac36066912dac037827b4ae63a7b1165a5aa93ed4eb") + } + + Post(pathOf(GENERIC, TO_SHA256), Input("")) ~> genericRoutes ~> check { + status shouldEqual StatusCodes.OK + responseAs[SuccessMsg] shouldEqual SuccessMsg("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855") + } + + Post(pathOf(GENERIC, TO_BASE64), Input("open sesame")) ~> genericRoutes ~> check { + status shouldEqual StatusCodes.OK + responseAs[SuccessMsg] shouldEqual SuccessMsg("b3BlbiBzZXNhbWU=") + } + + Post(pathOf(GENERIC, FROM_BASE64), Input("b3BlbiBzZXNhbWU=")) ~> genericRoutes ~> check { + status shouldEqual StatusCodes.OK + responseAs[SuccessMsg] shouldEqual SuccessMsg("open sesame") + } + + Post(pathOf(GENERIC, TO_EPOCH_TIME), Input("2018-04-11T18:52:01Z")) ~> genericRoutes ~> check { + status shouldEqual StatusCodes.OK + responseAs[SuccessMsg] shouldEqual SuccessMsg("1523472721") + } + + Post(pathOf(GENERIC, TIME_FROM_EPOCH), Input("1523472721")) ~> genericRoutes ~> check { + status shouldEqual StatusCodes.OK + responseAs[SuccessMsg] shouldEqual SuccessMsg("2018-04-11T18:52:01Z") + } + } + } } diff --git a/src/test/scala/com/fluency03/blockchain/api/routes/NetworkRoutesTest.scala b/src/test/scala/com/fluency03/blockchain/api/routes/NetworkRoutesTest.scala index ec99e99..f5f013d 100644 --- a/src/test/scala/com/fluency03/blockchain/api/routes/NetworkRoutesTest.scala +++ b/src/test/scala/com/fluency03/blockchain/api/routes/NetworkRoutesTest.scala @@ -1,5 +1,8 @@ package com.fluency03.blockchain.api.routes -class NetworkRoutesTest { +import akka.http.scaladsl.testkit.ScalatestRouteTest +import org.scalatest.{Matchers, WordSpec} + +class NetworkRoutesTest extends WordSpec with Matchers with ScalatestRouteTest { } diff --git a/src/test/scala/com/fluency03/blockchain/api/routes/TransactionRoutesTest.scala b/src/test/scala/com/fluency03/blockchain/api/routes/TransactionRoutesTest.scala index fdaa75d..7788748 100644 --- a/src/test/scala/com/fluency03/blockchain/api/routes/TransactionRoutesTest.scala +++ b/src/test/scala/com/fluency03/blockchain/api/routes/TransactionRoutesTest.scala @@ -1,5 +1,8 @@ package com.fluency03.blockchain.api.routes -class TransactionRoutesTest { +import akka.http.scaladsl.testkit.ScalatestRouteTest +import org.scalatest.{Matchers, WordSpec} + +class TransactionRoutesTest extends WordSpec with Matchers with ScalatestRouteTest { }