@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.
@@ -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 filter = stage.$match;
251
- results = results.filter((d) => matchesFilter(d, filter));
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, filter) {
281
- for (const [key, value] of Object.entries(filter)) {
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, filter) {
361
- if (!filter || Object.keys(filter).length === 0) return null;
362
- return docs.filter((d) => matchesFilter(d.data, filter));
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(filter) {
508
+ function extractEqualityFields(filter2) {
509
509
  const result = {};
510
- for (const [key, value] of Object.entries(filter)) {
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 groupId = c.req.param("groupId");
532
- const project = ms().projects.findOneBy("group_id", groupId);
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 '${groupId}' not found.`, 404);
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 groupId = generateGroupId();
547
+ const groupId2 = generateGroupId();
548
548
  const project = ms().projects.insert({
549
- group_id: groupId,
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 groupId = c.req.param("groupId");
558
- const project = ms().projects.findOneBy("group_id", groupId);
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 '${groupId}' not found.`, 404);
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 === groupId);
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 groupId = c.req.param("groupId");
572
- const project = ms().projects.findOneBy("group_id", groupId);
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 '${groupId}' not found.`, 404);
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 === groupId);
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 groupId = c.req.param("groupId");
584
- const clusterName = c.req.param("clusterName");
585
- const cluster = ms().clusters.all().find((cl) => cl.group_id === groupId && cl.name === clusterName);
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 '${clusterName}' not found.`, 404);
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 groupId = c.req.param("groupId");
593
- const project = ms().projects.findOneBy("group_id", groupId);
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 '${groupId}' not found.`, 404);
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 === groupId && cl.name === body.name);
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: groupId,
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 groupId = c.req.param("groupId");
630
- const clusterName = c.req.param("clusterName");
631
- const cluster = ms().clusters.all().find((cl) => cl.group_id === groupId && cl.name === clusterName);
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 '${clusterName}' not found.`, 404);
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 groupId = c.req.param("groupId");
652
- const clusterName = c.req.param("clusterName");
653
- const cluster = ms().clusters.all().find((cl) => cl.group_id === groupId && cl.name === clusterName);
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 '${clusterName}' not found.`, 404);
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", groupId);
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 groupId = c.req.param("groupId");
667
- const users = ms().users.all().filter((u) => u.group_id === groupId);
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 groupId = c.req.param("groupId");
675
- const username = c.req.param("username");
676
- const user = ms().users.all().find((u) => u.group_id === groupId && u.username === username);
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 '${username}' not found.`, 404);
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 groupId = c.req.param("groupId");
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 === groupId && u.username === body.username);
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: groupId,
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 groupId = c.req.param("groupId");
703
- const username = c.req.param("username");
704
- const user = ms().users.all().find((u) => u.group_id === groupId && u.username === username);
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 '${username}' not found.`, 404);
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 groupId = c.req.param("groupId");
713
- const clusterName = c.req.param("clusterName");
714
- const cluster = ms().clusters.all().find((cl) => cl.group_id === groupId && cl.name === clusterName);
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 '${clusterName}' not found.`, 404);
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 groupId = c.req.param("groupId");
726
- const clusterName = c.req.param("clusterName");
727
- const databaseName = c.req.param("databaseName");
728
- const cluster = ms().clusters.all().find((cl) => cl.group_id === groupId && cl.name === clusterName);
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 '${clusterName}' not found.`, 404);
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 === databaseName);
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 groupId = generateGroupId();
1388
+ const groupId2 = generateGroupId();
1031
1389
  ms.projects.insert({
1032
- group_id: groupId,
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: groupId,
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: groupId,
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 groupId = generateGroupId();
1434
+ const groupId2 = generateGroupId();
1077
1435
  ms.projects.insert({
1078
- group_id: groupId,
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, groupId);
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 groupId = projectIdMap.get(cl.project);
1094
- if (!groupId) continue;
1095
- const existing = ms.clusters.all().find((ec) => ec.group_id === groupId && ec.name === cl.name);
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: groupId,
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", groupId);
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 groupId = projectIdMap.get(u.project);
1134
- if (!groupId) continue;
1135
- const existing = ms.users.all().find((eu) => eu.group_id === groupId && eu.username === u.username);
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: groupId,
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-RMPDKZUA.js.map
1542
+ //# sourceMappingURL=dist-OT6R2YO2.js.map