diff --git a/build.sbt b/build.sbt index 95e2cb4..55f0d18 100644 --- a/build.sbt +++ b/build.sbt @@ -40,7 +40,7 @@ lazy val root = `zio-quickstart-prelude`, `zio-quickstart-stm`, `zio-quickstart-sql`, - `zio-quickstart-schema`, + `zio-quickstart-schema` ) .settings( testFrameworks += new TestFramework("zio.test.sbt.ZTestFramework") @@ -63,4 +63,4 @@ lazy val `zio-quickstart-cache` = project lazy val `zio-quickstart-prelude` = project lazy val `zio-quickstart-stm` = project lazy val `zio-quickstart-sql` = project -lazy val `zio-quickstart-schema` = project \ No newline at end of file +lazy val `zio-quickstart-schema` = project diff --git a/zio-quickstart-schema/build.sbt b/zio-quickstart-schema/build.sbt index 07a3e43..b4e6c36 100644 --- a/zio-quickstart-schema/build.sbt +++ b/zio-quickstart-schema/build.sbt @@ -1,16 +1,15 @@ scalaVersion := "2.13.12" organization := "dev.zio" -name := "zio-quickstart-restful-schema" - +name := "zio-quickstart-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 + "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 +resolvers ++= Resolver.sonatypeOssRepos("snapshots") 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 index 786218d..094c730 100644 --- a/zio-quickstart-schema/src/main/scala/dev/zio/quickstart/Person.scala +++ b/zio-quickstart-schema/src/main/scala/dev/zio/quickstart/Person.scala @@ -6,10 +6,12 @@ import zio.schema.validation.Validation // one can choose the detailed way of validation or use annotations -case class Person(name: String, - @validate(Validation.greaterThan(18)) - age: Int) +case class Person( + name: String, + @validate(Validation.greaterThan(18)) + age: Int +) object Person { - implicit val schema: Schema[Person] = DeriveSchema.gen -} \ No newline at end of file + implicit val schema: Schema[Person] = DeriveSchema.gen +} diff --git a/zio-quickstart-schema/src/main/scala/dev/zio/quickstart/PersonWithEmail.scala b/zio-quickstart-schema/src/main/scala/dev/zio/quickstart/PersonWithEmail.scala index 9df4683..7e690ba 100644 --- a/zio-quickstart-schema/src/main/scala/dev/zio/quickstart/PersonWithEmail.scala +++ b/zio-quickstart-schema/src/main/scala/dev/zio/quickstart/PersonWithEmail.scala @@ -12,8 +12,13 @@ case class PersonWithEmail(email: String) object PersonWithEmail { val localPart = Regex.letter.atLeast(3) - val tld = (Regex.literal("org") | Regex.literal("net") | Regex.literal("com")) // Matches top-level domains (2 or more letters) - val regexValidator = localPart ~ Regex.digit.atLeast(1) ~ Regex.literal("@") ~ Regex.letter.atLeast(3) ~ Regex.literal(".") ~ tld + val tld = + (Regex.literal("org") | Regex.literal("net") | Regex.literal( + "com" + )) // Matches top-level domains (2 or more letters) + val regexValidator = localPart ~ Regex.digit.atLeast(1) ~ Regex.literal( + "@" + ) ~ Regex.letter.atLeast(3) ~ Regex.literal(".") ~ tld implicit val schema: Schema[PersonWithEmail] = CaseClass1( id0 = TypeId.fromTypeName("PersonWithEmail"), @@ -24,7 +29,7 @@ object PersonWithEmail { get0 = (p: PersonWithEmail) => p.email, set0 = { (p: PersonWithEmail, s: String) => p.copy(email = s) } ), - defaultConstruct0 = email => PersonWithEmail(email), + defaultConstruct0 = email => PersonWithEmail(email) ) -} \ No newline at end of file +} 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 index a8d6753..26bb38c 100644 --- a/zio-quickstart-schema/src/main/scala/dev/zio/quickstart/PrepareDataUtils.scala +++ b/zio-quickstart-schema/src/main/scala/dev/zio/quickstart/PrepareDataUtils.scala @@ -19,14 +19,18 @@ object PrepareDataUtils { def withFile[A](name: String)(useFile: BufferedSource => Task[A]): Task[A] = ZIO.acquireReleaseWith(openFile(name))(closeFile)(useFile) - def getNameAndAgeOfPerson(bufferedSourceFile: BufferedSource): List[Person] = { + def getNameAndAgeOfPerson( + bufferedSourceFile: BufferedSource + ): List[Person] = { def getPerson: Iterator[Person] = for { - line <- bufferedSourceFile.getLines().filter(incomingString => !incomingString.contains("Name")) - list = line.split(",") - dob = list.reverse.head - name = list.head - age = getAge(dob) + line <- bufferedSourceFile + .getLines() + .filter(incomingString => !incomingString.contains("Name")) + list = line.split(",") + dob = list.reverse.head + name = list.head + age = getAge(dob) person = Person(name, age) } yield person @@ -34,14 +38,17 @@ object PrepareDataUtils { } - def getEmailOfPerson(bufferedSourceFile: BufferedSource): List[PersonWithEmail] = { + def getEmailOfPerson( + bufferedSourceFile: BufferedSource + ): List[PersonWithEmail] = { def getPersonWithEmail: Iterator[PersonWithEmail] = for { - line <- bufferedSourceFile.getLines().filter(incomingString => !incomingString.contains("Name")) - arr = line.split(",") + line <- bufferedSourceFile + .getLines() + .filter(incomingString => !incomingString.contains("Name")) + arr = line.split(",") emaii = arr(1) - personWithEmail = PersonWithEmail(emaii) } yield personWithEmail @@ -54,6 +61,4 @@ object PrepareDataUtils { 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 index 1cd256c..ac39357 100644 --- a/zio-quickstart-schema/src/main/scala/dev/zio/quickstart/SchemaValidator.scala +++ b/zio-quickstart-schema/src/main/scala/dev/zio/quickstart/SchemaValidator.scala @@ -6,38 +6,38 @@ import zio.schema.annotation.validate import zio.schema.validation.Validation import zio.stream.ZStream - object SchemaValidator extends ZIOAppDefault { import PrepareDataUtils._ - val res = withFile(resourceName)(file => ZIO.attempt(getNameAndAgeOfPerson(file))) + val res = + withFile(resourceName)(file => ZIO.attempt(getNameAndAgeOfPerson(file))) val runStream = ZStream .fromIterableZIO(res) - .map{ person => + .map { person => Person.schema.validate(person) match { - case Chunk() => Right(person) + case Chunk() => Right(person) case Chunk(_) | Chunk(_, _) => Left(person) } } - val listOfValidPersons = runStream + val listOfValidPersons = 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 + 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 - + def run = + program val program = for { - count <- runStream.runFold(0)((accum, _) => accum + 1) - c <- listOfValidPersons + count <- runStream.runFold(0)((accum, _) => accum + 1) + c <- listOfValidPersons _ <- 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 index 74b8c78..0620543 100644 --- a/zio-quickstart-schema/src/test/scala/dev/zio/quickstart/SchemaValidatorSpec.scala +++ b/zio-quickstart-schema/src/test/scala/dev/zio/quickstart/SchemaValidatorSpec.scala @@ -5,30 +5,31 @@ import zio.stream.ZStream import zio.test._ import zio.{Chunk, ZIO} - object StreamWithSchemaValidation { - private val listOfNameAndAgeOfPersons = withFile(resourceName)(file => ZIO.attempt(getNameAndAgeOfPerson(file))) - private val listOfEmailOfPersons = withFile(resourceName)(file => ZIO.attempt(getEmailOfPerson(file))) + private val listOfNameAndAgeOfPersons = + withFile(resourceName)(file => ZIO.attempt(getNameAndAgeOfPerson(file))) + private val listOfEmailOfPersons = + withFile(resourceName)(file => ZIO.attempt(getEmailOfPerson(file))) private val streamOfPersonWithNameAndAge = ZStream .fromIterableZIO(listOfNameAndAgeOfPersons) - .map{ person => + .map { person => val validatedRecord = Person.schema.validate(person) validatedRecord match { - case Chunk() => Right(person) - case Chunk(_) | Chunk(_,_) => Left(person) + case Chunk() => Right(person) + case Chunk(_) | Chunk(_, _) => Left(person) } } private val streamOfPersonWithEmail = ZStream .fromIterableZIO(listOfEmailOfPersons) - .map{ person => + .map { person => val validatedRecord = PersonWithEmail.schema.validate(person) validatedRecord match { - case Chunk() => Right(person) - case Chunk(_) | Chunk(_,_) => Left(person) + case Chunk() => Right(person) + case Chunk(_) | Chunk(_, _) => Left(person) } } @@ -37,40 +38,39 @@ object StreamWithSchemaValidation { .runFold(0)((acc, x) => x match { case Right(_) => acc + 1 - case Left(_) => acc + case Left(_) => acc } ) def getTotalNumberofRecords: ZIO[Any, Throwable, Int] = - streamOfPersonWithNameAndAge - .runCount + streamOfPersonWithNameAndAge.runCount .map(_.toInt) def getPeopleBelow18: ZIO[Any, Throwable, Int] = for { peopleAbove18 <- getTotalNumberofPeopleAbove18 - totalNumber <- getTotalNumberofRecords + totalNumber <- getTotalNumberofRecords } yield totalNumber - peopleAbove18 def getFirstPersonAbove18: ZIO[Any, Throwable, List[Person]] = streamOfPersonWithNameAndAge - .runFold(List.empty[Person]){ + .runFold(List.empty[Person]) { case (resList, Right(person)) => resList :+ person - case (resList, Left(_)) => resList + case (resList, Left(_)) => resList } def getFirstPersonBelow18: ZIO[Any, Throwable, List[Person]] = streamOfPersonWithNameAndAge - .runFold(List.empty[Person]){ + .runFold(List.empty[Person]) { case (resList, Left(person)) => resList :+ person - case (resList, Right(_)) => resList + case (resList, Right(_)) => resList } def validEmailsTotal: ZIO[Any, Throwable, Int] = streamOfPersonWithEmail .runFold(0)((accum, res) => { res match { - case Left(_) => accum + case Left(_) => accum case Right(_) => accum + 1 } }) @@ -78,25 +78,26 @@ object StreamWithSchemaValidation { def invalidEmailsTotal: ZIO[Any, Throwable, Int] = { for { res <- streamOfPersonWithEmail.runCollect - total = res.size + total = res.size validEmailsTotal <- validEmailsTotal } yield total - validEmailsTotal } def listOfValidEmail = { for { - list <- streamOfPersonWithEmail.runFold(List.empty[String])((list, res) => { - res match { - case Left(_) => list - case Right(elem) => list :+ elem.email + list <- streamOfPersonWithEmail.runFold(List.empty[String])( + (list, res) => { + res match { + case Left(_) => list + case Right(elem) => list :+ elem.email + } } - }) + ) } yield list } } - object ExamplesSpec extends ZIOSpecDefault { object PersonsData { val firstFiveNamesOfPeopleAbove18: List[Person] = List( @@ -115,7 +116,7 @@ object ExamplesSpec extends ZIOSpecDefault { Person("Sig.ra Vanessa Cremonesi", 16) ) - val listOfValidEmails = List( + val listOfValidEmails = List( "annamaria26@example.org", "rosaria88@example.com", "stefano58@example.net", @@ -130,58 +131,71 @@ object ExamplesSpec extends ZIOSpecDefault { } def spec: Spec[Any, Throwable] = suite("ExampleSpec")( - test("Total number of people is 31"){ - assertZIO(StreamWithSchemaValidation.getTotalNumberofRecords)(Assertion.equalTo(31)) + test("Total number of people is 31") { + assertZIO(StreamWithSchemaValidation.getTotalNumberofRecords)( + Assertion.equalTo(31) + ) }, - test("Number of persons above the age 18 is 26"){ + test("Number of persons above the age 18 is 26") { for { count <- StreamWithSchemaValidation.getTotalNumberofPeopleAbove18 } yield assertTrue(count == 26) }, - test("Number of persons below the age 18 is 5"){ + test("Number of persons below the age 18 is 5") { for { count <- StreamWithSchemaValidation.getPeopleBelow18 } yield assertTrue(count == 5) }, - test("First name in the stream of persons Above 18 is Michela Rizzo-Traetta"){ + test( + "First name in the stream of persons Above 18 is Michela Rizzo-Traetta" + ) { for { persons <- StreamWithSchemaValidation.getFirstPersonAbove18 - } yield assertTrue(persons.head == PersonsData.firstFiveNamesOfPeopleAbove18.head) + } yield assertTrue( + persons.head == PersonsData.firstFiveNamesOfPeopleAbove18.head + ) }, - test("First five names of persons above 18"){ + test("First five names of persons above 18") { for { persons <- StreamWithSchemaValidation.getFirstPersonAbove18 - } yield assertTrue(persons.take(5) == PersonsData.firstFiveNamesOfPeopleAbove18) + } yield assertTrue( + persons.take(5) == PersonsData.firstFiveNamesOfPeopleAbove18 + ) }, - test("First name of person below 18 is Rosalia Marina"){ + test("First name of person below 18 is Rosalia Marina") { for { persons <- StreamWithSchemaValidation.getFirstPersonBelow18 nameOfFirstPerson = persons.head.name - mustBeRosalia = PersonsData.firstFiveNamesOfPeoplebelow18.head.name + mustBeRosalia = PersonsData.firstFiveNamesOfPeoplebelow18.head.name } yield assertTrue(nameOfFirstPerson == mustBeRosalia) }, - test("Total number of valid emails ( valid emails must have numbers before the '@'"){ + test( + "Total number of valid emails ( valid emails must have numbers before the '@'" + ) { for { total <- StreamWithSchemaValidation.validEmailsTotal } yield assertTrue(total == 9) }, - test("Total number of invalid emails"){ + test("Total number of invalid emails") { for { total <- StreamWithSchemaValidation.invalidEmailsTotal } yield assertTrue(total == 22) }, - test("first valid email is annamaria26@example.org"){ + test("first valid email is annamaria26@example.org") { for { emails <- StreamWithSchemaValidation.listOfValidEmail firstEmail = emails.head } yield assertTrue(firstEmail == PersonsData.listOfValidEmails.head) }, - test("valid emails list must match the one provided"){ + test("valid emails list must match the one provided") { for { emails <- StreamWithSchemaValidation.listOfValidEmail - } yield assertTrue(emails == PersonsData.listOfValidEmails, emails.size == 9) + } yield assertTrue( + emails == PersonsData.listOfValidEmails, + emails.size == 9 + ) } ) }