Unverified Commit 16b71cf9 authored by Russell Cohen's avatar Russell Cohen Committed by GitHub
Browse files

Http Prefix Header Support (#263)

* Http Prefix Header Support

The impetus of this diff is adding support for HttpPrefix headers. This ended up leading to a bunch of other changes:

1. operation.build() now always returns a result to account for inability to construct HTTP requests from certain inputs
2. HeaderName/HeaderValue is now created eagerly to give a better error message
3. httpPrefixHeaders are supported
4. The Dynamo movies example now uses the fluent client.

* Disable clippy lint of extra wrap

* Fix example

* Delete some dead code
parent 41d948c5
Loading
Loading
Loading
Loading
+5 −1
Original line number Diff line number Diff line
@@ -79,6 +79,10 @@ class FluentClientGenerator(protocolConfig: ProtocolConfig) {
                    Self { handle: std::sync::Arc::new(Handle { conf, client })}
                }

                pub fn conf(&self) -> &crate::Config {
                    &self.handle.conf
                }

            """,
                "aws_hyper" to hyperDep.asType()
            )
@@ -115,7 +119,7 @@ class FluentClientGenerator(protocolConfig: ProtocolConfig) {
                    }

                    pub async fn send(self) -> Result<#{ok}, #{sdk_err}<#{operation_err}>> {
                        let op = self.inner.build(&self.handle.conf);
                        let op = self.inner.build(&self.handle.conf).map_err(|err|#{sdk_err}::ConstructionFailure(err.into()))?;
                        self.handle.client.call(op).await
                    }
                    """,
+1 −2
Original line number Diff line number Diff line
@@ -164,8 +164,7 @@ fun generateCargoWorkspace(services: List<AwsService>): String {
    val examples = projectDir.resolve("examples").listFiles { file -> !file.name.startsWith(".") }?.toList()
        ?.map { "examples/${it.name}" }.orEmpty()

    val modules = services.map(AwsService::module) + runtimeModules + awsModules + examples
        ?.toList()
    val modules = services.map(AwsService::module) + runtimeModules + awsModules + examples.toList()
    return """
    [workspace]
    members = [
+40 −33
Original line number Diff line number Diff line
@@ -6,15 +6,14 @@
use aws_http::AwsErrorRetryPolicy;
use aws_hyper::{SdkError, SdkSuccess};
use dynamodb::error::DescribeTableError;
use dynamodb::input::{
    create_table_input, put_item_input, query_input, DescribeTableInput, ListTablesInput,
    PutItemInput, QueryInput,
};
use dynamodb::fluent::fluent_builders::Query;
use dynamodb::fluent::Client;
use dynamodb::input::DescribeTableInput;
use dynamodb::model::{
    AttributeDefinition, AttributeValue, KeySchemaElement, KeyType, ProvisionedThroughput,
    ScalarAttributeType, TableStatus,
};
use dynamodb::operation::{CreateTable, DescribeTable};
use dynamodb::operation::DescribeTable;
use dynamodb::output::DescribeTableOutput;
use dynamodb::{Config, Region};
use serde_json::Value;
@@ -34,13 +33,16 @@ use std::time::Duration;
#[tokio::main]
async fn main() {
    let table_name = "dynamo-movies-example";
    let client = aws_hyper::Client::https();
    let conf = dynamodb::Config::builder()
        .region(Region::new("us-east-1"))
        .build();
    let conn = aws_hyper::conn::Standard::https();
    let client = dynamodb::fluent::Client::from_conf_conn(conf, conn);
    let raw_client = aws_hyper::Client::https();

    let table_exists = client
        .call(ListTablesInput::builder().build(&conf))
        .list_tables()
        .send()
        .await
        .expect("should succeed")
        .table_names
@@ -49,14 +51,14 @@ async fn main() {
        .contains(&table_name.to_string());

    if !table_exists {
        client
            .call(create_table(table_name).build(&conf))
        create_table(&client, table_name)
            .send()
            .await
            .expect("failed to create table");
    }

    client
        .call(wait_for_ready_table(table_name, &conf))
    raw_client
        .call(wait_for_ready_table(table_name, client.conf()))
        .await
        .expect("table should become ready");

@@ -66,21 +68,24 @@ async fn main() {
        Value::Array(inner) => inner,
        data => panic!("data must be an array, got: {:?}", data),
    };
    for item in data {
    for value in data {
        client
            .call(add_item(table_name, item).build(&conf))
            .put_item()
            .table_name(table_name)
            .item(parse_item(value))
            .send()
            .await
            .expect("failed to insert item");
    }
    let films_2222 = client
        .call(movies_in_year(table_name, 2222).build(&conf))
    let films_2222 = movies_in_year(&client, table_name, 2222)
        .send()
        .await
        .expect("query should succeed");
    // this isn't back to the future, there are no movies from 2022
    assert_eq!(films_2222.count, 0);

    let films_2013 = client
        .call(movies_in_year(table_name, 2013).build(&conf))
    let films_2013 = movies_in_year(&client, table_name, 2013)
        .send()
        .await
        .expect("query should succeed");
    assert_eq!(films_2013.count, 2);
@@ -99,8 +104,12 @@ async fn main() {
    );
}

fn create_table(table_name: &str) -> create_table_input::Builder {
    CreateTable::builder()
fn create_table(
    client: &Client,
    table_name: &str,
) -> dynamodb::fluent::fluent_builders::CreateTable {
    client
        .create_table()
        .table_name(table_name)
        .key_schema(vec![
            KeySchemaElement::builder()
@@ -130,6 +139,13 @@ fn create_table(table_name: &str) -> create_table_input::Builder {
        )
}

fn parse_item(value: Value) -> HashMap<String, AttributeValue> {
    match value_to_item(value) {
        AttributeValue::M(map) => map,
        other => panic!("can only insert top level values, got {:?}", other),
    }
}

fn value_to_item(value: Value) -> AttributeValue {
    match value {
        Value::Null => AttributeValue::Null(true),
@@ -143,23 +159,13 @@ fn value_to_item(value: Value) -> AttributeValue {
    }
}

fn add_item(table_name: impl Into<String>, item: Value) -> put_item_input::Builder {
    let attribute_value = match value_to_item(item) {
        AttributeValue::M(map) => map,
        other => panic!("can only insert top level values, got {:?}", other),
    };

    PutItemInput::builder()
        .table_name(table_name)
        .item(attribute_value)
}

fn movies_in_year(table_name: &str, year: u16) -> query_input::Builder {
fn movies_in_year(client: &Client, table_name: &str, year: u16) -> Query {
    let mut expr_attrib_names = HashMap::new();
    expr_attrib_names.insert("#yr".to_string(), "year".to_string());
    let mut expr_attrib_values = HashMap::new();
    expr_attrib_values.insert(":yyyy".to_string(), AttributeValue::N(year.to_string()));
    QueryInput::builder()
    client
        .query()
        .table_name(table_name)
        .key_condition_expression("#yr = :yyyy")
        .expression_attribute_names(expr_attrib_names)
@@ -214,7 +220,8 @@ fn wait_for_ready_table(
) -> Operation<DescribeTable, WaitForReadyTable<AwsErrorRetryPolicy>> {
    let operation = DescribeTableInput::builder()
        .table_name(table_name)
        .build(&conf);
        .build(&conf)
        .expect("valid input");
    let waiting_policy = WaitForReadyTable {
        inner: operation.retry_policy().clone(),
    };
+1 −1
Original line number Diff line number Diff line
@@ -15,7 +15,7 @@ async fn main() {
        .build();
    let client: StandardClient = aws_hyper::Client::https();
    let data = client
        .call(GenerateRandom::builder().number_of_bytes(64).build(&config))
        .call(GenerateRandom::builder().number_of_bytes(64).build(&config).expect("valid operation"))
        .await
        .expect("failed to generate random data");
    println!("{:?}", data);
+5 −5
Original line number Diff line number Diff line
@@ -143,7 +143,7 @@ fn wait_for_ready_table(
) -> Operation<DescribeTable, WaitForReadyTable<AwsErrorRetryPolicy>> {
    let operation = DescribeTableInput::builder()
        .table_name(table_name)
        .build(&conf);
        .build(&conf).expect("valid operation");
    let waiting_policy = WaitForReadyTable {
        inner: operation.retry_policy().clone(),
    };
@@ -181,7 +181,7 @@ async fn movies_it() {
        .credentials_provider(Credentials::from_keys("AKNOTREAL", "NOT_A_SECRET", None))
        .build();
    client
        .call(create_table(table_name).build(&conf))
        .call(create_table(table_name).build(&conf).expect("valid request"))
        .await
        .expect("failed to create table");

@@ -200,19 +200,19 @@ async fn movies_it() {
    };
    for item in data {
        client
            .call(add_item(table_name, item.clone()).build(&conf))
            .call(add_item(table_name, item.clone()).build(&conf).expect("valid request"))
            .await
            .expect("failed to insert item");
    }
    let films_2222 = client
        .call(movies_in_year(table_name, 2222).build(&conf))
        .call(movies_in_year(table_name, 2222).build(&conf).expect("valid request"))
        .await
        .expect("query should succeed");
    // this isn't back to the future, there are no movies from 2022
    assert_eq!(films_2222.count, 0);

    let films_2013 = client
        .call(movies_in_year(table_name, 2013).build(&conf))
        .call(movies_in_year(table_name, 2013).build(&conf).expect("valid request"))
        .await
        .expect("query should succeed");
    assert_eq!(films_2013.count, 2);
Loading