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