Packages

TempelAws is a service module for Tempel for AWS S3

Current section

Files

Jump to
tempel_aws lib tempel_aws.ex
Raw

lib/tempel_aws.ex

defmodule TempelAws do
@moduledoc """
This module is a service module for [tempel](https://bitbucket.org/aripurna/tempel) to handle
file persistence using AWS S3
"""
@spec init(
opts :: %{
aws_access_key: String.t(),
aws_secret_key: String.t(),
aws_region: String.t(),
aws_bucket: String.t()
}
) :: map()
def init(opts) do
access_key = Map.get(opts, :aws_access_key)
secret_key = Map.get(opts, :aws_secret_key)
region = Map.get(opts, :aws_region)
client = AWS.Client.create(access_key, secret_key, region)
Map.merge(opts, %{aws_client: client})
end
@spec save(
file ::
%{filename: binary(), content_type: binary(), path: binary()}
| %{filename: binary(), content_type: binary(), iodata: binary()},
opts :: map()
) ::
{:ok, binary()} | {:error, any()}
def save(file, opts) do
filename = map_by_atom_or_string(file, :filename)
content_type = map_by_atom_or_string(file, :content_type)
case extract_file(file) do
{:ok, iodata} -> write(iodata, content_type, filename, opts)
err -> err
end
end
@spec to_url(opts :: map(), object_key :: binary()) :: String.t()
def to_url(opts, object_key) do
ttl = map_by_atom_or_string(opts, :ttl) || 1800
bucket = map_by_atom_or_string(opts, :aws_bucket)
region = map_by_atom_or_string(opts, :aws_region)
access_key = map_by_atom_or_string(opts, :aws_access_key)
secret_key = map_by_atom_or_string(opts, :aws_secret_key)
uri =
URI.parse("https://#{bucket}.s3.#{region}.amazonaws.com/#{AWS.Util.encode_uri(object_key)}")
now = NaiveDateTime.truncate(NaiveDateTime.utc_now(), :second)
:aws_signature.sign_v4_query_params(
access_key,
secret_key,
region,
"s3",
NaiveDateTime.to_erl(now),
"GET",
URI.to_string(uri),
ttl: ttl,
body_digest: "UNSIGNED-PAYLOAD"
)
end
@spec delete(opts :: map(), object_keys :: list(binary())) :: :ok | {:error, reason :: any}
def delete(opts, object_keys) do
client = map_by_atom_or_string(opts, :aws_client)
bucket = map_by_atom_or_string(opts, :aws_bucket)
result =
Enum.reduce(object_keys, [], fn key, acc ->
case AWS.S3.delete_object(client, bucket, key, %{}) do
{:ok, _, _} -> acc
{:error, reason} -> acc ++ [reason]
end
end)
case result do
[] -> :ok
errors -> {:error, errors}
end
end
defp write(file, content_type, filename, opts) do
client = Map.get(opts, :aws_client)
bucket = Map.get(opts, :aws_bucket)
md5 = :crypto.hash(:md5, file) |> Base.encode64()
options = %{"Body" => file, "ContentMD5" => md5, "ContentType" => content_type}
case AWS.S3.put_object(client, bucket, filename, options) do
{:ok, _, _} -> {:ok, filename}
err -> err
end
end
defp extract_file(file) do
if path = map_by_atom_or_string(file, :path) do
File.read(path)
else
{:ok, map_by_atom_or_string(file, :iodata)}
end
end
defp map_by_atom_or_string(map, key) do
Map.get(map, key) || Map.get(map, Atom.to_string(key))
end
end