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

Orchestrator ResponseDeserializer codegen and auth #2494

Merged
merged 21 commits into from
Mar 27, 2023

Conversation

jdisanti
Copy link
Collaborator

Motivation and Context

This PR refactors the orchestrator's error handling and revises the code generator to implement the new ResponseDeserializer trait behind the enableNewSmithyRuntime codegen flag.


By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

@smithy-lang smithy-lang deleted a comment from github-actions bot Mar 24, 2023
@jdisanti jdisanti marked this pull request as ready for review March 24, 2023 01:44
@jdisanti jdisanti requested review from a team as code owners March 24, 2023 01:44
@github-actions
Copy link

A new generated diff is ready to view.

A new doc preview is ready to view.

@jdisanti jdisanti changed the title Generate ResponseDeserializer trait impls for the orchestrator Orchestrator ResponseDeserializer codegen and auth Mar 25, 2023
@github-actions
Copy link

A new generated diff is ready to view.

A new doc preview is ready to view.

use std::ops::{Deref, DerefMut};

#[derive(Debug)]
pub struct TypedBox<T> {
Copy link
Contributor

@Velfi Velfi Mar 27, 2023

Choose a reason for hiding this comment

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

I would love to see docs for this. Why does this exist? What does it allow us to do that we couldn't otherwise?

@@ -58,6 +58,12 @@ impl<B> RequestId for http::Response<B> {
}
}

impl RequestId for HeaderMap {
fn request_id(&self) -> Option<&str> {
extract_request_id(self)
Copy link
Contributor

Choose a reason for hiding this comment

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

When should functions that return an option have a try prefix? I'd ask the same question for traits too. Do we expect to only ever have a fallible version of this trait? I can see that being the case but I wanted to confirm.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This one will definitely always be fallible.

Comment on lines +63 to +65
fn extended_request_id(&self) -> Option<&str> {
extract_extended_request_id(self)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

What is an "extended" request ID? I'm not familiar with that phrasing.

Copy link
Contributor

Choose a reason for hiding this comment

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

Oh, this must be referring to that S3-specific way of sending it, right?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yeah, this is the S3 extended request ID.

Comment on lines +45 to 56
// TODO(enableNewSmithyRuntime): Implement checksumming via interceptor and delete this decorator
private fun applies(codegenContext: ClientCodegenContext): Boolean =
!codegenContext.settings.codegenConfig.enableNewSmithyRuntime

override fun operationCustomizations(
codegenContext: ClientCodegenContext,
operation: OperationShape,
baseCustomizations: List<OperationCustomization>,
): List<OperationCustomization> {
return baseCustomizations + HttpRequestChecksumCustomization(codegenContext, operation)
): List<OperationCustomization> = baseCustomizations.letIf(applies(codegenContext)) {
it + HttpRequestChecksumCustomization(codegenContext, operation)
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

This adds code to make_operation but I'd assumed we'd no longer be generating/calling that. Are we using it somehow? If not, why generate the code for it?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I'm currently continuing to generate make_operation for now to keep things compiling. I haven't made enough codegen progress to stop generating some pieces.

Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks for setting this up.

Comment on lines 83 to 97
private val orchestratorCodegenScope by lazy {
arrayOf(
"Error" to CargoDependency.smithyRuntimeApi(runtimeConfig).toType().resolve("client::interceptors::context::Error"),
"HttpResponse" to CargoDependency.smithyRuntimeApi(runtimeConfig).toType().resolve("client::orchestrator::HttpResponse"),
"Instrument" to CargoDependency.Tracing.toType().resolve("Instrument"),
"Output" to CargoDependency.smithyRuntimeApi(runtimeConfig).toType().resolve("client::interceptors::context::Output"),
"OutputOrError" to CargoDependency.smithyRuntimeApi(runtimeConfig).toType().resolve("client::interceptors::context::OutputOrError"),
"ResponseDeserializer" to CargoDependency.smithyRuntimeApi(runtimeConfig).toType().resolve("client::orchestrator::ResponseDeserializer"),
"SdkBody" to RuntimeType.sdkBody(runtimeConfig),
"SdkError" to RuntimeType.sdkError(runtimeConfig),
"TypedBox" to CargoDependency.smithyRuntimeApi(runtimeConfig).toType().resolve("type_erasure::TypedBox"),
"debug_span" to RuntimeType.Tracing.resolve("debug_span"),
"type_erase_result" to typeEraseResult(),
)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

This is a teeny bit more readable with some aliasing.

Suggested change
private val orchestratorCodegenScope by lazy {
arrayOf(
"Error" to CargoDependency.smithyRuntimeApi(runtimeConfig).toType().resolve("client::interceptors::context::Error"),
"HttpResponse" to CargoDependency.smithyRuntimeApi(runtimeConfig).toType().resolve("client::orchestrator::HttpResponse"),
"Instrument" to CargoDependency.Tracing.toType().resolve("Instrument"),
"Output" to CargoDependency.smithyRuntimeApi(runtimeConfig).toType().resolve("client::interceptors::context::Output"),
"OutputOrError" to CargoDependency.smithyRuntimeApi(runtimeConfig).toType().resolve("client::interceptors::context::OutputOrError"),
"ResponseDeserializer" to CargoDependency.smithyRuntimeApi(runtimeConfig).toType().resolve("client::orchestrator::ResponseDeserializer"),
"SdkBody" to RuntimeType.sdkBody(runtimeConfig),
"SdkError" to RuntimeType.sdkError(runtimeConfig),
"TypedBox" to CargoDependency.smithyRuntimeApi(runtimeConfig).toType().resolve("type_erasure::TypedBox"),
"debug_span" to RuntimeType.Tracing.resolve("debug_span"),
"type_erase_result" to typeEraseResult(),
)
}
val InterceptorContext = CargoDependency.smithyRuntimeApi(runtimeConfig).toType().resolve("client::interceptors::context");
val Orchestrator = CargoDependency.smithyRuntimeApi(runtimeConfig).toType().resolve("client::orchestrator");
private val orchestratorCodegenScope by lazy {
arrayOf(
"Error" to InterceptorContext.resolve("Error"),
"HttpResponse" to Orchestrator.resolve("HttpResponse"),
"Instrument" to CargoDependency.Tracing.toType().resolve("Instrument"),
"Output" to InterceptorContext.resolve("Output"),
"OutputOrError" to InterceptorContext.resolve("OutputOrError"),
"ResponseDeserializer" to Orchestrator.resolve("ResponseDeserializer"),
"SdkBody" to RuntimeType.sdkBody(runtimeConfig),
"SdkError" to RuntimeType.sdkError(runtimeConfig),
"TypedBox" to CargoDependency.smithyRuntimeApi(runtimeConfig).toType().resolve("type_erasure::TypedBox"),
"debug_span" to RuntimeType.Tracing.resolve("debug_span"),
"type_erase_result" to typeEraseResult(),
)
}

Copy link
Contributor

Choose a reason for hiding this comment

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

These aliases could even be added to RuntimeType.kt

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I was a little hesitant to add any conveniences to RuntimeType to avoid accidental inclusion in the old codegen paths.

Comment on lines +122 to +136
private fun typeEraseResult(): RuntimeType = ProtocolFunctions.crossOperationFn("type_erase_result") { fnName ->
rustTemplate(
"""
pub(crate) fn $fnName<O, E>(result: Result<O, E>) -> Result<#{Output}, #{Error}>
where
O: Send + Sync + 'static,
E: Send + Sync + 'static,
{
result.map(|output| #{TypedBox}::new(output).erase())
.map_err(|error| #{TypedBox}::new(error).erase())
}
""",
*orchestratorCodegenScope,
)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

I think adding docs or some sort of breadcrumb for people that don't understand why we do type erasure would be helpful

@@ -256,6 +403,43 @@ class HttpBoundProtocolTraitImplGenerator(
val outputSymbol = symbolProvider.toSymbol(outputShape)
val errorSymbol = symbolProvider.symbolForOperationError(operationShape)
return protocolFunctions.deserializeFn(operationShape, fnNameSuffix = "http_response") { fnName ->
Attribute.AllowClippyUnnecessaryWraps.render(this)
rustBlockTemplate(
"pub fn $fnName(response: &mut #{http}::Response<#{SdkBody}>) -> std::result::Result<#{O}, #{E}>",
Copy link
Contributor

Choose a reason for hiding this comment

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

We should probably add #[doc(hidden)] for this

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

They're in a pub(crate) module.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think we should document what the meaning/usage of all the sources are and why you'd choose one over the other. That, or we should create a TODO for that.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Ended up removing the modeled/unmodeled source changes to ServiceError since they're no longer needed.

Copy link
Contributor

Choose a reason for hiding this comment

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

This file is long enough that we should consider splitting it.

Comment on lines 15 to 20
pub mod client;

/// A typemap for storing configuration.
pub mod config_bag;
/// Smithy interceptors for smithy clients.
///
/// Interceptors are lifecycle hooks that can read/modify requests and responses.
pub mod interceptors;
/// Smithy code related to retry handling and token bucket.
///
/// This code defines when and how failed requests should be retried. It also defines the behavior
/// used to limit the rate that requests are sent.
pub mod retries;
/// Runtime plugin type definitions.
pub mod runtime_plugin;

pub mod type_erasure;
Copy link
Contributor

Choose a reason for hiding this comment

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

Please add docs for these modules here, or in the module files

@jdisanti jdisanti enabled auto-merge March 27, 2023 22:06
@github-actions
Copy link

A new generated diff is ready to view.

A new doc preview is ready to view.

@github-actions
Copy link

A new generated diff is ready to view.

A new doc preview is ready to view.

@jdisanti jdisanti added this pull request to the merge queue Mar 27, 2023
Merged via the queue into main with commit 6b21e46 Mar 27, 2023
@jdisanti jdisanti deleted the pair-programming-session branch March 27, 2023 23:39
unexge pushed a commit that referenced this pull request Apr 24, 2023
* Convert interceptor fns to macro invocations

Co-authored-by: John DiSanti <[email protected]>

* Add unmodeled error to ServiceError and bring in EventLog

Co-authored-by: John DiSanti <[email protected]>

* Simplify and test `EventLog`

* Attempt to integrate the event log with the orchestrator

* fix: error type in integration test
update: retry handling in orchestrator

* update: set the runtime plugins to do nothing instead of panic when called on

* Introduce a construct for type erasure

* Eliminate several generics and add phased error handling

* Code generate the `ResponseDeserializer` trait impls

* CI fixes

* Reorganize the new runtime crates

* Create the `Identity` type

* Reorganize orchestrator traits and accessors

* Set up code generated test environment for the new runtime

* Add initial auth orchestration implementation

* Fix clippy lint

* Incorporate feedback

* Fix external types lint

---------

Co-authored-by: Zelda Hessler <[email protected]>
rcoh pushed a commit that referenced this pull request Apr 24, 2023
* Convert interceptor fns to macro invocations

Co-authored-by: John DiSanti <[email protected]>

* Add unmodeled error to ServiceError and bring in EventLog

Co-authored-by: John DiSanti <[email protected]>

* Simplify and test `EventLog`

* Attempt to integrate the event log with the orchestrator

* fix: error type in integration test
update: retry handling in orchestrator

* update: set the runtime plugins to do nothing instead of panic when called on

* Introduce a construct for type erasure

* Eliminate several generics and add phased error handling

* Code generate the `ResponseDeserializer` trait impls

* CI fixes

* Reorganize the new runtime crates

* Create the `Identity` type

* Reorganize orchestrator traits and accessors

* Set up code generated test environment for the new runtime

* Add initial auth orchestration implementation

* Fix clippy lint

* Incorporate feedback

* Fix external types lint

---------

Co-authored-by: Zelda Hessler <[email protected]>
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.

3 participants