Skip to content
This repository was archived by the owner on Feb 10, 2023. It is now read-only.
Open
3 changes: 2 additions & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ val Version = new {
val Circe = "0.14.1"
val Java = "11"
val Munit = "0.7.29"
val OpenApi = "0.0.0+34-7853f5fb-SNAPSHOT"
val Scala213 = "2.13.8"
val Scala3 = "3.1.2"
val ScalaJavaTime = "2.3.0"
Expand Down Expand Up @@ -51,7 +52,7 @@ lazy val root = module(identifier = None)
lazy val core = module(identifier = Some("core"))
.settings(
libraryDependencies ++=
"io.circe" %%% "circe-parser" % Version.Circe ::
"io.hireproof" %%% "openapi-core" % Version.OpenApi ::
"org.typelevel" %%% "cats-core" % Version.Cats ::
"org.scalameta" %%% "munit" % Version.Munit % "test" ::
Nil
Expand Down

This file was deleted.

34 changes: 12 additions & 22 deletions modules/core/src/main/scala/io/hireproof/screening/Constraint.scala
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package io.hireproof.screening

import cats.syntax.all._
import io.circe.syntax._
import io.circe.{Encoder, Json}
import io.hireproof.openapi.{Encoder, OpenApi}
import io.hireproof.openapi.syntax._

import java.time.ZonedDateTime
import scala.concurrent.duration.FiniteDuration
Expand All @@ -22,7 +22,7 @@ object Constraint {

final case class Rule(
identifier: Constraint.Identifier,
reference: Option[Json],
reference: Option[OpenApi],
delta: Option[Double],
equal: Option[Boolean]
) extends Constraint {
Expand All @@ -42,7 +42,6 @@ object Constraint {
val Contains: Constraint.Identifier = Identifier("contains")
val Email: Constraint.Identifier = Identifier("email")
val GreaterThan: Constraint.Identifier = Identifier("greaterThan")
val Json: Constraint.Identifier = Identifier("json")
val LessThan: Constraint.Identifier = Identifier("lessThan")
val Equal: Constraint.Identifier = Identifier("equal")
val Matches: Constraint.Identifier = Identifier("matches")
Expand All @@ -51,38 +50,32 @@ object Constraint {
}

def apply[A: Encoder](identifier: Identifier, reference: A, delta: Double, equal: Boolean): Constraint =
Rule(identifier, reference.asJson.some, delta.some, equal.some)
Rule(identifier, reference.asOpenApi.some, delta.some, equal.some)
def apply[A: Encoder](identifier: Identifier, reference: A, delta: Double): Constraint =
Rule(identifier, reference.asJson.some, delta.some, equal = none)
Rule(identifier, reference.asOpenApi.some, delta.some, equal = none)
def apply[A: Encoder](identifier: Identifier, reference: A, equal: Boolean): Constraint =
Rule(identifier, reference.asJson.some, delta = none, equal.some)
Rule(identifier, reference.asOpenApi.some, delta = none, equal.some)
def apply[A: Encoder](identifier: Identifier, reference: A): Constraint =
Rule(identifier, reference.asJson.some, delta = none, equal = none)
Rule(identifier, reference.asOpenApi.some, delta = none, equal = none)
def apply(identifier: Identifier): Constraint = Rule(identifier, reference = none, delta = none, equal = none)

object collection {
def contains[A: Encoder](reference: A): Constraint = Constraint(Identifier.Contains, reference.asJson)
def contains[A: Encoder](reference: A): Constraint = Constraint(Identifier.Contains, reference)
}

object duration {
def equal(reference: FiniteDuration): Constraint = Constraint(Identifier.Equal, reference)

def greaterThan(reference: FiniteDuration, equal: Boolean = true): Constraint =
Constraint(Identifier.GreaterThan, reference, equal)

def lessThan(reference: FiniteDuration, equal: Boolean = true): Constraint =
Constraint(Identifier.LessThan, reference, equal)
}

def json(reference: String): Constraint = Constraint(Identifier.Json, reference)

object number {
def equal[A: Encoder](reference: A, delta: Double = 0d): Constraint =
Constraint(Identifier.Equal, reference, delta)

def greaterThan[A: Encoder](reference: A, delta: Double = 0d, equal: Boolean = true): Constraint =
Constraint(Identifier.GreaterThan, reference, delta, equal)

def lessThan[A: Encoder](reference: A, delta: Double = 0d, equal: Boolean = true): Constraint =
Constraint(Identifier.LessThan, reference, delta, equal)
}
Expand All @@ -93,19 +86,16 @@ object Constraint {

object text {
val email: Constraint = Constraint(Identifier.Email)

def equal(reference: String): Constraint = Constraint(Identifier.Equal, reference)

def matches(regex: Regex): Constraint = Constraint(Identifier.Matches, regex.regex)
def equal(reference: String): Constraint = Constraint(Identifier.Equal, OpenApi.fromString(reference))
def matches(regex: Regex): Constraint = Constraint(Identifier.Matches, OpenApi.fromString(regex.regex))
}

val required: Constraint = Constraint(Identifier.Required)

object time {
def after(reference: ZonedDateTime, equal: Boolean = true): Constraint =
Constraint(Identifier.After, reference, equal)

Constraint(Identifier.After, OpenApi.fromString(reference.toString), equal)
def before(reference: ZonedDateTime, equal: Boolean = true): Constraint =
Constraint(Identifier.Before, reference, equal)
Constraint(Identifier.Before, OpenApi.fromString(reference.toString), equal)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package io.hireproof.screening

import io.hireproof.openapi.syntax._
import io.hireproof.openapi.{Encoder, OpenApi}

import scala.concurrent.duration.{FiniteDuration, TimeUnit}

trait OpenApiInstances {
implicit val encoderTimeUnit: Encoder[TimeUnit] = Encoder[String].contramap(_.name())

implicit val encoderFiniteDuration: Encoder[FiniteDuration] = Encoder.instance { duration =>
OpenApi.obj("length" := duration.length, "unit" := duration.unit)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,14 @@ object Selection {

final case class History(toChain: Chain[Selection]) extends AnyVal {
def /(field: String): History = append(Field(field))

def /(index: Int): History = append(Index(index))

def /(selection: Selection): History = append(selection)

def append(selection: Selection): History = History(toChain append selection)

def /:(field: String): History = prepend(Field(field))

def /:(index: Int): History = prepend(Index(index))

def /:(selection: Selection): History = prepend(selection)

def prepend(selection: Selection): History = History(toChain prepend selection)

def ++(history: History): History = History(toChain ++ history.toChain)

def isRoot: Boolean = toChain.isEmpty

def up: Selection.History = toChain.initLast match {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import cats.Applicative
import cats.arrow.Arrow
import cats.data.{NonEmptyList, Validated, ValidatedNel}
import cats.syntax.all._
import io.circe.{Encoder, Json}
import io.hireproof.openapi.{Encoder, OpenApi}
import io.hireproof.screening.validations._

import scala.reflect.ClassTag
Expand Down Expand Up @@ -33,7 +33,7 @@ abstract class Validation[-I, +O] {

final def withConstraint(constraint: Constraint): Validation[I, O] =
Validation(Set(constraint))(run(_).leftMap { violations =>
val actual = violations.collectFirstSome(_.toActual).getOrElse(Json.Null)
val actual = violations.collectFirstSome(_.toActual).getOrElse(OpenApi.Null)
NonEmptyList.one(Violation(constraint, actual))
})

Expand Down
40 changes: 20 additions & 20 deletions modules/core/src/main/scala/io/hireproof/screening/Violation.scala
Original file line number Diff line number Diff line change
@@ -1,46 +1,46 @@
package io.hireproof.screening

import cats.syntax.all._
import io.circe.syntax._
import io.circe.{Encoder, Json}
import io.hireproof.openapi.{Encoder, OpenApi}
import io.hireproof.openapi.syntax._

sealed abstract class Violation extends Product with Serializable {
def toConstraint: Option[Constraint]

def toActual: Option[Json]
def toActual: Option[OpenApi]
}

object Violation {
final case class Validation(constraint: Constraint, actual: Json) extends Violation {
final case class Validation(constraint: Constraint, actual: OpenApi) extends Violation {
override def toConstraint: Option[Constraint] = constraint.some
override def toActual: Option[Json] = actual.some
override def toActual: Option[OpenApi] = actual.some
}

final case class Conflict(actual: Json) extends Violation {
final case class Conflict(actual: OpenApi) extends Violation {
override def toConstraint: Option[Constraint] = none
override def toActual: Option[Json] = actual.some
override def toActual: Option[OpenApi] = actual.some
}

final case class Invalid(reference: Option[Json], actual: Json) extends Violation {
final case class Invalid(reference: Option[OpenApi], actual: OpenApi) extends Violation {
override def toConstraint: Option[Constraint] = none
override def toActual: Option[Json] = actual.some
override def toActual: Option[OpenApi] = actual.some
}

final case class Missing(reference: Option[Json]) extends Violation {
final case class Missing(reference: Option[OpenApi]) extends Violation {
override def toConstraint: Option[Constraint] = none
override def toActual: Option[Json] = none
override def toActual: Option[OpenApi] = none
}

final case class Unknown(actual: Json) extends Violation {
final case class Unknown(actual: OpenApi) extends Violation {
override def toConstraint: Option[Constraint] = none
override def toActual: Option[Json] = actual.some
override def toActual: Option[OpenApi] = actual.some
}

def apply[A: Encoder](constraint: Constraint, actual: A): Violation = Validation(constraint, actual.asJson)
def conflict[A: Encoder](actual: A): Violation = Conflict(actual.asJson)
def invalid[A: Encoder](reference: A, actual: A): Violation = Invalid(reference.asJson.some, actual.asJson)
def invalid[A: Encoder](actual: A): Violation = Invalid(reference = none, actual.asJson)
def apply[A: Encoder](constraint: Constraint, actual: A): Violation = Validation(constraint, actual.asOpenApi)
def conflict[A: Encoder](actual: A): Violation = Conflict(actual.asOpenApi)
def invalid[A: Encoder, B: Encoder](reference: A, actual: B): Violation =
Invalid(reference.asOpenApi.some, actual.asOpenApi)
def invalid[A: Encoder](actual: A): Violation = Invalid(reference = none, actual.asOpenApi)
val missing: Violation = Missing(reference = none)
def missing[A: Encoder](reference: A): Violation = Missing(reference.asJson.some)
def unknown[A: Encoder](actual: A): Violation = Unknown(actual.asJson)
def missing[A: Encoder](reference: A): Violation = Missing(reference.asOpenApi.some)
def unknown[A: Encoder](actual: A): Violation = Unknown(actual.asOpenApi)
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package io.hireproof
import cats.data.ValidatedNel
import cats.syntax.all._

package object screening extends CirceInstances {
package object screening extends OpenApiInstances {
val hist: Selection.History = Selection.History.Root

implicit class RichValidatedNel[E, A](val validated: ValidatedNel[E, A]) extends AnyVal {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
package io.hireproof.screening

import cats.data.Chain
import cats.syntax.all._
import cats.{Eq, Traverse, UnorderedFoldable}
import io.circe.{Decoder, Encoder, Json, JsonObject}
import io.circe.parser.parse
import io.hireproof.openapi.Encoder

import java.time._
import java.time.format.DateTimeParseException
Expand Down Expand Up @@ -85,26 +83,6 @@ object validations {
Validation.condNel(Constraint.duration.equal(reference))(_ == reference)
}

object json {
def apply[A: Decoder](reference: String): Validation[Json, A] =
Validation.fromOptionNel(Constraint.json(reference))(_.as[A].toOption)

def field(name: String): Validation[Json, Json] =
Validation.fromOptionNel(Constraint.json(reference = name))(_.hcursor.downField(name).focus)

def index(i: Int): Validation[Json, Json] =
Validation.fromOptionNel(Constraint.json(reference = String.valueOf(i)))(_.hcursor.downN(i).focus)

val array: Validation[Json, Chain[Json]] =
Validation.fromOptionNel(Constraint.json(reference = "[]"))(_.as[Chain[Json]].toOption)

val obj: Validation[Json, JsonObject] =
Validation.fromOptionNel(Constraint.json(reference = "{}"))(_.as[JsonObject].toOption)

val string: Validation[Json, String] =
Validation.fromOptionNel(Constraint.json(reference = "String"))(_.asString)
}

object number {
def equal[I: Numeric: Encoder](reference: I, delta: I): Validation[I, Unit] =
Validation.condNel[I](Constraint.number.equal(reference, delta.toDouble)) { input =>
Expand Down Expand Up @@ -183,7 +161,6 @@ object validations {
val offsetDateTime: Validation[String, OffsetDateTime] =
catchOnly[DateTimeParseException]("offsetDateTime")(OffsetDateTime.parse)
val offsetTime: Validation[String, OffsetTime] = catchOnly[DateTimeParseException]("offsetTime")(OffsetTime.parse)
val json: Validation[String, Json] = parsing("json")(parse(_).toOption)
val short: Validation[String, Short] = parsing("short")(_.toShortOption)
val uuid: Validation[String, UUID] = catchOnly[IllegalArgumentException]("uuid")(UUID.fromString)
val zonedDateTime: Validation[String, ZonedDateTime] =
Expand Down