Farmbot-Web-App/frontend/connectivity/auto_sync.ts

89 lines
3.5 KiB
TypeScript
Raw Permalink Normal View History

2017-10-26 14:49:23 -06:00
import { GetState } from "../redux/interfaces";
import { maybeDetermineUuid } from "../resources/selectors";
2019-09-23 12:56:35 -06:00
import { TaggedResource, SpecialStatus } from "farmbot";
import { overwrite, init } from "../api/crud";
2017-11-10 14:16:18 -07:00
import { handleInbound } from "./auto_sync_handle_inbound";
2019-09-23 12:56:35 -06:00
import {
2020-02-28 09:35:32 -07:00
SyncPayload, MqttDataResult, Reason, UpdateMqttData,
2019-09-23 12:56:35 -06:00
} from "./interfaces";
2017-12-22 08:19:30 -07:00
import { outstandingRequests } from "./data_consistency";
import { newTaggedResource } from "../sync/actions";
2017-11-10 12:54:34 -07:00
export function decodeBinary(payload: Buffer): SyncPayload {
return JSON.parse((payload).toString());
}
2017-11-10 12:54:34 -07:00
2018-12-06 09:10:41 -07:00
const SKIP_THESE = ["DeviceSerialNumber"]; // Only FBOS Cares about this one.
2019-09-23 12:56:35 -06:00
export function routeMqttData(chan: string, payload: Buffer):
MqttDataResult<TaggedResource> {
2019-03-20 14:16:22 -06:00
/** Skip irrelevant messages: only resource auto-sync messages are desired.
* eg, `bot/device_#/sync/Resource/#` */
if (!(chan.split("/")[2] == "sync")) { return { status: "SKIP" }; }
2017-10-26 14:49:23 -06:00
/** Extract, Validate and scrub the data as it enters the frontend. */
2017-10-26 14:49:23 -06:00
const parts = chan.split("/");
if (parts.length !== 5) { return { status: "ERR", reason: Reason.BAD_CHAN }; }
const id = parseInt(parts.pop() || "0", 10);
2018-12-06 09:10:41 -07:00
const kind = parts.pop() as TaggedResource["kind"];
if (SKIP_THESE.includes(kind)) { return { status: "SKIP" }; }
const { body, args } = decodeBinary(payload);
2017-10-26 14:49:23 -06:00
if (body) {
return { status: "UPDATE", body, kind: kind, id, sessionId: args.label };
2017-10-26 14:49:23 -06:00
} else {
return { status: "DELETE", kind: kind, id }; // 'null' body means delete.
}
}
2019-09-23 12:56:35 -06:00
export function asTaggedResource(data: UpdateMqttData<TaggedResource>):
TaggedResource {
return newTaggedResource(data.kind, data.body)[0];
}
2017-11-10 12:54:34 -07:00
export const handleCreate =
(data: UpdateMqttData<TaggedResource>) => init(data.kind, data.body, true);
2017-11-10 12:54:34 -07:00
export const handleUpdate =
2018-11-06 17:05:15 -07:00
(d: UpdateMqttData<TaggedResource>, uuid: string) => {
const tr = asTaggedResource(d);
2018-11-06 17:05:15 -07:00
tr.uuid = uuid;
2017-11-07 09:02:21 -07:00
return overwrite(tr, tr.body, SpecialStatus.SAVED);
2017-10-30 12:05:15 -06:00
};
2017-10-26 14:49:23 -06:00
2017-11-10 12:54:34 -07:00
export function handleCreateOrUpdate(dispatch: Function,
getState: GetState,
data: UpdateMqttData<TaggedResource>) {
const state = getState();
const { index } = state.resources;
2017-12-25 18:54:18 -07:00
const hasCopy = maybeDetermineUuid(index, data.kind, data.id);
const isEcho = outstandingRequests.all.has(data.sessionId);
2017-12-25 18:54:18 -07:00
// Here be dragons. 🐲 🐉 ⚔ ️🛡️
2017-12-22 11:22:39 -07:00
// PROBLEM: You see incoming `UPDATE` messages.
// How do you know if it is a new record or an update to
// an existing?
//
// SOLUTION: Every inbound message has a `sessionId` that matches an entry in
// the `outstandingRequests` dictionary. If we have a copy of the
// `sessionId` in the `outstandingRequests` object, then you can
// disregard the sync message- you probably already got the data
// when your AJAX request finished. We call this an "echo"- a
2018-04-20 09:53:13 -06:00
// repetition of a data update we already knew about.
2017-12-22 11:22:39 -07:00
//
// The ultimate problem: We need to know if the incoming data update was
// created by us or some other user. That information lets us know if we are
2018-04-20 09:53:13 -06:00
// UPDATE-ing data or INSERTing data. It also prevents us from double updating
2017-12-22 11:22:39 -07:00
// data when an update comes in twice.
2018-11-06 17:05:15 -07:00
const action = hasCopy ? handleUpdate(data, hasCopy) : handleCreate(data);
2017-12-22 11:22:39 -07:00
return isEcho || dispatch(action);
}
2017-11-10 14:16:18 -07:00
export const autoSync =
2018-04-23 09:35:21 -06:00
(dispatch: Function, getState: GetState) => {
return (chan: string, payload: Buffer) => {
2017-11-10 14:16:18 -07:00
handleInbound(dispatch, getState, routeMqttData(chan, payload));
2017-10-26 14:49:23 -06:00
};
2018-04-23 09:35:21 -06:00
};