Test auto_sync ignore case
* Test case: auto_sync is disabled. * Test case: auto_sync of Device assets * Test case: auto_sync of FbosConfig * Test case: auto_sync of FbosConfig assets * Test case: auto_sync of FarmwareInstallation assets Test case: `cache_sync/3`pull/974/head
parent
6f49345477
commit
c3819359b0
|
@ -2,21 +2,68 @@ defmodule FarmbotCore.Asset.Command do
|
|||
@moduledoc """
|
||||
A collection of functions that _write_ to the DB
|
||||
"""
|
||||
require Logger
|
||||
|
||||
alias FarmbotCore.{
|
||||
Asset,
|
||||
Asset.Repo,
|
||||
Asset.Device
|
||||
}
|
||||
alias FarmbotCore.{Asset, Asset.Repo, Asset.Device}
|
||||
alias FarmbotExt.API.{EagerLoader}
|
||||
@type kind :: String.t()
|
||||
@type params :: map()
|
||||
@type id :: float()
|
||||
@type return_type :: :ok
|
||||
|
||||
@type kind :: Device | FbosConfig | FwConfig
|
||||
@type return_type ::
|
||||
Device.t() | FbosConfig.t() | FwConfig.t()
|
||||
@callback update(kind, params, id) :: return_type
|
||||
def update("Device", params) do
|
||||
Asset.device() |> Device.changeset(params) |> Repo.update!()
|
||||
end
|
||||
|
||||
@callback update(kind, params :: map()) :: return_type
|
||||
def update(Device, params) do
|
||||
Asset.device()
|
||||
|> Device.changeset(params)
|
||||
|> Repo.update!()
|
||||
def update("FbosConfig", params, _),
|
||||
do: Asset.update_fbos_config!(params)
|
||||
|
||||
def update("FirmwareConfig", params, _),
|
||||
do: Asset.update_firmware_config!(params)
|
||||
|
||||
def update("FarmwareEnv", params, id),
|
||||
do: Asset.upsert_farmware_env_by_id(id, params)
|
||||
|
||||
def update("FarmwareInstallation", id, params),
|
||||
do: Asset.upsert_farmware_env_by_id(id, params)
|
||||
|
||||
# Deletion use case:
|
||||
def update(asset_kind, nil, id) do
|
||||
old = Repo.get_by(as_module(asset_kind), id: id)
|
||||
old && Repo.delete!(old)
|
||||
:ok
|
||||
end
|
||||
|
||||
# Catch-all use case:
|
||||
def update(asset_kind, params, id) do
|
||||
Logger.info("autosyncing: #{asset_kind} #{id} #{inspect(params)}")
|
||||
|
||||
case Repo.get_by(as_module(asset_kind), id: id) do
|
||||
nil ->
|
||||
struct(asset_kind)
|
||||
|> asset_kind.changeset(params)
|
||||
|> Repo.insert!()
|
||||
|
||||
asset ->
|
||||
asset_kind.changeset(asset, params)
|
||||
|> Repo.update!()
|
||||
end
|
||||
|
||||
:ok
|
||||
end
|
||||
|
||||
@callback cached_update(kind, params, id) :: return_type
|
||||
def cached_update(asset_kind, params, id) do
|
||||
mod = as_module(asset_kind)
|
||||
asset = Repo.get_by(mod, id: id) || struct(asset_kind)
|
||||
changeset = mod.changeset(asset, params)
|
||||
:ok = EagerLoader.cache(changeset)
|
||||
:ok = BotState.set_sync_status("sync_now")
|
||||
end
|
||||
|
||||
# Convert string `"Device"` to module `Asset.Device`
|
||||
defp as_module(asset_kind) do
|
||||
Module.concat([Asset, asset_kind])
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
defmodule FarmbotCore.Asset.Query do
|
||||
alias FarmbotCore.Asset
|
||||
@callback auto_sync?() :: boolean()
|
||||
def auto_sync?() do
|
||||
Asset.fbos_config().auto_sync
|
||||
|
|
|
@ -10,21 +10,12 @@ defmodule FarmbotExt.AMQP.AutoSyncChannel do
|
|||
|
||||
alias FarmbotCore.BotState
|
||||
alias FarmbotExt.AMQP.ConnectionWorker
|
||||
alias FarmbotExt.API.{Preloader, EagerLoader}
|
||||
alias FarmbotExt.API.{Preloader}
|
||||
|
||||
require Logger
|
||||
require FarmbotCore.Logger
|
||||
|
||||
alias FarmbotCore.{
|
||||
Asset,
|
||||
Asset.Device,
|
||||
Asset.FarmwareEnv,
|
||||
Asset.FarmwareInstallation,
|
||||
Asset.FbosConfig,
|
||||
Asset.FirmwareConfig,
|
||||
Asset.Repo,
|
||||
JSON
|
||||
}
|
||||
alias FarmbotCore.{Asset, JSON}
|
||||
|
||||
@known_kinds ~w(
|
||||
Device
|
||||
|
@ -43,6 +34,13 @@ defmodule FarmbotExt.AMQP.AutoSyncChannel do
|
|||
Tool
|
||||
)
|
||||
|
||||
@cache_kinds ~w(
|
||||
Device
|
||||
FbosConfig
|
||||
FirmwareConfig
|
||||
FarmwareEnv
|
||||
FarmwareInstallation
|
||||
)
|
||||
defstruct [:conn, :chan, :jwt, :preloaded]
|
||||
alias __MODULE__, as: State
|
||||
|
||||
|
@ -105,7 +103,6 @@ defmodule FarmbotExt.AMQP.AutoSyncChannel do
|
|||
|
||||
case String.split(key, ".") do
|
||||
["bot", ^device, "sync", asset_kind, id_str] when asset_kind in @known_kinds ->
|
||||
asset_kind = Module.concat([Asset, asset_kind])
|
||||
id = data["id"] || String.to_integer(id_str)
|
||||
handle_asset(asset_kind, id, body)
|
||||
|
||||
|
@ -117,6 +114,63 @@ defmodule FarmbotExt.AMQP.AutoSyncChannel do
|
|||
{:noreply, state}
|
||||
end
|
||||
|
||||
def handle_asset(asset_kind, id, params) do
|
||||
auto_sync? = query().auto_sync?()
|
||||
|
||||
cond do
|
||||
# TODO(Connor) no way to cache a deletion yet
|
||||
is_nil(params) && !auto_sync? ->
|
||||
:ok
|
||||
|
||||
asset_kind == Device ->
|
||||
command().update(asset_kind, params)
|
||||
:ok
|
||||
|
||||
asset_kind == FbosConfig ->
|
||||
command().update(asset_kind, params)
|
||||
:ok
|
||||
|
||||
asset_kind == FirmwareConfig ->
|
||||
Asset.update_firmware_config!(params)
|
||||
:ok
|
||||
|
||||
# TODO(Connor) make this use `sync_group0()`
|
||||
asset_kind == FarmwareEnv ->
|
||||
Asset.upsert_farmware_env_by_id(id, params)
|
||||
:ok
|
||||
|
||||
# TODO(Connor) make this use `sync_group0()`
|
||||
asset_kind == FarmwareInstallation ->
|
||||
Asset.upsert_farmware_manifest_by_id(id, params)
|
||||
:ok
|
||||
|
||||
is_nil(params) && auto_sync? ->
|
||||
old = Repo.get_by(asset_kind, id: id)
|
||||
old && Repo.delete!(old)
|
||||
:ok
|
||||
|
||||
auto_sync? ->
|
||||
case Repo.get_by(asset_kind, id: id) do
|
||||
nil ->
|
||||
struct(asset_kind)
|
||||
|> asset_kind.changeset(params)
|
||||
|> Repo.insert!()
|
||||
|
||||
asset ->
|
||||
asset_kind.changeset(asset, params)
|
||||
|> Repo.update!()
|
||||
end
|
||||
|
||||
:ok
|
||||
|
||||
true ->
|
||||
asset = Repo.get_by(asset_kind, id: id) || struct(asset_kind)
|
||||
changeset = asset_kind.changeset(asset, params)
|
||||
:ok = EagerLoader.cache(changeset)
|
||||
:ok = BotState.set_sync_status("sync_now")
|
||||
end
|
||||
end
|
||||
|
||||
def handle_call(:network_status, _, state) do
|
||||
{
|
||||
:reply,
|
||||
|
@ -130,85 +184,25 @@ defmodule FarmbotExt.AMQP.AutoSyncChannel do
|
|||
end
|
||||
|
||||
def handle_asset(asset_kind, id, params) do
|
||||
auto_sync? = query().auto_sync?()
|
||||
|
||||
if auto_sync? do
|
||||
if query().auto_sync?() do
|
||||
:ok = BotState.set_sync_status("syncing")
|
||||
auto_sync(asset_kind, id, params)
|
||||
command().update(asset_kind, params, id)
|
||||
:ok = BotState.set_sync_status("synced")
|
||||
else
|
||||
cache_sync(asset_kind, id, params)
|
||||
cache_sync(asset_kind, params, id)
|
||||
end
|
||||
end
|
||||
|
||||
def auto_sync(asset_kind = Device, _id, params) do
|
||||
# TODO(Connor) maybe check this value?
|
||||
_ = command().update(asset_kind, params)
|
||||
:ok
|
||||
end
|
||||
|
||||
def auto_sync(FbosConfig, _id, params) do
|
||||
_ = Asset.update_fbos_config!(params)
|
||||
:ok
|
||||
end
|
||||
|
||||
def auto_sync(FirmwareConfig, _id, params) do
|
||||
_ = Asset.update_firmware_config!(params)
|
||||
:ok
|
||||
end
|
||||
|
||||
def auto_sync(FarmwareEnv, id, params) do
|
||||
_ = Asset.upsert_farmware_env_by_id(id, params)
|
||||
:ok
|
||||
end
|
||||
|
||||
def auto_sync(FarmwareInstallation, id, params) do
|
||||
_ = Asset.upsert_farmware_env_by_id(id, params)
|
||||
:ok
|
||||
end
|
||||
|
||||
def auto_sync(asset_kind, id, nil) do
|
||||
old = Repo.get_by(asset_kind, id: id)
|
||||
old && Repo.delete!(old)
|
||||
:ok
|
||||
end
|
||||
|
||||
def auto_sync(asset_kind, id, params) do
|
||||
Logger.info("autosyncing: #{asset_kind} #{id} #{inspect(params)}")
|
||||
|
||||
case Repo.get_by(asset_kind, id: id) do
|
||||
nil ->
|
||||
struct(asset_kind)
|
||||
|> asset_kind.changeset(params)
|
||||
|> Repo.insert!()
|
||||
|
||||
asset ->
|
||||
asset_kind.changeset(asset, params)
|
||||
|> Repo.update!()
|
||||
end
|
||||
|
||||
:ok
|
||||
end
|
||||
|
||||
def cache_sync(kind, id, params)
|
||||
when kind in [
|
||||
Device,
|
||||
FbosConfig,
|
||||
FirmwareConfig,
|
||||
FarmwareEnv,
|
||||
FarmwareInstallation
|
||||
] do
|
||||
def cache_sync(kind, params, id) when kind in @cache_kinds do
|
||||
:ok = BotState.set_sync_status("syncing")
|
||||
:ok = auto_sync(kind, id, params)
|
||||
:ok = command().update(kind, params, id)
|
||||
:ok = BotState.set_sync_status("synced")
|
||||
end
|
||||
|
||||
def cache_sync(asset_kind, id, params) do
|
||||
Logger.info("Autcaching sync #{asset_kind} #{id} #{inspect(params)}")
|
||||
asset = Repo.get_by(asset_kind, id: id) || struct(asset_kind)
|
||||
changeset = asset_kind.changeset(asset, params)
|
||||
:ok = EagerLoader.cache(changeset)
|
||||
:ok = BotState.set_sync_status("sync_now")
|
||||
def cache_sync(asset_kind, params, id) do
|
||||
Logger.info("Autocaching sync #{asset_kind} #{id} #{inspect(params)}")
|
||||
|
||||
command().cached_update(asset_kind, params, id)
|
||||
end
|
||||
|
||||
defp compute_reply_from_amqp_state(state, %{conn: conn, chan: chan}) do
|
||||
|
|
|
@ -42,7 +42,7 @@ defmodule FarmbotExt.AMQP.AutoSyncChannelTest do
|
|||
|
||||
stub(MockConnectionWorker, :close_channel, fn _ ->
|
||||
send(test_pid, :close_channel_called)
|
||||
nil
|
||||
:ok
|
||||
end)
|
||||
|
||||
stub(MockConnectionWorker, :rpc_reply, fn chan, jwt_dot_bot, label ->
|
||||
|
@ -94,7 +94,7 @@ defmodule FarmbotExt.AMQP.AutoSyncChannelTest do
|
|||
assert real_conn == fake_con
|
||||
assert is_preloaded
|
||||
send(pid, {:basic_cancel, "--NOT USED--"})
|
||||
assert_receive :close_channel_called, 75
|
||||
assert_receive :close_channel_called, 150
|
||||
end
|
||||
|
||||
test "catch-all clause for inbound AMQP messages" do
|
||||
|
@ -135,12 +135,101 @@ defmodule FarmbotExt.AMQP.AutoSyncChannelTest do
|
|||
key = "bot.device_15.sync.Device.999"
|
||||
stub(MockQuery, :auto_sync?, fn -> true end)
|
||||
|
||||
stub(MockCommand, :update, fn x, y ->
|
||||
send(test_pid, {:update_called, x, y})
|
||||
nil
|
||||
stub(MockCommand, :update, fn x, y, z ->
|
||||
send(test_pid, {:update_called, x, y, z})
|
||||
:ok
|
||||
end)
|
||||
|
||||
send(pid, {:basic_deliver, payload, %{routing_key: key}})
|
||||
assert_receive {:update_called, FarmbotCore.Asset.Device, %{}}, 10
|
||||
assert_receive {:update_called, "Device", %{}, 999}, 10
|
||||
end
|
||||
|
||||
def simple_asset_test_singleton(module_name) do
|
||||
%{pid: pid} = under_normal_conditions()
|
||||
test_pid = self()
|
||||
payload = '{"args":{"label":"foo"},"body":{"foo": "bar"}}'
|
||||
key = "bot.device_15.sync.#{module_name}.999"
|
||||
|
||||
stub(MockQuery, :auto_sync?, fn -> true end)
|
||||
|
||||
stub(MockCommand, :update, fn x, y, z ->
|
||||
send(test_pid, {:update_called, x, y, z})
|
||||
:ok
|
||||
end)
|
||||
|
||||
stub(MockCommand, :update, fn x, y, z ->
|
||||
send(test_pid, {:update_called, x, y, z})
|
||||
:ok
|
||||
end)
|
||||
|
||||
send(pid, {:basic_deliver, payload, %{routing_key: key}})
|
||||
|
||||
assert_receive {:update_called, module_name, %{"foo" => "bar"}, 999}, 10
|
||||
end
|
||||
|
||||
def simple_asset_test_plural(module_name) do
|
||||
%{pid: pid} = under_normal_conditions()
|
||||
test_pid = self()
|
||||
payload = '{"args":{"label":"foo"},"body":{"foo": "bar"}}'
|
||||
key = "bot.device_15.sync.#{module_name}.999"
|
||||
|
||||
stub(MockQuery, :auto_sync?, fn -> true end)
|
||||
|
||||
stub(MockCommand, :update, fn x, y, z ->
|
||||
send(test_pid, {:update_called, x, y, z})
|
||||
:ok
|
||||
end)
|
||||
|
||||
send(pid, {:basic_deliver, payload, %{routing_key: key}})
|
||||
|
||||
assert_receive {:update_called, module_name, %{"foo" => "bar"}, 999}, 10
|
||||
end
|
||||
|
||||
test "handles FbosConfig", do: simple_asset_test_singleton("FbosConfig")
|
||||
test "handles FirmwareConfig", do: simple_asset_test_singleton("FirmwareConfig")
|
||||
test "handles FarmwareEnv", do: simple_asset_test_plural("FarmwareEnv")
|
||||
test "handles FarmwareInstallation", do: simple_asset_test_plural("FarmwareInstallation")
|
||||
|
||||
test "handles auto_sync of 'cache_assets' when auto_sync is false" do
|
||||
test_pid = self()
|
||||
%{pid: pid} = under_normal_conditions()
|
||||
|
||||
key = "bot.device_15.sync.FbosConfig.999"
|
||||
payload = '{"args":{"label":"foo"},"body":{"foo": "bar"}}'
|
||||
|
||||
stub(MockQuery, :auto_sync?, fn ->
|
||||
send(test_pid, :called_auto_sync?)
|
||||
false
|
||||
end)
|
||||
|
||||
stub(MockCommand, :update, fn x, y, z ->
|
||||
send(test_pid, {:update_called, x, y, z})
|
||||
:ok
|
||||
end)
|
||||
|
||||
send(pid, {:basic_deliver, payload, %{routing_key: key}})
|
||||
assert_receive :called_auto_sync?, 10
|
||||
assert_receive {:update_called, "FbosConfig", %{"foo" => "bar"}, 999}, 10
|
||||
end
|
||||
|
||||
test "auto_sync disabled, resource not in @cache_kinds" do
|
||||
test_pid = self()
|
||||
%{pid: pid} = under_normal_conditions()
|
||||
|
||||
key = "bot.device_15.sync.Point.999"
|
||||
payload = '{"args":{"label":"foo"},"body":{"foo": "bar"}}'
|
||||
|
||||
stub(MockQuery, :auto_sync?, fn ->
|
||||
send(test_pid, :called_auto_sync?)
|
||||
false
|
||||
end)
|
||||
|
||||
stub(MockCommand, :cached_update, fn x, y, z ->
|
||||
send(test_pid, {:cached_update_called, x, y, z})
|
||||
:ok
|
||||
end)
|
||||
|
||||
send(pid, {:basic_deliver, payload, %{routing_key: key}})
|
||||
assert_receive {:cached_update_called, "Point", %{"foo" => "bar"}, 999}, 10
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue