@microsoft/m365-spec-parser 0.2.3-rc-hotfix.3 → 0.2.4-alpha.396238b05.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 +343 -253
- package/dist/index.esm2017.js.map +1 -1
- package/dist/index.esm2017.mjs +467 -328
- package/dist/index.esm2017.mjs.map +1 -1
- package/dist/index.esm5.js +343 -253
- package/dist/index.esm5.js.map +1 -1
- package/dist/index.node.cjs.js +467 -328
- 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/utils.d.ts +7 -4
- package/package.json +3 -3
package/dist/index.esm5.js
CHANGED
|
@@ -62,11 +62,8 @@ var ErrorType;
|
|
|
62
62
|
ErrorType["PostBodyContainMultipleMediaTypes"] = "post-body-contain-multiple-media-types";
|
|
63
63
|
ErrorType["ResponseContainMultipleMediaTypes"] = "response-contain-multiple-media-types";
|
|
64
64
|
ErrorType["ResponseJsonIsEmpty"] = "response-json-is-empty";
|
|
65
|
-
ErrorType["PostBodySchemaIsNotJson"] = "post-body-schema-is-not-json";
|
|
66
65
|
ErrorType["PostBodyContainsRequiredUnsupportedSchema"] = "post-body-contains-required-unsupported-schema";
|
|
67
66
|
ErrorType["ParamsContainRequiredUnsupportedSchema"] = "params-contain-required-unsupported-schema";
|
|
68
|
-
ErrorType["ParamsContainsNestedObject"] = "params-contains-nested-object";
|
|
69
|
-
ErrorType["RequestBodyContainsNestedObject"] = "request-body-contains-nested-object";
|
|
70
67
|
ErrorType["ExceededRequiredParamsLimit"] = "exceeded-required-params-limit";
|
|
71
68
|
ErrorType["NoParameter"] = "no-parameter";
|
|
72
69
|
ErrorType["NoAPIInfo"] = "no-api-info";
|
|
@@ -85,6 +82,9 @@ var WarningType;
|
|
|
85
82
|
WarningType["OperationOnlyContainsOptionalParam"] = "operation-only-contains-optional-param";
|
|
86
83
|
WarningType["ConvertSwaggerToOpenAPI"] = "convert-swagger-to-openapi";
|
|
87
84
|
WarningType["FuncDescriptionTooLong"] = "function-description-too-long";
|
|
85
|
+
WarningType["OperationIdContainsSpecialCharacters"] = "operationid-contains-special-characters";
|
|
86
|
+
WarningType["UnsupportedAuthType"] = "unsupported-auth-type";
|
|
87
|
+
WarningType["GenerateJsonDataFailed"] = "generate-json-data-failed";
|
|
88
88
|
WarningType["Unknown"] = "unknown";
|
|
89
89
|
})(WarningType || (WarningType = {}));
|
|
90
90
|
/**
|
|
@@ -130,25 +130,23 @@ ConstantString.ConvertSwaggerToOpenAPI = "The Swagger 2.0 file has been converte
|
|
|
130
130
|
ConstantString.SwaggerNotSupported = "Swagger 2.0 is not supported. Please convert to OpenAPI 3.0 manually before proceeding.";
|
|
131
131
|
ConstantString.SpecVersionNotSupported = "Unsupported OpenAPI version %s. Please use version 3.0.x.";
|
|
132
132
|
ConstantString.MultipleAuthNotSupported = "Multiple authentication methods are unsupported. Ensure all selected APIs use identical authentication.";
|
|
133
|
+
ConstantString.OperationIdContainsSpecialCharacters = "Operation id '%s' in OpenAPI description document contained special characters and was renamed to '%s'.";
|
|
134
|
+
ConstantString.AuthTypeIsNotSupported = "Unsupported authorization type in API '%s'. No authorization will be used.";
|
|
133
135
|
ConstantString.UnsupportedSchema = "Unsupported schema in %s %s: %s";
|
|
134
136
|
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.";
|
|
137
|
+
ConstantString.GenerateJsonDataFailed = "Failed to generate JSON data for api: %s due to %s.";
|
|
135
138
|
ConstantString.WrappedCardVersion = "devPreview";
|
|
136
139
|
ConstantString.WrappedCardSchema = "https://developer.microsoft.com/json-schemas/teams/vDevPreview/MicrosoftTeams.ResponseRenderingTemplate.schema.json";
|
|
137
140
|
ConstantString.WrappedCardResponseLayout = "list";
|
|
138
141
|
ConstantString.GetMethod = "get";
|
|
139
142
|
ConstantString.PostMethod = "post";
|
|
140
143
|
ConstantString.AdaptiveCardVersion = "1.5";
|
|
141
|
-
ConstantString.AdaptiveCardSchema = "
|
|
144
|
+
ConstantString.AdaptiveCardSchema = "https://adaptivecards.io/schemas/adaptive-card.json";
|
|
142
145
|
ConstantString.AdaptiveCardType = "AdaptiveCard";
|
|
143
146
|
ConstantString.TextBlockType = "TextBlock";
|
|
144
147
|
ConstantString.ImageType = "Image";
|
|
145
148
|
ConstantString.ContainerType = "Container";
|
|
146
|
-
ConstantString.RegistrationIdPostfix =
|
|
147
|
-
apiKey: "REGISTRATION_ID",
|
|
148
|
-
oauth2: "CONFIGURATION_ID",
|
|
149
|
-
http: "REGISTRATION_ID",
|
|
150
|
-
openIdConnect: "REGISTRATION_ID",
|
|
151
|
-
};
|
|
149
|
+
ConstantString.RegistrationIdPostfix = "REGISTRATION_ID";
|
|
152
150
|
ConstantString.ResponseCodeFor20X = [
|
|
153
151
|
"200",
|
|
154
152
|
"201",
|
|
@@ -217,16 +215,8 @@ ConstantString.PluginManifestSchema = "https://developer.microsoft.com/json-sche
|
|
|
217
215
|
|
|
218
216
|
// Copyright (c) Microsoft Corporation.
|
|
219
217
|
class Utils {
|
|
220
|
-
static
|
|
221
|
-
|
|
222
|
-
for (const property in schema.properties) {
|
|
223
|
-
const nestedSchema = schema.properties[property];
|
|
224
|
-
if (nestedSchema.type === "object") {
|
|
225
|
-
return true;
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
return false;
|
|
218
|
+
static isObjectSchema(schema) {
|
|
219
|
+
return schema.type === "object" || (!schema.type && !!schema.properties);
|
|
230
220
|
}
|
|
231
221
|
static containMultipleMediaTypes(bodyObject) {
|
|
232
222
|
return Object.keys((bodyObject === null || bodyObject === void 0 ? void 0 : bodyObject.content) || {}).length > 1;
|
|
@@ -237,11 +227,32 @@ class Utils {
|
|
|
237
227
|
static isAPIKeyAuth(authScheme) {
|
|
238
228
|
return authScheme.type === "apiKey";
|
|
239
229
|
}
|
|
230
|
+
static isAPIKeyAuthButNotInCookie(authScheme) {
|
|
231
|
+
return authScheme.type === "apiKey" && authScheme.in !== "cookie";
|
|
232
|
+
}
|
|
240
233
|
static isOAuthWithAuthCodeFlow(authScheme) {
|
|
241
234
|
return !!(authScheme.type === "oauth2" &&
|
|
242
235
|
authScheme.flows &&
|
|
243
236
|
authScheme.flows.authorizationCode);
|
|
244
237
|
}
|
|
238
|
+
static isNotSupportedAuth(authSchemeArray) {
|
|
239
|
+
if (authSchemeArray.length === 0) {
|
|
240
|
+
return false;
|
|
241
|
+
}
|
|
242
|
+
if (authSchemeArray.length > 0 && authSchemeArray.every((auths) => auths.length > 1)) {
|
|
243
|
+
return true;
|
|
244
|
+
}
|
|
245
|
+
for (const auths of authSchemeArray) {
|
|
246
|
+
if (auths.length === 1) {
|
|
247
|
+
if (Utils.isOAuthWithAuthCodeFlow(auths[0].authScheme) ||
|
|
248
|
+
Utils.isBearerTokenAuth(auths[0].authScheme) ||
|
|
249
|
+
Utils.isAPIKeyAuthButNotInCookie(auths[0].authScheme)) {
|
|
250
|
+
return false;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
return true;
|
|
255
|
+
}
|
|
245
256
|
static getAuthArray(securities, spec) {
|
|
246
257
|
var _a;
|
|
247
258
|
const result = [];
|
|
@@ -266,6 +277,20 @@ class Utils {
|
|
|
266
277
|
result.sort((a, b) => a[0].name.localeCompare(b[0].name));
|
|
267
278
|
return result;
|
|
268
279
|
}
|
|
280
|
+
static getAuthMap(spec) {
|
|
281
|
+
const authMap = {};
|
|
282
|
+
for (const url in spec.paths) {
|
|
283
|
+
for (const method in spec.paths[url]) {
|
|
284
|
+
const operation = spec.paths[url][method];
|
|
285
|
+
const authArray = Utils.getAuthArray(operation.security, spec);
|
|
286
|
+
if (authArray && authArray.length > 0) {
|
|
287
|
+
const currentAuth = authArray[0][0];
|
|
288
|
+
authMap[operation.operationId] = currentAuth;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
return authMap;
|
|
293
|
+
}
|
|
269
294
|
static getAuthInfo(spec) {
|
|
270
295
|
let authInfo = undefined;
|
|
271
296
|
for (const url in spec.paths) {
|
|
@@ -294,27 +319,33 @@ class Utils {
|
|
|
294
319
|
let multipleMediaType = false;
|
|
295
320
|
for (const code of ConstantString.ResponseCodeFor20X) {
|
|
296
321
|
const responseObject = (_a = operationObject === null || operationObject === void 0 ? void 0 : operationObject.responses) === null || _a === void 0 ? void 0 : _a[code];
|
|
297
|
-
if (responseObject
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
else {
|
|
310
|
-
return { json, multipleMediaType };
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
}
|
|
322
|
+
if (!responseObject) {
|
|
323
|
+
continue;
|
|
324
|
+
}
|
|
325
|
+
multipleMediaType = Utils.containMultipleMediaTypes(responseObject);
|
|
326
|
+
if (!allowMultipleMediaType && multipleMediaType) {
|
|
327
|
+
json = {};
|
|
328
|
+
continue;
|
|
329
|
+
}
|
|
330
|
+
const mediaObj = Utils.getJsonContentType(responseObject);
|
|
331
|
+
if (Object.keys(mediaObj).length > 0) {
|
|
332
|
+
json = mediaObj;
|
|
333
|
+
return { json, multipleMediaType };
|
|
314
334
|
}
|
|
315
335
|
}
|
|
316
336
|
return { json, multipleMediaType };
|
|
317
337
|
}
|
|
338
|
+
static getJsonContentType(responseObject) {
|
|
339
|
+
if (responseObject.content) {
|
|
340
|
+
for (const contentType of Object.keys(responseObject.content)) {
|
|
341
|
+
// json media type can also be "application/json; charset=utf-8"
|
|
342
|
+
if (contentType.indexOf("application/json") >= 0) {
|
|
343
|
+
return responseObject.content[contentType];
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
return {};
|
|
348
|
+
}
|
|
318
349
|
static convertPathToCamelCase(path) {
|
|
319
350
|
const pathSegments = path.split(/[./{]/);
|
|
320
351
|
const camelCaseSegments = pathSegments.map((segment) => {
|
|
@@ -350,7 +381,7 @@ class Utils {
|
|
|
350
381
|
}
|
|
351
382
|
return newStr;
|
|
352
383
|
}
|
|
353
|
-
static checkServerUrl(servers) {
|
|
384
|
+
static checkServerUrl(servers, allowHttp = false) {
|
|
354
385
|
const errors = [];
|
|
355
386
|
let serverUrl;
|
|
356
387
|
try {
|
|
@@ -373,8 +404,7 @@ class Utils {
|
|
|
373
404
|
data: servers,
|
|
374
405
|
});
|
|
375
406
|
}
|
|
376
|
-
else if (protocol !== "https:") {
|
|
377
|
-
// Http server url is not supported
|
|
407
|
+
else if (protocol !== "https:" && !(protocol === "http:" && allowHttp)) {
|
|
378
408
|
const protocolString = protocol.slice(0, -1);
|
|
379
409
|
errors.push({
|
|
380
410
|
type: ErrorType.UrlProtocolNotSupported,
|
|
@@ -390,10 +420,11 @@ class Utils {
|
|
|
390
420
|
let hasTopLevelServers = false;
|
|
391
421
|
let hasPathLevelServers = false;
|
|
392
422
|
let hasOperationLevelServers = false;
|
|
423
|
+
const allowHttp = options.projectType === ProjectType.Copilot;
|
|
393
424
|
if (spec.servers && spec.servers.length >= 1) {
|
|
394
425
|
hasTopLevelServers = true;
|
|
395
426
|
// for multiple server, we only use the first url
|
|
396
|
-
const serverErrors = Utils.checkServerUrl(spec.servers);
|
|
427
|
+
const serverErrors = Utils.checkServerUrl(spec.servers, allowHttp);
|
|
397
428
|
errors.push(...serverErrors);
|
|
398
429
|
}
|
|
399
430
|
const paths = spec.paths;
|
|
@@ -401,7 +432,7 @@ class Utils {
|
|
|
401
432
|
const methods = paths[path];
|
|
402
433
|
if ((methods === null || methods === void 0 ? void 0 : methods.servers) && methods.servers.length >= 1) {
|
|
403
434
|
hasPathLevelServers = true;
|
|
404
|
-
const serverErrors = Utils.checkServerUrl(methods.servers);
|
|
435
|
+
const serverErrors = Utils.checkServerUrl(methods.servers, allowHttp);
|
|
405
436
|
errors.push(...serverErrors);
|
|
406
437
|
}
|
|
407
438
|
for (const method in methods) {
|
|
@@ -409,7 +440,7 @@ class Utils {
|
|
|
409
440
|
if (((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && operationObject) {
|
|
410
441
|
if ((operationObject === null || operationObject === void 0 ? void 0 : operationObject.servers) && operationObject.servers.length >= 1) {
|
|
411
442
|
hasOperationLevelServers = true;
|
|
412
|
-
const serverErrors = Utils.checkServerUrl(operationObject.servers);
|
|
443
|
+
const serverErrors = Utils.checkServerUrl(operationObject.servers, allowHttp);
|
|
413
444
|
errors.push(...serverErrors);
|
|
414
445
|
}
|
|
415
446
|
}
|
|
@@ -456,7 +487,7 @@ class Utils {
|
|
|
456
487
|
optionalParams.push(parameter);
|
|
457
488
|
}
|
|
458
489
|
}
|
|
459
|
-
else if (schema
|
|
490
|
+
else if (Utils.isObjectSchema(schema)) {
|
|
460
491
|
const { properties } = schema;
|
|
461
492
|
for (const property in properties) {
|
|
462
493
|
let isRequired = false;
|
|
@@ -570,29 +601,6 @@ class Utils {
|
|
|
570
601
|
const serverUrl = operationServer || methodServer || rootServer;
|
|
571
602
|
return serverUrl;
|
|
572
603
|
}
|
|
573
|
-
static limitACBodyProperties(body, maxCount) {
|
|
574
|
-
const result = [];
|
|
575
|
-
let currentCount = 0;
|
|
576
|
-
for (const element of body) {
|
|
577
|
-
if (element.type === ConstantString.ContainerType) {
|
|
578
|
-
const items = this.limitACBodyProperties(element.items, maxCount - currentCount);
|
|
579
|
-
result.push({
|
|
580
|
-
type: ConstantString.ContainerType,
|
|
581
|
-
$data: element.$data,
|
|
582
|
-
items: items,
|
|
583
|
-
});
|
|
584
|
-
currentCount += items.length;
|
|
585
|
-
}
|
|
586
|
-
else {
|
|
587
|
-
result.push(element);
|
|
588
|
-
currentCount++;
|
|
589
|
-
}
|
|
590
|
-
if (currentCount >= maxCount) {
|
|
591
|
-
break;
|
|
592
|
-
}
|
|
593
|
-
}
|
|
594
|
-
return result;
|
|
595
|
-
}
|
|
596
604
|
}
|
|
597
605
|
|
|
598
606
|
// Copyright (c) Microsoft Corporation.
|
|
@@ -721,22 +729,6 @@ class Validator {
|
|
|
721
729
|
}
|
|
722
730
|
return result;
|
|
723
731
|
}
|
|
724
|
-
validateResponse(method, path) {
|
|
725
|
-
const result = { isValid: true, reason: [] };
|
|
726
|
-
const operationObject = this.spec.paths[path][method];
|
|
727
|
-
const { json, multipleMediaType } = Utils.getResponseJson(operationObject);
|
|
728
|
-
if (this.options.projectType === ProjectType.SME) {
|
|
729
|
-
// only support response body only contains “application/json” content type
|
|
730
|
-
if (multipleMediaType) {
|
|
731
|
-
result.reason.push(ErrorType.ResponseContainMultipleMediaTypes);
|
|
732
|
-
}
|
|
733
|
-
else if (Object.keys(json).length === 0) {
|
|
734
|
-
// response body should not be empty
|
|
735
|
-
result.reason.push(ErrorType.ResponseJsonIsEmpty);
|
|
736
|
-
}
|
|
737
|
-
}
|
|
738
|
-
return result;
|
|
739
|
-
}
|
|
740
732
|
validateServer(method, path) {
|
|
741
733
|
const result = { isValid: true, reason: [] };
|
|
742
734
|
const serverObj = Utils.getServerObject(this.spec, method, path);
|
|
@@ -745,8 +737,8 @@ class Validator {
|
|
|
745
737
|
result.reason.push(ErrorType.NoServerInformation);
|
|
746
738
|
}
|
|
747
739
|
else {
|
|
748
|
-
|
|
749
|
-
const serverValidateResult = Utils.checkServerUrl([serverObj]);
|
|
740
|
+
const allowHttp = this.projectType === ProjectType.Copilot;
|
|
741
|
+
const serverValidateResult = Utils.checkServerUrl([serverObj], allowHttp);
|
|
750
742
|
result.reason.push(...serverValidateResult.map((item) => item.type));
|
|
751
743
|
}
|
|
752
744
|
return result;
|
|
@@ -769,6 +761,9 @@ class Validator {
|
|
|
769
761
|
reason: [ErrorType.MultipleAuthNotSupported],
|
|
770
762
|
};
|
|
771
763
|
}
|
|
764
|
+
if (this.projectType === ProjectType.Copilot) {
|
|
765
|
+
return { isValid: true, reason: [] };
|
|
766
|
+
}
|
|
772
767
|
for (const auths of authSchemeArray) {
|
|
773
768
|
if (auths.length === 1) {
|
|
774
769
|
if ((this.options.allowAPIKeyAuth && Utils.isAPIKeyAuth(auths[0].authScheme)) ||
|
|
@@ -781,125 +776,6 @@ class Validator {
|
|
|
781
776
|
}
|
|
782
777
|
return { isValid: false, reason: [ErrorType.AuthTypeIsNotSupported] };
|
|
783
778
|
}
|
|
784
|
-
checkPostBodySchema(schema, isRequired = false) {
|
|
785
|
-
var _a;
|
|
786
|
-
const paramResult = {
|
|
787
|
-
requiredNum: 0,
|
|
788
|
-
optionalNum: 0,
|
|
789
|
-
isValid: true,
|
|
790
|
-
reason: [],
|
|
791
|
-
};
|
|
792
|
-
if (Object.keys(schema).length === 0) {
|
|
793
|
-
return paramResult;
|
|
794
|
-
}
|
|
795
|
-
const isRequiredWithoutDefault = isRequired && schema.default === undefined;
|
|
796
|
-
const isCopilot = this.projectType === ProjectType.Copilot;
|
|
797
|
-
if (isCopilot && this.hasNestedObjectInSchema(schema)) {
|
|
798
|
-
paramResult.isValid = false;
|
|
799
|
-
paramResult.reason = [ErrorType.RequestBodyContainsNestedObject];
|
|
800
|
-
return paramResult;
|
|
801
|
-
}
|
|
802
|
-
if (schema.type === "string" ||
|
|
803
|
-
schema.type === "integer" ||
|
|
804
|
-
schema.type === "boolean" ||
|
|
805
|
-
schema.type === "number") {
|
|
806
|
-
if (isRequiredWithoutDefault) {
|
|
807
|
-
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
808
|
-
}
|
|
809
|
-
else {
|
|
810
|
-
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
811
|
-
}
|
|
812
|
-
}
|
|
813
|
-
else if (schema.type === "object") {
|
|
814
|
-
const { properties } = schema;
|
|
815
|
-
for (const property in properties) {
|
|
816
|
-
let isRequired = false;
|
|
817
|
-
if (schema.required && ((_a = schema.required) === null || _a === void 0 ? void 0 : _a.indexOf(property)) >= 0) {
|
|
818
|
-
isRequired = true;
|
|
819
|
-
}
|
|
820
|
-
const result = this.checkPostBodySchema(properties[property], isRequired);
|
|
821
|
-
paramResult.requiredNum += result.requiredNum;
|
|
822
|
-
paramResult.optionalNum += result.optionalNum;
|
|
823
|
-
paramResult.isValid = paramResult.isValid && result.isValid;
|
|
824
|
-
paramResult.reason.push(...result.reason);
|
|
825
|
-
}
|
|
826
|
-
}
|
|
827
|
-
else {
|
|
828
|
-
if (isRequiredWithoutDefault && !isCopilot) {
|
|
829
|
-
paramResult.isValid = false;
|
|
830
|
-
paramResult.reason.push(ErrorType.PostBodyContainsRequiredUnsupportedSchema);
|
|
831
|
-
}
|
|
832
|
-
}
|
|
833
|
-
return paramResult;
|
|
834
|
-
}
|
|
835
|
-
checkParamSchema(paramObject) {
|
|
836
|
-
const paramResult = {
|
|
837
|
-
requiredNum: 0,
|
|
838
|
-
optionalNum: 0,
|
|
839
|
-
isValid: true,
|
|
840
|
-
reason: [],
|
|
841
|
-
};
|
|
842
|
-
if (!paramObject) {
|
|
843
|
-
return paramResult;
|
|
844
|
-
}
|
|
845
|
-
const isCopilot = this.projectType === ProjectType.Copilot;
|
|
846
|
-
for (let i = 0; i < paramObject.length; i++) {
|
|
847
|
-
const param = paramObject[i];
|
|
848
|
-
const schema = param.schema;
|
|
849
|
-
if (isCopilot && this.hasNestedObjectInSchema(schema)) {
|
|
850
|
-
paramResult.isValid = false;
|
|
851
|
-
paramResult.reason.push(ErrorType.ParamsContainsNestedObject);
|
|
852
|
-
continue;
|
|
853
|
-
}
|
|
854
|
-
const isRequiredWithoutDefault = param.required && schema.default === undefined;
|
|
855
|
-
if (isCopilot) {
|
|
856
|
-
if (isRequiredWithoutDefault) {
|
|
857
|
-
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
858
|
-
}
|
|
859
|
-
else {
|
|
860
|
-
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
861
|
-
}
|
|
862
|
-
continue;
|
|
863
|
-
}
|
|
864
|
-
if (param.in === "header" || param.in === "cookie") {
|
|
865
|
-
if (isRequiredWithoutDefault) {
|
|
866
|
-
paramResult.isValid = false;
|
|
867
|
-
paramResult.reason.push(ErrorType.ParamsContainRequiredUnsupportedSchema);
|
|
868
|
-
}
|
|
869
|
-
continue;
|
|
870
|
-
}
|
|
871
|
-
if (schema.type !== "boolean" &&
|
|
872
|
-
schema.type !== "string" &&
|
|
873
|
-
schema.type !== "number" &&
|
|
874
|
-
schema.type !== "integer") {
|
|
875
|
-
if (isRequiredWithoutDefault) {
|
|
876
|
-
paramResult.isValid = false;
|
|
877
|
-
paramResult.reason.push(ErrorType.ParamsContainRequiredUnsupportedSchema);
|
|
878
|
-
}
|
|
879
|
-
continue;
|
|
880
|
-
}
|
|
881
|
-
if (param.in === "query" || param.in === "path") {
|
|
882
|
-
if (isRequiredWithoutDefault) {
|
|
883
|
-
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
884
|
-
}
|
|
885
|
-
else {
|
|
886
|
-
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
887
|
-
}
|
|
888
|
-
}
|
|
889
|
-
}
|
|
890
|
-
return paramResult;
|
|
891
|
-
}
|
|
892
|
-
hasNestedObjectInSchema(schema) {
|
|
893
|
-
if (schema.type === "object") {
|
|
894
|
-
for (const property in schema.properties) {
|
|
895
|
-
const nestedSchema = schema.properties[property];
|
|
896
|
-
if (nestedSchema.type === "object") {
|
|
897
|
-
return true;
|
|
898
|
-
}
|
|
899
|
-
}
|
|
900
|
-
}
|
|
901
|
-
return false;
|
|
902
|
-
}
|
|
903
779
|
}
|
|
904
780
|
|
|
905
781
|
// Copyright (c) Microsoft Corporation.
|
|
@@ -909,7 +785,6 @@ class CopilotValidator extends Validator {
|
|
|
909
785
|
this.projectType = ProjectType.Copilot;
|
|
910
786
|
this.options = options;
|
|
911
787
|
this.spec = spec;
|
|
912
|
-
this.checkCircularReference();
|
|
913
788
|
}
|
|
914
789
|
validateSpec() {
|
|
915
790
|
const result = { errors: [], warnings: [] };
|
|
@@ -935,10 +810,6 @@ class CopilotValidator extends Validator {
|
|
|
935
810
|
if (!methodAndPathResult.isValid) {
|
|
936
811
|
return methodAndPathResult;
|
|
937
812
|
}
|
|
938
|
-
const circularReferenceResult = this.validateCircularReference(method, path);
|
|
939
|
-
if (!circularReferenceResult.isValid) {
|
|
940
|
-
return circularReferenceResult;
|
|
941
|
-
}
|
|
942
813
|
const operationObject = this.spec.paths[path][method];
|
|
943
814
|
// validate auth
|
|
944
815
|
const authCheckResult = this.validateAuth(method, path);
|
|
@@ -950,24 +821,6 @@ class CopilotValidator extends Validator {
|
|
|
950
821
|
// validate server
|
|
951
822
|
const validateServerResult = this.validateServer(method, path);
|
|
952
823
|
result.reason.push(...validateServerResult.reason);
|
|
953
|
-
// validate response
|
|
954
|
-
const validateResponseResult = this.validateResponse(method, path);
|
|
955
|
-
result.reason.push(...validateResponseResult.reason);
|
|
956
|
-
// validate requestBody
|
|
957
|
-
const requestBody = operationObject.requestBody;
|
|
958
|
-
const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
|
|
959
|
-
if (requestJsonBody) {
|
|
960
|
-
const requestBodySchema = requestJsonBody.schema;
|
|
961
|
-
if (requestBodySchema.type !== "object") {
|
|
962
|
-
result.reason.push(ErrorType.PostBodySchemaIsNotJson);
|
|
963
|
-
}
|
|
964
|
-
const requestBodyParamResult = this.checkPostBodySchema(requestBodySchema, requestBody.required);
|
|
965
|
-
result.reason.push(...requestBodyParamResult.reason);
|
|
966
|
-
}
|
|
967
|
-
// validate parameters
|
|
968
|
-
const paramObject = operationObject.parameters;
|
|
969
|
-
const paramResult = this.checkParamSchema(paramObject);
|
|
970
|
-
result.reason.push(...paramResult.reason);
|
|
971
824
|
if (result.reason.length > 0) {
|
|
972
825
|
result.isValid = false;
|
|
973
826
|
}
|
|
@@ -1059,6 +912,108 @@ class SMEValidator extends Validator {
|
|
|
1059
912
|
}
|
|
1060
913
|
return result;
|
|
1061
914
|
}
|
|
915
|
+
validateResponse(method, path) {
|
|
916
|
+
const result = { isValid: true, reason: [] };
|
|
917
|
+
const operationObject = this.spec.paths[path][method];
|
|
918
|
+
const { json, multipleMediaType } = Utils.getResponseJson(operationObject);
|
|
919
|
+
// only support response body only contains “application/json” content type
|
|
920
|
+
if (multipleMediaType) {
|
|
921
|
+
result.reason.push(ErrorType.ResponseContainMultipleMediaTypes);
|
|
922
|
+
}
|
|
923
|
+
else if (Object.keys(json).length === 0) {
|
|
924
|
+
// response body should not be empty
|
|
925
|
+
result.reason.push(ErrorType.ResponseJsonIsEmpty);
|
|
926
|
+
}
|
|
927
|
+
return result;
|
|
928
|
+
}
|
|
929
|
+
checkPostBodySchema(schema, isRequired = false) {
|
|
930
|
+
var _a;
|
|
931
|
+
const paramResult = {
|
|
932
|
+
requiredNum: 0,
|
|
933
|
+
optionalNum: 0,
|
|
934
|
+
isValid: true,
|
|
935
|
+
reason: [],
|
|
936
|
+
};
|
|
937
|
+
if (Object.keys(schema).length === 0) {
|
|
938
|
+
return paramResult;
|
|
939
|
+
}
|
|
940
|
+
const isRequiredWithoutDefault = isRequired && schema.default === undefined;
|
|
941
|
+
const isCopilot = this.projectType === ProjectType.Copilot;
|
|
942
|
+
if (schema.type === "string" ||
|
|
943
|
+
schema.type === "integer" ||
|
|
944
|
+
schema.type === "boolean" ||
|
|
945
|
+
schema.type === "number") {
|
|
946
|
+
if (isRequiredWithoutDefault) {
|
|
947
|
+
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
948
|
+
}
|
|
949
|
+
else {
|
|
950
|
+
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
else if (Utils.isObjectSchema(schema)) {
|
|
954
|
+
const { properties } = schema;
|
|
955
|
+
for (const property in properties) {
|
|
956
|
+
let isRequired = false;
|
|
957
|
+
if (schema.required && ((_a = schema.required) === null || _a === void 0 ? void 0 : _a.indexOf(property)) >= 0) {
|
|
958
|
+
isRequired = true;
|
|
959
|
+
}
|
|
960
|
+
const result = this.checkPostBodySchema(properties[property], isRequired);
|
|
961
|
+
paramResult.requiredNum += result.requiredNum;
|
|
962
|
+
paramResult.optionalNum += result.optionalNum;
|
|
963
|
+
paramResult.isValid = paramResult.isValid && result.isValid;
|
|
964
|
+
paramResult.reason.push(...result.reason);
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
else {
|
|
968
|
+
if (isRequiredWithoutDefault && !isCopilot) {
|
|
969
|
+
paramResult.isValid = false;
|
|
970
|
+
paramResult.reason.push(ErrorType.PostBodyContainsRequiredUnsupportedSchema);
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
return paramResult;
|
|
974
|
+
}
|
|
975
|
+
checkParamSchema(paramObject) {
|
|
976
|
+
const paramResult = {
|
|
977
|
+
requiredNum: 0,
|
|
978
|
+
optionalNum: 0,
|
|
979
|
+
isValid: true,
|
|
980
|
+
reason: [],
|
|
981
|
+
};
|
|
982
|
+
if (!paramObject) {
|
|
983
|
+
return paramResult;
|
|
984
|
+
}
|
|
985
|
+
for (let i = 0; i < paramObject.length; i++) {
|
|
986
|
+
const param = paramObject[i];
|
|
987
|
+
const schema = param.schema;
|
|
988
|
+
const isRequiredWithoutDefault = param.required && schema.default === undefined;
|
|
989
|
+
if (param.in === "header" || param.in === "cookie") {
|
|
990
|
+
if (isRequiredWithoutDefault) {
|
|
991
|
+
paramResult.isValid = false;
|
|
992
|
+
paramResult.reason.push(ErrorType.ParamsContainRequiredUnsupportedSchema);
|
|
993
|
+
}
|
|
994
|
+
continue;
|
|
995
|
+
}
|
|
996
|
+
if (schema.type !== "boolean" &&
|
|
997
|
+
schema.type !== "string" &&
|
|
998
|
+
schema.type !== "number" &&
|
|
999
|
+
schema.type !== "integer") {
|
|
1000
|
+
if (isRequiredWithoutDefault) {
|
|
1001
|
+
paramResult.isValid = false;
|
|
1002
|
+
paramResult.reason.push(ErrorType.ParamsContainRequiredUnsupportedSchema);
|
|
1003
|
+
}
|
|
1004
|
+
continue;
|
|
1005
|
+
}
|
|
1006
|
+
if (param.in === "query" || param.in === "path") {
|
|
1007
|
+
if (isRequiredWithoutDefault) {
|
|
1008
|
+
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
1009
|
+
}
|
|
1010
|
+
else {
|
|
1011
|
+
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
return paramResult;
|
|
1016
|
+
}
|
|
1062
1017
|
validateParamCount(postBodyResult, paramResult) {
|
|
1063
1018
|
const result = { isValid: true, reason: [] };
|
|
1064
1019
|
const totalRequiredParams = postBodyResult.requiredNum + paramResult.requiredNum;
|
|
@@ -1348,20 +1303,157 @@ class SpecParser {
|
|
|
1348
1303
|
}
|
|
1349
1304
|
}
|
|
1350
1305
|
|
|
1306
|
+
// Copyright (c) Microsoft Corporation.
|
|
1307
|
+
class JsonDataGenerator {
|
|
1308
|
+
static generate(schema) {
|
|
1309
|
+
return this.generateMockData(schema);
|
|
1310
|
+
}
|
|
1311
|
+
static generateMockData(schema) {
|
|
1312
|
+
if (this.visitedSchemas.has(schema)) {
|
|
1313
|
+
return null; // Prevent circular reference
|
|
1314
|
+
}
|
|
1315
|
+
this.visitedSchemas.add(schema);
|
|
1316
|
+
let result;
|
|
1317
|
+
if (schema.anyOf) {
|
|
1318
|
+
// Select the first schema in anyOf
|
|
1319
|
+
const selectedSchema = schema.anyOf[0];
|
|
1320
|
+
result = this.generateMockData(selectedSchema);
|
|
1321
|
+
}
|
|
1322
|
+
else if (schema.oneOf) {
|
|
1323
|
+
// Select the first schema in oneOf
|
|
1324
|
+
const selectedSchema = schema.oneOf[0];
|
|
1325
|
+
result = this.generateMockData(selectedSchema);
|
|
1326
|
+
}
|
|
1327
|
+
else if (schema.allOf) {
|
|
1328
|
+
// merge all schemas in allOf
|
|
1329
|
+
result = {};
|
|
1330
|
+
for (const subschema of schema.allOf) {
|
|
1331
|
+
const data = this.generateMockData(subschema);
|
|
1332
|
+
result = Object.assign(Object.assign({}, result), data);
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
else {
|
|
1336
|
+
switch (schema.type) {
|
|
1337
|
+
case "string":
|
|
1338
|
+
if (schema.example !== undefined) {
|
|
1339
|
+
result = schema.example;
|
|
1340
|
+
}
|
|
1341
|
+
else if (schema.format) {
|
|
1342
|
+
switch (schema.format) {
|
|
1343
|
+
case "date-time":
|
|
1344
|
+
result = "2024-11-01T05:25:43.593Z";
|
|
1345
|
+
break;
|
|
1346
|
+
case "email":
|
|
1347
|
+
result = "example@example.com";
|
|
1348
|
+
break;
|
|
1349
|
+
case "uuid":
|
|
1350
|
+
result = "123e4567-e89b-12d3-a456-426614174000";
|
|
1351
|
+
break;
|
|
1352
|
+
case "ipv4":
|
|
1353
|
+
result = "192.168.0.1";
|
|
1354
|
+
break;
|
|
1355
|
+
case "ipv6":
|
|
1356
|
+
result = "2001:0db8:85a3:0000:0000:8a2e:0370:7334";
|
|
1357
|
+
break;
|
|
1358
|
+
default:
|
|
1359
|
+
result = "example string";
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
else {
|
|
1363
|
+
result = "example string";
|
|
1364
|
+
}
|
|
1365
|
+
break;
|
|
1366
|
+
case "number":
|
|
1367
|
+
if (schema.example !== undefined) {
|
|
1368
|
+
result = schema.example;
|
|
1369
|
+
}
|
|
1370
|
+
else if (schema.format) {
|
|
1371
|
+
switch (schema.format) {
|
|
1372
|
+
case "float":
|
|
1373
|
+
result = 3.14;
|
|
1374
|
+
break;
|
|
1375
|
+
case "double":
|
|
1376
|
+
result = 3.14159;
|
|
1377
|
+
break;
|
|
1378
|
+
default:
|
|
1379
|
+
result = 123;
|
|
1380
|
+
}
|
|
1381
|
+
}
|
|
1382
|
+
else {
|
|
1383
|
+
result = 123;
|
|
1384
|
+
}
|
|
1385
|
+
break;
|
|
1386
|
+
case "integer":
|
|
1387
|
+
if (schema.example !== undefined) {
|
|
1388
|
+
result = schema.example;
|
|
1389
|
+
}
|
|
1390
|
+
else if (schema.format) {
|
|
1391
|
+
switch (schema.format) {
|
|
1392
|
+
case "int32":
|
|
1393
|
+
result = 123456;
|
|
1394
|
+
break;
|
|
1395
|
+
case "int64":
|
|
1396
|
+
result = 123456789;
|
|
1397
|
+
break;
|
|
1398
|
+
default:
|
|
1399
|
+
result = 123;
|
|
1400
|
+
}
|
|
1401
|
+
}
|
|
1402
|
+
else {
|
|
1403
|
+
result = 123;
|
|
1404
|
+
}
|
|
1405
|
+
break;
|
|
1406
|
+
case "boolean":
|
|
1407
|
+
result = schema.example !== undefined ? schema.example : true;
|
|
1408
|
+
break;
|
|
1409
|
+
case "array":
|
|
1410
|
+
result = [this.generateMockData(schema.items)];
|
|
1411
|
+
break;
|
|
1412
|
+
case "object":
|
|
1413
|
+
result = {};
|
|
1414
|
+
if (schema.properties) {
|
|
1415
|
+
for (const key in schema.properties) {
|
|
1416
|
+
result[key] = this.generateMockData(schema.properties[key]);
|
|
1417
|
+
}
|
|
1418
|
+
}
|
|
1419
|
+
break;
|
|
1420
|
+
default:
|
|
1421
|
+
result = schema.example || null;
|
|
1422
|
+
}
|
|
1423
|
+
}
|
|
1424
|
+
this.visitedSchemas.delete(schema);
|
|
1425
|
+
return result;
|
|
1426
|
+
}
|
|
1427
|
+
}
|
|
1428
|
+
JsonDataGenerator.visitedSchemas = new Set();
|
|
1429
|
+
|
|
1351
1430
|
// Copyright (c) Microsoft Corporation.
|
|
1352
1431
|
class AdaptiveCardGenerator {
|
|
1353
|
-
static generateAdaptiveCard(operationItem, allowMultipleMediaType = false) {
|
|
1432
|
+
static generateAdaptiveCard(operationItem, allowMultipleMediaType = false, maxElementCount = Number.MAX_SAFE_INTEGER) {
|
|
1354
1433
|
try {
|
|
1355
1434
|
const { json } = Utils.getResponseJson(operationItem, allowMultipleMediaType);
|
|
1356
1435
|
let cardBody = [];
|
|
1436
|
+
let jsonData = {};
|
|
1437
|
+
const warnings = [];
|
|
1438
|
+
const operationId = operationItem.operationId;
|
|
1357
1439
|
let schema = json.schema;
|
|
1358
1440
|
let jsonPath = "$";
|
|
1359
1441
|
if (schema && Object.keys(schema).length > 0) {
|
|
1442
|
+
try {
|
|
1443
|
+
jsonData = JsonDataGenerator.generate(schema);
|
|
1444
|
+
}
|
|
1445
|
+
catch (err) {
|
|
1446
|
+
warnings.push({
|
|
1447
|
+
type: WarningType.GenerateJsonDataFailed,
|
|
1448
|
+
content: Utils.format(ConstantString.GenerateJsonDataFailed, operationId, err.toString()),
|
|
1449
|
+
data: operationId,
|
|
1450
|
+
});
|
|
1451
|
+
}
|
|
1360
1452
|
jsonPath = AdaptiveCardGenerator.getResponseJsonPathFromSchema(schema);
|
|
1361
1453
|
if (jsonPath !== "$") {
|
|
1362
1454
|
schema = schema.properties[jsonPath];
|
|
1363
1455
|
}
|
|
1364
|
-
cardBody = AdaptiveCardGenerator.generateCardFromResponse(schema, "");
|
|
1456
|
+
cardBody = AdaptiveCardGenerator.generateCardFromResponse(schema, "", "", maxElementCount);
|
|
1365
1457
|
}
|
|
1366
1458
|
// if no schema, try to use example value
|
|
1367
1459
|
if (cardBody.length === 0 && (json.examples || json.example)) {
|
|
@@ -1389,16 +1481,20 @@ class AdaptiveCardGenerator {
|
|
|
1389
1481
|
version: ConstantString.AdaptiveCardVersion,
|
|
1390
1482
|
body: cardBody,
|
|
1391
1483
|
};
|
|
1392
|
-
return [fullCard, jsonPath];
|
|
1484
|
+
return [fullCard, jsonPath, jsonData, warnings];
|
|
1393
1485
|
}
|
|
1394
1486
|
catch (err) {
|
|
1395
1487
|
throw new SpecParserError(err.toString(), ErrorType.GenerateAdaptiveCardFailed);
|
|
1396
1488
|
}
|
|
1397
1489
|
}
|
|
1398
|
-
static generateCardFromResponse(schema, name, parentArrayName = "") {
|
|
1490
|
+
static generateCardFromResponse(schema, name, parentArrayName = "", maxElementCount = Number.MAX_SAFE_INTEGER, counter = { count: 0 }) {
|
|
1491
|
+
if (counter.count >= maxElementCount) {
|
|
1492
|
+
return [];
|
|
1493
|
+
}
|
|
1399
1494
|
if (schema.type === "array") {
|
|
1400
1495
|
// schema.items can be arbitrary object: schema { type: array, items: {} }
|
|
1401
1496
|
if (Object.keys(schema.items).length === 0) {
|
|
1497
|
+
counter.count++;
|
|
1402
1498
|
return [
|
|
1403
1499
|
{
|
|
1404
1500
|
type: ConstantString.TextBlockType,
|
|
@@ -1407,7 +1503,7 @@ class AdaptiveCardGenerator {
|
|
|
1407
1503
|
},
|
|
1408
1504
|
];
|
|
1409
1505
|
}
|
|
1410
|
-
const obj = AdaptiveCardGenerator.generateCardFromResponse(schema.items, "", name);
|
|
1506
|
+
const obj = AdaptiveCardGenerator.generateCardFromResponse(schema.items, "", name, maxElementCount, counter);
|
|
1411
1507
|
const template = {
|
|
1412
1508
|
type: ConstantString.ContainerType,
|
|
1413
1509
|
$data: name ? `\${${name}}` : "${$root}",
|
|
@@ -1417,11 +1513,11 @@ class AdaptiveCardGenerator {
|
|
|
1417
1513
|
return [template];
|
|
1418
1514
|
}
|
|
1419
1515
|
// some schema may not contain type but contain properties
|
|
1420
|
-
if (
|
|
1516
|
+
if (Utils.isObjectSchema(schema)) {
|
|
1421
1517
|
const { properties } = schema;
|
|
1422
1518
|
const result = [];
|
|
1423
1519
|
for (const property in properties) {
|
|
1424
|
-
const obj = AdaptiveCardGenerator.generateCardFromResponse(properties[property], name ? `${name}.${property}` : property, parentArrayName);
|
|
1520
|
+
const obj = AdaptiveCardGenerator.generateCardFromResponse(properties[property], name ? `${name}.${property}` : property, parentArrayName, maxElementCount, counter);
|
|
1425
1521
|
result.push(...obj);
|
|
1426
1522
|
}
|
|
1427
1523
|
if (schema.additionalProperties) {
|
|
@@ -1434,6 +1530,7 @@ class AdaptiveCardGenerator {
|
|
|
1434
1530
|
schema.type === "integer" ||
|
|
1435
1531
|
schema.type === "boolean" ||
|
|
1436
1532
|
schema.type === "number") {
|
|
1533
|
+
counter.count++;
|
|
1437
1534
|
if (!AdaptiveCardGenerator.isImageUrlProperty(schema, name, parentArrayName)) {
|
|
1438
1535
|
// string in root: "ddd"
|
|
1439
1536
|
let text = "result: ${$root}";
|
|
@@ -1458,24 +1555,17 @@ class AdaptiveCardGenerator {
|
|
|
1458
1555
|
];
|
|
1459
1556
|
}
|
|
1460
1557
|
else {
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
{
|
|
1473
|
-
type: "Image",
|
|
1474
|
-
url: "${$data}",
|
|
1475
|
-
$when: "${$data != null && $data != ''}",
|
|
1476
|
-
},
|
|
1477
|
-
];
|
|
1478
|
-
}
|
|
1558
|
+
const url = name ? `\${${name}}` : "${$data}";
|
|
1559
|
+
const condition = name
|
|
1560
|
+
? `\${${name} != null && ${name} != ''}`
|
|
1561
|
+
: "${$data != null && $data != ''}";
|
|
1562
|
+
return [
|
|
1563
|
+
{
|
|
1564
|
+
type: "Image",
|
|
1565
|
+
url,
|
|
1566
|
+
$when: condition,
|
|
1567
|
+
},
|
|
1568
|
+
];
|
|
1479
1569
|
}
|
|
1480
1570
|
}
|
|
1481
1571
|
if (schema.oneOf || schema.anyOf || schema.not || schema.allOf) {
|
|
@@ -1485,7 +1575,7 @@ class AdaptiveCardGenerator {
|
|
|
1485
1575
|
}
|
|
1486
1576
|
// Find the first array property in the response schema object with the well-known name
|
|
1487
1577
|
static getResponseJsonPathFromSchema(schema) {
|
|
1488
|
-
if (
|
|
1578
|
+
if (Utils.isObjectSchema(schema)) {
|
|
1489
1579
|
const { properties } = schema;
|
|
1490
1580
|
for (const property in properties) {
|
|
1491
1581
|
const schema = properties[property];
|