Comparing version 2.4.1 to 2.5.0
# EnvironmentUtils | ||
A really small service to centralize the place where you read environment variables and check if you are running on development or production. | ||
A really small service to centralize the place where you read and write environment variables, and check if you are running on development or production. | ||
Is not uncommon nowadays for Node apps to be checking `NODE_ENV` and other environment variables in order to do or not to do some stuff, and having multiple calls to `process.env` on different places of your app may not be a good idea: It's hard to track and maintain. | ||
Is not uncommon nowadays for Node apps to be checking `NODE_ENV` and other environment variables in order to do or not to do certain things, and having multiple calls to `process.env` on different places of your app may not be a good idea: It's hard to track and maintain. | ||
## Example | ||
## Examples | ||
### Read | ||
Let's say your code looks something like this: | ||
@@ -21,3 +23,3 @@ | ||
### Without Jimple | ||
#### Without Jimple | ||
@@ -37,3 +39,3 @@ > If you haven't tried [Jimple](https://github.com/fjorgemota/jimple), give it a try, it's excellent for organizing your app dependencies and services. | ||
```js | ||
if (environmentUtils.development()) { | ||
if (environmentUtils.development) { | ||
addSomeStuffForDevelopment(); | ||
@@ -48,3 +50,3 @@ } | ||
### With Jimple | ||
#### With Jimple | ||
@@ -77,2 +79,60 @@ Let's setup a dummy app and register the service: | ||
### Write | ||
Ok, writing on the environment from inside an application is not that common as reading, but there are certain cases where this may come in handy. | ||
Let's say you are using a library that has a _"debug mode"_ but the only way to enable it is using a environment variable, and for "purposes of debugging", you want to do it from your code: | ||
```js | ||
process.env.DEBUG_MY_LIBRARY = 'true'; | ||
runMyMagicLibrary(); | ||
``` | ||
Like for `get`, `set` also allows you to centralize where you overwrite your environment variables, but at the same time, it protects you from overwriting something that is already declared: Unless you tell `set` to overwrite declarations, if the variable already exists, it won't do it. | ||
#### Without Jimple | ||
Let's start with the setup: | ||
```js | ||
const { EnvironmentUtils } = require('wootils/node'); | ||
// Construct an instance. | ||
const environmentUtils = new EnvironmentUtils(); | ||
``` | ||
Now, to update the code: | ||
```js | ||
// If you add a third parameter with `true`, it will overwrite any previous declaration. | ||
const name = environmentUtils.set('DEBUG_MY_LIBRARY', 'true'); | ||
runMyMagicLibrary(); | ||
``` | ||
Done! Now you are not manually updating the environment variable and potentially overwriting something that was already declared. | ||
#### With Jimple | ||
Let's setup a dummy app and register the service: | ||
```js | ||
// Import all the required modules | ||
const Jimple = require('jimple'); | ||
const { environmentUtils } = require('wootils/node/providers'); | ||
// Create a dummy app | ||
const app = new Jimple(); | ||
app.register(environmentUtils); | ||
``` | ||
Now, to update the code: | ||
```js | ||
// The imported provider has the same name, that's why I called it `envUtils`. | ||
const envUtils = app.get('environmentUtils'); | ||
// If you add a third parameter with `true`, it will overwrite any previous declaration. | ||
const name = envUtils.set('DEBUG_MY_LIBRARY', 'true'); | ||
runMyMagicLibrary(); | ||
``` | ||
Done! Now you are not manually updating the environment variable and potentially overwriting something that was already declared. | ||
## Features | ||
@@ -82,6 +142,14 @@ | ||
This was demonstrated on the example above. You just need to `.get()` with the name of the variable you want to read. | ||
This was demonstrated on the first example. You just need to `.get()` with the name of the variable you want to read. | ||
### Checking the environment | ||
### Writing variables | ||
This was demostrated on the second example. You just need to `.set()` with the name and the value for the variable. | ||
### Validating variables | ||
You can call `.exists()` with the name of the variable and the service will tell you if it's defined on the environment. | ||
### Environment type validation | ||
No more `if (process.env.NODE_ENV ...`, `EnvironmentUtils` does it once when you instantiate it and then gives you `production()` and `development()` for you to use. | ||
@@ -97,2 +165,2 @@ | ||
open ./docs/index.html | ||
``` | ||
``` |
@@ -22,12 +22,19 @@ const { provider } = require('jimple'); | ||
/** | ||
* Get the value of an environment variable. | ||
* @param {string} name The name of the variable. | ||
* @param {string} [defaultValue=''] A fallback value in case the variable is `undefined` | ||
* Gets the value of an environment variable. | ||
* @param {string} name The name of the variable. | ||
* @param {string} [defaultValue=''] A fallback value in case the variable is `undefined` | ||
* @param {boolean} [required=false] If the variable is required and `undefined`, it will throw | ||
* an error. | ||
* @return {string} | ||
* @todo add a `require` parameter to throw an error if the variable is not preset. | ||
* @throws {Error} if `required` is set to `true` and the variable is `undefined`. | ||
*/ | ||
get(name, defaultValue = '') { | ||
// eslint-disable-next-line no-process-env | ||
let value = process.env[name]; | ||
if (typeof value === 'undefined') { | ||
get(name, defaultValue = '', required = false) { | ||
let value; | ||
if (this.exists(name)) { | ||
value = process.env[name]; | ||
} else { | ||
if (required) { | ||
throw new Error(`The following environment variable is missing: ${name}`); | ||
} | ||
value = defaultValue; | ||
@@ -39,2 +46,29 @@ } | ||
/** | ||
* Sets the value of an environment variable. | ||
* @param {string} name The name of the variable | ||
* @param {string} value The value of the variable. | ||
* @param {string} [overwrite=false] If the variable already exists, the method won't overwrite | ||
* it, unless you set this parameter to `true`. | ||
* @return {boolean} Whether or not the variable was set. | ||
*/ | ||
set(name, value, overwrite = false) { | ||
let result; | ||
if (!this.exists(name) || overwrite) { | ||
process.env[name] = value; | ||
result = true; | ||
} else { | ||
result = false; | ||
} | ||
return result; | ||
} | ||
/** | ||
* Checks whether an environment variable exists or not. | ||
* @param {string} name The name of the variable. | ||
* @return {boolean} | ||
*/ | ||
exists(name) { | ||
return typeof process.env[name] !== 'undefined'; | ||
} | ||
/** | ||
* Check whether or not the environment is for development. | ||
@@ -41,0 +75,0 @@ * @return {boolean} |
@@ -5,3 +5,2 @@ const { provider } = require('jimple'); | ||
* them with detail. | ||
* @todo The `process.exit` should be configurable. | ||
*/ | ||
@@ -8,0 +7,0 @@ class ErrorHandler { |
@@ -5,3 +5,3 @@ { | ||
"homepage": "https://homer0.github.io/wootils/", | ||
"version": "2.4.1", | ||
"version": "2.5.0", | ||
"repository": "homer0/wootils", | ||
@@ -11,25 +11,25 @@ "author": "Leonardo Apiwan (@homer0) <me@homer0.com>", | ||
"dependencies": { | ||
"jimple": "1.5.0", | ||
"fs-extra": "8.1.0", | ||
"colors": "1.3.3", | ||
"urijs": "1.19.1", | ||
"statuses": "1.5.0", | ||
"jimple": "^1.5.0", | ||
"fs-extra": "^8.1.0", | ||
"colors": "^1.3.3", | ||
"urijs": "^1.19.1", | ||
"statuses": "^1.5.0", | ||
"extend": "^3.0.2" | ||
}, | ||
"devDependencies": { | ||
"eslint": "5.16.0", | ||
"eslint-config-airbnb-base": "13.2.0", | ||
"eslint-plugin-import": "2.18.0", | ||
"eslint-plugin-node": "9.1.0", | ||
"eslint": "^5.16.0", | ||
"eslint-config-airbnb-base": "^13.2.0", | ||
"eslint-plugin-import": "^2.18.0", | ||
"eslint-plugin-node": "^9.1.0", | ||
"jest-ex": "^6.1.1", | ||
"jest-cli": "^24.8.0", | ||
"jasmine-expect": "4.0.2", | ||
"@babel/preset-env": "7.5.4", | ||
"@babel/core": "7.5.4", | ||
"@babel/plugin-transform-runtime": "7.5.0", | ||
"esdoc": "1.1.0", | ||
"esdoc-standard-plugin": "1.0.0", | ||
"esdoc-node": "1.0.4", | ||
"leasot": "7.5.0", | ||
"coveralls": "3.0.5" | ||
"jasmine-expect": "^4.0.2", | ||
"@babel/preset-env": "7.5.5", | ||
"@babel/core": "7.5.5", | ||
"@babel/plugin-transform-runtime": "7.5.5", | ||
"esdoc": "^1.1.0", | ||
"esdoc-standard-plugin": "^1.0.0", | ||
"esdoc-node": "^1.0.4", | ||
"leasot": "^7.5.0", | ||
"coveralls": "^3.0.5" | ||
}, | ||
@@ -36,0 +36,0 @@ "engine-strict": true, |
const statuses = require('statuses'); | ||
const urijs = require('urijs'); | ||
const ObjectUtils = require('./objectUtils'); | ||
/** | ||
@@ -97,64 +98,19 @@ * @typedef {Function:(string,Object):Promise<Object,Error>} FetchClient | ||
/** | ||
* Taks a dictionary of endpoints and flatten them on a single level. | ||
* @example | ||
* console.log(APIClient.flattenEndpoints({ | ||
* endpointOne: 'endpoint-one', | ||
* endpointTwo: { | ||
* path: 'endpoint-two', | ||
* query: { | ||
* count: 20, | ||
* }, | ||
* }, | ||
* endpointThree: { | ||
* subEndpointThreeOne: 'sub-endpoint-three-one', | ||
* subEndpointThreeTwo: { | ||
* path: 'sub-endpoint-three-two', | ||
* query: { | ||
* count: 20, | ||
* }, | ||
* }, | ||
* }, | ||
* })); | ||
* // Will output | ||
* { | ||
* endpointOne: 'endpoint-one', | ||
* endpointTwo: { | ||
* path: 'endpoint-two', | ||
* query: { | ||
* count: 20, | ||
* }, | ||
* }, | ||
* 'endpointThree.subEndpointThreeOne': 'sub-endpoint-three-one', | ||
* 'endpointThree.subEndpointThreeTwo': { | ||
* path: 'sub-endpoint-three-two', | ||
* query: { | ||
* count: 20, | ||
* }, | ||
* }, | ||
* }, | ||
* } | ||
* @param {Object} endpoints A dictionary of named endpoints. | ||
* @param {String} [parent=''] The parent key of the received endpoints. This is used when the | ||
* method is calling itself recursively. | ||
* Takes a dictionary of endpoints and flatten them on a single level. | ||
* The method just calls {@link ObjectUtils.flat}. | ||
* @param {APIClientEndpoints} endpoints A dictionary of named endpoints. | ||
* @return {Object} | ||
*/ | ||
flattenEndpoints(endpoints, parent = '') { | ||
const parentKey = parent ? `${parent}.` : ''; | ||
let result = {}; | ||
Object.keys(endpoints).forEach((name) => { | ||
const value = endpoints[name]; | ||
const key = `${parentKey}${name}`; | ||
if (typeof value === 'string' || value.path) { | ||
result[key] = value; | ||
} else { | ||
result = Object.assign({}, result, this.flattenEndpoints(value, key)); | ||
} | ||
}); | ||
return result; | ||
flattenEndpoints(endpoints) { | ||
return ObjectUtils.flat( | ||
endpoints, | ||
'.', | ||
'', | ||
(ignore, value) => typeof value.path === 'undefined' | ||
); | ||
} | ||
/** | ||
* Sets the authorization token for the requests. | ||
* @param {String} [token=''] The new authorization token. If the value is empty, it won't be | ||
* included on the requests. | ||
* Sets a bearer token for all the requests. | ||
* @param {String} [token=''] The new authorization token. If the value is empty, it will remove | ||
* any token previously saved. | ||
*/ | ||
@@ -306,7 +262,6 @@ setAuthorizationToken(token = '') { | ||
* Generates a dictionary of headers using the service `defaultHeaders` property as base. | ||
* If the service has an `authorizationToken`, it will be included as the `Authorization` | ||
* header. | ||
* If a token was set using `setAuthorizationToken`, the method will add an `Authorization` | ||
* header for the bearer token. | ||
* @param {Object} [overwrites={}] Extra headers to add. | ||
* @return {Object} | ||
* @todo Bearer should be configurable when setting the token. | ||
*/ | ||
@@ -331,3 +286,2 @@ headers(overwrites = {}) { | ||
* @return {Promise<Object,Error>} | ||
* @todo Add support for a `string` `body`. | ||
*/ | ||
@@ -334,0 +288,0 @@ fetch(options) { |
@@ -56,3 +56,3 @@ /* eslint-disable no-process-env */ | ||
it('should fallback to an empty string if a required variable doesn\'t exist', () => { | ||
it('should fallback to an empty string if a specified variable doesn\'t exist', () => { | ||
// Given | ||
@@ -67,2 +67,59 @@ let result = 'unknown'; | ||
it('should throw an error if a required variable doesn\'t exist', () => { | ||
// Given/When/Then | ||
const envUtils = new EnvironmentUtils(); | ||
expect(() => envUtils.get('Charito', '', true)) | ||
.toThrow(/The following environment variable is missing/i); | ||
}); | ||
it('should set the value for an environment variable', () => { | ||
// Given | ||
const varName = 'BATMAN_IDENTITY'; | ||
const varValue = 'Bruce Wayne'; | ||
let result = null; | ||
let saved = null; | ||
// When | ||
delete process.env[varName]; | ||
const envUtils = new EnvironmentUtils(); | ||
result = envUtils.set(varName, varValue); | ||
saved = envUtils.get(varName); | ||
// Then | ||
expect(result).toBeTrue(); | ||
expect(saved).toBe(varValue); | ||
}); | ||
it('shouldn\'t overwrite an existing environment variable', () => { | ||
// Given | ||
const varName = 'ROBIN_IDENTITY'; | ||
const varOriginalValue = 'Tim Drake'; | ||
const varValue = 'Damian Wayne'; | ||
let result = null; | ||
let saved = null; | ||
// When | ||
process.env[varName] = varOriginalValue; | ||
const envUtils = new EnvironmentUtils(); | ||
result = envUtils.set(varName, varValue); | ||
saved = envUtils.get(varName); | ||
// Then | ||
expect(result).toBeFalse(); | ||
expect(saved).toBe(varOriginalValue); | ||
}); | ||
it('should overwrite an existing environment variable', () => { | ||
// Given | ||
const varName = 'ROBIN_IDENTITY'; | ||
const varOriginalValue = 'Tim Drake'; | ||
const varValue = 'Damian Wayne'; | ||
let result = null; | ||
let saved = null; | ||
// When | ||
process.env[varName] = varOriginalValue; | ||
const envUtils = new EnvironmentUtils(); | ||
result = envUtils.set(varName, varValue, true); | ||
saved = envUtils.get(varName); | ||
// Then | ||
expect(result).toBeTrue(); | ||
expect(saved).toBe(varValue); | ||
}); | ||
it('should have a Jimple provider to register the service', () => { | ||
@@ -69,0 +126,0 @@ // Given |
@@ -0,1 +1,2 @@ | ||
jest.unmock('/shared/objectUtils'); | ||
jest.unmock('/shared/apiClient'); | ||
@@ -2,0 +3,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
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
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
576324
7439
16
+ Addedcolors@1.4.0(transitive)
+ Addedurijs@1.19.11(transitive)
- Removedcolors@1.3.3(transitive)
- Removedurijs@1.19.1(transitive)
Updatedcolors@^1.3.3
Updatedfs-extra@^8.1.0
Updatedjimple@^1.5.0
Updatedstatuses@^1.5.0
Updatedurijs@^1.19.1