@microsoft/m365-spec-parser 0.1.1-alpha.f04ac4ba4.0 → 0.1.1-alpha.f2bd7316b.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 +635 -334
- package/dist/index.esm2017.js.map +1 -1
- package/dist/index.esm2017.mjs +1076 -631
- package/dist/index.esm2017.mjs.map +1 -1
- package/dist/index.esm5.js +635 -334
- package/dist/index.esm5.js.map +1 -1
- package/dist/index.node.cjs.js +1089 -640
- package/dist/index.node.cjs.js.map +1 -1
- package/dist/src/adaptiveCardWrapper.d.ts +2 -0
- package/dist/src/constants.d.ts +9 -3
- package/dist/src/index.d.ts +1 -1
- package/dist/src/interfaces.d.ts +83 -20
- package/dist/src/manifestUpdater.d.ts +9 -6
- package/dist/src/specParser.browser.d.ts +3 -2
- package/dist/src/specParser.d.ts +5 -3
- package/dist/src/utils.d.ts +17 -33
- package/package.json +3 -3
package/dist/index.esm5.js
CHANGED
|
@@ -45,7 +45,8 @@ var ErrorType;
|
|
|
45
45
|
ErrorType["NoExtraAPICanBeAdded"] = "no-extra-api-can-be-added";
|
|
46
46
|
ErrorType["ResolveServerUrlFailed"] = "resolve-server-url-failed";
|
|
47
47
|
ErrorType["SwaggerNotSupported"] = "swagger-not-supported";
|
|
48
|
-
ErrorType["
|
|
48
|
+
ErrorType["MultipleAuthNotSupported"] = "multiple-auth-not-supported";
|
|
49
|
+
ErrorType["SpecVersionNotSupported"] = "spec-version-not-supported";
|
|
49
50
|
ErrorType["ListFailed"] = "list-failed";
|
|
50
51
|
ErrorType["listSupportedAPIInfoFailed"] = "list-supported-api-info-failed";
|
|
51
52
|
ErrorType["FilterSpecFailed"] = "filter-spec-failed";
|
|
@@ -54,6 +55,21 @@ var ErrorType;
|
|
|
54
55
|
ErrorType["GenerateFailed"] = "generate-failed";
|
|
55
56
|
ErrorType["ValidateFailed"] = "validate-failed";
|
|
56
57
|
ErrorType["GetSpecFailed"] = "get-spec-failed";
|
|
58
|
+
ErrorType["AuthTypeIsNotSupported"] = "auth-type-is-not-supported";
|
|
59
|
+
ErrorType["MissingOperationId"] = "missing-operation-id";
|
|
60
|
+
ErrorType["PostBodyContainMultipleMediaTypes"] = "post-body-contain-multiple-media-types";
|
|
61
|
+
ErrorType["ResponseContainMultipleMediaTypes"] = "response-contain-multiple-media-types";
|
|
62
|
+
ErrorType["ResponseJsonIsEmpty"] = "response-json-is-empty";
|
|
63
|
+
ErrorType["PostBodySchemaIsNotJson"] = "post-body-schema-is-not-json";
|
|
64
|
+
ErrorType["PostBodyContainsRequiredUnsupportedSchema"] = "post-body-contains-required-unsupported-schema";
|
|
65
|
+
ErrorType["ParamsContainRequiredUnsupportedSchema"] = "params-contain-required-unsupported-schema";
|
|
66
|
+
ErrorType["ParamsContainsNestedObject"] = "params-contains-nested-object";
|
|
67
|
+
ErrorType["RequestBodyContainsNestedObject"] = "request-body-contains-nested-object";
|
|
68
|
+
ErrorType["ExceededRequiredParamsLimit"] = "exceeded-required-params-limit";
|
|
69
|
+
ErrorType["NoParameter"] = "no-parameter";
|
|
70
|
+
ErrorType["NoAPIInfo"] = "no-api-info";
|
|
71
|
+
ErrorType["MethodNotAllowed"] = "method-not-allowed";
|
|
72
|
+
ErrorType["UrlPathNotExist"] = "url-path-not-exist";
|
|
57
73
|
ErrorType["Cancelled"] = "cancelled";
|
|
58
74
|
ErrorType["Unknown"] = "unknown";
|
|
59
75
|
})(ErrorType || (ErrorType = {}));
|
|
@@ -101,7 +117,7 @@ ConstantString.RemoteRefNotSupported = "Remote reference is not supported: %s.";
|
|
|
101
117
|
ConstantString.MissingOperationId = "Missing operationIds: %s.";
|
|
102
118
|
ConstantString.NoSupportedApi = "No supported API is found in the OpenAPI description document: only GET and POST methods are supported, additionally, there can be at most one required parameter, and no auth is allowed.";
|
|
103
119
|
ConstantString.AdditionalPropertiesNotSupported = "'additionalProperties' is not supported, and will be ignored.";
|
|
104
|
-
ConstantString.SchemaNotSupported = "'oneOf', 'anyOf', and 'not' schema are not supported: %s.";
|
|
120
|
+
ConstantString.SchemaNotSupported = "'oneOf', 'allOf', 'anyOf', and 'not' schema are not supported: %s.";
|
|
105
121
|
ConstantString.UnknownSchema = "Unknown schema: %s.";
|
|
106
122
|
ConstantString.UrlProtocolNotSupported = "Server url is not correct: protocol %s is not supported, you should use https protocol instead.";
|
|
107
123
|
ConstantString.RelativeServerUrlNotSupported = "Server url is not correct: relative server url is not supported.";
|
|
@@ -109,7 +125,8 @@ ConstantString.ResolveServerUrlFailed = "Unable to resolve the server URL: pleas
|
|
|
109
125
|
ConstantString.OperationOnlyContainsOptionalParam = "Operation %s contains multiple optional parameters. The first optional parameter is used for this command.";
|
|
110
126
|
ConstantString.ConvertSwaggerToOpenAPI = "The Swagger 2.0 file has been converted to OpenAPI 3.0.";
|
|
111
127
|
ConstantString.SwaggerNotSupported = "Swagger 2.0 is not supported. Please convert to OpenAPI 3.0 manually before proceeding.";
|
|
112
|
-
ConstantString.
|
|
128
|
+
ConstantString.SpecVersionNotSupported = "Unsupported OpenAPI version %s. Please use version 3.0.x.";
|
|
129
|
+
ConstantString.MultipleAuthNotSupported = "Multiple authentication methods are unsupported. Ensure all selected APIs use identical authentication.";
|
|
113
130
|
ConstantString.UnsupportedSchema = "Unsupported schema in %s %s: %s";
|
|
114
131
|
ConstantString.WrappedCardVersion = "devPreview";
|
|
115
132
|
ConstantString.WrappedCardSchema = "https://developer.microsoft.com/json-schemas/teams/vDevPreview/MicrosoftTeams.ResponseRenderingTemplate.schema.json";
|
|
@@ -120,8 +137,14 @@ ConstantString.AdaptiveCardVersion = "1.5";
|
|
|
120
137
|
ConstantString.AdaptiveCardSchema = "http://adaptivecards.io/schemas/adaptive-card.json";
|
|
121
138
|
ConstantString.AdaptiveCardType = "AdaptiveCard";
|
|
122
139
|
ConstantString.TextBlockType = "TextBlock";
|
|
140
|
+
ConstantString.ImageType = "Image";
|
|
123
141
|
ConstantString.ContainerType = "Container";
|
|
124
|
-
ConstantString.RegistrationIdPostfix =
|
|
142
|
+
ConstantString.RegistrationIdPostfix = {
|
|
143
|
+
apiKey: "REGISTRATION_ID",
|
|
144
|
+
oauth2: "CONFIGURATION_ID",
|
|
145
|
+
http: "REGISTRATION_ID",
|
|
146
|
+
openIdConnect: "REGISTRATION_ID",
|
|
147
|
+
};
|
|
125
148
|
ConstantString.ResponseCodeFor20X = [
|
|
126
149
|
"200",
|
|
127
150
|
"201",
|
|
@@ -180,9 +203,11 @@ ConstantString.ShortDescriptionMaxLens = 80;
|
|
|
180
203
|
ConstantString.FullDescriptionMaxLens = 4000;
|
|
181
204
|
ConstantString.CommandDescriptionMaxLens = 128;
|
|
182
205
|
ConstantString.ParameterDescriptionMaxLens = 128;
|
|
206
|
+
ConstantString.ConversationStarterMaxLens = 50;
|
|
183
207
|
ConstantString.CommandTitleMaxLens = 32;
|
|
184
208
|
ConstantString.ParameterTitleMaxLens = 32;
|
|
185
|
-
ConstantString.SMERequiredParamsMaxNum = 5;
|
|
209
|
+
ConstantString.SMERequiredParamsMaxNum = 5;
|
|
210
|
+
ConstantString.DefaultPluginId = "plugin_1";
|
|
186
211
|
|
|
187
212
|
// Copyright (c) Microsoft Corporation.
|
|
188
213
|
class Utils {
|
|
@@ -197,242 +222,33 @@ class Utils {
|
|
|
197
222
|
}
|
|
198
223
|
return false;
|
|
199
224
|
}
|
|
200
|
-
static
|
|
201
|
-
|
|
202
|
-
requiredNum: 0,
|
|
203
|
-
optionalNum: 0,
|
|
204
|
-
isValid: true,
|
|
205
|
-
};
|
|
206
|
-
if (!paramObject) {
|
|
207
|
-
return paramResult;
|
|
208
|
-
}
|
|
209
|
-
for (let i = 0; i < paramObject.length; i++) {
|
|
210
|
-
const param = paramObject[i];
|
|
211
|
-
const schema = param.schema;
|
|
212
|
-
if (isCopilot && this.hasNestedObjectInSchema(schema)) {
|
|
213
|
-
paramResult.isValid = false;
|
|
214
|
-
continue;
|
|
215
|
-
}
|
|
216
|
-
const isRequiredWithoutDefault = param.required && schema.default === undefined;
|
|
217
|
-
if (isCopilot) {
|
|
218
|
-
if (isRequiredWithoutDefault) {
|
|
219
|
-
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
220
|
-
}
|
|
221
|
-
else {
|
|
222
|
-
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
223
|
-
}
|
|
224
|
-
continue;
|
|
225
|
-
}
|
|
226
|
-
if (param.in === "header" || param.in === "cookie") {
|
|
227
|
-
if (isRequiredWithoutDefault) {
|
|
228
|
-
paramResult.isValid = false;
|
|
229
|
-
}
|
|
230
|
-
continue;
|
|
231
|
-
}
|
|
232
|
-
if (schema.type !== "boolean" &&
|
|
233
|
-
schema.type !== "string" &&
|
|
234
|
-
schema.type !== "number" &&
|
|
235
|
-
schema.type !== "integer") {
|
|
236
|
-
if (isRequiredWithoutDefault) {
|
|
237
|
-
paramResult.isValid = false;
|
|
238
|
-
}
|
|
239
|
-
continue;
|
|
240
|
-
}
|
|
241
|
-
if (param.in === "query" || param.in === "path") {
|
|
242
|
-
if (isRequiredWithoutDefault) {
|
|
243
|
-
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
244
|
-
}
|
|
245
|
-
else {
|
|
246
|
-
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
return paramResult;
|
|
225
|
+
static containMultipleMediaTypes(bodyObject) {
|
|
226
|
+
return Object.keys((bodyObject === null || bodyObject === void 0 ? void 0 : bodyObject.content) || {}).length > 1;
|
|
251
227
|
}
|
|
252
|
-
static
|
|
253
|
-
|
|
254
|
-
const paramResult = {
|
|
255
|
-
requiredNum: 0,
|
|
256
|
-
optionalNum: 0,
|
|
257
|
-
isValid: true,
|
|
258
|
-
};
|
|
259
|
-
if (Object.keys(schema).length === 0) {
|
|
260
|
-
return paramResult;
|
|
261
|
-
}
|
|
262
|
-
const isRequiredWithoutDefault = isRequired && schema.default === undefined;
|
|
263
|
-
if (isCopilot && this.hasNestedObjectInSchema(schema)) {
|
|
264
|
-
paramResult.isValid = false;
|
|
265
|
-
return paramResult;
|
|
266
|
-
}
|
|
267
|
-
if (schema.type === "string" ||
|
|
268
|
-
schema.type === "integer" ||
|
|
269
|
-
schema.type === "boolean" ||
|
|
270
|
-
schema.type === "number") {
|
|
271
|
-
if (isRequiredWithoutDefault) {
|
|
272
|
-
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
273
|
-
}
|
|
274
|
-
else {
|
|
275
|
-
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
else if (schema.type === "object") {
|
|
279
|
-
const { properties } = schema;
|
|
280
|
-
for (const property in properties) {
|
|
281
|
-
let isRequired = false;
|
|
282
|
-
if (schema.required && ((_a = schema.required) === null || _a === void 0 ? void 0 : _a.indexOf(property)) >= 0) {
|
|
283
|
-
isRequired = true;
|
|
284
|
-
}
|
|
285
|
-
const result = Utils.checkPostBody(properties[property], isRequired, isCopilot);
|
|
286
|
-
paramResult.requiredNum += result.requiredNum;
|
|
287
|
-
paramResult.optionalNum += result.optionalNum;
|
|
288
|
-
paramResult.isValid = paramResult.isValid && result.isValid;
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
else {
|
|
292
|
-
if (isRequiredWithoutDefault && !isCopilot) {
|
|
293
|
-
paramResult.isValid = false;
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
return paramResult;
|
|
228
|
+
static isBearerTokenAuth(authScheme) {
|
|
229
|
+
return authScheme.type === "http" && authScheme.scheme === "bearer";
|
|
297
230
|
}
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
* @param {string} method - The HTTP method of the API.
|
|
301
|
-
* @param {string} path - The path of the API.
|
|
302
|
-
* @param {OpenAPIV3.Document} spec - The OpenAPI specification document.
|
|
303
|
-
* @returns {boolean} - Returns true if the API is supported, false otherwise.
|
|
304
|
-
* @description The following APIs are supported:
|
|
305
|
-
* 1. only support Get/Post operation without auth property
|
|
306
|
-
* 2. parameter inside query or path only support string, number, boolean and integer
|
|
307
|
-
* 3. parameter inside post body only support string, number, boolean, integer and object
|
|
308
|
-
* 4. request body + required parameters <= 1
|
|
309
|
-
* 5. response body should be “application/json” and not empty, and response code should be 20X
|
|
310
|
-
* 6. only support request body with “application/json” content type
|
|
311
|
-
*/
|
|
312
|
-
static isSupportedApi(method, path, spec, options) {
|
|
313
|
-
var _a;
|
|
314
|
-
const pathObj = spec.paths[path];
|
|
315
|
-
method = method.toLocaleLowerCase();
|
|
316
|
-
if (pathObj) {
|
|
317
|
-
if (((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && pathObj[method]) {
|
|
318
|
-
const securities = pathObj[method].security;
|
|
319
|
-
const authArray = Utils.getAuthArray(securities, spec);
|
|
320
|
-
if (!Utils.isSupportedAuth(authArray, options)) {
|
|
321
|
-
return false;
|
|
322
|
-
}
|
|
323
|
-
const operationObject = pathObj[method];
|
|
324
|
-
if (!options.allowMissingId && !operationObject.operationId) {
|
|
325
|
-
return false;
|
|
326
|
-
}
|
|
327
|
-
const paramObject = operationObject.parameters;
|
|
328
|
-
const requestBody = operationObject.requestBody;
|
|
329
|
-
const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
|
|
330
|
-
const mediaTypesCount = Object.keys((requestBody === null || requestBody === void 0 ? void 0 : requestBody.content) || {}).length;
|
|
331
|
-
if (mediaTypesCount > 1) {
|
|
332
|
-
return false;
|
|
333
|
-
}
|
|
334
|
-
const responseJson = Utils.getResponseJson(operationObject);
|
|
335
|
-
if (Object.keys(responseJson).length === 0) {
|
|
336
|
-
return false;
|
|
337
|
-
}
|
|
338
|
-
let requestBodyParamResult = {
|
|
339
|
-
requiredNum: 0,
|
|
340
|
-
optionalNum: 0,
|
|
341
|
-
isValid: true,
|
|
342
|
-
};
|
|
343
|
-
const isCopilot = options.projectType === ProjectType.Copilot;
|
|
344
|
-
if (requestJsonBody) {
|
|
345
|
-
const requestBodySchema = requestJsonBody.schema;
|
|
346
|
-
if (isCopilot && requestBodySchema.type !== "object") {
|
|
347
|
-
return false;
|
|
348
|
-
}
|
|
349
|
-
requestBodyParamResult = Utils.checkPostBody(requestBodySchema, requestBody.required, isCopilot);
|
|
350
|
-
}
|
|
351
|
-
if (!requestBodyParamResult.isValid) {
|
|
352
|
-
return false;
|
|
353
|
-
}
|
|
354
|
-
const paramResult = Utils.checkParameters(paramObject, isCopilot);
|
|
355
|
-
if (!paramResult.isValid) {
|
|
356
|
-
return false;
|
|
357
|
-
}
|
|
358
|
-
// Copilot support arbitrary parameters
|
|
359
|
-
if (isCopilot) {
|
|
360
|
-
return true;
|
|
361
|
-
}
|
|
362
|
-
if (requestBodyParamResult.requiredNum + paramResult.requiredNum > 1) {
|
|
363
|
-
if (options.allowMultipleParameters &&
|
|
364
|
-
requestBodyParamResult.requiredNum + paramResult.requiredNum <=
|
|
365
|
-
ConstantString.SMERequiredParamsMaxNum) {
|
|
366
|
-
return true;
|
|
367
|
-
}
|
|
368
|
-
return false;
|
|
369
|
-
}
|
|
370
|
-
else if (requestBodyParamResult.requiredNum +
|
|
371
|
-
requestBodyParamResult.optionalNum +
|
|
372
|
-
paramResult.requiredNum +
|
|
373
|
-
paramResult.optionalNum ===
|
|
374
|
-
0) {
|
|
375
|
-
return false;
|
|
376
|
-
}
|
|
377
|
-
else {
|
|
378
|
-
return true;
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
return false;
|
|
231
|
+
static isAPIKeyAuth(authScheme) {
|
|
232
|
+
return authScheme.type === "apiKey";
|
|
383
233
|
}
|
|
384
|
-
static
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
if (options.allowAPIKeyAuth || options.allowOauth2) {
|
|
389
|
-
// Currently we don't support multiple auth in one operation
|
|
390
|
-
if (authSchemaArray.length > 0 && authSchemaArray.every((auths) => auths.length > 1)) {
|
|
391
|
-
return false;
|
|
392
|
-
}
|
|
393
|
-
for (const auths of authSchemaArray) {
|
|
394
|
-
if (auths.length === 1) {
|
|
395
|
-
if (!options.allowOauth2 &&
|
|
396
|
-
options.allowAPIKeyAuth &&
|
|
397
|
-
Utils.isAPIKeyAuth(auths[0].authSchema)) {
|
|
398
|
-
return true;
|
|
399
|
-
}
|
|
400
|
-
else if (!options.allowAPIKeyAuth &&
|
|
401
|
-
options.allowOauth2 &&
|
|
402
|
-
Utils.isBearerTokenAuth(auths[0].authSchema)) {
|
|
403
|
-
return true;
|
|
404
|
-
}
|
|
405
|
-
else if (options.allowAPIKeyAuth &&
|
|
406
|
-
options.allowOauth2 &&
|
|
407
|
-
(Utils.isAPIKeyAuth(auths[0].authSchema) ||
|
|
408
|
-
Utils.isBearerTokenAuth(auths[0].authSchema))) {
|
|
409
|
-
return true;
|
|
410
|
-
}
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
return false;
|
|
415
|
-
}
|
|
416
|
-
static isAPIKeyAuth(authSchema) {
|
|
417
|
-
return authSchema.type === "apiKey";
|
|
418
|
-
}
|
|
419
|
-
static isBearerTokenAuth(authSchema) {
|
|
420
|
-
return (authSchema.type === "oauth2" ||
|
|
421
|
-
authSchema.type === "openIdConnect" ||
|
|
422
|
-
(authSchema.type === "http" && authSchema.scheme === "bearer"));
|
|
234
|
+
static isOAuthWithAuthCodeFlow(authScheme) {
|
|
235
|
+
return !!(authScheme.type === "oauth2" &&
|
|
236
|
+
authScheme.flows &&
|
|
237
|
+
authScheme.flows.authorizationCode);
|
|
423
238
|
}
|
|
424
239
|
static getAuthArray(securities, spec) {
|
|
425
240
|
var _a;
|
|
426
241
|
const result = [];
|
|
427
242
|
const securitySchemas = (_a = spec.components) === null || _a === void 0 ? void 0 : _a.securitySchemes;
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
243
|
+
const securitiesArr = securities !== null && securities !== void 0 ? securities : spec.security;
|
|
244
|
+
if (securitiesArr && securitySchemas) {
|
|
245
|
+
for (let i = 0; i < securitiesArr.length; i++) {
|
|
246
|
+
const security = securitiesArr[i];
|
|
431
247
|
const authArray = [];
|
|
432
248
|
for (const name in security) {
|
|
433
249
|
const auth = securitySchemas[name];
|
|
434
250
|
authArray.push({
|
|
435
|
-
|
|
251
|
+
authScheme: auth,
|
|
436
252
|
name: name,
|
|
437
253
|
});
|
|
438
254
|
}
|
|
@@ -444,24 +260,47 @@ class Utils {
|
|
|
444
260
|
result.sort((a, b) => a[0].name.localeCompare(b[0].name));
|
|
445
261
|
return result;
|
|
446
262
|
}
|
|
263
|
+
static getAuthInfo(spec) {
|
|
264
|
+
let authInfo = undefined;
|
|
265
|
+
for (const url in spec.paths) {
|
|
266
|
+
for (const method in spec.paths[url]) {
|
|
267
|
+
const operation = spec.paths[url][method];
|
|
268
|
+
const authArray = Utils.getAuthArray(operation.security, spec);
|
|
269
|
+
if (authArray && authArray.length > 0) {
|
|
270
|
+
const currentAuth = authArray[0][0];
|
|
271
|
+
if (!authInfo) {
|
|
272
|
+
authInfo = authArray[0][0];
|
|
273
|
+
}
|
|
274
|
+
else if (authInfo.name !== currentAuth.name) {
|
|
275
|
+
throw new SpecParserError(ConstantString.MultipleAuthNotSupported, ErrorType.MultipleAuthNotSupported);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
return authInfo;
|
|
281
|
+
}
|
|
447
282
|
static updateFirstLetter(str) {
|
|
448
283
|
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
449
284
|
}
|
|
450
285
|
static getResponseJson(operationObject) {
|
|
451
286
|
var _a, _b;
|
|
452
287
|
let json = {};
|
|
288
|
+
let multipleMediaType = false;
|
|
453
289
|
for (const code of ConstantString.ResponseCodeFor20X) {
|
|
454
290
|
const responseObject = (_a = operationObject === null || operationObject === void 0 ? void 0 : operationObject.responses) === null || _a === void 0 ? void 0 : _a[code];
|
|
455
|
-
const mediaTypesCount = Object.keys((responseObject === null || responseObject === void 0 ? void 0 : responseObject.content) || {}).length;
|
|
456
|
-
if (mediaTypesCount > 1) {
|
|
457
|
-
return {};
|
|
458
|
-
}
|
|
459
291
|
if ((_b = responseObject === null || responseObject === void 0 ? void 0 : responseObject.content) === null || _b === void 0 ? void 0 : _b["application/json"]) {
|
|
292
|
+
multipleMediaType = false;
|
|
460
293
|
json = responseObject.content["application/json"];
|
|
461
|
-
|
|
294
|
+
if (Utils.containMultipleMediaTypes(responseObject)) {
|
|
295
|
+
multipleMediaType = true;
|
|
296
|
+
json = {};
|
|
297
|
+
}
|
|
298
|
+
else {
|
|
299
|
+
break;
|
|
300
|
+
}
|
|
462
301
|
}
|
|
463
302
|
}
|
|
464
|
-
return json;
|
|
303
|
+
return { json, multipleMediaType };
|
|
465
304
|
}
|
|
466
305
|
static convertPathToCamelCase(path) {
|
|
467
306
|
const pathSegments = path.split(/[./{]/);
|
|
@@ -481,10 +320,10 @@ class Utils {
|
|
|
481
320
|
return undefined;
|
|
482
321
|
}
|
|
483
322
|
}
|
|
484
|
-
static
|
|
323
|
+
static resolveEnv(str) {
|
|
485
324
|
const placeHolderReg = /\${{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*}}/g;
|
|
486
|
-
let matches = placeHolderReg.exec(
|
|
487
|
-
let
|
|
325
|
+
let matches = placeHolderReg.exec(str);
|
|
326
|
+
let newStr = str;
|
|
488
327
|
while (matches != null) {
|
|
489
328
|
const envVar = matches[1];
|
|
490
329
|
const envVal = process.env[envVar];
|
|
@@ -492,17 +331,17 @@ class Utils {
|
|
|
492
331
|
throw new Error(Utils.format(ConstantString.ResolveServerUrlFailed, envVar));
|
|
493
332
|
}
|
|
494
333
|
else {
|
|
495
|
-
|
|
334
|
+
newStr = newStr.replace(matches[0], envVal);
|
|
496
335
|
}
|
|
497
|
-
matches = placeHolderReg.exec(
|
|
336
|
+
matches = placeHolderReg.exec(str);
|
|
498
337
|
}
|
|
499
|
-
return
|
|
338
|
+
return newStr;
|
|
500
339
|
}
|
|
501
340
|
static checkServerUrl(servers) {
|
|
502
341
|
const errors = [];
|
|
503
342
|
let serverUrl;
|
|
504
343
|
try {
|
|
505
|
-
serverUrl = Utils.
|
|
344
|
+
serverUrl = Utils.resolveEnv(servers[0].url);
|
|
506
345
|
}
|
|
507
346
|
catch (err) {
|
|
508
347
|
errors.push({
|
|
@@ -533,6 +372,7 @@ class Utils {
|
|
|
533
372
|
return errors;
|
|
534
373
|
}
|
|
535
374
|
static validateServer(spec, options) {
|
|
375
|
+
var _a;
|
|
536
376
|
const errors = [];
|
|
537
377
|
let hasTopLevelServers = false;
|
|
538
378
|
let hasPathLevelServers = false;
|
|
@@ -553,7 +393,7 @@ class Utils {
|
|
|
553
393
|
}
|
|
554
394
|
for (const method in methods) {
|
|
555
395
|
const operationObject = methods[method];
|
|
556
|
-
if (
|
|
396
|
+
if (((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && operationObject) {
|
|
557
397
|
if ((operationObject === null || operationObject === void 0 ? void 0 : operationObject.servers) && operationObject.servers.length >= 1) {
|
|
558
398
|
hasOperationLevelServers = true;
|
|
559
399
|
const serverErrors = Utils.checkServerUrl(operationObject.servers);
|
|
@@ -596,6 +436,7 @@ class Utils {
|
|
|
596
436
|
Utils.updateParameterWithInputType(schema, parameter);
|
|
597
437
|
}
|
|
598
438
|
if (isRequired && schema.default === undefined) {
|
|
439
|
+
parameter.isRequired = true;
|
|
599
440
|
requiredParams.push(parameter);
|
|
600
441
|
}
|
|
601
442
|
else {
|
|
@@ -659,6 +500,7 @@ class Utils {
|
|
|
659
500
|
}
|
|
660
501
|
if (param.in !== "header" && param.in !== "cookie") {
|
|
661
502
|
if (param.required && (schema === null || schema === void 0 ? void 0 : schema.default) === undefined) {
|
|
503
|
+
parameter.isRequired = true;
|
|
662
504
|
requiredParams.push(parameter);
|
|
663
505
|
}
|
|
664
506
|
else {
|
|
@@ -678,13 +520,7 @@ class Utils {
|
|
|
678
520
|
}
|
|
679
521
|
}
|
|
680
522
|
const operationId = operationItem.operationId;
|
|
681
|
-
const parameters = [];
|
|
682
|
-
if (requiredParams.length !== 0) {
|
|
683
|
-
parameters.push(...requiredParams);
|
|
684
|
-
}
|
|
685
|
-
else {
|
|
686
|
-
parameters.push(optionalParams[0]);
|
|
687
|
-
}
|
|
523
|
+
const parameters = [...requiredParams, ...optionalParams];
|
|
688
524
|
const command = {
|
|
689
525
|
context: ["compose"],
|
|
690
526
|
type: "query",
|
|
@@ -693,105 +529,534 @@ class Utils {
|
|
|
693
529
|
parameters: parameters,
|
|
694
530
|
description: ((_b = operationItem.description) !== null && _b !== void 0 ? _b : "").slice(0, ConstantString.CommandDescriptionMaxLens),
|
|
695
531
|
};
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
532
|
+
return command;
|
|
533
|
+
}
|
|
534
|
+
static format(str, ...args) {
|
|
535
|
+
let index = 0;
|
|
536
|
+
return str.replace(/%s/g, () => {
|
|
537
|
+
const arg = args[index++];
|
|
538
|
+
return arg !== undefined ? arg : "";
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
static getSafeRegistrationIdEnvName(authName) {
|
|
542
|
+
if (!authName) {
|
|
543
|
+
return "";
|
|
544
|
+
}
|
|
545
|
+
let safeRegistrationIdEnvName = authName.toUpperCase().replace(/[^A-Z0-9_]/g, "_");
|
|
546
|
+
if (!safeRegistrationIdEnvName.match(/^[A-Z]/)) {
|
|
547
|
+
safeRegistrationIdEnvName = "PREFIX_" + safeRegistrationIdEnvName;
|
|
703
548
|
}
|
|
704
|
-
return
|
|
549
|
+
return safeRegistrationIdEnvName;
|
|
705
550
|
}
|
|
706
|
-
static
|
|
707
|
-
const
|
|
551
|
+
static getServerObject(spec, method, path) {
|
|
552
|
+
const pathObj = spec.paths[path];
|
|
553
|
+
const operationObject = pathObj[method];
|
|
554
|
+
const rootServer = spec.servers && spec.servers[0];
|
|
555
|
+
const methodServer = spec.paths[path].servers && spec.paths[path].servers[0];
|
|
556
|
+
const operationServer = operationObject.servers && operationObject.servers[0];
|
|
557
|
+
const serverUrl = operationServer || methodServer || rootServer;
|
|
558
|
+
return serverUrl;
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
// Copyright (c) Microsoft Corporation.
|
|
563
|
+
class Validator {
|
|
564
|
+
listAPIs() {
|
|
565
|
+
var _a;
|
|
566
|
+
if (this.apiMap) {
|
|
567
|
+
return this.apiMap;
|
|
568
|
+
}
|
|
569
|
+
const paths = this.spec.paths;
|
|
708
570
|
const result = {};
|
|
709
571
|
for (const path in paths) {
|
|
710
572
|
const methods = paths[path];
|
|
711
573
|
for (const method in methods) {
|
|
712
|
-
|
|
713
|
-
if (
|
|
714
|
-
const
|
|
715
|
-
result[`${method.toUpperCase()} ${path}`] =
|
|
574
|
+
const operationObject = methods[method];
|
|
575
|
+
if (((_a = this.options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && operationObject) {
|
|
576
|
+
const validateResult = this.validateAPI(method, path);
|
|
577
|
+
result[`${method.toUpperCase()} ${path}`] = {
|
|
578
|
+
operation: operationObject,
|
|
579
|
+
isValid: validateResult.isValid,
|
|
580
|
+
reason: validateResult.reason,
|
|
581
|
+
};
|
|
716
582
|
}
|
|
717
583
|
}
|
|
718
584
|
}
|
|
585
|
+
this.apiMap = result;
|
|
719
586
|
return result;
|
|
720
587
|
}
|
|
721
|
-
|
|
722
|
-
const
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
});
|
|
729
|
-
}
|
|
730
|
-
// Server validation
|
|
731
|
-
const serverErrors = Utils.validateServer(spec, options);
|
|
732
|
-
errors.push(...serverErrors);
|
|
733
|
-
// Remote reference not supported
|
|
734
|
-
const refPaths = parser.$refs.paths();
|
|
735
|
-
// refPaths [0] is the current spec file path
|
|
736
|
-
if (refPaths.length > 1) {
|
|
737
|
-
errors.push({
|
|
738
|
-
type: ErrorType.RemoteRefNotSupported,
|
|
739
|
-
content: Utils.format(ConstantString.RemoteRefNotSupported, refPaths.join(", ")),
|
|
740
|
-
data: refPaths,
|
|
588
|
+
validateSpecVersion() {
|
|
589
|
+
const result = { errors: [], warnings: [] };
|
|
590
|
+
if (this.spec.openapi >= "3.1.0") {
|
|
591
|
+
result.errors.push({
|
|
592
|
+
type: ErrorType.SpecVersionNotSupported,
|
|
593
|
+
content: Utils.format(ConstantString.SpecVersionNotSupported, this.spec.openapi),
|
|
594
|
+
data: this.spec.openapi,
|
|
741
595
|
});
|
|
742
596
|
}
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
597
|
+
return result;
|
|
598
|
+
}
|
|
599
|
+
validateSpecServer() {
|
|
600
|
+
const result = { errors: [], warnings: [] };
|
|
601
|
+
const serverErrors = Utils.validateServer(this.spec, this.options);
|
|
602
|
+
result.errors.push(...serverErrors);
|
|
603
|
+
return result;
|
|
604
|
+
}
|
|
605
|
+
validateSpecNoSupportAPI() {
|
|
606
|
+
const result = { errors: [], warnings: [] };
|
|
607
|
+
const apiMap = this.listAPIs();
|
|
608
|
+
const validAPIs = Object.entries(apiMap).filter(([, value]) => value.isValid);
|
|
609
|
+
if (validAPIs.length === 0) {
|
|
610
|
+
const data = [];
|
|
611
|
+
for (const key in apiMap) {
|
|
612
|
+
const { reason } = apiMap[key];
|
|
613
|
+
const apiInvalidReason = { api: key, reason: reason };
|
|
614
|
+
data.push(apiInvalidReason);
|
|
615
|
+
}
|
|
616
|
+
result.errors.push({
|
|
747
617
|
type: ErrorType.NoSupportedApi,
|
|
748
618
|
content: ConstantString.NoSupportedApi,
|
|
619
|
+
data,
|
|
749
620
|
});
|
|
750
621
|
}
|
|
622
|
+
return result;
|
|
623
|
+
}
|
|
624
|
+
validateSpecOperationId() {
|
|
625
|
+
const result = { errors: [], warnings: [] };
|
|
626
|
+
const apiMap = this.listAPIs();
|
|
751
627
|
// OperationId missing
|
|
752
628
|
const apisMissingOperationId = [];
|
|
753
629
|
for (const key in apiMap) {
|
|
754
|
-
const
|
|
755
|
-
if (!
|
|
630
|
+
const { operation } = apiMap[key];
|
|
631
|
+
if (!operation.operationId) {
|
|
756
632
|
apisMissingOperationId.push(key);
|
|
757
633
|
}
|
|
758
634
|
}
|
|
759
635
|
if (apisMissingOperationId.length > 0) {
|
|
760
|
-
warnings.push({
|
|
636
|
+
result.warnings.push({
|
|
761
637
|
type: WarningType.OperationIdMissing,
|
|
762
638
|
content: Utils.format(ConstantString.MissingOperationId, apisMissingOperationId.join(", ")),
|
|
763
639
|
data: apisMissingOperationId,
|
|
764
640
|
});
|
|
765
641
|
}
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
642
|
+
return result;
|
|
643
|
+
}
|
|
644
|
+
validateMethodAndPath(method, path) {
|
|
645
|
+
const result = { isValid: true, reason: [] };
|
|
646
|
+
if (this.options.allowMethods && !this.options.allowMethods.includes(method)) {
|
|
647
|
+
result.isValid = false;
|
|
648
|
+
result.reason.push(ErrorType.MethodNotAllowed);
|
|
649
|
+
return result;
|
|
650
|
+
}
|
|
651
|
+
const pathObj = this.spec.paths[path];
|
|
652
|
+
if (!pathObj || !pathObj[method]) {
|
|
653
|
+
result.isValid = false;
|
|
654
|
+
result.reason.push(ErrorType.UrlPathNotExist);
|
|
655
|
+
return result;
|
|
656
|
+
}
|
|
657
|
+
return result;
|
|
658
|
+
}
|
|
659
|
+
validateResponse(method, path) {
|
|
660
|
+
const result = { isValid: true, reason: [] };
|
|
661
|
+
const operationObject = this.spec.paths[path][method];
|
|
662
|
+
const { json, multipleMediaType } = Utils.getResponseJson(operationObject);
|
|
663
|
+
if (this.options.projectType === ProjectType.SME) {
|
|
664
|
+
// only support response body only contains “application/json” content type
|
|
665
|
+
if (multipleMediaType) {
|
|
666
|
+
result.reason.push(ErrorType.ResponseContainMultipleMediaTypes);
|
|
667
|
+
}
|
|
668
|
+
else if (Object.keys(json).length === 0) {
|
|
669
|
+
// response body should not be empty
|
|
670
|
+
result.reason.push(ErrorType.ResponseJsonIsEmpty);
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
return result;
|
|
674
|
+
}
|
|
675
|
+
validateServer(method, path) {
|
|
676
|
+
const result = { isValid: true, reason: [] };
|
|
677
|
+
const serverObj = Utils.getServerObject(this.spec, method, path);
|
|
678
|
+
if (!serverObj) {
|
|
679
|
+
// should contain server URL
|
|
680
|
+
result.reason.push(ErrorType.NoServerInformation);
|
|
769
681
|
}
|
|
770
|
-
else
|
|
771
|
-
|
|
682
|
+
else {
|
|
683
|
+
// server url should be absolute url with https protocol
|
|
684
|
+
const serverValidateResult = Utils.checkServerUrl([serverObj]);
|
|
685
|
+
result.reason.push(...serverValidateResult.map((item) => item.type));
|
|
772
686
|
}
|
|
773
|
-
return
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
687
|
+
return result;
|
|
688
|
+
}
|
|
689
|
+
validateAuth(method, path) {
|
|
690
|
+
const pathObj = this.spec.paths[path];
|
|
691
|
+
const operationObject = pathObj[method];
|
|
692
|
+
const securities = operationObject.security;
|
|
693
|
+
const authSchemeArray = Utils.getAuthArray(securities, this.spec);
|
|
694
|
+
if (authSchemeArray.length === 0) {
|
|
695
|
+
return { isValid: true, reason: [] };
|
|
696
|
+
}
|
|
697
|
+
if (this.options.allowAPIKeyAuth ||
|
|
698
|
+
this.options.allowOauth2 ||
|
|
699
|
+
this.options.allowBearerTokenAuth) {
|
|
700
|
+
// Currently we don't support multiple auth in one operation
|
|
701
|
+
if (authSchemeArray.length > 0 && authSchemeArray.every((auths) => auths.length > 1)) {
|
|
702
|
+
return {
|
|
703
|
+
isValid: false,
|
|
704
|
+
reason: [ErrorType.MultipleAuthNotSupported],
|
|
705
|
+
};
|
|
706
|
+
}
|
|
707
|
+
for (const auths of authSchemeArray) {
|
|
708
|
+
if (auths.length === 1) {
|
|
709
|
+
if ((this.options.allowAPIKeyAuth && Utils.isAPIKeyAuth(auths[0].authScheme)) ||
|
|
710
|
+
(this.options.allowOauth2 && Utils.isOAuthWithAuthCodeFlow(auths[0].authScheme)) ||
|
|
711
|
+
(this.options.allowBearerTokenAuth && Utils.isBearerTokenAuth(auths[0].authScheme))) {
|
|
712
|
+
return { isValid: true, reason: [] };
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
return { isValid: false, reason: [ErrorType.AuthTypeIsNotSupported] };
|
|
718
|
+
}
|
|
719
|
+
checkPostBodySchema(schema, isRequired = false) {
|
|
720
|
+
var _a;
|
|
721
|
+
const paramResult = {
|
|
722
|
+
requiredNum: 0,
|
|
723
|
+
optionalNum: 0,
|
|
724
|
+
isValid: true,
|
|
725
|
+
reason: [],
|
|
777
726
|
};
|
|
727
|
+
if (Object.keys(schema).length === 0) {
|
|
728
|
+
return paramResult;
|
|
729
|
+
}
|
|
730
|
+
const isRequiredWithoutDefault = isRequired && schema.default === undefined;
|
|
731
|
+
const isCopilot = this.projectType === ProjectType.Copilot;
|
|
732
|
+
if (isCopilot && this.hasNestedObjectInSchema(schema)) {
|
|
733
|
+
paramResult.isValid = false;
|
|
734
|
+
paramResult.reason = [ErrorType.RequestBodyContainsNestedObject];
|
|
735
|
+
return paramResult;
|
|
736
|
+
}
|
|
737
|
+
if (schema.type === "string" ||
|
|
738
|
+
schema.type === "integer" ||
|
|
739
|
+
schema.type === "boolean" ||
|
|
740
|
+
schema.type === "number") {
|
|
741
|
+
if (isRequiredWithoutDefault) {
|
|
742
|
+
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
743
|
+
}
|
|
744
|
+
else {
|
|
745
|
+
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
else if (schema.type === "object") {
|
|
749
|
+
const { properties } = schema;
|
|
750
|
+
for (const property in properties) {
|
|
751
|
+
let isRequired = false;
|
|
752
|
+
if (schema.required && ((_a = schema.required) === null || _a === void 0 ? void 0 : _a.indexOf(property)) >= 0) {
|
|
753
|
+
isRequired = true;
|
|
754
|
+
}
|
|
755
|
+
const result = this.checkPostBodySchema(properties[property], isRequired);
|
|
756
|
+
paramResult.requiredNum += result.requiredNum;
|
|
757
|
+
paramResult.optionalNum += result.optionalNum;
|
|
758
|
+
paramResult.isValid = paramResult.isValid && result.isValid;
|
|
759
|
+
paramResult.reason.push(...result.reason);
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
else {
|
|
763
|
+
if (isRequiredWithoutDefault && !isCopilot) {
|
|
764
|
+
paramResult.isValid = false;
|
|
765
|
+
paramResult.reason.push(ErrorType.PostBodyContainsRequiredUnsupportedSchema);
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
return paramResult;
|
|
778
769
|
}
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
770
|
+
checkParamSchema(paramObject) {
|
|
771
|
+
const paramResult = {
|
|
772
|
+
requiredNum: 0,
|
|
773
|
+
optionalNum: 0,
|
|
774
|
+
isValid: true,
|
|
775
|
+
reason: [],
|
|
776
|
+
};
|
|
777
|
+
if (!paramObject) {
|
|
778
|
+
return paramResult;
|
|
779
|
+
}
|
|
780
|
+
const isCopilot = this.projectType === ProjectType.Copilot;
|
|
781
|
+
for (let i = 0; i < paramObject.length; i++) {
|
|
782
|
+
const param = paramObject[i];
|
|
783
|
+
const schema = param.schema;
|
|
784
|
+
if (isCopilot && this.hasNestedObjectInSchema(schema)) {
|
|
785
|
+
paramResult.isValid = false;
|
|
786
|
+
paramResult.reason.push(ErrorType.ParamsContainsNestedObject);
|
|
787
|
+
continue;
|
|
788
|
+
}
|
|
789
|
+
const isRequiredWithoutDefault = param.required && schema.default === undefined;
|
|
790
|
+
if (isCopilot) {
|
|
791
|
+
if (isRequiredWithoutDefault) {
|
|
792
|
+
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
793
|
+
}
|
|
794
|
+
else {
|
|
795
|
+
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
796
|
+
}
|
|
797
|
+
continue;
|
|
798
|
+
}
|
|
799
|
+
if (param.in === "header" || param.in === "cookie") {
|
|
800
|
+
if (isRequiredWithoutDefault) {
|
|
801
|
+
paramResult.isValid = false;
|
|
802
|
+
paramResult.reason.push(ErrorType.ParamsContainRequiredUnsupportedSchema);
|
|
803
|
+
}
|
|
804
|
+
continue;
|
|
805
|
+
}
|
|
806
|
+
if (schema.type !== "boolean" &&
|
|
807
|
+
schema.type !== "string" &&
|
|
808
|
+
schema.type !== "number" &&
|
|
809
|
+
schema.type !== "integer") {
|
|
810
|
+
if (isRequiredWithoutDefault) {
|
|
811
|
+
paramResult.isValid = false;
|
|
812
|
+
paramResult.reason.push(ErrorType.ParamsContainRequiredUnsupportedSchema);
|
|
813
|
+
}
|
|
814
|
+
continue;
|
|
815
|
+
}
|
|
816
|
+
if (param.in === "query" || param.in === "path") {
|
|
817
|
+
if (isRequiredWithoutDefault) {
|
|
818
|
+
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
819
|
+
}
|
|
820
|
+
else {
|
|
821
|
+
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
return paramResult;
|
|
785
826
|
}
|
|
786
|
-
|
|
787
|
-
if (
|
|
788
|
-
|
|
827
|
+
hasNestedObjectInSchema(schema) {
|
|
828
|
+
if (schema.type === "object") {
|
|
829
|
+
for (const property in schema.properties) {
|
|
830
|
+
const nestedSchema = schema.properties[property];
|
|
831
|
+
if (nestedSchema.type === "object") {
|
|
832
|
+
return true;
|
|
833
|
+
}
|
|
834
|
+
}
|
|
789
835
|
}
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
836
|
+
return false;
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
// Copyright (c) Microsoft Corporation.
|
|
841
|
+
class CopilotValidator extends Validator {
|
|
842
|
+
constructor(spec, options) {
|
|
843
|
+
super();
|
|
844
|
+
this.projectType = ProjectType.Copilot;
|
|
845
|
+
this.options = options;
|
|
846
|
+
this.spec = spec;
|
|
847
|
+
}
|
|
848
|
+
validateSpec() {
|
|
849
|
+
const result = { errors: [], warnings: [] };
|
|
850
|
+
// validate spec version
|
|
851
|
+
let validationResult = this.validateSpecVersion();
|
|
852
|
+
result.errors.push(...validationResult.errors);
|
|
853
|
+
// validate spec server
|
|
854
|
+
validationResult = this.validateSpecServer();
|
|
855
|
+
result.errors.push(...validationResult.errors);
|
|
856
|
+
// validate no supported API
|
|
857
|
+
validationResult = this.validateSpecNoSupportAPI();
|
|
858
|
+
result.errors.push(...validationResult.errors);
|
|
859
|
+
// validate operationId missing
|
|
860
|
+
validationResult = this.validateSpecOperationId();
|
|
861
|
+
result.warnings.push(...validationResult.warnings);
|
|
862
|
+
return result;
|
|
863
|
+
}
|
|
864
|
+
validateAPI(method, path) {
|
|
865
|
+
const result = { isValid: true, reason: [] };
|
|
866
|
+
method = method.toLocaleLowerCase();
|
|
867
|
+
// validate method and path
|
|
868
|
+
const methodAndPathResult = this.validateMethodAndPath(method, path);
|
|
869
|
+
if (!methodAndPathResult.isValid) {
|
|
870
|
+
return methodAndPathResult;
|
|
871
|
+
}
|
|
872
|
+
const operationObject = this.spec.paths[path][method];
|
|
873
|
+
// validate auth
|
|
874
|
+
const authCheckResult = this.validateAuth(method, path);
|
|
875
|
+
result.reason.push(...authCheckResult.reason);
|
|
876
|
+
// validate operationId
|
|
877
|
+
if (!this.options.allowMissingId && !operationObject.operationId) {
|
|
878
|
+
result.reason.push(ErrorType.MissingOperationId);
|
|
879
|
+
}
|
|
880
|
+
// validate server
|
|
881
|
+
const validateServerResult = this.validateServer(method, path);
|
|
882
|
+
result.reason.push(...validateServerResult.reason);
|
|
883
|
+
// validate response
|
|
884
|
+
const validateResponseResult = this.validateResponse(method, path);
|
|
885
|
+
result.reason.push(...validateResponseResult.reason);
|
|
886
|
+
// validate requestBody
|
|
887
|
+
const requestBody = operationObject.requestBody;
|
|
888
|
+
const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
|
|
889
|
+
if (requestJsonBody) {
|
|
890
|
+
const requestBodySchema = requestJsonBody.schema;
|
|
891
|
+
if (requestBodySchema.type !== "object") {
|
|
892
|
+
result.reason.push(ErrorType.PostBodySchemaIsNotJson);
|
|
893
|
+
}
|
|
894
|
+
const requestBodyParamResult = this.checkPostBodySchema(requestBodySchema, requestBody.required);
|
|
895
|
+
result.reason.push(...requestBodyParamResult.reason);
|
|
896
|
+
}
|
|
897
|
+
// validate parameters
|
|
898
|
+
const paramObject = operationObject.parameters;
|
|
899
|
+
const paramResult = this.checkParamSchema(paramObject);
|
|
900
|
+
result.reason.push(...paramResult.reason);
|
|
901
|
+
if (result.reason.length > 0) {
|
|
902
|
+
result.isValid = false;
|
|
903
|
+
}
|
|
904
|
+
return result;
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
// Copyright (c) Microsoft Corporation.
|
|
909
|
+
class SMEValidator extends Validator {
|
|
910
|
+
constructor(spec, options) {
|
|
911
|
+
super();
|
|
912
|
+
this.projectType = ProjectType.SME;
|
|
913
|
+
this.options = options;
|
|
914
|
+
this.spec = spec;
|
|
915
|
+
}
|
|
916
|
+
validateSpec() {
|
|
917
|
+
const result = { errors: [], warnings: [] };
|
|
918
|
+
// validate spec version
|
|
919
|
+
let validationResult = this.validateSpecVersion();
|
|
920
|
+
result.errors.push(...validationResult.errors);
|
|
921
|
+
// validate spec server
|
|
922
|
+
validationResult = this.validateSpecServer();
|
|
923
|
+
result.errors.push(...validationResult.errors);
|
|
924
|
+
// validate no supported API
|
|
925
|
+
validationResult = this.validateSpecNoSupportAPI();
|
|
926
|
+
result.errors.push(...validationResult.errors);
|
|
927
|
+
// validate operationId missing
|
|
928
|
+
if (this.options.allowMissingId) {
|
|
929
|
+
validationResult = this.validateSpecOperationId();
|
|
930
|
+
result.warnings.push(...validationResult.warnings);
|
|
931
|
+
}
|
|
932
|
+
return result;
|
|
933
|
+
}
|
|
934
|
+
validateAPI(method, path) {
|
|
935
|
+
const result = { isValid: true, reason: [] };
|
|
936
|
+
method = method.toLocaleLowerCase();
|
|
937
|
+
// validate method and path
|
|
938
|
+
const methodAndPathResult = this.validateMethodAndPath(method, path);
|
|
939
|
+
if (!methodAndPathResult.isValid) {
|
|
940
|
+
return methodAndPathResult;
|
|
941
|
+
}
|
|
942
|
+
const operationObject = this.spec.paths[path][method];
|
|
943
|
+
// validate auth
|
|
944
|
+
const authCheckResult = this.validateAuth(method, path);
|
|
945
|
+
result.reason.push(...authCheckResult.reason);
|
|
946
|
+
// validate operationId
|
|
947
|
+
if (!this.options.allowMissingId && !operationObject.operationId) {
|
|
948
|
+
result.reason.push(ErrorType.MissingOperationId);
|
|
949
|
+
}
|
|
950
|
+
// validate server
|
|
951
|
+
const validateServerResult = this.validateServer(method, path);
|
|
952
|
+
result.reason.push(...validateServerResult.reason);
|
|
953
|
+
// validate response
|
|
954
|
+
const validateResponseResult = this.validateResponse(method, path);
|
|
955
|
+
result.reason.push(...validateResponseResult.reason);
|
|
956
|
+
let postBodyResult = {
|
|
957
|
+
requiredNum: 0,
|
|
958
|
+
optionalNum: 0,
|
|
959
|
+
isValid: true,
|
|
960
|
+
reason: [],
|
|
961
|
+
};
|
|
962
|
+
// validate requestBody
|
|
963
|
+
const requestBody = operationObject.requestBody;
|
|
964
|
+
const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
|
|
965
|
+
if (Utils.containMultipleMediaTypes(requestBody)) {
|
|
966
|
+
result.reason.push(ErrorType.PostBodyContainMultipleMediaTypes);
|
|
967
|
+
}
|
|
968
|
+
if (requestJsonBody) {
|
|
969
|
+
const requestBodySchema = requestJsonBody.schema;
|
|
970
|
+
postBodyResult = this.checkPostBodySchema(requestBodySchema, requestBody.required);
|
|
971
|
+
result.reason.push(...postBodyResult.reason);
|
|
972
|
+
}
|
|
973
|
+
// validate parameters
|
|
974
|
+
const paramObject = operationObject.parameters;
|
|
975
|
+
const paramResult = this.checkParamSchema(paramObject);
|
|
976
|
+
result.reason.push(...paramResult.reason);
|
|
977
|
+
// validate total parameters count
|
|
978
|
+
if (paramResult.isValid && postBodyResult.isValid) {
|
|
979
|
+
const paramCountResult = this.validateParamCount(postBodyResult, paramResult);
|
|
980
|
+
result.reason.push(...paramCountResult.reason);
|
|
981
|
+
}
|
|
982
|
+
if (result.reason.length > 0) {
|
|
983
|
+
result.isValid = false;
|
|
984
|
+
}
|
|
985
|
+
return result;
|
|
986
|
+
}
|
|
987
|
+
validateParamCount(postBodyResult, paramResult) {
|
|
988
|
+
const result = { isValid: true, reason: [] };
|
|
989
|
+
const totalRequiredParams = postBodyResult.requiredNum + paramResult.requiredNum;
|
|
990
|
+
const totalParams = totalRequiredParams + postBodyResult.optionalNum + paramResult.optionalNum;
|
|
991
|
+
if (totalRequiredParams > 1) {
|
|
992
|
+
if (!this.options.allowMultipleParameters ||
|
|
993
|
+
totalRequiredParams > SMEValidator.SMERequiredParamsMaxNum) {
|
|
994
|
+
result.reason.push(ErrorType.ExceededRequiredParamsLimit);
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
else if (totalParams === 0) {
|
|
998
|
+
result.reason.push(ErrorType.NoParameter);
|
|
999
|
+
}
|
|
1000
|
+
return result;
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
SMEValidator.SMERequiredParamsMaxNum = 5;
|
|
1004
|
+
|
|
1005
|
+
// Copyright (c) Microsoft Corporation.
|
|
1006
|
+
class TeamsAIValidator extends Validator {
|
|
1007
|
+
constructor(spec, options) {
|
|
1008
|
+
super();
|
|
1009
|
+
this.projectType = ProjectType.TeamsAi;
|
|
1010
|
+
this.options = options;
|
|
1011
|
+
this.spec = spec;
|
|
1012
|
+
}
|
|
1013
|
+
validateSpec() {
|
|
1014
|
+
const result = { errors: [], warnings: [] };
|
|
1015
|
+
// validate spec server
|
|
1016
|
+
let validationResult = this.validateSpecServer();
|
|
1017
|
+
result.errors.push(...validationResult.errors);
|
|
1018
|
+
// validate no supported API
|
|
1019
|
+
validationResult = this.validateSpecNoSupportAPI();
|
|
1020
|
+
result.errors.push(...validationResult.errors);
|
|
1021
|
+
return result;
|
|
1022
|
+
}
|
|
1023
|
+
validateAPI(method, path) {
|
|
1024
|
+
const result = { isValid: true, reason: [] };
|
|
1025
|
+
method = method.toLocaleLowerCase();
|
|
1026
|
+
// validate method and path
|
|
1027
|
+
const methodAndPathResult = this.validateMethodAndPath(method, path);
|
|
1028
|
+
if (!methodAndPathResult.isValid) {
|
|
1029
|
+
return methodAndPathResult;
|
|
1030
|
+
}
|
|
1031
|
+
const operationObject = this.spec.paths[path][method];
|
|
1032
|
+
// validate operationId
|
|
1033
|
+
if (!this.options.allowMissingId && !operationObject.operationId) {
|
|
1034
|
+
result.reason.push(ErrorType.MissingOperationId);
|
|
1035
|
+
}
|
|
1036
|
+
// validate server
|
|
1037
|
+
const validateServerResult = this.validateServer(method, path);
|
|
1038
|
+
result.reason.push(...validateServerResult.reason);
|
|
1039
|
+
if (result.reason.length > 0) {
|
|
1040
|
+
result.isValid = false;
|
|
1041
|
+
}
|
|
1042
|
+
return result;
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
class ValidatorFactory {
|
|
1047
|
+
static create(spec, options) {
|
|
1048
|
+
var _a;
|
|
1049
|
+
const type = (_a = options.projectType) !== null && _a !== void 0 ? _a : ProjectType.SME;
|
|
1050
|
+
switch (type) {
|
|
1051
|
+
case ProjectType.SME:
|
|
1052
|
+
return new SMEValidator(spec, options);
|
|
1053
|
+
case ProjectType.Copilot:
|
|
1054
|
+
return new CopilotValidator(spec, options);
|
|
1055
|
+
case ProjectType.TeamsAi:
|
|
1056
|
+
return new TeamsAIValidator(spec, options);
|
|
1057
|
+
default:
|
|
1058
|
+
throw new Error(`Invalid project type: ${type}`);
|
|
793
1059
|
}
|
|
794
|
-
return safeRegistrationIdEnvName;
|
|
795
1060
|
}
|
|
796
1061
|
}
|
|
797
1062
|
|
|
@@ -811,9 +1076,14 @@ class SpecParser {
|
|
|
811
1076
|
allowSwagger: false,
|
|
812
1077
|
allowAPIKeyAuth: false,
|
|
813
1078
|
allowMultipleParameters: false,
|
|
1079
|
+
allowBearerTokenAuth: false,
|
|
814
1080
|
allowOauth2: false,
|
|
815
1081
|
allowMethods: ["get", "post"],
|
|
1082
|
+
allowConversationStarters: false,
|
|
1083
|
+
allowResponseSemantics: false,
|
|
1084
|
+
allowConfirmation: false,
|
|
816
1085
|
projectType: ProjectType.SME,
|
|
1086
|
+
isGptPlugin: false,
|
|
817
1087
|
};
|
|
818
1088
|
this.pathOrSpec = pathOrDoc;
|
|
819
1089
|
this.parser = new SwaggerParser();
|
|
@@ -829,11 +1099,7 @@ class SpecParser {
|
|
|
829
1099
|
try {
|
|
830
1100
|
try {
|
|
831
1101
|
yield this.loadSpec();
|
|
832
|
-
yield this.parser.validate(this.spec
|
|
833
|
-
validate: {
|
|
834
|
-
schema: false,
|
|
835
|
-
},
|
|
836
|
-
});
|
|
1102
|
+
yield this.parser.validate(this.spec);
|
|
837
1103
|
}
|
|
838
1104
|
catch (e) {
|
|
839
1105
|
return {
|
|
@@ -842,16 +1108,46 @@ class SpecParser {
|
|
|
842
1108
|
errors: [{ type: ErrorType.SpecNotValid, content: e.toString() }],
|
|
843
1109
|
};
|
|
844
1110
|
}
|
|
1111
|
+
const errors = [];
|
|
1112
|
+
const warnings = [];
|
|
845
1113
|
if (!this.options.allowSwagger && this.isSwaggerFile) {
|
|
846
1114
|
return {
|
|
847
1115
|
status: ValidationStatus.Error,
|
|
848
1116
|
warnings: [],
|
|
849
1117
|
errors: [
|
|
850
|
-
{
|
|
1118
|
+
{
|
|
1119
|
+
type: ErrorType.SwaggerNotSupported,
|
|
1120
|
+
content: ConstantString.SwaggerNotSupported,
|
|
1121
|
+
},
|
|
851
1122
|
],
|
|
852
1123
|
};
|
|
853
1124
|
}
|
|
854
|
-
|
|
1125
|
+
// Remote reference not supported
|
|
1126
|
+
const refPaths = this.parser.$refs.paths();
|
|
1127
|
+
// refPaths [0] is the current spec file path
|
|
1128
|
+
if (refPaths.length > 1) {
|
|
1129
|
+
errors.push({
|
|
1130
|
+
type: ErrorType.RemoteRefNotSupported,
|
|
1131
|
+
content: Utils.format(ConstantString.RemoteRefNotSupported, refPaths.join(", ")),
|
|
1132
|
+
data: refPaths,
|
|
1133
|
+
});
|
|
1134
|
+
}
|
|
1135
|
+
const validator = this.getValidator(this.spec);
|
|
1136
|
+
const validationResult = validator.validateSpec();
|
|
1137
|
+
warnings.push(...validationResult.warnings);
|
|
1138
|
+
errors.push(...validationResult.errors);
|
|
1139
|
+
let status = ValidationStatus.Valid;
|
|
1140
|
+
if (warnings.length > 0 && errors.length === 0) {
|
|
1141
|
+
status = ValidationStatus.Warning;
|
|
1142
|
+
}
|
|
1143
|
+
else if (errors.length > 0) {
|
|
1144
|
+
status = ValidationStatus.Error;
|
|
1145
|
+
}
|
|
1146
|
+
return {
|
|
1147
|
+
status: status,
|
|
1148
|
+
warnings: warnings,
|
|
1149
|
+
errors: errors,
|
|
1150
|
+
};
|
|
855
1151
|
}
|
|
856
1152
|
catch (err) {
|
|
857
1153
|
throw new SpecParserError(err.toString(), ErrorType.ValidateFailed);
|
|
@@ -862,17 +1158,20 @@ class SpecParser {
|
|
|
862
1158
|
return __awaiter(this, void 0, void 0, function* () {
|
|
863
1159
|
try {
|
|
864
1160
|
yield this.loadSpec();
|
|
865
|
-
const apiMap = this.
|
|
1161
|
+
const apiMap = this.getAPIs(this.spec);
|
|
866
1162
|
const apiInfos = [];
|
|
867
1163
|
for (const key in apiMap) {
|
|
868
|
-
const
|
|
1164
|
+
const { operation, isValid } = apiMap[key];
|
|
1165
|
+
if (!isValid) {
|
|
1166
|
+
continue;
|
|
1167
|
+
}
|
|
869
1168
|
const [method, path] = key.split(" ");
|
|
870
|
-
const operationId =
|
|
1169
|
+
const operationId = operation.operationId;
|
|
871
1170
|
// In Browser environment, this api is by default not support api without operationId
|
|
872
1171
|
if (!operationId) {
|
|
873
1172
|
continue;
|
|
874
1173
|
}
|
|
875
|
-
const
|
|
1174
|
+
const command = Utils.parseApiInfo(operation, this.options);
|
|
876
1175
|
const apiInfo = {
|
|
877
1176
|
method: method,
|
|
878
1177
|
path: path,
|
|
@@ -881,9 +1180,6 @@ class SpecParser {
|
|
|
881
1180
|
parameters: command.parameters,
|
|
882
1181
|
description: command.description,
|
|
883
1182
|
};
|
|
884
|
-
if (warning) {
|
|
885
|
-
apiInfo.warning = warning;
|
|
886
|
-
}
|
|
887
1183
|
apiInfos.push(apiInfo);
|
|
888
1184
|
}
|
|
889
1185
|
return apiInfos;
|
|
@@ -953,13 +1249,18 @@ class SpecParser {
|
|
|
953
1249
|
}
|
|
954
1250
|
});
|
|
955
1251
|
}
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
1252
|
+
getAPIs(spec) {
|
|
1253
|
+
const validator = this.getValidator(spec);
|
|
1254
|
+
const apiMap = validator.listAPIs();
|
|
1255
|
+
return apiMap;
|
|
1256
|
+
}
|
|
1257
|
+
getValidator(spec) {
|
|
1258
|
+
if (this.validator) {
|
|
1259
|
+
return this.validator;
|
|
959
1260
|
}
|
|
960
|
-
const
|
|
961
|
-
this.
|
|
962
|
-
return
|
|
1261
|
+
const validator = ValidatorFactory.create(spec, this.options);
|
|
1262
|
+
this.validator = validator;
|
|
1263
|
+
return validator;
|
|
963
1264
|
}
|
|
964
1265
|
}
|
|
965
1266
|
|
|
@@ -967,7 +1268,7 @@ class SpecParser {
|
|
|
967
1268
|
class AdaptiveCardGenerator {
|
|
968
1269
|
static generateAdaptiveCard(operationItem) {
|
|
969
1270
|
try {
|
|
970
|
-
const json = Utils.getResponseJson(operationItem);
|
|
1271
|
+
const { json } = Utils.getResponseJson(operationItem);
|
|
971
1272
|
let cardBody = [];
|
|
972
1273
|
let schema = json.schema;
|
|
973
1274
|
let jsonPath = "$";
|