forked from DanielaSfregola/quiz-management-service
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[tutorial-4] Basic structure for the tutorial on spray magnet pattern…
… and location header generation
- Loading branch information
1 parent
601bc54
commit 65ad49c
Showing
12 changed files
with
271 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
tutorial-4/project/project/target |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
enablePlugins(JavaServerAppPackaging) | ||
|
||
name := "quiz-management-service" | ||
|
||
version := "0.1" | ||
|
||
organization := "com.danielasfregola" | ||
|
||
scalaVersion := "2.11.5" | ||
|
||
resolvers ++= Seq("Typesafe Repository" at "http://repo.typesafe.com/typesafe/releases/", | ||
"Spray Repository" at "http://repo.spray.io") | ||
|
||
libraryDependencies ++= { | ||
val AkkaVersion = "2.3.9" | ||
val SprayVersion = "1.3.2" | ||
val Json4sVersion = "3.2.11" | ||
Seq( | ||
"com.typesafe.akka" %% "akka-actor" % AkkaVersion, | ||
"io.spray" %% "spray-can" % SprayVersion, | ||
"io.spray" %% "spray-routing" % SprayVersion, | ||
"io.spray" %% "spray-json" % "1.3.1", | ||
"com.typesafe.akka" %% "akka-slf4j" % AkkaVersion, | ||
"ch.qos.logback" % "logback-classic" % "1.1.2", | ||
"org.json4s" %% "json4s-native" % Json4sVersion, | ||
"org.json4s" %% "json4s-ext" % Json4sVersion, | ||
"com.typesafe.akka" %% "akka-testkit" % AkkaVersion % "test", | ||
"io.spray" %% "spray-testkit" % SprayVersion % "test", | ||
"org.specs2" %% "specs2" % "2.3.13" % "test" | ||
) | ||
} | ||
|
||
// Assembly settings | ||
mainClass in Global := Some("com.danielasfregola.quiz.management.Main") | ||
|
||
jarName in assembly := "quiz-management-server.jar" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
sbt.version=0.13.6 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
resolvers += Classpaths.typesafeResolver | ||
|
||
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.12.0") | ||
|
||
addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "1.0.0-M4") | ||
|
||
addSbtPlugin("com.typesafe.sbt" % "sbt-multi-jvm" % "0.3.9") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
akka { | ||
loglevel = INFO | ||
stdout-loglevel = INFO | ||
loggers = ["akka.event.slf4j.Slf4jLogger"] | ||
default-dispatcher { | ||
fork-join-executor { | ||
parallelism-min = 8 | ||
} | ||
} | ||
test { | ||
timefactor = 1 | ||
} | ||
} | ||
|
||
spray { | ||
can { | ||
server { | ||
server-header = "Quiz Management Service REST API" | ||
} | ||
} | ||
} | ||
|
||
http { | ||
host = "0.0.0.0" | ||
host = ${?HOST} | ||
port = 5000 | ||
port = ${?PORT} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<configuration> | ||
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> | ||
<target>System.out</target> | ||
<encoder> | ||
<!--<pattern>%date{MM/dd HH:mm:ss} %-6level[%logger{0}]: %msg%n</pattern>--> | ||
<pattern>%-6level[%logger{0}]: %msg%n</pattern> | ||
</encoder> | ||
</appender> | ||
|
||
<logger name="akka" level="INFO" /> | ||
<logger name="spray" level="INFO" /> | ||
|
||
<logger name="com.danielasfregola" level="INFO" /> | ||
|
||
<root level="INFO"> | ||
<appender-ref ref="CONSOLE"/> | ||
</root> | ||
</configuration> |
33 changes: 33 additions & 0 deletions
33
tutorial-4/src/main/scala/com/danielasfregola/quiz/management/Main.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
package com.danielasfregola.quiz.management | ||
|
||
import akka.actor._ | ||
import akka.io.IO | ||
import akka.pattern.ask | ||
import akka.util.Timeout | ||
import com.typesafe.config.ConfigFactory | ||
import spray.can.Http | ||
|
||
import scala.concurrent.duration._ | ||
|
||
object Main extends App { | ||
val config = ConfigFactory.load() | ||
val host = config.getString("http.host") | ||
val port = config.getInt("http.port") | ||
|
||
implicit val system = ActorSystem("quiz-management-service") | ||
implicit val executionContext = system.dispatcher | ||
implicit val timeout = Timeout(10 seconds) | ||
|
||
val api = system.actorOf(Props(new RestInterface)) | ||
|
||
IO(Http).ask(Http.Bind(listener = api, interface = host, port = port)) | ||
.mapTo[Http.Event] | ||
.map { | ||
case Http.Bound(address) => | ||
println(s"REST interface bound to $address") | ||
case Http.CommandFailed(cmd) => | ||
println("REST interface could not bind to " + | ||
s"$host:$port, ${cmd.failureMessage}") | ||
system.shutdown() | ||
} | ||
} |
20 changes: 20 additions & 0 deletions
20
tutorial-4/src/main/scala/com/danielasfregola/quiz/management/RestInterface.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package com.danielasfregola.quiz.management | ||
|
||
import com.danielasfregola.quiz.management.resources.QuestionResource | ||
import com.danielasfregola.quiz.management.services.QuestionService | ||
import spray.routing._ | ||
|
||
import scala.concurrent.ExecutionContext | ||
import scala.language.postfixOps | ||
|
||
class RestInterface(implicit val executionContext: ExecutionContext) extends HttpServiceActor with Resources { | ||
|
||
def receive = runRoute(routes) | ||
|
||
val questionService = new QuestionService | ||
|
||
val routes: Route = questionRoutes | ||
|
||
} | ||
|
||
trait Resources extends QuestionResource |
40 changes: 40 additions & 0 deletions
40
...ial-4/src/main/scala/com/danielasfregola/quiz/management/resources/QuestionResource.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
package com.danielasfregola.quiz.management.resources | ||
|
||
import com.danielasfregola.quiz.management.serializers.JsonSupport | ||
import com.danielasfregola.quiz.management.services.{Question, QuestionService, QuestionUpdate} | ||
import spray.routing._ | ||
|
||
import scala.concurrent.ExecutionContext | ||
|
||
trait QuestionResource extends HttpService with JsonSupport { | ||
|
||
implicit val executionContext: ExecutionContext | ||
|
||
val questionService: QuestionService | ||
|
||
def questionRoutes: Route = pathPrefix("questions") { | ||
pathEnd { | ||
post { | ||
entity(as[Question]) { question => | ||
questionService.createQuestion(question) | ||
??? | ||
} | ||
} | ||
} ~ | ||
path(Segment) { id => | ||
get { | ||
complete(questionService.getQuestion(id)) | ||
} ~ | ||
put { | ||
entity(as[QuestionUpdate]) { update => | ||
questionService.updateQuestion(id, update) | ||
??? | ||
} | ||
} | ||
delete { | ||
complete(questionService.deleteQuestion(id)) | ||
} | ||
} | ||
} | ||
|
||
} |
19 changes: 19 additions & 0 deletions
19
.../src/main/scala/com/danielasfregola/quiz/management/serializers/CustomerSerializers.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package com.danielasfregola.quiz.management.serializers | ||
|
||
import java.sql.Timestamp | ||
|
||
import org.json4s.CustomSerializer | ||
import org.json4s.JsonAST.{JInt, JNull} | ||
|
||
object CustomSerializers { | ||
val all = List(CustomTimestampSerializer) | ||
} | ||
|
||
case object CustomTimestampSerializer extends CustomSerializer[Timestamp](format => | ||
({ | ||
case JInt(x) => new Timestamp(x.longValue * 1000) | ||
case JNull => null | ||
}, | ||
{ | ||
case date: Timestamp => JInt(date.getTime / 1000) | ||
})) |
17 changes: 17 additions & 0 deletions
17
tutorial-4/src/main/scala/com/danielasfregola/quiz/management/serializers/JsonSupport.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package com.danielasfregola.quiz.management.serializers | ||
|
||
import java.text.SimpleDateFormat | ||
|
||
import org.json4s.ext.JodaTimeSerializers | ||
import org.json4s.{DefaultFormats, Formats} | ||
import spray.httpx.Json4sSupport | ||
|
||
trait JsonSupport extends Json4sSupport { | ||
|
||
implicit def json4sFormats: Formats = customDateFormat ++ JodaTimeSerializers.all ++ CustomSerializers.all | ||
|
||
val customDateFormat = new DefaultFormats { | ||
override def dateFormatter = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss") | ||
} | ||
|
||
} |
50 changes: 50 additions & 0 deletions
50
tutorial-4/src/main/scala/com/danielasfregola/quiz/management/services/QuestionService.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
package com.danielasfregola.quiz.management.services | ||
|
||
import scala.concurrent.{ExecutionContext, Future} | ||
|
||
case class Question(id: String, title: String, text: String) | ||
|
||
case class QuestionUpdate(title: Option[String], text: Option[String]) | ||
|
||
class QuestionService(implicit executionContext: ExecutionContext) { | ||
|
||
var questions = Vector.empty[Question] | ||
|
||
def createQuestion(question: Question): Future[Option[String]] = Future { | ||
questions.find(_.id == question.id) match { | ||
case Some(q) => None // conflict! id is already taken | ||
case None => | ||
questions = questions :+ question | ||
Some(question.id) | ||
} | ||
} | ||
|
||
def getQuestion(id: String): Future[Option[Question]] = Future { | ||
questions.find(_.id == id) | ||
} | ||
|
||
def updateQuestion(id: String, update: QuestionUpdate): Future[Option[String]] = { | ||
|
||
def updateEntity(question: Question): Question = { | ||
val title = update.title.getOrElse(question.title) | ||
val text = update.text.getOrElse(question.text) | ||
Question(id, title, text) | ||
} | ||
|
||
getQuestion(id).flatMap { maybeQuestion => | ||
maybeQuestion match { | ||
case None => Future { None } // No question found | ||
case Some(question) => | ||
val updatedQuestion = updateEntity(question) | ||
deleteQuestion(id).flatMap(_ => createQuestion(updatedQuestion)) | ||
} | ||
} | ||
} | ||
|
||
def deleteQuestion(id: String): Future[Unit] = Future { | ||
questions.filterNot(_.id == id) | ||
} | ||
|
||
|
||
} | ||
|