@microsoft/m365-spec-parser 0.0.2-alpha.2 → 0.1.1-alpha.0de595af8.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 {
@@ -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";
@@ -114,6 +139,7 @@ ConstantString.AdaptiveCardType = "AdaptiveCard";
114
139
  ConstantString.TextBlockType = "TextBlock";
115
140
  ConstantString.ContainerType = "Container";
116
141
  ConstantString.RegistrationIdPostfix = "REGISTRATION_ID";
142
+ ConstantString.OAuthRegistrationIdPostFix = "OAUTH_REGISTRATION_ID";
117
143
  ConstantString.ResponseCodeFor20X = [
118
144
  "200",
119
145
  "201",
@@ -173,205 +199,35 @@ ConstantString.FullDescriptionMaxLens = 4000;
173
199
  ConstantString.CommandDescriptionMaxLens = 128;
174
200
  ConstantString.ParameterDescriptionMaxLens = 128;
175
201
  ConstantString.CommandTitleMaxLens = 32;
176
- ConstantString.ParameterTitleMaxLens = 32;
202
+ ConstantString.ParameterTitleMaxLens = 32;
203
+ ConstantString.SMERequiredParamsMaxNum = 5;
177
204
 
178
205
  // Copyright (c) Microsoft Corporation.
179
206
  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 {
207
+ static hasNestedObjectInSchema(schema) {
208
+ if (schema.type === "object") {
209
+ for (const property in schema.properties) {
210
+ const nestedSchema = schema.properties[property];
211
+ if (nestedSchema.type === "object") {
332
212
  return true;
333
213
  }
334
214
  }
335
215
  }
336
216
  return false;
337
217
  }
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;
218
+ static containMultipleMediaTypes(bodyObject) {
219
+ return Object.keys((bodyObject === null || bodyObject === void 0 ? void 0 : bodyObject.content) || {}).length > 1;
220
+ }
221
+ static isBearerTokenAuth(authScheme) {
222
+ return authScheme.type === "http" && authScheme.scheme === "bearer";
367
223
  }
368
- static isAPIKeyAuth(authSchema) {
369
- return authSchema.type === "apiKey";
224
+ static isAPIKeyAuth(authScheme) {
225
+ return authScheme.type === "apiKey";
370
226
  }
371
- static isBearerTokenAuth(authSchema) {
372
- return (authSchema.type === "oauth2" ||
373
- authSchema.type === "openIdConnect" ||
374
- (authSchema.type === "http" && authSchema.scheme === "bearer"));
227
+ static isOAuthWithAuthCodeFlow(authScheme) {
228
+ return !!(authScheme.type === "oauth2" &&
229
+ authScheme.flows &&
230
+ authScheme.flows.authorizationCode);
375
231
  }
376
232
  static getAuthArray(securities, spec) {
377
233
  var _a;
@@ -384,7 +240,7 @@ class Utils {
384
240
  for (const name in security) {
385
241
  const auth = securitySchemas[name];
386
242
  authArray.push({
387
- authSchema: auth,
243
+ authScheme: auth,
388
244
  name: name,
389
245
  });
390
246
  }
@@ -402,18 +258,22 @@ class Utils {
402
258
  static getResponseJson(operationObject) {
403
259
  var _a, _b;
404
260
  let json = {};
261
+ let multipleMediaType = false;
405
262
  for (const code of ConstantString.ResponseCodeFor20X) {
406
263
  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
264
  if ((_b = responseObject === null || responseObject === void 0 ? void 0 : responseObject.content) === null || _b === void 0 ? void 0 : _b["application/json"]) {
265
+ multipleMediaType = false;
412
266
  json = responseObject.content["application/json"];
413
- break;
267
+ if (Utils.containMultipleMediaTypes(responseObject)) {
268
+ multipleMediaType = true;
269
+ json = {};
270
+ }
271
+ else {
272
+ break;
273
+ }
414
274
  }
415
275
  }
416
- return json;
276
+ return { json, multipleMediaType };
417
277
  }
418
278
  static convertPathToCamelCase(path) {
419
279
  const pathSegments = path.split(/[./{]/);
@@ -433,10 +293,10 @@ class Utils {
433
293
  return undefined;
434
294
  }
435
295
  }
436
- static resolveServerUrl(url) {
296
+ static resolveEnv(str) {
437
297
  const placeHolderReg = /\${{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*}}/g;
438
- let matches = placeHolderReg.exec(url);
439
- let newUrl = url;
298
+ let matches = placeHolderReg.exec(str);
299
+ let newStr = str;
440
300
  while (matches != null) {
441
301
  const envVar = matches[1];
442
302
  const envVal = process.env[envVar];
@@ -444,17 +304,17 @@ class Utils {
444
304
  throw new Error(Utils.format(ConstantString.ResolveServerUrlFailed, envVar));
445
305
  }
446
306
  else {
447
- newUrl = newUrl.replace(matches[0], envVal);
307
+ newStr = newStr.replace(matches[0], envVal);
448
308
  }
449
- matches = placeHolderReg.exec(url);
309
+ matches = placeHolderReg.exec(str);
450
310
  }
451
- return newUrl;
311
+ return newStr;
452
312
  }
453
313
  static checkServerUrl(servers) {
454
314
  const errors = [];
455
315
  let serverUrl;
456
316
  try {
457
- serverUrl = Utils.resolveServerUrl(servers[0].url);
317
+ serverUrl = Utils.resolveEnv(servers[0].url);
458
318
  }
459
319
  catch (err) {
460
320
  errors.push({
@@ -484,7 +344,8 @@ class Utils {
484
344
  }
485
345
  return errors;
486
346
  }
487
- static validateServer(spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2) {
347
+ static validateServer(spec, options) {
348
+ var _a;
488
349
  const errors = [];
489
350
  let hasTopLevelServers = false;
490
351
  let hasPathLevelServers = false;
@@ -505,7 +366,7 @@ class Utils {
505
366
  }
506
367
  for (const method in methods) {
507
368
  const operationObject = methods[method];
508
- if (Utils.isSupportedApi(method, path, spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2)) {
369
+ if (((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && operationObject) {
509
370
  if ((operationObject === null || operationObject === void 0 ? void 0 : operationObject.servers) && operationObject.servers.length >= 1) {
510
371
  hasOperationLevelServers = true;
511
372
  const serverErrors = Utils.checkServerUrl(operationObject.servers);
@@ -548,6 +409,7 @@ class Utils {
548
409
  Utils.updateParameterWithInputType(schema, parameter);
549
410
  }
550
411
  if (isRequired && schema.default === undefined) {
412
+ parameter.isRequired = true;
551
413
  requiredParams.push(parameter);
552
414
  }
553
415
  else {
@@ -592,7 +454,7 @@ class Utils {
592
454
  param.value = schema.default;
593
455
  }
594
456
  }
595
- static parseApiInfo(operationItem, allowMultipleParameters) {
457
+ static parseApiInfo(operationItem, options) {
596
458
  var _a, _b;
597
459
  const requiredParams = [];
598
460
  const optionalParams = [];
@@ -606,11 +468,12 @@ class Utils {
606
468
  description: ((_a = param.description) !== null && _a !== void 0 ? _a : "").slice(0, ConstantString.ParameterDescriptionMaxLens),
607
469
  };
608
470
  const schema = param.schema;
609
- if (allowMultipleParameters && schema) {
471
+ if (options.allowMultipleParameters && schema) {
610
472
  Utils.updateParameterWithInputType(schema, parameter);
611
473
  }
612
474
  if (param.in !== "header" && param.in !== "cookie") {
613
475
  if (param.required && (schema === null || schema === void 0 ? void 0 : schema.default) === undefined) {
476
+ parameter.isRequired = true;
614
477
  requiredParams.push(parameter);
615
478
  }
616
479
  else {
@@ -624,19 +487,13 @@ class Utils {
624
487
  const requestJson = requestBody.content["application/json"];
625
488
  if (Object.keys(requestJson).length !== 0) {
626
489
  const schema = requestJson.schema;
627
- const [requiredP, optionalP] = Utils.generateParametersFromSchema(schema, "requestBody", allowMultipleParameters, requestBody.required);
490
+ const [requiredP, optionalP] = Utils.generateParametersFromSchema(schema, "requestBody", !!options.allowMultipleParameters, requestBody.required);
628
491
  requiredParams.push(...requiredP);
629
492
  optionalParams.push(...optionalP);
630
493
  }
631
494
  }
632
495
  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
- }
496
+ const parameters = [...requiredParams, ...optionalParams];
640
497
  const command = {
641
498
  context: ["compose"],
642
499
  type: "query",
@@ -645,32 +502,9 @@ class Utils {
645
502
  parameters: parameters,
646
503
  description: ((_b = operationItem.description) !== null && _b !== void 0 ? _b : "").slice(0, ConstantString.CommandDescriptionMaxLens),
647
504
  };
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
- };
655
- }
656
- return [command, warning];
657
- }
658
- static listSupportedAPIs(spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2) {
659
- const paths = spec.paths;
660
- const result = {};
661
- for (const path in paths) {
662
- const methods = paths[path];
663
- 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;
668
- }
669
- }
670
- }
671
- return result;
505
+ return command;
672
506
  }
673
- static validateSpec(spec, parser, isSwaggerFile, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2) {
507
+ static validateSpec(spec, parser, apiMap, isSwaggerFile, options) {
674
508
  const errors = [];
675
509
  const warnings = [];
676
510
  if (isSwaggerFile) {
@@ -679,8 +513,7 @@ class Utils {
679
513
  content: ConstantString.ConvertSwaggerToOpenAPI,
680
514
  });
681
515
  }
682
- // Server validation
683
- const serverErrors = Utils.validateServer(spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2);
516
+ const serverErrors = Utils.validateServer(spec, options);
684
517
  errors.push(...serverErrors);
685
518
  // Remote reference not supported
686
519
  const refPaths = parser.$refs.paths();
@@ -693,8 +526,8 @@ class Utils {
693
526
  });
694
527
  }
695
528
  // No supported API
696
- const apiMap = Utils.listSupportedAPIs(spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2);
697
- if (Object.keys(apiMap).length === 0) {
529
+ const validAPIs = Object.entries(apiMap).filter(([, value]) => value.isValid);
530
+ if (validAPIs.length === 0) {
698
531
  errors.push({
699
532
  type: ErrorType.NoSupportedApi,
700
533
  content: ConstantString.NoSupportedApi,
@@ -703,8 +536,8 @@ class Utils {
703
536
  // OperationId missing
704
537
  const apisMissingOperationId = [];
705
538
  for (const key in apiMap) {
706
- const pathObjectItem = apiMap[key];
707
- if (!pathObjectItem.operationId) {
539
+ const { operation } = apiMap[key];
540
+ if (!operation.operationId) {
708
541
  apisMissingOperationId.push(key);
709
542
  }
710
543
  }
@@ -745,6 +578,402 @@ class Utils {
745
578
  }
746
579
  return safeRegistrationIdEnvName;
747
580
  }
581
+ static getAllAPICount(spec) {
582
+ let count = 0;
583
+ const paths = spec.paths;
584
+ for (const path in paths) {
585
+ const methods = paths[path];
586
+ for (const method in methods) {
587
+ if (ConstantString.AllOperationMethods.includes(method)) {
588
+ count++;
589
+ }
590
+ }
591
+ }
592
+ return count;
593
+ }
594
+ }
595
+
596
+ // Copyright (c) Microsoft Corporation.
597
+ class Validator {
598
+ validateMethodAndPath(method, path) {
599
+ const result = { isValid: true, reason: [] };
600
+ if (this.options.allowMethods && !this.options.allowMethods.includes(method)) {
601
+ result.isValid = false;
602
+ result.reason.push(ErrorType.MethodNotAllowed);
603
+ return result;
604
+ }
605
+ const pathObj = this.spec.paths[path];
606
+ if (!pathObj || !pathObj[method]) {
607
+ result.isValid = false;
608
+ result.reason.push(ErrorType.UrlPathNotExist);
609
+ return result;
610
+ }
611
+ return result;
612
+ }
613
+ validateResponse(method, path) {
614
+ const result = { isValid: true, reason: [] };
615
+ const operationObject = this.spec.paths[path][method];
616
+ const { json, multipleMediaType } = Utils.getResponseJson(operationObject);
617
+ // only support response body only contains “application/json” content type
618
+ if (multipleMediaType) {
619
+ result.reason.push(ErrorType.ResponseContainMultipleMediaTypes);
620
+ }
621
+ else if (Object.keys(json).length === 0) {
622
+ // response body should not be empty
623
+ result.reason.push(ErrorType.ResponseJsonIsEmpty);
624
+ }
625
+ return result;
626
+ }
627
+ validateServer(method, path) {
628
+ const pathObj = this.spec.paths[path];
629
+ const result = { isValid: true, reason: [] };
630
+ const operationObject = pathObj[method];
631
+ const rootServer = this.spec.servers && this.spec.servers[0];
632
+ const methodServer = this.spec.paths[path].servers && this.spec.paths[path].servers[0];
633
+ const operationServer = operationObject.servers && operationObject.servers[0];
634
+ const serverUrl = operationServer || methodServer || rootServer;
635
+ if (!serverUrl) {
636
+ // should contain server URL
637
+ result.reason.push(ErrorType.NoServerInformation);
638
+ }
639
+ else {
640
+ // server url should be absolute url with https protocol
641
+ const serverValidateResult = Utils.checkServerUrl([serverUrl]);
642
+ result.reason.push(...serverValidateResult.map((item) => item.type));
643
+ }
644
+ return result;
645
+ }
646
+ validateAuth(method, path) {
647
+ const pathObj = this.spec.paths[path];
648
+ const operationObject = pathObj[method];
649
+ const securities = operationObject.security;
650
+ const authSchemeArray = Utils.getAuthArray(securities, this.spec);
651
+ if (authSchemeArray.length === 0) {
652
+ return { isValid: true, reason: [] };
653
+ }
654
+ if (this.options.allowAPIKeyAuth ||
655
+ this.options.allowOauth2 ||
656
+ this.options.allowBearerTokenAuth) {
657
+ // Currently we don't support multiple auth in one operation
658
+ if (authSchemeArray.length > 0 && authSchemeArray.every((auths) => auths.length > 1)) {
659
+ return {
660
+ isValid: false,
661
+ reason: [ErrorType.MultipleAuthNotSupported],
662
+ };
663
+ }
664
+ for (const auths of authSchemeArray) {
665
+ if (auths.length === 1) {
666
+ if ((this.options.allowAPIKeyAuth && Utils.isAPIKeyAuth(auths[0].authScheme)) ||
667
+ (this.options.allowOauth2 && Utils.isOAuthWithAuthCodeFlow(auths[0].authScheme)) ||
668
+ (this.options.allowBearerTokenAuth && Utils.isBearerTokenAuth(auths[0].authScheme))) {
669
+ return { isValid: true, reason: [] };
670
+ }
671
+ }
672
+ }
673
+ }
674
+ return { isValid: false, reason: [ErrorType.AuthTypeIsNotSupported] };
675
+ }
676
+ checkPostBodySchema(schema, isRequired = false) {
677
+ var _a;
678
+ const paramResult = {
679
+ requiredNum: 0,
680
+ optionalNum: 0,
681
+ isValid: true,
682
+ reason: [],
683
+ };
684
+ if (Object.keys(schema).length === 0) {
685
+ return paramResult;
686
+ }
687
+ const isRequiredWithoutDefault = isRequired && schema.default === undefined;
688
+ const isCopilot = this.projectType === ProjectType.Copilot;
689
+ if (isCopilot && this.hasNestedObjectInSchema(schema)) {
690
+ paramResult.isValid = false;
691
+ paramResult.reason = [ErrorType.RequestBodyContainsNestedObject];
692
+ return paramResult;
693
+ }
694
+ if (schema.type === "string" ||
695
+ schema.type === "integer" ||
696
+ schema.type === "boolean" ||
697
+ schema.type === "number") {
698
+ if (isRequiredWithoutDefault) {
699
+ paramResult.requiredNum = paramResult.requiredNum + 1;
700
+ }
701
+ else {
702
+ paramResult.optionalNum = paramResult.optionalNum + 1;
703
+ }
704
+ }
705
+ else if (schema.type === "object") {
706
+ const { properties } = schema;
707
+ for (const property in properties) {
708
+ let isRequired = false;
709
+ if (schema.required && ((_a = schema.required) === null || _a === void 0 ? void 0 : _a.indexOf(property)) >= 0) {
710
+ isRequired = true;
711
+ }
712
+ const result = this.checkPostBodySchema(properties[property], isRequired);
713
+ paramResult.requiredNum += result.requiredNum;
714
+ paramResult.optionalNum += result.optionalNum;
715
+ paramResult.isValid = paramResult.isValid && result.isValid;
716
+ paramResult.reason.push(...result.reason);
717
+ }
718
+ }
719
+ else {
720
+ if (isRequiredWithoutDefault && !isCopilot) {
721
+ paramResult.isValid = false;
722
+ paramResult.reason.push(ErrorType.PostBodyContainsRequiredUnsupportedSchema);
723
+ }
724
+ }
725
+ return paramResult;
726
+ }
727
+ checkParamSchema(paramObject) {
728
+ const paramResult = {
729
+ requiredNum: 0,
730
+ optionalNum: 0,
731
+ isValid: true,
732
+ reason: [],
733
+ };
734
+ if (!paramObject) {
735
+ return paramResult;
736
+ }
737
+ const isCopilot = this.projectType === ProjectType.Copilot;
738
+ for (let i = 0; i < paramObject.length; i++) {
739
+ const param = paramObject[i];
740
+ const schema = param.schema;
741
+ if (isCopilot && this.hasNestedObjectInSchema(schema)) {
742
+ paramResult.isValid = false;
743
+ paramResult.reason.push(ErrorType.ParamsContainsNestedObject);
744
+ continue;
745
+ }
746
+ const isRequiredWithoutDefault = param.required && schema.default === undefined;
747
+ if (isCopilot) {
748
+ if (isRequiredWithoutDefault) {
749
+ paramResult.requiredNum = paramResult.requiredNum + 1;
750
+ }
751
+ else {
752
+ paramResult.optionalNum = paramResult.optionalNum + 1;
753
+ }
754
+ continue;
755
+ }
756
+ if (param.in === "header" || param.in === "cookie") {
757
+ if (isRequiredWithoutDefault) {
758
+ paramResult.isValid = false;
759
+ paramResult.reason.push(ErrorType.ParamsContainRequiredUnsupportedSchema);
760
+ }
761
+ continue;
762
+ }
763
+ if (schema.type !== "boolean" &&
764
+ schema.type !== "string" &&
765
+ schema.type !== "number" &&
766
+ schema.type !== "integer") {
767
+ if (isRequiredWithoutDefault) {
768
+ paramResult.isValid = false;
769
+ paramResult.reason.push(ErrorType.ParamsContainRequiredUnsupportedSchema);
770
+ }
771
+ continue;
772
+ }
773
+ if (param.in === "query" || param.in === "path") {
774
+ if (isRequiredWithoutDefault) {
775
+ paramResult.requiredNum = paramResult.requiredNum + 1;
776
+ }
777
+ else {
778
+ paramResult.optionalNum = paramResult.optionalNum + 1;
779
+ }
780
+ }
781
+ }
782
+ return paramResult;
783
+ }
784
+ hasNestedObjectInSchema(schema) {
785
+ if (schema.type === "object") {
786
+ for (const property in schema.properties) {
787
+ const nestedSchema = schema.properties[property];
788
+ if (nestedSchema.type === "object") {
789
+ return true;
790
+ }
791
+ }
792
+ }
793
+ return false;
794
+ }
795
+ }
796
+
797
+ // Copyright (c) Microsoft Corporation.
798
+ class CopilotValidator extends Validator {
799
+ constructor(spec, options) {
800
+ super();
801
+ this.projectType = ProjectType.Copilot;
802
+ this.options = options;
803
+ this.spec = spec;
804
+ }
805
+ validateAPI(method, path) {
806
+ const result = { isValid: true, reason: [] };
807
+ method = method.toLocaleLowerCase();
808
+ // validate method and path
809
+ const methodAndPathResult = this.validateMethodAndPath(method, path);
810
+ if (!methodAndPathResult.isValid) {
811
+ return methodAndPathResult;
812
+ }
813
+ const operationObject = this.spec.paths[path][method];
814
+ // validate auth
815
+ const authCheckResult = this.validateAuth(method, path);
816
+ result.reason.push(...authCheckResult.reason);
817
+ // validate operationId
818
+ if (!this.options.allowMissingId && !operationObject.operationId) {
819
+ result.reason.push(ErrorType.MissingOperationId);
820
+ }
821
+ // validate server
822
+ const validateServerResult = this.validateServer(method, path);
823
+ result.reason.push(...validateServerResult.reason);
824
+ // validate response
825
+ const validateResponseResult = this.validateResponse(method, path);
826
+ result.reason.push(...validateResponseResult.reason);
827
+ // validate requestBody
828
+ const requestBody = operationObject.requestBody;
829
+ const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
830
+ if (Utils.containMultipleMediaTypes(requestBody)) {
831
+ result.reason.push(ErrorType.PostBodyContainMultipleMediaTypes);
832
+ }
833
+ if (requestJsonBody) {
834
+ const requestBodySchema = requestJsonBody.schema;
835
+ if (requestBodySchema.type !== "object") {
836
+ result.reason.push(ErrorType.PostBodySchemaIsNotJson);
837
+ }
838
+ const requestBodyParamResult = this.checkPostBodySchema(requestBodySchema, requestBody.required);
839
+ result.reason.push(...requestBodyParamResult.reason);
840
+ }
841
+ // validate parameters
842
+ const paramObject = operationObject.parameters;
843
+ const paramResult = this.checkParamSchema(paramObject);
844
+ result.reason.push(...paramResult.reason);
845
+ if (result.reason.length > 0) {
846
+ result.isValid = false;
847
+ }
848
+ return result;
849
+ }
850
+ }
851
+
852
+ // Copyright (c) Microsoft Corporation.
853
+ class SMEValidator extends Validator {
854
+ constructor(spec, options) {
855
+ super();
856
+ this.projectType = ProjectType.SME;
857
+ this.options = options;
858
+ this.spec = spec;
859
+ }
860
+ validateAPI(method, path) {
861
+ const result = { isValid: true, reason: [] };
862
+ method = method.toLocaleLowerCase();
863
+ // validate method and path
864
+ const methodAndPathResult = this.validateMethodAndPath(method, path);
865
+ if (!methodAndPathResult.isValid) {
866
+ return methodAndPathResult;
867
+ }
868
+ const operationObject = this.spec.paths[path][method];
869
+ // validate auth
870
+ const authCheckResult = this.validateAuth(method, path);
871
+ result.reason.push(...authCheckResult.reason);
872
+ // validate operationId
873
+ if (!this.options.allowMissingId && !operationObject.operationId) {
874
+ result.reason.push(ErrorType.MissingOperationId);
875
+ }
876
+ // validate server
877
+ const validateServerResult = this.validateServer(method, path);
878
+ result.reason.push(...validateServerResult.reason);
879
+ // validate response
880
+ const validateResponseResult = this.validateResponse(method, path);
881
+ result.reason.push(...validateResponseResult.reason);
882
+ let postBodyResult = {
883
+ requiredNum: 0,
884
+ optionalNum: 0,
885
+ isValid: true,
886
+ reason: [],
887
+ };
888
+ // validate requestBody
889
+ const requestBody = operationObject.requestBody;
890
+ const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
891
+ if (Utils.containMultipleMediaTypes(requestBody)) {
892
+ result.reason.push(ErrorType.PostBodyContainMultipleMediaTypes);
893
+ }
894
+ if (requestJsonBody) {
895
+ const requestBodySchema = requestJsonBody.schema;
896
+ postBodyResult = this.checkPostBodySchema(requestBodySchema, requestBody.required);
897
+ result.reason.push(...postBodyResult.reason);
898
+ }
899
+ // validate parameters
900
+ const paramObject = operationObject.parameters;
901
+ const paramResult = this.checkParamSchema(paramObject);
902
+ result.reason.push(...paramResult.reason);
903
+ // validate total parameters count
904
+ if (paramResult.isValid && postBodyResult.isValid) {
905
+ const paramCountResult = this.validateParamCount(postBodyResult, paramResult);
906
+ result.reason.push(...paramCountResult.reason);
907
+ }
908
+ if (result.reason.length > 0) {
909
+ result.isValid = false;
910
+ }
911
+ return result;
912
+ }
913
+ validateParamCount(postBodyResult, paramResult) {
914
+ const result = { isValid: true, reason: [] };
915
+ const totalRequiredParams = postBodyResult.requiredNum + paramResult.requiredNum;
916
+ const totalParams = totalRequiredParams + postBodyResult.optionalNum + paramResult.optionalNum;
917
+ if (totalRequiredParams > 1) {
918
+ if (!this.options.allowMultipleParameters ||
919
+ totalRequiredParams > SMEValidator.SMERequiredParamsMaxNum) {
920
+ result.reason.push(ErrorType.ExceededRequiredParamsLimit);
921
+ }
922
+ }
923
+ else if (totalParams === 0) {
924
+ result.reason.push(ErrorType.NoParameter);
925
+ }
926
+ return result;
927
+ }
928
+ }
929
+ SMEValidator.SMERequiredParamsMaxNum = 5;
930
+
931
+ // Copyright (c) Microsoft Corporation.
932
+ class TeamsAIValidator extends Validator {
933
+ constructor(spec, options) {
934
+ super();
935
+ this.projectType = ProjectType.TeamsAi;
936
+ this.options = options;
937
+ this.spec = spec;
938
+ }
939
+ validateAPI(method, path) {
940
+ const result = { isValid: true, reason: [] };
941
+ method = method.toLocaleLowerCase();
942
+ // validate method and path
943
+ const methodAndPathResult = this.validateMethodAndPath(method, path);
944
+ if (!methodAndPathResult.isValid) {
945
+ return methodAndPathResult;
946
+ }
947
+ const operationObject = this.spec.paths[path][method];
948
+ // validate operationId
949
+ if (!this.options.allowMissingId && !operationObject.operationId) {
950
+ result.reason.push(ErrorType.MissingOperationId);
951
+ }
952
+ // validate server
953
+ const validateServerResult = this.validateServer(method, path);
954
+ result.reason.push(...validateServerResult.reason);
955
+ if (result.reason.length > 0) {
956
+ result.isValid = false;
957
+ }
958
+ return result;
959
+ }
960
+ }
961
+
962
+ class ValidatorFactory {
963
+ static create(spec, options) {
964
+ var _a;
965
+ const type = (_a = options.projectType) !== null && _a !== void 0 ? _a : ProjectType.SME;
966
+ switch (type) {
967
+ case ProjectType.SME:
968
+ return new SMEValidator(spec, options);
969
+ case ProjectType.Copilot:
970
+ return new CopilotValidator(spec, options);
971
+ case ProjectType.TeamsAi:
972
+ return new TeamsAIValidator(spec, options);
973
+ default:
974
+ throw new Error(`Invalid project type: ${type}`);
975
+ }
976
+ }
748
977
  }
749
978
 
750
979
  // Copyright (c) Microsoft Corporation.
@@ -763,7 +992,10 @@ class SpecParser {
763
992
  allowSwagger: false,
764
993
  allowAPIKeyAuth: false,
765
994
  allowMultipleParameters: false,
995
+ allowBearerTokenAuth: false,
766
996
  allowOauth2: false,
997
+ allowMethods: ["get", "post"],
998
+ projectType: ProjectType.SME,
767
999
  };
768
1000
  this.pathOrSpec = pathOrDoc;
769
1001
  this.parser = new SwaggerParser();
@@ -801,7 +1033,8 @@ class SpecParser {
801
1033
  ],
802
1034
  };
803
1035
  }
804
- return Utils.validateSpec(this.spec, this.parser, !!this.isSwaggerFile, this.options.allowMissingId, this.options.allowAPIKeyAuth, this.options.allowMultipleParameters, this.options.allowOauth2);
1036
+ const apiMap = this.getAPIs(this.spec);
1037
+ return Utils.validateSpec(this.spec, this.parser, apiMap, !!this.isSwaggerFile, this.options);
805
1038
  }
806
1039
  catch (err) {
807
1040
  throw new SpecParserError(err.toString(), ErrorType.ValidateFailed);
@@ -812,17 +1045,20 @@ class SpecParser {
812
1045
  return __awaiter(this, void 0, void 0, function* () {
813
1046
  try {
814
1047
  yield this.loadSpec();
815
- const apiMap = this.getAllSupportedAPIs(this.spec);
1048
+ const apiMap = this.getAPIs(this.spec);
816
1049
  const apiInfos = [];
817
1050
  for (const key in apiMap) {
818
- const pathObjectItem = apiMap[key];
1051
+ const { operation, isValid } = apiMap[key];
1052
+ if (!isValid) {
1053
+ continue;
1054
+ }
819
1055
  const [method, path] = key.split(" ");
820
- const operationId = pathObjectItem.operationId;
1056
+ const operationId = operation.operationId;
821
1057
  // In Browser environment, this api is by default not support api without operationId
822
1058
  if (!operationId) {
823
1059
  continue;
824
1060
  }
825
- const [command, warning] = Utils.parseApiInfo(pathObjectItem, this.options.allowMultipleParameters);
1061
+ const command = Utils.parseApiInfo(operation, this.options);
826
1062
  const apiInfo = {
827
1063
  method: method,
828
1064
  path: path,
@@ -831,9 +1067,6 @@ class SpecParser {
831
1067
  parameters: command.parameters,
832
1068
  description: command.description,
833
1069
  };
834
- if (warning) {
835
- apiInfo.warning = warning;
836
- }
837
1070
  apiInfos.push(apiInfo);
838
1071
  }
839
1072
  return apiInfos;
@@ -854,12 +1087,36 @@ class SpecParser {
854
1087
  throw new Error("Method not implemented.");
855
1088
  });
856
1089
  }
1090
+ /**
1091
+ * Generate specs according to the filters.
1092
+ * @param filter An array of strings that represent the filters to apply when generating the artifacts. If filter is empty, it would process nothing.
1093
+ */
1094
+ // eslint-disable-next-line @typescript-eslint/require-await
1095
+ getFilteredSpecs(filter, signal) {
1096
+ return __awaiter(this, void 0, void 0, function* () {
1097
+ throw new Error("Method not implemented.");
1098
+ });
1099
+ }
1100
+ /**
1101
+ * Generates and update artifacts from the OpenAPI specification file. Generate Adaptive Cards, update Teams app manifest, and generate a new OpenAPI specification file.
1102
+ * @param manifestPath A file path of the Teams app manifest file to update.
1103
+ * @param filter An array of strings that represent the filters to apply when generating the artifacts. If filter is empty, it would process nothing.
1104
+ * @param outputSpecPath File path of the new OpenAPI specification file to generate. If not specified or empty, no spec file will be generated.
1105
+ * @param pluginFilePath File path of the api plugin file to generate.
1106
+ */
1107
+ // eslint-disable-next-line @typescript-eslint/require-await
1108
+ generateForCopilot(manifestPath, filter, outputSpecPath, pluginFilePath, signal) {
1109
+ return __awaiter(this, void 0, void 0, function* () {
1110
+ throw new Error("Method not implemented.");
1111
+ });
1112
+ }
857
1113
  /**
858
1114
  * Generates and update artifacts from the OpenAPI specification file. Generate Adaptive Cards, update Teams app manifest, and generate a new OpenAPI specification file.
859
1115
  * @param manifestPath A file path of the Teams app manifest file to update.
860
1116
  * @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
1117
  * @param outputSpecPath File path of the new OpenAPI specification file to generate. If not specified or empty, no spec file will be generated.
862
1118
  * @param adaptiveCardFolder Folder path where the Adaptive Card files will be generated. If not specified or empty, Adaptive Card files will not be generated.
1119
+ * @param isMe Boolean that indicates whether the project is an Messaging Extension. For Messaging Extension, composeExtensions will be added in Teams app manifest.
863
1120
  */
864
1121
  // eslint-disable-next-line @typescript-eslint/require-await
865
1122
  generate(manifestPath, filter, outputSpecPath, adaptiveCardFolder, signal) {
@@ -879,15 +1136,194 @@ class SpecParser {
879
1136
  }
880
1137
  });
881
1138
  }
882
- getAllSupportedAPIs(spec) {
1139
+ getAPIs(spec) {
883
1140
  if (this.apiMap !== undefined) {
884
1141
  return this.apiMap;
885
1142
  }
886
- const result = Utils.listSupportedAPIs(spec, this.options.allowMissingId, this.options.allowAPIKeyAuth, this.options.allowMultipleParameters, this.options.allowOauth2);
1143
+ const result = this.listAPIs(spec);
887
1144
  this.apiMap = result;
888
1145
  return result;
889
1146
  }
1147
+ listAPIs(spec) {
1148
+ var _a;
1149
+ const paths = spec.paths;
1150
+ const result = {};
1151
+ for (const path in paths) {
1152
+ const methods = paths[path];
1153
+ for (const method in methods) {
1154
+ const operationObject = methods[method];
1155
+ if (((_a = this.options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && operationObject) {
1156
+ const validator = ValidatorFactory.create(spec, this.options);
1157
+ const validateResult = validator.validateAPI(method, path);
1158
+ result[`${method.toUpperCase()} ${path}`] = {
1159
+ operation: operationObject,
1160
+ isValid: validateResult.isValid,
1161
+ reason: validateResult.reason,
1162
+ };
1163
+ }
1164
+ }
1165
+ }
1166
+ return result;
1167
+ }
1168
+ }
1169
+
1170
+ // Copyright (c) Microsoft Corporation.
1171
+ class AdaptiveCardGenerator {
1172
+ static generateAdaptiveCard(operationItem) {
1173
+ try {
1174
+ const { json } = Utils.getResponseJson(operationItem);
1175
+ let cardBody = [];
1176
+ let schema = json.schema;
1177
+ let jsonPath = "$";
1178
+ if (schema && Object.keys(schema).length > 0) {
1179
+ jsonPath = AdaptiveCardGenerator.getResponseJsonPathFromSchema(schema);
1180
+ if (jsonPath !== "$") {
1181
+ schema = schema.properties[jsonPath];
1182
+ }
1183
+ cardBody = AdaptiveCardGenerator.generateCardFromResponse(schema, "");
1184
+ }
1185
+ // if no schema, try to use example value
1186
+ if (cardBody.length === 0 && (json.examples || json.example)) {
1187
+ cardBody = [
1188
+ {
1189
+ type: ConstantString.TextBlockType,
1190
+ text: "${jsonStringify($root)}",
1191
+ wrap: true,
1192
+ },
1193
+ ];
1194
+ }
1195
+ // if no example value, use default success response
1196
+ if (cardBody.length === 0) {
1197
+ cardBody = [
1198
+ {
1199
+ type: ConstantString.TextBlockType,
1200
+ text: "success",
1201
+ wrap: true,
1202
+ },
1203
+ ];
1204
+ }
1205
+ const fullCard = {
1206
+ type: ConstantString.AdaptiveCardType,
1207
+ $schema: ConstantString.AdaptiveCardSchema,
1208
+ version: ConstantString.AdaptiveCardVersion,
1209
+ body: cardBody,
1210
+ };
1211
+ return [fullCard, jsonPath];
1212
+ }
1213
+ catch (err) {
1214
+ throw new SpecParserError(err.toString(), ErrorType.GenerateAdaptiveCardFailed);
1215
+ }
1216
+ }
1217
+ static generateCardFromResponse(schema, name, parentArrayName = "") {
1218
+ if (schema.type === "array") {
1219
+ // schema.items can be arbitrary object: schema { type: array, items: {} }
1220
+ if (Object.keys(schema.items).length === 0) {
1221
+ return [
1222
+ {
1223
+ type: ConstantString.TextBlockType,
1224
+ text: name ? `${name}: \${jsonStringify(${name})}` : "result: ${jsonStringify($root)}",
1225
+ wrap: true,
1226
+ },
1227
+ ];
1228
+ }
1229
+ const obj = AdaptiveCardGenerator.generateCardFromResponse(schema.items, "", name);
1230
+ const template = {
1231
+ type: ConstantString.ContainerType,
1232
+ $data: name ? `\${${name}}` : "${$root}",
1233
+ items: Array(),
1234
+ };
1235
+ template.items.push(...obj);
1236
+ return [template];
1237
+ }
1238
+ // some schema may not contain type but contain properties
1239
+ if (schema.type === "object" || (!schema.type && schema.properties)) {
1240
+ const { properties } = schema;
1241
+ const result = [];
1242
+ for (const property in properties) {
1243
+ const obj = AdaptiveCardGenerator.generateCardFromResponse(properties[property], name ? `${name}.${property}` : property, parentArrayName);
1244
+ result.push(...obj);
1245
+ }
1246
+ if (schema.additionalProperties) {
1247
+ // TODO: better ways to handler warnings.
1248
+ console.warn(ConstantString.AdditionalPropertiesNotSupported);
1249
+ }
1250
+ return result;
1251
+ }
1252
+ if (schema.type === "string" ||
1253
+ schema.type === "integer" ||
1254
+ schema.type === "boolean" ||
1255
+ schema.type === "number") {
1256
+ if (!AdaptiveCardGenerator.isImageUrlProperty(schema, name, parentArrayName)) {
1257
+ // string in root: "ddd"
1258
+ let text = "result: ${$root}";
1259
+ if (name) {
1260
+ // object { id: "1" }
1261
+ text = `${name}: \${if(${name}, ${name}, 'N/A')}`;
1262
+ if (parentArrayName) {
1263
+ // object types inside array: { tags: ["id": 1, "name": "name"] }
1264
+ text = `${parentArrayName}.${text}`;
1265
+ }
1266
+ }
1267
+ else if (parentArrayName) {
1268
+ // string array: photoUrls: ["1", "2"]
1269
+ text = `${parentArrayName}: ` + "${$data}";
1270
+ }
1271
+ return [
1272
+ {
1273
+ type: ConstantString.TextBlockType,
1274
+ text,
1275
+ wrap: true,
1276
+ },
1277
+ ];
1278
+ }
1279
+ else {
1280
+ if (name) {
1281
+ return [
1282
+ {
1283
+ type: "Image",
1284
+ url: `\${${name}}`,
1285
+ $when: `\${${name} != null}`,
1286
+ },
1287
+ ];
1288
+ }
1289
+ else {
1290
+ return [
1291
+ {
1292
+ type: "Image",
1293
+ url: "${$data}",
1294
+ $when: "${$data != null}",
1295
+ },
1296
+ ];
1297
+ }
1298
+ }
1299
+ }
1300
+ if (schema.oneOf || schema.anyOf || schema.not || schema.allOf) {
1301
+ throw new Error(Utils.format(ConstantString.SchemaNotSupported, JSON.stringify(schema)));
1302
+ }
1303
+ throw new Error(Utils.format(ConstantString.UnknownSchema, JSON.stringify(schema)));
1304
+ }
1305
+ // Find the first array property in the response schema object with the well-known name
1306
+ static getResponseJsonPathFromSchema(schema) {
1307
+ if (schema.type === "object" || (!schema.type && schema.properties)) {
1308
+ const { properties } = schema;
1309
+ for (const property in properties) {
1310
+ const schema = properties[property];
1311
+ if (schema.type === "array" &&
1312
+ Utils.isWellKnownName(property, ConstantString.WellknownResultNames)) {
1313
+ return property;
1314
+ }
1315
+ }
1316
+ }
1317
+ return "$";
1318
+ }
1319
+ static isImageUrlProperty(schema, name, parentArrayName) {
1320
+ const propertyName = name ? name : parentArrayName;
1321
+ return (!!propertyName &&
1322
+ schema.type === "string" &&
1323
+ Utils.isWellKnownName(propertyName, ConstantString.WellknownImageName) &&
1324
+ (propertyName.toLocaleLowerCase().indexOf("url") >= 0 || schema.format === "uri"));
1325
+ }
890
1326
  }
891
1327
 
892
- export { ConstantString, ErrorType, SpecParser, SpecParserError, Utils, ValidationStatus, WarningType };
1328
+ export { AdaptiveCardGenerator, ConstantString, ErrorType, SpecParser, SpecParserError, Utils, ValidationStatus, WarningType };
893
1329
  //# sourceMappingURL=index.esm5.js.map