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

UrlForm decoding support #1113

Merged
merged 43 commits into from
Aug 16, 2023
Merged

UrlForm decoding support #1113

merged 43 commits into from
Aug 16, 2023

Conversation

dhpiggott
Copy link
Contributor

@dhpiggott dhpiggott commented Jul 21, 2023

Follow up to #1120.

Related: disneystreaming/alloy#110

Changes:

  1. Add URL form parsing and decoding support.
  2. Update tests to cover parsing and decoding.
  3. Handle UrlForm model renaming TODOs.
  4. Simplify UrlForm model by making encoder and decoder work with lists.
  5. Fix bug in Blob and add regression test.
  6. Make URL form schema visitors work using urlFormFlattened and urlFormName from alloy so they're as AWS protocol agnostic as possible. AWS protocol codecs now translate xmlFlattened to urlFormFlattened and xmlName to urlFormName before passing schemas to the visitors.
  7. Make sandbox AWS specific.

This reads more easily and makes redundancy more obvious.
Same idea as the last commit, it makes reasoning about the operations
that occur on the data easier.
It's not necessary.
Everything that this covered is now covered by
UrlFormDataEncoderDecoderSchemaVisitorSpec.
There's not enough in common between that function and what OAuthCodecs
does for lifting it out to have benefit.
PayloadCodec is used for Jsoniter based reading/writing because Jsonitor
has a single trait for the read and write behaviour, and that's why
PayloadCodec is used in those code paths. But for cases like this where
reading and writing code paths are relatively separate, there's no
benefit to combining them in a PayloadCodec.
It's not clear there is benefit to pushing string encoding into Blob.
There probably isn't.

The bug isn't something I've seen any impact from, I just noticed it
while exploring the feasibility/benefit of handling the TODO.
@dhpiggott dhpiggott marked this pull request as ready for review August 11, 2023 16:28
@@ -29,7 +29,7 @@ object Dependencies {

val Alloy = new {
val org = "com.disneystreaming.alloy"
val alloyVersion = "0.2.5"
val alloyVersion = "0.2.6"
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This brings in urlFormFlattened and urlFormName from disneystreaming/alloy#110.

@@ -621,27 +607,20 @@ object XmlCodecSpec extends SimpleIOSuite {
.map(result => expect.same(result, expected))
}

// Decode document differs from decode content in that the top-level
// tag is checked against the ShapeId
private def decodeDocument[A: Schema](document: XmlDocument): IO[A] = {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I couldn't figure out why this was necessary. I believe I've been able to simplify things without losing anything, but might be worth a second opinion.

@@ -900,14 +900,14 @@ lazy val sandbox = projectMatrix
// Ignore deprecation warnings here - it's all generated code, anyway.
scalacOptions ++= Seq(
"-Wconf:cat=deprecation:silent"
) ++ scala3MigrationOption(scalaVersion.value),
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't remember what this was doing now but I think it started causing a problem, and didn't seem to be necessary.

}
object FormData {
case object Empty extends FormData {
final case class FormData(path: PayloadPath, maybeValue: Option[String]) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Significant model simplification here. FormData is no longer a coproduct. The various places that need to handle it can do so with less code that's also more readable.

val altMap = alternatives
.map(altDecoder(_))
.toMap[PayloadPath.Segment, UrlFormDataDecoder[U]]
({
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

locally is your friend. The parenthesis made me think this was a function application

Comment on lines 85 to 98
.groupBy { case (index, _) =>
index
}
.toVector
.sortBy { case (index, _) =>
index
}
.map { case (index, indicesAndValues) =>
UrlFormCursor(
history,
indicesAndValues.map { case (_, value) => value }
)
.down(PayloadPath.Segment.Index(index))
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you could use groupMap to make it more efficient. It's available in 2.12 thanks to scala-collection-compat

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 Done in e28d74d.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And for 2.12 in b0ad4eb.

.down(PayloadPath.Segment.Index(index))
}
groupedAndSortedCursors
.traverse[UrlFormDecodeError, A](memberDecoder.decode(_))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if the toVector is only there to satisfy the traverse, we can change/overload our traverse to accommodate the data structure and save an un-necessary .toVector call

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is and it isn't. The result of groupBy (and groupMap) is a Map, but we have to sort things before producing a result so that we're not making assumptions about input order. Map isn't sortable. I did initially write toList and puzzle over the compile error, before changing it to toVector. So it's toVector rather than toList to satisfy the traverse, but not toVector rather nothing to satisfy the traverse.

// be completely agnostic of AWS protocol details, they work with their
// own more appropriately named hints - which is what necessitates the
// translation here.
Schema.transformHintsTransitivelyK { hints =>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 (and thanks for the comment)

@Baccata
Copy link
Contributor

Baccata commented Aug 14, 2023

Good stuff overall, just a couple low-hanging fruits to address related to efficiency of list decoding

Also remove unused ScalaCompat conversion and references.
@@ -28,9 +28,4 @@ private[smithy4s] trait ScalaCompat {
private def opt[A](a: => A): Option[A] = try { Some(a) }
catch { case scala.util.control.NonFatal(_) => None }
}

private[smithy4s] implicit final class MapOps[K, V](val map: Map[K, V]) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couldn't see this being used anywhere.

@@ -16,8 +16,4 @@

package smithy4s

private[smithy4s] trait ScalaCompat {
implicit final class MapOps[K, V](val map: Map[K, V]) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couldn't see this being used anywhere.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should I merge the 2.13 and 3 ScalaCompat versions under src?

@@ -16,8 +16,4 @@

package smithy4s

private[smithy4s] trait ScalaCompat {
implicit final class MapOps[K, V](val map: Map[K, V]) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couldn't see this being used anywhere.

@@ -30,8 +30,7 @@ import cats.kernel.Monoid

private[smithy4s] class XmlEncoderSchemaVisitor(
val cache: CompilationCache[XmlEncoder]
) extends SchemaVisitor.Cached[XmlEncoder]
with smithy4s.ScalaCompat { compile =>
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused.

Copy link
Contributor

@Baccata Baccata left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good work !

@Baccata Baccata merged commit 65cc9c0 into disneystreaming:series/0.18 Aug 16, 2023
10 checks passed
@dhpiggott dhpiggott deleted the url-form-decoding-support branch August 16, 2023 09:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants