1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 | 'use strict';
|
17 |
|
18 |
|
19 |
|
20 |
|
21 | var _ = require('underscore');
|
22 | var commander = require('commander');
|
23 | var fs = require('fs');
|
24 | var path = require('path');
|
25 | var util = require('util');
|
26 | var utilsCore = require('./utilsCore');
|
27 | var pjson = require('../../package.json');
|
28 | var Constants = require('./constants');
|
29 |
|
30 | function ExtendedCommand(name) {
|
31 | ExtendedCommand['super_'].call(this, name);
|
32 | }
|
33 |
|
34 | util.inherits(ExtendedCommand, commander.Command);
|
35 |
|
36 | _.extend(ExtendedCommand.prototype, {
|
37 |
|
38 |
|
39 |
|
40 |
|
41 | fullName: function () {
|
42 | var name = this.name;
|
43 | var scan = this.parent;
|
44 | while (scan && scan.parent !== undefined) {
|
45 | name = scan.name ? scan.name + ' ' + name : name;
|
46 | scan = scan.parent;
|
47 | }
|
48 |
|
49 | return name;
|
50 | },
|
51 |
|
52 | usage: function (str) {
|
53 | var ret;
|
54 |
|
55 | if (str) {
|
56 | ret = commander.Command.prototype.usage.call(this, str);
|
57 | } else {
|
58 | ret = commander.Command.prototype.usage.call(this);
|
59 | ret = ret.replace(/,/g,' ');
|
60 | }
|
61 |
|
62 | return ret;
|
63 | },
|
64 |
|
65 | helpInformation: function() {
|
66 | var self = this;
|
67 |
|
68 | if (!self.parent) {
|
69 | var raw = self.normalize(process.argv.slice(2));
|
70 | var containsJson = raw.some(function (item) { return item === '--json'; });
|
71 | if (containsJson) {
|
72 | self.output.format({ json: true, level: 'data' });
|
73 | self.output.json(self.helpJSON());
|
74 | return '';
|
75 | }
|
76 | var packagePath = path.join(__dirname, '../../package.json');
|
77 | var packageInfo = JSON.parse(fs.readFileSync(packagePath));
|
78 |
|
79 | if (raw.indexOf('-v') >= 0) {
|
80 | console.log(packageInfo.version);
|
81 | } else if (raw.indexOf('--version') >= 0) {
|
82 | console.log(util.format('%s (node: %s)', packageInfo.version, process.versions.node));
|
83 | } else {
|
84 | self.setupCommandLogFormat(true);
|
85 |
|
86 | self.output.info(' _ _____ _ ___ ___'.cyan);
|
87 | self.output.info(' /_\\ |_ / | | | _ \\ __|'.cyan);
|
88 | self.output.info(' _ ___'.grey + '/ _ \\'.cyan + '__'.grey + '/ /| |_| | / _|'.cyan + '___ _ _'.grey);
|
89 | self.output.info('(___ '.grey + '/_/ \\_\\/___|\\___/|_|_\\___|'.cyan + ' _____)'.grey);
|
90 | self.output.info(' (_______ _ _) _ ______ _)_ _ '.grey);
|
91 | self.output.info(' (______________ _ ) (___ _ _)'.grey);
|
92 | self.output.info('');
|
93 |
|
94 | self.output.info('Microsoft Azure: Microsoft\'s Cloud Platform');
|
95 | self.output.info('');
|
96 | self.output.info('Tool version', packageInfo.version);
|
97 |
|
98 | self.helpCommands();
|
99 | self.helpCategoriesSummary(self.showMore());
|
100 | self.helpOptions();
|
101 | self.showCommandMode();
|
102 | }
|
103 | } else {
|
104 | self.output.help(self.description());
|
105 | self.output.help('');
|
106 | self.output.help('Usage:', self.fullName() + ' ' + self.usage());
|
107 | self.helpOptions();
|
108 | self.showCommandMode();
|
109 | }
|
110 |
|
111 | return '';
|
112 | },
|
113 |
|
114 | showCommandMode: function(){
|
115 | this.output.help('');
|
116 | var mode = this._mode;
|
117 | if (!mode) {
|
118 | mode = utilsCore.getMode();
|
119 | }
|
120 | var text = util.format('Current Mode: %s (Azure %s Management)', mode,
|
121 | (mode === Constants.API_VERSIONS.ASM ? 'Service' : 'Resource'));
|
122 | this.output.help(text);
|
123 | },
|
124 |
|
125 | showMore: function () {
|
126 | var raw = this.normalize(process.argv.slice(2));
|
127 | return raw.indexOf('--help') >= 0 || raw.indexOf('-h') >= 0;
|
128 | },
|
129 |
|
130 | categoryHelpInformation: function(containsJson) {
|
131 | var self = this;
|
132 | if (containsJson) {
|
133 | var result = self.getJSONMetadata(true);
|
134 | if (!result) {
|
135 | throw new Error(util.format('Unable to provide help in JSON format for \'%s\'', self.fullName()));
|
136 | }
|
137 | self.output.format({ json: true, level: 'data' });
|
138 | return self.output.json(result);
|
139 | }
|
140 | this.output.help(self.description());
|
141 |
|
142 | if (this.name === 'vm' || this.name === 'vmss' || this.name === 'acs' || this.name === 'managed-disk' || this.name === 'managed-snapshot' || this.name === 'managed-image') {
|
143 | self.helpComputeCategories(-1, 0, this.name);
|
144 | self.helpCommands();
|
145 | } else {
|
146 | self.helpCommands();
|
147 | self.helpCategories(-1);
|
148 | }
|
149 |
|
150 | self.helpOptions();
|
151 | self.showCommandMode();
|
152 | return '';
|
153 | },
|
154 |
|
155 | commandHelpInformation: function(containsJson) {
|
156 | var self = this;
|
157 | if (containsJson) {
|
158 | var result = self.getJSONMetadata(false);
|
159 | if (!result) {
|
160 | throw new Error(util.format('Unable to provide help in JSON format for \'%s\'', self.fullName()));
|
161 | }
|
162 | self.output.format({ json: true, level: 'data' });
|
163 | return self.output.json(result);
|
164 | }
|
165 | if (self._detailedDescription) {
|
166 | this.output.help(self.detailedDescription());
|
167 | } else {
|
168 | this.output.help(self.description());
|
169 | }
|
170 |
|
171 | this.output.help('');
|
172 | this.output.help('Usage:', self.fullName() + ' ' + self.usage());
|
173 |
|
174 | self.helpOptions();
|
175 | self.showCommandMode();
|
176 |
|
177 | return '';
|
178 | },
|
179 |
|
180 | helpJSON: function() {
|
181 | var self = this;
|
182 | var result = _.tap({}, function(res){
|
183 | if(_.isNull(self.parent) || _.isUndefined(self.parent)){
|
184 | res.name = pjson.name;
|
185 | res.description = pjson.description;
|
186 | res.author = pjson.author;
|
187 | res.version = pjson.version;
|
188 | res.contributors = pjson.contributors;
|
189 | res.homepage = pjson.homepage;
|
190 | res.licenses = pjson.licenses;
|
191 | } else {
|
192 | res.name = self.fullName();
|
193 | res.description = self.description();
|
194 | }
|
195 | res.usage = self.usage();
|
196 | });
|
197 |
|
198 | if (this.categories && Object.keys(this.categories).length > 0) {
|
199 | result.categories = {};
|
200 |
|
201 | for (var name in this.categories) {
|
202 | result.categories[name] = this.categories[name].helpJSON();
|
203 | }
|
204 | }
|
205 |
|
206 | if (this.commands && this.commands.length > 0) {
|
207 | result.commands = [];
|
208 |
|
209 | this.commands.forEach(function (cmd) {
|
210 | var command = {
|
211 | name: cmd.fullName(),
|
212 | description: cmd.description(),
|
213 | options: cmd.options,
|
214 | usage: cmd.usage()
|
215 | };
|
216 |
|
217 | result.commands.push(command);
|
218 | });
|
219 | }
|
220 |
|
221 | return result;
|
222 | },
|
223 |
|
224 | helpCategories: function(levels) {
|
225 | for (var name in this.categories) {
|
226 | var cat = this.categories[name];
|
227 |
|
228 | this.output.help('');
|
229 |
|
230 | this.output.help(cat.description().cyan);
|
231 |
|
232 | if (levels === -1 || levels > 0) {
|
233 | for (var index in cat.commands) {
|
234 | var cmd = cat.commands[index];
|
235 | this.output.help(' ', cmd.fullName() + ' ' + cmd.usage());
|
236 | }
|
237 |
|
238 | cat.helpCategories(levels !== -1 ? --levels : -1);
|
239 | } else {
|
240 | this.output.help(' ', cat.fullName());
|
241 | }
|
242 | }
|
243 | },
|
244 |
|
245 | helpComputeCategories: function(levels, depth, catName) {
|
246 | for (var name in this.categories) {
|
247 | var cat = this.categories[name];
|
248 | if (depth === 0) {
|
249 | this.output.help('');
|
250 | this.output.help(cat.description().cyan);
|
251 | }
|
252 |
|
253 | if (levels === -1 || levels > 0) {
|
254 |
|
255 |
|
256 |
|
257 | var index = 0;
|
258 | var cmd = null;
|
259 | if (depth >= 1 && utilsCore.stringStartsWith(cat.fullName(), catName + ' config ')) {
|
260 | var commandNames = [];
|
261 | for (index in cat.commands) {
|
262 | cmd = cat.commands[index];
|
263 | commandNames.push(cmd.name);
|
264 | }
|
265 | this.output.help(' ', cat.fullName() + ' ' + commandNames.join('|') + ' [options]');
|
266 | }
|
267 | else {
|
268 | for (index in cat.commands) {
|
269 | cmd = cat.commands[index];
|
270 | this.output.help(' ', cmd.fullName() + ' ' + cmd.usage());
|
271 | }
|
272 | }
|
273 |
|
274 | cat.helpComputeCategories(levels !== -1 ? --levels : -1, depth + 1, catName);
|
275 | } else {
|
276 | this.output.help(' ', cat.fullName());
|
277 | }
|
278 | }
|
279 | },
|
280 |
|
281 | helpCategoriesSummary: function(showMore) {
|
282 | var self = this;
|
283 | var categories = [];
|
284 | function scan(parent, levels, each) {
|
285 | for (var name in parent.categories) {
|
286 | var cat = parent.categories[name];
|
287 |
|
288 | each(cat);
|
289 |
|
290 | if (levels === -1 || levels > 0) {
|
291 | scan(cat, levels !== -1 ? --levels : -1, each);
|
292 | }
|
293 | }
|
294 | }
|
295 |
|
296 | scan(this, showMore ? -1 : 0, function (cat) { categories.push(cat); });
|
297 | var maxLength = 14;
|
298 |
|
299 |
|
300 | categories.sort(function (a, b) {
|
301 | return (a.fullName() < b.fullName()) ? -1 : (a.fullName() > b.fullName()) ? 1 : 0;
|
302 | });
|
303 |
|
304 | categories.forEach(function (cat) {
|
305 | if (maxLength < cat.fullName().length)
|
306 | maxLength = cat.fullName().length;
|
307 | });
|
308 |
|
309 | self.output.help('');
|
310 | self.output.help('Commands:');
|
311 | categories.forEach(function (cat) {
|
312 | var name = cat.fullName();
|
313 | while (name.length < maxLength) {
|
314 | name += ' ';
|
315 | }
|
316 |
|
317 | self.output.help(' ' + name + ' ' + cat.description().cyan);
|
318 | });
|
319 | },
|
320 |
|
321 | helpCommands: function() {
|
322 | var self = this;
|
323 |
|
324 | this.commands.forEach(function (cmd) {
|
325 | self.output.help('');
|
326 | self.output.help(cmd.description().cyan);
|
327 | self.output.help(' ', cmd.fullName() + ' ' + cmd.usage());
|
328 | });
|
329 | },
|
330 |
|
331 | helpOptions: function() {
|
332 | var self = this;
|
333 | self.output.help('');
|
334 | self.output.help('Options:');
|
335 | self.optionHelp().split('\n').forEach(function (line) { self.output.help(' ', line); });
|
336 | },
|
337 |
|
338 |
|
339 |
|
340 | fileRelatedOption: function (flags, description) {
|
341 | this.option(flags, description);
|
342 | this.options[this.options.length - 1].fileRelatedOption = true;
|
343 | return this;
|
344 | },
|
345 |
|
346 | option: function (flags, description, fn, defaultValue) {
|
347 | var self = this;
|
348 |
|
349 | var option = new commander.Option(flags, description);
|
350 |
|
351 |
|
352 | self.options = self.options.filter(function (o) {
|
353 | return o.name() !== option.name() || o.long !== option.long;
|
354 | });
|
355 |
|
356 | var oname = option.name();
|
357 | var name = utilsCore.camelcase(oname);
|
358 |
|
359 | if (!self.optionValues) {
|
360 | self.optionValues = {};
|
361 | }
|
362 |
|
363 |
|
364 | if ('function' !== typeof fn) {
|
365 | defaultValue = fn;
|
366 | fn = null;
|
367 | }
|
368 |
|
369 |
|
370 | if (false === option.bool || option.optional || option.required) {
|
371 |
|
372 | if (false === option.bool) defaultValue = true;
|
373 |
|
374 | if (undefined !== defaultValue) self.optionValues[name] = defaultValue;
|
375 | }
|
376 |
|
377 |
|
378 | this.options.push(option);
|
379 |
|
380 |
|
381 |
|
382 | this.on(oname, function (val) {
|
383 |
|
384 | if (val && fn) val = fn(val);
|
385 |
|
386 |
|
387 | if ('boolean' === typeof self.optionValues[name] || 'undefined' === typeof self.optionValues[name]) {
|
388 |
|
389 | if (!val) {
|
390 | self.optionValues[name] = option.bool ? defaultValue || true : false;
|
391 | } else {
|
392 | self.optionValues[name] = val;
|
393 | }
|
394 | } else if (val) {
|
395 |
|
396 | self.optionValues[name] = val;
|
397 | }
|
398 | });
|
399 |
|
400 | return this;
|
401 | },
|
402 |
|
403 | getJSONMetadata: function (isCategoryHelp) {
|
404 |
|
405 | function findItem(arr, item) {
|
406 | var filteredArray = arr.filter(function (element) {
|
407 | if (element.name === item) {
|
408 | return element;
|
409 | }
|
410 | });
|
411 | return filteredArray[0];
|
412 | }
|
413 |
|
414 | var self = this;
|
415 | var cmdMetadatafile = path.join(__dirname, '..', 'plugins.' + utilsCore.getMode() + '.json');
|
416 | var data = fs.readFileSync(cmdMetadatafile);
|
417 | var jsonInfo = {};
|
418 | try {
|
419 | jsonInfo = JSON.parse(data);
|
420 | } catch (err) {
|
421 | throw new Error(util.format('Error occurred while parsing the json file \'%s\': \n%s',
|
422 | cmdMetadatafile, util.inspect(err, { depth: null })));
|
423 | }
|
424 |
|
425 | var categories = jsonInfo.categories;
|
426 | var cmdLookup = self.fullName().trim().split(' ');
|
427 | var cats = cmdLookup;
|
428 | var command = '';
|
429 | if (!isCategoryHelp) {
|
430 | command = cmdLookup[cmdLookup.length - 1];
|
431 | var topCmd = findItem(jsonInfo.commands, command);
|
432 | if (topCmd) return topCmd;
|
433 | cats = cmdLookup.slice(0, cmdLookup.length - 1);
|
434 | }
|
435 |
|
436 | for (var i = 0; i< cats.length; i++) {
|
437 | if (categories[cats[i]]) {
|
438 | if (i === cats.length - 1) {
|
439 | categories = categories[cats[i]];
|
440 | } else {
|
441 | categories = categories[cats[i]].categories;
|
442 | }
|
443 | }
|
444 | }
|
445 |
|
446 | if (!isCategoryHelp) {
|
447 | return findItem(categories.commands, command);
|
448 | } else {
|
449 | return categories;
|
450 | }
|
451 | }
|
452 | });
|
453 |
|
454 | module.exports = ExtendedCommand;
|