This repository has been archived by the owner on May 30, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 65
/
feature_store.js
120 lines (99 loc) · 3.21 KB
/
feature_store.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
// The default in-memory implementation of a feature store, which holds feature flags and
// other related data received from LaunchDarkly.
//
// Other implementations of the same interface can be used by passing them in the featureStore
// property of the client configuration (that's why the interface here is async, even though
// the in-memory store doesn't do anything asynchronous - because other implementations may
// need to be async). The interface is defined by LDFeatureStore in index.d.ts.
//
// Additional implementations should use CachingStoreWrapper if possible.
// Note that the contract for feature store methods does *not* require callbacks to be deferred
// with setImmediate, process.nextTick, etc. It is both allowed and desirable to call them
// directly whenever possible (i.e. if we don't actually have to do any I/O), since otherwise
// feature flag retrieval is a major performance bottleneck. These methods are for internal use
// by the SDK, and the SDK does not make any assumptions about whether a callback executes
// before or after the next statement.
function InMemoryFeatureStore() {
let allData = {};
let initCalled = false;
const store = {};
function callbackResult(cb, result) {
cb && cb(result);
}
store.get = (kind, key, cb) => {
const items = allData[kind.namespace] || {};
if (Object.hasOwnProperty.call(items, key)) {
const item = items[key];
if (!item || item.deleted) {
callbackResult(cb, null);
} else {
callbackResult(cb, item);
}
} else {
callbackResult(cb, null);
}
};
store.all = (kind, cb) => {
const results = {};
const items = allData[kind.namespace] || {};
for (const [key, item] of Object.entries(items)) {
if (item && !item.deleted) {
results[key] = item;
}
}
callbackResult(cb, results);
};
store.init = (newData, cb) => {
allData = newData;
initCalled = true;
callbackResult(cb);
};
store.delete = (kind, key, version, cb) => {
let items = allData[kind.namespace];
if (!items) {
items = {};
allData[kind] = items;
}
const deletedItem = { version: version, deleted: true };
if (Object.hasOwnProperty.call(items, key)) {
const old = items[key];
if (!old || old.version < version) {
items[key] = deletedItem;
}
} else {
items[key] = deletedItem;
}
callbackResult(cb);
};
store.upsert = (kind, item, cb) => {
const key = item.key;
let items = allData[kind.namespace];
if (!items) {
items = {};
allData[kind.namespace] = items;
}
if (Object.hasOwnProperty.call(items, key)) {
const old = items[key];
if (old && old.version < item.version) {
items[key] = clone(item);
}
} else {
items[key] = clone(item);
}
callbackResult(cb);
};
store.initialized = cb => {
callbackResult(cb, initCalled === true);
};
store.close = () => {
// Close on the in-memory store is a no-op
};
store.description = 'memory';
return store;
}
// Deep clone an object. Does not preserve any
// functions on the object
function clone(obj) {
return JSON.parse(JSON.stringify(obj));
}
module.exports = InMemoryFeatureStore;