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