Store settings in lowdb

This commit is contained in:
Thomas Nordquist
2019-02-17 17:05:12 +01:00
parent 1740df6218
commit 3f52944f18
9 changed files with 194 additions and 22 deletions

View File

@@ -1,21 +1,80 @@
import { rendererEvents } from '../../events'
import { v4 } from 'uuid'
import {
storageStoreEvent,
makeStorageResponseEvent,
storageLoadEvent,
storageClearEvent,
makeStorageAcknoledgementEvent,
} from '../../events/StorageEvents'
export interface StorageIdentifier<Model> { export interface StorageIdentifier<Model> {
id: string id: string
} }
export interface PersistantStorage { export interface PersistantStorage {
store<Model>(identifier: StorageIdentifier<Model>, data: Model): void store<Model>(identifier: StorageIdentifier<Model>, data: Model): Promise<void>
load<Model>(identifier: StorageIdentifier<Model>): Model | undefined load<Model>(identifier: StorageIdentifier<Model>): Promise<Model | undefined>
clear(): Promise<void>
} }
class LocalStorage implements PersistantStorage { class RemoteStorage implements PersistantStorage {
public store<Model>(identifier: StorageIdentifier<Model>, data: Model) { private timeoutCallback(event: any, callback: any, reject: any) {
localStorage.setItem(identifier.id, JSON.stringify(data)) setTimeout(() => {
reject('remote storage timeout')
rendererEvents.unsubscribe(event, callback)
}, 10000)
} }
public load<Model>(identifier: StorageIdentifier<Model>): Model | undefined { private expectAck(transactionId: string): Promise<void> {
const data = localStorage.getItem(identifier.id) const ack = makeStorageAcknoledgementEvent(transactionId)
return data && JSON.parse(data) return new Promise<void>((resolve, reject) => {
const callback = () => {
resolve()
rendererEvents.unsubscribe(ack, callback)
}
rendererEvents.subscribe(ack, callback)
this.timeoutCallback(ack, callback, reject)
})
}
public store<Model>(identifier: StorageIdentifier<Model>, data: Model): Promise<void> {
const transactionId = v4()
const expectation = this.expectAck(transactionId)
rendererEvents.emit(storageStoreEvent, { data, transactionId, store: identifier.id })
return expectation
}
public load<Model>(identifier: StorageIdentifier<Model>): Promise<Model | undefined> {
const transactionId = v4()
const responseEvent = makeStorageResponseEvent(transactionId)
const promise = new Promise<Model>((resolve, reject) => {
const callback = (msg: any) => {
const data = msg.data && JSON.parse(msg.data)
resolve(data)
rendererEvents.unsubscribe(responseEvent, callback)
}
rendererEvents.subscribe(responseEvent, callback)
this.timeoutCallback(responseEvent, callback, reject)
})
rendererEvents.emit(storageLoadEvent, {
transactionId,
store: identifier.id,
})
return promise
}
public clear(): Promise<void> {
const transactionId = v4()
const expectation = this.expectAck(transactionId)
rendererEvents.emit(storageClearEvent, { transactionId })
return expectation
} }
} }
export default new LocalStorage() export default new RemoteStorage()

View File

@@ -12,9 +12,9 @@ const storedConnectionsIdentifier: StorageIdentifier<{[s: string]: ConnectionOpt
id: 'ConnectionManager_connections', id: 'ConnectionManager_connections',
} }
export const loadConnectionSettings = () => (dispatch: Dispatch<any>, getState: () => AppState) => { export const loadConnectionSettings = () => async (dispatch: Dispatch<any>, getState: () => AppState) => {
ensureConnectionsHaveBeenInitialized() await ensureConnectionsHaveBeenInitialized()
const connections = persistantStorage.load(storedConnectionsIdentifier) const connections = await persistantStorage.load(storedConnectionsIdentifier)
if (!connections) { if (!connections) {
return return
@@ -92,9 +92,8 @@ export const deleteConnection = (connectionId: string) => (dispatch: Dispatch<an
} }
} }
function ensureConnectionsHaveBeenInitialized() { async function ensureConnectionsHaveBeenInitialized() {
const connections = persistantStorage.load(storedConnectionsIdentifier) const connections = await persistantStorage.load(storedConnectionsIdentifier)
const requiresInitialization = !connections const requiresInitialization = !connections
if (requiresInitialization) { if (requiresInitialization) {
const migratedConnection = loadLegacyConnectionOptions() const migratedConnection = loadLegacyConnectionOptions()

View File

@@ -315,7 +315,6 @@ class ConnectionSettings extends React.Component<Props, State> {
const mqttOptions = toMqttConnection(this.props.connection) const mqttOptions = toMqttConnection(this.props.connection)
if (mqttOptions) { if (mqttOptions) {
console.log(mqttOptions)
this.props.actions.connect(mqttOptions, this.props.connection.id) this.props.actions.connect(mqttOptions, this.props.connection.id)
} }
} }

View File

@@ -0,0 +1,41 @@
import * as FileAsync from 'lowdb/adapters/FileAsync'
import * as lowdb from 'lowdb'
import { backendEvents } from '../../events'
import {
makeStorageResponseEvent,
storageClearEvent,
storageLoadEvent,
storageStoreEvent,
makeStorageAcknoledgementEvent,
} from '../../events/StorageEvents'
export default class ConfigStorage {
private adapter: any
constructor(file: string) {
this.adapter = new FileAsync(file)
}
public async init() {
const database: lowdb.LoDashExplicitAsyncWrapper<any> = await lowdb(this.adapter)
backendEvents.subscribe(storageStoreEvent, async (event) => {
await database.set(event.store, event.data).write()
backendEvents.emit(makeStorageAcknoledgementEvent(event.transactionId), undefined)
})
backendEvents.subscribe(storageLoadEvent, async (event) => {
const responseEvent = makeStorageResponseEvent(event.transactionId)
try {
const data = await database.get(event.store).value()
backendEvents.emit(responseEvent, { data, transactionId: event.transactionId })
} catch (error) {
console.error(error)
backendEvents.emit(responseEvent, { transactionId: event.transactionId })
}
})
backendEvents.subscribe(storageClearEvent, async (event) => {
await database.drop()
backendEvents.emit(makeStorageAcknoledgementEvent(event.transactionId), undefined)
})
}
}

View File

@@ -12,7 +12,7 @@ import {
updateAvailable, updateAvailable,
} from '../../events' } from '../../events'
import { DataSource, MqttSource } from './DataSource' import { DataSource, MqttSource } from './DataSource'
import ConfigStorage from './ConfigStorage'
import { UpdateInfo } from 'builder-util-runtime' import { UpdateInfo } from 'builder-util-runtime'
export class ConnectionManager { export class ConnectionManager {
@@ -80,3 +80,6 @@ class UpdateNotifier {
} }
export const updateNotifier = new UpdateNotifier() export const updateNotifier = new UpdateNotifier()
const configStorage = new ConfigStorage('blah.json')
configStorage.init()

View File

@@ -21,10 +21,11 @@ class IpcMainEventBus implements EventBusInterface {
this.ipc = ipc this.ipc = ipc
} }
public subscribe<MessageType>(event: Event<MessageType>, callback:(msg: MessageType) => void) { public subscribe<MessageType>(subscribeEvent: Event<MessageType>, callback:(msg: MessageType) => void) {
console.log('subscribing', event.topic) console.log('subscribing', subscribeEvent.topic)
this.ipc.on(event.topic, (event: any, arg: any) => { this.ipc.on(subscribeEvent.topic, (event: any, arg: any) => {
this.client = event.sender this.client = event.sender
console.log(subscribeEvent.topic, arg)
callback(arg) callback(arg)
}) })
} }

38
events/StorageEvents.ts Normal file
View File

@@ -0,0 +1,38 @@
import { Event } from './'
interface StorageEvent {
transactionId: string
}
export interface StoreCommand extends StorageEvent {
store: string,
data: any
}
export interface LoadCommand extends StorageEvent {
store: string,
}
export const storageStoreEvent: Event<StoreCommand> = {
topic: 'storage/store',
}
export const storageLoadEvent: Event<LoadCommand> = {
topic: 'storage/load',
}
export function makeStorageAcknoledgementEvent(transactionId: string): Event<StoreCommand> {
return {
topic: `storage/ack/${transactionId}`,
}
}
export function makeStorageResponseEvent(transactionId: string): Event<StoreCommand> {
return {
topic: `storage/response/${transactionId}`,
}
}
export const storageClearEvent: Event<StorageEvent> = {
topic: 'storage/clear',
}

View File

@@ -44,6 +44,7 @@
"license": "ISC", "license": "ISC",
"devDependencies": { "devDependencies": {
"@types/chai": "^4.1.7", "@types/chai": "^4.1.7",
"@types/lowdb": "^1.0.6",
"@types/mime": "^2.0.0", "@types/mime": "^2.0.0",
"@types/mocha": "^5.2.5", "@types/mocha": "^5.2.5",
"@types/mustache": "^0.8.32", "@types/mustache": "^0.8.32",
@@ -75,6 +76,7 @@
"electron-log": "^2.2.17", "electron-log": "^2.2.17",
"electron-telemetry": "git+https://github.com/thomasnordquist/electron-telemetry.git#dist", "electron-telemetry": "git+https://github.com/thomasnordquist/electron-telemetry.git#dist",
"electron-updater": "^4.0.6", "electron-updater": "^4.0.6",
"lowdb": "^1.0.0",
"mqtt": "^2.18.8", "mqtt": "^2.18.8",
"sha1": "^1.1.1" "sha1": "^1.1.1"
} }

View File

@@ -119,6 +119,18 @@
resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.1.7.tgz#1b8e33b61a8c09cbe1f85133071baa0dbf9fa71a" resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.1.7.tgz#1b8e33b61a8c09cbe1f85133071baa0dbf9fa71a"
integrity sha512-2Y8uPt0/jwjhQ6EiluT0XCri1Dbplr0ZxfFXUz+ye13gaqE8u5gL5ppao1JrUYr9cIip5S6MvQzBS7Kke7U9VA== integrity sha512-2Y8uPt0/jwjhQ6EiluT0XCri1Dbplr0ZxfFXUz+ye13gaqE8u5gL5ppao1JrUYr9cIip5S6MvQzBS7Kke7U9VA==
"@types/lodash@*":
version "4.14.121"
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.121.tgz#9327e20d49b95fc2bf983fc2f045b2c6effc80b9"
integrity sha512-ORj7IBWj13iYufXt/VXrCNMbUuCTJfhzme5kx9U/UtcIPdJYuvPDUAlHlbNhz/8lKCLy9XGIZnGrqXOtQbPGoQ==
"@types/lowdb@^1.0.6":
version "1.0.6"
resolved "https://registry.yarnpkg.com/@types/lowdb/-/lowdb-1.0.6.tgz#0e7adecb87cd79c1e97d50043d9835b65d59023f"
integrity sha512-C/p2p3ud6buHPUaj5QTN3gGera9Pi39aCQoQ1ngRZ2hsWeoqok4aCF/Jjj8FDsnSOTaQHrKI92/KHGt6S+Oy+Q==
dependencies:
"@types/lodash" "*"
"@types/mime@^2.0.0": "@types/mime@^2.0.0":
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/@types/mime/-/mime-2.0.0.tgz#5a7306e367c539b9f6543499de8dd519fac37a8b" resolved "https://registry.yarnpkg.com/@types/mime/-/mime-2.0.0.tgz#5a7306e367c539b9f6543499de8dd519fac37a8b"
@@ -1589,7 +1601,7 @@ got@^6.7.1:
unzip-response "^2.0.1" unzip-response "^2.0.1"
url-parse-lax "^1.0.0" url-parse-lax "^1.0.0"
graceful-fs@^4.1.0, graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6: graceful-fs@^4.1.0, graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.3, graceful-fs@^4.1.6:
version "4.1.15" version "4.1.15"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.15.tgz#ffb703e1066e8a0eeaa4c8b80ba9253eeefbfb00" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.15.tgz#ffb703e1066e8a0eeaa4c8b80ba9253eeefbfb00"
integrity sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA== integrity sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==
@@ -2171,7 +2183,7 @@ lodash.zip@^4.2.0:
resolved "https://registry.yarnpkg.com/lodash.zip/-/lodash.zip-4.2.0.tgz#ec6662e4896408ed4ab6c542a3990b72cc080020" resolved "https://registry.yarnpkg.com/lodash.zip/-/lodash.zip-4.2.0.tgz#ec6662e4896408ed4ab6c542a3990b72cc080020"
integrity sha1-7GZi5IlkCO1KtsVCo5kLcswIACA= integrity sha1-7GZi5IlkCO1KtsVCo5kLcswIACA=
lodash@^4.17.10, lodash@^4.3.0, lodash@^4.8.0, lodash@~4.17.10: lodash@4, lodash@^4.17.10, lodash@^4.3.0, lodash@^4.8.0, lodash@~4.17.10:
version "4.17.11" version "4.17.11"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d"
integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg== integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==
@@ -2194,6 +2206,17 @@ loud-rejection@^1.0.0:
currently-unhandled "^0.4.1" currently-unhandled "^0.4.1"
signal-exit "^3.0.0" signal-exit "^3.0.0"
lowdb@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/lowdb/-/lowdb-1.0.0.tgz#5243be6b22786ccce30e50c9a33eac36b20c8064"
integrity sha512-2+x8esE/Wb9SQ1F9IHaYWfsC9FIecLOPrK4g17FGEayjUWH172H6nwicRovGvSE2CPZouc2MCIqCI7h9d+GftQ==
dependencies:
graceful-fs "^4.1.3"
is-promise "^2.1.0"
lodash "4"
pify "^3.0.0"
steno "^0.4.1"
lowercase-keys@^1.0.0: lowercase-keys@^1.0.0:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f"
@@ -3296,6 +3319,13 @@ stat-mode@^0.2.2:
resolved "https://registry.yarnpkg.com/stat-mode/-/stat-mode-0.2.2.tgz#e6c80b623123d7d80cf132ce538f346289072502" resolved "https://registry.yarnpkg.com/stat-mode/-/stat-mode-0.2.2.tgz#e6c80b623123d7d80cf132ce538f346289072502"
integrity sha1-5sgLYjEj19gM8TLOU480YokHJQI= integrity sha1-5sgLYjEj19gM8TLOU480YokHJQI=
steno@^0.4.1:
version "0.4.4"
resolved "https://registry.yarnpkg.com/steno/-/steno-0.4.4.tgz#071105bdfc286e6615c0403c27e9d7b5dcb855cb"
integrity sha1-BxEFvfwobmYVwEA8J+nXtdy4Vcs=
dependencies:
graceful-fs "^4.1.3"
stream-shift@^1.0.0: stream-shift@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.0.tgz#d5c752825e5367e786f78e18e445ea223a155952" resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.0.tgz#d5c752825e5367e786f78e18e445ea223a155952"