@microsoft/m365-spec-parser 0.1.1-alpha.8d8f5a0bb.0 → 0.1.1-alpha.a277dba4e.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.
@@ -15,7 +15,8 @@ var ErrorType;
15
15
  ErrorType["NoExtraAPICanBeAdded"] = "no-extra-api-can-be-added";
16
16
  ErrorType["ResolveServerUrlFailed"] = "resolve-server-url-failed";
17
17
  ErrorType["SwaggerNotSupported"] = "swagger-not-supported";
18
- ErrorType["MultipleAPIKeyNotSupported"] = "multiple-api-key-not-supported";
18
+ ErrorType["MultipleAuthNotSupported"] = "multiple-auth-not-supported";
19
+ ErrorType["SpecVersionNotSupported"] = "spec-version-not-supported";
19
20
  ErrorType["ListFailed"] = "list-failed";
20
21
  ErrorType["listSupportedAPIInfoFailed"] = "list-supported-api-info-failed";
21
22
  ErrorType["FilterSpecFailed"] = "filter-spec-failed";
@@ -46,7 +47,13 @@ var ValidationStatus;
46
47
  ValidationStatus[ValidationStatus["Valid"] = 0] = "Valid";
47
48
  ValidationStatus[ValidationStatus["Warning"] = 1] = "Warning";
48
49
  ValidationStatus[ValidationStatus["Error"] = 2] = "Error";
49
- })(ValidationStatus || (ValidationStatus = {}));
50
+ })(ValidationStatus || (ValidationStatus = {}));
51
+ var ProjectType;
52
+ (function (ProjectType) {
53
+ ProjectType[ProjectType["Copilot"] = 0] = "Copilot";
54
+ ProjectType[ProjectType["SME"] = 1] = "SME";
55
+ ProjectType[ProjectType["TeamsAi"] = 2] = "TeamsAi";
56
+ })(ProjectType || (ProjectType = {}));
50
57
 
51
58
  // Copyright (c) Microsoft Corporation.
52
59
  class SpecParserError extends Error {
@@ -73,7 +80,8 @@ ConstantString.ResolveServerUrlFailed = "Unable to resolve the server URL: pleas
73
80
  ConstantString.OperationOnlyContainsOptionalParam = "Operation %s contains multiple optional parameters. The first optional parameter is used for this command.";
74
81
  ConstantString.ConvertSwaggerToOpenAPI = "The Swagger 2.0 file has been converted to OpenAPI 3.0.";
75
82
  ConstantString.SwaggerNotSupported = "Swagger 2.0 is not supported. Please convert to OpenAPI 3.0 manually before proceeding.";
76
- ConstantString.MultipleAPIKeyNotSupported = "Multiple API keys are not supported. Please make sure that all selected APIs use the same API key.";
83
+ ConstantString.SpecVersionNotSupported = "Unsupported OpenAPI version %s. Please use version 3.0.x.";
84
+ ConstantString.MultipleAuthNotSupported = "Multiple authentication methods are unsupported. Ensure all selected APIs use identical authentication.";
77
85
  ConstantString.UnsupportedSchema = "Unsupported schema in %s %s: %s";
78
86
  ConstantString.WrappedCardVersion = "devPreview";
79
87
  ConstantString.WrappedCardSchema = "https://developer.microsoft.com/json-schemas/teams/vDevPreview/MicrosoftTeams.ResponseRenderingTemplate.schema.json";
@@ -86,6 +94,7 @@ ConstantString.AdaptiveCardType = "AdaptiveCard";
86
94
  ConstantString.TextBlockType = "TextBlock";
87
95
  ConstantString.ContainerType = "Container";
88
96
  ConstantString.RegistrationIdPostfix = "REGISTRATION_ID";
97
+ ConstantString.OAuthRegistrationIdPostFix = "OAUTH_REGISTRATION_ID";
89
98
  ConstantString.ResponseCodeFor20X = [
90
99
  "200",
91
100
  "201",
@@ -145,7 +154,8 @@ ConstantString.FullDescriptionMaxLens = 4000;
145
154
  ConstantString.CommandDescriptionMaxLens = 128;
146
155
  ConstantString.ParameterDescriptionMaxLens = 128;
147
156
  ConstantString.CommandTitleMaxLens = 32;
148
- ConstantString.ParameterTitleMaxLens = 32;
157
+ ConstantString.ParameterTitleMaxLens = 32;
158
+ ConstantString.SMERequiredParamsMaxNum = 5;
149
159
 
150
160
  // Copyright (c) Microsoft Corporation.
151
161
  class Utils {
@@ -258,6 +268,9 @@ class Utils {
258
268
  }
259
269
  return paramResult;
260
270
  }
271
+ static containMultipleMediaTypes(bodyObject) {
272
+ return Object.keys((bodyObject === null || bodyObject === void 0 ? void 0 : bodyObject.content) || {}).length > 1;
273
+ }
261
274
  /**
262
275
  * Checks if the given API is supported.
263
276
  * @param {string} method - The HTTP method of the API.
@@ -272,32 +285,40 @@ class Utils {
272
285
  * 5. response body should be “application/json” and not empty, and response code should be 20X
273
286
  * 6. only support request body with “application/json” content type
274
287
  */
275
- static isSupportedApi(method, path, spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2, isCopilot) {
288
+ static isSupportedApi(method, path, spec, options) {
289
+ var _a;
276
290
  const pathObj = spec.paths[path];
277
291
  method = method.toLocaleLowerCase();
278
292
  if (pathObj) {
279
- if ((method === ConstantString.PostMethod || method === ConstantString.GetMethod) &&
280
- pathObj[method]) {
293
+ if (((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && pathObj[method]) {
281
294
  const securities = pathObj[method].security;
282
- const authArray = Utils.getAuthArray(securities, spec);
283
- if (!Utils.isSupportedAuth(authArray, allowAPIKeyAuth, allowOauth2)) {
284
- return false;
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
+ }
285
303
  }
286
304
  const operationObject = pathObj[method];
287
- if (!allowMissingId && !operationObject.operationId) {
305
+ if (!options.allowMissingId && !operationObject.operationId) {
288
306
  return false;
289
307
  }
290
308
  const paramObject = operationObject.parameters;
291
309
  const requestBody = operationObject.requestBody;
292
310
  const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
293
- const mediaTypesCount = Object.keys((requestBody === null || requestBody === void 0 ? void 0 : requestBody.content) || {}).length;
294
- if (mediaTypesCount > 1) {
311
+ if (!isTeamsAi && Utils.containMultipleMediaTypes(requestBody)) {
295
312
  return false;
296
313
  }
297
- const responseJson = Utils.getResponseJson(operationObject);
314
+ const responseJson = Utils.getResponseJson(operationObject, isTeamsAi);
298
315
  if (Object.keys(responseJson).length === 0) {
299
316
  return false;
300
317
  }
318
+ // Teams AI project doesn't care about request parameters/body
319
+ if (isTeamsAi) {
320
+ return true;
321
+ }
301
322
  let requestBodyParamResult = {
302
323
  requiredNum: 0,
303
324
  optionalNum: 0,
@@ -322,8 +343,9 @@ class Utils {
322
343
  return true;
323
344
  }
324
345
  if (requestBodyParamResult.requiredNum + paramResult.requiredNum > 1) {
325
- if (allowMultipleParameters &&
326
- requestBodyParamResult.requiredNum + paramResult.requiredNum <= 5) {
346
+ if (options.allowMultipleParameters &&
347
+ requestBodyParamResult.requiredNum + paramResult.requiredNum <=
348
+ ConstantString.SMERequiredParamsMaxNum) {
327
349
  return true;
328
350
  }
329
351
  return false;
@@ -342,29 +364,20 @@ class Utils {
342
364
  }
343
365
  return false;
344
366
  }
345
- static isSupportedAuth(authSchemaArray, allowAPIKeyAuth, allowOauth2) {
346
- if (authSchemaArray.length === 0) {
367
+ static isSupportedAuth(authSchemeArray, options) {
368
+ if (authSchemeArray.length === 0) {
347
369
  return true;
348
370
  }
349
- if (allowAPIKeyAuth || allowOauth2) {
371
+ if (options.allowAPIKeyAuth || options.allowOauth2 || options.allowBearerTokenAuth) {
350
372
  // Currently we don't support multiple auth in one operation
351
- if (authSchemaArray.length > 0 && authSchemaArray.every((auths) => auths.length > 1)) {
373
+ if (authSchemeArray.length > 0 && authSchemeArray.every((auths) => auths.length > 1)) {
352
374
  return false;
353
375
  }
354
- for (const auths of authSchemaArray) {
376
+ for (const auths of authSchemeArray) {
355
377
  if (auths.length === 1) {
356
- if (!allowOauth2 && allowAPIKeyAuth && Utils.isAPIKeyAuth(auths[0].authSchema)) {
357
- return true;
358
- }
359
- else if (!allowAPIKeyAuth &&
360
- allowOauth2 &&
361
- Utils.isBearerTokenAuth(auths[0].authSchema)) {
362
- return true;
363
- }
364
- else if (allowAPIKeyAuth &&
365
- allowOauth2 &&
366
- (Utils.isAPIKeyAuth(auths[0].authSchema) ||
367
- Utils.isBearerTokenAuth(auths[0].authSchema))) {
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))) {
368
381
  return true;
369
382
  }
370
383
  }
@@ -372,13 +385,17 @@ class Utils {
372
385
  }
373
386
  return false;
374
387
  }
375
- static isAPIKeyAuth(authSchema) {
376
- return authSchema.type === "apiKey";
388
+ static isBearerTokenAuth(authScheme) {
389
+ return authScheme.type === "http" && authScheme.scheme === "bearer";
377
390
  }
378
- static isBearerTokenAuth(authSchema) {
379
- return (authSchema.type === "oauth2" ||
380
- authSchema.type === "openIdConnect" ||
381
- (authSchema.type === "http" && authSchema.scheme === "bearer"));
391
+ static isAPIKeyAuth(authScheme) {
392
+ return authScheme.type === "apiKey";
393
+ }
394
+ static isOAuthWithAuthCodeFlow(authScheme) {
395
+ if (authScheme.type === "oauth2" && authScheme.flows && authScheme.flows.authorizationCode) {
396
+ return true;
397
+ }
398
+ return false;
382
399
  }
383
400
  static getAuthArray(securities, spec) {
384
401
  var _a;
@@ -391,7 +408,7 @@ class Utils {
391
408
  for (const name in security) {
392
409
  const auth = securitySchemas[name];
393
410
  authArray.push({
394
- authSchema: auth,
411
+ authScheme: auth,
395
412
  name: name,
396
413
  });
397
414
  }
@@ -406,18 +423,19 @@ class Utils {
406
423
  static updateFirstLetter(str) {
407
424
  return str.charAt(0).toUpperCase() + str.slice(1);
408
425
  }
409
- static getResponseJson(operationObject) {
426
+ static getResponseJson(operationObject, isTeamsAiProject = false) {
410
427
  var _a, _b;
411
428
  let json = {};
412
429
  for (const code of ConstantString.ResponseCodeFor20X) {
413
430
  const responseObject = (_a = operationObject === null || operationObject === void 0 ? void 0 : operationObject.responses) === null || _a === void 0 ? void 0 : _a[code];
414
- const mediaTypesCount = Object.keys((responseObject === null || responseObject === void 0 ? void 0 : responseObject.content) || {}).length;
415
- if (mediaTypesCount > 1) {
416
- return {};
417
- }
418
431
  if ((_b = responseObject === null || responseObject === void 0 ? void 0 : responseObject.content) === null || _b === void 0 ? void 0 : _b["application/json"]) {
419
432
  json = responseObject.content["application/json"];
420
- break;
433
+ if (!isTeamsAiProject && Utils.containMultipleMediaTypes(responseObject)) {
434
+ json = {};
435
+ }
436
+ else {
437
+ break;
438
+ }
421
439
  }
422
440
  }
423
441
  return json;
@@ -491,7 +509,7 @@ class Utils {
491
509
  }
492
510
  return errors;
493
511
  }
494
- static validateServer(spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2, isCopilot) {
512
+ static validateServer(spec, options) {
495
513
  const errors = [];
496
514
  let hasTopLevelServers = false;
497
515
  let hasPathLevelServers = false;
@@ -512,7 +530,7 @@ class Utils {
512
530
  }
513
531
  for (const method in methods) {
514
532
  const operationObject = methods[method];
515
- if (Utils.isSupportedApi(method, path, spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2, isCopilot)) {
533
+ if (Utils.isSupportedApi(method, path, spec, options)) {
516
534
  if ((operationObject === null || operationObject === void 0 ? void 0 : operationObject.servers) && operationObject.servers.length >= 1) {
517
535
  hasOperationLevelServers = true;
518
536
  const serverErrors = Utils.checkServerUrl(operationObject.servers);
@@ -555,6 +573,7 @@ class Utils {
555
573
  Utils.updateParameterWithInputType(schema, parameter);
556
574
  }
557
575
  if (isRequired && schema.default === undefined) {
576
+ parameter.isRequired = true;
558
577
  requiredParams.push(parameter);
559
578
  }
560
579
  else {
@@ -599,7 +618,7 @@ class Utils {
599
618
  param.value = schema.default;
600
619
  }
601
620
  }
602
- static parseApiInfo(operationItem, allowMultipleParameters) {
621
+ static parseApiInfo(operationItem, options) {
603
622
  var _a, _b;
604
623
  const requiredParams = [];
605
624
  const optionalParams = [];
@@ -613,11 +632,12 @@ class Utils {
613
632
  description: ((_a = param.description) !== null && _a !== void 0 ? _a : "").slice(0, ConstantString.ParameterDescriptionMaxLens),
614
633
  };
615
634
  const schema = param.schema;
616
- if (allowMultipleParameters && schema) {
635
+ if (options.allowMultipleParameters && schema) {
617
636
  Utils.updateParameterWithInputType(schema, parameter);
618
637
  }
619
638
  if (param.in !== "header" && param.in !== "cookie") {
620
639
  if (param.required && (schema === null || schema === void 0 ? void 0 : schema.default) === undefined) {
640
+ parameter.isRequired = true;
621
641
  requiredParams.push(parameter);
622
642
  }
623
643
  else {
@@ -631,7 +651,7 @@ class Utils {
631
651
  const requestJson = requestBody.content["application/json"];
632
652
  if (Object.keys(requestJson).length !== 0) {
633
653
  const schema = requestJson.schema;
634
- const [requiredP, optionalP] = Utils.generateParametersFromSchema(schema, "requestBody", allowMultipleParameters, requestBody.required);
654
+ const [requiredP, optionalP] = Utils.generateParametersFromSchema(schema, "requestBody", !!options.allowMultipleParameters, requestBody.required);
635
655
  requiredParams.push(...requiredP);
636
656
  optionalParams.push(...optionalP);
637
657
  }
@@ -662,14 +682,13 @@ class Utils {
662
682
  }
663
683
  return [command, warning];
664
684
  }
665
- static listSupportedAPIs(spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2, isCopilot) {
685
+ static listSupportedAPIs(spec, options) {
666
686
  const paths = spec.paths;
667
687
  const result = {};
668
688
  for (const path in paths) {
669
689
  const methods = paths[path];
670
690
  for (const method in methods) {
671
- // For developer preview, only support GET operation with only 1 parameter without auth
672
- if (Utils.isSupportedApi(method, path, spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2, isCopilot)) {
691
+ if (Utils.isSupportedApi(method, path, spec, options)) {
673
692
  const operationObject = methods[method];
674
693
  result[`${method.toUpperCase()} ${path}`] = operationObject;
675
694
  }
@@ -677,7 +696,7 @@ class Utils {
677
696
  }
678
697
  return result;
679
698
  }
680
- static validateSpec(spec, parser, isSwaggerFile, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2, isCopilot) {
699
+ static validateSpec(spec, parser, isSwaggerFile, options) {
681
700
  const errors = [];
682
701
  const warnings = [];
683
702
  if (isSwaggerFile) {
@@ -687,7 +706,7 @@ class Utils {
687
706
  });
688
707
  }
689
708
  // Server validation
690
- const serverErrors = Utils.validateServer(spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2, isCopilot);
709
+ const serverErrors = Utils.validateServer(spec, options);
691
710
  errors.push(...serverErrors);
692
711
  // Remote reference not supported
693
712
  const refPaths = parser.$refs.paths();
@@ -700,7 +719,7 @@ class Utils {
700
719
  });
701
720
  }
702
721
  // No supported API
703
- const apiMap = Utils.listSupportedAPIs(spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2, isCopilot);
722
+ const apiMap = Utils.listSupportedAPIs(spec, options);
704
723
  if (Object.keys(apiMap).length === 0) {
705
724
  errors.push({
706
725
  type: ErrorType.NoSupportedApi,
@@ -752,6 +771,19 @@ class Utils {
752
771
  }
753
772
  return safeRegistrationIdEnvName;
754
773
  }
774
+ static getAllAPICount(spec) {
775
+ let count = 0;
776
+ const paths = spec.paths;
777
+ for (const path in paths) {
778
+ const methods = paths[path];
779
+ for (const method in methods) {
780
+ if (ConstantString.AllOperationMethods.includes(method)) {
781
+ count++;
782
+ }
783
+ }
784
+ }
785
+ return count;
786
+ }
755
787
  }
756
788
 
757
789
  // Copyright (c) Microsoft Corporation.
@@ -770,8 +802,10 @@ class SpecParser {
770
802
  allowSwagger: false,
771
803
  allowAPIKeyAuth: false,
772
804
  allowMultipleParameters: false,
805
+ allowBearerTokenAuth: false,
773
806
  allowOauth2: false,
774
- isCopilot: false,
807
+ allowMethods: ["get", "post"],
808
+ projectType: ProjectType.SME,
775
809
  };
776
810
  this.pathOrSpec = pathOrDoc;
777
811
  this.parser = new SwaggerParser();
@@ -808,7 +842,7 @@ class SpecParser {
808
842
  ],
809
843
  };
810
844
  }
811
- return Utils.validateSpec(this.spec, this.parser, !!this.isSwaggerFile, this.options.allowMissingId, this.options.allowAPIKeyAuth, this.options.allowMultipleParameters, this.options.allowOauth2, this.options.isCopilot);
845
+ return Utils.validateSpec(this.spec, this.parser, !!this.isSwaggerFile, this.options);
812
846
  }
813
847
  catch (err) {
814
848
  throw new SpecParserError(err.toString(), ErrorType.ValidateFailed);
@@ -827,7 +861,7 @@ class SpecParser {
827
861
  if (!operationId) {
828
862
  continue;
829
863
  }
830
- const [command, warning] = Utils.parseApiInfo(pathObjectItem, this.options.allowMultipleParameters);
864
+ const [command, warning] = Utils.parseApiInfo(pathObjectItem, this.options);
831
865
  const apiInfo = {
832
866
  method: method,
833
867
  path: path,
@@ -884,7 +918,7 @@ class SpecParser {
884
918
  * @param isMe Boolean that indicates whether the project is an Messaging Extension. For Messaging Extension, composeExtensions will be added in Teams app manifest.
885
919
  */
886
920
  // eslint-disable-next-line @typescript-eslint/require-await
887
- async generate(manifestPath, filter, outputSpecPath, adaptiveCardFolder, signal, isMe) {
921
+ async generate(manifestPath, filter, outputSpecPath, adaptiveCardFolder, signal) {
888
922
  throw new Error("Method not implemented.");
889
923
  }
890
924
  async loadSpec() {
@@ -901,7 +935,7 @@ class SpecParser {
901
935
  if (this.apiMap !== undefined) {
902
936
  return this.apiMap;
903
937
  }
904
- const result = Utils.listSupportedAPIs(spec, this.options.allowMissingId, this.options.allowAPIKeyAuth, this.options.allowMultipleParameters, this.options.allowOauth2, this.options.isCopilot);
938
+ const result = Utils.listSupportedAPIs(spec, this.options);
905
939
  this.apiMap = result;
906
940
  return result;
907
941
  }