@microsoft/m365-spec-parser 0.1.1-alpha.fb5afedc0.0 → 0.1.1-alpha.fbb412c19.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 = {}));
@@ -109,7 +125,7 @@ ConstantString.RemoteRefNotSupported = "Remote reference is not supported: %s.";
109
125
  ConstantString.MissingOperationId = "Missing operationIds: %s.";
110
126
  ConstantString.NoSupportedApi = "No supported API is found in the OpenAPI description document: only GET and POST methods are supported, additionally, there can be at most one required parameter, and no auth is allowed.";
111
127
  ConstantString.AdditionalPropertiesNotSupported = "'additionalProperties' is not supported, and will be ignored.";
112
- ConstantString.SchemaNotSupported = "'oneOf', 'anyOf', and 'not' schema are not supported: %s.";
128
+ ConstantString.SchemaNotSupported = "'oneOf', 'allOf', 'anyOf', and 'not' schema are not supported: %s.";
113
129
  ConstantString.UnknownSchema = "Unknown schema: %s.";
114
130
  ConstantString.UrlProtocolNotSupported = "Server url is not correct: protocol %s is not supported, you should use https protocol instead.";
115
131
  ConstantString.RelativeServerUrlNotSupported = "Server url is not correct: relative server url is not supported.";
@@ -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,9 +145,14 @@ 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
- ConstantString.RegistrationIdPostfix = "REGISTRATION_ID";
133
- ConstantString.OAuthRegistrationIdPostFix = "OAUTH_REGISTRATION_ID";
150
+ ConstantString.RegistrationIdPostfix = {
151
+ apiKey: "REGISTRATION_ID",
152
+ oauth2: "CONFIGURATION_ID",
153
+ http: "REGISTRATION_ID",
154
+ openIdConnect: "REGISTRATION_ID",
155
+ };
134
156
  ConstantString.ResponseCodeFor20X = [
135
157
  "200",
136
158
  "201",
@@ -189,9 +211,11 @@ ConstantString.ShortDescriptionMaxLens = 80;
189
211
  ConstantString.FullDescriptionMaxLens = 4000;
190
212
  ConstantString.CommandDescriptionMaxLens = 128;
191
213
  ConstantString.ParameterDescriptionMaxLens = 128;
214
+ ConstantString.ConversationStarterMaxLens = 50;
192
215
  ConstantString.CommandTitleMaxLens = 32;
193
216
  ConstantString.ParameterTitleMaxLens = 32;
194
- ConstantString.SMERequiredParamsMaxNum = 5;
217
+ ConstantString.SMERequiredParamsMaxNum = 5;
218
+ ConstantString.DefaultPluginId = "plugin_1";
195
219
 
196
220
  // Copyright (c) Microsoft Corporation.
197
221
  class SpecParserError extends Error {
@@ -214,221 +238,9 @@ class Utils {
214
238
  }
215
239
  return false;
216
240
  }
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
241
  static containMultipleMediaTypes(bodyObject) {
316
242
  return Object.keys((bodyObject === null || bodyObject === void 0 ? void 0 : bodyObject.content) || {}).length > 1;
317
243
  }
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
244
  static isBearerTokenAuth(authScheme) {
433
245
  return authScheme.type === "http" && authScheme.scheme === "bearer";
434
246
  }
@@ -436,18 +248,18 @@ class Utils {
436
248
  return authScheme.type === "apiKey";
437
249
  }
438
250
  static isOAuthWithAuthCodeFlow(authScheme) {
439
- if (authScheme.type === "oauth2" && authScheme.flows && authScheme.flows.authorizationCode) {
440
- return true;
441
- }
442
- return false;
251
+ return !!(authScheme.type === "oauth2" &&
252
+ authScheme.flows &&
253
+ authScheme.flows.authorizationCode);
443
254
  }
444
255
  static getAuthArray(securities, spec) {
445
256
  var _a;
446
257
  const result = [];
447
258
  const securitySchemas = (_a = spec.components) === null || _a === void 0 ? void 0 : _a.securitySchemes;
448
- if (securities && securitySchemas) {
449
- for (let i = 0; i < securities.length; i++) {
450
- const security = securities[i];
259
+ const securitiesArr = securities !== null && securities !== void 0 ? securities : spec.security;
260
+ if (securitiesArr && securitySchemas) {
261
+ for (let i = 0; i < securitiesArr.length; i++) {
262
+ const security = securitiesArr[i];
451
263
  const authArray = [];
452
264
  for (const name in security) {
453
265
  const auth = securitySchemas[name];
@@ -464,17 +276,39 @@ class Utils {
464
276
  result.sort((a, b) => a[0].name.localeCompare(b[0].name));
465
277
  return result;
466
278
  }
279
+ static getAuthInfo(spec) {
280
+ let authInfo = undefined;
281
+ for (const url in spec.paths) {
282
+ for (const method in spec.paths[url]) {
283
+ const operation = spec.paths[url][method];
284
+ const authArray = Utils.getAuthArray(operation.security, spec);
285
+ if (authArray && authArray.length > 0) {
286
+ const currentAuth = authArray[0][0];
287
+ if (!authInfo) {
288
+ authInfo = authArray[0][0];
289
+ }
290
+ else if (authInfo.name !== currentAuth.name) {
291
+ throw new SpecParserError(ConstantString.MultipleAuthNotSupported, exports.ErrorType.MultipleAuthNotSupported);
292
+ }
293
+ }
294
+ }
295
+ }
296
+ return authInfo;
297
+ }
467
298
  static updateFirstLetter(str) {
468
299
  return str.charAt(0).toUpperCase() + str.slice(1);
469
300
  }
470
- static getResponseJson(operationObject, isTeamsAiProject = false) {
301
+ static getResponseJson(operationObject) {
471
302
  var _a, _b;
472
303
  let json = {};
304
+ let multipleMediaType = false;
473
305
  for (const code of ConstantString.ResponseCodeFor20X) {
474
306
  const responseObject = (_a = operationObject === null || operationObject === void 0 ? void 0 : operationObject.responses) === null || _a === void 0 ? void 0 : _a[code];
475
307
  if ((_b = responseObject === null || responseObject === void 0 ? void 0 : responseObject.content) === null || _b === void 0 ? void 0 : _b["application/json"]) {
308
+ multipleMediaType = false;
476
309
  json = responseObject.content["application/json"];
477
- if (!isTeamsAiProject && Utils.containMultipleMediaTypes(responseObject)) {
310
+ if (Utils.containMultipleMediaTypes(responseObject)) {
311
+ multipleMediaType = true;
478
312
  json = {};
479
313
  }
480
314
  else {
@@ -482,7 +316,7 @@ class Utils {
482
316
  }
483
317
  }
484
318
  }
485
- return json;
319
+ return { json, multipleMediaType };
486
320
  }
487
321
  static convertPathToCamelCase(path) {
488
322
  const pathSegments = path.split(/[./{]/);
@@ -502,10 +336,10 @@ class Utils {
502
336
  return undefined;
503
337
  }
504
338
  }
505
- static resolveServerUrl(url) {
339
+ static resolveEnv(str) {
506
340
  const placeHolderReg = /\${{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*}}/g;
507
- let matches = placeHolderReg.exec(url);
508
- let newUrl = url;
341
+ let matches = placeHolderReg.exec(str);
342
+ let newStr = str;
509
343
  while (matches != null) {
510
344
  const envVar = matches[1];
511
345
  const envVal = process.env[envVar];
@@ -513,17 +347,17 @@ class Utils {
513
347
  throw new Error(Utils.format(ConstantString.ResolveServerUrlFailed, envVar));
514
348
  }
515
349
  else {
516
- newUrl = newUrl.replace(matches[0], envVal);
350
+ newStr = newStr.replace(matches[0], envVal);
517
351
  }
518
- matches = placeHolderReg.exec(url);
352
+ matches = placeHolderReg.exec(str);
519
353
  }
520
- return newUrl;
354
+ return newStr;
521
355
  }
522
356
  static checkServerUrl(servers) {
523
357
  const errors = [];
524
358
  let serverUrl;
525
359
  try {
526
- serverUrl = Utils.resolveServerUrl(servers[0].url);
360
+ serverUrl = Utils.resolveEnv(servers[0].url);
527
361
  }
528
362
  catch (err) {
529
363
  errors.push({
@@ -554,6 +388,7 @@ class Utils {
554
388
  return errors;
555
389
  }
556
390
  static validateServer(spec, options) {
391
+ var _a;
557
392
  const errors = [];
558
393
  let hasTopLevelServers = false;
559
394
  let hasPathLevelServers = false;
@@ -574,7 +409,7 @@ class Utils {
574
409
  }
575
410
  for (const method in methods) {
576
411
  const operationObject = methods[method];
577
- if (Utils.isSupportedApi(method, path, spec, options)) {
412
+ if (((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && operationObject) {
578
413
  if ((operationObject === null || operationObject === void 0 ? void 0 : operationObject.servers) && operationObject.servers.length >= 1) {
579
414
  hasOperationLevelServers = true;
580
415
  const serverErrors = Utils.checkServerUrl(operationObject.servers);
@@ -701,13 +536,7 @@ class Utils {
701
536
  }
702
537
  }
703
538
  const operationId = operationItem.operationId;
704
- const parameters = [];
705
- if (requiredParams.length !== 0) {
706
- parameters.push(...requiredParams);
707
- }
708
- else {
709
- parameters.push(optionalParams[0]);
710
- }
539
+ const parameters = [...requiredParams, ...optionalParams];
711
540
  const command = {
712
541
  context: ["compose"],
713
542
  type: "query",
@@ -716,353 +545,575 @@ class Utils {
716
545
  parameters: parameters,
717
546
  description: ((_b = operationItem.description) !== null && _b !== void 0 ? _b : "").slice(0, ConstantString.CommandDescriptionMaxLens),
718
547
  };
719
- let warning = undefined;
720
- if (requiredParams.length === 0 && optionalParams.length > 1) {
721
- warning = {
722
- type: exports.WarningType.OperationOnlyContainsOptionalParam,
723
- content: Utils.format(ConstantString.OperationOnlyContainsOptionalParam, operationId),
724
- data: operationId,
725
- };
548
+ return command;
549
+ }
550
+ static format(str, ...args) {
551
+ let index = 0;
552
+ return str.replace(/%s/g, () => {
553
+ const arg = args[index++];
554
+ return arg !== undefined ? arg : "";
555
+ });
556
+ }
557
+ static getSafeRegistrationIdEnvName(authName) {
558
+ if (!authName) {
559
+ return "";
726
560
  }
727
- return [command, warning];
561
+ let safeRegistrationIdEnvName = authName.toUpperCase().replace(/[^A-Z0-9_]/g, "_");
562
+ if (!safeRegistrationIdEnvName.match(/^[A-Z]/)) {
563
+ safeRegistrationIdEnvName = "PREFIX_" + safeRegistrationIdEnvName;
564
+ }
565
+ return safeRegistrationIdEnvName;
728
566
  }
729
- static listSupportedAPIs(spec, options) {
730
- const paths = spec.paths;
567
+ static getServerObject(spec, method, path) {
568
+ const pathObj = spec.paths[path];
569
+ const operationObject = pathObj[method];
570
+ const rootServer = spec.servers && spec.servers[0];
571
+ const methodServer = spec.paths[path].servers && spec.paths[path].servers[0];
572
+ const operationServer = operationObject.servers && operationObject.servers[0];
573
+ const serverUrl = operationServer || methodServer || rootServer;
574
+ return serverUrl;
575
+ }
576
+ }
577
+
578
+ // Copyright (c) Microsoft Corporation.
579
+ class Validator {
580
+ listAPIs() {
581
+ var _a;
582
+ if (this.apiMap) {
583
+ return this.apiMap;
584
+ }
585
+ const paths = this.spec.paths;
731
586
  const result = {};
732
587
  for (const path in paths) {
733
588
  const methods = paths[path];
734
589
  for (const method in methods) {
735
- if (Utils.isSupportedApi(method, path, spec, options)) {
736
- const operationObject = methods[method];
737
- result[`${method.toUpperCase()} ${path}`] = operationObject;
590
+ const operationObject = methods[method];
591
+ if (((_a = this.options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && operationObject) {
592
+ const validateResult = this.validateAPI(method, path);
593
+ result[`${method.toUpperCase()} ${path}`] = {
594
+ operation: operationObject,
595
+ isValid: validateResult.isValid,
596
+ reason: validateResult.reason,
597
+ };
738
598
  }
739
599
  }
740
600
  }
601
+ this.apiMap = result;
741
602
  return result;
742
603
  }
743
- static validateSpec(spec, parser, isSwaggerFile, options) {
744
- const errors = [];
745
- const warnings = [];
746
- if (isSwaggerFile) {
747
- warnings.push({
748
- type: exports.WarningType.ConvertSwaggerToOpenAPI,
749
- content: ConstantString.ConvertSwaggerToOpenAPI,
604
+ validateSpecVersion() {
605
+ const result = { errors: [], warnings: [] };
606
+ if (this.spec.openapi >= "3.1.0") {
607
+ result.errors.push({
608
+ type: exports.ErrorType.SpecVersionNotSupported,
609
+ content: Utils.format(ConstantString.SpecVersionNotSupported, this.spec.openapi),
610
+ data: this.spec.openapi,
750
611
  });
751
612
  }
752
- // Server validation
753
- const serverErrors = Utils.validateServer(spec, options);
754
- errors.push(...serverErrors);
755
- // Remote reference not supported
756
- const refPaths = parser.$refs.paths();
757
- // refPaths [0] is the current spec file path
758
- if (refPaths.length > 1) {
759
- errors.push({
760
- type: exports.ErrorType.RemoteRefNotSupported,
761
- content: Utils.format(ConstantString.RemoteRefNotSupported, refPaths.join(", ")),
762
- data: refPaths,
763
- });
764
- }
765
- // No supported API
766
- const apiMap = Utils.listSupportedAPIs(spec, options);
767
- if (Object.keys(apiMap).length === 0) {
768
- errors.push({
613
+ return result;
614
+ }
615
+ validateSpecServer() {
616
+ const result = { errors: [], warnings: [] };
617
+ const serverErrors = Utils.validateServer(this.spec, this.options);
618
+ result.errors.push(...serverErrors);
619
+ return result;
620
+ }
621
+ validateSpecNoSupportAPI() {
622
+ const result = { errors: [], warnings: [] };
623
+ const apiMap = this.listAPIs();
624
+ const validAPIs = Object.entries(apiMap).filter(([, value]) => value.isValid);
625
+ if (validAPIs.length === 0) {
626
+ const data = [];
627
+ for (const key in apiMap) {
628
+ const { reason } = apiMap[key];
629
+ const apiInvalidReason = { api: key, reason: reason };
630
+ data.push(apiInvalidReason);
631
+ }
632
+ result.errors.push({
769
633
  type: exports.ErrorType.NoSupportedApi,
770
634
  content: ConstantString.NoSupportedApi,
635
+ data,
771
636
  });
772
637
  }
638
+ return result;
639
+ }
640
+ validateSpecOperationId() {
641
+ const result = { errors: [], warnings: [] };
642
+ const apiMap = this.listAPIs();
773
643
  // OperationId missing
774
644
  const apisMissingOperationId = [];
775
645
  for (const key in apiMap) {
776
- const pathObjectItem = apiMap[key];
777
- if (!pathObjectItem.operationId) {
646
+ const { operation } = apiMap[key];
647
+ if (!operation.operationId) {
778
648
  apisMissingOperationId.push(key);
779
649
  }
780
650
  }
781
651
  if (apisMissingOperationId.length > 0) {
782
- warnings.push({
652
+ result.warnings.push({
783
653
  type: exports.WarningType.OperationIdMissing,
784
654
  content: Utils.format(ConstantString.MissingOperationId, apisMissingOperationId.join(", ")),
785
655
  data: apisMissingOperationId,
786
656
  });
787
657
  }
788
- let status = exports.ValidationStatus.Valid;
789
- if (warnings.length > 0 && errors.length === 0) {
790
- status = exports.ValidationStatus.Warning;
658
+ return result;
659
+ }
660
+ validateMethodAndPath(method, path) {
661
+ const result = { isValid: true, reason: [] };
662
+ if (this.options.allowMethods && !this.options.allowMethods.includes(method)) {
663
+ result.isValid = false;
664
+ result.reason.push(exports.ErrorType.MethodNotAllowed);
665
+ return result;
791
666
  }
792
- else if (errors.length > 0) {
793
- status = exports.ValidationStatus.Error;
667
+ const pathObj = this.spec.paths[path];
668
+ if (!pathObj || !pathObj[method]) {
669
+ result.isValid = false;
670
+ result.reason.push(exports.ErrorType.UrlPathNotExist);
671
+ return result;
794
672
  }
795
- return {
796
- status,
797
- warnings,
798
- errors,
799
- };
673
+ return result;
800
674
  }
801
- static format(str, ...args) {
802
- let index = 0;
803
- return str.replace(/%s/g, () => {
804
- const arg = args[index++];
805
- return arg !== undefined ? arg : "";
806
- });
675
+ validateResponse(method, path) {
676
+ const result = { isValid: true, reason: [] };
677
+ const operationObject = this.spec.paths[path][method];
678
+ const { json, multipleMediaType } = Utils.getResponseJson(operationObject);
679
+ if (this.options.projectType === exports.ProjectType.SME) {
680
+ // only support response body only contains “application/json” content type
681
+ if (multipleMediaType) {
682
+ result.reason.push(exports.ErrorType.ResponseContainMultipleMediaTypes);
683
+ }
684
+ else if (Object.keys(json).length === 0) {
685
+ // response body should not be empty
686
+ result.reason.push(exports.ErrorType.ResponseJsonIsEmpty);
687
+ }
688
+ }
689
+ return result;
807
690
  }
808
- static getSafeRegistrationIdEnvName(authName) {
809
- if (!authName) {
810
- return "";
691
+ validateServer(method, path) {
692
+ const result = { isValid: true, reason: [] };
693
+ const serverObj = Utils.getServerObject(this.spec, method, path);
694
+ if (!serverObj) {
695
+ // should contain server URL
696
+ result.reason.push(exports.ErrorType.NoServerInformation);
811
697
  }
812
- let safeRegistrationIdEnvName = authName.toUpperCase().replace(/[^A-Z0-9_]/g, "_");
813
- if (!safeRegistrationIdEnvName.match(/^[A-Z]/)) {
814
- safeRegistrationIdEnvName = "PREFIX_" + safeRegistrationIdEnvName;
698
+ else {
699
+ // server url should be absolute url with https protocol
700
+ const serverValidateResult = Utils.checkServerUrl([serverObj]);
701
+ result.reason.push(...serverValidateResult.map((item) => item.type));
815
702
  }
816
- return safeRegistrationIdEnvName;
703
+ return result;
817
704
  }
818
- }
819
-
820
- // Copyright (c) Microsoft Corporation.
821
- class SpecFilter {
822
- static specFilter(filter, unResolveSpec, resolvedSpec, options) {
823
- try {
824
- const newSpec = Object.assign({}, unResolveSpec);
825
- const newPaths = {};
826
- for (const filterItem of filter) {
827
- const [method, path] = filterItem.split(" ");
828
- const methodName = method.toLowerCase();
829
- if (!Utils.isSupportedApi(methodName, path, resolvedSpec, options)) {
830
- continue;
831
- }
832
- if (!newPaths[path]) {
833
- newPaths[path] = Object.assign({}, unResolveSpec.paths[path]);
834
- for (const m of ConstantString.AllOperationMethods) {
835
- delete newPaths[path][m];
705
+ validateAuth(method, path) {
706
+ const pathObj = this.spec.paths[path];
707
+ const operationObject = pathObj[method];
708
+ const securities = operationObject.security;
709
+ const authSchemeArray = Utils.getAuthArray(securities, this.spec);
710
+ if (authSchemeArray.length === 0) {
711
+ return { isValid: true, reason: [] };
712
+ }
713
+ if (this.options.allowAPIKeyAuth ||
714
+ this.options.allowOauth2 ||
715
+ this.options.allowBearerTokenAuth) {
716
+ // Currently we don't support multiple auth in one operation
717
+ if (authSchemeArray.length > 0 && authSchemeArray.every((auths) => auths.length > 1)) {
718
+ return {
719
+ isValid: false,
720
+ reason: [exports.ErrorType.MultipleAuthNotSupported],
721
+ };
722
+ }
723
+ for (const auths of authSchemeArray) {
724
+ if (auths.length === 1) {
725
+ if ((this.options.allowAPIKeyAuth && Utils.isAPIKeyAuth(auths[0].authScheme)) ||
726
+ (this.options.allowOauth2 && Utils.isOAuthWithAuthCodeFlow(auths[0].authScheme)) ||
727
+ (this.options.allowBearerTokenAuth && Utils.isBearerTokenAuth(auths[0].authScheme))) {
728
+ return { isValid: true, reason: [] };
836
729
  }
837
730
  }
838
- newPaths[path][methodName] = unResolveSpec.paths[path][methodName];
839
- // Add the operationId if missing
840
- if (!newPaths[path][methodName].operationId) {
841
- newPaths[path][methodName].operationId = `${methodName}${Utils.convertPathToCamelCase(path)}`;
842
- }
843
731
  }
844
- newSpec.paths = newPaths;
845
- return newSpec;
846
- }
847
- catch (err) {
848
- throw new SpecParserError(err.toString(), exports.ErrorType.FilterSpecFailed);
849
732
  }
733
+ return { isValid: false, reason: [exports.ErrorType.AuthTypeIsNotSupported] };
850
734
  }
851
- }
852
-
853
- // Copyright (c) Microsoft Corporation.
854
- class ManifestUpdater {
855
- static updateManifestWithAiPlugin(manifestPath, outputSpecPath, apiPluginFilePath, spec, options) {
856
- return __awaiter(this, void 0, void 0, function* () {
857
- const manifest = yield fs__default['default'].readJSON(manifestPath);
858
- const apiPluginRelativePath = ManifestUpdater.getRelativePath(manifestPath, apiPluginFilePath);
859
- manifest.plugins = [
860
- {
861
- pluginFile: apiPluginRelativePath,
862
- },
863
- ];
864
- ManifestUpdater.updateManifestDescription(manifest, spec);
865
- const specRelativePath = ManifestUpdater.getRelativePath(manifestPath, outputSpecPath);
866
- const apiPlugin = ManifestUpdater.generatePluginManifestSchema(spec, specRelativePath, options);
867
- return [manifest, apiPlugin];
868
- });
869
- }
870
- static updateManifestDescription(manifest, spec) {
871
- var _a, _b;
872
- manifest.description = {
873
- short: spec.info.title.slice(0, ConstantString.ShortDescriptionMaxLens),
874
- 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),
735
+ checkPostBodySchema(schema, isRequired = false) {
736
+ var _a;
737
+ const paramResult = {
738
+ requiredNum: 0,
739
+ optionalNum: 0,
740
+ isValid: true,
741
+ reason: [],
875
742
  };
876
- }
877
- static mapOpenAPISchemaToFuncParam(schema, method, pathUrl) {
878
- let parameter;
743
+ if (Object.keys(schema).length === 0) {
744
+ return paramResult;
745
+ }
746
+ const isRequiredWithoutDefault = isRequired && schema.default === undefined;
747
+ const isCopilot = this.projectType === exports.ProjectType.Copilot;
748
+ if (isCopilot && this.hasNestedObjectInSchema(schema)) {
749
+ paramResult.isValid = false;
750
+ paramResult.reason = [exports.ErrorType.RequestBodyContainsNestedObject];
751
+ return paramResult;
752
+ }
879
753
  if (schema.type === "string" ||
880
- schema.type === "boolean" ||
881
754
  schema.type === "integer" ||
882
- schema.type === "number" ||
883
- schema.type === "array") {
884
- parameter = schema;
755
+ schema.type === "boolean" ||
756
+ schema.type === "number") {
757
+ if (isRequiredWithoutDefault) {
758
+ paramResult.requiredNum = paramResult.requiredNum + 1;
759
+ }
760
+ else {
761
+ paramResult.optionalNum = paramResult.optionalNum + 1;
762
+ }
763
+ }
764
+ else if (schema.type === "object") {
765
+ const { properties } = schema;
766
+ for (const property in properties) {
767
+ let isRequired = false;
768
+ if (schema.required && ((_a = schema.required) === null || _a === void 0 ? void 0 : _a.indexOf(property)) >= 0) {
769
+ isRequired = true;
770
+ }
771
+ const result = this.checkPostBodySchema(properties[property], isRequired);
772
+ paramResult.requiredNum += result.requiredNum;
773
+ paramResult.optionalNum += result.optionalNum;
774
+ paramResult.isValid = paramResult.isValid && result.isValid;
775
+ paramResult.reason.push(...result.reason);
776
+ }
885
777
  }
886
778
  else {
887
- throw new SpecParserError(Utils.format(ConstantString.UnsupportedSchema, method, pathUrl, JSON.stringify(schema)), exports.ErrorType.UpdateManifestFailed);
779
+ if (isRequiredWithoutDefault && !isCopilot) {
780
+ paramResult.isValid = false;
781
+ paramResult.reason.push(exports.ErrorType.PostBodyContainsRequiredUnsupportedSchema);
782
+ }
888
783
  }
889
- return parameter;
784
+ return paramResult;
890
785
  }
891
- static generatePluginManifestSchema(spec, specRelativePath, options) {
892
- var _a, _b, _c;
893
- const functions = [];
894
- const functionNames = [];
895
- const paths = spec.paths;
896
- for (const pathUrl in paths) {
897
- const pathItem = paths[pathUrl];
898
- if (pathItem) {
899
- const operations = pathItem;
900
- for (const method in operations) {
901
- if (options.allowMethods.includes(method)) {
902
- const operationItem = operations[method];
903
- if (operationItem) {
904
- const operationId = operationItem.operationId;
905
- const description = (_a = operationItem.description) !== null && _a !== void 0 ? _a : "";
906
- const paramObject = operationItem.parameters;
907
- const requestBody = operationItem.requestBody;
908
- const parameters = {
909
- type: "object",
910
- properties: {},
911
- required: [],
912
- };
913
- if (paramObject) {
914
- for (let i = 0; i < paramObject.length; i++) {
915
- const param = paramObject[i];
916
- const schema = param.schema;
917
- parameters.properties[param.name] = ManifestUpdater.mapOpenAPISchemaToFuncParam(schema, method, pathUrl);
918
- if (param.required) {
919
- parameters.required.push(param.name);
920
- }
921
- if (!parameters.properties[param.name].description) {
922
- parameters.properties[param.name].description = (_b = param.description) !== null && _b !== void 0 ? _b : "";
923
- }
924
- }
925
- }
926
- if (requestBody) {
927
- const requestJsonBody = requestBody.content["application/json"];
928
- const requestBodySchema = requestJsonBody.schema;
929
- if (requestBodySchema.type === "object") {
930
- if (requestBodySchema.required) {
931
- parameters.required.push(...requestBodySchema.required);
932
- }
933
- for (const property in requestBodySchema.properties) {
934
- const schema = requestBodySchema.properties[property];
935
- parameters.properties[property] = ManifestUpdater.mapOpenAPISchemaToFuncParam(schema, method, pathUrl);
936
- }
937
- }
938
- else {
939
- throw new SpecParserError(Utils.format(ConstantString.UnsupportedSchema, method, pathUrl, JSON.stringify(requestBodySchema)), exports.ErrorType.UpdateManifestFailed);
940
- }
941
- }
942
- const funcObj = {
943
- name: operationId,
944
- description: description,
945
- parameters: parameters,
946
- };
947
- functions.push(funcObj);
948
- functionNames.push(operationId);
949
- }
950
- }
786
+ checkParamSchema(paramObject) {
787
+ const paramResult = {
788
+ requiredNum: 0,
789
+ optionalNum: 0,
790
+ isValid: true,
791
+ reason: [],
792
+ };
793
+ if (!paramObject) {
794
+ return paramResult;
795
+ }
796
+ const isCopilot = this.projectType === exports.ProjectType.Copilot;
797
+ for (let i = 0; i < paramObject.length; i++) {
798
+ const param = paramObject[i];
799
+ const schema = param.schema;
800
+ if (isCopilot && this.hasNestedObjectInSchema(schema)) {
801
+ paramResult.isValid = false;
802
+ paramResult.reason.push(exports.ErrorType.ParamsContainsNestedObject);
803
+ continue;
804
+ }
805
+ const isRequiredWithoutDefault = param.required && schema.default === undefined;
806
+ if (isCopilot) {
807
+ if (isRequiredWithoutDefault) {
808
+ paramResult.requiredNum = paramResult.requiredNum + 1;
809
+ }
810
+ else {
811
+ paramResult.optionalNum = paramResult.optionalNum + 1;
812
+ }
813
+ continue;
814
+ }
815
+ if (param.in === "header" || param.in === "cookie") {
816
+ if (isRequiredWithoutDefault) {
817
+ paramResult.isValid = false;
818
+ paramResult.reason.push(exports.ErrorType.ParamsContainRequiredUnsupportedSchema);
819
+ }
820
+ continue;
821
+ }
822
+ if (schema.type !== "boolean" &&
823
+ schema.type !== "string" &&
824
+ schema.type !== "number" &&
825
+ schema.type !== "integer") {
826
+ if (isRequiredWithoutDefault) {
827
+ paramResult.isValid = false;
828
+ paramResult.reason.push(exports.ErrorType.ParamsContainRequiredUnsupportedSchema);
829
+ }
830
+ continue;
831
+ }
832
+ if (param.in === "query" || param.in === "path") {
833
+ if (isRequiredWithoutDefault) {
834
+ paramResult.requiredNum = paramResult.requiredNum + 1;
835
+ }
836
+ else {
837
+ paramResult.optionalNum = paramResult.optionalNum + 1;
951
838
  }
952
839
  }
953
840
  }
954
- const apiPlugin = {
955
- schema_version: "v2",
956
- name_for_human: spec.info.title,
957
- description_for_human: (_c = spec.info.description) !== null && _c !== void 0 ? _c : "<Please add description of the plugin>",
958
- functions: functions,
959
- runtimes: [
960
- {
961
- type: "OpenApi",
962
- auth: {
963
- type: "none", // TODO, support auth in the future
964
- },
965
- spec: {
966
- url: specRelativePath,
967
- },
968
- run_for_functions: functionNames,
969
- },
970
- ],
971
- };
972
- return apiPlugin;
841
+ return paramResult;
973
842
  }
974
- static updateManifest(manifestPath, outputSpecPath, spec, options, adaptiveCardFolder, authInfo) {
975
- return __awaiter(this, void 0, void 0, function* () {
976
- try {
977
- const originalManifest = yield fs__default['default'].readJSON(manifestPath);
978
- const updatedPart = {};
979
- updatedPart.composeExtensions = [];
980
- let warnings = [];
981
- if (options.projectType === exports.ProjectType.SME) {
982
- const updateResult = yield ManifestUpdater.generateCommands(spec, manifestPath, options, adaptiveCardFolder);
983
- const commands = updateResult[0];
984
- warnings = updateResult[1];
985
- const composeExtension = {
986
- composeExtensionType: "apiBased",
987
- apiSpecificationFile: ManifestUpdater.getRelativePath(manifestPath, outputSpecPath),
988
- commands: commands,
989
- };
990
- if (authInfo) {
991
- const auth = authInfo.authScheme;
992
- if (Utils.isAPIKeyAuth(auth) || Utils.isBearerTokenAuth(auth)) {
993
- const safeApiSecretRegistrationId = Utils.getSafeRegistrationIdEnvName(`${authInfo.name}_${ConstantString.RegistrationIdPostfix}`);
994
- composeExtension.authorization = {
995
- authType: "apiSecretServiceAuth",
996
- apiSecretServiceAuthConfiguration: {
997
- apiSecretRegistrationId: `\${{${safeApiSecretRegistrationId}}}`,
998
- },
999
- };
1000
- }
1001
- else if (Utils.isOAuthWithAuthCodeFlow(auth)) {
1002
- const safeOAuth2RegistrationId = Utils.getSafeRegistrationIdEnvName(`${authInfo.name}_${ConstantString.OAuthRegistrationIdPostFix}`);
1003
- composeExtension.authorization = {
1004
- authType: "oAuth2.0",
1005
- oAuthConfiguration: {
1006
- oauthConfigurationId: `\${{${safeOAuth2RegistrationId}}}`,
1007
- },
1008
- };
1009
- updatedPart.webApplicationInfo = {
1010
- id: "${{AAD_APP_CLIENT_ID}}",
1011
- resource: "api://${{DOMAIN}}/${{AAD_APP_CLIENT_ID}}",
1012
- };
1013
- }
1014
- }
1015
- updatedPart.composeExtensions = [composeExtension];
843
+ hasNestedObjectInSchema(schema) {
844
+ if (schema.type === "object") {
845
+ for (const property in schema.properties) {
846
+ const nestedSchema = schema.properties[property];
847
+ if (nestedSchema.type === "object") {
848
+ return true;
1016
849
  }
1017
- updatedPart.description = originalManifest.description;
1018
- ManifestUpdater.updateManifestDescription(updatedPart, spec);
1019
- const updatedManifest = Object.assign(Object.assign({}, originalManifest), updatedPart);
1020
- return [updatedManifest, warnings];
1021
850
  }
1022
- catch (err) {
1023
- throw new SpecParserError(err.toString(), exports.ErrorType.UpdateManifestFailed);
851
+ }
852
+ return false;
853
+ }
854
+ }
855
+
856
+ // Copyright (c) Microsoft Corporation.
857
+ class CopilotValidator extends Validator {
858
+ constructor(spec, options) {
859
+ super();
860
+ this.projectType = exports.ProjectType.Copilot;
861
+ this.options = options;
862
+ this.spec = spec;
863
+ }
864
+ validateSpec() {
865
+ const result = { errors: [], warnings: [] };
866
+ // validate spec version
867
+ let validationResult = this.validateSpecVersion();
868
+ result.errors.push(...validationResult.errors);
869
+ // validate spec server
870
+ validationResult = this.validateSpecServer();
871
+ result.errors.push(...validationResult.errors);
872
+ // validate no supported API
873
+ validationResult = this.validateSpecNoSupportAPI();
874
+ result.errors.push(...validationResult.errors);
875
+ // validate operationId missing
876
+ validationResult = this.validateSpecOperationId();
877
+ result.warnings.push(...validationResult.warnings);
878
+ return result;
879
+ }
880
+ validateAPI(method, path) {
881
+ const result = { isValid: true, reason: [] };
882
+ method = method.toLocaleLowerCase();
883
+ // validate method and path
884
+ const methodAndPathResult = this.validateMethodAndPath(method, path);
885
+ if (!methodAndPathResult.isValid) {
886
+ return methodAndPathResult;
887
+ }
888
+ const operationObject = this.spec.paths[path][method];
889
+ // validate auth
890
+ const authCheckResult = this.validateAuth(method, path);
891
+ result.reason.push(...authCheckResult.reason);
892
+ // validate operationId
893
+ if (!this.options.allowMissingId && !operationObject.operationId) {
894
+ result.reason.push(exports.ErrorType.MissingOperationId);
895
+ }
896
+ // validate server
897
+ const validateServerResult = this.validateServer(method, path);
898
+ result.reason.push(...validateServerResult.reason);
899
+ // validate response
900
+ const validateResponseResult = this.validateResponse(method, path);
901
+ result.reason.push(...validateResponseResult.reason);
902
+ // validate requestBody
903
+ const requestBody = operationObject.requestBody;
904
+ const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
905
+ if (requestJsonBody) {
906
+ const requestBodySchema = requestJsonBody.schema;
907
+ if (requestBodySchema.type !== "object") {
908
+ result.reason.push(exports.ErrorType.PostBodySchemaIsNotJson);
1024
909
  }
1025
- });
910
+ const requestBodyParamResult = this.checkPostBodySchema(requestBodySchema, requestBody.required);
911
+ result.reason.push(...requestBodyParamResult.reason);
912
+ }
913
+ // validate parameters
914
+ const paramObject = operationObject.parameters;
915
+ const paramResult = this.checkParamSchema(paramObject);
916
+ result.reason.push(...paramResult.reason);
917
+ if (result.reason.length > 0) {
918
+ result.isValid = false;
919
+ }
920
+ return result;
1026
921
  }
1027
- static generateCommands(spec, manifestPath, options, adaptiveCardFolder) {
922
+ }
923
+
924
+ // Copyright (c) Microsoft Corporation.
925
+ class SMEValidator extends Validator {
926
+ constructor(spec, options) {
927
+ super();
928
+ this.projectType = exports.ProjectType.SME;
929
+ this.options = options;
930
+ this.spec = spec;
931
+ }
932
+ validateSpec() {
933
+ const result = { errors: [], warnings: [] };
934
+ // validate spec version
935
+ let validationResult = this.validateSpecVersion();
936
+ result.errors.push(...validationResult.errors);
937
+ // validate spec server
938
+ validationResult = this.validateSpecServer();
939
+ result.errors.push(...validationResult.errors);
940
+ // validate no supported API
941
+ validationResult = this.validateSpecNoSupportAPI();
942
+ result.errors.push(...validationResult.errors);
943
+ // validate operationId missing
944
+ if (this.options.allowMissingId) {
945
+ validationResult = this.validateSpecOperationId();
946
+ result.warnings.push(...validationResult.warnings);
947
+ }
948
+ return result;
949
+ }
950
+ validateAPI(method, path) {
951
+ const result = { isValid: true, reason: [] };
952
+ method = method.toLocaleLowerCase();
953
+ // validate method and path
954
+ const methodAndPathResult = this.validateMethodAndPath(method, path);
955
+ if (!methodAndPathResult.isValid) {
956
+ return methodAndPathResult;
957
+ }
958
+ const operationObject = this.spec.paths[path][method];
959
+ // validate auth
960
+ const authCheckResult = this.validateAuth(method, path);
961
+ result.reason.push(...authCheckResult.reason);
962
+ // validate operationId
963
+ if (!this.options.allowMissingId && !operationObject.operationId) {
964
+ result.reason.push(exports.ErrorType.MissingOperationId);
965
+ }
966
+ // validate server
967
+ const validateServerResult = this.validateServer(method, path);
968
+ result.reason.push(...validateServerResult.reason);
969
+ // validate response
970
+ const validateResponseResult = this.validateResponse(method, path);
971
+ result.reason.push(...validateResponseResult.reason);
972
+ let postBodyResult = {
973
+ requiredNum: 0,
974
+ optionalNum: 0,
975
+ isValid: true,
976
+ reason: [],
977
+ };
978
+ // validate requestBody
979
+ const requestBody = operationObject.requestBody;
980
+ const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
981
+ if (Utils.containMultipleMediaTypes(requestBody)) {
982
+ result.reason.push(exports.ErrorType.PostBodyContainMultipleMediaTypes);
983
+ }
984
+ if (requestJsonBody) {
985
+ const requestBodySchema = requestJsonBody.schema;
986
+ postBodyResult = this.checkPostBodySchema(requestBodySchema, requestBody.required);
987
+ result.reason.push(...postBodyResult.reason);
988
+ }
989
+ // validate parameters
990
+ const paramObject = operationObject.parameters;
991
+ const paramResult = this.checkParamSchema(paramObject);
992
+ result.reason.push(...paramResult.reason);
993
+ // validate total parameters count
994
+ if (paramResult.isValid && postBodyResult.isValid) {
995
+ const paramCountResult = this.validateParamCount(postBodyResult, paramResult);
996
+ result.reason.push(...paramCountResult.reason);
997
+ }
998
+ if (result.reason.length > 0) {
999
+ result.isValid = false;
1000
+ }
1001
+ return result;
1002
+ }
1003
+ validateParamCount(postBodyResult, paramResult) {
1004
+ const result = { isValid: true, reason: [] };
1005
+ const totalRequiredParams = postBodyResult.requiredNum + paramResult.requiredNum;
1006
+ const totalParams = totalRequiredParams + postBodyResult.optionalNum + paramResult.optionalNum;
1007
+ if (totalRequiredParams > 1) {
1008
+ if (!this.options.allowMultipleParameters ||
1009
+ totalRequiredParams > SMEValidator.SMERequiredParamsMaxNum) {
1010
+ result.reason.push(exports.ErrorType.ExceededRequiredParamsLimit);
1011
+ }
1012
+ }
1013
+ else if (totalParams === 0) {
1014
+ result.reason.push(exports.ErrorType.NoParameter);
1015
+ }
1016
+ return result;
1017
+ }
1018
+ }
1019
+ SMEValidator.SMERequiredParamsMaxNum = 5;
1020
+
1021
+ // Copyright (c) Microsoft Corporation.
1022
+ class TeamsAIValidator extends Validator {
1023
+ constructor(spec, options) {
1024
+ super();
1025
+ this.projectType = exports.ProjectType.TeamsAi;
1026
+ this.options = options;
1027
+ this.spec = spec;
1028
+ }
1029
+ validateSpec() {
1030
+ const result = { errors: [], warnings: [] };
1031
+ // validate spec server
1032
+ let validationResult = this.validateSpecServer();
1033
+ result.errors.push(...validationResult.errors);
1034
+ // validate no supported API
1035
+ validationResult = this.validateSpecNoSupportAPI();
1036
+ result.errors.push(...validationResult.errors);
1037
+ return result;
1038
+ }
1039
+ validateAPI(method, path) {
1040
+ const result = { isValid: true, reason: [] };
1041
+ method = method.toLocaleLowerCase();
1042
+ // validate method and path
1043
+ const methodAndPathResult = this.validateMethodAndPath(method, path);
1044
+ if (!methodAndPathResult.isValid) {
1045
+ return methodAndPathResult;
1046
+ }
1047
+ const operationObject = this.spec.paths[path][method];
1048
+ // validate operationId
1049
+ if (!this.options.allowMissingId && !operationObject.operationId) {
1050
+ result.reason.push(exports.ErrorType.MissingOperationId);
1051
+ }
1052
+ // validate server
1053
+ const validateServerResult = this.validateServer(method, path);
1054
+ result.reason.push(...validateServerResult.reason);
1055
+ if (result.reason.length > 0) {
1056
+ result.isValid = false;
1057
+ }
1058
+ return result;
1059
+ }
1060
+ }
1061
+
1062
+ class ValidatorFactory {
1063
+ static create(spec, options) {
1028
1064
  var _a;
1029
- return __awaiter(this, void 0, void 0, function* () {
1030
- const paths = spec.paths;
1031
- const commands = [];
1032
- const warnings = [];
1033
- if (paths) {
1034
- for (const pathUrl in paths) {
1035
- const pathItem = paths[pathUrl];
1036
- if (pathItem) {
1037
- const operations = pathItem;
1038
- // Currently only support GET and POST method
1039
- for (const method in operations) {
1040
- if ((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) {
1041
- const operationItem = operations[method];
1042
- if (operationItem) {
1043
- const [command, warning] = Utils.parseApiInfo(operationItem, options);
1044
- if (adaptiveCardFolder) {
1045
- const adaptiveCardPath = path__default['default'].join(adaptiveCardFolder, command.id + ".json");
1046
- command.apiResponseRenderingTemplateFile = (yield fs__default['default'].pathExists(adaptiveCardPath))
1047
- ? ManifestUpdater.getRelativePath(manifestPath, adaptiveCardPath)
1048
- : "";
1049
- }
1050
- if (warning) {
1051
- warnings.push(warning);
1052
- }
1053
- commands.push(command);
1054
- }
1055
- }
1065
+ const type = (_a = options.projectType) !== null && _a !== void 0 ? _a : exports.ProjectType.SME;
1066
+ switch (type) {
1067
+ case exports.ProjectType.SME:
1068
+ return new SMEValidator(spec, options);
1069
+ case exports.ProjectType.Copilot:
1070
+ return new CopilotValidator(spec, options);
1071
+ case exports.ProjectType.TeamsAi:
1072
+ return new TeamsAIValidator(spec, options);
1073
+ default:
1074
+ throw new Error(`Invalid project type: ${type}`);
1075
+ }
1076
+ }
1077
+ }
1078
+
1079
+ // Copyright (c) Microsoft Corporation.
1080
+ class SpecFilter {
1081
+ static specFilter(filter, unResolveSpec, resolvedSpec, options) {
1082
+ var _a;
1083
+ try {
1084
+ const newSpec = Object.assign({}, unResolveSpec);
1085
+ const newPaths = {};
1086
+ for (const filterItem of filter) {
1087
+ const [method, path] = filterItem.split(" ");
1088
+ const methodName = method.toLowerCase();
1089
+ const pathObj = (_a = resolvedSpec.paths) === null || _a === void 0 ? void 0 : _a[path];
1090
+ if (ConstantString.AllOperationMethods.includes(methodName) &&
1091
+ pathObj &&
1092
+ pathObj[methodName]) {
1093
+ const validator = ValidatorFactory.create(resolvedSpec, options);
1094
+ const validateResult = validator.validateAPI(methodName, path);
1095
+ if (!validateResult.isValid) {
1096
+ continue;
1097
+ }
1098
+ if (!newPaths[path]) {
1099
+ newPaths[path] = Object.assign({}, unResolveSpec.paths[path]);
1100
+ for (const m of ConstantString.AllOperationMethods) {
1101
+ delete newPaths[path][m];
1056
1102
  }
1057
1103
  }
1104
+ newPaths[path][methodName] = unResolveSpec.paths[path][methodName];
1105
+ // Add the operationId if missing
1106
+ if (!newPaths[path][methodName].operationId) {
1107
+ newPaths[path][methodName].operationId = `${methodName}${Utils.convertPathToCamelCase(path)}`;
1108
+ }
1058
1109
  }
1059
1110
  }
1060
- return [commands, warnings];
1061
- });
1062
- }
1063
- static getRelativePath(from, to) {
1064
- const relativePath = path__default['default'].relative(path__default['default'].dirname(from), to);
1065
- return path__default['default'].normalize(relativePath).replace(/\\/g, "/");
1111
+ newSpec.paths = newPaths;
1112
+ return newSpec;
1113
+ }
1114
+ catch (err) {
1115
+ throw new SpecParserError(err.toString(), exports.ErrorType.FilterSpecFailed);
1116
+ }
1066
1117
  }
1067
1118
  }
1068
1119
 
@@ -1070,7 +1121,7 @@ class ManifestUpdater {
1070
1121
  class AdaptiveCardGenerator {
1071
1122
  static generateAdaptiveCard(operationItem) {
1072
1123
  try {
1073
- const json = Utils.getResponseJson(operationItem);
1124
+ const { json } = Utils.getResponseJson(operationItem);
1074
1125
  let cardBody = [];
1075
1126
  let schema = json.schema;
1076
1127
  let jsonPath = "$";
@@ -1236,6 +1287,27 @@ function wrapAdaptiveCard(card, jsonPath) {
1236
1287
  };
1237
1288
  return result;
1238
1289
  }
1290
+ function wrapResponseSemantics(card, jsonPath) {
1291
+ const props = inferProperties(card);
1292
+ const dataPath = jsonPath === "$" ? "$" : "$." + jsonPath;
1293
+ const result = {
1294
+ data_path: dataPath,
1295
+ };
1296
+ if (props.title || props.subtitle || props.imageUrl) {
1297
+ result.properties = {};
1298
+ if (props.title) {
1299
+ result.properties.title = "$." + props.title;
1300
+ }
1301
+ if (props.subtitle) {
1302
+ result.properties.subtitle = "$." + props.subtitle;
1303
+ }
1304
+ if (props.imageUrl) {
1305
+ result.properties.url = "$." + props.imageUrl;
1306
+ }
1307
+ }
1308
+ result.static_template = card;
1309
+ return result;
1310
+ }
1239
1311
  /**
1240
1312
  * Infers the preview card template from an Adaptive Card and a JSON path.
1241
1313
  * The preview card template includes a title and an optional subtitle and image.
@@ -1248,11 +1320,29 @@ function wrapAdaptiveCard(card, jsonPath) {
1248
1320
  * @returns The inferred preview card template.
1249
1321
  */
1250
1322
  function inferPreviewCardTemplate(card) {
1251
- var _a;
1252
1323
  const result = {
1253
- title: "",
1324
+ title: "result",
1254
1325
  };
1255
- const textBlockElements = new Set();
1326
+ const inferredProperties = inferProperties(card);
1327
+ if (inferredProperties.title) {
1328
+ result.title = `\${if(${inferredProperties.title}, ${inferredProperties.title}, 'N/A')}`;
1329
+ }
1330
+ if (inferredProperties.subtitle) {
1331
+ result.subtitle = `\${if(${inferredProperties.subtitle}, ${inferredProperties.subtitle}, 'N/A')}`;
1332
+ }
1333
+ if (inferredProperties.imageUrl) {
1334
+ result.image = {
1335
+ url: `\${${inferredProperties.imageUrl}}`,
1336
+ alt: `\${if(${inferredProperties.imageUrl}, ${inferredProperties.imageUrl}, 'N/A')}`,
1337
+ $when: `\${${inferredProperties.imageUrl} != null}`,
1338
+ };
1339
+ }
1340
+ return result;
1341
+ }
1342
+ function inferProperties(card) {
1343
+ var _a;
1344
+ const result = {};
1345
+ const nameSet = new Set();
1256
1346
  let rootObject;
1257
1347
  if (((_a = card.body[0]) === null || _a === void 0 ? void 0 : _a.type) === ConstantString.ContainerType) {
1258
1348
  rootObject = card.body[0].items;
@@ -1265,56 +1355,380 @@ function inferPreviewCardTemplate(card) {
1265
1355
  const textElement = element;
1266
1356
  const index = textElement.text.indexOf("${if(");
1267
1357
  if (index > 0) {
1268
- textElement.text = textElement.text.substring(index);
1269
- textBlockElements.add(textElement);
1358
+ const text = textElement.text.substring(index);
1359
+ const match = text.match(/\${if\(([^,]+),/);
1360
+ const property = match ? match[1] : "";
1361
+ if (property) {
1362
+ nameSet.add(property);
1363
+ }
1364
+ }
1365
+ }
1366
+ else if (element.type === ConstantString.ImageType) {
1367
+ const imageElement = element;
1368
+ const match = imageElement.url.match(/\${([^,]+)}/);
1369
+ const property = match ? match[1] : "";
1370
+ if (property) {
1371
+ nameSet.add(property);
1270
1372
  }
1271
1373
  }
1272
1374
  }
1273
- for (const element of textBlockElements) {
1274
- const text = element.text;
1275
- if (!result.title && Utils.isWellKnownName(text, ConstantString.WellknownTitleName)) {
1276
- result.title = text;
1277
- textBlockElements.delete(element);
1375
+ for (const name of nameSet) {
1376
+ if (!result.title && Utils.isWellKnownName(name, ConstantString.WellknownTitleName)) {
1377
+ result.title = name;
1378
+ nameSet.delete(name);
1278
1379
  }
1279
1380
  else if (!result.subtitle &&
1280
- Utils.isWellKnownName(text, ConstantString.WellknownSubtitleName)) {
1281
- result.subtitle = text;
1282
- textBlockElements.delete(element);
1381
+ Utils.isWellKnownName(name, ConstantString.WellknownSubtitleName)) {
1382
+ result.subtitle = name;
1383
+ nameSet.delete(name);
1283
1384
  }
1284
- else if (!result.image && Utils.isWellKnownName(text, ConstantString.WellknownImageName)) {
1285
- const match = text.match(/\${if\(([^,]+),/);
1286
- const property = match ? match[1] : "";
1287
- if (property) {
1288
- result.image = {
1289
- url: `\${${property}}`,
1290
- alt: text,
1291
- $when: `\${${property} != null}`,
1292
- };
1293
- }
1294
- textBlockElements.delete(element);
1385
+ else if (!result.imageUrl && Utils.isWellKnownName(name, ConstantString.WellknownImageName)) {
1386
+ result.imageUrl = name;
1387
+ nameSet.delete(name);
1295
1388
  }
1296
1389
  }
1297
- for (const element of textBlockElements) {
1298
- const text = element.text;
1390
+ for (const name of nameSet) {
1299
1391
  if (!result.title) {
1300
- result.title = text;
1301
- textBlockElements.delete(element);
1392
+ result.title = name;
1393
+ nameSet.delete(name);
1302
1394
  }
1303
1395
  else if (!result.subtitle) {
1304
- result.subtitle = text;
1305
- textBlockElements.delete(element);
1396
+ result.subtitle = name;
1397
+ nameSet.delete(name);
1306
1398
  }
1307
1399
  }
1308
1400
  if (!result.title && result.subtitle) {
1309
1401
  result.title = result.subtitle;
1310
1402
  delete result.subtitle;
1311
1403
  }
1312
- if (!result.title) {
1313
- result.title = "result";
1314
- }
1315
1404
  return result;
1316
1405
  }
1317
1406
 
1407
+ // Copyright (c) Microsoft Corporation.
1408
+ class ManifestUpdater {
1409
+ static updateManifestWithAiPlugin(manifestPath, outputSpecPath, apiPluginFilePath, spec, options, authInfo) {
1410
+ return __awaiter(this, void 0, void 0, function* () {
1411
+ const manifest = yield fs__default['default'].readJSON(manifestPath);
1412
+ const apiPluginRelativePath = ManifestUpdater.getRelativePath(manifestPath, apiPluginFilePath);
1413
+ // Insert plugins in manifest.json if it is plugin for Copilot.
1414
+ if (!options.isGptPlugin) {
1415
+ manifest.plugins = [
1416
+ {
1417
+ file: apiPluginRelativePath,
1418
+ id: ConstantString.DefaultPluginId,
1419
+ },
1420
+ ];
1421
+ ManifestUpdater.updateManifestDescription(manifest, spec);
1422
+ }
1423
+ const appName = this.removeEnvs(manifest.name.short);
1424
+ const specRelativePath = ManifestUpdater.getRelativePath(manifestPath, outputSpecPath);
1425
+ const apiPlugin = yield ManifestUpdater.generatePluginManifestSchema(spec, specRelativePath, apiPluginFilePath, appName, authInfo, options);
1426
+ return [manifest, apiPlugin];
1427
+ });
1428
+ }
1429
+ static updateManifestDescription(manifest, spec) {
1430
+ var _a, _b;
1431
+ manifest.description = {
1432
+ short: spec.info.title.slice(0, ConstantString.ShortDescriptionMaxLens),
1433
+ 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),
1434
+ };
1435
+ }
1436
+ static checkSchema(schema, method, pathUrl) {
1437
+ if (schema.type === "array") {
1438
+ const items = schema.items;
1439
+ ManifestUpdater.checkSchema(items, method, pathUrl);
1440
+ }
1441
+ else if (schema.type !== "string" &&
1442
+ schema.type !== "boolean" &&
1443
+ schema.type !== "integer" &&
1444
+ schema.type !== "number") {
1445
+ throw new SpecParserError(Utils.format(ConstantString.UnsupportedSchema, method, pathUrl, JSON.stringify(schema)), exports.ErrorType.UpdateManifestFailed);
1446
+ }
1447
+ }
1448
+ static generatePluginManifestSchema(spec, specRelativePath, apiPluginFilePath, appName, authInfo, options) {
1449
+ var _a, _b, _c, _d;
1450
+ return __awaiter(this, void 0, void 0, function* () {
1451
+ const functions = [];
1452
+ const functionNames = [];
1453
+ const conversationStarters = [];
1454
+ const paths = spec.paths;
1455
+ const pluginAuthObj = {
1456
+ type: "None",
1457
+ };
1458
+ if (authInfo) {
1459
+ if (Utils.isOAuthWithAuthCodeFlow(authInfo.authScheme)) {
1460
+ pluginAuthObj.type = "OAuthPluginVault";
1461
+ }
1462
+ else if (Utils.isBearerTokenAuth(authInfo.authScheme)) {
1463
+ pluginAuthObj.type = "ApiKeyPluginVault";
1464
+ }
1465
+ if (pluginAuthObj.type !== "None") {
1466
+ const safeRegistrationIdName = Utils.getSafeRegistrationIdEnvName(`${authInfo.name}_${ConstantString.RegistrationIdPostfix[authInfo.authScheme.type]}`);
1467
+ pluginAuthObj.reference_id = `\${{${safeRegistrationIdName}}}`;
1468
+ }
1469
+ }
1470
+ for (const pathUrl in paths) {
1471
+ const pathItem = paths[pathUrl];
1472
+ if (pathItem) {
1473
+ const operations = pathItem;
1474
+ for (const method in operations) {
1475
+ if (options.allowMethods.includes(method)) {
1476
+ const operationItem = operations[method];
1477
+ const confirmationBodies = [];
1478
+ if (operationItem) {
1479
+ const operationId = operationItem.operationId;
1480
+ const description = (_a = operationItem.description) !== null && _a !== void 0 ? _a : "";
1481
+ const summary = operationItem.summary;
1482
+ const paramObject = operationItem.parameters;
1483
+ const requestBody = operationItem.requestBody;
1484
+ if (paramObject) {
1485
+ for (let i = 0; i < paramObject.length; i++) {
1486
+ const param = paramObject[i];
1487
+ const schema = param.schema;
1488
+ ManifestUpdater.checkSchema(schema, method, pathUrl);
1489
+ confirmationBodies.push(ManifestUpdater.getConfirmationBodyItem(param.name));
1490
+ }
1491
+ }
1492
+ if (requestBody) {
1493
+ const requestJsonBody = requestBody.content["application/json"];
1494
+ const requestBodySchema = requestJsonBody.schema;
1495
+ if (requestBodySchema.type === "object") {
1496
+ for (const property in requestBodySchema.properties) {
1497
+ const schema = requestBodySchema.properties[property];
1498
+ ManifestUpdater.checkSchema(schema, method, pathUrl);
1499
+ confirmationBodies.push(ManifestUpdater.getConfirmationBodyItem(property));
1500
+ }
1501
+ }
1502
+ else {
1503
+ throw new SpecParserError(Utils.format(ConstantString.UnsupportedSchema, method, pathUrl, JSON.stringify(requestBodySchema)), exports.ErrorType.UpdateManifestFailed);
1504
+ }
1505
+ }
1506
+ const funcObj = {
1507
+ name: operationId,
1508
+ description: description,
1509
+ };
1510
+ if (options.allowResponseSemantics) {
1511
+ const { json } = Utils.getResponseJson(operationItem);
1512
+ if (json.schema) {
1513
+ const [card, jsonPath] = AdaptiveCardGenerator.generateAdaptiveCard(operationItem);
1514
+ const responseSemantic = wrapResponseSemantics(card, jsonPath);
1515
+ funcObj.capabilities = {
1516
+ response_semantics: responseSemantic,
1517
+ };
1518
+ }
1519
+ }
1520
+ if (options.allowConfirmation && method !== ConstantString.GetMethod) {
1521
+ if (!funcObj.capabilities) {
1522
+ funcObj.capabilities = {};
1523
+ }
1524
+ funcObj.capabilities.confirmation = {
1525
+ type: "AdaptiveCard",
1526
+ title: (_b = operationItem.summary) !== null && _b !== void 0 ? _b : description,
1527
+ };
1528
+ if (confirmationBodies.length > 0) {
1529
+ funcObj.capabilities.confirmation.body = confirmationBodies.join("\n");
1530
+ }
1531
+ }
1532
+ functions.push(funcObj);
1533
+ functionNames.push(operationId);
1534
+ const conversationStarterStr = (summary !== null && summary !== void 0 ? summary : description).slice(0, ConstantString.ConversationStarterMaxLens);
1535
+ if (conversationStarterStr) {
1536
+ conversationStarters.push(conversationStarterStr);
1537
+ }
1538
+ }
1539
+ }
1540
+ }
1541
+ }
1542
+ }
1543
+ let apiPlugin;
1544
+ if (yield fs__default['default'].pathExists(apiPluginFilePath)) {
1545
+ apiPlugin = yield fs__default['default'].readJSON(apiPluginFilePath);
1546
+ }
1547
+ else {
1548
+ apiPlugin = {
1549
+ schema_version: "v2.1",
1550
+ name_for_human: "",
1551
+ description_for_human: "",
1552
+ namespace: "",
1553
+ functions: [],
1554
+ runtimes: [],
1555
+ };
1556
+ }
1557
+ apiPlugin.functions = apiPlugin.functions || [];
1558
+ for (const func of functions) {
1559
+ const index = (_c = apiPlugin.functions) === null || _c === void 0 ? void 0 : _c.findIndex((f) => f.name === func.name);
1560
+ if (index === -1) {
1561
+ apiPlugin.functions.push(func);
1562
+ }
1563
+ else {
1564
+ apiPlugin.functions[index] = func;
1565
+ }
1566
+ }
1567
+ apiPlugin.runtimes = apiPlugin.runtimes || [];
1568
+ const index = apiPlugin.runtimes.findIndex((runtime) => {
1569
+ var _a, _b;
1570
+ return runtime.spec.url === specRelativePath &&
1571
+ runtime.type === "OpenApi" &&
1572
+ ((_b = (_a = runtime.auth) === null || _a === void 0 ? void 0 : _a.type) !== null && _b !== void 0 ? _b : "None") === pluginAuthObj.type;
1573
+ });
1574
+ if (index === -1) {
1575
+ apiPlugin.runtimes.push({
1576
+ type: "OpenApi",
1577
+ auth: pluginAuthObj,
1578
+ spec: {
1579
+ url: specRelativePath,
1580
+ },
1581
+ run_for_functions: functionNames,
1582
+ });
1583
+ }
1584
+ else {
1585
+ apiPlugin.runtimes[index].run_for_functions = functionNames;
1586
+ }
1587
+ if (!apiPlugin.name_for_human) {
1588
+ apiPlugin.name_for_human = appName;
1589
+ }
1590
+ if (!apiPlugin.namespace) {
1591
+ apiPlugin.namespace = ManifestUpdater.removeAllSpecialCharacters(appName);
1592
+ }
1593
+ if (!apiPlugin.description_for_human) {
1594
+ apiPlugin.description_for_human =
1595
+ (_d = spec.info.description) !== null && _d !== void 0 ? _d : "<Please add description of the plugin>";
1596
+ }
1597
+ if (options.allowConversationStarters && conversationStarters.length > 0) {
1598
+ if (!apiPlugin.capabilities) {
1599
+ apiPlugin.capabilities = {
1600
+ localization: {},
1601
+ };
1602
+ }
1603
+ if (!apiPlugin.capabilities.conversation_starters) {
1604
+ apiPlugin.capabilities.conversation_starters = conversationStarters
1605
+ .slice(0, 5)
1606
+ .map((text) => ({ text }));
1607
+ }
1608
+ }
1609
+ return apiPlugin;
1610
+ });
1611
+ }
1612
+ static updateManifest(manifestPath, outputSpecPath, spec, options, adaptiveCardFolder, authInfo) {
1613
+ return __awaiter(this, void 0, void 0, function* () {
1614
+ try {
1615
+ const originalManifest = yield fs__default['default'].readJSON(manifestPath);
1616
+ const updatedPart = {};
1617
+ updatedPart.composeExtensions = [];
1618
+ let warnings = [];
1619
+ if (options.projectType === exports.ProjectType.SME) {
1620
+ const updateResult = yield ManifestUpdater.generateCommands(spec, manifestPath, options, adaptiveCardFolder);
1621
+ const commands = updateResult[0];
1622
+ warnings = updateResult[1];
1623
+ const composeExtension = {
1624
+ composeExtensionType: "apiBased",
1625
+ apiSpecificationFile: ManifestUpdater.getRelativePath(manifestPath, outputSpecPath),
1626
+ commands: commands,
1627
+ };
1628
+ if (authInfo) {
1629
+ const auth = authInfo.authScheme;
1630
+ const safeRegistrationIdName = Utils.getSafeRegistrationIdEnvName(`${authInfo.name}_${ConstantString.RegistrationIdPostfix[authInfo.authScheme.type]}`);
1631
+ if (Utils.isAPIKeyAuth(auth) || Utils.isBearerTokenAuth(auth)) {
1632
+ const safeApiSecretRegistrationId = Utils.getSafeRegistrationIdEnvName(`${authInfo.name}_${ConstantString.RegistrationIdPostfix[authInfo.authScheme.type]}`);
1633
+ composeExtension.authorization = {
1634
+ authType: "apiSecretServiceAuth",
1635
+ apiSecretServiceAuthConfiguration: {
1636
+ apiSecretRegistrationId: `\${{${safeRegistrationIdName}}}`,
1637
+ },
1638
+ };
1639
+ }
1640
+ else if (Utils.isOAuthWithAuthCodeFlow(auth)) {
1641
+ composeExtension.authorization = {
1642
+ authType: "oAuth2.0",
1643
+ oAuthConfiguration: {
1644
+ oauthConfigurationId: `\${{${safeRegistrationIdName}}}`,
1645
+ },
1646
+ };
1647
+ updatedPart.webApplicationInfo = {
1648
+ id: "${{AAD_APP_CLIENT_ID}}",
1649
+ resource: "api://${{DOMAIN}}/${{AAD_APP_CLIENT_ID}}",
1650
+ };
1651
+ }
1652
+ }
1653
+ updatedPart.composeExtensions = [composeExtension];
1654
+ }
1655
+ updatedPart.description = originalManifest.description;
1656
+ ManifestUpdater.updateManifestDescription(updatedPart, spec);
1657
+ const updatedManifest = Object.assign(Object.assign({}, originalManifest), updatedPart);
1658
+ return [updatedManifest, warnings];
1659
+ }
1660
+ catch (err) {
1661
+ throw new SpecParserError(err.toString(), exports.ErrorType.UpdateManifestFailed);
1662
+ }
1663
+ });
1664
+ }
1665
+ static generateCommands(spec, manifestPath, options, adaptiveCardFolder) {
1666
+ var _a;
1667
+ return __awaiter(this, void 0, void 0, function* () {
1668
+ const paths = spec.paths;
1669
+ const commands = [];
1670
+ const warnings = [];
1671
+ if (paths) {
1672
+ for (const pathUrl in paths) {
1673
+ const pathItem = paths[pathUrl];
1674
+ if (pathItem) {
1675
+ const operations = pathItem;
1676
+ // Currently only support GET and POST method
1677
+ for (const method in operations) {
1678
+ if ((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) {
1679
+ const operationItem = operations[method];
1680
+ if (operationItem) {
1681
+ const command = Utils.parseApiInfo(operationItem, options);
1682
+ if (command.parameters &&
1683
+ command.parameters.length >= 1 &&
1684
+ command.parameters.some((param) => param.isRequired)) {
1685
+ command.parameters = command.parameters.filter((param) => param.isRequired);
1686
+ }
1687
+ else if (command.parameters && command.parameters.length > 0) {
1688
+ command.parameters = [command.parameters[0]];
1689
+ warnings.push({
1690
+ type: exports.WarningType.OperationOnlyContainsOptionalParam,
1691
+ content: Utils.format(ConstantString.OperationOnlyContainsOptionalParam, command.id),
1692
+ data: command.id,
1693
+ });
1694
+ }
1695
+ if (adaptiveCardFolder) {
1696
+ const adaptiveCardPath = path__default['default'].join(adaptiveCardFolder, command.id + ".json");
1697
+ command.apiResponseRenderingTemplateFile = (yield fs__default['default'].pathExists(adaptiveCardPath))
1698
+ ? ManifestUpdater.getRelativePath(manifestPath, adaptiveCardPath)
1699
+ : "";
1700
+ }
1701
+ commands.push(command);
1702
+ }
1703
+ }
1704
+ }
1705
+ }
1706
+ }
1707
+ }
1708
+ return [commands, warnings];
1709
+ });
1710
+ }
1711
+ static getRelativePath(from, to) {
1712
+ const relativePath = path__default['default'].relative(path__default['default'].dirname(from), to);
1713
+ return path__default['default'].normalize(relativePath).replace(/\\/g, "/");
1714
+ }
1715
+ static removeEnvs(str) {
1716
+ const placeHolderReg = /\${{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*}}/g;
1717
+ const matches = placeHolderReg.exec(str);
1718
+ let newStr = str;
1719
+ if (matches != null) {
1720
+ newStr = newStr.replace(matches[0], "");
1721
+ }
1722
+ return newStr;
1723
+ }
1724
+ static removeAllSpecialCharacters(str) {
1725
+ return str.toLowerCase().replace(/[^a-z0-9]/g, "");
1726
+ }
1727
+ static getConfirmationBodyItem(paramName) {
1728
+ return `* **${Utils.updateFirstLetter(paramName)}**: {{function.parameters.${paramName}}}`;
1729
+ }
1730
+ }
1731
+
1318
1732
  // Copyright (c) Microsoft Corporation.
1319
1733
  /**
1320
1734
  * A class that parses an OpenAPI specification file and provides methods to validate, list, and generate artifacts.
@@ -1334,7 +1748,11 @@ class SpecParser {
1334
1748
  allowMultipleParameters: false,
1335
1749
  allowOauth2: false,
1336
1750
  allowMethods: ["get", "post"],
1751
+ allowConversationStarters: false,
1752
+ allowResponseSemantics: false,
1753
+ allowConfirmation: false,
1337
1754
  projectType: exports.ProjectType.SME,
1755
+ isGptPlugin: false,
1338
1756
  };
1339
1757
  this.pathOrSpec = pathOrDoc;
1340
1758
  this.parser = new SwaggerParser__default['default']();
@@ -1359,6 +1777,8 @@ class SpecParser {
1359
1777
  errors: [{ type: exports.ErrorType.SpecNotValid, content: e.toString() }],
1360
1778
  };
1361
1779
  }
1780
+ const errors = [];
1781
+ const warnings = [];
1362
1782
  if (!this.options.allowSwagger && this.isSwaggerFile) {
1363
1783
  return {
1364
1784
  status: exports.ValidationStatus.Error,
@@ -1368,7 +1788,38 @@ class SpecParser {
1368
1788
  ],
1369
1789
  };
1370
1790
  }
1371
- return Utils.validateSpec(this.spec, this.parser, !!this.isSwaggerFile, this.options);
1791
+ // Remote reference not supported
1792
+ const refPaths = this.parser.$refs.paths();
1793
+ // refPaths [0] is the current spec file path
1794
+ if (refPaths.length > 1) {
1795
+ errors.push({
1796
+ type: exports.ErrorType.RemoteRefNotSupported,
1797
+ content: Utils.format(ConstantString.RemoteRefNotSupported, refPaths.join(", ")),
1798
+ data: refPaths,
1799
+ });
1800
+ }
1801
+ if (!!this.isSwaggerFile && this.options.allowSwagger) {
1802
+ warnings.push({
1803
+ type: exports.WarningType.ConvertSwaggerToOpenAPI,
1804
+ content: ConstantString.ConvertSwaggerToOpenAPI,
1805
+ });
1806
+ }
1807
+ const validator = this.getValidator(this.spec);
1808
+ const validationResult = validator.validateSpec();
1809
+ warnings.push(...validationResult.warnings);
1810
+ errors.push(...validationResult.errors);
1811
+ let status = exports.ValidationStatus.Valid;
1812
+ if (warnings.length > 0 && errors.length === 0) {
1813
+ status = exports.ValidationStatus.Warning;
1814
+ }
1815
+ else if (errors.length > 0) {
1816
+ status = exports.ValidationStatus.Error;
1817
+ }
1818
+ return {
1819
+ status: status,
1820
+ warnings: warnings,
1821
+ errors: errors,
1822
+ };
1372
1823
  }
1373
1824
  catch (err) {
1374
1825
  throw new SpecParserError(err.toString(), exports.ErrorType.ValidateFailed);
@@ -1392,39 +1843,40 @@ class SpecParser {
1392
1843
  try {
1393
1844
  yield this.loadSpec();
1394
1845
  const spec = this.spec;
1395
- const apiMap = this.getAllSupportedAPIs(spec);
1396
- const result = [];
1846
+ const apiMap = this.getAPIs(spec);
1847
+ const result = {
1848
+ APIs: [],
1849
+ allAPICount: 0,
1850
+ validAPICount: 0,
1851
+ };
1397
1852
  for (const apiKey in apiMap) {
1853
+ const { operation, isValid, reason } = apiMap[apiKey];
1854
+ const [method, path] = apiKey.split(" ");
1855
+ const operationId = (_a = operation.operationId) !== null && _a !== void 0 ? _a : `${method.toLowerCase()}${Utils.convertPathToCamelCase(path)}`;
1398
1856
  const apiResult = {
1399
- api: "",
1857
+ api: apiKey,
1400
1858
  server: "",
1401
- operationId: "",
1859
+ operationId: operationId,
1860
+ isValid: isValid,
1861
+ reason: reason,
1402
1862
  };
1403
- const [method, path] = apiKey.split(" ");
1404
- const operation = apiMap[apiKey];
1405
- const rootServer = spec.servers && spec.servers[0];
1406
- const methodServer = spec.paths[path].servers && ((_a = spec.paths[path]) === null || _a === void 0 ? void 0 : _a.servers[0]);
1407
- const operationServer = operation.servers && operation.servers[0];
1408
- const serverUrl = operationServer || methodServer || rootServer;
1409
- if (!serverUrl) {
1410
- throw new SpecParserError(ConstantString.NoServerInformation, exports.ErrorType.NoServerInformation);
1411
- }
1412
- apiResult.server = Utils.resolveServerUrl(serverUrl.url);
1413
- let operationId = operation.operationId;
1414
- if (!operationId) {
1415
- operationId = `${method.toLowerCase()}${Utils.convertPathToCamelCase(path)}`;
1416
- }
1417
- apiResult.operationId = operationId;
1418
- const authArray = Utils.getAuthArray(operation.security, spec);
1419
- for (const auths of authArray) {
1420
- if (auths.length === 1) {
1421
- apiResult.auth = auths[0].authScheme;
1422
- break;
1863
+ if (isValid) {
1864
+ const serverObj = Utils.getServerObject(spec, method.toLocaleLowerCase(), path);
1865
+ if (serverObj) {
1866
+ apiResult.server = Utils.resolveEnv(serverObj.url);
1867
+ }
1868
+ const authArray = Utils.getAuthArray(operation.security, spec);
1869
+ for (const auths of authArray) {
1870
+ if (auths.length === 1) {
1871
+ apiResult.auth = auths[0];
1872
+ break;
1873
+ }
1423
1874
  }
1424
1875
  }
1425
- apiResult.api = apiKey;
1426
- result.push(apiResult);
1876
+ result.APIs.push(apiResult);
1427
1877
  }
1878
+ result.allAPICount = result.APIs.length;
1879
+ result.validAPICount = result.APIs.filter((api) => api.isValid).length;
1428
1880
  return result;
1429
1881
  }
1430
1882
  catch (err) {
@@ -1481,18 +1933,12 @@ class SpecParser {
1481
1933
  const newSpecs = yield this.getFilteredSpecs(filter, signal);
1482
1934
  const newUnResolvedSpec = newSpecs[0];
1483
1935
  const newSpec = newSpecs[1];
1484
- let resultStr;
1485
- if (outputSpecPath.endsWith(".yaml") || outputSpecPath.endsWith(".yml")) {
1486
- resultStr = jsyaml__default['default'].dump(newUnResolvedSpec);
1487
- }
1488
- else {
1489
- resultStr = JSON.stringify(newUnResolvedSpec, null, 2);
1490
- }
1491
- yield fs__default['default'].outputFile(outputSpecPath, resultStr);
1936
+ const authInfo = Utils.getAuthInfo(newSpec);
1937
+ yield this.saveFilterSpec(outputSpecPath, newUnResolvedSpec);
1492
1938
  if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
1493
1939
  throw new SpecParserError(ConstantString.CancelledMessage, exports.ErrorType.Cancelled);
1494
1940
  }
1495
- const [updatedManifest, apiPlugin] = yield ManifestUpdater.updateManifestWithAiPlugin(manifestPath, outputSpecPath, pluginFilePath, newSpec, this.options);
1941
+ const [updatedManifest, apiPlugin] = yield ManifestUpdater.updateManifestWithAiPlugin(manifestPath, outputSpecPath, pluginFilePath, newSpec, this.options, authInfo);
1496
1942
  yield fs__default['default'].outputJSON(manifestPath, updatedManifest, { spaces: 2 });
1497
1943
  yield fs__default['default'].outputJSON(pluginFilePath, apiPlugin, { spaces: 2 });
1498
1944
  }
@@ -1522,32 +1968,11 @@ class SpecParser {
1522
1968
  const newSpecs = yield this.getFilteredSpecs(filter, signal);
1523
1969
  const newUnResolvedSpec = newSpecs[0];
1524
1970
  const newSpec = newSpecs[1];
1525
- const authSet = new Set();
1526
- let hasMultipleAuth = false;
1527
- for (const url in newSpec.paths) {
1528
- for (const method in newSpec.paths[url]) {
1529
- const operation = newSpec.paths[url][method];
1530
- const authArray = Utils.getAuthArray(operation.security, newSpec);
1531
- if (authArray && authArray.length > 0) {
1532
- authSet.add(authArray[0][0]);
1533
- if (authSet.size > 1) {
1534
- hasMultipleAuth = true;
1535
- break;
1536
- }
1537
- }
1538
- }
1539
- }
1540
- if (hasMultipleAuth && this.options.projectType !== exports.ProjectType.TeamsAi) {
1541
- throw new SpecParserError(ConstantString.MultipleAuthNotSupported, exports.ErrorType.MultipleAuthNotSupported);
1542
- }
1543
- let resultStr;
1544
- if (outputSpecPath.endsWith(".yaml") || outputSpecPath.endsWith(".yml")) {
1545
- resultStr = jsyaml__default['default'].dump(newUnResolvedSpec);
1971
+ let authInfo = undefined;
1972
+ if (this.options.projectType === exports.ProjectType.SME) {
1973
+ authInfo = Utils.getAuthInfo(newSpec);
1546
1974
  }
1547
- else {
1548
- resultStr = JSON.stringify(newUnResolvedSpec, null, 2);
1549
- }
1550
- yield fs__default['default'].outputFile(outputSpecPath, resultStr);
1975
+ yield this.saveFilterSpec(outputSpecPath, newUnResolvedSpec);
1551
1976
  if (adaptiveCardFolder) {
1552
1977
  for (const url in newSpec.paths) {
1553
1978
  for (const method in newSpec.paths[url]) {
@@ -1577,7 +2002,6 @@ class SpecParser {
1577
2002
  if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
1578
2003
  throw new SpecParserError(ConstantString.CancelledMessage, exports.ErrorType.Cancelled);
1579
2004
  }
1580
- const authInfo = Array.from(authSet)[0];
1581
2005
  const [updatedManifest, warnings] = yield ManifestUpdater.updateManifest(manifestPath, outputSpecPath, newSpec, this.options, adaptiveCardFolder, authInfo);
1582
2006
  yield fs__default['default'].outputJSON(manifestPath, updatedManifest, { spaces: 2 });
1583
2007
  result.warnings.push(...warnings);
@@ -1606,13 +2030,30 @@ class SpecParser {
1606
2030
  }
1607
2031
  });
1608
2032
  }
1609
- getAllSupportedAPIs(spec) {
1610
- if (this.apiMap !== undefined) {
1611
- return this.apiMap;
2033
+ getAPIs(spec) {
2034
+ const validator = this.getValidator(spec);
2035
+ const apiMap = validator.listAPIs();
2036
+ return apiMap;
2037
+ }
2038
+ getValidator(spec) {
2039
+ if (this.validator) {
2040
+ return this.validator;
1612
2041
  }
1613
- const result = Utils.listSupportedAPIs(spec, this.options);
1614
- this.apiMap = result;
1615
- return result;
2042
+ const validator = ValidatorFactory.create(spec, this.options);
2043
+ this.validator = validator;
2044
+ return validator;
2045
+ }
2046
+ saveFilterSpec(outputSpecPath, unResolvedSpec) {
2047
+ return __awaiter(this, void 0, void 0, function* () {
2048
+ let resultStr;
2049
+ if (outputSpecPath.endsWith(".yaml") || outputSpecPath.endsWith(".yml")) {
2050
+ resultStr = jsyaml__default['default'].dump(unResolvedSpec);
2051
+ }
2052
+ else {
2053
+ resultStr = JSON.stringify(unResolvedSpec, null, 2);
2054
+ }
2055
+ yield fs__default['default'].outputFile(outputSpecPath, resultStr);
2056
+ });
1616
2057
  }
1617
2058
  }
1618
2059