@microsoft/m365-spec-parser 0.1.1-alpha.a277dba4e.0 → 0.1.1-alpha.b015b287e.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.esm2017.js +170 -113
- package/dist/index.esm2017.js.map +1 -1
- package/dist/index.esm2017.mjs +214 -141
- package/dist/index.esm2017.mjs.map +1 -1
- package/dist/index.esm5.js +170 -113
- package/dist/index.esm5.js.map +1 -1
- package/dist/index.node.cjs.js +214 -141
- package/dist/index.node.cjs.js.map +1 -1
- package/dist/src/interfaces.d.ts +30 -1
- package/dist/src/manifestUpdater.d.ts +2 -1
- package/dist/src/specParser.browser.d.ts +1 -0
- package/dist/src/specParser.d.ts +1 -1
- package/dist/src/utils.d.ts +10 -9
- package/package.json +3 -3
package/dist/index.esm2017.mjs
CHANGED
|
@@ -29,6 +29,21 @@ var ErrorType;
|
|
|
29
29
|
ErrorType["GenerateFailed"] = "generate-failed";
|
|
30
30
|
ErrorType["ValidateFailed"] = "validate-failed";
|
|
31
31
|
ErrorType["GetSpecFailed"] = "get-spec-failed";
|
|
32
|
+
ErrorType["AuthTypeIsNotSupported"] = "auth-type-is-not-supported";
|
|
33
|
+
ErrorType["MissingOperationId"] = "missing-operation-id";
|
|
34
|
+
ErrorType["PostBodyContainMultipleMediaTypes"] = "post-body-contain-multiple-media-types";
|
|
35
|
+
ErrorType["ResponseContainMultipleMediaTypes"] = "response-contain-multiple-media-types";
|
|
36
|
+
ErrorType["ResponseJsonIsEmpty"] = "response-json-is-empty";
|
|
37
|
+
ErrorType["PostBodySchemaIsNotJson"] = "post-body-schema-is-not-json";
|
|
38
|
+
ErrorType["PostBodyContainsRequiredUnsupportedSchema"] = "post-body-contains-required-unsupported-schema";
|
|
39
|
+
ErrorType["ParamsContainRequiredUnsupportedSchema"] = "params-contain-required-unsupported-schema";
|
|
40
|
+
ErrorType["ParamsContainsNestedObject"] = "params-contains-nested-object";
|
|
41
|
+
ErrorType["RequestBodyContainsNestedObject"] = "request-body-contains-nested-object";
|
|
42
|
+
ErrorType["ExceededRequiredParamsLimit"] = "exceeded-required-params-limit";
|
|
43
|
+
ErrorType["NoParameter"] = "no-parameter";
|
|
44
|
+
ErrorType["NoAPIInfo"] = "no-api-info";
|
|
45
|
+
ErrorType["MethodNotAllowed"] = "method-not-allowed";
|
|
46
|
+
ErrorType["UrlPathNotExist"] = "url-path-not-exist";
|
|
32
47
|
ErrorType["Cancelled"] = "cancelled";
|
|
33
48
|
ErrorType["Unknown"] = "unknown";
|
|
34
49
|
})(ErrorType || (ErrorType = {}));
|
|
@@ -179,6 +194,7 @@ class Utils {
|
|
|
179
194
|
requiredNum: 0,
|
|
180
195
|
optionalNum: 0,
|
|
181
196
|
isValid: true,
|
|
197
|
+
reason: [],
|
|
182
198
|
};
|
|
183
199
|
if (!paramObject) {
|
|
184
200
|
return paramResult;
|
|
@@ -188,6 +204,7 @@ class Utils {
|
|
|
188
204
|
const schema = param.schema;
|
|
189
205
|
if (isCopilot && this.hasNestedObjectInSchema(schema)) {
|
|
190
206
|
paramResult.isValid = false;
|
|
207
|
+
paramResult.reason.push(ErrorType.ParamsContainsNestedObject);
|
|
191
208
|
continue;
|
|
192
209
|
}
|
|
193
210
|
const isRequiredWithoutDefault = param.required && schema.default === undefined;
|
|
@@ -203,6 +220,7 @@ class Utils {
|
|
|
203
220
|
if (param.in === "header" || param.in === "cookie") {
|
|
204
221
|
if (isRequiredWithoutDefault) {
|
|
205
222
|
paramResult.isValid = false;
|
|
223
|
+
paramResult.reason.push(ErrorType.ParamsContainRequiredUnsupportedSchema);
|
|
206
224
|
}
|
|
207
225
|
continue;
|
|
208
226
|
}
|
|
@@ -212,6 +230,7 @@ class Utils {
|
|
|
212
230
|
schema.type !== "integer") {
|
|
213
231
|
if (isRequiredWithoutDefault) {
|
|
214
232
|
paramResult.isValid = false;
|
|
233
|
+
paramResult.reason.push(ErrorType.ParamsContainRequiredUnsupportedSchema);
|
|
215
234
|
}
|
|
216
235
|
continue;
|
|
217
236
|
}
|
|
@@ -232,6 +251,7 @@ class Utils {
|
|
|
232
251
|
requiredNum: 0,
|
|
233
252
|
optionalNum: 0,
|
|
234
253
|
isValid: true,
|
|
254
|
+
reason: [],
|
|
235
255
|
};
|
|
236
256
|
if (Object.keys(schema).length === 0) {
|
|
237
257
|
return paramResult;
|
|
@@ -239,6 +259,7 @@ class Utils {
|
|
|
239
259
|
const isRequiredWithoutDefault = isRequired && schema.default === undefined;
|
|
240
260
|
if (isCopilot && this.hasNestedObjectInSchema(schema)) {
|
|
241
261
|
paramResult.isValid = false;
|
|
262
|
+
paramResult.reason = [ErrorType.RequestBodyContainsNestedObject];
|
|
242
263
|
return paramResult;
|
|
243
264
|
}
|
|
244
265
|
if (schema.type === "string" ||
|
|
@@ -263,11 +284,13 @@ class Utils {
|
|
|
263
284
|
paramResult.requiredNum += result.requiredNum;
|
|
264
285
|
paramResult.optionalNum += result.optionalNum;
|
|
265
286
|
paramResult.isValid = paramResult.isValid && result.isValid;
|
|
287
|
+
paramResult.reason.push(...result.reason);
|
|
266
288
|
}
|
|
267
289
|
}
|
|
268
290
|
else {
|
|
269
291
|
if (isRequiredWithoutDefault && !isCopilot) {
|
|
270
292
|
paramResult.isValid = false;
|
|
293
|
+
paramResult.reason.push(ErrorType.PostBodyContainsRequiredUnsupportedSchema);
|
|
271
294
|
}
|
|
272
295
|
}
|
|
273
296
|
return paramResult;
|
|
@@ -291,103 +314,123 @@ class Utils {
|
|
|
291
314
|
*/
|
|
292
315
|
static isSupportedApi(method, path, spec, options) {
|
|
293
316
|
var _a;
|
|
294
|
-
const
|
|
317
|
+
const result = { isValid: true, reason: [] };
|
|
295
318
|
method = method.toLocaleLowerCase();
|
|
296
|
-
if (
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
319
|
+
if (options.allowMethods && !options.allowMethods.includes(method)) {
|
|
320
|
+
result.isValid = false;
|
|
321
|
+
result.reason.push(ErrorType.MethodNotAllowed);
|
|
322
|
+
return result;
|
|
323
|
+
}
|
|
324
|
+
const pathObj = spec.paths[path];
|
|
325
|
+
if (!pathObj || !pathObj[method]) {
|
|
326
|
+
result.isValid = false;
|
|
327
|
+
result.reason.push(ErrorType.UrlPathNotExist);
|
|
328
|
+
return result;
|
|
329
|
+
}
|
|
330
|
+
const securities = pathObj[method].security;
|
|
331
|
+
const isTeamsAi = options.projectType === ProjectType.TeamsAi;
|
|
332
|
+
const isCopilot = options.projectType === ProjectType.Copilot;
|
|
333
|
+
// Teams AI project doesn't care about auth, it will use authProvider for user to implement
|
|
334
|
+
if (!isTeamsAi) {
|
|
335
|
+
const authArray = Utils.getAuthArray(securities, spec);
|
|
336
|
+
const authCheckResult = Utils.isSupportedAuth(authArray, options);
|
|
337
|
+
if (!authCheckResult.isValid) {
|
|
338
|
+
result.reason.push(...authCheckResult.reason);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
const operationObject = pathObj[method];
|
|
342
|
+
if (!options.allowMissingId && !operationObject.operationId) {
|
|
343
|
+
result.reason.push(ErrorType.MissingOperationId);
|
|
344
|
+
}
|
|
345
|
+
const rootServer = spec.servers && spec.servers[0];
|
|
346
|
+
const methodServer = spec.paths[path].servers && ((_a = spec.paths[path]) === null || _a === void 0 ? void 0 : _a.servers[0]);
|
|
347
|
+
const operationServer = operationObject.servers && operationObject.servers[0];
|
|
348
|
+
const serverUrl = operationServer || methodServer || rootServer;
|
|
349
|
+
if (!serverUrl) {
|
|
350
|
+
result.reason.push(ErrorType.NoServerInformation);
|
|
351
|
+
}
|
|
352
|
+
else {
|
|
353
|
+
const serverValidateResult = Utils.checkServerUrl([serverUrl]);
|
|
354
|
+
result.reason.push(...serverValidateResult.map((item) => item.type));
|
|
355
|
+
}
|
|
356
|
+
const paramObject = operationObject.parameters;
|
|
357
|
+
const requestBody = operationObject.requestBody;
|
|
358
|
+
const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
|
|
359
|
+
if (!isTeamsAi && Utils.containMultipleMediaTypes(requestBody)) {
|
|
360
|
+
result.reason.push(ErrorType.PostBodyContainMultipleMediaTypes);
|
|
361
|
+
}
|
|
362
|
+
const { json, multipleMediaType } = Utils.getResponseJson(operationObject, isTeamsAi);
|
|
363
|
+
if (multipleMediaType && !isTeamsAi) {
|
|
364
|
+
result.reason.push(ErrorType.ResponseContainMultipleMediaTypes);
|
|
365
|
+
}
|
|
366
|
+
else if (Object.keys(json).length === 0) {
|
|
367
|
+
result.reason.push(ErrorType.ResponseJsonIsEmpty);
|
|
368
|
+
}
|
|
369
|
+
// Teams AI project doesn't care about request parameters/body
|
|
370
|
+
if (!isTeamsAi) {
|
|
371
|
+
let requestBodyParamResult = {
|
|
372
|
+
requiredNum: 0,
|
|
373
|
+
optionalNum: 0,
|
|
374
|
+
isValid: true,
|
|
375
|
+
reason: [],
|
|
376
|
+
};
|
|
377
|
+
if (requestJsonBody) {
|
|
378
|
+
const requestBodySchema = requestJsonBody.schema;
|
|
379
|
+
if (isCopilot && requestBodySchema.type !== "object") {
|
|
380
|
+
result.reason.push(ErrorType.PostBodySchemaIsNotJson);
|
|
344
381
|
}
|
|
345
|
-
|
|
346
|
-
if (
|
|
347
|
-
|
|
382
|
+
requestBodyParamResult = Utils.checkPostBody(requestBodySchema, requestBody.required, isCopilot);
|
|
383
|
+
if (!requestBodyParamResult.isValid && requestBodyParamResult.reason) {
|
|
384
|
+
result.reason.push(...requestBodyParamResult.reason);
|
|
348
385
|
}
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
386
|
+
}
|
|
387
|
+
const paramResult = Utils.checkParameters(paramObject, isCopilot);
|
|
388
|
+
if (!paramResult.isValid && paramResult.reason) {
|
|
389
|
+
result.reason.push(...paramResult.reason);
|
|
390
|
+
}
|
|
391
|
+
// Copilot support arbitrary parameters
|
|
392
|
+
if (!isCopilot && paramResult.isValid && requestBodyParamResult.isValid) {
|
|
393
|
+
const totalRequiredParams = requestBodyParamResult.requiredNum + paramResult.requiredNum;
|
|
394
|
+
const totalParams = totalRequiredParams + requestBodyParamResult.optionalNum + paramResult.optionalNum;
|
|
395
|
+
if (totalRequiredParams > 1) {
|
|
396
|
+
if (!options.allowMultipleParameters ||
|
|
397
|
+
totalRequiredParams > ConstantString.SMERequiredParamsMaxNum) {
|
|
398
|
+
result.reason.push(ErrorType.ExceededRequiredParamsLimit);
|
|
354
399
|
}
|
|
355
|
-
return false;
|
|
356
|
-
}
|
|
357
|
-
else if (requestBodyParamResult.requiredNum +
|
|
358
|
-
requestBodyParamResult.optionalNum +
|
|
359
|
-
paramResult.requiredNum +
|
|
360
|
-
paramResult.optionalNum ===
|
|
361
|
-
0) {
|
|
362
|
-
return false;
|
|
363
400
|
}
|
|
364
|
-
else {
|
|
365
|
-
|
|
401
|
+
else if (totalParams === 0) {
|
|
402
|
+
result.reason.push(ErrorType.NoParameter);
|
|
366
403
|
}
|
|
367
404
|
}
|
|
368
405
|
}
|
|
369
|
-
|
|
406
|
+
if (result.reason.length > 0) {
|
|
407
|
+
result.isValid = false;
|
|
408
|
+
}
|
|
409
|
+
return result;
|
|
370
410
|
}
|
|
371
411
|
static isSupportedAuth(authSchemeArray, options) {
|
|
372
412
|
if (authSchemeArray.length === 0) {
|
|
373
|
-
return true;
|
|
413
|
+
return { isValid: true, reason: [] };
|
|
374
414
|
}
|
|
375
415
|
if (options.allowAPIKeyAuth || options.allowOauth2 || options.allowBearerTokenAuth) {
|
|
376
416
|
// Currently we don't support multiple auth in one operation
|
|
377
417
|
if (authSchemeArray.length > 0 && authSchemeArray.every((auths) => auths.length > 1)) {
|
|
378
|
-
return
|
|
418
|
+
return {
|
|
419
|
+
isValid: false,
|
|
420
|
+
reason: [ErrorType.MultipleAuthNotSupported],
|
|
421
|
+
};
|
|
379
422
|
}
|
|
380
423
|
for (const auths of authSchemeArray) {
|
|
381
424
|
if (auths.length === 1) {
|
|
382
425
|
if ((options.allowAPIKeyAuth && Utils.isAPIKeyAuth(auths[0].authScheme)) ||
|
|
383
426
|
(options.allowOauth2 && Utils.isOAuthWithAuthCodeFlow(auths[0].authScheme)) ||
|
|
384
427
|
(options.allowBearerTokenAuth && Utils.isBearerTokenAuth(auths[0].authScheme))) {
|
|
385
|
-
return true;
|
|
428
|
+
return { isValid: true, reason: [] };
|
|
386
429
|
}
|
|
387
430
|
}
|
|
388
431
|
}
|
|
389
432
|
}
|
|
390
|
-
return false;
|
|
433
|
+
return { isValid: false, reason: [ErrorType.AuthTypeIsNotSupported] };
|
|
391
434
|
}
|
|
392
435
|
static isBearerTokenAuth(authScheme) {
|
|
393
436
|
return authScheme.type === "http" && authScheme.scheme === "bearer";
|
|
@@ -430,11 +473,17 @@ class Utils {
|
|
|
430
473
|
static getResponseJson(operationObject, isTeamsAiProject = false) {
|
|
431
474
|
var _a, _b;
|
|
432
475
|
let json = {};
|
|
476
|
+
let multipleMediaType = false;
|
|
433
477
|
for (const code of ConstantString.ResponseCodeFor20X) {
|
|
434
478
|
const responseObject = (_a = operationObject === null || operationObject === void 0 ? void 0 : operationObject.responses) === null || _a === void 0 ? void 0 : _a[code];
|
|
435
479
|
if ((_b = responseObject === null || responseObject === void 0 ? void 0 : responseObject.content) === null || _b === void 0 ? void 0 : _b["application/json"]) {
|
|
480
|
+
multipleMediaType = false;
|
|
436
481
|
json = responseObject.content["application/json"];
|
|
437
|
-
if (
|
|
482
|
+
if (Utils.containMultipleMediaTypes(responseObject)) {
|
|
483
|
+
multipleMediaType = true;
|
|
484
|
+
if (isTeamsAiProject) {
|
|
485
|
+
break;
|
|
486
|
+
}
|
|
438
487
|
json = {};
|
|
439
488
|
}
|
|
440
489
|
else {
|
|
@@ -442,7 +491,7 @@ class Utils {
|
|
|
442
491
|
}
|
|
443
492
|
}
|
|
444
493
|
}
|
|
445
|
-
return json;
|
|
494
|
+
return { json, multipleMediaType };
|
|
446
495
|
}
|
|
447
496
|
static convertPathToCamelCase(path) {
|
|
448
497
|
const pathSegments = path.split(/[./{]/);
|
|
@@ -462,10 +511,10 @@ class Utils {
|
|
|
462
511
|
return undefined;
|
|
463
512
|
}
|
|
464
513
|
}
|
|
465
|
-
static
|
|
514
|
+
static resolveEnv(str) {
|
|
466
515
|
const placeHolderReg = /\${{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*}}/g;
|
|
467
|
-
let matches = placeHolderReg.exec(
|
|
468
|
-
let
|
|
516
|
+
let matches = placeHolderReg.exec(str);
|
|
517
|
+
let newStr = str;
|
|
469
518
|
while (matches != null) {
|
|
470
519
|
const envVar = matches[1];
|
|
471
520
|
const envVal = process.env[envVar];
|
|
@@ -473,17 +522,17 @@ class Utils {
|
|
|
473
522
|
throw new Error(Utils.format(ConstantString.ResolveServerUrlFailed, envVar));
|
|
474
523
|
}
|
|
475
524
|
else {
|
|
476
|
-
|
|
525
|
+
newStr = newStr.replace(matches[0], envVal);
|
|
477
526
|
}
|
|
478
|
-
matches = placeHolderReg.exec(
|
|
527
|
+
matches = placeHolderReg.exec(str);
|
|
479
528
|
}
|
|
480
|
-
return
|
|
529
|
+
return newStr;
|
|
481
530
|
}
|
|
482
531
|
static checkServerUrl(servers) {
|
|
483
532
|
const errors = [];
|
|
484
533
|
let serverUrl;
|
|
485
534
|
try {
|
|
486
|
-
serverUrl = Utils.
|
|
535
|
+
serverUrl = Utils.resolveEnv(servers[0].url);
|
|
487
536
|
}
|
|
488
537
|
catch (err) {
|
|
489
538
|
errors.push({
|
|
@@ -514,6 +563,7 @@ class Utils {
|
|
|
514
563
|
return errors;
|
|
515
564
|
}
|
|
516
565
|
static validateServer(spec, options) {
|
|
566
|
+
var _a;
|
|
517
567
|
const errors = [];
|
|
518
568
|
let hasTopLevelServers = false;
|
|
519
569
|
let hasPathLevelServers = false;
|
|
@@ -534,7 +584,7 @@ class Utils {
|
|
|
534
584
|
}
|
|
535
585
|
for (const method in methods) {
|
|
536
586
|
const operationObject = methods[method];
|
|
537
|
-
if (
|
|
587
|
+
if (((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && operationObject) {
|
|
538
588
|
if ((operationObject === null || operationObject === void 0 ? void 0 : operationObject.servers) && operationObject.servers.length >= 1) {
|
|
539
589
|
hasOperationLevelServers = true;
|
|
540
590
|
const serverErrors = Utils.checkServerUrl(operationObject.servers);
|
|
@@ -661,13 +711,7 @@ class Utils {
|
|
|
661
711
|
}
|
|
662
712
|
}
|
|
663
713
|
const operationId = operationItem.operationId;
|
|
664
|
-
const parameters = [];
|
|
665
|
-
if (requiredParams.length !== 0) {
|
|
666
|
-
parameters.push(...requiredParams);
|
|
667
|
-
}
|
|
668
|
-
else {
|
|
669
|
-
parameters.push(optionalParams[0]);
|
|
670
|
-
}
|
|
714
|
+
const parameters = [...requiredParams, ...optionalParams];
|
|
671
715
|
const command = {
|
|
672
716
|
context: ["compose"],
|
|
673
717
|
type: "query",
|
|
@@ -676,25 +720,23 @@ class Utils {
|
|
|
676
720
|
parameters: parameters,
|
|
677
721
|
description: ((_b = operationItem.description) !== null && _b !== void 0 ? _b : "").slice(0, ConstantString.CommandDescriptionMaxLens),
|
|
678
722
|
};
|
|
679
|
-
|
|
680
|
-
if (requiredParams.length === 0 && optionalParams.length > 1) {
|
|
681
|
-
warning = {
|
|
682
|
-
type: WarningType.OperationOnlyContainsOptionalParam,
|
|
683
|
-
content: Utils.format(ConstantString.OperationOnlyContainsOptionalParam, operationId),
|
|
684
|
-
data: operationId,
|
|
685
|
-
};
|
|
686
|
-
}
|
|
687
|
-
return [command, warning];
|
|
723
|
+
return command;
|
|
688
724
|
}
|
|
689
|
-
static
|
|
725
|
+
static listAPIs(spec, options) {
|
|
726
|
+
var _a;
|
|
690
727
|
const paths = spec.paths;
|
|
691
728
|
const result = {};
|
|
692
729
|
for (const path in paths) {
|
|
693
730
|
const methods = paths[path];
|
|
694
731
|
for (const method in methods) {
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
732
|
+
const operationObject = methods[method];
|
|
733
|
+
if (((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && operationObject) {
|
|
734
|
+
const validateResult = Utils.isSupportedApi(method, path, spec, options);
|
|
735
|
+
result[`${method.toUpperCase()} ${path}`] = {
|
|
736
|
+
operation: operationObject,
|
|
737
|
+
isValid: validateResult.isValid,
|
|
738
|
+
reason: validateResult.reason,
|
|
739
|
+
};
|
|
698
740
|
}
|
|
699
741
|
}
|
|
700
742
|
}
|
|
@@ -703,13 +745,13 @@ class Utils {
|
|
|
703
745
|
static validateSpec(spec, parser, isSwaggerFile, options) {
|
|
704
746
|
const errors = [];
|
|
705
747
|
const warnings = [];
|
|
748
|
+
const apiMap = Utils.listAPIs(spec, options);
|
|
706
749
|
if (isSwaggerFile) {
|
|
707
750
|
warnings.push({
|
|
708
751
|
type: WarningType.ConvertSwaggerToOpenAPI,
|
|
709
752
|
content: ConstantString.ConvertSwaggerToOpenAPI,
|
|
710
753
|
});
|
|
711
754
|
}
|
|
712
|
-
// Server validation
|
|
713
755
|
const serverErrors = Utils.validateServer(spec, options);
|
|
714
756
|
errors.push(...serverErrors);
|
|
715
757
|
// Remote reference not supported
|
|
@@ -723,8 +765,8 @@ class Utils {
|
|
|
723
765
|
});
|
|
724
766
|
}
|
|
725
767
|
// No supported API
|
|
726
|
-
const
|
|
727
|
-
if (
|
|
768
|
+
const validAPIs = Object.entries(apiMap).filter(([, value]) => value.isValid);
|
|
769
|
+
if (validAPIs.length === 0) {
|
|
728
770
|
errors.push({
|
|
729
771
|
type: ErrorType.NoSupportedApi,
|
|
730
772
|
content: ConstantString.NoSupportedApi,
|
|
@@ -733,8 +775,8 @@ class Utils {
|
|
|
733
775
|
// OperationId missing
|
|
734
776
|
const apisMissingOperationId = [];
|
|
735
777
|
for (const key in apiMap) {
|
|
736
|
-
const
|
|
737
|
-
if (!
|
|
778
|
+
const { operation } = apiMap[key];
|
|
779
|
+
if (!operation.operationId) {
|
|
738
780
|
apisMissingOperationId.push(key);
|
|
739
781
|
}
|
|
740
782
|
}
|
|
@@ -793,25 +835,32 @@ class Utils {
|
|
|
793
835
|
// Copyright (c) Microsoft Corporation.
|
|
794
836
|
class SpecFilter {
|
|
795
837
|
static specFilter(filter, unResolveSpec, resolvedSpec, options) {
|
|
838
|
+
var _a;
|
|
796
839
|
try {
|
|
797
840
|
const newSpec = Object.assign({}, unResolveSpec);
|
|
798
841
|
const newPaths = {};
|
|
799
842
|
for (const filterItem of filter) {
|
|
800
843
|
const [method, path] = filterItem.split(" ");
|
|
801
844
|
const methodName = method.toLowerCase();
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
845
|
+
const pathObj = (_a = resolvedSpec.paths) === null || _a === void 0 ? void 0 : _a[path];
|
|
846
|
+
if (ConstantString.AllOperationMethods.includes(methodName) &&
|
|
847
|
+
pathObj &&
|
|
848
|
+
pathObj[methodName]) {
|
|
849
|
+
const validateResult = Utils.isSupportedApi(methodName, path, resolvedSpec, options);
|
|
850
|
+
if (!validateResult.isValid) {
|
|
851
|
+
continue;
|
|
852
|
+
}
|
|
853
|
+
if (!newPaths[path]) {
|
|
854
|
+
newPaths[path] = Object.assign({}, unResolveSpec.paths[path]);
|
|
855
|
+
for (const m of ConstantString.AllOperationMethods) {
|
|
856
|
+
delete newPaths[path][m];
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
newPaths[path][methodName] = unResolveSpec.paths[path][methodName];
|
|
860
|
+
// Add the operationId if missing
|
|
861
|
+
if (!newPaths[path][methodName].operationId) {
|
|
862
|
+
newPaths[path][methodName].operationId = `${methodName}${Utils.convertPathToCamelCase(path)}`;
|
|
809
863
|
}
|
|
810
|
-
}
|
|
811
|
-
newPaths[path][methodName] = unResolveSpec.paths[path][methodName];
|
|
812
|
-
// Add the operationId if missing
|
|
813
|
-
if (!newPaths[path][methodName].operationId) {
|
|
814
|
-
newPaths[path][methodName].operationId = `${methodName}${Utils.convertPathToCamelCase(path)}`;
|
|
815
864
|
}
|
|
816
865
|
}
|
|
817
866
|
newSpec.paths = newPaths;
|
|
@@ -833,9 +882,10 @@ class ManifestUpdater {
|
|
|
833
882
|
pluginFile: apiPluginRelativePath,
|
|
834
883
|
},
|
|
835
884
|
];
|
|
885
|
+
const appName = this.removeEnvs(manifest.name.short);
|
|
836
886
|
ManifestUpdater.updateManifestDescription(manifest, spec);
|
|
837
887
|
const specRelativePath = ManifestUpdater.getRelativePath(manifestPath, outputSpecPath);
|
|
838
|
-
const apiPlugin = ManifestUpdater.generatePluginManifestSchema(spec, specRelativePath, options);
|
|
888
|
+
const apiPlugin = ManifestUpdater.generatePluginManifestSchema(spec, specRelativePath, appName, options);
|
|
839
889
|
return [manifest, apiPlugin];
|
|
840
890
|
}
|
|
841
891
|
static updateManifestDescription(manifest, spec) {
|
|
@@ -859,7 +909,7 @@ class ManifestUpdater {
|
|
|
859
909
|
}
|
|
860
910
|
return parameter;
|
|
861
911
|
}
|
|
862
|
-
static generatePluginManifestSchema(spec, specRelativePath, options) {
|
|
912
|
+
static generatePluginManifestSchema(spec, specRelativePath, appName, options) {
|
|
863
913
|
var _a, _b, _c;
|
|
864
914
|
const functions = [];
|
|
865
915
|
const functionNames = [];
|
|
@@ -924,7 +974,7 @@ class ManifestUpdater {
|
|
|
924
974
|
}
|
|
925
975
|
const apiPlugin = {
|
|
926
976
|
schema_version: "v2",
|
|
927
|
-
name_for_human:
|
|
977
|
+
name_for_human: appName,
|
|
928
978
|
description_for_human: (_c = spec.info.description) !== null && _c !== void 0 ? _c : "<Please add description of the plugin>",
|
|
929
979
|
functions: functions,
|
|
930
980
|
runtimes: [
|
|
@@ -1008,16 +1058,26 @@ class ManifestUpdater {
|
|
|
1008
1058
|
if ((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) {
|
|
1009
1059
|
const operationItem = operations[method];
|
|
1010
1060
|
if (operationItem) {
|
|
1011
|
-
const
|
|
1061
|
+
const command = Utils.parseApiInfo(operationItem, options);
|
|
1062
|
+
if (command.parameters &&
|
|
1063
|
+
command.parameters.length >= 1 &&
|
|
1064
|
+
command.parameters.some((param) => param.isRequired)) {
|
|
1065
|
+
command.parameters = command.parameters.filter((param) => param.isRequired);
|
|
1066
|
+
}
|
|
1067
|
+
else if (command.parameters && command.parameters.length > 0) {
|
|
1068
|
+
command.parameters = [command.parameters[0]];
|
|
1069
|
+
warnings.push({
|
|
1070
|
+
type: WarningType.OperationOnlyContainsOptionalParam,
|
|
1071
|
+
content: Utils.format(ConstantString.OperationOnlyContainsOptionalParam, command.id),
|
|
1072
|
+
data: command.id,
|
|
1073
|
+
});
|
|
1074
|
+
}
|
|
1012
1075
|
if (adaptiveCardFolder) {
|
|
1013
1076
|
const adaptiveCardPath = path.join(adaptiveCardFolder, command.id + ".json");
|
|
1014
1077
|
command.apiResponseRenderingTemplateFile = (await fs.pathExists(adaptiveCardPath))
|
|
1015
1078
|
? ManifestUpdater.getRelativePath(manifestPath, adaptiveCardPath)
|
|
1016
1079
|
: "";
|
|
1017
1080
|
}
|
|
1018
|
-
if (warning) {
|
|
1019
|
-
warnings.push(warning);
|
|
1020
|
-
}
|
|
1021
1081
|
commands.push(command);
|
|
1022
1082
|
}
|
|
1023
1083
|
}
|
|
@@ -1031,13 +1091,22 @@ class ManifestUpdater {
|
|
|
1031
1091
|
const relativePath = path.relative(path.dirname(from), to);
|
|
1032
1092
|
return path.normalize(relativePath).replace(/\\/g, "/");
|
|
1033
1093
|
}
|
|
1094
|
+
static removeEnvs(str) {
|
|
1095
|
+
const placeHolderReg = /\${{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*}}/g;
|
|
1096
|
+
const matches = placeHolderReg.exec(str);
|
|
1097
|
+
let newStr = str;
|
|
1098
|
+
if (matches != null) {
|
|
1099
|
+
newStr = newStr.replace(matches[0], "");
|
|
1100
|
+
}
|
|
1101
|
+
return newStr;
|
|
1102
|
+
}
|
|
1034
1103
|
}
|
|
1035
1104
|
|
|
1036
1105
|
// Copyright (c) Microsoft Corporation.
|
|
1037
1106
|
class AdaptiveCardGenerator {
|
|
1038
1107
|
static generateAdaptiveCard(operationItem) {
|
|
1039
1108
|
try {
|
|
1040
|
-
const json = Utils.getResponseJson(operationItem);
|
|
1109
|
+
const { json } = Utils.getResponseJson(operationItem);
|
|
1041
1110
|
let cardBody = [];
|
|
1042
1111
|
let schema = json.schema;
|
|
1043
1112
|
let jsonPath = "$";
|
|
@@ -1370,20 +1439,22 @@ class SpecParser {
|
|
|
1370
1439
|
try {
|
|
1371
1440
|
await this.loadSpec();
|
|
1372
1441
|
const spec = this.spec;
|
|
1373
|
-
const apiMap = this.
|
|
1442
|
+
const apiMap = this.getAPIs(spec);
|
|
1374
1443
|
const result = {
|
|
1375
|
-
|
|
1444
|
+
APIs: [],
|
|
1376
1445
|
allAPICount: 0,
|
|
1377
1446
|
validAPICount: 0,
|
|
1378
1447
|
};
|
|
1379
1448
|
for (const apiKey in apiMap) {
|
|
1449
|
+
const { operation, isValid, reason } = apiMap[apiKey];
|
|
1450
|
+
const [method, path] = apiKey.split(" ");
|
|
1380
1451
|
const apiResult = {
|
|
1381
1452
|
api: "",
|
|
1382
1453
|
server: "",
|
|
1383
1454
|
operationId: "",
|
|
1455
|
+
isValid: isValid,
|
|
1456
|
+
reason: reason,
|
|
1384
1457
|
};
|
|
1385
|
-
const [method, path] = apiKey.split(" ");
|
|
1386
|
-
const operation = apiMap[apiKey];
|
|
1387
1458
|
const rootServer = spec.servers && spec.servers[0];
|
|
1388
1459
|
const methodServer = spec.paths[path].servers && ((_a = spec.paths[path]) === null || _a === void 0 ? void 0 : _a.servers[0]);
|
|
1389
1460
|
const operationServer = operation.servers && operation.servers[0];
|
|
@@ -1391,7 +1462,7 @@ class SpecParser {
|
|
|
1391
1462
|
if (!serverUrl) {
|
|
1392
1463
|
throw new SpecParserError(ConstantString.NoServerInformation, ErrorType.NoServerInformation);
|
|
1393
1464
|
}
|
|
1394
|
-
apiResult.server = Utils.
|
|
1465
|
+
apiResult.server = Utils.resolveEnv(serverUrl.url);
|
|
1395
1466
|
let operationId = operation.operationId;
|
|
1396
1467
|
if (!operationId) {
|
|
1397
1468
|
operationId = `${method.toLowerCase()}${Utils.convertPathToCamelCase(path)}`;
|
|
@@ -1405,10 +1476,10 @@ class SpecParser {
|
|
|
1405
1476
|
}
|
|
1406
1477
|
}
|
|
1407
1478
|
apiResult.api = apiKey;
|
|
1408
|
-
result.
|
|
1479
|
+
result.APIs.push(apiResult);
|
|
1409
1480
|
}
|
|
1410
|
-
result.allAPICount =
|
|
1411
|
-
result.validAPICount = result.
|
|
1481
|
+
result.allAPICount = result.APIs.length;
|
|
1482
|
+
result.validAPICount = result.APIs.filter((api) => api.isValid).length;
|
|
1412
1483
|
return result;
|
|
1413
1484
|
}
|
|
1414
1485
|
catch (err) {
|
|
@@ -1500,15 +1571,18 @@ class SpecParser {
|
|
|
1500
1571
|
const newSpecs = await this.getFilteredSpecs(filter, signal);
|
|
1501
1572
|
const newUnResolvedSpec = newSpecs[0];
|
|
1502
1573
|
const newSpec = newSpecs[1];
|
|
1503
|
-
const authSet = new Set();
|
|
1504
1574
|
let hasMultipleAuth = false;
|
|
1575
|
+
let authInfo = undefined;
|
|
1505
1576
|
for (const url in newSpec.paths) {
|
|
1506
1577
|
for (const method in newSpec.paths[url]) {
|
|
1507
1578
|
const operation = newSpec.paths[url][method];
|
|
1508
1579
|
const authArray = Utils.getAuthArray(operation.security, newSpec);
|
|
1509
1580
|
if (authArray && authArray.length > 0) {
|
|
1510
|
-
|
|
1511
|
-
if (
|
|
1581
|
+
const currentAuth = authArray[0][0];
|
|
1582
|
+
if (!authInfo) {
|
|
1583
|
+
authInfo = authArray[0][0];
|
|
1584
|
+
}
|
|
1585
|
+
else if (authInfo.name !== currentAuth.name) {
|
|
1512
1586
|
hasMultipleAuth = true;
|
|
1513
1587
|
break;
|
|
1514
1588
|
}
|
|
@@ -1555,7 +1629,6 @@ class SpecParser {
|
|
|
1555
1629
|
if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
|
|
1556
1630
|
throw new SpecParserError(ConstantString.CancelledMessage, ErrorType.Cancelled);
|
|
1557
1631
|
}
|
|
1558
|
-
const authInfo = Array.from(authSet)[0];
|
|
1559
1632
|
const [updatedManifest, warnings] = await ManifestUpdater.updateManifest(manifestPath, outputSpecPath, newSpec, this.options, adaptiveCardFolder, authInfo);
|
|
1560
1633
|
await fs.outputJSON(manifestPath, updatedManifest, { spaces: 2 });
|
|
1561
1634
|
result.warnings.push(...warnings);
|
|
@@ -1581,11 +1654,11 @@ class SpecParser {
|
|
|
1581
1654
|
this.spec = (await this.parser.dereference(clonedUnResolveSpec));
|
|
1582
1655
|
}
|
|
1583
1656
|
}
|
|
1584
|
-
|
|
1657
|
+
getAPIs(spec) {
|
|
1585
1658
|
if (this.apiMap !== undefined) {
|
|
1586
1659
|
return this.apiMap;
|
|
1587
1660
|
}
|
|
1588
|
-
const result = Utils.
|
|
1661
|
+
const result = Utils.listAPIs(spec, this.options);
|
|
1589
1662
|
this.apiMap = result;
|
|
1590
1663
|
return result;
|
|
1591
1664
|
}
|