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

mongodb-js / mongodb-mcp-server / 18714130420

22 Oct 2025 11:06AM UTC coverage: 80.129% (-1.8%) from 81.905%
18714130420

Pull #674

github

web-flow
Merge ec48e82c7 into 17b595b2f
Pull Request #674: chore: move perf advisor to long running tests - MCP-269

1317 of 1776 branches covered (74.16%)

Branch coverage included in aggregate %.

6018 of 7378 relevant lines covered (81.57%)

73.88 hits per line

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

71.18
/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({
48✔
46
        useEnvironmentVariableProxies: true,
48✔
47
    }) as unknown as typeof fetch;
48✔
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 {
48✔
56
        return !!this.oauth2Client && !!this.oauth2Issuer;
162✔
57
    }
162✔
58

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

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

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

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

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

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

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

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

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

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

136
            this.client.use(this.authMiddleware);
25✔
137
        }
25✔
138
    }
123✔
139

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

156
        return { client: undefined, clientAuth: undefined };
85✔
157
    }
128✔
158

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

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

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

196
        return undefined;
×
197
    }
25✔
198

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

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

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

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

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

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

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

251
        try {
18✔
252
            await this.sendAuthEvents(events);
18✔
253
        } catch (error) {
19!
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
    }
107✔
266

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

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

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

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

303
        if (!response.ok) {
100!
304
            throw await ApiClientError.fromResponse(response);
1✔
305
        }
1✔
306
    }
100✔
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"]>) {
48✔
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"]>) {
48✔
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"]>) {
48✔
329
        const { data, error, response } = await this.client.POST("/api/atlas/v2/groups", options);
5✔
330
        if (error) {
5!
331
            throw ApiClientError.fromError(response, error);
×
332
        }
×
333
        return data;
5✔
334
    }
5✔
335

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

344
    // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
345
    async getProject(options: FetchOptions<operations["getProject"]>) {
48✔
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"]>) {
48✔
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"]>) {
48✔
364
        const { data, error, response } = await this.client.POST("/api/atlas/v2/groups/{groupId}/accessList", options);
15✔
365
        if (error) {
15!
366
            throw ApiClientError.fromError(response, error);
1✔
367
        }
1!
368
        return data;
14✔
369
    }
15✔
370

371
    // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
372
    async deleteProjectIpAccessList(options: FetchOptions<operations["deleteProjectIpAccessList"]>) {
48✔
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"]>) {
48✔
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"]>) {
48✔
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"]>) {
48✔
402
        const { data, error, response } = await this.client.POST("/api/atlas/v2/groups/{groupId}/clusters", options);
1✔
403
        if (error) {
1!
404
            throw ApiClientError.fromError(response, error);
×
405
        }
×
406
        return data;
1✔
407
    }
1✔
408

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

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

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

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

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

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

503
    // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
504
    async listFlexClusters(options: FetchOptions<operations["listFlexClusters"]>) {
48✔
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"]>) {
48✔
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"]>) {
48✔
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"]>) {
48✔
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"]>) {
48✔
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"]>) {
48✔
561
        const { data, error, response } = await this.client.GET("/api/atlas/v2/orgs", options);
9!
562
        if (error) {
7!
563
            throw ApiClientError.fromError(response, error);
×
564
        }
×
565
        return data;
7✔
566
    }
9✔
567

568
    // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
569
    async listOrganizationProjects(options: FetchOptions<operations["listOrganizationProjects"]>) {
48✔
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
}
48✔
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

© 2025 Coveralls, Inc