takorogo
Version:
Takorogo to JSON parser for Node.js
383 lines (349 loc) • 12.1 kB
JavaScript
(function() {
'use strict';
var Postprocessor, utils, _,
__bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
__indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
_ = require('lodash');
utils = require('./utils');
Postprocessor = (function() {
function Postprocessor() {
this.processRules = __bind(this.processRules, this);
this.processDefinition = __bind(this.processDefinition, this);
this.processEnumeration = __bind(this.processEnumeration, this);
}
Postprocessor.supportedRules = ['definition', 'enumeration', 'index', 'attribute', 'relation', 'resolvedRelation', 'link', 'meta'];
Postprocessor.directMetadata = ['title', 'description'];
Postprocessor.absoluteReferencePathForClass = function(className, ctx) {
return "" + ctx.__path + "/definitions/" + className;
};
Postprocessor.prototype.cleanRefs = function() {
this.typeRefs = [];
this.typeDefs = {};
return this.mainClass = false;
};
Postprocessor.prototype.postprocess = function(data) {
var schema;
this.cleanRefs();
schema = this.processRules(data, {
__path: '#'
});
this.resolveTypeReferences(schema);
this.cleanUpSchema(schema);
return schema;
};
Postprocessor.prototype.cleanUpSchema = function(schema) {
delete schema.__path;
_.forEach(this.typeDefs, function(def) {
return delete def.__path;
});
if (this.mainClass) {
_.defaults(schema, this.mainClass);
}
return schema;
};
Postprocessor.prototype.processMeta = function(meta, ctx) {
var _ref;
if (ctx == null) {
ctx = {};
}
if (_ref = meta.key, __indexOf.call(this.constructor.directMetadata, _ref) >= 0) {
ctx[meta.key] = meta.value;
} else {
utils.addAsObjectMember(ctx, 'metadata', meta.key, meta.value);
}
return ctx;
};
Postprocessor.prototype.processEnumeration = function(enumeration, ctx) {
var destructuredClassTitle, elements, klass;
if (ctx == null) {
ctx = {};
}
elements = enumeration.elements.map(function(element) {
return element.name;
});
destructuredClassTitle = "__" + enumeration.title + "__";
klass = _.merge(_.omit(enumeration, ['elements']), {
title: destructuredClassTitle,
additionalProperties: false,
required: elements
});
klass.required.forEach((function(_this) {
return function(key) {
return _this.addPropertyToContext(key, {}, klass);
};
})(this));
enumeration = {
__path: this.constructor.absoluteReferencePathForClass(enumeration.title, ctx),
type: 'array',
title: enumeration.title,
elements: elements,
minItems: elements.length,
maxItems: elements.length
};
enumeration.destructuredTo = {
$ref: this.constructor.absoluteReferencePathForClass(destructuredClassTitle, enumeration)
};
this.processDefinition(klass, enumeration);
utils.addAsObjectMember(ctx, 'definitions', enumeration.title, enumeration);
this.registerDefinition(enumeration);
return ctx;
};
Postprocessor.prototype.processDefinition = function(klass, ctx) {
if (ctx == null) {
ctx = {};
}
klass.__path = this.constructor.absoluteReferencePathForClass(klass.title, ctx);
if ((klass.rules != null) && klass.rules.length > 0) {
this.processRules(klass.rules, klass);
}
if (klass.isMainDefinition) {
this.mainClass = {
title: klass.title,
type: klass.type,
$ref: klass.__path
};
}
klass = _.omit(klass, ['rules', 'rule', 'isMainDefinition']);
utils.addAsObjectMember(ctx, 'definitions', klass.title, klass);
this.registerDefinition(klass);
return ctx;
};
Postprocessor.prototype.processRule = function(rule, ctx) {
var method, _ref;
if ((_ref = rule.rule, __indexOf.call(this.constructor.supportedRules, _ref) >= 0)) {
method = "process" + (utils.capitalizeFirst(rule.rule));
return this[method](rule, ctx);
} else {
throw new Error("Unrecognized Takorogo rule " + rule.rule + ".");
}
};
Postprocessor.prototype.processRules = function(rules, ctx) {
if (ctx == null) {
ctx = {};
}
rules.forEach((function(_this) {
return function(rule) {
return _this.processRule(rule, ctx);
};
})(this));
return ctx;
};
Postprocessor.prototype.processIndex = function(index, ctx) {
if (ctx == null) {
ctx = {};
}
index = _.omit(index, ['rule']);
index.key = index.key.map((function(_this) {
return function(field) {
if (field.type != null) {
_this.processAttribute({
attribute: field
}, ctx);
}
return field.name;
};
})(this));
utils.pushAsArrayItem(ctx, 'indexes', index);
index.key.forEach((function(_this) {
return function(key) {
var _ref;
if (((_ref = ctx.properties) != null ? _ref[key] : void 0) == null) {
_this.addPropertyToContext(key, {}, ctx);
}
return utils.pushAsArrayUniqueItem(ctx, 'required', key);
};
})(this));
return ctx;
};
Postprocessor.prototype.processAttribute = function(property, ctx) {
var name;
if (ctx == null) {
ctx = {};
}
if (property.attribute.aliasOf != null) {
utils.addAsObjectMember(ctx, 'aliases', property.attribute.name, property.attribute.aliasOf.name);
}
name = property.attribute.name;
property = _.omit(property.attribute, ['aliasOf', 'name']);
this.processPropertyType(property, ctx);
this.addPropertyToContext(name, property, ctx);
return ctx;
};
Postprocessor.prototype.addPropertyToContext = function(name, property, ctx) {
property = this.expandPropertyPath(name, property);
name = name.split('.')[0];
utils.addAsObjectMember(ctx, 'properties', name, property);
return ctx;
};
Postprocessor.prototype.expandPropertyPath = function(name, property) {
var path, wrapper;
path = name.split('.');
if (path.length > 1) {
wrapper = {
type: 'object',
properties: {}
};
wrapper.properties[path[1]] = this.expandPropertyPath(path.slice(1).join('.'), property);
property = wrapper;
}
return property;
};
Postprocessor.prototype.registerTypeReference = function(property, ctx) {
return this.typeRefs.push({
property: property,
ctx: ctx
});
};
Postprocessor.prototype.registerDefinition = function(definition) {
return this.typeDefs[definition.__path] = definition;
};
Postprocessor.prototype.findTypeReference = function(name, ctx) {
var $ref, path;
$ref = this.constructor.absoluteReferencePathForClass(name, ctx);
if (this.typeDefs[$ref] != null) {
return $ref;
} else {
path = ctx.__path.split('/').slice(0, -1).join('/');
if (path) {
return this.findTypeReference(name, {
__path: path
});
} else {
return false;
}
}
};
Postprocessor.prototype.resolveTypeReference = function(property, unresolved) {
var $ref, ref;
ref = property.property;
$ref = this.findTypeReference(ref.type.title, property.ctx);
if ($ref) {
ref.type.$ref = $ref;
} else {
ref.type.type = ref.type.title;
unresolved[ref.type.title] = true;
}
return _.merge(property.property, _.omit(ref.type, ['title']));
};
Postprocessor.prototype.resolveTypeReferences = function(schema) {
var unresolved;
unresolved = {};
this.typeRefs.forEach((function(_this) {
return function(ref) {
return _this.resolveTypeReference(ref, unresolved);
};
})(this));
unresolved = Object.keys(unresolved);
if (unresolved.length > 0) {
schema.unresolvedTypes = unresolved;
}
return schema;
};
Postprocessor.prototype.processPropertyType = function(property, ctx) {
var _ref, _ref1, _ref2;
if (ctx == null) {
ctx = {};
}
if (((_ref = property.type) != null ? _ref.rule : void 0) != null) {
this.processRule(property.type, ctx);
property.type = _.pick(property.type, ['type', 'title', 'isArrayOf', 'arrayDepth']);
}
if ((_ref1 = property.type) != null ? _ref1.isArrayOf : void 0) {
this.processArrayType(property, ctx);
}
if (((_ref2 = property.type) != null ? _ref2.type : void 0) === 'object') {
this.registerTypeReference(property, ctx);
}
return ctx;
};
Postprocessor.prototype.processArrayType = function(property, ctx) {
var arrayDepth;
arrayDepth = property.type.arrayDepth || 1;
property.items = {
type: _.omit(property.type, ['isArrayOf', 'arrayDepth'])
};
property.type = 'array';
this.registerTypeReference(property.items, ctx);
while (arrayDepth-- > 1) {
property.items = {
type: 'array',
items: property.items
};
}
return ctx;
};
Postprocessor.prototype.processRelation = function(relation, ctx, addAttribute) {
var relType, _i, _len, _ref, _ref1;
if (ctx == null) {
ctx = {};
}
if (addAttribute == null) {
addAttribute = true;
}
if (addAttribute) {
this.processAttribute({
attribute: relation.attribute
}, ctx);
}
relation = _.merge(_.omit(relation, ['rule', 'attribute']), {
property: relation.attribute.name
});
_ref = ['in', 'out'];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
relType = _ref[_i];
if (((_ref1 = relation[relType]) != null ? _ref1.attributes : void 0) != null) {
relation[relType].attributes = relation[relType].attributes.map((function(_this) {
return function(attr) {
var _ref2;
if (!((_ref2 = ctx.properties) != null ? _ref2[attr.name] : void 0)) {
_this.processAttribute({
attribute: attr
}, ctx);
}
return attr.name;
};
})(this));
}
}
utils.addAsObjectMember(ctx, 'relations', relation.property, relation);
return ctx;
};
Postprocessor.prototype.processResolvedRelation = function(relation, ctx) {
var attribute, _i, _len, _ref;
if (ctx == null) {
ctx = {};
}
_ref = relation.resolve.key;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
attribute = _ref[_i];
this.processAttribute({
attribute: attribute
}, ctx);
utils.pushAsArrayUniqueItem(ctx, 'required', attribute.name);
}
relation.resolve = {
key: relation.resolve.key.map(function(attr) {
return attr.name;
})
};
this.processRelation(relation, ctx, false);
return ctx;
};
Postprocessor.prototype.processLink = function(link, ctx) {
if (ctx == null) {
ctx = {};
}
this.processAttribute({
attribute: link.attribute
}, ctx);
link = _.merge(_.omit(link, ['rule', 'attribute']), {
property: link.attribute.name
});
utils.addAsObjectMember(ctx, 'links', link.property, link);
return ctx;
};
return Postprocessor;
})();
module.exports = Postprocessor;
}).call(this);
//# sourceMappingURL=postprocessor.js.map