@microsoft/m365-spec-parser 0.0.2-alpha.2 → 0.1.1-alpha.039039fab.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 +835 -306
- package/dist/index.esm2017.js.map +1 -1
- package/dist/index.esm2017.mjs +1185 -508
- package/dist/index.esm2017.mjs.map +1 -1
- package/dist/index.esm5.js +839 -306
- package/dist/index.esm5.js.map +1 -1
- package/dist/index.node.cjs.js +1203 -515
- package/dist/index.node.cjs.js.map +1 -1
- package/dist/src/adaptiveCardWrapper.d.ts +2 -0
- package/dist/src/constants.d.ts +11 -3
- package/dist/src/index.browser.d.ts +2 -1
- package/dist/src/index.d.ts +2 -1
- package/dist/src/interfaces.d.ts +111 -18
- package/dist/src/manifestUpdater.d.ts +11 -4
- package/dist/src/specFilter.d.ts +2 -1
- package/dist/src/specParser.browser.d.ts +18 -3
- package/dist/src/specParser.d.ts +19 -4
- package/dist/src/utils.d.ts +19 -34
- package/package.json +61 -18
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 {
|
|
@@ -94,7 +117,7 @@ ConstantString.RemoteRefNotSupported = "Remote reference is not supported: %s.";
|
|
|
94
117
|
ConstantString.MissingOperationId = "Missing operationIds: %s.";
|
|
95
118
|
ConstantString.NoSupportedApi = "No supported API is found in the OpenAPI description document: only GET and POST methods are supported, additionally, there can be at most one required parameter, and no auth is allowed.";
|
|
96
119
|
ConstantString.AdditionalPropertiesNotSupported = "'additionalProperties' is not supported, and will be ignored.";
|
|
97
|
-
ConstantString.SchemaNotSupported = "'oneOf', 'anyOf', and 'not' schema are not supported: %s.";
|
|
120
|
+
ConstantString.SchemaNotSupported = "'oneOf', 'allOf', 'anyOf', and 'not' schema are not supported: %s.";
|
|
98
121
|
ConstantString.UnknownSchema = "Unknown schema: %s.";
|
|
99
122
|
ConstantString.UrlProtocolNotSupported = "Server url is not correct: protocol %s is not supported, you should use https protocol instead.";
|
|
100
123
|
ConstantString.RelativeServerUrlNotSupported = "Server url is not correct: relative server url is not supported.";
|
|
@@ -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";
|
|
@@ -112,8 +137,14 @@ ConstantString.AdaptiveCardVersion = "1.5";
|
|
|
112
137
|
ConstantString.AdaptiveCardSchema = "http://adaptivecards.io/schemas/adaptive-card.json";
|
|
113
138
|
ConstantString.AdaptiveCardType = "AdaptiveCard";
|
|
114
139
|
ConstantString.TextBlockType = "TextBlock";
|
|
140
|
+
ConstantString.ImageType = "Image";
|
|
115
141
|
ConstantString.ContainerType = "Container";
|
|
116
|
-
ConstantString.RegistrationIdPostfix =
|
|
142
|
+
ConstantString.RegistrationIdPostfix = {
|
|
143
|
+
apiKey: "REGISTRATION_ID",
|
|
144
|
+
oauth2: "CONFIGURATION_ID",
|
|
145
|
+
http: "REGISTRATION_ID",
|
|
146
|
+
openIdConnect: "REGISTRATION_ID",
|
|
147
|
+
};
|
|
117
148
|
ConstantString.ResponseCodeFor20X = [
|
|
118
149
|
"200",
|
|
119
150
|
"201",
|
|
@@ -172,219 +203,52 @@ ConstantString.ShortDescriptionMaxLens = 80;
|
|
|
172
203
|
ConstantString.FullDescriptionMaxLens = 4000;
|
|
173
204
|
ConstantString.CommandDescriptionMaxLens = 128;
|
|
174
205
|
ConstantString.ParameterDescriptionMaxLens = 128;
|
|
206
|
+
ConstantString.ConversationStarterMaxLens = 50;
|
|
175
207
|
ConstantString.CommandTitleMaxLens = 32;
|
|
176
|
-
ConstantString.ParameterTitleMaxLens = 32;
|
|
208
|
+
ConstantString.ParameterTitleMaxLens = 32;
|
|
209
|
+
ConstantString.SMERequiredParamsMaxNum = 5;
|
|
210
|
+
ConstantString.DefaultPluginId = "plugin_1";
|
|
177
211
|
|
|
178
212
|
// Copyright (c) Microsoft Corporation.
|
|
179
213
|
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 {
|
|
214
|
+
static hasNestedObjectInSchema(schema) {
|
|
215
|
+
if (schema.type === "object") {
|
|
216
|
+
for (const property in schema.properties) {
|
|
217
|
+
const nestedSchema = schema.properties[property];
|
|
218
|
+
if (nestedSchema.type === "object") {
|
|
332
219
|
return true;
|
|
333
220
|
}
|
|
334
221
|
}
|
|
335
222
|
}
|
|
336
223
|
return false;
|
|
337
224
|
}
|
|
338
|
-
static
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
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;
|
|
225
|
+
static containMultipleMediaTypes(bodyObject) {
|
|
226
|
+
return Object.keys((bodyObject === null || bodyObject === void 0 ? void 0 : bodyObject.content) || {}).length > 1;
|
|
227
|
+
}
|
|
228
|
+
static isBearerTokenAuth(authScheme) {
|
|
229
|
+
return authScheme.type === "http" && authScheme.scheme === "bearer";
|
|
367
230
|
}
|
|
368
|
-
static isAPIKeyAuth(
|
|
369
|
-
return
|
|
231
|
+
static isAPIKeyAuth(authScheme) {
|
|
232
|
+
return authScheme.type === "apiKey";
|
|
370
233
|
}
|
|
371
|
-
static
|
|
372
|
-
return (
|
|
373
|
-
|
|
374
|
-
|
|
234
|
+
static isOAuthWithAuthCodeFlow(authScheme) {
|
|
235
|
+
return !!(authScheme.type === "oauth2" &&
|
|
236
|
+
authScheme.flows &&
|
|
237
|
+
authScheme.flows.authorizationCode);
|
|
375
238
|
}
|
|
376
239
|
static getAuthArray(securities, spec) {
|
|
377
240
|
var _a;
|
|
378
241
|
const result = [];
|
|
379
242
|
const securitySchemas = (_a = spec.components) === null || _a === void 0 ? void 0 : _a.securitySchemes;
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
243
|
+
const securitiesArr = securities !== null && securities !== void 0 ? securities : spec.security;
|
|
244
|
+
if (securitiesArr && securitySchemas) {
|
|
245
|
+
for (let i = 0; i < securitiesArr.length; i++) {
|
|
246
|
+
const security = securitiesArr[i];
|
|
383
247
|
const authArray = [];
|
|
384
248
|
for (const name in security) {
|
|
385
249
|
const auth = securitySchemas[name];
|
|
386
250
|
authArray.push({
|
|
387
|
-
|
|
251
|
+
authScheme: auth,
|
|
388
252
|
name: name,
|
|
389
253
|
});
|
|
390
254
|
}
|
|
@@ -396,24 +260,47 @@ class Utils {
|
|
|
396
260
|
result.sort((a, b) => a[0].name.localeCompare(b[0].name));
|
|
397
261
|
return result;
|
|
398
262
|
}
|
|
263
|
+
static getAuthInfo(spec) {
|
|
264
|
+
let authInfo = undefined;
|
|
265
|
+
for (const url in spec.paths) {
|
|
266
|
+
for (const method in spec.paths[url]) {
|
|
267
|
+
const operation = spec.paths[url][method];
|
|
268
|
+
const authArray = Utils.getAuthArray(operation.security, spec);
|
|
269
|
+
if (authArray && authArray.length > 0) {
|
|
270
|
+
const currentAuth = authArray[0][0];
|
|
271
|
+
if (!authInfo) {
|
|
272
|
+
authInfo = authArray[0][0];
|
|
273
|
+
}
|
|
274
|
+
else if (authInfo.name !== currentAuth.name) {
|
|
275
|
+
throw new SpecParserError(ConstantString.MultipleAuthNotSupported, ErrorType.MultipleAuthNotSupported);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
return authInfo;
|
|
281
|
+
}
|
|
399
282
|
static updateFirstLetter(str) {
|
|
400
283
|
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
401
284
|
}
|
|
402
285
|
static getResponseJson(operationObject) {
|
|
403
286
|
var _a, _b;
|
|
404
287
|
let json = {};
|
|
288
|
+
let multipleMediaType = false;
|
|
405
289
|
for (const code of ConstantString.ResponseCodeFor20X) {
|
|
406
290
|
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
291
|
if ((_b = responseObject === null || responseObject === void 0 ? void 0 : responseObject.content) === null || _b === void 0 ? void 0 : _b["application/json"]) {
|
|
292
|
+
multipleMediaType = false;
|
|
412
293
|
json = responseObject.content["application/json"];
|
|
413
|
-
|
|
294
|
+
if (Utils.containMultipleMediaTypes(responseObject)) {
|
|
295
|
+
multipleMediaType = true;
|
|
296
|
+
json = {};
|
|
297
|
+
}
|
|
298
|
+
else {
|
|
299
|
+
break;
|
|
300
|
+
}
|
|
414
301
|
}
|
|
415
302
|
}
|
|
416
|
-
return json;
|
|
303
|
+
return { json, multipleMediaType };
|
|
417
304
|
}
|
|
418
305
|
static convertPathToCamelCase(path) {
|
|
419
306
|
const pathSegments = path.split(/[./{]/);
|
|
@@ -433,10 +320,10 @@ class Utils {
|
|
|
433
320
|
return undefined;
|
|
434
321
|
}
|
|
435
322
|
}
|
|
436
|
-
static
|
|
323
|
+
static resolveEnv(str) {
|
|
437
324
|
const placeHolderReg = /\${{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*}}/g;
|
|
438
|
-
let matches = placeHolderReg.exec(
|
|
439
|
-
let
|
|
325
|
+
let matches = placeHolderReg.exec(str);
|
|
326
|
+
let newStr = str;
|
|
440
327
|
while (matches != null) {
|
|
441
328
|
const envVar = matches[1];
|
|
442
329
|
const envVal = process.env[envVar];
|
|
@@ -444,17 +331,17 @@ class Utils {
|
|
|
444
331
|
throw new Error(Utils.format(ConstantString.ResolveServerUrlFailed, envVar));
|
|
445
332
|
}
|
|
446
333
|
else {
|
|
447
|
-
|
|
334
|
+
newStr = newStr.replace(matches[0], envVal);
|
|
448
335
|
}
|
|
449
|
-
matches = placeHolderReg.exec(
|
|
336
|
+
matches = placeHolderReg.exec(str);
|
|
450
337
|
}
|
|
451
|
-
return
|
|
338
|
+
return newStr;
|
|
452
339
|
}
|
|
453
340
|
static checkServerUrl(servers) {
|
|
454
341
|
const errors = [];
|
|
455
342
|
let serverUrl;
|
|
456
343
|
try {
|
|
457
|
-
serverUrl = Utils.
|
|
344
|
+
serverUrl = Utils.resolveEnv(servers[0].url);
|
|
458
345
|
}
|
|
459
346
|
catch (err) {
|
|
460
347
|
errors.push({
|
|
@@ -484,7 +371,8 @@ class Utils {
|
|
|
484
371
|
}
|
|
485
372
|
return errors;
|
|
486
373
|
}
|
|
487
|
-
static validateServer(spec,
|
|
374
|
+
static validateServer(spec, options) {
|
|
375
|
+
var _a;
|
|
488
376
|
const errors = [];
|
|
489
377
|
let hasTopLevelServers = false;
|
|
490
378
|
let hasPathLevelServers = false;
|
|
@@ -505,7 +393,7 @@ class Utils {
|
|
|
505
393
|
}
|
|
506
394
|
for (const method in methods) {
|
|
507
395
|
const operationObject = methods[method];
|
|
508
|
-
if (
|
|
396
|
+
if (((_a = options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && operationObject) {
|
|
509
397
|
if ((operationObject === null || operationObject === void 0 ? void 0 : operationObject.servers) && operationObject.servers.length >= 1) {
|
|
510
398
|
hasOperationLevelServers = true;
|
|
511
399
|
const serverErrors = Utils.checkServerUrl(operationObject.servers);
|
|
@@ -548,6 +436,7 @@ class Utils {
|
|
|
548
436
|
Utils.updateParameterWithInputType(schema, parameter);
|
|
549
437
|
}
|
|
550
438
|
if (isRequired && schema.default === undefined) {
|
|
439
|
+
parameter.isRequired = true;
|
|
551
440
|
requiredParams.push(parameter);
|
|
552
441
|
}
|
|
553
442
|
else {
|
|
@@ -592,7 +481,7 @@ class Utils {
|
|
|
592
481
|
param.value = schema.default;
|
|
593
482
|
}
|
|
594
483
|
}
|
|
595
|
-
static parseApiInfo(operationItem,
|
|
484
|
+
static parseApiInfo(operationItem, options) {
|
|
596
485
|
var _a, _b;
|
|
597
486
|
const requiredParams = [];
|
|
598
487
|
const optionalParams = [];
|
|
@@ -606,11 +495,12 @@ class Utils {
|
|
|
606
495
|
description: ((_a = param.description) !== null && _a !== void 0 ? _a : "").slice(0, ConstantString.ParameterDescriptionMaxLens),
|
|
607
496
|
};
|
|
608
497
|
const schema = param.schema;
|
|
609
|
-
if (allowMultipleParameters && schema) {
|
|
498
|
+
if (options.allowMultipleParameters && schema) {
|
|
610
499
|
Utils.updateParameterWithInputType(schema, parameter);
|
|
611
500
|
}
|
|
612
501
|
if (param.in !== "header" && param.in !== "cookie") {
|
|
613
502
|
if (param.required && (schema === null || schema === void 0 ? void 0 : schema.default) === undefined) {
|
|
503
|
+
parameter.isRequired = true;
|
|
614
504
|
requiredParams.push(parameter);
|
|
615
505
|
}
|
|
616
506
|
else {
|
|
@@ -624,19 +514,13 @@ class Utils {
|
|
|
624
514
|
const requestJson = requestBody.content["application/json"];
|
|
625
515
|
if (Object.keys(requestJson).length !== 0) {
|
|
626
516
|
const schema = requestJson.schema;
|
|
627
|
-
const [requiredP, optionalP] = Utils.generateParametersFromSchema(schema, "requestBody", allowMultipleParameters, requestBody.required);
|
|
517
|
+
const [requiredP, optionalP] = Utils.generateParametersFromSchema(schema, "requestBody", !!options.allowMultipleParameters, requestBody.required);
|
|
628
518
|
requiredParams.push(...requiredP);
|
|
629
519
|
optionalParams.push(...optionalP);
|
|
630
520
|
}
|
|
631
521
|
}
|
|
632
522
|
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
|
-
}
|
|
523
|
+
const parameters = [...requiredParams, ...optionalParams];
|
|
640
524
|
const command = {
|
|
641
525
|
context: ["compose"],
|
|
642
526
|
type: "query",
|
|
@@ -645,105 +529,534 @@ class Utils {
|
|
|
645
529
|
parameters: parameters,
|
|
646
530
|
description: ((_b = operationItem.description) !== null && _b !== void 0 ? _b : "").slice(0, ConstantString.CommandDescriptionMaxLens),
|
|
647
531
|
};
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
532
|
+
return command;
|
|
533
|
+
}
|
|
534
|
+
static format(str, ...args) {
|
|
535
|
+
let index = 0;
|
|
536
|
+
return str.replace(/%s/g, () => {
|
|
537
|
+
const arg = args[index++];
|
|
538
|
+
return arg !== undefined ? arg : "";
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
static getSafeRegistrationIdEnvName(authName) {
|
|
542
|
+
if (!authName) {
|
|
543
|
+
return "";
|
|
544
|
+
}
|
|
545
|
+
let safeRegistrationIdEnvName = authName.toUpperCase().replace(/[^A-Z0-9_]/g, "_");
|
|
546
|
+
if (!safeRegistrationIdEnvName.match(/^[A-Z]/)) {
|
|
547
|
+
safeRegistrationIdEnvName = "PREFIX_" + safeRegistrationIdEnvName;
|
|
655
548
|
}
|
|
656
|
-
return
|
|
549
|
+
return safeRegistrationIdEnvName;
|
|
657
550
|
}
|
|
658
|
-
static
|
|
659
|
-
const
|
|
551
|
+
static getServerObject(spec, method, path) {
|
|
552
|
+
const pathObj = spec.paths[path];
|
|
553
|
+
const operationObject = pathObj[method];
|
|
554
|
+
const rootServer = spec.servers && spec.servers[0];
|
|
555
|
+
const methodServer = spec.paths[path].servers && spec.paths[path].servers[0];
|
|
556
|
+
const operationServer = operationObject.servers && operationObject.servers[0];
|
|
557
|
+
const serverUrl = operationServer || methodServer || rootServer;
|
|
558
|
+
return serverUrl;
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
// Copyright (c) Microsoft Corporation.
|
|
563
|
+
class Validator {
|
|
564
|
+
listAPIs() {
|
|
565
|
+
var _a;
|
|
566
|
+
if (this.apiMap) {
|
|
567
|
+
return this.apiMap;
|
|
568
|
+
}
|
|
569
|
+
const paths = this.spec.paths;
|
|
660
570
|
const result = {};
|
|
661
571
|
for (const path in paths) {
|
|
662
572
|
const methods = paths[path];
|
|
663
573
|
for (const method in methods) {
|
|
664
|
-
|
|
665
|
-
if (
|
|
666
|
-
const
|
|
667
|
-
result[`${method.toUpperCase()} ${path}`] =
|
|
574
|
+
const operationObject = methods[method];
|
|
575
|
+
if (((_a = this.options.allowMethods) === null || _a === void 0 ? void 0 : _a.includes(method)) && operationObject) {
|
|
576
|
+
const validateResult = this.validateAPI(method, path);
|
|
577
|
+
result[`${method.toUpperCase()} ${path}`] = {
|
|
578
|
+
operation: operationObject,
|
|
579
|
+
isValid: validateResult.isValid,
|
|
580
|
+
reason: validateResult.reason,
|
|
581
|
+
};
|
|
668
582
|
}
|
|
669
583
|
}
|
|
670
584
|
}
|
|
585
|
+
this.apiMap = result;
|
|
671
586
|
return result;
|
|
672
587
|
}
|
|
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,
|
|
588
|
+
validateSpecVersion() {
|
|
589
|
+
const result = { errors: [], warnings: [] };
|
|
590
|
+
if (this.spec.openapi >= "3.1.0") {
|
|
591
|
+
result.errors.push({
|
|
592
|
+
type: ErrorType.SpecVersionNotSupported,
|
|
593
|
+
content: Utils.format(ConstantString.SpecVersionNotSupported, this.spec.openapi),
|
|
594
|
+
data: this.spec.openapi,
|
|
693
595
|
});
|
|
694
596
|
}
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
597
|
+
return result;
|
|
598
|
+
}
|
|
599
|
+
validateSpecServer() {
|
|
600
|
+
const result = { errors: [], warnings: [] };
|
|
601
|
+
const serverErrors = Utils.validateServer(this.spec, this.options);
|
|
602
|
+
result.errors.push(...serverErrors);
|
|
603
|
+
return result;
|
|
604
|
+
}
|
|
605
|
+
validateSpecNoSupportAPI() {
|
|
606
|
+
const result = { errors: [], warnings: [] };
|
|
607
|
+
const apiMap = this.listAPIs();
|
|
608
|
+
const validAPIs = Object.entries(apiMap).filter(([, value]) => value.isValid);
|
|
609
|
+
if (validAPIs.length === 0) {
|
|
610
|
+
const data = [];
|
|
611
|
+
for (const key in apiMap) {
|
|
612
|
+
const { reason } = apiMap[key];
|
|
613
|
+
const apiInvalidReason = { api: key, reason: reason };
|
|
614
|
+
data.push(apiInvalidReason);
|
|
615
|
+
}
|
|
616
|
+
result.errors.push({
|
|
699
617
|
type: ErrorType.NoSupportedApi,
|
|
700
618
|
content: ConstantString.NoSupportedApi,
|
|
619
|
+
data,
|
|
701
620
|
});
|
|
702
621
|
}
|
|
622
|
+
return result;
|
|
623
|
+
}
|
|
624
|
+
validateSpecOperationId() {
|
|
625
|
+
const result = { errors: [], warnings: [] };
|
|
626
|
+
const apiMap = this.listAPIs();
|
|
703
627
|
// OperationId missing
|
|
704
628
|
const apisMissingOperationId = [];
|
|
705
629
|
for (const key in apiMap) {
|
|
706
|
-
const
|
|
707
|
-
if (!
|
|
630
|
+
const { operation } = apiMap[key];
|
|
631
|
+
if (!operation.operationId) {
|
|
708
632
|
apisMissingOperationId.push(key);
|
|
709
633
|
}
|
|
710
634
|
}
|
|
711
635
|
if (apisMissingOperationId.length > 0) {
|
|
712
|
-
warnings.push({
|
|
636
|
+
result.warnings.push({
|
|
713
637
|
type: WarningType.OperationIdMissing,
|
|
714
638
|
content: Utils.format(ConstantString.MissingOperationId, apisMissingOperationId.join(", ")),
|
|
715
639
|
data: apisMissingOperationId,
|
|
716
640
|
});
|
|
717
641
|
}
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
642
|
+
return result;
|
|
643
|
+
}
|
|
644
|
+
validateMethodAndPath(method, path) {
|
|
645
|
+
const result = { isValid: true, reason: [] };
|
|
646
|
+
if (this.options.allowMethods && !this.options.allowMethods.includes(method)) {
|
|
647
|
+
result.isValid = false;
|
|
648
|
+
result.reason.push(ErrorType.MethodNotAllowed);
|
|
649
|
+
return result;
|
|
650
|
+
}
|
|
651
|
+
const pathObj = this.spec.paths[path];
|
|
652
|
+
if (!pathObj || !pathObj[method]) {
|
|
653
|
+
result.isValid = false;
|
|
654
|
+
result.reason.push(ErrorType.UrlPathNotExist);
|
|
655
|
+
return result;
|
|
656
|
+
}
|
|
657
|
+
return result;
|
|
658
|
+
}
|
|
659
|
+
validateResponse(method, path) {
|
|
660
|
+
const result = { isValid: true, reason: [] };
|
|
661
|
+
const operationObject = this.spec.paths[path][method];
|
|
662
|
+
const { json, multipleMediaType } = Utils.getResponseJson(operationObject);
|
|
663
|
+
if (this.options.projectType === ProjectType.SME) {
|
|
664
|
+
// only support response body only contains “application/json” content type
|
|
665
|
+
if (multipleMediaType) {
|
|
666
|
+
result.reason.push(ErrorType.ResponseContainMultipleMediaTypes);
|
|
667
|
+
}
|
|
668
|
+
else if (Object.keys(json).length === 0) {
|
|
669
|
+
// response body should not be empty
|
|
670
|
+
result.reason.push(ErrorType.ResponseJsonIsEmpty);
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
return result;
|
|
674
|
+
}
|
|
675
|
+
validateServer(method, path) {
|
|
676
|
+
const result = { isValid: true, reason: [] };
|
|
677
|
+
const serverObj = Utils.getServerObject(this.spec, method, path);
|
|
678
|
+
if (!serverObj) {
|
|
679
|
+
// should contain server URL
|
|
680
|
+
result.reason.push(ErrorType.NoServerInformation);
|
|
681
|
+
}
|
|
682
|
+
else {
|
|
683
|
+
// server url should be absolute url with https protocol
|
|
684
|
+
const serverValidateResult = Utils.checkServerUrl([serverObj]);
|
|
685
|
+
result.reason.push(...serverValidateResult.map((item) => item.type));
|
|
686
|
+
}
|
|
687
|
+
return result;
|
|
688
|
+
}
|
|
689
|
+
validateAuth(method, path) {
|
|
690
|
+
const pathObj = this.spec.paths[path];
|
|
691
|
+
const operationObject = pathObj[method];
|
|
692
|
+
const securities = operationObject.security;
|
|
693
|
+
const authSchemeArray = Utils.getAuthArray(securities, this.spec);
|
|
694
|
+
if (authSchemeArray.length === 0) {
|
|
695
|
+
return { isValid: true, reason: [] };
|
|
721
696
|
}
|
|
722
|
-
|
|
723
|
-
|
|
697
|
+
if (this.options.allowAPIKeyAuth ||
|
|
698
|
+
this.options.allowOauth2 ||
|
|
699
|
+
this.options.allowBearerTokenAuth) {
|
|
700
|
+
// Currently we don't support multiple auth in one operation
|
|
701
|
+
if (authSchemeArray.length > 0 && authSchemeArray.every((auths) => auths.length > 1)) {
|
|
702
|
+
return {
|
|
703
|
+
isValid: false,
|
|
704
|
+
reason: [ErrorType.MultipleAuthNotSupported],
|
|
705
|
+
};
|
|
706
|
+
}
|
|
707
|
+
for (const auths of authSchemeArray) {
|
|
708
|
+
if (auths.length === 1) {
|
|
709
|
+
if ((this.options.allowAPIKeyAuth && Utils.isAPIKeyAuth(auths[0].authScheme)) ||
|
|
710
|
+
(this.options.allowOauth2 && Utils.isOAuthWithAuthCodeFlow(auths[0].authScheme)) ||
|
|
711
|
+
(this.options.allowBearerTokenAuth && Utils.isBearerTokenAuth(auths[0].authScheme))) {
|
|
712
|
+
return { isValid: true, reason: [] };
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
}
|
|
724
716
|
}
|
|
725
|
-
return {
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
717
|
+
return { isValid: false, reason: [ErrorType.AuthTypeIsNotSupported] };
|
|
718
|
+
}
|
|
719
|
+
checkPostBodySchema(schema, isRequired = false) {
|
|
720
|
+
var _a;
|
|
721
|
+
const paramResult = {
|
|
722
|
+
requiredNum: 0,
|
|
723
|
+
optionalNum: 0,
|
|
724
|
+
isValid: true,
|
|
725
|
+
reason: [],
|
|
729
726
|
};
|
|
727
|
+
if (Object.keys(schema).length === 0) {
|
|
728
|
+
return paramResult;
|
|
729
|
+
}
|
|
730
|
+
const isRequiredWithoutDefault = isRequired && schema.default === undefined;
|
|
731
|
+
const isCopilot = this.projectType === ProjectType.Copilot;
|
|
732
|
+
if (isCopilot && this.hasNestedObjectInSchema(schema)) {
|
|
733
|
+
paramResult.isValid = false;
|
|
734
|
+
paramResult.reason = [ErrorType.RequestBodyContainsNestedObject];
|
|
735
|
+
return paramResult;
|
|
736
|
+
}
|
|
737
|
+
if (schema.type === "string" ||
|
|
738
|
+
schema.type === "integer" ||
|
|
739
|
+
schema.type === "boolean" ||
|
|
740
|
+
schema.type === "number") {
|
|
741
|
+
if (isRequiredWithoutDefault) {
|
|
742
|
+
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
743
|
+
}
|
|
744
|
+
else {
|
|
745
|
+
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
else if (schema.type === "object") {
|
|
749
|
+
const { properties } = schema;
|
|
750
|
+
for (const property in properties) {
|
|
751
|
+
let isRequired = false;
|
|
752
|
+
if (schema.required && ((_a = schema.required) === null || _a === void 0 ? void 0 : _a.indexOf(property)) >= 0) {
|
|
753
|
+
isRequired = true;
|
|
754
|
+
}
|
|
755
|
+
const result = this.checkPostBodySchema(properties[property], isRequired);
|
|
756
|
+
paramResult.requiredNum += result.requiredNum;
|
|
757
|
+
paramResult.optionalNum += result.optionalNum;
|
|
758
|
+
paramResult.isValid = paramResult.isValid && result.isValid;
|
|
759
|
+
paramResult.reason.push(...result.reason);
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
else {
|
|
763
|
+
if (isRequiredWithoutDefault && !isCopilot) {
|
|
764
|
+
paramResult.isValid = false;
|
|
765
|
+
paramResult.reason.push(ErrorType.PostBodyContainsRequiredUnsupportedSchema);
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
return paramResult;
|
|
730
769
|
}
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
770
|
+
checkParamSchema(paramObject) {
|
|
771
|
+
const paramResult = {
|
|
772
|
+
requiredNum: 0,
|
|
773
|
+
optionalNum: 0,
|
|
774
|
+
isValid: true,
|
|
775
|
+
reason: [],
|
|
776
|
+
};
|
|
777
|
+
if (!paramObject) {
|
|
778
|
+
return paramResult;
|
|
779
|
+
}
|
|
780
|
+
const isCopilot = this.projectType === ProjectType.Copilot;
|
|
781
|
+
for (let i = 0; i < paramObject.length; i++) {
|
|
782
|
+
const param = paramObject[i];
|
|
783
|
+
const schema = param.schema;
|
|
784
|
+
if (isCopilot && this.hasNestedObjectInSchema(schema)) {
|
|
785
|
+
paramResult.isValid = false;
|
|
786
|
+
paramResult.reason.push(ErrorType.ParamsContainsNestedObject);
|
|
787
|
+
continue;
|
|
788
|
+
}
|
|
789
|
+
const isRequiredWithoutDefault = param.required && schema.default === undefined;
|
|
790
|
+
if (isCopilot) {
|
|
791
|
+
if (isRequiredWithoutDefault) {
|
|
792
|
+
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
793
|
+
}
|
|
794
|
+
else {
|
|
795
|
+
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
796
|
+
}
|
|
797
|
+
continue;
|
|
798
|
+
}
|
|
799
|
+
if (param.in === "header" || param.in === "cookie") {
|
|
800
|
+
if (isRequiredWithoutDefault) {
|
|
801
|
+
paramResult.isValid = false;
|
|
802
|
+
paramResult.reason.push(ErrorType.ParamsContainRequiredUnsupportedSchema);
|
|
803
|
+
}
|
|
804
|
+
continue;
|
|
805
|
+
}
|
|
806
|
+
if (schema.type !== "boolean" &&
|
|
807
|
+
schema.type !== "string" &&
|
|
808
|
+
schema.type !== "number" &&
|
|
809
|
+
schema.type !== "integer") {
|
|
810
|
+
if (isRequiredWithoutDefault) {
|
|
811
|
+
paramResult.isValid = false;
|
|
812
|
+
paramResult.reason.push(ErrorType.ParamsContainRequiredUnsupportedSchema);
|
|
813
|
+
}
|
|
814
|
+
continue;
|
|
815
|
+
}
|
|
816
|
+
if (param.in === "query" || param.in === "path") {
|
|
817
|
+
if (isRequiredWithoutDefault) {
|
|
818
|
+
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
819
|
+
}
|
|
820
|
+
else {
|
|
821
|
+
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
return paramResult;
|
|
737
826
|
}
|
|
738
|
-
|
|
739
|
-
if (
|
|
740
|
-
|
|
827
|
+
hasNestedObjectInSchema(schema) {
|
|
828
|
+
if (schema.type === "object") {
|
|
829
|
+
for (const property in schema.properties) {
|
|
830
|
+
const nestedSchema = schema.properties[property];
|
|
831
|
+
if (nestedSchema.type === "object") {
|
|
832
|
+
return true;
|
|
833
|
+
}
|
|
834
|
+
}
|
|
741
835
|
}
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
836
|
+
return false;
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
// Copyright (c) Microsoft Corporation.
|
|
841
|
+
class CopilotValidator extends Validator {
|
|
842
|
+
constructor(spec, options) {
|
|
843
|
+
super();
|
|
844
|
+
this.projectType = ProjectType.Copilot;
|
|
845
|
+
this.options = options;
|
|
846
|
+
this.spec = spec;
|
|
847
|
+
}
|
|
848
|
+
validateSpec() {
|
|
849
|
+
const result = { errors: [], warnings: [] };
|
|
850
|
+
// validate spec version
|
|
851
|
+
let validationResult = this.validateSpecVersion();
|
|
852
|
+
result.errors.push(...validationResult.errors);
|
|
853
|
+
// validate spec server
|
|
854
|
+
validationResult = this.validateSpecServer();
|
|
855
|
+
result.errors.push(...validationResult.errors);
|
|
856
|
+
// validate no supported API
|
|
857
|
+
validationResult = this.validateSpecNoSupportAPI();
|
|
858
|
+
result.errors.push(...validationResult.errors);
|
|
859
|
+
// validate operationId missing
|
|
860
|
+
validationResult = this.validateSpecOperationId();
|
|
861
|
+
result.warnings.push(...validationResult.warnings);
|
|
862
|
+
return result;
|
|
863
|
+
}
|
|
864
|
+
validateAPI(method, path) {
|
|
865
|
+
const result = { isValid: true, reason: [] };
|
|
866
|
+
method = method.toLocaleLowerCase();
|
|
867
|
+
// validate method and path
|
|
868
|
+
const methodAndPathResult = this.validateMethodAndPath(method, path);
|
|
869
|
+
if (!methodAndPathResult.isValid) {
|
|
870
|
+
return methodAndPathResult;
|
|
871
|
+
}
|
|
872
|
+
const operationObject = this.spec.paths[path][method];
|
|
873
|
+
// validate auth
|
|
874
|
+
const authCheckResult = this.validateAuth(method, path);
|
|
875
|
+
result.reason.push(...authCheckResult.reason);
|
|
876
|
+
// validate operationId
|
|
877
|
+
if (!this.options.allowMissingId && !operationObject.operationId) {
|
|
878
|
+
result.reason.push(ErrorType.MissingOperationId);
|
|
879
|
+
}
|
|
880
|
+
// validate server
|
|
881
|
+
const validateServerResult = this.validateServer(method, path);
|
|
882
|
+
result.reason.push(...validateServerResult.reason);
|
|
883
|
+
// validate response
|
|
884
|
+
const validateResponseResult = this.validateResponse(method, path);
|
|
885
|
+
result.reason.push(...validateResponseResult.reason);
|
|
886
|
+
// validate requestBody
|
|
887
|
+
const requestBody = operationObject.requestBody;
|
|
888
|
+
const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
|
|
889
|
+
if (requestJsonBody) {
|
|
890
|
+
const requestBodySchema = requestJsonBody.schema;
|
|
891
|
+
if (requestBodySchema.type !== "object") {
|
|
892
|
+
result.reason.push(ErrorType.PostBodySchemaIsNotJson);
|
|
893
|
+
}
|
|
894
|
+
const requestBodyParamResult = this.checkPostBodySchema(requestBodySchema, requestBody.required);
|
|
895
|
+
result.reason.push(...requestBodyParamResult.reason);
|
|
896
|
+
}
|
|
897
|
+
// validate parameters
|
|
898
|
+
const paramObject = operationObject.parameters;
|
|
899
|
+
const paramResult = this.checkParamSchema(paramObject);
|
|
900
|
+
result.reason.push(...paramResult.reason);
|
|
901
|
+
if (result.reason.length > 0) {
|
|
902
|
+
result.isValid = false;
|
|
903
|
+
}
|
|
904
|
+
return result;
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
// Copyright (c) Microsoft Corporation.
|
|
909
|
+
class SMEValidator extends Validator {
|
|
910
|
+
constructor(spec, options) {
|
|
911
|
+
super();
|
|
912
|
+
this.projectType = ProjectType.SME;
|
|
913
|
+
this.options = options;
|
|
914
|
+
this.spec = spec;
|
|
915
|
+
}
|
|
916
|
+
validateSpec() {
|
|
917
|
+
const result = { errors: [], warnings: [] };
|
|
918
|
+
// validate spec version
|
|
919
|
+
let validationResult = this.validateSpecVersion();
|
|
920
|
+
result.errors.push(...validationResult.errors);
|
|
921
|
+
// validate spec server
|
|
922
|
+
validationResult = this.validateSpecServer();
|
|
923
|
+
result.errors.push(...validationResult.errors);
|
|
924
|
+
// validate no supported API
|
|
925
|
+
validationResult = this.validateSpecNoSupportAPI();
|
|
926
|
+
result.errors.push(...validationResult.errors);
|
|
927
|
+
// validate operationId missing
|
|
928
|
+
if (this.options.allowMissingId) {
|
|
929
|
+
validationResult = this.validateSpecOperationId();
|
|
930
|
+
result.warnings.push(...validationResult.warnings);
|
|
931
|
+
}
|
|
932
|
+
return result;
|
|
933
|
+
}
|
|
934
|
+
validateAPI(method, path) {
|
|
935
|
+
const result = { isValid: true, reason: [] };
|
|
936
|
+
method = method.toLocaleLowerCase();
|
|
937
|
+
// validate method and path
|
|
938
|
+
const methodAndPathResult = this.validateMethodAndPath(method, path);
|
|
939
|
+
if (!methodAndPathResult.isValid) {
|
|
940
|
+
return methodAndPathResult;
|
|
941
|
+
}
|
|
942
|
+
const operationObject = this.spec.paths[path][method];
|
|
943
|
+
// validate auth
|
|
944
|
+
const authCheckResult = this.validateAuth(method, path);
|
|
945
|
+
result.reason.push(...authCheckResult.reason);
|
|
946
|
+
// validate operationId
|
|
947
|
+
if (!this.options.allowMissingId && !operationObject.operationId) {
|
|
948
|
+
result.reason.push(ErrorType.MissingOperationId);
|
|
949
|
+
}
|
|
950
|
+
// validate server
|
|
951
|
+
const validateServerResult = this.validateServer(method, path);
|
|
952
|
+
result.reason.push(...validateServerResult.reason);
|
|
953
|
+
// validate response
|
|
954
|
+
const validateResponseResult = this.validateResponse(method, path);
|
|
955
|
+
result.reason.push(...validateResponseResult.reason);
|
|
956
|
+
let postBodyResult = {
|
|
957
|
+
requiredNum: 0,
|
|
958
|
+
optionalNum: 0,
|
|
959
|
+
isValid: true,
|
|
960
|
+
reason: [],
|
|
961
|
+
};
|
|
962
|
+
// validate requestBody
|
|
963
|
+
const requestBody = operationObject.requestBody;
|
|
964
|
+
const requestJsonBody = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content["application/json"];
|
|
965
|
+
if (Utils.containMultipleMediaTypes(requestBody)) {
|
|
966
|
+
result.reason.push(ErrorType.PostBodyContainMultipleMediaTypes);
|
|
967
|
+
}
|
|
968
|
+
if (requestJsonBody) {
|
|
969
|
+
const requestBodySchema = requestJsonBody.schema;
|
|
970
|
+
postBodyResult = this.checkPostBodySchema(requestBodySchema, requestBody.required);
|
|
971
|
+
result.reason.push(...postBodyResult.reason);
|
|
972
|
+
}
|
|
973
|
+
// validate parameters
|
|
974
|
+
const paramObject = operationObject.parameters;
|
|
975
|
+
const paramResult = this.checkParamSchema(paramObject);
|
|
976
|
+
result.reason.push(...paramResult.reason);
|
|
977
|
+
// validate total parameters count
|
|
978
|
+
if (paramResult.isValid && postBodyResult.isValid) {
|
|
979
|
+
const paramCountResult = this.validateParamCount(postBodyResult, paramResult);
|
|
980
|
+
result.reason.push(...paramCountResult.reason);
|
|
981
|
+
}
|
|
982
|
+
if (result.reason.length > 0) {
|
|
983
|
+
result.isValid = false;
|
|
984
|
+
}
|
|
985
|
+
return result;
|
|
986
|
+
}
|
|
987
|
+
validateParamCount(postBodyResult, paramResult) {
|
|
988
|
+
const result = { isValid: true, reason: [] };
|
|
989
|
+
const totalRequiredParams = postBodyResult.requiredNum + paramResult.requiredNum;
|
|
990
|
+
const totalParams = totalRequiredParams + postBodyResult.optionalNum + paramResult.optionalNum;
|
|
991
|
+
if (totalRequiredParams > 1) {
|
|
992
|
+
if (!this.options.allowMultipleParameters ||
|
|
993
|
+
totalRequiredParams > SMEValidator.SMERequiredParamsMaxNum) {
|
|
994
|
+
result.reason.push(ErrorType.ExceededRequiredParamsLimit);
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
else if (totalParams === 0) {
|
|
998
|
+
result.reason.push(ErrorType.NoParameter);
|
|
999
|
+
}
|
|
1000
|
+
return result;
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
SMEValidator.SMERequiredParamsMaxNum = 5;
|
|
1004
|
+
|
|
1005
|
+
// Copyright (c) Microsoft Corporation.
|
|
1006
|
+
class TeamsAIValidator extends Validator {
|
|
1007
|
+
constructor(spec, options) {
|
|
1008
|
+
super();
|
|
1009
|
+
this.projectType = ProjectType.TeamsAi;
|
|
1010
|
+
this.options = options;
|
|
1011
|
+
this.spec = spec;
|
|
1012
|
+
}
|
|
1013
|
+
validateSpec() {
|
|
1014
|
+
const result = { errors: [], warnings: [] };
|
|
1015
|
+
// validate spec server
|
|
1016
|
+
let validationResult = this.validateSpecServer();
|
|
1017
|
+
result.errors.push(...validationResult.errors);
|
|
1018
|
+
// validate no supported API
|
|
1019
|
+
validationResult = this.validateSpecNoSupportAPI();
|
|
1020
|
+
result.errors.push(...validationResult.errors);
|
|
1021
|
+
return result;
|
|
1022
|
+
}
|
|
1023
|
+
validateAPI(method, path) {
|
|
1024
|
+
const result = { isValid: true, reason: [] };
|
|
1025
|
+
method = method.toLocaleLowerCase();
|
|
1026
|
+
// validate method and path
|
|
1027
|
+
const methodAndPathResult = this.validateMethodAndPath(method, path);
|
|
1028
|
+
if (!methodAndPathResult.isValid) {
|
|
1029
|
+
return methodAndPathResult;
|
|
1030
|
+
}
|
|
1031
|
+
const operationObject = this.spec.paths[path][method];
|
|
1032
|
+
// validate operationId
|
|
1033
|
+
if (!this.options.allowMissingId && !operationObject.operationId) {
|
|
1034
|
+
result.reason.push(ErrorType.MissingOperationId);
|
|
1035
|
+
}
|
|
1036
|
+
// validate server
|
|
1037
|
+
const validateServerResult = this.validateServer(method, path);
|
|
1038
|
+
result.reason.push(...validateServerResult.reason);
|
|
1039
|
+
if (result.reason.length > 0) {
|
|
1040
|
+
result.isValid = false;
|
|
1041
|
+
}
|
|
1042
|
+
return result;
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
class ValidatorFactory {
|
|
1047
|
+
static create(spec, options) {
|
|
1048
|
+
var _a;
|
|
1049
|
+
const type = (_a = options.projectType) !== null && _a !== void 0 ? _a : ProjectType.SME;
|
|
1050
|
+
switch (type) {
|
|
1051
|
+
case ProjectType.SME:
|
|
1052
|
+
return new SMEValidator(spec, options);
|
|
1053
|
+
case ProjectType.Copilot:
|
|
1054
|
+
return new CopilotValidator(spec, options);
|
|
1055
|
+
case ProjectType.TeamsAi:
|
|
1056
|
+
return new TeamsAIValidator(spec, options);
|
|
1057
|
+
default:
|
|
1058
|
+
throw new Error(`Invalid project type: ${type}`);
|
|
745
1059
|
}
|
|
746
|
-
return safeRegistrationIdEnvName;
|
|
747
1060
|
}
|
|
748
1061
|
}
|
|
749
1062
|
|
|
@@ -763,7 +1076,14 @@ class SpecParser {
|
|
|
763
1076
|
allowSwagger: false,
|
|
764
1077
|
allowAPIKeyAuth: false,
|
|
765
1078
|
allowMultipleParameters: false,
|
|
1079
|
+
allowBearerTokenAuth: false,
|
|
766
1080
|
allowOauth2: false,
|
|
1081
|
+
allowMethods: ["get", "post"],
|
|
1082
|
+
allowConversationStarters: false,
|
|
1083
|
+
allowResponseSemantics: false,
|
|
1084
|
+
allowConfirmation: false,
|
|
1085
|
+
projectType: ProjectType.SME,
|
|
1086
|
+
isGptPlugin: false,
|
|
767
1087
|
};
|
|
768
1088
|
this.pathOrSpec = pathOrDoc;
|
|
769
1089
|
this.parser = new SwaggerParser();
|
|
@@ -779,11 +1099,7 @@ class SpecParser {
|
|
|
779
1099
|
try {
|
|
780
1100
|
try {
|
|
781
1101
|
yield this.loadSpec();
|
|
782
|
-
yield this.parser.validate(this.spec
|
|
783
|
-
validate: {
|
|
784
|
-
schema: false,
|
|
785
|
-
},
|
|
786
|
-
});
|
|
1102
|
+
yield this.parser.validate(this.spec);
|
|
787
1103
|
}
|
|
788
1104
|
catch (e) {
|
|
789
1105
|
return {
|
|
@@ -792,16 +1108,46 @@ class SpecParser {
|
|
|
792
1108
|
errors: [{ type: ErrorType.SpecNotValid, content: e.toString() }],
|
|
793
1109
|
};
|
|
794
1110
|
}
|
|
1111
|
+
const errors = [];
|
|
1112
|
+
const warnings = [];
|
|
795
1113
|
if (!this.options.allowSwagger && this.isSwaggerFile) {
|
|
796
1114
|
return {
|
|
797
1115
|
status: ValidationStatus.Error,
|
|
798
1116
|
warnings: [],
|
|
799
1117
|
errors: [
|
|
800
|
-
{
|
|
1118
|
+
{
|
|
1119
|
+
type: ErrorType.SwaggerNotSupported,
|
|
1120
|
+
content: ConstantString.SwaggerNotSupported,
|
|
1121
|
+
},
|
|
801
1122
|
],
|
|
802
1123
|
};
|
|
803
1124
|
}
|
|
804
|
-
|
|
1125
|
+
// Remote reference not supported
|
|
1126
|
+
const refPaths = this.parser.$refs.paths();
|
|
1127
|
+
// refPaths [0] is the current spec file path
|
|
1128
|
+
if (refPaths.length > 1) {
|
|
1129
|
+
errors.push({
|
|
1130
|
+
type: ErrorType.RemoteRefNotSupported,
|
|
1131
|
+
content: Utils.format(ConstantString.RemoteRefNotSupported, refPaths.join(", ")),
|
|
1132
|
+
data: refPaths,
|
|
1133
|
+
});
|
|
1134
|
+
}
|
|
1135
|
+
const validator = this.getValidator(this.spec);
|
|
1136
|
+
const validationResult = validator.validateSpec();
|
|
1137
|
+
warnings.push(...validationResult.warnings);
|
|
1138
|
+
errors.push(...validationResult.errors);
|
|
1139
|
+
let status = ValidationStatus.Valid;
|
|
1140
|
+
if (warnings.length > 0 && errors.length === 0) {
|
|
1141
|
+
status = ValidationStatus.Warning;
|
|
1142
|
+
}
|
|
1143
|
+
else if (errors.length > 0) {
|
|
1144
|
+
status = ValidationStatus.Error;
|
|
1145
|
+
}
|
|
1146
|
+
return {
|
|
1147
|
+
status: status,
|
|
1148
|
+
warnings: warnings,
|
|
1149
|
+
errors: errors,
|
|
1150
|
+
};
|
|
805
1151
|
}
|
|
806
1152
|
catch (err) {
|
|
807
1153
|
throw new SpecParserError(err.toString(), ErrorType.ValidateFailed);
|
|
@@ -812,17 +1158,20 @@ class SpecParser {
|
|
|
812
1158
|
return __awaiter(this, void 0, void 0, function* () {
|
|
813
1159
|
try {
|
|
814
1160
|
yield this.loadSpec();
|
|
815
|
-
const apiMap = this.
|
|
1161
|
+
const apiMap = this.getAPIs(this.spec);
|
|
816
1162
|
const apiInfos = [];
|
|
817
1163
|
for (const key in apiMap) {
|
|
818
|
-
const
|
|
1164
|
+
const { operation, isValid } = apiMap[key];
|
|
1165
|
+
if (!isValid) {
|
|
1166
|
+
continue;
|
|
1167
|
+
}
|
|
819
1168
|
const [method, path] = key.split(" ");
|
|
820
|
-
const operationId =
|
|
1169
|
+
const operationId = operation.operationId;
|
|
821
1170
|
// In Browser environment, this api is by default not support api without operationId
|
|
822
1171
|
if (!operationId) {
|
|
823
1172
|
continue;
|
|
824
1173
|
}
|
|
825
|
-
const
|
|
1174
|
+
const command = Utils.parseApiInfo(operation, this.options);
|
|
826
1175
|
const apiInfo = {
|
|
827
1176
|
method: method,
|
|
828
1177
|
path: path,
|
|
@@ -831,9 +1180,6 @@ class SpecParser {
|
|
|
831
1180
|
parameters: command.parameters,
|
|
832
1181
|
description: command.description,
|
|
833
1182
|
};
|
|
834
|
-
if (warning) {
|
|
835
|
-
apiInfo.warning = warning;
|
|
836
|
-
}
|
|
837
1183
|
apiInfos.push(apiInfo);
|
|
838
1184
|
}
|
|
839
1185
|
return apiInfos;
|
|
@@ -854,12 +1200,36 @@ class SpecParser {
|
|
|
854
1200
|
throw new Error("Method not implemented.");
|
|
855
1201
|
});
|
|
856
1202
|
}
|
|
1203
|
+
/**
|
|
1204
|
+
* Generate specs according to the filters.
|
|
1205
|
+
* @param filter An array of strings that represent the filters to apply when generating the artifacts. If filter is empty, it would process nothing.
|
|
1206
|
+
*/
|
|
1207
|
+
// eslint-disable-next-line @typescript-eslint/require-await
|
|
1208
|
+
getFilteredSpecs(filter, signal) {
|
|
1209
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1210
|
+
throw new Error("Method not implemented.");
|
|
1211
|
+
});
|
|
1212
|
+
}
|
|
1213
|
+
/**
|
|
1214
|
+
* Generates and update artifacts from the OpenAPI specification file. Generate Adaptive Cards, update Teams app manifest, and generate a new OpenAPI specification file.
|
|
1215
|
+
* @param manifestPath A file path of the Teams app manifest file to update.
|
|
1216
|
+
* @param filter An array of strings that represent the filters to apply when generating the artifacts. If filter is empty, it would process nothing.
|
|
1217
|
+
* @param outputSpecPath File path of the new OpenAPI specification file to generate. If not specified or empty, no spec file will be generated.
|
|
1218
|
+
* @param pluginFilePath File path of the api plugin file to generate.
|
|
1219
|
+
*/
|
|
1220
|
+
// eslint-disable-next-line @typescript-eslint/require-await
|
|
1221
|
+
generateForCopilot(manifestPath, filter, outputSpecPath, pluginFilePath, signal) {
|
|
1222
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1223
|
+
throw new Error("Method not implemented.");
|
|
1224
|
+
});
|
|
1225
|
+
}
|
|
857
1226
|
/**
|
|
858
1227
|
* Generates and update artifacts from the OpenAPI specification file. Generate Adaptive Cards, update Teams app manifest, and generate a new OpenAPI specification file.
|
|
859
1228
|
* @param manifestPath A file path of the Teams app manifest file to update.
|
|
860
1229
|
* @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
1230
|
* @param outputSpecPath File path of the new OpenAPI specification file to generate. If not specified or empty, no spec file will be generated.
|
|
862
1231
|
* @param adaptiveCardFolder Folder path where the Adaptive Card files will be generated. If not specified or empty, Adaptive Card files will not be generated.
|
|
1232
|
+
* @param isMe Boolean that indicates whether the project is an Messaging Extension. For Messaging Extension, composeExtensions will be added in Teams app manifest.
|
|
863
1233
|
*/
|
|
864
1234
|
// eslint-disable-next-line @typescript-eslint/require-await
|
|
865
1235
|
generate(manifestPath, filter, outputSpecPath, adaptiveCardFolder, signal) {
|
|
@@ -879,15 +1249,178 @@ class SpecParser {
|
|
|
879
1249
|
}
|
|
880
1250
|
});
|
|
881
1251
|
}
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
1252
|
+
getAPIs(spec) {
|
|
1253
|
+
const validator = this.getValidator(spec);
|
|
1254
|
+
const apiMap = validator.listAPIs();
|
|
1255
|
+
return apiMap;
|
|
1256
|
+
}
|
|
1257
|
+
getValidator(spec) {
|
|
1258
|
+
if (this.validator) {
|
|
1259
|
+
return this.validator;
|
|
885
1260
|
}
|
|
886
|
-
const
|
|
887
|
-
this.
|
|
888
|
-
return
|
|
1261
|
+
const validator = ValidatorFactory.create(spec, this.options);
|
|
1262
|
+
this.validator = validator;
|
|
1263
|
+
return validator;
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1267
|
+
// Copyright (c) Microsoft Corporation.
|
|
1268
|
+
class AdaptiveCardGenerator {
|
|
1269
|
+
static generateAdaptiveCard(operationItem) {
|
|
1270
|
+
try {
|
|
1271
|
+
const { json } = Utils.getResponseJson(operationItem);
|
|
1272
|
+
let cardBody = [];
|
|
1273
|
+
let schema = json.schema;
|
|
1274
|
+
let jsonPath = "$";
|
|
1275
|
+
if (schema && Object.keys(schema).length > 0) {
|
|
1276
|
+
jsonPath = AdaptiveCardGenerator.getResponseJsonPathFromSchema(schema);
|
|
1277
|
+
if (jsonPath !== "$") {
|
|
1278
|
+
schema = schema.properties[jsonPath];
|
|
1279
|
+
}
|
|
1280
|
+
cardBody = AdaptiveCardGenerator.generateCardFromResponse(schema, "");
|
|
1281
|
+
}
|
|
1282
|
+
// if no schema, try to use example value
|
|
1283
|
+
if (cardBody.length === 0 && (json.examples || json.example)) {
|
|
1284
|
+
cardBody = [
|
|
1285
|
+
{
|
|
1286
|
+
type: ConstantString.TextBlockType,
|
|
1287
|
+
text: "${jsonStringify($root)}",
|
|
1288
|
+
wrap: true,
|
|
1289
|
+
},
|
|
1290
|
+
];
|
|
1291
|
+
}
|
|
1292
|
+
// if no example value, use default success response
|
|
1293
|
+
if (cardBody.length === 0) {
|
|
1294
|
+
cardBody = [
|
|
1295
|
+
{
|
|
1296
|
+
type: ConstantString.TextBlockType,
|
|
1297
|
+
text: "success",
|
|
1298
|
+
wrap: true,
|
|
1299
|
+
},
|
|
1300
|
+
];
|
|
1301
|
+
}
|
|
1302
|
+
const fullCard = {
|
|
1303
|
+
type: ConstantString.AdaptiveCardType,
|
|
1304
|
+
$schema: ConstantString.AdaptiveCardSchema,
|
|
1305
|
+
version: ConstantString.AdaptiveCardVersion,
|
|
1306
|
+
body: cardBody,
|
|
1307
|
+
};
|
|
1308
|
+
return [fullCard, jsonPath];
|
|
1309
|
+
}
|
|
1310
|
+
catch (err) {
|
|
1311
|
+
throw new SpecParserError(err.toString(), ErrorType.GenerateAdaptiveCardFailed);
|
|
1312
|
+
}
|
|
1313
|
+
}
|
|
1314
|
+
static generateCardFromResponse(schema, name, parentArrayName = "") {
|
|
1315
|
+
if (schema.type === "array") {
|
|
1316
|
+
// schema.items can be arbitrary object: schema { type: array, items: {} }
|
|
1317
|
+
if (Object.keys(schema.items).length === 0) {
|
|
1318
|
+
return [
|
|
1319
|
+
{
|
|
1320
|
+
type: ConstantString.TextBlockType,
|
|
1321
|
+
text: name ? `${name}: \${jsonStringify(${name})}` : "result: ${jsonStringify($root)}",
|
|
1322
|
+
wrap: true,
|
|
1323
|
+
},
|
|
1324
|
+
];
|
|
1325
|
+
}
|
|
1326
|
+
const obj = AdaptiveCardGenerator.generateCardFromResponse(schema.items, "", name);
|
|
1327
|
+
const template = {
|
|
1328
|
+
type: ConstantString.ContainerType,
|
|
1329
|
+
$data: name ? `\${${name}}` : "${$root}",
|
|
1330
|
+
items: Array(),
|
|
1331
|
+
};
|
|
1332
|
+
template.items.push(...obj);
|
|
1333
|
+
return [template];
|
|
1334
|
+
}
|
|
1335
|
+
// some schema may not contain type but contain properties
|
|
1336
|
+
if (schema.type === "object" || (!schema.type && schema.properties)) {
|
|
1337
|
+
const { properties } = schema;
|
|
1338
|
+
const result = [];
|
|
1339
|
+
for (const property in properties) {
|
|
1340
|
+
const obj = AdaptiveCardGenerator.generateCardFromResponse(properties[property], name ? `${name}.${property}` : property, parentArrayName);
|
|
1341
|
+
result.push(...obj);
|
|
1342
|
+
}
|
|
1343
|
+
if (schema.additionalProperties) {
|
|
1344
|
+
// TODO: better ways to handler warnings.
|
|
1345
|
+
console.warn(ConstantString.AdditionalPropertiesNotSupported);
|
|
1346
|
+
}
|
|
1347
|
+
return result;
|
|
1348
|
+
}
|
|
1349
|
+
if (schema.type === "string" ||
|
|
1350
|
+
schema.type === "integer" ||
|
|
1351
|
+
schema.type === "boolean" ||
|
|
1352
|
+
schema.type === "number") {
|
|
1353
|
+
if (!AdaptiveCardGenerator.isImageUrlProperty(schema, name, parentArrayName)) {
|
|
1354
|
+
// string in root: "ddd"
|
|
1355
|
+
let text = "result: ${$root}";
|
|
1356
|
+
if (name) {
|
|
1357
|
+
// object { id: "1" }
|
|
1358
|
+
text = `${name}: \${if(${name}, ${name}, 'N/A')}`;
|
|
1359
|
+
if (parentArrayName) {
|
|
1360
|
+
// object types inside array: { tags: ["id": 1, "name": "name"] }
|
|
1361
|
+
text = `${parentArrayName}.${text}`;
|
|
1362
|
+
}
|
|
1363
|
+
}
|
|
1364
|
+
else if (parentArrayName) {
|
|
1365
|
+
// string array: photoUrls: ["1", "2"]
|
|
1366
|
+
text = `${parentArrayName}: ` + "${$data}";
|
|
1367
|
+
}
|
|
1368
|
+
return [
|
|
1369
|
+
{
|
|
1370
|
+
type: ConstantString.TextBlockType,
|
|
1371
|
+
text,
|
|
1372
|
+
wrap: true,
|
|
1373
|
+
},
|
|
1374
|
+
];
|
|
1375
|
+
}
|
|
1376
|
+
else {
|
|
1377
|
+
if (name) {
|
|
1378
|
+
return [
|
|
1379
|
+
{
|
|
1380
|
+
type: "Image",
|
|
1381
|
+
url: `\${${name}}`,
|
|
1382
|
+
$when: `\${${name} != null}`,
|
|
1383
|
+
},
|
|
1384
|
+
];
|
|
1385
|
+
}
|
|
1386
|
+
else {
|
|
1387
|
+
return [
|
|
1388
|
+
{
|
|
1389
|
+
type: "Image",
|
|
1390
|
+
url: "${$data}",
|
|
1391
|
+
$when: "${$data != null}",
|
|
1392
|
+
},
|
|
1393
|
+
];
|
|
1394
|
+
}
|
|
1395
|
+
}
|
|
1396
|
+
}
|
|
1397
|
+
if (schema.oneOf || schema.anyOf || schema.not || schema.allOf) {
|
|
1398
|
+
throw new Error(Utils.format(ConstantString.SchemaNotSupported, JSON.stringify(schema)));
|
|
1399
|
+
}
|
|
1400
|
+
throw new Error(Utils.format(ConstantString.UnknownSchema, JSON.stringify(schema)));
|
|
1401
|
+
}
|
|
1402
|
+
// Find the first array property in the response schema object with the well-known name
|
|
1403
|
+
static getResponseJsonPathFromSchema(schema) {
|
|
1404
|
+
if (schema.type === "object" || (!schema.type && schema.properties)) {
|
|
1405
|
+
const { properties } = schema;
|
|
1406
|
+
for (const property in properties) {
|
|
1407
|
+
const schema = properties[property];
|
|
1408
|
+
if (schema.type === "array" &&
|
|
1409
|
+
Utils.isWellKnownName(property, ConstantString.WellknownResultNames)) {
|
|
1410
|
+
return property;
|
|
1411
|
+
}
|
|
1412
|
+
}
|
|
1413
|
+
}
|
|
1414
|
+
return "$";
|
|
1415
|
+
}
|
|
1416
|
+
static isImageUrlProperty(schema, name, parentArrayName) {
|
|
1417
|
+
const propertyName = name ? name : parentArrayName;
|
|
1418
|
+
return (!!propertyName &&
|
|
1419
|
+
schema.type === "string" &&
|
|
1420
|
+
Utils.isWellKnownName(propertyName, ConstantString.WellknownImageName) &&
|
|
1421
|
+
(propertyName.toLocaleLowerCase().indexOf("url") >= 0 || schema.format === "uri"));
|
|
889
1422
|
}
|
|
890
1423
|
}
|
|
891
1424
|
|
|
892
|
-
export { ConstantString, ErrorType, SpecParser, SpecParserError, Utils, ValidationStatus, WarningType };
|
|
1425
|
+
export { AdaptiveCardGenerator, ConstantString, ErrorType, SpecParser, SpecParserError, Utils, ValidationStatus, WarningType };
|
|
893
1426
|
//# sourceMappingURL=index.esm5.js.map
|