@microsoft/m365-spec-parser 0.1.1-alpha.2f5decfcc.0 → 0.1.1-alpha.42af26ca6.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.
@@ -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";
@@ -172,221 +189,9 @@ class Utils {
172
189
  }
173
190
  return false;
174
191
  }
175
- static checkParameters(paramObject, isCopilot) {
176
- const paramResult = {
177
- requiredNum: 0,
178
- optionalNum: 0,
179
- isValid: true,
180
- };
181
- if (!paramObject) {
182
- return paramResult;
183
- }
184
- for (let i = 0; i < paramObject.length; i++) {
185
- const param = paramObject[i];
186
- const schema = param.schema;
187
- if (isCopilot && this.hasNestedObjectInSchema(schema)) {
188
- paramResult.isValid = false;
189
- continue;
190
- }
191
- const isRequiredWithoutDefault = param.required && schema.default === undefined;
192
- if (isCopilot) {
193
- if (isRequiredWithoutDefault) {
194
- paramResult.requiredNum = paramResult.requiredNum + 1;
195
- }
196
- else {
197
- paramResult.optionalNum = paramResult.optionalNum + 1;
198
- }
199
- continue;
200
- }
201
- if (param.in === "header" || param.in === "cookie") {
202
- if (isRequiredWithoutDefault) {
203
- paramResult.isValid = false;
204
- }
205
- continue;
206
- }
207
- if (schema.type !== "boolean" &&
208
- schema.type !== "string" &&
209
- schema.type !== "number" &&
210
- schema.type !== "integer") {
211
- if (isRequiredWithoutDefault) {
212
- paramResult.isValid = false;
213
- }
214
- continue;
215
- }
216
- if (param.in === "query" || param.in === "path") {
217
- if (isRequiredWithoutDefault) {
218
- paramResult.requiredNum = paramResult.requiredNum + 1;
219
- }
220
- else {
221
- paramResult.optionalNum = paramResult.optionalNum + 1;
222
- }
223
- }
224
- }
225
- return paramResult;
226
- }
227
- static checkPostBody(schema, isRequired = false, isCopilot = false) {
228
- var _a;
229
- const paramResult = {
230
- requiredNum: 0,
231
- optionalNum: 0,
232
- isValid: true,
233
- };
234
- if (Object.keys(schema).length === 0) {
235
- return paramResult;
236
- }
237
- const isRequiredWithoutDefault = isRequired && schema.default === undefined;
238
- if (isCopilot && this.hasNestedObjectInSchema(schema)) {
239
- paramResult.isValid = false;
240
- return paramResult;
241
- }
242
- if (schema.type === "string" ||
243
- schema.type === "integer" ||
244
- schema.type === "boolean" ||
245
- schema.type === "number") {
246
- if (isRequiredWithoutDefault) {
247
- paramResult.requiredNum = paramResult.requiredNum + 1;
248
- }
249
- else {
250
- paramResult.optionalNum = paramResult.optionalNum + 1;
251
- }
252
- }
253
- else if (schema.type === "object") {
254
- const { properties } = schema;
255
- for (const property in properties) {
256
- let isRequired = false;
257
- if (schema.required && ((_a = schema.required) === null || _a === void 0 ? void 0 : _a.indexOf(property)) >= 0) {
258
- isRequired = true;
259
- }
260
- const result = Utils.checkPostBody(properties[property], isRequired, isCopilot);
261
- paramResult.requiredNum += result.requiredNum;
262
- paramResult.optionalNum += result.optionalNum;
263
- paramResult.isValid = paramResult.isValid && result.isValid;
264
- }
265
- }
266
- else {
267
- if (isRequiredWithoutDefault && !isCopilot) {
268
- paramResult.isValid = false;
269
- }
270
- }
271
- return paramResult;
272
- }
273
192
  static containMultipleMediaTypes(bodyObject) {
274
193
  return Object.keys((bodyObject === null || bodyObject === void 0 ? void 0 : bodyObject.content) || {}).length > 1;
275
194
  }
276
- /**
277
- * Checks if the given API is supported.
278
- * @param {string} method - The HTTP method of the API.
279
- * @param {string} path - The path of the API.
280
- * @param {OpenAPIV3.Document} spec - The OpenAPI specification document.
281
- * @returns {boolean} - Returns true if the API is supported, false otherwise.
282
- * @description The following APIs are supported:
283
- * 1. only support Get/Post operation without auth property
284
- * 2. parameter inside query or path only support string, number, boolean and integer
285
- * 3. parameter inside post body only support string, number, boolean, integer and object
286
- * 4. request body + required parameters <= 1
287
- * 5. response body should be “application/json” and not empty, and response code should be 20X
288
- * 6. only support request body with “application/json” content type
289
- */
290
- static isSupportedApi(method, path, spec, options) {
291
- var _a;
292
- const pathObj = spec.paths[path];
293
- method = method.toLocaleLowerCase();
294
- if (pathObj) {
295
- if (((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && pathObj[method]) {
296
- const securities = pathObj[method].security;
297
- const isTeamsAi = options.projectType === ProjectType.TeamsAi;
298
- const isCopilot = options.projectType === ProjectType.Copilot;
299
- // Teams AI project doesn't care about auth, it will use authProvider for user to implement
300
- if (!isTeamsAi) {
301
- const authArray = Utils.getAuthArray(securities, spec);
302
- if (!Utils.isSupportedAuth(authArray, options)) {
303
- return false;
304
- }
305
- }
306
- const operationObject = pathObj[method];
307
- if (!options.allowMissingId && !operationObject.operationId) {
308
- return false;
309
- }
310
- const paramObject = operationObject.parameters;
311
- const requestBody = operationObject.requestBody;
312
- const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
313
- if (!isTeamsAi && Utils.containMultipleMediaTypes(requestBody)) {
314
- return false;
315
- }
316
- const responseJson = Utils.getResponseJson(operationObject, isTeamsAi);
317
- if (Object.keys(responseJson).length === 0) {
318
- return false;
319
- }
320
- // Teams AI project doesn't care about request parameters/body
321
- if (isTeamsAi) {
322
- return true;
323
- }
324
- let requestBodyParamResult = {
325
- requiredNum: 0,
326
- optionalNum: 0,
327
- isValid: true,
328
- };
329
- if (requestJsonBody) {
330
- const requestBodySchema = requestJsonBody.schema;
331
- if (isCopilot && requestBodySchema.type !== "object") {
332
- return false;
333
- }
334
- requestBodyParamResult = Utils.checkPostBody(requestBodySchema, requestBody.required, isCopilot);
335
- }
336
- if (!requestBodyParamResult.isValid) {
337
- return false;
338
- }
339
- const paramResult = Utils.checkParameters(paramObject, isCopilot);
340
- if (!paramResult.isValid) {
341
- return false;
342
- }
343
- // Copilot support arbitrary parameters
344
- if (isCopilot) {
345
- return true;
346
- }
347
- if (requestBodyParamResult.requiredNum + paramResult.requiredNum > 1) {
348
- if (options.allowMultipleParameters &&
349
- requestBodyParamResult.requiredNum + paramResult.requiredNum <=
350
- ConstantString.SMERequiredParamsMaxNum) {
351
- return true;
352
- }
353
- return false;
354
- }
355
- else if (requestBodyParamResult.requiredNum +
356
- requestBodyParamResult.optionalNum +
357
- paramResult.requiredNum +
358
- paramResult.optionalNum ===
359
- 0) {
360
- return false;
361
- }
362
- else {
363
- return true;
364
- }
365
- }
366
- }
367
- return false;
368
- }
369
- static isSupportedAuth(authSchemeArray, options) {
370
- if (authSchemeArray.length === 0) {
371
- return true;
372
- }
373
- if (options.allowAPIKeyAuth || options.allowOauth2 || options.allowBearerTokenAuth) {
374
- // Currently we don't support multiple auth in one operation
375
- if (authSchemeArray.length > 0 && authSchemeArray.every((auths) => auths.length > 1)) {
376
- return false;
377
- }
378
- for (const auths of authSchemeArray) {
379
- if (auths.length === 1) {
380
- if ((options.allowAPIKeyAuth && Utils.isAPIKeyAuth(auths[0].authScheme)) ||
381
- (options.allowOauth2 && Utils.isOAuthWithAuthCodeFlow(auths[0].authScheme)) ||
382
- (options.allowBearerTokenAuth && Utils.isBearerTokenAuth(auths[0].authScheme))) {
383
- return true;
384
- }
385
- }
386
- }
387
- }
388
- return false;
389
- }
390
195
  static isBearerTokenAuth(authScheme) {
391
196
  return authScheme.type === "http" && authScheme.scheme === "bearer";
392
197
  }
@@ -394,10 +199,9 @@ class Utils {
394
199
  return authScheme.type === "apiKey";
395
200
  }
396
201
  static isOAuthWithAuthCodeFlow(authScheme) {
397
- if (authScheme.type === "oauth2" && authScheme.flows && authScheme.flows.authorizationCode) {
398
- return true;
399
- }
400
- return false;
202
+ return !!(authScheme.type === "oauth2" &&
203
+ authScheme.flows &&
204
+ authScheme.flows.authorizationCode);
401
205
  }
402
206
  static getAuthArray(securities, spec) {
403
207
  var _a;
@@ -425,14 +229,17 @@ class Utils {
425
229
  static updateFirstLetter(str) {
426
230
  return str.charAt(0).toUpperCase() + str.slice(1);
427
231
  }
428
- static getResponseJson(operationObject, isTeamsAiProject = false) {
232
+ static getResponseJson(operationObject) {
429
233
  var _a, _b;
430
234
  let json = {};
235
+ let multipleMediaType = false;
431
236
  for (const code of ConstantString.ResponseCodeFor20X) {
432
237
  const responseObject = (_a = operationObject === null || operationObject === void 0 ? void 0 : operationObject.responses) === null || _a === void 0 ? void 0 : _a[code];
433
238
  if ((_b = responseObject === null || responseObject === void 0 ? void 0 : responseObject.content) === null || _b === void 0 ? void 0 : _b["application/json"]) {
239
+ multipleMediaType = false;
434
240
  json = responseObject.content["application/json"];
435
- if (!isTeamsAiProject && Utils.containMultipleMediaTypes(responseObject)) {
241
+ if (Utils.containMultipleMediaTypes(responseObject)) {
242
+ multipleMediaType = true;
436
243
  json = {};
437
244
  }
438
245
  else {
@@ -440,7 +247,7 @@ class Utils {
440
247
  }
441
248
  }
442
249
  }
443
- return json;
250
+ return { json, multipleMediaType };
444
251
  }
445
252
  static convertPathToCamelCase(path) {
446
253
  const pathSegments = path.split(/[./{]/);
@@ -460,10 +267,10 @@ class Utils {
460
267
  return undefined;
461
268
  }
462
269
  }
463
- static resolveServerUrl(url) {
270
+ static resolveEnv(str) {
464
271
  const placeHolderReg = /\${{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*}}/g;
465
- let matches = placeHolderReg.exec(url);
466
- let newUrl = url;
272
+ let matches = placeHolderReg.exec(str);
273
+ let newStr = str;
467
274
  while (matches != null) {
468
275
  const envVar = matches[1];
469
276
  const envVal = process.env[envVar];
@@ -471,17 +278,17 @@ class Utils {
471
278
  throw new Error(Utils.format(ConstantString.ResolveServerUrlFailed, envVar));
472
279
  }
473
280
  else {
474
- newUrl = newUrl.replace(matches[0], envVal);
281
+ newStr = newStr.replace(matches[0], envVal);
475
282
  }
476
- matches = placeHolderReg.exec(url);
283
+ matches = placeHolderReg.exec(str);
477
284
  }
478
- return newUrl;
285
+ return newStr;
479
286
  }
480
287
  static checkServerUrl(servers) {
481
288
  const errors = [];
482
289
  let serverUrl;
483
290
  try {
484
- serverUrl = Utils.resolveServerUrl(servers[0].url);
291
+ serverUrl = Utils.resolveEnv(servers[0].url);
485
292
  }
486
293
  catch (err) {
487
294
  errors.push({
@@ -512,6 +319,7 @@ class Utils {
512
319
  return errors;
513
320
  }
514
321
  static validateServer(spec, options) {
322
+ var _a;
515
323
  const errors = [];
516
324
  let hasTopLevelServers = false;
517
325
  let hasPathLevelServers = false;
@@ -532,7 +340,7 @@ class Utils {
532
340
  }
533
341
  for (const method in methods) {
534
342
  const operationObject = methods[method];
535
- if (Utils.isSupportedApi(method, path, spec, options)) {
343
+ if (((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && operationObject) {
536
344
  if ((operationObject === null || operationObject === void 0 ? void 0 : operationObject.servers) && operationObject.servers.length >= 1) {
537
345
  hasOperationLevelServers = true;
538
346
  const serverErrors = Utils.checkServerUrl(operationObject.servers);
@@ -659,13 +467,7 @@ class Utils {
659
467
  }
660
468
  }
661
469
  const operationId = operationItem.operationId;
662
- const parameters = [];
663
- if (requiredParams.length !== 0) {
664
- parameters.push(...requiredParams);
665
- }
666
- else {
667
- parameters.push(optionalParams[0]);
668
- }
470
+ const parameters = [...requiredParams, ...optionalParams];
669
471
  const command = {
670
472
  context: ["compose"],
671
473
  type: "query",
@@ -674,142 +476,559 @@ class Utils {
674
476
  parameters: parameters,
675
477
  description: ((_b = operationItem.description) !== null && _b !== void 0 ? _b : "").slice(0, ConstantString.CommandDescriptionMaxLens),
676
478
  };
677
- let warning = undefined;
678
- if (requiredParams.length === 0 && optionalParams.length > 1) {
679
- warning = {
680
- type: WarningType.OperationOnlyContainsOptionalParam,
681
- content: Utils.format(ConstantString.OperationOnlyContainsOptionalParam, operationId),
682
- data: operationId,
683
- };
479
+ return command;
480
+ }
481
+ static format(str, ...args) {
482
+ let index = 0;
483
+ return str.replace(/%s/g, () => {
484
+ const arg = args[index++];
485
+ return arg !== undefined ? arg : "";
486
+ });
487
+ }
488
+ static getSafeRegistrationIdEnvName(authName) {
489
+ if (!authName) {
490
+ return "";
684
491
  }
685
- return [command, warning];
492
+ let safeRegistrationIdEnvName = authName.toUpperCase().replace(/[^A-Z0-9_]/g, "_");
493
+ if (!safeRegistrationIdEnvName.match(/^[A-Z]/)) {
494
+ safeRegistrationIdEnvName = "PREFIX_" + safeRegistrationIdEnvName;
495
+ }
496
+ return safeRegistrationIdEnvName;
686
497
  }
687
- static listSupportedAPIs(spec, options) {
688
- const paths = spec.paths;
498
+ static getServerObject(spec, method, path) {
499
+ const pathObj = spec.paths[path];
500
+ const operationObject = pathObj[method];
501
+ const rootServer = spec.servers && spec.servers[0];
502
+ const methodServer = spec.paths[path].servers && spec.paths[path].servers[0];
503
+ const operationServer = operationObject.servers && operationObject.servers[0];
504
+ const serverUrl = operationServer || methodServer || rootServer;
505
+ return serverUrl;
506
+ }
507
+ }
508
+
509
+ // Copyright (c) Microsoft Corporation.
510
+ class Validator {
511
+ listAPIs() {
512
+ var _a;
513
+ if (this.apiMap) {
514
+ return this.apiMap;
515
+ }
516
+ const paths = this.spec.paths;
689
517
  const result = {};
690
518
  for (const path in paths) {
691
519
  const methods = paths[path];
692
520
  for (const method in methods) {
693
- if (Utils.isSupportedApi(method, path, spec, options)) {
694
- const operationObject = methods[method];
695
- result[`${method.toUpperCase()} ${path}`] = operationObject;
521
+ const operationObject = methods[method];
522
+ if (((_a = this.options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && operationObject) {
523
+ const validateResult = this.validateAPI(method, path);
524
+ result[`${method.toUpperCase()} ${path}`] = {
525
+ operation: operationObject,
526
+ isValid: validateResult.isValid,
527
+ reason: validateResult.reason,
528
+ };
696
529
  }
697
530
  }
698
531
  }
532
+ this.apiMap = result;
699
533
  return result;
700
534
  }
701
- static validateSpec(spec, parser, isSwaggerFile, options) {
702
- const errors = [];
703
- const warnings = [];
704
- if (isSwaggerFile) {
705
- warnings.push({
706
- type: WarningType.ConvertSwaggerToOpenAPI,
707
- content: ConstantString.ConvertSwaggerToOpenAPI,
535
+ validateSpecVersion() {
536
+ const result = { errors: [], warnings: [] };
537
+ if (this.spec.openapi >= "3.1.0") {
538
+ result.errors.push({
539
+ type: ErrorType.SpecVersionNotSupported,
540
+ content: Utils.format(ConstantString.SpecVersionNotSupported, this.spec.openapi),
541
+ data: this.spec.openapi,
708
542
  });
709
543
  }
710
- // Server validation
711
- const serverErrors = Utils.validateServer(spec, options);
712
- errors.push(...serverErrors);
713
- // Remote reference not supported
714
- const refPaths = parser.$refs.paths();
715
- // refPaths [0] is the current spec file path
716
- if (refPaths.length > 1) {
717
- errors.push({
718
- type: ErrorType.RemoteRefNotSupported,
719
- content: Utils.format(ConstantString.RemoteRefNotSupported, refPaths.join(", ")),
720
- data: refPaths,
721
- });
722
- }
723
- // No supported API
724
- const apiMap = Utils.listSupportedAPIs(spec, options);
725
- if (Object.keys(apiMap).length === 0) {
726
- errors.push({
544
+ return result;
545
+ }
546
+ validateSpecServer() {
547
+ const result = { errors: [], warnings: [] };
548
+ const serverErrors = Utils.validateServer(this.spec, this.options);
549
+ result.errors.push(...serverErrors);
550
+ return result;
551
+ }
552
+ validateSpecNoSupportAPI() {
553
+ const result = { errors: [], warnings: [] };
554
+ const apiMap = this.listAPIs();
555
+ const validAPIs = Object.entries(apiMap).filter(([, value]) => value.isValid);
556
+ if (validAPIs.length === 0) {
557
+ result.errors.push({
727
558
  type: ErrorType.NoSupportedApi,
728
559
  content: ConstantString.NoSupportedApi,
729
560
  });
730
561
  }
562
+ return result;
563
+ }
564
+ validateSpecOperationId() {
565
+ const result = { errors: [], warnings: [] };
566
+ const apiMap = this.listAPIs();
731
567
  // OperationId missing
732
568
  const apisMissingOperationId = [];
733
569
  for (const key in apiMap) {
734
- const pathObjectItem = apiMap[key];
735
- if (!pathObjectItem.operationId) {
570
+ const { operation } = apiMap[key];
571
+ if (!operation.operationId) {
736
572
  apisMissingOperationId.push(key);
737
573
  }
738
574
  }
739
575
  if (apisMissingOperationId.length > 0) {
740
- warnings.push({
576
+ result.warnings.push({
741
577
  type: WarningType.OperationIdMissing,
742
578
  content: Utils.format(ConstantString.MissingOperationId, apisMissingOperationId.join(", ")),
743
579
  data: apisMissingOperationId,
744
580
  });
745
581
  }
746
- let status = ValidationStatus.Valid;
747
- if (warnings.length > 0 && errors.length === 0) {
748
- status = ValidationStatus.Warning;
582
+ return result;
583
+ }
584
+ validateMethodAndPath(method, path) {
585
+ const result = { isValid: true, reason: [] };
586
+ if (this.options.allowMethods && !this.options.allowMethods.includes(method)) {
587
+ result.isValid = false;
588
+ result.reason.push(ErrorType.MethodNotAllowed);
589
+ return result;
749
590
  }
750
- else if (errors.length > 0) {
751
- status = ValidationStatus.Error;
591
+ const pathObj = this.spec.paths[path];
592
+ if (!pathObj || !pathObj[method]) {
593
+ result.isValid = false;
594
+ result.reason.push(ErrorType.UrlPathNotExist);
595
+ return result;
752
596
  }
753
- return {
754
- status,
755
- warnings,
756
- errors,
757
- };
597
+ return result;
758
598
  }
759
- static format(str, ...args) {
760
- let index = 0;
761
- return str.replace(/%s/g, () => {
762
- const arg = args[index++];
763
- return arg !== undefined ? arg : "";
764
- });
599
+ validateResponse(method, path) {
600
+ const result = { isValid: true, reason: [] };
601
+ const operationObject = this.spec.paths[path][method];
602
+ const { json, multipleMediaType } = Utils.getResponseJson(operationObject);
603
+ // only support response body only contains “application/json” content type
604
+ if (multipleMediaType) {
605
+ result.reason.push(ErrorType.ResponseContainMultipleMediaTypes);
606
+ }
607
+ else if (Object.keys(json).length === 0) {
608
+ // response body should not be empty
609
+ result.reason.push(ErrorType.ResponseJsonIsEmpty);
610
+ }
611
+ return result;
765
612
  }
766
- static getSafeRegistrationIdEnvName(authName) {
767
- if (!authName) {
768
- return "";
613
+ validateServer(method, path) {
614
+ const result = { isValid: true, reason: [] };
615
+ const serverObj = Utils.getServerObject(this.spec, method, path);
616
+ if (!serverObj) {
617
+ // should contain server URL
618
+ result.reason.push(ErrorType.NoServerInformation);
769
619
  }
770
- let safeRegistrationIdEnvName = authName.toUpperCase().replace(/[^A-Z0-9_]/g, "_");
771
- if (!safeRegistrationIdEnvName.match(/^[A-Z]/)) {
772
- safeRegistrationIdEnvName = "PREFIX_" + safeRegistrationIdEnvName;
620
+ else {
621
+ // server url should be absolute url with https protocol
622
+ const serverValidateResult = Utils.checkServerUrl([serverObj]);
623
+ result.reason.push(...serverValidateResult.map((item) => item.type));
773
624
  }
774
- return safeRegistrationIdEnvName;
625
+ return result;
775
626
  }
776
- static getAllAPICount(spec) {
777
- let count = 0;
778
- const paths = spec.paths;
779
- for (const path in paths) {
780
- const methods = paths[path];
781
- for (const method in methods) {
782
- if (ConstantString.AllOperationMethods.includes(method)) {
783
- count++;
627
+ validateAuth(method, path) {
628
+ const pathObj = this.spec.paths[path];
629
+ const operationObject = pathObj[method];
630
+ const securities = operationObject.security;
631
+ const authSchemeArray = Utils.getAuthArray(securities, this.spec);
632
+ if (authSchemeArray.length === 0) {
633
+ return { isValid: true, reason: [] };
634
+ }
635
+ if (this.options.allowAPIKeyAuth ||
636
+ this.options.allowOauth2 ||
637
+ this.options.allowBearerTokenAuth) {
638
+ // Currently we don't support multiple auth in one operation
639
+ if (authSchemeArray.length > 0 && authSchemeArray.every((auths) => auths.length > 1)) {
640
+ return {
641
+ isValid: false,
642
+ reason: [ErrorType.MultipleAuthNotSupported],
643
+ };
644
+ }
645
+ for (const auths of authSchemeArray) {
646
+ if (auths.length === 1) {
647
+ if ((this.options.allowAPIKeyAuth && Utils.isAPIKeyAuth(auths[0].authScheme)) ||
648
+ (this.options.allowOauth2 && Utils.isOAuthWithAuthCodeFlow(auths[0].authScheme)) ||
649
+ (this.options.allowBearerTokenAuth && Utils.isBearerTokenAuth(auths[0].authScheme))) {
650
+ return { isValid: true, reason: [] };
651
+ }
652
+ }
653
+ }
654
+ }
655
+ return { isValid: false, reason: [ErrorType.AuthTypeIsNotSupported] };
656
+ }
657
+ checkPostBodySchema(schema, isRequired = false) {
658
+ var _a;
659
+ const paramResult = {
660
+ requiredNum: 0,
661
+ optionalNum: 0,
662
+ isValid: true,
663
+ reason: [],
664
+ };
665
+ if (Object.keys(schema).length === 0) {
666
+ return paramResult;
667
+ }
668
+ const isRequiredWithoutDefault = isRequired && schema.default === undefined;
669
+ const isCopilot = this.projectType === ProjectType.Copilot;
670
+ if (isCopilot && this.hasNestedObjectInSchema(schema)) {
671
+ paramResult.isValid = false;
672
+ paramResult.reason = [ErrorType.RequestBodyContainsNestedObject];
673
+ return paramResult;
674
+ }
675
+ if (schema.type === "string" ||
676
+ schema.type === "integer" ||
677
+ schema.type === "boolean" ||
678
+ schema.type === "number") {
679
+ if (isRequiredWithoutDefault) {
680
+ paramResult.requiredNum = paramResult.requiredNum + 1;
681
+ }
682
+ else {
683
+ paramResult.optionalNum = paramResult.optionalNum + 1;
684
+ }
685
+ }
686
+ else if (schema.type === "object") {
687
+ const { properties } = schema;
688
+ for (const property in properties) {
689
+ let isRequired = false;
690
+ if (schema.required && ((_a = schema.required) === null || _a === void 0 ? void 0 : _a.indexOf(property)) >= 0) {
691
+ isRequired = true;
784
692
  }
693
+ const result = this.checkPostBodySchema(properties[property], isRequired);
694
+ paramResult.requiredNum += result.requiredNum;
695
+ paramResult.optionalNum += result.optionalNum;
696
+ paramResult.isValid = paramResult.isValid && result.isValid;
697
+ paramResult.reason.push(...result.reason);
785
698
  }
786
699
  }
787
- return count;
700
+ else {
701
+ if (isRequiredWithoutDefault && !isCopilot) {
702
+ paramResult.isValid = false;
703
+ paramResult.reason.push(ErrorType.PostBodyContainsRequiredUnsupportedSchema);
704
+ }
705
+ }
706
+ return paramResult;
707
+ }
708
+ checkParamSchema(paramObject) {
709
+ const paramResult = {
710
+ requiredNum: 0,
711
+ optionalNum: 0,
712
+ isValid: true,
713
+ reason: [],
714
+ };
715
+ if (!paramObject) {
716
+ return paramResult;
717
+ }
718
+ const isCopilot = this.projectType === ProjectType.Copilot;
719
+ for (let i = 0; i < paramObject.length; i++) {
720
+ const param = paramObject[i];
721
+ const schema = param.schema;
722
+ if (isCopilot && this.hasNestedObjectInSchema(schema)) {
723
+ paramResult.isValid = false;
724
+ paramResult.reason.push(ErrorType.ParamsContainsNestedObject);
725
+ continue;
726
+ }
727
+ const isRequiredWithoutDefault = param.required && schema.default === undefined;
728
+ if (isCopilot) {
729
+ if (isRequiredWithoutDefault) {
730
+ paramResult.requiredNum = paramResult.requiredNum + 1;
731
+ }
732
+ else {
733
+ paramResult.optionalNum = paramResult.optionalNum + 1;
734
+ }
735
+ continue;
736
+ }
737
+ if (param.in === "header" || param.in === "cookie") {
738
+ if (isRequiredWithoutDefault) {
739
+ paramResult.isValid = false;
740
+ paramResult.reason.push(ErrorType.ParamsContainRequiredUnsupportedSchema);
741
+ }
742
+ continue;
743
+ }
744
+ if (schema.type !== "boolean" &&
745
+ schema.type !== "string" &&
746
+ schema.type !== "number" &&
747
+ schema.type !== "integer") {
748
+ if (isRequiredWithoutDefault) {
749
+ paramResult.isValid = false;
750
+ paramResult.reason.push(ErrorType.ParamsContainRequiredUnsupportedSchema);
751
+ }
752
+ continue;
753
+ }
754
+ if (param.in === "query" || param.in === "path") {
755
+ if (isRequiredWithoutDefault) {
756
+ paramResult.requiredNum = paramResult.requiredNum + 1;
757
+ }
758
+ else {
759
+ paramResult.optionalNum = paramResult.optionalNum + 1;
760
+ }
761
+ }
762
+ }
763
+ return paramResult;
764
+ }
765
+ hasNestedObjectInSchema(schema) {
766
+ if (schema.type === "object") {
767
+ for (const property in schema.properties) {
768
+ const nestedSchema = schema.properties[property];
769
+ if (nestedSchema.type === "object") {
770
+ return true;
771
+ }
772
+ }
773
+ }
774
+ return false;
775
+ }
776
+ }
777
+
778
+ // Copyright (c) Microsoft Corporation.
779
+ class CopilotValidator extends Validator {
780
+ constructor(spec, options) {
781
+ super();
782
+ this.projectType = ProjectType.Copilot;
783
+ this.options = options;
784
+ this.spec = spec;
785
+ }
786
+ validateSpec() {
787
+ const result = { errors: [], warnings: [] };
788
+ // validate spec version
789
+ let validationResult = this.validateSpecVersion();
790
+ result.errors.push(...validationResult.errors);
791
+ // validate spec server
792
+ validationResult = this.validateSpecServer();
793
+ result.errors.push(...validationResult.errors);
794
+ // validate no supported API
795
+ validationResult = this.validateSpecNoSupportAPI();
796
+ result.errors.push(...validationResult.errors);
797
+ // validate operationId missing
798
+ validationResult = this.validateSpecOperationId();
799
+ result.warnings.push(...validationResult.warnings);
800
+ return result;
801
+ }
802
+ validateAPI(method, path) {
803
+ const result = { isValid: true, reason: [] };
804
+ method = method.toLocaleLowerCase();
805
+ // validate method and path
806
+ const methodAndPathResult = this.validateMethodAndPath(method, path);
807
+ if (!methodAndPathResult.isValid) {
808
+ return methodAndPathResult;
809
+ }
810
+ const operationObject = this.spec.paths[path][method];
811
+ // validate auth
812
+ const authCheckResult = this.validateAuth(method, path);
813
+ result.reason.push(...authCheckResult.reason);
814
+ // validate operationId
815
+ if (!this.options.allowMissingId && !operationObject.operationId) {
816
+ result.reason.push(ErrorType.MissingOperationId);
817
+ }
818
+ // validate server
819
+ const validateServerResult = this.validateServer(method, path);
820
+ result.reason.push(...validateServerResult.reason);
821
+ // validate response
822
+ const validateResponseResult = this.validateResponse(method, path);
823
+ result.reason.push(...validateResponseResult.reason);
824
+ // validate requestBody
825
+ const requestBody = operationObject.requestBody;
826
+ const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
827
+ if (Utils.containMultipleMediaTypes(requestBody)) {
828
+ result.reason.push(ErrorType.PostBodyContainMultipleMediaTypes);
829
+ }
830
+ if (requestJsonBody) {
831
+ const requestBodySchema = requestJsonBody.schema;
832
+ if (requestBodySchema.type !== "object") {
833
+ result.reason.push(ErrorType.PostBodySchemaIsNotJson);
834
+ }
835
+ const requestBodyParamResult = this.checkPostBodySchema(requestBodySchema, requestBody.required);
836
+ result.reason.push(...requestBodyParamResult.reason);
837
+ }
838
+ // validate parameters
839
+ const paramObject = operationObject.parameters;
840
+ const paramResult = this.checkParamSchema(paramObject);
841
+ result.reason.push(...paramResult.reason);
842
+ if (result.reason.length > 0) {
843
+ result.isValid = false;
844
+ }
845
+ return result;
846
+ }
847
+ }
848
+
849
+ // Copyright (c) Microsoft Corporation.
850
+ class SMEValidator extends Validator {
851
+ constructor(spec, options) {
852
+ super();
853
+ this.projectType = ProjectType.SME;
854
+ this.options = options;
855
+ this.spec = spec;
856
+ }
857
+ validateSpec() {
858
+ const result = { errors: [], warnings: [] };
859
+ // validate spec version
860
+ let validationResult = this.validateSpecVersion();
861
+ result.errors.push(...validationResult.errors);
862
+ // validate spec server
863
+ validationResult = this.validateSpecServer();
864
+ result.errors.push(...validationResult.errors);
865
+ // validate no supported API
866
+ validationResult = this.validateSpecNoSupportAPI();
867
+ result.errors.push(...validationResult.errors);
868
+ // validate operationId missing
869
+ validationResult = this.validateSpecOperationId();
870
+ result.warnings.push(...validationResult.warnings);
871
+ return result;
872
+ }
873
+ validateAPI(method, path) {
874
+ const result = { isValid: true, reason: [] };
875
+ method = method.toLocaleLowerCase();
876
+ // validate method and path
877
+ const methodAndPathResult = this.validateMethodAndPath(method, path);
878
+ if (!methodAndPathResult.isValid) {
879
+ return methodAndPathResult;
880
+ }
881
+ const operationObject = this.spec.paths[path][method];
882
+ // validate auth
883
+ const authCheckResult = this.validateAuth(method, path);
884
+ result.reason.push(...authCheckResult.reason);
885
+ // validate operationId
886
+ if (!this.options.allowMissingId && !operationObject.operationId) {
887
+ result.reason.push(ErrorType.MissingOperationId);
888
+ }
889
+ // validate server
890
+ const validateServerResult = this.validateServer(method, path);
891
+ result.reason.push(...validateServerResult.reason);
892
+ // validate response
893
+ const validateResponseResult = this.validateResponse(method, path);
894
+ result.reason.push(...validateResponseResult.reason);
895
+ let postBodyResult = {
896
+ requiredNum: 0,
897
+ optionalNum: 0,
898
+ isValid: true,
899
+ reason: [],
900
+ };
901
+ // validate requestBody
902
+ const requestBody = operationObject.requestBody;
903
+ const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
904
+ if (Utils.containMultipleMediaTypes(requestBody)) {
905
+ result.reason.push(ErrorType.PostBodyContainMultipleMediaTypes);
906
+ }
907
+ if (requestJsonBody) {
908
+ const requestBodySchema = requestJsonBody.schema;
909
+ postBodyResult = this.checkPostBodySchema(requestBodySchema, requestBody.required);
910
+ result.reason.push(...postBodyResult.reason);
911
+ }
912
+ // validate parameters
913
+ const paramObject = operationObject.parameters;
914
+ const paramResult = this.checkParamSchema(paramObject);
915
+ result.reason.push(...paramResult.reason);
916
+ // validate total parameters count
917
+ if (paramResult.isValid && postBodyResult.isValid) {
918
+ const paramCountResult = this.validateParamCount(postBodyResult, paramResult);
919
+ result.reason.push(...paramCountResult.reason);
920
+ }
921
+ if (result.reason.length > 0) {
922
+ result.isValid = false;
923
+ }
924
+ return result;
925
+ }
926
+ validateParamCount(postBodyResult, paramResult) {
927
+ const result = { isValid: true, reason: [] };
928
+ const totalRequiredParams = postBodyResult.requiredNum + paramResult.requiredNum;
929
+ const totalParams = totalRequiredParams + postBodyResult.optionalNum + paramResult.optionalNum;
930
+ if (totalRequiredParams > 1) {
931
+ if (!this.options.allowMultipleParameters ||
932
+ totalRequiredParams > SMEValidator.SMERequiredParamsMaxNum) {
933
+ result.reason.push(ErrorType.ExceededRequiredParamsLimit);
934
+ }
935
+ }
936
+ else if (totalParams === 0) {
937
+ result.reason.push(ErrorType.NoParameter);
938
+ }
939
+ return result;
940
+ }
941
+ }
942
+ SMEValidator.SMERequiredParamsMaxNum = 5;
943
+
944
+ // Copyright (c) Microsoft Corporation.
945
+ class TeamsAIValidator extends Validator {
946
+ constructor(spec, options) {
947
+ super();
948
+ this.projectType = ProjectType.TeamsAi;
949
+ this.options = options;
950
+ this.spec = spec;
951
+ }
952
+ validateSpec() {
953
+ const result = { errors: [], warnings: [] };
954
+ // validate spec server
955
+ let validationResult = this.validateSpecServer();
956
+ result.errors.push(...validationResult.errors);
957
+ // validate no supported API
958
+ validationResult = this.validateSpecNoSupportAPI();
959
+ result.errors.push(...validationResult.errors);
960
+ return result;
961
+ }
962
+ validateAPI(method, path) {
963
+ const result = { isValid: true, reason: [] };
964
+ method = method.toLocaleLowerCase();
965
+ // validate method and path
966
+ const methodAndPathResult = this.validateMethodAndPath(method, path);
967
+ if (!methodAndPathResult.isValid) {
968
+ return methodAndPathResult;
969
+ }
970
+ const operationObject = this.spec.paths[path][method];
971
+ // validate operationId
972
+ if (!this.options.allowMissingId && !operationObject.operationId) {
973
+ result.reason.push(ErrorType.MissingOperationId);
974
+ }
975
+ // validate server
976
+ const validateServerResult = this.validateServer(method, path);
977
+ result.reason.push(...validateServerResult.reason);
978
+ if (result.reason.length > 0) {
979
+ result.isValid = false;
980
+ }
981
+ return result;
982
+ }
983
+ }
984
+
985
+ class ValidatorFactory {
986
+ static create(spec, options) {
987
+ var _a;
988
+ const type = (_a = options.projectType) !== null && _a !== void 0 ? _a : ProjectType.SME;
989
+ switch (type) {
990
+ case ProjectType.SME:
991
+ return new SMEValidator(spec, options);
992
+ case ProjectType.Copilot:
993
+ return new CopilotValidator(spec, options);
994
+ case ProjectType.TeamsAi:
995
+ return new TeamsAIValidator(spec, options);
996
+ default:
997
+ throw new Error(`Invalid project type: ${type}`);
998
+ }
788
999
  }
789
1000
  }
790
1001
 
791
1002
  // Copyright (c) Microsoft Corporation.
792
1003
  class SpecFilter {
793
1004
  static specFilter(filter, unResolveSpec, resolvedSpec, options) {
1005
+ var _a;
794
1006
  try {
795
1007
  const newSpec = Object.assign({}, unResolveSpec);
796
1008
  const newPaths = {};
797
1009
  for (const filterItem of filter) {
798
1010
  const [method, path] = filterItem.split(" ");
799
1011
  const methodName = method.toLowerCase();
800
- if (!Utils.isSupportedApi(methodName, path, resolvedSpec, options)) {
801
- continue;
802
- }
803
- if (!newPaths[path]) {
804
- newPaths[path] = Object.assign({}, unResolveSpec.paths[path]);
805
- for (const m of ConstantString.AllOperationMethods) {
806
- delete newPaths[path][m];
1012
+ const pathObj = (_a = resolvedSpec.paths) === null || _a === void 0 ? void 0 : _a[path];
1013
+ if (ConstantString.AllOperationMethods.includes(methodName) &&
1014
+ pathObj &&
1015
+ pathObj[methodName]) {
1016
+ const validator = ValidatorFactory.create(resolvedSpec, options);
1017
+ const validateResult = validator.validateAPI(methodName, path);
1018
+ if (!validateResult.isValid) {
1019
+ continue;
1020
+ }
1021
+ if (!newPaths[path]) {
1022
+ newPaths[path] = Object.assign({}, unResolveSpec.paths[path]);
1023
+ for (const m of ConstantString.AllOperationMethods) {
1024
+ delete newPaths[path][m];
1025
+ }
1026
+ }
1027
+ newPaths[path][methodName] = unResolveSpec.paths[path][methodName];
1028
+ // Add the operationId if missing
1029
+ if (!newPaths[path][methodName].operationId) {
1030
+ newPaths[path][methodName].operationId = `${methodName}${Utils.convertPathToCamelCase(path)}`;
807
1031
  }
808
- }
809
- newPaths[path][methodName] = unResolveSpec.paths[path][methodName];
810
- // Add the operationId if missing
811
- if (!newPaths[path][methodName].operationId) {
812
- newPaths[path][methodName].operationId = `${methodName}${Utils.convertPathToCamelCase(path)}`;
813
1032
  }
814
1033
  }
815
1034
  newSpec.paths = newPaths;
@@ -831,9 +1050,10 @@ class ManifestUpdater {
831
1050
  pluginFile: apiPluginRelativePath,
832
1051
  },
833
1052
  ];
1053
+ const appName = this.removeEnvs(manifest.name.short);
834
1054
  ManifestUpdater.updateManifestDescription(manifest, spec);
835
1055
  const specRelativePath = ManifestUpdater.getRelativePath(manifestPath, outputSpecPath);
836
- const apiPlugin = ManifestUpdater.generatePluginManifestSchema(spec, specRelativePath, options);
1056
+ const apiPlugin = ManifestUpdater.generatePluginManifestSchema(spec, specRelativePath, appName, options);
837
1057
  return [manifest, apiPlugin];
838
1058
  }
839
1059
  static updateManifestDescription(manifest, spec) {
@@ -857,7 +1077,7 @@ class ManifestUpdater {
857
1077
  }
858
1078
  return parameter;
859
1079
  }
860
- static generatePluginManifestSchema(spec, specRelativePath, options) {
1080
+ static generatePluginManifestSchema(spec, specRelativePath, appName, options) {
861
1081
  var _a, _b, _c;
862
1082
  const functions = [];
863
1083
  const functionNames = [];
@@ -922,7 +1142,7 @@ class ManifestUpdater {
922
1142
  }
923
1143
  const apiPlugin = {
924
1144
  schema_version: "v2",
925
- name_for_human: spec.info.title,
1145
+ name_for_human: appName,
926
1146
  description_for_human: (_c = spec.info.description) !== null && _c !== void 0 ? _c : "<Please add description of the plugin>",
927
1147
  functions: functions,
928
1148
  runtimes: [
@@ -1006,16 +1226,26 @@ class ManifestUpdater {
1006
1226
  if ((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) {
1007
1227
  const operationItem = operations[method];
1008
1228
  if (operationItem) {
1009
- const [command, warning] = Utils.parseApiInfo(operationItem, options);
1229
+ const command = Utils.parseApiInfo(operationItem, options);
1230
+ if (command.parameters &&
1231
+ command.parameters.length >= 1 &&
1232
+ command.parameters.some((param) => param.isRequired)) {
1233
+ command.parameters = command.parameters.filter((param) => param.isRequired);
1234
+ }
1235
+ else if (command.parameters && command.parameters.length > 0) {
1236
+ command.parameters = [command.parameters[0]];
1237
+ warnings.push({
1238
+ type: WarningType.OperationOnlyContainsOptionalParam,
1239
+ content: Utils.format(ConstantString.OperationOnlyContainsOptionalParam, command.id),
1240
+ data: command.id,
1241
+ });
1242
+ }
1010
1243
  if (adaptiveCardFolder) {
1011
1244
  const adaptiveCardPath = path.join(adaptiveCardFolder, command.id + ".json");
1012
1245
  command.apiResponseRenderingTemplateFile = (await fs.pathExists(adaptiveCardPath))
1013
1246
  ? ManifestUpdater.getRelativePath(manifestPath, adaptiveCardPath)
1014
1247
  : "";
1015
1248
  }
1016
- if (warning) {
1017
- warnings.push(warning);
1018
- }
1019
1249
  commands.push(command);
1020
1250
  }
1021
1251
  }
@@ -1029,13 +1259,22 @@ class ManifestUpdater {
1029
1259
  const relativePath = path.relative(path.dirname(from), to);
1030
1260
  return path.normalize(relativePath).replace(/\\/g, "/");
1031
1261
  }
1262
+ static removeEnvs(str) {
1263
+ const placeHolderReg = /\${{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*}}/g;
1264
+ const matches = placeHolderReg.exec(str);
1265
+ let newStr = str;
1266
+ if (matches != null) {
1267
+ newStr = newStr.replace(matches[0], "");
1268
+ }
1269
+ return newStr;
1270
+ }
1032
1271
  }
1033
1272
 
1034
1273
  // Copyright (c) Microsoft Corporation.
1035
1274
  class AdaptiveCardGenerator {
1036
1275
  static generateAdaptiveCard(operationItem) {
1037
1276
  try {
1038
- const json = Utils.getResponseJson(operationItem);
1277
+ const { json } = Utils.getResponseJson(operationItem);
1039
1278
  let cardBody = [];
1040
1279
  let schema = json.schema;
1041
1280
  let jsonPath = "$";
@@ -1323,6 +1562,8 @@ class SpecParser {
1323
1562
  errors: [{ type: ErrorType.SpecNotValid, content: e.toString() }],
1324
1563
  };
1325
1564
  }
1565
+ const errors = [];
1566
+ const warnings = [];
1326
1567
  if (!this.options.allowSwagger && this.isSwaggerFile) {
1327
1568
  return {
1328
1569
  status: ValidationStatus.Error,
@@ -1332,7 +1573,38 @@ class SpecParser {
1332
1573
  ],
1333
1574
  };
1334
1575
  }
1335
- return Utils.validateSpec(this.spec, this.parser, !!this.isSwaggerFile, this.options);
1576
+ // Remote reference not supported
1577
+ const refPaths = this.parser.$refs.paths();
1578
+ // refPaths [0] is the current spec file path
1579
+ if (refPaths.length > 1) {
1580
+ errors.push({
1581
+ type: ErrorType.RemoteRefNotSupported,
1582
+ content: Utils.format(ConstantString.RemoteRefNotSupported, refPaths.join(", ")),
1583
+ data: refPaths,
1584
+ });
1585
+ }
1586
+ if (!!this.isSwaggerFile && this.options.allowSwagger) {
1587
+ warnings.push({
1588
+ type: WarningType.ConvertSwaggerToOpenAPI,
1589
+ content: ConstantString.ConvertSwaggerToOpenAPI,
1590
+ });
1591
+ }
1592
+ const validator = this.getValidator(this.spec);
1593
+ const validationResult = validator.validateSpec();
1594
+ warnings.push(...validationResult.warnings);
1595
+ errors.push(...validationResult.errors);
1596
+ let status = ValidationStatus.Valid;
1597
+ if (warnings.length > 0 && errors.length === 0) {
1598
+ status = ValidationStatus.Warning;
1599
+ }
1600
+ else if (errors.length > 0) {
1601
+ status = ValidationStatus.Error;
1602
+ }
1603
+ return {
1604
+ status: status,
1605
+ warnings: warnings,
1606
+ errors: errors,
1607
+ };
1336
1608
  }
1337
1609
  catch (err) {
1338
1610
  throw new SpecParserError(err.toString(), ErrorType.ValidateFailed);
@@ -1352,45 +1624,40 @@ class SpecParser {
1352
1624
  try {
1353
1625
  await this.loadSpec();
1354
1626
  const spec = this.spec;
1355
- const apiMap = this.getAllSupportedAPIs(spec);
1627
+ const apiMap = this.getAPIs(spec);
1356
1628
  const result = {
1357
- validAPIs: [],
1629
+ APIs: [],
1358
1630
  allAPICount: 0,
1359
1631
  validAPICount: 0,
1360
1632
  };
1361
1633
  for (const apiKey in apiMap) {
1634
+ const { operation, isValid, reason } = apiMap[apiKey];
1635
+ const [method, path] = apiKey.split(" ");
1636
+ const operationId = (_a = operation.operationId) !== null && _a !== void 0 ? _a : `${method.toLowerCase()}${Utils.convertPathToCamelCase(path)}`;
1362
1637
  const apiResult = {
1363
- api: "",
1638
+ api: apiKey,
1364
1639
  server: "",
1365
- operationId: "",
1640
+ operationId: operationId,
1641
+ isValid: isValid,
1642
+ reason: reason,
1366
1643
  };
1367
- const [method, path] = apiKey.split(" ");
1368
- const operation = apiMap[apiKey];
1369
- const rootServer = spec.servers && spec.servers[0];
1370
- const methodServer = spec.paths[path].servers && ((_a = spec.paths[path]) === null || _a === void 0 ? void 0 : _a.servers[0]);
1371
- const operationServer = operation.servers && operation.servers[0];
1372
- const serverUrl = operationServer || methodServer || rootServer;
1373
- if (!serverUrl) {
1374
- throw new SpecParserError(ConstantString.NoServerInformation, ErrorType.NoServerInformation);
1375
- }
1376
- apiResult.server = Utils.resolveServerUrl(serverUrl.url);
1377
- let operationId = operation.operationId;
1378
- if (!operationId) {
1379
- operationId = `${method.toLowerCase()}${Utils.convertPathToCamelCase(path)}`;
1380
- }
1381
- apiResult.operationId = operationId;
1382
- const authArray = Utils.getAuthArray(operation.security, spec);
1383
- for (const auths of authArray) {
1384
- if (auths.length === 1) {
1385
- apiResult.auth = auths[0];
1386
- break;
1644
+ if (isValid) {
1645
+ const serverObj = Utils.getServerObject(spec, method.toLocaleLowerCase(), path);
1646
+ if (serverObj) {
1647
+ apiResult.server = Utils.resolveEnv(serverObj.url);
1648
+ }
1649
+ const authArray = Utils.getAuthArray(operation.security, spec);
1650
+ for (const auths of authArray) {
1651
+ if (auths.length === 1) {
1652
+ apiResult.auth = auths[0];
1653
+ break;
1654
+ }
1387
1655
  }
1388
1656
  }
1389
- apiResult.api = apiKey;
1390
- result.validAPIs.push(apiResult);
1657
+ result.APIs.push(apiResult);
1391
1658
  }
1392
- result.allAPICount = Utils.getAllAPICount(spec);
1393
- result.validAPICount = result.validAPIs.length;
1659
+ result.allAPICount = result.APIs.length;
1660
+ result.validAPICount = result.APIs.filter((api) => api.isValid).length;
1394
1661
  return result;
1395
1662
  }
1396
1663
  catch (err) {
@@ -1482,15 +1749,18 @@ class SpecParser {
1482
1749
  const newSpecs = await this.getFilteredSpecs(filter, signal);
1483
1750
  const newUnResolvedSpec = newSpecs[0];
1484
1751
  const newSpec = newSpecs[1];
1485
- const authSet = new Set();
1486
1752
  let hasMultipleAuth = false;
1753
+ let authInfo = undefined;
1487
1754
  for (const url in newSpec.paths) {
1488
1755
  for (const method in newSpec.paths[url]) {
1489
1756
  const operation = newSpec.paths[url][method];
1490
1757
  const authArray = Utils.getAuthArray(operation.security, newSpec);
1491
1758
  if (authArray && authArray.length > 0) {
1492
- authSet.add(authArray[0][0]);
1493
- if (authSet.size > 1) {
1759
+ const currentAuth = authArray[0][0];
1760
+ if (!authInfo) {
1761
+ authInfo = authArray[0][0];
1762
+ }
1763
+ else if (authInfo.name !== currentAuth.name) {
1494
1764
  hasMultipleAuth = true;
1495
1765
  break;
1496
1766
  }
@@ -1537,7 +1807,6 @@ class SpecParser {
1537
1807
  if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
1538
1808
  throw new SpecParserError(ConstantString.CancelledMessage, ErrorType.Cancelled);
1539
1809
  }
1540
- const authInfo = Array.from(authSet)[0];
1541
1810
  const [updatedManifest, warnings] = await ManifestUpdater.updateManifest(manifestPath, outputSpecPath, newSpec, this.options, adaptiveCardFolder, authInfo);
1542
1811
  await fs.outputJSON(manifestPath, updatedManifest, { spaces: 2 });
1543
1812
  result.warnings.push(...warnings);
@@ -1563,13 +1832,19 @@ class SpecParser {
1563
1832
  this.spec = (await this.parser.dereference(clonedUnResolveSpec));
1564
1833
  }
1565
1834
  }
1566
- getAllSupportedAPIs(spec) {
1567
- if (this.apiMap !== undefined) {
1568
- return this.apiMap;
1835
+ getAPIs(spec) {
1836
+ const validator = this.getValidator(spec);
1837
+ const apiMap = validator.listAPIs();
1838
+ this.apiMap = apiMap;
1839
+ return apiMap;
1840
+ }
1841
+ getValidator(spec) {
1842
+ if (this.validator) {
1843
+ return this.validator;
1569
1844
  }
1570
- const result = Utils.listSupportedAPIs(spec, this.options);
1571
- this.apiMap = result;
1572
- return result;
1845
+ const validator = ValidatorFactory.create(spec, this.options);
1846
+ this.validator = validator;
1847
+ return validator;
1573
1848
  }
1574
1849
  }
1575
1850