From dc2130062f03ff450eea11f4732e4fc3e74c9f5c Mon Sep 17 00:00:00 2001 From: Kyle A Anderson Date: Wed, 28 Nov 2018 18:15:55 -0800 Subject: [PATCH 1/7] add DynamoDb transaction API calls --- .formatter.exs | 7 ++ lib/ex_aws/dynamo.ex | 114 ++++++++++++++++++++++++--- test/lib/dynamo/integration_test.exs | 3 +- test/lib/dynamo_test.exs | 53 +++++++++++++ 4 files changed, 164 insertions(+), 13 deletions(-) create mode 100644 .formatter.exs diff --git a/.formatter.exs b/.formatter.exs new file mode 100644 index 0000000..88a498d --- /dev/null +++ b/.formatter.exs @@ -0,0 +1,7 @@ +[ + # files to format + inputs: ["mix.exs", "{config,lib,test,priv}/**/*.{ex,exs}"], + locals_without_parens: [config: 0], + line_length: 120, + rename_deprecated_at: "1.6.1" +] \ No newline at end of file diff --git a/lib/ex_aws/dynamo.ex b/lib/ex_aws/dynamo.ex index 82d6f62..8fe536e 100644 --- a/lib/ex_aws/dynamo.ex +++ b/lib/ex_aws/dynamo.ex @@ -60,7 +60,7 @@ defmodule ExAws.Dynamo do http://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Operations.html """ - import ExAws.Utils, only: [camelize_keys: 1, camelize_keys: 2, upcase: 1] + import ExAws.Utils, only: [camelize: 1, camelize_keys: 1, camelize_keys: 2, upcase: 1] alias __MODULE__ @nested_opts [:exclusive_start_key, :expression_attribute_values, :expression_attribute_names] @@ -333,8 +333,7 @@ defmodule ExAws.Dynamo do end @doc "Update time to live" - @spec update_time_to_live(table :: binary, ttl_attribute :: binary, enabled :: boolean) :: - ExAws.Operation.JSON.t() + @spec update_time_to_live(table :: binary, ttl_attribute :: binary, enabled :: boolean) :: ExAws.Operation.JSON.t() def update_time_to_live(table, ttl_attribute, enabled) do data = build_time_to_live(ttl_attribute, enabled) |> Map.merge(%{"TableName" => table}) @@ -487,8 +486,7 @@ defmodule ExAws.Dynamo do | {:keys, [primary_key]} ] @spec batch_get_item(%{table_name => get_item}) :: ExAws.Operation.JSON.t() - @spec batch_get_item(%{table_name => get_item}, opts :: batch_get_item_opts) :: - ExAws.Operation.JSON.t() + @spec batch_get_item(%{table_name => get_item}, opts :: batch_get_item_opts) :: ExAws.Operation.JSON.t() def batch_get_item(data, opts \\ []) do request_items = data @@ -526,8 +524,7 @@ defmodule ExAws.Dynamo do | {:return_values, return_values_vals} ] @spec put_item(table_name :: table_name, record :: map()) :: ExAws.Operation.JSON.t() - @spec put_item(table_name :: table_name, record :: map(), opts :: put_item_opts) :: - ExAws.Operation.JSON.t() + @spec put_item(table_name :: table_name, record :: map(), opts :: put_item_opts) :: ExAws.Operation.JSON.t() def put_item(name, record, opts \\ []) do data = opts @@ -558,8 +555,7 @@ defmodule ExAws.Dynamo do | {:return_item_collection_metrics, return_item_collection_metrics_vals} ] @spec batch_write_item(%{table_name => [write_item]}) :: ExAws.Operation.JSON.t() - @spec batch_write_item(%{table_name => [write_item]}, opts :: batch_write_item_opts) :: - ExAws.Operation.JSON.t() + @spec batch_write_item(%{table_name => [write_item]}, opts :: batch_write_item_opts) :: ExAws.Operation.JSON.t() def batch_write_item(data, opts \\ []) do request_items = data @@ -648,8 +644,7 @@ defmodule ExAws.Dynamo do | {:return_item_collection_metrics, return_item_collection_metrics_vals} | {:return_values, return_values_vals} ] - @spec delete_item(table_name :: table_name, primary_key :: primary_key) :: - ExAws.Operation.JSON.t() + @spec delete_item(table_name :: table_name, primary_key :: primary_key) :: ExAws.Operation.JSON.t() @spec delete_item( table_name :: table_name, primary_key :: primary_key, @@ -667,6 +662,103 @@ defmodule ExAws.Dynamo do request(:delete_item, data) end + @doc """ + Update item in table + + For update_args format see + http://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_UpdateItem.html + """ + @type transact_get_item_opts :: [ + {:expression_attribute_names, expression_attribute_names_vals} + | {:projection_expression, binary} + ] + + @type transact_get_item :: + {table_name :: binary, primary_key :: primary_key} + | {table_name :: binary, primary_key :: primary_key, transact_get_item_opts} + + @type transact_get_items_opts :: [ + {:return_consumed_capacity, return_consumed_capacity_vals} + ] + + @spec transact_get_items(items :: [transact_get_item], transact_get_items_opts) :: ExAws.Operation.JSON.t() + @spec transact_get_items(items :: [transact_get_item]) :: ExAws.Operation.JSON.t() + def transact_get_items(items, opts \\ []) do + data = + opts + |> build_opts + |> Map.merge(%{ + "TransactItems" => Enum.map(items, &build_transaction_item({:get, &1})) + }) + + request(:transact_get_items, data) + end + + defp build_transaction_item({method, {table, item}}), do: build_transaction_item({method, {table, item, []}}) + + defp build_transaction_item({method, {table, item, opts}}) do + build_transaction_item(method, table, item, opts) + end + + defp build_transaction_item(method, table_name, item, opts) do + item = item |> Map.new() |> Dynamo.Encoder.encode_root() + + details = + opts + |> build_opts() + |> Map.merge(%{ + "TableName" => table_name, + transaction_item_key(method) => item + }) + + %{camelize(method) => details} + end + + defp transaction_item_key(:put), do: "Item" + defp transaction_item_key(_any), do: "Key" + + @type return_values_on_condition_check_failure_vals :: :all_old | :none + + @type transact_standard_item_opts :: [ + {:condition_expression, binary} + | {:expression_attribute_names, expression_attribute_names_vals} + | {:expression_attribute_values, expression_attribute_values_vals} + | {:return_values_on_condition_check_failure, return_values_on_condition_check_failure_vals} + ] + + @type transact_update_item_opts :: [ + {:condition_expression, binary} + | {:expression_attribute_names, expression_attribute_names_vals} + | {:expression_attribute_values, expression_attribute_values_vals} + | {:return_values_on_condition_check_failure, return_values_on_condition_check_failure_vals} + | {:update_expression, binary} + ] + + @type transact_write_item :: + {:condition_check, {table_name :: binary, key :: primary_key, transact_standard_item_opts}} + | {:delete, {table_name :: binary, key :: primary_key, transact_standard_item_opts}} + | {:put, {table_name :: binary, item :: map(), transact_standard_item_opts}} + | {:update, {table_name :: binary, key :: primary_key, transact_update_item_opts}} + + @type transact_write_items_opts :: [ + {:client_request_token, binary} + | {:return_consumed_capacity, return_consumed_capacity_vals} + | {:return_item_collection_metrics, return_item_collection_metrics_vals} + ] + + @spec transact_write_items(items :: [transact_write_item], transact_write_items_opts) :: ExAws.Operation.JSON.t() + @spec transact_write_items(items :: [transact_write_item]) :: ExAws.Operation.JSON.t() + def transact_write_items(items, opts \\ []) do + data = + opts + |> build_opts + |> Map.merge(%{ + "TransactItems" => Enum.map(items, &build_transaction_item/1) + }) + + request(:transact_write_items, data) + end + ## Options builder ################### defp build_opts(opts) do diff --git a/test/lib/dynamo/integration_test.exs b/test/lib/dynamo/integration_test.exs index 2ccb6cf..90cf31d 100644 --- a/test/lib/dynamo/integration_test.exs +++ b/test/lib/dynamo/integration_test.exs @@ -62,8 +62,7 @@ defmodule ExAws.DynamoIntegrationTest do end test "put and get several items with map values work" do - {:ok, _} = - Dynamo.create_table("SeveralUsers", :email, [email: :string], 1, 1) |> ExAws.request() + {:ok, _} = Dynamo.create_table("SeveralUsers", :email, [email: :string], 1, 1) |> ExAws.request() user1 = %Test.User{ email: "foo@bar.com", diff --git a/test/lib/dynamo_test.exs b/test/lib/dynamo_test.exs index c4710ed..5a4e6ec 100644 --- a/test/lib/dynamo_test.exs +++ b/test/lib/dynamo_test.exs @@ -292,4 +292,57 @@ defmodule ExAws.DynamoTest do assert Enum.at(request.headers, 0) == {"x-amz-target", "DynamoDB_20120810.DescribeTimeToLive"} assert request.data == expected end + + test "transact_get_items" do + expected = %{ + "TransactItems" => [ + %{ + "Get" => %{ + "Key" => %{"email" => %{"S" => "foo@baz.com"}}, + "TableName" => "Users", + "ProjectionExpression" => "email,age" + } + } + ] + } + + request = + Dynamo.transact_get_items([ + {"Users", %{"email" => "foo@baz.com"}, projection_expression: "email,age"} + ]) + + assert Enum.at(request.headers, 0) == {"x-amz-target", "DynamoDB_20120810.TransactGetItems"} + assert request.data == expected + end + + test "transact_write_items" do + expected = %{ + "TransactItems" => [ + %{ + "Update" => %{ + "ConditionExpression" => "Likes = :old_likes", + "ExpressionAttributeValues" => %{ + ":likes" => %{"N" => "9"}, + ":old_likes" => %{"N" => "99"} + }, + "Key" => %{"email" => %{"S" => "foo@baz.com"}}, + "TableName" => "Users", + "UpdateExpression" => "set Likes = :likes" + } + } + ] + } + + request = + Dynamo.transact_write_items( + update: + {"Users", %{"email" => "foo@baz.com"}, + update_expression: "set Likes = :likes", + condition_expression: "Likes = :old_likes", + expression_attribute_values: [likes: 9, old_likes: 99]} + ) + + assert Enum.at(request.headers, 0) == {"x-amz-target", "DynamoDB_20120810.TransactWriteItems"} + assert request.data == expected + end end From 5f77ed03b47210b9d39d7ac846b23eb386ab23f2 Mon Sep 17 00:00:00 2001 From: Kyle A Anderson Date: Tue, 19 Feb 2019 17:28:07 -0800 Subject: [PATCH 2/7] add integration tests --- lib/ex_aws/dynamo.ex | 2 +- test/lib/dynamo/integration_test.exs | 51 ++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/lib/ex_aws/dynamo.ex b/lib/ex_aws/dynamo.ex index 8fe536e..e16175a 100644 --- a/lib/ex_aws/dynamo.ex +++ b/lib/ex_aws/dynamo.ex @@ -701,7 +701,7 @@ defmodule ExAws.Dynamo do end defp build_transaction_item(method, table_name, item, opts) do - item = item |> Map.new() |> Dynamo.Encoder.encode_root() + item = item |> Dynamo.Encoder.encode_root() details = opts diff --git a/test/lib/dynamo/integration_test.exs b/test/lib/dynamo/integration_test.exs index 90cf31d..99a34b7 100644 --- a/test/lib/dynamo/integration_test.exs +++ b/test/lib/dynamo/integration_test.exs @@ -17,6 +17,8 @@ defmodule ExAws.DynamoIntegrationTest do Dynamo.delete_table("SeveralUsers") |> ExAws.request() Dynamo.delete_table(Foo) |> ExAws.request() Dynamo.delete_table("books") |> ExAws.request() + Dynamo.delete_table("Transactions") |> ExAws.request() + Dynamo.delete_table("Transactions2") |> ExAws.request() :ok end @@ -90,6 +92,55 @@ defmodule ExAws.DynamoIntegrationTest do assert Enum.at(items, 1) == user2 end + test "transactions work" do + {:ok, _} = Dynamo.create_table("Transactions", :email, [email: :string], 1, 1) |> ExAws.request() + {:ok, _} = Dynamo.create_table("Transactions2", :email, [email: :string], 1, 1) |> ExAws.request() + + user1 = %Test.User{ + email: "foo@bar.com", + name: %{first: "bob", last: "bubba"}, + age: 23, + admin: false + } + + assert {:ok, _} = + Dynamo.transact_write_items(put: {"Transactions", user1}, put: {"Transactions2", user1}) |> ExAws.request() + + user2 = %Test.User{ + email: "bar@bar.com", + name: %{first: "jane", last: "bubba"}, + age: 21, + admin: true + } + + assert {:error, {"TransactionCanceledException", _}} = + Dynamo.transact_write_items( + put: {"Transactions", user2}, + condition_check: + {"Transactions2", Map.take(user2, [:email]), condition_expression: "attribute_exists(age)"} + ) + |> ExAws.request() + + assert {:ok, %{}} = + Dynamo.transact_write_items( + put: {"Transactions", user2}, + put: {"Transactions2", user2}, + update: + {"Transactions", Map.take(user1, [:email]), + update_expression: "set age = age + :one", expression_attribute_values: [one: 1]} + ) + |> ExAws.request() + + assert {:ok, %{"Responses" => [get1, _get2]}} = + Dynamo.transact_get_items([ + {"Transactions", Map.take(user1, [:email])}, + {"Transactions2", Map.take(user2, [:email])} + ]) + |> ExAws.request() + + assert 24 == get1 |> Dynamo.decode_item(as: Test.User) |> Map.get(:age) + end + test "stream scan" do {:ok, _} = Dynamo.create_table("Users", :email, [email: :string], 1, 1) |> ExAws.request() From e1467e0a0c89f5ba9a84ad8e0dc2d11b9f87eaad Mon Sep 17 00:00:00 2001 From: Darren Klein Date: Wed, 25 Sep 2019 11:58:57 -0400 Subject: [PATCH 3/7] Merge version 2.2.2 test 'fixes' into fork with support for DDB transactions. --- .gitignore | 3 ++ README.md | 33 ++++++++++++++ config/config.exs | 40 +---------------- config/dev.exs | 7 +++ config/test_options.exs | 2 + mix.exs | 2 +- test/lib/dynamo/integration_test.exs | 67 ++++++++++++++-------------- test/support/ddb_local.ex | 16 +++++++ 8 files changed, 97 insertions(+), 73 deletions(-) create mode 100644 config/dev.exs create mode 100644 test/support/ddb_local.ex diff --git a/.gitignore b/.gitignore index 12179ea..75dcd56 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,6 @@ erl_crash.dump # Also ignore archive artifacts (built via "mix archive.build"). *.ez + +# Ignore config/test.exs to avoid unintentionally interfering with a user's local DDB instance. See README. +config/test.exs diff --git a/README.md b/README.md index f8f8b6e..f039be7 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,39 @@ config :ex_aws, :dynamodb, decode_sets: true ``` +## Local testing + +This application supports three test commands: + +* `mix test` - run the normal test suite +* `mix test.options` - run the test suite with options enabled (see `config/test_options.exs`) +* `mix test.all` - run `mix test` and `mix test.options` sequentially + +### Integration tests (optional) + +The tests in `test/lib/dynamo/integration_test.exs` will attempt to run against a running local instance of DynamoDB - in order to run these tests, you will need both a running local instance of DynamoDB as well as a `config/test.exs` file (currently gitignored) formatted like so: + +`config/test.exs` +```elixir +use Mix.Config + +config :ex_aws, :dynamodb, + scheme: "http://", + host: "localhost", + port: CHOOSE_YOUR_TEST_PORT, + region: "us-east-1" + +config :ex_aws, + debug_requests: true, + access_key_id: "abcd", + secret_access_key: "1234", + region: "us-east-1" +``` + +Before setting the `port`, be aware that `integration_test.exs` will create and delete tables with the names `"TestUsers", Test.User, "TestSeveralUsers", TestFoo, "test_books", "TestUsersWithRange", "TestTransactions", "TestTransactions2"` - be careful when setting the port, as these operations may interfere with your current tables if they share any of those names. + +If you do not have a running local instance of DynamoDB and/or you don't provide a `config/test.exs` file, the integration tests will hang for a few seconds before returning `invalid` - this will not interfere with the successful execution of other tests. + ## License The MIT License (MIT) diff --git a/config/config.exs b/config/config.exs index 91d0805..c9c59bb 100644 --- a/config/config.exs +++ b/config/config.exs @@ -1,41 +1,3 @@ -# This file is responsible for configuring your application -# and its dependencies with the aid of the Mix.Config module. use Mix.Config -config :ex_aws, :dynamodb, - scheme: "http://", - host: "localhost", - port: 8000, - region: "us-east-1" - -case Mix.env do - :test_options -> import_config "test_options.exs" - _ -> nil -end - -# This configuration is loaded before any dependency and is restricted -# to this project. If another project depends on this project, this -# file won't be loaded nor affect the parent project. For this reason, -# if you want to provide default values for your application for -# 3rd-party users, it should be done in your "mix.exs" file. - -# You can configure your application as: -# -# config :ex_aws_dynamo, key: :value -# -# and access this configuration in your application as: -# -# Application.get_env(:ex_aws_dynamo, :key) -# -# You can also configure a 3rd-party app: -# -# config :logger, level: :info -# - -# It is also possible to import configuration files, relative to this -# directory. For example, you can emulate configuration per environment -# by uncommenting the line below and defining dev.exs, test.exs and such. -# Configuration from the imported file will override the ones defined -# here (which is why it is important to import them last). -# -# import_config "#{Mix.env}.exs" +import_config "#{Mix.env}.exs" diff --git a/config/dev.exs b/config/dev.exs new file mode 100644 index 0000000..699a109 --- /dev/null +++ b/config/dev.exs @@ -0,0 +1,7 @@ +use Mix.Config + +config :ex_aws, :dynamodb, + scheme: "http://", + host: "localhost", + port: 8000, + region: "us-east-1" diff --git a/config/test_options.exs b/config/test_options.exs index 2b0229b..eb8ba0b 100644 --- a/config/test_options.exs +++ b/config/test_options.exs @@ -3,3 +3,5 @@ require Logger config :ex_aws, :dynamodb, decode_sets: true + +import_config "test.exs" diff --git a/mix.exs b/mix.exs index e7f0b98..9d6b9a8 100644 --- a/mix.exs +++ b/mix.exs @@ -1,7 +1,7 @@ defmodule ExAws.Dynamo.Mixfile do use Mix.Project - @version "2.2.1" + @version "2.2.2" @service "dynamo" @url "https://github.com/ex-aws/ex_aws_#{@service}" @name __MODULE__ |> Module.split() |> Enum.take(2) |> Enum.join(".") diff --git a/test/lib/dynamo/integration_test.exs b/test/lib/dynamo/integration_test.exs index 99a34b7..34e8289 100644 --- a/test/lib/dynamo/integration_test.exs +++ b/test/lib/dynamo/integration_test.exs @@ -12,14 +12,14 @@ defmodule ExAws.DynamoIntegrationTest do @moduletag :dynamo setup_all do - Dynamo.delete_table("Users") |> ExAws.request() - Dynamo.delete_table(Test.User) |> ExAws.request() - Dynamo.delete_table("SeveralUsers") |> ExAws.request() - Dynamo.delete_table(Foo) |> ExAws.request() - Dynamo.delete_table("books") |> ExAws.request() - Dynamo.delete_table("Transactions") |> ExAws.request() - Dynamo.delete_table("Transactions2") |> ExAws.request() - :ok + tables = [ "TestUsers", Test.User, "TestSeveralUsers", TestFoo, + "test_books", "TestUsersWithRange", "TestTransactions", "TestTransactions2" ] + + DDBLocal.delete_test_tables(tables) + + on_exit(fn -> + DDBLocal.delete_test_tables(tables) + end) end test "#list_tables" do @@ -27,13 +27,13 @@ defmodule ExAws.DynamoIntegrationTest do end test "#create and destroy table" do - assert {:ok, %{"TableDescription" => %{"TableName" => "Elixir.Foo"}}} = - Dynamo.create_table(Foo, :shard_id, [shard_id: :string], 1, 1) |> ExAws.request() + assert {:ok, %{"TableDescription" => %{"TableName" => "Elixir.TestFoo"}}} = + Dynamo.create_table(TestFoo, :shard_id, [shard_id: :string], 1, 1) |> ExAws.request() end test "#create table with range" do assert Dynamo.create_table( - "UsersWithRange", + "TestUsersWithRange", [email: :hash, age: :range], [email: :string, age: :number], 1, @@ -64,7 +64,8 @@ defmodule ExAws.DynamoIntegrationTest do end test "put and get several items with map values work" do - {:ok, _} = Dynamo.create_table("SeveralUsers", :email, [email: :string], 1, 1) |> ExAws.request() + {:ok, _} = + Dynamo.create_table("TestSeveralUsers", :email, [email: :string], 1, 1) |> ExAws.request() user1 = %Test.User{ email: "foo@bar.com", @@ -80,11 +81,11 @@ defmodule ExAws.DynamoIntegrationTest do admin: true } - assert {:ok, _} = Dynamo.put_item("SeveralUsers", user1) |> ExAws.request() - assert {:ok, _} = Dynamo.put_item("SeveralUsers", user2) |> ExAws.request() + assert {:ok, _} = Dynamo.put_item("TestSeveralUsers", user1) |> ExAws.request() + assert {:ok, _} = Dynamo.put_item("TestSeveralUsers", user2) |> ExAws.request() items = - Dynamo.scan("SeveralUsers", limit: 2) + Dynamo.scan("TestSeveralUsers", limit: 2) |> ExAws.request!() |> Dynamo.decode_item(as: Test.User) @@ -93,8 +94,8 @@ defmodule ExAws.DynamoIntegrationTest do end test "transactions work" do - {:ok, _} = Dynamo.create_table("Transactions", :email, [email: :string], 1, 1) |> ExAws.request() - {:ok, _} = Dynamo.create_table("Transactions2", :email, [email: :string], 1, 1) |> ExAws.request() + {:ok, _} = Dynamo.create_table("TestTransactions", :email, [email: :string], 1, 1) |> ExAws.request() + {:ok, _} = Dynamo.create_table("TestTransactions2", :email, [email: :string], 1, 1) |> ExAws.request() user1 = %Test.User{ email: "foo@bar.com", @@ -104,7 +105,7 @@ defmodule ExAws.DynamoIntegrationTest do } assert {:ok, _} = - Dynamo.transact_write_items(put: {"Transactions", user1}, put: {"Transactions2", user1}) |> ExAws.request() + Dynamo.transact_write_items(put: {"TestTransactions", user1}, put: {"TestTransactions2", user1}) |> ExAws.request() user2 = %Test.User{ email: "bar@bar.com", @@ -115,26 +116,26 @@ defmodule ExAws.DynamoIntegrationTest do assert {:error, {"TransactionCanceledException", _}} = Dynamo.transact_write_items( - put: {"Transactions", user2}, + put: {"TestTransactions", user2}, condition_check: - {"Transactions2", Map.take(user2, [:email]), condition_expression: "attribute_exists(age)"} + {"TestTransactions2", Map.take(user2, [:email]), condition_expression: "attribute_exists(age)"} ) |> ExAws.request() assert {:ok, %{}} = Dynamo.transact_write_items( - put: {"Transactions", user2}, - put: {"Transactions2", user2}, + put: {"TestTransactions", user2}, + put: {"TestTransactions2", user2}, update: - {"Transactions", Map.take(user1, [:email]), + {"TestTransactions", Map.take(user1, [:email]), update_expression: "set age = age + :one", expression_attribute_values: [one: 1]} ) |> ExAws.request() assert {:ok, %{"Responses" => [get1, _get2]}} = Dynamo.transact_get_items([ - {"Transactions", Map.take(user1, [:email])}, - {"Transactions2", Map.take(user2, [:email])} + {"TestTransactions", Map.take(user1, [:email])}, + {"TestTransactions2", Map.take(user2, [:email])} ]) |> ExAws.request() @@ -142,7 +143,7 @@ defmodule ExAws.DynamoIntegrationTest do end test "stream scan" do - {:ok, _} = Dynamo.create_table("Users", :email, [email: :string], 1, 1) |> ExAws.request() + {:ok, _} = Dynamo.create_table("TestUsers", :email, [email: :string], 1, 1) |> ExAws.request() user = %Test.User{ email: "foo@bar.com", @@ -151,7 +152,7 @@ defmodule ExAws.DynamoIntegrationTest do admin: false } - assert {:ok, _} = Dynamo.put_item("Users", user) |> ExAws.request() + assert {:ok, _} = Dynamo.put_item("TestUsers", user) |> ExAws.request() user = %Test.User{ email: "bar@bar.com", @@ -160,7 +161,7 @@ defmodule ExAws.DynamoIntegrationTest do admin: false } - assert {:ok, _} = Dynamo.put_item("Users", user) |> ExAws.request() + assert {:ok, _} = Dynamo.put_item("TestUsers", user) |> ExAws.request() user = %Test.User{ email: "baz@bar.com", @@ -169,9 +170,9 @@ defmodule ExAws.DynamoIntegrationTest do admin: false } - assert {:ok, _} = Dynamo.put_item("Users", user) |> ExAws.request() + assert {:ok, _} = Dynamo.put_item("TestUsers", user) |> ExAws.request() - assert Dynamo.scan("Users", limit: 1) + assert Dynamo.scan("TestUsers", limit: 1) |> ExAws.stream!() |> Enum.count() == 3 end @@ -179,7 +180,7 @@ defmodule ExAws.DynamoIntegrationTest do test "batch_write_item works" do {:ok, _} = Dynamo.create_table( - "books", + "test_books", [title: "hash", format: "range"], [title: :string, format: :string], 1, @@ -192,13 +193,13 @@ defmodule ExAws.DynamoIntegrationTest do [put_request: [item: %{title: "Tale of Two Cities", format: "softcover", price: 10.00}]] ] - assert {:ok, _} = Dynamo.batch_write_item(%{"books" => requests}) |> ExAws.request() + assert {:ok, _} = Dynamo.batch_write_item(%{"test_books" => requests}) |> ExAws.request() delete_requests = [ [delete_request: [key: %{title: "Tale of Two Cities", format: "hardcover"}]], [delete_request: [key: %{title: "Tale of Two Cities", format: "softcover"}]] ] - assert {:ok, _} = Dynamo.batch_write_item(%{"books" => delete_requests}) |> ExAws.request() + assert {:ok, _} = Dynamo.batch_write_item(%{"test_books" => delete_requests}) |> ExAws.request() end end diff --git a/test/support/ddb_local.ex b/test/support/ddb_local.ex new file mode 100644 index 0000000..ce67607 --- /dev/null +++ b/test/support/ddb_local.ex @@ -0,0 +1,16 @@ +defmodule DDBLocal do + @moduledoc """ + Helper methods for working with local DynamoDB during testing. + """ + + alias ExAws.Dynamo + + @doc """ + Delete tables created while running tests. + """ + def delete_test_tables(tables) do + tables + |> Enum.each(fn table -> Dynamo.delete_table(table) |> ExAws.request() end) + :ok + end +end From dce6d101e1cf8abd5015591fccbc3bc3bf2efcb6 Mon Sep 17 00:00:00 2001 From: Darren Klein Date: Thu, 26 Sep 2019 08:35:16 -0400 Subject: [PATCH 4/7] Add additional transaction test case for deleting items. --- lib/ex_aws/dynamo.ex | 17 +++++++++-------- test/lib/dynamo/integration_test.exs | 13 +++++++++++++ 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/lib/ex_aws/dynamo.ex b/lib/ex_aws/dynamo.ex index e16175a..37de07f 100644 --- a/lib/ex_aws/dynamo.ex +++ b/lib/ex_aws/dynamo.ex @@ -402,7 +402,7 @@ defmodule ExAws.Dynamo do def scan(name, opts \\ []) do data = opts - |> build_opts + |> build_opts() |> Map.merge(%{"TableName" => name}) request(:scan, data, %{stream_builder: &ExAws.Dynamo.Lazy.stream_scan(name, opts, &1)}) @@ -442,7 +442,7 @@ defmodule ExAws.Dynamo do def query(name, opts \\ []) do data = opts - |> build_opts + |> build_opts() |> Map.merge(%{"TableName" => name}) request(:query, data, %{stream_builder: &ExAws.Dynamo.Lazy.stream_query(name, opts, &1)}) @@ -528,7 +528,7 @@ defmodule ExAws.Dynamo do def put_item(name, record, opts \\ []) do data = opts - |> build_opts + |> build_opts() |> Map.merge(%{ "TableName" => name, "Item" => Dynamo.Encoder.encode_root(record) @@ -594,7 +594,7 @@ defmodule ExAws.Dynamo do def get_item(name, primary_key, opts \\ []) do data = opts - |> build_opts + |> build_opts() |> Map.merge(%{ "TableName" => name, "Key" => primary_key |> Map.new() |> Dynamo.Encoder.encode_root() @@ -626,7 +626,7 @@ defmodule ExAws.Dynamo do def update_item(table_name, primary_key, update_opts) do data = update_opts - |> build_opts + |> build_opts() |> Map.merge(%{ "TableName" => table_name, "Key" => primary_key |> Map.new() |> Dynamo.Encoder.encode_root() @@ -653,7 +653,7 @@ defmodule ExAws.Dynamo do def delete_item(name, primary_key, opts \\ []) do data = opts - |> build_opts + |> build_opts() |> Map.merge(%{ "TableName" => name, "Key" => primary_key |> Map.new() |> Dynamo.Encoder.encode_root() @@ -686,7 +686,7 @@ defmodule ExAws.Dynamo do def transact_get_items(items, opts \\ []) do data = opts - |> build_opts + |> build_opts() |> Map.merge(%{ "TransactItems" => Enum.map(items, &build_transaction_item({:get, &1})) }) @@ -751,7 +751,7 @@ defmodule ExAws.Dynamo do def transact_write_items(items, opts \\ []) do data = opts - |> build_opts + |> build_opts() |> Map.merge(%{ "TransactItems" => Enum.map(items, &build_transaction_item/1) }) @@ -761,6 +761,7 @@ defmodule ExAws.Dynamo do ## Options builder ################### + defp build_opts(opts) do opts = opts |> Map.new() diff --git a/test/lib/dynamo/integration_test.exs b/test/lib/dynamo/integration_test.exs index 34e8289..952abd6 100644 --- a/test/lib/dynamo/integration_test.exs +++ b/test/lib/dynamo/integration_test.exs @@ -140,6 +140,19 @@ defmodule ExAws.DynamoIntegrationTest do |> ExAws.request() assert 24 == get1 |> Dynamo.decode_item(as: Test.User) |> Map.get(:age) + + assert {:ok, %{}} = + Dynamo.transact_write_items([ + delete: {"TestTransactions", Map.take(user1, [:email])}, + delete: {"TestTransactions2", Map.take(user2, [:email])} + ]) |> ExAws.request() + + assert {:ok, %{"Responses" => [%{}, %{}]}} = + Dynamo.transact_get_items([ + {"TestTransactions", Map.take(user1, [:email])}, + {"TestTransactions2", Map.take(user2, [:email])} + ]) + |> ExAws.request() end test "stream scan" do From 03169cd35f1129c962a75a7b62aaa4c094ebc9ba Mon Sep 17 00:00:00 2001 From: Darren Klein Date: Thu, 26 Sep 2019 08:45:10 -0400 Subject: [PATCH 5/7] Result of running $ mix format --- config/config.exs | 2 +- config/test_options.exs | 3 +- lib/ex_aws/dynamo.ex | 67 ++++++++++++++++++---------- lib/ex_aws/dynamo/decoder.ex | 3 ++ mix.exs | 7 +-- test/lib/dynamo/decoder_test.exs | 16 +++---- test/lib/dynamo/integration_test.exs | 37 +++++++++------ test/lib/dynamo_test.exs | 18 +++----- test/support/ddb_local.ex | 1 + 9 files changed, 90 insertions(+), 64 deletions(-) diff --git a/config/config.exs b/config/config.exs index c9c59bb..8233fe9 100644 --- a/config/config.exs +++ b/config/config.exs @@ -1,3 +1,3 @@ use Mix.Config -import_config "#{Mix.env}.exs" +import_config "#{Mix.env()}.exs" diff --git a/config/test_options.exs b/config/test_options.exs index eb8ba0b..3c31a55 100644 --- a/config/test_options.exs +++ b/config/test_options.exs @@ -1,7 +1,6 @@ use Mix.Config require Logger -config :ex_aws, :dynamodb, - decode_sets: true +config :ex_aws, :dynamodb, decode_sets: true import_config "test.exs" diff --git a/lib/ex_aws/dynamo.ex b/lib/ex_aws/dynamo.ex index 37de07f..ab14ebb 100644 --- a/lib/ex_aws/dynamo.ex +++ b/lib/ex_aws/dynamo.ex @@ -77,7 +77,7 @@ defmodule ExAws.Dynamo do @type primary_key :: [{atom, binary}] | %{atom => binary} @type exclusive_start_key_vals :: [{atom, binary}] | %{atom => binary} @type expression_attribute_names_vals :: %{binary => binary} - @type expression_attribute_values_vals :: [{atom, Dynamo.Encodable.t}] | %{atom => Dynamo.Encodable.t} + @type expression_attribute_values_vals :: [{atom, Dynamo.Encodable.t()}] | %{atom => Dynamo.Encodable.t()} @type return_consumed_capacity_vals :: :none | :total @@ -169,24 +169,35 @@ defmodule ExAws.Dynamo do write_capacity :: pos_integer, billing_mode :: dynamo_billing_types ) :: ExAws.Operation.JSON.t() - def create_table(name, primary_key, key_definitions, read_capacity, write_capacity, billing_mode \\ @default_billing_mode) def create_table( - name, - primary_key, - key_definitions, - read_capacity, - write_capacity, - billing_mode) + name, + primary_key, + key_definitions, + read_capacity, + write_capacity, + billing_mode \\ @default_billing_mode + ) + + def create_table( + name, + primary_key, + key_definitions, + read_capacity, + write_capacity, + billing_mode + ) when is_atom(primary_key) or is_binary(primary_key) do create_table(name, [{primary_key, :hash}], key_definitions, read_capacity, write_capacity, billing_mode) end + def create_table( - name, - key_schema, - key_definitions, - read_capacity, - write_capacity, - billing_mode) + name, + key_schema, + key_definitions, + read_capacity, + write_capacity, + billing_mode + ) when is_list(key_schema) do create_table(name, key_schema, key_definitions, read_capacity, write_capacity, [], [], billing_mode) end @@ -242,12 +253,13 @@ defmodule ExAws.Dynamo do local_indexes, billing_mode \\ @default_billing_mode ) do - data = build_billing_mode(read_capacity, write_capacity, billing_mode) - |> Map.merge( %{ - "TableName" => name, - "AttributeDefinitions" => key_definitions |> encode_key_definitions, - "KeySchema" => key_schema |> build_key_schema, - }) + data = + build_billing_mode(read_capacity, write_capacity, billing_mode) + |> Map.merge(%{ + "TableName" => name, + "AttributeDefinitions" => key_definitions |> encode_key_definitions, + "KeySchema" => key_schema |> build_key_schema + }) data = %{ @@ -262,7 +274,6 @@ defmodule ExAws.Dynamo do Map.put(data, name, indices) end) - request(:create_table, data) end @@ -275,7 +286,11 @@ defmodule ExAws.Dynamo do end) end - @spec build_billing_mode(read_capacity :: pos_integer, write_capacity :: pos_integer, billing_mode :: dynamo_billing_types) :: Map.t() + @spec build_billing_mode( + read_capacity :: pos_integer, + write_capacity :: pos_integer, + billing_mode :: dynamo_billing_types + ) :: Map.t() defp build_billing_mode(read_capacity, write_capacity, :provisioned) do %{ "BillingMode" => "PROVISIONED", @@ -285,6 +300,7 @@ defmodule ExAws.Dynamo do } } end + # Pay-per-request (AKA on-demand) tables do not have read/write capacities. defp build_billing_mode(_read_capacity, _write_capacity, :pay_per_request) do %{"BillingMode" => "PAY_PER_REQUEST"} @@ -312,7 +328,7 @@ defmodule ExAws.Dynamo do defp maybe_convert_billing_mode(attributes) do case attributes[:billing_mode] do nil -> attributes - _ -> convert_billing_mode(attributes, attributes[:billing_mode]) + _ -> convert_billing_mode(attributes, attributes[:billing_mode]) end end @@ -323,6 +339,7 @@ defmodule ExAws.Dynamo do @spec do_convert_billing_mode(attributes :: Keyword.t() | Map.t(), value :: String.t()) :: Keyword.t() | Map.t() defp do_convert_billing_mode(attributes, value) when is_map(attributes), do: Map.replace!(attributes, :billing_mode, value) + defp do_convert_billing_mode(attributes, value) when is_list(attributes), do: Keyword.replace!(attributes, :billing_mode, value) @@ -344,7 +361,8 @@ defmodule ExAws.Dynamo do defp build_time_to_live("", _enabled) do %{} end - defp build_time_to_live(ttl_attribute, enabled) when ttl_attribute != nil do + + defp build_time_to_live(ttl_attribute, enabled) when ttl_attribute != nil do %{ "TimeToLiveSpecification" => %{ "AttributeName" => ttl_attribute, @@ -352,6 +370,7 @@ defmodule ExAws.Dynamo do } } end + defp build_time_to_live(_ttl_attribute, _enabled) do %{} end diff --git a/lib/ex_aws/dynamo/decoder.ex b/lib/ex_aws/dynamo/decoder.ex index d785905..b7a4f91 100644 --- a/lib/ex_aws/dynamo/decoder.ex +++ b/lib/ex_aws/dynamo/decoder.ex @@ -39,9 +39,11 @@ defmodule ExAws.Dynamo.Decoder do def decode(%{"B" => value}), do: Base.decode64!(value) def decode(%{"S" => value}), do: value def decode(%{"M" => value}), do: value |> decode + if Application.get_env(:ex_aws, :dynamodb, [])[:decode_sets] do def decode(%{"BS" => values}), do: MapSet.new(values) def decode(%{"SS" => values}), do: MapSet.new(values) + def decode(%{"NS" => values}) do values |> Stream.map(&binary_to_number/1) @@ -50,6 +52,7 @@ defmodule ExAws.Dynamo.Decoder do else def decode(%{"BS" => values}), do: values def decode(%{"SS" => values}), do: values + def decode(%{"NS" => values}) do Enum.map(values, &binary_to_number/1) end diff --git a/mix.exs b/mix.exs index 9d6b9a8..ae4903e 100644 --- a/mix.exs +++ b/mix.exs @@ -68,15 +68,16 @@ defmodule ExAws.Dynamo.Mixfile do end defp run_tests(_) do - Mix.shell.cmd( + Mix.shell().cmd( "mix test --color", env: [{"MIX_ENV", "test"}] ) end defp run_options_tests(_) do - IO.puts "\nRunning tests with options enabled." - Mix.shell.cmd( + IO.puts("\nRunning tests with options enabled.") + + Mix.shell().cmd( "mix test --color", env: [{"MIX_ENV", "test_options"}] ) diff --git a/test/lib/dynamo/decoder_test.exs b/test/lib/dynamo/decoder_test.exs index 1681bc5..f9ab99a 100644 --- a/test/lib/dynamo/decoder_test.exs +++ b/test/lib/dynamo/decoder_test.exs @@ -6,38 +6,38 @@ defmodule ExAws.Dynamo.DecoderTest do if Application.get_env(:ex_aws, :dynamodb, [])[:decode_sets] do test "decoder decodes numberset to a mapset of numbers" do assert %{"NS" => ["1", "2", "3"]} - |> Decoder.decode() == MapSet.new([1, 2, 3]) + |> Decoder.decode() == MapSet.new([1, 2, 3]) assert %{"NS" => [1, 2, 3]} - |> Decoder.decode() == MapSet.new([1, 2, 3]) + |> Decoder.decode() == MapSet.new([1, 2, 3]) end test "decoder decodes stringset to a mapset of strings" do assert %{"SS" => ["foo", "bar", "baz"]} - |> Decoder.decode() == MapSet.new(["foo", "bar", "baz"]) + |> Decoder.decode() == MapSet.new(["foo", "bar", "baz"]) end test "decoder decodes binaryset to a mapset of strings" do assert %{"BS" => ["U3Vubnk=", "UmFpbnk=", "U25vd3k="]} - |> Decoder.decode() == MapSet.new(["U3Vubnk=", "UmFpbnk=", "U25vd3k="]) + |> Decoder.decode() == MapSet.new(["U3Vubnk=", "UmFpbnk=", "U25vd3k="]) end else test "decoder decodes numberset to a list of numbers" do assert %{"NS" => ["1", "2", "3"]} - |> Decoder.decode() == [1, 2, 3] + |> Decoder.decode() == [1, 2, 3] assert %{"NS" => [1, 2, 3]} - |> Decoder.decode() == [1, 2, 3] + |> Decoder.decode() == [1, 2, 3] end test "decoder decodes stringset to a list of strings" do assert %{"SS" => ["foo", "bar", "baz"]} - |> Decoder.decode() == ["foo", "bar", "baz"] + |> Decoder.decode() == ["foo", "bar", "baz"] end test "decoder decodes binaryset to a list of strings" do assert %{"BS" => ["U3Vubnk=", "UmFpbnk=", "U25vd3k="]} - |> Decoder.decode() == ["U3Vubnk=", "UmFpbnk=", "U25vd3k="] + |> Decoder.decode() == ["U3Vubnk=", "UmFpbnk=", "U25vd3k="] end end diff --git a/test/lib/dynamo/integration_test.exs b/test/lib/dynamo/integration_test.exs index 952abd6..9c0dedc 100644 --- a/test/lib/dynamo/integration_test.exs +++ b/test/lib/dynamo/integration_test.exs @@ -12,8 +12,16 @@ defmodule ExAws.DynamoIntegrationTest do @moduletag :dynamo setup_all do - tables = [ "TestUsers", Test.User, "TestSeveralUsers", TestFoo, - "test_books", "TestUsersWithRange", "TestTransactions", "TestTransactions2" ] + tables = [ + "TestUsers", + Test.User, + "TestSeveralUsers", + TestFoo, + "test_books", + "TestUsersWithRange", + "TestTransactions", + "TestTransactions2" + ] DDBLocal.delete_test_tables(tables) @@ -64,8 +72,7 @@ defmodule ExAws.DynamoIntegrationTest do end test "put and get several items with map values work" do - {:ok, _} = - Dynamo.create_table("TestSeveralUsers", :email, [email: :string], 1, 1) |> ExAws.request() + {:ok, _} = Dynamo.create_table("TestSeveralUsers", :email, [email: :string], 1, 1) |> ExAws.request() user1 = %Test.User{ email: "foo@bar.com", @@ -105,7 +112,8 @@ defmodule ExAws.DynamoIntegrationTest do } assert {:ok, _} = - Dynamo.transact_write_items(put: {"TestTransactions", user1}, put: {"TestTransactions2", user1}) |> ExAws.request() + Dynamo.transact_write_items(put: {"TestTransactions", user1}, put: {"TestTransactions2", user1}) + |> ExAws.request() user2 = %Test.User{ email: "bar@bar.com", @@ -142,17 +150,18 @@ defmodule ExAws.DynamoIntegrationTest do assert 24 == get1 |> Dynamo.decode_item(as: Test.User) |> Map.get(:age) assert {:ok, %{}} = - Dynamo.transact_write_items([ - delete: {"TestTransactions", Map.take(user1, [:email])}, - delete: {"TestTransactions2", Map.take(user2, [:email])} - ]) |> ExAws.request() + Dynamo.transact_write_items( + delete: {"TestTransactions", Map.take(user1, [:email])}, + delete: {"TestTransactions2", Map.take(user2, [:email])} + ) + |> ExAws.request() assert {:ok, %{"Responses" => [%{}, %{}]}} = - Dynamo.transact_get_items([ - {"TestTransactions", Map.take(user1, [:email])}, - {"TestTransactions2", Map.take(user2, [:email])} - ]) - |> ExAws.request() + Dynamo.transact_get_items([ + {"TestTransactions", Map.take(user1, [:email])}, + {"TestTransactions2", Map.take(user2, [:email])} + ]) + |> ExAws.request() end test "stream scan" do diff --git a/test/lib/dynamo_test.exs b/test/lib/dynamo_test.exs index 5a4e6ec..30cf7e9 100644 --- a/test/lib/dynamo_test.exs +++ b/test/lib/dynamo_test.exs @@ -104,7 +104,7 @@ defmodule ExAws.DynamoTest do assert Dynamo.update_table( "TestUsers", - [billing_mode: :pay_per_request] + billing_mode: :pay_per_request ).data == expected expected = %{ @@ -118,11 +118,8 @@ defmodule ExAws.DynamoTest do assert Dynamo.update_table( "TestUsers", - %{provisioned_throughput: - %{read_capacity_units: 1, - write_capacity_units: 1}, - billing_mode: :provisioned - }).data == expected + %{provisioned_throughput: %{read_capacity_units: 1, write_capacity_units: 1}, billing_mode: :provisioned} + ).data == expected expected = %{ "ProvisionedThroughput" => %{ @@ -132,12 +129,9 @@ defmodule ExAws.DynamoTest do "TableName" => "TestUsers" } - assert Dynamo.update_table( - "TestUsers", - [provisioned_throughput: - [read_capacity_units: 2, - write_capacity_units: 3] - ]).data == expected + assert Dynamo.update_table("TestUsers", + provisioned_throughput: [read_capacity_units: 2, write_capacity_units: 3] + ).data == expected end test "#scan" do diff --git a/test/support/ddb_local.ex b/test/support/ddb_local.ex index ce67607..60b34b3 100644 --- a/test/support/ddb_local.ex +++ b/test/support/ddb_local.ex @@ -11,6 +11,7 @@ defmodule DDBLocal do def delete_test_tables(tables) do tables |> Enum.each(fn table -> Dynamo.delete_table(table) |> ExAws.request() end) + :ok end end From ab8bfb2521bf82c9e430681b8b7fac2595230c42 Mon Sep 17 00:00:00 2001 From: Darren Klein Date: Thu, 26 Sep 2019 16:02:00 -0400 Subject: [PATCH 6/7] Add docs for transact_write_items and transact_get_items --- lib/ex_aws/dynamo.ex | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/ex_aws/dynamo.ex b/lib/ex_aws/dynamo.ex index ab14ebb..ea0681e 100644 --- a/lib/ex_aws/dynamo.ex +++ b/lib/ex_aws/dynamo.ex @@ -681,12 +681,6 @@ defmodule ExAws.Dynamo do request(:delete_item, data) end - @doc """ - Update item in table - - For update_args format see - http://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_UpdateItem.html - """ @type transact_get_item_opts :: [ {:expression_attribute_names, expression_attribute_names_vals} | {:projection_expression, binary} @@ -702,6 +696,10 @@ defmodule ExAws.Dynamo do @spec transact_get_items(items :: [transact_get_item], transact_get_items_opts) :: ExAws.Operation.JSON.t() @spec transact_get_items(items :: [transact_get_item]) :: ExAws.Operation.JSON.t() + + @doc """ + A synchronous operation that retrieves multiple items from one or more tables (but not from indexes) in a single account and region + """ def transact_get_items(items, opts \\ []) do data = opts @@ -765,6 +763,9 @@ defmodule ExAws.Dynamo do | {:return_item_collection_metrics, return_item_collection_metrics_vals} ] + @doc """ + A synchronous write operation that groups up to 25 action requests + """ @spec transact_write_items(items :: [transact_write_item], transact_write_items_opts) :: ExAws.Operation.JSON.t() @spec transact_write_items(items :: [transact_write_item]) :: ExAws.Operation.JSON.t() def transact_write_items(items, opts \\ []) do From 2226c922e2205e45e92305ae3002997a47622a58 Mon Sep 17 00:00:00 2001 From: Darren Klein Date: Fri, 27 Sep 2019 08:21:51 -0400 Subject: [PATCH 7/7] Version bump to 2.3.0 --- README.md | 18 +++++++++--------- config/dev.exs | 6 ++++++ config/test.exs.example | 15 +++++++++++++++ config/test_options.exs | 1 - mix.exs | 2 +- 5 files changed, 31 insertions(+), 11 deletions(-) create mode 100644 config/test.exs.example diff --git a/README.md b/README.md index f039be7..a7b44fb 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ along with `:ex_aws` and your preferred JSON codec / http client def deps do [ {:ex_aws, "~> 2.0"}, - {:ex_aws_dynamo, "~> 2.2"}, + {:ex_aws_dynamo, "~> 2.3"}, {:poison, "~> 3.0"}, {:hackney, "~> 1.9"}, ] @@ -49,22 +49,22 @@ The tests in `test/lib/dynamo/integration_test.exs` will attempt to run against ```elixir use Mix.Config -config :ex_aws, :dynamodb, - scheme: "http://", - host: "localhost", - port: CHOOSE_YOUR_TEST_PORT, - region: "us-east-1" - config :ex_aws, debug_requests: true, access_key_id: "abcd", secret_access_key: "1234", region: "us-east-1" + +config :ex_aws, :dynamodb, + scheme: "http://", + host: "localhost", + port: CHOOSE_YOUR_TEST_PORT, + region: "us-east-1" ``` -Before setting the `port`, be aware that `integration_test.exs` will create and delete tables with the names `"TestUsers", Test.User, "TestSeveralUsers", TestFoo, "test_books", "TestUsersWithRange", "TestTransactions", "TestTransactions2"` - be careful when setting the port, as these operations may interfere with your current tables if they share any of those names. +Before setting the `port`, be aware that `integration_test.exs` will create and delete tables with the names `"TestUsers", "Test.User", "TestSeveralUsers", "TestFoo", "test_books", "TestUsersWithRange", "TestTransactions", "TestTransactions2"` - be careful when setting the port, as these operations may affect your current tables if they share any of those names. -If you do not have a running local instance of DynamoDB and/or you don't provide a `config/test.exs` file, the integration tests will hang for a few seconds before returning `invalid` - this will not interfere with the successful execution of other tests. +If you do not have a running local instance of DynamoDB and/or you don't provide a `config/test.exs` file, the integration tests will hang for a few seconds before returning `invalid` - this will not interfere with the successful execution of the other tests. ## License diff --git a/config/dev.exs b/config/dev.exs index 699a109..39bc6ee 100644 --- a/config/dev.exs +++ b/config/dev.exs @@ -1,5 +1,11 @@ use Mix.Config +config :ex_aws, + debug_requests: true, + access_key_id: "abcd", + secret_access_key: "1234", + region: "us-east-1" + config :ex_aws, :dynamodb, scheme: "http://", host: "localhost", diff --git a/config/test.exs.example b/config/test.exs.example new file mode 100644 index 0000000..9ab4187 --- /dev/null +++ b/config/test.exs.example @@ -0,0 +1,15 @@ +# Sample test.exs configuration file. See README for more info. + +use Mix.Config + +config :ex_aws, + debug_requests: true, + access_key_id: "abcd", + secret_access_key: "1234", + region: "us-east-1" + +config :ex_aws, :dynamodb, + scheme: "http://", + host: "localhost", + port: CHOOSE_YOUR_TEST_PORT, + region: "us-east-1" diff --git a/config/test_options.exs b/config/test_options.exs index 3c31a55..a022499 100644 --- a/config/test_options.exs +++ b/config/test_options.exs @@ -1,5 +1,4 @@ use Mix.Config -require Logger config :ex_aws, :dynamodb, decode_sets: true diff --git a/mix.exs b/mix.exs index ae4903e..9068f11 100644 --- a/mix.exs +++ b/mix.exs @@ -1,7 +1,7 @@ defmodule ExAws.Dynamo.Mixfile do use Mix.Project - @version "2.2.2" + @version "2.3.0" @service "dynamo" @url "https://github.com/ex-aws/ex_aws_#{@service}" @name __MODULE__ |> Module.split() |> Enum.take(2) |> Enum.join(".")