@microsoft/m365-spec-parser 0.1.1-alpha.7fe3da414.0 → 0.1.1-alpha.87f45d762.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.
@@ -208,7 +208,8 @@ ConstantString.CommandDescriptionMaxLens = 128;
208
208
  ConstantString.ParameterDescriptionMaxLens = 128;
209
209
  ConstantString.CommandTitleMaxLens = 32;
210
210
  ConstantString.ParameterTitleMaxLens = 32;
211
- ConstantString.SMERequiredParamsMaxNum = 5;
211
+ ConstantString.SMERequiredParamsMaxNum = 5;
212
+ ConstantString.DefaultPluginId = "plugin_1";
212
213
 
213
214
  // Copyright (c) Microsoft Corporation.
214
215
  class SpecParserError extends Error {
@@ -231,249 +232,9 @@ class Utils {
231
232
  }
232
233
  return false;
233
234
  }
234
- static checkParameters(paramObject, isCopilot) {
235
- const paramResult = {
236
- requiredNum: 0,
237
- optionalNum: 0,
238
- isValid: true,
239
- reason: [],
240
- };
241
- if (!paramObject) {
242
- return paramResult;
243
- }
244
- for (let i = 0; i < paramObject.length; i++) {
245
- const param = paramObject[i];
246
- const schema = param.schema;
247
- if (isCopilot && this.hasNestedObjectInSchema(schema)) {
248
- paramResult.isValid = false;
249
- paramResult.reason.push(exports.ErrorType.ParamsContainsNestedObject);
250
- continue;
251
- }
252
- const isRequiredWithoutDefault = param.required && schema.default === undefined;
253
- if (isCopilot) {
254
- if (isRequiredWithoutDefault) {
255
- paramResult.requiredNum = paramResult.requiredNum + 1;
256
- }
257
- else {
258
- paramResult.optionalNum = paramResult.optionalNum + 1;
259
- }
260
- continue;
261
- }
262
- if (param.in === "header" || param.in === "cookie") {
263
- if (isRequiredWithoutDefault) {
264
- paramResult.isValid = false;
265
- paramResult.reason.push(exports.ErrorType.ParamsContainRequiredUnsupportedSchema);
266
- }
267
- continue;
268
- }
269
- if (schema.type !== "boolean" &&
270
- schema.type !== "string" &&
271
- schema.type !== "number" &&
272
- schema.type !== "integer") {
273
- if (isRequiredWithoutDefault) {
274
- paramResult.isValid = false;
275
- paramResult.reason.push(exports.ErrorType.ParamsContainRequiredUnsupportedSchema);
276
- }
277
- continue;
278
- }
279
- if (param.in === "query" || param.in === "path") {
280
- if (isRequiredWithoutDefault) {
281
- paramResult.requiredNum = paramResult.requiredNum + 1;
282
- }
283
- else {
284
- paramResult.optionalNum = paramResult.optionalNum + 1;
285
- }
286
- }
287
- }
288
- return paramResult;
289
- }
290
- static checkPostBody(schema, isRequired = false, isCopilot = false) {
291
- var _a;
292
- const paramResult = {
293
- requiredNum: 0,
294
- optionalNum: 0,
295
- isValid: true,
296
- reason: [],
297
- };
298
- if (Object.keys(schema).length === 0) {
299
- return paramResult;
300
- }
301
- const isRequiredWithoutDefault = isRequired && schema.default === undefined;
302
- if (isCopilot && this.hasNestedObjectInSchema(schema)) {
303
- paramResult.isValid = false;
304
- paramResult.reason = [exports.ErrorType.RequestBodyContainsNestedObject];
305
- return paramResult;
306
- }
307
- if (schema.type === "string" ||
308
- schema.type === "integer" ||
309
- schema.type === "boolean" ||
310
- schema.type === "number") {
311
- if (isRequiredWithoutDefault) {
312
- paramResult.requiredNum = paramResult.requiredNum + 1;
313
- }
314
- else {
315
- paramResult.optionalNum = paramResult.optionalNum + 1;
316
- }
317
- }
318
- else if (schema.type === "object") {
319
- const { properties } = schema;
320
- for (const property in properties) {
321
- let isRequired = false;
322
- if (schema.required && ((_a = schema.required) === null || _a === void 0 ? void 0 : _a.indexOf(property)) >= 0) {
323
- isRequired = true;
324
- }
325
- const result = Utils.checkPostBody(properties[property], isRequired, isCopilot);
326
- paramResult.requiredNum += result.requiredNum;
327
- paramResult.optionalNum += result.optionalNum;
328
- paramResult.isValid = paramResult.isValid && result.isValid;
329
- paramResult.reason.push(...result.reason);
330
- }
331
- }
332
- else {
333
- if (isRequiredWithoutDefault && !isCopilot) {
334
- paramResult.isValid = false;
335
- paramResult.reason.push(exports.ErrorType.PostBodyContainsRequiredUnsupportedSchema);
336
- }
337
- }
338
- return paramResult;
339
- }
340
235
  static containMultipleMediaTypes(bodyObject) {
341
236
  return Object.keys((bodyObject === null || bodyObject === void 0 ? void 0 : bodyObject.content) || {}).length > 1;
342
237
  }
343
- /**
344
- * Checks if the given API is supported.
345
- * @param {string} method - The HTTP method of the API.
346
- * @param {string} path - The path of the API.
347
- * @param {OpenAPIV3.Document} spec - The OpenAPI specification document.
348
- * @returns {boolean} - Returns true if the API is supported, false otherwise.
349
- * @description The following APIs are supported:
350
- * 1. only support Get/Post operation without auth property
351
- * 2. parameter inside query or path only support string, number, boolean and integer
352
- * 3. parameter inside post body only support string, number, boolean, integer and object
353
- * 4. request body + required parameters <= 1
354
- * 5. response body should be “application/json” and not empty, and response code should be 20X
355
- * 6. only support request body with “application/json” content type
356
- */
357
- static isSupportedApi(method, path, spec, options) {
358
- var _a;
359
- const result = { isValid: true, reason: [] };
360
- method = method.toLocaleLowerCase();
361
- if (options.allowMethods && !options.allowMethods.includes(method)) {
362
- result.isValid = false;
363
- result.reason.push(exports.ErrorType.MethodNotAllowed);
364
- return result;
365
- }
366
- const pathObj = spec.paths[path];
367
- if (!pathObj || !pathObj[method]) {
368
- result.isValid = false;
369
- result.reason.push(exports.ErrorType.UrlPathNotExist);
370
- return result;
371
- }
372
- const securities = pathObj[method].security;
373
- const isTeamsAi = options.projectType === exports.ProjectType.TeamsAi;
374
- const isCopilot = options.projectType === exports.ProjectType.Copilot;
375
- // Teams AI project doesn't care about auth, it will use authProvider for user to implement
376
- if (!isTeamsAi) {
377
- const authArray = Utils.getAuthArray(securities, spec);
378
- const authCheckResult = Utils.isSupportedAuth(authArray, options);
379
- if (!authCheckResult.isValid) {
380
- result.reason.push(...authCheckResult.reason);
381
- }
382
- }
383
- const operationObject = pathObj[method];
384
- if (!options.allowMissingId && !operationObject.operationId) {
385
- result.reason.push(exports.ErrorType.MissingOperationId);
386
- }
387
- const rootServer = spec.servers && spec.servers[0];
388
- const methodServer = spec.paths[path].servers && ((_a = spec.paths[path]) === null || _a === void 0 ? void 0 : _a.servers[0]);
389
- const operationServer = operationObject.servers && operationObject.servers[0];
390
- const serverUrl = operationServer || methodServer || rootServer;
391
- if (!serverUrl) {
392
- result.reason.push(exports.ErrorType.NoServerInformation);
393
- }
394
- else {
395
- const serverValidateResult = Utils.checkServerUrl([serverUrl]);
396
- result.reason.push(...serverValidateResult.map((item) => item.type));
397
- }
398
- const paramObject = operationObject.parameters;
399
- const requestBody = operationObject.requestBody;
400
- const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
401
- if (!isTeamsAi && Utils.containMultipleMediaTypes(requestBody)) {
402
- result.reason.push(exports.ErrorType.PostBodyContainMultipleMediaTypes);
403
- }
404
- const { json, multipleMediaType } = Utils.getResponseJson(operationObject, isTeamsAi);
405
- if (multipleMediaType && !isTeamsAi) {
406
- result.reason.push(exports.ErrorType.ResponseContainMultipleMediaTypes);
407
- }
408
- else if (Object.keys(json).length === 0) {
409
- result.reason.push(exports.ErrorType.ResponseJsonIsEmpty);
410
- }
411
- // Teams AI project doesn't care about request parameters/body
412
- if (!isTeamsAi) {
413
- let requestBodyParamResult = {
414
- requiredNum: 0,
415
- optionalNum: 0,
416
- isValid: true,
417
- reason: [],
418
- };
419
- if (requestJsonBody) {
420
- const requestBodySchema = requestJsonBody.schema;
421
- if (isCopilot && requestBodySchema.type !== "object") {
422
- result.reason.push(exports.ErrorType.PostBodySchemaIsNotJson);
423
- }
424
- requestBodyParamResult = Utils.checkPostBody(requestBodySchema, requestBody.required, isCopilot);
425
- if (!requestBodyParamResult.isValid && requestBodyParamResult.reason) {
426
- result.reason.push(...requestBodyParamResult.reason);
427
- }
428
- }
429
- const paramResult = Utils.checkParameters(paramObject, isCopilot);
430
- if (!paramResult.isValid && paramResult.reason) {
431
- result.reason.push(...paramResult.reason);
432
- }
433
- // Copilot support arbitrary parameters
434
- if (!isCopilot && paramResult.isValid && requestBodyParamResult.isValid) {
435
- const totalRequiredParams = requestBodyParamResult.requiredNum + paramResult.requiredNum;
436
- const totalParams = totalRequiredParams + requestBodyParamResult.optionalNum + paramResult.optionalNum;
437
- if (totalRequiredParams > 1) {
438
- if (!options.allowMultipleParameters ||
439
- totalRequiredParams > ConstantString.SMERequiredParamsMaxNum) {
440
- result.reason.push(exports.ErrorType.ExceededRequiredParamsLimit);
441
- }
442
- }
443
- else if (totalParams === 0) {
444
- result.reason.push(exports.ErrorType.NoParameter);
445
- }
446
- }
447
- }
448
- if (result.reason.length > 0) {
449
- result.isValid = false;
450
- }
451
- return result;
452
- }
453
- static isSupportedAuth(authSchemeArray, options) {
454
- if (authSchemeArray.length === 0) {
455
- return { isValid: true, reason: [] };
456
- }
457
- if (options.allowAPIKeyAuth || options.allowOauth2 || options.allowBearerTokenAuth) {
458
- // Currently we don't support multiple auth in one operation
459
- if (authSchemeArray.length > 0 && authSchemeArray.every((auths) => auths.length > 1)) {
460
- return {
461
- isValid: false,
462
- reason: [exports.ErrorType.MultipleAuthNotSupported],
463
- };
464
- }
465
- for (const auths of authSchemeArray) {
466
- if (auths.length === 1) {
467
- if ((options.allowAPIKeyAuth && Utils.isAPIKeyAuth(auths[0].authScheme)) ||
468
- (options.allowOauth2 && Utils.isOAuthWithAuthCodeFlow(auths[0].authScheme)) ||
469
- (options.allowBearerTokenAuth && Utils.isBearerTokenAuth(auths[0].authScheme))) {
470
- return { isValid: true, reason: [] };
471
- }
472
- }
473
- }
474
- }
475
- return { isValid: false, reason: [exports.ErrorType.AuthTypeIsNotSupported] };
476
- }
477
238
  static isBearerTokenAuth(authScheme) {
478
239
  return authScheme.type === "http" && authScheme.scheme === "bearer";
479
240
  }
@@ -481,10 +242,9 @@ class Utils {
481
242
  return authScheme.type === "apiKey";
482
243
  }
483
244
  static isOAuthWithAuthCodeFlow(authScheme) {
484
- if (authScheme.type === "oauth2" && authScheme.flows && authScheme.flows.authorizationCode) {
485
- return true;
486
- }
487
- return false;
245
+ return !!(authScheme.type === "oauth2" &&
246
+ authScheme.flows &&
247
+ authScheme.flows.authorizationCode);
488
248
  }
489
249
  static getAuthArray(securities, spec) {
490
250
  var _a;
@@ -512,7 +272,7 @@ class Utils {
512
272
  static updateFirstLetter(str) {
513
273
  return str.charAt(0).toUpperCase() + str.slice(1);
514
274
  }
515
- static getResponseJson(operationObject, isTeamsAiProject = false) {
275
+ static getResponseJson(operationObject) {
516
276
  var _a, _b;
517
277
  let json = {};
518
278
  let multipleMediaType = false;
@@ -523,9 +283,6 @@ class Utils {
523
283
  json = responseObject.content["application/json"];
524
284
  if (Utils.containMultipleMediaTypes(responseObject)) {
525
285
  multipleMediaType = true;
526
- if (isTeamsAiProject) {
527
- break;
528
- }
529
286
  json = {};
530
287
  }
531
288
  else {
@@ -675,216 +432,622 @@ class Utils {
675
432
  else {
676
433
  optionalParams.push(parameter);
677
434
  }
678
- }
679
- else if (schema.type === "object") {
680
- const { properties } = schema;
681
- for (const property in properties) {
682
- let isRequired = false;
683
- if (schema.required && ((_b = schema.required) === null || _b === void 0 ? void 0 : _b.indexOf(property)) >= 0) {
684
- isRequired = true;
435
+ }
436
+ else if (schema.type === "object") {
437
+ const { properties } = schema;
438
+ for (const property in properties) {
439
+ let isRequired = false;
440
+ if (schema.required && ((_b = schema.required) === null || _b === void 0 ? void 0 : _b.indexOf(property)) >= 0) {
441
+ isRequired = true;
442
+ }
443
+ const [requiredP, optionalP] = Utils.generateParametersFromSchema(properties[property], property, allowMultipleParameters, isRequired);
444
+ requiredParams.push(...requiredP);
445
+ optionalParams.push(...optionalP);
446
+ }
447
+ }
448
+ return [requiredParams, optionalParams];
449
+ }
450
+ static updateParameterWithInputType(schema, param) {
451
+ if (schema.enum) {
452
+ param.inputType = "choiceset";
453
+ param.choices = [];
454
+ for (let i = 0; i < schema.enum.length; i++) {
455
+ param.choices.push({
456
+ title: schema.enum[i],
457
+ value: schema.enum[i],
458
+ });
459
+ }
460
+ }
461
+ else if (schema.type === "string") {
462
+ param.inputType = "text";
463
+ }
464
+ else if (schema.type === "integer" || schema.type === "number") {
465
+ param.inputType = "number";
466
+ }
467
+ else if (schema.type === "boolean") {
468
+ param.inputType = "toggle";
469
+ }
470
+ if (schema.default) {
471
+ param.value = schema.default;
472
+ }
473
+ }
474
+ static parseApiInfo(operationItem, options) {
475
+ var _a, _b;
476
+ const requiredParams = [];
477
+ const optionalParams = [];
478
+ const paramObject = operationItem.parameters;
479
+ if (paramObject) {
480
+ paramObject.forEach((param) => {
481
+ var _a;
482
+ const parameter = {
483
+ name: param.name,
484
+ title: Utils.updateFirstLetter(param.name).slice(0, ConstantString.ParameterTitleMaxLens),
485
+ description: ((_a = param.description) !== null && _a !== void 0 ? _a : "").slice(0, ConstantString.ParameterDescriptionMaxLens),
486
+ };
487
+ const schema = param.schema;
488
+ if (options.allowMultipleParameters && schema) {
489
+ Utils.updateParameterWithInputType(schema, parameter);
490
+ }
491
+ if (param.in !== "header" && param.in !== "cookie") {
492
+ if (param.required && (schema === null || schema === void 0 ? void 0 : schema.default) === undefined) {
493
+ parameter.isRequired = true;
494
+ requiredParams.push(parameter);
495
+ }
496
+ else {
497
+ optionalParams.push(parameter);
498
+ }
499
+ }
500
+ });
501
+ }
502
+ if (operationItem.requestBody) {
503
+ const requestBody = operationItem.requestBody;
504
+ const requestJson = requestBody.content["application/json"];
505
+ if (Object.keys(requestJson).length !== 0) {
506
+ const schema = requestJson.schema;
507
+ const [requiredP, optionalP] = Utils.generateParametersFromSchema(schema, "requestBody", !!options.allowMultipleParameters, requestBody.required);
508
+ requiredParams.push(...requiredP);
509
+ optionalParams.push(...optionalP);
510
+ }
511
+ }
512
+ const operationId = operationItem.operationId;
513
+ const parameters = [...requiredParams, ...optionalParams];
514
+ const command = {
515
+ context: ["compose"],
516
+ type: "query",
517
+ title: ((_a = operationItem.summary) !== null && _a !== void 0 ? _a : "").slice(0, ConstantString.CommandTitleMaxLens),
518
+ id: operationId,
519
+ parameters: parameters,
520
+ description: ((_b = operationItem.description) !== null && _b !== void 0 ? _b : "").slice(0, ConstantString.CommandDescriptionMaxLens),
521
+ };
522
+ return command;
523
+ }
524
+ static format(str, ...args) {
525
+ let index = 0;
526
+ return str.replace(/%s/g, () => {
527
+ const arg = args[index++];
528
+ return arg !== undefined ? arg : "";
529
+ });
530
+ }
531
+ static getSafeRegistrationIdEnvName(authName) {
532
+ if (!authName) {
533
+ return "";
534
+ }
535
+ let safeRegistrationIdEnvName = authName.toUpperCase().replace(/[^A-Z0-9_]/g, "_");
536
+ if (!safeRegistrationIdEnvName.match(/^[A-Z]/)) {
537
+ safeRegistrationIdEnvName = "PREFIX_" + safeRegistrationIdEnvName;
538
+ }
539
+ return safeRegistrationIdEnvName;
540
+ }
541
+ static getServerObject(spec, method, path) {
542
+ const pathObj = spec.paths[path];
543
+ const operationObject = pathObj[method];
544
+ const rootServer = spec.servers && spec.servers[0];
545
+ const methodServer = spec.paths[path].servers && spec.paths[path].servers[0];
546
+ const operationServer = operationObject.servers && operationObject.servers[0];
547
+ const serverUrl = operationServer || methodServer || rootServer;
548
+ return serverUrl;
549
+ }
550
+ }
551
+
552
+ // Copyright (c) Microsoft Corporation.
553
+ class Validator {
554
+ listAPIs() {
555
+ var _a;
556
+ if (this.apiMap) {
557
+ return this.apiMap;
558
+ }
559
+ const paths = this.spec.paths;
560
+ const result = {};
561
+ for (const path in paths) {
562
+ const methods = paths[path];
563
+ for (const method in methods) {
564
+ const operationObject = methods[method];
565
+ if (((_a = this.options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && operationObject) {
566
+ const validateResult = this.validateAPI(method, path);
567
+ result[`${method.toUpperCase()} ${path}`] = {
568
+ operation: operationObject,
569
+ isValid: validateResult.isValid,
570
+ reason: validateResult.reason,
571
+ };
572
+ }
573
+ }
574
+ }
575
+ this.apiMap = result;
576
+ return result;
577
+ }
578
+ validateSpecVersion() {
579
+ const result = { errors: [], warnings: [] };
580
+ if (this.spec.openapi >= "3.1.0") {
581
+ result.errors.push({
582
+ type: exports.ErrorType.SpecVersionNotSupported,
583
+ content: Utils.format(ConstantString.SpecVersionNotSupported, this.spec.openapi),
584
+ data: this.spec.openapi,
585
+ });
586
+ }
587
+ return result;
588
+ }
589
+ validateSpecServer() {
590
+ const result = { errors: [], warnings: [] };
591
+ const serverErrors = Utils.validateServer(this.spec, this.options);
592
+ result.errors.push(...serverErrors);
593
+ return result;
594
+ }
595
+ validateSpecNoSupportAPI() {
596
+ const result = { errors: [], warnings: [] };
597
+ const apiMap = this.listAPIs();
598
+ const validAPIs = Object.entries(apiMap).filter(([, value]) => value.isValid);
599
+ if (validAPIs.length === 0) {
600
+ const data = [];
601
+ for (const key in apiMap) {
602
+ const { reason } = apiMap[key];
603
+ const apiInvalidReason = { api: key, reason: reason };
604
+ data.push(apiInvalidReason);
605
+ }
606
+ result.errors.push({
607
+ type: exports.ErrorType.NoSupportedApi,
608
+ content: ConstantString.NoSupportedApi,
609
+ data,
610
+ });
611
+ }
612
+ return result;
613
+ }
614
+ validateSpecOperationId() {
615
+ const result = { errors: [], warnings: [] };
616
+ const apiMap = this.listAPIs();
617
+ // OperationId missing
618
+ const apisMissingOperationId = [];
619
+ for (const key in apiMap) {
620
+ const { operation } = apiMap[key];
621
+ if (!operation.operationId) {
622
+ apisMissingOperationId.push(key);
623
+ }
624
+ }
625
+ if (apisMissingOperationId.length > 0) {
626
+ result.warnings.push({
627
+ type: exports.WarningType.OperationIdMissing,
628
+ content: Utils.format(ConstantString.MissingOperationId, apisMissingOperationId.join(", ")),
629
+ data: apisMissingOperationId,
630
+ });
631
+ }
632
+ return result;
633
+ }
634
+ validateMethodAndPath(method, path) {
635
+ const result = { isValid: true, reason: [] };
636
+ if (this.options.allowMethods && !this.options.allowMethods.includes(method)) {
637
+ result.isValid = false;
638
+ result.reason.push(exports.ErrorType.MethodNotAllowed);
639
+ return result;
640
+ }
641
+ const pathObj = this.spec.paths[path];
642
+ if (!pathObj || !pathObj[method]) {
643
+ result.isValid = false;
644
+ result.reason.push(exports.ErrorType.UrlPathNotExist);
645
+ return result;
646
+ }
647
+ return result;
648
+ }
649
+ validateResponse(method, path) {
650
+ const result = { isValid: true, reason: [] };
651
+ const operationObject = this.spec.paths[path][method];
652
+ const { json, multipleMediaType } = Utils.getResponseJson(operationObject);
653
+ // only support response body only contains “application/json” content type
654
+ if (multipleMediaType) {
655
+ result.reason.push(exports.ErrorType.ResponseContainMultipleMediaTypes);
656
+ }
657
+ else if (Object.keys(json).length === 0) {
658
+ // response body should not be empty
659
+ result.reason.push(exports.ErrorType.ResponseJsonIsEmpty);
660
+ }
661
+ return result;
662
+ }
663
+ validateServer(method, path) {
664
+ const result = { isValid: true, reason: [] };
665
+ const serverObj = Utils.getServerObject(this.spec, method, path);
666
+ if (!serverObj) {
667
+ // should contain server URL
668
+ result.reason.push(exports.ErrorType.NoServerInformation);
669
+ }
670
+ else {
671
+ // server url should be absolute url with https protocol
672
+ const serverValidateResult = Utils.checkServerUrl([serverObj]);
673
+ result.reason.push(...serverValidateResult.map((item) => item.type));
674
+ }
675
+ return result;
676
+ }
677
+ validateAuth(method, path) {
678
+ const pathObj = this.spec.paths[path];
679
+ const operationObject = pathObj[method];
680
+ const securities = operationObject.security;
681
+ const authSchemeArray = Utils.getAuthArray(securities, this.spec);
682
+ if (authSchemeArray.length === 0) {
683
+ return { isValid: true, reason: [] };
684
+ }
685
+ if (this.options.allowAPIKeyAuth ||
686
+ this.options.allowOauth2 ||
687
+ this.options.allowBearerTokenAuth) {
688
+ // Currently we don't support multiple auth in one operation
689
+ if (authSchemeArray.length > 0 && authSchemeArray.every((auths) => auths.length > 1)) {
690
+ return {
691
+ isValid: false,
692
+ reason: [exports.ErrorType.MultipleAuthNotSupported],
693
+ };
694
+ }
695
+ for (const auths of authSchemeArray) {
696
+ if (auths.length === 1) {
697
+ if ((this.options.allowAPIKeyAuth && Utils.isAPIKeyAuth(auths[0].authScheme)) ||
698
+ (this.options.allowOauth2 && Utils.isOAuthWithAuthCodeFlow(auths[0].authScheme)) ||
699
+ (this.options.allowBearerTokenAuth && Utils.isBearerTokenAuth(auths[0].authScheme))) {
700
+ return { isValid: true, reason: [] };
701
+ }
702
+ }
703
+ }
704
+ }
705
+ return { isValid: false, reason: [exports.ErrorType.AuthTypeIsNotSupported] };
706
+ }
707
+ checkPostBodySchema(schema, isRequired = false) {
708
+ var _a;
709
+ const paramResult = {
710
+ requiredNum: 0,
711
+ optionalNum: 0,
712
+ isValid: true,
713
+ reason: [],
714
+ };
715
+ if (Object.keys(schema).length === 0) {
716
+ return paramResult;
717
+ }
718
+ const isRequiredWithoutDefault = isRequired && schema.default === undefined;
719
+ const isCopilot = this.projectType === exports.ProjectType.Copilot;
720
+ if (isCopilot && this.hasNestedObjectInSchema(schema)) {
721
+ paramResult.isValid = false;
722
+ paramResult.reason = [exports.ErrorType.RequestBodyContainsNestedObject];
723
+ return paramResult;
724
+ }
725
+ if (schema.type === "string" ||
726
+ schema.type === "integer" ||
727
+ schema.type === "boolean" ||
728
+ schema.type === "number") {
729
+ if (isRequiredWithoutDefault) {
730
+ paramResult.requiredNum = paramResult.requiredNum + 1;
731
+ }
732
+ else {
733
+ paramResult.optionalNum = paramResult.optionalNum + 1;
734
+ }
735
+ }
736
+ else if (schema.type === "object") {
737
+ const { properties } = schema;
738
+ for (const property in properties) {
739
+ let isRequired = false;
740
+ if (schema.required && ((_a = schema.required) === null || _a === void 0 ? void 0 : _a.indexOf(property)) >= 0) {
741
+ isRequired = true;
742
+ }
743
+ const result = this.checkPostBodySchema(properties[property], isRequired);
744
+ paramResult.requiredNum += result.requiredNum;
745
+ paramResult.optionalNum += result.optionalNum;
746
+ paramResult.isValid = paramResult.isValid && result.isValid;
747
+ paramResult.reason.push(...result.reason);
748
+ }
749
+ }
750
+ else {
751
+ if (isRequiredWithoutDefault && !isCopilot) {
752
+ paramResult.isValid = false;
753
+ paramResult.reason.push(exports.ErrorType.PostBodyContainsRequiredUnsupportedSchema);
754
+ }
755
+ }
756
+ return paramResult;
757
+ }
758
+ checkParamSchema(paramObject) {
759
+ const paramResult = {
760
+ requiredNum: 0,
761
+ optionalNum: 0,
762
+ isValid: true,
763
+ reason: [],
764
+ };
765
+ if (!paramObject) {
766
+ return paramResult;
767
+ }
768
+ const isCopilot = this.projectType === exports.ProjectType.Copilot;
769
+ for (let i = 0; i < paramObject.length; i++) {
770
+ const param = paramObject[i];
771
+ const schema = param.schema;
772
+ if (isCopilot && this.hasNestedObjectInSchema(schema)) {
773
+ paramResult.isValid = false;
774
+ paramResult.reason.push(exports.ErrorType.ParamsContainsNestedObject);
775
+ continue;
776
+ }
777
+ const isRequiredWithoutDefault = param.required && schema.default === undefined;
778
+ if (isCopilot) {
779
+ if (isRequiredWithoutDefault) {
780
+ paramResult.requiredNum = paramResult.requiredNum + 1;
781
+ }
782
+ else {
783
+ paramResult.optionalNum = paramResult.optionalNum + 1;
784
+ }
785
+ continue;
786
+ }
787
+ if (param.in === "header" || param.in === "cookie") {
788
+ if (isRequiredWithoutDefault) {
789
+ paramResult.isValid = false;
790
+ paramResult.reason.push(exports.ErrorType.ParamsContainRequiredUnsupportedSchema);
791
+ }
792
+ continue;
793
+ }
794
+ if (schema.type !== "boolean" &&
795
+ schema.type !== "string" &&
796
+ schema.type !== "number" &&
797
+ schema.type !== "integer") {
798
+ if (isRequiredWithoutDefault) {
799
+ paramResult.isValid = false;
800
+ paramResult.reason.push(exports.ErrorType.ParamsContainRequiredUnsupportedSchema);
801
+ }
802
+ continue;
803
+ }
804
+ if (param.in === "query" || param.in === "path") {
805
+ if (isRequiredWithoutDefault) {
806
+ paramResult.requiredNum = paramResult.requiredNum + 1;
807
+ }
808
+ else {
809
+ paramResult.optionalNum = paramResult.optionalNum + 1;
685
810
  }
686
- const [requiredP, optionalP] = Utils.generateParametersFromSchema(properties[property], property, allowMultipleParameters, isRequired);
687
- requiredParams.push(...requiredP);
688
- optionalParams.push(...optionalP);
689
811
  }
690
812
  }
691
- return [requiredParams, optionalParams];
813
+ return paramResult;
692
814
  }
693
- static updateParameterWithInputType(schema, param) {
694
- if (schema.enum) {
695
- param.inputType = "choiceset";
696
- param.choices = [];
697
- for (let i = 0; i < schema.enum.length; i++) {
698
- param.choices.push({
699
- title: schema.enum[i],
700
- value: schema.enum[i],
701
- });
815
+ hasNestedObjectInSchema(schema) {
816
+ if (schema.type === "object") {
817
+ for (const property in schema.properties) {
818
+ const nestedSchema = schema.properties[property];
819
+ if (nestedSchema.type === "object") {
820
+ return true;
821
+ }
702
822
  }
703
823
  }
704
- else if (schema.type === "string") {
705
- param.inputType = "text";
706
- }
707
- else if (schema.type === "integer" || schema.type === "number") {
708
- param.inputType = "number";
709
- }
710
- else if (schema.type === "boolean") {
711
- param.inputType = "toggle";
712
- }
713
- if (schema.default) {
714
- param.value = schema.default;
715
- }
824
+ return false;
716
825
  }
717
- static parseApiInfo(operationItem, options) {
718
- var _a, _b;
719
- const requiredParams = [];
720
- const optionalParams = [];
721
- const paramObject = operationItem.parameters;
722
- if (paramObject) {
723
- paramObject.forEach((param) => {
724
- var _a;
725
- const parameter = {
726
- name: param.name,
727
- title: Utils.updateFirstLetter(param.name).slice(0, ConstantString.ParameterTitleMaxLens),
728
- description: ((_a = param.description) !== null && _a !== void 0 ? _a : "").slice(0, ConstantString.ParameterDescriptionMaxLens),
729
- };
730
- const schema = param.schema;
731
- if (options.allowMultipleParameters && schema) {
732
- Utils.updateParameterWithInputType(schema, parameter);
733
- }
734
- if (param.in !== "header" && param.in !== "cookie") {
735
- if (param.required && (schema === null || schema === void 0 ? void 0 : schema.default) === undefined) {
736
- parameter.isRequired = true;
737
- requiredParams.push(parameter);
738
- }
739
- else {
740
- optionalParams.push(parameter);
741
- }
742
- }
743
- });
744
- }
745
- if (operationItem.requestBody) {
746
- const requestBody = operationItem.requestBody;
747
- const requestJson = requestBody.content["application/json"];
748
- if (Object.keys(requestJson).length !== 0) {
749
- const schema = requestJson.schema;
750
- const [requiredP, optionalP] = Utils.generateParametersFromSchema(schema, "requestBody", !!options.allowMultipleParameters, requestBody.required);
751
- requiredParams.push(...requiredP);
752
- optionalParams.push(...optionalP);
753
- }
826
+ }
827
+
828
+ // Copyright (c) Microsoft Corporation.
829
+ class CopilotValidator extends Validator {
830
+ constructor(spec, options) {
831
+ super();
832
+ this.projectType = exports.ProjectType.Copilot;
833
+ this.options = options;
834
+ this.spec = spec;
835
+ }
836
+ validateSpec() {
837
+ const result = { errors: [], warnings: [] };
838
+ // validate spec version
839
+ let validationResult = this.validateSpecVersion();
840
+ result.errors.push(...validationResult.errors);
841
+ // validate spec server
842
+ validationResult = this.validateSpecServer();
843
+ result.errors.push(...validationResult.errors);
844
+ // validate no supported API
845
+ validationResult = this.validateSpecNoSupportAPI();
846
+ result.errors.push(...validationResult.errors);
847
+ // validate operationId missing
848
+ validationResult = this.validateSpecOperationId();
849
+ result.warnings.push(...validationResult.warnings);
850
+ return result;
851
+ }
852
+ validateAPI(method, path) {
853
+ const result = { isValid: true, reason: [] };
854
+ method = method.toLocaleLowerCase();
855
+ // validate method and path
856
+ const methodAndPathResult = this.validateMethodAndPath(method, path);
857
+ if (!methodAndPathResult.isValid) {
858
+ return methodAndPathResult;
859
+ }
860
+ const operationObject = this.spec.paths[path][method];
861
+ // validate auth
862
+ const authCheckResult = this.validateAuth(method, path);
863
+ result.reason.push(...authCheckResult.reason);
864
+ // validate operationId
865
+ if (!this.options.allowMissingId && !operationObject.operationId) {
866
+ result.reason.push(exports.ErrorType.MissingOperationId);
754
867
  }
755
- const operationId = operationItem.operationId;
756
- const parameters = [];
757
- if (requiredParams.length !== 0) {
758
- parameters.push(...requiredParams);
868
+ // validate server
869
+ const validateServerResult = this.validateServer(method, path);
870
+ result.reason.push(...validateServerResult.reason);
871
+ // validate response
872
+ const validateResponseResult = this.validateResponse(method, path);
873
+ result.reason.push(...validateResponseResult.reason);
874
+ // validate requestBody
875
+ const requestBody = operationObject.requestBody;
876
+ const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
877
+ if (Utils.containMultipleMediaTypes(requestBody)) {
878
+ result.reason.push(exports.ErrorType.PostBodyContainMultipleMediaTypes);
759
879
  }
760
- else {
761
- parameters.push(optionalParams[0]);
880
+ if (requestJsonBody) {
881
+ const requestBodySchema = requestJsonBody.schema;
882
+ if (requestBodySchema.type !== "object") {
883
+ result.reason.push(exports.ErrorType.PostBodySchemaIsNotJson);
884
+ }
885
+ const requestBodyParamResult = this.checkPostBodySchema(requestBodySchema, requestBody.required);
886
+ result.reason.push(...requestBodyParamResult.reason);
762
887
  }
763
- const command = {
764
- context: ["compose"],
765
- type: "query",
766
- title: ((_a = operationItem.summary) !== null && _a !== void 0 ? _a : "").slice(0, ConstantString.CommandTitleMaxLens),
767
- id: operationId,
768
- parameters: parameters,
769
- description: ((_b = operationItem.description) !== null && _b !== void 0 ? _b : "").slice(0, ConstantString.CommandDescriptionMaxLens),
770
- };
771
- let warning = undefined;
772
- if (requiredParams.length === 0 && optionalParams.length > 1) {
773
- warning = {
774
- type: exports.WarningType.OperationOnlyContainsOptionalParam,
775
- content: Utils.format(ConstantString.OperationOnlyContainsOptionalParam, operationId),
776
- data: operationId,
777
- };
888
+ // validate parameters
889
+ const paramObject = operationObject.parameters;
890
+ const paramResult = this.checkParamSchema(paramObject);
891
+ result.reason.push(...paramResult.reason);
892
+ if (result.reason.length > 0) {
893
+ result.isValid = false;
778
894
  }
779
- return [command, warning];
895
+ return result;
780
896
  }
781
- static listAPIs(spec, options) {
782
- var _a;
783
- const paths = spec.paths;
784
- const result = {};
785
- for (const path in paths) {
786
- const methods = paths[path];
787
- for (const method in methods) {
788
- const operationObject = methods[method];
789
- if (((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && operationObject) {
790
- const validateResult = Utils.isSupportedApi(method, path, spec, options);
791
- result[`${method.toUpperCase()} ${path}`] = {
792
- operation: operationObject,
793
- isValid: validateResult.isValid,
794
- reason: validateResult.reason,
795
- };
796
- }
797
- }
897
+ }
898
+
899
+ // Copyright (c) Microsoft Corporation.
900
+ class SMEValidator extends Validator {
901
+ constructor(spec, options) {
902
+ super();
903
+ this.projectType = exports.ProjectType.SME;
904
+ this.options = options;
905
+ this.spec = spec;
906
+ }
907
+ validateSpec() {
908
+ const result = { errors: [], warnings: [] };
909
+ // validate spec version
910
+ let validationResult = this.validateSpecVersion();
911
+ result.errors.push(...validationResult.errors);
912
+ // validate spec server
913
+ validationResult = this.validateSpecServer();
914
+ result.errors.push(...validationResult.errors);
915
+ // validate no supported API
916
+ validationResult = this.validateSpecNoSupportAPI();
917
+ result.errors.push(...validationResult.errors);
918
+ // validate operationId missing
919
+ if (this.options.allowMissingId) {
920
+ validationResult = this.validateSpecOperationId();
921
+ result.warnings.push(...validationResult.warnings);
798
922
  }
799
923
  return result;
800
924
  }
801
- static validateSpec(spec, parser, isSwaggerFile, options) {
802
- const errors = [];
803
- const warnings = [];
804
- const apiMap = Utils.listAPIs(spec, options);
805
- if (isSwaggerFile) {
806
- warnings.push({
807
- type: exports.WarningType.ConvertSwaggerToOpenAPI,
808
- content: ConstantString.ConvertSwaggerToOpenAPI,
809
- });
925
+ validateAPI(method, path) {
926
+ const result = { isValid: true, reason: [] };
927
+ method = method.toLocaleLowerCase();
928
+ // validate method and path
929
+ const methodAndPathResult = this.validateMethodAndPath(method, path);
930
+ if (!methodAndPathResult.isValid) {
931
+ return methodAndPathResult;
932
+ }
933
+ const operationObject = this.spec.paths[path][method];
934
+ // validate auth
935
+ const authCheckResult = this.validateAuth(method, path);
936
+ result.reason.push(...authCheckResult.reason);
937
+ // validate operationId
938
+ if (!this.options.allowMissingId && !operationObject.operationId) {
939
+ result.reason.push(exports.ErrorType.MissingOperationId);
810
940
  }
811
- const serverErrors = Utils.validateServer(spec, options);
812
- errors.push(...serverErrors);
813
- // Remote reference not supported
814
- const refPaths = parser.$refs.paths();
815
- // refPaths [0] is the current spec file path
816
- if (refPaths.length > 1) {
817
- errors.push({
818
- type: exports.ErrorType.RemoteRefNotSupported,
819
- content: Utils.format(ConstantString.RemoteRefNotSupported, refPaths.join(", ")),
820
- data: refPaths,
821
- });
941
+ // validate server
942
+ const validateServerResult = this.validateServer(method, path);
943
+ result.reason.push(...validateServerResult.reason);
944
+ // validate response
945
+ const validateResponseResult = this.validateResponse(method, path);
946
+ result.reason.push(...validateResponseResult.reason);
947
+ let postBodyResult = {
948
+ requiredNum: 0,
949
+ optionalNum: 0,
950
+ isValid: true,
951
+ reason: [],
952
+ };
953
+ // validate requestBody
954
+ const requestBody = operationObject.requestBody;
955
+ const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
956
+ if (Utils.containMultipleMediaTypes(requestBody)) {
957
+ result.reason.push(exports.ErrorType.PostBodyContainMultipleMediaTypes);
822
958
  }
823
- // No supported API
824
- const validAPIs = Object.entries(apiMap).filter(([, value]) => value.isValid);
825
- if (validAPIs.length === 0) {
826
- errors.push({
827
- type: exports.ErrorType.NoSupportedApi,
828
- content: ConstantString.NoSupportedApi,
829
- });
959
+ if (requestJsonBody) {
960
+ const requestBodySchema = requestJsonBody.schema;
961
+ postBodyResult = this.checkPostBodySchema(requestBodySchema, requestBody.required);
962
+ result.reason.push(...postBodyResult.reason);
830
963
  }
831
- // OperationId missing
832
- const apisMissingOperationId = [];
833
- for (const key in apiMap) {
834
- const { operation } = apiMap[key];
835
- if (!operation.operationId) {
836
- apisMissingOperationId.push(key);
837
- }
964
+ // validate parameters
965
+ const paramObject = operationObject.parameters;
966
+ const paramResult = this.checkParamSchema(paramObject);
967
+ result.reason.push(...paramResult.reason);
968
+ // validate total parameters count
969
+ if (paramResult.isValid && postBodyResult.isValid) {
970
+ const paramCountResult = this.validateParamCount(postBodyResult, paramResult);
971
+ result.reason.push(...paramCountResult.reason);
838
972
  }
839
- if (apisMissingOperationId.length > 0) {
840
- warnings.push({
841
- type: exports.WarningType.OperationIdMissing,
842
- content: Utils.format(ConstantString.MissingOperationId, apisMissingOperationId.join(", ")),
843
- data: apisMissingOperationId,
844
- });
973
+ if (result.reason.length > 0) {
974
+ result.isValid = false;
845
975
  }
846
- let status = exports.ValidationStatus.Valid;
847
- if (warnings.length > 0 && errors.length === 0) {
848
- status = exports.ValidationStatus.Warning;
976
+ return result;
977
+ }
978
+ validateParamCount(postBodyResult, paramResult) {
979
+ const result = { isValid: true, reason: [] };
980
+ const totalRequiredParams = postBodyResult.requiredNum + paramResult.requiredNum;
981
+ const totalParams = totalRequiredParams + postBodyResult.optionalNum + paramResult.optionalNum;
982
+ if (totalRequiredParams > 1) {
983
+ if (!this.options.allowMultipleParameters ||
984
+ totalRequiredParams > SMEValidator.SMERequiredParamsMaxNum) {
985
+ result.reason.push(exports.ErrorType.ExceededRequiredParamsLimit);
986
+ }
849
987
  }
850
- else if (errors.length > 0) {
851
- status = exports.ValidationStatus.Error;
988
+ else if (totalParams === 0) {
989
+ result.reason.push(exports.ErrorType.NoParameter);
852
990
  }
853
- return {
854
- status,
855
- warnings,
856
- errors,
857
- };
991
+ return result;
858
992
  }
859
- static format(str, ...args) {
860
- let index = 0;
861
- return str.replace(/%s/g, () => {
862
- const arg = args[index++];
863
- return arg !== undefined ? arg : "";
864
- });
993
+ }
994
+ SMEValidator.SMERequiredParamsMaxNum = 5;
995
+
996
+ // Copyright (c) Microsoft Corporation.
997
+ class TeamsAIValidator extends Validator {
998
+ constructor(spec, options) {
999
+ super();
1000
+ this.projectType = exports.ProjectType.TeamsAi;
1001
+ this.options = options;
1002
+ this.spec = spec;
865
1003
  }
866
- static getSafeRegistrationIdEnvName(authName) {
867
- if (!authName) {
868
- return "";
1004
+ validateSpec() {
1005
+ const result = { errors: [], warnings: [] };
1006
+ // validate spec server
1007
+ let validationResult = this.validateSpecServer();
1008
+ result.errors.push(...validationResult.errors);
1009
+ // validate no supported API
1010
+ validationResult = this.validateSpecNoSupportAPI();
1011
+ result.errors.push(...validationResult.errors);
1012
+ return result;
1013
+ }
1014
+ validateAPI(method, path) {
1015
+ const result = { isValid: true, reason: [] };
1016
+ method = method.toLocaleLowerCase();
1017
+ // validate method and path
1018
+ const methodAndPathResult = this.validateMethodAndPath(method, path);
1019
+ if (!methodAndPathResult.isValid) {
1020
+ return methodAndPathResult;
1021
+ }
1022
+ const operationObject = this.spec.paths[path][method];
1023
+ // validate operationId
1024
+ if (!this.options.allowMissingId && !operationObject.operationId) {
1025
+ result.reason.push(exports.ErrorType.MissingOperationId);
869
1026
  }
870
- let safeRegistrationIdEnvName = authName.toUpperCase().replace(/[^A-Z0-9_]/g, "_");
871
- if (!safeRegistrationIdEnvName.match(/^[A-Z]/)) {
872
- safeRegistrationIdEnvName = "PREFIX_" + safeRegistrationIdEnvName;
1027
+ // validate server
1028
+ const validateServerResult = this.validateServer(method, path);
1029
+ result.reason.push(...validateServerResult.reason);
1030
+ if (result.reason.length > 0) {
1031
+ result.isValid = false;
873
1032
  }
874
- return safeRegistrationIdEnvName;
1033
+ return result;
875
1034
  }
876
- static getAllAPICount(spec) {
877
- let count = 0;
878
- const paths = spec.paths;
879
- for (const path in paths) {
880
- const methods = paths[path];
881
- for (const method in methods) {
882
- if (ConstantString.AllOperationMethods.includes(method)) {
883
- count++;
884
- }
885
- }
1035
+ }
1036
+
1037
+ class ValidatorFactory {
1038
+ static create(spec, options) {
1039
+ var _a;
1040
+ const type = (_a = options.projectType) !== null && _a !== void 0 ? _a : exports.ProjectType.SME;
1041
+ switch (type) {
1042
+ case exports.ProjectType.SME:
1043
+ return new SMEValidator(spec, options);
1044
+ case exports.ProjectType.Copilot:
1045
+ return new CopilotValidator(spec, options);
1046
+ case exports.ProjectType.TeamsAi:
1047
+ return new TeamsAIValidator(spec, options);
1048
+ default:
1049
+ throw new Error(`Invalid project type: ${type}`);
886
1050
  }
887
- return count;
888
1051
  }
889
1052
  }
890
1053
 
@@ -902,7 +1065,8 @@ class SpecFilter {
902
1065
  if (ConstantString.AllOperationMethods.includes(methodName) &&
903
1066
  pathObj &&
904
1067
  pathObj[methodName]) {
905
- const validateResult = Utils.isSupportedApi(methodName, path, resolvedSpec, options);
1068
+ const validator = ValidatorFactory.create(resolvedSpec, options);
1069
+ const validateResult = validator.validateAPI(methodName, path);
906
1070
  if (!validateResult.isValid) {
907
1071
  continue;
908
1072
  }
@@ -936,13 +1100,14 @@ class ManifestUpdater {
936
1100
  const apiPluginRelativePath = ManifestUpdater.getRelativePath(manifestPath, apiPluginFilePath);
937
1101
  manifest.plugins = [
938
1102
  {
939
- pluginFile: apiPluginRelativePath,
1103
+ file: apiPluginRelativePath,
1104
+ id: ConstantString.DefaultPluginId,
940
1105
  },
941
1106
  ];
942
1107
  const appName = this.removeEnvs(manifest.name.short);
943
1108
  ManifestUpdater.updateManifestDescription(manifest, spec);
944
1109
  const specRelativePath = ManifestUpdater.getRelativePath(manifestPath, outputSpecPath);
945
- const apiPlugin = ManifestUpdater.generatePluginManifestSchema(spec, specRelativePath, appName, options);
1110
+ const apiPlugin = yield ManifestUpdater.generatePluginManifestSchema(spec, specRelativePath, apiPluginFilePath, appName, options);
946
1111
  return [manifest, apiPlugin];
947
1112
  });
948
1113
  }
@@ -967,88 +1132,119 @@ class ManifestUpdater {
967
1132
  }
968
1133
  return parameter;
969
1134
  }
970
- static generatePluginManifestSchema(spec, specRelativePath, appName, options) {
971
- var _a, _b, _c;
972
- const functions = [];
973
- const functionNames = [];
974
- const paths = spec.paths;
975
- for (const pathUrl in paths) {
976
- const pathItem = paths[pathUrl];
977
- if (pathItem) {
978
- const operations = pathItem;
979
- for (const method in operations) {
980
- if (options.allowMethods.includes(method)) {
981
- const operationItem = operations[method];
982
- if (operationItem) {
983
- const operationId = operationItem.operationId;
984
- const description = (_a = operationItem.description) !== null && _a !== void 0 ? _a : "";
985
- const paramObject = operationItem.parameters;
986
- const requestBody = operationItem.requestBody;
987
- const parameters = {
988
- type: "object",
989
- properties: {},
990
- required: [],
991
- };
992
- if (paramObject) {
993
- for (let i = 0; i < paramObject.length; i++) {
994
- const param = paramObject[i];
995
- const schema = param.schema;
996
- parameters.properties[param.name] = ManifestUpdater.mapOpenAPISchemaToFuncParam(schema, method, pathUrl);
997
- if (param.required) {
998
- parameters.required.push(param.name);
999
- }
1000
- if (!parameters.properties[param.name].description) {
1001
- parameters.properties[param.name].description = (_b = param.description) !== null && _b !== void 0 ? _b : "";
1135
+ static generatePluginManifestSchema(spec, specRelativePath, apiPluginFilePath, appName, options) {
1136
+ var _a, _b, _c, _d;
1137
+ return __awaiter(this, void 0, void 0, function* () {
1138
+ const functions = [];
1139
+ const functionNames = [];
1140
+ const paths = spec.paths;
1141
+ for (const pathUrl in paths) {
1142
+ const pathItem = paths[pathUrl];
1143
+ if (pathItem) {
1144
+ const operations = pathItem;
1145
+ for (const method in operations) {
1146
+ if (options.allowMethods.includes(method)) {
1147
+ const operationItem = operations[method];
1148
+ if (operationItem) {
1149
+ const operationId = operationItem.operationId;
1150
+ const description = (_a = operationItem.description) !== null && _a !== void 0 ? _a : "";
1151
+ const paramObject = operationItem.parameters;
1152
+ const requestBody = operationItem.requestBody;
1153
+ const parameters = {
1154
+ type: "object",
1155
+ properties: {},
1156
+ required: [],
1157
+ };
1158
+ if (paramObject) {
1159
+ for (let i = 0; i < paramObject.length; i++) {
1160
+ const param = paramObject[i];
1161
+ const schema = param.schema;
1162
+ parameters.properties[param.name] = ManifestUpdater.mapOpenAPISchemaToFuncParam(schema, method, pathUrl);
1163
+ if (param.required) {
1164
+ parameters.required.push(param.name);
1165
+ }
1166
+ if (!parameters.properties[param.name].description) {
1167
+ parameters.properties[param.name].description = (_b = param.description) !== null && _b !== void 0 ? _b : "";
1168
+ }
1002
1169
  }
1003
1170
  }
1004
- }
1005
- if (requestBody) {
1006
- const requestJsonBody = requestBody.content["application/json"];
1007
- const requestBodySchema = requestJsonBody.schema;
1008
- if (requestBodySchema.type === "object") {
1009
- if (requestBodySchema.required) {
1010
- parameters.required.push(...requestBodySchema.required);
1171
+ if (requestBody) {
1172
+ const requestJsonBody = requestBody.content["application/json"];
1173
+ const requestBodySchema = requestJsonBody.schema;
1174
+ if (requestBodySchema.type === "object") {
1175
+ if (requestBodySchema.required) {
1176
+ parameters.required.push(...requestBodySchema.required);
1177
+ }
1178
+ for (const property in requestBodySchema.properties) {
1179
+ const schema = requestBodySchema.properties[property];
1180
+ parameters.properties[property] = ManifestUpdater.mapOpenAPISchemaToFuncParam(schema, method, pathUrl);
1181
+ }
1011
1182
  }
1012
- for (const property in requestBodySchema.properties) {
1013
- const schema = requestBodySchema.properties[property];
1014
- parameters.properties[property] = ManifestUpdater.mapOpenAPISchemaToFuncParam(schema, method, pathUrl);
1183
+ else {
1184
+ throw new SpecParserError(Utils.format(ConstantString.UnsupportedSchema, method, pathUrl, JSON.stringify(requestBodySchema)), exports.ErrorType.UpdateManifestFailed);
1015
1185
  }
1016
1186
  }
1017
- else {
1018
- throw new SpecParserError(Utils.format(ConstantString.UnsupportedSchema, method, pathUrl, JSON.stringify(requestBodySchema)), exports.ErrorType.UpdateManifestFailed);
1019
- }
1187
+ const funcObj = {
1188
+ name: operationId,
1189
+ description: description,
1190
+ parameters: parameters,
1191
+ };
1192
+ functions.push(funcObj);
1193
+ functionNames.push(operationId);
1020
1194
  }
1021
- const funcObj = {
1022
- name: operationId,
1023
- description: description,
1024
- parameters: parameters,
1025
- };
1026
- functions.push(funcObj);
1027
- functionNames.push(operationId);
1028
1195
  }
1029
1196
  }
1030
1197
  }
1031
1198
  }
1032
- }
1033
- const apiPlugin = {
1034
- schema_version: "v2",
1035
- name_for_human: appName,
1036
- description_for_human: (_c = spec.info.description) !== null && _c !== void 0 ? _c : "<Please add description of the plugin>",
1037
- functions: functions,
1038
- runtimes: [
1039
- {
1199
+ let apiPlugin;
1200
+ if (yield fs__default['default'].pathExists(apiPluginFilePath)) {
1201
+ apiPlugin = yield fs__default['default'].readJSON(apiPluginFilePath);
1202
+ }
1203
+ else {
1204
+ apiPlugin = {
1205
+ schema_version: "v2",
1206
+ name_for_human: "",
1207
+ description_for_human: "",
1208
+ functions: [],
1209
+ runtimes: [],
1210
+ };
1211
+ }
1212
+ apiPlugin.functions = apiPlugin.functions || [];
1213
+ for (const func of functions) {
1214
+ const index = (_c = apiPlugin.functions) === null || _c === void 0 ? void 0 : _c.findIndex((f) => f.name === func.name);
1215
+ if (index === -1) {
1216
+ apiPlugin.functions.push(func);
1217
+ }
1218
+ else {
1219
+ apiPlugin.functions[index] = func;
1220
+ }
1221
+ }
1222
+ apiPlugin.runtimes = apiPlugin.runtimes || [];
1223
+ const index = apiPlugin.runtimes.findIndex((runtime) => runtime.spec.url === specRelativePath);
1224
+ if (index === -1) {
1225
+ apiPlugin.runtimes.push({
1040
1226
  type: "OpenApi",
1041
1227
  auth: {
1042
- type: "none", // TODO, support auth in the future
1228
+ type: "none",
1043
1229
  },
1044
1230
  spec: {
1045
1231
  url: specRelativePath,
1046
1232
  },
1047
1233
  run_for_functions: functionNames,
1048
- },
1049
- ],
1050
- };
1051
- return apiPlugin;
1234
+ });
1235
+ }
1236
+ else {
1237
+ apiPlugin.runtimes[index].run_for_functions = functionNames;
1238
+ }
1239
+ if (!apiPlugin.name_for_human) {
1240
+ apiPlugin.name_for_human = appName;
1241
+ }
1242
+ if (!apiPlugin.description_for_human) {
1243
+ apiPlugin.description_for_human =
1244
+ (_d = spec.info.description) !== null && _d !== void 0 ? _d : "<Please add description of the plugin>";
1245
+ }
1246
+ return apiPlugin;
1247
+ });
1052
1248
  }
1053
1249
  static updateManifest(manifestPath, outputSpecPath, spec, options, adaptiveCardFolder, authInfo) {
1054
1250
  return __awaiter(this, void 0, void 0, function* () {
@@ -1119,16 +1315,26 @@ class ManifestUpdater {
1119
1315
  if ((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) {
1120
1316
  const operationItem = operations[method];
1121
1317
  if (operationItem) {
1122
- const [command, warning] = Utils.parseApiInfo(operationItem, options);
1318
+ const command = Utils.parseApiInfo(operationItem, options);
1319
+ if (command.parameters &&
1320
+ command.parameters.length >= 1 &&
1321
+ command.parameters.some((param) => param.isRequired)) {
1322
+ command.parameters = command.parameters.filter((param) => param.isRequired);
1323
+ }
1324
+ else if (command.parameters && command.parameters.length > 0) {
1325
+ command.parameters = [command.parameters[0]];
1326
+ warnings.push({
1327
+ type: exports.WarningType.OperationOnlyContainsOptionalParam,
1328
+ content: Utils.format(ConstantString.OperationOnlyContainsOptionalParam, command.id),
1329
+ data: command.id,
1330
+ });
1331
+ }
1123
1332
  if (adaptiveCardFolder) {
1124
1333
  const adaptiveCardPath = path__default['default'].join(adaptiveCardFolder, command.id + ".json");
1125
1334
  command.apiResponseRenderingTemplateFile = (yield fs__default['default'].pathExists(adaptiveCardPath))
1126
1335
  ? ManifestUpdater.getRelativePath(manifestPath, adaptiveCardPath)
1127
1336
  : "";
1128
1337
  }
1129
- if (warning) {
1130
- warnings.push(warning);
1131
- }
1132
1338
  commands.push(command);
1133
1339
  }
1134
1340
  }
@@ -1447,6 +1653,8 @@ class SpecParser {
1447
1653
  errors: [{ type: exports.ErrorType.SpecNotValid, content: e.toString() }],
1448
1654
  };
1449
1655
  }
1656
+ const errors = [];
1657
+ const warnings = [];
1450
1658
  if (!this.options.allowSwagger && this.isSwaggerFile) {
1451
1659
  return {
1452
1660
  status: exports.ValidationStatus.Error,
@@ -1456,23 +1664,38 @@ class SpecParser {
1456
1664
  ],
1457
1665
  };
1458
1666
  }
1459
- if (this.options.projectType === exports.ProjectType.SME ||
1460
- this.options.projectType === exports.ProjectType.Copilot) {
1461
- if (this.spec.openapi >= "3.1.0") {
1462
- return {
1463
- status: exports.ValidationStatus.Error,
1464
- warnings: [],
1465
- errors: [
1466
- {
1467
- type: exports.ErrorType.SpecVersionNotSupported,
1468
- content: Utils.format(ConstantString.SpecVersionNotSupported, this.spec.openapi),
1469
- data: this.spec.openapi,
1470
- },
1471
- ],
1472
- };
1473
- }
1667
+ // Remote reference not supported
1668
+ const refPaths = this.parser.$refs.paths();
1669
+ // refPaths [0] is the current spec file path
1670
+ if (refPaths.length > 1) {
1671
+ errors.push({
1672
+ type: exports.ErrorType.RemoteRefNotSupported,
1673
+ content: Utils.format(ConstantString.RemoteRefNotSupported, refPaths.join(", ")),
1674
+ data: refPaths,
1675
+ });
1676
+ }
1677
+ if (!!this.isSwaggerFile && this.options.allowSwagger) {
1678
+ warnings.push({
1679
+ type: exports.WarningType.ConvertSwaggerToOpenAPI,
1680
+ content: ConstantString.ConvertSwaggerToOpenAPI,
1681
+ });
1682
+ }
1683
+ const validator = this.getValidator(this.spec);
1684
+ const validationResult = validator.validateSpec();
1685
+ warnings.push(...validationResult.warnings);
1686
+ errors.push(...validationResult.errors);
1687
+ let status = exports.ValidationStatus.Valid;
1688
+ if (warnings.length > 0 && errors.length === 0) {
1689
+ status = exports.ValidationStatus.Warning;
1474
1690
  }
1475
- return Utils.validateSpec(this.spec, this.parser, !!this.isSwaggerFile, this.options);
1691
+ else if (errors.length > 0) {
1692
+ status = exports.ValidationStatus.Error;
1693
+ }
1694
+ return {
1695
+ status: status,
1696
+ warnings: warnings,
1697
+ errors: errors,
1698
+ };
1476
1699
  }
1477
1700
  catch (err) {
1478
1701
  throw new SpecParserError(err.toString(), exports.ErrorType.ValidateFailed);
@@ -1505,34 +1728,27 @@ class SpecParser {
1505
1728
  for (const apiKey in apiMap) {
1506
1729
  const { operation, isValid, reason } = apiMap[apiKey];
1507
1730
  const [method, path] = apiKey.split(" ");
1731
+ const operationId = (_a = operation.operationId) !== null && _a !== void 0 ? _a : `${method.toLowerCase()}${Utils.convertPathToCamelCase(path)}`;
1508
1732
  const apiResult = {
1509
- api: "",
1733
+ api: apiKey,
1510
1734
  server: "",
1511
- operationId: "",
1735
+ operationId: operationId,
1512
1736
  isValid: isValid,
1513
1737
  reason: reason,
1514
1738
  };
1515
- const rootServer = spec.servers && spec.servers[0];
1516
- const methodServer = spec.paths[path].servers && ((_a = spec.paths[path]) === null || _a === void 0 ? void 0 : _a.servers[0]);
1517
- const operationServer = operation.servers && operation.servers[0];
1518
- const serverUrl = operationServer || methodServer || rootServer;
1519
- if (!serverUrl) {
1520
- throw new SpecParserError(ConstantString.NoServerInformation, exports.ErrorType.NoServerInformation);
1521
- }
1522
- apiResult.server = Utils.resolveEnv(serverUrl.url);
1523
- let operationId = operation.operationId;
1524
- if (!operationId) {
1525
- operationId = `${method.toLowerCase()}${Utils.convertPathToCamelCase(path)}`;
1526
- }
1527
- apiResult.operationId = operationId;
1528
- const authArray = Utils.getAuthArray(operation.security, spec);
1529
- for (const auths of authArray) {
1530
- if (auths.length === 1) {
1531
- apiResult.auth = auths[0];
1532
- break;
1739
+ if (isValid) {
1740
+ const serverObj = Utils.getServerObject(spec, method.toLocaleLowerCase(), path);
1741
+ if (serverObj) {
1742
+ apiResult.server = Utils.resolveEnv(serverObj.url);
1743
+ }
1744
+ const authArray = Utils.getAuthArray(operation.security, spec);
1745
+ for (const auths of authArray) {
1746
+ if (auths.length === 1) {
1747
+ apiResult.auth = auths[0];
1748
+ break;
1749
+ }
1533
1750
  }
1534
1751
  }
1535
- apiResult.api = apiKey;
1536
1752
  result.APIs.push(apiResult);
1537
1753
  }
1538
1754
  result.allAPICount = result.APIs.length;
@@ -1721,12 +1937,18 @@ class SpecParser {
1721
1937
  });
1722
1938
  }
1723
1939
  getAPIs(spec) {
1724
- if (this.apiMap !== undefined) {
1725
- return this.apiMap;
1940
+ const validator = this.getValidator(spec);
1941
+ const apiMap = validator.listAPIs();
1942
+ this.apiMap = apiMap;
1943
+ return apiMap;
1944
+ }
1945
+ getValidator(spec) {
1946
+ if (this.validator) {
1947
+ return this.validator;
1726
1948
  }
1727
- const result = Utils.listAPIs(spec, this.options);
1728
- this.apiMap = result;
1729
- return result;
1949
+ const validator = ValidatorFactory.create(spec, this.options);
1950
+ this.validator = validator;
1951
+ return validator;
1730
1952
  }
1731
1953
  }
1732
1954