@microsoft/m365-spec-parser 0.1.1-alpha.d1a09e202.0 → 0.1.1-alpha.ded43fb2d.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 +607 -318
- package/dist/index.esm2017.js.map +1 -1
- package/dist/index.esm2017.mjs +1143 -684
- package/dist/index.esm2017.mjs.map +1 -1
- package/dist/index.esm5.js +607 -318
- package/dist/index.esm5.js.map +1 -1
- package/dist/index.node.cjs.js +1086 -623
- package/dist/index.node.cjs.js.map +1 -1
- package/dist/src/adaptiveCardWrapper.d.ts +2 -0
- package/dist/src/constants.d.ts +3 -0
- package/dist/src/index.d.ts +1 -1
- package/dist/src/interfaces.d.ts +70 -15
- package/dist/src/manifestUpdater.d.ts +5 -2
- package/dist/src/specParser.browser.d.ts +3 -2
- package/dist/src/specParser.d.ts +5 -3
- package/dist/src/utils.d.ts +12 -30
- package/package.json +3 -3
package/dist/index.node.cjs.js
CHANGED
|
@@ -62,6 +62,7 @@ exports.ErrorType = void 0;
|
|
|
62
62
|
ErrorType["ResolveServerUrlFailed"] = "resolve-server-url-failed";
|
|
63
63
|
ErrorType["SwaggerNotSupported"] = "swagger-not-supported";
|
|
64
64
|
ErrorType["MultipleAuthNotSupported"] = "multiple-auth-not-supported";
|
|
65
|
+
ErrorType["SpecVersionNotSupported"] = "spec-version-not-supported";
|
|
65
66
|
ErrorType["ListFailed"] = "list-failed";
|
|
66
67
|
ErrorType["listSupportedAPIInfoFailed"] = "list-supported-api-info-failed";
|
|
67
68
|
ErrorType["FilterSpecFailed"] = "filter-spec-failed";
|
|
@@ -70,6 +71,21 @@ exports.ErrorType = void 0;
|
|
|
70
71
|
ErrorType["GenerateFailed"] = "generate-failed";
|
|
71
72
|
ErrorType["ValidateFailed"] = "validate-failed";
|
|
72
73
|
ErrorType["GetSpecFailed"] = "get-spec-failed";
|
|
74
|
+
ErrorType["AuthTypeIsNotSupported"] = "auth-type-is-not-supported";
|
|
75
|
+
ErrorType["MissingOperationId"] = "missing-operation-id";
|
|
76
|
+
ErrorType["PostBodyContainMultipleMediaTypes"] = "post-body-contain-multiple-media-types";
|
|
77
|
+
ErrorType["ResponseContainMultipleMediaTypes"] = "response-contain-multiple-media-types";
|
|
78
|
+
ErrorType["ResponseJsonIsEmpty"] = "response-json-is-empty";
|
|
79
|
+
ErrorType["PostBodySchemaIsNotJson"] = "post-body-schema-is-not-json";
|
|
80
|
+
ErrorType["PostBodyContainsRequiredUnsupportedSchema"] = "post-body-contains-required-unsupported-schema";
|
|
81
|
+
ErrorType["ParamsContainRequiredUnsupportedSchema"] = "params-contain-required-unsupported-schema";
|
|
82
|
+
ErrorType["ParamsContainsNestedObject"] = "params-contains-nested-object";
|
|
83
|
+
ErrorType["RequestBodyContainsNestedObject"] = "request-body-contains-nested-object";
|
|
84
|
+
ErrorType["ExceededRequiredParamsLimit"] = "exceeded-required-params-limit";
|
|
85
|
+
ErrorType["NoParameter"] = "no-parameter";
|
|
86
|
+
ErrorType["NoAPIInfo"] = "no-api-info";
|
|
87
|
+
ErrorType["MethodNotAllowed"] = "method-not-allowed";
|
|
88
|
+
ErrorType["UrlPathNotExist"] = "url-path-not-exist";
|
|
73
89
|
ErrorType["Cancelled"] = "cancelled";
|
|
74
90
|
ErrorType["Unknown"] = "unknown";
|
|
75
91
|
})(exports.ErrorType || (exports.ErrorType = {}));
|
|
@@ -117,6 +133,7 @@ ConstantString.ResolveServerUrlFailed = "Unable to resolve the server URL: pleas
|
|
|
117
133
|
ConstantString.OperationOnlyContainsOptionalParam = "Operation %s contains multiple optional parameters. The first optional parameter is used for this command.";
|
|
118
134
|
ConstantString.ConvertSwaggerToOpenAPI = "The Swagger 2.0 file has been converted to OpenAPI 3.0.";
|
|
119
135
|
ConstantString.SwaggerNotSupported = "Swagger 2.0 is not supported. Please convert to OpenAPI 3.0 manually before proceeding.";
|
|
136
|
+
ConstantString.SpecVersionNotSupported = "Unsupported OpenAPI version %s. Please use version 3.0.x.";
|
|
120
137
|
ConstantString.MultipleAuthNotSupported = "Multiple authentication methods are unsupported. Ensure all selected APIs use identical authentication.";
|
|
121
138
|
ConstantString.UnsupportedSchema = "Unsupported schema in %s %s: %s";
|
|
122
139
|
ConstantString.WrappedCardVersion = "devPreview";
|
|
@@ -128,6 +145,7 @@ ConstantString.AdaptiveCardVersion = "1.5";
|
|
|
128
145
|
ConstantString.AdaptiveCardSchema = "http://adaptivecards.io/schemas/adaptive-card.json";
|
|
129
146
|
ConstantString.AdaptiveCardType = "AdaptiveCard";
|
|
130
147
|
ConstantString.TextBlockType = "TextBlock";
|
|
148
|
+
ConstantString.ImageType = "Image";
|
|
131
149
|
ConstantString.ContainerType = "Container";
|
|
132
150
|
ConstantString.RegistrationIdPostfix = "REGISTRATION_ID";
|
|
133
151
|
ConstantString.OAuthRegistrationIdPostFix = "OAUTH_REGISTRATION_ID";
|
|
@@ -191,7 +209,8 @@ ConstantString.CommandDescriptionMaxLens = 128;
|
|
|
191
209
|
ConstantString.ParameterDescriptionMaxLens = 128;
|
|
192
210
|
ConstantString.CommandTitleMaxLens = 32;
|
|
193
211
|
ConstantString.ParameterTitleMaxLens = 32;
|
|
194
|
-
ConstantString.SMERequiredParamsMaxNum = 5;
|
|
212
|
+
ConstantString.SMERequiredParamsMaxNum = 5;
|
|
213
|
+
ConstantString.DefaultPluginId = "plugin_1";
|
|
195
214
|
|
|
196
215
|
// Copyright (c) Microsoft Corporation.
|
|
197
216
|
class SpecParserError extends Error {
|
|
@@ -214,221 +233,9 @@ class Utils {
|
|
|
214
233
|
}
|
|
215
234
|
return false;
|
|
216
235
|
}
|
|
217
|
-
static checkParameters(paramObject, isCopilot) {
|
|
218
|
-
const paramResult = {
|
|
219
|
-
requiredNum: 0,
|
|
220
|
-
optionalNum: 0,
|
|
221
|
-
isValid: true,
|
|
222
|
-
};
|
|
223
|
-
if (!paramObject) {
|
|
224
|
-
return paramResult;
|
|
225
|
-
}
|
|
226
|
-
for (let i = 0; i < paramObject.length; i++) {
|
|
227
|
-
const param = paramObject[i];
|
|
228
|
-
const schema = param.schema;
|
|
229
|
-
if (isCopilot && this.hasNestedObjectInSchema(schema)) {
|
|
230
|
-
paramResult.isValid = false;
|
|
231
|
-
continue;
|
|
232
|
-
}
|
|
233
|
-
const isRequiredWithoutDefault = param.required && schema.default === undefined;
|
|
234
|
-
if (isCopilot) {
|
|
235
|
-
if (isRequiredWithoutDefault) {
|
|
236
|
-
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
237
|
-
}
|
|
238
|
-
else {
|
|
239
|
-
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
240
|
-
}
|
|
241
|
-
continue;
|
|
242
|
-
}
|
|
243
|
-
if (param.in === "header" || param.in === "cookie") {
|
|
244
|
-
if (isRequiredWithoutDefault) {
|
|
245
|
-
paramResult.isValid = false;
|
|
246
|
-
}
|
|
247
|
-
continue;
|
|
248
|
-
}
|
|
249
|
-
if (schema.type !== "boolean" &&
|
|
250
|
-
schema.type !== "string" &&
|
|
251
|
-
schema.type !== "number" &&
|
|
252
|
-
schema.type !== "integer") {
|
|
253
|
-
if (isRequiredWithoutDefault) {
|
|
254
|
-
paramResult.isValid = false;
|
|
255
|
-
}
|
|
256
|
-
continue;
|
|
257
|
-
}
|
|
258
|
-
if (param.in === "query" || param.in === "path") {
|
|
259
|
-
if (isRequiredWithoutDefault) {
|
|
260
|
-
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
261
|
-
}
|
|
262
|
-
else {
|
|
263
|
-
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
return paramResult;
|
|
268
|
-
}
|
|
269
|
-
static checkPostBody(schema, isRequired = false, isCopilot = false) {
|
|
270
|
-
var _a;
|
|
271
|
-
const paramResult = {
|
|
272
|
-
requiredNum: 0,
|
|
273
|
-
optionalNum: 0,
|
|
274
|
-
isValid: true,
|
|
275
|
-
};
|
|
276
|
-
if (Object.keys(schema).length === 0) {
|
|
277
|
-
return paramResult;
|
|
278
|
-
}
|
|
279
|
-
const isRequiredWithoutDefault = isRequired && schema.default === undefined;
|
|
280
|
-
if (isCopilot && this.hasNestedObjectInSchema(schema)) {
|
|
281
|
-
paramResult.isValid = false;
|
|
282
|
-
return paramResult;
|
|
283
|
-
}
|
|
284
|
-
if (schema.type === "string" ||
|
|
285
|
-
schema.type === "integer" ||
|
|
286
|
-
schema.type === "boolean" ||
|
|
287
|
-
schema.type === "number") {
|
|
288
|
-
if (isRequiredWithoutDefault) {
|
|
289
|
-
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
290
|
-
}
|
|
291
|
-
else {
|
|
292
|
-
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
else if (schema.type === "object") {
|
|
296
|
-
const { properties } = schema;
|
|
297
|
-
for (const property in properties) {
|
|
298
|
-
let isRequired = false;
|
|
299
|
-
if (schema.required && ((_a = schema.required) === null || _a === void 0 ? void 0 : _a.indexOf(property)) >= 0) {
|
|
300
|
-
isRequired = true;
|
|
301
|
-
}
|
|
302
|
-
const result = Utils.checkPostBody(properties[property], isRequired, isCopilot);
|
|
303
|
-
paramResult.requiredNum += result.requiredNum;
|
|
304
|
-
paramResult.optionalNum += result.optionalNum;
|
|
305
|
-
paramResult.isValid = paramResult.isValid && result.isValid;
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
else {
|
|
309
|
-
if (isRequiredWithoutDefault && !isCopilot) {
|
|
310
|
-
paramResult.isValid = false;
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
return paramResult;
|
|
314
|
-
}
|
|
315
236
|
static containMultipleMediaTypes(bodyObject) {
|
|
316
237
|
return Object.keys((bodyObject === null || bodyObject === void 0 ? void 0 : bodyObject.content) || {}).length > 1;
|
|
317
238
|
}
|
|
318
|
-
/**
|
|
319
|
-
* Checks if the given API is supported.
|
|
320
|
-
* @param {string} method - The HTTP method of the API.
|
|
321
|
-
* @param {string} path - The path of the API.
|
|
322
|
-
* @param {OpenAPIV3.Document} spec - The OpenAPI specification document.
|
|
323
|
-
* @returns {boolean} - Returns true if the API is supported, false otherwise.
|
|
324
|
-
* @description The following APIs are supported:
|
|
325
|
-
* 1. only support Get/Post operation without auth property
|
|
326
|
-
* 2. parameter inside query or path only support string, number, boolean and integer
|
|
327
|
-
* 3. parameter inside post body only support string, number, boolean, integer and object
|
|
328
|
-
* 4. request body + required parameters <= 1
|
|
329
|
-
* 5. response body should be “application/json” and not empty, and response code should be 20X
|
|
330
|
-
* 6. only support request body with “application/json” content type
|
|
331
|
-
*/
|
|
332
|
-
static isSupportedApi(method, path, spec, options) {
|
|
333
|
-
var _a;
|
|
334
|
-
const pathObj = spec.paths[path];
|
|
335
|
-
method = method.toLocaleLowerCase();
|
|
336
|
-
if (pathObj) {
|
|
337
|
-
if (((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && pathObj[method]) {
|
|
338
|
-
const securities = pathObj[method].security;
|
|
339
|
-
const isTeamsAi = options.projectType === exports.ProjectType.TeamsAi;
|
|
340
|
-
const isCopilot = options.projectType === exports.ProjectType.Copilot;
|
|
341
|
-
// Teams AI project doesn't care about auth, it will use authProvider for user to implement
|
|
342
|
-
if (!isTeamsAi) {
|
|
343
|
-
const authArray = Utils.getAuthArray(securities, spec);
|
|
344
|
-
if (!Utils.isSupportedAuth(authArray, options)) {
|
|
345
|
-
return false;
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
const operationObject = pathObj[method];
|
|
349
|
-
if (!options.allowMissingId && !operationObject.operationId) {
|
|
350
|
-
return false;
|
|
351
|
-
}
|
|
352
|
-
const paramObject = operationObject.parameters;
|
|
353
|
-
const requestBody = operationObject.requestBody;
|
|
354
|
-
const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
|
|
355
|
-
if (!isTeamsAi && Utils.containMultipleMediaTypes(requestBody)) {
|
|
356
|
-
return false;
|
|
357
|
-
}
|
|
358
|
-
const responseJson = Utils.getResponseJson(operationObject, isTeamsAi);
|
|
359
|
-
if (Object.keys(responseJson).length === 0) {
|
|
360
|
-
return false;
|
|
361
|
-
}
|
|
362
|
-
// Teams AI project doesn't care about request parameters/body
|
|
363
|
-
if (isTeamsAi) {
|
|
364
|
-
return true;
|
|
365
|
-
}
|
|
366
|
-
let requestBodyParamResult = {
|
|
367
|
-
requiredNum: 0,
|
|
368
|
-
optionalNum: 0,
|
|
369
|
-
isValid: true,
|
|
370
|
-
};
|
|
371
|
-
if (requestJsonBody) {
|
|
372
|
-
const requestBodySchema = requestJsonBody.schema;
|
|
373
|
-
if (isCopilot && requestBodySchema.type !== "object") {
|
|
374
|
-
return false;
|
|
375
|
-
}
|
|
376
|
-
requestBodyParamResult = Utils.checkPostBody(requestBodySchema, requestBody.required, isCopilot);
|
|
377
|
-
}
|
|
378
|
-
if (!requestBodyParamResult.isValid) {
|
|
379
|
-
return false;
|
|
380
|
-
}
|
|
381
|
-
const paramResult = Utils.checkParameters(paramObject, isCopilot);
|
|
382
|
-
if (!paramResult.isValid) {
|
|
383
|
-
return false;
|
|
384
|
-
}
|
|
385
|
-
// Copilot support arbitrary parameters
|
|
386
|
-
if (isCopilot) {
|
|
387
|
-
return true;
|
|
388
|
-
}
|
|
389
|
-
if (requestBodyParamResult.requiredNum + paramResult.requiredNum > 1) {
|
|
390
|
-
if (options.allowMultipleParameters &&
|
|
391
|
-
requestBodyParamResult.requiredNum + paramResult.requiredNum <=
|
|
392
|
-
ConstantString.SMERequiredParamsMaxNum) {
|
|
393
|
-
return true;
|
|
394
|
-
}
|
|
395
|
-
return false;
|
|
396
|
-
}
|
|
397
|
-
else if (requestBodyParamResult.requiredNum +
|
|
398
|
-
requestBodyParamResult.optionalNum +
|
|
399
|
-
paramResult.requiredNum +
|
|
400
|
-
paramResult.optionalNum ===
|
|
401
|
-
0) {
|
|
402
|
-
return false;
|
|
403
|
-
}
|
|
404
|
-
else {
|
|
405
|
-
return true;
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
return false;
|
|
410
|
-
}
|
|
411
|
-
static isSupportedAuth(authSchemeArray, options) {
|
|
412
|
-
if (authSchemeArray.length === 0) {
|
|
413
|
-
return true;
|
|
414
|
-
}
|
|
415
|
-
if (options.allowAPIKeyAuth || options.allowOauth2 || options.allowBearerTokenAuth) {
|
|
416
|
-
// Currently we don't support multiple auth in one operation
|
|
417
|
-
if (authSchemeArray.length > 0 && authSchemeArray.every((auths) => auths.length > 1)) {
|
|
418
|
-
return false;
|
|
419
|
-
}
|
|
420
|
-
for (const auths of authSchemeArray) {
|
|
421
|
-
if (auths.length === 1) {
|
|
422
|
-
if ((options.allowAPIKeyAuth && Utils.isAPIKeyAuth(auths[0].authScheme)) ||
|
|
423
|
-
(options.allowOauth2 && Utils.isOAuthWithAuthCodeFlow(auths[0].authScheme)) ||
|
|
424
|
-
(options.allowBearerTokenAuth && Utils.isBearerTokenAuth(auths[0].authScheme))) {
|
|
425
|
-
return true;
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
return false;
|
|
431
|
-
}
|
|
432
239
|
static isBearerTokenAuth(authScheme) {
|
|
433
240
|
return authScheme.type === "http" && authScheme.scheme === "bearer";
|
|
434
241
|
}
|
|
@@ -436,10 +243,9 @@ class Utils {
|
|
|
436
243
|
return authScheme.type === "apiKey";
|
|
437
244
|
}
|
|
438
245
|
static isOAuthWithAuthCodeFlow(authScheme) {
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
return false;
|
|
246
|
+
return !!(authScheme.type === "oauth2" &&
|
|
247
|
+
authScheme.flows &&
|
|
248
|
+
authScheme.flows.authorizationCode);
|
|
443
249
|
}
|
|
444
250
|
static getAuthArray(securities, spec) {
|
|
445
251
|
var _a;
|
|
@@ -464,17 +270,39 @@ class Utils {
|
|
|
464
270
|
result.sort((a, b) => a[0].name.localeCompare(b[0].name));
|
|
465
271
|
return result;
|
|
466
272
|
}
|
|
273
|
+
static getAuthInfo(spec) {
|
|
274
|
+
let authInfo = undefined;
|
|
275
|
+
for (const url in spec.paths) {
|
|
276
|
+
for (const method in spec.paths[url]) {
|
|
277
|
+
const operation = spec.paths[url][method];
|
|
278
|
+
const authArray = Utils.getAuthArray(operation.security, spec);
|
|
279
|
+
if (authArray && authArray.length > 0) {
|
|
280
|
+
const currentAuth = authArray[0][0];
|
|
281
|
+
if (!authInfo) {
|
|
282
|
+
authInfo = authArray[0][0];
|
|
283
|
+
}
|
|
284
|
+
else if (authInfo.name !== currentAuth.name) {
|
|
285
|
+
throw new SpecParserError(ConstantString.MultipleAuthNotSupported, exports.ErrorType.MultipleAuthNotSupported);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
return authInfo;
|
|
291
|
+
}
|
|
467
292
|
static updateFirstLetter(str) {
|
|
468
293
|
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
469
294
|
}
|
|
470
|
-
static getResponseJson(operationObject
|
|
295
|
+
static getResponseJson(operationObject) {
|
|
471
296
|
var _a, _b;
|
|
472
297
|
let json = {};
|
|
298
|
+
let multipleMediaType = false;
|
|
473
299
|
for (const code of ConstantString.ResponseCodeFor20X) {
|
|
474
300
|
const responseObject = (_a = operationObject === null || operationObject === void 0 ? void 0 : operationObject.responses) === null || _a === void 0 ? void 0 : _a[code];
|
|
475
301
|
if ((_b = responseObject === null || responseObject === void 0 ? void 0 : responseObject.content) === null || _b === void 0 ? void 0 : _b["application/json"]) {
|
|
302
|
+
multipleMediaType = false;
|
|
476
303
|
json = responseObject.content["application/json"];
|
|
477
|
-
if (
|
|
304
|
+
if (Utils.containMultipleMediaTypes(responseObject)) {
|
|
305
|
+
multipleMediaType = true;
|
|
478
306
|
json = {};
|
|
479
307
|
}
|
|
480
308
|
else {
|
|
@@ -482,7 +310,7 @@ class Utils {
|
|
|
482
310
|
}
|
|
483
311
|
}
|
|
484
312
|
}
|
|
485
|
-
return json;
|
|
313
|
+
return { json, multipleMediaType };
|
|
486
314
|
}
|
|
487
315
|
static convertPathToCamelCase(path) {
|
|
488
316
|
const pathSegments = path.split(/[./{]/);
|
|
@@ -502,10 +330,10 @@ class Utils {
|
|
|
502
330
|
return undefined;
|
|
503
331
|
}
|
|
504
332
|
}
|
|
505
|
-
static
|
|
333
|
+
static resolveEnv(str) {
|
|
506
334
|
const placeHolderReg = /\${{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*}}/g;
|
|
507
|
-
let matches = placeHolderReg.exec(
|
|
508
|
-
let
|
|
335
|
+
let matches = placeHolderReg.exec(str);
|
|
336
|
+
let newStr = str;
|
|
509
337
|
while (matches != null) {
|
|
510
338
|
const envVar = matches[1];
|
|
511
339
|
const envVal = process.env[envVar];
|
|
@@ -513,17 +341,17 @@ class Utils {
|
|
|
513
341
|
throw new Error(Utils.format(ConstantString.ResolveServerUrlFailed, envVar));
|
|
514
342
|
}
|
|
515
343
|
else {
|
|
516
|
-
|
|
344
|
+
newStr = newStr.replace(matches[0], envVal);
|
|
517
345
|
}
|
|
518
|
-
matches = placeHolderReg.exec(
|
|
346
|
+
matches = placeHolderReg.exec(str);
|
|
519
347
|
}
|
|
520
|
-
return
|
|
348
|
+
return newStr;
|
|
521
349
|
}
|
|
522
350
|
static checkServerUrl(servers) {
|
|
523
351
|
const errors = [];
|
|
524
352
|
let serverUrl;
|
|
525
353
|
try {
|
|
526
|
-
serverUrl = Utils.
|
|
354
|
+
serverUrl = Utils.resolveEnv(servers[0].url);
|
|
527
355
|
}
|
|
528
356
|
catch (err) {
|
|
529
357
|
errors.push({
|
|
@@ -554,6 +382,7 @@ class Utils {
|
|
|
554
382
|
return errors;
|
|
555
383
|
}
|
|
556
384
|
static validateServer(spec, options) {
|
|
385
|
+
var _a;
|
|
557
386
|
const errors = [];
|
|
558
387
|
let hasTopLevelServers = false;
|
|
559
388
|
let hasPathLevelServers = false;
|
|
@@ -574,7 +403,7 @@ class Utils {
|
|
|
574
403
|
}
|
|
575
404
|
for (const method in methods) {
|
|
576
405
|
const operationObject = methods[method];
|
|
577
|
-
if (
|
|
406
|
+
if (((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && operationObject) {
|
|
578
407
|
if ((operationObject === null || operationObject === void 0 ? void 0 : operationObject.servers) && operationObject.servers.length >= 1) {
|
|
579
408
|
hasOperationLevelServers = true;
|
|
580
409
|
const serverErrors = Utils.checkServerUrl(operationObject.servers);
|
|
@@ -617,6 +446,7 @@ class Utils {
|
|
|
617
446
|
Utils.updateParameterWithInputType(schema, parameter);
|
|
618
447
|
}
|
|
619
448
|
if (isRequired && schema.default === undefined) {
|
|
449
|
+
parameter.isRequired = true;
|
|
620
450
|
requiredParams.push(parameter);
|
|
621
451
|
}
|
|
622
452
|
else {
|
|
@@ -680,6 +510,7 @@ class Utils {
|
|
|
680
510
|
}
|
|
681
511
|
if (param.in !== "header" && param.in !== "cookie") {
|
|
682
512
|
if (param.required && (schema === null || schema === void 0 ? void 0 : schema.default) === undefined) {
|
|
513
|
+
parameter.isRequired = true;
|
|
683
514
|
requiredParams.push(parameter);
|
|
684
515
|
}
|
|
685
516
|
else {
|
|
@@ -699,13 +530,7 @@ class Utils {
|
|
|
699
530
|
}
|
|
700
531
|
}
|
|
701
532
|
const operationId = operationItem.operationId;
|
|
702
|
-
const parameters = [];
|
|
703
|
-
if (requiredParams.length !== 0) {
|
|
704
|
-
parameters.push(...requiredParams);
|
|
705
|
-
}
|
|
706
|
-
else {
|
|
707
|
-
parameters.push(optionalParams[0]);
|
|
708
|
-
}
|
|
533
|
+
const parameters = [...requiredParams, ...optionalParams];
|
|
709
534
|
const command = {
|
|
710
535
|
context: ["compose"],
|
|
711
536
|
type: "query",
|
|
@@ -714,353 +539,575 @@ class Utils {
|
|
|
714
539
|
parameters: parameters,
|
|
715
540
|
description: ((_b = operationItem.description) !== null && _b !== void 0 ? _b : "").slice(0, ConstantString.CommandDescriptionMaxLens),
|
|
716
541
|
};
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
542
|
+
return command;
|
|
543
|
+
}
|
|
544
|
+
static format(str, ...args) {
|
|
545
|
+
let index = 0;
|
|
546
|
+
return str.replace(/%s/g, () => {
|
|
547
|
+
const arg = args[index++];
|
|
548
|
+
return arg !== undefined ? arg : "";
|
|
549
|
+
});
|
|
550
|
+
}
|
|
551
|
+
static getSafeRegistrationIdEnvName(authName) {
|
|
552
|
+
if (!authName) {
|
|
553
|
+
return "";
|
|
554
|
+
}
|
|
555
|
+
let safeRegistrationIdEnvName = authName.toUpperCase().replace(/[^A-Z0-9_]/g, "_");
|
|
556
|
+
if (!safeRegistrationIdEnvName.match(/^[A-Z]/)) {
|
|
557
|
+
safeRegistrationIdEnvName = "PREFIX_" + safeRegistrationIdEnvName;
|
|
724
558
|
}
|
|
725
|
-
return
|
|
559
|
+
return safeRegistrationIdEnvName;
|
|
726
560
|
}
|
|
727
|
-
static
|
|
728
|
-
const
|
|
561
|
+
static getServerObject(spec, method, path) {
|
|
562
|
+
const pathObj = spec.paths[path];
|
|
563
|
+
const operationObject = pathObj[method];
|
|
564
|
+
const rootServer = spec.servers && spec.servers[0];
|
|
565
|
+
const methodServer = spec.paths[path].servers && spec.paths[path].servers[0];
|
|
566
|
+
const operationServer = operationObject.servers && operationObject.servers[0];
|
|
567
|
+
const serverUrl = operationServer || methodServer || rootServer;
|
|
568
|
+
return serverUrl;
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
// Copyright (c) Microsoft Corporation.
|
|
573
|
+
class Validator {
|
|
574
|
+
listAPIs() {
|
|
575
|
+
var _a;
|
|
576
|
+
if (this.apiMap) {
|
|
577
|
+
return this.apiMap;
|
|
578
|
+
}
|
|
579
|
+
const paths = this.spec.paths;
|
|
729
580
|
const result = {};
|
|
730
581
|
for (const path in paths) {
|
|
731
582
|
const methods = paths[path];
|
|
732
583
|
for (const method in methods) {
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
584
|
+
const operationObject = methods[method];
|
|
585
|
+
if (((_a = this.options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && operationObject) {
|
|
586
|
+
const validateResult = this.validateAPI(method, path);
|
|
587
|
+
result[`${method.toUpperCase()} ${path}`] = {
|
|
588
|
+
operation: operationObject,
|
|
589
|
+
isValid: validateResult.isValid,
|
|
590
|
+
reason: validateResult.reason,
|
|
591
|
+
};
|
|
736
592
|
}
|
|
737
593
|
}
|
|
738
594
|
}
|
|
595
|
+
this.apiMap = result;
|
|
739
596
|
return result;
|
|
740
597
|
}
|
|
741
|
-
|
|
742
|
-
const
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
});
|
|
749
|
-
}
|
|
750
|
-
// Server validation
|
|
751
|
-
const serverErrors = Utils.validateServer(spec, options);
|
|
752
|
-
errors.push(...serverErrors);
|
|
753
|
-
// Remote reference not supported
|
|
754
|
-
const refPaths = parser.$refs.paths();
|
|
755
|
-
// refPaths [0] is the current spec file path
|
|
756
|
-
if (refPaths.length > 1) {
|
|
757
|
-
errors.push({
|
|
758
|
-
type: exports.ErrorType.RemoteRefNotSupported,
|
|
759
|
-
content: Utils.format(ConstantString.RemoteRefNotSupported, refPaths.join(", ")),
|
|
760
|
-
data: refPaths,
|
|
598
|
+
validateSpecVersion() {
|
|
599
|
+
const result = { errors: [], warnings: [] };
|
|
600
|
+
if (this.spec.openapi >= "3.1.0") {
|
|
601
|
+
result.errors.push({
|
|
602
|
+
type: exports.ErrorType.SpecVersionNotSupported,
|
|
603
|
+
content: Utils.format(ConstantString.SpecVersionNotSupported, this.spec.openapi),
|
|
604
|
+
data: this.spec.openapi,
|
|
761
605
|
});
|
|
762
606
|
}
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
607
|
+
return result;
|
|
608
|
+
}
|
|
609
|
+
validateSpecServer() {
|
|
610
|
+
const result = { errors: [], warnings: [] };
|
|
611
|
+
const serverErrors = Utils.validateServer(this.spec, this.options);
|
|
612
|
+
result.errors.push(...serverErrors);
|
|
613
|
+
return result;
|
|
614
|
+
}
|
|
615
|
+
validateSpecNoSupportAPI() {
|
|
616
|
+
const result = { errors: [], warnings: [] };
|
|
617
|
+
const apiMap = this.listAPIs();
|
|
618
|
+
const validAPIs = Object.entries(apiMap).filter(([, value]) => value.isValid);
|
|
619
|
+
if (validAPIs.length === 0) {
|
|
620
|
+
const data = [];
|
|
621
|
+
for (const key in apiMap) {
|
|
622
|
+
const { reason } = apiMap[key];
|
|
623
|
+
const apiInvalidReason = { api: key, reason: reason };
|
|
624
|
+
data.push(apiInvalidReason);
|
|
625
|
+
}
|
|
626
|
+
result.errors.push({
|
|
767
627
|
type: exports.ErrorType.NoSupportedApi,
|
|
768
628
|
content: ConstantString.NoSupportedApi,
|
|
629
|
+
data,
|
|
769
630
|
});
|
|
770
631
|
}
|
|
632
|
+
return result;
|
|
633
|
+
}
|
|
634
|
+
validateSpecOperationId() {
|
|
635
|
+
const result = { errors: [], warnings: [] };
|
|
636
|
+
const apiMap = this.listAPIs();
|
|
771
637
|
// OperationId missing
|
|
772
638
|
const apisMissingOperationId = [];
|
|
773
639
|
for (const key in apiMap) {
|
|
774
|
-
const
|
|
775
|
-
if (!
|
|
640
|
+
const { operation } = apiMap[key];
|
|
641
|
+
if (!operation.operationId) {
|
|
776
642
|
apisMissingOperationId.push(key);
|
|
777
643
|
}
|
|
778
644
|
}
|
|
779
645
|
if (apisMissingOperationId.length > 0) {
|
|
780
|
-
warnings.push({
|
|
646
|
+
result.warnings.push({
|
|
781
647
|
type: exports.WarningType.OperationIdMissing,
|
|
782
648
|
content: Utils.format(ConstantString.MissingOperationId, apisMissingOperationId.join(", ")),
|
|
783
649
|
data: apisMissingOperationId,
|
|
784
650
|
});
|
|
785
651
|
}
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
652
|
+
return result;
|
|
653
|
+
}
|
|
654
|
+
validateMethodAndPath(method, path) {
|
|
655
|
+
const result = { isValid: true, reason: [] };
|
|
656
|
+
if (this.options.allowMethods && !this.options.allowMethods.includes(method)) {
|
|
657
|
+
result.isValid = false;
|
|
658
|
+
result.reason.push(exports.ErrorType.MethodNotAllowed);
|
|
659
|
+
return result;
|
|
789
660
|
}
|
|
790
|
-
|
|
791
|
-
|
|
661
|
+
const pathObj = this.spec.paths[path];
|
|
662
|
+
if (!pathObj || !pathObj[method]) {
|
|
663
|
+
result.isValid = false;
|
|
664
|
+
result.reason.push(exports.ErrorType.UrlPathNotExist);
|
|
665
|
+
return result;
|
|
792
666
|
}
|
|
793
|
-
return
|
|
794
|
-
status,
|
|
795
|
-
warnings,
|
|
796
|
-
errors,
|
|
797
|
-
};
|
|
667
|
+
return result;
|
|
798
668
|
}
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
669
|
+
validateResponse(method, path) {
|
|
670
|
+
const result = { isValid: true, reason: [] };
|
|
671
|
+
const operationObject = this.spec.paths[path][method];
|
|
672
|
+
const { json, multipleMediaType } = Utils.getResponseJson(operationObject);
|
|
673
|
+
if (this.options.projectType === exports.ProjectType.SME) {
|
|
674
|
+
// only support response body only contains “application/json” content type
|
|
675
|
+
if (multipleMediaType) {
|
|
676
|
+
result.reason.push(exports.ErrorType.ResponseContainMultipleMediaTypes);
|
|
677
|
+
}
|
|
678
|
+
else if (Object.keys(json).length === 0) {
|
|
679
|
+
// response body should not be empty
|
|
680
|
+
result.reason.push(exports.ErrorType.ResponseJsonIsEmpty);
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
return result;
|
|
805
684
|
}
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
685
|
+
validateServer(method, path) {
|
|
686
|
+
const result = { isValid: true, reason: [] };
|
|
687
|
+
const serverObj = Utils.getServerObject(this.spec, method, path);
|
|
688
|
+
if (!serverObj) {
|
|
689
|
+
// should contain server URL
|
|
690
|
+
result.reason.push(exports.ErrorType.NoServerInformation);
|
|
809
691
|
}
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
692
|
+
else {
|
|
693
|
+
// server url should be absolute url with https protocol
|
|
694
|
+
const serverValidateResult = Utils.checkServerUrl([serverObj]);
|
|
695
|
+
result.reason.push(...serverValidateResult.map((item) => item.type));
|
|
813
696
|
}
|
|
814
|
-
return
|
|
697
|
+
return result;
|
|
815
698
|
}
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
699
|
+
validateAuth(method, path) {
|
|
700
|
+
const pathObj = this.spec.paths[path];
|
|
701
|
+
const operationObject = pathObj[method];
|
|
702
|
+
const securities = operationObject.security;
|
|
703
|
+
const authSchemeArray = Utils.getAuthArray(securities, this.spec);
|
|
704
|
+
if (authSchemeArray.length === 0) {
|
|
705
|
+
return { isValid: true, reason: [] };
|
|
706
|
+
}
|
|
707
|
+
if (this.options.allowAPIKeyAuth ||
|
|
708
|
+
this.options.allowOauth2 ||
|
|
709
|
+
this.options.allowBearerTokenAuth) {
|
|
710
|
+
// Currently we don't support multiple auth in one operation
|
|
711
|
+
if (authSchemeArray.length > 0 && authSchemeArray.every((auths) => auths.length > 1)) {
|
|
712
|
+
return {
|
|
713
|
+
isValid: false,
|
|
714
|
+
reason: [exports.ErrorType.MultipleAuthNotSupported],
|
|
715
|
+
};
|
|
716
|
+
}
|
|
717
|
+
for (const auths of authSchemeArray) {
|
|
718
|
+
if (auths.length === 1) {
|
|
719
|
+
if ((this.options.allowAPIKeyAuth && Utils.isAPIKeyAuth(auths[0].authScheme)) ||
|
|
720
|
+
(this.options.allowOauth2 && Utils.isOAuthWithAuthCodeFlow(auths[0].authScheme)) ||
|
|
721
|
+
(this.options.allowBearerTokenAuth && Utils.isBearerTokenAuth(auths[0].authScheme))) {
|
|
722
|
+
return { isValid: true, reason: [] };
|
|
834
723
|
}
|
|
835
724
|
}
|
|
836
|
-
newPaths[path][methodName] = unResolveSpec.paths[path][methodName];
|
|
837
|
-
// Add the operationId if missing
|
|
838
|
-
if (!newPaths[path][methodName].operationId) {
|
|
839
|
-
newPaths[path][methodName].operationId = `${methodName}${Utils.convertPathToCamelCase(path)}`;
|
|
840
|
-
}
|
|
841
725
|
}
|
|
842
|
-
newSpec.paths = newPaths;
|
|
843
|
-
return newSpec;
|
|
844
|
-
}
|
|
845
|
-
catch (err) {
|
|
846
|
-
throw new SpecParserError(err.toString(), exports.ErrorType.FilterSpecFailed);
|
|
847
726
|
}
|
|
727
|
+
return { isValid: false, reason: [exports.ErrorType.AuthTypeIsNotSupported] };
|
|
848
728
|
}
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
const apiPluginRelativePath = ManifestUpdater.getRelativePath(manifestPath, apiPluginFilePath);
|
|
857
|
-
manifest.plugins = [
|
|
858
|
-
{
|
|
859
|
-
pluginFile: apiPluginRelativePath,
|
|
860
|
-
},
|
|
861
|
-
];
|
|
862
|
-
ManifestUpdater.updateManifestDescription(manifest, spec);
|
|
863
|
-
const specRelativePath = ManifestUpdater.getRelativePath(manifestPath, outputSpecPath);
|
|
864
|
-
const apiPlugin = ManifestUpdater.generatePluginManifestSchema(spec, specRelativePath, options);
|
|
865
|
-
return [manifest, apiPlugin];
|
|
866
|
-
});
|
|
867
|
-
}
|
|
868
|
-
static updateManifestDescription(manifest, spec) {
|
|
869
|
-
var _a, _b;
|
|
870
|
-
manifest.description = {
|
|
871
|
-
short: spec.info.title.slice(0, ConstantString.ShortDescriptionMaxLens),
|
|
872
|
-
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),
|
|
729
|
+
checkPostBodySchema(schema, isRequired = false) {
|
|
730
|
+
var _a;
|
|
731
|
+
const paramResult = {
|
|
732
|
+
requiredNum: 0,
|
|
733
|
+
optionalNum: 0,
|
|
734
|
+
isValid: true,
|
|
735
|
+
reason: [],
|
|
873
736
|
};
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
737
|
+
if (Object.keys(schema).length === 0) {
|
|
738
|
+
return paramResult;
|
|
739
|
+
}
|
|
740
|
+
const isRequiredWithoutDefault = isRequired && schema.default === undefined;
|
|
741
|
+
const isCopilot = this.projectType === exports.ProjectType.Copilot;
|
|
742
|
+
if (isCopilot && this.hasNestedObjectInSchema(schema)) {
|
|
743
|
+
paramResult.isValid = false;
|
|
744
|
+
paramResult.reason = [exports.ErrorType.RequestBodyContainsNestedObject];
|
|
745
|
+
return paramResult;
|
|
746
|
+
}
|
|
877
747
|
if (schema.type === "string" ||
|
|
878
|
-
schema.type === "boolean" ||
|
|
879
748
|
schema.type === "integer" ||
|
|
880
|
-
schema.type === "
|
|
881
|
-
schema.type === "
|
|
882
|
-
|
|
749
|
+
schema.type === "boolean" ||
|
|
750
|
+
schema.type === "number") {
|
|
751
|
+
if (isRequiredWithoutDefault) {
|
|
752
|
+
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
753
|
+
}
|
|
754
|
+
else {
|
|
755
|
+
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
else if (schema.type === "object") {
|
|
759
|
+
const { properties } = schema;
|
|
760
|
+
for (const property in properties) {
|
|
761
|
+
let isRequired = false;
|
|
762
|
+
if (schema.required && ((_a = schema.required) === null || _a === void 0 ? void 0 : _a.indexOf(property)) >= 0) {
|
|
763
|
+
isRequired = true;
|
|
764
|
+
}
|
|
765
|
+
const result = this.checkPostBodySchema(properties[property], isRequired);
|
|
766
|
+
paramResult.requiredNum += result.requiredNum;
|
|
767
|
+
paramResult.optionalNum += result.optionalNum;
|
|
768
|
+
paramResult.isValid = paramResult.isValid && result.isValid;
|
|
769
|
+
paramResult.reason.push(...result.reason);
|
|
770
|
+
}
|
|
883
771
|
}
|
|
884
772
|
else {
|
|
885
|
-
|
|
773
|
+
if (isRequiredWithoutDefault && !isCopilot) {
|
|
774
|
+
paramResult.isValid = false;
|
|
775
|
+
paramResult.reason.push(exports.ErrorType.PostBodyContainsRequiredUnsupportedSchema);
|
|
776
|
+
}
|
|
886
777
|
}
|
|
887
|
-
return
|
|
778
|
+
return paramResult;
|
|
888
779
|
}
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
name: operationId,
|
|
942
|
-
description: description,
|
|
943
|
-
parameters: parameters,
|
|
944
|
-
};
|
|
945
|
-
functions.push(funcObj);
|
|
946
|
-
functionNames.push(operationId);
|
|
947
|
-
}
|
|
948
|
-
}
|
|
780
|
+
checkParamSchema(paramObject) {
|
|
781
|
+
const paramResult = {
|
|
782
|
+
requiredNum: 0,
|
|
783
|
+
optionalNum: 0,
|
|
784
|
+
isValid: true,
|
|
785
|
+
reason: [],
|
|
786
|
+
};
|
|
787
|
+
if (!paramObject) {
|
|
788
|
+
return paramResult;
|
|
789
|
+
}
|
|
790
|
+
const isCopilot = this.projectType === exports.ProjectType.Copilot;
|
|
791
|
+
for (let i = 0; i < paramObject.length; i++) {
|
|
792
|
+
const param = paramObject[i];
|
|
793
|
+
const schema = param.schema;
|
|
794
|
+
if (isCopilot && this.hasNestedObjectInSchema(schema)) {
|
|
795
|
+
paramResult.isValid = false;
|
|
796
|
+
paramResult.reason.push(exports.ErrorType.ParamsContainsNestedObject);
|
|
797
|
+
continue;
|
|
798
|
+
}
|
|
799
|
+
const isRequiredWithoutDefault = param.required && schema.default === undefined;
|
|
800
|
+
if (isCopilot) {
|
|
801
|
+
if (isRequiredWithoutDefault) {
|
|
802
|
+
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
803
|
+
}
|
|
804
|
+
else {
|
|
805
|
+
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
806
|
+
}
|
|
807
|
+
continue;
|
|
808
|
+
}
|
|
809
|
+
if (param.in === "header" || param.in === "cookie") {
|
|
810
|
+
if (isRequiredWithoutDefault) {
|
|
811
|
+
paramResult.isValid = false;
|
|
812
|
+
paramResult.reason.push(exports.ErrorType.ParamsContainRequiredUnsupportedSchema);
|
|
813
|
+
}
|
|
814
|
+
continue;
|
|
815
|
+
}
|
|
816
|
+
if (schema.type !== "boolean" &&
|
|
817
|
+
schema.type !== "string" &&
|
|
818
|
+
schema.type !== "number" &&
|
|
819
|
+
schema.type !== "integer") {
|
|
820
|
+
if (isRequiredWithoutDefault) {
|
|
821
|
+
paramResult.isValid = false;
|
|
822
|
+
paramResult.reason.push(exports.ErrorType.ParamsContainRequiredUnsupportedSchema);
|
|
823
|
+
}
|
|
824
|
+
continue;
|
|
825
|
+
}
|
|
826
|
+
if (param.in === "query" || param.in === "path") {
|
|
827
|
+
if (isRequiredWithoutDefault) {
|
|
828
|
+
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
829
|
+
}
|
|
830
|
+
else {
|
|
831
|
+
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
949
832
|
}
|
|
950
833
|
}
|
|
951
834
|
}
|
|
952
|
-
|
|
953
|
-
schema_version: "v2",
|
|
954
|
-
name_for_human: spec.info.title,
|
|
955
|
-
description_for_human: (_c = spec.info.description) !== null && _c !== void 0 ? _c : "<Please add description of the plugin>",
|
|
956
|
-
functions: functions,
|
|
957
|
-
runtimes: [
|
|
958
|
-
{
|
|
959
|
-
type: "OpenApi",
|
|
960
|
-
auth: {
|
|
961
|
-
type: "none", // TODO, support auth in the future
|
|
962
|
-
},
|
|
963
|
-
spec: {
|
|
964
|
-
url: specRelativePath,
|
|
965
|
-
},
|
|
966
|
-
run_for_functions: functionNames,
|
|
967
|
-
},
|
|
968
|
-
],
|
|
969
|
-
};
|
|
970
|
-
return apiPlugin;
|
|
835
|
+
return paramResult;
|
|
971
836
|
}
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
const
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
let warnings = [];
|
|
979
|
-
if (options.projectType === exports.ProjectType.SME) {
|
|
980
|
-
const updateResult = yield ManifestUpdater.generateCommands(spec, manifestPath, options, adaptiveCardFolder);
|
|
981
|
-
const commands = updateResult[0];
|
|
982
|
-
warnings = updateResult[1];
|
|
983
|
-
const composeExtension = {
|
|
984
|
-
composeExtensionType: "apiBased",
|
|
985
|
-
apiSpecificationFile: ManifestUpdater.getRelativePath(manifestPath, outputSpecPath),
|
|
986
|
-
commands: commands,
|
|
987
|
-
};
|
|
988
|
-
if (authInfo) {
|
|
989
|
-
const auth = authInfo.authScheme;
|
|
990
|
-
if (Utils.isAPIKeyAuth(auth) || Utils.isBearerTokenAuth(auth)) {
|
|
991
|
-
const safeApiSecretRegistrationId = Utils.getSafeRegistrationIdEnvName(`${authInfo.name}_${ConstantString.RegistrationIdPostfix}`);
|
|
992
|
-
composeExtension.authorization = {
|
|
993
|
-
authType: "apiSecretServiceAuth",
|
|
994
|
-
apiSecretServiceAuthConfiguration: {
|
|
995
|
-
apiSecretRegistrationId: `\${{${safeApiSecretRegistrationId}}}`,
|
|
996
|
-
},
|
|
997
|
-
};
|
|
998
|
-
}
|
|
999
|
-
else if (Utils.isOAuthWithAuthCodeFlow(auth)) {
|
|
1000
|
-
const safeOAuth2RegistrationId = Utils.getSafeRegistrationIdEnvName(`${authInfo.name}_${ConstantString.OAuthRegistrationIdPostFix}`);
|
|
1001
|
-
composeExtension.authorization = {
|
|
1002
|
-
authType: "oAuth2.0",
|
|
1003
|
-
oAuthConfiguration: {
|
|
1004
|
-
oauthConfigurationId: `\${{${safeOAuth2RegistrationId}}}`,
|
|
1005
|
-
},
|
|
1006
|
-
};
|
|
1007
|
-
updatedPart.webApplicationInfo = {
|
|
1008
|
-
id: "${{AAD_APP_CLIENT_ID}}",
|
|
1009
|
-
resource: "api://${{DOMAIN}}/${{AAD_APP_CLIENT_ID}}",
|
|
1010
|
-
};
|
|
1011
|
-
}
|
|
1012
|
-
}
|
|
1013
|
-
updatedPart.composeExtensions = [composeExtension];
|
|
837
|
+
hasNestedObjectInSchema(schema) {
|
|
838
|
+
if (schema.type === "object") {
|
|
839
|
+
for (const property in schema.properties) {
|
|
840
|
+
const nestedSchema = schema.properties[property];
|
|
841
|
+
if (nestedSchema.type === "object") {
|
|
842
|
+
return true;
|
|
1014
843
|
}
|
|
1015
|
-
updatedPart.description = originalManifest.description;
|
|
1016
|
-
ManifestUpdater.updateManifestDescription(updatedPart, spec);
|
|
1017
|
-
const updatedManifest = Object.assign(Object.assign({}, originalManifest), updatedPart);
|
|
1018
|
-
return [updatedManifest, warnings];
|
|
1019
844
|
}
|
|
1020
|
-
|
|
1021
|
-
|
|
845
|
+
}
|
|
846
|
+
return false;
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
// Copyright (c) Microsoft Corporation.
|
|
851
|
+
class CopilotValidator extends Validator {
|
|
852
|
+
constructor(spec, options) {
|
|
853
|
+
super();
|
|
854
|
+
this.projectType = exports.ProjectType.Copilot;
|
|
855
|
+
this.options = options;
|
|
856
|
+
this.spec = spec;
|
|
857
|
+
}
|
|
858
|
+
validateSpec() {
|
|
859
|
+
const result = { errors: [], warnings: [] };
|
|
860
|
+
// validate spec version
|
|
861
|
+
let validationResult = this.validateSpecVersion();
|
|
862
|
+
result.errors.push(...validationResult.errors);
|
|
863
|
+
// validate spec server
|
|
864
|
+
validationResult = this.validateSpecServer();
|
|
865
|
+
result.errors.push(...validationResult.errors);
|
|
866
|
+
// validate no supported API
|
|
867
|
+
validationResult = this.validateSpecNoSupportAPI();
|
|
868
|
+
result.errors.push(...validationResult.errors);
|
|
869
|
+
// validate operationId missing
|
|
870
|
+
validationResult = this.validateSpecOperationId();
|
|
871
|
+
result.warnings.push(...validationResult.warnings);
|
|
872
|
+
return result;
|
|
873
|
+
}
|
|
874
|
+
validateAPI(method, path) {
|
|
875
|
+
const result = { isValid: true, reason: [] };
|
|
876
|
+
method = method.toLocaleLowerCase();
|
|
877
|
+
// validate method and path
|
|
878
|
+
const methodAndPathResult = this.validateMethodAndPath(method, path);
|
|
879
|
+
if (!methodAndPathResult.isValid) {
|
|
880
|
+
return methodAndPathResult;
|
|
881
|
+
}
|
|
882
|
+
const operationObject = this.spec.paths[path][method];
|
|
883
|
+
// validate auth
|
|
884
|
+
const authCheckResult = this.validateAuth(method, path);
|
|
885
|
+
result.reason.push(...authCheckResult.reason);
|
|
886
|
+
// validate operationId
|
|
887
|
+
if (!this.options.allowMissingId && !operationObject.operationId) {
|
|
888
|
+
result.reason.push(exports.ErrorType.MissingOperationId);
|
|
889
|
+
}
|
|
890
|
+
// validate server
|
|
891
|
+
const validateServerResult = this.validateServer(method, path);
|
|
892
|
+
result.reason.push(...validateServerResult.reason);
|
|
893
|
+
// validate response
|
|
894
|
+
const validateResponseResult = this.validateResponse(method, path);
|
|
895
|
+
result.reason.push(...validateResponseResult.reason);
|
|
896
|
+
// validate requestBody
|
|
897
|
+
const requestBody = operationObject.requestBody;
|
|
898
|
+
const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
|
|
899
|
+
if (requestJsonBody) {
|
|
900
|
+
const requestBodySchema = requestJsonBody.schema;
|
|
901
|
+
if (requestBodySchema.type !== "object") {
|
|
902
|
+
result.reason.push(exports.ErrorType.PostBodySchemaIsNotJson);
|
|
1022
903
|
}
|
|
1023
|
-
|
|
904
|
+
const requestBodyParamResult = this.checkPostBodySchema(requestBodySchema, requestBody.required);
|
|
905
|
+
result.reason.push(...requestBodyParamResult.reason);
|
|
906
|
+
}
|
|
907
|
+
// validate parameters
|
|
908
|
+
const paramObject = operationObject.parameters;
|
|
909
|
+
const paramResult = this.checkParamSchema(paramObject);
|
|
910
|
+
result.reason.push(...paramResult.reason);
|
|
911
|
+
if (result.reason.length > 0) {
|
|
912
|
+
result.isValid = false;
|
|
913
|
+
}
|
|
914
|
+
return result;
|
|
1024
915
|
}
|
|
1025
|
-
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
// Copyright (c) Microsoft Corporation.
|
|
919
|
+
class SMEValidator extends Validator {
|
|
920
|
+
constructor(spec, options) {
|
|
921
|
+
super();
|
|
922
|
+
this.projectType = exports.ProjectType.SME;
|
|
923
|
+
this.options = options;
|
|
924
|
+
this.spec = spec;
|
|
925
|
+
}
|
|
926
|
+
validateSpec() {
|
|
927
|
+
const result = { errors: [], warnings: [] };
|
|
928
|
+
// validate spec version
|
|
929
|
+
let validationResult = this.validateSpecVersion();
|
|
930
|
+
result.errors.push(...validationResult.errors);
|
|
931
|
+
// validate spec server
|
|
932
|
+
validationResult = this.validateSpecServer();
|
|
933
|
+
result.errors.push(...validationResult.errors);
|
|
934
|
+
// validate no supported API
|
|
935
|
+
validationResult = this.validateSpecNoSupportAPI();
|
|
936
|
+
result.errors.push(...validationResult.errors);
|
|
937
|
+
// validate operationId missing
|
|
938
|
+
if (this.options.allowMissingId) {
|
|
939
|
+
validationResult = this.validateSpecOperationId();
|
|
940
|
+
result.warnings.push(...validationResult.warnings);
|
|
941
|
+
}
|
|
942
|
+
return result;
|
|
943
|
+
}
|
|
944
|
+
validateAPI(method, path) {
|
|
945
|
+
const result = { isValid: true, reason: [] };
|
|
946
|
+
method = method.toLocaleLowerCase();
|
|
947
|
+
// validate method and path
|
|
948
|
+
const methodAndPathResult = this.validateMethodAndPath(method, path);
|
|
949
|
+
if (!methodAndPathResult.isValid) {
|
|
950
|
+
return methodAndPathResult;
|
|
951
|
+
}
|
|
952
|
+
const operationObject = this.spec.paths[path][method];
|
|
953
|
+
// validate auth
|
|
954
|
+
const authCheckResult = this.validateAuth(method, path);
|
|
955
|
+
result.reason.push(...authCheckResult.reason);
|
|
956
|
+
// validate operationId
|
|
957
|
+
if (!this.options.allowMissingId && !operationObject.operationId) {
|
|
958
|
+
result.reason.push(exports.ErrorType.MissingOperationId);
|
|
959
|
+
}
|
|
960
|
+
// validate server
|
|
961
|
+
const validateServerResult = this.validateServer(method, path);
|
|
962
|
+
result.reason.push(...validateServerResult.reason);
|
|
963
|
+
// validate response
|
|
964
|
+
const validateResponseResult = this.validateResponse(method, path);
|
|
965
|
+
result.reason.push(...validateResponseResult.reason);
|
|
966
|
+
let postBodyResult = {
|
|
967
|
+
requiredNum: 0,
|
|
968
|
+
optionalNum: 0,
|
|
969
|
+
isValid: true,
|
|
970
|
+
reason: [],
|
|
971
|
+
};
|
|
972
|
+
// validate requestBody
|
|
973
|
+
const requestBody = operationObject.requestBody;
|
|
974
|
+
const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
|
|
975
|
+
if (Utils.containMultipleMediaTypes(requestBody)) {
|
|
976
|
+
result.reason.push(exports.ErrorType.PostBodyContainMultipleMediaTypes);
|
|
977
|
+
}
|
|
978
|
+
if (requestJsonBody) {
|
|
979
|
+
const requestBodySchema = requestJsonBody.schema;
|
|
980
|
+
postBodyResult = this.checkPostBodySchema(requestBodySchema, requestBody.required);
|
|
981
|
+
result.reason.push(...postBodyResult.reason);
|
|
982
|
+
}
|
|
983
|
+
// validate parameters
|
|
984
|
+
const paramObject = operationObject.parameters;
|
|
985
|
+
const paramResult = this.checkParamSchema(paramObject);
|
|
986
|
+
result.reason.push(...paramResult.reason);
|
|
987
|
+
// validate total parameters count
|
|
988
|
+
if (paramResult.isValid && postBodyResult.isValid) {
|
|
989
|
+
const paramCountResult = this.validateParamCount(postBodyResult, paramResult);
|
|
990
|
+
result.reason.push(...paramCountResult.reason);
|
|
991
|
+
}
|
|
992
|
+
if (result.reason.length > 0) {
|
|
993
|
+
result.isValid = false;
|
|
994
|
+
}
|
|
995
|
+
return result;
|
|
996
|
+
}
|
|
997
|
+
validateParamCount(postBodyResult, paramResult) {
|
|
998
|
+
const result = { isValid: true, reason: [] };
|
|
999
|
+
const totalRequiredParams = postBodyResult.requiredNum + paramResult.requiredNum;
|
|
1000
|
+
const totalParams = totalRequiredParams + postBodyResult.optionalNum + paramResult.optionalNum;
|
|
1001
|
+
if (totalRequiredParams > 1) {
|
|
1002
|
+
if (!this.options.allowMultipleParameters ||
|
|
1003
|
+
totalRequiredParams > SMEValidator.SMERequiredParamsMaxNum) {
|
|
1004
|
+
result.reason.push(exports.ErrorType.ExceededRequiredParamsLimit);
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
else if (totalParams === 0) {
|
|
1008
|
+
result.reason.push(exports.ErrorType.NoParameter);
|
|
1009
|
+
}
|
|
1010
|
+
return result;
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
SMEValidator.SMERequiredParamsMaxNum = 5;
|
|
1014
|
+
|
|
1015
|
+
// Copyright (c) Microsoft Corporation.
|
|
1016
|
+
class TeamsAIValidator extends Validator {
|
|
1017
|
+
constructor(spec, options) {
|
|
1018
|
+
super();
|
|
1019
|
+
this.projectType = exports.ProjectType.TeamsAi;
|
|
1020
|
+
this.options = options;
|
|
1021
|
+
this.spec = spec;
|
|
1022
|
+
}
|
|
1023
|
+
validateSpec() {
|
|
1024
|
+
const result = { errors: [], warnings: [] };
|
|
1025
|
+
// validate spec server
|
|
1026
|
+
let validationResult = this.validateSpecServer();
|
|
1027
|
+
result.errors.push(...validationResult.errors);
|
|
1028
|
+
// validate no supported API
|
|
1029
|
+
validationResult = this.validateSpecNoSupportAPI();
|
|
1030
|
+
result.errors.push(...validationResult.errors);
|
|
1031
|
+
return result;
|
|
1032
|
+
}
|
|
1033
|
+
validateAPI(method, path) {
|
|
1034
|
+
const result = { isValid: true, reason: [] };
|
|
1035
|
+
method = method.toLocaleLowerCase();
|
|
1036
|
+
// validate method and path
|
|
1037
|
+
const methodAndPathResult = this.validateMethodAndPath(method, path);
|
|
1038
|
+
if (!methodAndPathResult.isValid) {
|
|
1039
|
+
return methodAndPathResult;
|
|
1040
|
+
}
|
|
1041
|
+
const operationObject = this.spec.paths[path][method];
|
|
1042
|
+
// validate operationId
|
|
1043
|
+
if (!this.options.allowMissingId && !operationObject.operationId) {
|
|
1044
|
+
result.reason.push(exports.ErrorType.MissingOperationId);
|
|
1045
|
+
}
|
|
1046
|
+
// validate server
|
|
1047
|
+
const validateServerResult = this.validateServer(method, path);
|
|
1048
|
+
result.reason.push(...validateServerResult.reason);
|
|
1049
|
+
if (result.reason.length > 0) {
|
|
1050
|
+
result.isValid = false;
|
|
1051
|
+
}
|
|
1052
|
+
return result;
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
class ValidatorFactory {
|
|
1057
|
+
static create(spec, options) {
|
|
1026
1058
|
var _a;
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1059
|
+
const type = (_a = options.projectType) !== null && _a !== void 0 ? _a : exports.ProjectType.SME;
|
|
1060
|
+
switch (type) {
|
|
1061
|
+
case exports.ProjectType.SME:
|
|
1062
|
+
return new SMEValidator(spec, options);
|
|
1063
|
+
case exports.ProjectType.Copilot:
|
|
1064
|
+
return new CopilotValidator(spec, options);
|
|
1065
|
+
case exports.ProjectType.TeamsAi:
|
|
1066
|
+
return new TeamsAIValidator(spec, options);
|
|
1067
|
+
default:
|
|
1068
|
+
throw new Error(`Invalid project type: ${type}`);
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
// Copyright (c) Microsoft Corporation.
|
|
1074
|
+
class SpecFilter {
|
|
1075
|
+
static specFilter(filter, unResolveSpec, resolvedSpec, options) {
|
|
1076
|
+
var _a;
|
|
1077
|
+
try {
|
|
1078
|
+
const newSpec = Object.assign({}, unResolveSpec);
|
|
1079
|
+
const newPaths = {};
|
|
1080
|
+
for (const filterItem of filter) {
|
|
1081
|
+
const [method, path] = filterItem.split(" ");
|
|
1082
|
+
const methodName = method.toLowerCase();
|
|
1083
|
+
const pathObj = (_a = resolvedSpec.paths) === null || _a === void 0 ? void 0 : _a[path];
|
|
1084
|
+
if (ConstantString.AllOperationMethods.includes(methodName) &&
|
|
1085
|
+
pathObj &&
|
|
1086
|
+
pathObj[methodName]) {
|
|
1087
|
+
const validator = ValidatorFactory.create(resolvedSpec, options);
|
|
1088
|
+
const validateResult = validator.validateAPI(methodName, path);
|
|
1089
|
+
if (!validateResult.isValid) {
|
|
1090
|
+
continue;
|
|
1091
|
+
}
|
|
1092
|
+
if (!newPaths[path]) {
|
|
1093
|
+
newPaths[path] = Object.assign({}, unResolveSpec.paths[path]);
|
|
1094
|
+
for (const m of ConstantString.AllOperationMethods) {
|
|
1095
|
+
delete newPaths[path][m];
|
|
1054
1096
|
}
|
|
1055
1097
|
}
|
|
1098
|
+
newPaths[path][methodName] = unResolveSpec.paths[path][methodName];
|
|
1099
|
+
// Add the operationId if missing
|
|
1100
|
+
if (!newPaths[path][methodName].operationId) {
|
|
1101
|
+
newPaths[path][methodName].operationId = `${methodName}${Utils.convertPathToCamelCase(path)}`;
|
|
1102
|
+
}
|
|
1056
1103
|
}
|
|
1057
1104
|
}
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1105
|
+
newSpec.paths = newPaths;
|
|
1106
|
+
return newSpec;
|
|
1107
|
+
}
|
|
1108
|
+
catch (err) {
|
|
1109
|
+
throw new SpecParserError(err.toString(), exports.ErrorType.FilterSpecFailed);
|
|
1110
|
+
}
|
|
1064
1111
|
}
|
|
1065
1112
|
}
|
|
1066
1113
|
|
|
@@ -1068,7 +1115,7 @@ class ManifestUpdater {
|
|
|
1068
1115
|
class AdaptiveCardGenerator {
|
|
1069
1116
|
static generateAdaptiveCard(operationItem) {
|
|
1070
1117
|
try {
|
|
1071
|
-
const json = Utils.getResponseJson(operationItem);
|
|
1118
|
+
const { json } = Utils.getResponseJson(operationItem);
|
|
1072
1119
|
let cardBody = [];
|
|
1073
1120
|
let schema = json.schema;
|
|
1074
1121
|
let jsonPath = "$";
|
|
@@ -1234,6 +1281,27 @@ function wrapAdaptiveCard(card, jsonPath) {
|
|
|
1234
1281
|
};
|
|
1235
1282
|
return result;
|
|
1236
1283
|
}
|
|
1284
|
+
function wrapResponseSemantics(card, jsonPath) {
|
|
1285
|
+
const props = inferProperties(card);
|
|
1286
|
+
const dataPath = jsonPath === "$" ? "$" : "$." + jsonPath;
|
|
1287
|
+
const result = {
|
|
1288
|
+
data_path: dataPath,
|
|
1289
|
+
};
|
|
1290
|
+
if (props.title || props.subtitle || props.imageUrl) {
|
|
1291
|
+
result.properties = {};
|
|
1292
|
+
if (props.title) {
|
|
1293
|
+
result.properties.title = "$." + props.title;
|
|
1294
|
+
}
|
|
1295
|
+
if (props.subtitle) {
|
|
1296
|
+
result.properties.subtitle = "$." + props.subtitle;
|
|
1297
|
+
}
|
|
1298
|
+
if (props.imageUrl) {
|
|
1299
|
+
result.properties.url = "$." + props.imageUrl;
|
|
1300
|
+
}
|
|
1301
|
+
}
|
|
1302
|
+
result.static_template = card;
|
|
1303
|
+
return result;
|
|
1304
|
+
}
|
|
1237
1305
|
/**
|
|
1238
1306
|
* Infers the preview card template from an Adaptive Card and a JSON path.
|
|
1239
1307
|
* The preview card template includes a title and an optional subtitle and image.
|
|
@@ -1246,11 +1314,29 @@ function wrapAdaptiveCard(card, jsonPath) {
|
|
|
1246
1314
|
* @returns The inferred preview card template.
|
|
1247
1315
|
*/
|
|
1248
1316
|
function inferPreviewCardTemplate(card) {
|
|
1249
|
-
var _a;
|
|
1250
1317
|
const result = {
|
|
1251
|
-
title: "",
|
|
1318
|
+
title: "result",
|
|
1252
1319
|
};
|
|
1253
|
-
const
|
|
1320
|
+
const inferredProperties = inferProperties(card);
|
|
1321
|
+
if (inferredProperties.title) {
|
|
1322
|
+
result.title = `\${if(${inferredProperties.title}, ${inferredProperties.title}, 'N/A')}`;
|
|
1323
|
+
}
|
|
1324
|
+
if (inferredProperties.subtitle) {
|
|
1325
|
+
result.subtitle = `\${if(${inferredProperties.subtitle}, ${inferredProperties.subtitle}, 'N/A')}`;
|
|
1326
|
+
}
|
|
1327
|
+
if (inferredProperties.imageUrl) {
|
|
1328
|
+
result.image = {
|
|
1329
|
+
url: `\${${inferredProperties.imageUrl}}`,
|
|
1330
|
+
alt: `\${if(${inferredProperties.imageUrl}, ${inferredProperties.imageUrl}, 'N/A')}`,
|
|
1331
|
+
$when: `\${${inferredProperties.imageUrl} != null}`,
|
|
1332
|
+
};
|
|
1333
|
+
}
|
|
1334
|
+
return result;
|
|
1335
|
+
}
|
|
1336
|
+
function inferProperties(card) {
|
|
1337
|
+
var _a;
|
|
1338
|
+
const result = {};
|
|
1339
|
+
const nameSet = new Set();
|
|
1254
1340
|
let rootObject;
|
|
1255
1341
|
if (((_a = card.body[0]) === null || _a === void 0 ? void 0 : _a.type) === ConstantString.ContainerType) {
|
|
1256
1342
|
rootObject = card.body[0].items;
|
|
@@ -1263,56 +1349,407 @@ function inferPreviewCardTemplate(card) {
|
|
|
1263
1349
|
const textElement = element;
|
|
1264
1350
|
const index = textElement.text.indexOf("${if(");
|
|
1265
1351
|
if (index > 0) {
|
|
1266
|
-
|
|
1267
|
-
|
|
1352
|
+
const text = textElement.text.substring(index);
|
|
1353
|
+
const match = text.match(/\${if\(([^,]+),/);
|
|
1354
|
+
const property = match ? match[1] : "";
|
|
1355
|
+
if (property) {
|
|
1356
|
+
nameSet.add(property);
|
|
1357
|
+
}
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
1360
|
+
else if (element.type === ConstantString.ImageType) {
|
|
1361
|
+
const imageElement = element;
|
|
1362
|
+
const match = imageElement.url.match(/\${([^,]+)}/);
|
|
1363
|
+
const property = match ? match[1] : "";
|
|
1364
|
+
if (property) {
|
|
1365
|
+
nameSet.add(property);
|
|
1268
1366
|
}
|
|
1269
1367
|
}
|
|
1270
1368
|
}
|
|
1271
|
-
for (const
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
textBlockElements.delete(element);
|
|
1369
|
+
for (const name of nameSet) {
|
|
1370
|
+
if (!result.title && Utils.isWellKnownName(name, ConstantString.WellknownTitleName)) {
|
|
1371
|
+
result.title = name;
|
|
1372
|
+
nameSet.delete(name);
|
|
1276
1373
|
}
|
|
1277
1374
|
else if (!result.subtitle &&
|
|
1278
|
-
Utils.isWellKnownName(
|
|
1279
|
-
result.subtitle =
|
|
1280
|
-
|
|
1375
|
+
Utils.isWellKnownName(name, ConstantString.WellknownSubtitleName)) {
|
|
1376
|
+
result.subtitle = name;
|
|
1377
|
+
nameSet.delete(name);
|
|
1281
1378
|
}
|
|
1282
|
-
else if (!result.
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
if (property) {
|
|
1286
|
-
result.image = {
|
|
1287
|
-
url: `\${${property}}`,
|
|
1288
|
-
alt: text,
|
|
1289
|
-
$when: `\${${property} != null}`,
|
|
1290
|
-
};
|
|
1291
|
-
}
|
|
1292
|
-
textBlockElements.delete(element);
|
|
1379
|
+
else if (!result.imageUrl && Utils.isWellKnownName(name, ConstantString.WellknownImageName)) {
|
|
1380
|
+
result.imageUrl = name;
|
|
1381
|
+
nameSet.delete(name);
|
|
1293
1382
|
}
|
|
1294
1383
|
}
|
|
1295
|
-
for (const
|
|
1296
|
-
const text = element.text;
|
|
1384
|
+
for (const name of nameSet) {
|
|
1297
1385
|
if (!result.title) {
|
|
1298
|
-
result.title =
|
|
1299
|
-
|
|
1386
|
+
result.title = name;
|
|
1387
|
+
nameSet.delete(name);
|
|
1300
1388
|
}
|
|
1301
1389
|
else if (!result.subtitle) {
|
|
1302
|
-
result.subtitle =
|
|
1303
|
-
|
|
1390
|
+
result.subtitle = name;
|
|
1391
|
+
nameSet.delete(name);
|
|
1304
1392
|
}
|
|
1305
1393
|
}
|
|
1306
1394
|
if (!result.title && result.subtitle) {
|
|
1307
1395
|
result.title = result.subtitle;
|
|
1308
1396
|
delete result.subtitle;
|
|
1309
1397
|
}
|
|
1310
|
-
if (!result.title) {
|
|
1311
|
-
result.title = "result";
|
|
1312
|
-
}
|
|
1313
1398
|
return result;
|
|
1314
1399
|
}
|
|
1315
1400
|
|
|
1401
|
+
// Copyright (c) Microsoft Corporation.
|
|
1402
|
+
class ManifestUpdater {
|
|
1403
|
+
static updateManifestWithAiPlugin(manifestPath, outputSpecPath, apiPluginFilePath, spec, options, authInfo) {
|
|
1404
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1405
|
+
const manifest = yield fs__default['default'].readJSON(manifestPath);
|
|
1406
|
+
const apiPluginRelativePath = ManifestUpdater.getRelativePath(manifestPath, apiPluginFilePath);
|
|
1407
|
+
manifest.plugins = [
|
|
1408
|
+
{
|
|
1409
|
+
file: apiPluginRelativePath,
|
|
1410
|
+
id: ConstantString.DefaultPluginId,
|
|
1411
|
+
},
|
|
1412
|
+
];
|
|
1413
|
+
const appName = this.removeEnvs(manifest.name.short);
|
|
1414
|
+
ManifestUpdater.updateManifestDescription(manifest, spec);
|
|
1415
|
+
const specRelativePath = ManifestUpdater.getRelativePath(manifestPath, outputSpecPath);
|
|
1416
|
+
const apiPlugin = yield ManifestUpdater.generatePluginManifestSchema(spec, specRelativePath, apiPluginFilePath, appName, authInfo, options);
|
|
1417
|
+
return [manifest, apiPlugin];
|
|
1418
|
+
});
|
|
1419
|
+
}
|
|
1420
|
+
static updateManifestDescription(manifest, spec) {
|
|
1421
|
+
var _a, _b;
|
|
1422
|
+
manifest.description = {
|
|
1423
|
+
short: spec.info.title.slice(0, ConstantString.ShortDescriptionMaxLens),
|
|
1424
|
+
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),
|
|
1425
|
+
};
|
|
1426
|
+
}
|
|
1427
|
+
static mapOpenAPISchemaToFuncParam(schema, method, pathUrl) {
|
|
1428
|
+
let parameter;
|
|
1429
|
+
if (schema.type === "array") {
|
|
1430
|
+
const items = schema.items;
|
|
1431
|
+
parameter = {
|
|
1432
|
+
type: "array",
|
|
1433
|
+
items: ManifestUpdater.mapOpenAPISchemaToFuncParam(items, method, pathUrl),
|
|
1434
|
+
};
|
|
1435
|
+
}
|
|
1436
|
+
else if (schema.type === "string" ||
|
|
1437
|
+
schema.type === "boolean" ||
|
|
1438
|
+
schema.type === "integer" ||
|
|
1439
|
+
schema.type === "number") {
|
|
1440
|
+
parameter = {
|
|
1441
|
+
type: schema.type,
|
|
1442
|
+
};
|
|
1443
|
+
}
|
|
1444
|
+
else {
|
|
1445
|
+
throw new SpecParserError(Utils.format(ConstantString.UnsupportedSchema, method, pathUrl, JSON.stringify(schema)), exports.ErrorType.UpdateManifestFailed);
|
|
1446
|
+
}
|
|
1447
|
+
if (schema.enum) {
|
|
1448
|
+
parameter.enum = schema.enum;
|
|
1449
|
+
}
|
|
1450
|
+
if (schema.description) {
|
|
1451
|
+
parameter.description = schema.description;
|
|
1452
|
+
}
|
|
1453
|
+
if (schema.default) {
|
|
1454
|
+
parameter.default = schema.default;
|
|
1455
|
+
}
|
|
1456
|
+
return parameter;
|
|
1457
|
+
}
|
|
1458
|
+
static generatePluginManifestSchema(spec, specRelativePath, apiPluginFilePath, appName, authInfo, options) {
|
|
1459
|
+
var _a, _b, _c, _d, _e;
|
|
1460
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1461
|
+
const functions = [];
|
|
1462
|
+
const functionNames = [];
|
|
1463
|
+
const conversationStarters = [];
|
|
1464
|
+
const paths = spec.paths;
|
|
1465
|
+
const pluginAuthObj = {
|
|
1466
|
+
type: "None",
|
|
1467
|
+
};
|
|
1468
|
+
if (authInfo) {
|
|
1469
|
+
if (Utils.isOAuthWithAuthCodeFlow(authInfo.authScheme)) {
|
|
1470
|
+
pluginAuthObj.type = "OAuthPluginVault";
|
|
1471
|
+
}
|
|
1472
|
+
else if (Utils.isBearerTokenAuth(authInfo.authScheme)) {
|
|
1473
|
+
pluginAuthObj.type = "ApiKeyPluginVault";
|
|
1474
|
+
}
|
|
1475
|
+
if (pluginAuthObj.type !== "None") {
|
|
1476
|
+
pluginAuthObj.reference_id = `${Utils.getSafeRegistrationIdEnvName(authInfo.name)}_REGISTRATION_ID`;
|
|
1477
|
+
}
|
|
1478
|
+
}
|
|
1479
|
+
for (const pathUrl in paths) {
|
|
1480
|
+
const pathItem = paths[pathUrl];
|
|
1481
|
+
if (pathItem) {
|
|
1482
|
+
const operations = pathItem;
|
|
1483
|
+
for (const method in operations) {
|
|
1484
|
+
if (options.allowMethods.includes(method)) {
|
|
1485
|
+
const operationItem = operations[method];
|
|
1486
|
+
const confirmationBodies = [];
|
|
1487
|
+
if (operationItem) {
|
|
1488
|
+
const operationId = operationItem.operationId;
|
|
1489
|
+
const description = (_a = operationItem.description) !== null && _a !== void 0 ? _a : "";
|
|
1490
|
+
const paramObject = operationItem.parameters;
|
|
1491
|
+
const requestBody = operationItem.requestBody;
|
|
1492
|
+
const parameters = {
|
|
1493
|
+
type: "object",
|
|
1494
|
+
properties: {},
|
|
1495
|
+
required: [],
|
|
1496
|
+
};
|
|
1497
|
+
if (paramObject) {
|
|
1498
|
+
for (let i = 0; i < paramObject.length; i++) {
|
|
1499
|
+
const param = paramObject[i];
|
|
1500
|
+
const schema = param.schema;
|
|
1501
|
+
parameters.properties[param.name] = ManifestUpdater.mapOpenAPISchemaToFuncParam(schema, method, pathUrl);
|
|
1502
|
+
confirmationBodies.push(ManifestUpdater.getConfirmationBodyItem(param.name));
|
|
1503
|
+
if (param.required) {
|
|
1504
|
+
parameters.required.push(param.name);
|
|
1505
|
+
}
|
|
1506
|
+
if (!parameters.properties[param.name].description) {
|
|
1507
|
+
parameters.properties[param.name].description = (_b = param.description) !== null && _b !== void 0 ? _b : "";
|
|
1508
|
+
}
|
|
1509
|
+
}
|
|
1510
|
+
}
|
|
1511
|
+
if (requestBody) {
|
|
1512
|
+
const requestJsonBody = requestBody.content["application/json"];
|
|
1513
|
+
const requestBodySchema = requestJsonBody.schema;
|
|
1514
|
+
if (requestBodySchema.type === "object") {
|
|
1515
|
+
if (requestBodySchema.required) {
|
|
1516
|
+
parameters.required.push(...requestBodySchema.required);
|
|
1517
|
+
}
|
|
1518
|
+
for (const property in requestBodySchema.properties) {
|
|
1519
|
+
const schema = requestBodySchema.properties[property];
|
|
1520
|
+
parameters.properties[property] = ManifestUpdater.mapOpenAPISchemaToFuncParam(schema, method, pathUrl);
|
|
1521
|
+
confirmationBodies.push(ManifestUpdater.getConfirmationBodyItem(property));
|
|
1522
|
+
}
|
|
1523
|
+
}
|
|
1524
|
+
else {
|
|
1525
|
+
throw new SpecParserError(Utils.format(ConstantString.UnsupportedSchema, method, pathUrl, JSON.stringify(requestBodySchema)), exports.ErrorType.UpdateManifestFailed);
|
|
1526
|
+
}
|
|
1527
|
+
}
|
|
1528
|
+
const funcObj = {
|
|
1529
|
+
name: operationId,
|
|
1530
|
+
description: description,
|
|
1531
|
+
};
|
|
1532
|
+
if (paramObject || requestBody) {
|
|
1533
|
+
funcObj.parameters = parameters;
|
|
1534
|
+
}
|
|
1535
|
+
if (options.allowResponseSemantics) {
|
|
1536
|
+
const [card, jsonPath] = AdaptiveCardGenerator.generateAdaptiveCard(operationItem);
|
|
1537
|
+
const responseSemantic = wrapResponseSemantics(card, jsonPath);
|
|
1538
|
+
funcObj.capabilities = {
|
|
1539
|
+
response_semantics: responseSemantic,
|
|
1540
|
+
};
|
|
1541
|
+
}
|
|
1542
|
+
if (options.allowConfirmation && method !== ConstantString.GetMethod) {
|
|
1543
|
+
if (!funcObj.capabilities) {
|
|
1544
|
+
funcObj.capabilities = {};
|
|
1545
|
+
}
|
|
1546
|
+
funcObj.capabilities.confirmation = {
|
|
1547
|
+
type: "AdaptiveCard",
|
|
1548
|
+
title: (_c = operationItem.summary) !== null && _c !== void 0 ? _c : description,
|
|
1549
|
+
};
|
|
1550
|
+
if (confirmationBodies.length > 0) {
|
|
1551
|
+
funcObj.capabilities.confirmation.body = confirmationBodies.join("\n");
|
|
1552
|
+
}
|
|
1553
|
+
}
|
|
1554
|
+
functions.push(funcObj);
|
|
1555
|
+
functionNames.push(operationId);
|
|
1556
|
+
if (description) {
|
|
1557
|
+
conversationStarters.push(description);
|
|
1558
|
+
}
|
|
1559
|
+
}
|
|
1560
|
+
}
|
|
1561
|
+
}
|
|
1562
|
+
}
|
|
1563
|
+
}
|
|
1564
|
+
let apiPlugin;
|
|
1565
|
+
if (yield fs__default['default'].pathExists(apiPluginFilePath)) {
|
|
1566
|
+
apiPlugin = yield fs__default['default'].readJSON(apiPluginFilePath);
|
|
1567
|
+
}
|
|
1568
|
+
else {
|
|
1569
|
+
apiPlugin = {
|
|
1570
|
+
schema_version: "v2.1",
|
|
1571
|
+
name_for_human: "",
|
|
1572
|
+
description_for_human: "",
|
|
1573
|
+
namespace: "",
|
|
1574
|
+
functions: [],
|
|
1575
|
+
runtimes: [],
|
|
1576
|
+
};
|
|
1577
|
+
}
|
|
1578
|
+
apiPlugin.functions = apiPlugin.functions || [];
|
|
1579
|
+
for (const func of functions) {
|
|
1580
|
+
const index = (_d = apiPlugin.functions) === null || _d === void 0 ? void 0 : _d.findIndex((f) => f.name === func.name);
|
|
1581
|
+
if (index === -1) {
|
|
1582
|
+
apiPlugin.functions.push(func);
|
|
1583
|
+
}
|
|
1584
|
+
else {
|
|
1585
|
+
apiPlugin.functions[index] = func;
|
|
1586
|
+
}
|
|
1587
|
+
}
|
|
1588
|
+
apiPlugin.runtimes = apiPlugin.runtimes || [];
|
|
1589
|
+
const index = apiPlugin.runtimes.findIndex((runtime) => {
|
|
1590
|
+
var _a, _b;
|
|
1591
|
+
return runtime.spec.url === specRelativePath &&
|
|
1592
|
+
runtime.type === "OpenApi" &&
|
|
1593
|
+
((_b = (_a = runtime.auth) === null || _a === void 0 ? void 0 : _a.type) !== null && _b !== void 0 ? _b : "None") === pluginAuthObj.type;
|
|
1594
|
+
});
|
|
1595
|
+
if (index === -1) {
|
|
1596
|
+
apiPlugin.runtimes.push({
|
|
1597
|
+
type: "OpenApi",
|
|
1598
|
+
auth: pluginAuthObj,
|
|
1599
|
+
spec: {
|
|
1600
|
+
url: specRelativePath,
|
|
1601
|
+
},
|
|
1602
|
+
run_for_functions: functionNames,
|
|
1603
|
+
});
|
|
1604
|
+
}
|
|
1605
|
+
else {
|
|
1606
|
+
apiPlugin.runtimes[index].run_for_functions = functionNames;
|
|
1607
|
+
}
|
|
1608
|
+
if (!apiPlugin.name_for_human) {
|
|
1609
|
+
apiPlugin.name_for_human = appName;
|
|
1610
|
+
}
|
|
1611
|
+
if (!apiPlugin.namespace) {
|
|
1612
|
+
apiPlugin.namespace = ManifestUpdater.removeAllSpecialCharacters(appName);
|
|
1613
|
+
}
|
|
1614
|
+
if (!apiPlugin.description_for_human) {
|
|
1615
|
+
apiPlugin.description_for_human =
|
|
1616
|
+
(_e = spec.info.description) !== null && _e !== void 0 ? _e : "<Please add description of the plugin>";
|
|
1617
|
+
}
|
|
1618
|
+
if (options.allowConversationStarters && conversationStarters.length > 0) {
|
|
1619
|
+
if (!apiPlugin.capabilities) {
|
|
1620
|
+
apiPlugin.capabilities = {
|
|
1621
|
+
localization: {},
|
|
1622
|
+
};
|
|
1623
|
+
}
|
|
1624
|
+
if (!apiPlugin.capabilities.conversation_starters) {
|
|
1625
|
+
apiPlugin.capabilities.conversation_starters = conversationStarters
|
|
1626
|
+
.slice(0, 5)
|
|
1627
|
+
.map((text) => ({ text }));
|
|
1628
|
+
}
|
|
1629
|
+
}
|
|
1630
|
+
return apiPlugin;
|
|
1631
|
+
});
|
|
1632
|
+
}
|
|
1633
|
+
static updateManifest(manifestPath, outputSpecPath, spec, options, adaptiveCardFolder, authInfo) {
|
|
1634
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1635
|
+
try {
|
|
1636
|
+
const originalManifest = yield fs__default['default'].readJSON(manifestPath);
|
|
1637
|
+
const updatedPart = {};
|
|
1638
|
+
updatedPart.composeExtensions = [];
|
|
1639
|
+
let warnings = [];
|
|
1640
|
+
if (options.projectType === exports.ProjectType.SME) {
|
|
1641
|
+
const updateResult = yield ManifestUpdater.generateCommands(spec, manifestPath, options, adaptiveCardFolder);
|
|
1642
|
+
const commands = updateResult[0];
|
|
1643
|
+
warnings = updateResult[1];
|
|
1644
|
+
const composeExtension = {
|
|
1645
|
+
composeExtensionType: "apiBased",
|
|
1646
|
+
apiSpecificationFile: ManifestUpdater.getRelativePath(manifestPath, outputSpecPath),
|
|
1647
|
+
commands: commands,
|
|
1648
|
+
};
|
|
1649
|
+
if (authInfo) {
|
|
1650
|
+
const auth = authInfo.authScheme;
|
|
1651
|
+
if (Utils.isAPIKeyAuth(auth) || Utils.isBearerTokenAuth(auth)) {
|
|
1652
|
+
const safeApiSecretRegistrationId = Utils.getSafeRegistrationIdEnvName(`${authInfo.name}_${ConstantString.RegistrationIdPostfix}`);
|
|
1653
|
+
composeExtension.authorization = {
|
|
1654
|
+
authType: "apiSecretServiceAuth",
|
|
1655
|
+
apiSecretServiceAuthConfiguration: {
|
|
1656
|
+
apiSecretRegistrationId: `\${{${safeApiSecretRegistrationId}}}`,
|
|
1657
|
+
},
|
|
1658
|
+
};
|
|
1659
|
+
}
|
|
1660
|
+
else if (Utils.isOAuthWithAuthCodeFlow(auth)) {
|
|
1661
|
+
const safeOAuth2RegistrationId = Utils.getSafeRegistrationIdEnvName(`${authInfo.name}_${ConstantString.OAuthRegistrationIdPostFix}`);
|
|
1662
|
+
composeExtension.authorization = {
|
|
1663
|
+
authType: "oAuth2.0",
|
|
1664
|
+
oAuthConfiguration: {
|
|
1665
|
+
oauthConfigurationId: `\${{${safeOAuth2RegistrationId}}}`,
|
|
1666
|
+
},
|
|
1667
|
+
};
|
|
1668
|
+
updatedPart.webApplicationInfo = {
|
|
1669
|
+
id: "${{AAD_APP_CLIENT_ID}}",
|
|
1670
|
+
resource: "api://${{DOMAIN}}/${{AAD_APP_CLIENT_ID}}",
|
|
1671
|
+
};
|
|
1672
|
+
}
|
|
1673
|
+
}
|
|
1674
|
+
updatedPart.composeExtensions = [composeExtension];
|
|
1675
|
+
}
|
|
1676
|
+
updatedPart.description = originalManifest.description;
|
|
1677
|
+
ManifestUpdater.updateManifestDescription(updatedPart, spec);
|
|
1678
|
+
const updatedManifest = Object.assign(Object.assign({}, originalManifest), updatedPart);
|
|
1679
|
+
return [updatedManifest, warnings];
|
|
1680
|
+
}
|
|
1681
|
+
catch (err) {
|
|
1682
|
+
throw new SpecParserError(err.toString(), exports.ErrorType.UpdateManifestFailed);
|
|
1683
|
+
}
|
|
1684
|
+
});
|
|
1685
|
+
}
|
|
1686
|
+
static generateCommands(spec, manifestPath, options, adaptiveCardFolder) {
|
|
1687
|
+
var _a;
|
|
1688
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1689
|
+
const paths = spec.paths;
|
|
1690
|
+
const commands = [];
|
|
1691
|
+
const warnings = [];
|
|
1692
|
+
if (paths) {
|
|
1693
|
+
for (const pathUrl in paths) {
|
|
1694
|
+
const pathItem = paths[pathUrl];
|
|
1695
|
+
if (pathItem) {
|
|
1696
|
+
const operations = pathItem;
|
|
1697
|
+
// Currently only support GET and POST method
|
|
1698
|
+
for (const method in operations) {
|
|
1699
|
+
if ((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) {
|
|
1700
|
+
const operationItem = operations[method];
|
|
1701
|
+
if (operationItem) {
|
|
1702
|
+
const command = Utils.parseApiInfo(operationItem, options);
|
|
1703
|
+
if (command.parameters &&
|
|
1704
|
+
command.parameters.length >= 1 &&
|
|
1705
|
+
command.parameters.some((param) => param.isRequired)) {
|
|
1706
|
+
command.parameters = command.parameters.filter((param) => param.isRequired);
|
|
1707
|
+
}
|
|
1708
|
+
else if (command.parameters && command.parameters.length > 0) {
|
|
1709
|
+
command.parameters = [command.parameters[0]];
|
|
1710
|
+
warnings.push({
|
|
1711
|
+
type: exports.WarningType.OperationOnlyContainsOptionalParam,
|
|
1712
|
+
content: Utils.format(ConstantString.OperationOnlyContainsOptionalParam, command.id),
|
|
1713
|
+
data: command.id,
|
|
1714
|
+
});
|
|
1715
|
+
}
|
|
1716
|
+
if (adaptiveCardFolder) {
|
|
1717
|
+
const adaptiveCardPath = path__default['default'].join(adaptiveCardFolder, command.id + ".json");
|
|
1718
|
+
command.apiResponseRenderingTemplateFile = (yield fs__default['default'].pathExists(adaptiveCardPath))
|
|
1719
|
+
? ManifestUpdater.getRelativePath(manifestPath, adaptiveCardPath)
|
|
1720
|
+
: "";
|
|
1721
|
+
}
|
|
1722
|
+
commands.push(command);
|
|
1723
|
+
}
|
|
1724
|
+
}
|
|
1725
|
+
}
|
|
1726
|
+
}
|
|
1727
|
+
}
|
|
1728
|
+
}
|
|
1729
|
+
return [commands, warnings];
|
|
1730
|
+
});
|
|
1731
|
+
}
|
|
1732
|
+
static getRelativePath(from, to) {
|
|
1733
|
+
const relativePath = path__default['default'].relative(path__default['default'].dirname(from), to);
|
|
1734
|
+
return path__default['default'].normalize(relativePath).replace(/\\/g, "/");
|
|
1735
|
+
}
|
|
1736
|
+
static removeEnvs(str) {
|
|
1737
|
+
const placeHolderReg = /\${{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*}}/g;
|
|
1738
|
+
const matches = placeHolderReg.exec(str);
|
|
1739
|
+
let newStr = str;
|
|
1740
|
+
if (matches != null) {
|
|
1741
|
+
newStr = newStr.replace(matches[0], "");
|
|
1742
|
+
}
|
|
1743
|
+
return newStr;
|
|
1744
|
+
}
|
|
1745
|
+
static removeAllSpecialCharacters(str) {
|
|
1746
|
+
return str.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
1747
|
+
}
|
|
1748
|
+
static getConfirmationBodyItem(paramName) {
|
|
1749
|
+
return `* **${Utils.updateFirstLetter(paramName)}**: {{function.parameters.${paramName}}}`;
|
|
1750
|
+
}
|
|
1751
|
+
}
|
|
1752
|
+
|
|
1316
1753
|
// Copyright (c) Microsoft Corporation.
|
|
1317
1754
|
/**
|
|
1318
1755
|
* A class that parses an OpenAPI specification file and provides methods to validate, list, and generate artifacts.
|
|
@@ -1332,6 +1769,9 @@ class SpecParser {
|
|
|
1332
1769
|
allowMultipleParameters: false,
|
|
1333
1770
|
allowOauth2: false,
|
|
1334
1771
|
allowMethods: ["get", "post"],
|
|
1772
|
+
allowConversationStarters: false,
|
|
1773
|
+
allowResponseSemantics: false,
|
|
1774
|
+
allowConfirmation: false,
|
|
1335
1775
|
projectType: exports.ProjectType.SME,
|
|
1336
1776
|
};
|
|
1337
1777
|
this.pathOrSpec = pathOrDoc;
|
|
@@ -1357,6 +1797,8 @@ class SpecParser {
|
|
|
1357
1797
|
errors: [{ type: exports.ErrorType.SpecNotValid, content: e.toString() }],
|
|
1358
1798
|
};
|
|
1359
1799
|
}
|
|
1800
|
+
const errors = [];
|
|
1801
|
+
const warnings = [];
|
|
1360
1802
|
if (!this.options.allowSwagger && this.isSwaggerFile) {
|
|
1361
1803
|
return {
|
|
1362
1804
|
status: exports.ValidationStatus.Error,
|
|
@@ -1366,7 +1808,38 @@ class SpecParser {
|
|
|
1366
1808
|
],
|
|
1367
1809
|
};
|
|
1368
1810
|
}
|
|
1369
|
-
|
|
1811
|
+
// Remote reference not supported
|
|
1812
|
+
const refPaths = this.parser.$refs.paths();
|
|
1813
|
+
// refPaths [0] is the current spec file path
|
|
1814
|
+
if (refPaths.length > 1) {
|
|
1815
|
+
errors.push({
|
|
1816
|
+
type: exports.ErrorType.RemoteRefNotSupported,
|
|
1817
|
+
content: Utils.format(ConstantString.RemoteRefNotSupported, refPaths.join(", ")),
|
|
1818
|
+
data: refPaths,
|
|
1819
|
+
});
|
|
1820
|
+
}
|
|
1821
|
+
if (!!this.isSwaggerFile && this.options.allowSwagger) {
|
|
1822
|
+
warnings.push({
|
|
1823
|
+
type: exports.WarningType.ConvertSwaggerToOpenAPI,
|
|
1824
|
+
content: ConstantString.ConvertSwaggerToOpenAPI,
|
|
1825
|
+
});
|
|
1826
|
+
}
|
|
1827
|
+
const validator = this.getValidator(this.spec);
|
|
1828
|
+
const validationResult = validator.validateSpec();
|
|
1829
|
+
warnings.push(...validationResult.warnings);
|
|
1830
|
+
errors.push(...validationResult.errors);
|
|
1831
|
+
let status = exports.ValidationStatus.Valid;
|
|
1832
|
+
if (warnings.length > 0 && errors.length === 0) {
|
|
1833
|
+
status = exports.ValidationStatus.Warning;
|
|
1834
|
+
}
|
|
1835
|
+
else if (errors.length > 0) {
|
|
1836
|
+
status = exports.ValidationStatus.Error;
|
|
1837
|
+
}
|
|
1838
|
+
return {
|
|
1839
|
+
status: status,
|
|
1840
|
+
warnings: warnings,
|
|
1841
|
+
errors: errors,
|
|
1842
|
+
};
|
|
1370
1843
|
}
|
|
1371
1844
|
catch (err) {
|
|
1372
1845
|
throw new SpecParserError(err.toString(), exports.ErrorType.ValidateFailed);
|
|
@@ -1390,39 +1863,40 @@ class SpecParser {
|
|
|
1390
1863
|
try {
|
|
1391
1864
|
yield this.loadSpec();
|
|
1392
1865
|
const spec = this.spec;
|
|
1393
|
-
const apiMap = this.
|
|
1394
|
-
const result =
|
|
1866
|
+
const apiMap = this.getAPIs(spec);
|
|
1867
|
+
const result = {
|
|
1868
|
+
APIs: [],
|
|
1869
|
+
allAPICount: 0,
|
|
1870
|
+
validAPICount: 0,
|
|
1871
|
+
};
|
|
1395
1872
|
for (const apiKey in apiMap) {
|
|
1873
|
+
const { operation, isValid, reason } = apiMap[apiKey];
|
|
1874
|
+
const [method, path] = apiKey.split(" ");
|
|
1875
|
+
const operationId = (_a = operation.operationId) !== null && _a !== void 0 ? _a : `${method.toLowerCase()}${Utils.convertPathToCamelCase(path)}`;
|
|
1396
1876
|
const apiResult = {
|
|
1397
|
-
api:
|
|
1877
|
+
api: apiKey,
|
|
1398
1878
|
server: "",
|
|
1399
|
-
operationId:
|
|
1879
|
+
operationId: operationId,
|
|
1880
|
+
isValid: isValid,
|
|
1881
|
+
reason: reason,
|
|
1400
1882
|
};
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
if (!operationId) {
|
|
1413
|
-
operationId = `${method.toLowerCase()}${Utils.convertPathToCamelCase(path)}`;
|
|
1414
|
-
}
|
|
1415
|
-
apiResult.operationId = operationId;
|
|
1416
|
-
const authArray = Utils.getAuthArray(operation.security, spec);
|
|
1417
|
-
for (const auths of authArray) {
|
|
1418
|
-
if (auths.length === 1) {
|
|
1419
|
-
apiResult.auth = auths[0].authScheme;
|
|
1420
|
-
break;
|
|
1883
|
+
if (isValid) {
|
|
1884
|
+
const serverObj = Utils.getServerObject(spec, method.toLocaleLowerCase(), path);
|
|
1885
|
+
if (serverObj) {
|
|
1886
|
+
apiResult.server = Utils.resolveEnv(serverObj.url);
|
|
1887
|
+
}
|
|
1888
|
+
const authArray = Utils.getAuthArray(operation.security, spec);
|
|
1889
|
+
for (const auths of authArray) {
|
|
1890
|
+
if (auths.length === 1) {
|
|
1891
|
+
apiResult.auth = auths[0];
|
|
1892
|
+
break;
|
|
1893
|
+
}
|
|
1421
1894
|
}
|
|
1422
1895
|
}
|
|
1423
|
-
apiResult
|
|
1424
|
-
result.push(apiResult);
|
|
1896
|
+
result.APIs.push(apiResult);
|
|
1425
1897
|
}
|
|
1898
|
+
result.allAPICount = result.APIs.length;
|
|
1899
|
+
result.validAPICount = result.APIs.filter((api) => api.isValid).length;
|
|
1426
1900
|
return result;
|
|
1427
1901
|
}
|
|
1428
1902
|
catch (err) {
|
|
@@ -1479,18 +1953,12 @@ class SpecParser {
|
|
|
1479
1953
|
const newSpecs = yield this.getFilteredSpecs(filter, signal);
|
|
1480
1954
|
const newUnResolvedSpec = newSpecs[0];
|
|
1481
1955
|
const newSpec = newSpecs[1];
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
resultStr = jsyaml__default['default'].dump(newUnResolvedSpec);
|
|
1485
|
-
}
|
|
1486
|
-
else {
|
|
1487
|
-
resultStr = JSON.stringify(newUnResolvedSpec, null, 2);
|
|
1488
|
-
}
|
|
1489
|
-
yield fs__default['default'].outputFile(outputSpecPath, resultStr);
|
|
1956
|
+
const authInfo = Utils.getAuthInfo(newSpec);
|
|
1957
|
+
yield this.saveFilterSpec(outputSpecPath, newUnResolvedSpec);
|
|
1490
1958
|
if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
|
|
1491
1959
|
throw new SpecParserError(ConstantString.CancelledMessage, exports.ErrorType.Cancelled);
|
|
1492
1960
|
}
|
|
1493
|
-
const [updatedManifest, apiPlugin] = yield ManifestUpdater.updateManifestWithAiPlugin(manifestPath, outputSpecPath, pluginFilePath, newSpec, this.options);
|
|
1961
|
+
const [updatedManifest, apiPlugin] = yield ManifestUpdater.updateManifestWithAiPlugin(manifestPath, outputSpecPath, pluginFilePath, newSpec, this.options, authInfo);
|
|
1494
1962
|
yield fs__default['default'].outputJSON(manifestPath, updatedManifest, { spaces: 2 });
|
|
1495
1963
|
yield fs__default['default'].outputJSON(pluginFilePath, apiPlugin, { spaces: 2 });
|
|
1496
1964
|
}
|
|
@@ -1520,32 +1988,11 @@ class SpecParser {
|
|
|
1520
1988
|
const newSpecs = yield this.getFilteredSpecs(filter, signal);
|
|
1521
1989
|
const newUnResolvedSpec = newSpecs[0];
|
|
1522
1990
|
const newSpec = newSpecs[1];
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
for (const method in newSpec.paths[url]) {
|
|
1527
|
-
const operation = newSpec.paths[url][method];
|
|
1528
|
-
const authArray = Utils.getAuthArray(operation.security, newSpec);
|
|
1529
|
-
if (authArray && authArray.length > 0) {
|
|
1530
|
-
authSet.add(authArray[0][0]);
|
|
1531
|
-
if (authSet.size > 1) {
|
|
1532
|
-
hasMultipleAuth = true;
|
|
1533
|
-
break;
|
|
1534
|
-
}
|
|
1535
|
-
}
|
|
1536
|
-
}
|
|
1537
|
-
}
|
|
1538
|
-
if (hasMultipleAuth && this.options.projectType !== exports.ProjectType.TeamsAi) {
|
|
1539
|
-
throw new SpecParserError(ConstantString.MultipleAuthNotSupported, exports.ErrorType.MultipleAuthNotSupported);
|
|
1540
|
-
}
|
|
1541
|
-
let resultStr;
|
|
1542
|
-
if (outputSpecPath.endsWith(".yaml") || outputSpecPath.endsWith(".yml")) {
|
|
1543
|
-
resultStr = jsyaml__default['default'].dump(newUnResolvedSpec);
|
|
1991
|
+
let authInfo = undefined;
|
|
1992
|
+
if (this.options.projectType === exports.ProjectType.SME) {
|
|
1993
|
+
authInfo = Utils.getAuthInfo(newSpec);
|
|
1544
1994
|
}
|
|
1545
|
-
|
|
1546
|
-
resultStr = JSON.stringify(newUnResolvedSpec, null, 2);
|
|
1547
|
-
}
|
|
1548
|
-
yield fs__default['default'].outputFile(outputSpecPath, resultStr);
|
|
1995
|
+
yield this.saveFilterSpec(outputSpecPath, newUnResolvedSpec);
|
|
1549
1996
|
if (adaptiveCardFolder) {
|
|
1550
1997
|
for (const url in newSpec.paths) {
|
|
1551
1998
|
for (const method in newSpec.paths[url]) {
|
|
@@ -1575,7 +2022,6 @@ class SpecParser {
|
|
|
1575
2022
|
if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
|
|
1576
2023
|
throw new SpecParserError(ConstantString.CancelledMessage, exports.ErrorType.Cancelled);
|
|
1577
2024
|
}
|
|
1578
|
-
const authInfo = Array.from(authSet)[0];
|
|
1579
2025
|
const [updatedManifest, warnings] = yield ManifestUpdater.updateManifest(manifestPath, outputSpecPath, newSpec, this.options, adaptiveCardFolder, authInfo);
|
|
1580
2026
|
yield fs__default['default'].outputJSON(manifestPath, updatedManifest, { spaces: 2 });
|
|
1581
2027
|
result.warnings.push(...warnings);
|
|
@@ -1604,13 +2050,30 @@ class SpecParser {
|
|
|
1604
2050
|
}
|
|
1605
2051
|
});
|
|
1606
2052
|
}
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
2053
|
+
getAPIs(spec) {
|
|
2054
|
+
const validator = this.getValidator(spec);
|
|
2055
|
+
const apiMap = validator.listAPIs();
|
|
2056
|
+
return apiMap;
|
|
2057
|
+
}
|
|
2058
|
+
getValidator(spec) {
|
|
2059
|
+
if (this.validator) {
|
|
2060
|
+
return this.validator;
|
|
1610
2061
|
}
|
|
1611
|
-
const
|
|
1612
|
-
this.
|
|
1613
|
-
return
|
|
2062
|
+
const validator = ValidatorFactory.create(spec, this.options);
|
|
2063
|
+
this.validator = validator;
|
|
2064
|
+
return validator;
|
|
2065
|
+
}
|
|
2066
|
+
saveFilterSpec(outputSpecPath, unResolvedSpec) {
|
|
2067
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2068
|
+
let resultStr;
|
|
2069
|
+
if (outputSpecPath.endsWith(".yaml") || outputSpecPath.endsWith(".yml")) {
|
|
2070
|
+
resultStr = jsyaml__default['default'].dump(unResolvedSpec);
|
|
2071
|
+
}
|
|
2072
|
+
else {
|
|
2073
|
+
resultStr = JSON.stringify(unResolvedSpec, null, 2);
|
|
2074
|
+
}
|
|
2075
|
+
yield fs__default['default'].outputFile(outputSpecPath, resultStr);
|
|
2076
|
+
});
|
|
1614
2077
|
}
|
|
1615
2078
|
}
|
|
1616
2079
|
|