Skip to content

Commit 25d0de8

Browse files
refactor: clean up provisioner, add support for mongodb atlas serverless (#67)
* refactor: clean up the mongodb provisioner * bugfix: rename a few of the arguments to match the other changes * refactor: rename command for cluster * feat: support for mongodb atlas serverless
1 parent 7cb0485 commit 25d0de8

File tree

12 files changed

+392
-151
lines changed

12 files changed

+392
-151
lines changed

src/EntityDb.MongoDb.Provisioner/Commands/CommandBase.cs

Lines changed: 33 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,64 @@
11
using EntityDb.MongoDb.Provisioner.MongoDbAtlas;
22
using System.CommandLine;
3-
using System.CommandLine.Parsing;
43
using System.Text.RegularExpressions;
54
using System.Threading.Tasks;
65

76
namespace EntityDb.MongoDb.Provisioner.Commands;
87

98
internal abstract class CommandBase
109
{
11-
private static readonly Regex EntityNameRegex = new("^[a-z][a-z]*$", RegexOptions.IgnoreCase);
10+
private static readonly Regex ServiceNameRegex = new("^[a-z][a-z]*$", RegexOptions.IgnoreCase);
1211

13-
protected static void AddMongoDbAtlasArgumentsTo(Command command)
12+
protected static void AddMongoDbAtlasArguments(Command command)
1413
{
15-
var groupNameArgument = new Argument<string>("group-name",
16-
"The name of the MongoDb Atlas Group/Project to access via API.");
17-
var publicKeyArgument = new Argument<string>("public-key", "The public key used to authenticate via API.");
18-
var privateKeyArgument =
19-
new Argument<string>("private-key", "The private key used to authenticate via API.");
14+
var groupName = new Argument<string>("group-name")
15+
{
16+
Description = "The name of the MongoDb Atlas Group/Project to access via API."
17+
};
2018

21-
command.AddArgument(groupNameArgument);
22-
command.AddArgument(publicKeyArgument);
23-
command.AddArgument(privateKeyArgument);
24-
}
19+
var publicKey = new Argument<string>("public-key")
20+
{
21+
Description = "The public key used to authenticate via API."
22+
};
2523

26-
protected static void AddClusterNameArgumentTo(Command command)
27-
{
28-
var clusterNameArgument = new Argument<string>("cluster-name",
29-
"The name of the Cluster on which the entity will be provisioned.");
24+
var privateKey = new Argument<string>("private-key")
25+
{
26+
Description = "The private key used to authenticate via API."
27+
};
3028

31-
command.AddArgument(clusterNameArgument);
29+
command.AddArgument(groupName);
30+
command.AddArgument(publicKey);
31+
command.AddArgument(privateKey);
3232
}
3333

34-
protected static void AddEntityNameArgumentTo(Command command)
34+
protected static void AddServiceNameArgument(Command command)
3535
{
36-
var entityNameArgument = new Argument<string>("entity-name", "The name of the entity being provisioned.");
36+
var serviceName = new Argument<string>("service-name")
37+
{
38+
Description = "The name of the service that will use this database."
39+
};
3740

38-
entityNameArgument.AddValidator(entityNameResult =>
41+
serviceName.AddValidator(serviceNameResult =>
3942
{
40-
var entityName = entityNameResult.GetValueOrDefault<string>() ?? string.Empty;
43+
var serviceName = serviceNameResult.GetValueOrDefault<string>() ?? string.Empty;
4144

42-
if (!EntityNameRegex.IsMatch(entityName))
45+
if (!ServiceNameRegex.IsMatch(serviceName))
4346
{
44-
entityNameResult.ErrorMessage = "The entity name must begin with an letter, and can only contain letters.";
47+
serviceNameResult.ErrorMessage = "The service name must begin with an letter, and can only contain letters.";
4548
}
4649
});
4750

48-
command.AddArgument(entityNameArgument);
51+
command.AddArgument(serviceName);
4952
}
5053

51-
protected static void AddEntityPasswordArgumentTo(Command command)
54+
protected static void AddServicePasswordArgument(Command command)
5255
{
53-
var entityPasswordArgument =
54-
new Argument<string>("entity-password", "The password for the entity service user.");
56+
var servicePassword = new Argument<string>("service-password")
57+
{
58+
Description = "The password for the service that will use this database."
59+
};
5560

56-
command.AddArgument(entityPasswordArgument);
61+
command.AddArgument(servicePassword);
5762
}
5863

5964
internal static Task<MongoDbAtlasClient> GetMongoDbAtlasClient(string groupName, string publicKey,

src/EntityDb.MongoDb.Provisioner/Commands/CreateCollections.cs

Lines changed: 0 additions & 51 deletions
This file was deleted.
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
using EntityDb.MongoDb.Provisioner.Extensions;
2+
using MongoDB.Driver;
3+
using System;
4+
using System.CommandLine;
5+
using System.CommandLine.NamingConventionBinder;
6+
using System.Threading.Tasks;
7+
8+
namespace EntityDb.MongoDb.Provisioner.Commands;
9+
10+
internal class CreateCollectionsCluster : CommandBase
11+
{
12+
public record Arguments
13+
(
14+
string GroupName,
15+
string PublicKey,
16+
string PrivateKey,
17+
string ServiceName,
18+
string ServicePassword,
19+
string ClusterName
20+
);
21+
22+
private static async Task Execute(Arguments arguments)
23+
{
24+
const string expectedProtocol = "mongodb+srv://";
25+
26+
var mongoDbAtlasClient = await GetMongoDbAtlasClient
27+
(
28+
arguments.GroupName,
29+
arguments.PublicKey,
30+
arguments.PrivateKey
31+
);
32+
33+
var cluster = await mongoDbAtlasClient.GetCluster(arguments.ClusterName);
34+
35+
if (cluster?.SrvAddress?.StartsWith(expectedProtocol) != true)
36+
{
37+
throw new InvalidOperationException();
38+
}
39+
40+
var mongoClient =
41+
new MongoClient(
42+
$"{expectedProtocol}{arguments.ServiceName}:{arguments.ServicePassword}@{cluster.SrvAddress[expectedProtocol.Length..]}/admin");
43+
44+
await mongoClient.ProvisionCollections(arguments.ServiceName);
45+
}
46+
47+
protected static void AddClusterNameArgument(Command command)
48+
{
49+
var clusterNameArgument = new Argument<string>("cluster-name")
50+
{
51+
Description = "The name of the Cluster on which the entity will be provisioned."
52+
};
53+
54+
command.AddArgument(clusterNameArgument);
55+
}
56+
57+
public static void AddTo(RootCommand rootCommand)
58+
{
59+
var createCollections = new Command("create-collections-cluster")
60+
{
61+
Handler = CommandHandler.Create<Arguments>(Execute)
62+
};
63+
64+
AddMongoDbAtlasArguments(createCollections);
65+
AddServiceNameArgument(createCollections);
66+
AddServicePasswordArgument(createCollections);
67+
AddClusterNameArgument(createCollections);
68+
69+
rootCommand.AddCommand(createCollections);
70+
}
71+
}

src/EntityDb.MongoDb.Provisioner/Commands/CreateCollectionsDirect.cs

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,30 +8,39 @@ namespace EntityDb.MongoDb.Provisioner.Commands;
88

99
internal class CreateCollectionsDirect : CommandBase
1010
{
11-
public static void AddTo(RootCommand rootCommand)
12-
{
13-
var createCollectionsDirect = new Command("create-collections-direct");
14-
15-
AddEntityNameArgumentTo(createCollectionsDirect);
11+
public record Arguments
12+
(
13+
string ServiceName,
14+
string ConnectionString
15+
);
1616

17-
var connectionStringArgument =
18-
new Argument<string>("connection-string", "The connection string to the mongodb instance.");
17+
private static async Task Execute(Arguments arguments)
18+
{
19+
var mongoClient = new MongoClient(arguments.ConnectionString);
1920

20-
createCollectionsDirect.AddArgument(connectionStringArgument);
21+
await mongoClient.ProvisionCollections(arguments.ServiceName);
22+
}
2123

22-
createCollectionsDirect.Handler = CommandHandler.Create(
23-
async (string entityName, string connectionString) =>
24-
{
25-
await Execute(entityName, connectionString);
26-
});
24+
private static void AddConnectionStringArgument(Command command)
25+
{
26+
var connectionStringArgument = new Argument<string>("connection-string")
27+
{
28+
Description = "The connection string to the mongodb instance."
29+
};
2730

28-
rootCommand.AddCommand(createCollectionsDirect);
31+
command.AddArgument(connectionStringArgument);
2932
}
3033

31-
private static async Task Execute(string entityName, string connectionString)
34+
public static void AddTo(RootCommand rootCommand)
3235
{
33-
var mongoClient = new MongoClient(connectionString);
36+
var createCollectionsDirect = new Command("create-collections-direct")
37+
{
38+
Handler = CommandHandler.Create<Arguments>(Execute)
39+
};
3440

35-
await mongoClient.ProvisionCollections(entityName);
41+
AddServiceNameArgument(createCollectionsDirect);
42+
AddConnectionStringArgument(createCollectionsDirect);
43+
44+
rootCommand.AddCommand(createCollectionsDirect);
3645
}
3746
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
using EntityDb.MongoDb.Provisioner.Extensions;
2+
using MongoDB.Driver;
3+
using System;
4+
using System.CommandLine;
5+
using System.CommandLine.NamingConventionBinder;
6+
using System.Threading.Tasks;
7+
8+
namespace EntityDb.MongoDb.Provisioner.Commands;
9+
10+
internal class CreateCollectionsServerless : CommandBase
11+
{
12+
public record Arguments
13+
(
14+
string GroupName,
15+
string PublicKey,
16+
string PrivateKey,
17+
string ServiceName,
18+
string ServicePassword,
19+
string InstanceName
20+
);
21+
22+
private static async Task Execute(Arguments arguments)
23+
{
24+
const string expectedProtocol = "mongodb+srv://";
25+
26+
var mongoDbAtlasClient = await GetMongoDbAtlasClient
27+
(
28+
arguments.GroupName,
29+
arguments.PublicKey,
30+
arguments.PrivateKey
31+
);
32+
33+
var serverlessInstance = await mongoDbAtlasClient.GetServerlessInstance(arguments.InstanceName);
34+
35+
if (serverlessInstance?.ConnectionStrings?.StandardSrv?.StartsWith(expectedProtocol) != true)
36+
{
37+
throw new InvalidOperationException();
38+
}
39+
40+
var mongoClient =
41+
new MongoClient(
42+
$"{expectedProtocol}{arguments.ServiceName}:{arguments.ServicePassword}@{serverlessInstance.ConnectionStrings.StandardSrv[expectedProtocol.Length..]}/admin");
43+
44+
await mongoClient.ProvisionCollections(arguments.ServiceName);
45+
}
46+
47+
protected static void AddServerlessNameArgument(Command command)
48+
{
49+
var clusterNameArgument = new Argument<string>("instance-name")
50+
{
51+
Description = "The name of the Serverless Instance on which the database will be provisioned."
52+
};
53+
54+
command.AddArgument(clusterNameArgument);
55+
}
56+
57+
public static void AddTo(RootCommand rootCommand)
58+
{
59+
var createCollections = new Command("create-collections-serverless")
60+
{
61+
Handler = CommandHandler.Create<Arguments>(Execute)
62+
};
63+
64+
AddMongoDbAtlasArguments(createCollections);
65+
AddServiceNameArgument(createCollections);
66+
AddServicePasswordArgument(createCollections);
67+
AddServerlessNameArgument(createCollections);
68+
69+
rootCommand.AddCommand(createCollections);
70+
}
71+
}

0 commit comments

Comments
 (0)