@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.
@@ -62,6 +62,7 @@ exports.ErrorType = void 0;
62
62
  ErrorType["ResolveServerUrlFailed"] = "resolve-server-url-failed";
63
63
  ErrorType["SwaggerNotSupported"] = "swagger-not-supported";
64
64
  ErrorType["MultipleAuthNotSupported"] = "multiple-auth-not-supported";
65
+ ErrorType["SpecVersionNotSupported"] = "spec-version-not-supported";
65
66
  ErrorType["ListFailed"] = "list-failed";
66
67
  ErrorType["listSupportedAPIInfoFailed"] = "list-supported-api-info-failed";
67
68
  ErrorType["FilterSpecFailed"] = "filter-spec-failed";
@@ -70,6 +71,21 @@ exports.ErrorType = void 0;
70
71
  ErrorType["GenerateFailed"] = "generate-failed";
71
72
  ErrorType["ValidateFailed"] = "validate-failed";
72
73
  ErrorType["GetSpecFailed"] = "get-spec-failed";
74
+ ErrorType["AuthTypeIsNotSupported"] = "auth-type-is-not-supported";
75
+ ErrorType["MissingOperationId"] = "missing-operation-id";
76
+ ErrorType["PostBodyContainMultipleMediaTypes"] = "post-body-contain-multiple-media-types";
77
+ ErrorType["ResponseContainMultipleMediaTypes"] = "response-contain-multiple-media-types";
78
+ ErrorType["ResponseJsonIsEmpty"] = "response-json-is-empty";
79
+ ErrorType["PostBodySchemaIsNotJson"] = "post-body-schema-is-not-json";
80
+ ErrorType["PostBodyContainsRequiredUnsupportedSchema"] = "post-body-contains-required-unsupported-schema";
81
+ ErrorType["ParamsContainRequiredUnsupportedSchema"] = "params-contain-required-unsupported-schema";
82
+ ErrorType["ParamsContainsNestedObject"] = "params-contains-nested-object";
83
+ ErrorType["RequestBodyContainsNestedObject"] = "request-body-contains-nested-object";
84
+ ErrorType["ExceededRequiredParamsLimit"] = "exceeded-required-params-limit";
85
+ ErrorType["NoParameter"] = "no-parameter";
86
+ ErrorType["NoAPIInfo"] = "no-api-info";
87
+ ErrorType["MethodNotAllowed"] = "method-not-allowed";
88
+ ErrorType["UrlPathNotExist"] = "url-path-not-exist";
73
89
  ErrorType["Cancelled"] = "cancelled";
74
90
  ErrorType["Unknown"] = "unknown";
75
91
  })(exports.ErrorType || (exports.ErrorType = {}));
@@ -117,6 +133,7 @@ ConstantString.ResolveServerUrlFailed = "Unable to resolve the server URL: pleas
117
133
  ConstantString.OperationOnlyContainsOptionalParam = "Operation %s contains multiple optional parameters. The first optional parameter is used for this command.";
118
134
  ConstantString.ConvertSwaggerToOpenAPI = "The Swagger 2.0 file has been converted to OpenAPI 3.0.";
119
135
  ConstantString.SwaggerNotSupported = "Swagger 2.0 is not supported. Please convert to OpenAPI 3.0 manually before proceeding.";
136
+ ConstantString.SpecVersionNotSupported = "Unsupported OpenAPI version %s. Please use version 3.0.x.";
120
137
  ConstantString.MultipleAuthNotSupported = "Multiple authentication methods are unsupported. Ensure all selected APIs use identical authentication.";
121
138
  ConstantString.UnsupportedSchema = "Unsupported schema in %s %s: %s";
122
139
  ConstantString.WrappedCardVersion = "devPreview";
@@ -128,6 +145,7 @@ ConstantString.AdaptiveCardVersion = "1.5";
128
145
  ConstantString.AdaptiveCardSchema = "http://adaptivecards.io/schemas/adaptive-card.json";
129
146
  ConstantString.AdaptiveCardType = "AdaptiveCard";
130
147
  ConstantString.TextBlockType = "TextBlock";
148
+ ConstantString.ImageType = "Image";
131
149
  ConstantString.ContainerType = "Container";
132
150
  ConstantString.RegistrationIdPostfix = "REGISTRATION_ID";
133
151
  ConstantString.OAuthRegistrationIdPostFix = "OAUTH_REGISTRATION_ID";
@@ -191,7 +209,8 @@ ConstantString.CommandDescriptionMaxLens = 128;
191
209
  ConstantString.ParameterDescriptionMaxLens = 128;
192
210
  ConstantString.CommandTitleMaxLens = 32;
193
211
  ConstantString.ParameterTitleMaxLens = 32;
194
- ConstantString.SMERequiredParamsMaxNum = 5;
212
+ ConstantString.SMERequiredParamsMaxNum = 5;
213
+ ConstantString.DefaultPluginId = "plugin_1";
195
214
 
196
215
  // Copyright (c) Microsoft Corporation.
197
216
  class SpecParserError extends Error {
@@ -214,221 +233,9 @@ class Utils {
214
233
  }
215
234
  return false;
216
235
  }
217
- static checkParameters(paramObject, isCopilot) {
218
- const paramResult = {
219
- requiredNum: 0,
220
- optionalNum: 0,
221
- isValid: true,
222
- };
223
- if (!paramObject) {
224
- return paramResult;
225
- }
226
- for (let i = 0; i < paramObject.length; i++) {
227
- const param = paramObject[i];
228
- const schema = param.schema;
229
- if (isCopilot && this.hasNestedObjectInSchema(schema)) {
230
- paramResult.isValid = false;
231
- continue;
232
- }
233
- const isRequiredWithoutDefault = param.required && schema.default === undefined;
234
- if (isCopilot) {
235
- if (isRequiredWithoutDefault) {
236
- paramResult.requiredNum = paramResult.requiredNum + 1;
237
- }
238
- else {
239
- paramResult.optionalNum = paramResult.optionalNum + 1;
240
- }
241
- continue;
242
- }
243
- if (param.in === "header" || param.in === "cookie") {
244
- if (isRequiredWithoutDefault) {
245
- paramResult.isValid = false;
246
- }
247
- continue;
248
- }
249
- if (schema.type !== "boolean" &&
250
- schema.type !== "string" &&
251
- schema.type !== "number" &&
252
- schema.type !== "integer") {
253
- if (isRequiredWithoutDefault) {
254
- paramResult.isValid = false;
255
- }
256
- continue;
257
- }
258
- if (param.in === "query" || param.in === "path") {
259
- if (isRequiredWithoutDefault) {
260
- paramResult.requiredNum = paramResult.requiredNum + 1;
261
- }
262
- else {
263
- paramResult.optionalNum = paramResult.optionalNum + 1;
264
- }
265
- }
266
- }
267
- return paramResult;
268
- }
269
- static checkPostBody(schema, isRequired = false, isCopilot = false) {
270
- var _a;
271
- const paramResult = {
272
- requiredNum: 0,
273
- optionalNum: 0,
274
- isValid: true,
275
- };
276
- if (Object.keys(schema).length === 0) {
277
- return paramResult;
278
- }
279
- const isRequiredWithoutDefault = isRequired && schema.default === undefined;
280
- if (isCopilot && this.hasNestedObjectInSchema(schema)) {
281
- paramResult.isValid = false;
282
- return paramResult;
283
- }
284
- if (schema.type === "string" ||
285
- schema.type === "integer" ||
286
- schema.type === "boolean" ||
287
- schema.type === "number") {
288
- if (isRequiredWithoutDefault) {
289
- paramResult.requiredNum = paramResult.requiredNum + 1;
290
- }
291
- else {
292
- paramResult.optionalNum = paramResult.optionalNum + 1;
293
- }
294
- }
295
- else if (schema.type === "object") {
296
- const { properties } = schema;
297
- for (const property in properties) {
298
- let isRequired = false;
299
- if (schema.required && ((_a = schema.required) === null || _a === void 0 ? void 0 : _a.indexOf(property)) >= 0) {
300
- isRequired = true;
301
- }
302
- const result = Utils.checkPostBody(properties[property], isRequired, isCopilot);
303
- paramResult.requiredNum += result.requiredNum;
304
- paramResult.optionalNum += result.optionalNum;
305
- paramResult.isValid = paramResult.isValid && result.isValid;
306
- }
307
- }
308
- else {
309
- if (isRequiredWithoutDefault && !isCopilot) {
310
- paramResult.isValid = false;
311
- }
312
- }
313
- return paramResult;
314
- }
315
236
  static containMultipleMediaTypes(bodyObject) {
316
237
  return Object.keys((bodyObject === null || bodyObject === void 0 ? void 0 : bodyObject.content) || {}).length > 1;
317
238
  }
318
- /**
319
- * Checks if the given API is supported.
320
- * @param {string} method - The HTTP method of the API.
321
- * @param {string} path - The path of the API.
322
- * @param {OpenAPIV3.Document} spec - The OpenAPI specification document.
323
- * @returns {boolean} - Returns true if the API is supported, false otherwise.
324
- * @description The following APIs are supported:
325
- * 1. only support Get/Post operation without auth property
326
- * 2. parameter inside query or path only support string, number, boolean and integer
327
- * 3. parameter inside post body only support string, number, boolean, integer and object
328
- * 4. request body + required parameters <= 1
329
- * 5. response body should be “application/json” and not empty, and response code should be 20X
330
- * 6. only support request body with “application/json” content type
331
- */
332
- static isSupportedApi(method, path, spec, options) {
333
- var _a;
334
- const pathObj = spec.paths[path];
335
- method = method.toLocaleLowerCase();
336
- if (pathObj) {
337
- if (((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && pathObj[method]) {
338
- const securities = pathObj[method].security;
339
- const isTeamsAi = options.projectType === exports.ProjectType.TeamsAi;
340
- const isCopilot = options.projectType === exports.ProjectType.Copilot;
341
- // Teams AI project doesn't care about auth, it will use authProvider for user to implement
342
- if (!isTeamsAi) {
343
- const authArray = Utils.getAuthArray(securities, spec);
344
- if (!Utils.isSupportedAuth(authArray, options)) {
345
- return false;
346
- }
347
- }
348
- const operationObject = pathObj[method];
349
- if (!options.allowMissingId && !operationObject.operationId) {
350
- return false;
351
- }
352
- const paramObject = operationObject.parameters;
353
- const requestBody = operationObject.requestBody;
354
- const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
355
- if (!isTeamsAi && Utils.containMultipleMediaTypes(requestBody)) {
356
- return false;
357
- }
358
- const responseJson = Utils.getResponseJson(operationObject, isTeamsAi);
359
- if (Object.keys(responseJson).length === 0) {
360
- return false;
361
- }
362
- // Teams AI project doesn't care about request parameters/body
363
- if (isTeamsAi) {
364
- return true;
365
- }
366
- let requestBodyParamResult = {
367
- requiredNum: 0,
368
- optionalNum: 0,
369
- isValid: true,
370
- };
371
- if (requestJsonBody) {
372
- const requestBodySchema = requestJsonBody.schema;
373
- if (isCopilot && requestBodySchema.type !== "object") {
374
- return false;
375
- }
376
- requestBodyParamResult = Utils.checkPostBody(requestBodySchema, requestBody.required, isCopilot);
377
- }
378
- if (!requestBodyParamResult.isValid) {
379
- return false;
380
- }
381
- const paramResult = Utils.checkParameters(paramObject, isCopilot);
382
- if (!paramResult.isValid) {
383
- return false;
384
- }
385
- // Copilot support arbitrary parameters
386
- if (isCopilot) {
387
- return true;
388
- }
389
- if (requestBodyParamResult.requiredNum + paramResult.requiredNum > 1) {
390
- if (options.allowMultipleParameters &&
391
- requestBodyParamResult.requiredNum + paramResult.requiredNum <=
392
- ConstantString.SMERequiredParamsMaxNum) {
393
- return true;
394
- }
395
- return false;
396
- }
397
- else if (requestBodyParamResult.requiredNum +
398
- requestBodyParamResult.optionalNum +
399
- paramResult.requiredNum +
400
- paramResult.optionalNum ===
401
- 0) {
402
- return false;
403
- }
404
- else {
405
- return true;
406
- }
407
- }
408
- }
409
- return false;
410
- }
411
- static isSupportedAuth(authSchemeArray, options) {
412
- if (authSchemeArray.length === 0) {
413
- return true;
414
- }
415
- if (options.allowAPIKeyAuth || options.allowOauth2 || options.allowBearerTokenAuth) {
416
- // Currently we don't support multiple auth in one operation
417
- if (authSchemeArray.length > 0 && authSchemeArray.every((auths) => auths.length > 1)) {
418
- return false;
419
- }
420
- for (const auths of authSchemeArray) {
421
- if (auths.length === 1) {
422
- if ((options.allowAPIKeyAuth && Utils.isAPIKeyAuth(auths[0].authScheme)) ||
423
- (options.allowOauth2 && Utils.isOAuthWithAuthCodeFlow(auths[0].authScheme)) ||
424
- (options.allowBearerTokenAuth && Utils.isBearerTokenAuth(auths[0].authScheme))) {
425
- return true;
426
- }
427
- }
428
- }
429
- }
430
- return false;
431
- }
432
239
  static isBearerTokenAuth(authScheme) {
433
240
  return authScheme.type === "http" && authScheme.scheme === "bearer";
434
241
  }
@@ -436,10 +243,9 @@ class Utils {
436
243
  return authScheme.type === "apiKey";
437
244
  }
438
245
  static isOAuthWithAuthCodeFlow(authScheme) {
439
- if (authScheme.type === "oauth2" && authScheme.flows && authScheme.flows.authorizationCode) {
440
- return true;
441
- }
442
- return false;
246
+ return !!(authScheme.type === "oauth2" &&
247
+ authScheme.flows &&
248
+ authScheme.flows.authorizationCode);
443
249
  }
444
250
  static getAuthArray(securities, spec) {
445
251
  var _a;
@@ -464,17 +270,39 @@ class Utils {
464
270
  result.sort((a, b) => a[0].name.localeCompare(b[0].name));
465
271
  return result;
466
272
  }
273
+ static getAuthInfo(spec) {
274
+ let authInfo = undefined;
275
+ for (const url in spec.paths) {
276
+ for (const method in spec.paths[url]) {
277
+ const operation = spec.paths[url][method];
278
+ const authArray = Utils.getAuthArray(operation.security, spec);
279
+ if (authArray && authArray.length > 0) {
280
+ const currentAuth = authArray[0][0];
281
+ if (!authInfo) {
282
+ authInfo = authArray[0][0];
283
+ }
284
+ else if (authInfo.name !== currentAuth.name) {
285
+ throw new SpecParserError(ConstantString.MultipleAuthNotSupported, exports.ErrorType.MultipleAuthNotSupported);
286
+ }
287
+ }
288
+ }
289
+ }
290
+ return authInfo;
291
+ }
467
292
  static updateFirstLetter(str) {
468
293
  return str.charAt(0).toUpperCase() + str.slice(1);
469
294
  }
470
- static getResponseJson(operationObject, isTeamsAiProject = false) {
295
+ static getResponseJson(operationObject) {
471
296
  var _a, _b;
472
297
  let json = {};
298
+ let multipleMediaType = false;
473
299
  for (const code of ConstantString.ResponseCodeFor20X) {
474
300
  const responseObject = (_a = operationObject === null || operationObject === void 0 ? void 0 : operationObject.responses) === null || _a === void 0 ? void 0 : _a[code];
475
301
  if ((_b = responseObject === null || responseObject === void 0 ? void 0 : responseObject.content) === null || _b === void 0 ? void 0 : _b["application/json"]) {
302
+ multipleMediaType = false;
476
303
  json = responseObject.content["application/json"];
477
- if (!isTeamsAiProject && Utils.containMultipleMediaTypes(responseObject)) {
304
+ if (Utils.containMultipleMediaTypes(responseObject)) {
305
+ multipleMediaType = true;
478
306
  json = {};
479
307
  }
480
308
  else {
@@ -482,7 +310,7 @@ class Utils {
482
310
  }
483
311
  }
484
312
  }
485
- return json;
313
+ return { json, multipleMediaType };
486
314
  }
487
315
  static convertPathToCamelCase(path) {
488
316
  const pathSegments = path.split(/[./{]/);
@@ -502,10 +330,10 @@ class Utils {
502
330
  return undefined;
503
331
  }
504
332
  }
505
- static resolveServerUrl(url) {
333
+ static resolveEnv(str) {
506
334
  const placeHolderReg = /\${{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*}}/g;
507
- let matches = placeHolderReg.exec(url);
508
- let newUrl = url;
335
+ let matches = placeHolderReg.exec(str);
336
+ let newStr = str;
509
337
  while (matches != null) {
510
338
  const envVar = matches[1];
511
339
  const envVal = process.env[envVar];
@@ -513,17 +341,17 @@ class Utils {
513
341
  throw new Error(Utils.format(ConstantString.ResolveServerUrlFailed, envVar));
514
342
  }
515
343
  else {
516
- newUrl = newUrl.replace(matches[0], envVal);
344
+ newStr = newStr.replace(matches[0], envVal);
517
345
  }
518
- matches = placeHolderReg.exec(url);
346
+ matches = placeHolderReg.exec(str);
519
347
  }
520
- return newUrl;
348
+ return newStr;
521
349
  }
522
350
  static checkServerUrl(servers) {
523
351
  const errors = [];
524
352
  let serverUrl;
525
353
  try {
526
- serverUrl = Utils.resolveServerUrl(servers[0].url);
354
+ serverUrl = Utils.resolveEnv(servers[0].url);
527
355
  }
528
356
  catch (err) {
529
357
  errors.push({
@@ -554,6 +382,7 @@ class Utils {
554
382
  return errors;
555
383
  }
556
384
  static validateServer(spec, options) {
385
+ var _a;
557
386
  const errors = [];
558
387
  let hasTopLevelServers = false;
559
388
  let hasPathLevelServers = false;
@@ -574,7 +403,7 @@ class Utils {
574
403
  }
575
404
  for (const method in methods) {
576
405
  const operationObject = methods[method];
577
- if (Utils.isSupportedApi(method, path, spec, options)) {
406
+ if (((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && operationObject) {
578
407
  if ((operationObject === null || operationObject === void 0 ? void 0 : operationObject.servers) && operationObject.servers.length >= 1) {
579
408
  hasOperationLevelServers = true;
580
409
  const serverErrors = Utils.checkServerUrl(operationObject.servers);
@@ -617,6 +446,7 @@ class Utils {
617
446
  Utils.updateParameterWithInputType(schema, parameter);
618
447
  }
619
448
  if (isRequired && schema.default === undefined) {
449
+ parameter.isRequired = true;
620
450
  requiredParams.push(parameter);
621
451
  }
622
452
  else {
@@ -680,6 +510,7 @@ class Utils {
680
510
  }
681
511
  if (param.in !== "header" && param.in !== "cookie") {
682
512
  if (param.required && (schema === null || schema === void 0 ? void 0 : schema.default) === undefined) {
513
+ parameter.isRequired = true;
683
514
  requiredParams.push(parameter);
684
515
  }
685
516
  else {
@@ -699,13 +530,7 @@ class Utils {
699
530
  }
700
531
  }
701
532
  const operationId = operationItem.operationId;
702
- const parameters = [];
703
- if (requiredParams.length !== 0) {
704
- parameters.push(...requiredParams);
705
- }
706
- else {
707
- parameters.push(optionalParams[0]);
708
- }
533
+ const parameters = [...requiredParams, ...optionalParams];
709
534
  const command = {
710
535
  context: ["compose"],
711
536
  type: "query",
@@ -714,353 +539,575 @@ class Utils {
714
539
  parameters: parameters,
715
540
  description: ((_b = operationItem.description) !== null && _b !== void 0 ? _b : "").slice(0, ConstantString.CommandDescriptionMaxLens),
716
541
  };
717
- let warning = undefined;
718
- if (requiredParams.length === 0 && optionalParams.length > 1) {
719
- warning = {
720
- type: exports.WarningType.OperationOnlyContainsOptionalParam,
721
- content: Utils.format(ConstantString.OperationOnlyContainsOptionalParam, operationId),
722
- data: operationId,
723
- };
542
+ return command;
543
+ }
544
+ static format(str, ...args) {
545
+ let index = 0;
546
+ return str.replace(/%s/g, () => {
547
+ const arg = args[index++];
548
+ return arg !== undefined ? arg : "";
549
+ });
550
+ }
551
+ static getSafeRegistrationIdEnvName(authName) {
552
+ if (!authName) {
553
+ return "";
554
+ }
555
+ let safeRegistrationIdEnvName = authName.toUpperCase().replace(/[^A-Z0-9_]/g, "_");
556
+ if (!safeRegistrationIdEnvName.match(/^[A-Z]/)) {
557
+ safeRegistrationIdEnvName = "PREFIX_" + safeRegistrationIdEnvName;
724
558
  }
725
- return [command, warning];
559
+ return safeRegistrationIdEnvName;
726
560
  }
727
- static listSupportedAPIs(spec, options) {
728
- const paths = spec.paths;
561
+ static getServerObject(spec, method, path) {
562
+ const pathObj = spec.paths[path];
563
+ const operationObject = pathObj[method];
564
+ const rootServer = spec.servers && spec.servers[0];
565
+ const methodServer = spec.paths[path].servers && spec.paths[path].servers[0];
566
+ const operationServer = operationObject.servers && operationObject.servers[0];
567
+ const serverUrl = operationServer || methodServer || rootServer;
568
+ return serverUrl;
569
+ }
570
+ }
571
+
572
+ // Copyright (c) Microsoft Corporation.
573
+ class Validator {
574
+ listAPIs() {
575
+ var _a;
576
+ if (this.apiMap) {
577
+ return this.apiMap;
578
+ }
579
+ const paths = this.spec.paths;
729
580
  const result = {};
730
581
  for (const path in paths) {
731
582
  const methods = paths[path];
732
583
  for (const method in methods) {
733
- if (Utils.isSupportedApi(method, path, spec, options)) {
734
- const operationObject = methods[method];
735
- result[`${method.toUpperCase()} ${path}`] = operationObject;
584
+ const operationObject = methods[method];
585
+ if (((_a = this.options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && operationObject) {
586
+ const validateResult = this.validateAPI(method, path);
587
+ result[`${method.toUpperCase()} ${path}`] = {
588
+ operation: operationObject,
589
+ isValid: validateResult.isValid,
590
+ reason: validateResult.reason,
591
+ };
736
592
  }
737
593
  }
738
594
  }
595
+ this.apiMap = result;
739
596
  return result;
740
597
  }
741
- static validateSpec(spec, parser, isSwaggerFile, options) {
742
- const errors = [];
743
- const warnings = [];
744
- if (isSwaggerFile) {
745
- warnings.push({
746
- type: exports.WarningType.ConvertSwaggerToOpenAPI,
747
- content: ConstantString.ConvertSwaggerToOpenAPI,
748
- });
749
- }
750
- // Server validation
751
- const serverErrors = Utils.validateServer(spec, options);
752
- errors.push(...serverErrors);
753
- // Remote reference not supported
754
- const refPaths = parser.$refs.paths();
755
- // refPaths [0] is the current spec file path
756
- if (refPaths.length > 1) {
757
- errors.push({
758
- type: exports.ErrorType.RemoteRefNotSupported,
759
- content: Utils.format(ConstantString.RemoteRefNotSupported, refPaths.join(", ")),
760
- data: refPaths,
598
+ validateSpecVersion() {
599
+ const result = { errors: [], warnings: [] };
600
+ if (this.spec.openapi >= "3.1.0") {
601
+ result.errors.push({
602
+ type: exports.ErrorType.SpecVersionNotSupported,
603
+ content: Utils.format(ConstantString.SpecVersionNotSupported, this.spec.openapi),
604
+ data: this.spec.openapi,
761
605
  });
762
606
  }
763
- // No supported API
764
- const apiMap = Utils.listSupportedAPIs(spec, options);
765
- if (Object.keys(apiMap).length === 0) {
766
- errors.push({
607
+ return result;
608
+ }
609
+ validateSpecServer() {
610
+ const result = { errors: [], warnings: [] };
611
+ const serverErrors = Utils.validateServer(this.spec, this.options);
612
+ result.errors.push(...serverErrors);
613
+ return result;
614
+ }
615
+ validateSpecNoSupportAPI() {
616
+ const result = { errors: [], warnings: [] };
617
+ const apiMap = this.listAPIs();
618
+ const validAPIs = Object.entries(apiMap).filter(([, value]) => value.isValid);
619
+ if (validAPIs.length === 0) {
620
+ const data = [];
621
+ for (const key in apiMap) {
622
+ const { reason } = apiMap[key];
623
+ const apiInvalidReason = { api: key, reason: reason };
624
+ data.push(apiInvalidReason);
625
+ }
626
+ result.errors.push({
767
627
  type: exports.ErrorType.NoSupportedApi,
768
628
  content: ConstantString.NoSupportedApi,
629
+ data,
769
630
  });
770
631
  }
632
+ return result;
633
+ }
634
+ validateSpecOperationId() {
635
+ const result = { errors: [], warnings: [] };
636
+ const apiMap = this.listAPIs();
771
637
  // OperationId missing
772
638
  const apisMissingOperationId = [];
773
639
  for (const key in apiMap) {
774
- const pathObjectItem = apiMap[key];
775
- if (!pathObjectItem.operationId) {
640
+ const { operation } = apiMap[key];
641
+ if (!operation.operationId) {
776
642
  apisMissingOperationId.push(key);
777
643
  }
778
644
  }
779
645
  if (apisMissingOperationId.length > 0) {
780
- warnings.push({
646
+ result.warnings.push({
781
647
  type: exports.WarningType.OperationIdMissing,
782
648
  content: Utils.format(ConstantString.MissingOperationId, apisMissingOperationId.join(", ")),
783
649
  data: apisMissingOperationId,
784
650
  });
785
651
  }
786
- let status = exports.ValidationStatus.Valid;
787
- if (warnings.length > 0 && errors.length === 0) {
788
- status = exports.ValidationStatus.Warning;
652
+ return result;
653
+ }
654
+ validateMethodAndPath(method, path) {
655
+ const result = { isValid: true, reason: [] };
656
+ if (this.options.allowMethods && !this.options.allowMethods.includes(method)) {
657
+ result.isValid = false;
658
+ result.reason.push(exports.ErrorType.MethodNotAllowed);
659
+ return result;
789
660
  }
790
- else if (errors.length > 0) {
791
- status = exports.ValidationStatus.Error;
661
+ const pathObj = this.spec.paths[path];
662
+ if (!pathObj || !pathObj[method]) {
663
+ result.isValid = false;
664
+ result.reason.push(exports.ErrorType.UrlPathNotExist);
665
+ return result;
792
666
  }
793
- return {
794
- status,
795
- warnings,
796
- errors,
797
- };
667
+ return result;
798
668
  }
799
- static format(str, ...args) {
800
- let index = 0;
801
- return str.replace(/%s/g, () => {
802
- const arg = args[index++];
803
- return arg !== undefined ? arg : "";
804
- });
669
+ validateResponse(method, path) {
670
+ const result = { isValid: true, reason: [] };
671
+ const operationObject = this.spec.paths[path][method];
672
+ const { json, multipleMediaType } = Utils.getResponseJson(operationObject);
673
+ if (this.options.projectType === exports.ProjectType.SME) {
674
+ // only support response body only contains “application/json” content type
675
+ if (multipleMediaType) {
676
+ result.reason.push(exports.ErrorType.ResponseContainMultipleMediaTypes);
677
+ }
678
+ else if (Object.keys(json).length === 0) {
679
+ // response body should not be empty
680
+ result.reason.push(exports.ErrorType.ResponseJsonIsEmpty);
681
+ }
682
+ }
683
+ return result;
805
684
  }
806
- static getSafeRegistrationIdEnvName(authName) {
807
- if (!authName) {
808
- return "";
685
+ validateServer(method, path) {
686
+ const result = { isValid: true, reason: [] };
687
+ const serverObj = Utils.getServerObject(this.spec, method, path);
688
+ if (!serverObj) {
689
+ // should contain server URL
690
+ result.reason.push(exports.ErrorType.NoServerInformation);
809
691
  }
810
- let safeRegistrationIdEnvName = authName.toUpperCase().replace(/[^A-Z0-9_]/g, "_");
811
- if (!safeRegistrationIdEnvName.match(/^[A-Z]/)) {
812
- safeRegistrationIdEnvName = "PREFIX_" + safeRegistrationIdEnvName;
692
+ else {
693
+ // server url should be absolute url with https protocol
694
+ const serverValidateResult = Utils.checkServerUrl([serverObj]);
695
+ result.reason.push(...serverValidateResult.map((item) => item.type));
813
696
  }
814
- return safeRegistrationIdEnvName;
697
+ return result;
815
698
  }
816
- }
817
-
818
- // Copyright (c) Microsoft Corporation.
819
- class SpecFilter {
820
- static specFilter(filter, unResolveSpec, resolvedSpec, options) {
821
- try {
822
- const newSpec = Object.assign({}, unResolveSpec);
823
- const newPaths = {};
824
- for (const filterItem of filter) {
825
- const [method, path] = filterItem.split(" ");
826
- const methodName = method.toLowerCase();
827
- if (!Utils.isSupportedApi(methodName, path, resolvedSpec, options)) {
828
- continue;
829
- }
830
- if (!newPaths[path]) {
831
- newPaths[path] = Object.assign({}, unResolveSpec.paths[path]);
832
- for (const m of ConstantString.AllOperationMethods) {
833
- delete newPaths[path][m];
699
+ validateAuth(method, path) {
700
+ const pathObj = this.spec.paths[path];
701
+ const operationObject = pathObj[method];
702
+ const securities = operationObject.security;
703
+ const authSchemeArray = Utils.getAuthArray(securities, this.spec);
704
+ if (authSchemeArray.length === 0) {
705
+ return { isValid: true, reason: [] };
706
+ }
707
+ if (this.options.allowAPIKeyAuth ||
708
+ this.options.allowOauth2 ||
709
+ this.options.allowBearerTokenAuth) {
710
+ // Currently we don't support multiple auth in one operation
711
+ if (authSchemeArray.length > 0 && authSchemeArray.every((auths) => auths.length > 1)) {
712
+ return {
713
+ isValid: false,
714
+ reason: [exports.ErrorType.MultipleAuthNotSupported],
715
+ };
716
+ }
717
+ for (const auths of authSchemeArray) {
718
+ if (auths.length === 1) {
719
+ if ((this.options.allowAPIKeyAuth && Utils.isAPIKeyAuth(auths[0].authScheme)) ||
720
+ (this.options.allowOauth2 && Utils.isOAuthWithAuthCodeFlow(auths[0].authScheme)) ||
721
+ (this.options.allowBearerTokenAuth && Utils.isBearerTokenAuth(auths[0].authScheme))) {
722
+ return { isValid: true, reason: [] };
834
723
  }
835
724
  }
836
- newPaths[path][methodName] = unResolveSpec.paths[path][methodName];
837
- // Add the operationId if missing
838
- if (!newPaths[path][methodName].operationId) {
839
- newPaths[path][methodName].operationId = `${methodName}${Utils.convertPathToCamelCase(path)}`;
840
- }
841
725
  }
842
- newSpec.paths = newPaths;
843
- return newSpec;
844
- }
845
- catch (err) {
846
- throw new SpecParserError(err.toString(), exports.ErrorType.FilterSpecFailed);
847
726
  }
727
+ return { isValid: false, reason: [exports.ErrorType.AuthTypeIsNotSupported] };
848
728
  }
849
- }
850
-
851
- // Copyright (c) Microsoft Corporation.
852
- class ManifestUpdater {
853
- static updateManifestWithAiPlugin(manifestPath, outputSpecPath, apiPluginFilePath, spec, options) {
854
- return __awaiter(this, void 0, void 0, function* () {
855
- const manifest = yield fs__default['default'].readJSON(manifestPath);
856
- const apiPluginRelativePath = ManifestUpdater.getRelativePath(manifestPath, apiPluginFilePath);
857
- manifest.plugins = [
858
- {
859
- pluginFile: apiPluginRelativePath,
860
- },
861
- ];
862
- ManifestUpdater.updateManifestDescription(manifest, spec);
863
- const specRelativePath = ManifestUpdater.getRelativePath(manifestPath, outputSpecPath);
864
- const apiPlugin = ManifestUpdater.generatePluginManifestSchema(spec, specRelativePath, options);
865
- return [manifest, apiPlugin];
866
- });
867
- }
868
- static updateManifestDescription(manifest, spec) {
869
- var _a, _b;
870
- manifest.description = {
871
- short: spec.info.title.slice(0, ConstantString.ShortDescriptionMaxLens),
872
- full: (_b = ((_a = spec.info.description) !== null && _a !== void 0 ? _a : manifest.description.full)) === null || _b === void 0 ? void 0 : _b.slice(0, ConstantString.FullDescriptionMaxLens),
729
+ checkPostBodySchema(schema, isRequired = false) {
730
+ var _a;
731
+ const paramResult = {
732
+ requiredNum: 0,
733
+ optionalNum: 0,
734
+ isValid: true,
735
+ reason: [],
873
736
  };
874
- }
875
- static mapOpenAPISchemaToFuncParam(schema, method, pathUrl) {
876
- let parameter;
737
+ if (Object.keys(schema).length === 0) {
738
+ return paramResult;
739
+ }
740
+ const isRequiredWithoutDefault = isRequired && schema.default === undefined;
741
+ const isCopilot = this.projectType === exports.ProjectType.Copilot;
742
+ if (isCopilot && this.hasNestedObjectInSchema(schema)) {
743
+ paramResult.isValid = false;
744
+ paramResult.reason = [exports.ErrorType.RequestBodyContainsNestedObject];
745
+ return paramResult;
746
+ }
877
747
  if (schema.type === "string" ||
878
- schema.type === "boolean" ||
879
748
  schema.type === "integer" ||
880
- schema.type === "number" ||
881
- schema.type === "array") {
882
- parameter = schema;
749
+ schema.type === "boolean" ||
750
+ schema.type === "number") {
751
+ if (isRequiredWithoutDefault) {
752
+ paramResult.requiredNum = paramResult.requiredNum + 1;
753
+ }
754
+ else {
755
+ paramResult.optionalNum = paramResult.optionalNum + 1;
756
+ }
757
+ }
758
+ else if (schema.type === "object") {
759
+ const { properties } = schema;
760
+ for (const property in properties) {
761
+ let isRequired = false;
762
+ if (schema.required && ((_a = schema.required) === null || _a === void 0 ? void 0 : _a.indexOf(property)) >= 0) {
763
+ isRequired = true;
764
+ }
765
+ const result = this.checkPostBodySchema(properties[property], isRequired);
766
+ paramResult.requiredNum += result.requiredNum;
767
+ paramResult.optionalNum += result.optionalNum;
768
+ paramResult.isValid = paramResult.isValid && result.isValid;
769
+ paramResult.reason.push(...result.reason);
770
+ }
883
771
  }
884
772
  else {
885
- throw new SpecParserError(Utils.format(ConstantString.UnsupportedSchema, method, pathUrl, JSON.stringify(schema)), exports.ErrorType.UpdateManifestFailed);
773
+ if (isRequiredWithoutDefault && !isCopilot) {
774
+ paramResult.isValid = false;
775
+ paramResult.reason.push(exports.ErrorType.PostBodyContainsRequiredUnsupportedSchema);
776
+ }
886
777
  }
887
- return parameter;
778
+ return paramResult;
888
779
  }
889
- static generatePluginManifestSchema(spec, specRelativePath, options) {
890
- var _a, _b, _c;
891
- const functions = [];
892
- const functionNames = [];
893
- const paths = spec.paths;
894
- for (const pathUrl in paths) {
895
- const pathItem = paths[pathUrl];
896
- if (pathItem) {
897
- const operations = pathItem;
898
- for (const method in operations) {
899
- if (options.allowMethods.includes(method)) {
900
- const operationItem = operations[method];
901
- if (operationItem) {
902
- const operationId = operationItem.operationId;
903
- const description = (_a = operationItem.description) !== null && _a !== void 0 ? _a : "";
904
- const paramObject = operationItem.parameters;
905
- const requestBody = operationItem.requestBody;
906
- const parameters = {
907
- type: "object",
908
- properties: {},
909
- required: [],
910
- };
911
- if (paramObject) {
912
- for (let i = 0; i < paramObject.length; i++) {
913
- const param = paramObject[i];
914
- const schema = param.schema;
915
- parameters.properties[param.name] = ManifestUpdater.mapOpenAPISchemaToFuncParam(schema, method, pathUrl);
916
- if (param.required) {
917
- parameters.required.push(param.name);
918
- }
919
- if (!parameters.properties[param.name].description) {
920
- parameters.properties[param.name].description = (_b = param.description) !== null && _b !== void 0 ? _b : "";
921
- }
922
- }
923
- }
924
- if (requestBody) {
925
- const requestJsonBody = requestBody.content["application/json"];
926
- const requestBodySchema = requestJsonBody.schema;
927
- if (requestBodySchema.type === "object") {
928
- if (requestBodySchema.required) {
929
- parameters.required.push(...requestBodySchema.required);
930
- }
931
- for (const property in requestBodySchema.properties) {
932
- const schema = requestBodySchema.properties[property];
933
- parameters.properties[property] = ManifestUpdater.mapOpenAPISchemaToFuncParam(schema, method, pathUrl);
934
- }
935
- }
936
- else {
937
- throw new SpecParserError(Utils.format(ConstantString.UnsupportedSchema, method, pathUrl, JSON.stringify(requestBodySchema)), exports.ErrorType.UpdateManifestFailed);
938
- }
939
- }
940
- const funcObj = {
941
- name: operationId,
942
- description: description,
943
- parameters: parameters,
944
- };
945
- functions.push(funcObj);
946
- functionNames.push(operationId);
947
- }
948
- }
780
+ checkParamSchema(paramObject) {
781
+ const paramResult = {
782
+ requiredNum: 0,
783
+ optionalNum: 0,
784
+ isValid: true,
785
+ reason: [],
786
+ };
787
+ if (!paramObject) {
788
+ return paramResult;
789
+ }
790
+ const isCopilot = this.projectType === exports.ProjectType.Copilot;
791
+ for (let i = 0; i < paramObject.length; i++) {
792
+ const param = paramObject[i];
793
+ const schema = param.schema;
794
+ if (isCopilot && this.hasNestedObjectInSchema(schema)) {
795
+ paramResult.isValid = false;
796
+ paramResult.reason.push(exports.ErrorType.ParamsContainsNestedObject);
797
+ continue;
798
+ }
799
+ const isRequiredWithoutDefault = param.required && schema.default === undefined;
800
+ if (isCopilot) {
801
+ if (isRequiredWithoutDefault) {
802
+ paramResult.requiredNum = paramResult.requiredNum + 1;
803
+ }
804
+ else {
805
+ paramResult.optionalNum = paramResult.optionalNum + 1;
806
+ }
807
+ continue;
808
+ }
809
+ if (param.in === "header" || param.in === "cookie") {
810
+ if (isRequiredWithoutDefault) {
811
+ paramResult.isValid = false;
812
+ paramResult.reason.push(exports.ErrorType.ParamsContainRequiredUnsupportedSchema);
813
+ }
814
+ continue;
815
+ }
816
+ if (schema.type !== "boolean" &&
817
+ schema.type !== "string" &&
818
+ schema.type !== "number" &&
819
+ schema.type !== "integer") {
820
+ if (isRequiredWithoutDefault) {
821
+ paramResult.isValid = false;
822
+ paramResult.reason.push(exports.ErrorType.ParamsContainRequiredUnsupportedSchema);
823
+ }
824
+ continue;
825
+ }
826
+ if (param.in === "query" || param.in === "path") {
827
+ if (isRequiredWithoutDefault) {
828
+ paramResult.requiredNum = paramResult.requiredNum + 1;
829
+ }
830
+ else {
831
+ paramResult.optionalNum = paramResult.optionalNum + 1;
949
832
  }
950
833
  }
951
834
  }
952
- const apiPlugin = {
953
- schema_version: "v2",
954
- name_for_human: spec.info.title,
955
- description_for_human: (_c = spec.info.description) !== null && _c !== void 0 ? _c : "<Please add description of the plugin>",
956
- functions: functions,
957
- runtimes: [
958
- {
959
- type: "OpenApi",
960
- auth: {
961
- type: "none", // TODO, support auth in the future
962
- },
963
- spec: {
964
- url: specRelativePath,
965
- },
966
- run_for_functions: functionNames,
967
- },
968
- ],
969
- };
970
- return apiPlugin;
835
+ return paramResult;
971
836
  }
972
- static updateManifest(manifestPath, outputSpecPath, spec, options, adaptiveCardFolder, authInfo) {
973
- return __awaiter(this, void 0, void 0, function* () {
974
- try {
975
- const originalManifest = yield fs__default['default'].readJSON(manifestPath);
976
- const updatedPart = {};
977
- updatedPart.composeExtensions = [];
978
- let warnings = [];
979
- if (options.projectType === exports.ProjectType.SME) {
980
- const updateResult = yield ManifestUpdater.generateCommands(spec, manifestPath, options, adaptiveCardFolder);
981
- const commands = updateResult[0];
982
- warnings = updateResult[1];
983
- const composeExtension = {
984
- composeExtensionType: "apiBased",
985
- apiSpecificationFile: ManifestUpdater.getRelativePath(manifestPath, outputSpecPath),
986
- commands: commands,
987
- };
988
- if (authInfo) {
989
- const auth = authInfo.authScheme;
990
- if (Utils.isAPIKeyAuth(auth) || Utils.isBearerTokenAuth(auth)) {
991
- const safeApiSecretRegistrationId = Utils.getSafeRegistrationIdEnvName(`${authInfo.name}_${ConstantString.RegistrationIdPostfix}`);
992
- composeExtension.authorization = {
993
- authType: "apiSecretServiceAuth",
994
- apiSecretServiceAuthConfiguration: {
995
- apiSecretRegistrationId: `\${{${safeApiSecretRegistrationId}}}`,
996
- },
997
- };
998
- }
999
- else if (Utils.isOAuthWithAuthCodeFlow(auth)) {
1000
- const safeOAuth2RegistrationId = Utils.getSafeRegistrationIdEnvName(`${authInfo.name}_${ConstantString.OAuthRegistrationIdPostFix}`);
1001
- composeExtension.authorization = {
1002
- authType: "oAuth2.0",
1003
- oAuthConfiguration: {
1004
- oauthConfigurationId: `\${{${safeOAuth2RegistrationId}}}`,
1005
- },
1006
- };
1007
- updatedPart.webApplicationInfo = {
1008
- id: "${{AAD_APP_CLIENT_ID}}",
1009
- resource: "api://${{DOMAIN}}/${{AAD_APP_CLIENT_ID}}",
1010
- };
1011
- }
1012
- }
1013
- updatedPart.composeExtensions = [composeExtension];
837
+ hasNestedObjectInSchema(schema) {
838
+ if (schema.type === "object") {
839
+ for (const property in schema.properties) {
840
+ const nestedSchema = schema.properties[property];
841
+ if (nestedSchema.type === "object") {
842
+ return true;
1014
843
  }
1015
- updatedPart.description = originalManifest.description;
1016
- ManifestUpdater.updateManifestDescription(updatedPart, spec);
1017
- const updatedManifest = Object.assign(Object.assign({}, originalManifest), updatedPart);
1018
- return [updatedManifest, warnings];
1019
844
  }
1020
- catch (err) {
1021
- throw new SpecParserError(err.toString(), exports.ErrorType.UpdateManifestFailed);
845
+ }
846
+ return false;
847
+ }
848
+ }
849
+
850
+ // Copyright (c) Microsoft Corporation.
851
+ class CopilotValidator extends Validator {
852
+ constructor(spec, options) {
853
+ super();
854
+ this.projectType = exports.ProjectType.Copilot;
855
+ this.options = options;
856
+ this.spec = spec;
857
+ }
858
+ validateSpec() {
859
+ const result = { errors: [], warnings: [] };
860
+ // validate spec version
861
+ let validationResult = this.validateSpecVersion();
862
+ result.errors.push(...validationResult.errors);
863
+ // validate spec server
864
+ validationResult = this.validateSpecServer();
865
+ result.errors.push(...validationResult.errors);
866
+ // validate no supported API
867
+ validationResult = this.validateSpecNoSupportAPI();
868
+ result.errors.push(...validationResult.errors);
869
+ // validate operationId missing
870
+ validationResult = this.validateSpecOperationId();
871
+ result.warnings.push(...validationResult.warnings);
872
+ return result;
873
+ }
874
+ validateAPI(method, path) {
875
+ const result = { isValid: true, reason: [] };
876
+ method = method.toLocaleLowerCase();
877
+ // validate method and path
878
+ const methodAndPathResult = this.validateMethodAndPath(method, path);
879
+ if (!methodAndPathResult.isValid) {
880
+ return methodAndPathResult;
881
+ }
882
+ const operationObject = this.spec.paths[path][method];
883
+ // validate auth
884
+ const authCheckResult = this.validateAuth(method, path);
885
+ result.reason.push(...authCheckResult.reason);
886
+ // validate operationId
887
+ if (!this.options.allowMissingId && !operationObject.operationId) {
888
+ result.reason.push(exports.ErrorType.MissingOperationId);
889
+ }
890
+ // validate server
891
+ const validateServerResult = this.validateServer(method, path);
892
+ result.reason.push(...validateServerResult.reason);
893
+ // validate response
894
+ const validateResponseResult = this.validateResponse(method, path);
895
+ result.reason.push(...validateResponseResult.reason);
896
+ // validate requestBody
897
+ const requestBody = operationObject.requestBody;
898
+ const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
899
+ if (requestJsonBody) {
900
+ const requestBodySchema = requestJsonBody.schema;
901
+ if (requestBodySchema.type !== "object") {
902
+ result.reason.push(exports.ErrorType.PostBodySchemaIsNotJson);
1022
903
  }
1023
- });
904
+ const requestBodyParamResult = this.checkPostBodySchema(requestBodySchema, requestBody.required);
905
+ result.reason.push(...requestBodyParamResult.reason);
906
+ }
907
+ // validate parameters
908
+ const paramObject = operationObject.parameters;
909
+ const paramResult = this.checkParamSchema(paramObject);
910
+ result.reason.push(...paramResult.reason);
911
+ if (result.reason.length > 0) {
912
+ result.isValid = false;
913
+ }
914
+ return result;
1024
915
  }
1025
- static generateCommands(spec, manifestPath, options, adaptiveCardFolder) {
916
+ }
917
+
918
+ // Copyright (c) Microsoft Corporation.
919
+ class SMEValidator extends Validator {
920
+ constructor(spec, options) {
921
+ super();
922
+ this.projectType = exports.ProjectType.SME;
923
+ this.options = options;
924
+ this.spec = spec;
925
+ }
926
+ validateSpec() {
927
+ const result = { errors: [], warnings: [] };
928
+ // validate spec version
929
+ let validationResult = this.validateSpecVersion();
930
+ result.errors.push(...validationResult.errors);
931
+ // validate spec server
932
+ validationResult = this.validateSpecServer();
933
+ result.errors.push(...validationResult.errors);
934
+ // validate no supported API
935
+ validationResult = this.validateSpecNoSupportAPI();
936
+ result.errors.push(...validationResult.errors);
937
+ // validate operationId missing
938
+ if (this.options.allowMissingId) {
939
+ validationResult = this.validateSpecOperationId();
940
+ result.warnings.push(...validationResult.warnings);
941
+ }
942
+ return result;
943
+ }
944
+ validateAPI(method, path) {
945
+ const result = { isValid: true, reason: [] };
946
+ method = method.toLocaleLowerCase();
947
+ // validate method and path
948
+ const methodAndPathResult = this.validateMethodAndPath(method, path);
949
+ if (!methodAndPathResult.isValid) {
950
+ return methodAndPathResult;
951
+ }
952
+ const operationObject = this.spec.paths[path][method];
953
+ // validate auth
954
+ const authCheckResult = this.validateAuth(method, path);
955
+ result.reason.push(...authCheckResult.reason);
956
+ // validate operationId
957
+ if (!this.options.allowMissingId && !operationObject.operationId) {
958
+ result.reason.push(exports.ErrorType.MissingOperationId);
959
+ }
960
+ // validate server
961
+ const validateServerResult = this.validateServer(method, path);
962
+ result.reason.push(...validateServerResult.reason);
963
+ // validate response
964
+ const validateResponseResult = this.validateResponse(method, path);
965
+ result.reason.push(...validateResponseResult.reason);
966
+ let postBodyResult = {
967
+ requiredNum: 0,
968
+ optionalNum: 0,
969
+ isValid: true,
970
+ reason: [],
971
+ };
972
+ // validate requestBody
973
+ const requestBody = operationObject.requestBody;
974
+ const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
975
+ if (Utils.containMultipleMediaTypes(requestBody)) {
976
+ result.reason.push(exports.ErrorType.PostBodyContainMultipleMediaTypes);
977
+ }
978
+ if (requestJsonBody) {
979
+ const requestBodySchema = requestJsonBody.schema;
980
+ postBodyResult = this.checkPostBodySchema(requestBodySchema, requestBody.required);
981
+ result.reason.push(...postBodyResult.reason);
982
+ }
983
+ // validate parameters
984
+ const paramObject = operationObject.parameters;
985
+ const paramResult = this.checkParamSchema(paramObject);
986
+ result.reason.push(...paramResult.reason);
987
+ // validate total parameters count
988
+ if (paramResult.isValid && postBodyResult.isValid) {
989
+ const paramCountResult = this.validateParamCount(postBodyResult, paramResult);
990
+ result.reason.push(...paramCountResult.reason);
991
+ }
992
+ if (result.reason.length > 0) {
993
+ result.isValid = false;
994
+ }
995
+ return result;
996
+ }
997
+ validateParamCount(postBodyResult, paramResult) {
998
+ const result = { isValid: true, reason: [] };
999
+ const totalRequiredParams = postBodyResult.requiredNum + paramResult.requiredNum;
1000
+ const totalParams = totalRequiredParams + postBodyResult.optionalNum + paramResult.optionalNum;
1001
+ if (totalRequiredParams > 1) {
1002
+ if (!this.options.allowMultipleParameters ||
1003
+ totalRequiredParams > SMEValidator.SMERequiredParamsMaxNum) {
1004
+ result.reason.push(exports.ErrorType.ExceededRequiredParamsLimit);
1005
+ }
1006
+ }
1007
+ else if (totalParams === 0) {
1008
+ result.reason.push(exports.ErrorType.NoParameter);
1009
+ }
1010
+ return result;
1011
+ }
1012
+ }
1013
+ SMEValidator.SMERequiredParamsMaxNum = 5;
1014
+
1015
+ // Copyright (c) Microsoft Corporation.
1016
+ class TeamsAIValidator extends Validator {
1017
+ constructor(spec, options) {
1018
+ super();
1019
+ this.projectType = exports.ProjectType.TeamsAi;
1020
+ this.options = options;
1021
+ this.spec = spec;
1022
+ }
1023
+ validateSpec() {
1024
+ const result = { errors: [], warnings: [] };
1025
+ // validate spec server
1026
+ let validationResult = this.validateSpecServer();
1027
+ result.errors.push(...validationResult.errors);
1028
+ // validate no supported API
1029
+ validationResult = this.validateSpecNoSupportAPI();
1030
+ result.errors.push(...validationResult.errors);
1031
+ return result;
1032
+ }
1033
+ validateAPI(method, path) {
1034
+ const result = { isValid: true, reason: [] };
1035
+ method = method.toLocaleLowerCase();
1036
+ // validate method and path
1037
+ const methodAndPathResult = this.validateMethodAndPath(method, path);
1038
+ if (!methodAndPathResult.isValid) {
1039
+ return methodAndPathResult;
1040
+ }
1041
+ const operationObject = this.spec.paths[path][method];
1042
+ // validate operationId
1043
+ if (!this.options.allowMissingId && !operationObject.operationId) {
1044
+ result.reason.push(exports.ErrorType.MissingOperationId);
1045
+ }
1046
+ // validate server
1047
+ const validateServerResult = this.validateServer(method, path);
1048
+ result.reason.push(...validateServerResult.reason);
1049
+ if (result.reason.length > 0) {
1050
+ result.isValid = false;
1051
+ }
1052
+ return result;
1053
+ }
1054
+ }
1055
+
1056
+ class ValidatorFactory {
1057
+ static create(spec, options) {
1026
1058
  var _a;
1027
- return __awaiter(this, void 0, void 0, function* () {
1028
- const paths = spec.paths;
1029
- const commands = [];
1030
- const warnings = [];
1031
- if (paths) {
1032
- for (const pathUrl in paths) {
1033
- const pathItem = paths[pathUrl];
1034
- if (pathItem) {
1035
- const operations = pathItem;
1036
- // Currently only support GET and POST method
1037
- for (const method in operations) {
1038
- if ((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) {
1039
- const operationItem = operations[method];
1040
- if (operationItem) {
1041
- const [command, warning] = Utils.parseApiInfo(operationItem, options);
1042
- if (adaptiveCardFolder) {
1043
- const adaptiveCardPath = path__default['default'].join(adaptiveCardFolder, command.id + ".json");
1044
- command.apiResponseRenderingTemplateFile = (yield fs__default['default'].pathExists(adaptiveCardPath))
1045
- ? ManifestUpdater.getRelativePath(manifestPath, adaptiveCardPath)
1046
- : "";
1047
- }
1048
- if (warning) {
1049
- warnings.push(warning);
1050
- }
1051
- commands.push(command);
1052
- }
1053
- }
1059
+ const type = (_a = options.projectType) !== null && _a !== void 0 ? _a : exports.ProjectType.SME;
1060
+ switch (type) {
1061
+ case exports.ProjectType.SME:
1062
+ return new SMEValidator(spec, options);
1063
+ case exports.ProjectType.Copilot:
1064
+ return new CopilotValidator(spec, options);
1065
+ case exports.ProjectType.TeamsAi:
1066
+ return new TeamsAIValidator(spec, options);
1067
+ default:
1068
+ throw new Error(`Invalid project type: ${type}`);
1069
+ }
1070
+ }
1071
+ }
1072
+
1073
+ // Copyright (c) Microsoft Corporation.
1074
+ class SpecFilter {
1075
+ static specFilter(filter, unResolveSpec, resolvedSpec, options) {
1076
+ var _a;
1077
+ try {
1078
+ const newSpec = Object.assign({}, unResolveSpec);
1079
+ const newPaths = {};
1080
+ for (const filterItem of filter) {
1081
+ const [method, path] = filterItem.split(" ");
1082
+ const methodName = method.toLowerCase();
1083
+ const pathObj = (_a = resolvedSpec.paths) === null || _a === void 0 ? void 0 : _a[path];
1084
+ if (ConstantString.AllOperationMethods.includes(methodName) &&
1085
+ pathObj &&
1086
+ pathObj[methodName]) {
1087
+ const validator = ValidatorFactory.create(resolvedSpec, options);
1088
+ const validateResult = validator.validateAPI(methodName, path);
1089
+ if (!validateResult.isValid) {
1090
+ continue;
1091
+ }
1092
+ if (!newPaths[path]) {
1093
+ newPaths[path] = Object.assign({}, unResolveSpec.paths[path]);
1094
+ for (const m of ConstantString.AllOperationMethods) {
1095
+ delete newPaths[path][m];
1054
1096
  }
1055
1097
  }
1098
+ newPaths[path][methodName] = unResolveSpec.paths[path][methodName];
1099
+ // Add the operationId if missing
1100
+ if (!newPaths[path][methodName].operationId) {
1101
+ newPaths[path][methodName].operationId = `${methodName}${Utils.convertPathToCamelCase(path)}`;
1102
+ }
1056
1103
  }
1057
1104
  }
1058
- return [commands, warnings];
1059
- });
1060
- }
1061
- static getRelativePath(from, to) {
1062
- const relativePath = path__default['default'].relative(path__default['default'].dirname(from), to);
1063
- return path__default['default'].normalize(relativePath).replace(/\\/g, "/");
1105
+ newSpec.paths = newPaths;
1106
+ return newSpec;
1107
+ }
1108
+ catch (err) {
1109
+ throw new SpecParserError(err.toString(), exports.ErrorType.FilterSpecFailed);
1110
+ }
1064
1111
  }
1065
1112
  }
1066
1113
 
@@ -1068,7 +1115,7 @@ class ManifestUpdater {
1068
1115
  class AdaptiveCardGenerator {
1069
1116
  static generateAdaptiveCard(operationItem) {
1070
1117
  try {
1071
- const json = Utils.getResponseJson(operationItem);
1118
+ const { json } = Utils.getResponseJson(operationItem);
1072
1119
  let cardBody = [];
1073
1120
  let schema = json.schema;
1074
1121
  let jsonPath = "$";
@@ -1234,6 +1281,27 @@ function wrapAdaptiveCard(card, jsonPath) {
1234
1281
  };
1235
1282
  return result;
1236
1283
  }
1284
+ function wrapResponseSemantics(card, jsonPath) {
1285
+ const props = inferProperties(card);
1286
+ const dataPath = jsonPath === "$" ? "$" : "$." + jsonPath;
1287
+ const result = {
1288
+ data_path: dataPath,
1289
+ };
1290
+ if (props.title || props.subtitle || props.imageUrl) {
1291
+ result.properties = {};
1292
+ if (props.title) {
1293
+ result.properties.title = "$." + props.title;
1294
+ }
1295
+ if (props.subtitle) {
1296
+ result.properties.subtitle = "$." + props.subtitle;
1297
+ }
1298
+ if (props.imageUrl) {
1299
+ result.properties.url = "$." + props.imageUrl;
1300
+ }
1301
+ }
1302
+ result.static_template = card;
1303
+ return result;
1304
+ }
1237
1305
  /**
1238
1306
  * Infers the preview card template from an Adaptive Card and a JSON path.
1239
1307
  * The preview card template includes a title and an optional subtitle and image.
@@ -1246,11 +1314,29 @@ function wrapAdaptiveCard(card, jsonPath) {
1246
1314
  * @returns The inferred preview card template.
1247
1315
  */
1248
1316
  function inferPreviewCardTemplate(card) {
1249
- var _a;
1250
1317
  const result = {
1251
- title: "",
1318
+ title: "result",
1252
1319
  };
1253
- const textBlockElements = new Set();
1320
+ const inferredProperties = inferProperties(card);
1321
+ if (inferredProperties.title) {
1322
+ result.title = `\${if(${inferredProperties.title}, ${inferredProperties.title}, 'N/A')}`;
1323
+ }
1324
+ if (inferredProperties.subtitle) {
1325
+ result.subtitle = `\${if(${inferredProperties.subtitle}, ${inferredProperties.subtitle}, 'N/A')}`;
1326
+ }
1327
+ if (inferredProperties.imageUrl) {
1328
+ result.image = {
1329
+ url: `\${${inferredProperties.imageUrl}}`,
1330
+ alt: `\${if(${inferredProperties.imageUrl}, ${inferredProperties.imageUrl}, 'N/A')}`,
1331
+ $when: `\${${inferredProperties.imageUrl} != null}`,
1332
+ };
1333
+ }
1334
+ return result;
1335
+ }
1336
+ function inferProperties(card) {
1337
+ var _a;
1338
+ const result = {};
1339
+ const nameSet = new Set();
1254
1340
  let rootObject;
1255
1341
  if (((_a = card.body[0]) === null || _a === void 0 ? void 0 : _a.type) === ConstantString.ContainerType) {
1256
1342
  rootObject = card.body[0].items;
@@ -1263,56 +1349,407 @@ function inferPreviewCardTemplate(card) {
1263
1349
  const textElement = element;
1264
1350
  const index = textElement.text.indexOf("${if(");
1265
1351
  if (index > 0) {
1266
- textElement.text = textElement.text.substring(index);
1267
- textBlockElements.add(textElement);
1352
+ const text = textElement.text.substring(index);
1353
+ const match = text.match(/\${if\(([^,]+),/);
1354
+ const property = match ? match[1] : "";
1355
+ if (property) {
1356
+ nameSet.add(property);
1357
+ }
1358
+ }
1359
+ }
1360
+ else if (element.type === ConstantString.ImageType) {
1361
+ const imageElement = element;
1362
+ const match = imageElement.url.match(/\${([^,]+)}/);
1363
+ const property = match ? match[1] : "";
1364
+ if (property) {
1365
+ nameSet.add(property);
1268
1366
  }
1269
1367
  }
1270
1368
  }
1271
- for (const element of textBlockElements) {
1272
- const text = element.text;
1273
- if (!result.title && Utils.isWellKnownName(text, ConstantString.WellknownTitleName)) {
1274
- result.title = text;
1275
- textBlockElements.delete(element);
1369
+ for (const name of nameSet) {
1370
+ if (!result.title && Utils.isWellKnownName(name, ConstantString.WellknownTitleName)) {
1371
+ result.title = name;
1372
+ nameSet.delete(name);
1276
1373
  }
1277
1374
  else if (!result.subtitle &&
1278
- Utils.isWellKnownName(text, ConstantString.WellknownSubtitleName)) {
1279
- result.subtitle = text;
1280
- textBlockElements.delete(element);
1375
+ Utils.isWellKnownName(name, ConstantString.WellknownSubtitleName)) {
1376
+ result.subtitle = name;
1377
+ nameSet.delete(name);
1281
1378
  }
1282
- else if (!result.image && Utils.isWellKnownName(text, ConstantString.WellknownImageName)) {
1283
- const match = text.match(/\${if\(([^,]+),/);
1284
- const property = match ? match[1] : "";
1285
- if (property) {
1286
- result.image = {
1287
- url: `\${${property}}`,
1288
- alt: text,
1289
- $when: `\${${property} != null}`,
1290
- };
1291
- }
1292
- textBlockElements.delete(element);
1379
+ else if (!result.imageUrl && Utils.isWellKnownName(name, ConstantString.WellknownImageName)) {
1380
+ result.imageUrl = name;
1381
+ nameSet.delete(name);
1293
1382
  }
1294
1383
  }
1295
- for (const element of textBlockElements) {
1296
- const text = element.text;
1384
+ for (const name of nameSet) {
1297
1385
  if (!result.title) {
1298
- result.title = text;
1299
- textBlockElements.delete(element);
1386
+ result.title = name;
1387
+ nameSet.delete(name);
1300
1388
  }
1301
1389
  else if (!result.subtitle) {
1302
- result.subtitle = text;
1303
- textBlockElements.delete(element);
1390
+ result.subtitle = name;
1391
+ nameSet.delete(name);
1304
1392
  }
1305
1393
  }
1306
1394
  if (!result.title && result.subtitle) {
1307
1395
  result.title = result.subtitle;
1308
1396
  delete result.subtitle;
1309
1397
  }
1310
- if (!result.title) {
1311
- result.title = "result";
1312
- }
1313
1398
  return result;
1314
1399
  }
1315
1400
 
1401
+ // Copyright (c) Microsoft Corporation.
1402
+ class ManifestUpdater {
1403
+ static updateManifestWithAiPlugin(manifestPath, outputSpecPath, apiPluginFilePath, spec, options, authInfo) {
1404
+ return __awaiter(this, void 0, void 0, function* () {
1405
+ const manifest = yield fs__default['default'].readJSON(manifestPath);
1406
+ const apiPluginRelativePath = ManifestUpdater.getRelativePath(manifestPath, apiPluginFilePath);
1407
+ manifest.plugins = [
1408
+ {
1409
+ file: apiPluginRelativePath,
1410
+ id: ConstantString.DefaultPluginId,
1411
+ },
1412
+ ];
1413
+ const appName = this.removeEnvs(manifest.name.short);
1414
+ ManifestUpdater.updateManifestDescription(manifest, spec);
1415
+ const specRelativePath = ManifestUpdater.getRelativePath(manifestPath, outputSpecPath);
1416
+ const apiPlugin = yield ManifestUpdater.generatePluginManifestSchema(spec, specRelativePath, apiPluginFilePath, appName, authInfo, options);
1417
+ return [manifest, apiPlugin];
1418
+ });
1419
+ }
1420
+ static updateManifestDescription(manifest, spec) {
1421
+ var _a, _b;
1422
+ manifest.description = {
1423
+ short: spec.info.title.slice(0, ConstantString.ShortDescriptionMaxLens),
1424
+ full: (_b = ((_a = spec.info.description) !== null && _a !== void 0 ? _a : manifest.description.full)) === null || _b === void 0 ? void 0 : _b.slice(0, ConstantString.FullDescriptionMaxLens),
1425
+ };
1426
+ }
1427
+ static mapOpenAPISchemaToFuncParam(schema, method, pathUrl) {
1428
+ let parameter;
1429
+ if (schema.type === "array") {
1430
+ const items = schema.items;
1431
+ parameter = {
1432
+ type: "array",
1433
+ items: ManifestUpdater.mapOpenAPISchemaToFuncParam(items, method, pathUrl),
1434
+ };
1435
+ }
1436
+ else if (schema.type === "string" ||
1437
+ schema.type === "boolean" ||
1438
+ schema.type === "integer" ||
1439
+ schema.type === "number") {
1440
+ parameter = {
1441
+ type: schema.type,
1442
+ };
1443
+ }
1444
+ else {
1445
+ throw new SpecParserError(Utils.format(ConstantString.UnsupportedSchema, method, pathUrl, JSON.stringify(schema)), exports.ErrorType.UpdateManifestFailed);
1446
+ }
1447
+ if (schema.enum) {
1448
+ parameter.enum = schema.enum;
1449
+ }
1450
+ if (schema.description) {
1451
+ parameter.description = schema.description;
1452
+ }
1453
+ if (schema.default) {
1454
+ parameter.default = schema.default;
1455
+ }
1456
+ return parameter;
1457
+ }
1458
+ static generatePluginManifestSchema(spec, specRelativePath, apiPluginFilePath, appName, authInfo, options) {
1459
+ var _a, _b, _c, _d, _e;
1460
+ return __awaiter(this, void 0, void 0, function* () {
1461
+ const functions = [];
1462
+ const functionNames = [];
1463
+ const conversationStarters = [];
1464
+ const paths = spec.paths;
1465
+ const pluginAuthObj = {
1466
+ type: "None",
1467
+ };
1468
+ if (authInfo) {
1469
+ if (Utils.isOAuthWithAuthCodeFlow(authInfo.authScheme)) {
1470
+ pluginAuthObj.type = "OAuthPluginVault";
1471
+ }
1472
+ else if (Utils.isBearerTokenAuth(authInfo.authScheme)) {
1473
+ pluginAuthObj.type = "ApiKeyPluginVault";
1474
+ }
1475
+ if (pluginAuthObj.type !== "None") {
1476
+ pluginAuthObj.reference_id = `${Utils.getSafeRegistrationIdEnvName(authInfo.name)}_REGISTRATION_ID`;
1477
+ }
1478
+ }
1479
+ for (const pathUrl in paths) {
1480
+ const pathItem = paths[pathUrl];
1481
+ if (pathItem) {
1482
+ const operations = pathItem;
1483
+ for (const method in operations) {
1484
+ if (options.allowMethods.includes(method)) {
1485
+ const operationItem = operations[method];
1486
+ const confirmationBodies = [];
1487
+ if (operationItem) {
1488
+ const operationId = operationItem.operationId;
1489
+ const description = (_a = operationItem.description) !== null && _a !== void 0 ? _a : "";
1490
+ const paramObject = operationItem.parameters;
1491
+ const requestBody = operationItem.requestBody;
1492
+ const parameters = {
1493
+ type: "object",
1494
+ properties: {},
1495
+ required: [],
1496
+ };
1497
+ if (paramObject) {
1498
+ for (let i = 0; i < paramObject.length; i++) {
1499
+ const param = paramObject[i];
1500
+ const schema = param.schema;
1501
+ parameters.properties[param.name] = ManifestUpdater.mapOpenAPISchemaToFuncParam(schema, method, pathUrl);
1502
+ confirmationBodies.push(ManifestUpdater.getConfirmationBodyItem(param.name));
1503
+ if (param.required) {
1504
+ parameters.required.push(param.name);
1505
+ }
1506
+ if (!parameters.properties[param.name].description) {
1507
+ parameters.properties[param.name].description = (_b = param.description) !== null && _b !== void 0 ? _b : "";
1508
+ }
1509
+ }
1510
+ }
1511
+ if (requestBody) {
1512
+ const requestJsonBody = requestBody.content["application/json"];
1513
+ const requestBodySchema = requestJsonBody.schema;
1514
+ if (requestBodySchema.type === "object") {
1515
+ if (requestBodySchema.required) {
1516
+ parameters.required.push(...requestBodySchema.required);
1517
+ }
1518
+ for (const property in requestBodySchema.properties) {
1519
+ const schema = requestBodySchema.properties[property];
1520
+ parameters.properties[property] = ManifestUpdater.mapOpenAPISchemaToFuncParam(schema, method, pathUrl);
1521
+ confirmationBodies.push(ManifestUpdater.getConfirmationBodyItem(property));
1522
+ }
1523
+ }
1524
+ else {
1525
+ throw new SpecParserError(Utils.format(ConstantString.UnsupportedSchema, method, pathUrl, JSON.stringify(requestBodySchema)), exports.ErrorType.UpdateManifestFailed);
1526
+ }
1527
+ }
1528
+ const funcObj = {
1529
+ name: operationId,
1530
+ description: description,
1531
+ };
1532
+ if (paramObject || requestBody) {
1533
+ funcObj.parameters = parameters;
1534
+ }
1535
+ if (options.allowResponseSemantics) {
1536
+ const [card, jsonPath] = AdaptiveCardGenerator.generateAdaptiveCard(operationItem);
1537
+ const responseSemantic = wrapResponseSemantics(card, jsonPath);
1538
+ funcObj.capabilities = {
1539
+ response_semantics: responseSemantic,
1540
+ };
1541
+ }
1542
+ if (options.allowConfirmation && method !== ConstantString.GetMethod) {
1543
+ if (!funcObj.capabilities) {
1544
+ funcObj.capabilities = {};
1545
+ }
1546
+ funcObj.capabilities.confirmation = {
1547
+ type: "AdaptiveCard",
1548
+ title: (_c = operationItem.summary) !== null && _c !== void 0 ? _c : description,
1549
+ };
1550
+ if (confirmationBodies.length > 0) {
1551
+ funcObj.capabilities.confirmation.body = confirmationBodies.join("\n");
1552
+ }
1553
+ }
1554
+ functions.push(funcObj);
1555
+ functionNames.push(operationId);
1556
+ if (description) {
1557
+ conversationStarters.push(description);
1558
+ }
1559
+ }
1560
+ }
1561
+ }
1562
+ }
1563
+ }
1564
+ let apiPlugin;
1565
+ if (yield fs__default['default'].pathExists(apiPluginFilePath)) {
1566
+ apiPlugin = yield fs__default['default'].readJSON(apiPluginFilePath);
1567
+ }
1568
+ else {
1569
+ apiPlugin = {
1570
+ schema_version: "v2.1",
1571
+ name_for_human: "",
1572
+ description_for_human: "",
1573
+ namespace: "",
1574
+ functions: [],
1575
+ runtimes: [],
1576
+ };
1577
+ }
1578
+ apiPlugin.functions = apiPlugin.functions || [];
1579
+ for (const func of functions) {
1580
+ const index = (_d = apiPlugin.functions) === null || _d === void 0 ? void 0 : _d.findIndex((f) => f.name === func.name);
1581
+ if (index === -1) {
1582
+ apiPlugin.functions.push(func);
1583
+ }
1584
+ else {
1585
+ apiPlugin.functions[index] = func;
1586
+ }
1587
+ }
1588
+ apiPlugin.runtimes = apiPlugin.runtimes || [];
1589
+ const index = apiPlugin.runtimes.findIndex((runtime) => {
1590
+ var _a, _b;
1591
+ return runtime.spec.url === specRelativePath &&
1592
+ runtime.type === "OpenApi" &&
1593
+ ((_b = (_a = runtime.auth) === null || _a === void 0 ? void 0 : _a.type) !== null && _b !== void 0 ? _b : "None") === pluginAuthObj.type;
1594
+ });
1595
+ if (index === -1) {
1596
+ apiPlugin.runtimes.push({
1597
+ type: "OpenApi",
1598
+ auth: pluginAuthObj,
1599
+ spec: {
1600
+ url: specRelativePath,
1601
+ },
1602
+ run_for_functions: functionNames,
1603
+ });
1604
+ }
1605
+ else {
1606
+ apiPlugin.runtimes[index].run_for_functions = functionNames;
1607
+ }
1608
+ if (!apiPlugin.name_for_human) {
1609
+ apiPlugin.name_for_human = appName;
1610
+ }
1611
+ if (!apiPlugin.namespace) {
1612
+ apiPlugin.namespace = ManifestUpdater.removeAllSpecialCharacters(appName);
1613
+ }
1614
+ if (!apiPlugin.description_for_human) {
1615
+ apiPlugin.description_for_human =
1616
+ (_e = spec.info.description) !== null && _e !== void 0 ? _e : "<Please add description of the plugin>";
1617
+ }
1618
+ if (options.allowConversationStarters && conversationStarters.length > 0) {
1619
+ if (!apiPlugin.capabilities) {
1620
+ apiPlugin.capabilities = {
1621
+ localization: {},
1622
+ };
1623
+ }
1624
+ if (!apiPlugin.capabilities.conversation_starters) {
1625
+ apiPlugin.capabilities.conversation_starters = conversationStarters
1626
+ .slice(0, 5)
1627
+ .map((text) => ({ text }));
1628
+ }
1629
+ }
1630
+ return apiPlugin;
1631
+ });
1632
+ }
1633
+ static updateManifest(manifestPath, outputSpecPath, spec, options, adaptiveCardFolder, authInfo) {
1634
+ return __awaiter(this, void 0, void 0, function* () {
1635
+ try {
1636
+ const originalManifest = yield fs__default['default'].readJSON(manifestPath);
1637
+ const updatedPart = {};
1638
+ updatedPart.composeExtensions = [];
1639
+ let warnings = [];
1640
+ if (options.projectType === exports.ProjectType.SME) {
1641
+ const updateResult = yield ManifestUpdater.generateCommands(spec, manifestPath, options, adaptiveCardFolder);
1642
+ const commands = updateResult[0];
1643
+ warnings = updateResult[1];
1644
+ const composeExtension = {
1645
+ composeExtensionType: "apiBased",
1646
+ apiSpecificationFile: ManifestUpdater.getRelativePath(manifestPath, outputSpecPath),
1647
+ commands: commands,
1648
+ };
1649
+ if (authInfo) {
1650
+ const auth = authInfo.authScheme;
1651
+ if (Utils.isAPIKeyAuth(auth) || Utils.isBearerTokenAuth(auth)) {
1652
+ const safeApiSecretRegistrationId = Utils.getSafeRegistrationIdEnvName(`${authInfo.name}_${ConstantString.RegistrationIdPostfix}`);
1653
+ composeExtension.authorization = {
1654
+ authType: "apiSecretServiceAuth",
1655
+ apiSecretServiceAuthConfiguration: {
1656
+ apiSecretRegistrationId: `\${{${safeApiSecretRegistrationId}}}`,
1657
+ },
1658
+ };
1659
+ }
1660
+ else if (Utils.isOAuthWithAuthCodeFlow(auth)) {
1661
+ const safeOAuth2RegistrationId = Utils.getSafeRegistrationIdEnvName(`${authInfo.name}_${ConstantString.OAuthRegistrationIdPostFix}`);
1662
+ composeExtension.authorization = {
1663
+ authType: "oAuth2.0",
1664
+ oAuthConfiguration: {
1665
+ oauthConfigurationId: `\${{${safeOAuth2RegistrationId}}}`,
1666
+ },
1667
+ };
1668
+ updatedPart.webApplicationInfo = {
1669
+ id: "${{AAD_APP_CLIENT_ID}}",
1670
+ resource: "api://${{DOMAIN}}/${{AAD_APP_CLIENT_ID}}",
1671
+ };
1672
+ }
1673
+ }
1674
+ updatedPart.composeExtensions = [composeExtension];
1675
+ }
1676
+ updatedPart.description = originalManifest.description;
1677
+ ManifestUpdater.updateManifestDescription(updatedPart, spec);
1678
+ const updatedManifest = Object.assign(Object.assign({}, originalManifest), updatedPart);
1679
+ return [updatedManifest, warnings];
1680
+ }
1681
+ catch (err) {
1682
+ throw new SpecParserError(err.toString(), exports.ErrorType.UpdateManifestFailed);
1683
+ }
1684
+ });
1685
+ }
1686
+ static generateCommands(spec, manifestPath, options, adaptiveCardFolder) {
1687
+ var _a;
1688
+ return __awaiter(this, void 0, void 0, function* () {
1689
+ const paths = spec.paths;
1690
+ const commands = [];
1691
+ const warnings = [];
1692
+ if (paths) {
1693
+ for (const pathUrl in paths) {
1694
+ const pathItem = paths[pathUrl];
1695
+ if (pathItem) {
1696
+ const operations = pathItem;
1697
+ // Currently only support GET and POST method
1698
+ for (const method in operations) {
1699
+ if ((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) {
1700
+ const operationItem = operations[method];
1701
+ if (operationItem) {
1702
+ const command = Utils.parseApiInfo(operationItem, options);
1703
+ if (command.parameters &&
1704
+ command.parameters.length >= 1 &&
1705
+ command.parameters.some((param) => param.isRequired)) {
1706
+ command.parameters = command.parameters.filter((param) => param.isRequired);
1707
+ }
1708
+ else if (command.parameters && command.parameters.length > 0) {
1709
+ command.parameters = [command.parameters[0]];
1710
+ warnings.push({
1711
+ type: exports.WarningType.OperationOnlyContainsOptionalParam,
1712
+ content: Utils.format(ConstantString.OperationOnlyContainsOptionalParam, command.id),
1713
+ data: command.id,
1714
+ });
1715
+ }
1716
+ if (adaptiveCardFolder) {
1717
+ const adaptiveCardPath = path__default['default'].join(adaptiveCardFolder, command.id + ".json");
1718
+ command.apiResponseRenderingTemplateFile = (yield fs__default['default'].pathExists(adaptiveCardPath))
1719
+ ? ManifestUpdater.getRelativePath(manifestPath, adaptiveCardPath)
1720
+ : "";
1721
+ }
1722
+ commands.push(command);
1723
+ }
1724
+ }
1725
+ }
1726
+ }
1727
+ }
1728
+ }
1729
+ return [commands, warnings];
1730
+ });
1731
+ }
1732
+ static getRelativePath(from, to) {
1733
+ const relativePath = path__default['default'].relative(path__default['default'].dirname(from), to);
1734
+ return path__default['default'].normalize(relativePath).replace(/\\/g, "/");
1735
+ }
1736
+ static removeEnvs(str) {
1737
+ const placeHolderReg = /\${{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*}}/g;
1738
+ const matches = placeHolderReg.exec(str);
1739
+ let newStr = str;
1740
+ if (matches != null) {
1741
+ newStr = newStr.replace(matches[0], "");
1742
+ }
1743
+ return newStr;
1744
+ }
1745
+ static removeAllSpecialCharacters(str) {
1746
+ return str.toLowerCase().replace(/[^a-z0-9]/g, "");
1747
+ }
1748
+ static getConfirmationBodyItem(paramName) {
1749
+ return `* **${Utils.updateFirstLetter(paramName)}**: {{function.parameters.${paramName}}}`;
1750
+ }
1751
+ }
1752
+
1316
1753
  // Copyright (c) Microsoft Corporation.
1317
1754
  /**
1318
1755
  * A class that parses an OpenAPI specification file and provides methods to validate, list, and generate artifacts.
@@ -1332,6 +1769,9 @@ class SpecParser {
1332
1769
  allowMultipleParameters: false,
1333
1770
  allowOauth2: false,
1334
1771
  allowMethods: ["get", "post"],
1772
+ allowConversationStarters: false,
1773
+ allowResponseSemantics: false,
1774
+ allowConfirmation: false,
1335
1775
  projectType: exports.ProjectType.SME,
1336
1776
  };
1337
1777
  this.pathOrSpec = pathOrDoc;
@@ -1357,6 +1797,8 @@ class SpecParser {
1357
1797
  errors: [{ type: exports.ErrorType.SpecNotValid, content: e.toString() }],
1358
1798
  };
1359
1799
  }
1800
+ const errors = [];
1801
+ const warnings = [];
1360
1802
  if (!this.options.allowSwagger && this.isSwaggerFile) {
1361
1803
  return {
1362
1804
  status: exports.ValidationStatus.Error,
@@ -1366,7 +1808,38 @@ class SpecParser {
1366
1808
  ],
1367
1809
  };
1368
1810
  }
1369
- return Utils.validateSpec(this.spec, this.parser, !!this.isSwaggerFile, this.options);
1811
+ // Remote reference not supported
1812
+ const refPaths = this.parser.$refs.paths();
1813
+ // refPaths [0] is the current spec file path
1814
+ if (refPaths.length > 1) {
1815
+ errors.push({
1816
+ type: exports.ErrorType.RemoteRefNotSupported,
1817
+ content: Utils.format(ConstantString.RemoteRefNotSupported, refPaths.join(", ")),
1818
+ data: refPaths,
1819
+ });
1820
+ }
1821
+ if (!!this.isSwaggerFile && this.options.allowSwagger) {
1822
+ warnings.push({
1823
+ type: exports.WarningType.ConvertSwaggerToOpenAPI,
1824
+ content: ConstantString.ConvertSwaggerToOpenAPI,
1825
+ });
1826
+ }
1827
+ const validator = this.getValidator(this.spec);
1828
+ const validationResult = validator.validateSpec();
1829
+ warnings.push(...validationResult.warnings);
1830
+ errors.push(...validationResult.errors);
1831
+ let status = exports.ValidationStatus.Valid;
1832
+ if (warnings.length > 0 && errors.length === 0) {
1833
+ status = exports.ValidationStatus.Warning;
1834
+ }
1835
+ else if (errors.length > 0) {
1836
+ status = exports.ValidationStatus.Error;
1837
+ }
1838
+ return {
1839
+ status: status,
1840
+ warnings: warnings,
1841
+ errors: errors,
1842
+ };
1370
1843
  }
1371
1844
  catch (err) {
1372
1845
  throw new SpecParserError(err.toString(), exports.ErrorType.ValidateFailed);
@@ -1390,39 +1863,40 @@ class SpecParser {
1390
1863
  try {
1391
1864
  yield this.loadSpec();
1392
1865
  const spec = this.spec;
1393
- const apiMap = this.getAllSupportedAPIs(spec);
1394
- const result = [];
1866
+ const apiMap = this.getAPIs(spec);
1867
+ const result = {
1868
+ APIs: [],
1869
+ allAPICount: 0,
1870
+ validAPICount: 0,
1871
+ };
1395
1872
  for (const apiKey in apiMap) {
1873
+ const { operation, isValid, reason } = apiMap[apiKey];
1874
+ const [method, path] = apiKey.split(" ");
1875
+ const operationId = (_a = operation.operationId) !== null && _a !== void 0 ? _a : `${method.toLowerCase()}${Utils.convertPathToCamelCase(path)}`;
1396
1876
  const apiResult = {
1397
- api: "",
1877
+ api: apiKey,
1398
1878
  server: "",
1399
- operationId: "",
1879
+ operationId: operationId,
1880
+ isValid: isValid,
1881
+ reason: reason,
1400
1882
  };
1401
- const [method, path] = apiKey.split(" ");
1402
- const operation = apiMap[apiKey];
1403
- const rootServer = spec.servers && spec.servers[0];
1404
- const methodServer = spec.paths[path].servers && ((_a = spec.paths[path]) === null || _a === void 0 ? void 0 : _a.servers[0]);
1405
- const operationServer = operation.servers && operation.servers[0];
1406
- const serverUrl = operationServer || methodServer || rootServer;
1407
- if (!serverUrl) {
1408
- throw new SpecParserError(ConstantString.NoServerInformation, exports.ErrorType.NoServerInformation);
1409
- }
1410
- apiResult.server = Utils.resolveServerUrl(serverUrl.url);
1411
- let operationId = operation.operationId;
1412
- if (!operationId) {
1413
- operationId = `${method.toLowerCase()}${Utils.convertPathToCamelCase(path)}`;
1414
- }
1415
- apiResult.operationId = operationId;
1416
- const authArray = Utils.getAuthArray(operation.security, spec);
1417
- for (const auths of authArray) {
1418
- if (auths.length === 1) {
1419
- apiResult.auth = auths[0].authScheme;
1420
- break;
1883
+ if (isValid) {
1884
+ const serverObj = Utils.getServerObject(spec, method.toLocaleLowerCase(), path);
1885
+ if (serverObj) {
1886
+ apiResult.server = Utils.resolveEnv(serverObj.url);
1887
+ }
1888
+ const authArray = Utils.getAuthArray(operation.security, spec);
1889
+ for (const auths of authArray) {
1890
+ if (auths.length === 1) {
1891
+ apiResult.auth = auths[0];
1892
+ break;
1893
+ }
1421
1894
  }
1422
1895
  }
1423
- apiResult.api = apiKey;
1424
- result.push(apiResult);
1896
+ result.APIs.push(apiResult);
1425
1897
  }
1898
+ result.allAPICount = result.APIs.length;
1899
+ result.validAPICount = result.APIs.filter((api) => api.isValid).length;
1426
1900
  return result;
1427
1901
  }
1428
1902
  catch (err) {
@@ -1479,18 +1953,12 @@ class SpecParser {
1479
1953
  const newSpecs = yield this.getFilteredSpecs(filter, signal);
1480
1954
  const newUnResolvedSpec = newSpecs[0];
1481
1955
  const newSpec = newSpecs[1];
1482
- let resultStr;
1483
- if (outputSpecPath.endsWith(".yaml") || outputSpecPath.endsWith(".yml")) {
1484
- resultStr = jsyaml__default['default'].dump(newUnResolvedSpec);
1485
- }
1486
- else {
1487
- resultStr = JSON.stringify(newUnResolvedSpec, null, 2);
1488
- }
1489
- yield fs__default['default'].outputFile(outputSpecPath, resultStr);
1956
+ const authInfo = Utils.getAuthInfo(newSpec);
1957
+ yield this.saveFilterSpec(outputSpecPath, newUnResolvedSpec);
1490
1958
  if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
1491
1959
  throw new SpecParserError(ConstantString.CancelledMessage, exports.ErrorType.Cancelled);
1492
1960
  }
1493
- const [updatedManifest, apiPlugin] = yield ManifestUpdater.updateManifestWithAiPlugin(manifestPath, outputSpecPath, pluginFilePath, newSpec, this.options);
1961
+ const [updatedManifest, apiPlugin] = yield ManifestUpdater.updateManifestWithAiPlugin(manifestPath, outputSpecPath, pluginFilePath, newSpec, this.options, authInfo);
1494
1962
  yield fs__default['default'].outputJSON(manifestPath, updatedManifest, { spaces: 2 });
1495
1963
  yield fs__default['default'].outputJSON(pluginFilePath, apiPlugin, { spaces: 2 });
1496
1964
  }
@@ -1520,32 +1988,11 @@ class SpecParser {
1520
1988
  const newSpecs = yield this.getFilteredSpecs(filter, signal);
1521
1989
  const newUnResolvedSpec = newSpecs[0];
1522
1990
  const newSpec = newSpecs[1];
1523
- const authSet = new Set();
1524
- let hasMultipleAuth = false;
1525
- for (const url in newSpec.paths) {
1526
- for (const method in newSpec.paths[url]) {
1527
- const operation = newSpec.paths[url][method];
1528
- const authArray = Utils.getAuthArray(operation.security, newSpec);
1529
- if (authArray && authArray.length > 0) {
1530
- authSet.add(authArray[0][0]);
1531
- if (authSet.size > 1) {
1532
- hasMultipleAuth = true;
1533
- break;
1534
- }
1535
- }
1536
- }
1537
- }
1538
- if (hasMultipleAuth && this.options.projectType !== exports.ProjectType.TeamsAi) {
1539
- throw new SpecParserError(ConstantString.MultipleAuthNotSupported, exports.ErrorType.MultipleAuthNotSupported);
1540
- }
1541
- let resultStr;
1542
- if (outputSpecPath.endsWith(".yaml") || outputSpecPath.endsWith(".yml")) {
1543
- resultStr = jsyaml__default['default'].dump(newUnResolvedSpec);
1991
+ let authInfo = undefined;
1992
+ if (this.options.projectType === exports.ProjectType.SME) {
1993
+ authInfo = Utils.getAuthInfo(newSpec);
1544
1994
  }
1545
- else {
1546
- resultStr = JSON.stringify(newUnResolvedSpec, null, 2);
1547
- }
1548
- yield fs__default['default'].outputFile(outputSpecPath, resultStr);
1995
+ yield this.saveFilterSpec(outputSpecPath, newUnResolvedSpec);
1549
1996
  if (adaptiveCardFolder) {
1550
1997
  for (const url in newSpec.paths) {
1551
1998
  for (const method in newSpec.paths[url]) {
@@ -1575,7 +2022,6 @@ class SpecParser {
1575
2022
  if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
1576
2023
  throw new SpecParserError(ConstantString.CancelledMessage, exports.ErrorType.Cancelled);
1577
2024
  }
1578
- const authInfo = Array.from(authSet)[0];
1579
2025
  const [updatedManifest, warnings] = yield ManifestUpdater.updateManifest(manifestPath, outputSpecPath, newSpec, this.options, adaptiveCardFolder, authInfo);
1580
2026
  yield fs__default['default'].outputJSON(manifestPath, updatedManifest, { spaces: 2 });
1581
2027
  result.warnings.push(...warnings);
@@ -1604,13 +2050,30 @@ class SpecParser {
1604
2050
  }
1605
2051
  });
1606
2052
  }
1607
- getAllSupportedAPIs(spec) {
1608
- if (this.apiMap !== undefined) {
1609
- return this.apiMap;
2053
+ getAPIs(spec) {
2054
+ const validator = this.getValidator(spec);
2055
+ const apiMap = validator.listAPIs();
2056
+ return apiMap;
2057
+ }
2058
+ getValidator(spec) {
2059
+ if (this.validator) {
2060
+ return this.validator;
1610
2061
  }
1611
- const result = Utils.listSupportedAPIs(spec, this.options);
1612
- this.apiMap = result;
1613
- return result;
2062
+ const validator = ValidatorFactory.create(spec, this.options);
2063
+ this.validator = validator;
2064
+ return validator;
2065
+ }
2066
+ saveFilterSpec(outputSpecPath, unResolvedSpec) {
2067
+ return __awaiter(this, void 0, void 0, function* () {
2068
+ let resultStr;
2069
+ if (outputSpecPath.endsWith(".yaml") || outputSpecPath.endsWith(".yml")) {
2070
+ resultStr = jsyaml__default['default'].dump(unResolvedSpec);
2071
+ }
2072
+ else {
2073
+ resultStr = JSON.stringify(unResolvedSpec, null, 2);
2074
+ }
2075
+ yield fs__default['default'].outputFile(outputSpecPath, resultStr);
2076
+ });
1614
2077
  }
1615
2078
  }
1616
2079