Skip to content

Commit

Permalink
Merge pull request #30 from agilecontent/release-sprint70
Browse files Browse the repository at this point in the history
Release sprint70
  • Loading branch information
diegocsandrim authored Aug 16, 2017
2 parents e78967b + badbd07 commit 52d5b0f
Show file tree
Hide file tree
Showing 5 changed files with 195 additions and 2 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Change Log

## Unreleased

## v3.2.0
- [2017-08-15] *minor* [TM-4639](https://jiralabone.atlassian.net/browse/TM-4639) **Added remote config loader**

## v3.1.0
- [2017-05-17] *minor* [TM-4315](https://jiralabone.atlassian.net/browse/TM-4315) **Tools should accept Consistency level parameter**

Expand Down
1 change: 1 addition & 0 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ module.exports.createFieldSelector = require('./field-selector').create;
module.exports.createBaseResponse = require('./base-response').create;
module.exports.createFixedTimeService = require('./time-service/fixed-time-service').create;
module.exports.createCurrentTimeService = require('./time-service/current-time-service').create;
module.exports.createRemoteConfig = require('./remote-config').create;

//Helpers
module.exports.number = require('./number-helper');
Expand Down
75 changes: 75 additions & 0 deletions lib/remote-config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
'use strict';

const request = require('request');

class RemoteConfig {

static create(url, refreshTimeSeconds) {
return new RemoteConfig(url, refreshTimeSeconds);
}

constructor(url, refreshTimeSeconds) {
this.url = url;
this.refreshable = (refreshTimeSeconds) ? true : false;
this.refreshTimeSeconds = (refreshTimeSeconds) ? refreshTimeSeconds : 0;
this.isRefreshing = false;
this.nextRefresh = null;
this.configObject = null;
}

getConfigObject(context) {
return this.refreshCache(context);
}

getConfigObjectFromSource(context) {
return new Promise((resolve, reject) => {
if (!this.url) {
return reject(new Error('Missing remote config URL.'));
}
return request({
url: this.url,
json: true,
gzip: true
}, (error, response, body) => {
if (error) {
return reject(`Error calling url ${this.url}.`);
}

if (response.statusCode !== 200) {
return reject(new Error(`Could not get a valid response from ${this.url}.`));
}

return resolve(body);
});
});
}

isCacheExpired() {
return this.configObject === null || (this.refreshable && this.nextRefresh < (new Date()).getTime());
}

refreshCache(context) {
if (this.isRefreshing || !this.isCacheExpired()) {
return Promise.resolve(this.configObject);
}

this.isRefreshing = true;

return this.getConfigObjectFromSource(context)
.then((result) => {
this.configObject = result;
this.nextRefresh = new Date().getTime() + (1000 * this.refreshTimeSeconds);
this.isRefreshing = false;

return this.configObject;
})
.catch((err) => {
this.isRefreshing = false;
context.logger.error({ err: err }, 'Failed to refresh cache config from source.');

return Promise.reject(err);
});
}
}

module.exports = RemoteConfig;
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "itaas-nodejs-tools",
"version": "3.1.0",
"version": "3.2.0",
"private": true,
"description": "Node.js tools",
"main": "./lib/index.js",
Expand Down Expand Up @@ -46,9 +46,11 @@
"intercept-stdout": "^0.1.2",
"istanbul": "^0.4.3",
"mocha": "^2.5.3",
"nock": "^9.0.14",
"nodemon": "^1.9.2",
"request": "^2.72.0",
"should": "^9.0.1"
"should": "^9.0.1",
"sleep": "^5.1.1"
},
"author": "iTaaS",
"license": "ISC",
Expand Down
110 changes: 110 additions & 0 deletions test/unit/remote-config-tests.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
'use strict';
/* global describe, before, it*/

const nock = require('nock');
const should = require('should');
const RemoteConfig = require('../../lib/remote-config');
const uuid = require('uuid').v4;
const tools = require('../../lib/index');
const sleep = require('sleep');

let callId = uuid();
let config = { key: 'value' };
let logger = tools.createLogger();
let serviceLocator = tools.createServiceLocator();
let context = tools.createCallContext(callId, config, logger, serviceLocator);

const configServerUrl = 'http://remote.config';
const configUrl = 'http://remote.config/remoteconfig.json';
const notFoundConfigUrl = 'http://remote.config/notfound.json';
const invalidConfigUrl = 'http://config.invalid/invalid.json';
const configRefreshTime = 2;

const configObject = {
result : {
key1 : 'value1',
key2 : 'value2',
key3 : 3,
key4 : true
}
};

describe('Remote Config', function () {
describe('.getConfigObject', function () {

before(function() {
nock(configServerUrl)
.get('/remoteconfig.json')
.delay(200)
.reply(200, configObject);

nock(configServerUrl)
.get('/notfound.json')
.delay(200)
.reply(404, 'Not Found');
});

it('should return config object : cached and not cached.', function (done) {
this.timeout(10000);
let remoteConfig = new RemoteConfig(configUrl, configRefreshTime);

remoteConfig.getConfigObject(context)
.then((result)=> {
should.deepEqual(result, configObject);
configObject.result.key5 = 'newvalue';

let cached = () => {
return remoteConfig.getConfigObject(context);
};

let notCached = () => {
sleep.sleep((configRefreshTime + 1));

nock(configServerUrl)
.get('/remoteconfig.json')
.delay(200)
.reply(200, configObject);

return remoteConfig.getConfigObject(context);
};

return Promise.all([cached(), notCached()]);
})
.then((results)=>{
should.equal(configObject.result.key5,'newvalue');
should.notDeepEqual(results[0], configObject);
should.deepEqual(results[1], configObject);
done();
})
.catch(done);
});

it('should get error for not found url', function (done) {
let remoteConfig = new RemoteConfig(notFoundConfigUrl, configRefreshTime);

remoteConfig.getConfigObject(context)
.then((result)=>{
done(new Error('Should not resolve when url not found.'));
})
.catch((err)=>{
should.equal(err.message, `Could not get a valid response from ${notFoundConfigUrl}.`);
done();
})
.catch(done);
});

it('should get error for invalid url', function (done) {
let remoteConfig = new RemoteConfig(invalidConfigUrl, configRefreshTime);

remoteConfig.getConfigObject(context)
.then((result)=>{
done(new Error('Should not resolve when url is invalid.'));
})
.catch((err)=>{
should.equal(err, `Error calling url ${invalidConfigUrl}.`);
done();
})
.catch(done);
});
});
});

0 comments on commit 52d5b0f

Please sign in to comment.