@microsoft/m365-spec-parser 0.1.1-alpha.149079452.0 → 0.1.1-alpha.18377bc6e.0

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