@microsoft/m365-spec-parser 0.1.1-alpha.a277dba4e.0 → 0.1.1-alpha.ad8f60cf1.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 +606 -333
- package/dist/index.esm2017.js.map +1 -1
- package/dist/index.esm2017.mjs +1147 -719
- package/dist/index.esm2017.mjs.map +1 -1
- package/dist/index.esm5.js +606 -333
- package/dist/index.esm5.js.map +1 -1
- package/dist/index.node.cjs.js +1092 -660
- package/dist/index.node.cjs.js.map +1 -1
- package/dist/src/adaptiveCardWrapper.d.ts +2 -0
- package/dist/src/constants.d.ts +3 -2
- package/dist/src/index.d.ts +1 -1
- package/dist/src/interfaces.d.ts +65 -1
- package/dist/src/manifestUpdater.d.ts +5 -2
- package/dist/src/specParser.browser.d.ts +3 -2
- package/dist/src/specParser.d.ts +4 -2
- package/dist/src/utils.d.ts +9 -28
- package/package.json +3 -3
package/dist/index.node.cjs.js
CHANGED
|
@@ -71,6 +71,21 @@ exports.ErrorType = void 0;
|
|
|
71
71
|
ErrorType["GenerateFailed"] = "generate-failed";
|
|
72
72
|
ErrorType["ValidateFailed"] = "validate-failed";
|
|
73
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";
|
|
74
89
|
ErrorType["Cancelled"] = "cancelled";
|
|
75
90
|
ErrorType["Unknown"] = "unknown";
|
|
76
91
|
})(exports.ErrorType || (exports.ErrorType = {}));
|
|
@@ -110,7 +125,7 @@ ConstantString.RemoteRefNotSupported = "Remote reference is not supported: %s.";
|
|
|
110
125
|
ConstantString.MissingOperationId = "Missing operationIds: %s.";
|
|
111
126
|
ConstantString.NoSupportedApi = "No supported API is found in the OpenAPI description document: only GET and POST methods are supported, additionally, there can be at most one required parameter, and no auth is allowed.";
|
|
112
127
|
ConstantString.AdditionalPropertiesNotSupported = "'additionalProperties' is not supported, and will be ignored.";
|
|
113
|
-
ConstantString.SchemaNotSupported = "'oneOf', 'anyOf', and 'not' schema are not supported: %s.";
|
|
128
|
+
ConstantString.SchemaNotSupported = "'oneOf', 'allOf', 'anyOf', and 'not' schema are not supported: %s.";
|
|
114
129
|
ConstantString.UnknownSchema = "Unknown schema: %s.";
|
|
115
130
|
ConstantString.UrlProtocolNotSupported = "Server url is not correct: protocol %s is not supported, you should use https protocol instead.";
|
|
116
131
|
ConstantString.RelativeServerUrlNotSupported = "Server url is not correct: relative server url is not supported.";
|
|
@@ -130,9 +145,9 @@ ConstantString.AdaptiveCardVersion = "1.5";
|
|
|
130
145
|
ConstantString.AdaptiveCardSchema = "http://adaptivecards.io/schemas/adaptive-card.json";
|
|
131
146
|
ConstantString.AdaptiveCardType = "AdaptiveCard";
|
|
132
147
|
ConstantString.TextBlockType = "TextBlock";
|
|
148
|
+
ConstantString.ImageType = "Image";
|
|
133
149
|
ConstantString.ContainerType = "Container";
|
|
134
150
|
ConstantString.RegistrationIdPostfix = "REGISTRATION_ID";
|
|
135
|
-
ConstantString.OAuthRegistrationIdPostFix = "OAUTH_REGISTRATION_ID";
|
|
136
151
|
ConstantString.ResponseCodeFor20X = [
|
|
137
152
|
"200",
|
|
138
153
|
"201",
|
|
@@ -193,7 +208,8 @@ ConstantString.CommandDescriptionMaxLens = 128;
|
|
|
193
208
|
ConstantString.ParameterDescriptionMaxLens = 128;
|
|
194
209
|
ConstantString.CommandTitleMaxLens = 32;
|
|
195
210
|
ConstantString.ParameterTitleMaxLens = 32;
|
|
196
|
-
ConstantString.SMERequiredParamsMaxNum = 5;
|
|
211
|
+
ConstantString.SMERequiredParamsMaxNum = 5;
|
|
212
|
+
ConstantString.DefaultPluginId = "plugin_1";
|
|
197
213
|
|
|
198
214
|
// Copyright (c) Microsoft Corporation.
|
|
199
215
|
class SpecParserError extends Error {
|
|
@@ -216,221 +232,9 @@ class Utils {
|
|
|
216
232
|
}
|
|
217
233
|
return false;
|
|
218
234
|
}
|
|
219
|
-
static checkParameters(paramObject, isCopilot) {
|
|
220
|
-
const paramResult = {
|
|
221
|
-
requiredNum: 0,
|
|
222
|
-
optionalNum: 0,
|
|
223
|
-
isValid: true,
|
|
224
|
-
};
|
|
225
|
-
if (!paramObject) {
|
|
226
|
-
return paramResult;
|
|
227
|
-
}
|
|
228
|
-
for (let i = 0; i < paramObject.length; i++) {
|
|
229
|
-
const param = paramObject[i];
|
|
230
|
-
const schema = param.schema;
|
|
231
|
-
if (isCopilot && this.hasNestedObjectInSchema(schema)) {
|
|
232
|
-
paramResult.isValid = false;
|
|
233
|
-
continue;
|
|
234
|
-
}
|
|
235
|
-
const isRequiredWithoutDefault = param.required && schema.default === undefined;
|
|
236
|
-
if (isCopilot) {
|
|
237
|
-
if (isRequiredWithoutDefault) {
|
|
238
|
-
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
239
|
-
}
|
|
240
|
-
else {
|
|
241
|
-
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
242
|
-
}
|
|
243
|
-
continue;
|
|
244
|
-
}
|
|
245
|
-
if (param.in === "header" || param.in === "cookie") {
|
|
246
|
-
if (isRequiredWithoutDefault) {
|
|
247
|
-
paramResult.isValid = false;
|
|
248
|
-
}
|
|
249
|
-
continue;
|
|
250
|
-
}
|
|
251
|
-
if (schema.type !== "boolean" &&
|
|
252
|
-
schema.type !== "string" &&
|
|
253
|
-
schema.type !== "number" &&
|
|
254
|
-
schema.type !== "integer") {
|
|
255
|
-
if (isRequiredWithoutDefault) {
|
|
256
|
-
paramResult.isValid = false;
|
|
257
|
-
}
|
|
258
|
-
continue;
|
|
259
|
-
}
|
|
260
|
-
if (param.in === "query" || param.in === "path") {
|
|
261
|
-
if (isRequiredWithoutDefault) {
|
|
262
|
-
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
263
|
-
}
|
|
264
|
-
else {
|
|
265
|
-
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
return paramResult;
|
|
270
|
-
}
|
|
271
|
-
static checkPostBody(schema, isRequired = false, isCopilot = false) {
|
|
272
|
-
var _a;
|
|
273
|
-
const paramResult = {
|
|
274
|
-
requiredNum: 0,
|
|
275
|
-
optionalNum: 0,
|
|
276
|
-
isValid: true,
|
|
277
|
-
};
|
|
278
|
-
if (Object.keys(schema).length === 0) {
|
|
279
|
-
return paramResult;
|
|
280
|
-
}
|
|
281
|
-
const isRequiredWithoutDefault = isRequired && schema.default === undefined;
|
|
282
|
-
if (isCopilot && this.hasNestedObjectInSchema(schema)) {
|
|
283
|
-
paramResult.isValid = false;
|
|
284
|
-
return paramResult;
|
|
285
|
-
}
|
|
286
|
-
if (schema.type === "string" ||
|
|
287
|
-
schema.type === "integer" ||
|
|
288
|
-
schema.type === "boolean" ||
|
|
289
|
-
schema.type === "number") {
|
|
290
|
-
if (isRequiredWithoutDefault) {
|
|
291
|
-
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
292
|
-
}
|
|
293
|
-
else {
|
|
294
|
-
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
else if (schema.type === "object") {
|
|
298
|
-
const { properties } = schema;
|
|
299
|
-
for (const property in properties) {
|
|
300
|
-
let isRequired = false;
|
|
301
|
-
if (schema.required && ((_a = schema.required) === null || _a === void 0 ? void 0 : _a.indexOf(property)) >= 0) {
|
|
302
|
-
isRequired = true;
|
|
303
|
-
}
|
|
304
|
-
const result = Utils.checkPostBody(properties[property], isRequired, isCopilot);
|
|
305
|
-
paramResult.requiredNum += result.requiredNum;
|
|
306
|
-
paramResult.optionalNum += result.optionalNum;
|
|
307
|
-
paramResult.isValid = paramResult.isValid && result.isValid;
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
else {
|
|
311
|
-
if (isRequiredWithoutDefault && !isCopilot) {
|
|
312
|
-
paramResult.isValid = false;
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
return paramResult;
|
|
316
|
-
}
|
|
317
235
|
static containMultipleMediaTypes(bodyObject) {
|
|
318
236
|
return Object.keys((bodyObject === null || bodyObject === void 0 ? void 0 : bodyObject.content) || {}).length > 1;
|
|
319
237
|
}
|
|
320
|
-
/**
|
|
321
|
-
* Checks if the given API is supported.
|
|
322
|
-
* @param {string} method - The HTTP method of the API.
|
|
323
|
-
* @param {string} path - The path of the API.
|
|
324
|
-
* @param {OpenAPIV3.Document} spec - The OpenAPI specification document.
|
|
325
|
-
* @returns {boolean} - Returns true if the API is supported, false otherwise.
|
|
326
|
-
* @description The following APIs are supported:
|
|
327
|
-
* 1. only support Get/Post operation without auth property
|
|
328
|
-
* 2. parameter inside query or path only support string, number, boolean and integer
|
|
329
|
-
* 3. parameter inside post body only support string, number, boolean, integer and object
|
|
330
|
-
* 4. request body + required parameters <= 1
|
|
331
|
-
* 5. response body should be “application/json” and not empty, and response code should be 20X
|
|
332
|
-
* 6. only support request body with “application/json” content type
|
|
333
|
-
*/
|
|
334
|
-
static isSupportedApi(method, path, spec, options) {
|
|
335
|
-
var _a;
|
|
336
|
-
const pathObj = spec.paths[path];
|
|
337
|
-
method = method.toLocaleLowerCase();
|
|
338
|
-
if (pathObj) {
|
|
339
|
-
if (((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && pathObj[method]) {
|
|
340
|
-
const securities = pathObj[method].security;
|
|
341
|
-
const isTeamsAi = options.projectType === exports.ProjectType.TeamsAi;
|
|
342
|
-
const isCopilot = options.projectType === exports.ProjectType.Copilot;
|
|
343
|
-
// Teams AI project doesn't care about auth, it will use authProvider for user to implement
|
|
344
|
-
if (!isTeamsAi) {
|
|
345
|
-
const authArray = Utils.getAuthArray(securities, spec);
|
|
346
|
-
if (!Utils.isSupportedAuth(authArray, options)) {
|
|
347
|
-
return false;
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
const operationObject = pathObj[method];
|
|
351
|
-
if (!options.allowMissingId && !operationObject.operationId) {
|
|
352
|
-
return false;
|
|
353
|
-
}
|
|
354
|
-
const paramObject = operationObject.parameters;
|
|
355
|
-
const requestBody = operationObject.requestBody;
|
|
356
|
-
const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
|
|
357
|
-
if (!isTeamsAi && Utils.containMultipleMediaTypes(requestBody)) {
|
|
358
|
-
return false;
|
|
359
|
-
}
|
|
360
|
-
const responseJson = Utils.getResponseJson(operationObject, isTeamsAi);
|
|
361
|
-
if (Object.keys(responseJson).length === 0) {
|
|
362
|
-
return false;
|
|
363
|
-
}
|
|
364
|
-
// Teams AI project doesn't care about request parameters/body
|
|
365
|
-
if (isTeamsAi) {
|
|
366
|
-
return true;
|
|
367
|
-
}
|
|
368
|
-
let requestBodyParamResult = {
|
|
369
|
-
requiredNum: 0,
|
|
370
|
-
optionalNum: 0,
|
|
371
|
-
isValid: true,
|
|
372
|
-
};
|
|
373
|
-
if (requestJsonBody) {
|
|
374
|
-
const requestBodySchema = requestJsonBody.schema;
|
|
375
|
-
if (isCopilot && requestBodySchema.type !== "object") {
|
|
376
|
-
return false;
|
|
377
|
-
}
|
|
378
|
-
requestBodyParamResult = Utils.checkPostBody(requestBodySchema, requestBody.required, isCopilot);
|
|
379
|
-
}
|
|
380
|
-
if (!requestBodyParamResult.isValid) {
|
|
381
|
-
return false;
|
|
382
|
-
}
|
|
383
|
-
const paramResult = Utils.checkParameters(paramObject, isCopilot);
|
|
384
|
-
if (!paramResult.isValid) {
|
|
385
|
-
return false;
|
|
386
|
-
}
|
|
387
|
-
// Copilot support arbitrary parameters
|
|
388
|
-
if (isCopilot) {
|
|
389
|
-
return true;
|
|
390
|
-
}
|
|
391
|
-
if (requestBodyParamResult.requiredNum + paramResult.requiredNum > 1) {
|
|
392
|
-
if (options.allowMultipleParameters &&
|
|
393
|
-
requestBodyParamResult.requiredNum + paramResult.requiredNum <=
|
|
394
|
-
ConstantString.SMERequiredParamsMaxNum) {
|
|
395
|
-
return true;
|
|
396
|
-
}
|
|
397
|
-
return false;
|
|
398
|
-
}
|
|
399
|
-
else if (requestBodyParamResult.requiredNum +
|
|
400
|
-
requestBodyParamResult.optionalNum +
|
|
401
|
-
paramResult.requiredNum +
|
|
402
|
-
paramResult.optionalNum ===
|
|
403
|
-
0) {
|
|
404
|
-
return false;
|
|
405
|
-
}
|
|
406
|
-
else {
|
|
407
|
-
return true;
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
|
-
return false;
|
|
412
|
-
}
|
|
413
|
-
static isSupportedAuth(authSchemeArray, options) {
|
|
414
|
-
if (authSchemeArray.length === 0) {
|
|
415
|
-
return true;
|
|
416
|
-
}
|
|
417
|
-
if (options.allowAPIKeyAuth || options.allowOauth2 || options.allowBearerTokenAuth) {
|
|
418
|
-
// Currently we don't support multiple auth in one operation
|
|
419
|
-
if (authSchemeArray.length > 0 && authSchemeArray.every((auths) => auths.length > 1)) {
|
|
420
|
-
return false;
|
|
421
|
-
}
|
|
422
|
-
for (const auths of authSchemeArray) {
|
|
423
|
-
if (auths.length === 1) {
|
|
424
|
-
if ((options.allowAPIKeyAuth && Utils.isAPIKeyAuth(auths[0].authScheme)) ||
|
|
425
|
-
(options.allowOauth2 && Utils.isOAuthWithAuthCodeFlow(auths[0].authScheme)) ||
|
|
426
|
-
(options.allowBearerTokenAuth && Utils.isBearerTokenAuth(auths[0].authScheme))) {
|
|
427
|
-
return true;
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
}
|
|
432
|
-
return false;
|
|
433
|
-
}
|
|
434
238
|
static isBearerTokenAuth(authScheme) {
|
|
435
239
|
return authScheme.type === "http" && authScheme.scheme === "bearer";
|
|
436
240
|
}
|
|
@@ -438,18 +242,18 @@ class Utils {
|
|
|
438
242
|
return authScheme.type === "apiKey";
|
|
439
243
|
}
|
|
440
244
|
static isOAuthWithAuthCodeFlow(authScheme) {
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
return false;
|
|
245
|
+
return !!(authScheme.type === "oauth2" &&
|
|
246
|
+
authScheme.flows &&
|
|
247
|
+
authScheme.flows.authorizationCode);
|
|
445
248
|
}
|
|
446
249
|
static getAuthArray(securities, spec) {
|
|
447
250
|
var _a;
|
|
448
251
|
const result = [];
|
|
449
252
|
const securitySchemas = (_a = spec.components) === null || _a === void 0 ? void 0 : _a.securitySchemes;
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
253
|
+
const securitiesArr = securities !== null && securities !== void 0 ? securities : spec.security;
|
|
254
|
+
if (securitiesArr && securitySchemas) {
|
|
255
|
+
for (let i = 0; i < securitiesArr.length; i++) {
|
|
256
|
+
const security = securitiesArr[i];
|
|
453
257
|
const authArray = [];
|
|
454
258
|
for (const name in security) {
|
|
455
259
|
const auth = securitySchemas[name];
|
|
@@ -466,17 +270,39 @@ class Utils {
|
|
|
466
270
|
result.sort((a, b) => a[0].name.localeCompare(b[0].name));
|
|
467
271
|
return result;
|
|
468
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
|
+
}
|
|
469
292
|
static updateFirstLetter(str) {
|
|
470
293
|
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
471
294
|
}
|
|
472
|
-
static getResponseJson(operationObject
|
|
295
|
+
static getResponseJson(operationObject) {
|
|
473
296
|
var _a, _b;
|
|
474
297
|
let json = {};
|
|
298
|
+
let multipleMediaType = false;
|
|
475
299
|
for (const code of ConstantString.ResponseCodeFor20X) {
|
|
476
300
|
const responseObject = (_a = operationObject === null || operationObject === void 0 ? void 0 : operationObject.responses) === null || _a === void 0 ? void 0 : _a[code];
|
|
477
301
|
if ((_b = responseObject === null || responseObject === void 0 ? void 0 : responseObject.content) === null || _b === void 0 ? void 0 : _b["application/json"]) {
|
|
302
|
+
multipleMediaType = false;
|
|
478
303
|
json = responseObject.content["application/json"];
|
|
479
|
-
if (
|
|
304
|
+
if (Utils.containMultipleMediaTypes(responseObject)) {
|
|
305
|
+
multipleMediaType = true;
|
|
480
306
|
json = {};
|
|
481
307
|
}
|
|
482
308
|
else {
|
|
@@ -484,7 +310,7 @@ class Utils {
|
|
|
484
310
|
}
|
|
485
311
|
}
|
|
486
312
|
}
|
|
487
|
-
return json;
|
|
313
|
+
return { json, multipleMediaType };
|
|
488
314
|
}
|
|
489
315
|
static convertPathToCamelCase(path) {
|
|
490
316
|
const pathSegments = path.split(/[./{]/);
|
|
@@ -504,10 +330,10 @@ class Utils {
|
|
|
504
330
|
return undefined;
|
|
505
331
|
}
|
|
506
332
|
}
|
|
507
|
-
static
|
|
333
|
+
static resolveEnv(str) {
|
|
508
334
|
const placeHolderReg = /\${{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*}}/g;
|
|
509
|
-
let matches = placeHolderReg.exec(
|
|
510
|
-
let
|
|
335
|
+
let matches = placeHolderReg.exec(str);
|
|
336
|
+
let newStr = str;
|
|
511
337
|
while (matches != null) {
|
|
512
338
|
const envVar = matches[1];
|
|
513
339
|
const envVal = process.env[envVar];
|
|
@@ -515,17 +341,17 @@ class Utils {
|
|
|
515
341
|
throw new Error(Utils.format(ConstantString.ResolveServerUrlFailed, envVar));
|
|
516
342
|
}
|
|
517
343
|
else {
|
|
518
|
-
|
|
344
|
+
newStr = newStr.replace(matches[0], envVal);
|
|
519
345
|
}
|
|
520
|
-
matches = placeHolderReg.exec(
|
|
346
|
+
matches = placeHolderReg.exec(str);
|
|
521
347
|
}
|
|
522
|
-
return
|
|
348
|
+
return newStr;
|
|
523
349
|
}
|
|
524
350
|
static checkServerUrl(servers) {
|
|
525
351
|
const errors = [];
|
|
526
352
|
let serverUrl;
|
|
527
353
|
try {
|
|
528
|
-
serverUrl = Utils.
|
|
354
|
+
serverUrl = Utils.resolveEnv(servers[0].url);
|
|
529
355
|
}
|
|
530
356
|
catch (err) {
|
|
531
357
|
errors.push({
|
|
@@ -556,6 +382,7 @@ class Utils {
|
|
|
556
382
|
return errors;
|
|
557
383
|
}
|
|
558
384
|
static validateServer(spec, options) {
|
|
385
|
+
var _a;
|
|
559
386
|
const errors = [];
|
|
560
387
|
let hasTopLevelServers = false;
|
|
561
388
|
let hasPathLevelServers = false;
|
|
@@ -576,7 +403,7 @@ class Utils {
|
|
|
576
403
|
}
|
|
577
404
|
for (const method in methods) {
|
|
578
405
|
const operationObject = methods[method];
|
|
579
|
-
if (
|
|
406
|
+
if (((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && operationObject) {
|
|
580
407
|
if ((operationObject === null || operationObject === void 0 ? void 0 : operationObject.servers) && operationObject.servers.length >= 1) {
|
|
581
408
|
hasOperationLevelServers = true;
|
|
582
409
|
const serverErrors = Utils.checkServerUrl(operationObject.servers);
|
|
@@ -703,13 +530,7 @@ class Utils {
|
|
|
703
530
|
}
|
|
704
531
|
}
|
|
705
532
|
const operationId = operationItem.operationId;
|
|
706
|
-
const parameters = [];
|
|
707
|
-
if (requiredParams.length !== 0) {
|
|
708
|
-
parameters.push(...requiredParams);
|
|
709
|
-
}
|
|
710
|
-
else {
|
|
711
|
-
parameters.push(optionalParams[0]);
|
|
712
|
-
}
|
|
533
|
+
const parameters = [...requiredParams, ...optionalParams];
|
|
713
534
|
const command = {
|
|
714
535
|
context: ["compose"],
|
|
715
536
|
type: "query",
|
|
@@ -718,366 +539,575 @@ class Utils {
|
|
|
718
539
|
parameters: parameters,
|
|
719
540
|
description: ((_b = operationItem.description) !== null && _b !== void 0 ? _b : "").slice(0, ConstantString.CommandDescriptionMaxLens),
|
|
720
541
|
};
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
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;
|
|
728
558
|
}
|
|
729
|
-
return
|
|
559
|
+
return safeRegistrationIdEnvName;
|
|
730
560
|
}
|
|
731
|
-
static
|
|
732
|
-
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;
|
|
733
580
|
const result = {};
|
|
734
581
|
for (const path in paths) {
|
|
735
582
|
const methods = paths[path];
|
|
736
583
|
for (const method in methods) {
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
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
|
+
};
|
|
740
592
|
}
|
|
741
593
|
}
|
|
742
594
|
}
|
|
595
|
+
this.apiMap = result;
|
|
743
596
|
return result;
|
|
744
597
|
}
|
|
745
|
-
|
|
746
|
-
const
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
});
|
|
753
|
-
}
|
|
754
|
-
// Server validation
|
|
755
|
-
const serverErrors = Utils.validateServer(spec, options);
|
|
756
|
-
errors.push(...serverErrors);
|
|
757
|
-
// Remote reference not supported
|
|
758
|
-
const refPaths = parser.$refs.paths();
|
|
759
|
-
// refPaths [0] is the current spec file path
|
|
760
|
-
if (refPaths.length > 1) {
|
|
761
|
-
errors.push({
|
|
762
|
-
type: exports.ErrorType.RemoteRefNotSupported,
|
|
763
|
-
content: Utils.format(ConstantString.RemoteRefNotSupported, refPaths.join(", ")),
|
|
764
|
-
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,
|
|
765
605
|
});
|
|
766
606
|
}
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
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({
|
|
771
627
|
type: exports.ErrorType.NoSupportedApi,
|
|
772
628
|
content: ConstantString.NoSupportedApi,
|
|
629
|
+
data,
|
|
773
630
|
});
|
|
774
631
|
}
|
|
632
|
+
return result;
|
|
633
|
+
}
|
|
634
|
+
validateSpecOperationId() {
|
|
635
|
+
const result = { errors: [], warnings: [] };
|
|
636
|
+
const apiMap = this.listAPIs();
|
|
775
637
|
// OperationId missing
|
|
776
638
|
const apisMissingOperationId = [];
|
|
777
639
|
for (const key in apiMap) {
|
|
778
|
-
const
|
|
779
|
-
if (!
|
|
640
|
+
const { operation } = apiMap[key];
|
|
641
|
+
if (!operation.operationId) {
|
|
780
642
|
apisMissingOperationId.push(key);
|
|
781
643
|
}
|
|
782
644
|
}
|
|
783
645
|
if (apisMissingOperationId.length > 0) {
|
|
784
|
-
warnings.push({
|
|
646
|
+
result.warnings.push({
|
|
785
647
|
type: exports.WarningType.OperationIdMissing,
|
|
786
648
|
content: Utils.format(ConstantString.MissingOperationId, apisMissingOperationId.join(", ")),
|
|
787
649
|
data: apisMissingOperationId,
|
|
788
650
|
});
|
|
789
651
|
}
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
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;
|
|
793
660
|
}
|
|
794
|
-
|
|
795
|
-
|
|
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;
|
|
796
666
|
}
|
|
797
|
-
return
|
|
798
|
-
status,
|
|
799
|
-
warnings,
|
|
800
|
-
errors,
|
|
801
|
-
};
|
|
667
|
+
return result;
|
|
802
668
|
}
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
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;
|
|
809
684
|
}
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
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);
|
|
813
691
|
}
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
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));
|
|
817
696
|
}
|
|
818
|
-
return
|
|
697
|
+
return result;
|
|
819
698
|
}
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
const
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
count++;
|
|
828
|
-
}
|
|
829
|
-
}
|
|
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: [] };
|
|
830
706
|
}
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
for (const
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
if (!newPaths[path]) {
|
|
848
|
-
newPaths[path] = Object.assign({}, unResolveSpec.paths[path]);
|
|
849
|
-
for (const m of ConstantString.AllOperationMethods) {
|
|
850
|
-
delete newPaths[path][m];
|
|
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: [] };
|
|
851
723
|
}
|
|
852
724
|
}
|
|
853
|
-
newPaths[path][methodName] = unResolveSpec.paths[path][methodName];
|
|
854
|
-
// Add the operationId if missing
|
|
855
|
-
if (!newPaths[path][methodName].operationId) {
|
|
856
|
-
newPaths[path][methodName].operationId = `${methodName}${Utils.convertPathToCamelCase(path)}`;
|
|
857
|
-
}
|
|
858
725
|
}
|
|
859
|
-
newSpec.paths = newPaths;
|
|
860
|
-
return newSpec;
|
|
861
|
-
}
|
|
862
|
-
catch (err) {
|
|
863
|
-
throw new SpecParserError(err.toString(), exports.ErrorType.FilterSpecFailed);
|
|
864
726
|
}
|
|
727
|
+
return { isValid: false, reason: [exports.ErrorType.AuthTypeIsNotSupported] };
|
|
865
728
|
}
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
const apiPluginRelativePath = ManifestUpdater.getRelativePath(manifestPath, apiPluginFilePath);
|
|
874
|
-
manifest.plugins = [
|
|
875
|
-
{
|
|
876
|
-
pluginFile: apiPluginRelativePath,
|
|
877
|
-
},
|
|
878
|
-
];
|
|
879
|
-
ManifestUpdater.updateManifestDescription(manifest, spec);
|
|
880
|
-
const specRelativePath = ManifestUpdater.getRelativePath(manifestPath, outputSpecPath);
|
|
881
|
-
const apiPlugin = ManifestUpdater.generatePluginManifestSchema(spec, specRelativePath, options);
|
|
882
|
-
return [manifest, apiPlugin];
|
|
883
|
-
});
|
|
884
|
-
}
|
|
885
|
-
static updateManifestDescription(manifest, spec) {
|
|
886
|
-
var _a, _b;
|
|
887
|
-
manifest.description = {
|
|
888
|
-
short: spec.info.title.slice(0, ConstantString.ShortDescriptionMaxLens),
|
|
889
|
-
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: [],
|
|
890
736
|
};
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
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
|
+
}
|
|
894
747
|
if (schema.type === "string" ||
|
|
895
|
-
schema.type === "boolean" ||
|
|
896
748
|
schema.type === "integer" ||
|
|
897
|
-
schema.type === "
|
|
898
|
-
schema.type === "
|
|
899
|
-
|
|
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
|
+
}
|
|
900
771
|
}
|
|
901
772
|
else {
|
|
902
|
-
|
|
773
|
+
if (isRequiredWithoutDefault && !isCopilot) {
|
|
774
|
+
paramResult.isValid = false;
|
|
775
|
+
paramResult.reason.push(exports.ErrorType.PostBodyContainsRequiredUnsupportedSchema);
|
|
776
|
+
}
|
|
903
777
|
}
|
|
904
|
-
return
|
|
778
|
+
return paramResult;
|
|
905
779
|
}
|
|
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
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
name: operationId,
|
|
959
|
-
description: description,
|
|
960
|
-
parameters: parameters,
|
|
961
|
-
};
|
|
962
|
-
functions.push(funcObj);
|
|
963
|
-
functionNames.push(operationId);
|
|
964
|
-
}
|
|
965
|
-
}
|
|
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;
|
|
966
832
|
}
|
|
967
833
|
}
|
|
968
834
|
}
|
|
969
|
-
|
|
970
|
-
schema_version: "v2",
|
|
971
|
-
name_for_human: spec.info.title,
|
|
972
|
-
description_for_human: (_c = spec.info.description) !== null && _c !== void 0 ? _c : "<Please add description of the plugin>",
|
|
973
|
-
functions: functions,
|
|
974
|
-
runtimes: [
|
|
975
|
-
{
|
|
976
|
-
type: "OpenApi",
|
|
977
|
-
auth: {
|
|
978
|
-
type: "none", // TODO, support auth in the future
|
|
979
|
-
},
|
|
980
|
-
spec: {
|
|
981
|
-
url: specRelativePath,
|
|
982
|
-
},
|
|
983
|
-
run_for_functions: functionNames,
|
|
984
|
-
},
|
|
985
|
-
],
|
|
986
|
-
};
|
|
987
|
-
return apiPlugin;
|
|
835
|
+
return paramResult;
|
|
988
836
|
}
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
const
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
let warnings = [];
|
|
996
|
-
if (options.projectType === exports.ProjectType.SME) {
|
|
997
|
-
const updateResult = yield ManifestUpdater.generateCommands(spec, manifestPath, options, adaptiveCardFolder);
|
|
998
|
-
const commands = updateResult[0];
|
|
999
|
-
warnings = updateResult[1];
|
|
1000
|
-
const composeExtension = {
|
|
1001
|
-
composeExtensionType: "apiBased",
|
|
1002
|
-
apiSpecificationFile: ManifestUpdater.getRelativePath(manifestPath, outputSpecPath),
|
|
1003
|
-
commands: commands,
|
|
1004
|
-
};
|
|
1005
|
-
if (authInfo) {
|
|
1006
|
-
const auth = authInfo.authScheme;
|
|
1007
|
-
if (Utils.isAPIKeyAuth(auth) || Utils.isBearerTokenAuth(auth)) {
|
|
1008
|
-
const safeApiSecretRegistrationId = Utils.getSafeRegistrationIdEnvName(`${authInfo.name}_${ConstantString.RegistrationIdPostfix}`);
|
|
1009
|
-
composeExtension.authorization = {
|
|
1010
|
-
authType: "apiSecretServiceAuth",
|
|
1011
|
-
apiSecretServiceAuthConfiguration: {
|
|
1012
|
-
apiSecretRegistrationId: `\${{${safeApiSecretRegistrationId}}}`,
|
|
1013
|
-
},
|
|
1014
|
-
};
|
|
1015
|
-
}
|
|
1016
|
-
else if (Utils.isOAuthWithAuthCodeFlow(auth)) {
|
|
1017
|
-
const safeOAuth2RegistrationId = Utils.getSafeRegistrationIdEnvName(`${authInfo.name}_${ConstantString.OAuthRegistrationIdPostFix}`);
|
|
1018
|
-
composeExtension.authorization = {
|
|
1019
|
-
authType: "oAuth2.0",
|
|
1020
|
-
oAuthConfiguration: {
|
|
1021
|
-
oauthConfigurationId: `\${{${safeOAuth2RegistrationId}}}`,
|
|
1022
|
-
},
|
|
1023
|
-
};
|
|
1024
|
-
updatedPart.webApplicationInfo = {
|
|
1025
|
-
id: "${{AAD_APP_CLIENT_ID}}",
|
|
1026
|
-
resource: "api://${{DOMAIN}}/${{AAD_APP_CLIENT_ID}}",
|
|
1027
|
-
};
|
|
1028
|
-
}
|
|
1029
|
-
}
|
|
1030
|
-
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;
|
|
1031
843
|
}
|
|
1032
|
-
updatedPart.description = originalManifest.description;
|
|
1033
|
-
ManifestUpdater.updateManifestDescription(updatedPart, spec);
|
|
1034
|
-
const updatedManifest = Object.assign(Object.assign({}, originalManifest), updatedPart);
|
|
1035
|
-
return [updatedManifest, warnings];
|
|
1036
844
|
}
|
|
1037
|
-
|
|
1038
|
-
|
|
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);
|
|
1039
903
|
}
|
|
1040
|
-
|
|
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;
|
|
1041
915
|
}
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
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);
|
|
1074
1005
|
}
|
|
1075
|
-
|
|
1076
|
-
|
|
1006
|
+
}
|
|
1007
|
+
else if (totalParams === 0) {
|
|
1008
|
+
result.reason.push(exports.ErrorType.NoParameter);
|
|
1009
|
+
}
|
|
1010
|
+
return result;
|
|
1077
1011
|
}
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
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) {
|
|
1058
|
+
var _a;
|
|
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];
|
|
1096
|
+
}
|
|
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
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
newSpec.paths = newPaths;
|
|
1106
|
+
return newSpec;
|
|
1107
|
+
}
|
|
1108
|
+
catch (err) {
|
|
1109
|
+
throw new SpecParserError(err.toString(), exports.ErrorType.FilterSpecFailed);
|
|
1110
|
+
}
|
|
1081
1111
|
}
|
|
1082
1112
|
}
|
|
1083
1113
|
|
|
@@ -1085,7 +1115,7 @@ class ManifestUpdater {
|
|
|
1085
1115
|
class AdaptiveCardGenerator {
|
|
1086
1116
|
static generateAdaptiveCard(operationItem) {
|
|
1087
1117
|
try {
|
|
1088
|
-
const json = Utils.getResponseJson(operationItem);
|
|
1118
|
+
const { json } = Utils.getResponseJson(operationItem);
|
|
1089
1119
|
let cardBody = [];
|
|
1090
1120
|
let schema = json.schema;
|
|
1091
1121
|
let jsonPath = "$";
|
|
@@ -1251,6 +1281,27 @@ function wrapAdaptiveCard(card, jsonPath) {
|
|
|
1251
1281
|
};
|
|
1252
1282
|
return result;
|
|
1253
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
|
+
}
|
|
1254
1305
|
/**
|
|
1255
1306
|
* Infers the preview card template from an Adaptive Card and a JSON path.
|
|
1256
1307
|
* The preview card template includes a title and an optional subtitle and image.
|
|
@@ -1263,11 +1314,29 @@ function wrapAdaptiveCard(card, jsonPath) {
|
|
|
1263
1314
|
* @returns The inferred preview card template.
|
|
1264
1315
|
*/
|
|
1265
1316
|
function inferPreviewCardTemplate(card) {
|
|
1266
|
-
var _a;
|
|
1267
1317
|
const result = {
|
|
1268
|
-
title: "",
|
|
1318
|
+
title: "result",
|
|
1269
1319
|
};
|
|
1270
|
-
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();
|
|
1271
1340
|
let rootObject;
|
|
1272
1341
|
if (((_a = card.body[0]) === null || _a === void 0 ? void 0 : _a.type) === ConstantString.ContainerType) {
|
|
1273
1342
|
rootObject = card.body[0].items;
|
|
@@ -1280,56 +1349,414 @@ function inferPreviewCardTemplate(card) {
|
|
|
1280
1349
|
const textElement = element;
|
|
1281
1350
|
const index = textElement.text.indexOf("${if(");
|
|
1282
1351
|
if (index > 0) {
|
|
1283
|
-
|
|
1284
|
-
|
|
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);
|
|
1285
1366
|
}
|
|
1286
1367
|
}
|
|
1287
1368
|
}
|
|
1288
|
-
for (const
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
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);
|
|
1293
1373
|
}
|
|
1294
1374
|
else if (!result.subtitle &&
|
|
1295
|
-
Utils.isWellKnownName(
|
|
1296
|
-
result.subtitle =
|
|
1297
|
-
|
|
1375
|
+
Utils.isWellKnownName(name, ConstantString.WellknownSubtitleName)) {
|
|
1376
|
+
result.subtitle = name;
|
|
1377
|
+
nameSet.delete(name);
|
|
1298
1378
|
}
|
|
1299
|
-
else if (!result.
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
if (property) {
|
|
1303
|
-
result.image = {
|
|
1304
|
-
url: `\${${property}}`,
|
|
1305
|
-
alt: text,
|
|
1306
|
-
$when: `\${${property} != null}`,
|
|
1307
|
-
};
|
|
1308
|
-
}
|
|
1309
|
-
textBlockElements.delete(element);
|
|
1379
|
+
else if (!result.imageUrl && Utils.isWellKnownName(name, ConstantString.WellknownImageName)) {
|
|
1380
|
+
result.imageUrl = name;
|
|
1381
|
+
nameSet.delete(name);
|
|
1310
1382
|
}
|
|
1311
1383
|
}
|
|
1312
|
-
for (const
|
|
1313
|
-
const text = element.text;
|
|
1384
|
+
for (const name of nameSet) {
|
|
1314
1385
|
if (!result.title) {
|
|
1315
|
-
result.title =
|
|
1316
|
-
|
|
1386
|
+
result.title = name;
|
|
1387
|
+
nameSet.delete(name);
|
|
1317
1388
|
}
|
|
1318
1389
|
else if (!result.subtitle) {
|
|
1319
|
-
result.subtitle =
|
|
1320
|
-
|
|
1390
|
+
result.subtitle = name;
|
|
1391
|
+
nameSet.delete(name);
|
|
1321
1392
|
}
|
|
1322
1393
|
}
|
|
1323
1394
|
if (!result.title && result.subtitle) {
|
|
1324
1395
|
result.title = result.subtitle;
|
|
1325
1396
|
delete result.subtitle;
|
|
1326
1397
|
}
|
|
1327
|
-
if (!result.title) {
|
|
1328
|
-
result.title = "result";
|
|
1329
|
-
}
|
|
1330
1398
|
return result;
|
|
1331
1399
|
}
|
|
1332
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
|
+
// Insert plugins in manifest.json if it is plugin for Copilot.
|
|
1408
|
+
if (!options.isGptPlugin) {
|
|
1409
|
+
manifest.plugins = [
|
|
1410
|
+
{
|
|
1411
|
+
file: apiPluginRelativePath,
|
|
1412
|
+
id: ConstantString.DefaultPluginId,
|
|
1413
|
+
},
|
|
1414
|
+
];
|
|
1415
|
+
ManifestUpdater.updateManifestDescription(manifest, spec);
|
|
1416
|
+
}
|
|
1417
|
+
const appName = this.removeEnvs(manifest.name.short);
|
|
1418
|
+
const specRelativePath = ManifestUpdater.getRelativePath(manifestPath, outputSpecPath);
|
|
1419
|
+
const apiPlugin = yield ManifestUpdater.generatePluginManifestSchema(spec, specRelativePath, apiPluginFilePath, appName, authInfo, options);
|
|
1420
|
+
return [manifest, apiPlugin];
|
|
1421
|
+
});
|
|
1422
|
+
}
|
|
1423
|
+
static updateManifestDescription(manifest, spec) {
|
|
1424
|
+
var _a, _b;
|
|
1425
|
+
manifest.description = {
|
|
1426
|
+
short: spec.info.title.slice(0, ConstantString.ShortDescriptionMaxLens),
|
|
1427
|
+
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),
|
|
1428
|
+
};
|
|
1429
|
+
}
|
|
1430
|
+
static mapOpenAPISchemaToFuncParam(schema, method, pathUrl) {
|
|
1431
|
+
let parameter;
|
|
1432
|
+
if (schema.type === "array") {
|
|
1433
|
+
const items = schema.items;
|
|
1434
|
+
parameter = {
|
|
1435
|
+
type: "array",
|
|
1436
|
+
items: ManifestUpdater.mapOpenAPISchemaToFuncParam(items, method, pathUrl),
|
|
1437
|
+
};
|
|
1438
|
+
}
|
|
1439
|
+
else if (schema.type === "string" ||
|
|
1440
|
+
schema.type === "boolean" ||
|
|
1441
|
+
schema.type === "integer" ||
|
|
1442
|
+
schema.type === "number") {
|
|
1443
|
+
parameter = {
|
|
1444
|
+
type: schema.type,
|
|
1445
|
+
};
|
|
1446
|
+
}
|
|
1447
|
+
else {
|
|
1448
|
+
throw new SpecParserError(Utils.format(ConstantString.UnsupportedSchema, method, pathUrl, JSON.stringify(schema)), exports.ErrorType.UpdateManifestFailed);
|
|
1449
|
+
}
|
|
1450
|
+
if (schema.enum) {
|
|
1451
|
+
parameter.enum = schema.enum;
|
|
1452
|
+
}
|
|
1453
|
+
if (schema.description) {
|
|
1454
|
+
parameter.description = schema.description;
|
|
1455
|
+
}
|
|
1456
|
+
if (schema.default) {
|
|
1457
|
+
parameter.default = schema.default;
|
|
1458
|
+
}
|
|
1459
|
+
return parameter;
|
|
1460
|
+
}
|
|
1461
|
+
static generatePluginManifestSchema(spec, specRelativePath, apiPluginFilePath, appName, authInfo, options) {
|
|
1462
|
+
var _a, _b, _c, _d, _e;
|
|
1463
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1464
|
+
const functions = [];
|
|
1465
|
+
const functionNames = [];
|
|
1466
|
+
const conversationStarters = [];
|
|
1467
|
+
const paths = spec.paths;
|
|
1468
|
+
const pluginAuthObj = {
|
|
1469
|
+
type: "None",
|
|
1470
|
+
};
|
|
1471
|
+
if (authInfo) {
|
|
1472
|
+
if (Utils.isOAuthWithAuthCodeFlow(authInfo.authScheme)) {
|
|
1473
|
+
pluginAuthObj.type = "OAuthPluginVault";
|
|
1474
|
+
}
|
|
1475
|
+
else if (Utils.isBearerTokenAuth(authInfo.authScheme)) {
|
|
1476
|
+
pluginAuthObj.type = "ApiKeyPluginVault";
|
|
1477
|
+
}
|
|
1478
|
+
if (pluginAuthObj.type !== "None") {
|
|
1479
|
+
const safeRegistrationIdName = Utils.getSafeRegistrationIdEnvName(`${authInfo.name}_${ConstantString.RegistrationIdPostfix}`);
|
|
1480
|
+
pluginAuthObj.reference_id = `\${{${safeRegistrationIdName}}}`;
|
|
1481
|
+
}
|
|
1482
|
+
}
|
|
1483
|
+
for (const pathUrl in paths) {
|
|
1484
|
+
const pathItem = paths[pathUrl];
|
|
1485
|
+
if (pathItem) {
|
|
1486
|
+
const operations = pathItem;
|
|
1487
|
+
for (const method in operations) {
|
|
1488
|
+
if (options.allowMethods.includes(method)) {
|
|
1489
|
+
const operationItem = operations[method];
|
|
1490
|
+
const confirmationBodies = [];
|
|
1491
|
+
if (operationItem) {
|
|
1492
|
+
const operationId = operationItem.operationId;
|
|
1493
|
+
const description = (_a = operationItem.description) !== null && _a !== void 0 ? _a : "";
|
|
1494
|
+
const paramObject = operationItem.parameters;
|
|
1495
|
+
const requestBody = operationItem.requestBody;
|
|
1496
|
+
const parameters = {
|
|
1497
|
+
type: "object",
|
|
1498
|
+
properties: {},
|
|
1499
|
+
required: [],
|
|
1500
|
+
};
|
|
1501
|
+
if (paramObject) {
|
|
1502
|
+
for (let i = 0; i < paramObject.length; i++) {
|
|
1503
|
+
const param = paramObject[i];
|
|
1504
|
+
const schema = param.schema;
|
|
1505
|
+
parameters.properties[param.name] = ManifestUpdater.mapOpenAPISchemaToFuncParam(schema, method, pathUrl);
|
|
1506
|
+
confirmationBodies.push(ManifestUpdater.getConfirmationBodyItem(param.name));
|
|
1507
|
+
if (param.required) {
|
|
1508
|
+
parameters.required.push(param.name);
|
|
1509
|
+
}
|
|
1510
|
+
if (!parameters.properties[param.name].description) {
|
|
1511
|
+
parameters.properties[param.name].description = (_b = param.description) !== null && _b !== void 0 ? _b : "";
|
|
1512
|
+
}
|
|
1513
|
+
}
|
|
1514
|
+
}
|
|
1515
|
+
if (requestBody) {
|
|
1516
|
+
const requestJsonBody = requestBody.content["application/json"];
|
|
1517
|
+
const requestBodySchema = requestJsonBody.schema;
|
|
1518
|
+
if (requestBodySchema.type === "object") {
|
|
1519
|
+
if (requestBodySchema.required) {
|
|
1520
|
+
parameters.required.push(...requestBodySchema.required);
|
|
1521
|
+
}
|
|
1522
|
+
for (const property in requestBodySchema.properties) {
|
|
1523
|
+
const schema = requestBodySchema.properties[property];
|
|
1524
|
+
parameters.properties[property] = ManifestUpdater.mapOpenAPISchemaToFuncParam(schema, method, pathUrl);
|
|
1525
|
+
confirmationBodies.push(ManifestUpdater.getConfirmationBodyItem(property));
|
|
1526
|
+
}
|
|
1527
|
+
}
|
|
1528
|
+
else {
|
|
1529
|
+
throw new SpecParserError(Utils.format(ConstantString.UnsupportedSchema, method, pathUrl, JSON.stringify(requestBodySchema)), exports.ErrorType.UpdateManifestFailed);
|
|
1530
|
+
}
|
|
1531
|
+
}
|
|
1532
|
+
const funcObj = {
|
|
1533
|
+
name: operationId,
|
|
1534
|
+
description: description,
|
|
1535
|
+
};
|
|
1536
|
+
if (paramObject || requestBody) {
|
|
1537
|
+
funcObj.parameters = parameters;
|
|
1538
|
+
}
|
|
1539
|
+
if (options.allowResponseSemantics) {
|
|
1540
|
+
const { json } = Utils.getResponseJson(operationItem);
|
|
1541
|
+
if (json.schema) {
|
|
1542
|
+
const [card, jsonPath] = AdaptiveCardGenerator.generateAdaptiveCard(operationItem);
|
|
1543
|
+
const responseSemantic = wrapResponseSemantics(card, jsonPath);
|
|
1544
|
+
funcObj.capabilities = {
|
|
1545
|
+
response_semantics: responseSemantic,
|
|
1546
|
+
};
|
|
1547
|
+
}
|
|
1548
|
+
}
|
|
1549
|
+
if (options.allowConfirmation && method !== ConstantString.GetMethod) {
|
|
1550
|
+
if (!funcObj.capabilities) {
|
|
1551
|
+
funcObj.capabilities = {};
|
|
1552
|
+
}
|
|
1553
|
+
funcObj.capabilities.confirmation = {
|
|
1554
|
+
type: "AdaptiveCard",
|
|
1555
|
+
title: (_c = operationItem.summary) !== null && _c !== void 0 ? _c : description,
|
|
1556
|
+
};
|
|
1557
|
+
if (confirmationBodies.length > 0) {
|
|
1558
|
+
funcObj.capabilities.confirmation.body = confirmationBodies.join("\n");
|
|
1559
|
+
}
|
|
1560
|
+
}
|
|
1561
|
+
functions.push(funcObj);
|
|
1562
|
+
functionNames.push(operationId);
|
|
1563
|
+
if (description) {
|
|
1564
|
+
conversationStarters.push(description);
|
|
1565
|
+
}
|
|
1566
|
+
}
|
|
1567
|
+
}
|
|
1568
|
+
}
|
|
1569
|
+
}
|
|
1570
|
+
}
|
|
1571
|
+
let apiPlugin;
|
|
1572
|
+
if (yield fs__default['default'].pathExists(apiPluginFilePath)) {
|
|
1573
|
+
apiPlugin = yield fs__default['default'].readJSON(apiPluginFilePath);
|
|
1574
|
+
}
|
|
1575
|
+
else {
|
|
1576
|
+
apiPlugin = {
|
|
1577
|
+
schema_version: "v2.1",
|
|
1578
|
+
name_for_human: "",
|
|
1579
|
+
description_for_human: "",
|
|
1580
|
+
namespace: "",
|
|
1581
|
+
functions: [],
|
|
1582
|
+
runtimes: [],
|
|
1583
|
+
};
|
|
1584
|
+
}
|
|
1585
|
+
apiPlugin.functions = apiPlugin.functions || [];
|
|
1586
|
+
for (const func of functions) {
|
|
1587
|
+
const index = (_d = apiPlugin.functions) === null || _d === void 0 ? void 0 : _d.findIndex((f) => f.name === func.name);
|
|
1588
|
+
if (index === -1) {
|
|
1589
|
+
apiPlugin.functions.push(func);
|
|
1590
|
+
}
|
|
1591
|
+
else {
|
|
1592
|
+
apiPlugin.functions[index] = func;
|
|
1593
|
+
}
|
|
1594
|
+
}
|
|
1595
|
+
apiPlugin.runtimes = apiPlugin.runtimes || [];
|
|
1596
|
+
const index = apiPlugin.runtimes.findIndex((runtime) => {
|
|
1597
|
+
var _a, _b;
|
|
1598
|
+
return runtime.spec.url === specRelativePath &&
|
|
1599
|
+
runtime.type === "OpenApi" &&
|
|
1600
|
+
((_b = (_a = runtime.auth) === null || _a === void 0 ? void 0 : _a.type) !== null && _b !== void 0 ? _b : "None") === pluginAuthObj.type;
|
|
1601
|
+
});
|
|
1602
|
+
if (index === -1) {
|
|
1603
|
+
apiPlugin.runtimes.push({
|
|
1604
|
+
type: "OpenApi",
|
|
1605
|
+
auth: pluginAuthObj,
|
|
1606
|
+
spec: {
|
|
1607
|
+
url: specRelativePath,
|
|
1608
|
+
},
|
|
1609
|
+
run_for_functions: functionNames,
|
|
1610
|
+
});
|
|
1611
|
+
}
|
|
1612
|
+
else {
|
|
1613
|
+
apiPlugin.runtimes[index].run_for_functions = functionNames;
|
|
1614
|
+
}
|
|
1615
|
+
if (!apiPlugin.name_for_human) {
|
|
1616
|
+
apiPlugin.name_for_human = appName;
|
|
1617
|
+
}
|
|
1618
|
+
if (!apiPlugin.namespace) {
|
|
1619
|
+
apiPlugin.namespace = ManifestUpdater.removeAllSpecialCharacters(appName);
|
|
1620
|
+
}
|
|
1621
|
+
if (!apiPlugin.description_for_human) {
|
|
1622
|
+
apiPlugin.description_for_human =
|
|
1623
|
+
(_e = spec.info.description) !== null && _e !== void 0 ? _e : "<Please add description of the plugin>";
|
|
1624
|
+
}
|
|
1625
|
+
if (options.allowConversationStarters && conversationStarters.length > 0) {
|
|
1626
|
+
if (!apiPlugin.capabilities) {
|
|
1627
|
+
apiPlugin.capabilities = {
|
|
1628
|
+
localization: {},
|
|
1629
|
+
};
|
|
1630
|
+
}
|
|
1631
|
+
if (!apiPlugin.capabilities.conversation_starters) {
|
|
1632
|
+
apiPlugin.capabilities.conversation_starters = conversationStarters
|
|
1633
|
+
.slice(0, 5)
|
|
1634
|
+
.map((text) => ({ text }));
|
|
1635
|
+
}
|
|
1636
|
+
}
|
|
1637
|
+
return apiPlugin;
|
|
1638
|
+
});
|
|
1639
|
+
}
|
|
1640
|
+
static updateManifest(manifestPath, outputSpecPath, spec, options, adaptiveCardFolder, authInfo) {
|
|
1641
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1642
|
+
try {
|
|
1643
|
+
const originalManifest = yield fs__default['default'].readJSON(manifestPath);
|
|
1644
|
+
const updatedPart = {};
|
|
1645
|
+
updatedPart.composeExtensions = [];
|
|
1646
|
+
let warnings = [];
|
|
1647
|
+
if (options.projectType === exports.ProjectType.SME) {
|
|
1648
|
+
const updateResult = yield ManifestUpdater.generateCommands(spec, manifestPath, options, adaptiveCardFolder);
|
|
1649
|
+
const commands = updateResult[0];
|
|
1650
|
+
warnings = updateResult[1];
|
|
1651
|
+
const composeExtension = {
|
|
1652
|
+
composeExtensionType: "apiBased",
|
|
1653
|
+
apiSpecificationFile: ManifestUpdater.getRelativePath(manifestPath, outputSpecPath),
|
|
1654
|
+
commands: commands,
|
|
1655
|
+
};
|
|
1656
|
+
if (authInfo) {
|
|
1657
|
+
const auth = authInfo.authScheme;
|
|
1658
|
+
const safeRegistrationIdName = Utils.getSafeRegistrationIdEnvName(`${authInfo.name}_${ConstantString.RegistrationIdPostfix}`);
|
|
1659
|
+
if (Utils.isAPIKeyAuth(auth) || Utils.isBearerTokenAuth(auth)) {
|
|
1660
|
+
const safeApiSecretRegistrationId = Utils.getSafeRegistrationIdEnvName(`${authInfo.name}_${ConstantString.RegistrationIdPostfix}`);
|
|
1661
|
+
composeExtension.authorization = {
|
|
1662
|
+
authType: "apiSecretServiceAuth",
|
|
1663
|
+
apiSecretServiceAuthConfiguration: {
|
|
1664
|
+
apiSecretRegistrationId: `\${{${safeRegistrationIdName}}}`,
|
|
1665
|
+
},
|
|
1666
|
+
};
|
|
1667
|
+
}
|
|
1668
|
+
else if (Utils.isOAuthWithAuthCodeFlow(auth)) {
|
|
1669
|
+
composeExtension.authorization = {
|
|
1670
|
+
authType: "oAuth2.0",
|
|
1671
|
+
oAuthConfiguration: {
|
|
1672
|
+
oauthConfigurationId: `\${{${safeRegistrationIdName}}}`,
|
|
1673
|
+
},
|
|
1674
|
+
};
|
|
1675
|
+
updatedPart.webApplicationInfo = {
|
|
1676
|
+
id: "${{AAD_APP_CLIENT_ID}}",
|
|
1677
|
+
resource: "api://${{DOMAIN}}/${{AAD_APP_CLIENT_ID}}",
|
|
1678
|
+
};
|
|
1679
|
+
}
|
|
1680
|
+
}
|
|
1681
|
+
updatedPart.composeExtensions = [composeExtension];
|
|
1682
|
+
}
|
|
1683
|
+
updatedPart.description = originalManifest.description;
|
|
1684
|
+
ManifestUpdater.updateManifestDescription(updatedPart, spec);
|
|
1685
|
+
const updatedManifest = Object.assign(Object.assign({}, originalManifest), updatedPart);
|
|
1686
|
+
return [updatedManifest, warnings];
|
|
1687
|
+
}
|
|
1688
|
+
catch (err) {
|
|
1689
|
+
throw new SpecParserError(err.toString(), exports.ErrorType.UpdateManifestFailed);
|
|
1690
|
+
}
|
|
1691
|
+
});
|
|
1692
|
+
}
|
|
1693
|
+
static generateCommands(spec, manifestPath, options, adaptiveCardFolder) {
|
|
1694
|
+
var _a;
|
|
1695
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1696
|
+
const paths = spec.paths;
|
|
1697
|
+
const commands = [];
|
|
1698
|
+
const warnings = [];
|
|
1699
|
+
if (paths) {
|
|
1700
|
+
for (const pathUrl in paths) {
|
|
1701
|
+
const pathItem = paths[pathUrl];
|
|
1702
|
+
if (pathItem) {
|
|
1703
|
+
const operations = pathItem;
|
|
1704
|
+
// Currently only support GET and POST method
|
|
1705
|
+
for (const method in operations) {
|
|
1706
|
+
if ((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) {
|
|
1707
|
+
const operationItem = operations[method];
|
|
1708
|
+
if (operationItem) {
|
|
1709
|
+
const command = Utils.parseApiInfo(operationItem, options);
|
|
1710
|
+
if (command.parameters &&
|
|
1711
|
+
command.parameters.length >= 1 &&
|
|
1712
|
+
command.parameters.some((param) => param.isRequired)) {
|
|
1713
|
+
command.parameters = command.parameters.filter((param) => param.isRequired);
|
|
1714
|
+
}
|
|
1715
|
+
else if (command.parameters && command.parameters.length > 0) {
|
|
1716
|
+
command.parameters = [command.parameters[0]];
|
|
1717
|
+
warnings.push({
|
|
1718
|
+
type: exports.WarningType.OperationOnlyContainsOptionalParam,
|
|
1719
|
+
content: Utils.format(ConstantString.OperationOnlyContainsOptionalParam, command.id),
|
|
1720
|
+
data: command.id,
|
|
1721
|
+
});
|
|
1722
|
+
}
|
|
1723
|
+
if (adaptiveCardFolder) {
|
|
1724
|
+
const adaptiveCardPath = path__default['default'].join(adaptiveCardFolder, command.id + ".json");
|
|
1725
|
+
command.apiResponseRenderingTemplateFile = (yield fs__default['default'].pathExists(adaptiveCardPath))
|
|
1726
|
+
? ManifestUpdater.getRelativePath(manifestPath, adaptiveCardPath)
|
|
1727
|
+
: "";
|
|
1728
|
+
}
|
|
1729
|
+
commands.push(command);
|
|
1730
|
+
}
|
|
1731
|
+
}
|
|
1732
|
+
}
|
|
1733
|
+
}
|
|
1734
|
+
}
|
|
1735
|
+
}
|
|
1736
|
+
return [commands, warnings];
|
|
1737
|
+
});
|
|
1738
|
+
}
|
|
1739
|
+
static getRelativePath(from, to) {
|
|
1740
|
+
const relativePath = path__default['default'].relative(path__default['default'].dirname(from), to);
|
|
1741
|
+
return path__default['default'].normalize(relativePath).replace(/\\/g, "/");
|
|
1742
|
+
}
|
|
1743
|
+
static removeEnvs(str) {
|
|
1744
|
+
const placeHolderReg = /\${{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*}}/g;
|
|
1745
|
+
const matches = placeHolderReg.exec(str);
|
|
1746
|
+
let newStr = str;
|
|
1747
|
+
if (matches != null) {
|
|
1748
|
+
newStr = newStr.replace(matches[0], "");
|
|
1749
|
+
}
|
|
1750
|
+
return newStr;
|
|
1751
|
+
}
|
|
1752
|
+
static removeAllSpecialCharacters(str) {
|
|
1753
|
+
return str.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
1754
|
+
}
|
|
1755
|
+
static getConfirmationBodyItem(paramName) {
|
|
1756
|
+
return `* **${Utils.updateFirstLetter(paramName)}**: {{function.parameters.${paramName}}}`;
|
|
1757
|
+
}
|
|
1758
|
+
}
|
|
1759
|
+
|
|
1333
1760
|
// Copyright (c) Microsoft Corporation.
|
|
1334
1761
|
/**
|
|
1335
1762
|
* A class that parses an OpenAPI specification file and provides methods to validate, list, and generate artifacts.
|
|
@@ -1349,7 +1776,11 @@ class SpecParser {
|
|
|
1349
1776
|
allowMultipleParameters: false,
|
|
1350
1777
|
allowOauth2: false,
|
|
1351
1778
|
allowMethods: ["get", "post"],
|
|
1779
|
+
allowConversationStarters: false,
|
|
1780
|
+
allowResponseSemantics: false,
|
|
1781
|
+
allowConfirmation: false,
|
|
1352
1782
|
projectType: exports.ProjectType.SME,
|
|
1783
|
+
isGptPlugin: false,
|
|
1353
1784
|
};
|
|
1354
1785
|
this.pathOrSpec = pathOrDoc;
|
|
1355
1786
|
this.parser = new SwaggerParser__default['default']();
|
|
@@ -1374,6 +1805,8 @@ class SpecParser {
|
|
|
1374
1805
|
errors: [{ type: exports.ErrorType.SpecNotValid, content: e.toString() }],
|
|
1375
1806
|
};
|
|
1376
1807
|
}
|
|
1808
|
+
const errors = [];
|
|
1809
|
+
const warnings = [];
|
|
1377
1810
|
if (!this.options.allowSwagger && this.isSwaggerFile) {
|
|
1378
1811
|
return {
|
|
1379
1812
|
status: exports.ValidationStatus.Error,
|
|
@@ -1383,23 +1816,38 @@ class SpecParser {
|
|
|
1383
1816
|
],
|
|
1384
1817
|
};
|
|
1385
1818
|
}
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
}
|
|
1819
|
+
// Remote reference not supported
|
|
1820
|
+
const refPaths = this.parser.$refs.paths();
|
|
1821
|
+
// refPaths [0] is the current spec file path
|
|
1822
|
+
if (refPaths.length > 1) {
|
|
1823
|
+
errors.push({
|
|
1824
|
+
type: exports.ErrorType.RemoteRefNotSupported,
|
|
1825
|
+
content: Utils.format(ConstantString.RemoteRefNotSupported, refPaths.join(", ")),
|
|
1826
|
+
data: refPaths,
|
|
1827
|
+
});
|
|
1828
|
+
}
|
|
1829
|
+
if (!!this.isSwaggerFile && this.options.allowSwagger) {
|
|
1830
|
+
warnings.push({
|
|
1831
|
+
type: exports.WarningType.ConvertSwaggerToOpenAPI,
|
|
1832
|
+
content: ConstantString.ConvertSwaggerToOpenAPI,
|
|
1833
|
+
});
|
|
1401
1834
|
}
|
|
1402
|
-
|
|
1835
|
+
const validator = this.getValidator(this.spec);
|
|
1836
|
+
const validationResult = validator.validateSpec();
|
|
1837
|
+
warnings.push(...validationResult.warnings);
|
|
1838
|
+
errors.push(...validationResult.errors);
|
|
1839
|
+
let status = exports.ValidationStatus.Valid;
|
|
1840
|
+
if (warnings.length > 0 && errors.length === 0) {
|
|
1841
|
+
status = exports.ValidationStatus.Warning;
|
|
1842
|
+
}
|
|
1843
|
+
else if (errors.length > 0) {
|
|
1844
|
+
status = exports.ValidationStatus.Error;
|
|
1845
|
+
}
|
|
1846
|
+
return {
|
|
1847
|
+
status: status,
|
|
1848
|
+
warnings: warnings,
|
|
1849
|
+
errors: errors,
|
|
1850
|
+
};
|
|
1403
1851
|
}
|
|
1404
1852
|
catch (err) {
|
|
1405
1853
|
throw new SpecParserError(err.toString(), exports.ErrorType.ValidateFailed);
|
|
@@ -1423,45 +1871,40 @@ class SpecParser {
|
|
|
1423
1871
|
try {
|
|
1424
1872
|
yield this.loadSpec();
|
|
1425
1873
|
const spec = this.spec;
|
|
1426
|
-
const apiMap = this.
|
|
1874
|
+
const apiMap = this.getAPIs(spec);
|
|
1427
1875
|
const result = {
|
|
1428
|
-
|
|
1876
|
+
APIs: [],
|
|
1429
1877
|
allAPICount: 0,
|
|
1430
1878
|
validAPICount: 0,
|
|
1431
1879
|
};
|
|
1432
1880
|
for (const apiKey in apiMap) {
|
|
1881
|
+
const { operation, isValid, reason } = apiMap[apiKey];
|
|
1882
|
+
const [method, path] = apiKey.split(" ");
|
|
1883
|
+
const operationId = (_a = operation.operationId) !== null && _a !== void 0 ? _a : `${method.toLowerCase()}${Utils.convertPathToCamelCase(path)}`;
|
|
1433
1884
|
const apiResult = {
|
|
1434
|
-
api:
|
|
1885
|
+
api: apiKey,
|
|
1435
1886
|
server: "",
|
|
1436
|
-
operationId:
|
|
1887
|
+
operationId: operationId,
|
|
1888
|
+
isValid: isValid,
|
|
1889
|
+
reason: reason,
|
|
1437
1890
|
};
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
if (!operationId) {
|
|
1450
|
-
operationId = `${method.toLowerCase()}${Utils.convertPathToCamelCase(path)}`;
|
|
1451
|
-
}
|
|
1452
|
-
apiResult.operationId = operationId;
|
|
1453
|
-
const authArray = Utils.getAuthArray(operation.security, spec);
|
|
1454
|
-
for (const auths of authArray) {
|
|
1455
|
-
if (auths.length === 1) {
|
|
1456
|
-
apiResult.auth = auths[0];
|
|
1457
|
-
break;
|
|
1891
|
+
if (isValid) {
|
|
1892
|
+
const serverObj = Utils.getServerObject(spec, method.toLocaleLowerCase(), path);
|
|
1893
|
+
if (serverObj) {
|
|
1894
|
+
apiResult.server = Utils.resolveEnv(serverObj.url);
|
|
1895
|
+
}
|
|
1896
|
+
const authArray = Utils.getAuthArray(operation.security, spec);
|
|
1897
|
+
for (const auths of authArray) {
|
|
1898
|
+
if (auths.length === 1) {
|
|
1899
|
+
apiResult.auth = auths[0];
|
|
1900
|
+
break;
|
|
1901
|
+
}
|
|
1458
1902
|
}
|
|
1459
1903
|
}
|
|
1460
|
-
apiResult
|
|
1461
|
-
result.validAPIs.push(apiResult);
|
|
1904
|
+
result.APIs.push(apiResult);
|
|
1462
1905
|
}
|
|
1463
|
-
result.allAPICount =
|
|
1464
|
-
result.validAPICount = result.
|
|
1906
|
+
result.allAPICount = result.APIs.length;
|
|
1907
|
+
result.validAPICount = result.APIs.filter((api) => api.isValid).length;
|
|
1465
1908
|
return result;
|
|
1466
1909
|
}
|
|
1467
1910
|
catch (err) {
|
|
@@ -1518,18 +1961,12 @@ class SpecParser {
|
|
|
1518
1961
|
const newSpecs = yield this.getFilteredSpecs(filter, signal);
|
|
1519
1962
|
const newUnResolvedSpec = newSpecs[0];
|
|
1520
1963
|
const newSpec = newSpecs[1];
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
resultStr = jsyaml__default['default'].dump(newUnResolvedSpec);
|
|
1524
|
-
}
|
|
1525
|
-
else {
|
|
1526
|
-
resultStr = JSON.stringify(newUnResolvedSpec, null, 2);
|
|
1527
|
-
}
|
|
1528
|
-
yield fs__default['default'].outputFile(outputSpecPath, resultStr);
|
|
1964
|
+
const authInfo = Utils.getAuthInfo(newSpec);
|
|
1965
|
+
yield this.saveFilterSpec(outputSpecPath, newUnResolvedSpec);
|
|
1529
1966
|
if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
|
|
1530
1967
|
throw new SpecParserError(ConstantString.CancelledMessage, exports.ErrorType.Cancelled);
|
|
1531
1968
|
}
|
|
1532
|
-
const [updatedManifest, apiPlugin] = yield ManifestUpdater.updateManifestWithAiPlugin(manifestPath, outputSpecPath, pluginFilePath, newSpec, this.options);
|
|
1969
|
+
const [updatedManifest, apiPlugin] = yield ManifestUpdater.updateManifestWithAiPlugin(manifestPath, outputSpecPath, pluginFilePath, newSpec, this.options, authInfo);
|
|
1533
1970
|
yield fs__default['default'].outputJSON(manifestPath, updatedManifest, { spaces: 2 });
|
|
1534
1971
|
yield fs__default['default'].outputJSON(pluginFilePath, apiPlugin, { spaces: 2 });
|
|
1535
1972
|
}
|
|
@@ -1559,32 +1996,11 @@ class SpecParser {
|
|
|
1559
1996
|
const newSpecs = yield this.getFilteredSpecs(filter, signal);
|
|
1560
1997
|
const newUnResolvedSpec = newSpecs[0];
|
|
1561
1998
|
const newSpec = newSpecs[1];
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
for (const method in newSpec.paths[url]) {
|
|
1566
|
-
const operation = newSpec.paths[url][method];
|
|
1567
|
-
const authArray = Utils.getAuthArray(operation.security, newSpec);
|
|
1568
|
-
if (authArray && authArray.length > 0) {
|
|
1569
|
-
authSet.add(authArray[0][0]);
|
|
1570
|
-
if (authSet.size > 1) {
|
|
1571
|
-
hasMultipleAuth = true;
|
|
1572
|
-
break;
|
|
1573
|
-
}
|
|
1574
|
-
}
|
|
1575
|
-
}
|
|
1576
|
-
}
|
|
1577
|
-
if (hasMultipleAuth && this.options.projectType !== exports.ProjectType.TeamsAi) {
|
|
1578
|
-
throw new SpecParserError(ConstantString.MultipleAuthNotSupported, exports.ErrorType.MultipleAuthNotSupported);
|
|
1579
|
-
}
|
|
1580
|
-
let resultStr;
|
|
1581
|
-
if (outputSpecPath.endsWith(".yaml") || outputSpecPath.endsWith(".yml")) {
|
|
1582
|
-
resultStr = jsyaml__default['default'].dump(newUnResolvedSpec);
|
|
1999
|
+
let authInfo = undefined;
|
|
2000
|
+
if (this.options.projectType === exports.ProjectType.SME) {
|
|
2001
|
+
authInfo = Utils.getAuthInfo(newSpec);
|
|
1583
2002
|
}
|
|
1584
|
-
|
|
1585
|
-
resultStr = JSON.stringify(newUnResolvedSpec, null, 2);
|
|
1586
|
-
}
|
|
1587
|
-
yield fs__default['default'].outputFile(outputSpecPath, resultStr);
|
|
2003
|
+
yield this.saveFilterSpec(outputSpecPath, newUnResolvedSpec);
|
|
1588
2004
|
if (adaptiveCardFolder) {
|
|
1589
2005
|
for (const url in newSpec.paths) {
|
|
1590
2006
|
for (const method in newSpec.paths[url]) {
|
|
@@ -1614,7 +2030,6 @@ class SpecParser {
|
|
|
1614
2030
|
if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
|
|
1615
2031
|
throw new SpecParserError(ConstantString.CancelledMessage, exports.ErrorType.Cancelled);
|
|
1616
2032
|
}
|
|
1617
|
-
const authInfo = Array.from(authSet)[0];
|
|
1618
2033
|
const [updatedManifest, warnings] = yield ManifestUpdater.updateManifest(manifestPath, outputSpecPath, newSpec, this.options, adaptiveCardFolder, authInfo);
|
|
1619
2034
|
yield fs__default['default'].outputJSON(manifestPath, updatedManifest, { spaces: 2 });
|
|
1620
2035
|
result.warnings.push(...warnings);
|
|
@@ -1643,13 +2058,30 @@ class SpecParser {
|
|
|
1643
2058
|
}
|
|
1644
2059
|
});
|
|
1645
2060
|
}
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
2061
|
+
getAPIs(spec) {
|
|
2062
|
+
const validator = this.getValidator(spec);
|
|
2063
|
+
const apiMap = validator.listAPIs();
|
|
2064
|
+
return apiMap;
|
|
2065
|
+
}
|
|
2066
|
+
getValidator(spec) {
|
|
2067
|
+
if (this.validator) {
|
|
2068
|
+
return this.validator;
|
|
1649
2069
|
}
|
|
1650
|
-
const
|
|
1651
|
-
this.
|
|
1652
|
-
return
|
|
2070
|
+
const validator = ValidatorFactory.create(spec, this.options);
|
|
2071
|
+
this.validator = validator;
|
|
2072
|
+
return validator;
|
|
2073
|
+
}
|
|
2074
|
+
saveFilterSpec(outputSpecPath, unResolvedSpec) {
|
|
2075
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2076
|
+
let resultStr;
|
|
2077
|
+
if (outputSpecPath.endsWith(".yaml") || outputSpecPath.endsWith(".yml")) {
|
|
2078
|
+
resultStr = jsyaml__default['default'].dump(unResolvedSpec);
|
|
2079
|
+
}
|
|
2080
|
+
else {
|
|
2081
|
+
resultStr = JSON.stringify(unResolvedSpec, null, 2);
|
|
2082
|
+
}
|
|
2083
|
+
yield fs__default['default'].outputFile(outputSpecPath, resultStr);
|
|
2084
|
+
});
|
|
1653
2085
|
}
|
|
1654
2086
|
}
|
|
1655
2087
|
|