From 09d25caaac7e46b5b8d1a1d8ead5720f3a864e71 Mon Sep 17 00:00:00 2001 From: itamm15 Date: Fri, 21 Nov 2025 09:27:07 +0100 Subject: [PATCH 1/3] chore: add support for AWS_ENDPOINT in Client.create --- lib/aws/client.ex | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/lib/aws/client.ex b/lib/aws/client.ex index 369a1e57..2f1e0adc 100644 --- a/lib/aws/client.ex +++ b/lib/aws/client.ex @@ -72,6 +72,7 @@ defmodule AWS.Client do @aws_access_key_id "AWS_ACCESS_KEY_ID" @aws_secret_access_key "AWS_SECRET_ACCESS_KEY" @aws_session_token "AWS_SESSION_TOKEN" + @aws_endpoint "AWS_ENDPOINT" @aws_default_region "AWS_DEFAULT_REGION" @doc """ @@ -91,35 +92,33 @@ defmodule AWS.Client do def create(region) do case {System.get_env(@aws_access_key_id), System.get_env(@aws_secret_access_key), - System.get_env(@aws_session_token)} do - {nil, _, _} -> + System.get_env(@aws_session_token), System.get_env(@aws_endpoint)} do + {nil, _secret_key, _session_token, _endpoint} -> raise RuntimeError, "missing access key id" - {_, nil, _} -> + {_access_key, nil, _session_token, _endpoint} -> raise RuntimeError, "missing secret access key" - {access_key_id, secret_access_key, nil} -> - create(access_key_id, secret_access_key, region) - - {access_key_id, secret_access_key, token} -> - create(access_key_id, secret_access_key, token, region) + {access_key_id, secret_access_key, token, endpoint} -> + create(access_key_id, secret_access_key, token, region, endpoint) end end def create(access_key_id, secret_access_key, region) do - %AWS.Client{ - access_key_id: access_key_id, - secret_access_key: secret_access_key, - region: region - } + create(access_key_id, secret_access_key, nil, region, nil) end def create(access_key_id, secret_access_key, token, region) do + create(access_key_id, secret_access_key, token, region, nil) + end + + def create(access_key_id, secret_access_key, token, region, endpoint) do %AWS.Client{ access_key_id: access_key_id, secret_access_key: secret_access_key, session_token: token, - region: region + region: region, + endpoint: endpoint } end From 0bcf784eb8bcc1a4c93af0b8aea5bb9a79817ab6 Mon Sep 17 00:00:00 2001 From: itamm15 Date: Fri, 21 Nov 2025 09:49:09 +0100 Subject: [PATCH 2/3] test: cover create/n in tests --- test/aws/client_test.exs | 135 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 135 insertions(+) diff --git a/test/aws/client_test.exs b/test/aws/client_test.exs index 2bc510aa..157241dd 100644 --- a/test/aws/client_test.exs +++ b/test/aws/client_test.exs @@ -122,5 +122,140 @@ defmodule AWS.ClientTest do end end + describe "create/n" do + setup :prune_envs + + test "create/0 reads region from AWS_DEFAULT_REGION and builds client" do + System.put_env("AWS_ACCESS_KEY_ID", "AK") + System.put_env("AWS_SECRET_ACCESS_KEY", "SK") + System.put_env("AWS_DEFAULT_REGION", "us-east-1") + + client = AWS.Client.create() + + assert %AWS.Client{ + access_key_id: "AK", + secret_access_key: "SK", + session_token: nil, + region: "us-east-1", + endpoint: nil + } == client + end + + test "create/0 raises when AWS_DEFAULT_REGION missing" do + System.put_env("AWS_ACCESS_KEY_ID", "AK") + System.put_env("AWS_SECRET_ACCESS_KEY", "SK") + + assert_raise RuntimeError, "missing default region", fn -> + AWS.Client.create() + end + end + + test "create/1 reads credentials, token and endpoint from env" do + System.put_env("AWS_ACCESS_KEY_ID", "AK") + System.put_env("AWS_SECRET_ACCESS_KEY", "SK") + System.put_env("AWS_SESSION_TOKEN", "TOK") + System.put_env("AWS_ENDPOINT", "example.com") + + client = AWS.Client.create("us-west-2") + + assert %AWS.Client{ + access_key_id: "AK", + secret_access_key: "SK", + session_token: "TOK", + region: "us-west-2", + endpoint: "example.com" + } == client + end + + test "create/1 without token uses nil and endpoint from env" do + System.put_env("AWS_ACCESS_KEY_ID", "AK") + System.put_env("AWS_SECRET_ACCESS_KEY", "SK") + System.put_env("AWS_ENDPOINT", "example.com") + + client = AWS.Client.create("eu-west-1") + + assert %AWS.Client{ + access_key_id: "AK", + secret_access_key: "SK", + session_token: nil, + region: "eu-west-1", + endpoint: "example.com" + } == client + end + + test "create/1 raises when access key is missing" do + System.put_env("AWS_SECRET_ACCESS_KEY", "SK") + + assert_raise RuntimeError, "missing access key id", fn -> + AWS.Client.create("us-east-2") + end + end + + test "create/1 raises when secret access key is missing" do + System.put_env("AWS_ACCESS_KEY_ID", "AK") + + assert_raise RuntimeError, "missing secret access key", fn -> + AWS.Client.create("us-east-2") + end + end + + test "create/3 sets fields without token or endpoint" do + client = AWS.Client.create("AK", "SK", "eu-north-1") + + assert %AWS.Client{ + access_key_id: "AK", + secret_access_key: "SK", + session_token: nil, + region: "eu-north-1", + endpoint: nil + } == client + end + + test "create/4 sets token and leaves endpoint nil" do + client = AWS.Client.create("AK", "SK", "TOKEN", "ap-south-1") + + assert %AWS.Client{ + access_key_id: "AK", + secret_access_key: "SK", + session_token: "TOKEN", + region: "ap-south-1", + endpoint: nil + } == client + end + + test "create/5 sets token and endpoint" do + client = AWS.Client.create("AK", "SK", "TOKEN", "sa-east-1", "custom.local") + + assert %AWS.Client{ + access_key_id: "AK", + secret_access_key: "SK", + session_token: "TOKEN", + region: "sa-east-1", + endpoint: "custom.local" + } == client + end + + test "create/3 equals create/5 with nil token and endpoint" do + c3 = AWS.Client.create("AK", "SK", "us-east-1") + c5 = AWS.Client.create("AK", "SK", nil, "us-east-1", nil) + assert c3 == c5 + end + end + + defp prune_envs(_context) do + keys = + [ + "AWS_ACCESS_KEY_ID", + "AWS_SECRET_ACCESS_KEY", + "AWS_SESSION_TOKEN", + "AWS_ENDPOINT", + "AWS_DEFAULT_REGION" + ] + + on_exit(fn -> Enum.each(keys, &System.delete_env/1) end) + + :ok + end + defp url(bypass), do: "http://localhost:#{bypass.port}/" end From 43c42fa536c9e1d1fe830e9fd1e8972d333725cc Mon Sep 17 00:00:00 2001 From: itamm15 Date: Fri, 21 Nov 2025 10:01:26 +0100 Subject: [PATCH 3/3] chore: add typespec for create/n --- lib/aws/client.ex | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/lib/aws/client.ex b/lib/aws/client.ex index 2f1e0adc..1ea96860 100644 --- a/lib/aws/client.ex +++ b/lib/aws/client.ex @@ -52,12 +52,16 @@ defmodule AWS.Client do Check `put_endpoint/2` for more details. """ @type endpoint_config :: binary() | {:keep_prefixes, binary()} | (map() -> binary()) | nil + @type access_key_id :: binary() + @type secret_access_key :: binary() + @type token :: binary() | nil + @type region :: binary() @type t :: %__MODULE__{ - access_key_id: binary() | nil, - secret_access_key: binary() | nil, - session_token: binary() | nil, - region: binary() | nil, + access_key_id: access_key_id() | nil, + secret_access_key: secret_access_key() | nil, + session_token: token() | nil, + region: region() | nil, service: binary() | nil, endpoint: endpoint_config(), proto: binary(), @@ -83,6 +87,17 @@ defmodule AWS.Client do """ def default_endpoint, do: @aws_default_endpoint + @spec create() :: t() + @spec create(region()) :: t() + @spec create(access_key_id(), secret_access_key(), region()) :: t() + @spec create(access_key_id(), secret_access_key(), token(), region()) :: t() + @spec create( + access_key_id(), + secret_access_key(), + token() | nil, + region(), + endpoint_config() | nil + ) :: t() def create() do case System.get_env(@aws_default_region) do nil -> raise RuntimeError, "missing default region"