@executor-js/emulate 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api.d.ts +333 -3
- package/dist/api.js +125 -13
- package/dist/api.js.map +1 -1
- package/dist/{dist-YPRJYQHW.js → dist-FE2JWST3.js} +846 -234
- package/dist/dist-FE2JWST3.js.map +1 -0
- package/dist/{dist-K4CVTD6K.js → dist-KBZ6SM7D.js} +357 -12
- package/dist/dist-KBZ6SM7D.js.map +1 -0
- package/dist/{dist-RMPDKZUA.js → dist-OT6R2YO2.js} +440 -81
- package/dist/dist-OT6R2YO2.js.map +1 -0
- package/dist/{dist-WBKONLOE.js → dist-OVTPVMMW.js} +636 -165
- package/dist/dist-OVTPVMMW.js.map +1 -0
- package/dist/{dist-BTEY33DJ.js → dist-QXFWC3LV.js} +567 -75
- package/dist/dist-QXFWC3LV.js.map +1 -0
- package/dist/{dist-IYZPDKJW.js → dist-S47YJ552.js} +543 -43
- package/dist/dist-S47YJ552.js.map +1 -0
- package/dist/{dist-XM5HSBDC.js → dist-TWJXVA7X.js} +268 -30
- package/dist/dist-TWJXVA7X.js.map +1 -0
- package/dist/{dist-JJ2ZRCAX.js → dist-YXHZTLFR.js} +138 -7
- package/dist/dist-YXHZTLFR.js.map +1 -0
- package/dist/index.js +10 -10
- package/dist/index.js.map +1 -1
- package/package.json +4 -7
- package/dist/dist-BTEY33DJ.js.map +0 -1
- package/dist/dist-IYZPDKJW.js.map +0 -1
- package/dist/dist-JJ2ZRCAX.js.map +0 -1
- package/dist/dist-K4CVTD6K.js.map +0 -1
- package/dist/dist-RMPDKZUA.js.map +0 -1
- package/dist/dist-WBKONLOE.js.map +0 -1
- package/dist/dist-XM5HSBDC.js.map +0 -1
- package/dist/dist-YPRJYQHW.js.map +0 -1
|
@@ -247,8 +247,8 @@ function dataApiRoutes(ctx) {
|
|
|
247
247
|
let results = docs.map((d) => d.data);
|
|
248
248
|
for (const stage of pipeline) {
|
|
249
249
|
if ("$match" in stage) {
|
|
250
|
-
const
|
|
251
|
-
results = results.filter((d) => matchesFilter(d,
|
|
250
|
+
const filter2 = stage.$match;
|
|
251
|
+
results = results.filter((d) => matchesFilter(d, filter2));
|
|
252
252
|
} else if ("$limit" in stage) {
|
|
253
253
|
results = results.slice(0, stage.$limit);
|
|
254
254
|
} else if ("$skip" in stage) {
|
|
@@ -277,8 +277,8 @@ function ensureCollectionExists(ms, clusterId, database, collection) {
|
|
|
277
277
|
ms().collections.insert({ cluster_id: clusterId, database, name: collection });
|
|
278
278
|
}
|
|
279
279
|
}
|
|
280
|
-
function matchesFilter(data,
|
|
281
|
-
for (const [key, value] of Object.entries(
|
|
280
|
+
function matchesFilter(data, filter2) {
|
|
281
|
+
for (const [key, value] of Object.entries(filter2)) {
|
|
282
282
|
if (key === "$and") {
|
|
283
283
|
const conditions = value;
|
|
284
284
|
if (!conditions.every((cond) => matchesFilter(data, cond))) return false;
|
|
@@ -357,9 +357,9 @@ function getNestedValue(obj, path) {
|
|
|
357
357
|
}
|
|
358
358
|
return current;
|
|
359
359
|
}
|
|
360
|
-
function matchFilter(docs,
|
|
361
|
-
if (!
|
|
362
|
-
return docs.filter((d) => matchesFilter(d.data,
|
|
360
|
+
function matchFilter(docs, filter2) {
|
|
361
|
+
if (!filter2 || Object.keys(filter2).length === 0) return null;
|
|
362
|
+
return docs.filter((d) => matchesFilter(d.data, filter2));
|
|
363
363
|
}
|
|
364
364
|
function applyProjection(data, projection) {
|
|
365
365
|
if (!projection || Object.keys(projection).length === 0) return data;
|
|
@@ -505,9 +505,9 @@ function sortBySpec(docs, sortSpec, accessor) {
|
|
|
505
505
|
return 0;
|
|
506
506
|
});
|
|
507
507
|
}
|
|
508
|
-
function extractEqualityFields(
|
|
508
|
+
function extractEqualityFields(filter2) {
|
|
509
509
|
const result = {};
|
|
510
|
-
for (const [key, value] of Object.entries(
|
|
510
|
+
for (const [key, value] of Object.entries(filter2)) {
|
|
511
511
|
if (key.startsWith("$")) continue;
|
|
512
512
|
if (value !== null && typeof value === "object" && !Array.isArray(value)) {
|
|
513
513
|
const ops = value;
|
|
@@ -528,10 +528,10 @@ function adminRoutes(ctx) {
|
|
|
528
528
|
});
|
|
529
529
|
});
|
|
530
530
|
app.get("/api/atlas/v2/groups/:groupId", (c) => {
|
|
531
|
-
const
|
|
532
|
-
const project = ms().projects.findOneBy("group_id",
|
|
531
|
+
const groupId2 = c.req.param("groupId");
|
|
532
|
+
const project = ms().projects.findOneBy("group_id", groupId2);
|
|
533
533
|
if (!project) {
|
|
534
|
-
return mongoError(c, "GROUP_NOT_FOUND", `Group '${
|
|
534
|
+
return mongoError(c, "GROUP_NOT_FOUND", `Group '${groupId2}' not found.`, 404);
|
|
535
535
|
}
|
|
536
536
|
return mongoOk(c, formatProject(project));
|
|
537
537
|
});
|
|
@@ -544,9 +544,9 @@ function adminRoutes(ctx) {
|
|
|
544
544
|
if (existing) {
|
|
545
545
|
return mongoError(c, "DUPLICATE_GROUP_NAME", `Group name '${body.name}' already exists.`, 409);
|
|
546
546
|
}
|
|
547
|
-
const
|
|
547
|
+
const groupId2 = generateGroupId();
|
|
548
548
|
const project = ms().projects.insert({
|
|
549
|
-
group_id:
|
|
549
|
+
group_id: groupId2,
|
|
550
550
|
name: body.name,
|
|
551
551
|
org_id: body.orgId ?? "default_org",
|
|
552
552
|
cluster_count: 0
|
|
@@ -554,12 +554,12 @@ function adminRoutes(ctx) {
|
|
|
554
554
|
return mongoOk(c, formatProject(project), 201);
|
|
555
555
|
});
|
|
556
556
|
app.delete("/api/atlas/v2/groups/:groupId", (c) => {
|
|
557
|
-
const
|
|
558
|
-
const project = ms().projects.findOneBy("group_id",
|
|
557
|
+
const groupId2 = c.req.param("groupId");
|
|
558
|
+
const project = ms().projects.findOneBy("group_id", groupId2);
|
|
559
559
|
if (!project) {
|
|
560
|
-
return mongoError(c, "GROUP_NOT_FOUND", `Group '${
|
|
560
|
+
return mongoError(c, "GROUP_NOT_FOUND", `Group '${groupId2}' not found.`, 404);
|
|
561
561
|
}
|
|
562
|
-
const clusters = ms().clusters.all().filter((cl) => cl.group_id ===
|
|
562
|
+
const clusters = ms().clusters.all().filter((cl) => cl.group_id === groupId2);
|
|
563
563
|
for (const cluster of clusters) {
|
|
564
564
|
deleteClusterData(ms, cluster.cluster_id);
|
|
565
565
|
ms().clusters.delete(cluster.id);
|
|
@@ -568,37 +568,37 @@ function adminRoutes(ctx) {
|
|
|
568
568
|
return c.body(null, 204);
|
|
569
569
|
});
|
|
570
570
|
app.get("/api/atlas/v2/groups/:groupId/clusters", (c) => {
|
|
571
|
-
const
|
|
572
|
-
const project = ms().projects.findOneBy("group_id",
|
|
571
|
+
const groupId2 = c.req.param("groupId");
|
|
572
|
+
const project = ms().projects.findOneBy("group_id", groupId2);
|
|
573
573
|
if (!project) {
|
|
574
|
-
return mongoError(c, "GROUP_NOT_FOUND", `Group '${
|
|
574
|
+
return mongoError(c, "GROUP_NOT_FOUND", `Group '${groupId2}' not found.`, 404);
|
|
575
575
|
}
|
|
576
|
-
const clusters = ms().clusters.all().filter((cl) => cl.group_id ===
|
|
576
|
+
const clusters = ms().clusters.all().filter((cl) => cl.group_id === groupId2);
|
|
577
577
|
return mongoOk(c, {
|
|
578
578
|
results: clusters.map(formatCluster),
|
|
579
579
|
totalCount: clusters.length
|
|
580
580
|
});
|
|
581
581
|
});
|
|
582
582
|
app.get("/api/atlas/v2/groups/:groupId/clusters/:clusterName", (c) => {
|
|
583
|
-
const
|
|
584
|
-
const
|
|
585
|
-
const cluster = ms().clusters.all().find((cl) => cl.group_id ===
|
|
583
|
+
const groupId2 = c.req.param("groupId");
|
|
584
|
+
const clusterName2 = c.req.param("clusterName");
|
|
585
|
+
const cluster = ms().clusters.all().find((cl) => cl.group_id === groupId2 && cl.name === clusterName2);
|
|
586
586
|
if (!cluster) {
|
|
587
|
-
return mongoError(c, "CLUSTER_NOT_FOUND", `Cluster '${
|
|
587
|
+
return mongoError(c, "CLUSTER_NOT_FOUND", `Cluster '${clusterName2}' not found.`, 404);
|
|
588
588
|
}
|
|
589
589
|
return mongoOk(c, formatCluster(cluster));
|
|
590
590
|
});
|
|
591
591
|
app.post("/api/atlas/v2/groups/:groupId/clusters", async (c) => {
|
|
592
|
-
const
|
|
593
|
-
const project = ms().projects.findOneBy("group_id",
|
|
592
|
+
const groupId2 = c.req.param("groupId");
|
|
593
|
+
const project = ms().projects.findOneBy("group_id", groupId2);
|
|
594
594
|
if (!project) {
|
|
595
|
-
return mongoError(c, "GROUP_NOT_FOUND", `Group '${
|
|
595
|
+
return mongoError(c, "GROUP_NOT_FOUND", `Group '${groupId2}' not found.`, 404);
|
|
596
596
|
}
|
|
597
597
|
const body = await c.req.json();
|
|
598
598
|
if (!body.name?.trim()) {
|
|
599
599
|
return mongoError(c, "INVALID_PARAMETER", "name is required");
|
|
600
600
|
}
|
|
601
|
-
const existing = ms().clusters.all().find((cl) => cl.group_id ===
|
|
601
|
+
const existing = ms().clusters.all().find((cl) => cl.group_id === groupId2 && cl.name === body.name);
|
|
602
602
|
if (existing) {
|
|
603
603
|
return mongoError(c, "DUPLICATE_CLUSTER_NAME", `Cluster '${body.name}' already exists.`, 409);
|
|
604
604
|
}
|
|
@@ -606,7 +606,7 @@ function adminRoutes(ctx) {
|
|
|
606
606
|
const cluster = ms().clusters.insert({
|
|
607
607
|
cluster_id: clusterId,
|
|
608
608
|
name: body.name,
|
|
609
|
-
group_id:
|
|
609
|
+
group_id: groupId2,
|
|
610
610
|
state: "IDLE",
|
|
611
611
|
mongo_uri: `mongodb+srv://${body.name}.emulate.mongodb.net`,
|
|
612
612
|
connection_strings: {
|
|
@@ -626,11 +626,11 @@ function adminRoutes(ctx) {
|
|
|
626
626
|
return mongoOk(c, formatCluster(cluster), 201);
|
|
627
627
|
});
|
|
628
628
|
app.patch("/api/atlas/v2/groups/:groupId/clusters/:clusterName", async (c) => {
|
|
629
|
-
const
|
|
630
|
-
const
|
|
631
|
-
const cluster = ms().clusters.all().find((cl) => cl.group_id ===
|
|
629
|
+
const groupId2 = c.req.param("groupId");
|
|
630
|
+
const clusterName2 = c.req.param("clusterName");
|
|
631
|
+
const cluster = ms().clusters.all().find((cl) => cl.group_id === groupId2 && cl.name === clusterName2);
|
|
632
632
|
if (!cluster) {
|
|
633
|
-
return mongoError(c, "CLUSTER_NOT_FOUND", `Cluster '${
|
|
633
|
+
return mongoError(c, "CLUSTER_NOT_FOUND", `Cluster '${clusterName2}' not found.`, 404);
|
|
634
634
|
}
|
|
635
635
|
const body = await c.req.json();
|
|
636
636
|
const updates = {};
|
|
@@ -648,44 +648,44 @@ function adminRoutes(ctx) {
|
|
|
648
648
|
return mongoOk(c, formatCluster(updated));
|
|
649
649
|
});
|
|
650
650
|
app.delete("/api/atlas/v2/groups/:groupId/clusters/:clusterName", (c) => {
|
|
651
|
-
const
|
|
652
|
-
const
|
|
653
|
-
const cluster = ms().clusters.all().find((cl) => cl.group_id ===
|
|
651
|
+
const groupId2 = c.req.param("groupId");
|
|
652
|
+
const clusterName2 = c.req.param("clusterName");
|
|
653
|
+
const cluster = ms().clusters.all().find((cl) => cl.group_id === groupId2 && cl.name === clusterName2);
|
|
654
654
|
if (!cluster) {
|
|
655
|
-
return mongoError(c, "CLUSTER_NOT_FOUND", `Cluster '${
|
|
655
|
+
return mongoError(c, "CLUSTER_NOT_FOUND", `Cluster '${clusterName2}' not found.`, 404);
|
|
656
656
|
}
|
|
657
657
|
deleteClusterData(ms, cluster.cluster_id);
|
|
658
658
|
ms().clusters.delete(cluster.id);
|
|
659
|
-
const project = ms().projects.findOneBy("group_id",
|
|
659
|
+
const project = ms().projects.findOneBy("group_id", groupId2);
|
|
660
660
|
if (project) {
|
|
661
661
|
ms().projects.update(project.id, { cluster_count: Math.max(0, project.cluster_count - 1) });
|
|
662
662
|
}
|
|
663
663
|
return c.body(null, 204);
|
|
664
664
|
});
|
|
665
665
|
app.get("/api/atlas/v2/groups/:groupId/databaseUsers", (c) => {
|
|
666
|
-
const
|
|
667
|
-
const users = ms().users.all().filter((u) => u.group_id ===
|
|
666
|
+
const groupId2 = c.req.param("groupId");
|
|
667
|
+
const users = ms().users.all().filter((u) => u.group_id === groupId2);
|
|
668
668
|
return mongoOk(c, {
|
|
669
669
|
results: users.map(formatUser),
|
|
670
670
|
totalCount: users.length
|
|
671
671
|
});
|
|
672
672
|
});
|
|
673
673
|
app.get("/api/atlas/v2/groups/:groupId/databaseUsers/admin/:username", (c) => {
|
|
674
|
-
const
|
|
675
|
-
const
|
|
676
|
-
const user = ms().users.all().find((u) => u.group_id ===
|
|
674
|
+
const groupId2 = c.req.param("groupId");
|
|
675
|
+
const username2 = c.req.param("username");
|
|
676
|
+
const user = ms().users.all().find((u) => u.group_id === groupId2 && u.username === username2);
|
|
677
677
|
if (!user) {
|
|
678
|
-
return mongoError(c, "USER_NOT_FOUND", `Database user '${
|
|
678
|
+
return mongoError(c, "USER_NOT_FOUND", `Database user '${username2}' not found.`, 404);
|
|
679
679
|
}
|
|
680
680
|
return mongoOk(c, formatUser(user));
|
|
681
681
|
});
|
|
682
682
|
app.post("/api/atlas/v2/groups/:groupId/databaseUsers", async (c) => {
|
|
683
|
-
const
|
|
683
|
+
const groupId2 = c.req.param("groupId");
|
|
684
684
|
const body = await c.req.json();
|
|
685
685
|
if (!body.username?.trim()) {
|
|
686
686
|
return mongoError(c, "INVALID_PARAMETER", "username is required");
|
|
687
687
|
}
|
|
688
|
-
const existing = ms().users.all().find((u) => u.group_id ===
|
|
688
|
+
const existing = ms().users.all().find((u) => u.group_id === groupId2 && u.username === body.username);
|
|
689
689
|
if (existing) {
|
|
690
690
|
return mongoError(c, "DUPLICATE_USER", `User '${body.username}' already exists.`, 409);
|
|
691
691
|
}
|
|
@@ -693,27 +693,27 @@ function adminRoutes(ctx) {
|
|
|
693
693
|
const user = ms().users.insert({
|
|
694
694
|
user_id: userId,
|
|
695
695
|
username: body.username,
|
|
696
|
-
group_id:
|
|
696
|
+
group_id: groupId2,
|
|
697
697
|
roles: (body.roles ?? []).map((r) => ({ database_name: r.databaseName, role_name: r.roleName }))
|
|
698
698
|
});
|
|
699
699
|
return mongoOk(c, formatUser(user), 201);
|
|
700
700
|
});
|
|
701
701
|
app.delete("/api/atlas/v2/groups/:groupId/databaseUsers/admin/:username", (c) => {
|
|
702
|
-
const
|
|
703
|
-
const
|
|
704
|
-
const user = ms().users.all().find((u) => u.group_id ===
|
|
702
|
+
const groupId2 = c.req.param("groupId");
|
|
703
|
+
const username2 = c.req.param("username");
|
|
704
|
+
const user = ms().users.all().find((u) => u.group_id === groupId2 && u.username === username2);
|
|
705
705
|
if (!user) {
|
|
706
|
-
return mongoError(c, "USER_NOT_FOUND", `Database user '${
|
|
706
|
+
return mongoError(c, "USER_NOT_FOUND", `Database user '${username2}' not found.`, 404);
|
|
707
707
|
}
|
|
708
708
|
ms().users.delete(user.id);
|
|
709
709
|
return c.body(null, 204);
|
|
710
710
|
});
|
|
711
711
|
app.get("/api/atlas/v2/groups/:groupId/clusters/:clusterName/databases", (c) => {
|
|
712
|
-
const
|
|
713
|
-
const
|
|
714
|
-
const cluster = ms().clusters.all().find((cl) => cl.group_id ===
|
|
712
|
+
const groupId2 = c.req.param("groupId");
|
|
713
|
+
const clusterName2 = c.req.param("clusterName");
|
|
714
|
+
const cluster = ms().clusters.all().find((cl) => cl.group_id === groupId2 && cl.name === clusterName2);
|
|
715
715
|
if (!cluster) {
|
|
716
|
-
return mongoError(c, "CLUSTER_NOT_FOUND", `Cluster '${
|
|
716
|
+
return mongoError(c, "CLUSTER_NOT_FOUND", `Cluster '${clusterName2}' not found.`, 404);
|
|
717
717
|
}
|
|
718
718
|
const databases = ms().databases.all().filter((db) => db.cluster_id === cluster.cluster_id);
|
|
719
719
|
return mongoOk(c, {
|
|
@@ -722,16 +722,16 @@ function adminRoutes(ctx) {
|
|
|
722
722
|
});
|
|
723
723
|
});
|
|
724
724
|
app.get("/api/atlas/v2/groups/:groupId/clusters/:clusterName/databases/:databaseName/collections", (c) => {
|
|
725
|
-
const
|
|
726
|
-
const
|
|
727
|
-
const
|
|
728
|
-
const cluster = ms().clusters.all().find((cl) => cl.group_id ===
|
|
725
|
+
const groupId2 = c.req.param("groupId");
|
|
726
|
+
const clusterName2 = c.req.param("clusterName");
|
|
727
|
+
const databaseName2 = c.req.param("databaseName");
|
|
728
|
+
const cluster = ms().clusters.all().find((cl) => cl.group_id === groupId2 && cl.name === clusterName2);
|
|
729
729
|
if (!cluster) {
|
|
730
|
-
return mongoError(c, "CLUSTER_NOT_FOUND", `Cluster '${
|
|
730
|
+
return mongoError(c, "CLUSTER_NOT_FOUND", `Cluster '${clusterName2}' not found.`, 404);
|
|
731
731
|
}
|
|
732
|
-
const collections = ms().collections.all().filter((col) => col.cluster_id === cluster.cluster_id && col.database ===
|
|
732
|
+
const collections = ms().collections.all().filter((col) => col.cluster_id === cluster.cluster_id && col.database === databaseName2);
|
|
733
733
|
return mongoOk(c, {
|
|
734
|
-
results: collections.map((col) => ({ collectionName: col.name, databaseName })),
|
|
734
|
+
results: collections.map((col) => ({ collectionName: col.name, databaseName: databaseName2 })),
|
|
735
735
|
totalCount: collections.length
|
|
736
736
|
});
|
|
737
737
|
});
|
|
@@ -783,6 +783,362 @@ function formatUser(u) {
|
|
|
783
783
|
roles: u.roles.map((r) => ({ databaseName: r.database_name, roleName: r.role_name }))
|
|
784
784
|
};
|
|
785
785
|
}
|
|
786
|
+
function openapiRoutes({ app, baseUrl }) {
|
|
787
|
+
app.get("/openapi.json", (c) => c.json(buildSpec(baseUrl)));
|
|
788
|
+
}
|
|
789
|
+
var ok = (description) => ({
|
|
790
|
+
description,
|
|
791
|
+
content: { "application/json": { schema: { type: "object" } } }
|
|
792
|
+
});
|
|
793
|
+
var deleted = { description: "Deleted (no content)." };
|
|
794
|
+
var groupId = { name: "groupId", in: "path", required: true, schema: { type: "string" } };
|
|
795
|
+
var clusterName = { name: "clusterName", in: "path", required: true, schema: { type: "string" } };
|
|
796
|
+
var username = { name: "username", in: "path", required: true, schema: { type: "string" } };
|
|
797
|
+
var databaseName = { name: "databaseName", in: "path", required: true, schema: { type: "string" } };
|
|
798
|
+
var jsonBody = (properties, required, description) => ({
|
|
799
|
+
required: true,
|
|
800
|
+
description,
|
|
801
|
+
content: {
|
|
802
|
+
"application/json": {
|
|
803
|
+
schema: { type: "object", properties, required: [...required] }
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
});
|
|
807
|
+
var dataApiTarget = {
|
|
808
|
+
dataSource: { type: "string", description: "Cluster name, e.g. Cluster0." },
|
|
809
|
+
database: { type: "string" },
|
|
810
|
+
collection: { type: "string" }
|
|
811
|
+
};
|
|
812
|
+
var filter = { type: "object", description: "MongoDB query filter." };
|
|
813
|
+
function buildSpec(baseUrl) {
|
|
814
|
+
return {
|
|
815
|
+
openapi: "3.1.0",
|
|
816
|
+
info: {
|
|
817
|
+
title: "MongoDB Atlas Administration API (Emulated)",
|
|
818
|
+
version: "1.0.0",
|
|
819
|
+
description: "Emulated subset of the MongoDB Atlas Administration API v2 and Atlas Data API v1. Real Atlas uses HTTP digest (API key pair) or service-account bearer tokens; the emulator accepts `Authorization: Bearer <token>` for every surface (mint a token at POST /_emulate/credentials)."
|
|
820
|
+
},
|
|
821
|
+
servers: [{ url: baseUrl }],
|
|
822
|
+
components: {
|
|
823
|
+
securitySchemes: {
|
|
824
|
+
bearerAuth: {
|
|
825
|
+
type: "http",
|
|
826
|
+
scheme: "bearer",
|
|
827
|
+
description: "Atlas API token, sent as `Authorization: Bearer emu_mongoatlas_\u2026`. Stands in for Atlas digest or service-account auth."
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
},
|
|
831
|
+
security: [{ bearerAuth: [] }],
|
|
832
|
+
paths: {
|
|
833
|
+
"/api/atlas/v2/groups": {
|
|
834
|
+
get: {
|
|
835
|
+
operationId: "listProjects",
|
|
836
|
+
tags: ["projects"],
|
|
837
|
+
summary: "List projects",
|
|
838
|
+
responses: { "200": ok("Project list.") }
|
|
839
|
+
},
|
|
840
|
+
post: {
|
|
841
|
+
operationId: "createProject",
|
|
842
|
+
tags: ["projects"],
|
|
843
|
+
summary: "Create a project",
|
|
844
|
+
requestBody: jsonBody(
|
|
845
|
+
{ name: { type: "string" }, orgId: { type: "string" } },
|
|
846
|
+
["name"],
|
|
847
|
+
"The project to create."
|
|
848
|
+
),
|
|
849
|
+
responses: { "201": ok("The created project."), "409": ok("Duplicate project name.") }
|
|
850
|
+
}
|
|
851
|
+
},
|
|
852
|
+
"/api/atlas/v2/groups/{groupId}": {
|
|
853
|
+
get: {
|
|
854
|
+
operationId: "getProject",
|
|
855
|
+
tags: ["projects"],
|
|
856
|
+
summary: "Retrieve a project",
|
|
857
|
+
parameters: [groupId],
|
|
858
|
+
responses: { "200": ok("The project."), "404": ok("Not found.") }
|
|
859
|
+
},
|
|
860
|
+
delete: {
|
|
861
|
+
operationId: "deleteProject",
|
|
862
|
+
tags: ["projects"],
|
|
863
|
+
summary: "Delete a project and its clusters",
|
|
864
|
+
parameters: [groupId],
|
|
865
|
+
responses: { "204": deleted, "404": ok("Not found.") }
|
|
866
|
+
}
|
|
867
|
+
},
|
|
868
|
+
"/api/atlas/v2/groups/{groupId}/clusters": {
|
|
869
|
+
get: {
|
|
870
|
+
operationId: "listClusters",
|
|
871
|
+
tags: ["clusters"],
|
|
872
|
+
summary: "List clusters in a project",
|
|
873
|
+
parameters: [groupId],
|
|
874
|
+
responses: { "200": ok("Cluster list."), "404": ok("Project not found.") }
|
|
875
|
+
},
|
|
876
|
+
post: {
|
|
877
|
+
operationId: "createCluster",
|
|
878
|
+
tags: ["clusters"],
|
|
879
|
+
summary: "Create a cluster",
|
|
880
|
+
parameters: [groupId],
|
|
881
|
+
requestBody: jsonBody(
|
|
882
|
+
{
|
|
883
|
+
name: { type: "string" },
|
|
884
|
+
clusterType: { type: "string", enum: ["REPLICASET", "SHARDED"] },
|
|
885
|
+
providerSettings: {
|
|
886
|
+
type: "object",
|
|
887
|
+
properties: {
|
|
888
|
+
providerName: { type: "string" },
|
|
889
|
+
instanceSizeName: { type: "string" },
|
|
890
|
+
regionName: { type: "string" }
|
|
891
|
+
}
|
|
892
|
+
},
|
|
893
|
+
diskSizeGB: { type: "number" },
|
|
894
|
+
mongoDBMajorVersion: { type: "string" }
|
|
895
|
+
},
|
|
896
|
+
["name"],
|
|
897
|
+
"The cluster to create."
|
|
898
|
+
),
|
|
899
|
+
responses: {
|
|
900
|
+
"201": ok("The created cluster."),
|
|
901
|
+
"404": ok("Project not found."),
|
|
902
|
+
"409": ok("Duplicate cluster name.")
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
},
|
|
906
|
+
"/api/atlas/v2/groups/{groupId}/clusters/{clusterName}": {
|
|
907
|
+
get: {
|
|
908
|
+
operationId: "getCluster",
|
|
909
|
+
tags: ["clusters"],
|
|
910
|
+
summary: "Retrieve a cluster",
|
|
911
|
+
parameters: [groupId, clusterName],
|
|
912
|
+
responses: { "200": ok("The cluster."), "404": ok("Not found.") }
|
|
913
|
+
},
|
|
914
|
+
patch: {
|
|
915
|
+
operationId: "updateCluster",
|
|
916
|
+
tags: ["clusters"],
|
|
917
|
+
summary: "Update a cluster (instance size, region, disk size)",
|
|
918
|
+
parameters: [groupId, clusterName],
|
|
919
|
+
requestBody: jsonBody(
|
|
920
|
+
{
|
|
921
|
+
providerSettings: {
|
|
922
|
+
type: "object",
|
|
923
|
+
properties: {
|
|
924
|
+
instanceSizeName: { type: "string" },
|
|
925
|
+
regionName: { type: "string" }
|
|
926
|
+
}
|
|
927
|
+
},
|
|
928
|
+
diskSizeGB: { type: "number" }
|
|
929
|
+
},
|
|
930
|
+
[],
|
|
931
|
+
"Fields to update; other cluster fields are immutable in the emulator."
|
|
932
|
+
),
|
|
933
|
+
responses: { "200": ok("The updated cluster."), "404": ok("Not found.") }
|
|
934
|
+
},
|
|
935
|
+
delete: {
|
|
936
|
+
operationId: "deleteCluster",
|
|
937
|
+
tags: ["clusters"],
|
|
938
|
+
summary: "Delete a cluster and its data",
|
|
939
|
+
parameters: [groupId, clusterName],
|
|
940
|
+
responses: { "204": deleted, "404": ok("Not found.") }
|
|
941
|
+
}
|
|
942
|
+
},
|
|
943
|
+
"/api/atlas/v2/groups/{groupId}/databaseUsers": {
|
|
944
|
+
get: {
|
|
945
|
+
operationId: "listDatabaseUsers",
|
|
946
|
+
tags: ["database-users"],
|
|
947
|
+
summary: "List database users in a project",
|
|
948
|
+
parameters: [groupId],
|
|
949
|
+
responses: { "200": ok("Database user list.") }
|
|
950
|
+
},
|
|
951
|
+
post: {
|
|
952
|
+
operationId: "createDatabaseUser",
|
|
953
|
+
tags: ["database-users"],
|
|
954
|
+
summary: "Create a database user",
|
|
955
|
+
parameters: [groupId],
|
|
956
|
+
requestBody: jsonBody(
|
|
957
|
+
{
|
|
958
|
+
username: { type: "string" },
|
|
959
|
+
password: { type: "string" },
|
|
960
|
+
databaseName: { type: "string" },
|
|
961
|
+
roles: {
|
|
962
|
+
type: "array",
|
|
963
|
+
items: {
|
|
964
|
+
type: "object",
|
|
965
|
+
properties: { databaseName: { type: "string" }, roleName: { type: "string" } }
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
},
|
|
969
|
+
["username"],
|
|
970
|
+
"The database user to create."
|
|
971
|
+
),
|
|
972
|
+
responses: { "201": ok("The created database user."), "409": ok("Duplicate username.") }
|
|
973
|
+
}
|
|
974
|
+
},
|
|
975
|
+
"/api/atlas/v2/groups/{groupId}/databaseUsers/admin/{username}": {
|
|
976
|
+
get: {
|
|
977
|
+
operationId: "getDatabaseUser",
|
|
978
|
+
tags: ["database-users"],
|
|
979
|
+
summary: "Retrieve a database user",
|
|
980
|
+
parameters: [groupId, username],
|
|
981
|
+
responses: { "200": ok("The database user."), "404": ok("Not found.") }
|
|
982
|
+
},
|
|
983
|
+
delete: {
|
|
984
|
+
operationId: "deleteDatabaseUser",
|
|
985
|
+
tags: ["database-users"],
|
|
986
|
+
summary: "Delete a database user",
|
|
987
|
+
parameters: [groupId, username],
|
|
988
|
+
responses: { "204": deleted, "404": ok("Not found.") }
|
|
989
|
+
}
|
|
990
|
+
},
|
|
991
|
+
"/api/atlas/v2/groups/{groupId}/clusters/{clusterName}/databases": {
|
|
992
|
+
get: {
|
|
993
|
+
operationId: "listDatabases",
|
|
994
|
+
tags: ["databases"],
|
|
995
|
+
summary: "List databases in a cluster",
|
|
996
|
+
parameters: [groupId, clusterName],
|
|
997
|
+
responses: { "200": ok("Database list."), "404": ok("Cluster not found.") }
|
|
998
|
+
}
|
|
999
|
+
},
|
|
1000
|
+
"/api/atlas/v2/groups/{groupId}/clusters/{clusterName}/databases/{databaseName}/collections": {
|
|
1001
|
+
get: {
|
|
1002
|
+
operationId: "listCollections",
|
|
1003
|
+
tags: ["databases"],
|
|
1004
|
+
summary: "List collections in a database",
|
|
1005
|
+
parameters: [groupId, clusterName, databaseName],
|
|
1006
|
+
responses: { "200": ok("Collection list."), "404": ok("Cluster not found.") }
|
|
1007
|
+
}
|
|
1008
|
+
},
|
|
1009
|
+
"/app/data-api/v1/action/findOne": {
|
|
1010
|
+
post: {
|
|
1011
|
+
operationId: "findOne",
|
|
1012
|
+
tags: ["data-api"],
|
|
1013
|
+
summary: "Find a single document",
|
|
1014
|
+
requestBody: jsonBody(
|
|
1015
|
+
{ ...dataApiTarget, filter, projection: { type: "object" } },
|
|
1016
|
+
["dataSource", "database", "collection"],
|
|
1017
|
+
"Where to look and what to match."
|
|
1018
|
+
),
|
|
1019
|
+
responses: { "200": ok("`{ document }` (null when nothing matches)."), "404": ok("Cluster not found.") }
|
|
1020
|
+
}
|
|
1021
|
+
},
|
|
1022
|
+
"/app/data-api/v1/action/find": {
|
|
1023
|
+
post: {
|
|
1024
|
+
operationId: "find",
|
|
1025
|
+
tags: ["data-api"],
|
|
1026
|
+
summary: "Find multiple documents",
|
|
1027
|
+
requestBody: jsonBody(
|
|
1028
|
+
{
|
|
1029
|
+
...dataApiTarget,
|
|
1030
|
+
filter,
|
|
1031
|
+
projection: { type: "object" },
|
|
1032
|
+
sort: { type: "object" },
|
|
1033
|
+
limit: { type: "integer" },
|
|
1034
|
+
skip: { type: "integer" }
|
|
1035
|
+
},
|
|
1036
|
+
["dataSource", "database", "collection"],
|
|
1037
|
+
"Where to look, what to match, and how to page."
|
|
1038
|
+
),
|
|
1039
|
+
responses: { "200": ok("`{ documents }`."), "404": ok("Cluster not found.") }
|
|
1040
|
+
}
|
|
1041
|
+
},
|
|
1042
|
+
"/app/data-api/v1/action/insertOne": {
|
|
1043
|
+
post: {
|
|
1044
|
+
operationId: "insertOne",
|
|
1045
|
+
tags: ["data-api"],
|
|
1046
|
+
summary: "Insert a single document",
|
|
1047
|
+
requestBody: jsonBody(
|
|
1048
|
+
{ ...dataApiTarget, document: { type: "object" } },
|
|
1049
|
+
["dataSource", "database", "collection", "document"],
|
|
1050
|
+
"The document to insert."
|
|
1051
|
+
),
|
|
1052
|
+
responses: { "201": ok("`{ insertedId }`."), "404": ok("Cluster not found.") }
|
|
1053
|
+
}
|
|
1054
|
+
},
|
|
1055
|
+
"/app/data-api/v1/action/insertMany": {
|
|
1056
|
+
post: {
|
|
1057
|
+
operationId: "insertMany",
|
|
1058
|
+
tags: ["data-api"],
|
|
1059
|
+
summary: "Insert multiple documents",
|
|
1060
|
+
requestBody: jsonBody(
|
|
1061
|
+
{ ...dataApiTarget, documents: { type: "array", items: { type: "object" } } },
|
|
1062
|
+
["dataSource", "database", "collection", "documents"],
|
|
1063
|
+
"The documents to insert."
|
|
1064
|
+
),
|
|
1065
|
+
responses: { "201": ok("`{ insertedIds }`."), "404": ok("Cluster not found.") }
|
|
1066
|
+
}
|
|
1067
|
+
},
|
|
1068
|
+
"/app/data-api/v1/action/updateOne": {
|
|
1069
|
+
post: {
|
|
1070
|
+
operationId: "updateOne",
|
|
1071
|
+
tags: ["data-api"],
|
|
1072
|
+
summary: "Update a single document",
|
|
1073
|
+
requestBody: jsonBody(
|
|
1074
|
+
{ ...dataApiTarget, filter, update: { type: "object" }, upsert: { type: "boolean" } },
|
|
1075
|
+
["dataSource", "database", "collection", "update"],
|
|
1076
|
+
"What to match and how to update it ($set, $unset, $inc, $push, $pull, $rename, or replacement)."
|
|
1077
|
+
),
|
|
1078
|
+
responses: {
|
|
1079
|
+
"200": ok("`{ matchedCount, modifiedCount }` (plus `upsertedId` on upsert)."),
|
|
1080
|
+
"404": ok("Cluster not found.")
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
},
|
|
1084
|
+
"/app/data-api/v1/action/updateMany": {
|
|
1085
|
+
post: {
|
|
1086
|
+
operationId: "updateMany",
|
|
1087
|
+
tags: ["data-api"],
|
|
1088
|
+
summary: "Update multiple documents",
|
|
1089
|
+
requestBody: jsonBody(
|
|
1090
|
+
{ ...dataApiTarget, filter, update: { type: "object" }, upsert: { type: "boolean" } },
|
|
1091
|
+
["dataSource", "database", "collection", "update"],
|
|
1092
|
+
"What to match and how to update it ($set, $unset, $inc, $push, $pull, $rename, or replacement)."
|
|
1093
|
+
),
|
|
1094
|
+
responses: {
|
|
1095
|
+
"200": ok("`{ matchedCount, modifiedCount }` (plus `upsertedId` on upsert)."),
|
|
1096
|
+
"404": ok("Cluster not found.")
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1099
|
+
},
|
|
1100
|
+
"/app/data-api/v1/action/deleteOne": {
|
|
1101
|
+
post: {
|
|
1102
|
+
operationId: "deleteOne",
|
|
1103
|
+
tags: ["data-api"],
|
|
1104
|
+
summary: "Delete a single document",
|
|
1105
|
+
requestBody: jsonBody(
|
|
1106
|
+
{ ...dataApiTarget, filter },
|
|
1107
|
+
["dataSource", "database", "collection"],
|
|
1108
|
+
"What to match."
|
|
1109
|
+
),
|
|
1110
|
+
responses: { "200": ok("`{ deletedCount }`."), "404": ok("Cluster not found.") }
|
|
1111
|
+
}
|
|
1112
|
+
},
|
|
1113
|
+
"/app/data-api/v1/action/deleteMany": {
|
|
1114
|
+
post: {
|
|
1115
|
+
operationId: "deleteMany",
|
|
1116
|
+
tags: ["data-api"],
|
|
1117
|
+
summary: "Delete multiple documents",
|
|
1118
|
+
requestBody: jsonBody(
|
|
1119
|
+
{ ...dataApiTarget, filter },
|
|
1120
|
+
["dataSource", "database", "collection"],
|
|
1121
|
+
"What to match."
|
|
1122
|
+
),
|
|
1123
|
+
responses: { "200": ok("`{ deletedCount }`."), "404": ok("Cluster not found.") }
|
|
1124
|
+
}
|
|
1125
|
+
},
|
|
1126
|
+
"/app/data-api/v1/action/aggregate": {
|
|
1127
|
+
post: {
|
|
1128
|
+
operationId: "aggregate",
|
|
1129
|
+
tags: ["data-api"],
|
|
1130
|
+
summary: "Run an aggregation pipeline (subset: $match, $limit, $skip, $sort, $project, $count)",
|
|
1131
|
+
requestBody: jsonBody(
|
|
1132
|
+
{ ...dataApiTarget, pipeline: { type: "array", items: { type: "object" } } },
|
|
1133
|
+
["dataSource", "database", "collection"],
|
|
1134
|
+
"The pipeline to run; unsupported stages are ignored."
|
|
1135
|
+
),
|
|
1136
|
+
responses: { "200": ok("`{ documents }`."), "404": ok("Cluster not found.") }
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1140
|
+
};
|
|
1141
|
+
}
|
|
786
1142
|
var manifest = {
|
|
787
1143
|
id: "mongoatlas",
|
|
788
1144
|
name: "MongoDB Atlas",
|
|
@@ -798,6 +1154,7 @@ var manifest = {
|
|
|
798
1154
|
kind: "openapi",
|
|
799
1155
|
title: "Atlas Administration API v2 subset",
|
|
800
1156
|
coverage: "hand-authored",
|
|
1157
|
+
url: "/openapi.json",
|
|
801
1158
|
operations: [
|
|
802
1159
|
{ operationId: "listProjects", method: "GET", path: "/api/atlas/v2/groups", status: "hand-authored" },
|
|
803
1160
|
{ operationId: "getProject", method: "GET", path: "/api/atlas/v2/groups/:groupId", status: "hand-authored" },
|
|
@@ -880,6 +1237,7 @@ var manifest = {
|
|
|
880
1237
|
kind: "openapi",
|
|
881
1238
|
title: "Atlas Data API v1 subset",
|
|
882
1239
|
coverage: "hand-authored",
|
|
1240
|
+
url: "/openapi.json",
|
|
883
1241
|
operations: [
|
|
884
1242
|
{ operationId: "findOne", method: "POST", path: "/app/data-api/v1/action/findOne", status: "hand-authored" },
|
|
885
1243
|
{ operationId: "find", method: "POST", path: "/app/data-api/v1/action/find", status: "hand-authored" },
|
|
@@ -1027,9 +1385,9 @@ var manifest = {
|
|
|
1027
1385
|
};
|
|
1028
1386
|
function seedDefaults(store, _baseUrl) {
|
|
1029
1387
|
const ms = getMongoAtlasStore(store);
|
|
1030
|
-
const
|
|
1388
|
+
const groupId2 = generateGroupId();
|
|
1031
1389
|
ms.projects.insert({
|
|
1032
|
-
group_id:
|
|
1390
|
+
group_id: groupId2,
|
|
1033
1391
|
name: "Project0",
|
|
1034
1392
|
org_id: "default_org",
|
|
1035
1393
|
cluster_count: 1
|
|
@@ -1038,7 +1396,7 @@ function seedDefaults(store, _baseUrl) {
|
|
|
1038
1396
|
ms.clusters.insert({
|
|
1039
1397
|
cluster_id: clusterId,
|
|
1040
1398
|
name: "Cluster0",
|
|
1041
|
-
group_id:
|
|
1399
|
+
group_id: groupId2,
|
|
1042
1400
|
state: "IDLE",
|
|
1043
1401
|
mongo_uri: "mongodb+srv://Cluster0.emulate.mongodb.net",
|
|
1044
1402
|
connection_strings: {
|
|
@@ -1057,7 +1415,7 @@ function seedDefaults(store, _baseUrl) {
|
|
|
1057
1415
|
ms.users.insert({
|
|
1058
1416
|
user_id: generateUserId(),
|
|
1059
1417
|
username: "admin",
|
|
1060
|
-
group_id:
|
|
1418
|
+
group_id: groupId2,
|
|
1061
1419
|
roles: [{ database_name: "admin", role_name: "atlasAdmin" }]
|
|
1062
1420
|
});
|
|
1063
1421
|
ms.databases.insert({ cluster_id: clusterId, name: "test" });
|
|
@@ -1073,14 +1431,14 @@ function seedFromConfig(store, _baseUrl, config) {
|
|
|
1073
1431
|
projectIdMap.set(p.name, existing.group_id);
|
|
1074
1432
|
continue;
|
|
1075
1433
|
}
|
|
1076
|
-
const
|
|
1434
|
+
const groupId2 = generateGroupId();
|
|
1077
1435
|
ms.projects.insert({
|
|
1078
|
-
group_id:
|
|
1436
|
+
group_id: groupId2,
|
|
1079
1437
|
name: p.name,
|
|
1080
1438
|
org_id: p.org_id ?? "default_org",
|
|
1081
1439
|
cluster_count: 0
|
|
1082
1440
|
});
|
|
1083
|
-
projectIdMap.set(p.name,
|
|
1441
|
+
projectIdMap.set(p.name, groupId2);
|
|
1084
1442
|
}
|
|
1085
1443
|
}
|
|
1086
1444
|
const defaultProject = ms.projects.all()[0];
|
|
@@ -1090,9 +1448,9 @@ function seedFromConfig(store, _baseUrl, config) {
|
|
|
1090
1448
|
const clusterIdMap = /* @__PURE__ */ new Map();
|
|
1091
1449
|
if (config.clusters) {
|
|
1092
1450
|
for (const cl of config.clusters) {
|
|
1093
|
-
const
|
|
1094
|
-
if (!
|
|
1095
|
-
const existing = ms.clusters.all().find((ec) => ec.group_id ===
|
|
1451
|
+
const groupId2 = projectIdMap.get(cl.project);
|
|
1452
|
+
if (!groupId2) continue;
|
|
1453
|
+
const existing = ms.clusters.all().find((ec) => ec.group_id === groupId2 && ec.name === cl.name);
|
|
1096
1454
|
if (existing) {
|
|
1097
1455
|
clusterIdMap.set(cl.name, existing.cluster_id);
|
|
1098
1456
|
continue;
|
|
@@ -1101,7 +1459,7 @@ function seedFromConfig(store, _baseUrl, config) {
|
|
|
1101
1459
|
ms.clusters.insert({
|
|
1102
1460
|
cluster_id: clusterId,
|
|
1103
1461
|
name: cl.name,
|
|
1104
|
-
group_id:
|
|
1462
|
+
group_id: groupId2,
|
|
1105
1463
|
state: "IDLE",
|
|
1106
1464
|
mongo_uri: `mongodb+srv://${cl.name}.emulate.mongodb.net`,
|
|
1107
1465
|
connection_strings: {
|
|
@@ -1118,7 +1476,7 @@ function seedFromConfig(store, _baseUrl, config) {
|
|
|
1118
1476
|
mongodb_version: cl.mongodb_version ?? "8.0"
|
|
1119
1477
|
});
|
|
1120
1478
|
clusterIdMap.set(cl.name, clusterId);
|
|
1121
|
-
const project = ms.projects.findOneBy("group_id",
|
|
1479
|
+
const project = ms.projects.findOneBy("group_id", groupId2);
|
|
1122
1480
|
if (project) {
|
|
1123
1481
|
ms.projects.update(project.id, { cluster_count: project.cluster_count + 1 });
|
|
1124
1482
|
}
|
|
@@ -1130,14 +1488,14 @@ function seedFromConfig(store, _baseUrl, config) {
|
|
|
1130
1488
|
}
|
|
1131
1489
|
if (config.database_users) {
|
|
1132
1490
|
for (const u of config.database_users) {
|
|
1133
|
-
const
|
|
1134
|
-
if (!
|
|
1135
|
-
const existing = ms.users.all().find((eu) => eu.group_id ===
|
|
1491
|
+
const groupId2 = projectIdMap.get(u.project);
|
|
1492
|
+
if (!groupId2) continue;
|
|
1493
|
+
const existing = ms.users.all().find((eu) => eu.group_id === groupId2 && eu.username === u.username);
|
|
1136
1494
|
if (existing) continue;
|
|
1137
1495
|
ms.users.insert({
|
|
1138
1496
|
user_id: generateUserId(),
|
|
1139
1497
|
username: u.username,
|
|
1140
|
-
group_id:
|
|
1498
|
+
group_id: groupId2,
|
|
1141
1499
|
roles: u.roles ?? [{ database_name: "admin", role_name: "readWriteAnyDatabase" }]
|
|
1142
1500
|
});
|
|
1143
1501
|
}
|
|
@@ -1167,6 +1525,7 @@ var mongoatlasPlugin = {
|
|
|
1167
1525
|
const ctx = { app, store, webhooks, baseUrl, tokenMap };
|
|
1168
1526
|
adminRoutes(ctx);
|
|
1169
1527
|
dataApiRoutes(ctx);
|
|
1528
|
+
openapiRoutes(ctx);
|
|
1170
1529
|
},
|
|
1171
1530
|
seed(store, baseUrl) {
|
|
1172
1531
|
seedDefaults(store, baseUrl);
|
|
@@ -1180,4 +1539,4 @@ export {
|
|
|
1180
1539
|
mongoatlasPlugin,
|
|
1181
1540
|
seedFromConfig
|
|
1182
1541
|
};
|
|
1183
|
-
//# sourceMappingURL=dist-
|
|
1542
|
+
//# sourceMappingURL=dist-OT6R2YO2.js.map
|