@microsoft/m365-spec-parser 0.2.3 → 0.2.4-alpha.5e509eccd.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 +339 -253
- package/dist/index.esm2017.js.map +1 -1
- package/dist/index.esm2017.mjs +462 -328
- package/dist/index.esm2017.mjs.map +1 -1
- package/dist/index.esm5.js +339 -253
- package/dist/index.esm5.js.map +1 -1
- package/dist/index.node.cjs.js +462 -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 +6 -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;
|
|
@@ -242,6 +232,23 @@ class Utils {
|
|
|
242
232
|
authScheme.flows &&
|
|
243
233
|
authScheme.flows.authorizationCode);
|
|
244
234
|
}
|
|
235
|
+
static isNotSupportedAuth(authSchemeArray) {
|
|
236
|
+
if (authSchemeArray.length === 0) {
|
|
237
|
+
return false;
|
|
238
|
+
}
|
|
239
|
+
if (authSchemeArray.length > 0 && authSchemeArray.every((auths) => auths.length > 1)) {
|
|
240
|
+
return true;
|
|
241
|
+
}
|
|
242
|
+
for (const auths of authSchemeArray) {
|
|
243
|
+
if (auths.length === 1) {
|
|
244
|
+
if (Utils.isOAuthWithAuthCodeFlow(auths[0].authScheme) ||
|
|
245
|
+
Utils.isBearerTokenAuth(auths[0].authScheme)) {
|
|
246
|
+
return false;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
return true;
|
|
251
|
+
}
|
|
245
252
|
static getAuthArray(securities, spec) {
|
|
246
253
|
var _a;
|
|
247
254
|
const result = [];
|
|
@@ -266,6 +273,20 @@ class Utils {
|
|
|
266
273
|
result.sort((a, b) => a[0].name.localeCompare(b[0].name));
|
|
267
274
|
return result;
|
|
268
275
|
}
|
|
276
|
+
static getAuthMap(spec) {
|
|
277
|
+
const authMap = {};
|
|
278
|
+
for (const url in spec.paths) {
|
|
279
|
+
for (const method in spec.paths[url]) {
|
|
280
|
+
const operation = spec.paths[url][method];
|
|
281
|
+
const authArray = Utils.getAuthArray(operation.security, spec);
|
|
282
|
+
if (authArray && authArray.length > 0) {
|
|
283
|
+
const currentAuth = authArray[0][0];
|
|
284
|
+
authMap[operation.operationId] = currentAuth;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
return authMap;
|
|
289
|
+
}
|
|
269
290
|
static getAuthInfo(spec) {
|
|
270
291
|
let authInfo = undefined;
|
|
271
292
|
for (const url in spec.paths) {
|
|
@@ -294,27 +315,33 @@ class Utils {
|
|
|
294
315
|
let multipleMediaType = false;
|
|
295
316
|
for (const code of ConstantString.ResponseCodeFor20X) {
|
|
296
317
|
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
|
-
}
|
|
318
|
+
if (!responseObject) {
|
|
319
|
+
continue;
|
|
320
|
+
}
|
|
321
|
+
multipleMediaType = Utils.containMultipleMediaTypes(responseObject);
|
|
322
|
+
if (!allowMultipleMediaType && multipleMediaType) {
|
|
323
|
+
json = {};
|
|
324
|
+
continue;
|
|
325
|
+
}
|
|
326
|
+
const mediaObj = Utils.getJsonContentType(responseObject);
|
|
327
|
+
if (Object.keys(mediaObj).length > 0) {
|
|
328
|
+
json = mediaObj;
|
|
329
|
+
return { json, multipleMediaType };
|
|
314
330
|
}
|
|
315
331
|
}
|
|
316
332
|
return { json, multipleMediaType };
|
|
317
333
|
}
|
|
334
|
+
static getJsonContentType(responseObject) {
|
|
335
|
+
if (responseObject.content) {
|
|
336
|
+
for (const contentType of Object.keys(responseObject.content)) {
|
|
337
|
+
// json media type can also be "application/json; charset=utf-8"
|
|
338
|
+
if (contentType.indexOf("application/json") >= 0) {
|
|
339
|
+
return responseObject.content[contentType];
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
return {};
|
|
344
|
+
}
|
|
318
345
|
static convertPathToCamelCase(path) {
|
|
319
346
|
const pathSegments = path.split(/[./{]/);
|
|
320
347
|
const camelCaseSegments = pathSegments.map((segment) => {
|
|
@@ -350,7 +377,7 @@ class Utils {
|
|
|
350
377
|
}
|
|
351
378
|
return newStr;
|
|
352
379
|
}
|
|
353
|
-
static checkServerUrl(servers) {
|
|
380
|
+
static checkServerUrl(servers, allowHttp = false) {
|
|
354
381
|
const errors = [];
|
|
355
382
|
let serverUrl;
|
|
356
383
|
try {
|
|
@@ -373,8 +400,7 @@ class Utils {
|
|
|
373
400
|
data: servers,
|
|
374
401
|
});
|
|
375
402
|
}
|
|
376
|
-
else if (protocol !== "https:") {
|
|
377
|
-
// Http server url is not supported
|
|
403
|
+
else if (protocol !== "https:" && !(protocol === "http:" && allowHttp)) {
|
|
378
404
|
const protocolString = protocol.slice(0, -1);
|
|
379
405
|
errors.push({
|
|
380
406
|
type: ErrorType.UrlProtocolNotSupported,
|
|
@@ -390,10 +416,11 @@ class Utils {
|
|
|
390
416
|
let hasTopLevelServers = false;
|
|
391
417
|
let hasPathLevelServers = false;
|
|
392
418
|
let hasOperationLevelServers = false;
|
|
419
|
+
const allowHttp = options.projectType === ProjectType.Copilot;
|
|
393
420
|
if (spec.servers && spec.servers.length >= 1) {
|
|
394
421
|
hasTopLevelServers = true;
|
|
395
422
|
// for multiple server, we only use the first url
|
|
396
|
-
const serverErrors = Utils.checkServerUrl(spec.servers);
|
|
423
|
+
const serverErrors = Utils.checkServerUrl(spec.servers, allowHttp);
|
|
397
424
|
errors.push(...serverErrors);
|
|
398
425
|
}
|
|
399
426
|
const paths = spec.paths;
|
|
@@ -401,7 +428,7 @@ class Utils {
|
|
|
401
428
|
const methods = paths[path];
|
|
402
429
|
if ((methods === null || methods === void 0 ? void 0 : methods.servers) && methods.servers.length >= 1) {
|
|
403
430
|
hasPathLevelServers = true;
|
|
404
|
-
const serverErrors = Utils.checkServerUrl(methods.servers);
|
|
431
|
+
const serverErrors = Utils.checkServerUrl(methods.servers, allowHttp);
|
|
405
432
|
errors.push(...serverErrors);
|
|
406
433
|
}
|
|
407
434
|
for (const method in methods) {
|
|
@@ -409,7 +436,7 @@ class Utils {
|
|
|
409
436
|
if (((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && operationObject) {
|
|
410
437
|
if ((operationObject === null || operationObject === void 0 ? void 0 : operationObject.servers) && operationObject.servers.length >= 1) {
|
|
411
438
|
hasOperationLevelServers = true;
|
|
412
|
-
const serverErrors = Utils.checkServerUrl(operationObject.servers);
|
|
439
|
+
const serverErrors = Utils.checkServerUrl(operationObject.servers, allowHttp);
|
|
413
440
|
errors.push(...serverErrors);
|
|
414
441
|
}
|
|
415
442
|
}
|
|
@@ -456,7 +483,7 @@ class Utils {
|
|
|
456
483
|
optionalParams.push(parameter);
|
|
457
484
|
}
|
|
458
485
|
}
|
|
459
|
-
else if (schema
|
|
486
|
+
else if (Utils.isObjectSchema(schema)) {
|
|
460
487
|
const { properties } = schema;
|
|
461
488
|
for (const property in properties) {
|
|
462
489
|
let isRequired = false;
|
|
@@ -570,29 +597,6 @@ class Utils {
|
|
|
570
597
|
const serverUrl = operationServer || methodServer || rootServer;
|
|
571
598
|
return serverUrl;
|
|
572
599
|
}
|
|
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
600
|
}
|
|
597
601
|
|
|
598
602
|
// Copyright (c) Microsoft Corporation.
|
|
@@ -721,22 +725,6 @@ class Validator {
|
|
|
721
725
|
}
|
|
722
726
|
return result;
|
|
723
727
|
}
|
|
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
728
|
validateServer(method, path) {
|
|
741
729
|
const result = { isValid: true, reason: [] };
|
|
742
730
|
const serverObj = Utils.getServerObject(this.spec, method, path);
|
|
@@ -745,8 +733,8 @@ class Validator {
|
|
|
745
733
|
result.reason.push(ErrorType.NoServerInformation);
|
|
746
734
|
}
|
|
747
735
|
else {
|
|
748
|
-
|
|
749
|
-
const serverValidateResult = Utils.checkServerUrl([serverObj]);
|
|
736
|
+
const allowHttp = this.projectType === ProjectType.Copilot;
|
|
737
|
+
const serverValidateResult = Utils.checkServerUrl([serverObj], allowHttp);
|
|
750
738
|
result.reason.push(...serverValidateResult.map((item) => item.type));
|
|
751
739
|
}
|
|
752
740
|
return result;
|
|
@@ -769,6 +757,9 @@ class Validator {
|
|
|
769
757
|
reason: [ErrorType.MultipleAuthNotSupported],
|
|
770
758
|
};
|
|
771
759
|
}
|
|
760
|
+
if (this.projectType === ProjectType.Copilot) {
|
|
761
|
+
return { isValid: true, reason: [] };
|
|
762
|
+
}
|
|
772
763
|
for (const auths of authSchemeArray) {
|
|
773
764
|
if (auths.length === 1) {
|
|
774
765
|
if ((this.options.allowAPIKeyAuth && Utils.isAPIKeyAuth(auths[0].authScheme)) ||
|
|
@@ -781,125 +772,6 @@ class Validator {
|
|
|
781
772
|
}
|
|
782
773
|
return { isValid: false, reason: [ErrorType.AuthTypeIsNotSupported] };
|
|
783
774
|
}
|
|
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
775
|
}
|
|
904
776
|
|
|
905
777
|
// Copyright (c) Microsoft Corporation.
|
|
@@ -909,7 +781,6 @@ class CopilotValidator extends Validator {
|
|
|
909
781
|
this.projectType = ProjectType.Copilot;
|
|
910
782
|
this.options = options;
|
|
911
783
|
this.spec = spec;
|
|
912
|
-
this.checkCircularReference();
|
|
913
784
|
}
|
|
914
785
|
validateSpec() {
|
|
915
786
|
const result = { errors: [], warnings: [] };
|
|
@@ -935,10 +806,6 @@ class CopilotValidator extends Validator {
|
|
|
935
806
|
if (!methodAndPathResult.isValid) {
|
|
936
807
|
return methodAndPathResult;
|
|
937
808
|
}
|
|
938
|
-
const circularReferenceResult = this.validateCircularReference(method, path);
|
|
939
|
-
if (!circularReferenceResult.isValid) {
|
|
940
|
-
return circularReferenceResult;
|
|
941
|
-
}
|
|
942
809
|
const operationObject = this.spec.paths[path][method];
|
|
943
810
|
// validate auth
|
|
944
811
|
const authCheckResult = this.validateAuth(method, path);
|
|
@@ -950,24 +817,6 @@ class CopilotValidator extends Validator {
|
|
|
950
817
|
// validate server
|
|
951
818
|
const validateServerResult = this.validateServer(method, path);
|
|
952
819
|
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
820
|
if (result.reason.length > 0) {
|
|
972
821
|
result.isValid = false;
|
|
973
822
|
}
|
|
@@ -1059,6 +908,108 @@ class SMEValidator extends Validator {
|
|
|
1059
908
|
}
|
|
1060
909
|
return result;
|
|
1061
910
|
}
|
|
911
|
+
validateResponse(method, path) {
|
|
912
|
+
const result = { isValid: true, reason: [] };
|
|
913
|
+
const operationObject = this.spec.paths[path][method];
|
|
914
|
+
const { json, multipleMediaType } = Utils.getResponseJson(operationObject);
|
|
915
|
+
// only support response body only contains “application/json” content type
|
|
916
|
+
if (multipleMediaType) {
|
|
917
|
+
result.reason.push(ErrorType.ResponseContainMultipleMediaTypes);
|
|
918
|
+
}
|
|
919
|
+
else if (Object.keys(json).length === 0) {
|
|
920
|
+
// response body should not be empty
|
|
921
|
+
result.reason.push(ErrorType.ResponseJsonIsEmpty);
|
|
922
|
+
}
|
|
923
|
+
return result;
|
|
924
|
+
}
|
|
925
|
+
checkPostBodySchema(schema, isRequired = false) {
|
|
926
|
+
var _a;
|
|
927
|
+
const paramResult = {
|
|
928
|
+
requiredNum: 0,
|
|
929
|
+
optionalNum: 0,
|
|
930
|
+
isValid: true,
|
|
931
|
+
reason: [],
|
|
932
|
+
};
|
|
933
|
+
if (Object.keys(schema).length === 0) {
|
|
934
|
+
return paramResult;
|
|
935
|
+
}
|
|
936
|
+
const isRequiredWithoutDefault = isRequired && schema.default === undefined;
|
|
937
|
+
const isCopilot = this.projectType === ProjectType.Copilot;
|
|
938
|
+
if (schema.type === "string" ||
|
|
939
|
+
schema.type === "integer" ||
|
|
940
|
+
schema.type === "boolean" ||
|
|
941
|
+
schema.type === "number") {
|
|
942
|
+
if (isRequiredWithoutDefault) {
|
|
943
|
+
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
944
|
+
}
|
|
945
|
+
else {
|
|
946
|
+
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
else if (Utils.isObjectSchema(schema)) {
|
|
950
|
+
const { properties } = schema;
|
|
951
|
+
for (const property in properties) {
|
|
952
|
+
let isRequired = false;
|
|
953
|
+
if (schema.required && ((_a = schema.required) === null || _a === void 0 ? void 0 : _a.indexOf(property)) >= 0) {
|
|
954
|
+
isRequired = true;
|
|
955
|
+
}
|
|
956
|
+
const result = this.checkPostBodySchema(properties[property], isRequired);
|
|
957
|
+
paramResult.requiredNum += result.requiredNum;
|
|
958
|
+
paramResult.optionalNum += result.optionalNum;
|
|
959
|
+
paramResult.isValid = paramResult.isValid && result.isValid;
|
|
960
|
+
paramResult.reason.push(...result.reason);
|
|
961
|
+
}
|
|
962
|
+
}
|
|
963
|
+
else {
|
|
964
|
+
if (isRequiredWithoutDefault && !isCopilot) {
|
|
965
|
+
paramResult.isValid = false;
|
|
966
|
+
paramResult.reason.push(ErrorType.PostBodyContainsRequiredUnsupportedSchema);
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
return paramResult;
|
|
970
|
+
}
|
|
971
|
+
checkParamSchema(paramObject) {
|
|
972
|
+
const paramResult = {
|
|
973
|
+
requiredNum: 0,
|
|
974
|
+
optionalNum: 0,
|
|
975
|
+
isValid: true,
|
|
976
|
+
reason: [],
|
|
977
|
+
};
|
|
978
|
+
if (!paramObject) {
|
|
979
|
+
return paramResult;
|
|
980
|
+
}
|
|
981
|
+
for (let i = 0; i < paramObject.length; i++) {
|
|
982
|
+
const param = paramObject[i];
|
|
983
|
+
const schema = param.schema;
|
|
984
|
+
const isRequiredWithoutDefault = param.required && schema.default === undefined;
|
|
985
|
+
if (param.in === "header" || param.in === "cookie") {
|
|
986
|
+
if (isRequiredWithoutDefault) {
|
|
987
|
+
paramResult.isValid = false;
|
|
988
|
+
paramResult.reason.push(ErrorType.ParamsContainRequiredUnsupportedSchema);
|
|
989
|
+
}
|
|
990
|
+
continue;
|
|
991
|
+
}
|
|
992
|
+
if (schema.type !== "boolean" &&
|
|
993
|
+
schema.type !== "string" &&
|
|
994
|
+
schema.type !== "number" &&
|
|
995
|
+
schema.type !== "integer") {
|
|
996
|
+
if (isRequiredWithoutDefault) {
|
|
997
|
+
paramResult.isValid = false;
|
|
998
|
+
paramResult.reason.push(ErrorType.ParamsContainRequiredUnsupportedSchema);
|
|
999
|
+
}
|
|
1000
|
+
continue;
|
|
1001
|
+
}
|
|
1002
|
+
if (param.in === "query" || param.in === "path") {
|
|
1003
|
+
if (isRequiredWithoutDefault) {
|
|
1004
|
+
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
1005
|
+
}
|
|
1006
|
+
else {
|
|
1007
|
+
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
return paramResult;
|
|
1012
|
+
}
|
|
1062
1013
|
validateParamCount(postBodyResult, paramResult) {
|
|
1063
1014
|
const result = { isValid: true, reason: [] };
|
|
1064
1015
|
const totalRequiredParams = postBodyResult.requiredNum + paramResult.requiredNum;
|
|
@@ -1348,20 +1299,157 @@ class SpecParser {
|
|
|
1348
1299
|
}
|
|
1349
1300
|
}
|
|
1350
1301
|
|
|
1302
|
+
// Copyright (c) Microsoft Corporation.
|
|
1303
|
+
class JsonDataGenerator {
|
|
1304
|
+
static generate(schema) {
|
|
1305
|
+
return this.generateMockData(schema);
|
|
1306
|
+
}
|
|
1307
|
+
static generateMockData(schema) {
|
|
1308
|
+
if (this.visitedSchemas.has(schema)) {
|
|
1309
|
+
return null; // Prevent circular reference
|
|
1310
|
+
}
|
|
1311
|
+
this.visitedSchemas.add(schema);
|
|
1312
|
+
let result;
|
|
1313
|
+
if (schema.anyOf) {
|
|
1314
|
+
// Select the first schema in anyOf
|
|
1315
|
+
const selectedSchema = schema.anyOf[0];
|
|
1316
|
+
result = this.generateMockData(selectedSchema);
|
|
1317
|
+
}
|
|
1318
|
+
else if (schema.oneOf) {
|
|
1319
|
+
// Select the first schema in oneOf
|
|
1320
|
+
const selectedSchema = schema.oneOf[0];
|
|
1321
|
+
result = this.generateMockData(selectedSchema);
|
|
1322
|
+
}
|
|
1323
|
+
else if (schema.allOf) {
|
|
1324
|
+
// merge all schemas in allOf
|
|
1325
|
+
result = {};
|
|
1326
|
+
for (const subschema of schema.allOf) {
|
|
1327
|
+
const data = this.generateMockData(subschema);
|
|
1328
|
+
result = Object.assign(Object.assign({}, result), data);
|
|
1329
|
+
}
|
|
1330
|
+
}
|
|
1331
|
+
else {
|
|
1332
|
+
switch (schema.type) {
|
|
1333
|
+
case "string":
|
|
1334
|
+
if (schema.example !== undefined) {
|
|
1335
|
+
result = schema.example;
|
|
1336
|
+
}
|
|
1337
|
+
else if (schema.format) {
|
|
1338
|
+
switch (schema.format) {
|
|
1339
|
+
case "date-time":
|
|
1340
|
+
result = "2024-11-01T05:25:43.593Z";
|
|
1341
|
+
break;
|
|
1342
|
+
case "email":
|
|
1343
|
+
result = "example@example.com";
|
|
1344
|
+
break;
|
|
1345
|
+
case "uuid":
|
|
1346
|
+
result = "123e4567-e89b-12d3-a456-426614174000";
|
|
1347
|
+
break;
|
|
1348
|
+
case "ipv4":
|
|
1349
|
+
result = "192.168.0.1";
|
|
1350
|
+
break;
|
|
1351
|
+
case "ipv6":
|
|
1352
|
+
result = "2001:0db8:85a3:0000:0000:8a2e:0370:7334";
|
|
1353
|
+
break;
|
|
1354
|
+
default:
|
|
1355
|
+
result = "example string";
|
|
1356
|
+
}
|
|
1357
|
+
}
|
|
1358
|
+
else {
|
|
1359
|
+
result = "example string";
|
|
1360
|
+
}
|
|
1361
|
+
break;
|
|
1362
|
+
case "number":
|
|
1363
|
+
if (schema.example !== undefined) {
|
|
1364
|
+
result = schema.example;
|
|
1365
|
+
}
|
|
1366
|
+
else if (schema.format) {
|
|
1367
|
+
switch (schema.format) {
|
|
1368
|
+
case "float":
|
|
1369
|
+
result = 3.14;
|
|
1370
|
+
break;
|
|
1371
|
+
case "double":
|
|
1372
|
+
result = 3.14159;
|
|
1373
|
+
break;
|
|
1374
|
+
default:
|
|
1375
|
+
result = 123;
|
|
1376
|
+
}
|
|
1377
|
+
}
|
|
1378
|
+
else {
|
|
1379
|
+
result = 123;
|
|
1380
|
+
}
|
|
1381
|
+
break;
|
|
1382
|
+
case "integer":
|
|
1383
|
+
if (schema.example !== undefined) {
|
|
1384
|
+
result = schema.example;
|
|
1385
|
+
}
|
|
1386
|
+
else if (schema.format) {
|
|
1387
|
+
switch (schema.format) {
|
|
1388
|
+
case "int32":
|
|
1389
|
+
result = 123456;
|
|
1390
|
+
break;
|
|
1391
|
+
case "int64":
|
|
1392
|
+
result = 123456789;
|
|
1393
|
+
break;
|
|
1394
|
+
default:
|
|
1395
|
+
result = 123;
|
|
1396
|
+
}
|
|
1397
|
+
}
|
|
1398
|
+
else {
|
|
1399
|
+
result = 123;
|
|
1400
|
+
}
|
|
1401
|
+
break;
|
|
1402
|
+
case "boolean":
|
|
1403
|
+
result = schema.example !== undefined ? schema.example : true;
|
|
1404
|
+
break;
|
|
1405
|
+
case "array":
|
|
1406
|
+
result = [this.generateMockData(schema.items)];
|
|
1407
|
+
break;
|
|
1408
|
+
case "object":
|
|
1409
|
+
result = {};
|
|
1410
|
+
if (schema.properties) {
|
|
1411
|
+
for (const key in schema.properties) {
|
|
1412
|
+
result[key] = this.generateMockData(schema.properties[key]);
|
|
1413
|
+
}
|
|
1414
|
+
}
|
|
1415
|
+
break;
|
|
1416
|
+
default:
|
|
1417
|
+
result = schema.example || null;
|
|
1418
|
+
}
|
|
1419
|
+
}
|
|
1420
|
+
this.visitedSchemas.delete(schema);
|
|
1421
|
+
return result;
|
|
1422
|
+
}
|
|
1423
|
+
}
|
|
1424
|
+
JsonDataGenerator.visitedSchemas = new Set();
|
|
1425
|
+
|
|
1351
1426
|
// Copyright (c) Microsoft Corporation.
|
|
1352
1427
|
class AdaptiveCardGenerator {
|
|
1353
|
-
static generateAdaptiveCard(operationItem, allowMultipleMediaType = false) {
|
|
1428
|
+
static generateAdaptiveCard(operationItem, allowMultipleMediaType = false, maxElementCount = Number.MAX_SAFE_INTEGER) {
|
|
1354
1429
|
try {
|
|
1355
1430
|
const { json } = Utils.getResponseJson(operationItem, allowMultipleMediaType);
|
|
1356
1431
|
let cardBody = [];
|
|
1432
|
+
let jsonData = {};
|
|
1433
|
+
const warnings = [];
|
|
1434
|
+
const operationId = operationItem.operationId;
|
|
1357
1435
|
let schema = json.schema;
|
|
1358
1436
|
let jsonPath = "$";
|
|
1359
1437
|
if (schema && Object.keys(schema).length > 0) {
|
|
1438
|
+
try {
|
|
1439
|
+
jsonData = JsonDataGenerator.generate(schema);
|
|
1440
|
+
}
|
|
1441
|
+
catch (err) {
|
|
1442
|
+
warnings.push({
|
|
1443
|
+
type: WarningType.GenerateJsonDataFailed,
|
|
1444
|
+
content: Utils.format(ConstantString.GenerateJsonDataFailed, operationId, err.toString()),
|
|
1445
|
+
data: operationId,
|
|
1446
|
+
});
|
|
1447
|
+
}
|
|
1360
1448
|
jsonPath = AdaptiveCardGenerator.getResponseJsonPathFromSchema(schema);
|
|
1361
1449
|
if (jsonPath !== "$") {
|
|
1362
1450
|
schema = schema.properties[jsonPath];
|
|
1363
1451
|
}
|
|
1364
|
-
cardBody = AdaptiveCardGenerator.generateCardFromResponse(schema, "");
|
|
1452
|
+
cardBody = AdaptiveCardGenerator.generateCardFromResponse(schema, "", "", maxElementCount);
|
|
1365
1453
|
}
|
|
1366
1454
|
// if no schema, try to use example value
|
|
1367
1455
|
if (cardBody.length === 0 && (json.examples || json.example)) {
|
|
@@ -1389,16 +1477,20 @@ class AdaptiveCardGenerator {
|
|
|
1389
1477
|
version: ConstantString.AdaptiveCardVersion,
|
|
1390
1478
|
body: cardBody,
|
|
1391
1479
|
};
|
|
1392
|
-
return [fullCard, jsonPath];
|
|
1480
|
+
return [fullCard, jsonPath, jsonData, warnings];
|
|
1393
1481
|
}
|
|
1394
1482
|
catch (err) {
|
|
1395
1483
|
throw new SpecParserError(err.toString(), ErrorType.GenerateAdaptiveCardFailed);
|
|
1396
1484
|
}
|
|
1397
1485
|
}
|
|
1398
|
-
static generateCardFromResponse(schema, name, parentArrayName = "") {
|
|
1486
|
+
static generateCardFromResponse(schema, name, parentArrayName = "", maxElementCount = Number.MAX_SAFE_INTEGER, counter = { count: 0 }) {
|
|
1487
|
+
if (counter.count >= maxElementCount) {
|
|
1488
|
+
return [];
|
|
1489
|
+
}
|
|
1399
1490
|
if (schema.type === "array") {
|
|
1400
1491
|
// schema.items can be arbitrary object: schema { type: array, items: {} }
|
|
1401
1492
|
if (Object.keys(schema.items).length === 0) {
|
|
1493
|
+
counter.count++;
|
|
1402
1494
|
return [
|
|
1403
1495
|
{
|
|
1404
1496
|
type: ConstantString.TextBlockType,
|
|
@@ -1407,7 +1499,7 @@ class AdaptiveCardGenerator {
|
|
|
1407
1499
|
},
|
|
1408
1500
|
];
|
|
1409
1501
|
}
|
|
1410
|
-
const obj = AdaptiveCardGenerator.generateCardFromResponse(schema.items, "", name);
|
|
1502
|
+
const obj = AdaptiveCardGenerator.generateCardFromResponse(schema.items, "", name, maxElementCount, counter);
|
|
1411
1503
|
const template = {
|
|
1412
1504
|
type: ConstantString.ContainerType,
|
|
1413
1505
|
$data: name ? `\${${name}}` : "${$root}",
|
|
@@ -1417,11 +1509,11 @@ class AdaptiveCardGenerator {
|
|
|
1417
1509
|
return [template];
|
|
1418
1510
|
}
|
|
1419
1511
|
// some schema may not contain type but contain properties
|
|
1420
|
-
if (
|
|
1512
|
+
if (Utils.isObjectSchema(schema)) {
|
|
1421
1513
|
const { properties } = schema;
|
|
1422
1514
|
const result = [];
|
|
1423
1515
|
for (const property in properties) {
|
|
1424
|
-
const obj = AdaptiveCardGenerator.generateCardFromResponse(properties[property], name ? `${name}.${property}` : property, parentArrayName);
|
|
1516
|
+
const obj = AdaptiveCardGenerator.generateCardFromResponse(properties[property], name ? `${name}.${property}` : property, parentArrayName, maxElementCount, counter);
|
|
1425
1517
|
result.push(...obj);
|
|
1426
1518
|
}
|
|
1427
1519
|
if (schema.additionalProperties) {
|
|
@@ -1434,6 +1526,7 @@ class AdaptiveCardGenerator {
|
|
|
1434
1526
|
schema.type === "integer" ||
|
|
1435
1527
|
schema.type === "boolean" ||
|
|
1436
1528
|
schema.type === "number") {
|
|
1529
|
+
counter.count++;
|
|
1437
1530
|
if (!AdaptiveCardGenerator.isImageUrlProperty(schema, name, parentArrayName)) {
|
|
1438
1531
|
// string in root: "ddd"
|
|
1439
1532
|
let text = "result: ${$root}";
|
|
@@ -1458,24 +1551,17 @@ class AdaptiveCardGenerator {
|
|
|
1458
1551
|
];
|
|
1459
1552
|
}
|
|
1460
1553
|
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
|
-
}
|
|
1554
|
+
const url = name ? `\${${name}}` : "${$data}";
|
|
1555
|
+
const condition = name
|
|
1556
|
+
? `\${${name} != null && ${name} != ''}`
|
|
1557
|
+
: "${$data != null && $data != ''}";
|
|
1558
|
+
return [
|
|
1559
|
+
{
|
|
1560
|
+
type: "Image",
|
|
1561
|
+
url,
|
|
1562
|
+
$when: condition,
|
|
1563
|
+
},
|
|
1564
|
+
];
|
|
1479
1565
|
}
|
|
1480
1566
|
}
|
|
1481
1567
|
if (schema.oneOf || schema.anyOf || schema.not || schema.allOf) {
|
|
@@ -1485,7 +1571,7 @@ class AdaptiveCardGenerator {
|
|
|
1485
1571
|
}
|
|
1486
1572
|
// Find the first array property in the response schema object with the well-known name
|
|
1487
1573
|
static getResponseJsonPathFromSchema(schema) {
|
|
1488
|
-
if (
|
|
1574
|
+
if (Utils.isObjectSchema(schema)) {
|
|
1489
1575
|
const { properties } = schema;
|
|
1490
1576
|
for (const property in properties) {
|
|
1491
1577
|
const schema = properties[property];
|