UNPKG

12.1 kBJavaScriptView Raw
1/**
2 * Copyright(c) Microsoft Corporation.All rights reserved.
3 * Licensed under the MIT License.
4 */
5const Table = require('cli-table2');
6const chalk = require('chalk');
7const path = require('path');
8const fs = require('fs');
9const txtfile = require('read-text-file');
10const manifest = require('./api/qnamaker');
11const windowSize = require('window-size');
12const { getServiceManifest } = require('../lib/utils/argsUtil');
13
14/**
15 * Displays help content from the arguments.
16 *
17 * @param args The arguments input by the user
18 * @returns {Promise<void>}
19 */
20module.exports = async function help(args, output) {
21 if (!output)
22 output = process.stderr;
23
24 output.write('QnA Maker Command line interface - © 2018 Microsoft Corporation\n\n');
25 const helpContents = await getHelpContents(args, output);
26 let width = windowSize ? windowSize.width : 250;
27
28 let leftColWidth = 0;
29 for (let hc of helpContents) {
30 if (hc.table && hc.table[0].length > 0) {
31 const rows = hc.table[0].length;
32 for (let row in hc.table) {
33 let len = hc.table[row][0].length;
34 if (len > leftColWidth) {
35 leftColWidth = Math.min(len, Math.floor(width / 3));
36 }
37 }
38 let i = rows - 1;
39 }
40 }
41
42 helpContents.forEach(helpContent => {
43 output.write(chalk.white.bold(helpContent.head + '\n'));
44 if (helpContent.table && helpContent.table[0].length > 0) {
45 const rows = helpContent.table[0].length;
46 let i = rows - 1;
47
48 const colWidthsFor2On = ((width * .85) - leftColWidth) / i;
49 const colWidths = [leftColWidth];
50
51 while (i--) {
52 colWidths.push(~~colWidthsFor2On);
53 }
54
55 const table = new Table({
56 // don't use lines for table
57 chars: {
58 'top': '', 'top-mid': '', 'top-left': '', 'top-right': '',
59 'bottom': '', 'bottom-mid': '', 'bottom-left': '', 'bottom-right': '',
60 'left': '', 'left-mid': '', 'right': '', 'right-mid': '',
61 'mid': '', 'mid-mid': '', 'middle': ''
62 },
63 colWidths,
64 style: { 'padding-left': 1, 'padding-right': 1 },
65 wordWrap: true
66 });
67 table.push(...helpContent.table);
68 output.write(table.toString());
69 }
70 output.write('\n\n');
71 });
72}
73
74/**
75 * Retrieves help content vie the qnamaker.json from
76 * the arguments input by the user.
77 *
78* @param args The arguments input by the user
79* @returns {Promise<*>}
80*/
81async function getHelpContents(args, output) {
82 if ('!' in args) {
83 return getAllCommands(output);
84 }
85
86 if (args._.length == 0) {
87 return getGeneralHelpContents(output);
88 }
89 else if (args._.length == 1) {
90 return getVerbHelp(args._[0], output);
91 } else if (args._.length >= 2) {
92 const serviceManifest = getServiceManifest(args);
93 if (serviceManifest) {
94 const { operation } = serviceManifest;
95
96 output.write(`${operation.description}\n\n`);
97 output.write(`Usage:\n${chalk.cyan.bold(operation.command)}\n\n`);
98 } else {
99 return getVerbHelp(args._[0], output);
100 }
101 }
102
103 const serviceManifest = getServiceManifest(args);
104 if (serviceManifest) {
105 return getHelpContentsForService(serviceManifest, output);
106 }
107
108 return getGeneralHelpContents(output);
109}
110
111
112let configSection = {
113 head: 'Configuration and Overrides:',
114 table: [
115 [chalk.cyan.bold('--subscriptionKey <key>'), 'Specifies the qnamaker subscription key/access keys (found on the Cognitive Services Azure portal page under "access keys"). Overrides the .qnamakerrc value and the QNAMAKER_SUBSCRIPTION_KEY environment variable.'],
116 [chalk.cyan.bold('--hostname <url>'), 'Specifies the url for your private QnA service. Overrides the .qnamakerrc value and the QNAMAKER_HOSTNAME environment variable.'],
117 [chalk.cyan.bold('--endpointKey <key>'), 'Specifies the endpoint key for your private QnA service.(from qnamaker.ai portal user settings page). Overrides the .qnamakerrc value and the QNAMAKER_ENDPOINTKEY environment variable.'],
118 [chalk.cyan.bold('--kbId <kbId>'), 'Specifies the active qnamaker knowledgebase id. Overrides the .qnamakerrc value and the QNAMAKER_KBID environment variable.'],
119 ]
120};
121
122let globalArgs = {
123 head: 'Global Arguments:',
124 table: [
125 [chalk.cyan.bold('--help, -h'), 'Prints this help file.'],
126 [chalk.cyan.bold('--version, -v'), 'Prints the version of this cli tool'],
127 [chalk.cyan.bold('--! '), 'Dumps all documented commands to the console with descriptions']
128 ]
129};
130
131/**
132 * General help contents
133 *
134 * @returns {*[]}
135 */
136function getGeneralHelpContents(output) {
137 let operation;
138 let verbs = [];
139 let options = {
140 head: chalk.bold(`Available actions are:`),
141 table: [
142 [chalk.cyan.bold("create"), "create a resource"],
143 [chalk.cyan.bold("delete"), "delete a resource"],
144 [chalk.cyan.bold("export"), "export resources"],
145 [chalk.cyan.bold("get"), "get a resource"],
146 [chalk.cyan.bold('init'), 'Initializes the .qnamakerrc file with settings'],
147 [chalk.cyan.bold("list"), "list resources"],
148 [chalk.cyan.bold("publish"), "publish resource"],
149 [chalk.cyan.bold("query"), "query model for prediction"],
150 [chalk.cyan.bold("refresh"), "refresh resources"],
151 [chalk.cyan.bold("set"), "change the .qnamakerrc settings"],
152 [chalk.cyan.bold("update"), "update resources"],
153 [chalk.cyan.bold("replace"), "replace a resource"]
154
155 ]
156 };
157
158 let sections = [];
159 sections.push(options);
160 sections.push(configSection);
161 sections.push(globalArgs);
162 return sections;
163}
164
165/**
166 * General verb help contents
167 *
168 * @returns {*[]}
169 */
170function getVerbHelp(verb, output) {
171 let operation;
172 let targets = [];
173 let options = {
174 head: `Available resources for ${chalk.bold(verb)}:`,
175 table: []
176 };
177
178 // special verbs
179 let sections = [];
180 switch (verb) {
181 case "query":
182 output.write(chalk.cyan.bold("qnamaker query --question <querytext>\n\n"));
183 options.table.push([chalk.cyan.bold("--question <query>"), "query to get a prediction for"]);
184 sections.push(options);
185 sections.push(configSection);
186 sections.push(globalArgs);
187 return sections;
188
189 case "set":
190 output.write(chalk.cyan.bold("qnamaker set <.qnamakerrcSetting> <value>\n\n"));
191 options.table.push([chalk.cyan.bold("kbid <kbid>"), "change the active knowledgebase id "]);
192 options.table.push([chalk.cyan.bold("subscriptionkey <subscriptionkey>"), "change the active subscriptionkey"]);
193 sections.push(options);
194 sections.push(globalArgs);
195 return sections;
196 case "init":
197 output.write(chalk.cyan.bold("qnamaker init\n\n"));
198 sections.push(globalArgs);
199 return sections;
200 }
201
202 for (let apiGroupName in manifest) {
203 const category = manifest[apiGroupName];
204
205 for (let iOperation in category.operations) {
206 let operation = category.operations[iOperation];
207 if (operation.methodAlias == verb) {
208 let target = operation.target[0];
209 if (targets.indexOf(target) < 0) {
210 targets.push(target);
211 }
212 }
213 }
214 }
215
216 if (targets.length == 0)
217 return getGeneralHelpContents(output);
218
219 targets.sort();
220 for (let verb of targets) {
221 options.table.push([chalk.cyan.bold(verb), '']);
222 }
223 sections.push(options);
224 sections.push(configSection);
225 sections.push(globalArgs);
226 return sections;
227}
228
229/**
230 * Walks the qnamaker.json and pulls out all
231 * commands that are supported.
232 *
233 * @returns {*[]}
234 */
235function getAllCommands() {
236 const table = [];
237 let resourceTypes = [];
238 let tables = {};
239 Object.keys(manifest).forEach(key => {
240 const { [key]: category } = manifest;
241 Object.keys(category.operations).forEach((operationKey, index) => {
242 let operation = category.operations[operationKey];
243 let opCategory = operation.target[0] || operation.methodAlias;
244 if (resourceTypes.indexOf(opCategory) < 0) {
245 resourceTypes.push(opCategory);
246 tables[opCategory] = [];
247 }
248 tables[opCategory].push([chalk.cyan.bold(operation.command), operation.description]);
249 });
250 });
251
252 resourceTypes.sort();
253
254 let sections = [];
255 for (resourceType of resourceTypes) {
256 tables[resourceType].sort((a, b) => a[0].localeCompare(b[0]));
257 sections.push({
258 head: chalk.white.bold(resourceType),
259 table: tables[resourceType]
260 });
261 }
262
263 return sections;
264}
265
266/**
267 * Gets the help content for a target or sub target.
268 *
269 * @param {*} serviceManifest The manifest entry containing the operations
270 * @param {string} categoryName The name of the category it belongs to
271 * @param {string} targetName The name of the target (if present)
272 * @param {string} subTargetName the name of the subTarget (if present)
273 *
274 * @returns {Array}
275 */
276function getHelpContentsForService(serviceManifest, output) {
277 const { operation } = serviceManifest;
278 const operations = serviceManifest.operation ? [operation] : serviceManifest.operations;
279
280 const sections = [];
281 // params table is shown only if we have a single
282 // operation with 1 or more params.
283 if (serviceManifest.operation) {
284 let paramsHelp = { head: '', table: [] };
285 if (serviceManifest.operation.params) {
286 const { params } = operation;
287 paramsHelp = {
288 head: `Command arguments are:`,
289 table: params.map(param => [chalk.cyan.bold(`--${param.alias || param.name} <${param.type}>${param.required ? ' (required)' : ''}`), param.description])
290 };
291 if (operation.entityName) {
292 paramsHelp.table.unshift([chalk.cyan.bold(`--in ${operation.entityType}.json`), `The ${operation.entityType} object to send in the body of the request`],
293 ['', chalk.dim(getEntityTypeExample(operation.entityType))]);
294 }
295 } else if (operation.entityName) {
296 paramsHelp = {
297 head: `Command arguments are:`,
298 table: [
299 [chalk.cyan.bold(`--in ${operation.entityType}.json`), `The ${operation.entityType} object to send in the body of the request`],
300 ['', chalk.dim(getEntityTypeExample(operation.entityType))]
301 ]
302 };
303 }
304 if (operation.name == 'createKnowledgebase' || operation.name == 'getKnowledgebaseDetails') {
305 paramsHelp.table.push([chalk.cyan.bold(`--msbot`), `(OPTIONAL) Format the output as json for piping into msbot connect qna command`]);
306 }
307 if (paramsHelp.table.length > 0)
308 sections.push(paramsHelp);
309 }
310 sections.push(configSection);
311 sections.push(globalArgs);
312 return sections;
313}
314
315
316function getEntityTypeExample(entityType) {
317 try {
318 var examplePath = path.join(__dirname, `../examples/${entityType}.json`);
319 let json = txtfile.readSync(examplePath).replace(/[\r\f]+/g, '\n');
320 return json;
321 } catch (error) {
322 return `{/*example for ${entityType} missing*/}`;
323 }
324}
\No newline at end of file