@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.esm5.js
CHANGED
|
@@ -45,7 +45,8 @@ var ErrorType;
|
|
|
45
45
|
ErrorType["NoExtraAPICanBeAdded"] = "no-extra-api-can-be-added";
|
|
46
46
|
ErrorType["ResolveServerUrlFailed"] = "resolve-server-url-failed";
|
|
47
47
|
ErrorType["SwaggerNotSupported"] = "swagger-not-supported";
|
|
48
|
-
ErrorType["
|
|
48
|
+
ErrorType["MultipleAuthNotSupported"] = "multiple-auth-not-supported";
|
|
49
|
+
ErrorType["SpecVersionNotSupported"] = "spec-version-not-supported";
|
|
49
50
|
ErrorType["ListFailed"] = "list-failed";
|
|
50
51
|
ErrorType["listSupportedAPIInfoFailed"] = "list-supported-api-info-failed";
|
|
51
52
|
ErrorType["FilterSpecFailed"] = "filter-spec-failed";
|
|
@@ -53,6 +54,22 @@ var ErrorType;
|
|
|
53
54
|
ErrorType["GenerateAdaptiveCardFailed"] = "generate-adaptive-card-failed";
|
|
54
55
|
ErrorType["GenerateFailed"] = "generate-failed";
|
|
55
56
|
ErrorType["ValidateFailed"] = "validate-failed";
|
|
57
|
+
ErrorType["GetSpecFailed"] = "get-spec-failed";
|
|
58
|
+
ErrorType["AuthTypeIsNotSupported"] = "auth-type-is-not-supported";
|
|
59
|
+
ErrorType["MissingOperationId"] = "missing-operation-id";
|
|
60
|
+
ErrorType["PostBodyContainMultipleMediaTypes"] = "post-body-contain-multiple-media-types";
|
|
61
|
+
ErrorType["ResponseContainMultipleMediaTypes"] = "response-contain-multiple-media-types";
|
|
62
|
+
ErrorType["ResponseJsonIsEmpty"] = "response-json-is-empty";
|
|
63
|
+
ErrorType["PostBodySchemaIsNotJson"] = "post-body-schema-is-not-json";
|
|
64
|
+
ErrorType["PostBodyContainsRequiredUnsupportedSchema"] = "post-body-contains-required-unsupported-schema";
|
|
65
|
+
ErrorType["ParamsContainRequiredUnsupportedSchema"] = "params-contain-required-unsupported-schema";
|
|
66
|
+
ErrorType["ParamsContainsNestedObject"] = "params-contains-nested-object";
|
|
67
|
+
ErrorType["RequestBodyContainsNestedObject"] = "request-body-contains-nested-object";
|
|
68
|
+
ErrorType["ExceededRequiredParamsLimit"] = "exceeded-required-params-limit";
|
|
69
|
+
ErrorType["NoParameter"] = "no-parameter";
|
|
70
|
+
ErrorType["NoAPIInfo"] = "no-api-info";
|
|
71
|
+
ErrorType["MethodNotAllowed"] = "method-not-allowed";
|
|
72
|
+
ErrorType["UrlPathNotExist"] = "url-path-not-exist";
|
|
56
73
|
ErrorType["Cancelled"] = "cancelled";
|
|
57
74
|
ErrorType["Unknown"] = "unknown";
|
|
58
75
|
})(ErrorType || (ErrorType = {}));
|
|
@@ -75,7 +92,13 @@ var ValidationStatus;
|
|
|
75
92
|
ValidationStatus[ValidationStatus["Valid"] = 0] = "Valid";
|
|
76
93
|
ValidationStatus[ValidationStatus["Warning"] = 1] = "Warning";
|
|
77
94
|
ValidationStatus[ValidationStatus["Error"] = 2] = "Error";
|
|
78
|
-
})(ValidationStatus || (ValidationStatus = {}));
|
|
95
|
+
})(ValidationStatus || (ValidationStatus = {}));
|
|
96
|
+
var ProjectType;
|
|
97
|
+
(function (ProjectType) {
|
|
98
|
+
ProjectType[ProjectType["Copilot"] = 0] = "Copilot";
|
|
99
|
+
ProjectType[ProjectType["SME"] = 1] = "SME";
|
|
100
|
+
ProjectType[ProjectType["TeamsAi"] = 2] = "TeamsAi";
|
|
101
|
+
})(ProjectType || (ProjectType = {}));
|
|
79
102
|
|
|
80
103
|
// Copyright (c) Microsoft Corporation.
|
|
81
104
|
class SpecParserError extends Error {
|
|
@@ -102,7 +125,9 @@ ConstantString.ResolveServerUrlFailed = "Unable to resolve the server URL: pleas
|
|
|
102
125
|
ConstantString.OperationOnlyContainsOptionalParam = "Operation %s contains multiple optional parameters. The first optional parameter is used for this command.";
|
|
103
126
|
ConstantString.ConvertSwaggerToOpenAPI = "The Swagger 2.0 file has been converted to OpenAPI 3.0.";
|
|
104
127
|
ConstantString.SwaggerNotSupported = "Swagger 2.0 is not supported. Please convert to OpenAPI 3.0 manually before proceeding.";
|
|
105
|
-
ConstantString.
|
|
128
|
+
ConstantString.SpecVersionNotSupported = "Unsupported OpenAPI version %s. Please use version 3.0.x.";
|
|
129
|
+
ConstantString.MultipleAuthNotSupported = "Multiple authentication methods are unsupported. Ensure all selected APIs use identical authentication.";
|
|
130
|
+
ConstantString.UnsupportedSchema = "Unsupported schema in %s %s: %s";
|
|
106
131
|
ConstantString.WrappedCardVersion = "devPreview";
|
|
107
132
|
ConstantString.WrappedCardSchema = "https://developer.microsoft.com/json-schemas/teams/vDevPreview/MicrosoftTeams.ResponseRenderingTemplate.schema.json";
|
|
108
133
|
ConstantString.WrappedCardResponseLayout = "list";
|
|
@@ -114,6 +139,7 @@ ConstantString.AdaptiveCardType = "AdaptiveCard";
|
|
|
114
139
|
ConstantString.TextBlockType = "TextBlock";
|
|
115
140
|
ConstantString.ContainerType = "Container";
|
|
116
141
|
ConstantString.RegistrationIdPostfix = "REGISTRATION_ID";
|
|
142
|
+
ConstantString.OAuthRegistrationIdPostFix = "OAUTH_REGISTRATION_ID";
|
|
117
143
|
ConstantString.ResponseCodeFor20X = [
|
|
118
144
|
"200",
|
|
119
145
|
"201",
|
|
@@ -173,205 +199,36 @@ ConstantString.FullDescriptionMaxLens = 4000;
|
|
|
173
199
|
ConstantString.CommandDescriptionMaxLens = 128;
|
|
174
200
|
ConstantString.ParameterDescriptionMaxLens = 128;
|
|
175
201
|
ConstantString.CommandTitleMaxLens = 32;
|
|
176
|
-
ConstantString.ParameterTitleMaxLens = 32;
|
|
202
|
+
ConstantString.ParameterTitleMaxLens = 32;
|
|
203
|
+
ConstantString.SMERequiredParamsMaxNum = 5;
|
|
204
|
+
ConstantString.DefaultPluginId = "plugin_1";
|
|
177
205
|
|
|
178
206
|
// Copyright (c) Microsoft Corporation.
|
|
179
207
|
class Utils {
|
|
180
|
-
static
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
};
|
|
186
|
-
if (!paramObject) {
|
|
187
|
-
return paramResult;
|
|
188
|
-
}
|
|
189
|
-
for (let i = 0; i < paramObject.length; i++) {
|
|
190
|
-
const param = paramObject[i];
|
|
191
|
-
const schema = param.schema;
|
|
192
|
-
const isRequiredWithoutDefault = param.required && schema.default === undefined;
|
|
193
|
-
if (param.in === "header" || param.in === "cookie") {
|
|
194
|
-
if (isRequiredWithoutDefault) {
|
|
195
|
-
paramResult.isValid = false;
|
|
196
|
-
}
|
|
197
|
-
continue;
|
|
198
|
-
}
|
|
199
|
-
if (schema.type !== "boolean" &&
|
|
200
|
-
schema.type !== "string" &&
|
|
201
|
-
schema.type !== "number" &&
|
|
202
|
-
schema.type !== "integer") {
|
|
203
|
-
if (isRequiredWithoutDefault) {
|
|
204
|
-
paramResult.isValid = false;
|
|
205
|
-
}
|
|
206
|
-
continue;
|
|
207
|
-
}
|
|
208
|
-
if (param.in === "query" || param.in === "path") {
|
|
209
|
-
if (isRequiredWithoutDefault) {
|
|
210
|
-
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
211
|
-
}
|
|
212
|
-
else {
|
|
213
|
-
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
return paramResult;
|
|
218
|
-
}
|
|
219
|
-
static checkPostBody(schema, isRequired = false) {
|
|
220
|
-
var _a;
|
|
221
|
-
const paramResult = {
|
|
222
|
-
requiredNum: 0,
|
|
223
|
-
optionalNum: 0,
|
|
224
|
-
isValid: true,
|
|
225
|
-
};
|
|
226
|
-
if (Object.keys(schema).length === 0) {
|
|
227
|
-
return paramResult;
|
|
228
|
-
}
|
|
229
|
-
const isRequiredWithoutDefault = isRequired && schema.default === undefined;
|
|
230
|
-
if (schema.type === "string" ||
|
|
231
|
-
schema.type === "integer" ||
|
|
232
|
-
schema.type === "boolean" ||
|
|
233
|
-
schema.type === "number") {
|
|
234
|
-
if (isRequiredWithoutDefault) {
|
|
235
|
-
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
236
|
-
}
|
|
237
|
-
else {
|
|
238
|
-
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
else if (schema.type === "object") {
|
|
242
|
-
const { properties } = schema;
|
|
243
|
-
for (const property in properties) {
|
|
244
|
-
let isRequired = false;
|
|
245
|
-
if (schema.required && ((_a = schema.required) === null || _a === void 0 ? void 0 : _a.indexOf(property)) >= 0) {
|
|
246
|
-
isRequired = true;
|
|
247
|
-
}
|
|
248
|
-
const result = Utils.checkPostBody(properties[property], isRequired);
|
|
249
|
-
paramResult.requiredNum += result.requiredNum;
|
|
250
|
-
paramResult.optionalNum += result.optionalNum;
|
|
251
|
-
paramResult.isValid = paramResult.isValid && result.isValid;
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
else {
|
|
255
|
-
if (isRequiredWithoutDefault) {
|
|
256
|
-
paramResult.isValid = false;
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
return paramResult;
|
|
260
|
-
}
|
|
261
|
-
/**
|
|
262
|
-
* Checks if the given API is supported.
|
|
263
|
-
* @param {string} method - The HTTP method of the API.
|
|
264
|
-
* @param {string} path - The path of the API.
|
|
265
|
-
* @param {OpenAPIV3.Document} spec - The OpenAPI specification document.
|
|
266
|
-
* @returns {boolean} - Returns true if the API is supported, false otherwise.
|
|
267
|
-
* @description The following APIs are supported:
|
|
268
|
-
* 1. only support Get/Post operation without auth property
|
|
269
|
-
* 2. parameter inside query or path only support string, number, boolean and integer
|
|
270
|
-
* 3. parameter inside post body only support string, number, boolean, integer and object
|
|
271
|
-
* 4. request body + required parameters <= 1
|
|
272
|
-
* 5. response body should be “application/json” and not empty, and response code should be 20X
|
|
273
|
-
* 6. only support request body with “application/json” content type
|
|
274
|
-
*/
|
|
275
|
-
static isSupportedApi(method, path, spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2) {
|
|
276
|
-
const pathObj = spec.paths[path];
|
|
277
|
-
method = method.toLocaleLowerCase();
|
|
278
|
-
if (pathObj) {
|
|
279
|
-
if ((method === ConstantString.PostMethod || method === ConstantString.GetMethod) &&
|
|
280
|
-
pathObj[method]) {
|
|
281
|
-
const securities = pathObj[method].security;
|
|
282
|
-
const authArray = Utils.getAuthArray(securities, spec);
|
|
283
|
-
if (!Utils.isSupportedAuth(authArray, allowAPIKeyAuth, allowOauth2)) {
|
|
284
|
-
return false;
|
|
285
|
-
}
|
|
286
|
-
const operationObject = pathObj[method];
|
|
287
|
-
if (!allowMissingId && !operationObject.operationId) {
|
|
288
|
-
return false;
|
|
289
|
-
}
|
|
290
|
-
const paramObject = operationObject.parameters;
|
|
291
|
-
const requestBody = operationObject.requestBody;
|
|
292
|
-
const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
|
|
293
|
-
const mediaTypesCount = Object.keys((requestBody === null || requestBody === void 0 ? void 0 : requestBody.content) || {}).length;
|
|
294
|
-
if (mediaTypesCount > 1) {
|
|
295
|
-
return false;
|
|
296
|
-
}
|
|
297
|
-
const responseJson = Utils.getResponseJson(operationObject);
|
|
298
|
-
if (Object.keys(responseJson).length === 0) {
|
|
299
|
-
return false;
|
|
300
|
-
}
|
|
301
|
-
let requestBodyParamResult = {
|
|
302
|
-
requiredNum: 0,
|
|
303
|
-
optionalNum: 0,
|
|
304
|
-
isValid: true,
|
|
305
|
-
};
|
|
306
|
-
if (requestJsonBody) {
|
|
307
|
-
const requestBodySchema = requestJsonBody.schema;
|
|
308
|
-
requestBodyParamResult = Utils.checkPostBody(requestBodySchema, requestBody.required);
|
|
309
|
-
}
|
|
310
|
-
if (!requestBodyParamResult.isValid) {
|
|
311
|
-
return false;
|
|
312
|
-
}
|
|
313
|
-
const paramResult = Utils.checkParameters(paramObject);
|
|
314
|
-
if (!paramResult.isValid) {
|
|
315
|
-
return false;
|
|
316
|
-
}
|
|
317
|
-
if (requestBodyParamResult.requiredNum + paramResult.requiredNum > 1) {
|
|
318
|
-
if (allowMultipleParameters &&
|
|
319
|
-
requestBodyParamResult.requiredNum + paramResult.requiredNum <= 5) {
|
|
320
|
-
return true;
|
|
321
|
-
}
|
|
322
|
-
return false;
|
|
323
|
-
}
|
|
324
|
-
else if (requestBodyParamResult.requiredNum +
|
|
325
|
-
requestBodyParamResult.optionalNum +
|
|
326
|
-
paramResult.requiredNum +
|
|
327
|
-
paramResult.optionalNum ===
|
|
328
|
-
0) {
|
|
329
|
-
return false;
|
|
330
|
-
}
|
|
331
|
-
else {
|
|
208
|
+
static hasNestedObjectInSchema(schema) {
|
|
209
|
+
if (schema.type === "object") {
|
|
210
|
+
for (const property in schema.properties) {
|
|
211
|
+
const nestedSchema = schema.properties[property];
|
|
212
|
+
if (nestedSchema.type === "object") {
|
|
332
213
|
return true;
|
|
333
214
|
}
|
|
334
215
|
}
|
|
335
216
|
}
|
|
336
217
|
return false;
|
|
337
218
|
}
|
|
338
|
-
static
|
|
339
|
-
|
|
340
|
-
return true;
|
|
341
|
-
}
|
|
342
|
-
if (allowAPIKeyAuth || allowOauth2) {
|
|
343
|
-
// Currently we don't support multiple auth in one operation
|
|
344
|
-
if (authSchemaArray.length > 0 && authSchemaArray.every((auths) => auths.length > 1)) {
|
|
345
|
-
return false;
|
|
346
|
-
}
|
|
347
|
-
for (const auths of authSchemaArray) {
|
|
348
|
-
if (auths.length === 1) {
|
|
349
|
-
if (!allowOauth2 && allowAPIKeyAuth && Utils.isAPIKeyAuth(auths[0].authSchema)) {
|
|
350
|
-
return true;
|
|
351
|
-
}
|
|
352
|
-
else if (!allowAPIKeyAuth &&
|
|
353
|
-
allowOauth2 &&
|
|
354
|
-
Utils.isBearerTokenAuth(auths[0].authSchema)) {
|
|
355
|
-
return true;
|
|
356
|
-
}
|
|
357
|
-
else if (allowAPIKeyAuth &&
|
|
358
|
-
allowOauth2 &&
|
|
359
|
-
(Utils.isAPIKeyAuth(auths[0].authSchema) ||
|
|
360
|
-
Utils.isBearerTokenAuth(auths[0].authSchema))) {
|
|
361
|
-
return true;
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
return false;
|
|
219
|
+
static containMultipleMediaTypes(bodyObject) {
|
|
220
|
+
return Object.keys((bodyObject === null || bodyObject === void 0 ? void 0 : bodyObject.content) || {}).length > 1;
|
|
367
221
|
}
|
|
368
|
-
static
|
|
369
|
-
return
|
|
222
|
+
static isBearerTokenAuth(authScheme) {
|
|
223
|
+
return authScheme.type === "http" && authScheme.scheme === "bearer";
|
|
370
224
|
}
|
|
371
|
-
static
|
|
372
|
-
return
|
|
373
|
-
|
|
374
|
-
|
|
225
|
+
static isAPIKeyAuth(authScheme) {
|
|
226
|
+
return authScheme.type === "apiKey";
|
|
227
|
+
}
|
|
228
|
+
static isOAuthWithAuthCodeFlow(authScheme) {
|
|
229
|
+
return !!(authScheme.type === "oauth2" &&
|
|
230
|
+
authScheme.flows &&
|
|
231
|
+
authScheme.flows.authorizationCode);
|
|
375
232
|
}
|
|
376
233
|
static getAuthArray(securities, spec) {
|
|
377
234
|
var _a;
|
|
@@ -384,7 +241,7 @@ class Utils {
|
|
|
384
241
|
for (const name in security) {
|
|
385
242
|
const auth = securitySchemas[name];
|
|
386
243
|
authArray.push({
|
|
387
|
-
|
|
244
|
+
authScheme: auth,
|
|
388
245
|
name: name,
|
|
389
246
|
});
|
|
390
247
|
}
|
|
@@ -402,18 +259,22 @@ class Utils {
|
|
|
402
259
|
static getResponseJson(operationObject) {
|
|
403
260
|
var _a, _b;
|
|
404
261
|
let json = {};
|
|
262
|
+
let multipleMediaType = false;
|
|
405
263
|
for (const code of ConstantString.ResponseCodeFor20X) {
|
|
406
264
|
const responseObject = (_a = operationObject === null || operationObject === void 0 ? void 0 : operationObject.responses) === null || _a === void 0 ? void 0 : _a[code];
|
|
407
|
-
const mediaTypesCount = Object.keys((responseObject === null || responseObject === void 0 ? void 0 : responseObject.content) || {}).length;
|
|
408
|
-
if (mediaTypesCount > 1) {
|
|
409
|
-
return {};
|
|
410
|
-
}
|
|
411
265
|
if ((_b = responseObject === null || responseObject === void 0 ? void 0 : responseObject.content) === null || _b === void 0 ? void 0 : _b["application/json"]) {
|
|
266
|
+
multipleMediaType = false;
|
|
412
267
|
json = responseObject.content["application/json"];
|
|
413
|
-
|
|
268
|
+
if (Utils.containMultipleMediaTypes(responseObject)) {
|
|
269
|
+
multipleMediaType = true;
|
|
270
|
+
json = {};
|
|
271
|
+
}
|
|
272
|
+
else {
|
|
273
|
+
break;
|
|
274
|
+
}
|
|
414
275
|
}
|
|
415
276
|
}
|
|
416
|
-
return json;
|
|
277
|
+
return { json, multipleMediaType };
|
|
417
278
|
}
|
|
418
279
|
static convertPathToCamelCase(path) {
|
|
419
280
|
const pathSegments = path.split(/[./{]/);
|
|
@@ -433,10 +294,10 @@ class Utils {
|
|
|
433
294
|
return undefined;
|
|
434
295
|
}
|
|
435
296
|
}
|
|
436
|
-
static
|
|
297
|
+
static resolveEnv(str) {
|
|
437
298
|
const placeHolderReg = /\${{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*}}/g;
|
|
438
|
-
let matches = placeHolderReg.exec(
|
|
439
|
-
let
|
|
299
|
+
let matches = placeHolderReg.exec(str);
|
|
300
|
+
let newStr = str;
|
|
440
301
|
while (matches != null) {
|
|
441
302
|
const envVar = matches[1];
|
|
442
303
|
const envVal = process.env[envVar];
|
|
@@ -444,17 +305,17 @@ class Utils {
|
|
|
444
305
|
throw new Error(Utils.format(ConstantString.ResolveServerUrlFailed, envVar));
|
|
445
306
|
}
|
|
446
307
|
else {
|
|
447
|
-
|
|
308
|
+
newStr = newStr.replace(matches[0], envVal);
|
|
448
309
|
}
|
|
449
|
-
matches = placeHolderReg.exec(
|
|
310
|
+
matches = placeHolderReg.exec(str);
|
|
450
311
|
}
|
|
451
|
-
return
|
|
312
|
+
return newStr;
|
|
452
313
|
}
|
|
453
314
|
static checkServerUrl(servers) {
|
|
454
315
|
const errors = [];
|
|
455
316
|
let serverUrl;
|
|
456
317
|
try {
|
|
457
|
-
serverUrl = Utils.
|
|
318
|
+
serverUrl = Utils.resolveEnv(servers[0].url);
|
|
458
319
|
}
|
|
459
320
|
catch (err) {
|
|
460
321
|
errors.push({
|
|
@@ -484,7 +345,8 @@ class Utils {
|
|
|
484
345
|
}
|
|
485
346
|
return errors;
|
|
486
347
|
}
|
|
487
|
-
static validateServer(spec,
|
|
348
|
+
static validateServer(spec, options) {
|
|
349
|
+
var _a;
|
|
488
350
|
const errors = [];
|
|
489
351
|
let hasTopLevelServers = false;
|
|
490
352
|
let hasPathLevelServers = false;
|
|
@@ -505,7 +367,7 @@ class Utils {
|
|
|
505
367
|
}
|
|
506
368
|
for (const method in methods) {
|
|
507
369
|
const operationObject = methods[method];
|
|
508
|
-
if (
|
|
370
|
+
if (((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && operationObject) {
|
|
509
371
|
if ((operationObject === null || operationObject === void 0 ? void 0 : operationObject.servers) && operationObject.servers.length >= 1) {
|
|
510
372
|
hasOperationLevelServers = true;
|
|
511
373
|
const serverErrors = Utils.checkServerUrl(operationObject.servers);
|
|
@@ -548,6 +410,7 @@ class Utils {
|
|
|
548
410
|
Utils.updateParameterWithInputType(schema, parameter);
|
|
549
411
|
}
|
|
550
412
|
if (isRequired && schema.default === undefined) {
|
|
413
|
+
parameter.isRequired = true;
|
|
551
414
|
requiredParams.push(parameter);
|
|
552
415
|
}
|
|
553
416
|
else {
|
|
@@ -592,7 +455,7 @@ class Utils {
|
|
|
592
455
|
param.value = schema.default;
|
|
593
456
|
}
|
|
594
457
|
}
|
|
595
|
-
static parseApiInfo(operationItem,
|
|
458
|
+
static parseApiInfo(operationItem, options) {
|
|
596
459
|
var _a, _b;
|
|
597
460
|
const requiredParams = [];
|
|
598
461
|
const optionalParams = [];
|
|
@@ -606,11 +469,12 @@ class Utils {
|
|
|
606
469
|
description: ((_a = param.description) !== null && _a !== void 0 ? _a : "").slice(0, ConstantString.ParameterDescriptionMaxLens),
|
|
607
470
|
};
|
|
608
471
|
const schema = param.schema;
|
|
609
|
-
if (allowMultipleParameters && schema) {
|
|
472
|
+
if (options.allowMultipleParameters && schema) {
|
|
610
473
|
Utils.updateParameterWithInputType(schema, parameter);
|
|
611
474
|
}
|
|
612
475
|
if (param.in !== "header" && param.in !== "cookie") {
|
|
613
476
|
if (param.required && (schema === null || schema === void 0 ? void 0 : schema.default) === undefined) {
|
|
477
|
+
parameter.isRequired = true;
|
|
614
478
|
requiredParams.push(parameter);
|
|
615
479
|
}
|
|
616
480
|
else {
|
|
@@ -624,19 +488,13 @@ class Utils {
|
|
|
624
488
|
const requestJson = requestBody.content["application/json"];
|
|
625
489
|
if (Object.keys(requestJson).length !== 0) {
|
|
626
490
|
const schema = requestJson.schema;
|
|
627
|
-
const [requiredP, optionalP] = Utils.generateParametersFromSchema(schema, "requestBody", allowMultipleParameters, requestBody.required);
|
|
491
|
+
const [requiredP, optionalP] = Utils.generateParametersFromSchema(schema, "requestBody", !!options.allowMultipleParameters, requestBody.required);
|
|
628
492
|
requiredParams.push(...requiredP);
|
|
629
493
|
optionalParams.push(...optionalP);
|
|
630
494
|
}
|
|
631
495
|
}
|
|
632
496
|
const operationId = operationItem.operationId;
|
|
633
|
-
const parameters = [];
|
|
634
|
-
if (requiredParams.length !== 0) {
|
|
635
|
-
parameters.push(...requiredParams);
|
|
636
|
-
}
|
|
637
|
-
else {
|
|
638
|
-
parameters.push(optionalParams[0]);
|
|
639
|
-
}
|
|
497
|
+
const parameters = [...requiredParams, ...optionalParams];
|
|
640
498
|
const command = {
|
|
641
499
|
context: ["compose"],
|
|
642
500
|
type: "query",
|
|
@@ -645,105 +503,535 @@ class Utils {
|
|
|
645
503
|
parameters: parameters,
|
|
646
504
|
description: ((_b = operationItem.description) !== null && _b !== void 0 ? _b : "").slice(0, ConstantString.CommandDescriptionMaxLens),
|
|
647
505
|
};
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
506
|
+
return command;
|
|
507
|
+
}
|
|
508
|
+
static format(str, ...args) {
|
|
509
|
+
let index = 0;
|
|
510
|
+
return str.replace(/%s/g, () => {
|
|
511
|
+
const arg = args[index++];
|
|
512
|
+
return arg !== undefined ? arg : "";
|
|
513
|
+
});
|
|
514
|
+
}
|
|
515
|
+
static getSafeRegistrationIdEnvName(authName) {
|
|
516
|
+
if (!authName) {
|
|
517
|
+
return "";
|
|
655
518
|
}
|
|
656
|
-
|
|
519
|
+
let safeRegistrationIdEnvName = authName.toUpperCase().replace(/[^A-Z0-9_]/g, "_");
|
|
520
|
+
if (!safeRegistrationIdEnvName.match(/^[A-Z]/)) {
|
|
521
|
+
safeRegistrationIdEnvName = "PREFIX_" + safeRegistrationIdEnvName;
|
|
522
|
+
}
|
|
523
|
+
return safeRegistrationIdEnvName;
|
|
657
524
|
}
|
|
658
|
-
static
|
|
659
|
-
const
|
|
525
|
+
static getServerObject(spec, method, path) {
|
|
526
|
+
const pathObj = spec.paths[path];
|
|
527
|
+
const operationObject = pathObj[method];
|
|
528
|
+
const rootServer = spec.servers && spec.servers[0];
|
|
529
|
+
const methodServer = spec.paths[path].servers && spec.paths[path].servers[0];
|
|
530
|
+
const operationServer = operationObject.servers && operationObject.servers[0];
|
|
531
|
+
const serverUrl = operationServer || methodServer || rootServer;
|
|
532
|
+
return serverUrl;
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
// Copyright (c) Microsoft Corporation.
|
|
537
|
+
class Validator {
|
|
538
|
+
listAPIs() {
|
|
539
|
+
var _a;
|
|
540
|
+
if (this.apiMap) {
|
|
541
|
+
return this.apiMap;
|
|
542
|
+
}
|
|
543
|
+
const paths = this.spec.paths;
|
|
660
544
|
const result = {};
|
|
661
545
|
for (const path in paths) {
|
|
662
546
|
const methods = paths[path];
|
|
663
547
|
for (const method in methods) {
|
|
664
|
-
|
|
665
|
-
if (
|
|
666
|
-
const
|
|
667
|
-
result[`${method.toUpperCase()} ${path}`] =
|
|
548
|
+
const operationObject = methods[method];
|
|
549
|
+
if (((_a = this.options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && operationObject) {
|
|
550
|
+
const validateResult = this.validateAPI(method, path);
|
|
551
|
+
result[`${method.toUpperCase()} ${path}`] = {
|
|
552
|
+
operation: operationObject,
|
|
553
|
+
isValid: validateResult.isValid,
|
|
554
|
+
reason: validateResult.reason,
|
|
555
|
+
};
|
|
668
556
|
}
|
|
669
557
|
}
|
|
670
558
|
}
|
|
559
|
+
this.apiMap = result;
|
|
671
560
|
return result;
|
|
672
561
|
}
|
|
673
|
-
|
|
674
|
-
const
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
});
|
|
681
|
-
}
|
|
682
|
-
// Server validation
|
|
683
|
-
const serverErrors = Utils.validateServer(spec, allowMissingId, allowAPIKeyAuth, allowMultipleParameters, allowOauth2);
|
|
684
|
-
errors.push(...serverErrors);
|
|
685
|
-
// Remote reference not supported
|
|
686
|
-
const refPaths = parser.$refs.paths();
|
|
687
|
-
// refPaths [0] is the current spec file path
|
|
688
|
-
if (refPaths.length > 1) {
|
|
689
|
-
errors.push({
|
|
690
|
-
type: ErrorType.RemoteRefNotSupported,
|
|
691
|
-
content: Utils.format(ConstantString.RemoteRefNotSupported, refPaths.join(", ")),
|
|
692
|
-
data: refPaths,
|
|
562
|
+
validateSpecVersion() {
|
|
563
|
+
const result = { errors: [], warnings: [] };
|
|
564
|
+
if (this.spec.openapi >= "3.1.0") {
|
|
565
|
+
result.errors.push({
|
|
566
|
+
type: ErrorType.SpecVersionNotSupported,
|
|
567
|
+
content: Utils.format(ConstantString.SpecVersionNotSupported, this.spec.openapi),
|
|
568
|
+
data: this.spec.openapi,
|
|
693
569
|
});
|
|
694
570
|
}
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
571
|
+
return result;
|
|
572
|
+
}
|
|
573
|
+
validateSpecServer() {
|
|
574
|
+
const result = { errors: [], warnings: [] };
|
|
575
|
+
const serverErrors = Utils.validateServer(this.spec, this.options);
|
|
576
|
+
result.errors.push(...serverErrors);
|
|
577
|
+
return result;
|
|
578
|
+
}
|
|
579
|
+
validateSpecNoSupportAPI() {
|
|
580
|
+
const result = { errors: [], warnings: [] };
|
|
581
|
+
const apiMap = this.listAPIs();
|
|
582
|
+
const validAPIs = Object.entries(apiMap).filter(([, value]) => value.isValid);
|
|
583
|
+
if (validAPIs.length === 0) {
|
|
584
|
+
const data = [];
|
|
585
|
+
for (const key in apiMap) {
|
|
586
|
+
const { reason } = apiMap[key];
|
|
587
|
+
const apiInvalidReason = { api: key, reason: reason };
|
|
588
|
+
data.push(apiInvalidReason);
|
|
589
|
+
}
|
|
590
|
+
result.errors.push({
|
|
699
591
|
type: ErrorType.NoSupportedApi,
|
|
700
592
|
content: ConstantString.NoSupportedApi,
|
|
593
|
+
data,
|
|
701
594
|
});
|
|
702
595
|
}
|
|
596
|
+
return result;
|
|
597
|
+
}
|
|
598
|
+
validateSpecOperationId() {
|
|
599
|
+
const result = { errors: [], warnings: [] };
|
|
600
|
+
const apiMap = this.listAPIs();
|
|
703
601
|
// OperationId missing
|
|
704
602
|
const apisMissingOperationId = [];
|
|
705
603
|
for (const key in apiMap) {
|
|
706
|
-
const
|
|
707
|
-
if (!
|
|
604
|
+
const { operation } = apiMap[key];
|
|
605
|
+
if (!operation.operationId) {
|
|
708
606
|
apisMissingOperationId.push(key);
|
|
709
607
|
}
|
|
710
608
|
}
|
|
711
609
|
if (apisMissingOperationId.length > 0) {
|
|
712
|
-
warnings.push({
|
|
610
|
+
result.warnings.push({
|
|
713
611
|
type: WarningType.OperationIdMissing,
|
|
714
612
|
content: Utils.format(ConstantString.MissingOperationId, apisMissingOperationId.join(", ")),
|
|
715
613
|
data: apisMissingOperationId,
|
|
716
614
|
});
|
|
717
615
|
}
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
616
|
+
return result;
|
|
617
|
+
}
|
|
618
|
+
validateMethodAndPath(method, path) {
|
|
619
|
+
const result = { isValid: true, reason: [] };
|
|
620
|
+
if (this.options.allowMethods && !this.options.allowMethods.includes(method)) {
|
|
621
|
+
result.isValid = false;
|
|
622
|
+
result.reason.push(ErrorType.MethodNotAllowed);
|
|
623
|
+
return result;
|
|
624
|
+
}
|
|
625
|
+
const pathObj = this.spec.paths[path];
|
|
626
|
+
if (!pathObj || !pathObj[method]) {
|
|
627
|
+
result.isValid = false;
|
|
628
|
+
result.reason.push(ErrorType.UrlPathNotExist);
|
|
629
|
+
return result;
|
|
721
630
|
}
|
|
722
|
-
|
|
723
|
-
|
|
631
|
+
return result;
|
|
632
|
+
}
|
|
633
|
+
validateResponse(method, path) {
|
|
634
|
+
const result = { isValid: true, reason: [] };
|
|
635
|
+
const operationObject = this.spec.paths[path][method];
|
|
636
|
+
const { json, multipleMediaType } = Utils.getResponseJson(operationObject);
|
|
637
|
+
// only support response body only contains “application/json” content type
|
|
638
|
+
if (multipleMediaType) {
|
|
639
|
+
result.reason.push(ErrorType.ResponseContainMultipleMediaTypes);
|
|
724
640
|
}
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
641
|
+
else if (Object.keys(json).length === 0) {
|
|
642
|
+
// response body should not be empty
|
|
643
|
+
result.reason.push(ErrorType.ResponseJsonIsEmpty);
|
|
644
|
+
}
|
|
645
|
+
return result;
|
|
646
|
+
}
|
|
647
|
+
validateServer(method, path) {
|
|
648
|
+
const result = { isValid: true, reason: [] };
|
|
649
|
+
const serverObj = Utils.getServerObject(this.spec, method, path);
|
|
650
|
+
if (!serverObj) {
|
|
651
|
+
// should contain server URL
|
|
652
|
+
result.reason.push(ErrorType.NoServerInformation);
|
|
653
|
+
}
|
|
654
|
+
else {
|
|
655
|
+
// server url should be absolute url with https protocol
|
|
656
|
+
const serverValidateResult = Utils.checkServerUrl([serverObj]);
|
|
657
|
+
result.reason.push(...serverValidateResult.map((item) => item.type));
|
|
658
|
+
}
|
|
659
|
+
return result;
|
|
660
|
+
}
|
|
661
|
+
validateAuth(method, path) {
|
|
662
|
+
const pathObj = this.spec.paths[path];
|
|
663
|
+
const operationObject = pathObj[method];
|
|
664
|
+
const securities = operationObject.security;
|
|
665
|
+
const authSchemeArray = Utils.getAuthArray(securities, this.spec);
|
|
666
|
+
if (authSchemeArray.length === 0) {
|
|
667
|
+
return { isValid: true, reason: [] };
|
|
668
|
+
}
|
|
669
|
+
if (this.options.allowAPIKeyAuth ||
|
|
670
|
+
this.options.allowOauth2 ||
|
|
671
|
+
this.options.allowBearerTokenAuth) {
|
|
672
|
+
// Currently we don't support multiple auth in one operation
|
|
673
|
+
if (authSchemeArray.length > 0 && authSchemeArray.every((auths) => auths.length > 1)) {
|
|
674
|
+
return {
|
|
675
|
+
isValid: false,
|
|
676
|
+
reason: [ErrorType.MultipleAuthNotSupported],
|
|
677
|
+
};
|
|
678
|
+
}
|
|
679
|
+
for (const auths of authSchemeArray) {
|
|
680
|
+
if (auths.length === 1) {
|
|
681
|
+
if ((this.options.allowAPIKeyAuth && Utils.isAPIKeyAuth(auths[0].authScheme)) ||
|
|
682
|
+
(this.options.allowOauth2 && Utils.isOAuthWithAuthCodeFlow(auths[0].authScheme)) ||
|
|
683
|
+
(this.options.allowBearerTokenAuth && Utils.isBearerTokenAuth(auths[0].authScheme))) {
|
|
684
|
+
return { isValid: true, reason: [] };
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
return { isValid: false, reason: [ErrorType.AuthTypeIsNotSupported] };
|
|
690
|
+
}
|
|
691
|
+
checkPostBodySchema(schema, isRequired = false) {
|
|
692
|
+
var _a;
|
|
693
|
+
const paramResult = {
|
|
694
|
+
requiredNum: 0,
|
|
695
|
+
optionalNum: 0,
|
|
696
|
+
isValid: true,
|
|
697
|
+
reason: [],
|
|
729
698
|
};
|
|
699
|
+
if (Object.keys(schema).length === 0) {
|
|
700
|
+
return paramResult;
|
|
701
|
+
}
|
|
702
|
+
const isRequiredWithoutDefault = isRequired && schema.default === undefined;
|
|
703
|
+
const isCopilot = this.projectType === ProjectType.Copilot;
|
|
704
|
+
if (isCopilot && this.hasNestedObjectInSchema(schema)) {
|
|
705
|
+
paramResult.isValid = false;
|
|
706
|
+
paramResult.reason = [ErrorType.RequestBodyContainsNestedObject];
|
|
707
|
+
return paramResult;
|
|
708
|
+
}
|
|
709
|
+
if (schema.type === "string" ||
|
|
710
|
+
schema.type === "integer" ||
|
|
711
|
+
schema.type === "boolean" ||
|
|
712
|
+
schema.type === "number") {
|
|
713
|
+
if (isRequiredWithoutDefault) {
|
|
714
|
+
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
715
|
+
}
|
|
716
|
+
else {
|
|
717
|
+
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
else if (schema.type === "object") {
|
|
721
|
+
const { properties } = schema;
|
|
722
|
+
for (const property in properties) {
|
|
723
|
+
let isRequired = false;
|
|
724
|
+
if (schema.required && ((_a = schema.required) === null || _a === void 0 ? void 0 : _a.indexOf(property)) >= 0) {
|
|
725
|
+
isRequired = true;
|
|
726
|
+
}
|
|
727
|
+
const result = this.checkPostBodySchema(properties[property], isRequired);
|
|
728
|
+
paramResult.requiredNum += result.requiredNum;
|
|
729
|
+
paramResult.optionalNum += result.optionalNum;
|
|
730
|
+
paramResult.isValid = paramResult.isValid && result.isValid;
|
|
731
|
+
paramResult.reason.push(...result.reason);
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
else {
|
|
735
|
+
if (isRequiredWithoutDefault && !isCopilot) {
|
|
736
|
+
paramResult.isValid = false;
|
|
737
|
+
paramResult.reason.push(ErrorType.PostBodyContainsRequiredUnsupportedSchema);
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
return paramResult;
|
|
730
741
|
}
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
742
|
+
checkParamSchema(paramObject) {
|
|
743
|
+
const paramResult = {
|
|
744
|
+
requiredNum: 0,
|
|
745
|
+
optionalNum: 0,
|
|
746
|
+
isValid: true,
|
|
747
|
+
reason: [],
|
|
748
|
+
};
|
|
749
|
+
if (!paramObject) {
|
|
750
|
+
return paramResult;
|
|
751
|
+
}
|
|
752
|
+
const isCopilot = this.projectType === ProjectType.Copilot;
|
|
753
|
+
for (let i = 0; i < paramObject.length; i++) {
|
|
754
|
+
const param = paramObject[i];
|
|
755
|
+
const schema = param.schema;
|
|
756
|
+
if (isCopilot && this.hasNestedObjectInSchema(schema)) {
|
|
757
|
+
paramResult.isValid = false;
|
|
758
|
+
paramResult.reason.push(ErrorType.ParamsContainsNestedObject);
|
|
759
|
+
continue;
|
|
760
|
+
}
|
|
761
|
+
const isRequiredWithoutDefault = param.required && schema.default === undefined;
|
|
762
|
+
if (isCopilot) {
|
|
763
|
+
if (isRequiredWithoutDefault) {
|
|
764
|
+
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
765
|
+
}
|
|
766
|
+
else {
|
|
767
|
+
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
768
|
+
}
|
|
769
|
+
continue;
|
|
770
|
+
}
|
|
771
|
+
if (param.in === "header" || param.in === "cookie") {
|
|
772
|
+
if (isRequiredWithoutDefault) {
|
|
773
|
+
paramResult.isValid = false;
|
|
774
|
+
paramResult.reason.push(ErrorType.ParamsContainRequiredUnsupportedSchema);
|
|
775
|
+
}
|
|
776
|
+
continue;
|
|
777
|
+
}
|
|
778
|
+
if (schema.type !== "boolean" &&
|
|
779
|
+
schema.type !== "string" &&
|
|
780
|
+
schema.type !== "number" &&
|
|
781
|
+
schema.type !== "integer") {
|
|
782
|
+
if (isRequiredWithoutDefault) {
|
|
783
|
+
paramResult.isValid = false;
|
|
784
|
+
paramResult.reason.push(ErrorType.ParamsContainRequiredUnsupportedSchema);
|
|
785
|
+
}
|
|
786
|
+
continue;
|
|
787
|
+
}
|
|
788
|
+
if (param.in === "query" || param.in === "path") {
|
|
789
|
+
if (isRequiredWithoutDefault) {
|
|
790
|
+
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
791
|
+
}
|
|
792
|
+
else {
|
|
793
|
+
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
return paramResult;
|
|
737
798
|
}
|
|
738
|
-
|
|
739
|
-
if (
|
|
740
|
-
|
|
799
|
+
hasNestedObjectInSchema(schema) {
|
|
800
|
+
if (schema.type === "object") {
|
|
801
|
+
for (const property in schema.properties) {
|
|
802
|
+
const nestedSchema = schema.properties[property];
|
|
803
|
+
if (nestedSchema.type === "object") {
|
|
804
|
+
return true;
|
|
805
|
+
}
|
|
806
|
+
}
|
|
741
807
|
}
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
808
|
+
return false;
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
// Copyright (c) Microsoft Corporation.
|
|
813
|
+
class CopilotValidator extends Validator {
|
|
814
|
+
constructor(spec, options) {
|
|
815
|
+
super();
|
|
816
|
+
this.projectType = ProjectType.Copilot;
|
|
817
|
+
this.options = options;
|
|
818
|
+
this.spec = spec;
|
|
819
|
+
}
|
|
820
|
+
validateSpec() {
|
|
821
|
+
const result = { errors: [], warnings: [] };
|
|
822
|
+
// validate spec version
|
|
823
|
+
let validationResult = this.validateSpecVersion();
|
|
824
|
+
result.errors.push(...validationResult.errors);
|
|
825
|
+
// validate spec server
|
|
826
|
+
validationResult = this.validateSpecServer();
|
|
827
|
+
result.errors.push(...validationResult.errors);
|
|
828
|
+
// validate no supported API
|
|
829
|
+
validationResult = this.validateSpecNoSupportAPI();
|
|
830
|
+
result.errors.push(...validationResult.errors);
|
|
831
|
+
// validate operationId missing
|
|
832
|
+
validationResult = this.validateSpecOperationId();
|
|
833
|
+
result.warnings.push(...validationResult.warnings);
|
|
834
|
+
return result;
|
|
835
|
+
}
|
|
836
|
+
validateAPI(method, path) {
|
|
837
|
+
const result = { isValid: true, reason: [] };
|
|
838
|
+
method = method.toLocaleLowerCase();
|
|
839
|
+
// validate method and path
|
|
840
|
+
const methodAndPathResult = this.validateMethodAndPath(method, path);
|
|
841
|
+
if (!methodAndPathResult.isValid) {
|
|
842
|
+
return methodAndPathResult;
|
|
843
|
+
}
|
|
844
|
+
const operationObject = this.spec.paths[path][method];
|
|
845
|
+
// validate auth
|
|
846
|
+
const authCheckResult = this.validateAuth(method, path);
|
|
847
|
+
result.reason.push(...authCheckResult.reason);
|
|
848
|
+
// validate operationId
|
|
849
|
+
if (!this.options.allowMissingId && !operationObject.operationId) {
|
|
850
|
+
result.reason.push(ErrorType.MissingOperationId);
|
|
851
|
+
}
|
|
852
|
+
// validate server
|
|
853
|
+
const validateServerResult = this.validateServer(method, path);
|
|
854
|
+
result.reason.push(...validateServerResult.reason);
|
|
855
|
+
// validate response
|
|
856
|
+
const validateResponseResult = this.validateResponse(method, path);
|
|
857
|
+
result.reason.push(...validateResponseResult.reason);
|
|
858
|
+
// validate requestBody
|
|
859
|
+
const requestBody = operationObject.requestBody;
|
|
860
|
+
const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
|
|
861
|
+
if (Utils.containMultipleMediaTypes(requestBody)) {
|
|
862
|
+
result.reason.push(ErrorType.PostBodyContainMultipleMediaTypes);
|
|
863
|
+
}
|
|
864
|
+
if (requestJsonBody) {
|
|
865
|
+
const requestBodySchema = requestJsonBody.schema;
|
|
866
|
+
if (requestBodySchema.type !== "object") {
|
|
867
|
+
result.reason.push(ErrorType.PostBodySchemaIsNotJson);
|
|
868
|
+
}
|
|
869
|
+
const requestBodyParamResult = this.checkPostBodySchema(requestBodySchema, requestBody.required);
|
|
870
|
+
result.reason.push(...requestBodyParamResult.reason);
|
|
871
|
+
}
|
|
872
|
+
// validate parameters
|
|
873
|
+
const paramObject = operationObject.parameters;
|
|
874
|
+
const paramResult = this.checkParamSchema(paramObject);
|
|
875
|
+
result.reason.push(...paramResult.reason);
|
|
876
|
+
if (result.reason.length > 0) {
|
|
877
|
+
result.isValid = false;
|
|
878
|
+
}
|
|
879
|
+
return result;
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
// Copyright (c) Microsoft Corporation.
|
|
884
|
+
class SMEValidator extends Validator {
|
|
885
|
+
constructor(spec, options) {
|
|
886
|
+
super();
|
|
887
|
+
this.projectType = ProjectType.SME;
|
|
888
|
+
this.options = options;
|
|
889
|
+
this.spec = spec;
|
|
890
|
+
}
|
|
891
|
+
validateSpec() {
|
|
892
|
+
const result = { errors: [], warnings: [] };
|
|
893
|
+
// validate spec version
|
|
894
|
+
let validationResult = this.validateSpecVersion();
|
|
895
|
+
result.errors.push(...validationResult.errors);
|
|
896
|
+
// validate spec server
|
|
897
|
+
validationResult = this.validateSpecServer();
|
|
898
|
+
result.errors.push(...validationResult.errors);
|
|
899
|
+
// validate no supported API
|
|
900
|
+
validationResult = this.validateSpecNoSupportAPI();
|
|
901
|
+
result.errors.push(...validationResult.errors);
|
|
902
|
+
// validate operationId missing
|
|
903
|
+
if (this.options.allowMissingId) {
|
|
904
|
+
validationResult = this.validateSpecOperationId();
|
|
905
|
+
result.warnings.push(...validationResult.warnings);
|
|
906
|
+
}
|
|
907
|
+
return result;
|
|
908
|
+
}
|
|
909
|
+
validateAPI(method, path) {
|
|
910
|
+
const result = { isValid: true, reason: [] };
|
|
911
|
+
method = method.toLocaleLowerCase();
|
|
912
|
+
// validate method and path
|
|
913
|
+
const methodAndPathResult = this.validateMethodAndPath(method, path);
|
|
914
|
+
if (!methodAndPathResult.isValid) {
|
|
915
|
+
return methodAndPathResult;
|
|
916
|
+
}
|
|
917
|
+
const operationObject = this.spec.paths[path][method];
|
|
918
|
+
// validate auth
|
|
919
|
+
const authCheckResult = this.validateAuth(method, path);
|
|
920
|
+
result.reason.push(...authCheckResult.reason);
|
|
921
|
+
// validate operationId
|
|
922
|
+
if (!this.options.allowMissingId && !operationObject.operationId) {
|
|
923
|
+
result.reason.push(ErrorType.MissingOperationId);
|
|
924
|
+
}
|
|
925
|
+
// validate server
|
|
926
|
+
const validateServerResult = this.validateServer(method, path);
|
|
927
|
+
result.reason.push(...validateServerResult.reason);
|
|
928
|
+
// validate response
|
|
929
|
+
const validateResponseResult = this.validateResponse(method, path);
|
|
930
|
+
result.reason.push(...validateResponseResult.reason);
|
|
931
|
+
let postBodyResult = {
|
|
932
|
+
requiredNum: 0,
|
|
933
|
+
optionalNum: 0,
|
|
934
|
+
isValid: true,
|
|
935
|
+
reason: [],
|
|
936
|
+
};
|
|
937
|
+
// validate requestBody
|
|
938
|
+
const requestBody = operationObject.requestBody;
|
|
939
|
+
const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
|
|
940
|
+
if (Utils.containMultipleMediaTypes(requestBody)) {
|
|
941
|
+
result.reason.push(ErrorType.PostBodyContainMultipleMediaTypes);
|
|
942
|
+
}
|
|
943
|
+
if (requestJsonBody) {
|
|
944
|
+
const requestBodySchema = requestJsonBody.schema;
|
|
945
|
+
postBodyResult = this.checkPostBodySchema(requestBodySchema, requestBody.required);
|
|
946
|
+
result.reason.push(...postBodyResult.reason);
|
|
947
|
+
}
|
|
948
|
+
// validate parameters
|
|
949
|
+
const paramObject = operationObject.parameters;
|
|
950
|
+
const paramResult = this.checkParamSchema(paramObject);
|
|
951
|
+
result.reason.push(...paramResult.reason);
|
|
952
|
+
// validate total parameters count
|
|
953
|
+
if (paramResult.isValid && postBodyResult.isValid) {
|
|
954
|
+
const paramCountResult = this.validateParamCount(postBodyResult, paramResult);
|
|
955
|
+
result.reason.push(...paramCountResult.reason);
|
|
956
|
+
}
|
|
957
|
+
if (result.reason.length > 0) {
|
|
958
|
+
result.isValid = false;
|
|
959
|
+
}
|
|
960
|
+
return result;
|
|
961
|
+
}
|
|
962
|
+
validateParamCount(postBodyResult, paramResult) {
|
|
963
|
+
const result = { isValid: true, reason: [] };
|
|
964
|
+
const totalRequiredParams = postBodyResult.requiredNum + paramResult.requiredNum;
|
|
965
|
+
const totalParams = totalRequiredParams + postBodyResult.optionalNum + paramResult.optionalNum;
|
|
966
|
+
if (totalRequiredParams > 1) {
|
|
967
|
+
if (!this.options.allowMultipleParameters ||
|
|
968
|
+
totalRequiredParams > SMEValidator.SMERequiredParamsMaxNum) {
|
|
969
|
+
result.reason.push(ErrorType.ExceededRequiredParamsLimit);
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
else if (totalParams === 0) {
|
|
973
|
+
result.reason.push(ErrorType.NoParameter);
|
|
974
|
+
}
|
|
975
|
+
return result;
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
SMEValidator.SMERequiredParamsMaxNum = 5;
|
|
979
|
+
|
|
980
|
+
// Copyright (c) Microsoft Corporation.
|
|
981
|
+
class TeamsAIValidator extends Validator {
|
|
982
|
+
constructor(spec, options) {
|
|
983
|
+
super();
|
|
984
|
+
this.projectType = ProjectType.TeamsAi;
|
|
985
|
+
this.options = options;
|
|
986
|
+
this.spec = spec;
|
|
987
|
+
}
|
|
988
|
+
validateSpec() {
|
|
989
|
+
const result = { errors: [], warnings: [] };
|
|
990
|
+
// validate spec server
|
|
991
|
+
let validationResult = this.validateSpecServer();
|
|
992
|
+
result.errors.push(...validationResult.errors);
|
|
993
|
+
// validate no supported API
|
|
994
|
+
validationResult = this.validateSpecNoSupportAPI();
|
|
995
|
+
result.errors.push(...validationResult.errors);
|
|
996
|
+
return result;
|
|
997
|
+
}
|
|
998
|
+
validateAPI(method, path) {
|
|
999
|
+
const result = { isValid: true, reason: [] };
|
|
1000
|
+
method = method.toLocaleLowerCase();
|
|
1001
|
+
// validate method and path
|
|
1002
|
+
const methodAndPathResult = this.validateMethodAndPath(method, path);
|
|
1003
|
+
if (!methodAndPathResult.isValid) {
|
|
1004
|
+
return methodAndPathResult;
|
|
1005
|
+
}
|
|
1006
|
+
const operationObject = this.spec.paths[path][method];
|
|
1007
|
+
// validate operationId
|
|
1008
|
+
if (!this.options.allowMissingId && !operationObject.operationId) {
|
|
1009
|
+
result.reason.push(ErrorType.MissingOperationId);
|
|
1010
|
+
}
|
|
1011
|
+
// validate server
|
|
1012
|
+
const validateServerResult = this.validateServer(method, path);
|
|
1013
|
+
result.reason.push(...validateServerResult.reason);
|
|
1014
|
+
if (result.reason.length > 0) {
|
|
1015
|
+
result.isValid = false;
|
|
1016
|
+
}
|
|
1017
|
+
return result;
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
class ValidatorFactory {
|
|
1022
|
+
static create(spec, options) {
|
|
1023
|
+
var _a;
|
|
1024
|
+
const type = (_a = options.projectType) !== null && _a !== void 0 ? _a : ProjectType.SME;
|
|
1025
|
+
switch (type) {
|
|
1026
|
+
case ProjectType.SME:
|
|
1027
|
+
return new SMEValidator(spec, options);
|
|
1028
|
+
case ProjectType.Copilot:
|
|
1029
|
+
return new CopilotValidator(spec, options);
|
|
1030
|
+
case ProjectType.TeamsAi:
|
|
1031
|
+
return new TeamsAIValidator(spec, options);
|
|
1032
|
+
default:
|
|
1033
|
+
throw new Error(`Invalid project type: ${type}`);
|
|
745
1034
|
}
|
|
746
|
-
return safeRegistrationIdEnvName;
|
|
747
1035
|
}
|
|
748
1036
|
}
|
|
749
1037
|
|
|
@@ -763,7 +1051,10 @@ class SpecParser {
|
|
|
763
1051
|
allowSwagger: false,
|
|
764
1052
|
allowAPIKeyAuth: false,
|
|
765
1053
|
allowMultipleParameters: false,
|
|
1054
|
+
allowBearerTokenAuth: false,
|
|
766
1055
|
allowOauth2: false,
|
|
1056
|
+
allowMethods: ["get", "post"],
|
|
1057
|
+
projectType: ProjectType.SME,
|
|
767
1058
|
};
|
|
768
1059
|
this.pathOrSpec = pathOrDoc;
|
|
769
1060
|
this.parser = new SwaggerParser();
|
|
@@ -779,11 +1070,7 @@ class SpecParser {
|
|
|
779
1070
|
try {
|
|
780
1071
|
try {
|
|
781
1072
|
yield this.loadSpec();
|
|
782
|
-
yield this.parser.validate(this.spec
|
|
783
|
-
validate: {
|
|
784
|
-
schema: false,
|
|
785
|
-
},
|
|
786
|
-
});
|
|
1073
|
+
yield this.parser.validate(this.spec);
|
|
787
1074
|
}
|
|
788
1075
|
catch (e) {
|
|
789
1076
|
return {
|
|
@@ -792,16 +1079,46 @@ class SpecParser {
|
|
|
792
1079
|
errors: [{ type: ErrorType.SpecNotValid, content: e.toString() }],
|
|
793
1080
|
};
|
|
794
1081
|
}
|
|
1082
|
+
const errors = [];
|
|
1083
|
+
const warnings = [];
|
|
795
1084
|
if (!this.options.allowSwagger && this.isSwaggerFile) {
|
|
796
1085
|
return {
|
|
797
1086
|
status: ValidationStatus.Error,
|
|
798
1087
|
warnings: [],
|
|
799
1088
|
errors: [
|
|
800
|
-
{
|
|
1089
|
+
{
|
|
1090
|
+
type: ErrorType.SwaggerNotSupported,
|
|
1091
|
+
content: ConstantString.SwaggerNotSupported,
|
|
1092
|
+
},
|
|
801
1093
|
],
|
|
802
1094
|
};
|
|
803
1095
|
}
|
|
804
|
-
|
|
1096
|
+
// Remote reference not supported
|
|
1097
|
+
const refPaths = this.parser.$refs.paths();
|
|
1098
|
+
// refPaths [0] is the current spec file path
|
|
1099
|
+
if (refPaths.length > 1) {
|
|
1100
|
+
errors.push({
|
|
1101
|
+
type: ErrorType.RemoteRefNotSupported,
|
|
1102
|
+
content: Utils.format(ConstantString.RemoteRefNotSupported, refPaths.join(", ")),
|
|
1103
|
+
data: refPaths,
|
|
1104
|
+
});
|
|
1105
|
+
}
|
|
1106
|
+
const validator = this.getValidator(this.spec);
|
|
1107
|
+
const validationResult = validator.validateSpec();
|
|
1108
|
+
warnings.push(...validationResult.warnings);
|
|
1109
|
+
errors.push(...validationResult.errors);
|
|
1110
|
+
let status = ValidationStatus.Valid;
|
|
1111
|
+
if (warnings.length > 0 && errors.length === 0) {
|
|
1112
|
+
status = ValidationStatus.Warning;
|
|
1113
|
+
}
|
|
1114
|
+
else if (errors.length > 0) {
|
|
1115
|
+
status = ValidationStatus.Error;
|
|
1116
|
+
}
|
|
1117
|
+
return {
|
|
1118
|
+
status: status,
|
|
1119
|
+
warnings: warnings,
|
|
1120
|
+
errors: errors,
|
|
1121
|
+
};
|
|
805
1122
|
}
|
|
806
1123
|
catch (err) {
|
|
807
1124
|
throw new SpecParserError(err.toString(), ErrorType.ValidateFailed);
|
|
@@ -812,17 +1129,20 @@ class SpecParser {
|
|
|
812
1129
|
return __awaiter(this, void 0, void 0, function* () {
|
|
813
1130
|
try {
|
|
814
1131
|
yield this.loadSpec();
|
|
815
|
-
const apiMap = this.
|
|
1132
|
+
const apiMap = this.getAPIs(this.spec);
|
|
816
1133
|
const apiInfos = [];
|
|
817
1134
|
for (const key in apiMap) {
|
|
818
|
-
const
|
|
1135
|
+
const { operation, isValid } = apiMap[key];
|
|
1136
|
+
if (!isValid) {
|
|
1137
|
+
continue;
|
|
1138
|
+
}
|
|
819
1139
|
const [method, path] = key.split(" ");
|
|
820
|
-
const operationId =
|
|
1140
|
+
const operationId = operation.operationId;
|
|
821
1141
|
// In Browser environment, this api is by default not support api without operationId
|
|
822
1142
|
if (!operationId) {
|
|
823
1143
|
continue;
|
|
824
1144
|
}
|
|
825
|
-
const
|
|
1145
|
+
const command = Utils.parseApiInfo(operation, this.options);
|
|
826
1146
|
const apiInfo = {
|
|
827
1147
|
method: method,
|
|
828
1148
|
path: path,
|
|
@@ -831,9 +1151,6 @@ class SpecParser {
|
|
|
831
1151
|
parameters: command.parameters,
|
|
832
1152
|
description: command.description,
|
|
833
1153
|
};
|
|
834
|
-
if (warning) {
|
|
835
|
-
apiInfo.warning = warning;
|
|
836
|
-
}
|
|
837
1154
|
apiInfos.push(apiInfo);
|
|
838
1155
|
}
|
|
839
1156
|
return apiInfos;
|
|
@@ -854,12 +1171,36 @@ class SpecParser {
|
|
|
854
1171
|
throw new Error("Method not implemented.");
|
|
855
1172
|
});
|
|
856
1173
|
}
|
|
1174
|
+
/**
|
|
1175
|
+
* Generate specs according to the filters.
|
|
1176
|
+
* @param filter An array of strings that represent the filters to apply when generating the artifacts. If filter is empty, it would process nothing.
|
|
1177
|
+
*/
|
|
1178
|
+
// eslint-disable-next-line @typescript-eslint/require-await
|
|
1179
|
+
getFilteredSpecs(filter, signal) {
|
|
1180
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1181
|
+
throw new Error("Method not implemented.");
|
|
1182
|
+
});
|
|
1183
|
+
}
|
|
1184
|
+
/**
|
|
1185
|
+
* Generates and update artifacts from the OpenAPI specification file. Generate Adaptive Cards, update Teams app manifest, and generate a new OpenAPI specification file.
|
|
1186
|
+
* @param manifestPath A file path of the Teams app manifest file to update.
|
|
1187
|
+
* @param filter An array of strings that represent the filters to apply when generating the artifacts. If filter is empty, it would process nothing.
|
|
1188
|
+
* @param outputSpecPath File path of the new OpenAPI specification file to generate. If not specified or empty, no spec file will be generated.
|
|
1189
|
+
* @param pluginFilePath File path of the api plugin file to generate.
|
|
1190
|
+
*/
|
|
1191
|
+
// eslint-disable-next-line @typescript-eslint/require-await
|
|
1192
|
+
generateForCopilot(manifestPath, filter, outputSpecPath, pluginFilePath, signal) {
|
|
1193
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1194
|
+
throw new Error("Method not implemented.");
|
|
1195
|
+
});
|
|
1196
|
+
}
|
|
857
1197
|
/**
|
|
858
1198
|
* Generates and update artifacts from the OpenAPI specification file. Generate Adaptive Cards, update Teams app manifest, and generate a new OpenAPI specification file.
|
|
859
1199
|
* @param manifestPath A file path of the Teams app manifest file to update.
|
|
860
1200
|
* @param filter An array of strings that represent the filters to apply when generating the artifacts. If filter is empty, it would process nothing.
|
|
861
1201
|
* @param outputSpecPath File path of the new OpenAPI specification file to generate. If not specified or empty, no spec file will be generated.
|
|
862
1202
|
* @param adaptiveCardFolder Folder path where the Adaptive Card files will be generated. If not specified or empty, Adaptive Card files will not be generated.
|
|
1203
|
+
* @param isMe Boolean that indicates whether the project is an Messaging Extension. For Messaging Extension, composeExtensions will be added in Teams app manifest.
|
|
863
1204
|
*/
|
|
864
1205
|
// eslint-disable-next-line @typescript-eslint/require-await
|
|
865
1206
|
generate(manifestPath, filter, outputSpecPath, adaptiveCardFolder, signal) {
|
|
@@ -879,15 +1220,182 @@ class SpecParser {
|
|
|
879
1220
|
}
|
|
880
1221
|
});
|
|
881
1222
|
}
|
|
882
|
-
|
|
1223
|
+
getAPIs(spec) {
|
|
883
1224
|
if (this.apiMap !== undefined) {
|
|
884
1225
|
return this.apiMap;
|
|
885
1226
|
}
|
|
886
|
-
const
|
|
887
|
-
|
|
888
|
-
|
|
1227
|
+
const validator = this.getValidator(spec);
|
|
1228
|
+
const apiMap = validator.listAPIs();
|
|
1229
|
+
this.apiMap = apiMap;
|
|
1230
|
+
return apiMap;
|
|
1231
|
+
}
|
|
1232
|
+
getValidator(spec) {
|
|
1233
|
+
if (this.validator) {
|
|
1234
|
+
return this.validator;
|
|
1235
|
+
}
|
|
1236
|
+
const validator = ValidatorFactory.create(spec, this.options);
|
|
1237
|
+
this.validator = validator;
|
|
1238
|
+
return validator;
|
|
1239
|
+
}
|
|
1240
|
+
}
|
|
1241
|
+
|
|
1242
|
+
// Copyright (c) Microsoft Corporation.
|
|
1243
|
+
class AdaptiveCardGenerator {
|
|
1244
|
+
static generateAdaptiveCard(operationItem) {
|
|
1245
|
+
try {
|
|
1246
|
+
const { json } = Utils.getResponseJson(operationItem);
|
|
1247
|
+
let cardBody = [];
|
|
1248
|
+
let schema = json.schema;
|
|
1249
|
+
let jsonPath = "$";
|
|
1250
|
+
if (schema && Object.keys(schema).length > 0) {
|
|
1251
|
+
jsonPath = AdaptiveCardGenerator.getResponseJsonPathFromSchema(schema);
|
|
1252
|
+
if (jsonPath !== "$") {
|
|
1253
|
+
schema = schema.properties[jsonPath];
|
|
1254
|
+
}
|
|
1255
|
+
cardBody = AdaptiveCardGenerator.generateCardFromResponse(schema, "");
|
|
1256
|
+
}
|
|
1257
|
+
// if no schema, try to use example value
|
|
1258
|
+
if (cardBody.length === 0 && (json.examples || json.example)) {
|
|
1259
|
+
cardBody = [
|
|
1260
|
+
{
|
|
1261
|
+
type: ConstantString.TextBlockType,
|
|
1262
|
+
text: "${jsonStringify($root)}",
|
|
1263
|
+
wrap: true,
|
|
1264
|
+
},
|
|
1265
|
+
];
|
|
1266
|
+
}
|
|
1267
|
+
// if no example value, use default success response
|
|
1268
|
+
if (cardBody.length === 0) {
|
|
1269
|
+
cardBody = [
|
|
1270
|
+
{
|
|
1271
|
+
type: ConstantString.TextBlockType,
|
|
1272
|
+
text: "success",
|
|
1273
|
+
wrap: true,
|
|
1274
|
+
},
|
|
1275
|
+
];
|
|
1276
|
+
}
|
|
1277
|
+
const fullCard = {
|
|
1278
|
+
type: ConstantString.AdaptiveCardType,
|
|
1279
|
+
$schema: ConstantString.AdaptiveCardSchema,
|
|
1280
|
+
version: ConstantString.AdaptiveCardVersion,
|
|
1281
|
+
body: cardBody,
|
|
1282
|
+
};
|
|
1283
|
+
return [fullCard, jsonPath];
|
|
1284
|
+
}
|
|
1285
|
+
catch (err) {
|
|
1286
|
+
throw new SpecParserError(err.toString(), ErrorType.GenerateAdaptiveCardFailed);
|
|
1287
|
+
}
|
|
1288
|
+
}
|
|
1289
|
+
static generateCardFromResponse(schema, name, parentArrayName = "") {
|
|
1290
|
+
if (schema.type === "array") {
|
|
1291
|
+
// schema.items can be arbitrary object: schema { type: array, items: {} }
|
|
1292
|
+
if (Object.keys(schema.items).length === 0) {
|
|
1293
|
+
return [
|
|
1294
|
+
{
|
|
1295
|
+
type: ConstantString.TextBlockType,
|
|
1296
|
+
text: name ? `${name}: \${jsonStringify(${name})}` : "result: ${jsonStringify($root)}",
|
|
1297
|
+
wrap: true,
|
|
1298
|
+
},
|
|
1299
|
+
];
|
|
1300
|
+
}
|
|
1301
|
+
const obj = AdaptiveCardGenerator.generateCardFromResponse(schema.items, "", name);
|
|
1302
|
+
const template = {
|
|
1303
|
+
type: ConstantString.ContainerType,
|
|
1304
|
+
$data: name ? `\${${name}}` : "${$root}",
|
|
1305
|
+
items: Array(),
|
|
1306
|
+
};
|
|
1307
|
+
template.items.push(...obj);
|
|
1308
|
+
return [template];
|
|
1309
|
+
}
|
|
1310
|
+
// some schema may not contain type but contain properties
|
|
1311
|
+
if (schema.type === "object" || (!schema.type && schema.properties)) {
|
|
1312
|
+
const { properties } = schema;
|
|
1313
|
+
const result = [];
|
|
1314
|
+
for (const property in properties) {
|
|
1315
|
+
const obj = AdaptiveCardGenerator.generateCardFromResponse(properties[property], name ? `${name}.${property}` : property, parentArrayName);
|
|
1316
|
+
result.push(...obj);
|
|
1317
|
+
}
|
|
1318
|
+
if (schema.additionalProperties) {
|
|
1319
|
+
// TODO: better ways to handler warnings.
|
|
1320
|
+
console.warn(ConstantString.AdditionalPropertiesNotSupported);
|
|
1321
|
+
}
|
|
1322
|
+
return result;
|
|
1323
|
+
}
|
|
1324
|
+
if (schema.type === "string" ||
|
|
1325
|
+
schema.type === "integer" ||
|
|
1326
|
+
schema.type === "boolean" ||
|
|
1327
|
+
schema.type === "number") {
|
|
1328
|
+
if (!AdaptiveCardGenerator.isImageUrlProperty(schema, name, parentArrayName)) {
|
|
1329
|
+
// string in root: "ddd"
|
|
1330
|
+
let text = "result: ${$root}";
|
|
1331
|
+
if (name) {
|
|
1332
|
+
// object { id: "1" }
|
|
1333
|
+
text = `${name}: \${if(${name}, ${name}, 'N/A')}`;
|
|
1334
|
+
if (parentArrayName) {
|
|
1335
|
+
// object types inside array: { tags: ["id": 1, "name": "name"] }
|
|
1336
|
+
text = `${parentArrayName}.${text}`;
|
|
1337
|
+
}
|
|
1338
|
+
}
|
|
1339
|
+
else if (parentArrayName) {
|
|
1340
|
+
// string array: photoUrls: ["1", "2"]
|
|
1341
|
+
text = `${parentArrayName}: ` + "${$data}";
|
|
1342
|
+
}
|
|
1343
|
+
return [
|
|
1344
|
+
{
|
|
1345
|
+
type: ConstantString.TextBlockType,
|
|
1346
|
+
text,
|
|
1347
|
+
wrap: true,
|
|
1348
|
+
},
|
|
1349
|
+
];
|
|
1350
|
+
}
|
|
1351
|
+
else {
|
|
1352
|
+
if (name) {
|
|
1353
|
+
return [
|
|
1354
|
+
{
|
|
1355
|
+
type: "Image",
|
|
1356
|
+
url: `\${${name}}`,
|
|
1357
|
+
$when: `\${${name} != null}`,
|
|
1358
|
+
},
|
|
1359
|
+
];
|
|
1360
|
+
}
|
|
1361
|
+
else {
|
|
1362
|
+
return [
|
|
1363
|
+
{
|
|
1364
|
+
type: "Image",
|
|
1365
|
+
url: "${$data}",
|
|
1366
|
+
$when: "${$data != null}",
|
|
1367
|
+
},
|
|
1368
|
+
];
|
|
1369
|
+
}
|
|
1370
|
+
}
|
|
1371
|
+
}
|
|
1372
|
+
if (schema.oneOf || schema.anyOf || schema.not || schema.allOf) {
|
|
1373
|
+
throw new Error(Utils.format(ConstantString.SchemaNotSupported, JSON.stringify(schema)));
|
|
1374
|
+
}
|
|
1375
|
+
throw new Error(Utils.format(ConstantString.UnknownSchema, JSON.stringify(schema)));
|
|
1376
|
+
}
|
|
1377
|
+
// Find the first array property in the response schema object with the well-known name
|
|
1378
|
+
static getResponseJsonPathFromSchema(schema) {
|
|
1379
|
+
if (schema.type === "object" || (!schema.type && schema.properties)) {
|
|
1380
|
+
const { properties } = schema;
|
|
1381
|
+
for (const property in properties) {
|
|
1382
|
+
const schema = properties[property];
|
|
1383
|
+
if (schema.type === "array" &&
|
|
1384
|
+
Utils.isWellKnownName(property, ConstantString.WellknownResultNames)) {
|
|
1385
|
+
return property;
|
|
1386
|
+
}
|
|
1387
|
+
}
|
|
1388
|
+
}
|
|
1389
|
+
return "$";
|
|
1390
|
+
}
|
|
1391
|
+
static isImageUrlProperty(schema, name, parentArrayName) {
|
|
1392
|
+
const propertyName = name ? name : parentArrayName;
|
|
1393
|
+
return (!!propertyName &&
|
|
1394
|
+
schema.type === "string" &&
|
|
1395
|
+
Utils.isWellKnownName(propertyName, ConstantString.WellknownImageName) &&
|
|
1396
|
+
(propertyName.toLocaleLowerCase().indexOf("url") >= 0 || schema.format === "uri"));
|
|
889
1397
|
}
|
|
890
1398
|
}
|
|
891
1399
|
|
|
892
|
-
export { ConstantString, ErrorType, SpecParser, SpecParserError, Utils, ValidationStatus, WarningType };
|
|
1400
|
+
export { AdaptiveCardGenerator, ConstantString, ErrorType, SpecParser, SpecParserError, Utils, ValidationStatus, WarningType };
|
|
893
1401
|
//# sourceMappingURL=index.esm5.js.map
|