Skip to content

Commit

Permalink
✨ implement Export method
Browse files Browse the repository at this point in the history
  • Loading branch information
Odonno committed Nov 24, 2024
1 parent b4e6f8b commit 11b9a14
Show file tree
Hide file tree
Showing 12 changed files with 390 additions and 24 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,9 @@ FodyWeavers.xsd
*.sln.iml
.idea/

# Verify snaspshot testing
*.received.*

### VisualStudio Patch ###
# Additional files built by Visual Studio

Expand Down
1 change: 1 addition & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
<PackageVersion Include="System.Interactive.Async" Version="6.0.1" />
<PackageVersion Include="SystemTextJsonPatch" Version="4.0.0" />
<PackageVersion Include="Ulid" Version="1.3.3" />
<PackageVersion Include="Verify.Xunit" Version="28.2.1" />
<PackageVersion Include="Websocket.Client" Version="5.1.1" />
<PackageVersion Include="xunit" Version="2.9.2" />
<PackageVersion Include="xunit.runner.visualstudio" Version="2.8.2" />
Expand Down
122 changes: 122 additions & 0 deletions SurrealDb.Net.Tests/ExportTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
using System.Text;
using System.Text.RegularExpressions;

namespace SurrealDb.Net.Tests;

public class ExportTests
{
private readonly VerifySettings _verifySettings = new();

public ExportTests()
{
_verifySettings.DisableRequireUniquePrefix();
_verifySettings.UseDirectory("Snapshots");

// 💡 "ScrubInlineDateTimes" won't work as DateTime cannot be parsed with more than 7 seconds fraction units
_verifySettings.AddScrubber(
(sb, counter) =>
{
const string pattern = @"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{9}Z";
var matches = Regex.Matches(sb.ToString(), pattern);
foreach (Match match in matches)
{
string value = match.Value;
var date = DateTime.Parse(value[..^4] + "Z");
int id = counter.Next(date);
string name = $"DateTime_{id}";
sb.Replace(value, name);
}
}
);
}

[Theory]
[InlineData("Endpoint=mem://")]
[InlineData("Endpoint=rocksdb://")]
[InlineData("Endpoint=surrealkv://")]
public async Task NotSupportedForEmbeddedMode(string connectionString)
{
Func<Task> func = async () =>
{
await using var surrealDbClientGenerator = new SurrealDbClientGenerator();
var dbInfo = surrealDbClientGenerator.GenerateDatabaseInfo();
await using var client = surrealDbClientGenerator.Create(connectionString);
await client.Use(dbInfo.Namespace, dbInfo.Database);
await client.Export();
};

await func.Should().ThrowAsync<NotImplementedException>();
}

[Theory]
[InlineData("Endpoint=http://127.0.0.1:8000;User=root;Pass=root")]
[InlineData("Endpoint=ws://127.0.0.1:8000/rpc;User=root;Pass=root")]
public async Task ShouldExportEmptyDatabaseWithDefaultOptions(string connectionString)
{
string? result = null;

Func<Task> func = async () =>
{
await using var surrealDbClientGenerator = new SurrealDbClientGenerator();
var dbInfo = surrealDbClientGenerator.GenerateDatabaseInfo();
await using var client = surrealDbClientGenerator.Create(connectionString);
await client.Use(dbInfo.Namespace, dbInfo.Database);
result = await client.Export();
};

await func.Should().NotThrowAsync();

await Verify(result, _verifySettings);
}

[Theory]
[InlineData("Endpoint=http://127.0.0.1:8000;User=root;Pass=root")]
[InlineData("Endpoint=ws://127.0.0.1:8000/rpc;User=root;Pass=root")]
public async Task ShouldExportFullDatabaseWithDefaultOptions(string connectionString)
{
string? result = null;

Func<Task> func = async () =>
{
await using var surrealDbClientGenerator = new SurrealDbClientGenerator();
var dbInfo = surrealDbClientGenerator.GenerateDatabaseInfo();
string filePath = Path.Combine(
AppDomain.CurrentDomain.BaseDirectory,
"Schemas/post.surql"
);
string fileContent = await File.ReadAllTextAsync(filePath, Encoding.UTF8);
string query = fileContent;
await using var client = surrealDbClientGenerator.Create(connectionString);
await client.Use(dbInfo.Namespace, dbInfo.Database);
(await client.RawQuery(query)).EnsureAllOks();
await client.Delete("post");
var post = new Post
{
Id = ("post", "dotnet-123456"),
Title = "A new article",
Content = "This is a new article created using the .NET SDK"
};
await client.Create(post);
result = await client.Export();
};

await func.Should().NotThrowAsync();

await Verify(result, _verifySettings); //.AddScrubber();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
-- ------------------------------
-- OPTION
-- ------------------------------

OPTION IMPORT;

Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
-- ------------------------------
-- OPTION
-- ------------------------------

OPTION IMPORT;

-- ------------------------------
-- TABLE: post
-- ------------------------------

DEFINE TABLE post TYPE NORMAL SCHEMAFULL PERMISSIONS FOR select, create, update, delete WHERE $auth.id != NONE;

DEFINE FIELD content ON post TYPE string PERMISSIONS FULL;
DEFINE FIELD created_at ON post TYPE datetime DEFAULT time::now() PERMISSIONS FULL;
DEFINE FIELD status ON post TYPE string DEFAULT 'DRAFT' ASSERT $value INSIDE ['DRAFT', 'PUBLISHED'] PERMISSIONS FULL;
DEFINE FIELD title ON post TYPE string PERMISSIONS FULL;

-- ------------------------------
-- TABLE DATA: post
-- ------------------------------

INSERT [ { content: 'This is a new article created using the .NET SDK', created_at: d'DateTime_1', id: post:⟨dotnet-123456⟩, status: 'DRAFT', title: 'A new article' } ];

1 change: 1 addition & 0 deletions SurrealDb.Net.Tests/SurrealDb.Net.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
<PackageReference Include="Meziantou.Xunit.ParallelTestFramework" />
<PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="Semver" />
<PackageReference Include="Verify.Xunit" />
<PackageReference Include="xunit" />
<PackageReference Include="xunit.runner.visualstudio">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
Expand Down
73 changes: 53 additions & 20 deletions SurrealDb.Net/Internals/SurrealDbEngine.Http.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,16 @@ internal class SurrealDbHttpEngine : ISurrealDbEngine
{
private const string RPC_ENDPOINT = "/rpc";

private SemVersion? _version;
internal SemVersion? _version { get; private set; }
internal Action<CborOptions>? _configureCborOptions { get; }
internal SurrealDbHttpEngineConfig _config { get; }

private readonly Uri _uri;
private readonly SurrealDbOptions _parameters;
private readonly IHttpClientFactory? _httpClientFactory;
private readonly Action<CborOptions>? _configureCborOptions;
private readonly ISurrealDbLoggerFactory? _surrealDbLoggerFactory;
private readonly Lazy<HttpClient> _singleHttpClient = new(() => new HttpClient(), true);
private HttpClientConfiguration? _singleHttpClientConfiguration;
private readonly SurrealDbHttpEngineConfig _config;

public SurrealDbHttpEngine(
SurrealDbOptions parameters,
Expand Down Expand Up @@ -187,7 +188,11 @@ public ValueTask DisposeAsync()
public async Task<bool> Health(CancellationToken cancellationToken)
{
using var wrapper = CreateHttpClientWrapper();
using var body = CreateBodyContent(new SurrealDbHttpRequest { Method = "ping" });
using var body = CreateBodyContent(
_parameters.NamingPolicy,
_configureCborOptions,
new SurrealDbHttpRequest { Method = "ping" }
);

try
{
Expand Down Expand Up @@ -841,12 +846,12 @@ public async Task<string> Version(CancellationToken cancellationToken)
return version.Replace(VERSION_PREFIX, string.Empty);
}

private CborOptions GetCborSerializerOptions()
private static CborOptions GetCborSerializerOptions(
string? namingPolicy,
Action<CborOptions>? configureCborOptions
)
{
return SurrealDbCborOptions.GetCborSerializerOptions(
_parameters.NamingPolicy,
_configureCborOptions
);
return SurrealDbCborOptions.GetCborSerializerOptions(namingPolicy, configureCborOptions);
}

private HttpClientWrapper CreateHttpClientWrapper(
Expand Down Expand Up @@ -908,9 +913,24 @@ private void ApplyHttpClientConfiguration(
{
client.BaseAddress = _uri;

var ns = useConfiguration is not null ? useConfiguration.Ns : _config.Ns;
var db = useConfiguration is not null ? useConfiguration.Db : _config.Db;
SetNsDbHttpClientHeaders(client, _version, ns, db);

var auth = overridedAuth ?? _config.Auth;
SetAuthHttpClientHeaders(client, auth);
}

internal static void SetNsDbHttpClientHeaders(
HttpClient client,
SemVersion? version,
string? ns,
string? db
)
{
client.DefaultRequestHeaders.Remove(HttpConstants.ACCEPT_HEADER_NAME);

if (_version?.Major > 1)
if (version?.Major > 1)
{
client.DefaultRequestHeaders.Remove(HttpConstants.NS_HEADER_NAME_V2);
client.DefaultRequestHeaders.Remove(HttpConstants.DB_HEADER_NAME_V2);
Expand All @@ -923,10 +943,7 @@ private void ApplyHttpClientConfiguration(

client.DefaultRequestHeaders.Add(HttpConstants.ACCEPT_HEADER_NAME, ["application/cbor"]);

var ns = useConfiguration is not null ? useConfiguration.Ns : _config.Ns;
var db = useConfiguration is not null ? useConfiguration.Db : _config.Db;

if (_version?.Major > 1)
if (version?.Major > 1)
{
client.DefaultRequestHeaders.Add(HttpConstants.NS_HEADER_NAME_V2, ns);
client.DefaultRequestHeaders.Add(HttpConstants.DB_HEADER_NAME_V2, db);
Expand All @@ -936,9 +953,10 @@ private void ApplyHttpClientConfiguration(
client.DefaultRequestHeaders.Add(HttpConstants.NS_HEADER_NAME, ns);
client.DefaultRequestHeaders.Add(HttpConstants.DB_HEADER_NAME, db);
}
}

var auth = overridedAuth ?? _config.Auth;

internal static void SetAuthHttpClientHeaders(HttpClient client, IAuth? auth)
{
switch (auth)
{
case BearerAuth bearerAuth:
Expand Down Expand Up @@ -996,10 +1014,18 @@ private bool IsSingleHttpClient(HttpClient client)
return _singleHttpClient.IsValueCreated && client == _singleHttpClient.Value;
}

private HttpContent CreateBodyContent<T>(T data)
internal static HttpContent CreateBodyContent<T>(
string? namingPolicy,
Action<CborOptions>? configureCborOptions,
T data
)
{
var writer = new ArrayBufferWriter<byte>();
CborSerializer.Serialize(data, writer, GetCborSerializerOptions());
CborSerializer.Serialize(
data,
writer,
GetCborSerializerOptions(namingPolicy, configureCborOptions)
);
var payload = writer.WrittenSpan.ToArray();

var content = new ByteArrayContent(payload);
Expand All @@ -1021,7 +1047,11 @@ CancellationToken cancellationToken
}

using var wrapper = CreateHttpClientWrapper();
using var body = CreateBodyContent(request);
using var body = CreateBodyContent(
_parameters.NamingPolicy,
_configureCborOptions,
request
);

using var response = await wrapper
.Instance.PostAsync(RPC_ENDPOINT, body, cancellationToken)
Expand Down Expand Up @@ -1062,7 +1092,10 @@ CancellationToken cancellationToken
using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
#endif

var cborSerializerOptions = GetCborSerializerOptions();
var cborSerializerOptions = GetCborSerializerOptions(
_parameters.NamingPolicy,
_configureCborOptions
);

var result = await CborSerializer
.DeserializeAsync<ISurrealDbHttpResponse>(
Expand Down
9 changes: 5 additions & 4 deletions SurrealDb.Net/Internals/SurrealDbEngine.Ws.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,13 @@ internal class SurrealDbWsEngine : ISurrealDbEngine
{
private static readonly ConcurrentDictionary<string, SurrealDbWsEngine> _wsEngines = new();

private SemVersion? _version;
internal SemVersion? _version { get; private set; }
internal Action<CborOptions>? _configureCborOptions { get; }
internal SurrealDbWsEngineConfig _config { get; }

private readonly string _id;
private readonly SurrealDbOptions _parameters;
private readonly Action<CborOptions>? _configureCborOptions;
private readonly ISurrealDbLoggerFactory? _surrealDbLoggerFactory;
private readonly SurrealDbWsEngineConfig _config;
private readonly WebsocketClient _wsClient;
private readonly IDisposable _receiverSubscription;
private readonly ConcurrentDictionary<
Expand Down Expand Up @@ -1244,7 +1245,7 @@ await SendRequestAsync("ping", null, SurrealDbWsRequestPriority.High, cancellati
/// Avoid multiple connections in a multi-threading context
/// and prevent usage before initialized
/// </summary>
private async Task InternalConnectAsync(
internal async Task InternalConnectAsync(
bool requireInitialized,
CancellationToken cancellationToken
)
Expand Down
Loading

0 comments on commit 11b9a14

Please sign in to comment.