@microsoft/m365-spec-parser 0.1.1-alpha.48b9eab36.0 → 0.1.1-alpha.4e708f092.0

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