@microsoft/m365-spec-parser 0.2.3 → 0.2.4-alpha.679dc7df6.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.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",
|
|
@@ -187,16 +185,8 @@ ConstantString.PluginManifestSchema = "https://developer.microsoft.com/json-sche
|
|
|
187
185
|
|
|
188
186
|
// Copyright (c) Microsoft Corporation.
|
|
189
187
|
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;
|
|
188
|
+
static isObjectSchema(schema) {
|
|
189
|
+
return schema.type === "object" || (!schema.type && !!schema.properties);
|
|
200
190
|
}
|
|
201
191
|
static containMultipleMediaTypes(bodyObject) {
|
|
202
192
|
return Object.keys((bodyObject === null || bodyObject === void 0 ? void 0 : bodyObject.content) || {}).length > 1;
|
|
@@ -212,6 +202,23 @@ class Utils {
|
|
|
212
202
|
authScheme.flows &&
|
|
213
203
|
authScheme.flows.authorizationCode);
|
|
214
204
|
}
|
|
205
|
+
static isNotSupportedAuth(authSchemeArray) {
|
|
206
|
+
if (authSchemeArray.length === 0) {
|
|
207
|
+
return false;
|
|
208
|
+
}
|
|
209
|
+
if (authSchemeArray.length > 0 && authSchemeArray.every((auths) => auths.length > 1)) {
|
|
210
|
+
return true;
|
|
211
|
+
}
|
|
212
|
+
for (const auths of authSchemeArray) {
|
|
213
|
+
if (auths.length === 1) {
|
|
214
|
+
if (Utils.isOAuthWithAuthCodeFlow(auths[0].authScheme) ||
|
|
215
|
+
Utils.isBearerTokenAuth(auths[0].authScheme)) {
|
|
216
|
+
return false;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
return true;
|
|
221
|
+
}
|
|
215
222
|
static getAuthArray(securities, spec) {
|
|
216
223
|
var _a;
|
|
217
224
|
const result = [];
|
|
@@ -236,6 +243,20 @@ class Utils {
|
|
|
236
243
|
result.sort((a, b) => a[0].name.localeCompare(b[0].name));
|
|
237
244
|
return result;
|
|
238
245
|
}
|
|
246
|
+
static getAuthMap(spec) {
|
|
247
|
+
const authMap = {};
|
|
248
|
+
for (const url in spec.paths) {
|
|
249
|
+
for (const method in spec.paths[url]) {
|
|
250
|
+
const operation = spec.paths[url][method];
|
|
251
|
+
const authArray = Utils.getAuthArray(operation.security, spec);
|
|
252
|
+
if (authArray && authArray.length > 0) {
|
|
253
|
+
const currentAuth = authArray[0][0];
|
|
254
|
+
authMap[operation.operationId] = currentAuth;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
return authMap;
|
|
259
|
+
}
|
|
239
260
|
static getAuthInfo(spec) {
|
|
240
261
|
let authInfo = undefined;
|
|
241
262
|
for (const url in spec.paths) {
|
|
@@ -264,27 +285,33 @@ class Utils {
|
|
|
264
285
|
let multipleMediaType = false;
|
|
265
286
|
for (const code of ConstantString.ResponseCodeFor20X) {
|
|
266
287
|
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
|
-
}
|
|
288
|
+
if (!responseObject) {
|
|
289
|
+
continue;
|
|
290
|
+
}
|
|
291
|
+
multipleMediaType = Utils.containMultipleMediaTypes(responseObject);
|
|
292
|
+
if (!allowMultipleMediaType && multipleMediaType) {
|
|
293
|
+
json = {};
|
|
294
|
+
continue;
|
|
295
|
+
}
|
|
296
|
+
const mediaObj = Utils.getJsonContentType(responseObject);
|
|
297
|
+
if (Object.keys(mediaObj).length > 0) {
|
|
298
|
+
json = mediaObj;
|
|
299
|
+
return { json, multipleMediaType };
|
|
284
300
|
}
|
|
285
301
|
}
|
|
286
302
|
return { json, multipleMediaType };
|
|
287
303
|
}
|
|
304
|
+
static getJsonContentType(responseObject) {
|
|
305
|
+
if (responseObject.content) {
|
|
306
|
+
for (const contentType of Object.keys(responseObject.content)) {
|
|
307
|
+
// json media type can also be "application/json; charset=utf-8"
|
|
308
|
+
if (contentType.indexOf("application/json") >= 0) {
|
|
309
|
+
return responseObject.content[contentType];
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
return {};
|
|
314
|
+
}
|
|
288
315
|
static convertPathToCamelCase(path) {
|
|
289
316
|
const pathSegments = path.split(/[./{]/);
|
|
290
317
|
const camelCaseSegments = pathSegments.map((segment) => {
|
|
@@ -320,7 +347,7 @@ class Utils {
|
|
|
320
347
|
}
|
|
321
348
|
return newStr;
|
|
322
349
|
}
|
|
323
|
-
static checkServerUrl(servers) {
|
|
350
|
+
static checkServerUrl(servers, allowHttp = false) {
|
|
324
351
|
const errors = [];
|
|
325
352
|
let serverUrl;
|
|
326
353
|
try {
|
|
@@ -343,8 +370,7 @@ class Utils {
|
|
|
343
370
|
data: servers,
|
|
344
371
|
});
|
|
345
372
|
}
|
|
346
|
-
else if (protocol !== "https:") {
|
|
347
|
-
// Http server url is not supported
|
|
373
|
+
else if (protocol !== "https:" && !(protocol === "http:" && allowHttp)) {
|
|
348
374
|
const protocolString = protocol.slice(0, -1);
|
|
349
375
|
errors.push({
|
|
350
376
|
type: ErrorType.UrlProtocolNotSupported,
|
|
@@ -360,10 +386,11 @@ class Utils {
|
|
|
360
386
|
let hasTopLevelServers = false;
|
|
361
387
|
let hasPathLevelServers = false;
|
|
362
388
|
let hasOperationLevelServers = false;
|
|
389
|
+
const allowHttp = options.projectType === ProjectType.Copilot;
|
|
363
390
|
if (spec.servers && spec.servers.length >= 1) {
|
|
364
391
|
hasTopLevelServers = true;
|
|
365
392
|
// for multiple server, we only use the first url
|
|
366
|
-
const serverErrors = Utils.checkServerUrl(spec.servers);
|
|
393
|
+
const serverErrors = Utils.checkServerUrl(spec.servers, allowHttp);
|
|
367
394
|
errors.push(...serverErrors);
|
|
368
395
|
}
|
|
369
396
|
const paths = spec.paths;
|
|
@@ -371,7 +398,7 @@ class Utils {
|
|
|
371
398
|
const methods = paths[path];
|
|
372
399
|
if ((methods === null || methods === void 0 ? void 0 : methods.servers) && methods.servers.length >= 1) {
|
|
373
400
|
hasPathLevelServers = true;
|
|
374
|
-
const serverErrors = Utils.checkServerUrl(methods.servers);
|
|
401
|
+
const serverErrors = Utils.checkServerUrl(methods.servers, allowHttp);
|
|
375
402
|
errors.push(...serverErrors);
|
|
376
403
|
}
|
|
377
404
|
for (const method in methods) {
|
|
@@ -379,7 +406,7 @@ class Utils {
|
|
|
379
406
|
if (((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && operationObject) {
|
|
380
407
|
if ((operationObject === null || operationObject === void 0 ? void 0 : operationObject.servers) && operationObject.servers.length >= 1) {
|
|
381
408
|
hasOperationLevelServers = true;
|
|
382
|
-
const serverErrors = Utils.checkServerUrl(operationObject.servers);
|
|
409
|
+
const serverErrors = Utils.checkServerUrl(operationObject.servers, allowHttp);
|
|
383
410
|
errors.push(...serverErrors);
|
|
384
411
|
}
|
|
385
412
|
}
|
|
@@ -426,7 +453,7 @@ class Utils {
|
|
|
426
453
|
optionalParams.push(parameter);
|
|
427
454
|
}
|
|
428
455
|
}
|
|
429
|
-
else if (schema
|
|
456
|
+
else if (Utils.isObjectSchema(schema)) {
|
|
430
457
|
const { properties } = schema;
|
|
431
458
|
for (const property in properties) {
|
|
432
459
|
let isRequired = false;
|
|
@@ -540,29 +567,6 @@ class Utils {
|
|
|
540
567
|
const serverUrl = operationServer || methodServer || rootServer;
|
|
541
568
|
return serverUrl;
|
|
542
569
|
}
|
|
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
570
|
}
|
|
567
571
|
|
|
568
572
|
// Copyright (c) Microsoft Corporation.
|
|
@@ -691,22 +695,6 @@ class Validator {
|
|
|
691
695
|
}
|
|
692
696
|
return result;
|
|
693
697
|
}
|
|
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
698
|
validateServer(method, path) {
|
|
711
699
|
const result = { isValid: true, reason: [] };
|
|
712
700
|
const serverObj = Utils.getServerObject(this.spec, method, path);
|
|
@@ -715,8 +703,8 @@ class Validator {
|
|
|
715
703
|
result.reason.push(ErrorType.NoServerInformation);
|
|
716
704
|
}
|
|
717
705
|
else {
|
|
718
|
-
|
|
719
|
-
const serverValidateResult = Utils.checkServerUrl([serverObj]);
|
|
706
|
+
const allowHttp = this.projectType === ProjectType.Copilot;
|
|
707
|
+
const serverValidateResult = Utils.checkServerUrl([serverObj], allowHttp);
|
|
720
708
|
result.reason.push(...serverValidateResult.map((item) => item.type));
|
|
721
709
|
}
|
|
722
710
|
return result;
|
|
@@ -739,6 +727,9 @@ class Validator {
|
|
|
739
727
|
reason: [ErrorType.MultipleAuthNotSupported],
|
|
740
728
|
};
|
|
741
729
|
}
|
|
730
|
+
if (this.projectType === ProjectType.Copilot) {
|
|
731
|
+
return { isValid: true, reason: [] };
|
|
732
|
+
}
|
|
742
733
|
for (const auths of authSchemeArray) {
|
|
743
734
|
if (auths.length === 1) {
|
|
744
735
|
if ((this.options.allowAPIKeyAuth && Utils.isAPIKeyAuth(auths[0].authScheme)) ||
|
|
@@ -751,125 +742,6 @@ class Validator {
|
|
|
751
742
|
}
|
|
752
743
|
return { isValid: false, reason: [ErrorType.AuthTypeIsNotSupported] };
|
|
753
744
|
}
|
|
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
745
|
}
|
|
874
746
|
|
|
875
747
|
// Copyright (c) Microsoft Corporation.
|
|
@@ -879,7 +751,6 @@ class CopilotValidator extends Validator {
|
|
|
879
751
|
this.projectType = ProjectType.Copilot;
|
|
880
752
|
this.options = options;
|
|
881
753
|
this.spec = spec;
|
|
882
|
-
this.checkCircularReference();
|
|
883
754
|
}
|
|
884
755
|
validateSpec() {
|
|
885
756
|
const result = { errors: [], warnings: [] };
|
|
@@ -905,10 +776,6 @@ class CopilotValidator extends Validator {
|
|
|
905
776
|
if (!methodAndPathResult.isValid) {
|
|
906
777
|
return methodAndPathResult;
|
|
907
778
|
}
|
|
908
|
-
const circularReferenceResult = this.validateCircularReference(method, path);
|
|
909
|
-
if (!circularReferenceResult.isValid) {
|
|
910
|
-
return circularReferenceResult;
|
|
911
|
-
}
|
|
912
779
|
const operationObject = this.spec.paths[path][method];
|
|
913
780
|
// validate auth
|
|
914
781
|
const authCheckResult = this.validateAuth(method, path);
|
|
@@ -920,24 +787,6 @@ class CopilotValidator extends Validator {
|
|
|
920
787
|
// validate server
|
|
921
788
|
const validateServerResult = this.validateServer(method, path);
|
|
922
789
|
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
790
|
if (result.reason.length > 0) {
|
|
942
791
|
result.isValid = false;
|
|
943
792
|
}
|
|
@@ -1029,6 +878,108 @@ class SMEValidator extends Validator {
|
|
|
1029
878
|
}
|
|
1030
879
|
return result;
|
|
1031
880
|
}
|
|
881
|
+
validateResponse(method, path) {
|
|
882
|
+
const result = { isValid: true, reason: [] };
|
|
883
|
+
const operationObject = this.spec.paths[path][method];
|
|
884
|
+
const { json, multipleMediaType } = Utils.getResponseJson(operationObject);
|
|
885
|
+
// only support response body only contains “application/json” content type
|
|
886
|
+
if (multipleMediaType) {
|
|
887
|
+
result.reason.push(ErrorType.ResponseContainMultipleMediaTypes);
|
|
888
|
+
}
|
|
889
|
+
else if (Object.keys(json).length === 0) {
|
|
890
|
+
// response body should not be empty
|
|
891
|
+
result.reason.push(ErrorType.ResponseJsonIsEmpty);
|
|
892
|
+
}
|
|
893
|
+
return result;
|
|
894
|
+
}
|
|
895
|
+
checkPostBodySchema(schema, isRequired = false) {
|
|
896
|
+
var _a;
|
|
897
|
+
const paramResult = {
|
|
898
|
+
requiredNum: 0,
|
|
899
|
+
optionalNum: 0,
|
|
900
|
+
isValid: true,
|
|
901
|
+
reason: [],
|
|
902
|
+
};
|
|
903
|
+
if (Object.keys(schema).length === 0) {
|
|
904
|
+
return paramResult;
|
|
905
|
+
}
|
|
906
|
+
const isRequiredWithoutDefault = isRequired && schema.default === undefined;
|
|
907
|
+
const isCopilot = this.projectType === ProjectType.Copilot;
|
|
908
|
+
if (schema.type === "string" ||
|
|
909
|
+
schema.type === "integer" ||
|
|
910
|
+
schema.type === "boolean" ||
|
|
911
|
+
schema.type === "number") {
|
|
912
|
+
if (isRequiredWithoutDefault) {
|
|
913
|
+
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
914
|
+
}
|
|
915
|
+
else {
|
|
916
|
+
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
else if (Utils.isObjectSchema(schema)) {
|
|
920
|
+
const { properties } = schema;
|
|
921
|
+
for (const property in properties) {
|
|
922
|
+
let isRequired = false;
|
|
923
|
+
if (schema.required && ((_a = schema.required) === null || _a === void 0 ? void 0 : _a.indexOf(property)) >= 0) {
|
|
924
|
+
isRequired = true;
|
|
925
|
+
}
|
|
926
|
+
const result = this.checkPostBodySchema(properties[property], isRequired);
|
|
927
|
+
paramResult.requiredNum += result.requiredNum;
|
|
928
|
+
paramResult.optionalNum += result.optionalNum;
|
|
929
|
+
paramResult.isValid = paramResult.isValid && result.isValid;
|
|
930
|
+
paramResult.reason.push(...result.reason);
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
else {
|
|
934
|
+
if (isRequiredWithoutDefault && !isCopilot) {
|
|
935
|
+
paramResult.isValid = false;
|
|
936
|
+
paramResult.reason.push(ErrorType.PostBodyContainsRequiredUnsupportedSchema);
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
return paramResult;
|
|
940
|
+
}
|
|
941
|
+
checkParamSchema(paramObject) {
|
|
942
|
+
const paramResult = {
|
|
943
|
+
requiredNum: 0,
|
|
944
|
+
optionalNum: 0,
|
|
945
|
+
isValid: true,
|
|
946
|
+
reason: [],
|
|
947
|
+
};
|
|
948
|
+
if (!paramObject) {
|
|
949
|
+
return paramResult;
|
|
950
|
+
}
|
|
951
|
+
for (let i = 0; i < paramObject.length; i++) {
|
|
952
|
+
const param = paramObject[i];
|
|
953
|
+
const schema = param.schema;
|
|
954
|
+
const isRequiredWithoutDefault = param.required && schema.default === undefined;
|
|
955
|
+
if (param.in === "header" || param.in === "cookie") {
|
|
956
|
+
if (isRequiredWithoutDefault) {
|
|
957
|
+
paramResult.isValid = false;
|
|
958
|
+
paramResult.reason.push(ErrorType.ParamsContainRequiredUnsupportedSchema);
|
|
959
|
+
}
|
|
960
|
+
continue;
|
|
961
|
+
}
|
|
962
|
+
if (schema.type !== "boolean" &&
|
|
963
|
+
schema.type !== "string" &&
|
|
964
|
+
schema.type !== "number" &&
|
|
965
|
+
schema.type !== "integer") {
|
|
966
|
+
if (isRequiredWithoutDefault) {
|
|
967
|
+
paramResult.isValid = false;
|
|
968
|
+
paramResult.reason.push(ErrorType.ParamsContainRequiredUnsupportedSchema);
|
|
969
|
+
}
|
|
970
|
+
continue;
|
|
971
|
+
}
|
|
972
|
+
if (param.in === "query" || param.in === "path") {
|
|
973
|
+
if (isRequiredWithoutDefault) {
|
|
974
|
+
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
975
|
+
}
|
|
976
|
+
else {
|
|
977
|
+
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
return paramResult;
|
|
982
|
+
}
|
|
1032
983
|
validateParamCount(postBodyResult, paramResult) {
|
|
1033
984
|
const result = { isValid: true, reason: [] };
|
|
1034
985
|
const totalRequiredParams = postBodyResult.requiredNum + paramResult.requiredNum;
|
|
@@ -1304,20 +1255,157 @@ class SpecParser {
|
|
|
1304
1255
|
}
|
|
1305
1256
|
}
|
|
1306
1257
|
|
|
1258
|
+
// Copyright (c) Microsoft Corporation.
|
|
1259
|
+
class JsonDataGenerator {
|
|
1260
|
+
static generate(schema) {
|
|
1261
|
+
return this.generateMockData(schema);
|
|
1262
|
+
}
|
|
1263
|
+
static generateMockData(schema) {
|
|
1264
|
+
if (this.visitedSchemas.has(schema)) {
|
|
1265
|
+
return null; // Prevent circular reference
|
|
1266
|
+
}
|
|
1267
|
+
this.visitedSchemas.add(schema);
|
|
1268
|
+
let result;
|
|
1269
|
+
if (schema.anyOf) {
|
|
1270
|
+
// Select the first schema in anyOf
|
|
1271
|
+
const selectedSchema = schema.anyOf[0];
|
|
1272
|
+
result = this.generateMockData(selectedSchema);
|
|
1273
|
+
}
|
|
1274
|
+
else if (schema.oneOf) {
|
|
1275
|
+
// Select the first schema in oneOf
|
|
1276
|
+
const selectedSchema = schema.oneOf[0];
|
|
1277
|
+
result = this.generateMockData(selectedSchema);
|
|
1278
|
+
}
|
|
1279
|
+
else if (schema.allOf) {
|
|
1280
|
+
// merge all schemas in allOf
|
|
1281
|
+
result = {};
|
|
1282
|
+
for (const subschema of schema.allOf) {
|
|
1283
|
+
const data = this.generateMockData(subschema);
|
|
1284
|
+
result = Object.assign(Object.assign({}, result), data);
|
|
1285
|
+
}
|
|
1286
|
+
}
|
|
1287
|
+
else {
|
|
1288
|
+
switch (schema.type) {
|
|
1289
|
+
case "string":
|
|
1290
|
+
if (schema.example !== undefined) {
|
|
1291
|
+
result = schema.example;
|
|
1292
|
+
}
|
|
1293
|
+
else if (schema.format) {
|
|
1294
|
+
switch (schema.format) {
|
|
1295
|
+
case "date-time":
|
|
1296
|
+
result = "2024-11-01T05:25:43.593Z";
|
|
1297
|
+
break;
|
|
1298
|
+
case "email":
|
|
1299
|
+
result = "example@example.com";
|
|
1300
|
+
break;
|
|
1301
|
+
case "uuid":
|
|
1302
|
+
result = "123e4567-e89b-12d3-a456-426614174000";
|
|
1303
|
+
break;
|
|
1304
|
+
case "ipv4":
|
|
1305
|
+
result = "192.168.0.1";
|
|
1306
|
+
break;
|
|
1307
|
+
case "ipv6":
|
|
1308
|
+
result = "2001:0db8:85a3:0000:0000:8a2e:0370:7334";
|
|
1309
|
+
break;
|
|
1310
|
+
default:
|
|
1311
|
+
result = "example string";
|
|
1312
|
+
}
|
|
1313
|
+
}
|
|
1314
|
+
else {
|
|
1315
|
+
result = "example string";
|
|
1316
|
+
}
|
|
1317
|
+
break;
|
|
1318
|
+
case "number":
|
|
1319
|
+
if (schema.example !== undefined) {
|
|
1320
|
+
result = schema.example;
|
|
1321
|
+
}
|
|
1322
|
+
else if (schema.format) {
|
|
1323
|
+
switch (schema.format) {
|
|
1324
|
+
case "float":
|
|
1325
|
+
result = 3.14;
|
|
1326
|
+
break;
|
|
1327
|
+
case "double":
|
|
1328
|
+
result = 3.14159;
|
|
1329
|
+
break;
|
|
1330
|
+
default:
|
|
1331
|
+
result = 123;
|
|
1332
|
+
}
|
|
1333
|
+
}
|
|
1334
|
+
else {
|
|
1335
|
+
result = 123;
|
|
1336
|
+
}
|
|
1337
|
+
break;
|
|
1338
|
+
case "integer":
|
|
1339
|
+
if (schema.example !== undefined) {
|
|
1340
|
+
result = schema.example;
|
|
1341
|
+
}
|
|
1342
|
+
else if (schema.format) {
|
|
1343
|
+
switch (schema.format) {
|
|
1344
|
+
case "int32":
|
|
1345
|
+
result = 123456;
|
|
1346
|
+
break;
|
|
1347
|
+
case "int64":
|
|
1348
|
+
result = 123456789;
|
|
1349
|
+
break;
|
|
1350
|
+
default:
|
|
1351
|
+
result = 123;
|
|
1352
|
+
}
|
|
1353
|
+
}
|
|
1354
|
+
else {
|
|
1355
|
+
result = 123;
|
|
1356
|
+
}
|
|
1357
|
+
break;
|
|
1358
|
+
case "boolean":
|
|
1359
|
+
result = schema.example !== undefined ? schema.example : true;
|
|
1360
|
+
break;
|
|
1361
|
+
case "array":
|
|
1362
|
+
result = [this.generateMockData(schema.items)];
|
|
1363
|
+
break;
|
|
1364
|
+
case "object":
|
|
1365
|
+
result = {};
|
|
1366
|
+
if (schema.properties) {
|
|
1367
|
+
for (const key in schema.properties) {
|
|
1368
|
+
result[key] = this.generateMockData(schema.properties[key]);
|
|
1369
|
+
}
|
|
1370
|
+
}
|
|
1371
|
+
break;
|
|
1372
|
+
default:
|
|
1373
|
+
result = schema.example || null;
|
|
1374
|
+
}
|
|
1375
|
+
}
|
|
1376
|
+
this.visitedSchemas.delete(schema);
|
|
1377
|
+
return result;
|
|
1378
|
+
}
|
|
1379
|
+
}
|
|
1380
|
+
JsonDataGenerator.visitedSchemas = new Set();
|
|
1381
|
+
|
|
1307
1382
|
// Copyright (c) Microsoft Corporation.
|
|
1308
1383
|
class AdaptiveCardGenerator {
|
|
1309
|
-
static generateAdaptiveCard(operationItem, allowMultipleMediaType = false) {
|
|
1384
|
+
static generateAdaptiveCard(operationItem, allowMultipleMediaType = false, maxElementCount = Number.MAX_SAFE_INTEGER) {
|
|
1310
1385
|
try {
|
|
1311
1386
|
const { json } = Utils.getResponseJson(operationItem, allowMultipleMediaType);
|
|
1312
1387
|
let cardBody = [];
|
|
1388
|
+
let jsonData = {};
|
|
1389
|
+
const warnings = [];
|
|
1390
|
+
const operationId = operationItem.operationId;
|
|
1313
1391
|
let schema = json.schema;
|
|
1314
1392
|
let jsonPath = "$";
|
|
1315
1393
|
if (schema && Object.keys(schema).length > 0) {
|
|
1394
|
+
try {
|
|
1395
|
+
jsonData = JsonDataGenerator.generate(schema);
|
|
1396
|
+
}
|
|
1397
|
+
catch (err) {
|
|
1398
|
+
warnings.push({
|
|
1399
|
+
type: WarningType.GenerateJsonDataFailed,
|
|
1400
|
+
content: Utils.format(ConstantString.GenerateJsonDataFailed, operationId, err.toString()),
|
|
1401
|
+
data: operationId,
|
|
1402
|
+
});
|
|
1403
|
+
}
|
|
1316
1404
|
jsonPath = AdaptiveCardGenerator.getResponseJsonPathFromSchema(schema);
|
|
1317
1405
|
if (jsonPath !== "$") {
|
|
1318
1406
|
schema = schema.properties[jsonPath];
|
|
1319
1407
|
}
|
|
1320
|
-
cardBody = AdaptiveCardGenerator.generateCardFromResponse(schema, "");
|
|
1408
|
+
cardBody = AdaptiveCardGenerator.generateCardFromResponse(schema, "", "", maxElementCount);
|
|
1321
1409
|
}
|
|
1322
1410
|
// if no schema, try to use example value
|
|
1323
1411
|
if (cardBody.length === 0 && (json.examples || json.example)) {
|
|
@@ -1345,16 +1433,20 @@ class AdaptiveCardGenerator {
|
|
|
1345
1433
|
version: ConstantString.AdaptiveCardVersion,
|
|
1346
1434
|
body: cardBody,
|
|
1347
1435
|
};
|
|
1348
|
-
return [fullCard, jsonPath];
|
|
1436
|
+
return [fullCard, jsonPath, jsonData, warnings];
|
|
1349
1437
|
}
|
|
1350
1438
|
catch (err) {
|
|
1351
1439
|
throw new SpecParserError(err.toString(), ErrorType.GenerateAdaptiveCardFailed);
|
|
1352
1440
|
}
|
|
1353
1441
|
}
|
|
1354
|
-
static generateCardFromResponse(schema, name, parentArrayName = "") {
|
|
1442
|
+
static generateCardFromResponse(schema, name, parentArrayName = "", maxElementCount = Number.MAX_SAFE_INTEGER, counter = { count: 0 }) {
|
|
1443
|
+
if (counter.count >= maxElementCount) {
|
|
1444
|
+
return [];
|
|
1445
|
+
}
|
|
1355
1446
|
if (schema.type === "array") {
|
|
1356
1447
|
// schema.items can be arbitrary object: schema { type: array, items: {} }
|
|
1357
1448
|
if (Object.keys(schema.items).length === 0) {
|
|
1449
|
+
counter.count++;
|
|
1358
1450
|
return [
|
|
1359
1451
|
{
|
|
1360
1452
|
type: ConstantString.TextBlockType,
|
|
@@ -1363,7 +1455,7 @@ class AdaptiveCardGenerator {
|
|
|
1363
1455
|
},
|
|
1364
1456
|
];
|
|
1365
1457
|
}
|
|
1366
|
-
const obj = AdaptiveCardGenerator.generateCardFromResponse(schema.items, "", name);
|
|
1458
|
+
const obj = AdaptiveCardGenerator.generateCardFromResponse(schema.items, "", name, maxElementCount, counter);
|
|
1367
1459
|
const template = {
|
|
1368
1460
|
type: ConstantString.ContainerType,
|
|
1369
1461
|
$data: name ? `\${${name}}` : "${$root}",
|
|
@@ -1373,11 +1465,11 @@ class AdaptiveCardGenerator {
|
|
|
1373
1465
|
return [template];
|
|
1374
1466
|
}
|
|
1375
1467
|
// some schema may not contain type but contain properties
|
|
1376
|
-
if (
|
|
1468
|
+
if (Utils.isObjectSchema(schema)) {
|
|
1377
1469
|
const { properties } = schema;
|
|
1378
1470
|
const result = [];
|
|
1379
1471
|
for (const property in properties) {
|
|
1380
|
-
const obj = AdaptiveCardGenerator.generateCardFromResponse(properties[property], name ? `${name}.${property}` : property, parentArrayName);
|
|
1472
|
+
const obj = AdaptiveCardGenerator.generateCardFromResponse(properties[property], name ? `${name}.${property}` : property, parentArrayName, maxElementCount, counter);
|
|
1381
1473
|
result.push(...obj);
|
|
1382
1474
|
}
|
|
1383
1475
|
if (schema.additionalProperties) {
|
|
@@ -1390,6 +1482,7 @@ class AdaptiveCardGenerator {
|
|
|
1390
1482
|
schema.type === "integer" ||
|
|
1391
1483
|
schema.type === "boolean" ||
|
|
1392
1484
|
schema.type === "number") {
|
|
1485
|
+
counter.count++;
|
|
1393
1486
|
if (!AdaptiveCardGenerator.isImageUrlProperty(schema, name, parentArrayName)) {
|
|
1394
1487
|
// string in root: "ddd"
|
|
1395
1488
|
let text = "result: ${$root}";
|
|
@@ -1414,24 +1507,17 @@ class AdaptiveCardGenerator {
|
|
|
1414
1507
|
];
|
|
1415
1508
|
}
|
|
1416
1509
|
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
|
-
}
|
|
1510
|
+
const url = name ? `\${${name}}` : "${$data}";
|
|
1511
|
+
const condition = name
|
|
1512
|
+
? `\${${name} != null && ${name} != ''}`
|
|
1513
|
+
: "${$data != null && $data != ''}";
|
|
1514
|
+
return [
|
|
1515
|
+
{
|
|
1516
|
+
type: "Image",
|
|
1517
|
+
url,
|
|
1518
|
+
$when: condition,
|
|
1519
|
+
},
|
|
1520
|
+
];
|
|
1435
1521
|
}
|
|
1436
1522
|
}
|
|
1437
1523
|
if (schema.oneOf || schema.anyOf || schema.not || schema.allOf) {
|
|
@@ -1441,7 +1527,7 @@ class AdaptiveCardGenerator {
|
|
|
1441
1527
|
}
|
|
1442
1528
|
// Find the first array property in the response schema object with the well-known name
|
|
1443
1529
|
static getResponseJsonPathFromSchema(schema) {
|
|
1444
|
-
if (
|
|
1530
|
+
if (Utils.isObjectSchema(schema)) {
|
|
1445
1531
|
const { properties } = schema;
|
|
1446
1532
|
for (const property in properties) {
|
|
1447
1533
|
const schema = properties[property];
|