diff --git a/zio-quickstart-schema/build.sbt b/zio-quickstart-schema/build.sbt new file mode 100644 index 0000000..07a3e43 --- /dev/null +++ b/zio-quickstart-schema/build.sbt @@ -0,0 +1,16 @@ +scalaVersion := "2.13.12" +organization := "dev.zio" +name := "zio-quickstart-restful-schema" + + +libraryDependencies ++= Seq( + "dev.zio" %% "zio-schema" % "1.4.1", + "dev.zio" %% "zio-schema-zio-test" % "1.4.1", + "dev.zio" %% "zio-schema-derivation" % "1.4.1", + "org.scala-lang" % "scala-reflect" % scalaVersion.value % "provided", + "dev.zio" %% "zio-test" % "2.1.9" % Test, + "dev.zio" %% "zio-test-sbt" % "2.1.9" % Test, + "dev.zio" %% "zio-test-magnolia" % "2.1.9" % Test +) + +resolvers ++= Resolver.sonatypeOssRepos("snapshots") \ No newline at end of file diff --git a/zio-quickstart-schema/src/main/resources/incoming_data.txt b/zio-quickstart-schema/src/main/resources/incoming_data.txt new file mode 100644 index 0000000..db9a23d --- /dev/null +++ b/zio-quickstart-schema/src/main/resources/incoming_data.txt @@ -0,0 +1,32 @@ +Name,Email,Date of birth +Rosalia Marini,annamaria26@example.org,2021-10-29 +Michela Rizzo-Traetta,pisaroniantonietta@example.com,1955-07-05 +Gianpaolo Nibali,donatogagliardi@example.com,1995-06-08 +Orlando Gradenigo,rcatenazzi@example.org,1970-09-07 +Pasqual Disdero-Verri,rosaria88@example.com,1929-11-25 +Alphons Amato,stefano58@example.net,1963-04-30 +Annetta Balbi,federica81@example.org,1986-09-23 +Sig. Ricciotti Gigli,bianchiflavia@example.net,1930-03-16 +Isabella Petrocelli,francesca93@example.org,1972-09-27 +Carolina Veneziano-Giovine,giulio81@example.net,1916-03-27 +Melina Respighi,ippaziodisdero@example.org,2013-11-04 +Dott. Mario Crespi,giampieroravaglioli@example.com,1920-02-23 +Sig.ra Annunziata Sforza,ftomei@example.org,1952-08-27 +Dott. Greca Paganini,eleanora72@example.org,1995-08-10 +Virginia Draghi,krinaldi@example.org,1960-06-30 +Ivo Magrassi-Ginese,qvarano@example.com,1961-07-13 +Ferdinando Prodi,ybompiani@example.org,2019-02-17 +Lucrezia Lucciano,tcarullo@example.com,1968-07-20 +Germana Favata,marazzililiana@example.org,1983-10-12 +Gioffre Sagnelli,ucontarini@example.org,2010-02-05 +Elladio Garibaldi-Iannuzzi,farnesevirgilio@example.net,2002-06-02 +Leopoldo Donarelli-Pagliaro,bettinaparri@example.net,1934-08-31 +Dott. Cirillo Caetani,antoniorosselli@example.net,2001-08-27 +Rosalia Roncalli-Gangemi,turatieugenia@example.com,1986-06-15 +Rembrandt Briccialdi,imelda80@example.net,1965-07-18 +Gianmarco Fanucci,ermenegildoprati@example.net,1962-09-12 +Francesca Callegari,ferruccicostanzo@example.org,1917-09-09 +Sig.ra Daria Nordio,donatozarlino@example.org,1911-01-24 +Sig.ra Vanessa Cremonesi,virginia96@example.net,2008-05-20 +Dott. Durante Treccani,borghesegionata@example.com,1933-07-12 +Adelasia Satriani,rubertoarnaldo@example.com,1970-07-29 \ No newline at end of file diff --git a/zio-quickstart-schema/src/main/scala/dev/zio/quickstart/Person.scala b/zio-quickstart-schema/src/main/scala/dev/zio/quickstart/Person.scala new file mode 100644 index 0000000..794c9cb --- /dev/null +++ b/zio-quickstart-schema/src/main/scala/dev/zio/quickstart/Person.scala @@ -0,0 +1,46 @@ +package dev.zio.quickstart + +import zio._ +import zio.schema._ +import zio.schema.annotation.validate +import zio.schema.validation.Validation +import zio.stream.ZStream + +// one can choose the detailed way of validation or use annotations + +case class Person( + name: String, + @validate(Validation.greaterThan(18)) + age: Int) + +object Person { + implicit val schema : Schema[Person] = DeriveSchema.gen +} + + +// one can choose the detailed way of validation or use annotations + +//case class Person(name: String, age: Int) + +// object Person { +// implicit val schema: Schema[Person] = CaseClass2( +// id0 = TypeId.fromTypeName("Person"), +// field01 = Schema.Field( +// name0 = "name", +// schema0 = Schema[String], +// validation0 = Validation.minLength(15), +// get0 = (p: Person) => p.name, +// set0 = { (p: Person, s: String) => p.copy(name = s) } +// ), +// field02 = Schema.Field( +// name0 = "age", +// schema0 = Schema[Int], +// validation0 = Validation.greaterThan(18), +// get0 = (p: Person) => p.age, +// set0 = { (p: Person, age: Int) => p.copy(age = age) } +// ), +// construct0 = (name, age) => Person(name, age), +// ) + +// } + diff --git a/zio-quickstart-schema/src/main/scala/dev/zio/quickstart/PrepareDataUtils.scala b/zio-quickstart-schema/src/main/scala/dev/zio/quickstart/PrepareDataUtils.scala new file mode 100644 index 0000000..fce02da --- /dev/null +++ b/zio-quickstart-schema/src/main/scala/dev/zio/quickstart/PrepareDataUtils.scala @@ -0,0 +1,47 @@ +package dev.zio.quickstart + +import zio._ + +import java.time.LocalDate +import java.io.{File, FileNotFoundException, IOException} +import scala.io.{BufferedSource, Source} +import zio.schema + +object PrepareDataUtils { + + val homeDirectory = java.lang.System.getProperty("user.home") + val fileName = homeDirectory + "/Desktop/centre/scala/zio-quickstarts/zio-quickstart-schema/src/main/resources/incoming_data.txt" + + + def openFile(name: String): IO[IOException, BufferedSource] = + ZIO.attemptBlockingIO(Source.fromFile(name)) + + def closeFile(bufferedSourceFile: BufferedSource): ZIO[Any, Nothing, Unit] = + ZIO.succeedBlocking(bufferedSourceFile.close()) + + def withFile[A](name: String)(useFile: BufferedSource => Task[A]): Task[A] = + ZIO.acquireReleaseWith(openFile(name))(closeFile)(useFile) + + def prepareData(bufferedSourceFile: BufferedSource): List[Person] = { + + def getPerson: Iterator[Person] = for { + line <- bufferedSourceFile.getLines().filter(incomingString => !incomingString.contains("Name")) + arr = line.split(",") + dob = arr.reverse.head + name = arr.head + age = getAge(dob) + person = Person(name, age) + } yield person + + getPerson.toList + + } + + def getAge(dob: String): Int = { + val currYear = LocalDate.now().getYear + currYear - LocalDate.parse(dob).getYear + } + +} + + diff --git a/zio-quickstart-schema/src/main/scala/dev/zio/quickstart/SchemaValidator.scala b/zio-quickstart-schema/src/main/scala/dev/zio/quickstart/SchemaValidator.scala new file mode 100644 index 0000000..fd74eac --- /dev/null +++ b/zio-quickstart-schema/src/main/scala/dev/zio/quickstart/SchemaValidator.scala @@ -0,0 +1,44 @@ +package dev.zio.quickstart + +import zio._ +import zio.schema._ +import zio.schema.annotation.validate +import zio.schema.validation.Validation +import zio.stream.ZStream + + +object SchemaValidator extends ZIOAppDefault { + + import PrepareDataUtils._ + + val res = withFile(fileName)(file => ZIO.attempt(prepareData(file))) + + val runStream = ZStream + .fromIterableZIO(res) + .map{ person => + Person.schema.validate(person) match { + case Chunk() => Right(person) + case Chunk(_) | Chunk(_, _) => Left(person) + } + + } + + val b = runStream + .runFold((List.empty[Person], List.empty[String])) { + case ((valid, invalid), Right(person)) => (valid :+ person, invalid) // Collect valid persons + case ((valid, invalid), Left(error)) => (valid, invalid :+ error.toString) // Collect errors + } + + + def run= + program + + + val program = + for { + count <- runStream.runFold(0)((accum, _) => accum + 1) + c <- b + _ <- Console.printLine(s"Total count: ${c._2.size}") + } yield () + +} diff --git a/zio-quickstart-schema/src/test/scala/dev/zio/quickstart/SchemaValidatorSpec.scala b/zio-quickstart-schema/src/test/scala/dev/zio/quickstart/SchemaValidatorSpec.scala new file mode 100644 index 0000000..ea5d208 --- /dev/null +++ b/zio-quickstart-schema/src/test/scala/dev/zio/quickstart/SchemaValidatorSpec.scala @@ -0,0 +1,110 @@ +package dev.zio.quickstart + +import PrepareDataUtils._ +import zio.stream.ZStream +import zio.test._ +import zio.{Chunk, ZIO} + + +object StreamWithSchemaValidation { + private val res = withFile(fileName)(file => ZIO.attempt(prepareData(file))) + + private val runStream = ZStream + .fromIterableZIO(res) + .map{ person => + val validatedRecord = Person.schema.validate(person) + + validatedRecord match { + case Chunk() => Right(person) + case Chunk(_) | Chunk(_,_) => Left(person) + } + } + + def getTotalNumberofPeopleAbove18: ZIO[Any, Throwable, Int] = + runStream + .runFold(0)((acc, x) => + x match { + case Right(_) => acc + 1 + case Left(_) => acc + } + ) + + def getTotalNumberofRecords: ZIO[Any, Throwable, Int] = + runStream + .runCount + .map(_.toInt) + + def getPeopleBelow18: ZIO[Any, Throwable, Int] = + for { + peopleAbove18 <- getTotalNumberofPeopleAbove18 + totalNumber <- getTotalNumberofRecords + } yield totalNumber - peopleAbove18 + + + def getFirstPersonAbove18: ZIO[Any, Throwable, List[Person]] = + runStream + .runFold(List.empty[Person]){ + case (resList, Right(person)) => resList :+ person + case (resList, Left(_)) => resList + } + + def getFirstPersonbelow18: ZIO[Any, Throwable, List[Person]] = + runStream + .runFold(List.empty[Person]){ + case (resList, Left(person)) => resList :+ person + case (resList, Right(_)) => resList + } + +} + + +object SchemaValidatorSpec extends ZIOSpecDefault { + + object Answers { + val firstFiveNamesOfPeopleAbove18: List[Person] = List( + Person("Michela Rizzo-Traetta", 69), + Person("Gianpaolo Nibali", 29), + Person("Orlando Gradenigo", 54), + Person("Pasqual Disdero-Verri", 95), + Person("Alphons Amato", 61) + ) + + val firstFiveNamesOfPeoplebelow18 = List( + Person("Rosalia Marini", 3), + Person("Melina Respighi", 11), + Person("Ferdinando Prodi", 5), + Person("Gioffre Sagnelli", 14), + Person("Sig.ra Vanessa Cremonesi", 16) + ) + + } + def spec: Spec[Any, Throwable] = suite("ExampleSpec")( + test("Get total number of people"){ + assertZIO(StreamWithSchemaValidation.getTotalNumberofRecords)(Assertion.equalTo(31)) + }, + test("Get people above the age of 18"){ + for { + count <- StreamWithSchemaValidation.getTotalNumberofPeopleAbove18 + } yield assertTrue(count == 26) + }, + test("First name in the stream"){ + for { + persons <- StreamWithSchemaValidation.getFirstPersonAbove18 + } yield assertTrue(persons.head == Answers.firstFiveNamesOfPeopleAbove18.head) + }, + test("First five names of persons above 18"){ + for { + persons <- StreamWithSchemaValidation.getFirstPersonAbove18 + } yield assertTrue(persons.take(5) == Answers.firstFiveNamesOfPeopleAbove18) + }, + test("First name of person below 18 is Rosalia Marina"){ + for { + persons <- StreamWithSchemaValidation.getFirstPersonbelow18 + nameOfFirstPerson = persons.head.name + mustBeRosalia = Answers.firstFiveNamesOfPeoplebelow18.head.name + + } yield assertTrue(nameOfFirstPerson == mustBeRosalia) + } + + ) +}