@microsoft/m365-spec-parser 0.1.1-alpha.a372ccf67.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 +204 -137
- package/dist/index.esm2017.js.map +1 -1
- package/dist/index.esm2017.mjs +271 -167
- package/dist/index.esm2017.mjs.map +1 -1
- package/dist/index.esm5.js +204 -137
- package/dist/index.esm5.js.map +1 -1
- package/dist/index.node.cjs.js +271 -167
- package/dist/index.node.cjs.js.map +1 -1
- package/dist/src/constants.d.ts +1 -0
- package/dist/src/interfaces.d.ts +44 -16
- package/dist/src/manifestUpdater.d.ts +2 -1
- package/dist/src/specParser.browser.d.ts +1 -0
- package/dist/src/specParser.d.ts +2 -2
- package/dist/src/utils.d.ts +17 -14
- package/package.json +3 -3
package/dist/index.esm2017.mjs
CHANGED
|
@@ -20,6 +20,7 @@ var ErrorType;
|
|
|
20
20
|
ErrorType["ResolveServerUrlFailed"] = "resolve-server-url-failed";
|
|
21
21
|
ErrorType["SwaggerNotSupported"] = "swagger-not-supported";
|
|
22
22
|
ErrorType["MultipleAuthNotSupported"] = "multiple-auth-not-supported";
|
|
23
|
+
ErrorType["SpecVersionNotSupported"] = "spec-version-not-supported";
|
|
23
24
|
ErrorType["ListFailed"] = "list-failed";
|
|
24
25
|
ErrorType["listSupportedAPIInfoFailed"] = "list-supported-api-info-failed";
|
|
25
26
|
ErrorType["FilterSpecFailed"] = "filter-spec-failed";
|
|
@@ -28,6 +29,21 @@ var ErrorType;
|
|
|
28
29
|
ErrorType["GenerateFailed"] = "generate-failed";
|
|
29
30
|
ErrorType["ValidateFailed"] = "validate-failed";
|
|
30
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";
|
|
31
47
|
ErrorType["Cancelled"] = "cancelled";
|
|
32
48
|
ErrorType["Unknown"] = "unknown";
|
|
33
49
|
})(ErrorType || (ErrorType = {}));
|
|
@@ -75,6 +91,7 @@ ConstantString.ResolveServerUrlFailed = "Unable to resolve the server URL: pleas
|
|
|
75
91
|
ConstantString.OperationOnlyContainsOptionalParam = "Operation %s contains multiple optional parameters. The first optional parameter is used for this command.";
|
|
76
92
|
ConstantString.ConvertSwaggerToOpenAPI = "The Swagger 2.0 file has been converted to OpenAPI 3.0.";
|
|
77
93
|
ConstantString.SwaggerNotSupported = "Swagger 2.0 is not supported. Please convert to OpenAPI 3.0 manually before proceeding.";
|
|
94
|
+
ConstantString.SpecVersionNotSupported = "Unsupported OpenAPI version %s. Please use version 3.0.x.";
|
|
78
95
|
ConstantString.MultipleAuthNotSupported = "Multiple authentication methods are unsupported. Ensure all selected APIs use identical authentication.";
|
|
79
96
|
ConstantString.UnsupportedSchema = "Unsupported schema in %s %s: %s";
|
|
80
97
|
ConstantString.WrappedCardVersion = "devPreview";
|
|
@@ -177,6 +194,7 @@ class Utils {
|
|
|
177
194
|
requiredNum: 0,
|
|
178
195
|
optionalNum: 0,
|
|
179
196
|
isValid: true,
|
|
197
|
+
reason: [],
|
|
180
198
|
};
|
|
181
199
|
if (!paramObject) {
|
|
182
200
|
return paramResult;
|
|
@@ -186,6 +204,7 @@ class Utils {
|
|
|
186
204
|
const schema = param.schema;
|
|
187
205
|
if (isCopilot && this.hasNestedObjectInSchema(schema)) {
|
|
188
206
|
paramResult.isValid = false;
|
|
207
|
+
paramResult.reason.push(ErrorType.ParamsContainsNestedObject);
|
|
189
208
|
continue;
|
|
190
209
|
}
|
|
191
210
|
const isRequiredWithoutDefault = param.required && schema.default === undefined;
|
|
@@ -201,6 +220,7 @@ class Utils {
|
|
|
201
220
|
if (param.in === "header" || param.in === "cookie") {
|
|
202
221
|
if (isRequiredWithoutDefault) {
|
|
203
222
|
paramResult.isValid = false;
|
|
223
|
+
paramResult.reason.push(ErrorType.ParamsContainRequiredUnsupportedSchema);
|
|
204
224
|
}
|
|
205
225
|
continue;
|
|
206
226
|
}
|
|
@@ -210,6 +230,7 @@ class Utils {
|
|
|
210
230
|
schema.type !== "integer") {
|
|
211
231
|
if (isRequiredWithoutDefault) {
|
|
212
232
|
paramResult.isValid = false;
|
|
233
|
+
paramResult.reason.push(ErrorType.ParamsContainRequiredUnsupportedSchema);
|
|
213
234
|
}
|
|
214
235
|
continue;
|
|
215
236
|
}
|
|
@@ -230,6 +251,7 @@ class Utils {
|
|
|
230
251
|
requiredNum: 0,
|
|
231
252
|
optionalNum: 0,
|
|
232
253
|
isValid: true,
|
|
254
|
+
reason: [],
|
|
233
255
|
};
|
|
234
256
|
if (Object.keys(schema).length === 0) {
|
|
235
257
|
return paramResult;
|
|
@@ -237,6 +259,7 @@ class Utils {
|
|
|
237
259
|
const isRequiredWithoutDefault = isRequired && schema.default === undefined;
|
|
238
260
|
if (isCopilot && this.hasNestedObjectInSchema(schema)) {
|
|
239
261
|
paramResult.isValid = false;
|
|
262
|
+
paramResult.reason = [ErrorType.RequestBodyContainsNestedObject];
|
|
240
263
|
return paramResult;
|
|
241
264
|
}
|
|
242
265
|
if (schema.type === "string" ||
|
|
@@ -261,11 +284,13 @@ class Utils {
|
|
|
261
284
|
paramResult.requiredNum += result.requiredNum;
|
|
262
285
|
paramResult.optionalNum += result.optionalNum;
|
|
263
286
|
paramResult.isValid = paramResult.isValid && result.isValid;
|
|
287
|
+
paramResult.reason.push(...result.reason);
|
|
264
288
|
}
|
|
265
289
|
}
|
|
266
290
|
else {
|
|
267
291
|
if (isRequiredWithoutDefault && !isCopilot) {
|
|
268
292
|
paramResult.isValid = false;
|
|
293
|
+
paramResult.reason.push(ErrorType.PostBodyContainsRequiredUnsupportedSchema);
|
|
269
294
|
}
|
|
270
295
|
}
|
|
271
296
|
return paramResult;
|
|
@@ -289,120 +314,132 @@ class Utils {
|
|
|
289
314
|
*/
|
|
290
315
|
static isSupportedApi(method, path, spec, options) {
|
|
291
316
|
var _a;
|
|
292
|
-
const
|
|
317
|
+
const result = { isValid: true, reason: [] };
|
|
293
318
|
method = method.toLocaleLowerCase();
|
|
294
|
-
if (
|
|
295
|
-
|
|
296
|
-
|
|
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
|
-
|
|
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);
|
|
342
381
|
}
|
|
343
|
-
|
|
344
|
-
if (
|
|
345
|
-
|
|
382
|
+
requestBodyParamResult = Utils.checkPostBody(requestBodySchema, requestBody.required, isCopilot);
|
|
383
|
+
if (!requestBodyParamResult.isValid && requestBodyParamResult.reason) {
|
|
384
|
+
result.reason.push(...requestBodyParamResult.reason);
|
|
346
385
|
}
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
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);
|
|
352
399
|
}
|
|
353
|
-
return false;
|
|
354
400
|
}
|
|
355
|
-
else if (
|
|
356
|
-
|
|
357
|
-
paramResult.requiredNum +
|
|
358
|
-
paramResult.optionalNum ===
|
|
359
|
-
0) {
|
|
360
|
-
return false;
|
|
361
|
-
}
|
|
362
|
-
else {
|
|
363
|
-
return true;
|
|
401
|
+
else if (totalParams === 0) {
|
|
402
|
+
result.reason.push(ErrorType.NoParameter);
|
|
364
403
|
}
|
|
365
404
|
}
|
|
366
405
|
}
|
|
367
|
-
|
|
406
|
+
if (result.reason.length > 0) {
|
|
407
|
+
result.isValid = false;
|
|
408
|
+
}
|
|
409
|
+
return result;
|
|
368
410
|
}
|
|
369
|
-
static isSupportedAuth(
|
|
370
|
-
if (
|
|
371
|
-
return true;
|
|
411
|
+
static isSupportedAuth(authSchemeArray, options) {
|
|
412
|
+
if (authSchemeArray.length === 0) {
|
|
413
|
+
return { isValid: true, reason: [] };
|
|
372
414
|
}
|
|
373
|
-
if (options.allowAPIKeyAuth || options.allowOauth2) {
|
|
415
|
+
if (options.allowAPIKeyAuth || options.allowOauth2 || options.allowBearerTokenAuth) {
|
|
374
416
|
// Currently we don't support multiple auth in one operation
|
|
375
|
-
if (
|
|
376
|
-
return
|
|
417
|
+
if (authSchemeArray.length > 0 && authSchemeArray.every((auths) => auths.length > 1)) {
|
|
418
|
+
return {
|
|
419
|
+
isValid: false,
|
|
420
|
+
reason: [ErrorType.MultipleAuthNotSupported],
|
|
421
|
+
};
|
|
377
422
|
}
|
|
378
|
-
for (const auths of
|
|
423
|
+
for (const auths of authSchemeArray) {
|
|
379
424
|
if (auths.length === 1) {
|
|
380
|
-
if (
|
|
381
|
-
options.
|
|
382
|
-
Utils.
|
|
383
|
-
return true;
|
|
384
|
-
}
|
|
385
|
-
else if (!options.allowAPIKeyAuth &&
|
|
386
|
-
options.allowOauth2 &&
|
|
387
|
-
Utils.isOAuthWithAuthCodeFlow(auths[0].authSchema)) {
|
|
388
|
-
return true;
|
|
389
|
-
}
|
|
390
|
-
else if (options.allowAPIKeyAuth &&
|
|
391
|
-
options.allowOauth2 &&
|
|
392
|
-
(Utils.isAPIKeyAuth(auths[0].authSchema) ||
|
|
393
|
-
Utils.isOAuthWithAuthCodeFlow(auths[0].authSchema))) {
|
|
394
|
-
return true;
|
|
425
|
+
if ((options.allowAPIKeyAuth && Utils.isAPIKeyAuth(auths[0].authScheme)) ||
|
|
426
|
+
(options.allowOauth2 && Utils.isOAuthWithAuthCodeFlow(auths[0].authScheme)) ||
|
|
427
|
+
(options.allowBearerTokenAuth && Utils.isBearerTokenAuth(auths[0].authScheme))) {
|
|
428
|
+
return { isValid: true, reason: [] };
|
|
395
429
|
}
|
|
396
430
|
}
|
|
397
431
|
}
|
|
398
432
|
}
|
|
399
|
-
return false;
|
|
433
|
+
return { isValid: false, reason: [ErrorType.AuthTypeIsNotSupported] };
|
|
434
|
+
}
|
|
435
|
+
static isBearerTokenAuth(authScheme) {
|
|
436
|
+
return authScheme.type === "http" && authScheme.scheme === "bearer";
|
|
400
437
|
}
|
|
401
|
-
static isAPIKeyAuth(
|
|
402
|
-
return
|
|
438
|
+
static isAPIKeyAuth(authScheme) {
|
|
439
|
+
return authScheme.type === "apiKey";
|
|
403
440
|
}
|
|
404
|
-
static isOAuthWithAuthCodeFlow(
|
|
405
|
-
if (
|
|
441
|
+
static isOAuthWithAuthCodeFlow(authScheme) {
|
|
442
|
+
if (authScheme.type === "oauth2" && authScheme.flows && authScheme.flows.authorizationCode) {
|
|
406
443
|
return true;
|
|
407
444
|
}
|
|
408
445
|
return false;
|
|
@@ -418,7 +455,7 @@ class Utils {
|
|
|
418
455
|
for (const name in security) {
|
|
419
456
|
const auth = securitySchemas[name];
|
|
420
457
|
authArray.push({
|
|
421
|
-
|
|
458
|
+
authScheme: auth,
|
|
422
459
|
name: name,
|
|
423
460
|
});
|
|
424
461
|
}
|
|
@@ -436,11 +473,17 @@ class Utils {
|
|
|
436
473
|
static getResponseJson(operationObject, isTeamsAiProject = false) {
|
|
437
474
|
var _a, _b;
|
|
438
475
|
let json = {};
|
|
476
|
+
let multipleMediaType = false;
|
|
439
477
|
for (const code of ConstantString.ResponseCodeFor20X) {
|
|
440
478
|
const responseObject = (_a = operationObject === null || operationObject === void 0 ? void 0 : operationObject.responses) === null || _a === void 0 ? void 0 : _a[code];
|
|
441
479
|
if ((_b = responseObject === null || responseObject === void 0 ? void 0 : responseObject.content) === null || _b === void 0 ? void 0 : _b["application/json"]) {
|
|
480
|
+
multipleMediaType = false;
|
|
442
481
|
json = responseObject.content["application/json"];
|
|
443
|
-
if (
|
|
482
|
+
if (Utils.containMultipleMediaTypes(responseObject)) {
|
|
483
|
+
multipleMediaType = true;
|
|
484
|
+
if (isTeamsAiProject) {
|
|
485
|
+
break;
|
|
486
|
+
}
|
|
444
487
|
json = {};
|
|
445
488
|
}
|
|
446
489
|
else {
|
|
@@ -448,7 +491,7 @@ class Utils {
|
|
|
448
491
|
}
|
|
449
492
|
}
|
|
450
493
|
}
|
|
451
|
-
return json;
|
|
494
|
+
return { json, multipleMediaType };
|
|
452
495
|
}
|
|
453
496
|
static convertPathToCamelCase(path) {
|
|
454
497
|
const pathSegments = path.split(/[./{]/);
|
|
@@ -468,10 +511,10 @@ class Utils {
|
|
|
468
511
|
return undefined;
|
|
469
512
|
}
|
|
470
513
|
}
|
|
471
|
-
static
|
|
514
|
+
static resolveEnv(str) {
|
|
472
515
|
const placeHolderReg = /\${{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*}}/g;
|
|
473
|
-
let matches = placeHolderReg.exec(
|
|
474
|
-
let
|
|
516
|
+
let matches = placeHolderReg.exec(str);
|
|
517
|
+
let newStr = str;
|
|
475
518
|
while (matches != null) {
|
|
476
519
|
const envVar = matches[1];
|
|
477
520
|
const envVal = process.env[envVar];
|
|
@@ -479,17 +522,17 @@ class Utils {
|
|
|
479
522
|
throw new Error(Utils.format(ConstantString.ResolveServerUrlFailed, envVar));
|
|
480
523
|
}
|
|
481
524
|
else {
|
|
482
|
-
|
|
525
|
+
newStr = newStr.replace(matches[0], envVal);
|
|
483
526
|
}
|
|
484
|
-
matches = placeHolderReg.exec(
|
|
527
|
+
matches = placeHolderReg.exec(str);
|
|
485
528
|
}
|
|
486
|
-
return
|
|
529
|
+
return newStr;
|
|
487
530
|
}
|
|
488
531
|
static checkServerUrl(servers) {
|
|
489
532
|
const errors = [];
|
|
490
533
|
let serverUrl;
|
|
491
534
|
try {
|
|
492
|
-
serverUrl = Utils.
|
|
535
|
+
serverUrl = Utils.resolveEnv(servers[0].url);
|
|
493
536
|
}
|
|
494
537
|
catch (err) {
|
|
495
538
|
errors.push({
|
|
@@ -520,6 +563,7 @@ class Utils {
|
|
|
520
563
|
return errors;
|
|
521
564
|
}
|
|
522
565
|
static validateServer(spec, options) {
|
|
566
|
+
var _a;
|
|
523
567
|
const errors = [];
|
|
524
568
|
let hasTopLevelServers = false;
|
|
525
569
|
let hasPathLevelServers = false;
|
|
@@ -540,7 +584,7 @@ class Utils {
|
|
|
540
584
|
}
|
|
541
585
|
for (const method in methods) {
|
|
542
586
|
const operationObject = methods[method];
|
|
543
|
-
if (
|
|
587
|
+
if (((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && operationObject) {
|
|
544
588
|
if ((operationObject === null || operationObject === void 0 ? void 0 : operationObject.servers) && operationObject.servers.length >= 1) {
|
|
545
589
|
hasOperationLevelServers = true;
|
|
546
590
|
const serverErrors = Utils.checkServerUrl(operationObject.servers);
|
|
@@ -583,6 +627,7 @@ class Utils {
|
|
|
583
627
|
Utils.updateParameterWithInputType(schema, parameter);
|
|
584
628
|
}
|
|
585
629
|
if (isRequired && schema.default === undefined) {
|
|
630
|
+
parameter.isRequired = true;
|
|
586
631
|
requiredParams.push(parameter);
|
|
587
632
|
}
|
|
588
633
|
else {
|
|
@@ -646,6 +691,7 @@ class Utils {
|
|
|
646
691
|
}
|
|
647
692
|
if (param.in !== "header" && param.in !== "cookie") {
|
|
648
693
|
if (param.required && (schema === null || schema === void 0 ? void 0 : schema.default) === undefined) {
|
|
694
|
+
parameter.isRequired = true;
|
|
649
695
|
requiredParams.push(parameter);
|
|
650
696
|
}
|
|
651
697
|
else {
|
|
@@ -665,13 +711,7 @@ class Utils {
|
|
|
665
711
|
}
|
|
666
712
|
}
|
|
667
713
|
const operationId = operationItem.operationId;
|
|
668
|
-
const parameters = [];
|
|
669
|
-
if (requiredParams.length !== 0) {
|
|
670
|
-
parameters.push(...requiredParams);
|
|
671
|
-
}
|
|
672
|
-
else {
|
|
673
|
-
parameters.push(optionalParams[0]);
|
|
674
|
-
}
|
|
714
|
+
const parameters = [...requiredParams, ...optionalParams];
|
|
675
715
|
const command = {
|
|
676
716
|
context: ["compose"],
|
|
677
717
|
type: "query",
|
|
@@ -680,25 +720,23 @@ class Utils {
|
|
|
680
720
|
parameters: parameters,
|
|
681
721
|
description: ((_b = operationItem.description) !== null && _b !== void 0 ? _b : "").slice(0, ConstantString.CommandDescriptionMaxLens),
|
|
682
722
|
};
|
|
683
|
-
|
|
684
|
-
if (requiredParams.length === 0 && optionalParams.length > 1) {
|
|
685
|
-
warning = {
|
|
686
|
-
type: WarningType.OperationOnlyContainsOptionalParam,
|
|
687
|
-
content: Utils.format(ConstantString.OperationOnlyContainsOptionalParam, operationId),
|
|
688
|
-
data: operationId,
|
|
689
|
-
};
|
|
690
|
-
}
|
|
691
|
-
return [command, warning];
|
|
723
|
+
return command;
|
|
692
724
|
}
|
|
693
|
-
static
|
|
725
|
+
static listAPIs(spec, options) {
|
|
726
|
+
var _a;
|
|
694
727
|
const paths = spec.paths;
|
|
695
728
|
const result = {};
|
|
696
729
|
for (const path in paths) {
|
|
697
730
|
const methods = paths[path];
|
|
698
731
|
for (const method in methods) {
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
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
|
+
};
|
|
702
740
|
}
|
|
703
741
|
}
|
|
704
742
|
}
|
|
@@ -707,13 +745,13 @@ class Utils {
|
|
|
707
745
|
static validateSpec(spec, parser, isSwaggerFile, options) {
|
|
708
746
|
const errors = [];
|
|
709
747
|
const warnings = [];
|
|
748
|
+
const apiMap = Utils.listAPIs(spec, options);
|
|
710
749
|
if (isSwaggerFile) {
|
|
711
750
|
warnings.push({
|
|
712
751
|
type: WarningType.ConvertSwaggerToOpenAPI,
|
|
713
752
|
content: ConstantString.ConvertSwaggerToOpenAPI,
|
|
714
753
|
});
|
|
715
754
|
}
|
|
716
|
-
// Server validation
|
|
717
755
|
const serverErrors = Utils.validateServer(spec, options);
|
|
718
756
|
errors.push(...serverErrors);
|
|
719
757
|
// Remote reference not supported
|
|
@@ -727,8 +765,8 @@ class Utils {
|
|
|
727
765
|
});
|
|
728
766
|
}
|
|
729
767
|
// No supported API
|
|
730
|
-
const
|
|
731
|
-
if (
|
|
768
|
+
const validAPIs = Object.entries(apiMap).filter(([, value]) => value.isValid);
|
|
769
|
+
if (validAPIs.length === 0) {
|
|
732
770
|
errors.push({
|
|
733
771
|
type: ErrorType.NoSupportedApi,
|
|
734
772
|
content: ConstantString.NoSupportedApi,
|
|
@@ -737,8 +775,8 @@ class Utils {
|
|
|
737
775
|
// OperationId missing
|
|
738
776
|
const apisMissingOperationId = [];
|
|
739
777
|
for (const key in apiMap) {
|
|
740
|
-
const
|
|
741
|
-
if (!
|
|
778
|
+
const { operation } = apiMap[key];
|
|
779
|
+
if (!operation.operationId) {
|
|
742
780
|
apisMissingOperationId.push(key);
|
|
743
781
|
}
|
|
744
782
|
}
|
|
@@ -779,30 +817,50 @@ class Utils {
|
|
|
779
817
|
}
|
|
780
818
|
return safeRegistrationIdEnvName;
|
|
781
819
|
}
|
|
820
|
+
static getAllAPICount(spec) {
|
|
821
|
+
let count = 0;
|
|
822
|
+
const paths = spec.paths;
|
|
823
|
+
for (const path in paths) {
|
|
824
|
+
const methods = paths[path];
|
|
825
|
+
for (const method in methods) {
|
|
826
|
+
if (ConstantString.AllOperationMethods.includes(method)) {
|
|
827
|
+
count++;
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
return count;
|
|
832
|
+
}
|
|
782
833
|
}
|
|
783
834
|
|
|
784
835
|
// Copyright (c) Microsoft Corporation.
|
|
785
836
|
class SpecFilter {
|
|
786
837
|
static specFilter(filter, unResolveSpec, resolvedSpec, options) {
|
|
838
|
+
var _a;
|
|
787
839
|
try {
|
|
788
840
|
const newSpec = Object.assign({}, unResolveSpec);
|
|
789
841
|
const newPaths = {};
|
|
790
842
|
for (const filterItem of filter) {
|
|
791
843
|
const [method, path] = filterItem.split(" ");
|
|
792
844
|
const methodName = method.toLowerCase();
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
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)}`;
|
|
800
863
|
}
|
|
801
|
-
}
|
|
802
|
-
newPaths[path][methodName] = unResolveSpec.paths[path][methodName];
|
|
803
|
-
// Add the operationId if missing
|
|
804
|
-
if (!newPaths[path][methodName].operationId) {
|
|
805
|
-
newPaths[path][methodName].operationId = `${methodName}${Utils.convertPathToCamelCase(path)}`;
|
|
806
864
|
}
|
|
807
865
|
}
|
|
808
866
|
newSpec.paths = newPaths;
|
|
@@ -824,9 +882,10 @@ class ManifestUpdater {
|
|
|
824
882
|
pluginFile: apiPluginRelativePath,
|
|
825
883
|
},
|
|
826
884
|
];
|
|
885
|
+
const appName = this.removeEnvs(manifest.name.short);
|
|
827
886
|
ManifestUpdater.updateManifestDescription(manifest, spec);
|
|
828
887
|
const specRelativePath = ManifestUpdater.getRelativePath(manifestPath, outputSpecPath);
|
|
829
|
-
const apiPlugin = ManifestUpdater.generatePluginManifestSchema(spec, specRelativePath, options);
|
|
888
|
+
const apiPlugin = ManifestUpdater.generatePluginManifestSchema(spec, specRelativePath, appName, options);
|
|
830
889
|
return [manifest, apiPlugin];
|
|
831
890
|
}
|
|
832
891
|
static updateManifestDescription(manifest, spec) {
|
|
@@ -850,7 +909,7 @@ class ManifestUpdater {
|
|
|
850
909
|
}
|
|
851
910
|
return parameter;
|
|
852
911
|
}
|
|
853
|
-
static generatePluginManifestSchema(spec, specRelativePath, options) {
|
|
912
|
+
static generatePluginManifestSchema(spec, specRelativePath, appName, options) {
|
|
854
913
|
var _a, _b, _c;
|
|
855
914
|
const functions = [];
|
|
856
915
|
const functionNames = [];
|
|
@@ -915,7 +974,7 @@ class ManifestUpdater {
|
|
|
915
974
|
}
|
|
916
975
|
const apiPlugin = {
|
|
917
976
|
schema_version: "v2",
|
|
918
|
-
name_for_human:
|
|
977
|
+
name_for_human: appName,
|
|
919
978
|
description_for_human: (_c = spec.info.description) !== null && _c !== void 0 ? _c : "<Please add description of the plugin>",
|
|
920
979
|
functions: functions,
|
|
921
980
|
runtimes: [
|
|
@@ -949,9 +1008,8 @@ class ManifestUpdater {
|
|
|
949
1008
|
commands: commands,
|
|
950
1009
|
};
|
|
951
1010
|
if (authInfo) {
|
|
952
|
-
|
|
953
|
-
if (Utils.isAPIKeyAuth(auth)) {
|
|
954
|
-
auth = auth;
|
|
1011
|
+
const auth = authInfo.authScheme;
|
|
1012
|
+
if (Utils.isAPIKeyAuth(auth) || Utils.isBearerTokenAuth(auth)) {
|
|
955
1013
|
const safeApiSecretRegistrationId = Utils.getSafeRegistrationIdEnvName(`${authInfo.name}_${ConstantString.RegistrationIdPostfix}`);
|
|
956
1014
|
composeExtension.authorization = {
|
|
957
1015
|
authType: "apiSecretServiceAuth",
|
|
@@ -1000,16 +1058,26 @@ class ManifestUpdater {
|
|
|
1000
1058
|
if ((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) {
|
|
1001
1059
|
const operationItem = operations[method];
|
|
1002
1060
|
if (operationItem) {
|
|
1003
|
-
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
|
+
}
|
|
1004
1075
|
if (adaptiveCardFolder) {
|
|
1005
1076
|
const adaptiveCardPath = path.join(adaptiveCardFolder, command.id + ".json");
|
|
1006
1077
|
command.apiResponseRenderingTemplateFile = (await fs.pathExists(adaptiveCardPath))
|
|
1007
1078
|
? ManifestUpdater.getRelativePath(manifestPath, adaptiveCardPath)
|
|
1008
1079
|
: "";
|
|
1009
1080
|
}
|
|
1010
|
-
if (warning) {
|
|
1011
|
-
warnings.push(warning);
|
|
1012
|
-
}
|
|
1013
1081
|
commands.push(command);
|
|
1014
1082
|
}
|
|
1015
1083
|
}
|
|
@@ -1023,13 +1091,22 @@ class ManifestUpdater {
|
|
|
1023
1091
|
const relativePath = path.relative(path.dirname(from), to);
|
|
1024
1092
|
return path.normalize(relativePath).replace(/\\/g, "/");
|
|
1025
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
|
+
}
|
|
1026
1103
|
}
|
|
1027
1104
|
|
|
1028
1105
|
// Copyright (c) Microsoft Corporation.
|
|
1029
1106
|
class AdaptiveCardGenerator {
|
|
1030
1107
|
static generateAdaptiveCard(operationItem) {
|
|
1031
1108
|
try {
|
|
1032
|
-
const json = Utils.getResponseJson(operationItem);
|
|
1109
|
+
const { json } = Utils.getResponseJson(operationItem);
|
|
1033
1110
|
let cardBody = [];
|
|
1034
1111
|
let schema = json.schema;
|
|
1035
1112
|
let jsonPath = "$";
|
|
@@ -1289,6 +1366,7 @@ class SpecParser {
|
|
|
1289
1366
|
allowMissingId: true,
|
|
1290
1367
|
allowSwagger: true,
|
|
1291
1368
|
allowAPIKeyAuth: false,
|
|
1369
|
+
allowBearerTokenAuth: false,
|
|
1292
1370
|
allowMultipleParameters: false,
|
|
1293
1371
|
allowOauth2: false,
|
|
1294
1372
|
allowMethods: ["get", "post"],
|
|
@@ -1325,6 +1403,22 @@ class SpecParser {
|
|
|
1325
1403
|
],
|
|
1326
1404
|
};
|
|
1327
1405
|
}
|
|
1406
|
+
if (this.options.projectType === ProjectType.SME ||
|
|
1407
|
+
this.options.projectType === ProjectType.Copilot) {
|
|
1408
|
+
if (this.spec.openapi >= "3.1.0") {
|
|
1409
|
+
return {
|
|
1410
|
+
status: ValidationStatus.Error,
|
|
1411
|
+
warnings: [],
|
|
1412
|
+
errors: [
|
|
1413
|
+
{
|
|
1414
|
+
type: ErrorType.SpecVersionNotSupported,
|
|
1415
|
+
content: Utils.format(ConstantString.SpecVersionNotSupported, this.spec.openapi),
|
|
1416
|
+
data: this.spec.openapi,
|
|
1417
|
+
},
|
|
1418
|
+
],
|
|
1419
|
+
};
|
|
1420
|
+
}
|
|
1421
|
+
}
|
|
1328
1422
|
return Utils.validateSpec(this.spec, this.parser, !!this.isSwaggerFile, this.options);
|
|
1329
1423
|
}
|
|
1330
1424
|
catch (err) {
|
|
@@ -1345,16 +1439,22 @@ class SpecParser {
|
|
|
1345
1439
|
try {
|
|
1346
1440
|
await this.loadSpec();
|
|
1347
1441
|
const spec = this.spec;
|
|
1348
|
-
const apiMap = this.
|
|
1349
|
-
const result =
|
|
1442
|
+
const apiMap = this.getAPIs(spec);
|
|
1443
|
+
const result = {
|
|
1444
|
+
APIs: [],
|
|
1445
|
+
allAPICount: 0,
|
|
1446
|
+
validAPICount: 0,
|
|
1447
|
+
};
|
|
1350
1448
|
for (const apiKey in apiMap) {
|
|
1449
|
+
const { operation, isValid, reason } = apiMap[apiKey];
|
|
1450
|
+
const [method, path] = apiKey.split(" ");
|
|
1351
1451
|
const apiResult = {
|
|
1352
1452
|
api: "",
|
|
1353
1453
|
server: "",
|
|
1354
1454
|
operationId: "",
|
|
1455
|
+
isValid: isValid,
|
|
1456
|
+
reason: reason,
|
|
1355
1457
|
};
|
|
1356
|
-
const [method, path] = apiKey.split(" ");
|
|
1357
|
-
const operation = apiMap[apiKey];
|
|
1358
1458
|
const rootServer = spec.servers && spec.servers[0];
|
|
1359
1459
|
const methodServer = spec.paths[path].servers && ((_a = spec.paths[path]) === null || _a === void 0 ? void 0 : _a.servers[0]);
|
|
1360
1460
|
const operationServer = operation.servers && operation.servers[0];
|
|
@@ -1362,7 +1462,7 @@ class SpecParser {
|
|
|
1362
1462
|
if (!serverUrl) {
|
|
1363
1463
|
throw new SpecParserError(ConstantString.NoServerInformation, ErrorType.NoServerInformation);
|
|
1364
1464
|
}
|
|
1365
|
-
apiResult.server = Utils.
|
|
1465
|
+
apiResult.server = Utils.resolveEnv(serverUrl.url);
|
|
1366
1466
|
let operationId = operation.operationId;
|
|
1367
1467
|
if (!operationId) {
|
|
1368
1468
|
operationId = `${method.toLowerCase()}${Utils.convertPathToCamelCase(path)}`;
|
|
@@ -1371,13 +1471,15 @@ class SpecParser {
|
|
|
1371
1471
|
const authArray = Utils.getAuthArray(operation.security, spec);
|
|
1372
1472
|
for (const auths of authArray) {
|
|
1373
1473
|
if (auths.length === 1) {
|
|
1374
|
-
apiResult.auth = auths[0]
|
|
1474
|
+
apiResult.auth = auths[0];
|
|
1375
1475
|
break;
|
|
1376
1476
|
}
|
|
1377
1477
|
}
|
|
1378
1478
|
apiResult.api = apiKey;
|
|
1379
|
-
result.push(apiResult);
|
|
1479
|
+
result.APIs.push(apiResult);
|
|
1380
1480
|
}
|
|
1481
|
+
result.allAPICount = result.APIs.length;
|
|
1482
|
+
result.validAPICount = result.APIs.filter((api) => api.isValid).length;
|
|
1381
1483
|
return result;
|
|
1382
1484
|
}
|
|
1383
1485
|
catch (err) {
|
|
@@ -1469,15 +1571,18 @@ class SpecParser {
|
|
|
1469
1571
|
const newSpecs = await this.getFilteredSpecs(filter, signal);
|
|
1470
1572
|
const newUnResolvedSpec = newSpecs[0];
|
|
1471
1573
|
const newSpec = newSpecs[1];
|
|
1472
|
-
const authSet = new Set();
|
|
1473
1574
|
let hasMultipleAuth = false;
|
|
1575
|
+
let authInfo = undefined;
|
|
1474
1576
|
for (const url in newSpec.paths) {
|
|
1475
1577
|
for (const method in newSpec.paths[url]) {
|
|
1476
1578
|
const operation = newSpec.paths[url][method];
|
|
1477
1579
|
const authArray = Utils.getAuthArray(operation.security, newSpec);
|
|
1478
1580
|
if (authArray && authArray.length > 0) {
|
|
1479
|
-
|
|
1480
|
-
if (
|
|
1581
|
+
const currentAuth = authArray[0][0];
|
|
1582
|
+
if (!authInfo) {
|
|
1583
|
+
authInfo = authArray[0][0];
|
|
1584
|
+
}
|
|
1585
|
+
else if (authInfo.name !== currentAuth.name) {
|
|
1481
1586
|
hasMultipleAuth = true;
|
|
1482
1587
|
break;
|
|
1483
1588
|
}
|
|
@@ -1524,7 +1629,6 @@ class SpecParser {
|
|
|
1524
1629
|
if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
|
|
1525
1630
|
throw new SpecParserError(ConstantString.CancelledMessage, ErrorType.Cancelled);
|
|
1526
1631
|
}
|
|
1527
|
-
const authInfo = Array.from(authSet)[0];
|
|
1528
1632
|
const [updatedManifest, warnings] = await ManifestUpdater.updateManifest(manifestPath, outputSpecPath, newSpec, this.options, adaptiveCardFolder, authInfo);
|
|
1529
1633
|
await fs.outputJSON(manifestPath, updatedManifest, { spaces: 2 });
|
|
1530
1634
|
result.warnings.push(...warnings);
|
|
@@ -1550,11 +1654,11 @@ class SpecParser {
|
|
|
1550
1654
|
this.spec = (await this.parser.dereference(clonedUnResolveSpec));
|
|
1551
1655
|
}
|
|
1552
1656
|
}
|
|
1553
|
-
|
|
1657
|
+
getAPIs(spec) {
|
|
1554
1658
|
if (this.apiMap !== undefined) {
|
|
1555
1659
|
return this.apiMap;
|
|
1556
1660
|
}
|
|
1557
|
-
const result = Utils.
|
|
1661
|
+
const result = Utils.listAPIs(spec, this.options);
|
|
1558
1662
|
this.apiMap = result;
|
|
1559
1663
|
return result;
|
|
1560
1664
|
}
|