Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/node_modules
/generated-snapshots

*.idea
*.vscode
Expand Down
18 changes: 14 additions & 4 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,21 +48,31 @@ You can also activate features such as offline and silent mode:
```js
const offline = true;
const logger = true;
const snapshotAutoload = true;
const snapshotLocation = './snapshot/';
const silentMode = true;
const retryAfter = '5m';

let switcher = new Switcher(url, apiKey, domain, component, environment, {
offline, logger, snapshotLocation, silentMode, retryAfter
offline, logger, snapshotLocation, snapshotAutoload, silentMode, retryAfter
});
```

- **offline**: If activated, the client will only fetch the configuration inside your snapshot file. The default value is 'false'.
- **logger**: If activated, it is possible to retrieve the last results from a given Switcher key using Switcher.getLogger('KEY')
- **snapshotLocation**: Location of snapshot files. The default value is './snapshot/'.
- **snapshotAutload**: If activated, snapshot folder and files are going to be created automatically.
- **silentMode**: If activated, all connectivity issues will be ignored and the client will automatically fetch the configuration into your snapshot file.
- **retryAfter** : Time given to the module to re-establish connectivity with the API - e.g. 5s (s: seconds - m: minutes - h: hours).

## Pre-execution
Before you call the API, there is one single step you need to execute to complete the configuration.
If you are not running the API expecting to use the offline features, you can ignore this step.

After instantiating the Switcher, you need to load the snapshot engine to watch for changes in your Domain structure.
```js
await switcher.loadSnapshot();
```

## Executing
There are a few different ways to call the API using the JavaScript module.
Expand All @@ -77,10 +87,10 @@ await switcher.isItOn('FEATURE01');
```

2. **Promise**
Using promise is another way to call the API if you want, like:
Most functions were implemented using async operations. Here it is a differnet way to execute the criteria:

```js
switcher.isItOnPromise('KEY')
switcher.isItOn('KEY')
.then(result => console.log('Result:', result))
.catch(error => console.log(error));
```
Expand Down Expand Up @@ -119,4 +129,4 @@ For convenience, an implementation of a domain version checker is available if y

```js
switcher.checkSnapshot();
```
```
20 changes: 6 additions & 14 deletions src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,15 @@ declare class Switcher {
/**
* Validate the input provided to access the API
*/
validate(): void;
validate(): Promise<void>;

/**
* Pre-set input values before calling the API
*
* @param key
* @param input
*/
prepare(key: string, input?: string[]): void;
prepare(key: string, input?: string[]): Promise<void>;

/**
* Execute async criteria
Expand All @@ -40,27 +40,18 @@ declare class Switcher {
* @param input
* @param showReason
*/
isItOn(key?: string, input?: string[], showReason?: boolean): boolean;

/**
* Execute async criteria
*
* @param key
* @param input
* @param showReason
*/
isItOnPromise(key?: string, input?: string[], showReason?: boolean): Promise<boolean>;
isItOn(key?: string, input?: string[], showReason?: boolean): Promise<boolean>;

/**
* Read snapshot file locally and store in a parsed JSON object
*/
loadSnapshot(): void;
loadSnapshot(): Promise<void>;

/**
* Verifies if the current snapshot file is updated.
* Return true if an update has been made.
*/
checkSnapshot(): boolean;
checkSnapshot(): Promise<boolean>;

/**
* Remove snapshot from real-time update
Expand Down Expand Up @@ -93,6 +84,7 @@ declare interface SwitcherOptions {
offline: boolean;
logger: boolean;
snapshotLocation: string;
snapshotAutoload: string;
silentMode: boolean;
retryAfter: string;
}
Expand Down
27 changes: 15 additions & 12 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const DEFAULT_SNAPSHOT_LOCATION = './snapshot/';
const DEFAULT_RETRY_TIME = '5m';
const DEFAULT_OFFLINE = false;
const DEFAULT_LOGGER = false;
const DEFAULT_SNAPSHOT_AUTOLOAD = false;

class Switcher {

Expand All @@ -36,6 +37,10 @@ class Switcher {
this.snapshotLocation = options.snapshotLocation;
}

if ('snapshotAutoload' in options) {
this.snapshotAutoload = options.snapshotAutoload;
}

if ('silentMode' in options) {
this.silentMode = options.silentMode;
}
Expand All @@ -52,8 +57,6 @@ class Switcher {
this.retryDurationIn = DEFAULT_RETRY_TIME.charAt(1);
}
}

this.loadSnapshot();
}

async prepare(key, input) {
Expand Down Expand Up @@ -160,19 +163,19 @@ class Switcher {
return false;
}

isItOnPromise(key, input, showReason = false) {
return new Promise((resolve) => resolve(this.isItOn(key, input, showReason)));
}

loadSnapshot() {
async loadSnapshot() {
if (this.snapshotLocation) {
const snapshotFile = `${this.snapshotLocation}${this.environment}.json`;
this.snapshot = loadDomain(snapshotFile);
this.snapshot = loadDomain(this.snapshotLocation, this.environment, this.snapshotAutoload);

fs.unwatchFile(snapshotFile);
fs.watchFile(snapshotFile, (curr, prev) => {
this.snapshot = loadDomain(snapshotFile);
});
if (this.snapshot.data.domain.version == 0 && !this.offline) {
await this.checkSnapshot();
} else {
fs.unwatchFile(snapshotFile);
fs.watchFile(snapshotFile, (curr, prev) => {
this.snapshot = loadDomain(this.snapshotLocation, this.environment, this.snapshotAutoload);
});
}
}
}

Expand Down
14 changes: 12 additions & 2 deletions src/utils/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,19 @@ const fs = require('fs');
const moment = require('moment');
const IPCIDR = require('ip-cidr');

const loadDomain = (snapshotLocation) => {
const loadDomain = (snapshotLocation, environment, snapshotAutoload) => {
try {
const dataBuffer = fs.readFileSync(snapshotLocation);
let dataBuffer;
const snapshotFile = `${snapshotLocation}${environment}.json`;
if (fs.existsSync(snapshotFile)) {
dataBuffer = fs.readFileSync(snapshotFile);
} else if (snapshotAutoload) {
dataBuffer = '{ "data": { "domain": { "version": 0 } } }';
fs.mkdir(snapshotLocation, { recursive: true }, (err) => {});
fs.writeFileSync(snapshotFile, dataBuffer);
} else {
throw new Error();
}
const dataJSON = dataBuffer.toString();
return JSON.parse(dataJSON);
} catch (e) {
Expand Down
25 changes: 23 additions & 2 deletions test/playground/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ function setupSwitcher(offline) {
switcher = new Switcher(url, apiKey, domain, component, environment, {
offline, logger: true
});
switcher.loadSnapshot();
}

// Requires online API
Expand Down Expand Up @@ -47,7 +48,7 @@ const testAsyncCall = async () => {
let result = await switcher.isItOn('FEATURE2020');
console.log(result);

switcher.isItOnPromise('FEATURE2020')
switcher.isItOn('FEATURE2020')
.then(result => console.log('Promise result:', result))
.catch(error => console.log(error));

Expand Down Expand Up @@ -75,4 +76,24 @@ const testBypasser = async () => {
switcher.unloadSnapshot();
}

testSimpleAPICall();
// Requires online API
const testSnapshotAutoload = async () => {
const apiKey = '$2b$08$DYcg9NUcJouQkTtB6XxqeOQJ3DCprslij6Te.8aTF1Ds7y2sAvTjm'
const domain = 'My Domain'
const component = 'CustomerAPI'
const environment = 'generated'
const url = 'http://localhost:3000'

switcher = new Switcher(url, apiKey, domain, component, environment, {
snapshotAutoload: true
});

await switcher.loadSnapshot();

let result = await switcher.isItOn('FEATURE2020');
console.log(result);

switcher.unloadSnapshot();
}

testSnapshotAutoload();
Loading