@microsoft/m365-spec-parser 0.1.1-alpha.48b9eab36.0 → 0.1.1-alpha.4e708f092.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 +637 -302
- package/dist/index.esm2017.js.map +1 -1
- package/dist/index.esm2017.mjs +1096 -479
- package/dist/index.esm2017.mjs.map +1 -1
- package/dist/index.esm5.js +639 -302
- package/dist/index.esm5.js.map +1 -1
- package/dist/index.node.cjs.js +1105 -482
- package/dist/index.node.cjs.js.map +1 -1
- package/dist/src/adaptiveCardWrapper.d.ts +2 -0
- package/dist/src/constants.d.ts +7 -1
- package/dist/src/index.browser.d.ts +1 -1
- package/dist/src/index.d.ts +1 -1
- package/dist/src/interfaces.d.ts +102 -18
- package/dist/src/manifestUpdater.d.ts +9 -4
- package/dist/src/specFilter.d.ts +2 -1
- package/dist/src/specParser.browser.d.ts +12 -3
- package/dist/src/specParser.d.ts +13 -5
- package/dist/src/utils.d.ts +18 -34
- package/package.json +4 -4
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 = {}));
|
|
@@ -76,7 +92,13 @@ var ValidationStatus;
|
|
|
76
92
|
ValidationStatus[ValidationStatus["Valid"] = 0] = "Valid";
|
|
77
93
|
ValidationStatus[ValidationStatus["Warning"] = 1] = "Warning";
|
|
78
94
|
ValidationStatus[ValidationStatus["Error"] = 2] = "Error";
|
|
79
|
-
})(ValidationStatus || (ValidationStatus = {}));
|
|
95
|
+
})(ValidationStatus || (ValidationStatus = {}));
|
|
96
|
+
var ProjectType;
|
|
97
|
+
(function (ProjectType) {
|
|
98
|
+
ProjectType[ProjectType["Copilot"] = 0] = "Copilot";
|
|
99
|
+
ProjectType[ProjectType["SME"] = 1] = "SME";
|
|
100
|
+
ProjectType[ProjectType["TeamsAi"] = 2] = "TeamsAi";
|
|
101
|
+
})(ProjectType || (ProjectType = {}));
|
|
80
102
|
|
|
81
103
|
// Copyright (c) Microsoft Corporation.
|
|
82
104
|
class SpecParserError extends Error {
|
|
@@ -103,7 +125,9 @@ ConstantString.ResolveServerUrlFailed = "Unable to resolve the server URL: pleas
|
|
|
103
125
|
ConstantString.OperationOnlyContainsOptionalParam = "Operation %s contains multiple optional parameters. The first optional parameter is used for this command.";
|
|
104
126
|
ConstantString.ConvertSwaggerToOpenAPI = "The Swagger 2.0 file has been converted to OpenAPI 3.0.";
|
|
105
127
|
ConstantString.SwaggerNotSupported = "Swagger 2.0 is not supported. Please convert to OpenAPI 3.0 manually before proceeding.";
|
|
106
|
-
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.";
|
|
130
|
+
ConstantString.UnsupportedSchema = "Unsupported schema in %s %s: %s";
|
|
107
131
|
ConstantString.WrappedCardVersion = "devPreview";
|
|
108
132
|
ConstantString.WrappedCardSchema = "https://developer.microsoft.com/json-schemas/teams/vDevPreview/MicrosoftTeams.ResponseRenderingTemplate.schema.json";
|
|
109
133
|
ConstantString.WrappedCardResponseLayout = "list";
|
|
@@ -113,8 +137,10 @@ ConstantString.AdaptiveCardVersion = "1.5";
|
|
|
113
137
|
ConstantString.AdaptiveCardSchema = "http://adaptivecards.io/schemas/adaptive-card.json";
|
|
114
138
|
ConstantString.AdaptiveCardType = "AdaptiveCard";
|
|
115
139
|
ConstantString.TextBlockType = "TextBlock";
|
|
140
|
+
ConstantString.ImageType = "Image";
|
|
116
141
|
ConstantString.ContainerType = "Container";
|
|
117
142
|
ConstantString.RegistrationIdPostfix = "REGISTRATION_ID";
|
|
143
|
+
ConstantString.OAuthRegistrationIdPostFix = "OAUTH_REGISTRATION_ID";
|
|
118
144
|
ConstantString.ResponseCodeFor20X = [
|
|
119
145
|
"200",
|
|
120
146
|
"201",
|
|
@@ -174,205 +200,36 @@ ConstantString.FullDescriptionMaxLens = 4000;
|
|
|
174
200
|
ConstantString.CommandDescriptionMaxLens = 128;
|
|
175
201
|
ConstantString.ParameterDescriptionMaxLens = 128;
|
|
176
202
|
ConstantString.CommandTitleMaxLens = 32;
|
|
177
|
-
ConstantString.ParameterTitleMaxLens = 32;
|
|
203
|
+
ConstantString.ParameterTitleMaxLens = 32;
|
|
204
|
+
ConstantString.SMERequiredParamsMaxNum = 5;
|
|
205
|
+
ConstantString.DefaultPluginId = "plugin_1";
|
|
178
206
|
|
|
179
207
|
// Copyright (c) Microsoft Corporation.
|
|
180
208
|
class Utils {
|
|
181
|
-
static
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
};
|
|
187
|
-
if (!paramObject) {
|
|
188
|
-
return paramResult;
|
|
189
|
-
}
|
|
190
|
-
for (let i = 0; i < paramObject.length; i++) {
|
|
191
|
-
const param = paramObject[i];
|
|
192
|
-
const schema = param.schema;
|
|
193
|
-
const isRequiredWithoutDefault = param.required && schema.default === undefined;
|
|
194
|
-
if (param.in === "header" || param.in === "cookie") {
|
|
195
|
-
if (isRequiredWithoutDefault) {
|
|
196
|
-
paramResult.isValid = false;
|
|
197
|
-
}
|
|
198
|
-
continue;
|
|
199
|
-
}
|
|
200
|
-
if (schema.type !== "boolean" &&
|
|
201
|
-
schema.type !== "string" &&
|
|
202
|
-
schema.type !== "number" &&
|
|
203
|
-
schema.type !== "integer") {
|
|
204
|
-
if (isRequiredWithoutDefault) {
|
|
205
|
-
paramResult.isValid = false;
|
|
206
|
-
}
|
|
207
|
-
continue;
|
|
208
|
-
}
|
|
209
|
-
if (param.in === "query" || param.in === "path") {
|
|
210
|
-
if (isRequiredWithoutDefault) {
|
|
211
|
-
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
212
|
-
}
|
|
213
|
-
else {
|
|
214
|
-
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
return paramResult;
|
|
219
|
-
}
|
|
220
|
-
static checkPostBody(schema, isRequired = false) {
|
|
221
|
-
var _a;
|
|
222
|
-
const paramResult = {
|
|
223
|
-
requiredNum: 0,
|
|
224
|
-
optionalNum: 0,
|
|
225
|
-
isValid: true,
|
|
226
|
-
};
|
|
227
|
-
if (Object.keys(schema).length === 0) {
|
|
228
|
-
return paramResult;
|
|
229
|
-
}
|
|
230
|
-
const isRequiredWithoutDefault = isRequired && schema.default === undefined;
|
|
231
|
-
if (schema.type === "string" ||
|
|
232
|
-
schema.type === "integer" ||
|
|
233
|
-
schema.type === "boolean" ||
|
|
234
|
-
schema.type === "number") {
|
|
235
|
-
if (isRequiredWithoutDefault) {
|
|
236
|
-
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
237
|
-
}
|
|
238
|
-
else {
|
|
239
|
-
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
else if (schema.type === "object") {
|
|
243
|
-
const { properties } = schema;
|
|
244
|
-
for (const property in properties) {
|
|
245
|
-
let isRequired = false;
|
|
246
|
-
if (schema.required && ((_a = schema.required) === null || _a === void 0 ? void 0 : _a.indexOf(property)) >= 0) {
|
|
247
|
-
isRequired = true;
|
|
248
|
-
}
|
|
249
|
-
const result = Utils.checkPostBody(properties[property], isRequired);
|
|
250
|
-
paramResult.requiredNum += result.requiredNum;
|
|
251
|
-
paramResult.optionalNum += result.optionalNum;
|
|
252
|
-
paramResult.isValid = paramResult.isValid && result.isValid;
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
else {
|
|
256
|
-
if (isRequiredWithoutDefault) {
|
|
257
|
-
paramResult.isValid = false;
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
return paramResult;
|
|
261
|
-
}
|
|
262
|
-
/**
|
|
263
|
-
* Checks if the given API is supported.
|
|
264
|
-
* @param {string} method - The HTTP method of the API.
|
|
265
|
-
* @param {string} path - The path of the API.
|
|
266
|
-
* @param {OpenAPIV3.Document} spec - The OpenAPI specification document.
|
|
267
|
-
* @returns {boolean} - Returns true if the API is supported, false otherwise.
|
|
268
|
-
* @description The following APIs are supported:
|
|
269
|
-
* 1. only support Get/Post operation without auth property
|
|
270
|
-
* 2. parameter inside query or path only support string, number, boolean and integer
|
|
271
|
-
* 3. parameter inside post body only support string, number, boolean, integer and object
|
|
272
|
-
* 4. request body + required parameters <= 1
|
|
273
|
-
* 5. response body should be “application/json” and not empty, and response code should be 20X
|
|
274
|
-
* 6. only support request body with “application/json” content type
|
|
275
|
-
*/
|
|
276
|
-
static isSupportedApi(method, path, spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2) {
|
|
277
|
-
const pathObj = spec.paths[path];
|
|
278
|
-
method = method.toLocaleLowerCase();
|
|
279
|
-
if (pathObj) {
|
|
280
|
-
if ((method === ConstantString.PostMethod || method === ConstantString.GetMethod) &&
|
|
281
|
-
pathObj[method]) {
|
|
282
|
-
const securities = pathObj[method].security;
|
|
283
|
-
const authArray = Utils.getAuthArray(securities, spec);
|
|
284
|
-
if (!Utils.isSupportedAuth(authArray, allowAPIKeyAuth, allowOauth2)) {
|
|
285
|
-
return false;
|
|
286
|
-
}
|
|
287
|
-
const operationObject = pathObj[method];
|
|
288
|
-
if (!allowMissingId && !operationObject.operationId) {
|
|
289
|
-
return false;
|
|
290
|
-
}
|
|
291
|
-
const paramObject = operationObject.parameters;
|
|
292
|
-
const requestBody = operationObject.requestBody;
|
|
293
|
-
const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
|
|
294
|
-
const mediaTypesCount = Object.keys((requestBody === null || requestBody === void 0 ? void 0 : requestBody.content) || {}).length;
|
|
295
|
-
if (mediaTypesCount > 1) {
|
|
296
|
-
return false;
|
|
297
|
-
}
|
|
298
|
-
const responseJson = Utils.getResponseJson(operationObject);
|
|
299
|
-
if (Object.keys(responseJson).length === 0) {
|
|
300
|
-
return false;
|
|
301
|
-
}
|
|
302
|
-
let requestBodyParamResult = {
|
|
303
|
-
requiredNum: 0,
|
|
304
|
-
optionalNum: 0,
|
|
305
|
-
isValid: true,
|
|
306
|
-
};
|
|
307
|
-
if (requestJsonBody) {
|
|
308
|
-
const requestBodySchema = requestJsonBody.schema;
|
|
309
|
-
requestBodyParamResult = Utils.checkPostBody(requestBodySchema, requestBody.required);
|
|
310
|
-
}
|
|
311
|
-
if (!requestBodyParamResult.isValid) {
|
|
312
|
-
return false;
|
|
313
|
-
}
|
|
314
|
-
const paramResult = Utils.checkParameters(paramObject);
|
|
315
|
-
if (!paramResult.isValid) {
|
|
316
|
-
return false;
|
|
317
|
-
}
|
|
318
|
-
if (requestBodyParamResult.requiredNum + paramResult.requiredNum > 1) {
|
|
319
|
-
if (allowMultipleParameters &&
|
|
320
|
-
requestBodyParamResult.requiredNum + paramResult.requiredNum <= 5) {
|
|
321
|
-
return true;
|
|
322
|
-
}
|
|
323
|
-
return false;
|
|
324
|
-
}
|
|
325
|
-
else if (requestBodyParamResult.requiredNum +
|
|
326
|
-
requestBodyParamResult.optionalNum +
|
|
327
|
-
paramResult.requiredNum +
|
|
328
|
-
paramResult.optionalNum ===
|
|
329
|
-
0) {
|
|
330
|
-
return false;
|
|
331
|
-
}
|
|
332
|
-
else {
|
|
209
|
+
static hasNestedObjectInSchema(schema) {
|
|
210
|
+
if (schema.type === "object") {
|
|
211
|
+
for (const property in schema.properties) {
|
|
212
|
+
const nestedSchema = schema.properties[property];
|
|
213
|
+
if (nestedSchema.type === "object") {
|
|
333
214
|
return true;
|
|
334
215
|
}
|
|
335
216
|
}
|
|
336
217
|
}
|
|
337
218
|
return false;
|
|
338
219
|
}
|
|
339
|
-
static
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
// Currently we don't support multiple auth in one operation
|
|
345
|
-
if (authSchemaArray.length > 0 && authSchemaArray.every((auths) => auths.length > 1)) {
|
|
346
|
-
return false;
|
|
347
|
-
}
|
|
348
|
-
for (const auths of authSchemaArray) {
|
|
349
|
-
if (auths.length === 1) {
|
|
350
|
-
if (!allowOauth2 && allowAPIKeyAuth && Utils.isAPIKeyAuth(auths[0].authSchema)) {
|
|
351
|
-
return true;
|
|
352
|
-
}
|
|
353
|
-
else if (!allowAPIKeyAuth &&
|
|
354
|
-
allowOauth2 &&
|
|
355
|
-
Utils.isBearerTokenAuth(auths[0].authSchema)) {
|
|
356
|
-
return true;
|
|
357
|
-
}
|
|
358
|
-
else if (allowAPIKeyAuth &&
|
|
359
|
-
allowOauth2 &&
|
|
360
|
-
(Utils.isAPIKeyAuth(auths[0].authSchema) ||
|
|
361
|
-
Utils.isBearerTokenAuth(auths[0].authSchema))) {
|
|
362
|
-
return true;
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
return false;
|
|
220
|
+
static containMultipleMediaTypes(bodyObject) {
|
|
221
|
+
return Object.keys((bodyObject === null || bodyObject === void 0 ? void 0 : bodyObject.content) || {}).length > 1;
|
|
222
|
+
}
|
|
223
|
+
static isBearerTokenAuth(authScheme) {
|
|
224
|
+
return authScheme.type === "http" && authScheme.scheme === "bearer";
|
|
368
225
|
}
|
|
369
|
-
static isAPIKeyAuth(
|
|
370
|
-
return
|
|
226
|
+
static isAPIKeyAuth(authScheme) {
|
|
227
|
+
return authScheme.type === "apiKey";
|
|
371
228
|
}
|
|
372
|
-
static
|
|
373
|
-
return (
|
|
374
|
-
|
|
375
|
-
|
|
229
|
+
static isOAuthWithAuthCodeFlow(authScheme) {
|
|
230
|
+
return !!(authScheme.type === "oauth2" &&
|
|
231
|
+
authScheme.flows &&
|
|
232
|
+
authScheme.flows.authorizationCode);
|
|
376
233
|
}
|
|
377
234
|
static getAuthArray(securities, spec) {
|
|
378
235
|
var _a;
|
|
@@ -385,7 +242,7 @@ class Utils {
|
|
|
385
242
|
for (const name in security) {
|
|
386
243
|
const auth = securitySchemas[name];
|
|
387
244
|
authArray.push({
|
|
388
|
-
|
|
245
|
+
authScheme: auth,
|
|
389
246
|
name: name,
|
|
390
247
|
});
|
|
391
248
|
}
|
|
@@ -403,18 +260,22 @@ class Utils {
|
|
|
403
260
|
static getResponseJson(operationObject) {
|
|
404
261
|
var _a, _b;
|
|
405
262
|
let json = {};
|
|
263
|
+
let multipleMediaType = false;
|
|
406
264
|
for (const code of ConstantString.ResponseCodeFor20X) {
|
|
407
265
|
const responseObject = (_a = operationObject === null || operationObject === void 0 ? void 0 : operationObject.responses) === null || _a === void 0 ? void 0 : _a[code];
|
|
408
|
-
const mediaTypesCount = Object.keys((responseObject === null || responseObject === void 0 ? void 0 : responseObject.content) || {}).length;
|
|
409
|
-
if (mediaTypesCount > 1) {
|
|
410
|
-
return {};
|
|
411
|
-
}
|
|
412
266
|
if ((_b = responseObject === null || responseObject === void 0 ? void 0 : responseObject.content) === null || _b === void 0 ? void 0 : _b["application/json"]) {
|
|
267
|
+
multipleMediaType = false;
|
|
413
268
|
json = responseObject.content["application/json"];
|
|
414
|
-
|
|
269
|
+
if (Utils.containMultipleMediaTypes(responseObject)) {
|
|
270
|
+
multipleMediaType = true;
|
|
271
|
+
json = {};
|
|
272
|
+
}
|
|
273
|
+
else {
|
|
274
|
+
break;
|
|
275
|
+
}
|
|
415
276
|
}
|
|
416
277
|
}
|
|
417
|
-
return json;
|
|
278
|
+
return { json, multipleMediaType };
|
|
418
279
|
}
|
|
419
280
|
static convertPathToCamelCase(path) {
|
|
420
281
|
const pathSegments = path.split(/[./{]/);
|
|
@@ -434,10 +295,10 @@ class Utils {
|
|
|
434
295
|
return undefined;
|
|
435
296
|
}
|
|
436
297
|
}
|
|
437
|
-
static
|
|
298
|
+
static resolveEnv(str) {
|
|
438
299
|
const placeHolderReg = /\${{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*}}/g;
|
|
439
|
-
let matches = placeHolderReg.exec(
|
|
440
|
-
let
|
|
300
|
+
let matches = placeHolderReg.exec(str);
|
|
301
|
+
let newStr = str;
|
|
441
302
|
while (matches != null) {
|
|
442
303
|
const envVar = matches[1];
|
|
443
304
|
const envVal = process.env[envVar];
|
|
@@ -445,17 +306,17 @@ class Utils {
|
|
|
445
306
|
throw new Error(Utils.format(ConstantString.ResolveServerUrlFailed, envVar));
|
|
446
307
|
}
|
|
447
308
|
else {
|
|
448
|
-
|
|
309
|
+
newStr = newStr.replace(matches[0], envVal);
|
|
449
310
|
}
|
|
450
|
-
matches = placeHolderReg.exec(
|
|
311
|
+
matches = placeHolderReg.exec(str);
|
|
451
312
|
}
|
|
452
|
-
return
|
|
313
|
+
return newStr;
|
|
453
314
|
}
|
|
454
315
|
static checkServerUrl(servers) {
|
|
455
316
|
const errors = [];
|
|
456
317
|
let serverUrl;
|
|
457
318
|
try {
|
|
458
|
-
serverUrl = Utils.
|
|
319
|
+
serverUrl = Utils.resolveEnv(servers[0].url);
|
|
459
320
|
}
|
|
460
321
|
catch (err) {
|
|
461
322
|
errors.push({
|
|
@@ -485,7 +346,8 @@ class Utils {
|
|
|
485
346
|
}
|
|
486
347
|
return errors;
|
|
487
348
|
}
|
|
488
|
-
static validateServer(spec,
|
|
349
|
+
static validateServer(spec, options) {
|
|
350
|
+
var _a;
|
|
489
351
|
const errors = [];
|
|
490
352
|
let hasTopLevelServers = false;
|
|
491
353
|
let hasPathLevelServers = false;
|
|
@@ -506,7 +368,7 @@ class Utils {
|
|
|
506
368
|
}
|
|
507
369
|
for (const method in methods) {
|
|
508
370
|
const operationObject = methods[method];
|
|
509
|
-
if (
|
|
371
|
+
if (((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && operationObject) {
|
|
510
372
|
if ((operationObject === null || operationObject === void 0 ? void 0 : operationObject.servers) && operationObject.servers.length >= 1) {
|
|
511
373
|
hasOperationLevelServers = true;
|
|
512
374
|
const serverErrors = Utils.checkServerUrl(operationObject.servers);
|
|
@@ -549,6 +411,7 @@ class Utils {
|
|
|
549
411
|
Utils.updateParameterWithInputType(schema, parameter);
|
|
550
412
|
}
|
|
551
413
|
if (isRequired && schema.default === undefined) {
|
|
414
|
+
parameter.isRequired = true;
|
|
552
415
|
requiredParams.push(parameter);
|
|
553
416
|
}
|
|
554
417
|
else {
|
|
@@ -593,7 +456,7 @@ class Utils {
|
|
|
593
456
|
param.value = schema.default;
|
|
594
457
|
}
|
|
595
458
|
}
|
|
596
|
-
static parseApiInfo(operationItem,
|
|
459
|
+
static parseApiInfo(operationItem, options) {
|
|
597
460
|
var _a, _b;
|
|
598
461
|
const requiredParams = [];
|
|
599
462
|
const optionalParams = [];
|
|
@@ -607,11 +470,12 @@ class Utils {
|
|
|
607
470
|
description: ((_a = param.description) !== null && _a !== void 0 ? _a : "").slice(0, ConstantString.ParameterDescriptionMaxLens),
|
|
608
471
|
};
|
|
609
472
|
const schema = param.schema;
|
|
610
|
-
if (allowMultipleParameters && schema) {
|
|
473
|
+
if (options.allowMultipleParameters && schema) {
|
|
611
474
|
Utils.updateParameterWithInputType(schema, parameter);
|
|
612
475
|
}
|
|
613
476
|
if (param.in !== "header" && param.in !== "cookie") {
|
|
614
477
|
if (param.required && (schema === null || schema === void 0 ? void 0 : schema.default) === undefined) {
|
|
478
|
+
parameter.isRequired = true;
|
|
615
479
|
requiredParams.push(parameter);
|
|
616
480
|
}
|
|
617
481
|
else {
|
|
@@ -625,19 +489,13 @@ class Utils {
|
|
|
625
489
|
const requestJson = requestBody.content["application/json"];
|
|
626
490
|
if (Object.keys(requestJson).length !== 0) {
|
|
627
491
|
const schema = requestJson.schema;
|
|
628
|
-
const [requiredP, optionalP] = Utils.generateParametersFromSchema(schema, "requestBody", allowMultipleParameters, requestBody.required);
|
|
492
|
+
const [requiredP, optionalP] = Utils.generateParametersFromSchema(schema, "requestBody", !!options.allowMultipleParameters, requestBody.required);
|
|
629
493
|
requiredParams.push(...requiredP);
|
|
630
494
|
optionalParams.push(...optionalP);
|
|
631
495
|
}
|
|
632
496
|
}
|
|
633
497
|
const operationId = operationItem.operationId;
|
|
634
|
-
const parameters = [];
|
|
635
|
-
if (requiredParams.length !== 0) {
|
|
636
|
-
parameters.push(...requiredParams);
|
|
637
|
-
}
|
|
638
|
-
else {
|
|
639
|
-
parameters.push(optionalParams[0]);
|
|
640
|
-
}
|
|
498
|
+
const parameters = [...requiredParams, ...optionalParams];
|
|
641
499
|
const command = {
|
|
642
500
|
context: ["compose"],
|
|
643
501
|
type: "query",
|
|
@@ -646,105 +504,535 @@ class Utils {
|
|
|
646
504
|
parameters: parameters,
|
|
647
505
|
description: ((_b = operationItem.description) !== null && _b !== void 0 ? _b : "").slice(0, ConstantString.CommandDescriptionMaxLens),
|
|
648
506
|
};
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
507
|
+
return command;
|
|
508
|
+
}
|
|
509
|
+
static format(str, ...args) {
|
|
510
|
+
let index = 0;
|
|
511
|
+
return str.replace(/%s/g, () => {
|
|
512
|
+
const arg = args[index++];
|
|
513
|
+
return arg !== undefined ? arg : "";
|
|
514
|
+
});
|
|
515
|
+
}
|
|
516
|
+
static getSafeRegistrationIdEnvName(authName) {
|
|
517
|
+
if (!authName) {
|
|
518
|
+
return "";
|
|
656
519
|
}
|
|
657
|
-
|
|
520
|
+
let safeRegistrationIdEnvName = authName.toUpperCase().replace(/[^A-Z0-9_]/g, "_");
|
|
521
|
+
if (!safeRegistrationIdEnvName.match(/^[A-Z]/)) {
|
|
522
|
+
safeRegistrationIdEnvName = "PREFIX_" + safeRegistrationIdEnvName;
|
|
523
|
+
}
|
|
524
|
+
return safeRegistrationIdEnvName;
|
|
658
525
|
}
|
|
659
|
-
static
|
|
660
|
-
const
|
|
526
|
+
static getServerObject(spec, method, path) {
|
|
527
|
+
const pathObj = spec.paths[path];
|
|
528
|
+
const operationObject = pathObj[method];
|
|
529
|
+
const rootServer = spec.servers && spec.servers[0];
|
|
530
|
+
const methodServer = spec.paths[path].servers && spec.paths[path].servers[0];
|
|
531
|
+
const operationServer = operationObject.servers && operationObject.servers[0];
|
|
532
|
+
const serverUrl = operationServer || methodServer || rootServer;
|
|
533
|
+
return serverUrl;
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
// Copyright (c) Microsoft Corporation.
|
|
538
|
+
class Validator {
|
|
539
|
+
listAPIs() {
|
|
540
|
+
var _a;
|
|
541
|
+
if (this.apiMap) {
|
|
542
|
+
return this.apiMap;
|
|
543
|
+
}
|
|
544
|
+
const paths = this.spec.paths;
|
|
661
545
|
const result = {};
|
|
662
546
|
for (const path in paths) {
|
|
663
547
|
const methods = paths[path];
|
|
664
548
|
for (const method in methods) {
|
|
665
|
-
|
|
666
|
-
if (
|
|
667
|
-
const
|
|
668
|
-
result[`${method.toUpperCase()} ${path}`] =
|
|
549
|
+
const operationObject = methods[method];
|
|
550
|
+
if (((_a = this.options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && operationObject) {
|
|
551
|
+
const validateResult = this.validateAPI(method, path);
|
|
552
|
+
result[`${method.toUpperCase()} ${path}`] = {
|
|
553
|
+
operation: operationObject,
|
|
554
|
+
isValid: validateResult.isValid,
|
|
555
|
+
reason: validateResult.reason,
|
|
556
|
+
};
|
|
669
557
|
}
|
|
670
558
|
}
|
|
671
559
|
}
|
|
560
|
+
this.apiMap = result;
|
|
672
561
|
return result;
|
|
673
562
|
}
|
|
674
|
-
|
|
675
|
-
const
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
563
|
+
validateSpecVersion() {
|
|
564
|
+
const result = { errors: [], warnings: [] };
|
|
565
|
+
if (this.spec.openapi >= "3.1.0") {
|
|
566
|
+
result.errors.push({
|
|
567
|
+
type: ErrorType.SpecVersionNotSupported,
|
|
568
|
+
content: Utils.format(ConstantString.SpecVersionNotSupported, this.spec.openapi),
|
|
569
|
+
data: this.spec.openapi,
|
|
681
570
|
});
|
|
682
571
|
}
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
const
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
572
|
+
return result;
|
|
573
|
+
}
|
|
574
|
+
validateSpecServer() {
|
|
575
|
+
const result = { errors: [], warnings: [] };
|
|
576
|
+
const serverErrors = Utils.validateServer(this.spec, this.options);
|
|
577
|
+
result.errors.push(...serverErrors);
|
|
578
|
+
return result;
|
|
579
|
+
}
|
|
580
|
+
validateSpecNoSupportAPI() {
|
|
581
|
+
const result = { errors: [], warnings: [] };
|
|
582
|
+
const apiMap = this.listAPIs();
|
|
583
|
+
const validAPIs = Object.entries(apiMap).filter(([, value]) => value.isValid);
|
|
584
|
+
if (validAPIs.length === 0) {
|
|
585
|
+
const data = [];
|
|
586
|
+
for (const key in apiMap) {
|
|
587
|
+
const { reason } = apiMap[key];
|
|
588
|
+
const apiInvalidReason = { api: key, reason: reason };
|
|
589
|
+
data.push(apiInvalidReason);
|
|
590
|
+
}
|
|
591
|
+
result.errors.push({
|
|
700
592
|
type: ErrorType.NoSupportedApi,
|
|
701
593
|
content: ConstantString.NoSupportedApi,
|
|
594
|
+
data,
|
|
702
595
|
});
|
|
703
596
|
}
|
|
597
|
+
return result;
|
|
598
|
+
}
|
|
599
|
+
validateSpecOperationId() {
|
|
600
|
+
const result = { errors: [], warnings: [] };
|
|
601
|
+
const apiMap = this.listAPIs();
|
|
704
602
|
// OperationId missing
|
|
705
603
|
const apisMissingOperationId = [];
|
|
706
604
|
for (const key in apiMap) {
|
|
707
|
-
const
|
|
708
|
-
if (!
|
|
605
|
+
const { operation } = apiMap[key];
|
|
606
|
+
if (!operation.operationId) {
|
|
709
607
|
apisMissingOperationId.push(key);
|
|
710
608
|
}
|
|
711
609
|
}
|
|
712
610
|
if (apisMissingOperationId.length > 0) {
|
|
713
|
-
warnings.push({
|
|
611
|
+
result.warnings.push({
|
|
714
612
|
type: WarningType.OperationIdMissing,
|
|
715
613
|
content: Utils.format(ConstantString.MissingOperationId, apisMissingOperationId.join(", ")),
|
|
716
614
|
data: apisMissingOperationId,
|
|
717
615
|
});
|
|
718
616
|
}
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
617
|
+
return result;
|
|
618
|
+
}
|
|
619
|
+
validateMethodAndPath(method, path) {
|
|
620
|
+
const result = { isValid: true, reason: [] };
|
|
621
|
+
if (this.options.allowMethods && !this.options.allowMethods.includes(method)) {
|
|
622
|
+
result.isValid = false;
|
|
623
|
+
result.reason.push(ErrorType.MethodNotAllowed);
|
|
624
|
+
return result;
|
|
722
625
|
}
|
|
723
|
-
|
|
724
|
-
|
|
626
|
+
const pathObj = this.spec.paths[path];
|
|
627
|
+
if (!pathObj || !pathObj[method]) {
|
|
628
|
+
result.isValid = false;
|
|
629
|
+
result.reason.push(ErrorType.UrlPathNotExist);
|
|
630
|
+
return result;
|
|
725
631
|
}
|
|
726
|
-
return
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
632
|
+
return result;
|
|
633
|
+
}
|
|
634
|
+
validateResponse(method, path) {
|
|
635
|
+
const result = { isValid: true, reason: [] };
|
|
636
|
+
const operationObject = this.spec.paths[path][method];
|
|
637
|
+
const { json, multipleMediaType } = Utils.getResponseJson(operationObject);
|
|
638
|
+
// only support response body only contains “application/json” content type
|
|
639
|
+
if (multipleMediaType) {
|
|
640
|
+
result.reason.push(ErrorType.ResponseContainMultipleMediaTypes);
|
|
641
|
+
}
|
|
642
|
+
else if (Object.keys(json).length === 0) {
|
|
643
|
+
// response body should not be empty
|
|
644
|
+
result.reason.push(ErrorType.ResponseJsonIsEmpty);
|
|
645
|
+
}
|
|
646
|
+
return result;
|
|
647
|
+
}
|
|
648
|
+
validateServer(method, path) {
|
|
649
|
+
const result = { isValid: true, reason: [] };
|
|
650
|
+
const serverObj = Utils.getServerObject(this.spec, method, path);
|
|
651
|
+
if (!serverObj) {
|
|
652
|
+
// should contain server URL
|
|
653
|
+
result.reason.push(ErrorType.NoServerInformation);
|
|
654
|
+
}
|
|
655
|
+
else {
|
|
656
|
+
// server url should be absolute url with https protocol
|
|
657
|
+
const serverValidateResult = Utils.checkServerUrl([serverObj]);
|
|
658
|
+
result.reason.push(...serverValidateResult.map((item) => item.type));
|
|
659
|
+
}
|
|
660
|
+
return result;
|
|
661
|
+
}
|
|
662
|
+
validateAuth(method, path) {
|
|
663
|
+
const pathObj = this.spec.paths[path];
|
|
664
|
+
const operationObject = pathObj[method];
|
|
665
|
+
const securities = operationObject.security;
|
|
666
|
+
const authSchemeArray = Utils.getAuthArray(securities, this.spec);
|
|
667
|
+
if (authSchemeArray.length === 0) {
|
|
668
|
+
return { isValid: true, reason: [] };
|
|
669
|
+
}
|
|
670
|
+
if (this.options.allowAPIKeyAuth ||
|
|
671
|
+
this.options.allowOauth2 ||
|
|
672
|
+
this.options.allowBearerTokenAuth) {
|
|
673
|
+
// Currently we don't support multiple auth in one operation
|
|
674
|
+
if (authSchemeArray.length > 0 && authSchemeArray.every((auths) => auths.length > 1)) {
|
|
675
|
+
return {
|
|
676
|
+
isValid: false,
|
|
677
|
+
reason: [ErrorType.MultipleAuthNotSupported],
|
|
678
|
+
};
|
|
679
|
+
}
|
|
680
|
+
for (const auths of authSchemeArray) {
|
|
681
|
+
if (auths.length === 1) {
|
|
682
|
+
if ((this.options.allowAPIKeyAuth && Utils.isAPIKeyAuth(auths[0].authScheme)) ||
|
|
683
|
+
(this.options.allowOauth2 && Utils.isOAuthWithAuthCodeFlow(auths[0].authScheme)) ||
|
|
684
|
+
(this.options.allowBearerTokenAuth && Utils.isBearerTokenAuth(auths[0].authScheme))) {
|
|
685
|
+
return { isValid: true, reason: [] };
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
return { isValid: false, reason: [ErrorType.AuthTypeIsNotSupported] };
|
|
691
|
+
}
|
|
692
|
+
checkPostBodySchema(schema, isRequired = false) {
|
|
693
|
+
var _a;
|
|
694
|
+
const paramResult = {
|
|
695
|
+
requiredNum: 0,
|
|
696
|
+
optionalNum: 0,
|
|
697
|
+
isValid: true,
|
|
698
|
+
reason: [],
|
|
730
699
|
};
|
|
700
|
+
if (Object.keys(schema).length === 0) {
|
|
701
|
+
return paramResult;
|
|
702
|
+
}
|
|
703
|
+
const isRequiredWithoutDefault = isRequired && schema.default === undefined;
|
|
704
|
+
const isCopilot = this.projectType === ProjectType.Copilot;
|
|
705
|
+
if (isCopilot && this.hasNestedObjectInSchema(schema)) {
|
|
706
|
+
paramResult.isValid = false;
|
|
707
|
+
paramResult.reason = [ErrorType.RequestBodyContainsNestedObject];
|
|
708
|
+
return paramResult;
|
|
709
|
+
}
|
|
710
|
+
if (schema.type === "string" ||
|
|
711
|
+
schema.type === "integer" ||
|
|
712
|
+
schema.type === "boolean" ||
|
|
713
|
+
schema.type === "number") {
|
|
714
|
+
if (isRequiredWithoutDefault) {
|
|
715
|
+
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
716
|
+
}
|
|
717
|
+
else {
|
|
718
|
+
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
else if (schema.type === "object") {
|
|
722
|
+
const { properties } = schema;
|
|
723
|
+
for (const property in properties) {
|
|
724
|
+
let isRequired = false;
|
|
725
|
+
if (schema.required && ((_a = schema.required) === null || _a === void 0 ? void 0 : _a.indexOf(property)) >= 0) {
|
|
726
|
+
isRequired = true;
|
|
727
|
+
}
|
|
728
|
+
const result = this.checkPostBodySchema(properties[property], isRequired);
|
|
729
|
+
paramResult.requiredNum += result.requiredNum;
|
|
730
|
+
paramResult.optionalNum += result.optionalNum;
|
|
731
|
+
paramResult.isValid = paramResult.isValid && result.isValid;
|
|
732
|
+
paramResult.reason.push(...result.reason);
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
else {
|
|
736
|
+
if (isRequiredWithoutDefault && !isCopilot) {
|
|
737
|
+
paramResult.isValid = false;
|
|
738
|
+
paramResult.reason.push(ErrorType.PostBodyContainsRequiredUnsupportedSchema);
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
return paramResult;
|
|
731
742
|
}
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
743
|
+
checkParamSchema(paramObject) {
|
|
744
|
+
const paramResult = {
|
|
745
|
+
requiredNum: 0,
|
|
746
|
+
optionalNum: 0,
|
|
747
|
+
isValid: true,
|
|
748
|
+
reason: [],
|
|
749
|
+
};
|
|
750
|
+
if (!paramObject) {
|
|
751
|
+
return paramResult;
|
|
752
|
+
}
|
|
753
|
+
const isCopilot = this.projectType === ProjectType.Copilot;
|
|
754
|
+
for (let i = 0; i < paramObject.length; i++) {
|
|
755
|
+
const param = paramObject[i];
|
|
756
|
+
const schema = param.schema;
|
|
757
|
+
if (isCopilot && this.hasNestedObjectInSchema(schema)) {
|
|
758
|
+
paramResult.isValid = false;
|
|
759
|
+
paramResult.reason.push(ErrorType.ParamsContainsNestedObject);
|
|
760
|
+
continue;
|
|
761
|
+
}
|
|
762
|
+
const isRequiredWithoutDefault = param.required && schema.default === undefined;
|
|
763
|
+
if (isCopilot) {
|
|
764
|
+
if (isRequiredWithoutDefault) {
|
|
765
|
+
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
766
|
+
}
|
|
767
|
+
else {
|
|
768
|
+
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
769
|
+
}
|
|
770
|
+
continue;
|
|
771
|
+
}
|
|
772
|
+
if (param.in === "header" || param.in === "cookie") {
|
|
773
|
+
if (isRequiredWithoutDefault) {
|
|
774
|
+
paramResult.isValid = false;
|
|
775
|
+
paramResult.reason.push(ErrorType.ParamsContainRequiredUnsupportedSchema);
|
|
776
|
+
}
|
|
777
|
+
continue;
|
|
778
|
+
}
|
|
779
|
+
if (schema.type !== "boolean" &&
|
|
780
|
+
schema.type !== "string" &&
|
|
781
|
+
schema.type !== "number" &&
|
|
782
|
+
schema.type !== "integer") {
|
|
783
|
+
if (isRequiredWithoutDefault) {
|
|
784
|
+
paramResult.isValid = false;
|
|
785
|
+
paramResult.reason.push(ErrorType.ParamsContainRequiredUnsupportedSchema);
|
|
786
|
+
}
|
|
787
|
+
continue;
|
|
788
|
+
}
|
|
789
|
+
if (param.in === "query" || param.in === "path") {
|
|
790
|
+
if (isRequiredWithoutDefault) {
|
|
791
|
+
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
792
|
+
}
|
|
793
|
+
else {
|
|
794
|
+
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
return paramResult;
|
|
738
799
|
}
|
|
739
|
-
|
|
740
|
-
if (
|
|
741
|
-
|
|
800
|
+
hasNestedObjectInSchema(schema) {
|
|
801
|
+
if (schema.type === "object") {
|
|
802
|
+
for (const property in schema.properties) {
|
|
803
|
+
const nestedSchema = schema.properties[property];
|
|
804
|
+
if (nestedSchema.type === "object") {
|
|
805
|
+
return true;
|
|
806
|
+
}
|
|
807
|
+
}
|
|
742
808
|
}
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
809
|
+
return false;
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
// Copyright (c) Microsoft Corporation.
|
|
814
|
+
class CopilotValidator extends Validator {
|
|
815
|
+
constructor(spec, options) {
|
|
816
|
+
super();
|
|
817
|
+
this.projectType = ProjectType.Copilot;
|
|
818
|
+
this.options = options;
|
|
819
|
+
this.spec = spec;
|
|
820
|
+
}
|
|
821
|
+
validateSpec() {
|
|
822
|
+
const result = { errors: [], warnings: [] };
|
|
823
|
+
// validate spec version
|
|
824
|
+
let validationResult = this.validateSpecVersion();
|
|
825
|
+
result.errors.push(...validationResult.errors);
|
|
826
|
+
// validate spec server
|
|
827
|
+
validationResult = this.validateSpecServer();
|
|
828
|
+
result.errors.push(...validationResult.errors);
|
|
829
|
+
// validate no supported API
|
|
830
|
+
validationResult = this.validateSpecNoSupportAPI();
|
|
831
|
+
result.errors.push(...validationResult.errors);
|
|
832
|
+
// validate operationId missing
|
|
833
|
+
validationResult = this.validateSpecOperationId();
|
|
834
|
+
result.warnings.push(...validationResult.warnings);
|
|
835
|
+
return result;
|
|
836
|
+
}
|
|
837
|
+
validateAPI(method, path) {
|
|
838
|
+
const result = { isValid: true, reason: [] };
|
|
839
|
+
method = method.toLocaleLowerCase();
|
|
840
|
+
// validate method and path
|
|
841
|
+
const methodAndPathResult = this.validateMethodAndPath(method, path);
|
|
842
|
+
if (!methodAndPathResult.isValid) {
|
|
843
|
+
return methodAndPathResult;
|
|
844
|
+
}
|
|
845
|
+
const operationObject = this.spec.paths[path][method];
|
|
846
|
+
// validate auth
|
|
847
|
+
const authCheckResult = this.validateAuth(method, path);
|
|
848
|
+
result.reason.push(...authCheckResult.reason);
|
|
849
|
+
// validate operationId
|
|
850
|
+
if (!this.options.allowMissingId && !operationObject.operationId) {
|
|
851
|
+
result.reason.push(ErrorType.MissingOperationId);
|
|
852
|
+
}
|
|
853
|
+
// validate server
|
|
854
|
+
const validateServerResult = this.validateServer(method, path);
|
|
855
|
+
result.reason.push(...validateServerResult.reason);
|
|
856
|
+
// validate response
|
|
857
|
+
const validateResponseResult = this.validateResponse(method, path);
|
|
858
|
+
result.reason.push(...validateResponseResult.reason);
|
|
859
|
+
// validate requestBody
|
|
860
|
+
const requestBody = operationObject.requestBody;
|
|
861
|
+
const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
|
|
862
|
+
if (Utils.containMultipleMediaTypes(requestBody)) {
|
|
863
|
+
result.reason.push(ErrorType.PostBodyContainMultipleMediaTypes);
|
|
864
|
+
}
|
|
865
|
+
if (requestJsonBody) {
|
|
866
|
+
const requestBodySchema = requestJsonBody.schema;
|
|
867
|
+
if (requestBodySchema.type !== "object") {
|
|
868
|
+
result.reason.push(ErrorType.PostBodySchemaIsNotJson);
|
|
869
|
+
}
|
|
870
|
+
const requestBodyParamResult = this.checkPostBodySchema(requestBodySchema, requestBody.required);
|
|
871
|
+
result.reason.push(...requestBodyParamResult.reason);
|
|
872
|
+
}
|
|
873
|
+
// validate parameters
|
|
874
|
+
const paramObject = operationObject.parameters;
|
|
875
|
+
const paramResult = this.checkParamSchema(paramObject);
|
|
876
|
+
result.reason.push(...paramResult.reason);
|
|
877
|
+
if (result.reason.length > 0) {
|
|
878
|
+
result.isValid = false;
|
|
879
|
+
}
|
|
880
|
+
return result;
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
// Copyright (c) Microsoft Corporation.
|
|
885
|
+
class SMEValidator extends Validator {
|
|
886
|
+
constructor(spec, options) {
|
|
887
|
+
super();
|
|
888
|
+
this.projectType = ProjectType.SME;
|
|
889
|
+
this.options = options;
|
|
890
|
+
this.spec = spec;
|
|
891
|
+
}
|
|
892
|
+
validateSpec() {
|
|
893
|
+
const result = { errors: [], warnings: [] };
|
|
894
|
+
// validate spec version
|
|
895
|
+
let validationResult = this.validateSpecVersion();
|
|
896
|
+
result.errors.push(...validationResult.errors);
|
|
897
|
+
// validate spec server
|
|
898
|
+
validationResult = this.validateSpecServer();
|
|
899
|
+
result.errors.push(...validationResult.errors);
|
|
900
|
+
// validate no supported API
|
|
901
|
+
validationResult = this.validateSpecNoSupportAPI();
|
|
902
|
+
result.errors.push(...validationResult.errors);
|
|
903
|
+
// validate operationId missing
|
|
904
|
+
if (this.options.allowMissingId) {
|
|
905
|
+
validationResult = this.validateSpecOperationId();
|
|
906
|
+
result.warnings.push(...validationResult.warnings);
|
|
907
|
+
}
|
|
908
|
+
return result;
|
|
909
|
+
}
|
|
910
|
+
validateAPI(method, path) {
|
|
911
|
+
const result = { isValid: true, reason: [] };
|
|
912
|
+
method = method.toLocaleLowerCase();
|
|
913
|
+
// validate method and path
|
|
914
|
+
const methodAndPathResult = this.validateMethodAndPath(method, path);
|
|
915
|
+
if (!methodAndPathResult.isValid) {
|
|
916
|
+
return methodAndPathResult;
|
|
917
|
+
}
|
|
918
|
+
const operationObject = this.spec.paths[path][method];
|
|
919
|
+
// validate auth
|
|
920
|
+
const authCheckResult = this.validateAuth(method, path);
|
|
921
|
+
result.reason.push(...authCheckResult.reason);
|
|
922
|
+
// validate operationId
|
|
923
|
+
if (!this.options.allowMissingId && !operationObject.operationId) {
|
|
924
|
+
result.reason.push(ErrorType.MissingOperationId);
|
|
925
|
+
}
|
|
926
|
+
// validate server
|
|
927
|
+
const validateServerResult = this.validateServer(method, path);
|
|
928
|
+
result.reason.push(...validateServerResult.reason);
|
|
929
|
+
// validate response
|
|
930
|
+
const validateResponseResult = this.validateResponse(method, path);
|
|
931
|
+
result.reason.push(...validateResponseResult.reason);
|
|
932
|
+
let postBodyResult = {
|
|
933
|
+
requiredNum: 0,
|
|
934
|
+
optionalNum: 0,
|
|
935
|
+
isValid: true,
|
|
936
|
+
reason: [],
|
|
937
|
+
};
|
|
938
|
+
// validate requestBody
|
|
939
|
+
const requestBody = operationObject.requestBody;
|
|
940
|
+
const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
|
|
941
|
+
if (Utils.containMultipleMediaTypes(requestBody)) {
|
|
942
|
+
result.reason.push(ErrorType.PostBodyContainMultipleMediaTypes);
|
|
943
|
+
}
|
|
944
|
+
if (requestJsonBody) {
|
|
945
|
+
const requestBodySchema = requestJsonBody.schema;
|
|
946
|
+
postBodyResult = this.checkPostBodySchema(requestBodySchema, requestBody.required);
|
|
947
|
+
result.reason.push(...postBodyResult.reason);
|
|
948
|
+
}
|
|
949
|
+
// validate parameters
|
|
950
|
+
const paramObject = operationObject.parameters;
|
|
951
|
+
const paramResult = this.checkParamSchema(paramObject);
|
|
952
|
+
result.reason.push(...paramResult.reason);
|
|
953
|
+
// validate total parameters count
|
|
954
|
+
if (paramResult.isValid && postBodyResult.isValid) {
|
|
955
|
+
const paramCountResult = this.validateParamCount(postBodyResult, paramResult);
|
|
956
|
+
result.reason.push(...paramCountResult.reason);
|
|
957
|
+
}
|
|
958
|
+
if (result.reason.length > 0) {
|
|
959
|
+
result.isValid = false;
|
|
960
|
+
}
|
|
961
|
+
return result;
|
|
962
|
+
}
|
|
963
|
+
validateParamCount(postBodyResult, paramResult) {
|
|
964
|
+
const result = { isValid: true, reason: [] };
|
|
965
|
+
const totalRequiredParams = postBodyResult.requiredNum + paramResult.requiredNum;
|
|
966
|
+
const totalParams = totalRequiredParams + postBodyResult.optionalNum + paramResult.optionalNum;
|
|
967
|
+
if (totalRequiredParams > 1) {
|
|
968
|
+
if (!this.options.allowMultipleParameters ||
|
|
969
|
+
totalRequiredParams > SMEValidator.SMERequiredParamsMaxNum) {
|
|
970
|
+
result.reason.push(ErrorType.ExceededRequiredParamsLimit);
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
else if (totalParams === 0) {
|
|
974
|
+
result.reason.push(ErrorType.NoParameter);
|
|
975
|
+
}
|
|
976
|
+
return result;
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
SMEValidator.SMERequiredParamsMaxNum = 5;
|
|
980
|
+
|
|
981
|
+
// Copyright (c) Microsoft Corporation.
|
|
982
|
+
class TeamsAIValidator extends Validator {
|
|
983
|
+
constructor(spec, options) {
|
|
984
|
+
super();
|
|
985
|
+
this.projectType = ProjectType.TeamsAi;
|
|
986
|
+
this.options = options;
|
|
987
|
+
this.spec = spec;
|
|
988
|
+
}
|
|
989
|
+
validateSpec() {
|
|
990
|
+
const result = { errors: [], warnings: [] };
|
|
991
|
+
// validate spec server
|
|
992
|
+
let validationResult = this.validateSpecServer();
|
|
993
|
+
result.errors.push(...validationResult.errors);
|
|
994
|
+
// validate no supported API
|
|
995
|
+
validationResult = this.validateSpecNoSupportAPI();
|
|
996
|
+
result.errors.push(...validationResult.errors);
|
|
997
|
+
return result;
|
|
998
|
+
}
|
|
999
|
+
validateAPI(method, path) {
|
|
1000
|
+
const result = { isValid: true, reason: [] };
|
|
1001
|
+
method = method.toLocaleLowerCase();
|
|
1002
|
+
// validate method and path
|
|
1003
|
+
const methodAndPathResult = this.validateMethodAndPath(method, path);
|
|
1004
|
+
if (!methodAndPathResult.isValid) {
|
|
1005
|
+
return methodAndPathResult;
|
|
1006
|
+
}
|
|
1007
|
+
const operationObject = this.spec.paths[path][method];
|
|
1008
|
+
// validate operationId
|
|
1009
|
+
if (!this.options.allowMissingId && !operationObject.operationId) {
|
|
1010
|
+
result.reason.push(ErrorType.MissingOperationId);
|
|
1011
|
+
}
|
|
1012
|
+
// validate server
|
|
1013
|
+
const validateServerResult = this.validateServer(method, path);
|
|
1014
|
+
result.reason.push(...validateServerResult.reason);
|
|
1015
|
+
if (result.reason.length > 0) {
|
|
1016
|
+
result.isValid = false;
|
|
1017
|
+
}
|
|
1018
|
+
return result;
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
class ValidatorFactory {
|
|
1023
|
+
static create(spec, options) {
|
|
1024
|
+
var _a;
|
|
1025
|
+
const type = (_a = options.projectType) !== null && _a !== void 0 ? _a : ProjectType.SME;
|
|
1026
|
+
switch (type) {
|
|
1027
|
+
case ProjectType.SME:
|
|
1028
|
+
return new SMEValidator(spec, options);
|
|
1029
|
+
case ProjectType.Copilot:
|
|
1030
|
+
return new CopilotValidator(spec, options);
|
|
1031
|
+
case ProjectType.TeamsAi:
|
|
1032
|
+
return new TeamsAIValidator(spec, options);
|
|
1033
|
+
default:
|
|
1034
|
+
throw new Error(`Invalid project type: ${type}`);
|
|
746
1035
|
}
|
|
747
|
-
return safeRegistrationIdEnvName;
|
|
748
1036
|
}
|
|
749
1037
|
}
|
|
750
1038
|
|
|
@@ -764,7 +1052,12 @@ class SpecParser {
|
|
|
764
1052
|
allowSwagger: false,
|
|
765
1053
|
allowAPIKeyAuth: false,
|
|
766
1054
|
allowMultipleParameters: false,
|
|
1055
|
+
allowBearerTokenAuth: false,
|
|
767
1056
|
allowOauth2: false,
|
|
1057
|
+
allowMethods: ["get", "post"],
|
|
1058
|
+
allowConversationStarters: false,
|
|
1059
|
+
allowResponseSemantics: false,
|
|
1060
|
+
projectType: ProjectType.SME,
|
|
768
1061
|
};
|
|
769
1062
|
this.pathOrSpec = pathOrDoc;
|
|
770
1063
|
this.parser = new SwaggerParser();
|
|
@@ -780,11 +1073,7 @@ class SpecParser {
|
|
|
780
1073
|
try {
|
|
781
1074
|
try {
|
|
782
1075
|
yield this.loadSpec();
|
|
783
|
-
yield this.parser.validate(this.spec
|
|
784
|
-
validate: {
|
|
785
|
-
schema: false,
|
|
786
|
-
},
|
|
787
|
-
});
|
|
1076
|
+
yield this.parser.validate(this.spec);
|
|
788
1077
|
}
|
|
789
1078
|
catch (e) {
|
|
790
1079
|
return {
|
|
@@ -793,16 +1082,46 @@ class SpecParser {
|
|
|
793
1082
|
errors: [{ type: ErrorType.SpecNotValid, content: e.toString() }],
|
|
794
1083
|
};
|
|
795
1084
|
}
|
|
1085
|
+
const errors = [];
|
|
1086
|
+
const warnings = [];
|
|
796
1087
|
if (!this.options.allowSwagger && this.isSwaggerFile) {
|
|
797
1088
|
return {
|
|
798
1089
|
status: ValidationStatus.Error,
|
|
799
1090
|
warnings: [],
|
|
800
1091
|
errors: [
|
|
801
|
-
{
|
|
1092
|
+
{
|
|
1093
|
+
type: ErrorType.SwaggerNotSupported,
|
|
1094
|
+
content: ConstantString.SwaggerNotSupported,
|
|
1095
|
+
},
|
|
802
1096
|
],
|
|
803
1097
|
};
|
|
804
1098
|
}
|
|
805
|
-
|
|
1099
|
+
// Remote reference not supported
|
|
1100
|
+
const refPaths = this.parser.$refs.paths();
|
|
1101
|
+
// refPaths [0] is the current spec file path
|
|
1102
|
+
if (refPaths.length > 1) {
|
|
1103
|
+
errors.push({
|
|
1104
|
+
type: ErrorType.RemoteRefNotSupported,
|
|
1105
|
+
content: Utils.format(ConstantString.RemoteRefNotSupported, refPaths.join(", ")),
|
|
1106
|
+
data: refPaths,
|
|
1107
|
+
});
|
|
1108
|
+
}
|
|
1109
|
+
const validator = this.getValidator(this.spec);
|
|
1110
|
+
const validationResult = validator.validateSpec();
|
|
1111
|
+
warnings.push(...validationResult.warnings);
|
|
1112
|
+
errors.push(...validationResult.errors);
|
|
1113
|
+
let status = ValidationStatus.Valid;
|
|
1114
|
+
if (warnings.length > 0 && errors.length === 0) {
|
|
1115
|
+
status = ValidationStatus.Warning;
|
|
1116
|
+
}
|
|
1117
|
+
else if (errors.length > 0) {
|
|
1118
|
+
status = ValidationStatus.Error;
|
|
1119
|
+
}
|
|
1120
|
+
return {
|
|
1121
|
+
status: status,
|
|
1122
|
+
warnings: warnings,
|
|
1123
|
+
errors: errors,
|
|
1124
|
+
};
|
|
806
1125
|
}
|
|
807
1126
|
catch (err) {
|
|
808
1127
|
throw new SpecParserError(err.toString(), ErrorType.ValidateFailed);
|
|
@@ -813,17 +1132,20 @@ class SpecParser {
|
|
|
813
1132
|
return __awaiter(this, void 0, void 0, function* () {
|
|
814
1133
|
try {
|
|
815
1134
|
yield this.loadSpec();
|
|
816
|
-
const apiMap = this.
|
|
1135
|
+
const apiMap = this.getAPIs(this.spec);
|
|
817
1136
|
const apiInfos = [];
|
|
818
1137
|
for (const key in apiMap) {
|
|
819
|
-
const
|
|
1138
|
+
const { operation, isValid } = apiMap[key];
|
|
1139
|
+
if (!isValid) {
|
|
1140
|
+
continue;
|
|
1141
|
+
}
|
|
820
1142
|
const [method, path] = key.split(" ");
|
|
821
|
-
const operationId =
|
|
1143
|
+
const operationId = operation.operationId;
|
|
822
1144
|
// In Browser environment, this api is by default not support api without operationId
|
|
823
1145
|
if (!operationId) {
|
|
824
1146
|
continue;
|
|
825
1147
|
}
|
|
826
|
-
const
|
|
1148
|
+
const command = Utils.parseApiInfo(operation, this.options);
|
|
827
1149
|
const apiInfo = {
|
|
828
1150
|
method: method,
|
|
829
1151
|
path: path,
|
|
@@ -832,9 +1154,6 @@ class SpecParser {
|
|
|
832
1154
|
parameters: command.parameters,
|
|
833
1155
|
description: command.description,
|
|
834
1156
|
};
|
|
835
|
-
if (warning) {
|
|
836
|
-
apiInfo.warning = warning;
|
|
837
|
-
}
|
|
838
1157
|
apiInfos.push(apiInfo);
|
|
839
1158
|
}
|
|
840
1159
|
return apiInfos;
|
|
@@ -865,6 +1184,19 @@ class SpecParser {
|
|
|
865
1184
|
throw new Error("Method not implemented.");
|
|
866
1185
|
});
|
|
867
1186
|
}
|
|
1187
|
+
/**
|
|
1188
|
+
* Generates and update artifacts from the OpenAPI specification file. Generate Adaptive Cards, update Teams app manifest, and generate a new OpenAPI specification file.
|
|
1189
|
+
* @param manifestPath A file path of the Teams app manifest file to update.
|
|
1190
|
+
* @param filter An array of strings that represent the filters to apply when generating the artifacts. If filter is empty, it would process nothing.
|
|
1191
|
+
* @param outputSpecPath File path of the new OpenAPI specification file to generate. If not specified or empty, no spec file will be generated.
|
|
1192
|
+
* @param pluginFilePath File path of the api plugin file to generate.
|
|
1193
|
+
*/
|
|
1194
|
+
// eslint-disable-next-line @typescript-eslint/require-await
|
|
1195
|
+
generateForCopilot(manifestPath, filter, outputSpecPath, pluginFilePath, signal) {
|
|
1196
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1197
|
+
throw new Error("Method not implemented.");
|
|
1198
|
+
});
|
|
1199
|
+
}
|
|
868
1200
|
/**
|
|
869
1201
|
* Generates and update artifacts from the OpenAPI specification file. Generate Adaptive Cards, update Teams app manifest, and generate a new OpenAPI specification file.
|
|
870
1202
|
* @param manifestPath A file path of the Teams app manifest file to update.
|
|
@@ -874,7 +1206,7 @@ class SpecParser {
|
|
|
874
1206
|
* @param isMe Boolean that indicates whether the project is an Messaging Extension. For Messaging Extension, composeExtensions will be added in Teams app manifest.
|
|
875
1207
|
*/
|
|
876
1208
|
// eslint-disable-next-line @typescript-eslint/require-await
|
|
877
|
-
generate(manifestPath, filter, outputSpecPath, adaptiveCardFolder, signal
|
|
1209
|
+
generate(manifestPath, filter, outputSpecPath, adaptiveCardFolder, signal) {
|
|
878
1210
|
return __awaiter(this, void 0, void 0, function* () {
|
|
879
1211
|
throw new Error("Method not implemented.");
|
|
880
1212
|
});
|
|
@@ -891,13 +1223,18 @@ class SpecParser {
|
|
|
891
1223
|
}
|
|
892
1224
|
});
|
|
893
1225
|
}
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
1226
|
+
getAPIs(spec) {
|
|
1227
|
+
const validator = this.getValidator(spec);
|
|
1228
|
+
const apiMap = validator.listAPIs();
|
|
1229
|
+
return apiMap;
|
|
1230
|
+
}
|
|
1231
|
+
getValidator(spec) {
|
|
1232
|
+
if (this.validator) {
|
|
1233
|
+
return this.validator;
|
|
897
1234
|
}
|
|
898
|
-
const
|
|
899
|
-
this.
|
|
900
|
-
return
|
|
1235
|
+
const validator = ValidatorFactory.create(spec, this.options);
|
|
1236
|
+
this.validator = validator;
|
|
1237
|
+
return validator;
|
|
901
1238
|
}
|
|
902
1239
|
}
|
|
903
1240
|
|
|
@@ -905,7 +1242,7 @@ class SpecParser {
|
|
|
905
1242
|
class AdaptiveCardGenerator {
|
|
906
1243
|
static generateAdaptiveCard(operationItem) {
|
|
907
1244
|
try {
|
|
908
|
-
const json = Utils.getResponseJson(operationItem);
|
|
1245
|
+
const { json } = Utils.getResponseJson(operationItem);
|
|
909
1246
|
let cardBody = [];
|
|
910
1247
|
let schema = json.schema;
|
|
911
1248
|
let jsonPath = "$";
|