@smartbear/mcp 0.7.0 → 0.9.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.
Files changed (52) hide show
  1. package/README.md +19 -2
  2. package/dist/api-hub/client/api.js +198 -23
  3. package/dist/api-hub/client/registry-types.js +22 -0
  4. package/dist/api-hub/client/tools.js +19 -1
  5. package/dist/api-hub/client.js +9 -0
  6. package/dist/bugsnag/client/api/CurrentUser.js +13 -50
  7. package/dist/bugsnag/client/api/Error.js +30 -155
  8. package/dist/bugsnag/client/api/Project.js +56 -124
  9. package/dist/bugsnag/client/api/api.js +3743 -0
  10. package/dist/bugsnag/client/api/base.js +107 -32
  11. package/dist/bugsnag/client/api/configuration.js +26 -0
  12. package/dist/bugsnag/client/api/index.js +2 -0
  13. package/dist/bugsnag/client/filters.js +28 -0
  14. package/dist/bugsnag/client.js +157 -325
  15. package/dist/common/server.js +29 -4
  16. package/dist/common/types.js +6 -1
  17. package/dist/index.js +8 -1
  18. package/dist/pactflow/client/prompt-utils.js +2 -1
  19. package/dist/pactflow/client/tools.js +9 -9
  20. package/dist/pactflow/client/utils.js +5 -4
  21. package/dist/pactflow/client.js +16 -14
  22. package/dist/qmetry/client/api/client-api.js +21 -16
  23. package/dist/qmetry/client/api/error-handler.js +329 -0
  24. package/dist/qmetry/client/auto-resolve.js +74 -0
  25. package/dist/qmetry/client/handlers.js +19 -2
  26. package/dist/qmetry/client/issues.js +26 -0
  27. package/dist/qmetry/client/project.js +56 -0
  28. package/dist/qmetry/client/requirement.js +76 -0
  29. package/dist/qmetry/client/testcase.js +46 -8
  30. package/dist/qmetry/client/testsuite.js +117 -0
  31. package/dist/qmetry/client/tools.js +1455 -4
  32. package/dist/qmetry/client/utils.js +16 -0
  33. package/dist/qmetry/client.js +19 -16
  34. package/dist/qmetry/config/constants.js +14 -0
  35. package/dist/qmetry/config/rest-endpoints.js +20 -0
  36. package/dist/qmetry/types/common.js +313 -8
  37. package/dist/qmetry/types/issues.js +6 -0
  38. package/dist/qmetry/types/project.js +10 -0
  39. package/dist/qmetry/types/requirements.js +19 -0
  40. package/dist/qmetry/types/testcase.js +14 -0
  41. package/dist/qmetry/types/testsuite.js +26 -0
  42. package/dist/reflect/client.js +7 -6
  43. package/dist/zephyr/client.js +16 -0
  44. package/dist/zephyr/common/api-client.js +27 -0
  45. package/dist/zephyr/common/auth-service.js +15 -0
  46. package/dist/zephyr/common/types.js +35 -0
  47. package/dist/zephyr/tool/project/get-projects.js +54 -0
  48. package/dist/zephyr/tool/zephyr-tool.js +1 -0
  49. package/package.json +3 -2
  50. package/dist/bugsnag/client/api/filters.js +0 -167
  51. package/dist/bugsnag/client/configuration.js +0 -10
  52. package/dist/bugsnag/client/index.js +0 -2
@@ -0,0 +1,329 @@
1
+ /**
2
+ * QMetry API Error Handling Utilities
3
+ *
4
+ * This module provides centralized error handling for QMetry API operations,
5
+ * including user-friendly error messages and troubleshooting guidance.
6
+ *
7
+ * Handles common error scenarios:
8
+ * - SSL/TLS Certificate errors (corporate proxies like Zscaler)
9
+ * - Authentication/Authorization failures
10
+ * - CORS (Cross-Origin Resource Sharing) issues
11
+ * - Project access errors
12
+ * - Network connectivity problems
13
+ * - Generic API errors with helpful troubleshooting
14
+ */
15
+ /**
16
+ * Error message templates for common QMetry API issues
17
+ */
18
+ const ERROR_TEMPLATES = {
19
+ AUTHENTICATION_FAILED: (baseUrl, errorText) => `QMetry API Authentication Failed: Invalid or expired API key.\n\n` +
20
+ `To resolve this issue:\n` +
21
+ `1. Log into your QMetry Test Management instance: ${baseUrl}\n` +
22
+ `2. Search Open API → Go To Open API Page\n` +
23
+ `3. Generate a new API key OR copy an existing valid key\n` +
24
+ `4. Copy the API key to your clipboard\n` +
25
+ `5. Restart VS Code or reload the MCP server\n` +
26
+ `6. When prompted, paste the new API key for 'QMetry Open API Key'\n\n` +
27
+ `Note: API keys may expire or be revoked. Always use the latest key from your QMetry instance.\n` +
28
+ `Original error: ${errorText}`,
29
+ AUTHORIZATION_ERROR: (errorText) => `QMetry Authorization Error: Insufficient permissions.\n\n` +
30
+ `Your API key is valid but lacks the necessary permissions for this operation.\n\n` +
31
+ `Possible causes:\n` +
32
+ `1. Your user role doesn't have access to this resource\n` +
33
+ `2. The project permissions are restricted\n` +
34
+ `3. The specific operation requires higher privileges\n\n` +
35
+ `To resolve:\n` +
36
+ `1. Contact your QMetry administrator to review your permissions\n` +
37
+ `2. Verify you have the correct project access\n` +
38
+ `3. Check if your user role includes the required privileges\n\n` +
39
+ `Original error: ${errorText}`,
40
+ PROJECT_ACCESS_ERROR: (project, errorText) => `QMetry Project Access Error: Cannot access project '${project}'.\n\n` +
41
+ `Possible causes:\n` +
42
+ `1. API key lacks permissions for this project\n` +
43
+ `2. Project key '${project}' doesn't exist or is archived\n` +
44
+ `3. Your user account doesn't have access to this project\n\n` +
45
+ `To resolve:\n` +
46
+ `1. Verify the project key is correct\n` +
47
+ `2. Check your QMetry permissions for this project\n` +
48
+ `3. Contact your QMetry administrator if needed\n\n` +
49
+ `Original error: ${errorText}`,
50
+ GENERIC_API_ERROR: (status, baseUrl, errorText) => `QMetry API request failed (${status}): ${errorText}\n\n` +
51
+ `Troubleshooting tips:\n` +
52
+ `Verify your QMetry instance URL: ${baseUrl}\n` +
53
+ `Check if your API key is valid and not expired\n` +
54
+ `Ensure you have the necessary permissions\n` +
55
+ `Try accessing QMetry directly in your browser to confirm connectivity`,
56
+ CORS_ERROR: (baseUrl, errorText) => `QMetry API CORS Error: Cross-Origin Request Blocked.\n\n` +
57
+ `CORS (Cross-Origin Resource Sharing) issue detected:\n` +
58
+ `Your browser is blocking the request due to CORS policy\n` +
59
+ `The QMetry server may not be configured to allow requests from this origin\n\n` +
60
+ `Possible solutions:\n` +
61
+ `1. Contact your QMetry administrator to configure CORS headers\n` +
62
+ `2. Ensure the QMetry instance URL is correct: ${baseUrl}\n` +
63
+ `3. If using a proxy, verify proxy CORS configuration\n` +
64
+ `4. Try accessing QMetry from the same domain/protocol\n` +
65
+ `5. Check if browser extensions are blocking the request\n\n` +
66
+ `Technical details: ${errorText}`,
67
+ SSL_CERTIFICATE_ERROR: (baseUrl, errorText) => `QMetry API SSL Certificate Error: Unable to verify certificate.\n\n` +
68
+ `SSL/TLS Certificate verification failed - Common in corporate networks:\n` +
69
+ `• Corporate proxy/firewall (Zscaler, Forcepoint, etc.) is intercepting HTTPS traffic\n` +
70
+ `• Self-signed or corporate certificates are being used\n` +
71
+ `• Certificate chain validation is failing\n\n` +
72
+ `Solution for corporate network environments:\n` +
73
+ `Contact your IT administrator to:\n` +
74
+ `• Add QMetry domain to certificate bypass list\n` +
75
+ `• Configure corporate certificates properly\n` +
76
+ `• Whitelist the QMetry API endpoints\n` +
77
+ `Target URL: ${baseUrl}\n` +
78
+ `Technical details: ${errorText}`,
79
+ INVALID_URL_ERROR: (baseUrl, path, errorText) => `QMetry API Invalid URL Error: The API endpoint appears to be incorrect.\n\n` +
80
+ `Request details:\n` +
81
+ `• Base URL: ${baseUrl}\n` +
82
+ `• API Path: ${path}\n` +
83
+ `• Full URL: ${baseUrl}${path}\n\n` +
84
+ `Common URL issues:\n` +
85
+ `1. Wrong API endpoint path (check QMetry API documentation)\n` +
86
+ `2. Typo in the URL path (missing '/', wrong spelling)\n` +
87
+ `3. API version mismatch (v1, v2, etc.)\n` +
88
+ `4. Incorrect base URL (should end with QMetry instance domain)\n` +
89
+ `5. Body stream errors (often caused by malformed URLs or wrong HTTP method)\n\n` +
90
+ `Troubleshooting steps:\n` +
91
+ `1. Verify the API endpoint in QMetry documentation\n` +
92
+ `2. Check the QMetry instance URL is correct\n` +
93
+ `3. Test the endpoint manually using a REST client\n` +
94
+ `4. Ensure the API path matches the expected format\n` +
95
+ `5. If you see 'Body is unusable' errors, check for URL typos or wrong endpoints\n\n` +
96
+ `Expected URL format: https://your-qmetry-instance.com/rest/...\n` +
97
+ `Server response: ${errorText}`,
98
+ };
99
+ /**
100
+ * Checks if the error is related to authentication (invalid/expired API key)
101
+ */
102
+ function isAuthenticationError(context) {
103
+ const { status, errorData } = context;
104
+ return (status === 401 ||
105
+ errorData?.code === "CO.INVALID_API_KEY" ||
106
+ errorData?.message?.toLowerCase().includes("invalid api key"));
107
+ }
108
+ /**
109
+ * Checks if the error is related to authorization (insufficient permissions)
110
+ */
111
+ function isAuthorizationError(context) {
112
+ const { status } = context;
113
+ return status === 403;
114
+ }
115
+ /**
116
+ * Checks if the error is related to project access
117
+ */
118
+ function isProjectAccessError(context) {
119
+ const { status, path } = context;
120
+ return status === 404 && (path?.includes("project") ?? false);
121
+ }
122
+ /**
123
+ * Checks if the error is related to CORS (Cross-Origin Resource Sharing)
124
+ */
125
+ function isCorsError(context) {
126
+ const { status, errorText, isCorsError } = context;
127
+ // Explicit CORS flag from fetch error
128
+ if (isCorsError) {
129
+ return true;
130
+ }
131
+ // Common CORS indicators
132
+ const corsIndicators = [
133
+ "cors",
134
+ "cross-origin",
135
+ "cross origin",
136
+ "preflight",
137
+ "access-control-allow-origin",
138
+ "network error when attempting to fetch resource",
139
+ "failed to fetch",
140
+ ];
141
+ const lowercaseErrorText = errorText.toLowerCase();
142
+ // Status 0 often indicates CORS issues
143
+ if (status === 0) {
144
+ return true;
145
+ }
146
+ // Check error text for CORS-related keywords
147
+ return corsIndicators.some((indicator) => lowercaseErrorText.includes(indicator));
148
+ }
149
+ /**
150
+ * Checks if the error is related to SSL/TLS certificate issues
151
+ */
152
+ function isSslCertificateError(context) {
153
+ const { errorText } = context;
154
+ // Common SSL certificate error indicators
155
+ const sslErrorIndicators = [
156
+ "unable to get local issuer certificate",
157
+ "unable_to_get_issuer_cert_locally",
158
+ "self signed certificate",
159
+ "certificate verify failed",
160
+ "ssl certificate problem",
161
+ "certificate has expired",
162
+ "certificate authority is invalid",
163
+ "untrusted certificate",
164
+ "cert authority invalid",
165
+ "tls certificate verification failed",
166
+ "certificate validation error",
167
+ ];
168
+ const lowercaseErrorText = errorText.toLowerCase();
169
+ // Check for specific error codes that indicate SSL issues
170
+ if (lowercaseErrorText.includes("unable_to_get_issuer_cert_locally") ||
171
+ lowercaseErrorText.includes("cert_untrusted") ||
172
+ lowercaseErrorText.includes("cert_authority_invalid") ||
173
+ lowercaseErrorText.includes("certificate verify failed")) {
174
+ return true;
175
+ }
176
+ // Check error text for SSL-related keywords
177
+ return sslErrorIndicators.some((indicator) => lowercaseErrorText.includes(indicator));
178
+ }
179
+ /**
180
+ * Checks if the error is related to invalid/wrong API URL
181
+ */
182
+ function isInvalidUrlError(context) {
183
+ const { status, errorText, errorData } = context;
184
+ const lowercaseErrorText = errorText.toLowerCase();
185
+ // HTTP 404 is the most common indicator of wrong URL/endpoint
186
+ if (status === 404) {
187
+ return true;
188
+ }
189
+ // Check for specific error messages that indicate wrong URL
190
+ const urlErrorIndicators = [
191
+ "not found",
192
+ "resource not found",
193
+ "endpoint not found",
194
+ "path not found",
195
+ "url not found",
196
+ "route not found",
197
+ "invalid endpoint",
198
+ "unknown endpoint",
199
+ "method not allowed",
200
+ "no handler found",
201
+ "404",
202
+ // Body stream errors that often indicate wrong URL/malformed requests
203
+ "body is unusable",
204
+ "body has already been read",
205
+ "request body already read",
206
+ "body stream already consumed",
207
+ "invalid request body",
208
+ "malformed request",
209
+ ];
210
+ // Check error text for URL-related keywords
211
+ if (urlErrorIndicators.some((indicator) => lowercaseErrorText.includes(indicator))) {
212
+ return true;
213
+ }
214
+ // Check if error data indicates invalid endpoint
215
+ if (errorData?.error?.toLowerCase().includes("not found") ||
216
+ errorData?.message?.toLowerCase().includes("not found")) {
217
+ return true;
218
+ }
219
+ return false;
220
+ }
221
+ /**
222
+ * Creates an appropriate error message based on the error context
223
+ */
224
+ export function createQMetryError(context) {
225
+ const { status, errorText, baseUrl, project } = context;
226
+ // SSL certificate errors should be checked first as they're network-level issues
227
+ if (isSslCertificateError(context)) {
228
+ return new Error(ERROR_TEMPLATES.SSL_CERTIFICATE_ERROR(baseUrl, errorText));
229
+ }
230
+ if (isCorsError(context)) {
231
+ return new Error(ERROR_TEMPLATES.CORS_ERROR(baseUrl, errorText));
232
+ }
233
+ if (isAuthenticationError(context)) {
234
+ return new Error(ERROR_TEMPLATES.AUTHENTICATION_FAILED(baseUrl, errorText));
235
+ }
236
+ if (isAuthorizationError(context)) {
237
+ return new Error(ERROR_TEMPLATES.AUTHORIZATION_ERROR(errorText));
238
+ }
239
+ if (isProjectAccessError(context) && project) {
240
+ return new Error(ERROR_TEMPLATES.PROJECT_ACCESS_ERROR(project, errorText));
241
+ }
242
+ // Check for invalid URL/endpoint errors (404, wrong paths, etc.)
243
+ if (isInvalidUrlError(context)) {
244
+ const path = context.path || "";
245
+ return new Error(ERROR_TEMPLATES.INVALID_URL_ERROR(baseUrl, path, errorText));
246
+ }
247
+ return new Error(ERROR_TEMPLATES.GENERIC_API_ERROR(status, baseUrl, errorText));
248
+ }
249
+ /**
250
+ * Handles QMetry API errors in a standardized way
251
+ *
252
+ * @param response - The failed HTTP response
253
+ * @param baseUrl - The QMetry base URL
254
+ * @param project - Optional project context
255
+ * @param path - Optional API path context
256
+ */
257
+ export async function handleQMetryApiError(response, baseUrl, project, path) {
258
+ let errorData;
259
+ let errorText;
260
+ try {
261
+ errorData = await response.json();
262
+ errorText = JSON.stringify(errorData);
263
+ }
264
+ catch {
265
+ errorText = (await response.text()) || `HTTP ${response.status}`;
266
+ }
267
+ const context = {
268
+ status: response.status,
269
+ errorData,
270
+ errorText,
271
+ baseUrl,
272
+ project,
273
+ path,
274
+ };
275
+ throw createQMetryError(context);
276
+ }
277
+ /**
278
+ * Handles fetch errors that occur before receiving a response (e.g., CORS, network issues)
279
+ *
280
+ * @param error - The fetch error
281
+ * @param baseUrl - The QMetry base URL
282
+ * @param project - Optional project context
283
+ * @param path - Optional API path context
284
+ */
285
+ export function handleQMetryFetchError(error, baseUrl, project, path) {
286
+ // Extract comprehensive error information
287
+ let errorText = error.message || error.toString();
288
+ // Check if error has a cause property with more details (common for SSL errors)
289
+ if (error.cause && typeof error.cause === "object") {
290
+ const cause = error.cause;
291
+ if (cause.message) {
292
+ errorText += ` | Cause: ${cause.message}`;
293
+ }
294
+ if (cause.code) {
295
+ errorText += ` | Code: ${cause.code}`;
296
+ }
297
+ // Add the full cause details for better debugging
298
+ if (cause.toString && typeof cause.toString === "function") {
299
+ errorText += ` | Details: ${cause.toString()}`;
300
+ }
301
+ }
302
+ // Check for specific SSL error patterns in the full error
303
+ const fullErrorString = JSON.stringify(error, Object.getOwnPropertyNames(error));
304
+ if (fullErrorString.includes("unable to get local issuer certificate") ||
305
+ fullErrorString.includes("UNABLE_TO_GET_ISSUER_CERT_LOCALLY")) {
306
+ errorText +=
307
+ " | SSL Certificate Error: Corporate proxy/firewall intercepting HTTPS";
308
+ }
309
+ // Check if this is an SSL certificate error first
310
+ const tempContext = {
311
+ status: 0,
312
+ errorText,
313
+ baseUrl,
314
+ project,
315
+ path,
316
+ };
317
+ const isSSLError = isSslCertificateError(tempContext);
318
+ const isURLError = isInvalidUrlError(tempContext);
319
+ const context = {
320
+ status: 0, // Status 0 typically indicates network/CORS/SSL issues
321
+ errorText,
322
+ baseUrl,
323
+ project,
324
+ path,
325
+ // Only assume CORS if it's not an SSL or URL error
326
+ isCorsError: !isSSLError && !isURLError,
327
+ };
328
+ throw createQMetryError(context);
329
+ }
@@ -0,0 +1,74 @@
1
+ import { QMetryToolsHandlers } from "../config/constants.js";
2
+ /**
3
+ * Registry of all modules that support auto-resolution
4
+ * Add new modules here to automatically support viewId/folderPath resolution
5
+ */
6
+ export const AUTO_RESOLVE_MODULES = [
7
+ {
8
+ handler: QMetryToolsHandlers.FETCH_TEST_CASES,
9
+ viewIdPath: "latestViews.TC.viewId",
10
+ moduleName: "Test Cases",
11
+ },
12
+ {
13
+ handler: QMetryToolsHandlers.FETCH_REQUIREMENTS,
14
+ viewIdPath: "latestViews.RQ.viewId",
15
+ moduleName: "Requirements",
16
+ },
17
+ {
18
+ handler: QMetryToolsHandlers.FETCH_TESTSUITES_FOR_TESTCASE,
19
+ viewIdPath: "latestViews.TSFS.viewId",
20
+ folderIdPath: "rootFolders.TS.id",
21
+ folderIdField: "tsFolderID",
22
+ moduleName: "Test Suites",
23
+ },
24
+ ];
25
+ /**
26
+ * Helper function to safely get nested property value using dot notation
27
+ * @param obj - Object to traverse
28
+ * @param path - Dot notation path (e.g., 'latestViews.TC.viewId')
29
+ * @returns The value at the path or undefined if not found
30
+ */
31
+ export function getNestedProperty(obj, path) {
32
+ return path.split(".").reduce((current, key) => current?.[key], obj);
33
+ }
34
+ /**
35
+ * Generic auto-resolve function for viewId, folderPath, and folderID
36
+ * @param args - Tool arguments that may contain viewId/folderPath/folderID
37
+ * @param projectInfo - Project information from QMetry API
38
+ * @param config - Module configuration for this specific handler
39
+ * @returns Updated args with resolved values
40
+ */
41
+ export function autoResolveViewIdAndFolderPath(args, projectInfo, config) {
42
+ const updatedArgs = { ...args };
43
+ let viewId = updatedArgs.viewId;
44
+ const folderPath = updatedArgs.folderPath;
45
+ // Auto-resolve viewId if not provided and config has viewIdPath
46
+ if (!viewId && config.viewIdPath) {
47
+ viewId = getNestedProperty(projectInfo, config.viewIdPath);
48
+ if (viewId) {
49
+ updatedArgs.viewId = viewId;
50
+ }
51
+ }
52
+ // Auto-resolve folderPath if not provided (defaults to root)
53
+ if (folderPath === undefined) {
54
+ updatedArgs.folderPath = "";
55
+ }
56
+ // Auto-resolve folder ID if not provided and config has folderIdPath
57
+ if (config.folderIdPath &&
58
+ config.folderIdField &&
59
+ !updatedArgs[config.folderIdField]) {
60
+ const folderId = getNestedProperty(projectInfo, config.folderIdPath);
61
+ if (folderId) {
62
+ updatedArgs[config.folderIdField] = folderId;
63
+ }
64
+ }
65
+ return updatedArgs;
66
+ }
67
+ /**
68
+ * Find the auto-resolve configuration for a given handler
69
+ * @param handler - The handler name to find config for
70
+ * @returns Module configuration or undefined if not found
71
+ */
72
+ export function findAutoResolveConfig(handler) {
73
+ return AUTO_RESOLVE_MODULES.find((module) => module.handler === handler);
74
+ }
@@ -1,11 +1,28 @@
1
1
  import { QMetryToolsHandlers } from "../config/constants.js";
2
- import { getProjectInfo } from "./project.js";
3
- import { fetchTestCaseDetails, fetchTestCaseSteps, fetchTestCases, fetchTestCaseVersionDetails, } from "./testcase.js";
2
+ import { fetchIssuesLinkedToTestCase } from "./issues.js";
3
+ import { getBuilds, getPlatforms, getProjectInfo, getReleasesCycles, } from "./project.js";
4
+ import { fetchRequirementDetails, fetchRequirements, fetchRequirementsLinkedToTestCase, } from "./requirement.js";
5
+ import { fetchTestCaseDetails, fetchTestCaseExecutions, fetchTestCaseSteps, fetchTestCases, fetchTestCasesLinkedToRequirement, fetchTestCaseVersionDetails, } from "./testcase.js";
6
+ import { fetchExecutionsByTestSuite, fetchLinkedIssuesByTestCaseRun, fetchTestCaseRunsByTestSuiteRun, fetchTestCasesByTestSuite, fetchTestSuitesForTestCase, } from "./testsuite.js";
4
7
  export const QMETRY_HANDLER_MAP = {
5
8
  [QMetryToolsHandlers.SET_PROJECT_INFO]: getProjectInfo,
6
9
  [QMetryToolsHandlers.FETCH_PROJECT_INFO]: getProjectInfo,
10
+ [QMetryToolsHandlers.FETCH_RELEASES_CYCLES]: getReleasesCycles,
11
+ [QMetryToolsHandlers.FETCH_BUILDS]: getBuilds,
12
+ [QMetryToolsHandlers.FETCH_PLATFORMS]: getPlatforms,
7
13
  [QMetryToolsHandlers.FETCH_TEST_CASES]: fetchTestCases,
8
14
  [QMetryToolsHandlers.FETCH_TEST_CASE_DETAILS]: fetchTestCaseDetails,
9
15
  [QMetryToolsHandlers.FETCH_TEST_CASE_VERSION_DETAILS]: fetchTestCaseVersionDetails,
10
16
  [QMetryToolsHandlers.FETCH_TEST_CASE_STEPS]: fetchTestCaseSteps,
17
+ [QMetryToolsHandlers.FETCH_TEST_CASE_EXECUTIONS]: fetchTestCaseExecutions,
18
+ [QMetryToolsHandlers.FETCH_REQUIREMENTS]: fetchRequirements,
19
+ [QMetryToolsHandlers.FETCH_REQUIREMENT_DETAILS]: fetchRequirementDetails,
20
+ [QMetryToolsHandlers.FETCH_TESTCASES_LINKED_TO_REQUIREMENT]: fetchTestCasesLinkedToRequirement,
21
+ [QMetryToolsHandlers.FETCH_REQUIREMENTS_LINKED_TO_TESTCASE]: fetchRequirementsLinkedToTestCase,
22
+ [QMetryToolsHandlers.FETCH_TESTSUITES_FOR_TESTCASE]: fetchTestSuitesForTestCase,
23
+ [QMetryToolsHandlers.FETCH_TESTCASES_BY_TESTSUITE]: fetchTestCasesByTestSuite,
24
+ [QMetryToolsHandlers.FETCH_EXECUTIONS_BY_TESTSUITE]: fetchExecutionsByTestSuite,
25
+ [QMetryToolsHandlers.FETCH_TESTCASE_RUNS_BY_TESTSUITE_RUN]: fetchTestCaseRunsByTestSuiteRun,
26
+ [QMetryToolsHandlers.FETCH_LINKED_ISSUES_BY_TESTCASE_RUN]: fetchLinkedIssuesByTestCaseRun,
27
+ [QMetryToolsHandlers.FETCH_ISSUES_LINKED_TO_TESTCASE]: fetchIssuesLinkedToTestCase,
11
28
  };
@@ -0,0 +1,26 @@
1
+ import { QMETRY_PATHS } from "../config/rest-endpoints.js";
2
+ import { DEFAULT_FETCH_ISSUES_LINKED_TO_TESTCASE_PAYLOAD, } from "../types/issues.js";
3
+ import { qmetryRequest } from "./api/client-api.js";
4
+ import { resolveDefaults } from "./utils.js";
5
+ /**
6
+ * Fetches issues linked to a specific test case.
7
+ * @throws If `tcID` is missing/invalid.
8
+ */
9
+ export async function fetchIssuesLinkedToTestCase(token, baseUrl, project, payload) {
10
+ const { resolvedBaseUrl, resolvedProject } = resolveDefaults(baseUrl, project);
11
+ const body = {
12
+ ...DEFAULT_FETCH_ISSUES_LINKED_TO_TESTCASE_PAYLOAD,
13
+ ...payload,
14
+ };
15
+ if (typeof body.tcID !== "number") {
16
+ throw new Error("[fetchIssuesLinkedToTestCase] Missing or invalid required parameter: 'tcID'.");
17
+ }
18
+ return qmetryRequest({
19
+ method: "POST",
20
+ path: QMETRY_PATHS.ISSUES.GET_ISSUES_LINKED_TO_TC,
21
+ token,
22
+ project: resolvedProject,
23
+ baseUrl: resolvedBaseUrl,
24
+ body,
25
+ });
26
+ }
@@ -1,5 +1,6 @@
1
1
  import { QMETRY_DEFAULTS } from "../config/constants.js";
2
2
  import { QMETRY_PATHS } from "../config/rest-endpoints.js";
3
+ import { DEFAULT_FETCH_BUILD_PAYLOAD, DEFAULT_FETCH_PLATFORMS_PAYLOAD, } from "../types/project.js";
3
4
  import { qmetryRequest } from "./api/client-api.js";
4
5
  /**
5
6
  * Retrieves project information from QMetry
@@ -25,3 +26,58 @@ export async function getProjectInfo(token, baseUrl, project) {
25
26
  project: project || QMETRY_DEFAULTS.PROJECT_KEY,
26
27
  });
27
28
  }
29
+ export async function getReleasesCycles(token, baseUrl, project, payload = {}) {
30
+ let showArchiveValue;
31
+ if (payload.showArchive !== undefined) {
32
+ showArchiveValue = payload.showArchive;
33
+ }
34
+ else {
35
+ showArchiveValue = false;
36
+ }
37
+ const body = {
38
+ showArchive: showArchiveValue,
39
+ };
40
+ return qmetryRequest({
41
+ method: "POST",
42
+ path: QMETRY_PATHS.PROJECT.GET_RELEASES_CYCLES,
43
+ token,
44
+ baseUrl: baseUrl || QMETRY_DEFAULTS.BASE_URL,
45
+ project: project || QMETRY_DEFAULTS.PROJECT_KEY,
46
+ body,
47
+ });
48
+ }
49
+ /**
50
+ * Fetches a List of Builds of the current project.
51
+ */
52
+ export async function getBuilds(token, baseUrl, project, payload) {
53
+ const body = {
54
+ ...DEFAULT_FETCH_BUILD_PAYLOAD,
55
+ ...payload,
56
+ };
57
+ return qmetryRequest({
58
+ method: "POST",
59
+ path: QMETRY_PATHS.PROJECT.GET_BUILD,
60
+ token,
61
+ baseUrl: baseUrl || QMETRY_DEFAULTS.BASE_URL,
62
+ project: project || QMETRY_DEFAULTS.PROJECT_KEY,
63
+ body,
64
+ });
65
+ }
66
+ /**
67
+ * Fetches platforms from the current QMetry project
68
+ *
69
+ */
70
+ export async function getPlatforms(token, baseUrl, project, payload) {
71
+ const body = {
72
+ ...DEFAULT_FETCH_PLATFORMS_PAYLOAD,
73
+ ...payload,
74
+ };
75
+ return qmetryRequest({
76
+ method: "POST",
77
+ path: QMETRY_PATHS.PROJECT.GET_PLATFORMS,
78
+ token,
79
+ baseUrl: baseUrl || QMETRY_DEFAULTS.BASE_URL,
80
+ project: project || QMETRY_DEFAULTS.PROJECT_KEY,
81
+ body,
82
+ });
83
+ }
@@ -0,0 +1,76 @@
1
+ import { QMETRY_PATHS } from "../config/rest-endpoints.js";
2
+ import { DEFAULT_FETCH_REQUIREMENT_DETAILS_PAYLOAD, DEFAULT_FETCH_REQUIREMENTS_LINKED_TO_TESTCASE_PAYLOAD, DEFAULT_FETCH_REQUIREMENTS_PAYLOAD, } from "../types/requirements.js";
3
+ import { qmetryRequest } from "./api/client-api.js";
4
+ import { resolveDefaults } from "./utils.js";
5
+ /**
6
+ * Fetches a list of requirements.
7
+ * @throws If `viewId` or `folderPath` are missing/invalid.
8
+ */
9
+ export async function fetchRequirements(token, baseUrl, project, payload) {
10
+ const { resolvedBaseUrl, resolvedProject } = resolveDefaults(baseUrl, project);
11
+ const body = {
12
+ ...DEFAULT_FETCH_REQUIREMENTS_PAYLOAD,
13
+ ...payload,
14
+ };
15
+ if (typeof body.viewId !== "number") {
16
+ throw new Error("[fetchRequirements] Missing or invalid required parameter: 'viewId'.");
17
+ }
18
+ if (typeof body.folderPath !== "string") {
19
+ throw new Error("[fetchRequirements] Missing or invalid required parameter: 'folderPath'.");
20
+ }
21
+ return qmetryRequest({
22
+ method: "POST",
23
+ path: QMETRY_PATHS.REQUIREMENT.GET_RQ_LIST,
24
+ token,
25
+ project: resolvedProject,
26
+ baseUrl: resolvedBaseUrl,
27
+ body,
28
+ });
29
+ }
30
+ /**
31
+ * Fetches requirement details by numeric ID.
32
+ * @throws If `id` or `version` are missing/invalid.
33
+ */
34
+ export async function fetchRequirementDetails(token, baseUrl, project, payload) {
35
+ const { resolvedBaseUrl, resolvedProject } = resolveDefaults(baseUrl, project);
36
+ const body = {
37
+ ...DEFAULT_FETCH_REQUIREMENT_DETAILS_PAYLOAD,
38
+ ...payload,
39
+ };
40
+ if (typeof body.id !== "number") {
41
+ throw new Error("[fetchRequirementDetails] Missing or invalid required parameter: 'id'.");
42
+ }
43
+ if (typeof body.version !== "number") {
44
+ throw new Error("[fetchRequirementDetails] Missing or invalid required parameter: 'version'.");
45
+ }
46
+ return qmetryRequest({
47
+ method: "POST",
48
+ path: QMETRY_PATHS.REQUIREMENT.GET_RQ_DETAILS,
49
+ token,
50
+ project: resolvedProject,
51
+ baseUrl: resolvedBaseUrl,
52
+ body,
53
+ });
54
+ }
55
+ /**
56
+ * Fetches requirements linked to a specific test case.
57
+ * @throws If `tcID` is missing/invalid.
58
+ */
59
+ export async function fetchRequirementsLinkedToTestCase(token, baseUrl, project, payload) {
60
+ const { resolvedBaseUrl, resolvedProject } = resolveDefaults(baseUrl, project);
61
+ const body = {
62
+ ...DEFAULT_FETCH_REQUIREMENTS_LINKED_TO_TESTCASE_PAYLOAD,
63
+ ...payload,
64
+ };
65
+ if (typeof body.tcID !== "number") {
66
+ throw new Error("[fetchRequirementsLinkedToTestCase] Missing or invalid required parameter: 'tcID'.");
67
+ }
68
+ return qmetryRequest({
69
+ method: "POST",
70
+ path: QMETRY_PATHS.REQUIREMENT.GET_RQ_LINKED_TO_TC,
71
+ token,
72
+ project: resolvedProject,
73
+ baseUrl: resolvedBaseUrl,
74
+ body,
75
+ });
76
+ }
@@ -1,13 +1,7 @@
1
- import { QMETRY_DEFAULTS } from "../config/constants.js";
2
1
  import { QMETRY_PATHS } from "../config/rest-endpoints.js";
3
- import { DEFAULT_FETCH_TESTCASE_DETAILS_PAYLOAD, DEFAULT_FETCH_TESTCASE_STEPS_PAYLOAD, DEFAULT_FETCH_TESTCASE_VERSION_DETAILS_PAYLOAD, DEFAULT_FETCH_TESTCASES_PAYLOAD, } from "../types/testcase.js";
2
+ import { DEFAULT_FETCH_TESTCASE_DETAILS_PAYLOAD, DEFAULT_FETCH_TESTCASE_EXECUTIONS_PAYLOAD, DEFAULT_FETCH_TESTCASE_STEPS_PAYLOAD, DEFAULT_FETCH_TESTCASE_VERSION_DETAILS_PAYLOAD, DEFAULT_FETCH_TESTCASES_LINKED_TO_REQUIREMENT_PAYLOAD, DEFAULT_FETCH_TESTCASES_PAYLOAD, } from "../types/testcase.js";
4
3
  import { qmetryRequest } from "./api/client-api.js";
5
- function resolveDefaults(baseUrl, project) {
6
- return {
7
- resolvedBaseUrl: baseUrl || QMETRY_DEFAULTS.BASE_URL,
8
- resolvedProject: project || QMETRY_DEFAULTS.PROJECT_KEY,
9
- };
10
- }
4
+ import { resolveDefaults } from "./utils.js";
11
5
  /**
12
6
  * Fetches a list of test cases.
13
7
  * @throws If `viewId` or `folderPath` are missing/invalid.
@@ -102,3 +96,47 @@ export async function fetchTestCaseSteps(token, baseUrl, project, payload) {
102
96
  body,
103
97
  });
104
98
  }
99
+ /**
100
+ * Fetches test cases linked to a specific requirement.
101
+ * @throws If `rqID` is missing/invalid.
102
+ */
103
+ export async function fetchTestCasesLinkedToRequirement(token, baseUrl, project, payload) {
104
+ const { resolvedBaseUrl, resolvedProject } = resolveDefaults(baseUrl, project);
105
+ const body = {
106
+ ...DEFAULT_FETCH_TESTCASES_LINKED_TO_REQUIREMENT_PAYLOAD,
107
+ ...payload,
108
+ };
109
+ if (typeof body.rqID !== "number") {
110
+ throw new Error("[fetchTestCasesLinkedToRequirement] Missing or invalid required parameter: 'rqID'.");
111
+ }
112
+ return qmetryRequest({
113
+ method: "POST",
114
+ path: QMETRY_PATHS.TESTCASE.GET_TC_LINKED_TO_RQ,
115
+ token,
116
+ project: resolvedProject,
117
+ baseUrl: resolvedBaseUrl,
118
+ body,
119
+ });
120
+ }
121
+ /**
122
+ * Fetches executions for a specific test case.
123
+ * @throws If `tcid` is missing/invalid.
124
+ */
125
+ export async function fetchTestCaseExecutions(token, baseUrl, project, payload) {
126
+ const { resolvedBaseUrl, resolvedProject } = resolveDefaults(baseUrl, project);
127
+ const body = {
128
+ ...DEFAULT_FETCH_TESTCASE_EXECUTIONS_PAYLOAD,
129
+ ...payload,
130
+ };
131
+ if (typeof body.tcid !== "number") {
132
+ throw new Error("[fetchTestCaseExecutions] Missing or invalid required parameter: 'tcid'.");
133
+ }
134
+ return qmetryRequest({
135
+ method: "POST",
136
+ path: QMETRY_PATHS.TESTCASE.GET_TC_EXECUTIONS,
137
+ token,
138
+ project: resolvedProject,
139
+ baseUrl: resolvedBaseUrl,
140
+ body,
141
+ });
142
+ }