@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.
@@ -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 {
@@ -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";
@@ -84,6 +109,7 @@ ConstantString.AdaptiveCardType = "AdaptiveCard";
84
109
  ConstantString.TextBlockType = "TextBlock";
85
110
  ConstantString.ContainerType = "Container";
86
111
  ConstantString.RegistrationIdPostfix = "REGISTRATION_ID";
112
+ ConstantString.OAuthRegistrationIdPostFix = "OAUTH_REGISTRATION_ID";
87
113
  ConstantString.ResponseCodeFor20X = [
88
114
  "200",
89
115
  "201",
@@ -143,205 +169,35 @@ ConstantString.FullDescriptionMaxLens = 4000;
143
169
  ConstantString.CommandDescriptionMaxLens = 128;
144
170
  ConstantString.ParameterDescriptionMaxLens = 128;
145
171
  ConstantString.CommandTitleMaxLens = 32;
146
- ConstantString.ParameterTitleMaxLens = 32;
172
+ ConstantString.ParameterTitleMaxLens = 32;
173
+ ConstantString.SMERequiredParamsMaxNum = 5;
147
174
 
148
175
  // Copyright (c) Microsoft Corporation.
149
176
  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 {
177
+ static hasNestedObjectInSchema(schema) {
178
+ if (schema.type === "object") {
179
+ for (const property in schema.properties) {
180
+ const nestedSchema = schema.properties[property];
181
+ if (nestedSchema.type === "object") {
302
182
  return true;
303
183
  }
304
184
  }
305
185
  }
306
186
  return false;
307
187
  }
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;
188
+ static containMultipleMediaTypes(bodyObject) {
189
+ return Object.keys((bodyObject === null || bodyObject === void 0 ? void 0 : bodyObject.content) || {}).length > 1;
190
+ }
191
+ static isBearerTokenAuth(authScheme) {
192
+ return authScheme.type === "http" && authScheme.scheme === "bearer";
337
193
  }
338
- static isAPIKeyAuth(authSchema) {
339
- return authSchema.type === "apiKey";
194
+ static isAPIKeyAuth(authScheme) {
195
+ return authScheme.type === "apiKey";
340
196
  }
341
- static isBearerTokenAuth(authSchema) {
342
- return (authSchema.type === "oauth2" ||
343
- authSchema.type === "openIdConnect" ||
344
- (authSchema.type === "http" && authSchema.scheme === "bearer"));
197
+ static isOAuthWithAuthCodeFlow(authScheme) {
198
+ return !!(authScheme.type === "oauth2" &&
199
+ authScheme.flows &&
200
+ authScheme.flows.authorizationCode);
345
201
  }
346
202
  static getAuthArray(securities, spec) {
347
203
  var _a;
@@ -354,7 +210,7 @@ class Utils {
354
210
  for (const name in security) {
355
211
  const auth = securitySchemas[name];
356
212
  authArray.push({
357
- authSchema: auth,
213
+ authScheme: auth,
358
214
  name: name,
359
215
  });
360
216
  }
@@ -372,18 +228,22 @@ class Utils {
372
228
  static getResponseJson(operationObject) {
373
229
  var _a, _b;
374
230
  let json = {};
231
+ let multipleMediaType = false;
375
232
  for (const code of ConstantString.ResponseCodeFor20X) {
376
233
  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
234
  if ((_b = responseObject === null || responseObject === void 0 ? void 0 : responseObject.content) === null || _b === void 0 ? void 0 : _b["application/json"]) {
235
+ multipleMediaType = false;
382
236
  json = responseObject.content["application/json"];
383
- break;
237
+ if (Utils.containMultipleMediaTypes(responseObject)) {
238
+ multipleMediaType = true;
239
+ json = {};
240
+ }
241
+ else {
242
+ break;
243
+ }
384
244
  }
385
245
  }
386
- return json;
246
+ return { json, multipleMediaType };
387
247
  }
388
248
  static convertPathToCamelCase(path) {
389
249
  const pathSegments = path.split(/[./{]/);
@@ -403,10 +263,10 @@ class Utils {
403
263
  return undefined;
404
264
  }
405
265
  }
406
- static resolveServerUrl(url) {
266
+ static resolveEnv(str) {
407
267
  const placeHolderReg = /\${{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*}}/g;
408
- let matches = placeHolderReg.exec(url);
409
- let newUrl = url;
268
+ let matches = placeHolderReg.exec(str);
269
+ let newStr = str;
410
270
  while (matches != null) {
411
271
  const envVar = matches[1];
412
272
  const envVal = process.env[envVar];
@@ -414,17 +274,17 @@ class Utils {
414
274
  throw new Error(Utils.format(ConstantString.ResolveServerUrlFailed, envVar));
415
275
  }
416
276
  else {
417
- newUrl = newUrl.replace(matches[0], envVal);
277
+ newStr = newStr.replace(matches[0], envVal);
418
278
  }
419
- matches = placeHolderReg.exec(url);
279
+ matches = placeHolderReg.exec(str);
420
280
  }
421
- return newUrl;
281
+ return newStr;
422
282
  }
423
283
  static checkServerUrl(servers) {
424
284
  const errors = [];
425
285
  let serverUrl;
426
286
  try {
427
- serverUrl = Utils.resolveServerUrl(servers[0].url);
287
+ serverUrl = Utils.resolveEnv(servers[0].url);
428
288
  }
429
289
  catch (err) {
430
290
  errors.push({
@@ -454,7 +314,8 @@ class Utils {
454
314
  }
455
315
  return errors;
456
316
  }
457
- static validateServer(spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2) {
317
+ static validateServer(spec, options) {
318
+ var _a;
458
319
  const errors = [];
459
320
  let hasTopLevelServers = false;
460
321
  let hasPathLevelServers = false;
@@ -475,7 +336,7 @@ class Utils {
475
336
  }
476
337
  for (const method in methods) {
477
338
  const operationObject = methods[method];
478
- if (Utils.isSupportedApi(method, path, spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2)) {
339
+ if (((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && operationObject) {
479
340
  if ((operationObject === null || operationObject === void 0 ? void 0 : operationObject.servers) && operationObject.servers.length >= 1) {
480
341
  hasOperationLevelServers = true;
481
342
  const serverErrors = Utils.checkServerUrl(operationObject.servers);
@@ -518,6 +379,7 @@ class Utils {
518
379
  Utils.updateParameterWithInputType(schema, parameter);
519
380
  }
520
381
  if (isRequired && schema.default === undefined) {
382
+ parameter.isRequired = true;
521
383
  requiredParams.push(parameter);
522
384
  }
523
385
  else {
@@ -562,7 +424,7 @@ class Utils {
562
424
  param.value = schema.default;
563
425
  }
564
426
  }
565
- static parseApiInfo(operationItem, allowMultipleParameters) {
427
+ static parseApiInfo(operationItem, options) {
566
428
  var _a, _b;
567
429
  const requiredParams = [];
568
430
  const optionalParams = [];
@@ -576,11 +438,12 @@ class Utils {
576
438
  description: ((_a = param.description) !== null && _a !== void 0 ? _a : "").slice(0, ConstantString.ParameterDescriptionMaxLens),
577
439
  };
578
440
  const schema = param.schema;
579
- if (allowMultipleParameters && schema) {
441
+ if (options.allowMultipleParameters && schema) {
580
442
  Utils.updateParameterWithInputType(schema, parameter);
581
443
  }
582
444
  if (param.in !== "header" && param.in !== "cookie") {
583
445
  if (param.required && (schema === null || schema === void 0 ? void 0 : schema.default) === undefined) {
446
+ parameter.isRequired = true;
584
447
  requiredParams.push(parameter);
585
448
  }
586
449
  else {
@@ -594,19 +457,13 @@ class Utils {
594
457
  const requestJson = requestBody.content["application/json"];
595
458
  if (Object.keys(requestJson).length !== 0) {
596
459
  const schema = requestJson.schema;
597
- const [requiredP, optionalP] = Utils.generateParametersFromSchema(schema, "requestBody", allowMultipleParameters, requestBody.required);
460
+ const [requiredP, optionalP] = Utils.generateParametersFromSchema(schema, "requestBody", !!options.allowMultipleParameters, requestBody.required);
598
461
  requiredParams.push(...requiredP);
599
462
  optionalParams.push(...optionalP);
600
463
  }
601
464
  }
602
465
  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
- }
466
+ const parameters = [...requiredParams, ...optionalParams];
610
467
  const command = {
611
468
  context: ["compose"],
612
469
  type: "query",
@@ -615,32 +472,9 @@ class Utils {
615
472
  parameters: parameters,
616
473
  description: ((_b = operationItem.description) !== null && _b !== void 0 ? _b : "").slice(0, ConstantString.CommandDescriptionMaxLens),
617
474
  };
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
- };
625
- }
626
- return [command, warning];
627
- }
628
- static listSupportedAPIs(spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2) {
629
- const paths = spec.paths;
630
- const result = {};
631
- for (const path in paths) {
632
- const methods = paths[path];
633
- 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;
638
- }
639
- }
640
- }
641
- return result;
475
+ return command;
642
476
  }
643
- static validateSpec(spec, parser, isSwaggerFile, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2) {
477
+ static validateSpec(spec, parser, apiMap, isSwaggerFile, options) {
644
478
  const errors = [];
645
479
  const warnings = [];
646
480
  if (isSwaggerFile) {
@@ -649,8 +483,7 @@ class Utils {
649
483
  content: ConstantString.ConvertSwaggerToOpenAPI,
650
484
  });
651
485
  }
652
- // Server validation
653
- const serverErrors = Utils.validateServer(spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2);
486
+ const serverErrors = Utils.validateServer(spec, options);
654
487
  errors.push(...serverErrors);
655
488
  // Remote reference not supported
656
489
  const refPaths = parser.$refs.paths();
@@ -663,8 +496,8 @@ class Utils {
663
496
  });
664
497
  }
665
498
  // No supported API
666
- const apiMap = Utils.listSupportedAPIs(spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2);
667
- if (Object.keys(apiMap).length === 0) {
499
+ const validAPIs = Object.entries(apiMap).filter(([, value]) => value.isValid);
500
+ if (validAPIs.length === 0) {
668
501
  errors.push({
669
502
  type: ErrorType.NoSupportedApi,
670
503
  content: ConstantString.NoSupportedApi,
@@ -673,8 +506,8 @@ class Utils {
673
506
  // OperationId missing
674
507
  const apisMissingOperationId = [];
675
508
  for (const key in apiMap) {
676
- const pathObjectItem = apiMap[key];
677
- if (!pathObjectItem.operationId) {
509
+ const { operation } = apiMap[key];
510
+ if (!operation.operationId) {
678
511
  apisMissingOperationId.push(key);
679
512
  }
680
513
  }
@@ -715,6 +548,402 @@ class Utils {
715
548
  }
716
549
  return safeRegistrationIdEnvName;
717
550
  }
551
+ static getAllAPICount(spec) {
552
+ let count = 0;
553
+ const paths = spec.paths;
554
+ for (const path in paths) {
555
+ const methods = paths[path];
556
+ for (const method in methods) {
557
+ if (ConstantString.AllOperationMethods.includes(method)) {
558
+ count++;
559
+ }
560
+ }
561
+ }
562
+ return count;
563
+ }
564
+ }
565
+
566
+ // Copyright (c) Microsoft Corporation.
567
+ class Validator {
568
+ validateMethodAndPath(method, path) {
569
+ const result = { isValid: true, reason: [] };
570
+ if (this.options.allowMethods && !this.options.allowMethods.includes(method)) {
571
+ result.isValid = false;
572
+ result.reason.push(ErrorType.MethodNotAllowed);
573
+ return result;
574
+ }
575
+ const pathObj = this.spec.paths[path];
576
+ if (!pathObj || !pathObj[method]) {
577
+ result.isValid = false;
578
+ result.reason.push(ErrorType.UrlPathNotExist);
579
+ return result;
580
+ }
581
+ return result;
582
+ }
583
+ validateResponse(method, path) {
584
+ const result = { isValid: true, reason: [] };
585
+ const operationObject = this.spec.paths[path][method];
586
+ const { json, multipleMediaType } = Utils.getResponseJson(operationObject);
587
+ // only support response body only contains “application/json” content type
588
+ if (multipleMediaType) {
589
+ result.reason.push(ErrorType.ResponseContainMultipleMediaTypes);
590
+ }
591
+ else if (Object.keys(json).length === 0) {
592
+ // response body should not be empty
593
+ result.reason.push(ErrorType.ResponseJsonIsEmpty);
594
+ }
595
+ return result;
596
+ }
597
+ validateServer(method, path) {
598
+ const pathObj = this.spec.paths[path];
599
+ const result = { isValid: true, reason: [] };
600
+ const operationObject = pathObj[method];
601
+ const rootServer = this.spec.servers && this.spec.servers[0];
602
+ const methodServer = this.spec.paths[path].servers && this.spec.paths[path].servers[0];
603
+ const operationServer = operationObject.servers && operationObject.servers[0];
604
+ const serverUrl = operationServer || methodServer || rootServer;
605
+ if (!serverUrl) {
606
+ // should contain server URL
607
+ result.reason.push(ErrorType.NoServerInformation);
608
+ }
609
+ else {
610
+ // server url should be absolute url with https protocol
611
+ const serverValidateResult = Utils.checkServerUrl([serverUrl]);
612
+ result.reason.push(...serverValidateResult.map((item) => item.type));
613
+ }
614
+ return result;
615
+ }
616
+ validateAuth(method, path) {
617
+ const pathObj = this.spec.paths[path];
618
+ const operationObject = pathObj[method];
619
+ const securities = operationObject.security;
620
+ const authSchemeArray = Utils.getAuthArray(securities, this.spec);
621
+ if (authSchemeArray.length === 0) {
622
+ return { isValid: true, reason: [] };
623
+ }
624
+ if (this.options.allowAPIKeyAuth ||
625
+ this.options.allowOauth2 ||
626
+ this.options.allowBearerTokenAuth) {
627
+ // Currently we don't support multiple auth in one operation
628
+ if (authSchemeArray.length > 0 && authSchemeArray.every((auths) => auths.length > 1)) {
629
+ return {
630
+ isValid: false,
631
+ reason: [ErrorType.MultipleAuthNotSupported],
632
+ };
633
+ }
634
+ for (const auths of authSchemeArray) {
635
+ if (auths.length === 1) {
636
+ if ((this.options.allowAPIKeyAuth && Utils.isAPIKeyAuth(auths[0].authScheme)) ||
637
+ (this.options.allowOauth2 && Utils.isOAuthWithAuthCodeFlow(auths[0].authScheme)) ||
638
+ (this.options.allowBearerTokenAuth && Utils.isBearerTokenAuth(auths[0].authScheme))) {
639
+ return { isValid: true, reason: [] };
640
+ }
641
+ }
642
+ }
643
+ }
644
+ return { isValid: false, reason: [ErrorType.AuthTypeIsNotSupported] };
645
+ }
646
+ checkPostBodySchema(schema, isRequired = false) {
647
+ var _a;
648
+ const paramResult = {
649
+ requiredNum: 0,
650
+ optionalNum: 0,
651
+ isValid: true,
652
+ reason: [],
653
+ };
654
+ if (Object.keys(schema).length === 0) {
655
+ return paramResult;
656
+ }
657
+ const isRequiredWithoutDefault = isRequired && schema.default === undefined;
658
+ const isCopilot = this.projectType === ProjectType.Copilot;
659
+ if (isCopilot && this.hasNestedObjectInSchema(schema)) {
660
+ paramResult.isValid = false;
661
+ paramResult.reason = [ErrorType.RequestBodyContainsNestedObject];
662
+ return paramResult;
663
+ }
664
+ if (schema.type === "string" ||
665
+ schema.type === "integer" ||
666
+ schema.type === "boolean" ||
667
+ schema.type === "number") {
668
+ if (isRequiredWithoutDefault) {
669
+ paramResult.requiredNum = paramResult.requiredNum + 1;
670
+ }
671
+ else {
672
+ paramResult.optionalNum = paramResult.optionalNum + 1;
673
+ }
674
+ }
675
+ else if (schema.type === "object") {
676
+ const { properties } = schema;
677
+ for (const property in properties) {
678
+ let isRequired = false;
679
+ if (schema.required && ((_a = schema.required) === null || _a === void 0 ? void 0 : _a.indexOf(property)) >= 0) {
680
+ isRequired = true;
681
+ }
682
+ const result = this.checkPostBodySchema(properties[property], isRequired);
683
+ paramResult.requiredNum += result.requiredNum;
684
+ paramResult.optionalNum += result.optionalNum;
685
+ paramResult.isValid = paramResult.isValid && result.isValid;
686
+ paramResult.reason.push(...result.reason);
687
+ }
688
+ }
689
+ else {
690
+ if (isRequiredWithoutDefault && !isCopilot) {
691
+ paramResult.isValid = false;
692
+ paramResult.reason.push(ErrorType.PostBodyContainsRequiredUnsupportedSchema);
693
+ }
694
+ }
695
+ return paramResult;
696
+ }
697
+ checkParamSchema(paramObject) {
698
+ const paramResult = {
699
+ requiredNum: 0,
700
+ optionalNum: 0,
701
+ isValid: true,
702
+ reason: [],
703
+ };
704
+ if (!paramObject) {
705
+ return paramResult;
706
+ }
707
+ const isCopilot = this.projectType === ProjectType.Copilot;
708
+ for (let i = 0; i < paramObject.length; i++) {
709
+ const param = paramObject[i];
710
+ const schema = param.schema;
711
+ if (isCopilot && this.hasNestedObjectInSchema(schema)) {
712
+ paramResult.isValid = false;
713
+ paramResult.reason.push(ErrorType.ParamsContainsNestedObject);
714
+ continue;
715
+ }
716
+ const isRequiredWithoutDefault = param.required && schema.default === undefined;
717
+ if (isCopilot) {
718
+ if (isRequiredWithoutDefault) {
719
+ paramResult.requiredNum = paramResult.requiredNum + 1;
720
+ }
721
+ else {
722
+ paramResult.optionalNum = paramResult.optionalNum + 1;
723
+ }
724
+ continue;
725
+ }
726
+ if (param.in === "header" || param.in === "cookie") {
727
+ if (isRequiredWithoutDefault) {
728
+ paramResult.isValid = false;
729
+ paramResult.reason.push(ErrorType.ParamsContainRequiredUnsupportedSchema);
730
+ }
731
+ continue;
732
+ }
733
+ if (schema.type !== "boolean" &&
734
+ schema.type !== "string" &&
735
+ schema.type !== "number" &&
736
+ schema.type !== "integer") {
737
+ if (isRequiredWithoutDefault) {
738
+ paramResult.isValid = false;
739
+ paramResult.reason.push(ErrorType.ParamsContainRequiredUnsupportedSchema);
740
+ }
741
+ continue;
742
+ }
743
+ if (param.in === "query" || param.in === "path") {
744
+ if (isRequiredWithoutDefault) {
745
+ paramResult.requiredNum = paramResult.requiredNum + 1;
746
+ }
747
+ else {
748
+ paramResult.optionalNum = paramResult.optionalNum + 1;
749
+ }
750
+ }
751
+ }
752
+ return paramResult;
753
+ }
754
+ hasNestedObjectInSchema(schema) {
755
+ if (schema.type === "object") {
756
+ for (const property in schema.properties) {
757
+ const nestedSchema = schema.properties[property];
758
+ if (nestedSchema.type === "object") {
759
+ return true;
760
+ }
761
+ }
762
+ }
763
+ return false;
764
+ }
765
+ }
766
+
767
+ // Copyright (c) Microsoft Corporation.
768
+ class CopilotValidator extends Validator {
769
+ constructor(spec, options) {
770
+ super();
771
+ this.projectType = ProjectType.Copilot;
772
+ this.options = options;
773
+ this.spec = spec;
774
+ }
775
+ validateAPI(method, path) {
776
+ const result = { isValid: true, reason: [] };
777
+ method = method.toLocaleLowerCase();
778
+ // validate method and path
779
+ const methodAndPathResult = this.validateMethodAndPath(method, path);
780
+ if (!methodAndPathResult.isValid) {
781
+ return methodAndPathResult;
782
+ }
783
+ const operationObject = this.spec.paths[path][method];
784
+ // validate auth
785
+ const authCheckResult = this.validateAuth(method, path);
786
+ result.reason.push(...authCheckResult.reason);
787
+ // validate operationId
788
+ if (!this.options.allowMissingId && !operationObject.operationId) {
789
+ result.reason.push(ErrorType.MissingOperationId);
790
+ }
791
+ // validate server
792
+ const validateServerResult = this.validateServer(method, path);
793
+ result.reason.push(...validateServerResult.reason);
794
+ // validate response
795
+ const validateResponseResult = this.validateResponse(method, path);
796
+ result.reason.push(...validateResponseResult.reason);
797
+ // validate requestBody
798
+ const requestBody = operationObject.requestBody;
799
+ const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
800
+ if (Utils.containMultipleMediaTypes(requestBody)) {
801
+ result.reason.push(ErrorType.PostBodyContainMultipleMediaTypes);
802
+ }
803
+ if (requestJsonBody) {
804
+ const requestBodySchema = requestJsonBody.schema;
805
+ if (requestBodySchema.type !== "object") {
806
+ result.reason.push(ErrorType.PostBodySchemaIsNotJson);
807
+ }
808
+ const requestBodyParamResult = this.checkPostBodySchema(requestBodySchema, requestBody.required);
809
+ result.reason.push(...requestBodyParamResult.reason);
810
+ }
811
+ // validate parameters
812
+ const paramObject = operationObject.parameters;
813
+ const paramResult = this.checkParamSchema(paramObject);
814
+ result.reason.push(...paramResult.reason);
815
+ if (result.reason.length > 0) {
816
+ result.isValid = false;
817
+ }
818
+ return result;
819
+ }
820
+ }
821
+
822
+ // Copyright (c) Microsoft Corporation.
823
+ class SMEValidator extends Validator {
824
+ constructor(spec, options) {
825
+ super();
826
+ this.projectType = ProjectType.SME;
827
+ this.options = options;
828
+ this.spec = spec;
829
+ }
830
+ validateAPI(method, path) {
831
+ const result = { isValid: true, reason: [] };
832
+ method = method.toLocaleLowerCase();
833
+ // validate method and path
834
+ const methodAndPathResult = this.validateMethodAndPath(method, path);
835
+ if (!methodAndPathResult.isValid) {
836
+ return methodAndPathResult;
837
+ }
838
+ const operationObject = this.spec.paths[path][method];
839
+ // validate auth
840
+ const authCheckResult = this.validateAuth(method, path);
841
+ result.reason.push(...authCheckResult.reason);
842
+ // validate operationId
843
+ if (!this.options.allowMissingId && !operationObject.operationId) {
844
+ result.reason.push(ErrorType.MissingOperationId);
845
+ }
846
+ // validate server
847
+ const validateServerResult = this.validateServer(method, path);
848
+ result.reason.push(...validateServerResult.reason);
849
+ // validate response
850
+ const validateResponseResult = this.validateResponse(method, path);
851
+ result.reason.push(...validateResponseResult.reason);
852
+ let postBodyResult = {
853
+ requiredNum: 0,
854
+ optionalNum: 0,
855
+ isValid: true,
856
+ reason: [],
857
+ };
858
+ // validate requestBody
859
+ const requestBody = operationObject.requestBody;
860
+ const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
861
+ if (Utils.containMultipleMediaTypes(requestBody)) {
862
+ result.reason.push(ErrorType.PostBodyContainMultipleMediaTypes);
863
+ }
864
+ if (requestJsonBody) {
865
+ const requestBodySchema = requestJsonBody.schema;
866
+ postBodyResult = this.checkPostBodySchema(requestBodySchema, requestBody.required);
867
+ result.reason.push(...postBodyResult.reason);
868
+ }
869
+ // validate parameters
870
+ const paramObject = operationObject.parameters;
871
+ const paramResult = this.checkParamSchema(paramObject);
872
+ result.reason.push(...paramResult.reason);
873
+ // validate total parameters count
874
+ if (paramResult.isValid && postBodyResult.isValid) {
875
+ const paramCountResult = this.validateParamCount(postBodyResult, paramResult);
876
+ result.reason.push(...paramCountResult.reason);
877
+ }
878
+ if (result.reason.length > 0) {
879
+ result.isValid = false;
880
+ }
881
+ return result;
882
+ }
883
+ validateParamCount(postBodyResult, paramResult) {
884
+ const result = { isValid: true, reason: [] };
885
+ const totalRequiredParams = postBodyResult.requiredNum + paramResult.requiredNum;
886
+ const totalParams = totalRequiredParams + postBodyResult.optionalNum + paramResult.optionalNum;
887
+ if (totalRequiredParams > 1) {
888
+ if (!this.options.allowMultipleParameters ||
889
+ totalRequiredParams > SMEValidator.SMERequiredParamsMaxNum) {
890
+ result.reason.push(ErrorType.ExceededRequiredParamsLimit);
891
+ }
892
+ }
893
+ else if (totalParams === 0) {
894
+ result.reason.push(ErrorType.NoParameter);
895
+ }
896
+ return result;
897
+ }
898
+ }
899
+ SMEValidator.SMERequiredParamsMaxNum = 5;
900
+
901
+ // Copyright (c) Microsoft Corporation.
902
+ class TeamsAIValidator extends Validator {
903
+ constructor(spec, options) {
904
+ super();
905
+ this.projectType = ProjectType.TeamsAi;
906
+ this.options = options;
907
+ this.spec = spec;
908
+ }
909
+ validateAPI(method, path) {
910
+ const result = { isValid: true, reason: [] };
911
+ method = method.toLocaleLowerCase();
912
+ // validate method and path
913
+ const methodAndPathResult = this.validateMethodAndPath(method, path);
914
+ if (!methodAndPathResult.isValid) {
915
+ return methodAndPathResult;
916
+ }
917
+ const operationObject = this.spec.paths[path][method];
918
+ // validate operationId
919
+ if (!this.options.allowMissingId && !operationObject.operationId) {
920
+ result.reason.push(ErrorType.MissingOperationId);
921
+ }
922
+ // validate server
923
+ const validateServerResult = this.validateServer(method, path);
924
+ result.reason.push(...validateServerResult.reason);
925
+ if (result.reason.length > 0) {
926
+ result.isValid = false;
927
+ }
928
+ return result;
929
+ }
930
+ }
931
+
932
+ class ValidatorFactory {
933
+ static create(spec, options) {
934
+ var _a;
935
+ const type = (_a = options.projectType) !== null && _a !== void 0 ? _a : ProjectType.SME;
936
+ switch (type) {
937
+ case ProjectType.SME:
938
+ return new SMEValidator(spec, options);
939
+ case ProjectType.Copilot:
940
+ return new CopilotValidator(spec, options);
941
+ case ProjectType.TeamsAi:
942
+ return new TeamsAIValidator(spec, options);
943
+ default:
944
+ throw new Error(`Invalid project type: ${type}`);
945
+ }
946
+ }
718
947
  }
719
948
 
720
949
  // Copyright (c) Microsoft Corporation.
@@ -733,7 +962,10 @@ class SpecParser {
733
962
  allowSwagger: false,
734
963
  allowAPIKeyAuth: false,
735
964
  allowMultipleParameters: false,
965
+ allowBearerTokenAuth: false,
736
966
  allowOauth2: false,
967
+ allowMethods: ["get", "post"],
968
+ projectType: ProjectType.SME,
737
969
  };
738
970
  this.pathOrSpec = pathOrDoc;
739
971
  this.parser = new SwaggerParser();
@@ -770,7 +1002,8 @@ class SpecParser {
770
1002
  ],
771
1003
  };
772
1004
  }
773
- return Utils.validateSpec(this.spec, this.parser, !!this.isSwaggerFile, this.options.allowMissingId, this.options.allowAPIKeyAuth, this.options.allowMultipleParameters, this.options.allowOauth2);
1005
+ const apiMap = this.getAPIs(this.spec);
1006
+ return Utils.validateSpec(this.spec, this.parser, apiMap, !!this.isSwaggerFile, this.options);
774
1007
  }
775
1008
  catch (err) {
776
1009
  throw new SpecParserError(err.toString(), ErrorType.ValidateFailed);
@@ -779,17 +1012,20 @@ class SpecParser {
779
1012
  async listSupportedAPIInfo() {
780
1013
  try {
781
1014
  await this.loadSpec();
782
- const apiMap = this.getAllSupportedAPIs(this.spec);
1015
+ const apiMap = this.getAPIs(this.spec);
783
1016
  const apiInfos = [];
784
1017
  for (const key in apiMap) {
785
- const pathObjectItem = apiMap[key];
1018
+ const { operation, isValid } = apiMap[key];
1019
+ if (!isValid) {
1020
+ continue;
1021
+ }
786
1022
  const [method, path] = key.split(" ");
787
- const operationId = pathObjectItem.operationId;
1023
+ const operationId = operation.operationId;
788
1024
  // In Browser environment, this api is by default not support api without operationId
789
1025
  if (!operationId) {
790
1026
  continue;
791
1027
  }
792
- const [command, warning] = Utils.parseApiInfo(pathObjectItem, this.options.allowMultipleParameters);
1028
+ const command = Utils.parseApiInfo(operation, this.options);
793
1029
  const apiInfo = {
794
1030
  method: method,
795
1031
  path: path,
@@ -798,9 +1034,6 @@ class SpecParser {
798
1034
  parameters: command.parameters,
799
1035
  description: command.description,
800
1036
  };
801
- if (warning) {
802
- apiInfo.warning = warning;
803
- }
804
1037
  apiInfos.push(apiInfo);
805
1038
  }
806
1039
  return apiInfos;
@@ -818,12 +1051,32 @@ class SpecParser {
818
1051
  async list() {
819
1052
  throw new Error("Method not implemented.");
820
1053
  }
1054
+ /**
1055
+ * Generate specs according to the filters.
1056
+ * @param filter An array of strings that represent the filters to apply when generating the artifacts. If filter is empty, it would process nothing.
1057
+ */
1058
+ // eslint-disable-next-line @typescript-eslint/require-await
1059
+ async getFilteredSpecs(filter, signal) {
1060
+ throw new Error("Method not implemented.");
1061
+ }
1062
+ /**
1063
+ * Generates and update artifacts from the OpenAPI specification file. Generate Adaptive Cards, update Teams app manifest, and generate a new OpenAPI specification file.
1064
+ * @param manifestPath A file path of the Teams app manifest file to update.
1065
+ * @param filter An array of strings that represent the filters to apply when generating the artifacts. If filter is empty, it would process nothing.
1066
+ * @param outputSpecPath File path of the new OpenAPI specification file to generate. If not specified or empty, no spec file will be generated.
1067
+ * @param pluginFilePath File path of the api plugin file to generate.
1068
+ */
1069
+ // eslint-disable-next-line @typescript-eslint/require-await
1070
+ async generateForCopilot(manifestPath, filter, outputSpecPath, pluginFilePath, signal) {
1071
+ throw new Error("Method not implemented.");
1072
+ }
821
1073
  /**
822
1074
  * Generates and update artifacts from the OpenAPI specification file. Generate Adaptive Cards, update Teams app manifest, and generate a new OpenAPI specification file.
823
1075
  * @param manifestPath A file path of the Teams app manifest file to update.
824
1076
  * @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
1077
  * @param outputSpecPath File path of the new OpenAPI specification file to generate. If not specified or empty, no spec file will be generated.
826
1078
  * @param adaptiveCardFolder Folder path where the Adaptive Card files will be generated. If not specified or empty, Adaptive Card files will not be generated.
1079
+ * @param isMe Boolean that indicates whether the project is an Messaging Extension. For Messaging Extension, composeExtensions will be added in Teams app manifest.
827
1080
  */
828
1081
  // eslint-disable-next-line @typescript-eslint/require-await
829
1082
  async generate(manifestPath, filter, outputSpecPath, adaptiveCardFolder, signal) {
@@ -839,15 +1092,194 @@ class SpecParser {
839
1092
  this.spec = (await this.parser.dereference(clonedUnResolveSpec));
840
1093
  }
841
1094
  }
842
- getAllSupportedAPIs(spec) {
1095
+ getAPIs(spec) {
843
1096
  if (this.apiMap !== undefined) {
844
1097
  return this.apiMap;
845
1098
  }
846
- const result = Utils.listSupportedAPIs(spec, this.options.allowMissingId, this.options.allowAPIKeyAuth, this.options.allowMultipleParameters, this.options.allowOauth2);
1099
+ const result = this.listAPIs(spec);
847
1100
  this.apiMap = result;
848
1101
  return result;
849
1102
  }
1103
+ listAPIs(spec) {
1104
+ var _a;
1105
+ const paths = spec.paths;
1106
+ const result = {};
1107
+ for (const path in paths) {
1108
+ const methods = paths[path];
1109
+ for (const method in methods) {
1110
+ const operationObject = methods[method];
1111
+ if (((_a = this.options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && operationObject) {
1112
+ const validator = ValidatorFactory.create(spec, this.options);
1113
+ const validateResult = validator.validateAPI(method, path);
1114
+ result[`${method.toUpperCase()} ${path}`] = {
1115
+ operation: operationObject,
1116
+ isValid: validateResult.isValid,
1117
+ reason: validateResult.reason,
1118
+ };
1119
+ }
1120
+ }
1121
+ }
1122
+ return result;
1123
+ }
1124
+ }
1125
+
1126
+ // Copyright (c) Microsoft Corporation.
1127
+ class AdaptiveCardGenerator {
1128
+ static generateAdaptiveCard(operationItem) {
1129
+ try {
1130
+ const { json } = Utils.getResponseJson(operationItem);
1131
+ let cardBody = [];
1132
+ let schema = json.schema;
1133
+ let jsonPath = "$";
1134
+ if (schema && Object.keys(schema).length > 0) {
1135
+ jsonPath = AdaptiveCardGenerator.getResponseJsonPathFromSchema(schema);
1136
+ if (jsonPath !== "$") {
1137
+ schema = schema.properties[jsonPath];
1138
+ }
1139
+ cardBody = AdaptiveCardGenerator.generateCardFromResponse(schema, "");
1140
+ }
1141
+ // if no schema, try to use example value
1142
+ if (cardBody.length === 0 && (json.examples || json.example)) {
1143
+ cardBody = [
1144
+ {
1145
+ type: ConstantString.TextBlockType,
1146
+ text: "${jsonStringify($root)}",
1147
+ wrap: true,
1148
+ },
1149
+ ];
1150
+ }
1151
+ // if no example value, use default success response
1152
+ if (cardBody.length === 0) {
1153
+ cardBody = [
1154
+ {
1155
+ type: ConstantString.TextBlockType,
1156
+ text: "success",
1157
+ wrap: true,
1158
+ },
1159
+ ];
1160
+ }
1161
+ const fullCard = {
1162
+ type: ConstantString.AdaptiveCardType,
1163
+ $schema: ConstantString.AdaptiveCardSchema,
1164
+ version: ConstantString.AdaptiveCardVersion,
1165
+ body: cardBody,
1166
+ };
1167
+ return [fullCard, jsonPath];
1168
+ }
1169
+ catch (err) {
1170
+ throw new SpecParserError(err.toString(), ErrorType.GenerateAdaptiveCardFailed);
1171
+ }
1172
+ }
1173
+ static generateCardFromResponse(schema, name, parentArrayName = "") {
1174
+ if (schema.type === "array") {
1175
+ // schema.items can be arbitrary object: schema { type: array, items: {} }
1176
+ if (Object.keys(schema.items).length === 0) {
1177
+ return [
1178
+ {
1179
+ type: ConstantString.TextBlockType,
1180
+ text: name ? `${name}: \${jsonStringify(${name})}` : "result: ${jsonStringify($root)}",
1181
+ wrap: true,
1182
+ },
1183
+ ];
1184
+ }
1185
+ const obj = AdaptiveCardGenerator.generateCardFromResponse(schema.items, "", name);
1186
+ const template = {
1187
+ type: ConstantString.ContainerType,
1188
+ $data: name ? `\${${name}}` : "${$root}",
1189
+ items: Array(),
1190
+ };
1191
+ template.items.push(...obj);
1192
+ return [template];
1193
+ }
1194
+ // some schema may not contain type but contain properties
1195
+ if (schema.type === "object" || (!schema.type && schema.properties)) {
1196
+ const { properties } = schema;
1197
+ const result = [];
1198
+ for (const property in properties) {
1199
+ const obj = AdaptiveCardGenerator.generateCardFromResponse(properties[property], name ? `${name}.${property}` : property, parentArrayName);
1200
+ result.push(...obj);
1201
+ }
1202
+ if (schema.additionalProperties) {
1203
+ // TODO: better ways to handler warnings.
1204
+ console.warn(ConstantString.AdditionalPropertiesNotSupported);
1205
+ }
1206
+ return result;
1207
+ }
1208
+ if (schema.type === "string" ||
1209
+ schema.type === "integer" ||
1210
+ schema.type === "boolean" ||
1211
+ schema.type === "number") {
1212
+ if (!AdaptiveCardGenerator.isImageUrlProperty(schema, name, parentArrayName)) {
1213
+ // string in root: "ddd"
1214
+ let text = "result: ${$root}";
1215
+ if (name) {
1216
+ // object { id: "1" }
1217
+ text = `${name}: \${if(${name}, ${name}, 'N/A')}`;
1218
+ if (parentArrayName) {
1219
+ // object types inside array: { tags: ["id": 1, "name": "name"] }
1220
+ text = `${parentArrayName}.${text}`;
1221
+ }
1222
+ }
1223
+ else if (parentArrayName) {
1224
+ // string array: photoUrls: ["1", "2"]
1225
+ text = `${parentArrayName}: ` + "${$data}";
1226
+ }
1227
+ return [
1228
+ {
1229
+ type: ConstantString.TextBlockType,
1230
+ text,
1231
+ wrap: true,
1232
+ },
1233
+ ];
1234
+ }
1235
+ else {
1236
+ if (name) {
1237
+ return [
1238
+ {
1239
+ type: "Image",
1240
+ url: `\${${name}}`,
1241
+ $when: `\${${name} != null}`,
1242
+ },
1243
+ ];
1244
+ }
1245
+ else {
1246
+ return [
1247
+ {
1248
+ type: "Image",
1249
+ url: "${$data}",
1250
+ $when: "${$data != null}",
1251
+ },
1252
+ ];
1253
+ }
1254
+ }
1255
+ }
1256
+ if (schema.oneOf || schema.anyOf || schema.not || schema.allOf) {
1257
+ throw new Error(Utils.format(ConstantString.SchemaNotSupported, JSON.stringify(schema)));
1258
+ }
1259
+ throw new Error(Utils.format(ConstantString.UnknownSchema, JSON.stringify(schema)));
1260
+ }
1261
+ // Find the first array property in the response schema object with the well-known name
1262
+ static getResponseJsonPathFromSchema(schema) {
1263
+ if (schema.type === "object" || (!schema.type && schema.properties)) {
1264
+ const { properties } = schema;
1265
+ for (const property in properties) {
1266
+ const schema = properties[property];
1267
+ if (schema.type === "array" &&
1268
+ Utils.isWellKnownName(property, ConstantString.WellknownResultNames)) {
1269
+ return property;
1270
+ }
1271
+ }
1272
+ }
1273
+ return "$";
1274
+ }
1275
+ static isImageUrlProperty(schema, name, parentArrayName) {
1276
+ const propertyName = name ? name : parentArrayName;
1277
+ return (!!propertyName &&
1278
+ schema.type === "string" &&
1279
+ Utils.isWellKnownName(propertyName, ConstantString.WellknownImageName) &&
1280
+ (propertyName.toLocaleLowerCase().indexOf("url") >= 0 || schema.format === "uri"));
1281
+ }
850
1282
  }
851
1283
 
852
- export { ConstantString, ErrorType, SpecParser, SpecParserError, Utils, ValidationStatus, WarningType };
1284
+ export { AdaptiveCardGenerator, ConstantString, ErrorType, SpecParser, SpecParserError, Utils, ValidationStatus, WarningType };
853
1285
  //# sourceMappingURL=index.esm2017.js.map