Skip to content

Commit d5666da

Browse files
committed
Merge branch 'main' into fmenezes/feat_hide_atlas
2 parents c0b14b1 + 5c43a16 commit d5666da

File tree

20 files changed

+2739
-1961
lines changed

20 files changed

+2739
-1961
lines changed

.github/workflows/code_health.yaml

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,20 @@ jobs:
2020
- name: Run style check
2121
run: npm run check
2222

23+
check-generate:
24+
runs-on: ubuntu-latest
25+
steps:
26+
- uses: GitHubSecurityLab/actions-permissions/monitor@v1
27+
- uses: actions/checkout@v4
28+
- uses: actions/setup-node@v4
29+
with:
30+
node-version-file: package.json
31+
cache: "npm"
32+
- name: Install dependencies
33+
run: npm ci
34+
- name: Run style check
35+
run: npm run generate
36+
2337
run-tests:
2438
strategy:
2539
matrix:
@@ -29,12 +43,6 @@ jobs:
2943
steps:
3044
- uses: GitHubSecurityLab/actions-permissions/monitor@v1
3145
if: matrix.os != 'windows-latest'
32-
- name: Install keyring deps on Ubuntu
33-
if: matrix.os == 'ubuntu-latest'
34-
run: |
35-
sudo apt update -y
36-
sudo apt install -y gnome-keyring libdbus-1-dev
37-
3846
- uses: actions/checkout@v4
3947
- uses: actions/setup-node@v4
4048
with:
@@ -43,6 +51,10 @@ jobs:
4351
- name: Install dependencies
4452
run: npm ci
4553
- name: Run tests
54+
env:
55+
MDB_MCP_API_CLIENT_ID: ${{ secrets.TEST_ATLAS_CLIENT_ID }}
56+
MDB_MCP_API_CLIENT_SECRET: ${{ secrets.TEST_ATLAS_CLIENT_SECRET }}
57+
MDB_MCP_API_BASE_URL: ${{ vars.TEST_ATLAS_BASE_URL }}
4658
run: npm test
4759
- name: Coveralls GitHub Action
4860
uses: coverallsapp/github-action@v2.3.6

README.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,12 +98,14 @@ You may experiment asking `Can you connect to my mongodb instance?`.
9898

9999
#### MongoDB Atlas Tools
100100

101-
- `atlas-list-clusters` - Lists MongoDB Atlas clusters
101+
- `atlas-list-orgs` - Lists MongoDB Atlas organizations
102102
- `atlas-list-projects` - Lists MongoDB Atlas projects
103+
- `atlas-create-project` - Creates a new MongoDB Atlas project
104+
- `atlas-list-clusters` - Lists MongoDB Atlas clusters
103105
- `atlas-inspect-cluster` - Inspect a specific MongoDB Atlas cluster
104106
- `atlas-create-free-cluster` - Create a free MongoDB Atlas cluster
105-
- `atlas-create-access-list` - Configure IP/CIDR access list for MongoDB Atlas clusters
106107
- `atlas-inspect-access-list` - Inspect IP/CIDR ranges with access to MongoDB Atlas clusters
108+
- `atlas-create-access-list` - Configure IP/CIDR access list for MongoDB Atlas clusters
107109
- `atlas-list-db-users` - List MongoDB Atlas database users
108110
- `atlas-create-db-user` - List MongoDB Atlas database users
109111

jest.config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ export default {
33
preset: "ts-jest/presets/default-esm",
44
testEnvironment: "node",
55
extensionsToTreatAsEsm: [".ts"],
6+
testTimeout: 3600000, // 3600 seconds
67
moduleNameMapper: {
78
"^(\\.{1,2}/.*)\\.js$": "$1", // Map .js to real paths for ESM
89
},

package-lock.json

Lines changed: 1721 additions & 1890 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

scripts/apply.ts

Lines changed: 54 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,22 @@ import fs from "fs/promises";
22
import { OpenAPIV3_1 } from "openapi-types";
33
import argv from "yargs-parser";
44

5-
function findParamFromRef(ref: string, openapi: OpenAPIV3_1.Document): OpenAPIV3_1.ParameterObject {
5+
function findObjectFromRef<T>(obj: T | OpenAPIV3_1.ReferenceObject, openapi: OpenAPIV3_1.Document): T {
6+
const ref = (obj as OpenAPIV3_1.ReferenceObject).$ref;
7+
if (ref === undefined) {
8+
return obj as T;
9+
}
610
const paramParts = ref.split("/");
711
paramParts.shift(); // Remove the first part which is always '#'
8-
let param: any = openapi; // eslint-disable-line @typescript-eslint/no-explicit-any
12+
let foundObj: any = openapi; // eslint-disable-line @typescript-eslint/no-explicit-any
913
while (true) {
1014
const part = paramParts.shift();
1115
if (!part) {
1216
break;
1317
}
14-
param = param[part];
18+
foundObj = foundObj[part];
1519
}
16-
return param;
20+
return foundObj as T;
1721
}
1822

1923
async function main() {
@@ -32,6 +36,7 @@ async function main() {
3236
operationId: string;
3337
requiredParams: boolean;
3438
tag: string;
39+
hasResponseBody: boolean;
3540
}[] = [];
3641

3742
const openapi = JSON.parse(specFile) as OpenAPIV3_1.Document;
@@ -44,13 +49,27 @@ async function main() {
4449
}
4550

4651
let requiredParams = !!operation.requestBody;
52+
let hasResponseBody = false;
53+
for (const code in operation.responses) {
54+
try {
55+
const httpCode = parseInt(code, 10);
56+
if (httpCode >= 200 && httpCode < 300) {
57+
const response = operation.responses[code];
58+
const responseObject = findObjectFromRef(response, openapi);
59+
if (responseObject.content) {
60+
for (const contentType in responseObject.content) {
61+
const content = responseObject.content[contentType];
62+
hasResponseBody = !!content.schema;
63+
}
64+
}
65+
}
66+
} catch {
67+
continue;
68+
}
69+
}
4770

4871
for (const param of operation.parameters || []) {
49-
const ref = (param as OpenAPIV3_1.ReferenceObject).$ref as string | undefined;
50-
let paramObject: OpenAPIV3_1.ParameterObject = param as OpenAPIV3_1.ParameterObject;
51-
if (ref) {
52-
paramObject = findParamFromRef(ref, openapi);
53-
}
72+
const paramObject = findObjectFromRef(param, openapi);
5473
if (paramObject.in === "path") {
5574
requiredParams = true;
5675
}
@@ -61,27 +80,45 @@ async function main() {
6180
method: method.toUpperCase(),
6281
operationId: operation.operationId || "",
6382
requiredParams,
83+
hasResponseBody,
6484
tag: operation.tags[0],
6585
});
6686
}
6787
}
6888

6989
const operationOutput = operations
7090
.map((operation) => {
71-
const { operationId, method, path, requiredParams } = operation;
91+
const { operationId, method, path, requiredParams, hasResponseBody } = operation;
7292
return `async ${operationId}(options${requiredParams ? "" : "?"}: FetchOptions<operations["${operationId}"]>) {
73-
const { data } = await this.client.${method}("${path}", options);
74-
return data;
75-
}
93+
${hasResponseBody ? `const { data } = ` : ``}await this.client.${method}("${path}", options);
94+
${
95+
hasResponseBody
96+
? `return data;
97+
`
98+
: ``
99+
}}
76100
`;
77101
})
78102
.join("\n");
79103

80104
const templateFile = (await fs.readFile(file, "utf8")) as string;
81-
const output = templateFile.replace(
82-
/\/\/ DO NOT EDIT\. This is auto-generated code\.\n.*\/\/ DO NOT EDIT\. This is auto-generated code\./g,
83-
operationOutput
84-
);
105+
const templateLines = templateFile.split("\n");
106+
let outputLines: string[] = [];
107+
let addLines = true;
108+
for (const line of templateLines) {
109+
if (line.includes("DO NOT EDIT. This is auto-generated code.")) {
110+
addLines = !addLines;
111+
outputLines.push(line);
112+
if (!addLines) {
113+
outputLines.push(operationOutput);
114+
}
115+
continue;
116+
}
117+
if (addLines) {
118+
outputLines.push(line);
119+
}
120+
}
121+
const output = outputLines.join("\n");
85122

86123
await fs.writeFile(file, output, "utf8");
87124
}

scripts/filter.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,19 @@ function filterOpenapi(openapi: OpenAPIV3_1.Document): OpenAPIV3_1.Document {
2222
"listOrganizations",
2323
"getProject",
2424
"createProject",
25+
"deleteProject",
2526
"listClusters",
2627
"getCluster",
2728
"createCluster",
29+
"deleteCluster",
2830
"listClustersForAllProjects",
2931
"createDatabaseUser",
32+
"deleteDatabaseUser",
3033
"listDatabaseUsers",
3134
"listProjectIpAccessLists",
3235
"createProjectIpAccessList",
36+
"deleteProjectIpAccessList",
37+
"listOrganizationProjects",
3338
];
3439

3540
const filteredPaths = {};

src/common/atlas/apiClient.ts

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -119,8 +119,8 @@ export class ApiClient {
119119
}
120120

121121
// DO NOT EDIT. This is auto-generated code.
122-
async listOrganizations(options?: FetchOptions<operations["listOrganizations"]>) {
123-
const { data } = await this.client.GET("/api/atlas/v2/orgs", options);
122+
async listClustersForAllProjects(options?: FetchOptions<operations["listClustersForAllProjects"]>) {
123+
const { data } = await this.client.GET("/api/atlas/v2/clusters", options);
124124
return data;
125125
}
126126

@@ -134,16 +134,29 @@ export class ApiClient {
134134
return data;
135135
}
136136

137-
async listClustersForAllProjects(options?: FetchOptions<operations["listClustersForAllProjects"]>) {
138-
const { data } = await this.client.GET("/api/atlas/v2/clusters", options);
139-
return data;
137+
async deleteProject(options: FetchOptions<operations["deleteProject"]>) {
138+
await this.client.DELETE("/api/atlas/v2/groups/{groupId}", options);
140139
}
141140

142141
async getProject(options: FetchOptions<operations["getProject"]>) {
143142
const { data } = await this.client.GET("/api/atlas/v2/groups/{groupId}", options);
144143
return data;
145144
}
146145

146+
async listProjectIpAccessLists(options: FetchOptions<operations["listProjectIpAccessLists"]>) {
147+
const { data } = await this.client.GET("/api/atlas/v2/groups/{groupId}/accessList", options);
148+
return data;
149+
}
150+
151+
async createProjectIpAccessList(options: FetchOptions<operations["createProjectIpAccessList"]>) {
152+
const { data } = await this.client.POST("/api/atlas/v2/groups/{groupId}/accessList", options);
153+
return data;
154+
}
155+
156+
async deleteProjectIpAccessList(options: FetchOptions<operations["deleteProjectIpAccessList"]>) {
157+
await this.client.DELETE("/api/atlas/v2/groups/{groupId}/accessList/{entryValue}", options);
158+
}
159+
147160
async listClusters(options: FetchOptions<operations["listClusters"]>) {
148161
const { data } = await this.client.GET("/api/atlas/v2/groups/{groupId}/clusters", options);
149162
return data;
@@ -154,13 +167,12 @@ export class ApiClient {
154167
return data;
155168
}
156169

157-
async listProjectIpAccessLists(options: FetchOptions<operations["listProjectIpAccessLists"]>) {
158-
const { data } = await this.client.GET("/api/atlas/v2/groups/{groupId}/accessList", options);
159-
return data;
170+
async deleteCluster(options: FetchOptions<operations["deleteCluster"]>) {
171+
await this.client.DELETE("/api/atlas/v2/groups/{groupId}/clusters/{clusterName}", options);
160172
}
161173

162-
async createProjectIpAccessList(options: FetchOptions<operations["createProjectIpAccessList"]>) {
163-
const { data } = await this.client.POST("/api/atlas/v2/groups/{groupId}/accessList", options);
174+
async getCluster(options: FetchOptions<operations["getCluster"]>) {
175+
const { data } = await this.client.GET("/api/atlas/v2/groups/{groupId}/clusters/{clusterName}", options);
164176
return data;
165177
}
166178

@@ -174,9 +186,19 @@ export class ApiClient {
174186
return data;
175187
}
176188

177-
async getCluster(options: FetchOptions<operations["getCluster"]>) {
178-
const { data } = await this.client.GET("/api/atlas/v2/groups/{groupId}/clusters/{clusterName}", options);
189+
async deleteDatabaseUser(options: FetchOptions<operations["deleteDatabaseUser"]>) {
190+
await this.client.DELETE("/api/atlas/v2/groups/{groupId}/databaseUsers/{databaseName}/{username}", options);
191+
}
192+
193+
async listOrganizations(options?: FetchOptions<operations["listOrganizations"]>) {
194+
const { data } = await this.client.GET("/api/atlas/v2/orgs", options);
179195
return data;
180196
}
197+
198+
async listOrganizationProjects(options: FetchOptions<operations["listOrganizationProjects"]>) {
199+
const { data } = await this.client.GET("/api/atlas/v2/orgs/{orgId}/groups", options);
200+
return data;
201+
}
202+
181203
// DO NOT EDIT. This is auto-generated code.
182204
}

0 commit comments

Comments
 (0)