Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for dynamic enum validation #384

Merged
merged 2 commits into from
Feb 21, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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(
schema: Schema.EnumerationSchema[Int]
): Schema[Int] = schema
.reifyHint(RefinementProvider.lengthConstraint(schema.total(_).stringValue.size))
.reifyHint(RefinementProvider.rangeConstraint[Int, Int](schema.total(_).intValue))
.reifyHint(RefinementProvider.patternConstraint(schema.total(_).stringValue))

def apply[A](
schema: Schema[A]
): Schema[A] =
Expand All @@ -69,10 +76,12 @@ 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.asInstanceOf[EnumerationSchema[Int]])
kubukoz marked this conversation as resolved.
Show resolved Hide resolved
.asInstanceOf[Schema[A]]
// 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"
]
}