From 091f13d834596ab87e44cdad76ff338efb7d433a Mon Sep 17 00:00:00 2001 From: connor rigby Date: Mon, 11 Jun 2018 15:57:34 -0700 Subject: [PATCH] Add json wrapper --- config/config.exs | 3 ++- formatted_files | 2 ++ lib/farmbot/json/jason_parser.ex | 10 ++++++++++ lib/farmbot/json/json.ex | 25 +++++++++++++++++++++++++ lib/farmbot/json/parser.ex | 7 +++++++ lib/farmbot/jwt.ex | 21 ++++++++++++++++++--- lib/mix/tasks/farmbot/firmware/slack.ex | 1 - mix.exs | 1 + mix.lock.host | 3 ++- test/farmbot/jwt_test.exs | 8 ++++---- 10 files changed, 71 insertions(+), 10 deletions(-) create mode 100644 lib/farmbot/json/jason_parser.ex create mode 100644 lib/farmbot/json/json.ex create mode 100644 lib/farmbot/json/parser.ex diff --git a/config/config.exs b/config/config.exs index c5ffef4e..a2127cff 100644 --- a/config/config.exs +++ b/config/config.exs @@ -44,7 +44,8 @@ config :farmbot, :behaviour, authorization: Farmbot.Bootstrap.Authorization, firmware_handler: Farmbot.Firmware.StubHandler, http_adapter: Farmbot.HTTP.HTTPoisonAdapter, - gpio_handler: Farmbot.System.GPIO.StubHandler + gpio_handler: Farmbot.System.GPIO.StubHandler, + json_parser: Farmbot.JSON.JasonParser config :farmbot, :farmware, first_part_farmware_manifest_url: "https://raw.githubusercontent.com/FarmBot-Labs/farmware_manifests/master/manifest.json" diff --git a/formatted_files b/formatted_files index e11ba662..7e86103d 100644 --- a/formatted_files +++ b/formatted_files @@ -9,3 +9,5 @@ lib/farmbot/system/gpio/gpio.ex lib/farmbot/asset/farm_event.ex lib/farmbot/asset/regimen.ex lib/farmbot/jwt.ex +lib/farmbot/json/jason_parser.ex +lib/farmbot/json/parser.ex diff --git a/lib/farmbot/json/jason_parser.ex b/lib/farmbot/json/jason_parser.ex new file mode 100644 index 00000000..efdd1443 --- /dev/null +++ b/lib/farmbot/json/jason_parser.ex @@ -0,0 +1,10 @@ +defmodule Farmbot.JSON.JasonParser do + @moduledoc "Parser handler for Jason" + @behaviour Farmbot.JSON.Parser + + def decode(data), do: Jason.decode(data) + def encode(data), do: Jason.encode(data) + + require Protocol + Protocol.derive(Jason.Encoder, Farmbot.Jwt) +end diff --git a/lib/farmbot/json/json.ex b/lib/farmbot/json/json.ex new file mode 100644 index 00000000..adceda4e --- /dev/null +++ b/lib/farmbot/json/json.ex @@ -0,0 +1,25 @@ +defmodule Farmbot.JSON do + @moduledoc "Wraps a dependency for easy upgrade and no vendor lock." + + @parser Application.get_env(:farmbot, :behaviour)[:json_parser] + @parser || Mix.raise("Unconfigured JSON Parser.") + @spec decode(iodata) :: {:ok, term} | {:error, term} + def decode(iodata), do: @parser.decode(iodata) + + @spec encode(term) :: {:ok, term} | {:error, term} + def encode(data), do: @parser.encode(data) + + def decode!(iodata) do + case decode(iodata) do + {:ok, results} -> results + {:error, reason} -> raise(reason) + end + end + + def encode!(data) do + case encode(data) do + {:ok, results} -> results + {:error, reason} -> raise(reason) + end + end +end diff --git a/lib/farmbot/json/parser.ex b/lib/farmbot/json/parser.ex new file mode 100644 index 00000000..da0a76c0 --- /dev/null +++ b/lib/farmbot/json/parser.ex @@ -0,0 +1,7 @@ +defmodule Farmbot.JSON.Parser do + @moduledoc """ + Callback module for wrapping a json dependency. + """ + @callback decode(iodata) :: {:ok, term} | {:error, term} + @callback encode(term) :: {:ok, iodata} | {:error, term} +end diff --git a/lib/farmbot/jwt.ex b/lib/farmbot/jwt.ex index 8a7e76ef..40d1f502 100644 --- a/lib/farmbot/jwt.ex +++ b/lib/farmbot/jwt.ex @@ -29,11 +29,12 @@ defmodule Farmbot.Jwt do body = tkn |> String.split(".") |> Enum.at(1) with {:ok, json} <- Base.decode64(body, padding: false), - {:ok, jwt} <- Poison.decode(json, as: %__MODULE__{}) do + {:ok, data} <- Farmbot.JSON.decode(json), + {:ok, jwt} <- decode_map(data) do {:ok, jwt} else - :error -> {:error, :base64_decode_fail} - {:error, :invalid, _} -> {:error, :json_decode_error} + :error -> {:error, "base64_decode_fail"} + {:error, _resson} -> {:error, "json_decode_error"} end end @@ -45,4 +46,18 @@ defmodule Farmbot.Jwt do {:error, reason} -> raise(reason) end end + + defp decode_map(%{} = map) do + {:ok, + struct( + Farmbot.Jwt, + bot: map["bot"], + exp: map["exp"], + iss: map["iss"], + mqtt: map["mqtt"], + os_update_server: map["os_update_server"], + vhost: map["vhost"], + interim_email: map["interim_email"] + )} + end end diff --git a/lib/mix/tasks/farmbot/firmware/slack.ex b/lib/mix/tasks/farmbot/firmware/slack.ex index 6252d2a3..b2c73fd4 100644 --- a/lib/mix/tasks/farmbot/firmware/slack.ex +++ b/lib/mix/tasks/farmbot/firmware/slack.ex @@ -5,7 +5,6 @@ defmodule Mix.Tasks.Farmbot.Firmware.Slack do use Mix.Task import Mix.Tasks.Farmbot.Env - @dialyzer {[:no_return], [run: 1]} def run(opts) do token = slack_token() diff --git a/mix.exs b/mix.exs index cadc6d4d..ab811e62 100644 --- a/mix.exs +++ b/mix.exs @@ -102,6 +102,7 @@ defmodule Farmbot.Mixfile do {:gen_stage, "~> 0.12"}, {:phoenix_html, "~> 2.10.5"}, {:poison, "~> 3.1.0"}, + {:jason, "~> 1.0"}, {:httpoison, "~> 1.1"}, {:jsx, "~> 2.8.0"}, {:timex, "~> 3.3"}, diff --git a/mix.lock.host b/mix.lock.host index 5a2e1c7a..900904e7 100644 --- a/mix.lock.host +++ b/mix.lock.host @@ -14,7 +14,7 @@ "credo": {:hex, :credo, "0.9.1", "f021affa11b32a94dc2e807a6472ce0914289c9132f99644a97fc84432b202a1", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:poison, ">= 0.0.0", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm"}, "db_connection": {:hex, :db_connection, "1.1.3", "89b30ca1ef0a3b469b1c779579590688561d586694a3ce8792985d4d7e575a61", [:mix], [{:connection, "~> 1.0.2", [hex: :connection, repo: "hexpm", optional: false]}, {:poolboy, "~> 1.5", [hex: :poolboy, repo: "hexpm", optional: true]}, {:sbroker, "~> 1.0", [hex: :sbroker, repo: "hexpm", optional: true]}], "hexpm"}, "decimal": {:hex, :decimal, "1.4.1", "ad9e501edf7322f122f7fc151cce7c2a0c9ada96f2b0155b8a09a795c2029770", [:mix], [], "hexpm"}, - "dialyxir": {:git, "https://github.com/jeremyjh/dialyxir.git", "292191fdb71a10827e7088d01d9ba16461e40369", []}, + "dialyxir": {:git, "https://github.com/jeremyjh/dialyxir.git", "fa821b418b5e7c54ca7a2f184d55b5310693457c", []}, "distillery": {:hex, :distillery, "1.5.2", "eec18b2d37b55b0bcb670cf2bcf64228ed38ce8b046bb30a9b636a6f5a4c0080", [:mix], [], "hexpm"}, "earmark": {:hex, :earmark, "1.2.4", "99b637c62a4d65a20a9fb674b8cffb8baa771c04605a80c911c4418c69b75439", [:mix], [], "hexpm"}, "ecto": {:hex, :ecto, "2.2.8", "a4463c0928b970f2cee722cd29aaac154e866a15882c5737e0038bbfcf03ec2c", [:mix], [{:db_connection, "~> 1.1", [hex: :db_connection, repo: "hexpm", optional: true]}, {:decimal, "~> 1.2", [hex: :decimal, repo: "hexpm", optional: false]}, {:mariaex, "~> 0.8.0", [hex: :mariaex, repo: "hexpm", optional: true]}, {:poison, "~> 2.2 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: true]}, {:poolboy, "~> 1.5", [hex: :poolboy, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.13.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:sbroker, "~> 1.0", [hex: :sbroker, repo: "hexpm", optional: true]}], "hexpm"}, @@ -32,6 +32,7 @@ "httpoison": {:hex, :httpoison, "1.1.1", "96ed7ab79f78a31081bb523eefec205fd2900a02cda6dbc2300e7a1226219566", [:mix], [{:hackney, "~> 1.8", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"}, "idna": {:hex, :idna, "5.1.1", "cbc3b2fa1645113267cc59c760bafa64b2ea0334635ef06dbac8801e42f7279c", [:rebar3], [{:unicode_util_compat, "0.3.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm"}, "inch_ex": {:hex, :inch_ex, "0.5.6", "418357418a553baa6d04eccd1b44171936817db61f4c0840112b420b8e378e67", [:mix], [{:poison, "~> 1.5 or ~> 2.0 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm"}, + "jason": {:hex, :jason, "1.0.0", "0f7cfa9bdb23fed721ec05419bcee2b2c21a77e926bce0deda029b5adc716fe2", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm"}, "joken": {:hex, :joken, "1.5.0", "42a0953e80bd933fc98a0874e156771f78bf0e92abe6c3a9c22feb6da28efb0b", [:mix], [{:jose, "~> 1.8", [hex: :jose, repo: "hexpm", optional: false]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}, {:poison, "~> 1.5 or ~> 2.0 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: true]}], "hexpm"}, "jose": {:hex, :jose, "1.8.4", "7946d1e5c03a76ac9ef42a6e6a20001d35987afd68c2107bcd8f01a84e75aa73", [:mix, :rebar3], [{:base64url, "~> 0.0.1", [hex: :base64url, repo: "hexpm", optional: false]}], "hexpm"}, "jsx": {:hex, :jsx, "2.8.2", "7acc7d785b5abe8a6e9adbde926a24e481f29956dd8b4df49e3e4e7bcc92a018", [:mix, :rebar3], [], "hexpm"}, diff --git a/test/farmbot/jwt_test.exs b/test/farmbot/jwt_test.exs index 76396416..e6f3674f 100644 --- a/test/farmbot/jwt_test.exs +++ b/test/farmbot/jwt_test.exs @@ -30,7 +30,7 @@ defmodule Farmbot.JwtTest do tkn = [head, "not_a_valid_token", foot] |> Enum.join(".") r = Jwt.decode(tkn) refute match?({:ok, _}, r) - assert r == :error + assert r == {:error, "base64_decode_fail"} end test "Gives Poison Error when it can't be decoded as json", %{token: tkn} do @@ -39,14 +39,14 @@ defmodule Farmbot.JwtTest do tkn = [head, not_token, foot] |> Enum.join(".") r = Jwt.decode(tkn) refute match?({:ok, _}, r) - assert r == {:error, {:invalid, "h", 0}} + assert r == {:error, "json_decode_error"} end test "raises on bad token because base64", %{token: tkn} do [head, _body, foot] = String.split(tkn, ".") tkn = [head, "not_a_valid_token", foot] |> Enum.join(".") - assert_raise RuntimeError, "Failed to base64 decode.", fn -> + assert_raise RuntimeError, "base64_decode_fail", fn -> Jwt.decode!(tkn) end end @@ -56,7 +56,7 @@ defmodule Farmbot.JwtTest do not_token = Base.encode64("hello world", padding: false) tkn = [head, not_token, foot] |> Enum.join(".") - assert_raise RuntimeError, "Failed to json decode.", fn -> + assert_raise RuntimeError, "json_decode_error", fn -> Jwt.decode!(tkn) end end