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