File size: 3,061 Bytes
2e92879
 
 
 
a8d7371
2e92879
b2ff4b5
21d5884
b2ff4b5
 
2e92879
 
 
 
 
 
a8d7371
0b53aa4
 
 
a8d7371
0b53aa4
 
abcde98
a8d7371
abcde98
 
0b53aa4
f085bb2
 
 
 
0b53aa4
 
a8d7371
8f1b0f2
a8d7371
 
 
 
 
 
 
0b53aa4
2e92879
 
b2ff4b5
2e92879
0b53aa4
b2ff4b5
2e92879
 
 
 
 
8f1b0f2
a8d7371
 
 
 
bb41a4c
 
a8d7371
 
 
 
 
 
 
 
bb41a4c
 
 
a8d7371
 
 
2e92879
0f49779
 
 
 
 
2e92879
b2ff4b5
0f49779
b2ff4b5
 
0f49779
b2ff4b5
 
0f49779
b2ff4b5
abcde98
0f49779
abcde98
b2ff4b5
0f49779
b2ff4b5
bb41a4c
0f49779
b2ff4b5
76486d3
ba82689
2e92879
 
0f49779
 
 
 
b2ff4b5
5ce1d4c
 
0f49779
5ce1d4c
 
2e92879
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
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