Skip to content

Commit

Permalink
Updated to sbt 1.0
Browse files Browse the repository at this point in the history
Updated formatting
  • Loading branch information
pauljamescleary authored and paulcleary committed Oct 9, 2017
1 parent ed5fbb4 commit 9553ce8
Show file tree
Hide file tree
Showing 14 changed files with 53 additions and 84 deletions.
13 changes: 13 additions & 0 deletions .scalafmt.conf
@@ -0,0 +1,13 @@
style = default

maxColumn = 100

align = none

rewrite.rules = [
AvoidInfix
RedundantBraces
RedundantParens
AsciiSortImports
PreferCurlyFors
]
2 changes: 1 addition & 1 deletion project/build.properties
@@ -1 +1 @@
sbt.version=0.13.13
sbt.version=1.0.1
4 changes: 2 additions & 2 deletions project/plugins.sbt
@@ -1,8 +1,8 @@
// Makes our code tidy
addSbtPlugin("com.lucidchart" % "sbt-scalafmt" % "1.12")
addSbtPlugin("com.geirsson" % "sbt-scalafmt" % "1.3.0")

// Revolver allows us to use re-start and work a lot faster!
addSbtPlugin("io.spray" % "sbt-revolver" % "0.9.0")

// Native Packager allows us to create standalone jar
addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "1.2.0")
addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "1.2.2")
17 changes: 4 additions & 13 deletions src/main/scala/io/github/pauljamescleary/petstore/Server.scala
Expand Up @@ -4,10 +4,7 @@ import cats.effect._
import cats.implicits._
import doobie.h2.H2Transactor
import fs2.Stream
import io.github.pauljamescleary.petstore.endpoint.{
OrderEndpoints,
PetEndpoints
}
import io.github.pauljamescleary.petstore.endpoint.{OrderEndpoints, PetEndpoints}
import io.github.pauljamescleary.petstore.repository.{
DoobieOrderRepositoryInterpreter,
DoobiePetRepositoryInterpreter
Expand All @@ -19,18 +16,13 @@ import org.http4s.util.StreamApp

object Server extends StreamApp[IO] {

override def stream(args: List[String],
shutdown: IO[Unit]): Stream[IO, Nothing] = {
override def stream(args: List[String], shutdown: IO[Unit]): Stream[IO, Nothing] =
createStream[IO](args, shutdown).unsafeRunSync()
}

def createStream[F[_]](args: List[String], shutdown: F[Unit])(
implicit E: Effect[F]): F[Stream[F, Nothing]] = {

implicit E: Effect[F]): F[Stream[F, Nothing]] =
for {
xa <- H2Transactor[F]("jdbc:h2:mem:test;MODE=MySQL;DB_CLOSE_DELAY=-1",
"sa",
"")
xa <- H2Transactor[F]("jdbc:h2:mem:test;MODE=MySQL;DB_CLOSE_DELAY=-1", "sa", "")
petRepo = DoobiePetRepositoryInterpreter[F](xa)
orderRepo = DoobieOrderRepositoryInterpreter[F](xa)
_ <- petRepo.migrate
Expand All @@ -48,5 +40,4 @@ object Server extends StreamApp[IO] {
.serve
}

}
}
Expand Up @@ -12,9 +12,8 @@ object JodaDateTime {
}
}

implicit val encodeDateTime: Encoder[DateTime] = Encoder.instance {
dateTime: DateTime =>
Json.fromString(this.toString(dateTime))
implicit val encodeDateTime: Encoder[DateTime] = Encoder.instance { dateTime: DateTime =>
Json.fromString(this.toString(dateTime))
}

def toString(dateTime: DateTime): String = dateTime.toString(dateFormat)
Expand Down
Expand Up @@ -25,7 +25,7 @@ class OrderEndpoints[F[_]: Sync] extends Http4sDsl[F] {
implicit val statusDecoder: Decoder[OrderStatus] = deriveEnumerationDecoder
implicit val statusEncoder: Encoder[OrderStatus] = deriveEnumerationEncoder

def placeOrderEndpoint(orderService: OrderService[F]): HttpService[F] = {
def placeOrderEndpoint(orderService: OrderService[F]): HttpService[F] =
HttpService[F] {
case req @ POST -> Root / "orders" => {
for {
Expand All @@ -35,14 +35,12 @@ class OrderEndpoints[F[_]: Sync] extends Http4sDsl[F] {
} yield resp
}
}
}

def endpoints(orderService: OrderService[F]): HttpService[F] =
placeOrderEndpoint(orderService)
}

object OrderEndpoints {
def endpoints[F[_]: Sync](orderService: OrderService[F]): HttpService[F] = {
def endpoints[F[_]: Sync](orderService: OrderService[F]): HttpService[F] =
new OrderEndpoints[F].endpoints(orderService)
}
}
Expand Up @@ -10,10 +10,7 @@ import io.circe.generic.extras.semiauto._
import io.circe.syntax._
import io.github.pauljamescleary.petstore.model.{Pet, PetStatus}
import io.github.pauljamescleary.petstore.service.PetService
import io.github.pauljamescleary.petstore.validation.{
PetAlreadyExistsError,
PetNotFoundError
}
import io.github.pauljamescleary.petstore.validation.{PetAlreadyExistsError, PetNotFoundError}
import org.http4s.circe._
import org.http4s.dsl.Http4sDsl
import org.http4s.implicits._
Expand All @@ -38,8 +35,7 @@ class PetEndpoints[F[_]: Sync] extends Http4sDsl[F] {
QueryParamDecoder[String].map(PetStatus.apply)

/* Relies on the statusQueryParamDecoder implicit, will parse out a possible multi-value query parameter */
object StatusMatcher
extends OptionalMultiQueryParamDecoderMatcher[PetStatus]("status")
object StatusMatcher extends OptionalMultiQueryParamDecoderMatcher[PetStatus]("status")

/* We need to define an enum encoder and decoder since these do not come out of the box with generic derivation */
implicit val statusDecoder: Decoder[PetStatus] = deriveEnumerationDecoder
Expand All @@ -57,8 +53,7 @@ class PetEndpoints[F[_]: Sync] extends Http4sDsl[F] {
case Right(saved) =>
Ok(saved.asJson)
case Left(PetAlreadyExistsError(existing)) =>
Conflict(
s"The pet ${existing.name} of category ${existing.category} already exists")
Conflict(s"The pet ${existing.name} of category ${existing.category} already exists")
case Left(unexpected) =>
InternalServerError(s"Unexpected error: $unexpected")
}
Expand Down Expand Up @@ -102,45 +97,38 @@ class PetEndpoints[F[_]: Sync] extends Http4sDsl[F] {

private def listPetsEndpoint(petService: PetService[F]): HttpService[F] =
HttpService[F] {
case GET -> Root / "pets" :? PageSizeMatcher(pageSize) :? OffsetMatcher(
offset) =>
case GET -> Root / "pets" :? PageSizeMatcher(pageSize) :? OffsetMatcher(offset) =>
for {
retrieved <- petService.list(pageSize, offset)
resp <- Ok(retrieved.asJson)
} yield resp
}

private def findPetsByStatusEndpoint(
petService: PetService[F]): HttpService[F] =
private def findPetsByStatusEndpoint(petService: PetService[F]): HttpService[F] =
HttpService[F] {
case GET -> Root / "pets" / "findByStatus" :? StatusMatcher(
Valid(Nil)) =>
case GET -> Root / "pets" / "findByStatus" :? StatusMatcher(Valid(Nil)) =>
// User did not specify any statuses
BadRequest("status parameter not specified")

case GET -> Root / "pets" / "findByStatus" :? StatusMatcher(
Valid(statuses)) =>
case GET -> Root / "pets" / "findByStatus" :? StatusMatcher(Valid(statuses)) =>
// We have a list of valid statuses, find them and return
for {
retrieved <- petService.findByStatus(
NonEmptyList.fromListUnsafe(statuses))
retrieved <- petService.findByStatus(NonEmptyList.fromListUnsafe(statuses))
resp <- Ok(retrieved.asJson)
} yield resp
}

def endpoints(petService: PetService[F]): HttpService[F] = {
def endpoints(petService: PetService[F]): HttpService[F] =
createPetEndpoint(petService) <+>
getPetEndpoint(petService) <+>
deletePetEndpoint(petService) <+>
listPetsEndpoint(petService) <+>
findPetsByStatusEndpoint(petService) <+>
updatePetEndpoint(petService)
}

}

object PetEndpoints {
def endpoints[F[_]: Sync](petService: PetService[F]): HttpService[F] = {
def endpoints[F[_]: Sync](petService: PetService[F]): HttpService[F] =
new PetEndpoints[F].endpoints(petService)
}
}
Expand Up @@ -37,9 +37,8 @@ class DoobieOrderRepositoryInterpreter[F[_]: Monad](val xa: Transactor[F])
dt => new java.sql.Timestamp(dt.getMillis)
)

def migrate: F[Int] = {
def migrate: F[Int] =
dropOrdersTable >> createOrdersTable
}

def put(order: Order): F[Order] = {
val insert: ConnectionIO[Order] =
Expand All @@ -50,15 +49,14 @@ class DoobieOrderRepositoryInterpreter[F[_]: Monad](val xa: Transactor[F])
insert.transact(xa)
}

def get(orderId: Long): F[Option[Order]] = {
def get(orderId: Long): F[Option[Order]] =
sql"""
SELECT PET_ID, SHIP_DATE, STATUS, COMPLETE
FROM ORDERS
WHERE ID = $orderId
""".query[Order].option.transact(xa)
}

def delete(orderId: Long): F[Option[Order]] = {
def delete(orderId: Long): F[Option[Order]] =
get(orderId).flatMap {
case Some(order) =>
sql"DELETE FROM ORDERS WHERE ID = $orderId".update.run
Expand All @@ -67,12 +65,9 @@ class DoobieOrderRepositoryInterpreter[F[_]: Monad](val xa: Transactor[F])
case None =>
none[Order].pure[F]
}
}
}

object DoobieOrderRepositoryInterpreter {
def apply[F[_]: Monad](
xa: Transactor[F]): DoobieOrderRepositoryInterpreter[F] = {
def apply[F[_]: Monad](xa: Transactor[F]): DoobieOrderRepositoryInterpreter[F] =
new DoobieOrderRepositoryInterpreter(xa)
}
}
Expand Up @@ -33,9 +33,8 @@ class DoobiePetRepositoryInterpreter[F[_]: Monad](val xa: Transactor[F])
private implicit val SetStringMeta: Meta[Set[String]] = Meta[String]
.xmap(str => str.split(',').toSet, strSet => strSet.mkString(","))

def migrate: F[Int] = {
def migrate: F[Int] =
dropPetTable >> createPetTable
}

def put(pet: Pet): F[Pet] = {
val insert: ConnectionIO[Pet] =
Expand All @@ -46,15 +45,14 @@ class DoobiePetRepositoryInterpreter[F[_]: Monad](val xa: Transactor[F])
insert.transact(xa)
}

def get(id: Long): F[Option[Pet]] = {
def get(id: Long): F[Option[Pet]] =
sql"""
SELECT NAME, CATEGORY, BIO, STATUS, TAGS, PHOTO_URLS, ID
FROM PET
WHERE ID = $id
""".query[Pet].option.transact(xa)
}

def delete(id: Long): F[Option[Pet]] = {
def delete(id: Long): F[Option[Pet]] =
get(id).flatMap {
case Some(pet) =>
sql"DELETE FROM PET WHERE ID = $id".update.run
Expand All @@ -63,23 +61,20 @@ class DoobiePetRepositoryInterpreter[F[_]: Monad](val xa: Transactor[F])
case None =>
none[Pet].pure[F]
}
}

def findByNameAndCategory(name: String, category: String): F[Set[Pet]] = {
def findByNameAndCategory(name: String, category: String): F[Set[Pet]] =
sql"""SELECT NAME, CATEGORY, BIO, STATUS, TAGS, PHOTO_URLS, ID
FROM PET
WHERE NAME = $name AND CATEGORY = $category
""".query[Pet].list.transact(xa).map(_.toSet)
}

def list(pageSize: Int, offset: Int): F[List[Pet]] = {
def list(pageSize: Int, offset: Int): F[List[Pet]] =
sql"""SELECT NAME, CATEGORY, BIO, STATUS, TAGS, PHOTO_URLS, ID
FROM PET
ORDER BY NAME LIMIT $offset,$pageSize"""
.query[Pet]
.list
.transact(xa)
}

override def findByStatus(statuses: NonEmptyList[PetStatus]): F[List[Pet]] = {
val q = sql"""SELECT NAME, CATEGORY, BIO, STATUS, TAGS, PHOTO_URLS, ID
Expand All @@ -91,8 +86,6 @@ class DoobiePetRepositoryInterpreter[F[_]: Monad](val xa: Transactor[F])
}

object DoobiePetRepositoryInterpreter {
def apply[F[_]: Monad](
xa: Transactor[F]): DoobiePetRepositoryInterpreter[F] = {
def apply[F[_]: Monad](xa: Transactor[F]): DoobiePetRepositoryInterpreter[F] =
new DoobiePetRepositoryInterpreter(xa)
}
}
Expand Up @@ -7,8 +7,7 @@ import io.github.pauljamescleary.petstore.model.Order
import scala.collection.concurrent.TrieMap
import scala.util.Random

class OrderRepositoryInMemoryInterpreter[F[_]: Applicative]
extends OrderRepositoryAlgebra[F] {
class OrderRepositoryInMemoryInterpreter[F[_]: Applicative] extends OrderRepositoryAlgebra[F] {

private val cache = new TrieMap[Long, Order]

Expand Down
Expand Up @@ -8,8 +8,7 @@ import io.github.pauljamescleary.petstore.model.{Pet, PetStatus}
import scala.collection.concurrent.TrieMap
import scala.util.Random

class PetRepositoryInMemoryInterpreter[F[_]: Applicative]
extends PetRepositoryAlgebra[F] {
class PetRepositoryInMemoryInterpreter[F[_]: Applicative] extends PetRepositoryAlgebra[F] {

private val cache = new TrieMap[Long, Pet]

Expand Down
Expand Up @@ -19,26 +19,21 @@ import scala.language.higherKinds
* @tparam F - this is the container for the things we work with, could be scala.concurrent.Future, Option, anything
* as long as it is a Monad
*/
class PetService[F[_]](repository: PetRepositoryAlgebra[F],
validation: PetValidationAlgebra[F]) {
class PetService[F[_]](repository: PetRepositoryAlgebra[F], validation: PetValidationAlgebra[F]) {
import cats.syntax.all._

def create(pet: Pet)(
implicit M: Monad[F]): EitherT[F, ValidationError, Pet] = {
def create(pet: Pet)(implicit M: Monad[F]): EitherT[F, ValidationError, Pet] =
for {
_ <- validation.doesNotExist(pet)
saved <- EitherT.liftT(repository.put(pet))
} yield saved
}

/* Could argue that we could make this idempotent on put and not check if the pet exists */
def update(pet: Pet)(
implicit M: Monad[F]): EitherT[F, ValidationError, Pet] = {
def update(pet: Pet)(implicit M: Monad[F]): EitherT[F, ValidationError, Pet] =
for {
_ <- validation.exists(pet.id)
saved <- EitherT.liftT(repository.put(pet))
} yield saved
}

def get(id: Long)(implicit M: Monad[F]): EitherT[F, ValidationError, Pet] =
EitherT {
Expand All @@ -60,7 +55,6 @@ class PetService[F[_]](repository: PetRepositoryAlgebra[F],
}

object PetService {
def apply[F[_]: Monad](repository: PetRepositoryAlgebra[F],
validation: PetValidationAlgebra[F]) =
def apply[F[_]: Monad](repository: PetRepositoryAlgebra[F], validation: PetValidationAlgebra[F]) =
new PetService[F](repository, validation)
}
Expand Up @@ -6,8 +6,7 @@ import cats.data.EitherT
import io.github.pauljamescleary.petstore.model.Pet
import io.github.pauljamescleary.petstore.repository.PetRepositoryAlgebra

class PetValidationInterpreter[F[_]: Monad](
repository: PetRepositoryAlgebra[F])
class PetValidationInterpreter[F[_]: Monad](repository: PetRepositoryAlgebra[F])
extends PetValidationAlgebra[F] {

def doesNotExist(pet: Pet): EitherT[F, ValidationError, Unit] = EitherT {
Expand Down
Expand Up @@ -42,7 +42,8 @@ trait PetStoreArbitraries {
tags <- Gen.listOfN(numTags, Gen.alphaStr).map(_.toSet)
photoUrls <- Gen
.listOfN(numTags, Gen.alphaStr)
.map(_.map(x => s"http://${x}.com")) map (_.toSet)
.map(_.map(x => s"http://${x}.com"))
.map(_.toSet)
id <- Gen.option(Gen.posNum[Long])
} yield Pet(name, category, bio, status, tags, photoUrls, id)
}
Expand Down

0 comments on commit 9553ce8

Please sign in to comment.