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

mongodb-js / mongodb-mcp-server / 20343091701

18 Dec 2025 04:01PM UTC coverage: 74.508% (-4.9%) from 79.433%
20343091701

push

github

web-flow
chore(deps): bump peter-evans/create-pull-request from 7.0.9 to 8.0.0 (#802)

1338 of 1659 branches covered (80.65%)

Branch coverage included in aggregate %.

6159 of 8403 relevant lines covered (73.3%)

78.92 hits per line

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

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

13
const ATLAS_API_VERSION = "2025-03-12";
3✔
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 {
3✔
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({
3✔
46
        useEnvironmentVariableProxies: true,
3✔
47
    }) as unknown as typeof fetch;
3✔
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 {
3✔
56
        return !!this.oauth2Client && !!this.oauth2Issuer;
67✔
57
    }
67✔
58

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

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

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

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

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

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

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

109
        this.client = createClient<paths>({
144✔
110
            baseUrl: this.options.baseUrl,
144✔
111
            headers: {
144✔
112
                "User-Agent": this.options.userAgent,
144✔
113
                Accept: `application/vnd.atlas.${ATLAS_API_VERSION}+json`,
144✔
114
            },
144✔
115
            fetch: ApiClient.customFetch,
144✔
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"],
144✔
120
        });
144✔
121

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

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

136
            this.client.use(this.authMiddleware);
19✔
137
        }
19✔
138
    }
144✔
139

140
    private getOauthClientAuth(): { client: oauth.Client | undefined; clientAuth: oauth.ClientAuth | undefined } {
3✔
141
        if (this.options.credentials?.clientId && this.options.credentials.clientSecret) {
133!
142
            const clientSecret = this.options.credentials.clientSecret;
31✔
143
            const clientId = this.options.credentials.clientId;
31✔
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 {
31✔
148
                client: { client_id: clientId },
31✔
149
                clientAuth: (_as, client, _body, headers): void => {
31✔
150
                    const credentials = Buffer.from(`${clientId}:${clientSecret}`).toString("base64");
22✔
151
                    headers.set("Authorization", `Basic ${credentials}`);
22✔
152
                },
22✔
153
            };
31✔
154
        }
31✔
155

156
        return { client: undefined, clientAuth: undefined };
102✔
157
    }
133✔
158

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

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

180
                const result = await oauth.processClientCredentialsResponse(this.oauth2Issuer, client, response);
19!
181
                this.accessToken = {
×
182
                    access_token: result.access_token,
×
183
                    expires_at: Date.now() + (result.expires_in ?? 0) * 1000,
19✔
184
                };
19✔
185
            } catch (error: unknown) {
19✔
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;
19✔
194
        }
19!
195

196
        return undefined;
×
197
    }
19✔
198

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

203
    public async close(): Promise<void> {
3✔
204
        const { client, clientAuth } = this.getOauthClientAuth();
114✔
205
        try {
114✔
206
            if (this.oauth2Issuer && this.accessToken && client && clientAuth) {
114!
207
                await oauth.revocationRequest(this.oauth2Issuer, client, clientAuth, this.accessToken.access_token);
3✔
208
            }
1✔
209
        } catch (error: unknown) {
114!
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;
114✔
218
    }
114✔
219

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

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

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

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

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

251
        try {
12✔
252
            await this.sendAuthEvents(events);
12✔
253
        } catch (error) {
13✔
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
    }
118✔
266

267
    private async sendAuthEvents(events: TelemetryEvent<CommonProperties>[]): Promise<void> {
3✔
268
        const accessToken = await this.getAccessToken();
12✔
269
        if (!accessToken) {
12✔
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);
3✔
273
        const response = await fetch(authUrl, {
3✔
274
            method: "POST",
3✔
275
            headers: {
3✔
276
                Accept: "application/json",
3✔
277
                "Content-Type": "application/json",
3✔
278
                "User-Agent": this.options.userAgent,
3✔
279
                Authorization: `Bearer ${accessToken}`,
3✔
280
            },
3✔
281
            body: JSON.stringify(events),
3✔
282
        });
3✔
283

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

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

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

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

308
    // DO NOT EDIT. This is auto-generated code.
309
    // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
310
    async listClusterDetails(options?: FetchOptions<operations["listClusterDetails"]>) {
3✔
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 listGroups(options?: FetchOptions<operations["listGroups"]>) {
3✔
320
        const { data, error, response } = await this.client.GET("/api/atlas/v2/groups", options);
2✔
321
        if (error) {
2✔
322
            throw ApiClientError.fromError(response, error);
1✔
323
        }
1✔
324
        return data;
1✔
325
    }
2✔
326

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

563
    // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
564
    async listOrgs(options?: FetchOptions<operations["listOrgs"]>) {
3✔
565
        const { data, error, response } = await this.client.GET("/api/atlas/v2/orgs", options);
2!
566
        if (error) {
×
567
            throw ApiClientError.fromError(response, error);
×
568
        }
×
569
        return data;
×
570
    }
2✔
571

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

581
    // DO NOT EDIT. This is auto-generated code.
582
}
3✔
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