Skip to content

Instantly share code, notes, and snippets.

@Yummy-Yums
Last active May 26, 2024 18:25
Show Gist options
  • Save Yummy-Yums/ed5d572ae5291a18daca577cf82b3cb4 to your computer and use it in GitHub Desktop.
Save Yummy-Yums/ed5d572ae5291a18daca577cf82b3cb4 to your computer and use it in GitHub Desktop.
ZIO Services pattern tutorial
import zio._
case class Doc(
title: String,
description: String,
language: String,
format: String,
content: Array[Byte]
)
trait DocRepo {
def get(id: String): ZIO[Any, Throwable, Doc]
def save(document: Doc): ZIO[Any, Throwable, String]
def delete(id: String): ZIO[Any, Throwable, Unit]
def findByTitle(title: String): ZIO[Any, Throwable, List[Doc]]
}
case class DocRepoImpl(
metadataRepo: MetadataRepo,
BlobStorage: BlobStorage
) extends DocRepo {
override def get(id: String): ZIO[Any, Throwable, Doc] =
for {
metadata <- metadataRepo.get(id)
content <- BlobStorage.get(id)
} yield Doc(
metadata.title,
metadata.description,
metadata.language,
metadata.format,
content
)
override def save(document: Doc): ZIO[Any, Throwable, String] =
for {
id <- BlobStorage.put(document.content)
_ <- metadataRepo.put(
id,
Metadata(
document.title,
document.description,
document.language,
document.format
)
)
} yield id
override def delete(id: String): ZIO[Any, Throwable, Unit] =
for {
_ <- BlobStorage.delete(id)
_ <- metadataRepo.delete(id)
} yield ()
override def findByTitle(title: String): ZIO[Any, Throwable, List[Doc]] =
for {
map <- metadataRepo.findByTitle(title)
content <- ZIO.foreach(map)((id, metadata) =>
for {
content <- BlobStorage.get(id)
} yield id -> Doc(
metadata.title,
metadata.description,
metadata.language,
metadata.format,
content
)
)
} yield content.values.toList
}
object DocRepo {
def get(id: String): ZIO[DocRepo, Throwable, Doc] =
ZIO.serviceWithZIO[DocRepo](_.get(id))
def save(document: Doc): ZIO[DocRepo, Throwable, String] =
ZIO.serviceWithZIO[DocRepo](_.save(document))
def delete(id: String): ZIO[DocRepo, Throwable, Unit] =
ZIO.serviceWithZIO[DocRepo](_.delete(id))
def findByTitle(title: String): ZIO[DocRepo, Throwable, List[Doc]] =
ZIO.serviceWithZIO[DocRepo](_.findByTitle(title))
}
case class Metadata(
title: String,
description: String,
language: String,
format: String
)
trait MetadataRepo {
def get(id: String): ZIO[Any, Throwable, Metadata]
def put(id: String, metadata: Metadata): ZIO[Any, Throwable, Unit]
def delete(id: String): ZIO[Any, Throwable, Unit]
def findByTitle(title: String): ZIO[Any, Throwable, Map[String, Metadata]]
}
case class MetadataImpl(map: Ref[Map[String, Metadata]]) extends MetadataRepo {
override def get(id: String): ZIO[Any, Throwable, Metadata] = {
println("this is the map" + map.get)
map.get.map(_.get(id).get)
}
override def put(id: String, metadata: Metadata): ZIO[Any,Throwable,Unit] =
for {
id <- Random.nextUUID.map(_.toString)
_ <- map.update(_ + (id -> metadata))
} yield ()
override def delete(id: String): ZIO[Any,Throwable,Unit] =
map.get.map(_.-(id))
override def findByTitle(title: String): ZIO[Any,Throwable, Map[String, Metadata]] = {
map.get.map{ everyMap =>
everyMap.filter{
case (_, metadata) => metadata.title == title
}
}
}
}
object MetadataRepo {
def get(id: String): ZIO[MetadataRepo, Throwable, Metadata] =
ZIO.serviceWithZIO[MetadataRepo](_.get(id))
def put(id: String, metadata: Metadata): ZIO[MetadataRepo, Throwable, Unit] =
ZIO.serviceWithZIO[MetadataRepo](_.put(id, metadata))
def delete(id: String): ZIO[MetadataRepo, Throwable, Unit] =
ZIO.serviceWithZIO[MetadataRepo](_.delete(id))
def findByTitle(title: String): ZIO[MetadataRepo, Throwable, Map[String, Metadata]] =
ZIO.serviceWithZIO[MetadataRepo](_.findByTitle(title))
}
trait BlobStorage {
def get(id: String): ZIO[Any, Throwable, Array[Byte]]
def put(content: Array[Byte]): ZIO[Any, Throwable, String]
def delete(id: String): ZIO[Any, Throwable, Unit]
}
case class BlobStorageImpl(map: Ref[Map[String, Array[Byte]]]) extends BlobStorage {
override def get(id: String): ZIO[Any, Throwable, Array[Byte]] =
map.get.flatMap { storage =>
storage.get(id) match {
case Some(content) => ZIO.succeed(content)
case None => ZIO.fail(new Exception("Blob not found"))
}
}
override def put(content: Array[Byte]): ZIO[Any, Throwable, String] =
for {
id <- Random.nextUUID.map(_.toString)
_ <- map.update(_ + (id -> content))
} yield id
override def delete(id: String): ZIO[Any, Throwable, Unit] =
map.update(_ - id).unit
}
object BlobStorage {
def get(id: String): ZIO[BlobStorage, Throwable, Array[Byte]] =
ZIO.serviceWithZIO[BlobStorage](_.get(id))
def put(content: Array[Byte]): ZIO[BlobStorage, Throwable, String] =
ZIO.serviceWithZIO[BlobStorage](_.put(content))
def delete(id: String): ZIO[BlobStorage, Throwable, Unit] =
ZIO.serviceWithZIO[BlobStorage](_.delete(id))
}
object InmemoryMetadataRepo {
val layer =
ZLayer.fromZIO{
Ref.make(Map.empty[String, Metadata]).map(new MetadataImpl(_))
}
}
object InmemoryBlobStorage {
val layer =
ZLayer.fromZIO {
Ref.make(Map.empty[String, Array[Byte]]).map(new BlobStorageImpl(_))
}
}
object DocRepoImpl {
val layer: ZLayer [BlobStorage with MetadataRepo, Nothing, DocRepo] =
ZLayer {
for {
metadataRepo <- ZIO.service[MetadataRepo]
BlobStorage <- ZIO.service[BlobStorage]
} yield DocRepoImpl(metadataRepo, BlobStorage)
}
}
import zio._
import java.io.IOException
object MainApp extends ZIOAppDefault {
val app =
for {
id <-
DocRepo.save(
Doc(
"title",
"description",
"en",
"text/plain",
"content".getBytes()
)
)
doc <- DocRepo.get(id)
_ <- Console.printLine(doc)
_ <- Console.printLine(
s"""
|Downloaded the document with $id id:
| title: ${doc.title}
| description: ${doc.description}
| language: ${doc.language}
| format: ${doc.format}
|""".stripMargin
)
_ <- DocRepo.delete(id)
_ <- Console.printLine(s"Deleted the document with $id id")
} yield ()
def run =
app.provide(
DocRepoImpl.layer,
InmemoryBlobStorage.layer,
InmemoryMetadataRepo.layer
)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment