fastify-feature-flags
Advanced tools
Comparing version 1.0.1 to 1.1.0
@@ -5,2 +5,18 @@ # Change Log | ||
# [1.1.0](https://gitlab.com/m03geek/fastify-feature-flags/compare/v1.0.1...v1.1.0) (2019-04-25) | ||
### Bug Fixes | ||
* **provider:** support sitings in config provider ([671b883](https://gitlab.com/m03geek/fastify-feature-flags/commit/671b883)) | ||
### Features | ||
* **provider:** add env provider ([25b11c5](https://gitlab.com/m03geek/fastify-feature-flags/commit/25b11c5)) | ||
* **provider:** add new providers ([f41d897](https://gitlab.com/m03geek/fastify-feature-flags/commit/f41d897)) | ||
* **provider:** add unleash provider ([943fc1f](https://gitlab.com/m03geek/fastify-feature-flags/commit/943fc1f)) | ||
## [1.0.1](https://gitlab.com/m03geek/fastify-feature-flags/compare/v1.0.0...v1.0.1) (2019-04-23) | ||
@@ -7,0 +23,0 @@ |
@@ -5,4 +5,4 @@ /// <reference types="node" /> | ||
import { GenericProvider } from './providers/generic'; | ||
declare const pluginName = "featureFlags"; | ||
declare namespace FastifyFeatureSwitchPlugin { | ||
declare const PLUGIN_NAME = "featureFlags"; | ||
declare namespace FastifyFeatureFlagsPlugin { | ||
interface Plugin { | ||
@@ -17,5 +17,5 @@ providers: { | ||
*/ | ||
isEnabled(feature: string): Promise<boolean>; | ||
isEnabled(feature: string, context?: any): Promise<boolean>; | ||
/** | ||
* Check if feature is enabled | ||
* Check if feature is enabled and throws exception if not | ||
* @param feature feature name | ||
@@ -25,6 +25,7 @@ * @throws Error if feature is not enabled | ||
*/ | ||
checkEnabled(feature: string): void; | ||
checkEnabled(feature: string, context?: any): void; | ||
} | ||
interface Options { | ||
providers: Array<GenericProvider>; | ||
errorCode: number; | ||
} | ||
@@ -37,6 +38,6 @@ } | ||
*/ | ||
[pluginName]: FastifyFeatureSwitchPlugin.Plugin; | ||
[PLUGIN_NAME]: FastifyFeatureFlagsPlugin.Plugin; | ||
} | ||
} | ||
declare const FastifyFeatureSwitchPlugin: Plugin<http.Server, http.IncomingMessage, http.ServerResponse, FastifyFeatureSwitchPlugin.Options>; | ||
export = FastifyFeatureSwitchPlugin; | ||
declare const FastifyFeatureFlagsPlugin: Plugin<http.Server, http.IncomingMessage, http.ServerResponse, FastifyFeatureFlagsPlugin.Options>; | ||
export = FastifyFeatureFlagsPlugin; |
@@ -7,11 +7,20 @@ "use strict"; | ||
const config_1 = require("./providers/config"); | ||
const pluginName = 'featureFlags'; | ||
const fastifyFeatureSwitch = async function eventlogClient(fastify, options) { | ||
const { providers } = options; | ||
const isEnabled = async (feature) => { | ||
const result = await Promise.all(providers.map((provider) => provider.isEnabled(feature))); | ||
const env_1 = require("./providers/env"); | ||
const unleash_1 = require("./providers/unleash"); | ||
const feature_error_1 = require("./feature-error"); | ||
const PLUGIN_NAME = 'featureFlags'; | ||
const DEFAULT_ERROR_CODE = 500; | ||
const fastifyFeatureSwitch = async function featureFlags(fastify, options) { | ||
const { providers, errorCode = DEFAULT_ERROR_CODE } = options; | ||
const isEnabled = async (feature, context) => { | ||
const result = await Promise.all(providers.map((provider) => provider.isEnabled(feature, context))); | ||
return result.reduce((prev, curr) => prev && curr); | ||
}; | ||
const checkEnabled = async (feature) => { | ||
await Promise.all(providers.map((provider) => provider.checkEnabled(feature))); | ||
const checkEnabled = async (feature, context) => { | ||
const result = await isEnabled(feature, context); | ||
if (!result) { | ||
const err = new feature_error_1.FeatureError('Feature disabled'); | ||
err.statusCode = errorCode; | ||
throw err; | ||
} | ||
}; | ||
@@ -21,2 +30,4 @@ const plugin = { | ||
ConfigProvider: config_1.ConfigProvider, | ||
EnvProvider: env_1.EnvProvider, | ||
UnleashProvider: unleash_1.UnleashProvider, | ||
}, | ||
@@ -26,9 +37,9 @@ isEnabled, | ||
}; | ||
fastify.decorate(pluginName, plugin); | ||
fastify.decorate(PLUGIN_NAME, plugin); | ||
}; | ||
const FastifyFeatureSwitchPlugin = fastify_plugin_1.default(fastifyFeatureSwitch, { | ||
const FastifyFeatureFlagsPlugin = fastify_plugin_1.default(fastifyFeatureSwitch, { | ||
fastify: '>=1.0.0', | ||
name: 'fastify-feature-switch', | ||
name: 'fastify-feature-flags', | ||
}); | ||
module.exports = FastifyFeatureSwitchPlugin; | ||
module.exports = FastifyFeatureFlagsPlugin; | ||
//# sourceMappingURL=index.js.map |
import { GenericProvider } from './generic'; | ||
export declare namespace ConfigProvider { | ||
interface Options { | ||
path: string; | ||
interface Options extends GenericProvider.Options { | ||
/** | ||
* Config prefix for flags | ||
*/ | ||
prefix: string; | ||
} | ||
} | ||
export declare class ConfigProvider extends GenericProvider { | ||
private conf; | ||
constructor(options: ConfigProvider.Options); | ||
isEnabled(feature: string): Promise<boolean>; | ||
} |
@@ -11,5 +11,8 @@ "use strict"; | ||
super(options); | ||
this.conf = config_1.default.get(`${options.prefix}`); | ||
} | ||
async isEnabled(feature) { | ||
return Boolean(config_1.default.get(`${this.options.path}.${feature}`)); | ||
return (this.conf[feature] === 'true' || | ||
this.conf[feature] === true || | ||
this.conf[feature] === '1'); | ||
} | ||
@@ -16,0 +19,0 @@ } |
@@ -1,21 +0,19 @@ | ||
export { FeatureError } from '../feature-error'; | ||
import fastify from 'fastify'; | ||
export declare namespace GenericProvider { | ||
interface Options { | ||
fastify: fastify.FastifyInstance; | ||
} | ||
} | ||
export declare abstract class GenericProvider { | ||
protected options: any; | ||
protected options: GenericProvider.Options; | ||
/** | ||
* @param options Provider options | ||
*/ | ||
constructor(options?: any); | ||
constructor(options: GenericProvider.Options); | ||
/** | ||
* Checks if feature is enabled | ||
* @param _feature feature name | ||
* @param _args any other options | ||
* @param _context context used in feature check | ||
*/ | ||
isEnabled(_feature: string, ..._args: Array<any>): Promise<boolean>; | ||
/** | ||
* Checks if feature is enabled | ||
* @param feature feature name | ||
* @param args any other options | ||
* @throws FeatureError | ||
*/ | ||
checkEnabled(feature: string, ...args: Array<any>): Promise<void>; | ||
isEnabled(_feature: string, _context?: any): Promise<boolean>; | ||
} |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const feature_error_1 = require("../feature-error"); | ||
var feature_error_2 = require("../feature-error"); | ||
exports.FeatureError = feature_error_2.FeatureError; | ||
class GenericProvider { | ||
@@ -16,22 +13,9 @@ /** | ||
* @param _feature feature name | ||
* @param _args any other options | ||
* @param _context context used in feature check | ||
*/ | ||
async isEnabled(_feature, ..._args) { | ||
async isEnabled(_feature, _context) { | ||
return false; | ||
} | ||
/** | ||
* Checks if feature is enabled | ||
* @param feature feature name | ||
* @param args any other options | ||
* @throws FeatureError | ||
*/ | ||
async checkEnabled(feature, ...args) { | ||
const result = await this.isEnabled(feature, ...args); | ||
if (!result) { | ||
throw new feature_error_1.FeatureError('Feature disabled'); | ||
} | ||
return; | ||
} | ||
} | ||
exports.GenericProvider = GenericProvider; | ||
//# sourceMappingURL=generic.js.map |
{ | ||
"name": "fastify-feature-flags", | ||
"version": "1.0.1", | ||
"version": "1.1.0", | ||
"description": "Fastify feature flags plugin", | ||
@@ -47,3 +47,9 @@ "main": "dist/index.js", | ||
}, | ||
"keywords": [], | ||
"keywords": [ | ||
"feature", | ||
"toggle", | ||
"feature toggle", | ||
"fastify", | ||
"unleash" | ||
], | ||
"devDependencies": { | ||
@@ -65,8 +71,9 @@ "@types/config": "0.0.34", | ||
"typedoc-plugin-markdown": "^1.1.27", | ||
"typescript": "^3.3.4000" | ||
"typescript": "^3.4.5" | ||
}, | ||
"dependencies": { | ||
"config": "^3.1.0", | ||
"fastify-plugin": "^1.5.0" | ||
"fastify-plugin": "^1.5.0", | ||
"unleash-client": "^3.2.3" | ||
} | ||
} |
114
README.md
@@ -12,2 +12,4 @@ # fastify-feature-flags | ||
This plugin is currently in **beta**, so some bugs can appear. Feel free to create an issue and I'll try to fix them asap. | ||
## ToC | ||
@@ -20,2 +22,8 @@ - [fastify-feature-flags](#fastify-feature-flags) | ||
- [Usage](#usage) | ||
- [Providers](#providers) | ||
- [Generic provider](#generic-provider) | ||
- [Config provider](#config-provider) | ||
- [Env provider](#env-provider) | ||
- [Unleash provider](#unleash-provider) | ||
- [Using plugin](#using-plugin) | ||
- [Docs](#docs) | ||
@@ -59,6 +67,110 @@ - [Changelog](#changelog) | ||
Plugin adds an object with built-in providers and generic provider interface that you can extend. For checking features availability it adds two methods: `fastify.featureFlags.isEnabled` which returns `true` or `false` and `fastify.featureFlags.checkEnabled` which throws an error if feature is disabled. | ||
Plugin adds an object with built-in providers and generic provider interface that you can extend. For checking features availability it adds two methods: `fastify.featureFlags.isEnabled` which returns `true` or `false` and `fastify.featureFlags.checkEnabled` which throws an error if feature is disabled. The list of built-in providers is available below. | ||
<sub>[Back to top](#toc)</sub> | ||
### Providers | ||
#### Generic provider | ||
Generic provider is an abstract class that you may extend to add new providers. It should have `isEnabled` method that consumes feature name and context (optionally) and returns `true` or `false`. | ||
#### Config provider | ||
Reads feature flags from specified config section. Depends on [`config`](https://www.npmjs.com/package/config) module. It's constuctor consumes options object that contains `prefix` for config section where features are defined. | ||
Example: | ||
`default.js` (in config directory): | ||
```js | ||
module.exports = { | ||
features: { | ||
a: true, | ||
b: false, | ||
} | ||
} | ||
``` | ||
Configuring provider: | ||
```js | ||
const provider = new ffPlugin.ConfigProvider({ | ||
prefix: 'features', | ||
}) | ||
``` | ||
Valid config values for feature to be enabled are: `true`, `"true"` or `"1"`. Last two may be useful if you're using config module with env overrides. | ||
#### Env provider | ||
Reads feature flags from env variables. It's constuctor consumes options object that may contain `prefix` for filtering env variables containing features. | ||
Example: | ||
`default.js` (in config directory): | ||
```.env | ||
FEATURE_A = true | ||
FEATURE_B = false | ||
``` | ||
Configuring provider: | ||
```js | ||
const provider = new ffPlugin.EnvProvider({ | ||
prefix: 'FEATURE_', | ||
}) | ||
``` | ||
Valid config values for feature to be enabled are: `"true"` or `"1"`. | ||
#### Unleash provider | ||
This provider relies on feature flags service [Unleash](https://github.com/Unleash/unleash). You may configure it separately, see instructions in their repo. | ||
Example: | ||
Configuring provider: | ||
```js | ||
const provider = new ffPlugin.UnleashProvider({ | ||
appName: 'my-fastify-app'; | ||
url: 'https://unleash.example.com'; | ||
}) | ||
``` | ||
For more options please refer to [unleash docs](https://github.com/unleash/unleash-client-node#advanced-usage) | ||
### Using plugin | ||
After configuring providers and registering the plugin in your fastify app you can use `isEnabled` or `checkEnabled` methods. | ||
You may also specify multiple providers, then the feature will be enabled only when it will be enabled in **all** providers. | ||
Example: | ||
```js | ||
const fastify = require('fastify')(); | ||
const ffPlugin = require('fastify-feature-flags'); | ||
fastify.register(ffPlugin, { | ||
providers: [new ffPlugin.EnvProvider({prefix: 'FEATURE_'})] | ||
}); | ||
fastify.get('/a', async (request, reply) => { | ||
await fastify.featureFlags.checkEnabled('A'); | ||
reply.type('application/json').code(200); | ||
return { a: 'enabled' }; | ||
}); | ||
fastify.get('/b', async (request, reply) => { | ||
const isEnabled = await fastify.featureFlags.isEnabled('B'); | ||
reply.type('application/json').code(200); | ||
return { b: isEnabled }; | ||
}); | ||
(async () => { | ||
await fastify.ready(); | ||
await fastify.listen(3000); | ||
})(); | ||
``` | ||
<sub>[Back to top](#toc)</sub> | ||
## Docs | ||
@@ -65,0 +177,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 2 instances in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
18532
16
236
196
3
3
+ Addedunleash-client@^3.2.3
+ Added@gar/promisify@1.1.3(transitive)
+ Added@npmcli/fs@2.1.2(transitive)
+ Added@npmcli/move-file@2.0.1(transitive)
+ Added@tootallnate/once@2.0.0(transitive)
+ Addedagent-base@6.0.2(transitive)
+ Addedagentkeepalive@4.5.0(transitive)
+ Addedaggregate-error@3.1.0(transitive)
+ Addedbalanced-match@1.0.2(transitive)
+ Addedbrace-expansion@1.1.112.0.1(transitive)
+ Addedcacache@16.1.3(transitive)
+ Addedchownr@2.0.0(transitive)
+ Addedclean-stack@2.2.0(transitive)
+ Addedconcat-map@0.0.1(transitive)
+ Addeddebug@4.3.7(transitive)
+ Addedencoding@0.1.13(transitive)
+ Addederr-code@2.0.3(transitive)
+ Addedfs-minipass@2.1.0(transitive)
+ Addedfs.realpath@1.0.0(transitive)
+ Addedglob@7.2.38.1.0(transitive)
+ Addedhttp-cache-semantics@4.1.1(transitive)
+ Addedhttp-proxy-agent@5.0.0(transitive)
+ Addedhttps-proxy-agent@5.0.1(transitive)
+ Addedhumanize-ms@1.2.1(transitive)
+ Addediconv-lite@0.6.3(transitive)
+ Addedimurmurhash@0.1.4(transitive)
+ Addedindent-string@4.0.0(transitive)
+ Addedinfer-owner@1.0.4(transitive)
+ Addedinflight@1.0.6(transitive)
+ Addedinherits@2.0.4(transitive)
+ Addedip@1.1.9(transitive)
+ Addedip-address@9.0.5(transitive)
+ Addedis-lambda@1.0.1(transitive)
+ Addedjsbn@1.1.0(transitive)
+ Addedlru-cache@7.18.3(transitive)
+ Addedmake-fetch-happen@10.2.1(transitive)
+ Addedminimatch@3.1.25.1.6(transitive)
+ Addedminipass@3.3.65.0.0(transitive)
+ Addedminipass-collect@1.0.2(transitive)
+ Addedminipass-fetch@2.1.2(transitive)
+ Addedminipass-flush@1.0.5(transitive)
+ Addedminipass-pipeline@1.2.4(transitive)
+ Addedminipass-sized@1.0.3(transitive)
+ Addedminizlib@2.1.2(transitive)
+ Addedmkdirp@1.0.4(transitive)
+ Addedms@2.1.3(transitive)
+ Addedmurmurhash3js@3.0.1(transitive)
+ Addednegotiator@0.6.4(transitive)
+ Addedonce@1.4.0(transitive)
+ Addedp-map@4.0.0(transitive)
+ Addedpath-is-absolute@1.0.1(transitive)
+ Addedpromise-inflight@1.0.1(transitive)
+ Addedpromise-retry@2.0.1(transitive)
+ Addedretry@0.12.0(transitive)
+ Addedrimraf@3.0.2(transitive)
+ Addedsafer-buffer@2.1.2(transitive)
+ Addedsemver@7.6.3(transitive)
+ Addedsmart-buffer@4.2.0(transitive)
+ Addedsocks@2.8.3(transitive)
+ Addedsocks-proxy-agent@7.0.0(transitive)
+ Addedsprintf-js@1.1.3(transitive)
+ Addedssri@9.0.1(transitive)
+ Addedtar@6.2.1(transitive)
+ Addedunique-filename@2.0.1(transitive)
+ Addedunique-slug@3.0.0(transitive)
+ Addedunleash-client@3.21.0(transitive)
+ Addedwrappy@1.0.2(transitive)
+ Addedyallist@4.0.0(transitive)