Backend half
This commit is contained in:
+160
@@ -0,0 +1,160 @@
|
||||
/* A couple of utility methods */
|
||||
|
||||
function each(obj, iter) {
|
||||
for (var key in obj) {
|
||||
if (obj.hasOwnProperty(key)) iter(key, obj[key]);
|
||||
}
|
||||
}
|
||||
|
||||
function nextString(str) {
|
||||
return 'S' + (parseInt(str.substr(1), 36) + 1).toString(36);
|
||||
}
|
||||
|
||||
/* End utility methods */
|
||||
|
||||
function Translator(api, options) {
|
||||
var origLength = JSON.stringify(api, null, 2).length;
|
||||
var debugInfo = {flattened: {}, pruned: {}};
|
||||
var shapeName = 'S0';
|
||||
var shapeNameMap = {};
|
||||
var visitedShapes = {};
|
||||
|
||||
function logResults() {
|
||||
console.log('** Generated', api.metadata.endpointPrefix + '-' +
|
||||
api.metadata.apiVersion +'.min.json' +
|
||||
(process.env.DEBUG ? ':' : ''));
|
||||
|
||||
if (process.env.DEBUG) {
|
||||
var pruned = Object.keys(debugInfo.pruned);
|
||||
var flattened = Object.keys(debugInfo.flattened);
|
||||
var newLength = JSON.stringify(api, null, 2).length;
|
||||
console.log('- Pruned Shapes:', pruned.length);
|
||||
console.log('- Flattened Shapes:', flattened.length);
|
||||
console.log('- Remaining Shapes:', Object.keys(api.shapes).length);
|
||||
console.log('- Original Size:', origLength / 1024.0, 'kb');
|
||||
console.log('- Minified Size:', newLength / 1024.0, 'kb');
|
||||
console.log('- Size Saving:', (origLength - newLength) / 1024.0, 'kb');
|
||||
console.log('');
|
||||
}
|
||||
}
|
||||
|
||||
function deleteTraits(obj) {
|
||||
if (!options.documentation) {
|
||||
delete obj.documentation;
|
||||
delete obj.documentationUrl;
|
||||
delete obj.errors;
|
||||
delete obj.min;
|
||||
delete obj.max;
|
||||
delete obj.pattern;
|
||||
delete obj['enum'];
|
||||
delete obj.box;
|
||||
}
|
||||
}
|
||||
|
||||
function trackShapeDeclaration(ref) {
|
||||
if (ref.shape && !shapeNameMap[ref.shape]) {
|
||||
// found a shape declaration we have not yet visited.
|
||||
// assign a new generated name in the shapeNameMap & visit it
|
||||
var oldShapeName = ref.shape;
|
||||
ref.shape = shapeName = nextString(shapeName);
|
||||
|
||||
visitedShapes[shapeName] = api.shapes[oldShapeName];
|
||||
shapeNameMap[oldShapeName] = {name: shapeName, refs: [ref]};
|
||||
|
||||
traverseShapeRef(api.shapes[oldShapeName]);
|
||||
} else if (ref.shape && shapeNameMap[ref.shape]) {
|
||||
// we visited this shape before. keep track of this ref and rename
|
||||
// the referenced declaration to the generated name
|
||||
var map = shapeNameMap[ref.shape];
|
||||
map.refs.push(ref);
|
||||
ref.shape = map.name;
|
||||
}
|
||||
}
|
||||
|
||||
function pruneShapes() {
|
||||
// prune shapes visited only once or only have type specifiers
|
||||
each(shapeNameMap, function(name, map) {
|
||||
if (Object.keys(visitedShapes[map.name]).join() === 'type' &&
|
||||
['structure','map','list'].indexOf(visitedShapes[map.name].type) < 0) {
|
||||
// flatten out the shape (only a scalar type property is on the shape)
|
||||
for (var i = 0; i < map.refs.length; i++) {
|
||||
var ref = map.refs[i];
|
||||
debugInfo.flattened[name] = true;
|
||||
delete ref.shape;
|
||||
ref.type = visitedShapes[map.name].type;
|
||||
|
||||
// string type is default, don't need to specify this
|
||||
if (ref.type === 'string') delete ref.type;
|
||||
}
|
||||
|
||||
// we flattened all refs, we can prune the shape too
|
||||
delete visitedShapes[map.name];
|
||||
debugInfo.pruned[name] = true;
|
||||
} else if (map.refs.length === 1) { // only visited once
|
||||
// merge shape data onto ref
|
||||
var shape = visitedShapes[map.name];
|
||||
|
||||
for (var i = 0; i < map.refs.length; i++) {
|
||||
delete map.refs[i].shape;
|
||||
for (var prop in shape) {
|
||||
if (shape.hasOwnProperty(prop)) {
|
||||
//Translator prefers timestampFormat trait in members rather than in shape
|
||||
if (map.refs[i].hasOwnProperty(prop) && ['timestampFormat'].indexOf(prop) >= 0) {
|
||||
continue;
|
||||
}
|
||||
map.refs[i][prop] = shape[prop];
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// delete the visited shape
|
||||
delete visitedShapes[map.name];
|
||||
debugInfo.pruned[name] = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function traverseShapeRef(ref) {
|
||||
if (!ref) return;
|
||||
|
||||
deleteTraits(ref);
|
||||
|
||||
traverseShapeRef(ref.key); // for maps
|
||||
traverseShapeRef(ref.value); // for maps
|
||||
traverseShapeRef(ref.member); // for lists
|
||||
|
||||
// for structures
|
||||
each(ref.members || {}, function(key, value) { traverseShapeRef(value); });
|
||||
|
||||
// resolve shape declarations
|
||||
trackShapeDeclaration(ref);
|
||||
}
|
||||
|
||||
function traverseOperation(op) {
|
||||
deleteTraits(op);
|
||||
|
||||
delete op.name;
|
||||
if (op.http) {
|
||||
if (op.http.method === 'POST') delete op.http.method;
|
||||
if (op.http.requestUri === '/') delete op.http.requestUri;
|
||||
if (Object.keys(op.http).length === 0) delete op.http;
|
||||
}
|
||||
|
||||
traverseShapeRef(op.input);
|
||||
traverseShapeRef(op.output);
|
||||
}
|
||||
|
||||
function traverseApi() {
|
||||
deleteTraits(api);
|
||||
each(api.operations, function(name, op) { traverseOperation(op); });
|
||||
api.shapes = visitedShapes;
|
||||
}
|
||||
|
||||
traverseApi();
|
||||
pruneShapes();
|
||||
logResults();
|
||||
return api;
|
||||
}
|
||||
|
||||
module.exports = Translator;
|
||||
Reference in New Issue
Block a user