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
33 changes: 7 additions & 26 deletions .devcontainer/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,32 +1,13 @@
#-------------------------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information.
#-------------------------------------------------------------------------------------------------------------
FROM node:22
FROM node:22-alpine

# The node image includes a non-root user with sudo access. Use the
# "remoteUser" property in devcontainer.json to use it. On Linux, update
# these values to ensure the container user's UID/GID matches your local values.
# See https://aka.ms/vscode-remote/containers/non-root-user for details.
ARG USERNAME=node
ARG USER_UID=1000
ARG USER_GID=$USER_UID

RUN echo "deb http://archive.debian.org/debian stretch main" > /etc/apt/sources.list

# Configure apt and install packages
RUN apt-get update \
&& apt-get -y install --no-install-recommends dialog 2>&1 \
# Verify git and needed tools are installed
&& apt-get -y install git iproute2 procps \
# Update npm to the latest version
&& npm install -g npm@latest \
# Clean up
&& apt-get autoremove -y \
&& apt-get clean -y \
&& rm -rf /var/lib/apt/lists/*

# set up a nicer prompt
RUN git clone https://github.com/magicmonty/bash-git-prompt.git ~/.bash-git-prompt --depth=1

RUN echo "source $HOME/.bash-git-prompt/gitprompt.sh" >> ~/.bashrc
RUN apk update && \
apk add --no-cache \
ca-certificates \
curl \
git \
openssh
15 changes: 9 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,25 +65,28 @@ const snapshotLocation = './snapshot/';
const snapshotAutoUpdateInterval = 3;
const snapshotWatcher = true;
const silentMode = '5m';
const restrictRelay = true;
const certPath = './certs/ca.pem';

Client.buildContext({ url, apiKey, domain, component, environment }, {
local, logger, snapshotLocation, snapshotAutoUpdateInterval, snapshotWatcher, silentMode, certPath
local, logger, snapshotLocation, snapshotAutoUpdateInterval,
snapshotWatcher, silentMode, restrictRelay, certPath
});

const switcher = Client.getSwitcher();
```

- **local**: 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 Client.getLogger('KEY')
- **snapshotLocation**: Location of snapshot files. The default value is './snapshot/'
- **snapshotAutoUpdateInterval**: Enable Snapshot Auto Update given an interval in seconds (default: 0 disabled).
- **snapshotWatcher**: Enable Snapshot Watcher to monitor changes in the snapshot file (default: false).
- **snapshotLocation**: Location of snapshot files
- **snapshotAutoUpdateInterval**: Enable Snapshot Auto Update given an interval in seconds (default: 0 disabled)
- **snapshotWatcher**: Enable Snapshot Watcher to monitor changes in the snapshot file (default: false)
- **silentMode**: Enable contigency given the time for the client to retry - e.g. 5s (s: seconds - m: minutes - h: hours)
- **regexSafe**: Enable REGEX Safe mode - Prevent agaist reDOS attack (default: true).
- **restrictRelay**: Enable Relay Restriction - Allow managing Relay restrictions when running in local mode (default: true)
- **regexSafe**: Enable REGEX Safe mode - Prevent agaist reDOS attack (default: true)
- **regexMaxBlackList**: Number of entries cached when REGEX Strategy fails to perform (reDOS safe) - default: 50
- **regexMaxTimeLimit**: Time limit (ms) used by REGEX workers (reDOS safe) - default - 3000ms
- **certPath**: Path to the certificate file used to establish a secure connection with the API.
- **certPath**: Path to the certificate file used to establish a secure connection with the API

(*) regexSafe is a feature that prevents your application from being exposed to a reDOS attack. It is recommended to keep this feature enabled.<br>

Expand Down
11 changes: 6 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,12 @@
],
"license": "MIT",
"scripts": {
"lint": "eslint ./src/**/*.js ./test/**/*.js",
"test": "npm run coverage \"./test/**/*.test.js\"",
"test-local": "env-cmd npm run coverage \"./test/**/*.test.js\"",
"lint": "eslint ./src/**/*.js ./tests/**/*.js",
"test": "npm run coverage \"./tests/**/*.test.js\"",
"test-file": "env-cmd npm run coverage",
"test-local": "env-cmd npm run coverage \"./tests/**/*.test.js\"",
"coverage": "c8 --include='src/**/*.js' mocha",
"play": "env-cmd node ./test/playground/index.js"
"play": "env-cmd node ./tests/playground/index.js"
},
"files": [
"LICENSE",
Expand All @@ -37,7 +38,7 @@
"chai": "^5.2.0",
"env-cmd": "^10.1.0",
"eslint": "^9.29.0",
"mocha": "^11.7.0",
"mocha": "^11.7.1",
"mocha-sonarqube-reporter": "^1.0.2",
"sinon": "^21.0.0"
},
Expand Down
2 changes: 1 addition & 1 deletion sonar-project.properties
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ sonar.javascript.lcov.reportPaths=coverage/lcov.info

# Path is relative to the sonar-project.properties file. Replace "\" by "/" on Windows.
sonar.sources=src
sonar.tests=test
sonar.tests=tests
sonar.language=js
sonar.exclusions=src/**/*.d.ts, src/lib/utils/timed-match/match-proc.js

Expand Down
5 changes: 5 additions & 0 deletions src/client.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,11 @@ export type SwitcherOptions = {
*/
snapshotWatcher?: boolean;

/**
* Allow local snapshots to ignore or require Relay verification.
*/
restrictRelay?: boolean;

/**
* When defined it will switch to local during the specified time before it switches back to remote
* e.g. 5s (s: seconds - m: minutes - h: hours)
Expand Down
6 changes: 5 additions & 1 deletion src/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ export class Client {
const optionsHandler = {
[SWITCHER_OPTIONS.CERT_PATH]: (val) => val && remote.setCerts(val),
[SWITCHER_OPTIONS.SILENT_MODE]: (val) => val && this.#initSilentMode(val),
[SWITCHER_OPTIONS.RESTRICT_RELAY]: (val) => {
GlobalOptions.updateOptions({ restrictRelay: val });
},
[SWITCHER_OPTIONS.SNAPSHOT_AUTO_UPDATE_INTERVAL]: (val) => {
GlobalOptions.updateOptions({ snapshotAutoUpdateInterval: val });
this.scheduleSnapshotAutoUpdate();
Expand Down Expand Up @@ -97,7 +100,8 @@ export class Client {
}

static getSwitcher(key) {
return new Switcher(util.get(key, ''));
return new Switcher(util.get(key, ''))
.restrictRelay(GlobalOptions.restrictRelay);
}

static async checkSnapshot() {
Expand Down
1 change: 1 addition & 0 deletions src/lib/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export const SWITCHER_OPTIONS = Object.freeze({
SNAPSHOT_LOCATION: 'snapshotLocation',
SNAPSHOT_AUTO_UPDATE_INTERVAL: 'snapshotAutoUpdateInterval',
SNAPSHOT_WATCHER: 'snapshotWatcher',
RESTRICT_RELAY: 'restrictRelay',
SILENT_MODE: 'silentMode',
REGEX_SAFE: 'regexSafe',
REGEX_MAX_BLACK_LIST: 'regexMaxBlackList',
Expand Down
4 changes: 4 additions & 0 deletions src/lib/globals/globalOptions.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,8 @@ export class GlobalOptions {
static get silentMode() {
return this.#options.silentMode;
}

static get restrictRelay() {
return this.#options.restrictRelay;
}
}
1 change: 1 addition & 0 deletions src/lib/remote.js
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ export async function resolveSnapshot(domain, environment, component) {
group { name activated
config { key activated
strategies { strategy activated operation values }
relay { type activated }
components
}
}
Expand Down
8 changes: 8 additions & 0 deletions src/lib/resolver.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ async function checkConfig(group, config, switcher) {
throw new CriteriaFailed('Config disabled');
}

if (hasRelayEnabled(config) && switcher.isRelayRestricted) {
throw new CriteriaFailed(`Config '${config.key}' is restricted to relay`);
}

if (config.strategies) {
return await checkStrategy(config, util.get(switcher.input, []));
}
Expand Down Expand Up @@ -105,6 +109,10 @@ async function checkStrategyInput(entry, { strategy, operation, values }) {
}
}

function hasRelayEnabled(config) {
return config.relay?.activated;
}

export default async function checkCriteriaLocal(snapshot, switcher) {
if (!snapshot) {
throw new Error('Snapshot not loaded. Try to use \'Client.loadSnapshot()\'');
Expand Down
5 changes: 5 additions & 0 deletions src/switcher.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@ export class Switcher {
* Define a default result when the client enters in panic mode
*/
defaultResult(defaultResult: boolean): Switcher;

/**
* Allow local snapshots to ignore or require Relay verification.
*/
restrictRelay(restrict: boolean): Switcher;

/**
* Adds a strategy for validation
Expand Down
6 changes: 6 additions & 0 deletions src/switcherBuilder.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export class SwitcherBuilder {
_defaultResult;
_forceRemote = false;
_showDetail = false;
_restrictRelay = true;

constructor(key) {
this._key = key;
Expand Down Expand Up @@ -42,6 +43,11 @@ export class SwitcherBuilder {
this._defaultResult = defaultResult;
return this;
}

restrictRelay(restrict = true) {
this._restrictRelay = restrict;
return this;
}

check(startegyType, input) {
if (!this._input) {
Expand Down
7 changes: 7 additions & 0 deletions src/switcherRequest.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,11 @@ export class SwitcherRequest extends SwitcherBuilder {
get input() {
return this._input;
}

/**
* Return Relay restriction value
*/
get isRelayRestricted() {
return this._restrictRelay;
}
}
File renamed without changes.
File renamed without changes.
6 changes: 3 additions & 3 deletions test/playground/index.js → tests/playground/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const domain = 'Switcher API';
const component = 'switcher-client-js';
const environment = 'default';
const url = 'https://api.switcherapi.com';
const snapshotLocation = './test/playground/snapshot/';
const snapshotLocation = './tests/playground/snapshot/';

/**
* Playground environment for showcasing the API
Expand All @@ -23,14 +23,14 @@ async function setupSwitcher(local) {
* This code snippet is a minimal example of how to configure and use Switcher4Deno locally.
* No remote API account is required.
*
* Snapshot is loaded from file at test/playground/snapshot/local.json
* Snapshot is loaded from file at tests/playground/snapshot/local.json
*/
const _testLocal = async () => {
Client.buildContext({
domain: 'Local Playground',
environment: 'local'
}, {
snapshotLocation: './test/playground/snapshot/',
snapshotLocation: './tests/playground/snapshot/',
local: true
});

Expand Down
27 changes: 27 additions & 0 deletions test/snapshot/default.json → tests/snapshot/default.json
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,33 @@
"components": []
}
]
},
{
"name": "Relay test",
"description": "Relay group",
"activated": true,
"config": [
{
"key": "USECASE103",
"description": "Relay enabled",
"activated": true,
"relay": {
"type": "VALIDATOR",
"activated": true
},
"components": []
},
{
"key": "USECASE104",
"description": "Relay disabled",
"relay": {
"type": "VALIDATOR",
"activated": false
},
"activated": true,
"components": []
}
]
}
]
}
Expand Down
File renamed without changes.
File renamed without changes.
Loading