@microsoft/m365-spec-parser 0.2.12-alpha.d9ea25f7c.0 → 0.2.12-alpha.eaecfe55f.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 +1565 -1565
- package/dist/index.esm2017.mjs +2605 -2604
- package/dist/index.esm2017.mjs.map +1 -1
- package/dist/index.esm5.js +1572 -1612
- package/dist/index.esm5.js.map +1 -1
- package/dist/index.node.cjs.js +2606 -2672
- package/dist/index.node.cjs.js.map +1 -1
- package/dist/src/adaptiveCardGenerator.d.ts +10 -10
- package/dist/src/adaptiveCardWrapper.d.ts +16 -16
- package/dist/src/constants.d.ts +53 -53
- package/dist/src/index.browser.d.ts +7 -7
- package/dist/src/index.d.ts +6 -6
- package/dist/src/interfaces.d.ts +300 -300
- package/dist/src/jsonDataGenerator.d.ts +6 -6
- package/dist/src/manifestUpdater.d.ts +16 -16
- package/dist/src/specFilter.d.ts +5 -5
- package/dist/src/specOptimizer.d.ts +18 -18
- package/dist/src/specParser.browser.d.ts +60 -60
- package/dist/src/specParser.d.ts +71 -71
- package/dist/src/specParserError.d.ts +5 -5
- package/dist/src/utils.d.ts +34 -34
- package/package.json +21 -20
package/dist/index.esm5.js
CHANGED
|
@@ -1,1636 +1,1596 @@
|
|
|
1
1
|
import SwaggerParser from '@apidevtools/swagger-parser';
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
ErrorType["
|
|
40
|
-
ErrorType["
|
|
41
|
-
ErrorType["
|
|
42
|
-
ErrorType["
|
|
43
|
-
ErrorType["
|
|
44
|
-
ErrorType["
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
})(
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
(function (WarningType) {
|
|
81
|
-
WarningType["OperationIdMissing"] = "operationid-missing";
|
|
82
|
-
WarningType["GenerateCardFailed"] = "generate-card-failed";
|
|
83
|
-
WarningType["OperationOnlyContainsOptionalParam"] = "operation-only-contains-optional-param";
|
|
84
|
-
WarningType["ConvertSwaggerToOpenAPI"] = "convert-swagger-to-openapi";
|
|
85
|
-
WarningType["OpenAPI31ConvertTo30"] = "openapi31-convert-to-30";
|
|
86
|
-
WarningType["OperationIdContainsSpecialCharacters"] = "operationid-contains-special-characters";
|
|
87
|
-
WarningType["UnsupportedAuthType"] = "unsupported-auth-type";
|
|
88
|
-
WarningType["GenerateJsonDataFailed"] = "generate-json-data-failed";
|
|
89
|
-
WarningType["Unknown"] = "unknown";
|
|
90
|
-
})(WarningType || (WarningType = {}));
|
|
91
|
-
/**
|
|
92
|
-
* An enum that represents the validation status of an OpenAPI specification file.
|
|
93
|
-
*/
|
|
94
|
-
var ValidationStatus;
|
|
95
|
-
(function (ValidationStatus) {
|
|
96
|
-
ValidationStatus[ValidationStatus["Valid"] = 0] = "Valid";
|
|
97
|
-
ValidationStatus[ValidationStatus["Warning"] = 1] = "Warning";
|
|
98
|
-
ValidationStatus[ValidationStatus["Error"] = 2] = "Error";
|
|
99
|
-
})(ValidationStatus || (ValidationStatus = {}));
|
|
100
|
-
var ProjectType;
|
|
101
|
-
(function (ProjectType) {
|
|
102
|
-
ProjectType[ProjectType["Copilot"] = 0] = "Copilot";
|
|
103
|
-
ProjectType[ProjectType["SME"] = 1] = "SME";
|
|
104
|
-
ProjectType[ProjectType["TeamsAi"] = 2] = "TeamsAi";
|
|
105
|
-
})(ProjectType || (ProjectType = {}));
|
|
106
|
-
var AdaptiveCardUpdateStrategy;
|
|
107
|
-
(function (AdaptiveCardUpdateStrategy) {
|
|
108
|
-
AdaptiveCardUpdateStrategy[AdaptiveCardUpdateStrategy["CreateNew"] = 0] = "CreateNew";
|
|
109
|
-
AdaptiveCardUpdateStrategy[AdaptiveCardUpdateStrategy["KeepExisting"] = 1] = "KeepExisting";
|
|
3
|
+
// Copyright (c) Microsoft Corporation.
|
|
4
|
+
/**
|
|
5
|
+
* An enum that represents the types of errors that can occur during validation.
|
|
6
|
+
*/
|
|
7
|
+
var ErrorType;
|
|
8
|
+
(function (ErrorType) {
|
|
9
|
+
ErrorType["SpecNotValid"] = "spec-not-valid";
|
|
10
|
+
ErrorType["RemoteRefNotSupported"] = "remote-ref-not-supported";
|
|
11
|
+
ErrorType["NoServerInformation"] = "no-server-information";
|
|
12
|
+
ErrorType["UrlProtocolNotSupported"] = "url-protocol-not-supported";
|
|
13
|
+
ErrorType["RelativeServerUrlNotSupported"] = "relative-server-url-not-supported";
|
|
14
|
+
ErrorType["NoSupportedApi"] = "no-supported-api";
|
|
15
|
+
ErrorType["NoExtraAPICanBeAdded"] = "no-extra-api-can-be-added";
|
|
16
|
+
ErrorType["AddedAPINotInOriginalSpec"] = "added-api-not-in-original-spec";
|
|
17
|
+
ErrorType["ResolveServerUrlFailed"] = "resolve-server-url-failed";
|
|
18
|
+
ErrorType["SwaggerNotSupported"] = "swagger-not-supported";
|
|
19
|
+
ErrorType["MultipleAuthNotSupported"] = "multiple-auth-not-supported";
|
|
20
|
+
ErrorType["SpecVersionNotSupported"] = "spec-version-not-supported";
|
|
21
|
+
ErrorType["CircularReferenceNotSupported"] = "circular-reference-not-supported";
|
|
22
|
+
ErrorType["ListFailed"] = "list-failed";
|
|
23
|
+
ErrorType["listSupportedAPIInfoFailed"] = "list-supported-api-info-failed";
|
|
24
|
+
ErrorType["FilterSpecFailed"] = "filter-spec-failed";
|
|
25
|
+
ErrorType["UpdateManifestFailed"] = "update-manifest-failed";
|
|
26
|
+
ErrorType["GenerateAdaptiveCardFailed"] = "generate-adaptive-card-failed";
|
|
27
|
+
ErrorType["GenerateFailed"] = "generate-failed";
|
|
28
|
+
ErrorType["ValidateFailed"] = "validate-failed";
|
|
29
|
+
ErrorType["GetSpecFailed"] = "get-spec-failed";
|
|
30
|
+
ErrorType["AuthTypeIsNotSupported"] = "auth-type-is-not-supported";
|
|
31
|
+
ErrorType["MissingOperationId"] = "missing-operation-id";
|
|
32
|
+
ErrorType["PostBodyContainMultipleMediaTypes"] = "post-body-contain-multiple-media-types";
|
|
33
|
+
ErrorType["ResponseContainMultipleMediaTypes"] = "response-contain-multiple-media-types";
|
|
34
|
+
ErrorType["ResponseJsonIsEmpty"] = "response-json-is-empty";
|
|
35
|
+
ErrorType["PostBodyContainsRequiredUnsupportedSchema"] = "post-body-contains-required-unsupported-schema";
|
|
36
|
+
ErrorType["ParamsContainRequiredUnsupportedSchema"] = "params-contain-required-unsupported-schema";
|
|
37
|
+
ErrorType["ExceededRequiredParamsLimit"] = "exceeded-required-params-limit";
|
|
38
|
+
ErrorType["NoParameter"] = "no-parameter";
|
|
39
|
+
ErrorType["NoAPIInfo"] = "no-api-info";
|
|
40
|
+
ErrorType["MethodNotAllowed"] = "method-not-allowed";
|
|
41
|
+
ErrorType["UrlPathNotExist"] = "url-path-not-exist";
|
|
42
|
+
ErrorType["Cancelled"] = "cancelled";
|
|
43
|
+
ErrorType["Unknown"] = "unknown";
|
|
44
|
+
ErrorType["AddAuthFailed"] = "add-auth-failed";
|
|
45
|
+
})(ErrorType || (ErrorType = {}));
|
|
46
|
+
/**
|
|
47
|
+
* An enum that represents the types of warnings that can occur during validation.
|
|
48
|
+
*/
|
|
49
|
+
var WarningType;
|
|
50
|
+
(function (WarningType) {
|
|
51
|
+
WarningType["OperationIdMissing"] = "operationid-missing";
|
|
52
|
+
WarningType["GenerateCardFailed"] = "generate-card-failed";
|
|
53
|
+
WarningType["OperationOnlyContainsOptionalParam"] = "operation-only-contains-optional-param";
|
|
54
|
+
WarningType["ConvertSwaggerToOpenAPI"] = "convert-swagger-to-openapi";
|
|
55
|
+
WarningType["OpenAPI31ConvertTo30"] = "openapi31-convert-to-30";
|
|
56
|
+
WarningType["OperationIdContainsSpecialCharacters"] = "operationid-contains-special-characters";
|
|
57
|
+
WarningType["UnsupportedAuthType"] = "unsupported-auth-type";
|
|
58
|
+
WarningType["GenerateJsonDataFailed"] = "generate-json-data-failed";
|
|
59
|
+
WarningType["Unknown"] = "unknown";
|
|
60
|
+
})(WarningType || (WarningType = {}));
|
|
61
|
+
/**
|
|
62
|
+
* An enum that represents the validation status of an OpenAPI specification file.
|
|
63
|
+
*/
|
|
64
|
+
var ValidationStatus;
|
|
65
|
+
(function (ValidationStatus) {
|
|
66
|
+
ValidationStatus[ValidationStatus["Valid"] = 0] = "Valid";
|
|
67
|
+
ValidationStatus[ValidationStatus["Warning"] = 1] = "Warning";
|
|
68
|
+
ValidationStatus[ValidationStatus["Error"] = 2] = "Error";
|
|
69
|
+
})(ValidationStatus || (ValidationStatus = {}));
|
|
70
|
+
var ProjectType;
|
|
71
|
+
(function (ProjectType) {
|
|
72
|
+
ProjectType[ProjectType["Copilot"] = 0] = "Copilot";
|
|
73
|
+
ProjectType[ProjectType["SME"] = 1] = "SME";
|
|
74
|
+
ProjectType[ProjectType["TeamsAi"] = 2] = "TeamsAi";
|
|
75
|
+
})(ProjectType || (ProjectType = {}));
|
|
76
|
+
var AdaptiveCardUpdateStrategy;
|
|
77
|
+
(function (AdaptiveCardUpdateStrategy) {
|
|
78
|
+
AdaptiveCardUpdateStrategy[AdaptiveCardUpdateStrategy["CreateNew"] = 0] = "CreateNew";
|
|
79
|
+
AdaptiveCardUpdateStrategy[AdaptiveCardUpdateStrategy["KeepExisting"] = 1] = "KeepExisting";
|
|
110
80
|
})(AdaptiveCardUpdateStrategy || (AdaptiveCardUpdateStrategy = {}));
|
|
111
81
|
|
|
112
|
-
// Copyright (c) Microsoft Corporation.
|
|
113
|
-
class SpecParserError extends Error {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
82
|
+
// Copyright (c) Microsoft Corporation.
|
|
83
|
+
class SpecParserError extends Error {
|
|
84
|
+
errorType;
|
|
85
|
+
constructor(message, errorType) {
|
|
86
|
+
super(message);
|
|
87
|
+
this.errorType = errorType;
|
|
88
|
+
}
|
|
118
89
|
}
|
|
119
90
|
|
|
120
|
-
// Copyright (c) Microsoft Corporation.
|
|
121
|
-
class ConstantString {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
];
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
91
|
+
// Copyright (c) Microsoft Corporation.
|
|
92
|
+
class ConstantString {
|
|
93
|
+
static CancelledMessage = "Operation is cancelled.";
|
|
94
|
+
static NoServerInformation = "No server information is found in the OpenAPI description document.";
|
|
95
|
+
static RemoteRefNotSupported = "Remote reference is not supported: %s.";
|
|
96
|
+
static MissingOperationId = "Missing operationIds: %s.";
|
|
97
|
+
static 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.";
|
|
98
|
+
static AdditionalPropertiesNotSupported = "'additionalProperties' is not supported, and will be ignored.";
|
|
99
|
+
static SchemaNotSupported = "'oneOf', 'allOf', 'anyOf', and 'not' schema are not supported: %s.";
|
|
100
|
+
static UnknownSchema = "Unknown schema: %s.";
|
|
101
|
+
static UrlProtocolNotSupported = "Server url is not correct: protocol %s is not supported, you should use https protocol instead.";
|
|
102
|
+
static RelativeServerUrlNotSupported = "Server url is not correct: relative server url is not supported.";
|
|
103
|
+
static ResolveServerUrlFailed = "Unable to resolve the server URL: please make sure that the environment variable %s is defined.";
|
|
104
|
+
static OperationOnlyContainsOptionalParam = "Operation %s contains multiple optional parameters. The first optional parameter is used for this command.";
|
|
105
|
+
static ConvertSwaggerToOpenAPI = "The Swagger 2.0 file has been converted to OpenAPI 3.0.";
|
|
106
|
+
static OpenAPI31ConvertTo30 = "OpenAPI 3.1 document has been converted to OpenAPI 3.0.";
|
|
107
|
+
static SwaggerNotSupported = "Swagger 2.0 is not supported. Please convert to OpenAPI 3.0 manually before proceeding.";
|
|
108
|
+
static SpecVersionNotSupported = "Unsupported OpenAPI version %s. Please use version 3.0.x.";
|
|
109
|
+
static MultipleAuthNotSupported = "Multiple authentication methods are unsupported. Ensure all selected APIs use identical authentication.";
|
|
110
|
+
static OperationIdContainsSpecialCharacters = "Operation id '%s' in OpenAPI description document contained special characters and was renamed to '%s'.";
|
|
111
|
+
static AuthTypeIsNotSupported = "Unsupported authorization type in API '%s'. No authorization will be used.";
|
|
112
|
+
static UnsupportedSchema = "Unsupported schema in %s %s: %s";
|
|
113
|
+
static FuncDescriptionTooLong = "The description of the function '%s' is too long. The current length is %s characters, while the maximum allowed length is %s characters.";
|
|
114
|
+
static GenerateJsonDataFailed = "Failed to generate JSON data for api: %s due to %s.";
|
|
115
|
+
static WrappedCardVersion = "1.0";
|
|
116
|
+
static WrappedCardSchema = "https://developer.microsoft.com/json-schemas/teams/v1.19/MicrosoftTeams.ResponseRenderingTemplate.schema.json";
|
|
117
|
+
static WrappedCardResponseLayout = "list";
|
|
118
|
+
static GetMethod = "get";
|
|
119
|
+
static PostMethod = "post";
|
|
120
|
+
static AdaptiveCardVersion = "1.5";
|
|
121
|
+
static AdaptiveCardSchema = "https://adaptivecards.io/schemas/adaptive-card.json";
|
|
122
|
+
static AdaptiveCardType = "AdaptiveCard";
|
|
123
|
+
static TextBlockType = "TextBlock";
|
|
124
|
+
static ImageType = "Image";
|
|
125
|
+
static ContainerType = "Container";
|
|
126
|
+
static RegistrationIdPostfix = "REGISTRATION_ID";
|
|
127
|
+
static ResponseCodeFor20X = [
|
|
128
|
+
"200",
|
|
129
|
+
"201",
|
|
130
|
+
"202",
|
|
131
|
+
"203",
|
|
132
|
+
"204",
|
|
133
|
+
"205",
|
|
134
|
+
"206",
|
|
135
|
+
"207",
|
|
136
|
+
"208",
|
|
137
|
+
"226",
|
|
138
|
+
"2XX",
|
|
139
|
+
"default",
|
|
140
|
+
];
|
|
141
|
+
static AllOperationMethods = [
|
|
142
|
+
"get",
|
|
143
|
+
"post",
|
|
144
|
+
"put",
|
|
145
|
+
"delete",
|
|
146
|
+
"patch",
|
|
147
|
+
"head",
|
|
148
|
+
"options",
|
|
149
|
+
"trace",
|
|
150
|
+
];
|
|
151
|
+
// TODO: update after investigating the usage of these constants.
|
|
152
|
+
static WellknownResultNames = [
|
|
153
|
+
"result",
|
|
154
|
+
"data",
|
|
155
|
+
"items",
|
|
156
|
+
"root",
|
|
157
|
+
"matches",
|
|
158
|
+
"queries",
|
|
159
|
+
"list",
|
|
160
|
+
"output",
|
|
161
|
+
];
|
|
162
|
+
static WellknownTitleName = ["title", "name", "summary", "caption", "subject", "label"];
|
|
163
|
+
static WellknownSubtitleName = [
|
|
164
|
+
"subtitle",
|
|
165
|
+
"id",
|
|
166
|
+
"uid",
|
|
167
|
+
"description",
|
|
168
|
+
"desc",
|
|
169
|
+
"detail",
|
|
170
|
+
];
|
|
171
|
+
static WellknownImageName = [
|
|
172
|
+
"image",
|
|
173
|
+
"icon",
|
|
174
|
+
"avatar",
|
|
175
|
+
"picture",
|
|
176
|
+
"photo",
|
|
177
|
+
"logo",
|
|
178
|
+
"pic",
|
|
179
|
+
"thumbnail",
|
|
180
|
+
"img",
|
|
181
|
+
];
|
|
182
|
+
static ShortDescriptionMaxLens = 80;
|
|
183
|
+
static FullDescriptionMaxLens = 4000;
|
|
184
|
+
static CommandDescriptionMaxLens = 128;
|
|
185
|
+
static ParameterDescriptionMaxLens = 128;
|
|
186
|
+
static ConversationStarterMaxLens = 50;
|
|
187
|
+
static CommandTitleMaxLens = 32;
|
|
188
|
+
static ParameterTitleMaxLens = 32;
|
|
189
|
+
static SMERequiredParamsMaxNum = 5;
|
|
190
|
+
static FunctionDescriptionMaxLens = 100;
|
|
191
|
+
static DefaultPluginId = "plugin_1";
|
|
192
|
+
static PluginManifestSchema = "https://developer.microsoft.com/json-schemas/copilot/plugin/v2.2/schema.json";
|
|
193
|
+
}
|
|
223
194
|
|
|
224
|
-
// Copyright (c) Microsoft Corporation.
|
|
225
|
-
class Utils {
|
|
226
|
-
static isObjectSchema(schema) {
|
|
227
|
-
return schema.type === "object" || (!schema.type && !!schema.properties);
|
|
228
|
-
}
|
|
229
|
-
static containMultipleMediaTypes(bodyObject) {
|
|
230
|
-
return Object.keys(
|
|
231
|
-
}
|
|
232
|
-
static isBearerTokenAuth(authScheme) {
|
|
233
|
-
return authScheme.type === "http" && authScheme.scheme.toLowerCase() === "bearer";
|
|
234
|
-
}
|
|
235
|
-
static isAPIKeyAuth(authScheme) {
|
|
236
|
-
return authScheme.type === "apiKey";
|
|
237
|
-
}
|
|
238
|
-
static isAPIKeyAuthButNotInCookie(authScheme) {
|
|
239
|
-
return authScheme.type === "apiKey" && authScheme.in !== "cookie";
|
|
240
|
-
}
|
|
241
|
-
static isOAuthWithAuthCodeFlow(authScheme) {
|
|
242
|
-
return !!(authScheme.type === "oauth2" &&
|
|
243
|
-
authScheme.flows &&
|
|
244
|
-
authScheme.flows.authorizationCode);
|
|
245
|
-
}
|
|
246
|
-
static isNotSupportedAuth(authSchemeArray) {
|
|
247
|
-
if (authSchemeArray.length === 0) {
|
|
248
|
-
return false;
|
|
249
|
-
}
|
|
250
|
-
if (authSchemeArray.length > 0 && authSchemeArray.every((auths) => auths.length > 1)) {
|
|
251
|
-
return true;
|
|
252
|
-
}
|
|
253
|
-
for (const auths of authSchemeArray) {
|
|
254
|
-
if (auths.length === 1) {
|
|
255
|
-
if (Utils.isOAuthWithAuthCodeFlow(auths[0].authScheme) ||
|
|
256
|
-
Utils.isBearerTokenAuth(auths[0].authScheme) ||
|
|
257
|
-
Utils.isAPIKeyAuthButNotInCookie(auths[0].authScheme)) {
|
|
258
|
-
return false;
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
return true;
|
|
263
|
-
}
|
|
264
|
-
static getAuthArray(securities, spec) {
|
|
265
|
-
|
|
266
|
-
const
|
|
267
|
-
const
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
const
|
|
272
|
-
const
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
result
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
const
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
const
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
const
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
}
|
|
490
|
-
|
|
491
|
-
parameter
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
};
|
|
627
|
-
case "
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
};
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
return {
|
|
636
|
-
type: "http",
|
|
637
|
-
scheme: "bearer",
|
|
638
|
-
};
|
|
639
|
-
}
|
|
640
|
-
}
|
|
195
|
+
// Copyright (c) Microsoft Corporation.
|
|
196
|
+
class Utils {
|
|
197
|
+
static isObjectSchema(schema) {
|
|
198
|
+
return schema.type === "object" || (!schema.type && !!schema.properties);
|
|
199
|
+
}
|
|
200
|
+
static containMultipleMediaTypes(bodyObject) {
|
|
201
|
+
return Object.keys(bodyObject?.content || {}).length > 1;
|
|
202
|
+
}
|
|
203
|
+
static isBearerTokenAuth(authScheme) {
|
|
204
|
+
return authScheme.type === "http" && authScheme.scheme.toLowerCase() === "bearer";
|
|
205
|
+
}
|
|
206
|
+
static isAPIKeyAuth(authScheme) {
|
|
207
|
+
return authScheme.type === "apiKey";
|
|
208
|
+
}
|
|
209
|
+
static isAPIKeyAuthButNotInCookie(authScheme) {
|
|
210
|
+
return authScheme.type === "apiKey" && authScheme.in !== "cookie";
|
|
211
|
+
}
|
|
212
|
+
static isOAuthWithAuthCodeFlow(authScheme) {
|
|
213
|
+
return !!(authScheme.type === "oauth2" &&
|
|
214
|
+
authScheme.flows &&
|
|
215
|
+
authScheme.flows.authorizationCode);
|
|
216
|
+
}
|
|
217
|
+
static isNotSupportedAuth(authSchemeArray) {
|
|
218
|
+
if (authSchemeArray.length === 0) {
|
|
219
|
+
return false;
|
|
220
|
+
}
|
|
221
|
+
if (authSchemeArray.length > 0 && authSchemeArray.every((auths) => auths.length > 1)) {
|
|
222
|
+
return true;
|
|
223
|
+
}
|
|
224
|
+
for (const auths of authSchemeArray) {
|
|
225
|
+
if (auths.length === 1) {
|
|
226
|
+
if (Utils.isOAuthWithAuthCodeFlow(auths[0].authScheme) ||
|
|
227
|
+
Utils.isBearerTokenAuth(auths[0].authScheme) ||
|
|
228
|
+
Utils.isAPIKeyAuthButNotInCookie(auths[0].authScheme)) {
|
|
229
|
+
return false;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
return true;
|
|
234
|
+
}
|
|
235
|
+
static getAuthArray(securities, spec) {
|
|
236
|
+
const result = [];
|
|
237
|
+
const securitySchemas = spec.components?.securitySchemes;
|
|
238
|
+
const securitiesArr = securities ?? spec.security;
|
|
239
|
+
if (securitiesArr && securitySchemas) {
|
|
240
|
+
for (let i = 0; i < securitiesArr.length; i++) {
|
|
241
|
+
const security = securitiesArr[i];
|
|
242
|
+
const authArray = [];
|
|
243
|
+
for (const name in security) {
|
|
244
|
+
const auth = securitySchemas[name];
|
|
245
|
+
authArray.push({
|
|
246
|
+
authScheme: auth,
|
|
247
|
+
name: name,
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
if (authArray.length > 0) {
|
|
251
|
+
result.push(authArray);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
result.sort((a, b) => a[0].name.localeCompare(b[0].name));
|
|
256
|
+
return result;
|
|
257
|
+
}
|
|
258
|
+
static getAuthMap(spec) {
|
|
259
|
+
const authMap = {};
|
|
260
|
+
for (const url in spec.paths) {
|
|
261
|
+
for (const method in spec.paths[url]) {
|
|
262
|
+
const operation = spec.paths[url][method];
|
|
263
|
+
const authArray = Utils.getAuthArray(operation.security, spec);
|
|
264
|
+
if (authArray && authArray.length > 0) {
|
|
265
|
+
const currentAuth = authArray[0][0];
|
|
266
|
+
authMap[operation.operationId] = currentAuth;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
return authMap;
|
|
271
|
+
}
|
|
272
|
+
static getAuthInfo(spec) {
|
|
273
|
+
let authInfo = undefined;
|
|
274
|
+
for (const url in spec.paths) {
|
|
275
|
+
for (const method in spec.paths[url]) {
|
|
276
|
+
const operation = spec.paths[url][method];
|
|
277
|
+
const authArray = Utils.getAuthArray(operation.security, spec);
|
|
278
|
+
if (authArray && authArray.length > 0) {
|
|
279
|
+
const currentAuth = authArray[0][0];
|
|
280
|
+
if (!authInfo) {
|
|
281
|
+
authInfo = authArray[0][0];
|
|
282
|
+
}
|
|
283
|
+
else if (authInfo.name !== currentAuth.name) {
|
|
284
|
+
throw new SpecParserError(ConstantString.MultipleAuthNotSupported, ErrorType.MultipleAuthNotSupported);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
return authInfo;
|
|
290
|
+
}
|
|
291
|
+
static updateFirstLetter(str) {
|
|
292
|
+
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
293
|
+
}
|
|
294
|
+
static getResponseJson(operationObject, allowMultipleMediaType = false) {
|
|
295
|
+
let json = {};
|
|
296
|
+
let multipleMediaType = false;
|
|
297
|
+
for (const code of ConstantString.ResponseCodeFor20X) {
|
|
298
|
+
const responseObject = operationObject?.responses?.[code];
|
|
299
|
+
if (!responseObject) {
|
|
300
|
+
continue;
|
|
301
|
+
}
|
|
302
|
+
multipleMediaType = Utils.containMultipleMediaTypes(responseObject);
|
|
303
|
+
if (!allowMultipleMediaType && multipleMediaType) {
|
|
304
|
+
json = {};
|
|
305
|
+
continue;
|
|
306
|
+
}
|
|
307
|
+
const mediaObj = Utils.getJsonContentType(responseObject);
|
|
308
|
+
if (Object.keys(mediaObj).length > 0) {
|
|
309
|
+
json = mediaObj;
|
|
310
|
+
return { json, multipleMediaType };
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
return { json, multipleMediaType };
|
|
314
|
+
}
|
|
315
|
+
static getJsonContentType(responseObject) {
|
|
316
|
+
if (responseObject.content) {
|
|
317
|
+
for (const contentType of Object.keys(responseObject.content)) {
|
|
318
|
+
// json media type can also be "application/json; charset=utf-8"
|
|
319
|
+
if (contentType.indexOf("application/json") >= 0) {
|
|
320
|
+
return responseObject.content[contentType];
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
return {};
|
|
325
|
+
}
|
|
326
|
+
static convertPathToCamelCase(path) {
|
|
327
|
+
const pathSegments = path.split(/[./{]/);
|
|
328
|
+
const camelCaseSegments = pathSegments.map((segment) => {
|
|
329
|
+
segment = segment.replace(/}/g, "");
|
|
330
|
+
return segment.charAt(0).toUpperCase() + segment.slice(1);
|
|
331
|
+
});
|
|
332
|
+
const camelCasePath = camelCaseSegments.join("");
|
|
333
|
+
return camelCasePath;
|
|
334
|
+
}
|
|
335
|
+
static getUrlProtocol(urlString) {
|
|
336
|
+
try {
|
|
337
|
+
const url = new URL(urlString);
|
|
338
|
+
return url.protocol;
|
|
339
|
+
}
|
|
340
|
+
catch (err) {
|
|
341
|
+
return undefined;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
static resolveEnv(str) {
|
|
345
|
+
const placeHolderReg = /\${{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*}}/g;
|
|
346
|
+
let matches = placeHolderReg.exec(str);
|
|
347
|
+
let newStr = str;
|
|
348
|
+
while (matches != null) {
|
|
349
|
+
const envVar = matches[1];
|
|
350
|
+
const envVal = process.env[envVar];
|
|
351
|
+
if (!envVal) {
|
|
352
|
+
throw new Error(Utils.format(ConstantString.ResolveServerUrlFailed, envVar));
|
|
353
|
+
}
|
|
354
|
+
else {
|
|
355
|
+
newStr = newStr.replace(matches[0], envVal);
|
|
356
|
+
}
|
|
357
|
+
matches = placeHolderReg.exec(str);
|
|
358
|
+
}
|
|
359
|
+
return newStr;
|
|
360
|
+
}
|
|
361
|
+
static checkServerUrl(servers, allowHttp = false) {
|
|
362
|
+
const errors = [];
|
|
363
|
+
let serverUrl;
|
|
364
|
+
try {
|
|
365
|
+
serverUrl = Utils.resolveEnv(servers[0].url);
|
|
366
|
+
}
|
|
367
|
+
catch (err) {
|
|
368
|
+
errors.push({
|
|
369
|
+
type: ErrorType.ResolveServerUrlFailed,
|
|
370
|
+
content: err.message,
|
|
371
|
+
data: servers,
|
|
372
|
+
});
|
|
373
|
+
return errors;
|
|
374
|
+
}
|
|
375
|
+
const protocol = Utils.getUrlProtocol(serverUrl);
|
|
376
|
+
if (!protocol) {
|
|
377
|
+
// Relative server url is not supported
|
|
378
|
+
errors.push({
|
|
379
|
+
type: ErrorType.RelativeServerUrlNotSupported,
|
|
380
|
+
content: ConstantString.RelativeServerUrlNotSupported,
|
|
381
|
+
data: servers,
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
else if (protocol !== "https:" && !(protocol === "http:" && allowHttp)) {
|
|
385
|
+
const protocolString = protocol.slice(0, -1);
|
|
386
|
+
errors.push({
|
|
387
|
+
type: ErrorType.UrlProtocolNotSupported,
|
|
388
|
+
content: Utils.format(ConstantString.UrlProtocolNotSupported, protocol.slice(0, -1)),
|
|
389
|
+
data: protocolString,
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
return errors;
|
|
393
|
+
}
|
|
394
|
+
static validateServer(spec, options) {
|
|
395
|
+
const errors = [];
|
|
396
|
+
let hasTopLevelServers = false;
|
|
397
|
+
let hasPathLevelServers = false;
|
|
398
|
+
let hasOperationLevelServers = false;
|
|
399
|
+
const allowHttp = options.projectType === ProjectType.Copilot;
|
|
400
|
+
if (spec.servers && spec.servers.length >= 1) {
|
|
401
|
+
hasTopLevelServers = true;
|
|
402
|
+
// for multiple server, we only use the first url
|
|
403
|
+
const serverErrors = Utils.checkServerUrl(spec.servers, allowHttp);
|
|
404
|
+
errors.push(...serverErrors);
|
|
405
|
+
}
|
|
406
|
+
const paths = spec.paths;
|
|
407
|
+
for (const path in paths) {
|
|
408
|
+
const methods = paths[path];
|
|
409
|
+
if (methods?.servers && methods.servers.length >= 1) {
|
|
410
|
+
hasPathLevelServers = true;
|
|
411
|
+
const serverErrors = Utils.checkServerUrl(methods.servers, allowHttp);
|
|
412
|
+
errors.push(...serverErrors);
|
|
413
|
+
}
|
|
414
|
+
for (const method in methods) {
|
|
415
|
+
const operationObject = methods[method];
|
|
416
|
+
if (options.allowMethods?.includes(method) && operationObject) {
|
|
417
|
+
if (operationObject?.servers && operationObject.servers.length >= 1) {
|
|
418
|
+
hasOperationLevelServers = true;
|
|
419
|
+
const serverErrors = Utils.checkServerUrl(operationObject.servers, allowHttp);
|
|
420
|
+
errors.push(...serverErrors);
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
if (!hasTopLevelServers && !hasPathLevelServers && !hasOperationLevelServers) {
|
|
426
|
+
errors.push({
|
|
427
|
+
type: ErrorType.NoServerInformation,
|
|
428
|
+
content: ConstantString.NoServerInformation,
|
|
429
|
+
});
|
|
430
|
+
}
|
|
431
|
+
return errors;
|
|
432
|
+
}
|
|
433
|
+
static isWellKnownName(name, wellknownNameList) {
|
|
434
|
+
for (let i = 0; i < wellknownNameList.length; i++) {
|
|
435
|
+
name = name.replace(/_/g, "").replace(/-/g, "");
|
|
436
|
+
if (name.toLowerCase().includes(wellknownNameList[i])) {
|
|
437
|
+
return true;
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
return false;
|
|
441
|
+
}
|
|
442
|
+
static generateParametersFromSchema(schema, name, allowMultipleParameters, isRequired = false) {
|
|
443
|
+
const requiredParams = [];
|
|
444
|
+
const optionalParams = [];
|
|
445
|
+
if (schema.type === "string" ||
|
|
446
|
+
schema.type === "integer" ||
|
|
447
|
+
schema.type === "boolean" ||
|
|
448
|
+
schema.type === "number") {
|
|
449
|
+
const parameter = {
|
|
450
|
+
name: name,
|
|
451
|
+
title: Utils.updateFirstLetter(name).slice(0, ConstantString.ParameterTitleMaxLens),
|
|
452
|
+
description: (schema.description ?? "").slice(0, ConstantString.ParameterDescriptionMaxLens),
|
|
453
|
+
};
|
|
454
|
+
if (allowMultipleParameters) {
|
|
455
|
+
Utils.updateParameterWithInputType(schema, parameter);
|
|
456
|
+
}
|
|
457
|
+
if (isRequired && schema.default === undefined) {
|
|
458
|
+
parameter.isRequired = true;
|
|
459
|
+
requiredParams.push(parameter);
|
|
460
|
+
}
|
|
461
|
+
else {
|
|
462
|
+
optionalParams.push(parameter);
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
else if (Utils.isObjectSchema(schema)) {
|
|
466
|
+
const { properties } = schema;
|
|
467
|
+
for (const property in properties) {
|
|
468
|
+
let isRequired = false;
|
|
469
|
+
if (schema.required && schema.required?.indexOf(property) >= 0) {
|
|
470
|
+
isRequired = true;
|
|
471
|
+
}
|
|
472
|
+
const [requiredP, optionalP] = Utils.generateParametersFromSchema(properties[property], property, allowMultipleParameters, isRequired);
|
|
473
|
+
requiredParams.push(...requiredP);
|
|
474
|
+
optionalParams.push(...optionalP);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
return [requiredParams, optionalParams];
|
|
478
|
+
}
|
|
479
|
+
static updateParameterWithInputType(schema, param) {
|
|
480
|
+
if (schema.enum) {
|
|
481
|
+
param.inputType = "choiceset";
|
|
482
|
+
param.choices = [];
|
|
483
|
+
for (let i = 0; i < schema.enum.length; i++) {
|
|
484
|
+
param.choices.push({
|
|
485
|
+
title: schema.enum[i],
|
|
486
|
+
value: schema.enum[i],
|
|
487
|
+
});
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
else if (schema.type === "string") {
|
|
491
|
+
param.inputType = "text";
|
|
492
|
+
}
|
|
493
|
+
else if (schema.type === "integer" || schema.type === "number") {
|
|
494
|
+
param.inputType = "number";
|
|
495
|
+
}
|
|
496
|
+
else if (schema.type === "boolean") {
|
|
497
|
+
param.inputType = "toggle";
|
|
498
|
+
}
|
|
499
|
+
if (schema.default) {
|
|
500
|
+
param.value = schema.default;
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
static parseApiInfo(operationItem, options) {
|
|
504
|
+
const requiredParams = [];
|
|
505
|
+
const optionalParams = [];
|
|
506
|
+
const paramObject = operationItem.parameters;
|
|
507
|
+
if (paramObject) {
|
|
508
|
+
paramObject.forEach((param) => {
|
|
509
|
+
const parameter = {
|
|
510
|
+
name: param.name,
|
|
511
|
+
title: Utils.updateFirstLetter(param.name).slice(0, ConstantString.ParameterTitleMaxLens),
|
|
512
|
+
description: (param.description ?? "").slice(0, ConstantString.ParameterDescriptionMaxLens),
|
|
513
|
+
};
|
|
514
|
+
const schema = param.schema;
|
|
515
|
+
if (options.allowMultipleParameters && schema) {
|
|
516
|
+
Utils.updateParameterWithInputType(schema, parameter);
|
|
517
|
+
}
|
|
518
|
+
if (param.in !== "header" && param.in !== "cookie") {
|
|
519
|
+
if (param.required && schema?.default === undefined) {
|
|
520
|
+
parameter.isRequired = true;
|
|
521
|
+
requiredParams.push(parameter);
|
|
522
|
+
}
|
|
523
|
+
else {
|
|
524
|
+
optionalParams.push(parameter);
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
});
|
|
528
|
+
}
|
|
529
|
+
if (operationItem.requestBody) {
|
|
530
|
+
const requestBody = operationItem.requestBody;
|
|
531
|
+
const requestJson = requestBody.content["application/json"];
|
|
532
|
+
if (Object.keys(requestJson).length !== 0) {
|
|
533
|
+
const schema = requestJson.schema;
|
|
534
|
+
const [requiredP, optionalP] = Utils.generateParametersFromSchema(schema, "requestBody", !!options.allowMultipleParameters, requestBody.required);
|
|
535
|
+
requiredParams.push(...requiredP);
|
|
536
|
+
optionalParams.push(...optionalP);
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
const operationId = operationItem.operationId;
|
|
540
|
+
const parameters = [...requiredParams, ...optionalParams];
|
|
541
|
+
const command = {
|
|
542
|
+
context: ["compose"],
|
|
543
|
+
type: "query",
|
|
544
|
+
title: (operationItem.summary ?? "").slice(0, ConstantString.CommandTitleMaxLens),
|
|
545
|
+
id: operationId,
|
|
546
|
+
parameters: parameters,
|
|
547
|
+
description: (operationItem.description ?? "").slice(0, ConstantString.CommandDescriptionMaxLens),
|
|
548
|
+
};
|
|
549
|
+
return command;
|
|
550
|
+
}
|
|
551
|
+
static format(str, ...args) {
|
|
552
|
+
let index = 0;
|
|
553
|
+
return str.replace(/%s/g, () => {
|
|
554
|
+
const arg = args[index++];
|
|
555
|
+
return arg !== undefined ? arg : "";
|
|
556
|
+
});
|
|
557
|
+
}
|
|
558
|
+
static getSafeRegistrationIdEnvName(authName) {
|
|
559
|
+
if (!authName) {
|
|
560
|
+
return "";
|
|
561
|
+
}
|
|
562
|
+
let safeRegistrationIdEnvName = authName.toUpperCase().replace(/[^A-Z0-9_]/g, "_");
|
|
563
|
+
if (!safeRegistrationIdEnvName.match(/^[A-Z]/)) {
|
|
564
|
+
safeRegistrationIdEnvName = "PREFIX_" + safeRegistrationIdEnvName;
|
|
565
|
+
}
|
|
566
|
+
return safeRegistrationIdEnvName;
|
|
567
|
+
}
|
|
568
|
+
static getServerObject(spec, method, path) {
|
|
569
|
+
const pathObj = spec.paths[path];
|
|
570
|
+
const operationObject = pathObj[method];
|
|
571
|
+
const rootServer = spec.servers && spec.servers[0];
|
|
572
|
+
const methodServer = spec.paths[path].servers && spec.paths[path].servers[0];
|
|
573
|
+
const operationServer = operationObject.servers && operationObject.servers[0];
|
|
574
|
+
const serverUrl = operationServer || methodServer || rootServer;
|
|
575
|
+
return serverUrl;
|
|
576
|
+
}
|
|
577
|
+
static getAuthSchemaObject(authType, authParameters) {
|
|
578
|
+
switch (authType) {
|
|
579
|
+
case "oauth":
|
|
580
|
+
case "microsoft-entra":
|
|
581
|
+
return {
|
|
582
|
+
type: "oauth2",
|
|
583
|
+
flows: {
|
|
584
|
+
authorizationCode: {
|
|
585
|
+
authorizationUrl: authParameters.authorizationUrl,
|
|
586
|
+
tokenUrl: authParameters.tokenUrl,
|
|
587
|
+
refreshUrl: authParameters.refreshUrl,
|
|
588
|
+
scopes: authParameters.scopes,
|
|
589
|
+
},
|
|
590
|
+
},
|
|
591
|
+
};
|
|
592
|
+
case "api-key":
|
|
593
|
+
return {
|
|
594
|
+
type: "apiKey",
|
|
595
|
+
in: authParameters.in,
|
|
596
|
+
name: authParameters.name,
|
|
597
|
+
};
|
|
598
|
+
case "bearer-token":
|
|
599
|
+
default:
|
|
600
|
+
return {
|
|
601
|
+
type: "http",
|
|
602
|
+
scheme: "bearer",
|
|
603
|
+
};
|
|
604
|
+
}
|
|
605
|
+
}
|
|
641
606
|
}
|
|
642
607
|
|
|
643
|
-
// Copyright (c) Microsoft Corporation.
|
|
644
|
-
class Validator {
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
const
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
const
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
const
|
|
702
|
-
const
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
const
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
const
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
result.
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
result.
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
result.
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
const
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
const
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
const
|
|
786
|
-
const
|
|
787
|
-
const
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
this.options.
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
(this.options.
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
608
|
+
// Copyright (c) Microsoft Corporation.
|
|
609
|
+
class Validator {
|
|
610
|
+
projectType;
|
|
611
|
+
spec;
|
|
612
|
+
options;
|
|
613
|
+
apiMap;
|
|
614
|
+
hasCircularReference = false;
|
|
615
|
+
checkCircularReference() {
|
|
616
|
+
try {
|
|
617
|
+
JSON.stringify(this.spec);
|
|
618
|
+
}
|
|
619
|
+
catch (e) {
|
|
620
|
+
if (e.message.includes("Converting circular structure to JSON")) {
|
|
621
|
+
this.hasCircularReference = true;
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
listAPIs() {
|
|
626
|
+
if (this.apiMap) {
|
|
627
|
+
return this.apiMap;
|
|
628
|
+
}
|
|
629
|
+
const paths = this.spec.paths;
|
|
630
|
+
const result = {};
|
|
631
|
+
for (const path in paths) {
|
|
632
|
+
const methods = paths[path];
|
|
633
|
+
for (const method in methods) {
|
|
634
|
+
const operationObject = methods[method];
|
|
635
|
+
if (this.options.allowMethods?.includes(method) && operationObject) {
|
|
636
|
+
const validateResult = this.validateAPI(method, path);
|
|
637
|
+
result[`${method.toUpperCase()} ${path}`] = {
|
|
638
|
+
operation: operationObject,
|
|
639
|
+
isValid: validateResult.isValid,
|
|
640
|
+
reason: validateResult.reason,
|
|
641
|
+
};
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
this.apiMap = result;
|
|
646
|
+
return result;
|
|
647
|
+
}
|
|
648
|
+
validateSpecVersion() {
|
|
649
|
+
const result = { errors: [], warnings: [] };
|
|
650
|
+
if (this.spec.openapi >= "3.1.0") {
|
|
651
|
+
result.errors.push({
|
|
652
|
+
type: ErrorType.SpecVersionNotSupported,
|
|
653
|
+
content: Utils.format(ConstantString.SpecVersionNotSupported, this.spec.openapi),
|
|
654
|
+
data: this.spec.openapi,
|
|
655
|
+
});
|
|
656
|
+
}
|
|
657
|
+
return result;
|
|
658
|
+
}
|
|
659
|
+
validateSpecServer() {
|
|
660
|
+
const result = { errors: [], warnings: [] };
|
|
661
|
+
const serverErrors = Utils.validateServer(this.spec, this.options);
|
|
662
|
+
result.errors.push(...serverErrors);
|
|
663
|
+
return result;
|
|
664
|
+
}
|
|
665
|
+
validateSpecNoSupportAPI() {
|
|
666
|
+
const result = { errors: [], warnings: [] };
|
|
667
|
+
const apiMap = this.listAPIs();
|
|
668
|
+
const validAPIs = Object.entries(apiMap).filter(([, value]) => value.isValid);
|
|
669
|
+
if (validAPIs.length === 0) {
|
|
670
|
+
const data = [];
|
|
671
|
+
for (const key in apiMap) {
|
|
672
|
+
const { reason } = apiMap[key];
|
|
673
|
+
const apiInvalidReason = { api: key, reason: reason };
|
|
674
|
+
data.push(apiInvalidReason);
|
|
675
|
+
}
|
|
676
|
+
result.errors.push({
|
|
677
|
+
type: ErrorType.NoSupportedApi,
|
|
678
|
+
content: ConstantString.NoSupportedApi,
|
|
679
|
+
data,
|
|
680
|
+
});
|
|
681
|
+
}
|
|
682
|
+
return result;
|
|
683
|
+
}
|
|
684
|
+
validateSpecOperationId() {
|
|
685
|
+
const result = { errors: [], warnings: [] };
|
|
686
|
+
const apiMap = this.listAPIs();
|
|
687
|
+
// OperationId missing
|
|
688
|
+
const apisMissingOperationId = [];
|
|
689
|
+
for (const key in apiMap) {
|
|
690
|
+
const { operation } = apiMap[key];
|
|
691
|
+
if (!operation.operationId) {
|
|
692
|
+
apisMissingOperationId.push(key);
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
if (apisMissingOperationId.length > 0) {
|
|
696
|
+
result.warnings.push({
|
|
697
|
+
type: WarningType.OperationIdMissing,
|
|
698
|
+
content: Utils.format(ConstantString.MissingOperationId, apisMissingOperationId.join(", ")),
|
|
699
|
+
data: apisMissingOperationId,
|
|
700
|
+
});
|
|
701
|
+
}
|
|
702
|
+
return result;
|
|
703
|
+
}
|
|
704
|
+
validateMethodAndPath(method, path) {
|
|
705
|
+
const result = { isValid: true, reason: [] };
|
|
706
|
+
if (this.options.allowMethods && !this.options.allowMethods.includes(method)) {
|
|
707
|
+
result.isValid = false;
|
|
708
|
+
result.reason.push(ErrorType.MethodNotAllowed);
|
|
709
|
+
return result;
|
|
710
|
+
}
|
|
711
|
+
const pathObj = this.spec.paths[path];
|
|
712
|
+
if (!pathObj || !pathObj[method]) {
|
|
713
|
+
result.isValid = false;
|
|
714
|
+
result.reason.push(ErrorType.UrlPathNotExist);
|
|
715
|
+
return result;
|
|
716
|
+
}
|
|
717
|
+
return result;
|
|
718
|
+
}
|
|
719
|
+
validateCircularReference(method, path) {
|
|
720
|
+
const result = { isValid: true, reason: [] };
|
|
721
|
+
if (this.hasCircularReference) {
|
|
722
|
+
const operationObject = this.spec.paths[path][method];
|
|
723
|
+
try {
|
|
724
|
+
JSON.stringify(operationObject);
|
|
725
|
+
}
|
|
726
|
+
catch (e) {
|
|
727
|
+
if (e.message.includes("Converting circular structure to JSON")) {
|
|
728
|
+
result.isValid = false;
|
|
729
|
+
result.reason.push(ErrorType.CircularReferenceNotSupported);
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
return result;
|
|
734
|
+
}
|
|
735
|
+
validateServer(method, path) {
|
|
736
|
+
const result = { isValid: true, reason: [] };
|
|
737
|
+
const serverObj = Utils.getServerObject(this.spec, method, path);
|
|
738
|
+
if (!serverObj) {
|
|
739
|
+
// should contain server URL
|
|
740
|
+
result.reason.push(ErrorType.NoServerInformation);
|
|
741
|
+
}
|
|
742
|
+
else {
|
|
743
|
+
const allowHttp = this.projectType === ProjectType.Copilot;
|
|
744
|
+
const serverValidateResult = Utils.checkServerUrl([serverObj], allowHttp);
|
|
745
|
+
result.reason.push(...serverValidateResult.map((item) => item.type));
|
|
746
|
+
}
|
|
747
|
+
return result;
|
|
748
|
+
}
|
|
749
|
+
validateAuth(method, path) {
|
|
750
|
+
const pathObj = this.spec.paths[path];
|
|
751
|
+
const operationObject = pathObj[method];
|
|
752
|
+
const securities = operationObject.security;
|
|
753
|
+
const authSchemeArray = Utils.getAuthArray(securities, this.spec);
|
|
754
|
+
if (authSchemeArray.length === 0) {
|
|
755
|
+
return { isValid: true, reason: [] };
|
|
756
|
+
}
|
|
757
|
+
if (this.options.allowAPIKeyAuth ||
|
|
758
|
+
this.options.allowOauth2 ||
|
|
759
|
+
this.options.allowBearerTokenAuth) {
|
|
760
|
+
// Currently we don't support multiple auth in one operation
|
|
761
|
+
if (authSchemeArray.length > 0 && authSchemeArray.every((auths) => auths.length > 1)) {
|
|
762
|
+
return {
|
|
763
|
+
isValid: false,
|
|
764
|
+
reason: [ErrorType.MultipleAuthNotSupported],
|
|
765
|
+
};
|
|
766
|
+
}
|
|
767
|
+
if (this.projectType === ProjectType.Copilot) {
|
|
768
|
+
return { isValid: true, reason: [] };
|
|
769
|
+
}
|
|
770
|
+
for (const auths of authSchemeArray) {
|
|
771
|
+
if (auths.length === 1) {
|
|
772
|
+
if ((this.options.allowAPIKeyAuth && Utils.isAPIKeyAuth(auths[0].authScheme)) ||
|
|
773
|
+
(this.options.allowOauth2 && Utils.isOAuthWithAuthCodeFlow(auths[0].authScheme)) ||
|
|
774
|
+
(this.options.allowBearerTokenAuth && Utils.isBearerTokenAuth(auths[0].authScheme))) {
|
|
775
|
+
return { isValid: true, reason: [] };
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
return { isValid: false, reason: [ErrorType.AuthTypeIsNotSupported] };
|
|
781
|
+
}
|
|
816
782
|
}
|
|
817
783
|
|
|
818
|
-
// Copyright (c) Microsoft Corporation.
|
|
819
|
-
class CopilotValidator extends Validator {
|
|
820
|
-
constructor(spec, options) {
|
|
821
|
-
super();
|
|
822
|
-
this.projectType = ProjectType.Copilot;
|
|
823
|
-
this.options = options;
|
|
824
|
-
this.spec = spec;
|
|
825
|
-
}
|
|
826
|
-
validateSpec() {
|
|
827
|
-
const result = { errors: [], warnings: [] };
|
|
828
|
-
// validate spec version
|
|
829
|
-
let validationResult = this.validateSpecVersion();
|
|
830
|
-
result.errors.push(...validationResult.errors);
|
|
831
|
-
// validate spec server
|
|
832
|
-
validationResult = this.validateSpecServer();
|
|
833
|
-
result.errors.push(...validationResult.errors);
|
|
834
|
-
// validate no supported API
|
|
835
|
-
validationResult = this.validateSpecNoSupportAPI();
|
|
836
|
-
result.errors.push(...validationResult.errors);
|
|
837
|
-
// validate operationId missing
|
|
838
|
-
validationResult = this.validateSpecOperationId();
|
|
839
|
-
result.warnings.push(...validationResult.warnings);
|
|
840
|
-
return result;
|
|
841
|
-
}
|
|
842
|
-
validateAPI(method, path) {
|
|
843
|
-
const result = { isValid: true, reason: [] };
|
|
844
|
-
method = method.toLocaleLowerCase();
|
|
845
|
-
// validate method and path
|
|
846
|
-
const methodAndPathResult = this.validateMethodAndPath(method, path);
|
|
847
|
-
if (!methodAndPathResult.isValid) {
|
|
848
|
-
return methodAndPathResult;
|
|
849
|
-
}
|
|
850
|
-
const operationObject = this.spec.paths[path][method];
|
|
851
|
-
// validate auth
|
|
852
|
-
const authCheckResult = this.validateAuth(method, path);
|
|
853
|
-
result.reason.push(...authCheckResult.reason);
|
|
854
|
-
// validate operationId
|
|
855
|
-
if (!this.options.allowMissingId && !operationObject.operationId) {
|
|
856
|
-
result.reason.push(ErrorType.MissingOperationId);
|
|
857
|
-
}
|
|
858
|
-
// validate server
|
|
859
|
-
const validateServerResult = this.validateServer(method, path);
|
|
860
|
-
result.reason.push(...validateServerResult.reason);
|
|
861
|
-
if (result.reason.length > 0) {
|
|
862
|
-
result.isValid = false;
|
|
863
|
-
}
|
|
864
|
-
return result;
|
|
865
|
-
}
|
|
784
|
+
// Copyright (c) Microsoft Corporation.
|
|
785
|
+
class CopilotValidator extends Validator {
|
|
786
|
+
constructor(spec, options) {
|
|
787
|
+
super();
|
|
788
|
+
this.projectType = ProjectType.Copilot;
|
|
789
|
+
this.options = options;
|
|
790
|
+
this.spec = spec;
|
|
791
|
+
}
|
|
792
|
+
validateSpec() {
|
|
793
|
+
const result = { errors: [], warnings: [] };
|
|
794
|
+
// validate spec version
|
|
795
|
+
let validationResult = this.validateSpecVersion();
|
|
796
|
+
result.errors.push(...validationResult.errors);
|
|
797
|
+
// validate spec server
|
|
798
|
+
validationResult = this.validateSpecServer();
|
|
799
|
+
result.errors.push(...validationResult.errors);
|
|
800
|
+
// validate no supported API
|
|
801
|
+
validationResult = this.validateSpecNoSupportAPI();
|
|
802
|
+
result.errors.push(...validationResult.errors);
|
|
803
|
+
// validate operationId missing
|
|
804
|
+
validationResult = this.validateSpecOperationId();
|
|
805
|
+
result.warnings.push(...validationResult.warnings);
|
|
806
|
+
return result;
|
|
807
|
+
}
|
|
808
|
+
validateAPI(method, path) {
|
|
809
|
+
const result = { isValid: true, reason: [] };
|
|
810
|
+
method = method.toLocaleLowerCase();
|
|
811
|
+
// validate method and path
|
|
812
|
+
const methodAndPathResult = this.validateMethodAndPath(method, path);
|
|
813
|
+
if (!methodAndPathResult.isValid) {
|
|
814
|
+
return methodAndPathResult;
|
|
815
|
+
}
|
|
816
|
+
const operationObject = this.spec.paths[path][method];
|
|
817
|
+
// validate auth
|
|
818
|
+
const authCheckResult = this.validateAuth(method, path);
|
|
819
|
+
result.reason.push(...authCheckResult.reason);
|
|
820
|
+
// validate operationId
|
|
821
|
+
if (!this.options.allowMissingId && !operationObject.operationId) {
|
|
822
|
+
result.reason.push(ErrorType.MissingOperationId);
|
|
823
|
+
}
|
|
824
|
+
// validate server
|
|
825
|
+
const validateServerResult = this.validateServer(method, path);
|
|
826
|
+
result.reason.push(...validateServerResult.reason);
|
|
827
|
+
if (result.reason.length > 0) {
|
|
828
|
+
result.isValid = false;
|
|
829
|
+
}
|
|
830
|
+
return result;
|
|
831
|
+
}
|
|
866
832
|
}
|
|
867
833
|
|
|
868
|
-
// Copyright (c) Microsoft Corporation.
|
|
869
|
-
class SMEValidator extends Validator {
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
this.
|
|
874
|
-
this.
|
|
875
|
-
this.
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
const
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
const
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
const
|
|
955
|
-
const
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
const paramResult = {
|
|
969
|
-
requiredNum: 0,
|
|
970
|
-
optionalNum: 0,
|
|
971
|
-
isValid: true,
|
|
972
|
-
reason: [],
|
|
973
|
-
};
|
|
974
|
-
if (Object.keys(schema).length === 0) {
|
|
975
|
-
return paramResult;
|
|
976
|
-
}
|
|
977
|
-
const isRequiredWithoutDefault = isRequired && schema.default === undefined;
|
|
978
|
-
const isCopilot = this.projectType === ProjectType.Copilot;
|
|
979
|
-
if (schema.type === "string" ||
|
|
980
|
-
schema.type === "integer" ||
|
|
981
|
-
schema.type === "boolean" ||
|
|
982
|
-
schema.type === "number") {
|
|
983
|
-
if (isRequiredWithoutDefault) {
|
|
984
|
-
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
985
|
-
}
|
|
986
|
-
else {
|
|
987
|
-
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
988
|
-
}
|
|
989
|
-
}
|
|
990
|
-
else if (Utils.isObjectSchema(schema)) {
|
|
991
|
-
const { properties } = schema;
|
|
992
|
-
for (const property in properties) {
|
|
993
|
-
let isRequired = false;
|
|
994
|
-
if (schema.required &&
|
|
995
|
-
isRequired = true;
|
|
996
|
-
}
|
|
997
|
-
const result = this.checkPostBodySchema(properties[property], isRequired);
|
|
998
|
-
paramResult.requiredNum += result.requiredNum;
|
|
999
|
-
paramResult.optionalNum += result.optionalNum;
|
|
1000
|
-
paramResult.isValid = paramResult.isValid && result.isValid;
|
|
1001
|
-
paramResult.reason.push(...result.reason);
|
|
1002
|
-
}
|
|
1003
|
-
}
|
|
1004
|
-
else {
|
|
1005
|
-
if (isRequiredWithoutDefault && !isCopilot) {
|
|
1006
|
-
paramResult.isValid = false;
|
|
1007
|
-
paramResult.reason.push(ErrorType.PostBodyContainsRequiredUnsupportedSchema);
|
|
1008
|
-
}
|
|
1009
|
-
}
|
|
1010
|
-
return paramResult;
|
|
1011
|
-
}
|
|
1012
|
-
checkParamSchema(paramObject) {
|
|
1013
|
-
const paramResult = {
|
|
1014
|
-
requiredNum: 0,
|
|
1015
|
-
optionalNum: 0,
|
|
1016
|
-
isValid: true,
|
|
1017
|
-
reason: [],
|
|
1018
|
-
};
|
|
1019
|
-
if (!paramObject) {
|
|
1020
|
-
return paramResult;
|
|
1021
|
-
}
|
|
1022
|
-
for (let i = 0; i < paramObject.length; i++) {
|
|
1023
|
-
const param = paramObject[i];
|
|
1024
|
-
const schema = param.schema;
|
|
1025
|
-
const isRequiredWithoutDefault = param.required && schema.default === undefined;
|
|
1026
|
-
if (param.in === "header" || param.in === "cookie") {
|
|
1027
|
-
if (isRequiredWithoutDefault) {
|
|
1028
|
-
paramResult.isValid = false;
|
|
1029
|
-
paramResult.reason.push(ErrorType.ParamsContainRequiredUnsupportedSchema);
|
|
1030
|
-
}
|
|
1031
|
-
continue;
|
|
1032
|
-
}
|
|
1033
|
-
if (schema.type !== "boolean" &&
|
|
1034
|
-
schema.type !== "string" &&
|
|
1035
|
-
schema.type !== "number" &&
|
|
1036
|
-
schema.type !== "integer") {
|
|
1037
|
-
if (isRequiredWithoutDefault) {
|
|
1038
|
-
paramResult.isValid = false;
|
|
1039
|
-
paramResult.reason.push(ErrorType.ParamsContainRequiredUnsupportedSchema);
|
|
1040
|
-
}
|
|
1041
|
-
continue;
|
|
1042
|
-
}
|
|
1043
|
-
if (param.in === "query" || param.in === "path") {
|
|
1044
|
-
if (isRequiredWithoutDefault) {
|
|
1045
|
-
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
1046
|
-
}
|
|
1047
|
-
else {
|
|
1048
|
-
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
1049
|
-
}
|
|
1050
|
-
}
|
|
1051
|
-
}
|
|
1052
|
-
return paramResult;
|
|
1053
|
-
}
|
|
1054
|
-
validateParamCount(postBodyResult, paramResult) {
|
|
1055
|
-
const result = { isValid: true, reason: [] };
|
|
1056
|
-
const totalRequiredParams = postBodyResult.requiredNum + paramResult.requiredNum;
|
|
1057
|
-
const totalParams = totalRequiredParams + postBodyResult.optionalNum + paramResult.optionalNum;
|
|
1058
|
-
if (totalRequiredParams > 1) {
|
|
1059
|
-
if (!this.options.allowMultipleParameters ||
|
|
1060
|
-
totalRequiredParams > SMEValidator.SMERequiredParamsMaxNum) {
|
|
1061
|
-
result.reason.push(ErrorType.ExceededRequiredParamsLimit);
|
|
1062
|
-
}
|
|
1063
|
-
}
|
|
1064
|
-
else if (totalParams === 0) {
|
|
1065
|
-
result.reason.push(ErrorType.NoParameter);
|
|
1066
|
-
}
|
|
1067
|
-
return result;
|
|
1068
|
-
}
|
|
1069
|
-
}
|
|
1070
|
-
SMEValidator.SMERequiredParamsMaxNum = 5;
|
|
834
|
+
// Copyright (c) Microsoft Corporation.
|
|
835
|
+
class SMEValidator extends Validator {
|
|
836
|
+
static SMERequiredParamsMaxNum = 5;
|
|
837
|
+
constructor(spec, options) {
|
|
838
|
+
super();
|
|
839
|
+
this.projectType = ProjectType.SME;
|
|
840
|
+
this.options = options;
|
|
841
|
+
this.spec = spec;
|
|
842
|
+
this.checkCircularReference();
|
|
843
|
+
}
|
|
844
|
+
validateSpec() {
|
|
845
|
+
const result = { errors: [], warnings: [] };
|
|
846
|
+
// validate spec version
|
|
847
|
+
let validationResult = this.validateSpecVersion();
|
|
848
|
+
result.errors.push(...validationResult.errors);
|
|
849
|
+
// validate spec server
|
|
850
|
+
validationResult = this.validateSpecServer();
|
|
851
|
+
result.errors.push(...validationResult.errors);
|
|
852
|
+
// validate no supported API
|
|
853
|
+
validationResult = this.validateSpecNoSupportAPI();
|
|
854
|
+
result.errors.push(...validationResult.errors);
|
|
855
|
+
// validate operationId missing
|
|
856
|
+
if (this.options.allowMissingId) {
|
|
857
|
+
validationResult = this.validateSpecOperationId();
|
|
858
|
+
result.warnings.push(...validationResult.warnings);
|
|
859
|
+
}
|
|
860
|
+
return result;
|
|
861
|
+
}
|
|
862
|
+
validateAPI(method, path) {
|
|
863
|
+
const result = { isValid: true, reason: [] };
|
|
864
|
+
method = method.toLocaleLowerCase();
|
|
865
|
+
// validate method and path
|
|
866
|
+
const methodAndPathResult = this.validateMethodAndPath(method, path);
|
|
867
|
+
if (!methodAndPathResult.isValid) {
|
|
868
|
+
return methodAndPathResult;
|
|
869
|
+
}
|
|
870
|
+
const circularReferenceResult = this.validateCircularReference(method, path);
|
|
871
|
+
if (!circularReferenceResult.isValid) {
|
|
872
|
+
return circularReferenceResult;
|
|
873
|
+
}
|
|
874
|
+
const operationObject = this.spec.paths[path][method];
|
|
875
|
+
// validate auth
|
|
876
|
+
const authCheckResult = this.validateAuth(method, path);
|
|
877
|
+
result.reason.push(...authCheckResult.reason);
|
|
878
|
+
// validate operationId
|
|
879
|
+
if (!this.options.allowMissingId && !operationObject.operationId) {
|
|
880
|
+
result.reason.push(ErrorType.MissingOperationId);
|
|
881
|
+
}
|
|
882
|
+
// validate server
|
|
883
|
+
const validateServerResult = this.validateServer(method, path);
|
|
884
|
+
result.reason.push(...validateServerResult.reason);
|
|
885
|
+
// validate response
|
|
886
|
+
const validateResponseResult = this.validateResponse(method, path);
|
|
887
|
+
result.reason.push(...validateResponseResult.reason);
|
|
888
|
+
let postBodyResult = {
|
|
889
|
+
requiredNum: 0,
|
|
890
|
+
optionalNum: 0,
|
|
891
|
+
isValid: true,
|
|
892
|
+
reason: [],
|
|
893
|
+
};
|
|
894
|
+
// validate requestBody
|
|
895
|
+
const requestBody = operationObject.requestBody;
|
|
896
|
+
const requestJsonBody = requestBody?.content["application/json"];
|
|
897
|
+
if (Utils.containMultipleMediaTypes(requestBody)) {
|
|
898
|
+
result.reason.push(ErrorType.PostBodyContainMultipleMediaTypes);
|
|
899
|
+
}
|
|
900
|
+
if (requestJsonBody) {
|
|
901
|
+
const requestBodySchema = requestJsonBody.schema;
|
|
902
|
+
postBodyResult = this.checkPostBodySchema(requestBodySchema, requestBody.required);
|
|
903
|
+
result.reason.push(...postBodyResult.reason);
|
|
904
|
+
}
|
|
905
|
+
// validate parameters
|
|
906
|
+
const paramObject = operationObject.parameters;
|
|
907
|
+
const paramResult = this.checkParamSchema(paramObject);
|
|
908
|
+
result.reason.push(...paramResult.reason);
|
|
909
|
+
// validate total parameters count
|
|
910
|
+
if (paramResult.isValid && postBodyResult.isValid) {
|
|
911
|
+
const paramCountResult = this.validateParamCount(postBodyResult, paramResult);
|
|
912
|
+
result.reason.push(...paramCountResult.reason);
|
|
913
|
+
}
|
|
914
|
+
if (result.reason.length > 0) {
|
|
915
|
+
result.isValid = false;
|
|
916
|
+
}
|
|
917
|
+
return result;
|
|
918
|
+
}
|
|
919
|
+
validateResponse(method, path) {
|
|
920
|
+
const result = { isValid: true, reason: [] };
|
|
921
|
+
const operationObject = this.spec.paths[path][method];
|
|
922
|
+
const { json, multipleMediaType } = Utils.getResponseJson(operationObject);
|
|
923
|
+
// only support response body only contains “application/json” content type
|
|
924
|
+
if (multipleMediaType) {
|
|
925
|
+
result.reason.push(ErrorType.ResponseContainMultipleMediaTypes);
|
|
926
|
+
}
|
|
927
|
+
else if (Object.keys(json).length === 0) {
|
|
928
|
+
// response body should not be empty
|
|
929
|
+
result.reason.push(ErrorType.ResponseJsonIsEmpty);
|
|
930
|
+
}
|
|
931
|
+
return result;
|
|
932
|
+
}
|
|
933
|
+
checkPostBodySchema(schema, isRequired = false) {
|
|
934
|
+
const paramResult = {
|
|
935
|
+
requiredNum: 0,
|
|
936
|
+
optionalNum: 0,
|
|
937
|
+
isValid: true,
|
|
938
|
+
reason: [],
|
|
939
|
+
};
|
|
940
|
+
if (Object.keys(schema).length === 0) {
|
|
941
|
+
return paramResult;
|
|
942
|
+
}
|
|
943
|
+
const isRequiredWithoutDefault = isRequired && schema.default === undefined;
|
|
944
|
+
const isCopilot = this.projectType === ProjectType.Copilot;
|
|
945
|
+
if (schema.type === "string" ||
|
|
946
|
+
schema.type === "integer" ||
|
|
947
|
+
schema.type === "boolean" ||
|
|
948
|
+
schema.type === "number") {
|
|
949
|
+
if (isRequiredWithoutDefault) {
|
|
950
|
+
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
951
|
+
}
|
|
952
|
+
else {
|
|
953
|
+
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
else if (Utils.isObjectSchema(schema)) {
|
|
957
|
+
const { properties } = schema;
|
|
958
|
+
for (const property in properties) {
|
|
959
|
+
let isRequired = false;
|
|
960
|
+
if (schema.required && schema.required?.indexOf(property) >= 0) {
|
|
961
|
+
isRequired = true;
|
|
962
|
+
}
|
|
963
|
+
const result = this.checkPostBodySchema(properties[property], isRequired);
|
|
964
|
+
paramResult.requiredNum += result.requiredNum;
|
|
965
|
+
paramResult.optionalNum += result.optionalNum;
|
|
966
|
+
paramResult.isValid = paramResult.isValid && result.isValid;
|
|
967
|
+
paramResult.reason.push(...result.reason);
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
else {
|
|
971
|
+
if (isRequiredWithoutDefault && !isCopilot) {
|
|
972
|
+
paramResult.isValid = false;
|
|
973
|
+
paramResult.reason.push(ErrorType.PostBodyContainsRequiredUnsupportedSchema);
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
return paramResult;
|
|
977
|
+
}
|
|
978
|
+
checkParamSchema(paramObject) {
|
|
979
|
+
const paramResult = {
|
|
980
|
+
requiredNum: 0,
|
|
981
|
+
optionalNum: 0,
|
|
982
|
+
isValid: true,
|
|
983
|
+
reason: [],
|
|
984
|
+
};
|
|
985
|
+
if (!paramObject) {
|
|
986
|
+
return paramResult;
|
|
987
|
+
}
|
|
988
|
+
for (let i = 0; i < paramObject.length; i++) {
|
|
989
|
+
const param = paramObject[i];
|
|
990
|
+
const schema = param.schema;
|
|
991
|
+
const isRequiredWithoutDefault = param.required && schema.default === undefined;
|
|
992
|
+
if (param.in === "header" || param.in === "cookie") {
|
|
993
|
+
if (isRequiredWithoutDefault) {
|
|
994
|
+
paramResult.isValid = false;
|
|
995
|
+
paramResult.reason.push(ErrorType.ParamsContainRequiredUnsupportedSchema);
|
|
996
|
+
}
|
|
997
|
+
continue;
|
|
998
|
+
}
|
|
999
|
+
if (schema.type !== "boolean" &&
|
|
1000
|
+
schema.type !== "string" &&
|
|
1001
|
+
schema.type !== "number" &&
|
|
1002
|
+
schema.type !== "integer") {
|
|
1003
|
+
if (isRequiredWithoutDefault) {
|
|
1004
|
+
paramResult.isValid = false;
|
|
1005
|
+
paramResult.reason.push(ErrorType.ParamsContainRequiredUnsupportedSchema);
|
|
1006
|
+
}
|
|
1007
|
+
continue;
|
|
1008
|
+
}
|
|
1009
|
+
if (param.in === "query" || param.in === "path") {
|
|
1010
|
+
if (isRequiredWithoutDefault) {
|
|
1011
|
+
paramResult.requiredNum = paramResult.requiredNum + 1;
|
|
1012
|
+
}
|
|
1013
|
+
else {
|
|
1014
|
+
paramResult.optionalNum = paramResult.optionalNum + 1;
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
return paramResult;
|
|
1019
|
+
}
|
|
1020
|
+
validateParamCount(postBodyResult, paramResult) {
|
|
1021
|
+
const result = { isValid: true, reason: [] };
|
|
1022
|
+
const totalRequiredParams = postBodyResult.requiredNum + paramResult.requiredNum;
|
|
1023
|
+
const totalParams = totalRequiredParams + postBodyResult.optionalNum + paramResult.optionalNum;
|
|
1024
|
+
if (totalRequiredParams > 1) {
|
|
1025
|
+
if (!this.options.allowMultipleParameters ||
|
|
1026
|
+
totalRequiredParams > SMEValidator.SMERequiredParamsMaxNum) {
|
|
1027
|
+
result.reason.push(ErrorType.ExceededRequiredParamsLimit);
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
else if (totalParams === 0) {
|
|
1031
|
+
result.reason.push(ErrorType.NoParameter);
|
|
1032
|
+
}
|
|
1033
|
+
return result;
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1071
1036
|
|
|
1072
|
-
// Copyright (c) Microsoft Corporation.
|
|
1073
|
-
class TeamsAIValidator extends Validator {
|
|
1074
|
-
constructor(spec, options) {
|
|
1075
|
-
super();
|
|
1076
|
-
this.projectType = ProjectType.TeamsAi;
|
|
1077
|
-
this.options = options;
|
|
1078
|
-
this.spec = spec;
|
|
1079
|
-
this.checkCircularReference();
|
|
1080
|
-
}
|
|
1081
|
-
validateSpec() {
|
|
1082
|
-
const result = { errors: [], warnings: [] };
|
|
1083
|
-
// validate spec server
|
|
1084
|
-
let validationResult = this.validateSpecServer();
|
|
1085
|
-
result.errors.push(...validationResult.errors);
|
|
1086
|
-
// validate no supported API
|
|
1087
|
-
validationResult = this.validateSpecNoSupportAPI();
|
|
1088
|
-
result.errors.push(...validationResult.errors);
|
|
1089
|
-
return result;
|
|
1090
|
-
}
|
|
1091
|
-
validateAPI(method, path) {
|
|
1092
|
-
const result = { isValid: true, reason: [] };
|
|
1093
|
-
method = method.toLocaleLowerCase();
|
|
1094
|
-
// validate method and path
|
|
1095
|
-
const methodAndPathResult = this.validateMethodAndPath(method, path);
|
|
1096
|
-
if (!methodAndPathResult.isValid) {
|
|
1097
|
-
return methodAndPathResult;
|
|
1098
|
-
}
|
|
1099
|
-
const circularReferenceResult = this.validateCircularReference(method, path);
|
|
1100
|
-
if (!circularReferenceResult.isValid) {
|
|
1101
|
-
return circularReferenceResult;
|
|
1102
|
-
}
|
|
1103
|
-
const operationObject = this.spec.paths[path][method];
|
|
1104
|
-
// validate operationId
|
|
1105
|
-
if (!this.options.allowMissingId && !operationObject.operationId) {
|
|
1106
|
-
result.reason.push(ErrorType.MissingOperationId);
|
|
1107
|
-
}
|
|
1108
|
-
// validate server
|
|
1109
|
-
const validateServerResult = this.validateServer(method, path);
|
|
1110
|
-
result.reason.push(...validateServerResult.reason);
|
|
1111
|
-
if (result.reason.length > 0) {
|
|
1112
|
-
result.isValid = false;
|
|
1113
|
-
}
|
|
1114
|
-
return result;
|
|
1115
|
-
}
|
|
1037
|
+
// Copyright (c) Microsoft Corporation.
|
|
1038
|
+
class TeamsAIValidator extends Validator {
|
|
1039
|
+
constructor(spec, options) {
|
|
1040
|
+
super();
|
|
1041
|
+
this.projectType = ProjectType.TeamsAi;
|
|
1042
|
+
this.options = options;
|
|
1043
|
+
this.spec = spec;
|
|
1044
|
+
this.checkCircularReference();
|
|
1045
|
+
}
|
|
1046
|
+
validateSpec() {
|
|
1047
|
+
const result = { errors: [], warnings: [] };
|
|
1048
|
+
// validate spec server
|
|
1049
|
+
let validationResult = this.validateSpecServer();
|
|
1050
|
+
result.errors.push(...validationResult.errors);
|
|
1051
|
+
// validate no supported API
|
|
1052
|
+
validationResult = this.validateSpecNoSupportAPI();
|
|
1053
|
+
result.errors.push(...validationResult.errors);
|
|
1054
|
+
return result;
|
|
1055
|
+
}
|
|
1056
|
+
validateAPI(method, path) {
|
|
1057
|
+
const result = { isValid: true, reason: [] };
|
|
1058
|
+
method = method.toLocaleLowerCase();
|
|
1059
|
+
// validate method and path
|
|
1060
|
+
const methodAndPathResult = this.validateMethodAndPath(method, path);
|
|
1061
|
+
if (!methodAndPathResult.isValid) {
|
|
1062
|
+
return methodAndPathResult;
|
|
1063
|
+
}
|
|
1064
|
+
const circularReferenceResult = this.validateCircularReference(method, path);
|
|
1065
|
+
if (!circularReferenceResult.isValid) {
|
|
1066
|
+
return circularReferenceResult;
|
|
1067
|
+
}
|
|
1068
|
+
const operationObject = this.spec.paths[path][method];
|
|
1069
|
+
// validate operationId
|
|
1070
|
+
if (!this.options.allowMissingId && !operationObject.operationId) {
|
|
1071
|
+
result.reason.push(ErrorType.MissingOperationId);
|
|
1072
|
+
}
|
|
1073
|
+
// validate server
|
|
1074
|
+
const validateServerResult = this.validateServer(method, path);
|
|
1075
|
+
result.reason.push(...validateServerResult.reason);
|
|
1076
|
+
if (result.reason.length > 0) {
|
|
1077
|
+
result.isValid = false;
|
|
1078
|
+
}
|
|
1079
|
+
return result;
|
|
1080
|
+
}
|
|
1116
1081
|
}
|
|
1117
1082
|
|
|
1118
|
-
class ValidatorFactory {
|
|
1119
|
-
static create(spec, options) {
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
}
|
|
1083
|
+
class ValidatorFactory {
|
|
1084
|
+
static create(spec, options) {
|
|
1085
|
+
const type = options.projectType ?? ProjectType.SME;
|
|
1086
|
+
switch (type) {
|
|
1087
|
+
case ProjectType.SME:
|
|
1088
|
+
return new SMEValidator(spec, options);
|
|
1089
|
+
case ProjectType.Copilot:
|
|
1090
|
+
return new CopilotValidator(spec, options);
|
|
1091
|
+
case ProjectType.TeamsAi:
|
|
1092
|
+
return new TeamsAIValidator(spec, options);
|
|
1093
|
+
default:
|
|
1094
|
+
throw new Error(`Invalid project type: ${type}`);
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1133
1097
|
}
|
|
1134
1098
|
|
|
1135
|
-
// Copyright (c) Microsoft Corporation.
|
|
1136
|
-
/**
|
|
1137
|
-
* A class that parses an OpenAPI specification file and provides methods to validate, list, and generate artifacts.
|
|
1138
|
-
*/
|
|
1139
|
-
class SpecParser {
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
errors.push(
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
}
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
}
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
}
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
*
|
|
1293
|
-
* @param
|
|
1294
|
-
* @param
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
}
|
|
1302
|
-
/**
|
|
1303
|
-
* Generates and update artifacts from the OpenAPI specification file. Generate Adaptive Cards, update Teams app manifest, and generate a new OpenAPI specification file.
|
|
1304
|
-
* @param manifestPath A file path of the Teams app manifest file to update.
|
|
1305
|
-
* @param filter An array of strings that represent the filters to apply when generating the artifacts. If filter is empty, it would process nothing.
|
|
1306
|
-
* @param outputSpecPath File path of the new OpenAPI specification file to generate. If not specified or empty, no spec file will be generated.
|
|
1307
|
-
* @param adaptiveCardFolder Folder path where the Adaptive Card files will be generated. If not specified or empty, Adaptive Card files will not be generated.
|
|
1308
|
-
* @param isMe Boolean that indicates whether the project is an Messaging Extension. For Messaging Extension, composeExtensions will be added in Teams app manifest.
|
|
1309
|
-
*/
|
|
1310
|
-
// eslint-disable-next-line @typescript-eslint/require-await
|
|
1311
|
-
generate(manifestPath, filter, outputSpecPath, adaptiveCardFolder, signal) {
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
const validator = ValidatorFactory.create(spec, this.options);
|
|
1338
|
-
this.validator = validator;
|
|
1339
|
-
return validator;
|
|
1340
|
-
}
|
|
1099
|
+
// Copyright (c) Microsoft Corporation.
|
|
1100
|
+
/**
|
|
1101
|
+
* A class that parses an OpenAPI specification file and provides methods to validate, list, and generate artifacts.
|
|
1102
|
+
*/
|
|
1103
|
+
class SpecParser {
|
|
1104
|
+
pathOrSpec;
|
|
1105
|
+
parser;
|
|
1106
|
+
options;
|
|
1107
|
+
spec;
|
|
1108
|
+
validator;
|
|
1109
|
+
unResolveSpec;
|
|
1110
|
+
isSwaggerFile;
|
|
1111
|
+
defaultOptions = {
|
|
1112
|
+
allowMissingId: false,
|
|
1113
|
+
allowSwagger: false,
|
|
1114
|
+
allowAPIKeyAuth: false,
|
|
1115
|
+
allowMultipleParameters: false,
|
|
1116
|
+
allowBearerTokenAuth: false,
|
|
1117
|
+
allowOauth2: false,
|
|
1118
|
+
allowMethods: ["get", "post"],
|
|
1119
|
+
allowConversationStarters: false,
|
|
1120
|
+
allowResponseSemantics: false,
|
|
1121
|
+
allowConfirmation: false,
|
|
1122
|
+
projectType: ProjectType.SME,
|
|
1123
|
+
isGptPlugin: false,
|
|
1124
|
+
};
|
|
1125
|
+
/**
|
|
1126
|
+
* Creates a new instance of the SpecParser class.
|
|
1127
|
+
* @param pathOrDoc The path to the OpenAPI specification file or the OpenAPI specification object.
|
|
1128
|
+
* @param options The options for parsing the OpenAPI specification file.
|
|
1129
|
+
*/
|
|
1130
|
+
constructor(pathOrDoc, options) {
|
|
1131
|
+
this.pathOrSpec = pathOrDoc;
|
|
1132
|
+
this.parser = new SwaggerParser();
|
|
1133
|
+
this.options = {
|
|
1134
|
+
...this.defaultOptions,
|
|
1135
|
+
...(options ?? {}),
|
|
1136
|
+
};
|
|
1137
|
+
}
|
|
1138
|
+
/**
|
|
1139
|
+
* Validates the OpenAPI specification file and returns a validation result.
|
|
1140
|
+
*
|
|
1141
|
+
* @returns A validation result object that contains information about any errors or warnings in the specification file.
|
|
1142
|
+
*/
|
|
1143
|
+
async validate() {
|
|
1144
|
+
try {
|
|
1145
|
+
try {
|
|
1146
|
+
await this.loadSpec();
|
|
1147
|
+
await this.parser.validate(this.spec, {
|
|
1148
|
+
validate: {
|
|
1149
|
+
schema: false,
|
|
1150
|
+
},
|
|
1151
|
+
});
|
|
1152
|
+
}
|
|
1153
|
+
catch (e) {
|
|
1154
|
+
return {
|
|
1155
|
+
status: ValidationStatus.Error,
|
|
1156
|
+
warnings: [],
|
|
1157
|
+
errors: [{ type: ErrorType.SpecNotValid, content: e.toString() }],
|
|
1158
|
+
};
|
|
1159
|
+
}
|
|
1160
|
+
const errors = [];
|
|
1161
|
+
const warnings = [];
|
|
1162
|
+
if (!this.options.allowSwagger && this.isSwaggerFile) {
|
|
1163
|
+
return {
|
|
1164
|
+
status: ValidationStatus.Error,
|
|
1165
|
+
warnings: [],
|
|
1166
|
+
errors: [
|
|
1167
|
+
{
|
|
1168
|
+
type: ErrorType.SwaggerNotSupported,
|
|
1169
|
+
content: ConstantString.SwaggerNotSupported,
|
|
1170
|
+
},
|
|
1171
|
+
],
|
|
1172
|
+
};
|
|
1173
|
+
}
|
|
1174
|
+
// Remote reference not supported
|
|
1175
|
+
const refPaths = this.parser.$refs.paths();
|
|
1176
|
+
// refPaths [0] is the current spec file path
|
|
1177
|
+
if (refPaths.length > 1) {
|
|
1178
|
+
errors.push({
|
|
1179
|
+
type: ErrorType.RemoteRefNotSupported,
|
|
1180
|
+
content: Utils.format(ConstantString.RemoteRefNotSupported, refPaths.join(", ")),
|
|
1181
|
+
data: refPaths,
|
|
1182
|
+
});
|
|
1183
|
+
}
|
|
1184
|
+
const validator = this.getValidator(this.spec);
|
|
1185
|
+
const validationResult = validator.validateSpec();
|
|
1186
|
+
warnings.push(...validationResult.warnings);
|
|
1187
|
+
errors.push(...validationResult.errors);
|
|
1188
|
+
let status = ValidationStatus.Valid;
|
|
1189
|
+
if (warnings.length > 0 && errors.length === 0) {
|
|
1190
|
+
status = ValidationStatus.Warning;
|
|
1191
|
+
}
|
|
1192
|
+
else if (errors.length > 0) {
|
|
1193
|
+
status = ValidationStatus.Error;
|
|
1194
|
+
}
|
|
1195
|
+
return {
|
|
1196
|
+
status: status,
|
|
1197
|
+
warnings: warnings,
|
|
1198
|
+
errors: errors,
|
|
1199
|
+
};
|
|
1200
|
+
}
|
|
1201
|
+
catch (err) {
|
|
1202
|
+
throw new SpecParserError(err.toString(), ErrorType.ValidateFailed);
|
|
1203
|
+
}
|
|
1204
|
+
}
|
|
1205
|
+
async listSupportedAPIInfo() {
|
|
1206
|
+
try {
|
|
1207
|
+
await this.loadSpec();
|
|
1208
|
+
const apiMap = this.getAPIs(this.spec);
|
|
1209
|
+
const apiInfos = [];
|
|
1210
|
+
for (const key in apiMap) {
|
|
1211
|
+
const { operation, isValid } = apiMap[key];
|
|
1212
|
+
if (!isValid) {
|
|
1213
|
+
continue;
|
|
1214
|
+
}
|
|
1215
|
+
const [method, path] = key.split(" ");
|
|
1216
|
+
const operationId = operation.operationId;
|
|
1217
|
+
// In Browser environment, this api is by default not support api without operationId
|
|
1218
|
+
if (!operationId) {
|
|
1219
|
+
continue;
|
|
1220
|
+
}
|
|
1221
|
+
const command = Utils.parseApiInfo(operation, this.options);
|
|
1222
|
+
const apiInfo = {
|
|
1223
|
+
method: method,
|
|
1224
|
+
path: path,
|
|
1225
|
+
title: command.title,
|
|
1226
|
+
id: operationId,
|
|
1227
|
+
parameters: command.parameters,
|
|
1228
|
+
description: command.description,
|
|
1229
|
+
};
|
|
1230
|
+
apiInfos.push(apiInfo);
|
|
1231
|
+
}
|
|
1232
|
+
return apiInfos;
|
|
1233
|
+
}
|
|
1234
|
+
catch (err) {
|
|
1235
|
+
throw new SpecParserError(err.toString(), ErrorType.listSupportedAPIInfoFailed);
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1238
|
+
/**
|
|
1239
|
+
* Lists all the OpenAPI operations in the specification file.
|
|
1240
|
+
* @returns A string array that represents the HTTP method and path of each operation, such as ['GET /pets/{petId}', 'GET /user/{userId}']
|
|
1241
|
+
* according to copilot plugin spec, only list get and post method without auth
|
|
1242
|
+
*/
|
|
1243
|
+
// eslint-disable-next-line @typescript-eslint/require-await
|
|
1244
|
+
async list() {
|
|
1245
|
+
throw new Error("Method not implemented.");
|
|
1246
|
+
}
|
|
1247
|
+
/**
|
|
1248
|
+
* Generate specs according to the filters.
|
|
1249
|
+
* @param filter An array of strings that represent the filters to apply when generating the artifacts. If filter is empty, it would process nothing.
|
|
1250
|
+
*/
|
|
1251
|
+
// eslint-disable-next-line @typescript-eslint/require-await
|
|
1252
|
+
async getFilteredSpecs(filter, signal) {
|
|
1253
|
+
throw new Error("Method not implemented.");
|
|
1254
|
+
}
|
|
1255
|
+
/**
|
|
1256
|
+
* Generates and update artifacts from the OpenAPI specification file. Generate Adaptive Cards, update Teams app manifest, and generate a new OpenAPI specification file.
|
|
1257
|
+
* @param manifestPath A file path of the Teams app manifest file to update.
|
|
1258
|
+
* @param filter An array of strings that represent the filters to apply when generating the artifacts. If filter is empty, it would process nothing.
|
|
1259
|
+
* @param outputSpecPath File path of the new OpenAPI specification file to generate. If not specified or empty, no spec file will be generated.
|
|
1260
|
+
* @param pluginFilePath File path of the api plugin file to generate.
|
|
1261
|
+
*/
|
|
1262
|
+
// eslint-disable-next-line @typescript-eslint/require-await
|
|
1263
|
+
async generateForCopilot(manifestPath, filter, outputSpecPath, pluginFilePath, signal) {
|
|
1264
|
+
throw new Error("Method not implemented.");
|
|
1265
|
+
}
|
|
1266
|
+
/**
|
|
1267
|
+
* Generates and update artifacts from the OpenAPI specification file. Generate Adaptive Cards, update Teams app manifest, and generate a new OpenAPI specification file.
|
|
1268
|
+
* @param manifestPath A file path of the Teams app manifest file to update.
|
|
1269
|
+
* @param filter An array of strings that represent the filters to apply when generating the artifacts. If filter is empty, it would process nothing.
|
|
1270
|
+
* @param outputSpecPath File path of the new OpenAPI specification file to generate. If not specified or empty, no spec file will be generated.
|
|
1271
|
+
* @param adaptiveCardFolder Folder path where the Adaptive Card files will be generated. If not specified or empty, Adaptive Card files will not be generated.
|
|
1272
|
+
* @param isMe Boolean that indicates whether the project is an Messaging Extension. For Messaging Extension, composeExtensions will be added in Teams app manifest.
|
|
1273
|
+
*/
|
|
1274
|
+
// eslint-disable-next-line @typescript-eslint/require-await
|
|
1275
|
+
async generate(manifestPath, filter, outputSpecPath, adaptiveCardFolder, signal) {
|
|
1276
|
+
throw new Error("Method not implemented.");
|
|
1277
|
+
}
|
|
1278
|
+
async loadSpec() {
|
|
1279
|
+
if (!this.spec) {
|
|
1280
|
+
this.unResolveSpec = (await this.parser.parse(this.pathOrSpec));
|
|
1281
|
+
if (!this.unResolveSpec.openapi && this.unResolveSpec.swagger === "2.0") {
|
|
1282
|
+
this.isSwaggerFile = true;
|
|
1283
|
+
}
|
|
1284
|
+
const clonedUnResolveSpec = JSON.parse(JSON.stringify(this.unResolveSpec));
|
|
1285
|
+
this.spec = (await this.parser.dereference(clonedUnResolveSpec));
|
|
1286
|
+
}
|
|
1287
|
+
}
|
|
1288
|
+
getAPIs(spec) {
|
|
1289
|
+
const validator = this.getValidator(spec);
|
|
1290
|
+
const apiMap = validator.listAPIs();
|
|
1291
|
+
return apiMap;
|
|
1292
|
+
}
|
|
1293
|
+
getValidator(spec) {
|
|
1294
|
+
if (this.validator) {
|
|
1295
|
+
return this.validator;
|
|
1296
|
+
}
|
|
1297
|
+
const validator = ValidatorFactory.create(spec, this.options);
|
|
1298
|
+
this.validator = validator;
|
|
1299
|
+
return validator;
|
|
1300
|
+
}
|
|
1341
1301
|
}
|
|
1342
1302
|
|
|
1343
|
-
// Copyright (c) Microsoft Corporation.
|
|
1344
|
-
class JsonDataGenerator {
|
|
1345
|
-
static
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
}
|
|
1465
|
-
|
|
1303
|
+
// Copyright (c) Microsoft Corporation.
|
|
1304
|
+
class JsonDataGenerator {
|
|
1305
|
+
static visitedSchemas = new Set();
|
|
1306
|
+
static generate(schema) {
|
|
1307
|
+
return this.generateMockData(schema);
|
|
1308
|
+
}
|
|
1309
|
+
static generateMockData(schema) {
|
|
1310
|
+
if (this.visitedSchemas.has(schema)) {
|
|
1311
|
+
return null; // Prevent circular reference
|
|
1312
|
+
}
|
|
1313
|
+
this.visitedSchemas.add(schema);
|
|
1314
|
+
let result;
|
|
1315
|
+
if (schema.anyOf) {
|
|
1316
|
+
// Select the first schema in anyOf
|
|
1317
|
+
const selectedSchema = schema.anyOf[0];
|
|
1318
|
+
result = this.generateMockData(selectedSchema);
|
|
1319
|
+
}
|
|
1320
|
+
else if (schema.oneOf) {
|
|
1321
|
+
// Select the first schema in oneOf
|
|
1322
|
+
const selectedSchema = schema.oneOf[0];
|
|
1323
|
+
result = this.generateMockData(selectedSchema);
|
|
1324
|
+
}
|
|
1325
|
+
else if (schema.allOf) {
|
|
1326
|
+
// merge all schemas in allOf
|
|
1327
|
+
result = {};
|
|
1328
|
+
for (const subschema of schema.allOf) {
|
|
1329
|
+
const data = this.generateMockData(subschema);
|
|
1330
|
+
result = { ...result, ...data };
|
|
1331
|
+
}
|
|
1332
|
+
}
|
|
1333
|
+
else {
|
|
1334
|
+
switch (schema.type) {
|
|
1335
|
+
case "string":
|
|
1336
|
+
if (schema.example !== undefined) {
|
|
1337
|
+
result = schema.example;
|
|
1338
|
+
}
|
|
1339
|
+
else if (schema.format) {
|
|
1340
|
+
switch (schema.format) {
|
|
1341
|
+
case "date-time":
|
|
1342
|
+
result = "2024-11-01T05:25:43.593Z";
|
|
1343
|
+
break;
|
|
1344
|
+
case "email":
|
|
1345
|
+
result = "example@example.com";
|
|
1346
|
+
break;
|
|
1347
|
+
case "uuid":
|
|
1348
|
+
result = "123e4567-e89b-12d3-a456-426614174000";
|
|
1349
|
+
break;
|
|
1350
|
+
case "ipv4":
|
|
1351
|
+
result = "192.168.0.1";
|
|
1352
|
+
break;
|
|
1353
|
+
case "ipv6":
|
|
1354
|
+
result = "2001:0db8:85a3:0000:0000:8a2e:0370:7334";
|
|
1355
|
+
break;
|
|
1356
|
+
default:
|
|
1357
|
+
result = "example string";
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
1360
|
+
else {
|
|
1361
|
+
result = "example string";
|
|
1362
|
+
}
|
|
1363
|
+
break;
|
|
1364
|
+
case "number":
|
|
1365
|
+
if (schema.example !== undefined) {
|
|
1366
|
+
result = schema.example;
|
|
1367
|
+
}
|
|
1368
|
+
else if (schema.format) {
|
|
1369
|
+
switch (schema.format) {
|
|
1370
|
+
case "float":
|
|
1371
|
+
result = 3.14;
|
|
1372
|
+
break;
|
|
1373
|
+
case "double":
|
|
1374
|
+
result = 3.14159;
|
|
1375
|
+
break;
|
|
1376
|
+
default:
|
|
1377
|
+
result = 123;
|
|
1378
|
+
}
|
|
1379
|
+
}
|
|
1380
|
+
else {
|
|
1381
|
+
result = 123;
|
|
1382
|
+
}
|
|
1383
|
+
break;
|
|
1384
|
+
case "integer":
|
|
1385
|
+
if (schema.example !== undefined) {
|
|
1386
|
+
result = schema.example;
|
|
1387
|
+
}
|
|
1388
|
+
else if (schema.format) {
|
|
1389
|
+
switch (schema.format) {
|
|
1390
|
+
case "int32":
|
|
1391
|
+
result = 123456;
|
|
1392
|
+
break;
|
|
1393
|
+
case "int64":
|
|
1394
|
+
result = 123456789;
|
|
1395
|
+
break;
|
|
1396
|
+
default:
|
|
1397
|
+
result = 123;
|
|
1398
|
+
}
|
|
1399
|
+
}
|
|
1400
|
+
else {
|
|
1401
|
+
result = 123;
|
|
1402
|
+
}
|
|
1403
|
+
break;
|
|
1404
|
+
case "boolean":
|
|
1405
|
+
result = schema.example !== undefined ? schema.example : true;
|
|
1406
|
+
break;
|
|
1407
|
+
case "array":
|
|
1408
|
+
result = [this.generateMockData(schema.items)];
|
|
1409
|
+
break;
|
|
1410
|
+
case "object":
|
|
1411
|
+
result = {};
|
|
1412
|
+
if (schema.properties) {
|
|
1413
|
+
for (const key in schema.properties) {
|
|
1414
|
+
result[key] = this.generateMockData(schema.properties[key]);
|
|
1415
|
+
}
|
|
1416
|
+
}
|
|
1417
|
+
break;
|
|
1418
|
+
default:
|
|
1419
|
+
result = schema.example || null;
|
|
1420
|
+
}
|
|
1421
|
+
}
|
|
1422
|
+
this.visitedSchemas.delete(schema);
|
|
1423
|
+
return result;
|
|
1424
|
+
}
|
|
1425
|
+
}
|
|
1466
1426
|
|
|
1467
|
-
// Copyright (c) Microsoft Corporation.
|
|
1468
|
-
class AdaptiveCardGenerator {
|
|
1469
|
-
static generateAdaptiveCard(operationItem, allowMultipleMediaType = false, maxElementCount = Number.MAX_SAFE_INTEGER) {
|
|
1470
|
-
try {
|
|
1471
|
-
const { json } = Utils.getResponseJson(operationItem, allowMultipleMediaType);
|
|
1472
|
-
let cardBody = [];
|
|
1473
|
-
let jsonData = {};
|
|
1474
|
-
const warnings = [];
|
|
1475
|
-
const operationId = operationItem.operationId;
|
|
1476
|
-
let schema = json.schema;
|
|
1477
|
-
let jsonPath = "$";
|
|
1478
|
-
if (schema && Object.keys(schema).length > 0) {
|
|
1479
|
-
try {
|
|
1480
|
-
jsonData = JsonDataGenerator.generate(schema);
|
|
1481
|
-
}
|
|
1482
|
-
catch (err) {
|
|
1483
|
-
warnings.push({
|
|
1484
|
-
type: WarningType.GenerateJsonDataFailed,
|
|
1485
|
-
content: Utils.format(ConstantString.GenerateJsonDataFailed, operationId, err.toString()),
|
|
1486
|
-
data: operationId,
|
|
1487
|
-
});
|
|
1488
|
-
}
|
|
1489
|
-
jsonPath = AdaptiveCardGenerator.getResponseJsonPathFromSchema(schema);
|
|
1490
|
-
if (jsonPath !== "$") {
|
|
1491
|
-
schema = schema.properties[jsonPath];
|
|
1492
|
-
}
|
|
1493
|
-
cardBody = AdaptiveCardGenerator.generateCardFromResponse(schema, "", "", maxElementCount);
|
|
1494
|
-
}
|
|
1495
|
-
// if no schema, try to use example value
|
|
1496
|
-
if (cardBody.length === 0 && (json.examples || json.example)) {
|
|
1497
|
-
cardBody = [
|
|
1498
|
-
{
|
|
1499
|
-
type: ConstantString.TextBlockType,
|
|
1500
|
-
text: "${jsonStringify($root)}",
|
|
1501
|
-
wrap: true,
|
|
1502
|
-
},
|
|
1503
|
-
];
|
|
1504
|
-
}
|
|
1505
|
-
// if no example value, use default success response
|
|
1506
|
-
if (cardBody.length === 0) {
|
|
1507
|
-
cardBody = [
|
|
1508
|
-
{
|
|
1509
|
-
type: ConstantString.TextBlockType,
|
|
1510
|
-
text: "success",
|
|
1511
|
-
wrap: true,
|
|
1512
|
-
},
|
|
1513
|
-
];
|
|
1514
|
-
}
|
|
1515
|
-
const fullCard = {
|
|
1516
|
-
type: ConstantString.AdaptiveCardType,
|
|
1517
|
-
$schema: ConstantString.AdaptiveCardSchema,
|
|
1518
|
-
version: ConstantString.AdaptiveCardVersion,
|
|
1519
|
-
body: cardBody,
|
|
1520
|
-
};
|
|
1521
|
-
return [fullCard, jsonPath, jsonData, warnings];
|
|
1522
|
-
}
|
|
1523
|
-
catch (err) {
|
|
1524
|
-
throw new SpecParserError(err.toString(), ErrorType.GenerateAdaptiveCardFailed);
|
|
1525
|
-
}
|
|
1526
|
-
}
|
|
1527
|
-
static generateCardFromResponse(schema, name, parentArrayName = "", maxElementCount = Number.MAX_SAFE_INTEGER, counter = { count: 0 }) {
|
|
1528
|
-
if (counter.count >= maxElementCount) {
|
|
1529
|
-
return [];
|
|
1530
|
-
}
|
|
1531
|
-
if (schema.type === "array") {
|
|
1532
|
-
// schema.items can be arbitrary object: schema { type: array, items: {} }
|
|
1533
|
-
if (Object.keys(schema.items).length === 0) {
|
|
1534
|
-
counter.count++;
|
|
1535
|
-
return [
|
|
1536
|
-
{
|
|
1537
|
-
type: ConstantString.TextBlockType,
|
|
1538
|
-
text: name ? `${name}: \${jsonStringify(${name})}` : "result: ${jsonStringify($root)}",
|
|
1539
|
-
wrap: true,
|
|
1540
|
-
},
|
|
1541
|
-
];
|
|
1542
|
-
}
|
|
1543
|
-
const obj = AdaptiveCardGenerator.generateCardFromResponse(schema.items, "", name, maxElementCount, counter);
|
|
1544
|
-
const template = {
|
|
1545
|
-
type: ConstantString.ContainerType,
|
|
1546
|
-
$data: name ? `\${${name}}` : "${$root}",
|
|
1547
|
-
items: Array(),
|
|
1548
|
-
};
|
|
1549
|
-
template.items.push(...obj);
|
|
1550
|
-
return [template];
|
|
1551
|
-
}
|
|
1552
|
-
// some schema may not contain type but contain properties
|
|
1553
|
-
if (Utils.isObjectSchema(schema)) {
|
|
1554
|
-
const { properties } = schema;
|
|
1555
|
-
const result = [];
|
|
1556
|
-
for (const property in properties) {
|
|
1557
|
-
const obj = AdaptiveCardGenerator.generateCardFromResponse(properties[property], name ? `${name}.${property}` : property, parentArrayName, maxElementCount, counter);
|
|
1558
|
-
result.push(...obj);
|
|
1559
|
-
}
|
|
1560
|
-
if (schema.additionalProperties) {
|
|
1561
|
-
// TODO: better ways to handler warnings.
|
|
1562
|
-
console.warn(ConstantString.AdditionalPropertiesNotSupported);
|
|
1563
|
-
}
|
|
1564
|
-
return result;
|
|
1565
|
-
}
|
|
1566
|
-
if (schema.type === "string" ||
|
|
1567
|
-
schema.type === "integer" ||
|
|
1568
|
-
schema.type === "boolean" ||
|
|
1569
|
-
schema.type === "number") {
|
|
1570
|
-
counter.count++;
|
|
1571
|
-
if (!AdaptiveCardGenerator.isImageUrlProperty(schema, name, parentArrayName)) {
|
|
1572
|
-
// string in root: "ddd"
|
|
1573
|
-
let text = "result: ${$root}";
|
|
1574
|
-
if (name) {
|
|
1575
|
-
// object { id: "1" }
|
|
1576
|
-
text = `${name}: \${if(${name}, ${name}, 'N/A')}`;
|
|
1577
|
-
if (parentArrayName) {
|
|
1578
|
-
// object types inside array: { tags: ["id": 1, "name": "name"] }
|
|
1579
|
-
text = `${parentArrayName}.${text}`;
|
|
1580
|
-
}
|
|
1581
|
-
}
|
|
1582
|
-
else if (parentArrayName) {
|
|
1583
|
-
// string array: photoUrls: ["1", "2"]
|
|
1584
|
-
text = `${parentArrayName}: ` + "${$data}";
|
|
1585
|
-
}
|
|
1586
|
-
return [
|
|
1587
|
-
{
|
|
1588
|
-
type: ConstantString.TextBlockType,
|
|
1589
|
-
text,
|
|
1590
|
-
wrap: true,
|
|
1591
|
-
},
|
|
1592
|
-
];
|
|
1593
|
-
}
|
|
1594
|
-
else {
|
|
1595
|
-
const url = name ? `\${${name}}` : "${$data}";
|
|
1596
|
-
const condition = name
|
|
1597
|
-
? `\${${name} != null && ${name} != ''}`
|
|
1598
|
-
: "${$data != null && $data != ''}";
|
|
1599
|
-
return [
|
|
1600
|
-
{
|
|
1601
|
-
type: "Image",
|
|
1602
|
-
url,
|
|
1603
|
-
$when: condition,
|
|
1604
|
-
},
|
|
1605
|
-
];
|
|
1606
|
-
}
|
|
1607
|
-
}
|
|
1608
|
-
if (schema.oneOf || schema.anyOf || schema.not || schema.allOf) {
|
|
1609
|
-
throw new Error(Utils.format(ConstantString.SchemaNotSupported, JSON.stringify(schema)));
|
|
1610
|
-
}
|
|
1611
|
-
throw new Error(Utils.format(ConstantString.UnknownSchema, JSON.stringify(schema)));
|
|
1612
|
-
}
|
|
1613
|
-
// Find the first array property in the response schema object with the well-known name
|
|
1614
|
-
static getResponseJsonPathFromSchema(schema) {
|
|
1615
|
-
if (Utils.isObjectSchema(schema)) {
|
|
1616
|
-
const { properties } = schema;
|
|
1617
|
-
for (const property in properties) {
|
|
1618
|
-
const schema = properties[property];
|
|
1619
|
-
if (schema.type === "array" &&
|
|
1620
|
-
Utils.isWellKnownName(property, ConstantString.WellknownResultNames)) {
|
|
1621
|
-
return property;
|
|
1622
|
-
}
|
|
1623
|
-
}
|
|
1624
|
-
}
|
|
1625
|
-
return "$";
|
|
1626
|
-
}
|
|
1627
|
-
static isImageUrlProperty(schema, name, parentArrayName) {
|
|
1628
|
-
const propertyName = name ? name : parentArrayName;
|
|
1629
|
-
return (!!propertyName &&
|
|
1630
|
-
schema.type === "string" &&
|
|
1631
|
-
Utils.isWellKnownName(propertyName, ConstantString.WellknownImageName) &&
|
|
1632
|
-
(propertyName.toLocaleLowerCase().indexOf("url") >= 0 || schema.format === "uri"));
|
|
1633
|
-
}
|
|
1427
|
+
// Copyright (c) Microsoft Corporation.
|
|
1428
|
+
class AdaptiveCardGenerator {
|
|
1429
|
+
static generateAdaptiveCard(operationItem, allowMultipleMediaType = false, maxElementCount = Number.MAX_SAFE_INTEGER) {
|
|
1430
|
+
try {
|
|
1431
|
+
const { json } = Utils.getResponseJson(operationItem, allowMultipleMediaType);
|
|
1432
|
+
let cardBody = [];
|
|
1433
|
+
let jsonData = {};
|
|
1434
|
+
const warnings = [];
|
|
1435
|
+
const operationId = operationItem.operationId;
|
|
1436
|
+
let schema = json.schema;
|
|
1437
|
+
let jsonPath = "$";
|
|
1438
|
+
if (schema && Object.keys(schema).length > 0) {
|
|
1439
|
+
try {
|
|
1440
|
+
jsonData = JsonDataGenerator.generate(schema);
|
|
1441
|
+
}
|
|
1442
|
+
catch (err) {
|
|
1443
|
+
warnings.push({
|
|
1444
|
+
type: WarningType.GenerateJsonDataFailed,
|
|
1445
|
+
content: Utils.format(ConstantString.GenerateJsonDataFailed, operationId, err.toString()),
|
|
1446
|
+
data: operationId,
|
|
1447
|
+
});
|
|
1448
|
+
}
|
|
1449
|
+
jsonPath = AdaptiveCardGenerator.getResponseJsonPathFromSchema(schema);
|
|
1450
|
+
if (jsonPath !== "$") {
|
|
1451
|
+
schema = schema.properties[jsonPath];
|
|
1452
|
+
}
|
|
1453
|
+
cardBody = AdaptiveCardGenerator.generateCardFromResponse(schema, "", "", maxElementCount);
|
|
1454
|
+
}
|
|
1455
|
+
// if no schema, try to use example value
|
|
1456
|
+
if (cardBody.length === 0 && (json.examples || json.example)) {
|
|
1457
|
+
cardBody = [
|
|
1458
|
+
{
|
|
1459
|
+
type: ConstantString.TextBlockType,
|
|
1460
|
+
text: "${jsonStringify($root)}",
|
|
1461
|
+
wrap: true,
|
|
1462
|
+
},
|
|
1463
|
+
];
|
|
1464
|
+
}
|
|
1465
|
+
// if no example value, use default success response
|
|
1466
|
+
if (cardBody.length === 0) {
|
|
1467
|
+
cardBody = [
|
|
1468
|
+
{
|
|
1469
|
+
type: ConstantString.TextBlockType,
|
|
1470
|
+
text: "success",
|
|
1471
|
+
wrap: true,
|
|
1472
|
+
},
|
|
1473
|
+
];
|
|
1474
|
+
}
|
|
1475
|
+
const fullCard = {
|
|
1476
|
+
type: ConstantString.AdaptiveCardType,
|
|
1477
|
+
$schema: ConstantString.AdaptiveCardSchema,
|
|
1478
|
+
version: ConstantString.AdaptiveCardVersion,
|
|
1479
|
+
body: cardBody,
|
|
1480
|
+
};
|
|
1481
|
+
return [fullCard, jsonPath, jsonData, warnings];
|
|
1482
|
+
}
|
|
1483
|
+
catch (err) {
|
|
1484
|
+
throw new SpecParserError(err.toString(), ErrorType.GenerateAdaptiveCardFailed);
|
|
1485
|
+
}
|
|
1486
|
+
}
|
|
1487
|
+
static generateCardFromResponse(schema, name, parentArrayName = "", maxElementCount = Number.MAX_SAFE_INTEGER, counter = { count: 0 }) {
|
|
1488
|
+
if (counter.count >= maxElementCount) {
|
|
1489
|
+
return [];
|
|
1490
|
+
}
|
|
1491
|
+
if (schema.type === "array") {
|
|
1492
|
+
// schema.items can be arbitrary object: schema { type: array, items: {} }
|
|
1493
|
+
if (Object.keys(schema.items).length === 0) {
|
|
1494
|
+
counter.count++;
|
|
1495
|
+
return [
|
|
1496
|
+
{
|
|
1497
|
+
type: ConstantString.TextBlockType,
|
|
1498
|
+
text: name ? `${name}: \${jsonStringify(${name})}` : "result: ${jsonStringify($root)}",
|
|
1499
|
+
wrap: true,
|
|
1500
|
+
},
|
|
1501
|
+
];
|
|
1502
|
+
}
|
|
1503
|
+
const obj = AdaptiveCardGenerator.generateCardFromResponse(schema.items, "", name, maxElementCount, counter);
|
|
1504
|
+
const template = {
|
|
1505
|
+
type: ConstantString.ContainerType,
|
|
1506
|
+
$data: name ? `\${${name}}` : "${$root}",
|
|
1507
|
+
items: Array(),
|
|
1508
|
+
};
|
|
1509
|
+
template.items.push(...obj);
|
|
1510
|
+
return [template];
|
|
1511
|
+
}
|
|
1512
|
+
// some schema may not contain type but contain properties
|
|
1513
|
+
if (Utils.isObjectSchema(schema)) {
|
|
1514
|
+
const { properties } = schema;
|
|
1515
|
+
const result = [];
|
|
1516
|
+
for (const property in properties) {
|
|
1517
|
+
const obj = AdaptiveCardGenerator.generateCardFromResponse(properties[property], name ? `${name}.${property}` : property, parentArrayName, maxElementCount, counter);
|
|
1518
|
+
result.push(...obj);
|
|
1519
|
+
}
|
|
1520
|
+
if (schema.additionalProperties) {
|
|
1521
|
+
// TODO: better ways to handler warnings.
|
|
1522
|
+
console.warn(ConstantString.AdditionalPropertiesNotSupported);
|
|
1523
|
+
}
|
|
1524
|
+
return result;
|
|
1525
|
+
}
|
|
1526
|
+
if (schema.type === "string" ||
|
|
1527
|
+
schema.type === "integer" ||
|
|
1528
|
+
schema.type === "boolean" ||
|
|
1529
|
+
schema.type === "number") {
|
|
1530
|
+
counter.count++;
|
|
1531
|
+
if (!AdaptiveCardGenerator.isImageUrlProperty(schema, name, parentArrayName)) {
|
|
1532
|
+
// string in root: "ddd"
|
|
1533
|
+
let text = "result: ${$root}";
|
|
1534
|
+
if (name) {
|
|
1535
|
+
// object { id: "1" }
|
|
1536
|
+
text = `${name}: \${if(${name}, ${name}, 'N/A')}`;
|
|
1537
|
+
if (parentArrayName) {
|
|
1538
|
+
// object types inside array: { tags: ["id": 1, "name": "name"] }
|
|
1539
|
+
text = `${parentArrayName}.${text}`;
|
|
1540
|
+
}
|
|
1541
|
+
}
|
|
1542
|
+
else if (parentArrayName) {
|
|
1543
|
+
// string array: photoUrls: ["1", "2"]
|
|
1544
|
+
text = `${parentArrayName}: ` + "${$data}";
|
|
1545
|
+
}
|
|
1546
|
+
return [
|
|
1547
|
+
{
|
|
1548
|
+
type: ConstantString.TextBlockType,
|
|
1549
|
+
text,
|
|
1550
|
+
wrap: true,
|
|
1551
|
+
},
|
|
1552
|
+
];
|
|
1553
|
+
}
|
|
1554
|
+
else {
|
|
1555
|
+
const url = name ? `\${${name}}` : "${$data}";
|
|
1556
|
+
const condition = name
|
|
1557
|
+
? `\${${name} != null && ${name} != ''}`
|
|
1558
|
+
: "${$data != null && $data != ''}";
|
|
1559
|
+
return [
|
|
1560
|
+
{
|
|
1561
|
+
type: "Image",
|
|
1562
|
+
url,
|
|
1563
|
+
$when: condition,
|
|
1564
|
+
},
|
|
1565
|
+
];
|
|
1566
|
+
}
|
|
1567
|
+
}
|
|
1568
|
+
if (schema.oneOf || schema.anyOf || schema.not || schema.allOf) {
|
|
1569
|
+
throw new Error(Utils.format(ConstantString.SchemaNotSupported, JSON.stringify(schema)));
|
|
1570
|
+
}
|
|
1571
|
+
throw new Error(Utils.format(ConstantString.UnknownSchema, JSON.stringify(schema)));
|
|
1572
|
+
}
|
|
1573
|
+
// Find the first array property in the response schema object with the well-known name
|
|
1574
|
+
static getResponseJsonPathFromSchema(schema) {
|
|
1575
|
+
if (Utils.isObjectSchema(schema)) {
|
|
1576
|
+
const { properties } = schema;
|
|
1577
|
+
for (const property in properties) {
|
|
1578
|
+
const schema = properties[property];
|
|
1579
|
+
if (schema.type === "array" &&
|
|
1580
|
+
Utils.isWellKnownName(property, ConstantString.WellknownResultNames)) {
|
|
1581
|
+
return property;
|
|
1582
|
+
}
|
|
1583
|
+
}
|
|
1584
|
+
}
|
|
1585
|
+
return "$";
|
|
1586
|
+
}
|
|
1587
|
+
static isImageUrlProperty(schema, name, parentArrayName) {
|
|
1588
|
+
const propertyName = name ? name : parentArrayName;
|
|
1589
|
+
return (!!propertyName &&
|
|
1590
|
+
schema.type === "string" &&
|
|
1591
|
+
Utils.isWellKnownName(propertyName, ConstantString.WellknownImageName) &&
|
|
1592
|
+
(propertyName.toLocaleLowerCase().indexOf("url") >= 0 || schema.format === "uri"));
|
|
1593
|
+
}
|
|
1634
1594
|
}
|
|
1635
1595
|
|
|
1636
1596
|
export { AdaptiveCardGenerator, ConstantString, ErrorType, SpecParser, SpecParserError, Utils, ValidationStatus, WarningType };
|