Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ implement Export method #154

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading