Skip to content

Commit 859cbeb

Browse files
authored
Added Client.assume:when criteria to bypass Switcher results (#195)
1 parent ef301f8 commit 859cbeb

File tree

11 files changed

+204
-88
lines changed

11 files changed

+204
-88
lines changed

.github/workflows/master.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ jobs:
2929
SWITCHER_API_KEY: ${{ secrets.SWITCHER_API_KEY }}
3030

3131
- name: SonarCloud Scan
32-
uses: sonarsource/sonarcloud-github-action@master
32+
uses: sonarsource/sonarqube-scan-action@v4.1.0
3333
env:
3434
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
3535
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}

README.md

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ const apiKey = '[API_KEY]';
4646
const environment = 'default';
4747
const domain = 'My Domain';
4848
const component = 'MyApp';
49-
const url = 'https://switcherapi.com/api';
49+
const url = 'https://api.switcherapi.com';
5050
```
5151

5252
- **domain**: Domain name.
@@ -148,13 +148,13 @@ Client.subscribeNotifyError((error) => {
148148
6. **Hybrid mode**
149149
Forcing Switchers to resolve remotely can help you define exclusive features that cannot be resolved locally.
150150
This feature is ideal if you want to run the SDK in local mode but still want to resolve a specific switcher remotely.
151-
```ts
151+
```js
152152
const switcher = Client.getSwitcher();
153153
await switcher.remote().isItOn('FEATURE01');
154154
```
155155

156156
## Built-in mock feature
157-
You can also bypass your switcher configuration by invoking 'Client.assume'. This is perfect for your test code where you want to test both scenarios when the switcher is true and false.
157+
You can also bypass your switcher configuration by invoking 'Client.assume'. This is perfect for your test code where you want to validate both scenarios when the switcher is true and false.
158158

159159
```js
160160
Client.assume('FEATURE01').true();
@@ -166,6 +166,12 @@ switcher.isItOn('FEATURE01'); // Now, it's going to return the result retrieved
166166
Client.assume('FEATURE01').false().withMetadata({ message: 'Feature is disabled' }); // Include metadata to emulate Relay response
167167
const response = await switcher.detail().isItOn('FEATURE01'); // false
168168
console.log(response.metadata.message); // Feature is disabled
169+
170+
Client.assume('FEATURE01').true().when(StrategiesType.VALUE, 'USER_1');
171+
switcher.checkValue('USER_1').isItOn('FEATURE01'); // true when the value is 'USER_1'
172+
173+
Client.assume('FEATURE01').true().when(StrategiesType.NETWORK, ['USER_2', 'USER_3']);
174+
switcher.checkValue('USER_1').isItOn('FEATURE01'); // false as the value is not in the list
169175
```
170176

171177
**Enabling Test Mode**

package.json

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "switcher-client",
3-
"version": "4.1.2",
3+
"version": "4.2.0",
44
"description": "Client JS SDK for working with Switcher-API",
55
"main": "./switcher-client.js",
66
"type": "module",
@@ -30,14 +30,14 @@
3030
"src/"
3131
],
3232
"devDependencies": {
33-
"@babel/eslint-parser": "^7.25.8",
34-
"@typescript-eslint/eslint-plugin": "^8.10.0",
35-
"@typescript-eslint/parser": "^8.10.0",
33+
"@babel/eslint-parser": "^7.25.9",
34+
"@typescript-eslint/eslint-plugin": "^8.17.0",
35+
"@typescript-eslint/parser": "^8.17.0",
3636
"c8": "^10.1.2",
37-
"chai": "^5.1.1",
37+
"chai": "^5.1.2",
3838
"env-cmd": "^10.1.0",
39-
"eslint": "^9.13.0",
40-
"mocha": "^10.7.3",
39+
"eslint": "^9.16.0",
40+
"mocha": "^11.0.1",
4141
"mocha-sonarqube-reporter": "^1.0.2",
4242
"sinon": "^19.0.2"
4343
},

sonar-project.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
sonar.projectKey=switcherapi_switcher-client-master
22
sonar.projectName=switcher-client-js
33
sonar.organization=switcherapi
4-
sonar.projectVersion=4.1.2
4+
sonar.projectVersion=4.2.0
55
sonar.links.homepage=https://github.com/switcherapi/switcher-client-js
66

77
sonar.javascript.lcov.reportPaths=coverage/lcov.info

src/client.d.ts

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -123,8 +123,22 @@ export class Client {
123123
*/
124124
export type ResultDetail = {
125125
result: boolean;
126-
reason: string;
127-
metadata: any;
126+
reason: string | undefined;
127+
metadata: object | undefined;
128+
}
129+
130+
/**
131+
* Criteria defines the condition(s) to evaluate the switcher when using Client.assume(key)
132+
*/
133+
export type Criteria = {
134+
135+
/**
136+
* Add a new strategy/input to the criteria
137+
*
138+
* @param strategy (StrategiesType) value to be evaluated
139+
*/
140+
and(strategy: string, input: string | string[]): this;
141+
128142
}
129143

130144
/**
@@ -199,8 +213,15 @@ declare class Key {
199213
*/
200214
withMetadata(metadata: any): Key;
201215

216+
/**
217+
* Conditionally set result based on strategy
218+
*
219+
* @param strategy (StrategiesType) value to be evaluated
220+
*/
221+
when(strategy: string, input: string | string[]): Criteria;
222+
202223
/**
203224
* Return key response
204225
*/
205-
getResponse(): ResultDetail;
226+
getResponse(input?: string[][]): ResultDetail;
206227
}

src/lib/bypasser/criteria.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { StrategiesType } from '../snapshot.js';
2+
3+
/**
4+
* Criteria defines a set of conditions (when) that are used to evaluate the bypasser strategies
5+
*/
6+
export default class Criteria {
7+
#when;
8+
9+
constructor(strategy, input) {
10+
this.#when = new Map();
11+
this.#when.set(strategy, Array.isArray(input) ? input : [input]);
12+
}
13+
14+
/**
15+
* Add a new strategy/input to the criteria
16+
*/
17+
and(strategy, input) {
18+
if (Object.values(StrategiesType).filter((s) => s === strategy).length) {
19+
this.#when.set(strategy, Array.isArray(input) ? input : [input]);
20+
}
21+
22+
return this;
23+
}
24+
25+
getWhen() {
26+
return this.#when;
27+
}
28+
}

src/lib/bypasser/key.js

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
1+
import Criteria from './criteria.js';
2+
13
/**
24
* Key record used to store key response when bypassing criteria execution
35
*/
46
export default class Key {
5-
67
#key;
78
#result;
89
#reason;
910
#metadata;
10-
11+
#criteria;
12+
1113
constructor(key) {
1214
this.#key = key;
1315
this.#result = undefined;
@@ -41,6 +43,14 @@ export default class Key {
4143
return this;
4244
}
4345

46+
/**
47+
* Conditionally set result based on strategy
48+
*/
49+
when(strategy, input) {
50+
this.#criteria = new Criteria(strategy, input);
51+
return this.#criteria;
52+
}
53+
4454
/**
4555
* Return selected switcher name
4656
*/
@@ -51,12 +61,28 @@ export default class Key {
5161
/**
5262
* Return key response
5363
*/
54-
getResponse() {
64+
getResponse(input) {
65+
let result = this.#result;
66+
if (this.#criteria && input) {
67+
result = this.#getResultBasedOnCriteria(this.#criteria, input);
68+
}
69+
5570
return {
56-
key: this.#key,
57-
result: this.#result,
71+
result,
5872
reason: this.#reason,
59-
metadata: this.#metadata
73+
metadata: this.#metadata,
6074
};
6175
}
76+
77+
#getResultBasedOnCriteria(criteria, input) {
78+
for (const [strategyWhen, inputWhen] of criteria.getWhen()) {
79+
const entry = input.filter((e) => e[0] === strategyWhen);
80+
if (entry.length && !inputWhen.includes(entry[0][1])) {
81+
this.#reason = `Forced to ${!this.#result} when: [${inputWhen}] - input: ${entry[0][1]}`;
82+
return !this.#result;
83+
}
84+
}
85+
86+
return this.#result;
87+
}
6288
}

src/switcher.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ export class Switcher {
5454
// verify if query from Bypasser
5555
const bypassKey = Bypasser.searchBypassed(this.#key);
5656
if (bypassKey) {
57-
const response = bypassKey.getResponse();
57+
const response = bypassKey.getResponse(util.get(this.#input, []));
5858
return this.#showDetail ? response : response.result;
5959
}
6060

switcher-client.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,5 @@
1616
*/
1717

1818
export { Client, ResultDetail, SwitcherContext, SwitcherOptions } from './src/client.js';
19-
export { Switcher } from './src/switcher.js';
19+
export { Switcher } from './src/switcher.js';
20+
export { StrategiesType } from './src/lib/snapshot.js';

0 commit comments

Comments
 (0)