Skip to content

Commit

Permalink
Prevent default values from being rendered for refined fields (#1487)
Browse files Browse the repository at this point in the history
Fixes a bug that would happen when a refinement trait would be used
on a member rather than a top-level shape, that would allow for default
values of the wrong type to be rendered for the case class field.

This corrects the behaviour by preventing the rendering of default values
altogether for case-class fields that are refined

Co-authored-by: Jakub Kozłowski <kubukoz@gmail.com>
  • Loading branch information
Baccata and kubukoz committed Apr 17, 2024
1 parent b6c7897 commit 4295aa0
Show file tree
Hide file tree
Showing 6 changed files with 44 additions and 49 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
@@ -1,5 +1,6 @@
# 0.18.16

* Fixes bug leading to refined case-class fields being rendered with default values of the wrong type
* Adds a `smithy4s-protobuf` module, containing derivation logic for protobuf codecs. See https://github.com/disneystreaming/smithy4s/pull/1455
* Add support for converting smithy4s services and schemas to smithy models
* Add `smithy4s.meta#only` annotation allowing to filter operations in
Expand Down
Expand Up @@ -10,9 +10,7 @@ import smithy4s.schema.Schema.int

object Age extends Newtype[smithy4s.refined.Age] {
val id: ShapeId = ShapeId("smithy4s.example", "Age")
val hints: Hints = Hints(
smithy.api.Default(smithy4s.Document.fromDouble(0.0d)),
).lazily
val hints: Hints = Hints.empty
val underlyingSchema: Schema[smithy4s.refined.Age] = int.refined[smithy4s.refined.Age](smithy4s.example.AgeFormat()).withId(id).addHints(hints)
implicit val schema: Schema[Age] = bijection(underlyingSchema, asBijection)
}
Expand Up @@ -10,9 +10,7 @@ import smithy4s.schema.Schema.int

object PersonAge extends Newtype[smithy4s.refined.Age] {
val id: ShapeId = ShapeId("smithy4s.example", "PersonAge")
val hints: Hints = Hints(
smithy.api.Default(smithy4s.Document.fromDouble(0.0d)),
).lazily
val hints: Hints = Hints.empty
val underlyingSchema: Schema[smithy4s.refined.Age] = int.refined[smithy4s.refined.Age](smithy4s.example.AgeFormat()).withId(id).addHints(hints)
implicit val schema: Schema[PersonAge] = bijection(underlyingSchema, asBijection)
}
Expand Up @@ -4,25 +4,28 @@ import smithy4s.Hints
import smithy4s.Schema
import smithy4s.ShapeId
import smithy4s.ShapeTag
import smithy4s.refined.Age.provider._
import smithy4s.schema.Schema.int
import smithy4s.schema.Schema.struct

final case class StructureWithRefinedTypes(age: Age, personAge: PersonAge, requiredAge: Age, fancyList: Option[smithy4s.example.FancyList] = None, unwrappedFancyList: Option[smithy4s.refined.FancyList] = None, name: Option[smithy4s.example.Name] = None, dogName: Option[smithy4s.refined.Name] = None)
final case class StructureWithRefinedTypes(requiredAge: smithy4s.example.Age, personAge: PersonAge, inlineFieldConstraint: smithy4s.refined.Age, age: Option[smithy4s.example.Age] = None, fancyList: Option[smithy4s.example.FancyList] = None, unwrappedFancyList: Option[smithy4s.refined.FancyList] = None, name: Option[smithy4s.example.Name] = None, dogName: Option[smithy4s.refined.Name] = None)

object StructureWithRefinedTypes extends ShapeTag.Companion[StructureWithRefinedTypes] {
val id: ShapeId = ShapeId("smithy4s.example", "StructureWithRefinedTypes")

val hints: Hints = Hints.empty

// constructor using the original order from the spec
private def make(age: Age, personAge: PersonAge, requiredAge: Age, fancyList: Option[smithy4s.example.FancyList], unwrappedFancyList: Option[smithy4s.refined.FancyList], name: Option[smithy4s.example.Name], dogName: Option[smithy4s.refined.Name]): StructureWithRefinedTypes = StructureWithRefinedTypes(age, personAge, requiredAge, fancyList, unwrappedFancyList, name, dogName)
private def make(age: Option[smithy4s.example.Age], personAge: PersonAge, requiredAge: smithy4s.example.Age, fancyList: Option[smithy4s.example.FancyList], unwrappedFancyList: Option[smithy4s.refined.FancyList], name: Option[smithy4s.example.Name], dogName: Option[smithy4s.refined.Name], inlineFieldConstraint: smithy4s.refined.Age): StructureWithRefinedTypes = StructureWithRefinedTypes(requiredAge, personAge, inlineFieldConstraint, age, fancyList, unwrappedFancyList, name, dogName)

implicit val schema: Schema[StructureWithRefinedTypes] = struct(
Age.schema.field[StructureWithRefinedTypes]("age", _.age).addHints(smithy.api.Default(smithy4s.Document.fromDouble(0.0d))),
PersonAge.schema.field[StructureWithRefinedTypes]("personAge", _.personAge).addHints(smithy.api.Default(smithy4s.Document.fromDouble(0.0d))),
Age.schema.required[StructureWithRefinedTypes]("requiredAge", _.requiredAge).addHints(smithy.api.Default(smithy4s.Document.fromDouble(0.0d))),
smithy4s.example.Age.schema.optional[StructureWithRefinedTypes]("age", _.age),
PersonAge.schema.field[StructureWithRefinedTypes]("personAge", _.personAge).addHints(smithy.api.Default(smithy4s.Document.fromDouble(1.0d))),
smithy4s.example.Age.schema.required[StructureWithRefinedTypes]("requiredAge", _.requiredAge),
smithy4s.example.FancyList.schema.optional[StructureWithRefinedTypes]("fancyList", _.fancyList),
UnwrappedFancyList.underlyingSchema.optional[StructureWithRefinedTypes]("unwrappedFancyList", _.unwrappedFancyList),
smithy4s.example.Name.schema.optional[StructureWithRefinedTypes]("name", _.name),
DogName.underlyingSchema.optional[StructureWithRefinedTypes]("dogName", _.dogName),
int.refined[smithy4s.refined.Age](smithy4s.example.AgeFormat()).field[StructureWithRefinedTypes]("inlineFieldConstraint", _.inlineFieldConstraint).addHints(smithy.api.Default(smithy4s.Document.fromDouble(1.0d))),
)(make).withId(id).addHints(hints)
}
Expand Up @@ -862,7 +862,7 @@ private[codegen] class SmithyToIR(model: Model, namespace: String) {
}
}
val node = tr.toNode()
val targetTpe = shape.getTarget.tpe.get
val targetTpe = shape.tpe.get
// Constructing the initial value for the refold
val nodeAndType = targetTpe match {
case Alias(_, _, tpe, true) => NodeAndType(node, tpe)
Expand Down
69 changes: 32 additions & 37 deletions sampleSpecs/refined.smithy
@@ -1,64 +1,57 @@
$version: "2"

namespace smithy4s.example

use smithy4s.meta#refinement
use smithy4s.meta#unwrap

@trait(selector: ":test(integer, member > integer)")
@refinement(
targetType: "smithy4s.refined.Age",
providerImport: "smithy4s.refined.Age.provider._"
targetType: "smithy4s.refined.Age",
providerImport: "smithy4s.refined.Age.provider._"
)
structure ageFormat {}

@trait(selector: "list:test(> member > string)") // lists with string members
@refinement(
targetType: "smithy4s.refined.FancyList"
)
@trait(selector: "list:test(> member > string)")
// lists with string members
@refinement(targetType: "smithy4s.refined.FancyList")
structure fancyListFormat {}

@trait(selector: "string")
@refinement(
targetType: "smithy4s.refined.Name"
)
@refinement(targetType: "smithy4s.refined.Name")
structure nameFormat {}

@trait(selector: "list")
@refinement(
targetType: "smithy4s.refined.NonEmptyList",
parameterised: true
)
@refinement(targetType: "smithy4s.refined.NonEmptyList", parameterised: true)
structure nonEmptyListFormat {}

@trait(selector: "map")
@refinement(
targetType: "smithy4s.refined.NonEmptyMap",
parameterised: true
)
@refinement(targetType: "smithy4s.refined.NonEmptyMap", parameterised: true)
structure nonEmptyMapFormat {}

@nonEmptyListFormat
list NonEmptyStrings {
member: String
member: String
}

@nonEmptyListFormat
list NonEmptyNames {
member: Name
member: Name
}

structure Candy {
name: String
name: String
}

@nonEmptyListFormat
list NonEmptyCandies {
member: Candy
member: Candy
}

@nonEmptyMapFormat
map NonEmptyMapNumbers {
key: String
value: Integer
key: String
value: Integer
}

@ageFormat
Expand All @@ -69,13 +62,13 @@ integer PersonAge

@fancyListFormat
list FancyList {
member: String
member: String
}

@fancyListFormat
@unwrap
list UnwrappedFancyList {
member: String
member: String
}

@nameFormat
Expand All @@ -86,22 +79,24 @@ string Name
string DogName

structure StructureWithRefinedTypes {
age: Age,
personAge: PersonAge,
@required
requiredAge: Age,
fancyList: FancyList,
unwrappedFancyList: UnwrappedFancyList,
name: Name,
dogName: DogName
age: Age
personAge: PersonAge = 1
@required
requiredAge: Age
fancyList: FancyList
unwrappedFancyList: UnwrappedFancyList
name: Name
dogName: DogName
@ageFormat
inlineFieldConstraint: Integer = 1
}

union UnionWithRefinedTypes {
age: Age,
dogName: DogName
age: Age
dogName: DogName
}

structure StructureWithRefinedMember {
@ageFormat
otherAge: Integer,
@ageFormat
otherAge: Integer
}

0 comments on commit 4295aa0

Please sign in to comment.