@microsoft/m365-spec-parser 0.1.1-alpha.cf377d39f.0 → 0.1.1-alpha.ded43fb2d.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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
  }
@@ -413,24 +270,47 @@ class Utils {
413
270
  result.sort((a, b) => a[0].name.localeCompare(b[0].name));
414
271
  return result;
415
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
+ }
416
292
  static updateFirstLetter(str) {
417
293
  return str.charAt(0).toUpperCase() + str.slice(1);
418
294
  }
419
295
  static getResponseJson(operationObject) {
420
296
  var _a, _b;
421
297
  let json = {};
298
+ let multipleMediaType = false;
422
299
  for (const code of ConstantString.ResponseCodeFor20X) {
423
300
  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
301
  if ((_b = responseObject === null || responseObject === void 0 ? void 0 : responseObject.content) === null || _b === void 0 ? void 0 : _b["application/json"]) {
302
+ multipleMediaType = false;
429
303
  json = responseObject.content["application/json"];
430
- break;
304
+ if (Utils.containMultipleMediaTypes(responseObject)) {
305
+ multipleMediaType = true;
306
+ json = {};
307
+ }
308
+ else {
309
+ break;
310
+ }
431
311
  }
432
312
  }
433
- return json;
313
+ return { json, multipleMediaType };
434
314
  }
435
315
  static convertPathToCamelCase(path) {
436
316
  const pathSegments = path.split(/[./{]/);
@@ -450,10 +330,10 @@ class Utils {
450
330
  return undefined;
451
331
  }
452
332
  }
453
- static resolveServerUrl(url) {
333
+ static resolveEnv(str) {
454
334
  const placeHolderReg = /\${{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*}}/g;
455
- let matches = placeHolderReg.exec(url);
456
- let newUrl = url;
335
+ let matches = placeHolderReg.exec(str);
336
+ let newStr = str;
457
337
  while (matches != null) {
458
338
  const envVar = matches[1];
459
339
  const envVal = process.env[envVar];
@@ -461,17 +341,17 @@ class Utils {
461
341
  throw new Error(Utils.format(ConstantString.ResolveServerUrlFailed, envVar));
462
342
  }
463
343
  else {
464
- newUrl = newUrl.replace(matches[0], envVal);
344
+ newStr = newStr.replace(matches[0], envVal);
465
345
  }
466
- matches = placeHolderReg.exec(url);
346
+ matches = placeHolderReg.exec(str);
467
347
  }
468
- return newUrl;
348
+ return newStr;
469
349
  }
470
350
  static checkServerUrl(servers) {
471
351
  const errors = [];
472
352
  let serverUrl;
473
353
  try {
474
- serverUrl = Utils.resolveServerUrl(servers[0].url);
354
+ serverUrl = Utils.resolveEnv(servers[0].url);
475
355
  }
476
356
  catch (err) {
477
357
  errors.push({
@@ -501,7 +381,8 @@ class Utils {
501
381
  }
502
382
  return errors;
503
383
  }
504
- static validateServer(spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2) {
384
+ static validateServer(spec, options) {
385
+ var _a;
505
386
  const errors = [];
506
387
  let hasTopLevelServers = false;
507
388
  let hasPathLevelServers = false;
@@ -522,7 +403,7 @@ class Utils {
522
403
  }
523
404
  for (const method in methods) {
524
405
  const operationObject = methods[method];
525
- if (Utils.isSupportedApi(method, path, spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2)) {
406
+ if (((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && operationObject) {
526
407
  if ((operationObject === null || operationObject === void 0 ? void 0 : operationObject.servers) && operationObject.servers.length >= 1) {
527
408
  hasOperationLevelServers = true;
528
409
  const serverErrors = Utils.checkServerUrl(operationObject.servers);
@@ -565,6 +446,7 @@ class Utils {
565
446
  Utils.updateParameterWithInputType(schema, parameter);
566
447
  }
567
448
  if (isRequired && schema.default === undefined) {
449
+ parameter.isRequired = true;
568
450
  requiredParams.push(parameter);
569
451
  }
570
452
  else {
@@ -609,7 +491,7 @@ class Utils {
609
491
  param.value = schema.default;
610
492
  }
611
493
  }
612
- static parseApiInfo(operationItem, allowMultipleParameters) {
494
+ static parseApiInfo(operationItem, options) {
613
495
  var _a, _b;
614
496
  const requiredParams = [];
615
497
  const optionalParams = [];
@@ -623,11 +505,12 @@ class Utils {
623
505
  description: ((_a = param.description) !== null && _a !== void 0 ? _a : "").slice(0, ConstantString.ParameterDescriptionMaxLens),
624
506
  };
625
507
  const schema = param.schema;
626
- if (allowMultipleParameters && schema) {
508
+ if (options.allowMultipleParameters && schema) {
627
509
  Utils.updateParameterWithInputType(schema, parameter);
628
510
  }
629
511
  if (param.in !== "header" && param.in !== "cookie") {
630
512
  if (param.required && (schema === null || schema === void 0 ? void 0 : schema.default) === undefined) {
513
+ parameter.isRequired = true;
631
514
  requiredParams.push(parameter);
632
515
  }
633
516
  else {
@@ -641,19 +524,13 @@ class Utils {
641
524
  const requestJson = requestBody.content["application/json"];
642
525
  if (Object.keys(requestJson).length !== 0) {
643
526
  const schema = requestJson.schema;
644
- const [requiredP, optionalP] = Utils.generateParametersFromSchema(schema, "requestBody", allowMultipleParameters, requestBody.required);
527
+ const [requiredP, optionalP] = Utils.generateParametersFromSchema(schema, "requestBody", !!options.allowMultipleParameters, requestBody.required);
645
528
  requiredParams.push(...requiredP);
646
529
  optionalParams.push(...optionalP);
647
530
  }
648
531
  }
649
532
  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
- }
533
+ const parameters = [...requiredParams, ...optionalParams];
657
534
  const command = {
658
535
  context: ["compose"],
659
536
  type: "query",
@@ -662,228 +539,575 @@ class Utils {
662
539
  parameters: parameters,
663
540
  description: ((_b = operationItem.description) !== null && _b !== void 0 ? _b : "").slice(0, ConstantString.CommandDescriptionMaxLens),
664
541
  };
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
- };
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 "";
672
554
  }
673
- return [command, warning];
555
+ let safeRegistrationIdEnvName = authName.toUpperCase().replace(/[^A-Z0-9_]/g, "_");
556
+ if (!safeRegistrationIdEnvName.match(/^[A-Z]/)) {
557
+ safeRegistrationIdEnvName = "PREFIX_" + safeRegistrationIdEnvName;
558
+ }
559
+ return safeRegistrationIdEnvName;
674
560
  }
675
- static listSupportedAPIs(spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2) {
676
- 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;
677
580
  const result = {};
678
581
  for (const path in paths) {
679
582
  const methods = paths[path];
680
583
  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;
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
+ };
685
592
  }
686
593
  }
687
594
  }
595
+ this.apiMap = result;
688
596
  return result;
689
597
  }
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,
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,
697
605
  });
698
606
  }
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({
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({
716
627
  type: exports.ErrorType.NoSupportedApi,
717
628
  content: ConstantString.NoSupportedApi,
629
+ data,
718
630
  });
719
631
  }
632
+ return result;
633
+ }
634
+ validateSpecOperationId() {
635
+ const result = { errors: [], warnings: [] };
636
+ const apiMap = this.listAPIs();
720
637
  // OperationId missing
721
638
  const apisMissingOperationId = [];
722
639
  for (const key in apiMap) {
723
- const pathObjectItem = apiMap[key];
724
- if (!pathObjectItem.operationId) {
640
+ const { operation } = apiMap[key];
641
+ if (!operation.operationId) {
725
642
  apisMissingOperationId.push(key);
726
643
  }
727
644
  }
728
645
  if (apisMissingOperationId.length > 0) {
729
- warnings.push({
646
+ result.warnings.push({
730
647
  type: exports.WarningType.OperationIdMissing,
731
648
  content: Utils.format(ConstantString.MissingOperationId, apisMissingOperationId.join(", ")),
732
649
  data: apisMissingOperationId,
733
650
  });
734
651
  }
735
- let status = exports.ValidationStatus.Valid;
736
- if (warnings.length > 0 && errors.length === 0) {
737
- 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;
738
660
  }
739
- else if (errors.length > 0) {
740
- 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;
741
666
  }
742
- return {
743
- status,
744
- warnings,
745
- errors,
746
- };
667
+ return result;
747
668
  }
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
- });
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;
754
684
  }
755
- static getSafeRegistrationIdEnvName(authName) {
756
- if (!authName) {
757
- 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);
758
691
  }
759
- let safeRegistrationIdEnvName = authName.toUpperCase().replace(/[^A-Z0-9_]/g, "_");
760
- if (!safeRegistrationIdEnvName.match(/^[A-Z]/)) {
761
- 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));
762
696
  }
763
- return safeRegistrationIdEnvName;
697
+ return result;
764
698
  }
765
- }
766
-
767
- // Copyright (c) Microsoft Corporation.
768
- class SpecFilter {
769
- static specFilter(filter, unResolveSpec, resolvedSpec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2) {
770
- try {
771
- const newSpec = Object.assign({}, unResolveSpec);
772
- const newPaths = {};
773
- for (const filterItem of filter) {
774
- const [method, path] = filterItem.split(" ");
775
- 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];
699
+ validateAuth(method, path) {
700
+ const pathObj = this.spec.paths[path];
701
+ const operationObject = pathObj[method];
702
+ const securities = operationObject.security;
703
+ const authSchemeArray = Utils.getAuthArray(securities, this.spec);
704
+ if (authSchemeArray.length === 0) {
705
+ return { isValid: true, reason: [] };
706
+ }
707
+ if (this.options.allowAPIKeyAuth ||
708
+ this.options.allowOauth2 ||
709
+ this.options.allowBearerTokenAuth) {
710
+ // Currently we don't support multiple auth in one operation
711
+ if (authSchemeArray.length > 0 && authSchemeArray.every((auths) => auths.length > 1)) {
712
+ return {
713
+ isValid: false,
714
+ reason: [exports.ErrorType.MultipleAuthNotSupported],
715
+ };
716
+ }
717
+ for (const auths of authSchemeArray) {
718
+ if (auths.length === 1) {
719
+ if ((this.options.allowAPIKeyAuth && Utils.isAPIKeyAuth(auths[0].authScheme)) ||
720
+ (this.options.allowOauth2 && Utils.isOAuthWithAuthCodeFlow(auths[0].authScheme)) ||
721
+ (this.options.allowBearerTokenAuth && Utils.isBearerTokenAuth(auths[0].authScheme))) {
722
+ return { isValid: true, reason: [] };
783
723
  }
784
724
  }
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)}`;
725
+ }
726
+ }
727
+ return { isValid: false, reason: [exports.ErrorType.AuthTypeIsNotSupported] };
728
+ }
729
+ checkPostBodySchema(schema, isRequired = false) {
730
+ var _a;
731
+ const paramResult = {
732
+ requiredNum: 0,
733
+ optionalNum: 0,
734
+ isValid: true,
735
+ reason: [],
736
+ };
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
+ }
747
+ if (schema.type === "string" ||
748
+ schema.type === "integer" ||
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;
789
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);
790
770
  }
791
- newSpec.paths = newPaths;
792
- return newSpec;
793
771
  }
794
- catch (err) {
795
- throw new SpecParserError(err.toString(), exports.ErrorType.FilterSpecFailed);
772
+ else {
773
+ if (isRequiredWithoutDefault && !isCopilot) {
774
+ paramResult.isValid = false;
775
+ paramResult.reason.push(exports.ErrorType.PostBodyContainsRequiredUnsupportedSchema);
776
+ }
796
777
  }
778
+ return paramResult;
779
+ }
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;
832
+ }
833
+ }
834
+ }
835
+ return paramResult;
836
+ }
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;
843
+ }
844
+ }
845
+ }
846
+ return false;
797
847
  }
798
848
  }
799
849
 
800
850
  // 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];
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);
845
903
  }
846
- catch (err) {
847
- throw new SpecParserError(err.toString(), exports.ErrorType.UpdateManifestFailed);
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;
915
+ }
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);
848
1005
  }
849
- });
1006
+ }
1007
+ else if (totalParams === 0) {
1008
+ result.reason.push(exports.ErrorType.NoParameter);
1009
+ }
1010
+ return result;
1011
+ }
1012
+ }
1013
+ SMEValidator.SMERequiredParamsMaxNum = 5;
1014
+
1015
+ // Copyright (c) Microsoft Corporation.
1016
+ class TeamsAIValidator extends Validator {
1017
+ constructor(spec, options) {
1018
+ super();
1019
+ this.projectType = exports.ProjectType.TeamsAi;
1020
+ this.options = options;
1021
+ this.spec = spec;
1022
+ }
1023
+ validateSpec() {
1024
+ const result = { errors: [], warnings: [] };
1025
+ // validate spec server
1026
+ let validationResult = this.validateSpecServer();
1027
+ result.errors.push(...validationResult.errors);
1028
+ // validate no supported API
1029
+ validationResult = this.validateSpecNoSupportAPI();
1030
+ result.errors.push(...validationResult.errors);
1031
+ return result;
1032
+ }
1033
+ validateAPI(method, path) {
1034
+ const result = { isValid: true, reason: [] };
1035
+ method = method.toLocaleLowerCase();
1036
+ // validate method and path
1037
+ const methodAndPathResult = this.validateMethodAndPath(method, path);
1038
+ if (!methodAndPathResult.isValid) {
1039
+ return methodAndPathResult;
1040
+ }
1041
+ const operationObject = this.spec.paths[path][method];
1042
+ // validate operationId
1043
+ if (!this.options.allowMissingId && !operationObject.operationId) {
1044
+ result.reason.push(exports.ErrorType.MissingOperationId);
1045
+ }
1046
+ // validate server
1047
+ const validateServerResult = this.validateServer(method, path);
1048
+ result.reason.push(...validateServerResult.reason);
1049
+ if (result.reason.length > 0) {
1050
+ result.isValid = false;
1051
+ }
1052
+ return result;
1053
+ }
1054
+ }
1055
+
1056
+ class ValidatorFactory {
1057
+ static create(spec, options) {
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
+ }
850
1070
  }
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
- }
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];
877
1096
  }
878
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
+ }
879
1103
  }
880
1104
  }
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, "/");
1105
+ newSpec.paths = newPaths;
1106
+ return newSpec;
1107
+ }
1108
+ catch (err) {
1109
+ throw new SpecParserError(err.toString(), exports.ErrorType.FilterSpecFailed);
1110
+ }
887
1111
  }
888
1112
  }
889
1113
 
@@ -891,7 +1115,7 @@ class ManifestUpdater {
891
1115
  class AdaptiveCardGenerator {
892
1116
  static generateAdaptiveCard(operationItem) {
893
1117
  try {
894
- const json = Utils.getResponseJson(operationItem);
1118
+ const { json } = Utils.getResponseJson(operationItem);
895
1119
  let cardBody = [];
896
1120
  let schema = json.schema;
897
1121
  let jsonPath = "$";
@@ -1057,6 +1281,27 @@ function wrapAdaptiveCard(card, jsonPath) {
1057
1281
  };
1058
1282
  return result;
1059
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
+ }
1060
1305
  /**
1061
1306
  * Infers the preview card template from an Adaptive Card and a JSON path.
1062
1307
  * The preview card template includes a title and an optional subtitle and image.
@@ -1069,11 +1314,29 @@ function wrapAdaptiveCard(card, jsonPath) {
1069
1314
  * @returns The inferred preview card template.
1070
1315
  */
1071
1316
  function inferPreviewCardTemplate(card) {
1072
- var _a;
1073
1317
  const result = {
1074
- title: "",
1318
+ title: "result",
1075
1319
  };
1076
- 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();
1077
1340
  let rootObject;
1078
1341
  if (((_a = card.body[0]) === null || _a === void 0 ? void 0 : _a.type) === ConstantString.ContainerType) {
1079
1342
  rootObject = card.body[0].items;
@@ -1086,56 +1349,407 @@ function inferPreviewCardTemplate(card) {
1086
1349
  const textElement = element;
1087
1350
  const index = textElement.text.indexOf("${if(");
1088
1351
  if (index > 0) {
1089
- textElement.text = textElement.text.substring(index);
1090
- 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);
1091
1366
  }
1092
1367
  }
1093
1368
  }
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);
1369
+ for (const name of nameSet) {
1370
+ if (!result.title && Utils.isWellKnownName(name, ConstantString.WellknownTitleName)) {
1371
+ result.title = name;
1372
+ nameSet.delete(name);
1099
1373
  }
1100
1374
  else if (!result.subtitle &&
1101
- Utils.isWellKnownName(text, ConstantString.WellknownSubtitleName)) {
1102
- result.subtitle = text;
1103
- textBlockElements.delete(element);
1375
+ Utils.isWellKnownName(name, ConstantString.WellknownSubtitleName)) {
1376
+ result.subtitle = name;
1377
+ nameSet.delete(name);
1104
1378
  }
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);
1379
+ else if (!result.imageUrl && Utils.isWellKnownName(name, ConstantString.WellknownImageName)) {
1380
+ result.imageUrl = name;
1381
+ nameSet.delete(name);
1116
1382
  }
1117
1383
  }
1118
- for (const element of textBlockElements) {
1119
- const text = element.text;
1384
+ for (const name of nameSet) {
1120
1385
  if (!result.title) {
1121
- result.title = text;
1122
- textBlockElements.delete(element);
1386
+ result.title = name;
1387
+ nameSet.delete(name);
1123
1388
  }
1124
1389
  else if (!result.subtitle) {
1125
- result.subtitle = text;
1126
- textBlockElements.delete(element);
1390
+ result.subtitle = name;
1391
+ nameSet.delete(name);
1127
1392
  }
1128
1393
  }
1129
1394
  if (!result.title && result.subtitle) {
1130
1395
  result.title = result.subtitle;
1131
1396
  delete result.subtitle;
1132
1397
  }
1133
- if (!result.title) {
1134
- result.title = "result";
1135
- }
1136
1398
  return result;
1137
1399
  }
1138
1400
 
1401
+ // Copyright (c) Microsoft Corporation.
1402
+ class ManifestUpdater {
1403
+ static updateManifestWithAiPlugin(manifestPath, outputSpecPath, apiPluginFilePath, spec, options, authInfo) {
1404
+ return __awaiter(this, void 0, void 0, function* () {
1405
+ const manifest = yield fs__default['default'].readJSON(manifestPath);
1406
+ const apiPluginRelativePath = ManifestUpdater.getRelativePath(manifestPath, apiPluginFilePath);
1407
+ manifest.plugins = [
1408
+ {
1409
+ file: apiPluginRelativePath,
1410
+ id: ConstantString.DefaultPluginId,
1411
+ },
1412
+ ];
1413
+ const appName = this.removeEnvs(manifest.name.short);
1414
+ ManifestUpdater.updateManifestDescription(manifest, spec);
1415
+ const specRelativePath = ManifestUpdater.getRelativePath(manifestPath, outputSpecPath);
1416
+ const apiPlugin = yield ManifestUpdater.generatePluginManifestSchema(spec, specRelativePath, apiPluginFilePath, appName, authInfo, options);
1417
+ return [manifest, apiPlugin];
1418
+ });
1419
+ }
1420
+ static updateManifestDescription(manifest, spec) {
1421
+ var _a, _b;
1422
+ manifest.description = {
1423
+ short: spec.info.title.slice(0, ConstantString.ShortDescriptionMaxLens),
1424
+ full: (_b = ((_a = spec.info.description) !== null && _a !== void 0 ? _a : manifest.description.full)) === null || _b === void 0 ? void 0 : _b.slice(0, ConstantString.FullDescriptionMaxLens),
1425
+ };
1426
+ }
1427
+ static mapOpenAPISchemaToFuncParam(schema, method, pathUrl) {
1428
+ let parameter;
1429
+ if (schema.type === "array") {
1430
+ const items = schema.items;
1431
+ parameter = {
1432
+ type: "array",
1433
+ items: ManifestUpdater.mapOpenAPISchemaToFuncParam(items, method, pathUrl),
1434
+ };
1435
+ }
1436
+ else if (schema.type === "string" ||
1437
+ schema.type === "boolean" ||
1438
+ schema.type === "integer" ||
1439
+ schema.type === "number") {
1440
+ parameter = {
1441
+ type: schema.type,
1442
+ };
1443
+ }
1444
+ else {
1445
+ throw new SpecParserError(Utils.format(ConstantString.UnsupportedSchema, method, pathUrl, JSON.stringify(schema)), exports.ErrorType.UpdateManifestFailed);
1446
+ }
1447
+ if (schema.enum) {
1448
+ parameter.enum = schema.enum;
1449
+ }
1450
+ if (schema.description) {
1451
+ parameter.description = schema.description;
1452
+ }
1453
+ if (schema.default) {
1454
+ parameter.default = schema.default;
1455
+ }
1456
+ return parameter;
1457
+ }
1458
+ static generatePluginManifestSchema(spec, specRelativePath, apiPluginFilePath, appName, authInfo, options) {
1459
+ var _a, _b, _c, _d, _e;
1460
+ return __awaiter(this, void 0, void 0, function* () {
1461
+ const functions = [];
1462
+ const functionNames = [];
1463
+ const conversationStarters = [];
1464
+ const paths = spec.paths;
1465
+ const pluginAuthObj = {
1466
+ type: "None",
1467
+ };
1468
+ if (authInfo) {
1469
+ if (Utils.isOAuthWithAuthCodeFlow(authInfo.authScheme)) {
1470
+ pluginAuthObj.type = "OAuthPluginVault";
1471
+ }
1472
+ else if (Utils.isBearerTokenAuth(authInfo.authScheme)) {
1473
+ pluginAuthObj.type = "ApiKeyPluginVault";
1474
+ }
1475
+ if (pluginAuthObj.type !== "None") {
1476
+ pluginAuthObj.reference_id = `${Utils.getSafeRegistrationIdEnvName(authInfo.name)}_REGISTRATION_ID`;
1477
+ }
1478
+ }
1479
+ for (const pathUrl in paths) {
1480
+ const pathItem = paths[pathUrl];
1481
+ if (pathItem) {
1482
+ const operations = pathItem;
1483
+ for (const method in operations) {
1484
+ if (options.allowMethods.includes(method)) {
1485
+ const operationItem = operations[method];
1486
+ const confirmationBodies = [];
1487
+ if (operationItem) {
1488
+ const operationId = operationItem.operationId;
1489
+ const description = (_a = operationItem.description) !== null && _a !== void 0 ? _a : "";
1490
+ const paramObject = operationItem.parameters;
1491
+ const requestBody = operationItem.requestBody;
1492
+ const parameters = {
1493
+ type: "object",
1494
+ properties: {},
1495
+ required: [],
1496
+ };
1497
+ if (paramObject) {
1498
+ for (let i = 0; i < paramObject.length; i++) {
1499
+ const param = paramObject[i];
1500
+ const schema = param.schema;
1501
+ parameters.properties[param.name] = ManifestUpdater.mapOpenAPISchemaToFuncParam(schema, method, pathUrl);
1502
+ confirmationBodies.push(ManifestUpdater.getConfirmationBodyItem(param.name));
1503
+ if (param.required) {
1504
+ parameters.required.push(param.name);
1505
+ }
1506
+ if (!parameters.properties[param.name].description) {
1507
+ parameters.properties[param.name].description = (_b = param.description) !== null && _b !== void 0 ? _b : "";
1508
+ }
1509
+ }
1510
+ }
1511
+ if (requestBody) {
1512
+ const requestJsonBody = requestBody.content["application/json"];
1513
+ const requestBodySchema = requestJsonBody.schema;
1514
+ if (requestBodySchema.type === "object") {
1515
+ if (requestBodySchema.required) {
1516
+ parameters.required.push(...requestBodySchema.required);
1517
+ }
1518
+ for (const property in requestBodySchema.properties) {
1519
+ const schema = requestBodySchema.properties[property];
1520
+ parameters.properties[property] = ManifestUpdater.mapOpenAPISchemaToFuncParam(schema, method, pathUrl);
1521
+ confirmationBodies.push(ManifestUpdater.getConfirmationBodyItem(property));
1522
+ }
1523
+ }
1524
+ else {
1525
+ throw new SpecParserError(Utils.format(ConstantString.UnsupportedSchema, method, pathUrl, JSON.stringify(requestBodySchema)), exports.ErrorType.UpdateManifestFailed);
1526
+ }
1527
+ }
1528
+ const funcObj = {
1529
+ name: operationId,
1530
+ description: description,
1531
+ };
1532
+ if (paramObject || requestBody) {
1533
+ funcObj.parameters = parameters;
1534
+ }
1535
+ if (options.allowResponseSemantics) {
1536
+ const [card, jsonPath] = AdaptiveCardGenerator.generateAdaptiveCard(operationItem);
1537
+ const responseSemantic = wrapResponseSemantics(card, jsonPath);
1538
+ funcObj.capabilities = {
1539
+ response_semantics: responseSemantic,
1540
+ };
1541
+ }
1542
+ if (options.allowConfirmation && method !== ConstantString.GetMethod) {
1543
+ if (!funcObj.capabilities) {
1544
+ funcObj.capabilities = {};
1545
+ }
1546
+ funcObj.capabilities.confirmation = {
1547
+ type: "AdaptiveCard",
1548
+ title: (_c = operationItem.summary) !== null && _c !== void 0 ? _c : description,
1549
+ };
1550
+ if (confirmationBodies.length > 0) {
1551
+ funcObj.capabilities.confirmation.body = confirmationBodies.join("\n");
1552
+ }
1553
+ }
1554
+ functions.push(funcObj);
1555
+ functionNames.push(operationId);
1556
+ if (description) {
1557
+ conversationStarters.push(description);
1558
+ }
1559
+ }
1560
+ }
1561
+ }
1562
+ }
1563
+ }
1564
+ let apiPlugin;
1565
+ if (yield fs__default['default'].pathExists(apiPluginFilePath)) {
1566
+ apiPlugin = yield fs__default['default'].readJSON(apiPluginFilePath);
1567
+ }
1568
+ else {
1569
+ apiPlugin = {
1570
+ schema_version: "v2.1",
1571
+ name_for_human: "",
1572
+ description_for_human: "",
1573
+ namespace: "",
1574
+ functions: [],
1575
+ runtimes: [],
1576
+ };
1577
+ }
1578
+ apiPlugin.functions = apiPlugin.functions || [];
1579
+ for (const func of functions) {
1580
+ const index = (_d = apiPlugin.functions) === null || _d === void 0 ? void 0 : _d.findIndex((f) => f.name === func.name);
1581
+ if (index === -1) {
1582
+ apiPlugin.functions.push(func);
1583
+ }
1584
+ else {
1585
+ apiPlugin.functions[index] = func;
1586
+ }
1587
+ }
1588
+ apiPlugin.runtimes = apiPlugin.runtimes || [];
1589
+ const index = apiPlugin.runtimes.findIndex((runtime) => {
1590
+ var _a, _b;
1591
+ return runtime.spec.url === specRelativePath &&
1592
+ runtime.type === "OpenApi" &&
1593
+ ((_b = (_a = runtime.auth) === null || _a === void 0 ? void 0 : _a.type) !== null && _b !== void 0 ? _b : "None") === pluginAuthObj.type;
1594
+ });
1595
+ if (index === -1) {
1596
+ apiPlugin.runtimes.push({
1597
+ type: "OpenApi",
1598
+ auth: pluginAuthObj,
1599
+ spec: {
1600
+ url: specRelativePath,
1601
+ },
1602
+ run_for_functions: functionNames,
1603
+ });
1604
+ }
1605
+ else {
1606
+ apiPlugin.runtimes[index].run_for_functions = functionNames;
1607
+ }
1608
+ if (!apiPlugin.name_for_human) {
1609
+ apiPlugin.name_for_human = appName;
1610
+ }
1611
+ if (!apiPlugin.namespace) {
1612
+ apiPlugin.namespace = ManifestUpdater.removeAllSpecialCharacters(appName);
1613
+ }
1614
+ if (!apiPlugin.description_for_human) {
1615
+ apiPlugin.description_for_human =
1616
+ (_e = spec.info.description) !== null && _e !== void 0 ? _e : "<Please add description of the plugin>";
1617
+ }
1618
+ if (options.allowConversationStarters && conversationStarters.length > 0) {
1619
+ if (!apiPlugin.capabilities) {
1620
+ apiPlugin.capabilities = {
1621
+ localization: {},
1622
+ };
1623
+ }
1624
+ if (!apiPlugin.capabilities.conversation_starters) {
1625
+ apiPlugin.capabilities.conversation_starters = conversationStarters
1626
+ .slice(0, 5)
1627
+ .map((text) => ({ text }));
1628
+ }
1629
+ }
1630
+ return apiPlugin;
1631
+ });
1632
+ }
1633
+ static updateManifest(manifestPath, outputSpecPath, spec, options, adaptiveCardFolder, authInfo) {
1634
+ return __awaiter(this, void 0, void 0, function* () {
1635
+ try {
1636
+ const originalManifest = yield fs__default['default'].readJSON(manifestPath);
1637
+ const updatedPart = {};
1638
+ updatedPart.composeExtensions = [];
1639
+ let warnings = [];
1640
+ if (options.projectType === exports.ProjectType.SME) {
1641
+ const updateResult = yield ManifestUpdater.generateCommands(spec, manifestPath, options, adaptiveCardFolder);
1642
+ const commands = updateResult[0];
1643
+ warnings = updateResult[1];
1644
+ const composeExtension = {
1645
+ composeExtensionType: "apiBased",
1646
+ apiSpecificationFile: ManifestUpdater.getRelativePath(manifestPath, outputSpecPath),
1647
+ commands: commands,
1648
+ };
1649
+ if (authInfo) {
1650
+ const auth = authInfo.authScheme;
1651
+ if (Utils.isAPIKeyAuth(auth) || Utils.isBearerTokenAuth(auth)) {
1652
+ const safeApiSecretRegistrationId = Utils.getSafeRegistrationIdEnvName(`${authInfo.name}_${ConstantString.RegistrationIdPostfix}`);
1653
+ composeExtension.authorization = {
1654
+ authType: "apiSecretServiceAuth",
1655
+ apiSecretServiceAuthConfiguration: {
1656
+ apiSecretRegistrationId: `\${{${safeApiSecretRegistrationId}}}`,
1657
+ },
1658
+ };
1659
+ }
1660
+ else if (Utils.isOAuthWithAuthCodeFlow(auth)) {
1661
+ const safeOAuth2RegistrationId = Utils.getSafeRegistrationIdEnvName(`${authInfo.name}_${ConstantString.OAuthRegistrationIdPostFix}`);
1662
+ composeExtension.authorization = {
1663
+ authType: "oAuth2.0",
1664
+ oAuthConfiguration: {
1665
+ oauthConfigurationId: `\${{${safeOAuth2RegistrationId}}}`,
1666
+ },
1667
+ };
1668
+ updatedPart.webApplicationInfo = {
1669
+ id: "${{AAD_APP_CLIENT_ID}}",
1670
+ resource: "api://${{DOMAIN}}/${{AAD_APP_CLIENT_ID}}",
1671
+ };
1672
+ }
1673
+ }
1674
+ updatedPart.composeExtensions = [composeExtension];
1675
+ }
1676
+ updatedPart.description = originalManifest.description;
1677
+ ManifestUpdater.updateManifestDescription(updatedPart, spec);
1678
+ const updatedManifest = Object.assign(Object.assign({}, originalManifest), updatedPart);
1679
+ return [updatedManifest, warnings];
1680
+ }
1681
+ catch (err) {
1682
+ throw new SpecParserError(err.toString(), exports.ErrorType.UpdateManifestFailed);
1683
+ }
1684
+ });
1685
+ }
1686
+ static generateCommands(spec, manifestPath, options, adaptiveCardFolder) {
1687
+ var _a;
1688
+ return __awaiter(this, void 0, void 0, function* () {
1689
+ const paths = spec.paths;
1690
+ const commands = [];
1691
+ const warnings = [];
1692
+ if (paths) {
1693
+ for (const pathUrl in paths) {
1694
+ const pathItem = paths[pathUrl];
1695
+ if (pathItem) {
1696
+ const operations = pathItem;
1697
+ // Currently only support GET and POST method
1698
+ for (const method in operations) {
1699
+ if ((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) {
1700
+ const operationItem = operations[method];
1701
+ if (operationItem) {
1702
+ const command = Utils.parseApiInfo(operationItem, options);
1703
+ if (command.parameters &&
1704
+ command.parameters.length >= 1 &&
1705
+ command.parameters.some((param) => param.isRequired)) {
1706
+ command.parameters = command.parameters.filter((param) => param.isRequired);
1707
+ }
1708
+ else if (command.parameters && command.parameters.length > 0) {
1709
+ command.parameters = [command.parameters[0]];
1710
+ warnings.push({
1711
+ type: exports.WarningType.OperationOnlyContainsOptionalParam,
1712
+ content: Utils.format(ConstantString.OperationOnlyContainsOptionalParam, command.id),
1713
+ data: command.id,
1714
+ });
1715
+ }
1716
+ if (adaptiveCardFolder) {
1717
+ const adaptiveCardPath = path__default['default'].join(adaptiveCardFolder, command.id + ".json");
1718
+ command.apiResponseRenderingTemplateFile = (yield fs__default['default'].pathExists(adaptiveCardPath))
1719
+ ? ManifestUpdater.getRelativePath(manifestPath, adaptiveCardPath)
1720
+ : "";
1721
+ }
1722
+ commands.push(command);
1723
+ }
1724
+ }
1725
+ }
1726
+ }
1727
+ }
1728
+ }
1729
+ return [commands, warnings];
1730
+ });
1731
+ }
1732
+ static getRelativePath(from, to) {
1733
+ const relativePath = path__default['default'].relative(path__default['default'].dirname(from), to);
1734
+ return path__default['default'].normalize(relativePath).replace(/\\/g, "/");
1735
+ }
1736
+ static removeEnvs(str) {
1737
+ const placeHolderReg = /\${{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*}}/g;
1738
+ const matches = placeHolderReg.exec(str);
1739
+ let newStr = str;
1740
+ if (matches != null) {
1741
+ newStr = newStr.replace(matches[0], "");
1742
+ }
1743
+ return newStr;
1744
+ }
1745
+ static removeAllSpecialCharacters(str) {
1746
+ return str.toLowerCase().replace(/[^a-z0-9]/g, "");
1747
+ }
1748
+ static getConfirmationBodyItem(paramName) {
1749
+ return `* **${Utils.updateFirstLetter(paramName)}**: {{function.parameters.${paramName}}}`;
1750
+ }
1751
+ }
1752
+
1139
1753
  // Copyright (c) Microsoft Corporation.
1140
1754
  /**
1141
1755
  * A class that parses an OpenAPI specification file and provides methods to validate, list, and generate artifacts.
@@ -1151,8 +1765,14 @@ class SpecParser {
1151
1765
  allowMissingId: true,
1152
1766
  allowSwagger: true,
1153
1767
  allowAPIKeyAuth: false,
1768
+ allowBearerTokenAuth: false,
1154
1769
  allowMultipleParameters: false,
1155
1770
  allowOauth2: false,
1771
+ allowMethods: ["get", "post"],
1772
+ allowConversationStarters: false,
1773
+ allowResponseSemantics: false,
1774
+ allowConfirmation: false,
1775
+ projectType: exports.ProjectType.SME,
1156
1776
  };
1157
1777
  this.pathOrSpec = pathOrDoc;
1158
1778
  this.parser = new SwaggerParser__default['default']();
@@ -1177,6 +1797,8 @@ class SpecParser {
1177
1797
  errors: [{ type: exports.ErrorType.SpecNotValid, content: e.toString() }],
1178
1798
  };
1179
1799
  }
1800
+ const errors = [];
1801
+ const warnings = [];
1180
1802
  if (!this.options.allowSwagger && this.isSwaggerFile) {
1181
1803
  return {
1182
1804
  status: exports.ValidationStatus.Error,
@@ -1186,7 +1808,38 @@ class SpecParser {
1186
1808
  ],
1187
1809
  };
1188
1810
  }
1189
- return Utils.validateSpec(this.spec, this.parser, !!this.isSwaggerFile, this.options.allowMissingId, this.options.allowAPIKeyAuth, this.options.allowMultipleParameters, this.options.allowOauth2);
1811
+ // Remote reference not supported
1812
+ const refPaths = this.parser.$refs.paths();
1813
+ // refPaths [0] is the current spec file path
1814
+ if (refPaths.length > 1) {
1815
+ errors.push({
1816
+ type: exports.ErrorType.RemoteRefNotSupported,
1817
+ content: Utils.format(ConstantString.RemoteRefNotSupported, refPaths.join(", ")),
1818
+ data: refPaths,
1819
+ });
1820
+ }
1821
+ if (!!this.isSwaggerFile && this.options.allowSwagger) {
1822
+ warnings.push({
1823
+ type: exports.WarningType.ConvertSwaggerToOpenAPI,
1824
+ content: ConstantString.ConvertSwaggerToOpenAPI,
1825
+ });
1826
+ }
1827
+ const validator = this.getValidator(this.spec);
1828
+ const validationResult = validator.validateSpec();
1829
+ warnings.push(...validationResult.warnings);
1830
+ errors.push(...validationResult.errors);
1831
+ let status = exports.ValidationStatus.Valid;
1832
+ if (warnings.length > 0 && errors.length === 0) {
1833
+ status = exports.ValidationStatus.Warning;
1834
+ }
1835
+ else if (errors.length > 0) {
1836
+ status = exports.ValidationStatus.Error;
1837
+ }
1838
+ return {
1839
+ status: status,
1840
+ warnings: warnings,
1841
+ errors: errors,
1842
+ };
1190
1843
  }
1191
1844
  catch (err) {
1192
1845
  throw new SpecParserError(err.toString(), exports.ErrorType.ValidateFailed);
@@ -1210,39 +1863,40 @@ class SpecParser {
1210
1863
  try {
1211
1864
  yield this.loadSpec();
1212
1865
  const spec = this.spec;
1213
- const apiMap = this.getAllSupportedAPIs(spec);
1214
- const result = [];
1866
+ const apiMap = this.getAPIs(spec);
1867
+ const result = {
1868
+ APIs: [],
1869
+ allAPICount: 0,
1870
+ validAPICount: 0,
1871
+ };
1215
1872
  for (const apiKey in apiMap) {
1873
+ const { operation, isValid, reason } = apiMap[apiKey];
1874
+ const [method, path] = apiKey.split(" ");
1875
+ const operationId = (_a = operation.operationId) !== null && _a !== void 0 ? _a : `${method.toLowerCase()}${Utils.convertPathToCamelCase(path)}`;
1216
1876
  const apiResult = {
1217
- api: "",
1877
+ api: apiKey,
1218
1878
  server: "",
1219
- operationId: "",
1879
+ operationId: operationId,
1880
+ isValid: isValid,
1881
+ reason: reason,
1220
1882
  };
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;
1883
+ if (isValid) {
1884
+ const serverObj = Utils.getServerObject(spec, method.toLocaleLowerCase(), path);
1885
+ if (serverObj) {
1886
+ apiResult.server = Utils.resolveEnv(serverObj.url);
1887
+ }
1888
+ const authArray = Utils.getAuthArray(operation.security, spec);
1889
+ for (const auths of authArray) {
1890
+ if (auths.length === 1) {
1891
+ apiResult.auth = auths[0];
1892
+ break;
1893
+ }
1241
1894
  }
1242
1895
  }
1243
- apiResult.api = apiKey;
1244
- result.push(apiResult);
1896
+ result.APIs.push(apiResult);
1245
1897
  }
1898
+ result.allAPICount = result.APIs.length;
1899
+ result.validAPICount = result.APIs.filter((api) => api.isValid).length;
1246
1900
  return result;
1247
1901
  }
1248
1902
  catch (err) {
@@ -1267,7 +1921,7 @@ class SpecParser {
1267
1921
  if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
1268
1922
  throw new SpecParserError(ConstantString.CancelledMessage, exports.ErrorType.Cancelled);
1269
1923
  }
1270
- const newUnResolvedSpec = SpecFilter.specFilter(filter, this.unResolveSpec, this.spec, this.options.allowMissingId, this.options.allowAPIKeyAuth, this.options.allowMultipleParameters, this.options.allowOauth2);
1924
+ const newUnResolvedSpec = SpecFilter.specFilter(filter, this.unResolveSpec, this.spec, this.options);
1271
1925
  if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
1272
1926
  throw new SpecParserError(ConstantString.CancelledMessage, exports.ErrorType.Cancelled);
1273
1927
  }
@@ -1287,10 +1941,9 @@ class SpecParser {
1287
1941
  * @param manifestPath A file path of the Teams app manifest file to update.
1288
1942
  * @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
1943
  * @param outputSpecPath File path of the new OpenAPI specification file to generate. If not specified or empty, no spec file will be generated.
1290
- * @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.
1944
+ * @param pluginFilePath File path of the api plugin file to generate.
1292
1945
  */
1293
- generate(manifestPath, filter, outputSpecPath, adaptiveCardFolder, signal, isMe) {
1946
+ generateForCopilot(manifestPath, filter, outputSpecPath, pluginFilePath, signal) {
1294
1947
  return __awaiter(this, void 0, void 0, function* () {
1295
1948
  const result = {
1296
1949
  allSuccess: true,
@@ -1300,52 +1953,68 @@ class SpecParser {
1300
1953
  const newSpecs = yield this.getFilteredSpecs(filter, signal);
1301
1954
  const newUnResolvedSpec = newSpecs[0];
1302
1955
  const newSpec = newSpecs[1];
1303
- const AuthSet = new Set();
1304
- let hasMultipleAPIKeyAuth = false;
1305
- for (const url in newSpec.paths) {
1306
- for (const method in newSpec.paths[url]) {
1307
- const operation = newSpec.paths[url][method];
1308
- const authArray = Utils.getAuthArray(operation.security, newSpec);
1309
- if (authArray && authArray.length > 0) {
1310
- AuthSet.add(authArray[0][0].authSchema);
1311
- if (AuthSet.size > 1) {
1312
- hasMultipleAPIKeyAuth = true;
1313
- break;
1314
- }
1315
- }
1316
- }
1317
- }
1318
- if (hasMultipleAPIKeyAuth) {
1319
- throw new SpecParserError(ConstantString.MultipleAPIKeyNotSupported, exports.ErrorType.MultipleAPIKeyNotSupported);
1956
+ const authInfo = Utils.getAuthInfo(newSpec);
1957
+ yield this.saveFilterSpec(outputSpecPath, newUnResolvedSpec);
1958
+ if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
1959
+ throw new SpecParserError(ConstantString.CancelledMessage, exports.ErrorType.Cancelled);
1320
1960
  }
1321
- let resultStr;
1322
- if (outputSpecPath.endsWith(".yaml") || outputSpecPath.endsWith(".yml")) {
1323
- resultStr = jsyaml__default['default'].dump(newUnResolvedSpec);
1961
+ const [updatedManifest, apiPlugin] = yield ManifestUpdater.updateManifestWithAiPlugin(manifestPath, outputSpecPath, pluginFilePath, newSpec, this.options, authInfo);
1962
+ yield fs__default['default'].outputJSON(manifestPath, updatedManifest, { spaces: 2 });
1963
+ yield fs__default['default'].outputJSON(pluginFilePath, apiPlugin, { spaces: 2 });
1964
+ }
1965
+ catch (err) {
1966
+ if (err instanceof SpecParserError) {
1967
+ throw err;
1324
1968
  }
1325
- else {
1326
- resultStr = JSON.stringify(newUnResolvedSpec, null, 2);
1969
+ throw new SpecParserError(err.toString(), exports.ErrorType.GenerateFailed);
1970
+ }
1971
+ return result;
1972
+ });
1973
+ }
1974
+ /**
1975
+ * Generates and update artifacts from the OpenAPI specification file. Generate Adaptive Cards, update Teams app manifest, and generate a new OpenAPI specification file.
1976
+ * @param manifestPath A file path of the Teams app manifest file to update.
1977
+ * @param filter An array of strings that represent the filters to apply when generating the artifacts. If filter is empty, it would process nothing.
1978
+ * @param outputSpecPath File path of the new OpenAPI specification file to generate. If not specified or empty, no spec file will be generated.
1979
+ * @param adaptiveCardFolder Folder path where the Adaptive Card files will be generated. If not specified or empty, Adaptive Card files will not be generated.
1980
+ */
1981
+ generate(manifestPath, filter, outputSpecPath, adaptiveCardFolder, signal) {
1982
+ return __awaiter(this, void 0, void 0, function* () {
1983
+ const result = {
1984
+ allSuccess: true,
1985
+ warnings: [],
1986
+ };
1987
+ try {
1988
+ const newSpecs = yield this.getFilteredSpecs(filter, signal);
1989
+ const newUnResolvedSpec = newSpecs[0];
1990
+ const newSpec = newSpecs[1];
1991
+ let authInfo = undefined;
1992
+ if (this.options.projectType === exports.ProjectType.SME) {
1993
+ authInfo = Utils.getAuthInfo(newSpec);
1327
1994
  }
1328
- 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
- });
1995
+ yield this.saveFilterSpec(outputSpecPath, newUnResolvedSpec);
1996
+ if (adaptiveCardFolder) {
1997
+ for (const url in newSpec.paths) {
1998
+ for (const method in newSpec.paths[url]) {
1999
+ // paths object may contain description/summary which is not a http method, so we need to check if it is a operation object
2000
+ if (this.options.allowMethods.includes(method)) {
2001
+ const operation = newSpec.paths[url][method];
2002
+ try {
2003
+ const [card, jsonPath] = AdaptiveCardGenerator.generateAdaptiveCard(operation);
2004
+ const fileName = path__default['default'].join(adaptiveCardFolder, `${operation.operationId}.json`);
2005
+ const wrappedCard = wrapAdaptiveCard(card, jsonPath);
2006
+ yield fs__default['default'].outputJSON(fileName, wrappedCard, { spaces: 2 });
2007
+ const dataFileName = path__default['default'].join(adaptiveCardFolder, `${operation.operationId}.data.json`);
2008
+ yield fs__default['default'].outputJSON(dataFileName, {}, { spaces: 2 });
2009
+ }
2010
+ catch (err) {
2011
+ result.allSuccess = false;
2012
+ result.warnings.push({
2013
+ type: exports.WarningType.GenerateCardFailed,
2014
+ content: err.toString(),
2015
+ data: operation.operationId,
2016
+ });
2017
+ }
1349
2018
  }
1350
2019
  }
1351
2020
  }
@@ -1353,8 +2022,7 @@ class SpecParser {
1353
2022
  if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
1354
2023
  throw new SpecParserError(ConstantString.CancelledMessage, exports.ErrorType.Cancelled);
1355
2024
  }
1356
- const auth = Array.from(AuthSet)[0];
1357
- const [updatedManifest, warnings] = yield ManifestUpdater.updateManifest(manifestPath, outputSpecPath, adaptiveCardFolder, newSpec, this.options.allowMultipleParameters, auth, isMe);
2025
+ const [updatedManifest, warnings] = yield ManifestUpdater.updateManifest(manifestPath, outputSpecPath, newSpec, this.options, adaptiveCardFolder, authInfo);
1358
2026
  yield fs__default['default'].outputJSON(manifestPath, updatedManifest, { spaces: 2 });
1359
2027
  result.warnings.push(...warnings);
1360
2028
  }
@@ -1382,13 +2050,30 @@ class SpecParser {
1382
2050
  }
1383
2051
  });
1384
2052
  }
1385
- getAllSupportedAPIs(spec) {
1386
- if (this.apiMap !== undefined) {
1387
- return this.apiMap;
2053
+ getAPIs(spec) {
2054
+ const validator = this.getValidator(spec);
2055
+ const apiMap = validator.listAPIs();
2056
+ return apiMap;
2057
+ }
2058
+ getValidator(spec) {
2059
+ if (this.validator) {
2060
+ return this.validator;
1388
2061
  }
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;
2062
+ const validator = ValidatorFactory.create(spec, this.options);
2063
+ this.validator = validator;
2064
+ return validator;
2065
+ }
2066
+ saveFilterSpec(outputSpecPath, unResolvedSpec) {
2067
+ return __awaiter(this, void 0, void 0, function* () {
2068
+ let resultStr;
2069
+ if (outputSpecPath.endsWith(".yaml") || outputSpecPath.endsWith(".yml")) {
2070
+ resultStr = jsyaml__default['default'].dump(unResolvedSpec);
2071
+ }
2072
+ else {
2073
+ resultStr = JSON.stringify(unResolvedSpec, null, 2);
2074
+ }
2075
+ yield fs__default['default'].outputFile(outputSpecPath, resultStr);
2076
+ });
1392
2077
  }
1393
2078
  }
1394
2079