• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mongodb-js / mongodb-mcp-server / 18382799942

09 Oct 2025 04:42PM UTC coverage: 82.186% (-0.4%) from 82.619%
18382799942

push

github

web-flow
chore(deps): bump github/codeql-action from 3 to 4 (#634)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

1154 of 1531 branches covered (75.38%)

Branch coverage included in aggregate %.

5614 of 6704 relevant lines covered (83.74%)

66.92 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

75.92
/src/common/atlas/apiClient.ts
1
import createClient from "openapi-fetch";
2✔
2
import type { ClientOptions, FetchOptions, Client, Middleware } from "openapi-fetch";
3
import { ApiClientError } from "./apiClientError.js";
2✔
4
import type { paths, operations } from "./openapi.js";
5
import type { CommonProperties, TelemetryEvent } from "../../telemetry/types.js";
6
import { packageInfo } from "../packageInfo.js";
2✔
7
import type { LoggerBase } from "../logger.js";
8
import { LogId } from "../logger.js";
2✔
9
import { createFetch } from "@mongodb-js/devtools-proxy-support";
2✔
10
import * as oauth from "oauth4webapi";
2✔
11
import { Request as NodeFetchRequest } from "node-fetch";
2✔
12

13
const ATLAS_API_VERSION = "2025-03-12";
2✔
14

15
export interface ApiClientCredentials {
16
    clientId: string;
17
    clientSecret: string;
18
}
19

20
export interface ApiClientOptions {
21
    credentials?: ApiClientCredentials;
22
    baseUrl: string;
23
    userAgent?: string;
24
}
25

26
export interface AccessToken {
27
    access_token: string;
28
    expires_at?: number;
29
}
30

31
export class ApiClient {
2✔
32
    private readonly options: {
33
        baseUrl: string;
34
        userAgent: string;
35
        credentials?: {
36
            clientId: string;
37
            clientSecret: string;
38
        };
39
    };
40

41
    // createFetch assumes that the first parameter of fetch is always a string
42
    // with the URL. However, fetch can also receive a Request object. While
43
    // the typechecking complains, createFetch does passthrough the parameters
44
    // so it works fine.
45
    private static customFetch: typeof fetch = createFetch({
52✔
46
        useEnvironmentVariableProxies: true,
52✔
47
    }) as unknown as typeof fetch;
52✔
48

49
    private client: Client<paths>;
50

51
    private oauth2Client?: oauth.Client;
52
    private oauth2Issuer?: oauth.AuthorizationServer;
53
    private accessToken?: AccessToken;
54

55
    public hasCredentials(): boolean {
52✔
56
        return !!this.oauth2Client && !!this.oauth2Issuer;
245✔
57
    }
245✔
58

59
    private isAccessTokenValid(): boolean {
52✔
60
        return !!(
206✔
61
            this.accessToken &&
206✔
62
            this.accessToken.expires_at !== undefined &&
181✔
63
            this.accessToken.expires_at > Date.now()
181✔
64
        );
65
    }
206✔
66

67
    private getAccessToken = async (): Promise<string | undefined> => {
52✔
68
        if (!this.hasCredentials()) {
206!
69
            return undefined;
×
70
        }
×
71

72
        if (!this.isAccessTokenValid()) {
206✔
73
            this.accessToken = await this.getNewAccessToken();
26✔
74
        }
26✔
75

76
        return this.accessToken?.access_token;
206✔
77
    };
206✔
78

79
    private authMiddleware: Middleware = {
52✔
80
        onRequest: async ({ request, schemaPath }) => {
52✔
81
            if (schemaPath.startsWith("/api/private/unauth") || schemaPath.startsWith("/api/oauth")) {
147!
82
                return undefined;
×
83
            }
×
84

85
            try {
147✔
86
                const accessToken = await this.getAccessToken();
147✔
87
                if (accessToken) {
147✔
88
                    request.headers.set("Authorization", `Bearer ${accessToken}`);
146✔
89
                }
146✔
90
                return request;
147✔
91
            } catch {
147!
92
                // ignore not availble tokens, API will return 401
93
                return undefined;
×
94
            }
×
95
        },
147✔
96
    };
52✔
97

98
    constructor(
52✔
99
        options: ApiClientOptions,
102✔
100
        public readonly logger: LoggerBase
102✔
101
    ) {
102✔
102
        this.options = {
102✔
103
            ...options,
102✔
104
            userAgent:
102✔
105
                options.userAgent ||
102✔
106
                `AtlasMCP/${packageInfo.version} (${process.platform}; ${process.arch}; ${process.env.HOSTNAME || "unknown"})`,
90✔
107
        };
102✔
108

109
        this.client = createClient<paths>({
102✔
110
            baseUrl: this.options.baseUrl,
102✔
111
            headers: {
102✔
112
                "User-Agent": this.options.userAgent,
102✔
113
                Accept: `application/vnd.atlas.${ATLAS_API_VERSION}+json`,
102✔
114
            },
102✔
115
            fetch: ApiClient.customFetch,
102✔
116
            // NodeFetchRequest has more overloadings than the native Request
117
            // so it complains here. However, the interfaces are actually compatible
118
            // so it's not a real problem, just a type checking problem.
119
            Request: NodeFetchRequest as unknown as ClientOptions["Request"],
102✔
120
        });
102✔
121

122
        if (this.options.credentials?.clientId && this.options.credentials?.clientSecret) {
102✔
123
            this.oauth2Issuer = {
26✔
124
                issuer: this.options.baseUrl,
26✔
125
                token_endpoint: new URL("/api/oauth/token", this.options.baseUrl).toString(),
26✔
126
                revocation_endpoint: new URL("/api/oauth/revoke", this.options.baseUrl).toString(),
26✔
127
                token_endpoint_auth_methods_supported: ["client_secret_basic"],
26✔
128
                grant_types_supported: ["client_credentials"],
26✔
129
            };
26✔
130

131
            this.oauth2Client = {
26✔
132
                client_id: this.options.credentials.clientId,
26✔
133
                client_secret: this.options.credentials.clientSecret,
26✔
134
            };
26✔
135

136
            this.client.use(this.authMiddleware);
26✔
137
        }
26✔
138
    }
102✔
139

140
    private getOauthClientAuth(): { client: oauth.Client | undefined; clientAuth: oauth.ClientAuth | undefined } {
52✔
141
        if (this.options.credentials?.clientId && this.options.credentials.clientSecret) {
112✔
142
            const clientSecret = this.options.credentials.clientSecret;
45✔
143
            const clientId = this.options.credentials.clientId;
45✔
144

145
            // We are using our own ClientAuth because ClientSecretBasic URL encodes wrongly
146
            // the username and password (for example, encodes `_` to %5F, which is wrong).
147
            return {
45✔
148
                client: { client_id: clientId },
45✔
149
                clientAuth: (_as, client, _body, headers): void => {
45✔
150
                    const credentials = Buffer.from(`${clientId}:${clientSecret}`).toString("base64");
36✔
151
                    headers.set("Authorization", `Basic ${credentials}`);
36✔
152
                },
36✔
153
            };
45✔
154
        }
45!
155

156
        return { client: undefined, clientAuth: undefined };
67✔
157
    }
112✔
158

159
    private async getNewAccessToken(): Promise<AccessToken | undefined> {
52✔
160
        if (!this.hasCredentials() || !this.oauth2Issuer) {
26!
161
            return undefined;
×
162
        }
×
163

164
        const { client, clientAuth } = this.getOauthClientAuth();
26✔
165
        if (client && clientAuth) {
26✔
166
            try {
26✔
167
                const response = await oauth.clientCredentialsGrantRequest(
26✔
168
                    this.oauth2Issuer,
26✔
169
                    client,
26✔
170
                    clientAuth,
26✔
171
                    new URLSearchParams(),
26✔
172
                    {
26✔
173
                        [oauth.customFetch]: ApiClient.customFetch,
26✔
174
                        headers: {
26✔
175
                            "User-Agent": this.options.userAgent,
26✔
176
                        },
26✔
177
                    }
26✔
178
                );
26✔
179

180
                const result = await oauth.processClientCredentialsResponse(this.oauth2Issuer, client, response);
26!
181
                this.accessToken = {
7✔
182
                    access_token: result.access_token,
7✔
183
                    expires_at: Date.now() + (result.expires_in ?? 0) * 1000,
26!
184
                };
26✔
185
            } catch (error: unknown) {
26!
186
                const err = error instanceof Error ? error : new Error(String(error));
19!
187
                this.logger.error({
19✔
188
                    id: LogId.atlasConnectFailure,
19✔
189
                    context: "apiClient",
19✔
190
                    message: `Failed to request access token: ${err.message}`,
19✔
191
                });
19✔
192
            }
19✔
193
            return this.accessToken;
26✔
194
        }
26!
195

196
        return undefined;
×
197
    }
26✔
198

199
    public async validateAccessToken(): Promise<void> {
52✔
200
        await this.getAccessToken();
21✔
201
    }
21✔
202

203
    public async close(): Promise<void> {
52✔
204
        const { client, clientAuth } = this.getOauthClientAuth();
86✔
205
        try {
86✔
206
            if (this.oauth2Issuer && this.accessToken && client && clientAuth) {
86✔
207
                await oauth.revocationRequest(this.oauth2Issuer, client, clientAuth, this.accessToken.access_token);
10✔
208
            }
8✔
209
        } catch (error: unknown) {
86!
210
            const err = error instanceof Error ? error : new Error(String(error));
2!
211
            this.logger.error({
2✔
212
                id: LogId.atlasApiRevokeFailure,
2✔
213
                context: "apiClient",
2✔
214
                message: `Failed to revoke access token: ${err.message}`,
2✔
215
            });
2✔
216
        }
2✔
217
        this.accessToken = undefined;
86✔
218
    }
86✔
219

220
    public async getIpInfo(): Promise<{
52✔
221
        currentIpv4Address: string;
222
    }> {
24✔
223
        const accessToken = await this.getAccessToken();
24✔
224

225
        const endpoint = "api/private/ipinfo";
24✔
226
        const url = new URL(endpoint, this.options.baseUrl);
24✔
227
        const response = await fetch(url, {
24✔
228
            method: "GET",
24✔
229
            headers: {
24✔
230
                Accept: "application/json",
24✔
231
                Authorization: `Bearer ${accessToken}`,
24✔
232
                "User-Agent": this.options.userAgent,
24✔
233
            },
24✔
234
        });
24✔
235

236
        if (!response.ok) {
24!
237
            throw await ApiClientError.fromResponse(response);
2✔
238
        }
2!
239

240
        return (await response.json()) as Promise<{
22✔
241
            currentIpv4Address: string;
242
        }>;
243
    }
24✔
244

245
    public async sendEvents(events: TelemetryEvent<CommonProperties>[]): Promise<void> {
52✔
246
        if (!this.options.credentials) {
90!
247
            await this.sendUnauthEvents(events);
71✔
248
            return;
71✔
249
        }
71✔
250

251
        try {
19✔
252
            await this.sendAuthEvents(events);
19✔
253
        } catch (error) {
20!
254
            if (error instanceof ApiClientError) {
11✔
255
                if (error.response.status !== 401) {
2!
256
                    throw error;
×
257
                }
×
258
            }
2✔
259

260
            // send unauth events if any of the following are true:
261
            // 1: the token is not valid (not ApiClientError)
262
            // 2: if the api responded with 401 (ApiClientError with status 401)
263
            await this.sendUnauthEvents(events);
11✔
264
        }
10✔
265
    }
90✔
266

267
    private async sendAuthEvents(events: TelemetryEvent<CommonProperties>[]): Promise<void> {
52✔
268
        const accessToken = await this.getAccessToken();
19✔
269
        if (!accessToken) {
19!
270
            throw new Error("No access token available");
8✔
271
        }
8✔
272
        const authUrl = new URL("api/private/v1.0/telemetry/events", this.options.baseUrl);
10✔
273
        const response = await fetch(authUrl, {
10✔
274
            method: "POST",
10✔
275
            headers: {
10✔
276
                Accept: "application/json",
10✔
277
                "Content-Type": "application/json",
10✔
278
                "User-Agent": this.options.userAgent,
10✔
279
                Authorization: `Bearer ${accessToken}`,
10✔
280
            },
10✔
281
            body: JSON.stringify(events),
10✔
282
        });
10✔
283

284
        if (!response.ok) {
12!
285
            throw await ApiClientError.fromResponse(response);
2✔
286
        }
2✔
287
    }
19✔
288

289
    private async sendUnauthEvents(events: TelemetryEvent<CommonProperties>[]): Promise<void> {
52✔
290
        const headers: Record<string, string> = {
82✔
291
            Accept: "application/json",
82✔
292
            "Content-Type": "application/json",
82✔
293
            "User-Agent": this.options.userAgent,
82✔
294
        };
82✔
295

296
        const unauthUrl = new URL("api/private/unauth/telemetry/events", this.options.baseUrl);
82✔
297
        const response = await fetch(unauthUrl, {
82✔
298
            method: "POST",
82✔
299
            headers,
82✔
300
            body: JSON.stringify(events),
82✔
301
        });
82✔
302

303
        if (!response.ok) {
82✔
304
            throw await ApiClientError.fromResponse(response);
1✔
305
        }
1✔
306
    }
82✔
307

308
    // DO NOT EDIT. This is auto-generated code.
309
    // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
310
    async listClustersForAllProjects(options?: FetchOptions<operations["listClustersForAllProjects"]>) {
52✔
311
        const { data, error, response } = await this.client.GET("/api/atlas/v2/clusters", options);
×
312
        if (error) {
×
313
            throw ApiClientError.fromError(response, error);
×
314
        }
×
315
        return data;
×
316
    }
×
317

318
    // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
319
    async listProjects(options?: FetchOptions<operations["listProjects"]>) {
52✔
320
        const { data, error, response } = await this.client.GET("/api/atlas/v2/groups", options);
4✔
321
        if (error) {
4!
322
            throw ApiClientError.fromError(response, error);
1✔
323
        }
1✔
324
        return data;
3✔
325
    }
4✔
326

327
    // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
328
    async createProject(options: FetchOptions<operations["createProject"]>) {
52✔
329
        const { data, error, response } = await this.client.POST("/api/atlas/v2/groups", options);
6✔
330
        if (error) {
6!
331
            throw ApiClientError.fromError(response, error);
×
332
        }
×
333
        return data;
6✔
334
    }
6✔
335

336
    // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
337
    async deleteProject(options: FetchOptions<operations["deleteProject"]>) {
52✔
338
        const { error, response } = await this.client.DELETE("/api/atlas/v2/groups/{groupId}", options);
6✔
339
        if (error) {
3✔
340
            throw ApiClientError.fromError(response, error);
2✔
341
        }
2✔
342
    }
6✔
343

344
    // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
345
    async getProject(options: FetchOptions<operations["getProject"]>) {
52✔
346
        const { data, error, response } = await this.client.GET("/api/atlas/v2/groups/{groupId}", options);
1✔
347
        if (error) {
1!
348
            throw ApiClientError.fromError(response, error);
×
349
        }
×
350
        return data;
1✔
351
    }
1✔
352

353
    // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
354
    async listProjectIpAccessLists(options: FetchOptions<operations["listProjectIpAccessLists"]>) {
52✔
355
        const { data, error, response } = await this.client.GET("/api/atlas/v2/groups/{groupId}/accessList", options);
4✔
356
        if (error) {
4!
357
            throw ApiClientError.fromError(response, error);
×
358
        }
×
359
        return data;
4✔
360
    }
4✔
361

362
    // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
363
    async createProjectIpAccessList(options: FetchOptions<operations["createProjectIpAccessList"]>) {
52✔
364
        const { data, error, response } = await this.client.POST("/api/atlas/v2/groups/{groupId}/accessList", options);
16✔
365
        if (error) {
16!
366
            throw ApiClientError.fromError(response, error);
1✔
367
        }
1!
368
        return data;
15✔
369
    }
16✔
370

371
    // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
372
    async deleteProjectIpAccessList(options: FetchOptions<operations["deleteProjectIpAccessList"]>) {
52✔
373
        const { error, response } = await this.client.DELETE(
5✔
374
            "/api/atlas/v2/groups/{groupId}/accessList/{entryValue}",
5✔
375
            options
5✔
376
        );
5✔
377
        if (error) {
5!
378
            throw ApiClientError.fromError(response, error);
×
379
        }
×
380
    }
5✔
381

382
    // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
383
    async listAlerts(options: FetchOptions<operations["listAlerts"]>) {
52✔
384
        const { data, error, response } = await this.client.GET("/api/atlas/v2/groups/{groupId}/alerts", options);
1✔
385
        if (error) {
1!
386
            throw ApiClientError.fromError(response, error);
×
387
        }
×
388
        return data;
1✔
389
    }
1✔
390

391
    // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
392
    async listClusters(options: FetchOptions<operations["listClusters"]>) {
52✔
393
        const { data, error, response } = await this.client.GET("/api/atlas/v2/groups/{groupId}/clusters", options);
1✔
394
        if (error) {
1!
395
            throw ApiClientError.fromError(response, error);
×
396
        }
×
397
        return data;
1✔
398
    }
1✔
399

400
    // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
401
    async createCluster(options: FetchOptions<operations["createCluster"]>) {
52✔
402
        const { data, error, response } = await this.client.POST("/api/atlas/v2/groups/{groupId}/clusters", options);
2✔
403
        if (error) {
2!
404
            throw ApiClientError.fromError(response, error);
×
405
        }
×
406
        return data;
2✔
407
    }
2✔
408

409
    // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
410
    async deleteCluster(options: FetchOptions<operations["deleteCluster"]>) {
52✔
411
        const { error, response } = await this.client.DELETE(
2✔
412
            "/api/atlas/v2/groups/{groupId}/clusters/{clusterName}",
2✔
413
            options
2✔
414
        );
2✔
415
        if (error) {
2!
416
            throw ApiClientError.fromError(response, error);
×
417
        }
×
418
    }
2✔
419

420
    // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
421
    async getCluster(options: FetchOptions<operations["getCluster"]>) {
52✔
422
        const { data, error, response } = await this.client.GET(
73✔
423
            "/api/atlas/v2/groups/{groupId}/clusters/{clusterName}",
73✔
424
            options
73✔
425
        );
73✔
426
        if (error) {
73!
427
            throw ApiClientError.fromError(response, error);
×
428
        }
×
429
        return data;
73✔
430
    }
73✔
431

432
    // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
433
    async listDropIndexes(options: FetchOptions<operations["listDropIndexes"]>) {
52✔
434
        const { data, error, response } = await this.client.GET(
1✔
435
            "/api/atlas/v2/groups/{groupId}/clusters/{clusterName}/performanceAdvisor/dropIndexSuggestions",
1✔
436
            options
1✔
437
        );
1✔
438
        if (error) {
1!
439
            throw ApiClientError.fromError(response, error);
×
440
        }
×
441
        return data;
1✔
442
    }
1✔
443

444
    // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
445
    async listSchemaAdvice(options: FetchOptions<operations["listSchemaAdvice"]>) {
52✔
446
        const { data, error, response } = await this.client.GET(
1✔
447
            "/api/atlas/v2/groups/{groupId}/clusters/{clusterName}/performanceAdvisor/schemaAdvice",
1✔
448
            options
1✔
449
        );
1✔
450
        if (error) {
1!
451
            throw ApiClientError.fromError(response, error);
×
452
        }
×
453
        return data;
1✔
454
    }
1✔
455

456
    // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
457
    async listClusterSuggestedIndexes(options: FetchOptions<operations["listClusterSuggestedIndexes"]>) {
52✔
458
        const { data, error, response } = await this.client.GET(
1✔
459
            "/api/atlas/v2/groups/{groupId}/clusters/{clusterName}/performanceAdvisor/suggestedIndexes",
1✔
460
            options
1✔
461
        );
1✔
462
        if (error) {
1!
463
            throw ApiClientError.fromError(response, error);
×
464
        }
×
465
        return data;
1✔
466
    }
1✔
467

468
    // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
469
    async listDatabaseUsers(options: FetchOptions<operations["listDatabaseUsers"]>) {
52✔
470
        const { data, error, response } = await this.client.GET(
1✔
471
            "/api/atlas/v2/groups/{groupId}/databaseUsers",
1✔
472
            options
1✔
473
        );
1✔
474
        if (error) {
1!
475
            throw ApiClientError.fromError(response, error);
×
476
        }
×
477
        return data;
1✔
478
    }
1✔
479

480
    // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
481
    async createDatabaseUser(options: FetchOptions<operations["createDatabaseUser"]>) {
52✔
482
        const { data, error, response } = await this.client.POST(
6✔
483
            "/api/atlas/v2/groups/{groupId}/databaseUsers",
6✔
484
            options
6✔
485
        );
6✔
486
        if (error) {
6!
487
            throw ApiClientError.fromError(response, error);
×
488
        }
×
489
        return data;
6✔
490
    }
6✔
491

492
    // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
493
    async deleteDatabaseUser(options: FetchOptions<operations["deleteDatabaseUser"]>) {
52✔
494
        const { error, response } = await this.client.DELETE(
8✔
495
            "/api/atlas/v2/groups/{groupId}/databaseUsers/{databaseName}/{username}",
8✔
496
            options
8✔
497
        );
8✔
498
        if (error) {
8✔
499
            throw ApiClientError.fromError(response, error);
2✔
500
        }
2✔
501
    }
8✔
502

503
    // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
504
    async listFlexClusters(options: FetchOptions<operations["listFlexClusters"]>) {
52✔
505
        const { data, error, response } = await this.client.GET("/api/atlas/v2/groups/{groupId}/flexClusters", options);
×
506
        if (error) {
×
507
            throw ApiClientError.fromError(response, error);
×
508
        }
×
509
        return data;
×
510
    }
×
511

512
    // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
513
    async createFlexCluster(options: FetchOptions<operations["createFlexCluster"]>) {
52✔
514
        const { data, error, response } = await this.client.POST(
×
515
            "/api/atlas/v2/groups/{groupId}/flexClusters",
×
516
            options
×
517
        );
×
518
        if (error) {
×
519
            throw ApiClientError.fromError(response, error);
×
520
        }
×
521
        return data;
×
522
    }
×
523

524
    // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
525
    async deleteFlexCluster(options: FetchOptions<operations["deleteFlexCluster"]>) {
52✔
526
        const { error, response } = await this.client.DELETE(
×
527
            "/api/atlas/v2/groups/{groupId}/flexClusters/{name}",
×
528
            options
×
529
        );
×
530
        if (error) {
×
531
            throw ApiClientError.fromError(response, error);
×
532
        }
×
533
    }
×
534

535
    // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
536
    async getFlexCluster(options: FetchOptions<operations["getFlexCluster"]>) {
52✔
537
        const { data, error, response } = await this.client.GET(
×
538
            "/api/atlas/v2/groups/{groupId}/flexClusters/{name}",
×
539
            options
×
540
        );
×
541
        if (error) {
×
542
            throw ApiClientError.fromError(response, error);
×
543
        }
×
544
        return data;
×
545
    }
×
546

547
    // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
548
    async listSlowQueries(options: FetchOptions<operations["listSlowQueries"]>) {
52✔
549
        const { data, error, response } = await this.client.GET(
×
550
            "/api/atlas/v2/groups/{groupId}/processes/{processId}/performanceAdvisor/slowQueryLogs",
×
551
            options
×
552
        );
×
553
        if (error) {
×
554
            throw ApiClientError.fromError(response, error);
×
555
        }
×
556
        return data;
×
557
    }
×
558

559
    // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
560
    async listOrganizations(options?: FetchOptions<operations["listOrganizations"]>) {
52✔
561
        const { data, error, response } = await this.client.GET("/api/atlas/v2/orgs", options);
10!
562
        if (error) {
8!
563
            throw ApiClientError.fromError(response, error);
×
564
        }
×
565
        return data;
8✔
566
    }
10✔
567

568
    // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
569
    async listOrganizationProjects(options: FetchOptions<operations["listOrganizationProjects"]>) {
52✔
570
        const { data, error, response } = await this.client.GET("/api/atlas/v2/orgs/{orgId}/groups", options);
×
571
        if (error) {
×
572
            throw ApiClientError.fromError(response, error);
×
573
        }
×
574
        return data;
×
575
    }
×
576

577
    // DO NOT EDIT. This is auto-generated code.
578
}
52✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc