@microsoft/m365-spec-parser 0.1.1-alpha.8d8f5a0bb.0 → 0.1.1-alpha.8da24c130.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 +650 -337
- package/dist/index.esm2017.js.map +1 -1
- package/dist/index.esm2017.mjs +1097 -633
- package/dist/index.esm2017.mjs.map +1 -1
- package/dist/index.esm5.js +650 -337
- package/dist/index.esm5.js.map +1 -1
- package/dist/index.node.cjs.js +1109 -641
- package/dist/index.node.cjs.js.map +1 -1
- package/dist/src/adaptiveCardWrapper.d.ts +2 -0
- package/dist/src/constants.d.ts +10 -3
- package/dist/src/index.browser.d.ts +1 -1
- package/dist/src/index.d.ts +1 -1
- package/dist/src/interfaces.d.ts +110 -19
- package/dist/src/manifestUpdater.d.ts +10 -7
- package/dist/src/specFilter.d.ts +2 -1
- package/dist/src/specParser.browser.d.ts +4 -3
- package/dist/src/specParser.d.ts +6 -5
- package/dist/src/utils.d.ts +18 -34
- package/package.json +3 -3
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 {
|
|
@@ -103,7 +125,7 @@ ConstantString.RemoteRefNotSupported = "Remote reference is not supported: %s.";
|
|
|
103
125
|
ConstantString.MissingOperationId = "Missing operationIds: %s.";
|
|
104
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.";
|
|
105
127
|
ConstantString.AdditionalPropertiesNotSupported = "'additionalProperties' is not supported, and will be ignored.";
|
|
106
|
-
ConstantString.SchemaNotSupported = "'oneOf', 'anyOf', and 'not' schema are not supported: %s.";
|
|
128
|
+
ConstantString.SchemaNotSupported = "'oneOf', 'allOf', 'anyOf', and 'not' schema are not supported: %s.";
|
|
107
129
|
ConstantString.UnknownSchema = "Unknown schema: %s.";
|
|
108
130
|
ConstantString.UrlProtocolNotSupported = "Server url is not correct: protocol %s is not supported, you should use https protocol instead.";
|
|
109
131
|
ConstantString.RelativeServerUrlNotSupported = "Server url is not correct: relative server url is not supported.";
|
|
@@ -111,7 +133,8 @@ 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.";
|
|
115
138
|
ConstantString.UnsupportedSchema = "Unsupported schema in %s %s: %s";
|
|
116
139
|
ConstantString.WrappedCardVersion = "devPreview";
|
|
117
140
|
ConstantString.WrappedCardSchema = "https://developer.microsoft.com/json-schemas/teams/vDevPreview/MicrosoftTeams.ResponseRenderingTemplate.schema.json";
|
|
@@ -122,8 +145,14 @@ ConstantString.AdaptiveCardVersion = "1.5";
|
|
|
122
145
|
ConstantString.AdaptiveCardSchema = "http://adaptivecards.io/schemas/adaptive-card.json";
|
|
123
146
|
ConstantString.AdaptiveCardType = "AdaptiveCard";
|
|
124
147
|
ConstantString.TextBlockType = "TextBlock";
|
|
148
|
+
ConstantString.ImageType = "Image";
|
|
125
149
|
ConstantString.ContainerType = "Container";
|
|
126
|
-
ConstantString.RegistrationIdPostfix =
|
|
150
|
+
ConstantString.RegistrationIdPostfix = {
|
|
151
|
+
apiKey: "REGISTRATION_ID",
|
|
152
|
+
oauth2: "CONFIGURATION_ID",
|
|
153
|
+
http: "REGISTRATION_ID",
|
|
154
|
+
openIdConnect: "REGISTRATION_ID",
|
|
155
|
+
};
|
|
127
156
|
ConstantString.ResponseCodeFor20X = [
|
|
128
157
|
"200",
|
|
129
158
|
"201",
|
|
@@ -182,8 +211,11 @@ ConstantString.ShortDescriptionMaxLens = 80;
|
|
|
182
211
|
ConstantString.FullDescriptionMaxLens = 4000;
|
|
183
212
|
ConstantString.CommandDescriptionMaxLens = 128;
|
|
184
213
|
ConstantString.ParameterDescriptionMaxLens = 128;
|
|
214
|
+
ConstantString.ConversationStarterMaxLens = 50;
|
|
185
215
|
ConstantString.CommandTitleMaxLens = 32;
|
|
186
|
-
ConstantString.ParameterTitleMaxLens = 32;
|
|
216
|
+
ConstantString.ParameterTitleMaxLens = 32;
|
|
217
|
+
ConstantString.SMERequiredParamsMaxNum = 5;
|
|
218
|
+
ConstantString.DefaultPluginId = "plugin_1";
|
|
187
219
|
|
|
188
220
|
// Copyright (c) Microsoft Corporation.
|
|
189
221
|
class SpecParserError extends Error {
|
|
@@ -206,238 +238,33 @@ class Utils {
|
|
|
206
238
|
}
|
|
207
239
|
return false;
|
|
208
240
|
}
|
|
209
|
-
static
|
|
210
|
-
|
|
211
|
-
requiredNum: 0,
|
|
212
|
-
optionalNum: 0,
|
|
213
|
-
isValid: true,
|
|
214
|
-
};
|
|
215
|
-
if (!paramObject) {
|
|
216
|
-
return paramResult;
|
|
217
|
-
}
|
|
218
|
-
for (let i = 0; i < paramObject.length; i++) {
|
|
219
|
-
const param = paramObject[i];
|
|
220
|
-
const schema = param.schema;
|
|
221
|
-
if (isCopilot && this.hasNestedObjectInSchema(schema)) {
|
|
222
|
-
paramResult.isValid = false;
|
|
223
|
-
continue;
|
|
224
|
-
}
|
|
225
|
-
const isRequiredWithoutDefault = param.required && schema.default === undefined;
|
|
226
|
-
if (isCopilot) {
|
|
227
|
-
if (isRequiredWithoutDefault) {
|
|
228
|
-
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
229
|
-
}
|
|
230
|
-
else {
|
|
231
|
-
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
232
|
-
}
|
|
233
|
-
continue;
|
|
234
|
-
}
|
|
235
|
-
if (param.in === "header" || param.in === "cookie") {
|
|
236
|
-
if (isRequiredWithoutDefault) {
|
|
237
|
-
paramResult.isValid = false;
|
|
238
|
-
}
|
|
239
|
-
continue;
|
|
240
|
-
}
|
|
241
|
-
if (schema.type !== "boolean" &&
|
|
242
|
-
schema.type !== "string" &&
|
|
243
|
-
schema.type !== "number" &&
|
|
244
|
-
schema.type !== "integer") {
|
|
245
|
-
if (isRequiredWithoutDefault) {
|
|
246
|
-
paramResult.isValid = false;
|
|
247
|
-
}
|
|
248
|
-
continue;
|
|
249
|
-
}
|
|
250
|
-
if (param.in === "query" || param.in === "path") {
|
|
251
|
-
if (isRequiredWithoutDefault) {
|
|
252
|
-
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
253
|
-
}
|
|
254
|
-
else {
|
|
255
|
-
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
return paramResult;
|
|
260
|
-
}
|
|
261
|
-
static checkPostBody(schema, isRequired = false, isCopilot = false) {
|
|
262
|
-
var _a;
|
|
263
|
-
const paramResult = {
|
|
264
|
-
requiredNum: 0,
|
|
265
|
-
optionalNum: 0,
|
|
266
|
-
isValid: true,
|
|
267
|
-
};
|
|
268
|
-
if (Object.keys(schema).length === 0) {
|
|
269
|
-
return paramResult;
|
|
270
|
-
}
|
|
271
|
-
const isRequiredWithoutDefault = isRequired && schema.default === undefined;
|
|
272
|
-
if (isCopilot && this.hasNestedObjectInSchema(schema)) {
|
|
273
|
-
paramResult.isValid = false;
|
|
274
|
-
return paramResult;
|
|
275
|
-
}
|
|
276
|
-
if (schema.type === "string" ||
|
|
277
|
-
schema.type === "integer" ||
|
|
278
|
-
schema.type === "boolean" ||
|
|
279
|
-
schema.type === "number") {
|
|
280
|
-
if (isRequiredWithoutDefault) {
|
|
281
|
-
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
282
|
-
}
|
|
283
|
-
else {
|
|
284
|
-
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
else if (schema.type === "object") {
|
|
288
|
-
const { properties } = schema;
|
|
289
|
-
for (const property in properties) {
|
|
290
|
-
let isRequired = false;
|
|
291
|
-
if (schema.required && ((_a = schema.required) === null || _a === void 0 ? void 0 : _a.indexOf(property)) >= 0) {
|
|
292
|
-
isRequired = true;
|
|
293
|
-
}
|
|
294
|
-
const result = Utils.checkPostBody(properties[property], isRequired, isCopilot);
|
|
295
|
-
paramResult.requiredNum += result.requiredNum;
|
|
296
|
-
paramResult.optionalNum += result.optionalNum;
|
|
297
|
-
paramResult.isValid = paramResult.isValid && result.isValid;
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
else {
|
|
301
|
-
if (isRequiredWithoutDefault && !isCopilot) {
|
|
302
|
-
paramResult.isValid = false;
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
return paramResult;
|
|
306
|
-
}
|
|
307
|
-
/**
|
|
308
|
-
* Checks if the given API is supported.
|
|
309
|
-
* @param {string} method - The HTTP method of the API.
|
|
310
|
-
* @param {string} path - The path of the API.
|
|
311
|
-
* @param {OpenAPIV3.Document} spec - The OpenAPI specification document.
|
|
312
|
-
* @returns {boolean} - Returns true if the API is supported, false otherwise.
|
|
313
|
-
* @description The following APIs are supported:
|
|
314
|
-
* 1. only support Get/Post operation without auth property
|
|
315
|
-
* 2. parameter inside query or path only support string, number, boolean and integer
|
|
316
|
-
* 3. parameter inside post body only support string, number, boolean, integer and object
|
|
317
|
-
* 4. request body + required parameters <= 1
|
|
318
|
-
* 5. response body should be “application/json” and not empty, and response code should be 20X
|
|
319
|
-
* 6. only support request body with “application/json” content type
|
|
320
|
-
*/
|
|
321
|
-
static isSupportedApi(method, path, spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2, isCopilot) {
|
|
322
|
-
const pathObj = spec.paths[path];
|
|
323
|
-
method = method.toLocaleLowerCase();
|
|
324
|
-
if (pathObj) {
|
|
325
|
-
if ((method === ConstantString.PostMethod || method === ConstantString.GetMethod) &&
|
|
326
|
-
pathObj[method]) {
|
|
327
|
-
const securities = pathObj[method].security;
|
|
328
|
-
const authArray = Utils.getAuthArray(securities, spec);
|
|
329
|
-
if (!Utils.isSupportedAuth(authArray, allowAPIKeyAuth, allowOauth2)) {
|
|
330
|
-
return false;
|
|
331
|
-
}
|
|
332
|
-
const operationObject = pathObj[method];
|
|
333
|
-
if (!allowMissingId && !operationObject.operationId) {
|
|
334
|
-
return false;
|
|
335
|
-
}
|
|
336
|
-
const paramObject = operationObject.parameters;
|
|
337
|
-
const requestBody = operationObject.requestBody;
|
|
338
|
-
const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
|
|
339
|
-
const mediaTypesCount = Object.keys((requestBody === null || requestBody === void 0 ? void 0 : requestBody.content) || {}).length;
|
|
340
|
-
if (mediaTypesCount > 1) {
|
|
341
|
-
return false;
|
|
342
|
-
}
|
|
343
|
-
const responseJson = Utils.getResponseJson(operationObject);
|
|
344
|
-
if (Object.keys(responseJson).length === 0) {
|
|
345
|
-
return false;
|
|
346
|
-
}
|
|
347
|
-
let requestBodyParamResult = {
|
|
348
|
-
requiredNum: 0,
|
|
349
|
-
optionalNum: 0,
|
|
350
|
-
isValid: true,
|
|
351
|
-
};
|
|
352
|
-
if (requestJsonBody) {
|
|
353
|
-
const requestBodySchema = requestJsonBody.schema;
|
|
354
|
-
if (isCopilot && requestBodySchema.type !== "object") {
|
|
355
|
-
return false;
|
|
356
|
-
}
|
|
357
|
-
requestBodyParamResult = Utils.checkPostBody(requestBodySchema, requestBody.required, isCopilot);
|
|
358
|
-
}
|
|
359
|
-
if (!requestBodyParamResult.isValid) {
|
|
360
|
-
return false;
|
|
361
|
-
}
|
|
362
|
-
const paramResult = Utils.checkParameters(paramObject, isCopilot);
|
|
363
|
-
if (!paramResult.isValid) {
|
|
364
|
-
return false;
|
|
365
|
-
}
|
|
366
|
-
// Copilot support arbitrary parameters
|
|
367
|
-
if (isCopilot) {
|
|
368
|
-
return true;
|
|
369
|
-
}
|
|
370
|
-
if (requestBodyParamResult.requiredNum + paramResult.requiredNum > 1) {
|
|
371
|
-
if (allowMultipleParameters &&
|
|
372
|
-
requestBodyParamResult.requiredNum + paramResult.requiredNum <= 5) {
|
|
373
|
-
return true;
|
|
374
|
-
}
|
|
375
|
-
return false;
|
|
376
|
-
}
|
|
377
|
-
else if (requestBodyParamResult.requiredNum +
|
|
378
|
-
requestBodyParamResult.optionalNum +
|
|
379
|
-
paramResult.requiredNum +
|
|
380
|
-
paramResult.optionalNum ===
|
|
381
|
-
0) {
|
|
382
|
-
return false;
|
|
383
|
-
}
|
|
384
|
-
else {
|
|
385
|
-
return true;
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
return false;
|
|
241
|
+
static containMultipleMediaTypes(bodyObject) {
|
|
242
|
+
return Object.keys((bodyObject === null || bodyObject === void 0 ? void 0 : bodyObject.content) || {}).length > 1;
|
|
390
243
|
}
|
|
391
|
-
static
|
|
392
|
-
|
|
393
|
-
return true;
|
|
394
|
-
}
|
|
395
|
-
if (allowAPIKeyAuth || allowOauth2) {
|
|
396
|
-
// Currently we don't support multiple auth in one operation
|
|
397
|
-
if (authSchemaArray.length > 0 && authSchemaArray.every((auths) => auths.length > 1)) {
|
|
398
|
-
return false;
|
|
399
|
-
}
|
|
400
|
-
for (const auths of authSchemaArray) {
|
|
401
|
-
if (auths.length === 1) {
|
|
402
|
-
if (!allowOauth2 && allowAPIKeyAuth && Utils.isAPIKeyAuth(auths[0].authSchema)) {
|
|
403
|
-
return true;
|
|
404
|
-
}
|
|
405
|
-
else if (!allowAPIKeyAuth &&
|
|
406
|
-
allowOauth2 &&
|
|
407
|
-
Utils.isBearerTokenAuth(auths[0].authSchema)) {
|
|
408
|
-
return true;
|
|
409
|
-
}
|
|
410
|
-
else if (allowAPIKeyAuth &&
|
|
411
|
-
allowOauth2 &&
|
|
412
|
-
(Utils.isAPIKeyAuth(auths[0].authSchema) ||
|
|
413
|
-
Utils.isBearerTokenAuth(auths[0].authSchema))) {
|
|
414
|
-
return true;
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
return false;
|
|
244
|
+
static isBearerTokenAuth(authScheme) {
|
|
245
|
+
return authScheme.type === "http" && authScheme.scheme === "bearer";
|
|
420
246
|
}
|
|
421
|
-
static isAPIKeyAuth(
|
|
422
|
-
return
|
|
247
|
+
static isAPIKeyAuth(authScheme) {
|
|
248
|
+
return authScheme.type === "apiKey";
|
|
423
249
|
}
|
|
424
|
-
static
|
|
425
|
-
return (
|
|
426
|
-
|
|
427
|
-
|
|
250
|
+
static isOAuthWithAuthCodeFlow(authScheme) {
|
|
251
|
+
return !!(authScheme.type === "oauth2" &&
|
|
252
|
+
authScheme.flows &&
|
|
253
|
+
authScheme.flows.authorizationCode);
|
|
428
254
|
}
|
|
429
255
|
static getAuthArray(securities, spec) {
|
|
430
256
|
var _a;
|
|
431
257
|
const result = [];
|
|
432
258
|
const securitySchemas = (_a = spec.components) === null || _a === void 0 ? void 0 : _a.securitySchemes;
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
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];
|
|
436
263
|
const authArray = [];
|
|
437
264
|
for (const name in security) {
|
|
438
265
|
const auth = securitySchemas[name];
|
|
439
266
|
authArray.push({
|
|
440
|
-
|
|
267
|
+
authScheme: auth,
|
|
441
268
|
name: name,
|
|
442
269
|
});
|
|
443
270
|
}
|
|
@@ -449,24 +276,47 @@ class Utils {
|
|
|
449
276
|
result.sort((a, b) => a[0].name.localeCompare(b[0].name));
|
|
450
277
|
return result;
|
|
451
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
|
+
}
|
|
452
298
|
static updateFirstLetter(str) {
|
|
453
299
|
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
454
300
|
}
|
|
455
301
|
static getResponseJson(operationObject) {
|
|
456
302
|
var _a, _b;
|
|
457
303
|
let json = {};
|
|
304
|
+
let multipleMediaType = false;
|
|
458
305
|
for (const code of ConstantString.ResponseCodeFor20X) {
|
|
459
306
|
const responseObject = (_a = operationObject === null || operationObject === void 0 ? void 0 : operationObject.responses) === null || _a === void 0 ? void 0 : _a[code];
|
|
460
|
-
const mediaTypesCount = Object.keys((responseObject === null || responseObject === void 0 ? void 0 : responseObject.content) || {}).length;
|
|
461
|
-
if (mediaTypesCount > 1) {
|
|
462
|
-
return {};
|
|
463
|
-
}
|
|
464
307
|
if ((_b = responseObject === null || responseObject === void 0 ? void 0 : responseObject.content) === null || _b === void 0 ? void 0 : _b["application/json"]) {
|
|
308
|
+
multipleMediaType = false;
|
|
465
309
|
json = responseObject.content["application/json"];
|
|
466
|
-
|
|
310
|
+
if (Utils.containMultipleMediaTypes(responseObject)) {
|
|
311
|
+
multipleMediaType = true;
|
|
312
|
+
json = {};
|
|
313
|
+
}
|
|
314
|
+
else {
|
|
315
|
+
break;
|
|
316
|
+
}
|
|
467
317
|
}
|
|
468
318
|
}
|
|
469
|
-
return json;
|
|
319
|
+
return { json, multipleMediaType };
|
|
470
320
|
}
|
|
471
321
|
static convertPathToCamelCase(path) {
|
|
472
322
|
const pathSegments = path.split(/[./{]/);
|
|
@@ -486,10 +336,10 @@ class Utils {
|
|
|
486
336
|
return undefined;
|
|
487
337
|
}
|
|
488
338
|
}
|
|
489
|
-
static
|
|
339
|
+
static resolveEnv(str) {
|
|
490
340
|
const placeHolderReg = /\${{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*}}/g;
|
|
491
|
-
let matches = placeHolderReg.exec(
|
|
492
|
-
let
|
|
341
|
+
let matches = placeHolderReg.exec(str);
|
|
342
|
+
let newStr = str;
|
|
493
343
|
while (matches != null) {
|
|
494
344
|
const envVar = matches[1];
|
|
495
345
|
const envVal = process.env[envVar];
|
|
@@ -497,17 +347,17 @@ class Utils {
|
|
|
497
347
|
throw new Error(Utils.format(ConstantString.ResolveServerUrlFailed, envVar));
|
|
498
348
|
}
|
|
499
349
|
else {
|
|
500
|
-
|
|
350
|
+
newStr = newStr.replace(matches[0], envVal);
|
|
501
351
|
}
|
|
502
|
-
matches = placeHolderReg.exec(
|
|
352
|
+
matches = placeHolderReg.exec(str);
|
|
503
353
|
}
|
|
504
|
-
return
|
|
354
|
+
return newStr;
|
|
505
355
|
}
|
|
506
356
|
static checkServerUrl(servers) {
|
|
507
357
|
const errors = [];
|
|
508
358
|
let serverUrl;
|
|
509
359
|
try {
|
|
510
|
-
serverUrl = Utils.
|
|
360
|
+
serverUrl = Utils.resolveEnv(servers[0].url);
|
|
511
361
|
}
|
|
512
362
|
catch (err) {
|
|
513
363
|
errors.push({
|
|
@@ -537,7 +387,8 @@ class Utils {
|
|
|
537
387
|
}
|
|
538
388
|
return errors;
|
|
539
389
|
}
|
|
540
|
-
static validateServer(spec,
|
|
390
|
+
static validateServer(spec, options) {
|
|
391
|
+
var _a;
|
|
541
392
|
const errors = [];
|
|
542
393
|
let hasTopLevelServers = false;
|
|
543
394
|
let hasPathLevelServers = false;
|
|
@@ -558,7 +409,7 @@ class Utils {
|
|
|
558
409
|
}
|
|
559
410
|
for (const method in methods) {
|
|
560
411
|
const operationObject = methods[method];
|
|
561
|
-
if (
|
|
412
|
+
if (((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && operationObject) {
|
|
562
413
|
if ((operationObject === null || operationObject === void 0 ? void 0 : operationObject.servers) && operationObject.servers.length >= 1) {
|
|
563
414
|
hasOperationLevelServers = true;
|
|
564
415
|
const serverErrors = Utils.checkServerUrl(operationObject.servers);
|
|
@@ -601,6 +452,7 @@ class Utils {
|
|
|
601
452
|
Utils.updateParameterWithInputType(schema, parameter);
|
|
602
453
|
}
|
|
603
454
|
if (isRequired && schema.default === undefined) {
|
|
455
|
+
parameter.isRequired = true;
|
|
604
456
|
requiredParams.push(parameter);
|
|
605
457
|
}
|
|
606
458
|
else {
|
|
@@ -645,7 +497,7 @@ class Utils {
|
|
|
645
497
|
param.value = schema.default;
|
|
646
498
|
}
|
|
647
499
|
}
|
|
648
|
-
static parseApiInfo(operationItem,
|
|
500
|
+
static parseApiInfo(operationItem, options) {
|
|
649
501
|
var _a, _b;
|
|
650
502
|
const requiredParams = [];
|
|
651
503
|
const optionalParams = [];
|
|
@@ -659,11 +511,12 @@ class Utils {
|
|
|
659
511
|
description: ((_a = param.description) !== null && _a !== void 0 ? _a : "").slice(0, ConstantString.ParameterDescriptionMaxLens),
|
|
660
512
|
};
|
|
661
513
|
const schema = param.schema;
|
|
662
|
-
if (allowMultipleParameters && schema) {
|
|
514
|
+
if (options.allowMultipleParameters && schema) {
|
|
663
515
|
Utils.updateParameterWithInputType(schema, parameter);
|
|
664
516
|
}
|
|
665
517
|
if (param.in !== "header" && param.in !== "cookie") {
|
|
666
518
|
if (param.required && (schema === null || schema === void 0 ? void 0 : schema.default) === undefined) {
|
|
519
|
+
parameter.isRequired = true;
|
|
667
520
|
requiredParams.push(parameter);
|
|
668
521
|
}
|
|
669
522
|
else {
|
|
@@ -677,19 +530,13 @@ class Utils {
|
|
|
677
530
|
const requestJson = requestBody.content["application/json"];
|
|
678
531
|
if (Object.keys(requestJson).length !== 0) {
|
|
679
532
|
const schema = requestJson.schema;
|
|
680
|
-
const [requiredP, optionalP] = Utils.generateParametersFromSchema(schema, "requestBody", allowMultipleParameters, requestBody.required);
|
|
533
|
+
const [requiredP, optionalP] = Utils.generateParametersFromSchema(schema, "requestBody", !!options.allowMultipleParameters, requestBody.required);
|
|
681
534
|
requiredParams.push(...requiredP);
|
|
682
535
|
optionalParams.push(...optionalP);
|
|
683
536
|
}
|
|
684
537
|
}
|
|
685
538
|
const operationId = operationItem.operationId;
|
|
686
|
-
const parameters = [];
|
|
687
|
-
if (requiredParams.length !== 0) {
|
|
688
|
-
parameters.push(...requiredParams);
|
|
689
|
-
}
|
|
690
|
-
else {
|
|
691
|
-
parameters.push(optionalParams[0]);
|
|
692
|
-
}
|
|
539
|
+
const parameters = [...requiredParams, ...optionalParams];
|
|
693
540
|
const command = {
|
|
694
541
|
context: ["compose"],
|
|
695
542
|
type: "query",
|
|
@@ -698,344 +545,575 @@ class Utils {
|
|
|
698
545
|
parameters: parameters,
|
|
699
546
|
description: ((_b = operationItem.description) !== null && _b !== void 0 ? _b : "").slice(0, ConstantString.CommandDescriptionMaxLens),
|
|
700
547
|
};
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
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 "";
|
|
560
|
+
}
|
|
561
|
+
let safeRegistrationIdEnvName = authName.toUpperCase().replace(/[^A-Z0-9_]/g, "_");
|
|
562
|
+
if (!safeRegistrationIdEnvName.match(/^[A-Z]/)) {
|
|
563
|
+
safeRegistrationIdEnvName = "PREFIX_" + safeRegistrationIdEnvName;
|
|
708
564
|
}
|
|
709
|
-
return
|
|
565
|
+
return safeRegistrationIdEnvName;
|
|
710
566
|
}
|
|
711
|
-
static
|
|
712
|
-
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;
|
|
713
586
|
const result = {};
|
|
714
587
|
for (const path in paths) {
|
|
715
588
|
const methods = paths[path];
|
|
716
589
|
for (const method in methods) {
|
|
717
|
-
|
|
718
|
-
if (
|
|
719
|
-
const
|
|
720
|
-
result[`${method.toUpperCase()} ${path}`] =
|
|
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
|
+
};
|
|
721
598
|
}
|
|
722
599
|
}
|
|
723
600
|
}
|
|
601
|
+
this.apiMap = result;
|
|
724
602
|
return result;
|
|
725
603
|
}
|
|
726
|
-
|
|
727
|
-
const
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
});
|
|
734
|
-
}
|
|
735
|
-
// Server validation
|
|
736
|
-
const serverErrors = Utils.validateServer(spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2, isCopilot);
|
|
737
|
-
errors.push(...serverErrors);
|
|
738
|
-
// Remote reference not supported
|
|
739
|
-
const refPaths = parser.$refs.paths();
|
|
740
|
-
// refPaths [0] is the current spec file path
|
|
741
|
-
if (refPaths.length > 1) {
|
|
742
|
-
errors.push({
|
|
743
|
-
type: exports.ErrorType.RemoteRefNotSupported,
|
|
744
|
-
content: Utils.format(ConstantString.RemoteRefNotSupported, refPaths.join(", ")),
|
|
745
|
-
data: refPaths,
|
|
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,
|
|
746
611
|
});
|
|
747
612
|
}
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
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({
|
|
752
633
|
type: exports.ErrorType.NoSupportedApi,
|
|
753
634
|
content: ConstantString.NoSupportedApi,
|
|
635
|
+
data,
|
|
754
636
|
});
|
|
755
637
|
}
|
|
638
|
+
return result;
|
|
639
|
+
}
|
|
640
|
+
validateSpecOperationId() {
|
|
641
|
+
const result = { errors: [], warnings: [] };
|
|
642
|
+
const apiMap = this.listAPIs();
|
|
756
643
|
// OperationId missing
|
|
757
644
|
const apisMissingOperationId = [];
|
|
758
645
|
for (const key in apiMap) {
|
|
759
|
-
const
|
|
760
|
-
if (!
|
|
646
|
+
const { operation } = apiMap[key];
|
|
647
|
+
if (!operation.operationId) {
|
|
761
648
|
apisMissingOperationId.push(key);
|
|
762
649
|
}
|
|
763
650
|
}
|
|
764
651
|
if (apisMissingOperationId.length > 0) {
|
|
765
|
-
warnings.push({
|
|
652
|
+
result.warnings.push({
|
|
766
653
|
type: exports.WarningType.OperationIdMissing,
|
|
767
654
|
content: Utils.format(ConstantString.MissingOperationId, apisMissingOperationId.join(", ")),
|
|
768
655
|
data: apisMissingOperationId,
|
|
769
656
|
});
|
|
770
657
|
}
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
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;
|
|
774
666
|
}
|
|
775
|
-
|
|
776
|
-
|
|
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;
|
|
777
672
|
}
|
|
778
|
-
return
|
|
779
|
-
status,
|
|
780
|
-
warnings,
|
|
781
|
-
errors,
|
|
782
|
-
};
|
|
673
|
+
return result;
|
|
783
674
|
}
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
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;
|
|
790
690
|
}
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
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);
|
|
794
697
|
}
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
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));
|
|
798
702
|
}
|
|
799
|
-
return
|
|
703
|
+
return result;
|
|
800
704
|
}
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
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: [] };
|
|
819
729
|
}
|
|
820
730
|
}
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
return { isValid: false, reason: [exports.ErrorType.AuthTypeIsNotSupported] };
|
|
734
|
+
}
|
|
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;
|
|
825
770
|
}
|
|
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);
|
|
826
776
|
}
|
|
827
|
-
newSpec.paths = newPaths;
|
|
828
|
-
return newSpec;
|
|
829
777
|
}
|
|
830
|
-
|
|
831
|
-
|
|
778
|
+
else {
|
|
779
|
+
if (isRequiredWithoutDefault && !isCopilot) {
|
|
780
|
+
paramResult.isValid = false;
|
|
781
|
+
paramResult.reason.push(exports.ErrorType.PostBodyContainsRequiredUnsupportedSchema);
|
|
782
|
+
}
|
|
832
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;
|
|
809
|
+
}
|
|
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;
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
}
|
|
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
|
+
}
|
|
851
|
+
}
|
|
852
|
+
return false;
|
|
833
853
|
}
|
|
834
854
|
}
|
|
835
855
|
|
|
836
856
|
// Copyright (c) Microsoft Corporation.
|
|
837
|
-
class
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
{
|
|
844
|
-
pluginFile: apiPluginRelativePath,
|
|
845
|
-
},
|
|
846
|
-
];
|
|
847
|
-
ManifestUpdater.updateManifestDescription(manifest, spec);
|
|
848
|
-
const specRelativePath = ManifestUpdater.getRelativePath(manifestPath, outputSpecPath);
|
|
849
|
-
const apiPlugin = ManifestUpdater.generatePluginManifestSchema(spec, specRelativePath);
|
|
850
|
-
return [manifest, apiPlugin];
|
|
851
|
-
});
|
|
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;
|
|
852
863
|
}
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
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;
|
|
859
879
|
}
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
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);
|
|
868
912
|
}
|
|
869
|
-
|
|
870
|
-
|
|
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;
|
|
871
919
|
}
|
|
872
|
-
return
|
|
920
|
+
return result;
|
|
873
921
|
}
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
const schema = param.schema;
|
|
900
|
-
parameters.properties[param.name] = ManifestUpdater.mapOpenAPISchemaToFuncParam(schema, method, pathUrl);
|
|
901
|
-
if (param.required) {
|
|
902
|
-
parameters.required.push(param.name);
|
|
903
|
-
}
|
|
904
|
-
if (!parameters.properties[param.name].description) {
|
|
905
|
-
parameters.properties[param.name].description = (_b = param.description) !== null && _b !== void 0 ? _b : "";
|
|
906
|
-
}
|
|
907
|
-
}
|
|
908
|
-
}
|
|
909
|
-
if (requestBody) {
|
|
910
|
-
const requestJsonBody = requestBody.content["application/json"];
|
|
911
|
-
const requestBodySchema = requestJsonBody.schema;
|
|
912
|
-
if (requestBodySchema.type === "object") {
|
|
913
|
-
if (requestBodySchema.required) {
|
|
914
|
-
parameters.required.push(...requestBodySchema.required);
|
|
915
|
-
}
|
|
916
|
-
for (const property in requestBodySchema.properties) {
|
|
917
|
-
const schema = requestBodySchema.properties[property];
|
|
918
|
-
parameters.properties[property] = ManifestUpdater.mapOpenAPISchemaToFuncParam(schema, method, pathUrl);
|
|
919
|
-
}
|
|
920
|
-
}
|
|
921
|
-
else {
|
|
922
|
-
throw new SpecParserError(Utils.format(ConstantString.UnsupportedSchema, method, pathUrl, JSON.stringify(requestBodySchema)), exports.ErrorType.UpdateManifestFailed);
|
|
923
|
-
}
|
|
924
|
-
}
|
|
925
|
-
const funcObj = {
|
|
926
|
-
name: operationId,
|
|
927
|
-
description: description,
|
|
928
|
-
parameters: parameters,
|
|
929
|
-
};
|
|
930
|
-
functions.push(funcObj);
|
|
931
|
-
functionNames.push(operationId);
|
|
932
|
-
}
|
|
933
|
-
}
|
|
934
|
-
}
|
|
935
|
-
}
|
|
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);
|
|
936
947
|
}
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
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: [],
|
|
954
977
|
};
|
|
955
|
-
|
|
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;
|
|
1002
|
+
}
|
|
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);
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
else if (totalParams === 0) {
|
|
1014
|
+
result.reason.push(exports.ErrorType.NoParameter);
|
|
1015
|
+
}
|
|
1016
|
+
return result;
|
|
1017
|
+
}
|
|
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) {
|
|
1064
|
+
var _a;
|
|
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
|
+
}
|
|
956
1076
|
}
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
};
|
|
978
|
-
}
|
|
979
|
-
else if (Utils.isBearerTokenAuth(auth)) {
|
|
980
|
-
composeExtension.authorization = {
|
|
981
|
-
authType: "microsoftEntra",
|
|
982
|
-
microsoftEntraConfiguration: {
|
|
983
|
-
supportsSingleSignOn: true,
|
|
984
|
-
},
|
|
985
|
-
};
|
|
986
|
-
updatedPart.webApplicationInfo = {
|
|
987
|
-
id: "${{AAD_APP_CLIENT_ID}}",
|
|
988
|
-
resource: "api://${{DOMAIN}}/${{AAD_APP_CLIENT_ID}}",
|
|
989
|
-
};
|
|
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;
|
|
990
1097
|
}
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
const updatedManifest = Object.assign(Object.assign({}, originalManifest), updatedPart);
|
|
996
|
-
return [updatedManifest, warnings];
|
|
997
|
-
}
|
|
998
|
-
catch (err) {
|
|
999
|
-
throw new SpecParserError(err.toString(), exports.ErrorType.UpdateManifestFailed);
|
|
1000
|
-
}
|
|
1001
|
-
});
|
|
1002
|
-
}
|
|
1003
|
-
static generateCommands(spec, adaptiveCardFolder, manifestPath, allowMultipleParameters) {
|
|
1004
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
1005
|
-
const paths = spec.paths;
|
|
1006
|
-
const commands = [];
|
|
1007
|
-
const warnings = [];
|
|
1008
|
-
if (paths) {
|
|
1009
|
-
for (const pathUrl in paths) {
|
|
1010
|
-
const pathItem = paths[pathUrl];
|
|
1011
|
-
if (pathItem) {
|
|
1012
|
-
const operations = pathItem;
|
|
1013
|
-
// Currently only support GET and POST method
|
|
1014
|
-
for (const method in operations) {
|
|
1015
|
-
if (method === ConstantString.PostMethod || method === ConstantString.GetMethod) {
|
|
1016
|
-
const operationItem = operations[method];
|
|
1017
|
-
if (operationItem) {
|
|
1018
|
-
const [command, warning] = Utils.parseApiInfo(operationItem, allowMultipleParameters);
|
|
1019
|
-
const adaptiveCardPath = path__default['default'].join(adaptiveCardFolder, command.id + ".json");
|
|
1020
|
-
command.apiResponseRenderingTemplateFile = (yield fs__default['default'].pathExists(adaptiveCardPath))
|
|
1021
|
-
? ManifestUpdater.getRelativePath(manifestPath, adaptiveCardPath)
|
|
1022
|
-
: "";
|
|
1023
|
-
if (warning) {
|
|
1024
|
-
warnings.push(warning);
|
|
1025
|
-
}
|
|
1026
|
-
commands.push(command);
|
|
1027
|
-
}
|
|
1028
|
-
}
|
|
1098
|
+
if (!newPaths[path]) {
|
|
1099
|
+
newPaths[path] = Object.assign({}, unResolveSpec.paths[path]);
|
|
1100
|
+
for (const m of ConstantString.AllOperationMethods) {
|
|
1101
|
+
delete newPaths[path][m];
|
|
1029
1102
|
}
|
|
1030
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
|
+
}
|
|
1031
1109
|
}
|
|
1032
1110
|
}
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1111
|
+
newSpec.paths = newPaths;
|
|
1112
|
+
return newSpec;
|
|
1113
|
+
}
|
|
1114
|
+
catch (err) {
|
|
1115
|
+
throw new SpecParserError(err.toString(), exports.ErrorType.FilterSpecFailed);
|
|
1116
|
+
}
|
|
1039
1117
|
}
|
|
1040
1118
|
}
|
|
1041
1119
|
|
|
@@ -1043,7 +1121,7 @@ class ManifestUpdater {
|
|
|
1043
1121
|
class AdaptiveCardGenerator {
|
|
1044
1122
|
static generateAdaptiveCard(operationItem) {
|
|
1045
1123
|
try {
|
|
1046
|
-
const json = Utils.getResponseJson(operationItem);
|
|
1124
|
+
const { json } = Utils.getResponseJson(operationItem);
|
|
1047
1125
|
let cardBody = [];
|
|
1048
1126
|
let schema = json.schema;
|
|
1049
1127
|
let jsonPath = "$";
|
|
@@ -1209,6 +1287,27 @@ function wrapAdaptiveCard(card, jsonPath) {
|
|
|
1209
1287
|
};
|
|
1210
1288
|
return result;
|
|
1211
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
|
+
}
|
|
1212
1311
|
/**
|
|
1213
1312
|
* Infers the preview card template from an Adaptive Card and a JSON path.
|
|
1214
1313
|
* The preview card template includes a title and an optional subtitle and image.
|
|
@@ -1221,11 +1320,29 @@ function wrapAdaptiveCard(card, jsonPath) {
|
|
|
1221
1320
|
* @returns The inferred preview card template.
|
|
1222
1321
|
*/
|
|
1223
1322
|
function inferPreviewCardTemplate(card) {
|
|
1224
|
-
var _a;
|
|
1225
1323
|
const result = {
|
|
1226
|
-
title: "",
|
|
1324
|
+
title: "result",
|
|
1227
1325
|
};
|
|
1228
|
-
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();
|
|
1229
1346
|
let rootObject;
|
|
1230
1347
|
if (((_a = card.body[0]) === null || _a === void 0 ? void 0 : _a.type) === ConstantString.ContainerType) {
|
|
1231
1348
|
rootObject = card.body[0].items;
|
|
@@ -1238,56 +1355,380 @@ function inferPreviewCardTemplate(card) {
|
|
|
1238
1355
|
const textElement = element;
|
|
1239
1356
|
const index = textElement.text.indexOf("${if(");
|
|
1240
1357
|
if (index > 0) {
|
|
1241
|
-
|
|
1242
|
-
|
|
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);
|
|
1243
1372
|
}
|
|
1244
1373
|
}
|
|
1245
1374
|
}
|
|
1246
|
-
for (const
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
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);
|
|
1251
1379
|
}
|
|
1252
1380
|
else if (!result.subtitle &&
|
|
1253
|
-
Utils.isWellKnownName(
|
|
1254
|
-
result.subtitle =
|
|
1255
|
-
|
|
1381
|
+
Utils.isWellKnownName(name, ConstantString.WellknownSubtitleName)) {
|
|
1382
|
+
result.subtitle = name;
|
|
1383
|
+
nameSet.delete(name);
|
|
1256
1384
|
}
|
|
1257
|
-
else if (!result.
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
if (property) {
|
|
1261
|
-
result.image = {
|
|
1262
|
-
url: `\${${property}}`,
|
|
1263
|
-
alt: text,
|
|
1264
|
-
$when: `\${${property} != null}`,
|
|
1265
|
-
};
|
|
1266
|
-
}
|
|
1267
|
-
textBlockElements.delete(element);
|
|
1385
|
+
else if (!result.imageUrl && Utils.isWellKnownName(name, ConstantString.WellknownImageName)) {
|
|
1386
|
+
result.imageUrl = name;
|
|
1387
|
+
nameSet.delete(name);
|
|
1268
1388
|
}
|
|
1269
1389
|
}
|
|
1270
|
-
for (const
|
|
1271
|
-
const text = element.text;
|
|
1390
|
+
for (const name of nameSet) {
|
|
1272
1391
|
if (!result.title) {
|
|
1273
|
-
result.title =
|
|
1274
|
-
|
|
1392
|
+
result.title = name;
|
|
1393
|
+
nameSet.delete(name);
|
|
1275
1394
|
}
|
|
1276
1395
|
else if (!result.subtitle) {
|
|
1277
|
-
result.subtitle =
|
|
1278
|
-
|
|
1396
|
+
result.subtitle = name;
|
|
1397
|
+
nameSet.delete(name);
|
|
1279
1398
|
}
|
|
1280
1399
|
}
|
|
1281
1400
|
if (!result.title && result.subtitle) {
|
|
1282
1401
|
result.title = result.subtitle;
|
|
1283
1402
|
delete result.subtitle;
|
|
1284
1403
|
}
|
|
1285
|
-
if (!result.title) {
|
|
1286
|
-
result.title = "result";
|
|
1287
|
-
}
|
|
1288
1404
|
return result;
|
|
1289
1405
|
}
|
|
1290
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
|
+
|
|
1291
1732
|
// Copyright (c) Microsoft Corporation.
|
|
1292
1733
|
/**
|
|
1293
1734
|
* A class that parses an OpenAPI specification file and provides methods to validate, list, and generate artifacts.
|
|
@@ -1303,9 +1744,15 @@ class SpecParser {
|
|
|
1303
1744
|
allowMissingId: true,
|
|
1304
1745
|
allowSwagger: true,
|
|
1305
1746
|
allowAPIKeyAuth: false,
|
|
1747
|
+
allowBearerTokenAuth: false,
|
|
1306
1748
|
allowMultipleParameters: false,
|
|
1307
1749
|
allowOauth2: false,
|
|
1308
|
-
|
|
1750
|
+
allowMethods: ["get", "post"],
|
|
1751
|
+
allowConversationStarters: false,
|
|
1752
|
+
allowResponseSemantics: false,
|
|
1753
|
+
allowConfirmation: false,
|
|
1754
|
+
projectType: exports.ProjectType.SME,
|
|
1755
|
+
isGptPlugin: false,
|
|
1309
1756
|
};
|
|
1310
1757
|
this.pathOrSpec = pathOrDoc;
|
|
1311
1758
|
this.parser = new SwaggerParser__default['default']();
|
|
@@ -1330,6 +1777,8 @@ class SpecParser {
|
|
|
1330
1777
|
errors: [{ type: exports.ErrorType.SpecNotValid, content: e.toString() }],
|
|
1331
1778
|
};
|
|
1332
1779
|
}
|
|
1780
|
+
const errors = [];
|
|
1781
|
+
const warnings = [];
|
|
1333
1782
|
if (!this.options.allowSwagger && this.isSwaggerFile) {
|
|
1334
1783
|
return {
|
|
1335
1784
|
status: exports.ValidationStatus.Error,
|
|
@@ -1339,7 +1788,38 @@ class SpecParser {
|
|
|
1339
1788
|
],
|
|
1340
1789
|
};
|
|
1341
1790
|
}
|
|
1342
|
-
|
|
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
|
+
};
|
|
1343
1823
|
}
|
|
1344
1824
|
catch (err) {
|
|
1345
1825
|
throw new SpecParserError(err.toString(), exports.ErrorType.ValidateFailed);
|
|
@@ -1363,39 +1843,40 @@ class SpecParser {
|
|
|
1363
1843
|
try {
|
|
1364
1844
|
yield this.loadSpec();
|
|
1365
1845
|
const spec = this.spec;
|
|
1366
|
-
const apiMap = this.
|
|
1367
|
-
const result =
|
|
1846
|
+
const apiMap = this.getAPIs(spec);
|
|
1847
|
+
const result = {
|
|
1848
|
+
APIs: [],
|
|
1849
|
+
allAPICount: 0,
|
|
1850
|
+
validAPICount: 0,
|
|
1851
|
+
};
|
|
1368
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)}`;
|
|
1369
1856
|
const apiResult = {
|
|
1370
|
-
api:
|
|
1857
|
+
api: apiKey,
|
|
1371
1858
|
server: "",
|
|
1372
|
-
operationId:
|
|
1859
|
+
operationId: operationId,
|
|
1860
|
+
isValid: isValid,
|
|
1861
|
+
reason: reason,
|
|
1373
1862
|
};
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
if (!operationId) {
|
|
1386
|
-
operationId = `${method.toLowerCase()}${Utils.convertPathToCamelCase(path)}`;
|
|
1387
|
-
}
|
|
1388
|
-
apiResult.operationId = operationId;
|
|
1389
|
-
const authArray = Utils.getAuthArray(operation.security, spec);
|
|
1390
|
-
for (const auths of authArray) {
|
|
1391
|
-
if (auths.length === 1) {
|
|
1392
|
-
apiResult.auth = auths[0].authSchema;
|
|
1393
|
-
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
|
+
}
|
|
1394
1874
|
}
|
|
1395
1875
|
}
|
|
1396
|
-
apiResult
|
|
1397
|
-
result.push(apiResult);
|
|
1876
|
+
result.APIs.push(apiResult);
|
|
1398
1877
|
}
|
|
1878
|
+
result.allAPICount = result.APIs.length;
|
|
1879
|
+
result.validAPICount = result.APIs.filter((api) => api.isValid).length;
|
|
1399
1880
|
return result;
|
|
1400
1881
|
}
|
|
1401
1882
|
catch (err) {
|
|
@@ -1420,7 +1901,7 @@ class SpecParser {
|
|
|
1420
1901
|
if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
|
|
1421
1902
|
throw new SpecParserError(ConstantString.CancelledMessage, exports.ErrorType.Cancelled);
|
|
1422
1903
|
}
|
|
1423
|
-
const newUnResolvedSpec = SpecFilter.specFilter(filter, this.unResolveSpec, this.spec, this.options
|
|
1904
|
+
const newUnResolvedSpec = SpecFilter.specFilter(filter, this.unResolveSpec, this.spec, this.options);
|
|
1424
1905
|
if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
|
|
1425
1906
|
throw new SpecParserError(ConstantString.CancelledMessage, exports.ErrorType.Cancelled);
|
|
1426
1907
|
}
|
|
@@ -1452,18 +1933,12 @@ class SpecParser {
|
|
|
1452
1933
|
const newSpecs = yield this.getFilteredSpecs(filter, signal);
|
|
1453
1934
|
const newUnResolvedSpec = newSpecs[0];
|
|
1454
1935
|
const newSpec = newSpecs[1];
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
resultStr = jsyaml__default['default'].dump(newUnResolvedSpec);
|
|
1458
|
-
}
|
|
1459
|
-
else {
|
|
1460
|
-
resultStr = JSON.stringify(newUnResolvedSpec, null, 2);
|
|
1461
|
-
}
|
|
1462
|
-
yield fs__default['default'].outputFile(outputSpecPath, resultStr);
|
|
1936
|
+
const authInfo = Utils.getAuthInfo(newSpec);
|
|
1937
|
+
yield this.saveFilterSpec(outputSpecPath, newUnResolvedSpec);
|
|
1463
1938
|
if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
|
|
1464
1939
|
throw new SpecParserError(ConstantString.CancelledMessage, exports.ErrorType.Cancelled);
|
|
1465
1940
|
}
|
|
1466
|
-
const [updatedManifest, apiPlugin] = yield ManifestUpdater.updateManifestWithAiPlugin(manifestPath, outputSpecPath, pluginFilePath, newSpec);
|
|
1941
|
+
const [updatedManifest, apiPlugin] = yield ManifestUpdater.updateManifestWithAiPlugin(manifestPath, outputSpecPath, pluginFilePath, newSpec, this.options, authInfo);
|
|
1467
1942
|
yield fs__default['default'].outputJSON(manifestPath, updatedManifest, { spaces: 2 });
|
|
1468
1943
|
yield fs__default['default'].outputJSON(pluginFilePath, apiPlugin, { spaces: 2 });
|
|
1469
1944
|
}
|
|
@@ -1482,9 +1957,8 @@ class SpecParser {
|
|
|
1482
1957
|
* @param filter An array of strings that represent the filters to apply when generating the artifacts. If filter is empty, it would process nothing.
|
|
1483
1958
|
* @param outputSpecPath File path of the new OpenAPI specification file to generate. If not specified or empty, no spec file will be generated.
|
|
1484
1959
|
* @param adaptiveCardFolder Folder path where the Adaptive Card files will be generated. If not specified or empty, Adaptive Card files will not be generated.
|
|
1485
|
-
* @param isMe Boolean that indicates whether the project is an Messaging Extension. For Messaging Extension, composeExtensions will be added in Teams app manifest.
|
|
1486
1960
|
*/
|
|
1487
|
-
generate(manifestPath, filter, outputSpecPath, adaptiveCardFolder, signal
|
|
1961
|
+
generate(manifestPath, filter, outputSpecPath, adaptiveCardFolder, signal) {
|
|
1488
1962
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1489
1963
|
const result = {
|
|
1490
1964
|
allSuccess: true,
|
|
@@ -1494,38 +1968,16 @@ class SpecParser {
|
|
|
1494
1968
|
const newSpecs = yield this.getFilteredSpecs(filter, signal);
|
|
1495
1969
|
const newUnResolvedSpec = newSpecs[0];
|
|
1496
1970
|
const newSpec = newSpecs[1];
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
for (const method in newSpec.paths[url]) {
|
|
1501
|
-
const operation = newSpec.paths[url][method];
|
|
1502
|
-
const authArray = Utils.getAuthArray(operation.security, newSpec);
|
|
1503
|
-
if (authArray && authArray.length > 0) {
|
|
1504
|
-
AuthSet.add(authArray[0][0].authSchema);
|
|
1505
|
-
if (AuthSet.size > 1) {
|
|
1506
|
-
hasMultipleAPIKeyAuth = true;
|
|
1507
|
-
break;
|
|
1508
|
-
}
|
|
1509
|
-
}
|
|
1510
|
-
}
|
|
1511
|
-
}
|
|
1512
|
-
if (hasMultipleAPIKeyAuth) {
|
|
1513
|
-
throw new SpecParserError(ConstantString.MultipleAPIKeyNotSupported, exports.ErrorType.MultipleAPIKeyNotSupported);
|
|
1514
|
-
}
|
|
1515
|
-
let resultStr;
|
|
1516
|
-
if (outputSpecPath.endsWith(".yaml") || outputSpecPath.endsWith(".yml")) {
|
|
1517
|
-
resultStr = jsyaml__default['default'].dump(newUnResolvedSpec);
|
|
1971
|
+
let authInfo = undefined;
|
|
1972
|
+
if (this.options.projectType === exports.ProjectType.SME) {
|
|
1973
|
+
authInfo = Utils.getAuthInfo(newSpec);
|
|
1518
1974
|
}
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
}
|
|
1522
|
-
yield fs__default['default'].outputFile(outputSpecPath, resultStr);
|
|
1523
|
-
if (isMe === undefined || isMe === true) {
|
|
1524
|
-
// Only generate adaptive card for Messaging Extension
|
|
1975
|
+
yield this.saveFilterSpec(outputSpecPath, newUnResolvedSpec);
|
|
1976
|
+
if (adaptiveCardFolder) {
|
|
1525
1977
|
for (const url in newSpec.paths) {
|
|
1526
1978
|
for (const method in newSpec.paths[url]) {
|
|
1527
|
-
// paths object may contain description/summary, so we need to check if it is a operation object
|
|
1528
|
-
if (
|
|
1979
|
+
// paths object may contain description/summary which is not a http method, so we need to check if it is a operation object
|
|
1980
|
+
if (this.options.allowMethods.includes(method)) {
|
|
1529
1981
|
const operation = newSpec.paths[url][method];
|
|
1530
1982
|
try {
|
|
1531
1983
|
const [card, jsonPath] = AdaptiveCardGenerator.generateAdaptiveCard(operation);
|
|
@@ -1550,8 +2002,7 @@ class SpecParser {
|
|
|
1550
2002
|
if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
|
|
1551
2003
|
throw new SpecParserError(ConstantString.CancelledMessage, exports.ErrorType.Cancelled);
|
|
1552
2004
|
}
|
|
1553
|
-
const
|
|
1554
|
-
const [updatedManifest, warnings] = yield ManifestUpdater.updateManifest(manifestPath, outputSpecPath, adaptiveCardFolder, newSpec, this.options.allowMultipleParameters, auth, isMe);
|
|
2005
|
+
const [updatedManifest, warnings] = yield ManifestUpdater.updateManifest(manifestPath, outputSpecPath, newSpec, this.options, adaptiveCardFolder, authInfo);
|
|
1555
2006
|
yield fs__default['default'].outputJSON(manifestPath, updatedManifest, { spaces: 2 });
|
|
1556
2007
|
result.warnings.push(...warnings);
|
|
1557
2008
|
}
|
|
@@ -1579,13 +2030,30 @@ class SpecParser {
|
|
|
1579
2030
|
}
|
|
1580
2031
|
});
|
|
1581
2032
|
}
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
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;
|
|
1585
2041
|
}
|
|
1586
|
-
const
|
|
1587
|
-
this.
|
|
1588
|
-
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
|
+
});
|
|
1589
2057
|
}
|
|
1590
2058
|
}
|
|
1591
2059
|
|