@microsoft/m365-spec-parser 0.1.0 → 0.1.1-alpha.039039fab.0

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