This commit is contained in:
Amar Zavery 2017-12-04 10:42:00 -08:00
Родитель 18461be905
Коммит 7921344cf7
6 изменённых файлов: 347 добавлений и 50 удалений

12
.vscode/launch.json поставляемый
Просмотреть файл

@ -43,6 +43,18 @@
],
"env": {}
},
{
"type": "node",
"request": "launch",
"name": "generate uml",
"program": "${workspaceRoot}/cli.js",
"cwd": "${workspaceRoot}",
"args": [
"generate-uml",
"D:/sdk/azure-rest-api-specs-pr/specification/datamigration/resource-manager/Microsoft.DataMigration/2017-11-15-preview/datamigration.json"
],
"env": {}
},
{
"type": "node",
"request": "launch",

Просмотреть файл

@ -0,0 +1,35 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
'use strict';
var util = require('util'),
log = require('../util/logging'),
validate = require('../validate');
exports.command = 'generate-uml <spec-path>';
exports.describe = 'Generates a class diagram of the model definitions in the given swagger spec.';
exports.builder = {
d: {
alias: 'outputDir',
describe: 'Output directory where the class diagram will be stored.',
string: true,
default: './'
}
};
exports.handler = function (argv) {
log.debug(argv);
let specPath = argv.specPath;
let vOptions = {};
vOptions.consoleLogLevel = argv.logLevel;
vOptions.logFilepath = argv.f;
function execGenerateUml() {
return validate.generateUml(specPath, argv.d, vOptions);
}
execGenerateUml();
};
exports = module.exports;

151
lib/umlGenerator.js Normal file
Просмотреть файл

@ -0,0 +1,151 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
'use strict';
var util = require('util'),
JsonRefs = require('json-refs'),
yuml2svg = require('yuml2svg'),
utils = require('./util/utils'),
Constants = require('./util/constants'),
ErrorCodes = Constants.ErrorCodes;
/**
* @class
* Generates a Uml Diagaram in svg format.
*/
class UmlGenerator {
/**
* @constructor
* Initializes a new instance of the UmlGenerator class.
*
* @param {object} specInJson the parsed spec in json format
*
* @return {object} An instance of the UmlGenerator class.
*/
constructor(specInJson, options) {
if (specInJson === null || specInJson === undefined || typeof specInJson !== 'object') {
throw new Error('specInJson is a required property of type object')
}
this.specInJson = specInJson;
this.graphDefinition = '';
}
generateGraphDefinition() {
this.generateModelPropertiesGraph();
this.generateAllOfGraph();
}
generateAllOfGraph() {
let spec = this.specInJson;
let definitions = spec.definitions;
for (let modelName in definitions) {
let model = definitions[modelName];
if (model.allOf) {
model.allOf.map((item) => {
let referencedModel = item;
let ref = item['$ref'];
let segments = ref.split('/');
let parent = segments[segments.length - 1];
this.graphDefinition += `\n[${parent}]^-.-allOf[${modelName}]`;
});
}
}
}
generateModelPropertiesGraph() {
let spec = this.specInJson;
let definitions = spec.definitions;
let references = [];
for (let modelName in definitions) {
let model = definitions[modelName];
let modelProperties = model.properties;
let props = '';
let bg = '{bg:cornsilk}';
if (modelProperties) {
for (let propertyName in modelProperties) {
let property = modelProperties[propertyName];
let propertyType = this.getPropertyType(modelName, property, references);
let discriminator = '';
if (model.discriminator && model.discriminator === propertyName) {
discriminator = '(discriminator)';
}
props += `-${propertyName}${discriminator}:${propertyType};`;
}
}
this.graphDefinition += props.length ? `[${modelName}|${props}${bg}]\n` : `[${modelName}${bg}]\n`;
}
if (references.length) {
this.graphDefinition += references.join('\n');
}
}
getPropertyType(modelName, property, references) {
if (property.type && property.type.match(/^(string|number|boolean)$/i) !== null) {
return property.type;
}
if (property.type === 'array') {
let result = 'Array<'
if (property.items) {
result += this.getPropertyType(modelName, property.items, references);
}
result += '>';
return result;
}
if (property['$ref']) {
let segments = property['$ref'].split('/');
let referencedModel = segments[segments.length - 1];
references.push(`[${modelName}]->[${referencedModel}]`);
return referencedModel;
}
if (property.additionalProperties && typeof property.additionalProperties === 'object') {
let result = 'Dictionary<';
result += this.getPropertyType(modelName, property.additionalProperties, references);
result += '>';
return result;
}
if (property.type === 'object') {
return 'Object'
}
return '';
}
generateDiagramFromGraph() {
this.generateGraphDefinition();
let svg = '';
try {
console.log(this.graphDefinition);
svg = yuml2svg(this.graphDefinition);
//console.log(svg);
} catch (err) {
return Promise.reject(err);
}
return Promise.resolve(svg);
}
generateInheritanceGraph() {
let self = this;
let spec = self.specInJson;
let definitions = spec.definitions;
let modelNames = Object.keys(definitions);
let subTreeMap = new Map();
modelNames.map((modelName) => {
if (definitions[modelName].allOf) {
let rootNode = subTreeMap.get(modelName)
if (!rootNode) {
rootNode = utils.createInheritanceTree(spec, modelName, subTreeMap, { discriminator: definitions[modelName].discriminator });
}
self.updateReferencesWithOneOf(subTreeMap, references);
}
});
}
}
module.exports = UmlGenerator;

Просмотреть файл

@ -16,7 +16,8 @@ var fs = require('fs'),
SpecValidator = require('./validators/specValidator'),
WireFormatGenerator = require('./wireFormatGenerator'),
XMsExampleExtractor = require('./xMsExampleExtractor'),
SpecResolver = require('./validators/specResolver');
SpecResolver = require('./validators/specResolver'),
UmlGenerator = require('./umlGenerator');
exports = module.exports;
@ -206,6 +207,41 @@ exports.generateWireFormatInCompositeSpec = function generateWireFormatInComposi
});
};
exports.generateUml = function generateUml(specPath, outputDir, options) {
if (!options) options = {};
log.consoleLogLevel = options.consoleLogLevel || log.consoleLogLevel;
log.filepath = options.logFilepath || log.filepath;
let specFileName = path.basename(specPath);
let resolver;
options.shouldResolveRelativePaths = true;
options.shouldResolveXmsExamples = false;
options.shouldResolveAllOf = false;
options.shouldSetAdditionalPropertiesFalse = false;
options.shouldResolvePureObjects = false;
options.shouldResolveDiscriminator = false;
options.shouldResolveParameterizedHost = false;
options.shouldResolveNullableTypes = false;
return utils.parseJson(specPath).then((result) => {
resolver = new SpecResolver(specPath, result, options);
return resolver.resolve();
}).then(() => {
let umlGenerator = new UmlGenerator(resolver.specInJson);
return umlGenerator.generateDiagramFromGraph();
}).then((svgGraph) => {
if (outputDir !== './' && !fs.existsSync(outputDir)) {
fs.mkdirSync(outputDir);
}
let svgFile = specFileName.replace(path.extname(specFileName), '.svg');
let outputFilepath = `${path.join(outputDir, svgFile)}`;
fs.writeFileSync(`${path.join(outputDir, svgFile)}`, svgGraph, { encoding: 'utf8' });
console.log(`Saved the uml at "${outputFilepath}".`)
return Promise.resolve();
}).catch((err) => {
log.error(err);
return Promise.reject(err);
});
};
exports.updateEndResultOfSingleValidation = function updateEndResultOfSingleValidation(validator) {
if (validator.specValidationResult.validityStatus) {
if (!(log.consoleLogLevel === 'json' || log.consoleLogLevel === 'off')) {

156
package-lock.json сгенерированный
Просмотреть файл

@ -7,7 +7,7 @@
"@types/form-data": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/@types/form-data/-/form-data-2.2.1.tgz",
"integrity": "sha512-JAMFhOaHIciYVh8fb5/83nmuO/AHwmto+Hq7a9y8FzLDcC1KCU344XDOMEmahnrTFlHjgh4L0WJFczNIX2GxnQ==",
"integrity": "sha1-7is7jqoRwJOCiZU2BrdFtzjFSx4=",
"requires": {
"@types/node": "8.0.53"
}
@ -21,12 +21,12 @@
"@types/node": {
"version": "8.0.53",
"resolved": "https://registry.npmjs.org/@types/node/-/node-8.0.53.tgz",
"integrity": "sha512-54Dm6NwYeiSQmRB1BLXKr5GELi0wFapR1npi8bnZhEcu84d/yQKqnwwXQ56hZ0RUbTG6L5nqDZaN3dgByQXQRQ=="
"integrity": "sha1-OWs1r4JvpmqtRyyMt7jV4nf05tg="
},
"@types/request": {
"version": "2.0.8",
"resolved": "https://registry.npmjs.org/@types/request/-/request-2.0.8.tgz",
"integrity": "sha512-fp8gsp0Qlq5wRas4UDjzayBxzWtQVcIumsMaHnNJzrk1Skx4WRpX5/HchSdZZf5/3Jp9m59EUBIGSI6mQEMOOg==",
"integrity": "sha1-Qk094lWGgQftTdZpXGXF8XZquoA=",
"requires": {
"@types/form-data": "2.2.1",
"@types/node": "8.0.53"
@ -41,7 +41,7 @@
"@types/uuid": {
"version": "3.4.3",
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-3.4.3.tgz",
"integrity": "sha512-5fRLCYhLtDb3hMWqQyH10qtF+Ud2JnNCXTCZ+9ktNdCcgslcuXkDTkFcJNk++MT29yDntDnlF1+jD+uVGumsbw==",
"integrity": "sha1-EhrOJl9Vac5A9PbQ/3ijOMcyp1Q=",
"requires": {
"@types/node": "8.0.53"
}
@ -333,6 +333,37 @@
"resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
"integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c="
},
"color": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color/-/color-2.0.1.tgz",
"integrity": "sha512-ubUCVVKfT7r2w2D3qtHakj8mbmKms+tThR8gI8zEYCbUBl8/voqFGt3kgBqGwXAopgXybnkuOq+qMYCRrp4cXw==",
"requires": {
"color-convert": "1.9.1",
"color-string": "1.5.2"
}
},
"color-convert": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz",
"integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==",
"requires": {
"color-name": "1.1.3"
}
},
"color-name": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
},
"color-string": {
"version": "1.5.2",
"resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.2.tgz",
"integrity": "sha1-JuRYFLw8mny9Z1FkikFDRRSnc6k=",
"requires": {
"color-name": "1.1.3",
"simple-swizzle": "0.2.2"
}
},
"colors": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/colors/-/colors-0.5.1.tgz",
@ -465,6 +496,14 @@
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz",
"integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k="
},
"deref": {
"version": "0.7.1",
"resolved": "https://registry.npmjs.org/deref/-/deref-0.7.1.tgz",
"integrity": "sha1-/gbyAyyjwiLFjHweld24BzL4RoE=",
"requires": {
"deep-extend": "0.4.2"
}
},
"destroy": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
@ -683,6 +722,11 @@
"resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz",
"integrity": "sha1-Ys8SAjTGg3hdkCNIqADvPgzCC8A="
},
"faker": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/faker/-/faker-4.1.0.tgz",
"integrity": "sha1-HkW7vsxndLPBlfrSg1EJxtdIzD8="
},
"fast-deep-equal": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz",
@ -934,7 +978,7 @@
"is-buffer": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
"integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
"integrity": "sha1-76ouqdqg16suoTqXsritUf776L4="
},
"is-builtin-module": {
"version": "1.0.0",
@ -1087,6 +1131,32 @@
"resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
"integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM="
},
"json-schema-faker": {
"version": "0.5.0-rc9",
"resolved": "https://registry.npmjs.org/json-schema-faker/-/json-schema-faker-0.5.0-rc9.tgz",
"integrity": "sha1-yv8wpFX0+LJnxZdGuzqXVBQIfBE=",
"requires": {
"deref": "0.7.1",
"json-schema-ref-parser": "3.3.1",
"randexp": "0.4.6",
"tslib": "1.8.0"
},
"dependencies": {
"json-schema-ref-parser": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/json-schema-ref-parser/-/json-schema-ref-parser-3.3.1.tgz",
"integrity": "sha1-hudRuAmTV79gGnz+QtEBI+6QajI=",
"requires": {
"call-me-maybe": "1.0.1",
"debug": "3.1.0",
"es6-promise": "4.1.1",
"js-yaml": "3.10.0",
"ono": "4.0.2",
"z-schema": "3.18.4"
}
}
}
},
"json-schema-ref-parser": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/json-schema-ref-parser/-/json-schema-ref-parser-1.4.1.tgz",
@ -1660,7 +1730,7 @@
"ms-rest": {
"version": "2.2.7",
"resolved": "https://registry.npmjs.org/ms-rest/-/ms-rest-2.2.7.tgz",
"integrity": "sha512-mpzbZaeYSN7OaKHW0qUbI2pDZ47oddjGJK6AzjRh7UZ7doFlr1LrgMEWQAiZDDFO9jE8usmCOz4kqWATJq/+nQ==",
"integrity": "sha1-zKD0s1VV4t9kdEAo4lI9QBdejig=",
"requires": {
"@types/node": "8.0.53",
"@types/request": "2.0.8",
@ -1678,7 +1748,7 @@
"ms-rest-azure": {
"version": "2.4.5",
"resolved": "https://registry.npmjs.org/ms-rest-azure/-/ms-rest-azure-2.4.5.tgz",
"integrity": "sha512-O6ho+0EQB0QcRhR5SnTdfRols5WW8FoPCikUwWPQU4f5YOtu/eFNuEZjrsLeIZ06HV2erKh2KH1kUyMHXe1jTw==",
"integrity": "sha1-vyenyP9fEKVPDxhBML/Qt5dOdVM=",
"requires": {
"@types/node": "8.0.53",
"@types/uuid": "3.4.3",
@ -2093,6 +2163,21 @@
"integrity": "sha1-ATKgVBemEmhmQmrPEW8e1WI6XNA=",
"dev": true
},
"simple-swizzle": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
"integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=",
"requires": {
"is-arrayish": "0.3.1"
},
"dependencies": {
"is-arrayish": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.1.tgz",
"integrity": "sha1-wt/DhquqDD4zxI2z/ocFnmkGXv0="
}
}
},
"slash": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz",
@ -2342,7 +2427,7 @@
}
},
"sway": {
"version": "github:amarzavery/sway#b54dceada91eb998744720ec166ff19d4e603a2c",
"version": "github:amarzavery/sway#3eb975c383770c33f01297ab3b150f5dfe7e69c3",
"requires": {
"debug": "3.1.0",
"faker": "4.1.0",
@ -2358,48 +2443,11 @@
"z-schema": "3.18.4"
},
"dependencies": {
"deref": {
"version": "0.7.1",
"resolved": "https://registry.npmjs.org/deref/-/deref-0.7.1.tgz",
"integrity": "sha512-XSRDBknHtbTPqrLwZyqZ8Cr7kRE3vhyVnizbYeIkgZUQHr54u6QMRXGOoYwxoEgy0Xw5dPCXirHtGHFPYm4IwQ==",
"requires": {
"deep-extend": "0.4.2"
}
},
"faker": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/faker/-/faker-4.1.0.tgz",
"integrity": "sha1-HkW7vsxndLPBlfrSg1EJxtdIzD8="
},
"isarray": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
"integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
},
"json-schema-faker": {
"version": "0.5.0-rc9",
"resolved": "https://registry.npmjs.org/json-schema-faker/-/json-schema-faker-0.5.0-rc9.tgz",
"integrity": "sha1-yv8wpFX0+LJnxZdGuzqXVBQIfBE=",
"requires": {
"deref": "0.7.1",
"json-schema-ref-parser": "3.3.1",
"randexp": "0.4.6",
"tslib": "1.8.0"
}
},
"json-schema-ref-parser": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/json-schema-ref-parser/-/json-schema-ref-parser-3.3.1.tgz",
"integrity": "sha512-stQTMhec2R/p2L9dH4XXRlpNCP0mY8QrLd/9Kl+8SHJQmwHtE1nDfXH4wbsSM+GkJMl8t92yZbI0OIol432CIQ==",
"requires": {
"call-me-maybe": "1.0.1",
"debug": "3.1.0",
"es6-promise": "4.1.1",
"js-yaml": "3.10.0",
"ono": "4.0.2",
"z-schema": "3.18.4"
}
},
"lodash": {
"version": "4.17.4",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz",
@ -2441,7 +2489,7 @@
"tunnel": {
"version": "0.0.5",
"resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.5.tgz",
"integrity": "sha512-gj5sdqherx4VZKMcBA4vewER7zdK25Td+z1npBqpbDys4eJrLx+SlYjJvq1bDXs2irkuJM5pf8ktaEQVipkrbA=="
"integrity": "sha1-0VMiVHSe02Yg/NEBCGVJWh+p0K4="
},
"tunnel-agent": {
"version": "0.6.0",
@ -2530,6 +2578,11 @@
"extsprintf": "1.3.0"
}
},
"viz.js": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/viz.js/-/viz.js-1.8.0.tgz",
"integrity": "sha1-4Mta0kE2jjWxpulgaR66RUwklR8="
},
"vscode-jsonrpc": {
"version": "3.4.1",
"resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-3.4.1.tgz",
@ -2627,6 +2680,15 @@
"camelcase": "3.0.0"
}
},
"yuml2svg": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/yuml2svg/-/yuml2svg-3.1.0.tgz",
"integrity": "sha512-gTGq+637C+ZdURr9yyjiAKw4JcL15ZvSiifOEtvSQ1cU1FDOlC6P2+bMnlo6mwkSJaATuKwCOjYjvBRVGkA5Rw==",
"requires": {
"color": "2.0.1",
"viz.js": "1.8.0"
}
},
"z-schema": {
"version": "3.18.4",
"resolved": "https://registry.npmjs.org/z-schema/-/z-schema-3.18.4.tgz",
@ -2639,4 +2701,4 @@
}
}
}
}
}

Просмотреть файл

@ -28,7 +28,8 @@
"jsonpath": "^0.2.11",
"vscode-jsonrpc": "^3.2.0",
"autorest-extension-base": "olydis/autorest-extension-base",
"lodash": "^1.0.0"
"lodash": "^1.0.0",
"yuml2svg": "^3.1.0"
},
"devDependencies": {
"jshint": "2.9.4",
@ -54,4 +55,4 @@
"test": "npm -s run-script jshint && mocha -t 100000",
"start": "node ./lib/autorestPlugin/pluginHost.js"
}
}
}