defmodule ExAws.Lambda do
@moduledoc """
Operations on ExAws Lambda.
"""
import ExAws.Utils, only: [camelize_key: 1, camelize_keys: 1, upcase: 1]
require Logger
@actions %{
add_permission: :post,
create_event_source_mapping: :post,
create_function: :post,
delete_event_source_mapping: :delete,
delete_function: :delete,
get_event_source_mapping: :get,
get_function: :get,
get_function_configuration: :get,
get_policy: :get,
invoke: :post,
invoke_async: :post,
list_event_source_mappings: :get,
list_functions: :get,
remove_permission: :delete,
update_event_source_mapping: :put,
update_function_code: :put,
update_function_configuration: :put,
tag_resource: :post
}
@type starting_position_vals :: :trim_horizon | :latest
@type add_permission_opts :: [
{:source_account, binary}
| {:source_arn, binary}
]
@doc """
Adds a permission to the access policy associated with the specified AWS Lambda function.
Action pattern: (lambda:[*]|lambda:[a-zA-Z]+|[*])
"""
@spec add_permission(
function_name :: binary,
principal :: binary,
action :: binary,
statement_id :: binary
) :: ExAws.Operation.JSON.t()
@spec add_permission(
function_name :: binary,
principal :: binary,
action :: binary,
statement_id :: binary,
opts :: add_permission_opts
) :: ExAws.Operation.JSON.t()
def add_permission(function_name, principal, action, statement_id, opts \\ []) do
data =
opts
|> normalize_opts
|> Map.merge(%{
"Action" => action,
"Principal" => principal,
"StatementId" => statement_id
})
request(:add_permission, data, "/2015-03-31/functions/#{function_name}/versions/HEAD/policy")
end
@type create_event_source_mapping_opts :: [
{:batch_size, pos_integer}
| {:enabled, boolean}
]
@doc """
Creates a stream based event source for a function.
"""
@spec create_event_source_mapping(
function_name :: binary,
event_source_arn :: binary,
starting_position :: starting_position_vals
) :: ExAws.Operation.JSON.t()
@spec create_event_source_mapping(
function_name :: binary,
event_source_arn :: binary,
starting_position :: starting_position_vals,
opts :: create_event_source_mapping_opts
) :: ExAws.Operation.JSON.t()
def create_event_source_mapping(function_name, event_source_arn, starting_position, opts \\ []) do
data =
opts
|> normalize_opts
|> Map.merge(%{
"FunctionName" => function_name,
"EventSourceArn" => event_source_arn,
"StartingPosition" => starting_position |> upcase
})
request(:create_event_source_mapping, data, "/2015-03-31/event-source-mappings/")
end
@type create_function_opts :: [
{:description, binary}
| {:memory_size, pos_integer}
| {:timeout, pos_integer}
]
@doc """
Create a function.
Runtime defaults to nodejs, as that is the only one available.
"""
@spec create_function(
function_name :: binary,
handler :: binary,
zipfile :: binary
) :: ExAws.Operation.JSON.t()
@spec create_function(
function_name :: binary,
handler :: binary,
zipfile :: binary,
opts :: create_function_opts
) :: ExAws.Operation.JSON.t()
def create_function(function_name, handler, zipfile, opts \\ []) do
data =
opts
|> normalize_opts
|> Map.merge(%{
"FunctionName" => function_name,
"Handler" => handler,
"Code" => %{"ZipFile" => zipfile}
})
request(:create_function, data, "/2015-03-31/functions")
end
@doc """
Delete an event source mapping.
"""
@spec delete_event_source_mapping(source_mapping_uuid :: binary) :: ExAws.Operation.JSON.t()
def delete_event_source_mapping(source_mapping_uuid) do
request(
:delete_event_source_mapping,
%{},
"/2015-03-31/event-source-mappings/#{source_mapping_uuid}"
)
end
@doc """
Delete a lambda function.
"""
@spec delete_function(function_name :: binary) :: ExAws.Operation.JSON.t()
def delete_function(function_name) do
request(:delete_function, %{}, "/2015-03-31/functions/#{function_name}")
end
@doc """
Get an event source mapping.
"""
@spec get_event_source_mapping(source_mapping_uuid :: binary) :: ExAws.Operation.JSON.t()
def get_event_source_mapping(source_mapping_uuid) do
request(
:get_event_source_mapping,
%{},
"/2015-03-31/event-source-mappings/#{source_mapping_uuid}"
)
end
@doc """
Get a function.
"""
@spec get_function(function_name :: binary) :: ExAws.Operation.JSON.t()
def get_function(function_name) do
request(:get_function, %{}, "/2015-03-31/functions/#{function_name}/versions/HEAD")
end
@doc """
Get a function configuration.
"""
@spec get_function_configuration(function_name :: binary) :: ExAws.Operation.JSON.t()
def get_function_configuration(function_name) do
request(
:get_function_configuration,
%{},
"/2015-03-31/functions/#{function_name}/versions/HEAD/configuration"
)
end
@doc """
Get a function access policy.
"""
@spec get_policy(function_name :: binary) :: ExAws.Operation.JSON.t()
def get_policy(function_name) do
request(:get_policy, %{}, "/2015-03-31/functions/#{function_name}/versions/HEAD/policy")
end
@doc """
Invoke a lambda function.
"""
@type invoke_opts :: [
{:invocation_type, :event | :request_response | :dry_run}
| {:log_type, :none | :tail}
| {:qualifier, String.t()}
]
@spec invoke(function_name :: binary, payload :: map(), client_context :: map()) ::
ExAws.Operation.JSON.t()
@spec invoke(
function_name :: binary,
payload :: map(),
client_context :: map(),
opts :: invoke_opts
) :: ExAws.Operation.JSON.t()
@context_header "X-Amz-Client-Context"
def invoke(function_name, payload, client_context, opts \\ []) do
{qualifier, opts} = Map.pop(Enum.into(opts, %{}), :qualifier)
headers =
[
invocation_type: "X-Amz-Invocation-Type",
log_type: "X-Amz-Log-Type",
xray_trace_id: "X-Amzn-Trace-Id"
]
|> Enum.reduce([], fn {opt, header}, headers ->
case Map.fetch(opts, opt) do
:error -> headers
{:ok, nil} -> headers
{:ok, value} -> [{header, value |> camelize_key} | headers]
end
end)
headers =
case client_context do
%{} ->
headers
context ->
[{@context_header, context} | headers]
end
url = "/2015-03-31/functions/#{function_name}/invocations"
url = if qualifier, do: url <> "?Qualifier=#{qualifier}", else: url
request(:invoke, payload, url, [], headers, fn operation, config ->
headers =
operation.headers
|> List.keyfind(@context_header, 0, nil)
|> case do
nil ->
operation.headers
{_, val} ->
new_val = val |> config.json_codec.encode! |> Base.encode64()
List.keyreplace(operation.headers, @context_header, 0, {@context_header, new_val})
end
%{operation | headers: headers}
end)
end
@doc """
Invoke a lambda function asynchronously.
"""
@spec invoke_async(function_name :: binary, args :: map()) :: ExAws.Operation.JSON.t()
def invoke_async(function_name, args) do
Logger.info(
"This API is deprecated. See invoke/5 with the Event value set as invocation type"
)
request(
:invoke,
args |> normalize_opts,
"/2014-11-13/functions/#{function_name}/invoke-async/"
)
end
@doc """
List event source mappings.
"""
@type list_event_source_mappings_opts :: [
{:event_source_arn, binary}
| {:function_name, binary}
| {:marker, binary}
| {:max_items, pos_integer}
]
@spec list_event_source_mappings() :: ExAws.Operation.JSON.t()
@spec list_event_source_mappings(opts :: list_event_source_mappings_opts) ::
ExAws.Operation.JSON.t()
def list_event_source_mappings(opts \\ []) do
params =
opts
|> normalize_opts
request(:list_event_source_mappings, %{}, "/2015-03-31/event-source-mappings/", params)
end
@doc """
List functions.
"""
@type list_functions_opts :: [
{:marker, binary}
| {:max_items, pos_integer}
]
@spec list_functions() :: ExAws.Operation.JSON.t()
@spec list_functions(opts :: list_functions_opts) :: ExAws.Operation.JSON.t()
def list_functions(opts \\ []) do
request(:list_functions, %{}, "/2015-03-31/functions/", normalize_opts(opts))
end
@doc """
Remove individual permissions from an function's access policy.
"""
@spec remove_permission(function_name :: binary, statement_id :: binary) ::
ExAws.Operation.JSON.t()
def remove_permission(function_name, statement_id) do
request(
:remove_permission,
%{},
"/2015-03-31/functions/#{function_name}/versions/HEAD/policy/#{statement_id}"
)
end
@doc """
Update event source mapping.
"""
@spec update_event_source_mapping(uuid :: binary, attrs_to_update :: map()) ::
ExAws.Operation.JSON.t()
def update_event_source_mapping(uuid, attrs_to_update) do
request(
:update_event_source_mapping,
attrs_to_update,
"/2015-03-31/event-source-mappings/#{uuid}"
)
end
@doc """
Update function code.
"""
@spec update_function_code(function_name :: binary, zipfile :: binary) ::
ExAws.Operation.JSON.t()
def update_function_code(function_name, zipfile) do
data = %{"ZipFile" => zipfile}
request(
:update_function_code,
data,
"/2015-03-31/functions/#{function_name}/versions/HEAD/code"
)
end
@doc """
Update a function configuration.
"""
@spec update_function_configuration(function_name :: binary, configuration :: map()) ::
ExAws.Operation.JSON.t()
def update_function_configuration(function_name, configuration) do
data = configuration |> normalize_opts
request(
:update_function_configuration,
data,
"/2015-03-31/functions/#{function_name}/configuration"
)
end
@doc """
Tag the Lambda resource.
"""
@spec tag_resource(function_name :: binary, tags :: map()) :: ExAws.Operation.JSON.t()
def tag_resource(function_arn, tags) do
data = %{"Tags" => Map.new(tags, fn {k, v} -> {to_string(k), v} end)}
request(
:tag_resource,
data,
"/2017-03-31/tags/#{function_arn}"
)
end
defp normalize_opts(opts) do
opts
|> Map.new()
|> camelize_keys
end
defp request(action, data, path, params \\ [], headers \\ [], before_request \\ nil) do
path = [path, "?", params |> URI.encode_query()] |> IO.iodata_to_binary()
http_method = @actions |> Map.fetch!(action)
ExAws.Operation.JSON.new(:lambda, %{
http_method: http_method,
path: path,
data: data,
headers: [{"content-type", "application/json"} | headers],
before_request: before_request
})
end
end