@microsoft/m365-spec-parser 0.1.1-alpha.78701ec6a.0 → 0.1.1-alpha.87f45d762.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 +803 -299
- package/dist/index.esm2017.js.map +1 -1
- package/dist/index.esm2017.mjs +983 -409
- package/dist/index.esm2017.mjs.map +1 -1
- package/dist/index.esm5.js +807 -299
- package/dist/index.esm5.js.map +1 -1
- package/dist/index.node.cjs.js +991 -408
- package/dist/index.node.cjs.js.map +1 -1
- package/dist/src/constants.d.ts +6 -1
- package/dist/src/index.browser.d.ts +2 -1
- package/dist/src/index.d.ts +2 -1
- package/dist/src/interfaces.d.ts +90 -18
- package/dist/src/manifestUpdater.d.ts +9 -4
- package/dist/src/specFilter.d.ts +2 -1
- package/dist/src/specParser.browser.d.ts +18 -2
- package/dist/src/specParser.d.ts +18 -3
- package/dist/src/utils.d.ts +18 -34
- package/package.json +4 -4
package/dist/index.esm2017.mjs
CHANGED
|
@@ -19,7 +19,8 @@ var ErrorType;
|
|
|
19
19
|
ErrorType["NoExtraAPICanBeAdded"] = "no-extra-api-can-be-added";
|
|
20
20
|
ErrorType["ResolveServerUrlFailed"] = "resolve-server-url-failed";
|
|
21
21
|
ErrorType["SwaggerNotSupported"] = "swagger-not-supported";
|
|
22
|
-
ErrorType["
|
|
22
|
+
ErrorType["MultipleAuthNotSupported"] = "multiple-auth-not-supported";
|
|
23
|
+
ErrorType["SpecVersionNotSupported"] = "spec-version-not-supported";
|
|
23
24
|
ErrorType["ListFailed"] = "list-failed";
|
|
24
25
|
ErrorType["listSupportedAPIInfoFailed"] = "list-supported-api-info-failed";
|
|
25
26
|
ErrorType["FilterSpecFailed"] = "filter-spec-failed";
|
|
@@ -27,6 +28,22 @@ var ErrorType;
|
|
|
27
28
|
ErrorType["GenerateAdaptiveCardFailed"] = "generate-adaptive-card-failed";
|
|
28
29
|
ErrorType["GenerateFailed"] = "generate-failed";
|
|
29
30
|
ErrorType["ValidateFailed"] = "validate-failed";
|
|
31
|
+
ErrorType["GetSpecFailed"] = "get-spec-failed";
|
|
32
|
+
ErrorType["AuthTypeIsNotSupported"] = "auth-type-is-not-supported";
|
|
33
|
+
ErrorType["MissingOperationId"] = "missing-operation-id";
|
|
34
|
+
ErrorType["PostBodyContainMultipleMediaTypes"] = "post-body-contain-multiple-media-types";
|
|
35
|
+
ErrorType["ResponseContainMultipleMediaTypes"] = "response-contain-multiple-media-types";
|
|
36
|
+
ErrorType["ResponseJsonIsEmpty"] = "response-json-is-empty";
|
|
37
|
+
ErrorType["PostBodySchemaIsNotJson"] = "post-body-schema-is-not-json";
|
|
38
|
+
ErrorType["PostBodyContainsRequiredUnsupportedSchema"] = "post-body-contains-required-unsupported-schema";
|
|
39
|
+
ErrorType["ParamsContainRequiredUnsupportedSchema"] = "params-contain-required-unsupported-schema";
|
|
40
|
+
ErrorType["ParamsContainsNestedObject"] = "params-contains-nested-object";
|
|
41
|
+
ErrorType["RequestBodyContainsNestedObject"] = "request-body-contains-nested-object";
|
|
42
|
+
ErrorType["ExceededRequiredParamsLimit"] = "exceeded-required-params-limit";
|
|
43
|
+
ErrorType["NoParameter"] = "no-parameter";
|
|
44
|
+
ErrorType["NoAPIInfo"] = "no-api-info";
|
|
45
|
+
ErrorType["MethodNotAllowed"] = "method-not-allowed";
|
|
46
|
+
ErrorType["UrlPathNotExist"] = "url-path-not-exist";
|
|
30
47
|
ErrorType["Cancelled"] = "cancelled";
|
|
31
48
|
ErrorType["Unknown"] = "unknown";
|
|
32
49
|
})(ErrorType || (ErrorType = {}));
|
|
@@ -49,7 +66,13 @@ var ValidationStatus;
|
|
|
49
66
|
ValidationStatus[ValidationStatus["Valid"] = 0] = "Valid";
|
|
50
67
|
ValidationStatus[ValidationStatus["Warning"] = 1] = "Warning";
|
|
51
68
|
ValidationStatus[ValidationStatus["Error"] = 2] = "Error";
|
|
52
|
-
})(ValidationStatus || (ValidationStatus = {}));
|
|
69
|
+
})(ValidationStatus || (ValidationStatus = {}));
|
|
70
|
+
var ProjectType;
|
|
71
|
+
(function (ProjectType) {
|
|
72
|
+
ProjectType[ProjectType["Copilot"] = 0] = "Copilot";
|
|
73
|
+
ProjectType[ProjectType["SME"] = 1] = "SME";
|
|
74
|
+
ProjectType[ProjectType["TeamsAi"] = 2] = "TeamsAi";
|
|
75
|
+
})(ProjectType || (ProjectType = {}));
|
|
53
76
|
|
|
54
77
|
// Copyright (c) Microsoft Corporation.
|
|
55
78
|
class ConstantString {
|
|
@@ -68,7 +91,9 @@ ConstantString.ResolveServerUrlFailed = "Unable to resolve the server URL: pleas
|
|
|
68
91
|
ConstantString.OperationOnlyContainsOptionalParam = "Operation %s contains multiple optional parameters. The first optional parameter is used for this command.";
|
|
69
92
|
ConstantString.ConvertSwaggerToOpenAPI = "The Swagger 2.0 file has been converted to OpenAPI 3.0.";
|
|
70
93
|
ConstantString.SwaggerNotSupported = "Swagger 2.0 is not supported. Please convert to OpenAPI 3.0 manually before proceeding.";
|
|
71
|
-
ConstantString.
|
|
94
|
+
ConstantString.SpecVersionNotSupported = "Unsupported OpenAPI version %s. Please use version 3.0.x.";
|
|
95
|
+
ConstantString.MultipleAuthNotSupported = "Multiple authentication methods are unsupported. Ensure all selected APIs use identical authentication.";
|
|
96
|
+
ConstantString.UnsupportedSchema = "Unsupported schema in %s %s: %s";
|
|
72
97
|
ConstantString.WrappedCardVersion = "devPreview";
|
|
73
98
|
ConstantString.WrappedCardSchema = "https://developer.microsoft.com/json-schemas/teams/vDevPreview/MicrosoftTeams.ResponseRenderingTemplate.schema.json";
|
|
74
99
|
ConstantString.WrappedCardResponseLayout = "list";
|
|
@@ -80,6 +105,7 @@ ConstantString.AdaptiveCardType = "AdaptiveCard";
|
|
|
80
105
|
ConstantString.TextBlockType = "TextBlock";
|
|
81
106
|
ConstantString.ContainerType = "Container";
|
|
82
107
|
ConstantString.RegistrationIdPostfix = "REGISTRATION_ID";
|
|
108
|
+
ConstantString.OAuthRegistrationIdPostFix = "OAUTH_REGISTRATION_ID";
|
|
83
109
|
ConstantString.ResponseCodeFor20X = [
|
|
84
110
|
"200",
|
|
85
111
|
"201",
|
|
@@ -139,7 +165,9 @@ ConstantString.FullDescriptionMaxLens = 4000;
|
|
|
139
165
|
ConstantString.CommandDescriptionMaxLens = 128;
|
|
140
166
|
ConstantString.ParameterDescriptionMaxLens = 128;
|
|
141
167
|
ConstantString.CommandTitleMaxLens = 32;
|
|
142
|
-
ConstantString.ParameterTitleMaxLens = 32;
|
|
168
|
+
ConstantString.ParameterTitleMaxLens = 32;
|
|
169
|
+
ConstantString.SMERequiredParamsMaxNum = 5;
|
|
170
|
+
ConstantString.DefaultPluginId = "plugin_1";
|
|
143
171
|
|
|
144
172
|
// Copyright (c) Microsoft Corporation.
|
|
145
173
|
class SpecParserError extends Error {
|
|
@@ -151,201 +179,30 @@ class SpecParserError extends Error {
|
|
|
151
179
|
|
|
152
180
|
// Copyright (c) Microsoft Corporation.
|
|
153
181
|
class Utils {
|
|
154
|
-
static
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
};
|
|
160
|
-
if (!paramObject) {
|
|
161
|
-
return paramResult;
|
|
162
|
-
}
|
|
163
|
-
for (let i = 0; i < paramObject.length; i++) {
|
|
164
|
-
const param = paramObject[i];
|
|
165
|
-
const schema = param.schema;
|
|
166
|
-
const isRequiredWithoutDefault = param.required && schema.default === undefined;
|
|
167
|
-
if (param.in === "header" || param.in === "cookie") {
|
|
168
|
-
if (isRequiredWithoutDefault) {
|
|
169
|
-
paramResult.isValid = false;
|
|
170
|
-
}
|
|
171
|
-
continue;
|
|
172
|
-
}
|
|
173
|
-
if (schema.type !== "boolean" &&
|
|
174
|
-
schema.type !== "string" &&
|
|
175
|
-
schema.type !== "number" &&
|
|
176
|
-
schema.type !== "integer") {
|
|
177
|
-
if (isRequiredWithoutDefault) {
|
|
178
|
-
paramResult.isValid = false;
|
|
179
|
-
}
|
|
180
|
-
continue;
|
|
181
|
-
}
|
|
182
|
-
if (param.in === "query" || param.in === "path") {
|
|
183
|
-
if (isRequiredWithoutDefault) {
|
|
184
|
-
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
185
|
-
}
|
|
186
|
-
else {
|
|
187
|
-
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
return paramResult;
|
|
192
|
-
}
|
|
193
|
-
static checkPostBody(schema, isRequired = false) {
|
|
194
|
-
var _a;
|
|
195
|
-
const paramResult = {
|
|
196
|
-
requiredNum: 0,
|
|
197
|
-
optionalNum: 0,
|
|
198
|
-
isValid: true,
|
|
199
|
-
};
|
|
200
|
-
if (Object.keys(schema).length === 0) {
|
|
201
|
-
return paramResult;
|
|
202
|
-
}
|
|
203
|
-
const isRequiredWithoutDefault = isRequired && schema.default === undefined;
|
|
204
|
-
if (schema.type === "string" ||
|
|
205
|
-
schema.type === "integer" ||
|
|
206
|
-
schema.type === "boolean" ||
|
|
207
|
-
schema.type === "number") {
|
|
208
|
-
if (isRequiredWithoutDefault) {
|
|
209
|
-
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
210
|
-
}
|
|
211
|
-
else {
|
|
212
|
-
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
else if (schema.type === "object") {
|
|
216
|
-
const { properties } = schema;
|
|
217
|
-
for (const property in properties) {
|
|
218
|
-
let isRequired = false;
|
|
219
|
-
if (schema.required && ((_a = schema.required) === null || _a === void 0 ? void 0 : _a.indexOf(property)) >= 0) {
|
|
220
|
-
isRequired = true;
|
|
221
|
-
}
|
|
222
|
-
const result = Utils.checkPostBody(properties[property], isRequired);
|
|
223
|
-
paramResult.requiredNum += result.requiredNum;
|
|
224
|
-
paramResult.optionalNum += result.optionalNum;
|
|
225
|
-
paramResult.isValid = paramResult.isValid && result.isValid;
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
else {
|
|
229
|
-
if (isRequiredWithoutDefault) {
|
|
230
|
-
paramResult.isValid = false;
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
return paramResult;
|
|
234
|
-
}
|
|
235
|
-
/**
|
|
236
|
-
* Checks if the given API is supported.
|
|
237
|
-
* @param {string} method - The HTTP method of the API.
|
|
238
|
-
* @param {string} path - The path of the API.
|
|
239
|
-
* @param {OpenAPIV3.Document} spec - The OpenAPI specification document.
|
|
240
|
-
* @returns {boolean} - Returns true if the API is supported, false otherwise.
|
|
241
|
-
* @description The following APIs are supported:
|
|
242
|
-
* 1. only support Get/Post operation without auth property
|
|
243
|
-
* 2. parameter inside query or path only support string, number, boolean and integer
|
|
244
|
-
* 3. parameter inside post body only support string, number, boolean, integer and object
|
|
245
|
-
* 4. request body + required parameters <= 1
|
|
246
|
-
* 5. response body should be “application/json” and not empty, and response code should be 20X
|
|
247
|
-
* 6. only support request body with “application/json” content type
|
|
248
|
-
*/
|
|
249
|
-
static isSupportedApi(method, path, spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2) {
|
|
250
|
-
const pathObj = spec.paths[path];
|
|
251
|
-
method = method.toLocaleLowerCase();
|
|
252
|
-
if (pathObj) {
|
|
253
|
-
if ((method === ConstantString.PostMethod || method === ConstantString.GetMethod) &&
|
|
254
|
-
pathObj[method]) {
|
|
255
|
-
const securities = pathObj[method].security;
|
|
256
|
-
const authArray = Utils.getAuthArray(securities, spec);
|
|
257
|
-
if (!Utils.isSupportedAuth(authArray, allowAPIKeyAuth, allowOauth2)) {
|
|
258
|
-
return false;
|
|
259
|
-
}
|
|
260
|
-
const operationObject = pathObj[method];
|
|
261
|
-
if (!allowMissingId && !operationObject.operationId) {
|
|
262
|
-
return false;
|
|
263
|
-
}
|
|
264
|
-
const paramObject = operationObject.parameters;
|
|
265
|
-
const requestBody = operationObject.requestBody;
|
|
266
|
-
const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
|
|
267
|
-
const mediaTypesCount = Object.keys((requestBody === null || requestBody === void 0 ? void 0 : requestBody.content) || {}).length;
|
|
268
|
-
if (mediaTypesCount > 1) {
|
|
269
|
-
return false;
|
|
270
|
-
}
|
|
271
|
-
const responseJson = Utils.getResponseJson(operationObject);
|
|
272
|
-
if (Object.keys(responseJson).length === 0) {
|
|
273
|
-
return false;
|
|
274
|
-
}
|
|
275
|
-
let requestBodyParamResult = {
|
|
276
|
-
requiredNum: 0,
|
|
277
|
-
optionalNum: 0,
|
|
278
|
-
isValid: true,
|
|
279
|
-
};
|
|
280
|
-
if (requestJsonBody) {
|
|
281
|
-
const requestBodySchema = requestJsonBody.schema;
|
|
282
|
-
requestBodyParamResult = Utils.checkPostBody(requestBodySchema, requestBody.required);
|
|
283
|
-
}
|
|
284
|
-
if (!requestBodyParamResult.isValid) {
|
|
285
|
-
return false;
|
|
286
|
-
}
|
|
287
|
-
const paramResult = Utils.checkParameters(paramObject);
|
|
288
|
-
if (!paramResult.isValid) {
|
|
289
|
-
return false;
|
|
290
|
-
}
|
|
291
|
-
if (requestBodyParamResult.requiredNum + paramResult.requiredNum > 1) {
|
|
292
|
-
if (allowMultipleParameters &&
|
|
293
|
-
requestBodyParamResult.requiredNum + paramResult.requiredNum <= 5) {
|
|
294
|
-
return true;
|
|
295
|
-
}
|
|
296
|
-
return false;
|
|
297
|
-
}
|
|
298
|
-
else if (requestBodyParamResult.requiredNum +
|
|
299
|
-
requestBodyParamResult.optionalNum +
|
|
300
|
-
paramResult.requiredNum +
|
|
301
|
-
paramResult.optionalNum ===
|
|
302
|
-
0) {
|
|
303
|
-
return false;
|
|
304
|
-
}
|
|
305
|
-
else {
|
|
182
|
+
static hasNestedObjectInSchema(schema) {
|
|
183
|
+
if (schema.type === "object") {
|
|
184
|
+
for (const property in schema.properties) {
|
|
185
|
+
const nestedSchema = schema.properties[property];
|
|
186
|
+
if (nestedSchema.type === "object") {
|
|
306
187
|
return true;
|
|
307
188
|
}
|
|
308
189
|
}
|
|
309
190
|
}
|
|
310
191
|
return false;
|
|
311
192
|
}
|
|
312
|
-
static
|
|
313
|
-
|
|
314
|
-
return true;
|
|
315
|
-
}
|
|
316
|
-
if (allowAPIKeyAuth || allowOauth2) {
|
|
317
|
-
// Currently we don't support multiple auth in one operation
|
|
318
|
-
if (authSchemaArray.length > 0 && authSchemaArray.every((auths) => auths.length > 1)) {
|
|
319
|
-
return false;
|
|
320
|
-
}
|
|
321
|
-
for (const auths of authSchemaArray) {
|
|
322
|
-
if (auths.length === 1) {
|
|
323
|
-
if (!allowOauth2 && allowAPIKeyAuth && Utils.isAPIKeyAuth(auths[0].authSchema)) {
|
|
324
|
-
return true;
|
|
325
|
-
}
|
|
326
|
-
else if (!allowAPIKeyAuth &&
|
|
327
|
-
allowOauth2 &&
|
|
328
|
-
Utils.isBearerTokenAuth(auths[0].authSchema)) {
|
|
329
|
-
return true;
|
|
330
|
-
}
|
|
331
|
-
else if (allowAPIKeyAuth &&
|
|
332
|
-
allowOauth2 &&
|
|
333
|
-
(Utils.isAPIKeyAuth(auths[0].authSchema) ||
|
|
334
|
-
Utils.isBearerTokenAuth(auths[0].authSchema))) {
|
|
335
|
-
return true;
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
return false;
|
|
193
|
+
static containMultipleMediaTypes(bodyObject) {
|
|
194
|
+
return Object.keys((bodyObject === null || bodyObject === void 0 ? void 0 : bodyObject.content) || {}).length > 1;
|
|
341
195
|
}
|
|
342
|
-
static
|
|
343
|
-
return
|
|
196
|
+
static isBearerTokenAuth(authScheme) {
|
|
197
|
+
return authScheme.type === "http" && authScheme.scheme === "bearer";
|
|
344
198
|
}
|
|
345
|
-
static
|
|
346
|
-
return
|
|
347
|
-
|
|
348
|
-
|
|
199
|
+
static isAPIKeyAuth(authScheme) {
|
|
200
|
+
return authScheme.type === "apiKey";
|
|
201
|
+
}
|
|
202
|
+
static isOAuthWithAuthCodeFlow(authScheme) {
|
|
203
|
+
return !!(authScheme.type === "oauth2" &&
|
|
204
|
+
authScheme.flows &&
|
|
205
|
+
authScheme.flows.authorizationCode);
|
|
349
206
|
}
|
|
350
207
|
static getAuthArray(securities, spec) {
|
|
351
208
|
var _a;
|
|
@@ -358,7 +215,7 @@ class Utils {
|
|
|
358
215
|
for (const name in security) {
|
|
359
216
|
const auth = securitySchemas[name];
|
|
360
217
|
authArray.push({
|
|
361
|
-
|
|
218
|
+
authScheme: auth,
|
|
362
219
|
name: name,
|
|
363
220
|
});
|
|
364
221
|
}
|
|
@@ -376,18 +233,22 @@ class Utils {
|
|
|
376
233
|
static getResponseJson(operationObject) {
|
|
377
234
|
var _a, _b;
|
|
378
235
|
let json = {};
|
|
236
|
+
let multipleMediaType = false;
|
|
379
237
|
for (const code of ConstantString.ResponseCodeFor20X) {
|
|
380
238
|
const responseObject = (_a = operationObject === null || operationObject === void 0 ? void 0 : operationObject.responses) === null || _a === void 0 ? void 0 : _a[code];
|
|
381
|
-
const mediaTypesCount = Object.keys((responseObject === null || responseObject === void 0 ? void 0 : responseObject.content) || {}).length;
|
|
382
|
-
if (mediaTypesCount > 1) {
|
|
383
|
-
return {};
|
|
384
|
-
}
|
|
385
239
|
if ((_b = responseObject === null || responseObject === void 0 ? void 0 : responseObject.content) === null || _b === void 0 ? void 0 : _b["application/json"]) {
|
|
240
|
+
multipleMediaType = false;
|
|
386
241
|
json = responseObject.content["application/json"];
|
|
387
|
-
|
|
242
|
+
if (Utils.containMultipleMediaTypes(responseObject)) {
|
|
243
|
+
multipleMediaType = true;
|
|
244
|
+
json = {};
|
|
245
|
+
}
|
|
246
|
+
else {
|
|
247
|
+
break;
|
|
248
|
+
}
|
|
388
249
|
}
|
|
389
250
|
}
|
|
390
|
-
return json;
|
|
251
|
+
return { json, multipleMediaType };
|
|
391
252
|
}
|
|
392
253
|
static convertPathToCamelCase(path) {
|
|
393
254
|
const pathSegments = path.split(/[./{]/);
|
|
@@ -407,10 +268,10 @@ class Utils {
|
|
|
407
268
|
return undefined;
|
|
408
269
|
}
|
|
409
270
|
}
|
|
410
|
-
static
|
|
271
|
+
static resolveEnv(str) {
|
|
411
272
|
const placeHolderReg = /\${{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*}}/g;
|
|
412
|
-
let matches = placeHolderReg.exec(
|
|
413
|
-
let
|
|
273
|
+
let matches = placeHolderReg.exec(str);
|
|
274
|
+
let newStr = str;
|
|
414
275
|
while (matches != null) {
|
|
415
276
|
const envVar = matches[1];
|
|
416
277
|
const envVal = process.env[envVar];
|
|
@@ -418,17 +279,17 @@ class Utils {
|
|
|
418
279
|
throw new Error(Utils.format(ConstantString.ResolveServerUrlFailed, envVar));
|
|
419
280
|
}
|
|
420
281
|
else {
|
|
421
|
-
|
|
282
|
+
newStr = newStr.replace(matches[0], envVal);
|
|
422
283
|
}
|
|
423
|
-
matches = placeHolderReg.exec(
|
|
284
|
+
matches = placeHolderReg.exec(str);
|
|
424
285
|
}
|
|
425
|
-
return
|
|
286
|
+
return newStr;
|
|
426
287
|
}
|
|
427
288
|
static checkServerUrl(servers) {
|
|
428
289
|
const errors = [];
|
|
429
290
|
let serverUrl;
|
|
430
291
|
try {
|
|
431
|
-
serverUrl = Utils.
|
|
292
|
+
serverUrl = Utils.resolveEnv(servers[0].url);
|
|
432
293
|
}
|
|
433
294
|
catch (err) {
|
|
434
295
|
errors.push({
|
|
@@ -458,7 +319,8 @@ class Utils {
|
|
|
458
319
|
}
|
|
459
320
|
return errors;
|
|
460
321
|
}
|
|
461
|
-
static validateServer(spec,
|
|
322
|
+
static validateServer(spec, options) {
|
|
323
|
+
var _a;
|
|
462
324
|
const errors = [];
|
|
463
325
|
let hasTopLevelServers = false;
|
|
464
326
|
let hasPathLevelServers = false;
|
|
@@ -479,7 +341,7 @@ class Utils {
|
|
|
479
341
|
}
|
|
480
342
|
for (const method in methods) {
|
|
481
343
|
const operationObject = methods[method];
|
|
482
|
-
if (
|
|
344
|
+
if (((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && operationObject) {
|
|
483
345
|
if ((operationObject === null || operationObject === void 0 ? void 0 : operationObject.servers) && operationObject.servers.length >= 1) {
|
|
484
346
|
hasOperationLevelServers = true;
|
|
485
347
|
const serverErrors = Utils.checkServerUrl(operationObject.servers);
|
|
@@ -522,6 +384,7 @@ class Utils {
|
|
|
522
384
|
Utils.updateParameterWithInputType(schema, parameter);
|
|
523
385
|
}
|
|
524
386
|
if (isRequired && schema.default === undefined) {
|
|
387
|
+
parameter.isRequired = true;
|
|
525
388
|
requiredParams.push(parameter);
|
|
526
389
|
}
|
|
527
390
|
else {
|
|
@@ -566,7 +429,7 @@ class Utils {
|
|
|
566
429
|
param.value = schema.default;
|
|
567
430
|
}
|
|
568
431
|
}
|
|
569
|
-
static parseApiInfo(operationItem,
|
|
432
|
+
static parseApiInfo(operationItem, options) {
|
|
570
433
|
var _a, _b;
|
|
571
434
|
const requiredParams = [];
|
|
572
435
|
const optionalParams = [];
|
|
@@ -580,11 +443,12 @@ class Utils {
|
|
|
580
443
|
description: ((_a = param.description) !== null && _a !== void 0 ? _a : "").slice(0, ConstantString.ParameterDescriptionMaxLens),
|
|
581
444
|
};
|
|
582
445
|
const schema = param.schema;
|
|
583
|
-
if (allowMultipleParameters && schema) {
|
|
446
|
+
if (options.allowMultipleParameters && schema) {
|
|
584
447
|
Utils.updateParameterWithInputType(schema, parameter);
|
|
585
448
|
}
|
|
586
449
|
if (param.in !== "header" && param.in !== "cookie") {
|
|
587
450
|
if (param.required && (schema === null || schema === void 0 ? void 0 : schema.default) === undefined) {
|
|
451
|
+
parameter.isRequired = true;
|
|
588
452
|
requiredParams.push(parameter);
|
|
589
453
|
}
|
|
590
454
|
else {
|
|
@@ -598,19 +462,13 @@ class Utils {
|
|
|
598
462
|
const requestJson = requestBody.content["application/json"];
|
|
599
463
|
if (Object.keys(requestJson).length !== 0) {
|
|
600
464
|
const schema = requestJson.schema;
|
|
601
|
-
const [requiredP, optionalP] = Utils.generateParametersFromSchema(schema, "requestBody", allowMultipleParameters, requestBody.required);
|
|
465
|
+
const [requiredP, optionalP] = Utils.generateParametersFromSchema(schema, "requestBody", !!options.allowMultipleParameters, requestBody.required);
|
|
602
466
|
requiredParams.push(...requiredP);
|
|
603
467
|
optionalParams.push(...optionalP);
|
|
604
468
|
}
|
|
605
469
|
}
|
|
606
470
|
const operationId = operationItem.operationId;
|
|
607
|
-
const parameters = [];
|
|
608
|
-
if (requiredParams.length !== 0) {
|
|
609
|
-
parameters.push(...requiredParams);
|
|
610
|
-
}
|
|
611
|
-
else {
|
|
612
|
-
parameters.push(optionalParams[0]);
|
|
613
|
-
}
|
|
471
|
+
const parameters = [...requiredParams, ...optionalParams];
|
|
614
472
|
const command = {
|
|
615
473
|
context: ["compose"],
|
|
616
474
|
type: "query",
|
|
@@ -619,130 +477,568 @@ class Utils {
|
|
|
619
477
|
parameters: parameters,
|
|
620
478
|
description: ((_b = operationItem.description) !== null && _b !== void 0 ? _b : "").slice(0, ConstantString.CommandDescriptionMaxLens),
|
|
621
479
|
};
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
480
|
+
return command;
|
|
481
|
+
}
|
|
482
|
+
static format(str, ...args) {
|
|
483
|
+
let index = 0;
|
|
484
|
+
return str.replace(/%s/g, () => {
|
|
485
|
+
const arg = args[index++];
|
|
486
|
+
return arg !== undefined ? arg : "";
|
|
487
|
+
});
|
|
488
|
+
}
|
|
489
|
+
static getSafeRegistrationIdEnvName(authName) {
|
|
490
|
+
if (!authName) {
|
|
491
|
+
return "";
|
|
629
492
|
}
|
|
630
|
-
|
|
493
|
+
let safeRegistrationIdEnvName = authName.toUpperCase().replace(/[^A-Z0-9_]/g, "_");
|
|
494
|
+
if (!safeRegistrationIdEnvName.match(/^[A-Z]/)) {
|
|
495
|
+
safeRegistrationIdEnvName = "PREFIX_" + safeRegistrationIdEnvName;
|
|
496
|
+
}
|
|
497
|
+
return safeRegistrationIdEnvName;
|
|
631
498
|
}
|
|
632
|
-
static
|
|
633
|
-
const
|
|
499
|
+
static getServerObject(spec, method, path) {
|
|
500
|
+
const pathObj = spec.paths[path];
|
|
501
|
+
const operationObject = pathObj[method];
|
|
502
|
+
const rootServer = spec.servers && spec.servers[0];
|
|
503
|
+
const methodServer = spec.paths[path].servers && spec.paths[path].servers[0];
|
|
504
|
+
const operationServer = operationObject.servers && operationObject.servers[0];
|
|
505
|
+
const serverUrl = operationServer || methodServer || rootServer;
|
|
506
|
+
return serverUrl;
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
// Copyright (c) Microsoft Corporation.
|
|
511
|
+
class Validator {
|
|
512
|
+
listAPIs() {
|
|
513
|
+
var _a;
|
|
514
|
+
if (this.apiMap) {
|
|
515
|
+
return this.apiMap;
|
|
516
|
+
}
|
|
517
|
+
const paths = this.spec.paths;
|
|
634
518
|
const result = {};
|
|
635
519
|
for (const path in paths) {
|
|
636
520
|
const methods = paths[path];
|
|
637
521
|
for (const method in methods) {
|
|
638
|
-
|
|
639
|
-
if (
|
|
640
|
-
const
|
|
641
|
-
result[`${method.toUpperCase()} ${path}`] =
|
|
522
|
+
const operationObject = methods[method];
|
|
523
|
+
if (((_a = this.options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && operationObject) {
|
|
524
|
+
const validateResult = this.validateAPI(method, path);
|
|
525
|
+
result[`${method.toUpperCase()} ${path}`] = {
|
|
526
|
+
operation: operationObject,
|
|
527
|
+
isValid: validateResult.isValid,
|
|
528
|
+
reason: validateResult.reason,
|
|
529
|
+
};
|
|
642
530
|
}
|
|
643
531
|
}
|
|
644
532
|
}
|
|
533
|
+
this.apiMap = result;
|
|
645
534
|
return result;
|
|
646
535
|
}
|
|
647
|
-
|
|
648
|
-
const
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
536
|
+
validateSpecVersion() {
|
|
537
|
+
const result = { errors: [], warnings: [] };
|
|
538
|
+
if (this.spec.openapi >= "3.1.0") {
|
|
539
|
+
result.errors.push({
|
|
540
|
+
type: ErrorType.SpecVersionNotSupported,
|
|
541
|
+
content: Utils.format(ConstantString.SpecVersionNotSupported, this.spec.openapi),
|
|
542
|
+
data: this.spec.openapi,
|
|
654
543
|
});
|
|
655
544
|
}
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
const
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
545
|
+
return result;
|
|
546
|
+
}
|
|
547
|
+
validateSpecServer() {
|
|
548
|
+
const result = { errors: [], warnings: [] };
|
|
549
|
+
const serverErrors = Utils.validateServer(this.spec, this.options);
|
|
550
|
+
result.errors.push(...serverErrors);
|
|
551
|
+
return result;
|
|
552
|
+
}
|
|
553
|
+
validateSpecNoSupportAPI() {
|
|
554
|
+
const result = { errors: [], warnings: [] };
|
|
555
|
+
const apiMap = this.listAPIs();
|
|
556
|
+
const validAPIs = Object.entries(apiMap).filter(([, value]) => value.isValid);
|
|
557
|
+
if (validAPIs.length === 0) {
|
|
558
|
+
const data = [];
|
|
559
|
+
for (const key in apiMap) {
|
|
560
|
+
const { reason } = apiMap[key];
|
|
561
|
+
const apiInvalidReason = { api: key, reason: reason };
|
|
562
|
+
data.push(apiInvalidReason);
|
|
563
|
+
}
|
|
564
|
+
result.errors.push({
|
|
673
565
|
type: ErrorType.NoSupportedApi,
|
|
674
566
|
content: ConstantString.NoSupportedApi,
|
|
567
|
+
data,
|
|
675
568
|
});
|
|
676
569
|
}
|
|
570
|
+
return result;
|
|
571
|
+
}
|
|
572
|
+
validateSpecOperationId() {
|
|
573
|
+
const result = { errors: [], warnings: [] };
|
|
574
|
+
const apiMap = this.listAPIs();
|
|
677
575
|
// OperationId missing
|
|
678
576
|
const apisMissingOperationId = [];
|
|
679
577
|
for (const key in apiMap) {
|
|
680
|
-
const
|
|
681
|
-
if (!
|
|
578
|
+
const { operation } = apiMap[key];
|
|
579
|
+
if (!operation.operationId) {
|
|
682
580
|
apisMissingOperationId.push(key);
|
|
683
581
|
}
|
|
684
582
|
}
|
|
685
583
|
if (apisMissingOperationId.length > 0) {
|
|
686
|
-
warnings.push({
|
|
584
|
+
result.warnings.push({
|
|
687
585
|
type: WarningType.OperationIdMissing,
|
|
688
586
|
content: Utils.format(ConstantString.MissingOperationId, apisMissingOperationId.join(", ")),
|
|
689
587
|
data: apisMissingOperationId,
|
|
690
588
|
});
|
|
691
589
|
}
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
590
|
+
return result;
|
|
591
|
+
}
|
|
592
|
+
validateMethodAndPath(method, path) {
|
|
593
|
+
const result = { isValid: true, reason: [] };
|
|
594
|
+
if (this.options.allowMethods && !this.options.allowMethods.includes(method)) {
|
|
595
|
+
result.isValid = false;
|
|
596
|
+
result.reason.push(ErrorType.MethodNotAllowed);
|
|
597
|
+
return result;
|
|
598
|
+
}
|
|
599
|
+
const pathObj = this.spec.paths[path];
|
|
600
|
+
if (!pathObj || !pathObj[method]) {
|
|
601
|
+
result.isValid = false;
|
|
602
|
+
result.reason.push(ErrorType.UrlPathNotExist);
|
|
603
|
+
return result;
|
|
604
|
+
}
|
|
605
|
+
return result;
|
|
606
|
+
}
|
|
607
|
+
validateResponse(method, path) {
|
|
608
|
+
const result = { isValid: true, reason: [] };
|
|
609
|
+
const operationObject = this.spec.paths[path][method];
|
|
610
|
+
const { json, multipleMediaType } = Utils.getResponseJson(operationObject);
|
|
611
|
+
// only support response body only contains “application/json” content type
|
|
612
|
+
if (multipleMediaType) {
|
|
613
|
+
result.reason.push(ErrorType.ResponseContainMultipleMediaTypes);
|
|
614
|
+
}
|
|
615
|
+
else if (Object.keys(json).length === 0) {
|
|
616
|
+
// response body should not be empty
|
|
617
|
+
result.reason.push(ErrorType.ResponseJsonIsEmpty);
|
|
695
618
|
}
|
|
696
|
-
|
|
697
|
-
|
|
619
|
+
return result;
|
|
620
|
+
}
|
|
621
|
+
validateServer(method, path) {
|
|
622
|
+
const result = { isValid: true, reason: [] };
|
|
623
|
+
const serverObj = Utils.getServerObject(this.spec, method, path);
|
|
624
|
+
if (!serverObj) {
|
|
625
|
+
// should contain server URL
|
|
626
|
+
result.reason.push(ErrorType.NoServerInformation);
|
|
627
|
+
}
|
|
628
|
+
else {
|
|
629
|
+
// server url should be absolute url with https protocol
|
|
630
|
+
const serverValidateResult = Utils.checkServerUrl([serverObj]);
|
|
631
|
+
result.reason.push(...serverValidateResult.map((item) => item.type));
|
|
632
|
+
}
|
|
633
|
+
return result;
|
|
634
|
+
}
|
|
635
|
+
validateAuth(method, path) {
|
|
636
|
+
const pathObj = this.spec.paths[path];
|
|
637
|
+
const operationObject = pathObj[method];
|
|
638
|
+
const securities = operationObject.security;
|
|
639
|
+
const authSchemeArray = Utils.getAuthArray(securities, this.spec);
|
|
640
|
+
if (authSchemeArray.length === 0) {
|
|
641
|
+
return { isValid: true, reason: [] };
|
|
642
|
+
}
|
|
643
|
+
if (this.options.allowAPIKeyAuth ||
|
|
644
|
+
this.options.allowOauth2 ||
|
|
645
|
+
this.options.allowBearerTokenAuth) {
|
|
646
|
+
// Currently we don't support multiple auth in one operation
|
|
647
|
+
if (authSchemeArray.length > 0 && authSchemeArray.every((auths) => auths.length > 1)) {
|
|
648
|
+
return {
|
|
649
|
+
isValid: false,
|
|
650
|
+
reason: [ErrorType.MultipleAuthNotSupported],
|
|
651
|
+
};
|
|
652
|
+
}
|
|
653
|
+
for (const auths of authSchemeArray) {
|
|
654
|
+
if (auths.length === 1) {
|
|
655
|
+
if ((this.options.allowAPIKeyAuth && Utils.isAPIKeyAuth(auths[0].authScheme)) ||
|
|
656
|
+
(this.options.allowOauth2 && Utils.isOAuthWithAuthCodeFlow(auths[0].authScheme)) ||
|
|
657
|
+
(this.options.allowBearerTokenAuth && Utils.isBearerTokenAuth(auths[0].authScheme))) {
|
|
658
|
+
return { isValid: true, reason: [] };
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
}
|
|
698
662
|
}
|
|
699
|
-
return {
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
663
|
+
return { isValid: false, reason: [ErrorType.AuthTypeIsNotSupported] };
|
|
664
|
+
}
|
|
665
|
+
checkPostBodySchema(schema, isRequired = false) {
|
|
666
|
+
var _a;
|
|
667
|
+
const paramResult = {
|
|
668
|
+
requiredNum: 0,
|
|
669
|
+
optionalNum: 0,
|
|
670
|
+
isValid: true,
|
|
671
|
+
reason: [],
|
|
703
672
|
};
|
|
673
|
+
if (Object.keys(schema).length === 0) {
|
|
674
|
+
return paramResult;
|
|
675
|
+
}
|
|
676
|
+
const isRequiredWithoutDefault = isRequired && schema.default === undefined;
|
|
677
|
+
const isCopilot = this.projectType === ProjectType.Copilot;
|
|
678
|
+
if (isCopilot && this.hasNestedObjectInSchema(schema)) {
|
|
679
|
+
paramResult.isValid = false;
|
|
680
|
+
paramResult.reason = [ErrorType.RequestBodyContainsNestedObject];
|
|
681
|
+
return paramResult;
|
|
682
|
+
}
|
|
683
|
+
if (schema.type === "string" ||
|
|
684
|
+
schema.type === "integer" ||
|
|
685
|
+
schema.type === "boolean" ||
|
|
686
|
+
schema.type === "number") {
|
|
687
|
+
if (isRequiredWithoutDefault) {
|
|
688
|
+
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
689
|
+
}
|
|
690
|
+
else {
|
|
691
|
+
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
else if (schema.type === "object") {
|
|
695
|
+
const { properties } = schema;
|
|
696
|
+
for (const property in properties) {
|
|
697
|
+
let isRequired = false;
|
|
698
|
+
if (schema.required && ((_a = schema.required) === null || _a === void 0 ? void 0 : _a.indexOf(property)) >= 0) {
|
|
699
|
+
isRequired = true;
|
|
700
|
+
}
|
|
701
|
+
const result = this.checkPostBodySchema(properties[property], isRequired);
|
|
702
|
+
paramResult.requiredNum += result.requiredNum;
|
|
703
|
+
paramResult.optionalNum += result.optionalNum;
|
|
704
|
+
paramResult.isValid = paramResult.isValid && result.isValid;
|
|
705
|
+
paramResult.reason.push(...result.reason);
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
else {
|
|
709
|
+
if (isRequiredWithoutDefault && !isCopilot) {
|
|
710
|
+
paramResult.isValid = false;
|
|
711
|
+
paramResult.reason.push(ErrorType.PostBodyContainsRequiredUnsupportedSchema);
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
return paramResult;
|
|
704
715
|
}
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
716
|
+
checkParamSchema(paramObject) {
|
|
717
|
+
const paramResult = {
|
|
718
|
+
requiredNum: 0,
|
|
719
|
+
optionalNum: 0,
|
|
720
|
+
isValid: true,
|
|
721
|
+
reason: [],
|
|
722
|
+
};
|
|
723
|
+
if (!paramObject) {
|
|
724
|
+
return paramResult;
|
|
725
|
+
}
|
|
726
|
+
const isCopilot = this.projectType === ProjectType.Copilot;
|
|
727
|
+
for (let i = 0; i < paramObject.length; i++) {
|
|
728
|
+
const param = paramObject[i];
|
|
729
|
+
const schema = param.schema;
|
|
730
|
+
if (isCopilot && this.hasNestedObjectInSchema(schema)) {
|
|
731
|
+
paramResult.isValid = false;
|
|
732
|
+
paramResult.reason.push(ErrorType.ParamsContainsNestedObject);
|
|
733
|
+
continue;
|
|
734
|
+
}
|
|
735
|
+
const isRequiredWithoutDefault = param.required && schema.default === undefined;
|
|
736
|
+
if (isCopilot) {
|
|
737
|
+
if (isRequiredWithoutDefault) {
|
|
738
|
+
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
739
|
+
}
|
|
740
|
+
else {
|
|
741
|
+
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
742
|
+
}
|
|
743
|
+
continue;
|
|
744
|
+
}
|
|
745
|
+
if (param.in === "header" || param.in === "cookie") {
|
|
746
|
+
if (isRequiredWithoutDefault) {
|
|
747
|
+
paramResult.isValid = false;
|
|
748
|
+
paramResult.reason.push(ErrorType.ParamsContainRequiredUnsupportedSchema);
|
|
749
|
+
}
|
|
750
|
+
continue;
|
|
751
|
+
}
|
|
752
|
+
if (schema.type !== "boolean" &&
|
|
753
|
+
schema.type !== "string" &&
|
|
754
|
+
schema.type !== "number" &&
|
|
755
|
+
schema.type !== "integer") {
|
|
756
|
+
if (isRequiredWithoutDefault) {
|
|
757
|
+
paramResult.isValid = false;
|
|
758
|
+
paramResult.reason.push(ErrorType.ParamsContainRequiredUnsupportedSchema);
|
|
759
|
+
}
|
|
760
|
+
continue;
|
|
761
|
+
}
|
|
762
|
+
if (param.in === "query" || param.in === "path") {
|
|
763
|
+
if (isRequiredWithoutDefault) {
|
|
764
|
+
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
765
|
+
}
|
|
766
|
+
else {
|
|
767
|
+
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
return paramResult;
|
|
711
772
|
}
|
|
712
|
-
|
|
713
|
-
if (
|
|
714
|
-
|
|
773
|
+
hasNestedObjectInSchema(schema) {
|
|
774
|
+
if (schema.type === "object") {
|
|
775
|
+
for (const property in schema.properties) {
|
|
776
|
+
const nestedSchema = schema.properties[property];
|
|
777
|
+
if (nestedSchema.type === "object") {
|
|
778
|
+
return true;
|
|
779
|
+
}
|
|
780
|
+
}
|
|
715
781
|
}
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
782
|
+
return false;
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
// Copyright (c) Microsoft Corporation.
|
|
787
|
+
class CopilotValidator extends Validator {
|
|
788
|
+
constructor(spec, options) {
|
|
789
|
+
super();
|
|
790
|
+
this.projectType = ProjectType.Copilot;
|
|
791
|
+
this.options = options;
|
|
792
|
+
this.spec = spec;
|
|
793
|
+
}
|
|
794
|
+
validateSpec() {
|
|
795
|
+
const result = { errors: [], warnings: [] };
|
|
796
|
+
// validate spec version
|
|
797
|
+
let validationResult = this.validateSpecVersion();
|
|
798
|
+
result.errors.push(...validationResult.errors);
|
|
799
|
+
// validate spec server
|
|
800
|
+
validationResult = this.validateSpecServer();
|
|
801
|
+
result.errors.push(...validationResult.errors);
|
|
802
|
+
// validate no supported API
|
|
803
|
+
validationResult = this.validateSpecNoSupportAPI();
|
|
804
|
+
result.errors.push(...validationResult.errors);
|
|
805
|
+
// validate operationId missing
|
|
806
|
+
validationResult = this.validateSpecOperationId();
|
|
807
|
+
result.warnings.push(...validationResult.warnings);
|
|
808
|
+
return result;
|
|
809
|
+
}
|
|
810
|
+
validateAPI(method, path) {
|
|
811
|
+
const result = { isValid: true, reason: [] };
|
|
812
|
+
method = method.toLocaleLowerCase();
|
|
813
|
+
// validate method and path
|
|
814
|
+
const methodAndPathResult = this.validateMethodAndPath(method, path);
|
|
815
|
+
if (!methodAndPathResult.isValid) {
|
|
816
|
+
return methodAndPathResult;
|
|
817
|
+
}
|
|
818
|
+
const operationObject = this.spec.paths[path][method];
|
|
819
|
+
// validate auth
|
|
820
|
+
const authCheckResult = this.validateAuth(method, path);
|
|
821
|
+
result.reason.push(...authCheckResult.reason);
|
|
822
|
+
// validate operationId
|
|
823
|
+
if (!this.options.allowMissingId && !operationObject.operationId) {
|
|
824
|
+
result.reason.push(ErrorType.MissingOperationId);
|
|
825
|
+
}
|
|
826
|
+
// validate server
|
|
827
|
+
const validateServerResult = this.validateServer(method, path);
|
|
828
|
+
result.reason.push(...validateServerResult.reason);
|
|
829
|
+
// validate response
|
|
830
|
+
const validateResponseResult = this.validateResponse(method, path);
|
|
831
|
+
result.reason.push(...validateResponseResult.reason);
|
|
832
|
+
// validate requestBody
|
|
833
|
+
const requestBody = operationObject.requestBody;
|
|
834
|
+
const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
|
|
835
|
+
if (Utils.containMultipleMediaTypes(requestBody)) {
|
|
836
|
+
result.reason.push(ErrorType.PostBodyContainMultipleMediaTypes);
|
|
837
|
+
}
|
|
838
|
+
if (requestJsonBody) {
|
|
839
|
+
const requestBodySchema = requestJsonBody.schema;
|
|
840
|
+
if (requestBodySchema.type !== "object") {
|
|
841
|
+
result.reason.push(ErrorType.PostBodySchemaIsNotJson);
|
|
842
|
+
}
|
|
843
|
+
const requestBodyParamResult = this.checkPostBodySchema(requestBodySchema, requestBody.required);
|
|
844
|
+
result.reason.push(...requestBodyParamResult.reason);
|
|
845
|
+
}
|
|
846
|
+
// validate parameters
|
|
847
|
+
const paramObject = operationObject.parameters;
|
|
848
|
+
const paramResult = this.checkParamSchema(paramObject);
|
|
849
|
+
result.reason.push(...paramResult.reason);
|
|
850
|
+
if (result.reason.length > 0) {
|
|
851
|
+
result.isValid = false;
|
|
852
|
+
}
|
|
853
|
+
return result;
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
// Copyright (c) Microsoft Corporation.
|
|
858
|
+
class SMEValidator extends Validator {
|
|
859
|
+
constructor(spec, options) {
|
|
860
|
+
super();
|
|
861
|
+
this.projectType = ProjectType.SME;
|
|
862
|
+
this.options = options;
|
|
863
|
+
this.spec = spec;
|
|
864
|
+
}
|
|
865
|
+
validateSpec() {
|
|
866
|
+
const result = { errors: [], warnings: [] };
|
|
867
|
+
// validate spec version
|
|
868
|
+
let validationResult = this.validateSpecVersion();
|
|
869
|
+
result.errors.push(...validationResult.errors);
|
|
870
|
+
// validate spec server
|
|
871
|
+
validationResult = this.validateSpecServer();
|
|
872
|
+
result.errors.push(...validationResult.errors);
|
|
873
|
+
// validate no supported API
|
|
874
|
+
validationResult = this.validateSpecNoSupportAPI();
|
|
875
|
+
result.errors.push(...validationResult.errors);
|
|
876
|
+
// validate operationId missing
|
|
877
|
+
if (this.options.allowMissingId) {
|
|
878
|
+
validationResult = this.validateSpecOperationId();
|
|
879
|
+
result.warnings.push(...validationResult.warnings);
|
|
880
|
+
}
|
|
881
|
+
return result;
|
|
882
|
+
}
|
|
883
|
+
validateAPI(method, path) {
|
|
884
|
+
const result = { isValid: true, reason: [] };
|
|
885
|
+
method = method.toLocaleLowerCase();
|
|
886
|
+
// validate method and path
|
|
887
|
+
const methodAndPathResult = this.validateMethodAndPath(method, path);
|
|
888
|
+
if (!methodAndPathResult.isValid) {
|
|
889
|
+
return methodAndPathResult;
|
|
890
|
+
}
|
|
891
|
+
const operationObject = this.spec.paths[path][method];
|
|
892
|
+
// validate auth
|
|
893
|
+
const authCheckResult = this.validateAuth(method, path);
|
|
894
|
+
result.reason.push(...authCheckResult.reason);
|
|
895
|
+
// validate operationId
|
|
896
|
+
if (!this.options.allowMissingId && !operationObject.operationId) {
|
|
897
|
+
result.reason.push(ErrorType.MissingOperationId);
|
|
898
|
+
}
|
|
899
|
+
// validate server
|
|
900
|
+
const validateServerResult = this.validateServer(method, path);
|
|
901
|
+
result.reason.push(...validateServerResult.reason);
|
|
902
|
+
// validate response
|
|
903
|
+
const validateResponseResult = this.validateResponse(method, path);
|
|
904
|
+
result.reason.push(...validateResponseResult.reason);
|
|
905
|
+
let postBodyResult = {
|
|
906
|
+
requiredNum: 0,
|
|
907
|
+
optionalNum: 0,
|
|
908
|
+
isValid: true,
|
|
909
|
+
reason: [],
|
|
910
|
+
};
|
|
911
|
+
// validate requestBody
|
|
912
|
+
const requestBody = operationObject.requestBody;
|
|
913
|
+
const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
|
|
914
|
+
if (Utils.containMultipleMediaTypes(requestBody)) {
|
|
915
|
+
result.reason.push(ErrorType.PostBodyContainMultipleMediaTypes);
|
|
916
|
+
}
|
|
917
|
+
if (requestJsonBody) {
|
|
918
|
+
const requestBodySchema = requestJsonBody.schema;
|
|
919
|
+
postBodyResult = this.checkPostBodySchema(requestBodySchema, requestBody.required);
|
|
920
|
+
result.reason.push(...postBodyResult.reason);
|
|
921
|
+
}
|
|
922
|
+
// validate parameters
|
|
923
|
+
const paramObject = operationObject.parameters;
|
|
924
|
+
const paramResult = this.checkParamSchema(paramObject);
|
|
925
|
+
result.reason.push(...paramResult.reason);
|
|
926
|
+
// validate total parameters count
|
|
927
|
+
if (paramResult.isValid && postBodyResult.isValid) {
|
|
928
|
+
const paramCountResult = this.validateParamCount(postBodyResult, paramResult);
|
|
929
|
+
result.reason.push(...paramCountResult.reason);
|
|
930
|
+
}
|
|
931
|
+
if (result.reason.length > 0) {
|
|
932
|
+
result.isValid = false;
|
|
933
|
+
}
|
|
934
|
+
return result;
|
|
935
|
+
}
|
|
936
|
+
validateParamCount(postBodyResult, paramResult) {
|
|
937
|
+
const result = { isValid: true, reason: [] };
|
|
938
|
+
const totalRequiredParams = postBodyResult.requiredNum + paramResult.requiredNum;
|
|
939
|
+
const totalParams = totalRequiredParams + postBodyResult.optionalNum + paramResult.optionalNum;
|
|
940
|
+
if (totalRequiredParams > 1) {
|
|
941
|
+
if (!this.options.allowMultipleParameters ||
|
|
942
|
+
totalRequiredParams > SMEValidator.SMERequiredParamsMaxNum) {
|
|
943
|
+
result.reason.push(ErrorType.ExceededRequiredParamsLimit);
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
else if (totalParams === 0) {
|
|
947
|
+
result.reason.push(ErrorType.NoParameter);
|
|
948
|
+
}
|
|
949
|
+
return result;
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
SMEValidator.SMERequiredParamsMaxNum = 5;
|
|
953
|
+
|
|
954
|
+
// Copyright (c) Microsoft Corporation.
|
|
955
|
+
class TeamsAIValidator extends Validator {
|
|
956
|
+
constructor(spec, options) {
|
|
957
|
+
super();
|
|
958
|
+
this.projectType = ProjectType.TeamsAi;
|
|
959
|
+
this.options = options;
|
|
960
|
+
this.spec = spec;
|
|
961
|
+
}
|
|
962
|
+
validateSpec() {
|
|
963
|
+
const result = { errors: [], warnings: [] };
|
|
964
|
+
// validate spec server
|
|
965
|
+
let validationResult = this.validateSpecServer();
|
|
966
|
+
result.errors.push(...validationResult.errors);
|
|
967
|
+
// validate no supported API
|
|
968
|
+
validationResult = this.validateSpecNoSupportAPI();
|
|
969
|
+
result.errors.push(...validationResult.errors);
|
|
970
|
+
return result;
|
|
971
|
+
}
|
|
972
|
+
validateAPI(method, path) {
|
|
973
|
+
const result = { isValid: true, reason: [] };
|
|
974
|
+
method = method.toLocaleLowerCase();
|
|
975
|
+
// validate method and path
|
|
976
|
+
const methodAndPathResult = this.validateMethodAndPath(method, path);
|
|
977
|
+
if (!methodAndPathResult.isValid) {
|
|
978
|
+
return methodAndPathResult;
|
|
979
|
+
}
|
|
980
|
+
const operationObject = this.spec.paths[path][method];
|
|
981
|
+
// validate operationId
|
|
982
|
+
if (!this.options.allowMissingId && !operationObject.operationId) {
|
|
983
|
+
result.reason.push(ErrorType.MissingOperationId);
|
|
984
|
+
}
|
|
985
|
+
// validate server
|
|
986
|
+
const validateServerResult = this.validateServer(method, path);
|
|
987
|
+
result.reason.push(...validateServerResult.reason);
|
|
988
|
+
if (result.reason.length > 0) {
|
|
989
|
+
result.isValid = false;
|
|
990
|
+
}
|
|
991
|
+
return result;
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
class ValidatorFactory {
|
|
996
|
+
static create(spec, options) {
|
|
997
|
+
var _a;
|
|
998
|
+
const type = (_a = options.projectType) !== null && _a !== void 0 ? _a : ProjectType.SME;
|
|
999
|
+
switch (type) {
|
|
1000
|
+
case ProjectType.SME:
|
|
1001
|
+
return new SMEValidator(spec, options);
|
|
1002
|
+
case ProjectType.Copilot:
|
|
1003
|
+
return new CopilotValidator(spec, options);
|
|
1004
|
+
case ProjectType.TeamsAi:
|
|
1005
|
+
return new TeamsAIValidator(spec, options);
|
|
1006
|
+
default:
|
|
1007
|
+
throw new Error(`Invalid project type: ${type}`);
|
|
719
1008
|
}
|
|
720
|
-
return safeRegistrationIdEnvName;
|
|
721
1009
|
}
|
|
722
1010
|
}
|
|
723
1011
|
|
|
724
1012
|
// Copyright (c) Microsoft Corporation.
|
|
725
1013
|
class SpecFilter {
|
|
726
|
-
static specFilter(filter, unResolveSpec, resolvedSpec,
|
|
1014
|
+
static specFilter(filter, unResolveSpec, resolvedSpec, options) {
|
|
1015
|
+
var _a;
|
|
727
1016
|
try {
|
|
728
1017
|
const newSpec = Object.assign({}, unResolveSpec);
|
|
729
1018
|
const newPaths = {};
|
|
730
1019
|
for (const filterItem of filter) {
|
|
731
1020
|
const [method, path] = filterItem.split(" ");
|
|
732
1021
|
const methodName = method.toLowerCase();
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
1022
|
+
const pathObj = (_a = resolvedSpec.paths) === null || _a === void 0 ? void 0 : _a[path];
|
|
1023
|
+
if (ConstantString.AllOperationMethods.includes(methodName) &&
|
|
1024
|
+
pathObj &&
|
|
1025
|
+
pathObj[methodName]) {
|
|
1026
|
+
const validator = ValidatorFactory.create(resolvedSpec, options);
|
|
1027
|
+
const validateResult = validator.validateAPI(methodName, path);
|
|
1028
|
+
if (!validateResult.isValid) {
|
|
1029
|
+
continue;
|
|
1030
|
+
}
|
|
1031
|
+
if (!newPaths[path]) {
|
|
1032
|
+
newPaths[path] = Object.assign({}, unResolveSpec.paths[path]);
|
|
1033
|
+
for (const m of ConstantString.AllOperationMethods) {
|
|
1034
|
+
delete newPaths[path][m];
|
|
1035
|
+
}
|
|
1036
|
+
}
|
|
1037
|
+
newPaths[path][methodName] = unResolveSpec.paths[path][methodName];
|
|
1038
|
+
// Add the operationId if missing
|
|
1039
|
+
if (!newPaths[path][methodName].operationId) {
|
|
1040
|
+
newPaths[path][methodName].operationId = `${methodName}${Utils.convertPathToCamelCase(path)}`;
|
|
740
1041
|
}
|
|
741
|
-
}
|
|
742
|
-
newPaths[path][methodName] = unResolveSpec.paths[path][methodName];
|
|
743
|
-
// Add the operationId if missing
|
|
744
|
-
if (!newPaths[path][methodName].operationId) {
|
|
745
|
-
newPaths[path][methodName].operationId = `${methodName}${Utils.convertPathToCamelCase(path)}`;
|
|
746
1042
|
}
|
|
747
1043
|
}
|
|
748
1044
|
newSpec.paths = newPaths;
|
|
@@ -756,46 +1052,198 @@ class SpecFilter {
|
|
|
756
1052
|
|
|
757
1053
|
// Copyright (c) Microsoft Corporation.
|
|
758
1054
|
class ManifestUpdater {
|
|
759
|
-
static async
|
|
1055
|
+
static async updateManifestWithAiPlugin(manifestPath, outputSpecPath, apiPluginFilePath, spec, options) {
|
|
1056
|
+
const manifest = await fs.readJSON(manifestPath);
|
|
1057
|
+
const apiPluginRelativePath = ManifestUpdater.getRelativePath(manifestPath, apiPluginFilePath);
|
|
1058
|
+
manifest.plugins = [
|
|
1059
|
+
{
|
|
1060
|
+
file: apiPluginRelativePath,
|
|
1061
|
+
id: ConstantString.DefaultPluginId,
|
|
1062
|
+
},
|
|
1063
|
+
];
|
|
1064
|
+
const appName = this.removeEnvs(manifest.name.short);
|
|
1065
|
+
ManifestUpdater.updateManifestDescription(manifest, spec);
|
|
1066
|
+
const specRelativePath = ManifestUpdater.getRelativePath(manifestPath, outputSpecPath);
|
|
1067
|
+
const apiPlugin = await ManifestUpdater.generatePluginManifestSchema(spec, specRelativePath, apiPluginFilePath, appName, options);
|
|
1068
|
+
return [manifest, apiPlugin];
|
|
1069
|
+
}
|
|
1070
|
+
static updateManifestDescription(manifest, spec) {
|
|
760
1071
|
var _a, _b;
|
|
1072
|
+
manifest.description = {
|
|
1073
|
+
short: spec.info.title.slice(0, ConstantString.ShortDescriptionMaxLens),
|
|
1074
|
+
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),
|
|
1075
|
+
};
|
|
1076
|
+
}
|
|
1077
|
+
static mapOpenAPISchemaToFuncParam(schema, method, pathUrl) {
|
|
1078
|
+
let parameter;
|
|
1079
|
+
if (schema.type === "string" ||
|
|
1080
|
+
schema.type === "boolean" ||
|
|
1081
|
+
schema.type === "integer" ||
|
|
1082
|
+
schema.type === "number" ||
|
|
1083
|
+
schema.type === "array") {
|
|
1084
|
+
parameter = schema;
|
|
1085
|
+
}
|
|
1086
|
+
else {
|
|
1087
|
+
throw new SpecParserError(Utils.format(ConstantString.UnsupportedSchema, method, pathUrl, JSON.stringify(schema)), ErrorType.UpdateManifestFailed);
|
|
1088
|
+
}
|
|
1089
|
+
return parameter;
|
|
1090
|
+
}
|
|
1091
|
+
static async generatePluginManifestSchema(spec, specRelativePath, apiPluginFilePath, appName, options) {
|
|
1092
|
+
var _a, _b, _c, _d;
|
|
1093
|
+
const functions = [];
|
|
1094
|
+
const functionNames = [];
|
|
1095
|
+
const paths = spec.paths;
|
|
1096
|
+
for (const pathUrl in paths) {
|
|
1097
|
+
const pathItem = paths[pathUrl];
|
|
1098
|
+
if (pathItem) {
|
|
1099
|
+
const operations = pathItem;
|
|
1100
|
+
for (const method in operations) {
|
|
1101
|
+
if (options.allowMethods.includes(method)) {
|
|
1102
|
+
const operationItem = operations[method];
|
|
1103
|
+
if (operationItem) {
|
|
1104
|
+
const operationId = operationItem.operationId;
|
|
1105
|
+
const description = (_a = operationItem.description) !== null && _a !== void 0 ? _a : "";
|
|
1106
|
+
const paramObject = operationItem.parameters;
|
|
1107
|
+
const requestBody = operationItem.requestBody;
|
|
1108
|
+
const parameters = {
|
|
1109
|
+
type: "object",
|
|
1110
|
+
properties: {},
|
|
1111
|
+
required: [],
|
|
1112
|
+
};
|
|
1113
|
+
if (paramObject) {
|
|
1114
|
+
for (let i = 0; i < paramObject.length; i++) {
|
|
1115
|
+
const param = paramObject[i];
|
|
1116
|
+
const schema = param.schema;
|
|
1117
|
+
parameters.properties[param.name] = ManifestUpdater.mapOpenAPISchemaToFuncParam(schema, method, pathUrl);
|
|
1118
|
+
if (param.required) {
|
|
1119
|
+
parameters.required.push(param.name);
|
|
1120
|
+
}
|
|
1121
|
+
if (!parameters.properties[param.name].description) {
|
|
1122
|
+
parameters.properties[param.name].description = (_b = param.description) !== null && _b !== void 0 ? _b : "";
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
}
|
|
1126
|
+
if (requestBody) {
|
|
1127
|
+
const requestJsonBody = requestBody.content["application/json"];
|
|
1128
|
+
const requestBodySchema = requestJsonBody.schema;
|
|
1129
|
+
if (requestBodySchema.type === "object") {
|
|
1130
|
+
if (requestBodySchema.required) {
|
|
1131
|
+
parameters.required.push(...requestBodySchema.required);
|
|
1132
|
+
}
|
|
1133
|
+
for (const property in requestBodySchema.properties) {
|
|
1134
|
+
const schema = requestBodySchema.properties[property];
|
|
1135
|
+
parameters.properties[property] = ManifestUpdater.mapOpenAPISchemaToFuncParam(schema, method, pathUrl);
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
else {
|
|
1139
|
+
throw new SpecParserError(Utils.format(ConstantString.UnsupportedSchema, method, pathUrl, JSON.stringify(requestBodySchema)), ErrorType.UpdateManifestFailed);
|
|
1140
|
+
}
|
|
1141
|
+
}
|
|
1142
|
+
const funcObj = {
|
|
1143
|
+
name: operationId,
|
|
1144
|
+
description: description,
|
|
1145
|
+
parameters: parameters,
|
|
1146
|
+
};
|
|
1147
|
+
functions.push(funcObj);
|
|
1148
|
+
functionNames.push(operationId);
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1154
|
+
let apiPlugin;
|
|
1155
|
+
if (await fs.pathExists(apiPluginFilePath)) {
|
|
1156
|
+
apiPlugin = await fs.readJSON(apiPluginFilePath);
|
|
1157
|
+
}
|
|
1158
|
+
else {
|
|
1159
|
+
apiPlugin = {
|
|
1160
|
+
schema_version: "v2",
|
|
1161
|
+
name_for_human: "",
|
|
1162
|
+
description_for_human: "",
|
|
1163
|
+
functions: [],
|
|
1164
|
+
runtimes: [],
|
|
1165
|
+
};
|
|
1166
|
+
}
|
|
1167
|
+
apiPlugin.functions = apiPlugin.functions || [];
|
|
1168
|
+
for (const func of functions) {
|
|
1169
|
+
const index = (_c = apiPlugin.functions) === null || _c === void 0 ? void 0 : _c.findIndex((f) => f.name === func.name);
|
|
1170
|
+
if (index === -1) {
|
|
1171
|
+
apiPlugin.functions.push(func);
|
|
1172
|
+
}
|
|
1173
|
+
else {
|
|
1174
|
+
apiPlugin.functions[index] = func;
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
apiPlugin.runtimes = apiPlugin.runtimes || [];
|
|
1178
|
+
const index = apiPlugin.runtimes.findIndex((runtime) => runtime.spec.url === specRelativePath);
|
|
1179
|
+
if (index === -1) {
|
|
1180
|
+
apiPlugin.runtimes.push({
|
|
1181
|
+
type: "OpenApi",
|
|
1182
|
+
auth: {
|
|
1183
|
+
type: "none",
|
|
1184
|
+
},
|
|
1185
|
+
spec: {
|
|
1186
|
+
url: specRelativePath,
|
|
1187
|
+
},
|
|
1188
|
+
run_for_functions: functionNames,
|
|
1189
|
+
});
|
|
1190
|
+
}
|
|
1191
|
+
else {
|
|
1192
|
+
apiPlugin.runtimes[index].run_for_functions = functionNames;
|
|
1193
|
+
}
|
|
1194
|
+
if (!apiPlugin.name_for_human) {
|
|
1195
|
+
apiPlugin.name_for_human = appName;
|
|
1196
|
+
}
|
|
1197
|
+
if (!apiPlugin.description_for_human) {
|
|
1198
|
+
apiPlugin.description_for_human =
|
|
1199
|
+
(_d = spec.info.description) !== null && _d !== void 0 ? _d : "<Please add description of the plugin>";
|
|
1200
|
+
}
|
|
1201
|
+
return apiPlugin;
|
|
1202
|
+
}
|
|
1203
|
+
static async updateManifest(manifestPath, outputSpecPath, spec, options, adaptiveCardFolder, authInfo) {
|
|
761
1204
|
try {
|
|
762
1205
|
const originalManifest = await fs.readJSON(manifestPath);
|
|
763
1206
|
const updatedPart = {};
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
commands
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
}
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
1207
|
+
updatedPart.composeExtensions = [];
|
|
1208
|
+
let warnings = [];
|
|
1209
|
+
if (options.projectType === ProjectType.SME) {
|
|
1210
|
+
const updateResult = await ManifestUpdater.generateCommands(spec, manifestPath, options, adaptiveCardFolder);
|
|
1211
|
+
const commands = updateResult[0];
|
|
1212
|
+
warnings = updateResult[1];
|
|
1213
|
+
const composeExtension = {
|
|
1214
|
+
composeExtensionType: "apiBased",
|
|
1215
|
+
apiSpecificationFile: ManifestUpdater.getRelativePath(manifestPath, outputSpecPath),
|
|
1216
|
+
commands: commands,
|
|
1217
|
+
};
|
|
1218
|
+
if (authInfo) {
|
|
1219
|
+
const auth = authInfo.authScheme;
|
|
1220
|
+
if (Utils.isAPIKeyAuth(auth) || Utils.isBearerTokenAuth(auth)) {
|
|
1221
|
+
const safeApiSecretRegistrationId = Utils.getSafeRegistrationIdEnvName(`${authInfo.name}_${ConstantString.RegistrationIdPostfix}`);
|
|
1222
|
+
composeExtension.authorization = {
|
|
1223
|
+
authType: "apiSecretServiceAuth",
|
|
1224
|
+
apiSecretServiceAuthConfiguration: {
|
|
1225
|
+
apiSecretRegistrationId: `\${{${safeApiSecretRegistrationId}}}`,
|
|
1226
|
+
},
|
|
1227
|
+
};
|
|
1228
|
+
}
|
|
1229
|
+
else if (Utils.isOAuthWithAuthCodeFlow(auth)) {
|
|
1230
|
+
const safeOAuth2RegistrationId = Utils.getSafeRegistrationIdEnvName(`${authInfo.name}_${ConstantString.OAuthRegistrationIdPostFix}`);
|
|
1231
|
+
composeExtension.authorization = {
|
|
1232
|
+
authType: "oAuth2.0",
|
|
1233
|
+
oAuthConfiguration: {
|
|
1234
|
+
oauthConfigurationId: `\${{${safeOAuth2RegistrationId}}}`,
|
|
1235
|
+
},
|
|
1236
|
+
};
|
|
1237
|
+
updatedPart.webApplicationInfo = {
|
|
1238
|
+
id: "${{AAD_APP_CLIENT_ID}}",
|
|
1239
|
+
resource: "api://${{DOMAIN}}/${{AAD_APP_CLIENT_ID}}",
|
|
1240
|
+
};
|
|
1241
|
+
}
|
|
792
1242
|
}
|
|
1243
|
+
updatedPart.composeExtensions = [composeExtension];
|
|
793
1244
|
}
|
|
794
|
-
updatedPart.description =
|
|
795
|
-
|
|
796
|
-
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),
|
|
797
|
-
};
|
|
798
|
-
updatedPart.composeExtensions = [composeExtension];
|
|
1245
|
+
updatedPart.description = originalManifest.description;
|
|
1246
|
+
ManifestUpdater.updateManifestDescription(updatedPart, spec);
|
|
799
1247
|
const updatedManifest = Object.assign(Object.assign({}, originalManifest), updatedPart);
|
|
800
1248
|
return [updatedManifest, warnings];
|
|
801
1249
|
}
|
|
@@ -803,7 +1251,8 @@ class ManifestUpdater {
|
|
|
803
1251
|
throw new SpecParserError(err.toString(), ErrorType.UpdateManifestFailed);
|
|
804
1252
|
}
|
|
805
1253
|
}
|
|
806
|
-
static async generateCommands(spec,
|
|
1254
|
+
static async generateCommands(spec, manifestPath, options, adaptiveCardFolder) {
|
|
1255
|
+
var _a;
|
|
807
1256
|
const paths = spec.paths;
|
|
808
1257
|
const commands = [];
|
|
809
1258
|
const warnings = [];
|
|
@@ -814,16 +1263,28 @@ class ManifestUpdater {
|
|
|
814
1263
|
const operations = pathItem;
|
|
815
1264
|
// Currently only support GET and POST method
|
|
816
1265
|
for (const method in operations) {
|
|
817
|
-
if (
|
|
1266
|
+
if ((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) {
|
|
818
1267
|
const operationItem = operations[method];
|
|
819
1268
|
if (operationItem) {
|
|
820
|
-
const
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
1269
|
+
const command = Utils.parseApiInfo(operationItem, options);
|
|
1270
|
+
if (command.parameters &&
|
|
1271
|
+
command.parameters.length >= 1 &&
|
|
1272
|
+
command.parameters.some((param) => param.isRequired)) {
|
|
1273
|
+
command.parameters = command.parameters.filter((param) => param.isRequired);
|
|
1274
|
+
}
|
|
1275
|
+
else if (command.parameters && command.parameters.length > 0) {
|
|
1276
|
+
command.parameters = [command.parameters[0]];
|
|
1277
|
+
warnings.push({
|
|
1278
|
+
type: WarningType.OperationOnlyContainsOptionalParam,
|
|
1279
|
+
content: Utils.format(ConstantString.OperationOnlyContainsOptionalParam, command.id),
|
|
1280
|
+
data: command.id,
|
|
1281
|
+
});
|
|
1282
|
+
}
|
|
1283
|
+
if (adaptiveCardFolder) {
|
|
1284
|
+
const adaptiveCardPath = path.join(adaptiveCardFolder, command.id + ".json");
|
|
1285
|
+
command.apiResponseRenderingTemplateFile = (await fs.pathExists(adaptiveCardPath))
|
|
1286
|
+
? ManifestUpdater.getRelativePath(manifestPath, adaptiveCardPath)
|
|
1287
|
+
: "";
|
|
827
1288
|
}
|
|
828
1289
|
commands.push(command);
|
|
829
1290
|
}
|
|
@@ -838,13 +1299,22 @@ class ManifestUpdater {
|
|
|
838
1299
|
const relativePath = path.relative(path.dirname(from), to);
|
|
839
1300
|
return path.normalize(relativePath).replace(/\\/g, "/");
|
|
840
1301
|
}
|
|
1302
|
+
static removeEnvs(str) {
|
|
1303
|
+
const placeHolderReg = /\${{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*}}/g;
|
|
1304
|
+
const matches = placeHolderReg.exec(str);
|
|
1305
|
+
let newStr = str;
|
|
1306
|
+
if (matches != null) {
|
|
1307
|
+
newStr = newStr.replace(matches[0], "");
|
|
1308
|
+
}
|
|
1309
|
+
return newStr;
|
|
1310
|
+
}
|
|
841
1311
|
}
|
|
842
1312
|
|
|
843
1313
|
// Copyright (c) Microsoft Corporation.
|
|
844
1314
|
class AdaptiveCardGenerator {
|
|
845
1315
|
static generateAdaptiveCard(operationItem) {
|
|
846
1316
|
try {
|
|
847
|
-
const json = Utils.getResponseJson(operationItem);
|
|
1317
|
+
const { json } = Utils.getResponseJson(operationItem);
|
|
848
1318
|
let cardBody = [];
|
|
849
1319
|
let schema = json.schema;
|
|
850
1320
|
let jsonPath = "$";
|
|
@@ -1104,8 +1574,11 @@ class SpecParser {
|
|
|
1104
1574
|
allowMissingId: true,
|
|
1105
1575
|
allowSwagger: true,
|
|
1106
1576
|
allowAPIKeyAuth: false,
|
|
1577
|
+
allowBearerTokenAuth: false,
|
|
1107
1578
|
allowMultipleParameters: false,
|
|
1108
1579
|
allowOauth2: false,
|
|
1580
|
+
allowMethods: ["get", "post"],
|
|
1581
|
+
projectType: ProjectType.SME,
|
|
1109
1582
|
};
|
|
1110
1583
|
this.pathOrSpec = pathOrDoc;
|
|
1111
1584
|
this.parser = new SwaggerParser();
|
|
@@ -1129,6 +1602,8 @@ class SpecParser {
|
|
|
1129
1602
|
errors: [{ type: ErrorType.SpecNotValid, content: e.toString() }],
|
|
1130
1603
|
};
|
|
1131
1604
|
}
|
|
1605
|
+
const errors = [];
|
|
1606
|
+
const warnings = [];
|
|
1132
1607
|
if (!this.options.allowSwagger && this.isSwaggerFile) {
|
|
1133
1608
|
return {
|
|
1134
1609
|
status: ValidationStatus.Error,
|
|
@@ -1138,7 +1613,38 @@ class SpecParser {
|
|
|
1138
1613
|
],
|
|
1139
1614
|
};
|
|
1140
1615
|
}
|
|
1141
|
-
|
|
1616
|
+
// Remote reference not supported
|
|
1617
|
+
const refPaths = this.parser.$refs.paths();
|
|
1618
|
+
// refPaths [0] is the current spec file path
|
|
1619
|
+
if (refPaths.length > 1) {
|
|
1620
|
+
errors.push({
|
|
1621
|
+
type: ErrorType.RemoteRefNotSupported,
|
|
1622
|
+
content: Utils.format(ConstantString.RemoteRefNotSupported, refPaths.join(", ")),
|
|
1623
|
+
data: refPaths,
|
|
1624
|
+
});
|
|
1625
|
+
}
|
|
1626
|
+
if (!!this.isSwaggerFile && this.options.allowSwagger) {
|
|
1627
|
+
warnings.push({
|
|
1628
|
+
type: WarningType.ConvertSwaggerToOpenAPI,
|
|
1629
|
+
content: ConstantString.ConvertSwaggerToOpenAPI,
|
|
1630
|
+
});
|
|
1631
|
+
}
|
|
1632
|
+
const validator = this.getValidator(this.spec);
|
|
1633
|
+
const validationResult = validator.validateSpec();
|
|
1634
|
+
warnings.push(...validationResult.warnings);
|
|
1635
|
+
errors.push(...validationResult.errors);
|
|
1636
|
+
let status = ValidationStatus.Valid;
|
|
1637
|
+
if (warnings.length > 0 && errors.length === 0) {
|
|
1638
|
+
status = ValidationStatus.Warning;
|
|
1639
|
+
}
|
|
1640
|
+
else if (errors.length > 0) {
|
|
1641
|
+
status = ValidationStatus.Error;
|
|
1642
|
+
}
|
|
1643
|
+
return {
|
|
1644
|
+
status: status,
|
|
1645
|
+
warnings: warnings,
|
|
1646
|
+
errors: errors,
|
|
1647
|
+
};
|
|
1142
1648
|
}
|
|
1143
1649
|
catch (err) {
|
|
1144
1650
|
throw new SpecParserError(err.toString(), ErrorType.ValidateFailed);
|
|
@@ -1158,39 +1664,40 @@ class SpecParser {
|
|
|
1158
1664
|
try {
|
|
1159
1665
|
await this.loadSpec();
|
|
1160
1666
|
const spec = this.spec;
|
|
1161
|
-
const apiMap = this.
|
|
1162
|
-
const result =
|
|
1667
|
+
const apiMap = this.getAPIs(spec);
|
|
1668
|
+
const result = {
|
|
1669
|
+
APIs: [],
|
|
1670
|
+
allAPICount: 0,
|
|
1671
|
+
validAPICount: 0,
|
|
1672
|
+
};
|
|
1163
1673
|
for (const apiKey in apiMap) {
|
|
1674
|
+
const { operation, isValid, reason } = apiMap[apiKey];
|
|
1675
|
+
const [method, path] = apiKey.split(" ");
|
|
1676
|
+
const operationId = (_a = operation.operationId) !== null && _a !== void 0 ? _a : `${method.toLowerCase()}${Utils.convertPathToCamelCase(path)}`;
|
|
1164
1677
|
const apiResult = {
|
|
1165
|
-
api:
|
|
1678
|
+
api: apiKey,
|
|
1166
1679
|
server: "",
|
|
1167
|
-
operationId:
|
|
1680
|
+
operationId: operationId,
|
|
1681
|
+
isValid: isValid,
|
|
1682
|
+
reason: reason,
|
|
1168
1683
|
};
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
if (!operationId) {
|
|
1181
|
-
operationId = `${method.toLowerCase()}${Utils.convertPathToCamelCase(path)}`;
|
|
1182
|
-
}
|
|
1183
|
-
apiResult.operationId = operationId;
|
|
1184
|
-
const authArray = Utils.getAuthArray(operation.security, spec);
|
|
1185
|
-
for (const auths of authArray) {
|
|
1186
|
-
if (auths.length === 1) {
|
|
1187
|
-
apiResult.auth = auths[0].authSchema;
|
|
1188
|
-
break;
|
|
1684
|
+
if (isValid) {
|
|
1685
|
+
const serverObj = Utils.getServerObject(spec, method.toLocaleLowerCase(), path);
|
|
1686
|
+
if (serverObj) {
|
|
1687
|
+
apiResult.server = Utils.resolveEnv(serverObj.url);
|
|
1688
|
+
}
|
|
1689
|
+
const authArray = Utils.getAuthArray(operation.security, spec);
|
|
1690
|
+
for (const auths of authArray) {
|
|
1691
|
+
if (auths.length === 1) {
|
|
1692
|
+
apiResult.auth = auths[0];
|
|
1693
|
+
break;
|
|
1694
|
+
}
|
|
1189
1695
|
}
|
|
1190
1696
|
}
|
|
1191
|
-
apiResult
|
|
1192
|
-
result.push(apiResult);
|
|
1697
|
+
result.APIs.push(apiResult);
|
|
1193
1698
|
}
|
|
1699
|
+
result.allAPICount = result.APIs.length;
|
|
1700
|
+
result.validAPICount = result.APIs.filter((api) => api.isValid).length;
|
|
1194
1701
|
return result;
|
|
1195
1702
|
}
|
|
1196
1703
|
catch (err) {
|
|
@@ -1200,48 +1707,108 @@ class SpecParser {
|
|
|
1200
1707
|
throw new SpecParserError(err.toString(), ErrorType.ListFailed);
|
|
1201
1708
|
}
|
|
1202
1709
|
}
|
|
1710
|
+
/**
|
|
1711
|
+
* Generate specs according to the filters.
|
|
1712
|
+
* @param filter An array of strings that represent the filters to apply when generating the artifacts. If filter is empty, it would process nothing.
|
|
1713
|
+
*/
|
|
1714
|
+
async getFilteredSpecs(filter, signal) {
|
|
1715
|
+
try {
|
|
1716
|
+
if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
|
|
1717
|
+
throw new SpecParserError(ConstantString.CancelledMessage, ErrorType.Cancelled);
|
|
1718
|
+
}
|
|
1719
|
+
await this.loadSpec();
|
|
1720
|
+
if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
|
|
1721
|
+
throw new SpecParserError(ConstantString.CancelledMessage, ErrorType.Cancelled);
|
|
1722
|
+
}
|
|
1723
|
+
const newUnResolvedSpec = SpecFilter.specFilter(filter, this.unResolveSpec, this.spec, this.options);
|
|
1724
|
+
if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
|
|
1725
|
+
throw new SpecParserError(ConstantString.CancelledMessage, ErrorType.Cancelled);
|
|
1726
|
+
}
|
|
1727
|
+
const newSpec = (await this.parser.dereference(newUnResolvedSpec));
|
|
1728
|
+
return [newUnResolvedSpec, newSpec];
|
|
1729
|
+
}
|
|
1730
|
+
catch (err) {
|
|
1731
|
+
if (err instanceof SpecParserError) {
|
|
1732
|
+
throw err;
|
|
1733
|
+
}
|
|
1734
|
+
throw new SpecParserError(err.toString(), ErrorType.GetSpecFailed);
|
|
1735
|
+
}
|
|
1736
|
+
}
|
|
1203
1737
|
/**
|
|
1204
1738
|
* Generates and update artifacts from the OpenAPI specification file. Generate Adaptive Cards, update Teams app manifest, and generate a new OpenAPI specification file.
|
|
1205
1739
|
* @param manifestPath A file path of the Teams app manifest file to update.
|
|
1206
1740
|
* @param filter An array of strings that represent the filters to apply when generating the artifacts. If filter is empty, it would process nothing.
|
|
1207
1741
|
* @param outputSpecPath File path of the new OpenAPI specification file to generate. If not specified or empty, no spec file will be generated.
|
|
1208
|
-
* @param
|
|
1742
|
+
* @param pluginFilePath File path of the api plugin file to generate.
|
|
1209
1743
|
*/
|
|
1210
|
-
async
|
|
1744
|
+
async generateForCopilot(manifestPath, filter, outputSpecPath, pluginFilePath, signal) {
|
|
1211
1745
|
const result = {
|
|
1212
1746
|
allSuccess: true,
|
|
1213
1747
|
warnings: [],
|
|
1214
1748
|
};
|
|
1215
1749
|
try {
|
|
1216
|
-
|
|
1217
|
-
|
|
1750
|
+
const newSpecs = await this.getFilteredSpecs(filter, signal);
|
|
1751
|
+
const newUnResolvedSpec = newSpecs[0];
|
|
1752
|
+
const newSpec = newSpecs[1];
|
|
1753
|
+
let resultStr;
|
|
1754
|
+
if (outputSpecPath.endsWith(".yaml") || outputSpecPath.endsWith(".yml")) {
|
|
1755
|
+
resultStr = jsyaml.dump(newUnResolvedSpec);
|
|
1218
1756
|
}
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
throw new SpecParserError(ConstantString.CancelledMessage, ErrorType.Cancelled);
|
|
1757
|
+
else {
|
|
1758
|
+
resultStr = JSON.stringify(newUnResolvedSpec, null, 2);
|
|
1222
1759
|
}
|
|
1223
|
-
|
|
1760
|
+
await fs.outputFile(outputSpecPath, resultStr);
|
|
1224
1761
|
if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
|
|
1225
1762
|
throw new SpecParserError(ConstantString.CancelledMessage, ErrorType.Cancelled);
|
|
1226
1763
|
}
|
|
1227
|
-
const
|
|
1228
|
-
|
|
1229
|
-
|
|
1764
|
+
const [updatedManifest, apiPlugin] = await ManifestUpdater.updateManifestWithAiPlugin(manifestPath, outputSpecPath, pluginFilePath, newSpec, this.options);
|
|
1765
|
+
await fs.outputJSON(manifestPath, updatedManifest, { spaces: 2 });
|
|
1766
|
+
await fs.outputJSON(pluginFilePath, apiPlugin, { spaces: 2 });
|
|
1767
|
+
}
|
|
1768
|
+
catch (err) {
|
|
1769
|
+
if (err instanceof SpecParserError) {
|
|
1770
|
+
throw err;
|
|
1771
|
+
}
|
|
1772
|
+
throw new SpecParserError(err.toString(), ErrorType.GenerateFailed);
|
|
1773
|
+
}
|
|
1774
|
+
return result;
|
|
1775
|
+
}
|
|
1776
|
+
/**
|
|
1777
|
+
* Generates and update artifacts from the OpenAPI specification file. Generate Adaptive Cards, update Teams app manifest, and generate a new OpenAPI specification file.
|
|
1778
|
+
* @param manifestPath A file path of the Teams app manifest file to update.
|
|
1779
|
+
* @param filter An array of strings that represent the filters to apply when generating the artifacts. If filter is empty, it would process nothing.
|
|
1780
|
+
* @param outputSpecPath File path of the new OpenAPI specification file to generate. If not specified or empty, no spec file will be generated.
|
|
1781
|
+
* @param adaptiveCardFolder Folder path where the Adaptive Card files will be generated. If not specified or empty, Adaptive Card files will not be generated.
|
|
1782
|
+
*/
|
|
1783
|
+
async generate(manifestPath, filter, outputSpecPath, adaptiveCardFolder, signal) {
|
|
1784
|
+
const result = {
|
|
1785
|
+
allSuccess: true,
|
|
1786
|
+
warnings: [],
|
|
1787
|
+
};
|
|
1788
|
+
try {
|
|
1789
|
+
const newSpecs = await this.getFilteredSpecs(filter, signal);
|
|
1790
|
+
const newUnResolvedSpec = newSpecs[0];
|
|
1791
|
+
const newSpec = newSpecs[1];
|
|
1792
|
+
let hasMultipleAuth = false;
|
|
1793
|
+
let authInfo = undefined;
|
|
1230
1794
|
for (const url in newSpec.paths) {
|
|
1231
1795
|
for (const method in newSpec.paths[url]) {
|
|
1232
1796
|
const operation = newSpec.paths[url][method];
|
|
1233
1797
|
const authArray = Utils.getAuthArray(operation.security, newSpec);
|
|
1234
1798
|
if (authArray && authArray.length > 0) {
|
|
1235
|
-
|
|
1236
|
-
if (
|
|
1237
|
-
|
|
1799
|
+
const currentAuth = authArray[0][0];
|
|
1800
|
+
if (!authInfo) {
|
|
1801
|
+
authInfo = authArray[0][0];
|
|
1802
|
+
}
|
|
1803
|
+
else if (authInfo.name !== currentAuth.name) {
|
|
1804
|
+
hasMultipleAuth = true;
|
|
1238
1805
|
break;
|
|
1239
1806
|
}
|
|
1240
1807
|
}
|
|
1241
1808
|
}
|
|
1242
1809
|
}
|
|
1243
|
-
if (
|
|
1244
|
-
throw new SpecParserError(ConstantString.
|
|
1810
|
+
if (hasMultipleAuth && this.options.projectType !== ProjectType.TeamsAi) {
|
|
1811
|
+
throw new SpecParserError(ConstantString.MultipleAuthNotSupported, ErrorType.MultipleAuthNotSupported);
|
|
1245
1812
|
}
|
|
1246
1813
|
let resultStr;
|
|
1247
1814
|
if (outputSpecPath.endsWith(".yaml") || outputSpecPath.endsWith(".yml")) {
|
|
@@ -1251,26 +1818,28 @@ class SpecParser {
|
|
|
1251
1818
|
resultStr = JSON.stringify(newUnResolvedSpec, null, 2);
|
|
1252
1819
|
}
|
|
1253
1820
|
await fs.outputFile(outputSpecPath, resultStr);
|
|
1254
|
-
|
|
1255
|
-
for (const
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1821
|
+
if (adaptiveCardFolder) {
|
|
1822
|
+
for (const url in newSpec.paths) {
|
|
1823
|
+
for (const method in newSpec.paths[url]) {
|
|
1824
|
+
// paths object may contain description/summary which is not a http method, so we need to check if it is a operation object
|
|
1825
|
+
if (this.options.allowMethods.includes(method)) {
|
|
1826
|
+
const operation = newSpec.paths[url][method];
|
|
1827
|
+
try {
|
|
1828
|
+
const [card, jsonPath] = AdaptiveCardGenerator.generateAdaptiveCard(operation);
|
|
1829
|
+
const fileName = path.join(adaptiveCardFolder, `${operation.operationId}.json`);
|
|
1830
|
+
const wrappedCard = wrapAdaptiveCard(card, jsonPath);
|
|
1831
|
+
await fs.outputJSON(fileName, wrappedCard, { spaces: 2 });
|
|
1832
|
+
const dataFileName = path.join(adaptiveCardFolder, `${operation.operationId}.data.json`);
|
|
1833
|
+
await fs.outputJSON(dataFileName, {}, { spaces: 2 });
|
|
1834
|
+
}
|
|
1835
|
+
catch (err) {
|
|
1836
|
+
result.allSuccess = false;
|
|
1837
|
+
result.warnings.push({
|
|
1838
|
+
type: WarningType.GenerateCardFailed,
|
|
1839
|
+
content: err.toString(),
|
|
1840
|
+
data: operation.operationId,
|
|
1841
|
+
});
|
|
1842
|
+
}
|
|
1274
1843
|
}
|
|
1275
1844
|
}
|
|
1276
1845
|
}
|
|
@@ -1278,8 +1847,7 @@ class SpecParser {
|
|
|
1278
1847
|
if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
|
|
1279
1848
|
throw new SpecParserError(ConstantString.CancelledMessage, ErrorType.Cancelled);
|
|
1280
1849
|
}
|
|
1281
|
-
const
|
|
1282
|
-
const [updatedManifest, warnings] = await ManifestUpdater.updateManifest(manifestPath, outputSpecPath, adaptiveCardFolder, newSpec, this.options.allowMultipleParameters, auth);
|
|
1850
|
+
const [updatedManifest, warnings] = await ManifestUpdater.updateManifest(manifestPath, outputSpecPath, newSpec, this.options, adaptiveCardFolder, authInfo);
|
|
1283
1851
|
await fs.outputJSON(manifestPath, updatedManifest, { spaces: 2 });
|
|
1284
1852
|
result.warnings.push(...warnings);
|
|
1285
1853
|
}
|
|
@@ -1304,15 +1872,21 @@ class SpecParser {
|
|
|
1304
1872
|
this.spec = (await this.parser.dereference(clonedUnResolveSpec));
|
|
1305
1873
|
}
|
|
1306
1874
|
}
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1875
|
+
getAPIs(spec) {
|
|
1876
|
+
const validator = this.getValidator(spec);
|
|
1877
|
+
const apiMap = validator.listAPIs();
|
|
1878
|
+
this.apiMap = apiMap;
|
|
1879
|
+
return apiMap;
|
|
1880
|
+
}
|
|
1881
|
+
getValidator(spec) {
|
|
1882
|
+
if (this.validator) {
|
|
1883
|
+
return this.validator;
|
|
1310
1884
|
}
|
|
1311
|
-
const
|
|
1312
|
-
this.
|
|
1313
|
-
return
|
|
1885
|
+
const validator = ValidatorFactory.create(spec, this.options);
|
|
1886
|
+
this.validator = validator;
|
|
1887
|
+
return validator;
|
|
1314
1888
|
}
|
|
1315
1889
|
}
|
|
1316
1890
|
|
|
1317
|
-
export { ConstantString, ErrorType, SpecParser, SpecParserError, Utils, ValidationStatus, WarningType };
|
|
1891
|
+
export { AdaptiveCardGenerator, ConstantString, ErrorType, ProjectType, SpecParser, SpecParserError, Utils, ValidationStatus, WarningType };
|
|
1318
1892
|
//# sourceMappingURL=index.esm2017.mjs.map
|