@microsoft/m365-spec-parser 0.2.3 → 0.2.4-alpha.ad0d7aa1a.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.
- package/dist/index.esm2017.js +324 -247
- package/dist/index.esm2017.js.map +1 -1
- package/dist/index.esm2017.mjs +385 -282
- package/dist/index.esm2017.mjs.map +1 -1
- package/dist/index.esm5.js +324 -247
- package/dist/index.esm5.js.map +1 -1
- package/dist/index.node.cjs.js +385 -282
- package/dist/index.node.cjs.js.map +1 -1
- package/dist/src/adaptiveCardGenerator.d.ts +5 -3
- package/dist/src/constants.d.ts +4 -1
- package/dist/src/interfaces.d.ts +3 -3
- package/dist/src/jsonDataGenerator.d.ts +6 -0
- package/dist/src/utils.d.ts +5 -4
- package/package.json +3 -3
package/dist/index.esm2017.js
CHANGED
|
@@ -32,11 +32,8 @@ var ErrorType;
|
|
|
32
32
|
ErrorType["PostBodyContainMultipleMediaTypes"] = "post-body-contain-multiple-media-types";
|
|
33
33
|
ErrorType["ResponseContainMultipleMediaTypes"] = "response-contain-multiple-media-types";
|
|
34
34
|
ErrorType["ResponseJsonIsEmpty"] = "response-json-is-empty";
|
|
35
|
-
ErrorType["PostBodySchemaIsNotJson"] = "post-body-schema-is-not-json";
|
|
36
35
|
ErrorType["PostBodyContainsRequiredUnsupportedSchema"] = "post-body-contains-required-unsupported-schema";
|
|
37
36
|
ErrorType["ParamsContainRequiredUnsupportedSchema"] = "params-contain-required-unsupported-schema";
|
|
38
|
-
ErrorType["ParamsContainsNestedObject"] = "params-contains-nested-object";
|
|
39
|
-
ErrorType["RequestBodyContainsNestedObject"] = "request-body-contains-nested-object";
|
|
40
37
|
ErrorType["ExceededRequiredParamsLimit"] = "exceeded-required-params-limit";
|
|
41
38
|
ErrorType["NoParameter"] = "no-parameter";
|
|
42
39
|
ErrorType["NoAPIInfo"] = "no-api-info";
|
|
@@ -55,6 +52,9 @@ var WarningType;
|
|
|
55
52
|
WarningType["OperationOnlyContainsOptionalParam"] = "operation-only-contains-optional-param";
|
|
56
53
|
WarningType["ConvertSwaggerToOpenAPI"] = "convert-swagger-to-openapi";
|
|
57
54
|
WarningType["FuncDescriptionTooLong"] = "function-description-too-long";
|
|
55
|
+
WarningType["OperationIdContainsSpecialCharacters"] = "operationid-contains-special-characters";
|
|
56
|
+
WarningType["UnsupportedAuthType"] = "unsupported-auth-type";
|
|
57
|
+
WarningType["GenerateJsonDataFailed"] = "generate-json-data-failed";
|
|
58
58
|
WarningType["Unknown"] = "unknown";
|
|
59
59
|
})(WarningType || (WarningType = {}));
|
|
60
60
|
/**
|
|
@@ -100,15 +100,18 @@ ConstantString.ConvertSwaggerToOpenAPI = "The Swagger 2.0 file has been converte
|
|
|
100
100
|
ConstantString.SwaggerNotSupported = "Swagger 2.0 is not supported. Please convert to OpenAPI 3.0 manually before proceeding.";
|
|
101
101
|
ConstantString.SpecVersionNotSupported = "Unsupported OpenAPI version %s. Please use version 3.0.x.";
|
|
102
102
|
ConstantString.MultipleAuthNotSupported = "Multiple authentication methods are unsupported. Ensure all selected APIs use identical authentication.";
|
|
103
|
+
ConstantString.OperationIdContainsSpecialCharacters = "Operation id '%s' in OpenAPI description document contained special characters and was renamed to '%s'.";
|
|
104
|
+
ConstantString.AuthTypeIsNotSupported = "Unsupported authorization type in API '%s'. No authorization will be used.";
|
|
103
105
|
ConstantString.UnsupportedSchema = "Unsupported schema in %s %s: %s";
|
|
104
106
|
ConstantString.FuncDescriptionTooLong = "The description of the function '%s' is too long. The current length is %s characters, while the maximum allowed length is %s characters.";
|
|
107
|
+
ConstantString.GenerateJsonDataFailed = "Failed to generate JSON data for api: %s due to %s.";
|
|
105
108
|
ConstantString.WrappedCardVersion = "devPreview";
|
|
106
109
|
ConstantString.WrappedCardSchema = "https://developer.microsoft.com/json-schemas/teams/vDevPreview/MicrosoftTeams.ResponseRenderingTemplate.schema.json";
|
|
107
110
|
ConstantString.WrappedCardResponseLayout = "list";
|
|
108
111
|
ConstantString.GetMethod = "get";
|
|
109
112
|
ConstantString.PostMethod = "post";
|
|
110
113
|
ConstantString.AdaptiveCardVersion = "1.5";
|
|
111
|
-
ConstantString.AdaptiveCardSchema = "
|
|
114
|
+
ConstantString.AdaptiveCardSchema = "https://adaptivecards.io/schemas/adaptive-card.json";
|
|
112
115
|
ConstantString.AdaptiveCardType = "AdaptiveCard";
|
|
113
116
|
ConstantString.TextBlockType = "TextBlock";
|
|
114
117
|
ConstantString.ImageType = "Image";
|
|
@@ -187,16 +190,8 @@ ConstantString.PluginManifestSchema = "https://developer.microsoft.com/json-sche
|
|
|
187
190
|
|
|
188
191
|
// Copyright (c) Microsoft Corporation.
|
|
189
192
|
class Utils {
|
|
190
|
-
static
|
|
191
|
-
|
|
192
|
-
for (const property in schema.properties) {
|
|
193
|
-
const nestedSchema = schema.properties[property];
|
|
194
|
-
if (nestedSchema.type === "object") {
|
|
195
|
-
return true;
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
return false;
|
|
193
|
+
static isObjectSchema(schema) {
|
|
194
|
+
return schema.type === "object" || (!schema.type && !!schema.properties);
|
|
200
195
|
}
|
|
201
196
|
static containMultipleMediaTypes(bodyObject) {
|
|
202
197
|
return Object.keys((bodyObject === null || bodyObject === void 0 ? void 0 : bodyObject.content) || {}).length > 1;
|
|
@@ -212,6 +207,23 @@ class Utils {
|
|
|
212
207
|
authScheme.flows &&
|
|
213
208
|
authScheme.flows.authorizationCode);
|
|
214
209
|
}
|
|
210
|
+
static isNotSupportedAuth(authSchemeArray) {
|
|
211
|
+
if (authSchemeArray.length === 0) {
|
|
212
|
+
return false;
|
|
213
|
+
}
|
|
214
|
+
if (authSchemeArray.length > 0 && authSchemeArray.every((auths) => auths.length > 1)) {
|
|
215
|
+
return true;
|
|
216
|
+
}
|
|
217
|
+
for (const auths of authSchemeArray) {
|
|
218
|
+
if (auths.length === 1) {
|
|
219
|
+
if (Utils.isOAuthWithAuthCodeFlow(auths[0].authScheme) ||
|
|
220
|
+
Utils.isBearerTokenAuth(auths[0].authScheme)) {
|
|
221
|
+
return false;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
return true;
|
|
226
|
+
}
|
|
215
227
|
static getAuthArray(securities, spec) {
|
|
216
228
|
var _a;
|
|
217
229
|
const result = [];
|
|
@@ -264,27 +276,33 @@ class Utils {
|
|
|
264
276
|
let multipleMediaType = false;
|
|
265
277
|
for (const code of ConstantString.ResponseCodeFor20X) {
|
|
266
278
|
const responseObject = (_a = operationObject === null || operationObject === void 0 ? void 0 : operationObject.responses) === null || _a === void 0 ? void 0 : _a[code];
|
|
267
|
-
if (responseObject
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
else {
|
|
280
|
-
return { json, multipleMediaType };
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
}
|
|
279
|
+
if (!responseObject) {
|
|
280
|
+
continue;
|
|
281
|
+
}
|
|
282
|
+
multipleMediaType = Utils.containMultipleMediaTypes(responseObject);
|
|
283
|
+
if (!allowMultipleMediaType && multipleMediaType) {
|
|
284
|
+
json = {};
|
|
285
|
+
continue;
|
|
286
|
+
}
|
|
287
|
+
const mediaObj = Utils.getJsonContentType(responseObject);
|
|
288
|
+
if (Object.keys(mediaObj).length > 0) {
|
|
289
|
+
json = mediaObj;
|
|
290
|
+
return { json, multipleMediaType };
|
|
284
291
|
}
|
|
285
292
|
}
|
|
286
293
|
return { json, multipleMediaType };
|
|
287
294
|
}
|
|
295
|
+
static getJsonContentType(responseObject) {
|
|
296
|
+
if (responseObject.content) {
|
|
297
|
+
for (const contentType of Object.keys(responseObject.content)) {
|
|
298
|
+
// json media type can also be "application/json; charset=utf-8"
|
|
299
|
+
if (contentType.indexOf("application/json") >= 0) {
|
|
300
|
+
return responseObject.content[contentType];
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
return {};
|
|
305
|
+
}
|
|
288
306
|
static convertPathToCamelCase(path) {
|
|
289
307
|
const pathSegments = path.split(/[./{]/);
|
|
290
308
|
const camelCaseSegments = pathSegments.map((segment) => {
|
|
@@ -320,7 +338,7 @@ class Utils {
|
|
|
320
338
|
}
|
|
321
339
|
return newStr;
|
|
322
340
|
}
|
|
323
|
-
static checkServerUrl(servers) {
|
|
341
|
+
static checkServerUrl(servers, allowHttp = false) {
|
|
324
342
|
const errors = [];
|
|
325
343
|
let serverUrl;
|
|
326
344
|
try {
|
|
@@ -343,8 +361,7 @@ class Utils {
|
|
|
343
361
|
data: servers,
|
|
344
362
|
});
|
|
345
363
|
}
|
|
346
|
-
else if (protocol !== "https:") {
|
|
347
|
-
// Http server url is not supported
|
|
364
|
+
else if (protocol !== "https:" && !(protocol === "http:" && allowHttp)) {
|
|
348
365
|
const protocolString = protocol.slice(0, -1);
|
|
349
366
|
errors.push({
|
|
350
367
|
type: ErrorType.UrlProtocolNotSupported,
|
|
@@ -360,10 +377,11 @@ class Utils {
|
|
|
360
377
|
let hasTopLevelServers = false;
|
|
361
378
|
let hasPathLevelServers = false;
|
|
362
379
|
let hasOperationLevelServers = false;
|
|
380
|
+
const allowHttp = options.projectType === ProjectType.Copilot;
|
|
363
381
|
if (spec.servers && spec.servers.length >= 1) {
|
|
364
382
|
hasTopLevelServers = true;
|
|
365
383
|
// for multiple server, we only use the first url
|
|
366
|
-
const serverErrors = Utils.checkServerUrl(spec.servers);
|
|
384
|
+
const serverErrors = Utils.checkServerUrl(spec.servers, allowHttp);
|
|
367
385
|
errors.push(...serverErrors);
|
|
368
386
|
}
|
|
369
387
|
const paths = spec.paths;
|
|
@@ -371,7 +389,7 @@ class Utils {
|
|
|
371
389
|
const methods = paths[path];
|
|
372
390
|
if ((methods === null || methods === void 0 ? void 0 : methods.servers) && methods.servers.length >= 1) {
|
|
373
391
|
hasPathLevelServers = true;
|
|
374
|
-
const serverErrors = Utils.checkServerUrl(methods.servers);
|
|
392
|
+
const serverErrors = Utils.checkServerUrl(methods.servers, allowHttp);
|
|
375
393
|
errors.push(...serverErrors);
|
|
376
394
|
}
|
|
377
395
|
for (const method in methods) {
|
|
@@ -379,7 +397,7 @@ class Utils {
|
|
|
379
397
|
if (((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && operationObject) {
|
|
380
398
|
if ((operationObject === null || operationObject === void 0 ? void 0 : operationObject.servers) && operationObject.servers.length >= 1) {
|
|
381
399
|
hasOperationLevelServers = true;
|
|
382
|
-
const serverErrors = Utils.checkServerUrl(operationObject.servers);
|
|
400
|
+
const serverErrors = Utils.checkServerUrl(operationObject.servers, allowHttp);
|
|
383
401
|
errors.push(...serverErrors);
|
|
384
402
|
}
|
|
385
403
|
}
|
|
@@ -426,7 +444,7 @@ class Utils {
|
|
|
426
444
|
optionalParams.push(parameter);
|
|
427
445
|
}
|
|
428
446
|
}
|
|
429
|
-
else if (schema
|
|
447
|
+
else if (Utils.isObjectSchema(schema)) {
|
|
430
448
|
const { properties } = schema;
|
|
431
449
|
for (const property in properties) {
|
|
432
450
|
let isRequired = false;
|
|
@@ -540,29 +558,6 @@ class Utils {
|
|
|
540
558
|
const serverUrl = operationServer || methodServer || rootServer;
|
|
541
559
|
return serverUrl;
|
|
542
560
|
}
|
|
543
|
-
static limitACBodyProperties(body, maxCount) {
|
|
544
|
-
const result = [];
|
|
545
|
-
let currentCount = 0;
|
|
546
|
-
for (const element of body) {
|
|
547
|
-
if (element.type === ConstantString.ContainerType) {
|
|
548
|
-
const items = this.limitACBodyProperties(element.items, maxCount - currentCount);
|
|
549
|
-
result.push({
|
|
550
|
-
type: ConstantString.ContainerType,
|
|
551
|
-
$data: element.$data,
|
|
552
|
-
items: items,
|
|
553
|
-
});
|
|
554
|
-
currentCount += items.length;
|
|
555
|
-
}
|
|
556
|
-
else {
|
|
557
|
-
result.push(element);
|
|
558
|
-
currentCount++;
|
|
559
|
-
}
|
|
560
|
-
if (currentCount >= maxCount) {
|
|
561
|
-
break;
|
|
562
|
-
}
|
|
563
|
-
}
|
|
564
|
-
return result;
|
|
565
|
-
}
|
|
566
561
|
}
|
|
567
562
|
|
|
568
563
|
// Copyright (c) Microsoft Corporation.
|
|
@@ -691,22 +686,6 @@ class Validator {
|
|
|
691
686
|
}
|
|
692
687
|
return result;
|
|
693
688
|
}
|
|
694
|
-
validateResponse(method, path) {
|
|
695
|
-
const result = { isValid: true, reason: [] };
|
|
696
|
-
const operationObject = this.spec.paths[path][method];
|
|
697
|
-
const { json, multipleMediaType } = Utils.getResponseJson(operationObject);
|
|
698
|
-
if (this.options.projectType === ProjectType.SME) {
|
|
699
|
-
// only support response body only contains “application/json” content type
|
|
700
|
-
if (multipleMediaType) {
|
|
701
|
-
result.reason.push(ErrorType.ResponseContainMultipleMediaTypes);
|
|
702
|
-
}
|
|
703
|
-
else if (Object.keys(json).length === 0) {
|
|
704
|
-
// response body should not be empty
|
|
705
|
-
result.reason.push(ErrorType.ResponseJsonIsEmpty);
|
|
706
|
-
}
|
|
707
|
-
}
|
|
708
|
-
return result;
|
|
709
|
-
}
|
|
710
689
|
validateServer(method, path) {
|
|
711
690
|
const result = { isValid: true, reason: [] };
|
|
712
691
|
const serverObj = Utils.getServerObject(this.spec, method, path);
|
|
@@ -715,8 +694,8 @@ class Validator {
|
|
|
715
694
|
result.reason.push(ErrorType.NoServerInformation);
|
|
716
695
|
}
|
|
717
696
|
else {
|
|
718
|
-
|
|
719
|
-
const serverValidateResult = Utils.checkServerUrl([serverObj]);
|
|
697
|
+
const allowHttp = this.projectType === ProjectType.Copilot;
|
|
698
|
+
const serverValidateResult = Utils.checkServerUrl([serverObj], allowHttp);
|
|
720
699
|
result.reason.push(...serverValidateResult.map((item) => item.type));
|
|
721
700
|
}
|
|
722
701
|
return result;
|
|
@@ -739,6 +718,9 @@ class Validator {
|
|
|
739
718
|
reason: [ErrorType.MultipleAuthNotSupported],
|
|
740
719
|
};
|
|
741
720
|
}
|
|
721
|
+
if (this.projectType === ProjectType.Copilot) {
|
|
722
|
+
return { isValid: true, reason: [] };
|
|
723
|
+
}
|
|
742
724
|
for (const auths of authSchemeArray) {
|
|
743
725
|
if (auths.length === 1) {
|
|
744
726
|
if ((this.options.allowAPIKeyAuth && Utils.isAPIKeyAuth(auths[0].authScheme)) ||
|
|
@@ -751,125 +733,6 @@ class Validator {
|
|
|
751
733
|
}
|
|
752
734
|
return { isValid: false, reason: [ErrorType.AuthTypeIsNotSupported] };
|
|
753
735
|
}
|
|
754
|
-
checkPostBodySchema(schema, isRequired = false) {
|
|
755
|
-
var _a;
|
|
756
|
-
const paramResult = {
|
|
757
|
-
requiredNum: 0,
|
|
758
|
-
optionalNum: 0,
|
|
759
|
-
isValid: true,
|
|
760
|
-
reason: [],
|
|
761
|
-
};
|
|
762
|
-
if (Object.keys(schema).length === 0) {
|
|
763
|
-
return paramResult;
|
|
764
|
-
}
|
|
765
|
-
const isRequiredWithoutDefault = isRequired && schema.default === undefined;
|
|
766
|
-
const isCopilot = this.projectType === ProjectType.Copilot;
|
|
767
|
-
if (isCopilot && this.hasNestedObjectInSchema(schema)) {
|
|
768
|
-
paramResult.isValid = false;
|
|
769
|
-
paramResult.reason = [ErrorType.RequestBodyContainsNestedObject];
|
|
770
|
-
return paramResult;
|
|
771
|
-
}
|
|
772
|
-
if (schema.type === "string" ||
|
|
773
|
-
schema.type === "integer" ||
|
|
774
|
-
schema.type === "boolean" ||
|
|
775
|
-
schema.type === "number") {
|
|
776
|
-
if (isRequiredWithoutDefault) {
|
|
777
|
-
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
778
|
-
}
|
|
779
|
-
else {
|
|
780
|
-
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
781
|
-
}
|
|
782
|
-
}
|
|
783
|
-
else if (schema.type === "object") {
|
|
784
|
-
const { properties } = schema;
|
|
785
|
-
for (const property in properties) {
|
|
786
|
-
let isRequired = false;
|
|
787
|
-
if (schema.required && ((_a = schema.required) === null || _a === void 0 ? void 0 : _a.indexOf(property)) >= 0) {
|
|
788
|
-
isRequired = true;
|
|
789
|
-
}
|
|
790
|
-
const result = this.checkPostBodySchema(properties[property], isRequired);
|
|
791
|
-
paramResult.requiredNum += result.requiredNum;
|
|
792
|
-
paramResult.optionalNum += result.optionalNum;
|
|
793
|
-
paramResult.isValid = paramResult.isValid && result.isValid;
|
|
794
|
-
paramResult.reason.push(...result.reason);
|
|
795
|
-
}
|
|
796
|
-
}
|
|
797
|
-
else {
|
|
798
|
-
if (isRequiredWithoutDefault && !isCopilot) {
|
|
799
|
-
paramResult.isValid = false;
|
|
800
|
-
paramResult.reason.push(ErrorType.PostBodyContainsRequiredUnsupportedSchema);
|
|
801
|
-
}
|
|
802
|
-
}
|
|
803
|
-
return paramResult;
|
|
804
|
-
}
|
|
805
|
-
checkParamSchema(paramObject) {
|
|
806
|
-
const paramResult = {
|
|
807
|
-
requiredNum: 0,
|
|
808
|
-
optionalNum: 0,
|
|
809
|
-
isValid: true,
|
|
810
|
-
reason: [],
|
|
811
|
-
};
|
|
812
|
-
if (!paramObject) {
|
|
813
|
-
return paramResult;
|
|
814
|
-
}
|
|
815
|
-
const isCopilot = this.projectType === ProjectType.Copilot;
|
|
816
|
-
for (let i = 0; i < paramObject.length; i++) {
|
|
817
|
-
const param = paramObject[i];
|
|
818
|
-
const schema = param.schema;
|
|
819
|
-
if (isCopilot && this.hasNestedObjectInSchema(schema)) {
|
|
820
|
-
paramResult.isValid = false;
|
|
821
|
-
paramResult.reason.push(ErrorType.ParamsContainsNestedObject);
|
|
822
|
-
continue;
|
|
823
|
-
}
|
|
824
|
-
const isRequiredWithoutDefault = param.required && schema.default === undefined;
|
|
825
|
-
if (isCopilot) {
|
|
826
|
-
if (isRequiredWithoutDefault) {
|
|
827
|
-
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
828
|
-
}
|
|
829
|
-
else {
|
|
830
|
-
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
831
|
-
}
|
|
832
|
-
continue;
|
|
833
|
-
}
|
|
834
|
-
if (param.in === "header" || param.in === "cookie") {
|
|
835
|
-
if (isRequiredWithoutDefault) {
|
|
836
|
-
paramResult.isValid = false;
|
|
837
|
-
paramResult.reason.push(ErrorType.ParamsContainRequiredUnsupportedSchema);
|
|
838
|
-
}
|
|
839
|
-
continue;
|
|
840
|
-
}
|
|
841
|
-
if (schema.type !== "boolean" &&
|
|
842
|
-
schema.type !== "string" &&
|
|
843
|
-
schema.type !== "number" &&
|
|
844
|
-
schema.type !== "integer") {
|
|
845
|
-
if (isRequiredWithoutDefault) {
|
|
846
|
-
paramResult.isValid = false;
|
|
847
|
-
paramResult.reason.push(ErrorType.ParamsContainRequiredUnsupportedSchema);
|
|
848
|
-
}
|
|
849
|
-
continue;
|
|
850
|
-
}
|
|
851
|
-
if (param.in === "query" || param.in === "path") {
|
|
852
|
-
if (isRequiredWithoutDefault) {
|
|
853
|
-
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
854
|
-
}
|
|
855
|
-
else {
|
|
856
|
-
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
857
|
-
}
|
|
858
|
-
}
|
|
859
|
-
}
|
|
860
|
-
return paramResult;
|
|
861
|
-
}
|
|
862
|
-
hasNestedObjectInSchema(schema) {
|
|
863
|
-
if (schema.type === "object") {
|
|
864
|
-
for (const property in schema.properties) {
|
|
865
|
-
const nestedSchema = schema.properties[property];
|
|
866
|
-
if (nestedSchema.type === "object") {
|
|
867
|
-
return true;
|
|
868
|
-
}
|
|
869
|
-
}
|
|
870
|
-
}
|
|
871
|
-
return false;
|
|
872
|
-
}
|
|
873
736
|
}
|
|
874
737
|
|
|
875
738
|
// Copyright (c) Microsoft Corporation.
|
|
@@ -879,7 +742,6 @@ class CopilotValidator extends Validator {
|
|
|
879
742
|
this.projectType = ProjectType.Copilot;
|
|
880
743
|
this.options = options;
|
|
881
744
|
this.spec = spec;
|
|
882
|
-
this.checkCircularReference();
|
|
883
745
|
}
|
|
884
746
|
validateSpec() {
|
|
885
747
|
const result = { errors: [], warnings: [] };
|
|
@@ -905,10 +767,6 @@ class CopilotValidator extends Validator {
|
|
|
905
767
|
if (!methodAndPathResult.isValid) {
|
|
906
768
|
return methodAndPathResult;
|
|
907
769
|
}
|
|
908
|
-
const circularReferenceResult = this.validateCircularReference(method, path);
|
|
909
|
-
if (!circularReferenceResult.isValid) {
|
|
910
|
-
return circularReferenceResult;
|
|
911
|
-
}
|
|
912
770
|
const operationObject = this.spec.paths[path][method];
|
|
913
771
|
// validate auth
|
|
914
772
|
const authCheckResult = this.validateAuth(method, path);
|
|
@@ -920,24 +778,6 @@ class CopilotValidator extends Validator {
|
|
|
920
778
|
// validate server
|
|
921
779
|
const validateServerResult = this.validateServer(method, path);
|
|
922
780
|
result.reason.push(...validateServerResult.reason);
|
|
923
|
-
// validate response
|
|
924
|
-
const validateResponseResult = this.validateResponse(method, path);
|
|
925
|
-
result.reason.push(...validateResponseResult.reason);
|
|
926
|
-
// validate requestBody
|
|
927
|
-
const requestBody = operationObject.requestBody;
|
|
928
|
-
const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
|
|
929
|
-
if (requestJsonBody) {
|
|
930
|
-
const requestBodySchema = requestJsonBody.schema;
|
|
931
|
-
if (requestBodySchema.type !== "object") {
|
|
932
|
-
result.reason.push(ErrorType.PostBodySchemaIsNotJson);
|
|
933
|
-
}
|
|
934
|
-
const requestBodyParamResult = this.checkPostBodySchema(requestBodySchema, requestBody.required);
|
|
935
|
-
result.reason.push(...requestBodyParamResult.reason);
|
|
936
|
-
}
|
|
937
|
-
// validate parameters
|
|
938
|
-
const paramObject = operationObject.parameters;
|
|
939
|
-
const paramResult = this.checkParamSchema(paramObject);
|
|
940
|
-
result.reason.push(...paramResult.reason);
|
|
941
781
|
if (result.reason.length > 0) {
|
|
942
782
|
result.isValid = false;
|
|
943
783
|
}
|
|
@@ -1029,6 +869,108 @@ class SMEValidator extends Validator {
|
|
|
1029
869
|
}
|
|
1030
870
|
return result;
|
|
1031
871
|
}
|
|
872
|
+
validateResponse(method, path) {
|
|
873
|
+
const result = { isValid: true, reason: [] };
|
|
874
|
+
const operationObject = this.spec.paths[path][method];
|
|
875
|
+
const { json, multipleMediaType } = Utils.getResponseJson(operationObject);
|
|
876
|
+
// only support response body only contains “application/json” content type
|
|
877
|
+
if (multipleMediaType) {
|
|
878
|
+
result.reason.push(ErrorType.ResponseContainMultipleMediaTypes);
|
|
879
|
+
}
|
|
880
|
+
else if (Object.keys(json).length === 0) {
|
|
881
|
+
// response body should not be empty
|
|
882
|
+
result.reason.push(ErrorType.ResponseJsonIsEmpty);
|
|
883
|
+
}
|
|
884
|
+
return result;
|
|
885
|
+
}
|
|
886
|
+
checkPostBodySchema(schema, isRequired = false) {
|
|
887
|
+
var _a;
|
|
888
|
+
const paramResult = {
|
|
889
|
+
requiredNum: 0,
|
|
890
|
+
optionalNum: 0,
|
|
891
|
+
isValid: true,
|
|
892
|
+
reason: [],
|
|
893
|
+
};
|
|
894
|
+
if (Object.keys(schema).length === 0) {
|
|
895
|
+
return paramResult;
|
|
896
|
+
}
|
|
897
|
+
const isRequiredWithoutDefault = isRequired && schema.default === undefined;
|
|
898
|
+
const isCopilot = this.projectType === ProjectType.Copilot;
|
|
899
|
+
if (schema.type === "string" ||
|
|
900
|
+
schema.type === "integer" ||
|
|
901
|
+
schema.type === "boolean" ||
|
|
902
|
+
schema.type === "number") {
|
|
903
|
+
if (isRequiredWithoutDefault) {
|
|
904
|
+
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
905
|
+
}
|
|
906
|
+
else {
|
|
907
|
+
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
else if (Utils.isObjectSchema(schema)) {
|
|
911
|
+
const { properties } = schema;
|
|
912
|
+
for (const property in properties) {
|
|
913
|
+
let isRequired = false;
|
|
914
|
+
if (schema.required && ((_a = schema.required) === null || _a === void 0 ? void 0 : _a.indexOf(property)) >= 0) {
|
|
915
|
+
isRequired = true;
|
|
916
|
+
}
|
|
917
|
+
const result = this.checkPostBodySchema(properties[property], isRequired);
|
|
918
|
+
paramResult.requiredNum += result.requiredNum;
|
|
919
|
+
paramResult.optionalNum += result.optionalNum;
|
|
920
|
+
paramResult.isValid = paramResult.isValid && result.isValid;
|
|
921
|
+
paramResult.reason.push(...result.reason);
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
else {
|
|
925
|
+
if (isRequiredWithoutDefault && !isCopilot) {
|
|
926
|
+
paramResult.isValid = false;
|
|
927
|
+
paramResult.reason.push(ErrorType.PostBodyContainsRequiredUnsupportedSchema);
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
return paramResult;
|
|
931
|
+
}
|
|
932
|
+
checkParamSchema(paramObject) {
|
|
933
|
+
const paramResult = {
|
|
934
|
+
requiredNum: 0,
|
|
935
|
+
optionalNum: 0,
|
|
936
|
+
isValid: true,
|
|
937
|
+
reason: [],
|
|
938
|
+
};
|
|
939
|
+
if (!paramObject) {
|
|
940
|
+
return paramResult;
|
|
941
|
+
}
|
|
942
|
+
for (let i = 0; i < paramObject.length; i++) {
|
|
943
|
+
const param = paramObject[i];
|
|
944
|
+
const schema = param.schema;
|
|
945
|
+
const isRequiredWithoutDefault = param.required && schema.default === undefined;
|
|
946
|
+
if (param.in === "header" || param.in === "cookie") {
|
|
947
|
+
if (isRequiredWithoutDefault) {
|
|
948
|
+
paramResult.isValid = false;
|
|
949
|
+
paramResult.reason.push(ErrorType.ParamsContainRequiredUnsupportedSchema);
|
|
950
|
+
}
|
|
951
|
+
continue;
|
|
952
|
+
}
|
|
953
|
+
if (schema.type !== "boolean" &&
|
|
954
|
+
schema.type !== "string" &&
|
|
955
|
+
schema.type !== "number" &&
|
|
956
|
+
schema.type !== "integer") {
|
|
957
|
+
if (isRequiredWithoutDefault) {
|
|
958
|
+
paramResult.isValid = false;
|
|
959
|
+
paramResult.reason.push(ErrorType.ParamsContainRequiredUnsupportedSchema);
|
|
960
|
+
}
|
|
961
|
+
continue;
|
|
962
|
+
}
|
|
963
|
+
if (param.in === "query" || param.in === "path") {
|
|
964
|
+
if (isRequiredWithoutDefault) {
|
|
965
|
+
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
966
|
+
}
|
|
967
|
+
else {
|
|
968
|
+
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
return paramResult;
|
|
973
|
+
}
|
|
1032
974
|
validateParamCount(postBodyResult, paramResult) {
|
|
1033
975
|
const result = { isValid: true, reason: [] };
|
|
1034
976
|
const totalRequiredParams = postBodyResult.requiredNum + paramResult.requiredNum;
|
|
@@ -1304,20 +1246,157 @@ class SpecParser {
|
|
|
1304
1246
|
}
|
|
1305
1247
|
}
|
|
1306
1248
|
|
|
1249
|
+
// Copyright (c) Microsoft Corporation.
|
|
1250
|
+
class JsonDataGenerator {
|
|
1251
|
+
static generate(schema) {
|
|
1252
|
+
return this.generateMockData(schema);
|
|
1253
|
+
}
|
|
1254
|
+
static generateMockData(schema) {
|
|
1255
|
+
if (this.visitedSchemas.has(schema)) {
|
|
1256
|
+
return null; // Prevent circular reference
|
|
1257
|
+
}
|
|
1258
|
+
this.visitedSchemas.add(schema);
|
|
1259
|
+
let result;
|
|
1260
|
+
if (schema.anyOf) {
|
|
1261
|
+
// Select the first schema in anyOf
|
|
1262
|
+
const selectedSchema = schema.anyOf[0];
|
|
1263
|
+
result = this.generateMockData(selectedSchema);
|
|
1264
|
+
}
|
|
1265
|
+
else if (schema.oneOf) {
|
|
1266
|
+
// Select the first schema in oneOf
|
|
1267
|
+
const selectedSchema = schema.oneOf[0];
|
|
1268
|
+
result = this.generateMockData(selectedSchema);
|
|
1269
|
+
}
|
|
1270
|
+
else if (schema.allOf) {
|
|
1271
|
+
// merge all schemas in allOf
|
|
1272
|
+
result = {};
|
|
1273
|
+
for (const subschema of schema.allOf) {
|
|
1274
|
+
const data = this.generateMockData(subschema);
|
|
1275
|
+
result = Object.assign(Object.assign({}, result), data);
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
1278
|
+
else {
|
|
1279
|
+
switch (schema.type) {
|
|
1280
|
+
case "string":
|
|
1281
|
+
if (schema.example !== undefined) {
|
|
1282
|
+
result = schema.example;
|
|
1283
|
+
}
|
|
1284
|
+
else if (schema.format) {
|
|
1285
|
+
switch (schema.format) {
|
|
1286
|
+
case "date-time":
|
|
1287
|
+
result = "2024-11-01T05:25:43.593Z";
|
|
1288
|
+
break;
|
|
1289
|
+
case "email":
|
|
1290
|
+
result = "example@example.com";
|
|
1291
|
+
break;
|
|
1292
|
+
case "uuid":
|
|
1293
|
+
result = "123e4567-e89b-12d3-a456-426614174000";
|
|
1294
|
+
break;
|
|
1295
|
+
case "ipv4":
|
|
1296
|
+
result = "192.168.0.1";
|
|
1297
|
+
break;
|
|
1298
|
+
case "ipv6":
|
|
1299
|
+
result = "2001:0db8:85a3:0000:0000:8a2e:0370:7334";
|
|
1300
|
+
break;
|
|
1301
|
+
default:
|
|
1302
|
+
result = "example string";
|
|
1303
|
+
}
|
|
1304
|
+
}
|
|
1305
|
+
else {
|
|
1306
|
+
result = "example string";
|
|
1307
|
+
}
|
|
1308
|
+
break;
|
|
1309
|
+
case "number":
|
|
1310
|
+
if (schema.example !== undefined) {
|
|
1311
|
+
result = schema.example;
|
|
1312
|
+
}
|
|
1313
|
+
else if (schema.format) {
|
|
1314
|
+
switch (schema.format) {
|
|
1315
|
+
case "float":
|
|
1316
|
+
result = 3.14;
|
|
1317
|
+
break;
|
|
1318
|
+
case "double":
|
|
1319
|
+
result = 3.14159;
|
|
1320
|
+
break;
|
|
1321
|
+
default:
|
|
1322
|
+
result = 123;
|
|
1323
|
+
}
|
|
1324
|
+
}
|
|
1325
|
+
else {
|
|
1326
|
+
result = 123;
|
|
1327
|
+
}
|
|
1328
|
+
break;
|
|
1329
|
+
case "integer":
|
|
1330
|
+
if (schema.example !== undefined) {
|
|
1331
|
+
result = schema.example;
|
|
1332
|
+
}
|
|
1333
|
+
else if (schema.format) {
|
|
1334
|
+
switch (schema.format) {
|
|
1335
|
+
case "int32":
|
|
1336
|
+
result = 123456;
|
|
1337
|
+
break;
|
|
1338
|
+
case "int64":
|
|
1339
|
+
result = 123456789;
|
|
1340
|
+
break;
|
|
1341
|
+
default:
|
|
1342
|
+
result = 123;
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1345
|
+
else {
|
|
1346
|
+
result = 123;
|
|
1347
|
+
}
|
|
1348
|
+
break;
|
|
1349
|
+
case "boolean":
|
|
1350
|
+
result = schema.example !== undefined ? schema.example : true;
|
|
1351
|
+
break;
|
|
1352
|
+
case "array":
|
|
1353
|
+
result = [this.generateMockData(schema.items)];
|
|
1354
|
+
break;
|
|
1355
|
+
case "object":
|
|
1356
|
+
result = {};
|
|
1357
|
+
if (schema.properties) {
|
|
1358
|
+
for (const key in schema.properties) {
|
|
1359
|
+
result[key] = this.generateMockData(schema.properties[key]);
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
break;
|
|
1363
|
+
default:
|
|
1364
|
+
result = schema.example || null;
|
|
1365
|
+
}
|
|
1366
|
+
}
|
|
1367
|
+
this.visitedSchemas.delete(schema);
|
|
1368
|
+
return result;
|
|
1369
|
+
}
|
|
1370
|
+
}
|
|
1371
|
+
JsonDataGenerator.visitedSchemas = new Set();
|
|
1372
|
+
|
|
1307
1373
|
// Copyright (c) Microsoft Corporation.
|
|
1308
1374
|
class AdaptiveCardGenerator {
|
|
1309
|
-
static generateAdaptiveCard(operationItem, allowMultipleMediaType = false) {
|
|
1375
|
+
static generateAdaptiveCard(operationItem, allowMultipleMediaType = false, maxElementCount = Number.MAX_SAFE_INTEGER) {
|
|
1310
1376
|
try {
|
|
1311
1377
|
const { json } = Utils.getResponseJson(operationItem, allowMultipleMediaType);
|
|
1312
1378
|
let cardBody = [];
|
|
1379
|
+
let jsonData = {};
|
|
1380
|
+
const warnings = [];
|
|
1381
|
+
const operationId = operationItem.operationId;
|
|
1313
1382
|
let schema = json.schema;
|
|
1314
1383
|
let jsonPath = "$";
|
|
1315
1384
|
if (schema && Object.keys(schema).length > 0) {
|
|
1385
|
+
try {
|
|
1386
|
+
jsonData = JsonDataGenerator.generate(schema);
|
|
1387
|
+
}
|
|
1388
|
+
catch (err) {
|
|
1389
|
+
warnings.push({
|
|
1390
|
+
type: WarningType.GenerateJsonDataFailed,
|
|
1391
|
+
content: Utils.format(ConstantString.GenerateJsonDataFailed, operationId, err.toString()),
|
|
1392
|
+
data: operationId,
|
|
1393
|
+
});
|
|
1394
|
+
}
|
|
1316
1395
|
jsonPath = AdaptiveCardGenerator.getResponseJsonPathFromSchema(schema);
|
|
1317
1396
|
if (jsonPath !== "$") {
|
|
1318
1397
|
schema = schema.properties[jsonPath];
|
|
1319
1398
|
}
|
|
1320
|
-
cardBody = AdaptiveCardGenerator.generateCardFromResponse(schema, "");
|
|
1399
|
+
cardBody = AdaptiveCardGenerator.generateCardFromResponse(schema, "", "", maxElementCount);
|
|
1321
1400
|
}
|
|
1322
1401
|
// if no schema, try to use example value
|
|
1323
1402
|
if (cardBody.length === 0 && (json.examples || json.example)) {
|
|
@@ -1345,16 +1424,20 @@ class AdaptiveCardGenerator {
|
|
|
1345
1424
|
version: ConstantString.AdaptiveCardVersion,
|
|
1346
1425
|
body: cardBody,
|
|
1347
1426
|
};
|
|
1348
|
-
return [fullCard, jsonPath];
|
|
1427
|
+
return [fullCard, jsonPath, jsonData, warnings];
|
|
1349
1428
|
}
|
|
1350
1429
|
catch (err) {
|
|
1351
1430
|
throw new SpecParserError(err.toString(), ErrorType.GenerateAdaptiveCardFailed);
|
|
1352
1431
|
}
|
|
1353
1432
|
}
|
|
1354
|
-
static generateCardFromResponse(schema, name, parentArrayName = "") {
|
|
1433
|
+
static generateCardFromResponse(schema, name, parentArrayName = "", maxElementCount = Number.MAX_SAFE_INTEGER, counter = { count: 0 }) {
|
|
1434
|
+
if (counter.count >= maxElementCount) {
|
|
1435
|
+
return [];
|
|
1436
|
+
}
|
|
1355
1437
|
if (schema.type === "array") {
|
|
1356
1438
|
// schema.items can be arbitrary object: schema { type: array, items: {} }
|
|
1357
1439
|
if (Object.keys(schema.items).length === 0) {
|
|
1440
|
+
counter.count++;
|
|
1358
1441
|
return [
|
|
1359
1442
|
{
|
|
1360
1443
|
type: ConstantString.TextBlockType,
|
|
@@ -1363,7 +1446,7 @@ class AdaptiveCardGenerator {
|
|
|
1363
1446
|
},
|
|
1364
1447
|
];
|
|
1365
1448
|
}
|
|
1366
|
-
const obj = AdaptiveCardGenerator.generateCardFromResponse(schema.items, "", name);
|
|
1449
|
+
const obj = AdaptiveCardGenerator.generateCardFromResponse(schema.items, "", name, maxElementCount, counter);
|
|
1367
1450
|
const template = {
|
|
1368
1451
|
type: ConstantString.ContainerType,
|
|
1369
1452
|
$data: name ? `\${${name}}` : "${$root}",
|
|
@@ -1373,11 +1456,11 @@ class AdaptiveCardGenerator {
|
|
|
1373
1456
|
return [template];
|
|
1374
1457
|
}
|
|
1375
1458
|
// some schema may not contain type but contain properties
|
|
1376
|
-
if (
|
|
1459
|
+
if (Utils.isObjectSchema(schema)) {
|
|
1377
1460
|
const { properties } = schema;
|
|
1378
1461
|
const result = [];
|
|
1379
1462
|
for (const property in properties) {
|
|
1380
|
-
const obj = AdaptiveCardGenerator.generateCardFromResponse(properties[property], name ? `${name}.${property}` : property, parentArrayName);
|
|
1463
|
+
const obj = AdaptiveCardGenerator.generateCardFromResponse(properties[property], name ? `${name}.${property}` : property, parentArrayName, maxElementCount, counter);
|
|
1381
1464
|
result.push(...obj);
|
|
1382
1465
|
}
|
|
1383
1466
|
if (schema.additionalProperties) {
|
|
@@ -1390,6 +1473,7 @@ class AdaptiveCardGenerator {
|
|
|
1390
1473
|
schema.type === "integer" ||
|
|
1391
1474
|
schema.type === "boolean" ||
|
|
1392
1475
|
schema.type === "number") {
|
|
1476
|
+
counter.count++;
|
|
1393
1477
|
if (!AdaptiveCardGenerator.isImageUrlProperty(schema, name, parentArrayName)) {
|
|
1394
1478
|
// string in root: "ddd"
|
|
1395
1479
|
let text = "result: ${$root}";
|
|
@@ -1414,24 +1498,17 @@ class AdaptiveCardGenerator {
|
|
|
1414
1498
|
];
|
|
1415
1499
|
}
|
|
1416
1500
|
else {
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
{
|
|
1429
|
-
type: "Image",
|
|
1430
|
-
url: "${$data}",
|
|
1431
|
-
$when: "${$data != null && $data != ''}",
|
|
1432
|
-
},
|
|
1433
|
-
];
|
|
1434
|
-
}
|
|
1501
|
+
const url = name ? `\${${name}}` : "${$data}";
|
|
1502
|
+
const condition = name
|
|
1503
|
+
? `\${${name} != null && ${name} != ''}`
|
|
1504
|
+
: "${$data != null && $data != ''}";
|
|
1505
|
+
return [
|
|
1506
|
+
{
|
|
1507
|
+
type: "Image",
|
|
1508
|
+
url,
|
|
1509
|
+
$when: condition,
|
|
1510
|
+
},
|
|
1511
|
+
];
|
|
1435
1512
|
}
|
|
1436
1513
|
}
|
|
1437
1514
|
if (schema.oneOf || schema.anyOf || schema.not || schema.allOf) {
|
|
@@ -1441,7 +1518,7 @@ class AdaptiveCardGenerator {
|
|
|
1441
1518
|
}
|
|
1442
1519
|
// Find the first array property in the response schema object with the well-known name
|
|
1443
1520
|
static getResponseJsonPathFromSchema(schema) {
|
|
1444
|
-
if (
|
|
1521
|
+
if (Utils.isObjectSchema(schema)) {
|
|
1445
1522
|
const { properties } = schema;
|
|
1446
1523
|
for (const property in properties) {
|
|
1447
1524
|
const schema = properties[property];
|