Skip to content

Commit

Permalink
Add support for dynamic enum validation (#384)
Browse files Browse the repository at this point in the history
* Add support for dynamic enum validation

* We don't need no casting

---------

Co-authored-by: Jakub Kozłowski <[email protected]>
  • Loading branch information
msosnicki and kubukoz authored Feb 21, 2024
1 parent 72ded79 commit 60955c8
Show file tree
Hide file tree
Showing 6 changed files with 91 additions and 7 deletions.
6 changes: 3 additions & 3 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ lazy val source = module("source")
lazy val parser = module("parser")
.settings(
libraryDependencies ++= Seq(
"org.typelevel" %% "cats-parse" % "0.3.10",
"org.typelevel" %% "cats-parse" % "1.0.0",
"io.circe" %% "circe-generic" % "0.14.6" % Test,
"io.circe" %% "circe-parser" % "0.14.6" % Test,
"co.fs2" %% "fs2-io" % "3.9.4" % Test,
Expand Down Expand Up @@ -154,8 +154,8 @@ lazy val lsp = module("lsp")
libraryDependencies ++= Seq(
"org.eclipse.lsp4j" % "org.eclipse.lsp4j" % "0.21.2",
"io.circe" %% "circe-core" % "0.14.6",
"org.http4s" %% "http4s-ember-client" % "0.23.23",
"org.http4s" %% "http4s-ember-server" % "0.23.23" % Test,
"org.http4s" %% "http4s-ember-client" % "0.23.25",
"org.http4s" %% "http4s-ember-server" % "0.23.25" % Test,
"io.get-coursier" %% "coursier" % "2.1.9",
"org.typelevel" %% "cats-tagless-core" % "0.15.0",
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,13 @@ object AddDynamicRefinements extends (Schema ~> Schema) {
schema.reifyHint(RefinementProvider.iterableLengthConstraint[IndexedSeq, A])
}

private def enumSchema[A](
schema: Schema.EnumerationSchema[A]
): Schema[A] = schema
.reifyHint(RefinementProvider.lengthConstraint(schema.total(_).stringValue.size))
.reifyHint(RefinementProvider.rangeConstraint[A, Int](schema.total(_).intValue))
.reifyHint(RefinementProvider.patternConstraint(schema.total(_).stringValue))

def apply[A](
schema: Schema[A]
): Schema[A] =
Expand All @@ -69,10 +76,10 @@ object AddDynamicRefinements extends (Schema ~> Schema) {

case c: CollectionSchema[_, _] => collection(c)
case m: MapSchema[_, _] => m.reifyHint[api.Length]
// explicitly handling each remaining case, in order to get a "mising match" warning if the schema model changes
case e: EnumerationSchema[_] => enumSchema(e)
// explicitly handling each remaining case, in order to get a "missing match" warning if the schema model changes
case b: BijectionSchema[_, _] => b
case r: RefinementSchema[_, _] => r
case e: EnumerationSchema[_] => e
case s: StructSchema[_] => s
case l: LazySchema[_] => l
case u: UnionSchema[_] => u
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import com.softwaremill.diffx.Diff
import com.softwaremill.diffx.cats._
import demo.smithy.Bad
import demo.smithy.DeprecatedServiceGen
import demo.smithy.EnumStruct
import demo.smithy.FriendSet
import demo.smithy.Good
import demo.smithy.HasConstraintFields
Expand Down Expand Up @@ -732,6 +733,57 @@ object CompilationTests extends SimpleIOSuite with Checkers {
)
}

pureTest("enum - length validation (dynamic, OK)") {
assert.same(
Ior.right(Document.obj("enumWithLength" -> Document.fromString("AB"))),
compile(
WithSource.liftId(struct("enumWithLength" -> "AB").mapK(WithSource.liftId))
)(dynamicSchemaFor[EnumStruct]),
)
}

pureTest("enum - length validation (dynamic, fail)") {
assert(
compile(
WithSource.liftId(struct("enumWithLength" -> "ABC").mapK(WithSource.liftId))
)(dynamicSchemaFor[EnumStruct]).isLeft
)
}

pureTest("enum - range validation (dynamic, OK)") {
assert.same(
Ior.right(Document.obj("intEnumWithRange" -> Document.fromInt(2))),
compile(
WithSource.liftId(struct("intEnumWithRange" -> "QUEEN").mapK(WithSource.liftId))
)(dynamicSchemaFor[EnumStruct]),
)
}

pureTest("enum - range validation (dynamic, fail)") {
assert(
compile(
WithSource.liftId(struct("intEnumWithRange" -> "KING").mapK(WithSource.liftId))
)(dynamicSchemaFor[EnumStruct]).isLeft
)
}

pureTest("enum - pattern validation (dynamic, OK)") {
assert.same(
Ior.right(Document.obj("enumWithPattern" -> Document.fromString("AB"))),
compile(
WithSource.liftId(struct("enumWithPattern" -> "AB").mapK(WithSource.liftId))
)(dynamicSchemaFor[EnumStruct]),
)
}

pureTest("enum - pattern validation (dynamic, fail)") {
assert(
compile(
WithSource.liftId(struct("enumWithPattern" -> "ABC").mapK(WithSource.liftId))
)(dynamicSchemaFor[EnumStruct]).isLeft
)
}

pureTest("enum - fallback to string value") {
implicit val diffPower: Diff[Power] = Diff.derived

Expand Down
25 changes: 25 additions & 0 deletions modules/core/src/test/smithy/demo.smithy
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,31 @@ string MyString
@length(min: 1)
string StringWithLength

structure EnumStruct {
@length(max: 2)
enumWithLength: EnumABC

@pattern("^.{0,2}$")
enumWithPattern: EnumABC

@range(max: 2)
intEnumWithRange: FaceCard
}

enum EnumABC {
A,
AB,
ABC
}

intEnum FaceCard {
JACK = 1
QUEEN = 2
KING = 3
ACE = 4
JOKER = 5
}

structure HasConstraintFields {
@required
minLength: StringWithLength
Expand Down
2 changes: 1 addition & 1 deletion project/plugins.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ addSbtPlugin("com.github.sbt" % "sbt-ci-release" % "1.5.11")
addSbtPlugin("io.github.davidgregory084" % "sbt-tpolecat" % "0.4.3")

// try to keep in sync with smithy-build.json
addSbtPlugin("com.disneystreaming.smithy4s" % "smithy4s-sbt-codegen" % "0.18.0")
addSbtPlugin("com.disneystreaming.smithy4s" % "smithy4s-sbt-codegen" % "0.18.8")

addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.11.0")
addSbtPlugin("com.typesafe" % "sbt-mima-plugin" % "1.1.1")
Expand Down
2 changes: 1 addition & 1 deletion smithy-build.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"imports": ["modules/core/src/test/smithy"],
"mavenDependencies": [
"com.disneystreaming.alloy:alloy-core:0.2.8",
"com.disneystreaming.smithy4s:smithy4s-protocol:0.18.2",
"com.disneystreaming.smithy4s:smithy4s-protocol:0.18.8",
"software.amazon.smithy:smithy-aws-traits:1.45.0"
]
}

0 comments on commit 60955c8

Please sign in to comment.