From d49b15e5bbcb1c85e93a593b54e54c0f3fd86553 Mon Sep 17 00:00:00 2001 From: Ben Wilson Date: Sat, 15 Aug 2015 16:40:13 -0400 Subject: [PATCH] fix issues with creating secondary indices in dynamo --- lib/ex_aws/dynamo/client.ex | 45 ++++++++++++++++++++-------- lib/ex_aws/dynamo/impl.ex | 9 +++--- lib/ex_aws/utils.ex | 4 +++ test/lib/ex_aws/dynamo/impl_test.exs | 2 +- test/lib/ex_aws/dynamo_test.exs | 19 ++++++++++++ 5 files changed, 61 insertions(+), 18 deletions(-) diff --git a/lib/ex_aws/dynamo/client.ex b/lib/ex_aws/dynamo/client.ex index 63eab0f7..e1fc5b8d 100644 --- a/lib/ex_aws/dynamo/client.ex +++ b/lib/ex_aws/dynamo/client.ex @@ -151,35 +151,54 @@ defmodule ExAws.Dynamo.Client do """ defcallback create_table( table_name :: binary, - key_schema :: binary | atom, + key_schema :: binary | atom | key_schema, key_definitions :: key_definitions, read_capacity :: pos_integer, write_capacity :: pos_integer) :: ExAws.Request.response_t - @doc """ - Create table + @doc """ + Create table - key_schema allows specifying hash and / or range keys IE - ``` - [api_key: :hash, something_rangy: :range] - ``` - """ + key_schema allows specifying hash and / or range keys IE + ``` + [api_key: :hash, something_rangy: :range] + ``` + """ defcallback create_table( table_name :: binary, - key_schema :: [key_schema], + key_schema :: key_schema, key_definitions :: key_definitions, read_capacity :: pos_integer, write_capacity :: pos_integer) :: ExAws.Request.response_t - @doc "Create table with indices" + @doc """ + Create table with secondary indices + + Each index should follow the format outlined here: http://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_CreateTable.html + + For convenience, the keys in each index map are allowed to be atoms. IE: + `"KeySchema"` in the aws docs can be `key_schema:` + + Note that both the `global_indexes` and `local_indexes` arguments expect a list of such indices. + Examples + secondary_index = [%{ + index_name: "my-global-index", + key_schema: [%{ + attribute_name: "email", + attribute_type: "string", + }] + }] + create_table("TestUsers", [id: :hash], %{id: :string}, 1, 1, secondary_index, []) + + """ defcallback create_table( table_name :: binary, - key_schema :: [key_schema], + key_schema :: key_schema, key_definitions :: key_definitions, read_capacity :: pos_integer, write_capacity :: pos_integer, - global_indexes :: Keyword.t, - local_indexes :: Keyword.t) :: ExAws.Request.response_t + global_indexes :: [Map.t], + local_indexes :: [Map.t]) :: ExAws.Request.response_t @doc "Describe table" defcallback describe_table(name :: binary) :: ExAws.Request.response_t diff --git a/lib/ex_aws/dynamo/impl.ex b/lib/ex_aws/dynamo/impl.ex index 9f6fc117..b5c0e77b 100644 --- a/lib/ex_aws/dynamo/impl.ex +++ b/lib/ex_aws/dynamo/impl.ex @@ -61,11 +61,12 @@ defmodule ExAws.Dynamo.Impl do } } data = %{ - "GlobalSecondaryIndexes" => global_indexes |> camelize_keys(deep: true), - "LocalSecondaryIndexes" => local_indexes |> camelize_keys(deep: true) + "GlobalSecondaryIndexes" => global_indexes |> Enum.map(&camelize_keys(&1, deep: true)), + "LocalSecondaryIndexes" => local_indexes |> Enum.map(&camelize_keys(&1, deep: true)) } |> Enum.reduce(data, fn - ({_, indices = %{}}, data) when map_size(indices) == 0 -> data - {name, indices}, data -> Map.put(data, name, Enum.into(indices, %{})) + {_, []}, data -> data + {name, indices}, data -> + Map.put(data, name, indices) end) request(client, :create_table, data) diff --git a/lib/ex_aws/utils.ex b/lib/ex_aws/utils.ex index b2309566..df3c5227 100644 --- a/lib/ex_aws/utils.ex +++ b/lib/ex_aws/utils.ex @@ -17,6 +17,10 @@ defmodule ExAws.Utils do end end) end + + def camelize_keys([%{} | _] = opts, deep: deep) do + Enum.map(opts, &camelize_keys(&1, deep: deep)) + end def camelize_keys(opts, depth) do try do diff --git a/test/lib/ex_aws/dynamo/impl_test.exs b/test/lib/ex_aws/dynamo/impl_test.exs index 8cac6d30..e98d6594 100644 --- a/test/lib/ex_aws/dynamo/impl_test.exs +++ b/test/lib/ex_aws/dynamo/impl_test.exs @@ -3,7 +3,7 @@ defmodule Test.ClientData.Dynamo do def config_root, do: Application.get_all_env(:ex_aws) - def request(client, _data, _action), do: client + def request(client, _action, _data), do: client end defmodule ExAws.Dynamo.ImplTest do diff --git a/test/lib/ex_aws/dynamo_test.exs b/test/lib/ex_aws/dynamo_test.exs index f54a7722..825395a1 100644 --- a/test/lib/ex_aws/dynamo_test.exs +++ b/test/lib/ex_aws/dynamo_test.exs @@ -23,6 +23,25 @@ defmodule ExAws.DynamoTest do assert Dynamo.create_table("Users", [email: :hash, age: :range], [email: :string, age: :number], 1, 1) == expected end + test "create_table with secondary indexes" do + expected = %{"AttributeDefinitions" => [%{"AttributeName" => :id, "AttributeType" => "S"}], + "GlobalSecondaryIndexes" => [%{"IndexName" => "my-global-index", "KeySchema" => + [%{"AttributeName" => "email", "AttributeType" => "string"}]}], + "KeySchema" => [%{"AttributeName" => :id, "KeyType" => "HASH"}], + "LocalSecondaryIndexes" => [%{"IndexName" => "my-global-index", "KeySchema" => + [%{"AttributeName" => "email", "AttributeType" => "string"}]}], + "ProvisionedThroughput" => %{"ReadCapacityUnits" => 1, "WriteCapacityUnits" => 1}, "TableName" => "TestUsers"} + + secondary_index = [%{ + index_name: "my-global-index", + key_schema: [%{ + attribute_name: "email", + attribute_type: "string", + }] + }] + assert Dynamo.create_table("TestUsers", [id: :hash], %{id: :string}, 1, 1, secondary_index, secondary_index) == expected + end + test "#scan" do expected = %{"ExclusiveStartKey" => %{api_key: %{"S" => "api_key"}}, "ExpressionAttributeNames" => %{api_key: "#api_key"}, "ExpressionAttributeValues" => %{":api_key" => %{"S" => "asdfasdfasdf"}, ":name" => %{"S" => "bubba"}},