Skip to content

Commit 8b5b3c1

Browse files
committed
Implement SimpleRepository Insert/Get.
Various cleanups and tweaks.
1 parent 010e5a8 commit 8b5b3c1

File tree

14 files changed

+203
-21
lines changed

14 files changed

+203
-21
lines changed

ArcadeDB.Client.sln

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ArcadeDb.Client", "src\Arca
88
EndProject
99
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ArcadeDb.Client.IntegrationTests", "test\ArcadeDb.Client.IntegrationTests\ArcadeDb.Client.IntegrationTests.csproj", "{7C9F4285-9833-46EA-9B67-F8B69ECB2027}"
1010
EndProject
11+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ArcadeDb.Client.Extras", "src\ArcadeDb.Client.Extras\ArcadeDb.Client.Extras.csproj", "{22DE5FAF-EDFC-4AB8-BF3A-7A5158A1C519}"
12+
EndProject
1113
Global
1214
GlobalSection(SolutionConfigurationPlatforms) = preSolution
1315
Debug|Any CPU = Debug|Any CPU
@@ -16,6 +18,7 @@ Global
1618
GlobalSection(NestedProjects) = preSolution
1719
{A034B342-C3C5-494D-9138-572F821EA2B9} = {079019A0-B944-4A3E-838D-D756EB7D9B9A}
1820
{7C9F4285-9833-46EA-9B67-F8B69ECB2027} = {921751B9-1D79-4DD1-A159-076AEA938C53}
21+
{22DE5FAF-EDFC-4AB8-BF3A-7A5158A1C519} = {079019A0-B944-4A3E-838D-D756EB7D9B9A}
1922
EndGlobalSection
2023
GlobalSection(ProjectConfigurationPlatforms) = postSolution
2124
{A034B342-C3C5-494D-9138-572F821EA2B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
@@ -26,5 +29,9 @@ Global
2629
{7C9F4285-9833-46EA-9B67-F8B69ECB2027}.Debug|Any CPU.Build.0 = Debug|Any CPU
2730
{7C9F4285-9833-46EA-9B67-F8B69ECB2027}.Release|Any CPU.ActiveCfg = Release|Any CPU
2831
{7C9F4285-9833-46EA-9B67-F8B69ECB2027}.Release|Any CPU.Build.0 = Release|Any CPU
32+
{22DE5FAF-EDFC-4AB8-BF3A-7A5158A1C519}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
33+
{22DE5FAF-EDFC-4AB8-BF3A-7A5158A1C519}.Debug|Any CPU.Build.0 = Debug|Any CPU
34+
{22DE5FAF-EDFC-4AB8-BF3A-7A5158A1C519}.Release|Any CPU.ActiveCfg = Release|Any CPU
35+
{22DE5FAF-EDFC-4AB8-BF3A-7A5158A1C519}.Release|Any CPU.Build.0 = Release|Any CPU
2936
EndGlobalSection
3037
EndGlobal

src/ArcadeDB.Client/DatabaseResult.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,5 @@ public record DatabaseResult(string? Exception, string? Detail, string? Error)
1313

1414
public record DatabaseResult<T>(string? Exception, string? Detail, string? Error) : DatabaseResult(Exception, Detail, Error)
1515
{
16-
public new T Result { get; init; }
16+
public new T[] Result { get; init; }
1717
}

src/ArcadeDB.Client/Json.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,13 @@
33

44
public static class Json
55
{
6-
public static JsonSerializerOptions DefaultSerializerOptions = SetSerializerSettings(new JsonSerializerOptions());
6+
public static readonly JsonSerializerOptions DefaultSerializerOptions = SetSerializerSettings(new JsonSerializerOptions());
77

88
private static JsonSerializerOptions SetSerializerSettings(JsonSerializerOptions settings)
99
{
1010
settings.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
1111
settings.Converters.Add(new JsonStringEnumConverter());
1212
settings.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
13-
//settings.ConfigureForNodaTime(DateTimeZoneProviders.Tzdb);
1413
return settings;
1514
}
1615

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
namespace ArcadeDb.Client;
2+
3+
public enum QueryLanguage
4+
{
5+
Sql,
6+
Cypher
7+
}

src/ArcadeDB.Client/RemoteDatabase.cs

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using System.Linq;
1+
using System.Diagnostics;
2+
using System.Linq;
23
using System.Net;
34
using System.Net.Http;
45
using System.Net.Http.Headers;
@@ -7,12 +8,6 @@
78

89
namespace ArcadeDb.Client;
910

10-
public enum QueryLanguage
11-
{
12-
Sql,
13-
Cypher
14-
}
15-
1611
public class RemoteDatabase : IDisposable
1712
{
1813
private readonly HttpClient httpClient;
@@ -43,16 +38,16 @@ public async Task<DatabaseResult> Create(string? database = null) =>
4338
public async Task<DatabaseResult> Drop(string? database = null) =>
4439
await this.HttpCommand("drop", database ?? this.Database);
4540

46-
public async Task<DatabaseResult<T>> Command<T>(string command, object @params, QueryLanguage language = QueryLanguage.Sql) =>
41+
public async Task<DatabaseResult<T>> Command<T>(string command, object? @params = null, QueryLanguage language = QueryLanguage.Sql) =>
4742
await this.HttpCommand<DatabaseResult<T>>("command", this.Database, command, @params, language).ConfigureAwait(false);
4843

49-
public async Task<DatabaseResult<T>> Query<T>(string command, object @params, QueryLanguage language = QueryLanguage.Sql) =>
44+
public async Task<DatabaseResult<T>> Query<T>(string command, object? @params = null, QueryLanguage language = QueryLanguage.Sql) =>
5045
await this.HttpCommand<DatabaseResult<T>>("query", this.Database, command, @params, language).ConfigureAwait(false);
5146

52-
public async Task<DatabaseResult> Command(string command, object @params, QueryLanguage language = QueryLanguage.Sql) =>
47+
public async Task<DatabaseResult> Command(string command, object? @params = null, QueryLanguage language = QueryLanguage.Sql) =>
5348
await this.HttpCommand("command", this.Database, command, @params, language).ConfigureAwait(false);
5449

55-
public async Task<DatabaseResult> Query(string command, object @params, QueryLanguage language = QueryLanguage.Sql) =>
50+
public async Task<DatabaseResult> Query(string command, object? @params = null, QueryLanguage language = QueryLanguage.Sql) =>
5651
await this.HttpCommand("query", this.Database, command, @params, language).ConfigureAwait(false);
5752

5853
private async Task<DatabaseResult> HttpCommand(string operation, string database, string? command = null, object? @params = null,
@@ -84,6 +79,8 @@ private async Task<T> HttpCommand<T>(string operation, string database, string?
8479
};
8580
}
8681

82+
Debug.WriteLine(await response.Content.ReadAsStringAsync());
83+
8784
return await JsonSerializer.DeserializeAsync<T>(await response.Content.ReadAsStreamAsync(), Json.DefaultSerializerOptions)
8885
.ConfigureAwait(false) ?? throw new ArcadeDbException($"Could not deserialize result as {nameof(T)}.");
8986
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net6.0</TargetFramework>
5+
<ImplicitUsings>enable</ImplicitUsings>
6+
<Nullable>enable</Nullable>
7+
</PropertyGroup>
8+
9+
<ItemGroup>
10+
<ProjectReference Include="..\ArcadeDb.Client\ArcadeDb.Client.csproj" />
11+
</ItemGroup>
12+
13+
<ItemGroup>
14+
<PackageReference Include="NodaTime" Version="3.0.7" />
15+
<PackageReference Include="NodaTime.Serialization.SystemTextJson" Version="1.0.0" />
16+
</ItemGroup>
17+
18+
</Project>
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
global using System;
2+
global using System.Linq;
3+
global using NodaTime;
4+
5+
using NodaTime.Serialization.SystemTextJson;
6+
7+
namespace ArcadeDb.Client.Extras;
8+
9+
public class ExtrasModule
10+
{
11+
public static void Initialize()
12+
{
13+
Json.DefaultSerializerOptions.ConfigureForNodaTime(DateTimeZoneProviders.Tzdb);
14+
}
15+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
using System.Reflection;
2+
using System.Text.Json.Serialization;
3+
using System.Text.RegularExpressions;
4+
using ArcadeDb.Client.Extras;
5+
6+
namespace ArcadeDb.Client;
7+
8+
public abstract record Entity
9+
{
10+
public string? Rid { get; init; }
11+
12+
public Instant? CreatedDate { get; init; }
13+
14+
public Instant? UpdatedDate { get; init; }
15+
}
16+
17+
public class SimpleRepository<T> : IDisposable
18+
where T : Entity
19+
{
20+
private readonly RemoteDatabase database;
21+
private readonly IClock clock;
22+
private readonly string propertiesTemplate;
23+
private static readonly string EntityName = typeof(T).Name;
24+
private static readonly Regex RidRegex = new Regex(@"^#\d+:\d+$", RegexOptions.Compiled);
25+
26+
public SimpleRepository(RemoteDatabase database, IClock clock)
27+
{
28+
this.database = database;
29+
this.clock = clock;
30+
31+
var properties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(pi => pi.IsInitOnly() && pi.Name != "Id");
32+
this.propertiesTemplate = $"{{ {string.Join(",", properties.Select(pi => $"\"{pi.Name.ToCamelCase()}\": :{pi.Name.ToCamelCase()}"))} }}";
33+
34+
// TODO: This should happen somewhere else!
35+
this.database.Command($"CREATE DOCUMENT TYPE {EntityName} IF NOT EXISTS").Wait();
36+
}
37+
38+
public async Task<T> Get(string recordId)
39+
{
40+
if (RidRegex.IsMatch(recordId) == false) throw new ArgumentException("Id must be a Record Id in the form of #0.0.");
41+
var result = await this.database.Query<T>($"SELECT FROM {EntityName} WHERE @rid={recordId}");
42+
return result.Result.Single();
43+
}
44+
45+
public async Task<T> Insert(T entity)
46+
{
47+
entity = entity with { CreatedDate = this.clock.GetCurrentInstant() };
48+
var templateString = $"INSERT INTO {EntityName} CONTENT {this.propertiesTemplate} RETURN @rid";
49+
var result = await this.database.Command(templateString, entity);
50+
entity = entity with { Rid = result.Result.EnumerateArray().Single().GetProperty("@rid").GetString() };
51+
return entity;
52+
}
53+
54+
public void Dispose()
55+
{
56+
this.database.Dispose();
57+
}
58+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
namespace ArcadeDb.Client.Extras;
2+
3+
public static class StringExtensions
4+
{
5+
public static string ToCamelCase(this string str, bool invariantCulture = true)
6+
{
7+
if (string.IsNullOrWhiteSpace(str)) return str;
8+
if (str.Length == 1) return invariantCulture ? str.ToLowerInvariant() : str.ToLower();
9+
return (invariantCulture ? char.ToLowerInvariant(str[0]) : char.ToLower(str[0])) + str[1..];
10+
}
11+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
using System.Reflection;
2+
3+
namespace ArcadeDb.Client.Extras;
4+
5+
public static class TypeExtensions
6+
{
7+
public static bool IsInitOnly(this PropertyInfo propertyInfo)
8+
{
9+
var setMethod = propertyInfo.SetMethod;
10+
if (setMethod == null) return false;
11+
var isExternalInitType = typeof(System.Runtime.CompilerServices.IsExternalInit);
12+
return setMethod.ReturnParameter.GetRequiredCustomModifiers().Contains(isExternalInitType);
13+
}
14+
}

0 commit comments

Comments
 (0)