@microsoft/m365-spec-parser 0.1.1-alpha.78701ec6a.0 → 0.1.1-alpha.7fe3da414.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 +449 -125
- package/dist/index.esm2017.js.map +1 -1
- package/dist/index.esm2017.mjs +579 -225
- package/dist/index.esm2017.mjs.map +1 -1
- package/dist/index.esm5.js +453 -125
- package/dist/index.esm5.js.map +1 -1
- package/dist/index.node.cjs.js +585 -224
- 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 +76 -18
- package/dist/src/manifestUpdater.d.ts +9 -4
- package/dist/src/specFilter.d.ts +2 -1
- package/dist/src/specParser.browser.d.ts +16 -1
- package/dist/src/specParser.d.ts +16 -3
- package/dist/src/utils.d.ts +24 -19
- 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,22 @@ 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";
|
|
32
|
+
ErrorType["AuthTypeIsNotSupported"] = "auth-type-is-not-supported";
|
|
33
|
+
ErrorType["MissingOperationId"] = "missing-operation-id";
|
|
34
|
+
ErrorType["PostBodyContainMultipleMediaTypes"] = "post-body-contain-multiple-media-types";
|
|
35
|
+
ErrorType["ResponseContainMultipleMediaTypes"] = "response-contain-multiple-media-types";
|
|
36
|
+
ErrorType["ResponseJsonIsEmpty"] = "response-json-is-empty";
|
|
37
|
+
ErrorType["PostBodySchemaIsNotJson"] = "post-body-schema-is-not-json";
|
|
38
|
+
ErrorType["PostBodyContainsRequiredUnsupportedSchema"] = "post-body-contains-required-unsupported-schema";
|
|
39
|
+
ErrorType["ParamsContainRequiredUnsupportedSchema"] = "params-contain-required-unsupported-schema";
|
|
40
|
+
ErrorType["ParamsContainsNestedObject"] = "params-contains-nested-object";
|
|
41
|
+
ErrorType["RequestBodyContainsNestedObject"] = "request-body-contains-nested-object";
|
|
42
|
+
ErrorType["ExceededRequiredParamsLimit"] = "exceeded-required-params-limit";
|
|
43
|
+
ErrorType["NoParameter"] = "no-parameter";
|
|
44
|
+
ErrorType["NoAPIInfo"] = "no-api-info";
|
|
45
|
+
ErrorType["MethodNotAllowed"] = "method-not-allowed";
|
|
46
|
+
ErrorType["UrlPathNotExist"] = "url-path-not-exist";
|
|
30
47
|
ErrorType["Cancelled"] = "cancelled";
|
|
31
48
|
ErrorType["Unknown"] = "unknown";
|
|
32
49
|
})(ErrorType || (ErrorType = {}));
|
|
@@ -49,7 +66,13 @@ var ValidationStatus;
|
|
|
49
66
|
ValidationStatus[ValidationStatus["Valid"] = 0] = "Valid";
|
|
50
67
|
ValidationStatus[ValidationStatus["Warning"] = 1] = "Warning";
|
|
51
68
|
ValidationStatus[ValidationStatus["Error"] = 2] = "Error";
|
|
52
|
-
})(ValidationStatus || (ValidationStatus = {}));
|
|
69
|
+
})(ValidationStatus || (ValidationStatus = {}));
|
|
70
|
+
var ProjectType;
|
|
71
|
+
(function (ProjectType) {
|
|
72
|
+
ProjectType[ProjectType["Copilot"] = 0] = "Copilot";
|
|
73
|
+
ProjectType[ProjectType["SME"] = 1] = "SME";
|
|
74
|
+
ProjectType[ProjectType["TeamsAi"] = 2] = "TeamsAi";
|
|
75
|
+
})(ProjectType || (ProjectType = {}));
|
|
53
76
|
|
|
54
77
|
// Copyright (c) Microsoft Corporation.
|
|
55
78
|
class ConstantString {
|
|
@@ -68,7 +91,9 @@ ConstantString.ResolveServerUrlFailed = "Unable to resolve the server URL: pleas
|
|
|
68
91
|
ConstantString.OperationOnlyContainsOptionalParam = "Operation %s contains multiple optional parameters. The first optional parameter is used for this command.";
|
|
69
92
|
ConstantString.ConvertSwaggerToOpenAPI = "The Swagger 2.0 file has been converted to OpenAPI 3.0.";
|
|
70
93
|
ConstantString.SwaggerNotSupported = "Swagger 2.0 is not supported. Please convert to OpenAPI 3.0 manually before proceeding.";
|
|
71
|
-
ConstantString.
|
|
94
|
+
ConstantString.SpecVersionNotSupported = "Unsupported OpenAPI version %s. Please use version 3.0.x.";
|
|
95
|
+
ConstantString.MultipleAuthNotSupported = "Multiple authentication methods are unsupported. Ensure all selected APIs use identical authentication.";
|
|
96
|
+
ConstantString.UnsupportedSchema = "Unsupported schema in %s %s: %s";
|
|
72
97
|
ConstantString.WrappedCardVersion = "devPreview";
|
|
73
98
|
ConstantString.WrappedCardSchema = "https://developer.microsoft.com/json-schemas/teams/vDevPreview/MicrosoftTeams.ResponseRenderingTemplate.schema.json";
|
|
74
99
|
ConstantString.WrappedCardResponseLayout = "list";
|
|
@@ -80,6 +105,7 @@ ConstantString.AdaptiveCardType = "AdaptiveCard";
|
|
|
80
105
|
ConstantString.TextBlockType = "TextBlock";
|
|
81
106
|
ConstantString.ContainerType = "Container";
|
|
82
107
|
ConstantString.RegistrationIdPostfix = "REGISTRATION_ID";
|
|
108
|
+
ConstantString.OAuthRegistrationIdPostFix = "OAUTH_REGISTRATION_ID";
|
|
83
109
|
ConstantString.ResponseCodeFor20X = [
|
|
84
110
|
"200",
|
|
85
111
|
"201",
|
|
@@ -139,7 +165,8 @@ ConstantString.FullDescriptionMaxLens = 4000;
|
|
|
139
165
|
ConstantString.CommandDescriptionMaxLens = 128;
|
|
140
166
|
ConstantString.ParameterDescriptionMaxLens = 128;
|
|
141
167
|
ConstantString.CommandTitleMaxLens = 32;
|
|
142
|
-
ConstantString.ParameterTitleMaxLens = 32;
|
|
168
|
+
ConstantString.ParameterTitleMaxLens = 32;
|
|
169
|
+
ConstantString.SMERequiredParamsMaxNum = 5;
|
|
143
170
|
|
|
144
171
|
// Copyright (c) Microsoft Corporation.
|
|
145
172
|
class SpecParserError extends Error {
|
|
@@ -151,11 +178,23 @@ class SpecParserError extends Error {
|
|
|
151
178
|
|
|
152
179
|
// Copyright (c) Microsoft Corporation.
|
|
153
180
|
class Utils {
|
|
154
|
-
static
|
|
181
|
+
static hasNestedObjectInSchema(schema) {
|
|
182
|
+
if (schema.type === "object") {
|
|
183
|
+
for (const property in schema.properties) {
|
|
184
|
+
const nestedSchema = schema.properties[property];
|
|
185
|
+
if (nestedSchema.type === "object") {
|
|
186
|
+
return true;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
return false;
|
|
191
|
+
}
|
|
192
|
+
static checkParameters(paramObject, isCopilot) {
|
|
155
193
|
const paramResult = {
|
|
156
194
|
requiredNum: 0,
|
|
157
195
|
optionalNum: 0,
|
|
158
196
|
isValid: true,
|
|
197
|
+
reason: [],
|
|
159
198
|
};
|
|
160
199
|
if (!paramObject) {
|
|
161
200
|
return paramResult;
|
|
@@ -163,10 +202,25 @@ class Utils {
|
|
|
163
202
|
for (let i = 0; i < paramObject.length; i++) {
|
|
164
203
|
const param = paramObject[i];
|
|
165
204
|
const schema = param.schema;
|
|
205
|
+
if (isCopilot && this.hasNestedObjectInSchema(schema)) {
|
|
206
|
+
paramResult.isValid = false;
|
|
207
|
+
paramResult.reason.push(ErrorType.ParamsContainsNestedObject);
|
|
208
|
+
continue;
|
|
209
|
+
}
|
|
166
210
|
const isRequiredWithoutDefault = param.required && schema.default === undefined;
|
|
211
|
+
if (isCopilot) {
|
|
212
|
+
if (isRequiredWithoutDefault) {
|
|
213
|
+
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
214
|
+
}
|
|
215
|
+
else {
|
|
216
|
+
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
217
|
+
}
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
167
220
|
if (param.in === "header" || param.in === "cookie") {
|
|
168
221
|
if (isRequiredWithoutDefault) {
|
|
169
222
|
paramResult.isValid = false;
|
|
223
|
+
paramResult.reason.push(ErrorType.ParamsContainRequiredUnsupportedSchema);
|
|
170
224
|
}
|
|
171
225
|
continue;
|
|
172
226
|
}
|
|
@@ -176,6 +230,7 @@ class Utils {
|
|
|
176
230
|
schema.type !== "integer") {
|
|
177
231
|
if (isRequiredWithoutDefault) {
|
|
178
232
|
paramResult.isValid = false;
|
|
233
|
+
paramResult.reason.push(ErrorType.ParamsContainRequiredUnsupportedSchema);
|
|
179
234
|
}
|
|
180
235
|
continue;
|
|
181
236
|
}
|
|
@@ -190,17 +245,23 @@ class Utils {
|
|
|
190
245
|
}
|
|
191
246
|
return paramResult;
|
|
192
247
|
}
|
|
193
|
-
static checkPostBody(schema, isRequired = false) {
|
|
248
|
+
static checkPostBody(schema, isRequired = false, isCopilot = false) {
|
|
194
249
|
var _a;
|
|
195
250
|
const paramResult = {
|
|
196
251
|
requiredNum: 0,
|
|
197
252
|
optionalNum: 0,
|
|
198
253
|
isValid: true,
|
|
254
|
+
reason: [],
|
|
199
255
|
};
|
|
200
256
|
if (Object.keys(schema).length === 0) {
|
|
201
257
|
return paramResult;
|
|
202
258
|
}
|
|
203
259
|
const isRequiredWithoutDefault = isRequired && schema.default === undefined;
|
|
260
|
+
if (isCopilot && this.hasNestedObjectInSchema(schema)) {
|
|
261
|
+
paramResult.isValid = false;
|
|
262
|
+
paramResult.reason = [ErrorType.RequestBodyContainsNestedObject];
|
|
263
|
+
return paramResult;
|
|
264
|
+
}
|
|
204
265
|
if (schema.type === "string" ||
|
|
205
266
|
schema.type === "integer" ||
|
|
206
267
|
schema.type === "boolean" ||
|
|
@@ -219,19 +280,24 @@ class Utils {
|
|
|
219
280
|
if (schema.required && ((_a = schema.required) === null || _a === void 0 ? void 0 : _a.indexOf(property)) >= 0) {
|
|
220
281
|
isRequired = true;
|
|
221
282
|
}
|
|
222
|
-
const result = Utils.checkPostBody(properties[property], isRequired);
|
|
283
|
+
const result = Utils.checkPostBody(properties[property], isRequired, isCopilot);
|
|
223
284
|
paramResult.requiredNum += result.requiredNum;
|
|
224
285
|
paramResult.optionalNum += result.optionalNum;
|
|
225
286
|
paramResult.isValid = paramResult.isValid && result.isValid;
|
|
287
|
+
paramResult.reason.push(...result.reason);
|
|
226
288
|
}
|
|
227
289
|
}
|
|
228
290
|
else {
|
|
229
|
-
if (isRequiredWithoutDefault) {
|
|
291
|
+
if (isRequiredWithoutDefault && !isCopilot) {
|
|
230
292
|
paramResult.isValid = false;
|
|
293
|
+
paramResult.reason.push(ErrorType.PostBodyContainsRequiredUnsupportedSchema);
|
|
231
294
|
}
|
|
232
295
|
}
|
|
233
296
|
return paramResult;
|
|
234
297
|
}
|
|
298
|
+
static containMultipleMediaTypes(bodyObject) {
|
|
299
|
+
return Object.keys((bodyObject === null || bodyObject === void 0 ? void 0 : bodyObject.content) || {}).length > 1;
|
|
300
|
+
}
|
|
235
301
|
/**
|
|
236
302
|
* Checks if the given API is supported.
|
|
237
303
|
* @param {string} method - The HTTP method of the API.
|
|
@@ -246,106 +312,137 @@ class Utils {
|
|
|
246
312
|
* 5. response body should be “application/json” and not empty, and response code should be 20X
|
|
247
313
|
* 6. only support request body with “application/json” content type
|
|
248
314
|
*/
|
|
249
|
-
static isSupportedApi(method, path, spec,
|
|
250
|
-
|
|
315
|
+
static isSupportedApi(method, path, spec, options) {
|
|
316
|
+
var _a;
|
|
317
|
+
const result = { isValid: true, reason: [] };
|
|
251
318
|
method = method.toLocaleLowerCase();
|
|
252
|
-
if (
|
|
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
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
319
|
+
if (options.allowMethods && !options.allowMethods.includes(method)) {
|
|
320
|
+
result.isValid = false;
|
|
321
|
+
result.reason.push(ErrorType.MethodNotAllowed);
|
|
322
|
+
return result;
|
|
323
|
+
}
|
|
324
|
+
const pathObj = spec.paths[path];
|
|
325
|
+
if (!pathObj || !pathObj[method]) {
|
|
326
|
+
result.isValid = false;
|
|
327
|
+
result.reason.push(ErrorType.UrlPathNotExist);
|
|
328
|
+
return result;
|
|
329
|
+
}
|
|
330
|
+
const securities = pathObj[method].security;
|
|
331
|
+
const isTeamsAi = options.projectType === ProjectType.TeamsAi;
|
|
332
|
+
const isCopilot = options.projectType === ProjectType.Copilot;
|
|
333
|
+
// Teams AI project doesn't care about auth, it will use authProvider for user to implement
|
|
334
|
+
if (!isTeamsAi) {
|
|
335
|
+
const authArray = Utils.getAuthArray(securities, spec);
|
|
336
|
+
const authCheckResult = Utils.isSupportedAuth(authArray, options);
|
|
337
|
+
if (!authCheckResult.isValid) {
|
|
338
|
+
result.reason.push(...authCheckResult.reason);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
const operationObject = pathObj[method];
|
|
342
|
+
if (!options.allowMissingId && !operationObject.operationId) {
|
|
343
|
+
result.reason.push(ErrorType.MissingOperationId);
|
|
344
|
+
}
|
|
345
|
+
const rootServer = spec.servers && spec.servers[0];
|
|
346
|
+
const methodServer = spec.paths[path].servers && ((_a = spec.paths[path]) === null || _a === void 0 ? void 0 : _a.servers[0]);
|
|
347
|
+
const operationServer = operationObject.servers && operationObject.servers[0];
|
|
348
|
+
const serverUrl = operationServer || methodServer || rootServer;
|
|
349
|
+
if (!serverUrl) {
|
|
350
|
+
result.reason.push(ErrorType.NoServerInformation);
|
|
351
|
+
}
|
|
352
|
+
else {
|
|
353
|
+
const serverValidateResult = Utils.checkServerUrl([serverUrl]);
|
|
354
|
+
result.reason.push(...serverValidateResult.map((item) => item.type));
|
|
355
|
+
}
|
|
356
|
+
const paramObject = operationObject.parameters;
|
|
357
|
+
const requestBody = operationObject.requestBody;
|
|
358
|
+
const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
|
|
359
|
+
if (!isTeamsAi && Utils.containMultipleMediaTypes(requestBody)) {
|
|
360
|
+
result.reason.push(ErrorType.PostBodyContainMultipleMediaTypes);
|
|
361
|
+
}
|
|
362
|
+
const { json, multipleMediaType } = Utils.getResponseJson(operationObject, isTeamsAi);
|
|
363
|
+
if (multipleMediaType && !isTeamsAi) {
|
|
364
|
+
result.reason.push(ErrorType.ResponseContainMultipleMediaTypes);
|
|
365
|
+
}
|
|
366
|
+
else if (Object.keys(json).length === 0) {
|
|
367
|
+
result.reason.push(ErrorType.ResponseJsonIsEmpty);
|
|
368
|
+
}
|
|
369
|
+
// Teams AI project doesn't care about request parameters/body
|
|
370
|
+
if (!isTeamsAi) {
|
|
371
|
+
let requestBodyParamResult = {
|
|
372
|
+
requiredNum: 0,
|
|
373
|
+
optionalNum: 0,
|
|
374
|
+
isValid: true,
|
|
375
|
+
reason: [],
|
|
376
|
+
};
|
|
377
|
+
if (requestJsonBody) {
|
|
378
|
+
const requestBodySchema = requestJsonBody.schema;
|
|
379
|
+
if (isCopilot && requestBodySchema.type !== "object") {
|
|
380
|
+
result.reason.push(ErrorType.PostBodySchemaIsNotJson);
|
|
286
381
|
}
|
|
287
|
-
|
|
288
|
-
if (!
|
|
289
|
-
|
|
382
|
+
requestBodyParamResult = Utils.checkPostBody(requestBodySchema, requestBody.required, isCopilot);
|
|
383
|
+
if (!requestBodyParamResult.isValid && requestBodyParamResult.reason) {
|
|
384
|
+
result.reason.push(...requestBodyParamResult.reason);
|
|
290
385
|
}
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
386
|
+
}
|
|
387
|
+
const paramResult = Utils.checkParameters(paramObject, isCopilot);
|
|
388
|
+
if (!paramResult.isValid && paramResult.reason) {
|
|
389
|
+
result.reason.push(...paramResult.reason);
|
|
390
|
+
}
|
|
391
|
+
// Copilot support arbitrary parameters
|
|
392
|
+
if (!isCopilot && paramResult.isValid && requestBodyParamResult.isValid) {
|
|
393
|
+
const totalRequiredParams = requestBodyParamResult.requiredNum + paramResult.requiredNum;
|
|
394
|
+
const totalParams = totalRequiredParams + requestBodyParamResult.optionalNum + paramResult.optionalNum;
|
|
395
|
+
if (totalRequiredParams > 1) {
|
|
396
|
+
if (!options.allowMultipleParameters ||
|
|
397
|
+
totalRequiredParams > ConstantString.SMERequiredParamsMaxNum) {
|
|
398
|
+
result.reason.push(ErrorType.ExceededRequiredParamsLimit);
|
|
295
399
|
}
|
|
296
|
-
return false;
|
|
297
|
-
}
|
|
298
|
-
else if (requestBodyParamResult.requiredNum +
|
|
299
|
-
requestBodyParamResult.optionalNum +
|
|
300
|
-
paramResult.requiredNum +
|
|
301
|
-
paramResult.optionalNum ===
|
|
302
|
-
0) {
|
|
303
|
-
return false;
|
|
304
400
|
}
|
|
305
|
-
else {
|
|
306
|
-
|
|
401
|
+
else if (totalParams === 0) {
|
|
402
|
+
result.reason.push(ErrorType.NoParameter);
|
|
307
403
|
}
|
|
308
404
|
}
|
|
309
405
|
}
|
|
310
|
-
|
|
406
|
+
if (result.reason.length > 0) {
|
|
407
|
+
result.isValid = false;
|
|
408
|
+
}
|
|
409
|
+
return result;
|
|
311
410
|
}
|
|
312
|
-
static isSupportedAuth(
|
|
313
|
-
if (
|
|
314
|
-
return true;
|
|
411
|
+
static isSupportedAuth(authSchemeArray, options) {
|
|
412
|
+
if (authSchemeArray.length === 0) {
|
|
413
|
+
return { isValid: true, reason: [] };
|
|
315
414
|
}
|
|
316
|
-
if (allowAPIKeyAuth || allowOauth2) {
|
|
415
|
+
if (options.allowAPIKeyAuth || options.allowOauth2 || options.allowBearerTokenAuth) {
|
|
317
416
|
// Currently we don't support multiple auth in one operation
|
|
318
|
-
if (
|
|
319
|
-
return
|
|
417
|
+
if (authSchemeArray.length > 0 && authSchemeArray.every((auths) => auths.length > 1)) {
|
|
418
|
+
return {
|
|
419
|
+
isValid: false,
|
|
420
|
+
reason: [ErrorType.MultipleAuthNotSupported],
|
|
421
|
+
};
|
|
320
422
|
}
|
|
321
|
-
for (const auths of
|
|
423
|
+
for (const auths of authSchemeArray) {
|
|
322
424
|
if (auths.length === 1) {
|
|
323
|
-
if (
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
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))) {
|
|
335
|
-
return true;
|
|
425
|
+
if ((options.allowAPIKeyAuth && Utils.isAPIKeyAuth(auths[0].authScheme)) ||
|
|
426
|
+
(options.allowOauth2 && Utils.isOAuthWithAuthCodeFlow(auths[0].authScheme)) ||
|
|
427
|
+
(options.allowBearerTokenAuth && Utils.isBearerTokenAuth(auths[0].authScheme))) {
|
|
428
|
+
return { isValid: true, reason: [] };
|
|
336
429
|
}
|
|
337
430
|
}
|
|
338
431
|
}
|
|
339
432
|
}
|
|
340
|
-
return false;
|
|
433
|
+
return { isValid: false, reason: [ErrorType.AuthTypeIsNotSupported] };
|
|
341
434
|
}
|
|
342
|
-
static
|
|
343
|
-
return
|
|
435
|
+
static isBearerTokenAuth(authScheme) {
|
|
436
|
+
return authScheme.type === "http" && authScheme.scheme === "bearer";
|
|
344
437
|
}
|
|
345
|
-
static
|
|
346
|
-
return
|
|
347
|
-
|
|
348
|
-
|
|
438
|
+
static isAPIKeyAuth(authScheme) {
|
|
439
|
+
return authScheme.type === "apiKey";
|
|
440
|
+
}
|
|
441
|
+
static isOAuthWithAuthCodeFlow(authScheme) {
|
|
442
|
+
if (authScheme.type === "oauth2" && authScheme.flows && authScheme.flows.authorizationCode) {
|
|
443
|
+
return true;
|
|
444
|
+
}
|
|
445
|
+
return false;
|
|
349
446
|
}
|
|
350
447
|
static getAuthArray(securities, spec) {
|
|
351
448
|
var _a;
|
|
@@ -358,7 +455,7 @@ class Utils {
|
|
|
358
455
|
for (const name in security) {
|
|
359
456
|
const auth = securitySchemas[name];
|
|
360
457
|
authArray.push({
|
|
361
|
-
|
|
458
|
+
authScheme: auth,
|
|
362
459
|
name: name,
|
|
363
460
|
});
|
|
364
461
|
}
|
|
@@ -373,21 +470,28 @@ class Utils {
|
|
|
373
470
|
static updateFirstLetter(str) {
|
|
374
471
|
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
375
472
|
}
|
|
376
|
-
static getResponseJson(operationObject) {
|
|
473
|
+
static getResponseJson(operationObject, isTeamsAiProject = false) {
|
|
377
474
|
var _a, _b;
|
|
378
475
|
let json = {};
|
|
476
|
+
let multipleMediaType = false;
|
|
379
477
|
for (const code of ConstantString.ResponseCodeFor20X) {
|
|
380
478
|
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
479
|
if ((_b = responseObject === null || responseObject === void 0 ? void 0 : responseObject.content) === null || _b === void 0 ? void 0 : _b["application/json"]) {
|
|
480
|
+
multipleMediaType = false;
|
|
386
481
|
json = responseObject.content["application/json"];
|
|
387
|
-
|
|
482
|
+
if (Utils.containMultipleMediaTypes(responseObject)) {
|
|
483
|
+
multipleMediaType = true;
|
|
484
|
+
if (isTeamsAiProject) {
|
|
485
|
+
break;
|
|
486
|
+
}
|
|
487
|
+
json = {};
|
|
488
|
+
}
|
|
489
|
+
else {
|
|
490
|
+
break;
|
|
491
|
+
}
|
|
388
492
|
}
|
|
389
493
|
}
|
|
390
|
-
return json;
|
|
494
|
+
return { json, multipleMediaType };
|
|
391
495
|
}
|
|
392
496
|
static convertPathToCamelCase(path) {
|
|
393
497
|
const pathSegments = path.split(/[./{]/);
|
|
@@ -407,10 +511,10 @@ class Utils {
|
|
|
407
511
|
return undefined;
|
|
408
512
|
}
|
|
409
513
|
}
|
|
410
|
-
static
|
|
514
|
+
static resolveEnv(str) {
|
|
411
515
|
const placeHolderReg = /\${{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*}}/g;
|
|
412
|
-
let matches = placeHolderReg.exec(
|
|
413
|
-
let
|
|
516
|
+
let matches = placeHolderReg.exec(str);
|
|
517
|
+
let newStr = str;
|
|
414
518
|
while (matches != null) {
|
|
415
519
|
const envVar = matches[1];
|
|
416
520
|
const envVal = process.env[envVar];
|
|
@@ -418,17 +522,17 @@ class Utils {
|
|
|
418
522
|
throw new Error(Utils.format(ConstantString.ResolveServerUrlFailed, envVar));
|
|
419
523
|
}
|
|
420
524
|
else {
|
|
421
|
-
|
|
525
|
+
newStr = newStr.replace(matches[0], envVal);
|
|
422
526
|
}
|
|
423
|
-
matches = placeHolderReg.exec(
|
|
527
|
+
matches = placeHolderReg.exec(str);
|
|
424
528
|
}
|
|
425
|
-
return
|
|
529
|
+
return newStr;
|
|
426
530
|
}
|
|
427
531
|
static checkServerUrl(servers) {
|
|
428
532
|
const errors = [];
|
|
429
533
|
let serverUrl;
|
|
430
534
|
try {
|
|
431
|
-
serverUrl = Utils.
|
|
535
|
+
serverUrl = Utils.resolveEnv(servers[0].url);
|
|
432
536
|
}
|
|
433
537
|
catch (err) {
|
|
434
538
|
errors.push({
|
|
@@ -458,7 +562,8 @@ class Utils {
|
|
|
458
562
|
}
|
|
459
563
|
return errors;
|
|
460
564
|
}
|
|
461
|
-
static validateServer(spec,
|
|
565
|
+
static validateServer(spec, options) {
|
|
566
|
+
var _a;
|
|
462
567
|
const errors = [];
|
|
463
568
|
let hasTopLevelServers = false;
|
|
464
569
|
let hasPathLevelServers = false;
|
|
@@ -479,7 +584,7 @@ class Utils {
|
|
|
479
584
|
}
|
|
480
585
|
for (const method in methods) {
|
|
481
586
|
const operationObject = methods[method];
|
|
482
|
-
if (
|
|
587
|
+
if (((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && operationObject) {
|
|
483
588
|
if ((operationObject === null || operationObject === void 0 ? void 0 : operationObject.servers) && operationObject.servers.length >= 1) {
|
|
484
589
|
hasOperationLevelServers = true;
|
|
485
590
|
const serverErrors = Utils.checkServerUrl(operationObject.servers);
|
|
@@ -522,6 +627,7 @@ class Utils {
|
|
|
522
627
|
Utils.updateParameterWithInputType(schema, parameter);
|
|
523
628
|
}
|
|
524
629
|
if (isRequired && schema.default === undefined) {
|
|
630
|
+
parameter.isRequired = true;
|
|
525
631
|
requiredParams.push(parameter);
|
|
526
632
|
}
|
|
527
633
|
else {
|
|
@@ -566,7 +672,7 @@ class Utils {
|
|
|
566
672
|
param.value = schema.default;
|
|
567
673
|
}
|
|
568
674
|
}
|
|
569
|
-
static parseApiInfo(operationItem,
|
|
675
|
+
static parseApiInfo(operationItem, options) {
|
|
570
676
|
var _a, _b;
|
|
571
677
|
const requiredParams = [];
|
|
572
678
|
const optionalParams = [];
|
|
@@ -580,11 +686,12 @@ class Utils {
|
|
|
580
686
|
description: ((_a = param.description) !== null && _a !== void 0 ? _a : "").slice(0, ConstantString.ParameterDescriptionMaxLens),
|
|
581
687
|
};
|
|
582
688
|
const schema = param.schema;
|
|
583
|
-
if (allowMultipleParameters && schema) {
|
|
689
|
+
if (options.allowMultipleParameters && schema) {
|
|
584
690
|
Utils.updateParameterWithInputType(schema, parameter);
|
|
585
691
|
}
|
|
586
692
|
if (param.in !== "header" && param.in !== "cookie") {
|
|
587
693
|
if (param.required && (schema === null || schema === void 0 ? void 0 : schema.default) === undefined) {
|
|
694
|
+
parameter.isRequired = true;
|
|
588
695
|
requiredParams.push(parameter);
|
|
589
696
|
}
|
|
590
697
|
else {
|
|
@@ -598,7 +705,7 @@ class Utils {
|
|
|
598
705
|
const requestJson = requestBody.content["application/json"];
|
|
599
706
|
if (Object.keys(requestJson).length !== 0) {
|
|
600
707
|
const schema = requestJson.schema;
|
|
601
|
-
const [requiredP, optionalP] = Utils.generateParametersFromSchema(schema, "requestBody", allowMultipleParameters, requestBody.required);
|
|
708
|
+
const [requiredP, optionalP] = Utils.generateParametersFromSchema(schema, "requestBody", !!options.allowMultipleParameters, requestBody.required);
|
|
602
709
|
requiredParams.push(...requiredP);
|
|
603
710
|
optionalParams.push(...optionalP);
|
|
604
711
|
}
|
|
@@ -629,32 +736,37 @@ class Utils {
|
|
|
629
736
|
}
|
|
630
737
|
return [command, warning];
|
|
631
738
|
}
|
|
632
|
-
static
|
|
739
|
+
static listAPIs(spec, options) {
|
|
740
|
+
var _a;
|
|
633
741
|
const paths = spec.paths;
|
|
634
742
|
const result = {};
|
|
635
743
|
for (const path in paths) {
|
|
636
744
|
const methods = paths[path];
|
|
637
745
|
for (const method in methods) {
|
|
638
|
-
|
|
639
|
-
if (
|
|
640
|
-
const
|
|
641
|
-
result[`${method.toUpperCase()} ${path}`] =
|
|
746
|
+
const operationObject = methods[method];
|
|
747
|
+
if (((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && operationObject) {
|
|
748
|
+
const validateResult = Utils.isSupportedApi(method, path, spec, options);
|
|
749
|
+
result[`${method.toUpperCase()} ${path}`] = {
|
|
750
|
+
operation: operationObject,
|
|
751
|
+
isValid: validateResult.isValid,
|
|
752
|
+
reason: validateResult.reason,
|
|
753
|
+
};
|
|
642
754
|
}
|
|
643
755
|
}
|
|
644
756
|
}
|
|
645
757
|
return result;
|
|
646
758
|
}
|
|
647
|
-
static validateSpec(spec, parser, isSwaggerFile,
|
|
759
|
+
static validateSpec(spec, parser, isSwaggerFile, options) {
|
|
648
760
|
const errors = [];
|
|
649
761
|
const warnings = [];
|
|
762
|
+
const apiMap = Utils.listAPIs(spec, options);
|
|
650
763
|
if (isSwaggerFile) {
|
|
651
764
|
warnings.push({
|
|
652
765
|
type: WarningType.ConvertSwaggerToOpenAPI,
|
|
653
766
|
content: ConstantString.ConvertSwaggerToOpenAPI,
|
|
654
767
|
});
|
|
655
768
|
}
|
|
656
|
-
|
|
657
|
-
const serverErrors = Utils.validateServer(spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2);
|
|
769
|
+
const serverErrors = Utils.validateServer(spec, options);
|
|
658
770
|
errors.push(...serverErrors);
|
|
659
771
|
// Remote reference not supported
|
|
660
772
|
const refPaths = parser.$refs.paths();
|
|
@@ -667,8 +779,8 @@ class Utils {
|
|
|
667
779
|
});
|
|
668
780
|
}
|
|
669
781
|
// No supported API
|
|
670
|
-
const
|
|
671
|
-
if (
|
|
782
|
+
const validAPIs = Object.entries(apiMap).filter(([, value]) => value.isValid);
|
|
783
|
+
if (validAPIs.length === 0) {
|
|
672
784
|
errors.push({
|
|
673
785
|
type: ErrorType.NoSupportedApi,
|
|
674
786
|
content: ConstantString.NoSupportedApi,
|
|
@@ -677,8 +789,8 @@ class Utils {
|
|
|
677
789
|
// OperationId missing
|
|
678
790
|
const apisMissingOperationId = [];
|
|
679
791
|
for (const key in apiMap) {
|
|
680
|
-
const
|
|
681
|
-
if (!
|
|
792
|
+
const { operation } = apiMap[key];
|
|
793
|
+
if (!operation.operationId) {
|
|
682
794
|
apisMissingOperationId.push(key);
|
|
683
795
|
}
|
|
684
796
|
}
|
|
@@ -719,30 +831,50 @@ class Utils {
|
|
|
719
831
|
}
|
|
720
832
|
return safeRegistrationIdEnvName;
|
|
721
833
|
}
|
|
834
|
+
static getAllAPICount(spec) {
|
|
835
|
+
let count = 0;
|
|
836
|
+
const paths = spec.paths;
|
|
837
|
+
for (const path in paths) {
|
|
838
|
+
const methods = paths[path];
|
|
839
|
+
for (const method in methods) {
|
|
840
|
+
if (ConstantString.AllOperationMethods.includes(method)) {
|
|
841
|
+
count++;
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
return count;
|
|
846
|
+
}
|
|
722
847
|
}
|
|
723
848
|
|
|
724
849
|
// Copyright (c) Microsoft Corporation.
|
|
725
850
|
class SpecFilter {
|
|
726
|
-
static specFilter(filter, unResolveSpec, resolvedSpec,
|
|
851
|
+
static specFilter(filter, unResolveSpec, resolvedSpec, options) {
|
|
852
|
+
var _a;
|
|
727
853
|
try {
|
|
728
854
|
const newSpec = Object.assign({}, unResolveSpec);
|
|
729
855
|
const newPaths = {};
|
|
730
856
|
for (const filterItem of filter) {
|
|
731
857
|
const [method, path] = filterItem.split(" ");
|
|
732
858
|
const methodName = method.toLowerCase();
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
859
|
+
const pathObj = (_a = resolvedSpec.paths) === null || _a === void 0 ? void 0 : _a[path];
|
|
860
|
+
if (ConstantString.AllOperationMethods.includes(methodName) &&
|
|
861
|
+
pathObj &&
|
|
862
|
+
pathObj[methodName]) {
|
|
863
|
+
const validateResult = Utils.isSupportedApi(methodName, path, resolvedSpec, options);
|
|
864
|
+
if (!validateResult.isValid) {
|
|
865
|
+
continue;
|
|
866
|
+
}
|
|
867
|
+
if (!newPaths[path]) {
|
|
868
|
+
newPaths[path] = Object.assign({}, unResolveSpec.paths[path]);
|
|
869
|
+
for (const m of ConstantString.AllOperationMethods) {
|
|
870
|
+
delete newPaths[path][m];
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
newPaths[path][methodName] = unResolveSpec.paths[path][methodName];
|
|
874
|
+
// Add the operationId if missing
|
|
875
|
+
if (!newPaths[path][methodName].operationId) {
|
|
876
|
+
newPaths[path][methodName].operationId = `${methodName}${Utils.convertPathToCamelCase(path)}`;
|
|
740
877
|
}
|
|
741
|
-
}
|
|
742
|
-
newPaths[path][methodName] = unResolveSpec.paths[path][methodName];
|
|
743
|
-
// Add the operationId if missing
|
|
744
|
-
if (!newPaths[path][methodName].operationId) {
|
|
745
|
-
newPaths[path][methodName].operationId = `${methodName}${Utils.convertPathToCamelCase(path)}`;
|
|
746
878
|
}
|
|
747
879
|
}
|
|
748
880
|
newSpec.paths = newPaths;
|
|
@@ -756,46 +888,168 @@ class SpecFilter {
|
|
|
756
888
|
|
|
757
889
|
// Copyright (c) Microsoft Corporation.
|
|
758
890
|
class ManifestUpdater {
|
|
759
|
-
static async
|
|
891
|
+
static async updateManifestWithAiPlugin(manifestPath, outputSpecPath, apiPluginFilePath, spec, options) {
|
|
892
|
+
const manifest = await fs.readJSON(manifestPath);
|
|
893
|
+
const apiPluginRelativePath = ManifestUpdater.getRelativePath(manifestPath, apiPluginFilePath);
|
|
894
|
+
manifest.plugins = [
|
|
895
|
+
{
|
|
896
|
+
pluginFile: apiPluginRelativePath,
|
|
897
|
+
},
|
|
898
|
+
];
|
|
899
|
+
const appName = this.removeEnvs(manifest.name.short);
|
|
900
|
+
ManifestUpdater.updateManifestDescription(manifest, spec);
|
|
901
|
+
const specRelativePath = ManifestUpdater.getRelativePath(manifestPath, outputSpecPath);
|
|
902
|
+
const apiPlugin = ManifestUpdater.generatePluginManifestSchema(spec, specRelativePath, appName, options);
|
|
903
|
+
return [manifest, apiPlugin];
|
|
904
|
+
}
|
|
905
|
+
static updateManifestDescription(manifest, spec) {
|
|
760
906
|
var _a, _b;
|
|
907
|
+
manifest.description = {
|
|
908
|
+
short: spec.info.title.slice(0, ConstantString.ShortDescriptionMaxLens),
|
|
909
|
+
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),
|
|
910
|
+
};
|
|
911
|
+
}
|
|
912
|
+
static mapOpenAPISchemaToFuncParam(schema, method, pathUrl) {
|
|
913
|
+
let parameter;
|
|
914
|
+
if (schema.type === "string" ||
|
|
915
|
+
schema.type === "boolean" ||
|
|
916
|
+
schema.type === "integer" ||
|
|
917
|
+
schema.type === "number" ||
|
|
918
|
+
schema.type === "array") {
|
|
919
|
+
parameter = schema;
|
|
920
|
+
}
|
|
921
|
+
else {
|
|
922
|
+
throw new SpecParserError(Utils.format(ConstantString.UnsupportedSchema, method, pathUrl, JSON.stringify(schema)), ErrorType.UpdateManifestFailed);
|
|
923
|
+
}
|
|
924
|
+
return parameter;
|
|
925
|
+
}
|
|
926
|
+
static generatePluginManifestSchema(spec, specRelativePath, appName, options) {
|
|
927
|
+
var _a, _b, _c;
|
|
928
|
+
const functions = [];
|
|
929
|
+
const functionNames = [];
|
|
930
|
+
const paths = spec.paths;
|
|
931
|
+
for (const pathUrl in paths) {
|
|
932
|
+
const pathItem = paths[pathUrl];
|
|
933
|
+
if (pathItem) {
|
|
934
|
+
const operations = pathItem;
|
|
935
|
+
for (const method in operations) {
|
|
936
|
+
if (options.allowMethods.includes(method)) {
|
|
937
|
+
const operationItem = operations[method];
|
|
938
|
+
if (operationItem) {
|
|
939
|
+
const operationId = operationItem.operationId;
|
|
940
|
+
const description = (_a = operationItem.description) !== null && _a !== void 0 ? _a : "";
|
|
941
|
+
const paramObject = operationItem.parameters;
|
|
942
|
+
const requestBody = operationItem.requestBody;
|
|
943
|
+
const parameters = {
|
|
944
|
+
type: "object",
|
|
945
|
+
properties: {},
|
|
946
|
+
required: [],
|
|
947
|
+
};
|
|
948
|
+
if (paramObject) {
|
|
949
|
+
for (let i = 0; i < paramObject.length; i++) {
|
|
950
|
+
const param = paramObject[i];
|
|
951
|
+
const schema = param.schema;
|
|
952
|
+
parameters.properties[param.name] = ManifestUpdater.mapOpenAPISchemaToFuncParam(schema, method, pathUrl);
|
|
953
|
+
if (param.required) {
|
|
954
|
+
parameters.required.push(param.name);
|
|
955
|
+
}
|
|
956
|
+
if (!parameters.properties[param.name].description) {
|
|
957
|
+
parameters.properties[param.name].description = (_b = param.description) !== null && _b !== void 0 ? _b : "";
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
if (requestBody) {
|
|
962
|
+
const requestJsonBody = requestBody.content["application/json"];
|
|
963
|
+
const requestBodySchema = requestJsonBody.schema;
|
|
964
|
+
if (requestBodySchema.type === "object") {
|
|
965
|
+
if (requestBodySchema.required) {
|
|
966
|
+
parameters.required.push(...requestBodySchema.required);
|
|
967
|
+
}
|
|
968
|
+
for (const property in requestBodySchema.properties) {
|
|
969
|
+
const schema = requestBodySchema.properties[property];
|
|
970
|
+
parameters.properties[property] = ManifestUpdater.mapOpenAPISchemaToFuncParam(schema, method, pathUrl);
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
else {
|
|
974
|
+
throw new SpecParserError(Utils.format(ConstantString.UnsupportedSchema, method, pathUrl, JSON.stringify(requestBodySchema)), ErrorType.UpdateManifestFailed);
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
const funcObj = {
|
|
978
|
+
name: operationId,
|
|
979
|
+
description: description,
|
|
980
|
+
parameters: parameters,
|
|
981
|
+
};
|
|
982
|
+
functions.push(funcObj);
|
|
983
|
+
functionNames.push(operationId);
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
const apiPlugin = {
|
|
990
|
+
schema_version: "v2",
|
|
991
|
+
name_for_human: appName,
|
|
992
|
+
description_for_human: (_c = spec.info.description) !== null && _c !== void 0 ? _c : "<Please add description of the plugin>",
|
|
993
|
+
functions: functions,
|
|
994
|
+
runtimes: [
|
|
995
|
+
{
|
|
996
|
+
type: "OpenApi",
|
|
997
|
+
auth: {
|
|
998
|
+
type: "none", // TODO, support auth in the future
|
|
999
|
+
},
|
|
1000
|
+
spec: {
|
|
1001
|
+
url: specRelativePath,
|
|
1002
|
+
},
|
|
1003
|
+
run_for_functions: functionNames,
|
|
1004
|
+
},
|
|
1005
|
+
],
|
|
1006
|
+
};
|
|
1007
|
+
return apiPlugin;
|
|
1008
|
+
}
|
|
1009
|
+
static async updateManifest(manifestPath, outputSpecPath, spec, options, adaptiveCardFolder, authInfo) {
|
|
761
1010
|
try {
|
|
762
1011
|
const originalManifest = await fs.readJSON(manifestPath);
|
|
763
1012
|
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
|
-
|
|
1013
|
+
updatedPart.composeExtensions = [];
|
|
1014
|
+
let warnings = [];
|
|
1015
|
+
if (options.projectType === ProjectType.SME) {
|
|
1016
|
+
const updateResult = await ManifestUpdater.generateCommands(spec, manifestPath, options, adaptiveCardFolder);
|
|
1017
|
+
const commands = updateResult[0];
|
|
1018
|
+
warnings = updateResult[1];
|
|
1019
|
+
const composeExtension = {
|
|
1020
|
+
composeExtensionType: "apiBased",
|
|
1021
|
+
apiSpecificationFile: ManifestUpdater.getRelativePath(manifestPath, outputSpecPath),
|
|
1022
|
+
commands: commands,
|
|
1023
|
+
};
|
|
1024
|
+
if (authInfo) {
|
|
1025
|
+
const auth = authInfo.authScheme;
|
|
1026
|
+
if (Utils.isAPIKeyAuth(auth) || Utils.isBearerTokenAuth(auth)) {
|
|
1027
|
+
const safeApiSecretRegistrationId = Utils.getSafeRegistrationIdEnvName(`${authInfo.name}_${ConstantString.RegistrationIdPostfix}`);
|
|
1028
|
+
composeExtension.authorization = {
|
|
1029
|
+
authType: "apiSecretServiceAuth",
|
|
1030
|
+
apiSecretServiceAuthConfiguration: {
|
|
1031
|
+
apiSecretRegistrationId: `\${{${safeApiSecretRegistrationId}}}`,
|
|
1032
|
+
},
|
|
1033
|
+
};
|
|
1034
|
+
}
|
|
1035
|
+
else if (Utils.isOAuthWithAuthCodeFlow(auth)) {
|
|
1036
|
+
const safeOAuth2RegistrationId = Utils.getSafeRegistrationIdEnvName(`${authInfo.name}_${ConstantString.OAuthRegistrationIdPostFix}`);
|
|
1037
|
+
composeExtension.authorization = {
|
|
1038
|
+
authType: "oAuth2.0",
|
|
1039
|
+
oAuthConfiguration: {
|
|
1040
|
+
oauthConfigurationId: `\${{${safeOAuth2RegistrationId}}}`,
|
|
1041
|
+
},
|
|
1042
|
+
};
|
|
1043
|
+
updatedPart.webApplicationInfo = {
|
|
1044
|
+
id: "${{AAD_APP_CLIENT_ID}}",
|
|
1045
|
+
resource: "api://${{DOMAIN}}/${{AAD_APP_CLIENT_ID}}",
|
|
1046
|
+
};
|
|
1047
|
+
}
|
|
792
1048
|
}
|
|
1049
|
+
updatedPart.composeExtensions = [composeExtension];
|
|
793
1050
|
}
|
|
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];
|
|
1051
|
+
updatedPart.description = originalManifest.description;
|
|
1052
|
+
ManifestUpdater.updateManifestDescription(updatedPart, spec);
|
|
799
1053
|
const updatedManifest = Object.assign(Object.assign({}, originalManifest), updatedPart);
|
|
800
1054
|
return [updatedManifest, warnings];
|
|
801
1055
|
}
|
|
@@ -803,7 +1057,8 @@ class ManifestUpdater {
|
|
|
803
1057
|
throw new SpecParserError(err.toString(), ErrorType.UpdateManifestFailed);
|
|
804
1058
|
}
|
|
805
1059
|
}
|
|
806
|
-
static async generateCommands(spec,
|
|
1060
|
+
static async generateCommands(spec, manifestPath, options, adaptiveCardFolder) {
|
|
1061
|
+
var _a;
|
|
807
1062
|
const paths = spec.paths;
|
|
808
1063
|
const commands = [];
|
|
809
1064
|
const warnings = [];
|
|
@@ -814,14 +1069,16 @@ class ManifestUpdater {
|
|
|
814
1069
|
const operations = pathItem;
|
|
815
1070
|
// Currently only support GET and POST method
|
|
816
1071
|
for (const method in operations) {
|
|
817
|
-
if (
|
|
1072
|
+
if ((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) {
|
|
818
1073
|
const operationItem = operations[method];
|
|
819
1074
|
if (operationItem) {
|
|
820
|
-
const [command, warning] = Utils.parseApiInfo(operationItem,
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
1075
|
+
const [command, warning] = Utils.parseApiInfo(operationItem, options);
|
|
1076
|
+
if (adaptiveCardFolder) {
|
|
1077
|
+
const adaptiveCardPath = path.join(adaptiveCardFolder, command.id + ".json");
|
|
1078
|
+
command.apiResponseRenderingTemplateFile = (await fs.pathExists(adaptiveCardPath))
|
|
1079
|
+
? ManifestUpdater.getRelativePath(manifestPath, adaptiveCardPath)
|
|
1080
|
+
: "";
|
|
1081
|
+
}
|
|
825
1082
|
if (warning) {
|
|
826
1083
|
warnings.push(warning);
|
|
827
1084
|
}
|
|
@@ -838,13 +1095,22 @@ class ManifestUpdater {
|
|
|
838
1095
|
const relativePath = path.relative(path.dirname(from), to);
|
|
839
1096
|
return path.normalize(relativePath).replace(/\\/g, "/");
|
|
840
1097
|
}
|
|
1098
|
+
static removeEnvs(str) {
|
|
1099
|
+
const placeHolderReg = /\${{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*}}/g;
|
|
1100
|
+
const matches = placeHolderReg.exec(str);
|
|
1101
|
+
let newStr = str;
|
|
1102
|
+
if (matches != null) {
|
|
1103
|
+
newStr = newStr.replace(matches[0], "");
|
|
1104
|
+
}
|
|
1105
|
+
return newStr;
|
|
1106
|
+
}
|
|
841
1107
|
}
|
|
842
1108
|
|
|
843
1109
|
// Copyright (c) Microsoft Corporation.
|
|
844
1110
|
class AdaptiveCardGenerator {
|
|
845
1111
|
static generateAdaptiveCard(operationItem) {
|
|
846
1112
|
try {
|
|
847
|
-
const json = Utils.getResponseJson(operationItem);
|
|
1113
|
+
const { json } = Utils.getResponseJson(operationItem);
|
|
848
1114
|
let cardBody = [];
|
|
849
1115
|
let schema = json.schema;
|
|
850
1116
|
let jsonPath = "$";
|
|
@@ -1104,8 +1370,11 @@ class SpecParser {
|
|
|
1104
1370
|
allowMissingId: true,
|
|
1105
1371
|
allowSwagger: true,
|
|
1106
1372
|
allowAPIKeyAuth: false,
|
|
1373
|
+
allowBearerTokenAuth: false,
|
|
1107
1374
|
allowMultipleParameters: false,
|
|
1108
1375
|
allowOauth2: false,
|
|
1376
|
+
allowMethods: ["get", "post"],
|
|
1377
|
+
projectType: ProjectType.SME,
|
|
1109
1378
|
};
|
|
1110
1379
|
this.pathOrSpec = pathOrDoc;
|
|
1111
1380
|
this.parser = new SwaggerParser();
|
|
@@ -1138,7 +1407,23 @@ class SpecParser {
|
|
|
1138
1407
|
],
|
|
1139
1408
|
};
|
|
1140
1409
|
}
|
|
1141
|
-
|
|
1410
|
+
if (this.options.projectType === ProjectType.SME ||
|
|
1411
|
+
this.options.projectType === ProjectType.Copilot) {
|
|
1412
|
+
if (this.spec.openapi >= "3.1.0") {
|
|
1413
|
+
return {
|
|
1414
|
+
status: ValidationStatus.Error,
|
|
1415
|
+
warnings: [],
|
|
1416
|
+
errors: [
|
|
1417
|
+
{
|
|
1418
|
+
type: ErrorType.SpecVersionNotSupported,
|
|
1419
|
+
content: Utils.format(ConstantString.SpecVersionNotSupported, this.spec.openapi),
|
|
1420
|
+
data: this.spec.openapi,
|
|
1421
|
+
},
|
|
1422
|
+
],
|
|
1423
|
+
};
|
|
1424
|
+
}
|
|
1425
|
+
}
|
|
1426
|
+
return Utils.validateSpec(this.spec, this.parser, !!this.isSwaggerFile, this.options);
|
|
1142
1427
|
}
|
|
1143
1428
|
catch (err) {
|
|
1144
1429
|
throw new SpecParserError(err.toString(), ErrorType.ValidateFailed);
|
|
@@ -1158,16 +1443,22 @@ class SpecParser {
|
|
|
1158
1443
|
try {
|
|
1159
1444
|
await this.loadSpec();
|
|
1160
1445
|
const spec = this.spec;
|
|
1161
|
-
const apiMap = this.
|
|
1162
|
-
const result =
|
|
1446
|
+
const apiMap = this.getAPIs(spec);
|
|
1447
|
+
const result = {
|
|
1448
|
+
APIs: [],
|
|
1449
|
+
allAPICount: 0,
|
|
1450
|
+
validAPICount: 0,
|
|
1451
|
+
};
|
|
1163
1452
|
for (const apiKey in apiMap) {
|
|
1453
|
+
const { operation, isValid, reason } = apiMap[apiKey];
|
|
1454
|
+
const [method, path] = apiKey.split(" ");
|
|
1164
1455
|
const apiResult = {
|
|
1165
1456
|
api: "",
|
|
1166
1457
|
server: "",
|
|
1167
1458
|
operationId: "",
|
|
1459
|
+
isValid: isValid,
|
|
1460
|
+
reason: reason,
|
|
1168
1461
|
};
|
|
1169
|
-
const [method, path] = apiKey.split(" ");
|
|
1170
|
-
const operation = apiMap[apiKey];
|
|
1171
1462
|
const rootServer = spec.servers && spec.servers[0];
|
|
1172
1463
|
const methodServer = spec.paths[path].servers && ((_a = spec.paths[path]) === null || _a === void 0 ? void 0 : _a.servers[0]);
|
|
1173
1464
|
const operationServer = operation.servers && operation.servers[0];
|
|
@@ -1175,7 +1466,7 @@ class SpecParser {
|
|
|
1175
1466
|
if (!serverUrl) {
|
|
1176
1467
|
throw new SpecParserError(ConstantString.NoServerInformation, ErrorType.NoServerInformation);
|
|
1177
1468
|
}
|
|
1178
|
-
apiResult.server = Utils.
|
|
1469
|
+
apiResult.server = Utils.resolveEnv(serverUrl.url);
|
|
1179
1470
|
let operationId = operation.operationId;
|
|
1180
1471
|
if (!operationId) {
|
|
1181
1472
|
operationId = `${method.toLowerCase()}${Utils.convertPathToCamelCase(path)}`;
|
|
@@ -1184,13 +1475,15 @@ class SpecParser {
|
|
|
1184
1475
|
const authArray = Utils.getAuthArray(operation.security, spec);
|
|
1185
1476
|
for (const auths of authArray) {
|
|
1186
1477
|
if (auths.length === 1) {
|
|
1187
|
-
apiResult.auth = auths[0]
|
|
1478
|
+
apiResult.auth = auths[0];
|
|
1188
1479
|
break;
|
|
1189
1480
|
}
|
|
1190
1481
|
}
|
|
1191
1482
|
apiResult.api = apiKey;
|
|
1192
|
-
result.push(apiResult);
|
|
1483
|
+
result.APIs.push(apiResult);
|
|
1193
1484
|
}
|
|
1485
|
+
result.allAPICount = result.APIs.length;
|
|
1486
|
+
result.validAPICount = result.APIs.filter((api) => api.isValid).length;
|
|
1194
1487
|
return result;
|
|
1195
1488
|
}
|
|
1196
1489
|
catch (err) {
|
|
@@ -1200,48 +1493,108 @@ class SpecParser {
|
|
|
1200
1493
|
throw new SpecParserError(err.toString(), ErrorType.ListFailed);
|
|
1201
1494
|
}
|
|
1202
1495
|
}
|
|
1496
|
+
/**
|
|
1497
|
+
* Generate specs according to the filters.
|
|
1498
|
+
* @param filter An array of strings that represent the filters to apply when generating the artifacts. If filter is empty, it would process nothing.
|
|
1499
|
+
*/
|
|
1500
|
+
async getFilteredSpecs(filter, signal) {
|
|
1501
|
+
try {
|
|
1502
|
+
if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
|
|
1503
|
+
throw new SpecParserError(ConstantString.CancelledMessage, ErrorType.Cancelled);
|
|
1504
|
+
}
|
|
1505
|
+
await this.loadSpec();
|
|
1506
|
+
if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
|
|
1507
|
+
throw new SpecParserError(ConstantString.CancelledMessage, ErrorType.Cancelled);
|
|
1508
|
+
}
|
|
1509
|
+
const newUnResolvedSpec = SpecFilter.specFilter(filter, this.unResolveSpec, this.spec, this.options);
|
|
1510
|
+
if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
|
|
1511
|
+
throw new SpecParserError(ConstantString.CancelledMessage, ErrorType.Cancelled);
|
|
1512
|
+
}
|
|
1513
|
+
const newSpec = (await this.parser.dereference(newUnResolvedSpec));
|
|
1514
|
+
return [newUnResolvedSpec, newSpec];
|
|
1515
|
+
}
|
|
1516
|
+
catch (err) {
|
|
1517
|
+
if (err instanceof SpecParserError) {
|
|
1518
|
+
throw err;
|
|
1519
|
+
}
|
|
1520
|
+
throw new SpecParserError(err.toString(), ErrorType.GetSpecFailed);
|
|
1521
|
+
}
|
|
1522
|
+
}
|
|
1203
1523
|
/**
|
|
1204
1524
|
* Generates and update artifacts from the OpenAPI specification file. Generate Adaptive Cards, update Teams app manifest, and generate a new OpenAPI specification file.
|
|
1205
1525
|
* @param manifestPath A file path of the Teams app manifest file to update.
|
|
1206
1526
|
* @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
1527
|
* @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
|
|
1528
|
+
* @param pluginFilePath File path of the api plugin file to generate.
|
|
1209
1529
|
*/
|
|
1210
|
-
async
|
|
1530
|
+
async generateForCopilot(manifestPath, filter, outputSpecPath, pluginFilePath, signal) {
|
|
1211
1531
|
const result = {
|
|
1212
1532
|
allSuccess: true,
|
|
1213
1533
|
warnings: [],
|
|
1214
1534
|
};
|
|
1215
1535
|
try {
|
|
1216
|
-
|
|
1217
|
-
|
|
1536
|
+
const newSpecs = await this.getFilteredSpecs(filter, signal);
|
|
1537
|
+
const newUnResolvedSpec = newSpecs[0];
|
|
1538
|
+
const newSpec = newSpecs[1];
|
|
1539
|
+
let resultStr;
|
|
1540
|
+
if (outputSpecPath.endsWith(".yaml") || outputSpecPath.endsWith(".yml")) {
|
|
1541
|
+
resultStr = jsyaml.dump(newUnResolvedSpec);
|
|
1218
1542
|
}
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
throw new SpecParserError(ConstantString.CancelledMessage, ErrorType.Cancelled);
|
|
1543
|
+
else {
|
|
1544
|
+
resultStr = JSON.stringify(newUnResolvedSpec, null, 2);
|
|
1222
1545
|
}
|
|
1223
|
-
|
|
1546
|
+
await fs.outputFile(outputSpecPath, resultStr);
|
|
1224
1547
|
if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
|
|
1225
1548
|
throw new SpecParserError(ConstantString.CancelledMessage, ErrorType.Cancelled);
|
|
1226
1549
|
}
|
|
1227
|
-
const
|
|
1228
|
-
|
|
1229
|
-
|
|
1550
|
+
const [updatedManifest, apiPlugin] = await ManifestUpdater.updateManifestWithAiPlugin(manifestPath, outputSpecPath, pluginFilePath, newSpec, this.options);
|
|
1551
|
+
await fs.outputJSON(manifestPath, updatedManifest, { spaces: 2 });
|
|
1552
|
+
await fs.outputJSON(pluginFilePath, apiPlugin, { spaces: 2 });
|
|
1553
|
+
}
|
|
1554
|
+
catch (err) {
|
|
1555
|
+
if (err instanceof SpecParserError) {
|
|
1556
|
+
throw err;
|
|
1557
|
+
}
|
|
1558
|
+
throw new SpecParserError(err.toString(), ErrorType.GenerateFailed);
|
|
1559
|
+
}
|
|
1560
|
+
return result;
|
|
1561
|
+
}
|
|
1562
|
+
/**
|
|
1563
|
+
* Generates and update artifacts from the OpenAPI specification file. Generate Adaptive Cards, update Teams app manifest, and generate a new OpenAPI specification file.
|
|
1564
|
+
* @param manifestPath A file path of the Teams app manifest file to update.
|
|
1565
|
+
* @param filter An array of strings that represent the filters to apply when generating the artifacts. If filter is empty, it would process nothing.
|
|
1566
|
+
* @param outputSpecPath File path of the new OpenAPI specification file to generate. If not specified or empty, no spec file will be generated.
|
|
1567
|
+
* @param adaptiveCardFolder Folder path where the Adaptive Card files will be generated. If not specified or empty, Adaptive Card files will not be generated.
|
|
1568
|
+
*/
|
|
1569
|
+
async generate(manifestPath, filter, outputSpecPath, adaptiveCardFolder, signal) {
|
|
1570
|
+
const result = {
|
|
1571
|
+
allSuccess: true,
|
|
1572
|
+
warnings: [],
|
|
1573
|
+
};
|
|
1574
|
+
try {
|
|
1575
|
+
const newSpecs = await this.getFilteredSpecs(filter, signal);
|
|
1576
|
+
const newUnResolvedSpec = newSpecs[0];
|
|
1577
|
+
const newSpec = newSpecs[1];
|
|
1578
|
+
let hasMultipleAuth = false;
|
|
1579
|
+
let authInfo = undefined;
|
|
1230
1580
|
for (const url in newSpec.paths) {
|
|
1231
1581
|
for (const method in newSpec.paths[url]) {
|
|
1232
1582
|
const operation = newSpec.paths[url][method];
|
|
1233
1583
|
const authArray = Utils.getAuthArray(operation.security, newSpec);
|
|
1234
1584
|
if (authArray && authArray.length > 0) {
|
|
1235
|
-
|
|
1236
|
-
if (
|
|
1237
|
-
|
|
1585
|
+
const currentAuth = authArray[0][0];
|
|
1586
|
+
if (!authInfo) {
|
|
1587
|
+
authInfo = authArray[0][0];
|
|
1588
|
+
}
|
|
1589
|
+
else if (authInfo.name !== currentAuth.name) {
|
|
1590
|
+
hasMultipleAuth = true;
|
|
1238
1591
|
break;
|
|
1239
1592
|
}
|
|
1240
1593
|
}
|
|
1241
1594
|
}
|
|
1242
1595
|
}
|
|
1243
|
-
if (
|
|
1244
|
-
throw new SpecParserError(ConstantString.
|
|
1596
|
+
if (hasMultipleAuth && this.options.projectType !== ProjectType.TeamsAi) {
|
|
1597
|
+
throw new SpecParserError(ConstantString.MultipleAuthNotSupported, ErrorType.MultipleAuthNotSupported);
|
|
1245
1598
|
}
|
|
1246
1599
|
let resultStr;
|
|
1247
1600
|
if (outputSpecPath.endsWith(".yaml") || outputSpecPath.endsWith(".yml")) {
|
|
@@ -1251,26 +1604,28 @@ class SpecParser {
|
|
|
1251
1604
|
resultStr = JSON.stringify(newUnResolvedSpec, null, 2);
|
|
1252
1605
|
}
|
|
1253
1606
|
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
|
-
|
|
1607
|
+
if (adaptiveCardFolder) {
|
|
1608
|
+
for (const url in newSpec.paths) {
|
|
1609
|
+
for (const method in newSpec.paths[url]) {
|
|
1610
|
+
// paths object may contain description/summary which is not a http method, so we need to check if it is a operation object
|
|
1611
|
+
if (this.options.allowMethods.includes(method)) {
|
|
1612
|
+
const operation = newSpec.paths[url][method];
|
|
1613
|
+
try {
|
|
1614
|
+
const [card, jsonPath] = AdaptiveCardGenerator.generateAdaptiveCard(operation);
|
|
1615
|
+
const fileName = path.join(adaptiveCardFolder, `${operation.operationId}.json`);
|
|
1616
|
+
const wrappedCard = wrapAdaptiveCard(card, jsonPath);
|
|
1617
|
+
await fs.outputJSON(fileName, wrappedCard, { spaces: 2 });
|
|
1618
|
+
const dataFileName = path.join(adaptiveCardFolder, `${operation.operationId}.data.json`);
|
|
1619
|
+
await fs.outputJSON(dataFileName, {}, { spaces: 2 });
|
|
1620
|
+
}
|
|
1621
|
+
catch (err) {
|
|
1622
|
+
result.allSuccess = false;
|
|
1623
|
+
result.warnings.push({
|
|
1624
|
+
type: WarningType.GenerateCardFailed,
|
|
1625
|
+
content: err.toString(),
|
|
1626
|
+
data: operation.operationId,
|
|
1627
|
+
});
|
|
1628
|
+
}
|
|
1274
1629
|
}
|
|
1275
1630
|
}
|
|
1276
1631
|
}
|
|
@@ -1278,8 +1633,7 @@ class SpecParser {
|
|
|
1278
1633
|
if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
|
|
1279
1634
|
throw new SpecParserError(ConstantString.CancelledMessage, ErrorType.Cancelled);
|
|
1280
1635
|
}
|
|
1281
|
-
const
|
|
1282
|
-
const [updatedManifest, warnings] = await ManifestUpdater.updateManifest(manifestPath, outputSpecPath, adaptiveCardFolder, newSpec, this.options.allowMultipleParameters, auth);
|
|
1636
|
+
const [updatedManifest, warnings] = await ManifestUpdater.updateManifest(manifestPath, outputSpecPath, newSpec, this.options, adaptiveCardFolder, authInfo);
|
|
1283
1637
|
await fs.outputJSON(manifestPath, updatedManifest, { spaces: 2 });
|
|
1284
1638
|
result.warnings.push(...warnings);
|
|
1285
1639
|
}
|
|
@@ -1304,15 +1658,15 @@ class SpecParser {
|
|
|
1304
1658
|
this.spec = (await this.parser.dereference(clonedUnResolveSpec));
|
|
1305
1659
|
}
|
|
1306
1660
|
}
|
|
1307
|
-
|
|
1661
|
+
getAPIs(spec) {
|
|
1308
1662
|
if (this.apiMap !== undefined) {
|
|
1309
1663
|
return this.apiMap;
|
|
1310
1664
|
}
|
|
1311
|
-
const result = Utils.
|
|
1665
|
+
const result = Utils.listAPIs(spec, this.options);
|
|
1312
1666
|
this.apiMap = result;
|
|
1313
1667
|
return result;
|
|
1314
1668
|
}
|
|
1315
1669
|
}
|
|
1316
1670
|
|
|
1317
|
-
export { ConstantString, ErrorType, SpecParser, SpecParserError, Utils, ValidationStatus, WarningType };
|
|
1671
|
+
export { AdaptiveCardGenerator, ConstantString, ErrorType, ProjectType, SpecParser, SpecParserError, Utils, ValidationStatus, WarningType };
|
|
1318
1672
|
//# sourceMappingURL=index.esm2017.mjs.map
|