From e42900dec066a0d9a4d4a2e10d2d1236dd438f28 Mon Sep 17 00:00:00 2001 From: Brent Anderson Date: Mon, 3 Apr 2023 16:42:48 -0500 Subject: [PATCH 1/2] feat(kno-3079): idempotency keys for workflow triggers --- CHANGELOG.md | 3 +++ lib/knock.ex | 2 +- lib/knock/api.ex | 11 +++++++---- lib/knock/resources/workflows.ex | 9 ++++++--- mix.exs | 2 +- 5 files changed, 18 insertions(+), 9 deletions(-) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..cf9cd71 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,3 @@ +## v0.4.7 + +* Add support for `idempotency_key` on workflow triggers \ No newline at end of file diff --git a/lib/knock.ex b/lib/knock.ex index 7376f15..04dfcd1 100644 --- a/lib/knock.ex +++ b/lib/knock.ex @@ -88,5 +88,5 @@ defmodule Knock do @doc """ Issues a notify call, triggering a workflow with the given key. """ - defdelegate notify(client, key, properties), to: Knock.Workflows, as: :trigger + defdelegate notify(client, key, properties, options), to: Knock.Workflows, as: :trigger end diff --git a/lib/knock/api.ex b/lib/knock/api.ex index 0a09a28..5b6660d 100644 --- a/lib/knock/api.ex +++ b/lib/knock/api.ex @@ -34,7 +34,7 @@ defmodule Knock.Api do @spec put(Client.t(), String.t(), map(), options()) :: response() def put(client, path, body, opts \\ []) do client - |> http_client() + |> http_client(opts) |> Tesla.put(path, body, opts) |> handle_response() end @@ -45,7 +45,7 @@ defmodule Knock.Api do @spec post(Client.t(), String.t(), map(), options()) :: response() def post(client, path, body, opts \\ []) do client - |> http_client() + |> http_client(opts) |> Tesla.post(path, body, opts) |> handle_response() end @@ -77,7 +77,7 @@ defmodule Knock.Api do """ def library_version, do: @lib_version - defp http_client(config) do + defp http_client(config, opts \\ []) do middleware = [ {Tesla.Middleware.BaseUrl, config.host <> "/v1"}, {Tesla.Middleware.JSON, engine: config.json_client}, @@ -85,9 +85,12 @@ defmodule Knock.Api do [ {"Authorization", "Bearer " <> config.api_key}, {"User-Agent", "knocklabs/knock-elixir@#{library_version()}"} - ]} + ] ++ maybe_idempotency_key_header(opts)} ] Tesla.client(middleware, config.adapter) end + + defp maybe_idempotency_key_header(idempotency_key: key), do: [{"Idempotency-Key", key}] + defp maybe_idempotency_key_header(_), do: [] end diff --git a/lib/knock/resources/workflows.ex b/lib/knock/resources/workflows.ex index 1442444..97ad562 100644 --- a/lib/knock/resources/workflows.ex +++ b/lib/knock/resources/workflows.ex @@ -8,10 +8,13 @@ defmodule Knock.Workflows do Executes a notify call for the workflow with the given key. Note: properties must contain at least `recipents` for the call to be valid. + + Options can include: + * `idempotency_key`: A unique key to prevent duplicate requests """ - @spec trigger(Knock.Client.t(), String.t(), map()) :: Api.response() - def trigger(client, key, properties) do - Api.post(client, "/workflows/#{key}/trigger", properties) + @spec trigger(Knock.Client.t(), String.t(), map(), keyword()) :: Api.response() + def trigger(client, key, properties, options \\ []) do + Api.post(client, "/workflows/#{key}/trigger", properties, options) end @doc """ diff --git a/mix.exs b/mix.exs index 179fe98..41c5b0d 100644 --- a/mix.exs +++ b/mix.exs @@ -2,7 +2,7 @@ defmodule Knock.MixProject do use Mix.Project @source_url "https://github.com/knocklabs/knock-elixir" - @version "0.4.6" + @version "0.4.7" def project do [ From c49feaf363c295aa96ce548fcf83df8458d6f5cf Mon Sep 17 00:00:00 2001 From: Brent Anderson Date: Wed, 5 Apr 2023 13:24:59 -0500 Subject: [PATCH 2/2] fix: pr feedback --- lib/knock/api.ex | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/knock/api.ex b/lib/knock/api.ex index 5b6660d..fa0c314 100644 --- a/lib/knock/api.ex +++ b/lib/knock/api.ex @@ -85,12 +85,14 @@ defmodule Knock.Api do [ {"Authorization", "Bearer " <> config.api_key}, {"User-Agent", "knocklabs/knock-elixir@#{library_version()}"} - ] ++ maybe_idempotency_key_header(opts)} + ] ++ maybe_idempotency_key_header(Map.new(opts))} ] Tesla.client(middleware, config.adapter) end - defp maybe_idempotency_key_header(idempotency_key: key), do: [{"Idempotency-Key", key}] + defp maybe_idempotency_key_header(%{idempotency_key: key}) when is_binary(key), + do: [{"Idempotency-Key", key}] + defp maybe_idempotency_key_header(_), do: [] end