@microsoft/m365-spec-parser 0.1.1-alpha.78701ec6a.0 → 0.1.1-alpha.87f45d762.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.
@@ -19,7 +19,8 @@ var ErrorType;
19
19
  ErrorType["NoExtraAPICanBeAdded"] = "no-extra-api-can-be-added";
20
20
  ErrorType["ResolveServerUrlFailed"] = "resolve-server-url-failed";
21
21
  ErrorType["SwaggerNotSupported"] = "swagger-not-supported";
22
- ErrorType["MultipleAPIKeyNotSupported"] = "multiple-api-key-not-supported";
22
+ ErrorType["MultipleAuthNotSupported"] = "multiple-auth-not-supported";
23
+ ErrorType["SpecVersionNotSupported"] = "spec-version-not-supported";
23
24
  ErrorType["ListFailed"] = "list-failed";
24
25
  ErrorType["listSupportedAPIInfoFailed"] = "list-supported-api-info-failed";
25
26
  ErrorType["FilterSpecFailed"] = "filter-spec-failed";
@@ -27,6 +28,22 @@ var ErrorType;
27
28
  ErrorType["GenerateAdaptiveCardFailed"] = "generate-adaptive-card-failed";
28
29
  ErrorType["GenerateFailed"] = "generate-failed";
29
30
  ErrorType["ValidateFailed"] = "validate-failed";
31
+ ErrorType["GetSpecFailed"] = "get-spec-failed";
32
+ ErrorType["AuthTypeIsNotSupported"] = "auth-type-is-not-supported";
33
+ ErrorType["MissingOperationId"] = "missing-operation-id";
34
+ ErrorType["PostBodyContainMultipleMediaTypes"] = "post-body-contain-multiple-media-types";
35
+ ErrorType["ResponseContainMultipleMediaTypes"] = "response-contain-multiple-media-types";
36
+ ErrorType["ResponseJsonIsEmpty"] = "response-json-is-empty";
37
+ ErrorType["PostBodySchemaIsNotJson"] = "post-body-schema-is-not-json";
38
+ ErrorType["PostBodyContainsRequiredUnsupportedSchema"] = "post-body-contains-required-unsupported-schema";
39
+ ErrorType["ParamsContainRequiredUnsupportedSchema"] = "params-contain-required-unsupported-schema";
40
+ ErrorType["ParamsContainsNestedObject"] = "params-contains-nested-object";
41
+ ErrorType["RequestBodyContainsNestedObject"] = "request-body-contains-nested-object";
42
+ ErrorType["ExceededRequiredParamsLimit"] = "exceeded-required-params-limit";
43
+ ErrorType["NoParameter"] = "no-parameter";
44
+ ErrorType["NoAPIInfo"] = "no-api-info";
45
+ ErrorType["MethodNotAllowed"] = "method-not-allowed";
46
+ ErrorType["UrlPathNotExist"] = "url-path-not-exist";
30
47
  ErrorType["Cancelled"] = "cancelled";
31
48
  ErrorType["Unknown"] = "unknown";
32
49
  })(ErrorType || (ErrorType = {}));
@@ -49,7 +66,13 @@ var ValidationStatus;
49
66
  ValidationStatus[ValidationStatus["Valid"] = 0] = "Valid";
50
67
  ValidationStatus[ValidationStatus["Warning"] = 1] = "Warning";
51
68
  ValidationStatus[ValidationStatus["Error"] = 2] = "Error";
52
- })(ValidationStatus || (ValidationStatus = {}));
69
+ })(ValidationStatus || (ValidationStatus = {}));
70
+ var ProjectType;
71
+ (function (ProjectType) {
72
+ ProjectType[ProjectType["Copilot"] = 0] = "Copilot";
73
+ ProjectType[ProjectType["SME"] = 1] = "SME";
74
+ ProjectType[ProjectType["TeamsAi"] = 2] = "TeamsAi";
75
+ })(ProjectType || (ProjectType = {}));
53
76
 
54
77
  // Copyright (c) Microsoft Corporation.
55
78
  class ConstantString {
@@ -68,7 +91,9 @@ ConstantString.ResolveServerUrlFailed = "Unable to resolve the server URL: pleas
68
91
  ConstantString.OperationOnlyContainsOptionalParam = "Operation %s contains multiple optional parameters. The first optional parameter is used for this command.";
69
92
  ConstantString.ConvertSwaggerToOpenAPI = "The Swagger 2.0 file has been converted to OpenAPI 3.0.";
70
93
  ConstantString.SwaggerNotSupported = "Swagger 2.0 is not supported. Please convert to OpenAPI 3.0 manually before proceeding.";
71
- ConstantString.MultipleAPIKeyNotSupported = "Multiple API keys are not supported. Please make sure that all selected APIs use the same API key.";
94
+ ConstantString.SpecVersionNotSupported = "Unsupported OpenAPI version %s. Please use version 3.0.x.";
95
+ ConstantString.MultipleAuthNotSupported = "Multiple authentication methods are unsupported. Ensure all selected APIs use identical authentication.";
96
+ ConstantString.UnsupportedSchema = "Unsupported schema in %s %s: %s";
72
97
  ConstantString.WrappedCardVersion = "devPreview";
73
98
  ConstantString.WrappedCardSchema = "https://developer.microsoft.com/json-schemas/teams/vDevPreview/MicrosoftTeams.ResponseRenderingTemplate.schema.json";
74
99
  ConstantString.WrappedCardResponseLayout = "list";
@@ -80,6 +105,7 @@ ConstantString.AdaptiveCardType = "AdaptiveCard";
80
105
  ConstantString.TextBlockType = "TextBlock";
81
106
  ConstantString.ContainerType = "Container";
82
107
  ConstantString.RegistrationIdPostfix = "REGISTRATION_ID";
108
+ ConstantString.OAuthRegistrationIdPostFix = "OAUTH_REGISTRATION_ID";
83
109
  ConstantString.ResponseCodeFor20X = [
84
110
  "200",
85
111
  "201",
@@ -139,7 +165,9 @@ ConstantString.FullDescriptionMaxLens = 4000;
139
165
  ConstantString.CommandDescriptionMaxLens = 128;
140
166
  ConstantString.ParameterDescriptionMaxLens = 128;
141
167
  ConstantString.CommandTitleMaxLens = 32;
142
- ConstantString.ParameterTitleMaxLens = 32;
168
+ ConstantString.ParameterTitleMaxLens = 32;
169
+ ConstantString.SMERequiredParamsMaxNum = 5;
170
+ ConstantString.DefaultPluginId = "plugin_1";
143
171
 
144
172
  // Copyright (c) Microsoft Corporation.
145
173
  class SpecParserError extends Error {
@@ -151,201 +179,30 @@ class SpecParserError extends Error {
151
179
 
152
180
  // Copyright (c) Microsoft Corporation.
153
181
  class Utils {
154
- static checkParameters(paramObject) {
155
- const paramResult = {
156
- requiredNum: 0,
157
- optionalNum: 0,
158
- isValid: true,
159
- };
160
- if (!paramObject) {
161
- return paramResult;
162
- }
163
- for (let i = 0; i < paramObject.length; i++) {
164
- const param = paramObject[i];
165
- const schema = param.schema;
166
- const isRequiredWithoutDefault = param.required && schema.default === undefined;
167
- if (param.in === "header" || param.in === "cookie") {
168
- if (isRequiredWithoutDefault) {
169
- paramResult.isValid = false;
170
- }
171
- continue;
172
- }
173
- if (schema.type !== "boolean" &&
174
- schema.type !== "string" &&
175
- schema.type !== "number" &&
176
- schema.type !== "integer") {
177
- if (isRequiredWithoutDefault) {
178
- paramResult.isValid = false;
179
- }
180
- continue;
181
- }
182
- if (param.in === "query" || param.in === "path") {
183
- if (isRequiredWithoutDefault) {
184
- paramResult.requiredNum = paramResult.requiredNum + 1;
185
- }
186
- else {
187
- paramResult.optionalNum = paramResult.optionalNum + 1;
188
- }
189
- }
190
- }
191
- return paramResult;
192
- }
193
- static checkPostBody(schema, isRequired = false) {
194
- var _a;
195
- const paramResult = {
196
- requiredNum: 0,
197
- optionalNum: 0,
198
- isValid: true,
199
- };
200
- if (Object.keys(schema).length === 0) {
201
- return paramResult;
202
- }
203
- const isRequiredWithoutDefault = isRequired && schema.default === undefined;
204
- if (schema.type === "string" ||
205
- schema.type === "integer" ||
206
- schema.type === "boolean" ||
207
- schema.type === "number") {
208
- if (isRequiredWithoutDefault) {
209
- paramResult.requiredNum = paramResult.requiredNum + 1;
210
- }
211
- else {
212
- paramResult.optionalNum = paramResult.optionalNum + 1;
213
- }
214
- }
215
- else if (schema.type === "object") {
216
- const { properties } = schema;
217
- for (const property in properties) {
218
- let isRequired = false;
219
- if (schema.required && ((_a = schema.required) === null || _a === void 0 ? void 0 : _a.indexOf(property)) >= 0) {
220
- isRequired = true;
221
- }
222
- const result = Utils.checkPostBody(properties[property], isRequired);
223
- paramResult.requiredNum += result.requiredNum;
224
- paramResult.optionalNum += result.optionalNum;
225
- paramResult.isValid = paramResult.isValid && result.isValid;
226
- }
227
- }
228
- else {
229
- if (isRequiredWithoutDefault) {
230
- paramResult.isValid = false;
231
- }
232
- }
233
- return paramResult;
234
- }
235
- /**
236
- * Checks if the given API is supported.
237
- * @param {string} method - The HTTP method of the API.
238
- * @param {string} path - The path of the API.
239
- * @param {OpenAPIV3.Document} spec - The OpenAPI specification document.
240
- * @returns {boolean} - Returns true if the API is supported, false otherwise.
241
- * @description The following APIs are supported:
242
- * 1. only support Get/Post operation without auth property
243
- * 2. parameter inside query or path only support string, number, boolean and integer
244
- * 3. parameter inside post body only support string, number, boolean, integer and object
245
- * 4. request body + required parameters <= 1
246
- * 5. response body should be “application/json” and not empty, and response code should be 20X
247
- * 6. only support request body with “application/json” content type
248
- */
249
- static isSupportedApi(method, path, spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2) {
250
- const pathObj = spec.paths[path];
251
- method = method.toLocaleLowerCase();
252
- if (pathObj) {
253
- if ((method === ConstantString.PostMethod || method === ConstantString.GetMethod) &&
254
- pathObj[method]) {
255
- const securities = pathObj[method].security;
256
- const authArray = Utils.getAuthArray(securities, spec);
257
- if (!Utils.isSupportedAuth(authArray, allowAPIKeyAuth, allowOauth2)) {
258
- return false;
259
- }
260
- const operationObject = pathObj[method];
261
- if (!allowMissingId && !operationObject.operationId) {
262
- return false;
263
- }
264
- const paramObject = operationObject.parameters;
265
- const requestBody = operationObject.requestBody;
266
- const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
267
- const mediaTypesCount = Object.keys((requestBody === null || requestBody === void 0 ? void 0 : requestBody.content) || {}).length;
268
- if (mediaTypesCount > 1) {
269
- return false;
270
- }
271
- const responseJson = Utils.getResponseJson(operationObject);
272
- if (Object.keys(responseJson).length === 0) {
273
- return false;
274
- }
275
- let requestBodyParamResult = {
276
- requiredNum: 0,
277
- optionalNum: 0,
278
- isValid: true,
279
- };
280
- if (requestJsonBody) {
281
- const requestBodySchema = requestJsonBody.schema;
282
- requestBodyParamResult = Utils.checkPostBody(requestBodySchema, requestBody.required);
283
- }
284
- if (!requestBodyParamResult.isValid) {
285
- return false;
286
- }
287
- const paramResult = Utils.checkParameters(paramObject);
288
- if (!paramResult.isValid) {
289
- return false;
290
- }
291
- if (requestBodyParamResult.requiredNum + paramResult.requiredNum > 1) {
292
- if (allowMultipleParameters &&
293
- requestBodyParamResult.requiredNum + paramResult.requiredNum <= 5) {
294
- return true;
295
- }
296
- return false;
297
- }
298
- else if (requestBodyParamResult.requiredNum +
299
- requestBodyParamResult.optionalNum +
300
- paramResult.requiredNum +
301
- paramResult.optionalNum ===
302
- 0) {
303
- return false;
304
- }
305
- else {
182
+ static hasNestedObjectInSchema(schema) {
183
+ if (schema.type === "object") {
184
+ for (const property in schema.properties) {
185
+ const nestedSchema = schema.properties[property];
186
+ if (nestedSchema.type === "object") {
306
187
  return true;
307
188
  }
308
189
  }
309
190
  }
310
191
  return false;
311
192
  }
312
- static isSupportedAuth(authSchemaArray, allowAPIKeyAuth, allowOauth2) {
313
- if (authSchemaArray.length === 0) {
314
- return true;
315
- }
316
- if (allowAPIKeyAuth || allowOauth2) {
317
- // Currently we don't support multiple auth in one operation
318
- if (authSchemaArray.length > 0 && authSchemaArray.every((auths) => auths.length > 1)) {
319
- return false;
320
- }
321
- for (const auths of authSchemaArray) {
322
- if (auths.length === 1) {
323
- if (!allowOauth2 && allowAPIKeyAuth && Utils.isAPIKeyAuth(auths[0].authSchema)) {
324
- return true;
325
- }
326
- else if (!allowAPIKeyAuth &&
327
- allowOauth2 &&
328
- Utils.isBearerTokenAuth(auths[0].authSchema)) {
329
- return true;
330
- }
331
- else if (allowAPIKeyAuth &&
332
- allowOauth2 &&
333
- (Utils.isAPIKeyAuth(auths[0].authSchema) ||
334
- Utils.isBearerTokenAuth(auths[0].authSchema))) {
335
- return true;
336
- }
337
- }
338
- }
339
- }
340
- return false;
193
+ static containMultipleMediaTypes(bodyObject) {
194
+ return Object.keys((bodyObject === null || bodyObject === void 0 ? void 0 : bodyObject.content) || {}).length > 1;
341
195
  }
342
- static isAPIKeyAuth(authSchema) {
343
- return authSchema.type === "apiKey";
196
+ static isBearerTokenAuth(authScheme) {
197
+ return authScheme.type === "http" && authScheme.scheme === "bearer";
344
198
  }
345
- static isBearerTokenAuth(authSchema) {
346
- return (authSchema.type === "oauth2" ||
347
- authSchema.type === "openIdConnect" ||
348
- (authSchema.type === "http" && authSchema.scheme === "bearer"));
199
+ static isAPIKeyAuth(authScheme) {
200
+ return authScheme.type === "apiKey";
201
+ }
202
+ static isOAuthWithAuthCodeFlow(authScheme) {
203
+ return !!(authScheme.type === "oauth2" &&
204
+ authScheme.flows &&
205
+ authScheme.flows.authorizationCode);
349
206
  }
350
207
  static getAuthArray(securities, spec) {
351
208
  var _a;
@@ -358,7 +215,7 @@ class Utils {
358
215
  for (const name in security) {
359
216
  const auth = securitySchemas[name];
360
217
  authArray.push({
361
- authSchema: auth,
218
+ authScheme: auth,
362
219
  name: name,
363
220
  });
364
221
  }
@@ -376,18 +233,22 @@ class Utils {
376
233
  static getResponseJson(operationObject) {
377
234
  var _a, _b;
378
235
  let json = {};
236
+ let multipleMediaType = false;
379
237
  for (const code of ConstantString.ResponseCodeFor20X) {
380
238
  const responseObject = (_a = operationObject === null || operationObject === void 0 ? void 0 : operationObject.responses) === null || _a === void 0 ? void 0 : _a[code];
381
- const mediaTypesCount = Object.keys((responseObject === null || responseObject === void 0 ? void 0 : responseObject.content) || {}).length;
382
- if (mediaTypesCount > 1) {
383
- return {};
384
- }
385
239
  if ((_b = responseObject === null || responseObject === void 0 ? void 0 : responseObject.content) === null || _b === void 0 ? void 0 : _b["application/json"]) {
240
+ multipleMediaType = false;
386
241
  json = responseObject.content["application/json"];
387
- break;
242
+ if (Utils.containMultipleMediaTypes(responseObject)) {
243
+ multipleMediaType = true;
244
+ json = {};
245
+ }
246
+ else {
247
+ break;
248
+ }
388
249
  }
389
250
  }
390
- return json;
251
+ return { json, multipleMediaType };
391
252
  }
392
253
  static convertPathToCamelCase(path) {
393
254
  const pathSegments = path.split(/[./{]/);
@@ -407,10 +268,10 @@ class Utils {
407
268
  return undefined;
408
269
  }
409
270
  }
410
- static resolveServerUrl(url) {
271
+ static resolveEnv(str) {
411
272
  const placeHolderReg = /\${{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*}}/g;
412
- let matches = placeHolderReg.exec(url);
413
- let newUrl = url;
273
+ let matches = placeHolderReg.exec(str);
274
+ let newStr = str;
414
275
  while (matches != null) {
415
276
  const envVar = matches[1];
416
277
  const envVal = process.env[envVar];
@@ -418,17 +279,17 @@ class Utils {
418
279
  throw new Error(Utils.format(ConstantString.ResolveServerUrlFailed, envVar));
419
280
  }
420
281
  else {
421
- newUrl = newUrl.replace(matches[0], envVal);
282
+ newStr = newStr.replace(matches[0], envVal);
422
283
  }
423
- matches = placeHolderReg.exec(url);
284
+ matches = placeHolderReg.exec(str);
424
285
  }
425
- return newUrl;
286
+ return newStr;
426
287
  }
427
288
  static checkServerUrl(servers) {
428
289
  const errors = [];
429
290
  let serverUrl;
430
291
  try {
431
- serverUrl = Utils.resolveServerUrl(servers[0].url);
292
+ serverUrl = Utils.resolveEnv(servers[0].url);
432
293
  }
433
294
  catch (err) {
434
295
  errors.push({
@@ -458,7 +319,8 @@ class Utils {
458
319
  }
459
320
  return errors;
460
321
  }
461
- static validateServer(spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2) {
322
+ static validateServer(spec, options) {
323
+ var _a;
462
324
  const errors = [];
463
325
  let hasTopLevelServers = false;
464
326
  let hasPathLevelServers = false;
@@ -479,7 +341,7 @@ class Utils {
479
341
  }
480
342
  for (const method in methods) {
481
343
  const operationObject = methods[method];
482
- if (Utils.isSupportedApi(method, path, spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2)) {
344
+ if (((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && operationObject) {
483
345
  if ((operationObject === null || operationObject === void 0 ? void 0 : operationObject.servers) && operationObject.servers.length >= 1) {
484
346
  hasOperationLevelServers = true;
485
347
  const serverErrors = Utils.checkServerUrl(operationObject.servers);
@@ -522,6 +384,7 @@ class Utils {
522
384
  Utils.updateParameterWithInputType(schema, parameter);
523
385
  }
524
386
  if (isRequired && schema.default === undefined) {
387
+ parameter.isRequired = true;
525
388
  requiredParams.push(parameter);
526
389
  }
527
390
  else {
@@ -566,7 +429,7 @@ class Utils {
566
429
  param.value = schema.default;
567
430
  }
568
431
  }
569
- static parseApiInfo(operationItem, allowMultipleParameters) {
432
+ static parseApiInfo(operationItem, options) {
570
433
  var _a, _b;
571
434
  const requiredParams = [];
572
435
  const optionalParams = [];
@@ -580,11 +443,12 @@ class Utils {
580
443
  description: ((_a = param.description) !== null && _a !== void 0 ? _a : "").slice(0, ConstantString.ParameterDescriptionMaxLens),
581
444
  };
582
445
  const schema = param.schema;
583
- if (allowMultipleParameters && schema) {
446
+ if (options.allowMultipleParameters && schema) {
584
447
  Utils.updateParameterWithInputType(schema, parameter);
585
448
  }
586
449
  if (param.in !== "header" && param.in !== "cookie") {
587
450
  if (param.required && (schema === null || schema === void 0 ? void 0 : schema.default) === undefined) {
451
+ parameter.isRequired = true;
588
452
  requiredParams.push(parameter);
589
453
  }
590
454
  else {
@@ -598,19 +462,13 @@ class Utils {
598
462
  const requestJson = requestBody.content["application/json"];
599
463
  if (Object.keys(requestJson).length !== 0) {
600
464
  const schema = requestJson.schema;
601
- const [requiredP, optionalP] = Utils.generateParametersFromSchema(schema, "requestBody", allowMultipleParameters, requestBody.required);
465
+ const [requiredP, optionalP] = Utils.generateParametersFromSchema(schema, "requestBody", !!options.allowMultipleParameters, requestBody.required);
602
466
  requiredParams.push(...requiredP);
603
467
  optionalParams.push(...optionalP);
604
468
  }
605
469
  }
606
470
  const operationId = operationItem.operationId;
607
- const parameters = [];
608
- if (requiredParams.length !== 0) {
609
- parameters.push(...requiredParams);
610
- }
611
- else {
612
- parameters.push(optionalParams[0]);
613
- }
471
+ const parameters = [...requiredParams, ...optionalParams];
614
472
  const command = {
615
473
  context: ["compose"],
616
474
  type: "query",
@@ -619,130 +477,568 @@ class Utils {
619
477
  parameters: parameters,
620
478
  description: ((_b = operationItem.description) !== null && _b !== void 0 ? _b : "").slice(0, ConstantString.CommandDescriptionMaxLens),
621
479
  };
622
- let warning = undefined;
623
- if (requiredParams.length === 0 && optionalParams.length > 1) {
624
- warning = {
625
- type: WarningType.OperationOnlyContainsOptionalParam,
626
- content: Utils.format(ConstantString.OperationOnlyContainsOptionalParam, operationId),
627
- data: operationId,
628
- };
480
+ return command;
481
+ }
482
+ static format(str, ...args) {
483
+ let index = 0;
484
+ return str.replace(/%s/g, () => {
485
+ const arg = args[index++];
486
+ return arg !== undefined ? arg : "";
487
+ });
488
+ }
489
+ static getSafeRegistrationIdEnvName(authName) {
490
+ if (!authName) {
491
+ return "";
629
492
  }
630
- return [command, warning];
493
+ let safeRegistrationIdEnvName = authName.toUpperCase().replace(/[^A-Z0-9_]/g, "_");
494
+ if (!safeRegistrationIdEnvName.match(/^[A-Z]/)) {
495
+ safeRegistrationIdEnvName = "PREFIX_" + safeRegistrationIdEnvName;
496
+ }
497
+ return safeRegistrationIdEnvName;
631
498
  }
632
- static listSupportedAPIs(spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2) {
633
- const paths = spec.paths;
499
+ static getServerObject(spec, method, path) {
500
+ const pathObj = spec.paths[path];
501
+ const operationObject = pathObj[method];
502
+ const rootServer = spec.servers && spec.servers[0];
503
+ const methodServer = spec.paths[path].servers && spec.paths[path].servers[0];
504
+ const operationServer = operationObject.servers && operationObject.servers[0];
505
+ const serverUrl = operationServer || methodServer || rootServer;
506
+ return serverUrl;
507
+ }
508
+ }
509
+
510
+ // Copyright (c) Microsoft Corporation.
511
+ class Validator {
512
+ listAPIs() {
513
+ var _a;
514
+ if (this.apiMap) {
515
+ return this.apiMap;
516
+ }
517
+ const paths = this.spec.paths;
634
518
  const result = {};
635
519
  for (const path in paths) {
636
520
  const methods = paths[path];
637
521
  for (const method in methods) {
638
- // For developer preview, only support GET operation with only 1 parameter without auth
639
- if (Utils.isSupportedApi(method, path, spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2)) {
640
- const operationObject = methods[method];
641
- result[`${method.toUpperCase()} ${path}`] = operationObject;
522
+ const operationObject = methods[method];
523
+ if (((_a = this.options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && operationObject) {
524
+ const validateResult = this.validateAPI(method, path);
525
+ result[`${method.toUpperCase()} ${path}`] = {
526
+ operation: operationObject,
527
+ isValid: validateResult.isValid,
528
+ reason: validateResult.reason,
529
+ };
642
530
  }
643
531
  }
644
532
  }
533
+ this.apiMap = result;
645
534
  return result;
646
535
  }
647
- static validateSpec(spec, parser, isSwaggerFile, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2) {
648
- const errors = [];
649
- const warnings = [];
650
- if (isSwaggerFile) {
651
- warnings.push({
652
- type: WarningType.ConvertSwaggerToOpenAPI,
653
- content: ConstantString.ConvertSwaggerToOpenAPI,
536
+ validateSpecVersion() {
537
+ const result = { errors: [], warnings: [] };
538
+ if (this.spec.openapi >= "3.1.0") {
539
+ result.errors.push({
540
+ type: ErrorType.SpecVersionNotSupported,
541
+ content: Utils.format(ConstantString.SpecVersionNotSupported, this.spec.openapi),
542
+ data: this.spec.openapi,
654
543
  });
655
544
  }
656
- // Server validation
657
- const serverErrors = Utils.validateServer(spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2);
658
- errors.push(...serverErrors);
659
- // Remote reference not supported
660
- const refPaths = parser.$refs.paths();
661
- // refPaths [0] is the current spec file path
662
- if (refPaths.length > 1) {
663
- errors.push({
664
- type: ErrorType.RemoteRefNotSupported,
665
- content: Utils.format(ConstantString.RemoteRefNotSupported, refPaths.join(", ")),
666
- data: refPaths,
667
- });
668
- }
669
- // No supported API
670
- const apiMap = Utils.listSupportedAPIs(spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2);
671
- if (Object.keys(apiMap).length === 0) {
672
- errors.push({
545
+ return result;
546
+ }
547
+ validateSpecServer() {
548
+ const result = { errors: [], warnings: [] };
549
+ const serverErrors = Utils.validateServer(this.spec, this.options);
550
+ result.errors.push(...serverErrors);
551
+ return result;
552
+ }
553
+ validateSpecNoSupportAPI() {
554
+ const result = { errors: [], warnings: [] };
555
+ const apiMap = this.listAPIs();
556
+ const validAPIs = Object.entries(apiMap).filter(([, value]) => value.isValid);
557
+ if (validAPIs.length === 0) {
558
+ const data = [];
559
+ for (const key in apiMap) {
560
+ const { reason } = apiMap[key];
561
+ const apiInvalidReason = { api: key, reason: reason };
562
+ data.push(apiInvalidReason);
563
+ }
564
+ result.errors.push({
673
565
  type: ErrorType.NoSupportedApi,
674
566
  content: ConstantString.NoSupportedApi,
567
+ data,
675
568
  });
676
569
  }
570
+ return result;
571
+ }
572
+ validateSpecOperationId() {
573
+ const result = { errors: [], warnings: [] };
574
+ const apiMap = this.listAPIs();
677
575
  // OperationId missing
678
576
  const apisMissingOperationId = [];
679
577
  for (const key in apiMap) {
680
- const pathObjectItem = apiMap[key];
681
- if (!pathObjectItem.operationId) {
578
+ const { operation } = apiMap[key];
579
+ if (!operation.operationId) {
682
580
  apisMissingOperationId.push(key);
683
581
  }
684
582
  }
685
583
  if (apisMissingOperationId.length > 0) {
686
- warnings.push({
584
+ result.warnings.push({
687
585
  type: WarningType.OperationIdMissing,
688
586
  content: Utils.format(ConstantString.MissingOperationId, apisMissingOperationId.join(", ")),
689
587
  data: apisMissingOperationId,
690
588
  });
691
589
  }
692
- let status = ValidationStatus.Valid;
693
- if (warnings.length > 0 && errors.length === 0) {
694
- status = ValidationStatus.Warning;
590
+ return result;
591
+ }
592
+ validateMethodAndPath(method, path) {
593
+ const result = { isValid: true, reason: [] };
594
+ if (this.options.allowMethods && !this.options.allowMethods.includes(method)) {
595
+ result.isValid = false;
596
+ result.reason.push(ErrorType.MethodNotAllowed);
597
+ return result;
598
+ }
599
+ const pathObj = this.spec.paths[path];
600
+ if (!pathObj || !pathObj[method]) {
601
+ result.isValid = false;
602
+ result.reason.push(ErrorType.UrlPathNotExist);
603
+ return result;
604
+ }
605
+ return result;
606
+ }
607
+ validateResponse(method, path) {
608
+ const result = { isValid: true, reason: [] };
609
+ const operationObject = this.spec.paths[path][method];
610
+ const { json, multipleMediaType } = Utils.getResponseJson(operationObject);
611
+ // only support response body only contains “application/json” content type
612
+ if (multipleMediaType) {
613
+ result.reason.push(ErrorType.ResponseContainMultipleMediaTypes);
614
+ }
615
+ else if (Object.keys(json).length === 0) {
616
+ // response body should not be empty
617
+ result.reason.push(ErrorType.ResponseJsonIsEmpty);
695
618
  }
696
- else if (errors.length > 0) {
697
- status = ValidationStatus.Error;
619
+ return result;
620
+ }
621
+ validateServer(method, path) {
622
+ const result = { isValid: true, reason: [] };
623
+ const serverObj = Utils.getServerObject(this.spec, method, path);
624
+ if (!serverObj) {
625
+ // should contain server URL
626
+ result.reason.push(ErrorType.NoServerInformation);
627
+ }
628
+ else {
629
+ // server url should be absolute url with https protocol
630
+ const serverValidateResult = Utils.checkServerUrl([serverObj]);
631
+ result.reason.push(...serverValidateResult.map((item) => item.type));
632
+ }
633
+ return result;
634
+ }
635
+ validateAuth(method, path) {
636
+ const pathObj = this.spec.paths[path];
637
+ const operationObject = pathObj[method];
638
+ const securities = operationObject.security;
639
+ const authSchemeArray = Utils.getAuthArray(securities, this.spec);
640
+ if (authSchemeArray.length === 0) {
641
+ return { isValid: true, reason: [] };
642
+ }
643
+ if (this.options.allowAPIKeyAuth ||
644
+ this.options.allowOauth2 ||
645
+ this.options.allowBearerTokenAuth) {
646
+ // Currently we don't support multiple auth in one operation
647
+ if (authSchemeArray.length > 0 && authSchemeArray.every((auths) => auths.length > 1)) {
648
+ return {
649
+ isValid: false,
650
+ reason: [ErrorType.MultipleAuthNotSupported],
651
+ };
652
+ }
653
+ for (const auths of authSchemeArray) {
654
+ if (auths.length === 1) {
655
+ if ((this.options.allowAPIKeyAuth && Utils.isAPIKeyAuth(auths[0].authScheme)) ||
656
+ (this.options.allowOauth2 && Utils.isOAuthWithAuthCodeFlow(auths[0].authScheme)) ||
657
+ (this.options.allowBearerTokenAuth && Utils.isBearerTokenAuth(auths[0].authScheme))) {
658
+ return { isValid: true, reason: [] };
659
+ }
660
+ }
661
+ }
698
662
  }
699
- return {
700
- status,
701
- warnings,
702
- errors,
663
+ return { isValid: false, reason: [ErrorType.AuthTypeIsNotSupported] };
664
+ }
665
+ checkPostBodySchema(schema, isRequired = false) {
666
+ var _a;
667
+ const paramResult = {
668
+ requiredNum: 0,
669
+ optionalNum: 0,
670
+ isValid: true,
671
+ reason: [],
703
672
  };
673
+ if (Object.keys(schema).length === 0) {
674
+ return paramResult;
675
+ }
676
+ const isRequiredWithoutDefault = isRequired && schema.default === undefined;
677
+ const isCopilot = this.projectType === ProjectType.Copilot;
678
+ if (isCopilot && this.hasNestedObjectInSchema(schema)) {
679
+ paramResult.isValid = false;
680
+ paramResult.reason = [ErrorType.RequestBodyContainsNestedObject];
681
+ return paramResult;
682
+ }
683
+ if (schema.type === "string" ||
684
+ schema.type === "integer" ||
685
+ schema.type === "boolean" ||
686
+ schema.type === "number") {
687
+ if (isRequiredWithoutDefault) {
688
+ paramResult.requiredNum = paramResult.requiredNum + 1;
689
+ }
690
+ else {
691
+ paramResult.optionalNum = paramResult.optionalNum + 1;
692
+ }
693
+ }
694
+ else if (schema.type === "object") {
695
+ const { properties } = schema;
696
+ for (const property in properties) {
697
+ let isRequired = false;
698
+ if (schema.required && ((_a = schema.required) === null || _a === void 0 ? void 0 : _a.indexOf(property)) >= 0) {
699
+ isRequired = true;
700
+ }
701
+ const result = this.checkPostBodySchema(properties[property], isRequired);
702
+ paramResult.requiredNum += result.requiredNum;
703
+ paramResult.optionalNum += result.optionalNum;
704
+ paramResult.isValid = paramResult.isValid && result.isValid;
705
+ paramResult.reason.push(...result.reason);
706
+ }
707
+ }
708
+ else {
709
+ if (isRequiredWithoutDefault && !isCopilot) {
710
+ paramResult.isValid = false;
711
+ paramResult.reason.push(ErrorType.PostBodyContainsRequiredUnsupportedSchema);
712
+ }
713
+ }
714
+ return paramResult;
704
715
  }
705
- static format(str, ...args) {
706
- let index = 0;
707
- return str.replace(/%s/g, () => {
708
- const arg = args[index++];
709
- return arg !== undefined ? arg : "";
710
- });
716
+ checkParamSchema(paramObject) {
717
+ const paramResult = {
718
+ requiredNum: 0,
719
+ optionalNum: 0,
720
+ isValid: true,
721
+ reason: [],
722
+ };
723
+ if (!paramObject) {
724
+ return paramResult;
725
+ }
726
+ const isCopilot = this.projectType === ProjectType.Copilot;
727
+ for (let i = 0; i < paramObject.length; i++) {
728
+ const param = paramObject[i];
729
+ const schema = param.schema;
730
+ if (isCopilot && this.hasNestedObjectInSchema(schema)) {
731
+ paramResult.isValid = false;
732
+ paramResult.reason.push(ErrorType.ParamsContainsNestedObject);
733
+ continue;
734
+ }
735
+ const isRequiredWithoutDefault = param.required && schema.default === undefined;
736
+ if (isCopilot) {
737
+ if (isRequiredWithoutDefault) {
738
+ paramResult.requiredNum = paramResult.requiredNum + 1;
739
+ }
740
+ else {
741
+ paramResult.optionalNum = paramResult.optionalNum + 1;
742
+ }
743
+ continue;
744
+ }
745
+ if (param.in === "header" || param.in === "cookie") {
746
+ if (isRequiredWithoutDefault) {
747
+ paramResult.isValid = false;
748
+ paramResult.reason.push(ErrorType.ParamsContainRequiredUnsupportedSchema);
749
+ }
750
+ continue;
751
+ }
752
+ if (schema.type !== "boolean" &&
753
+ schema.type !== "string" &&
754
+ schema.type !== "number" &&
755
+ schema.type !== "integer") {
756
+ if (isRequiredWithoutDefault) {
757
+ paramResult.isValid = false;
758
+ paramResult.reason.push(ErrorType.ParamsContainRequiredUnsupportedSchema);
759
+ }
760
+ continue;
761
+ }
762
+ if (param.in === "query" || param.in === "path") {
763
+ if (isRequiredWithoutDefault) {
764
+ paramResult.requiredNum = paramResult.requiredNum + 1;
765
+ }
766
+ else {
767
+ paramResult.optionalNum = paramResult.optionalNum + 1;
768
+ }
769
+ }
770
+ }
771
+ return paramResult;
711
772
  }
712
- static getSafeRegistrationIdEnvName(authName) {
713
- if (!authName) {
714
- return "";
773
+ hasNestedObjectInSchema(schema) {
774
+ if (schema.type === "object") {
775
+ for (const property in schema.properties) {
776
+ const nestedSchema = schema.properties[property];
777
+ if (nestedSchema.type === "object") {
778
+ return true;
779
+ }
780
+ }
715
781
  }
716
- let safeRegistrationIdEnvName = authName.toUpperCase().replace(/[^A-Z0-9_]/g, "_");
717
- if (!safeRegistrationIdEnvName.match(/^[A-Z]/)) {
718
- safeRegistrationIdEnvName = "PREFIX_" + safeRegistrationIdEnvName;
782
+ return false;
783
+ }
784
+ }
785
+
786
+ // Copyright (c) Microsoft Corporation.
787
+ class CopilotValidator extends Validator {
788
+ constructor(spec, options) {
789
+ super();
790
+ this.projectType = ProjectType.Copilot;
791
+ this.options = options;
792
+ this.spec = spec;
793
+ }
794
+ validateSpec() {
795
+ const result = { errors: [], warnings: [] };
796
+ // validate spec version
797
+ let validationResult = this.validateSpecVersion();
798
+ result.errors.push(...validationResult.errors);
799
+ // validate spec server
800
+ validationResult = this.validateSpecServer();
801
+ result.errors.push(...validationResult.errors);
802
+ // validate no supported API
803
+ validationResult = this.validateSpecNoSupportAPI();
804
+ result.errors.push(...validationResult.errors);
805
+ // validate operationId missing
806
+ validationResult = this.validateSpecOperationId();
807
+ result.warnings.push(...validationResult.warnings);
808
+ return result;
809
+ }
810
+ validateAPI(method, path) {
811
+ const result = { isValid: true, reason: [] };
812
+ method = method.toLocaleLowerCase();
813
+ // validate method and path
814
+ const methodAndPathResult = this.validateMethodAndPath(method, path);
815
+ if (!methodAndPathResult.isValid) {
816
+ return methodAndPathResult;
817
+ }
818
+ const operationObject = this.spec.paths[path][method];
819
+ // validate auth
820
+ const authCheckResult = this.validateAuth(method, path);
821
+ result.reason.push(...authCheckResult.reason);
822
+ // validate operationId
823
+ if (!this.options.allowMissingId && !operationObject.operationId) {
824
+ result.reason.push(ErrorType.MissingOperationId);
825
+ }
826
+ // validate server
827
+ const validateServerResult = this.validateServer(method, path);
828
+ result.reason.push(...validateServerResult.reason);
829
+ // validate response
830
+ const validateResponseResult = this.validateResponse(method, path);
831
+ result.reason.push(...validateResponseResult.reason);
832
+ // validate requestBody
833
+ const requestBody = operationObject.requestBody;
834
+ const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
835
+ if (Utils.containMultipleMediaTypes(requestBody)) {
836
+ result.reason.push(ErrorType.PostBodyContainMultipleMediaTypes);
837
+ }
838
+ if (requestJsonBody) {
839
+ const requestBodySchema = requestJsonBody.schema;
840
+ if (requestBodySchema.type !== "object") {
841
+ result.reason.push(ErrorType.PostBodySchemaIsNotJson);
842
+ }
843
+ const requestBodyParamResult = this.checkPostBodySchema(requestBodySchema, requestBody.required);
844
+ result.reason.push(...requestBodyParamResult.reason);
845
+ }
846
+ // validate parameters
847
+ const paramObject = operationObject.parameters;
848
+ const paramResult = this.checkParamSchema(paramObject);
849
+ result.reason.push(...paramResult.reason);
850
+ if (result.reason.length > 0) {
851
+ result.isValid = false;
852
+ }
853
+ return result;
854
+ }
855
+ }
856
+
857
+ // Copyright (c) Microsoft Corporation.
858
+ class SMEValidator extends Validator {
859
+ constructor(spec, options) {
860
+ super();
861
+ this.projectType = ProjectType.SME;
862
+ this.options = options;
863
+ this.spec = spec;
864
+ }
865
+ validateSpec() {
866
+ const result = { errors: [], warnings: [] };
867
+ // validate spec version
868
+ let validationResult = this.validateSpecVersion();
869
+ result.errors.push(...validationResult.errors);
870
+ // validate spec server
871
+ validationResult = this.validateSpecServer();
872
+ result.errors.push(...validationResult.errors);
873
+ // validate no supported API
874
+ validationResult = this.validateSpecNoSupportAPI();
875
+ result.errors.push(...validationResult.errors);
876
+ // validate operationId missing
877
+ if (this.options.allowMissingId) {
878
+ validationResult = this.validateSpecOperationId();
879
+ result.warnings.push(...validationResult.warnings);
880
+ }
881
+ return result;
882
+ }
883
+ validateAPI(method, path) {
884
+ const result = { isValid: true, reason: [] };
885
+ method = method.toLocaleLowerCase();
886
+ // validate method and path
887
+ const methodAndPathResult = this.validateMethodAndPath(method, path);
888
+ if (!methodAndPathResult.isValid) {
889
+ return methodAndPathResult;
890
+ }
891
+ const operationObject = this.spec.paths[path][method];
892
+ // validate auth
893
+ const authCheckResult = this.validateAuth(method, path);
894
+ result.reason.push(...authCheckResult.reason);
895
+ // validate operationId
896
+ if (!this.options.allowMissingId && !operationObject.operationId) {
897
+ result.reason.push(ErrorType.MissingOperationId);
898
+ }
899
+ // validate server
900
+ const validateServerResult = this.validateServer(method, path);
901
+ result.reason.push(...validateServerResult.reason);
902
+ // validate response
903
+ const validateResponseResult = this.validateResponse(method, path);
904
+ result.reason.push(...validateResponseResult.reason);
905
+ let postBodyResult = {
906
+ requiredNum: 0,
907
+ optionalNum: 0,
908
+ isValid: true,
909
+ reason: [],
910
+ };
911
+ // validate requestBody
912
+ const requestBody = operationObject.requestBody;
913
+ const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
914
+ if (Utils.containMultipleMediaTypes(requestBody)) {
915
+ result.reason.push(ErrorType.PostBodyContainMultipleMediaTypes);
916
+ }
917
+ if (requestJsonBody) {
918
+ const requestBodySchema = requestJsonBody.schema;
919
+ postBodyResult = this.checkPostBodySchema(requestBodySchema, requestBody.required);
920
+ result.reason.push(...postBodyResult.reason);
921
+ }
922
+ // validate parameters
923
+ const paramObject = operationObject.parameters;
924
+ const paramResult = this.checkParamSchema(paramObject);
925
+ result.reason.push(...paramResult.reason);
926
+ // validate total parameters count
927
+ if (paramResult.isValid && postBodyResult.isValid) {
928
+ const paramCountResult = this.validateParamCount(postBodyResult, paramResult);
929
+ result.reason.push(...paramCountResult.reason);
930
+ }
931
+ if (result.reason.length > 0) {
932
+ result.isValid = false;
933
+ }
934
+ return result;
935
+ }
936
+ validateParamCount(postBodyResult, paramResult) {
937
+ const result = { isValid: true, reason: [] };
938
+ const totalRequiredParams = postBodyResult.requiredNum + paramResult.requiredNum;
939
+ const totalParams = totalRequiredParams + postBodyResult.optionalNum + paramResult.optionalNum;
940
+ if (totalRequiredParams > 1) {
941
+ if (!this.options.allowMultipleParameters ||
942
+ totalRequiredParams > SMEValidator.SMERequiredParamsMaxNum) {
943
+ result.reason.push(ErrorType.ExceededRequiredParamsLimit);
944
+ }
945
+ }
946
+ else if (totalParams === 0) {
947
+ result.reason.push(ErrorType.NoParameter);
948
+ }
949
+ return result;
950
+ }
951
+ }
952
+ SMEValidator.SMERequiredParamsMaxNum = 5;
953
+
954
+ // Copyright (c) Microsoft Corporation.
955
+ class TeamsAIValidator extends Validator {
956
+ constructor(spec, options) {
957
+ super();
958
+ this.projectType = ProjectType.TeamsAi;
959
+ this.options = options;
960
+ this.spec = spec;
961
+ }
962
+ validateSpec() {
963
+ const result = { errors: [], warnings: [] };
964
+ // validate spec server
965
+ let validationResult = this.validateSpecServer();
966
+ result.errors.push(...validationResult.errors);
967
+ // validate no supported API
968
+ validationResult = this.validateSpecNoSupportAPI();
969
+ result.errors.push(...validationResult.errors);
970
+ return result;
971
+ }
972
+ validateAPI(method, path) {
973
+ const result = { isValid: true, reason: [] };
974
+ method = method.toLocaleLowerCase();
975
+ // validate method and path
976
+ const methodAndPathResult = this.validateMethodAndPath(method, path);
977
+ if (!methodAndPathResult.isValid) {
978
+ return methodAndPathResult;
979
+ }
980
+ const operationObject = this.spec.paths[path][method];
981
+ // validate operationId
982
+ if (!this.options.allowMissingId && !operationObject.operationId) {
983
+ result.reason.push(ErrorType.MissingOperationId);
984
+ }
985
+ // validate server
986
+ const validateServerResult = this.validateServer(method, path);
987
+ result.reason.push(...validateServerResult.reason);
988
+ if (result.reason.length > 0) {
989
+ result.isValid = false;
990
+ }
991
+ return result;
992
+ }
993
+ }
994
+
995
+ class ValidatorFactory {
996
+ static create(spec, options) {
997
+ var _a;
998
+ const type = (_a = options.projectType) !== null && _a !== void 0 ? _a : ProjectType.SME;
999
+ switch (type) {
1000
+ case ProjectType.SME:
1001
+ return new SMEValidator(spec, options);
1002
+ case ProjectType.Copilot:
1003
+ return new CopilotValidator(spec, options);
1004
+ case ProjectType.TeamsAi:
1005
+ return new TeamsAIValidator(spec, options);
1006
+ default:
1007
+ throw new Error(`Invalid project type: ${type}`);
719
1008
  }
720
- return safeRegistrationIdEnvName;
721
1009
  }
722
1010
  }
723
1011
 
724
1012
  // Copyright (c) Microsoft Corporation.
725
1013
  class SpecFilter {
726
- static specFilter(filter, unResolveSpec, resolvedSpec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2) {
1014
+ static specFilter(filter, unResolveSpec, resolvedSpec, options) {
1015
+ var _a;
727
1016
  try {
728
1017
  const newSpec = Object.assign({}, unResolveSpec);
729
1018
  const newPaths = {};
730
1019
  for (const filterItem of filter) {
731
1020
  const [method, path] = filterItem.split(" ");
732
1021
  const methodName = method.toLowerCase();
733
- if (!Utils.isSupportedApi(methodName, path, resolvedSpec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2)) {
734
- continue;
735
- }
736
- if (!newPaths[path]) {
737
- newPaths[path] = Object.assign({}, unResolveSpec.paths[path]);
738
- for (const m of ConstantString.AllOperationMethods) {
739
- delete newPaths[path][m];
1022
+ const pathObj = (_a = resolvedSpec.paths) === null || _a === void 0 ? void 0 : _a[path];
1023
+ if (ConstantString.AllOperationMethods.includes(methodName) &&
1024
+ pathObj &&
1025
+ pathObj[methodName]) {
1026
+ const validator = ValidatorFactory.create(resolvedSpec, options);
1027
+ const validateResult = validator.validateAPI(methodName, path);
1028
+ if (!validateResult.isValid) {
1029
+ continue;
1030
+ }
1031
+ if (!newPaths[path]) {
1032
+ newPaths[path] = Object.assign({}, unResolveSpec.paths[path]);
1033
+ for (const m of ConstantString.AllOperationMethods) {
1034
+ delete newPaths[path][m];
1035
+ }
1036
+ }
1037
+ newPaths[path][methodName] = unResolveSpec.paths[path][methodName];
1038
+ // Add the operationId if missing
1039
+ if (!newPaths[path][methodName].operationId) {
1040
+ newPaths[path][methodName].operationId = `${methodName}${Utils.convertPathToCamelCase(path)}`;
740
1041
  }
741
- }
742
- newPaths[path][methodName] = unResolveSpec.paths[path][methodName];
743
- // Add the operationId if missing
744
- if (!newPaths[path][methodName].operationId) {
745
- newPaths[path][methodName].operationId = `${methodName}${Utils.convertPathToCamelCase(path)}`;
746
1042
  }
747
1043
  }
748
1044
  newSpec.paths = newPaths;
@@ -756,46 +1052,198 @@ class SpecFilter {
756
1052
 
757
1053
  // Copyright (c) Microsoft Corporation.
758
1054
  class ManifestUpdater {
759
- static async updateManifest(manifestPath, outputSpecPath, adaptiveCardFolder, spec, allowMultipleParameters, auth) {
1055
+ static async updateManifestWithAiPlugin(manifestPath, outputSpecPath, apiPluginFilePath, spec, options) {
1056
+ const manifest = await fs.readJSON(manifestPath);
1057
+ const apiPluginRelativePath = ManifestUpdater.getRelativePath(manifestPath, apiPluginFilePath);
1058
+ manifest.plugins = [
1059
+ {
1060
+ file: apiPluginRelativePath,
1061
+ id: ConstantString.DefaultPluginId,
1062
+ },
1063
+ ];
1064
+ const appName = this.removeEnvs(manifest.name.short);
1065
+ ManifestUpdater.updateManifestDescription(manifest, spec);
1066
+ const specRelativePath = ManifestUpdater.getRelativePath(manifestPath, outputSpecPath);
1067
+ const apiPlugin = await ManifestUpdater.generatePluginManifestSchema(spec, specRelativePath, apiPluginFilePath, appName, options);
1068
+ return [manifest, apiPlugin];
1069
+ }
1070
+ static updateManifestDescription(manifest, spec) {
760
1071
  var _a, _b;
1072
+ manifest.description = {
1073
+ short: spec.info.title.slice(0, ConstantString.ShortDescriptionMaxLens),
1074
+ 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),
1075
+ };
1076
+ }
1077
+ static mapOpenAPISchemaToFuncParam(schema, method, pathUrl) {
1078
+ let parameter;
1079
+ if (schema.type === "string" ||
1080
+ schema.type === "boolean" ||
1081
+ schema.type === "integer" ||
1082
+ schema.type === "number" ||
1083
+ schema.type === "array") {
1084
+ parameter = schema;
1085
+ }
1086
+ else {
1087
+ throw new SpecParserError(Utils.format(ConstantString.UnsupportedSchema, method, pathUrl, JSON.stringify(schema)), ErrorType.UpdateManifestFailed);
1088
+ }
1089
+ return parameter;
1090
+ }
1091
+ static async generatePluginManifestSchema(spec, specRelativePath, apiPluginFilePath, appName, options) {
1092
+ var _a, _b, _c, _d;
1093
+ const functions = [];
1094
+ const functionNames = [];
1095
+ const paths = spec.paths;
1096
+ for (const pathUrl in paths) {
1097
+ const pathItem = paths[pathUrl];
1098
+ if (pathItem) {
1099
+ const operations = pathItem;
1100
+ for (const method in operations) {
1101
+ if (options.allowMethods.includes(method)) {
1102
+ const operationItem = operations[method];
1103
+ if (operationItem) {
1104
+ const operationId = operationItem.operationId;
1105
+ const description = (_a = operationItem.description) !== null && _a !== void 0 ? _a : "";
1106
+ const paramObject = operationItem.parameters;
1107
+ const requestBody = operationItem.requestBody;
1108
+ const parameters = {
1109
+ type: "object",
1110
+ properties: {},
1111
+ required: [],
1112
+ };
1113
+ if (paramObject) {
1114
+ for (let i = 0; i < paramObject.length; i++) {
1115
+ const param = paramObject[i];
1116
+ const schema = param.schema;
1117
+ parameters.properties[param.name] = ManifestUpdater.mapOpenAPISchemaToFuncParam(schema, method, pathUrl);
1118
+ if (param.required) {
1119
+ parameters.required.push(param.name);
1120
+ }
1121
+ if (!parameters.properties[param.name].description) {
1122
+ parameters.properties[param.name].description = (_b = param.description) !== null && _b !== void 0 ? _b : "";
1123
+ }
1124
+ }
1125
+ }
1126
+ if (requestBody) {
1127
+ const requestJsonBody = requestBody.content["application/json"];
1128
+ const requestBodySchema = requestJsonBody.schema;
1129
+ if (requestBodySchema.type === "object") {
1130
+ if (requestBodySchema.required) {
1131
+ parameters.required.push(...requestBodySchema.required);
1132
+ }
1133
+ for (const property in requestBodySchema.properties) {
1134
+ const schema = requestBodySchema.properties[property];
1135
+ parameters.properties[property] = ManifestUpdater.mapOpenAPISchemaToFuncParam(schema, method, pathUrl);
1136
+ }
1137
+ }
1138
+ else {
1139
+ throw new SpecParserError(Utils.format(ConstantString.UnsupportedSchema, method, pathUrl, JSON.stringify(requestBodySchema)), ErrorType.UpdateManifestFailed);
1140
+ }
1141
+ }
1142
+ const funcObj = {
1143
+ name: operationId,
1144
+ description: description,
1145
+ parameters: parameters,
1146
+ };
1147
+ functions.push(funcObj);
1148
+ functionNames.push(operationId);
1149
+ }
1150
+ }
1151
+ }
1152
+ }
1153
+ }
1154
+ let apiPlugin;
1155
+ if (await fs.pathExists(apiPluginFilePath)) {
1156
+ apiPlugin = await fs.readJSON(apiPluginFilePath);
1157
+ }
1158
+ else {
1159
+ apiPlugin = {
1160
+ schema_version: "v2",
1161
+ name_for_human: "",
1162
+ description_for_human: "",
1163
+ functions: [],
1164
+ runtimes: [],
1165
+ };
1166
+ }
1167
+ apiPlugin.functions = apiPlugin.functions || [];
1168
+ for (const func of functions) {
1169
+ const index = (_c = apiPlugin.functions) === null || _c === void 0 ? void 0 : _c.findIndex((f) => f.name === func.name);
1170
+ if (index === -1) {
1171
+ apiPlugin.functions.push(func);
1172
+ }
1173
+ else {
1174
+ apiPlugin.functions[index] = func;
1175
+ }
1176
+ }
1177
+ apiPlugin.runtimes = apiPlugin.runtimes || [];
1178
+ const index = apiPlugin.runtimes.findIndex((runtime) => runtime.spec.url === specRelativePath);
1179
+ if (index === -1) {
1180
+ apiPlugin.runtimes.push({
1181
+ type: "OpenApi",
1182
+ auth: {
1183
+ type: "none",
1184
+ },
1185
+ spec: {
1186
+ url: specRelativePath,
1187
+ },
1188
+ run_for_functions: functionNames,
1189
+ });
1190
+ }
1191
+ else {
1192
+ apiPlugin.runtimes[index].run_for_functions = functionNames;
1193
+ }
1194
+ if (!apiPlugin.name_for_human) {
1195
+ apiPlugin.name_for_human = appName;
1196
+ }
1197
+ if (!apiPlugin.description_for_human) {
1198
+ apiPlugin.description_for_human =
1199
+ (_d = spec.info.description) !== null && _d !== void 0 ? _d : "<Please add description of the plugin>";
1200
+ }
1201
+ return apiPlugin;
1202
+ }
1203
+ static async updateManifest(manifestPath, outputSpecPath, spec, options, adaptiveCardFolder, authInfo) {
761
1204
  try {
762
1205
  const originalManifest = await fs.readJSON(manifestPath);
763
1206
  const updatedPart = {};
764
- const [commands, warnings] = await ManifestUpdater.generateCommands(spec, adaptiveCardFolder, manifestPath, allowMultipleParameters);
765
- const composeExtension = {
766
- composeExtensionType: "apiBased",
767
- apiSpecificationFile: ManifestUpdater.getRelativePath(manifestPath, outputSpecPath),
768
- commands: commands,
769
- };
770
- if (auth) {
771
- if (Utils.isAPIKeyAuth(auth)) {
772
- auth = auth;
773
- const safeApiSecretRegistrationId = Utils.getSafeRegistrationIdEnvName(`${auth.name}_${ConstantString.RegistrationIdPostfix}`);
774
- composeExtension.authorization = {
775
- authType: "apiSecretServiceAuth",
776
- apiSecretServiceAuthConfiguration: {
777
- apiSecretRegistrationId: `\${{${safeApiSecretRegistrationId}}}`,
778
- },
779
- };
780
- }
781
- else if (Utils.isBearerTokenAuth(auth)) {
782
- composeExtension.authorization = {
783
- authType: "microsoftEntra",
784
- microsoftEntraConfiguration: {
785
- supportsSingleSignOn: true,
786
- },
787
- };
788
- updatedPart.webApplicationInfo = {
789
- id: "${{AAD_APP_CLIENT_ID}}",
790
- resource: "api://${{DOMAIN}}/${{AAD_APP_CLIENT_ID}}",
791
- };
1207
+ updatedPart.composeExtensions = [];
1208
+ let warnings = [];
1209
+ if (options.projectType === ProjectType.SME) {
1210
+ const updateResult = await ManifestUpdater.generateCommands(spec, manifestPath, options, adaptiveCardFolder);
1211
+ const commands = updateResult[0];
1212
+ warnings = updateResult[1];
1213
+ const composeExtension = {
1214
+ composeExtensionType: "apiBased",
1215
+ apiSpecificationFile: ManifestUpdater.getRelativePath(manifestPath, outputSpecPath),
1216
+ commands: commands,
1217
+ };
1218
+ if (authInfo) {
1219
+ const auth = authInfo.authScheme;
1220
+ if (Utils.isAPIKeyAuth(auth) || Utils.isBearerTokenAuth(auth)) {
1221
+ const safeApiSecretRegistrationId = Utils.getSafeRegistrationIdEnvName(`${authInfo.name}_${ConstantString.RegistrationIdPostfix}`);
1222
+ composeExtension.authorization = {
1223
+ authType: "apiSecretServiceAuth",
1224
+ apiSecretServiceAuthConfiguration: {
1225
+ apiSecretRegistrationId: `\${{${safeApiSecretRegistrationId}}}`,
1226
+ },
1227
+ };
1228
+ }
1229
+ else if (Utils.isOAuthWithAuthCodeFlow(auth)) {
1230
+ const safeOAuth2RegistrationId = Utils.getSafeRegistrationIdEnvName(`${authInfo.name}_${ConstantString.OAuthRegistrationIdPostFix}`);
1231
+ composeExtension.authorization = {
1232
+ authType: "oAuth2.0",
1233
+ oAuthConfiguration: {
1234
+ oauthConfigurationId: `\${{${safeOAuth2RegistrationId}}}`,
1235
+ },
1236
+ };
1237
+ updatedPart.webApplicationInfo = {
1238
+ id: "${{AAD_APP_CLIENT_ID}}",
1239
+ resource: "api://${{DOMAIN}}/${{AAD_APP_CLIENT_ID}}",
1240
+ };
1241
+ }
792
1242
  }
1243
+ updatedPart.composeExtensions = [composeExtension];
793
1244
  }
794
- updatedPart.description = {
795
- short: spec.info.title.slice(0, ConstantString.ShortDescriptionMaxLens),
796
- 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),
797
- };
798
- updatedPart.composeExtensions = [composeExtension];
1245
+ updatedPart.description = originalManifest.description;
1246
+ ManifestUpdater.updateManifestDescription(updatedPart, spec);
799
1247
  const updatedManifest = Object.assign(Object.assign({}, originalManifest), updatedPart);
800
1248
  return [updatedManifest, warnings];
801
1249
  }
@@ -803,7 +1251,8 @@ class ManifestUpdater {
803
1251
  throw new SpecParserError(err.toString(), ErrorType.UpdateManifestFailed);
804
1252
  }
805
1253
  }
806
- static async generateCommands(spec, adaptiveCardFolder, manifestPath, allowMultipleParameters) {
1254
+ static async generateCommands(spec, manifestPath, options, adaptiveCardFolder) {
1255
+ var _a;
807
1256
  const paths = spec.paths;
808
1257
  const commands = [];
809
1258
  const warnings = [];
@@ -814,16 +1263,28 @@ class ManifestUpdater {
814
1263
  const operations = pathItem;
815
1264
  // Currently only support GET and POST method
816
1265
  for (const method in operations) {
817
- if (method === ConstantString.PostMethod || method === ConstantString.GetMethod) {
1266
+ if ((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) {
818
1267
  const operationItem = operations[method];
819
1268
  if (operationItem) {
820
- const [command, warning] = Utils.parseApiInfo(operationItem, allowMultipleParameters);
821
- const adaptiveCardPath = path.join(adaptiveCardFolder, command.id + ".json");
822
- command.apiResponseRenderingTemplateFile = (await fs.pathExists(adaptiveCardPath))
823
- ? ManifestUpdater.getRelativePath(manifestPath, adaptiveCardPath)
824
- : "";
825
- if (warning) {
826
- warnings.push(warning);
1269
+ const command = Utils.parseApiInfo(operationItem, options);
1270
+ if (command.parameters &&
1271
+ command.parameters.length >= 1 &&
1272
+ command.parameters.some((param) => param.isRequired)) {
1273
+ command.parameters = command.parameters.filter((param) => param.isRequired);
1274
+ }
1275
+ else if (command.parameters && command.parameters.length > 0) {
1276
+ command.parameters = [command.parameters[0]];
1277
+ warnings.push({
1278
+ type: WarningType.OperationOnlyContainsOptionalParam,
1279
+ content: Utils.format(ConstantString.OperationOnlyContainsOptionalParam, command.id),
1280
+ data: command.id,
1281
+ });
1282
+ }
1283
+ if (adaptiveCardFolder) {
1284
+ const adaptiveCardPath = path.join(adaptiveCardFolder, command.id + ".json");
1285
+ command.apiResponseRenderingTemplateFile = (await fs.pathExists(adaptiveCardPath))
1286
+ ? ManifestUpdater.getRelativePath(manifestPath, adaptiveCardPath)
1287
+ : "";
827
1288
  }
828
1289
  commands.push(command);
829
1290
  }
@@ -838,13 +1299,22 @@ class ManifestUpdater {
838
1299
  const relativePath = path.relative(path.dirname(from), to);
839
1300
  return path.normalize(relativePath).replace(/\\/g, "/");
840
1301
  }
1302
+ static removeEnvs(str) {
1303
+ const placeHolderReg = /\${{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*}}/g;
1304
+ const matches = placeHolderReg.exec(str);
1305
+ let newStr = str;
1306
+ if (matches != null) {
1307
+ newStr = newStr.replace(matches[0], "");
1308
+ }
1309
+ return newStr;
1310
+ }
841
1311
  }
842
1312
 
843
1313
  // Copyright (c) Microsoft Corporation.
844
1314
  class AdaptiveCardGenerator {
845
1315
  static generateAdaptiveCard(operationItem) {
846
1316
  try {
847
- const json = Utils.getResponseJson(operationItem);
1317
+ const { json } = Utils.getResponseJson(operationItem);
848
1318
  let cardBody = [];
849
1319
  let schema = json.schema;
850
1320
  let jsonPath = "$";
@@ -1104,8 +1574,11 @@ class SpecParser {
1104
1574
  allowMissingId: true,
1105
1575
  allowSwagger: true,
1106
1576
  allowAPIKeyAuth: false,
1577
+ allowBearerTokenAuth: false,
1107
1578
  allowMultipleParameters: false,
1108
1579
  allowOauth2: false,
1580
+ allowMethods: ["get", "post"],
1581
+ projectType: ProjectType.SME,
1109
1582
  };
1110
1583
  this.pathOrSpec = pathOrDoc;
1111
1584
  this.parser = new SwaggerParser();
@@ -1129,6 +1602,8 @@ class SpecParser {
1129
1602
  errors: [{ type: ErrorType.SpecNotValid, content: e.toString() }],
1130
1603
  };
1131
1604
  }
1605
+ const errors = [];
1606
+ const warnings = [];
1132
1607
  if (!this.options.allowSwagger && this.isSwaggerFile) {
1133
1608
  return {
1134
1609
  status: ValidationStatus.Error,
@@ -1138,7 +1613,38 @@ class SpecParser {
1138
1613
  ],
1139
1614
  };
1140
1615
  }
1141
- return Utils.validateSpec(this.spec, this.parser, !!this.isSwaggerFile, this.options.allowMissingId, this.options.allowAPIKeyAuth, this.options.allowMultipleParameters, this.options.allowOauth2);
1616
+ // Remote reference not supported
1617
+ const refPaths = this.parser.$refs.paths();
1618
+ // refPaths [0] is the current spec file path
1619
+ if (refPaths.length > 1) {
1620
+ errors.push({
1621
+ type: ErrorType.RemoteRefNotSupported,
1622
+ content: Utils.format(ConstantString.RemoteRefNotSupported, refPaths.join(", ")),
1623
+ data: refPaths,
1624
+ });
1625
+ }
1626
+ if (!!this.isSwaggerFile && this.options.allowSwagger) {
1627
+ warnings.push({
1628
+ type: WarningType.ConvertSwaggerToOpenAPI,
1629
+ content: ConstantString.ConvertSwaggerToOpenAPI,
1630
+ });
1631
+ }
1632
+ const validator = this.getValidator(this.spec);
1633
+ const validationResult = validator.validateSpec();
1634
+ warnings.push(...validationResult.warnings);
1635
+ errors.push(...validationResult.errors);
1636
+ let status = ValidationStatus.Valid;
1637
+ if (warnings.length > 0 && errors.length === 0) {
1638
+ status = ValidationStatus.Warning;
1639
+ }
1640
+ else if (errors.length > 0) {
1641
+ status = ValidationStatus.Error;
1642
+ }
1643
+ return {
1644
+ status: status,
1645
+ warnings: warnings,
1646
+ errors: errors,
1647
+ };
1142
1648
  }
1143
1649
  catch (err) {
1144
1650
  throw new SpecParserError(err.toString(), ErrorType.ValidateFailed);
@@ -1158,39 +1664,40 @@ class SpecParser {
1158
1664
  try {
1159
1665
  await this.loadSpec();
1160
1666
  const spec = this.spec;
1161
- const apiMap = this.getAllSupportedAPIs(spec);
1162
- const result = [];
1667
+ const apiMap = this.getAPIs(spec);
1668
+ const result = {
1669
+ APIs: [],
1670
+ allAPICount: 0,
1671
+ validAPICount: 0,
1672
+ };
1163
1673
  for (const apiKey in apiMap) {
1674
+ const { operation, isValid, reason } = apiMap[apiKey];
1675
+ const [method, path] = apiKey.split(" ");
1676
+ const operationId = (_a = operation.operationId) !== null && _a !== void 0 ? _a : `${method.toLowerCase()}${Utils.convertPathToCamelCase(path)}`;
1164
1677
  const apiResult = {
1165
- api: "",
1678
+ api: apiKey,
1166
1679
  server: "",
1167
- operationId: "",
1680
+ operationId: operationId,
1681
+ isValid: isValid,
1682
+ reason: reason,
1168
1683
  };
1169
- const [method, path] = apiKey.split(" ");
1170
- const operation = apiMap[apiKey];
1171
- const rootServer = spec.servers && spec.servers[0];
1172
- const methodServer = spec.paths[path].servers && ((_a = spec.paths[path]) === null || _a === void 0 ? void 0 : _a.servers[0]);
1173
- const operationServer = operation.servers && operation.servers[0];
1174
- const serverUrl = operationServer || methodServer || rootServer;
1175
- if (!serverUrl) {
1176
- throw new SpecParserError(ConstantString.NoServerInformation, ErrorType.NoServerInformation);
1177
- }
1178
- apiResult.server = Utils.resolveServerUrl(serverUrl.url);
1179
- let operationId = operation.operationId;
1180
- if (!operationId) {
1181
- operationId = `${method.toLowerCase()}${Utils.convertPathToCamelCase(path)}`;
1182
- }
1183
- apiResult.operationId = operationId;
1184
- const authArray = Utils.getAuthArray(operation.security, spec);
1185
- for (const auths of authArray) {
1186
- if (auths.length === 1) {
1187
- apiResult.auth = auths[0].authSchema;
1188
- break;
1684
+ if (isValid) {
1685
+ const serverObj = Utils.getServerObject(spec, method.toLocaleLowerCase(), path);
1686
+ if (serverObj) {
1687
+ apiResult.server = Utils.resolveEnv(serverObj.url);
1688
+ }
1689
+ const authArray = Utils.getAuthArray(operation.security, spec);
1690
+ for (const auths of authArray) {
1691
+ if (auths.length === 1) {
1692
+ apiResult.auth = auths[0];
1693
+ break;
1694
+ }
1189
1695
  }
1190
1696
  }
1191
- apiResult.api = apiKey;
1192
- result.push(apiResult);
1697
+ result.APIs.push(apiResult);
1193
1698
  }
1699
+ result.allAPICount = result.APIs.length;
1700
+ result.validAPICount = result.APIs.filter((api) => api.isValid).length;
1194
1701
  return result;
1195
1702
  }
1196
1703
  catch (err) {
@@ -1200,48 +1707,108 @@ class SpecParser {
1200
1707
  throw new SpecParserError(err.toString(), ErrorType.ListFailed);
1201
1708
  }
1202
1709
  }
1710
+ /**
1711
+ * Generate specs according to the filters.
1712
+ * @param filter An array of strings that represent the filters to apply when generating the artifacts. If filter is empty, it would process nothing.
1713
+ */
1714
+ async getFilteredSpecs(filter, signal) {
1715
+ try {
1716
+ if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
1717
+ throw new SpecParserError(ConstantString.CancelledMessage, ErrorType.Cancelled);
1718
+ }
1719
+ await this.loadSpec();
1720
+ if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
1721
+ throw new SpecParserError(ConstantString.CancelledMessage, ErrorType.Cancelled);
1722
+ }
1723
+ const newUnResolvedSpec = SpecFilter.specFilter(filter, this.unResolveSpec, this.spec, this.options);
1724
+ if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
1725
+ throw new SpecParserError(ConstantString.CancelledMessage, ErrorType.Cancelled);
1726
+ }
1727
+ const newSpec = (await this.parser.dereference(newUnResolvedSpec));
1728
+ return [newUnResolvedSpec, newSpec];
1729
+ }
1730
+ catch (err) {
1731
+ if (err instanceof SpecParserError) {
1732
+ throw err;
1733
+ }
1734
+ throw new SpecParserError(err.toString(), ErrorType.GetSpecFailed);
1735
+ }
1736
+ }
1203
1737
  /**
1204
1738
  * Generates and update artifacts from the OpenAPI specification file. Generate Adaptive Cards, update Teams app manifest, and generate a new OpenAPI specification file.
1205
1739
  * @param manifestPath A file path of the Teams app manifest file to update.
1206
1740
  * @param filter An array of strings that represent the filters to apply when generating the artifacts. If filter is empty, it would process nothing.
1207
1741
  * @param outputSpecPath File path of the new OpenAPI specification file to generate. If not specified or empty, no spec file will be generated.
1208
- * @param adaptiveCardFolder Folder path where the Adaptive Card files will be generated. If not specified or empty, Adaptive Card files will not be generated.
1742
+ * @param pluginFilePath File path of the api plugin file to generate.
1209
1743
  */
1210
- async generate(manifestPath, filter, outputSpecPath, adaptiveCardFolder, signal) {
1744
+ async generateForCopilot(manifestPath, filter, outputSpecPath, pluginFilePath, signal) {
1211
1745
  const result = {
1212
1746
  allSuccess: true,
1213
1747
  warnings: [],
1214
1748
  };
1215
1749
  try {
1216
- if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
1217
- throw new SpecParserError(ConstantString.CancelledMessage, ErrorType.Cancelled);
1750
+ const newSpecs = await this.getFilteredSpecs(filter, signal);
1751
+ const newUnResolvedSpec = newSpecs[0];
1752
+ const newSpec = newSpecs[1];
1753
+ let resultStr;
1754
+ if (outputSpecPath.endsWith(".yaml") || outputSpecPath.endsWith(".yml")) {
1755
+ resultStr = jsyaml.dump(newUnResolvedSpec);
1218
1756
  }
1219
- await this.loadSpec();
1220
- if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
1221
- throw new SpecParserError(ConstantString.CancelledMessage, ErrorType.Cancelled);
1757
+ else {
1758
+ resultStr = JSON.stringify(newUnResolvedSpec, null, 2);
1222
1759
  }
1223
- const newUnResolvedSpec = SpecFilter.specFilter(filter, this.unResolveSpec, this.spec, this.options.allowMissingId, this.options.allowAPIKeyAuth, this.options.allowMultipleParameters, this.options.allowOauth2);
1760
+ await fs.outputFile(outputSpecPath, resultStr);
1224
1761
  if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
1225
1762
  throw new SpecParserError(ConstantString.CancelledMessage, ErrorType.Cancelled);
1226
1763
  }
1227
- const newSpec = (await this.parser.dereference(newUnResolvedSpec));
1228
- const AuthSet = new Set();
1229
- let hasMultipleAPIKeyAuth = false;
1764
+ const [updatedManifest, apiPlugin] = await ManifestUpdater.updateManifestWithAiPlugin(manifestPath, outputSpecPath, pluginFilePath, newSpec, this.options);
1765
+ await fs.outputJSON(manifestPath, updatedManifest, { spaces: 2 });
1766
+ await fs.outputJSON(pluginFilePath, apiPlugin, { spaces: 2 });
1767
+ }
1768
+ catch (err) {
1769
+ if (err instanceof SpecParserError) {
1770
+ throw err;
1771
+ }
1772
+ throw new SpecParserError(err.toString(), ErrorType.GenerateFailed);
1773
+ }
1774
+ return result;
1775
+ }
1776
+ /**
1777
+ * Generates and update artifacts from the OpenAPI specification file. Generate Adaptive Cards, update Teams app manifest, and generate a new OpenAPI specification file.
1778
+ * @param manifestPath A file path of the Teams app manifest file to update.
1779
+ * @param filter An array of strings that represent the filters to apply when generating the artifacts. If filter is empty, it would process nothing.
1780
+ * @param outputSpecPath File path of the new OpenAPI specification file to generate. If not specified or empty, no spec file will be generated.
1781
+ * @param adaptiveCardFolder Folder path where the Adaptive Card files will be generated. If not specified or empty, Adaptive Card files will not be generated.
1782
+ */
1783
+ async generate(manifestPath, filter, outputSpecPath, adaptiveCardFolder, signal) {
1784
+ const result = {
1785
+ allSuccess: true,
1786
+ warnings: [],
1787
+ };
1788
+ try {
1789
+ const newSpecs = await this.getFilteredSpecs(filter, signal);
1790
+ const newUnResolvedSpec = newSpecs[0];
1791
+ const newSpec = newSpecs[1];
1792
+ let hasMultipleAuth = false;
1793
+ let authInfo = undefined;
1230
1794
  for (const url in newSpec.paths) {
1231
1795
  for (const method in newSpec.paths[url]) {
1232
1796
  const operation = newSpec.paths[url][method];
1233
1797
  const authArray = Utils.getAuthArray(operation.security, newSpec);
1234
1798
  if (authArray && authArray.length > 0) {
1235
- AuthSet.add(authArray[0][0].authSchema);
1236
- if (AuthSet.size > 1) {
1237
- hasMultipleAPIKeyAuth = true;
1799
+ const currentAuth = authArray[0][0];
1800
+ if (!authInfo) {
1801
+ authInfo = authArray[0][0];
1802
+ }
1803
+ else if (authInfo.name !== currentAuth.name) {
1804
+ hasMultipleAuth = true;
1238
1805
  break;
1239
1806
  }
1240
1807
  }
1241
1808
  }
1242
1809
  }
1243
- if (hasMultipleAPIKeyAuth) {
1244
- throw new SpecParserError(ConstantString.MultipleAPIKeyNotSupported, ErrorType.MultipleAPIKeyNotSupported);
1810
+ if (hasMultipleAuth && this.options.projectType !== ProjectType.TeamsAi) {
1811
+ throw new SpecParserError(ConstantString.MultipleAuthNotSupported, ErrorType.MultipleAuthNotSupported);
1245
1812
  }
1246
1813
  let resultStr;
1247
1814
  if (outputSpecPath.endsWith(".yaml") || outputSpecPath.endsWith(".yml")) {
@@ -1251,26 +1818,28 @@ class SpecParser {
1251
1818
  resultStr = JSON.stringify(newUnResolvedSpec, null, 2);
1252
1819
  }
1253
1820
  await fs.outputFile(outputSpecPath, resultStr);
1254
- for (const url in newSpec.paths) {
1255
- for (const method in newSpec.paths[url]) {
1256
- // paths object may contain description/summary, so we need to check if it is a operation object
1257
- if (method === ConstantString.PostMethod || method === ConstantString.GetMethod) {
1258
- const operation = newSpec.paths[url][method];
1259
- try {
1260
- const [card, jsonPath] = AdaptiveCardGenerator.generateAdaptiveCard(operation);
1261
- const fileName = path.join(adaptiveCardFolder, `${operation.operationId}.json`);
1262
- const wrappedCard = wrapAdaptiveCard(card, jsonPath);
1263
- await fs.outputJSON(fileName, wrappedCard, { spaces: 2 });
1264
- const dataFileName = path.join(adaptiveCardFolder, `${operation.operationId}.data.json`);
1265
- await fs.outputJSON(dataFileName, {}, { spaces: 2 });
1266
- }
1267
- catch (err) {
1268
- result.allSuccess = false;
1269
- result.warnings.push({
1270
- type: WarningType.GenerateCardFailed,
1271
- content: err.toString(),
1272
- data: operation.operationId,
1273
- });
1821
+ if (adaptiveCardFolder) {
1822
+ for (const url in newSpec.paths) {
1823
+ for (const method in newSpec.paths[url]) {
1824
+ // paths object may contain description/summary which is not a http method, so we need to check if it is a operation object
1825
+ if (this.options.allowMethods.includes(method)) {
1826
+ const operation = newSpec.paths[url][method];
1827
+ try {
1828
+ const [card, jsonPath] = AdaptiveCardGenerator.generateAdaptiveCard(operation);
1829
+ const fileName = path.join(adaptiveCardFolder, `${operation.operationId}.json`);
1830
+ const wrappedCard = wrapAdaptiveCard(card, jsonPath);
1831
+ await fs.outputJSON(fileName, wrappedCard, { spaces: 2 });
1832
+ const dataFileName = path.join(adaptiveCardFolder, `${operation.operationId}.data.json`);
1833
+ await fs.outputJSON(dataFileName, {}, { spaces: 2 });
1834
+ }
1835
+ catch (err) {
1836
+ result.allSuccess = false;
1837
+ result.warnings.push({
1838
+ type: WarningType.GenerateCardFailed,
1839
+ content: err.toString(),
1840
+ data: operation.operationId,
1841
+ });
1842
+ }
1274
1843
  }
1275
1844
  }
1276
1845
  }
@@ -1278,8 +1847,7 @@ class SpecParser {
1278
1847
  if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
1279
1848
  throw new SpecParserError(ConstantString.CancelledMessage, ErrorType.Cancelled);
1280
1849
  }
1281
- const auth = Array.from(AuthSet)[0];
1282
- const [updatedManifest, warnings] = await ManifestUpdater.updateManifest(manifestPath, outputSpecPath, adaptiveCardFolder, newSpec, this.options.allowMultipleParameters, auth);
1850
+ const [updatedManifest, warnings] = await ManifestUpdater.updateManifest(manifestPath, outputSpecPath, newSpec, this.options, adaptiveCardFolder, authInfo);
1283
1851
  await fs.outputJSON(manifestPath, updatedManifest, { spaces: 2 });
1284
1852
  result.warnings.push(...warnings);
1285
1853
  }
@@ -1304,15 +1872,21 @@ class SpecParser {
1304
1872
  this.spec = (await this.parser.dereference(clonedUnResolveSpec));
1305
1873
  }
1306
1874
  }
1307
- getAllSupportedAPIs(spec) {
1308
- if (this.apiMap !== undefined) {
1309
- return this.apiMap;
1875
+ getAPIs(spec) {
1876
+ const validator = this.getValidator(spec);
1877
+ const apiMap = validator.listAPIs();
1878
+ this.apiMap = apiMap;
1879
+ return apiMap;
1880
+ }
1881
+ getValidator(spec) {
1882
+ if (this.validator) {
1883
+ return this.validator;
1310
1884
  }
1311
- const result = Utils.listSupportedAPIs(spec, this.options.allowMissingId, this.options.allowAPIKeyAuth, this.options.allowMultipleParameters, this.options.allowOauth2);
1312
- this.apiMap = result;
1313
- return result;
1885
+ const validator = ValidatorFactory.create(spec, this.options);
1886
+ this.validator = validator;
1887
+ return validator;
1314
1888
  }
1315
1889
  }
1316
1890
 
1317
- export { ConstantString, ErrorType, SpecParser, SpecParserError, Utils, ValidationStatus, WarningType };
1891
+ export { AdaptiveCardGenerator, ConstantString, ErrorType, ProjectType, SpecParser, SpecParserError, Utils, ValidationStatus, WarningType };
1318
1892
  //# sourceMappingURL=index.esm2017.mjs.map