Skip to content

Commit

Permalink
Update examples to use orchestrator
Browse files Browse the repository at this point in the history
  • Loading branch information
Fahad Zubair committed Oct 6, 2023
1 parent d6e01db commit f531651
Show file tree
Hide file tree
Showing 23 changed files with 722 additions and 1,357 deletions.
14 changes: 13 additions & 1 deletion examples/pokemon-service-client-usage/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,31 @@ version = "0.1.0"
edition = "2021"
publish = false

[[bin]]
name="checking"
path="main.rs"

[features]


[dependencies]
aws-smithy-async = { path = "../../rust-runtime/aws-smithy-async/", features = ["rt-tokio"] }
aws-smithy-client = { path = "../../rust-runtime/aws-smithy-client/", features = ["rustls"] }
aws-smithy-http = { path = "../../rust-runtime/aws-smithy-http/" }
aws-smithy-http-tower = { path = "../../rust-runtime/aws-smithy-http-tower/" }
aws-smithy-types = { path = "../../rust-runtime/aws-smithy-types/" }
pokemon-service-client = { path = "../pokemon-service-client/" }
hyper = "0.14.25"
hyper = { version = "0.14.25", features = ["client", "full"] }
tower = "0.4.13"
tokio = {version = "1.26.0", features=["full"]}
color-eyre = "0.6.2"
tracing = "0.1.37"
tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
pin-project = "1.1.0"
hyper-rustls = "0.24.1"
native-tls = "0.2.11"
hyper-native-tls = "0.3.0"
openssl = "0.10.57"
hyper-tls = "0.5.0"
http = "0.2.9"
aws-smithy-runtime-api = { version = "0.56.1", features = ["client"] }
27 changes: 13 additions & 14 deletions examples/pokemon-service-client-usage/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,27 +27,26 @@ You can view a list of examples by running `cargo run --example` from the
folder. To run an example, pass its name to the `cargo run --example` command, e.g.:

```
cargo run --example dyn-client
cargo run --example simple-client
```

## List of examples

| Rust Example | Description |
| ----------------------------- | --------------------------------------------------------------------------------------------------------- |
| dyn-client | Creates a type erased Smithy Client and calls an operation on it. |
| tower-client | Defines the complete Tower stack of layers for the Smithy Client, and then calls an operation on it. |
| endpoint-resolver | How to set a custom endpoint resolver, and optionally a header to each request. |
| endpoint-using-middleware | A custom middleware layer for setting endpoint on a request. |
| handling-errors | How to send an input parameter to an operation, and to handle errors. |
| custom-header | How to add headers to a request. |
| custom-middleware-request | How to add headers using a custom middleware. |
| custom-middleware-maprequest | How to use convenience fn `map_request` to write a middleware. |
| custom-middleware-mapresponse | A middleware that is called after the response has been received. It uses convenience fn `map_response`. |
| tower-middleware | Shows how a Tower middleware can be used to inspect / modify request and response. |
| property-bag-usage | How to use the property bag to pass data across middleware. |
| simple-client | DONE: Creates a Smithy Client and calls an operation on it. |
| endpoint-resolver | How to set a custom endpoint resolver |
| endpoint-using-middleware | FIX-LATER: A custom middleware layer for setting endpoint on a request. |
| handling-errors | FIX-LATER: How to send an input parameter to an operation, and to handle errors. |
| custom-header | How to add headers to a request. |
| custom-header-using-interceptor | How to add headers to all request using an inteceptor |
| response-header-interceptor | A middleware that is called after the response has been received. |
| property-bag-usage | How to use the property bag to pass data across interceptors. |
| retries-customize | Customize retry settings. |
| retries-disable | How to disable retries by default. |
| timeout-config | How to set configuration for different timeout. |
| tower-middleware | A Tower middleware that can inspect both request and response. As an example it prints the headers. |
| tower-middleware-nonboxed | An example of writing a middleware by implementing Future on a structure instead of using an async block. |
| mock-request | Use a custom connector to generate responses without sending them on the wire. |
|Testing using MockSmithyConnector|TODO|
|Runtime Components|TODO|
|Handling redirects|TODO|
|Raising error in interceptor|TODO|
89 changes: 89 additions & 0 deletions examples/pokemon-service-client-usage/examples/client-connector.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
use std::time::Duration;

use aws_smithy_client::{http_connector::ConnectorSettings, hyper_ext};
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
/// This example demonstrates how to create a Smithy Client and call an
/// [operation](https://smithy.io/2.0/spec/idl.html?highlight=operation#operation-shape).
///
/// The example assumes that the Pokemon service is running on the localhost on TCP port 13734.
/// Refer to the [README.md](https://github.com/awslabs/smithy-rs/tree/main/examples/pokemon-service-client-usage/README.md)
/// file for instructions on how to launch the service locally.
///
/// The example can be run using `cargo run --example dyn-client`.
///
use tracing::info;

use pokemon_service_client::Client as PokemonClient;

static BASE_URL: &str = "http://localhost:13734";

/// Creates a new Smithy client that is configured to communicate with a locally running Pokemon
/// service on TCP port 13734.
///
/// # Examples
///
/// Basic usage:
/// ```
/// let client = create_client();
/// ```
fn create_client() -> PokemonClient {
let https_connector = hyper_rustls::HttpsConnectorBuilder::new()
.with_native_roots()
// This can be changed to .https_only() to ensure that the client always uses https
.https_or_http()
.enable_http1()
.enable_http2()
.build();

let smithy_connector = hyper_ext::Adapter::builder()
// Optionally set things like timeouts as well
.connector_settings(
ConnectorSettings::builder()
.connect_timeout(Duration::from_secs(5))
.build(),
)
.build(https_connector);

let config = pokemon_service_client::Config::builder()
.endpoint_url(BASE_URL)
.http_connector(smithy_connector)
.build();

// Instantiate a client by applying the configuration.
pokemon_service_client::Client::from_conf(config)
}

#[tokio::main]
async fn main() {
tracing_setup();

// Create a configured Smithy client.
let client = create_client();

// Call an operation `get_server_statistics` on Pokemon service.
let response = client
.get_server_statistics()
.send()
.await
.expect("Pokemon service does not seem to be running on localhost:13734");

// Print the response received from the service.
info!(%BASE_URL, ?response, "Response received");
}

fn tracing_setup() {
if std::env::var("RUST_LIB_BACKTRACE").is_err() {
std::env::set_var("RUST_LIB_BACKTRACE", "1");
}
let _ = color_eyre::install();

if std::env::var("RUST_LOG").is_err() {
std::env::set_var("RUST_LOG", "info");
}
tracing_subscriber::fmt::fmt()
.with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
.init();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
/// In this example, a custom header x-amzn-client-ttl-seconds is set for all outgoing requests.
/// It serves as a demonstration of how an operation name can be retrieved and utilized within
/// the interceptor.
///
/// The example assumes that the Pokemon service is running on the localhost on TCP port 13734.
/// Refer to the [README.md](https://github.com/awslabs/smithy-rs/tree/main/examples/pokemon-service-client-usage/README.md)
/// file for instructions on how to launch the service locally.
///
/// The example can be run using `cargo run --example custom-header-using-interceptor`.
///
use std::{collections::HashMap, time::Duration};

use aws_smithy_types::config_bag::ConfigBag;
use pokemon_service_client::Client as PokemonClient;
use pokemon_service_client::{
config::{interceptors::BeforeTransmitInterceptorContextMut, Interceptor, RuntimeComponents},
error::BoxError,
};
use tracing::info;

// URL where example Pokemon service is running.
static BASE_URL: &str = "http://localhost:13734";
// By default set the value of all operations to 6.0
static DEFAULT_TTL: Duration = Duration::from_secs(6);
// Header to send with each operation.
const HEADER_TO_SEND: hyper::header::HeaderName =
hyper::header::HeaderName::from_static("x-amzn-client-ttl-seconds");

// The TtlHeaderInterceptor keeps a map of operation specific value to send
// for the header.
#[derive(Debug, Clone)]
pub struct TtlHeaderInterceptor {
/// Default time-to-live for an operation.
default_ttl: hyper::http::HeaderValue,
/// Operation specific time-to-live
operation_ttl: HashMap<&'static str, hyper::http::HeaderValue>,
}

fn format_ttl_value(ttl: Duration) -> String {
format!("{:.2}", ttl.as_secs_f64())
}

impl TtlHeaderInterceptor {
fn new(default_ttl: Duration) -> Self {
let duration_str = format_ttl_value(default_ttl);
let default_ttl_value = hyper::http::HeaderValue::from_str(duration_str.as_str())
.expect("could not create a header value for the default ttl");

Self {
default_ttl: default_ttl_value,
operation_ttl: Default::default(),
}
}

/// Adds an operation name specific value to the map.
fn add_operation_ttl(&mut self, operation_name: &'static str, ttl: Duration) {
let duration_str = format_ttl_value(ttl);

self.operation_ttl.insert(
operation_name,
hyper::http::HeaderValue::from_str(duration_str.as_str())
.expect("cannot create header value for the given ttl duration"),
);
}
}

///
impl Interceptor for TtlHeaderInterceptor {
fn name(&self) -> &'static str {
"TtlHeaderInterceptor"
}

/// Before the request is signed, add the header to the outgoing request.
fn modify_before_signing(
&self,
context: &mut BeforeTransmitInterceptorContextMut<'_>,
_runtime_components: &RuntimeComponents,
cfg: &mut ConfigBag,
) -> Result<(), BoxError> {
let metadata = cfg
.load::<aws_smithy_http::operation::Metadata>()
.expect("metadata should exist");
let operation_name = metadata.name().to_string();

let ttl = match self.operation_ttl.get(operation_name.as_str()) {
Some(ttl) => ttl,
None => &self.default_ttl,
};

context
.request_mut()
.headers_mut()
.insert(&HEADER_TO_SEND, ttl.clone());

info!("{operation_name} header set to {ttl:?}");

Ok(())
}
}

/// Creates a new Smithy client that is configured to communicate with a locally running Pokemon service on TCP port 13734.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// let client = create_client();
/// ```
fn create_client() -> PokemonClient {
let mut ttl_headers_interceptor = TtlHeaderInterceptor::new(DEFAULT_TTL);
ttl_headers_interceptor.add_operation_ttl("GetStorage", Duration::from_millis(3500));

// The generated client has a type config::Builder that can be used to build a Config, which
// allows configuring endpoint-resolver, timeouts, retries etc.
let config = pokemon_service_client::Config::builder()
.endpoint_resolver(BASE_URL)
.interceptor(ttl_headers_interceptor)
.build();

pokemon_service_client::Client::from_conf(config)
}

#[tokio::main]
async fn main() {
tracing_setup();

// Create a configured Smithy client.
let client = create_client();

// Call an operation `get_server_statistics` on Pokemon service.
let response = client
.get_server_statistics()
.send()
.await
.expect("Pokemon service does not seem to be running on localhost:13734");

info!(%BASE_URL, ?response, "Response for get_server_statistics()");

// Call the operation `get_storage` on Pokemon service. The AddHeader middleware
// will add a specific header name / value pair for this operation.
let response = client
.get_storage()
.user("ash")
.passcode("pikachu123")
.send()
.await
.expect("Operation could not be called");

info!(%BASE_URL, ?response, "Response for get_storage()");
}

fn tracing_setup() {
if std::env::var("RUST_LIB_BACKTRACE").is_err() {
std::env::set_var("RUST_LIB_BACKTRACE", "1");
}
let _ = color_eyre::install();

if std::env::var("RUST_LOG").is_err() {
std::env::set_var("RUST_LOG", "info");
}
tracing_subscriber::fmt::fmt()
.with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
.init();
}
33 changes: 4 additions & 29 deletions examples/pokemon-service-client-usage/examples/custom-header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,9 @@
///
/// The example can be run using `cargo run --example custom-header`
///
use aws_smithy_client::erase::{DynConnector, DynMiddleware};
use aws_smithy_http::endpoint::middleware::SmithyEndpointStage;
use aws_smithy_http_tower::map_request::MapRequestLayer;
use tower::ServiceBuilder;
use tracing::info;

// Define a type alias that makes it easy to pass around the Client type.
type PokemonClient = pokemon_service_client::Client<DynConnector, DynMiddleware<DynConnector>>;
use pokemon_service_client::Client as PokemonClient;

static BASE_URL: &str = "http://localhost:13734";

Expand All @@ -39,28 +34,8 @@ fn create_client() -> PokemonClient {
.endpoint_resolver(BASE_URL)
.build();

// When an operation is called on the generated Smithy client, it invokes the endpoint resolver
// that has been set in the Config, and puts the
// [result](https://docs.rs/aws-smithy-http/latest/aws_smithy_http/endpoint/type.Result.html)
// in the property bag. Since each [http::Request](https://docs.rs/http/latest/http/request/struct.Request.html)
// needs to have the endpoint set on it, there must be layer in the middleware pipeline,
// that gets the endpoint resolution result from the property bag and sets it on the request.
// For this, we need to add
// [SmithyEndpointStage](https://docs.rs/aws-smithy-http/latest/aws_smithy_http/endpoint/middleware/struct.SmithyEndpointStage.html) as a layer.
let endpoint_layer = MapRequestLayer::for_mapper(SmithyEndpointStage::new());
let middleware = ServiceBuilder::new().layer(endpoint_layer).into_inner();

// The Smithy client is not tied to a specific HTTP library, and by default it can
// use Hyper as the HTTP transport. The Client::Builder data type can be used
// to configure the transport (using `dyn_https_connector` function) and the middleware to be applied
// (using `middleware` function) to each request.
let raw_client = pokemon_service_client::Builder::new()
.dyn_https_connector(Default::default())
.middleware(middleware)
.build_dyn();

// Apply the configuration on the Client, and return that.
pokemon_service_client::Client::with_config(raw_client, config)
pokemon_service_client::Client::from_conf(config)
}

#[tokio::main]
Expand All @@ -82,8 +57,8 @@ async fn main() {
.mutate_request(|req| {
let headers = req.headers_mut();
headers.insert(
hyper::http::HeaderName::from_static("x-ttl-seconds"),
hyper::http::HeaderValue::from(30),
hyper::header::HeaderName::from_static("x-ttl-seconds"),
hyper::header::HeaderValue::from(30),
);
})
.send()
Expand Down
Loading

0 comments on commit f531651

Please sign in to comment.