@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.
@@ -45,7 +45,7 @@ var ErrorType;
45
45
  ErrorType["NoExtraAPICanBeAdded"] = "no-extra-api-can-be-added";
46
46
  ErrorType["ResolveServerUrlFailed"] = "resolve-server-url-failed";
47
47
  ErrorType["SwaggerNotSupported"] = "swagger-not-supported";
48
- ErrorType["MultipleAPIKeyNotSupported"] = "multiple-api-key-not-supported";
48
+ ErrorType["MultipleAuthNotSupported"] = "multiple-auth-not-supported";
49
49
  ErrorType["ListFailed"] = "list-failed";
50
50
  ErrorType["listSupportedAPIInfoFailed"] = "list-supported-api-info-failed";
51
51
  ErrorType["FilterSpecFailed"] = "filter-spec-failed";
@@ -76,7 +76,13 @@ var ValidationStatus;
76
76
  ValidationStatus[ValidationStatus["Valid"] = 0] = "Valid";
77
77
  ValidationStatus[ValidationStatus["Warning"] = 1] = "Warning";
78
78
  ValidationStatus[ValidationStatus["Error"] = 2] = "Error";
79
- })(ValidationStatus || (ValidationStatus = {}));
79
+ })(ValidationStatus || (ValidationStatus = {}));
80
+ var ProjectType;
81
+ (function (ProjectType) {
82
+ ProjectType[ProjectType["Copilot"] = 0] = "Copilot";
83
+ ProjectType[ProjectType["SME"] = 1] = "SME";
84
+ ProjectType[ProjectType["TeamsAi"] = 2] = "TeamsAi";
85
+ })(ProjectType || (ProjectType = {}));
80
86
 
81
87
  // Copyright (c) Microsoft Corporation.
82
88
  class SpecParserError extends Error {
@@ -103,7 +109,8 @@ ConstantString.ResolveServerUrlFailed = "Unable to resolve the server URL: pleas
103
109
  ConstantString.OperationOnlyContainsOptionalParam = "Operation %s contains multiple optional parameters. The first optional parameter is used for this command.";
104
110
  ConstantString.ConvertSwaggerToOpenAPI = "The Swagger 2.0 file has been converted to OpenAPI 3.0.";
105
111
  ConstantString.SwaggerNotSupported = "Swagger 2.0 is not supported. Please convert to OpenAPI 3.0 manually before proceeding.";
106
- ConstantString.MultipleAPIKeyNotSupported = "Multiple API keys are not supported. Please make sure that all selected APIs use the same API key.";
112
+ ConstantString.MultipleAuthNotSupported = "Multiple authentication methods are unsupported. Ensure all selected APIs use identical authentication.";
113
+ ConstantString.UnsupportedSchema = "Unsupported schema in %s %s: %s";
107
114
  ConstantString.WrappedCardVersion = "devPreview";
108
115
  ConstantString.WrappedCardSchema = "https://developer.microsoft.com/json-schemas/teams/vDevPreview/MicrosoftTeams.ResponseRenderingTemplate.schema.json";
109
116
  ConstantString.WrappedCardResponseLayout = "list";
@@ -115,6 +122,7 @@ ConstantString.AdaptiveCardType = "AdaptiveCard";
115
122
  ConstantString.TextBlockType = "TextBlock";
116
123
  ConstantString.ContainerType = "Container";
117
124
  ConstantString.RegistrationIdPostfix = "REGISTRATION_ID";
125
+ ConstantString.OAuthRegistrationIdPostFix = "OAUTH_REGISTRATION_ID";
118
126
  ConstantString.ResponseCodeFor20X = [
119
127
  "200",
120
128
  "201",
@@ -174,11 +182,23 @@ ConstantString.FullDescriptionMaxLens = 4000;
174
182
  ConstantString.CommandDescriptionMaxLens = 128;
175
183
  ConstantString.ParameterDescriptionMaxLens = 128;
176
184
  ConstantString.CommandTitleMaxLens = 32;
177
- ConstantString.ParameterTitleMaxLens = 32;
185
+ ConstantString.ParameterTitleMaxLens = 32;
186
+ ConstantString.SMERequiredParamsMaxNum = 5;
178
187
 
179
188
  // Copyright (c) Microsoft Corporation.
180
189
  class Utils {
181
- static checkParameters(paramObject) {
190
+ static hasNestedObjectInSchema(schema) {
191
+ if (schema.type === "object") {
192
+ for (const property in schema.properties) {
193
+ const nestedSchema = schema.properties[property];
194
+ if (nestedSchema.type === "object") {
195
+ return true;
196
+ }
197
+ }
198
+ }
199
+ return false;
200
+ }
201
+ static checkParameters(paramObject, isCopilot) {
182
202
  const paramResult = {
183
203
  requiredNum: 0,
184
204
  optionalNum: 0,
@@ -190,7 +210,20 @@ class Utils {
190
210
  for (let i = 0; i < paramObject.length; i++) {
191
211
  const param = paramObject[i];
192
212
  const schema = param.schema;
213
+ if (isCopilot && this.hasNestedObjectInSchema(schema)) {
214
+ paramResult.isValid = false;
215
+ continue;
216
+ }
193
217
  const isRequiredWithoutDefault = param.required && schema.default === undefined;
218
+ if (isCopilot) {
219
+ if (isRequiredWithoutDefault) {
220
+ paramResult.requiredNum = paramResult.requiredNum + 1;
221
+ }
222
+ else {
223
+ paramResult.optionalNum = paramResult.optionalNum + 1;
224
+ }
225
+ continue;
226
+ }
194
227
  if (param.in === "header" || param.in === "cookie") {
195
228
  if (isRequiredWithoutDefault) {
196
229
  paramResult.isValid = false;
@@ -217,7 +250,7 @@ class Utils {
217
250
  }
218
251
  return paramResult;
219
252
  }
220
- static checkPostBody(schema, isRequired = false) {
253
+ static checkPostBody(schema, isRequired = false, isCopilot = false) {
221
254
  var _a;
222
255
  const paramResult = {
223
256
  requiredNum: 0,
@@ -228,6 +261,10 @@ class Utils {
228
261
  return paramResult;
229
262
  }
230
263
  const isRequiredWithoutDefault = isRequired && schema.default === undefined;
264
+ if (isCopilot && this.hasNestedObjectInSchema(schema)) {
265
+ paramResult.isValid = false;
266
+ return paramResult;
267
+ }
231
268
  if (schema.type === "string" ||
232
269
  schema.type === "integer" ||
233
270
  schema.type === "boolean" ||
@@ -246,19 +283,22 @@ class Utils {
246
283
  if (schema.required && ((_a = schema.required) === null || _a === void 0 ? void 0 : _a.indexOf(property)) >= 0) {
247
284
  isRequired = true;
248
285
  }
249
- const result = Utils.checkPostBody(properties[property], isRequired);
286
+ const result = Utils.checkPostBody(properties[property], isRequired, isCopilot);
250
287
  paramResult.requiredNum += result.requiredNum;
251
288
  paramResult.optionalNum += result.optionalNum;
252
289
  paramResult.isValid = paramResult.isValid && result.isValid;
253
290
  }
254
291
  }
255
292
  else {
256
- if (isRequiredWithoutDefault) {
293
+ if (isRequiredWithoutDefault && !isCopilot) {
257
294
  paramResult.isValid = false;
258
295
  }
259
296
  }
260
297
  return paramResult;
261
298
  }
299
+ static containMultipleMediaTypes(bodyObject) {
300
+ return Object.keys((bodyObject === null || bodyObject === void 0 ? void 0 : bodyObject.content) || {}).length > 1;
301
+ }
262
302
  /**
263
303
  * Checks if the given API is supported.
264
304
  * @param {string} method - The HTTP method of the API.
@@ -273,32 +313,40 @@ class Utils {
273
313
  * 5. response body should be “application/json” and not empty, and response code should be 20X
274
314
  * 6. only support request body with “application/json” content type
275
315
  */
276
- static isSupportedApi(method, path, spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2) {
316
+ static isSupportedApi(method, path, spec, options) {
317
+ var _a;
277
318
  const pathObj = spec.paths[path];
278
319
  method = method.toLocaleLowerCase();
279
320
  if (pathObj) {
280
- if ((method === ConstantString.PostMethod || method === ConstantString.GetMethod) &&
281
- pathObj[method]) {
321
+ if (((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && pathObj[method]) {
282
322
  const securities = pathObj[method].security;
283
- const authArray = Utils.getAuthArray(securities, spec);
284
- if (!Utils.isSupportedAuth(authArray, allowAPIKeyAuth, allowOauth2)) {
285
- return false;
323
+ const isTeamsAi = options.projectType === ProjectType.TeamsAi;
324
+ const isCopilot = options.projectType === ProjectType.Copilot;
325
+ // Teams AI project doesn't care about auth, it will use authProvider for user to implement
326
+ if (!isTeamsAi) {
327
+ const authArray = Utils.getAuthArray(securities, spec);
328
+ if (!Utils.isSupportedAuth(authArray, options)) {
329
+ return false;
330
+ }
286
331
  }
287
332
  const operationObject = pathObj[method];
288
- if (!allowMissingId && !operationObject.operationId) {
333
+ if (!options.allowMissingId && !operationObject.operationId) {
289
334
  return false;
290
335
  }
291
336
  const paramObject = operationObject.parameters;
292
337
  const requestBody = operationObject.requestBody;
293
338
  const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
294
- const mediaTypesCount = Object.keys((requestBody === null || requestBody === void 0 ? void 0 : requestBody.content) || {}).length;
295
- if (mediaTypesCount > 1) {
339
+ if (!isTeamsAi && Utils.containMultipleMediaTypes(requestBody)) {
296
340
  return false;
297
341
  }
298
- const responseJson = Utils.getResponseJson(operationObject);
342
+ const responseJson = Utils.getResponseJson(operationObject, isTeamsAi);
299
343
  if (Object.keys(responseJson).length === 0) {
300
344
  return false;
301
345
  }
346
+ // Teams AI project doesn't care about request parameters/body
347
+ if (isTeamsAi) {
348
+ return true;
349
+ }
302
350
  let requestBodyParamResult = {
303
351
  requiredNum: 0,
304
352
  optionalNum: 0,
@@ -306,18 +354,26 @@ class Utils {
306
354
  };
307
355
  if (requestJsonBody) {
308
356
  const requestBodySchema = requestJsonBody.schema;
309
- requestBodyParamResult = Utils.checkPostBody(requestBodySchema, requestBody.required);
357
+ if (isCopilot && requestBodySchema.type !== "object") {
358
+ return false;
359
+ }
360
+ requestBodyParamResult = Utils.checkPostBody(requestBodySchema, requestBody.required, isCopilot);
310
361
  }
311
362
  if (!requestBodyParamResult.isValid) {
312
363
  return false;
313
364
  }
314
- const paramResult = Utils.checkParameters(paramObject);
365
+ const paramResult = Utils.checkParameters(paramObject, isCopilot);
315
366
  if (!paramResult.isValid) {
316
367
  return false;
317
368
  }
369
+ // Copilot support arbitrary parameters
370
+ if (isCopilot) {
371
+ return true;
372
+ }
318
373
  if (requestBodyParamResult.requiredNum + paramResult.requiredNum > 1) {
319
- if (allowMultipleParameters &&
320
- requestBodyParamResult.requiredNum + paramResult.requiredNum <= 5) {
374
+ if (options.allowMultipleParameters &&
375
+ requestBodyParamResult.requiredNum + paramResult.requiredNum <=
376
+ ConstantString.SMERequiredParamsMaxNum) {
321
377
  return true;
322
378
  }
323
379
  return false;
@@ -336,29 +392,31 @@ class Utils {
336
392
  }
337
393
  return false;
338
394
  }
339
- static isSupportedAuth(authSchemaArray, allowAPIKeyAuth, allowOauth2) {
395
+ static isSupportedAuth(authSchemaArray, options) {
340
396
  if (authSchemaArray.length === 0) {
341
397
  return true;
342
398
  }
343
- if (allowAPIKeyAuth || allowOauth2) {
399
+ if (options.allowAPIKeyAuth || options.allowOauth2) {
344
400
  // Currently we don't support multiple auth in one operation
345
401
  if (authSchemaArray.length > 0 && authSchemaArray.every((auths) => auths.length > 1)) {
346
402
  return false;
347
403
  }
348
404
  for (const auths of authSchemaArray) {
349
405
  if (auths.length === 1) {
350
- if (!allowOauth2 && allowAPIKeyAuth && Utils.isAPIKeyAuth(auths[0].authSchema)) {
406
+ if (!options.allowOauth2 &&
407
+ options.allowAPIKeyAuth &&
408
+ Utils.isAPIKeyAuth(auths[0].authSchema)) {
351
409
  return true;
352
410
  }
353
- else if (!allowAPIKeyAuth &&
354
- allowOauth2 &&
355
- Utils.isBearerTokenAuth(auths[0].authSchema)) {
411
+ else if (!options.allowAPIKeyAuth &&
412
+ options.allowOauth2 &&
413
+ Utils.isOAuthWithAuthCodeFlow(auths[0].authSchema)) {
356
414
  return true;
357
415
  }
358
- else if (allowAPIKeyAuth &&
359
- allowOauth2 &&
416
+ else if (options.allowAPIKeyAuth &&
417
+ options.allowOauth2 &&
360
418
  (Utils.isAPIKeyAuth(auths[0].authSchema) ||
361
- Utils.isBearerTokenAuth(auths[0].authSchema))) {
419
+ Utils.isOAuthWithAuthCodeFlow(auths[0].authSchema))) {
362
420
  return true;
363
421
  }
364
422
  }
@@ -369,10 +427,11 @@ class Utils {
369
427
  static isAPIKeyAuth(authSchema) {
370
428
  return authSchema.type === "apiKey";
371
429
  }
372
- static isBearerTokenAuth(authSchema) {
373
- return (authSchema.type === "oauth2" ||
374
- authSchema.type === "openIdConnect" ||
375
- (authSchema.type === "http" && authSchema.scheme === "bearer"));
430
+ static isOAuthWithAuthCodeFlow(authSchema) {
431
+ if (authSchema.type === "oauth2" && authSchema.flows && authSchema.flows.authorizationCode) {
432
+ return true;
433
+ }
434
+ return false;
376
435
  }
377
436
  static getAuthArray(securities, spec) {
378
437
  var _a;
@@ -400,18 +459,19 @@ class Utils {
400
459
  static updateFirstLetter(str) {
401
460
  return str.charAt(0).toUpperCase() + str.slice(1);
402
461
  }
403
- static getResponseJson(operationObject) {
462
+ static getResponseJson(operationObject, isTeamsAiProject = false) {
404
463
  var _a, _b;
405
464
  let json = {};
406
465
  for (const code of ConstantString.ResponseCodeFor20X) {
407
466
  const responseObject = (_a = operationObject === null || operationObject === void 0 ? void 0 : operationObject.responses) === null || _a === void 0 ? void 0 : _a[code];
408
- const mediaTypesCount = Object.keys((responseObject === null || responseObject === void 0 ? void 0 : responseObject.content) || {}).length;
409
- if (mediaTypesCount > 1) {
410
- return {};
411
- }
412
467
  if ((_b = responseObject === null || responseObject === void 0 ? void 0 : responseObject.content) === null || _b === void 0 ? void 0 : _b["application/json"]) {
413
468
  json = responseObject.content["application/json"];
414
- break;
469
+ if (!isTeamsAiProject && Utils.containMultipleMediaTypes(responseObject)) {
470
+ json = {};
471
+ }
472
+ else {
473
+ break;
474
+ }
415
475
  }
416
476
  }
417
477
  return json;
@@ -485,7 +545,7 @@ class Utils {
485
545
  }
486
546
  return errors;
487
547
  }
488
- static validateServer(spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2) {
548
+ static validateServer(spec, options) {
489
549
  const errors = [];
490
550
  let hasTopLevelServers = false;
491
551
  let hasPathLevelServers = false;
@@ -506,7 +566,7 @@ class Utils {
506
566
  }
507
567
  for (const method in methods) {
508
568
  const operationObject = methods[method];
509
- if (Utils.isSupportedApi(method, path, spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2)) {
569
+ if (Utils.isSupportedApi(method, path, spec, options)) {
510
570
  if ((operationObject === null || operationObject === void 0 ? void 0 : operationObject.servers) && operationObject.servers.length >= 1) {
511
571
  hasOperationLevelServers = true;
512
572
  const serverErrors = Utils.checkServerUrl(operationObject.servers);
@@ -593,7 +653,7 @@ class Utils {
593
653
  param.value = schema.default;
594
654
  }
595
655
  }
596
- static parseApiInfo(operationItem, allowMultipleParameters) {
656
+ static parseApiInfo(operationItem, options) {
597
657
  var _a, _b;
598
658
  const requiredParams = [];
599
659
  const optionalParams = [];
@@ -607,7 +667,7 @@ class Utils {
607
667
  description: ((_a = param.description) !== null && _a !== void 0 ? _a : "").slice(0, ConstantString.ParameterDescriptionMaxLens),
608
668
  };
609
669
  const schema = param.schema;
610
- if (allowMultipleParameters && schema) {
670
+ if (options.allowMultipleParameters && schema) {
611
671
  Utils.updateParameterWithInputType(schema, parameter);
612
672
  }
613
673
  if (param.in !== "header" && param.in !== "cookie") {
@@ -625,7 +685,7 @@ class Utils {
625
685
  const requestJson = requestBody.content["application/json"];
626
686
  if (Object.keys(requestJson).length !== 0) {
627
687
  const schema = requestJson.schema;
628
- const [requiredP, optionalP] = Utils.generateParametersFromSchema(schema, "requestBody", allowMultipleParameters, requestBody.required);
688
+ const [requiredP, optionalP] = Utils.generateParametersFromSchema(schema, "requestBody", !!options.allowMultipleParameters, requestBody.required);
629
689
  requiredParams.push(...requiredP);
630
690
  optionalParams.push(...optionalP);
631
691
  }
@@ -656,14 +716,13 @@ class Utils {
656
716
  }
657
717
  return [command, warning];
658
718
  }
659
- static listSupportedAPIs(spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2) {
719
+ static listSupportedAPIs(spec, options) {
660
720
  const paths = spec.paths;
661
721
  const result = {};
662
722
  for (const path in paths) {
663
723
  const methods = paths[path];
664
724
  for (const method in methods) {
665
- // For developer preview, only support GET operation with only 1 parameter without auth
666
- if (Utils.isSupportedApi(method, path, spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2)) {
725
+ if (Utils.isSupportedApi(method, path, spec, options)) {
667
726
  const operationObject = methods[method];
668
727
  result[`${method.toUpperCase()} ${path}`] = operationObject;
669
728
  }
@@ -671,7 +730,7 @@ class Utils {
671
730
  }
672
731
  return result;
673
732
  }
674
- static validateSpec(spec, parser, isSwaggerFile, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2) {
733
+ static validateSpec(spec, parser, isSwaggerFile, options) {
675
734
  const errors = [];
676
735
  const warnings = [];
677
736
  if (isSwaggerFile) {
@@ -681,7 +740,7 @@ class Utils {
681
740
  });
682
741
  }
683
742
  // Server validation
684
- const serverErrors = Utils.validateServer(spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2);
743
+ const serverErrors = Utils.validateServer(spec, options);
685
744
  errors.push(...serverErrors);
686
745
  // Remote reference not supported
687
746
  const refPaths = parser.$refs.paths();
@@ -694,7 +753,7 @@ class Utils {
694
753
  });
695
754
  }
696
755
  // No supported API
697
- const apiMap = Utils.listSupportedAPIs(spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2);
756
+ const apiMap = Utils.listSupportedAPIs(spec, options);
698
757
  if (Object.keys(apiMap).length === 0) {
699
758
  errors.push({
700
759
  type: ErrorType.NoSupportedApi,
@@ -765,6 +824,8 @@ class SpecParser {
765
824
  allowAPIKeyAuth: false,
766
825
  allowMultipleParameters: false,
767
826
  allowOauth2: false,
827
+ allowMethods: ["get", "post"],
828
+ projectType: ProjectType.SME,
768
829
  };
769
830
  this.pathOrSpec = pathOrDoc;
770
831
  this.parser = new SwaggerParser();
@@ -802,7 +863,7 @@ class SpecParser {
802
863
  ],
803
864
  };
804
865
  }
805
- return Utils.validateSpec(this.spec, this.parser, !!this.isSwaggerFile, this.options.allowMissingId, this.options.allowAPIKeyAuth, this.options.allowMultipleParameters, this.options.allowOauth2);
866
+ return Utils.validateSpec(this.spec, this.parser, !!this.isSwaggerFile, this.options);
806
867
  }
807
868
  catch (err) {
808
869
  throw new SpecParserError(err.toString(), ErrorType.ValidateFailed);
@@ -823,7 +884,7 @@ class SpecParser {
823
884
  if (!operationId) {
824
885
  continue;
825
886
  }
826
- const [command, warning] = Utils.parseApiInfo(pathObjectItem, this.options.allowMultipleParameters);
887
+ const [command, warning] = Utils.parseApiInfo(pathObjectItem, this.options);
827
888
  const apiInfo = {
828
889
  method: method,
829
890
  path: path,
@@ -865,6 +926,19 @@ class SpecParser {
865
926
  throw new Error("Method not implemented.");
866
927
  });
867
928
  }
929
+ /**
930
+ * Generates and update artifacts from the OpenAPI specification file. Generate Adaptive Cards, update Teams app manifest, and generate a new OpenAPI specification file.
931
+ * @param manifestPath A file path of the Teams app manifest file to update.
932
+ * @param filter An array of strings that represent the filters to apply when generating the artifacts. If filter is empty, it would process nothing.
933
+ * @param outputSpecPath File path of the new OpenAPI specification file to generate. If not specified or empty, no spec file will be generated.
934
+ * @param pluginFilePath File path of the api plugin file to generate.
935
+ */
936
+ // eslint-disable-next-line @typescript-eslint/require-await
937
+ generateForCopilot(manifestPath, filter, outputSpecPath, pluginFilePath, signal) {
938
+ return __awaiter(this, void 0, void 0, function* () {
939
+ throw new Error("Method not implemented.");
940
+ });
941
+ }
868
942
  /**
869
943
  * Generates and update artifacts from the OpenAPI specification file. Generate Adaptive Cards, update Teams app manifest, and generate a new OpenAPI specification file.
870
944
  * @param manifestPath A file path of the Teams app manifest file to update.
@@ -874,7 +948,7 @@ class SpecParser {
874
948
  * @param isMe Boolean that indicates whether the project is an Messaging Extension. For Messaging Extension, composeExtensions will be added in Teams app manifest.
875
949
  */
876
950
  // eslint-disable-next-line @typescript-eslint/require-await
877
- generate(manifestPath, filter, outputSpecPath, adaptiveCardFolder, signal, isMe) {
951
+ generate(manifestPath, filter, outputSpecPath, adaptiveCardFolder, signal) {
878
952
  return __awaiter(this, void 0, void 0, function* () {
879
953
  throw new Error("Method not implemented.");
880
954
  });
@@ -895,7 +969,7 @@ class SpecParser {
895
969
  if (this.apiMap !== undefined) {
896
970
  return this.apiMap;
897
971
  }
898
- const result = Utils.listSupportedAPIs(spec, this.options.allowMissingId, this.options.allowAPIKeyAuth, this.options.allowMultipleParameters, this.options.allowOauth2);
972
+ const result = Utils.listSupportedAPIs(spec, this.options);
899
973
  this.apiMap = result;
900
974
  return result;
901
975
  }