Skip to content

Commit

Permalink
Merge pull request #49 from disneystreaming/discriminated-whitespace-…
Browse files Browse the repository at this point in the history
…repro

Allow decoding of discriminated unions with whitespace
  • Loading branch information
Baccata authored Jan 16, 2022
2 parents 8070f42 + a7c6194 commit e3d718e
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 24 deletions.
5 changes: 4 additions & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -421,7 +421,10 @@ lazy val openapi = projectMatrix
*/
lazy val json = projectMatrix
.in(file("modules/json"))
.dependsOn(core, `scalacheck` % "test -> compile")
.dependsOn(
core % "test->test;compile->compile",
`scalacheck` % "test -> compile"
)
.settings(
isCE3 := true,
libraryDependencies ++= Seq(
Expand Down
37 changes: 16 additions & 21 deletions modules/json/src/smithy4s/http/json/SchematicJCodec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -565,29 +565,24 @@ private[smithy4s] class SchematicJCodec(constraints: Constraints, maxArity: Int)

def decodeValue(cursor: Cursor, in: JsonReader): Z = {
if (in.isNextToken('{')) {
if (in.isNextToken('}'))
in.decodeError("Expected at least a one key/value pair")
else {
in.setMark()
val key = if (in.skipToKey(discriminated.value)) {
val k = in.readString("")
in.rollbackToMark()
in.rollbackToken()
in.setMark()
val key = if (in.skipToKey(discriminated.value)) {
val k = in.readString("")
in.rollbackToMark()
in.rollbackToken()
k
} else {
in.decodeError(
s"Unable to find discriminator ${discriminated.value}"
)
}
val result = cursor.under(key) {
handlerMap.get(key).map(_.apply(cursor, in))
} match {
case Some(value) => value
case None => in.discriminatorValueError(key)
}
result
k
} else {
in.decodeError(
s"Unable to find discriminator ${discriminated.value}"
)
}
val result = cursor.under(key) {
handlerMap.get(key).map(_.apply(cursor, in))
} match {
case Some(value) => value
case None => in.discriminatorValueError(key)
}
result
} else in.decodeError("Expected JSON object")
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
package smithy4s
package http.json

import com.github.plokhotnyuk.jsoniter_scala.core._
import com.github.plokhotnyuk.jsoniter_scala.core.{readFromString => _, _}
import schematic.ByteArray
import smithy.api.JsonName
import smithy4s.http.PayloadError
Expand All @@ -26,6 +26,9 @@ import weaver._
import smithy4s.api.Discriminated

import scala.collection.immutable.ListMap
import smithy4s.example.PayloadData
import smithy4s.example.TestBiggerUnion
import smithy4s.example.One

object SchematicJCodecTests extends SimpleIOSuite {

Expand All @@ -38,6 +41,16 @@ object SchematicJCodecTests extends SimpleIOSuite {
}
}

private val readerConfig: ReaderConfig = ReaderConfig
.withThrowReaderExceptionWithStackTrace(true)
.withAppendHexDumpToParseException(true)
.withCheckForEndOfInput(false)

def readFromString[A: JsonCodec](str: String): A = {
com.github.plokhotnyuk.jsoniter_scala.core
.readFromString[A](str, readerConfig)
}

import JCodec.deriveJCodecFromSchema

case class IntList(head: Int, tail: Option[IntList] = None)
Expand Down Expand Up @@ -158,6 +171,36 @@ object SchematicJCodecTests extends SimpleIOSuite {
expect(bin == jsonBin)
}

pureTest("Discriminated union decoding tolerates whitespace") {
val json = """ { "tpe" : "one" , "value" : "hello" }"""
val result = readFromString[TestBiggerUnion](json)

expect(
result == TestBiggerUnion.OneCase(One(Some("hello")))
)
}

pureTest("Discriminated union discriminator can follow other keys") {
val json = """ { "value" : "hello", "tpe" : "one" }"""
val result = readFromString[TestBiggerUnion](json)

expect(
result == TestBiggerUnion.OneCase(One(Some("hello")))
)
}

pureTest("Nested discriminated union decoding tolerates whitespace") {
val json = """{ "testBiggerUnion": { "tpe": "one", "value": "hello" } }"""
val result = readFromString[PayloadData](json)

expect(
result == PayloadData(
None,
Some(TestBiggerUnion.OneCase(One(Some("hello"))))
)
)
}

pureTest("Discriminated union gets routed to the correct codec") {
val jsonBaz = """{"type":"baz","str":"test"}"""
val jsonBin = """{"type":"binBin","binStr":"foo","int":2022}"""
Expand Down
17 changes: 16 additions & 1 deletion sampleSpecs/discriminated.smithy
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,26 @@ structure TestDiscriminatedOutput {
}

structure PayloadData {
testUnion: TestUnion
testUnion: TestUnion,
testBiggerUnion: TestBiggerUnion
}

@discriminated("type")
union TestUnion {
one: String,
two: Integer
}

@discriminated("tpe")
union TestBiggerUnion {
one: One,
two: Two
}

structure One {
value: String
}

structure Two {
value: Integer
}

0 comments on commit e3d718e

Please sign in to comment.