@microsoft/m365-spec-parser 0.1.1-alpha.48b9eab36.0 → 0.1.1-alpha.4f2290daa.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 +284 -145
- package/dist/index.esm2017.js.map +1 -1
- package/dist/index.esm2017.mjs +567 -237
- package/dist/index.esm2017.mjs.map +1 -1
- package/dist/index.esm5.js +286 -145
- package/dist/index.esm5.js.map +1 -1
- package/dist/index.node.cjs.js +570 -236
- package/dist/index.node.cjs.js.map +1 -1
- package/dist/src/constants.d.ts +5 -1
- package/dist/src/index.browser.d.ts +1 -1
- package/dist/src/index.d.ts +1 -1
- package/dist/src/interfaces.d.ts +75 -18
- package/dist/src/manifestUpdater.d.ts +9 -4
- package/dist/src/specFilter.d.ts +2 -1
- package/dist/src/specParser.browser.d.ts +10 -1
- package/dist/src/specParser.d.ts +11 -4
- package/dist/src/utils.d.ts +24 -19
- 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";
|
|
@@ -85,6 +109,7 @@ ConstantString.AdaptiveCardType = "AdaptiveCard";
|
|
|
85
109
|
ConstantString.TextBlockType = "TextBlock";
|
|
86
110
|
ConstantString.ContainerType = "Container";
|
|
87
111
|
ConstantString.RegistrationIdPostfix = "REGISTRATION_ID";
|
|
112
|
+
ConstantString.OAuthRegistrationIdPostFix = "OAUTH_REGISTRATION_ID";
|
|
88
113
|
ConstantString.ResponseCodeFor20X = [
|
|
89
114
|
"200",
|
|
90
115
|
"201",
|
|
@@ -144,15 +169,28 @@ ConstantString.FullDescriptionMaxLens = 4000;
|
|
|
144
169
|
ConstantString.CommandDescriptionMaxLens = 128;
|
|
145
170
|
ConstantString.ParameterDescriptionMaxLens = 128;
|
|
146
171
|
ConstantString.CommandTitleMaxLens = 32;
|
|
147
|
-
ConstantString.ParameterTitleMaxLens = 32;
|
|
172
|
+
ConstantString.ParameterTitleMaxLens = 32;
|
|
173
|
+
ConstantString.SMERequiredParamsMaxNum = 5;
|
|
148
174
|
|
|
149
175
|
// Copyright (c) Microsoft Corporation.
|
|
150
176
|
class Utils {
|
|
151
|
-
static
|
|
177
|
+
static hasNestedObjectInSchema(schema) {
|
|
178
|
+
if (schema.type === "object") {
|
|
179
|
+
for (const property in schema.properties) {
|
|
180
|
+
const nestedSchema = schema.properties[property];
|
|
181
|
+
if (nestedSchema.type === "object") {
|
|
182
|
+
return true;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return false;
|
|
187
|
+
}
|
|
188
|
+
static checkParameters(paramObject, isCopilot) {
|
|
152
189
|
const paramResult = {
|
|
153
190
|
requiredNum: 0,
|
|
154
191
|
optionalNum: 0,
|
|
155
192
|
isValid: true,
|
|
193
|
+
reason: [],
|
|
156
194
|
};
|
|
157
195
|
if (!paramObject) {
|
|
158
196
|
return paramResult;
|
|
@@ -160,10 +198,25 @@ class Utils {
|
|
|
160
198
|
for (let i = 0; i < paramObject.length; i++) {
|
|
161
199
|
const param = paramObject[i];
|
|
162
200
|
const schema = param.schema;
|
|
201
|
+
if (isCopilot && this.hasNestedObjectInSchema(schema)) {
|
|
202
|
+
paramResult.isValid = false;
|
|
203
|
+
paramResult.reason.push(ErrorType.ParamsContainsNestedObject);
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
163
206
|
const isRequiredWithoutDefault = param.required && schema.default === undefined;
|
|
207
|
+
if (isCopilot) {
|
|
208
|
+
if (isRequiredWithoutDefault) {
|
|
209
|
+
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
210
|
+
}
|
|
211
|
+
else {
|
|
212
|
+
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
213
|
+
}
|
|
214
|
+
continue;
|
|
215
|
+
}
|
|
164
216
|
if (param.in === "header" || param.in === "cookie") {
|
|
165
217
|
if (isRequiredWithoutDefault) {
|
|
166
218
|
paramResult.isValid = false;
|
|
219
|
+
paramResult.reason.push(ErrorType.ParamsContainRequiredUnsupportedSchema);
|
|
167
220
|
}
|
|
168
221
|
continue;
|
|
169
222
|
}
|
|
@@ -173,6 +226,7 @@ class Utils {
|
|
|
173
226
|
schema.type !== "integer") {
|
|
174
227
|
if (isRequiredWithoutDefault) {
|
|
175
228
|
paramResult.isValid = false;
|
|
229
|
+
paramResult.reason.push(ErrorType.ParamsContainRequiredUnsupportedSchema);
|
|
176
230
|
}
|
|
177
231
|
continue;
|
|
178
232
|
}
|
|
@@ -187,17 +241,23 @@ class Utils {
|
|
|
187
241
|
}
|
|
188
242
|
return paramResult;
|
|
189
243
|
}
|
|
190
|
-
static checkPostBody(schema, isRequired = false) {
|
|
244
|
+
static checkPostBody(schema, isRequired = false, isCopilot = false) {
|
|
191
245
|
var _a;
|
|
192
246
|
const paramResult = {
|
|
193
247
|
requiredNum: 0,
|
|
194
248
|
optionalNum: 0,
|
|
195
249
|
isValid: true,
|
|
250
|
+
reason: [],
|
|
196
251
|
};
|
|
197
252
|
if (Object.keys(schema).length === 0) {
|
|
198
253
|
return paramResult;
|
|
199
254
|
}
|
|
200
255
|
const isRequiredWithoutDefault = isRequired && schema.default === undefined;
|
|
256
|
+
if (isCopilot && this.hasNestedObjectInSchema(schema)) {
|
|
257
|
+
paramResult.isValid = false;
|
|
258
|
+
paramResult.reason = [ErrorType.RequestBodyContainsNestedObject];
|
|
259
|
+
return paramResult;
|
|
260
|
+
}
|
|
201
261
|
if (schema.type === "string" ||
|
|
202
262
|
schema.type === "integer" ||
|
|
203
263
|
schema.type === "boolean" ||
|
|
@@ -216,19 +276,24 @@ class Utils {
|
|
|
216
276
|
if (schema.required && ((_a = schema.required) === null || _a === void 0 ? void 0 : _a.indexOf(property)) >= 0) {
|
|
217
277
|
isRequired = true;
|
|
218
278
|
}
|
|
219
|
-
const result = Utils.checkPostBody(properties[property], isRequired);
|
|
279
|
+
const result = Utils.checkPostBody(properties[property], isRequired, isCopilot);
|
|
220
280
|
paramResult.requiredNum += result.requiredNum;
|
|
221
281
|
paramResult.optionalNum += result.optionalNum;
|
|
222
282
|
paramResult.isValid = paramResult.isValid && result.isValid;
|
|
283
|
+
paramResult.reason.push(...result.reason);
|
|
223
284
|
}
|
|
224
285
|
}
|
|
225
286
|
else {
|
|
226
|
-
if (isRequiredWithoutDefault) {
|
|
287
|
+
if (isRequiredWithoutDefault && !isCopilot) {
|
|
227
288
|
paramResult.isValid = false;
|
|
289
|
+
paramResult.reason.push(ErrorType.PostBodyContainsRequiredUnsupportedSchema);
|
|
228
290
|
}
|
|
229
291
|
}
|
|
230
292
|
return paramResult;
|
|
231
293
|
}
|
|
294
|
+
static containMultipleMediaTypes(bodyObject) {
|
|
295
|
+
return Object.keys((bodyObject === null || bodyObject === void 0 ? void 0 : bodyObject.content) || {}).length > 1;
|
|
296
|
+
}
|
|
232
297
|
/**
|
|
233
298
|
* Checks if the given API is supported.
|
|
234
299
|
* @param {string} method - The HTTP method of the API.
|
|
@@ -243,106 +308,137 @@ class Utils {
|
|
|
243
308
|
* 5. response body should be “application/json” and not empty, and response code should be 20X
|
|
244
309
|
* 6. only support request body with “application/json” content type
|
|
245
310
|
*/
|
|
246
|
-
static isSupportedApi(method, path, spec,
|
|
247
|
-
|
|
311
|
+
static isSupportedApi(method, path, spec, options) {
|
|
312
|
+
var _a;
|
|
313
|
+
const result = { isValid: true, reason: [] };
|
|
248
314
|
method = method.toLocaleLowerCase();
|
|
249
|
-
if (
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
315
|
+
if (options.allowMethods && !options.allowMethods.includes(method)) {
|
|
316
|
+
result.isValid = false;
|
|
317
|
+
result.reason.push(ErrorType.MethodNotAllowed);
|
|
318
|
+
return result;
|
|
319
|
+
}
|
|
320
|
+
const pathObj = spec.paths[path];
|
|
321
|
+
if (!pathObj || !pathObj[method]) {
|
|
322
|
+
result.isValid = false;
|
|
323
|
+
result.reason.push(ErrorType.UrlPathNotExist);
|
|
324
|
+
return result;
|
|
325
|
+
}
|
|
326
|
+
const securities = pathObj[method].security;
|
|
327
|
+
const isTeamsAi = options.projectType === ProjectType.TeamsAi;
|
|
328
|
+
const isCopilot = options.projectType === ProjectType.Copilot;
|
|
329
|
+
// Teams AI project doesn't care about auth, it will use authProvider for user to implement
|
|
330
|
+
if (!isTeamsAi) {
|
|
331
|
+
const authArray = Utils.getAuthArray(securities, spec);
|
|
332
|
+
const authCheckResult = Utils.isSupportedAuth(authArray, options);
|
|
333
|
+
if (!authCheckResult.isValid) {
|
|
334
|
+
result.reason.push(...authCheckResult.reason);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
const operationObject = pathObj[method];
|
|
338
|
+
if (!options.allowMissingId && !operationObject.operationId) {
|
|
339
|
+
result.reason.push(ErrorType.MissingOperationId);
|
|
340
|
+
}
|
|
341
|
+
const rootServer = spec.servers && spec.servers[0];
|
|
342
|
+
const methodServer = spec.paths[path].servers && ((_a = spec.paths[path]) === null || _a === void 0 ? void 0 : _a.servers[0]);
|
|
343
|
+
const operationServer = operationObject.servers && operationObject.servers[0];
|
|
344
|
+
const serverUrl = operationServer || methodServer || rootServer;
|
|
345
|
+
if (!serverUrl) {
|
|
346
|
+
result.reason.push(ErrorType.NoServerInformation);
|
|
347
|
+
}
|
|
348
|
+
else {
|
|
349
|
+
const serverValidateResult = Utils.checkServerUrl([serverUrl]);
|
|
350
|
+
result.reason.push(...serverValidateResult.map((item) => item.type));
|
|
351
|
+
}
|
|
352
|
+
const paramObject = operationObject.parameters;
|
|
353
|
+
const requestBody = operationObject.requestBody;
|
|
354
|
+
const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
|
|
355
|
+
if (!isTeamsAi && Utils.containMultipleMediaTypes(requestBody)) {
|
|
356
|
+
result.reason.push(ErrorType.PostBodyContainMultipleMediaTypes);
|
|
357
|
+
}
|
|
358
|
+
const { json, multipleMediaType } = Utils.getResponseJson(operationObject, isTeamsAi);
|
|
359
|
+
if (multipleMediaType && !isTeamsAi) {
|
|
360
|
+
result.reason.push(ErrorType.ResponseContainMultipleMediaTypes);
|
|
361
|
+
}
|
|
362
|
+
else if (Object.keys(json).length === 0) {
|
|
363
|
+
result.reason.push(ErrorType.ResponseJsonIsEmpty);
|
|
364
|
+
}
|
|
365
|
+
// Teams AI project doesn't care about request parameters/body
|
|
366
|
+
if (!isTeamsAi) {
|
|
367
|
+
let requestBodyParamResult = {
|
|
368
|
+
requiredNum: 0,
|
|
369
|
+
optionalNum: 0,
|
|
370
|
+
isValid: true,
|
|
371
|
+
reason: [],
|
|
372
|
+
};
|
|
373
|
+
if (requestJsonBody) {
|
|
374
|
+
const requestBodySchema = requestJsonBody.schema;
|
|
375
|
+
if (isCopilot && requestBodySchema.type !== "object") {
|
|
376
|
+
result.reason.push(ErrorType.PostBodySchemaIsNotJson);
|
|
283
377
|
}
|
|
284
|
-
|
|
285
|
-
if (!
|
|
286
|
-
|
|
378
|
+
requestBodyParamResult = Utils.checkPostBody(requestBodySchema, requestBody.required, isCopilot);
|
|
379
|
+
if (!requestBodyParamResult.isValid && requestBodyParamResult.reason) {
|
|
380
|
+
result.reason.push(...requestBodyParamResult.reason);
|
|
287
381
|
}
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
382
|
+
}
|
|
383
|
+
const paramResult = Utils.checkParameters(paramObject, isCopilot);
|
|
384
|
+
if (!paramResult.isValid && paramResult.reason) {
|
|
385
|
+
result.reason.push(...paramResult.reason);
|
|
386
|
+
}
|
|
387
|
+
// Copilot support arbitrary parameters
|
|
388
|
+
if (!isCopilot && paramResult.isValid && requestBodyParamResult.isValid) {
|
|
389
|
+
const totalRequiredParams = requestBodyParamResult.requiredNum + paramResult.requiredNum;
|
|
390
|
+
const totalParams = totalRequiredParams + requestBodyParamResult.optionalNum + paramResult.optionalNum;
|
|
391
|
+
if (totalRequiredParams > 1) {
|
|
392
|
+
if (!options.allowMultipleParameters ||
|
|
393
|
+
totalRequiredParams > ConstantString.SMERequiredParamsMaxNum) {
|
|
394
|
+
result.reason.push(ErrorType.ExceededRequiredParamsLimit);
|
|
292
395
|
}
|
|
293
|
-
return false;
|
|
294
396
|
}
|
|
295
|
-
else if (
|
|
296
|
-
|
|
297
|
-
paramResult.requiredNum +
|
|
298
|
-
paramResult.optionalNum ===
|
|
299
|
-
0) {
|
|
300
|
-
return false;
|
|
301
|
-
}
|
|
302
|
-
else {
|
|
303
|
-
return true;
|
|
397
|
+
else if (totalParams === 0) {
|
|
398
|
+
result.reason.push(ErrorType.NoParameter);
|
|
304
399
|
}
|
|
305
400
|
}
|
|
306
401
|
}
|
|
307
|
-
|
|
402
|
+
if (result.reason.length > 0) {
|
|
403
|
+
result.isValid = false;
|
|
404
|
+
}
|
|
405
|
+
return result;
|
|
308
406
|
}
|
|
309
|
-
static isSupportedAuth(
|
|
310
|
-
if (
|
|
311
|
-
return true;
|
|
407
|
+
static isSupportedAuth(authSchemeArray, options) {
|
|
408
|
+
if (authSchemeArray.length === 0) {
|
|
409
|
+
return { isValid: true, reason: [] };
|
|
312
410
|
}
|
|
313
|
-
if (allowAPIKeyAuth || allowOauth2) {
|
|
411
|
+
if (options.allowAPIKeyAuth || options.allowOauth2 || options.allowBearerTokenAuth) {
|
|
314
412
|
// Currently we don't support multiple auth in one operation
|
|
315
|
-
if (
|
|
316
|
-
return
|
|
413
|
+
if (authSchemeArray.length > 0 && authSchemeArray.every((auths) => auths.length > 1)) {
|
|
414
|
+
return {
|
|
415
|
+
isValid: false,
|
|
416
|
+
reason: [ErrorType.MultipleAuthNotSupported],
|
|
417
|
+
};
|
|
317
418
|
}
|
|
318
|
-
for (const auths of
|
|
419
|
+
for (const auths of authSchemeArray) {
|
|
319
420
|
if (auths.length === 1) {
|
|
320
|
-
if (
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
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;
|
|
421
|
+
if ((options.allowAPIKeyAuth && Utils.isAPIKeyAuth(auths[0].authScheme)) ||
|
|
422
|
+
(options.allowOauth2 && Utils.isOAuthWithAuthCodeFlow(auths[0].authScheme)) ||
|
|
423
|
+
(options.allowBearerTokenAuth && Utils.isBearerTokenAuth(auths[0].authScheme))) {
|
|
424
|
+
return { isValid: true, reason: [] };
|
|
333
425
|
}
|
|
334
426
|
}
|
|
335
427
|
}
|
|
336
428
|
}
|
|
337
|
-
return false;
|
|
429
|
+
return { isValid: false, reason: [ErrorType.AuthTypeIsNotSupported] };
|
|
430
|
+
}
|
|
431
|
+
static isBearerTokenAuth(authScheme) {
|
|
432
|
+
return authScheme.type === "http" && authScheme.scheme === "bearer";
|
|
338
433
|
}
|
|
339
|
-
static isAPIKeyAuth(
|
|
340
|
-
return
|
|
434
|
+
static isAPIKeyAuth(authScheme) {
|
|
435
|
+
return authScheme.type === "apiKey";
|
|
341
436
|
}
|
|
342
|
-
static
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
437
|
+
static isOAuthWithAuthCodeFlow(authScheme) {
|
|
438
|
+
if (authScheme.type === "oauth2" && authScheme.flows && authScheme.flows.authorizationCode) {
|
|
439
|
+
return true;
|
|
440
|
+
}
|
|
441
|
+
return false;
|
|
346
442
|
}
|
|
347
443
|
static getAuthArray(securities, spec) {
|
|
348
444
|
var _a;
|
|
@@ -355,7 +451,7 @@ class Utils {
|
|
|
355
451
|
for (const name in security) {
|
|
356
452
|
const auth = securitySchemas[name];
|
|
357
453
|
authArray.push({
|
|
358
|
-
|
|
454
|
+
authScheme: auth,
|
|
359
455
|
name: name,
|
|
360
456
|
});
|
|
361
457
|
}
|
|
@@ -370,21 +466,28 @@ class Utils {
|
|
|
370
466
|
static updateFirstLetter(str) {
|
|
371
467
|
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
372
468
|
}
|
|
373
|
-
static getResponseJson(operationObject) {
|
|
469
|
+
static getResponseJson(operationObject, isTeamsAiProject = false) {
|
|
374
470
|
var _a, _b;
|
|
375
471
|
let json = {};
|
|
472
|
+
let multipleMediaType = false;
|
|
376
473
|
for (const code of ConstantString.ResponseCodeFor20X) {
|
|
377
474
|
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
475
|
if ((_b = responseObject === null || responseObject === void 0 ? void 0 : responseObject.content) === null || _b === void 0 ? void 0 : _b["application/json"]) {
|
|
476
|
+
multipleMediaType = false;
|
|
383
477
|
json = responseObject.content["application/json"];
|
|
384
|
-
|
|
478
|
+
if (Utils.containMultipleMediaTypes(responseObject)) {
|
|
479
|
+
multipleMediaType = true;
|
|
480
|
+
if (isTeamsAiProject) {
|
|
481
|
+
break;
|
|
482
|
+
}
|
|
483
|
+
json = {};
|
|
484
|
+
}
|
|
485
|
+
else {
|
|
486
|
+
break;
|
|
487
|
+
}
|
|
385
488
|
}
|
|
386
489
|
}
|
|
387
|
-
return json;
|
|
490
|
+
return { json, multipleMediaType };
|
|
388
491
|
}
|
|
389
492
|
static convertPathToCamelCase(path) {
|
|
390
493
|
const pathSegments = path.split(/[./{]/);
|
|
@@ -404,10 +507,10 @@ class Utils {
|
|
|
404
507
|
return undefined;
|
|
405
508
|
}
|
|
406
509
|
}
|
|
407
|
-
static
|
|
510
|
+
static resolveEnv(str) {
|
|
408
511
|
const placeHolderReg = /\${{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*}}/g;
|
|
409
|
-
let matches = placeHolderReg.exec(
|
|
410
|
-
let
|
|
512
|
+
let matches = placeHolderReg.exec(str);
|
|
513
|
+
let newStr = str;
|
|
411
514
|
while (matches != null) {
|
|
412
515
|
const envVar = matches[1];
|
|
413
516
|
const envVal = process.env[envVar];
|
|
@@ -415,17 +518,17 @@ class Utils {
|
|
|
415
518
|
throw new Error(Utils.format(ConstantString.ResolveServerUrlFailed, envVar));
|
|
416
519
|
}
|
|
417
520
|
else {
|
|
418
|
-
|
|
521
|
+
newStr = newStr.replace(matches[0], envVal);
|
|
419
522
|
}
|
|
420
|
-
matches = placeHolderReg.exec(
|
|
523
|
+
matches = placeHolderReg.exec(str);
|
|
421
524
|
}
|
|
422
|
-
return
|
|
525
|
+
return newStr;
|
|
423
526
|
}
|
|
424
527
|
static checkServerUrl(servers) {
|
|
425
528
|
const errors = [];
|
|
426
529
|
let serverUrl;
|
|
427
530
|
try {
|
|
428
|
-
serverUrl = Utils.
|
|
531
|
+
serverUrl = Utils.resolveEnv(servers[0].url);
|
|
429
532
|
}
|
|
430
533
|
catch (err) {
|
|
431
534
|
errors.push({
|
|
@@ -455,7 +558,8 @@ class Utils {
|
|
|
455
558
|
}
|
|
456
559
|
return errors;
|
|
457
560
|
}
|
|
458
|
-
static validateServer(spec,
|
|
561
|
+
static validateServer(spec, options) {
|
|
562
|
+
var _a;
|
|
459
563
|
const errors = [];
|
|
460
564
|
let hasTopLevelServers = false;
|
|
461
565
|
let hasPathLevelServers = false;
|
|
@@ -476,7 +580,7 @@ class Utils {
|
|
|
476
580
|
}
|
|
477
581
|
for (const method in methods) {
|
|
478
582
|
const operationObject = methods[method];
|
|
479
|
-
if (
|
|
583
|
+
if (((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && operationObject) {
|
|
480
584
|
if ((operationObject === null || operationObject === void 0 ? void 0 : operationObject.servers) && operationObject.servers.length >= 1) {
|
|
481
585
|
hasOperationLevelServers = true;
|
|
482
586
|
const serverErrors = Utils.checkServerUrl(operationObject.servers);
|
|
@@ -519,6 +623,7 @@ class Utils {
|
|
|
519
623
|
Utils.updateParameterWithInputType(schema, parameter);
|
|
520
624
|
}
|
|
521
625
|
if (isRequired && schema.default === undefined) {
|
|
626
|
+
parameter.isRequired = true;
|
|
522
627
|
requiredParams.push(parameter);
|
|
523
628
|
}
|
|
524
629
|
else {
|
|
@@ -563,7 +668,7 @@ class Utils {
|
|
|
563
668
|
param.value = schema.default;
|
|
564
669
|
}
|
|
565
670
|
}
|
|
566
|
-
static parseApiInfo(operationItem,
|
|
671
|
+
static parseApiInfo(operationItem, options) {
|
|
567
672
|
var _a, _b;
|
|
568
673
|
const requiredParams = [];
|
|
569
674
|
const optionalParams = [];
|
|
@@ -577,11 +682,12 @@ class Utils {
|
|
|
577
682
|
description: ((_a = param.description) !== null && _a !== void 0 ? _a : "").slice(0, ConstantString.ParameterDescriptionMaxLens),
|
|
578
683
|
};
|
|
579
684
|
const schema = param.schema;
|
|
580
|
-
if (allowMultipleParameters && schema) {
|
|
685
|
+
if (options.allowMultipleParameters && schema) {
|
|
581
686
|
Utils.updateParameterWithInputType(schema, parameter);
|
|
582
687
|
}
|
|
583
688
|
if (param.in !== "header" && param.in !== "cookie") {
|
|
584
689
|
if (param.required && (schema === null || schema === void 0 ? void 0 : schema.default) === undefined) {
|
|
690
|
+
parameter.isRequired = true;
|
|
585
691
|
requiredParams.push(parameter);
|
|
586
692
|
}
|
|
587
693
|
else {
|
|
@@ -595,19 +701,13 @@ class Utils {
|
|
|
595
701
|
const requestJson = requestBody.content["application/json"];
|
|
596
702
|
if (Object.keys(requestJson).length !== 0) {
|
|
597
703
|
const schema = requestJson.schema;
|
|
598
|
-
const [requiredP, optionalP] = Utils.generateParametersFromSchema(schema, "requestBody", allowMultipleParameters, requestBody.required);
|
|
704
|
+
const [requiredP, optionalP] = Utils.generateParametersFromSchema(schema, "requestBody", !!options.allowMultipleParameters, requestBody.required);
|
|
599
705
|
requiredParams.push(...requiredP);
|
|
600
706
|
optionalParams.push(...optionalP);
|
|
601
707
|
}
|
|
602
708
|
}
|
|
603
709
|
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
|
-
}
|
|
710
|
+
const parameters = [...requiredParams, ...optionalParams];
|
|
611
711
|
const command = {
|
|
612
712
|
context: ["compose"],
|
|
613
713
|
type: "query",
|
|
@@ -616,42 +716,39 @@ class Utils {
|
|
|
616
716
|
parameters: parameters,
|
|
617
717
|
description: ((_b = operationItem.description) !== null && _b !== void 0 ? _b : "").slice(0, ConstantString.CommandDescriptionMaxLens),
|
|
618
718
|
};
|
|
619
|
-
|
|
620
|
-
if (requiredParams.length === 0 && optionalParams.length > 1) {
|
|
621
|
-
warning = {
|
|
622
|
-
type: WarningType.OperationOnlyContainsOptionalParam,
|
|
623
|
-
content: Utils.format(ConstantString.OperationOnlyContainsOptionalParam, operationId),
|
|
624
|
-
data: operationId,
|
|
625
|
-
};
|
|
626
|
-
}
|
|
627
|
-
return [command, warning];
|
|
719
|
+
return command;
|
|
628
720
|
}
|
|
629
|
-
static
|
|
721
|
+
static listAPIs(spec, options) {
|
|
722
|
+
var _a;
|
|
630
723
|
const paths = spec.paths;
|
|
631
724
|
const result = {};
|
|
632
725
|
for (const path in paths) {
|
|
633
726
|
const methods = paths[path];
|
|
634
727
|
for (const method in methods) {
|
|
635
|
-
|
|
636
|
-
if (
|
|
637
|
-
const
|
|
638
|
-
result[`${method.toUpperCase()} ${path}`] =
|
|
728
|
+
const operationObject = methods[method];
|
|
729
|
+
if (((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && operationObject) {
|
|
730
|
+
const validateResult = Utils.isSupportedApi(method, path, spec, options);
|
|
731
|
+
result[`${method.toUpperCase()} ${path}`] = {
|
|
732
|
+
operation: operationObject,
|
|
733
|
+
isValid: validateResult.isValid,
|
|
734
|
+
reason: validateResult.reason,
|
|
735
|
+
};
|
|
639
736
|
}
|
|
640
737
|
}
|
|
641
738
|
}
|
|
642
739
|
return result;
|
|
643
740
|
}
|
|
644
|
-
static validateSpec(spec, parser, isSwaggerFile,
|
|
741
|
+
static validateSpec(spec, parser, isSwaggerFile, options) {
|
|
645
742
|
const errors = [];
|
|
646
743
|
const warnings = [];
|
|
744
|
+
const apiMap = Utils.listAPIs(spec, options);
|
|
647
745
|
if (isSwaggerFile) {
|
|
648
746
|
warnings.push({
|
|
649
747
|
type: WarningType.ConvertSwaggerToOpenAPI,
|
|
650
748
|
content: ConstantString.ConvertSwaggerToOpenAPI,
|
|
651
749
|
});
|
|
652
750
|
}
|
|
653
|
-
|
|
654
|
-
const serverErrors = Utils.validateServer(spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2);
|
|
751
|
+
const serverErrors = Utils.validateServer(spec, options);
|
|
655
752
|
errors.push(...serverErrors);
|
|
656
753
|
// Remote reference not supported
|
|
657
754
|
const refPaths = parser.$refs.paths();
|
|
@@ -664,8 +761,8 @@ class Utils {
|
|
|
664
761
|
});
|
|
665
762
|
}
|
|
666
763
|
// No supported API
|
|
667
|
-
const
|
|
668
|
-
if (
|
|
764
|
+
const validAPIs = Object.entries(apiMap).filter(([, value]) => value.isValid);
|
|
765
|
+
if (validAPIs.length === 0) {
|
|
669
766
|
errors.push({
|
|
670
767
|
type: ErrorType.NoSupportedApi,
|
|
671
768
|
content: ConstantString.NoSupportedApi,
|
|
@@ -674,8 +771,8 @@ class Utils {
|
|
|
674
771
|
// OperationId missing
|
|
675
772
|
const apisMissingOperationId = [];
|
|
676
773
|
for (const key in apiMap) {
|
|
677
|
-
const
|
|
678
|
-
if (!
|
|
774
|
+
const { operation } = apiMap[key];
|
|
775
|
+
if (!operation.operationId) {
|
|
679
776
|
apisMissingOperationId.push(key);
|
|
680
777
|
}
|
|
681
778
|
}
|
|
@@ -716,6 +813,19 @@ class Utils {
|
|
|
716
813
|
}
|
|
717
814
|
return safeRegistrationIdEnvName;
|
|
718
815
|
}
|
|
816
|
+
static getAllAPICount(spec) {
|
|
817
|
+
let count = 0;
|
|
818
|
+
const paths = spec.paths;
|
|
819
|
+
for (const path in paths) {
|
|
820
|
+
const methods = paths[path];
|
|
821
|
+
for (const method in methods) {
|
|
822
|
+
if (ConstantString.AllOperationMethods.includes(method)) {
|
|
823
|
+
count++;
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
return count;
|
|
828
|
+
}
|
|
719
829
|
}
|
|
720
830
|
|
|
721
831
|
// Copyright (c) Microsoft Corporation.
|
|
@@ -734,7 +844,10 @@ class SpecParser {
|
|
|
734
844
|
allowSwagger: false,
|
|
735
845
|
allowAPIKeyAuth: false,
|
|
736
846
|
allowMultipleParameters: false,
|
|
847
|
+
allowBearerTokenAuth: false,
|
|
737
848
|
allowOauth2: false,
|
|
849
|
+
allowMethods: ["get", "post"],
|
|
850
|
+
projectType: ProjectType.SME,
|
|
738
851
|
};
|
|
739
852
|
this.pathOrSpec = pathOrDoc;
|
|
740
853
|
this.parser = new SwaggerParser();
|
|
@@ -771,7 +884,7 @@ class SpecParser {
|
|
|
771
884
|
],
|
|
772
885
|
};
|
|
773
886
|
}
|
|
774
|
-
return Utils.validateSpec(this.spec, this.parser, !!this.isSwaggerFile, this.options
|
|
887
|
+
return Utils.validateSpec(this.spec, this.parser, !!this.isSwaggerFile, this.options);
|
|
775
888
|
}
|
|
776
889
|
catch (err) {
|
|
777
890
|
throw new SpecParserError(err.toString(), ErrorType.ValidateFailed);
|
|
@@ -790,7 +903,7 @@ class SpecParser {
|
|
|
790
903
|
if (!operationId) {
|
|
791
904
|
continue;
|
|
792
905
|
}
|
|
793
|
-
const
|
|
906
|
+
const command = Utils.parseApiInfo(pathObjectItem, this.options);
|
|
794
907
|
const apiInfo = {
|
|
795
908
|
method: method,
|
|
796
909
|
path: path,
|
|
@@ -799,9 +912,6 @@ class SpecParser {
|
|
|
799
912
|
parameters: command.parameters,
|
|
800
913
|
description: command.description,
|
|
801
914
|
};
|
|
802
|
-
if (warning) {
|
|
803
|
-
apiInfo.warning = warning;
|
|
804
|
-
}
|
|
805
915
|
apiInfos.push(apiInfo);
|
|
806
916
|
}
|
|
807
917
|
return apiInfos;
|
|
@@ -827,6 +937,17 @@ class SpecParser {
|
|
|
827
937
|
async getFilteredSpecs(filter, signal) {
|
|
828
938
|
throw new Error("Method not implemented.");
|
|
829
939
|
}
|
|
940
|
+
/**
|
|
941
|
+
* Generates and update artifacts from the OpenAPI specification file. Generate Adaptive Cards, update Teams app manifest, and generate a new OpenAPI specification file.
|
|
942
|
+
* @param manifestPath A file path of the Teams app manifest file to update.
|
|
943
|
+
* @param filter An array of strings that represent the filters to apply when generating the artifacts. If filter is empty, it would process nothing.
|
|
944
|
+
* @param outputSpecPath File path of the new OpenAPI specification file to generate. If not specified or empty, no spec file will be generated.
|
|
945
|
+
* @param pluginFilePath File path of the api plugin file to generate.
|
|
946
|
+
*/
|
|
947
|
+
// eslint-disable-next-line @typescript-eslint/require-await
|
|
948
|
+
async generateForCopilot(manifestPath, filter, outputSpecPath, pluginFilePath, signal) {
|
|
949
|
+
throw new Error("Method not implemented.");
|
|
950
|
+
}
|
|
830
951
|
/**
|
|
831
952
|
* Generates and update artifacts from the OpenAPI specification file. Generate Adaptive Cards, update Teams app manifest, and generate a new OpenAPI specification file.
|
|
832
953
|
* @param manifestPath A file path of the Teams app manifest file to update.
|
|
@@ -836,7 +957,7 @@ class SpecParser {
|
|
|
836
957
|
* @param isMe Boolean that indicates whether the project is an Messaging Extension. For Messaging Extension, composeExtensions will be added in Teams app manifest.
|
|
837
958
|
*/
|
|
838
959
|
// eslint-disable-next-line @typescript-eslint/require-await
|
|
839
|
-
async generate(manifestPath, filter, outputSpecPath, adaptiveCardFolder, signal
|
|
960
|
+
async generate(manifestPath, filter, outputSpecPath, adaptiveCardFolder, signal) {
|
|
840
961
|
throw new Error("Method not implemented.");
|
|
841
962
|
}
|
|
842
963
|
async loadSpec() {
|
|
@@ -853,17 +974,35 @@ class SpecParser {
|
|
|
853
974
|
if (this.apiMap !== undefined) {
|
|
854
975
|
return this.apiMap;
|
|
855
976
|
}
|
|
856
|
-
const result =
|
|
977
|
+
const result = this.listSupportedAPIs(spec, this.options);
|
|
857
978
|
this.apiMap = result;
|
|
858
979
|
return result;
|
|
859
980
|
}
|
|
981
|
+
listSupportedAPIs(spec, options) {
|
|
982
|
+
var _a;
|
|
983
|
+
const paths = spec.paths;
|
|
984
|
+
const result = {};
|
|
985
|
+
for (const path in paths) {
|
|
986
|
+
const methods = paths[path];
|
|
987
|
+
for (const method in methods) {
|
|
988
|
+
const operationObject = methods[method];
|
|
989
|
+
if (((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && operationObject) {
|
|
990
|
+
const validateResult = Utils.isSupportedApi(method, path, spec, options);
|
|
991
|
+
if (validateResult.isValid) {
|
|
992
|
+
result[`${method.toUpperCase()} ${path}`] = operationObject;
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
return result;
|
|
998
|
+
}
|
|
860
999
|
}
|
|
861
1000
|
|
|
862
1001
|
// Copyright (c) Microsoft Corporation.
|
|
863
1002
|
class AdaptiveCardGenerator {
|
|
864
1003
|
static generateAdaptiveCard(operationItem) {
|
|
865
1004
|
try {
|
|
866
|
-
const json = Utils.getResponseJson(operationItem);
|
|
1005
|
+
const { json } = Utils.getResponseJson(operationItem);
|
|
867
1006
|
let cardBody = [];
|
|
868
1007
|
let schema = json.schema;
|
|
869
1008
|
let jsonPath = "$";
|