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