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

mongodb-js / mongodb-mcp-server / 14599304051

22 Apr 2025 04:00PM UTC coverage: 76.792% (+23.6%) from 53.208%
14599304051

Pull #83

github

fmenezes
fix: tests
Pull Request #83: chore: add atlas tests

78 of 148 branches covered (52.7%)

Branch coverage included in aggregate %.

26 of 34 new or added lines in 5 files covered. (76.47%)

1 existing line in 1 file now uncovered.

554 of 675 relevant lines covered (82.07%)

27.25 hits per line

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

88.89
/src/common/atlas/apiClient.ts
1
import config from "../../config.js";
16✔
2
import createClient, { Client, FetchOptions, Middleware } from "openapi-fetch";
16✔
3
import { AccessToken, ClientCredentials } from "simple-oauth2";
16✔
4
import { ApiClientError } from "./apiClientError.js";
16✔
5
import { paths, operations } from "./openapi.js";
6

7
const ATLAS_API_VERSION = "2025-03-12";
16✔
8

9
export interface ApiClientOptions {
10
    credentials?: {
11
        clientId: string;
12
        clientSecret: string;
13
    };
14
    baseUrl?: string;
15
    userAgent?: string;
16
}
17

18
export class ApiClient {
16✔
19
    private options: {
20
        baseUrl: string;
21
        userAgent: string;
22
        credentials?: {
23
            clientId: string;
24
            clientSecret: string;
25
        };
26
    };
27
    private client: Client<paths>;
28
    private oauth2Client?: ClientCredentials;
29
    private accessToken?: AccessToken;
30

31
    private getAccessToken = async () => {
5✔
32
        if (this.oauth2Client && (!this.accessToken || this.accessToken.expired())) {
90✔
33
            this.accessToken = await this.oauth2Client.getToken({});
5✔
34
        }
35
        return this.accessToken?.token.access_token as string | undefined;
90✔
36
    };
37

38
    private authMiddleware: Middleware = {
5✔
39
        onRequest: async ({ request, schemaPath }) => {
40
            if (schemaPath.startsWith("/api/private/unauth") || schemaPath.startsWith("/api/oauth")) {
88!
41
                return undefined;
×
42
            }
43

44
            try {
88✔
45
                const accessToken = await this.getAccessToken();
88✔
46
                request.headers.set("Authorization", `Bearer ${accessToken}`);
88✔
47
                return request;
88✔
48
            } catch {
49
                // ignore not availble tokens, API will return 401
50
            }
51
        },
52
    };
53

54
    private readonly errorMiddleware: Middleware = {
5✔
55
        async onResponse({ response }) {
56
            if (!response.ok) {
88✔
57
                throw await ApiClientError.fromResponse(response);
1✔
58
            }
59
        },
60
    };
61

62
    constructor(options?: ApiClientOptions) {
63
        this.options = {
5✔
64
            ...options,
65
            baseUrl: options?.baseUrl || "https://cloud.mongodb.com/",
5!
66
            userAgent:
67
                options?.userAgent ||
10✔
68
                `AtlasMCP/${config.version} (${process.platform}; ${process.arch}; ${process.env.HOSTNAME || "unknown"})`,
10✔
69
        };
70

71
        this.client = createClient<paths>({
5✔
72
            baseUrl: this.options.baseUrl,
73
            headers: {
74
                "User-Agent": this.options.userAgent,
75
                Accept: `application/vnd.atlas.${ATLAS_API_VERSION}+json`,
76
            },
77
        });
78
        if (this.options.credentials?.clientId && this.options.credentials?.clientSecret) {
5✔
79
            this.oauth2Client = new ClientCredentials({
5✔
80
                client: {
81
                    id: this.options.credentials.clientId,
82
                    secret: this.options.credentials.clientSecret,
83
                },
84
                auth: {
85
                    tokenHost: this.options.baseUrl,
86
                    tokenPath: "/api/oauth/token",
87
                },
88
            });
89
            this.client.use(this.authMiddleware);
5✔
90
        }
91
        this.client.use(this.errorMiddleware);
5✔
92
    }
93

94
    public async getIpInfo(): Promise<{
95
        currentIpv4Address: string;
96
    }> {
97
        const accessToken = await this.getAccessToken();
2✔
98

99
        const endpoint = "api/private/ipinfo";
2✔
100
        const url = new URL(endpoint, this.options.baseUrl);
2✔
101
        const response = await fetch(url, {
2✔
102
            method: "GET",
103
            headers: {
104
                Accept: "application/json",
105
                Authorization: `Bearer ${accessToken}`,
106
                "User-Agent": this.options.userAgent,
107
            },
108
        });
109

110
        if (!response.ok) {
2!
111
            throw await ApiClientError.fromResponse(response);
×
112
        }
113

114
        return (await response.json()) as Promise<{
2✔
115
            currentIpv4Address: string;
116
        }>;
117
    }
118

119
    // DO NOT EDIT. This is auto-generated code.
120
    async listClustersForAllProjects(options?: FetchOptions<operations["listClustersForAllProjects"]>) {
NEW
121
        const { data } = await this.client.GET("/api/atlas/v2/clusters", options);
×
UNCOV
122
        return data;
×
123
    }
124

125
    async listProjects(options?: FetchOptions<operations["listProjects"]>) {
126
        const { data } = await this.client.GET("/api/atlas/v2/groups", options);
2✔
127
        return data;
2✔
128
    }
129

130
    async createProject(options: FetchOptions<operations["createProject"]>) {
131
        const { data } = await this.client.POST("/api/atlas/v2/groups", options);
4✔
132
        return data;
4✔
133
    }
134

135
    async deleteProject(options: FetchOptions<operations["deleteProject"]>) {
136
        await this.client.DELETE("/api/atlas/v2/groups/{groupId}", options);
4✔
137
    }
138

139
    async getProject(options: FetchOptions<operations["getProject"]>) {
140
        const { data } = await this.client.GET("/api/atlas/v2/groups/{groupId}", options);
1✔
141
        return data;
1✔
142
    }
143

144
    async listProjectIpAccessLists(options: FetchOptions<operations["listProjectIpAccessLists"]>) {
145
        const { data } = await this.client.GET("/api/atlas/v2/groups/{groupId}/accessList", options);
1✔
146
        return data;
1✔
147
    }
148

149
    async createProjectIpAccessList(options: FetchOptions<operations["createProjectIpAccessList"]>) {
150
        const { data } = await this.client.POST("/api/atlas/v2/groups/{groupId}/accessList", options);
1✔
151
        return data;
1✔
152
    }
153

154
    async deleteProjectIpAccessList(options: FetchOptions<operations["deleteProjectIpAccessList"]>) {
155
        await this.client.DELETE("/api/atlas/v2/groups/{groupId}/accessList/{entryValue}", options);
3✔
156
    }
157

158
    async listClusters(options: FetchOptions<operations["listClusters"]>) {
159
        const { data } = await this.client.GET("/api/atlas/v2/groups/{groupId}/clusters", options);
1✔
160
        return data;
1✔
161
    }
162

163
    async createCluster(options: FetchOptions<operations["createCluster"]>) {
164
        const { data } = await this.client.POST("/api/atlas/v2/groups/{groupId}/clusters", options);
1✔
165
        return data;
1✔
166
    }
167

168
    async deleteCluster(options: FetchOptions<operations["deleteCluster"]>) {
169
        await this.client.DELETE("/api/atlas/v2/groups/{groupId}/clusters/{clusterName}", options);
1✔
170
    }
171

172
    async getCluster(options: FetchOptions<operations["getCluster"]>) {
173
        const { data } = await this.client.GET("/api/atlas/v2/groups/{groupId}/clusters/{clusterName}", options);
61✔
174
        return data;
60✔
175
    }
176

177
    async listDatabaseUsers(options: FetchOptions<operations["listDatabaseUsers"]>) {
178
        const { data } = await this.client.GET("/api/atlas/v2/groups/{groupId}/databaseUsers", options);
1✔
179
        return data;
1✔
180
    }
181

182
    async createDatabaseUser(options: FetchOptions<operations["createDatabaseUser"]>) {
183
        const { data } = await this.client.POST("/api/atlas/v2/groups/{groupId}/databaseUsers", options);
1✔
184
        return data;
1✔
185
    }
186

187
    async deleteDatabaseUser(options: FetchOptions<operations["deleteDatabaseUser"]>) {
188
        await this.client.DELETE("/api/atlas/v2/groups/{groupId}/databaseUsers/{databaseName}/{username}", options);
1✔
189
    }
190

191
    async listOrganizations(options?: FetchOptions<operations["listOrganizations"]>) {
192
        const { data } = await this.client.GET("/api/atlas/v2/orgs", options);
5✔
193
        return data;
5✔
194
    }
195

196
    async listOrganizationProjects(options: FetchOptions<operations["listOrganizationProjects"]>) {
NEW
197
        const { data } = await this.client.GET("/api/atlas/v2/orgs/{orgId}/groups", options);
×
NEW
198
        return data;
×
199
    }
200

201
    // DO NOT EDIT. This is auto-generated code.
202
}
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