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