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