@microsoft/m365-spec-parser 0.1.1-alpha.2f5decfcc.0 → 0.1.1-alpha.3bb446445.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 +615 -334
- package/dist/index.esm2017.js.map +1 -1
- package/dist/index.esm2017.mjs +1053 -635
- package/dist/index.esm2017.mjs.map +1 -1
- package/dist/index.esm5.js +615 -334
- package/dist/index.esm5.js.map +1 -1
- package/dist/index.node.cjs.js +1062 -640
- package/dist/index.node.cjs.js.map +1 -1
- package/dist/src/adaptiveCardWrapper.d.ts +2 -0
- package/dist/src/constants.d.ts +8 -3
- package/dist/src/index.d.ts +1 -1
- package/dist/src/interfaces.d.ts +66 -1
- package/dist/src/manifestUpdater.d.ts +7 -4
- 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 -4
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 = {}));
|
|
@@ -109,7 +125,7 @@ ConstantString.RemoteRefNotSupported = "Remote reference is not supported: %s.";
|
|
|
109
125
|
ConstantString.MissingOperationId = "Missing operationIds: %s.";
|
|
110
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.";
|
|
111
127
|
ConstantString.AdditionalPropertiesNotSupported = "'additionalProperties' is not supported, and will be ignored.";
|
|
112
|
-
ConstantString.SchemaNotSupported = "'oneOf', 'anyOf', and 'not' schema are not supported: %s.";
|
|
128
|
+
ConstantString.SchemaNotSupported = "'oneOf', 'allOf', 'anyOf', and 'not' schema are not supported: %s.";
|
|
113
129
|
ConstantString.UnknownSchema = "Unknown schema: %s.";
|
|
114
130
|
ConstantString.UrlProtocolNotSupported = "Server url is not correct: protocol %s is not supported, you should use https protocol instead.";
|
|
115
131
|
ConstantString.RelativeServerUrlNotSupported = "Server url is not correct: relative server url is not supported.";
|
|
@@ -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,9 +145,14 @@ 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
|
-
ConstantString.RegistrationIdPostfix =
|
|
133
|
-
|
|
150
|
+
ConstantString.RegistrationIdPostfix = {
|
|
151
|
+
apiKey: "REGISTRATION_ID",
|
|
152
|
+
oauth2: "CONFIGURATION_ID",
|
|
153
|
+
http: "REGISTRATION_ID",
|
|
154
|
+
openIdConnect: "REGISTRATION_ID",
|
|
155
|
+
};
|
|
134
156
|
ConstantString.ResponseCodeFor20X = [
|
|
135
157
|
"200",
|
|
136
158
|
"201",
|
|
@@ -189,9 +211,11 @@ ConstantString.ShortDescriptionMaxLens = 80;
|
|
|
189
211
|
ConstantString.FullDescriptionMaxLens = 4000;
|
|
190
212
|
ConstantString.CommandDescriptionMaxLens = 128;
|
|
191
213
|
ConstantString.ParameterDescriptionMaxLens = 128;
|
|
214
|
+
ConstantString.ConversationStarterMaxLens = 50;
|
|
192
215
|
ConstantString.CommandTitleMaxLens = 32;
|
|
193
216
|
ConstantString.ParameterTitleMaxLens = 32;
|
|
194
|
-
ConstantString.SMERequiredParamsMaxNum = 5;
|
|
217
|
+
ConstantString.SMERequiredParamsMaxNum = 5;
|
|
218
|
+
ConstantString.DefaultPluginId = "plugin_1";
|
|
195
219
|
|
|
196
220
|
// Copyright (c) Microsoft Corporation.
|
|
197
221
|
class SpecParserError extends Error {
|
|
@@ -214,221 +238,9 @@ class Utils {
|
|
|
214
238
|
}
|
|
215
239
|
return false;
|
|
216
240
|
}
|
|
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
241
|
static containMultipleMediaTypes(bodyObject) {
|
|
316
242
|
return Object.keys((bodyObject === null || bodyObject === void 0 ? void 0 : bodyObject.content) || {}).length > 1;
|
|
317
243
|
}
|
|
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
244
|
static isBearerTokenAuth(authScheme) {
|
|
433
245
|
return authScheme.type === "http" && authScheme.scheme === "bearer";
|
|
434
246
|
}
|
|
@@ -436,18 +248,18 @@ class Utils {
|
|
|
436
248
|
return authScheme.type === "apiKey";
|
|
437
249
|
}
|
|
438
250
|
static isOAuthWithAuthCodeFlow(authScheme) {
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
return false;
|
|
251
|
+
return !!(authScheme.type === "oauth2" &&
|
|
252
|
+
authScheme.flows &&
|
|
253
|
+
authScheme.flows.authorizationCode);
|
|
443
254
|
}
|
|
444
255
|
static getAuthArray(securities, spec) {
|
|
445
256
|
var _a;
|
|
446
257
|
const result = [];
|
|
447
258
|
const securitySchemas = (_a = spec.components) === null || _a === void 0 ? void 0 : _a.securitySchemes;
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
259
|
+
const securitiesArr = securities !== null && securities !== void 0 ? securities : spec.security;
|
|
260
|
+
if (securitiesArr && securitySchemas) {
|
|
261
|
+
for (let i = 0; i < securitiesArr.length; i++) {
|
|
262
|
+
const security = securitiesArr[i];
|
|
451
263
|
const authArray = [];
|
|
452
264
|
for (const name in security) {
|
|
453
265
|
const auth = securitySchemas[name];
|
|
@@ -464,17 +276,39 @@ class Utils {
|
|
|
464
276
|
result.sort((a, b) => a[0].name.localeCompare(b[0].name));
|
|
465
277
|
return result;
|
|
466
278
|
}
|
|
279
|
+
static getAuthInfo(spec) {
|
|
280
|
+
let authInfo = undefined;
|
|
281
|
+
for (const url in spec.paths) {
|
|
282
|
+
for (const method in spec.paths[url]) {
|
|
283
|
+
const operation = spec.paths[url][method];
|
|
284
|
+
const authArray = Utils.getAuthArray(operation.security, spec);
|
|
285
|
+
if (authArray && authArray.length > 0) {
|
|
286
|
+
const currentAuth = authArray[0][0];
|
|
287
|
+
if (!authInfo) {
|
|
288
|
+
authInfo = authArray[0][0];
|
|
289
|
+
}
|
|
290
|
+
else if (authInfo.name !== currentAuth.name) {
|
|
291
|
+
throw new SpecParserError(ConstantString.MultipleAuthNotSupported, exports.ErrorType.MultipleAuthNotSupported);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
return authInfo;
|
|
297
|
+
}
|
|
467
298
|
static updateFirstLetter(str) {
|
|
468
299
|
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
469
300
|
}
|
|
470
|
-
static getResponseJson(operationObject
|
|
301
|
+
static getResponseJson(operationObject) {
|
|
471
302
|
var _a, _b;
|
|
472
303
|
let json = {};
|
|
304
|
+
let multipleMediaType = false;
|
|
473
305
|
for (const code of ConstantString.ResponseCodeFor20X) {
|
|
474
306
|
const responseObject = (_a = operationObject === null || operationObject === void 0 ? void 0 : operationObject.responses) === null || _a === void 0 ? void 0 : _a[code];
|
|
475
307
|
if ((_b = responseObject === null || responseObject === void 0 ? void 0 : responseObject.content) === null || _b === void 0 ? void 0 : _b["application/json"]) {
|
|
308
|
+
multipleMediaType = false;
|
|
476
309
|
json = responseObject.content["application/json"];
|
|
477
|
-
if (
|
|
310
|
+
if (Utils.containMultipleMediaTypes(responseObject)) {
|
|
311
|
+
multipleMediaType = true;
|
|
478
312
|
json = {};
|
|
479
313
|
}
|
|
480
314
|
else {
|
|
@@ -482,7 +316,7 @@ class Utils {
|
|
|
482
316
|
}
|
|
483
317
|
}
|
|
484
318
|
}
|
|
485
|
-
return json;
|
|
319
|
+
return { json, multipleMediaType };
|
|
486
320
|
}
|
|
487
321
|
static convertPathToCamelCase(path) {
|
|
488
322
|
const pathSegments = path.split(/[./{]/);
|
|
@@ -502,10 +336,10 @@ class Utils {
|
|
|
502
336
|
return undefined;
|
|
503
337
|
}
|
|
504
338
|
}
|
|
505
|
-
static
|
|
339
|
+
static resolveEnv(str) {
|
|
506
340
|
const placeHolderReg = /\${{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*}}/g;
|
|
507
|
-
let matches = placeHolderReg.exec(
|
|
508
|
-
let
|
|
341
|
+
let matches = placeHolderReg.exec(str);
|
|
342
|
+
let newStr = str;
|
|
509
343
|
while (matches != null) {
|
|
510
344
|
const envVar = matches[1];
|
|
511
345
|
const envVal = process.env[envVar];
|
|
@@ -513,17 +347,17 @@ class Utils {
|
|
|
513
347
|
throw new Error(Utils.format(ConstantString.ResolveServerUrlFailed, envVar));
|
|
514
348
|
}
|
|
515
349
|
else {
|
|
516
|
-
|
|
350
|
+
newStr = newStr.replace(matches[0], envVal);
|
|
517
351
|
}
|
|
518
|
-
matches = placeHolderReg.exec(
|
|
352
|
+
matches = placeHolderReg.exec(str);
|
|
519
353
|
}
|
|
520
|
-
return
|
|
354
|
+
return newStr;
|
|
521
355
|
}
|
|
522
356
|
static checkServerUrl(servers) {
|
|
523
357
|
const errors = [];
|
|
524
358
|
let serverUrl;
|
|
525
359
|
try {
|
|
526
|
-
serverUrl = Utils.
|
|
360
|
+
serverUrl = Utils.resolveEnv(servers[0].url);
|
|
527
361
|
}
|
|
528
362
|
catch (err) {
|
|
529
363
|
errors.push({
|
|
@@ -554,6 +388,7 @@ class Utils {
|
|
|
554
388
|
return errors;
|
|
555
389
|
}
|
|
556
390
|
static validateServer(spec, options) {
|
|
391
|
+
var _a;
|
|
557
392
|
const errors = [];
|
|
558
393
|
let hasTopLevelServers = false;
|
|
559
394
|
let hasPathLevelServers = false;
|
|
@@ -574,7 +409,7 @@ class Utils {
|
|
|
574
409
|
}
|
|
575
410
|
for (const method in methods) {
|
|
576
411
|
const operationObject = methods[method];
|
|
577
|
-
if (
|
|
412
|
+
if (((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && operationObject) {
|
|
578
413
|
if ((operationObject === null || operationObject === void 0 ? void 0 : operationObject.servers) && operationObject.servers.length >= 1) {
|
|
579
414
|
hasOperationLevelServers = true;
|
|
580
415
|
const serverErrors = Utils.checkServerUrl(operationObject.servers);
|
|
@@ -701,13 +536,7 @@ class Utils {
|
|
|
701
536
|
}
|
|
702
537
|
}
|
|
703
538
|
const operationId = operationItem.operationId;
|
|
704
|
-
const parameters = [];
|
|
705
|
-
if (requiredParams.length !== 0) {
|
|
706
|
-
parameters.push(...requiredParams);
|
|
707
|
-
}
|
|
708
|
-
else {
|
|
709
|
-
parameters.push(optionalParams[0]);
|
|
710
|
-
}
|
|
539
|
+
const parameters = [...requiredParams, ...optionalParams];
|
|
711
540
|
const command = {
|
|
712
541
|
context: ["compose"],
|
|
713
542
|
type: "query",
|
|
@@ -716,366 +545,575 @@ class Utils {
|
|
|
716
545
|
parameters: parameters,
|
|
717
546
|
description: ((_b = operationItem.description) !== null && _b !== void 0 ? _b : "").slice(0, ConstantString.CommandDescriptionMaxLens),
|
|
718
547
|
};
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
548
|
+
return command;
|
|
549
|
+
}
|
|
550
|
+
static format(str, ...args) {
|
|
551
|
+
let index = 0;
|
|
552
|
+
return str.replace(/%s/g, () => {
|
|
553
|
+
const arg = args[index++];
|
|
554
|
+
return arg !== undefined ? arg : "";
|
|
555
|
+
});
|
|
556
|
+
}
|
|
557
|
+
static getSafeRegistrationIdEnvName(authName) {
|
|
558
|
+
if (!authName) {
|
|
559
|
+
return "";
|
|
726
560
|
}
|
|
727
|
-
|
|
561
|
+
let safeRegistrationIdEnvName = authName.toUpperCase().replace(/[^A-Z0-9_]/g, "_");
|
|
562
|
+
if (!safeRegistrationIdEnvName.match(/^[A-Z]/)) {
|
|
563
|
+
safeRegistrationIdEnvName = "PREFIX_" + safeRegistrationIdEnvName;
|
|
564
|
+
}
|
|
565
|
+
return safeRegistrationIdEnvName;
|
|
728
566
|
}
|
|
729
|
-
static
|
|
730
|
-
const
|
|
567
|
+
static getServerObject(spec, method, path) {
|
|
568
|
+
const pathObj = spec.paths[path];
|
|
569
|
+
const operationObject = pathObj[method];
|
|
570
|
+
const rootServer = spec.servers && spec.servers[0];
|
|
571
|
+
const methodServer = spec.paths[path].servers && spec.paths[path].servers[0];
|
|
572
|
+
const operationServer = operationObject.servers && operationObject.servers[0];
|
|
573
|
+
const serverUrl = operationServer || methodServer || rootServer;
|
|
574
|
+
return serverUrl;
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
// Copyright (c) Microsoft Corporation.
|
|
579
|
+
class Validator {
|
|
580
|
+
listAPIs() {
|
|
581
|
+
var _a;
|
|
582
|
+
if (this.apiMap) {
|
|
583
|
+
return this.apiMap;
|
|
584
|
+
}
|
|
585
|
+
const paths = this.spec.paths;
|
|
731
586
|
const result = {};
|
|
732
587
|
for (const path in paths) {
|
|
733
588
|
const methods = paths[path];
|
|
734
589
|
for (const method in methods) {
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
590
|
+
const operationObject = methods[method];
|
|
591
|
+
if (((_a = this.options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && operationObject) {
|
|
592
|
+
const validateResult = this.validateAPI(method, path);
|
|
593
|
+
result[`${method.toUpperCase()} ${path}`] = {
|
|
594
|
+
operation: operationObject,
|
|
595
|
+
isValid: validateResult.isValid,
|
|
596
|
+
reason: validateResult.reason,
|
|
597
|
+
};
|
|
738
598
|
}
|
|
739
599
|
}
|
|
740
600
|
}
|
|
601
|
+
this.apiMap = result;
|
|
741
602
|
return result;
|
|
742
603
|
}
|
|
743
|
-
|
|
744
|
-
const
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
604
|
+
validateSpecVersion() {
|
|
605
|
+
const result = { errors: [], warnings: [] };
|
|
606
|
+
if (this.spec.openapi >= "3.1.0") {
|
|
607
|
+
result.errors.push({
|
|
608
|
+
type: exports.ErrorType.SpecVersionNotSupported,
|
|
609
|
+
content: Utils.format(ConstantString.SpecVersionNotSupported, this.spec.openapi),
|
|
610
|
+
data: this.spec.openapi,
|
|
750
611
|
});
|
|
751
612
|
}
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
const
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
613
|
+
return result;
|
|
614
|
+
}
|
|
615
|
+
validateSpecServer() {
|
|
616
|
+
const result = { errors: [], warnings: [] };
|
|
617
|
+
const serverErrors = Utils.validateServer(this.spec, this.options);
|
|
618
|
+
result.errors.push(...serverErrors);
|
|
619
|
+
return result;
|
|
620
|
+
}
|
|
621
|
+
validateSpecNoSupportAPI() {
|
|
622
|
+
const result = { errors: [], warnings: [] };
|
|
623
|
+
const apiMap = this.listAPIs();
|
|
624
|
+
const validAPIs = Object.entries(apiMap).filter(([, value]) => value.isValid);
|
|
625
|
+
if (validAPIs.length === 0) {
|
|
626
|
+
const data = [];
|
|
627
|
+
for (const key in apiMap) {
|
|
628
|
+
const { reason } = apiMap[key];
|
|
629
|
+
const apiInvalidReason = { api: key, reason: reason };
|
|
630
|
+
data.push(apiInvalidReason);
|
|
631
|
+
}
|
|
632
|
+
result.errors.push({
|
|
769
633
|
type: exports.ErrorType.NoSupportedApi,
|
|
770
634
|
content: ConstantString.NoSupportedApi,
|
|
635
|
+
data,
|
|
771
636
|
});
|
|
772
637
|
}
|
|
638
|
+
return result;
|
|
639
|
+
}
|
|
640
|
+
validateSpecOperationId() {
|
|
641
|
+
const result = { errors: [], warnings: [] };
|
|
642
|
+
const apiMap = this.listAPIs();
|
|
773
643
|
// OperationId missing
|
|
774
644
|
const apisMissingOperationId = [];
|
|
775
645
|
for (const key in apiMap) {
|
|
776
|
-
const
|
|
777
|
-
if (!
|
|
646
|
+
const { operation } = apiMap[key];
|
|
647
|
+
if (!operation.operationId) {
|
|
778
648
|
apisMissingOperationId.push(key);
|
|
779
649
|
}
|
|
780
650
|
}
|
|
781
651
|
if (apisMissingOperationId.length > 0) {
|
|
782
|
-
warnings.push({
|
|
652
|
+
result.warnings.push({
|
|
783
653
|
type: exports.WarningType.OperationIdMissing,
|
|
784
654
|
content: Utils.format(ConstantString.MissingOperationId, apisMissingOperationId.join(", ")),
|
|
785
655
|
data: apisMissingOperationId,
|
|
786
656
|
});
|
|
787
657
|
}
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
658
|
+
return result;
|
|
659
|
+
}
|
|
660
|
+
validateMethodAndPath(method, path) {
|
|
661
|
+
const result = { isValid: true, reason: [] };
|
|
662
|
+
if (this.options.allowMethods && !this.options.allowMethods.includes(method)) {
|
|
663
|
+
result.isValid = false;
|
|
664
|
+
result.reason.push(exports.ErrorType.MethodNotAllowed);
|
|
665
|
+
return result;
|
|
791
666
|
}
|
|
792
|
-
|
|
793
|
-
|
|
667
|
+
const pathObj = this.spec.paths[path];
|
|
668
|
+
if (!pathObj || !pathObj[method]) {
|
|
669
|
+
result.isValid = false;
|
|
670
|
+
result.reason.push(exports.ErrorType.UrlPathNotExist);
|
|
671
|
+
return result;
|
|
794
672
|
}
|
|
795
|
-
return
|
|
796
|
-
status,
|
|
797
|
-
warnings,
|
|
798
|
-
errors,
|
|
799
|
-
};
|
|
673
|
+
return result;
|
|
800
674
|
}
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
675
|
+
validateResponse(method, path) {
|
|
676
|
+
const result = { isValid: true, reason: [] };
|
|
677
|
+
const operationObject = this.spec.paths[path][method];
|
|
678
|
+
const { json, multipleMediaType } = Utils.getResponseJson(operationObject);
|
|
679
|
+
if (this.options.projectType === exports.ProjectType.SME) {
|
|
680
|
+
// only support response body only contains “application/json” content type
|
|
681
|
+
if (multipleMediaType) {
|
|
682
|
+
result.reason.push(exports.ErrorType.ResponseContainMultipleMediaTypes);
|
|
683
|
+
}
|
|
684
|
+
else if (Object.keys(json).length === 0) {
|
|
685
|
+
// response body should not be empty
|
|
686
|
+
result.reason.push(exports.ErrorType.ResponseJsonIsEmpty);
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
return result;
|
|
807
690
|
}
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
691
|
+
validateServer(method, path) {
|
|
692
|
+
const result = { isValid: true, reason: [] };
|
|
693
|
+
const serverObj = Utils.getServerObject(this.spec, method, path);
|
|
694
|
+
if (!serverObj) {
|
|
695
|
+
// should contain server URL
|
|
696
|
+
result.reason.push(exports.ErrorType.NoServerInformation);
|
|
811
697
|
}
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
698
|
+
else {
|
|
699
|
+
// server url should be absolute url with https protocol
|
|
700
|
+
const serverValidateResult = Utils.checkServerUrl([serverObj]);
|
|
701
|
+
result.reason.push(...serverValidateResult.map((item) => item.type));
|
|
815
702
|
}
|
|
816
|
-
return
|
|
703
|
+
return result;
|
|
817
704
|
}
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
const
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
705
|
+
validateAuth(method, path) {
|
|
706
|
+
const pathObj = this.spec.paths[path];
|
|
707
|
+
const operationObject = pathObj[method];
|
|
708
|
+
const securities = operationObject.security;
|
|
709
|
+
const authSchemeArray = Utils.getAuthArray(securities, this.spec);
|
|
710
|
+
if (authSchemeArray.length === 0) {
|
|
711
|
+
return { isValid: true, reason: [] };
|
|
712
|
+
}
|
|
713
|
+
if (this.options.allowAPIKeyAuth ||
|
|
714
|
+
this.options.allowOauth2 ||
|
|
715
|
+
this.options.allowBearerTokenAuth) {
|
|
716
|
+
// Currently we don't support multiple auth in one operation
|
|
717
|
+
if (authSchemeArray.length > 0 && authSchemeArray.every((auths) => auths.length > 1)) {
|
|
718
|
+
return {
|
|
719
|
+
isValid: false,
|
|
720
|
+
reason: [exports.ErrorType.MultipleAuthNotSupported],
|
|
721
|
+
};
|
|
722
|
+
}
|
|
723
|
+
for (const auths of authSchemeArray) {
|
|
724
|
+
if (auths.length === 1) {
|
|
725
|
+
if ((this.options.allowAPIKeyAuth && Utils.isAPIKeyAuth(auths[0].authScheme)) ||
|
|
726
|
+
(this.options.allowOauth2 && Utils.isOAuthWithAuthCodeFlow(auths[0].authScheme)) ||
|
|
727
|
+
(this.options.allowBearerTokenAuth && Utils.isBearerTokenAuth(auths[0].authScheme))) {
|
|
728
|
+
return { isValid: true, reason: [] };
|
|
729
|
+
}
|
|
826
730
|
}
|
|
827
731
|
}
|
|
828
732
|
}
|
|
829
|
-
return
|
|
733
|
+
return { isValid: false, reason: [exports.ErrorType.AuthTypeIsNotSupported] };
|
|
830
734
|
}
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
735
|
+
checkPostBodySchema(schema, isRequired = false) {
|
|
736
|
+
var _a;
|
|
737
|
+
const paramResult = {
|
|
738
|
+
requiredNum: 0,
|
|
739
|
+
optionalNum: 0,
|
|
740
|
+
isValid: true,
|
|
741
|
+
reason: [],
|
|
742
|
+
};
|
|
743
|
+
if (Object.keys(schema).length === 0) {
|
|
744
|
+
return paramResult;
|
|
745
|
+
}
|
|
746
|
+
const isRequiredWithoutDefault = isRequired && schema.default === undefined;
|
|
747
|
+
const isCopilot = this.projectType === exports.ProjectType.Copilot;
|
|
748
|
+
if (isCopilot && this.hasNestedObjectInSchema(schema)) {
|
|
749
|
+
paramResult.isValid = false;
|
|
750
|
+
paramResult.reason = [exports.ErrorType.RequestBodyContainsNestedObject];
|
|
751
|
+
return paramResult;
|
|
752
|
+
}
|
|
753
|
+
if (schema.type === "string" ||
|
|
754
|
+
schema.type === "integer" ||
|
|
755
|
+
schema.type === "boolean" ||
|
|
756
|
+
schema.type === "number") {
|
|
757
|
+
if (isRequiredWithoutDefault) {
|
|
758
|
+
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
759
|
+
}
|
|
760
|
+
else {
|
|
761
|
+
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
else if (schema.type === "object") {
|
|
765
|
+
const { properties } = schema;
|
|
766
|
+
for (const property in properties) {
|
|
767
|
+
let isRequired = false;
|
|
768
|
+
if (schema.required && ((_a = schema.required) === null || _a === void 0 ? void 0 : _a.indexOf(property)) >= 0) {
|
|
769
|
+
isRequired = true;
|
|
844
770
|
}
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
771
|
+
const result = this.checkPostBodySchema(properties[property], isRequired);
|
|
772
|
+
paramResult.requiredNum += result.requiredNum;
|
|
773
|
+
paramResult.optionalNum += result.optionalNum;
|
|
774
|
+
paramResult.isValid = paramResult.isValid && result.isValid;
|
|
775
|
+
paramResult.reason.push(...result.reason);
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
else {
|
|
779
|
+
if (isRequiredWithoutDefault && !isCopilot) {
|
|
780
|
+
paramResult.isValid = false;
|
|
781
|
+
paramResult.reason.push(exports.ErrorType.PostBodyContainsRequiredUnsupportedSchema);
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
return paramResult;
|
|
785
|
+
}
|
|
786
|
+
checkParamSchema(paramObject) {
|
|
787
|
+
const paramResult = {
|
|
788
|
+
requiredNum: 0,
|
|
789
|
+
optionalNum: 0,
|
|
790
|
+
isValid: true,
|
|
791
|
+
reason: [],
|
|
792
|
+
};
|
|
793
|
+
if (!paramObject) {
|
|
794
|
+
return paramResult;
|
|
795
|
+
}
|
|
796
|
+
const isCopilot = this.projectType === exports.ProjectType.Copilot;
|
|
797
|
+
for (let i = 0; i < paramObject.length; i++) {
|
|
798
|
+
const param = paramObject[i];
|
|
799
|
+
const schema = param.schema;
|
|
800
|
+
if (isCopilot && this.hasNestedObjectInSchema(schema)) {
|
|
801
|
+
paramResult.isValid = false;
|
|
802
|
+
paramResult.reason.push(exports.ErrorType.ParamsContainsNestedObject);
|
|
803
|
+
continue;
|
|
804
|
+
}
|
|
805
|
+
const isRequiredWithoutDefault = param.required && schema.default === undefined;
|
|
806
|
+
if (isCopilot) {
|
|
807
|
+
if (isRequiredWithoutDefault) {
|
|
808
|
+
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
850
809
|
}
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
810
|
+
else {
|
|
811
|
+
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
812
|
+
}
|
|
813
|
+
continue;
|
|
814
|
+
}
|
|
815
|
+
if (param.in === "header" || param.in === "cookie") {
|
|
816
|
+
if (isRequiredWithoutDefault) {
|
|
817
|
+
paramResult.isValid = false;
|
|
818
|
+
paramResult.reason.push(exports.ErrorType.ParamsContainRequiredUnsupportedSchema);
|
|
819
|
+
}
|
|
820
|
+
continue;
|
|
821
|
+
}
|
|
822
|
+
if (schema.type !== "boolean" &&
|
|
823
|
+
schema.type !== "string" &&
|
|
824
|
+
schema.type !== "number" &&
|
|
825
|
+
schema.type !== "integer") {
|
|
826
|
+
if (isRequiredWithoutDefault) {
|
|
827
|
+
paramResult.isValid = false;
|
|
828
|
+
paramResult.reason.push(exports.ErrorType.ParamsContainRequiredUnsupportedSchema);
|
|
829
|
+
}
|
|
830
|
+
continue;
|
|
831
|
+
}
|
|
832
|
+
if (param.in === "query" || param.in === "path") {
|
|
833
|
+
if (isRequiredWithoutDefault) {
|
|
834
|
+
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
835
|
+
}
|
|
836
|
+
else {
|
|
837
|
+
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
855
838
|
}
|
|
856
839
|
}
|
|
857
|
-
newSpec.paths = newPaths;
|
|
858
|
-
return newSpec;
|
|
859
840
|
}
|
|
860
|
-
|
|
861
|
-
|
|
841
|
+
return paramResult;
|
|
842
|
+
}
|
|
843
|
+
hasNestedObjectInSchema(schema) {
|
|
844
|
+
if (schema.type === "object") {
|
|
845
|
+
for (const property in schema.properties) {
|
|
846
|
+
const nestedSchema = schema.properties[property];
|
|
847
|
+
if (nestedSchema.type === "object") {
|
|
848
|
+
return true;
|
|
849
|
+
}
|
|
850
|
+
}
|
|
862
851
|
}
|
|
852
|
+
return false;
|
|
863
853
|
}
|
|
864
854
|
}
|
|
865
855
|
|
|
866
856
|
// Copyright (c) Microsoft Corporation.
|
|
867
|
-
class
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
{
|
|
874
|
-
pluginFile: apiPluginRelativePath,
|
|
875
|
-
},
|
|
876
|
-
];
|
|
877
|
-
ManifestUpdater.updateManifestDescription(manifest, spec);
|
|
878
|
-
const specRelativePath = ManifestUpdater.getRelativePath(manifestPath, outputSpecPath);
|
|
879
|
-
const apiPlugin = ManifestUpdater.generatePluginManifestSchema(spec, specRelativePath, options);
|
|
880
|
-
return [manifest, apiPlugin];
|
|
881
|
-
});
|
|
857
|
+
class CopilotValidator extends Validator {
|
|
858
|
+
constructor(spec, options) {
|
|
859
|
+
super();
|
|
860
|
+
this.projectType = exports.ProjectType.Copilot;
|
|
861
|
+
this.options = options;
|
|
862
|
+
this.spec = spec;
|
|
882
863
|
}
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
864
|
+
validateSpec() {
|
|
865
|
+
const result = { errors: [], warnings: [] };
|
|
866
|
+
// validate spec version
|
|
867
|
+
let validationResult = this.validateSpecVersion();
|
|
868
|
+
result.errors.push(...validationResult.errors);
|
|
869
|
+
// validate spec server
|
|
870
|
+
validationResult = this.validateSpecServer();
|
|
871
|
+
result.errors.push(...validationResult.errors);
|
|
872
|
+
// validate no supported API
|
|
873
|
+
validationResult = this.validateSpecNoSupportAPI();
|
|
874
|
+
result.errors.push(...validationResult.errors);
|
|
875
|
+
// validate operationId missing
|
|
876
|
+
validationResult = this.validateSpecOperationId();
|
|
877
|
+
result.warnings.push(...validationResult.warnings);
|
|
878
|
+
return result;
|
|
889
879
|
}
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
880
|
+
validateAPI(method, path) {
|
|
881
|
+
const result = { isValid: true, reason: [] };
|
|
882
|
+
method = method.toLocaleLowerCase();
|
|
883
|
+
// validate method and path
|
|
884
|
+
const methodAndPathResult = this.validateMethodAndPath(method, path);
|
|
885
|
+
if (!methodAndPathResult.isValid) {
|
|
886
|
+
return methodAndPathResult;
|
|
887
|
+
}
|
|
888
|
+
const operationObject = this.spec.paths[path][method];
|
|
889
|
+
// validate auth
|
|
890
|
+
const authCheckResult = this.validateAuth(method, path);
|
|
891
|
+
result.reason.push(...authCheckResult.reason);
|
|
892
|
+
// validate operationId
|
|
893
|
+
if (!this.options.allowMissingId && !operationObject.operationId) {
|
|
894
|
+
result.reason.push(exports.ErrorType.MissingOperationId);
|
|
895
|
+
}
|
|
896
|
+
// validate server
|
|
897
|
+
const validateServerResult = this.validateServer(method, path);
|
|
898
|
+
result.reason.push(...validateServerResult.reason);
|
|
899
|
+
// validate response
|
|
900
|
+
const validateResponseResult = this.validateResponse(method, path);
|
|
901
|
+
result.reason.push(...validateResponseResult.reason);
|
|
902
|
+
// validate requestBody
|
|
903
|
+
const requestBody = operationObject.requestBody;
|
|
904
|
+
const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
|
|
905
|
+
if (requestJsonBody) {
|
|
906
|
+
const requestBodySchema = requestJsonBody.schema;
|
|
907
|
+
if (requestBodySchema.type !== "object") {
|
|
908
|
+
result.reason.push(exports.ErrorType.PostBodySchemaIsNotJson);
|
|
909
|
+
}
|
|
910
|
+
const requestBodyParamResult = this.checkPostBodySchema(requestBodySchema, requestBody.required);
|
|
911
|
+
result.reason.push(...requestBodyParamResult.reason);
|
|
898
912
|
}
|
|
899
|
-
|
|
900
|
-
|
|
913
|
+
// validate parameters
|
|
914
|
+
const paramObject = operationObject.parameters;
|
|
915
|
+
const paramResult = this.checkParamSchema(paramObject);
|
|
916
|
+
result.reason.push(...paramResult.reason);
|
|
917
|
+
if (result.reason.length > 0) {
|
|
918
|
+
result.isValid = false;
|
|
901
919
|
}
|
|
902
|
-
return
|
|
920
|
+
return result;
|
|
903
921
|
}
|
|
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
|
-
const schema = param.schema;
|
|
930
|
-
parameters.properties[param.name] = ManifestUpdater.mapOpenAPISchemaToFuncParam(schema, method, pathUrl);
|
|
931
|
-
if (param.required) {
|
|
932
|
-
parameters.required.push(param.name);
|
|
933
|
-
}
|
|
934
|
-
if (!parameters.properties[param.name].description) {
|
|
935
|
-
parameters.properties[param.name].description = (_b = param.description) !== null && _b !== void 0 ? _b : "";
|
|
936
|
-
}
|
|
937
|
-
}
|
|
938
|
-
}
|
|
939
|
-
if (requestBody) {
|
|
940
|
-
const requestJsonBody = requestBody.content["application/json"];
|
|
941
|
-
const requestBodySchema = requestJsonBody.schema;
|
|
942
|
-
if (requestBodySchema.type === "object") {
|
|
943
|
-
if (requestBodySchema.required) {
|
|
944
|
-
parameters.required.push(...requestBodySchema.required);
|
|
945
|
-
}
|
|
946
|
-
for (const property in requestBodySchema.properties) {
|
|
947
|
-
const schema = requestBodySchema.properties[property];
|
|
948
|
-
parameters.properties[property] = ManifestUpdater.mapOpenAPISchemaToFuncParam(schema, method, pathUrl);
|
|
949
|
-
}
|
|
950
|
-
}
|
|
951
|
-
else {
|
|
952
|
-
throw new SpecParserError(Utils.format(ConstantString.UnsupportedSchema, method, pathUrl, JSON.stringify(requestBodySchema)), exports.ErrorType.UpdateManifestFailed);
|
|
953
|
-
}
|
|
954
|
-
}
|
|
955
|
-
const funcObj = {
|
|
956
|
-
name: operationId,
|
|
957
|
-
description: description,
|
|
958
|
-
parameters: parameters,
|
|
959
|
-
};
|
|
960
|
-
functions.push(funcObj);
|
|
961
|
-
functionNames.push(operationId);
|
|
962
|
-
}
|
|
963
|
-
}
|
|
964
|
-
}
|
|
965
|
-
}
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
// Copyright (c) Microsoft Corporation.
|
|
925
|
+
class SMEValidator extends Validator {
|
|
926
|
+
constructor(spec, options) {
|
|
927
|
+
super();
|
|
928
|
+
this.projectType = exports.ProjectType.SME;
|
|
929
|
+
this.options = options;
|
|
930
|
+
this.spec = spec;
|
|
931
|
+
}
|
|
932
|
+
validateSpec() {
|
|
933
|
+
const result = { errors: [], warnings: [] };
|
|
934
|
+
// validate spec version
|
|
935
|
+
let validationResult = this.validateSpecVersion();
|
|
936
|
+
result.errors.push(...validationResult.errors);
|
|
937
|
+
// validate spec server
|
|
938
|
+
validationResult = this.validateSpecServer();
|
|
939
|
+
result.errors.push(...validationResult.errors);
|
|
940
|
+
// validate no supported API
|
|
941
|
+
validationResult = this.validateSpecNoSupportAPI();
|
|
942
|
+
result.errors.push(...validationResult.errors);
|
|
943
|
+
// validate operationId missing
|
|
944
|
+
if (this.options.allowMissingId) {
|
|
945
|
+
validationResult = this.validateSpecOperationId();
|
|
946
|
+
result.warnings.push(...validationResult.warnings);
|
|
966
947
|
}
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
948
|
+
return result;
|
|
949
|
+
}
|
|
950
|
+
validateAPI(method, path) {
|
|
951
|
+
const result = { isValid: true, reason: [] };
|
|
952
|
+
method = method.toLocaleLowerCase();
|
|
953
|
+
// validate method and path
|
|
954
|
+
const methodAndPathResult = this.validateMethodAndPath(method, path);
|
|
955
|
+
if (!methodAndPathResult.isValid) {
|
|
956
|
+
return methodAndPathResult;
|
|
957
|
+
}
|
|
958
|
+
const operationObject = this.spec.paths[path][method];
|
|
959
|
+
// validate auth
|
|
960
|
+
const authCheckResult = this.validateAuth(method, path);
|
|
961
|
+
result.reason.push(...authCheckResult.reason);
|
|
962
|
+
// validate operationId
|
|
963
|
+
if (!this.options.allowMissingId && !operationObject.operationId) {
|
|
964
|
+
result.reason.push(exports.ErrorType.MissingOperationId);
|
|
965
|
+
}
|
|
966
|
+
// validate server
|
|
967
|
+
const validateServerResult = this.validateServer(method, path);
|
|
968
|
+
result.reason.push(...validateServerResult.reason);
|
|
969
|
+
// validate response
|
|
970
|
+
const validateResponseResult = this.validateResponse(method, path);
|
|
971
|
+
result.reason.push(...validateResponseResult.reason);
|
|
972
|
+
let postBodyResult = {
|
|
973
|
+
requiredNum: 0,
|
|
974
|
+
optionalNum: 0,
|
|
975
|
+
isValid: true,
|
|
976
|
+
reason: [],
|
|
984
977
|
};
|
|
985
|
-
|
|
978
|
+
// validate requestBody
|
|
979
|
+
const requestBody = operationObject.requestBody;
|
|
980
|
+
const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
|
|
981
|
+
if (Utils.containMultipleMediaTypes(requestBody)) {
|
|
982
|
+
result.reason.push(exports.ErrorType.PostBodyContainMultipleMediaTypes);
|
|
983
|
+
}
|
|
984
|
+
if (requestJsonBody) {
|
|
985
|
+
const requestBodySchema = requestJsonBody.schema;
|
|
986
|
+
postBodyResult = this.checkPostBodySchema(requestBodySchema, requestBody.required);
|
|
987
|
+
result.reason.push(...postBodyResult.reason);
|
|
988
|
+
}
|
|
989
|
+
// validate parameters
|
|
990
|
+
const paramObject = operationObject.parameters;
|
|
991
|
+
const paramResult = this.checkParamSchema(paramObject);
|
|
992
|
+
result.reason.push(...paramResult.reason);
|
|
993
|
+
// validate total parameters count
|
|
994
|
+
if (paramResult.isValid && postBodyResult.isValid) {
|
|
995
|
+
const paramCountResult = this.validateParamCount(postBodyResult, paramResult);
|
|
996
|
+
result.reason.push(...paramCountResult.reason);
|
|
997
|
+
}
|
|
998
|
+
if (result.reason.length > 0) {
|
|
999
|
+
result.isValid = false;
|
|
1000
|
+
}
|
|
1001
|
+
return result;
|
|
986
1002
|
}
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
const updateResult = yield ManifestUpdater.generateCommands(spec, manifestPath, options, adaptiveCardFolder);
|
|
996
|
-
const commands = updateResult[0];
|
|
997
|
-
warnings = updateResult[1];
|
|
998
|
-
const composeExtension = {
|
|
999
|
-
composeExtensionType: "apiBased",
|
|
1000
|
-
apiSpecificationFile: ManifestUpdater.getRelativePath(manifestPath, outputSpecPath),
|
|
1001
|
-
commands: commands,
|
|
1002
|
-
};
|
|
1003
|
-
if (authInfo) {
|
|
1004
|
-
const auth = authInfo.authScheme;
|
|
1005
|
-
if (Utils.isAPIKeyAuth(auth) || Utils.isBearerTokenAuth(auth)) {
|
|
1006
|
-
const safeApiSecretRegistrationId = Utils.getSafeRegistrationIdEnvName(`${authInfo.name}_${ConstantString.RegistrationIdPostfix}`);
|
|
1007
|
-
composeExtension.authorization = {
|
|
1008
|
-
authType: "apiSecretServiceAuth",
|
|
1009
|
-
apiSecretServiceAuthConfiguration: {
|
|
1010
|
-
apiSecretRegistrationId: `\${{${safeApiSecretRegistrationId}}}`,
|
|
1011
|
-
},
|
|
1012
|
-
};
|
|
1013
|
-
}
|
|
1014
|
-
else if (Utils.isOAuthWithAuthCodeFlow(auth)) {
|
|
1015
|
-
const safeOAuth2RegistrationId = Utils.getSafeRegistrationIdEnvName(`${authInfo.name}_${ConstantString.OAuthRegistrationIdPostFix}`);
|
|
1016
|
-
composeExtension.authorization = {
|
|
1017
|
-
authType: "oAuth2.0",
|
|
1018
|
-
oAuthConfiguration: {
|
|
1019
|
-
oauthConfigurationId: `\${{${safeOAuth2RegistrationId}}}`,
|
|
1020
|
-
},
|
|
1021
|
-
};
|
|
1022
|
-
updatedPart.webApplicationInfo = {
|
|
1023
|
-
id: "${{AAD_APP_CLIENT_ID}}",
|
|
1024
|
-
resource: "api://${{DOMAIN}}/${{AAD_APP_CLIENT_ID}}",
|
|
1025
|
-
};
|
|
1026
|
-
}
|
|
1027
|
-
}
|
|
1028
|
-
updatedPart.composeExtensions = [composeExtension];
|
|
1029
|
-
}
|
|
1030
|
-
updatedPart.description = originalManifest.description;
|
|
1031
|
-
ManifestUpdater.updateManifestDescription(updatedPart, spec);
|
|
1032
|
-
const updatedManifest = Object.assign(Object.assign({}, originalManifest), updatedPart);
|
|
1033
|
-
return [updatedManifest, warnings];
|
|
1034
|
-
}
|
|
1035
|
-
catch (err) {
|
|
1036
|
-
throw new SpecParserError(err.toString(), exports.ErrorType.UpdateManifestFailed);
|
|
1003
|
+
validateParamCount(postBodyResult, paramResult) {
|
|
1004
|
+
const result = { isValid: true, reason: [] };
|
|
1005
|
+
const totalRequiredParams = postBodyResult.requiredNum + paramResult.requiredNum;
|
|
1006
|
+
const totalParams = totalRequiredParams + postBodyResult.optionalNum + paramResult.optionalNum;
|
|
1007
|
+
if (totalRequiredParams > 1) {
|
|
1008
|
+
if (!this.options.allowMultipleParameters ||
|
|
1009
|
+
totalRequiredParams > SMEValidator.SMERequiredParamsMaxNum) {
|
|
1010
|
+
result.reason.push(exports.ErrorType.ExceededRequiredParamsLimit);
|
|
1037
1011
|
}
|
|
1038
|
-
}
|
|
1012
|
+
}
|
|
1013
|
+
else if (totalParams === 0) {
|
|
1014
|
+
result.reason.push(exports.ErrorType.NoParameter);
|
|
1015
|
+
}
|
|
1016
|
+
return result;
|
|
1039
1017
|
}
|
|
1040
|
-
|
|
1018
|
+
}
|
|
1019
|
+
SMEValidator.SMERequiredParamsMaxNum = 5;
|
|
1020
|
+
|
|
1021
|
+
// Copyright (c) Microsoft Corporation.
|
|
1022
|
+
class TeamsAIValidator extends Validator {
|
|
1023
|
+
constructor(spec, options) {
|
|
1024
|
+
super();
|
|
1025
|
+
this.projectType = exports.ProjectType.TeamsAi;
|
|
1026
|
+
this.options = options;
|
|
1027
|
+
this.spec = spec;
|
|
1028
|
+
}
|
|
1029
|
+
validateSpec() {
|
|
1030
|
+
const result = { errors: [], warnings: [] };
|
|
1031
|
+
// validate spec server
|
|
1032
|
+
let validationResult = this.validateSpecServer();
|
|
1033
|
+
result.errors.push(...validationResult.errors);
|
|
1034
|
+
// validate no supported API
|
|
1035
|
+
validationResult = this.validateSpecNoSupportAPI();
|
|
1036
|
+
result.errors.push(...validationResult.errors);
|
|
1037
|
+
return result;
|
|
1038
|
+
}
|
|
1039
|
+
validateAPI(method, path) {
|
|
1040
|
+
const result = { isValid: true, reason: [] };
|
|
1041
|
+
method = method.toLocaleLowerCase();
|
|
1042
|
+
// validate method and path
|
|
1043
|
+
const methodAndPathResult = this.validateMethodAndPath(method, path);
|
|
1044
|
+
if (!methodAndPathResult.isValid) {
|
|
1045
|
+
return methodAndPathResult;
|
|
1046
|
+
}
|
|
1047
|
+
const operationObject = this.spec.paths[path][method];
|
|
1048
|
+
// validate operationId
|
|
1049
|
+
if (!this.options.allowMissingId && !operationObject.operationId) {
|
|
1050
|
+
result.reason.push(exports.ErrorType.MissingOperationId);
|
|
1051
|
+
}
|
|
1052
|
+
// validate server
|
|
1053
|
+
const validateServerResult = this.validateServer(method, path);
|
|
1054
|
+
result.reason.push(...validateServerResult.reason);
|
|
1055
|
+
if (result.reason.length > 0) {
|
|
1056
|
+
result.isValid = false;
|
|
1057
|
+
}
|
|
1058
|
+
return result;
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
class ValidatorFactory {
|
|
1063
|
+
static create(spec, options) {
|
|
1041
1064
|
var _a;
|
|
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
|
-
|
|
1065
|
+
const type = (_a = options.projectType) !== null && _a !== void 0 ? _a : exports.ProjectType.SME;
|
|
1066
|
+
switch (type) {
|
|
1067
|
+
case exports.ProjectType.SME:
|
|
1068
|
+
return new SMEValidator(spec, options);
|
|
1069
|
+
case exports.ProjectType.Copilot:
|
|
1070
|
+
return new CopilotValidator(spec, options);
|
|
1071
|
+
case exports.ProjectType.TeamsAi:
|
|
1072
|
+
return new TeamsAIValidator(spec, options);
|
|
1073
|
+
default:
|
|
1074
|
+
throw new Error(`Invalid project type: ${type}`);
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
// Copyright (c) Microsoft Corporation.
|
|
1080
|
+
class SpecFilter {
|
|
1081
|
+
static specFilter(filter, unResolveSpec, resolvedSpec, options) {
|
|
1082
|
+
var _a;
|
|
1083
|
+
try {
|
|
1084
|
+
const newSpec = Object.assign({}, unResolveSpec);
|
|
1085
|
+
const newPaths = {};
|
|
1086
|
+
for (const filterItem of filter) {
|
|
1087
|
+
const [method, path] = filterItem.split(" ");
|
|
1088
|
+
const methodName = method.toLowerCase();
|
|
1089
|
+
const pathObj = (_a = resolvedSpec.paths) === null || _a === void 0 ? void 0 : _a[path];
|
|
1090
|
+
if (ConstantString.AllOperationMethods.includes(methodName) &&
|
|
1091
|
+
pathObj &&
|
|
1092
|
+
pathObj[methodName]) {
|
|
1093
|
+
const validator = ValidatorFactory.create(resolvedSpec, options);
|
|
1094
|
+
const validateResult = validator.validateAPI(methodName, path);
|
|
1095
|
+
if (!validateResult.isValid) {
|
|
1096
|
+
continue;
|
|
1097
|
+
}
|
|
1098
|
+
if (!newPaths[path]) {
|
|
1099
|
+
newPaths[path] = Object.assign({}, unResolveSpec.paths[path]);
|
|
1100
|
+
for (const m of ConstantString.AllOperationMethods) {
|
|
1101
|
+
delete newPaths[path][m];
|
|
1069
1102
|
}
|
|
1070
1103
|
}
|
|
1104
|
+
newPaths[path][methodName] = unResolveSpec.paths[path][methodName];
|
|
1105
|
+
// Add the operationId if missing
|
|
1106
|
+
if (!newPaths[path][methodName].operationId) {
|
|
1107
|
+
newPaths[path][methodName].operationId = `${methodName}${Utils.convertPathToCamelCase(path)}`;
|
|
1108
|
+
}
|
|
1071
1109
|
}
|
|
1072
1110
|
}
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1111
|
+
newSpec.paths = newPaths;
|
|
1112
|
+
return newSpec;
|
|
1113
|
+
}
|
|
1114
|
+
catch (err) {
|
|
1115
|
+
throw new SpecParserError(err.toString(), exports.ErrorType.FilterSpecFailed);
|
|
1116
|
+
}
|
|
1079
1117
|
}
|
|
1080
1118
|
}
|
|
1081
1119
|
|
|
@@ -1083,7 +1121,7 @@ class ManifestUpdater {
|
|
|
1083
1121
|
class AdaptiveCardGenerator {
|
|
1084
1122
|
static generateAdaptiveCard(operationItem) {
|
|
1085
1123
|
try {
|
|
1086
|
-
const json = Utils.getResponseJson(operationItem);
|
|
1124
|
+
const { json } = Utils.getResponseJson(operationItem);
|
|
1087
1125
|
let cardBody = [];
|
|
1088
1126
|
let schema = json.schema;
|
|
1089
1127
|
let jsonPath = "$";
|
|
@@ -1249,6 +1287,27 @@ function wrapAdaptiveCard(card, jsonPath) {
|
|
|
1249
1287
|
};
|
|
1250
1288
|
return result;
|
|
1251
1289
|
}
|
|
1290
|
+
function wrapResponseSemantics(card, jsonPath) {
|
|
1291
|
+
const props = inferProperties(card);
|
|
1292
|
+
const dataPath = jsonPath === "$" ? "$" : "$." + jsonPath;
|
|
1293
|
+
const result = {
|
|
1294
|
+
data_path: dataPath,
|
|
1295
|
+
};
|
|
1296
|
+
if (props.title || props.subtitle || props.imageUrl) {
|
|
1297
|
+
result.properties = {};
|
|
1298
|
+
if (props.title) {
|
|
1299
|
+
result.properties.title = "$." + props.title;
|
|
1300
|
+
}
|
|
1301
|
+
if (props.subtitle) {
|
|
1302
|
+
result.properties.subtitle = "$." + props.subtitle;
|
|
1303
|
+
}
|
|
1304
|
+
if (props.imageUrl) {
|
|
1305
|
+
result.properties.url = "$." + props.imageUrl;
|
|
1306
|
+
}
|
|
1307
|
+
}
|
|
1308
|
+
result.static_template = card;
|
|
1309
|
+
return result;
|
|
1310
|
+
}
|
|
1252
1311
|
/**
|
|
1253
1312
|
* Infers the preview card template from an Adaptive Card and a JSON path.
|
|
1254
1313
|
* The preview card template includes a title and an optional subtitle and image.
|
|
@@ -1261,11 +1320,29 @@ function wrapAdaptiveCard(card, jsonPath) {
|
|
|
1261
1320
|
* @returns The inferred preview card template.
|
|
1262
1321
|
*/
|
|
1263
1322
|
function inferPreviewCardTemplate(card) {
|
|
1264
|
-
var _a;
|
|
1265
1323
|
const result = {
|
|
1266
|
-
title: "",
|
|
1324
|
+
title: "result",
|
|
1267
1325
|
};
|
|
1268
|
-
const
|
|
1326
|
+
const inferredProperties = inferProperties(card);
|
|
1327
|
+
if (inferredProperties.title) {
|
|
1328
|
+
result.title = `\${if(${inferredProperties.title}, ${inferredProperties.title}, 'N/A')}`;
|
|
1329
|
+
}
|
|
1330
|
+
if (inferredProperties.subtitle) {
|
|
1331
|
+
result.subtitle = `\${if(${inferredProperties.subtitle}, ${inferredProperties.subtitle}, 'N/A')}`;
|
|
1332
|
+
}
|
|
1333
|
+
if (inferredProperties.imageUrl) {
|
|
1334
|
+
result.image = {
|
|
1335
|
+
url: `\${${inferredProperties.imageUrl}}`,
|
|
1336
|
+
alt: `\${if(${inferredProperties.imageUrl}, ${inferredProperties.imageUrl}, 'N/A')}`,
|
|
1337
|
+
$when: `\${${inferredProperties.imageUrl} != null}`,
|
|
1338
|
+
};
|
|
1339
|
+
}
|
|
1340
|
+
return result;
|
|
1341
|
+
}
|
|
1342
|
+
function inferProperties(card) {
|
|
1343
|
+
var _a;
|
|
1344
|
+
const result = {};
|
|
1345
|
+
const nameSet = new Set();
|
|
1269
1346
|
let rootObject;
|
|
1270
1347
|
if (((_a = card.body[0]) === null || _a === void 0 ? void 0 : _a.type) === ConstantString.ContainerType) {
|
|
1271
1348
|
rootObject = card.body[0].items;
|
|
@@ -1278,56 +1355,380 @@ function inferPreviewCardTemplate(card) {
|
|
|
1278
1355
|
const textElement = element;
|
|
1279
1356
|
const index = textElement.text.indexOf("${if(");
|
|
1280
1357
|
if (index > 0) {
|
|
1281
|
-
|
|
1282
|
-
|
|
1358
|
+
const text = textElement.text.substring(index);
|
|
1359
|
+
const match = text.match(/\${if\(([^,]+),/);
|
|
1360
|
+
const property = match ? match[1] : "";
|
|
1361
|
+
if (property) {
|
|
1362
|
+
nameSet.add(property);
|
|
1363
|
+
}
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
else if (element.type === ConstantString.ImageType) {
|
|
1367
|
+
const imageElement = element;
|
|
1368
|
+
const match = imageElement.url.match(/\${([^,]+)}/);
|
|
1369
|
+
const property = match ? match[1] : "";
|
|
1370
|
+
if (property) {
|
|
1371
|
+
nameSet.add(property);
|
|
1283
1372
|
}
|
|
1284
1373
|
}
|
|
1285
1374
|
}
|
|
1286
|
-
for (const
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
textBlockElements.delete(element);
|
|
1375
|
+
for (const name of nameSet) {
|
|
1376
|
+
if (!result.title && Utils.isWellKnownName(name, ConstantString.WellknownTitleName)) {
|
|
1377
|
+
result.title = name;
|
|
1378
|
+
nameSet.delete(name);
|
|
1291
1379
|
}
|
|
1292
1380
|
else if (!result.subtitle &&
|
|
1293
|
-
Utils.isWellKnownName(
|
|
1294
|
-
result.subtitle =
|
|
1295
|
-
|
|
1381
|
+
Utils.isWellKnownName(name, ConstantString.WellknownSubtitleName)) {
|
|
1382
|
+
result.subtitle = name;
|
|
1383
|
+
nameSet.delete(name);
|
|
1296
1384
|
}
|
|
1297
|
-
else if (!result.
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
if (property) {
|
|
1301
|
-
result.image = {
|
|
1302
|
-
url: `\${${property}}`,
|
|
1303
|
-
alt: text,
|
|
1304
|
-
$when: `\${${property} != null}`,
|
|
1305
|
-
};
|
|
1306
|
-
}
|
|
1307
|
-
textBlockElements.delete(element);
|
|
1385
|
+
else if (!result.imageUrl && Utils.isWellKnownName(name, ConstantString.WellknownImageName)) {
|
|
1386
|
+
result.imageUrl = name;
|
|
1387
|
+
nameSet.delete(name);
|
|
1308
1388
|
}
|
|
1309
1389
|
}
|
|
1310
|
-
for (const
|
|
1311
|
-
const text = element.text;
|
|
1390
|
+
for (const name of nameSet) {
|
|
1312
1391
|
if (!result.title) {
|
|
1313
|
-
result.title =
|
|
1314
|
-
|
|
1392
|
+
result.title = name;
|
|
1393
|
+
nameSet.delete(name);
|
|
1315
1394
|
}
|
|
1316
1395
|
else if (!result.subtitle) {
|
|
1317
|
-
result.subtitle =
|
|
1318
|
-
|
|
1396
|
+
result.subtitle = name;
|
|
1397
|
+
nameSet.delete(name);
|
|
1319
1398
|
}
|
|
1320
1399
|
}
|
|
1321
1400
|
if (!result.title && result.subtitle) {
|
|
1322
1401
|
result.title = result.subtitle;
|
|
1323
1402
|
delete result.subtitle;
|
|
1324
1403
|
}
|
|
1325
|
-
if (!result.title) {
|
|
1326
|
-
result.title = "result";
|
|
1327
|
-
}
|
|
1328
1404
|
return result;
|
|
1329
1405
|
}
|
|
1330
1406
|
|
|
1407
|
+
// Copyright (c) Microsoft Corporation.
|
|
1408
|
+
class ManifestUpdater {
|
|
1409
|
+
static updateManifestWithAiPlugin(manifestPath, outputSpecPath, apiPluginFilePath, spec, options, authInfo) {
|
|
1410
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1411
|
+
const manifest = yield fs__default['default'].readJSON(manifestPath);
|
|
1412
|
+
const apiPluginRelativePath = ManifestUpdater.getRelativePath(manifestPath, apiPluginFilePath);
|
|
1413
|
+
// Insert plugins in manifest.json if it is plugin for Copilot.
|
|
1414
|
+
if (!options.isGptPlugin) {
|
|
1415
|
+
manifest.plugins = [
|
|
1416
|
+
{
|
|
1417
|
+
file: apiPluginRelativePath,
|
|
1418
|
+
id: ConstantString.DefaultPluginId,
|
|
1419
|
+
},
|
|
1420
|
+
];
|
|
1421
|
+
ManifestUpdater.updateManifestDescription(manifest, spec);
|
|
1422
|
+
}
|
|
1423
|
+
const appName = this.removeEnvs(manifest.name.short);
|
|
1424
|
+
const specRelativePath = ManifestUpdater.getRelativePath(manifestPath, outputSpecPath);
|
|
1425
|
+
const apiPlugin = yield ManifestUpdater.generatePluginManifestSchema(spec, specRelativePath, apiPluginFilePath, appName, authInfo, options);
|
|
1426
|
+
return [manifest, apiPlugin];
|
|
1427
|
+
});
|
|
1428
|
+
}
|
|
1429
|
+
static updateManifestDescription(manifest, spec) {
|
|
1430
|
+
var _a, _b;
|
|
1431
|
+
manifest.description = {
|
|
1432
|
+
short: spec.info.title.slice(0, ConstantString.ShortDescriptionMaxLens),
|
|
1433
|
+
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),
|
|
1434
|
+
};
|
|
1435
|
+
}
|
|
1436
|
+
static checkSchema(schema, method, pathUrl) {
|
|
1437
|
+
if (schema.type === "array") {
|
|
1438
|
+
const items = schema.items;
|
|
1439
|
+
ManifestUpdater.checkSchema(items, method, pathUrl);
|
|
1440
|
+
}
|
|
1441
|
+
else if (schema.type !== "string" &&
|
|
1442
|
+
schema.type !== "boolean" &&
|
|
1443
|
+
schema.type !== "integer" &&
|
|
1444
|
+
schema.type !== "number") {
|
|
1445
|
+
throw new SpecParserError(Utils.format(ConstantString.UnsupportedSchema, method, pathUrl, JSON.stringify(schema)), exports.ErrorType.UpdateManifestFailed);
|
|
1446
|
+
}
|
|
1447
|
+
}
|
|
1448
|
+
static generatePluginManifestSchema(spec, specRelativePath, apiPluginFilePath, appName, authInfo, options) {
|
|
1449
|
+
var _a, _b, _c, _d;
|
|
1450
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1451
|
+
const functions = [];
|
|
1452
|
+
const functionNames = [];
|
|
1453
|
+
const conversationStarters = [];
|
|
1454
|
+
const paths = spec.paths;
|
|
1455
|
+
const pluginAuthObj = {
|
|
1456
|
+
type: "None",
|
|
1457
|
+
};
|
|
1458
|
+
if (authInfo) {
|
|
1459
|
+
if (Utils.isOAuthWithAuthCodeFlow(authInfo.authScheme)) {
|
|
1460
|
+
pluginAuthObj.type = "OAuthPluginVault";
|
|
1461
|
+
}
|
|
1462
|
+
else if (Utils.isBearerTokenAuth(authInfo.authScheme)) {
|
|
1463
|
+
pluginAuthObj.type = "ApiKeyPluginVault";
|
|
1464
|
+
}
|
|
1465
|
+
if (pluginAuthObj.type !== "None") {
|
|
1466
|
+
const safeRegistrationIdName = Utils.getSafeRegistrationIdEnvName(`${authInfo.name}_${ConstantString.RegistrationIdPostfix[authInfo.authScheme.type]}`);
|
|
1467
|
+
pluginAuthObj.reference_id = `\${{${safeRegistrationIdName}}}`;
|
|
1468
|
+
}
|
|
1469
|
+
}
|
|
1470
|
+
for (const pathUrl in paths) {
|
|
1471
|
+
const pathItem = paths[pathUrl];
|
|
1472
|
+
if (pathItem) {
|
|
1473
|
+
const operations = pathItem;
|
|
1474
|
+
for (const method in operations) {
|
|
1475
|
+
if (options.allowMethods.includes(method)) {
|
|
1476
|
+
const operationItem = operations[method];
|
|
1477
|
+
const confirmationBodies = [];
|
|
1478
|
+
if (operationItem) {
|
|
1479
|
+
const operationId = operationItem.operationId;
|
|
1480
|
+
const description = (_a = operationItem.description) !== null && _a !== void 0 ? _a : "";
|
|
1481
|
+
const summary = operationItem.summary;
|
|
1482
|
+
const paramObject = operationItem.parameters;
|
|
1483
|
+
const requestBody = operationItem.requestBody;
|
|
1484
|
+
if (paramObject) {
|
|
1485
|
+
for (let i = 0; i < paramObject.length; i++) {
|
|
1486
|
+
const param = paramObject[i];
|
|
1487
|
+
const schema = param.schema;
|
|
1488
|
+
ManifestUpdater.checkSchema(schema, method, pathUrl);
|
|
1489
|
+
confirmationBodies.push(ManifestUpdater.getConfirmationBodyItem(param.name));
|
|
1490
|
+
}
|
|
1491
|
+
}
|
|
1492
|
+
if (requestBody) {
|
|
1493
|
+
const requestJsonBody = requestBody.content["application/json"];
|
|
1494
|
+
const requestBodySchema = requestJsonBody.schema;
|
|
1495
|
+
if (requestBodySchema.type === "object") {
|
|
1496
|
+
for (const property in requestBodySchema.properties) {
|
|
1497
|
+
const schema = requestBodySchema.properties[property];
|
|
1498
|
+
ManifestUpdater.checkSchema(schema, method, pathUrl);
|
|
1499
|
+
confirmationBodies.push(ManifestUpdater.getConfirmationBodyItem(property));
|
|
1500
|
+
}
|
|
1501
|
+
}
|
|
1502
|
+
else {
|
|
1503
|
+
throw new SpecParserError(Utils.format(ConstantString.UnsupportedSchema, method, pathUrl, JSON.stringify(requestBodySchema)), exports.ErrorType.UpdateManifestFailed);
|
|
1504
|
+
}
|
|
1505
|
+
}
|
|
1506
|
+
const funcObj = {
|
|
1507
|
+
name: operationId,
|
|
1508
|
+
description: description,
|
|
1509
|
+
};
|
|
1510
|
+
if (options.allowResponseSemantics) {
|
|
1511
|
+
const { json } = Utils.getResponseJson(operationItem);
|
|
1512
|
+
if (json.schema) {
|
|
1513
|
+
const [card, jsonPath] = AdaptiveCardGenerator.generateAdaptiveCard(operationItem);
|
|
1514
|
+
const responseSemantic = wrapResponseSemantics(card, jsonPath);
|
|
1515
|
+
funcObj.capabilities = {
|
|
1516
|
+
response_semantics: responseSemantic,
|
|
1517
|
+
};
|
|
1518
|
+
}
|
|
1519
|
+
}
|
|
1520
|
+
if (options.allowConfirmation && method !== ConstantString.GetMethod) {
|
|
1521
|
+
if (!funcObj.capabilities) {
|
|
1522
|
+
funcObj.capabilities = {};
|
|
1523
|
+
}
|
|
1524
|
+
funcObj.capabilities.confirmation = {
|
|
1525
|
+
type: "AdaptiveCard",
|
|
1526
|
+
title: (_b = operationItem.summary) !== null && _b !== void 0 ? _b : description,
|
|
1527
|
+
};
|
|
1528
|
+
if (confirmationBodies.length > 0) {
|
|
1529
|
+
funcObj.capabilities.confirmation.body = confirmationBodies.join("\n");
|
|
1530
|
+
}
|
|
1531
|
+
}
|
|
1532
|
+
functions.push(funcObj);
|
|
1533
|
+
functionNames.push(operationId);
|
|
1534
|
+
const conversationStarterStr = (summary !== null && summary !== void 0 ? summary : description).slice(0, ConstantString.ConversationStarterMaxLens);
|
|
1535
|
+
if (conversationStarterStr) {
|
|
1536
|
+
conversationStarters.push(conversationStarterStr);
|
|
1537
|
+
}
|
|
1538
|
+
}
|
|
1539
|
+
}
|
|
1540
|
+
}
|
|
1541
|
+
}
|
|
1542
|
+
}
|
|
1543
|
+
let apiPlugin;
|
|
1544
|
+
if (yield fs__default['default'].pathExists(apiPluginFilePath)) {
|
|
1545
|
+
apiPlugin = yield fs__default['default'].readJSON(apiPluginFilePath);
|
|
1546
|
+
}
|
|
1547
|
+
else {
|
|
1548
|
+
apiPlugin = {
|
|
1549
|
+
schema_version: "v2.1",
|
|
1550
|
+
name_for_human: "",
|
|
1551
|
+
description_for_human: "",
|
|
1552
|
+
namespace: "",
|
|
1553
|
+
functions: [],
|
|
1554
|
+
runtimes: [],
|
|
1555
|
+
};
|
|
1556
|
+
}
|
|
1557
|
+
apiPlugin.functions = apiPlugin.functions || [];
|
|
1558
|
+
for (const func of functions) {
|
|
1559
|
+
const index = (_c = apiPlugin.functions) === null || _c === void 0 ? void 0 : _c.findIndex((f) => f.name === func.name);
|
|
1560
|
+
if (index === -1) {
|
|
1561
|
+
apiPlugin.functions.push(func);
|
|
1562
|
+
}
|
|
1563
|
+
else {
|
|
1564
|
+
apiPlugin.functions[index] = func;
|
|
1565
|
+
}
|
|
1566
|
+
}
|
|
1567
|
+
apiPlugin.runtimes = apiPlugin.runtimes || [];
|
|
1568
|
+
const index = apiPlugin.runtimes.findIndex((runtime) => {
|
|
1569
|
+
var _a, _b;
|
|
1570
|
+
return runtime.spec.url === specRelativePath &&
|
|
1571
|
+
runtime.type === "OpenApi" &&
|
|
1572
|
+
((_b = (_a = runtime.auth) === null || _a === void 0 ? void 0 : _a.type) !== null && _b !== void 0 ? _b : "None") === pluginAuthObj.type;
|
|
1573
|
+
});
|
|
1574
|
+
if (index === -1) {
|
|
1575
|
+
apiPlugin.runtimes.push({
|
|
1576
|
+
type: "OpenApi",
|
|
1577
|
+
auth: pluginAuthObj,
|
|
1578
|
+
spec: {
|
|
1579
|
+
url: specRelativePath,
|
|
1580
|
+
},
|
|
1581
|
+
run_for_functions: functionNames,
|
|
1582
|
+
});
|
|
1583
|
+
}
|
|
1584
|
+
else {
|
|
1585
|
+
apiPlugin.runtimes[index].run_for_functions = functionNames;
|
|
1586
|
+
}
|
|
1587
|
+
if (!apiPlugin.name_for_human) {
|
|
1588
|
+
apiPlugin.name_for_human = appName;
|
|
1589
|
+
}
|
|
1590
|
+
if (!apiPlugin.namespace) {
|
|
1591
|
+
apiPlugin.namespace = ManifestUpdater.removeAllSpecialCharacters(appName);
|
|
1592
|
+
}
|
|
1593
|
+
if (!apiPlugin.description_for_human) {
|
|
1594
|
+
apiPlugin.description_for_human =
|
|
1595
|
+
(_d = spec.info.description) !== null && _d !== void 0 ? _d : "<Please add description of the plugin>";
|
|
1596
|
+
}
|
|
1597
|
+
if (options.allowConversationStarters && conversationStarters.length > 0) {
|
|
1598
|
+
if (!apiPlugin.capabilities) {
|
|
1599
|
+
apiPlugin.capabilities = {
|
|
1600
|
+
localization: {},
|
|
1601
|
+
};
|
|
1602
|
+
}
|
|
1603
|
+
if (!apiPlugin.capabilities.conversation_starters) {
|
|
1604
|
+
apiPlugin.capabilities.conversation_starters = conversationStarters
|
|
1605
|
+
.slice(0, 5)
|
|
1606
|
+
.map((text) => ({ text }));
|
|
1607
|
+
}
|
|
1608
|
+
}
|
|
1609
|
+
return apiPlugin;
|
|
1610
|
+
});
|
|
1611
|
+
}
|
|
1612
|
+
static updateManifest(manifestPath, outputSpecPath, spec, options, adaptiveCardFolder, authInfo) {
|
|
1613
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1614
|
+
try {
|
|
1615
|
+
const originalManifest = yield fs__default['default'].readJSON(manifestPath);
|
|
1616
|
+
const updatedPart = {};
|
|
1617
|
+
updatedPart.composeExtensions = [];
|
|
1618
|
+
let warnings = [];
|
|
1619
|
+
if (options.projectType === exports.ProjectType.SME) {
|
|
1620
|
+
const updateResult = yield ManifestUpdater.generateCommands(spec, manifestPath, options, adaptiveCardFolder);
|
|
1621
|
+
const commands = updateResult[0];
|
|
1622
|
+
warnings = updateResult[1];
|
|
1623
|
+
const composeExtension = {
|
|
1624
|
+
composeExtensionType: "apiBased",
|
|
1625
|
+
apiSpecificationFile: ManifestUpdater.getRelativePath(manifestPath, outputSpecPath),
|
|
1626
|
+
commands: commands,
|
|
1627
|
+
};
|
|
1628
|
+
if (authInfo) {
|
|
1629
|
+
const auth = authInfo.authScheme;
|
|
1630
|
+
const safeRegistrationIdName = Utils.getSafeRegistrationIdEnvName(`${authInfo.name}_${ConstantString.RegistrationIdPostfix[authInfo.authScheme.type]}`);
|
|
1631
|
+
if (Utils.isAPIKeyAuth(auth) || Utils.isBearerTokenAuth(auth)) {
|
|
1632
|
+
const safeApiSecretRegistrationId = Utils.getSafeRegistrationIdEnvName(`${authInfo.name}_${ConstantString.RegistrationIdPostfix[authInfo.authScheme.type]}`);
|
|
1633
|
+
composeExtension.authorization = {
|
|
1634
|
+
authType: "apiSecretServiceAuth",
|
|
1635
|
+
apiSecretServiceAuthConfiguration: {
|
|
1636
|
+
apiSecretRegistrationId: `\${{${safeRegistrationIdName}}}`,
|
|
1637
|
+
},
|
|
1638
|
+
};
|
|
1639
|
+
}
|
|
1640
|
+
else if (Utils.isOAuthWithAuthCodeFlow(auth)) {
|
|
1641
|
+
composeExtension.authorization = {
|
|
1642
|
+
authType: "oAuth2.0",
|
|
1643
|
+
oAuthConfiguration: {
|
|
1644
|
+
oauthConfigurationId: `\${{${safeRegistrationIdName}}}`,
|
|
1645
|
+
},
|
|
1646
|
+
};
|
|
1647
|
+
updatedPart.webApplicationInfo = {
|
|
1648
|
+
id: "${{AAD_APP_CLIENT_ID}}",
|
|
1649
|
+
resource: "api://${{DOMAIN}}/${{AAD_APP_CLIENT_ID}}",
|
|
1650
|
+
};
|
|
1651
|
+
}
|
|
1652
|
+
}
|
|
1653
|
+
updatedPart.composeExtensions = [composeExtension];
|
|
1654
|
+
}
|
|
1655
|
+
updatedPart.description = originalManifest.description;
|
|
1656
|
+
ManifestUpdater.updateManifestDescription(updatedPart, spec);
|
|
1657
|
+
const updatedManifest = Object.assign(Object.assign({}, originalManifest), updatedPart);
|
|
1658
|
+
return [updatedManifest, warnings];
|
|
1659
|
+
}
|
|
1660
|
+
catch (err) {
|
|
1661
|
+
throw new SpecParserError(err.toString(), exports.ErrorType.UpdateManifestFailed);
|
|
1662
|
+
}
|
|
1663
|
+
});
|
|
1664
|
+
}
|
|
1665
|
+
static generateCommands(spec, manifestPath, options, adaptiveCardFolder) {
|
|
1666
|
+
var _a;
|
|
1667
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1668
|
+
const paths = spec.paths;
|
|
1669
|
+
const commands = [];
|
|
1670
|
+
const warnings = [];
|
|
1671
|
+
if (paths) {
|
|
1672
|
+
for (const pathUrl in paths) {
|
|
1673
|
+
const pathItem = paths[pathUrl];
|
|
1674
|
+
if (pathItem) {
|
|
1675
|
+
const operations = pathItem;
|
|
1676
|
+
// Currently only support GET and POST method
|
|
1677
|
+
for (const method in operations) {
|
|
1678
|
+
if ((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) {
|
|
1679
|
+
const operationItem = operations[method];
|
|
1680
|
+
if (operationItem) {
|
|
1681
|
+
const command = Utils.parseApiInfo(operationItem, options);
|
|
1682
|
+
if (command.parameters &&
|
|
1683
|
+
command.parameters.length >= 1 &&
|
|
1684
|
+
command.parameters.some((param) => param.isRequired)) {
|
|
1685
|
+
command.parameters = command.parameters.filter((param) => param.isRequired);
|
|
1686
|
+
}
|
|
1687
|
+
else if (command.parameters && command.parameters.length > 0) {
|
|
1688
|
+
command.parameters = [command.parameters[0]];
|
|
1689
|
+
warnings.push({
|
|
1690
|
+
type: exports.WarningType.OperationOnlyContainsOptionalParam,
|
|
1691
|
+
content: Utils.format(ConstantString.OperationOnlyContainsOptionalParam, command.id),
|
|
1692
|
+
data: command.id,
|
|
1693
|
+
});
|
|
1694
|
+
}
|
|
1695
|
+
if (adaptiveCardFolder) {
|
|
1696
|
+
const adaptiveCardPath = path__default['default'].join(adaptiveCardFolder, command.id + ".json");
|
|
1697
|
+
command.apiResponseRenderingTemplateFile = (yield fs__default['default'].pathExists(adaptiveCardPath))
|
|
1698
|
+
? ManifestUpdater.getRelativePath(manifestPath, adaptiveCardPath)
|
|
1699
|
+
: "";
|
|
1700
|
+
}
|
|
1701
|
+
commands.push(command);
|
|
1702
|
+
}
|
|
1703
|
+
}
|
|
1704
|
+
}
|
|
1705
|
+
}
|
|
1706
|
+
}
|
|
1707
|
+
}
|
|
1708
|
+
return [commands, warnings];
|
|
1709
|
+
});
|
|
1710
|
+
}
|
|
1711
|
+
static getRelativePath(from, to) {
|
|
1712
|
+
const relativePath = path__default['default'].relative(path__default['default'].dirname(from), to);
|
|
1713
|
+
return path__default['default'].normalize(relativePath).replace(/\\/g, "/");
|
|
1714
|
+
}
|
|
1715
|
+
static removeEnvs(str) {
|
|
1716
|
+
const placeHolderReg = /\${{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*}}/g;
|
|
1717
|
+
const matches = placeHolderReg.exec(str);
|
|
1718
|
+
let newStr = str;
|
|
1719
|
+
if (matches != null) {
|
|
1720
|
+
newStr = newStr.replace(matches[0], "");
|
|
1721
|
+
}
|
|
1722
|
+
return newStr;
|
|
1723
|
+
}
|
|
1724
|
+
static removeAllSpecialCharacters(str) {
|
|
1725
|
+
return str.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
1726
|
+
}
|
|
1727
|
+
static getConfirmationBodyItem(paramName) {
|
|
1728
|
+
return `* **${Utils.updateFirstLetter(paramName)}**: {{function.parameters.${paramName}}}`;
|
|
1729
|
+
}
|
|
1730
|
+
}
|
|
1731
|
+
|
|
1331
1732
|
// Copyright (c) Microsoft Corporation.
|
|
1332
1733
|
/**
|
|
1333
1734
|
* A class that parses an OpenAPI specification file and provides methods to validate, list, and generate artifacts.
|
|
@@ -1347,7 +1748,11 @@ class SpecParser {
|
|
|
1347
1748
|
allowMultipleParameters: false,
|
|
1348
1749
|
allowOauth2: false,
|
|
1349
1750
|
allowMethods: ["get", "post"],
|
|
1751
|
+
allowConversationStarters: false,
|
|
1752
|
+
allowResponseSemantics: false,
|
|
1753
|
+
allowConfirmation: false,
|
|
1350
1754
|
projectType: exports.ProjectType.SME,
|
|
1755
|
+
isGptPlugin: false,
|
|
1351
1756
|
};
|
|
1352
1757
|
this.pathOrSpec = pathOrDoc;
|
|
1353
1758
|
this.parser = new SwaggerParser__default['default']();
|
|
@@ -1372,6 +1777,8 @@ class SpecParser {
|
|
|
1372
1777
|
errors: [{ type: exports.ErrorType.SpecNotValid, content: e.toString() }],
|
|
1373
1778
|
};
|
|
1374
1779
|
}
|
|
1780
|
+
const errors = [];
|
|
1781
|
+
const warnings = [];
|
|
1375
1782
|
if (!this.options.allowSwagger && this.isSwaggerFile) {
|
|
1376
1783
|
return {
|
|
1377
1784
|
status: exports.ValidationStatus.Error,
|
|
@@ -1381,7 +1788,38 @@ class SpecParser {
|
|
|
1381
1788
|
],
|
|
1382
1789
|
};
|
|
1383
1790
|
}
|
|
1384
|
-
|
|
1791
|
+
// Remote reference not supported
|
|
1792
|
+
const refPaths = this.parser.$refs.paths();
|
|
1793
|
+
// refPaths [0] is the current spec file path
|
|
1794
|
+
if (refPaths.length > 1) {
|
|
1795
|
+
errors.push({
|
|
1796
|
+
type: exports.ErrorType.RemoteRefNotSupported,
|
|
1797
|
+
content: Utils.format(ConstantString.RemoteRefNotSupported, refPaths.join(", ")),
|
|
1798
|
+
data: refPaths,
|
|
1799
|
+
});
|
|
1800
|
+
}
|
|
1801
|
+
if (!!this.isSwaggerFile && this.options.allowSwagger) {
|
|
1802
|
+
warnings.push({
|
|
1803
|
+
type: exports.WarningType.ConvertSwaggerToOpenAPI,
|
|
1804
|
+
content: ConstantString.ConvertSwaggerToOpenAPI,
|
|
1805
|
+
});
|
|
1806
|
+
}
|
|
1807
|
+
const validator = this.getValidator(this.spec);
|
|
1808
|
+
const validationResult = validator.validateSpec();
|
|
1809
|
+
warnings.push(...validationResult.warnings);
|
|
1810
|
+
errors.push(...validationResult.errors);
|
|
1811
|
+
let status = exports.ValidationStatus.Valid;
|
|
1812
|
+
if (warnings.length > 0 && errors.length === 0) {
|
|
1813
|
+
status = exports.ValidationStatus.Warning;
|
|
1814
|
+
}
|
|
1815
|
+
else if (errors.length > 0) {
|
|
1816
|
+
status = exports.ValidationStatus.Error;
|
|
1817
|
+
}
|
|
1818
|
+
return {
|
|
1819
|
+
status: status,
|
|
1820
|
+
warnings: warnings,
|
|
1821
|
+
errors: errors,
|
|
1822
|
+
};
|
|
1385
1823
|
}
|
|
1386
1824
|
catch (err) {
|
|
1387
1825
|
throw new SpecParserError(err.toString(), exports.ErrorType.ValidateFailed);
|
|
@@ -1405,45 +1843,40 @@ class SpecParser {
|
|
|
1405
1843
|
try {
|
|
1406
1844
|
yield this.loadSpec();
|
|
1407
1845
|
const spec = this.spec;
|
|
1408
|
-
const apiMap = this.
|
|
1846
|
+
const apiMap = this.getAPIs(spec);
|
|
1409
1847
|
const result = {
|
|
1410
|
-
|
|
1848
|
+
APIs: [],
|
|
1411
1849
|
allAPICount: 0,
|
|
1412
1850
|
validAPICount: 0,
|
|
1413
1851
|
};
|
|
1414
1852
|
for (const apiKey in apiMap) {
|
|
1853
|
+
const { operation, isValid, reason } = apiMap[apiKey];
|
|
1854
|
+
const [method, path] = apiKey.split(" ");
|
|
1855
|
+
const operationId = (_a = operation.operationId) !== null && _a !== void 0 ? _a : `${method.toLowerCase()}${Utils.convertPathToCamelCase(path)}`;
|
|
1415
1856
|
const apiResult = {
|
|
1416
|
-
api:
|
|
1857
|
+
api: apiKey,
|
|
1417
1858
|
server: "",
|
|
1418
|
-
operationId:
|
|
1859
|
+
operationId: operationId,
|
|
1860
|
+
isValid: isValid,
|
|
1861
|
+
reason: reason,
|
|
1419
1862
|
};
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
if (!operationId) {
|
|
1432
|
-
operationId = `${method.toLowerCase()}${Utils.convertPathToCamelCase(path)}`;
|
|
1433
|
-
}
|
|
1434
|
-
apiResult.operationId = operationId;
|
|
1435
|
-
const authArray = Utils.getAuthArray(operation.security, spec);
|
|
1436
|
-
for (const auths of authArray) {
|
|
1437
|
-
if (auths.length === 1) {
|
|
1438
|
-
apiResult.auth = auths[0];
|
|
1439
|
-
break;
|
|
1863
|
+
if (isValid) {
|
|
1864
|
+
const serverObj = Utils.getServerObject(spec, method.toLocaleLowerCase(), path);
|
|
1865
|
+
if (serverObj) {
|
|
1866
|
+
apiResult.server = Utils.resolveEnv(serverObj.url);
|
|
1867
|
+
}
|
|
1868
|
+
const authArray = Utils.getAuthArray(operation.security, spec);
|
|
1869
|
+
for (const auths of authArray) {
|
|
1870
|
+
if (auths.length === 1) {
|
|
1871
|
+
apiResult.auth = auths[0];
|
|
1872
|
+
break;
|
|
1873
|
+
}
|
|
1440
1874
|
}
|
|
1441
1875
|
}
|
|
1442
|
-
apiResult
|
|
1443
|
-
result.validAPIs.push(apiResult);
|
|
1876
|
+
result.APIs.push(apiResult);
|
|
1444
1877
|
}
|
|
1445
|
-
result.allAPICount =
|
|
1446
|
-
result.validAPICount = result.
|
|
1878
|
+
result.allAPICount = result.APIs.length;
|
|
1879
|
+
result.validAPICount = result.APIs.filter((api) => api.isValid).length;
|
|
1447
1880
|
return result;
|
|
1448
1881
|
}
|
|
1449
1882
|
catch (err) {
|
|
@@ -1500,18 +1933,12 @@ class SpecParser {
|
|
|
1500
1933
|
const newSpecs = yield this.getFilteredSpecs(filter, signal);
|
|
1501
1934
|
const newUnResolvedSpec = newSpecs[0];
|
|
1502
1935
|
const newSpec = newSpecs[1];
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
resultStr = jsyaml__default['default'].dump(newUnResolvedSpec);
|
|
1506
|
-
}
|
|
1507
|
-
else {
|
|
1508
|
-
resultStr = JSON.stringify(newUnResolvedSpec, null, 2);
|
|
1509
|
-
}
|
|
1510
|
-
yield fs__default['default'].outputFile(outputSpecPath, resultStr);
|
|
1936
|
+
const authInfo = Utils.getAuthInfo(newSpec);
|
|
1937
|
+
yield this.saveFilterSpec(outputSpecPath, newUnResolvedSpec);
|
|
1511
1938
|
if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
|
|
1512
1939
|
throw new SpecParserError(ConstantString.CancelledMessage, exports.ErrorType.Cancelled);
|
|
1513
1940
|
}
|
|
1514
|
-
const [updatedManifest, apiPlugin] = yield ManifestUpdater.updateManifestWithAiPlugin(manifestPath, outputSpecPath, pluginFilePath, newSpec, this.options);
|
|
1941
|
+
const [updatedManifest, apiPlugin] = yield ManifestUpdater.updateManifestWithAiPlugin(manifestPath, outputSpecPath, pluginFilePath, newSpec, this.options, authInfo);
|
|
1515
1942
|
yield fs__default['default'].outputJSON(manifestPath, updatedManifest, { spaces: 2 });
|
|
1516
1943
|
yield fs__default['default'].outputJSON(pluginFilePath, apiPlugin, { spaces: 2 });
|
|
1517
1944
|
}
|
|
@@ -1541,32 +1968,11 @@ class SpecParser {
|
|
|
1541
1968
|
const newSpecs = yield this.getFilteredSpecs(filter, signal);
|
|
1542
1969
|
const newUnResolvedSpec = newSpecs[0];
|
|
1543
1970
|
const newSpec = newSpecs[1];
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
for (const method in newSpec.paths[url]) {
|
|
1548
|
-
const operation = newSpec.paths[url][method];
|
|
1549
|
-
const authArray = Utils.getAuthArray(operation.security, newSpec);
|
|
1550
|
-
if (authArray && authArray.length > 0) {
|
|
1551
|
-
authSet.add(authArray[0][0]);
|
|
1552
|
-
if (authSet.size > 1) {
|
|
1553
|
-
hasMultipleAuth = true;
|
|
1554
|
-
break;
|
|
1555
|
-
}
|
|
1556
|
-
}
|
|
1557
|
-
}
|
|
1558
|
-
}
|
|
1559
|
-
if (hasMultipleAuth && this.options.projectType !== exports.ProjectType.TeamsAi) {
|
|
1560
|
-
throw new SpecParserError(ConstantString.MultipleAuthNotSupported, exports.ErrorType.MultipleAuthNotSupported);
|
|
1561
|
-
}
|
|
1562
|
-
let resultStr;
|
|
1563
|
-
if (outputSpecPath.endsWith(".yaml") || outputSpecPath.endsWith(".yml")) {
|
|
1564
|
-
resultStr = jsyaml__default['default'].dump(newUnResolvedSpec);
|
|
1971
|
+
let authInfo = undefined;
|
|
1972
|
+
if (this.options.projectType === exports.ProjectType.SME) {
|
|
1973
|
+
authInfo = Utils.getAuthInfo(newSpec);
|
|
1565
1974
|
}
|
|
1566
|
-
|
|
1567
|
-
resultStr = JSON.stringify(newUnResolvedSpec, null, 2);
|
|
1568
|
-
}
|
|
1569
|
-
yield fs__default['default'].outputFile(outputSpecPath, resultStr);
|
|
1975
|
+
yield this.saveFilterSpec(outputSpecPath, newUnResolvedSpec);
|
|
1570
1976
|
if (adaptiveCardFolder) {
|
|
1571
1977
|
for (const url in newSpec.paths) {
|
|
1572
1978
|
for (const method in newSpec.paths[url]) {
|
|
@@ -1596,7 +2002,6 @@ class SpecParser {
|
|
|
1596
2002
|
if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
|
|
1597
2003
|
throw new SpecParserError(ConstantString.CancelledMessage, exports.ErrorType.Cancelled);
|
|
1598
2004
|
}
|
|
1599
|
-
const authInfo = Array.from(authSet)[0];
|
|
1600
2005
|
const [updatedManifest, warnings] = yield ManifestUpdater.updateManifest(manifestPath, outputSpecPath, newSpec, this.options, adaptiveCardFolder, authInfo);
|
|
1601
2006
|
yield fs__default['default'].outputJSON(manifestPath, updatedManifest, { spaces: 2 });
|
|
1602
2007
|
result.warnings.push(...warnings);
|
|
@@ -1625,13 +2030,30 @@ class SpecParser {
|
|
|
1625
2030
|
}
|
|
1626
2031
|
});
|
|
1627
2032
|
}
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
2033
|
+
getAPIs(spec) {
|
|
2034
|
+
const validator = this.getValidator(spec);
|
|
2035
|
+
const apiMap = validator.listAPIs();
|
|
2036
|
+
return apiMap;
|
|
2037
|
+
}
|
|
2038
|
+
getValidator(spec) {
|
|
2039
|
+
if (this.validator) {
|
|
2040
|
+
return this.validator;
|
|
1631
2041
|
}
|
|
1632
|
-
const
|
|
1633
|
-
this.
|
|
1634
|
-
return
|
|
2042
|
+
const validator = ValidatorFactory.create(spec, this.options);
|
|
2043
|
+
this.validator = validator;
|
|
2044
|
+
return validator;
|
|
2045
|
+
}
|
|
2046
|
+
saveFilterSpec(outputSpecPath, unResolvedSpec) {
|
|
2047
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2048
|
+
let resultStr;
|
|
2049
|
+
if (outputSpecPath.endsWith(".yaml") || outputSpecPath.endsWith(".yml")) {
|
|
2050
|
+
resultStr = jsyaml__default['default'].dump(unResolvedSpec);
|
|
2051
|
+
}
|
|
2052
|
+
else {
|
|
2053
|
+
resultStr = JSON.stringify(unResolvedSpec, null, 2);
|
|
2054
|
+
}
|
|
2055
|
+
yield fs__default['default'].outputFile(outputSpecPath, resultStr);
|
|
2056
|
+
});
|
|
1635
2057
|
}
|
|
1636
2058
|
}
|
|
1637
2059
|
|