@microsoft/m365-spec-parser 0.1.1-alpha.48b9eab36.0 → 0.1.1-alpha.4e708f092.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 +637 -302
- package/dist/index.esm2017.js.map +1 -1
- package/dist/index.esm2017.mjs +1096 -479
- package/dist/index.esm2017.mjs.map +1 -1
- package/dist/index.esm5.js +639 -302
- package/dist/index.esm5.js.map +1 -1
- package/dist/index.node.cjs.js +1105 -482
- package/dist/index.node.cjs.js.map +1 -1
- package/dist/src/adaptiveCardWrapper.d.ts +2 -0
- package/dist/src/constants.d.ts +7 -1
- package/dist/src/index.browser.d.ts +1 -1
- package/dist/src/index.d.ts +1 -1
- package/dist/src/interfaces.d.ts +102 -18
- package/dist/src/manifestUpdater.d.ts +9 -4
- package/dist/src/specFilter.d.ts +2 -1
- package/dist/src/specParser.browser.d.ts +12 -3
- package/dist/src/specParser.d.ts +13 -5
- package/dist/src/utils.d.ts +18 -34
- package/package.json +4 -4
package/dist/index.node.cjs.js
CHANGED
|
@@ -61,7 +61,8 @@ exports.ErrorType = void 0;
|
|
|
61
61
|
ErrorType["NoExtraAPICanBeAdded"] = "no-extra-api-can-be-added";
|
|
62
62
|
ErrorType["ResolveServerUrlFailed"] = "resolve-server-url-failed";
|
|
63
63
|
ErrorType["SwaggerNotSupported"] = "swagger-not-supported";
|
|
64
|
-
ErrorType["
|
|
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 = {}));
|
|
@@ -92,7 +108,13 @@ exports.ValidationStatus = void 0;
|
|
|
92
108
|
ValidationStatus[ValidationStatus["Valid"] = 0] = "Valid";
|
|
93
109
|
ValidationStatus[ValidationStatus["Warning"] = 1] = "Warning";
|
|
94
110
|
ValidationStatus[ValidationStatus["Error"] = 2] = "Error";
|
|
95
|
-
})(exports.ValidationStatus || (exports.ValidationStatus = {}));
|
|
111
|
+
})(exports.ValidationStatus || (exports.ValidationStatus = {}));
|
|
112
|
+
exports.ProjectType = void 0;
|
|
113
|
+
(function (ProjectType) {
|
|
114
|
+
ProjectType[ProjectType["Copilot"] = 0] = "Copilot";
|
|
115
|
+
ProjectType[ProjectType["SME"] = 1] = "SME";
|
|
116
|
+
ProjectType[ProjectType["TeamsAi"] = 2] = "TeamsAi";
|
|
117
|
+
})(exports.ProjectType || (exports.ProjectType = {}));
|
|
96
118
|
|
|
97
119
|
// Copyright (c) Microsoft Corporation.
|
|
98
120
|
class ConstantString {
|
|
@@ -111,7 +133,9 @@ ConstantString.ResolveServerUrlFailed = "Unable to resolve the server URL: pleas
|
|
|
111
133
|
ConstantString.OperationOnlyContainsOptionalParam = "Operation %s contains multiple optional parameters. The first optional parameter is used for this command.";
|
|
112
134
|
ConstantString.ConvertSwaggerToOpenAPI = "The Swagger 2.0 file has been converted to OpenAPI 3.0.";
|
|
113
135
|
ConstantString.SwaggerNotSupported = "Swagger 2.0 is not supported. Please convert to OpenAPI 3.0 manually before proceeding.";
|
|
114
|
-
ConstantString.
|
|
136
|
+
ConstantString.SpecVersionNotSupported = "Unsupported OpenAPI version %s. Please use version 3.0.x.";
|
|
137
|
+
ConstantString.MultipleAuthNotSupported = "Multiple authentication methods are unsupported. Ensure all selected APIs use identical authentication.";
|
|
138
|
+
ConstantString.UnsupportedSchema = "Unsupported schema in %s %s: %s";
|
|
115
139
|
ConstantString.WrappedCardVersion = "devPreview";
|
|
116
140
|
ConstantString.WrappedCardSchema = "https://developer.microsoft.com/json-schemas/teams/vDevPreview/MicrosoftTeams.ResponseRenderingTemplate.schema.json";
|
|
117
141
|
ConstantString.WrappedCardResponseLayout = "list";
|
|
@@ -121,8 +145,10 @@ ConstantString.AdaptiveCardVersion = "1.5";
|
|
|
121
145
|
ConstantString.AdaptiveCardSchema = "http://adaptivecards.io/schemas/adaptive-card.json";
|
|
122
146
|
ConstantString.AdaptiveCardType = "AdaptiveCard";
|
|
123
147
|
ConstantString.TextBlockType = "TextBlock";
|
|
148
|
+
ConstantString.ImageType = "Image";
|
|
124
149
|
ConstantString.ContainerType = "Container";
|
|
125
150
|
ConstantString.RegistrationIdPostfix = "REGISTRATION_ID";
|
|
151
|
+
ConstantString.OAuthRegistrationIdPostFix = "OAUTH_REGISTRATION_ID";
|
|
126
152
|
ConstantString.ResponseCodeFor20X = [
|
|
127
153
|
"200",
|
|
128
154
|
"201",
|
|
@@ -182,7 +208,9 @@ ConstantString.FullDescriptionMaxLens = 4000;
|
|
|
182
208
|
ConstantString.CommandDescriptionMaxLens = 128;
|
|
183
209
|
ConstantString.ParameterDescriptionMaxLens = 128;
|
|
184
210
|
ConstantString.CommandTitleMaxLens = 32;
|
|
185
|
-
ConstantString.ParameterTitleMaxLens = 32;
|
|
211
|
+
ConstantString.ParameterTitleMaxLens = 32;
|
|
212
|
+
ConstantString.SMERequiredParamsMaxNum = 5;
|
|
213
|
+
ConstantString.DefaultPluginId = "plugin_1";
|
|
186
214
|
|
|
187
215
|
// Copyright (c) Microsoft Corporation.
|
|
188
216
|
class SpecParserError extends Error {
|
|
@@ -194,201 +222,30 @@ class SpecParserError extends Error {
|
|
|
194
222
|
|
|
195
223
|
// Copyright (c) Microsoft Corporation.
|
|
196
224
|
class Utils {
|
|
197
|
-
static
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
};
|
|
203
|
-
if (!paramObject) {
|
|
204
|
-
return paramResult;
|
|
205
|
-
}
|
|
206
|
-
for (let i = 0; i < paramObject.length; i++) {
|
|
207
|
-
const param = paramObject[i];
|
|
208
|
-
const schema = param.schema;
|
|
209
|
-
const isRequiredWithoutDefault = param.required && schema.default === undefined;
|
|
210
|
-
if (param.in === "header" || param.in === "cookie") {
|
|
211
|
-
if (isRequiredWithoutDefault) {
|
|
212
|
-
paramResult.isValid = false;
|
|
213
|
-
}
|
|
214
|
-
continue;
|
|
215
|
-
}
|
|
216
|
-
if (schema.type !== "boolean" &&
|
|
217
|
-
schema.type !== "string" &&
|
|
218
|
-
schema.type !== "number" &&
|
|
219
|
-
schema.type !== "integer") {
|
|
220
|
-
if (isRequiredWithoutDefault) {
|
|
221
|
-
paramResult.isValid = false;
|
|
222
|
-
}
|
|
223
|
-
continue;
|
|
224
|
-
}
|
|
225
|
-
if (param.in === "query" || param.in === "path") {
|
|
226
|
-
if (isRequiredWithoutDefault) {
|
|
227
|
-
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
228
|
-
}
|
|
229
|
-
else {
|
|
230
|
-
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
return paramResult;
|
|
235
|
-
}
|
|
236
|
-
static checkPostBody(schema, isRequired = false) {
|
|
237
|
-
var _a;
|
|
238
|
-
const paramResult = {
|
|
239
|
-
requiredNum: 0,
|
|
240
|
-
optionalNum: 0,
|
|
241
|
-
isValid: true,
|
|
242
|
-
};
|
|
243
|
-
if (Object.keys(schema).length === 0) {
|
|
244
|
-
return paramResult;
|
|
245
|
-
}
|
|
246
|
-
const isRequiredWithoutDefault = isRequired && schema.default === undefined;
|
|
247
|
-
if (schema.type === "string" ||
|
|
248
|
-
schema.type === "integer" ||
|
|
249
|
-
schema.type === "boolean" ||
|
|
250
|
-
schema.type === "number") {
|
|
251
|
-
if (isRequiredWithoutDefault) {
|
|
252
|
-
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
253
|
-
}
|
|
254
|
-
else {
|
|
255
|
-
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
else if (schema.type === "object") {
|
|
259
|
-
const { properties } = schema;
|
|
260
|
-
for (const property in properties) {
|
|
261
|
-
let isRequired = false;
|
|
262
|
-
if (schema.required && ((_a = schema.required) === null || _a === void 0 ? void 0 : _a.indexOf(property)) >= 0) {
|
|
263
|
-
isRequired = true;
|
|
264
|
-
}
|
|
265
|
-
const result = Utils.checkPostBody(properties[property], isRequired);
|
|
266
|
-
paramResult.requiredNum += result.requiredNum;
|
|
267
|
-
paramResult.optionalNum += result.optionalNum;
|
|
268
|
-
paramResult.isValid = paramResult.isValid && result.isValid;
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
else {
|
|
272
|
-
if (isRequiredWithoutDefault) {
|
|
273
|
-
paramResult.isValid = false;
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
return paramResult;
|
|
277
|
-
}
|
|
278
|
-
/**
|
|
279
|
-
* Checks if the given API is supported.
|
|
280
|
-
* @param {string} method - The HTTP method of the API.
|
|
281
|
-
* @param {string} path - The path of the API.
|
|
282
|
-
* @param {OpenAPIV3.Document} spec - The OpenAPI specification document.
|
|
283
|
-
* @returns {boolean} - Returns true if the API is supported, false otherwise.
|
|
284
|
-
* @description The following APIs are supported:
|
|
285
|
-
* 1. only support Get/Post operation without auth property
|
|
286
|
-
* 2. parameter inside query or path only support string, number, boolean and integer
|
|
287
|
-
* 3. parameter inside post body only support string, number, boolean, integer and object
|
|
288
|
-
* 4. request body + required parameters <= 1
|
|
289
|
-
* 5. response body should be “application/json” and not empty, and response code should be 20X
|
|
290
|
-
* 6. only support request body with “application/json” content type
|
|
291
|
-
*/
|
|
292
|
-
static isSupportedApi(method, path, spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2) {
|
|
293
|
-
const pathObj = spec.paths[path];
|
|
294
|
-
method = method.toLocaleLowerCase();
|
|
295
|
-
if (pathObj) {
|
|
296
|
-
if ((method === ConstantString.PostMethod || method === ConstantString.GetMethod) &&
|
|
297
|
-
pathObj[method]) {
|
|
298
|
-
const securities = pathObj[method].security;
|
|
299
|
-
const authArray = Utils.getAuthArray(securities, spec);
|
|
300
|
-
if (!Utils.isSupportedAuth(authArray, allowAPIKeyAuth, allowOauth2)) {
|
|
301
|
-
return false;
|
|
302
|
-
}
|
|
303
|
-
const operationObject = pathObj[method];
|
|
304
|
-
if (!allowMissingId && !operationObject.operationId) {
|
|
305
|
-
return false;
|
|
306
|
-
}
|
|
307
|
-
const paramObject = operationObject.parameters;
|
|
308
|
-
const requestBody = operationObject.requestBody;
|
|
309
|
-
const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
|
|
310
|
-
const mediaTypesCount = Object.keys((requestBody === null || requestBody === void 0 ? void 0 : requestBody.content) || {}).length;
|
|
311
|
-
if (mediaTypesCount > 1) {
|
|
312
|
-
return false;
|
|
313
|
-
}
|
|
314
|
-
const responseJson = Utils.getResponseJson(operationObject);
|
|
315
|
-
if (Object.keys(responseJson).length === 0) {
|
|
316
|
-
return false;
|
|
317
|
-
}
|
|
318
|
-
let requestBodyParamResult = {
|
|
319
|
-
requiredNum: 0,
|
|
320
|
-
optionalNum: 0,
|
|
321
|
-
isValid: true,
|
|
322
|
-
};
|
|
323
|
-
if (requestJsonBody) {
|
|
324
|
-
const requestBodySchema = requestJsonBody.schema;
|
|
325
|
-
requestBodyParamResult = Utils.checkPostBody(requestBodySchema, requestBody.required);
|
|
326
|
-
}
|
|
327
|
-
if (!requestBodyParamResult.isValid) {
|
|
328
|
-
return false;
|
|
329
|
-
}
|
|
330
|
-
const paramResult = Utils.checkParameters(paramObject);
|
|
331
|
-
if (!paramResult.isValid) {
|
|
332
|
-
return false;
|
|
333
|
-
}
|
|
334
|
-
if (requestBodyParamResult.requiredNum + paramResult.requiredNum > 1) {
|
|
335
|
-
if (allowMultipleParameters &&
|
|
336
|
-
requestBodyParamResult.requiredNum + paramResult.requiredNum <= 5) {
|
|
337
|
-
return true;
|
|
338
|
-
}
|
|
339
|
-
return false;
|
|
340
|
-
}
|
|
341
|
-
else if (requestBodyParamResult.requiredNum +
|
|
342
|
-
requestBodyParamResult.optionalNum +
|
|
343
|
-
paramResult.requiredNum +
|
|
344
|
-
paramResult.optionalNum ===
|
|
345
|
-
0) {
|
|
346
|
-
return false;
|
|
347
|
-
}
|
|
348
|
-
else {
|
|
225
|
+
static hasNestedObjectInSchema(schema) {
|
|
226
|
+
if (schema.type === "object") {
|
|
227
|
+
for (const property in schema.properties) {
|
|
228
|
+
const nestedSchema = schema.properties[property];
|
|
229
|
+
if (nestedSchema.type === "object") {
|
|
349
230
|
return true;
|
|
350
231
|
}
|
|
351
232
|
}
|
|
352
233
|
}
|
|
353
234
|
return false;
|
|
354
235
|
}
|
|
355
|
-
static
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
// Currently we don't support multiple auth in one operation
|
|
361
|
-
if (authSchemaArray.length > 0 && authSchemaArray.every((auths) => auths.length > 1)) {
|
|
362
|
-
return false;
|
|
363
|
-
}
|
|
364
|
-
for (const auths of authSchemaArray) {
|
|
365
|
-
if (auths.length === 1) {
|
|
366
|
-
if (!allowOauth2 && allowAPIKeyAuth && Utils.isAPIKeyAuth(auths[0].authSchema)) {
|
|
367
|
-
return true;
|
|
368
|
-
}
|
|
369
|
-
else if (!allowAPIKeyAuth &&
|
|
370
|
-
allowOauth2 &&
|
|
371
|
-
Utils.isBearerTokenAuth(auths[0].authSchema)) {
|
|
372
|
-
return true;
|
|
373
|
-
}
|
|
374
|
-
else if (allowAPIKeyAuth &&
|
|
375
|
-
allowOauth2 &&
|
|
376
|
-
(Utils.isAPIKeyAuth(auths[0].authSchema) ||
|
|
377
|
-
Utils.isBearerTokenAuth(auths[0].authSchema))) {
|
|
378
|
-
return true;
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
return false;
|
|
236
|
+
static containMultipleMediaTypes(bodyObject) {
|
|
237
|
+
return Object.keys((bodyObject === null || bodyObject === void 0 ? void 0 : bodyObject.content) || {}).length > 1;
|
|
238
|
+
}
|
|
239
|
+
static isBearerTokenAuth(authScheme) {
|
|
240
|
+
return authScheme.type === "http" && authScheme.scheme === "bearer";
|
|
384
241
|
}
|
|
385
|
-
static isAPIKeyAuth(
|
|
386
|
-
return
|
|
242
|
+
static isAPIKeyAuth(authScheme) {
|
|
243
|
+
return authScheme.type === "apiKey";
|
|
387
244
|
}
|
|
388
|
-
static
|
|
389
|
-
return (
|
|
390
|
-
|
|
391
|
-
|
|
245
|
+
static isOAuthWithAuthCodeFlow(authScheme) {
|
|
246
|
+
return !!(authScheme.type === "oauth2" &&
|
|
247
|
+
authScheme.flows &&
|
|
248
|
+
authScheme.flows.authorizationCode);
|
|
392
249
|
}
|
|
393
250
|
static getAuthArray(securities, spec) {
|
|
394
251
|
var _a;
|
|
@@ -401,7 +258,7 @@ class Utils {
|
|
|
401
258
|
for (const name in security) {
|
|
402
259
|
const auth = securitySchemas[name];
|
|
403
260
|
authArray.push({
|
|
404
|
-
|
|
261
|
+
authScheme: auth,
|
|
405
262
|
name: name,
|
|
406
263
|
});
|
|
407
264
|
}
|
|
@@ -419,18 +276,22 @@ class Utils {
|
|
|
419
276
|
static getResponseJson(operationObject) {
|
|
420
277
|
var _a, _b;
|
|
421
278
|
let json = {};
|
|
279
|
+
let multipleMediaType = false;
|
|
422
280
|
for (const code of ConstantString.ResponseCodeFor20X) {
|
|
423
281
|
const responseObject = (_a = operationObject === null || operationObject === void 0 ? void 0 : operationObject.responses) === null || _a === void 0 ? void 0 : _a[code];
|
|
424
|
-
const mediaTypesCount = Object.keys((responseObject === null || responseObject === void 0 ? void 0 : responseObject.content) || {}).length;
|
|
425
|
-
if (mediaTypesCount > 1) {
|
|
426
|
-
return {};
|
|
427
|
-
}
|
|
428
282
|
if ((_b = responseObject === null || responseObject === void 0 ? void 0 : responseObject.content) === null || _b === void 0 ? void 0 : _b["application/json"]) {
|
|
283
|
+
multipleMediaType = false;
|
|
429
284
|
json = responseObject.content["application/json"];
|
|
430
|
-
|
|
285
|
+
if (Utils.containMultipleMediaTypes(responseObject)) {
|
|
286
|
+
multipleMediaType = true;
|
|
287
|
+
json = {};
|
|
288
|
+
}
|
|
289
|
+
else {
|
|
290
|
+
break;
|
|
291
|
+
}
|
|
431
292
|
}
|
|
432
293
|
}
|
|
433
|
-
return json;
|
|
294
|
+
return { json, multipleMediaType };
|
|
434
295
|
}
|
|
435
296
|
static convertPathToCamelCase(path) {
|
|
436
297
|
const pathSegments = path.split(/[./{]/);
|
|
@@ -450,10 +311,10 @@ class Utils {
|
|
|
450
311
|
return undefined;
|
|
451
312
|
}
|
|
452
313
|
}
|
|
453
|
-
static
|
|
314
|
+
static resolveEnv(str) {
|
|
454
315
|
const placeHolderReg = /\${{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*}}/g;
|
|
455
|
-
let matches = placeHolderReg.exec(
|
|
456
|
-
let
|
|
316
|
+
let matches = placeHolderReg.exec(str);
|
|
317
|
+
let newStr = str;
|
|
457
318
|
while (matches != null) {
|
|
458
319
|
const envVar = matches[1];
|
|
459
320
|
const envVal = process.env[envVar];
|
|
@@ -461,17 +322,17 @@ class Utils {
|
|
|
461
322
|
throw new Error(Utils.format(ConstantString.ResolveServerUrlFailed, envVar));
|
|
462
323
|
}
|
|
463
324
|
else {
|
|
464
|
-
|
|
325
|
+
newStr = newStr.replace(matches[0], envVal);
|
|
465
326
|
}
|
|
466
|
-
matches = placeHolderReg.exec(
|
|
327
|
+
matches = placeHolderReg.exec(str);
|
|
467
328
|
}
|
|
468
|
-
return
|
|
329
|
+
return newStr;
|
|
469
330
|
}
|
|
470
331
|
static checkServerUrl(servers) {
|
|
471
332
|
const errors = [];
|
|
472
333
|
let serverUrl;
|
|
473
334
|
try {
|
|
474
|
-
serverUrl = Utils.
|
|
335
|
+
serverUrl = Utils.resolveEnv(servers[0].url);
|
|
475
336
|
}
|
|
476
337
|
catch (err) {
|
|
477
338
|
errors.push({
|
|
@@ -501,7 +362,8 @@ class Utils {
|
|
|
501
362
|
}
|
|
502
363
|
return errors;
|
|
503
364
|
}
|
|
504
|
-
static validateServer(spec,
|
|
365
|
+
static validateServer(spec, options) {
|
|
366
|
+
var _a;
|
|
505
367
|
const errors = [];
|
|
506
368
|
let hasTopLevelServers = false;
|
|
507
369
|
let hasPathLevelServers = false;
|
|
@@ -522,7 +384,7 @@ class Utils {
|
|
|
522
384
|
}
|
|
523
385
|
for (const method in methods) {
|
|
524
386
|
const operationObject = methods[method];
|
|
525
|
-
if (
|
|
387
|
+
if (((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && operationObject) {
|
|
526
388
|
if ((operationObject === null || operationObject === void 0 ? void 0 : operationObject.servers) && operationObject.servers.length >= 1) {
|
|
527
389
|
hasOperationLevelServers = true;
|
|
528
390
|
const serverErrors = Utils.checkServerUrl(operationObject.servers);
|
|
@@ -565,6 +427,7 @@ class Utils {
|
|
|
565
427
|
Utils.updateParameterWithInputType(schema, parameter);
|
|
566
428
|
}
|
|
567
429
|
if (isRequired && schema.default === undefined) {
|
|
430
|
+
parameter.isRequired = true;
|
|
568
431
|
requiredParams.push(parameter);
|
|
569
432
|
}
|
|
570
433
|
else {
|
|
@@ -609,7 +472,7 @@ class Utils {
|
|
|
609
472
|
param.value = schema.default;
|
|
610
473
|
}
|
|
611
474
|
}
|
|
612
|
-
static parseApiInfo(operationItem,
|
|
475
|
+
static parseApiInfo(operationItem, options) {
|
|
613
476
|
var _a, _b;
|
|
614
477
|
const requiredParams = [];
|
|
615
478
|
const optionalParams = [];
|
|
@@ -623,11 +486,12 @@ class Utils {
|
|
|
623
486
|
description: ((_a = param.description) !== null && _a !== void 0 ? _a : "").slice(0, ConstantString.ParameterDescriptionMaxLens),
|
|
624
487
|
};
|
|
625
488
|
const schema = param.schema;
|
|
626
|
-
if (allowMultipleParameters && schema) {
|
|
489
|
+
if (options.allowMultipleParameters && schema) {
|
|
627
490
|
Utils.updateParameterWithInputType(schema, parameter);
|
|
628
491
|
}
|
|
629
492
|
if (param.in !== "header" && param.in !== "cookie") {
|
|
630
493
|
if (param.required && (schema === null || schema === void 0 ? void 0 : schema.default) === undefined) {
|
|
494
|
+
parameter.isRequired = true;
|
|
631
495
|
requiredParams.push(parameter);
|
|
632
496
|
}
|
|
633
497
|
else {
|
|
@@ -641,19 +505,13 @@ class Utils {
|
|
|
641
505
|
const requestJson = requestBody.content["application/json"];
|
|
642
506
|
if (Object.keys(requestJson).length !== 0) {
|
|
643
507
|
const schema = requestJson.schema;
|
|
644
|
-
const [requiredP, optionalP] = Utils.generateParametersFromSchema(schema, "requestBody", allowMultipleParameters, requestBody.required);
|
|
508
|
+
const [requiredP, optionalP] = Utils.generateParametersFromSchema(schema, "requestBody", !!options.allowMultipleParameters, requestBody.required);
|
|
645
509
|
requiredParams.push(...requiredP);
|
|
646
510
|
optionalParams.push(...optionalP);
|
|
647
511
|
}
|
|
648
512
|
}
|
|
649
513
|
const operationId = operationItem.operationId;
|
|
650
|
-
const parameters = [];
|
|
651
|
-
if (requiredParams.length !== 0) {
|
|
652
|
-
parameters.push(...requiredParams);
|
|
653
|
-
}
|
|
654
|
-
else {
|
|
655
|
-
parameters.push(optionalParams[0]);
|
|
656
|
-
}
|
|
514
|
+
const parameters = [...requiredParams, ...optionalParams];
|
|
657
515
|
const command = {
|
|
658
516
|
context: ["compose"],
|
|
659
517
|
type: "query",
|
|
@@ -662,130 +520,568 @@ class Utils {
|
|
|
662
520
|
parameters: parameters,
|
|
663
521
|
description: ((_b = operationItem.description) !== null && _b !== void 0 ? _b : "").slice(0, ConstantString.CommandDescriptionMaxLens),
|
|
664
522
|
};
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
523
|
+
return command;
|
|
524
|
+
}
|
|
525
|
+
static format(str, ...args) {
|
|
526
|
+
let index = 0;
|
|
527
|
+
return str.replace(/%s/g, () => {
|
|
528
|
+
const arg = args[index++];
|
|
529
|
+
return arg !== undefined ? arg : "";
|
|
530
|
+
});
|
|
531
|
+
}
|
|
532
|
+
static getSafeRegistrationIdEnvName(authName) {
|
|
533
|
+
if (!authName) {
|
|
534
|
+
return "";
|
|
535
|
+
}
|
|
536
|
+
let safeRegistrationIdEnvName = authName.toUpperCase().replace(/[^A-Z0-9_]/g, "_");
|
|
537
|
+
if (!safeRegistrationIdEnvName.match(/^[A-Z]/)) {
|
|
538
|
+
safeRegistrationIdEnvName = "PREFIX_" + safeRegistrationIdEnvName;
|
|
672
539
|
}
|
|
673
|
-
return
|
|
540
|
+
return safeRegistrationIdEnvName;
|
|
674
541
|
}
|
|
675
|
-
static
|
|
676
|
-
const
|
|
542
|
+
static getServerObject(spec, method, path) {
|
|
543
|
+
const pathObj = spec.paths[path];
|
|
544
|
+
const operationObject = pathObj[method];
|
|
545
|
+
const rootServer = spec.servers && spec.servers[0];
|
|
546
|
+
const methodServer = spec.paths[path].servers && spec.paths[path].servers[0];
|
|
547
|
+
const operationServer = operationObject.servers && operationObject.servers[0];
|
|
548
|
+
const serverUrl = operationServer || methodServer || rootServer;
|
|
549
|
+
return serverUrl;
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
// Copyright (c) Microsoft Corporation.
|
|
554
|
+
class Validator {
|
|
555
|
+
listAPIs() {
|
|
556
|
+
var _a;
|
|
557
|
+
if (this.apiMap) {
|
|
558
|
+
return this.apiMap;
|
|
559
|
+
}
|
|
560
|
+
const paths = this.spec.paths;
|
|
677
561
|
const result = {};
|
|
678
562
|
for (const path in paths) {
|
|
679
563
|
const methods = paths[path];
|
|
680
564
|
for (const method in methods) {
|
|
681
|
-
|
|
682
|
-
if (
|
|
683
|
-
const
|
|
684
|
-
result[`${method.toUpperCase()} ${path}`] =
|
|
565
|
+
const operationObject = methods[method];
|
|
566
|
+
if (((_a = this.options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && operationObject) {
|
|
567
|
+
const validateResult = this.validateAPI(method, path);
|
|
568
|
+
result[`${method.toUpperCase()} ${path}`] = {
|
|
569
|
+
operation: operationObject,
|
|
570
|
+
isValid: validateResult.isValid,
|
|
571
|
+
reason: validateResult.reason,
|
|
572
|
+
};
|
|
685
573
|
}
|
|
686
574
|
}
|
|
687
575
|
}
|
|
576
|
+
this.apiMap = result;
|
|
688
577
|
return result;
|
|
689
578
|
}
|
|
690
|
-
|
|
691
|
-
const
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
579
|
+
validateSpecVersion() {
|
|
580
|
+
const result = { errors: [], warnings: [] };
|
|
581
|
+
if (this.spec.openapi >= "3.1.0") {
|
|
582
|
+
result.errors.push({
|
|
583
|
+
type: exports.ErrorType.SpecVersionNotSupported,
|
|
584
|
+
content: Utils.format(ConstantString.SpecVersionNotSupported, this.spec.openapi),
|
|
585
|
+
data: this.spec.openapi,
|
|
697
586
|
});
|
|
698
587
|
}
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
const
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
588
|
+
return result;
|
|
589
|
+
}
|
|
590
|
+
validateSpecServer() {
|
|
591
|
+
const result = { errors: [], warnings: [] };
|
|
592
|
+
const serverErrors = Utils.validateServer(this.spec, this.options);
|
|
593
|
+
result.errors.push(...serverErrors);
|
|
594
|
+
return result;
|
|
595
|
+
}
|
|
596
|
+
validateSpecNoSupportAPI() {
|
|
597
|
+
const result = { errors: [], warnings: [] };
|
|
598
|
+
const apiMap = this.listAPIs();
|
|
599
|
+
const validAPIs = Object.entries(apiMap).filter(([, value]) => value.isValid);
|
|
600
|
+
if (validAPIs.length === 0) {
|
|
601
|
+
const data = [];
|
|
602
|
+
for (const key in apiMap) {
|
|
603
|
+
const { reason } = apiMap[key];
|
|
604
|
+
const apiInvalidReason = { api: key, reason: reason };
|
|
605
|
+
data.push(apiInvalidReason);
|
|
606
|
+
}
|
|
607
|
+
result.errors.push({
|
|
716
608
|
type: exports.ErrorType.NoSupportedApi,
|
|
717
609
|
content: ConstantString.NoSupportedApi,
|
|
610
|
+
data,
|
|
718
611
|
});
|
|
719
612
|
}
|
|
613
|
+
return result;
|
|
614
|
+
}
|
|
615
|
+
validateSpecOperationId() {
|
|
616
|
+
const result = { errors: [], warnings: [] };
|
|
617
|
+
const apiMap = this.listAPIs();
|
|
720
618
|
// OperationId missing
|
|
721
619
|
const apisMissingOperationId = [];
|
|
722
620
|
for (const key in apiMap) {
|
|
723
|
-
const
|
|
724
|
-
if (!
|
|
621
|
+
const { operation } = apiMap[key];
|
|
622
|
+
if (!operation.operationId) {
|
|
725
623
|
apisMissingOperationId.push(key);
|
|
726
624
|
}
|
|
727
625
|
}
|
|
728
626
|
if (apisMissingOperationId.length > 0) {
|
|
729
|
-
warnings.push({
|
|
627
|
+
result.warnings.push({
|
|
730
628
|
type: exports.WarningType.OperationIdMissing,
|
|
731
629
|
content: Utils.format(ConstantString.MissingOperationId, apisMissingOperationId.join(", ")),
|
|
732
630
|
data: apisMissingOperationId,
|
|
733
631
|
});
|
|
734
632
|
}
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
633
|
+
return result;
|
|
634
|
+
}
|
|
635
|
+
validateMethodAndPath(method, path) {
|
|
636
|
+
const result = { isValid: true, reason: [] };
|
|
637
|
+
if (this.options.allowMethods && !this.options.allowMethods.includes(method)) {
|
|
638
|
+
result.isValid = false;
|
|
639
|
+
result.reason.push(exports.ErrorType.MethodNotAllowed);
|
|
640
|
+
return result;
|
|
641
|
+
}
|
|
642
|
+
const pathObj = this.spec.paths[path];
|
|
643
|
+
if (!pathObj || !pathObj[method]) {
|
|
644
|
+
result.isValid = false;
|
|
645
|
+
result.reason.push(exports.ErrorType.UrlPathNotExist);
|
|
646
|
+
return result;
|
|
647
|
+
}
|
|
648
|
+
return result;
|
|
649
|
+
}
|
|
650
|
+
validateResponse(method, path) {
|
|
651
|
+
const result = { isValid: true, reason: [] };
|
|
652
|
+
const operationObject = this.spec.paths[path][method];
|
|
653
|
+
const { json, multipleMediaType } = Utils.getResponseJson(operationObject);
|
|
654
|
+
// only support response body only contains “application/json” content type
|
|
655
|
+
if (multipleMediaType) {
|
|
656
|
+
result.reason.push(exports.ErrorType.ResponseContainMultipleMediaTypes);
|
|
657
|
+
}
|
|
658
|
+
else if (Object.keys(json).length === 0) {
|
|
659
|
+
// response body should not be empty
|
|
660
|
+
result.reason.push(exports.ErrorType.ResponseJsonIsEmpty);
|
|
661
|
+
}
|
|
662
|
+
return result;
|
|
663
|
+
}
|
|
664
|
+
validateServer(method, path) {
|
|
665
|
+
const result = { isValid: true, reason: [] };
|
|
666
|
+
const serverObj = Utils.getServerObject(this.spec, method, path);
|
|
667
|
+
if (!serverObj) {
|
|
668
|
+
// should contain server URL
|
|
669
|
+
result.reason.push(exports.ErrorType.NoServerInformation);
|
|
670
|
+
}
|
|
671
|
+
else {
|
|
672
|
+
// server url should be absolute url with https protocol
|
|
673
|
+
const serverValidateResult = Utils.checkServerUrl([serverObj]);
|
|
674
|
+
result.reason.push(...serverValidateResult.map((item) => item.type));
|
|
738
675
|
}
|
|
739
|
-
|
|
740
|
-
|
|
676
|
+
return result;
|
|
677
|
+
}
|
|
678
|
+
validateAuth(method, path) {
|
|
679
|
+
const pathObj = this.spec.paths[path];
|
|
680
|
+
const operationObject = pathObj[method];
|
|
681
|
+
const securities = operationObject.security;
|
|
682
|
+
const authSchemeArray = Utils.getAuthArray(securities, this.spec);
|
|
683
|
+
if (authSchemeArray.length === 0) {
|
|
684
|
+
return { isValid: true, reason: [] };
|
|
741
685
|
}
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
686
|
+
if (this.options.allowAPIKeyAuth ||
|
|
687
|
+
this.options.allowOauth2 ||
|
|
688
|
+
this.options.allowBearerTokenAuth) {
|
|
689
|
+
// Currently we don't support multiple auth in one operation
|
|
690
|
+
if (authSchemeArray.length > 0 && authSchemeArray.every((auths) => auths.length > 1)) {
|
|
691
|
+
return {
|
|
692
|
+
isValid: false,
|
|
693
|
+
reason: [exports.ErrorType.MultipleAuthNotSupported],
|
|
694
|
+
};
|
|
695
|
+
}
|
|
696
|
+
for (const auths of authSchemeArray) {
|
|
697
|
+
if (auths.length === 1) {
|
|
698
|
+
if ((this.options.allowAPIKeyAuth && Utils.isAPIKeyAuth(auths[0].authScheme)) ||
|
|
699
|
+
(this.options.allowOauth2 && Utils.isOAuthWithAuthCodeFlow(auths[0].authScheme)) ||
|
|
700
|
+
(this.options.allowBearerTokenAuth && Utils.isBearerTokenAuth(auths[0].authScheme))) {
|
|
701
|
+
return { isValid: true, reason: [] };
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
return { isValid: false, reason: [exports.ErrorType.AuthTypeIsNotSupported] };
|
|
707
|
+
}
|
|
708
|
+
checkPostBodySchema(schema, isRequired = false) {
|
|
709
|
+
var _a;
|
|
710
|
+
const paramResult = {
|
|
711
|
+
requiredNum: 0,
|
|
712
|
+
optionalNum: 0,
|
|
713
|
+
isValid: true,
|
|
714
|
+
reason: [],
|
|
746
715
|
};
|
|
716
|
+
if (Object.keys(schema).length === 0) {
|
|
717
|
+
return paramResult;
|
|
718
|
+
}
|
|
719
|
+
const isRequiredWithoutDefault = isRequired && schema.default === undefined;
|
|
720
|
+
const isCopilot = this.projectType === exports.ProjectType.Copilot;
|
|
721
|
+
if (isCopilot && this.hasNestedObjectInSchema(schema)) {
|
|
722
|
+
paramResult.isValid = false;
|
|
723
|
+
paramResult.reason = [exports.ErrorType.RequestBodyContainsNestedObject];
|
|
724
|
+
return paramResult;
|
|
725
|
+
}
|
|
726
|
+
if (schema.type === "string" ||
|
|
727
|
+
schema.type === "integer" ||
|
|
728
|
+
schema.type === "boolean" ||
|
|
729
|
+
schema.type === "number") {
|
|
730
|
+
if (isRequiredWithoutDefault) {
|
|
731
|
+
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
732
|
+
}
|
|
733
|
+
else {
|
|
734
|
+
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
else if (schema.type === "object") {
|
|
738
|
+
const { properties } = schema;
|
|
739
|
+
for (const property in properties) {
|
|
740
|
+
let isRequired = false;
|
|
741
|
+
if (schema.required && ((_a = schema.required) === null || _a === void 0 ? void 0 : _a.indexOf(property)) >= 0) {
|
|
742
|
+
isRequired = true;
|
|
743
|
+
}
|
|
744
|
+
const result = this.checkPostBodySchema(properties[property], isRequired);
|
|
745
|
+
paramResult.requiredNum += result.requiredNum;
|
|
746
|
+
paramResult.optionalNum += result.optionalNum;
|
|
747
|
+
paramResult.isValid = paramResult.isValid && result.isValid;
|
|
748
|
+
paramResult.reason.push(...result.reason);
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
else {
|
|
752
|
+
if (isRequiredWithoutDefault && !isCopilot) {
|
|
753
|
+
paramResult.isValid = false;
|
|
754
|
+
paramResult.reason.push(exports.ErrorType.PostBodyContainsRequiredUnsupportedSchema);
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
return paramResult;
|
|
747
758
|
}
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
759
|
+
checkParamSchema(paramObject) {
|
|
760
|
+
const paramResult = {
|
|
761
|
+
requiredNum: 0,
|
|
762
|
+
optionalNum: 0,
|
|
763
|
+
isValid: true,
|
|
764
|
+
reason: [],
|
|
765
|
+
};
|
|
766
|
+
if (!paramObject) {
|
|
767
|
+
return paramResult;
|
|
768
|
+
}
|
|
769
|
+
const isCopilot = this.projectType === exports.ProjectType.Copilot;
|
|
770
|
+
for (let i = 0; i < paramObject.length; i++) {
|
|
771
|
+
const param = paramObject[i];
|
|
772
|
+
const schema = param.schema;
|
|
773
|
+
if (isCopilot && this.hasNestedObjectInSchema(schema)) {
|
|
774
|
+
paramResult.isValid = false;
|
|
775
|
+
paramResult.reason.push(exports.ErrorType.ParamsContainsNestedObject);
|
|
776
|
+
continue;
|
|
777
|
+
}
|
|
778
|
+
const isRequiredWithoutDefault = param.required && schema.default === undefined;
|
|
779
|
+
if (isCopilot) {
|
|
780
|
+
if (isRequiredWithoutDefault) {
|
|
781
|
+
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
782
|
+
}
|
|
783
|
+
else {
|
|
784
|
+
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
785
|
+
}
|
|
786
|
+
continue;
|
|
787
|
+
}
|
|
788
|
+
if (param.in === "header" || param.in === "cookie") {
|
|
789
|
+
if (isRequiredWithoutDefault) {
|
|
790
|
+
paramResult.isValid = false;
|
|
791
|
+
paramResult.reason.push(exports.ErrorType.ParamsContainRequiredUnsupportedSchema);
|
|
792
|
+
}
|
|
793
|
+
continue;
|
|
794
|
+
}
|
|
795
|
+
if (schema.type !== "boolean" &&
|
|
796
|
+
schema.type !== "string" &&
|
|
797
|
+
schema.type !== "number" &&
|
|
798
|
+
schema.type !== "integer") {
|
|
799
|
+
if (isRequiredWithoutDefault) {
|
|
800
|
+
paramResult.isValid = false;
|
|
801
|
+
paramResult.reason.push(exports.ErrorType.ParamsContainRequiredUnsupportedSchema);
|
|
802
|
+
}
|
|
803
|
+
continue;
|
|
804
|
+
}
|
|
805
|
+
if (param.in === "query" || param.in === "path") {
|
|
806
|
+
if (isRequiredWithoutDefault) {
|
|
807
|
+
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
808
|
+
}
|
|
809
|
+
else {
|
|
810
|
+
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
return paramResult;
|
|
754
815
|
}
|
|
755
|
-
|
|
756
|
-
if (
|
|
757
|
-
|
|
816
|
+
hasNestedObjectInSchema(schema) {
|
|
817
|
+
if (schema.type === "object") {
|
|
818
|
+
for (const property in schema.properties) {
|
|
819
|
+
const nestedSchema = schema.properties[property];
|
|
820
|
+
if (nestedSchema.type === "object") {
|
|
821
|
+
return true;
|
|
822
|
+
}
|
|
823
|
+
}
|
|
758
824
|
}
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
825
|
+
return false;
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
// Copyright (c) Microsoft Corporation.
|
|
830
|
+
class CopilotValidator extends Validator {
|
|
831
|
+
constructor(spec, options) {
|
|
832
|
+
super();
|
|
833
|
+
this.projectType = exports.ProjectType.Copilot;
|
|
834
|
+
this.options = options;
|
|
835
|
+
this.spec = spec;
|
|
836
|
+
}
|
|
837
|
+
validateSpec() {
|
|
838
|
+
const result = { errors: [], warnings: [] };
|
|
839
|
+
// validate spec version
|
|
840
|
+
let validationResult = this.validateSpecVersion();
|
|
841
|
+
result.errors.push(...validationResult.errors);
|
|
842
|
+
// validate spec server
|
|
843
|
+
validationResult = this.validateSpecServer();
|
|
844
|
+
result.errors.push(...validationResult.errors);
|
|
845
|
+
// validate no supported API
|
|
846
|
+
validationResult = this.validateSpecNoSupportAPI();
|
|
847
|
+
result.errors.push(...validationResult.errors);
|
|
848
|
+
// validate operationId missing
|
|
849
|
+
validationResult = this.validateSpecOperationId();
|
|
850
|
+
result.warnings.push(...validationResult.warnings);
|
|
851
|
+
return result;
|
|
852
|
+
}
|
|
853
|
+
validateAPI(method, path) {
|
|
854
|
+
const result = { isValid: true, reason: [] };
|
|
855
|
+
method = method.toLocaleLowerCase();
|
|
856
|
+
// validate method and path
|
|
857
|
+
const methodAndPathResult = this.validateMethodAndPath(method, path);
|
|
858
|
+
if (!methodAndPathResult.isValid) {
|
|
859
|
+
return methodAndPathResult;
|
|
860
|
+
}
|
|
861
|
+
const operationObject = this.spec.paths[path][method];
|
|
862
|
+
// validate auth
|
|
863
|
+
const authCheckResult = this.validateAuth(method, path);
|
|
864
|
+
result.reason.push(...authCheckResult.reason);
|
|
865
|
+
// validate operationId
|
|
866
|
+
if (!this.options.allowMissingId && !operationObject.operationId) {
|
|
867
|
+
result.reason.push(exports.ErrorType.MissingOperationId);
|
|
868
|
+
}
|
|
869
|
+
// validate server
|
|
870
|
+
const validateServerResult = this.validateServer(method, path);
|
|
871
|
+
result.reason.push(...validateServerResult.reason);
|
|
872
|
+
// validate response
|
|
873
|
+
const validateResponseResult = this.validateResponse(method, path);
|
|
874
|
+
result.reason.push(...validateResponseResult.reason);
|
|
875
|
+
// validate requestBody
|
|
876
|
+
const requestBody = operationObject.requestBody;
|
|
877
|
+
const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
|
|
878
|
+
if (Utils.containMultipleMediaTypes(requestBody)) {
|
|
879
|
+
result.reason.push(exports.ErrorType.PostBodyContainMultipleMediaTypes);
|
|
880
|
+
}
|
|
881
|
+
if (requestJsonBody) {
|
|
882
|
+
const requestBodySchema = requestJsonBody.schema;
|
|
883
|
+
if (requestBodySchema.type !== "object") {
|
|
884
|
+
result.reason.push(exports.ErrorType.PostBodySchemaIsNotJson);
|
|
885
|
+
}
|
|
886
|
+
const requestBodyParamResult = this.checkPostBodySchema(requestBodySchema, requestBody.required);
|
|
887
|
+
result.reason.push(...requestBodyParamResult.reason);
|
|
888
|
+
}
|
|
889
|
+
// validate parameters
|
|
890
|
+
const paramObject = operationObject.parameters;
|
|
891
|
+
const paramResult = this.checkParamSchema(paramObject);
|
|
892
|
+
result.reason.push(...paramResult.reason);
|
|
893
|
+
if (result.reason.length > 0) {
|
|
894
|
+
result.isValid = false;
|
|
895
|
+
}
|
|
896
|
+
return result;
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
// Copyright (c) Microsoft Corporation.
|
|
901
|
+
class SMEValidator extends Validator {
|
|
902
|
+
constructor(spec, options) {
|
|
903
|
+
super();
|
|
904
|
+
this.projectType = exports.ProjectType.SME;
|
|
905
|
+
this.options = options;
|
|
906
|
+
this.spec = spec;
|
|
907
|
+
}
|
|
908
|
+
validateSpec() {
|
|
909
|
+
const result = { errors: [], warnings: [] };
|
|
910
|
+
// validate spec version
|
|
911
|
+
let validationResult = this.validateSpecVersion();
|
|
912
|
+
result.errors.push(...validationResult.errors);
|
|
913
|
+
// validate spec server
|
|
914
|
+
validationResult = this.validateSpecServer();
|
|
915
|
+
result.errors.push(...validationResult.errors);
|
|
916
|
+
// validate no supported API
|
|
917
|
+
validationResult = this.validateSpecNoSupportAPI();
|
|
918
|
+
result.errors.push(...validationResult.errors);
|
|
919
|
+
// validate operationId missing
|
|
920
|
+
if (this.options.allowMissingId) {
|
|
921
|
+
validationResult = this.validateSpecOperationId();
|
|
922
|
+
result.warnings.push(...validationResult.warnings);
|
|
923
|
+
}
|
|
924
|
+
return result;
|
|
925
|
+
}
|
|
926
|
+
validateAPI(method, path) {
|
|
927
|
+
const result = { isValid: true, reason: [] };
|
|
928
|
+
method = method.toLocaleLowerCase();
|
|
929
|
+
// validate method and path
|
|
930
|
+
const methodAndPathResult = this.validateMethodAndPath(method, path);
|
|
931
|
+
if (!methodAndPathResult.isValid) {
|
|
932
|
+
return methodAndPathResult;
|
|
933
|
+
}
|
|
934
|
+
const operationObject = this.spec.paths[path][method];
|
|
935
|
+
// validate auth
|
|
936
|
+
const authCheckResult = this.validateAuth(method, path);
|
|
937
|
+
result.reason.push(...authCheckResult.reason);
|
|
938
|
+
// validate operationId
|
|
939
|
+
if (!this.options.allowMissingId && !operationObject.operationId) {
|
|
940
|
+
result.reason.push(exports.ErrorType.MissingOperationId);
|
|
941
|
+
}
|
|
942
|
+
// validate server
|
|
943
|
+
const validateServerResult = this.validateServer(method, path);
|
|
944
|
+
result.reason.push(...validateServerResult.reason);
|
|
945
|
+
// validate response
|
|
946
|
+
const validateResponseResult = this.validateResponse(method, path);
|
|
947
|
+
result.reason.push(...validateResponseResult.reason);
|
|
948
|
+
let postBodyResult = {
|
|
949
|
+
requiredNum: 0,
|
|
950
|
+
optionalNum: 0,
|
|
951
|
+
isValid: true,
|
|
952
|
+
reason: [],
|
|
953
|
+
};
|
|
954
|
+
// validate requestBody
|
|
955
|
+
const requestBody = operationObject.requestBody;
|
|
956
|
+
const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
|
|
957
|
+
if (Utils.containMultipleMediaTypes(requestBody)) {
|
|
958
|
+
result.reason.push(exports.ErrorType.PostBodyContainMultipleMediaTypes);
|
|
959
|
+
}
|
|
960
|
+
if (requestJsonBody) {
|
|
961
|
+
const requestBodySchema = requestJsonBody.schema;
|
|
962
|
+
postBodyResult = this.checkPostBodySchema(requestBodySchema, requestBody.required);
|
|
963
|
+
result.reason.push(...postBodyResult.reason);
|
|
964
|
+
}
|
|
965
|
+
// validate parameters
|
|
966
|
+
const paramObject = operationObject.parameters;
|
|
967
|
+
const paramResult = this.checkParamSchema(paramObject);
|
|
968
|
+
result.reason.push(...paramResult.reason);
|
|
969
|
+
// validate total parameters count
|
|
970
|
+
if (paramResult.isValid && postBodyResult.isValid) {
|
|
971
|
+
const paramCountResult = this.validateParamCount(postBodyResult, paramResult);
|
|
972
|
+
result.reason.push(...paramCountResult.reason);
|
|
973
|
+
}
|
|
974
|
+
if (result.reason.length > 0) {
|
|
975
|
+
result.isValid = false;
|
|
976
|
+
}
|
|
977
|
+
return result;
|
|
978
|
+
}
|
|
979
|
+
validateParamCount(postBodyResult, paramResult) {
|
|
980
|
+
const result = { isValid: true, reason: [] };
|
|
981
|
+
const totalRequiredParams = postBodyResult.requiredNum + paramResult.requiredNum;
|
|
982
|
+
const totalParams = totalRequiredParams + postBodyResult.optionalNum + paramResult.optionalNum;
|
|
983
|
+
if (totalRequiredParams > 1) {
|
|
984
|
+
if (!this.options.allowMultipleParameters ||
|
|
985
|
+
totalRequiredParams > SMEValidator.SMERequiredParamsMaxNum) {
|
|
986
|
+
result.reason.push(exports.ErrorType.ExceededRequiredParamsLimit);
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
else if (totalParams === 0) {
|
|
990
|
+
result.reason.push(exports.ErrorType.NoParameter);
|
|
991
|
+
}
|
|
992
|
+
return result;
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
SMEValidator.SMERequiredParamsMaxNum = 5;
|
|
996
|
+
|
|
997
|
+
// Copyright (c) Microsoft Corporation.
|
|
998
|
+
class TeamsAIValidator extends Validator {
|
|
999
|
+
constructor(spec, options) {
|
|
1000
|
+
super();
|
|
1001
|
+
this.projectType = exports.ProjectType.TeamsAi;
|
|
1002
|
+
this.options = options;
|
|
1003
|
+
this.spec = spec;
|
|
1004
|
+
}
|
|
1005
|
+
validateSpec() {
|
|
1006
|
+
const result = { errors: [], warnings: [] };
|
|
1007
|
+
// validate spec server
|
|
1008
|
+
let validationResult = this.validateSpecServer();
|
|
1009
|
+
result.errors.push(...validationResult.errors);
|
|
1010
|
+
// validate no supported API
|
|
1011
|
+
validationResult = this.validateSpecNoSupportAPI();
|
|
1012
|
+
result.errors.push(...validationResult.errors);
|
|
1013
|
+
return result;
|
|
1014
|
+
}
|
|
1015
|
+
validateAPI(method, path) {
|
|
1016
|
+
const result = { isValid: true, reason: [] };
|
|
1017
|
+
method = method.toLocaleLowerCase();
|
|
1018
|
+
// validate method and path
|
|
1019
|
+
const methodAndPathResult = this.validateMethodAndPath(method, path);
|
|
1020
|
+
if (!methodAndPathResult.isValid) {
|
|
1021
|
+
return methodAndPathResult;
|
|
1022
|
+
}
|
|
1023
|
+
const operationObject = this.spec.paths[path][method];
|
|
1024
|
+
// validate operationId
|
|
1025
|
+
if (!this.options.allowMissingId && !operationObject.operationId) {
|
|
1026
|
+
result.reason.push(exports.ErrorType.MissingOperationId);
|
|
1027
|
+
}
|
|
1028
|
+
// validate server
|
|
1029
|
+
const validateServerResult = this.validateServer(method, path);
|
|
1030
|
+
result.reason.push(...validateServerResult.reason);
|
|
1031
|
+
if (result.reason.length > 0) {
|
|
1032
|
+
result.isValid = false;
|
|
1033
|
+
}
|
|
1034
|
+
return result;
|
|
1035
|
+
}
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
class ValidatorFactory {
|
|
1039
|
+
static create(spec, options) {
|
|
1040
|
+
var _a;
|
|
1041
|
+
const type = (_a = options.projectType) !== null && _a !== void 0 ? _a : exports.ProjectType.SME;
|
|
1042
|
+
switch (type) {
|
|
1043
|
+
case exports.ProjectType.SME:
|
|
1044
|
+
return new SMEValidator(spec, options);
|
|
1045
|
+
case exports.ProjectType.Copilot:
|
|
1046
|
+
return new CopilotValidator(spec, options);
|
|
1047
|
+
case exports.ProjectType.TeamsAi:
|
|
1048
|
+
return new TeamsAIValidator(spec, options);
|
|
1049
|
+
default:
|
|
1050
|
+
throw new Error(`Invalid project type: ${type}`);
|
|
762
1051
|
}
|
|
763
|
-
return safeRegistrationIdEnvName;
|
|
764
1052
|
}
|
|
765
1053
|
}
|
|
766
1054
|
|
|
767
1055
|
// Copyright (c) Microsoft Corporation.
|
|
768
1056
|
class SpecFilter {
|
|
769
|
-
static specFilter(filter, unResolveSpec, resolvedSpec,
|
|
1057
|
+
static specFilter(filter, unResolveSpec, resolvedSpec, options) {
|
|
1058
|
+
var _a;
|
|
770
1059
|
try {
|
|
771
1060
|
const newSpec = Object.assign({}, unResolveSpec);
|
|
772
1061
|
const newPaths = {};
|
|
773
1062
|
for (const filterItem of filter) {
|
|
774
1063
|
const [method, path] = filterItem.split(" ");
|
|
775
1064
|
const methodName = method.toLowerCase();
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
1065
|
+
const pathObj = (_a = resolvedSpec.paths) === null || _a === void 0 ? void 0 : _a[path];
|
|
1066
|
+
if (ConstantString.AllOperationMethods.includes(methodName) &&
|
|
1067
|
+
pathObj &&
|
|
1068
|
+
pathObj[methodName]) {
|
|
1069
|
+
const validator = ValidatorFactory.create(resolvedSpec, options);
|
|
1070
|
+
const validateResult = validator.validateAPI(methodName, path);
|
|
1071
|
+
if (!validateResult.isValid) {
|
|
1072
|
+
continue;
|
|
1073
|
+
}
|
|
1074
|
+
if (!newPaths[path]) {
|
|
1075
|
+
newPaths[path] = Object.assign({}, unResolveSpec.paths[path]);
|
|
1076
|
+
for (const m of ConstantString.AllOperationMethods) {
|
|
1077
|
+
delete newPaths[path][m];
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
newPaths[path][methodName] = unResolveSpec.paths[path][methodName];
|
|
1081
|
+
// Add the operationId if missing
|
|
1082
|
+
if (!newPaths[path][methodName].operationId) {
|
|
1083
|
+
newPaths[path][methodName].operationId = `${methodName}${Utils.convertPathToCamelCase(path)}`;
|
|
783
1084
|
}
|
|
784
|
-
}
|
|
785
|
-
newPaths[path][methodName] = unResolveSpec.paths[path][methodName];
|
|
786
|
-
// Add the operationId if missing
|
|
787
|
-
if (!newPaths[path][methodName].operationId) {
|
|
788
|
-
newPaths[path][methodName].operationId = `${methodName}${Utils.convertPathToCamelCase(path)}`;
|
|
789
1085
|
}
|
|
790
1086
|
}
|
|
791
1087
|
newSpec.paths = newPaths;
|
|
@@ -797,101 +1093,11 @@ class SpecFilter {
|
|
|
797
1093
|
}
|
|
798
1094
|
}
|
|
799
1095
|
|
|
800
|
-
// Copyright (c) Microsoft Corporation.
|
|
801
|
-
class ManifestUpdater {
|
|
802
|
-
static updateManifest(manifestPath, outputSpecPath, adaptiveCardFolder, spec, allowMultipleParameters, auth, isMe) {
|
|
803
|
-
var _a, _b;
|
|
804
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
805
|
-
try {
|
|
806
|
-
const originalManifest = yield fs__default['default'].readJSON(manifestPath);
|
|
807
|
-
const updatedPart = {};
|
|
808
|
-
const [commands, warnings] = yield ManifestUpdater.generateCommands(spec, adaptiveCardFolder, manifestPath, allowMultipleParameters);
|
|
809
|
-
const composeExtension = {
|
|
810
|
-
composeExtensionType: "apiBased",
|
|
811
|
-
apiSpecificationFile: ManifestUpdater.getRelativePath(manifestPath, outputSpecPath),
|
|
812
|
-
commands: commands,
|
|
813
|
-
};
|
|
814
|
-
if (auth) {
|
|
815
|
-
if (Utils.isAPIKeyAuth(auth)) {
|
|
816
|
-
auth = auth;
|
|
817
|
-
const safeApiSecretRegistrationId = Utils.getSafeRegistrationIdEnvName(`${auth.name}_${ConstantString.RegistrationIdPostfix}`);
|
|
818
|
-
composeExtension.authorization = {
|
|
819
|
-
authType: "apiSecretServiceAuth",
|
|
820
|
-
apiSecretServiceAuthConfiguration: {
|
|
821
|
-
apiSecretRegistrationId: `\${{${safeApiSecretRegistrationId}}}`,
|
|
822
|
-
},
|
|
823
|
-
};
|
|
824
|
-
}
|
|
825
|
-
else if (Utils.isBearerTokenAuth(auth)) {
|
|
826
|
-
composeExtension.authorization = {
|
|
827
|
-
authType: "microsoftEntra",
|
|
828
|
-
microsoftEntraConfiguration: {
|
|
829
|
-
supportsSingleSignOn: true,
|
|
830
|
-
},
|
|
831
|
-
};
|
|
832
|
-
updatedPart.webApplicationInfo = {
|
|
833
|
-
id: "${{AAD_APP_CLIENT_ID}}",
|
|
834
|
-
resource: "api://${{DOMAIN}}/${{AAD_APP_CLIENT_ID}}",
|
|
835
|
-
};
|
|
836
|
-
}
|
|
837
|
-
}
|
|
838
|
-
updatedPart.description = {
|
|
839
|
-
short: spec.info.title.slice(0, ConstantString.ShortDescriptionMaxLens),
|
|
840
|
-
full: (_b = ((_a = spec.info.description) !== null && _a !== void 0 ? _a : originalManifest.description.full)) === null || _b === void 0 ? void 0 : _b.slice(0, ConstantString.FullDescriptionMaxLens),
|
|
841
|
-
};
|
|
842
|
-
updatedPart.composeExtensions = isMe === undefined || isMe === true ? [composeExtension] : [];
|
|
843
|
-
const updatedManifest = Object.assign(Object.assign({}, originalManifest), updatedPart);
|
|
844
|
-
return [updatedManifest, warnings];
|
|
845
|
-
}
|
|
846
|
-
catch (err) {
|
|
847
|
-
throw new SpecParserError(err.toString(), exports.ErrorType.UpdateManifestFailed);
|
|
848
|
-
}
|
|
849
|
-
});
|
|
850
|
-
}
|
|
851
|
-
static generateCommands(spec, adaptiveCardFolder, manifestPath, allowMultipleParameters) {
|
|
852
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
853
|
-
const paths = spec.paths;
|
|
854
|
-
const commands = [];
|
|
855
|
-
const warnings = [];
|
|
856
|
-
if (paths) {
|
|
857
|
-
for (const pathUrl in paths) {
|
|
858
|
-
const pathItem = paths[pathUrl];
|
|
859
|
-
if (pathItem) {
|
|
860
|
-
const operations = pathItem;
|
|
861
|
-
// Currently only support GET and POST method
|
|
862
|
-
for (const method in operations) {
|
|
863
|
-
if (method === ConstantString.PostMethod || method === ConstantString.GetMethod) {
|
|
864
|
-
const operationItem = operations[method];
|
|
865
|
-
if (operationItem) {
|
|
866
|
-
const [command, warning] = Utils.parseApiInfo(operationItem, allowMultipleParameters);
|
|
867
|
-
const adaptiveCardPath = path__default['default'].join(adaptiveCardFolder, command.id + ".json");
|
|
868
|
-
command.apiResponseRenderingTemplateFile = (yield fs__default['default'].pathExists(adaptiveCardPath))
|
|
869
|
-
? ManifestUpdater.getRelativePath(manifestPath, adaptiveCardPath)
|
|
870
|
-
: "";
|
|
871
|
-
if (warning) {
|
|
872
|
-
warnings.push(warning);
|
|
873
|
-
}
|
|
874
|
-
commands.push(command);
|
|
875
|
-
}
|
|
876
|
-
}
|
|
877
|
-
}
|
|
878
|
-
}
|
|
879
|
-
}
|
|
880
|
-
}
|
|
881
|
-
return [commands, warnings];
|
|
882
|
-
});
|
|
883
|
-
}
|
|
884
|
-
static getRelativePath(from, to) {
|
|
885
|
-
const relativePath = path__default['default'].relative(path__default['default'].dirname(from), to);
|
|
886
|
-
return path__default['default'].normalize(relativePath).replace(/\\/g, "/");
|
|
887
|
-
}
|
|
888
|
-
}
|
|
889
|
-
|
|
890
1096
|
// Copyright (c) Microsoft Corporation.
|
|
891
1097
|
class AdaptiveCardGenerator {
|
|
892
1098
|
static generateAdaptiveCard(operationItem) {
|
|
893
1099
|
try {
|
|
894
|
-
const json = Utils.getResponseJson(operationItem);
|
|
1100
|
+
const { json } = Utils.getResponseJson(operationItem);
|
|
895
1101
|
let cardBody = [];
|
|
896
1102
|
let schema = json.schema;
|
|
897
1103
|
let jsonPath = "$";
|
|
@@ -1057,6 +1263,27 @@ function wrapAdaptiveCard(card, jsonPath) {
|
|
|
1057
1263
|
};
|
|
1058
1264
|
return result;
|
|
1059
1265
|
}
|
|
1266
|
+
function wrapResponseSemantics(card, jsonPath) {
|
|
1267
|
+
const props = inferProperties(card);
|
|
1268
|
+
const dataPath = jsonPath === "$" ? "$" : "$." + jsonPath;
|
|
1269
|
+
const result = {
|
|
1270
|
+
data_path: dataPath,
|
|
1271
|
+
};
|
|
1272
|
+
if (props.title || props.subtitle || props.imageUrl) {
|
|
1273
|
+
result.properties = {};
|
|
1274
|
+
if (props.title) {
|
|
1275
|
+
result.properties.title = "$." + props.title;
|
|
1276
|
+
}
|
|
1277
|
+
if (props.subtitle) {
|
|
1278
|
+
result.properties.subtitle = "$." + props.subtitle;
|
|
1279
|
+
}
|
|
1280
|
+
if (props.imageUrl) {
|
|
1281
|
+
result.properties.url = "$." + props.imageUrl;
|
|
1282
|
+
}
|
|
1283
|
+
}
|
|
1284
|
+
result.static_template = card;
|
|
1285
|
+
return result;
|
|
1286
|
+
}
|
|
1060
1287
|
/**
|
|
1061
1288
|
* Infers the preview card template from an Adaptive Card and a JSON path.
|
|
1062
1289
|
* The preview card template includes a title and an optional subtitle and image.
|
|
@@ -1069,11 +1296,29 @@ function wrapAdaptiveCard(card, jsonPath) {
|
|
|
1069
1296
|
* @returns The inferred preview card template.
|
|
1070
1297
|
*/
|
|
1071
1298
|
function inferPreviewCardTemplate(card) {
|
|
1072
|
-
var _a;
|
|
1073
1299
|
const result = {
|
|
1074
|
-
title: "",
|
|
1300
|
+
title: "result",
|
|
1075
1301
|
};
|
|
1076
|
-
const
|
|
1302
|
+
const inferredProperties = inferProperties(card);
|
|
1303
|
+
if (inferredProperties.title) {
|
|
1304
|
+
result.title = `\${if(${inferredProperties.title}, ${inferredProperties.title}, 'N/A')}`;
|
|
1305
|
+
}
|
|
1306
|
+
if (inferredProperties.subtitle) {
|
|
1307
|
+
result.subtitle = `\${if(${inferredProperties.subtitle}, ${inferredProperties.subtitle}, 'N/A')}`;
|
|
1308
|
+
}
|
|
1309
|
+
if (inferredProperties.imageUrl) {
|
|
1310
|
+
result.image = {
|
|
1311
|
+
url: `\${${inferredProperties.imageUrl}}`,
|
|
1312
|
+
alt: `\${if(${inferredProperties.imageUrl}, ${inferredProperties.imageUrl}, 'N/A')}`,
|
|
1313
|
+
$when: `\${${inferredProperties.imageUrl} != null}`,
|
|
1314
|
+
};
|
|
1315
|
+
}
|
|
1316
|
+
return result;
|
|
1317
|
+
}
|
|
1318
|
+
function inferProperties(card) {
|
|
1319
|
+
var _a;
|
|
1320
|
+
const result = {};
|
|
1321
|
+
const nameSet = new Set();
|
|
1077
1322
|
let rootObject;
|
|
1078
1323
|
if (((_a = card.body[0]) === null || _a === void 0 ? void 0 : _a.type) === ConstantString.ContainerType) {
|
|
1079
1324
|
rootObject = card.body[0].items;
|
|
@@ -1086,56 +1331,346 @@ function inferPreviewCardTemplate(card) {
|
|
|
1086
1331
|
const textElement = element;
|
|
1087
1332
|
const index = textElement.text.indexOf("${if(");
|
|
1088
1333
|
if (index > 0) {
|
|
1089
|
-
|
|
1090
|
-
|
|
1334
|
+
const text = textElement.text.substring(index);
|
|
1335
|
+
const match = text.match(/\${if\(([^,]+),/);
|
|
1336
|
+
const property = match ? match[1] : "";
|
|
1337
|
+
if (property) {
|
|
1338
|
+
nameSet.add(property);
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1342
|
+
else if (element.type === ConstantString.ImageType) {
|
|
1343
|
+
const imageElement = element;
|
|
1344
|
+
const match = imageElement.url.match(/\${([^,]+)}/);
|
|
1345
|
+
const property = match ? match[1] : "";
|
|
1346
|
+
if (property) {
|
|
1347
|
+
nameSet.add(property);
|
|
1091
1348
|
}
|
|
1092
1349
|
}
|
|
1093
1350
|
}
|
|
1094
|
-
for (const
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
textBlockElements.delete(element);
|
|
1351
|
+
for (const name of nameSet) {
|
|
1352
|
+
if (!result.title && Utils.isWellKnownName(name, ConstantString.WellknownTitleName)) {
|
|
1353
|
+
result.title = name;
|
|
1354
|
+
nameSet.delete(name);
|
|
1099
1355
|
}
|
|
1100
1356
|
else if (!result.subtitle &&
|
|
1101
|
-
Utils.isWellKnownName(
|
|
1102
|
-
result.subtitle =
|
|
1103
|
-
|
|
1357
|
+
Utils.isWellKnownName(name, ConstantString.WellknownSubtitleName)) {
|
|
1358
|
+
result.subtitle = name;
|
|
1359
|
+
nameSet.delete(name);
|
|
1104
1360
|
}
|
|
1105
|
-
else if (!result.
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
if (property) {
|
|
1109
|
-
result.image = {
|
|
1110
|
-
url: `\${${property}}`,
|
|
1111
|
-
alt: text,
|
|
1112
|
-
$when: `\${${property} != null}`,
|
|
1113
|
-
};
|
|
1114
|
-
}
|
|
1115
|
-
textBlockElements.delete(element);
|
|
1361
|
+
else if (!result.imageUrl && Utils.isWellKnownName(name, ConstantString.WellknownImageName)) {
|
|
1362
|
+
result.imageUrl = name;
|
|
1363
|
+
nameSet.delete(name);
|
|
1116
1364
|
}
|
|
1117
1365
|
}
|
|
1118
|
-
for (const
|
|
1119
|
-
const text = element.text;
|
|
1366
|
+
for (const name of nameSet) {
|
|
1120
1367
|
if (!result.title) {
|
|
1121
|
-
result.title =
|
|
1122
|
-
|
|
1368
|
+
result.title = name;
|
|
1369
|
+
nameSet.delete(name);
|
|
1123
1370
|
}
|
|
1124
1371
|
else if (!result.subtitle) {
|
|
1125
|
-
result.subtitle =
|
|
1126
|
-
|
|
1372
|
+
result.subtitle = name;
|
|
1373
|
+
nameSet.delete(name);
|
|
1127
1374
|
}
|
|
1128
1375
|
}
|
|
1129
1376
|
if (!result.title && result.subtitle) {
|
|
1130
1377
|
result.title = result.subtitle;
|
|
1131
1378
|
delete result.subtitle;
|
|
1132
1379
|
}
|
|
1133
|
-
if (!result.title) {
|
|
1134
|
-
result.title = "result";
|
|
1135
|
-
}
|
|
1136
1380
|
return result;
|
|
1137
1381
|
}
|
|
1138
1382
|
|
|
1383
|
+
// Copyright (c) Microsoft Corporation.
|
|
1384
|
+
class ManifestUpdater {
|
|
1385
|
+
static updateManifestWithAiPlugin(manifestPath, outputSpecPath, apiPluginFilePath, spec, options) {
|
|
1386
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1387
|
+
const manifest = yield fs__default['default'].readJSON(manifestPath);
|
|
1388
|
+
const apiPluginRelativePath = ManifestUpdater.getRelativePath(manifestPath, apiPluginFilePath);
|
|
1389
|
+
manifest.plugins = [
|
|
1390
|
+
{
|
|
1391
|
+
file: apiPluginRelativePath,
|
|
1392
|
+
id: ConstantString.DefaultPluginId,
|
|
1393
|
+
},
|
|
1394
|
+
];
|
|
1395
|
+
const appName = this.removeEnvs(manifest.name.short);
|
|
1396
|
+
ManifestUpdater.updateManifestDescription(manifest, spec);
|
|
1397
|
+
const specRelativePath = ManifestUpdater.getRelativePath(manifestPath, outputSpecPath);
|
|
1398
|
+
const apiPlugin = yield ManifestUpdater.generatePluginManifestSchema(spec, specRelativePath, apiPluginFilePath, appName, options);
|
|
1399
|
+
return [manifest, apiPlugin];
|
|
1400
|
+
});
|
|
1401
|
+
}
|
|
1402
|
+
static updateManifestDescription(manifest, spec) {
|
|
1403
|
+
var _a, _b;
|
|
1404
|
+
manifest.description = {
|
|
1405
|
+
short: spec.info.title.slice(0, ConstantString.ShortDescriptionMaxLens),
|
|
1406
|
+
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),
|
|
1407
|
+
};
|
|
1408
|
+
}
|
|
1409
|
+
static mapOpenAPISchemaToFuncParam(schema, method, pathUrl) {
|
|
1410
|
+
let parameter;
|
|
1411
|
+
if (schema.type === "string" ||
|
|
1412
|
+
schema.type === "boolean" ||
|
|
1413
|
+
schema.type === "integer" ||
|
|
1414
|
+
schema.type === "number" ||
|
|
1415
|
+
schema.type === "array") {
|
|
1416
|
+
parameter = schema;
|
|
1417
|
+
}
|
|
1418
|
+
else {
|
|
1419
|
+
throw new SpecParserError(Utils.format(ConstantString.UnsupportedSchema, method, pathUrl, JSON.stringify(schema)), exports.ErrorType.UpdateManifestFailed);
|
|
1420
|
+
}
|
|
1421
|
+
return parameter;
|
|
1422
|
+
}
|
|
1423
|
+
static generatePluginManifestSchema(spec, specRelativePath, apiPluginFilePath, appName, options) {
|
|
1424
|
+
var _a, _b, _c, _d;
|
|
1425
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1426
|
+
const functions = [];
|
|
1427
|
+
const functionNames = [];
|
|
1428
|
+
const conversationStarters = [];
|
|
1429
|
+
const paths = spec.paths;
|
|
1430
|
+
for (const pathUrl in paths) {
|
|
1431
|
+
const pathItem = paths[pathUrl];
|
|
1432
|
+
if (pathItem) {
|
|
1433
|
+
const operations = pathItem;
|
|
1434
|
+
for (const method in operations) {
|
|
1435
|
+
if (options.allowMethods.includes(method)) {
|
|
1436
|
+
const operationItem = operations[method];
|
|
1437
|
+
if (operationItem) {
|
|
1438
|
+
const operationId = operationItem.operationId;
|
|
1439
|
+
const description = (_a = operationItem.description) !== null && _a !== void 0 ? _a : "";
|
|
1440
|
+
const paramObject = operationItem.parameters;
|
|
1441
|
+
const requestBody = operationItem.requestBody;
|
|
1442
|
+
const parameters = {
|
|
1443
|
+
type: "object",
|
|
1444
|
+
properties: {},
|
|
1445
|
+
required: [],
|
|
1446
|
+
};
|
|
1447
|
+
if (paramObject) {
|
|
1448
|
+
for (let i = 0; i < paramObject.length; i++) {
|
|
1449
|
+
const param = paramObject[i];
|
|
1450
|
+
const schema = param.schema;
|
|
1451
|
+
parameters.properties[param.name] = ManifestUpdater.mapOpenAPISchemaToFuncParam(schema, method, pathUrl);
|
|
1452
|
+
if (param.required) {
|
|
1453
|
+
parameters.required.push(param.name);
|
|
1454
|
+
}
|
|
1455
|
+
if (!parameters.properties[param.name].description) {
|
|
1456
|
+
parameters.properties[param.name].description = (_b = param.description) !== null && _b !== void 0 ? _b : "";
|
|
1457
|
+
}
|
|
1458
|
+
}
|
|
1459
|
+
}
|
|
1460
|
+
if (requestBody) {
|
|
1461
|
+
const requestJsonBody = requestBody.content["application/json"];
|
|
1462
|
+
const requestBodySchema = requestJsonBody.schema;
|
|
1463
|
+
if (requestBodySchema.type === "object") {
|
|
1464
|
+
if (requestBodySchema.required) {
|
|
1465
|
+
parameters.required.push(...requestBodySchema.required);
|
|
1466
|
+
}
|
|
1467
|
+
for (const property in requestBodySchema.properties) {
|
|
1468
|
+
const schema = requestBodySchema.properties[property];
|
|
1469
|
+
parameters.properties[property] = ManifestUpdater.mapOpenAPISchemaToFuncParam(schema, method, pathUrl);
|
|
1470
|
+
}
|
|
1471
|
+
}
|
|
1472
|
+
else {
|
|
1473
|
+
throw new SpecParserError(Utils.format(ConstantString.UnsupportedSchema, method, pathUrl, JSON.stringify(requestBodySchema)), exports.ErrorType.UpdateManifestFailed);
|
|
1474
|
+
}
|
|
1475
|
+
}
|
|
1476
|
+
const funcObj = {
|
|
1477
|
+
name: operationId,
|
|
1478
|
+
description: description,
|
|
1479
|
+
parameters: parameters,
|
|
1480
|
+
};
|
|
1481
|
+
if (options.allowResponseSemantics) {
|
|
1482
|
+
const [card, jsonPath] = AdaptiveCardGenerator.generateAdaptiveCard(operationItem);
|
|
1483
|
+
const responseSemantic = wrapResponseSemantics(card, jsonPath);
|
|
1484
|
+
funcObj.capabilities = {
|
|
1485
|
+
response_semantics: responseSemantic,
|
|
1486
|
+
};
|
|
1487
|
+
}
|
|
1488
|
+
functions.push(funcObj);
|
|
1489
|
+
functionNames.push(operationId);
|
|
1490
|
+
if (description) {
|
|
1491
|
+
conversationStarters.push(description);
|
|
1492
|
+
}
|
|
1493
|
+
}
|
|
1494
|
+
}
|
|
1495
|
+
}
|
|
1496
|
+
}
|
|
1497
|
+
}
|
|
1498
|
+
let apiPlugin;
|
|
1499
|
+
if (yield fs__default['default'].pathExists(apiPluginFilePath)) {
|
|
1500
|
+
apiPlugin = yield fs__default['default'].readJSON(apiPluginFilePath);
|
|
1501
|
+
}
|
|
1502
|
+
else {
|
|
1503
|
+
apiPlugin = {
|
|
1504
|
+
schema_version: "v2",
|
|
1505
|
+
name_for_human: "",
|
|
1506
|
+
description_for_human: "",
|
|
1507
|
+
functions: [],
|
|
1508
|
+
runtimes: [],
|
|
1509
|
+
};
|
|
1510
|
+
}
|
|
1511
|
+
apiPlugin.functions = apiPlugin.functions || [];
|
|
1512
|
+
for (const func of functions) {
|
|
1513
|
+
const index = (_c = apiPlugin.functions) === null || _c === void 0 ? void 0 : _c.findIndex((f) => f.name === func.name);
|
|
1514
|
+
if (index === -1) {
|
|
1515
|
+
apiPlugin.functions.push(func);
|
|
1516
|
+
}
|
|
1517
|
+
else {
|
|
1518
|
+
apiPlugin.functions[index] = func;
|
|
1519
|
+
}
|
|
1520
|
+
}
|
|
1521
|
+
apiPlugin.runtimes = apiPlugin.runtimes || [];
|
|
1522
|
+
const index = apiPlugin.runtimes.findIndex((runtime) => runtime.spec.url === specRelativePath);
|
|
1523
|
+
if (index === -1) {
|
|
1524
|
+
apiPlugin.runtimes.push({
|
|
1525
|
+
type: "OpenApi",
|
|
1526
|
+
auth: {
|
|
1527
|
+
type: "none",
|
|
1528
|
+
},
|
|
1529
|
+
spec: {
|
|
1530
|
+
url: specRelativePath,
|
|
1531
|
+
},
|
|
1532
|
+
run_for_functions: functionNames,
|
|
1533
|
+
});
|
|
1534
|
+
}
|
|
1535
|
+
else {
|
|
1536
|
+
apiPlugin.runtimes[index].run_for_functions = functionNames;
|
|
1537
|
+
}
|
|
1538
|
+
if (!apiPlugin.name_for_human) {
|
|
1539
|
+
apiPlugin.name_for_human = appName;
|
|
1540
|
+
}
|
|
1541
|
+
if (!apiPlugin.description_for_human) {
|
|
1542
|
+
apiPlugin.description_for_human =
|
|
1543
|
+
(_d = spec.info.description) !== null && _d !== void 0 ? _d : "<Please add description of the plugin>";
|
|
1544
|
+
}
|
|
1545
|
+
if (options.allowConversationStarters && conversationStarters.length > 0) {
|
|
1546
|
+
if (!apiPlugin.capabilities) {
|
|
1547
|
+
apiPlugin.capabilities = {
|
|
1548
|
+
localization: {},
|
|
1549
|
+
};
|
|
1550
|
+
}
|
|
1551
|
+
if (!apiPlugin.capabilities.conversation_starters) {
|
|
1552
|
+
apiPlugin.capabilities.conversation_starters = conversationStarters
|
|
1553
|
+
.slice(0, 5)
|
|
1554
|
+
.map((text) => ({ text }));
|
|
1555
|
+
}
|
|
1556
|
+
}
|
|
1557
|
+
return apiPlugin;
|
|
1558
|
+
});
|
|
1559
|
+
}
|
|
1560
|
+
static updateManifest(manifestPath, outputSpecPath, spec, options, adaptiveCardFolder, authInfo) {
|
|
1561
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1562
|
+
try {
|
|
1563
|
+
const originalManifest = yield fs__default['default'].readJSON(manifestPath);
|
|
1564
|
+
const updatedPart = {};
|
|
1565
|
+
updatedPart.composeExtensions = [];
|
|
1566
|
+
let warnings = [];
|
|
1567
|
+
if (options.projectType === exports.ProjectType.SME) {
|
|
1568
|
+
const updateResult = yield ManifestUpdater.generateCommands(spec, manifestPath, options, adaptiveCardFolder);
|
|
1569
|
+
const commands = updateResult[0];
|
|
1570
|
+
warnings = updateResult[1];
|
|
1571
|
+
const composeExtension = {
|
|
1572
|
+
composeExtensionType: "apiBased",
|
|
1573
|
+
apiSpecificationFile: ManifestUpdater.getRelativePath(manifestPath, outputSpecPath),
|
|
1574
|
+
commands: commands,
|
|
1575
|
+
};
|
|
1576
|
+
if (authInfo) {
|
|
1577
|
+
const auth = authInfo.authScheme;
|
|
1578
|
+
if (Utils.isAPIKeyAuth(auth) || Utils.isBearerTokenAuth(auth)) {
|
|
1579
|
+
const safeApiSecretRegistrationId = Utils.getSafeRegistrationIdEnvName(`${authInfo.name}_${ConstantString.RegistrationIdPostfix}`);
|
|
1580
|
+
composeExtension.authorization = {
|
|
1581
|
+
authType: "apiSecretServiceAuth",
|
|
1582
|
+
apiSecretServiceAuthConfiguration: {
|
|
1583
|
+
apiSecretRegistrationId: `\${{${safeApiSecretRegistrationId}}}`,
|
|
1584
|
+
},
|
|
1585
|
+
};
|
|
1586
|
+
}
|
|
1587
|
+
else if (Utils.isOAuthWithAuthCodeFlow(auth)) {
|
|
1588
|
+
const safeOAuth2RegistrationId = Utils.getSafeRegistrationIdEnvName(`${authInfo.name}_${ConstantString.OAuthRegistrationIdPostFix}`);
|
|
1589
|
+
composeExtension.authorization = {
|
|
1590
|
+
authType: "oAuth2.0",
|
|
1591
|
+
oAuthConfiguration: {
|
|
1592
|
+
oauthConfigurationId: `\${{${safeOAuth2RegistrationId}}}`,
|
|
1593
|
+
},
|
|
1594
|
+
};
|
|
1595
|
+
updatedPart.webApplicationInfo = {
|
|
1596
|
+
id: "${{AAD_APP_CLIENT_ID}}",
|
|
1597
|
+
resource: "api://${{DOMAIN}}/${{AAD_APP_CLIENT_ID}}",
|
|
1598
|
+
};
|
|
1599
|
+
}
|
|
1600
|
+
}
|
|
1601
|
+
updatedPart.composeExtensions = [composeExtension];
|
|
1602
|
+
}
|
|
1603
|
+
updatedPart.description = originalManifest.description;
|
|
1604
|
+
ManifestUpdater.updateManifestDescription(updatedPart, spec);
|
|
1605
|
+
const updatedManifest = Object.assign(Object.assign({}, originalManifest), updatedPart);
|
|
1606
|
+
return [updatedManifest, warnings];
|
|
1607
|
+
}
|
|
1608
|
+
catch (err) {
|
|
1609
|
+
throw new SpecParserError(err.toString(), exports.ErrorType.UpdateManifestFailed);
|
|
1610
|
+
}
|
|
1611
|
+
});
|
|
1612
|
+
}
|
|
1613
|
+
static generateCommands(spec, manifestPath, options, adaptiveCardFolder) {
|
|
1614
|
+
var _a;
|
|
1615
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1616
|
+
const paths = spec.paths;
|
|
1617
|
+
const commands = [];
|
|
1618
|
+
const warnings = [];
|
|
1619
|
+
if (paths) {
|
|
1620
|
+
for (const pathUrl in paths) {
|
|
1621
|
+
const pathItem = paths[pathUrl];
|
|
1622
|
+
if (pathItem) {
|
|
1623
|
+
const operations = pathItem;
|
|
1624
|
+
// Currently only support GET and POST method
|
|
1625
|
+
for (const method in operations) {
|
|
1626
|
+
if ((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) {
|
|
1627
|
+
const operationItem = operations[method];
|
|
1628
|
+
if (operationItem) {
|
|
1629
|
+
const command = Utils.parseApiInfo(operationItem, options);
|
|
1630
|
+
if (command.parameters &&
|
|
1631
|
+
command.parameters.length >= 1 &&
|
|
1632
|
+
command.parameters.some((param) => param.isRequired)) {
|
|
1633
|
+
command.parameters = command.parameters.filter((param) => param.isRequired);
|
|
1634
|
+
}
|
|
1635
|
+
else if (command.parameters && command.parameters.length > 0) {
|
|
1636
|
+
command.parameters = [command.parameters[0]];
|
|
1637
|
+
warnings.push({
|
|
1638
|
+
type: exports.WarningType.OperationOnlyContainsOptionalParam,
|
|
1639
|
+
content: Utils.format(ConstantString.OperationOnlyContainsOptionalParam, command.id),
|
|
1640
|
+
data: command.id,
|
|
1641
|
+
});
|
|
1642
|
+
}
|
|
1643
|
+
if (adaptiveCardFolder) {
|
|
1644
|
+
const adaptiveCardPath = path__default['default'].join(adaptiveCardFolder, command.id + ".json");
|
|
1645
|
+
command.apiResponseRenderingTemplateFile = (yield fs__default['default'].pathExists(adaptiveCardPath))
|
|
1646
|
+
? ManifestUpdater.getRelativePath(manifestPath, adaptiveCardPath)
|
|
1647
|
+
: "";
|
|
1648
|
+
}
|
|
1649
|
+
commands.push(command);
|
|
1650
|
+
}
|
|
1651
|
+
}
|
|
1652
|
+
}
|
|
1653
|
+
}
|
|
1654
|
+
}
|
|
1655
|
+
}
|
|
1656
|
+
return [commands, warnings];
|
|
1657
|
+
});
|
|
1658
|
+
}
|
|
1659
|
+
static getRelativePath(from, to) {
|
|
1660
|
+
const relativePath = path__default['default'].relative(path__default['default'].dirname(from), to);
|
|
1661
|
+
return path__default['default'].normalize(relativePath).replace(/\\/g, "/");
|
|
1662
|
+
}
|
|
1663
|
+
static removeEnvs(str) {
|
|
1664
|
+
const placeHolderReg = /\${{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*}}/g;
|
|
1665
|
+
const matches = placeHolderReg.exec(str);
|
|
1666
|
+
let newStr = str;
|
|
1667
|
+
if (matches != null) {
|
|
1668
|
+
newStr = newStr.replace(matches[0], "");
|
|
1669
|
+
}
|
|
1670
|
+
return newStr;
|
|
1671
|
+
}
|
|
1672
|
+
}
|
|
1673
|
+
|
|
1139
1674
|
// Copyright (c) Microsoft Corporation.
|
|
1140
1675
|
/**
|
|
1141
1676
|
* A class that parses an OpenAPI specification file and provides methods to validate, list, and generate artifacts.
|
|
@@ -1151,8 +1686,13 @@ class SpecParser {
|
|
|
1151
1686
|
allowMissingId: true,
|
|
1152
1687
|
allowSwagger: true,
|
|
1153
1688
|
allowAPIKeyAuth: false,
|
|
1689
|
+
allowBearerTokenAuth: false,
|
|
1154
1690
|
allowMultipleParameters: false,
|
|
1155
1691
|
allowOauth2: false,
|
|
1692
|
+
allowMethods: ["get", "post"],
|
|
1693
|
+
allowConversationStarters: false,
|
|
1694
|
+
allowResponseSemantics: false,
|
|
1695
|
+
projectType: exports.ProjectType.SME,
|
|
1156
1696
|
};
|
|
1157
1697
|
this.pathOrSpec = pathOrDoc;
|
|
1158
1698
|
this.parser = new SwaggerParser__default['default']();
|
|
@@ -1177,6 +1717,8 @@ class SpecParser {
|
|
|
1177
1717
|
errors: [{ type: exports.ErrorType.SpecNotValid, content: e.toString() }],
|
|
1178
1718
|
};
|
|
1179
1719
|
}
|
|
1720
|
+
const errors = [];
|
|
1721
|
+
const warnings = [];
|
|
1180
1722
|
if (!this.options.allowSwagger && this.isSwaggerFile) {
|
|
1181
1723
|
return {
|
|
1182
1724
|
status: exports.ValidationStatus.Error,
|
|
@@ -1186,7 +1728,38 @@ class SpecParser {
|
|
|
1186
1728
|
],
|
|
1187
1729
|
};
|
|
1188
1730
|
}
|
|
1189
|
-
|
|
1731
|
+
// Remote reference not supported
|
|
1732
|
+
const refPaths = this.parser.$refs.paths();
|
|
1733
|
+
// refPaths [0] is the current spec file path
|
|
1734
|
+
if (refPaths.length > 1) {
|
|
1735
|
+
errors.push({
|
|
1736
|
+
type: exports.ErrorType.RemoteRefNotSupported,
|
|
1737
|
+
content: Utils.format(ConstantString.RemoteRefNotSupported, refPaths.join(", ")),
|
|
1738
|
+
data: refPaths,
|
|
1739
|
+
});
|
|
1740
|
+
}
|
|
1741
|
+
if (!!this.isSwaggerFile && this.options.allowSwagger) {
|
|
1742
|
+
warnings.push({
|
|
1743
|
+
type: exports.WarningType.ConvertSwaggerToOpenAPI,
|
|
1744
|
+
content: ConstantString.ConvertSwaggerToOpenAPI,
|
|
1745
|
+
});
|
|
1746
|
+
}
|
|
1747
|
+
const validator = this.getValidator(this.spec);
|
|
1748
|
+
const validationResult = validator.validateSpec();
|
|
1749
|
+
warnings.push(...validationResult.warnings);
|
|
1750
|
+
errors.push(...validationResult.errors);
|
|
1751
|
+
let status = exports.ValidationStatus.Valid;
|
|
1752
|
+
if (warnings.length > 0 && errors.length === 0) {
|
|
1753
|
+
status = exports.ValidationStatus.Warning;
|
|
1754
|
+
}
|
|
1755
|
+
else if (errors.length > 0) {
|
|
1756
|
+
status = exports.ValidationStatus.Error;
|
|
1757
|
+
}
|
|
1758
|
+
return {
|
|
1759
|
+
status: status,
|
|
1760
|
+
warnings: warnings,
|
|
1761
|
+
errors: errors,
|
|
1762
|
+
};
|
|
1190
1763
|
}
|
|
1191
1764
|
catch (err) {
|
|
1192
1765
|
throw new SpecParserError(err.toString(), exports.ErrorType.ValidateFailed);
|
|
@@ -1210,39 +1783,40 @@ class SpecParser {
|
|
|
1210
1783
|
try {
|
|
1211
1784
|
yield this.loadSpec();
|
|
1212
1785
|
const spec = this.spec;
|
|
1213
|
-
const apiMap = this.
|
|
1214
|
-
const result =
|
|
1786
|
+
const apiMap = this.getAPIs(spec);
|
|
1787
|
+
const result = {
|
|
1788
|
+
APIs: [],
|
|
1789
|
+
allAPICount: 0,
|
|
1790
|
+
validAPICount: 0,
|
|
1791
|
+
};
|
|
1215
1792
|
for (const apiKey in apiMap) {
|
|
1793
|
+
const { operation, isValid, reason } = apiMap[apiKey];
|
|
1794
|
+
const [method, path] = apiKey.split(" ");
|
|
1795
|
+
const operationId = (_a = operation.operationId) !== null && _a !== void 0 ? _a : `${method.toLowerCase()}${Utils.convertPathToCamelCase(path)}`;
|
|
1216
1796
|
const apiResult = {
|
|
1217
|
-
api:
|
|
1797
|
+
api: apiKey,
|
|
1218
1798
|
server: "",
|
|
1219
|
-
operationId:
|
|
1799
|
+
operationId: operationId,
|
|
1800
|
+
isValid: isValid,
|
|
1801
|
+
reason: reason,
|
|
1220
1802
|
};
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
if (!operationId) {
|
|
1233
|
-
operationId = `${method.toLowerCase()}${Utils.convertPathToCamelCase(path)}`;
|
|
1234
|
-
}
|
|
1235
|
-
apiResult.operationId = operationId;
|
|
1236
|
-
const authArray = Utils.getAuthArray(operation.security, spec);
|
|
1237
|
-
for (const auths of authArray) {
|
|
1238
|
-
if (auths.length === 1) {
|
|
1239
|
-
apiResult.auth = auths[0].authSchema;
|
|
1240
|
-
break;
|
|
1803
|
+
if (isValid) {
|
|
1804
|
+
const serverObj = Utils.getServerObject(spec, method.toLocaleLowerCase(), path);
|
|
1805
|
+
if (serverObj) {
|
|
1806
|
+
apiResult.server = Utils.resolveEnv(serverObj.url);
|
|
1807
|
+
}
|
|
1808
|
+
const authArray = Utils.getAuthArray(operation.security, spec);
|
|
1809
|
+
for (const auths of authArray) {
|
|
1810
|
+
if (auths.length === 1) {
|
|
1811
|
+
apiResult.auth = auths[0];
|
|
1812
|
+
break;
|
|
1813
|
+
}
|
|
1241
1814
|
}
|
|
1242
1815
|
}
|
|
1243
|
-
apiResult
|
|
1244
|
-
result.push(apiResult);
|
|
1816
|
+
result.APIs.push(apiResult);
|
|
1245
1817
|
}
|
|
1818
|
+
result.allAPICount = result.APIs.length;
|
|
1819
|
+
result.validAPICount = result.APIs.filter((api) => api.isValid).length;
|
|
1246
1820
|
return result;
|
|
1247
1821
|
}
|
|
1248
1822
|
catch (err) {
|
|
@@ -1267,7 +1841,7 @@ class SpecParser {
|
|
|
1267
1841
|
if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
|
|
1268
1842
|
throw new SpecParserError(ConstantString.CancelledMessage, exports.ErrorType.Cancelled);
|
|
1269
1843
|
}
|
|
1270
|
-
const newUnResolvedSpec = SpecFilter.specFilter(filter, this.unResolveSpec, this.spec, this.options
|
|
1844
|
+
const newUnResolvedSpec = SpecFilter.specFilter(filter, this.unResolveSpec, this.spec, this.options);
|
|
1271
1845
|
if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
|
|
1272
1846
|
throw new SpecParserError(ConstantString.CancelledMessage, exports.ErrorType.Cancelled);
|
|
1273
1847
|
}
|
|
@@ -1282,15 +1856,55 @@ class SpecParser {
|
|
|
1282
1856
|
}
|
|
1283
1857
|
});
|
|
1284
1858
|
}
|
|
1859
|
+
/**
|
|
1860
|
+
* Generates and update artifacts from the OpenAPI specification file. Generate Adaptive Cards, update Teams app manifest, and generate a new OpenAPI specification file.
|
|
1861
|
+
* @param manifestPath A file path of the Teams app manifest file to update.
|
|
1862
|
+
* @param filter An array of strings that represent the filters to apply when generating the artifacts. If filter is empty, it would process nothing.
|
|
1863
|
+
* @param outputSpecPath File path of the new OpenAPI specification file to generate. If not specified or empty, no spec file will be generated.
|
|
1864
|
+
* @param pluginFilePath File path of the api plugin file to generate.
|
|
1865
|
+
*/
|
|
1866
|
+
generateForCopilot(manifestPath, filter, outputSpecPath, pluginFilePath, signal) {
|
|
1867
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1868
|
+
const result = {
|
|
1869
|
+
allSuccess: true,
|
|
1870
|
+
warnings: [],
|
|
1871
|
+
};
|
|
1872
|
+
try {
|
|
1873
|
+
const newSpecs = yield this.getFilteredSpecs(filter, signal);
|
|
1874
|
+
const newUnResolvedSpec = newSpecs[0];
|
|
1875
|
+
const newSpec = newSpecs[1];
|
|
1876
|
+
let resultStr;
|
|
1877
|
+
if (outputSpecPath.endsWith(".yaml") || outputSpecPath.endsWith(".yml")) {
|
|
1878
|
+
resultStr = jsyaml__default['default'].dump(newUnResolvedSpec);
|
|
1879
|
+
}
|
|
1880
|
+
else {
|
|
1881
|
+
resultStr = JSON.stringify(newUnResolvedSpec, null, 2);
|
|
1882
|
+
}
|
|
1883
|
+
yield fs__default['default'].outputFile(outputSpecPath, resultStr);
|
|
1884
|
+
if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
|
|
1885
|
+
throw new SpecParserError(ConstantString.CancelledMessage, exports.ErrorType.Cancelled);
|
|
1886
|
+
}
|
|
1887
|
+
const [updatedManifest, apiPlugin] = yield ManifestUpdater.updateManifestWithAiPlugin(manifestPath, outputSpecPath, pluginFilePath, newSpec, this.options);
|
|
1888
|
+
yield fs__default['default'].outputJSON(manifestPath, updatedManifest, { spaces: 2 });
|
|
1889
|
+
yield fs__default['default'].outputJSON(pluginFilePath, apiPlugin, { spaces: 2 });
|
|
1890
|
+
}
|
|
1891
|
+
catch (err) {
|
|
1892
|
+
if (err instanceof SpecParserError) {
|
|
1893
|
+
throw err;
|
|
1894
|
+
}
|
|
1895
|
+
throw new SpecParserError(err.toString(), exports.ErrorType.GenerateFailed);
|
|
1896
|
+
}
|
|
1897
|
+
return result;
|
|
1898
|
+
});
|
|
1899
|
+
}
|
|
1285
1900
|
/**
|
|
1286
1901
|
* Generates and update artifacts from the OpenAPI specification file. Generate Adaptive Cards, update Teams app manifest, and generate a new OpenAPI specification file.
|
|
1287
1902
|
* @param manifestPath A file path of the Teams app manifest file to update.
|
|
1288
1903
|
* @param filter An array of strings that represent the filters to apply when generating the artifacts. If filter is empty, it would process nothing.
|
|
1289
1904
|
* @param outputSpecPath File path of the new OpenAPI specification file to generate. If not specified or empty, no spec file will be generated.
|
|
1290
1905
|
* @param adaptiveCardFolder Folder path where the Adaptive Card files will be generated. If not specified or empty, Adaptive Card files will not be generated.
|
|
1291
|
-
* @param isMe Boolean that indicates whether the project is an Messaging Extension. For Messaging Extension, composeExtensions will be added in Teams app manifest.
|
|
1292
1906
|
*/
|
|
1293
|
-
generate(manifestPath, filter, outputSpecPath, adaptiveCardFolder, signal
|
|
1907
|
+
generate(manifestPath, filter, outputSpecPath, adaptiveCardFolder, signal) {
|
|
1294
1908
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1295
1909
|
const result = {
|
|
1296
1910
|
allSuccess: true,
|
|
@@ -1300,23 +1914,26 @@ class SpecParser {
|
|
|
1300
1914
|
const newSpecs = yield this.getFilteredSpecs(filter, signal);
|
|
1301
1915
|
const newUnResolvedSpec = newSpecs[0];
|
|
1302
1916
|
const newSpec = newSpecs[1];
|
|
1303
|
-
|
|
1304
|
-
let
|
|
1917
|
+
let hasMultipleAuth = false;
|
|
1918
|
+
let authInfo = undefined;
|
|
1305
1919
|
for (const url in newSpec.paths) {
|
|
1306
1920
|
for (const method in newSpec.paths[url]) {
|
|
1307
1921
|
const operation = newSpec.paths[url][method];
|
|
1308
1922
|
const authArray = Utils.getAuthArray(operation.security, newSpec);
|
|
1309
1923
|
if (authArray && authArray.length > 0) {
|
|
1310
|
-
|
|
1311
|
-
if (
|
|
1312
|
-
|
|
1924
|
+
const currentAuth = authArray[0][0];
|
|
1925
|
+
if (!authInfo) {
|
|
1926
|
+
authInfo = authArray[0][0];
|
|
1927
|
+
}
|
|
1928
|
+
else if (authInfo.name !== currentAuth.name) {
|
|
1929
|
+
hasMultipleAuth = true;
|
|
1313
1930
|
break;
|
|
1314
1931
|
}
|
|
1315
1932
|
}
|
|
1316
1933
|
}
|
|
1317
1934
|
}
|
|
1318
|
-
if (
|
|
1319
|
-
throw new SpecParserError(ConstantString.
|
|
1935
|
+
if (hasMultipleAuth && this.options.projectType !== exports.ProjectType.TeamsAi) {
|
|
1936
|
+
throw new SpecParserError(ConstantString.MultipleAuthNotSupported, exports.ErrorType.MultipleAuthNotSupported);
|
|
1320
1937
|
}
|
|
1321
1938
|
let resultStr;
|
|
1322
1939
|
if (outputSpecPath.endsWith(".yaml") || outputSpecPath.endsWith(".yml")) {
|
|
@@ -1326,26 +1943,28 @@ class SpecParser {
|
|
|
1326
1943
|
resultStr = JSON.stringify(newUnResolvedSpec, null, 2);
|
|
1327
1944
|
}
|
|
1328
1945
|
yield fs__default['default'].outputFile(outputSpecPath, resultStr);
|
|
1329
|
-
|
|
1330
|
-
for (const
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1946
|
+
if (adaptiveCardFolder) {
|
|
1947
|
+
for (const url in newSpec.paths) {
|
|
1948
|
+
for (const method in newSpec.paths[url]) {
|
|
1949
|
+
// paths object may contain description/summary which is not a http method, so we need to check if it is a operation object
|
|
1950
|
+
if (this.options.allowMethods.includes(method)) {
|
|
1951
|
+
const operation = newSpec.paths[url][method];
|
|
1952
|
+
try {
|
|
1953
|
+
const [card, jsonPath] = AdaptiveCardGenerator.generateAdaptiveCard(operation);
|
|
1954
|
+
const fileName = path__default['default'].join(adaptiveCardFolder, `${operation.operationId}.json`);
|
|
1955
|
+
const wrappedCard = wrapAdaptiveCard(card, jsonPath);
|
|
1956
|
+
yield fs__default['default'].outputJSON(fileName, wrappedCard, { spaces: 2 });
|
|
1957
|
+
const dataFileName = path__default['default'].join(adaptiveCardFolder, `${operation.operationId}.data.json`);
|
|
1958
|
+
yield fs__default['default'].outputJSON(dataFileName, {}, { spaces: 2 });
|
|
1959
|
+
}
|
|
1960
|
+
catch (err) {
|
|
1961
|
+
result.allSuccess = false;
|
|
1962
|
+
result.warnings.push({
|
|
1963
|
+
type: exports.WarningType.GenerateCardFailed,
|
|
1964
|
+
content: err.toString(),
|
|
1965
|
+
data: operation.operationId,
|
|
1966
|
+
});
|
|
1967
|
+
}
|
|
1349
1968
|
}
|
|
1350
1969
|
}
|
|
1351
1970
|
}
|
|
@@ -1353,8 +1972,7 @@ class SpecParser {
|
|
|
1353
1972
|
if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
|
|
1354
1973
|
throw new SpecParserError(ConstantString.CancelledMessage, exports.ErrorType.Cancelled);
|
|
1355
1974
|
}
|
|
1356
|
-
const
|
|
1357
|
-
const [updatedManifest, warnings] = yield ManifestUpdater.updateManifest(manifestPath, outputSpecPath, adaptiveCardFolder, newSpec, this.options.allowMultipleParameters, auth, isMe);
|
|
1975
|
+
const [updatedManifest, warnings] = yield ManifestUpdater.updateManifest(manifestPath, outputSpecPath, newSpec, this.options, adaptiveCardFolder, authInfo);
|
|
1358
1976
|
yield fs__default['default'].outputJSON(manifestPath, updatedManifest, { spaces: 2 });
|
|
1359
1977
|
result.warnings.push(...warnings);
|
|
1360
1978
|
}
|
|
@@ -1382,13 +2000,18 @@ class SpecParser {
|
|
|
1382
2000
|
}
|
|
1383
2001
|
});
|
|
1384
2002
|
}
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
2003
|
+
getAPIs(spec) {
|
|
2004
|
+
const validator = this.getValidator(spec);
|
|
2005
|
+
const apiMap = validator.listAPIs();
|
|
2006
|
+
return apiMap;
|
|
2007
|
+
}
|
|
2008
|
+
getValidator(spec) {
|
|
2009
|
+
if (this.validator) {
|
|
2010
|
+
return this.validator;
|
|
1388
2011
|
}
|
|
1389
|
-
const
|
|
1390
|
-
this.
|
|
1391
|
-
return
|
|
2012
|
+
const validator = ValidatorFactory.create(spec, this.options);
|
|
2013
|
+
this.validator = validator;
|
|
2014
|
+
return validator;
|
|
1392
2015
|
}
|
|
1393
2016
|
}
|
|
1394
2017
|
|