@microsoft/m365-spec-parser 0.1.1-alpha.cf377d39f.0 → 0.1.1-alpha.e17ffd4d1.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,7 @@ 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
19
  ErrorType["ListFailed"] = "list-failed";
20
20
  ErrorType["listSupportedAPIInfoFailed"] = "list-supported-api-info-failed";
21
21
  ErrorType["FilterSpecFailed"] = "filter-spec-failed";
@@ -46,7 +46,13 @@ var ValidationStatus;
46
46
  ValidationStatus[ValidationStatus["Valid"] = 0] = "Valid";
47
47
  ValidationStatus[ValidationStatus["Warning"] = 1] = "Warning";
48
48
  ValidationStatus[ValidationStatus["Error"] = 2] = "Error";
49
- })(ValidationStatus || (ValidationStatus = {}));
49
+ })(ValidationStatus || (ValidationStatus = {}));
50
+ var ProjectType;
51
+ (function (ProjectType) {
52
+ ProjectType[ProjectType["Copilot"] = 0] = "Copilot";
53
+ ProjectType[ProjectType["SME"] = 1] = "SME";
54
+ ProjectType[ProjectType["TeamsAi"] = 2] = "TeamsAi";
55
+ })(ProjectType || (ProjectType = {}));
50
56
 
51
57
  // Copyright (c) Microsoft Corporation.
52
58
  class SpecParserError extends Error {
@@ -73,7 +79,8 @@ ConstantString.ResolveServerUrlFailed = "Unable to resolve the server URL: pleas
73
79
  ConstantString.OperationOnlyContainsOptionalParam = "Operation %s contains multiple optional parameters. The first optional parameter is used for this command.";
74
80
  ConstantString.ConvertSwaggerToOpenAPI = "The Swagger 2.0 file has been converted to OpenAPI 3.0.";
75
81
  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.";
82
+ ConstantString.MultipleAuthNotSupported = "Multiple authentication methods are unsupported. Ensure all selected APIs use identical authentication.";
83
+ ConstantString.UnsupportedSchema = "Unsupported schema in %s %s: %s";
77
84
  ConstantString.WrappedCardVersion = "devPreview";
78
85
  ConstantString.WrappedCardSchema = "https://developer.microsoft.com/json-schemas/teams/vDevPreview/MicrosoftTeams.ResponseRenderingTemplate.schema.json";
79
86
  ConstantString.WrappedCardResponseLayout = "list";
@@ -85,6 +92,7 @@ ConstantString.AdaptiveCardType = "AdaptiveCard";
85
92
  ConstantString.TextBlockType = "TextBlock";
86
93
  ConstantString.ContainerType = "Container";
87
94
  ConstantString.RegistrationIdPostfix = "REGISTRATION_ID";
95
+ ConstantString.OAuthRegistrationIdPostFix = "OAUTH_REGISTRATION_ID";
88
96
  ConstantString.ResponseCodeFor20X = [
89
97
  "200",
90
98
  "201",
@@ -144,11 +152,23 @@ ConstantString.FullDescriptionMaxLens = 4000;
144
152
  ConstantString.CommandDescriptionMaxLens = 128;
145
153
  ConstantString.ParameterDescriptionMaxLens = 128;
146
154
  ConstantString.CommandTitleMaxLens = 32;
147
- ConstantString.ParameterTitleMaxLens = 32;
155
+ ConstantString.ParameterTitleMaxLens = 32;
156
+ ConstantString.SMERequiredParamsMaxNum = 5;
148
157
 
149
158
  // Copyright (c) Microsoft Corporation.
150
159
  class Utils {
151
- static checkParameters(paramObject) {
160
+ static hasNestedObjectInSchema(schema) {
161
+ if (schema.type === "object") {
162
+ for (const property in schema.properties) {
163
+ const nestedSchema = schema.properties[property];
164
+ if (nestedSchema.type === "object") {
165
+ return true;
166
+ }
167
+ }
168
+ }
169
+ return false;
170
+ }
171
+ static checkParameters(paramObject, isCopilot) {
152
172
  const paramResult = {
153
173
  requiredNum: 0,
154
174
  optionalNum: 0,
@@ -160,7 +180,20 @@ class Utils {
160
180
  for (let i = 0; i < paramObject.length; i++) {
161
181
  const param = paramObject[i];
162
182
  const schema = param.schema;
183
+ if (isCopilot && this.hasNestedObjectInSchema(schema)) {
184
+ paramResult.isValid = false;
185
+ continue;
186
+ }
163
187
  const isRequiredWithoutDefault = param.required && schema.default === undefined;
188
+ if (isCopilot) {
189
+ if (isRequiredWithoutDefault) {
190
+ paramResult.requiredNum = paramResult.requiredNum + 1;
191
+ }
192
+ else {
193
+ paramResult.optionalNum = paramResult.optionalNum + 1;
194
+ }
195
+ continue;
196
+ }
164
197
  if (param.in === "header" || param.in === "cookie") {
165
198
  if (isRequiredWithoutDefault) {
166
199
  paramResult.isValid = false;
@@ -187,7 +220,7 @@ class Utils {
187
220
  }
188
221
  return paramResult;
189
222
  }
190
- static checkPostBody(schema, isRequired = false) {
223
+ static checkPostBody(schema, isRequired = false, isCopilot = false) {
191
224
  var _a;
192
225
  const paramResult = {
193
226
  requiredNum: 0,
@@ -198,6 +231,10 @@ class Utils {
198
231
  return paramResult;
199
232
  }
200
233
  const isRequiredWithoutDefault = isRequired && schema.default === undefined;
234
+ if (isCopilot && this.hasNestedObjectInSchema(schema)) {
235
+ paramResult.isValid = false;
236
+ return paramResult;
237
+ }
201
238
  if (schema.type === "string" ||
202
239
  schema.type === "integer" ||
203
240
  schema.type === "boolean" ||
@@ -216,19 +253,22 @@ class Utils {
216
253
  if (schema.required && ((_a = schema.required) === null || _a === void 0 ? void 0 : _a.indexOf(property)) >= 0) {
217
254
  isRequired = true;
218
255
  }
219
- const result = Utils.checkPostBody(properties[property], isRequired);
256
+ const result = Utils.checkPostBody(properties[property], isRequired, isCopilot);
220
257
  paramResult.requiredNum += result.requiredNum;
221
258
  paramResult.optionalNum += result.optionalNum;
222
259
  paramResult.isValid = paramResult.isValid && result.isValid;
223
260
  }
224
261
  }
225
262
  else {
226
- if (isRequiredWithoutDefault) {
263
+ if (isRequiredWithoutDefault && !isCopilot) {
227
264
  paramResult.isValid = false;
228
265
  }
229
266
  }
230
267
  return paramResult;
231
268
  }
269
+ static containMultipleMediaTypes(bodyObject) {
270
+ return Object.keys((bodyObject === null || bodyObject === void 0 ? void 0 : bodyObject.content) || {}).length > 1;
271
+ }
232
272
  /**
233
273
  * Checks if the given API is supported.
234
274
  * @param {string} method - The HTTP method of the API.
@@ -243,32 +283,40 @@ class Utils {
243
283
  * 5. response body should be “application/json” and not empty, and response code should be 20X
244
284
  * 6. only support request body with “application/json” content type
245
285
  */
246
- static isSupportedApi(method, path, spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2) {
286
+ static isSupportedApi(method, path, spec, options) {
287
+ var _a;
247
288
  const pathObj = spec.paths[path];
248
289
  method = method.toLocaleLowerCase();
249
290
  if (pathObj) {
250
- if ((method === ConstantString.PostMethod || method === ConstantString.GetMethod) &&
251
- pathObj[method]) {
291
+ if (((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && pathObj[method]) {
252
292
  const securities = pathObj[method].security;
253
- const authArray = Utils.getAuthArray(securities, spec);
254
- if (!Utils.isSupportedAuth(authArray, allowAPIKeyAuth, allowOauth2)) {
255
- return false;
293
+ const isTeamsAi = options.projectType === ProjectType.TeamsAi;
294
+ const isCopilot = options.projectType === ProjectType.Copilot;
295
+ // Teams AI project doesn't care about auth, it will use authProvider for user to implement
296
+ if (!isTeamsAi) {
297
+ const authArray = Utils.getAuthArray(securities, spec);
298
+ if (!Utils.isSupportedAuth(authArray, options)) {
299
+ return false;
300
+ }
256
301
  }
257
302
  const operationObject = pathObj[method];
258
- if (!allowMissingId && !operationObject.operationId) {
303
+ if (!options.allowMissingId && !operationObject.operationId) {
259
304
  return false;
260
305
  }
261
306
  const paramObject = operationObject.parameters;
262
307
  const requestBody = operationObject.requestBody;
263
308
  const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
264
- const mediaTypesCount = Object.keys((requestBody === null || requestBody === void 0 ? void 0 : requestBody.content) || {}).length;
265
- if (mediaTypesCount > 1) {
309
+ if (!isTeamsAi && Utils.containMultipleMediaTypes(requestBody)) {
266
310
  return false;
267
311
  }
268
- const responseJson = Utils.getResponseJson(operationObject);
312
+ const responseJson = Utils.getResponseJson(operationObject, isTeamsAi);
269
313
  if (Object.keys(responseJson).length === 0) {
270
314
  return false;
271
315
  }
316
+ // Teams AI project doesn't care about request parameters/body
317
+ if (isTeamsAi) {
318
+ return true;
319
+ }
272
320
  let requestBodyParamResult = {
273
321
  requiredNum: 0,
274
322
  optionalNum: 0,
@@ -276,18 +324,26 @@ class Utils {
276
324
  };
277
325
  if (requestJsonBody) {
278
326
  const requestBodySchema = requestJsonBody.schema;
279
- requestBodyParamResult = Utils.checkPostBody(requestBodySchema, requestBody.required);
327
+ if (isCopilot && requestBodySchema.type !== "object") {
328
+ return false;
329
+ }
330
+ requestBodyParamResult = Utils.checkPostBody(requestBodySchema, requestBody.required, isCopilot);
280
331
  }
281
332
  if (!requestBodyParamResult.isValid) {
282
333
  return false;
283
334
  }
284
- const paramResult = Utils.checkParameters(paramObject);
335
+ const paramResult = Utils.checkParameters(paramObject, isCopilot);
285
336
  if (!paramResult.isValid) {
286
337
  return false;
287
338
  }
339
+ // Copilot support arbitrary parameters
340
+ if (isCopilot) {
341
+ return true;
342
+ }
288
343
  if (requestBodyParamResult.requiredNum + paramResult.requiredNum > 1) {
289
- if (allowMultipleParameters &&
290
- requestBodyParamResult.requiredNum + paramResult.requiredNum <= 5) {
344
+ if (options.allowMultipleParameters &&
345
+ requestBodyParamResult.requiredNum + paramResult.requiredNum <=
346
+ ConstantString.SMERequiredParamsMaxNum) {
291
347
  return true;
292
348
  }
293
349
  return false;
@@ -306,29 +362,31 @@ class Utils {
306
362
  }
307
363
  return false;
308
364
  }
309
- static isSupportedAuth(authSchemaArray, allowAPIKeyAuth, allowOauth2) {
365
+ static isSupportedAuth(authSchemaArray, options) {
310
366
  if (authSchemaArray.length === 0) {
311
367
  return true;
312
368
  }
313
- if (allowAPIKeyAuth || allowOauth2) {
369
+ if (options.allowAPIKeyAuth || options.allowOauth2) {
314
370
  // Currently we don't support multiple auth in one operation
315
371
  if (authSchemaArray.length > 0 && authSchemaArray.every((auths) => auths.length > 1)) {
316
372
  return false;
317
373
  }
318
374
  for (const auths of authSchemaArray) {
319
375
  if (auths.length === 1) {
320
- if (!allowOauth2 && allowAPIKeyAuth && Utils.isAPIKeyAuth(auths[0].authSchema)) {
376
+ if (!options.allowOauth2 &&
377
+ options.allowAPIKeyAuth &&
378
+ Utils.isAPIKeyAuth(auths[0].authSchema)) {
321
379
  return true;
322
380
  }
323
- else if (!allowAPIKeyAuth &&
324
- allowOauth2 &&
325
- Utils.isBearerTokenAuth(auths[0].authSchema)) {
381
+ else if (!options.allowAPIKeyAuth &&
382
+ options.allowOauth2 &&
383
+ Utils.isOAuthWithAuthCodeFlow(auths[0].authSchema)) {
326
384
  return true;
327
385
  }
328
- else if (allowAPIKeyAuth &&
329
- allowOauth2 &&
386
+ else if (options.allowAPIKeyAuth &&
387
+ options.allowOauth2 &&
330
388
  (Utils.isAPIKeyAuth(auths[0].authSchema) ||
331
- Utils.isBearerTokenAuth(auths[0].authSchema))) {
389
+ Utils.isOAuthWithAuthCodeFlow(auths[0].authSchema))) {
332
390
  return true;
333
391
  }
334
392
  }
@@ -339,10 +397,11 @@ class Utils {
339
397
  static isAPIKeyAuth(authSchema) {
340
398
  return authSchema.type === "apiKey";
341
399
  }
342
- static isBearerTokenAuth(authSchema) {
343
- return (authSchema.type === "oauth2" ||
344
- authSchema.type === "openIdConnect" ||
345
- (authSchema.type === "http" && authSchema.scheme === "bearer"));
400
+ static isOAuthWithAuthCodeFlow(authSchema) {
401
+ if (authSchema.type === "oauth2" && authSchema.flows && authSchema.flows.authorizationCode) {
402
+ return true;
403
+ }
404
+ return false;
346
405
  }
347
406
  static getAuthArray(securities, spec) {
348
407
  var _a;
@@ -370,18 +429,19 @@ class Utils {
370
429
  static updateFirstLetter(str) {
371
430
  return str.charAt(0).toUpperCase() + str.slice(1);
372
431
  }
373
- static getResponseJson(operationObject) {
432
+ static getResponseJson(operationObject, isTeamsAiProject = false) {
374
433
  var _a, _b;
375
434
  let json = {};
376
435
  for (const code of ConstantString.ResponseCodeFor20X) {
377
436
  const responseObject = (_a = operationObject === null || operationObject === void 0 ? void 0 : operationObject.responses) === null || _a === void 0 ? void 0 : _a[code];
378
- const mediaTypesCount = Object.keys((responseObject === null || responseObject === void 0 ? void 0 : responseObject.content) || {}).length;
379
- if (mediaTypesCount > 1) {
380
- return {};
381
- }
382
437
  if ((_b = responseObject === null || responseObject === void 0 ? void 0 : responseObject.content) === null || _b === void 0 ? void 0 : _b["application/json"]) {
383
438
  json = responseObject.content["application/json"];
384
- break;
439
+ if (!isTeamsAiProject && Utils.containMultipleMediaTypes(responseObject)) {
440
+ json = {};
441
+ }
442
+ else {
443
+ break;
444
+ }
385
445
  }
386
446
  }
387
447
  return json;
@@ -455,7 +515,7 @@ class Utils {
455
515
  }
456
516
  return errors;
457
517
  }
458
- static validateServer(spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2) {
518
+ static validateServer(spec, options) {
459
519
  const errors = [];
460
520
  let hasTopLevelServers = false;
461
521
  let hasPathLevelServers = false;
@@ -476,7 +536,7 @@ class Utils {
476
536
  }
477
537
  for (const method in methods) {
478
538
  const operationObject = methods[method];
479
- if (Utils.isSupportedApi(method, path, spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2)) {
539
+ if (Utils.isSupportedApi(method, path, spec, options)) {
480
540
  if ((operationObject === null || operationObject === void 0 ? void 0 : operationObject.servers) && operationObject.servers.length >= 1) {
481
541
  hasOperationLevelServers = true;
482
542
  const serverErrors = Utils.checkServerUrl(operationObject.servers);
@@ -563,7 +623,7 @@ class Utils {
563
623
  param.value = schema.default;
564
624
  }
565
625
  }
566
- static parseApiInfo(operationItem, allowMultipleParameters) {
626
+ static parseApiInfo(operationItem, options) {
567
627
  var _a, _b;
568
628
  const requiredParams = [];
569
629
  const optionalParams = [];
@@ -577,7 +637,7 @@ class Utils {
577
637
  description: ((_a = param.description) !== null && _a !== void 0 ? _a : "").slice(0, ConstantString.ParameterDescriptionMaxLens),
578
638
  };
579
639
  const schema = param.schema;
580
- if (allowMultipleParameters && schema) {
640
+ if (options.allowMultipleParameters && schema) {
581
641
  Utils.updateParameterWithInputType(schema, parameter);
582
642
  }
583
643
  if (param.in !== "header" && param.in !== "cookie") {
@@ -595,7 +655,7 @@ class Utils {
595
655
  const requestJson = requestBody.content["application/json"];
596
656
  if (Object.keys(requestJson).length !== 0) {
597
657
  const schema = requestJson.schema;
598
- const [requiredP, optionalP] = Utils.generateParametersFromSchema(schema, "requestBody", allowMultipleParameters, requestBody.required);
658
+ const [requiredP, optionalP] = Utils.generateParametersFromSchema(schema, "requestBody", !!options.allowMultipleParameters, requestBody.required);
599
659
  requiredParams.push(...requiredP);
600
660
  optionalParams.push(...optionalP);
601
661
  }
@@ -626,14 +686,13 @@ class Utils {
626
686
  }
627
687
  return [command, warning];
628
688
  }
629
- static listSupportedAPIs(spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2) {
689
+ static listSupportedAPIs(spec, options) {
630
690
  const paths = spec.paths;
631
691
  const result = {};
632
692
  for (const path in paths) {
633
693
  const methods = paths[path];
634
694
  for (const method in methods) {
635
- // For developer preview, only support GET operation with only 1 parameter without auth
636
- if (Utils.isSupportedApi(method, path, spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2)) {
695
+ if (Utils.isSupportedApi(method, path, spec, options)) {
637
696
  const operationObject = methods[method];
638
697
  result[`${method.toUpperCase()} ${path}`] = operationObject;
639
698
  }
@@ -641,7 +700,7 @@ class Utils {
641
700
  }
642
701
  return result;
643
702
  }
644
- static validateSpec(spec, parser, isSwaggerFile, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2) {
703
+ static validateSpec(spec, parser, isSwaggerFile, options) {
645
704
  const errors = [];
646
705
  const warnings = [];
647
706
  if (isSwaggerFile) {
@@ -651,7 +710,7 @@ class Utils {
651
710
  });
652
711
  }
653
712
  // Server validation
654
- const serverErrors = Utils.validateServer(spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2);
713
+ const serverErrors = Utils.validateServer(spec, options);
655
714
  errors.push(...serverErrors);
656
715
  // Remote reference not supported
657
716
  const refPaths = parser.$refs.paths();
@@ -664,7 +723,7 @@ class Utils {
664
723
  });
665
724
  }
666
725
  // No supported API
667
- const apiMap = Utils.listSupportedAPIs(spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2);
726
+ const apiMap = Utils.listSupportedAPIs(spec, options);
668
727
  if (Object.keys(apiMap).length === 0) {
669
728
  errors.push({
670
729
  type: ErrorType.NoSupportedApi,
@@ -735,6 +794,8 @@ class SpecParser {
735
794
  allowAPIKeyAuth: false,
736
795
  allowMultipleParameters: false,
737
796
  allowOauth2: false,
797
+ allowMethods: ["get", "post"],
798
+ projectType: ProjectType.SME,
738
799
  };
739
800
  this.pathOrSpec = pathOrDoc;
740
801
  this.parser = new SwaggerParser();
@@ -771,7 +832,7 @@ class SpecParser {
771
832
  ],
772
833
  };
773
834
  }
774
- return Utils.validateSpec(this.spec, this.parser, !!this.isSwaggerFile, this.options.allowMissingId, this.options.allowAPIKeyAuth, this.options.allowMultipleParameters, this.options.allowOauth2);
835
+ return Utils.validateSpec(this.spec, this.parser, !!this.isSwaggerFile, this.options);
775
836
  }
776
837
  catch (err) {
777
838
  throw new SpecParserError(err.toString(), ErrorType.ValidateFailed);
@@ -790,7 +851,7 @@ class SpecParser {
790
851
  if (!operationId) {
791
852
  continue;
792
853
  }
793
- const [command, warning] = Utils.parseApiInfo(pathObjectItem, this.options.allowMultipleParameters);
854
+ const [command, warning] = Utils.parseApiInfo(pathObjectItem, this.options);
794
855
  const apiInfo = {
795
856
  method: method,
796
857
  path: path,
@@ -827,6 +888,17 @@ class SpecParser {
827
888
  async getFilteredSpecs(filter, signal) {
828
889
  throw new Error("Method not implemented.");
829
890
  }
891
+ /**
892
+ * Generates and update artifacts from the OpenAPI specification file. Generate Adaptive Cards, update Teams app manifest, and generate a new OpenAPI specification file.
893
+ * @param manifestPath A file path of the Teams app manifest file to update.
894
+ * @param filter An array of strings that represent the filters to apply when generating the artifacts. If filter is empty, it would process nothing.
895
+ * @param outputSpecPath File path of the new OpenAPI specification file to generate. If not specified or empty, no spec file will be generated.
896
+ * @param pluginFilePath File path of the api plugin file to generate.
897
+ */
898
+ // eslint-disable-next-line @typescript-eslint/require-await
899
+ async generateForCopilot(manifestPath, filter, outputSpecPath, pluginFilePath, signal) {
900
+ throw new Error("Method not implemented.");
901
+ }
830
902
  /**
831
903
  * Generates and update artifacts from the OpenAPI specification file. Generate Adaptive Cards, update Teams app manifest, and generate a new OpenAPI specification file.
832
904
  * @param manifestPath A file path of the Teams app manifest file to update.
@@ -836,7 +908,7 @@ class SpecParser {
836
908
  * @param isMe Boolean that indicates whether the project is an Messaging Extension. For Messaging Extension, composeExtensions will be added in Teams app manifest.
837
909
  */
838
910
  // eslint-disable-next-line @typescript-eslint/require-await
839
- async generate(manifestPath, filter, outputSpecPath, adaptiveCardFolder, signal, isMe) {
911
+ async generate(manifestPath, filter, outputSpecPath, adaptiveCardFolder, signal) {
840
912
  throw new Error("Method not implemented.");
841
913
  }
842
914
  async loadSpec() {
@@ -853,7 +925,7 @@ class SpecParser {
853
925
  if (this.apiMap !== undefined) {
854
926
  return this.apiMap;
855
927
  }
856
- const result = Utils.listSupportedAPIs(spec, this.options.allowMissingId, this.options.allowAPIKeyAuth, this.options.allowMultipleParameters, this.options.allowOauth2);
928
+ const result = Utils.listSupportedAPIs(spec, this.options);
857
929
  this.apiMap = result;
858
930
  return result;
859
931
  }