@microsoft/m365-spec-parser 0.1.1-alpha.54a90c74e.0 → 0.1.1-alpha.6341e718e.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.
@@ -25,6 +25,21 @@ var ErrorType;
25
25
  ErrorType["GenerateFailed"] = "generate-failed";
26
26
  ErrorType["ValidateFailed"] = "validate-failed";
27
27
  ErrorType["GetSpecFailed"] = "get-spec-failed";
28
+ ErrorType["AuthTypeIsNotSupported"] = "auth-type-is-not-supported";
29
+ ErrorType["MissingOperationId"] = "missing-operation-id";
30
+ ErrorType["PostBodyContainMultipleMediaTypes"] = "post-body-contain-multiple-media-types";
31
+ ErrorType["ResponseContainMultipleMediaTypes"] = "response-contain-multiple-media-types";
32
+ ErrorType["ResponseJsonIsEmpty"] = "response-json-is-empty";
33
+ ErrorType["PostBodySchemaIsNotJson"] = "post-body-schema-is-not-json";
34
+ ErrorType["PostBodyContainsRequiredUnsupportedSchema"] = "post-body-contains-required-unsupported-schema";
35
+ ErrorType["ParamsContainRequiredUnsupportedSchema"] = "params-contain-required-unsupported-schema";
36
+ ErrorType["ParamsContainsNestedObject"] = "params-contains-nested-object";
37
+ ErrorType["RequestBodyContainsNestedObject"] = "request-body-contains-nested-object";
38
+ ErrorType["ExceededRequiredParamsLimit"] = "exceeded-required-params-limit";
39
+ ErrorType["NoParameter"] = "no-parameter";
40
+ ErrorType["NoAPIInfo"] = "no-api-info";
41
+ ErrorType["MethodNotAllowed"] = "method-not-allowed";
42
+ ErrorType["UrlPathNotExist"] = "url-path-not-exist";
28
43
  ErrorType["Cancelled"] = "cancelled";
29
44
  ErrorType["Unknown"] = "unknown";
30
45
  })(ErrorType || (ErrorType = {}));
@@ -170,221 +185,9 @@ class Utils {
170
185
  }
171
186
  return false;
172
187
  }
173
- static checkParameters(paramObject, isCopilot) {
174
- const paramResult = {
175
- requiredNum: 0,
176
- optionalNum: 0,
177
- isValid: true,
178
- };
179
- if (!paramObject) {
180
- return paramResult;
181
- }
182
- for (let i = 0; i < paramObject.length; i++) {
183
- const param = paramObject[i];
184
- const schema = param.schema;
185
- if (isCopilot && this.hasNestedObjectInSchema(schema)) {
186
- paramResult.isValid = false;
187
- continue;
188
- }
189
- const isRequiredWithoutDefault = param.required && schema.default === undefined;
190
- if (isCopilot) {
191
- if (isRequiredWithoutDefault) {
192
- paramResult.requiredNum = paramResult.requiredNum + 1;
193
- }
194
- else {
195
- paramResult.optionalNum = paramResult.optionalNum + 1;
196
- }
197
- continue;
198
- }
199
- if (param.in === "header" || param.in === "cookie") {
200
- if (isRequiredWithoutDefault) {
201
- paramResult.isValid = false;
202
- }
203
- continue;
204
- }
205
- if (schema.type !== "boolean" &&
206
- schema.type !== "string" &&
207
- schema.type !== "number" &&
208
- schema.type !== "integer") {
209
- if (isRequiredWithoutDefault) {
210
- paramResult.isValid = false;
211
- }
212
- continue;
213
- }
214
- if (param.in === "query" || param.in === "path") {
215
- if (isRequiredWithoutDefault) {
216
- paramResult.requiredNum = paramResult.requiredNum + 1;
217
- }
218
- else {
219
- paramResult.optionalNum = paramResult.optionalNum + 1;
220
- }
221
- }
222
- }
223
- return paramResult;
224
- }
225
- static checkPostBody(schema, isRequired = false, isCopilot = false) {
226
- var _a;
227
- const paramResult = {
228
- requiredNum: 0,
229
- optionalNum: 0,
230
- isValid: true,
231
- };
232
- if (Object.keys(schema).length === 0) {
233
- return paramResult;
234
- }
235
- const isRequiredWithoutDefault = isRequired && schema.default === undefined;
236
- if (isCopilot && this.hasNestedObjectInSchema(schema)) {
237
- paramResult.isValid = false;
238
- return paramResult;
239
- }
240
- if (schema.type === "string" ||
241
- schema.type === "integer" ||
242
- schema.type === "boolean" ||
243
- schema.type === "number") {
244
- if (isRequiredWithoutDefault) {
245
- paramResult.requiredNum = paramResult.requiredNum + 1;
246
- }
247
- else {
248
- paramResult.optionalNum = paramResult.optionalNum + 1;
249
- }
250
- }
251
- else if (schema.type === "object") {
252
- const { properties } = schema;
253
- for (const property in properties) {
254
- let isRequired = false;
255
- if (schema.required && ((_a = schema.required) === null || _a === void 0 ? void 0 : _a.indexOf(property)) >= 0) {
256
- isRequired = true;
257
- }
258
- const result = Utils.checkPostBody(properties[property], isRequired, isCopilot);
259
- paramResult.requiredNum += result.requiredNum;
260
- paramResult.optionalNum += result.optionalNum;
261
- paramResult.isValid = paramResult.isValid && result.isValid;
262
- }
263
- }
264
- else {
265
- if (isRequiredWithoutDefault && !isCopilot) {
266
- paramResult.isValid = false;
267
- }
268
- }
269
- return paramResult;
270
- }
271
188
  static containMultipleMediaTypes(bodyObject) {
272
189
  return Object.keys((bodyObject === null || bodyObject === void 0 ? void 0 : bodyObject.content) || {}).length > 1;
273
190
  }
274
- /**
275
- * Checks if the given API is supported.
276
- * @param {string} method - The HTTP method of the API.
277
- * @param {string} path - The path of the API.
278
- * @param {OpenAPIV3.Document} spec - The OpenAPI specification document.
279
- * @returns {boolean} - Returns true if the API is supported, false otherwise.
280
- * @description The following APIs are supported:
281
- * 1. only support Get/Post operation without auth property
282
- * 2. parameter inside query or path only support string, number, boolean and integer
283
- * 3. parameter inside post body only support string, number, boolean, integer and object
284
- * 4. request body + required parameters <= 1
285
- * 5. response body should be “application/json” and not empty, and response code should be 20X
286
- * 6. only support request body with “application/json” content type
287
- */
288
- static isSupportedApi(method, path, spec, options) {
289
- var _a;
290
- const pathObj = spec.paths[path];
291
- method = method.toLocaleLowerCase();
292
- if (pathObj) {
293
- if (((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && pathObj[method]) {
294
- const securities = pathObj[method].security;
295
- const isTeamsAi = options.projectType === ProjectType.TeamsAi;
296
- const isCopilot = options.projectType === ProjectType.Copilot;
297
- // Teams AI project doesn't care about auth, it will use authProvider for user to implement
298
- if (!isTeamsAi) {
299
- const authArray = Utils.getAuthArray(securities, spec);
300
- if (!Utils.isSupportedAuth(authArray, options)) {
301
- return false;
302
- }
303
- }
304
- const operationObject = pathObj[method];
305
- if (!options.allowMissingId && !operationObject.operationId) {
306
- return false;
307
- }
308
- const paramObject = operationObject.parameters;
309
- const requestBody = operationObject.requestBody;
310
- const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
311
- if (!isTeamsAi && Utils.containMultipleMediaTypes(requestBody)) {
312
- return false;
313
- }
314
- const responseJson = Utils.getResponseJson(operationObject, isTeamsAi);
315
- if (Object.keys(responseJson).length === 0) {
316
- return false;
317
- }
318
- // Teams AI project doesn't care about request parameters/body
319
- if (isTeamsAi) {
320
- return true;
321
- }
322
- let requestBodyParamResult = {
323
- requiredNum: 0,
324
- optionalNum: 0,
325
- isValid: true,
326
- };
327
- if (requestJsonBody) {
328
- const requestBodySchema = requestJsonBody.schema;
329
- if (isCopilot && requestBodySchema.type !== "object") {
330
- return false;
331
- }
332
- requestBodyParamResult = Utils.checkPostBody(requestBodySchema, requestBody.required, isCopilot);
333
- }
334
- if (!requestBodyParamResult.isValid) {
335
- return false;
336
- }
337
- const paramResult = Utils.checkParameters(paramObject, isCopilot);
338
- if (!paramResult.isValid) {
339
- return false;
340
- }
341
- // Copilot support arbitrary parameters
342
- if (isCopilot) {
343
- return true;
344
- }
345
- if (requestBodyParamResult.requiredNum + paramResult.requiredNum > 1) {
346
- if (options.allowMultipleParameters &&
347
- requestBodyParamResult.requiredNum + paramResult.requiredNum <=
348
- ConstantString.SMERequiredParamsMaxNum) {
349
- return true;
350
- }
351
- return false;
352
- }
353
- else if (requestBodyParamResult.requiredNum +
354
- requestBodyParamResult.optionalNum +
355
- paramResult.requiredNum +
356
- paramResult.optionalNum ===
357
- 0) {
358
- return false;
359
- }
360
- else {
361
- return true;
362
- }
363
- }
364
- }
365
- return false;
366
- }
367
- static isSupportedAuth(authSchemeArray, options) {
368
- if (authSchemeArray.length === 0) {
369
- return true;
370
- }
371
- if (options.allowAPIKeyAuth || options.allowOauth2 || options.allowBearerTokenAuth) {
372
- // Currently we don't support multiple auth in one operation
373
- if (authSchemeArray.length > 0 && authSchemeArray.every((auths) => auths.length > 1)) {
374
- return false;
375
- }
376
- for (const auths of authSchemeArray) {
377
- if (auths.length === 1) {
378
- if ((options.allowAPIKeyAuth && Utils.isAPIKeyAuth(auths[0].authScheme)) ||
379
- (options.allowOauth2 && Utils.isOAuthWithAuthCodeFlow(auths[0].authScheme)) ||
380
- (options.allowBearerTokenAuth && Utils.isBearerTokenAuth(auths[0].authScheme))) {
381
- return true;
382
- }
383
- }
384
- }
385
- }
386
- return false;
387
- }
388
191
  static isBearerTokenAuth(authScheme) {
389
192
  return authScheme.type === "http" && authScheme.scheme === "bearer";
390
193
  }
@@ -392,10 +195,9 @@ class Utils {
392
195
  return authScheme.type === "apiKey";
393
196
  }
394
197
  static isOAuthWithAuthCodeFlow(authScheme) {
395
- if (authScheme.type === "oauth2" && authScheme.flows && authScheme.flows.authorizationCode) {
396
- return true;
397
- }
398
- return false;
198
+ return !!(authScheme.type === "oauth2" &&
199
+ authScheme.flows &&
200
+ authScheme.flows.authorizationCode);
399
201
  }
400
202
  static getAuthArray(securities, spec) {
401
203
  var _a;
@@ -423,14 +225,17 @@ class Utils {
423
225
  static updateFirstLetter(str) {
424
226
  return str.charAt(0).toUpperCase() + str.slice(1);
425
227
  }
426
- static getResponseJson(operationObject, isTeamsAiProject = false) {
228
+ static getResponseJson(operationObject) {
427
229
  var _a, _b;
428
230
  let json = {};
231
+ let multipleMediaType = false;
429
232
  for (const code of ConstantString.ResponseCodeFor20X) {
430
233
  const responseObject = (_a = operationObject === null || operationObject === void 0 ? void 0 : operationObject.responses) === null || _a === void 0 ? void 0 : _a[code];
431
234
  if ((_b = responseObject === null || responseObject === void 0 ? void 0 : responseObject.content) === null || _b === void 0 ? void 0 : _b["application/json"]) {
235
+ multipleMediaType = false;
432
236
  json = responseObject.content["application/json"];
433
- if (!isTeamsAiProject && Utils.containMultipleMediaTypes(responseObject)) {
237
+ if (Utils.containMultipleMediaTypes(responseObject)) {
238
+ multipleMediaType = true;
434
239
  json = {};
435
240
  }
436
241
  else {
@@ -438,7 +243,7 @@ class Utils {
438
243
  }
439
244
  }
440
245
  }
441
- return json;
246
+ return { json, multipleMediaType };
442
247
  }
443
248
  static convertPathToCamelCase(path) {
444
249
  const pathSegments = path.split(/[./{]/);
@@ -458,10 +263,10 @@ class Utils {
458
263
  return undefined;
459
264
  }
460
265
  }
461
- static resolveServerUrl(url) {
266
+ static resolveEnv(str) {
462
267
  const placeHolderReg = /\${{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*}}/g;
463
- let matches = placeHolderReg.exec(url);
464
- let newUrl = url;
268
+ let matches = placeHolderReg.exec(str);
269
+ let newStr = str;
465
270
  while (matches != null) {
466
271
  const envVar = matches[1];
467
272
  const envVal = process.env[envVar];
@@ -469,17 +274,17 @@ class Utils {
469
274
  throw new Error(Utils.format(ConstantString.ResolveServerUrlFailed, envVar));
470
275
  }
471
276
  else {
472
- newUrl = newUrl.replace(matches[0], envVal);
277
+ newStr = newStr.replace(matches[0], envVal);
473
278
  }
474
- matches = placeHolderReg.exec(url);
279
+ matches = placeHolderReg.exec(str);
475
280
  }
476
- return newUrl;
281
+ return newStr;
477
282
  }
478
283
  static checkServerUrl(servers) {
479
284
  const errors = [];
480
285
  let serverUrl;
481
286
  try {
482
- serverUrl = Utils.resolveServerUrl(servers[0].url);
287
+ serverUrl = Utils.resolveEnv(servers[0].url);
483
288
  }
484
289
  catch (err) {
485
290
  errors.push({
@@ -510,6 +315,7 @@ class Utils {
510
315
  return errors;
511
316
  }
512
317
  static validateServer(spec, options) {
318
+ var _a;
513
319
  const errors = [];
514
320
  let hasTopLevelServers = false;
515
321
  let hasPathLevelServers = false;
@@ -530,7 +336,7 @@ class Utils {
530
336
  }
531
337
  for (const method in methods) {
532
338
  const operationObject = methods[method];
533
- if (Utils.isSupportedApi(method, path, spec, options)) {
339
+ if (((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && operationObject) {
534
340
  if ((operationObject === null || operationObject === void 0 ? void 0 : operationObject.servers) && operationObject.servers.length >= 1) {
535
341
  hasOperationLevelServers = true;
536
342
  const serverErrors = Utils.checkServerUrl(operationObject.servers);
@@ -657,13 +463,7 @@ class Utils {
657
463
  }
658
464
  }
659
465
  const operationId = operationItem.operationId;
660
- const parameters = [];
661
- if (requiredParams.length !== 0) {
662
- parameters.push(...requiredParams);
663
- }
664
- else {
665
- parameters.push(optionalParams[0]);
666
- }
466
+ const parameters = [...requiredParams, ...optionalParams];
667
467
  const command = {
668
468
  context: ["compose"],
669
469
  type: "query",
@@ -672,31 +472,9 @@ class Utils {
672
472
  parameters: parameters,
673
473
  description: ((_b = operationItem.description) !== null && _b !== void 0 ? _b : "").slice(0, ConstantString.CommandDescriptionMaxLens),
674
474
  };
675
- let warning = undefined;
676
- if (requiredParams.length === 0 && optionalParams.length > 1) {
677
- warning = {
678
- type: WarningType.OperationOnlyContainsOptionalParam,
679
- content: Utils.format(ConstantString.OperationOnlyContainsOptionalParam, operationId),
680
- data: operationId,
681
- };
682
- }
683
- return [command, warning];
684
- }
685
- static listSupportedAPIs(spec, options) {
686
- const paths = spec.paths;
687
- const result = {};
688
- for (const path in paths) {
689
- const methods = paths[path];
690
- for (const method in methods) {
691
- if (Utils.isSupportedApi(method, path, spec, options)) {
692
- const operationObject = methods[method];
693
- result[`${method.toUpperCase()} ${path}`] = operationObject;
694
- }
695
- }
696
- }
697
- return result;
475
+ return command;
698
476
  }
699
- static validateSpec(spec, parser, isSwaggerFile, options) {
477
+ static validateSpec(spec, parser, apiMap, isSwaggerFile, options) {
700
478
  const errors = [];
701
479
  const warnings = [];
702
480
  if (isSwaggerFile) {
@@ -705,7 +483,6 @@ class Utils {
705
483
  content: ConstantString.ConvertSwaggerToOpenAPI,
706
484
  });
707
485
  }
708
- // Server validation
709
486
  const serverErrors = Utils.validateServer(spec, options);
710
487
  errors.push(...serverErrors);
711
488
  // Remote reference not supported
@@ -719,8 +496,8 @@ class Utils {
719
496
  });
720
497
  }
721
498
  // No supported API
722
- const apiMap = Utils.listSupportedAPIs(spec, options);
723
- if (Object.keys(apiMap).length === 0) {
499
+ const validAPIs = Object.entries(apiMap).filter(([, value]) => value.isValid);
500
+ if (validAPIs.length === 0) {
724
501
  errors.push({
725
502
  type: ErrorType.NoSupportedApi,
726
503
  content: ConstantString.NoSupportedApi,
@@ -729,8 +506,8 @@ class Utils {
729
506
  // OperationId missing
730
507
  const apisMissingOperationId = [];
731
508
  for (const key in apiMap) {
732
- const pathObjectItem = apiMap[key];
733
- if (!pathObjectItem.operationId) {
509
+ const { operation } = apiMap[key];
510
+ if (!operation.operationId) {
734
511
  apisMissingOperationId.push(key);
735
512
  }
736
513
  }
@@ -786,6 +563,389 @@ class Utils {
786
563
  }
787
564
  }
788
565
 
566
+ // Copyright (c) Microsoft Corporation.
567
+ class Validator {
568
+ validateMethodAndPath(method, path) {
569
+ const result = { isValid: true, reason: [] };
570
+ if (this.options.allowMethods && !this.options.allowMethods.includes(method)) {
571
+ result.isValid = false;
572
+ result.reason.push(ErrorType.MethodNotAllowed);
573
+ return result;
574
+ }
575
+ const pathObj = this.spec.paths[path];
576
+ if (!pathObj || !pathObj[method]) {
577
+ result.isValid = false;
578
+ result.reason.push(ErrorType.UrlPathNotExist);
579
+ return result;
580
+ }
581
+ return result;
582
+ }
583
+ validateResponse(method, path) {
584
+ const result = { isValid: true, reason: [] };
585
+ const operationObject = this.spec.paths[path][method];
586
+ const { json, multipleMediaType } = Utils.getResponseJson(operationObject);
587
+ // only support response body only contains “application/json” content type
588
+ if (multipleMediaType) {
589
+ result.reason.push(ErrorType.ResponseContainMultipleMediaTypes);
590
+ }
591
+ else if (Object.keys(json).length === 0) {
592
+ // response body should not be empty
593
+ result.reason.push(ErrorType.ResponseJsonIsEmpty);
594
+ }
595
+ return result;
596
+ }
597
+ validateServer(method, path) {
598
+ const pathObj = this.spec.paths[path];
599
+ const result = { isValid: true, reason: [] };
600
+ const operationObject = pathObj[method];
601
+ const rootServer = this.spec.servers && this.spec.servers[0];
602
+ const methodServer = this.spec.paths[path].servers && this.spec.paths[path].servers[0];
603
+ const operationServer = operationObject.servers && operationObject.servers[0];
604
+ const serverUrl = operationServer || methodServer || rootServer;
605
+ if (!serverUrl) {
606
+ // should contain server URL
607
+ result.reason.push(ErrorType.NoServerInformation);
608
+ }
609
+ else {
610
+ // server url should be absolute url with https protocol
611
+ const serverValidateResult = Utils.checkServerUrl([serverUrl]);
612
+ result.reason.push(...serverValidateResult.map((item) => item.type));
613
+ }
614
+ return result;
615
+ }
616
+ validateAuth(method, path) {
617
+ const pathObj = this.spec.paths[path];
618
+ const operationObject = pathObj[method];
619
+ const securities = operationObject.security;
620
+ const authSchemeArray = Utils.getAuthArray(securities, this.spec);
621
+ if (authSchemeArray.length === 0) {
622
+ return { isValid: true, reason: [] };
623
+ }
624
+ if (this.options.allowAPIKeyAuth ||
625
+ this.options.allowOauth2 ||
626
+ this.options.allowBearerTokenAuth) {
627
+ // Currently we don't support multiple auth in one operation
628
+ if (authSchemeArray.length > 0 && authSchemeArray.every((auths) => auths.length > 1)) {
629
+ return {
630
+ isValid: false,
631
+ reason: [ErrorType.MultipleAuthNotSupported],
632
+ };
633
+ }
634
+ for (const auths of authSchemeArray) {
635
+ if (auths.length === 1) {
636
+ if ((this.options.allowAPIKeyAuth && Utils.isAPIKeyAuth(auths[0].authScheme)) ||
637
+ (this.options.allowOauth2 && Utils.isOAuthWithAuthCodeFlow(auths[0].authScheme)) ||
638
+ (this.options.allowBearerTokenAuth && Utils.isBearerTokenAuth(auths[0].authScheme))) {
639
+ return { isValid: true, reason: [] };
640
+ }
641
+ }
642
+ }
643
+ }
644
+ return { isValid: false, reason: [ErrorType.AuthTypeIsNotSupported] };
645
+ }
646
+ checkPostBodySchema(schema, isRequired = false) {
647
+ var _a;
648
+ const paramResult = {
649
+ requiredNum: 0,
650
+ optionalNum: 0,
651
+ isValid: true,
652
+ reason: [],
653
+ };
654
+ if (Object.keys(schema).length === 0) {
655
+ return paramResult;
656
+ }
657
+ const isRequiredWithoutDefault = isRequired && schema.default === undefined;
658
+ const isCopilot = this.projectType === ProjectType.Copilot;
659
+ if (isCopilot && this.hasNestedObjectInSchema(schema)) {
660
+ paramResult.isValid = false;
661
+ paramResult.reason = [ErrorType.RequestBodyContainsNestedObject];
662
+ return paramResult;
663
+ }
664
+ if (schema.type === "string" ||
665
+ schema.type === "integer" ||
666
+ schema.type === "boolean" ||
667
+ schema.type === "number") {
668
+ if (isRequiredWithoutDefault) {
669
+ paramResult.requiredNum = paramResult.requiredNum + 1;
670
+ }
671
+ else {
672
+ paramResult.optionalNum = paramResult.optionalNum + 1;
673
+ }
674
+ }
675
+ else if (schema.type === "object") {
676
+ const { properties } = schema;
677
+ for (const property in properties) {
678
+ let isRequired = false;
679
+ if (schema.required && ((_a = schema.required) === null || _a === void 0 ? void 0 : _a.indexOf(property)) >= 0) {
680
+ isRequired = true;
681
+ }
682
+ const result = this.checkPostBodySchema(properties[property], isRequired);
683
+ paramResult.requiredNum += result.requiredNum;
684
+ paramResult.optionalNum += result.optionalNum;
685
+ paramResult.isValid = paramResult.isValid && result.isValid;
686
+ paramResult.reason.push(...result.reason);
687
+ }
688
+ }
689
+ else {
690
+ if (isRequiredWithoutDefault && !isCopilot) {
691
+ paramResult.isValid = false;
692
+ paramResult.reason.push(ErrorType.PostBodyContainsRequiredUnsupportedSchema);
693
+ }
694
+ }
695
+ return paramResult;
696
+ }
697
+ checkParamSchema(paramObject) {
698
+ const paramResult = {
699
+ requiredNum: 0,
700
+ optionalNum: 0,
701
+ isValid: true,
702
+ reason: [],
703
+ };
704
+ if (!paramObject) {
705
+ return paramResult;
706
+ }
707
+ const isCopilot = this.projectType === ProjectType.Copilot;
708
+ for (let i = 0; i < paramObject.length; i++) {
709
+ const param = paramObject[i];
710
+ const schema = param.schema;
711
+ if (isCopilot && this.hasNestedObjectInSchema(schema)) {
712
+ paramResult.isValid = false;
713
+ paramResult.reason.push(ErrorType.ParamsContainsNestedObject);
714
+ continue;
715
+ }
716
+ const isRequiredWithoutDefault = param.required && schema.default === undefined;
717
+ if (isCopilot) {
718
+ if (isRequiredWithoutDefault) {
719
+ paramResult.requiredNum = paramResult.requiredNum + 1;
720
+ }
721
+ else {
722
+ paramResult.optionalNum = paramResult.optionalNum + 1;
723
+ }
724
+ continue;
725
+ }
726
+ if (param.in === "header" || param.in === "cookie") {
727
+ if (isRequiredWithoutDefault) {
728
+ paramResult.isValid = false;
729
+ paramResult.reason.push(ErrorType.ParamsContainRequiredUnsupportedSchema);
730
+ }
731
+ continue;
732
+ }
733
+ if (schema.type !== "boolean" &&
734
+ schema.type !== "string" &&
735
+ schema.type !== "number" &&
736
+ schema.type !== "integer") {
737
+ if (isRequiredWithoutDefault) {
738
+ paramResult.isValid = false;
739
+ paramResult.reason.push(ErrorType.ParamsContainRequiredUnsupportedSchema);
740
+ }
741
+ continue;
742
+ }
743
+ if (param.in === "query" || param.in === "path") {
744
+ if (isRequiredWithoutDefault) {
745
+ paramResult.requiredNum = paramResult.requiredNum + 1;
746
+ }
747
+ else {
748
+ paramResult.optionalNum = paramResult.optionalNum + 1;
749
+ }
750
+ }
751
+ }
752
+ return paramResult;
753
+ }
754
+ hasNestedObjectInSchema(schema) {
755
+ if (schema.type === "object") {
756
+ for (const property in schema.properties) {
757
+ const nestedSchema = schema.properties[property];
758
+ if (nestedSchema.type === "object") {
759
+ return true;
760
+ }
761
+ }
762
+ }
763
+ return false;
764
+ }
765
+ }
766
+
767
+ // Copyright (c) Microsoft Corporation.
768
+ class CopilotValidator extends Validator {
769
+ constructor(spec, options) {
770
+ super();
771
+ this.projectType = ProjectType.Copilot;
772
+ this.options = options;
773
+ this.spec = spec;
774
+ }
775
+ validateAPI(method, path) {
776
+ const result = { isValid: true, reason: [] };
777
+ method = method.toLocaleLowerCase();
778
+ // validate method and path
779
+ const methodAndPathResult = this.validateMethodAndPath(method, path);
780
+ if (!methodAndPathResult.isValid) {
781
+ return methodAndPathResult;
782
+ }
783
+ const operationObject = this.spec.paths[path][method];
784
+ // validate auth
785
+ const authCheckResult = this.validateAuth(method, path);
786
+ result.reason.push(...authCheckResult.reason);
787
+ // validate operationId
788
+ if (!this.options.allowMissingId && !operationObject.operationId) {
789
+ result.reason.push(ErrorType.MissingOperationId);
790
+ }
791
+ // validate server
792
+ const validateServerResult = this.validateServer(method, path);
793
+ result.reason.push(...validateServerResult.reason);
794
+ // validate response
795
+ const validateResponseResult = this.validateResponse(method, path);
796
+ result.reason.push(...validateResponseResult.reason);
797
+ // validate requestBody
798
+ const requestBody = operationObject.requestBody;
799
+ const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
800
+ if (Utils.containMultipleMediaTypes(requestBody)) {
801
+ result.reason.push(ErrorType.PostBodyContainMultipleMediaTypes);
802
+ }
803
+ if (requestJsonBody) {
804
+ const requestBodySchema = requestJsonBody.schema;
805
+ if (requestBodySchema.type !== "object") {
806
+ result.reason.push(ErrorType.PostBodySchemaIsNotJson);
807
+ }
808
+ const requestBodyParamResult = this.checkPostBodySchema(requestBodySchema, requestBody.required);
809
+ result.reason.push(...requestBodyParamResult.reason);
810
+ }
811
+ // validate parameters
812
+ const paramObject = operationObject.parameters;
813
+ const paramResult = this.checkParamSchema(paramObject);
814
+ result.reason.push(...paramResult.reason);
815
+ if (result.reason.length > 0) {
816
+ result.isValid = false;
817
+ }
818
+ return result;
819
+ }
820
+ }
821
+
822
+ // Copyright (c) Microsoft Corporation.
823
+ class SMEValidator extends Validator {
824
+ constructor(spec, options) {
825
+ super();
826
+ this.projectType = ProjectType.SME;
827
+ this.options = options;
828
+ this.spec = spec;
829
+ }
830
+ validateAPI(method, path) {
831
+ const result = { isValid: true, reason: [] };
832
+ method = method.toLocaleLowerCase();
833
+ // validate method and path
834
+ const methodAndPathResult = this.validateMethodAndPath(method, path);
835
+ if (!methodAndPathResult.isValid) {
836
+ return methodAndPathResult;
837
+ }
838
+ const operationObject = this.spec.paths[path][method];
839
+ // validate auth
840
+ const authCheckResult = this.validateAuth(method, path);
841
+ result.reason.push(...authCheckResult.reason);
842
+ // validate operationId
843
+ if (!this.options.allowMissingId && !operationObject.operationId) {
844
+ result.reason.push(ErrorType.MissingOperationId);
845
+ }
846
+ // validate server
847
+ const validateServerResult = this.validateServer(method, path);
848
+ result.reason.push(...validateServerResult.reason);
849
+ // validate response
850
+ const validateResponseResult = this.validateResponse(method, path);
851
+ result.reason.push(...validateResponseResult.reason);
852
+ let postBodyResult = {
853
+ requiredNum: 0,
854
+ optionalNum: 0,
855
+ isValid: true,
856
+ reason: [],
857
+ };
858
+ // validate requestBody
859
+ const requestBody = operationObject.requestBody;
860
+ const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
861
+ if (Utils.containMultipleMediaTypes(requestBody)) {
862
+ result.reason.push(ErrorType.PostBodyContainMultipleMediaTypes);
863
+ }
864
+ if (requestJsonBody) {
865
+ const requestBodySchema = requestJsonBody.schema;
866
+ postBodyResult = this.checkPostBodySchema(requestBodySchema, requestBody.required);
867
+ result.reason.push(...postBodyResult.reason);
868
+ }
869
+ // validate parameters
870
+ const paramObject = operationObject.parameters;
871
+ const paramResult = this.checkParamSchema(paramObject);
872
+ result.reason.push(...paramResult.reason);
873
+ // validate total parameters count
874
+ if (paramResult.isValid && postBodyResult.isValid) {
875
+ const paramCountResult = this.validateParamCount(postBodyResult, paramResult);
876
+ result.reason.push(...paramCountResult.reason);
877
+ }
878
+ if (result.reason.length > 0) {
879
+ result.isValid = false;
880
+ }
881
+ return result;
882
+ }
883
+ validateParamCount(postBodyResult, paramResult) {
884
+ const result = { isValid: true, reason: [] };
885
+ const totalRequiredParams = postBodyResult.requiredNum + paramResult.requiredNum;
886
+ const totalParams = totalRequiredParams + postBodyResult.optionalNum + paramResult.optionalNum;
887
+ if (totalRequiredParams > 1) {
888
+ if (!this.options.allowMultipleParameters ||
889
+ totalRequiredParams > SMEValidator.SMERequiredParamsMaxNum) {
890
+ result.reason.push(ErrorType.ExceededRequiredParamsLimit);
891
+ }
892
+ }
893
+ else if (totalParams === 0) {
894
+ result.reason.push(ErrorType.NoParameter);
895
+ }
896
+ return result;
897
+ }
898
+ }
899
+ SMEValidator.SMERequiredParamsMaxNum = 5;
900
+
901
+ // Copyright (c) Microsoft Corporation.
902
+ class TeamsAIValidator extends Validator {
903
+ constructor(spec, options) {
904
+ super();
905
+ this.projectType = ProjectType.TeamsAi;
906
+ this.options = options;
907
+ this.spec = spec;
908
+ }
909
+ validateAPI(method, path) {
910
+ const result = { isValid: true, reason: [] };
911
+ method = method.toLocaleLowerCase();
912
+ // validate method and path
913
+ const methodAndPathResult = this.validateMethodAndPath(method, path);
914
+ if (!methodAndPathResult.isValid) {
915
+ return methodAndPathResult;
916
+ }
917
+ const operationObject = this.spec.paths[path][method];
918
+ // validate operationId
919
+ if (!this.options.allowMissingId && !operationObject.operationId) {
920
+ result.reason.push(ErrorType.MissingOperationId);
921
+ }
922
+ // validate server
923
+ const validateServerResult = this.validateServer(method, path);
924
+ result.reason.push(...validateServerResult.reason);
925
+ if (result.reason.length > 0) {
926
+ result.isValid = false;
927
+ }
928
+ return result;
929
+ }
930
+ }
931
+
932
+ class ValidatorFactory {
933
+ static create(spec, options) {
934
+ var _a;
935
+ const type = (_a = options.projectType) !== null && _a !== void 0 ? _a : ProjectType.SME;
936
+ switch (type) {
937
+ case ProjectType.SME:
938
+ return new SMEValidator(spec, options);
939
+ case ProjectType.Copilot:
940
+ return new CopilotValidator(spec, options);
941
+ case ProjectType.TeamsAi:
942
+ return new TeamsAIValidator(spec, options);
943
+ default:
944
+ throw new Error(`Invalid project type: ${type}`);
945
+ }
946
+ }
947
+ }
948
+
789
949
  // Copyright (c) Microsoft Corporation.
790
950
  /**
791
951
  * A class that parses an OpenAPI specification file and provides methods to validate, list, and generate artifacts.
@@ -842,7 +1002,8 @@ class SpecParser {
842
1002
  ],
843
1003
  };
844
1004
  }
845
- return Utils.validateSpec(this.spec, this.parser, !!this.isSwaggerFile, this.options);
1005
+ const apiMap = this.getAPIs(this.spec);
1006
+ return Utils.validateSpec(this.spec, this.parser, apiMap, !!this.isSwaggerFile, this.options);
846
1007
  }
847
1008
  catch (err) {
848
1009
  throw new SpecParserError(err.toString(), ErrorType.ValidateFailed);
@@ -851,17 +1012,20 @@ class SpecParser {
851
1012
  async listSupportedAPIInfo() {
852
1013
  try {
853
1014
  await this.loadSpec();
854
- const apiMap = this.getAllSupportedAPIs(this.spec);
1015
+ const apiMap = this.getAPIs(this.spec);
855
1016
  const apiInfos = [];
856
1017
  for (const key in apiMap) {
857
- const pathObjectItem = apiMap[key];
1018
+ const { operation, isValid } = apiMap[key];
1019
+ if (!isValid) {
1020
+ continue;
1021
+ }
858
1022
  const [method, path] = key.split(" ");
859
- const operationId = pathObjectItem.operationId;
1023
+ const operationId = operation.operationId;
860
1024
  // In Browser environment, this api is by default not support api without operationId
861
1025
  if (!operationId) {
862
1026
  continue;
863
1027
  }
864
- const [command, warning] = Utils.parseApiInfo(pathObjectItem, this.options);
1028
+ const command = Utils.parseApiInfo(operation, this.options);
865
1029
  const apiInfo = {
866
1030
  method: method,
867
1031
  path: path,
@@ -870,9 +1034,6 @@ class SpecParser {
870
1034
  parameters: command.parameters,
871
1035
  description: command.description,
872
1036
  };
873
- if (warning) {
874
- apiInfo.warning = warning;
875
- }
876
1037
  apiInfos.push(apiInfo);
877
1038
  }
878
1039
  return apiInfos;
@@ -931,21 +1092,42 @@ class SpecParser {
931
1092
  this.spec = (await this.parser.dereference(clonedUnResolveSpec));
932
1093
  }
933
1094
  }
934
- getAllSupportedAPIs(spec) {
1095
+ getAPIs(spec) {
935
1096
  if (this.apiMap !== undefined) {
936
1097
  return this.apiMap;
937
1098
  }
938
- const result = Utils.listSupportedAPIs(spec, this.options);
1099
+ const result = this.listAPIs(spec);
939
1100
  this.apiMap = result;
940
1101
  return result;
941
1102
  }
1103
+ listAPIs(spec) {
1104
+ var _a;
1105
+ const paths = spec.paths;
1106
+ const result = {};
1107
+ for (const path in paths) {
1108
+ const methods = paths[path];
1109
+ for (const method in methods) {
1110
+ const operationObject = methods[method];
1111
+ if (((_a = this.options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && operationObject) {
1112
+ const validator = ValidatorFactory.create(spec, this.options);
1113
+ const validateResult = validator.validateAPI(method, path);
1114
+ result[`${method.toUpperCase()} ${path}`] = {
1115
+ operation: operationObject,
1116
+ isValid: validateResult.isValid,
1117
+ reason: validateResult.reason,
1118
+ };
1119
+ }
1120
+ }
1121
+ }
1122
+ return result;
1123
+ }
942
1124
  }
943
1125
 
944
1126
  // Copyright (c) Microsoft Corporation.
945
1127
  class AdaptiveCardGenerator {
946
1128
  static generateAdaptiveCard(operationItem) {
947
1129
  try {
948
- const json = Utils.getResponseJson(operationItem);
1130
+ const { json } = Utils.getResponseJson(operationItem);
949
1131
  let cardBody = [];
950
1132
  let schema = json.schema;
951
1133
  let jsonPath = "$";