Remove generic body from `ParseHttpResponse` (#626)

* Remove generic body from `ParseHttpResponse`

* Update CHANGELOG
This commit is contained in:
John DiSanti 2021-08-04 17:31:48 -07:00 committed by GitHub
parent 8011dc325d
commit 8aa3a74af9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 41 additions and 38 deletions

View File

@ -1,10 +1,23 @@
vNext (Month Day Year) vNext (Month Day Year)
---------------------- ----------------------
**Breaking changes**
- (#626) `ParseHttpResponse` no longer has a generic argument for the body type, but instead, always uses `SdkBody`. This may cause compilation failures for you if you are using Smithy generated types to parse JSON or XML without using a client to request data from a service. The fix should be as simple as removing `<SdkBody>` in the example below:
Before:
```rust
let output = <Query as ParseHttpResponse<SdkBody>>::parse_loaded(&parser, &response).unwrap();
```
After:
```rust
let output = <Query as ParseHttpResponse>::parse_loaded(&parser, &response).unwrap();
```
**New This Week** **New This Week**
- (When complete) Add profile file provider for region (#594, #xyz) - (When complete) Add profile file provider for region (#594, #xyz)
v0.19 (August 3rd, 2021) v0.19 (August 3rd, 2021)
------------------------ ------------------------

View File

@ -54,14 +54,11 @@ impl ProvideErrorKind for OperationError {
} }
} }
impl<B> ParseHttpResponse<B> for TestOperationParser impl ParseHttpResponse for TestOperationParser {
where
B: http_body::Body,
{
type Output = Result<String, OperationError>; type Output = Result<String, OperationError>;
fn parse_unloaded(&self, _response: &mut Response<B>) -> Option<Self::Output> { fn parse_unloaded(&self, response: &mut Response<SdkBody>) -> Option<Self::Output> {
if _response.status().is_success() { if response.status().is_success() {
Some(Ok("Hello!".to_string())) Some(Ok("Hello!".to_string()))
} else { } else {
Some(Err(OperationError)) Some(Err(OperationError))

View File

@ -10,12 +10,13 @@ edition = "2018"
[dependencies] [dependencies]
aws-auth = { path = "../../build/aws-sdk/aws-auth" } aws-auth = { path = "../../build/aws-sdk/aws-auth" }
aws-http = { path = "../../build/aws-sdk/aws-http" } aws-http = { path = "../../build/aws-sdk/aws-http" }
aws-hyper = { path = "../../build/aws-sdk/aws-hyper", features = ["test-util"] } aws-hyper = { path = "../../build/aws-sdk/aws-hyper" }
aws-sdk-dynamodb = { path = "../../build/aws-sdk/dynamodb" } aws-sdk-dynamodb = { path = "../../build/aws-sdk/dynamodb" }
bytes = "1" bytes = "1"
criterion = { version = "0.3.4" } criterion = { version = "0.3.4" }
http = "0.2.4" http = "0.2.4"
serde_json = "1" serde_json = "1"
smithy-client = { path = "../../build/aws-sdk/smithy-client", features = ["test-util"] }
smithy-http = { path = "../../build/aws-sdk/smithy-http" } smithy-http = { path = "../../build/aws-sdk/smithy-http" }
smithy-types = { path = "../../build/aws-sdk/smithy-types" } smithy-types = { path = "../../build/aws-sdk/smithy-types" }
tokio = { version = "1", features = ["full", "test-util"]} tokio = { version = "1", features = ["full", "test-util"]}

View File

@ -6,7 +6,6 @@
use aws_sdk_dynamodb::operation::Query; use aws_sdk_dynamodb::operation::Query;
use bytes::Bytes; use bytes::Bytes;
use criterion::{criterion_group, criterion_main, Criterion}; use criterion::{criterion_group, criterion_main, Criterion};
use smithy_http::body::SdkBody;
use smithy_http::response::ParseHttpResponse; use smithy_http::response::ParseHttpResponse;
fn do_bench() { fn do_bench() {
@ -23,7 +22,7 @@ fn do_bench() {
.unwrap(); .unwrap();
let parser = Query::new(); let parser = Query::new();
let output = <Query as ParseHttpResponse<SdkBody>>::parse_loaded(&parser, &response).unwrap(); let output = <Query as ParseHttpResponse>::parse_loaded(&parser, &response).unwrap();
assert_eq!(2, output.count); assert_eq!(2, output.count);
} }

View File

@ -277,14 +277,13 @@ class HttpProtocolTestGenerator(
let parsed = parser.parse_unloaded(&mut http_response); let parsed = parser.parse_unloaded(&mut http_response);
let parsed = parsed.unwrap_or_else(|| { let parsed = parsed.unwrap_or_else(|| {
let http_response = http_response.map(|body|#{bytes}::copy_from_slice(body.bytes().unwrap())); let http_response = http_response.map(|body|#{bytes}::copy_from_slice(body.bytes().unwrap()));
<#{op} as #{parse_http_response}<#{sdk_body}>>::parse_loaded(&parser, &http_response) <#{op} as #{parse_http_response}>::parse_loaded(&parser, &http_response)
}); });
""", """,
"op" to operationSymbol, "op" to operationSymbol,
"bytes" to RuntimeType.Bytes, "bytes" to RuntimeType.Bytes,
"parse_http_response" to CargoDependency.SmithyHttp(protocolConfig.runtimeConfig).asType() "parse_http_response" to CargoDependency.SmithyHttp(protocolConfig.runtimeConfig).asType()
.member("response::ParseHttpResponse"), .member("response::ParseHttpResponse"),
"sdk_body" to RuntimeType.sdkBody(runtimeConfig = protocolConfig.runtimeConfig)
) )
if (expectedShape.hasTrait<ErrorTrait>()) { if (expectedShape.hasTrait<ErrorTrait>()) {
val errorSymbol = operationShape.errorSymbol(protocolConfig.symbolProvider) val errorSymbol = operationShape.errorSymbol(protocolConfig.symbolProvider)

View File

@ -245,7 +245,7 @@ class HttpBoundProtocolGenerator(
val successCode = httpBindingResolver.httpTrait(operationShape).code val successCode = httpBindingResolver.httpTrait(operationShape).code
rustTemplate( rustTemplate(
""" """
impl #{ParseResponse}<#{SdkBody}> for $operationName { impl #{ParseResponse} for $operationName {
type Output = std::result::Result<#{O}, #{E}>; type Output = std::result::Result<#{O}, #{E}>;
fn parse_unloaded(&self, response: &mut http::Response<#{SdkBody}>) -> Option<Self::Output> { fn parse_unloaded(&self, response: &mut http::Response<#{SdkBody}>) -> Option<Self::Output> {
// This is an error, defer to the non-streaming parser // This is an error, defer to the non-streaming parser

View File

@ -185,6 +185,7 @@ class HttpProtocolTestGeneratorTest {
} }
) )
visitor.execute() visitor.execute()
println("file:///$testDir/src/operation.rs")
return testDir return testDir
} }
@ -241,7 +242,6 @@ class HttpProtocolTestGeneratorTest {
@Test @Test
fun `test invalid url parameter`() { fun `test invalid url parameter`() {
// Hard coded implementation for this 1 test // Hard coded implementation for this 1 test
val path = generateService( val path = generateService(
""" """

View File

@ -119,7 +119,7 @@ pub trait SmithyRetryPolicy<O, T, E, Retry>:
/// Forwarding type to `O` for bound inference. /// Forwarding type to `O` for bound inference.
/// ///
/// See module-level docs for details. /// See module-level docs for details.
type O: ParseHttpResponse<SdkBody, Output = Result<T, Self::E>> + Send + Sync + Clone + 'static; type O: ParseHttpResponse<Output = Result<T, Self::E>> + Send + Sync + Clone + 'static;
/// Forwarding type to `E` for bound inference. /// Forwarding type to `E` for bound inference.
/// ///
/// See module-level docs for details. /// See module-level docs for details.
@ -134,7 +134,7 @@ pub trait SmithyRetryPolicy<O, T, E, Retry>:
impl<R, O, T, E, Retry> SmithyRetryPolicy<O, T, E, Retry> for R impl<R, O, T, E, Retry> SmithyRetryPolicy<O, T, E, Retry> for R
where where
R: tower::retry::Policy<Operation<O, Retry>, SdkSuccess<T>, SdkError<E>> + Clone, R: tower::retry::Policy<Operation<O, Retry>, SdkSuccess<T>, SdkError<E>> + Clone,
O: ParseHttpResponse<SdkBody, Output = Result<T, E>> + Send + Sync + Clone + 'static, O: ParseHttpResponse<Output = Result<T, E>> + Send + Sync + Clone + 'static,
E: Error + ProvideErrorKind, E: Error + ProvideErrorKind,
Retry: ClassifyResponse<SdkSuccess<T>, SdkError<E>>, Retry: ClassifyResponse<SdkSuccess<T>, SdkError<E>>,
{ {

View File

@ -24,7 +24,7 @@ impl ProvideErrorKind for TestOperationError {
#[derive(Clone)] #[derive(Clone)]
#[non_exhaustive] #[non_exhaustive]
pub struct TestOperation; pub struct TestOperation;
impl ParseHttpResponse<SdkBody> for TestOperation { impl ParseHttpResponse for TestOperation {
type Output = Result<(), TestOperationError>; type Output = Result<(), TestOperationError>;
fn parse_unloaded(&self, _: &mut http::Response<SdkBody>) -> Option<Self::Output> { fn parse_unloaded(&self, _: &mut http::Response<SdkBody>) -> Option<Self::Output> {

View File

@ -73,7 +73,7 @@ impl<S, O, T, E, R> tower::Service<operation::Operation<O, R>> for ParseResponse
where where
S: Service<operation::Request, Response = http::Response<SdkBody>, Error = SendOperationError>, S: Service<operation::Request, Response = http::Response<SdkBody>, Error = SendOperationError>,
S::Future: Send + 'static, S::Future: Send + 'static,
O: ParseHttpResponse<SdkBody, Output = Result<T, E>> + Send + Sync + 'static, O: ParseHttpResponse<Output = Result<T, E>> + Send + Sync + 'static,
E: Error, E: Error,
{ {
type Response = smithy_http::result::SdkSuccess<T>; type Response = smithy_http::result::SdkSuccess<T>;

View File

@ -90,7 +90,7 @@ pub async fn load_response<T, E, O>(
handler: &O, handler: &O,
) -> Result<SdkSuccess<T>, SdkError<E>> ) -> Result<SdkSuccess<T>, SdkError<E>>
where where
O: ParseHttpResponse<SdkBody, Output = Result<T, E>>, O: ParseHttpResponse<Output = Result<T, E>>,
{ {
if let Some(parsed_response) = handler.parse_unloaded(&mut response) { if let Some(parsed_response) = handler.parse_unloaded(&mut response) {
trace!(response = ?response); trace!(response = ?response);

View File

@ -3,6 +3,7 @@
* SPDX-License-Identifier: Apache-2.0. * SPDX-License-Identifier: Apache-2.0.
*/ */
use crate::body::SdkBody;
use bytes::Bytes; use bytes::Bytes;
use http::Response; use http::Response;
@ -18,7 +19,7 @@ use http::Response;
/// ///
/// It also enables this critical and core trait to avoid being async, and it makes code that uses /// It also enables this critical and core trait to avoid being async, and it makes code that uses
/// the trait easier to test. /// the trait easier to test.
pub trait ParseHttpResponse<B> { pub trait ParseHttpResponse {
/// Output type of the HttpResponse. /// Output type of the HttpResponse.
/// ///
/// For request/response style operations, this is typically something like: /// For request/response style operations, this is typically something like:
@ -44,7 +45,7 @@ pub trait ParseHttpResponse<B> {
/// the streaming body with an empty body as long as the body implements default. /// the streaming body with an empty body as long as the body implements default.
/// ///
/// We should consider if this is too limiting & if this should take an owned response instead. /// We should consider if this is too limiting & if this should take an owned response instead.
fn parse_unloaded(&self, response: &mut http::Response<B>) -> Option<Self::Output>; fn parse_unloaded(&self, response: &mut http::Response<SdkBody>) -> Option<Self::Output>;
/// Parse an HTTP request from a fully loaded body. This is for standard request/response style /// Parse an HTTP request from a fully loaded body. This is for standard request/response style
/// APIs like AwsJson 1.0/1.1 and the error path of most streaming APIs /// APIs like AwsJson 1.0/1.1 and the error path of most streaming APIs
@ -69,13 +70,10 @@ pub trait ParseStrictResponse {
fn parse(&self, response: &Response<Bytes>) -> Self::Output; fn parse(&self, response: &Response<Bytes>) -> Self::Output;
} }
impl<B, T> ParseHttpResponse<B> for T impl<T: ParseStrictResponse> ParseHttpResponse for T {
where
T: ParseStrictResponse,
{
type Output = T::Output; type Output = T::Output;
fn parse_unloaded(&self, _response: &mut Response<B>) -> Option<Self::Output> { fn parse_unloaded(&self, _response: &mut Response<SdkBody>) -> Option<Self::Output> {
None None
} }
@ -86,30 +84,26 @@ where
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use crate::body::SdkBody;
use crate::response::ParseHttpResponse; use crate::response::ParseHttpResponse;
use bytes::Bytes; use bytes::Bytes;
use http::Response; use http::Response;
use http_body::Body;
use std::mem; use std::mem;
#[test] #[test]
fn supports_streaming_body() { fn supports_streaming_body() {
pub struct S3GetObject<B: Body> { pub struct S3GetObject {
pub body: B, pub body: SdkBody,
} }
struct S3GetObjectParser; struct S3GetObjectParser;
impl<B> ParseHttpResponse<B> for S3GetObjectParser impl ParseHttpResponse for S3GetObjectParser {
where type Output = S3GetObject;
B: Default + Body,
{
type Output = S3GetObject<B>;
fn parse_unloaded(&self, response: &mut Response<B>) -> Option<Self::Output> { fn parse_unloaded(&self, response: &mut Response<SdkBody>) -> Option<Self::Output> {
// For responses that pass on the body, use mem::take to leave behind an empty // For responses that pass on the body, use mem::take to leave behind an empty body
// body let body = mem::replace(response.body_mut(), SdkBody::taken());
let body = mem::take(response.body_mut());
Some(S3GetObject { body }) Some(S3GetObject { body })
} }