@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.
@@ -45,7 +45,8 @@ var ErrorType;
45
45
  ErrorType["NoExtraAPICanBeAdded"] = "no-extra-api-can-be-added";
46
46
  ErrorType["ResolveServerUrlFailed"] = "resolve-server-url-failed";
47
47
  ErrorType["SwaggerNotSupported"] = "swagger-not-supported";
48
- ErrorType["MultipleAPIKeyNotSupported"] = "multiple-api-key-not-supported";
48
+ ErrorType["MultipleAuthNotSupported"] = "multiple-auth-not-supported";
49
+ ErrorType["SpecVersionNotSupported"] = "spec-version-not-supported";
49
50
  ErrorType["ListFailed"] = "list-failed";
50
51
  ErrorType["listSupportedAPIInfoFailed"] = "list-supported-api-info-failed";
51
52
  ErrorType["FilterSpecFailed"] = "filter-spec-failed";
@@ -53,6 +54,22 @@ var ErrorType;
53
54
  ErrorType["GenerateAdaptiveCardFailed"] = "generate-adaptive-card-failed";
54
55
  ErrorType["GenerateFailed"] = "generate-failed";
55
56
  ErrorType["ValidateFailed"] = "validate-failed";
57
+ ErrorType["GetSpecFailed"] = "get-spec-failed";
58
+ ErrorType["AuthTypeIsNotSupported"] = "auth-type-is-not-supported";
59
+ ErrorType["MissingOperationId"] = "missing-operation-id";
60
+ ErrorType["PostBodyContainMultipleMediaTypes"] = "post-body-contain-multiple-media-types";
61
+ ErrorType["ResponseContainMultipleMediaTypes"] = "response-contain-multiple-media-types";
62
+ ErrorType["ResponseJsonIsEmpty"] = "response-json-is-empty";
63
+ ErrorType["PostBodySchemaIsNotJson"] = "post-body-schema-is-not-json";
64
+ ErrorType["PostBodyContainsRequiredUnsupportedSchema"] = "post-body-contains-required-unsupported-schema";
65
+ ErrorType["ParamsContainRequiredUnsupportedSchema"] = "params-contain-required-unsupported-schema";
66
+ ErrorType["ParamsContainsNestedObject"] = "params-contains-nested-object";
67
+ ErrorType["RequestBodyContainsNestedObject"] = "request-body-contains-nested-object";
68
+ ErrorType["ExceededRequiredParamsLimit"] = "exceeded-required-params-limit";
69
+ ErrorType["NoParameter"] = "no-parameter";
70
+ ErrorType["NoAPIInfo"] = "no-api-info";
71
+ ErrorType["MethodNotAllowed"] = "method-not-allowed";
72
+ ErrorType["UrlPathNotExist"] = "url-path-not-exist";
56
73
  ErrorType["Cancelled"] = "cancelled";
57
74
  ErrorType["Unknown"] = "unknown";
58
75
  })(ErrorType || (ErrorType = {}));
@@ -75,7 +92,13 @@ var ValidationStatus;
75
92
  ValidationStatus[ValidationStatus["Valid"] = 0] = "Valid";
76
93
  ValidationStatus[ValidationStatus["Warning"] = 1] = "Warning";
77
94
  ValidationStatus[ValidationStatus["Error"] = 2] = "Error";
78
- })(ValidationStatus || (ValidationStatus = {}));
95
+ })(ValidationStatus || (ValidationStatus = {}));
96
+ var ProjectType;
97
+ (function (ProjectType) {
98
+ ProjectType[ProjectType["Copilot"] = 0] = "Copilot";
99
+ ProjectType[ProjectType["SME"] = 1] = "SME";
100
+ ProjectType[ProjectType["TeamsAi"] = 2] = "TeamsAi";
101
+ })(ProjectType || (ProjectType = {}));
79
102
 
80
103
  // Copyright (c) Microsoft Corporation.
81
104
  class SpecParserError extends Error {
@@ -94,7 +117,7 @@ ConstantString.RemoteRefNotSupported = "Remote reference is not supported: %s.";
94
117
  ConstantString.MissingOperationId = "Missing operationIds: %s.";
95
118
  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.";
96
119
  ConstantString.AdditionalPropertiesNotSupported = "'additionalProperties' is not supported, and will be ignored.";
97
- ConstantString.SchemaNotSupported = "'oneOf', 'anyOf', and 'not' schema are not supported: %s.";
120
+ ConstantString.SchemaNotSupported = "'oneOf', 'allOf', 'anyOf', and 'not' schema are not supported: %s.";
98
121
  ConstantString.UnknownSchema = "Unknown schema: %s.";
99
122
  ConstantString.UrlProtocolNotSupported = "Server url is not correct: protocol %s is not supported, you should use https protocol instead.";
100
123
  ConstantString.RelativeServerUrlNotSupported = "Server url is not correct: relative server url is not supported.";
@@ -102,7 +125,9 @@ ConstantString.ResolveServerUrlFailed = "Unable to resolve the server URL: pleas
102
125
  ConstantString.OperationOnlyContainsOptionalParam = "Operation %s contains multiple optional parameters. The first optional parameter is used for this command.";
103
126
  ConstantString.ConvertSwaggerToOpenAPI = "The Swagger 2.0 file has been converted to OpenAPI 3.0.";
104
127
  ConstantString.SwaggerNotSupported = "Swagger 2.0 is not supported. Please convert to OpenAPI 3.0 manually before proceeding.";
105
- ConstantString.MultipleAPIKeyNotSupported = "Multiple API keys are not supported. Please make sure that all selected APIs use the same API key.";
128
+ ConstantString.SpecVersionNotSupported = "Unsupported OpenAPI version %s. Please use version 3.0.x.";
129
+ ConstantString.MultipleAuthNotSupported = "Multiple authentication methods are unsupported. Ensure all selected APIs use identical authentication.";
130
+ ConstantString.UnsupportedSchema = "Unsupported schema in %s %s: %s";
106
131
  ConstantString.WrappedCardVersion = "devPreview";
107
132
  ConstantString.WrappedCardSchema = "https://developer.microsoft.com/json-schemas/teams/vDevPreview/MicrosoftTeams.ResponseRenderingTemplate.schema.json";
108
133
  ConstantString.WrappedCardResponseLayout = "list";
@@ -112,8 +137,14 @@ ConstantString.AdaptiveCardVersion = "1.5";
112
137
  ConstantString.AdaptiveCardSchema = "http://adaptivecards.io/schemas/adaptive-card.json";
113
138
  ConstantString.AdaptiveCardType = "AdaptiveCard";
114
139
  ConstantString.TextBlockType = "TextBlock";
140
+ ConstantString.ImageType = "Image";
115
141
  ConstantString.ContainerType = "Container";
116
- ConstantString.RegistrationIdPostfix = "REGISTRATION_ID";
142
+ ConstantString.RegistrationIdPostfix = {
143
+ apiKey: "REGISTRATION_ID",
144
+ oauth2: "CONFIGURATION_ID",
145
+ http: "REGISTRATION_ID",
146
+ openIdConnect: "REGISTRATION_ID",
147
+ };
117
148
  ConstantString.ResponseCodeFor20X = [
118
149
  "200",
119
150
  "201",
@@ -172,219 +203,52 @@ ConstantString.ShortDescriptionMaxLens = 80;
172
203
  ConstantString.FullDescriptionMaxLens = 4000;
173
204
  ConstantString.CommandDescriptionMaxLens = 128;
174
205
  ConstantString.ParameterDescriptionMaxLens = 128;
206
+ ConstantString.ConversationStarterMaxLens = 50;
175
207
  ConstantString.CommandTitleMaxLens = 32;
176
- ConstantString.ParameterTitleMaxLens = 32;
208
+ ConstantString.ParameterTitleMaxLens = 32;
209
+ ConstantString.SMERequiredParamsMaxNum = 5;
210
+ ConstantString.DefaultPluginId = "plugin_1";
177
211
 
178
212
  // Copyright (c) Microsoft Corporation.
179
213
  class Utils {
180
- static checkParameters(paramObject) {
181
- const paramResult = {
182
- requiredNum: 0,
183
- optionalNum: 0,
184
- isValid: true,
185
- };
186
- if (!paramObject) {
187
- return paramResult;
188
- }
189
- for (let i = 0; i < paramObject.length; i++) {
190
- const param = paramObject[i];
191
- const schema = param.schema;
192
- const isRequiredWithoutDefault = param.required && schema.default === undefined;
193
- if (param.in === "header" || param.in === "cookie") {
194
- if (isRequiredWithoutDefault) {
195
- paramResult.isValid = false;
196
- }
197
- continue;
198
- }
199
- if (schema.type !== "boolean" &&
200
- schema.type !== "string" &&
201
- schema.type !== "number" &&
202
- schema.type !== "integer") {
203
- if (isRequiredWithoutDefault) {
204
- paramResult.isValid = false;
205
- }
206
- continue;
207
- }
208
- if (param.in === "query" || param.in === "path") {
209
- if (isRequiredWithoutDefault) {
210
- paramResult.requiredNum = paramResult.requiredNum + 1;
211
- }
212
- else {
213
- paramResult.optionalNum = paramResult.optionalNum + 1;
214
- }
215
- }
216
- }
217
- return paramResult;
218
- }
219
- static checkPostBody(schema, isRequired = false) {
220
- var _a;
221
- const paramResult = {
222
- requiredNum: 0,
223
- optionalNum: 0,
224
- isValid: true,
225
- };
226
- if (Object.keys(schema).length === 0) {
227
- return paramResult;
228
- }
229
- const isRequiredWithoutDefault = isRequired && schema.default === undefined;
230
- if (schema.type === "string" ||
231
- schema.type === "integer" ||
232
- schema.type === "boolean" ||
233
- schema.type === "number") {
234
- if (isRequiredWithoutDefault) {
235
- paramResult.requiredNum = paramResult.requiredNum + 1;
236
- }
237
- else {
238
- paramResult.optionalNum = paramResult.optionalNum + 1;
239
- }
240
- }
241
- else if (schema.type === "object") {
242
- const { properties } = schema;
243
- for (const property in properties) {
244
- let isRequired = false;
245
- if (schema.required && ((_a = schema.required) === null || _a === void 0 ? void 0 : _a.indexOf(property)) >= 0) {
246
- isRequired = true;
247
- }
248
- const result = Utils.checkPostBody(properties[property], isRequired);
249
- paramResult.requiredNum += result.requiredNum;
250
- paramResult.optionalNum += result.optionalNum;
251
- paramResult.isValid = paramResult.isValid && result.isValid;
252
- }
253
- }
254
- else {
255
- if (isRequiredWithoutDefault) {
256
- paramResult.isValid = false;
257
- }
258
- }
259
- return paramResult;
260
- }
261
- /**
262
- * Checks if the given API is supported.
263
- * @param {string} method - The HTTP method of the API.
264
- * @param {string} path - The path of the API.
265
- * @param {OpenAPIV3.Document} spec - The OpenAPI specification document.
266
- * @returns {boolean} - Returns true if the API is supported, false otherwise.
267
- * @description The following APIs are supported:
268
- * 1. only support Get/Post operation without auth property
269
- * 2. parameter inside query or path only support string, number, boolean and integer
270
- * 3. parameter inside post body only support string, number, boolean, integer and object
271
- * 4. request body + required parameters <= 1
272
- * 5. response body should be “application/json” and not empty, and response code should be 20X
273
- * 6. only support request body with “application/json” content type
274
- */
275
- static isSupportedApi(method, path, spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2) {
276
- const pathObj = spec.paths[path];
277
- method = method.toLocaleLowerCase();
278
- if (pathObj) {
279
- if ((method === ConstantString.PostMethod || method === ConstantString.GetMethod) &&
280
- pathObj[method]) {
281
- const securities = pathObj[method].security;
282
- const authArray = Utils.getAuthArray(securities, spec);
283
- if (!Utils.isSupportedAuth(authArray, allowAPIKeyAuth, allowOauth2)) {
284
- return false;
285
- }
286
- const operationObject = pathObj[method];
287
- if (!allowMissingId && !operationObject.operationId) {
288
- return false;
289
- }
290
- const paramObject = operationObject.parameters;
291
- const requestBody = operationObject.requestBody;
292
- const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
293
- const mediaTypesCount = Object.keys((requestBody === null || requestBody === void 0 ? void 0 : requestBody.content) || {}).length;
294
- if (mediaTypesCount > 1) {
295
- return false;
296
- }
297
- const responseJson = Utils.getResponseJson(operationObject);
298
- if (Object.keys(responseJson).length === 0) {
299
- return false;
300
- }
301
- let requestBodyParamResult = {
302
- requiredNum: 0,
303
- optionalNum: 0,
304
- isValid: true,
305
- };
306
- if (requestJsonBody) {
307
- const requestBodySchema = requestJsonBody.schema;
308
- requestBodyParamResult = Utils.checkPostBody(requestBodySchema, requestBody.required);
309
- }
310
- if (!requestBodyParamResult.isValid) {
311
- return false;
312
- }
313
- const paramResult = Utils.checkParameters(paramObject);
314
- if (!paramResult.isValid) {
315
- return false;
316
- }
317
- if (requestBodyParamResult.requiredNum + paramResult.requiredNum > 1) {
318
- if (allowMultipleParameters &&
319
- requestBodyParamResult.requiredNum + paramResult.requiredNum <= 5) {
320
- return true;
321
- }
322
- return false;
323
- }
324
- else if (requestBodyParamResult.requiredNum +
325
- requestBodyParamResult.optionalNum +
326
- paramResult.requiredNum +
327
- paramResult.optionalNum ===
328
- 0) {
329
- return false;
330
- }
331
- else {
214
+ static hasNestedObjectInSchema(schema) {
215
+ if (schema.type === "object") {
216
+ for (const property in schema.properties) {
217
+ const nestedSchema = schema.properties[property];
218
+ if (nestedSchema.type === "object") {
332
219
  return true;
333
220
  }
334
221
  }
335
222
  }
336
223
  return false;
337
224
  }
338
- static isSupportedAuth(authSchemaArray, allowAPIKeyAuth, allowOauth2) {
339
- if (authSchemaArray.length === 0) {
340
- return true;
341
- }
342
- if (allowAPIKeyAuth || allowOauth2) {
343
- // Currently we don't support multiple auth in one operation
344
- if (authSchemaArray.length > 0 && authSchemaArray.every((auths) => auths.length > 1)) {
345
- return false;
346
- }
347
- for (const auths of authSchemaArray) {
348
- if (auths.length === 1) {
349
- if (!allowOauth2 && allowAPIKeyAuth && Utils.isAPIKeyAuth(auths[0].authSchema)) {
350
- return true;
351
- }
352
- else if (!allowAPIKeyAuth &&
353
- allowOauth2 &&
354
- Utils.isBearerTokenAuth(auths[0].authSchema)) {
355
- return true;
356
- }
357
- else if (allowAPIKeyAuth &&
358
- allowOauth2 &&
359
- (Utils.isAPIKeyAuth(auths[0].authSchema) ||
360
- Utils.isBearerTokenAuth(auths[0].authSchema))) {
361
- return true;
362
- }
363
- }
364
- }
365
- }
366
- return false;
225
+ static containMultipleMediaTypes(bodyObject) {
226
+ return Object.keys((bodyObject === null || bodyObject === void 0 ? void 0 : bodyObject.content) || {}).length > 1;
227
+ }
228
+ static isBearerTokenAuth(authScheme) {
229
+ return authScheme.type === "http" && authScheme.scheme === "bearer";
367
230
  }
368
- static isAPIKeyAuth(authSchema) {
369
- return authSchema.type === "apiKey";
231
+ static isAPIKeyAuth(authScheme) {
232
+ return authScheme.type === "apiKey";
370
233
  }
371
- static isBearerTokenAuth(authSchema) {
372
- return (authSchema.type === "oauth2" ||
373
- authSchema.type === "openIdConnect" ||
374
- (authSchema.type === "http" && authSchema.scheme === "bearer"));
234
+ static isOAuthWithAuthCodeFlow(authScheme) {
235
+ return !!(authScheme.type === "oauth2" &&
236
+ authScheme.flows &&
237
+ authScheme.flows.authorizationCode);
375
238
  }
376
239
  static getAuthArray(securities, spec) {
377
240
  var _a;
378
241
  const result = [];
379
242
  const securitySchemas = (_a = spec.components) === null || _a === void 0 ? void 0 : _a.securitySchemes;
380
- if (securities && securitySchemas) {
381
- for (let i = 0; i < securities.length; i++) {
382
- const security = securities[i];
243
+ const securitiesArr = securities !== null && securities !== void 0 ? securities : spec.security;
244
+ if (securitiesArr && securitySchemas) {
245
+ for (let i = 0; i < securitiesArr.length; i++) {
246
+ const security = securitiesArr[i];
383
247
  const authArray = [];
384
248
  for (const name in security) {
385
249
  const auth = securitySchemas[name];
386
250
  authArray.push({
387
- authSchema: auth,
251
+ authScheme: auth,
388
252
  name: name,
389
253
  });
390
254
  }
@@ -396,24 +260,47 @@ class Utils {
396
260
  result.sort((a, b) => a[0].name.localeCompare(b[0].name));
397
261
  return result;
398
262
  }
263
+ static getAuthInfo(spec) {
264
+ let authInfo = undefined;
265
+ for (const url in spec.paths) {
266
+ for (const method in spec.paths[url]) {
267
+ const operation = spec.paths[url][method];
268
+ const authArray = Utils.getAuthArray(operation.security, spec);
269
+ if (authArray && authArray.length > 0) {
270
+ const currentAuth = authArray[0][0];
271
+ if (!authInfo) {
272
+ authInfo = authArray[0][0];
273
+ }
274
+ else if (authInfo.name !== currentAuth.name) {
275
+ throw new SpecParserError(ConstantString.MultipleAuthNotSupported, ErrorType.MultipleAuthNotSupported);
276
+ }
277
+ }
278
+ }
279
+ }
280
+ return authInfo;
281
+ }
399
282
  static updateFirstLetter(str) {
400
283
  return str.charAt(0).toUpperCase() + str.slice(1);
401
284
  }
402
285
  static getResponseJson(operationObject) {
403
286
  var _a, _b;
404
287
  let json = {};
288
+ let multipleMediaType = false;
405
289
  for (const code of ConstantString.ResponseCodeFor20X) {
406
290
  const responseObject = (_a = operationObject === null || operationObject === void 0 ? void 0 : operationObject.responses) === null || _a === void 0 ? void 0 : _a[code];
407
- const mediaTypesCount = Object.keys((responseObject === null || responseObject === void 0 ? void 0 : responseObject.content) || {}).length;
408
- if (mediaTypesCount > 1) {
409
- return {};
410
- }
411
291
  if ((_b = responseObject === null || responseObject === void 0 ? void 0 : responseObject.content) === null || _b === void 0 ? void 0 : _b["application/json"]) {
292
+ multipleMediaType = false;
412
293
  json = responseObject.content["application/json"];
413
- break;
294
+ if (Utils.containMultipleMediaTypes(responseObject)) {
295
+ multipleMediaType = true;
296
+ json = {};
297
+ }
298
+ else {
299
+ break;
300
+ }
414
301
  }
415
302
  }
416
- return json;
303
+ return { json, multipleMediaType };
417
304
  }
418
305
  static convertPathToCamelCase(path) {
419
306
  const pathSegments = path.split(/[./{]/);
@@ -433,10 +320,10 @@ class Utils {
433
320
  return undefined;
434
321
  }
435
322
  }
436
- static resolveServerUrl(url) {
323
+ static resolveEnv(str) {
437
324
  const placeHolderReg = /\${{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*}}/g;
438
- let matches = placeHolderReg.exec(url);
439
- let newUrl = url;
325
+ let matches = placeHolderReg.exec(str);
326
+ let newStr = str;
440
327
  while (matches != null) {
441
328
  const envVar = matches[1];
442
329
  const envVal = process.env[envVar];
@@ -444,17 +331,17 @@ class Utils {
444
331
  throw new Error(Utils.format(ConstantString.ResolveServerUrlFailed, envVar));
445
332
  }
446
333
  else {
447
- newUrl = newUrl.replace(matches[0], envVal);
334
+ newStr = newStr.replace(matches[0], envVal);
448
335
  }
449
- matches = placeHolderReg.exec(url);
336
+ matches = placeHolderReg.exec(str);
450
337
  }
451
- return newUrl;
338
+ return newStr;
452
339
  }
453
340
  static checkServerUrl(servers) {
454
341
  const errors = [];
455
342
  let serverUrl;
456
343
  try {
457
- serverUrl = Utils.resolveServerUrl(servers[0].url);
344
+ serverUrl = Utils.resolveEnv(servers[0].url);
458
345
  }
459
346
  catch (err) {
460
347
  errors.push({
@@ -484,7 +371,8 @@ class Utils {
484
371
  }
485
372
  return errors;
486
373
  }
487
- static validateServer(spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2) {
374
+ static validateServer(spec, options) {
375
+ var _a;
488
376
  const errors = [];
489
377
  let hasTopLevelServers = false;
490
378
  let hasPathLevelServers = false;
@@ -505,7 +393,7 @@ class Utils {
505
393
  }
506
394
  for (const method in methods) {
507
395
  const operationObject = methods[method];
508
- if (Utils.isSupportedApi(method, path, spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2)) {
396
+ if (((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && operationObject) {
509
397
  if ((operationObject === null || operationObject === void 0 ? void 0 : operationObject.servers) && operationObject.servers.length >= 1) {
510
398
  hasOperationLevelServers = true;
511
399
  const serverErrors = Utils.checkServerUrl(operationObject.servers);
@@ -548,6 +436,7 @@ class Utils {
548
436
  Utils.updateParameterWithInputType(schema, parameter);
549
437
  }
550
438
  if (isRequired && schema.default === undefined) {
439
+ parameter.isRequired = true;
551
440
  requiredParams.push(parameter);
552
441
  }
553
442
  else {
@@ -592,7 +481,7 @@ class Utils {
592
481
  param.value = schema.default;
593
482
  }
594
483
  }
595
- static parseApiInfo(operationItem, allowMultipleParameters) {
484
+ static parseApiInfo(operationItem, options) {
596
485
  var _a, _b;
597
486
  const requiredParams = [];
598
487
  const optionalParams = [];
@@ -606,11 +495,12 @@ class Utils {
606
495
  description: ((_a = param.description) !== null && _a !== void 0 ? _a : "").slice(0, ConstantString.ParameterDescriptionMaxLens),
607
496
  };
608
497
  const schema = param.schema;
609
- if (allowMultipleParameters && schema) {
498
+ if (options.allowMultipleParameters && schema) {
610
499
  Utils.updateParameterWithInputType(schema, parameter);
611
500
  }
612
501
  if (param.in !== "header" && param.in !== "cookie") {
613
502
  if (param.required && (schema === null || schema === void 0 ? void 0 : schema.default) === undefined) {
503
+ parameter.isRequired = true;
614
504
  requiredParams.push(parameter);
615
505
  }
616
506
  else {
@@ -624,19 +514,13 @@ class Utils {
624
514
  const requestJson = requestBody.content["application/json"];
625
515
  if (Object.keys(requestJson).length !== 0) {
626
516
  const schema = requestJson.schema;
627
- const [requiredP, optionalP] = Utils.generateParametersFromSchema(schema, "requestBody", allowMultipleParameters, requestBody.required);
517
+ const [requiredP, optionalP] = Utils.generateParametersFromSchema(schema, "requestBody", !!options.allowMultipleParameters, requestBody.required);
628
518
  requiredParams.push(...requiredP);
629
519
  optionalParams.push(...optionalP);
630
520
  }
631
521
  }
632
522
  const operationId = operationItem.operationId;
633
- const parameters = [];
634
- if (requiredParams.length !== 0) {
635
- parameters.push(...requiredParams);
636
- }
637
- else {
638
- parameters.push(optionalParams[0]);
639
- }
523
+ const parameters = [...requiredParams, ...optionalParams];
640
524
  const command = {
641
525
  context: ["compose"],
642
526
  type: "query",
@@ -645,105 +529,534 @@ class Utils {
645
529
  parameters: parameters,
646
530
  description: ((_b = operationItem.description) !== null && _b !== void 0 ? _b : "").slice(0, ConstantString.CommandDescriptionMaxLens),
647
531
  };
648
- let warning = undefined;
649
- if (requiredParams.length === 0 && optionalParams.length > 1) {
650
- warning = {
651
- type: WarningType.OperationOnlyContainsOptionalParam,
652
- content: Utils.format(ConstantString.OperationOnlyContainsOptionalParam, operationId),
653
- data: operationId,
654
- };
532
+ return command;
533
+ }
534
+ static format(str, ...args) {
535
+ let index = 0;
536
+ return str.replace(/%s/g, () => {
537
+ const arg = args[index++];
538
+ return arg !== undefined ? arg : "";
539
+ });
540
+ }
541
+ static getSafeRegistrationIdEnvName(authName) {
542
+ if (!authName) {
543
+ return "";
544
+ }
545
+ let safeRegistrationIdEnvName = authName.toUpperCase().replace(/[^A-Z0-9_]/g, "_");
546
+ if (!safeRegistrationIdEnvName.match(/^[A-Z]/)) {
547
+ safeRegistrationIdEnvName = "PREFIX_" + safeRegistrationIdEnvName;
655
548
  }
656
- return [command, warning];
549
+ return safeRegistrationIdEnvName;
657
550
  }
658
- static listSupportedAPIs(spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2) {
659
- const paths = spec.paths;
551
+ static getServerObject(spec, method, path) {
552
+ const pathObj = spec.paths[path];
553
+ const operationObject = pathObj[method];
554
+ const rootServer = spec.servers && spec.servers[0];
555
+ const methodServer = spec.paths[path].servers && spec.paths[path].servers[0];
556
+ const operationServer = operationObject.servers && operationObject.servers[0];
557
+ const serverUrl = operationServer || methodServer || rootServer;
558
+ return serverUrl;
559
+ }
560
+ }
561
+
562
+ // Copyright (c) Microsoft Corporation.
563
+ class Validator {
564
+ listAPIs() {
565
+ var _a;
566
+ if (this.apiMap) {
567
+ return this.apiMap;
568
+ }
569
+ const paths = this.spec.paths;
660
570
  const result = {};
661
571
  for (const path in paths) {
662
572
  const methods = paths[path];
663
573
  for (const method in methods) {
664
- // For developer preview, only support GET operation with only 1 parameter without auth
665
- if (Utils.isSupportedApi(method, path, spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2)) {
666
- const operationObject = methods[method];
667
- result[`${method.toUpperCase()} ${path}`] = operationObject;
574
+ const operationObject = methods[method];
575
+ if (((_a = this.options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && operationObject) {
576
+ const validateResult = this.validateAPI(method, path);
577
+ result[`${method.toUpperCase()} ${path}`] = {
578
+ operation: operationObject,
579
+ isValid: validateResult.isValid,
580
+ reason: validateResult.reason,
581
+ };
668
582
  }
669
583
  }
670
584
  }
585
+ this.apiMap = result;
671
586
  return result;
672
587
  }
673
- static validateSpec(spec, parser, isSwaggerFile, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2) {
674
- const errors = [];
675
- const warnings = [];
676
- if (isSwaggerFile) {
677
- warnings.push({
678
- type: WarningType.ConvertSwaggerToOpenAPI,
679
- content: ConstantString.ConvertSwaggerToOpenAPI,
680
- });
681
- }
682
- // Server validation
683
- const serverErrors = Utils.validateServer(spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2);
684
- errors.push(...serverErrors);
685
- // Remote reference not supported
686
- const refPaths = parser.$refs.paths();
687
- // refPaths [0] is the current spec file path
688
- if (refPaths.length > 1) {
689
- errors.push({
690
- type: ErrorType.RemoteRefNotSupported,
691
- content: Utils.format(ConstantString.RemoteRefNotSupported, refPaths.join(", ")),
692
- data: refPaths,
588
+ validateSpecVersion() {
589
+ const result = { errors: [], warnings: [] };
590
+ if (this.spec.openapi >= "3.1.0") {
591
+ result.errors.push({
592
+ type: ErrorType.SpecVersionNotSupported,
593
+ content: Utils.format(ConstantString.SpecVersionNotSupported, this.spec.openapi),
594
+ data: this.spec.openapi,
693
595
  });
694
596
  }
695
- // No supported API
696
- const apiMap = Utils.listSupportedAPIs(spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2);
697
- if (Object.keys(apiMap).length === 0) {
698
- errors.push({
597
+ return result;
598
+ }
599
+ validateSpecServer() {
600
+ const result = { errors: [], warnings: [] };
601
+ const serverErrors = Utils.validateServer(this.spec, this.options);
602
+ result.errors.push(...serverErrors);
603
+ return result;
604
+ }
605
+ validateSpecNoSupportAPI() {
606
+ const result = { errors: [], warnings: [] };
607
+ const apiMap = this.listAPIs();
608
+ const validAPIs = Object.entries(apiMap).filter(([, value]) => value.isValid);
609
+ if (validAPIs.length === 0) {
610
+ const data = [];
611
+ for (const key in apiMap) {
612
+ const { reason } = apiMap[key];
613
+ const apiInvalidReason = { api: key, reason: reason };
614
+ data.push(apiInvalidReason);
615
+ }
616
+ result.errors.push({
699
617
  type: ErrorType.NoSupportedApi,
700
618
  content: ConstantString.NoSupportedApi,
619
+ data,
701
620
  });
702
621
  }
622
+ return result;
623
+ }
624
+ validateSpecOperationId() {
625
+ const result = { errors: [], warnings: [] };
626
+ const apiMap = this.listAPIs();
703
627
  // OperationId missing
704
628
  const apisMissingOperationId = [];
705
629
  for (const key in apiMap) {
706
- const pathObjectItem = apiMap[key];
707
- if (!pathObjectItem.operationId) {
630
+ const { operation } = apiMap[key];
631
+ if (!operation.operationId) {
708
632
  apisMissingOperationId.push(key);
709
633
  }
710
634
  }
711
635
  if (apisMissingOperationId.length > 0) {
712
- warnings.push({
636
+ result.warnings.push({
713
637
  type: WarningType.OperationIdMissing,
714
638
  content: Utils.format(ConstantString.MissingOperationId, apisMissingOperationId.join(", ")),
715
639
  data: apisMissingOperationId,
716
640
  });
717
641
  }
718
- let status = ValidationStatus.Valid;
719
- if (warnings.length > 0 && errors.length === 0) {
720
- status = ValidationStatus.Warning;
642
+ return result;
643
+ }
644
+ validateMethodAndPath(method, path) {
645
+ const result = { isValid: true, reason: [] };
646
+ if (this.options.allowMethods && !this.options.allowMethods.includes(method)) {
647
+ result.isValid = false;
648
+ result.reason.push(ErrorType.MethodNotAllowed);
649
+ return result;
650
+ }
651
+ const pathObj = this.spec.paths[path];
652
+ if (!pathObj || !pathObj[method]) {
653
+ result.isValid = false;
654
+ result.reason.push(ErrorType.UrlPathNotExist);
655
+ return result;
656
+ }
657
+ return result;
658
+ }
659
+ validateResponse(method, path) {
660
+ const result = { isValid: true, reason: [] };
661
+ const operationObject = this.spec.paths[path][method];
662
+ const { json, multipleMediaType } = Utils.getResponseJson(operationObject);
663
+ if (this.options.projectType === ProjectType.SME) {
664
+ // only support response body only contains “application/json” content type
665
+ if (multipleMediaType) {
666
+ result.reason.push(ErrorType.ResponseContainMultipleMediaTypes);
667
+ }
668
+ else if (Object.keys(json).length === 0) {
669
+ // response body should not be empty
670
+ result.reason.push(ErrorType.ResponseJsonIsEmpty);
671
+ }
672
+ }
673
+ return result;
674
+ }
675
+ validateServer(method, path) {
676
+ const result = { isValid: true, reason: [] };
677
+ const serverObj = Utils.getServerObject(this.spec, method, path);
678
+ if (!serverObj) {
679
+ // should contain server URL
680
+ result.reason.push(ErrorType.NoServerInformation);
681
+ }
682
+ else {
683
+ // server url should be absolute url with https protocol
684
+ const serverValidateResult = Utils.checkServerUrl([serverObj]);
685
+ result.reason.push(...serverValidateResult.map((item) => item.type));
686
+ }
687
+ return result;
688
+ }
689
+ validateAuth(method, path) {
690
+ const pathObj = this.spec.paths[path];
691
+ const operationObject = pathObj[method];
692
+ const securities = operationObject.security;
693
+ const authSchemeArray = Utils.getAuthArray(securities, this.spec);
694
+ if (authSchemeArray.length === 0) {
695
+ return { isValid: true, reason: [] };
721
696
  }
722
- else if (errors.length > 0) {
723
- status = ValidationStatus.Error;
697
+ if (this.options.allowAPIKeyAuth ||
698
+ this.options.allowOauth2 ||
699
+ this.options.allowBearerTokenAuth) {
700
+ // Currently we don't support multiple auth in one operation
701
+ if (authSchemeArray.length > 0 && authSchemeArray.every((auths) => auths.length > 1)) {
702
+ return {
703
+ isValid: false,
704
+ reason: [ErrorType.MultipleAuthNotSupported],
705
+ };
706
+ }
707
+ for (const auths of authSchemeArray) {
708
+ if (auths.length === 1) {
709
+ if ((this.options.allowAPIKeyAuth && Utils.isAPIKeyAuth(auths[0].authScheme)) ||
710
+ (this.options.allowOauth2 && Utils.isOAuthWithAuthCodeFlow(auths[0].authScheme)) ||
711
+ (this.options.allowBearerTokenAuth && Utils.isBearerTokenAuth(auths[0].authScheme))) {
712
+ return { isValid: true, reason: [] };
713
+ }
714
+ }
715
+ }
724
716
  }
725
- return {
726
- status,
727
- warnings,
728
- errors,
717
+ return { isValid: false, reason: [ErrorType.AuthTypeIsNotSupported] };
718
+ }
719
+ checkPostBodySchema(schema, isRequired = false) {
720
+ var _a;
721
+ const paramResult = {
722
+ requiredNum: 0,
723
+ optionalNum: 0,
724
+ isValid: true,
725
+ reason: [],
729
726
  };
727
+ if (Object.keys(schema).length === 0) {
728
+ return paramResult;
729
+ }
730
+ const isRequiredWithoutDefault = isRequired && schema.default === undefined;
731
+ const isCopilot = this.projectType === ProjectType.Copilot;
732
+ if (isCopilot && this.hasNestedObjectInSchema(schema)) {
733
+ paramResult.isValid = false;
734
+ paramResult.reason = [ErrorType.RequestBodyContainsNestedObject];
735
+ return paramResult;
736
+ }
737
+ if (schema.type === "string" ||
738
+ schema.type === "integer" ||
739
+ schema.type === "boolean" ||
740
+ schema.type === "number") {
741
+ if (isRequiredWithoutDefault) {
742
+ paramResult.requiredNum = paramResult.requiredNum + 1;
743
+ }
744
+ else {
745
+ paramResult.optionalNum = paramResult.optionalNum + 1;
746
+ }
747
+ }
748
+ else if (schema.type === "object") {
749
+ const { properties } = schema;
750
+ for (const property in properties) {
751
+ let isRequired = false;
752
+ if (schema.required && ((_a = schema.required) === null || _a === void 0 ? void 0 : _a.indexOf(property)) >= 0) {
753
+ isRequired = true;
754
+ }
755
+ const result = this.checkPostBodySchema(properties[property], isRequired);
756
+ paramResult.requiredNum += result.requiredNum;
757
+ paramResult.optionalNum += result.optionalNum;
758
+ paramResult.isValid = paramResult.isValid && result.isValid;
759
+ paramResult.reason.push(...result.reason);
760
+ }
761
+ }
762
+ else {
763
+ if (isRequiredWithoutDefault && !isCopilot) {
764
+ paramResult.isValid = false;
765
+ paramResult.reason.push(ErrorType.PostBodyContainsRequiredUnsupportedSchema);
766
+ }
767
+ }
768
+ return paramResult;
730
769
  }
731
- static format(str, ...args) {
732
- let index = 0;
733
- return str.replace(/%s/g, () => {
734
- const arg = args[index++];
735
- return arg !== undefined ? arg : "";
736
- });
770
+ checkParamSchema(paramObject) {
771
+ const paramResult = {
772
+ requiredNum: 0,
773
+ optionalNum: 0,
774
+ isValid: true,
775
+ reason: [],
776
+ };
777
+ if (!paramObject) {
778
+ return paramResult;
779
+ }
780
+ const isCopilot = this.projectType === ProjectType.Copilot;
781
+ for (let i = 0; i < paramObject.length; i++) {
782
+ const param = paramObject[i];
783
+ const schema = param.schema;
784
+ if (isCopilot && this.hasNestedObjectInSchema(schema)) {
785
+ paramResult.isValid = false;
786
+ paramResult.reason.push(ErrorType.ParamsContainsNestedObject);
787
+ continue;
788
+ }
789
+ const isRequiredWithoutDefault = param.required && schema.default === undefined;
790
+ if (isCopilot) {
791
+ if (isRequiredWithoutDefault) {
792
+ paramResult.requiredNum = paramResult.requiredNum + 1;
793
+ }
794
+ else {
795
+ paramResult.optionalNum = paramResult.optionalNum + 1;
796
+ }
797
+ continue;
798
+ }
799
+ if (param.in === "header" || param.in === "cookie") {
800
+ if (isRequiredWithoutDefault) {
801
+ paramResult.isValid = false;
802
+ paramResult.reason.push(ErrorType.ParamsContainRequiredUnsupportedSchema);
803
+ }
804
+ continue;
805
+ }
806
+ if (schema.type !== "boolean" &&
807
+ schema.type !== "string" &&
808
+ schema.type !== "number" &&
809
+ schema.type !== "integer") {
810
+ if (isRequiredWithoutDefault) {
811
+ paramResult.isValid = false;
812
+ paramResult.reason.push(ErrorType.ParamsContainRequiredUnsupportedSchema);
813
+ }
814
+ continue;
815
+ }
816
+ if (param.in === "query" || param.in === "path") {
817
+ if (isRequiredWithoutDefault) {
818
+ paramResult.requiredNum = paramResult.requiredNum + 1;
819
+ }
820
+ else {
821
+ paramResult.optionalNum = paramResult.optionalNum + 1;
822
+ }
823
+ }
824
+ }
825
+ return paramResult;
737
826
  }
738
- static getSafeRegistrationIdEnvName(authName) {
739
- if (!authName) {
740
- return "";
827
+ hasNestedObjectInSchema(schema) {
828
+ if (schema.type === "object") {
829
+ for (const property in schema.properties) {
830
+ const nestedSchema = schema.properties[property];
831
+ if (nestedSchema.type === "object") {
832
+ return true;
833
+ }
834
+ }
741
835
  }
742
- let safeRegistrationIdEnvName = authName.toUpperCase().replace(/[^A-Z0-9_]/g, "_");
743
- if (!safeRegistrationIdEnvName.match(/^[A-Z]/)) {
744
- safeRegistrationIdEnvName = "PREFIX_" + safeRegistrationIdEnvName;
836
+ return false;
837
+ }
838
+ }
839
+
840
+ // Copyright (c) Microsoft Corporation.
841
+ class CopilotValidator extends Validator {
842
+ constructor(spec, options) {
843
+ super();
844
+ this.projectType = ProjectType.Copilot;
845
+ this.options = options;
846
+ this.spec = spec;
847
+ }
848
+ validateSpec() {
849
+ const result = { errors: [], warnings: [] };
850
+ // validate spec version
851
+ let validationResult = this.validateSpecVersion();
852
+ result.errors.push(...validationResult.errors);
853
+ // validate spec server
854
+ validationResult = this.validateSpecServer();
855
+ result.errors.push(...validationResult.errors);
856
+ // validate no supported API
857
+ validationResult = this.validateSpecNoSupportAPI();
858
+ result.errors.push(...validationResult.errors);
859
+ // validate operationId missing
860
+ validationResult = this.validateSpecOperationId();
861
+ result.warnings.push(...validationResult.warnings);
862
+ return result;
863
+ }
864
+ validateAPI(method, path) {
865
+ const result = { isValid: true, reason: [] };
866
+ method = method.toLocaleLowerCase();
867
+ // validate method and path
868
+ const methodAndPathResult = this.validateMethodAndPath(method, path);
869
+ if (!methodAndPathResult.isValid) {
870
+ return methodAndPathResult;
871
+ }
872
+ const operationObject = this.spec.paths[path][method];
873
+ // validate auth
874
+ const authCheckResult = this.validateAuth(method, path);
875
+ result.reason.push(...authCheckResult.reason);
876
+ // validate operationId
877
+ if (!this.options.allowMissingId && !operationObject.operationId) {
878
+ result.reason.push(ErrorType.MissingOperationId);
879
+ }
880
+ // validate server
881
+ const validateServerResult = this.validateServer(method, path);
882
+ result.reason.push(...validateServerResult.reason);
883
+ // validate response
884
+ const validateResponseResult = this.validateResponse(method, path);
885
+ result.reason.push(...validateResponseResult.reason);
886
+ // validate requestBody
887
+ const requestBody = operationObject.requestBody;
888
+ const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
889
+ if (requestJsonBody) {
890
+ const requestBodySchema = requestJsonBody.schema;
891
+ if (requestBodySchema.type !== "object") {
892
+ result.reason.push(ErrorType.PostBodySchemaIsNotJson);
893
+ }
894
+ const requestBodyParamResult = this.checkPostBodySchema(requestBodySchema, requestBody.required);
895
+ result.reason.push(...requestBodyParamResult.reason);
896
+ }
897
+ // validate parameters
898
+ const paramObject = operationObject.parameters;
899
+ const paramResult = this.checkParamSchema(paramObject);
900
+ result.reason.push(...paramResult.reason);
901
+ if (result.reason.length > 0) {
902
+ result.isValid = false;
903
+ }
904
+ return result;
905
+ }
906
+ }
907
+
908
+ // Copyright (c) Microsoft Corporation.
909
+ class SMEValidator extends Validator {
910
+ constructor(spec, options) {
911
+ super();
912
+ this.projectType = ProjectType.SME;
913
+ this.options = options;
914
+ this.spec = spec;
915
+ }
916
+ validateSpec() {
917
+ const result = { errors: [], warnings: [] };
918
+ // validate spec version
919
+ let validationResult = this.validateSpecVersion();
920
+ result.errors.push(...validationResult.errors);
921
+ // validate spec server
922
+ validationResult = this.validateSpecServer();
923
+ result.errors.push(...validationResult.errors);
924
+ // validate no supported API
925
+ validationResult = this.validateSpecNoSupportAPI();
926
+ result.errors.push(...validationResult.errors);
927
+ // validate operationId missing
928
+ if (this.options.allowMissingId) {
929
+ validationResult = this.validateSpecOperationId();
930
+ result.warnings.push(...validationResult.warnings);
931
+ }
932
+ return result;
933
+ }
934
+ validateAPI(method, path) {
935
+ const result = { isValid: true, reason: [] };
936
+ method = method.toLocaleLowerCase();
937
+ // validate method and path
938
+ const methodAndPathResult = this.validateMethodAndPath(method, path);
939
+ if (!methodAndPathResult.isValid) {
940
+ return methodAndPathResult;
941
+ }
942
+ const operationObject = this.spec.paths[path][method];
943
+ // validate auth
944
+ const authCheckResult = this.validateAuth(method, path);
945
+ result.reason.push(...authCheckResult.reason);
946
+ // validate operationId
947
+ if (!this.options.allowMissingId && !operationObject.operationId) {
948
+ result.reason.push(ErrorType.MissingOperationId);
949
+ }
950
+ // validate server
951
+ const validateServerResult = this.validateServer(method, path);
952
+ result.reason.push(...validateServerResult.reason);
953
+ // validate response
954
+ const validateResponseResult = this.validateResponse(method, path);
955
+ result.reason.push(...validateResponseResult.reason);
956
+ let postBodyResult = {
957
+ requiredNum: 0,
958
+ optionalNum: 0,
959
+ isValid: true,
960
+ reason: [],
961
+ };
962
+ // validate requestBody
963
+ const requestBody = operationObject.requestBody;
964
+ const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
965
+ if (Utils.containMultipleMediaTypes(requestBody)) {
966
+ result.reason.push(ErrorType.PostBodyContainMultipleMediaTypes);
967
+ }
968
+ if (requestJsonBody) {
969
+ const requestBodySchema = requestJsonBody.schema;
970
+ postBodyResult = this.checkPostBodySchema(requestBodySchema, requestBody.required);
971
+ result.reason.push(...postBodyResult.reason);
972
+ }
973
+ // validate parameters
974
+ const paramObject = operationObject.parameters;
975
+ const paramResult = this.checkParamSchema(paramObject);
976
+ result.reason.push(...paramResult.reason);
977
+ // validate total parameters count
978
+ if (paramResult.isValid && postBodyResult.isValid) {
979
+ const paramCountResult = this.validateParamCount(postBodyResult, paramResult);
980
+ result.reason.push(...paramCountResult.reason);
981
+ }
982
+ if (result.reason.length > 0) {
983
+ result.isValid = false;
984
+ }
985
+ return result;
986
+ }
987
+ validateParamCount(postBodyResult, paramResult) {
988
+ const result = { isValid: true, reason: [] };
989
+ const totalRequiredParams = postBodyResult.requiredNum + paramResult.requiredNum;
990
+ const totalParams = totalRequiredParams + postBodyResult.optionalNum + paramResult.optionalNum;
991
+ if (totalRequiredParams > 1) {
992
+ if (!this.options.allowMultipleParameters ||
993
+ totalRequiredParams > SMEValidator.SMERequiredParamsMaxNum) {
994
+ result.reason.push(ErrorType.ExceededRequiredParamsLimit);
995
+ }
996
+ }
997
+ else if (totalParams === 0) {
998
+ result.reason.push(ErrorType.NoParameter);
999
+ }
1000
+ return result;
1001
+ }
1002
+ }
1003
+ SMEValidator.SMERequiredParamsMaxNum = 5;
1004
+
1005
+ // Copyright (c) Microsoft Corporation.
1006
+ class TeamsAIValidator extends Validator {
1007
+ constructor(spec, options) {
1008
+ super();
1009
+ this.projectType = ProjectType.TeamsAi;
1010
+ this.options = options;
1011
+ this.spec = spec;
1012
+ }
1013
+ validateSpec() {
1014
+ const result = { errors: [], warnings: [] };
1015
+ // validate spec server
1016
+ let validationResult = this.validateSpecServer();
1017
+ result.errors.push(...validationResult.errors);
1018
+ // validate no supported API
1019
+ validationResult = this.validateSpecNoSupportAPI();
1020
+ result.errors.push(...validationResult.errors);
1021
+ return result;
1022
+ }
1023
+ validateAPI(method, path) {
1024
+ const result = { isValid: true, reason: [] };
1025
+ method = method.toLocaleLowerCase();
1026
+ // validate method and path
1027
+ const methodAndPathResult = this.validateMethodAndPath(method, path);
1028
+ if (!methodAndPathResult.isValid) {
1029
+ return methodAndPathResult;
1030
+ }
1031
+ const operationObject = this.spec.paths[path][method];
1032
+ // validate operationId
1033
+ if (!this.options.allowMissingId && !operationObject.operationId) {
1034
+ result.reason.push(ErrorType.MissingOperationId);
1035
+ }
1036
+ // validate server
1037
+ const validateServerResult = this.validateServer(method, path);
1038
+ result.reason.push(...validateServerResult.reason);
1039
+ if (result.reason.length > 0) {
1040
+ result.isValid = false;
1041
+ }
1042
+ return result;
1043
+ }
1044
+ }
1045
+
1046
+ class ValidatorFactory {
1047
+ static create(spec, options) {
1048
+ var _a;
1049
+ const type = (_a = options.projectType) !== null && _a !== void 0 ? _a : ProjectType.SME;
1050
+ switch (type) {
1051
+ case ProjectType.SME:
1052
+ return new SMEValidator(spec, options);
1053
+ case ProjectType.Copilot:
1054
+ return new CopilotValidator(spec, options);
1055
+ case ProjectType.TeamsAi:
1056
+ return new TeamsAIValidator(spec, options);
1057
+ default:
1058
+ throw new Error(`Invalid project type: ${type}`);
745
1059
  }
746
- return safeRegistrationIdEnvName;
747
1060
  }
748
1061
  }
749
1062
 
@@ -763,7 +1076,14 @@ class SpecParser {
763
1076
  allowSwagger: false,
764
1077
  allowAPIKeyAuth: false,
765
1078
  allowMultipleParameters: false,
1079
+ allowBearerTokenAuth: false,
766
1080
  allowOauth2: false,
1081
+ allowMethods: ["get", "post"],
1082
+ allowConversationStarters: false,
1083
+ allowResponseSemantics: false,
1084
+ allowConfirmation: false,
1085
+ projectType: ProjectType.SME,
1086
+ isGptPlugin: false,
767
1087
  };
768
1088
  this.pathOrSpec = pathOrDoc;
769
1089
  this.parser = new SwaggerParser();
@@ -779,11 +1099,7 @@ class SpecParser {
779
1099
  try {
780
1100
  try {
781
1101
  yield this.loadSpec();
782
- yield this.parser.validate(this.spec, {
783
- validate: {
784
- schema: false,
785
- },
786
- });
1102
+ yield this.parser.validate(this.spec);
787
1103
  }
788
1104
  catch (e) {
789
1105
  return {
@@ -792,16 +1108,46 @@ class SpecParser {
792
1108
  errors: [{ type: ErrorType.SpecNotValid, content: e.toString() }],
793
1109
  };
794
1110
  }
1111
+ const errors = [];
1112
+ const warnings = [];
795
1113
  if (!this.options.allowSwagger && this.isSwaggerFile) {
796
1114
  return {
797
1115
  status: ValidationStatus.Error,
798
1116
  warnings: [],
799
1117
  errors: [
800
- { type: ErrorType.SwaggerNotSupported, content: ConstantString.SwaggerNotSupported },
1118
+ {
1119
+ type: ErrorType.SwaggerNotSupported,
1120
+ content: ConstantString.SwaggerNotSupported,
1121
+ },
801
1122
  ],
802
1123
  };
803
1124
  }
804
- return Utils.validateSpec(this.spec, this.parser, !!this.isSwaggerFile, this.options.allowMissingId, this.options.allowAPIKeyAuth, this.options.allowMultipleParameters, this.options.allowOauth2);
1125
+ // Remote reference not supported
1126
+ const refPaths = this.parser.$refs.paths();
1127
+ // refPaths [0] is the current spec file path
1128
+ if (refPaths.length > 1) {
1129
+ errors.push({
1130
+ type: ErrorType.RemoteRefNotSupported,
1131
+ content: Utils.format(ConstantString.RemoteRefNotSupported, refPaths.join(", ")),
1132
+ data: refPaths,
1133
+ });
1134
+ }
1135
+ const validator = this.getValidator(this.spec);
1136
+ const validationResult = validator.validateSpec();
1137
+ warnings.push(...validationResult.warnings);
1138
+ errors.push(...validationResult.errors);
1139
+ let status = ValidationStatus.Valid;
1140
+ if (warnings.length > 0 && errors.length === 0) {
1141
+ status = ValidationStatus.Warning;
1142
+ }
1143
+ else if (errors.length > 0) {
1144
+ status = ValidationStatus.Error;
1145
+ }
1146
+ return {
1147
+ status: status,
1148
+ warnings: warnings,
1149
+ errors: errors,
1150
+ };
805
1151
  }
806
1152
  catch (err) {
807
1153
  throw new SpecParserError(err.toString(), ErrorType.ValidateFailed);
@@ -812,17 +1158,20 @@ class SpecParser {
812
1158
  return __awaiter(this, void 0, void 0, function* () {
813
1159
  try {
814
1160
  yield this.loadSpec();
815
- const apiMap = this.getAllSupportedAPIs(this.spec);
1161
+ const apiMap = this.getAPIs(this.spec);
816
1162
  const apiInfos = [];
817
1163
  for (const key in apiMap) {
818
- const pathObjectItem = apiMap[key];
1164
+ const { operation, isValid } = apiMap[key];
1165
+ if (!isValid) {
1166
+ continue;
1167
+ }
819
1168
  const [method, path] = key.split(" ");
820
- const operationId = pathObjectItem.operationId;
1169
+ const operationId = operation.operationId;
821
1170
  // In Browser environment, this api is by default not support api without operationId
822
1171
  if (!operationId) {
823
1172
  continue;
824
1173
  }
825
- const [command, warning] = Utils.parseApiInfo(pathObjectItem, this.options.allowMultipleParameters);
1174
+ const command = Utils.parseApiInfo(operation, this.options);
826
1175
  const apiInfo = {
827
1176
  method: method,
828
1177
  path: path,
@@ -831,9 +1180,6 @@ class SpecParser {
831
1180
  parameters: command.parameters,
832
1181
  description: command.description,
833
1182
  };
834
- if (warning) {
835
- apiInfo.warning = warning;
836
- }
837
1183
  apiInfos.push(apiInfo);
838
1184
  }
839
1185
  return apiInfos;
@@ -854,12 +1200,36 @@ class SpecParser {
854
1200
  throw new Error("Method not implemented.");
855
1201
  });
856
1202
  }
1203
+ /**
1204
+ * Generate specs according to the filters.
1205
+ * @param filter An array of strings that represent the filters to apply when generating the artifacts. If filter is empty, it would process nothing.
1206
+ */
1207
+ // eslint-disable-next-line @typescript-eslint/require-await
1208
+ getFilteredSpecs(filter, signal) {
1209
+ return __awaiter(this, void 0, void 0, function* () {
1210
+ throw new Error("Method not implemented.");
1211
+ });
1212
+ }
1213
+ /**
1214
+ * Generates and update artifacts from the OpenAPI specification file. Generate Adaptive Cards, update Teams app manifest, and generate a new OpenAPI specification file.
1215
+ * @param manifestPath A file path of the Teams app manifest file to update.
1216
+ * @param filter An array of strings that represent the filters to apply when generating the artifacts. If filter is empty, it would process nothing.
1217
+ * @param outputSpecPath File path of the new OpenAPI specification file to generate. If not specified or empty, no spec file will be generated.
1218
+ * @param pluginFilePath File path of the api plugin file to generate.
1219
+ */
1220
+ // eslint-disable-next-line @typescript-eslint/require-await
1221
+ generateForCopilot(manifestPath, filter, outputSpecPath, pluginFilePath, signal) {
1222
+ return __awaiter(this, void 0, void 0, function* () {
1223
+ throw new Error("Method not implemented.");
1224
+ });
1225
+ }
857
1226
  /**
858
1227
  * Generates and update artifacts from the OpenAPI specification file. Generate Adaptive Cards, update Teams app manifest, and generate a new OpenAPI specification file.
859
1228
  * @param manifestPath A file path of the Teams app manifest file to update.
860
1229
  * @param filter An array of strings that represent the filters to apply when generating the artifacts. If filter is empty, it would process nothing.
861
1230
  * @param outputSpecPath File path of the new OpenAPI specification file to generate. If not specified or empty, no spec file will be generated.
862
1231
  * @param adaptiveCardFolder Folder path where the Adaptive Card files will be generated. If not specified or empty, Adaptive Card files will not be generated.
1232
+ * @param isMe Boolean that indicates whether the project is an Messaging Extension. For Messaging Extension, composeExtensions will be added in Teams app manifest.
863
1233
  */
864
1234
  // eslint-disable-next-line @typescript-eslint/require-await
865
1235
  generate(manifestPath, filter, outputSpecPath, adaptiveCardFolder, signal) {
@@ -879,15 +1249,178 @@ class SpecParser {
879
1249
  }
880
1250
  });
881
1251
  }
882
- getAllSupportedAPIs(spec) {
883
- if (this.apiMap !== undefined) {
884
- return this.apiMap;
1252
+ getAPIs(spec) {
1253
+ const validator = this.getValidator(spec);
1254
+ const apiMap = validator.listAPIs();
1255
+ return apiMap;
1256
+ }
1257
+ getValidator(spec) {
1258
+ if (this.validator) {
1259
+ return this.validator;
885
1260
  }
886
- const result = Utils.listSupportedAPIs(spec, this.options.allowMissingId, this.options.allowAPIKeyAuth, this.options.allowMultipleParameters, this.options.allowOauth2);
887
- this.apiMap = result;
888
- return result;
1261
+ const validator = ValidatorFactory.create(spec, this.options);
1262
+ this.validator = validator;
1263
+ return validator;
1264
+ }
1265
+ }
1266
+
1267
+ // Copyright (c) Microsoft Corporation.
1268
+ class AdaptiveCardGenerator {
1269
+ static generateAdaptiveCard(operationItem) {
1270
+ try {
1271
+ const { json } = Utils.getResponseJson(operationItem);
1272
+ let cardBody = [];
1273
+ let schema = json.schema;
1274
+ let jsonPath = "$";
1275
+ if (schema && Object.keys(schema).length > 0) {
1276
+ jsonPath = AdaptiveCardGenerator.getResponseJsonPathFromSchema(schema);
1277
+ if (jsonPath !== "$") {
1278
+ schema = schema.properties[jsonPath];
1279
+ }
1280
+ cardBody = AdaptiveCardGenerator.generateCardFromResponse(schema, "");
1281
+ }
1282
+ // if no schema, try to use example value
1283
+ if (cardBody.length === 0 && (json.examples || json.example)) {
1284
+ cardBody = [
1285
+ {
1286
+ type: ConstantString.TextBlockType,
1287
+ text: "${jsonStringify($root)}",
1288
+ wrap: true,
1289
+ },
1290
+ ];
1291
+ }
1292
+ // if no example value, use default success response
1293
+ if (cardBody.length === 0) {
1294
+ cardBody = [
1295
+ {
1296
+ type: ConstantString.TextBlockType,
1297
+ text: "success",
1298
+ wrap: true,
1299
+ },
1300
+ ];
1301
+ }
1302
+ const fullCard = {
1303
+ type: ConstantString.AdaptiveCardType,
1304
+ $schema: ConstantString.AdaptiveCardSchema,
1305
+ version: ConstantString.AdaptiveCardVersion,
1306
+ body: cardBody,
1307
+ };
1308
+ return [fullCard, jsonPath];
1309
+ }
1310
+ catch (err) {
1311
+ throw new SpecParserError(err.toString(), ErrorType.GenerateAdaptiveCardFailed);
1312
+ }
1313
+ }
1314
+ static generateCardFromResponse(schema, name, parentArrayName = "") {
1315
+ if (schema.type === "array") {
1316
+ // schema.items can be arbitrary object: schema { type: array, items: {} }
1317
+ if (Object.keys(schema.items).length === 0) {
1318
+ return [
1319
+ {
1320
+ type: ConstantString.TextBlockType,
1321
+ text: name ? `${name}: \${jsonStringify(${name})}` : "result: ${jsonStringify($root)}",
1322
+ wrap: true,
1323
+ },
1324
+ ];
1325
+ }
1326
+ const obj = AdaptiveCardGenerator.generateCardFromResponse(schema.items, "", name);
1327
+ const template = {
1328
+ type: ConstantString.ContainerType,
1329
+ $data: name ? `\${${name}}` : "${$root}",
1330
+ items: Array(),
1331
+ };
1332
+ template.items.push(...obj);
1333
+ return [template];
1334
+ }
1335
+ // some schema may not contain type but contain properties
1336
+ if (schema.type === "object" || (!schema.type && schema.properties)) {
1337
+ const { properties } = schema;
1338
+ const result = [];
1339
+ for (const property in properties) {
1340
+ const obj = AdaptiveCardGenerator.generateCardFromResponse(properties[property], name ? `${name}.${property}` : property, parentArrayName);
1341
+ result.push(...obj);
1342
+ }
1343
+ if (schema.additionalProperties) {
1344
+ // TODO: better ways to handler warnings.
1345
+ console.warn(ConstantString.AdditionalPropertiesNotSupported);
1346
+ }
1347
+ return result;
1348
+ }
1349
+ if (schema.type === "string" ||
1350
+ schema.type === "integer" ||
1351
+ schema.type === "boolean" ||
1352
+ schema.type === "number") {
1353
+ if (!AdaptiveCardGenerator.isImageUrlProperty(schema, name, parentArrayName)) {
1354
+ // string in root: "ddd"
1355
+ let text = "result: ${$root}";
1356
+ if (name) {
1357
+ // object { id: "1" }
1358
+ text = `${name}: \${if(${name}, ${name}, 'N/A')}`;
1359
+ if (parentArrayName) {
1360
+ // object types inside array: { tags: ["id": 1, "name": "name"] }
1361
+ text = `${parentArrayName}.${text}`;
1362
+ }
1363
+ }
1364
+ else if (parentArrayName) {
1365
+ // string array: photoUrls: ["1", "2"]
1366
+ text = `${parentArrayName}: ` + "${$data}";
1367
+ }
1368
+ return [
1369
+ {
1370
+ type: ConstantString.TextBlockType,
1371
+ text,
1372
+ wrap: true,
1373
+ },
1374
+ ];
1375
+ }
1376
+ else {
1377
+ if (name) {
1378
+ return [
1379
+ {
1380
+ type: "Image",
1381
+ url: `\${${name}}`,
1382
+ $when: `\${${name} != null}`,
1383
+ },
1384
+ ];
1385
+ }
1386
+ else {
1387
+ return [
1388
+ {
1389
+ type: "Image",
1390
+ url: "${$data}",
1391
+ $when: "${$data != null}",
1392
+ },
1393
+ ];
1394
+ }
1395
+ }
1396
+ }
1397
+ if (schema.oneOf || schema.anyOf || schema.not || schema.allOf) {
1398
+ throw new Error(Utils.format(ConstantString.SchemaNotSupported, JSON.stringify(schema)));
1399
+ }
1400
+ throw new Error(Utils.format(ConstantString.UnknownSchema, JSON.stringify(schema)));
1401
+ }
1402
+ // Find the first array property in the response schema object with the well-known name
1403
+ static getResponseJsonPathFromSchema(schema) {
1404
+ if (schema.type === "object" || (!schema.type && schema.properties)) {
1405
+ const { properties } = schema;
1406
+ for (const property in properties) {
1407
+ const schema = properties[property];
1408
+ if (schema.type === "array" &&
1409
+ Utils.isWellKnownName(property, ConstantString.WellknownResultNames)) {
1410
+ return property;
1411
+ }
1412
+ }
1413
+ }
1414
+ return "$";
1415
+ }
1416
+ static isImageUrlProperty(schema, name, parentArrayName) {
1417
+ const propertyName = name ? name : parentArrayName;
1418
+ return (!!propertyName &&
1419
+ schema.type === "string" &&
1420
+ Utils.isWellKnownName(propertyName, ConstantString.WellknownImageName) &&
1421
+ (propertyName.toLocaleLowerCase().indexOf("url") >= 0 || schema.format === "uri"));
889
1422
  }
890
1423
  }
891
1424
 
892
- export { ConstantString, ErrorType, SpecParser, SpecParserError, Utils, ValidationStatus, WarningType };
1425
+ export { AdaptiveCardGenerator, ConstantString, ErrorType, SpecParser, SpecParserError, Utils, ValidationStatus, WarningType };
893
1426
  //# sourceMappingURL=index.esm5.js.map