@microsoft/m365-spec-parser 0.1.1-alpha.48b9eab36.0 → 0.1.1-alpha.4e708f092.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.
@@ -61,7 +61,8 @@ exports.ErrorType = void 0;
61
61
  ErrorType["NoExtraAPICanBeAdded"] = "no-extra-api-can-be-added";
62
62
  ErrorType["ResolveServerUrlFailed"] = "resolve-server-url-failed";
63
63
  ErrorType["SwaggerNotSupported"] = "swagger-not-supported";
64
- ErrorType["MultipleAPIKeyNotSupported"] = "multiple-api-key-not-supported";
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 = {}));
@@ -92,7 +108,13 @@ exports.ValidationStatus = void 0;
92
108
  ValidationStatus[ValidationStatus["Valid"] = 0] = "Valid";
93
109
  ValidationStatus[ValidationStatus["Warning"] = 1] = "Warning";
94
110
  ValidationStatus[ValidationStatus["Error"] = 2] = "Error";
95
- })(exports.ValidationStatus || (exports.ValidationStatus = {}));
111
+ })(exports.ValidationStatus || (exports.ValidationStatus = {}));
112
+ exports.ProjectType = void 0;
113
+ (function (ProjectType) {
114
+ ProjectType[ProjectType["Copilot"] = 0] = "Copilot";
115
+ ProjectType[ProjectType["SME"] = 1] = "SME";
116
+ ProjectType[ProjectType["TeamsAi"] = 2] = "TeamsAi";
117
+ })(exports.ProjectType || (exports.ProjectType = {}));
96
118
 
97
119
  // Copyright (c) Microsoft Corporation.
98
120
  class ConstantString {
@@ -111,7 +133,9 @@ ConstantString.ResolveServerUrlFailed = "Unable to resolve the server URL: pleas
111
133
  ConstantString.OperationOnlyContainsOptionalParam = "Operation %s contains multiple optional parameters. The first optional parameter is used for this command.";
112
134
  ConstantString.ConvertSwaggerToOpenAPI = "The Swagger 2.0 file has been converted to OpenAPI 3.0.";
113
135
  ConstantString.SwaggerNotSupported = "Swagger 2.0 is not supported. Please convert to OpenAPI 3.0 manually before proceeding.";
114
- ConstantString.MultipleAPIKeyNotSupported = "Multiple API keys are not supported. Please make sure that all selected APIs use the same API key.";
136
+ ConstantString.SpecVersionNotSupported = "Unsupported OpenAPI version %s. Please use version 3.0.x.";
137
+ ConstantString.MultipleAuthNotSupported = "Multiple authentication methods are unsupported. Ensure all selected APIs use identical authentication.";
138
+ ConstantString.UnsupportedSchema = "Unsupported schema in %s %s: %s";
115
139
  ConstantString.WrappedCardVersion = "devPreview";
116
140
  ConstantString.WrappedCardSchema = "https://developer.microsoft.com/json-schemas/teams/vDevPreview/MicrosoftTeams.ResponseRenderingTemplate.schema.json";
117
141
  ConstantString.WrappedCardResponseLayout = "list";
@@ -121,8 +145,10 @@ ConstantString.AdaptiveCardVersion = "1.5";
121
145
  ConstantString.AdaptiveCardSchema = "http://adaptivecards.io/schemas/adaptive-card.json";
122
146
  ConstantString.AdaptiveCardType = "AdaptiveCard";
123
147
  ConstantString.TextBlockType = "TextBlock";
148
+ ConstantString.ImageType = "Image";
124
149
  ConstantString.ContainerType = "Container";
125
150
  ConstantString.RegistrationIdPostfix = "REGISTRATION_ID";
151
+ ConstantString.OAuthRegistrationIdPostFix = "OAUTH_REGISTRATION_ID";
126
152
  ConstantString.ResponseCodeFor20X = [
127
153
  "200",
128
154
  "201",
@@ -182,7 +208,9 @@ ConstantString.FullDescriptionMaxLens = 4000;
182
208
  ConstantString.CommandDescriptionMaxLens = 128;
183
209
  ConstantString.ParameterDescriptionMaxLens = 128;
184
210
  ConstantString.CommandTitleMaxLens = 32;
185
- ConstantString.ParameterTitleMaxLens = 32;
211
+ ConstantString.ParameterTitleMaxLens = 32;
212
+ ConstantString.SMERequiredParamsMaxNum = 5;
213
+ ConstantString.DefaultPluginId = "plugin_1";
186
214
 
187
215
  // Copyright (c) Microsoft Corporation.
188
216
  class SpecParserError extends Error {
@@ -194,201 +222,30 @@ class SpecParserError extends Error {
194
222
 
195
223
  // Copyright (c) Microsoft Corporation.
196
224
  class Utils {
197
- static checkParameters(paramObject) {
198
- const paramResult = {
199
- requiredNum: 0,
200
- optionalNum: 0,
201
- isValid: true,
202
- };
203
- if (!paramObject) {
204
- return paramResult;
205
- }
206
- for (let i = 0; i < paramObject.length; i++) {
207
- const param = paramObject[i];
208
- const schema = param.schema;
209
- const isRequiredWithoutDefault = param.required && schema.default === undefined;
210
- if (param.in === "header" || param.in === "cookie") {
211
- if (isRequiredWithoutDefault) {
212
- paramResult.isValid = false;
213
- }
214
- continue;
215
- }
216
- if (schema.type !== "boolean" &&
217
- schema.type !== "string" &&
218
- schema.type !== "number" &&
219
- schema.type !== "integer") {
220
- if (isRequiredWithoutDefault) {
221
- paramResult.isValid = false;
222
- }
223
- continue;
224
- }
225
- if (param.in === "query" || param.in === "path") {
226
- if (isRequiredWithoutDefault) {
227
- paramResult.requiredNum = paramResult.requiredNum + 1;
228
- }
229
- else {
230
- paramResult.optionalNum = paramResult.optionalNum + 1;
231
- }
232
- }
233
- }
234
- return paramResult;
235
- }
236
- static checkPostBody(schema, isRequired = false) {
237
- var _a;
238
- const paramResult = {
239
- requiredNum: 0,
240
- optionalNum: 0,
241
- isValid: true,
242
- };
243
- if (Object.keys(schema).length === 0) {
244
- return paramResult;
245
- }
246
- const isRequiredWithoutDefault = isRequired && schema.default === undefined;
247
- if (schema.type === "string" ||
248
- schema.type === "integer" ||
249
- schema.type === "boolean" ||
250
- schema.type === "number") {
251
- if (isRequiredWithoutDefault) {
252
- paramResult.requiredNum = paramResult.requiredNum + 1;
253
- }
254
- else {
255
- paramResult.optionalNum = paramResult.optionalNum + 1;
256
- }
257
- }
258
- else if (schema.type === "object") {
259
- const { properties } = schema;
260
- for (const property in properties) {
261
- let isRequired = false;
262
- if (schema.required && ((_a = schema.required) === null || _a === void 0 ? void 0 : _a.indexOf(property)) >= 0) {
263
- isRequired = true;
264
- }
265
- const result = Utils.checkPostBody(properties[property], isRequired);
266
- paramResult.requiredNum += result.requiredNum;
267
- paramResult.optionalNum += result.optionalNum;
268
- paramResult.isValid = paramResult.isValid && result.isValid;
269
- }
270
- }
271
- else {
272
- if (isRequiredWithoutDefault) {
273
- paramResult.isValid = false;
274
- }
275
- }
276
- return paramResult;
277
- }
278
- /**
279
- * Checks if the given API is supported.
280
- * @param {string} method - The HTTP method of the API.
281
- * @param {string} path - The path of the API.
282
- * @param {OpenAPIV3.Document} spec - The OpenAPI specification document.
283
- * @returns {boolean} - Returns true if the API is supported, false otherwise.
284
- * @description The following APIs are supported:
285
- * 1. only support Get/Post operation without auth property
286
- * 2. parameter inside query or path only support string, number, boolean and integer
287
- * 3. parameter inside post body only support string, number, boolean, integer and object
288
- * 4. request body + required parameters <= 1
289
- * 5. response body should be “application/json” and not empty, and response code should be 20X
290
- * 6. only support request body with “application/json” content type
291
- */
292
- static isSupportedApi(method, path, spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2) {
293
- const pathObj = spec.paths[path];
294
- method = method.toLocaleLowerCase();
295
- if (pathObj) {
296
- if ((method === ConstantString.PostMethod || method === ConstantString.GetMethod) &&
297
- pathObj[method]) {
298
- const securities = pathObj[method].security;
299
- const authArray = Utils.getAuthArray(securities, spec);
300
- if (!Utils.isSupportedAuth(authArray, allowAPIKeyAuth, allowOauth2)) {
301
- return false;
302
- }
303
- const operationObject = pathObj[method];
304
- if (!allowMissingId && !operationObject.operationId) {
305
- return false;
306
- }
307
- const paramObject = operationObject.parameters;
308
- const requestBody = operationObject.requestBody;
309
- const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
310
- const mediaTypesCount = Object.keys((requestBody === null || requestBody === void 0 ? void 0 : requestBody.content) || {}).length;
311
- if (mediaTypesCount > 1) {
312
- return false;
313
- }
314
- const responseJson = Utils.getResponseJson(operationObject);
315
- if (Object.keys(responseJson).length === 0) {
316
- return false;
317
- }
318
- let requestBodyParamResult = {
319
- requiredNum: 0,
320
- optionalNum: 0,
321
- isValid: true,
322
- };
323
- if (requestJsonBody) {
324
- const requestBodySchema = requestJsonBody.schema;
325
- requestBodyParamResult = Utils.checkPostBody(requestBodySchema, requestBody.required);
326
- }
327
- if (!requestBodyParamResult.isValid) {
328
- return false;
329
- }
330
- const paramResult = Utils.checkParameters(paramObject);
331
- if (!paramResult.isValid) {
332
- return false;
333
- }
334
- if (requestBodyParamResult.requiredNum + paramResult.requiredNum > 1) {
335
- if (allowMultipleParameters &&
336
- requestBodyParamResult.requiredNum + paramResult.requiredNum <= 5) {
337
- return true;
338
- }
339
- return false;
340
- }
341
- else if (requestBodyParamResult.requiredNum +
342
- requestBodyParamResult.optionalNum +
343
- paramResult.requiredNum +
344
- paramResult.optionalNum ===
345
- 0) {
346
- return false;
347
- }
348
- else {
225
+ static hasNestedObjectInSchema(schema) {
226
+ if (schema.type === "object") {
227
+ for (const property in schema.properties) {
228
+ const nestedSchema = schema.properties[property];
229
+ if (nestedSchema.type === "object") {
349
230
  return true;
350
231
  }
351
232
  }
352
233
  }
353
234
  return false;
354
235
  }
355
- static isSupportedAuth(authSchemaArray, allowAPIKeyAuth, allowOauth2) {
356
- if (authSchemaArray.length === 0) {
357
- return true;
358
- }
359
- if (allowAPIKeyAuth || allowOauth2) {
360
- // Currently we don't support multiple auth in one operation
361
- if (authSchemaArray.length > 0 && authSchemaArray.every((auths) => auths.length > 1)) {
362
- return false;
363
- }
364
- for (const auths of authSchemaArray) {
365
- if (auths.length === 1) {
366
- if (!allowOauth2 && allowAPIKeyAuth && Utils.isAPIKeyAuth(auths[0].authSchema)) {
367
- return true;
368
- }
369
- else if (!allowAPIKeyAuth &&
370
- allowOauth2 &&
371
- Utils.isBearerTokenAuth(auths[0].authSchema)) {
372
- return true;
373
- }
374
- else if (allowAPIKeyAuth &&
375
- allowOauth2 &&
376
- (Utils.isAPIKeyAuth(auths[0].authSchema) ||
377
- Utils.isBearerTokenAuth(auths[0].authSchema))) {
378
- return true;
379
- }
380
- }
381
- }
382
- }
383
- return false;
236
+ static containMultipleMediaTypes(bodyObject) {
237
+ return Object.keys((bodyObject === null || bodyObject === void 0 ? void 0 : bodyObject.content) || {}).length > 1;
238
+ }
239
+ static isBearerTokenAuth(authScheme) {
240
+ return authScheme.type === "http" && authScheme.scheme === "bearer";
384
241
  }
385
- static isAPIKeyAuth(authSchema) {
386
- return authSchema.type === "apiKey";
242
+ static isAPIKeyAuth(authScheme) {
243
+ return authScheme.type === "apiKey";
387
244
  }
388
- static isBearerTokenAuth(authSchema) {
389
- return (authSchema.type === "oauth2" ||
390
- authSchema.type === "openIdConnect" ||
391
- (authSchema.type === "http" && authSchema.scheme === "bearer"));
245
+ static isOAuthWithAuthCodeFlow(authScheme) {
246
+ return !!(authScheme.type === "oauth2" &&
247
+ authScheme.flows &&
248
+ authScheme.flows.authorizationCode);
392
249
  }
393
250
  static getAuthArray(securities, spec) {
394
251
  var _a;
@@ -401,7 +258,7 @@ class Utils {
401
258
  for (const name in security) {
402
259
  const auth = securitySchemas[name];
403
260
  authArray.push({
404
- authSchema: auth,
261
+ authScheme: auth,
405
262
  name: name,
406
263
  });
407
264
  }
@@ -419,18 +276,22 @@ class Utils {
419
276
  static getResponseJson(operationObject) {
420
277
  var _a, _b;
421
278
  let json = {};
279
+ let multipleMediaType = false;
422
280
  for (const code of ConstantString.ResponseCodeFor20X) {
423
281
  const responseObject = (_a = operationObject === null || operationObject === void 0 ? void 0 : operationObject.responses) === null || _a === void 0 ? void 0 : _a[code];
424
- const mediaTypesCount = Object.keys((responseObject === null || responseObject === void 0 ? void 0 : responseObject.content) || {}).length;
425
- if (mediaTypesCount > 1) {
426
- return {};
427
- }
428
282
  if ((_b = responseObject === null || responseObject === void 0 ? void 0 : responseObject.content) === null || _b === void 0 ? void 0 : _b["application/json"]) {
283
+ multipleMediaType = false;
429
284
  json = responseObject.content["application/json"];
430
- break;
285
+ if (Utils.containMultipleMediaTypes(responseObject)) {
286
+ multipleMediaType = true;
287
+ json = {};
288
+ }
289
+ else {
290
+ break;
291
+ }
431
292
  }
432
293
  }
433
- return json;
294
+ return { json, multipleMediaType };
434
295
  }
435
296
  static convertPathToCamelCase(path) {
436
297
  const pathSegments = path.split(/[./{]/);
@@ -450,10 +311,10 @@ class Utils {
450
311
  return undefined;
451
312
  }
452
313
  }
453
- static resolveServerUrl(url) {
314
+ static resolveEnv(str) {
454
315
  const placeHolderReg = /\${{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*}}/g;
455
- let matches = placeHolderReg.exec(url);
456
- let newUrl = url;
316
+ let matches = placeHolderReg.exec(str);
317
+ let newStr = str;
457
318
  while (matches != null) {
458
319
  const envVar = matches[1];
459
320
  const envVal = process.env[envVar];
@@ -461,17 +322,17 @@ class Utils {
461
322
  throw new Error(Utils.format(ConstantString.ResolveServerUrlFailed, envVar));
462
323
  }
463
324
  else {
464
- newUrl = newUrl.replace(matches[0], envVal);
325
+ newStr = newStr.replace(matches[0], envVal);
465
326
  }
466
- matches = placeHolderReg.exec(url);
327
+ matches = placeHolderReg.exec(str);
467
328
  }
468
- return newUrl;
329
+ return newStr;
469
330
  }
470
331
  static checkServerUrl(servers) {
471
332
  const errors = [];
472
333
  let serverUrl;
473
334
  try {
474
- serverUrl = Utils.resolveServerUrl(servers[0].url);
335
+ serverUrl = Utils.resolveEnv(servers[0].url);
475
336
  }
476
337
  catch (err) {
477
338
  errors.push({
@@ -501,7 +362,8 @@ class Utils {
501
362
  }
502
363
  return errors;
503
364
  }
504
- static validateServer(spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2) {
365
+ static validateServer(spec, options) {
366
+ var _a;
505
367
  const errors = [];
506
368
  let hasTopLevelServers = false;
507
369
  let hasPathLevelServers = false;
@@ -522,7 +384,7 @@ class Utils {
522
384
  }
523
385
  for (const method in methods) {
524
386
  const operationObject = methods[method];
525
- if (Utils.isSupportedApi(method, path, spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2)) {
387
+ if (((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && operationObject) {
526
388
  if ((operationObject === null || operationObject === void 0 ? void 0 : operationObject.servers) && operationObject.servers.length >= 1) {
527
389
  hasOperationLevelServers = true;
528
390
  const serverErrors = Utils.checkServerUrl(operationObject.servers);
@@ -565,6 +427,7 @@ class Utils {
565
427
  Utils.updateParameterWithInputType(schema, parameter);
566
428
  }
567
429
  if (isRequired && schema.default === undefined) {
430
+ parameter.isRequired = true;
568
431
  requiredParams.push(parameter);
569
432
  }
570
433
  else {
@@ -609,7 +472,7 @@ class Utils {
609
472
  param.value = schema.default;
610
473
  }
611
474
  }
612
- static parseApiInfo(operationItem, allowMultipleParameters) {
475
+ static parseApiInfo(operationItem, options) {
613
476
  var _a, _b;
614
477
  const requiredParams = [];
615
478
  const optionalParams = [];
@@ -623,11 +486,12 @@ class Utils {
623
486
  description: ((_a = param.description) !== null && _a !== void 0 ? _a : "").slice(0, ConstantString.ParameterDescriptionMaxLens),
624
487
  };
625
488
  const schema = param.schema;
626
- if (allowMultipleParameters && schema) {
489
+ if (options.allowMultipleParameters && schema) {
627
490
  Utils.updateParameterWithInputType(schema, parameter);
628
491
  }
629
492
  if (param.in !== "header" && param.in !== "cookie") {
630
493
  if (param.required && (schema === null || schema === void 0 ? void 0 : schema.default) === undefined) {
494
+ parameter.isRequired = true;
631
495
  requiredParams.push(parameter);
632
496
  }
633
497
  else {
@@ -641,19 +505,13 @@ class Utils {
641
505
  const requestJson = requestBody.content["application/json"];
642
506
  if (Object.keys(requestJson).length !== 0) {
643
507
  const schema = requestJson.schema;
644
- const [requiredP, optionalP] = Utils.generateParametersFromSchema(schema, "requestBody", allowMultipleParameters, requestBody.required);
508
+ const [requiredP, optionalP] = Utils.generateParametersFromSchema(schema, "requestBody", !!options.allowMultipleParameters, requestBody.required);
645
509
  requiredParams.push(...requiredP);
646
510
  optionalParams.push(...optionalP);
647
511
  }
648
512
  }
649
513
  const operationId = operationItem.operationId;
650
- const parameters = [];
651
- if (requiredParams.length !== 0) {
652
- parameters.push(...requiredParams);
653
- }
654
- else {
655
- parameters.push(optionalParams[0]);
656
- }
514
+ const parameters = [...requiredParams, ...optionalParams];
657
515
  const command = {
658
516
  context: ["compose"],
659
517
  type: "query",
@@ -662,130 +520,568 @@ class Utils {
662
520
  parameters: parameters,
663
521
  description: ((_b = operationItem.description) !== null && _b !== void 0 ? _b : "").slice(0, ConstantString.CommandDescriptionMaxLens),
664
522
  };
665
- let warning = undefined;
666
- if (requiredParams.length === 0 && optionalParams.length > 1) {
667
- warning = {
668
- type: exports.WarningType.OperationOnlyContainsOptionalParam,
669
- content: Utils.format(ConstantString.OperationOnlyContainsOptionalParam, operationId),
670
- data: operationId,
671
- };
523
+ return command;
524
+ }
525
+ static format(str, ...args) {
526
+ let index = 0;
527
+ return str.replace(/%s/g, () => {
528
+ const arg = args[index++];
529
+ return arg !== undefined ? arg : "";
530
+ });
531
+ }
532
+ static getSafeRegistrationIdEnvName(authName) {
533
+ if (!authName) {
534
+ return "";
535
+ }
536
+ let safeRegistrationIdEnvName = authName.toUpperCase().replace(/[^A-Z0-9_]/g, "_");
537
+ if (!safeRegistrationIdEnvName.match(/^[A-Z]/)) {
538
+ safeRegistrationIdEnvName = "PREFIX_" + safeRegistrationIdEnvName;
672
539
  }
673
- return [command, warning];
540
+ return safeRegistrationIdEnvName;
674
541
  }
675
- static listSupportedAPIs(spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2) {
676
- const paths = spec.paths;
542
+ static getServerObject(spec, method, path) {
543
+ const pathObj = spec.paths[path];
544
+ const operationObject = pathObj[method];
545
+ const rootServer = spec.servers && spec.servers[0];
546
+ const methodServer = spec.paths[path].servers && spec.paths[path].servers[0];
547
+ const operationServer = operationObject.servers && operationObject.servers[0];
548
+ const serverUrl = operationServer || methodServer || rootServer;
549
+ return serverUrl;
550
+ }
551
+ }
552
+
553
+ // Copyright (c) Microsoft Corporation.
554
+ class Validator {
555
+ listAPIs() {
556
+ var _a;
557
+ if (this.apiMap) {
558
+ return this.apiMap;
559
+ }
560
+ const paths = this.spec.paths;
677
561
  const result = {};
678
562
  for (const path in paths) {
679
563
  const methods = paths[path];
680
564
  for (const method in methods) {
681
- // For developer preview, only support GET operation with only 1 parameter without auth
682
- if (Utils.isSupportedApi(method, path, spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2)) {
683
- const operationObject = methods[method];
684
- result[`${method.toUpperCase()} ${path}`] = operationObject;
565
+ const operationObject = methods[method];
566
+ if (((_a = this.options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && operationObject) {
567
+ const validateResult = this.validateAPI(method, path);
568
+ result[`${method.toUpperCase()} ${path}`] = {
569
+ operation: operationObject,
570
+ isValid: validateResult.isValid,
571
+ reason: validateResult.reason,
572
+ };
685
573
  }
686
574
  }
687
575
  }
576
+ this.apiMap = result;
688
577
  return result;
689
578
  }
690
- static validateSpec(spec, parser, isSwaggerFile, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2) {
691
- const errors = [];
692
- const warnings = [];
693
- if (isSwaggerFile) {
694
- warnings.push({
695
- type: exports.WarningType.ConvertSwaggerToOpenAPI,
696
- content: ConstantString.ConvertSwaggerToOpenAPI,
579
+ validateSpecVersion() {
580
+ const result = { errors: [], warnings: [] };
581
+ if (this.spec.openapi >= "3.1.0") {
582
+ result.errors.push({
583
+ type: exports.ErrorType.SpecVersionNotSupported,
584
+ content: Utils.format(ConstantString.SpecVersionNotSupported, this.spec.openapi),
585
+ data: this.spec.openapi,
697
586
  });
698
587
  }
699
- // Server validation
700
- const serverErrors = Utils.validateServer(spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2);
701
- errors.push(...serverErrors);
702
- // Remote reference not supported
703
- const refPaths = parser.$refs.paths();
704
- // refPaths [0] is the current spec file path
705
- if (refPaths.length > 1) {
706
- errors.push({
707
- type: exports.ErrorType.RemoteRefNotSupported,
708
- content: Utils.format(ConstantString.RemoteRefNotSupported, refPaths.join(", ")),
709
- data: refPaths,
710
- });
711
- }
712
- // No supported API
713
- const apiMap = Utils.listSupportedAPIs(spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2);
714
- if (Object.keys(apiMap).length === 0) {
715
- errors.push({
588
+ return result;
589
+ }
590
+ validateSpecServer() {
591
+ const result = { errors: [], warnings: [] };
592
+ const serverErrors = Utils.validateServer(this.spec, this.options);
593
+ result.errors.push(...serverErrors);
594
+ return result;
595
+ }
596
+ validateSpecNoSupportAPI() {
597
+ const result = { errors: [], warnings: [] };
598
+ const apiMap = this.listAPIs();
599
+ const validAPIs = Object.entries(apiMap).filter(([, value]) => value.isValid);
600
+ if (validAPIs.length === 0) {
601
+ const data = [];
602
+ for (const key in apiMap) {
603
+ const { reason } = apiMap[key];
604
+ const apiInvalidReason = { api: key, reason: reason };
605
+ data.push(apiInvalidReason);
606
+ }
607
+ result.errors.push({
716
608
  type: exports.ErrorType.NoSupportedApi,
717
609
  content: ConstantString.NoSupportedApi,
610
+ data,
718
611
  });
719
612
  }
613
+ return result;
614
+ }
615
+ validateSpecOperationId() {
616
+ const result = { errors: [], warnings: [] };
617
+ const apiMap = this.listAPIs();
720
618
  // OperationId missing
721
619
  const apisMissingOperationId = [];
722
620
  for (const key in apiMap) {
723
- const pathObjectItem = apiMap[key];
724
- if (!pathObjectItem.operationId) {
621
+ const { operation } = apiMap[key];
622
+ if (!operation.operationId) {
725
623
  apisMissingOperationId.push(key);
726
624
  }
727
625
  }
728
626
  if (apisMissingOperationId.length > 0) {
729
- warnings.push({
627
+ result.warnings.push({
730
628
  type: exports.WarningType.OperationIdMissing,
731
629
  content: Utils.format(ConstantString.MissingOperationId, apisMissingOperationId.join(", ")),
732
630
  data: apisMissingOperationId,
733
631
  });
734
632
  }
735
- let status = exports.ValidationStatus.Valid;
736
- if (warnings.length > 0 && errors.length === 0) {
737
- status = exports.ValidationStatus.Warning;
633
+ return result;
634
+ }
635
+ validateMethodAndPath(method, path) {
636
+ const result = { isValid: true, reason: [] };
637
+ if (this.options.allowMethods && !this.options.allowMethods.includes(method)) {
638
+ result.isValid = false;
639
+ result.reason.push(exports.ErrorType.MethodNotAllowed);
640
+ return result;
641
+ }
642
+ const pathObj = this.spec.paths[path];
643
+ if (!pathObj || !pathObj[method]) {
644
+ result.isValid = false;
645
+ result.reason.push(exports.ErrorType.UrlPathNotExist);
646
+ return result;
647
+ }
648
+ return result;
649
+ }
650
+ validateResponse(method, path) {
651
+ const result = { isValid: true, reason: [] };
652
+ const operationObject = this.spec.paths[path][method];
653
+ const { json, multipleMediaType } = Utils.getResponseJson(operationObject);
654
+ // only support response body only contains “application/json” content type
655
+ if (multipleMediaType) {
656
+ result.reason.push(exports.ErrorType.ResponseContainMultipleMediaTypes);
657
+ }
658
+ else if (Object.keys(json).length === 0) {
659
+ // response body should not be empty
660
+ result.reason.push(exports.ErrorType.ResponseJsonIsEmpty);
661
+ }
662
+ return result;
663
+ }
664
+ validateServer(method, path) {
665
+ const result = { isValid: true, reason: [] };
666
+ const serverObj = Utils.getServerObject(this.spec, method, path);
667
+ if (!serverObj) {
668
+ // should contain server URL
669
+ result.reason.push(exports.ErrorType.NoServerInformation);
670
+ }
671
+ else {
672
+ // server url should be absolute url with https protocol
673
+ const serverValidateResult = Utils.checkServerUrl([serverObj]);
674
+ result.reason.push(...serverValidateResult.map((item) => item.type));
738
675
  }
739
- else if (errors.length > 0) {
740
- status = exports.ValidationStatus.Error;
676
+ return result;
677
+ }
678
+ validateAuth(method, path) {
679
+ const pathObj = this.spec.paths[path];
680
+ const operationObject = pathObj[method];
681
+ const securities = operationObject.security;
682
+ const authSchemeArray = Utils.getAuthArray(securities, this.spec);
683
+ if (authSchemeArray.length === 0) {
684
+ return { isValid: true, reason: [] };
741
685
  }
742
- return {
743
- status,
744
- warnings,
745
- errors,
686
+ if (this.options.allowAPIKeyAuth ||
687
+ this.options.allowOauth2 ||
688
+ this.options.allowBearerTokenAuth) {
689
+ // Currently we don't support multiple auth in one operation
690
+ if (authSchemeArray.length > 0 && authSchemeArray.every((auths) => auths.length > 1)) {
691
+ return {
692
+ isValid: false,
693
+ reason: [exports.ErrorType.MultipleAuthNotSupported],
694
+ };
695
+ }
696
+ for (const auths of authSchemeArray) {
697
+ if (auths.length === 1) {
698
+ if ((this.options.allowAPIKeyAuth && Utils.isAPIKeyAuth(auths[0].authScheme)) ||
699
+ (this.options.allowOauth2 && Utils.isOAuthWithAuthCodeFlow(auths[0].authScheme)) ||
700
+ (this.options.allowBearerTokenAuth && Utils.isBearerTokenAuth(auths[0].authScheme))) {
701
+ return { isValid: true, reason: [] };
702
+ }
703
+ }
704
+ }
705
+ }
706
+ return { isValid: false, reason: [exports.ErrorType.AuthTypeIsNotSupported] };
707
+ }
708
+ checkPostBodySchema(schema, isRequired = false) {
709
+ var _a;
710
+ const paramResult = {
711
+ requiredNum: 0,
712
+ optionalNum: 0,
713
+ isValid: true,
714
+ reason: [],
746
715
  };
716
+ if (Object.keys(schema).length === 0) {
717
+ return paramResult;
718
+ }
719
+ const isRequiredWithoutDefault = isRequired && schema.default === undefined;
720
+ const isCopilot = this.projectType === exports.ProjectType.Copilot;
721
+ if (isCopilot && this.hasNestedObjectInSchema(schema)) {
722
+ paramResult.isValid = false;
723
+ paramResult.reason = [exports.ErrorType.RequestBodyContainsNestedObject];
724
+ return paramResult;
725
+ }
726
+ if (schema.type === "string" ||
727
+ schema.type === "integer" ||
728
+ schema.type === "boolean" ||
729
+ schema.type === "number") {
730
+ if (isRequiredWithoutDefault) {
731
+ paramResult.requiredNum = paramResult.requiredNum + 1;
732
+ }
733
+ else {
734
+ paramResult.optionalNum = paramResult.optionalNum + 1;
735
+ }
736
+ }
737
+ else if (schema.type === "object") {
738
+ const { properties } = schema;
739
+ for (const property in properties) {
740
+ let isRequired = false;
741
+ if (schema.required && ((_a = schema.required) === null || _a === void 0 ? void 0 : _a.indexOf(property)) >= 0) {
742
+ isRequired = true;
743
+ }
744
+ const result = this.checkPostBodySchema(properties[property], isRequired);
745
+ paramResult.requiredNum += result.requiredNum;
746
+ paramResult.optionalNum += result.optionalNum;
747
+ paramResult.isValid = paramResult.isValid && result.isValid;
748
+ paramResult.reason.push(...result.reason);
749
+ }
750
+ }
751
+ else {
752
+ if (isRequiredWithoutDefault && !isCopilot) {
753
+ paramResult.isValid = false;
754
+ paramResult.reason.push(exports.ErrorType.PostBodyContainsRequiredUnsupportedSchema);
755
+ }
756
+ }
757
+ return paramResult;
747
758
  }
748
- static format(str, ...args) {
749
- let index = 0;
750
- return str.replace(/%s/g, () => {
751
- const arg = args[index++];
752
- return arg !== undefined ? arg : "";
753
- });
759
+ checkParamSchema(paramObject) {
760
+ const paramResult = {
761
+ requiredNum: 0,
762
+ optionalNum: 0,
763
+ isValid: true,
764
+ reason: [],
765
+ };
766
+ if (!paramObject) {
767
+ return paramResult;
768
+ }
769
+ const isCopilot = this.projectType === exports.ProjectType.Copilot;
770
+ for (let i = 0; i < paramObject.length; i++) {
771
+ const param = paramObject[i];
772
+ const schema = param.schema;
773
+ if (isCopilot && this.hasNestedObjectInSchema(schema)) {
774
+ paramResult.isValid = false;
775
+ paramResult.reason.push(exports.ErrorType.ParamsContainsNestedObject);
776
+ continue;
777
+ }
778
+ const isRequiredWithoutDefault = param.required && schema.default === undefined;
779
+ if (isCopilot) {
780
+ if (isRequiredWithoutDefault) {
781
+ paramResult.requiredNum = paramResult.requiredNum + 1;
782
+ }
783
+ else {
784
+ paramResult.optionalNum = paramResult.optionalNum + 1;
785
+ }
786
+ continue;
787
+ }
788
+ if (param.in === "header" || param.in === "cookie") {
789
+ if (isRequiredWithoutDefault) {
790
+ paramResult.isValid = false;
791
+ paramResult.reason.push(exports.ErrorType.ParamsContainRequiredUnsupportedSchema);
792
+ }
793
+ continue;
794
+ }
795
+ if (schema.type !== "boolean" &&
796
+ schema.type !== "string" &&
797
+ schema.type !== "number" &&
798
+ schema.type !== "integer") {
799
+ if (isRequiredWithoutDefault) {
800
+ paramResult.isValid = false;
801
+ paramResult.reason.push(exports.ErrorType.ParamsContainRequiredUnsupportedSchema);
802
+ }
803
+ continue;
804
+ }
805
+ if (param.in === "query" || param.in === "path") {
806
+ if (isRequiredWithoutDefault) {
807
+ paramResult.requiredNum = paramResult.requiredNum + 1;
808
+ }
809
+ else {
810
+ paramResult.optionalNum = paramResult.optionalNum + 1;
811
+ }
812
+ }
813
+ }
814
+ return paramResult;
754
815
  }
755
- static getSafeRegistrationIdEnvName(authName) {
756
- if (!authName) {
757
- return "";
816
+ hasNestedObjectInSchema(schema) {
817
+ if (schema.type === "object") {
818
+ for (const property in schema.properties) {
819
+ const nestedSchema = schema.properties[property];
820
+ if (nestedSchema.type === "object") {
821
+ return true;
822
+ }
823
+ }
758
824
  }
759
- let safeRegistrationIdEnvName = authName.toUpperCase().replace(/[^A-Z0-9_]/g, "_");
760
- if (!safeRegistrationIdEnvName.match(/^[A-Z]/)) {
761
- safeRegistrationIdEnvName = "PREFIX_" + safeRegistrationIdEnvName;
825
+ return false;
826
+ }
827
+ }
828
+
829
+ // Copyright (c) Microsoft Corporation.
830
+ class CopilotValidator extends Validator {
831
+ constructor(spec, options) {
832
+ super();
833
+ this.projectType = exports.ProjectType.Copilot;
834
+ this.options = options;
835
+ this.spec = spec;
836
+ }
837
+ validateSpec() {
838
+ const result = { errors: [], warnings: [] };
839
+ // validate spec version
840
+ let validationResult = this.validateSpecVersion();
841
+ result.errors.push(...validationResult.errors);
842
+ // validate spec server
843
+ validationResult = this.validateSpecServer();
844
+ result.errors.push(...validationResult.errors);
845
+ // validate no supported API
846
+ validationResult = this.validateSpecNoSupportAPI();
847
+ result.errors.push(...validationResult.errors);
848
+ // validate operationId missing
849
+ validationResult = this.validateSpecOperationId();
850
+ result.warnings.push(...validationResult.warnings);
851
+ return result;
852
+ }
853
+ validateAPI(method, path) {
854
+ const result = { isValid: true, reason: [] };
855
+ method = method.toLocaleLowerCase();
856
+ // validate method and path
857
+ const methodAndPathResult = this.validateMethodAndPath(method, path);
858
+ if (!methodAndPathResult.isValid) {
859
+ return methodAndPathResult;
860
+ }
861
+ const operationObject = this.spec.paths[path][method];
862
+ // validate auth
863
+ const authCheckResult = this.validateAuth(method, path);
864
+ result.reason.push(...authCheckResult.reason);
865
+ // validate operationId
866
+ if (!this.options.allowMissingId && !operationObject.operationId) {
867
+ result.reason.push(exports.ErrorType.MissingOperationId);
868
+ }
869
+ // validate server
870
+ const validateServerResult = this.validateServer(method, path);
871
+ result.reason.push(...validateServerResult.reason);
872
+ // validate response
873
+ const validateResponseResult = this.validateResponse(method, path);
874
+ result.reason.push(...validateResponseResult.reason);
875
+ // validate requestBody
876
+ const requestBody = operationObject.requestBody;
877
+ const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
878
+ if (Utils.containMultipleMediaTypes(requestBody)) {
879
+ result.reason.push(exports.ErrorType.PostBodyContainMultipleMediaTypes);
880
+ }
881
+ if (requestJsonBody) {
882
+ const requestBodySchema = requestJsonBody.schema;
883
+ if (requestBodySchema.type !== "object") {
884
+ result.reason.push(exports.ErrorType.PostBodySchemaIsNotJson);
885
+ }
886
+ const requestBodyParamResult = this.checkPostBodySchema(requestBodySchema, requestBody.required);
887
+ result.reason.push(...requestBodyParamResult.reason);
888
+ }
889
+ // validate parameters
890
+ const paramObject = operationObject.parameters;
891
+ const paramResult = this.checkParamSchema(paramObject);
892
+ result.reason.push(...paramResult.reason);
893
+ if (result.reason.length > 0) {
894
+ result.isValid = false;
895
+ }
896
+ return result;
897
+ }
898
+ }
899
+
900
+ // Copyright (c) Microsoft Corporation.
901
+ class SMEValidator extends Validator {
902
+ constructor(spec, options) {
903
+ super();
904
+ this.projectType = exports.ProjectType.SME;
905
+ this.options = options;
906
+ this.spec = spec;
907
+ }
908
+ validateSpec() {
909
+ const result = { errors: [], warnings: [] };
910
+ // validate spec version
911
+ let validationResult = this.validateSpecVersion();
912
+ result.errors.push(...validationResult.errors);
913
+ // validate spec server
914
+ validationResult = this.validateSpecServer();
915
+ result.errors.push(...validationResult.errors);
916
+ // validate no supported API
917
+ validationResult = this.validateSpecNoSupportAPI();
918
+ result.errors.push(...validationResult.errors);
919
+ // validate operationId missing
920
+ if (this.options.allowMissingId) {
921
+ validationResult = this.validateSpecOperationId();
922
+ result.warnings.push(...validationResult.warnings);
923
+ }
924
+ return result;
925
+ }
926
+ validateAPI(method, path) {
927
+ const result = { isValid: true, reason: [] };
928
+ method = method.toLocaleLowerCase();
929
+ // validate method and path
930
+ const methodAndPathResult = this.validateMethodAndPath(method, path);
931
+ if (!methodAndPathResult.isValid) {
932
+ return methodAndPathResult;
933
+ }
934
+ const operationObject = this.spec.paths[path][method];
935
+ // validate auth
936
+ const authCheckResult = this.validateAuth(method, path);
937
+ result.reason.push(...authCheckResult.reason);
938
+ // validate operationId
939
+ if (!this.options.allowMissingId && !operationObject.operationId) {
940
+ result.reason.push(exports.ErrorType.MissingOperationId);
941
+ }
942
+ // validate server
943
+ const validateServerResult = this.validateServer(method, path);
944
+ result.reason.push(...validateServerResult.reason);
945
+ // validate response
946
+ const validateResponseResult = this.validateResponse(method, path);
947
+ result.reason.push(...validateResponseResult.reason);
948
+ let postBodyResult = {
949
+ requiredNum: 0,
950
+ optionalNum: 0,
951
+ isValid: true,
952
+ reason: [],
953
+ };
954
+ // validate requestBody
955
+ const requestBody = operationObject.requestBody;
956
+ const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
957
+ if (Utils.containMultipleMediaTypes(requestBody)) {
958
+ result.reason.push(exports.ErrorType.PostBodyContainMultipleMediaTypes);
959
+ }
960
+ if (requestJsonBody) {
961
+ const requestBodySchema = requestJsonBody.schema;
962
+ postBodyResult = this.checkPostBodySchema(requestBodySchema, requestBody.required);
963
+ result.reason.push(...postBodyResult.reason);
964
+ }
965
+ // validate parameters
966
+ const paramObject = operationObject.parameters;
967
+ const paramResult = this.checkParamSchema(paramObject);
968
+ result.reason.push(...paramResult.reason);
969
+ // validate total parameters count
970
+ if (paramResult.isValid && postBodyResult.isValid) {
971
+ const paramCountResult = this.validateParamCount(postBodyResult, paramResult);
972
+ result.reason.push(...paramCountResult.reason);
973
+ }
974
+ if (result.reason.length > 0) {
975
+ result.isValid = false;
976
+ }
977
+ return result;
978
+ }
979
+ validateParamCount(postBodyResult, paramResult) {
980
+ const result = { isValid: true, reason: [] };
981
+ const totalRequiredParams = postBodyResult.requiredNum + paramResult.requiredNum;
982
+ const totalParams = totalRequiredParams + postBodyResult.optionalNum + paramResult.optionalNum;
983
+ if (totalRequiredParams > 1) {
984
+ if (!this.options.allowMultipleParameters ||
985
+ totalRequiredParams > SMEValidator.SMERequiredParamsMaxNum) {
986
+ result.reason.push(exports.ErrorType.ExceededRequiredParamsLimit);
987
+ }
988
+ }
989
+ else if (totalParams === 0) {
990
+ result.reason.push(exports.ErrorType.NoParameter);
991
+ }
992
+ return result;
993
+ }
994
+ }
995
+ SMEValidator.SMERequiredParamsMaxNum = 5;
996
+
997
+ // Copyright (c) Microsoft Corporation.
998
+ class TeamsAIValidator extends Validator {
999
+ constructor(spec, options) {
1000
+ super();
1001
+ this.projectType = exports.ProjectType.TeamsAi;
1002
+ this.options = options;
1003
+ this.spec = spec;
1004
+ }
1005
+ validateSpec() {
1006
+ const result = { errors: [], warnings: [] };
1007
+ // validate spec server
1008
+ let validationResult = this.validateSpecServer();
1009
+ result.errors.push(...validationResult.errors);
1010
+ // validate no supported API
1011
+ validationResult = this.validateSpecNoSupportAPI();
1012
+ result.errors.push(...validationResult.errors);
1013
+ return result;
1014
+ }
1015
+ validateAPI(method, path) {
1016
+ const result = { isValid: true, reason: [] };
1017
+ method = method.toLocaleLowerCase();
1018
+ // validate method and path
1019
+ const methodAndPathResult = this.validateMethodAndPath(method, path);
1020
+ if (!methodAndPathResult.isValid) {
1021
+ return methodAndPathResult;
1022
+ }
1023
+ const operationObject = this.spec.paths[path][method];
1024
+ // validate operationId
1025
+ if (!this.options.allowMissingId && !operationObject.operationId) {
1026
+ result.reason.push(exports.ErrorType.MissingOperationId);
1027
+ }
1028
+ // validate server
1029
+ const validateServerResult = this.validateServer(method, path);
1030
+ result.reason.push(...validateServerResult.reason);
1031
+ if (result.reason.length > 0) {
1032
+ result.isValid = false;
1033
+ }
1034
+ return result;
1035
+ }
1036
+ }
1037
+
1038
+ class ValidatorFactory {
1039
+ static create(spec, options) {
1040
+ var _a;
1041
+ const type = (_a = options.projectType) !== null && _a !== void 0 ? _a : exports.ProjectType.SME;
1042
+ switch (type) {
1043
+ case exports.ProjectType.SME:
1044
+ return new SMEValidator(spec, options);
1045
+ case exports.ProjectType.Copilot:
1046
+ return new CopilotValidator(spec, options);
1047
+ case exports.ProjectType.TeamsAi:
1048
+ return new TeamsAIValidator(spec, options);
1049
+ default:
1050
+ throw new Error(`Invalid project type: ${type}`);
762
1051
  }
763
- return safeRegistrationIdEnvName;
764
1052
  }
765
1053
  }
766
1054
 
767
1055
  // Copyright (c) Microsoft Corporation.
768
1056
  class SpecFilter {
769
- static specFilter(filter, unResolveSpec, resolvedSpec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2) {
1057
+ static specFilter(filter, unResolveSpec, resolvedSpec, options) {
1058
+ var _a;
770
1059
  try {
771
1060
  const newSpec = Object.assign({}, unResolveSpec);
772
1061
  const newPaths = {};
773
1062
  for (const filterItem of filter) {
774
1063
  const [method, path] = filterItem.split(" ");
775
1064
  const methodName = method.toLowerCase();
776
- if (!Utils.isSupportedApi(methodName, path, resolvedSpec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2)) {
777
- continue;
778
- }
779
- if (!newPaths[path]) {
780
- newPaths[path] = Object.assign({}, unResolveSpec.paths[path]);
781
- for (const m of ConstantString.AllOperationMethods) {
782
- delete newPaths[path][m];
1065
+ const pathObj = (_a = resolvedSpec.paths) === null || _a === void 0 ? void 0 : _a[path];
1066
+ if (ConstantString.AllOperationMethods.includes(methodName) &&
1067
+ pathObj &&
1068
+ pathObj[methodName]) {
1069
+ const validator = ValidatorFactory.create(resolvedSpec, options);
1070
+ const validateResult = validator.validateAPI(methodName, path);
1071
+ if (!validateResult.isValid) {
1072
+ continue;
1073
+ }
1074
+ if (!newPaths[path]) {
1075
+ newPaths[path] = Object.assign({}, unResolveSpec.paths[path]);
1076
+ for (const m of ConstantString.AllOperationMethods) {
1077
+ delete newPaths[path][m];
1078
+ }
1079
+ }
1080
+ newPaths[path][methodName] = unResolveSpec.paths[path][methodName];
1081
+ // Add the operationId if missing
1082
+ if (!newPaths[path][methodName].operationId) {
1083
+ newPaths[path][methodName].operationId = `${methodName}${Utils.convertPathToCamelCase(path)}`;
783
1084
  }
784
- }
785
- newPaths[path][methodName] = unResolveSpec.paths[path][methodName];
786
- // Add the operationId if missing
787
- if (!newPaths[path][methodName].operationId) {
788
- newPaths[path][methodName].operationId = `${methodName}${Utils.convertPathToCamelCase(path)}`;
789
1085
  }
790
1086
  }
791
1087
  newSpec.paths = newPaths;
@@ -797,101 +1093,11 @@ class SpecFilter {
797
1093
  }
798
1094
  }
799
1095
 
800
- // Copyright (c) Microsoft Corporation.
801
- class ManifestUpdater {
802
- static updateManifest(manifestPath, outputSpecPath, adaptiveCardFolder, spec, allowMultipleParameters, auth, isMe) {
803
- var _a, _b;
804
- return __awaiter(this, void 0, void 0, function* () {
805
- try {
806
- const originalManifest = yield fs__default['default'].readJSON(manifestPath);
807
- const updatedPart = {};
808
- const [commands, warnings] = yield ManifestUpdater.generateCommands(spec, adaptiveCardFolder, manifestPath, allowMultipleParameters);
809
- const composeExtension = {
810
- composeExtensionType: "apiBased",
811
- apiSpecificationFile: ManifestUpdater.getRelativePath(manifestPath, outputSpecPath),
812
- commands: commands,
813
- };
814
- if (auth) {
815
- if (Utils.isAPIKeyAuth(auth)) {
816
- auth = auth;
817
- const safeApiSecretRegistrationId = Utils.getSafeRegistrationIdEnvName(`${auth.name}_${ConstantString.RegistrationIdPostfix}`);
818
- composeExtension.authorization = {
819
- authType: "apiSecretServiceAuth",
820
- apiSecretServiceAuthConfiguration: {
821
- apiSecretRegistrationId: `\${{${safeApiSecretRegistrationId}}}`,
822
- },
823
- };
824
- }
825
- else if (Utils.isBearerTokenAuth(auth)) {
826
- composeExtension.authorization = {
827
- authType: "microsoftEntra",
828
- microsoftEntraConfiguration: {
829
- supportsSingleSignOn: true,
830
- },
831
- };
832
- updatedPart.webApplicationInfo = {
833
- id: "${{AAD_APP_CLIENT_ID}}",
834
- resource: "api://${{DOMAIN}}/${{AAD_APP_CLIENT_ID}}",
835
- };
836
- }
837
- }
838
- updatedPart.description = {
839
- short: spec.info.title.slice(0, ConstantString.ShortDescriptionMaxLens),
840
- full: (_b = ((_a = spec.info.description) !== null && _a !== void 0 ? _a : originalManifest.description.full)) === null || _b === void 0 ? void 0 : _b.slice(0, ConstantString.FullDescriptionMaxLens),
841
- };
842
- updatedPart.composeExtensions = isMe === undefined || isMe === true ? [composeExtension] : [];
843
- const updatedManifest = Object.assign(Object.assign({}, originalManifest), updatedPart);
844
- return [updatedManifest, warnings];
845
- }
846
- catch (err) {
847
- throw new SpecParserError(err.toString(), exports.ErrorType.UpdateManifestFailed);
848
- }
849
- });
850
- }
851
- static generateCommands(spec, adaptiveCardFolder, manifestPath, allowMultipleParameters) {
852
- return __awaiter(this, void 0, void 0, function* () {
853
- const paths = spec.paths;
854
- const commands = [];
855
- const warnings = [];
856
- if (paths) {
857
- for (const pathUrl in paths) {
858
- const pathItem = paths[pathUrl];
859
- if (pathItem) {
860
- const operations = pathItem;
861
- // Currently only support GET and POST method
862
- for (const method in operations) {
863
- if (method === ConstantString.PostMethod || method === ConstantString.GetMethod) {
864
- const operationItem = operations[method];
865
- if (operationItem) {
866
- const [command, warning] = Utils.parseApiInfo(operationItem, allowMultipleParameters);
867
- const adaptiveCardPath = path__default['default'].join(adaptiveCardFolder, command.id + ".json");
868
- command.apiResponseRenderingTemplateFile = (yield fs__default['default'].pathExists(adaptiveCardPath))
869
- ? ManifestUpdater.getRelativePath(manifestPath, adaptiveCardPath)
870
- : "";
871
- if (warning) {
872
- warnings.push(warning);
873
- }
874
- commands.push(command);
875
- }
876
- }
877
- }
878
- }
879
- }
880
- }
881
- return [commands, warnings];
882
- });
883
- }
884
- static getRelativePath(from, to) {
885
- const relativePath = path__default['default'].relative(path__default['default'].dirname(from), to);
886
- return path__default['default'].normalize(relativePath).replace(/\\/g, "/");
887
- }
888
- }
889
-
890
1096
  // Copyright (c) Microsoft Corporation.
891
1097
  class AdaptiveCardGenerator {
892
1098
  static generateAdaptiveCard(operationItem) {
893
1099
  try {
894
- const json = Utils.getResponseJson(operationItem);
1100
+ const { json } = Utils.getResponseJson(operationItem);
895
1101
  let cardBody = [];
896
1102
  let schema = json.schema;
897
1103
  let jsonPath = "$";
@@ -1057,6 +1263,27 @@ function wrapAdaptiveCard(card, jsonPath) {
1057
1263
  };
1058
1264
  return result;
1059
1265
  }
1266
+ function wrapResponseSemantics(card, jsonPath) {
1267
+ const props = inferProperties(card);
1268
+ const dataPath = jsonPath === "$" ? "$" : "$." + jsonPath;
1269
+ const result = {
1270
+ data_path: dataPath,
1271
+ };
1272
+ if (props.title || props.subtitle || props.imageUrl) {
1273
+ result.properties = {};
1274
+ if (props.title) {
1275
+ result.properties.title = "$." + props.title;
1276
+ }
1277
+ if (props.subtitle) {
1278
+ result.properties.subtitle = "$." + props.subtitle;
1279
+ }
1280
+ if (props.imageUrl) {
1281
+ result.properties.url = "$." + props.imageUrl;
1282
+ }
1283
+ }
1284
+ result.static_template = card;
1285
+ return result;
1286
+ }
1060
1287
  /**
1061
1288
  * Infers the preview card template from an Adaptive Card and a JSON path.
1062
1289
  * The preview card template includes a title and an optional subtitle and image.
@@ -1069,11 +1296,29 @@ function wrapAdaptiveCard(card, jsonPath) {
1069
1296
  * @returns The inferred preview card template.
1070
1297
  */
1071
1298
  function inferPreviewCardTemplate(card) {
1072
- var _a;
1073
1299
  const result = {
1074
- title: "",
1300
+ title: "result",
1075
1301
  };
1076
- const textBlockElements = new Set();
1302
+ const inferredProperties = inferProperties(card);
1303
+ if (inferredProperties.title) {
1304
+ result.title = `\${if(${inferredProperties.title}, ${inferredProperties.title}, 'N/A')}`;
1305
+ }
1306
+ if (inferredProperties.subtitle) {
1307
+ result.subtitle = `\${if(${inferredProperties.subtitle}, ${inferredProperties.subtitle}, 'N/A')}`;
1308
+ }
1309
+ if (inferredProperties.imageUrl) {
1310
+ result.image = {
1311
+ url: `\${${inferredProperties.imageUrl}}`,
1312
+ alt: `\${if(${inferredProperties.imageUrl}, ${inferredProperties.imageUrl}, 'N/A')}`,
1313
+ $when: `\${${inferredProperties.imageUrl} != null}`,
1314
+ };
1315
+ }
1316
+ return result;
1317
+ }
1318
+ function inferProperties(card) {
1319
+ var _a;
1320
+ const result = {};
1321
+ const nameSet = new Set();
1077
1322
  let rootObject;
1078
1323
  if (((_a = card.body[0]) === null || _a === void 0 ? void 0 : _a.type) === ConstantString.ContainerType) {
1079
1324
  rootObject = card.body[0].items;
@@ -1086,56 +1331,346 @@ function inferPreviewCardTemplate(card) {
1086
1331
  const textElement = element;
1087
1332
  const index = textElement.text.indexOf("${if(");
1088
1333
  if (index > 0) {
1089
- textElement.text = textElement.text.substring(index);
1090
- textBlockElements.add(textElement);
1334
+ const text = textElement.text.substring(index);
1335
+ const match = text.match(/\${if\(([^,]+),/);
1336
+ const property = match ? match[1] : "";
1337
+ if (property) {
1338
+ nameSet.add(property);
1339
+ }
1340
+ }
1341
+ }
1342
+ else if (element.type === ConstantString.ImageType) {
1343
+ const imageElement = element;
1344
+ const match = imageElement.url.match(/\${([^,]+)}/);
1345
+ const property = match ? match[1] : "";
1346
+ if (property) {
1347
+ nameSet.add(property);
1091
1348
  }
1092
1349
  }
1093
1350
  }
1094
- for (const element of textBlockElements) {
1095
- const text = element.text;
1096
- if (!result.title && Utils.isWellKnownName(text, ConstantString.WellknownTitleName)) {
1097
- result.title = text;
1098
- textBlockElements.delete(element);
1351
+ for (const name of nameSet) {
1352
+ if (!result.title && Utils.isWellKnownName(name, ConstantString.WellknownTitleName)) {
1353
+ result.title = name;
1354
+ nameSet.delete(name);
1099
1355
  }
1100
1356
  else if (!result.subtitle &&
1101
- Utils.isWellKnownName(text, ConstantString.WellknownSubtitleName)) {
1102
- result.subtitle = text;
1103
- textBlockElements.delete(element);
1357
+ Utils.isWellKnownName(name, ConstantString.WellknownSubtitleName)) {
1358
+ result.subtitle = name;
1359
+ nameSet.delete(name);
1104
1360
  }
1105
- else if (!result.image && Utils.isWellKnownName(text, ConstantString.WellknownImageName)) {
1106
- const match = text.match(/\${if\(([^,]+),/);
1107
- const property = match ? match[1] : "";
1108
- if (property) {
1109
- result.image = {
1110
- url: `\${${property}}`,
1111
- alt: text,
1112
- $when: `\${${property} != null}`,
1113
- };
1114
- }
1115
- textBlockElements.delete(element);
1361
+ else if (!result.imageUrl && Utils.isWellKnownName(name, ConstantString.WellknownImageName)) {
1362
+ result.imageUrl = name;
1363
+ nameSet.delete(name);
1116
1364
  }
1117
1365
  }
1118
- for (const element of textBlockElements) {
1119
- const text = element.text;
1366
+ for (const name of nameSet) {
1120
1367
  if (!result.title) {
1121
- result.title = text;
1122
- textBlockElements.delete(element);
1368
+ result.title = name;
1369
+ nameSet.delete(name);
1123
1370
  }
1124
1371
  else if (!result.subtitle) {
1125
- result.subtitle = text;
1126
- textBlockElements.delete(element);
1372
+ result.subtitle = name;
1373
+ nameSet.delete(name);
1127
1374
  }
1128
1375
  }
1129
1376
  if (!result.title && result.subtitle) {
1130
1377
  result.title = result.subtitle;
1131
1378
  delete result.subtitle;
1132
1379
  }
1133
- if (!result.title) {
1134
- result.title = "result";
1135
- }
1136
1380
  return result;
1137
1381
  }
1138
1382
 
1383
+ // Copyright (c) Microsoft Corporation.
1384
+ class ManifestUpdater {
1385
+ static updateManifestWithAiPlugin(manifestPath, outputSpecPath, apiPluginFilePath, spec, options) {
1386
+ return __awaiter(this, void 0, void 0, function* () {
1387
+ const manifest = yield fs__default['default'].readJSON(manifestPath);
1388
+ const apiPluginRelativePath = ManifestUpdater.getRelativePath(manifestPath, apiPluginFilePath);
1389
+ manifest.plugins = [
1390
+ {
1391
+ file: apiPluginRelativePath,
1392
+ id: ConstantString.DefaultPluginId,
1393
+ },
1394
+ ];
1395
+ const appName = this.removeEnvs(manifest.name.short);
1396
+ ManifestUpdater.updateManifestDescription(manifest, spec);
1397
+ const specRelativePath = ManifestUpdater.getRelativePath(manifestPath, outputSpecPath);
1398
+ const apiPlugin = yield ManifestUpdater.generatePluginManifestSchema(spec, specRelativePath, apiPluginFilePath, appName, options);
1399
+ return [manifest, apiPlugin];
1400
+ });
1401
+ }
1402
+ static updateManifestDescription(manifest, spec) {
1403
+ var _a, _b;
1404
+ manifest.description = {
1405
+ short: spec.info.title.slice(0, ConstantString.ShortDescriptionMaxLens),
1406
+ 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),
1407
+ };
1408
+ }
1409
+ static mapOpenAPISchemaToFuncParam(schema, method, pathUrl) {
1410
+ let parameter;
1411
+ if (schema.type === "string" ||
1412
+ schema.type === "boolean" ||
1413
+ schema.type === "integer" ||
1414
+ schema.type === "number" ||
1415
+ schema.type === "array") {
1416
+ parameter = schema;
1417
+ }
1418
+ else {
1419
+ throw new SpecParserError(Utils.format(ConstantString.UnsupportedSchema, method, pathUrl, JSON.stringify(schema)), exports.ErrorType.UpdateManifestFailed);
1420
+ }
1421
+ return parameter;
1422
+ }
1423
+ static generatePluginManifestSchema(spec, specRelativePath, apiPluginFilePath, appName, options) {
1424
+ var _a, _b, _c, _d;
1425
+ return __awaiter(this, void 0, void 0, function* () {
1426
+ const functions = [];
1427
+ const functionNames = [];
1428
+ const conversationStarters = [];
1429
+ const paths = spec.paths;
1430
+ for (const pathUrl in paths) {
1431
+ const pathItem = paths[pathUrl];
1432
+ if (pathItem) {
1433
+ const operations = pathItem;
1434
+ for (const method in operations) {
1435
+ if (options.allowMethods.includes(method)) {
1436
+ const operationItem = operations[method];
1437
+ if (operationItem) {
1438
+ const operationId = operationItem.operationId;
1439
+ const description = (_a = operationItem.description) !== null && _a !== void 0 ? _a : "";
1440
+ const paramObject = operationItem.parameters;
1441
+ const requestBody = operationItem.requestBody;
1442
+ const parameters = {
1443
+ type: "object",
1444
+ properties: {},
1445
+ required: [],
1446
+ };
1447
+ if (paramObject) {
1448
+ for (let i = 0; i < paramObject.length; i++) {
1449
+ const param = paramObject[i];
1450
+ const schema = param.schema;
1451
+ parameters.properties[param.name] = ManifestUpdater.mapOpenAPISchemaToFuncParam(schema, method, pathUrl);
1452
+ if (param.required) {
1453
+ parameters.required.push(param.name);
1454
+ }
1455
+ if (!parameters.properties[param.name].description) {
1456
+ parameters.properties[param.name].description = (_b = param.description) !== null && _b !== void 0 ? _b : "";
1457
+ }
1458
+ }
1459
+ }
1460
+ if (requestBody) {
1461
+ const requestJsonBody = requestBody.content["application/json"];
1462
+ const requestBodySchema = requestJsonBody.schema;
1463
+ if (requestBodySchema.type === "object") {
1464
+ if (requestBodySchema.required) {
1465
+ parameters.required.push(...requestBodySchema.required);
1466
+ }
1467
+ for (const property in requestBodySchema.properties) {
1468
+ const schema = requestBodySchema.properties[property];
1469
+ parameters.properties[property] = ManifestUpdater.mapOpenAPISchemaToFuncParam(schema, method, pathUrl);
1470
+ }
1471
+ }
1472
+ else {
1473
+ throw new SpecParserError(Utils.format(ConstantString.UnsupportedSchema, method, pathUrl, JSON.stringify(requestBodySchema)), exports.ErrorType.UpdateManifestFailed);
1474
+ }
1475
+ }
1476
+ const funcObj = {
1477
+ name: operationId,
1478
+ description: description,
1479
+ parameters: parameters,
1480
+ };
1481
+ if (options.allowResponseSemantics) {
1482
+ const [card, jsonPath] = AdaptiveCardGenerator.generateAdaptiveCard(operationItem);
1483
+ const responseSemantic = wrapResponseSemantics(card, jsonPath);
1484
+ funcObj.capabilities = {
1485
+ response_semantics: responseSemantic,
1486
+ };
1487
+ }
1488
+ functions.push(funcObj);
1489
+ functionNames.push(operationId);
1490
+ if (description) {
1491
+ conversationStarters.push(description);
1492
+ }
1493
+ }
1494
+ }
1495
+ }
1496
+ }
1497
+ }
1498
+ let apiPlugin;
1499
+ if (yield fs__default['default'].pathExists(apiPluginFilePath)) {
1500
+ apiPlugin = yield fs__default['default'].readJSON(apiPluginFilePath);
1501
+ }
1502
+ else {
1503
+ apiPlugin = {
1504
+ schema_version: "v2",
1505
+ name_for_human: "",
1506
+ description_for_human: "",
1507
+ functions: [],
1508
+ runtimes: [],
1509
+ };
1510
+ }
1511
+ apiPlugin.functions = apiPlugin.functions || [];
1512
+ for (const func of functions) {
1513
+ const index = (_c = apiPlugin.functions) === null || _c === void 0 ? void 0 : _c.findIndex((f) => f.name === func.name);
1514
+ if (index === -1) {
1515
+ apiPlugin.functions.push(func);
1516
+ }
1517
+ else {
1518
+ apiPlugin.functions[index] = func;
1519
+ }
1520
+ }
1521
+ apiPlugin.runtimes = apiPlugin.runtimes || [];
1522
+ const index = apiPlugin.runtimes.findIndex((runtime) => runtime.spec.url === specRelativePath);
1523
+ if (index === -1) {
1524
+ apiPlugin.runtimes.push({
1525
+ type: "OpenApi",
1526
+ auth: {
1527
+ type: "none",
1528
+ },
1529
+ spec: {
1530
+ url: specRelativePath,
1531
+ },
1532
+ run_for_functions: functionNames,
1533
+ });
1534
+ }
1535
+ else {
1536
+ apiPlugin.runtimes[index].run_for_functions = functionNames;
1537
+ }
1538
+ if (!apiPlugin.name_for_human) {
1539
+ apiPlugin.name_for_human = appName;
1540
+ }
1541
+ if (!apiPlugin.description_for_human) {
1542
+ apiPlugin.description_for_human =
1543
+ (_d = spec.info.description) !== null && _d !== void 0 ? _d : "<Please add description of the plugin>";
1544
+ }
1545
+ if (options.allowConversationStarters && conversationStarters.length > 0) {
1546
+ if (!apiPlugin.capabilities) {
1547
+ apiPlugin.capabilities = {
1548
+ localization: {},
1549
+ };
1550
+ }
1551
+ if (!apiPlugin.capabilities.conversation_starters) {
1552
+ apiPlugin.capabilities.conversation_starters = conversationStarters
1553
+ .slice(0, 5)
1554
+ .map((text) => ({ text }));
1555
+ }
1556
+ }
1557
+ return apiPlugin;
1558
+ });
1559
+ }
1560
+ static updateManifest(manifestPath, outputSpecPath, spec, options, adaptiveCardFolder, authInfo) {
1561
+ return __awaiter(this, void 0, void 0, function* () {
1562
+ try {
1563
+ const originalManifest = yield fs__default['default'].readJSON(manifestPath);
1564
+ const updatedPart = {};
1565
+ updatedPart.composeExtensions = [];
1566
+ let warnings = [];
1567
+ if (options.projectType === exports.ProjectType.SME) {
1568
+ const updateResult = yield ManifestUpdater.generateCommands(spec, manifestPath, options, adaptiveCardFolder);
1569
+ const commands = updateResult[0];
1570
+ warnings = updateResult[1];
1571
+ const composeExtension = {
1572
+ composeExtensionType: "apiBased",
1573
+ apiSpecificationFile: ManifestUpdater.getRelativePath(manifestPath, outputSpecPath),
1574
+ commands: commands,
1575
+ };
1576
+ if (authInfo) {
1577
+ const auth = authInfo.authScheme;
1578
+ if (Utils.isAPIKeyAuth(auth) || Utils.isBearerTokenAuth(auth)) {
1579
+ const safeApiSecretRegistrationId = Utils.getSafeRegistrationIdEnvName(`${authInfo.name}_${ConstantString.RegistrationIdPostfix}`);
1580
+ composeExtension.authorization = {
1581
+ authType: "apiSecretServiceAuth",
1582
+ apiSecretServiceAuthConfiguration: {
1583
+ apiSecretRegistrationId: `\${{${safeApiSecretRegistrationId}}}`,
1584
+ },
1585
+ };
1586
+ }
1587
+ else if (Utils.isOAuthWithAuthCodeFlow(auth)) {
1588
+ const safeOAuth2RegistrationId = Utils.getSafeRegistrationIdEnvName(`${authInfo.name}_${ConstantString.OAuthRegistrationIdPostFix}`);
1589
+ composeExtension.authorization = {
1590
+ authType: "oAuth2.0",
1591
+ oAuthConfiguration: {
1592
+ oauthConfigurationId: `\${{${safeOAuth2RegistrationId}}}`,
1593
+ },
1594
+ };
1595
+ updatedPart.webApplicationInfo = {
1596
+ id: "${{AAD_APP_CLIENT_ID}}",
1597
+ resource: "api://${{DOMAIN}}/${{AAD_APP_CLIENT_ID}}",
1598
+ };
1599
+ }
1600
+ }
1601
+ updatedPart.composeExtensions = [composeExtension];
1602
+ }
1603
+ updatedPart.description = originalManifest.description;
1604
+ ManifestUpdater.updateManifestDescription(updatedPart, spec);
1605
+ const updatedManifest = Object.assign(Object.assign({}, originalManifest), updatedPart);
1606
+ return [updatedManifest, warnings];
1607
+ }
1608
+ catch (err) {
1609
+ throw new SpecParserError(err.toString(), exports.ErrorType.UpdateManifestFailed);
1610
+ }
1611
+ });
1612
+ }
1613
+ static generateCommands(spec, manifestPath, options, adaptiveCardFolder) {
1614
+ var _a;
1615
+ return __awaiter(this, void 0, void 0, function* () {
1616
+ const paths = spec.paths;
1617
+ const commands = [];
1618
+ const warnings = [];
1619
+ if (paths) {
1620
+ for (const pathUrl in paths) {
1621
+ const pathItem = paths[pathUrl];
1622
+ if (pathItem) {
1623
+ const operations = pathItem;
1624
+ // Currently only support GET and POST method
1625
+ for (const method in operations) {
1626
+ if ((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) {
1627
+ const operationItem = operations[method];
1628
+ if (operationItem) {
1629
+ const command = Utils.parseApiInfo(operationItem, options);
1630
+ if (command.parameters &&
1631
+ command.parameters.length >= 1 &&
1632
+ command.parameters.some((param) => param.isRequired)) {
1633
+ command.parameters = command.parameters.filter((param) => param.isRequired);
1634
+ }
1635
+ else if (command.parameters && command.parameters.length > 0) {
1636
+ command.parameters = [command.parameters[0]];
1637
+ warnings.push({
1638
+ type: exports.WarningType.OperationOnlyContainsOptionalParam,
1639
+ content: Utils.format(ConstantString.OperationOnlyContainsOptionalParam, command.id),
1640
+ data: command.id,
1641
+ });
1642
+ }
1643
+ if (adaptiveCardFolder) {
1644
+ const adaptiveCardPath = path__default['default'].join(adaptiveCardFolder, command.id + ".json");
1645
+ command.apiResponseRenderingTemplateFile = (yield fs__default['default'].pathExists(adaptiveCardPath))
1646
+ ? ManifestUpdater.getRelativePath(manifestPath, adaptiveCardPath)
1647
+ : "";
1648
+ }
1649
+ commands.push(command);
1650
+ }
1651
+ }
1652
+ }
1653
+ }
1654
+ }
1655
+ }
1656
+ return [commands, warnings];
1657
+ });
1658
+ }
1659
+ static getRelativePath(from, to) {
1660
+ const relativePath = path__default['default'].relative(path__default['default'].dirname(from), to);
1661
+ return path__default['default'].normalize(relativePath).replace(/\\/g, "/");
1662
+ }
1663
+ static removeEnvs(str) {
1664
+ const placeHolderReg = /\${{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*}}/g;
1665
+ const matches = placeHolderReg.exec(str);
1666
+ let newStr = str;
1667
+ if (matches != null) {
1668
+ newStr = newStr.replace(matches[0], "");
1669
+ }
1670
+ return newStr;
1671
+ }
1672
+ }
1673
+
1139
1674
  // Copyright (c) Microsoft Corporation.
1140
1675
  /**
1141
1676
  * A class that parses an OpenAPI specification file and provides methods to validate, list, and generate artifacts.
@@ -1151,8 +1686,13 @@ class SpecParser {
1151
1686
  allowMissingId: true,
1152
1687
  allowSwagger: true,
1153
1688
  allowAPIKeyAuth: false,
1689
+ allowBearerTokenAuth: false,
1154
1690
  allowMultipleParameters: false,
1155
1691
  allowOauth2: false,
1692
+ allowMethods: ["get", "post"],
1693
+ allowConversationStarters: false,
1694
+ allowResponseSemantics: false,
1695
+ projectType: exports.ProjectType.SME,
1156
1696
  };
1157
1697
  this.pathOrSpec = pathOrDoc;
1158
1698
  this.parser = new SwaggerParser__default['default']();
@@ -1177,6 +1717,8 @@ class SpecParser {
1177
1717
  errors: [{ type: exports.ErrorType.SpecNotValid, content: e.toString() }],
1178
1718
  };
1179
1719
  }
1720
+ const errors = [];
1721
+ const warnings = [];
1180
1722
  if (!this.options.allowSwagger && this.isSwaggerFile) {
1181
1723
  return {
1182
1724
  status: exports.ValidationStatus.Error,
@@ -1186,7 +1728,38 @@ class SpecParser {
1186
1728
  ],
1187
1729
  };
1188
1730
  }
1189
- return Utils.validateSpec(this.spec, this.parser, !!this.isSwaggerFile, this.options.allowMissingId, this.options.allowAPIKeyAuth, this.options.allowMultipleParameters, this.options.allowOauth2);
1731
+ // Remote reference not supported
1732
+ const refPaths = this.parser.$refs.paths();
1733
+ // refPaths [0] is the current spec file path
1734
+ if (refPaths.length > 1) {
1735
+ errors.push({
1736
+ type: exports.ErrorType.RemoteRefNotSupported,
1737
+ content: Utils.format(ConstantString.RemoteRefNotSupported, refPaths.join(", ")),
1738
+ data: refPaths,
1739
+ });
1740
+ }
1741
+ if (!!this.isSwaggerFile && this.options.allowSwagger) {
1742
+ warnings.push({
1743
+ type: exports.WarningType.ConvertSwaggerToOpenAPI,
1744
+ content: ConstantString.ConvertSwaggerToOpenAPI,
1745
+ });
1746
+ }
1747
+ const validator = this.getValidator(this.spec);
1748
+ const validationResult = validator.validateSpec();
1749
+ warnings.push(...validationResult.warnings);
1750
+ errors.push(...validationResult.errors);
1751
+ let status = exports.ValidationStatus.Valid;
1752
+ if (warnings.length > 0 && errors.length === 0) {
1753
+ status = exports.ValidationStatus.Warning;
1754
+ }
1755
+ else if (errors.length > 0) {
1756
+ status = exports.ValidationStatus.Error;
1757
+ }
1758
+ return {
1759
+ status: status,
1760
+ warnings: warnings,
1761
+ errors: errors,
1762
+ };
1190
1763
  }
1191
1764
  catch (err) {
1192
1765
  throw new SpecParserError(err.toString(), exports.ErrorType.ValidateFailed);
@@ -1210,39 +1783,40 @@ class SpecParser {
1210
1783
  try {
1211
1784
  yield this.loadSpec();
1212
1785
  const spec = this.spec;
1213
- const apiMap = this.getAllSupportedAPIs(spec);
1214
- const result = [];
1786
+ const apiMap = this.getAPIs(spec);
1787
+ const result = {
1788
+ APIs: [],
1789
+ allAPICount: 0,
1790
+ validAPICount: 0,
1791
+ };
1215
1792
  for (const apiKey in apiMap) {
1793
+ const { operation, isValid, reason } = apiMap[apiKey];
1794
+ const [method, path] = apiKey.split(" ");
1795
+ const operationId = (_a = operation.operationId) !== null && _a !== void 0 ? _a : `${method.toLowerCase()}${Utils.convertPathToCamelCase(path)}`;
1216
1796
  const apiResult = {
1217
- api: "",
1797
+ api: apiKey,
1218
1798
  server: "",
1219
- operationId: "",
1799
+ operationId: operationId,
1800
+ isValid: isValid,
1801
+ reason: reason,
1220
1802
  };
1221
- const [method, path] = apiKey.split(" ");
1222
- const operation = apiMap[apiKey];
1223
- const rootServer = spec.servers && spec.servers[0];
1224
- const methodServer = spec.paths[path].servers && ((_a = spec.paths[path]) === null || _a === void 0 ? void 0 : _a.servers[0]);
1225
- const operationServer = operation.servers && operation.servers[0];
1226
- const serverUrl = operationServer || methodServer || rootServer;
1227
- if (!serverUrl) {
1228
- throw new SpecParserError(ConstantString.NoServerInformation, exports.ErrorType.NoServerInformation);
1229
- }
1230
- apiResult.server = Utils.resolveServerUrl(serverUrl.url);
1231
- let operationId = operation.operationId;
1232
- if (!operationId) {
1233
- operationId = `${method.toLowerCase()}${Utils.convertPathToCamelCase(path)}`;
1234
- }
1235
- apiResult.operationId = operationId;
1236
- const authArray = Utils.getAuthArray(operation.security, spec);
1237
- for (const auths of authArray) {
1238
- if (auths.length === 1) {
1239
- apiResult.auth = auths[0].authSchema;
1240
- break;
1803
+ if (isValid) {
1804
+ const serverObj = Utils.getServerObject(spec, method.toLocaleLowerCase(), path);
1805
+ if (serverObj) {
1806
+ apiResult.server = Utils.resolveEnv(serverObj.url);
1807
+ }
1808
+ const authArray = Utils.getAuthArray(operation.security, spec);
1809
+ for (const auths of authArray) {
1810
+ if (auths.length === 1) {
1811
+ apiResult.auth = auths[0];
1812
+ break;
1813
+ }
1241
1814
  }
1242
1815
  }
1243
- apiResult.api = apiKey;
1244
- result.push(apiResult);
1816
+ result.APIs.push(apiResult);
1245
1817
  }
1818
+ result.allAPICount = result.APIs.length;
1819
+ result.validAPICount = result.APIs.filter((api) => api.isValid).length;
1246
1820
  return result;
1247
1821
  }
1248
1822
  catch (err) {
@@ -1267,7 +1841,7 @@ class SpecParser {
1267
1841
  if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
1268
1842
  throw new SpecParserError(ConstantString.CancelledMessage, exports.ErrorType.Cancelled);
1269
1843
  }
1270
- const newUnResolvedSpec = SpecFilter.specFilter(filter, this.unResolveSpec, this.spec, this.options.allowMissingId, this.options.allowAPIKeyAuth, this.options.allowMultipleParameters, this.options.allowOauth2);
1844
+ const newUnResolvedSpec = SpecFilter.specFilter(filter, this.unResolveSpec, this.spec, this.options);
1271
1845
  if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
1272
1846
  throw new SpecParserError(ConstantString.CancelledMessage, exports.ErrorType.Cancelled);
1273
1847
  }
@@ -1282,15 +1856,55 @@ class SpecParser {
1282
1856
  }
1283
1857
  });
1284
1858
  }
1859
+ /**
1860
+ * Generates and update artifacts from the OpenAPI specification file. Generate Adaptive Cards, update Teams app manifest, and generate a new OpenAPI specification file.
1861
+ * @param manifestPath A file path of the Teams app manifest file to update.
1862
+ * @param filter An array of strings that represent the filters to apply when generating the artifacts. If filter is empty, it would process nothing.
1863
+ * @param outputSpecPath File path of the new OpenAPI specification file to generate. If not specified or empty, no spec file will be generated.
1864
+ * @param pluginFilePath File path of the api plugin file to generate.
1865
+ */
1866
+ generateForCopilot(manifestPath, filter, outputSpecPath, pluginFilePath, signal) {
1867
+ return __awaiter(this, void 0, void 0, function* () {
1868
+ const result = {
1869
+ allSuccess: true,
1870
+ warnings: [],
1871
+ };
1872
+ try {
1873
+ const newSpecs = yield this.getFilteredSpecs(filter, signal);
1874
+ const newUnResolvedSpec = newSpecs[0];
1875
+ const newSpec = newSpecs[1];
1876
+ let resultStr;
1877
+ if (outputSpecPath.endsWith(".yaml") || outputSpecPath.endsWith(".yml")) {
1878
+ resultStr = jsyaml__default['default'].dump(newUnResolvedSpec);
1879
+ }
1880
+ else {
1881
+ resultStr = JSON.stringify(newUnResolvedSpec, null, 2);
1882
+ }
1883
+ yield fs__default['default'].outputFile(outputSpecPath, resultStr);
1884
+ if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
1885
+ throw new SpecParserError(ConstantString.CancelledMessage, exports.ErrorType.Cancelled);
1886
+ }
1887
+ const [updatedManifest, apiPlugin] = yield ManifestUpdater.updateManifestWithAiPlugin(manifestPath, outputSpecPath, pluginFilePath, newSpec, this.options);
1888
+ yield fs__default['default'].outputJSON(manifestPath, updatedManifest, { spaces: 2 });
1889
+ yield fs__default['default'].outputJSON(pluginFilePath, apiPlugin, { spaces: 2 });
1890
+ }
1891
+ catch (err) {
1892
+ if (err instanceof SpecParserError) {
1893
+ throw err;
1894
+ }
1895
+ throw new SpecParserError(err.toString(), exports.ErrorType.GenerateFailed);
1896
+ }
1897
+ return result;
1898
+ });
1899
+ }
1285
1900
  /**
1286
1901
  * Generates and update artifacts from the OpenAPI specification file. Generate Adaptive Cards, update Teams app manifest, and generate a new OpenAPI specification file.
1287
1902
  * @param manifestPath A file path of the Teams app manifest file to update.
1288
1903
  * @param filter An array of strings that represent the filters to apply when generating the artifacts. If filter is empty, it would process nothing.
1289
1904
  * @param outputSpecPath File path of the new OpenAPI specification file to generate. If not specified or empty, no spec file will be generated.
1290
1905
  * @param adaptiveCardFolder Folder path where the Adaptive Card files will be generated. If not specified or empty, Adaptive Card files will not be generated.
1291
- * @param isMe Boolean that indicates whether the project is an Messaging Extension. For Messaging Extension, composeExtensions will be added in Teams app manifest.
1292
1906
  */
1293
- generate(manifestPath, filter, outputSpecPath, adaptiveCardFolder, signal, isMe) {
1907
+ generate(manifestPath, filter, outputSpecPath, adaptiveCardFolder, signal) {
1294
1908
  return __awaiter(this, void 0, void 0, function* () {
1295
1909
  const result = {
1296
1910
  allSuccess: true,
@@ -1300,23 +1914,26 @@ class SpecParser {
1300
1914
  const newSpecs = yield this.getFilteredSpecs(filter, signal);
1301
1915
  const newUnResolvedSpec = newSpecs[0];
1302
1916
  const newSpec = newSpecs[1];
1303
- const AuthSet = new Set();
1304
- let hasMultipleAPIKeyAuth = false;
1917
+ let hasMultipleAuth = false;
1918
+ let authInfo = undefined;
1305
1919
  for (const url in newSpec.paths) {
1306
1920
  for (const method in newSpec.paths[url]) {
1307
1921
  const operation = newSpec.paths[url][method];
1308
1922
  const authArray = Utils.getAuthArray(operation.security, newSpec);
1309
1923
  if (authArray && authArray.length > 0) {
1310
- AuthSet.add(authArray[0][0].authSchema);
1311
- if (AuthSet.size > 1) {
1312
- hasMultipleAPIKeyAuth = true;
1924
+ const currentAuth = authArray[0][0];
1925
+ if (!authInfo) {
1926
+ authInfo = authArray[0][0];
1927
+ }
1928
+ else if (authInfo.name !== currentAuth.name) {
1929
+ hasMultipleAuth = true;
1313
1930
  break;
1314
1931
  }
1315
1932
  }
1316
1933
  }
1317
1934
  }
1318
- if (hasMultipleAPIKeyAuth) {
1319
- throw new SpecParserError(ConstantString.MultipleAPIKeyNotSupported, exports.ErrorType.MultipleAPIKeyNotSupported);
1935
+ if (hasMultipleAuth && this.options.projectType !== exports.ProjectType.TeamsAi) {
1936
+ throw new SpecParserError(ConstantString.MultipleAuthNotSupported, exports.ErrorType.MultipleAuthNotSupported);
1320
1937
  }
1321
1938
  let resultStr;
1322
1939
  if (outputSpecPath.endsWith(".yaml") || outputSpecPath.endsWith(".yml")) {
@@ -1326,26 +1943,28 @@ class SpecParser {
1326
1943
  resultStr = JSON.stringify(newUnResolvedSpec, null, 2);
1327
1944
  }
1328
1945
  yield fs__default['default'].outputFile(outputSpecPath, resultStr);
1329
- for (const url in newSpec.paths) {
1330
- for (const method in newSpec.paths[url]) {
1331
- // paths object may contain description/summary, so we need to check if it is a operation object
1332
- if (method === ConstantString.PostMethod || method === ConstantString.GetMethod) {
1333
- const operation = newSpec.paths[url][method];
1334
- try {
1335
- const [card, jsonPath] = AdaptiveCardGenerator.generateAdaptiveCard(operation);
1336
- const fileName = path__default['default'].join(adaptiveCardFolder, `${operation.operationId}.json`);
1337
- const wrappedCard = wrapAdaptiveCard(card, jsonPath);
1338
- yield fs__default['default'].outputJSON(fileName, wrappedCard, { spaces: 2 });
1339
- const dataFileName = path__default['default'].join(adaptiveCardFolder, `${operation.operationId}.data.json`);
1340
- yield fs__default['default'].outputJSON(dataFileName, {}, { spaces: 2 });
1341
- }
1342
- catch (err) {
1343
- result.allSuccess = false;
1344
- result.warnings.push({
1345
- type: exports.WarningType.GenerateCardFailed,
1346
- content: err.toString(),
1347
- data: operation.operationId,
1348
- });
1946
+ if (adaptiveCardFolder) {
1947
+ for (const url in newSpec.paths) {
1948
+ for (const method in newSpec.paths[url]) {
1949
+ // paths object may contain description/summary which is not a http method, so we need to check if it is a operation object
1950
+ if (this.options.allowMethods.includes(method)) {
1951
+ const operation = newSpec.paths[url][method];
1952
+ try {
1953
+ const [card, jsonPath] = AdaptiveCardGenerator.generateAdaptiveCard(operation);
1954
+ const fileName = path__default['default'].join(adaptiveCardFolder, `${operation.operationId}.json`);
1955
+ const wrappedCard = wrapAdaptiveCard(card, jsonPath);
1956
+ yield fs__default['default'].outputJSON(fileName, wrappedCard, { spaces: 2 });
1957
+ const dataFileName = path__default['default'].join(adaptiveCardFolder, `${operation.operationId}.data.json`);
1958
+ yield fs__default['default'].outputJSON(dataFileName, {}, { spaces: 2 });
1959
+ }
1960
+ catch (err) {
1961
+ result.allSuccess = false;
1962
+ result.warnings.push({
1963
+ type: exports.WarningType.GenerateCardFailed,
1964
+ content: err.toString(),
1965
+ data: operation.operationId,
1966
+ });
1967
+ }
1349
1968
  }
1350
1969
  }
1351
1970
  }
@@ -1353,8 +1972,7 @@ class SpecParser {
1353
1972
  if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
1354
1973
  throw new SpecParserError(ConstantString.CancelledMessage, exports.ErrorType.Cancelled);
1355
1974
  }
1356
- const auth = Array.from(AuthSet)[0];
1357
- const [updatedManifest, warnings] = yield ManifestUpdater.updateManifest(manifestPath, outputSpecPath, adaptiveCardFolder, newSpec, this.options.allowMultipleParameters, auth, isMe);
1975
+ const [updatedManifest, warnings] = yield ManifestUpdater.updateManifest(manifestPath, outputSpecPath, newSpec, this.options, adaptiveCardFolder, authInfo);
1358
1976
  yield fs__default['default'].outputJSON(manifestPath, updatedManifest, { spaces: 2 });
1359
1977
  result.warnings.push(...warnings);
1360
1978
  }
@@ -1382,13 +2000,18 @@ class SpecParser {
1382
2000
  }
1383
2001
  });
1384
2002
  }
1385
- getAllSupportedAPIs(spec) {
1386
- if (this.apiMap !== undefined) {
1387
- return this.apiMap;
2003
+ getAPIs(spec) {
2004
+ const validator = this.getValidator(spec);
2005
+ const apiMap = validator.listAPIs();
2006
+ return apiMap;
2007
+ }
2008
+ getValidator(spec) {
2009
+ if (this.validator) {
2010
+ return this.validator;
1388
2011
  }
1389
- const result = Utils.listSupportedAPIs(spec, this.options.allowMissingId, this.options.allowAPIKeyAuth, this.options.allowMultipleParameters, this.options.allowOauth2);
1390
- this.apiMap = result;
1391
- return result;
2012
+ const validator = ValidatorFactory.create(spec, this.options);
2013
+ this.validator = validator;
2014
+ return validator;
1392
2015
  }
1393
2016
  }
1394
2017