@microsoft/m365-spec-parser 0.2.3 → 0.2.4-alpha.24cc1dfce.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 +344 -253
- package/dist/index.esm2017.js.map +1 -1
- package/dist/index.esm2017.mjs +481 -332
- package/dist/index.esm2017.mjs.map +1 -1
- package/dist/index.esm5.js +344 -253
- package/dist/index.esm5.js.map +1 -1
- package/dist/index.node.cjs.js +484 -333
- package/dist/index.node.cjs.js.map +1 -1
- package/dist/src/adaptiveCardGenerator.d.ts +5 -3
- package/dist/src/constants.d.ts +5 -4
- package/dist/src/interfaces.d.ts +12 -3
- package/dist/src/jsonDataGenerator.d.ts +6 -0
- package/dist/src/manifestUpdater.d.ts +3 -3
- package/dist/src/specParser.d.ts +2 -0
- package/dist/src/utils.d.ts +7 -4
- package/package.json +4 -4
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,25 +100,23 @@ 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";
|
|
115
118
|
ConstantString.ContainerType = "Container";
|
|
116
|
-
ConstantString.RegistrationIdPostfix =
|
|
117
|
-
apiKey: "REGISTRATION_ID",
|
|
118
|
-
oauth2: "CONFIGURATION_ID",
|
|
119
|
-
http: "REGISTRATION_ID",
|
|
120
|
-
openIdConnect: "REGISTRATION_ID",
|
|
121
|
-
};
|
|
119
|
+
ConstantString.RegistrationIdPostfix = "REGISTRATION_ID";
|
|
122
120
|
ConstantString.ResponseCodeFor20X = [
|
|
123
121
|
"200",
|
|
124
122
|
"201",
|
|
@@ -130,6 +128,7 @@ ConstantString.ResponseCodeFor20X = [
|
|
|
130
128
|
"207",
|
|
131
129
|
"208",
|
|
132
130
|
"226",
|
|
131
|
+
"2XX",
|
|
133
132
|
"default",
|
|
134
133
|
];
|
|
135
134
|
ConstantString.AllOperationMethods = [
|
|
@@ -187,16 +186,8 @@ ConstantString.PluginManifestSchema = "https://developer.microsoft.com/json-sche
|
|
|
187
186
|
|
|
188
187
|
// Copyright (c) Microsoft Corporation.
|
|
189
188
|
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;
|
|
189
|
+
static isObjectSchema(schema) {
|
|
190
|
+
return schema.type === "object" || (!schema.type && !!schema.properties);
|
|
200
191
|
}
|
|
201
192
|
static containMultipleMediaTypes(bodyObject) {
|
|
202
193
|
return Object.keys((bodyObject === null || bodyObject === void 0 ? void 0 : bodyObject.content) || {}).length > 1;
|
|
@@ -207,11 +198,32 @@ class Utils {
|
|
|
207
198
|
static isAPIKeyAuth(authScheme) {
|
|
208
199
|
return authScheme.type === "apiKey";
|
|
209
200
|
}
|
|
201
|
+
static isAPIKeyAuthButNotInCookie(authScheme) {
|
|
202
|
+
return authScheme.type === "apiKey" && authScheme.in !== "cookie";
|
|
203
|
+
}
|
|
210
204
|
static isOAuthWithAuthCodeFlow(authScheme) {
|
|
211
205
|
return !!(authScheme.type === "oauth2" &&
|
|
212
206
|
authScheme.flows &&
|
|
213
207
|
authScheme.flows.authorizationCode);
|
|
214
208
|
}
|
|
209
|
+
static isNotSupportedAuth(authSchemeArray) {
|
|
210
|
+
if (authSchemeArray.length === 0) {
|
|
211
|
+
return false;
|
|
212
|
+
}
|
|
213
|
+
if (authSchemeArray.length > 0 && authSchemeArray.every((auths) => auths.length > 1)) {
|
|
214
|
+
return true;
|
|
215
|
+
}
|
|
216
|
+
for (const auths of authSchemeArray) {
|
|
217
|
+
if (auths.length === 1) {
|
|
218
|
+
if (Utils.isOAuthWithAuthCodeFlow(auths[0].authScheme) ||
|
|
219
|
+
Utils.isBearerTokenAuth(auths[0].authScheme) ||
|
|
220
|
+
Utils.isAPIKeyAuthButNotInCookie(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 = [];
|
|
@@ -236,6 +248,20 @@ class Utils {
|
|
|
236
248
|
result.sort((a, b) => a[0].name.localeCompare(b[0].name));
|
|
237
249
|
return result;
|
|
238
250
|
}
|
|
251
|
+
static getAuthMap(spec) {
|
|
252
|
+
const authMap = {};
|
|
253
|
+
for (const url in spec.paths) {
|
|
254
|
+
for (const method in spec.paths[url]) {
|
|
255
|
+
const operation = spec.paths[url][method];
|
|
256
|
+
const authArray = Utils.getAuthArray(operation.security, spec);
|
|
257
|
+
if (authArray && authArray.length > 0) {
|
|
258
|
+
const currentAuth = authArray[0][0];
|
|
259
|
+
authMap[operation.operationId] = currentAuth;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
return authMap;
|
|
264
|
+
}
|
|
239
265
|
static getAuthInfo(spec) {
|
|
240
266
|
let authInfo = undefined;
|
|
241
267
|
for (const url in spec.paths) {
|
|
@@ -264,27 +290,33 @@ class Utils {
|
|
|
264
290
|
let multipleMediaType = false;
|
|
265
291
|
for (const code of ConstantString.ResponseCodeFor20X) {
|
|
266
292
|
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
|
-
}
|
|
293
|
+
if (!responseObject) {
|
|
294
|
+
continue;
|
|
295
|
+
}
|
|
296
|
+
multipleMediaType = Utils.containMultipleMediaTypes(responseObject);
|
|
297
|
+
if (!allowMultipleMediaType && multipleMediaType) {
|
|
298
|
+
json = {};
|
|
299
|
+
continue;
|
|
300
|
+
}
|
|
301
|
+
const mediaObj = Utils.getJsonContentType(responseObject);
|
|
302
|
+
if (Object.keys(mediaObj).length > 0) {
|
|
303
|
+
json = mediaObj;
|
|
304
|
+
return { json, multipleMediaType };
|
|
284
305
|
}
|
|
285
306
|
}
|
|
286
307
|
return { json, multipleMediaType };
|
|
287
308
|
}
|
|
309
|
+
static getJsonContentType(responseObject) {
|
|
310
|
+
if (responseObject.content) {
|
|
311
|
+
for (const contentType of Object.keys(responseObject.content)) {
|
|
312
|
+
// json media type can also be "application/json; charset=utf-8"
|
|
313
|
+
if (contentType.indexOf("application/json") >= 0) {
|
|
314
|
+
return responseObject.content[contentType];
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
return {};
|
|
319
|
+
}
|
|
288
320
|
static convertPathToCamelCase(path) {
|
|
289
321
|
const pathSegments = path.split(/[./{]/);
|
|
290
322
|
const camelCaseSegments = pathSegments.map((segment) => {
|
|
@@ -320,7 +352,7 @@ class Utils {
|
|
|
320
352
|
}
|
|
321
353
|
return newStr;
|
|
322
354
|
}
|
|
323
|
-
static checkServerUrl(servers) {
|
|
355
|
+
static checkServerUrl(servers, allowHttp = false) {
|
|
324
356
|
const errors = [];
|
|
325
357
|
let serverUrl;
|
|
326
358
|
try {
|
|
@@ -343,8 +375,7 @@ class Utils {
|
|
|
343
375
|
data: servers,
|
|
344
376
|
});
|
|
345
377
|
}
|
|
346
|
-
else if (protocol !== "https:") {
|
|
347
|
-
// Http server url is not supported
|
|
378
|
+
else if (protocol !== "https:" && !(protocol === "http:" && allowHttp)) {
|
|
348
379
|
const protocolString = protocol.slice(0, -1);
|
|
349
380
|
errors.push({
|
|
350
381
|
type: ErrorType.UrlProtocolNotSupported,
|
|
@@ -360,10 +391,11 @@ class Utils {
|
|
|
360
391
|
let hasTopLevelServers = false;
|
|
361
392
|
let hasPathLevelServers = false;
|
|
362
393
|
let hasOperationLevelServers = false;
|
|
394
|
+
const allowHttp = options.projectType === ProjectType.Copilot;
|
|
363
395
|
if (spec.servers && spec.servers.length >= 1) {
|
|
364
396
|
hasTopLevelServers = true;
|
|
365
397
|
// for multiple server, we only use the first url
|
|
366
|
-
const serverErrors = Utils.checkServerUrl(spec.servers);
|
|
398
|
+
const serverErrors = Utils.checkServerUrl(spec.servers, allowHttp);
|
|
367
399
|
errors.push(...serverErrors);
|
|
368
400
|
}
|
|
369
401
|
const paths = spec.paths;
|
|
@@ -371,7 +403,7 @@ class Utils {
|
|
|
371
403
|
const methods = paths[path];
|
|
372
404
|
if ((methods === null || methods === void 0 ? void 0 : methods.servers) && methods.servers.length >= 1) {
|
|
373
405
|
hasPathLevelServers = true;
|
|
374
|
-
const serverErrors = Utils.checkServerUrl(methods.servers);
|
|
406
|
+
const serverErrors = Utils.checkServerUrl(methods.servers, allowHttp);
|
|
375
407
|
errors.push(...serverErrors);
|
|
376
408
|
}
|
|
377
409
|
for (const method in methods) {
|
|
@@ -379,7 +411,7 @@ class Utils {
|
|
|
379
411
|
if (((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && operationObject) {
|
|
380
412
|
if ((operationObject === null || operationObject === void 0 ? void 0 : operationObject.servers) && operationObject.servers.length >= 1) {
|
|
381
413
|
hasOperationLevelServers = true;
|
|
382
|
-
const serverErrors = Utils.checkServerUrl(operationObject.servers);
|
|
414
|
+
const serverErrors = Utils.checkServerUrl(operationObject.servers, allowHttp);
|
|
383
415
|
errors.push(...serverErrors);
|
|
384
416
|
}
|
|
385
417
|
}
|
|
@@ -426,7 +458,7 @@ class Utils {
|
|
|
426
458
|
optionalParams.push(parameter);
|
|
427
459
|
}
|
|
428
460
|
}
|
|
429
|
-
else if (schema
|
|
461
|
+
else if (Utils.isObjectSchema(schema)) {
|
|
430
462
|
const { properties } = schema;
|
|
431
463
|
for (const property in properties) {
|
|
432
464
|
let isRequired = false;
|
|
@@ -540,29 +572,6 @@ class Utils {
|
|
|
540
572
|
const serverUrl = operationServer || methodServer || rootServer;
|
|
541
573
|
return serverUrl;
|
|
542
574
|
}
|
|
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
575
|
}
|
|
567
576
|
|
|
568
577
|
// Copyright (c) Microsoft Corporation.
|
|
@@ -691,22 +700,6 @@ class Validator {
|
|
|
691
700
|
}
|
|
692
701
|
return result;
|
|
693
702
|
}
|
|
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
703
|
validateServer(method, path) {
|
|
711
704
|
const result = { isValid: true, reason: [] };
|
|
712
705
|
const serverObj = Utils.getServerObject(this.spec, method, path);
|
|
@@ -715,8 +708,8 @@ class Validator {
|
|
|
715
708
|
result.reason.push(ErrorType.NoServerInformation);
|
|
716
709
|
}
|
|
717
710
|
else {
|
|
718
|
-
|
|
719
|
-
const serverValidateResult = Utils.checkServerUrl([serverObj]);
|
|
711
|
+
const allowHttp = this.projectType === ProjectType.Copilot;
|
|
712
|
+
const serverValidateResult = Utils.checkServerUrl([serverObj], allowHttp);
|
|
720
713
|
result.reason.push(...serverValidateResult.map((item) => item.type));
|
|
721
714
|
}
|
|
722
715
|
return result;
|
|
@@ -739,6 +732,9 @@ class Validator {
|
|
|
739
732
|
reason: [ErrorType.MultipleAuthNotSupported],
|
|
740
733
|
};
|
|
741
734
|
}
|
|
735
|
+
if (this.projectType === ProjectType.Copilot) {
|
|
736
|
+
return { isValid: true, reason: [] };
|
|
737
|
+
}
|
|
742
738
|
for (const auths of authSchemeArray) {
|
|
743
739
|
if (auths.length === 1) {
|
|
744
740
|
if ((this.options.allowAPIKeyAuth && Utils.isAPIKeyAuth(auths[0].authScheme)) ||
|
|
@@ -751,125 +747,6 @@ class Validator {
|
|
|
751
747
|
}
|
|
752
748
|
return { isValid: false, reason: [ErrorType.AuthTypeIsNotSupported] };
|
|
753
749
|
}
|
|
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
750
|
}
|
|
874
751
|
|
|
875
752
|
// Copyright (c) Microsoft Corporation.
|
|
@@ -879,7 +756,6 @@ class CopilotValidator extends Validator {
|
|
|
879
756
|
this.projectType = ProjectType.Copilot;
|
|
880
757
|
this.options = options;
|
|
881
758
|
this.spec = spec;
|
|
882
|
-
this.checkCircularReference();
|
|
883
759
|
}
|
|
884
760
|
validateSpec() {
|
|
885
761
|
const result = { errors: [], warnings: [] };
|
|
@@ -905,10 +781,6 @@ class CopilotValidator extends Validator {
|
|
|
905
781
|
if (!methodAndPathResult.isValid) {
|
|
906
782
|
return methodAndPathResult;
|
|
907
783
|
}
|
|
908
|
-
const circularReferenceResult = this.validateCircularReference(method, path);
|
|
909
|
-
if (!circularReferenceResult.isValid) {
|
|
910
|
-
return circularReferenceResult;
|
|
911
|
-
}
|
|
912
784
|
const operationObject = this.spec.paths[path][method];
|
|
913
785
|
// validate auth
|
|
914
786
|
const authCheckResult = this.validateAuth(method, path);
|
|
@@ -920,24 +792,6 @@ class CopilotValidator extends Validator {
|
|
|
920
792
|
// validate server
|
|
921
793
|
const validateServerResult = this.validateServer(method, path);
|
|
922
794
|
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
795
|
if (result.reason.length > 0) {
|
|
942
796
|
result.isValid = false;
|
|
943
797
|
}
|
|
@@ -1029,6 +883,108 @@ class SMEValidator extends Validator {
|
|
|
1029
883
|
}
|
|
1030
884
|
return result;
|
|
1031
885
|
}
|
|
886
|
+
validateResponse(method, path) {
|
|
887
|
+
const result = { isValid: true, reason: [] };
|
|
888
|
+
const operationObject = this.spec.paths[path][method];
|
|
889
|
+
const { json, multipleMediaType } = Utils.getResponseJson(operationObject);
|
|
890
|
+
// only support response body only contains “application/json” content type
|
|
891
|
+
if (multipleMediaType) {
|
|
892
|
+
result.reason.push(ErrorType.ResponseContainMultipleMediaTypes);
|
|
893
|
+
}
|
|
894
|
+
else if (Object.keys(json).length === 0) {
|
|
895
|
+
// response body should not be empty
|
|
896
|
+
result.reason.push(ErrorType.ResponseJsonIsEmpty);
|
|
897
|
+
}
|
|
898
|
+
return result;
|
|
899
|
+
}
|
|
900
|
+
checkPostBodySchema(schema, isRequired = false) {
|
|
901
|
+
var _a;
|
|
902
|
+
const paramResult = {
|
|
903
|
+
requiredNum: 0,
|
|
904
|
+
optionalNum: 0,
|
|
905
|
+
isValid: true,
|
|
906
|
+
reason: [],
|
|
907
|
+
};
|
|
908
|
+
if (Object.keys(schema).length === 0) {
|
|
909
|
+
return paramResult;
|
|
910
|
+
}
|
|
911
|
+
const isRequiredWithoutDefault = isRequired && schema.default === undefined;
|
|
912
|
+
const isCopilot = this.projectType === ProjectType.Copilot;
|
|
913
|
+
if (schema.type === "string" ||
|
|
914
|
+
schema.type === "integer" ||
|
|
915
|
+
schema.type === "boolean" ||
|
|
916
|
+
schema.type === "number") {
|
|
917
|
+
if (isRequiredWithoutDefault) {
|
|
918
|
+
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
919
|
+
}
|
|
920
|
+
else {
|
|
921
|
+
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
else if (Utils.isObjectSchema(schema)) {
|
|
925
|
+
const { properties } = schema;
|
|
926
|
+
for (const property in properties) {
|
|
927
|
+
let isRequired = false;
|
|
928
|
+
if (schema.required && ((_a = schema.required) === null || _a === void 0 ? void 0 : _a.indexOf(property)) >= 0) {
|
|
929
|
+
isRequired = true;
|
|
930
|
+
}
|
|
931
|
+
const result = this.checkPostBodySchema(properties[property], isRequired);
|
|
932
|
+
paramResult.requiredNum += result.requiredNum;
|
|
933
|
+
paramResult.optionalNum += result.optionalNum;
|
|
934
|
+
paramResult.isValid = paramResult.isValid && result.isValid;
|
|
935
|
+
paramResult.reason.push(...result.reason);
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
else {
|
|
939
|
+
if (isRequiredWithoutDefault && !isCopilot) {
|
|
940
|
+
paramResult.isValid = false;
|
|
941
|
+
paramResult.reason.push(ErrorType.PostBodyContainsRequiredUnsupportedSchema);
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
return paramResult;
|
|
945
|
+
}
|
|
946
|
+
checkParamSchema(paramObject) {
|
|
947
|
+
const paramResult = {
|
|
948
|
+
requiredNum: 0,
|
|
949
|
+
optionalNum: 0,
|
|
950
|
+
isValid: true,
|
|
951
|
+
reason: [],
|
|
952
|
+
};
|
|
953
|
+
if (!paramObject) {
|
|
954
|
+
return paramResult;
|
|
955
|
+
}
|
|
956
|
+
for (let i = 0; i < paramObject.length; i++) {
|
|
957
|
+
const param = paramObject[i];
|
|
958
|
+
const schema = param.schema;
|
|
959
|
+
const isRequiredWithoutDefault = param.required && schema.default === undefined;
|
|
960
|
+
if (param.in === "header" || param.in === "cookie") {
|
|
961
|
+
if (isRequiredWithoutDefault) {
|
|
962
|
+
paramResult.isValid = false;
|
|
963
|
+
paramResult.reason.push(ErrorType.ParamsContainRequiredUnsupportedSchema);
|
|
964
|
+
}
|
|
965
|
+
continue;
|
|
966
|
+
}
|
|
967
|
+
if (schema.type !== "boolean" &&
|
|
968
|
+
schema.type !== "string" &&
|
|
969
|
+
schema.type !== "number" &&
|
|
970
|
+
schema.type !== "integer") {
|
|
971
|
+
if (isRequiredWithoutDefault) {
|
|
972
|
+
paramResult.isValid = false;
|
|
973
|
+
paramResult.reason.push(ErrorType.ParamsContainRequiredUnsupportedSchema);
|
|
974
|
+
}
|
|
975
|
+
continue;
|
|
976
|
+
}
|
|
977
|
+
if (param.in === "query" || param.in === "path") {
|
|
978
|
+
if (isRequiredWithoutDefault) {
|
|
979
|
+
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
980
|
+
}
|
|
981
|
+
else {
|
|
982
|
+
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
return paramResult;
|
|
987
|
+
}
|
|
1032
988
|
validateParamCount(postBodyResult, paramResult) {
|
|
1033
989
|
const result = { isValid: true, reason: [] };
|
|
1034
990
|
const totalRequiredParams = postBodyResult.requiredNum + paramResult.requiredNum;
|
|
@@ -1304,20 +1260,157 @@ class SpecParser {
|
|
|
1304
1260
|
}
|
|
1305
1261
|
}
|
|
1306
1262
|
|
|
1263
|
+
// Copyright (c) Microsoft Corporation.
|
|
1264
|
+
class JsonDataGenerator {
|
|
1265
|
+
static generate(schema) {
|
|
1266
|
+
return this.generateMockData(schema);
|
|
1267
|
+
}
|
|
1268
|
+
static generateMockData(schema) {
|
|
1269
|
+
if (this.visitedSchemas.has(schema)) {
|
|
1270
|
+
return null; // Prevent circular reference
|
|
1271
|
+
}
|
|
1272
|
+
this.visitedSchemas.add(schema);
|
|
1273
|
+
let result;
|
|
1274
|
+
if (schema.anyOf) {
|
|
1275
|
+
// Select the first schema in anyOf
|
|
1276
|
+
const selectedSchema = schema.anyOf[0];
|
|
1277
|
+
result = this.generateMockData(selectedSchema);
|
|
1278
|
+
}
|
|
1279
|
+
else if (schema.oneOf) {
|
|
1280
|
+
// Select the first schema in oneOf
|
|
1281
|
+
const selectedSchema = schema.oneOf[0];
|
|
1282
|
+
result = this.generateMockData(selectedSchema);
|
|
1283
|
+
}
|
|
1284
|
+
else if (schema.allOf) {
|
|
1285
|
+
// merge all schemas in allOf
|
|
1286
|
+
result = {};
|
|
1287
|
+
for (const subschema of schema.allOf) {
|
|
1288
|
+
const data = this.generateMockData(subschema);
|
|
1289
|
+
result = Object.assign(Object.assign({}, result), data);
|
|
1290
|
+
}
|
|
1291
|
+
}
|
|
1292
|
+
else {
|
|
1293
|
+
switch (schema.type) {
|
|
1294
|
+
case "string":
|
|
1295
|
+
if (schema.example !== undefined) {
|
|
1296
|
+
result = schema.example;
|
|
1297
|
+
}
|
|
1298
|
+
else if (schema.format) {
|
|
1299
|
+
switch (schema.format) {
|
|
1300
|
+
case "date-time":
|
|
1301
|
+
result = "2024-11-01T05:25:43.593Z";
|
|
1302
|
+
break;
|
|
1303
|
+
case "email":
|
|
1304
|
+
result = "example@example.com";
|
|
1305
|
+
break;
|
|
1306
|
+
case "uuid":
|
|
1307
|
+
result = "123e4567-e89b-12d3-a456-426614174000";
|
|
1308
|
+
break;
|
|
1309
|
+
case "ipv4":
|
|
1310
|
+
result = "192.168.0.1";
|
|
1311
|
+
break;
|
|
1312
|
+
case "ipv6":
|
|
1313
|
+
result = "2001:0db8:85a3:0000:0000:8a2e:0370:7334";
|
|
1314
|
+
break;
|
|
1315
|
+
default:
|
|
1316
|
+
result = "example string";
|
|
1317
|
+
}
|
|
1318
|
+
}
|
|
1319
|
+
else {
|
|
1320
|
+
result = "example string";
|
|
1321
|
+
}
|
|
1322
|
+
break;
|
|
1323
|
+
case "number":
|
|
1324
|
+
if (schema.example !== undefined) {
|
|
1325
|
+
result = schema.example;
|
|
1326
|
+
}
|
|
1327
|
+
else if (schema.format) {
|
|
1328
|
+
switch (schema.format) {
|
|
1329
|
+
case "float":
|
|
1330
|
+
result = 3.14;
|
|
1331
|
+
break;
|
|
1332
|
+
case "double":
|
|
1333
|
+
result = 3.14159;
|
|
1334
|
+
break;
|
|
1335
|
+
default:
|
|
1336
|
+
result = 123;
|
|
1337
|
+
}
|
|
1338
|
+
}
|
|
1339
|
+
else {
|
|
1340
|
+
result = 123;
|
|
1341
|
+
}
|
|
1342
|
+
break;
|
|
1343
|
+
case "integer":
|
|
1344
|
+
if (schema.example !== undefined) {
|
|
1345
|
+
result = schema.example;
|
|
1346
|
+
}
|
|
1347
|
+
else if (schema.format) {
|
|
1348
|
+
switch (schema.format) {
|
|
1349
|
+
case "int32":
|
|
1350
|
+
result = 123456;
|
|
1351
|
+
break;
|
|
1352
|
+
case "int64":
|
|
1353
|
+
result = 123456789;
|
|
1354
|
+
break;
|
|
1355
|
+
default:
|
|
1356
|
+
result = 123;
|
|
1357
|
+
}
|
|
1358
|
+
}
|
|
1359
|
+
else {
|
|
1360
|
+
result = 123;
|
|
1361
|
+
}
|
|
1362
|
+
break;
|
|
1363
|
+
case "boolean":
|
|
1364
|
+
result = schema.example !== undefined ? schema.example : true;
|
|
1365
|
+
break;
|
|
1366
|
+
case "array":
|
|
1367
|
+
result = [this.generateMockData(schema.items)];
|
|
1368
|
+
break;
|
|
1369
|
+
case "object":
|
|
1370
|
+
result = {};
|
|
1371
|
+
if (schema.properties) {
|
|
1372
|
+
for (const key in schema.properties) {
|
|
1373
|
+
result[key] = this.generateMockData(schema.properties[key]);
|
|
1374
|
+
}
|
|
1375
|
+
}
|
|
1376
|
+
break;
|
|
1377
|
+
default:
|
|
1378
|
+
result = schema.example || null;
|
|
1379
|
+
}
|
|
1380
|
+
}
|
|
1381
|
+
this.visitedSchemas.delete(schema);
|
|
1382
|
+
return result;
|
|
1383
|
+
}
|
|
1384
|
+
}
|
|
1385
|
+
JsonDataGenerator.visitedSchemas = new Set();
|
|
1386
|
+
|
|
1307
1387
|
// Copyright (c) Microsoft Corporation.
|
|
1308
1388
|
class AdaptiveCardGenerator {
|
|
1309
|
-
static generateAdaptiveCard(operationItem, allowMultipleMediaType = false) {
|
|
1389
|
+
static generateAdaptiveCard(operationItem, allowMultipleMediaType = false, maxElementCount = Number.MAX_SAFE_INTEGER) {
|
|
1310
1390
|
try {
|
|
1311
1391
|
const { json } = Utils.getResponseJson(operationItem, allowMultipleMediaType);
|
|
1312
1392
|
let cardBody = [];
|
|
1393
|
+
let jsonData = {};
|
|
1394
|
+
const warnings = [];
|
|
1395
|
+
const operationId = operationItem.operationId;
|
|
1313
1396
|
let schema = json.schema;
|
|
1314
1397
|
let jsonPath = "$";
|
|
1315
1398
|
if (schema && Object.keys(schema).length > 0) {
|
|
1399
|
+
try {
|
|
1400
|
+
jsonData = JsonDataGenerator.generate(schema);
|
|
1401
|
+
}
|
|
1402
|
+
catch (err) {
|
|
1403
|
+
warnings.push({
|
|
1404
|
+
type: WarningType.GenerateJsonDataFailed,
|
|
1405
|
+
content: Utils.format(ConstantString.GenerateJsonDataFailed, operationId, err.toString()),
|
|
1406
|
+
data: operationId,
|
|
1407
|
+
});
|
|
1408
|
+
}
|
|
1316
1409
|
jsonPath = AdaptiveCardGenerator.getResponseJsonPathFromSchema(schema);
|
|
1317
1410
|
if (jsonPath !== "$") {
|
|
1318
1411
|
schema = schema.properties[jsonPath];
|
|
1319
1412
|
}
|
|
1320
|
-
cardBody = AdaptiveCardGenerator.generateCardFromResponse(schema, "");
|
|
1413
|
+
cardBody = AdaptiveCardGenerator.generateCardFromResponse(schema, "", "", maxElementCount);
|
|
1321
1414
|
}
|
|
1322
1415
|
// if no schema, try to use example value
|
|
1323
1416
|
if (cardBody.length === 0 && (json.examples || json.example)) {
|
|
@@ -1345,16 +1438,20 @@ class AdaptiveCardGenerator {
|
|
|
1345
1438
|
version: ConstantString.AdaptiveCardVersion,
|
|
1346
1439
|
body: cardBody,
|
|
1347
1440
|
};
|
|
1348
|
-
return [fullCard, jsonPath];
|
|
1441
|
+
return [fullCard, jsonPath, jsonData, warnings];
|
|
1349
1442
|
}
|
|
1350
1443
|
catch (err) {
|
|
1351
1444
|
throw new SpecParserError(err.toString(), ErrorType.GenerateAdaptiveCardFailed);
|
|
1352
1445
|
}
|
|
1353
1446
|
}
|
|
1354
|
-
static generateCardFromResponse(schema, name, parentArrayName = "") {
|
|
1447
|
+
static generateCardFromResponse(schema, name, parentArrayName = "", maxElementCount = Number.MAX_SAFE_INTEGER, counter = { count: 0 }) {
|
|
1448
|
+
if (counter.count >= maxElementCount) {
|
|
1449
|
+
return [];
|
|
1450
|
+
}
|
|
1355
1451
|
if (schema.type === "array") {
|
|
1356
1452
|
// schema.items can be arbitrary object: schema { type: array, items: {} }
|
|
1357
1453
|
if (Object.keys(schema.items).length === 0) {
|
|
1454
|
+
counter.count++;
|
|
1358
1455
|
return [
|
|
1359
1456
|
{
|
|
1360
1457
|
type: ConstantString.TextBlockType,
|
|
@@ -1363,7 +1460,7 @@ class AdaptiveCardGenerator {
|
|
|
1363
1460
|
},
|
|
1364
1461
|
];
|
|
1365
1462
|
}
|
|
1366
|
-
const obj = AdaptiveCardGenerator.generateCardFromResponse(schema.items, "", name);
|
|
1463
|
+
const obj = AdaptiveCardGenerator.generateCardFromResponse(schema.items, "", name, maxElementCount, counter);
|
|
1367
1464
|
const template = {
|
|
1368
1465
|
type: ConstantString.ContainerType,
|
|
1369
1466
|
$data: name ? `\${${name}}` : "${$root}",
|
|
@@ -1373,11 +1470,11 @@ class AdaptiveCardGenerator {
|
|
|
1373
1470
|
return [template];
|
|
1374
1471
|
}
|
|
1375
1472
|
// some schema may not contain type but contain properties
|
|
1376
|
-
if (
|
|
1473
|
+
if (Utils.isObjectSchema(schema)) {
|
|
1377
1474
|
const { properties } = schema;
|
|
1378
1475
|
const result = [];
|
|
1379
1476
|
for (const property in properties) {
|
|
1380
|
-
const obj = AdaptiveCardGenerator.generateCardFromResponse(properties[property], name ? `${name}.${property}` : property, parentArrayName);
|
|
1477
|
+
const obj = AdaptiveCardGenerator.generateCardFromResponse(properties[property], name ? `${name}.${property}` : property, parentArrayName, maxElementCount, counter);
|
|
1381
1478
|
result.push(...obj);
|
|
1382
1479
|
}
|
|
1383
1480
|
if (schema.additionalProperties) {
|
|
@@ -1390,6 +1487,7 @@ class AdaptiveCardGenerator {
|
|
|
1390
1487
|
schema.type === "integer" ||
|
|
1391
1488
|
schema.type === "boolean" ||
|
|
1392
1489
|
schema.type === "number") {
|
|
1490
|
+
counter.count++;
|
|
1393
1491
|
if (!AdaptiveCardGenerator.isImageUrlProperty(schema, name, parentArrayName)) {
|
|
1394
1492
|
// string in root: "ddd"
|
|
1395
1493
|
let text = "result: ${$root}";
|
|
@@ -1414,24 +1512,17 @@ class AdaptiveCardGenerator {
|
|
|
1414
1512
|
];
|
|
1415
1513
|
}
|
|
1416
1514
|
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
|
-
}
|
|
1515
|
+
const url = name ? `\${${name}}` : "${$data}";
|
|
1516
|
+
const condition = name
|
|
1517
|
+
? `\${${name} != null && ${name} != ''}`
|
|
1518
|
+
: "${$data != null && $data != ''}";
|
|
1519
|
+
return [
|
|
1520
|
+
{
|
|
1521
|
+
type: "Image",
|
|
1522
|
+
url,
|
|
1523
|
+
$when: condition,
|
|
1524
|
+
},
|
|
1525
|
+
];
|
|
1435
1526
|
}
|
|
1436
1527
|
}
|
|
1437
1528
|
if (schema.oneOf || schema.anyOf || schema.not || schema.allOf) {
|
|
@@ -1441,7 +1532,7 @@ class AdaptiveCardGenerator {
|
|
|
1441
1532
|
}
|
|
1442
1533
|
// Find the first array property in the response schema object with the well-known name
|
|
1443
1534
|
static getResponseJsonPathFromSchema(schema) {
|
|
1444
|
-
if (
|
|
1535
|
+
if (Utils.isObjectSchema(schema)) {
|
|
1445
1536
|
const { properties } = schema;
|
|
1446
1537
|
for (const property in properties) {
|
|
1447
1538
|
const schema = properties[property];
|