import axios from "axios"; import { API } from "../api"; import { Actions } from "../constants"; import { TaggedResource as TR, SpecialStatus, TaggedResource } from "farmbot"; import { Session } from "../session"; import { arrayWrap, generateUuid } from "../resources/util"; export interface SyncBodyContents { kind: T["kind"]; body: T[]; } export interface SyncResponse { type: Actions.RESOURCE_READY; payload: SyncBodyContents } export const resourceReady = (kind: T["kind"], body: T | T[]): SyncResponse => { return { type: Actions.RESOURCE_READY, payload: { kind, body: arrayWrap(body) } }; }; export const newTaggedResource = (kind: T["kind"], bodies: T["body"] | T["body"][], specialStatus = SpecialStatus.SAVED): T[] => { const arr = arrayWrap(bodies); return arr.map((body: T["body"]): T => { return { kind: kind as TaggedResource["kind"], body: body as TaggedResource["body"], uuid: generateUuid(body?.id, kind), specialStatus } as T; }); }; export function syncFail(e: Error) { console.error("DATA SYNC ERROR!"); Session.clear() as never; throw e; } const download = (dispatch: Function) => (kind: T["kind"], url: string) => axios .get(url) .then(({ data }) => { dispatch(resourceReady(kind, newTaggedResource(kind, data))); }, syncFail); export async function fetchSyncData(dispatch: Function) { const get = download(dispatch); /** Resources are placed into groups based on their dependencies. * For example, if: * * a Regimen relies on a Sequence * * a Sequence relies on a tool * * a tool has no deps. * then they must be loaded in the order Regimen => Sequence => Tool to avoid * conflicts. */ const group = { 0: () => Promise.all<{}>([ get("User", API.current.usersPath), get("Device", API.current.devicePath), get("FirmwareConfig", API.current.firmwareConfigPath), get("FarmwareEnv", API.current.farmwareEnvPath), get("FarmwareInstallation", API.current.farmwareInstallationPath), get("WebAppConfig", API.current.webAppConfigPath), get("SavedGarden", API.current.savedGardensPath), ]), 1: () => Promise.all<{}>([ get("PlantTemplate", API.current.plantTemplatePath), get("Peripheral", API.current.peripheralsPath), get("Point", API.current.allPointsPath), get("Sensor", API.current.sensorPath), get("Tool", API.current.toolsPath), get("Alert", API.current.alertPath), get("Folder", API.current.foldersPath), ]), 2: () => Promise.all<{}>([ get("SensorReading", API.current.sensorReadingPath), get("Sequence", API.current.sequencesPath), get("PointGroup", API.current.pointGroupsPath), ]), 3: () => Promise.all<{}>([ get("FbosConfig", API.current.fbosConfigPath), get("Regimen", API.current.regimensPath), get("PinBinding", API.current.pinBindingPath), ]), 4: () => Promise.all<{}>([ get("FarmEvent", API.current.farmEventsPath), get("Image", API.current.imagesPath), get("Log", API.current.filteredLogsPath), get("WebcamFeed", API.current.webcamFeedPath), ]), }; const step = (num: keyof typeof group) => group[num]; step(0)().then(step(1)).then(step(2)).then(step(3)).then(step(4)).catch(syncFail); }