@microsoft/m365-spec-parser 0.2.3-rc-hotfix.3 → 0.2.4-alpha.396238b05.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.esm2017.js +343 -253
- package/dist/index.esm2017.js.map +1 -1
- package/dist/index.esm2017.mjs +467 -328
- package/dist/index.esm2017.mjs.map +1 -1
- package/dist/index.esm5.js +343 -253
- package/dist/index.esm5.js.map +1 -1
- package/dist/index.node.cjs.js +467 -328
- package/dist/index.node.cjs.js.map +1 -1
- package/dist/src/adaptiveCardGenerator.d.ts +5 -3
- package/dist/src/constants.d.ts +5 -4
- package/dist/src/interfaces.d.ts +12 -3
- package/dist/src/jsonDataGenerator.d.ts +6 -0
- package/dist/src/manifestUpdater.d.ts +3 -3
- package/dist/src/utils.d.ts +7 -4
- package/package.json +3 -3
package/dist/index.esm2017.mjs
CHANGED
|
@@ -38,11 +38,8 @@ var ErrorType;
|
|
|
38
38
|
ErrorType["PostBodyContainMultipleMediaTypes"] = "post-body-contain-multiple-media-types";
|
|
39
39
|
ErrorType["ResponseContainMultipleMediaTypes"] = "response-contain-multiple-media-types";
|
|
40
40
|
ErrorType["ResponseJsonIsEmpty"] = "response-json-is-empty";
|
|
41
|
-
ErrorType["PostBodySchemaIsNotJson"] = "post-body-schema-is-not-json";
|
|
42
41
|
ErrorType["PostBodyContainsRequiredUnsupportedSchema"] = "post-body-contains-required-unsupported-schema";
|
|
43
42
|
ErrorType["ParamsContainRequiredUnsupportedSchema"] = "params-contain-required-unsupported-schema";
|
|
44
|
-
ErrorType["ParamsContainsNestedObject"] = "params-contains-nested-object";
|
|
45
|
-
ErrorType["RequestBodyContainsNestedObject"] = "request-body-contains-nested-object";
|
|
46
43
|
ErrorType["ExceededRequiredParamsLimit"] = "exceeded-required-params-limit";
|
|
47
44
|
ErrorType["NoParameter"] = "no-parameter";
|
|
48
45
|
ErrorType["NoAPIInfo"] = "no-api-info";
|
|
@@ -61,6 +58,9 @@ var WarningType;
|
|
|
61
58
|
WarningType["OperationOnlyContainsOptionalParam"] = "operation-only-contains-optional-param";
|
|
62
59
|
WarningType["ConvertSwaggerToOpenAPI"] = "convert-swagger-to-openapi";
|
|
63
60
|
WarningType["FuncDescriptionTooLong"] = "function-description-too-long";
|
|
61
|
+
WarningType["OperationIdContainsSpecialCharacters"] = "operationid-contains-special-characters";
|
|
62
|
+
WarningType["UnsupportedAuthType"] = "unsupported-auth-type";
|
|
63
|
+
WarningType["GenerateJsonDataFailed"] = "generate-json-data-failed";
|
|
64
64
|
WarningType["Unknown"] = "unknown";
|
|
65
65
|
})(WarningType || (WarningType = {}));
|
|
66
66
|
/**
|
|
@@ -98,25 +98,23 @@ ConstantString.ConvertSwaggerToOpenAPI = "The Swagger 2.0 file has been converte
|
|
|
98
98
|
ConstantString.SwaggerNotSupported = "Swagger 2.0 is not supported. Please convert to OpenAPI 3.0 manually before proceeding.";
|
|
99
99
|
ConstantString.SpecVersionNotSupported = "Unsupported OpenAPI version %s. Please use version 3.0.x.";
|
|
100
100
|
ConstantString.MultipleAuthNotSupported = "Multiple authentication methods are unsupported. Ensure all selected APIs use identical authentication.";
|
|
101
|
+
ConstantString.OperationIdContainsSpecialCharacters = "Operation id '%s' in OpenAPI description document contained special characters and was renamed to '%s'.";
|
|
102
|
+
ConstantString.AuthTypeIsNotSupported = "Unsupported authorization type in API '%s'. No authorization will be used.";
|
|
101
103
|
ConstantString.UnsupportedSchema = "Unsupported schema in %s %s: %s";
|
|
102
104
|
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.";
|
|
105
|
+
ConstantString.GenerateJsonDataFailed = "Failed to generate JSON data for api: %s due to %s.";
|
|
103
106
|
ConstantString.WrappedCardVersion = "devPreview";
|
|
104
107
|
ConstantString.WrappedCardSchema = "https://developer.microsoft.com/json-schemas/teams/vDevPreview/MicrosoftTeams.ResponseRenderingTemplate.schema.json";
|
|
105
108
|
ConstantString.WrappedCardResponseLayout = "list";
|
|
106
109
|
ConstantString.GetMethod = "get";
|
|
107
110
|
ConstantString.PostMethod = "post";
|
|
108
111
|
ConstantString.AdaptiveCardVersion = "1.5";
|
|
109
|
-
ConstantString.AdaptiveCardSchema = "
|
|
112
|
+
ConstantString.AdaptiveCardSchema = "https://adaptivecards.io/schemas/adaptive-card.json";
|
|
110
113
|
ConstantString.AdaptiveCardType = "AdaptiveCard";
|
|
111
114
|
ConstantString.TextBlockType = "TextBlock";
|
|
112
115
|
ConstantString.ImageType = "Image";
|
|
113
116
|
ConstantString.ContainerType = "Container";
|
|
114
|
-
ConstantString.RegistrationIdPostfix =
|
|
115
|
-
apiKey: "REGISTRATION_ID",
|
|
116
|
-
oauth2: "CONFIGURATION_ID",
|
|
117
|
-
http: "REGISTRATION_ID",
|
|
118
|
-
openIdConnect: "REGISTRATION_ID",
|
|
119
|
-
};
|
|
117
|
+
ConstantString.RegistrationIdPostfix = "REGISTRATION_ID";
|
|
120
118
|
ConstantString.ResponseCodeFor20X = [
|
|
121
119
|
"200",
|
|
122
120
|
"201",
|
|
@@ -193,16 +191,8 @@ class SpecParserError extends Error {
|
|
|
193
191
|
|
|
194
192
|
// Copyright (c) Microsoft Corporation.
|
|
195
193
|
class Utils {
|
|
196
|
-
static
|
|
197
|
-
|
|
198
|
-
for (const property in schema.properties) {
|
|
199
|
-
const nestedSchema = schema.properties[property];
|
|
200
|
-
if (nestedSchema.type === "object") {
|
|
201
|
-
return true;
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
return false;
|
|
194
|
+
static isObjectSchema(schema) {
|
|
195
|
+
return schema.type === "object" || (!schema.type && !!schema.properties);
|
|
206
196
|
}
|
|
207
197
|
static containMultipleMediaTypes(bodyObject) {
|
|
208
198
|
return Object.keys((bodyObject === null || bodyObject === void 0 ? void 0 : bodyObject.content) || {}).length > 1;
|
|
@@ -213,11 +203,32 @@ class Utils {
|
|
|
213
203
|
static isAPIKeyAuth(authScheme) {
|
|
214
204
|
return authScheme.type === "apiKey";
|
|
215
205
|
}
|
|
206
|
+
static isAPIKeyAuthButNotInCookie(authScheme) {
|
|
207
|
+
return authScheme.type === "apiKey" && authScheme.in !== "cookie";
|
|
208
|
+
}
|
|
216
209
|
static isOAuthWithAuthCodeFlow(authScheme) {
|
|
217
210
|
return !!(authScheme.type === "oauth2" &&
|
|
218
211
|
authScheme.flows &&
|
|
219
212
|
authScheme.flows.authorizationCode);
|
|
220
213
|
}
|
|
214
|
+
static isNotSupportedAuth(authSchemeArray) {
|
|
215
|
+
if (authSchemeArray.length === 0) {
|
|
216
|
+
return false;
|
|
217
|
+
}
|
|
218
|
+
if (authSchemeArray.length > 0 && authSchemeArray.every((auths) => auths.length > 1)) {
|
|
219
|
+
return true;
|
|
220
|
+
}
|
|
221
|
+
for (const auths of authSchemeArray) {
|
|
222
|
+
if (auths.length === 1) {
|
|
223
|
+
if (Utils.isOAuthWithAuthCodeFlow(auths[0].authScheme) ||
|
|
224
|
+
Utils.isBearerTokenAuth(auths[0].authScheme) ||
|
|
225
|
+
Utils.isAPIKeyAuthButNotInCookie(auths[0].authScheme)) {
|
|
226
|
+
return false;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
return true;
|
|
231
|
+
}
|
|
221
232
|
static getAuthArray(securities, spec) {
|
|
222
233
|
var _a;
|
|
223
234
|
const result = [];
|
|
@@ -242,6 +253,20 @@ class Utils {
|
|
|
242
253
|
result.sort((a, b) => a[0].name.localeCompare(b[0].name));
|
|
243
254
|
return result;
|
|
244
255
|
}
|
|
256
|
+
static getAuthMap(spec) {
|
|
257
|
+
const authMap = {};
|
|
258
|
+
for (const url in spec.paths) {
|
|
259
|
+
for (const method in spec.paths[url]) {
|
|
260
|
+
const operation = spec.paths[url][method];
|
|
261
|
+
const authArray = Utils.getAuthArray(operation.security, spec);
|
|
262
|
+
if (authArray && authArray.length > 0) {
|
|
263
|
+
const currentAuth = authArray[0][0];
|
|
264
|
+
authMap[operation.operationId] = currentAuth;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
return authMap;
|
|
269
|
+
}
|
|
245
270
|
static getAuthInfo(spec) {
|
|
246
271
|
let authInfo = undefined;
|
|
247
272
|
for (const url in spec.paths) {
|
|
@@ -270,27 +295,33 @@ class Utils {
|
|
|
270
295
|
let multipleMediaType = false;
|
|
271
296
|
for (const code of ConstantString.ResponseCodeFor20X) {
|
|
272
297
|
const responseObject = (_a = operationObject === null || operationObject === void 0 ? void 0 : operationObject.responses) === null || _a === void 0 ? void 0 : _a[code];
|
|
273
|
-
if (responseObject
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
else {
|
|
286
|
-
return { json, multipleMediaType };
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
}
|
|
298
|
+
if (!responseObject) {
|
|
299
|
+
continue;
|
|
300
|
+
}
|
|
301
|
+
multipleMediaType = Utils.containMultipleMediaTypes(responseObject);
|
|
302
|
+
if (!allowMultipleMediaType && multipleMediaType) {
|
|
303
|
+
json = {};
|
|
304
|
+
continue;
|
|
305
|
+
}
|
|
306
|
+
const mediaObj = Utils.getJsonContentType(responseObject);
|
|
307
|
+
if (Object.keys(mediaObj).length > 0) {
|
|
308
|
+
json = mediaObj;
|
|
309
|
+
return { json, multipleMediaType };
|
|
290
310
|
}
|
|
291
311
|
}
|
|
292
312
|
return { json, multipleMediaType };
|
|
293
313
|
}
|
|
314
|
+
static getJsonContentType(responseObject) {
|
|
315
|
+
if (responseObject.content) {
|
|
316
|
+
for (const contentType of Object.keys(responseObject.content)) {
|
|
317
|
+
// json media type can also be "application/json; charset=utf-8"
|
|
318
|
+
if (contentType.indexOf("application/json") >= 0) {
|
|
319
|
+
return responseObject.content[contentType];
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
return {};
|
|
324
|
+
}
|
|
294
325
|
static convertPathToCamelCase(path) {
|
|
295
326
|
const pathSegments = path.split(/[./{]/);
|
|
296
327
|
const camelCaseSegments = pathSegments.map((segment) => {
|
|
@@ -326,7 +357,7 @@ class Utils {
|
|
|
326
357
|
}
|
|
327
358
|
return newStr;
|
|
328
359
|
}
|
|
329
|
-
static checkServerUrl(servers) {
|
|
360
|
+
static checkServerUrl(servers, allowHttp = false) {
|
|
330
361
|
const errors = [];
|
|
331
362
|
let serverUrl;
|
|
332
363
|
try {
|
|
@@ -349,8 +380,7 @@ class Utils {
|
|
|
349
380
|
data: servers,
|
|
350
381
|
});
|
|
351
382
|
}
|
|
352
|
-
else if (protocol !== "https:") {
|
|
353
|
-
// Http server url is not supported
|
|
383
|
+
else if (protocol !== "https:" && !(protocol === "http:" && allowHttp)) {
|
|
354
384
|
const protocolString = protocol.slice(0, -1);
|
|
355
385
|
errors.push({
|
|
356
386
|
type: ErrorType.UrlProtocolNotSupported,
|
|
@@ -366,10 +396,11 @@ class Utils {
|
|
|
366
396
|
let hasTopLevelServers = false;
|
|
367
397
|
let hasPathLevelServers = false;
|
|
368
398
|
let hasOperationLevelServers = false;
|
|
399
|
+
const allowHttp = options.projectType === ProjectType.Copilot;
|
|
369
400
|
if (spec.servers && spec.servers.length >= 1) {
|
|
370
401
|
hasTopLevelServers = true;
|
|
371
402
|
// for multiple server, we only use the first url
|
|
372
|
-
const serverErrors = Utils.checkServerUrl(spec.servers);
|
|
403
|
+
const serverErrors = Utils.checkServerUrl(spec.servers, allowHttp);
|
|
373
404
|
errors.push(...serverErrors);
|
|
374
405
|
}
|
|
375
406
|
const paths = spec.paths;
|
|
@@ -377,7 +408,7 @@ class Utils {
|
|
|
377
408
|
const methods = paths[path];
|
|
378
409
|
if ((methods === null || methods === void 0 ? void 0 : methods.servers) && methods.servers.length >= 1) {
|
|
379
410
|
hasPathLevelServers = true;
|
|
380
|
-
const serverErrors = Utils.checkServerUrl(methods.servers);
|
|
411
|
+
const serverErrors = Utils.checkServerUrl(methods.servers, allowHttp);
|
|
381
412
|
errors.push(...serverErrors);
|
|
382
413
|
}
|
|
383
414
|
for (const method in methods) {
|
|
@@ -385,7 +416,7 @@ class Utils {
|
|
|
385
416
|
if (((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && operationObject) {
|
|
386
417
|
if ((operationObject === null || operationObject === void 0 ? void 0 : operationObject.servers) && operationObject.servers.length >= 1) {
|
|
387
418
|
hasOperationLevelServers = true;
|
|
388
|
-
const serverErrors = Utils.checkServerUrl(operationObject.servers);
|
|
419
|
+
const serverErrors = Utils.checkServerUrl(operationObject.servers, allowHttp);
|
|
389
420
|
errors.push(...serverErrors);
|
|
390
421
|
}
|
|
391
422
|
}
|
|
@@ -432,7 +463,7 @@ class Utils {
|
|
|
432
463
|
optionalParams.push(parameter);
|
|
433
464
|
}
|
|
434
465
|
}
|
|
435
|
-
else if (schema
|
|
466
|
+
else if (Utils.isObjectSchema(schema)) {
|
|
436
467
|
const { properties } = schema;
|
|
437
468
|
for (const property in properties) {
|
|
438
469
|
let isRequired = false;
|
|
@@ -546,29 +577,6 @@ class Utils {
|
|
|
546
577
|
const serverUrl = operationServer || methodServer || rootServer;
|
|
547
578
|
return serverUrl;
|
|
548
579
|
}
|
|
549
|
-
static limitACBodyProperties(body, maxCount) {
|
|
550
|
-
const result = [];
|
|
551
|
-
let currentCount = 0;
|
|
552
|
-
for (const element of body) {
|
|
553
|
-
if (element.type === ConstantString.ContainerType) {
|
|
554
|
-
const items = this.limitACBodyProperties(element.items, maxCount - currentCount);
|
|
555
|
-
result.push({
|
|
556
|
-
type: ConstantString.ContainerType,
|
|
557
|
-
$data: element.$data,
|
|
558
|
-
items: items,
|
|
559
|
-
});
|
|
560
|
-
currentCount += items.length;
|
|
561
|
-
}
|
|
562
|
-
else {
|
|
563
|
-
result.push(element);
|
|
564
|
-
currentCount++;
|
|
565
|
-
}
|
|
566
|
-
if (currentCount >= maxCount) {
|
|
567
|
-
break;
|
|
568
|
-
}
|
|
569
|
-
}
|
|
570
|
-
return result;
|
|
571
|
-
}
|
|
572
580
|
}
|
|
573
581
|
|
|
574
582
|
// Copyright (c) Microsoft Corporation.
|
|
@@ -697,22 +705,6 @@ class Validator {
|
|
|
697
705
|
}
|
|
698
706
|
return result;
|
|
699
707
|
}
|
|
700
|
-
validateResponse(method, path) {
|
|
701
|
-
const result = { isValid: true, reason: [] };
|
|
702
|
-
const operationObject = this.spec.paths[path][method];
|
|
703
|
-
const { json, multipleMediaType } = Utils.getResponseJson(operationObject);
|
|
704
|
-
if (this.options.projectType === ProjectType.SME) {
|
|
705
|
-
// only support response body only contains “application/json” content type
|
|
706
|
-
if (multipleMediaType) {
|
|
707
|
-
result.reason.push(ErrorType.ResponseContainMultipleMediaTypes);
|
|
708
|
-
}
|
|
709
|
-
else if (Object.keys(json).length === 0) {
|
|
710
|
-
// response body should not be empty
|
|
711
|
-
result.reason.push(ErrorType.ResponseJsonIsEmpty);
|
|
712
|
-
}
|
|
713
|
-
}
|
|
714
|
-
return result;
|
|
715
|
-
}
|
|
716
708
|
validateServer(method, path) {
|
|
717
709
|
const result = { isValid: true, reason: [] };
|
|
718
710
|
const serverObj = Utils.getServerObject(this.spec, method, path);
|
|
@@ -721,8 +713,8 @@ class Validator {
|
|
|
721
713
|
result.reason.push(ErrorType.NoServerInformation);
|
|
722
714
|
}
|
|
723
715
|
else {
|
|
724
|
-
|
|
725
|
-
const serverValidateResult = Utils.checkServerUrl([serverObj]);
|
|
716
|
+
const allowHttp = this.projectType === ProjectType.Copilot;
|
|
717
|
+
const serverValidateResult = Utils.checkServerUrl([serverObj], allowHttp);
|
|
726
718
|
result.reason.push(...serverValidateResult.map((item) => item.type));
|
|
727
719
|
}
|
|
728
720
|
return result;
|
|
@@ -745,6 +737,9 @@ class Validator {
|
|
|
745
737
|
reason: [ErrorType.MultipleAuthNotSupported],
|
|
746
738
|
};
|
|
747
739
|
}
|
|
740
|
+
if (this.projectType === ProjectType.Copilot) {
|
|
741
|
+
return { isValid: true, reason: [] };
|
|
742
|
+
}
|
|
748
743
|
for (const auths of authSchemeArray) {
|
|
749
744
|
if (auths.length === 1) {
|
|
750
745
|
if ((this.options.allowAPIKeyAuth && Utils.isAPIKeyAuth(auths[0].authScheme)) ||
|
|
@@ -757,125 +752,6 @@ class Validator {
|
|
|
757
752
|
}
|
|
758
753
|
return { isValid: false, reason: [ErrorType.AuthTypeIsNotSupported] };
|
|
759
754
|
}
|
|
760
|
-
checkPostBodySchema(schema, isRequired = false) {
|
|
761
|
-
var _a;
|
|
762
|
-
const paramResult = {
|
|
763
|
-
requiredNum: 0,
|
|
764
|
-
optionalNum: 0,
|
|
765
|
-
isValid: true,
|
|
766
|
-
reason: [],
|
|
767
|
-
};
|
|
768
|
-
if (Object.keys(schema).length === 0) {
|
|
769
|
-
return paramResult;
|
|
770
|
-
}
|
|
771
|
-
const isRequiredWithoutDefault = isRequired && schema.default === undefined;
|
|
772
|
-
const isCopilot = this.projectType === ProjectType.Copilot;
|
|
773
|
-
if (isCopilot && this.hasNestedObjectInSchema(schema)) {
|
|
774
|
-
paramResult.isValid = false;
|
|
775
|
-
paramResult.reason = [ErrorType.RequestBodyContainsNestedObject];
|
|
776
|
-
return paramResult;
|
|
777
|
-
}
|
|
778
|
-
if (schema.type === "string" ||
|
|
779
|
-
schema.type === "integer" ||
|
|
780
|
-
schema.type === "boolean" ||
|
|
781
|
-
schema.type === "number") {
|
|
782
|
-
if (isRequiredWithoutDefault) {
|
|
783
|
-
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
784
|
-
}
|
|
785
|
-
else {
|
|
786
|
-
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
787
|
-
}
|
|
788
|
-
}
|
|
789
|
-
else if (schema.type === "object") {
|
|
790
|
-
const { properties } = schema;
|
|
791
|
-
for (const property in properties) {
|
|
792
|
-
let isRequired = false;
|
|
793
|
-
if (schema.required && ((_a = schema.required) === null || _a === void 0 ? void 0 : _a.indexOf(property)) >= 0) {
|
|
794
|
-
isRequired = true;
|
|
795
|
-
}
|
|
796
|
-
const result = this.checkPostBodySchema(properties[property], isRequired);
|
|
797
|
-
paramResult.requiredNum += result.requiredNum;
|
|
798
|
-
paramResult.optionalNum += result.optionalNum;
|
|
799
|
-
paramResult.isValid = paramResult.isValid && result.isValid;
|
|
800
|
-
paramResult.reason.push(...result.reason);
|
|
801
|
-
}
|
|
802
|
-
}
|
|
803
|
-
else {
|
|
804
|
-
if (isRequiredWithoutDefault && !isCopilot) {
|
|
805
|
-
paramResult.isValid = false;
|
|
806
|
-
paramResult.reason.push(ErrorType.PostBodyContainsRequiredUnsupportedSchema);
|
|
807
|
-
}
|
|
808
|
-
}
|
|
809
|
-
return paramResult;
|
|
810
|
-
}
|
|
811
|
-
checkParamSchema(paramObject) {
|
|
812
|
-
const paramResult = {
|
|
813
|
-
requiredNum: 0,
|
|
814
|
-
optionalNum: 0,
|
|
815
|
-
isValid: true,
|
|
816
|
-
reason: [],
|
|
817
|
-
};
|
|
818
|
-
if (!paramObject) {
|
|
819
|
-
return paramResult;
|
|
820
|
-
}
|
|
821
|
-
const isCopilot = this.projectType === ProjectType.Copilot;
|
|
822
|
-
for (let i = 0; i < paramObject.length; i++) {
|
|
823
|
-
const param = paramObject[i];
|
|
824
|
-
const schema = param.schema;
|
|
825
|
-
if (isCopilot && this.hasNestedObjectInSchema(schema)) {
|
|
826
|
-
paramResult.isValid = false;
|
|
827
|
-
paramResult.reason.push(ErrorType.ParamsContainsNestedObject);
|
|
828
|
-
continue;
|
|
829
|
-
}
|
|
830
|
-
const isRequiredWithoutDefault = param.required && schema.default === undefined;
|
|
831
|
-
if (isCopilot) {
|
|
832
|
-
if (isRequiredWithoutDefault) {
|
|
833
|
-
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
834
|
-
}
|
|
835
|
-
else {
|
|
836
|
-
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
837
|
-
}
|
|
838
|
-
continue;
|
|
839
|
-
}
|
|
840
|
-
if (param.in === "header" || param.in === "cookie") {
|
|
841
|
-
if (isRequiredWithoutDefault) {
|
|
842
|
-
paramResult.isValid = false;
|
|
843
|
-
paramResult.reason.push(ErrorType.ParamsContainRequiredUnsupportedSchema);
|
|
844
|
-
}
|
|
845
|
-
continue;
|
|
846
|
-
}
|
|
847
|
-
if (schema.type !== "boolean" &&
|
|
848
|
-
schema.type !== "string" &&
|
|
849
|
-
schema.type !== "number" &&
|
|
850
|
-
schema.type !== "integer") {
|
|
851
|
-
if (isRequiredWithoutDefault) {
|
|
852
|
-
paramResult.isValid = false;
|
|
853
|
-
paramResult.reason.push(ErrorType.ParamsContainRequiredUnsupportedSchema);
|
|
854
|
-
}
|
|
855
|
-
continue;
|
|
856
|
-
}
|
|
857
|
-
if (param.in === "query" || param.in === "path") {
|
|
858
|
-
if (isRequiredWithoutDefault) {
|
|
859
|
-
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
860
|
-
}
|
|
861
|
-
else {
|
|
862
|
-
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
863
|
-
}
|
|
864
|
-
}
|
|
865
|
-
}
|
|
866
|
-
return paramResult;
|
|
867
|
-
}
|
|
868
|
-
hasNestedObjectInSchema(schema) {
|
|
869
|
-
if (schema.type === "object") {
|
|
870
|
-
for (const property in schema.properties) {
|
|
871
|
-
const nestedSchema = schema.properties[property];
|
|
872
|
-
if (nestedSchema.type === "object") {
|
|
873
|
-
return true;
|
|
874
|
-
}
|
|
875
|
-
}
|
|
876
|
-
}
|
|
877
|
-
return false;
|
|
878
|
-
}
|
|
879
755
|
}
|
|
880
756
|
|
|
881
757
|
// Copyright (c) Microsoft Corporation.
|
|
@@ -885,7 +761,6 @@ class CopilotValidator extends Validator {
|
|
|
885
761
|
this.projectType = ProjectType.Copilot;
|
|
886
762
|
this.options = options;
|
|
887
763
|
this.spec = spec;
|
|
888
|
-
this.checkCircularReference();
|
|
889
764
|
}
|
|
890
765
|
validateSpec() {
|
|
891
766
|
const result = { errors: [], warnings: [] };
|
|
@@ -911,10 +786,6 @@ class CopilotValidator extends Validator {
|
|
|
911
786
|
if (!methodAndPathResult.isValid) {
|
|
912
787
|
return methodAndPathResult;
|
|
913
788
|
}
|
|
914
|
-
const circularReferenceResult = this.validateCircularReference(method, path);
|
|
915
|
-
if (!circularReferenceResult.isValid) {
|
|
916
|
-
return circularReferenceResult;
|
|
917
|
-
}
|
|
918
789
|
const operationObject = this.spec.paths[path][method];
|
|
919
790
|
// validate auth
|
|
920
791
|
const authCheckResult = this.validateAuth(method, path);
|
|
@@ -926,24 +797,6 @@ class CopilotValidator extends Validator {
|
|
|
926
797
|
// validate server
|
|
927
798
|
const validateServerResult = this.validateServer(method, path);
|
|
928
799
|
result.reason.push(...validateServerResult.reason);
|
|
929
|
-
// validate response
|
|
930
|
-
const validateResponseResult = this.validateResponse(method, path);
|
|
931
|
-
result.reason.push(...validateResponseResult.reason);
|
|
932
|
-
// validate requestBody
|
|
933
|
-
const requestBody = operationObject.requestBody;
|
|
934
|
-
const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
|
|
935
|
-
if (requestJsonBody) {
|
|
936
|
-
const requestBodySchema = requestJsonBody.schema;
|
|
937
|
-
if (requestBodySchema.type !== "object") {
|
|
938
|
-
result.reason.push(ErrorType.PostBodySchemaIsNotJson);
|
|
939
|
-
}
|
|
940
|
-
const requestBodyParamResult = this.checkPostBodySchema(requestBodySchema, requestBody.required);
|
|
941
|
-
result.reason.push(...requestBodyParamResult.reason);
|
|
942
|
-
}
|
|
943
|
-
// validate parameters
|
|
944
|
-
const paramObject = operationObject.parameters;
|
|
945
|
-
const paramResult = this.checkParamSchema(paramObject);
|
|
946
|
-
result.reason.push(...paramResult.reason);
|
|
947
800
|
if (result.reason.length > 0) {
|
|
948
801
|
result.isValid = false;
|
|
949
802
|
}
|
|
@@ -1035,6 +888,108 @@ class SMEValidator extends Validator {
|
|
|
1035
888
|
}
|
|
1036
889
|
return result;
|
|
1037
890
|
}
|
|
891
|
+
validateResponse(method, path) {
|
|
892
|
+
const result = { isValid: true, reason: [] };
|
|
893
|
+
const operationObject = this.spec.paths[path][method];
|
|
894
|
+
const { json, multipleMediaType } = Utils.getResponseJson(operationObject);
|
|
895
|
+
// only support response body only contains “application/json” content type
|
|
896
|
+
if (multipleMediaType) {
|
|
897
|
+
result.reason.push(ErrorType.ResponseContainMultipleMediaTypes);
|
|
898
|
+
}
|
|
899
|
+
else if (Object.keys(json).length === 0) {
|
|
900
|
+
// response body should not be empty
|
|
901
|
+
result.reason.push(ErrorType.ResponseJsonIsEmpty);
|
|
902
|
+
}
|
|
903
|
+
return result;
|
|
904
|
+
}
|
|
905
|
+
checkPostBodySchema(schema, isRequired = false) {
|
|
906
|
+
var _a;
|
|
907
|
+
const paramResult = {
|
|
908
|
+
requiredNum: 0,
|
|
909
|
+
optionalNum: 0,
|
|
910
|
+
isValid: true,
|
|
911
|
+
reason: [],
|
|
912
|
+
};
|
|
913
|
+
if (Object.keys(schema).length === 0) {
|
|
914
|
+
return paramResult;
|
|
915
|
+
}
|
|
916
|
+
const isRequiredWithoutDefault = isRequired && schema.default === undefined;
|
|
917
|
+
const isCopilot = this.projectType === ProjectType.Copilot;
|
|
918
|
+
if (schema.type === "string" ||
|
|
919
|
+
schema.type === "integer" ||
|
|
920
|
+
schema.type === "boolean" ||
|
|
921
|
+
schema.type === "number") {
|
|
922
|
+
if (isRequiredWithoutDefault) {
|
|
923
|
+
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
924
|
+
}
|
|
925
|
+
else {
|
|
926
|
+
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
else if (Utils.isObjectSchema(schema)) {
|
|
930
|
+
const { properties } = schema;
|
|
931
|
+
for (const property in properties) {
|
|
932
|
+
let isRequired = false;
|
|
933
|
+
if (schema.required && ((_a = schema.required) === null || _a === void 0 ? void 0 : _a.indexOf(property)) >= 0) {
|
|
934
|
+
isRequired = true;
|
|
935
|
+
}
|
|
936
|
+
const result = this.checkPostBodySchema(properties[property], isRequired);
|
|
937
|
+
paramResult.requiredNum += result.requiredNum;
|
|
938
|
+
paramResult.optionalNum += result.optionalNum;
|
|
939
|
+
paramResult.isValid = paramResult.isValid && result.isValid;
|
|
940
|
+
paramResult.reason.push(...result.reason);
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
else {
|
|
944
|
+
if (isRequiredWithoutDefault && !isCopilot) {
|
|
945
|
+
paramResult.isValid = false;
|
|
946
|
+
paramResult.reason.push(ErrorType.PostBodyContainsRequiredUnsupportedSchema);
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
return paramResult;
|
|
950
|
+
}
|
|
951
|
+
checkParamSchema(paramObject) {
|
|
952
|
+
const paramResult = {
|
|
953
|
+
requiredNum: 0,
|
|
954
|
+
optionalNum: 0,
|
|
955
|
+
isValid: true,
|
|
956
|
+
reason: [],
|
|
957
|
+
};
|
|
958
|
+
if (!paramObject) {
|
|
959
|
+
return paramResult;
|
|
960
|
+
}
|
|
961
|
+
for (let i = 0; i < paramObject.length; i++) {
|
|
962
|
+
const param = paramObject[i];
|
|
963
|
+
const schema = param.schema;
|
|
964
|
+
const isRequiredWithoutDefault = param.required && schema.default === undefined;
|
|
965
|
+
if (param.in === "header" || param.in === "cookie") {
|
|
966
|
+
if (isRequiredWithoutDefault) {
|
|
967
|
+
paramResult.isValid = false;
|
|
968
|
+
paramResult.reason.push(ErrorType.ParamsContainRequiredUnsupportedSchema);
|
|
969
|
+
}
|
|
970
|
+
continue;
|
|
971
|
+
}
|
|
972
|
+
if (schema.type !== "boolean" &&
|
|
973
|
+
schema.type !== "string" &&
|
|
974
|
+
schema.type !== "number" &&
|
|
975
|
+
schema.type !== "integer") {
|
|
976
|
+
if (isRequiredWithoutDefault) {
|
|
977
|
+
paramResult.isValid = false;
|
|
978
|
+
paramResult.reason.push(ErrorType.ParamsContainRequiredUnsupportedSchema);
|
|
979
|
+
}
|
|
980
|
+
continue;
|
|
981
|
+
}
|
|
982
|
+
if (param.in === "query" || param.in === "path") {
|
|
983
|
+
if (isRequiredWithoutDefault) {
|
|
984
|
+
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
985
|
+
}
|
|
986
|
+
else {
|
|
987
|
+
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
return paramResult;
|
|
992
|
+
}
|
|
1038
993
|
validateParamCount(postBodyResult, paramResult) {
|
|
1039
994
|
const result = { isValid: true, reason: [] };
|
|
1040
995
|
const totalRequiredParams = postBodyResult.requiredNum + paramResult.requiredNum;
|
|
@@ -1312,20 +1267,157 @@ class SpecFilter {
|
|
|
1312
1267
|
}
|
|
1313
1268
|
}
|
|
1314
1269
|
|
|
1270
|
+
// Copyright (c) Microsoft Corporation.
|
|
1271
|
+
class JsonDataGenerator {
|
|
1272
|
+
static generate(schema) {
|
|
1273
|
+
return this.generateMockData(schema);
|
|
1274
|
+
}
|
|
1275
|
+
static generateMockData(schema) {
|
|
1276
|
+
if (this.visitedSchemas.has(schema)) {
|
|
1277
|
+
return null; // Prevent circular reference
|
|
1278
|
+
}
|
|
1279
|
+
this.visitedSchemas.add(schema);
|
|
1280
|
+
let result;
|
|
1281
|
+
if (schema.anyOf) {
|
|
1282
|
+
// Select the first schema in anyOf
|
|
1283
|
+
const selectedSchema = schema.anyOf[0];
|
|
1284
|
+
result = this.generateMockData(selectedSchema);
|
|
1285
|
+
}
|
|
1286
|
+
else if (schema.oneOf) {
|
|
1287
|
+
// Select the first schema in oneOf
|
|
1288
|
+
const selectedSchema = schema.oneOf[0];
|
|
1289
|
+
result = this.generateMockData(selectedSchema);
|
|
1290
|
+
}
|
|
1291
|
+
else if (schema.allOf) {
|
|
1292
|
+
// merge all schemas in allOf
|
|
1293
|
+
result = {};
|
|
1294
|
+
for (const subschema of schema.allOf) {
|
|
1295
|
+
const data = this.generateMockData(subschema);
|
|
1296
|
+
result = Object.assign(Object.assign({}, result), data);
|
|
1297
|
+
}
|
|
1298
|
+
}
|
|
1299
|
+
else {
|
|
1300
|
+
switch (schema.type) {
|
|
1301
|
+
case "string":
|
|
1302
|
+
if (schema.example !== undefined) {
|
|
1303
|
+
result = schema.example;
|
|
1304
|
+
}
|
|
1305
|
+
else if (schema.format) {
|
|
1306
|
+
switch (schema.format) {
|
|
1307
|
+
case "date-time":
|
|
1308
|
+
result = "2024-11-01T05:25:43.593Z";
|
|
1309
|
+
break;
|
|
1310
|
+
case "email":
|
|
1311
|
+
result = "example@example.com";
|
|
1312
|
+
break;
|
|
1313
|
+
case "uuid":
|
|
1314
|
+
result = "123e4567-e89b-12d3-a456-426614174000";
|
|
1315
|
+
break;
|
|
1316
|
+
case "ipv4":
|
|
1317
|
+
result = "192.168.0.1";
|
|
1318
|
+
break;
|
|
1319
|
+
case "ipv6":
|
|
1320
|
+
result = "2001:0db8:85a3:0000:0000:8a2e:0370:7334";
|
|
1321
|
+
break;
|
|
1322
|
+
default:
|
|
1323
|
+
result = "example string";
|
|
1324
|
+
}
|
|
1325
|
+
}
|
|
1326
|
+
else {
|
|
1327
|
+
result = "example string";
|
|
1328
|
+
}
|
|
1329
|
+
break;
|
|
1330
|
+
case "number":
|
|
1331
|
+
if (schema.example !== undefined) {
|
|
1332
|
+
result = schema.example;
|
|
1333
|
+
}
|
|
1334
|
+
else if (schema.format) {
|
|
1335
|
+
switch (schema.format) {
|
|
1336
|
+
case "float":
|
|
1337
|
+
result = 3.14;
|
|
1338
|
+
break;
|
|
1339
|
+
case "double":
|
|
1340
|
+
result = 3.14159;
|
|
1341
|
+
break;
|
|
1342
|
+
default:
|
|
1343
|
+
result = 123;
|
|
1344
|
+
}
|
|
1345
|
+
}
|
|
1346
|
+
else {
|
|
1347
|
+
result = 123;
|
|
1348
|
+
}
|
|
1349
|
+
break;
|
|
1350
|
+
case "integer":
|
|
1351
|
+
if (schema.example !== undefined) {
|
|
1352
|
+
result = schema.example;
|
|
1353
|
+
}
|
|
1354
|
+
else if (schema.format) {
|
|
1355
|
+
switch (schema.format) {
|
|
1356
|
+
case "int32":
|
|
1357
|
+
result = 123456;
|
|
1358
|
+
break;
|
|
1359
|
+
case "int64":
|
|
1360
|
+
result = 123456789;
|
|
1361
|
+
break;
|
|
1362
|
+
default:
|
|
1363
|
+
result = 123;
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
else {
|
|
1367
|
+
result = 123;
|
|
1368
|
+
}
|
|
1369
|
+
break;
|
|
1370
|
+
case "boolean":
|
|
1371
|
+
result = schema.example !== undefined ? schema.example : true;
|
|
1372
|
+
break;
|
|
1373
|
+
case "array":
|
|
1374
|
+
result = [this.generateMockData(schema.items)];
|
|
1375
|
+
break;
|
|
1376
|
+
case "object":
|
|
1377
|
+
result = {};
|
|
1378
|
+
if (schema.properties) {
|
|
1379
|
+
for (const key in schema.properties) {
|
|
1380
|
+
result[key] = this.generateMockData(schema.properties[key]);
|
|
1381
|
+
}
|
|
1382
|
+
}
|
|
1383
|
+
break;
|
|
1384
|
+
default:
|
|
1385
|
+
result = schema.example || null;
|
|
1386
|
+
}
|
|
1387
|
+
}
|
|
1388
|
+
this.visitedSchemas.delete(schema);
|
|
1389
|
+
return result;
|
|
1390
|
+
}
|
|
1391
|
+
}
|
|
1392
|
+
JsonDataGenerator.visitedSchemas = new Set();
|
|
1393
|
+
|
|
1315
1394
|
// Copyright (c) Microsoft Corporation.
|
|
1316
1395
|
class AdaptiveCardGenerator {
|
|
1317
|
-
static generateAdaptiveCard(operationItem, allowMultipleMediaType = false) {
|
|
1396
|
+
static generateAdaptiveCard(operationItem, allowMultipleMediaType = false, maxElementCount = Number.MAX_SAFE_INTEGER) {
|
|
1318
1397
|
try {
|
|
1319
1398
|
const { json } = Utils.getResponseJson(operationItem, allowMultipleMediaType);
|
|
1320
1399
|
let cardBody = [];
|
|
1400
|
+
let jsonData = {};
|
|
1401
|
+
const warnings = [];
|
|
1402
|
+
const operationId = operationItem.operationId;
|
|
1321
1403
|
let schema = json.schema;
|
|
1322
1404
|
let jsonPath = "$";
|
|
1323
1405
|
if (schema && Object.keys(schema).length > 0) {
|
|
1406
|
+
try {
|
|
1407
|
+
jsonData = JsonDataGenerator.generate(schema);
|
|
1408
|
+
}
|
|
1409
|
+
catch (err) {
|
|
1410
|
+
warnings.push({
|
|
1411
|
+
type: WarningType.GenerateJsonDataFailed,
|
|
1412
|
+
content: Utils.format(ConstantString.GenerateJsonDataFailed, operationId, err.toString()),
|
|
1413
|
+
data: operationId,
|
|
1414
|
+
});
|
|
1415
|
+
}
|
|
1324
1416
|
jsonPath = AdaptiveCardGenerator.getResponseJsonPathFromSchema(schema);
|
|
1325
1417
|
if (jsonPath !== "$") {
|
|
1326
1418
|
schema = schema.properties[jsonPath];
|
|
1327
1419
|
}
|
|
1328
|
-
cardBody = AdaptiveCardGenerator.generateCardFromResponse(schema, "");
|
|
1420
|
+
cardBody = AdaptiveCardGenerator.generateCardFromResponse(schema, "", "", maxElementCount);
|
|
1329
1421
|
}
|
|
1330
1422
|
// if no schema, try to use example value
|
|
1331
1423
|
if (cardBody.length === 0 && (json.examples || json.example)) {
|
|
@@ -1353,16 +1445,20 @@ class AdaptiveCardGenerator {
|
|
|
1353
1445
|
version: ConstantString.AdaptiveCardVersion,
|
|
1354
1446
|
body: cardBody,
|
|
1355
1447
|
};
|
|
1356
|
-
return [fullCard, jsonPath];
|
|
1448
|
+
return [fullCard, jsonPath, jsonData, warnings];
|
|
1357
1449
|
}
|
|
1358
1450
|
catch (err) {
|
|
1359
1451
|
throw new SpecParserError(err.toString(), ErrorType.GenerateAdaptiveCardFailed);
|
|
1360
1452
|
}
|
|
1361
1453
|
}
|
|
1362
|
-
static generateCardFromResponse(schema, name, parentArrayName = "") {
|
|
1454
|
+
static generateCardFromResponse(schema, name, parentArrayName = "", maxElementCount = Number.MAX_SAFE_INTEGER, counter = { count: 0 }) {
|
|
1455
|
+
if (counter.count >= maxElementCount) {
|
|
1456
|
+
return [];
|
|
1457
|
+
}
|
|
1363
1458
|
if (schema.type === "array") {
|
|
1364
1459
|
// schema.items can be arbitrary object: schema { type: array, items: {} }
|
|
1365
1460
|
if (Object.keys(schema.items).length === 0) {
|
|
1461
|
+
counter.count++;
|
|
1366
1462
|
return [
|
|
1367
1463
|
{
|
|
1368
1464
|
type: ConstantString.TextBlockType,
|
|
@@ -1371,7 +1467,7 @@ class AdaptiveCardGenerator {
|
|
|
1371
1467
|
},
|
|
1372
1468
|
];
|
|
1373
1469
|
}
|
|
1374
|
-
const obj = AdaptiveCardGenerator.generateCardFromResponse(schema.items, "", name);
|
|
1470
|
+
const obj = AdaptiveCardGenerator.generateCardFromResponse(schema.items, "", name, maxElementCount, counter);
|
|
1375
1471
|
const template = {
|
|
1376
1472
|
type: ConstantString.ContainerType,
|
|
1377
1473
|
$data: name ? `\${${name}}` : "${$root}",
|
|
@@ -1381,11 +1477,11 @@ class AdaptiveCardGenerator {
|
|
|
1381
1477
|
return [template];
|
|
1382
1478
|
}
|
|
1383
1479
|
// some schema may not contain type but contain properties
|
|
1384
|
-
if (
|
|
1480
|
+
if (Utils.isObjectSchema(schema)) {
|
|
1385
1481
|
const { properties } = schema;
|
|
1386
1482
|
const result = [];
|
|
1387
1483
|
for (const property in properties) {
|
|
1388
|
-
const obj = AdaptiveCardGenerator.generateCardFromResponse(properties[property], name ? `${name}.${property}` : property, parentArrayName);
|
|
1484
|
+
const obj = AdaptiveCardGenerator.generateCardFromResponse(properties[property], name ? `${name}.${property}` : property, parentArrayName, maxElementCount, counter);
|
|
1389
1485
|
result.push(...obj);
|
|
1390
1486
|
}
|
|
1391
1487
|
if (schema.additionalProperties) {
|
|
@@ -1398,6 +1494,7 @@ class AdaptiveCardGenerator {
|
|
|
1398
1494
|
schema.type === "integer" ||
|
|
1399
1495
|
schema.type === "boolean" ||
|
|
1400
1496
|
schema.type === "number") {
|
|
1497
|
+
counter.count++;
|
|
1401
1498
|
if (!AdaptiveCardGenerator.isImageUrlProperty(schema, name, parentArrayName)) {
|
|
1402
1499
|
// string in root: "ddd"
|
|
1403
1500
|
let text = "result: ${$root}";
|
|
@@ -1422,24 +1519,17 @@ class AdaptiveCardGenerator {
|
|
|
1422
1519
|
];
|
|
1423
1520
|
}
|
|
1424
1521
|
else {
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
{
|
|
1437
|
-
type: "Image",
|
|
1438
|
-
url: "${$data}",
|
|
1439
|
-
$when: "${$data != null && $data != ''}",
|
|
1440
|
-
},
|
|
1441
|
-
];
|
|
1442
|
-
}
|
|
1522
|
+
const url = name ? `\${${name}}` : "${$data}";
|
|
1523
|
+
const condition = name
|
|
1524
|
+
? `\${${name} != null && ${name} != ''}`
|
|
1525
|
+
: "${$data != null && $data != ''}";
|
|
1526
|
+
return [
|
|
1527
|
+
{
|
|
1528
|
+
type: "Image",
|
|
1529
|
+
url,
|
|
1530
|
+
$when: condition,
|
|
1531
|
+
},
|
|
1532
|
+
];
|
|
1443
1533
|
}
|
|
1444
1534
|
}
|
|
1445
1535
|
if (schema.oneOf || schema.anyOf || schema.not || schema.allOf) {
|
|
@@ -1449,7 +1539,7 @@ class AdaptiveCardGenerator {
|
|
|
1449
1539
|
}
|
|
1450
1540
|
// Find the first array property in the response schema object with the well-known name
|
|
1451
1541
|
static getResponseJsonPathFromSchema(schema) {
|
|
1452
|
-
if (
|
|
1542
|
+
if (Utils.isObjectSchema(schema)) {
|
|
1453
1543
|
const { properties } = schema;
|
|
1454
1544
|
for (const property in properties) {
|
|
1455
1545
|
const schema = properties[property];
|
|
@@ -1601,7 +1691,7 @@ function inferProperties(card) {
|
|
|
1601
1691
|
|
|
1602
1692
|
// Copyright (c) Microsoft Corporation.
|
|
1603
1693
|
class ManifestUpdater {
|
|
1604
|
-
static async updateManifestWithAiPlugin(manifestPath, outputSpecPath, apiPluginFilePath, spec, options,
|
|
1694
|
+
static async updateManifestWithAiPlugin(manifestPath, outputSpecPath, apiPluginFilePath, spec, options, authMap, existingPluginManifestInfo) {
|
|
1605
1695
|
const manifest = await fs.readJSON(manifestPath);
|
|
1606
1696
|
const apiPluginRelativePath = ManifestUpdater.getRelativePath(manifestPath, apiPluginFilePath);
|
|
1607
1697
|
const useCopilotExtensionsInSchema = await ManifestUtil.useCopilotExtensionsInSchema(manifest);
|
|
@@ -1631,7 +1721,7 @@ class ManifestUpdater {
|
|
|
1631
1721
|
}
|
|
1632
1722
|
const appName = this.removeEnvs(manifest.name.short);
|
|
1633
1723
|
const specRelativePath = ManifestUpdater.getRelativePath(manifestPath, outputSpecPath);
|
|
1634
|
-
const [apiPlugin, warnings] = await ManifestUpdater.generatePluginManifestSchema(spec, specRelativePath, apiPluginFilePath, appName,
|
|
1724
|
+
const [apiPlugin, warnings] = await ManifestUpdater.generatePluginManifestSchema(spec, specRelativePath, apiPluginFilePath, appName, authMap, options, existingPluginManifestInfo);
|
|
1635
1725
|
return [manifest, apiPlugin, warnings];
|
|
1636
1726
|
}
|
|
1637
1727
|
static updateManifestDescription(manifest, spec) {
|
|
@@ -1653,28 +1743,13 @@ class ManifestUpdater {
|
|
|
1653
1743
|
throw new SpecParserError(Utils.format(ConstantString.UnsupportedSchema, method, pathUrl, JSON.stringify(schema)), ErrorType.UpdateManifestFailed);
|
|
1654
1744
|
}
|
|
1655
1745
|
}
|
|
1656
|
-
static async generatePluginManifestSchema(spec, specRelativePath, apiPluginFilePath, appName,
|
|
1746
|
+
static async generatePluginManifestSchema(spec, specRelativePath, apiPluginFilePath, appName, authMap, options, existingPluginManifestInfo) {
|
|
1657
1747
|
var _a, _b, _c, _d;
|
|
1658
1748
|
const warnings = [];
|
|
1659
1749
|
const functions = [];
|
|
1660
|
-
const
|
|
1750
|
+
const functionNamesMap = {};
|
|
1661
1751
|
const conversationStarters = [];
|
|
1662
1752
|
const paths = spec.paths;
|
|
1663
|
-
const pluginAuthObj = {
|
|
1664
|
-
type: "None",
|
|
1665
|
-
};
|
|
1666
|
-
if (authInfo) {
|
|
1667
|
-
if (Utils.isOAuthWithAuthCodeFlow(authInfo.authScheme)) {
|
|
1668
|
-
pluginAuthObj.type = "OAuthPluginVault";
|
|
1669
|
-
}
|
|
1670
|
-
else if (Utils.isBearerTokenAuth(authInfo.authScheme)) {
|
|
1671
|
-
pluginAuthObj.type = "ApiKeyPluginVault";
|
|
1672
|
-
}
|
|
1673
|
-
if (pluginAuthObj.type !== "None") {
|
|
1674
|
-
const safeRegistrationIdName = Utils.getSafeRegistrationIdEnvName(`${authInfo.name}_${ConstantString.RegistrationIdPostfix[authInfo.authScheme.type]}`);
|
|
1675
|
-
pluginAuthObj.reference_id = `\${{${safeRegistrationIdName}}}`;
|
|
1676
|
-
}
|
|
1677
|
-
}
|
|
1678
1753
|
for (const pathUrl in paths) {
|
|
1679
1754
|
const pathItem = paths[pathUrl];
|
|
1680
1755
|
if (pathItem) {
|
|
@@ -1682,36 +1757,11 @@ class ManifestUpdater {
|
|
|
1682
1757
|
for (const method in operations) {
|
|
1683
1758
|
if (options.allowMethods.includes(method)) {
|
|
1684
1759
|
const operationItem = operations[method];
|
|
1685
|
-
const confirmationBodies = [];
|
|
1686
1760
|
if (operationItem) {
|
|
1687
1761
|
const operationId = operationItem.operationId;
|
|
1688
1762
|
const safeFunctionName = operationId.replace(/[^a-zA-Z0-9]/g, "_");
|
|
1689
1763
|
const description = (_a = operationItem.description) !== null && _a !== void 0 ? _a : "";
|
|
1690
1764
|
const summary = operationItem.summary;
|
|
1691
|
-
const paramObject = operationItem.parameters;
|
|
1692
|
-
const requestBody = operationItem.requestBody;
|
|
1693
|
-
if (paramObject) {
|
|
1694
|
-
for (let i = 0; i < paramObject.length; i++) {
|
|
1695
|
-
const param = paramObject[i];
|
|
1696
|
-
const schema = param.schema;
|
|
1697
|
-
ManifestUpdater.checkSchema(schema, method, pathUrl);
|
|
1698
|
-
confirmationBodies.push(ManifestUpdater.getConfirmationBodyItem(param.name));
|
|
1699
|
-
}
|
|
1700
|
-
}
|
|
1701
|
-
if (requestBody) {
|
|
1702
|
-
const requestJsonBody = requestBody.content["application/json"];
|
|
1703
|
-
const requestBodySchema = requestJsonBody.schema;
|
|
1704
|
-
if (requestBodySchema.type === "object") {
|
|
1705
|
-
for (const property in requestBodySchema.properties) {
|
|
1706
|
-
const schema = requestBodySchema.properties[property];
|
|
1707
|
-
ManifestUpdater.checkSchema(schema, method, pathUrl);
|
|
1708
|
-
confirmationBodies.push(ManifestUpdater.getConfirmationBodyItem(property));
|
|
1709
|
-
}
|
|
1710
|
-
}
|
|
1711
|
-
else {
|
|
1712
|
-
throw new SpecParserError(Utils.format(ConstantString.UnsupportedSchema, method, pathUrl, JSON.stringify(requestBodySchema)), ErrorType.UpdateManifestFailed);
|
|
1713
|
-
}
|
|
1714
|
-
}
|
|
1715
1765
|
let funcDescription = operationItem.description || operationItem.summary || "";
|
|
1716
1766
|
if (funcDescription.length > ConstantString.FunctionDescriptionMaxLens) {
|
|
1717
1767
|
warnings.push({
|
|
@@ -1729,8 +1779,7 @@ class ManifestUpdater {
|
|
|
1729
1779
|
try {
|
|
1730
1780
|
const { json } = Utils.getResponseJson(operationItem);
|
|
1731
1781
|
if (json.schema) {
|
|
1732
|
-
const [card, jsonPath] = AdaptiveCardGenerator.generateAdaptiveCard(operationItem);
|
|
1733
|
-
card.body = Utils.limitACBodyProperties(card.body, 5);
|
|
1782
|
+
const [card, jsonPath] = AdaptiveCardGenerator.generateAdaptiveCard(operationItem, false, 5);
|
|
1734
1783
|
const responseSemantic = wrapResponseSemantics(card, jsonPath);
|
|
1735
1784
|
funcObj.capabilities = {
|
|
1736
1785
|
response_semantics: responseSemantic,
|
|
@@ -1746,19 +1795,66 @@ class ManifestUpdater {
|
|
|
1746
1795
|
}
|
|
1747
1796
|
}
|
|
1748
1797
|
if (options.allowConfirmation && method !== ConstantString.GetMethod) {
|
|
1749
|
-
|
|
1750
|
-
|
|
1798
|
+
const paramObject = operationItem.parameters;
|
|
1799
|
+
const requestBody = operationItem.requestBody;
|
|
1800
|
+
const confirmationBodies = [];
|
|
1801
|
+
if (paramObject) {
|
|
1802
|
+
for (let i = 0; i < paramObject.length; i++) {
|
|
1803
|
+
const param = paramObject[i];
|
|
1804
|
+
const schema = param.schema;
|
|
1805
|
+
ManifestUpdater.checkSchema(schema, method, pathUrl);
|
|
1806
|
+
confirmationBodies.push(ManifestUpdater.getConfirmationBodyItem(param.name));
|
|
1807
|
+
}
|
|
1808
|
+
}
|
|
1809
|
+
if (requestBody) {
|
|
1810
|
+
const requestJsonBody = Utils.getJsonContentType(requestBody);
|
|
1811
|
+
const requestBodySchema = requestJsonBody.schema;
|
|
1812
|
+
if (Utils.isObjectSchema(requestBodySchema)) {
|
|
1813
|
+
for (const property in requestBodySchema.properties) {
|
|
1814
|
+
const schema = requestBodySchema.properties[property];
|
|
1815
|
+
ManifestUpdater.checkSchema(schema, method, pathUrl);
|
|
1816
|
+
confirmationBodies.push(ManifestUpdater.getConfirmationBodyItem(property));
|
|
1817
|
+
}
|
|
1818
|
+
}
|
|
1819
|
+
else {
|
|
1820
|
+
throw new SpecParserError(Utils.format(ConstantString.UnsupportedSchema, method, pathUrl, JSON.stringify(requestBodySchema)), ErrorType.UpdateManifestFailed);
|
|
1821
|
+
}
|
|
1751
1822
|
}
|
|
1752
|
-
funcObj.capabilities.confirmation = {
|
|
1753
|
-
type: "AdaptiveCard",
|
|
1754
|
-
title: (_b = operationItem.summary) !== null && _b !== void 0 ? _b : description,
|
|
1755
|
-
};
|
|
1756
1823
|
if (confirmationBodies.length > 0) {
|
|
1824
|
+
if (!funcObj.capabilities) {
|
|
1825
|
+
funcObj.capabilities = {};
|
|
1826
|
+
}
|
|
1827
|
+
funcObj.capabilities.confirmation = {
|
|
1828
|
+
type: "AdaptiveCard",
|
|
1829
|
+
title: (_b = operationItem.summary) !== null && _b !== void 0 ? _b : description,
|
|
1830
|
+
};
|
|
1757
1831
|
funcObj.capabilities.confirmation.body = confirmationBodies.join("\n");
|
|
1758
1832
|
}
|
|
1759
1833
|
}
|
|
1760
1834
|
functions.push(funcObj);
|
|
1761
|
-
|
|
1835
|
+
const authInfo = authMap[operationId];
|
|
1836
|
+
let key = "None";
|
|
1837
|
+
let authName = "None";
|
|
1838
|
+
if (authInfo) {
|
|
1839
|
+
if (Utils.isOAuthWithAuthCodeFlow(authInfo.authScheme)) {
|
|
1840
|
+
key = "OAuthPluginVault";
|
|
1841
|
+
authName = authInfo.name;
|
|
1842
|
+
}
|
|
1843
|
+
else if (Utils.isBearerTokenAuth(authInfo.authScheme) ||
|
|
1844
|
+
Utils.isAPIKeyAuthButNotInCookie(authInfo.authScheme)) {
|
|
1845
|
+
key = "ApiKeyPluginVault";
|
|
1846
|
+
authName = authInfo.name;
|
|
1847
|
+
}
|
|
1848
|
+
}
|
|
1849
|
+
if (functionNamesMap[key]) {
|
|
1850
|
+
functionNamesMap[key].functionNames.push(safeFunctionName);
|
|
1851
|
+
}
|
|
1852
|
+
else {
|
|
1853
|
+
functionNamesMap[key] = {
|
|
1854
|
+
functionNames: [safeFunctionName],
|
|
1855
|
+
authName: authName,
|
|
1856
|
+
};
|
|
1857
|
+
}
|
|
1762
1858
|
const conversationStarterStr = (summary !== null && summary !== void 0 ? summary : description).slice(0, ConstantString.ConversationStarterMaxLens);
|
|
1763
1859
|
if (conversationStarterStr) {
|
|
1764
1860
|
conversationStarters.push(conversationStarterStr);
|
|
@@ -1768,6 +1864,12 @@ class ManifestUpdater {
|
|
|
1768
1864
|
}
|
|
1769
1865
|
}
|
|
1770
1866
|
}
|
|
1867
|
+
if (Object.keys(functionNamesMap).length === 0) {
|
|
1868
|
+
functionNamesMap["None"] = {
|
|
1869
|
+
functionNames: [],
|
|
1870
|
+
authName: "None",
|
|
1871
|
+
};
|
|
1872
|
+
}
|
|
1771
1873
|
let apiPlugin;
|
|
1772
1874
|
if (await fs.pathExists(apiPluginFilePath)) {
|
|
1773
1875
|
apiPlugin = await fs.readJSON(apiPluginFilePath);
|
|
@@ -1803,24 +1905,35 @@ class ManifestUpdater {
|
|
|
1803
1905
|
const relativePath = ManifestUpdater.getRelativePath(existingPluginManifestInfo.manifestPath, existingPluginManifestInfo.specPath);
|
|
1804
1906
|
apiPlugin.runtimes = apiPlugin.runtimes.filter((runtime) => runtime.spec.url !== relativePath);
|
|
1805
1907
|
}
|
|
1806
|
-
const
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1908
|
+
for (const authType in functionNamesMap) {
|
|
1909
|
+
const pluginAuthObj = {
|
|
1910
|
+
type: authType,
|
|
1911
|
+
};
|
|
1912
|
+
const authName = functionNamesMap[authType].authName;
|
|
1913
|
+
if (pluginAuthObj.type !== "None") {
|
|
1914
|
+
const safeRegistrationIdName = Utils.getSafeRegistrationIdEnvName(`${authName}_${ConstantString.RegistrationIdPostfix}`);
|
|
1915
|
+
pluginAuthObj.reference_id = `\${{${safeRegistrationIdName}}}`;
|
|
1916
|
+
}
|
|
1917
|
+
const functionNamesInfo = functionNamesMap[authType];
|
|
1918
|
+
const index = apiPlugin.runtimes.findIndex((runtime) => {
|
|
1919
|
+
var _a, _b;
|
|
1920
|
+
return runtime.spec.url === specRelativePath &&
|
|
1921
|
+
runtime.type === "OpenApi" &&
|
|
1922
|
+
((_b = (_a = runtime.auth) === null || _a === void 0 ? void 0 : _a.type) !== null && _b !== void 0 ? _b : "None") === authType;
|
|
1820
1923
|
});
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1924
|
+
if (index === -1) {
|
|
1925
|
+
apiPlugin.runtimes.push({
|
|
1926
|
+
type: "OpenApi",
|
|
1927
|
+
auth: pluginAuthObj,
|
|
1928
|
+
spec: {
|
|
1929
|
+
url: specRelativePath,
|
|
1930
|
+
},
|
|
1931
|
+
run_for_functions: functionNamesInfo.functionNames,
|
|
1932
|
+
});
|
|
1933
|
+
}
|
|
1934
|
+
else {
|
|
1935
|
+
apiPlugin.runtimes[index].run_for_functions = functionNamesInfo.functionNames;
|
|
1936
|
+
}
|
|
1824
1937
|
}
|
|
1825
1938
|
if (!apiPlugin.name_for_human) {
|
|
1826
1939
|
apiPlugin.name_for_human = appName;
|
|
@@ -1861,7 +1974,7 @@ class ManifestUpdater {
|
|
|
1861
1974
|
};
|
|
1862
1975
|
if (authInfo) {
|
|
1863
1976
|
const auth = authInfo.authScheme;
|
|
1864
|
-
const safeRegistrationIdName = Utils.getSafeRegistrationIdEnvName(`${authInfo.name}_${ConstantString.RegistrationIdPostfix
|
|
1977
|
+
const safeRegistrationIdName = Utils.getSafeRegistrationIdEnvName(`${authInfo.name}_${ConstantString.RegistrationIdPostfix}`);
|
|
1865
1978
|
if (Utils.isAPIKeyAuth(auth) || Utils.isBearerTokenAuth(auth)) {
|
|
1866
1979
|
composeExtension.authorization = {
|
|
1867
1980
|
authType: "apiSecretServiceAuth",
|
|
@@ -2188,7 +2301,31 @@ class SpecParser {
|
|
|
2188
2301
|
const newSpecs = await this.getFilteredSpecs(filter, signal);
|
|
2189
2302
|
const newUnResolvedSpec = newSpecs[0];
|
|
2190
2303
|
const newSpec = newSpecs[1];
|
|
2191
|
-
const
|
|
2304
|
+
const paths = newUnResolvedSpec.paths;
|
|
2305
|
+
for (const pathUrl in paths) {
|
|
2306
|
+
const operations = paths[pathUrl];
|
|
2307
|
+
for (const method in operations) {
|
|
2308
|
+
const operationItem = operations[method];
|
|
2309
|
+
const operationId = operationItem.operationId;
|
|
2310
|
+
const containsSpecialCharacters = /[^a-zA-Z0-9_]/.test(operationId);
|
|
2311
|
+
if (containsSpecialCharacters) {
|
|
2312
|
+
operationItem.operationId = operationId.replace(/[^a-zA-Z0-9]/g, "_");
|
|
2313
|
+
result.warnings.push({
|
|
2314
|
+
type: WarningType.OperationIdContainsSpecialCharacters,
|
|
2315
|
+
content: Utils.format(ConstantString.OperationIdContainsSpecialCharacters, operationId, operationItem.operationId),
|
|
2316
|
+
data: operationId,
|
|
2317
|
+
});
|
|
2318
|
+
}
|
|
2319
|
+
const authArray = Utils.getAuthArray(operationItem.security, newSpec);
|
|
2320
|
+
if (Utils.isNotSupportedAuth(authArray)) {
|
|
2321
|
+
result.warnings.push({
|
|
2322
|
+
type: WarningType.UnsupportedAuthType,
|
|
2323
|
+
content: Utils.format(ConstantString.AuthTypeIsNotSupported, operationId),
|
|
2324
|
+
data: operationId,
|
|
2325
|
+
});
|
|
2326
|
+
}
|
|
2327
|
+
}
|
|
2328
|
+
}
|
|
2192
2329
|
await this.saveFilterSpec(outputSpecPath, newUnResolvedSpec);
|
|
2193
2330
|
if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
|
|
2194
2331
|
throw new SpecParserError(ConstantString.CancelledMessage, ErrorType.Cancelled);
|
|
@@ -2199,7 +2336,8 @@ class SpecParser {
|
|
|
2199
2336
|
specPath: this.pathOrSpec,
|
|
2200
2337
|
}
|
|
2201
2338
|
: undefined;
|
|
2202
|
-
const
|
|
2339
|
+
const authMap = Utils.getAuthMap(newSpec);
|
|
2340
|
+
const [updatedManifest, apiPlugin, warnings] = await ManifestUpdater.updateManifestWithAiPlugin(manifestPath, outputSpecPath, pluginFilePath, newSpec, this.options, authMap, existingPluginManifestInfo);
|
|
2203
2341
|
result.warnings.push(...warnings);
|
|
2204
2342
|
await fs.outputJSON(manifestPath, updatedManifest, { spaces: 4 });
|
|
2205
2343
|
await fs.outputJSON(pluginFilePath, apiPlugin, { spaces: 4 });
|
|
@@ -2240,13 +2378,14 @@ class SpecParser {
|
|
|
2240
2378
|
if (this.options.allowMethods.includes(method)) {
|
|
2241
2379
|
const operation = newSpec.paths[url][method];
|
|
2242
2380
|
try {
|
|
2243
|
-
const [card, jsonPath] = AdaptiveCardGenerator.generateAdaptiveCard(operation);
|
|
2381
|
+
const [card, jsonPath, jsonData, warnings] = AdaptiveCardGenerator.generateAdaptiveCard(operation);
|
|
2382
|
+
result.warnings.push(...warnings);
|
|
2244
2383
|
const safeAdaptiveCardName = operation.operationId.replace(/[^a-zA-Z0-9]/g, "_");
|
|
2245
2384
|
const fileName = path.join(adaptiveCardFolder, `${safeAdaptiveCardName}.json`);
|
|
2246
2385
|
const wrappedCard = wrapAdaptiveCard(card, jsonPath);
|
|
2247
2386
|
await fs.outputJSON(fileName, wrappedCard, { spaces: 2 });
|
|
2248
2387
|
const dataFileName = path.join(adaptiveCardFolder, `${safeAdaptiveCardName}.data.json`);
|
|
2249
|
-
await fs.outputJSON(dataFileName,
|
|
2388
|
+
await fs.outputJSON(dataFileName, jsonData, { spaces: 2 });
|
|
2250
2389
|
}
|
|
2251
2390
|
catch (err) {
|
|
2252
2391
|
result.allSuccess = false;
|