5
5
using CommunityToolkit . Aspire . SurrealDb ;
6
6
using Microsoft . Extensions . DependencyInjection ;
7
7
using Microsoft . Extensions . Diagnostics . HealthChecks ;
8
+ using Microsoft . Extensions . Logging ;
8
9
using SurrealDb . Net ;
10
+ using SurrealDb . Net . Models . Response ;
9
11
using System . Text ;
10
12
using System . Text . Json ;
11
13
@@ -80,6 +82,59 @@ public static IResourceBuilder<SurrealDbServerResource> AddSurrealServer(
80
82
: SurrealDbContainerImageTags . Tag ;
81
83
82
84
var surrealServer = new SurrealDbServerResource ( name , userName ? . Resource , passwordParameter ) ;
85
+
86
+ string ? connectionString = null ;
87
+
88
+ builder . Eventing . Subscribe < ConnectionStringAvailableEvent > ( surrealServer , async ( _ , ct ) =>
89
+ {
90
+ connectionString = await surrealServer . GetConnectionStringAsync ( ct ) . ConfigureAwait ( false ) ;
91
+
92
+ if ( connectionString == null )
93
+ {
94
+ throw new DistributedApplicationException ( $ "ConnectionStringAvailableEvent was published for the '{ surrealServer . Name } ' resource but the connection string was null.") ;
95
+ }
96
+ } ) ;
97
+
98
+ builder . Eventing . Subscribe < ResourceReadyEvent > ( surrealServer , async ( @event , ct ) =>
99
+ {
100
+ if ( connectionString is null )
101
+ {
102
+ throw new DistributedApplicationException ( $ "ResourceReadyEvent was published for the '{ surrealServer . Name } ' resource but the connection string was null.") ;
103
+ }
104
+
105
+ if ( ! strictMode )
106
+ {
107
+ return ;
108
+ }
109
+
110
+ var options = new SurrealDbOptionsBuilder ( ) . FromConnectionString ( connectionString ) . Build ( ) ;
111
+ await using var surrealClient = new SurrealDbClient ( options ) ;
112
+
113
+ foreach ( var nsResourceName in surrealServer . Namespaces . Keys )
114
+ {
115
+ if ( builder . Resources . FirstOrDefault ( n =>
116
+ string . Equals ( n . Name , nsResourceName , StringComparison . OrdinalIgnoreCase ) ) is
117
+ SurrealDbNamespaceResource surrealDbNamespace )
118
+ {
119
+ await CreateNamespaceAsync ( surrealClient , surrealDbNamespace , @event . Services , ct )
120
+ . ConfigureAwait ( false ) ;
121
+
122
+ await surrealClient . Use ( surrealDbNamespace . NamespaceName , null ! , ct ) . ConfigureAwait ( false ) ;
123
+
124
+ foreach ( var dbResourceName in surrealDbNamespace . Databases . Keys )
125
+ {
126
+ if ( builder . Resources . FirstOrDefault ( n =>
127
+ string . Equals ( n . Name , dbResourceName , StringComparison . OrdinalIgnoreCase ) ) is
128
+ SurrealDbDatabaseResource surrealDbDatabase )
129
+ {
130
+ await CreateDatabaseAsync ( surrealClient , surrealDbDatabase , @event . Services , ct )
131
+ . ConfigureAwait ( false ) ;
132
+ }
133
+ }
134
+ }
135
+ }
136
+ } ) ;
137
+
83
138
return builder . AddResource ( surrealServer )
84
139
. WithEndpoint ( port : port , targetPort : SurrealDbPort , name : SurrealDbServerResource . PrimaryEndpointName )
85
140
. WithImage ( SurrealDbContainerImageTags . Image , imageTag )
@@ -132,6 +187,25 @@ public static IResourceBuilder<SurrealDbNamespaceResource> AddNamespace(
132
187
var surrealServerNamespace = new SurrealDbNamespaceResource ( name , namespaceName , builder . Resource ) ;
133
188
return builder . ApplicationBuilder . AddResource ( surrealServerNamespace ) ;
134
189
}
190
+
191
+ /// <summary>
192
+ /// Defines the SQL script used to create the namespace.
193
+ /// </summary>
194
+ /// <param name="builder">The builder for the <see cref="SurrealDbNamespaceResource"/>.</param>
195
+ /// <param name="script">The SQL script used to create the namespace.</param>
196
+ /// <returns>A reference to the <see cref="IResourceBuilder{T}"/>.</returns>
197
+ /// <remarks>
198
+ /// <value>Default script is <code>DEFINE NAMESPACE IF NOT EXISTS `QUOTED_NAMESPACE_NAME`;</code></value>
199
+ /// </remarks>
200
+ public static IResourceBuilder < SurrealDbNamespaceResource > WithCreationScript ( this IResourceBuilder < SurrealDbNamespaceResource > builder , string script )
201
+ {
202
+ ArgumentNullException . ThrowIfNull ( builder ) ;
203
+ ArgumentNullException . ThrowIfNull ( script ) ;
204
+
205
+ builder . WithAnnotation ( new SurrealDbCreateNamespaceScriptAnnotation ( script ) ) ;
206
+
207
+ return builder ;
208
+ }
135
209
136
210
/// <summary>
137
211
/// Adds a SurrealDB database to the application model. This is a child resource of a <see cref="SurrealDbNamespaceResource"/>.
@@ -202,6 +276,25 @@ public static IResourceBuilder<SurrealDbDatabaseResource> AddDatabase(
202
276
return builder . ApplicationBuilder . AddResource ( surrealServerDatabase )
203
277
. WithHealthCheck ( healthCheckKey ) ;
204
278
}
279
+
280
+ /// <summary>
281
+ /// Defines the SQL script used to create the database.
282
+ /// </summary>
283
+ /// <param name="builder">The builder for the <see cref="SurrealDbDatabaseResource"/>.</param>
284
+ /// <param name="script">The SQL script used to create the database.</param>
285
+ /// <returns>A reference to the <see cref="IResourceBuilder{T}"/>.</returns>
286
+ /// <remarks>
287
+ /// <value>Default script is <code>DEFINE DATABASE IF NOT EXISTS `QUOTED_DATABASE_NAME`;</code></value>
288
+ /// </remarks>
289
+ public static IResourceBuilder < SurrealDbDatabaseResource > WithCreationScript ( this IResourceBuilder < SurrealDbDatabaseResource > builder , string script )
290
+ {
291
+ ArgumentNullException . ThrowIfNull ( builder ) ;
292
+ ArgumentNullException . ThrowIfNull ( script ) ;
293
+
294
+ builder . WithAnnotation ( new SurrealDbCreateDatabaseScriptAnnotation ( script ) ) ;
295
+
296
+ return builder ;
297
+ }
205
298
206
299
/// <summary>
207
300
/// Adds a named volume for the data folder to a SurrealDB resource.
@@ -447,4 +540,62 @@ CancellationToken cancellationToken
447
540
448
541
return Encoding . UTF8 . GetString ( stream . ToArray ( ) ) ;
449
542
}
543
+
544
+ private static async Task CreateNamespaceAsync (
545
+ SurrealDbClient surrealClient ,
546
+ SurrealDbNamespaceResource namespaceResource ,
547
+ IServiceProvider serviceProvider ,
548
+ CancellationToken cancellationToken
549
+ )
550
+ {
551
+ var scriptAnnotation = namespaceResource . Annotations . OfType < SurrealDbCreateNamespaceScriptAnnotation > ( ) . LastOrDefault ( ) ;
552
+
553
+ var logger = serviceProvider . GetRequiredService < ResourceLoggerService > ( ) . GetLogger ( namespaceResource . Parent ) ;
554
+ logger . LogDebug ( "Creating namespace '{NamespaceName}'" , namespaceResource . NamespaceName ) ;
555
+
556
+ try
557
+ {
558
+ var response = await surrealClient . RawQuery (
559
+ scriptAnnotation ? . Script ?? $ "DEFINE NAMESPACE IF NOT EXISTS `{ namespaceResource . NamespaceName } `;",
560
+ cancellationToken : cancellationToken
561
+ ) . ConfigureAwait ( false ) ;
562
+
563
+ response . EnsureAllOks ( ) ;
564
+
565
+ logger . LogDebug ( "Namespace '{NamespaceName}' created successfully" , namespaceResource . NamespaceName ) ;
566
+ }
567
+ catch ( Exception e )
568
+ {
569
+ logger . LogError ( e , "Failed to create namespace '{NamespaceName}'" , namespaceResource . NamespaceName ) ;
570
+ }
571
+ }
572
+
573
+ private static async Task CreateDatabaseAsync (
574
+ SurrealDbClient surrealClient ,
575
+ SurrealDbDatabaseResource databaseResource ,
576
+ IServiceProvider serviceProvider ,
577
+ CancellationToken cancellationToken
578
+ )
579
+ {
580
+ var scriptAnnotation = databaseResource . Annotations . OfType < SurrealDbCreateDatabaseScriptAnnotation > ( ) . LastOrDefault ( ) ;
581
+
582
+ var logger = serviceProvider . GetRequiredService < ResourceLoggerService > ( ) . GetLogger ( databaseResource . Parent . Parent ) ;
583
+ logger . LogDebug ( "Creating database '{DatabaseName}'" , databaseResource . DatabaseName ) ;
584
+
585
+ try
586
+ {
587
+ var response = await surrealClient . RawQuery (
588
+ scriptAnnotation ? . Script ?? $ "DEFINE DATABASE IF NOT EXISTS `{ databaseResource . DatabaseName } `;",
589
+ cancellationToken : cancellationToken
590
+ ) . ConfigureAwait ( false ) ;
591
+
592
+ response . EnsureAllOks ( ) ;
593
+
594
+ logger . LogDebug ( "Database '{DatabaseName}' created successfully" , databaseResource . DatabaseName ) ;
595
+ }
596
+ catch ( Exception e )
597
+ {
598
+ logger . LogError ( e , "Failed to create database '{DatabaseName}'" , databaseResource . DatabaseName ) ;
599
+ }
600
+ }
450
601
}
0 commit comments