Skip to content

Commit

Permalink
Create module for SDK response and simplify invoke
Browse files Browse the repository at this point in the history
  • Loading branch information
darwin67 committed Nov 23, 2023
1 parent db42a3a commit 57c5b22
Show file tree
Hide file tree
Showing 4 changed files with 127 additions and 58 deletions.
23 changes: 11 additions & 12 deletions lib/inngest/headers.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,16 @@ defmodule Inngest.Headers do
Header values used by the SDK
"""

def env, do: "X-Inngest-Env"
def forwarded_for, do: "X-Forwarded-For"
def framework, do: "X-Inngest-Framework"
def platform, do: "X-Inngest-Platform"
def sdk, do: "X-Inngest-SDK"
def signature, do: "X-Inngest-Signature"
def server_kind, do: "X-Inngest-Server-Kind"
def req_version, do: "X-Inngest-Req-Version"
def server_timing, do: "Server-Timing"
def env, do: "x-inngest-env"
def forwarded_for, do: "x-forwarded-for"
def framework, do: "x-inngest-framework"
def platform, do: "x-inngest-platform"

Check warning on line 9 in lib/inngest/headers.ex

View check run for this annotation

Codecov / codecov/patch

lib/inngest/headers.ex#L6-L9

Added lines #L6 - L9 were not covered by tests
def sdk_version, do: "x-inngest-sdk"
def signature, do: "x-inngest-signature"
def server_kind, do: "x-inngest-server-kind"
def server_timing, do: "server-timing"

Check warning on line 13 in lib/inngest/headers.ex

View check run for this annotation

Codecov / codecov/patch

lib/inngest/headers.ex#L11-L13

Added lines #L11 - L13 were not covered by tests

# Retries
def no_retry, do: "X-Inngest-No-Retry"
def retry_after, do: "Retry-After"
# retries
def no_retry, do: "x-inngest-no-retry"
def retry_after, do: "retry-after"

Check warning on line 17 in lib/inngest/headers.ex

View check run for this annotation

Codecov / codecov/patch

lib/inngest/headers.ex#L16-L17

Added lines #L16 - L17 were not covered by tests
end
94 changes: 94 additions & 0 deletions lib/inngest/response.ex
Original file line number Diff line number Diff line change
@@ -1,3 +1,97 @@
defmodule Inngest.NonRetriableError do
defexception message: "Not retrying error. Exiting."
end

defmodule Inngest.SdkResponse do
@moduledoc """
Represents an SDK response to the executor when ran
"""

defstruct [
:status,
:body,
:retry
]

@type t() :: %__MODULE__{
status: number(),
body: binary(),
retry: nil | :noretry | binary() | boolean()
}

alias Inngest.Headers

# NOTES:
# ********* RESPONSE ***********
# Each results has a specific meaning to it.
# status, data
# 206, generatorcode -> store result and continue execution
# 200, resp -> execution completed (including steps) of function
# 400, error -> non retriable error
# 500, error -> retriable error
def from_result({:ok, value}) do
case Jason.encode(value) do
{:ok, encoded} ->
%__MODULE__{
status: 200,
body: encoded
}

{:error, _error} ->
%__MODULE__{

Check warning on line 41 in lib/inngest/response.ex

View check run for this annotation

Codecov / codecov/patch

lib/inngest/response.ex#L41

Added line #L41 was not covered by tests
status: 500,
body: "Failed to encode result into JSON: #{value}"

Check warning on line 43 in lib/inngest/response.ex

View check run for this annotation

Codecov / codecov/patch

lib/inngest/response.ex#L43

Added line #L43 was not covered by tests
}
end
end

def from_result({:ok, opcodes, :continue}) do
%__MODULE__{
status: 206,
body: Jason.encode!(opcodes)
}
end

# No retry error response
def from_result({:error, error, :noretry}) do
encoded =

Check warning on line 57 in lib/inngest/response.ex

View check run for this annotation

Codecov / codecov/patch

lib/inngest/response.ex#L57

Added line #L57 was not covered by tests
case Jason.encode(error) do
{:ok, encoded} -> encoded
{:error, _} -> "Failed to encode error: #{error}"

Check warning on line 60 in lib/inngest/response.ex

View check run for this annotation

Codecov / codecov/patch

lib/inngest/response.ex#L59-L60

Added lines #L59 - L60 were not covered by tests
end

%__MODULE__{

Check warning on line 63 in lib/inngest/response.ex

View check run for this annotation

Codecov / codecov/patch

lib/inngest/response.ex#L63

Added line #L63 was not covered by tests
status: 400,
body: encoded,
retry: :noretry
}
end

def from_result({:error, error, _}) do
encoded =

Check warning on line 71 in lib/inngest/response.ex

View check run for this annotation

Codecov / codecov/patch

lib/inngest/response.ex#L71

Added line #L71 was not covered by tests
case Jason.encode(error) do
{:ok, encoded} -> encoded
{:error, _} -> "Failed to encode error: #{error}"

Check warning on line 74 in lib/inngest/response.ex

View check run for this annotation

Codecov / codecov/patch

lib/inngest/response.ex#L73-L74

Added lines #L73 - L74 were not covered by tests
end

%__MODULE__{

Check warning on line 77 in lib/inngest/response.ex

View check run for this annotation

Codecov / codecov/patch

lib/inngest/response.ex#L77

Added line #L77 was not covered by tests
status: 500,
body: encoded,
retry: true
}
end

@doc """
Set the retry header depending on response
"""
@spec maybe_retry_header(Plug.Conn.t(), t()) :: Plug.Conn.t()
def maybe_retry_header(conn, %{retry: :noretry} = _resp) do
Plug.Conn.put_resp_header(conn, Headers.no_retry(), "true")

Check warning on line 89 in lib/inngest/response.ex

View check run for this annotation

Codecov / codecov/patch

lib/inngest/response.ex#L89

Added line #L89 was not covered by tests
end

def maybe_retry_header(conn, %{retry: dur} = _resp) when is_binary(dur) do
Plug.Conn.put_resp_header(conn, Headers.retry_after(), dur)

Check warning on line 93 in lib/inngest/response.ex

View check run for this annotation

Codecov / codecov/patch

lib/inngest/response.ex#L93

Added line #L93 was not covered by tests
end

def maybe_retry_header(conn, _resp), do: conn
end
60 changes: 16 additions & 44 deletions lib/inngest/router/invoke.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ defmodule Inngest.Router.Invoke do

import Plug.Conn
import Inngest.Router.Helper
alias Inngest.{Config, Signature}
alias Inngest.{Config, Headers, Signature, SdkResponse}
alias Inngest.Function.GeneratorOpCode

@content_type "application/json"
Expand Down Expand Up @@ -58,73 +58,45 @@ defmodule Inngest.Router.Invoke do
step: Inngest.StepTool
}

{status, payload} =
resp =
case Config.is_dev() do
true ->
{status, resp} = invoke(func, ctx, input)

payload =
case Jason.encode(resp) do
{:ok, val} -> val
{:error, err} -> Jason.encode!(err.message)
end

{status, payload}
invoke(func, ctx, input)

false ->
with sig <- conn |> Plug.Conn.get_req_header("x-inngest-signature") |> List.first(),
true <- Signature.signing_key_valid?(sig, Config.signing_key(), body) do
{status, resp} = invoke(func, ctx, input)

payload =
case Jason.encode(resp) do
{:ok, val} -> val
{:error, err} -> Jason.encode!(err.message)
end

{status, payload}
invoke(func, ctx, input)

Check warning on line 69 in lib/inngest/router/invoke.ex

View check run for this annotation

Codecov / codecov/patch

lib/inngest/router/invoke.ex#L69

Added line #L69 was not covered by tests
else
_ -> {400, Jason.encode!(%{error: "unable to verify signature"})}
_ ->
SdkResponse.from_result({:error, "unable to verify signature", :noretry})

Check warning on line 72 in lib/inngest/router/invoke.ex

View check run for this annotation

Codecov / codecov/patch

lib/inngest/router/invoke.ex#L72

Added line #L72 was not covered by tests
end
end

conn
|> put_resp_content_type(@content_type)
|> put_req_header("x-inngest-sdk", "elixir:v1")
|> send_resp(status, payload)
|> put_resp_header(Headers.sdk_version(), Config.sdk_version())
|> SdkResponse.maybe_retry_header(resp)
|> send_resp(resp.status, resp.body)
|> halt()
end

# NOTES:
# ********* RESPONSE ***********
# Each results has a specific meaning to it.
# status, data
# 206, generatorcode -> store result and continue execution
# 200, resp -> execution completed (including steps) of function
# 400, error -> non retriable error
# 500, error -> retriable error
defp invoke(func, ctx, input) do
try do
case func.mod.exec(ctx, input) do
{:ok, val} ->
{200, val}

{:error, error} ->
{400, error}
end
func.mod.exec(ctx, input) |> SdkResponse.from_result()
rescue
non_retry in Inngest.NonRetriableError ->
{400, non_retry.message}
SdkResponse.from_result({:error, non_retry.message, :noretry})

Check warning on line 89 in lib/inngest/router/invoke.ex

View check run for this annotation

Codecov / codecov/patch

lib/inngest/router/invoke.ex#L88-L89

Added lines #L88 - L89 were not covered by tests

err ->
{400, err.message}
error ->
SdkResponse.from_result({:error, error.message, :retry})

Check warning on line 92 in lib/inngest/router/invoke.ex

View check run for this annotation

Codecov / codecov/patch

lib/inngest/router/invoke.ex#L91-L92

Added lines #L91 - L92 were not covered by tests
catch
# Finished step, report back to executor
%GeneratorOpCode{} = opcode ->
{206, [opcode]}
SdkResponse.from_result({:ok, [opcode], :continue})

_ ->
{400, "error"}
error ->
SdkResponse.from_result({:error, "unknown error: #{error}", []})

Check warning on line 99 in lib/inngest/router/invoke.ex

View check run for this annotation

Codecov / codecov/patch

lib/inngest/router/invoke.ex#L99

Added line #L99 was not covered by tests
end
end

Expand Down
8 changes: 6 additions & 2 deletions lib/inngest/trigger.ex
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,14 @@ end

defimpl Jason.Encoder, for: Inngest.Trigger do
def encode(%{cron: cron} = value, opts) when is_binary(cron) do
Jason.Encode.map(Map.take(value, [:cron]), opts)
value
|> Map.take([:cron])
|> Jason.Encode.map(opts)

Check warning on line 36 in lib/inngest/trigger.ex

View check run for this annotation

Codecov / codecov/patch

lib/inngest/trigger.ex#L36

Added line #L36 was not covered by tests
end

def encode(value, opts) do
Jason.Encode.map(Map.take(value, [:event, :expression]), opts)
value
|> Map.take([:event, :expression])
|> Jason.Encode.map(opts)
end
end

0 comments on commit 57c5b22

Please sign in to comment.