-
Notifications
You must be signed in to change notification settings - Fork 72
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
Baccata
merged 43 commits into
disneystreaming:series/0.18
from
dhpiggott:url-form-decoding-support
Aug 16, 2023
Merged
UrlForm decoding support #1113
Changes from 38 commits
Commits
Show all changes
43 commits
Select commit
Hold shift + click to select a range
7ab3c8d
Update tests to cover decoding, fix various bugs uncovered by them
dhpiggott e20d0d3
Consolidate cursor behaviour within UrlFormCursor
dhpiggott 5d0e251
Remove redundant FailedValue cursor type
dhpiggott 7accb71
Use headOption, not head
dhpiggott c4d1ae6
Further cursor simplification
dhpiggott bc174d2
Handle renaming TODOs
dhpiggott a5fc091
Flatten UrlForm types by making encoder and decoder cursor work with …
dhpiggott 5ceae82
Flatten UrlFormCursor
dhpiggott e2466f5
Handle TODO re. DRYing error messages
dhpiggott 679c682
Remove TODO
dhpiggott c100080
Make spec test primitive decoding too
dhpiggott e99c5cb
Remove redundant UrlFormParserSpec
dhpiggott 06cf841
Fix codecs
dhpiggott e1c8cd0
First pass of OAuthCodecs
dhpiggott 149f826
First pass of sandbox clients and servers
dhpiggott b60496d
Fix copyright headers
dhpiggott 83d6d3c
Apply suggestions from code review
dhpiggott a96480c
Scalafmt
dhpiggott 2701b0f
Remove redundant dependency
dhpiggott 5ac670f
Convert oauth-sandbox apps to Cats Effect
dhpiggott 1f43fd4
Move OAuthCodecs to http4s module
dhpiggott ddb8f30
Remove redundant TODO
dhpiggott 4ed323e
Remove TODO re. PayloadCodec
dhpiggott 0e97a74
Remove redundant TODO and fix bug in Blob
dhpiggott 9068eae
Revert previous bug fix, add test, verify it fails
dhpiggott 4a42267
Reapply bug fix, verify new test passes
dhpiggott ecfbeee
Fold UrlFormParser into UrlForm, restrict internals visibility
dhpiggott 7f9e8d0
Scalafmt
dhpiggott 58d39eb
Update TODOs re. downstreaming
dhpiggott 40b6754
Remove queryFlattened and queryName
dhpiggott ed9b4dc
Make URL form support work using urlFormFlattened and urlFormName
dhpiggott e4337de
Fix OAuthCodecs
dhpiggott 841fa69
Merge branch 'series/0.18' into url-form-decoding-support
dhpiggott eccb3b7
Fix enum handling
dhpiggott 824c479
Remove OAuthCodecs and oauth-sandbox
dhpiggott dfe3eb2
Fix aws-sandbox directory
dhpiggott ffba6d7
Merge branch 'series/0.18' into url-form-decoding-support
dhpiggott e8d0a89
Fix imports
dhpiggott 8c081b6
Use locally for readability
dhpiggott e28d74d
Use groupMap
dhpiggott 18a723d
s/list/vector/
dhpiggott b0ad4eb
Fix use of groupMap for Scala 2.12
dhpiggott 033173c
Oops, too hasty with "dead" code removal
dhpiggott File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,18 +18,21 @@ package smithy4s.aws | |
package internals | ||
|
||
import _root_.aws.protocols.AwsQueryError | ||
import alloy.UrlFormFlattened | ||
import alloy.UrlFormName | ||
import cats.effect.Concurrent | ||
import cats.syntax.all._ | ||
import fs2.compression.Compression | ||
import org.http4s.EntityEncoder | ||
import smithy4s.Endpoint | ||
import smithy.api.XmlFlattened | ||
import smithy.api.XmlName | ||
import smithy4s._ | ||
import smithy4s.codecs.PayloadPath | ||
import smithy4s.http.Metadata | ||
import smithy4s.http._ | ||
import smithy4s.http4s.kernel._ | ||
import smithy4s.kinds.PolyFunction | ||
import smithy4s.schema.CachedSchemaCompiler | ||
import smithy4s.Blob | ||
import smithy4s.xml.internals.XmlStartingPath | ||
|
||
private[aws] object AwsQueryCodecs { | ||
|
||
|
@@ -48,7 +51,7 @@ private[aws] object AwsQueryCodecs { | |
) | ||
val requestEncoderCompilersWithCompression = transformEncoders( | ||
requestEncoderCompilers[F]( | ||
ignoreXmlFlattened = false, | ||
ignoreUrlFormFlattened = false, | ||
capitalizeStructAndUnionMemberNames = false, | ||
action = endpoint.id.name, | ||
version = version | ||
|
@@ -61,23 +64,15 @@ private[aws] object AwsQueryCodecs { | |
AwsXmlCodecs | ||
.responseDecoderCompilers[F] | ||
.contramapSchema( | ||
smithy4s.schema.Schema.transformHintsLocallyK( | ||
_ ++ smithy4s.Hints( | ||
smithy4s.xml.internals.XmlStartingPath( | ||
List(responseTag, resultTag) | ||
) | ||
) | ||
Schema.transformHintsLocallyK( | ||
_ ++ Hints(XmlStartingPath(List(responseTag, resultTag))) | ||
) | ||
) | ||
val errorDecoderCompilers = AwsXmlCodecs | ||
.responseDecoderCompilers[F] | ||
.contramapSchema( | ||
smithy4s.schema.Schema.transformHintsLocallyK( | ||
_ ++ smithy4s.Hints( | ||
smithy4s.xml.internals.XmlStartingPath( | ||
List("ErrorResponse", "Error") | ||
) | ||
) | ||
Schema.transformHintsLocallyK( | ||
_ ++ Hints(XmlStartingPath(List("ErrorResponse", "Error"))) | ||
) | ||
) | ||
// Takes the `@awsQueryError` trait into consideration to decide how to | ||
|
@@ -91,14 +86,15 @@ private[aws] object AwsQueryCodecs { | |
val shapeName = alt.schema.shapeId.name | ||
alt.hints.get(AwsQueryError).map(_.code).map(_ -> shapeName) | ||
}.toMap | ||
(errorCode: String) => mapping.getOrElse(errorCode, errorCode) | ||
errorCode => mapping.getOrElse(errorCode, errorCode) | ||
} | ||
val errorDiscriminator = AwsErrorTypeDecoder | ||
.fromResponse(errorDecoderCompilers) | ||
.andThen(_.map(_.map { | ||
case HttpDiscriminator.NameOnly(name) => | ||
HttpDiscriminator.NameOnly(errorNameMapping(name)) | ||
case other => other | ||
case other => | ||
other | ||
})) | ||
|
||
val make = UnaryClientCodecs.Make[F]( | ||
|
@@ -112,43 +108,63 @@ private[aws] object AwsQueryCodecs { | |
} | ||
|
||
def requestEncoderCompilers[F[_]: Concurrent]( | ||
ignoreXmlFlattened: Boolean, | ||
ignoreUrlFormFlattened: Boolean, | ||
capitalizeStructAndUnionMemberNames: Boolean, | ||
action: String, | ||
version: String | ||
): CachedSchemaCompiler[RequestEncoder[F, *]] = { | ||
val urlFormEntityEncoderCompilers = UrlForm | ||
.Encoder( | ||
ignoreXmlFlattened = ignoreXmlFlattened, | ||
ignoreUrlFormFlattened = ignoreUrlFormFlattened, | ||
capitalizeStructAndUnionMemberNames = | ||
capitalizeStructAndUnionMemberNames | ||
) | ||
.mapK( | ||
new PolyFunction[UrlForm.Encoder, EntityEncoder[F, *]] { | ||
def apply[A](fa: UrlForm.Encoder[A]): EntityEncoder[F, A] = | ||
urlFormEntityEncoder[F].contramap((a: A) => | ||
urlFormEntityEncoder[F].contramap(a => | ||
UrlForm( | ||
formData = UrlForm.FormData.MultipleValues( | ||
values = Vector( | ||
UrlForm.FormData.PathedValue(PayloadPath("Action"), action), | ||
UrlForm.FormData | ||
.PathedValue(PayloadPath("Version"), version) | ||
) ++ fa.encode(a).formData.values | ||
) | ||
List( | ||
UrlForm.FormData(PayloadPath("Action"), Some(action)), | ||
UrlForm.FormData(PayloadPath("Version"), Some(version)) | ||
) ++ fa.encode(a).values | ||
) | ||
) | ||
} | ||
) | ||
RequestEncoder.restSchemaCompiler[F]( | ||
metadataEncoderCompiler = Metadata.AwsEncoder, | ||
entityEncoderCompiler = urlFormEntityEncoderCompilers, | ||
// We have to set this so that a body is produced even in the case where a | ||
// top-level struct input is empty. If it wasn't then the contramap above | ||
// wouldn't have the required effect because there would be no UrlForm to | ||
// add Action and Version to (literally no UrlForm value - not just an | ||
// empty one). | ||
writeEmptyStructs = true | ||
) | ||
RequestEncoder | ||
.restSchemaCompiler[F]( | ||
metadataEncoderCompiler = Metadata.AwsEncoder, | ||
entityEncoderCompiler = urlFormEntityEncoderCompilers, | ||
// We have to set this so that a body is produced even in the case where | ||
// a top-level struct input is empty. If it wasn't then the contramap | ||
// above wouldn't have the required effect because there would be no | ||
// UrlForm to add Action and Version to (literally no UrlForm value - | ||
// not just an empty one). | ||
writeEmptyStructs = true | ||
) | ||
.contramapSchema( | ||
// The AWS protocol works in terms of XmlFlattened and XmlName hints, | ||
// even for the input side, which is most definitely _not_ XML. Partly | ||
// because that seems odd, but mostly so that the URL form support can | ||
// 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 => | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 (and thanks for the comment) |
||
def translateFlattened(hints: Hints): Hints = | ||
hints.memberHints.get(XmlFlattened) match { | ||
case Some(_) => hints.addMemberHints(UrlFormFlattened()) | ||
case None => hints | ||
} | ||
def translateName(hints: Hints): Hints = | ||
hints.memberHints.get(XmlName) match { | ||
case Some(XmlName(name)) => | ||
hints.addMemberHints(UrlFormName(name)) | ||
case None => hints | ||
} | ||
(translateFlattened _ andThen translateName _)(hints) | ||
} | ||
) | ||
} | ||
|
||
private def urlFormEntityEncoder[F[_]]: EntityEncoder[F, UrlForm] = | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
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.