@smartbear/mcp 0.4.0 → 0.6.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 (31) hide show
  1. package/README.md +15 -121
  2. package/dist/{insight-hub → bugsnag}/client/api/CurrentUser.js +4 -4
  3. package/dist/{insight-hub → bugsnag}/client/api/Error.js +37 -4
  4. package/dist/bugsnag/client/api/Project.js +163 -0
  5. package/dist/{insight-hub → bugsnag}/client/api/base.js +39 -11
  6. package/dist/{insight-hub → bugsnag}/client/api/filters.js +2 -2
  7. package/dist/{insight-hub → bugsnag}/client.js +511 -29
  8. package/dist/common/info.js +1 -1
  9. package/dist/common/server.js +17 -5
  10. package/dist/index.js +11 -11
  11. package/dist/pactflow/client/ai.js +56 -6
  12. package/dist/pactflow/client/base.js +19 -1
  13. package/dist/pactflow/client/prompt-utils.js +89 -0
  14. package/dist/pactflow/client/prompts.js +133 -0
  15. package/dist/pactflow/client/tools.js +43 -2
  16. package/dist/pactflow/client/utils.js +70 -0
  17. package/dist/pactflow/client.js +192 -13
  18. package/package.json +9 -4
  19. package/dist/insight-hub/client/api/Project.js +0 -46
  20. package/dist/package.json +0 -60
  21. package/dist/tests/unit/common/server.test.js +0 -319
  22. package/dist/tests/unit/insight-hub/api-utilities.test.js +0 -31
  23. package/dist/tests/unit/insight-hub/client.test.js +0 -852
  24. package/dist/tests/unit/insight-hub/filters.test.js +0 -93
  25. package/dist/tests/unit/pactflow/ai.test.js +0 -21
  26. package/dist/tests/unit/pactflow/client.test.js +0 -67
  27. package/dist/tests/unit/pactflow/tools.test.js +0 -34
  28. package/dist/vitest.config.js +0 -57
  29. /package/dist/{insight-hub → bugsnag}/client/api/index.js +0 -0
  30. /package/dist/{insight-hub → bugsnag}/client/configuration.js +0 -0
  31. /package/dist/{insight-hub → bugsnag}/client/index.js +0 -0
package/README.md CHANGED
@@ -9,7 +9,7 @@
9
9
 
10
10
  <!-- Badges -->
11
11
  <div>
12
- <a href="https://github.com/SmartBear/smartbear-mcp/actions/workflows/test.yml"><img src="https://github.com/SmartBear/smartbear-mcp/workflows/Test%20Suite/badge.svg" alt="Test Status"></a>
12
+ <a href="https://github.com/SmartBear/smartbear-mcp/actions/workflows/node-ci.yml"><img src="https://github.com/SmartBear/smartbear-mcp/actions/workflows/node-ci.yml/badge.svg?branch=next" alt="Test Status"></a>
13
13
  <a href="https://smartbear.github.io/smartbear-mcp/"><img src="https://img.shields.io/badge/coverage-dynamic-brightgreen" alt="Coverage"></a>
14
14
  <a href="https://www.npmjs.com/package/@smartbear/mcp"><img src="https://img.shields.io/npm/v/@smartbear/mcp" alt="npm version"></a>
15
15
  <a href="https://modelcontextprotocol.io"><img src="https://img.shields.io/badge/MCP-Compatible-blue" alt="MCP Compatible"></a>
@@ -18,7 +18,7 @@
18
18
  </div>
19
19
  <br />
20
20
 
21
- A Model Context Protocol (MCP) server which provides AI assistants with seamless access to SmartBear's suite of testing and monitoring tools, including [Insight Hub](https://www.smartbear.com/insight-hub), [Reflect](https://reflect.run), [API Hub](https://www.smartbear.com/api-hub), [PactFlow](https://pactflow.io/) and [Pact Broker](https://docs.pact.io/)
21
+ A Model Context Protocol (MCP) server which provides AI assistants with seamless access to SmartBear's suite of testing and monitoring tools, including [BugSnag](https://www.bugsnag.com/), [Reflect](https://reflect.run), [API Hub](https://www.smartbear.com/api-hub), [PactFlow](https://pactflow.io/) and [Pact Broker](https://docs.pact.io/)
22
22
 
23
23
  ## What is MCP?
24
24
 
@@ -28,7 +28,7 @@ The [Model Context Protocol (MCP)](https://modelcontextprotocol.io/introduction)
28
28
 
29
29
  See individual guides for suggested prompts and supported tools and resources:
30
30
 
31
- - [Insight Hub](https://developer.smartbear.com/smartbear-mcp/docs/insight-hub-integration) - Comprehensive error monitoring and debugging capabilities
31
+ - [BugSnag](https://developer.smartbear.com/smartbear-mcp/docs/bugsnag-integration) - Comprehensive error monitoring and debugging capabilities
32
32
  - [Test Hub](https://developer.smartbear.com/smartbear-mcp/docs/test-hub-integration) - Test management and execution capabilities
33
33
  - [API Hub](https://developer.smartbear.com/smartbear-mcp/docs/api-hub-integration) - Portal management capabilities
34
34
  - [PactFlow](https://developer.smartbear.com/pactflow/default/getting-started) - Contract testing capabilities
@@ -37,14 +37,14 @@ See individual guides for suggested prompts and supported tools and resources:
37
37
  ## Prerequisites
38
38
 
39
39
  - Node.js 20+ and npm
40
- - Access to SmartBear products (Insight Hub, Reflect, or API Hub)
40
+ - Access to SmartBear products (BugSnag, Reflect, or API Hub)
41
41
  - Valid API tokens for the products you want to integrate
42
42
 
43
43
  ## Installation
44
44
 
45
45
  The MCP server is distributed as an npm package [`@smartbear/mcp`](https://www.npmjs.com/package/@smartbear/mcp), making it easy to integrate into your development workflow.
46
46
 
47
- The server is started with the API key or auth token that you use with your SmartBear product(s). They are optional and can be removed from your configuration if you aren't using the product. For Insight Hub, if you provide a project API key it will narrow down all searches to a single project in your Insight Hub dashboard. Leave this field blank if you wish to interact across multiple projects at a time.
47
+ The server is started with the API key or auth token that you use with your SmartBear product(s). They are optional and can be removed from your configuration if you aren't using the product. For BugSnag, if you provide a project API key it will narrow down all searches to a single project in your BugSnag dashboard. Leave this field blank if you wish to interact across multiple projects at a time.
48
48
 
49
49
  ### VS Code with Copilot
50
50
 
@@ -66,8 +66,8 @@ Alternatively, you can use `npx` (or globally install) the `@smartbear/mcp` pack
66
66
  "@smartbear/mcp@latest"
67
67
  ],
68
68
  "env": {
69
- "INSIGHT_HUB_AUTH_TOKEN": "${input:insight_hub_auth_token}",
70
- "INSIGHT_HUB_PROJECT_API_KEY": "${input:insight_hub_project_api_key}",
69
+ "BUGSNAG_AUTH_TOKEN": "${input:bugsnag_auth_token}",
70
+ "BUGSNAG_PROJECT_API_KEY": "${input:bugsnag_project_api_key}",
71
71
  "REFLECT_API_TOKEN": "${input:reflect_api_token}",
72
72
  "API_HUB_API_KEY": "${input:api_hub_api_key}",
73
73
  "PACT_BROKER_BASE_URL": "${input:pact_broker_base_url}",
@@ -79,15 +79,15 @@ Alternatively, you can use `npx` (or globally install) the `@smartbear/mcp` pack
79
79
  },
80
80
  "inputs": [
81
81
  {
82
- "id": "insight_hub_auth_token",
82
+ "id": "bugsnag_auth_token",
83
83
  "type": "promptString",
84
- "description": "Insight Hub Auth Token - leave blank to disable Insight Hub tools",
84
+ "description": "BugSnag Auth Token - leave blank to disable BugSnag tools",
85
85
  "password": true
86
86
  },
87
87
  {
88
- "id": "insight_hub_project_api_key",
88
+ "id": "bugsnag_project_api_key",
89
89
  "type": "promptString",
90
- "description": "Insight Hub Project API Key - for single project interactions",
90
+ "description": "BugSnag Project API Key - for single project interactions",
91
91
  "password": false
92
92
  },
93
93
  {
@@ -145,8 +145,8 @@ Add the following configuration to your `claude_desktop_config.json` to launch t
145
145
  "@smartbear/mcp@latest"
146
146
  ],
147
147
  "env": {
148
- "INSIGHT_HUB_AUTH_TOKEN": "your_personal_auth_token",
149
- "INSIGHT_HUB_PROJECT_API_KEY": "your_project_api_key",
148
+ "BUGSNAG_AUTH_TOKEN": "your_personal_auth_token",
149
+ "BUGSNAG_PROJECT_API_KEY": "your_project_api_key",
150
150
  "REFLECT_API_TOKEN": "your_reflect_token",
151
151
  "API_HUB_API_KEY": "your_api_hub_key",
152
152
  "PACT_BROKER_BASE_URL": "your_pactflow_or_pactbroker_base_url",
@@ -165,113 +165,7 @@ For detailed introduction, examples, and advanced configuration visit our 📖 [
165
165
 
166
166
  ## Local Development
167
167
 
168
- For developers who want to contribute to the SmartBear MCP server, customize its functionality, or work with the latest development features, you can build and run the server directly from source code. This approach gives you full control over the implementation and allows you to make modifications as needed.
169
-
170
- 1. **Clone the repository:**
171
- ```bash
172
- git clone https://github.com/SmartBear/smartbear-mcp.git
173
- cd smartbear-mcp
174
- ```
175
-
176
- 2. **Install dependencies:**
177
- ```bash
178
- npm install
179
- ```
180
-
181
- 3. **Build the server:**
182
- ```bash
183
- npm run build
184
- ```
185
-
186
- 4. **Add the server to your IDE** configuration updating `.vscode/mcp.json` (or equivalent) to point to your local build
187
-
188
- <details>
189
- <summary><strong>📋 VSCode (mcp.json)</strong></summary>
190
-
191
- ```json
192
- {
193
- "servers": {
194
- "smartbear": {
195
- "type": "stdio",
196
- "command": "node",
197
- "dev": { // <-- To allow debugging in VS Code
198
- "watch": "dist/**/*.js",
199
- "debug": {
200
- "type": "node"
201
- },
202
- },
203
- "args": ["<PATH_TO_SMARTBEAR_MCP_REPO>/dist/index.js"],
204
- "env": {
205
- "INSIGHT_HUB_AUTH_TOKEN": "${input:insight_hub_auth_token}",
206
- "INSIGHT_HUB_PROJECT_API_KEY": "${input:insight_hub_project_api_key}",
207
- "REFLECT_API_TOKEN": "${input:reflect_api_token}",
208
- "API_HUB_API_KEY": "${input:api_hub_api_key}",
209
- "PACT_BROKER_BASE_URL": "${input:pact_broker_base_url}",
210
- "PACT_BROKER_TOKEN": "${input:pact_broker_token}",
211
- "PACT_BROKER_USERNAME": "${input:pact_broker_username}",
212
- "PACT_BROKER_PASSWORD": "${input:pact_broker_password}"
213
- }
214
- }
215
- },
216
- "inputs": [
217
- {
218
- "id": "insight_hub_auth_token",
219
- "type": "promptString",
220
- "description": "Insight Hub Auth Token - leave blank to disable Insight Hub tools",
221
- "password": true
222
- },
223
- {
224
- "id": "insight_hub_project_api_key",
225
- "type": "promptString",
226
- "description": "Insight Hub Project API Key - for single project interactions",
227
- "password": false
228
- },
229
- {
230
- "id": "reflect_api_token",
231
- "type": "promptString",
232
- "description": "Reflect API Token - leave blank to disable Reflect tools",
233
- "password": true
234
- },
235
- {
236
- "id": "api_hub_api_key",
237
- "type": "promptString",
238
- "description": "API Hub API Key - leave blank to disable API Hub tools",
239
- "password": true
240
- },
241
- {
242
- "id": "pact_broker_base_url",
243
- "type": "promptString",
244
- "description": "PactFlow or Pact Broker base url - leave blank to disable PactFlow tools",
245
- "password": true
246
- },
247
- {
248
- "id": "pact_broker_token",
249
- "type": "promptString",
250
- "description": "PactFlow Authentication Token",
251
- "password": true
252
- },
253
- {
254
- "id": "pact_broker_username",
255
- "type": "promptString",
256
- "description": "Pact Broker Username",
257
- "password": true
258
- },
259
- {
260
- "id": "pact_broker_password",
261
- "type": "promptString",
262
- "description": "Pact Broker Password",
263
- "password": true
264
- },
265
- ]
266
- }
267
- ```
268
- </details>
269
-
270
- 5. **Local testing** using the [MCP Inspector](https://github.com/modelcontextprotocol/inspector) web interface:
271
-
272
- ```bash
273
- INSIGHT_HUB_AUTH_TOKEN="your_token" REFLECT_API_TOKEN="your_reflect_token" API_HUB_API_KEY="your_api_hub_key" PACT_BROKER_BASE_URL="your-pactflow-url" PACT_BROKER_TOKEN="your-pactflow-token" PACT_BROKER_USERNAME="your-pact-broker-username" PACT_BROKER_PASSWORD="your-pact-broker-password" npx @modelcontextprotocol/inspector npx @smartbear/mcp@latest
274
- ```
168
+ For developers who want to contribute to the SmartBear MCP server, please see the [CONTRIBUTING.md](CONTRIBUTING.md) guide.
275
169
 
276
170
  ## License
277
171
 
@@ -285,4 +179,4 @@ This MCP server is licensed under the MIT License. This means you are free to us
285
179
 
286
180
  ---
287
181
 
288
- **SmartBear MCP Server** - Bringing the power of SmartBear's testing and monitoring ecosystem to your AI-powered development workflow.
182
+ **SmartBear MCP Server** - Bringing the power of SmartBear's testing and monitoring ecosystem to your AI-powered development workflow.
@@ -1,10 +1,11 @@
1
1
  import { BaseAPI, pickFieldsFromArray } from './base.js';
2
2
  // --- API Class ---
3
3
  export class CurrentUserAPI extends BaseAPI {
4
+ static filterFields = ["collaborators_url", "projects_url", "upgrade_url"];
4
5
  static organizationFields = ['id', 'name', 'slug'];
5
6
  static projectFields = ['id', 'name', 'slug', 'api_key'];
6
7
  constructor(configuration) {
7
- super(configuration);
8
+ super(configuration, CurrentUserAPI.filterFields);
8
9
  }
9
10
  /**
10
11
  * List the current user's organizations
@@ -38,7 +39,7 @@ export class CurrentUserAPI extends BaseAPI {
38
39
  * @returns A promise that resolves to the list of projects in the organization
39
40
  */
40
41
  async getOrganizationProjects(organizationId, options = {}) {
41
- const { paginate = false, ...queryOptions } = options;
42
+ const { ...queryOptions } = options;
42
43
  const params = new URLSearchParams();
43
44
  for (const [key, value] of Object.entries(queryOptions)) {
44
45
  if (value !== undefined)
@@ -50,8 +51,7 @@ export class CurrentUserAPI extends BaseAPI {
50
51
  const data = await this.request({
51
52
  method: 'GET',
52
53
  url,
53
- }, paginate);
54
- // Only return allowed fields
54
+ }, true); // Always paginate for projects
55
55
  return {
56
56
  ...data,
57
57
  body: pickFieldsFromArray(data.body || [], CurrentUserAPI.projectFields)
@@ -22,8 +22,9 @@ export const ReopenConditions = [
22
22
  ];
23
23
  // --- API Class ---
24
24
  export class ErrorAPI extends BaseAPI {
25
+ static filterFields = ["url", "project_url", "events_url"];
25
26
  constructor(configuration) {
26
- super(configuration);
27
+ super(configuration, ErrorAPI.filterFields);
27
28
  }
28
29
  /**
29
30
  * View an Error on a Project
@@ -95,9 +96,41 @@ export class ErrorAPI extends BaseAPI {
95
96
  * GET /projects/{project_id}/errors
96
97
  */
97
98
  async listProjectErrors(projectId, options = {}) {
98
- const url = options.filters
99
- ? `/projects/${projectId}/errors?${toQueryString(options.filters)}`
100
- : `/projects/${projectId}/errors`;
99
+ let url = `/projects/${projectId}/errors`;
100
+ // Next links need to be used as-is to ensure results are consistent, so only the page size can be modified
101
+ if (options.next !== undefined) {
102
+ const nextUrl = new URL(options.next);
103
+ if (options.per_page !== undefined) {
104
+ nextUrl.searchParams.set('per_page', options.per_page.toString());
105
+ }
106
+ url = nextUrl.toString();
107
+ }
108
+ else {
109
+ const params = new URLSearchParams();
110
+ // Add filter parameters
111
+ if (options.filters) {
112
+ const filterParams = new URLSearchParams(toQueryString(options.filters));
113
+ filterParams.forEach((value, key) => {
114
+ params.append(key, value);
115
+ });
116
+ }
117
+ // Add pagination and sorting parameters
118
+ if (options.base !== undefined) {
119
+ params.append('base', options.base);
120
+ }
121
+ if (options.sort !== undefined) {
122
+ params.append('sort', options.sort);
123
+ }
124
+ if (options.direction !== undefined) {
125
+ params.append('direction', options.direction);
126
+ }
127
+ if (options.per_page !== undefined) {
128
+ params.append('per_page', options.per_page.toString());
129
+ }
130
+ if (params.size > 0) {
131
+ url = `/projects/${projectId}/errors?${params}`;
132
+ }
133
+ }
101
134
  return (await this.request({
102
135
  method: 'GET',
103
136
  url,
@@ -0,0 +1,163 @@
1
+ import { BaseAPI, pickFieldsFromArray, pickFields } from "./base.js";
2
+ // --- API Class ---
3
+ export class ProjectAPI extends BaseAPI {
4
+ static filterFields = ["errors_url", "events_url", "url", "html_url"];
5
+ static eventFieldFields = ["custom", "display_id", "filter_options", "pivot_options"];
6
+ static buildFields = [
7
+ "id",
8
+ "release_time",
9
+ "app_version",
10
+ "release_stage",
11
+ "errors_introduced_count",
12
+ "errors_seen_count",
13
+ "total_sessions_count",
14
+ "unhandled_sessions_count",
15
+ "accumulative_daily_users_seen",
16
+ "accumulative_daily_users_with_unhandled",
17
+ ];
18
+ static releaseFields = [
19
+ "id",
20
+ "release_stage_name",
21
+ "app_version",
22
+ "first_released_at",
23
+ "first_release_id",
24
+ "releases_count",
25
+ "visible",
26
+ "total_sessions_count",
27
+ "unhandled_sessions_count",
28
+ "sessions_count_in_last_24h",
29
+ "accumulative_daily_users_seen",
30
+ "accumulative_daily_users_with_unhandled",
31
+ ];
32
+ static stabilityFields = [
33
+ "critical_stability",
34
+ "target_stability",
35
+ "stability_target_type",
36
+ ];
37
+ constructor(configuration) {
38
+ super(configuration, ProjectAPI.filterFields);
39
+ }
40
+ /**
41
+ * List the Event Fields for a Project
42
+ * GET /projects/{project_id}/event_fields
43
+ * @param projectId The project ID
44
+ * @returns A promise that resolves to the list of event fields
45
+ */
46
+ async listProjectEventFields(projectId) {
47
+ const url = `/projects/${projectId}/event_fields`;
48
+ const data = await this.request({
49
+ method: 'GET',
50
+ url,
51
+ });
52
+ // Only return allowed fields
53
+ return {
54
+ ...data,
55
+ body: pickFieldsFromArray(data.body || [], ProjectAPI.eventFieldFields)
56
+ };
57
+ }
58
+ /**
59
+ * Create a Project in an Organization
60
+ * POST /organizations/{organization_id}/projects
61
+ * @param organizationId The organization ID
62
+ * @param data The project creation request body
63
+ * @returns A promise that resolves to the created project
64
+ */
65
+ async createProject(organizationId, data) {
66
+ const url = `/organizations/${organizationId}/projects`;
67
+ return await this.request({
68
+ method: 'POST',
69
+ url,
70
+ body: data,
71
+ });
72
+ }
73
+ /**
74
+ * Retrieves the stability targets for a specific project.
75
+ * GET /projects/{project_id} (with internal header)
76
+ * @param projectId The ID of the project.
77
+ * @returns A promise that resolves to the project's stability targets.
78
+ */
79
+ async getProjectStabilityTargets(projectId) {
80
+ const url = `/projects/${projectId}`;
81
+ const response = await this.request({
82
+ method: "GET",
83
+ url,
84
+ });
85
+ return pickFields(response.body || {}, ProjectAPI.stabilityFields);
86
+ }
87
+ /**
88
+ * Lists builds for a specific project.
89
+ * GET /projects/{project_id}/releases
90
+ * @param projectId The ID of the project.
91
+ * @param opts Options for listing releases, including filtering by release stage.
92
+ * @returns A promise that resolves to an array of `ListReleasesResponse` objects.
93
+ */
94
+ async listBuilds(projectId, opts) {
95
+ const url = opts.next_url ?? `/projects/${projectId}/releases${opts.release_stage ? `?release_stage=${opts.release_stage}` : ""}`;
96
+ const response = await this.request({
97
+ method: "GET",
98
+ url,
99
+ });
100
+ return {
101
+ ...response,
102
+ body: pickFieldsFromArray(response.body || [], ProjectAPI.buildFields),
103
+ };
104
+ }
105
+ /**
106
+ * Retrieves a specific build from a project.
107
+ * GET /projects/{project_id}/releases/{release_id}
108
+ * @param projectId The ID of the project.
109
+ * @param buildId The ID of the release to retrieve.
110
+ * @returns A promise that resolves to the release data.
111
+ */
112
+ async getBuild(projectId, buildId) {
113
+ const url = `/projects/${projectId}/releases/${buildId}`;
114
+ return await this.request({
115
+ method: "GET",
116
+ url,
117
+ });
118
+ }
119
+ /**
120
+ * Lists releases for a specific project.
121
+ * GET /projects/{project_id}/release_groups
122
+ * @param projectId The ID of the project.
123
+ * @param opts Options for listing releases, including filtering by release stage and visibility.
124
+ * @returns A promise that resolves to an array of `ReleaseSummaryResponse` objects.
125
+ */
126
+ async listReleases(projectId, opts) {
127
+ const url = opts.next_url ?? `/projects/${projectId}/release_groups?release_stage_name=${opts.release_stage_name}&visible_only=${opts.visible_only}&top_only=true`;
128
+ const response = await this.request({
129
+ method: "GET",
130
+ url
131
+ });
132
+ return {
133
+ ...response,
134
+ body: pickFieldsFromArray(response.body || [], ProjectAPI.releaseFields),
135
+ };
136
+ }
137
+ /**
138
+ * Retrieves a specific release by its ID.
139
+ * GET /release_groups/{release_id}
140
+ * @param releaseId The ID of the release to retrieve.
141
+ * @returns A promise that resolves to the release data.
142
+ */
143
+ async getRelease(releaseId) {
144
+ const url = `/release_groups/${releaseId}`;
145
+ return await this.request({
146
+ method: "GET",
147
+ url,
148
+ });
149
+ }
150
+ /**
151
+ * Lists builds associated with a specific release group.
152
+ * GET /release_groups/{release_id}/releases
153
+ * @param releaseId The ID of the release group.
154
+ * @return A promise that resolves to an array of `BuildResponse` objects.
155
+ */
156
+ async listBuildsInRelease(releaseId) {
157
+ const url = `/release_groups/${releaseId}/releases`;
158
+ return await this.request({
159
+ method: "GET",
160
+ url,
161
+ }, true);
162
+ }
163
+ }
@@ -12,10 +12,30 @@ export function pickFields(obj, keys) {
12
12
  export function pickFieldsFromArray(arr, keys) {
13
13
  return arr.map(obj => pickFields(obj, keys));
14
14
  }
15
+ // Utility to extract next URL path from Link header
16
+ export function getNextUrlPathFromHeader(headers, basePath) {
17
+ if (!headers)
18
+ return null;
19
+ const link = headers.get("link") || headers.get("Link");
20
+ if (!link)
21
+ return null;
22
+ const match = link.match(/<([^>]+)>;\s*rel="next"/)?.[1];
23
+ if (!match)
24
+ return null;
25
+ return match.replace(basePath, "");
26
+ }
27
+ // Ensure URL is absolute
28
+ // The MCP tools exposed use only the path for pagination
29
+ // For making requests, we need to ensure the URL is absolute
30
+ export function ensureFullUrl(url, basePath) {
31
+ return url.startsWith('http') ? url : `${basePath}${url}`;
32
+ }
15
33
  export class BaseAPI {
16
34
  configuration;
17
- constructor(configuration) {
35
+ filterFields;
36
+ constructor(configuration, filterFields) {
18
37
  this.configuration = configuration;
38
+ this.filterFields = filterFields || [];
19
39
  }
20
40
  async request(options, paginate = false) {
21
41
  const headers = {
@@ -28,11 +48,11 @@ export class BaseAPI {
28
48
  headers,
29
49
  body: options.body ? JSON.stringify(options.body) : undefined,
30
50
  };
31
- const url = options.url.startsWith('http') ? options.url : `${this.configuration.basePath || ''}${options.url}`;
32
51
  let results = [];
33
- let nextUrl = url;
52
+ let nextUrl = options.url;
34
53
  let apiResponse;
35
54
  do {
55
+ nextUrl = ensureFullUrl(nextUrl, this.configuration.basePath);
36
56
  const response = await fetch(nextUrl, fetchOptions);
37
57
  if (!response.ok) {
38
58
  const errorText = await response.text();
@@ -45,14 +65,7 @@ export class BaseAPI {
45
65
  const data = await response.json();
46
66
  if (paginate) {
47
67
  results = results.concat(data);
48
- const link = response.headers.get('Link');
49
- if (link) {
50
- const match = link.match(/<([^>]+)>;\s*rel="next"/);
51
- nextUrl = match ? match[1] : undefined;
52
- }
53
- else {
54
- nextUrl = undefined;
55
- }
68
+ nextUrl = getNextUrlPathFromHeader(response.headers, this.configuration.basePath);
56
69
  }
57
70
  else {
58
71
  apiResponse.body = data;
@@ -61,6 +74,21 @@ export class BaseAPI {
61
74
  if (paginate) {
62
75
  apiResponse.body = results;
63
76
  }
77
+ if (Array.isArray(apiResponse.body)) {
78
+ apiResponse.body.forEach(this.sanitizeResponse.bind(this));
79
+ }
80
+ else {
81
+ this.sanitizeResponse(apiResponse.body ?? {});
82
+ }
64
83
  return apiResponse;
65
84
  }
85
+ sanitizeResponse(data) {
86
+ if (!data)
87
+ return;
88
+ for (const key of this.filterFields) {
89
+ if (key in data) {
90
+ delete data[key];
91
+ }
92
+ }
93
+ }
66
94
  }
@@ -1,8 +1,8 @@
1
1
  /**
2
- * Filters utility for Insight Hub API
2
+ * Filters utility for BugSnag API
3
3
  *
4
4
  * This file provides utility functions for creating filter URL parameters
5
- * based on the Insight Hub filtering specification described in the Filtering.md document.
5
+ * based on the BugSnag filtering specification described in the Filtering.md document.
6
6
  */
7
7
  import { z } from "zod";
8
8
  export const FilterValueSchema = z.object({