Convert `string` type to more specific type in Log interface

pull/492/head
Rick Carlino 2017-10-10 10:41:37 -05:00
parent 9cf4fdeb89
commit d12e609420
4 changed files with 104 additions and 66 deletions

View File

@ -0,0 +1,34 @@
import { HardwareState } from "../../../devices/interfaces";
import { incomingStatus, ifToastWorthy } from "../../connect_device";
import { Actions } from "../../../constants";
import { Log } from "../../../interfaces";
import { ALLOWED_CHANNEL_NAMES, ALLOWED_MESSAGE_TYPES } from "farmbot";
describe("incomingStatus", () => {
it("creates an action", () => {
const stub = {} as HardwareState;
const result = incomingStatus(stub);
expect(result.type).toEqual(Actions.BOT_CHANGE);
expect(result.payload).toEqual(stub);
});
});
describe("ifToast", () => {
function log(meta_type: ALLOWED_MESSAGE_TYPES,
chan: ALLOWED_CHANNEL_NAMES = "toast"): Log {
return {
message: "toasty!",
meta: { type: meta_type },
channels: [chan],
created_at: -1
};
}
it("skips irrelevant channels like `email`", () => {
const callback = jest.fn();
ifToastWorthy(log("success", "email"), callback);
expect(callback).not.toHaveBeenCalled();
});
it("does not toast others");
});

View File

@ -1,7 +1,7 @@
import { devices } from "../device";
import { dispatchNetworkUp, dispatchNetworkDown } from "./index";
import { Log } from "../interfaces";
import { ALLOWED_CHANNEL_NAMES, ALLOWED_MESSAGE_TYPES, Farmbot, BotStateTree } from "farmbot";
import { ALLOWED_CHANNEL_NAMES, Farmbot, BotStateTree } from "farmbot";
import { get, set, throttle, noop } from "lodash";
import { success, error, info, warning } from "farmbot-toastr";
import { HardwareState } from "../devices/interfaces";
@ -28,19 +28,23 @@ const BAD_WORDS = ["WPA", "PSK", "PASSWORD", "NERVES"];
let alreadyToldYou = false;
const mq = "user.mqtt";
function incomingStatus(statusMessage: HardwareState) {
/** Action creator that is called when FarmBot OS emits a status update.
* Coordinate updates, movement, etc.*/
export function incomingStatus(statusMessage: HardwareState) {
return { type: Actions.BOT_CHANGE, payload: statusMessage };
}
function ifToast(log: Log, cb: (m: ALLOWED_MESSAGE_TYPES) => void) {
/** Determine if an incoming log is a toast message. If it is, execute the
* supplied callback */
export function ifToastWorthy(log: Log | undefined,
cb: (log: Log) => void) {
const chanList = get(log, CHANNELS, ["ERROR FETCHING CHANNELS"]);
return chanList.includes(TOAST) ?
cb(log.meta.type as ALLOWED_MESSAGE_TYPES) : noop();
return log && chanList.includes(TOAST) ? cb(log) : noop();
}
function maybeShowLog(log: Log) {
ifToast(log, (m) => {
switch (m) {
function maybeShowLog(mystery: Log | undefined) {
ifToastWorthy(mystery, (log) => {
switch (log.meta.type) {
case "success":
return success(log.message, TITLE);
case "busy":
@ -68,39 +72,37 @@ function readStatus() {
.then(() => { commandOK(noun); }, () => { });
}
export function connectDevice(oldToken: string): ConnectDeviceReturn {
return (dispatch: Function, getState: GetState) => {
const ath = getState().auth;
const ok = doConnect(dispatch, getState);
ath ? (maybeRefreshToken(ath).then(ok)) : bail(AUTH_NOT_READY);
};
}
const onOffline = () => {
dispatchNetworkDown("user.mqtt");
error(t(Content.MQTT_DISCONNECTED));
};
const doConnect = (dispatch: Function, getState: GetState) =>
({
token }: { token: Token }) => {
const secure = location.protocol === "https:";
const bot = new Farmbot({ token: token.encoded, secure });
bot.on("online", onOnline);
bot.on("offline", onOffline);
return bot
.connect()
.then(bootstrapAllTheThings(bot, dispatch, getState), noop);
};
const changeLastClientConnected = (bot: Farmbot) => () => {
bot.setUserEnv({
"LAST_CLIENT_CONNECTED": JSON.stringify(new Date())
});
};
function onMalformed() {
bothUp();
if (!alreadyToldYou) {
warning(t(Content.MALFORMED_MESSAGE_REC_UPGRADE));
alreadyToldYou = true;
}
}
const onStatus = (dispatch: Function, getState: GetState) =>
(throttle(function (msg: BotStateTree) {
bothUp();
dispatch(incomingStatus(msg));
if (NEED_VERSION_CHECK) {
const IS_OK = versionOK(getState()
.bot
.hardware
.informational_settings
.controller_version, EXPECTED_MAJOR, EXPECTED_MINOR);
if (!IS_OK) { badVersion(); }
NEED_VERSION_CHECK = false;
}
}, 500));
const onSent = (/** The MQTT Client Object (bot.client) */ client: {}) =>
(any: {}) => {
const netState = (get(client, "connected", false));
netState ? dispatchNetworkUp(mq) : dispatchNetworkDown(mq);
};
const onLogs = (dispatch: Function) => (msg: Log) => {
bothUp();
@ -126,34 +128,13 @@ const onLogs = (dispatch: Function) => (msg: Log) => {
}
};
const onSent = (/** The MQTT Client Object (bot.client) */ client: {}) =>
(any: {}) => {
const netState = (get(client, "connected", false));
netState ? dispatchNetworkUp(mq) : dispatchNetworkDown(mq);
};
const onStatus = (dispatch: Function, getState: GetState) =>
(throttle(function (msg: BotStateTree) {
bothUp();
dispatch(incomingStatus(msg));
if (NEED_VERSION_CHECK) {
const IS_OK = versionOK(getState()
.bot
.hardware
.informational_settings
.controller_version, EXPECTED_MAJOR, EXPECTED_MINOR);
if (!IS_OK) { badVersion(); }
NEED_VERSION_CHECK = false;
}
}, 500));
const onOnline = () => dispatchNetworkUp("user.mqtt");
const changeLastClientConnected = (bot: Farmbot) => () => {
bot.setUserEnv({
"LAST_CLIENT_CONNECTED": JSON.stringify(new Date())
});
};
function onMalformed() {
bothUp();
if (!alreadyToldYou) {
warning(t(Content.MALFORMED_MESSAGE_REC_UPGRADE));
alreadyToldYou = true;
}
}
const bootstrapAllTheThings =
(bot: Farmbot, dispatch: Function, getState: GetState) => () => {
@ -166,3 +147,26 @@ const bootstrapAllTheThings =
readStatus().then(changeLastClientConnected(bot), noop);
set(window, "current_bot", bot);
};
const onOnline = () => dispatchNetworkUp("user.mqtt");
const doConnect = (dispatch: Function, getState: GetState) =>
({
token }: { token: Token }) => {
const secure = location.protocol === "https:";
const bot = new Farmbot({ token: token.encoded, secure });
bot.on("online", onOnline);
bot.on("offline", onOffline);
return bot
.connect()
.then(bootstrapAllTheThings(bot, dispatch, getState), noop);
};
export function connectDevice(oldToken: string): ConnectDeviceReturn {
return (dispatch: Function, getState: GetState) => {
const ath = getState().auth;
const ok = doConnect(dispatch, getState);
ath ? (maybeRefreshToken(ath).then(ok)) : bail(AUTH_NOT_READY);
};
}

View File

@ -1,7 +1,7 @@
import { AuthState } from "./auth/interfaces";
import { ConfigState } from "./config/interfaces";
import { BotState } from "./devices/interfaces";
import { Color as FarmBotJsColor } from "farmbot";
import { Color as FarmBotJsColor, ALLOWED_MESSAGE_TYPES } from "farmbot";
import { DraggableState } from "./draggable/interfaces";
import { PeripheralState } from "./controls/peripherals/interfaces";
import { RestResources } from "./resources/interfaces";
@ -25,7 +25,7 @@ export interface SelectOptionsParams {
export interface Log {
id?: number | undefined;
message: string;
meta: { type: string; };
meta: { type: ALLOWED_MESSAGE_TYPES; };
channels: string[];
created_at: number;
}

View File

@ -29,7 +29,7 @@ export let TickerList = (props: TickerListProps) => {
const firstTicker: Log = props.logs.filter(
log => !log.message.toLowerCase().includes("filtered"))[0];
const noLogs: Log = {
message: "No logs yet.", meta: { type: "" }, channels: [], created_at: NaN
message: "No logs yet.", meta: { type: "success" }, channels: [], created_at: NaN
};
return (
<div