Skip to content

Instantly share code, notes, and snippets.

@anxious-coder-lhs
Last active September 29, 2018 21:51
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save anxious-coder-lhs/1b4bb26bd097548de0ce60a6959bce01 to your computer and use it in GitHub Desktop.
Save anxious-coder-lhs/1b4bb26bd097548de0ce60a6959bce01 to your computer and use it in GitHub Desktop.
Akka HTTP Employee RESTful APIs.
package com.codersbistro.controllers
import akka.actor.ActorSystem
import akka.event.Logging
import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport
import akka.http.scaladsl.model.{StatusCodes}
import akka.http.scaladsl.server.Route
import akka.http.scaladsl.server.Directives._
import com.codersbistro.controllers.EmployeeController.QueryEmployee
import com.codersbistro.repository.EmployeeRepository
import com.codersbistro.repository.EmployeeRepository.{Address, Employee}
import spray.json.DefaultJsonProtocol
import scala.util.{Failure, Success}
object EmployeeController {
case class QueryEmployee(id: String,
firstName: String,
lastName: String)
object EmployeeJsonProtocol extends SprayJsonSupport with DefaultJsonProtocol {
implicit val addressFormat = jsonFormat5(Address.apply)
implicit val employeeFormat = jsonFormat5(Employee.apply)
implicit val employeeQueryFormat = jsonFormat3(QueryEmployee.apply)
}
}
trait EmployeeController
extends EmployeeRepository {
/**
* The Actor system to be used by the Future Context.
*
* @return
*/
implicit def actorSystem: ActorSystem
/**
* Logging using the actor system.
*/
lazy val logger = Logging(actorSystem, classOf[EmployeeController])
import EmployeeRepository._
import com.codersbistro.controllers.EmployeeController.EmployeeJsonProtocol._
/**
* Employee Routes for the GET/POST/Other REST endpoints for the Employee endpoints.
*/
lazy val employeeRoutes: Route = pathPrefix("employee") {
get {
path(Segment) { id =>
onComplete(getEmployeeById(id)) {
_ match {
case Success(employee) =>
logger.info(s"Got the employee records given the employee id ${id}")
complete(StatusCodes.OK, employee)
case Failure(throwable) =>
logger.error(s"Failed to get the employee record given the employee id ${id}")
throwable match {
case e: EmployeeNotFoundException => complete(StatusCodes.NotFound, "No employee found")
case e: DubiousEmployeeRecordsException => complete(StatusCodes.NotFound, "Dubious records found.")
case _ => complete(StatusCodes.InternalServerError, "Failed to get the employees.")
}
}
}
}
} ~ post {
path("query") {
entity(as[QueryEmployee]) { q =>
onComplete(queryEmployee(q.id, q.firstName, q.lastName)) {
_ match {
case Success(employees) =>
logger.info("Got the employee records for the search query.")
complete(StatusCodes.OK, employees)
case Failure(throwable) =>
logger.error("Failed to get the employees with the given query condition.")
complete(StatusCodes.InternalServerError, "Failed to query the employees.")
}
}
}
}
}
}
}
package com.codersbistro
import akka.actor.ActorSystem
import akka.http.scaladsl.Http
import akka.http.scaladsl.server.Route
import akka.stream.ActorMaterializer
import akka.http.scaladsl.server.Directives._
import com.codersbistro.controllers.EmployeeController
import scala.concurrent.Await
import scala.concurrent.duration.Duration
object Server extends App with EmployeeController {
implicit val actorSystem = ActorSystem("AkkaHTTPExampleServices")
implicit val materializer = ActorMaterializer()
lazy val apiRoutes: Route = pathPrefix("api") {
employeeRoutes
}
Http().bindAndHandle(apiRoutes, "localhost", 8080)
logger.info("Starting the HTTP server at 8080")
Await.result(actorSystem.whenTerminated, Duration.Inf)
}
package com.codersbistro.repository
import akka.actor.ActorSystem
object RepositoryContext {
lazy val actorSystem = ActorSystem("RepositoryContext")
lazy val scheduler = actorSystem.scheduler
implicit lazy val executionContext = actorSystem.dispatcher
}
package com.codersbistro.repository
import scala.concurrent.Future
object EmployeeRepository {
case class Employee(id: String,
firstName: String,
lastName: String,
active: Boolean,
address: Address)
case class Address(line1: String,
line2: String,
city: String,
state: String,
zipCode: String)
val EmployeeDB = Seq(
Employee("100", "Lala", "Lee", true, Address("2234", "Cambridge Street", "Torronto", "Torronto", "2414132")),
Employee("101", "Nancy", "Argan", true, Address("35", "Waterloo Park", "Niaora", "Nigeria", "546465")),
Employee("102", "Manilla", "Neptune", true, Address("22", "Bakers Street", "Gurgaon", "Haryana", "21341324")),
Employee("103", "Neeru", "Andrew", true, Address("3463", "St. Peters Road", "Rocky Hill", "Connecticut", "456546")),
Employee("104", "Michael", "Nicholas", true, Address("56756", "Aurbhindo Marg", "Minnesota", "Minnetonka", "45242")),
Employee("105", "Sam", "Montroe", true, Address("12312", "Ethens Street", "Delhi", "Delhi", "235353")),
Employee("106", "Mila", "Hanson", true, Address("432", "Bridge Road", "San jose", "San Fransico", "3434534")),
Employee("106", "Manila", "Winston", false, Address("432", "Bridge Road", "San jose", "San Fransico", "3434534"))
)
class EmployeeNotFoundException extends Throwable("No employee found in the database.")
class DubiousEmployeeRecordsException extends Throwable("Dubious Employee records found given the Employee ID.")
}
trait EmployeeRepository {
import EmployeeRepository._
import akka.pattern.after
import scala.concurrent.duration._
import com.codersbistro.repository.RepositoryContext._
/**
* Fetch the employee records with a mocked delay to synthesize transaction delays.
*/
def fetchDBWithDelay(): Future[Seq[Employee]] = {
val randomDuration = (Math.random() * 5 + 3).toInt.seconds
after(randomDuration, scheduler) {
Future {
EmployeeDB
}
}
}
/**
* Get the employee details given the Employee Id.
*
* @param id
* @return
*/
def getEmployeeById(id: String) = fetchDBWithDelay().map { db =>
val data = db.filter(_.id == id)
if (data.isEmpty)
throw new EmployeeNotFoundException
else if (data.length > 1)
throw new DubiousEmployeeRecordsException
else
data(0)
}
/**
* Query the employee repository with the given query condition.
*
* @param id
* @param firstName
* @param lastName
* @return
*/
def queryEmployee(id: String, firstName: String, lastName: String): Future[Seq[Employee]] = {
fetchDBWithDelay().map { db =>
db.filter { elem =>
elem.id == id && elem.firstName == firstName && elem.lastName == lastName
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment