forked from launchdarkly/node-server-sdk
-
Notifications
You must be signed in to change notification settings - Fork 0
/
feature_store.js
117 lines (98 loc) · 3.11 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
var dataKind = require('./versioned_data_kind');
// 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. There is a
// Redis-backed implementation in RedisFeatureStore; for other options, see
// [https://docs.launchdarkly.com/v2.0/docs/using-a-persistent-feature-store].
//
// Additional implementations should use CachingStoreWrapper if possible.
var noop = function(){};
function InMemoryFeatureStore() {
var store = {allData:{}};
function callbackResult(cb, result) {
cb = cb || noop;
setTimeout(function() { cb(result); }, 0); // ensure this is dispatched asynchronously
}
store.get = function(kind, key, cb) {
var items = this.allData[kind.namespace] || {};
if (Object.hasOwnProperty.call(items, key)) {
var item = items[key];
if (!item || item.deleted) {
callbackResult(cb, null);
} else {
callbackResult(cb, clone(item));
}
} else {
callbackResult(cb, null);
}
}
store.all = function(kind, cb) {
var results = {};
var items = this.allData[kind.namespace] || {};
for (var key in items) {
if (Object.hasOwnProperty.call(items, key)) {
var item = items[key];
if (item && !item.deleted) {
results[key] = clone(item);
}
}
}
callbackResult(cb, results);
}
store.init = function(allData, cb) {
this.allData = allData;
this.initCalled = true;
callbackResult(cb);
}
store.delete = function(kind, key, version, cb) {
var items = this.allData[kind.namespace];
if (!items) {
items = {};
this.allData[kind] = items;
}
var deletedItem = { version: version, deleted: true };
if (Object.hasOwnProperty.call(items, key)) {
var old = items[key];
if (!old || old.version < version) {
items[key] = deletedItem;
}
} else {
items[key] = deletedItem;
}
callbackResult(cb);
}
store.upsert = function(kind, item, cb) {
var key = item.key;
var items = this.allData[kind.namespace];
if (!items) {
items = {};
this.allData[kind.namespace] = items;
}
if (Object.hasOwnProperty.call(items, key)) {
var old = items[key];
if (old && old.version < item.version) {
items[key] = item;
}
} else {
items[key] = item;
}
callbackResult(cb);
}
store.initialized = function(cb) {
callbackResult(cb, this.initCalled === true);
}
store.close = function() {
// Close on the in-memory store is a no-op
}
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;