@microsoft/m365-spec-parser 0.1.1-alpha.78701ec6a.0 → 0.1.1-alpha.87f45d762.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,8 @@ 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
+ ErrorType["SpecVersionNotSupported"] = "spec-version-not-supported";
49
50
  ErrorType["ListFailed"] = "list-failed";
50
51
  ErrorType["listSupportedAPIInfoFailed"] = "list-supported-api-info-failed";
51
52
  ErrorType["FilterSpecFailed"] = "filter-spec-failed";
@@ -53,6 +54,22 @@ var ErrorType;
53
54
  ErrorType["GenerateAdaptiveCardFailed"] = "generate-adaptive-card-failed";
54
55
  ErrorType["GenerateFailed"] = "generate-failed";
55
56
  ErrorType["ValidateFailed"] = "validate-failed";
57
+ ErrorType["GetSpecFailed"] = "get-spec-failed";
58
+ ErrorType["AuthTypeIsNotSupported"] = "auth-type-is-not-supported";
59
+ ErrorType["MissingOperationId"] = "missing-operation-id";
60
+ ErrorType["PostBodyContainMultipleMediaTypes"] = "post-body-contain-multiple-media-types";
61
+ ErrorType["ResponseContainMultipleMediaTypes"] = "response-contain-multiple-media-types";
62
+ ErrorType["ResponseJsonIsEmpty"] = "response-json-is-empty";
63
+ ErrorType["PostBodySchemaIsNotJson"] = "post-body-schema-is-not-json";
64
+ ErrorType["PostBodyContainsRequiredUnsupportedSchema"] = "post-body-contains-required-unsupported-schema";
65
+ ErrorType["ParamsContainRequiredUnsupportedSchema"] = "params-contain-required-unsupported-schema";
66
+ ErrorType["ParamsContainsNestedObject"] = "params-contains-nested-object";
67
+ ErrorType["RequestBodyContainsNestedObject"] = "request-body-contains-nested-object";
68
+ ErrorType["ExceededRequiredParamsLimit"] = "exceeded-required-params-limit";
69
+ ErrorType["NoParameter"] = "no-parameter";
70
+ ErrorType["NoAPIInfo"] = "no-api-info";
71
+ ErrorType["MethodNotAllowed"] = "method-not-allowed";
72
+ ErrorType["UrlPathNotExist"] = "url-path-not-exist";
56
73
  ErrorType["Cancelled"] = "cancelled";
57
74
  ErrorType["Unknown"] = "unknown";
58
75
  })(ErrorType || (ErrorType = {}));
@@ -75,7 +92,13 @@ var ValidationStatus;
75
92
  ValidationStatus[ValidationStatus["Valid"] = 0] = "Valid";
76
93
  ValidationStatus[ValidationStatus["Warning"] = 1] = "Warning";
77
94
  ValidationStatus[ValidationStatus["Error"] = 2] = "Error";
78
- })(ValidationStatus || (ValidationStatus = {}));
95
+ })(ValidationStatus || (ValidationStatus = {}));
96
+ var ProjectType;
97
+ (function (ProjectType) {
98
+ ProjectType[ProjectType["Copilot"] = 0] = "Copilot";
99
+ ProjectType[ProjectType["SME"] = 1] = "SME";
100
+ ProjectType[ProjectType["TeamsAi"] = 2] = "TeamsAi";
101
+ })(ProjectType || (ProjectType = {}));
79
102
 
80
103
  // Copyright (c) Microsoft Corporation.
81
104
  class SpecParserError extends Error {
@@ -102,7 +125,9 @@ ConstantString.ResolveServerUrlFailed = "Unable to resolve the server URL: pleas
102
125
  ConstantString.OperationOnlyContainsOptionalParam = "Operation %s contains multiple optional parameters. The first optional parameter is used for this command.";
103
126
  ConstantString.ConvertSwaggerToOpenAPI = "The Swagger 2.0 file has been converted to OpenAPI 3.0.";
104
127
  ConstantString.SwaggerNotSupported = "Swagger 2.0 is not supported. Please convert to OpenAPI 3.0 manually before proceeding.";
105
- ConstantString.MultipleAPIKeyNotSupported = "Multiple API keys are not supported. Please make sure that all selected APIs use the same API key.";
128
+ ConstantString.SpecVersionNotSupported = "Unsupported OpenAPI version %s. Please use version 3.0.x.";
129
+ ConstantString.MultipleAuthNotSupported = "Multiple authentication methods are unsupported. Ensure all selected APIs use identical authentication.";
130
+ ConstantString.UnsupportedSchema = "Unsupported schema in %s %s: %s";
106
131
  ConstantString.WrappedCardVersion = "devPreview";
107
132
  ConstantString.WrappedCardSchema = "https://developer.microsoft.com/json-schemas/teams/vDevPreview/MicrosoftTeams.ResponseRenderingTemplate.schema.json";
108
133
  ConstantString.WrappedCardResponseLayout = "list";
@@ -114,6 +139,7 @@ ConstantString.AdaptiveCardType = "AdaptiveCard";
114
139
  ConstantString.TextBlockType = "TextBlock";
115
140
  ConstantString.ContainerType = "Container";
116
141
  ConstantString.RegistrationIdPostfix = "REGISTRATION_ID";
142
+ ConstantString.OAuthRegistrationIdPostFix = "OAUTH_REGISTRATION_ID";
117
143
  ConstantString.ResponseCodeFor20X = [
118
144
  "200",
119
145
  "201",
@@ -173,205 +199,36 @@ ConstantString.FullDescriptionMaxLens = 4000;
173
199
  ConstantString.CommandDescriptionMaxLens = 128;
174
200
  ConstantString.ParameterDescriptionMaxLens = 128;
175
201
  ConstantString.CommandTitleMaxLens = 32;
176
- ConstantString.ParameterTitleMaxLens = 32;
202
+ ConstantString.ParameterTitleMaxLens = 32;
203
+ ConstantString.SMERequiredParamsMaxNum = 5;
204
+ ConstantString.DefaultPluginId = "plugin_1";
177
205
 
178
206
  // Copyright (c) Microsoft Corporation.
179
207
  class Utils {
180
- static checkParameters(paramObject) {
181
- const paramResult = {
182
- requiredNum: 0,
183
- optionalNum: 0,
184
- isValid: true,
185
- };
186
- if (!paramObject) {
187
- return paramResult;
188
- }
189
- for (let i = 0; i < paramObject.length; i++) {
190
- const param = paramObject[i];
191
- const schema = param.schema;
192
- const isRequiredWithoutDefault = param.required && schema.default === undefined;
193
- if (param.in === "header" || param.in === "cookie") {
194
- if (isRequiredWithoutDefault) {
195
- paramResult.isValid = false;
196
- }
197
- continue;
198
- }
199
- if (schema.type !== "boolean" &&
200
- schema.type !== "string" &&
201
- schema.type !== "number" &&
202
- schema.type !== "integer") {
203
- if (isRequiredWithoutDefault) {
204
- paramResult.isValid = false;
205
- }
206
- continue;
207
- }
208
- if (param.in === "query" || param.in === "path") {
209
- if (isRequiredWithoutDefault) {
210
- paramResult.requiredNum = paramResult.requiredNum + 1;
211
- }
212
- else {
213
- paramResult.optionalNum = paramResult.optionalNum + 1;
214
- }
215
- }
216
- }
217
- return paramResult;
218
- }
219
- static checkPostBody(schema, isRequired = false) {
220
- var _a;
221
- const paramResult = {
222
- requiredNum: 0,
223
- optionalNum: 0,
224
- isValid: true,
225
- };
226
- if (Object.keys(schema).length === 0) {
227
- return paramResult;
228
- }
229
- const isRequiredWithoutDefault = isRequired && schema.default === undefined;
230
- if (schema.type === "string" ||
231
- schema.type === "integer" ||
232
- schema.type === "boolean" ||
233
- schema.type === "number") {
234
- if (isRequiredWithoutDefault) {
235
- paramResult.requiredNum = paramResult.requiredNum + 1;
236
- }
237
- else {
238
- paramResult.optionalNum = paramResult.optionalNum + 1;
239
- }
240
- }
241
- else if (schema.type === "object") {
242
- const { properties } = schema;
243
- for (const property in properties) {
244
- let isRequired = false;
245
- if (schema.required && ((_a = schema.required) === null || _a === void 0 ? void 0 : _a.indexOf(property)) >= 0) {
246
- isRequired = true;
247
- }
248
- const result = Utils.checkPostBody(properties[property], isRequired);
249
- paramResult.requiredNum += result.requiredNum;
250
- paramResult.optionalNum += result.optionalNum;
251
- paramResult.isValid = paramResult.isValid && result.isValid;
252
- }
253
- }
254
- else {
255
- if (isRequiredWithoutDefault) {
256
- paramResult.isValid = false;
257
- }
258
- }
259
- return paramResult;
260
- }
261
- /**
262
- * Checks if the given API is supported.
263
- * @param {string} method - The HTTP method of the API.
264
- * @param {string} path - The path of the API.
265
- * @param {OpenAPIV3.Document} spec - The OpenAPI specification document.
266
- * @returns {boolean} - Returns true if the API is supported, false otherwise.
267
- * @description The following APIs are supported:
268
- * 1. only support Get/Post operation without auth property
269
- * 2. parameter inside query or path only support string, number, boolean and integer
270
- * 3. parameter inside post body only support string, number, boolean, integer and object
271
- * 4. request body + required parameters <= 1
272
- * 5. response body should be “application/json” and not empty, and response code should be 20X
273
- * 6. only support request body with “application/json” content type
274
- */
275
- static isSupportedApi(method, path, spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2) {
276
- const pathObj = spec.paths[path];
277
- method = method.toLocaleLowerCase();
278
- if (pathObj) {
279
- if ((method === ConstantString.PostMethod || method === ConstantString.GetMethod) &&
280
- pathObj[method]) {
281
- const securities = pathObj[method].security;
282
- const authArray = Utils.getAuthArray(securities, spec);
283
- if (!Utils.isSupportedAuth(authArray, allowAPIKeyAuth, allowOauth2)) {
284
- return false;
285
- }
286
- const operationObject = pathObj[method];
287
- if (!allowMissingId && !operationObject.operationId) {
288
- return false;
289
- }
290
- const paramObject = operationObject.parameters;
291
- const requestBody = operationObject.requestBody;
292
- 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) {
295
- return false;
296
- }
297
- const responseJson = Utils.getResponseJson(operationObject);
298
- if (Object.keys(responseJson).length === 0) {
299
- return false;
300
- }
301
- let requestBodyParamResult = {
302
- requiredNum: 0,
303
- optionalNum: 0,
304
- isValid: true,
305
- };
306
- if (requestJsonBody) {
307
- const requestBodySchema = requestJsonBody.schema;
308
- requestBodyParamResult = Utils.checkPostBody(requestBodySchema, requestBody.required);
309
- }
310
- if (!requestBodyParamResult.isValid) {
311
- return false;
312
- }
313
- const paramResult = Utils.checkParameters(paramObject);
314
- if (!paramResult.isValid) {
315
- return false;
316
- }
317
- if (requestBodyParamResult.requiredNum + paramResult.requiredNum > 1) {
318
- if (allowMultipleParameters &&
319
- requestBodyParamResult.requiredNum + paramResult.requiredNum <= 5) {
320
- return true;
321
- }
322
- return false;
323
- }
324
- else if (requestBodyParamResult.requiredNum +
325
- requestBodyParamResult.optionalNum +
326
- paramResult.requiredNum +
327
- paramResult.optionalNum ===
328
- 0) {
329
- return false;
330
- }
331
- else {
208
+ static hasNestedObjectInSchema(schema) {
209
+ if (schema.type === "object") {
210
+ for (const property in schema.properties) {
211
+ const nestedSchema = schema.properties[property];
212
+ if (nestedSchema.type === "object") {
332
213
  return true;
333
214
  }
334
215
  }
335
216
  }
336
217
  return false;
337
218
  }
338
- static isSupportedAuth(authSchemaArray, allowAPIKeyAuth, allowOauth2) {
339
- if (authSchemaArray.length === 0) {
340
- return true;
341
- }
342
- if (allowAPIKeyAuth || allowOauth2) {
343
- // Currently we don't support multiple auth in one operation
344
- if (authSchemaArray.length > 0 && authSchemaArray.every((auths) => auths.length > 1)) {
345
- return false;
346
- }
347
- for (const auths of authSchemaArray) {
348
- if (auths.length === 1) {
349
- if (!allowOauth2 && allowAPIKeyAuth && Utils.isAPIKeyAuth(auths[0].authSchema)) {
350
- return true;
351
- }
352
- else if (!allowAPIKeyAuth &&
353
- allowOauth2 &&
354
- Utils.isBearerTokenAuth(auths[0].authSchema)) {
355
- return true;
356
- }
357
- else if (allowAPIKeyAuth &&
358
- allowOauth2 &&
359
- (Utils.isAPIKeyAuth(auths[0].authSchema) ||
360
- Utils.isBearerTokenAuth(auths[0].authSchema))) {
361
- return true;
362
- }
363
- }
364
- }
365
- }
366
- return false;
219
+ static containMultipleMediaTypes(bodyObject) {
220
+ return Object.keys((bodyObject === null || bodyObject === void 0 ? void 0 : bodyObject.content) || {}).length > 1;
367
221
  }
368
- static isAPIKeyAuth(authSchema) {
369
- return authSchema.type === "apiKey";
222
+ static isBearerTokenAuth(authScheme) {
223
+ return authScheme.type === "http" && authScheme.scheme === "bearer";
370
224
  }
371
- static isBearerTokenAuth(authSchema) {
372
- return (authSchema.type === "oauth2" ||
373
- authSchema.type === "openIdConnect" ||
374
- (authSchema.type === "http" && authSchema.scheme === "bearer"));
225
+ static isAPIKeyAuth(authScheme) {
226
+ return authScheme.type === "apiKey";
227
+ }
228
+ static isOAuthWithAuthCodeFlow(authScheme) {
229
+ return !!(authScheme.type === "oauth2" &&
230
+ authScheme.flows &&
231
+ authScheme.flows.authorizationCode);
375
232
  }
376
233
  static getAuthArray(securities, spec) {
377
234
  var _a;
@@ -384,7 +241,7 @@ class Utils {
384
241
  for (const name in security) {
385
242
  const auth = securitySchemas[name];
386
243
  authArray.push({
387
- authSchema: auth,
244
+ authScheme: auth,
388
245
  name: name,
389
246
  });
390
247
  }
@@ -402,18 +259,22 @@ class Utils {
402
259
  static getResponseJson(operationObject) {
403
260
  var _a, _b;
404
261
  let json = {};
262
+ let multipleMediaType = false;
405
263
  for (const code of ConstantString.ResponseCodeFor20X) {
406
264
  const responseObject = (_a = operationObject === null || operationObject === void 0 ? void 0 : operationObject.responses) === null || _a === void 0 ? void 0 : _a[code];
407
- const mediaTypesCount = Object.keys((responseObject === null || responseObject === void 0 ? void 0 : responseObject.content) || {}).length;
408
- if (mediaTypesCount > 1) {
409
- return {};
410
- }
411
265
  if ((_b = responseObject === null || responseObject === void 0 ? void 0 : responseObject.content) === null || _b === void 0 ? void 0 : _b["application/json"]) {
266
+ multipleMediaType = false;
412
267
  json = responseObject.content["application/json"];
413
- break;
268
+ if (Utils.containMultipleMediaTypes(responseObject)) {
269
+ multipleMediaType = true;
270
+ json = {};
271
+ }
272
+ else {
273
+ break;
274
+ }
414
275
  }
415
276
  }
416
- return json;
277
+ return { json, multipleMediaType };
417
278
  }
418
279
  static convertPathToCamelCase(path) {
419
280
  const pathSegments = path.split(/[./{]/);
@@ -433,10 +294,10 @@ class Utils {
433
294
  return undefined;
434
295
  }
435
296
  }
436
- static resolveServerUrl(url) {
297
+ static resolveEnv(str) {
437
298
  const placeHolderReg = /\${{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*}}/g;
438
- let matches = placeHolderReg.exec(url);
439
- let newUrl = url;
299
+ let matches = placeHolderReg.exec(str);
300
+ let newStr = str;
440
301
  while (matches != null) {
441
302
  const envVar = matches[1];
442
303
  const envVal = process.env[envVar];
@@ -444,17 +305,17 @@ class Utils {
444
305
  throw new Error(Utils.format(ConstantString.ResolveServerUrlFailed, envVar));
445
306
  }
446
307
  else {
447
- newUrl = newUrl.replace(matches[0], envVal);
308
+ newStr = newStr.replace(matches[0], envVal);
448
309
  }
449
- matches = placeHolderReg.exec(url);
310
+ matches = placeHolderReg.exec(str);
450
311
  }
451
- return newUrl;
312
+ return newStr;
452
313
  }
453
314
  static checkServerUrl(servers) {
454
315
  const errors = [];
455
316
  let serverUrl;
456
317
  try {
457
- serverUrl = Utils.resolveServerUrl(servers[0].url);
318
+ serverUrl = Utils.resolveEnv(servers[0].url);
458
319
  }
459
320
  catch (err) {
460
321
  errors.push({
@@ -484,7 +345,8 @@ class Utils {
484
345
  }
485
346
  return errors;
486
347
  }
487
- static validateServer(spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2) {
348
+ static validateServer(spec, options) {
349
+ var _a;
488
350
  const errors = [];
489
351
  let hasTopLevelServers = false;
490
352
  let hasPathLevelServers = false;
@@ -505,7 +367,7 @@ class Utils {
505
367
  }
506
368
  for (const method in methods) {
507
369
  const operationObject = methods[method];
508
- if (Utils.isSupportedApi(method, path, spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2)) {
370
+ if (((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && operationObject) {
509
371
  if ((operationObject === null || operationObject === void 0 ? void 0 : operationObject.servers) && operationObject.servers.length >= 1) {
510
372
  hasOperationLevelServers = true;
511
373
  const serverErrors = Utils.checkServerUrl(operationObject.servers);
@@ -548,6 +410,7 @@ class Utils {
548
410
  Utils.updateParameterWithInputType(schema, parameter);
549
411
  }
550
412
  if (isRequired && schema.default === undefined) {
413
+ parameter.isRequired = true;
551
414
  requiredParams.push(parameter);
552
415
  }
553
416
  else {
@@ -592,7 +455,7 @@ class Utils {
592
455
  param.value = schema.default;
593
456
  }
594
457
  }
595
- static parseApiInfo(operationItem, allowMultipleParameters) {
458
+ static parseApiInfo(operationItem, options) {
596
459
  var _a, _b;
597
460
  const requiredParams = [];
598
461
  const optionalParams = [];
@@ -606,11 +469,12 @@ class Utils {
606
469
  description: ((_a = param.description) !== null && _a !== void 0 ? _a : "").slice(0, ConstantString.ParameterDescriptionMaxLens),
607
470
  };
608
471
  const schema = param.schema;
609
- if (allowMultipleParameters && schema) {
472
+ if (options.allowMultipleParameters && schema) {
610
473
  Utils.updateParameterWithInputType(schema, parameter);
611
474
  }
612
475
  if (param.in !== "header" && param.in !== "cookie") {
613
476
  if (param.required && (schema === null || schema === void 0 ? void 0 : schema.default) === undefined) {
477
+ parameter.isRequired = true;
614
478
  requiredParams.push(parameter);
615
479
  }
616
480
  else {
@@ -624,19 +488,13 @@ class Utils {
624
488
  const requestJson = requestBody.content["application/json"];
625
489
  if (Object.keys(requestJson).length !== 0) {
626
490
  const schema = requestJson.schema;
627
- const [requiredP, optionalP] = Utils.generateParametersFromSchema(schema, "requestBody", allowMultipleParameters, requestBody.required);
491
+ const [requiredP, optionalP] = Utils.generateParametersFromSchema(schema, "requestBody", !!options.allowMultipleParameters, requestBody.required);
628
492
  requiredParams.push(...requiredP);
629
493
  optionalParams.push(...optionalP);
630
494
  }
631
495
  }
632
496
  const operationId = operationItem.operationId;
633
- const parameters = [];
634
- if (requiredParams.length !== 0) {
635
- parameters.push(...requiredParams);
636
- }
637
- else {
638
- parameters.push(optionalParams[0]);
639
- }
497
+ const parameters = [...requiredParams, ...optionalParams];
640
498
  const command = {
641
499
  context: ["compose"],
642
500
  type: "query",
@@ -645,105 +503,535 @@ class Utils {
645
503
  parameters: parameters,
646
504
  description: ((_b = operationItem.description) !== null && _b !== void 0 ? _b : "").slice(0, ConstantString.CommandDescriptionMaxLens),
647
505
  };
648
- let warning = undefined;
649
- if (requiredParams.length === 0 && optionalParams.length > 1) {
650
- warning = {
651
- type: WarningType.OperationOnlyContainsOptionalParam,
652
- content: Utils.format(ConstantString.OperationOnlyContainsOptionalParam, operationId),
653
- data: operationId,
654
- };
506
+ return command;
507
+ }
508
+ static format(str, ...args) {
509
+ let index = 0;
510
+ return str.replace(/%s/g, () => {
511
+ const arg = args[index++];
512
+ return arg !== undefined ? arg : "";
513
+ });
514
+ }
515
+ static getSafeRegistrationIdEnvName(authName) {
516
+ if (!authName) {
517
+ return "";
655
518
  }
656
- return [command, warning];
519
+ let safeRegistrationIdEnvName = authName.toUpperCase().replace(/[^A-Z0-9_]/g, "_");
520
+ if (!safeRegistrationIdEnvName.match(/^[A-Z]/)) {
521
+ safeRegistrationIdEnvName = "PREFIX_" + safeRegistrationIdEnvName;
522
+ }
523
+ return safeRegistrationIdEnvName;
657
524
  }
658
- static listSupportedAPIs(spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2) {
659
- const paths = spec.paths;
525
+ static getServerObject(spec, method, path) {
526
+ const pathObj = spec.paths[path];
527
+ const operationObject = pathObj[method];
528
+ const rootServer = spec.servers && spec.servers[0];
529
+ const methodServer = spec.paths[path].servers && spec.paths[path].servers[0];
530
+ const operationServer = operationObject.servers && operationObject.servers[0];
531
+ const serverUrl = operationServer || methodServer || rootServer;
532
+ return serverUrl;
533
+ }
534
+ }
535
+
536
+ // Copyright (c) Microsoft Corporation.
537
+ class Validator {
538
+ listAPIs() {
539
+ var _a;
540
+ if (this.apiMap) {
541
+ return this.apiMap;
542
+ }
543
+ const paths = this.spec.paths;
660
544
  const result = {};
661
545
  for (const path in paths) {
662
546
  const methods = paths[path];
663
547
  for (const method in methods) {
664
- // For developer preview, only support GET operation with only 1 parameter without auth
665
- if (Utils.isSupportedApi(method, path, spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2)) {
666
- const operationObject = methods[method];
667
- result[`${method.toUpperCase()} ${path}`] = operationObject;
548
+ const operationObject = methods[method];
549
+ if (((_a = this.options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && operationObject) {
550
+ const validateResult = this.validateAPI(method, path);
551
+ result[`${method.toUpperCase()} ${path}`] = {
552
+ operation: operationObject,
553
+ isValid: validateResult.isValid,
554
+ reason: validateResult.reason,
555
+ };
668
556
  }
669
557
  }
670
558
  }
559
+ this.apiMap = result;
671
560
  return result;
672
561
  }
673
- static validateSpec(spec, parser, isSwaggerFile, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2) {
674
- const errors = [];
675
- const warnings = [];
676
- if (isSwaggerFile) {
677
- warnings.push({
678
- type: WarningType.ConvertSwaggerToOpenAPI,
679
- content: ConstantString.ConvertSwaggerToOpenAPI,
680
- });
681
- }
682
- // Server validation
683
- const serverErrors = Utils.validateServer(spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2);
684
- errors.push(...serverErrors);
685
- // Remote reference not supported
686
- const refPaths = parser.$refs.paths();
687
- // refPaths [0] is the current spec file path
688
- if (refPaths.length > 1) {
689
- errors.push({
690
- type: ErrorType.RemoteRefNotSupported,
691
- content: Utils.format(ConstantString.RemoteRefNotSupported, refPaths.join(", ")),
692
- data: refPaths,
562
+ validateSpecVersion() {
563
+ const result = { errors: [], warnings: [] };
564
+ if (this.spec.openapi >= "3.1.0") {
565
+ result.errors.push({
566
+ type: ErrorType.SpecVersionNotSupported,
567
+ content: Utils.format(ConstantString.SpecVersionNotSupported, this.spec.openapi),
568
+ data: this.spec.openapi,
693
569
  });
694
570
  }
695
- // No supported API
696
- const apiMap = Utils.listSupportedAPIs(spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2);
697
- if (Object.keys(apiMap).length === 0) {
698
- errors.push({
571
+ return result;
572
+ }
573
+ validateSpecServer() {
574
+ const result = { errors: [], warnings: [] };
575
+ const serverErrors = Utils.validateServer(this.spec, this.options);
576
+ result.errors.push(...serverErrors);
577
+ return result;
578
+ }
579
+ validateSpecNoSupportAPI() {
580
+ const result = { errors: [], warnings: [] };
581
+ const apiMap = this.listAPIs();
582
+ const validAPIs = Object.entries(apiMap).filter(([, value]) => value.isValid);
583
+ if (validAPIs.length === 0) {
584
+ const data = [];
585
+ for (const key in apiMap) {
586
+ const { reason } = apiMap[key];
587
+ const apiInvalidReason = { api: key, reason: reason };
588
+ data.push(apiInvalidReason);
589
+ }
590
+ result.errors.push({
699
591
  type: ErrorType.NoSupportedApi,
700
592
  content: ConstantString.NoSupportedApi,
593
+ data,
701
594
  });
702
595
  }
596
+ return result;
597
+ }
598
+ validateSpecOperationId() {
599
+ const result = { errors: [], warnings: [] };
600
+ const apiMap = this.listAPIs();
703
601
  // OperationId missing
704
602
  const apisMissingOperationId = [];
705
603
  for (const key in apiMap) {
706
- const pathObjectItem = apiMap[key];
707
- if (!pathObjectItem.operationId) {
604
+ const { operation } = apiMap[key];
605
+ if (!operation.operationId) {
708
606
  apisMissingOperationId.push(key);
709
607
  }
710
608
  }
711
609
  if (apisMissingOperationId.length > 0) {
712
- warnings.push({
610
+ result.warnings.push({
713
611
  type: WarningType.OperationIdMissing,
714
612
  content: Utils.format(ConstantString.MissingOperationId, apisMissingOperationId.join(", ")),
715
613
  data: apisMissingOperationId,
716
614
  });
717
615
  }
718
- let status = ValidationStatus.Valid;
719
- if (warnings.length > 0 && errors.length === 0) {
720
- status = ValidationStatus.Warning;
616
+ return result;
617
+ }
618
+ validateMethodAndPath(method, path) {
619
+ const result = { isValid: true, reason: [] };
620
+ if (this.options.allowMethods && !this.options.allowMethods.includes(method)) {
621
+ result.isValid = false;
622
+ result.reason.push(ErrorType.MethodNotAllowed);
623
+ return result;
624
+ }
625
+ const pathObj = this.spec.paths[path];
626
+ if (!pathObj || !pathObj[method]) {
627
+ result.isValid = false;
628
+ result.reason.push(ErrorType.UrlPathNotExist);
629
+ return result;
721
630
  }
722
- else if (errors.length > 0) {
723
- status = ValidationStatus.Error;
631
+ return result;
632
+ }
633
+ validateResponse(method, path) {
634
+ const result = { isValid: true, reason: [] };
635
+ const operationObject = this.spec.paths[path][method];
636
+ const { json, multipleMediaType } = Utils.getResponseJson(operationObject);
637
+ // only support response body only contains “application/json” content type
638
+ if (multipleMediaType) {
639
+ result.reason.push(ErrorType.ResponseContainMultipleMediaTypes);
724
640
  }
725
- return {
726
- status,
727
- warnings,
728
- errors,
641
+ else if (Object.keys(json).length === 0) {
642
+ // response body should not be empty
643
+ result.reason.push(ErrorType.ResponseJsonIsEmpty);
644
+ }
645
+ return result;
646
+ }
647
+ validateServer(method, path) {
648
+ const result = { isValid: true, reason: [] };
649
+ const serverObj = Utils.getServerObject(this.spec, method, path);
650
+ if (!serverObj) {
651
+ // should contain server URL
652
+ result.reason.push(ErrorType.NoServerInformation);
653
+ }
654
+ else {
655
+ // server url should be absolute url with https protocol
656
+ const serverValidateResult = Utils.checkServerUrl([serverObj]);
657
+ result.reason.push(...serverValidateResult.map((item) => item.type));
658
+ }
659
+ return result;
660
+ }
661
+ validateAuth(method, path) {
662
+ const pathObj = this.spec.paths[path];
663
+ const operationObject = pathObj[method];
664
+ const securities = operationObject.security;
665
+ const authSchemeArray = Utils.getAuthArray(securities, this.spec);
666
+ if (authSchemeArray.length === 0) {
667
+ return { isValid: true, reason: [] };
668
+ }
669
+ if (this.options.allowAPIKeyAuth ||
670
+ this.options.allowOauth2 ||
671
+ this.options.allowBearerTokenAuth) {
672
+ // Currently we don't support multiple auth in one operation
673
+ if (authSchemeArray.length > 0 && authSchemeArray.every((auths) => auths.length > 1)) {
674
+ return {
675
+ isValid: false,
676
+ reason: [ErrorType.MultipleAuthNotSupported],
677
+ };
678
+ }
679
+ for (const auths of authSchemeArray) {
680
+ if (auths.length === 1) {
681
+ if ((this.options.allowAPIKeyAuth && Utils.isAPIKeyAuth(auths[0].authScheme)) ||
682
+ (this.options.allowOauth2 && Utils.isOAuthWithAuthCodeFlow(auths[0].authScheme)) ||
683
+ (this.options.allowBearerTokenAuth && Utils.isBearerTokenAuth(auths[0].authScheme))) {
684
+ return { isValid: true, reason: [] };
685
+ }
686
+ }
687
+ }
688
+ }
689
+ return { isValid: false, reason: [ErrorType.AuthTypeIsNotSupported] };
690
+ }
691
+ checkPostBodySchema(schema, isRequired = false) {
692
+ var _a;
693
+ const paramResult = {
694
+ requiredNum: 0,
695
+ optionalNum: 0,
696
+ isValid: true,
697
+ reason: [],
729
698
  };
699
+ if (Object.keys(schema).length === 0) {
700
+ return paramResult;
701
+ }
702
+ const isRequiredWithoutDefault = isRequired && schema.default === undefined;
703
+ const isCopilot = this.projectType === ProjectType.Copilot;
704
+ if (isCopilot && this.hasNestedObjectInSchema(schema)) {
705
+ paramResult.isValid = false;
706
+ paramResult.reason = [ErrorType.RequestBodyContainsNestedObject];
707
+ return paramResult;
708
+ }
709
+ if (schema.type === "string" ||
710
+ schema.type === "integer" ||
711
+ schema.type === "boolean" ||
712
+ schema.type === "number") {
713
+ if (isRequiredWithoutDefault) {
714
+ paramResult.requiredNum = paramResult.requiredNum + 1;
715
+ }
716
+ else {
717
+ paramResult.optionalNum = paramResult.optionalNum + 1;
718
+ }
719
+ }
720
+ else if (schema.type === "object") {
721
+ const { properties } = schema;
722
+ for (const property in properties) {
723
+ let isRequired = false;
724
+ if (schema.required && ((_a = schema.required) === null || _a === void 0 ? void 0 : _a.indexOf(property)) >= 0) {
725
+ isRequired = true;
726
+ }
727
+ const result = this.checkPostBodySchema(properties[property], isRequired);
728
+ paramResult.requiredNum += result.requiredNum;
729
+ paramResult.optionalNum += result.optionalNum;
730
+ paramResult.isValid = paramResult.isValid && result.isValid;
731
+ paramResult.reason.push(...result.reason);
732
+ }
733
+ }
734
+ else {
735
+ if (isRequiredWithoutDefault && !isCopilot) {
736
+ paramResult.isValid = false;
737
+ paramResult.reason.push(ErrorType.PostBodyContainsRequiredUnsupportedSchema);
738
+ }
739
+ }
740
+ return paramResult;
730
741
  }
731
- static format(str, ...args) {
732
- let index = 0;
733
- return str.replace(/%s/g, () => {
734
- const arg = args[index++];
735
- return arg !== undefined ? arg : "";
736
- });
742
+ checkParamSchema(paramObject) {
743
+ const paramResult = {
744
+ requiredNum: 0,
745
+ optionalNum: 0,
746
+ isValid: true,
747
+ reason: [],
748
+ };
749
+ if (!paramObject) {
750
+ return paramResult;
751
+ }
752
+ const isCopilot = this.projectType === ProjectType.Copilot;
753
+ for (let i = 0; i < paramObject.length; i++) {
754
+ const param = paramObject[i];
755
+ const schema = param.schema;
756
+ if (isCopilot && this.hasNestedObjectInSchema(schema)) {
757
+ paramResult.isValid = false;
758
+ paramResult.reason.push(ErrorType.ParamsContainsNestedObject);
759
+ continue;
760
+ }
761
+ const isRequiredWithoutDefault = param.required && schema.default === undefined;
762
+ if (isCopilot) {
763
+ if (isRequiredWithoutDefault) {
764
+ paramResult.requiredNum = paramResult.requiredNum + 1;
765
+ }
766
+ else {
767
+ paramResult.optionalNum = paramResult.optionalNum + 1;
768
+ }
769
+ continue;
770
+ }
771
+ if (param.in === "header" || param.in === "cookie") {
772
+ if (isRequiredWithoutDefault) {
773
+ paramResult.isValid = false;
774
+ paramResult.reason.push(ErrorType.ParamsContainRequiredUnsupportedSchema);
775
+ }
776
+ continue;
777
+ }
778
+ if (schema.type !== "boolean" &&
779
+ schema.type !== "string" &&
780
+ schema.type !== "number" &&
781
+ schema.type !== "integer") {
782
+ if (isRequiredWithoutDefault) {
783
+ paramResult.isValid = false;
784
+ paramResult.reason.push(ErrorType.ParamsContainRequiredUnsupportedSchema);
785
+ }
786
+ continue;
787
+ }
788
+ if (param.in === "query" || param.in === "path") {
789
+ if (isRequiredWithoutDefault) {
790
+ paramResult.requiredNum = paramResult.requiredNum + 1;
791
+ }
792
+ else {
793
+ paramResult.optionalNum = paramResult.optionalNum + 1;
794
+ }
795
+ }
796
+ }
797
+ return paramResult;
737
798
  }
738
- static getSafeRegistrationIdEnvName(authName) {
739
- if (!authName) {
740
- return "";
799
+ hasNestedObjectInSchema(schema) {
800
+ if (schema.type === "object") {
801
+ for (const property in schema.properties) {
802
+ const nestedSchema = schema.properties[property];
803
+ if (nestedSchema.type === "object") {
804
+ return true;
805
+ }
806
+ }
741
807
  }
742
- let safeRegistrationIdEnvName = authName.toUpperCase().replace(/[^A-Z0-9_]/g, "_");
743
- if (!safeRegistrationIdEnvName.match(/^[A-Z]/)) {
744
- safeRegistrationIdEnvName = "PREFIX_" + safeRegistrationIdEnvName;
808
+ return false;
809
+ }
810
+ }
811
+
812
+ // Copyright (c) Microsoft Corporation.
813
+ class CopilotValidator extends Validator {
814
+ constructor(spec, options) {
815
+ super();
816
+ this.projectType = ProjectType.Copilot;
817
+ this.options = options;
818
+ this.spec = spec;
819
+ }
820
+ validateSpec() {
821
+ const result = { errors: [], warnings: [] };
822
+ // validate spec version
823
+ let validationResult = this.validateSpecVersion();
824
+ result.errors.push(...validationResult.errors);
825
+ // validate spec server
826
+ validationResult = this.validateSpecServer();
827
+ result.errors.push(...validationResult.errors);
828
+ // validate no supported API
829
+ validationResult = this.validateSpecNoSupportAPI();
830
+ result.errors.push(...validationResult.errors);
831
+ // validate operationId missing
832
+ validationResult = this.validateSpecOperationId();
833
+ result.warnings.push(...validationResult.warnings);
834
+ return result;
835
+ }
836
+ validateAPI(method, path) {
837
+ const result = { isValid: true, reason: [] };
838
+ method = method.toLocaleLowerCase();
839
+ // validate method and path
840
+ const methodAndPathResult = this.validateMethodAndPath(method, path);
841
+ if (!methodAndPathResult.isValid) {
842
+ return methodAndPathResult;
843
+ }
844
+ const operationObject = this.spec.paths[path][method];
845
+ // validate auth
846
+ const authCheckResult = this.validateAuth(method, path);
847
+ result.reason.push(...authCheckResult.reason);
848
+ // validate operationId
849
+ if (!this.options.allowMissingId && !operationObject.operationId) {
850
+ result.reason.push(ErrorType.MissingOperationId);
851
+ }
852
+ // validate server
853
+ const validateServerResult = this.validateServer(method, path);
854
+ result.reason.push(...validateServerResult.reason);
855
+ // validate response
856
+ const validateResponseResult = this.validateResponse(method, path);
857
+ result.reason.push(...validateResponseResult.reason);
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
+ if (requestBodySchema.type !== "object") {
867
+ result.reason.push(ErrorType.PostBodySchemaIsNotJson);
868
+ }
869
+ const requestBodyParamResult = this.checkPostBodySchema(requestBodySchema, requestBody.required);
870
+ result.reason.push(...requestBodyParamResult.reason);
871
+ }
872
+ // validate parameters
873
+ const paramObject = operationObject.parameters;
874
+ const paramResult = this.checkParamSchema(paramObject);
875
+ result.reason.push(...paramResult.reason);
876
+ if (result.reason.length > 0) {
877
+ result.isValid = false;
878
+ }
879
+ return result;
880
+ }
881
+ }
882
+
883
+ // Copyright (c) Microsoft Corporation.
884
+ class SMEValidator extends Validator {
885
+ constructor(spec, options) {
886
+ super();
887
+ this.projectType = ProjectType.SME;
888
+ this.options = options;
889
+ this.spec = spec;
890
+ }
891
+ validateSpec() {
892
+ const result = { errors: [], warnings: [] };
893
+ // validate spec version
894
+ let validationResult = this.validateSpecVersion();
895
+ result.errors.push(...validationResult.errors);
896
+ // validate spec server
897
+ validationResult = this.validateSpecServer();
898
+ result.errors.push(...validationResult.errors);
899
+ // validate no supported API
900
+ validationResult = this.validateSpecNoSupportAPI();
901
+ result.errors.push(...validationResult.errors);
902
+ // validate operationId missing
903
+ if (this.options.allowMissingId) {
904
+ validationResult = this.validateSpecOperationId();
905
+ result.warnings.push(...validationResult.warnings);
906
+ }
907
+ return result;
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 auth
919
+ const authCheckResult = this.validateAuth(method, path);
920
+ result.reason.push(...authCheckResult.reason);
921
+ // validate operationId
922
+ if (!this.options.allowMissingId && !operationObject.operationId) {
923
+ result.reason.push(ErrorType.MissingOperationId);
924
+ }
925
+ // validate server
926
+ const validateServerResult = this.validateServer(method, path);
927
+ result.reason.push(...validateServerResult.reason);
928
+ // validate response
929
+ const validateResponseResult = this.validateResponse(method, path);
930
+ result.reason.push(...validateResponseResult.reason);
931
+ let postBodyResult = {
932
+ requiredNum: 0,
933
+ optionalNum: 0,
934
+ isValid: true,
935
+ reason: [],
936
+ };
937
+ // validate requestBody
938
+ const requestBody = operationObject.requestBody;
939
+ const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
940
+ if (Utils.containMultipleMediaTypes(requestBody)) {
941
+ result.reason.push(ErrorType.PostBodyContainMultipleMediaTypes);
942
+ }
943
+ if (requestJsonBody) {
944
+ const requestBodySchema = requestJsonBody.schema;
945
+ postBodyResult = this.checkPostBodySchema(requestBodySchema, requestBody.required);
946
+ result.reason.push(...postBodyResult.reason);
947
+ }
948
+ // validate parameters
949
+ const paramObject = operationObject.parameters;
950
+ const paramResult = this.checkParamSchema(paramObject);
951
+ result.reason.push(...paramResult.reason);
952
+ // validate total parameters count
953
+ if (paramResult.isValid && postBodyResult.isValid) {
954
+ const paramCountResult = this.validateParamCount(postBodyResult, paramResult);
955
+ result.reason.push(...paramCountResult.reason);
956
+ }
957
+ if (result.reason.length > 0) {
958
+ result.isValid = false;
959
+ }
960
+ return result;
961
+ }
962
+ validateParamCount(postBodyResult, paramResult) {
963
+ const result = { isValid: true, reason: [] };
964
+ const totalRequiredParams = postBodyResult.requiredNum + paramResult.requiredNum;
965
+ const totalParams = totalRequiredParams + postBodyResult.optionalNum + paramResult.optionalNum;
966
+ if (totalRequiredParams > 1) {
967
+ if (!this.options.allowMultipleParameters ||
968
+ totalRequiredParams > SMEValidator.SMERequiredParamsMaxNum) {
969
+ result.reason.push(ErrorType.ExceededRequiredParamsLimit);
970
+ }
971
+ }
972
+ else if (totalParams === 0) {
973
+ result.reason.push(ErrorType.NoParameter);
974
+ }
975
+ return result;
976
+ }
977
+ }
978
+ SMEValidator.SMERequiredParamsMaxNum = 5;
979
+
980
+ // Copyright (c) Microsoft Corporation.
981
+ class TeamsAIValidator extends Validator {
982
+ constructor(spec, options) {
983
+ super();
984
+ this.projectType = ProjectType.TeamsAi;
985
+ this.options = options;
986
+ this.spec = spec;
987
+ }
988
+ validateSpec() {
989
+ const result = { errors: [], warnings: [] };
990
+ // validate spec server
991
+ let validationResult = this.validateSpecServer();
992
+ result.errors.push(...validationResult.errors);
993
+ // validate no supported API
994
+ validationResult = this.validateSpecNoSupportAPI();
995
+ result.errors.push(...validationResult.errors);
996
+ return result;
997
+ }
998
+ validateAPI(method, path) {
999
+ const result = { isValid: true, reason: [] };
1000
+ method = method.toLocaleLowerCase();
1001
+ // validate method and path
1002
+ const methodAndPathResult = this.validateMethodAndPath(method, path);
1003
+ if (!methodAndPathResult.isValid) {
1004
+ return methodAndPathResult;
1005
+ }
1006
+ const operationObject = this.spec.paths[path][method];
1007
+ // validate operationId
1008
+ if (!this.options.allowMissingId && !operationObject.operationId) {
1009
+ result.reason.push(ErrorType.MissingOperationId);
1010
+ }
1011
+ // validate server
1012
+ const validateServerResult = this.validateServer(method, path);
1013
+ result.reason.push(...validateServerResult.reason);
1014
+ if (result.reason.length > 0) {
1015
+ result.isValid = false;
1016
+ }
1017
+ return result;
1018
+ }
1019
+ }
1020
+
1021
+ class ValidatorFactory {
1022
+ static create(spec, options) {
1023
+ var _a;
1024
+ const type = (_a = options.projectType) !== null && _a !== void 0 ? _a : ProjectType.SME;
1025
+ switch (type) {
1026
+ case ProjectType.SME:
1027
+ return new SMEValidator(spec, options);
1028
+ case ProjectType.Copilot:
1029
+ return new CopilotValidator(spec, options);
1030
+ case ProjectType.TeamsAi:
1031
+ return new TeamsAIValidator(spec, options);
1032
+ default:
1033
+ throw new Error(`Invalid project type: ${type}`);
745
1034
  }
746
- return safeRegistrationIdEnvName;
747
1035
  }
748
1036
  }
749
1037
 
@@ -763,7 +1051,10 @@ class SpecParser {
763
1051
  allowSwagger: false,
764
1052
  allowAPIKeyAuth: false,
765
1053
  allowMultipleParameters: false,
1054
+ allowBearerTokenAuth: false,
766
1055
  allowOauth2: false,
1056
+ allowMethods: ["get", "post"],
1057
+ projectType: ProjectType.SME,
767
1058
  };
768
1059
  this.pathOrSpec = pathOrDoc;
769
1060
  this.parser = new SwaggerParser();
@@ -779,11 +1070,7 @@ class SpecParser {
779
1070
  try {
780
1071
  try {
781
1072
  yield this.loadSpec();
782
- yield this.parser.validate(this.spec, {
783
- validate: {
784
- schema: false,
785
- },
786
- });
1073
+ yield this.parser.validate(this.spec);
787
1074
  }
788
1075
  catch (e) {
789
1076
  return {
@@ -792,16 +1079,46 @@ class SpecParser {
792
1079
  errors: [{ type: ErrorType.SpecNotValid, content: e.toString() }],
793
1080
  };
794
1081
  }
1082
+ const errors = [];
1083
+ const warnings = [];
795
1084
  if (!this.options.allowSwagger && this.isSwaggerFile) {
796
1085
  return {
797
1086
  status: ValidationStatus.Error,
798
1087
  warnings: [],
799
1088
  errors: [
800
- { type: ErrorType.SwaggerNotSupported, content: ConstantString.SwaggerNotSupported },
1089
+ {
1090
+ type: ErrorType.SwaggerNotSupported,
1091
+ content: ConstantString.SwaggerNotSupported,
1092
+ },
801
1093
  ],
802
1094
  };
803
1095
  }
804
- return Utils.validateSpec(this.spec, this.parser, !!this.isSwaggerFile, this.options.allowMissingId, this.options.allowAPIKeyAuth, this.options.allowMultipleParameters, this.options.allowOauth2);
1096
+ // Remote reference not supported
1097
+ const refPaths = this.parser.$refs.paths();
1098
+ // refPaths [0] is the current spec file path
1099
+ if (refPaths.length > 1) {
1100
+ errors.push({
1101
+ type: ErrorType.RemoteRefNotSupported,
1102
+ content: Utils.format(ConstantString.RemoteRefNotSupported, refPaths.join(", ")),
1103
+ data: refPaths,
1104
+ });
1105
+ }
1106
+ const validator = this.getValidator(this.spec);
1107
+ const validationResult = validator.validateSpec();
1108
+ warnings.push(...validationResult.warnings);
1109
+ errors.push(...validationResult.errors);
1110
+ let status = ValidationStatus.Valid;
1111
+ if (warnings.length > 0 && errors.length === 0) {
1112
+ status = ValidationStatus.Warning;
1113
+ }
1114
+ else if (errors.length > 0) {
1115
+ status = ValidationStatus.Error;
1116
+ }
1117
+ return {
1118
+ status: status,
1119
+ warnings: warnings,
1120
+ errors: errors,
1121
+ };
805
1122
  }
806
1123
  catch (err) {
807
1124
  throw new SpecParserError(err.toString(), ErrorType.ValidateFailed);
@@ -812,17 +1129,20 @@ class SpecParser {
812
1129
  return __awaiter(this, void 0, void 0, function* () {
813
1130
  try {
814
1131
  yield this.loadSpec();
815
- const apiMap = this.getAllSupportedAPIs(this.spec);
1132
+ const apiMap = this.getAPIs(this.spec);
816
1133
  const apiInfos = [];
817
1134
  for (const key in apiMap) {
818
- const pathObjectItem = apiMap[key];
1135
+ const { operation, isValid } = apiMap[key];
1136
+ if (!isValid) {
1137
+ continue;
1138
+ }
819
1139
  const [method, path] = key.split(" ");
820
- const operationId = pathObjectItem.operationId;
1140
+ const operationId = operation.operationId;
821
1141
  // In Browser environment, this api is by default not support api without operationId
822
1142
  if (!operationId) {
823
1143
  continue;
824
1144
  }
825
- const [command, warning] = Utils.parseApiInfo(pathObjectItem, this.options.allowMultipleParameters);
1145
+ const command = Utils.parseApiInfo(operation, this.options);
826
1146
  const apiInfo = {
827
1147
  method: method,
828
1148
  path: path,
@@ -831,9 +1151,6 @@ class SpecParser {
831
1151
  parameters: command.parameters,
832
1152
  description: command.description,
833
1153
  };
834
- if (warning) {
835
- apiInfo.warning = warning;
836
- }
837
1154
  apiInfos.push(apiInfo);
838
1155
  }
839
1156
  return apiInfos;
@@ -854,12 +1171,36 @@ class SpecParser {
854
1171
  throw new Error("Method not implemented.");
855
1172
  });
856
1173
  }
1174
+ /**
1175
+ * Generate specs according to the filters.
1176
+ * @param filter An array of strings that represent the filters to apply when generating the artifacts. If filter is empty, it would process nothing.
1177
+ */
1178
+ // eslint-disable-next-line @typescript-eslint/require-await
1179
+ getFilteredSpecs(filter, signal) {
1180
+ return __awaiter(this, void 0, void 0, function* () {
1181
+ throw new Error("Method not implemented.");
1182
+ });
1183
+ }
1184
+ /**
1185
+ * Generates and update artifacts from the OpenAPI specification file. Generate Adaptive Cards, update Teams app manifest, and generate a new OpenAPI specification file.
1186
+ * @param manifestPath A file path of the Teams app manifest file to update.
1187
+ * @param filter An array of strings that represent the filters to apply when generating the artifacts. If filter is empty, it would process nothing.
1188
+ * @param outputSpecPath File path of the new OpenAPI specification file to generate. If not specified or empty, no spec file will be generated.
1189
+ * @param pluginFilePath File path of the api plugin file to generate.
1190
+ */
1191
+ // eslint-disable-next-line @typescript-eslint/require-await
1192
+ generateForCopilot(manifestPath, filter, outputSpecPath, pluginFilePath, signal) {
1193
+ return __awaiter(this, void 0, void 0, function* () {
1194
+ throw new Error("Method not implemented.");
1195
+ });
1196
+ }
857
1197
  /**
858
1198
  * Generates and update artifacts from the OpenAPI specification file. Generate Adaptive Cards, update Teams app manifest, and generate a new OpenAPI specification file.
859
1199
  * @param manifestPath A file path of the Teams app manifest file to update.
860
1200
  * @param filter An array of strings that represent the filters to apply when generating the artifacts. If filter is empty, it would process nothing.
861
1201
  * @param outputSpecPath File path of the new OpenAPI specification file to generate. If not specified or empty, no spec file will be generated.
862
1202
  * @param adaptiveCardFolder Folder path where the Adaptive Card files will be generated. If not specified or empty, Adaptive Card files will not be generated.
1203
+ * @param isMe Boolean that indicates whether the project is an Messaging Extension. For Messaging Extension, composeExtensions will be added in Teams app manifest.
863
1204
  */
864
1205
  // eslint-disable-next-line @typescript-eslint/require-await
865
1206
  generate(manifestPath, filter, outputSpecPath, adaptiveCardFolder, signal) {
@@ -879,15 +1220,182 @@ class SpecParser {
879
1220
  }
880
1221
  });
881
1222
  }
882
- getAllSupportedAPIs(spec) {
1223
+ getAPIs(spec) {
883
1224
  if (this.apiMap !== undefined) {
884
1225
  return this.apiMap;
885
1226
  }
886
- const result = Utils.listSupportedAPIs(spec, this.options.allowMissingId, this.options.allowAPIKeyAuth, this.options.allowMultipleParameters, this.options.allowOauth2);
887
- this.apiMap = result;
888
- return result;
1227
+ const validator = this.getValidator(spec);
1228
+ const apiMap = validator.listAPIs();
1229
+ this.apiMap = apiMap;
1230
+ return apiMap;
1231
+ }
1232
+ getValidator(spec) {
1233
+ if (this.validator) {
1234
+ return this.validator;
1235
+ }
1236
+ const validator = ValidatorFactory.create(spec, this.options);
1237
+ this.validator = validator;
1238
+ return validator;
1239
+ }
1240
+ }
1241
+
1242
+ // Copyright (c) Microsoft Corporation.
1243
+ class AdaptiveCardGenerator {
1244
+ static generateAdaptiveCard(operationItem) {
1245
+ try {
1246
+ const { json } = Utils.getResponseJson(operationItem);
1247
+ let cardBody = [];
1248
+ let schema = json.schema;
1249
+ let jsonPath = "$";
1250
+ if (schema && Object.keys(schema).length > 0) {
1251
+ jsonPath = AdaptiveCardGenerator.getResponseJsonPathFromSchema(schema);
1252
+ if (jsonPath !== "$") {
1253
+ schema = schema.properties[jsonPath];
1254
+ }
1255
+ cardBody = AdaptiveCardGenerator.generateCardFromResponse(schema, "");
1256
+ }
1257
+ // if no schema, try to use example value
1258
+ if (cardBody.length === 0 && (json.examples || json.example)) {
1259
+ cardBody = [
1260
+ {
1261
+ type: ConstantString.TextBlockType,
1262
+ text: "${jsonStringify($root)}",
1263
+ wrap: true,
1264
+ },
1265
+ ];
1266
+ }
1267
+ // if no example value, use default success response
1268
+ if (cardBody.length === 0) {
1269
+ cardBody = [
1270
+ {
1271
+ type: ConstantString.TextBlockType,
1272
+ text: "success",
1273
+ wrap: true,
1274
+ },
1275
+ ];
1276
+ }
1277
+ const fullCard = {
1278
+ type: ConstantString.AdaptiveCardType,
1279
+ $schema: ConstantString.AdaptiveCardSchema,
1280
+ version: ConstantString.AdaptiveCardVersion,
1281
+ body: cardBody,
1282
+ };
1283
+ return [fullCard, jsonPath];
1284
+ }
1285
+ catch (err) {
1286
+ throw new SpecParserError(err.toString(), ErrorType.GenerateAdaptiveCardFailed);
1287
+ }
1288
+ }
1289
+ static generateCardFromResponse(schema, name, parentArrayName = "") {
1290
+ if (schema.type === "array") {
1291
+ // schema.items can be arbitrary object: schema { type: array, items: {} }
1292
+ if (Object.keys(schema.items).length === 0) {
1293
+ return [
1294
+ {
1295
+ type: ConstantString.TextBlockType,
1296
+ text: name ? `${name}: \${jsonStringify(${name})}` : "result: ${jsonStringify($root)}",
1297
+ wrap: true,
1298
+ },
1299
+ ];
1300
+ }
1301
+ const obj = AdaptiveCardGenerator.generateCardFromResponse(schema.items, "", name);
1302
+ const template = {
1303
+ type: ConstantString.ContainerType,
1304
+ $data: name ? `\${${name}}` : "${$root}",
1305
+ items: Array(),
1306
+ };
1307
+ template.items.push(...obj);
1308
+ return [template];
1309
+ }
1310
+ // some schema may not contain type but contain properties
1311
+ if (schema.type === "object" || (!schema.type && schema.properties)) {
1312
+ const { properties } = schema;
1313
+ const result = [];
1314
+ for (const property in properties) {
1315
+ const obj = AdaptiveCardGenerator.generateCardFromResponse(properties[property], name ? `${name}.${property}` : property, parentArrayName);
1316
+ result.push(...obj);
1317
+ }
1318
+ if (schema.additionalProperties) {
1319
+ // TODO: better ways to handler warnings.
1320
+ console.warn(ConstantString.AdditionalPropertiesNotSupported);
1321
+ }
1322
+ return result;
1323
+ }
1324
+ if (schema.type === "string" ||
1325
+ schema.type === "integer" ||
1326
+ schema.type === "boolean" ||
1327
+ schema.type === "number") {
1328
+ if (!AdaptiveCardGenerator.isImageUrlProperty(schema, name, parentArrayName)) {
1329
+ // string in root: "ddd"
1330
+ let text = "result: ${$root}";
1331
+ if (name) {
1332
+ // object { id: "1" }
1333
+ text = `${name}: \${if(${name}, ${name}, 'N/A')}`;
1334
+ if (parentArrayName) {
1335
+ // object types inside array: { tags: ["id": 1, "name": "name"] }
1336
+ text = `${parentArrayName}.${text}`;
1337
+ }
1338
+ }
1339
+ else if (parentArrayName) {
1340
+ // string array: photoUrls: ["1", "2"]
1341
+ text = `${parentArrayName}: ` + "${$data}";
1342
+ }
1343
+ return [
1344
+ {
1345
+ type: ConstantString.TextBlockType,
1346
+ text,
1347
+ wrap: true,
1348
+ },
1349
+ ];
1350
+ }
1351
+ else {
1352
+ if (name) {
1353
+ return [
1354
+ {
1355
+ type: "Image",
1356
+ url: `\${${name}}`,
1357
+ $when: `\${${name} != null}`,
1358
+ },
1359
+ ];
1360
+ }
1361
+ else {
1362
+ return [
1363
+ {
1364
+ type: "Image",
1365
+ url: "${$data}",
1366
+ $when: "${$data != null}",
1367
+ },
1368
+ ];
1369
+ }
1370
+ }
1371
+ }
1372
+ if (schema.oneOf || schema.anyOf || schema.not || schema.allOf) {
1373
+ throw new Error(Utils.format(ConstantString.SchemaNotSupported, JSON.stringify(schema)));
1374
+ }
1375
+ throw new Error(Utils.format(ConstantString.UnknownSchema, JSON.stringify(schema)));
1376
+ }
1377
+ // Find the first array property in the response schema object with the well-known name
1378
+ static getResponseJsonPathFromSchema(schema) {
1379
+ if (schema.type === "object" || (!schema.type && schema.properties)) {
1380
+ const { properties } = schema;
1381
+ for (const property in properties) {
1382
+ const schema = properties[property];
1383
+ if (schema.type === "array" &&
1384
+ Utils.isWellKnownName(property, ConstantString.WellknownResultNames)) {
1385
+ return property;
1386
+ }
1387
+ }
1388
+ }
1389
+ return "$";
1390
+ }
1391
+ static isImageUrlProperty(schema, name, parentArrayName) {
1392
+ const propertyName = name ? name : parentArrayName;
1393
+ return (!!propertyName &&
1394
+ schema.type === "string" &&
1395
+ Utils.isWellKnownName(propertyName, ConstantString.WellknownImageName) &&
1396
+ (propertyName.toLocaleLowerCase().indexOf("url") >= 0 || schema.format === "uri"));
889
1397
  }
890
1398
  }
891
1399
 
892
- export { ConstantString, ErrorType, SpecParser, SpecParserError, Utils, ValidationStatus, WarningType };
1400
+ export { AdaptiveCardGenerator, ConstantString, ErrorType, SpecParser, SpecParserError, Utils, ValidationStatus, WarningType };
893
1401
  //# sourceMappingURL=index.esm5.js.map