@smartbear/mcp 0.12.1 → 0.13.1
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/README.md +30 -6
- package/dist/bugsnag/client/api/CurrentUser.js +50 -26
- package/dist/bugsnag/client/api/Error.js +156 -93
- package/dist/bugsnag/client/api/Project.js +398 -276
- package/dist/bugsnag/client/api/api.js +4087 -3837
- package/dist/bugsnag/client/api/base.js +155 -173
- package/dist/bugsnag/client/api/configuration.js +28 -25
- package/dist/bugsnag/client/filters.js +11 -20
- package/dist/bugsnag/client.js +1398 -1281
- package/dist/bugsnag/input-schemas.js +39 -57
- package/dist/collaborator/client.js +335 -371
- package/dist/common/bugsnag.js +5 -3
- package/dist/common/cache.js +50 -57
- package/dist/common/client-registry.js +106 -119
- package/dist/common/info.js +7 -3
- package/dist/common/register-clients.js +0 -16
- package/dist/common/server.js +270 -228
- package/dist/common/tools.js +19 -0
- package/dist/common/transport-http.js +252 -343
- package/dist/common/transport-stdio.js +40 -37
- package/dist/common/zod-utils.js +20 -0
- package/dist/index.js +18 -23
- package/dist/package.json.js +11 -0
- package/dist/pactflow/client/ai.js +142 -169
- package/dist/pactflow/client/base.js +41 -51
- package/dist/pactflow/client/prompt-utils.js +93 -84
- package/dist/pactflow/client/prompts.js +95 -92
- package/dist/pactflow/client/tools.js +94 -83
- package/dist/pactflow/client/utils.js +60 -64
- package/dist/pactflow/client.js +399 -320
- package/dist/qmetry/client/api/client-api.js +43 -41
- package/dist/qmetry/client/api/error-handler.js +264 -310
- package/dist/qmetry/client/auto-resolve.js +78 -99
- package/dist/qmetry/client/automation.js +139 -162
- package/dist/qmetry/client/handlers.js +49 -46
- package/dist/qmetry/client/issues.js +133 -115
- package/dist/qmetry/client/project.js +153 -174
- package/dist/qmetry/client/requirement.js +82 -70
- package/dist/qmetry/client/testcase.js +240 -208
- package/dist/qmetry/client/testsuite.js +332 -293
- package/dist/qmetry/client/tools/automation-tools.js +291 -288
- package/dist/qmetry/client/tools/index.js +16 -13
- package/dist/qmetry/client/tools/issue-tools.js +534 -543
- package/dist/qmetry/client/tools/project-tools.js +635 -656
- package/dist/qmetry/client/tools/requirement-tools.js +525 -528
- package/dist/qmetry/client/tools/testcase-tools.js +773 -786
- package/dist/qmetry/client/tools/testsuite-tools.js +1069 -1083
- package/dist/qmetry/client/utils.js +8 -14
- package/dist/qmetry/client.js +111 -109
- package/dist/qmetry/config/constants.js +48 -44
- package/dist/qmetry/config/rest-endpoints.js +51 -48
- package/dist/qmetry/types/automation.js +7 -7
- package/dist/qmetry/types/common.js +763 -1049
- package/dist/qmetry/types/issues.js +26 -19
- package/dist/qmetry/types/project.js +32 -25
- package/dist/qmetry/types/requirements.js +26 -21
- package/dist/qmetry/types/testcase.js +55 -44
- package/dist/qmetry/types/testsuite.js +66 -52
- package/dist/reflect/client.js +284 -226
- package/dist/swagger/client/api.js +645 -662
- package/dist/swagger/client/configuration.js +31 -33
- package/dist/swagger/client/portal-types.js +204 -244
- package/dist/swagger/client/registry-types.js +62 -96
- package/dist/swagger/client/tools.js +148 -158
- package/dist/swagger/client/user-management-types.js +11 -22
- package/dist/swagger/client.js +143 -135
- package/dist/swagger/config-utils.js +10 -16
- package/dist/zephyr/client.js +43 -42
- package/dist/zephyr/common/api-client.js +35 -30
- package/dist/zephyr/common/auth-service.js +16 -13
- package/dist/zephyr/common/rest-api-schemas.js +3173 -5146
- package/dist/zephyr/tool/environment/get-environments.js +66 -66
- package/dist/zephyr/tool/priority/get-priorities.js +41 -41
- package/dist/zephyr/tool/project/get-project.js +37 -37
- package/dist/zephyr/tool/project/get-projects.js +46 -46
- package/dist/zephyr/tool/status/get-statuses.js +47 -47
- package/dist/zephyr/tool/test-case/get-test-case.js +37 -37
- package/dist/zephyr/tool/test-case/get-test-cases.js +62 -62
- package/dist/zephyr/tool/test-cycle/get-test-cycle.js +37 -37
- package/dist/zephyr/tool/test-cycle/get-test-cycles.js +70 -70
- package/dist/zephyr/tool/test-execution/get-test-execution.js +37 -37
- package/dist/zephyr/tool/test-execution/get-test-executions.js +43 -43
- package/package.json +5 -5
- package/dist/bugsnag/client/api/index.js +0 -6
- package/dist/common/types.js +0 -6
- package/dist/qmetry/client/tools/types.js +0 -1
- package/dist/swagger/client/index.js +0 -6
- package/dist/tests/unit/bugsnag/utils/factories.js +0 -86
- package/dist/zephyr/tool/zephyr-tool.js +0 -1
|
@@ -1,673 +1,656 @@
|
|
|
1
|
-
import { ToolError } from "../../common/
|
|
2
|
-
// Regex to extract owner, name, and version from SwaggerHub URLs.
|
|
3
|
-
// Matches /apis/owner/name/version, /domains/owner/name/version, or /templates/owner/name/version
|
|
4
|
-
// Example: /apis/acme/petstore/1.0.0
|
|
5
|
-
// - group 1: type (apis|domains|templates)
|
|
6
|
-
// - group 2: owner
|
|
7
|
-
// - group 3: name
|
|
8
|
-
// - group 4: version
|
|
1
|
+
import { ToolError } from "../../common/tools.js";
|
|
9
2
|
const SWAGGER_URL_REGEX = /\/(apis|domains|templates)\/([^/]+)\/([^/]+)\/([^/]+)/;
|
|
10
|
-
/**
|
|
11
|
-
* Type guard to check if a value has an 'id' property
|
|
12
|
-
*/
|
|
13
3
|
function hasId(value) {
|
|
14
|
-
|
|
4
|
+
return typeof value === "object" && value !== null && "id" in value;
|
|
15
5
|
}
|
|
16
|
-
/**
|
|
17
|
-
* Type guard to check if a value has a 'message' property
|
|
18
|
-
*/
|
|
19
6
|
function hasMessage(value) {
|
|
20
|
-
|
|
7
|
+
return typeof value === "object" && value !== null && "message" in value;
|
|
21
8
|
}
|
|
22
|
-
/**
|
|
23
|
-
* Type guard to check if a value has an 'errorsFound' property
|
|
24
|
-
*/
|
|
25
9
|
function hasErrorsFound(value) {
|
|
26
|
-
|
|
10
|
+
return typeof value === "object" && value !== null && "errorsFound" in value;
|
|
27
11
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
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
|
-
const firstChar = trimmed[0];
|
|
71
|
-
if (firstChar === "{" || firstChar === "[") {
|
|
72
|
-
try {
|
|
73
|
-
return JSON.parse(trimmed);
|
|
74
|
-
}
|
|
75
|
-
catch (error) {
|
|
76
|
-
console.warn("Heuristic JSON parse failed:", error);
|
|
77
|
-
return { message: text };
|
|
78
|
-
}
|
|
79
|
-
}
|
|
12
|
+
class SwaggerAPI {
|
|
13
|
+
config;
|
|
14
|
+
headers;
|
|
15
|
+
constructor(config, userAgent) {
|
|
16
|
+
this.config = config;
|
|
17
|
+
this.headers = config.getHeaders(userAgent);
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Core response parsing logic shared between different response handlers.
|
|
21
|
+
* Handles 204 No Content, empty responses, JSON parsing, and text fallbacks.
|
|
22
|
+
* @template T - Expected JSON response data type
|
|
23
|
+
* @template D - Default return type for empty responses
|
|
24
|
+
* @param response - The fetch Response object
|
|
25
|
+
* @param defaultReturn - Default value to return for empty responses
|
|
26
|
+
* @returns Parsed response data or fallback value
|
|
27
|
+
*/
|
|
28
|
+
async parseResponse(response, defaultReturn = {}) {
|
|
29
|
+
if (response.status === 204) {
|
|
30
|
+
return defaultReturn;
|
|
31
|
+
}
|
|
32
|
+
const contentLength = response.headers.get("content-length");
|
|
33
|
+
if (contentLength === "0") {
|
|
34
|
+
return defaultReturn;
|
|
35
|
+
}
|
|
36
|
+
const contentType = response.headers.get("content-type");
|
|
37
|
+
if (contentType?.includes("application/json")) {
|
|
38
|
+
try {
|
|
39
|
+
return await response.json();
|
|
40
|
+
} catch (error) {
|
|
41
|
+
console.warn("Failed to parse JSON response (declared JSON):", error);
|
|
42
|
+
return defaultReturn;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
const text = await response.text();
|
|
46
|
+
if (!text) return defaultReturn;
|
|
47
|
+
const trimmed = text.trim();
|
|
48
|
+
const firstChar = trimmed[0];
|
|
49
|
+
if (firstChar === "{" || firstChar === "[") {
|
|
50
|
+
try {
|
|
51
|
+
return JSON.parse(trimmed);
|
|
52
|
+
} catch (error) {
|
|
53
|
+
console.warn("Heuristic JSON parse failed:", error);
|
|
80
54
|
return { message: text };
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return { message: text };
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Handles HTTP responses with smart JSON parsing and fallback handling.
|
|
61
|
+
* Includes HTTP error checking before parsing.
|
|
62
|
+
* @template T - Expected response data type
|
|
63
|
+
* @param response - The fetch Response object
|
|
64
|
+
* @param defaultReturn - Default value to return for empty responses
|
|
65
|
+
* @returns Parsed response data or fallback value
|
|
66
|
+
*/
|
|
67
|
+
async handleResponse(response, defaultReturn = {}) {
|
|
68
|
+
if (!response.ok) {
|
|
69
|
+
throw new ToolError(`HTTP ${response.status}: ${response.statusText}`);
|
|
70
|
+
}
|
|
71
|
+
return this.parseResponse(
|
|
72
|
+
response,
|
|
73
|
+
defaultReturn
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
async getPortals() {
|
|
77
|
+
const response = await fetch(`${this.config.portalBasePath}/portals`, {
|
|
78
|
+
method: "GET",
|
|
79
|
+
headers: this.headers
|
|
80
|
+
});
|
|
81
|
+
const result = await this.handleResponse(
|
|
82
|
+
response,
|
|
83
|
+
[]
|
|
84
|
+
);
|
|
85
|
+
return result;
|
|
86
|
+
}
|
|
87
|
+
async getOrganizations(params) {
|
|
88
|
+
const searchParams = new URLSearchParams();
|
|
89
|
+
if (params?.q) searchParams.append("q", params.q);
|
|
90
|
+
if (params?.sortBy) searchParams.append("sortBy", params.sortBy);
|
|
91
|
+
if (params?.order) searchParams.append("order", params.order);
|
|
92
|
+
if (params?.page !== void 0)
|
|
93
|
+
searchParams.append("page", params.page.toString());
|
|
94
|
+
if (params?.pageSize)
|
|
95
|
+
searchParams.append("pageSize", params.pageSize.toString());
|
|
96
|
+
const queryString = searchParams.toString();
|
|
97
|
+
const url = `${this.config.userManagementBasePath}/orgs${queryString ? `?${queryString}` : ""}`;
|
|
98
|
+
const response = await fetch(url, {
|
|
99
|
+
method: "GET",
|
|
100
|
+
headers: this.headers
|
|
101
|
+
});
|
|
102
|
+
const defaultResponse = {
|
|
103
|
+
items: [],
|
|
104
|
+
totalCount: 0,
|
|
105
|
+
pageSize: 50,
|
|
106
|
+
page: 0
|
|
107
|
+
};
|
|
108
|
+
const result = await this.handleResponse(
|
|
109
|
+
response,
|
|
110
|
+
defaultResponse
|
|
111
|
+
);
|
|
112
|
+
return result;
|
|
113
|
+
}
|
|
114
|
+
async createPortal(body) {
|
|
115
|
+
const response = await fetch(`${this.config.portalBasePath}/portals`, {
|
|
116
|
+
method: "POST",
|
|
117
|
+
headers: this.headers,
|
|
118
|
+
body: JSON.stringify(body)
|
|
119
|
+
});
|
|
120
|
+
const result = await this.handleResponse(response);
|
|
121
|
+
if (!hasId(result)) {
|
|
122
|
+
throw new Error("Unexpected empty response creating portal");
|
|
123
|
+
}
|
|
124
|
+
return result;
|
|
125
|
+
}
|
|
126
|
+
async getPortal(portalId) {
|
|
127
|
+
const response = await fetch(
|
|
128
|
+
`${this.config.portalBasePath}/portals/${portalId}`,
|
|
129
|
+
{
|
|
130
|
+
method: "GET",
|
|
131
|
+
headers: this.headers
|
|
132
|
+
}
|
|
133
|
+
);
|
|
134
|
+
const result = await this.handleResponse(response);
|
|
135
|
+
if (!hasId(result)) {
|
|
136
|
+
throw new ToolError("Portal not found or empty response");
|
|
137
|
+
}
|
|
138
|
+
return result;
|
|
139
|
+
}
|
|
140
|
+
async updatePortal(portalId, body) {
|
|
141
|
+
const response = await fetch(
|
|
142
|
+
`${this.config.portalBasePath}/portals/${portalId}`,
|
|
143
|
+
{
|
|
144
|
+
method: "PATCH",
|
|
145
|
+
headers: this.headers,
|
|
146
|
+
body: JSON.stringify(body)
|
|
147
|
+
}
|
|
148
|
+
);
|
|
149
|
+
return this.handleResponse(response);
|
|
150
|
+
}
|
|
151
|
+
async getPortalProducts(portalId) {
|
|
152
|
+
const response = await fetch(
|
|
153
|
+
`${this.config.portalBasePath}/portals/${portalId}/products`,
|
|
154
|
+
{
|
|
155
|
+
method: "GET",
|
|
156
|
+
headers: this.headers
|
|
157
|
+
}
|
|
158
|
+
);
|
|
159
|
+
const result = await this.handleResponse(
|
|
160
|
+
response,
|
|
161
|
+
[]
|
|
162
|
+
);
|
|
163
|
+
return result;
|
|
164
|
+
}
|
|
165
|
+
async createPortalProduct(portalId, body) {
|
|
166
|
+
const response = await fetch(
|
|
167
|
+
`${this.config.portalBasePath}/portals/${portalId}/products`,
|
|
168
|
+
{
|
|
169
|
+
method: "POST",
|
|
170
|
+
headers: this.headers,
|
|
171
|
+
body: JSON.stringify(body)
|
|
172
|
+
}
|
|
173
|
+
);
|
|
174
|
+
const result = await this.handleResponse(response);
|
|
175
|
+
if (!hasId(result)) {
|
|
176
|
+
throw new Error("Unexpected empty response creating product");
|
|
177
|
+
}
|
|
178
|
+
return result;
|
|
179
|
+
}
|
|
180
|
+
async getPortalProduct(productId) {
|
|
181
|
+
const response = await fetch(
|
|
182
|
+
`${this.config.portalBasePath}/products/${productId}`,
|
|
183
|
+
{
|
|
184
|
+
method: "GET",
|
|
185
|
+
headers: this.headers
|
|
186
|
+
}
|
|
187
|
+
);
|
|
188
|
+
const result = await this.handleResponse(response);
|
|
189
|
+
if (!hasId(result)) {
|
|
190
|
+
throw new ToolError("Product not found or empty response");
|
|
191
|
+
}
|
|
192
|
+
return result;
|
|
193
|
+
}
|
|
194
|
+
async deletePortalProduct(productId) {
|
|
195
|
+
const response = await fetch(
|
|
196
|
+
`${this.config.portalBasePath}/products/${productId}`,
|
|
197
|
+
{
|
|
198
|
+
method: "DELETE",
|
|
199
|
+
headers: this.headers
|
|
200
|
+
}
|
|
201
|
+
);
|
|
202
|
+
return this.handleResponse(response);
|
|
203
|
+
}
|
|
204
|
+
async updatePortalProduct(productId, body) {
|
|
205
|
+
const response = await fetch(
|
|
206
|
+
`${this.config.portalBasePath}/products/${productId}`,
|
|
207
|
+
{
|
|
208
|
+
method: "PATCH",
|
|
209
|
+
headers: this.headers,
|
|
210
|
+
body: JSON.stringify(body)
|
|
211
|
+
}
|
|
212
|
+
);
|
|
213
|
+
return this.handleResponse(response, {
|
|
214
|
+
success: true
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
async publishPortalProduct(productId, preview = false) {
|
|
218
|
+
const response = await fetch(
|
|
219
|
+
`${this.config.portalBasePath}/products/${productId}/published-content?preview=${preview}`,
|
|
220
|
+
{
|
|
221
|
+
method: "PUT",
|
|
222
|
+
headers: this.headers
|
|
223
|
+
}
|
|
224
|
+
);
|
|
225
|
+
return this.handleResponse(response, {
|
|
226
|
+
success: true
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
async getPortalProductSections(productId, params) {
|
|
230
|
+
const queryParameters = new URLSearchParams();
|
|
231
|
+
if (params.embed) {
|
|
232
|
+
for (const item of params.embed) {
|
|
233
|
+
queryParameters.append("embed", item);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
if (params.page !== void 0) {
|
|
237
|
+
queryParameters.append("page", params.page.toString());
|
|
238
|
+
}
|
|
239
|
+
if (params.size !== void 0) {
|
|
240
|
+
queryParameters.append("size", params.size.toString());
|
|
241
|
+
}
|
|
242
|
+
const url = `${this.config.portalBasePath}/products/${productId}/sections${queryParameters.toString() ? `?${queryParameters.toString()}` : ""}`;
|
|
243
|
+
const response = await fetch(url, {
|
|
244
|
+
method: "GET",
|
|
245
|
+
headers: this.headers
|
|
246
|
+
});
|
|
247
|
+
const result = await this.handleResponse(
|
|
248
|
+
response,
|
|
249
|
+
[]
|
|
250
|
+
);
|
|
251
|
+
return result;
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Create a new table of contents item in a portal product section
|
|
255
|
+
* @param sectionId - Section ID where the table of contents item will be created
|
|
256
|
+
* @param body - Table of contents creation parameters
|
|
257
|
+
* @returns Created table of contents item with metadata
|
|
258
|
+
*/
|
|
259
|
+
async createTableOfContents(sectionId, body) {
|
|
260
|
+
const url = `${this.config.portalBasePath}/sections/${sectionId}/table-of-contents`;
|
|
261
|
+
const response = await fetch(url, {
|
|
262
|
+
method: "POST",
|
|
263
|
+
headers: this.headers,
|
|
264
|
+
body: JSON.stringify(body)
|
|
265
|
+
});
|
|
266
|
+
if (!response.ok) {
|
|
267
|
+
const errorText = await response.text();
|
|
268
|
+
throw new Error(
|
|
269
|
+
`API Hub createTableOfContents failed - status: ${response.status} ${response.statusText}. Response: ${errorText}`
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
const result = await response.json();
|
|
273
|
+
return result;
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Get table of contents for a section
|
|
277
|
+
* @param args - Parameters for retrieving table of contents
|
|
278
|
+
* @returns List of table of contents items
|
|
279
|
+
*/
|
|
280
|
+
async getTableOfContents(args) {
|
|
281
|
+
const { sectionId, embed, page, size } = args;
|
|
282
|
+
const searchParams = new URLSearchParams();
|
|
283
|
+
if (embed) {
|
|
284
|
+
for (const item of embed) {
|
|
285
|
+
searchParams.append("embed", item);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
if (page !== void 0) {
|
|
289
|
+
searchParams.set("page", page.toString());
|
|
290
|
+
}
|
|
291
|
+
if (size !== void 0) {
|
|
292
|
+
searchParams.set("size", size.toString());
|
|
293
|
+
}
|
|
294
|
+
const url = `${this.config.portalBasePath}/sections/${sectionId}/table-of-contents${searchParams.toString() ? `?${searchParams.toString()}` : ""}`;
|
|
295
|
+
const response = await fetch(url, {
|
|
296
|
+
method: "GET",
|
|
297
|
+
headers: this.headers
|
|
298
|
+
});
|
|
299
|
+
if (!response.ok) {
|
|
300
|
+
const errorText = await response.text();
|
|
301
|
+
throw new Error(
|
|
302
|
+
`API Hub getTableOfContents failed - status: ${response.status} ${response.statusText}. Response: ${errorText}`
|
|
303
|
+
);
|
|
304
|
+
}
|
|
305
|
+
const result = await response.json();
|
|
306
|
+
return result.items;
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Get document content and metadata
|
|
310
|
+
* @param args - Parameters for retrieving document
|
|
311
|
+
* @returns Document with content and metadata
|
|
312
|
+
*/
|
|
313
|
+
async getDocument(args) {
|
|
314
|
+
const { documentId } = args;
|
|
315
|
+
const url = `${this.config.portalBasePath}/documents/${documentId}`;
|
|
316
|
+
const response = await fetch(url, {
|
|
317
|
+
method: "GET",
|
|
318
|
+
headers: this.headers
|
|
319
|
+
});
|
|
320
|
+
if (!response.ok) {
|
|
321
|
+
const errorText = await response.text();
|
|
322
|
+
throw new Error(
|
|
323
|
+
`API Hub getDocument failed - status: ${response.status} ${response.statusText}. Response: ${errorText}`
|
|
324
|
+
);
|
|
325
|
+
}
|
|
326
|
+
const result = await response.json();
|
|
327
|
+
return result;
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Update document content
|
|
331
|
+
* @param args - Parameters for updating document
|
|
332
|
+
* @returns Success response
|
|
333
|
+
*/
|
|
334
|
+
async updateDocument(args) {
|
|
335
|
+
const { documentId, ...body } = args;
|
|
336
|
+
const url = `${this.config.portalBasePath}/documents/${documentId}`;
|
|
337
|
+
const response = await fetch(url, {
|
|
338
|
+
method: "PATCH",
|
|
339
|
+
headers: this.headers,
|
|
340
|
+
body: JSON.stringify(body)
|
|
341
|
+
});
|
|
342
|
+
if (!response.ok) {
|
|
343
|
+
const errorText = await response.text();
|
|
344
|
+
throw new Error(
|
|
345
|
+
`API Hub updateDocument failed - status: ${response.status} ${response.statusText}. Response: ${errorText}`
|
|
346
|
+
);
|
|
347
|
+
}
|
|
348
|
+
return { success: true };
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* Delete table of contents entry
|
|
352
|
+
* @param args - Parameters for deleting table of contents entry
|
|
353
|
+
* @returns Success response
|
|
354
|
+
*/
|
|
355
|
+
async deleteTableOfContents(args) {
|
|
356
|
+
const { tableOfContentsId, recursive } = args;
|
|
357
|
+
const searchParams = new URLSearchParams();
|
|
358
|
+
if (recursive !== void 0) {
|
|
359
|
+
searchParams.set("recursive", recursive.toString());
|
|
360
|
+
}
|
|
361
|
+
const url = `${this.config.portalBasePath}/table-of-contents/${tableOfContentsId}${searchParams.toString() ? `?${searchParams.toString()}` : ""}`;
|
|
362
|
+
const response = await fetch(url, {
|
|
363
|
+
method: "DELETE",
|
|
364
|
+
headers: this.headers
|
|
365
|
+
});
|
|
366
|
+
if (!response.ok) {
|
|
367
|
+
const errorText = await response.text();
|
|
368
|
+
throw new Error(
|
|
369
|
+
`API Hub deleteTableOfContents failed - status: ${response.status} ${response.statusText}. Response: ${errorText}`
|
|
370
|
+
);
|
|
371
|
+
}
|
|
372
|
+
return { success: true };
|
|
373
|
+
}
|
|
374
|
+
/**
|
|
375
|
+
* Helper method for handling responses when error checking is already done.
|
|
376
|
+
* Delegates to parseResponse for the actual parsing logic.
|
|
377
|
+
* @template T - Expected response data type
|
|
378
|
+
* @template D - Default return type
|
|
379
|
+
* @param response - The fetch Response object
|
|
380
|
+
* @param defaultReturn - Default value to return for empty responses
|
|
381
|
+
* @returns Parsed response data or fallback value
|
|
382
|
+
*/
|
|
383
|
+
// Registry API methods for SwaggerHub Design functionality
|
|
384
|
+
/**
|
|
385
|
+
* Search APIs and Domains in SwaggerHub Registry using /specs endpoint
|
|
386
|
+
* @param params Search parameters
|
|
387
|
+
* @returns Array of processed API metadata
|
|
388
|
+
*/
|
|
389
|
+
async searchApis(params = {}) {
|
|
390
|
+
const searchParams = new URLSearchParams();
|
|
391
|
+
if (params.query) searchParams.append("query", params.query);
|
|
392
|
+
if (params.state) searchParams.append("state", params.state);
|
|
393
|
+
if (params.tag) searchParams.append("tag", params.tag);
|
|
394
|
+
if (params.offset !== void 0)
|
|
395
|
+
searchParams.append("offset", params.offset.toString());
|
|
396
|
+
if (params.limit !== void 0)
|
|
397
|
+
searchParams.append("limit", params.limit.toString());
|
|
398
|
+
if (params.sort) searchParams.append("sort", params.sort);
|
|
399
|
+
if (params.order) searchParams.append("order", params.order);
|
|
400
|
+
if (params.owner) searchParams.append("owner", params.owner);
|
|
401
|
+
if (params.specType) searchParams.append("specType", params.specType);
|
|
402
|
+
const url = `${this.config.registryBasePath}/specs${searchParams.toString() ? `?${searchParams.toString()}` : ""}`;
|
|
403
|
+
const response = await fetch(url, {
|
|
404
|
+
method: "GET",
|
|
405
|
+
headers: this.headers
|
|
406
|
+
});
|
|
407
|
+
if (!response.ok) {
|
|
408
|
+
throw new ToolError(
|
|
409
|
+
`SwaggerHub Registry API searchApis failed - status: ${response.status} ${response.statusText}`
|
|
410
|
+
);
|
|
411
|
+
}
|
|
412
|
+
const apisJsonResponse = await response.json();
|
|
413
|
+
return this.transformApisJsonToMetadata(apisJsonResponse.apis);
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* Transform APIs.json specifications to our ApiMetadata format
|
|
417
|
+
* @param specs Array of API specifications from APIs.json
|
|
418
|
+
* @returns Array of processed API metadata
|
|
419
|
+
*/
|
|
420
|
+
transformApisJsonToMetadata(specs) {
|
|
421
|
+
return specs.map((spec) => {
|
|
422
|
+
const properties = spec.properties || [];
|
|
423
|
+
const getProperty = (type) => {
|
|
424
|
+
const property = properties.find((p) => p.type === type);
|
|
425
|
+
return property?.value || property?.url;
|
|
426
|
+
};
|
|
427
|
+
const swaggerUrl = getProperty("Swagger") || "";
|
|
428
|
+
const urlMatch = RegExp(SWAGGER_URL_REGEX).exec(swaggerUrl);
|
|
429
|
+
return {
|
|
430
|
+
owner: urlMatch?.[2] || "",
|
|
431
|
+
name: spec.name || "",
|
|
432
|
+
description: spec.description || "",
|
|
433
|
+
summary: spec.summary || "",
|
|
434
|
+
version: getProperty("X-Version") || urlMatch?.[4] || "",
|
|
435
|
+
specification: getProperty("X-Specification") || "",
|
|
436
|
+
created: getProperty("X-Created"),
|
|
437
|
+
modified: getProperty("X-Modified"),
|
|
438
|
+
published: getProperty("X-Published"),
|
|
439
|
+
private: getProperty("X-Private"),
|
|
440
|
+
oasVersion: getProperty("X-OASVersion"),
|
|
441
|
+
url: swaggerUrl
|
|
442
|
+
};
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
/**
|
|
446
|
+
* Get API definition from SwaggerHub Registry
|
|
447
|
+
* @param params Parameters including owner, api name, version, and options
|
|
448
|
+
* @returns API definition (OpenAPI/Swagger specification)
|
|
449
|
+
*/
|
|
450
|
+
async getApiDefinition(params) {
|
|
451
|
+
const searchParams = new URLSearchParams();
|
|
452
|
+
if (params.resolved !== void 0)
|
|
453
|
+
searchParams.append("resolved", params.resolved.toString());
|
|
454
|
+
if (params.flatten !== void 0)
|
|
455
|
+
searchParams.append("flatten", params.flatten.toString());
|
|
456
|
+
const url = `${this.config.registryBasePath}/apis/${encodeURIComponent(params.owner)}/${encodeURIComponent(params.api)}/${encodeURIComponent(params.version)}${searchParams.toString() ? `?${searchParams.toString()}` : ""}`;
|
|
457
|
+
const response = await fetch(url, {
|
|
458
|
+
method: "GET",
|
|
459
|
+
headers: this.headers
|
|
460
|
+
});
|
|
461
|
+
if (!response.ok) {
|
|
462
|
+
throw new ToolError(
|
|
463
|
+
`SwaggerHub Registry API getApiDefinition failed - status: ${response.status} ${response.statusText}`
|
|
464
|
+
);
|
|
465
|
+
}
|
|
466
|
+
const contentType = response.headers.get("content-type");
|
|
467
|
+
if (contentType?.includes("application/json")) {
|
|
468
|
+
return response.json();
|
|
469
|
+
} else {
|
|
470
|
+
return response.text();
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
/**
|
|
474
|
+
* Create or Update API in SwaggerHub Registry
|
|
475
|
+
* @param params Parameters for creating or updating the API including owner, name, version, specification, and definition
|
|
476
|
+
* @returns Created or updated API metadata with URL. HTTP 201 indicates creation, HTTP 200 indicates update
|
|
477
|
+
*/
|
|
478
|
+
async createOrUpdateApi(params) {
|
|
479
|
+
let contentType;
|
|
480
|
+
let requestBody;
|
|
481
|
+
const format = this.detectDefinitionFormat(params.definition);
|
|
482
|
+
if (format === "yaml") {
|
|
483
|
+
contentType = "application/yaml";
|
|
484
|
+
requestBody = params.definition;
|
|
485
|
+
} else {
|
|
486
|
+
contentType = "application/json";
|
|
487
|
+
try {
|
|
488
|
+
const parsedDefinition = JSON.parse(params.definition);
|
|
489
|
+
requestBody = JSON.stringify(parsedDefinition);
|
|
490
|
+
} catch (error) {
|
|
491
|
+
throw new ToolError(
|
|
492
|
+
`Invalid JSON format in definition: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
493
|
+
);
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
const searchParams = new URLSearchParams();
|
|
497
|
+
searchParams.append("isPrivate", "true");
|
|
498
|
+
const url = `${this.config.registryBasePath}/apis/${encodeURIComponent(
|
|
499
|
+
params.owner
|
|
500
|
+
)}/${encodeURIComponent(params.apiName)}?${searchParams.toString()}`;
|
|
501
|
+
const response = await fetch(url, {
|
|
502
|
+
method: "POST",
|
|
503
|
+
headers: {
|
|
504
|
+
...this.headers,
|
|
505
|
+
"Content-Type": contentType
|
|
506
|
+
},
|
|
507
|
+
body: requestBody
|
|
508
|
+
});
|
|
509
|
+
if (!response.ok) {
|
|
510
|
+
const errorText = await response.text().catch(() => "");
|
|
511
|
+
throw new ToolError(
|
|
512
|
+
`SwaggerHub Registry API createOrUpdateApi failed - status: ${response.status} ${response.statusText}${errorText ? ` - ${errorText}` : ""}. URL: ${url}`
|
|
513
|
+
);
|
|
514
|
+
}
|
|
515
|
+
const operation = response.status === 201 ? "create" : "update";
|
|
516
|
+
const version = response.headers.get("X-Version") || "1.0.0";
|
|
517
|
+
return {
|
|
518
|
+
owner: params.owner,
|
|
519
|
+
apiName: params.apiName,
|
|
520
|
+
version,
|
|
521
|
+
url: `${this.config.uiBasePath}/apis/${params.owner}/${params.apiName}/${version}`,
|
|
522
|
+
operation
|
|
523
|
+
};
|
|
524
|
+
}
|
|
525
|
+
/**
|
|
526
|
+
* Create API from Prompt using SmartBear AI
|
|
527
|
+
* @param params Parameters for creating API from prompt including owner, api name, prompt, and specification type
|
|
528
|
+
* @returns Created API metadata with URL. HTTP 201 indicates creation, HTTP 200 for update, HTTP 205 for reload
|
|
529
|
+
*/
|
|
530
|
+
async createApiFromPrompt(params) {
|
|
531
|
+
const searchParams = new URLSearchParams();
|
|
532
|
+
const specType = params.specType ?? "openapi30x";
|
|
533
|
+
searchParams.append("specType", specType);
|
|
534
|
+
const url = `${this.config.registryBasePath}/apis/${encodeURIComponent(
|
|
535
|
+
params.owner
|
|
536
|
+
)}/${encodeURIComponent(params.apiName)}/.ai?${searchParams.toString()}`;
|
|
537
|
+
const response = await fetch(url, {
|
|
538
|
+
method: "POST",
|
|
539
|
+
headers: {
|
|
540
|
+
...this.headers,
|
|
541
|
+
"Content-Type": "application/json"
|
|
542
|
+
},
|
|
543
|
+
body: JSON.stringify(params.prompt)
|
|
544
|
+
});
|
|
545
|
+
if (!response.ok) {
|
|
546
|
+
const errorText = await response.text().catch(() => "");
|
|
547
|
+
throw new ToolError(
|
|
548
|
+
`SwaggerHub Registry API createApiFromPrompt failed - status: ${response.status} ${response.statusText}${errorText ? ` - ${errorText}` : ""}. URL: ${url}`
|
|
549
|
+
);
|
|
550
|
+
}
|
|
551
|
+
const operation = response.status === 201 ? "create" : "update";
|
|
552
|
+
const version = response.headers.get("X-Version");
|
|
553
|
+
return {
|
|
554
|
+
owner: params.owner,
|
|
555
|
+
apiName: params.apiName,
|
|
556
|
+
specType,
|
|
557
|
+
version: version || void 0,
|
|
558
|
+
url: version ? `${this.config.uiBasePath}/apis/${params.owner}/${params.apiName}/${version}` : `${this.config.uiBasePath}/apis/${params.owner}/${params.apiName}`,
|
|
559
|
+
operation
|
|
560
|
+
};
|
|
561
|
+
}
|
|
562
|
+
/**
|
|
563
|
+
* Auto-detect the format of an API definition string
|
|
564
|
+
* @param definition The API definition content
|
|
565
|
+
* @returns 'json' or 'yaml'
|
|
566
|
+
*/
|
|
567
|
+
detectDefinitionFormat(definition) {
|
|
568
|
+
const trimmed = definition.trim();
|
|
569
|
+
if (!trimmed) {
|
|
570
|
+
throw new ToolError("Empty definition content provided");
|
|
571
|
+
}
|
|
572
|
+
try {
|
|
573
|
+
JSON.parse(trimmed);
|
|
574
|
+
return "json";
|
|
575
|
+
} catch {
|
|
576
|
+
return "yaml";
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
/**
|
|
580
|
+
* Run a standardization scan against an API definition
|
|
581
|
+
* @param params Parameters including organization name and API definition
|
|
582
|
+
* @returns Standardization result with validation errors
|
|
583
|
+
*/
|
|
584
|
+
async scanStandardization(params) {
|
|
585
|
+
const format = this.detectDefinitionFormat(params.definition);
|
|
586
|
+
let contentType;
|
|
587
|
+
let requestBody;
|
|
588
|
+
if (format === "yaml") {
|
|
589
|
+
contentType = "application/yaml";
|
|
590
|
+
requestBody = params.definition;
|
|
591
|
+
} else {
|
|
592
|
+
contentType = "application/json";
|
|
593
|
+
try {
|
|
594
|
+
const parsedDefinition = JSON.parse(params.definition);
|
|
595
|
+
requestBody = JSON.stringify(parsedDefinition);
|
|
596
|
+
} catch (error) {
|
|
597
|
+
throw new ToolError(
|
|
598
|
+
`Invalid JSON format in definition: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
599
|
+
);
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
const url = `${this.config.registryBasePath}/standardization/${encodeURIComponent(
|
|
603
|
+
params.orgName
|
|
604
|
+
)}/scan`;
|
|
605
|
+
const response = await fetch(url, {
|
|
606
|
+
method: "POST",
|
|
607
|
+
headers: {
|
|
608
|
+
...this.headers,
|
|
609
|
+
"Content-Type": contentType
|
|
610
|
+
},
|
|
611
|
+
body: requestBody
|
|
612
|
+
});
|
|
613
|
+
if (!response.ok) {
|
|
614
|
+
const errorText = await response.text().catch(() => "");
|
|
615
|
+
throw new ToolError(
|
|
616
|
+
`SwaggerHub Registry API scanStandardization failed - status: ${response.status} ${response.statusText}${errorText ? ` - ${errorText}` : ""}. URL: ${url}`
|
|
617
|
+
);
|
|
618
|
+
}
|
|
619
|
+
return await this.handleResponse(response);
|
|
620
|
+
}
|
|
621
|
+
/**
|
|
622
|
+
* Standardize and fix an API definition using AI
|
|
623
|
+
* @param params Parameters including owner, API name, and version
|
|
624
|
+
* @returns Standardization response with status and fixed definition
|
|
625
|
+
*/
|
|
626
|
+
async standardizeApi(params) {
|
|
627
|
+
const url = `${this.config.registryBasePath}/apis/${encodeURIComponent(
|
|
628
|
+
params.owner
|
|
629
|
+
)}/${encodeURIComponent(params.api)}/${encodeURIComponent(
|
|
630
|
+
params.version
|
|
631
|
+
)}/standardize`;
|
|
632
|
+
const response = await fetch(url, {
|
|
633
|
+
method: "POST",
|
|
634
|
+
headers: this.headers
|
|
635
|
+
});
|
|
636
|
+
if (!response.ok) {
|
|
637
|
+
const errorText = await response.text().catch(() => "");
|
|
638
|
+
throw new ToolError(
|
|
639
|
+
`SwaggerHub Registry API standardizeApi failed - status: ${response.status} ${response.statusText}${errorText ? ` - ${errorText}` : ""}. URL: ${url}`
|
|
640
|
+
);
|
|
641
|
+
}
|
|
642
|
+
const result = await this.handleResponse(response);
|
|
643
|
+
if (!hasMessage(result)) {
|
|
644
|
+
throw new ToolError(
|
|
645
|
+
"Unexpected response format from standardizeApi endpoint"
|
|
646
|
+
);
|
|
81
647
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
* Includes HTTP error checking before parsing.
|
|
85
|
-
* @template T - Expected response data type
|
|
86
|
-
* @param response - The fetch Response object
|
|
87
|
-
* @param defaultReturn - Default value to return for empty responses
|
|
88
|
-
* @returns Parsed response data or fallback value
|
|
89
|
-
*/
|
|
90
|
-
async handleResponse(response, defaultReturn = {}) {
|
|
91
|
-
if (!response.ok) {
|
|
92
|
-
throw new ToolError(`HTTP ${response.status}: ${response.statusText}`);
|
|
93
|
-
}
|
|
94
|
-
return this.parseResponse(response, defaultReturn);
|
|
95
|
-
}
|
|
96
|
-
async getPortals() {
|
|
97
|
-
const response = await fetch(`${this.config.portalBasePath}/portals`, {
|
|
98
|
-
method: "GET",
|
|
99
|
-
headers: this.headers,
|
|
100
|
-
});
|
|
101
|
-
const result = await this.handleResponse(response, []);
|
|
102
|
-
return result;
|
|
103
|
-
}
|
|
104
|
-
async getOrganizations(params) {
|
|
105
|
-
// Build query string if parameters are provided
|
|
106
|
-
const searchParams = new URLSearchParams();
|
|
107
|
-
if (params?.q)
|
|
108
|
-
searchParams.append("q", params.q);
|
|
109
|
-
if (params?.sortBy)
|
|
110
|
-
searchParams.append("sortBy", params.sortBy);
|
|
111
|
-
if (params?.order)
|
|
112
|
-
searchParams.append("order", params.order);
|
|
113
|
-
if (params?.page !== undefined)
|
|
114
|
-
searchParams.append("page", params.page.toString());
|
|
115
|
-
if (params?.pageSize)
|
|
116
|
-
searchParams.append("pageSize", params.pageSize.toString());
|
|
117
|
-
const queryString = searchParams.toString();
|
|
118
|
-
const url = `${this.config.userManagementBasePath}/orgs${queryString ? `?${queryString}` : ""}`;
|
|
119
|
-
const response = await fetch(url, {
|
|
120
|
-
method: "GET",
|
|
121
|
-
headers: this.headers,
|
|
122
|
-
});
|
|
123
|
-
const defaultResponse = {
|
|
124
|
-
items: [],
|
|
125
|
-
totalCount: 0,
|
|
126
|
-
pageSize: 50,
|
|
127
|
-
page: 0,
|
|
128
|
-
};
|
|
129
|
-
const result = await this.handleResponse(response, defaultResponse);
|
|
130
|
-
return result;
|
|
131
|
-
}
|
|
132
|
-
async createPortal(body) {
|
|
133
|
-
const response = await fetch(`${this.config.portalBasePath}/portals`, {
|
|
134
|
-
method: "POST",
|
|
135
|
-
headers: this.headers,
|
|
136
|
-
body: JSON.stringify(body),
|
|
137
|
-
});
|
|
138
|
-
const result = await this.handleResponse(response);
|
|
139
|
-
if (!hasId(result)) {
|
|
140
|
-
throw new Error("Unexpected empty response creating portal");
|
|
141
|
-
}
|
|
142
|
-
return result;
|
|
143
|
-
}
|
|
144
|
-
async getPortal(portalId) {
|
|
145
|
-
const response = await fetch(`${this.config.portalBasePath}/portals/${portalId}`, {
|
|
146
|
-
method: "GET",
|
|
147
|
-
headers: this.headers,
|
|
148
|
-
});
|
|
149
|
-
const result = await this.handleResponse(response);
|
|
150
|
-
if (!hasId(result)) {
|
|
151
|
-
throw new ToolError("Portal not found or empty response");
|
|
152
|
-
}
|
|
153
|
-
return result;
|
|
154
|
-
}
|
|
155
|
-
async updatePortal(portalId, body) {
|
|
156
|
-
const response = await fetch(`${this.config.portalBasePath}/portals/${portalId}`, {
|
|
157
|
-
method: "PATCH",
|
|
158
|
-
headers: this.headers,
|
|
159
|
-
body: JSON.stringify(body),
|
|
160
|
-
});
|
|
161
|
-
return this.handleResponse(response);
|
|
162
|
-
}
|
|
163
|
-
async getPortalProducts(portalId) {
|
|
164
|
-
const response = await fetch(`${this.config.portalBasePath}/portals/${portalId}/products`, {
|
|
165
|
-
method: "GET",
|
|
166
|
-
headers: this.headers,
|
|
167
|
-
});
|
|
168
|
-
const result = await this.handleResponse(response, []);
|
|
169
|
-
return result;
|
|
170
|
-
}
|
|
171
|
-
async createPortalProduct(portalId, body) {
|
|
172
|
-
const response = await fetch(`${this.config.portalBasePath}/portals/${portalId}/products`, {
|
|
173
|
-
method: "POST",
|
|
174
|
-
headers: this.headers,
|
|
175
|
-
body: JSON.stringify(body),
|
|
176
|
-
});
|
|
177
|
-
const result = await this.handleResponse(response);
|
|
178
|
-
if (!hasId(result)) {
|
|
179
|
-
throw new Error("Unexpected empty response creating product");
|
|
180
|
-
}
|
|
181
|
-
return result;
|
|
182
|
-
}
|
|
183
|
-
async getPortalProduct(productId) {
|
|
184
|
-
const response = await fetch(`${this.config.portalBasePath}/products/${productId}`, {
|
|
185
|
-
method: "GET",
|
|
186
|
-
headers: this.headers,
|
|
187
|
-
});
|
|
188
|
-
const result = await this.handleResponse(response);
|
|
189
|
-
if (!hasId(result)) {
|
|
190
|
-
throw new ToolError("Product not found or empty response");
|
|
191
|
-
}
|
|
192
|
-
return result;
|
|
193
|
-
}
|
|
194
|
-
async deletePortalProduct(productId) {
|
|
195
|
-
const response = await fetch(`${this.config.portalBasePath}/products/${productId}`, {
|
|
196
|
-
method: "DELETE",
|
|
197
|
-
headers: this.headers,
|
|
198
|
-
});
|
|
199
|
-
return this.handleResponse(response);
|
|
200
|
-
}
|
|
201
|
-
async updatePortalProduct(productId, body) {
|
|
202
|
-
const response = await fetch(`${this.config.portalBasePath}/products/${productId}`, {
|
|
203
|
-
method: "PATCH",
|
|
204
|
-
headers: this.headers,
|
|
205
|
-
body: JSON.stringify(body),
|
|
206
|
-
});
|
|
207
|
-
return this.handleResponse(response, {
|
|
208
|
-
success: true,
|
|
209
|
-
});
|
|
210
|
-
}
|
|
211
|
-
async publishPortalProduct(productId, preview = false) {
|
|
212
|
-
const response = await fetch(`${this.config.portalBasePath}/products/${productId}/published-content?preview=${preview}`, {
|
|
213
|
-
method: "PUT",
|
|
214
|
-
headers: this.headers,
|
|
215
|
-
});
|
|
216
|
-
return this.handleResponse(response, {
|
|
217
|
-
success: true,
|
|
218
|
-
});
|
|
219
|
-
}
|
|
220
|
-
async getPortalProductSections(productId, params) {
|
|
221
|
-
const queryParameters = new URLSearchParams();
|
|
222
|
-
if (params.embed) {
|
|
223
|
-
for (const item of params.embed) {
|
|
224
|
-
queryParameters.append("embed", item);
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
if (params.page !== undefined) {
|
|
228
|
-
queryParameters.append("page", params.page.toString());
|
|
229
|
-
}
|
|
230
|
-
if (params.size !== undefined) {
|
|
231
|
-
queryParameters.append("size", params.size.toString());
|
|
232
|
-
}
|
|
233
|
-
const url = `${this.config.portalBasePath}/products/${productId}/sections${queryParameters.toString() ? `?${queryParameters.toString()}` : ""}`;
|
|
234
|
-
const response = await fetch(url, {
|
|
235
|
-
method: "GET",
|
|
236
|
-
headers: this.headers,
|
|
237
|
-
});
|
|
238
|
-
const result = await this.handleResponse(response, []);
|
|
239
|
-
return result;
|
|
240
|
-
}
|
|
241
|
-
/**
|
|
242
|
-
* Create a new table of contents item in a portal product section
|
|
243
|
-
* @param sectionId - Section ID where the table of contents item will be created
|
|
244
|
-
* @param body - Table of contents creation parameters
|
|
245
|
-
* @returns Created table of contents item with metadata
|
|
246
|
-
*/
|
|
247
|
-
async createTableOfContents(sectionId, body) {
|
|
248
|
-
const url = `${this.config.portalBasePath}/sections/${sectionId}/table-of-contents`;
|
|
249
|
-
const response = await fetch(url, {
|
|
250
|
-
method: "POST",
|
|
251
|
-
headers: this.headers,
|
|
252
|
-
body: JSON.stringify(body),
|
|
253
|
-
});
|
|
254
|
-
if (!response.ok) {
|
|
255
|
-
const errorText = await response.text();
|
|
256
|
-
throw new Error(`API Hub createTableOfContents failed - status: ${response.status} ${response.statusText}. Response: ${errorText}`);
|
|
257
|
-
}
|
|
258
|
-
const result = await response.json();
|
|
259
|
-
return result;
|
|
260
|
-
}
|
|
261
|
-
/**
|
|
262
|
-
* Get table of contents for a section
|
|
263
|
-
* @param args - Parameters for retrieving table of contents
|
|
264
|
-
* @returns List of table of contents items
|
|
265
|
-
*/
|
|
266
|
-
async getTableOfContents(args) {
|
|
267
|
-
const { sectionId, embed, page, size } = args;
|
|
268
|
-
const searchParams = new URLSearchParams();
|
|
269
|
-
if (embed) {
|
|
270
|
-
for (const item of embed) {
|
|
271
|
-
searchParams.append("embed", item);
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
if (page !== undefined) {
|
|
275
|
-
searchParams.set("page", page.toString());
|
|
276
|
-
}
|
|
277
|
-
if (size !== undefined) {
|
|
278
|
-
searchParams.set("size", size.toString());
|
|
279
|
-
}
|
|
280
|
-
const url = `${this.config.portalBasePath}/sections/${sectionId}/table-of-contents${searchParams.toString() ? `?${searchParams.toString()}` : ""}`;
|
|
281
|
-
const response = await fetch(url, {
|
|
282
|
-
method: "GET",
|
|
283
|
-
headers: this.headers,
|
|
284
|
-
});
|
|
285
|
-
if (!response.ok) {
|
|
286
|
-
const errorText = await response.text();
|
|
287
|
-
throw new Error(`API Hub getTableOfContents failed - status: ${response.status} ${response.statusText}. Response: ${errorText}`);
|
|
288
|
-
}
|
|
289
|
-
const result = await response.json();
|
|
290
|
-
// The API returns a paginated response, so we extract the items array
|
|
291
|
-
return result.items;
|
|
292
|
-
}
|
|
293
|
-
/**
|
|
294
|
-
* Get document content and metadata
|
|
295
|
-
* @param args - Parameters for retrieving document
|
|
296
|
-
* @returns Document with content and metadata
|
|
297
|
-
*/
|
|
298
|
-
async getDocument(args) {
|
|
299
|
-
const { documentId } = args;
|
|
300
|
-
const url = `${this.config.portalBasePath}/documents/${documentId}`;
|
|
301
|
-
const response = await fetch(url, {
|
|
302
|
-
method: "GET",
|
|
303
|
-
headers: this.headers,
|
|
304
|
-
});
|
|
305
|
-
if (!response.ok) {
|
|
306
|
-
const errorText = await response.text();
|
|
307
|
-
throw new Error(`API Hub getDocument failed - status: ${response.status} ${response.statusText}. Response: ${errorText}`);
|
|
308
|
-
}
|
|
309
|
-
const result = await response.json();
|
|
310
|
-
return result;
|
|
311
|
-
}
|
|
312
|
-
/**
|
|
313
|
-
* Update document content
|
|
314
|
-
* @param args - Parameters for updating document
|
|
315
|
-
* @returns Success response
|
|
316
|
-
*/
|
|
317
|
-
async updateDocument(args) {
|
|
318
|
-
const { documentId, ...body } = args;
|
|
319
|
-
const url = `${this.config.portalBasePath}/documents/${documentId}`;
|
|
320
|
-
const response = await fetch(url, {
|
|
321
|
-
method: "PATCH",
|
|
322
|
-
headers: this.headers,
|
|
323
|
-
body: JSON.stringify(body),
|
|
324
|
-
});
|
|
325
|
-
if (!response.ok) {
|
|
326
|
-
const errorText = await response.text();
|
|
327
|
-
throw new Error(`API Hub updateDocument failed - status: ${response.status} ${response.statusText}. Response: ${errorText}`);
|
|
328
|
-
}
|
|
329
|
-
return { success: true };
|
|
330
|
-
}
|
|
331
|
-
/**
|
|
332
|
-
* Delete table of contents entry
|
|
333
|
-
* @param args - Parameters for deleting table of contents entry
|
|
334
|
-
* @returns Success response
|
|
335
|
-
*/
|
|
336
|
-
async deleteTableOfContents(args) {
|
|
337
|
-
const { tableOfContentsId, recursive } = args;
|
|
338
|
-
const searchParams = new URLSearchParams();
|
|
339
|
-
if (recursive !== undefined) {
|
|
340
|
-
searchParams.set("recursive", recursive.toString());
|
|
341
|
-
}
|
|
342
|
-
const url = `${this.config.portalBasePath}/table-of-contents/${tableOfContentsId}${searchParams.toString() ? `?${searchParams.toString()}` : ""}`;
|
|
343
|
-
const response = await fetch(url, {
|
|
344
|
-
method: "DELETE",
|
|
345
|
-
headers: this.headers,
|
|
346
|
-
});
|
|
347
|
-
if (!response.ok) {
|
|
348
|
-
const errorText = await response.text();
|
|
349
|
-
throw new Error(`API Hub deleteTableOfContents failed - status: ${response.status} ${response.statusText}. Response: ${errorText}`);
|
|
350
|
-
}
|
|
351
|
-
return { success: true };
|
|
352
|
-
}
|
|
353
|
-
/**
|
|
354
|
-
* Helper method for handling responses when error checking is already done.
|
|
355
|
-
* Delegates to parseResponse for the actual parsing logic.
|
|
356
|
-
* @template T - Expected response data type
|
|
357
|
-
* @template D - Default return type
|
|
358
|
-
* @param response - The fetch Response object
|
|
359
|
-
* @param defaultReturn - Default value to return for empty responses
|
|
360
|
-
* @returns Parsed response data or fallback value
|
|
361
|
-
*/
|
|
362
|
-
// Registry API methods for SwaggerHub Design functionality
|
|
363
|
-
/**
|
|
364
|
-
* Search APIs and Domains in SwaggerHub Registry using /specs endpoint
|
|
365
|
-
* @param params Search parameters
|
|
366
|
-
* @returns Array of processed API metadata
|
|
367
|
-
*/
|
|
368
|
-
async searchApis(params = {}) {
|
|
369
|
-
const searchParams = new URLSearchParams();
|
|
370
|
-
if (params.query)
|
|
371
|
-
searchParams.append("query", params.query);
|
|
372
|
-
if (params.state)
|
|
373
|
-
searchParams.append("state", params.state);
|
|
374
|
-
if (params.tag)
|
|
375
|
-
searchParams.append("tag", params.tag);
|
|
376
|
-
if (params.offset !== undefined)
|
|
377
|
-
searchParams.append("offset", params.offset.toString());
|
|
378
|
-
if (params.limit !== undefined)
|
|
379
|
-
searchParams.append("limit", params.limit.toString());
|
|
380
|
-
if (params.sort)
|
|
381
|
-
searchParams.append("sort", params.sort);
|
|
382
|
-
if (params.order)
|
|
383
|
-
searchParams.append("order", params.order);
|
|
384
|
-
if (params.owner)
|
|
385
|
-
searchParams.append("owner", params.owner);
|
|
386
|
-
if (params.specType)
|
|
387
|
-
searchParams.append("specType", params.specType);
|
|
388
|
-
const url = `${this.config.registryBasePath}/specs${searchParams.toString() ? `?${searchParams.toString()}` : ""}`;
|
|
389
|
-
const response = await fetch(url, {
|
|
390
|
-
method: "GET",
|
|
391
|
-
headers: this.headers,
|
|
392
|
-
});
|
|
393
|
-
if (!response.ok) {
|
|
394
|
-
throw new ToolError(`SwaggerHub Registry API searchApis failed - status: ${response.status} ${response.statusText}`);
|
|
395
|
-
}
|
|
396
|
-
const apisJsonResponse = (await response.json());
|
|
397
|
-
// Transform APIs.json response to our ApiMetadata format
|
|
398
|
-
return this.transformApisJsonToMetadata(apisJsonResponse.apis);
|
|
399
|
-
}
|
|
400
|
-
/**
|
|
401
|
-
* Transform APIs.json specifications to our ApiMetadata format
|
|
402
|
-
* @param specs Array of API specifications from APIs.json
|
|
403
|
-
* @returns Array of processed API metadata
|
|
404
|
-
*/
|
|
405
|
-
transformApisJsonToMetadata(specs) {
|
|
406
|
-
return specs.map((spec) => {
|
|
407
|
-
// Extract useful properties from the properties array
|
|
408
|
-
const properties = spec.properties || [];
|
|
409
|
-
const getProperty = (type) => {
|
|
410
|
-
const property = properties.find((p) => p.type === type);
|
|
411
|
-
return property?.value || property?.url;
|
|
412
|
-
};
|
|
413
|
-
// Extract owner, name, and version from the Swagger URL using the regex constant
|
|
414
|
-
const swaggerUrl = getProperty("Swagger") || "";
|
|
415
|
-
const urlMatch = RegExp(SWAGGER_URL_REGEX).exec(swaggerUrl);
|
|
416
|
-
return {
|
|
417
|
-
owner: urlMatch?.[2] || "",
|
|
418
|
-
name: spec.name || "",
|
|
419
|
-
description: spec.description || "",
|
|
420
|
-
summary: spec.summary || "",
|
|
421
|
-
version: getProperty("X-Version") || urlMatch?.[4] || "",
|
|
422
|
-
specification: getProperty("X-Specification") || "",
|
|
423
|
-
created: getProperty("X-Created"),
|
|
424
|
-
modified: getProperty("X-Modified"),
|
|
425
|
-
published: getProperty("X-Published"),
|
|
426
|
-
private: getProperty("X-Private"),
|
|
427
|
-
oasVersion: getProperty("X-OASVersion"),
|
|
428
|
-
url: swaggerUrl,
|
|
429
|
-
};
|
|
430
|
-
});
|
|
431
|
-
}
|
|
432
|
-
/**
|
|
433
|
-
* Get API definition from SwaggerHub Registry
|
|
434
|
-
* @param params Parameters including owner, api name, version, and options
|
|
435
|
-
* @returns API definition (OpenAPI/Swagger specification)
|
|
436
|
-
*/
|
|
437
|
-
async getApiDefinition(params) {
|
|
438
|
-
const searchParams = new URLSearchParams();
|
|
439
|
-
if (params.resolved !== undefined)
|
|
440
|
-
searchParams.append("resolved", params.resolved.toString());
|
|
441
|
-
if (params.flatten !== undefined)
|
|
442
|
-
searchParams.append("flatten", params.flatten.toString());
|
|
443
|
-
const url = `${this.config.registryBasePath}/apis/${encodeURIComponent(params.owner)}/${encodeURIComponent(params.api)}/${encodeURIComponent(params.version)}${searchParams.toString() ? `?${searchParams.toString()}` : ""}`;
|
|
444
|
-
const response = await fetch(url, {
|
|
445
|
-
method: "GET",
|
|
446
|
-
headers: this.headers,
|
|
447
|
-
});
|
|
448
|
-
if (!response.ok) {
|
|
449
|
-
throw new ToolError(`SwaggerHub Registry API getApiDefinition failed - status: ${response.status} ${response.statusText}`);
|
|
450
|
-
}
|
|
451
|
-
// Return the raw API definition (could be JSON or YAML)
|
|
452
|
-
const contentType = response.headers.get("content-type");
|
|
453
|
-
if (contentType?.includes("application/json")) {
|
|
454
|
-
return response.json();
|
|
455
|
-
}
|
|
456
|
-
else {
|
|
457
|
-
return response.text();
|
|
458
|
-
}
|
|
459
|
-
}
|
|
460
|
-
/**
|
|
461
|
-
* Create or Update API in SwaggerHub Registry
|
|
462
|
-
* @param params Parameters for creating or updating the API including owner, name, version, specification, and definition
|
|
463
|
-
* @returns Created or updated API metadata with URL. HTTP 201 indicates creation, HTTP 200 indicates update
|
|
464
|
-
*/
|
|
465
|
-
async createOrUpdateApi(params) {
|
|
466
|
-
// Determine the format of the definition
|
|
467
|
-
let contentType;
|
|
468
|
-
let requestBody;
|
|
469
|
-
// Auto-detect format from the definition content
|
|
470
|
-
const format = this.detectDefinitionFormat(params.definition);
|
|
471
|
-
if (format === "yaml") {
|
|
472
|
-
contentType = "application/yaml";
|
|
473
|
-
requestBody = params.definition; // Send YAML as-is
|
|
474
|
-
}
|
|
475
|
-
else {
|
|
476
|
-
contentType = "application/json";
|
|
477
|
-
// For JSON, parse and stringify to ensure valid JSON
|
|
478
|
-
try {
|
|
479
|
-
const parsedDefinition = JSON.parse(params.definition);
|
|
480
|
-
requestBody = JSON.stringify(parsedDefinition);
|
|
481
|
-
}
|
|
482
|
-
catch (error) {
|
|
483
|
-
throw new ToolError(`Invalid JSON format in definition: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
484
|
-
}
|
|
485
|
-
}
|
|
486
|
-
// Construct the URL with query parameters
|
|
487
|
-
// Fixed values: visibility=private, automock=false, version=1.0.0
|
|
488
|
-
const searchParams = new URLSearchParams();
|
|
489
|
-
searchParams.append("isPrivate", "true");
|
|
490
|
-
const url = `${this.config.registryBasePath}/apis/${encodeURIComponent(params.owner)}/${encodeURIComponent(params.apiName)}?${searchParams.toString()}`;
|
|
491
|
-
// Use POST method with the appropriate content type
|
|
492
|
-
const response = await fetch(url, {
|
|
493
|
-
method: "POST",
|
|
494
|
-
headers: {
|
|
495
|
-
...this.headers,
|
|
496
|
-
"Content-Type": contentType,
|
|
497
|
-
},
|
|
498
|
-
body: requestBody,
|
|
499
|
-
});
|
|
500
|
-
if (!response.ok) {
|
|
501
|
-
const errorText = await response.text().catch(() => "");
|
|
502
|
-
throw new ToolError(`SwaggerHub Registry API createOrUpdateApi failed - status: ${response.status} ${response.statusText}${errorText ? ` - ${errorText}` : ""}. URL: ${url}`);
|
|
503
|
-
}
|
|
504
|
-
// Determine operation type based on HTTP status code
|
|
505
|
-
const operation = response.status === 201 ? "create" : "update";
|
|
506
|
-
// Return formatted response with the required fields
|
|
507
|
-
// Fixed version is always 1.0.0
|
|
508
|
-
return {
|
|
509
|
-
owner: params.owner,
|
|
510
|
-
apiName: params.apiName,
|
|
511
|
-
version: "1.0.0",
|
|
512
|
-
url: `${this.config.uiBasePath}/apis/${params.owner}/${params.apiName}/1.0.0`,
|
|
513
|
-
operation,
|
|
514
|
-
};
|
|
515
|
-
}
|
|
516
|
-
/**
|
|
517
|
-
* Create API from Template in SwaggerHub Registry
|
|
518
|
-
* @param params Parameters for creating API from template including owner, api name, and template
|
|
519
|
-
* @returns Created API metadata with URL. HTTP 201 indicates creation, HTTP 200 indicates update
|
|
520
|
-
*/
|
|
521
|
-
async createApiFromTemplate(params) {
|
|
522
|
-
// Construct the URL with query parameters
|
|
523
|
-
// Fixed values: visibility=private, no project, noReconcile=false
|
|
524
|
-
const searchParams = new URLSearchParams();
|
|
525
|
-
searchParams.append("isPrivate", "true");
|
|
526
|
-
searchParams.append("template", params.template);
|
|
527
|
-
const url = `${this.config.registryBasePath}/apis/${encodeURIComponent(params.owner)}/${encodeURIComponent(params.apiName)}/.template?${searchParams.toString()}`;
|
|
528
|
-
// Use POST method for template creation
|
|
529
|
-
const response = await fetch(url, {
|
|
530
|
-
method: "POST",
|
|
531
|
-
headers: this.headers,
|
|
532
|
-
});
|
|
533
|
-
if (!response.ok) {
|
|
534
|
-
const errorText = await response.text().catch(() => "");
|
|
535
|
-
throw new ToolError(`SwaggerHub Registry API createApiFromTemplate failed - status: ${response.status} ${response.statusText}${errorText ? ` - ${errorText}` : ""}. URL: ${url}`);
|
|
536
|
-
}
|
|
537
|
-
// Determine operation type based on HTTP status code
|
|
538
|
-
const operation = response.status === 201 ? "create" : "update";
|
|
539
|
-
// Return formatted response with the required fields
|
|
540
|
-
return {
|
|
541
|
-
owner: params.owner,
|
|
542
|
-
apiName: params.apiName,
|
|
543
|
-
template: params.template,
|
|
544
|
-
url: `${this.config.uiBasePath}/apis/${params.owner}/${params.apiName}`,
|
|
545
|
-
operation,
|
|
546
|
-
};
|
|
547
|
-
}
|
|
548
|
-
/**
|
|
549
|
-
* Create API from Prompt using SmartBear AI
|
|
550
|
-
* @param params Parameters for creating API from prompt including owner, api name, prompt, and specification type
|
|
551
|
-
* @returns Created API metadata with URL. HTTP 201 indicates creation, HTTP 200 for update, HTTP 205 for reload
|
|
552
|
-
*/
|
|
553
|
-
async createApiFromPrompt(params) {
|
|
554
|
-
// Construct the URL with query parameters
|
|
555
|
-
const searchParams = new URLSearchParams();
|
|
556
|
-
const specType = params.specType ?? "openapi30x";
|
|
557
|
-
searchParams.append("specType", specType);
|
|
558
|
-
const url = `${this.config.registryBasePath}/apis/${encodeURIComponent(params.owner)}/${encodeURIComponent(params.apiName)}/.ai?${searchParams.toString()}`;
|
|
559
|
-
// Use POST method with JSON body containing the prompt
|
|
560
|
-
const response = await fetch(url, {
|
|
561
|
-
method: "POST",
|
|
562
|
-
headers: {
|
|
563
|
-
...this.headers,
|
|
564
|
-
"Content-Type": "application/json",
|
|
565
|
-
},
|
|
566
|
-
body: JSON.stringify(params.prompt),
|
|
567
|
-
});
|
|
568
|
-
if (!response.ok) {
|
|
569
|
-
const errorText = await response.text().catch(() => "");
|
|
570
|
-
throw new ToolError(`SwaggerHub Registry API createApiFromPrompt failed - status: ${response.status} ${response.statusText}${errorText ? ` - ${errorText}` : ""}. URL: ${url}`);
|
|
571
|
-
}
|
|
572
|
-
// Determine operation type based on HTTP status code
|
|
573
|
-
// 201 = new API created, 200 = existing API updated, 205 = API saved and should be reloaded
|
|
574
|
-
const operation = response.status === 201 ? "create" : "update";
|
|
575
|
-
// Extract version from X-Version header
|
|
576
|
-
const version = response.headers.get("X-Version");
|
|
577
|
-
// Return formatted response with the required fields
|
|
578
|
-
return {
|
|
579
|
-
owner: params.owner,
|
|
580
|
-
apiName: params.apiName,
|
|
581
|
-
specType: specType,
|
|
582
|
-
version: version || undefined,
|
|
583
|
-
url: version
|
|
584
|
-
? `${this.config.uiBasePath}/apis/${params.owner}/${params.apiName}/${version}`
|
|
585
|
-
: `${this.config.uiBasePath}/apis/${params.owner}/${params.apiName}`,
|
|
586
|
-
operation,
|
|
587
|
-
};
|
|
588
|
-
}
|
|
589
|
-
/**
|
|
590
|
-
* Auto-detect the format of an API definition string
|
|
591
|
-
* @param definition The API definition content
|
|
592
|
-
* @returns 'json' or 'yaml'
|
|
593
|
-
*/
|
|
594
|
-
detectDefinitionFormat(definition) {
|
|
595
|
-
const trimmed = definition.trim();
|
|
596
|
-
if (!trimmed) {
|
|
597
|
-
throw new ToolError("Empty definition content provided");
|
|
598
|
-
}
|
|
599
|
-
try {
|
|
600
|
-
JSON.parse(trimmed);
|
|
601
|
-
return "json";
|
|
602
|
-
}
|
|
603
|
-
catch {
|
|
604
|
-
return "yaml";
|
|
605
|
-
}
|
|
606
|
-
}
|
|
607
|
-
/**
|
|
608
|
-
* Run a standardization scan against an API definition
|
|
609
|
-
* @param params Parameters including organization name and API definition
|
|
610
|
-
* @returns Standardization result with validation errors
|
|
611
|
-
*/
|
|
612
|
-
async scanStandardization(params) {
|
|
613
|
-
// Auto-detect format from the definition content
|
|
614
|
-
const format = this.detectDefinitionFormat(params.definition);
|
|
615
|
-
let contentType;
|
|
616
|
-
let requestBody;
|
|
617
|
-
if (format === "yaml") {
|
|
618
|
-
contentType = "application/yaml";
|
|
619
|
-
requestBody = params.definition; // Send YAML as-is
|
|
620
|
-
}
|
|
621
|
-
else {
|
|
622
|
-
contentType = "application/json";
|
|
623
|
-
// For JSON, parse and stringify to ensure valid JSON
|
|
624
|
-
try {
|
|
625
|
-
const parsedDefinition = JSON.parse(params.definition);
|
|
626
|
-
requestBody = JSON.stringify(parsedDefinition);
|
|
627
|
-
}
|
|
628
|
-
catch (error) {
|
|
629
|
-
throw new ToolError(`Invalid JSON format in definition: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
630
|
-
}
|
|
631
|
-
}
|
|
632
|
-
const url = `${this.config.registryBasePath}/standardization/${encodeURIComponent(params.orgName)}/scan`;
|
|
633
|
-
const response = await fetch(url, {
|
|
634
|
-
method: "POST",
|
|
635
|
-
headers: {
|
|
636
|
-
...this.headers,
|
|
637
|
-
"Content-Type": contentType,
|
|
638
|
-
},
|
|
639
|
-
body: requestBody,
|
|
640
|
-
});
|
|
641
|
-
if (!response.ok) {
|
|
642
|
-
const errorText = await response.text().catch(() => "");
|
|
643
|
-
throw new ToolError(`SwaggerHub Registry API scanStandardization failed - status: ${response.status} ${response.statusText}${errorText ? ` - ${errorText}` : ""}. URL: ${url}`);
|
|
644
|
-
}
|
|
645
|
-
return await this.handleResponse(response);
|
|
646
|
-
}
|
|
647
|
-
/**
|
|
648
|
-
* Standardize and fix an API definition using AI
|
|
649
|
-
* @param params Parameters including owner, API name, and version
|
|
650
|
-
* @returns Standardization response with status and fixed definition
|
|
651
|
-
*/
|
|
652
|
-
async standardizeApi(params) {
|
|
653
|
-
const url = `${this.config.registryBasePath}/apis/${encodeURIComponent(params.owner)}/${encodeURIComponent(params.api)}/${encodeURIComponent(params.version)}/standardize`;
|
|
654
|
-
const response = await fetch(url, {
|
|
655
|
-
method: "POST",
|
|
656
|
-
headers: this.headers,
|
|
657
|
-
});
|
|
658
|
-
if (!response.ok) {
|
|
659
|
-
const errorText = await response.text().catch(() => "");
|
|
660
|
-
throw new ToolError(`SwaggerHub Registry API standardizeApi failed - status: ${response.status} ${response.statusText}${errorText ? ` - ${errorText}` : ""}. URL: ${url}`);
|
|
661
|
-
}
|
|
662
|
-
const result = await this.handleResponse(response);
|
|
663
|
-
// Validate that we have the expected response structure
|
|
664
|
-
if (!hasMessage(result)) {
|
|
665
|
-
throw new ToolError("Unexpected response format from standardizeApi endpoint");
|
|
666
|
-
}
|
|
667
|
-
// If errorsFound is not present, default to 0 (no errors found)
|
|
668
|
-
if (!hasErrorsFound(result)) {
|
|
669
|
-
return { ...result, errorsFound: 0 };
|
|
670
|
-
}
|
|
671
|
-
return result;
|
|
648
|
+
if (!hasErrorsFound(result)) {
|
|
649
|
+
return { ...result, errorsFound: 0 };
|
|
672
650
|
}
|
|
651
|
+
return result;
|
|
652
|
+
}
|
|
673
653
|
}
|
|
654
|
+
export {
|
|
655
|
+
SwaggerAPI
|
|
656
|
+
};
|