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
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