Skip to content

Commit

Permalink
translate-api script now removes operations with eventstream trait (#…
Browse files Browse the repository at this point in the history
chrisradek authored Apr 2, 2018

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
1 parent d32e926 commit 960fb85
Showing 5 changed files with 262 additions and 1 deletion.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -138,6 +138,7 @@
"build-react-native-dist": "webpack --config dist-tools/webpack.config.rn.js",
"build-react-native": "npm -s run-script build-react-native-deps && npm -s run-script build-react-native-core && npm -s run-script build-react-native-dist",
"react-native-test": "npm -s run-script build-react-native && rake reactnative:test && karma start",
"region-check": "node ./scripts/region-checker/index.js"
"region-check": "node ./scripts/region-checker/index.js",
"test-remove-event-stream": "mocha scripts/lib/remove-event-stream-ops.spec.js"
}
}
104 changes: 104 additions & 0 deletions scripts/lib/foo-2018-03-30.normal.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
{
"version": "2.0",
"metadata": {
"apiVersion": "2018-03-30",
"endpointPrefix": "foo",
"protocol": "rest-json",
"serviceId": "Foo",
"uid": "foo-2018-03-30"
},
"operations": {
"BarOperation": {
"name": "BarOperation",
"http": {
"method": "GET",
"requireUri": "/"
},
"input": {
"shape": "BarOperationInput"
},
"output": {
"shape": "BarOperationOutput"
}
},
"EventStreamOnInputOperation": {
"name": "EventStreamOnInputOperation",
"http": {
"method": "GET",
"requireUri": "/"
},
"input": {
"shape": "EventStreamStructure"
}
},
"EventStreamOnInputPayloadOperation": {
"name": "EventStreamOnInputPayloadOperation",
"http": {
"method": "GET",
"requireUri": "/"
},
"input": {
"shape": "EventStreamPayload"
}
},
"EventStreamOnOutputOperation": {
"name": "EventStreamOnOutputOperation",
"http": {
"method": "GET",
"requireUri": "/"
},
"output": {
"shape": "EventStreamStructure"
}
},
"EventStreamOnOutputPayloadOperation": {
"name": "EventStreamOnOutputPayloadOperation",
"http": {
"method": "GET",
"requireUri": "/"
},
"output": {
"shape": "EventStreamPayload"
}
}
},
"shapes": {
"BarOperationInput": {
"type": "structure",
"members": {
"String": {
"shape": "StringShape"
}
}
},
"BarOperationOutput": {
"type": "structure",
"members": {
"String": {
"shape": "StringShape"
}
}
},
"EventStreamPayload": {
"type": "structure",
"members": {
"Payload": {
"shape": "EventStreamStructure"
},
"payload": "Payload"
}
},
"EventStreamStructure": {
"type": "structure",
"members": {
"String": {
"shape": "StringShape"
}
},
"eventstream": true
},
"StringShape": {
"type": "string"
}
}
}
57 changes: 57 additions & 0 deletions scripts/lib/remove-event-stream-ops.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/**
* Removes operations from the model if they require event streams.
* Specifically looks at input and output shapes.
* @param {Object} model - JSON parsed API model (*.normal.json)
*/
function removeEventStreamOperations(model) {
var modifiedModel = false;
// loop over all operations
var operations = model.operations;
var operationNames = Object.keys(operations);
for (var i = 0; i < operationNames.length; i++) {
var operationName = operationNames[i];
var operation = operations[operationName];
// check input and output shapes
var inputShapeName = operation.input && operation.input.shape;
var outputShapeName = operation.output && operation.output.shape;

var requiresEventStream = false;
if (inputShapeName && hasEventStream(model.shapes[inputShapeName], model)) {
requiresEventStream = true;
}
if (outputShapeName && hasEventStream(model.shapes[outputShapeName], model)) {
requiresEventStream = true;
}

if (requiresEventStream) {
modifiedModel = true;
// remove the operation from the model
console.log('Removing ' + operationName + ' because it depends on event streams.');
delete model.operations[operationName];
}
}
return modifiedModel;
}

function hasEventStream(shape, model) {
if (shape.eventstream) {
return true;
} else {
// check each member shape
var memberNames = Object.keys(shape.members);
for (var i = 0; i < memberNames.length; i++) {
var member = shape.members[memberNames[i]];
if (member.eventstream) {
return true;
}
var memberShape = model.shapes[member.shape];
if (memberShape.eventstream) {
return true;
}
}
}
}

module.exports = {
removeEventStreamOperations: removeEventStreamOperations
};
93 changes: 93 additions & 0 deletions scripts/lib/remove-event-stream-ops.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
var expect = require('chai').expect;
var removeEventStreamOperations = require('./remove-event-stream-ops').removeEventStreamOperations;
var fooModel = require('./foo-2018-03-30.normal.json');

describe('removeEventStreamOperations', function() {
describe('removes operations when eventstream', function() {
it('is on the input shape shape', function() {
var mockModel = deepCopyObject(fooModel);
expect(typeof mockModel.operations['EventStreamOnInputOperation']).to.equal('object');
removeEventStreamOperations(mockModel);
expect(typeof mockModel.operations['EventStreamOnInputOperation']).to.equal('undefined');
});

it('is on the input shape payload shape', function() {
var mockModel = deepCopyObject(fooModel);
expect(typeof mockModel.operations['EventStreamOnInputPayloadOperation']).to.equal('object');
removeEventStreamOperations(mockModel);
expect(typeof mockModel.operations['EventStreamOnInputPayloadOperation']).to.equal('undefined');
});

it('is on the output shape ', function() {
var mockModel = deepCopyObject(fooModel);
expect(typeof mockModel.operations['EventStreamOnOutputOperation']).to.equal('object');
removeEventStreamOperations(mockModel);
expect(typeof mockModel.operations['EventStreamOnOutputOperation']).to.equal('undefined');
});

it('is on the output shape payload shape', function() {
var mockModel = deepCopyObject(fooModel);
expect(typeof mockModel.operations['EventStreamOnOutputPayloadOperation']).to.equal('object');
removeEventStreamOperations(mockModel);
expect(typeof mockModel.operations['EventStreamOnOutputPayloadOperation']).to.equal('undefined');
});
});

describe('does not remove operations', function() {
it('when eventstream is not present on input or output shapes', function() {
var mockModel = deepCopyObject(fooModel);
expect(typeof mockModel.operations['BarOperation']).to.equal('object');
removeEventStreamOperations(mockModel);
expect(typeof mockModel.operations['BarOperation']).to.equal('object');
});
});

it('returns true when an operation is removed', function() {
var mockModel = deepCopyObject(fooModel);
expect(typeof mockModel.operations['EventStreamOnOutputPayloadOperation']).to.equal('object');
var didRemove = removeEventStreamOperations(mockModel);
expect(typeof mockModel.operations['EventStreamOnOutputPayloadOperation']).to.equal('undefined');
expect(didRemove).to.equal(true);
});

it('returns false when no operations are removed', function() {
var mockModel = deepCopyObject(fooModel);
// delete operations we know will be removed
var operationsToRemove = [
'EventStreamOnInputOperation',
'EventStreamOnInputPayloadOperation',
'EventStreamOnOutputOperation',
'EventStreamOnOutputPayloadOperation'
];
for (var i = 0; i < operationsToRemove.length; i++) {
delete mockModel.operations[operationsToRemove[i]];
}
expect(typeof mockModel.operations['BarOperation']).to.equal('object');
var didRemove = removeEventStreamOperations(mockModel);
expect(typeof mockModel.operations['BarOperation']).to.equal('object');
expect(didRemove).to.equal(false);
});
});

function deepCopyObject(original) {
if (typeof original !== 'object' || original === null) {
return original;
}
var newObject = {};
var keys = Object.keys(original);
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
var value = original[key];
if (Array.isArray(value)) {
newObject[key] = [];
for (var j = 0; j < value.length; j++) {
newObject[key].push(deepCopyObject(value[j]));
}
} else if (typeof value === 'object' && value !== null) {
newObject[key] = deepCopyObject(value)
} else {
newObject[key] = value;
}
}
return newObject;
}
6 changes: 6 additions & 0 deletions scripts/translate-api
Original file line number Diff line number Diff line change
@@ -2,6 +2,7 @@

var fs = require('fs');
var Translator = require('./lib/translator');
var removeEventStreamOperations = require('./lib/remove-event-stream-ops').removeEventStreamOperations;
var util = require('util');

var basePath = __dirname + '/../apis/';
@@ -12,6 +13,11 @@ paths.forEach(function (path) {
if (path.match(new RegExp(modelName + ".+\\.normal\\.json$"))) {
var opath = path.replace(/\.normal\.json$/, '.min.json');
var data = JSON.parse(fs.readFileSync(basePath + path).toString());
var didModify = removeEventStreamOperations(data);
if (didModify) {
// original model modified, replace existing normal.json so docs/ts definitions are accurate
fs.writeFileSync(basePath + path, JSON.stringify(data, null, ' '));
}
var translated = new Translator(data, {documentation: false});
var json = JSON.stringify(translated, null, ' ');
fs.writeFileSync(basePath + opath, json);

0 comments on commit 960fb85

Please sign in to comment.