Spaces:
Sleeping
Sleeping
defmodule Srh.Http.BaseRouter do | |
use Plug.Router | |
alias Srh.Http.RequestValidator | |
alias Srh.Http.CommandHandler | |
alias Srh.Http.ResultEncoder | |
plug(:match) | |
plug(Srh.Http.ContentTypeCheckPlug) | |
plug(Plug.Parsers, parsers: [:json], pass: ["application/json"], json_decoder: Jason) | |
plug(:dispatch) | |
get "/" do | |
handle_response({:ok, "Welcome to Serverless Redis HTTP!"}, conn) | |
end | |
post "/" do | |
do_command_request(conn, &CommandHandler.handle_command(&1, &2)) | |
end | |
post "/pipeline" do | |
do_command_request(conn, &CommandHandler.handle_command_array(&1, &2)) | |
end | |
post "/multi-exec" do | |
do_command_request(conn, &CommandHandler.handle_command_transaction_array(&1, &2)) | |
end | |
match _ do | |
handle_response( | |
{:not_found, "SRH: Endpoint not found. SRH might not support this feature yet."}, | |
conn | |
) | |
end | |
defp do_command_request(conn, success_lambda) do | |
encoding_enabled = handle_extract_encoding?(conn) | |
conn | |
|> handle_extract_auth(&success_lambda.(conn, &1)) | |
|> handle_encoding_step(encoding_enabled) | |
|> handle_response(conn) | |
end | |
defp handle_extract_auth(conn, success_lambda) do | |
case conn | |
|> get_req_header("authorization") | |
|> RequestValidator.validate_bearer_header() do | |
{:ok, token} -> | |
success_lambda.(token) | |
{:error, _} -> | |
{:malformed_data, "Missing/Invalid authorization header"} | |
end | |
end | |
defp handle_extract_encoding?(conn) do | |
case conn | |
|> get_req_header("upstash-encoding") | |
|> RequestValidator.validate_encoding_header() do | |
{:ok, _encoding_enabled} -> true | |
# it's not required to be present | |
{:error, _} -> false | |
end | |
end | |
defp handle_encoding_step(response, encoding_enabled) do | |
case encoding_enabled do | |
true -> | |
# We need to use the encoder to | |
ResultEncoder.encode_response(response) | |
false -> | |
response | |
end | |
end | |
defp handle_response(response, conn) do | |
# Errors are strings, and data just means the content is directly encoded with Jason.encode! | |
# {404, {:error, "Message"}} | |
# {200, {:data, ""}} | |
{code, resp_data} = | |
case response do | |
{:ok, data} -> | |
{200, {:data, data}} | |
{:not_found, message} -> | |
{404, {:error, message}} | |
{:malformed_data, message} -> | |
{400, {:error, message}} | |
{:redis_error, data} -> | |
{400, {:data, data}} | |
{:not_authorized, message} -> | |
{401, {:error, message}} | |
{:connection_error, message} -> | |
{500, {:error, message}} | |
_ -> | |
{500, {:error, "SRH: An error occurred internally"}} | |
end | |
conn | |
|> put_resp_header("content-type", "application/json") | |
|> send_resp(code, create_response_body(resp_data)) | |
end | |
# :data just directly encodes | |
defp create_response_body({:data, data}), do: Jason.encode!(data) | |
# :error wraps the message in an error object | |
defp create_response_body({:error, error}), do: Jason.encode!(%{error: error}) | |
end | |