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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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
  }
@@ -371,24 +228,47 @@ class Utils {
371
228
  result.sort((a, b) => a[0].name.localeCompare(b[0].name));
372
229
  return result;
373
230
  }
231
+ static getAuthInfo(spec) {
232
+ let authInfo = undefined;
233
+ for (const url in spec.paths) {
234
+ for (const method in spec.paths[url]) {
235
+ const operation = spec.paths[url][method];
236
+ const authArray = Utils.getAuthArray(operation.security, spec);
237
+ if (authArray && authArray.length > 0) {
238
+ const currentAuth = authArray[0][0];
239
+ if (!authInfo) {
240
+ authInfo = authArray[0][0];
241
+ }
242
+ else if (authInfo.name !== currentAuth.name) {
243
+ throw new SpecParserError(ConstantString.MultipleAuthNotSupported, ErrorType.MultipleAuthNotSupported);
244
+ }
245
+ }
246
+ }
247
+ }
248
+ return authInfo;
249
+ }
374
250
  static updateFirstLetter(str) {
375
251
  return str.charAt(0).toUpperCase() + str.slice(1);
376
252
  }
377
253
  static getResponseJson(operationObject) {
378
254
  var _a, _b;
379
255
  let json = {};
256
+ let multipleMediaType = false;
380
257
  for (const code of ConstantString.ResponseCodeFor20X) {
381
258
  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
259
  if ((_b = responseObject === null || responseObject === void 0 ? void 0 : responseObject.content) === null || _b === void 0 ? void 0 : _b["application/json"]) {
260
+ multipleMediaType = false;
387
261
  json = responseObject.content["application/json"];
388
- break;
262
+ if (Utils.containMultipleMediaTypes(responseObject)) {
263
+ multipleMediaType = true;
264
+ json = {};
265
+ }
266
+ else {
267
+ break;
268
+ }
389
269
  }
390
270
  }
391
- return json;
271
+ return { json, multipleMediaType };
392
272
  }
393
273
  static convertPathToCamelCase(path) {
394
274
  const pathSegments = path.split(/[./{]/);
@@ -408,10 +288,10 @@ class Utils {
408
288
  return undefined;
409
289
  }
410
290
  }
411
- static resolveServerUrl(url) {
291
+ static resolveEnv(str) {
412
292
  const placeHolderReg = /\${{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*}}/g;
413
- let matches = placeHolderReg.exec(url);
414
- let newUrl = url;
293
+ let matches = placeHolderReg.exec(str);
294
+ let newStr = str;
415
295
  while (matches != null) {
416
296
  const envVar = matches[1];
417
297
  const envVal = process.env[envVar];
@@ -419,17 +299,17 @@ class Utils {
419
299
  throw new Error(Utils.format(ConstantString.ResolveServerUrlFailed, envVar));
420
300
  }
421
301
  else {
422
- newUrl = newUrl.replace(matches[0], envVal);
302
+ newStr = newStr.replace(matches[0], envVal);
423
303
  }
424
- matches = placeHolderReg.exec(url);
304
+ matches = placeHolderReg.exec(str);
425
305
  }
426
- return newUrl;
306
+ return newStr;
427
307
  }
428
308
  static checkServerUrl(servers) {
429
309
  const errors = [];
430
310
  let serverUrl;
431
311
  try {
432
- serverUrl = Utils.resolveServerUrl(servers[0].url);
312
+ serverUrl = Utils.resolveEnv(servers[0].url);
433
313
  }
434
314
  catch (err) {
435
315
  errors.push({
@@ -459,7 +339,8 @@ class Utils {
459
339
  }
460
340
  return errors;
461
341
  }
462
- static validateServer(spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2) {
342
+ static validateServer(spec, options) {
343
+ var _a;
463
344
  const errors = [];
464
345
  let hasTopLevelServers = false;
465
346
  let hasPathLevelServers = false;
@@ -480,7 +361,7 @@ class Utils {
480
361
  }
481
362
  for (const method in methods) {
482
363
  const operationObject = methods[method];
483
- if (Utils.isSupportedApi(method, path, spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2)) {
364
+ if (((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && operationObject) {
484
365
  if ((operationObject === null || operationObject === void 0 ? void 0 : operationObject.servers) && operationObject.servers.length >= 1) {
485
366
  hasOperationLevelServers = true;
486
367
  const serverErrors = Utils.checkServerUrl(operationObject.servers);
@@ -523,6 +404,7 @@ class Utils {
523
404
  Utils.updateParameterWithInputType(schema, parameter);
524
405
  }
525
406
  if (isRequired && schema.default === undefined) {
407
+ parameter.isRequired = true;
526
408
  requiredParams.push(parameter);
527
409
  }
528
410
  else {
@@ -567,7 +449,7 @@ class Utils {
567
449
  param.value = schema.default;
568
450
  }
569
451
  }
570
- static parseApiInfo(operationItem, allowMultipleParameters) {
452
+ static parseApiInfo(operationItem, options) {
571
453
  var _a, _b;
572
454
  const requiredParams = [];
573
455
  const optionalParams = [];
@@ -581,11 +463,12 @@ class Utils {
581
463
  description: ((_a = param.description) !== null && _a !== void 0 ? _a : "").slice(0, ConstantString.ParameterDescriptionMaxLens),
582
464
  };
583
465
  const schema = param.schema;
584
- if (allowMultipleParameters && schema) {
466
+ if (options.allowMultipleParameters && schema) {
585
467
  Utils.updateParameterWithInputType(schema, parameter);
586
468
  }
587
469
  if (param.in !== "header" && param.in !== "cookie") {
588
470
  if (param.required && (schema === null || schema === void 0 ? void 0 : schema.default) === undefined) {
471
+ parameter.isRequired = true;
589
472
  requiredParams.push(parameter);
590
473
  }
591
474
  else {
@@ -599,19 +482,13 @@ class Utils {
599
482
  const requestJson = requestBody.content["application/json"];
600
483
  if (Object.keys(requestJson).length !== 0) {
601
484
  const schema = requestJson.schema;
602
- const [requiredP, optionalP] = Utils.generateParametersFromSchema(schema, "requestBody", allowMultipleParameters, requestBody.required);
485
+ const [requiredP, optionalP] = Utils.generateParametersFromSchema(schema, "requestBody", !!options.allowMultipleParameters, requestBody.required);
603
486
  requiredParams.push(...requiredP);
604
487
  optionalParams.push(...optionalP);
605
488
  }
606
489
  }
607
490
  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
- }
491
+ const parameters = [...requiredParams, ...optionalParams];
615
492
  const command = {
616
493
  context: ["compose"],
617
494
  type: "query",
@@ -620,130 +497,567 @@ class Utils {
620
497
  parameters: parameters,
621
498
  description: ((_b = operationItem.description) !== null && _b !== void 0 ? _b : "").slice(0, ConstantString.CommandDescriptionMaxLens),
622
499
  };
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
- };
500
+ return command;
501
+ }
502
+ static format(str, ...args) {
503
+ let index = 0;
504
+ return str.replace(/%s/g, () => {
505
+ const arg = args[index++];
506
+ return arg !== undefined ? arg : "";
507
+ });
508
+ }
509
+ static getSafeRegistrationIdEnvName(authName) {
510
+ if (!authName) {
511
+ return "";
630
512
  }
631
- return [command, warning];
513
+ let safeRegistrationIdEnvName = authName.toUpperCase().replace(/[^A-Z0-9_]/g, "_");
514
+ if (!safeRegistrationIdEnvName.match(/^[A-Z]/)) {
515
+ safeRegistrationIdEnvName = "PREFIX_" + safeRegistrationIdEnvName;
516
+ }
517
+ return safeRegistrationIdEnvName;
632
518
  }
633
- static listSupportedAPIs(spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2) {
634
- const paths = spec.paths;
519
+ static getServerObject(spec, method, path) {
520
+ const pathObj = spec.paths[path];
521
+ const operationObject = pathObj[method];
522
+ const rootServer = spec.servers && spec.servers[0];
523
+ const methodServer = spec.paths[path].servers && spec.paths[path].servers[0];
524
+ const operationServer = operationObject.servers && operationObject.servers[0];
525
+ const serverUrl = operationServer || methodServer || rootServer;
526
+ return serverUrl;
527
+ }
528
+ }
529
+
530
+ // Copyright (c) Microsoft Corporation.
531
+ class Validator {
532
+ listAPIs() {
533
+ var _a;
534
+ if (this.apiMap) {
535
+ return this.apiMap;
536
+ }
537
+ const paths = this.spec.paths;
635
538
  const result = {};
636
539
  for (const path in paths) {
637
540
  const methods = paths[path];
638
541
  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;
542
+ const operationObject = methods[method];
543
+ if (((_a = this.options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && operationObject) {
544
+ const validateResult = this.validateAPI(method, path);
545
+ result[`${method.toUpperCase()} ${path}`] = {
546
+ operation: operationObject,
547
+ isValid: validateResult.isValid,
548
+ reason: validateResult.reason,
549
+ };
643
550
  }
644
551
  }
645
552
  }
553
+ this.apiMap = result;
646
554
  return result;
647
555
  }
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,
655
- });
656
- }
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,
556
+ validateSpecVersion() {
557
+ const result = { errors: [], warnings: [] };
558
+ if (this.spec.openapi >= "3.1.0") {
559
+ result.errors.push({
560
+ type: ErrorType.SpecVersionNotSupported,
561
+ content: Utils.format(ConstantString.SpecVersionNotSupported, this.spec.openapi),
562
+ data: this.spec.openapi,
668
563
  });
669
564
  }
670
- // No supported API
671
- const apiMap = Utils.listSupportedAPIs(spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2);
672
- if (Object.keys(apiMap).length === 0) {
673
- errors.push({
565
+ return result;
566
+ }
567
+ validateSpecServer() {
568
+ const result = { errors: [], warnings: [] };
569
+ const serverErrors = Utils.validateServer(this.spec, this.options);
570
+ result.errors.push(...serverErrors);
571
+ return result;
572
+ }
573
+ validateSpecNoSupportAPI() {
574
+ const result = { errors: [], warnings: [] };
575
+ const apiMap = this.listAPIs();
576
+ const validAPIs = Object.entries(apiMap).filter(([, value]) => value.isValid);
577
+ if (validAPIs.length === 0) {
578
+ const data = [];
579
+ for (const key in apiMap) {
580
+ const { reason } = apiMap[key];
581
+ const apiInvalidReason = { api: key, reason: reason };
582
+ data.push(apiInvalidReason);
583
+ }
584
+ result.errors.push({
674
585
  type: ErrorType.NoSupportedApi,
675
586
  content: ConstantString.NoSupportedApi,
587
+ data,
676
588
  });
677
589
  }
590
+ return result;
591
+ }
592
+ validateSpecOperationId() {
593
+ const result = { errors: [], warnings: [] };
594
+ const apiMap = this.listAPIs();
678
595
  // OperationId missing
679
596
  const apisMissingOperationId = [];
680
597
  for (const key in apiMap) {
681
- const pathObjectItem = apiMap[key];
682
- if (!pathObjectItem.operationId) {
598
+ const { operation } = apiMap[key];
599
+ if (!operation.operationId) {
683
600
  apisMissingOperationId.push(key);
684
601
  }
685
602
  }
686
603
  if (apisMissingOperationId.length > 0) {
687
- warnings.push({
604
+ result.warnings.push({
688
605
  type: WarningType.OperationIdMissing,
689
606
  content: Utils.format(ConstantString.MissingOperationId, apisMissingOperationId.join(", ")),
690
607
  data: apisMissingOperationId,
691
608
  });
692
609
  }
693
- let status = ValidationStatus.Valid;
694
- if (warnings.length > 0 && errors.length === 0) {
695
- status = ValidationStatus.Warning;
610
+ return result;
611
+ }
612
+ validateMethodAndPath(method, path) {
613
+ const result = { isValid: true, reason: [] };
614
+ if (this.options.allowMethods && !this.options.allowMethods.includes(method)) {
615
+ result.isValid = false;
616
+ result.reason.push(ErrorType.MethodNotAllowed);
617
+ return result;
618
+ }
619
+ const pathObj = this.spec.paths[path];
620
+ if (!pathObj || !pathObj[method]) {
621
+ result.isValid = false;
622
+ result.reason.push(ErrorType.UrlPathNotExist);
623
+ return result;
624
+ }
625
+ return result;
626
+ }
627
+ validateResponse(method, path) {
628
+ const result = { isValid: true, reason: [] };
629
+ const operationObject = this.spec.paths[path][method];
630
+ const { json, multipleMediaType } = Utils.getResponseJson(operationObject);
631
+ if (this.options.projectType === ProjectType.SME) {
632
+ // only support response body only contains “application/json” content type
633
+ if (multipleMediaType) {
634
+ result.reason.push(ErrorType.ResponseContainMultipleMediaTypes);
635
+ }
636
+ else if (Object.keys(json).length === 0) {
637
+ // response body should not be empty
638
+ result.reason.push(ErrorType.ResponseJsonIsEmpty);
639
+ }
640
+ }
641
+ return result;
642
+ }
643
+ validateServer(method, path) {
644
+ const result = { isValid: true, reason: [] };
645
+ const serverObj = Utils.getServerObject(this.spec, method, path);
646
+ if (!serverObj) {
647
+ // should contain server URL
648
+ result.reason.push(ErrorType.NoServerInformation);
696
649
  }
697
- else if (errors.length > 0) {
698
- status = ValidationStatus.Error;
650
+ else {
651
+ // server url should be absolute url with https protocol
652
+ const serverValidateResult = Utils.checkServerUrl([serverObj]);
653
+ result.reason.push(...serverValidateResult.map((item) => item.type));
654
+ }
655
+ return result;
656
+ }
657
+ validateAuth(method, path) {
658
+ const pathObj = this.spec.paths[path];
659
+ const operationObject = pathObj[method];
660
+ const securities = operationObject.security;
661
+ const authSchemeArray = Utils.getAuthArray(securities, this.spec);
662
+ if (authSchemeArray.length === 0) {
663
+ return { isValid: true, reason: [] };
664
+ }
665
+ if (this.options.allowAPIKeyAuth ||
666
+ this.options.allowOauth2 ||
667
+ this.options.allowBearerTokenAuth) {
668
+ // Currently we don't support multiple auth in one operation
669
+ if (authSchemeArray.length > 0 && authSchemeArray.every((auths) => auths.length > 1)) {
670
+ return {
671
+ isValid: false,
672
+ reason: [ErrorType.MultipleAuthNotSupported],
673
+ };
674
+ }
675
+ for (const auths of authSchemeArray) {
676
+ if (auths.length === 1) {
677
+ if ((this.options.allowAPIKeyAuth && Utils.isAPIKeyAuth(auths[0].authScheme)) ||
678
+ (this.options.allowOauth2 && Utils.isOAuthWithAuthCodeFlow(auths[0].authScheme)) ||
679
+ (this.options.allowBearerTokenAuth && Utils.isBearerTokenAuth(auths[0].authScheme))) {
680
+ return { isValid: true, reason: [] };
681
+ }
682
+ }
683
+ }
699
684
  }
700
- return {
701
- status,
702
- warnings,
703
- errors,
685
+ return { isValid: false, reason: [ErrorType.AuthTypeIsNotSupported] };
686
+ }
687
+ checkPostBodySchema(schema, isRequired = false) {
688
+ var _a;
689
+ const paramResult = {
690
+ requiredNum: 0,
691
+ optionalNum: 0,
692
+ isValid: true,
693
+ reason: [],
704
694
  };
695
+ if (Object.keys(schema).length === 0) {
696
+ return paramResult;
697
+ }
698
+ const isRequiredWithoutDefault = isRequired && schema.default === undefined;
699
+ const isCopilot = this.projectType === ProjectType.Copilot;
700
+ if (isCopilot && this.hasNestedObjectInSchema(schema)) {
701
+ paramResult.isValid = false;
702
+ paramResult.reason = [ErrorType.RequestBodyContainsNestedObject];
703
+ return paramResult;
704
+ }
705
+ if (schema.type === "string" ||
706
+ schema.type === "integer" ||
707
+ schema.type === "boolean" ||
708
+ schema.type === "number") {
709
+ if (isRequiredWithoutDefault) {
710
+ paramResult.requiredNum = paramResult.requiredNum + 1;
711
+ }
712
+ else {
713
+ paramResult.optionalNum = paramResult.optionalNum + 1;
714
+ }
715
+ }
716
+ else if (schema.type === "object") {
717
+ const { properties } = schema;
718
+ for (const property in properties) {
719
+ let isRequired = false;
720
+ if (schema.required && ((_a = schema.required) === null || _a === void 0 ? void 0 : _a.indexOf(property)) >= 0) {
721
+ isRequired = true;
722
+ }
723
+ const result = this.checkPostBodySchema(properties[property], isRequired);
724
+ paramResult.requiredNum += result.requiredNum;
725
+ paramResult.optionalNum += result.optionalNum;
726
+ paramResult.isValid = paramResult.isValid && result.isValid;
727
+ paramResult.reason.push(...result.reason);
728
+ }
729
+ }
730
+ else {
731
+ if (isRequiredWithoutDefault && !isCopilot) {
732
+ paramResult.isValid = false;
733
+ paramResult.reason.push(ErrorType.PostBodyContainsRequiredUnsupportedSchema);
734
+ }
735
+ }
736
+ return paramResult;
705
737
  }
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
- });
738
+ checkParamSchema(paramObject) {
739
+ const paramResult = {
740
+ requiredNum: 0,
741
+ optionalNum: 0,
742
+ isValid: true,
743
+ reason: [],
744
+ };
745
+ if (!paramObject) {
746
+ return paramResult;
747
+ }
748
+ const isCopilot = this.projectType === ProjectType.Copilot;
749
+ for (let i = 0; i < paramObject.length; i++) {
750
+ const param = paramObject[i];
751
+ const schema = param.schema;
752
+ if (isCopilot && this.hasNestedObjectInSchema(schema)) {
753
+ paramResult.isValid = false;
754
+ paramResult.reason.push(ErrorType.ParamsContainsNestedObject);
755
+ continue;
756
+ }
757
+ const isRequiredWithoutDefault = param.required && schema.default === undefined;
758
+ if (isCopilot) {
759
+ if (isRequiredWithoutDefault) {
760
+ paramResult.requiredNum = paramResult.requiredNum + 1;
761
+ }
762
+ else {
763
+ paramResult.optionalNum = paramResult.optionalNum + 1;
764
+ }
765
+ continue;
766
+ }
767
+ if (param.in === "header" || param.in === "cookie") {
768
+ if (isRequiredWithoutDefault) {
769
+ paramResult.isValid = false;
770
+ paramResult.reason.push(ErrorType.ParamsContainRequiredUnsupportedSchema);
771
+ }
772
+ continue;
773
+ }
774
+ if (schema.type !== "boolean" &&
775
+ schema.type !== "string" &&
776
+ schema.type !== "number" &&
777
+ schema.type !== "integer") {
778
+ if (isRequiredWithoutDefault) {
779
+ paramResult.isValid = false;
780
+ paramResult.reason.push(ErrorType.ParamsContainRequiredUnsupportedSchema);
781
+ }
782
+ continue;
783
+ }
784
+ if (param.in === "query" || param.in === "path") {
785
+ if (isRequiredWithoutDefault) {
786
+ paramResult.requiredNum = paramResult.requiredNum + 1;
787
+ }
788
+ else {
789
+ paramResult.optionalNum = paramResult.optionalNum + 1;
790
+ }
791
+ }
792
+ }
793
+ return paramResult;
712
794
  }
713
- static getSafeRegistrationIdEnvName(authName) {
714
- if (!authName) {
715
- return "";
795
+ hasNestedObjectInSchema(schema) {
796
+ if (schema.type === "object") {
797
+ for (const property in schema.properties) {
798
+ const nestedSchema = schema.properties[property];
799
+ if (nestedSchema.type === "object") {
800
+ return true;
801
+ }
802
+ }
716
803
  }
717
- let safeRegistrationIdEnvName = authName.toUpperCase().replace(/[^A-Z0-9_]/g, "_");
718
- if (!safeRegistrationIdEnvName.match(/^[A-Z]/)) {
719
- safeRegistrationIdEnvName = "PREFIX_" + safeRegistrationIdEnvName;
804
+ return false;
805
+ }
806
+ }
807
+
808
+ // Copyright (c) Microsoft Corporation.
809
+ class CopilotValidator extends Validator {
810
+ constructor(spec, options) {
811
+ super();
812
+ this.projectType = ProjectType.Copilot;
813
+ this.options = options;
814
+ this.spec = spec;
815
+ }
816
+ validateSpec() {
817
+ const result = { errors: [], warnings: [] };
818
+ // validate spec version
819
+ let validationResult = this.validateSpecVersion();
820
+ result.errors.push(...validationResult.errors);
821
+ // validate spec server
822
+ validationResult = this.validateSpecServer();
823
+ result.errors.push(...validationResult.errors);
824
+ // validate no supported API
825
+ validationResult = this.validateSpecNoSupportAPI();
826
+ result.errors.push(...validationResult.errors);
827
+ // validate operationId missing
828
+ validationResult = this.validateSpecOperationId();
829
+ result.warnings.push(...validationResult.warnings);
830
+ return result;
831
+ }
832
+ validateAPI(method, path) {
833
+ const result = { isValid: true, reason: [] };
834
+ method = method.toLocaleLowerCase();
835
+ // validate method and path
836
+ const methodAndPathResult = this.validateMethodAndPath(method, path);
837
+ if (!methodAndPathResult.isValid) {
838
+ return methodAndPathResult;
839
+ }
840
+ const operationObject = this.spec.paths[path][method];
841
+ // validate auth
842
+ const authCheckResult = this.validateAuth(method, path);
843
+ result.reason.push(...authCheckResult.reason);
844
+ // validate operationId
845
+ if (!this.options.allowMissingId && !operationObject.operationId) {
846
+ result.reason.push(ErrorType.MissingOperationId);
847
+ }
848
+ // validate server
849
+ const validateServerResult = this.validateServer(method, path);
850
+ result.reason.push(...validateServerResult.reason);
851
+ // validate response
852
+ const validateResponseResult = this.validateResponse(method, path);
853
+ result.reason.push(...validateResponseResult.reason);
854
+ // validate requestBody
855
+ const requestBody = operationObject.requestBody;
856
+ const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
857
+ if (requestJsonBody) {
858
+ const requestBodySchema = requestJsonBody.schema;
859
+ if (requestBodySchema.type !== "object") {
860
+ result.reason.push(ErrorType.PostBodySchemaIsNotJson);
861
+ }
862
+ const requestBodyParamResult = this.checkPostBodySchema(requestBodySchema, requestBody.required);
863
+ result.reason.push(...requestBodyParamResult.reason);
864
+ }
865
+ // validate parameters
866
+ const paramObject = operationObject.parameters;
867
+ const paramResult = this.checkParamSchema(paramObject);
868
+ result.reason.push(...paramResult.reason);
869
+ if (result.reason.length > 0) {
870
+ result.isValid = false;
871
+ }
872
+ return result;
873
+ }
874
+ }
875
+
876
+ // Copyright (c) Microsoft Corporation.
877
+ class SMEValidator extends Validator {
878
+ constructor(spec, options) {
879
+ super();
880
+ this.projectType = ProjectType.SME;
881
+ this.options = options;
882
+ this.spec = spec;
883
+ }
884
+ validateSpec() {
885
+ const result = { errors: [], warnings: [] };
886
+ // validate spec version
887
+ let validationResult = this.validateSpecVersion();
888
+ result.errors.push(...validationResult.errors);
889
+ // validate spec server
890
+ validationResult = this.validateSpecServer();
891
+ result.errors.push(...validationResult.errors);
892
+ // validate no supported API
893
+ validationResult = this.validateSpecNoSupportAPI();
894
+ result.errors.push(...validationResult.errors);
895
+ // validate operationId missing
896
+ if (this.options.allowMissingId) {
897
+ validationResult = this.validateSpecOperationId();
898
+ result.warnings.push(...validationResult.warnings);
899
+ }
900
+ return result;
901
+ }
902
+ validateAPI(method, path) {
903
+ const result = { isValid: true, reason: [] };
904
+ method = method.toLocaleLowerCase();
905
+ // validate method and path
906
+ const methodAndPathResult = this.validateMethodAndPath(method, path);
907
+ if (!methodAndPathResult.isValid) {
908
+ return methodAndPathResult;
909
+ }
910
+ const operationObject = this.spec.paths[path][method];
911
+ // validate auth
912
+ const authCheckResult = this.validateAuth(method, path);
913
+ result.reason.push(...authCheckResult.reason);
914
+ // validate operationId
915
+ if (!this.options.allowMissingId && !operationObject.operationId) {
916
+ result.reason.push(ErrorType.MissingOperationId);
917
+ }
918
+ // validate server
919
+ const validateServerResult = this.validateServer(method, path);
920
+ result.reason.push(...validateServerResult.reason);
921
+ // validate response
922
+ const validateResponseResult = this.validateResponse(method, path);
923
+ result.reason.push(...validateResponseResult.reason);
924
+ let postBodyResult = {
925
+ requiredNum: 0,
926
+ optionalNum: 0,
927
+ isValid: true,
928
+ reason: [],
929
+ };
930
+ // validate requestBody
931
+ const requestBody = operationObject.requestBody;
932
+ const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
933
+ if (Utils.containMultipleMediaTypes(requestBody)) {
934
+ result.reason.push(ErrorType.PostBodyContainMultipleMediaTypes);
935
+ }
936
+ if (requestJsonBody) {
937
+ const requestBodySchema = requestJsonBody.schema;
938
+ postBodyResult = this.checkPostBodySchema(requestBodySchema, requestBody.required);
939
+ result.reason.push(...postBodyResult.reason);
940
+ }
941
+ // validate parameters
942
+ const paramObject = operationObject.parameters;
943
+ const paramResult = this.checkParamSchema(paramObject);
944
+ result.reason.push(...paramResult.reason);
945
+ // validate total parameters count
946
+ if (paramResult.isValid && postBodyResult.isValid) {
947
+ const paramCountResult = this.validateParamCount(postBodyResult, paramResult);
948
+ result.reason.push(...paramCountResult.reason);
949
+ }
950
+ if (result.reason.length > 0) {
951
+ result.isValid = false;
952
+ }
953
+ return result;
954
+ }
955
+ validateParamCount(postBodyResult, paramResult) {
956
+ const result = { isValid: true, reason: [] };
957
+ const totalRequiredParams = postBodyResult.requiredNum + paramResult.requiredNum;
958
+ const totalParams = totalRequiredParams + postBodyResult.optionalNum + paramResult.optionalNum;
959
+ if (totalRequiredParams > 1) {
960
+ if (!this.options.allowMultipleParameters ||
961
+ totalRequiredParams > SMEValidator.SMERequiredParamsMaxNum) {
962
+ result.reason.push(ErrorType.ExceededRequiredParamsLimit);
963
+ }
964
+ }
965
+ else if (totalParams === 0) {
966
+ result.reason.push(ErrorType.NoParameter);
967
+ }
968
+ return result;
969
+ }
970
+ }
971
+ SMEValidator.SMERequiredParamsMaxNum = 5;
972
+
973
+ // Copyright (c) Microsoft Corporation.
974
+ class TeamsAIValidator extends Validator {
975
+ constructor(spec, options) {
976
+ super();
977
+ this.projectType = ProjectType.TeamsAi;
978
+ this.options = options;
979
+ this.spec = spec;
980
+ }
981
+ validateSpec() {
982
+ const result = { errors: [], warnings: [] };
983
+ // validate spec server
984
+ let validationResult = this.validateSpecServer();
985
+ result.errors.push(...validationResult.errors);
986
+ // validate no supported API
987
+ validationResult = this.validateSpecNoSupportAPI();
988
+ result.errors.push(...validationResult.errors);
989
+ return result;
990
+ }
991
+ validateAPI(method, path) {
992
+ const result = { isValid: true, reason: [] };
993
+ method = method.toLocaleLowerCase();
994
+ // validate method and path
995
+ const methodAndPathResult = this.validateMethodAndPath(method, path);
996
+ if (!methodAndPathResult.isValid) {
997
+ return methodAndPathResult;
998
+ }
999
+ const operationObject = this.spec.paths[path][method];
1000
+ // validate operationId
1001
+ if (!this.options.allowMissingId && !operationObject.operationId) {
1002
+ result.reason.push(ErrorType.MissingOperationId);
1003
+ }
1004
+ // validate server
1005
+ const validateServerResult = this.validateServer(method, path);
1006
+ result.reason.push(...validateServerResult.reason);
1007
+ if (result.reason.length > 0) {
1008
+ result.isValid = false;
1009
+ }
1010
+ return result;
1011
+ }
1012
+ }
1013
+
1014
+ class ValidatorFactory {
1015
+ static create(spec, options) {
1016
+ var _a;
1017
+ const type = (_a = options.projectType) !== null && _a !== void 0 ? _a : ProjectType.SME;
1018
+ switch (type) {
1019
+ case ProjectType.SME:
1020
+ return new SMEValidator(spec, options);
1021
+ case ProjectType.Copilot:
1022
+ return new CopilotValidator(spec, options);
1023
+ case ProjectType.TeamsAi:
1024
+ return new TeamsAIValidator(spec, options);
1025
+ default:
1026
+ throw new Error(`Invalid project type: ${type}`);
720
1027
  }
721
- return safeRegistrationIdEnvName;
722
1028
  }
723
1029
  }
724
1030
 
725
1031
  // Copyright (c) Microsoft Corporation.
726
1032
  class SpecFilter {
727
- static specFilter(filter, unResolveSpec, resolvedSpec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2) {
1033
+ static specFilter(filter, unResolveSpec, resolvedSpec, options) {
1034
+ var _a;
728
1035
  try {
729
1036
  const newSpec = Object.assign({}, unResolveSpec);
730
1037
  const newPaths = {};
731
1038
  for (const filterItem of filter) {
732
1039
  const [method, path] = filterItem.split(" ");
733
1040
  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];
1041
+ const pathObj = (_a = resolvedSpec.paths) === null || _a === void 0 ? void 0 : _a[path];
1042
+ if (ConstantString.AllOperationMethods.includes(methodName) &&
1043
+ pathObj &&
1044
+ pathObj[methodName]) {
1045
+ const validator = ValidatorFactory.create(resolvedSpec, options);
1046
+ const validateResult = validator.validateAPI(methodName, path);
1047
+ if (!validateResult.isValid) {
1048
+ continue;
1049
+ }
1050
+ if (!newPaths[path]) {
1051
+ newPaths[path] = Object.assign({}, unResolveSpec.paths[path]);
1052
+ for (const m of ConstantString.AllOperationMethods) {
1053
+ delete newPaths[path][m];
1054
+ }
1055
+ }
1056
+ newPaths[path][methodName] = unResolveSpec.paths[path][methodName];
1057
+ // Add the operationId if missing
1058
+ if (!newPaths[path][methodName].operationId) {
1059
+ newPaths[path][methodName].operationId = `${methodName}${Utils.convertPathToCamelCase(path)}`;
741
1060
  }
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
1061
  }
748
1062
  }
749
1063
  newSpec.paths = newPaths;
@@ -755,97 +1069,11 @@ class SpecFilter {
755
1069
  }
756
1070
  }
757
1071
 
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
1072
  // Copyright (c) Microsoft Corporation.
845
1073
  class AdaptiveCardGenerator {
846
1074
  static generateAdaptiveCard(operationItem) {
847
1075
  try {
848
- const json = Utils.getResponseJson(operationItem);
1076
+ const { json } = Utils.getResponseJson(operationItem);
849
1077
  let cardBody = [];
850
1078
  let schema = json.schema;
851
1079
  let jsonPath = "$";
@@ -1011,6 +1239,27 @@ function wrapAdaptiveCard(card, jsonPath) {
1011
1239
  };
1012
1240
  return result;
1013
1241
  }
1242
+ function wrapResponseSemantics(card, jsonPath) {
1243
+ const props = inferProperties(card);
1244
+ const dataPath = jsonPath === "$" ? "$" : "$." + jsonPath;
1245
+ const result = {
1246
+ data_path: dataPath,
1247
+ };
1248
+ if (props.title || props.subtitle || props.imageUrl) {
1249
+ result.properties = {};
1250
+ if (props.title) {
1251
+ result.properties.title = "$." + props.title;
1252
+ }
1253
+ if (props.subtitle) {
1254
+ result.properties.subtitle = "$." + props.subtitle;
1255
+ }
1256
+ if (props.imageUrl) {
1257
+ result.properties.url = "$." + props.imageUrl;
1258
+ }
1259
+ }
1260
+ result.static_template = card;
1261
+ return result;
1262
+ }
1014
1263
  /**
1015
1264
  * Infers the preview card template from an Adaptive Card and a JSON path.
1016
1265
  * The preview card template includes a title and an optional subtitle and image.
@@ -1023,11 +1272,29 @@ function wrapAdaptiveCard(card, jsonPath) {
1023
1272
  * @returns The inferred preview card template.
1024
1273
  */
1025
1274
  function inferPreviewCardTemplate(card) {
1026
- var _a;
1027
1275
  const result = {
1028
- title: "",
1276
+ title: "result",
1029
1277
  };
1030
- const textBlockElements = new Set();
1278
+ const inferredProperties = inferProperties(card);
1279
+ if (inferredProperties.title) {
1280
+ result.title = `\${if(${inferredProperties.title}, ${inferredProperties.title}, 'N/A')}`;
1281
+ }
1282
+ if (inferredProperties.subtitle) {
1283
+ result.subtitle = `\${if(${inferredProperties.subtitle}, ${inferredProperties.subtitle}, 'N/A')}`;
1284
+ }
1285
+ if (inferredProperties.imageUrl) {
1286
+ result.image = {
1287
+ url: `\${${inferredProperties.imageUrl}}`,
1288
+ alt: `\${if(${inferredProperties.imageUrl}, ${inferredProperties.imageUrl}, 'N/A')}`,
1289
+ $when: `\${${inferredProperties.imageUrl} != null}`,
1290
+ };
1291
+ }
1292
+ return result;
1293
+ }
1294
+ function inferProperties(card) {
1295
+ var _a;
1296
+ const result = {};
1297
+ const nameSet = new Set();
1031
1298
  let rootObject;
1032
1299
  if (((_a = card.body[0]) === null || _a === void 0 ? void 0 : _a.type) === ConstantString.ContainerType) {
1033
1300
  rootObject = card.body[0].items;
@@ -1040,56 +1307,399 @@ function inferPreviewCardTemplate(card) {
1040
1307
  const textElement = element;
1041
1308
  const index = textElement.text.indexOf("${if(");
1042
1309
  if (index > 0) {
1043
- textElement.text = textElement.text.substring(index);
1044
- textBlockElements.add(textElement);
1310
+ const text = textElement.text.substring(index);
1311
+ const match = text.match(/\${if\(([^,]+),/);
1312
+ const property = match ? match[1] : "";
1313
+ if (property) {
1314
+ nameSet.add(property);
1315
+ }
1316
+ }
1317
+ }
1318
+ else if (element.type === ConstantString.ImageType) {
1319
+ const imageElement = element;
1320
+ const match = imageElement.url.match(/\${([^,]+)}/);
1321
+ const property = match ? match[1] : "";
1322
+ if (property) {
1323
+ nameSet.add(property);
1045
1324
  }
1046
1325
  }
1047
1326
  }
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);
1327
+ for (const name of nameSet) {
1328
+ if (!result.title && Utils.isWellKnownName(name, ConstantString.WellknownTitleName)) {
1329
+ result.title = name;
1330
+ nameSet.delete(name);
1053
1331
  }
1054
1332
  else if (!result.subtitle &&
1055
- Utils.isWellKnownName(text, ConstantString.WellknownSubtitleName)) {
1056
- result.subtitle = text;
1057
- textBlockElements.delete(element);
1333
+ Utils.isWellKnownName(name, ConstantString.WellknownSubtitleName)) {
1334
+ result.subtitle = name;
1335
+ nameSet.delete(name);
1058
1336
  }
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);
1337
+ else if (!result.imageUrl && Utils.isWellKnownName(name, ConstantString.WellknownImageName)) {
1338
+ result.imageUrl = name;
1339
+ nameSet.delete(name);
1070
1340
  }
1071
1341
  }
1072
- for (const element of textBlockElements) {
1073
- const text = element.text;
1342
+ for (const name of nameSet) {
1074
1343
  if (!result.title) {
1075
- result.title = text;
1076
- textBlockElements.delete(element);
1344
+ result.title = name;
1345
+ nameSet.delete(name);
1077
1346
  }
1078
1347
  else if (!result.subtitle) {
1079
- result.subtitle = text;
1080
- textBlockElements.delete(element);
1348
+ result.subtitle = name;
1349
+ nameSet.delete(name);
1081
1350
  }
1082
1351
  }
1083
1352
  if (!result.title && result.subtitle) {
1084
1353
  result.title = result.subtitle;
1085
1354
  delete result.subtitle;
1086
1355
  }
1087
- if (!result.title) {
1088
- result.title = "result";
1089
- }
1090
1356
  return result;
1091
1357
  }
1092
1358
 
1359
+ // Copyright (c) Microsoft Corporation.
1360
+ class ManifestUpdater {
1361
+ static async updateManifestWithAiPlugin(manifestPath, outputSpecPath, apiPluginFilePath, spec, options, authInfo) {
1362
+ const manifest = await fs.readJSON(manifestPath);
1363
+ const apiPluginRelativePath = ManifestUpdater.getRelativePath(manifestPath, apiPluginFilePath);
1364
+ manifest.plugins = [
1365
+ {
1366
+ file: apiPluginRelativePath,
1367
+ id: ConstantString.DefaultPluginId,
1368
+ },
1369
+ ];
1370
+ const appName = this.removeEnvs(manifest.name.short);
1371
+ ManifestUpdater.updateManifestDescription(manifest, spec);
1372
+ const specRelativePath = ManifestUpdater.getRelativePath(manifestPath, outputSpecPath);
1373
+ const apiPlugin = await ManifestUpdater.generatePluginManifestSchema(spec, specRelativePath, apiPluginFilePath, appName, authInfo, options);
1374
+ return [manifest, apiPlugin];
1375
+ }
1376
+ static updateManifestDescription(manifest, spec) {
1377
+ var _a, _b;
1378
+ manifest.description = {
1379
+ short: spec.info.title.slice(0, ConstantString.ShortDescriptionMaxLens),
1380
+ 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),
1381
+ };
1382
+ }
1383
+ static mapOpenAPISchemaToFuncParam(schema, method, pathUrl) {
1384
+ let parameter;
1385
+ if (schema.type === "array") {
1386
+ const items = schema.items;
1387
+ parameter = {
1388
+ type: "array",
1389
+ items: ManifestUpdater.mapOpenAPISchemaToFuncParam(items, method, pathUrl),
1390
+ };
1391
+ }
1392
+ else if (schema.type === "string" ||
1393
+ schema.type === "boolean" ||
1394
+ schema.type === "integer" ||
1395
+ schema.type === "number") {
1396
+ parameter = {
1397
+ type: schema.type,
1398
+ };
1399
+ }
1400
+ else {
1401
+ throw new SpecParserError(Utils.format(ConstantString.UnsupportedSchema, method, pathUrl, JSON.stringify(schema)), ErrorType.UpdateManifestFailed);
1402
+ }
1403
+ if (schema.enum) {
1404
+ parameter.enum = schema.enum;
1405
+ }
1406
+ if (schema.description) {
1407
+ parameter.description = schema.description;
1408
+ }
1409
+ if (schema.default) {
1410
+ parameter.default = schema.default;
1411
+ }
1412
+ return parameter;
1413
+ }
1414
+ static async generatePluginManifestSchema(spec, specRelativePath, apiPluginFilePath, appName, authInfo, options) {
1415
+ var _a, _b, _c, _d, _e;
1416
+ const functions = [];
1417
+ const functionNames = [];
1418
+ const conversationStarters = [];
1419
+ const paths = spec.paths;
1420
+ const pluginAuthObj = {
1421
+ type: "None",
1422
+ };
1423
+ if (authInfo) {
1424
+ if (Utils.isOAuthWithAuthCodeFlow(authInfo.authScheme)) {
1425
+ pluginAuthObj.type = "OAuthPluginVault";
1426
+ }
1427
+ else if (Utils.isBearerTokenAuth(authInfo.authScheme)) {
1428
+ pluginAuthObj.type = "ApiKeyPluginVault";
1429
+ }
1430
+ if (pluginAuthObj.type !== "None") {
1431
+ pluginAuthObj.reference_id = `${Utils.getSafeRegistrationIdEnvName(authInfo.name)}_REGISTRATION_ID`;
1432
+ }
1433
+ }
1434
+ for (const pathUrl in paths) {
1435
+ const pathItem = paths[pathUrl];
1436
+ if (pathItem) {
1437
+ const operations = pathItem;
1438
+ for (const method in operations) {
1439
+ if (options.allowMethods.includes(method)) {
1440
+ const operationItem = operations[method];
1441
+ const confirmationBodies = [];
1442
+ if (operationItem) {
1443
+ const operationId = operationItem.operationId;
1444
+ const description = (_a = operationItem.description) !== null && _a !== void 0 ? _a : "";
1445
+ const paramObject = operationItem.parameters;
1446
+ const requestBody = operationItem.requestBody;
1447
+ const parameters = {
1448
+ type: "object",
1449
+ properties: {},
1450
+ required: [],
1451
+ };
1452
+ if (paramObject) {
1453
+ for (let i = 0; i < paramObject.length; i++) {
1454
+ const param = paramObject[i];
1455
+ const schema = param.schema;
1456
+ parameters.properties[param.name] = ManifestUpdater.mapOpenAPISchemaToFuncParam(schema, method, pathUrl);
1457
+ confirmationBodies.push(ManifestUpdater.getConfirmationBodyItem(param.name));
1458
+ if (param.required) {
1459
+ parameters.required.push(param.name);
1460
+ }
1461
+ if (!parameters.properties[param.name].description) {
1462
+ parameters.properties[param.name].description = (_b = param.description) !== null && _b !== void 0 ? _b : "";
1463
+ }
1464
+ }
1465
+ }
1466
+ if (requestBody) {
1467
+ const requestJsonBody = requestBody.content["application/json"];
1468
+ const requestBodySchema = requestJsonBody.schema;
1469
+ if (requestBodySchema.type === "object") {
1470
+ if (requestBodySchema.required) {
1471
+ parameters.required.push(...requestBodySchema.required);
1472
+ }
1473
+ for (const property in requestBodySchema.properties) {
1474
+ const schema = requestBodySchema.properties[property];
1475
+ parameters.properties[property] = ManifestUpdater.mapOpenAPISchemaToFuncParam(schema, method, pathUrl);
1476
+ confirmationBodies.push(ManifestUpdater.getConfirmationBodyItem(property));
1477
+ }
1478
+ }
1479
+ else {
1480
+ throw new SpecParserError(Utils.format(ConstantString.UnsupportedSchema, method, pathUrl, JSON.stringify(requestBodySchema)), ErrorType.UpdateManifestFailed);
1481
+ }
1482
+ }
1483
+ const funcObj = {
1484
+ name: operationId,
1485
+ description: description,
1486
+ };
1487
+ if (paramObject || requestBody) {
1488
+ funcObj.parameters = parameters;
1489
+ }
1490
+ if (options.allowResponseSemantics) {
1491
+ const [card, jsonPath] = AdaptiveCardGenerator.generateAdaptiveCard(operationItem);
1492
+ const responseSemantic = wrapResponseSemantics(card, jsonPath);
1493
+ funcObj.capabilities = {
1494
+ response_semantics: responseSemantic,
1495
+ };
1496
+ }
1497
+ if (options.allowConfirmation && method !== ConstantString.GetMethod) {
1498
+ if (!funcObj.capabilities) {
1499
+ funcObj.capabilities = {};
1500
+ }
1501
+ funcObj.capabilities.confirmation = {
1502
+ type: "AdaptiveCard",
1503
+ title: (_c = operationItem.summary) !== null && _c !== void 0 ? _c : description,
1504
+ };
1505
+ if (confirmationBodies.length > 0) {
1506
+ funcObj.capabilities.confirmation.body = confirmationBodies.join("\n");
1507
+ }
1508
+ }
1509
+ functions.push(funcObj);
1510
+ functionNames.push(operationId);
1511
+ if (description) {
1512
+ conversationStarters.push(description);
1513
+ }
1514
+ }
1515
+ }
1516
+ }
1517
+ }
1518
+ }
1519
+ let apiPlugin;
1520
+ if (await fs.pathExists(apiPluginFilePath)) {
1521
+ apiPlugin = await fs.readJSON(apiPluginFilePath);
1522
+ }
1523
+ else {
1524
+ apiPlugin = {
1525
+ schema_version: "v2.1",
1526
+ name_for_human: "",
1527
+ description_for_human: "",
1528
+ namespace: "",
1529
+ functions: [],
1530
+ runtimes: [],
1531
+ };
1532
+ }
1533
+ apiPlugin.functions = apiPlugin.functions || [];
1534
+ for (const func of functions) {
1535
+ const index = (_d = apiPlugin.functions) === null || _d === void 0 ? void 0 : _d.findIndex((f) => f.name === func.name);
1536
+ if (index === -1) {
1537
+ apiPlugin.functions.push(func);
1538
+ }
1539
+ else {
1540
+ apiPlugin.functions[index] = func;
1541
+ }
1542
+ }
1543
+ apiPlugin.runtimes = apiPlugin.runtimes || [];
1544
+ const index = apiPlugin.runtimes.findIndex((runtime) => {
1545
+ var _a, _b;
1546
+ return runtime.spec.url === specRelativePath &&
1547
+ runtime.type === "OpenApi" &&
1548
+ ((_b = (_a = runtime.auth) === null || _a === void 0 ? void 0 : _a.type) !== null && _b !== void 0 ? _b : "None") === pluginAuthObj.type;
1549
+ });
1550
+ if (index === -1) {
1551
+ apiPlugin.runtimes.push({
1552
+ type: "OpenApi",
1553
+ auth: pluginAuthObj,
1554
+ spec: {
1555
+ url: specRelativePath,
1556
+ },
1557
+ run_for_functions: functionNames,
1558
+ });
1559
+ }
1560
+ else {
1561
+ apiPlugin.runtimes[index].run_for_functions = functionNames;
1562
+ }
1563
+ if (!apiPlugin.name_for_human) {
1564
+ apiPlugin.name_for_human = appName;
1565
+ }
1566
+ if (!apiPlugin.namespace) {
1567
+ apiPlugin.namespace = ManifestUpdater.removeAllSpecialCharacters(appName);
1568
+ }
1569
+ if (!apiPlugin.description_for_human) {
1570
+ apiPlugin.description_for_human =
1571
+ (_e = spec.info.description) !== null && _e !== void 0 ? _e : "<Please add description of the plugin>";
1572
+ }
1573
+ if (options.allowConversationStarters && conversationStarters.length > 0) {
1574
+ if (!apiPlugin.capabilities) {
1575
+ apiPlugin.capabilities = {
1576
+ localization: {},
1577
+ };
1578
+ }
1579
+ if (!apiPlugin.capabilities.conversation_starters) {
1580
+ apiPlugin.capabilities.conversation_starters = conversationStarters
1581
+ .slice(0, 5)
1582
+ .map((text) => ({ text }));
1583
+ }
1584
+ }
1585
+ return apiPlugin;
1586
+ }
1587
+ static async updateManifest(manifestPath, outputSpecPath, spec, options, adaptiveCardFolder, authInfo) {
1588
+ try {
1589
+ const originalManifest = await fs.readJSON(manifestPath);
1590
+ const updatedPart = {};
1591
+ updatedPart.composeExtensions = [];
1592
+ let warnings = [];
1593
+ if (options.projectType === ProjectType.SME) {
1594
+ const updateResult = await ManifestUpdater.generateCommands(spec, manifestPath, options, adaptiveCardFolder);
1595
+ const commands = updateResult[0];
1596
+ warnings = updateResult[1];
1597
+ const composeExtension = {
1598
+ composeExtensionType: "apiBased",
1599
+ apiSpecificationFile: ManifestUpdater.getRelativePath(manifestPath, outputSpecPath),
1600
+ commands: commands,
1601
+ };
1602
+ if (authInfo) {
1603
+ const auth = authInfo.authScheme;
1604
+ if (Utils.isAPIKeyAuth(auth) || Utils.isBearerTokenAuth(auth)) {
1605
+ const safeApiSecretRegistrationId = Utils.getSafeRegistrationIdEnvName(`${authInfo.name}_${ConstantString.RegistrationIdPostfix}`);
1606
+ composeExtension.authorization = {
1607
+ authType: "apiSecretServiceAuth",
1608
+ apiSecretServiceAuthConfiguration: {
1609
+ apiSecretRegistrationId: `\${{${safeApiSecretRegistrationId}}}`,
1610
+ },
1611
+ };
1612
+ }
1613
+ else if (Utils.isOAuthWithAuthCodeFlow(auth)) {
1614
+ const safeOAuth2RegistrationId = Utils.getSafeRegistrationIdEnvName(`${authInfo.name}_${ConstantString.OAuthRegistrationIdPostFix}`);
1615
+ composeExtension.authorization = {
1616
+ authType: "oAuth2.0",
1617
+ oAuthConfiguration: {
1618
+ oauthConfigurationId: `\${{${safeOAuth2RegistrationId}}}`,
1619
+ },
1620
+ };
1621
+ updatedPart.webApplicationInfo = {
1622
+ id: "${{AAD_APP_CLIENT_ID}}",
1623
+ resource: "api://${{DOMAIN}}/${{AAD_APP_CLIENT_ID}}",
1624
+ };
1625
+ }
1626
+ }
1627
+ updatedPart.composeExtensions = [composeExtension];
1628
+ }
1629
+ updatedPart.description = originalManifest.description;
1630
+ ManifestUpdater.updateManifestDescription(updatedPart, spec);
1631
+ const updatedManifest = Object.assign(Object.assign({}, originalManifest), updatedPart);
1632
+ return [updatedManifest, warnings];
1633
+ }
1634
+ catch (err) {
1635
+ throw new SpecParserError(err.toString(), ErrorType.UpdateManifestFailed);
1636
+ }
1637
+ }
1638
+ static async generateCommands(spec, manifestPath, options, adaptiveCardFolder) {
1639
+ var _a;
1640
+ const paths = spec.paths;
1641
+ const commands = [];
1642
+ const warnings = [];
1643
+ if (paths) {
1644
+ for (const pathUrl in paths) {
1645
+ const pathItem = paths[pathUrl];
1646
+ if (pathItem) {
1647
+ const operations = pathItem;
1648
+ // Currently only support GET and POST method
1649
+ for (const method in operations) {
1650
+ if ((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) {
1651
+ const operationItem = operations[method];
1652
+ if (operationItem) {
1653
+ const command = Utils.parseApiInfo(operationItem, options);
1654
+ if (command.parameters &&
1655
+ command.parameters.length >= 1 &&
1656
+ command.parameters.some((param) => param.isRequired)) {
1657
+ command.parameters = command.parameters.filter((param) => param.isRequired);
1658
+ }
1659
+ else if (command.parameters && command.parameters.length > 0) {
1660
+ command.parameters = [command.parameters[0]];
1661
+ warnings.push({
1662
+ type: WarningType.OperationOnlyContainsOptionalParam,
1663
+ content: Utils.format(ConstantString.OperationOnlyContainsOptionalParam, command.id),
1664
+ data: command.id,
1665
+ });
1666
+ }
1667
+ if (adaptiveCardFolder) {
1668
+ const adaptiveCardPath = path.join(adaptiveCardFolder, command.id + ".json");
1669
+ command.apiResponseRenderingTemplateFile = (await fs.pathExists(adaptiveCardPath))
1670
+ ? ManifestUpdater.getRelativePath(manifestPath, adaptiveCardPath)
1671
+ : "";
1672
+ }
1673
+ commands.push(command);
1674
+ }
1675
+ }
1676
+ }
1677
+ }
1678
+ }
1679
+ }
1680
+ return [commands, warnings];
1681
+ }
1682
+ static getRelativePath(from, to) {
1683
+ const relativePath = path.relative(path.dirname(from), to);
1684
+ return path.normalize(relativePath).replace(/\\/g, "/");
1685
+ }
1686
+ static removeEnvs(str) {
1687
+ const placeHolderReg = /\${{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*}}/g;
1688
+ const matches = placeHolderReg.exec(str);
1689
+ let newStr = str;
1690
+ if (matches != null) {
1691
+ newStr = newStr.replace(matches[0], "");
1692
+ }
1693
+ return newStr;
1694
+ }
1695
+ static removeAllSpecialCharacters(str) {
1696
+ return str.toLowerCase().replace(/[^a-z0-9]/g, "");
1697
+ }
1698
+ static getConfirmationBodyItem(paramName) {
1699
+ return `* **${Utils.updateFirstLetter(paramName)}**: {{function.parameters.${paramName}}}`;
1700
+ }
1701
+ }
1702
+
1093
1703
  // Copyright (c) Microsoft Corporation.
1094
1704
  /**
1095
1705
  * A class that parses an OpenAPI specification file and provides methods to validate, list, and generate artifacts.
@@ -1105,8 +1715,14 @@ class SpecParser {
1105
1715
  allowMissingId: true,
1106
1716
  allowSwagger: true,
1107
1717
  allowAPIKeyAuth: false,
1718
+ allowBearerTokenAuth: false,
1108
1719
  allowMultipleParameters: false,
1109
1720
  allowOauth2: false,
1721
+ allowMethods: ["get", "post"],
1722
+ allowConversationStarters: false,
1723
+ allowResponseSemantics: false,
1724
+ allowConfirmation: false,
1725
+ projectType: ProjectType.SME,
1110
1726
  };
1111
1727
  this.pathOrSpec = pathOrDoc;
1112
1728
  this.parser = new SwaggerParser();
@@ -1130,6 +1746,8 @@ class SpecParser {
1130
1746
  errors: [{ type: ErrorType.SpecNotValid, content: e.toString() }],
1131
1747
  };
1132
1748
  }
1749
+ const errors = [];
1750
+ const warnings = [];
1133
1751
  if (!this.options.allowSwagger && this.isSwaggerFile) {
1134
1752
  return {
1135
1753
  status: ValidationStatus.Error,
@@ -1139,7 +1757,38 @@ class SpecParser {
1139
1757
  ],
1140
1758
  };
1141
1759
  }
1142
- return Utils.validateSpec(this.spec, this.parser, !!this.isSwaggerFile, this.options.allowMissingId, this.options.allowAPIKeyAuth, this.options.allowMultipleParameters, this.options.allowOauth2);
1760
+ // Remote reference not supported
1761
+ const refPaths = this.parser.$refs.paths();
1762
+ // refPaths [0] is the current spec file path
1763
+ if (refPaths.length > 1) {
1764
+ errors.push({
1765
+ type: ErrorType.RemoteRefNotSupported,
1766
+ content: Utils.format(ConstantString.RemoteRefNotSupported, refPaths.join(", ")),
1767
+ data: refPaths,
1768
+ });
1769
+ }
1770
+ if (!!this.isSwaggerFile && this.options.allowSwagger) {
1771
+ warnings.push({
1772
+ type: WarningType.ConvertSwaggerToOpenAPI,
1773
+ content: ConstantString.ConvertSwaggerToOpenAPI,
1774
+ });
1775
+ }
1776
+ const validator = this.getValidator(this.spec);
1777
+ const validationResult = validator.validateSpec();
1778
+ warnings.push(...validationResult.warnings);
1779
+ errors.push(...validationResult.errors);
1780
+ let status = ValidationStatus.Valid;
1781
+ if (warnings.length > 0 && errors.length === 0) {
1782
+ status = ValidationStatus.Warning;
1783
+ }
1784
+ else if (errors.length > 0) {
1785
+ status = ValidationStatus.Error;
1786
+ }
1787
+ return {
1788
+ status: status,
1789
+ warnings: warnings,
1790
+ errors: errors,
1791
+ };
1143
1792
  }
1144
1793
  catch (err) {
1145
1794
  throw new SpecParserError(err.toString(), ErrorType.ValidateFailed);
@@ -1159,39 +1808,40 @@ class SpecParser {
1159
1808
  try {
1160
1809
  await this.loadSpec();
1161
1810
  const spec = this.spec;
1162
- const apiMap = this.getAllSupportedAPIs(spec);
1163
- const result = [];
1811
+ const apiMap = this.getAPIs(spec);
1812
+ const result = {
1813
+ APIs: [],
1814
+ allAPICount: 0,
1815
+ validAPICount: 0,
1816
+ };
1164
1817
  for (const apiKey in apiMap) {
1818
+ const { operation, isValid, reason } = apiMap[apiKey];
1819
+ const [method, path] = apiKey.split(" ");
1820
+ const operationId = (_a = operation.operationId) !== null && _a !== void 0 ? _a : `${method.toLowerCase()}${Utils.convertPathToCamelCase(path)}`;
1165
1821
  const apiResult = {
1166
- api: "",
1822
+ api: apiKey,
1167
1823
  server: "",
1168
- operationId: "",
1824
+ operationId: operationId,
1825
+ isValid: isValid,
1826
+ reason: reason,
1169
1827
  };
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;
1828
+ if (isValid) {
1829
+ const serverObj = Utils.getServerObject(spec, method.toLocaleLowerCase(), path);
1830
+ if (serverObj) {
1831
+ apiResult.server = Utils.resolveEnv(serverObj.url);
1832
+ }
1833
+ const authArray = Utils.getAuthArray(operation.security, spec);
1834
+ for (const auths of authArray) {
1835
+ if (auths.length === 1) {
1836
+ apiResult.auth = auths[0];
1837
+ break;
1838
+ }
1190
1839
  }
1191
1840
  }
1192
- apiResult.api = apiKey;
1193
- result.push(apiResult);
1841
+ result.APIs.push(apiResult);
1194
1842
  }
1843
+ result.allAPICount = result.APIs.length;
1844
+ result.validAPICount = result.APIs.filter((api) => api.isValid).length;
1195
1845
  return result;
1196
1846
  }
1197
1847
  catch (err) {
@@ -1214,7 +1864,7 @@ class SpecParser {
1214
1864
  if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
1215
1865
  throw new SpecParserError(ConstantString.CancelledMessage, ErrorType.Cancelled);
1216
1866
  }
1217
- const newUnResolvedSpec = SpecFilter.specFilter(filter, this.unResolveSpec, this.spec, this.options.allowMissingId, this.options.allowAPIKeyAuth, this.options.allowMultipleParameters, this.options.allowOauth2);
1867
+ const newUnResolvedSpec = SpecFilter.specFilter(filter, this.unResolveSpec, this.spec, this.options);
1218
1868
  if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
1219
1869
  throw new SpecParserError(ConstantString.CancelledMessage, ErrorType.Cancelled);
1220
1870
  }
@@ -1233,10 +1883,9 @@ class SpecParser {
1233
1883
  * @param manifestPath A file path of the Teams app manifest file to update.
1234
1884
  * @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
1885
  * @param outputSpecPath File path of the new OpenAPI specification file to generate. If not specified or empty, no spec file will be generated.
1236
- * @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.
1886
+ * @param pluginFilePath File path of the api plugin file to generate.
1238
1887
  */
1239
- async generate(manifestPath, filter, outputSpecPath, adaptiveCardFolder, signal, isMe) {
1888
+ async generateForCopilot(manifestPath, filter, outputSpecPath, pluginFilePath, signal) {
1240
1889
  const result = {
1241
1890
  allSuccess: true,
1242
1891
  warnings: [],
@@ -1245,52 +1894,66 @@ class SpecParser {
1245
1894
  const newSpecs = await this.getFilteredSpecs(filter, signal);
1246
1895
  const newUnResolvedSpec = newSpecs[0];
1247
1896
  const newSpec = newSpecs[1];
1248
- const AuthSet = new Set();
1249
- let hasMultipleAPIKeyAuth = false;
1250
- for (const url in newSpec.paths) {
1251
- for (const method in newSpec.paths[url]) {
1252
- const operation = newSpec.paths[url][method];
1253
- const authArray = Utils.getAuthArray(operation.security, newSpec);
1254
- if (authArray && authArray.length > 0) {
1255
- AuthSet.add(authArray[0][0].authSchema);
1256
- if (AuthSet.size > 1) {
1257
- hasMultipleAPIKeyAuth = true;
1258
- break;
1259
- }
1260
- }
1261
- }
1897
+ const authInfo = Utils.getAuthInfo(newSpec);
1898
+ await this.saveFilterSpec(outputSpecPath, newUnResolvedSpec);
1899
+ if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
1900
+ throw new SpecParserError(ConstantString.CancelledMessage, ErrorType.Cancelled);
1262
1901
  }
1263
- if (hasMultipleAPIKeyAuth) {
1264
- throw new SpecParserError(ConstantString.MultipleAPIKeyNotSupported, ErrorType.MultipleAPIKeyNotSupported);
1902
+ const [updatedManifest, apiPlugin] = await ManifestUpdater.updateManifestWithAiPlugin(manifestPath, outputSpecPath, pluginFilePath, newSpec, this.options, authInfo);
1903
+ await fs.outputJSON(manifestPath, updatedManifest, { spaces: 2 });
1904
+ await fs.outputJSON(pluginFilePath, apiPlugin, { spaces: 2 });
1905
+ }
1906
+ catch (err) {
1907
+ if (err instanceof SpecParserError) {
1908
+ throw err;
1265
1909
  }
1266
- let resultStr;
1267
- if (outputSpecPath.endsWith(".yaml") || outputSpecPath.endsWith(".yml")) {
1268
- resultStr = jsyaml.dump(newUnResolvedSpec);
1910
+ throw new SpecParserError(err.toString(), ErrorType.GenerateFailed);
1911
+ }
1912
+ return result;
1913
+ }
1914
+ /**
1915
+ * Generates and update artifacts from the OpenAPI specification file. Generate Adaptive Cards, update Teams app manifest, and generate a new OpenAPI specification file.
1916
+ * @param manifestPath A file path of the Teams app manifest file to update.
1917
+ * @param filter An array of strings that represent the filters to apply when generating the artifacts. If filter is empty, it would process nothing.
1918
+ * @param outputSpecPath File path of the new OpenAPI specification file to generate. If not specified or empty, no spec file will be generated.
1919
+ * @param adaptiveCardFolder Folder path where the Adaptive Card files will be generated. If not specified or empty, Adaptive Card files will not be generated.
1920
+ */
1921
+ async generate(manifestPath, filter, outputSpecPath, adaptiveCardFolder, signal) {
1922
+ const result = {
1923
+ allSuccess: true,
1924
+ warnings: [],
1925
+ };
1926
+ try {
1927
+ const newSpecs = await this.getFilteredSpecs(filter, signal);
1928
+ const newUnResolvedSpec = newSpecs[0];
1929
+ const newSpec = newSpecs[1];
1930
+ let authInfo = undefined;
1931
+ if (this.options.projectType === ProjectType.SME) {
1932
+ authInfo = Utils.getAuthInfo(newSpec);
1269
1933
  }
1270
- else {
1271
- resultStr = JSON.stringify(newUnResolvedSpec, null, 2);
1272
- }
1273
- 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
- });
1934
+ await this.saveFilterSpec(outputSpecPath, newUnResolvedSpec);
1935
+ if (adaptiveCardFolder) {
1936
+ for (const url in newSpec.paths) {
1937
+ for (const method in newSpec.paths[url]) {
1938
+ // paths object may contain description/summary which is not a http method, so we need to check if it is a operation object
1939
+ if (this.options.allowMethods.includes(method)) {
1940
+ const operation = newSpec.paths[url][method];
1941
+ try {
1942
+ const [card, jsonPath] = AdaptiveCardGenerator.generateAdaptiveCard(operation);
1943
+ const fileName = path.join(adaptiveCardFolder, `${operation.operationId}.json`);
1944
+ const wrappedCard = wrapAdaptiveCard(card, jsonPath);
1945
+ await fs.outputJSON(fileName, wrappedCard, { spaces: 2 });
1946
+ const dataFileName = path.join(adaptiveCardFolder, `${operation.operationId}.data.json`);
1947
+ await fs.outputJSON(dataFileName, {}, { spaces: 2 });
1948
+ }
1949
+ catch (err) {
1950
+ result.allSuccess = false;
1951
+ result.warnings.push({
1952
+ type: WarningType.GenerateCardFailed,
1953
+ content: err.toString(),
1954
+ data: operation.operationId,
1955
+ });
1956
+ }
1294
1957
  }
1295
1958
  }
1296
1959
  }
@@ -1298,8 +1961,7 @@ class SpecParser {
1298
1961
  if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
1299
1962
  throw new SpecParserError(ConstantString.CancelledMessage, ErrorType.Cancelled);
1300
1963
  }
1301
- const auth = Array.from(AuthSet)[0];
1302
- const [updatedManifest, warnings] = await ManifestUpdater.updateManifest(manifestPath, outputSpecPath, adaptiveCardFolder, newSpec, this.options.allowMultipleParameters, auth, isMe);
1964
+ const [updatedManifest, warnings] = await ManifestUpdater.updateManifest(manifestPath, outputSpecPath, newSpec, this.options, adaptiveCardFolder, authInfo);
1303
1965
  await fs.outputJSON(manifestPath, updatedManifest, { spaces: 2 });
1304
1966
  result.warnings.push(...warnings);
1305
1967
  }
@@ -1324,15 +1986,30 @@ class SpecParser {
1324
1986
  this.spec = (await this.parser.dereference(clonedUnResolveSpec));
1325
1987
  }
1326
1988
  }
1327
- getAllSupportedAPIs(spec) {
1328
- if (this.apiMap !== undefined) {
1329
- return this.apiMap;
1989
+ getAPIs(spec) {
1990
+ const validator = this.getValidator(spec);
1991
+ const apiMap = validator.listAPIs();
1992
+ return apiMap;
1993
+ }
1994
+ getValidator(spec) {
1995
+ if (this.validator) {
1996
+ return this.validator;
1330
1997
  }
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;
1998
+ const validator = ValidatorFactory.create(spec, this.options);
1999
+ this.validator = validator;
2000
+ return validator;
2001
+ }
2002
+ async saveFilterSpec(outputSpecPath, unResolvedSpec) {
2003
+ let resultStr;
2004
+ if (outputSpecPath.endsWith(".yaml") || outputSpecPath.endsWith(".yml")) {
2005
+ resultStr = jsyaml.dump(unResolvedSpec);
2006
+ }
2007
+ else {
2008
+ resultStr = JSON.stringify(unResolvedSpec, null, 2);
2009
+ }
2010
+ await fs.outputFile(outputSpecPath, resultStr);
1334
2011
  }
1335
2012
  }
1336
2013
 
1337
- export { AdaptiveCardGenerator, ConstantString, ErrorType, SpecParser, SpecParserError, Utils, ValidationStatus, WarningType };
2014
+ export { AdaptiveCardGenerator, ConstantString, ErrorType, ProjectType, SpecParser, SpecParserError, Utils, ValidationStatus, WarningType };
1338
2015
  //# sourceMappingURL=index.esm2017.mjs.map