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
  • Loading branch information
chrisradek committed Apr 2, 2018
1 parent d32e926 commit 960fb85
Show file tree
Hide file tree
Showing 5 changed files with 262 additions and 1 deletion.
3 changes: 2 additions & 1 deletion package.json
Expand Up @@ -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
@@ -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
@@ -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
@@ -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
Expand Up @@ -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/';
Expand All @@ -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);
Expand Down

0 comments on commit 960fb85

Please sign in to comment.