Skip to content

Commit 764bd0c

Browse files
authored
checkCompatibility feature (#10)
v1.4.0
1 parent a8573b0 commit 764bd0c

8 files changed

+142
-24
lines changed

.travis.yml

-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,5 @@ language: node_js
22
node_js:
33
- stable
44
- 6
5-
- 4
65
sudo: false
76
after_success: "npm run coveralls"

package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "sir-transformalot",
3-
"version": "1.3.0",
3+
"version": "1.4.0",
44
"description": "Transform data between versions",
55
"main": "sir-transformalot.js",
66
"scripts": {
@@ -48,6 +48,6 @@
4848
"sir-transformalot"
4949
],
5050
"engines": {
51-
"node": ">=4"
51+
"node": ">=6"
5252
}
5353
}

sir-transformalot.js

+55-19
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
var async = require('async');
2-
var through2 = require('through2');
1+
const async = require('async');
2+
const through2 = require('through2');
33

44
module.exports = function(transforms) {
55

@@ -8,7 +8,7 @@ module.exports = function(transforms) {
88
}
99

1010
function _transformReadyData(data, fromVersion, toVersion, preparedData, options) {
11-
var version, transformationCode;
11+
let version, transformationCode;
1212
if (fromVersion < toVersion) {
1313
for (version = fromVersion; version < toVersion; version++) {
1414
transformationCode = 'V' + version + 'toV' + (version + 1);
@@ -34,7 +34,7 @@ module.exports = function(transforms) {
3434
if (err) {
3535
return callback(err);
3636
}
37-
var transformedData = _transformReadyData(data, fromVersion, toVersion, preparedData, options);
37+
const transformedData = _transformReadyData(data, fromVersion, toVersion, preparedData, options);
3838
if (transformedData instanceof Error) {
3939
return callback(transformedData);
4040
}
@@ -45,9 +45,9 @@ module.exports = function(transforms) {
4545
function getTransformStream(fromVersion, toVersion, mongo, options) {
4646
fromVersion = parseVersion(fromVersion);
4747
toVersion = parseVersion(toVersion);
48-
var preparedDataSets = null;
48+
let preparedDataSets = null;
4949
return through2.obj(function(obj, encoding, callback){
50-
var through = this;
50+
const through = this;
5151
if(!preparedDataSets) {
5252
_prepareTransform(null, fromVersion, toVersion, mongo, options, function(err, _preparedDataSets){
5353
preparedDataSets = _preparedDataSets;
@@ -62,37 +62,73 @@ module.exports = function(transforms) {
6262
}
6363

6464
function _prepareTransform(id, fromVersion, toVersion, mongo, options, callback) {
65-
var version, transformationCode;
66-
var tasks = {};
65+
const tasks = _createTasksForVersions(
66+
fromVersion,
67+
toVersion,
68+
(version, transformationCode) => createPrepareTransformTask(id, version, transformationCode, mongo, options));
69+
return async.parallel(tasks, callback);
70+
}
71+
72+
function _createTasksForVersions(fromVersion, toVersion, createTaskForVersion) {
73+
let version, transformationCode;
74+
const tasks = {};
6775
if (fromVersion < toVersion) {
6876
for (version = fromVersion; version < toVersion; version++) {
6977
transformationCode = 'V' + version + 'toV' + (version + 1);
70-
tasks[transformationCode] = createPrepareTransformTask(id, version + 1, transformationCode, mongo, options);
78+
tasks[transformationCode] = createTaskForVersion(version + 1, transformationCode);
7179
}
72-
async.parallel(tasks, callback);
7380
} else if (fromVersion > toVersion) {
7481
for (version = fromVersion; version > toVersion; version--) {
7582
transformationCode = 'V' + version + 'toV' + (version - 1);
76-
tasks[transformationCode] = createPrepareTransformTask(id, version, transformationCode, mongo, options);
83+
tasks[transformationCode] = createTaskForVersion(version, transformationCode);
7784
}
78-
async.parallel(tasks, callback);
79-
} else {
80-
callback();
8185
}
86+
return tasks;
8287
}
8388

8489
function createPrepareTransformTask(id, _version, _transformationCode, mongo, options) {
85-
var prepareTransform = transforms['v' + _version][_transformationCode].prepareTransform;
90+
const prepareTransform = transforms['v' + _version][_transformationCode].prepareTransform;
91+
if (!prepareTransform) {
92+
return noopTask;
93+
}
8694
if (options) {
87-
prepareTransform = prepareTransform || function (id, mongo, options, cb) {cb();};
8895
return prepareTransform.bind(null, id, mongo, options);
8996
}
90-
prepareTransform = prepareTransform || function (id, mongo, cb) {cb();};
9197
return prepareTransform.bind(null, id, mongo);
9298
}
9399

100+
function checkCompatibility(id, fromVersion, toVersion, mongo, options, callback) {
101+
if (typeof options === 'function') {
102+
callback = options;
103+
options = null;
104+
}
105+
fromVersion = parseVersion(fromVersion);
106+
toVersion = parseVersion(toVersion);
107+
const tasks = _createTasksForVersions(
108+
fromVersion,
109+
toVersion,
110+
(version, transformationCode) => createCheckCompatibilityTask(id, version, transformationCode, mongo, options));
111+
return async.parallel(tasks, callback);
112+
}
113+
114+
function createCheckCompatibilityTask(id, _version, _transformationCode, mongo, options) {
115+
const checkCompatibility = transforms['v' + _version][_transformationCode].checkCompatibility;
116+
if (!checkCompatibility) {
117+
return noopTask;
118+
}
119+
if (options) {
120+
return checkCompatibility.bind(null, id, mongo, options);
121+
}
122+
return checkCompatibility.bind(null, id, mongo);
123+
}
124+
125+
function noopTask(cb) {
126+
cb();
127+
}
128+
94129
return {
95-
transformObject: transformObject,
96-
getTransformStream: getTransformStream
130+
transformObject,
131+
getTransformStream,
132+
checkCompatibility
97133
};
98134
};

test/common.js

+9-1
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,15 @@ global.getHttpFunction = function (methodType, headers) {
6767
global.responseReturned = null;
6868
return callback(err);
6969
}
70-
expect(resp.statusCode, {body: resp.body, stack: '\n\n' + location + '\n\n'}).to.equal(statusCode);
70+
try {
71+
expect({
72+
statusCode: resp.statusCode,
73+
body: resp.body
74+
}).to.containSubset({statusCode});
75+
} catch(ex) {
76+
location && (ex.stack = location);
77+
return callback(ex);
78+
}
7179
global.responseReturned = resp;
7280
global.bodyReturned = resp.body;
7381
return successCallback(resp);

test/integration/integration.spec.js

+34
Original file line numberDiff line numberDiff line change
@@ -189,4 +189,38 @@ describe('integration tests', function () {
189189
});
190190
});
191191
});
192+
193+
describe('when we check for compatibility first', () => {
194+
describe('when data is compatible', () => {
195+
before((done) => get('entityCheckingCompatibility/v1', 200, done));
196+
197+
it('should return data', () => {
198+
expect(bodyReturned).to.containSubset([
199+
{id: 1},
200+
{id: 2}
201+
]);
202+
});
203+
});
204+
205+
describe('when data is not compatible', () => {
206+
before((done) => get('entityCheckingCompatibility/v1?testDataNotCompatible=true', 500, done));
207+
208+
it('should handle them', () => {
209+
expect(bodyReturned).to.containSubset({
210+
message: 'Data incompatible with old version of endpoint, use new version'
211+
});
212+
});
213+
});
214+
215+
describe('when new version of endpoint is used', () => {
216+
before((done) => get('entityCheckingCompatibility/v2?testDataNotCompatible=true', 200, done));
217+
218+
it('should return data', () => {
219+
expect(bodyReturned).to.containSubset([
220+
{id: 1},
221+
{id: 2}
222+
]);
223+
});
224+
});
225+
});
192226
});

test/testApp/app.js

+17
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,23 @@ app.post('/entityReturningErrorOnTransform/:version(v2|v1)', function(req, res,
104104
});
105105
});
106106

107+
app.get('/entityCheckingCompatibility/:version(v2|v1)', (req, res, next) => {
108+
const mongo = 'Yeap, this is mongo :P';
109+
const checkOpts = {testDataNotCompatible: req.query.testDataNotCompatible === 'true'};
110+
transforms.entityCheckingCompatibility.checkCompatibility(req.params.id, 'v2', req.params.version, mongo, checkOpts, (err) => {
111+
if (err) {
112+
return next(err);
113+
}
114+
res.header('content-type', 'application/json; charset=utf-8');
115+
const transformStream = transforms.entityCheckingCompatibility.getTransformStream('v2', req.params.version, mongo);
116+
pump(db.getDataStream(), transformStream, JSONStream.stringify(), res, function(err) {
117+
if (err) {
118+
console.log(err); // can't do next(err) because stream is closed
119+
}
120+
});
121+
});
122+
});
123+
107124
app.use(function (err, req, res, next) {
108125
res.status(500).json({message: err.message});
109126
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
const entityCheckingCompatibility = {
2+
v2: {
3+
V1toV2: {
4+
transform: function(data, preparedData, options) {
5+
return data;
6+
}
7+
},
8+
V2toV1: {
9+
checkCompatibility: function(id, mongo, options, callback) {
10+
if (options.testDataNotCompatible) {
11+
return callback(new Error('Data incompatible with old version of endpoint, use new version'));
12+
}
13+
callback();
14+
},
15+
transform: function(data, preparedData, options) {
16+
return data;
17+
}
18+
}
19+
}
20+
};
21+
22+
const transformalot = require('../../../sir-transformalot');
23+
module.exports = transformalot(entityCheckingCompatibility);

test/testApp/transforms/index.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
module.exports = {
22
entity: require('./entity'),
33
entityNeedingOptions: require('./entityNeedingOptions'),
4-
entityReturningErrorOnTransform: require('./entityReturningErrorOnTransform')
4+
entityReturningErrorOnTransform: require('./entityReturningErrorOnTransform'),
5+
entityCheckingCompatibility: require('./entityCheckingCompatibility')
56
};

0 commit comments

Comments
 (0)