@microsoft/m365-spec-parser 0.1.1-alpha.78701ec6a.0 → 0.1.1-alpha.a277dba4e.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 +317 -67
- package/dist/index.esm2017.js.map +1 -1
- package/dist/index.esm2017.mjs +427 -150
- package/dist/index.esm2017.mjs.map +1 -1
- package/dist/index.esm5.js +321 -67
- package/dist/index.esm5.js.map +1 -1
- package/dist/index.node.cjs.js +433 -149
- package/dist/index.node.cjs.js.map +1 -1
- package/dist/src/constants.d.ts +5 -1
- package/dist/src/index.browser.d.ts +2 -1
- package/dist/src/index.d.ts +2 -1
- package/dist/src/interfaces.d.ts +47 -18
- package/dist/src/manifestUpdater.d.ts +8 -4
- package/dist/src/specFilter.d.ts +2 -1
- package/dist/src/specParser.browser.d.ts +15 -1
- package/dist/src/specParser.d.ts +15 -2
- package/dist/src/utils.d.ts +20 -16
- package/package.json +4 -4
package/dist/index.esm2017.mjs
CHANGED
|
@@ -19,7 +19,8 @@ var ErrorType;
|
|
|
19
19
|
ErrorType["NoExtraAPICanBeAdded"] = "no-extra-api-can-be-added";
|
|
20
20
|
ErrorType["ResolveServerUrlFailed"] = "resolve-server-url-failed";
|
|
21
21
|
ErrorType["SwaggerNotSupported"] = "swagger-not-supported";
|
|
22
|
-
ErrorType["
|
|
22
|
+
ErrorType["MultipleAuthNotSupported"] = "multiple-auth-not-supported";
|
|
23
|
+
ErrorType["SpecVersionNotSupported"] = "spec-version-not-supported";
|
|
23
24
|
ErrorType["ListFailed"] = "list-failed";
|
|
24
25
|
ErrorType["listSupportedAPIInfoFailed"] = "list-supported-api-info-failed";
|
|
25
26
|
ErrorType["FilterSpecFailed"] = "filter-spec-failed";
|
|
@@ -27,6 +28,7 @@ var ErrorType;
|
|
|
27
28
|
ErrorType["GenerateAdaptiveCardFailed"] = "generate-adaptive-card-failed";
|
|
28
29
|
ErrorType["GenerateFailed"] = "generate-failed";
|
|
29
30
|
ErrorType["ValidateFailed"] = "validate-failed";
|
|
31
|
+
ErrorType["GetSpecFailed"] = "get-spec-failed";
|
|
30
32
|
ErrorType["Cancelled"] = "cancelled";
|
|
31
33
|
ErrorType["Unknown"] = "unknown";
|
|
32
34
|
})(ErrorType || (ErrorType = {}));
|
|
@@ -49,7 +51,13 @@ var ValidationStatus;
|
|
|
49
51
|
ValidationStatus[ValidationStatus["Valid"] = 0] = "Valid";
|
|
50
52
|
ValidationStatus[ValidationStatus["Warning"] = 1] = "Warning";
|
|
51
53
|
ValidationStatus[ValidationStatus["Error"] = 2] = "Error";
|
|
52
|
-
})(ValidationStatus || (ValidationStatus = {}));
|
|
54
|
+
})(ValidationStatus || (ValidationStatus = {}));
|
|
55
|
+
var ProjectType;
|
|
56
|
+
(function (ProjectType) {
|
|
57
|
+
ProjectType[ProjectType["Copilot"] = 0] = "Copilot";
|
|
58
|
+
ProjectType[ProjectType["SME"] = 1] = "SME";
|
|
59
|
+
ProjectType[ProjectType["TeamsAi"] = 2] = "TeamsAi";
|
|
60
|
+
})(ProjectType || (ProjectType = {}));
|
|
53
61
|
|
|
54
62
|
// Copyright (c) Microsoft Corporation.
|
|
55
63
|
class ConstantString {
|
|
@@ -68,7 +76,9 @@ ConstantString.ResolveServerUrlFailed = "Unable to resolve the server URL: pleas
|
|
|
68
76
|
ConstantString.OperationOnlyContainsOptionalParam = "Operation %s contains multiple optional parameters. The first optional parameter is used for this command.";
|
|
69
77
|
ConstantString.ConvertSwaggerToOpenAPI = "The Swagger 2.0 file has been converted to OpenAPI 3.0.";
|
|
70
78
|
ConstantString.SwaggerNotSupported = "Swagger 2.0 is not supported. Please convert to OpenAPI 3.0 manually before proceeding.";
|
|
71
|
-
ConstantString.
|
|
79
|
+
ConstantString.SpecVersionNotSupported = "Unsupported OpenAPI version %s. Please use version 3.0.x.";
|
|
80
|
+
ConstantString.MultipleAuthNotSupported = "Multiple authentication methods are unsupported. Ensure all selected APIs use identical authentication.";
|
|
81
|
+
ConstantString.UnsupportedSchema = "Unsupported schema in %s %s: %s";
|
|
72
82
|
ConstantString.WrappedCardVersion = "devPreview";
|
|
73
83
|
ConstantString.WrappedCardSchema = "https://developer.microsoft.com/json-schemas/teams/vDevPreview/MicrosoftTeams.ResponseRenderingTemplate.schema.json";
|
|
74
84
|
ConstantString.WrappedCardResponseLayout = "list";
|
|
@@ -80,6 +90,7 @@ ConstantString.AdaptiveCardType = "AdaptiveCard";
|
|
|
80
90
|
ConstantString.TextBlockType = "TextBlock";
|
|
81
91
|
ConstantString.ContainerType = "Container";
|
|
82
92
|
ConstantString.RegistrationIdPostfix = "REGISTRATION_ID";
|
|
93
|
+
ConstantString.OAuthRegistrationIdPostFix = "OAUTH_REGISTRATION_ID";
|
|
83
94
|
ConstantString.ResponseCodeFor20X = [
|
|
84
95
|
"200",
|
|
85
96
|
"201",
|
|
@@ -139,7 +150,8 @@ ConstantString.FullDescriptionMaxLens = 4000;
|
|
|
139
150
|
ConstantString.CommandDescriptionMaxLens = 128;
|
|
140
151
|
ConstantString.ParameterDescriptionMaxLens = 128;
|
|
141
152
|
ConstantString.CommandTitleMaxLens = 32;
|
|
142
|
-
ConstantString.ParameterTitleMaxLens = 32;
|
|
153
|
+
ConstantString.ParameterTitleMaxLens = 32;
|
|
154
|
+
ConstantString.SMERequiredParamsMaxNum = 5;
|
|
143
155
|
|
|
144
156
|
// Copyright (c) Microsoft Corporation.
|
|
145
157
|
class SpecParserError extends Error {
|
|
@@ -151,7 +163,18 @@ class SpecParserError extends Error {
|
|
|
151
163
|
|
|
152
164
|
// Copyright (c) Microsoft Corporation.
|
|
153
165
|
class Utils {
|
|
154
|
-
static
|
|
166
|
+
static hasNestedObjectInSchema(schema) {
|
|
167
|
+
if (schema.type === "object") {
|
|
168
|
+
for (const property in schema.properties) {
|
|
169
|
+
const nestedSchema = schema.properties[property];
|
|
170
|
+
if (nestedSchema.type === "object") {
|
|
171
|
+
return true;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return false;
|
|
176
|
+
}
|
|
177
|
+
static checkParameters(paramObject, isCopilot) {
|
|
155
178
|
const paramResult = {
|
|
156
179
|
requiredNum: 0,
|
|
157
180
|
optionalNum: 0,
|
|
@@ -163,7 +186,20 @@ class Utils {
|
|
|
163
186
|
for (let i = 0; i < paramObject.length; i++) {
|
|
164
187
|
const param = paramObject[i];
|
|
165
188
|
const schema = param.schema;
|
|
189
|
+
if (isCopilot && this.hasNestedObjectInSchema(schema)) {
|
|
190
|
+
paramResult.isValid = false;
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
166
193
|
const isRequiredWithoutDefault = param.required && schema.default === undefined;
|
|
194
|
+
if (isCopilot) {
|
|
195
|
+
if (isRequiredWithoutDefault) {
|
|
196
|
+
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
197
|
+
}
|
|
198
|
+
else {
|
|
199
|
+
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
200
|
+
}
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
167
203
|
if (param.in === "header" || param.in === "cookie") {
|
|
168
204
|
if (isRequiredWithoutDefault) {
|
|
169
205
|
paramResult.isValid = false;
|
|
@@ -190,7 +226,7 @@ class Utils {
|
|
|
190
226
|
}
|
|
191
227
|
return paramResult;
|
|
192
228
|
}
|
|
193
|
-
static checkPostBody(schema, isRequired = false) {
|
|
229
|
+
static checkPostBody(schema, isRequired = false, isCopilot = false) {
|
|
194
230
|
var _a;
|
|
195
231
|
const paramResult = {
|
|
196
232
|
requiredNum: 0,
|
|
@@ -201,6 +237,10 @@ class Utils {
|
|
|
201
237
|
return paramResult;
|
|
202
238
|
}
|
|
203
239
|
const isRequiredWithoutDefault = isRequired && schema.default === undefined;
|
|
240
|
+
if (isCopilot && this.hasNestedObjectInSchema(schema)) {
|
|
241
|
+
paramResult.isValid = false;
|
|
242
|
+
return paramResult;
|
|
243
|
+
}
|
|
204
244
|
if (schema.type === "string" ||
|
|
205
245
|
schema.type === "integer" ||
|
|
206
246
|
schema.type === "boolean" ||
|
|
@@ -219,19 +259,22 @@ class Utils {
|
|
|
219
259
|
if (schema.required && ((_a = schema.required) === null || _a === void 0 ? void 0 : _a.indexOf(property)) >= 0) {
|
|
220
260
|
isRequired = true;
|
|
221
261
|
}
|
|
222
|
-
const result = Utils.checkPostBody(properties[property], isRequired);
|
|
262
|
+
const result = Utils.checkPostBody(properties[property], isRequired, isCopilot);
|
|
223
263
|
paramResult.requiredNum += result.requiredNum;
|
|
224
264
|
paramResult.optionalNum += result.optionalNum;
|
|
225
265
|
paramResult.isValid = paramResult.isValid && result.isValid;
|
|
226
266
|
}
|
|
227
267
|
}
|
|
228
268
|
else {
|
|
229
|
-
if (isRequiredWithoutDefault) {
|
|
269
|
+
if (isRequiredWithoutDefault && !isCopilot) {
|
|
230
270
|
paramResult.isValid = false;
|
|
231
271
|
}
|
|
232
272
|
}
|
|
233
273
|
return paramResult;
|
|
234
274
|
}
|
|
275
|
+
static containMultipleMediaTypes(bodyObject) {
|
|
276
|
+
return Object.keys((bodyObject === null || bodyObject === void 0 ? void 0 : bodyObject.content) || {}).length > 1;
|
|
277
|
+
}
|
|
235
278
|
/**
|
|
236
279
|
* Checks if the given API is supported.
|
|
237
280
|
* @param {string} method - The HTTP method of the API.
|
|
@@ -246,32 +289,40 @@ class Utils {
|
|
|
246
289
|
* 5. response body should be “application/json” and not empty, and response code should be 20X
|
|
247
290
|
* 6. only support request body with “application/json” content type
|
|
248
291
|
*/
|
|
249
|
-
static isSupportedApi(method, path, spec,
|
|
292
|
+
static isSupportedApi(method, path, spec, options) {
|
|
293
|
+
var _a;
|
|
250
294
|
const pathObj = spec.paths[path];
|
|
251
295
|
method = method.toLocaleLowerCase();
|
|
252
296
|
if (pathObj) {
|
|
253
|
-
if ((
|
|
254
|
-
pathObj[method]) {
|
|
297
|
+
if (((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && pathObj[method]) {
|
|
255
298
|
const securities = pathObj[method].security;
|
|
256
|
-
const
|
|
257
|
-
|
|
258
|
-
|
|
299
|
+
const isTeamsAi = options.projectType === ProjectType.TeamsAi;
|
|
300
|
+
const isCopilot = options.projectType === ProjectType.Copilot;
|
|
301
|
+
// Teams AI project doesn't care about auth, it will use authProvider for user to implement
|
|
302
|
+
if (!isTeamsAi) {
|
|
303
|
+
const authArray = Utils.getAuthArray(securities, spec);
|
|
304
|
+
if (!Utils.isSupportedAuth(authArray, options)) {
|
|
305
|
+
return false;
|
|
306
|
+
}
|
|
259
307
|
}
|
|
260
308
|
const operationObject = pathObj[method];
|
|
261
|
-
if (!allowMissingId && !operationObject.operationId) {
|
|
309
|
+
if (!options.allowMissingId && !operationObject.operationId) {
|
|
262
310
|
return false;
|
|
263
311
|
}
|
|
264
312
|
const paramObject = operationObject.parameters;
|
|
265
313
|
const requestBody = operationObject.requestBody;
|
|
266
314
|
const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
|
|
267
|
-
|
|
268
|
-
if (mediaTypesCount > 1) {
|
|
315
|
+
if (!isTeamsAi && Utils.containMultipleMediaTypes(requestBody)) {
|
|
269
316
|
return false;
|
|
270
317
|
}
|
|
271
|
-
const responseJson = Utils.getResponseJson(operationObject);
|
|
318
|
+
const responseJson = Utils.getResponseJson(operationObject, isTeamsAi);
|
|
272
319
|
if (Object.keys(responseJson).length === 0) {
|
|
273
320
|
return false;
|
|
274
321
|
}
|
|
322
|
+
// Teams AI project doesn't care about request parameters/body
|
|
323
|
+
if (isTeamsAi) {
|
|
324
|
+
return true;
|
|
325
|
+
}
|
|
275
326
|
let requestBodyParamResult = {
|
|
276
327
|
requiredNum: 0,
|
|
277
328
|
optionalNum: 0,
|
|
@@ -279,18 +330,26 @@ class Utils {
|
|
|
279
330
|
};
|
|
280
331
|
if (requestJsonBody) {
|
|
281
332
|
const requestBodySchema = requestJsonBody.schema;
|
|
282
|
-
|
|
333
|
+
if (isCopilot && requestBodySchema.type !== "object") {
|
|
334
|
+
return false;
|
|
335
|
+
}
|
|
336
|
+
requestBodyParamResult = Utils.checkPostBody(requestBodySchema, requestBody.required, isCopilot);
|
|
283
337
|
}
|
|
284
338
|
if (!requestBodyParamResult.isValid) {
|
|
285
339
|
return false;
|
|
286
340
|
}
|
|
287
|
-
const paramResult = Utils.checkParameters(paramObject);
|
|
341
|
+
const paramResult = Utils.checkParameters(paramObject, isCopilot);
|
|
288
342
|
if (!paramResult.isValid) {
|
|
289
343
|
return false;
|
|
290
344
|
}
|
|
345
|
+
// Copilot support arbitrary parameters
|
|
346
|
+
if (isCopilot) {
|
|
347
|
+
return true;
|
|
348
|
+
}
|
|
291
349
|
if (requestBodyParamResult.requiredNum + paramResult.requiredNum > 1) {
|
|
292
|
-
if (allowMultipleParameters &&
|
|
293
|
-
requestBodyParamResult.requiredNum + paramResult.requiredNum <=
|
|
350
|
+
if (options.allowMultipleParameters &&
|
|
351
|
+
requestBodyParamResult.requiredNum + paramResult.requiredNum <=
|
|
352
|
+
ConstantString.SMERequiredParamsMaxNum) {
|
|
294
353
|
return true;
|
|
295
354
|
}
|
|
296
355
|
return false;
|
|
@@ -309,29 +368,20 @@ class Utils {
|
|
|
309
368
|
}
|
|
310
369
|
return false;
|
|
311
370
|
}
|
|
312
|
-
static isSupportedAuth(
|
|
313
|
-
if (
|
|
371
|
+
static isSupportedAuth(authSchemeArray, options) {
|
|
372
|
+
if (authSchemeArray.length === 0) {
|
|
314
373
|
return true;
|
|
315
374
|
}
|
|
316
|
-
if (allowAPIKeyAuth || allowOauth2) {
|
|
375
|
+
if (options.allowAPIKeyAuth || options.allowOauth2 || options.allowBearerTokenAuth) {
|
|
317
376
|
// Currently we don't support multiple auth in one operation
|
|
318
|
-
if (
|
|
377
|
+
if (authSchemeArray.length > 0 && authSchemeArray.every((auths) => auths.length > 1)) {
|
|
319
378
|
return false;
|
|
320
379
|
}
|
|
321
|
-
for (const auths of
|
|
380
|
+
for (const auths of authSchemeArray) {
|
|
322
381
|
if (auths.length === 1) {
|
|
323
|
-
if (
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
else if (!allowAPIKeyAuth &&
|
|
327
|
-
allowOauth2 &&
|
|
328
|
-
Utils.isBearerTokenAuth(auths[0].authSchema)) {
|
|
329
|
-
return true;
|
|
330
|
-
}
|
|
331
|
-
else if (allowAPIKeyAuth &&
|
|
332
|
-
allowOauth2 &&
|
|
333
|
-
(Utils.isAPIKeyAuth(auths[0].authSchema) ||
|
|
334
|
-
Utils.isBearerTokenAuth(auths[0].authSchema))) {
|
|
382
|
+
if ((options.allowAPIKeyAuth && Utils.isAPIKeyAuth(auths[0].authScheme)) ||
|
|
383
|
+
(options.allowOauth2 && Utils.isOAuthWithAuthCodeFlow(auths[0].authScheme)) ||
|
|
384
|
+
(options.allowBearerTokenAuth && Utils.isBearerTokenAuth(auths[0].authScheme))) {
|
|
335
385
|
return true;
|
|
336
386
|
}
|
|
337
387
|
}
|
|
@@ -339,13 +389,17 @@ class Utils {
|
|
|
339
389
|
}
|
|
340
390
|
return false;
|
|
341
391
|
}
|
|
342
|
-
static
|
|
343
|
-
return
|
|
392
|
+
static isBearerTokenAuth(authScheme) {
|
|
393
|
+
return authScheme.type === "http" && authScheme.scheme === "bearer";
|
|
344
394
|
}
|
|
345
|
-
static
|
|
346
|
-
return
|
|
347
|
-
|
|
348
|
-
|
|
395
|
+
static isAPIKeyAuth(authScheme) {
|
|
396
|
+
return authScheme.type === "apiKey";
|
|
397
|
+
}
|
|
398
|
+
static isOAuthWithAuthCodeFlow(authScheme) {
|
|
399
|
+
if (authScheme.type === "oauth2" && authScheme.flows && authScheme.flows.authorizationCode) {
|
|
400
|
+
return true;
|
|
401
|
+
}
|
|
402
|
+
return false;
|
|
349
403
|
}
|
|
350
404
|
static getAuthArray(securities, spec) {
|
|
351
405
|
var _a;
|
|
@@ -358,7 +412,7 @@ class Utils {
|
|
|
358
412
|
for (const name in security) {
|
|
359
413
|
const auth = securitySchemas[name];
|
|
360
414
|
authArray.push({
|
|
361
|
-
|
|
415
|
+
authScheme: auth,
|
|
362
416
|
name: name,
|
|
363
417
|
});
|
|
364
418
|
}
|
|
@@ -373,18 +427,19 @@ class Utils {
|
|
|
373
427
|
static updateFirstLetter(str) {
|
|
374
428
|
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
375
429
|
}
|
|
376
|
-
static getResponseJson(operationObject) {
|
|
430
|
+
static getResponseJson(operationObject, isTeamsAiProject = false) {
|
|
377
431
|
var _a, _b;
|
|
378
432
|
let json = {};
|
|
379
433
|
for (const code of ConstantString.ResponseCodeFor20X) {
|
|
380
434
|
const responseObject = (_a = operationObject === null || operationObject === void 0 ? void 0 : operationObject.responses) === null || _a === void 0 ? void 0 : _a[code];
|
|
381
|
-
const mediaTypesCount = Object.keys((responseObject === null || responseObject === void 0 ? void 0 : responseObject.content) || {}).length;
|
|
382
|
-
if (mediaTypesCount > 1) {
|
|
383
|
-
return {};
|
|
384
|
-
}
|
|
385
435
|
if ((_b = responseObject === null || responseObject === void 0 ? void 0 : responseObject.content) === null || _b === void 0 ? void 0 : _b["application/json"]) {
|
|
386
436
|
json = responseObject.content["application/json"];
|
|
387
|
-
|
|
437
|
+
if (!isTeamsAiProject && Utils.containMultipleMediaTypes(responseObject)) {
|
|
438
|
+
json = {};
|
|
439
|
+
}
|
|
440
|
+
else {
|
|
441
|
+
break;
|
|
442
|
+
}
|
|
388
443
|
}
|
|
389
444
|
}
|
|
390
445
|
return json;
|
|
@@ -458,7 +513,7 @@ class Utils {
|
|
|
458
513
|
}
|
|
459
514
|
return errors;
|
|
460
515
|
}
|
|
461
|
-
static validateServer(spec,
|
|
516
|
+
static validateServer(spec, options) {
|
|
462
517
|
const errors = [];
|
|
463
518
|
let hasTopLevelServers = false;
|
|
464
519
|
let hasPathLevelServers = false;
|
|
@@ -479,7 +534,7 @@ class Utils {
|
|
|
479
534
|
}
|
|
480
535
|
for (const method in methods) {
|
|
481
536
|
const operationObject = methods[method];
|
|
482
|
-
if (Utils.isSupportedApi(method, path, spec,
|
|
537
|
+
if (Utils.isSupportedApi(method, path, spec, options)) {
|
|
483
538
|
if ((operationObject === null || operationObject === void 0 ? void 0 : operationObject.servers) && operationObject.servers.length >= 1) {
|
|
484
539
|
hasOperationLevelServers = true;
|
|
485
540
|
const serverErrors = Utils.checkServerUrl(operationObject.servers);
|
|
@@ -522,6 +577,7 @@ class Utils {
|
|
|
522
577
|
Utils.updateParameterWithInputType(schema, parameter);
|
|
523
578
|
}
|
|
524
579
|
if (isRequired && schema.default === undefined) {
|
|
580
|
+
parameter.isRequired = true;
|
|
525
581
|
requiredParams.push(parameter);
|
|
526
582
|
}
|
|
527
583
|
else {
|
|
@@ -566,7 +622,7 @@ class Utils {
|
|
|
566
622
|
param.value = schema.default;
|
|
567
623
|
}
|
|
568
624
|
}
|
|
569
|
-
static parseApiInfo(operationItem,
|
|
625
|
+
static parseApiInfo(operationItem, options) {
|
|
570
626
|
var _a, _b;
|
|
571
627
|
const requiredParams = [];
|
|
572
628
|
const optionalParams = [];
|
|
@@ -580,11 +636,12 @@ class Utils {
|
|
|
580
636
|
description: ((_a = param.description) !== null && _a !== void 0 ? _a : "").slice(0, ConstantString.ParameterDescriptionMaxLens),
|
|
581
637
|
};
|
|
582
638
|
const schema = param.schema;
|
|
583
|
-
if (allowMultipleParameters && schema) {
|
|
639
|
+
if (options.allowMultipleParameters && schema) {
|
|
584
640
|
Utils.updateParameterWithInputType(schema, parameter);
|
|
585
641
|
}
|
|
586
642
|
if (param.in !== "header" && param.in !== "cookie") {
|
|
587
643
|
if (param.required && (schema === null || schema === void 0 ? void 0 : schema.default) === undefined) {
|
|
644
|
+
parameter.isRequired = true;
|
|
588
645
|
requiredParams.push(parameter);
|
|
589
646
|
}
|
|
590
647
|
else {
|
|
@@ -598,7 +655,7 @@ class Utils {
|
|
|
598
655
|
const requestJson = requestBody.content["application/json"];
|
|
599
656
|
if (Object.keys(requestJson).length !== 0) {
|
|
600
657
|
const schema = requestJson.schema;
|
|
601
|
-
const [requiredP, optionalP] = Utils.generateParametersFromSchema(schema, "requestBody", allowMultipleParameters, requestBody.required);
|
|
658
|
+
const [requiredP, optionalP] = Utils.generateParametersFromSchema(schema, "requestBody", !!options.allowMultipleParameters, requestBody.required);
|
|
602
659
|
requiredParams.push(...requiredP);
|
|
603
660
|
optionalParams.push(...optionalP);
|
|
604
661
|
}
|
|
@@ -629,14 +686,13 @@ class Utils {
|
|
|
629
686
|
}
|
|
630
687
|
return [command, warning];
|
|
631
688
|
}
|
|
632
|
-
static listSupportedAPIs(spec,
|
|
689
|
+
static listSupportedAPIs(spec, options) {
|
|
633
690
|
const paths = spec.paths;
|
|
634
691
|
const result = {};
|
|
635
692
|
for (const path in paths) {
|
|
636
693
|
const methods = paths[path];
|
|
637
694
|
for (const method in methods) {
|
|
638
|
-
|
|
639
|
-
if (Utils.isSupportedApi(method, path, spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2)) {
|
|
695
|
+
if (Utils.isSupportedApi(method, path, spec, options)) {
|
|
640
696
|
const operationObject = methods[method];
|
|
641
697
|
result[`${method.toUpperCase()} ${path}`] = operationObject;
|
|
642
698
|
}
|
|
@@ -644,7 +700,7 @@ class Utils {
|
|
|
644
700
|
}
|
|
645
701
|
return result;
|
|
646
702
|
}
|
|
647
|
-
static validateSpec(spec, parser, isSwaggerFile,
|
|
703
|
+
static validateSpec(spec, parser, isSwaggerFile, options) {
|
|
648
704
|
const errors = [];
|
|
649
705
|
const warnings = [];
|
|
650
706
|
if (isSwaggerFile) {
|
|
@@ -654,7 +710,7 @@ class Utils {
|
|
|
654
710
|
});
|
|
655
711
|
}
|
|
656
712
|
// Server validation
|
|
657
|
-
const serverErrors = Utils.validateServer(spec,
|
|
713
|
+
const serverErrors = Utils.validateServer(spec, options);
|
|
658
714
|
errors.push(...serverErrors);
|
|
659
715
|
// Remote reference not supported
|
|
660
716
|
const refPaths = parser.$refs.paths();
|
|
@@ -667,7 +723,7 @@ class Utils {
|
|
|
667
723
|
});
|
|
668
724
|
}
|
|
669
725
|
// No supported API
|
|
670
|
-
const apiMap = Utils.listSupportedAPIs(spec,
|
|
726
|
+
const apiMap = Utils.listSupportedAPIs(spec, options);
|
|
671
727
|
if (Object.keys(apiMap).length === 0) {
|
|
672
728
|
errors.push({
|
|
673
729
|
type: ErrorType.NoSupportedApi,
|
|
@@ -719,18 +775,31 @@ class Utils {
|
|
|
719
775
|
}
|
|
720
776
|
return safeRegistrationIdEnvName;
|
|
721
777
|
}
|
|
778
|
+
static getAllAPICount(spec) {
|
|
779
|
+
let count = 0;
|
|
780
|
+
const paths = spec.paths;
|
|
781
|
+
for (const path in paths) {
|
|
782
|
+
const methods = paths[path];
|
|
783
|
+
for (const method in methods) {
|
|
784
|
+
if (ConstantString.AllOperationMethods.includes(method)) {
|
|
785
|
+
count++;
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
return count;
|
|
790
|
+
}
|
|
722
791
|
}
|
|
723
792
|
|
|
724
793
|
// Copyright (c) Microsoft Corporation.
|
|
725
794
|
class SpecFilter {
|
|
726
|
-
static specFilter(filter, unResolveSpec, resolvedSpec,
|
|
795
|
+
static specFilter(filter, unResolveSpec, resolvedSpec, options) {
|
|
727
796
|
try {
|
|
728
797
|
const newSpec = Object.assign({}, unResolveSpec);
|
|
729
798
|
const newPaths = {};
|
|
730
799
|
for (const filterItem of filter) {
|
|
731
800
|
const [method, path] = filterItem.split(" ");
|
|
732
801
|
const methodName = method.toLowerCase();
|
|
733
|
-
if (!Utils.isSupportedApi(methodName, path, resolvedSpec,
|
|
802
|
+
if (!Utils.isSupportedApi(methodName, path, resolvedSpec, options)) {
|
|
734
803
|
continue;
|
|
735
804
|
}
|
|
736
805
|
if (!newPaths[path]) {
|
|
@@ -756,46 +825,167 @@ class SpecFilter {
|
|
|
756
825
|
|
|
757
826
|
// Copyright (c) Microsoft Corporation.
|
|
758
827
|
class ManifestUpdater {
|
|
759
|
-
static async
|
|
828
|
+
static async updateManifestWithAiPlugin(manifestPath, outputSpecPath, apiPluginFilePath, spec, options) {
|
|
829
|
+
const manifest = await fs.readJSON(manifestPath);
|
|
830
|
+
const apiPluginRelativePath = ManifestUpdater.getRelativePath(manifestPath, apiPluginFilePath);
|
|
831
|
+
manifest.plugins = [
|
|
832
|
+
{
|
|
833
|
+
pluginFile: apiPluginRelativePath,
|
|
834
|
+
},
|
|
835
|
+
];
|
|
836
|
+
ManifestUpdater.updateManifestDescription(manifest, spec);
|
|
837
|
+
const specRelativePath = ManifestUpdater.getRelativePath(manifestPath, outputSpecPath);
|
|
838
|
+
const apiPlugin = ManifestUpdater.generatePluginManifestSchema(spec, specRelativePath, options);
|
|
839
|
+
return [manifest, apiPlugin];
|
|
840
|
+
}
|
|
841
|
+
static updateManifestDescription(manifest, spec) {
|
|
760
842
|
var _a, _b;
|
|
843
|
+
manifest.description = {
|
|
844
|
+
short: spec.info.title.slice(0, ConstantString.ShortDescriptionMaxLens),
|
|
845
|
+
full: (_b = ((_a = spec.info.description) !== null && _a !== void 0 ? _a : manifest.description.full)) === null || _b === void 0 ? void 0 : _b.slice(0, ConstantString.FullDescriptionMaxLens),
|
|
846
|
+
};
|
|
847
|
+
}
|
|
848
|
+
static mapOpenAPISchemaToFuncParam(schema, method, pathUrl) {
|
|
849
|
+
let parameter;
|
|
850
|
+
if (schema.type === "string" ||
|
|
851
|
+
schema.type === "boolean" ||
|
|
852
|
+
schema.type === "integer" ||
|
|
853
|
+
schema.type === "number" ||
|
|
854
|
+
schema.type === "array") {
|
|
855
|
+
parameter = schema;
|
|
856
|
+
}
|
|
857
|
+
else {
|
|
858
|
+
throw new SpecParserError(Utils.format(ConstantString.UnsupportedSchema, method, pathUrl, JSON.stringify(schema)), ErrorType.UpdateManifestFailed);
|
|
859
|
+
}
|
|
860
|
+
return parameter;
|
|
861
|
+
}
|
|
862
|
+
static generatePluginManifestSchema(spec, specRelativePath, options) {
|
|
863
|
+
var _a, _b, _c;
|
|
864
|
+
const functions = [];
|
|
865
|
+
const functionNames = [];
|
|
866
|
+
const paths = spec.paths;
|
|
867
|
+
for (const pathUrl in paths) {
|
|
868
|
+
const pathItem = paths[pathUrl];
|
|
869
|
+
if (pathItem) {
|
|
870
|
+
const operations = pathItem;
|
|
871
|
+
for (const method in operations) {
|
|
872
|
+
if (options.allowMethods.includes(method)) {
|
|
873
|
+
const operationItem = operations[method];
|
|
874
|
+
if (operationItem) {
|
|
875
|
+
const operationId = operationItem.operationId;
|
|
876
|
+
const description = (_a = operationItem.description) !== null && _a !== void 0 ? _a : "";
|
|
877
|
+
const paramObject = operationItem.parameters;
|
|
878
|
+
const requestBody = operationItem.requestBody;
|
|
879
|
+
const parameters = {
|
|
880
|
+
type: "object",
|
|
881
|
+
properties: {},
|
|
882
|
+
required: [],
|
|
883
|
+
};
|
|
884
|
+
if (paramObject) {
|
|
885
|
+
for (let i = 0; i < paramObject.length; i++) {
|
|
886
|
+
const param = paramObject[i];
|
|
887
|
+
const schema = param.schema;
|
|
888
|
+
parameters.properties[param.name] = ManifestUpdater.mapOpenAPISchemaToFuncParam(schema, method, pathUrl);
|
|
889
|
+
if (param.required) {
|
|
890
|
+
parameters.required.push(param.name);
|
|
891
|
+
}
|
|
892
|
+
if (!parameters.properties[param.name].description) {
|
|
893
|
+
parameters.properties[param.name].description = (_b = param.description) !== null && _b !== void 0 ? _b : "";
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
if (requestBody) {
|
|
898
|
+
const requestJsonBody = requestBody.content["application/json"];
|
|
899
|
+
const requestBodySchema = requestJsonBody.schema;
|
|
900
|
+
if (requestBodySchema.type === "object") {
|
|
901
|
+
if (requestBodySchema.required) {
|
|
902
|
+
parameters.required.push(...requestBodySchema.required);
|
|
903
|
+
}
|
|
904
|
+
for (const property in requestBodySchema.properties) {
|
|
905
|
+
const schema = requestBodySchema.properties[property];
|
|
906
|
+
parameters.properties[property] = ManifestUpdater.mapOpenAPISchemaToFuncParam(schema, method, pathUrl);
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
else {
|
|
910
|
+
throw new SpecParserError(Utils.format(ConstantString.UnsupportedSchema, method, pathUrl, JSON.stringify(requestBodySchema)), ErrorType.UpdateManifestFailed);
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
const funcObj = {
|
|
914
|
+
name: operationId,
|
|
915
|
+
description: description,
|
|
916
|
+
parameters: parameters,
|
|
917
|
+
};
|
|
918
|
+
functions.push(funcObj);
|
|
919
|
+
functionNames.push(operationId);
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
const apiPlugin = {
|
|
926
|
+
schema_version: "v2",
|
|
927
|
+
name_for_human: spec.info.title,
|
|
928
|
+
description_for_human: (_c = spec.info.description) !== null && _c !== void 0 ? _c : "<Please add description of the plugin>",
|
|
929
|
+
functions: functions,
|
|
930
|
+
runtimes: [
|
|
931
|
+
{
|
|
932
|
+
type: "OpenApi",
|
|
933
|
+
auth: {
|
|
934
|
+
type: "none", // TODO, support auth in the future
|
|
935
|
+
},
|
|
936
|
+
spec: {
|
|
937
|
+
url: specRelativePath,
|
|
938
|
+
},
|
|
939
|
+
run_for_functions: functionNames,
|
|
940
|
+
},
|
|
941
|
+
],
|
|
942
|
+
};
|
|
943
|
+
return apiPlugin;
|
|
944
|
+
}
|
|
945
|
+
static async updateManifest(manifestPath, outputSpecPath, spec, options, adaptiveCardFolder, authInfo) {
|
|
761
946
|
try {
|
|
762
947
|
const originalManifest = await fs.readJSON(manifestPath);
|
|
763
948
|
const updatedPart = {};
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
commands
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
}
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
949
|
+
updatedPart.composeExtensions = [];
|
|
950
|
+
let warnings = [];
|
|
951
|
+
if (options.projectType === ProjectType.SME) {
|
|
952
|
+
const updateResult = await ManifestUpdater.generateCommands(spec, manifestPath, options, adaptiveCardFolder);
|
|
953
|
+
const commands = updateResult[0];
|
|
954
|
+
warnings = updateResult[1];
|
|
955
|
+
const composeExtension = {
|
|
956
|
+
composeExtensionType: "apiBased",
|
|
957
|
+
apiSpecificationFile: ManifestUpdater.getRelativePath(manifestPath, outputSpecPath),
|
|
958
|
+
commands: commands,
|
|
959
|
+
};
|
|
960
|
+
if (authInfo) {
|
|
961
|
+
const auth = authInfo.authScheme;
|
|
962
|
+
if (Utils.isAPIKeyAuth(auth) || Utils.isBearerTokenAuth(auth)) {
|
|
963
|
+
const safeApiSecretRegistrationId = Utils.getSafeRegistrationIdEnvName(`${authInfo.name}_${ConstantString.RegistrationIdPostfix}`);
|
|
964
|
+
composeExtension.authorization = {
|
|
965
|
+
authType: "apiSecretServiceAuth",
|
|
966
|
+
apiSecretServiceAuthConfiguration: {
|
|
967
|
+
apiSecretRegistrationId: `\${{${safeApiSecretRegistrationId}}}`,
|
|
968
|
+
},
|
|
969
|
+
};
|
|
970
|
+
}
|
|
971
|
+
else if (Utils.isOAuthWithAuthCodeFlow(auth)) {
|
|
972
|
+
const safeOAuth2RegistrationId = Utils.getSafeRegistrationIdEnvName(`${authInfo.name}_${ConstantString.OAuthRegistrationIdPostFix}`);
|
|
973
|
+
composeExtension.authorization = {
|
|
974
|
+
authType: "oAuth2.0",
|
|
975
|
+
oAuthConfiguration: {
|
|
976
|
+
oauthConfigurationId: `\${{${safeOAuth2RegistrationId}}}`,
|
|
977
|
+
},
|
|
978
|
+
};
|
|
979
|
+
updatedPart.webApplicationInfo = {
|
|
980
|
+
id: "${{AAD_APP_CLIENT_ID}}",
|
|
981
|
+
resource: "api://${{DOMAIN}}/${{AAD_APP_CLIENT_ID}}",
|
|
982
|
+
};
|
|
983
|
+
}
|
|
792
984
|
}
|
|
985
|
+
updatedPart.composeExtensions = [composeExtension];
|
|
793
986
|
}
|
|
794
|
-
updatedPart.description =
|
|
795
|
-
|
|
796
|
-
full: (_b = ((_a = spec.info.description) !== null && _a !== void 0 ? _a : originalManifest.description.full)) === null || _b === void 0 ? void 0 : _b.slice(0, ConstantString.FullDescriptionMaxLens),
|
|
797
|
-
};
|
|
798
|
-
updatedPart.composeExtensions = [composeExtension];
|
|
987
|
+
updatedPart.description = originalManifest.description;
|
|
988
|
+
ManifestUpdater.updateManifestDescription(updatedPart, spec);
|
|
799
989
|
const updatedManifest = Object.assign(Object.assign({}, originalManifest), updatedPart);
|
|
800
990
|
return [updatedManifest, warnings];
|
|
801
991
|
}
|
|
@@ -803,7 +993,8 @@ class ManifestUpdater {
|
|
|
803
993
|
throw new SpecParserError(err.toString(), ErrorType.UpdateManifestFailed);
|
|
804
994
|
}
|
|
805
995
|
}
|
|
806
|
-
static async generateCommands(spec,
|
|
996
|
+
static async generateCommands(spec, manifestPath, options, adaptiveCardFolder) {
|
|
997
|
+
var _a;
|
|
807
998
|
const paths = spec.paths;
|
|
808
999
|
const commands = [];
|
|
809
1000
|
const warnings = [];
|
|
@@ -814,14 +1005,16 @@ class ManifestUpdater {
|
|
|
814
1005
|
const operations = pathItem;
|
|
815
1006
|
// Currently only support GET and POST method
|
|
816
1007
|
for (const method in operations) {
|
|
817
|
-
if (
|
|
1008
|
+
if ((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) {
|
|
818
1009
|
const operationItem = operations[method];
|
|
819
1010
|
if (operationItem) {
|
|
820
|
-
const [command, warning] = Utils.parseApiInfo(operationItem,
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
1011
|
+
const [command, warning] = Utils.parseApiInfo(operationItem, options);
|
|
1012
|
+
if (adaptiveCardFolder) {
|
|
1013
|
+
const adaptiveCardPath = path.join(adaptiveCardFolder, command.id + ".json");
|
|
1014
|
+
command.apiResponseRenderingTemplateFile = (await fs.pathExists(adaptiveCardPath))
|
|
1015
|
+
? ManifestUpdater.getRelativePath(manifestPath, adaptiveCardPath)
|
|
1016
|
+
: "";
|
|
1017
|
+
}
|
|
825
1018
|
if (warning) {
|
|
826
1019
|
warnings.push(warning);
|
|
827
1020
|
}
|
|
@@ -1104,8 +1297,11 @@ class SpecParser {
|
|
|
1104
1297
|
allowMissingId: true,
|
|
1105
1298
|
allowSwagger: true,
|
|
1106
1299
|
allowAPIKeyAuth: false,
|
|
1300
|
+
allowBearerTokenAuth: false,
|
|
1107
1301
|
allowMultipleParameters: false,
|
|
1108
1302
|
allowOauth2: false,
|
|
1303
|
+
allowMethods: ["get", "post"],
|
|
1304
|
+
projectType: ProjectType.SME,
|
|
1109
1305
|
};
|
|
1110
1306
|
this.pathOrSpec = pathOrDoc;
|
|
1111
1307
|
this.parser = new SwaggerParser();
|
|
@@ -1138,7 +1334,23 @@ class SpecParser {
|
|
|
1138
1334
|
],
|
|
1139
1335
|
};
|
|
1140
1336
|
}
|
|
1141
|
-
|
|
1337
|
+
if (this.options.projectType === ProjectType.SME ||
|
|
1338
|
+
this.options.projectType === ProjectType.Copilot) {
|
|
1339
|
+
if (this.spec.openapi >= "3.1.0") {
|
|
1340
|
+
return {
|
|
1341
|
+
status: ValidationStatus.Error,
|
|
1342
|
+
warnings: [],
|
|
1343
|
+
errors: [
|
|
1344
|
+
{
|
|
1345
|
+
type: ErrorType.SpecVersionNotSupported,
|
|
1346
|
+
content: Utils.format(ConstantString.SpecVersionNotSupported, this.spec.openapi),
|
|
1347
|
+
data: this.spec.openapi,
|
|
1348
|
+
},
|
|
1349
|
+
],
|
|
1350
|
+
};
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1353
|
+
return Utils.validateSpec(this.spec, this.parser, !!this.isSwaggerFile, this.options);
|
|
1142
1354
|
}
|
|
1143
1355
|
catch (err) {
|
|
1144
1356
|
throw new SpecParserError(err.toString(), ErrorType.ValidateFailed);
|
|
@@ -1159,7 +1371,11 @@ class SpecParser {
|
|
|
1159
1371
|
await this.loadSpec();
|
|
1160
1372
|
const spec = this.spec;
|
|
1161
1373
|
const apiMap = this.getAllSupportedAPIs(spec);
|
|
1162
|
-
const result =
|
|
1374
|
+
const result = {
|
|
1375
|
+
validAPIs: [],
|
|
1376
|
+
allAPICount: 0,
|
|
1377
|
+
validAPICount: 0,
|
|
1378
|
+
};
|
|
1163
1379
|
for (const apiKey in apiMap) {
|
|
1164
1380
|
const apiResult = {
|
|
1165
1381
|
api: "",
|
|
@@ -1184,13 +1400,15 @@ class SpecParser {
|
|
|
1184
1400
|
const authArray = Utils.getAuthArray(operation.security, spec);
|
|
1185
1401
|
for (const auths of authArray) {
|
|
1186
1402
|
if (auths.length === 1) {
|
|
1187
|
-
apiResult.auth = auths[0]
|
|
1403
|
+
apiResult.auth = auths[0];
|
|
1188
1404
|
break;
|
|
1189
1405
|
}
|
|
1190
1406
|
}
|
|
1191
1407
|
apiResult.api = apiKey;
|
|
1192
|
-
result.push(apiResult);
|
|
1408
|
+
result.validAPIs.push(apiResult);
|
|
1193
1409
|
}
|
|
1410
|
+
result.allAPICount = Utils.getAllAPICount(spec);
|
|
1411
|
+
result.validAPICount = result.validAPIs.length;
|
|
1194
1412
|
return result;
|
|
1195
1413
|
}
|
|
1196
1414
|
catch (err) {
|
|
@@ -1200,48 +1418,105 @@ class SpecParser {
|
|
|
1200
1418
|
throw new SpecParserError(err.toString(), ErrorType.ListFailed);
|
|
1201
1419
|
}
|
|
1202
1420
|
}
|
|
1421
|
+
/**
|
|
1422
|
+
* Generate specs according to the filters.
|
|
1423
|
+
* @param filter An array of strings that represent the filters to apply when generating the artifacts. If filter is empty, it would process nothing.
|
|
1424
|
+
*/
|
|
1425
|
+
async getFilteredSpecs(filter, signal) {
|
|
1426
|
+
try {
|
|
1427
|
+
if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
|
|
1428
|
+
throw new SpecParserError(ConstantString.CancelledMessage, ErrorType.Cancelled);
|
|
1429
|
+
}
|
|
1430
|
+
await this.loadSpec();
|
|
1431
|
+
if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
|
|
1432
|
+
throw new SpecParserError(ConstantString.CancelledMessage, ErrorType.Cancelled);
|
|
1433
|
+
}
|
|
1434
|
+
const newUnResolvedSpec = SpecFilter.specFilter(filter, this.unResolveSpec, this.spec, this.options);
|
|
1435
|
+
if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
|
|
1436
|
+
throw new SpecParserError(ConstantString.CancelledMessage, ErrorType.Cancelled);
|
|
1437
|
+
}
|
|
1438
|
+
const newSpec = (await this.parser.dereference(newUnResolvedSpec));
|
|
1439
|
+
return [newUnResolvedSpec, newSpec];
|
|
1440
|
+
}
|
|
1441
|
+
catch (err) {
|
|
1442
|
+
if (err instanceof SpecParserError) {
|
|
1443
|
+
throw err;
|
|
1444
|
+
}
|
|
1445
|
+
throw new SpecParserError(err.toString(), ErrorType.GetSpecFailed);
|
|
1446
|
+
}
|
|
1447
|
+
}
|
|
1203
1448
|
/**
|
|
1204
1449
|
* Generates and update artifacts from the OpenAPI specification file. Generate Adaptive Cards, update Teams app manifest, and generate a new OpenAPI specification file.
|
|
1205
1450
|
* @param manifestPath A file path of the Teams app manifest file to update.
|
|
1206
1451
|
* @param filter An array of strings that represent the filters to apply when generating the artifacts. If filter is empty, it would process nothing.
|
|
1207
1452
|
* @param outputSpecPath File path of the new OpenAPI specification file to generate. If not specified or empty, no spec file will be generated.
|
|
1208
|
-
* @param
|
|
1453
|
+
* @param pluginFilePath File path of the api plugin file to generate.
|
|
1209
1454
|
*/
|
|
1210
|
-
async
|
|
1455
|
+
async generateForCopilot(manifestPath, filter, outputSpecPath, pluginFilePath, signal) {
|
|
1211
1456
|
const result = {
|
|
1212
1457
|
allSuccess: true,
|
|
1213
1458
|
warnings: [],
|
|
1214
1459
|
};
|
|
1215
1460
|
try {
|
|
1216
|
-
|
|
1217
|
-
|
|
1461
|
+
const newSpecs = await this.getFilteredSpecs(filter, signal);
|
|
1462
|
+
const newUnResolvedSpec = newSpecs[0];
|
|
1463
|
+
const newSpec = newSpecs[1];
|
|
1464
|
+
let resultStr;
|
|
1465
|
+
if (outputSpecPath.endsWith(".yaml") || outputSpecPath.endsWith(".yml")) {
|
|
1466
|
+
resultStr = jsyaml.dump(newUnResolvedSpec);
|
|
1218
1467
|
}
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
throw new SpecParserError(ConstantString.CancelledMessage, ErrorType.Cancelled);
|
|
1468
|
+
else {
|
|
1469
|
+
resultStr = JSON.stringify(newUnResolvedSpec, null, 2);
|
|
1222
1470
|
}
|
|
1223
|
-
|
|
1471
|
+
await fs.outputFile(outputSpecPath, resultStr);
|
|
1224
1472
|
if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
|
|
1225
1473
|
throw new SpecParserError(ConstantString.CancelledMessage, ErrorType.Cancelled);
|
|
1226
1474
|
}
|
|
1227
|
-
const
|
|
1228
|
-
|
|
1229
|
-
|
|
1475
|
+
const [updatedManifest, apiPlugin] = await ManifestUpdater.updateManifestWithAiPlugin(manifestPath, outputSpecPath, pluginFilePath, newSpec, this.options);
|
|
1476
|
+
await fs.outputJSON(manifestPath, updatedManifest, { spaces: 2 });
|
|
1477
|
+
await fs.outputJSON(pluginFilePath, apiPlugin, { spaces: 2 });
|
|
1478
|
+
}
|
|
1479
|
+
catch (err) {
|
|
1480
|
+
if (err instanceof SpecParserError) {
|
|
1481
|
+
throw err;
|
|
1482
|
+
}
|
|
1483
|
+
throw new SpecParserError(err.toString(), ErrorType.GenerateFailed);
|
|
1484
|
+
}
|
|
1485
|
+
return result;
|
|
1486
|
+
}
|
|
1487
|
+
/**
|
|
1488
|
+
* Generates and update artifacts from the OpenAPI specification file. Generate Adaptive Cards, update Teams app manifest, and generate a new OpenAPI specification file.
|
|
1489
|
+
* @param manifestPath A file path of the Teams app manifest file to update.
|
|
1490
|
+
* @param filter An array of strings that represent the filters to apply when generating the artifacts. If filter is empty, it would process nothing.
|
|
1491
|
+
* @param outputSpecPath File path of the new OpenAPI specification file to generate. If not specified or empty, no spec file will be generated.
|
|
1492
|
+
* @param adaptiveCardFolder Folder path where the Adaptive Card files will be generated. If not specified or empty, Adaptive Card files will not be generated.
|
|
1493
|
+
*/
|
|
1494
|
+
async generate(manifestPath, filter, outputSpecPath, adaptiveCardFolder, signal) {
|
|
1495
|
+
const result = {
|
|
1496
|
+
allSuccess: true,
|
|
1497
|
+
warnings: [],
|
|
1498
|
+
};
|
|
1499
|
+
try {
|
|
1500
|
+
const newSpecs = await this.getFilteredSpecs(filter, signal);
|
|
1501
|
+
const newUnResolvedSpec = newSpecs[0];
|
|
1502
|
+
const newSpec = newSpecs[1];
|
|
1503
|
+
const authSet = new Set();
|
|
1504
|
+
let hasMultipleAuth = false;
|
|
1230
1505
|
for (const url in newSpec.paths) {
|
|
1231
1506
|
for (const method in newSpec.paths[url]) {
|
|
1232
1507
|
const operation = newSpec.paths[url][method];
|
|
1233
1508
|
const authArray = Utils.getAuthArray(operation.security, newSpec);
|
|
1234
1509
|
if (authArray && authArray.length > 0) {
|
|
1235
|
-
|
|
1236
|
-
if (
|
|
1237
|
-
|
|
1510
|
+
authSet.add(authArray[0][0]);
|
|
1511
|
+
if (authSet.size > 1) {
|
|
1512
|
+
hasMultipleAuth = true;
|
|
1238
1513
|
break;
|
|
1239
1514
|
}
|
|
1240
1515
|
}
|
|
1241
1516
|
}
|
|
1242
1517
|
}
|
|
1243
|
-
if (
|
|
1244
|
-
throw new SpecParserError(ConstantString.
|
|
1518
|
+
if (hasMultipleAuth && this.options.projectType !== ProjectType.TeamsAi) {
|
|
1519
|
+
throw new SpecParserError(ConstantString.MultipleAuthNotSupported, ErrorType.MultipleAuthNotSupported);
|
|
1245
1520
|
}
|
|
1246
1521
|
let resultStr;
|
|
1247
1522
|
if (outputSpecPath.endsWith(".yaml") || outputSpecPath.endsWith(".yml")) {
|
|
@@ -1251,26 +1526,28 @@ class SpecParser {
|
|
|
1251
1526
|
resultStr = JSON.stringify(newUnResolvedSpec, null, 2);
|
|
1252
1527
|
}
|
|
1253
1528
|
await fs.outputFile(outputSpecPath, resultStr);
|
|
1254
|
-
|
|
1255
|
-
for (const
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1529
|
+
if (adaptiveCardFolder) {
|
|
1530
|
+
for (const url in newSpec.paths) {
|
|
1531
|
+
for (const method in newSpec.paths[url]) {
|
|
1532
|
+
// paths object may contain description/summary which is not a http method, so we need to check if it is a operation object
|
|
1533
|
+
if (this.options.allowMethods.includes(method)) {
|
|
1534
|
+
const operation = newSpec.paths[url][method];
|
|
1535
|
+
try {
|
|
1536
|
+
const [card, jsonPath] = AdaptiveCardGenerator.generateAdaptiveCard(operation);
|
|
1537
|
+
const fileName = path.join(adaptiveCardFolder, `${operation.operationId}.json`);
|
|
1538
|
+
const wrappedCard = wrapAdaptiveCard(card, jsonPath);
|
|
1539
|
+
await fs.outputJSON(fileName, wrappedCard, { spaces: 2 });
|
|
1540
|
+
const dataFileName = path.join(adaptiveCardFolder, `${operation.operationId}.data.json`);
|
|
1541
|
+
await fs.outputJSON(dataFileName, {}, { spaces: 2 });
|
|
1542
|
+
}
|
|
1543
|
+
catch (err) {
|
|
1544
|
+
result.allSuccess = false;
|
|
1545
|
+
result.warnings.push({
|
|
1546
|
+
type: WarningType.GenerateCardFailed,
|
|
1547
|
+
content: err.toString(),
|
|
1548
|
+
data: operation.operationId,
|
|
1549
|
+
});
|
|
1550
|
+
}
|
|
1274
1551
|
}
|
|
1275
1552
|
}
|
|
1276
1553
|
}
|
|
@@ -1278,8 +1555,8 @@ class SpecParser {
|
|
|
1278
1555
|
if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
|
|
1279
1556
|
throw new SpecParserError(ConstantString.CancelledMessage, ErrorType.Cancelled);
|
|
1280
1557
|
}
|
|
1281
|
-
const
|
|
1282
|
-
const [updatedManifest, warnings] = await ManifestUpdater.updateManifest(manifestPath, outputSpecPath,
|
|
1558
|
+
const authInfo = Array.from(authSet)[0];
|
|
1559
|
+
const [updatedManifest, warnings] = await ManifestUpdater.updateManifest(manifestPath, outputSpecPath, newSpec, this.options, adaptiveCardFolder, authInfo);
|
|
1283
1560
|
await fs.outputJSON(manifestPath, updatedManifest, { spaces: 2 });
|
|
1284
1561
|
result.warnings.push(...warnings);
|
|
1285
1562
|
}
|
|
@@ -1308,11 +1585,11 @@ class SpecParser {
|
|
|
1308
1585
|
if (this.apiMap !== undefined) {
|
|
1309
1586
|
return this.apiMap;
|
|
1310
1587
|
}
|
|
1311
|
-
const result = Utils.listSupportedAPIs(spec, this.options
|
|
1588
|
+
const result = Utils.listSupportedAPIs(spec, this.options);
|
|
1312
1589
|
this.apiMap = result;
|
|
1313
1590
|
return result;
|
|
1314
1591
|
}
|
|
1315
1592
|
}
|
|
1316
1593
|
|
|
1317
|
-
export { ConstantString, ErrorType, SpecParser, SpecParserError, Utils, ValidationStatus, WarningType };
|
|
1594
|
+
export { AdaptiveCardGenerator, ConstantString, ErrorType, ProjectType, SpecParser, SpecParserError, Utils, ValidationStatus, WarningType };
|
|
1318
1595
|
//# sourceMappingURL=index.esm2017.mjs.map
|