diff --git a/lib/cache/redis/global.ex b/lib/cache/redis/global.ex index b42fef2..6c0faee 100644 --- a/lib/cache/redis/global.ex +++ b/lib/cache/redis/global.ex @@ -91,9 +91,22 @@ defmodule Cache.Redis.Global do defp handle_response({:ok, "OK"}), do: :ok defp handle_response({:ok, _} = res), do: res + defp handle_response({:error, %Redix.ConnectionError{reason: reason}}) do {:error, ErrorMessage.service_unavailable("redis connection errored because: #{reason}")} end - defp handle_response({:error, _} = res), do: res + defp handle_response({:error, %Redix.Error{message: "ERR Path" <> _rest = message}}) do + {:error, ErrorMessage.not_found(message)} + end + + defp handle_response( + {:error, %Redix.Error{message: "ERR new objects must be created at the root" = message}} + ) do + {:error, ErrorMessage.bad_request(message)} + end + + defp handle_response({:error, error}) do + {:error, ErrorMessage.internal_server_error("Internal server error", %{error: inspect(error)})} + end end diff --git a/lib/cache/sandbox.ex b/lib/cache/sandbox.ex index 8430ae8..2364c69 100644 --- a/lib/cache/sandbox.ex +++ b/lib/cache/sandbox.ex @@ -128,16 +128,29 @@ defmodule Cache.Sandbox do def json_get(cache_name, key, path, _opts) do Agent.get(cache_name, fn state -> - {:ok, get_in(state, [key, String.split(path, ".")])} + case get_in(state, [key | String.split(path, ".")]) do + nil -> {:error, ErrorMessage.not_found("ERR Path '$.#{path}' does not exist")} + value -> {:ok, value} + end end) end def json_set(cache_name, key, path, value, _opts) do Agent.update(cache_name, fn state -> - Map.put(state, key, put_in(state, [String.split(path, ".")], value)) + put_in(state, add_defaults([key | String.split(path, ".")]), value) end) end + defp add_defaults([key | keys]) do + [Access.key(key, key_default(key)) | add_defaults(keys)] + end + + defp add_defaults(keys), do: keys + + defp key_default(key) do + if Regex.match?(~r/\d+/, key), do: [], else: %{} + end + def json_incr(cache_name, key, path, incr \\ 1, _opts) do Agent.update(cache_name, fn state -> Map.update(state, key, nil, fn value -> diff --git a/test/cache/redis_json_test.exs b/test/cache/redis_json_test.exs index f71c61f..f13ce7a 100644 --- a/test/cache/redis_json_test.exs +++ b/test/cache/redis_json_test.exs @@ -9,7 +9,7 @@ defmodule Cache.RedisJSONTest do some_integer: 1234, some_array: [1, 2, 3, 4], some_empty_array: [], - some_map: %{one: 1, two: 2, three: 3, four: 4}, + some_map: %{one: 1, two: 2, three: 3, four: 4} } defmodule RedisCache do @@ -44,6 +44,15 @@ defmodule Cache.RedisJSONTest do assert {:ok, @test_value.some_map.one} === RedisCache.json_get(key, [:some_map, :one]) assert {:ok, Enum.at(@test_value.some_array, 0)} === RedisCache.json_get(key, [:some_array, 0]) end + + test "returns :error tuple if path not found", %{key: key} do + assert {:error, + %ErrorMessage{ + message: "ERR Path '$.non_existing.path' does not exist", + code: :not_found, + details: nil + }} === RedisCache.json_get(key, [:non_existing, :path]) + end end describe "&json_set/2" do @@ -58,6 +67,33 @@ defmodule Cache.RedisJSONTest do assert :ok = RedisCache.json_set(key, [:some_map, :one], 4) assert {:ok, 4} = RedisCache.json_get(key, [:some_map, :one]) end + + test "returns error tuple if key does not exist" do + assert {:error, + %ErrorMessage{ + message: "ERR new objects must be created at the root", + code: :bad_request, + details: nil + }} === RedisCache.json_set("non_existing", [:some_map, :one], 4) + end + + test "returns :ok and nil if key exists but not the path", %{key: key} do + assert {:ok, nil} = RedisCache.json_set(key, [:some_other_map, :two], 4) + + assert {:ok, + %{ + "some_integer" => 1234, + "some_array" => [1, 2, 3, 4], + "some_empty_array" => [], + "some_map" => %{"one" => 1, "two" => 2, "three" => 3, "four" => 4} + }} === RedisCache.json_get(key) + end + + test "ignores'.' as path", %{key: key} do + assert :ok = RedisCache.json_set(key, ["."], "some value") + assert {:ok, "some value"} === RedisCache.json_get(key) + assert {:ok, "some value"} === RedisCache.json_get(key, ["."]) + end end describe "&json_delete/2" do diff --git a/test/cache_sandbox_test.exs b/test/cache_sandbox_test.exs index 9d71916..5816da6 100644 --- a/test/cache_sandbox_test.exs +++ b/test/cache_sandbox_test.exs @@ -11,6 +11,7 @@ defmodule CacheSandboxTest do @cache_key "SomeKey" @cache_value 1234 + @cache_path "a.b" setup do Cache.SandboxRegistry.start(TestCache) @@ -27,5 +28,16 @@ defmodule CacheSandboxTest do test "works to seperate caches between tests" do assert {:ok, nil} = TestCache.get(@cache_key) end + + test "works with json commands" do + assert {:error, + %ErrorMessage{ + message: "ERR Path '$.c.d' does not exist", + code: :not_found, + details: nil + }} === TestCache.json_get(@cache_key, "c.d") + assert :ok = TestCache.json_set(@cache_key, @cache_path, @cache_value) + assert {:ok, @cache_value} = TestCache.json_get(@cache_key, @cache_path) + end end end