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