@microsoft/m365-spec-parser 0.1.1-alpha.78701ec6a.0 → 0.1.1-alpha.87f45d762.0

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