Skip to content

Commit 68f60cd

Browse files
committed
Automatically initialize node-persist storage (remove requirement to call the hap.init function)
1 parent 7b6b820 commit 68f60cd

File tree

9 files changed

+130
-29
lines changed

9 files changed

+130
-29
lines changed

@types/node-persist.d.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
11
declare module 'node-persist' {
22

33
export interface InitOptions {
4-
dir?: string;
4+
dir?: string; // default 'persist'
5+
stringify?: typeof JSON.stringify, // default JSON.stringify
6+
parse?: typeof JSON.parse, // default JSON.parse
7+
encoding?: string, // default 'utf8'
8+
logging?: boolean,
9+
continuous?: boolean, // default true (instantly persists to disk)
10+
interval?: false | number, // milliseconds
11+
ttl?: false | true | number, // can be true for 24h default or a number in MILLISECONDS
512
}
613

714
export class LocalStorage {
@@ -12,6 +19,7 @@ declare module 'node-persist' {
1219
getItem(key: string): any;
1320
setItemSync(key: string, value: any): void;
1421
removeItemSync(key: string): void
22+
persistSync(): void;
1523

1624
}
1725

__mocks__/node-persist.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ class Storage {
33
setItemSync = jest.fn();
44
persistSync = jest.fn();
55
removeItemSync = jest.fn();
6+
initSync = jest.fn();
7+
create = jest.fn().mockImplementation(() => new Storage());
68
}
79

810
export default new Storage();

src/index.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
/// <reference path="../@types/bonjour-hap.d.ts" />
22
/// <reference path="../@types/node-persist.d.ts" />
3-
import storage from 'node-persist';
43

54
import './lib/gen';
65
import * as accessoryLoader from './lib/AccessoryLoader';
76
import * as uuidFunctions from './lib/util/uuid';
87
import * as legacyTypes from './accessories/types';
8+
import { HAPStorage } from "./lib/model/HAPStorage";
99

1010
export const AccessoryLoader = accessoryLoader;
1111
export const uuid = uuidFunctions;
@@ -31,10 +31,14 @@ export * from './lib/util/tlv';
3131
export * from './types';
3232
export const LegacyTypes = legacyTypes;
3333

34+
/**
35+
*
36+
* @param {string} storagePath
37+
* @deprecated the need to manually initialize the internal storage was removed. If you want to set a custom
38+
* storage path location, please use {@link HAPStorage.setCustomStoragePath} directly.
39+
*/
3440
export function init(storagePath?: string) {
35-
// initialize our underlying storage system, passing on the directory if needed
36-
if (typeof storagePath !== 'undefined')
37-
storage.initSync({ dir: storagePath });
38-
else
39-
storage.initSync(); // use whatever is default
41+
if (storagePath) {
42+
HAPStorage.setCustomStoragePath(storagePath);
43+
}
4044
}

src/lib/model/AccessoryInfo.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
import storage from 'node-persist';
21
import util from 'util';
32
import assert from 'assert';
43
import tweetnacl from 'tweetnacl';
54

65
import { Categories } from '../Accessory';
76
import { Session } from "../util/eventedhttp";
87
import { MacAddress } from "../../types";
8+
import { HAPStorage } from "./HAPStorage";
99

1010
export enum PermissionTypes {
1111
USER = 0x00,
@@ -220,8 +220,7 @@ export class AccessoryInfo {
220220

221221
var key = AccessoryInfo.persistKey(this.username);
222222

223-
storage.setItemSync(key, saved);
224-
storage.persistSync();
223+
HAPStorage.storage().setItemSync(key, saved);
225224
}
226225

227226
// Gets a key for storing this AccessoryInfo in the filesystem, like "AccessoryInfo.CC223DE3CEF3.json"
@@ -246,7 +245,7 @@ export class AccessoryInfo {
246245
AccessoryInfo.assertValidUsername(username);
247246

248247
var key = AccessoryInfo.persistKey(username);
249-
var saved = storage.getItem(key);
248+
var saved = HAPStorage.storage().getItem(key);
250249

251250
if (saved) {
252251
var info = new AccessoryInfo(username);
@@ -293,7 +292,7 @@ export class AccessoryInfo {
293292

294293
static remove(username: MacAddress) {
295294
const key = AccessoryInfo.persistKey(username);
296-
storage.removeItemSync(key);
295+
HAPStorage.storage().removeItemSync(key);
297296
}
298297

299298
static assertValidUsername = (username: MacAddress) => {

src/lib/model/ControllerStorage.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
import storage from 'node-persist';
21
import {MacAddress} from "../../types";
32
import util from "util";
43
import createDebug from "debug";
54
import {ControllerType, SerializableController} from "../controller";
65
import {Accessory} from "../Accessory";
6+
import { HAPStorage } from "./HAPStorage";
77

88
const debug = createDebug("ControllerStorage");
99

@@ -159,7 +159,7 @@ export class ControllerStorage {
159159
this.username = username;
160160

161161
const key = ControllerStorage.persistKey(username);
162-
const saved: StorageLayout | undefined = storage.getItem(key);
162+
const saved: StorageLayout | undefined = HAPStorage.storage().getItem(key);
163163

164164
let ownData;
165165
if (saved) {
@@ -230,10 +230,10 @@ export class ControllerStorage {
230230
};
231231

232232
this.fileCreated = true;
233-
storage.setItemSync(key, saved);
233+
HAPStorage.storage().setItemSync(key, saved);
234234
} else if (this.fileCreated) {
235235
this.fileCreated = false;
236-
storage.removeItemSync(key);
236+
HAPStorage.storage().removeItemSync(key);
237237
}
238238
}
239239

@@ -243,7 +243,7 @@ export class ControllerStorage {
243243

244244
static remove(username: MacAddress) {
245245
const key = ControllerStorage.persistKey(username);
246-
storage.removeItemSync(key);
246+
HAPStorage.storage().removeItemSync(key);
247247
}
248248

249249
}

src/lib/model/HAPStorage.spec.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// @ts-ignore
2+
import nodePersist from "node-persist";
3+
import { HAPStorage } from "./HAPStorage";
4+
5+
describe(HAPStorage, () => {
6+
7+
describe("storage", () => {
8+
it('should init storage correctly and only once', function () {
9+
const storage = new HAPStorage();
10+
11+
// @ts-ignore
12+
expect(storage.localStore).toBeUndefined();
13+
const localStore = storage.storage(); // init first time
14+
expect(nodePersist.create).toHaveBeenCalledTimes(1);
15+
expect(localStore.initSync).toHaveBeenCalledTimes(1);
16+
17+
// @ts-ignore
18+
expect(storage.localStore).toBeDefined();
19+
const localStore2 = storage.storage(); // init first time
20+
expect(nodePersist.create).toHaveBeenCalledTimes(1);
21+
expect(localStore2).toEqual(localStore);
22+
expect(localStore2.initSync).toHaveBeenCalledTimes(1);
23+
});
24+
25+
});
26+
27+
describe("setCustomStoragePath", () => {
28+
it('should init storage correctly with custom storage path', function () {
29+
const storage = new HAPStorage();
30+
31+
storage.setCustomStoragePath("asdfPath");
32+
const localStore = storage.storage();
33+
expect(localStore.initSync).toHaveBeenCalledTimes(1);
34+
expect(localStore.initSync).toHaveBeenLastCalledWith({dir: "asdfPath"});
35+
});
36+
37+
it('should reject setCustomStoragePath after storage has already been initialized', function () {
38+
const storage = new HAPStorage();
39+
40+
storage.storage();
41+
expect(() => storage.setCustomStoragePath("customPath")).toThrow(Error);
42+
});
43+
})
44+
45+
});

src/lib/model/HAPStorage.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import storage, { LocalStorage } from "node-persist";
2+
3+
export class HAPStorage {
4+
5+
private static readonly INSTANCE = new HAPStorage();
6+
7+
private localStore?: LocalStorage;
8+
private customStoragePath?: string;
9+
10+
public static storage(): LocalStorage {
11+
return this.INSTANCE.storage();
12+
}
13+
14+
public static setCustomStoragePath(path: string): void {
15+
this.INSTANCE.setCustomStoragePath(path);
16+
}
17+
18+
public storage(): LocalStorage {
19+
if (!this.localStore) {
20+
this.localStore = storage.create();
21+
22+
if (this.customStoragePath) {
23+
this.localStore.initSync({
24+
dir: this.customStoragePath,
25+
})
26+
} else {
27+
this.localStore.initSync();
28+
}
29+
}
30+
31+
return this.localStore;
32+
}
33+
34+
public setCustomStoragePath(path: string): void {
35+
if (this.localStore) {
36+
throw new Error("Cannot change storage path after it has already been initialized!");
37+
}
38+
39+
this.customStoragePath = path;
40+
}
41+
42+
}

src/lib/model/IdentifierCache.spec.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
1-
// @ts-ignore
2-
import storage from 'node-persist';
3-
41
import { IdentifierCache } from './IdentifierCache';
2+
import { LocalStorage } from "node-persist";
3+
import { HAPStorage } from "./HAPStorage";
4+
5+
function pullOutLocalStore(): LocalStorage {
6+
// @ts-ignore
7+
return HAPStorage.INSTANCE.localStore
8+
}
59

610
const createIdentifierCache = (username = 'username') => {
711
return new IdentifierCache(username);
@@ -106,8 +110,7 @@ describe('IdentifierCache', () => {
106110
const identifierCache = createIdentifierCache();
107111
identifierCache.save();
108112

109-
expect(storage.setItemSync).toHaveBeenCalledTimes(1);
110-
expect(storage.persistSync).toHaveBeenCalledTimes(1);
113+
expect(pullOutLocalStore().setItemSync).toHaveBeenCalledTimes(1);
111114
});
112115
});
113116

@@ -116,7 +119,7 @@ describe('IdentifierCache', () => {
116119
const identifierCache = createIdentifierCache();
117120
IdentifierCache.remove(identifierCache.username);
118121

119-
expect(storage.removeItemSync).toHaveBeenCalledTimes(1);
122+
expect(pullOutLocalStore().removeItemSync).toHaveBeenCalledTimes(1);
120123
});
121124
});
122125

src/lib/model/IdentifierCache.ts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import crypto from 'crypto';
22
import util from 'util';
3-
4-
import storage from 'node-persist';
53
import { MacAddress } from "../../types";
4+
import { HAPStorage } from "./HAPStorage";
65

76
/**
87
* IdentifierCache is a model class that manages a system of associating HAP "Accessory IDs" and "Instance IDs"
@@ -86,8 +85,7 @@ export class IdentifierCache {
8685
cache: this._cache
8786
};
8887
var key = IdentifierCache.persistKey(this.username);
89-
storage.setItemSync(key, saved);
90-
storage.persistSync();
88+
HAPStorage.storage().setItemSync(key, saved);
9189
this._savedCacheHash = newCacheHash; //update hash of saved cache for future use
9290
}
9391
}
@@ -102,7 +100,7 @@ export class IdentifierCache {
102100

103101
static load = (username: MacAddress) => {
104102
var key = IdentifierCache.persistKey(username);
105-
var saved = storage.getItem(key);
103+
var saved = HAPStorage.storage().getItem(key);
106104
if (saved) {
107105
var info = new IdentifierCache(username);
108106
info._cache = saved.cache;
@@ -115,7 +113,7 @@ export class IdentifierCache {
115113

116114
static remove(username: MacAddress) {
117115
const key = this.persistKey(username);
118-
storage.removeItemSync(key);
116+
HAPStorage.storage().removeItemSync(key);
119117
}
120118

121119
}

0 commit comments

Comments
 (0)