Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

translate-api script now removes operations with eventstream trait #1992

Merged
merged 1 commit into from
Apr 2, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
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
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
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