@microsoft/m365-spec-parser 0.1.1-alpha.d1a09e202.0 → 0.1.1-alpha.ded43fb2d.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.
@@ -20,6 +20,7 @@ var ErrorType;
20
20
  ErrorType["ResolveServerUrlFailed"] = "resolve-server-url-failed";
21
21
  ErrorType["SwaggerNotSupported"] = "swagger-not-supported";
22
22
  ErrorType["MultipleAuthNotSupported"] = "multiple-auth-not-supported";
23
+ ErrorType["SpecVersionNotSupported"] = "spec-version-not-supported";
23
24
  ErrorType["ListFailed"] = "list-failed";
24
25
  ErrorType["listSupportedAPIInfoFailed"] = "list-supported-api-info-failed";
25
26
  ErrorType["FilterSpecFailed"] = "filter-spec-failed";
@@ -28,6 +29,21 @@ var ErrorType;
28
29
  ErrorType["GenerateFailed"] = "generate-failed";
29
30
  ErrorType["ValidateFailed"] = "validate-failed";
30
31
  ErrorType["GetSpecFailed"] = "get-spec-failed";
32
+ ErrorType["AuthTypeIsNotSupported"] = "auth-type-is-not-supported";
33
+ ErrorType["MissingOperationId"] = "missing-operation-id";
34
+ ErrorType["PostBodyContainMultipleMediaTypes"] = "post-body-contain-multiple-media-types";
35
+ ErrorType["ResponseContainMultipleMediaTypes"] = "response-contain-multiple-media-types";
36
+ ErrorType["ResponseJsonIsEmpty"] = "response-json-is-empty";
37
+ ErrorType["PostBodySchemaIsNotJson"] = "post-body-schema-is-not-json";
38
+ ErrorType["PostBodyContainsRequiredUnsupportedSchema"] = "post-body-contains-required-unsupported-schema";
39
+ ErrorType["ParamsContainRequiredUnsupportedSchema"] = "params-contain-required-unsupported-schema";
40
+ ErrorType["ParamsContainsNestedObject"] = "params-contains-nested-object";
41
+ ErrorType["RequestBodyContainsNestedObject"] = "request-body-contains-nested-object";
42
+ ErrorType["ExceededRequiredParamsLimit"] = "exceeded-required-params-limit";
43
+ ErrorType["NoParameter"] = "no-parameter";
44
+ ErrorType["NoAPIInfo"] = "no-api-info";
45
+ ErrorType["MethodNotAllowed"] = "method-not-allowed";
46
+ ErrorType["UrlPathNotExist"] = "url-path-not-exist";
31
47
  ErrorType["Cancelled"] = "cancelled";
32
48
  ErrorType["Unknown"] = "unknown";
33
49
  })(ErrorType || (ErrorType = {}));
@@ -75,6 +91,7 @@ ConstantString.ResolveServerUrlFailed = "Unable to resolve the server URL: pleas
75
91
  ConstantString.OperationOnlyContainsOptionalParam = "Operation %s contains multiple optional parameters. The first optional parameter is used for this command.";
76
92
  ConstantString.ConvertSwaggerToOpenAPI = "The Swagger 2.0 file has been converted to OpenAPI 3.0.";
77
93
  ConstantString.SwaggerNotSupported = "Swagger 2.0 is not supported. Please convert to OpenAPI 3.0 manually before proceeding.";
94
+ ConstantString.SpecVersionNotSupported = "Unsupported OpenAPI version %s. Please use version 3.0.x.";
78
95
  ConstantString.MultipleAuthNotSupported = "Multiple authentication methods are unsupported. Ensure all selected APIs use identical authentication.";
79
96
  ConstantString.UnsupportedSchema = "Unsupported schema in %s %s: %s";
80
97
  ConstantString.WrappedCardVersion = "devPreview";
@@ -86,6 +103,7 @@ ConstantString.AdaptiveCardVersion = "1.5";
86
103
  ConstantString.AdaptiveCardSchema = "http://adaptivecards.io/schemas/adaptive-card.json";
87
104
  ConstantString.AdaptiveCardType = "AdaptiveCard";
88
105
  ConstantString.TextBlockType = "TextBlock";
106
+ ConstantString.ImageType = "Image";
89
107
  ConstantString.ContainerType = "Container";
90
108
  ConstantString.RegistrationIdPostfix = "REGISTRATION_ID";
91
109
  ConstantString.OAuthRegistrationIdPostFix = "OAUTH_REGISTRATION_ID";
@@ -149,7 +167,8 @@ ConstantString.CommandDescriptionMaxLens = 128;
149
167
  ConstantString.ParameterDescriptionMaxLens = 128;
150
168
  ConstantString.CommandTitleMaxLens = 32;
151
169
  ConstantString.ParameterTitleMaxLens = 32;
152
- ConstantString.SMERequiredParamsMaxNum = 5;
170
+ ConstantString.SMERequiredParamsMaxNum = 5;
171
+ ConstantString.DefaultPluginId = "plugin_1";
153
172
 
154
173
  // Copyright (c) Microsoft Corporation.
155
174
  class SpecParserError extends Error {
@@ -172,221 +191,9 @@ class Utils {
172
191
  }
173
192
  return false;
174
193
  }
175
- static checkParameters(paramObject, isCopilot) {
176
- const paramResult = {
177
- requiredNum: 0,
178
- optionalNum: 0,
179
- isValid: true,
180
- };
181
- if (!paramObject) {
182
- return paramResult;
183
- }
184
- for (let i = 0; i < paramObject.length; i++) {
185
- const param = paramObject[i];
186
- const schema = param.schema;
187
- if (isCopilot && this.hasNestedObjectInSchema(schema)) {
188
- paramResult.isValid = false;
189
- continue;
190
- }
191
- const isRequiredWithoutDefault = param.required && schema.default === undefined;
192
- if (isCopilot) {
193
- if (isRequiredWithoutDefault) {
194
- paramResult.requiredNum = paramResult.requiredNum + 1;
195
- }
196
- else {
197
- paramResult.optionalNum = paramResult.optionalNum + 1;
198
- }
199
- continue;
200
- }
201
- if (param.in === "header" || param.in === "cookie") {
202
- if (isRequiredWithoutDefault) {
203
- paramResult.isValid = false;
204
- }
205
- continue;
206
- }
207
- if (schema.type !== "boolean" &&
208
- schema.type !== "string" &&
209
- schema.type !== "number" &&
210
- schema.type !== "integer") {
211
- if (isRequiredWithoutDefault) {
212
- paramResult.isValid = false;
213
- }
214
- continue;
215
- }
216
- if (param.in === "query" || param.in === "path") {
217
- if (isRequiredWithoutDefault) {
218
- paramResult.requiredNum = paramResult.requiredNum + 1;
219
- }
220
- else {
221
- paramResult.optionalNum = paramResult.optionalNum + 1;
222
- }
223
- }
224
- }
225
- return paramResult;
226
- }
227
- static checkPostBody(schema, isRequired = false, isCopilot = false) {
228
- var _a;
229
- const paramResult = {
230
- requiredNum: 0,
231
- optionalNum: 0,
232
- isValid: true,
233
- };
234
- if (Object.keys(schema).length === 0) {
235
- return paramResult;
236
- }
237
- const isRequiredWithoutDefault = isRequired && schema.default === undefined;
238
- if (isCopilot && this.hasNestedObjectInSchema(schema)) {
239
- paramResult.isValid = false;
240
- return paramResult;
241
- }
242
- if (schema.type === "string" ||
243
- schema.type === "integer" ||
244
- schema.type === "boolean" ||
245
- schema.type === "number") {
246
- if (isRequiredWithoutDefault) {
247
- paramResult.requiredNum = paramResult.requiredNum + 1;
248
- }
249
- else {
250
- paramResult.optionalNum = paramResult.optionalNum + 1;
251
- }
252
- }
253
- else if (schema.type === "object") {
254
- const { properties } = schema;
255
- for (const property in properties) {
256
- let isRequired = false;
257
- if (schema.required && ((_a = schema.required) === null || _a === void 0 ? void 0 : _a.indexOf(property)) >= 0) {
258
- isRequired = true;
259
- }
260
- const result = Utils.checkPostBody(properties[property], isRequired, isCopilot);
261
- paramResult.requiredNum += result.requiredNum;
262
- paramResult.optionalNum += result.optionalNum;
263
- paramResult.isValid = paramResult.isValid && result.isValid;
264
- }
265
- }
266
- else {
267
- if (isRequiredWithoutDefault && !isCopilot) {
268
- paramResult.isValid = false;
269
- }
270
- }
271
- return paramResult;
272
- }
273
194
  static containMultipleMediaTypes(bodyObject) {
274
195
  return Object.keys((bodyObject === null || bodyObject === void 0 ? void 0 : bodyObject.content) || {}).length > 1;
275
196
  }
276
- /**
277
- * Checks if the given API is supported.
278
- * @param {string} method - The HTTP method of the API.
279
- * @param {string} path - The path of the API.
280
- * @param {OpenAPIV3.Document} spec - The OpenAPI specification document.
281
- * @returns {boolean} - Returns true if the API is supported, false otherwise.
282
- * @description The following APIs are supported:
283
- * 1. only support Get/Post operation without auth property
284
- * 2. parameter inside query or path only support string, number, boolean and integer
285
- * 3. parameter inside post body only support string, number, boolean, integer and object
286
- * 4. request body + required parameters <= 1
287
- * 5. response body should be “application/json” and not empty, and response code should be 20X
288
- * 6. only support request body with “application/json” content type
289
- */
290
- static isSupportedApi(method, path, spec, options) {
291
- var _a;
292
- const pathObj = spec.paths[path];
293
- method = method.toLocaleLowerCase();
294
- if (pathObj) {
295
- if (((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && pathObj[method]) {
296
- const securities = pathObj[method].security;
297
- const isTeamsAi = options.projectType === ProjectType.TeamsAi;
298
- const isCopilot = options.projectType === ProjectType.Copilot;
299
- // Teams AI project doesn't care about auth, it will use authProvider for user to implement
300
- if (!isTeamsAi) {
301
- const authArray = Utils.getAuthArray(securities, spec);
302
- if (!Utils.isSupportedAuth(authArray, options)) {
303
- return false;
304
- }
305
- }
306
- const operationObject = pathObj[method];
307
- if (!options.allowMissingId && !operationObject.operationId) {
308
- return false;
309
- }
310
- const paramObject = operationObject.parameters;
311
- const requestBody = operationObject.requestBody;
312
- const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
313
- if (!isTeamsAi && Utils.containMultipleMediaTypes(requestBody)) {
314
- return false;
315
- }
316
- const responseJson = Utils.getResponseJson(operationObject, isTeamsAi);
317
- if (Object.keys(responseJson).length === 0) {
318
- return false;
319
- }
320
- // Teams AI project doesn't care about request parameters/body
321
- if (isTeamsAi) {
322
- return true;
323
- }
324
- let requestBodyParamResult = {
325
- requiredNum: 0,
326
- optionalNum: 0,
327
- isValid: true,
328
- };
329
- if (requestJsonBody) {
330
- const requestBodySchema = requestJsonBody.schema;
331
- if (isCopilot && requestBodySchema.type !== "object") {
332
- return false;
333
- }
334
- requestBodyParamResult = Utils.checkPostBody(requestBodySchema, requestBody.required, isCopilot);
335
- }
336
- if (!requestBodyParamResult.isValid) {
337
- return false;
338
- }
339
- const paramResult = Utils.checkParameters(paramObject, isCopilot);
340
- if (!paramResult.isValid) {
341
- return false;
342
- }
343
- // Copilot support arbitrary parameters
344
- if (isCopilot) {
345
- return true;
346
- }
347
- if (requestBodyParamResult.requiredNum + paramResult.requiredNum > 1) {
348
- if (options.allowMultipleParameters &&
349
- requestBodyParamResult.requiredNum + paramResult.requiredNum <=
350
- ConstantString.SMERequiredParamsMaxNum) {
351
- return true;
352
- }
353
- return false;
354
- }
355
- else if (requestBodyParamResult.requiredNum +
356
- requestBodyParamResult.optionalNum +
357
- paramResult.requiredNum +
358
- paramResult.optionalNum ===
359
- 0) {
360
- return false;
361
- }
362
- else {
363
- return true;
364
- }
365
- }
366
- }
367
- return false;
368
- }
369
- static isSupportedAuth(authSchemeArray, options) {
370
- if (authSchemeArray.length === 0) {
371
- return true;
372
- }
373
- if (options.allowAPIKeyAuth || options.allowOauth2 || options.allowBearerTokenAuth) {
374
- // Currently we don't support multiple auth in one operation
375
- if (authSchemeArray.length > 0 && authSchemeArray.every((auths) => auths.length > 1)) {
376
- return false;
377
- }
378
- for (const auths of authSchemeArray) {
379
- if (auths.length === 1) {
380
- if ((options.allowAPIKeyAuth && Utils.isAPIKeyAuth(auths[0].authScheme)) ||
381
- (options.allowOauth2 && Utils.isOAuthWithAuthCodeFlow(auths[0].authScheme)) ||
382
- (options.allowBearerTokenAuth && Utils.isBearerTokenAuth(auths[0].authScheme))) {
383
- return true;
384
- }
385
- }
386
- }
387
- }
388
- return false;
389
- }
390
197
  static isBearerTokenAuth(authScheme) {
391
198
  return authScheme.type === "http" && authScheme.scheme === "bearer";
392
199
  }
@@ -394,10 +201,9 @@ class Utils {
394
201
  return authScheme.type === "apiKey";
395
202
  }
396
203
  static isOAuthWithAuthCodeFlow(authScheme) {
397
- if (authScheme.type === "oauth2" && authScheme.flows && authScheme.flows.authorizationCode) {
398
- return true;
399
- }
400
- return false;
204
+ return !!(authScheme.type === "oauth2" &&
205
+ authScheme.flows &&
206
+ authScheme.flows.authorizationCode);
401
207
  }
402
208
  static getAuthArray(securities, spec) {
403
209
  var _a;
@@ -422,17 +228,39 @@ class Utils {
422
228
  result.sort((a, b) => a[0].name.localeCompare(b[0].name));
423
229
  return result;
424
230
  }
231
+ static getAuthInfo(spec) {
232
+ let authInfo = undefined;
233
+ for (const url in spec.paths) {
234
+ for (const method in spec.paths[url]) {
235
+ const operation = spec.paths[url][method];
236
+ const authArray = Utils.getAuthArray(operation.security, spec);
237
+ if (authArray && authArray.length > 0) {
238
+ const currentAuth = authArray[0][0];
239
+ if (!authInfo) {
240
+ authInfo = authArray[0][0];
241
+ }
242
+ else if (authInfo.name !== currentAuth.name) {
243
+ throw new SpecParserError(ConstantString.MultipleAuthNotSupported, ErrorType.MultipleAuthNotSupported);
244
+ }
245
+ }
246
+ }
247
+ }
248
+ return authInfo;
249
+ }
425
250
  static updateFirstLetter(str) {
426
251
  return str.charAt(0).toUpperCase() + str.slice(1);
427
252
  }
428
- static getResponseJson(operationObject, isTeamsAiProject = false) {
253
+ static getResponseJson(operationObject) {
429
254
  var _a, _b;
430
255
  let json = {};
256
+ let multipleMediaType = false;
431
257
  for (const code of ConstantString.ResponseCodeFor20X) {
432
258
  const responseObject = (_a = operationObject === null || operationObject === void 0 ? void 0 : operationObject.responses) === null || _a === void 0 ? void 0 : _a[code];
433
259
  if ((_b = responseObject === null || responseObject === void 0 ? void 0 : responseObject.content) === null || _b === void 0 ? void 0 : _b["application/json"]) {
260
+ multipleMediaType = false;
434
261
  json = responseObject.content["application/json"];
435
- if (!isTeamsAiProject && Utils.containMultipleMediaTypes(responseObject)) {
262
+ if (Utils.containMultipleMediaTypes(responseObject)) {
263
+ multipleMediaType = true;
436
264
  json = {};
437
265
  }
438
266
  else {
@@ -440,7 +268,7 @@ class Utils {
440
268
  }
441
269
  }
442
270
  }
443
- return json;
271
+ return { json, multipleMediaType };
444
272
  }
445
273
  static convertPathToCamelCase(path) {
446
274
  const pathSegments = path.split(/[./{]/);
@@ -460,10 +288,10 @@ class Utils {
460
288
  return undefined;
461
289
  }
462
290
  }
463
- static resolveServerUrl(url) {
291
+ static resolveEnv(str) {
464
292
  const placeHolderReg = /\${{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*}}/g;
465
- let matches = placeHolderReg.exec(url);
466
- let newUrl = url;
293
+ let matches = placeHolderReg.exec(str);
294
+ let newStr = str;
467
295
  while (matches != null) {
468
296
  const envVar = matches[1];
469
297
  const envVal = process.env[envVar];
@@ -471,17 +299,17 @@ class Utils {
471
299
  throw new Error(Utils.format(ConstantString.ResolveServerUrlFailed, envVar));
472
300
  }
473
301
  else {
474
- newUrl = newUrl.replace(matches[0], envVal);
302
+ newStr = newStr.replace(matches[0], envVal);
475
303
  }
476
- matches = placeHolderReg.exec(url);
304
+ matches = placeHolderReg.exec(str);
477
305
  }
478
- return newUrl;
306
+ return newStr;
479
307
  }
480
308
  static checkServerUrl(servers) {
481
309
  const errors = [];
482
310
  let serverUrl;
483
311
  try {
484
- serverUrl = Utils.resolveServerUrl(servers[0].url);
312
+ serverUrl = Utils.resolveEnv(servers[0].url);
485
313
  }
486
314
  catch (err) {
487
315
  errors.push({
@@ -512,6 +340,7 @@ class Utils {
512
340
  return errors;
513
341
  }
514
342
  static validateServer(spec, options) {
343
+ var _a;
515
344
  const errors = [];
516
345
  let hasTopLevelServers = false;
517
346
  let hasPathLevelServers = false;
@@ -532,7 +361,7 @@ class Utils {
532
361
  }
533
362
  for (const method in methods) {
534
363
  const operationObject = methods[method];
535
- if (Utils.isSupportedApi(method, path, spec, options)) {
364
+ if (((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && operationObject) {
536
365
  if ((operationObject === null || operationObject === void 0 ? void 0 : operationObject.servers) && operationObject.servers.length >= 1) {
537
366
  hasOperationLevelServers = true;
538
367
  const serverErrors = Utils.checkServerUrl(operationObject.servers);
@@ -575,6 +404,7 @@ class Utils {
575
404
  Utils.updateParameterWithInputType(schema, parameter);
576
405
  }
577
406
  if (isRequired && schema.default === undefined) {
407
+ parameter.isRequired = true;
578
408
  requiredParams.push(parameter);
579
409
  }
580
410
  else {
@@ -638,6 +468,7 @@ class Utils {
638
468
  }
639
469
  if (param.in !== "header" && param.in !== "cookie") {
640
470
  if (param.required && (schema === null || schema === void 0 ? void 0 : schema.default) === undefined) {
471
+ parameter.isRequired = true;
641
472
  requiredParams.push(parameter);
642
473
  }
643
474
  else {
@@ -657,13 +488,7 @@ class Utils {
657
488
  }
658
489
  }
659
490
  const operationId = operationItem.operationId;
660
- const parameters = [];
661
- if (requiredParams.length !== 0) {
662
- parameters.push(...requiredParams);
663
- }
664
- else {
665
- parameters.push(optionalParams[0]);
666
- }
491
+ const parameters = [...requiredParams, ...optionalParams];
667
492
  const command = {
668
493
  context: ["compose"],
669
494
  type: "query",
@@ -672,155 +497,882 @@ class Utils {
672
497
  parameters: parameters,
673
498
  description: ((_b = operationItem.description) !== null && _b !== void 0 ? _b : "").slice(0, ConstantString.CommandDescriptionMaxLens),
674
499
  };
675
- let warning = undefined;
676
- if (requiredParams.length === 0 && optionalParams.length > 1) {
677
- warning = {
678
- type: WarningType.OperationOnlyContainsOptionalParam,
679
- content: Utils.format(ConstantString.OperationOnlyContainsOptionalParam, operationId),
680
- data: operationId,
681
- };
500
+ return command;
501
+ }
502
+ static format(str, ...args) {
503
+ let index = 0;
504
+ return str.replace(/%s/g, () => {
505
+ const arg = args[index++];
506
+ return arg !== undefined ? arg : "";
507
+ });
508
+ }
509
+ static getSafeRegistrationIdEnvName(authName) {
510
+ if (!authName) {
511
+ return "";
512
+ }
513
+ let safeRegistrationIdEnvName = authName.toUpperCase().replace(/[^A-Z0-9_]/g, "_");
514
+ if (!safeRegistrationIdEnvName.match(/^[A-Z]/)) {
515
+ safeRegistrationIdEnvName = "PREFIX_" + safeRegistrationIdEnvName;
682
516
  }
683
- return [command, warning];
517
+ return safeRegistrationIdEnvName;
684
518
  }
685
- static listSupportedAPIs(spec, options) {
686
- const paths = spec.paths;
519
+ static getServerObject(spec, method, path) {
520
+ const pathObj = spec.paths[path];
521
+ const operationObject = pathObj[method];
522
+ const rootServer = spec.servers && spec.servers[0];
523
+ const methodServer = spec.paths[path].servers && spec.paths[path].servers[0];
524
+ const operationServer = operationObject.servers && operationObject.servers[0];
525
+ const serverUrl = operationServer || methodServer || rootServer;
526
+ return serverUrl;
527
+ }
528
+ }
529
+
530
+ // Copyright (c) Microsoft Corporation.
531
+ class Validator {
532
+ listAPIs() {
533
+ var _a;
534
+ if (this.apiMap) {
535
+ return this.apiMap;
536
+ }
537
+ const paths = this.spec.paths;
687
538
  const result = {};
688
539
  for (const path in paths) {
689
540
  const methods = paths[path];
690
541
  for (const method in methods) {
691
- if (Utils.isSupportedApi(method, path, spec, options)) {
692
- const operationObject = methods[method];
693
- result[`${method.toUpperCase()} ${path}`] = operationObject;
542
+ const operationObject = methods[method];
543
+ if (((_a = this.options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && operationObject) {
544
+ const validateResult = this.validateAPI(method, path);
545
+ result[`${method.toUpperCase()} ${path}`] = {
546
+ operation: operationObject,
547
+ isValid: validateResult.isValid,
548
+ reason: validateResult.reason,
549
+ };
694
550
  }
695
551
  }
696
552
  }
553
+ this.apiMap = result;
697
554
  return result;
698
555
  }
699
- static validateSpec(spec, parser, isSwaggerFile, options) {
700
- const errors = [];
701
- const warnings = [];
702
- if (isSwaggerFile) {
703
- warnings.push({
704
- type: WarningType.ConvertSwaggerToOpenAPI,
705
- content: ConstantString.ConvertSwaggerToOpenAPI,
556
+ validateSpecVersion() {
557
+ const result = { errors: [], warnings: [] };
558
+ if (this.spec.openapi >= "3.1.0") {
559
+ result.errors.push({
560
+ type: ErrorType.SpecVersionNotSupported,
561
+ content: Utils.format(ConstantString.SpecVersionNotSupported, this.spec.openapi),
562
+ data: this.spec.openapi,
706
563
  });
707
564
  }
708
- // Server validation
709
- const serverErrors = Utils.validateServer(spec, options);
710
- errors.push(...serverErrors);
711
- // Remote reference not supported
712
- const refPaths = parser.$refs.paths();
713
- // refPaths [0] is the current spec file path
714
- if (refPaths.length > 1) {
715
- errors.push({
716
- type: ErrorType.RemoteRefNotSupported,
717
- content: Utils.format(ConstantString.RemoteRefNotSupported, refPaths.join(", ")),
718
- data: refPaths,
719
- });
720
- }
721
- // No supported API
722
- const apiMap = Utils.listSupportedAPIs(spec, options);
723
- if (Object.keys(apiMap).length === 0) {
724
- errors.push({
565
+ return result;
566
+ }
567
+ validateSpecServer() {
568
+ const result = { errors: [], warnings: [] };
569
+ const serverErrors = Utils.validateServer(this.spec, this.options);
570
+ result.errors.push(...serverErrors);
571
+ return result;
572
+ }
573
+ validateSpecNoSupportAPI() {
574
+ const result = { errors: [], warnings: [] };
575
+ const apiMap = this.listAPIs();
576
+ const validAPIs = Object.entries(apiMap).filter(([, value]) => value.isValid);
577
+ if (validAPIs.length === 0) {
578
+ const data = [];
579
+ for (const key in apiMap) {
580
+ const { reason } = apiMap[key];
581
+ const apiInvalidReason = { api: key, reason: reason };
582
+ data.push(apiInvalidReason);
583
+ }
584
+ result.errors.push({
725
585
  type: ErrorType.NoSupportedApi,
726
586
  content: ConstantString.NoSupportedApi,
587
+ data,
727
588
  });
728
589
  }
590
+ return result;
591
+ }
592
+ validateSpecOperationId() {
593
+ const result = { errors: [], warnings: [] };
594
+ const apiMap = this.listAPIs();
729
595
  // OperationId missing
730
596
  const apisMissingOperationId = [];
731
597
  for (const key in apiMap) {
732
- const pathObjectItem = apiMap[key];
733
- if (!pathObjectItem.operationId) {
598
+ const { operation } = apiMap[key];
599
+ if (!operation.operationId) {
734
600
  apisMissingOperationId.push(key);
735
601
  }
736
602
  }
737
603
  if (apisMissingOperationId.length > 0) {
738
- warnings.push({
604
+ result.warnings.push({
739
605
  type: WarningType.OperationIdMissing,
740
606
  content: Utils.format(ConstantString.MissingOperationId, apisMissingOperationId.join(", ")),
741
607
  data: apisMissingOperationId,
742
608
  });
743
609
  }
744
- let status = ValidationStatus.Valid;
745
- if (warnings.length > 0 && errors.length === 0) {
746
- status = ValidationStatus.Warning;
610
+ return result;
611
+ }
612
+ validateMethodAndPath(method, path) {
613
+ const result = { isValid: true, reason: [] };
614
+ if (this.options.allowMethods && !this.options.allowMethods.includes(method)) {
615
+ result.isValid = false;
616
+ result.reason.push(ErrorType.MethodNotAllowed);
617
+ return result;
747
618
  }
748
- else if (errors.length > 0) {
749
- status = ValidationStatus.Error;
619
+ const pathObj = this.spec.paths[path];
620
+ if (!pathObj || !pathObj[method]) {
621
+ result.isValid = false;
622
+ result.reason.push(ErrorType.UrlPathNotExist);
623
+ return result;
750
624
  }
751
- return {
752
- status,
753
- warnings,
754
- errors,
755
- };
625
+ return result;
756
626
  }
757
- static format(str, ...args) {
758
- let index = 0;
759
- return str.replace(/%s/g, () => {
760
- const arg = args[index++];
761
- return arg !== undefined ? arg : "";
762
- });
627
+ validateResponse(method, path) {
628
+ const result = { isValid: true, reason: [] };
629
+ const operationObject = this.spec.paths[path][method];
630
+ const { json, multipleMediaType } = Utils.getResponseJson(operationObject);
631
+ if (this.options.projectType === ProjectType.SME) {
632
+ // only support response body only contains “application/json” content type
633
+ if (multipleMediaType) {
634
+ result.reason.push(ErrorType.ResponseContainMultipleMediaTypes);
635
+ }
636
+ else if (Object.keys(json).length === 0) {
637
+ // response body should not be empty
638
+ result.reason.push(ErrorType.ResponseJsonIsEmpty);
639
+ }
640
+ }
641
+ return result;
763
642
  }
764
- static getSafeRegistrationIdEnvName(authName) {
765
- if (!authName) {
766
- return "";
643
+ validateServer(method, path) {
644
+ const result = { isValid: true, reason: [] };
645
+ const serverObj = Utils.getServerObject(this.spec, method, path);
646
+ if (!serverObj) {
647
+ // should contain server URL
648
+ result.reason.push(ErrorType.NoServerInformation);
767
649
  }
768
- let safeRegistrationIdEnvName = authName.toUpperCase().replace(/[^A-Z0-9_]/g, "_");
769
- if (!safeRegistrationIdEnvName.match(/^[A-Z]/)) {
770
- safeRegistrationIdEnvName = "PREFIX_" + safeRegistrationIdEnvName;
650
+ else {
651
+ // server url should be absolute url with https protocol
652
+ const serverValidateResult = Utils.checkServerUrl([serverObj]);
653
+ result.reason.push(...serverValidateResult.map((item) => item.type));
771
654
  }
772
- return safeRegistrationIdEnvName;
655
+ return result;
773
656
  }
774
- }
775
-
776
- // Copyright (c) Microsoft Corporation.
777
- class SpecFilter {
778
- static specFilter(filter, unResolveSpec, resolvedSpec, options) {
779
- try {
780
- const newSpec = Object.assign({}, unResolveSpec);
781
- const newPaths = {};
782
- for (const filterItem of filter) {
783
- const [method, path] = filterItem.split(" ");
784
- const methodName = method.toLowerCase();
785
- if (!Utils.isSupportedApi(methodName, path, resolvedSpec, options)) {
786
- continue;
787
- }
788
- if (!newPaths[path]) {
789
- newPaths[path] = Object.assign({}, unResolveSpec.paths[path]);
790
- for (const m of ConstantString.AllOperationMethods) {
791
- delete newPaths[path][m];
657
+ validateAuth(method, path) {
658
+ const pathObj = this.spec.paths[path];
659
+ const operationObject = pathObj[method];
660
+ const securities = operationObject.security;
661
+ const authSchemeArray = Utils.getAuthArray(securities, this.spec);
662
+ if (authSchemeArray.length === 0) {
663
+ return { isValid: true, reason: [] };
664
+ }
665
+ if (this.options.allowAPIKeyAuth ||
666
+ this.options.allowOauth2 ||
667
+ this.options.allowBearerTokenAuth) {
668
+ // Currently we don't support multiple auth in one operation
669
+ if (authSchemeArray.length > 0 && authSchemeArray.every((auths) => auths.length > 1)) {
670
+ return {
671
+ isValid: false,
672
+ reason: [ErrorType.MultipleAuthNotSupported],
673
+ };
674
+ }
675
+ for (const auths of authSchemeArray) {
676
+ if (auths.length === 1) {
677
+ if ((this.options.allowAPIKeyAuth && Utils.isAPIKeyAuth(auths[0].authScheme)) ||
678
+ (this.options.allowOauth2 && Utils.isOAuthWithAuthCodeFlow(auths[0].authScheme)) ||
679
+ (this.options.allowBearerTokenAuth && Utils.isBearerTokenAuth(auths[0].authScheme))) {
680
+ return { isValid: true, reason: [] };
792
681
  }
793
682
  }
794
- newPaths[path][methodName] = unResolveSpec.paths[path][methodName];
795
- // Add the operationId if missing
796
- if (!newPaths[path][methodName].operationId) {
797
- newPaths[path][methodName].operationId = `${methodName}${Utils.convertPathToCamelCase(path)}`;
798
- }
799
683
  }
800
- newSpec.paths = newPaths;
801
- return newSpec;
802
- }
803
- catch (err) {
804
- throw new SpecParserError(err.toString(), ErrorType.FilterSpecFailed);
805
684
  }
685
+ return { isValid: false, reason: [ErrorType.AuthTypeIsNotSupported] };
686
+ }
687
+ checkPostBodySchema(schema, isRequired = false) {
688
+ var _a;
689
+ const paramResult = {
690
+ requiredNum: 0,
691
+ optionalNum: 0,
692
+ isValid: true,
693
+ reason: [],
694
+ };
695
+ if (Object.keys(schema).length === 0) {
696
+ return paramResult;
697
+ }
698
+ const isRequiredWithoutDefault = isRequired && schema.default === undefined;
699
+ const isCopilot = this.projectType === ProjectType.Copilot;
700
+ if (isCopilot && this.hasNestedObjectInSchema(schema)) {
701
+ paramResult.isValid = false;
702
+ paramResult.reason = [ErrorType.RequestBodyContainsNestedObject];
703
+ return paramResult;
704
+ }
705
+ if (schema.type === "string" ||
706
+ schema.type === "integer" ||
707
+ schema.type === "boolean" ||
708
+ schema.type === "number") {
709
+ if (isRequiredWithoutDefault) {
710
+ paramResult.requiredNum = paramResult.requiredNum + 1;
711
+ }
712
+ else {
713
+ paramResult.optionalNum = paramResult.optionalNum + 1;
714
+ }
715
+ }
716
+ else if (schema.type === "object") {
717
+ const { properties } = schema;
718
+ for (const property in properties) {
719
+ let isRequired = false;
720
+ if (schema.required && ((_a = schema.required) === null || _a === void 0 ? void 0 : _a.indexOf(property)) >= 0) {
721
+ isRequired = true;
722
+ }
723
+ const result = this.checkPostBodySchema(properties[property], isRequired);
724
+ paramResult.requiredNum += result.requiredNum;
725
+ paramResult.optionalNum += result.optionalNum;
726
+ paramResult.isValid = paramResult.isValid && result.isValid;
727
+ paramResult.reason.push(...result.reason);
728
+ }
729
+ }
730
+ else {
731
+ if (isRequiredWithoutDefault && !isCopilot) {
732
+ paramResult.isValid = false;
733
+ paramResult.reason.push(ErrorType.PostBodyContainsRequiredUnsupportedSchema);
734
+ }
735
+ }
736
+ return paramResult;
737
+ }
738
+ checkParamSchema(paramObject) {
739
+ const paramResult = {
740
+ requiredNum: 0,
741
+ optionalNum: 0,
742
+ isValid: true,
743
+ reason: [],
744
+ };
745
+ if (!paramObject) {
746
+ return paramResult;
747
+ }
748
+ const isCopilot = this.projectType === ProjectType.Copilot;
749
+ for (let i = 0; i < paramObject.length; i++) {
750
+ const param = paramObject[i];
751
+ const schema = param.schema;
752
+ if (isCopilot && this.hasNestedObjectInSchema(schema)) {
753
+ paramResult.isValid = false;
754
+ paramResult.reason.push(ErrorType.ParamsContainsNestedObject);
755
+ continue;
756
+ }
757
+ const isRequiredWithoutDefault = param.required && schema.default === undefined;
758
+ if (isCopilot) {
759
+ if (isRequiredWithoutDefault) {
760
+ paramResult.requiredNum = paramResult.requiredNum + 1;
761
+ }
762
+ else {
763
+ paramResult.optionalNum = paramResult.optionalNum + 1;
764
+ }
765
+ continue;
766
+ }
767
+ if (param.in === "header" || param.in === "cookie") {
768
+ if (isRequiredWithoutDefault) {
769
+ paramResult.isValid = false;
770
+ paramResult.reason.push(ErrorType.ParamsContainRequiredUnsupportedSchema);
771
+ }
772
+ continue;
773
+ }
774
+ if (schema.type !== "boolean" &&
775
+ schema.type !== "string" &&
776
+ schema.type !== "number" &&
777
+ schema.type !== "integer") {
778
+ if (isRequiredWithoutDefault) {
779
+ paramResult.isValid = false;
780
+ paramResult.reason.push(ErrorType.ParamsContainRequiredUnsupportedSchema);
781
+ }
782
+ continue;
783
+ }
784
+ if (param.in === "query" || param.in === "path") {
785
+ if (isRequiredWithoutDefault) {
786
+ paramResult.requiredNum = paramResult.requiredNum + 1;
787
+ }
788
+ else {
789
+ paramResult.optionalNum = paramResult.optionalNum + 1;
790
+ }
791
+ }
792
+ }
793
+ return paramResult;
794
+ }
795
+ hasNestedObjectInSchema(schema) {
796
+ if (schema.type === "object") {
797
+ for (const property in schema.properties) {
798
+ const nestedSchema = schema.properties[property];
799
+ if (nestedSchema.type === "object") {
800
+ return true;
801
+ }
802
+ }
803
+ }
804
+ return false;
805
+ }
806
+ }
807
+
808
+ // Copyright (c) Microsoft Corporation.
809
+ class CopilotValidator extends Validator {
810
+ constructor(spec, options) {
811
+ super();
812
+ this.projectType = ProjectType.Copilot;
813
+ this.options = options;
814
+ this.spec = spec;
815
+ }
816
+ validateSpec() {
817
+ const result = { errors: [], warnings: [] };
818
+ // validate spec version
819
+ let validationResult = this.validateSpecVersion();
820
+ result.errors.push(...validationResult.errors);
821
+ // validate spec server
822
+ validationResult = this.validateSpecServer();
823
+ result.errors.push(...validationResult.errors);
824
+ // validate no supported API
825
+ validationResult = this.validateSpecNoSupportAPI();
826
+ result.errors.push(...validationResult.errors);
827
+ // validate operationId missing
828
+ validationResult = this.validateSpecOperationId();
829
+ result.warnings.push(...validationResult.warnings);
830
+ return result;
831
+ }
832
+ validateAPI(method, path) {
833
+ const result = { isValid: true, reason: [] };
834
+ method = method.toLocaleLowerCase();
835
+ // validate method and path
836
+ const methodAndPathResult = this.validateMethodAndPath(method, path);
837
+ if (!methodAndPathResult.isValid) {
838
+ return methodAndPathResult;
839
+ }
840
+ const operationObject = this.spec.paths[path][method];
841
+ // validate auth
842
+ const authCheckResult = this.validateAuth(method, path);
843
+ result.reason.push(...authCheckResult.reason);
844
+ // validate operationId
845
+ if (!this.options.allowMissingId && !operationObject.operationId) {
846
+ result.reason.push(ErrorType.MissingOperationId);
847
+ }
848
+ // validate server
849
+ const validateServerResult = this.validateServer(method, path);
850
+ result.reason.push(...validateServerResult.reason);
851
+ // validate response
852
+ const validateResponseResult = this.validateResponse(method, path);
853
+ result.reason.push(...validateResponseResult.reason);
854
+ // validate requestBody
855
+ const requestBody = operationObject.requestBody;
856
+ const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
857
+ if (requestJsonBody) {
858
+ const requestBodySchema = requestJsonBody.schema;
859
+ if (requestBodySchema.type !== "object") {
860
+ result.reason.push(ErrorType.PostBodySchemaIsNotJson);
861
+ }
862
+ const requestBodyParamResult = this.checkPostBodySchema(requestBodySchema, requestBody.required);
863
+ result.reason.push(...requestBodyParamResult.reason);
864
+ }
865
+ // validate parameters
866
+ const paramObject = operationObject.parameters;
867
+ const paramResult = this.checkParamSchema(paramObject);
868
+ result.reason.push(...paramResult.reason);
869
+ if (result.reason.length > 0) {
870
+ result.isValid = false;
871
+ }
872
+ return result;
873
+ }
874
+ }
875
+
876
+ // Copyright (c) Microsoft Corporation.
877
+ class SMEValidator extends Validator {
878
+ constructor(spec, options) {
879
+ super();
880
+ this.projectType = ProjectType.SME;
881
+ this.options = options;
882
+ this.spec = spec;
883
+ }
884
+ validateSpec() {
885
+ const result = { errors: [], warnings: [] };
886
+ // validate spec version
887
+ let validationResult = this.validateSpecVersion();
888
+ result.errors.push(...validationResult.errors);
889
+ // validate spec server
890
+ validationResult = this.validateSpecServer();
891
+ result.errors.push(...validationResult.errors);
892
+ // validate no supported API
893
+ validationResult = this.validateSpecNoSupportAPI();
894
+ result.errors.push(...validationResult.errors);
895
+ // validate operationId missing
896
+ if (this.options.allowMissingId) {
897
+ validationResult = this.validateSpecOperationId();
898
+ result.warnings.push(...validationResult.warnings);
899
+ }
900
+ return result;
901
+ }
902
+ validateAPI(method, path) {
903
+ const result = { isValid: true, reason: [] };
904
+ method = method.toLocaleLowerCase();
905
+ // validate method and path
906
+ const methodAndPathResult = this.validateMethodAndPath(method, path);
907
+ if (!methodAndPathResult.isValid) {
908
+ return methodAndPathResult;
909
+ }
910
+ const operationObject = this.spec.paths[path][method];
911
+ // validate auth
912
+ const authCheckResult = this.validateAuth(method, path);
913
+ result.reason.push(...authCheckResult.reason);
914
+ // validate operationId
915
+ if (!this.options.allowMissingId && !operationObject.operationId) {
916
+ result.reason.push(ErrorType.MissingOperationId);
917
+ }
918
+ // validate server
919
+ const validateServerResult = this.validateServer(method, path);
920
+ result.reason.push(...validateServerResult.reason);
921
+ // validate response
922
+ const validateResponseResult = this.validateResponse(method, path);
923
+ result.reason.push(...validateResponseResult.reason);
924
+ let postBodyResult = {
925
+ requiredNum: 0,
926
+ optionalNum: 0,
927
+ isValid: true,
928
+ reason: [],
929
+ };
930
+ // validate requestBody
931
+ const requestBody = operationObject.requestBody;
932
+ const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
933
+ if (Utils.containMultipleMediaTypes(requestBody)) {
934
+ result.reason.push(ErrorType.PostBodyContainMultipleMediaTypes);
935
+ }
936
+ if (requestJsonBody) {
937
+ const requestBodySchema = requestJsonBody.schema;
938
+ postBodyResult = this.checkPostBodySchema(requestBodySchema, requestBody.required);
939
+ result.reason.push(...postBodyResult.reason);
940
+ }
941
+ // validate parameters
942
+ const paramObject = operationObject.parameters;
943
+ const paramResult = this.checkParamSchema(paramObject);
944
+ result.reason.push(...paramResult.reason);
945
+ // validate total parameters count
946
+ if (paramResult.isValid && postBodyResult.isValid) {
947
+ const paramCountResult = this.validateParamCount(postBodyResult, paramResult);
948
+ result.reason.push(...paramCountResult.reason);
949
+ }
950
+ if (result.reason.length > 0) {
951
+ result.isValid = false;
952
+ }
953
+ return result;
954
+ }
955
+ validateParamCount(postBodyResult, paramResult) {
956
+ const result = { isValid: true, reason: [] };
957
+ const totalRequiredParams = postBodyResult.requiredNum + paramResult.requiredNum;
958
+ const totalParams = totalRequiredParams + postBodyResult.optionalNum + paramResult.optionalNum;
959
+ if (totalRequiredParams > 1) {
960
+ if (!this.options.allowMultipleParameters ||
961
+ totalRequiredParams > SMEValidator.SMERequiredParamsMaxNum) {
962
+ result.reason.push(ErrorType.ExceededRequiredParamsLimit);
963
+ }
964
+ }
965
+ else if (totalParams === 0) {
966
+ result.reason.push(ErrorType.NoParameter);
967
+ }
968
+ return result;
969
+ }
970
+ }
971
+ SMEValidator.SMERequiredParamsMaxNum = 5;
972
+
973
+ // Copyright (c) Microsoft Corporation.
974
+ class TeamsAIValidator extends Validator {
975
+ constructor(spec, options) {
976
+ super();
977
+ this.projectType = ProjectType.TeamsAi;
978
+ this.options = options;
979
+ this.spec = spec;
980
+ }
981
+ validateSpec() {
982
+ const result = { errors: [], warnings: [] };
983
+ // validate spec server
984
+ let validationResult = this.validateSpecServer();
985
+ result.errors.push(...validationResult.errors);
986
+ // validate no supported API
987
+ validationResult = this.validateSpecNoSupportAPI();
988
+ result.errors.push(...validationResult.errors);
989
+ return result;
990
+ }
991
+ validateAPI(method, path) {
992
+ const result = { isValid: true, reason: [] };
993
+ method = method.toLocaleLowerCase();
994
+ // validate method and path
995
+ const methodAndPathResult = this.validateMethodAndPath(method, path);
996
+ if (!methodAndPathResult.isValid) {
997
+ return methodAndPathResult;
998
+ }
999
+ const operationObject = this.spec.paths[path][method];
1000
+ // validate operationId
1001
+ if (!this.options.allowMissingId && !operationObject.operationId) {
1002
+ result.reason.push(ErrorType.MissingOperationId);
1003
+ }
1004
+ // validate server
1005
+ const validateServerResult = this.validateServer(method, path);
1006
+ result.reason.push(...validateServerResult.reason);
1007
+ if (result.reason.length > 0) {
1008
+ result.isValid = false;
1009
+ }
1010
+ return result;
1011
+ }
1012
+ }
1013
+
1014
+ class ValidatorFactory {
1015
+ static create(spec, options) {
1016
+ var _a;
1017
+ const type = (_a = options.projectType) !== null && _a !== void 0 ? _a : ProjectType.SME;
1018
+ switch (type) {
1019
+ case ProjectType.SME:
1020
+ return new SMEValidator(spec, options);
1021
+ case ProjectType.Copilot:
1022
+ return new CopilotValidator(spec, options);
1023
+ case ProjectType.TeamsAi:
1024
+ return new TeamsAIValidator(spec, options);
1025
+ default:
1026
+ throw new Error(`Invalid project type: ${type}`);
1027
+ }
1028
+ }
1029
+ }
1030
+
1031
+ // Copyright (c) Microsoft Corporation.
1032
+ class SpecFilter {
1033
+ static specFilter(filter, unResolveSpec, resolvedSpec, options) {
1034
+ var _a;
1035
+ try {
1036
+ const newSpec = Object.assign({}, unResolveSpec);
1037
+ const newPaths = {};
1038
+ for (const filterItem of filter) {
1039
+ const [method, path] = filterItem.split(" ");
1040
+ const methodName = method.toLowerCase();
1041
+ const pathObj = (_a = resolvedSpec.paths) === null || _a === void 0 ? void 0 : _a[path];
1042
+ if (ConstantString.AllOperationMethods.includes(methodName) &&
1043
+ pathObj &&
1044
+ pathObj[methodName]) {
1045
+ const validator = ValidatorFactory.create(resolvedSpec, options);
1046
+ const validateResult = validator.validateAPI(methodName, path);
1047
+ if (!validateResult.isValid) {
1048
+ continue;
1049
+ }
1050
+ if (!newPaths[path]) {
1051
+ newPaths[path] = Object.assign({}, unResolveSpec.paths[path]);
1052
+ for (const m of ConstantString.AllOperationMethods) {
1053
+ delete newPaths[path][m];
1054
+ }
1055
+ }
1056
+ newPaths[path][methodName] = unResolveSpec.paths[path][methodName];
1057
+ // Add the operationId if missing
1058
+ if (!newPaths[path][methodName].operationId) {
1059
+ newPaths[path][methodName].operationId = `${methodName}${Utils.convertPathToCamelCase(path)}`;
1060
+ }
1061
+ }
1062
+ }
1063
+ newSpec.paths = newPaths;
1064
+ return newSpec;
1065
+ }
1066
+ catch (err) {
1067
+ throw new SpecParserError(err.toString(), ErrorType.FilterSpecFailed);
1068
+ }
1069
+ }
1070
+ }
1071
+
1072
+ // Copyright (c) Microsoft Corporation.
1073
+ class AdaptiveCardGenerator {
1074
+ static generateAdaptiveCard(operationItem) {
1075
+ try {
1076
+ const { json } = Utils.getResponseJson(operationItem);
1077
+ let cardBody = [];
1078
+ let schema = json.schema;
1079
+ let jsonPath = "$";
1080
+ if (schema && Object.keys(schema).length > 0) {
1081
+ jsonPath = AdaptiveCardGenerator.getResponseJsonPathFromSchema(schema);
1082
+ if (jsonPath !== "$") {
1083
+ schema = schema.properties[jsonPath];
1084
+ }
1085
+ cardBody = AdaptiveCardGenerator.generateCardFromResponse(schema, "");
1086
+ }
1087
+ // if no schema, try to use example value
1088
+ if (cardBody.length === 0 && (json.examples || json.example)) {
1089
+ cardBody = [
1090
+ {
1091
+ type: ConstantString.TextBlockType,
1092
+ text: "${jsonStringify($root)}",
1093
+ wrap: true,
1094
+ },
1095
+ ];
1096
+ }
1097
+ // if no example value, use default success response
1098
+ if (cardBody.length === 0) {
1099
+ cardBody = [
1100
+ {
1101
+ type: ConstantString.TextBlockType,
1102
+ text: "success",
1103
+ wrap: true,
1104
+ },
1105
+ ];
1106
+ }
1107
+ const fullCard = {
1108
+ type: ConstantString.AdaptiveCardType,
1109
+ $schema: ConstantString.AdaptiveCardSchema,
1110
+ version: ConstantString.AdaptiveCardVersion,
1111
+ body: cardBody,
1112
+ };
1113
+ return [fullCard, jsonPath];
1114
+ }
1115
+ catch (err) {
1116
+ throw new SpecParserError(err.toString(), ErrorType.GenerateAdaptiveCardFailed);
1117
+ }
1118
+ }
1119
+ static generateCardFromResponse(schema, name, parentArrayName = "") {
1120
+ if (schema.type === "array") {
1121
+ // schema.items can be arbitrary object: schema { type: array, items: {} }
1122
+ if (Object.keys(schema.items).length === 0) {
1123
+ return [
1124
+ {
1125
+ type: ConstantString.TextBlockType,
1126
+ text: name ? `${name}: \${jsonStringify(${name})}` : "result: ${jsonStringify($root)}",
1127
+ wrap: true,
1128
+ },
1129
+ ];
1130
+ }
1131
+ const obj = AdaptiveCardGenerator.generateCardFromResponse(schema.items, "", name);
1132
+ const template = {
1133
+ type: ConstantString.ContainerType,
1134
+ $data: name ? `\${${name}}` : "${$root}",
1135
+ items: Array(),
1136
+ };
1137
+ template.items.push(...obj);
1138
+ return [template];
1139
+ }
1140
+ // some schema may not contain type but contain properties
1141
+ if (schema.type === "object" || (!schema.type && schema.properties)) {
1142
+ const { properties } = schema;
1143
+ const result = [];
1144
+ for (const property in properties) {
1145
+ const obj = AdaptiveCardGenerator.generateCardFromResponse(properties[property], name ? `${name}.${property}` : property, parentArrayName);
1146
+ result.push(...obj);
1147
+ }
1148
+ if (schema.additionalProperties) {
1149
+ // TODO: better ways to handler warnings.
1150
+ console.warn(ConstantString.AdditionalPropertiesNotSupported);
1151
+ }
1152
+ return result;
1153
+ }
1154
+ if (schema.type === "string" ||
1155
+ schema.type === "integer" ||
1156
+ schema.type === "boolean" ||
1157
+ schema.type === "number") {
1158
+ if (!AdaptiveCardGenerator.isImageUrlProperty(schema, name, parentArrayName)) {
1159
+ // string in root: "ddd"
1160
+ let text = "result: ${$root}";
1161
+ if (name) {
1162
+ // object { id: "1" }
1163
+ text = `${name}: \${if(${name}, ${name}, 'N/A')}`;
1164
+ if (parentArrayName) {
1165
+ // object types inside array: { tags: ["id": 1, "name": "name"] }
1166
+ text = `${parentArrayName}.${text}`;
1167
+ }
1168
+ }
1169
+ else if (parentArrayName) {
1170
+ // string array: photoUrls: ["1", "2"]
1171
+ text = `${parentArrayName}: ` + "${$data}";
1172
+ }
1173
+ return [
1174
+ {
1175
+ type: ConstantString.TextBlockType,
1176
+ text,
1177
+ wrap: true,
1178
+ },
1179
+ ];
1180
+ }
1181
+ else {
1182
+ if (name) {
1183
+ return [
1184
+ {
1185
+ type: "Image",
1186
+ url: `\${${name}}`,
1187
+ $when: `\${${name} != null}`,
1188
+ },
1189
+ ];
1190
+ }
1191
+ else {
1192
+ return [
1193
+ {
1194
+ type: "Image",
1195
+ url: "${$data}",
1196
+ $when: "${$data != null}",
1197
+ },
1198
+ ];
1199
+ }
1200
+ }
1201
+ }
1202
+ if (schema.oneOf || schema.anyOf || schema.not || schema.allOf) {
1203
+ throw new Error(Utils.format(ConstantString.SchemaNotSupported, JSON.stringify(schema)));
1204
+ }
1205
+ throw new Error(Utils.format(ConstantString.UnknownSchema, JSON.stringify(schema)));
1206
+ }
1207
+ // Find the first array property in the response schema object with the well-known name
1208
+ static getResponseJsonPathFromSchema(schema) {
1209
+ if (schema.type === "object" || (!schema.type && schema.properties)) {
1210
+ const { properties } = schema;
1211
+ for (const property in properties) {
1212
+ const schema = properties[property];
1213
+ if (schema.type === "array" &&
1214
+ Utils.isWellKnownName(property, ConstantString.WellknownResultNames)) {
1215
+ return property;
1216
+ }
1217
+ }
1218
+ }
1219
+ return "$";
1220
+ }
1221
+ static isImageUrlProperty(schema, name, parentArrayName) {
1222
+ const propertyName = name ? name : parentArrayName;
1223
+ return (!!propertyName &&
1224
+ schema.type === "string" &&
1225
+ Utils.isWellKnownName(propertyName, ConstantString.WellknownImageName) &&
1226
+ (propertyName.toLocaleLowerCase().indexOf("url") >= 0 || schema.format === "uri"));
1227
+ }
1228
+ }
1229
+
1230
+ // Copyright (c) Microsoft Corporation.
1231
+ function wrapAdaptiveCard(card, jsonPath) {
1232
+ const result = {
1233
+ version: ConstantString.WrappedCardVersion,
1234
+ $schema: ConstantString.WrappedCardSchema,
1235
+ jsonPath: jsonPath,
1236
+ responseLayout: ConstantString.WrappedCardResponseLayout,
1237
+ responseCardTemplate: card,
1238
+ previewCardTemplate: inferPreviewCardTemplate(card),
1239
+ };
1240
+ return result;
1241
+ }
1242
+ function wrapResponseSemantics(card, jsonPath) {
1243
+ const props = inferProperties(card);
1244
+ const dataPath = jsonPath === "$" ? "$" : "$." + jsonPath;
1245
+ const result = {
1246
+ data_path: dataPath,
1247
+ };
1248
+ if (props.title || props.subtitle || props.imageUrl) {
1249
+ result.properties = {};
1250
+ if (props.title) {
1251
+ result.properties.title = "$." + props.title;
1252
+ }
1253
+ if (props.subtitle) {
1254
+ result.properties.subtitle = "$." + props.subtitle;
1255
+ }
1256
+ if (props.imageUrl) {
1257
+ result.properties.url = "$." + props.imageUrl;
1258
+ }
1259
+ }
1260
+ result.static_template = card;
1261
+ return result;
1262
+ }
1263
+ /**
1264
+ * Infers the preview card template from an Adaptive Card and a JSON path.
1265
+ * The preview card template includes a title and an optional subtitle and image.
1266
+ * It populates the preview card template with the first text block that matches
1267
+ * each well-known name, in the order of title, subtitle, and image.
1268
+ * If no text block matches the title or subtitle, it uses the first two text block as the title and subtitle.
1269
+ * If the title is still empty and the subtitle is not empty, it uses subtitle as the title.
1270
+ * @param card The Adaptive Card to infer the preview card template from.
1271
+ * @param jsonPath The JSON path to the root object in the card body.
1272
+ * @returns The inferred preview card template.
1273
+ */
1274
+ function inferPreviewCardTemplate(card) {
1275
+ const result = {
1276
+ title: "result",
1277
+ };
1278
+ const inferredProperties = inferProperties(card);
1279
+ if (inferredProperties.title) {
1280
+ result.title = `\${if(${inferredProperties.title}, ${inferredProperties.title}, 'N/A')}`;
1281
+ }
1282
+ if (inferredProperties.subtitle) {
1283
+ result.subtitle = `\${if(${inferredProperties.subtitle}, ${inferredProperties.subtitle}, 'N/A')}`;
1284
+ }
1285
+ if (inferredProperties.imageUrl) {
1286
+ result.image = {
1287
+ url: `\${${inferredProperties.imageUrl}}`,
1288
+ alt: `\${if(${inferredProperties.imageUrl}, ${inferredProperties.imageUrl}, 'N/A')}`,
1289
+ $when: `\${${inferredProperties.imageUrl} != null}`,
1290
+ };
1291
+ }
1292
+ return result;
1293
+ }
1294
+ function inferProperties(card) {
1295
+ var _a;
1296
+ const result = {};
1297
+ const nameSet = new Set();
1298
+ let rootObject;
1299
+ if (((_a = card.body[0]) === null || _a === void 0 ? void 0 : _a.type) === ConstantString.ContainerType) {
1300
+ rootObject = card.body[0].items;
1301
+ }
1302
+ else {
1303
+ rootObject = card.body;
1304
+ }
1305
+ for (const element of rootObject) {
1306
+ if (element.type === ConstantString.TextBlockType) {
1307
+ const textElement = element;
1308
+ const index = textElement.text.indexOf("${if(");
1309
+ if (index > 0) {
1310
+ const text = textElement.text.substring(index);
1311
+ const match = text.match(/\${if\(([^,]+),/);
1312
+ const property = match ? match[1] : "";
1313
+ if (property) {
1314
+ nameSet.add(property);
1315
+ }
1316
+ }
1317
+ }
1318
+ else if (element.type === ConstantString.ImageType) {
1319
+ const imageElement = element;
1320
+ const match = imageElement.url.match(/\${([^,]+)}/);
1321
+ const property = match ? match[1] : "";
1322
+ if (property) {
1323
+ nameSet.add(property);
1324
+ }
1325
+ }
1326
+ }
1327
+ for (const name of nameSet) {
1328
+ if (!result.title && Utils.isWellKnownName(name, ConstantString.WellknownTitleName)) {
1329
+ result.title = name;
1330
+ nameSet.delete(name);
1331
+ }
1332
+ else if (!result.subtitle &&
1333
+ Utils.isWellKnownName(name, ConstantString.WellknownSubtitleName)) {
1334
+ result.subtitle = name;
1335
+ nameSet.delete(name);
1336
+ }
1337
+ else if (!result.imageUrl && Utils.isWellKnownName(name, ConstantString.WellknownImageName)) {
1338
+ result.imageUrl = name;
1339
+ nameSet.delete(name);
1340
+ }
1341
+ }
1342
+ for (const name of nameSet) {
1343
+ if (!result.title) {
1344
+ result.title = name;
1345
+ nameSet.delete(name);
1346
+ }
1347
+ else if (!result.subtitle) {
1348
+ result.subtitle = name;
1349
+ nameSet.delete(name);
1350
+ }
1351
+ }
1352
+ if (!result.title && result.subtitle) {
1353
+ result.title = result.subtitle;
1354
+ delete result.subtitle;
1355
+ }
1356
+ return result;
1357
+ }
1358
+
1359
+ // Copyright (c) Microsoft Corporation.
1360
+ class ManifestUpdater {
1361
+ static async updateManifestWithAiPlugin(manifestPath, outputSpecPath, apiPluginFilePath, spec, options, authInfo) {
1362
+ const manifest = await fs.readJSON(manifestPath);
1363
+ const apiPluginRelativePath = ManifestUpdater.getRelativePath(manifestPath, apiPluginFilePath);
1364
+ manifest.plugins = [
1365
+ {
1366
+ file: apiPluginRelativePath,
1367
+ id: ConstantString.DefaultPluginId,
1368
+ },
1369
+ ];
1370
+ const appName = this.removeEnvs(manifest.name.short);
1371
+ ManifestUpdater.updateManifestDescription(manifest, spec);
1372
+ const specRelativePath = ManifestUpdater.getRelativePath(manifestPath, outputSpecPath);
1373
+ const apiPlugin = await ManifestUpdater.generatePluginManifestSchema(spec, specRelativePath, apiPluginFilePath, appName, authInfo, options);
1374
+ return [manifest, apiPlugin];
806
1375
  }
807
- }
808
-
809
- // Copyright (c) Microsoft Corporation.
810
- class ManifestUpdater {
811
- static async updateManifestWithAiPlugin(manifestPath, outputSpecPath, apiPluginFilePath, spec, options) {
812
- const manifest = await fs.readJSON(manifestPath);
813
- const apiPluginRelativePath = ManifestUpdater.getRelativePath(manifestPath, apiPluginFilePath);
814
- manifest.plugins = [
815
- {
816
- pluginFile: apiPluginRelativePath,
817
- },
818
- ];
819
- ManifestUpdater.updateManifestDescription(manifest, spec);
820
- const specRelativePath = ManifestUpdater.getRelativePath(manifestPath, outputSpecPath);
821
- const apiPlugin = ManifestUpdater.generatePluginManifestSchema(spec, specRelativePath, options);
822
- return [manifest, apiPlugin];
823
- }
824
1376
  static updateManifestDescription(manifest, spec) {
825
1377
  var _a, _b;
826
1378
  manifest.description = {
@@ -830,23 +1382,55 @@ class ManifestUpdater {
830
1382
  }
831
1383
  static mapOpenAPISchemaToFuncParam(schema, method, pathUrl) {
832
1384
  let parameter;
833
- if (schema.type === "string" ||
1385
+ if (schema.type === "array") {
1386
+ const items = schema.items;
1387
+ parameter = {
1388
+ type: "array",
1389
+ items: ManifestUpdater.mapOpenAPISchemaToFuncParam(items, method, pathUrl),
1390
+ };
1391
+ }
1392
+ else if (schema.type === "string" ||
834
1393
  schema.type === "boolean" ||
835
1394
  schema.type === "integer" ||
836
- schema.type === "number" ||
837
- schema.type === "array") {
838
- parameter = schema;
1395
+ schema.type === "number") {
1396
+ parameter = {
1397
+ type: schema.type,
1398
+ };
839
1399
  }
840
1400
  else {
841
1401
  throw new SpecParserError(Utils.format(ConstantString.UnsupportedSchema, method, pathUrl, JSON.stringify(schema)), ErrorType.UpdateManifestFailed);
842
1402
  }
1403
+ if (schema.enum) {
1404
+ parameter.enum = schema.enum;
1405
+ }
1406
+ if (schema.description) {
1407
+ parameter.description = schema.description;
1408
+ }
1409
+ if (schema.default) {
1410
+ parameter.default = schema.default;
1411
+ }
843
1412
  return parameter;
844
1413
  }
845
- static generatePluginManifestSchema(spec, specRelativePath, options) {
846
- var _a, _b, _c;
1414
+ static async generatePluginManifestSchema(spec, specRelativePath, apiPluginFilePath, appName, authInfo, options) {
1415
+ var _a, _b, _c, _d, _e;
847
1416
  const functions = [];
848
1417
  const functionNames = [];
1418
+ const conversationStarters = [];
849
1419
  const paths = spec.paths;
1420
+ const pluginAuthObj = {
1421
+ type: "None",
1422
+ };
1423
+ if (authInfo) {
1424
+ if (Utils.isOAuthWithAuthCodeFlow(authInfo.authScheme)) {
1425
+ pluginAuthObj.type = "OAuthPluginVault";
1426
+ }
1427
+ else if (Utils.isBearerTokenAuth(authInfo.authScheme)) {
1428
+ pluginAuthObj.type = "ApiKeyPluginVault";
1429
+ }
1430
+ if (pluginAuthObj.type !== "None") {
1431
+ pluginAuthObj.reference_id = `${Utils.getSafeRegistrationIdEnvName(authInfo.name)}_REGISTRATION_ID`;
1432
+ }
1433
+ }
850
1434
  for (const pathUrl in paths) {
851
1435
  const pathItem = paths[pathUrl];
852
1436
  if (pathItem) {
@@ -854,6 +1438,7 @@ class ManifestUpdater {
854
1438
  for (const method in operations) {
855
1439
  if (options.allowMethods.includes(method)) {
856
1440
  const operationItem = operations[method];
1441
+ const confirmationBodies = [];
857
1442
  if (operationItem) {
858
1443
  const operationId = operationItem.operationId;
859
1444
  const description = (_a = operationItem.description) !== null && _a !== void 0 ? _a : "";
@@ -869,6 +1454,7 @@ class ManifestUpdater {
869
1454
  const param = paramObject[i];
870
1455
  const schema = param.schema;
871
1456
  parameters.properties[param.name] = ManifestUpdater.mapOpenAPISchemaToFuncParam(schema, method, pathUrl);
1457
+ confirmationBodies.push(ManifestUpdater.getConfirmationBodyItem(param.name));
872
1458
  if (param.required) {
873
1459
  parameters.required.push(param.name);
874
1460
  }
@@ -887,6 +1473,7 @@ class ManifestUpdater {
887
1473
  for (const property in requestBodySchema.properties) {
888
1474
  const schema = requestBodySchema.properties[property];
889
1475
  parameters.properties[property] = ManifestUpdater.mapOpenAPISchemaToFuncParam(schema, method, pathUrl);
1476
+ confirmationBodies.push(ManifestUpdater.getConfirmationBodyItem(property));
890
1477
  }
891
1478
  }
892
1479
  else {
@@ -896,33 +1483,105 @@ class ManifestUpdater {
896
1483
  const funcObj = {
897
1484
  name: operationId,
898
1485
  description: description,
899
- parameters: parameters,
900
1486
  };
1487
+ if (paramObject || requestBody) {
1488
+ funcObj.parameters = parameters;
1489
+ }
1490
+ if (options.allowResponseSemantics) {
1491
+ const [card, jsonPath] = AdaptiveCardGenerator.generateAdaptiveCard(operationItem);
1492
+ const responseSemantic = wrapResponseSemantics(card, jsonPath);
1493
+ funcObj.capabilities = {
1494
+ response_semantics: responseSemantic,
1495
+ };
1496
+ }
1497
+ if (options.allowConfirmation && method !== ConstantString.GetMethod) {
1498
+ if (!funcObj.capabilities) {
1499
+ funcObj.capabilities = {};
1500
+ }
1501
+ funcObj.capabilities.confirmation = {
1502
+ type: "AdaptiveCard",
1503
+ title: (_c = operationItem.summary) !== null && _c !== void 0 ? _c : description,
1504
+ };
1505
+ if (confirmationBodies.length > 0) {
1506
+ funcObj.capabilities.confirmation.body = confirmationBodies.join("\n");
1507
+ }
1508
+ }
901
1509
  functions.push(funcObj);
902
1510
  functionNames.push(operationId);
1511
+ if (description) {
1512
+ conversationStarters.push(description);
1513
+ }
903
1514
  }
904
1515
  }
905
1516
  }
906
1517
  }
907
1518
  }
908
- const apiPlugin = {
909
- schema_version: "v2",
910
- name_for_human: spec.info.title,
911
- description_for_human: (_c = spec.info.description) !== null && _c !== void 0 ? _c : "<Please add description of the plugin>",
912
- functions: functions,
913
- runtimes: [
914
- {
915
- type: "OpenApi",
916
- auth: {
917
- type: "none", // TODO, support auth in the future
918
- },
919
- spec: {
920
- url: specRelativePath,
921
- },
922
- run_for_functions: functionNames,
1519
+ let apiPlugin;
1520
+ if (await fs.pathExists(apiPluginFilePath)) {
1521
+ apiPlugin = await fs.readJSON(apiPluginFilePath);
1522
+ }
1523
+ else {
1524
+ apiPlugin = {
1525
+ schema_version: "v2.1",
1526
+ name_for_human: "",
1527
+ description_for_human: "",
1528
+ namespace: "",
1529
+ functions: [],
1530
+ runtimes: [],
1531
+ };
1532
+ }
1533
+ apiPlugin.functions = apiPlugin.functions || [];
1534
+ for (const func of functions) {
1535
+ const index = (_d = apiPlugin.functions) === null || _d === void 0 ? void 0 : _d.findIndex((f) => f.name === func.name);
1536
+ if (index === -1) {
1537
+ apiPlugin.functions.push(func);
1538
+ }
1539
+ else {
1540
+ apiPlugin.functions[index] = func;
1541
+ }
1542
+ }
1543
+ apiPlugin.runtimes = apiPlugin.runtimes || [];
1544
+ const index = apiPlugin.runtimes.findIndex((runtime) => {
1545
+ var _a, _b;
1546
+ return runtime.spec.url === specRelativePath &&
1547
+ runtime.type === "OpenApi" &&
1548
+ ((_b = (_a = runtime.auth) === null || _a === void 0 ? void 0 : _a.type) !== null && _b !== void 0 ? _b : "None") === pluginAuthObj.type;
1549
+ });
1550
+ if (index === -1) {
1551
+ apiPlugin.runtimes.push({
1552
+ type: "OpenApi",
1553
+ auth: pluginAuthObj,
1554
+ spec: {
1555
+ url: specRelativePath,
923
1556
  },
924
- ],
925
- };
1557
+ run_for_functions: functionNames,
1558
+ });
1559
+ }
1560
+ else {
1561
+ apiPlugin.runtimes[index].run_for_functions = functionNames;
1562
+ }
1563
+ if (!apiPlugin.name_for_human) {
1564
+ apiPlugin.name_for_human = appName;
1565
+ }
1566
+ if (!apiPlugin.namespace) {
1567
+ apiPlugin.namespace = ManifestUpdater.removeAllSpecialCharacters(appName);
1568
+ }
1569
+ if (!apiPlugin.description_for_human) {
1570
+ apiPlugin.description_for_human =
1571
+ (_e = spec.info.description) !== null && _e !== void 0 ? _e : "<Please add description of the plugin>";
1572
+ }
1573
+ if (options.allowConversationStarters && conversationStarters.length > 0) {
1574
+ if (!apiPlugin.capabilities) {
1575
+ apiPlugin.capabilities = {
1576
+ localization: {},
1577
+ };
1578
+ }
1579
+ if (!apiPlugin.capabilities.conversation_starters) {
1580
+ apiPlugin.capabilities.conversation_starters = conversationStarters
1581
+ .slice(0, 5)
1582
+ .map((text) => ({ text }));
1583
+ }
1584
+ }
926
1585
  return apiPlugin;
927
1586
  }
928
1587
  static async updateManifest(manifestPath, outputSpecPath, spec, options, adaptiveCardFolder, authInfo) {
@@ -991,16 +1650,26 @@ class ManifestUpdater {
991
1650
  if ((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) {
992
1651
  const operationItem = operations[method];
993
1652
  if (operationItem) {
994
- const [command, warning] = Utils.parseApiInfo(operationItem, options);
1653
+ const command = Utils.parseApiInfo(operationItem, options);
1654
+ if (command.parameters &&
1655
+ command.parameters.length >= 1 &&
1656
+ command.parameters.some((param) => param.isRequired)) {
1657
+ command.parameters = command.parameters.filter((param) => param.isRequired);
1658
+ }
1659
+ else if (command.parameters && command.parameters.length > 0) {
1660
+ command.parameters = [command.parameters[0]];
1661
+ warnings.push({
1662
+ type: WarningType.OperationOnlyContainsOptionalParam,
1663
+ content: Utils.format(ConstantString.OperationOnlyContainsOptionalParam, command.id),
1664
+ data: command.id,
1665
+ });
1666
+ }
995
1667
  if (adaptiveCardFolder) {
996
1668
  const adaptiveCardPath = path.join(adaptiveCardFolder, command.id + ".json");
997
1669
  command.apiResponseRenderingTemplateFile = (await fs.pathExists(adaptiveCardPath))
998
1670
  ? ManifestUpdater.getRelativePath(manifestPath, adaptiveCardPath)
999
1671
  : "";
1000
1672
  }
1001
- if (warning) {
1002
- warnings.push(warning);
1003
- }
1004
1673
  commands.push(command);
1005
1674
  }
1006
1675
  }
@@ -1014,255 +1683,21 @@ class ManifestUpdater {
1014
1683
  const relativePath = path.relative(path.dirname(from), to);
1015
1684
  return path.normalize(relativePath).replace(/\\/g, "/");
1016
1685
  }
1017
- }
1018
-
1019
- // Copyright (c) Microsoft Corporation.
1020
- class AdaptiveCardGenerator {
1021
- static generateAdaptiveCard(operationItem) {
1022
- try {
1023
- const json = Utils.getResponseJson(operationItem);
1024
- let cardBody = [];
1025
- let schema = json.schema;
1026
- let jsonPath = "$";
1027
- if (schema && Object.keys(schema).length > 0) {
1028
- jsonPath = AdaptiveCardGenerator.getResponseJsonPathFromSchema(schema);
1029
- if (jsonPath !== "$") {
1030
- schema = schema.properties[jsonPath];
1031
- }
1032
- cardBody = AdaptiveCardGenerator.generateCardFromResponse(schema, "");
1033
- }
1034
- // if no schema, try to use example value
1035
- if (cardBody.length === 0 && (json.examples || json.example)) {
1036
- cardBody = [
1037
- {
1038
- type: ConstantString.TextBlockType,
1039
- text: "${jsonStringify($root)}",
1040
- wrap: true,
1041
- },
1042
- ];
1043
- }
1044
- // if no example value, use default success response
1045
- if (cardBody.length === 0) {
1046
- cardBody = [
1047
- {
1048
- type: ConstantString.TextBlockType,
1049
- text: "success",
1050
- wrap: true,
1051
- },
1052
- ];
1053
- }
1054
- const fullCard = {
1055
- type: ConstantString.AdaptiveCardType,
1056
- $schema: ConstantString.AdaptiveCardSchema,
1057
- version: ConstantString.AdaptiveCardVersion,
1058
- body: cardBody,
1059
- };
1060
- return [fullCard, jsonPath];
1061
- }
1062
- catch (err) {
1063
- throw new SpecParserError(err.toString(), ErrorType.GenerateAdaptiveCardFailed);
1064
- }
1065
- }
1066
- static generateCardFromResponse(schema, name, parentArrayName = "") {
1067
- if (schema.type === "array") {
1068
- // schema.items can be arbitrary object: schema { type: array, items: {} }
1069
- if (Object.keys(schema.items).length === 0) {
1070
- return [
1071
- {
1072
- type: ConstantString.TextBlockType,
1073
- text: name ? `${name}: \${jsonStringify(${name})}` : "result: ${jsonStringify($root)}",
1074
- wrap: true,
1075
- },
1076
- ];
1077
- }
1078
- const obj = AdaptiveCardGenerator.generateCardFromResponse(schema.items, "", name);
1079
- const template = {
1080
- type: ConstantString.ContainerType,
1081
- $data: name ? `\${${name}}` : "${$root}",
1082
- items: Array(),
1083
- };
1084
- template.items.push(...obj);
1085
- return [template];
1086
- }
1087
- // some schema may not contain type but contain properties
1088
- if (schema.type === "object" || (!schema.type && schema.properties)) {
1089
- const { properties } = schema;
1090
- const result = [];
1091
- for (const property in properties) {
1092
- const obj = AdaptiveCardGenerator.generateCardFromResponse(properties[property], name ? `${name}.${property}` : property, parentArrayName);
1093
- result.push(...obj);
1094
- }
1095
- if (schema.additionalProperties) {
1096
- // TODO: better ways to handler warnings.
1097
- console.warn(ConstantString.AdditionalPropertiesNotSupported);
1098
- }
1099
- return result;
1100
- }
1101
- if (schema.type === "string" ||
1102
- schema.type === "integer" ||
1103
- schema.type === "boolean" ||
1104
- schema.type === "number") {
1105
- if (!AdaptiveCardGenerator.isImageUrlProperty(schema, name, parentArrayName)) {
1106
- // string in root: "ddd"
1107
- let text = "result: ${$root}";
1108
- if (name) {
1109
- // object { id: "1" }
1110
- text = `${name}: \${if(${name}, ${name}, 'N/A')}`;
1111
- if (parentArrayName) {
1112
- // object types inside array: { tags: ["id": 1, "name": "name"] }
1113
- text = `${parentArrayName}.${text}`;
1114
- }
1115
- }
1116
- else if (parentArrayName) {
1117
- // string array: photoUrls: ["1", "2"]
1118
- text = `${parentArrayName}: ` + "${$data}";
1119
- }
1120
- return [
1121
- {
1122
- type: ConstantString.TextBlockType,
1123
- text,
1124
- wrap: true,
1125
- },
1126
- ];
1127
- }
1128
- else {
1129
- if (name) {
1130
- return [
1131
- {
1132
- type: "Image",
1133
- url: `\${${name}}`,
1134
- $when: `\${${name} != null}`,
1135
- },
1136
- ];
1137
- }
1138
- else {
1139
- return [
1140
- {
1141
- type: "Image",
1142
- url: "${$data}",
1143
- $when: "${$data != null}",
1144
- },
1145
- ];
1146
- }
1147
- }
1148
- }
1149
- if (schema.oneOf || schema.anyOf || schema.not || schema.allOf) {
1150
- throw new Error(Utils.format(ConstantString.SchemaNotSupported, JSON.stringify(schema)));
1151
- }
1152
- throw new Error(Utils.format(ConstantString.UnknownSchema, JSON.stringify(schema)));
1153
- }
1154
- // Find the first array property in the response schema object with the well-known name
1155
- static getResponseJsonPathFromSchema(schema) {
1156
- if (schema.type === "object" || (!schema.type && schema.properties)) {
1157
- const { properties } = schema;
1158
- for (const property in properties) {
1159
- const schema = properties[property];
1160
- if (schema.type === "array" &&
1161
- Utils.isWellKnownName(property, ConstantString.WellknownResultNames)) {
1162
- return property;
1163
- }
1164
- }
1165
- }
1166
- return "$";
1167
- }
1168
- static isImageUrlProperty(schema, name, parentArrayName) {
1169
- const propertyName = name ? name : parentArrayName;
1170
- return (!!propertyName &&
1171
- schema.type === "string" &&
1172
- Utils.isWellKnownName(propertyName, ConstantString.WellknownImageName) &&
1173
- (propertyName.toLocaleLowerCase().indexOf("url") >= 0 || schema.format === "uri"));
1174
- }
1175
- }
1176
-
1177
- // Copyright (c) Microsoft Corporation.
1178
- function wrapAdaptiveCard(card, jsonPath) {
1179
- const result = {
1180
- version: ConstantString.WrappedCardVersion,
1181
- $schema: ConstantString.WrappedCardSchema,
1182
- jsonPath: jsonPath,
1183
- responseLayout: ConstantString.WrappedCardResponseLayout,
1184
- responseCardTemplate: card,
1185
- previewCardTemplate: inferPreviewCardTemplate(card),
1186
- };
1187
- return result;
1188
- }
1189
- /**
1190
- * Infers the preview card template from an Adaptive Card and a JSON path.
1191
- * The preview card template includes a title and an optional subtitle and image.
1192
- * It populates the preview card template with the first text block that matches
1193
- * each well-known name, in the order of title, subtitle, and image.
1194
- * If no text block matches the title or subtitle, it uses the first two text block as the title and subtitle.
1195
- * If the title is still empty and the subtitle is not empty, it uses subtitle as the title.
1196
- * @param card The Adaptive Card to infer the preview card template from.
1197
- * @param jsonPath The JSON path to the root object in the card body.
1198
- * @returns The inferred preview card template.
1199
- */
1200
- function inferPreviewCardTemplate(card) {
1201
- var _a;
1202
- const result = {
1203
- title: "",
1204
- };
1205
- const textBlockElements = new Set();
1206
- let rootObject;
1207
- if (((_a = card.body[0]) === null || _a === void 0 ? void 0 : _a.type) === ConstantString.ContainerType) {
1208
- rootObject = card.body[0].items;
1209
- }
1210
- else {
1211
- rootObject = card.body;
1212
- }
1213
- for (const element of rootObject) {
1214
- if (element.type === ConstantString.TextBlockType) {
1215
- const textElement = element;
1216
- const index = textElement.text.indexOf("${if(");
1217
- if (index > 0) {
1218
- textElement.text = textElement.text.substring(index);
1219
- textBlockElements.add(textElement);
1220
- }
1221
- }
1222
- }
1223
- for (const element of textBlockElements) {
1224
- const text = element.text;
1225
- if (!result.title && Utils.isWellKnownName(text, ConstantString.WellknownTitleName)) {
1226
- result.title = text;
1227
- textBlockElements.delete(element);
1228
- }
1229
- else if (!result.subtitle &&
1230
- Utils.isWellKnownName(text, ConstantString.WellknownSubtitleName)) {
1231
- result.subtitle = text;
1232
- textBlockElements.delete(element);
1233
- }
1234
- else if (!result.image && Utils.isWellKnownName(text, ConstantString.WellknownImageName)) {
1235
- const match = text.match(/\${if\(([^,]+),/);
1236
- const property = match ? match[1] : "";
1237
- if (property) {
1238
- result.image = {
1239
- url: `\${${property}}`,
1240
- alt: text,
1241
- $when: `\${${property} != null}`,
1242
- };
1243
- }
1244
- textBlockElements.delete(element);
1245
- }
1246
- }
1247
- for (const element of textBlockElements) {
1248
- const text = element.text;
1249
- if (!result.title) {
1250
- result.title = text;
1251
- textBlockElements.delete(element);
1252
- }
1253
- else if (!result.subtitle) {
1254
- result.subtitle = text;
1255
- textBlockElements.delete(element);
1686
+ static removeEnvs(str) {
1687
+ const placeHolderReg = /\${{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*}}/g;
1688
+ const matches = placeHolderReg.exec(str);
1689
+ let newStr = str;
1690
+ if (matches != null) {
1691
+ newStr = newStr.replace(matches[0], "");
1256
1692
  }
1693
+ return newStr;
1257
1694
  }
1258
- if (!result.title && result.subtitle) {
1259
- result.title = result.subtitle;
1260
- delete result.subtitle;
1695
+ static removeAllSpecialCharacters(str) {
1696
+ return str.toLowerCase().replace(/[^a-z0-9]/g, "");
1261
1697
  }
1262
- if (!result.title) {
1263
- result.title = "result";
1698
+ static getConfirmationBodyItem(paramName) {
1699
+ return `* **${Utils.updateFirstLetter(paramName)}**: {{function.parameters.${paramName}}}`;
1264
1700
  }
1265
- return result;
1266
1701
  }
1267
1702
 
1268
1703
  // Copyright (c) Microsoft Corporation.
@@ -1284,6 +1719,9 @@ class SpecParser {
1284
1719
  allowMultipleParameters: false,
1285
1720
  allowOauth2: false,
1286
1721
  allowMethods: ["get", "post"],
1722
+ allowConversationStarters: false,
1723
+ allowResponseSemantics: false,
1724
+ allowConfirmation: false,
1287
1725
  projectType: ProjectType.SME,
1288
1726
  };
1289
1727
  this.pathOrSpec = pathOrDoc;
@@ -1308,6 +1746,8 @@ class SpecParser {
1308
1746
  errors: [{ type: ErrorType.SpecNotValid, content: e.toString() }],
1309
1747
  };
1310
1748
  }
1749
+ const errors = [];
1750
+ const warnings = [];
1311
1751
  if (!this.options.allowSwagger && this.isSwaggerFile) {
1312
1752
  return {
1313
1753
  status: ValidationStatus.Error,
@@ -1317,7 +1757,38 @@ class SpecParser {
1317
1757
  ],
1318
1758
  };
1319
1759
  }
1320
- return Utils.validateSpec(this.spec, this.parser, !!this.isSwaggerFile, this.options);
1760
+ // Remote reference not supported
1761
+ const refPaths = this.parser.$refs.paths();
1762
+ // refPaths [0] is the current spec file path
1763
+ if (refPaths.length > 1) {
1764
+ errors.push({
1765
+ type: ErrorType.RemoteRefNotSupported,
1766
+ content: Utils.format(ConstantString.RemoteRefNotSupported, refPaths.join(", ")),
1767
+ data: refPaths,
1768
+ });
1769
+ }
1770
+ if (!!this.isSwaggerFile && this.options.allowSwagger) {
1771
+ warnings.push({
1772
+ type: WarningType.ConvertSwaggerToOpenAPI,
1773
+ content: ConstantString.ConvertSwaggerToOpenAPI,
1774
+ });
1775
+ }
1776
+ const validator = this.getValidator(this.spec);
1777
+ const validationResult = validator.validateSpec();
1778
+ warnings.push(...validationResult.warnings);
1779
+ errors.push(...validationResult.errors);
1780
+ let status = ValidationStatus.Valid;
1781
+ if (warnings.length > 0 && errors.length === 0) {
1782
+ status = ValidationStatus.Warning;
1783
+ }
1784
+ else if (errors.length > 0) {
1785
+ status = ValidationStatus.Error;
1786
+ }
1787
+ return {
1788
+ status: status,
1789
+ warnings: warnings,
1790
+ errors: errors,
1791
+ };
1321
1792
  }
1322
1793
  catch (err) {
1323
1794
  throw new SpecParserError(err.toString(), ErrorType.ValidateFailed);
@@ -1337,39 +1808,40 @@ class SpecParser {
1337
1808
  try {
1338
1809
  await this.loadSpec();
1339
1810
  const spec = this.spec;
1340
- const apiMap = this.getAllSupportedAPIs(spec);
1341
- const result = [];
1811
+ const apiMap = this.getAPIs(spec);
1812
+ const result = {
1813
+ APIs: [],
1814
+ allAPICount: 0,
1815
+ validAPICount: 0,
1816
+ };
1342
1817
  for (const apiKey in apiMap) {
1818
+ const { operation, isValid, reason } = apiMap[apiKey];
1819
+ const [method, path] = apiKey.split(" ");
1820
+ const operationId = (_a = operation.operationId) !== null && _a !== void 0 ? _a : `${method.toLowerCase()}${Utils.convertPathToCamelCase(path)}`;
1343
1821
  const apiResult = {
1344
- api: "",
1822
+ api: apiKey,
1345
1823
  server: "",
1346
- operationId: "",
1824
+ operationId: operationId,
1825
+ isValid: isValid,
1826
+ reason: reason,
1347
1827
  };
1348
- const [method, path] = apiKey.split(" ");
1349
- const operation = apiMap[apiKey];
1350
- const rootServer = spec.servers && spec.servers[0];
1351
- const methodServer = spec.paths[path].servers && ((_a = spec.paths[path]) === null || _a === void 0 ? void 0 : _a.servers[0]);
1352
- const operationServer = operation.servers && operation.servers[0];
1353
- const serverUrl = operationServer || methodServer || rootServer;
1354
- if (!serverUrl) {
1355
- throw new SpecParserError(ConstantString.NoServerInformation, ErrorType.NoServerInformation);
1356
- }
1357
- apiResult.server = Utils.resolveServerUrl(serverUrl.url);
1358
- let operationId = operation.operationId;
1359
- if (!operationId) {
1360
- operationId = `${method.toLowerCase()}${Utils.convertPathToCamelCase(path)}`;
1361
- }
1362
- apiResult.operationId = operationId;
1363
- const authArray = Utils.getAuthArray(operation.security, spec);
1364
- for (const auths of authArray) {
1365
- if (auths.length === 1) {
1366
- apiResult.auth = auths[0].authScheme;
1367
- break;
1828
+ if (isValid) {
1829
+ const serverObj = Utils.getServerObject(spec, method.toLocaleLowerCase(), path);
1830
+ if (serverObj) {
1831
+ apiResult.server = Utils.resolveEnv(serverObj.url);
1832
+ }
1833
+ const authArray = Utils.getAuthArray(operation.security, spec);
1834
+ for (const auths of authArray) {
1835
+ if (auths.length === 1) {
1836
+ apiResult.auth = auths[0];
1837
+ break;
1838
+ }
1368
1839
  }
1369
1840
  }
1370
- apiResult.api = apiKey;
1371
- result.push(apiResult);
1841
+ result.APIs.push(apiResult);
1372
1842
  }
1843
+ result.allAPICount = result.APIs.length;
1844
+ result.validAPICount = result.APIs.filter((api) => api.isValid).length;
1373
1845
  return result;
1374
1846
  }
1375
1847
  catch (err) {
@@ -1422,18 +1894,12 @@ class SpecParser {
1422
1894
  const newSpecs = await this.getFilteredSpecs(filter, signal);
1423
1895
  const newUnResolvedSpec = newSpecs[0];
1424
1896
  const newSpec = newSpecs[1];
1425
- let resultStr;
1426
- if (outputSpecPath.endsWith(".yaml") || outputSpecPath.endsWith(".yml")) {
1427
- resultStr = jsyaml.dump(newUnResolvedSpec);
1428
- }
1429
- else {
1430
- resultStr = JSON.stringify(newUnResolvedSpec, null, 2);
1431
- }
1432
- await fs.outputFile(outputSpecPath, resultStr);
1897
+ const authInfo = Utils.getAuthInfo(newSpec);
1898
+ await this.saveFilterSpec(outputSpecPath, newUnResolvedSpec);
1433
1899
  if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
1434
1900
  throw new SpecParserError(ConstantString.CancelledMessage, ErrorType.Cancelled);
1435
1901
  }
1436
- const [updatedManifest, apiPlugin] = await ManifestUpdater.updateManifestWithAiPlugin(manifestPath, outputSpecPath, pluginFilePath, newSpec, this.options);
1902
+ const [updatedManifest, apiPlugin] = await ManifestUpdater.updateManifestWithAiPlugin(manifestPath, outputSpecPath, pluginFilePath, newSpec, this.options, authInfo);
1437
1903
  await fs.outputJSON(manifestPath, updatedManifest, { spaces: 2 });
1438
1904
  await fs.outputJSON(pluginFilePath, apiPlugin, { spaces: 2 });
1439
1905
  }
@@ -1461,32 +1927,11 @@ class SpecParser {
1461
1927
  const newSpecs = await this.getFilteredSpecs(filter, signal);
1462
1928
  const newUnResolvedSpec = newSpecs[0];
1463
1929
  const newSpec = newSpecs[1];
1464
- const authSet = new Set();
1465
- let hasMultipleAuth = false;
1466
- for (const url in newSpec.paths) {
1467
- for (const method in newSpec.paths[url]) {
1468
- const operation = newSpec.paths[url][method];
1469
- const authArray = Utils.getAuthArray(operation.security, newSpec);
1470
- if (authArray && authArray.length > 0) {
1471
- authSet.add(authArray[0][0]);
1472
- if (authSet.size > 1) {
1473
- hasMultipleAuth = true;
1474
- break;
1475
- }
1476
- }
1477
- }
1478
- }
1479
- if (hasMultipleAuth && this.options.projectType !== ProjectType.TeamsAi) {
1480
- throw new SpecParserError(ConstantString.MultipleAuthNotSupported, ErrorType.MultipleAuthNotSupported);
1481
- }
1482
- let resultStr;
1483
- if (outputSpecPath.endsWith(".yaml") || outputSpecPath.endsWith(".yml")) {
1484
- resultStr = jsyaml.dump(newUnResolvedSpec);
1485
- }
1486
- else {
1487
- resultStr = JSON.stringify(newUnResolvedSpec, null, 2);
1930
+ let authInfo = undefined;
1931
+ if (this.options.projectType === ProjectType.SME) {
1932
+ authInfo = Utils.getAuthInfo(newSpec);
1488
1933
  }
1489
- await fs.outputFile(outputSpecPath, resultStr);
1934
+ await this.saveFilterSpec(outputSpecPath, newUnResolvedSpec);
1490
1935
  if (adaptiveCardFolder) {
1491
1936
  for (const url in newSpec.paths) {
1492
1937
  for (const method in newSpec.paths[url]) {
@@ -1516,7 +1961,6 @@ class SpecParser {
1516
1961
  if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
1517
1962
  throw new SpecParserError(ConstantString.CancelledMessage, ErrorType.Cancelled);
1518
1963
  }
1519
- const authInfo = Array.from(authSet)[0];
1520
1964
  const [updatedManifest, warnings] = await ManifestUpdater.updateManifest(manifestPath, outputSpecPath, newSpec, this.options, adaptiveCardFolder, authInfo);
1521
1965
  await fs.outputJSON(manifestPath, updatedManifest, { spaces: 2 });
1522
1966
  result.warnings.push(...warnings);
@@ -1542,13 +1986,28 @@ class SpecParser {
1542
1986
  this.spec = (await this.parser.dereference(clonedUnResolveSpec));
1543
1987
  }
1544
1988
  }
1545
- getAllSupportedAPIs(spec) {
1546
- if (this.apiMap !== undefined) {
1547
- return this.apiMap;
1989
+ getAPIs(spec) {
1990
+ const validator = this.getValidator(spec);
1991
+ const apiMap = validator.listAPIs();
1992
+ return apiMap;
1993
+ }
1994
+ getValidator(spec) {
1995
+ if (this.validator) {
1996
+ return this.validator;
1548
1997
  }
1549
- const result = Utils.listSupportedAPIs(spec, this.options);
1550
- this.apiMap = result;
1551
- return result;
1998
+ const validator = ValidatorFactory.create(spec, this.options);
1999
+ this.validator = validator;
2000
+ return validator;
2001
+ }
2002
+ async saveFilterSpec(outputSpecPath, unResolvedSpec) {
2003
+ let resultStr;
2004
+ if (outputSpecPath.endsWith(".yaml") || outputSpecPath.endsWith(".yml")) {
2005
+ resultStr = jsyaml.dump(unResolvedSpec);
2006
+ }
2007
+ else {
2008
+ resultStr = JSON.stringify(unResolvedSpec, null, 2);
2009
+ }
2010
+ await fs.outputFile(outputSpecPath, resultStr);
1552
2011
  }
1553
2012
  }
1554
2013