Skip to content

Commit

Permalink
move opts out of the impl and into the spec
Browse files Browse the repository at this point in the history
  • Loading branch information
benwilson512 committed May 6, 2015
1 parent 0261e42 commit 7a9c216
Show file tree
Hide file tree
Showing 6 changed files with 101 additions and 53 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ A flexible easy to use set of clients AWS APIs.
- Minimal dependencies. Choose your favorite JSON / XML codec and HTTP client.
- Elixir streams to automatically retrieve paginated resources.
- Elixir protocols allow easy customization of Dynamo encoding / decoding.
- `mix kinesis.tail your-stream-name` task for easily grabbing the contents of a kinesis stream.
- `mix kinesis.tail your-stream-name` task for easily watching the contents of a kinesis stream.
- Simple. ExAws aims to provide a clear and consistent elixir wrapping around AWS APIs, not abstract them away entirely. For every action in a given AWS API there is a corresponding function within the appropriate module. Higher level abstractions like the aforementioned streams are in addition to and not instead of basic API calls.

## Getting started
Expand Down
47 changes: 44 additions & 3 deletions lib/ex_aws/dynamo/client.ex
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,12 @@ defmodule ExAws.Dynamo.Client do
## Tables
######################

@type exclusive_start_key :: [{atom, binary}] | %{atom => binary}
@type expression_attribute_names :: %{binary => binary}
@type expression_attribute_values :: [{atom, binary}] | %{atom => binary}
@type return_consumed_capacity :: :none | :total | :indexes


@doc "List tables"
defcallback list_tables() :: ExAws.Request.response_t

Expand Down Expand Up @@ -142,8 +148,21 @@ defmodule ExAws.Dynamo.Client do
Parameters with keys that are automatically annotated with dynamo types are:
`[:exclusive_start_key, :expression_attribute_names]`
"""
@type scan_opts :: [
{:exclusive_start_key, exclusive_start_key} |
{:expression_attribute_names, expression_attribute_names} |
{:expression_attribute_values, expression_attribute_values} |
{:filter_expression, binary} |
{:index_name, binary} |
{:limit, pos_integer} |
{:projection_expression, binary} |
{:return_consumed_capacity, return_consumed_capacity} |
{:segment, non_neg_integer} |
{:select, any} |
{:total_segments, :all_attributes | :count | :specific_attributes}]
defcallback scan(table_name :: binary) :: ExAws.Request.response_t
defcallback scan(table_name :: binary, opts :: Keyword.t) :: ExAws.Request.response_t
defcallback scan(table_name :: binary, opts :: scan_opts) :: ExAws.Request.response_t


@doc """
Stream records from table
Expand All @@ -156,7 +175,7 @@ defmodule ExAws.Dynamo.Client do
```
"""
defcallback stream_scan(table_name :: binary) :: ExAws.Request.response_t
defcallback stream_scan(table_name :: binary, opts :: Keyword.t) :: ExAws.Request.response_t
defcallback stream_scan(table_name :: binary, opts :: scan_opts) :: ExAws.Request.response_t

@doc """
Query Table
Expand All @@ -166,8 +185,21 @@ defmodule ExAws.Dynamo.Client do
Parameters with keys that are automatically annotated with dynamo types are:
`[:exclusive_start_key, :expression_attribute_names]`
"""
@type query_opts :: [
{:consistent_read, boolean} |
{:exclusive_start_key, exclusive_start_key} |
{:expression_attribute_names, expression_attribute_names} |
{:expression_attribute_values, expression_attribute_values} |
{:filter_expression, binary} |
{:index_name, binary} |
{:key_conditions_expression, binary} |
{:limit, pos_integer} |
{:projection_expression, binary} |
{:return_consumed_capacity, return_consumed_capacity} |
{:scan_index_forward, boolean} |
{:select, :all_attributes | :all_projected_attributes | :specific_attributes | :count}]
defcallback query(table_name :: binary) :: ExAws.Request.response_t
defcallback query(table_name :: binary, opts :: Keyword.t) :: ExAws.Request.response_t
defcallback query(table_name :: binary, opts :: query_opts) :: ExAws.Request.response_t

@doc """
Get up to 100 items (16mb)
Expand Down Expand Up @@ -213,7 +245,16 @@ defmodule ExAws.Dynamo.Client do
defcallback batch_write_item(%{String.t => %{}}) :: ExAws.Request.response_t

@doc "Put item in table"
@type put_item_opts :: [
{:condition_expression, binary} |
{:expression_attribute_names, expression_attribute_names} |
{:expression_attribute_values, expression_attribute_values} |
{:return_consumed_capacity, return_consumed_capacity} |
{:return_item_collection_metrics, :size | :none } |
{:return_values, :none | :all_old | :updated_old | :all_new | :updated_new}
]
defcallback put_item(table_name :: binary, record :: %{}) :: ExAws.Request.response_t
defcallback put_item(table_name :: binary, record :: %{}, opts :: put_item_opts) :: ExAws.Request.response_t

@doc "Get item from table"
defcallback get_item(table_name :: binary, primary_key_value :: binary) :: ExAws.Request.response_t
Expand Down
1 change: 0 additions & 1 deletion lib/ex_aws/dynamo/decodable.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ defprotocol ExAws.Dynamo.Decodable do
@moduledoc """
Allows custom decoding logic for your struct.
"""

def decode(value)
end

Expand Down
3 changes: 3 additions & 0 deletions lib/ex_aws/dynamo/encodable.ex
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
defprotocol ExAws.Dynamo.Encodable do

@type t :: any

@doc "Converts an elixir value into a map tagging the value with its dynamodb type"
def encode(value)
end
Expand Down
99 changes: 52 additions & 47 deletions lib/ex_aws/dynamo/impl.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ defmodule ExAws.Dynamo.Impl do
import ExAws.Utils, only: [camelize_keys: 1, camelize_keys: 2]
use ExAws.Actions

@nested_opts [:exclusive_start_key, :expression_attribute_values, :expression_attribute_names]
@upcase_opts [:return_values, :return_item_collection_metrics, :select, :total_segments]
@special_opts @nested_opts ++ @upcase_opts


defdelegate stream_scan(client, name), to: ExAws.Dynamo.Lazy
defdelegate stream_scan(client, name, opts), to: ExAws.Dynamo.Lazy
Expand Down Expand Up @@ -86,24 +90,8 @@ defmodule ExAws.Dynamo.Impl do

## Records
######################
@opts [
:exclusive_start_key,
:expression_attribute_names,
:expression_attribute_values,
:filter_expression,
:index_name,
:limit,
:projection_expression,
:return_consumed_capacity,
:segment,
:select,
:total_segments
]
@special_opts [:exclusive_start_key, :expression_attribute_values, :expression_attribute_names]
def scan(client, name, opts \\ []) do
opts = opts
|> Enum.into(%{})
|> Map.take(@opts)
opts = opts |> Enum.into(%{})

regular_opts = opts
|> Map.drop(@special_opts)
Expand All @@ -113,29 +101,14 @@ defmodule ExAws.Dynamo.Impl do
|> build_exclusive_start_key(opts)
|> build_expression_attribute_names(opts)
|> build_expression_attribute_values(opts)
|> build_total_segments(opts)
|> Map.merge(regular_opts)
|> client.request(:scan)
end

@opts [
:consistent_read,
:exclusive_start_key,
:expression_attribute_names,
:expression_attribute_values,
:filter_expression,
:index_name,
:key_conditions_expression,
:limit,
:projection_expression,
:return_consumed_capacity,
:scan_index_forward,
:select
]
@special_opts [:exclusive_start_key, :expression_attribute_values, :expression_attribute_names]
def query(client, name, opts \\ []) do
opts = opts
|> Enum.into(%{})
|> Map.take(@opts)

regular_opts = opts
|> Map.drop(@special_opts)
Expand All @@ -144,6 +117,7 @@ defmodule ExAws.Dynamo.Impl do
%{"TableName" => name}
|> build_exclusive_start_key(opts)
|> build_expression_attribute_values(opts)
|> build_select(opts)
|> Map.merge(regular_opts)
|> client.request(:query)
end
Expand All @@ -164,23 +138,18 @@ defmodule ExAws.Dynamo.Impl do
|> client.request(:batch_get_item)
end

@opts [
:condition_expression,
:conditional_operator,
:expression_attribute_names,
:expression_attribute_values,
:return_consumed_capacity,
:return_item_collection_metrics,
:return_values
]
def put_item(client, name, record, opts \\ []) do
opts
|> Enum.into(%{})

%{
|> Map.drop(@special_opts)
|> build_exclusive_start_key(opts)
|> build_expression_attribute_values(opts)
|> build_return_item_collection_metrics(opts)
|> build_return_values(opts)
|> Map.merge(%{
"TableName" => name,
"Item" => Dynamo.Encoder.encode(record)
} |> client.request(:put_item)
}) |> client.request(:put_item)
end

def batch_write_item(client, data) do
Expand Down Expand Up @@ -225,13 +194,14 @@ defmodule ExAws.Dynamo.Impl do
end)
end

# Expects exclusive_start_key shape like
## Builders for special options
###################

defp build_exclusive_start_key(data, %{exclusive_start_key: start_key}) do
Map.put(data, "ExclusiveStartKey", start_key |> encode_values)
end
defp build_exclusive_start_key(data, _), do: data


defp build_expression_attribute_names(data, %{expression_attribute_names: names}) do
Map.put(data, "ExpressionAttributeNames", names |> Enum.into(%{}))
end
Expand All @@ -242,6 +212,41 @@ defmodule ExAws.Dynamo.Impl do
end
defp build_expression_attribute_values(data, _), do: data

## TODO: metaprogram these upcase ones.

defp build_total_segments(data, %{total_segments: segments}) do
Map.put(data, "TotalSegments", segments |> upcase)
end
defp build_total_segments(data, _), do: data

defp build_return_item_collection_metrics(data, %{return_item_collection_metrics: metrics}) do
Map.put(data, "ReturnItemCollectionMetrics", metrics |> upcase)
end
defp build_return_item_collection_metrics(data, _), do: data

defp build_select(data, %{select: select}) do
Map.put(data, "Select", select |> upcase)
end
defp build_select(data, _), do: data

defp build_return_values(data, %{return_values: return_values}) do
Map.put(data, "ReturnValues", return_values |> upcase)
end
defp build_return_values(data, _), do: data

## Various other helpers
################

defp upcase(value) when is_atom(value) do
value
|> Atom.to_string
|> String.upcase
end

defp upcase(value) when is_binary(value) do
String.upcase(value)
end

defp encode_values(dict) do
Enum.reduce(dict, %{}, fn {attr, value}, attribute_values ->
Map.put(attribute_values, attr, Dynamo.Encoder.encode(value))
Expand Down
2 changes: 1 addition & 1 deletion test/lib/ex_aws/dynamo/integration_test.exs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
defmodule ExAws.DynamoIntegrationTest do
alias Test.Dynamo
alias ExAws.Dynamo.Decoder
use ExUnit.Case, async: false
use ExUnit.Case, async: true

setup_all do
Dynamo.delete_table("Users")
Expand Down

0 comments on commit 7a9c216

Please sign in to comment.