Forked from anxious-coder-lhs/employee-controller.scala
Last active
September 29, 2018 21:51
-
-
Save sentenza/8e8acccb07192fed4c93c271d9bfb41c to your computer and use it in GitHub Desktop.
Akka HTTP Employee RESTful APIs.
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
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.") | |
} | |
} | |
} | |
} | |
} | |
} | |
} |
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
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) | |
} |
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
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 | |
} |
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
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