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