Skip to content

Commit cea8ab8

Browse files
authored
Standardized callback for loadSnapshot and watchSnapshot (#189)
* Standardized callback for loadSnapshot and watchSnapshot * chore: fixed test async when success * Fixed scheduleSnapshotAutoUpdate callback
1 parent 7405a75 commit cea8ab8

File tree

8 files changed

+105
-76
lines changed

8 files changed

+105
-76
lines changed

README.md

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ https://github.com/switcherapi/switcher-api
4040
The context properties stores all information regarding connectivity.
4141

4242
```js
43-
import { Switcher } from 'switcher-client';
43+
import { Client } from 'switcher-client';
4444

4545
const apiKey = '[API_KEY]';
4646
const environment = 'default';
@@ -200,9 +200,10 @@ Client.loadSnapshot();
200200
Activate and monitor snapshot changes using this feature. Optionally, you can implement any action based on the callback response.
201201

202202
```js
203-
Client.watchSnapshot(
204-
() => console.log('In-memory snapshot updated'),
205-
(err) => console.log(err));
203+
Client.watchSnapshot({
204+
success: () => console.log('In-memory snapshot updated'),
205+
reject: (err) => console.log(err)
206+
});
206207
```
207208

208209
## Snapshot version check
@@ -217,5 +218,8 @@ You can also schedule a snapshot update using the method below.<br>
217218
It allows you to run the Client SDK in local mode (zero latency) and still have the snapshot updated automatically.
218219

219220
```js
220-
Client.scheduleSnapshotAutoUpdate(1 * 60 * 60 * 24); // 24 hours
221+
Client.scheduleSnapshotAutoUpdate(3000, {
222+
success: (updated) => console.log('Snapshot updated', updated),
223+
reject: (err) => console.log(err)
224+
});
221225
```

src/client.d.ts

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { Switcher } from './switcher';
55
*
66
* 1. Use Client.buildContext() to define the arguments to connect to the API.
77
* 2. Use Client.getSwitcher() to create a new instance of Switcher.
8-
* 3. Use the instance created to call isItOn to query the API.
8+
* 3. Use the instance created to call isItOn to execute criteria evaluation.
99
*/
1010
export class Client {
1111

@@ -22,11 +22,8 @@ export class Client {
2222

2323
/**
2424
* Read snapshot and load it into memory
25-
*
26-
* @param watchSnapshot when true, it will watch for snapshot file modifications
27-
* @param fetchRemote when true, it will initialize the snapshot from the API
2825
*/
29-
static loadSnapshot(watchSnapshot?: boolean, fetchRemote?: boolean): Promise<void>;
26+
static loadSnapshot(options?: LoadSnapshotOptions): Promise<void>;
3027

3128
/**
3229
* Verifies if the current snapshot file is updated.
@@ -42,8 +39,12 @@ export class Client {
4239
* building context
4340
*
4441
* @param interval in ms
42+
* @param success returns true if snapshot has been updated
4543
*/
46-
static scheduleSnapshotAutoUpdate(interval?: number, callback?: (updated: boolean, err: Error) => void): void;
44+
static scheduleSnapshotAutoUpdate(interval?: number, callback?: {
45+
success?: (updated: boolean) => void;
46+
reject?: (err: Error) => void;
47+
}): void;
4748

4849
/**
4950
* Terminates Snapshot Auto Update
@@ -64,7 +65,10 @@ export class Client {
6465
* @param success when snapshot has successfully updated
6566
* @param error when any error has thrown when attempting to load snapshot
6667
*/
67-
static watchSnapshot(success?: () => void, error?: (err: any) => void): void;
68+
static watchSnapshot(callback: {
69+
success?: () => void | Promise<void>;
70+
reject?: (err: Error) => void;
71+
}): void;
6872

6973
/**
7074
* Terminate watching snapshot files
@@ -160,6 +164,17 @@ export type SwitcherOptions = {
160164
certPath?: string;
161165
}
162166

167+
/**
168+
* LoadSnapshotOptions defines the options to load a snapshot.
169+
*
170+
* @param watchSnapshot when true, it will watch for snapshot file modifications
171+
* @param fetchRemote when true, it will initialize the snapshot from the API
172+
*/
173+
export type LoadSnapshotOptions = {
174+
watchSnapshot?: boolean;
175+
fetchRemote?: boolean;
176+
}
177+
163178
declare class Key {
164179

165180
constructor(key: string);

src/client.js

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -119,27 +119,29 @@ export class Client {
119119
return false;
120120
}
121121

122-
static async loadSnapshot(watchSnapshot = false, fetchRemote = false) {
122+
static async loadSnapshot(options = { fetchRemote: false, watchSnapshot: false }) {
123123
Client.#snapshot = loadDomain(
124124
util.get(Client.#options.snapshotLocation, ''),
125125
util.get(Client.#context.environment, DEFAULT_ENVIRONMENT)
126126
);
127127

128128
if (Client.#snapshot.data.domain.version == 0 &&
129-
(fetchRemote || !Client.#options.local)) {
129+
(options.fetchRemote || !Client.#options.local)) {
130130
await Client.checkSnapshot();
131131
}
132132

133-
if (watchSnapshot) {
133+
if (options.watchSnapshot) {
134134
Client.watchSnapshot();
135135
}
136136

137137
return Client.#snapshot?.data.domain.version || 0;
138138
}
139139

140-
static watchSnapshot(success = () => {}, error = () => {}) {
140+
static watchSnapshot(callback = {}) {
141+
const { success = () => {}, reject = () => {} } = callback;
142+
141143
if (Client.testEnabled || !Client.#options.snapshotLocation?.length) {
142-
return error(new Error('Watch Snapshot cannot be used in test mode or without a snapshot location'));
144+
return reject(new Error('Watch Snapshot cannot be used in test mode or without a snapshot location'));
143145
}
144146

145147
const snapshotFile = `${Client.#options.snapshotLocation}/${Client.#context.environment}.json`;
@@ -151,7 +153,7 @@ export class Client {
151153
success();
152154
}
153155
} catch (e) {
154-
error(e);
156+
reject(e);
155157
} finally {
156158
lastUpdate = listener.ctime;
157159
}
@@ -168,13 +170,20 @@ export class Client {
168170
unwatchFile(snapshotFile);
169171
}
170172

171-
static scheduleSnapshotAutoUpdate(interval, callback) {
173+
static scheduleSnapshotAutoUpdate(interval, callback = {}) {
174+
const { success = () => {}, reject = () => {} } = callback;
175+
172176
if (interval) {
173177
Client.#options.snapshotAutoUpdateInterval = interval;
174178
}
175179

176180
if (Client.#options.snapshotAutoUpdateInterval && Client.#options.snapshotAutoUpdateInterval > 0) {
177-
SnapshotAutoUpdater.schedule(Client.#options.snapshotAutoUpdateInterval, this.checkSnapshot, callback);
181+
SnapshotAutoUpdater.schedule(
182+
Client.#options.snapshotAutoUpdateInterval,
183+
this.checkSnapshot,
184+
success,
185+
reject
186+
);
178187
}
179188
}
180189

src/lib/bypasser/key.js

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,62 @@
11
/**
2-
* Type definition for Switcher Keys which are used to mock results
2+
* Key record used to store key response when bypassing criteria execution
33
*/
44
export default class Key {
5+
6+
#key;
7+
#result;
8+
#reason;
9+
#metadata;
510

611
constructor(key) {
7-
this._key = key;
8-
this._result = undefined;
9-
this._reason = undefined;
10-
this._metadata = undefined;
12+
this.#key = key;
13+
this.#result = undefined;
14+
this.#reason = undefined;
15+
this.#metadata = undefined;
1116
}
1217

1318
/**
1419
* Force result to true
1520
*/
1621
true() {
17-
this._result = true;
18-
this._reason = 'Forced to true';
22+
this.#result = true;
23+
this.#reason = 'Forced to true';
1924
return this;
2025
}
2126

2227
/**
2328
* Force result to false
2429
*/
2530
false() {
26-
this._result = false;
27-
this._reason = 'Forced to false';
31+
this.#result = false;
32+
this.#reason = 'Forced to false';
2833
return this;
2934
}
3035

3136
/**
3237
* Define metadata for the response
3338
*/
3439
withMetadata(metadata) {
35-
this._metadata = metadata;
40+
this.#metadata = metadata;
3641
return this;
3742
}
3843

3944
/**
4045
* Return selected switcher name
4146
*/
4247
getKey() {
43-
return this._key;
48+
return this.#key;
4449
}
4550

4651
/**
4752
* Return key response
4853
*/
4954
getResponse() {
5055
return {
51-
key: this._key,
52-
result: this._result,
53-
reason: this._reason,
54-
metadata: this._metadata
56+
key: this.#key,
57+
result: this.#result,
58+
reason: this.#reason,
59+
metadata: this.#metadata
5560
};
5661
}
5762
}

src/lib/utils/snapshotAutoUpdater.js

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,18 @@
11
export default class SnapshotAutoUpdater {
22
static _worker = undefined;
33

4-
static schedule(interval, checkSnapshot, callback) {
4+
static schedule(interval, checkSnapshot, success, reject) {
55
if (this._worker) {
66
this.terminate();
77
}
88

99
this._worker = setInterval(async () => {
1010
try {
1111
const updated = await checkSnapshot();
12-
if (callback) {
13-
callback(updated);
14-
}
12+
success(updated);
1513
} catch (err) {
16-
if (callback) {
17-
this.terminate();
18-
callback(null, err);
19-
}
14+
this.terminate();
15+
reject(err);
2016
}
2117
}, interval * 1000);
2218
}

test/playground/index.js

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ const snapshotLocation = './test/playground/snapshot/';
1717
*/
1818
async function setupSwitcher(local) {
1919
Client.buildContext({ url, apiKey, domain, component, environment }, { local, logger: true });
20-
await Client.loadSnapshot(false, local)
20+
await Client.loadSnapshot({ watchSnapshot: false, fetchRemote: local })
2121
.then(version => console.log('Snapshot loaded - version:', version))
2222
.catch(() => console.log('Failed to load Snapshot'));
2323
}
@@ -131,28 +131,30 @@ const _testBypasser = async () => {
131131
// Requires remote API
132132
const _testWatchSnapshot = async () => {
133133
Client.buildContext({ url, apiKey, domain, component, environment }, { snapshotLocation, local: true, logger: true });
134-
await Client.loadSnapshot(false, true)
134+
await Client.loadSnapshot({ watchSnapshot: false, fetchRemote: true })
135135
.then(() => console.log('Snapshot loaded'))
136136
.catch(() => console.log('Failed to load Snapshot'));
137137

138138
const switcher = Client.getSwitcher();
139-
140-
Client.watchSnapshot(
141-
async () => console.log('In-memory snapshot updated', await switcher.isItOn(SWITCHER_KEY)),
142-
(err) => console.log(err));
139+
140+
Client.watchSnapshot({
141+
success: async () => console.log('In-memory snapshot updated', await switcher.isItOn(SWITCHER_KEY)),
142+
reject: (err) => console.log(err)
143+
});
143144
};
144145

145146
// Requires remote API
146147
const _testSnapshotAutoUpdate = async () => {
147148
Client.buildContext({ url, apiKey, domain, component, environment },
148149
{ local: true, logger: true });
149150

150-
await Client.loadSnapshot(false, true);
151+
await Client.loadSnapshot({ watchSnapshot: false, fetchRemote: true });
151152
const switcher = Client.getSwitcher();
152153

153-
Client.scheduleSnapshotAutoUpdate(3,
154-
(updated) => console.log('In-memory snapshot updated', updated),
155-
(err) => console.log(err));
154+
Client.scheduleSnapshotAutoUpdate(1, {
155+
success: (updated) => console.log('In-memory snapshot updated', updated),
156+
reject: (err) => console.log(err)
157+
});
156158

157159
setInterval(async () => {
158160
const time = Date.now();

test/switcher-snapshot.test.js

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ describe('E2E test - Switcher local - Snapshot:', function () {
5656
regexSafe: false
5757
});
5858

59-
await Client.loadSnapshot(true);
59+
await Client.loadSnapshot({ watchSnapshot: true });
6060
assert.isTrue(await Client.checkSnapshot());
6161

6262
//restore state to avoid process leakage
@@ -79,7 +79,7 @@ describe('E2E test - Switcher local - Snapshot:', function () {
7979
regexSafe: false
8080
});
8181

82-
await Client.loadSnapshot(true);
82+
await Client.loadSnapshot({ watchSnapshot: true });
8383
assert.isTrue(await Client.checkSnapshot());
8484
assert.isTrue(existsSync(`generated-snapshots/${environment}.json`));
8585

@@ -102,7 +102,7 @@ describe('E2E test - Switcher local - Snapshot:', function () {
102102
regexSafe: false
103103
});
104104

105-
await Client.loadSnapshot(true, true);
105+
await Client.loadSnapshot({ watchSnapshot: true, fetchRemote: true });
106106
assert.isTrue(existsSync(`generated-snapshots/${environment}.json`));
107107

108108
//restore state to avoid process leakage
@@ -328,13 +328,11 @@ describe('E2E test - Snapshot AutoUpdater:', function () {
328328
});
329329

330330
let snapshotUpdated = false;
331-
Client.scheduleSnapshotAutoUpdate(1, (updated) => {
332-
if (updated != undefined) {
333-
snapshotUpdated = updated;
334-
}
331+
Client.scheduleSnapshotAutoUpdate(1, {
332+
success: (updated) => snapshotUpdated = updated
335333
});
336334

337-
await Client.loadSnapshot(false, true);
335+
await Client.loadSnapshot({ watchSnapshot: false, fetchRemote: true });
338336

339337
const switcher = Client.getSwitcher();
340338
assert.isFalse(await switcher.isItOn('FF2FOR2030'));
@@ -361,13 +359,11 @@ describe('E2E test - Snapshot AutoUpdater:', function () {
361359
});
362360

363361
let error;
364-
Client.scheduleSnapshotAutoUpdate(1, (updated, err) => {
365-
if (err != undefined) {
366-
error = err;
367-
}
362+
Client.scheduleSnapshotAutoUpdate(1, {
363+
reject: (err) => error = err
368364
});
369365

370-
await Client.loadSnapshot(false, true);
366+
await Client.loadSnapshot({ watchSnapshot: false, fetchRemote: true });
371367

372368
//next call will fail
373369
givenError(fetchStub, 3, { errno: 'ECONNREFUSED' });

0 commit comments

Comments
 (0)