diff --git a/.github/workflows/modules-cicd.yml b/.github/workflows/modules-cicd.yml index 55e4a1a..67b0614 100644 --- a/.github/workflows/modules-cicd.yml +++ b/.github/workflows/modules-cicd.yml @@ -3,7 +3,7 @@ name: CI-CD on: push: tags: - - "*v[0-9]+.[0-9]+.[0-9]+-preview[0-9][0-9][0-9][0-9]" + - "*v*" jobs: deploy: diff --git a/_src/Implementation/Configuration/Factories/JsonConfigurationQueryFactory.cs b/_src/Implementation/Configuration/Factories/JsonConfigurationQueryFactory.cs new file mode 100644 index 0000000..770b876 --- /dev/null +++ b/_src/Implementation/Configuration/Factories/JsonConfigurationQueryFactory.cs @@ -0,0 +1,13 @@ +using Infrastructure.Configuration; +using Infrastructure.Configuration.Factories; + +namespace Implementation.Configuration.Factories +{ + internal class JsonConfigurationQueryFactory : IConfigurationQueryFactory + { + public IConfigurationQuery CreateConfigurationQuery(string filePath) + { + return new JsonConfigurationQuery(filePath); + } + } +} diff --git a/_src/Implementation/Configuration/JsonConfigurationQuery.cs b/_src/Implementation/Configuration/JsonConfigurationQuery.cs new file mode 100644 index 0000000..402b549 --- /dev/null +++ b/_src/Implementation/Configuration/JsonConfigurationQuery.cs @@ -0,0 +1,98 @@ +using System; +using System.IO; +using System.Threading.Tasks; +using Infrastructure.Configuration; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace Implementation.Configuration +{ + internal class JsonConfigurationQuery : IConfigurationQuery + { + private readonly string _filePath; + + public JsonConfigurationQuery(string filePath) + { + _filePath = filePath ?? throw new ArgumentNullException(nameof(filePath)); + } + + #region Get + public async Task GetStringAttributeAsync(string path) + { + return (await GetAttributeAsync(path)).Value(); + } + + public async Task GetIntAttributeAsync(string path) + { + return (await GetAttributeAsync(path)).Value(); + } + + public async Task GetBoolAttributeAsync(string path) + { + return (await GetAttributeAsync(path)).Value(); + } + + public async Task GetObjectAsync(string path) + { + var token = await GetAttributeAsync(path); + return JsonConvert.DeserializeObject(token?.ToString()); + } + #endregion + + public async Task SetAttributeAsync(string path, string value) + { + await SetObjectAsync(path, value); + } + + public async Task SetAttributeAsync(string path, int value) + { + await SetObjectAsync(path, value); + } + + public async Task SetAttributeAsync(string path, bool value) + { + await SetObjectAsync(path, value); + } + + public async Task SetObjectAsync(string path, T value) + { + var jsonObject = JObject.Parse(await File.ReadAllTextAsync(_filePath)); + JToken? token = jsonObject; + var split = path.Trim('.').Split('.'); + for (var index = 0; index < split.Length; index++) + { + var segment = split[index]; + if (token is not JObject jObject) + { + break; + } + if (index == split.Length - 1) + { + jObject[segment] = JToken.FromObject(value); + break; + } + token = jObject.TryGetValue(segment, out var nextToken) ? nextToken : null; + if (token == null) + { + break; + } + } + await File.WriteAllTextAsync(_filePath, jsonObject.ToString(Formatting.Indented)); + } + + private async Task GetAttributeAsync(string path) + { + JToken? token = JObject.Parse(await File.ReadAllTextAsync(_filePath)); + foreach (var segment in path.Trim('.').Split('.')) + { + if (token is not JObject jObject) + { + return default; + } + + token = jObject.TryGetValue(segment, out var nextToken) ? nextToken : null; + } + return token; + } + } +} diff --git a/_src/Implementation/Implementation.csproj b/_src/Implementation/Implementation.csproj index 59c0eff..d4be2b1 100644 --- a/_src/Implementation/Implementation.csproj +++ b/_src/Implementation/Implementation.csproj @@ -12,6 +12,7 @@ $(TargetsForTfmSpecificBuildOutput);CopyProjectReferencesToPackage AnyCPU joshika39.$(MSBuildProjectName) + enable @@ -36,6 +37,12 @@ + + + + + + diff --git a/_src/Implementation/Module/CoreModule.cs b/_src/Implementation/Module/CoreModule.cs index 63c20bd..1cee86c 100644 --- a/_src/Implementation/Module/CoreModule.cs +++ b/_src/Implementation/Module/CoreModule.cs @@ -1,8 +1,10 @@ using System; +using Implementation.Configuration.Factories; using Implementation.IO; using Implementation.IO.Factories; using Implementation.Logger.Factories; using Implementation.Navigator.Factories; +using Infrastructure.Configuration.Factories; using Infrastructure.IO; using Infrastructure.IO.Factories; using Infrastructure.Logger; @@ -23,6 +25,7 @@ public void LoadModules(IServiceCollection collection) collection.AddTransient(); collection.AddTransient(); collection.AddTransient(); + collection.AddTransient(); collection.AddScoped(); collection.AddScoped(); } diff --git a/_src/Infrastructure/Configuration/Factories/IConfigurationQueryFactory.cs b/_src/Infrastructure/Configuration/Factories/IConfigurationQueryFactory.cs new file mode 100644 index 0000000..029c9fb --- /dev/null +++ b/_src/Infrastructure/Configuration/Factories/IConfigurationQueryFactory.cs @@ -0,0 +1,7 @@ +namespace Infrastructure.Configuration.Factories +{ + public interface IConfigurationQueryFactory + { + IConfigurationQuery CreateConfigurationQuery(string filePath); + } +} diff --git a/_src/Infrastructure/Configuration/IConfigurationQuery.cs b/_src/Infrastructure/Configuration/IConfigurationQuery.cs new file mode 100644 index 0000000..760e058 --- /dev/null +++ b/_src/Infrastructure/Configuration/IConfigurationQuery.cs @@ -0,0 +1,17 @@ +using System.Threading.Tasks; + +namespace Infrastructure.Configuration +{ + public interface IConfigurationQuery + { + Task GetStringAttributeAsync(string path); + Task GetIntAttributeAsync(string path); + Task GetBoolAttributeAsync(string path); + Task GetObjectAsync(string path); + + Task SetAttributeAsync(string path, string value); + Task SetAttributeAsync(string path, int value); + Task SetAttributeAsync(string path, bool value); + Task SetObjectAsync(string path, T value); + } +} diff --git a/_src/Infrastructure/Infrastructure.csproj b/_src/Infrastructure/Infrastructure.csproj index a041817..df246f0 100644 --- a/_src/Infrastructure/Infrastructure.csproj +++ b/_src/Infrastructure/Infrastructure.csproj @@ -29,4 +29,8 @@ + + + + diff --git a/_src/_Tests/ImplementationTest/Configuration/JsonConfigurationQueryTests.cs b/_src/_Tests/ImplementationTest/Configuration/JsonConfigurationQueryTests.cs new file mode 100644 index 0000000..c9cc379 --- /dev/null +++ b/_src/_Tests/ImplementationTest/Configuration/JsonConfigurationQueryTests.cs @@ -0,0 +1,66 @@ +using System.Threading.Tasks; +using Implementation.Configuration; +using ImplementationTest.Configuration.Model; +using Xunit; + +namespace ImplementationTest.Configuration +{ + public class JsonConfigurationQueryTests + { + [Fact] + public async Task JCQT_0001() + { + var filePath = @".\Resources\JCQT\0001.json"; + + var jsonService = new JsonConfigurationQuery(filePath); + + var test = await jsonService.GetStringAttributeAsync("server.db.connection"); + } + + [Fact] + public async Task JCQT_0002() + { + var filePath = @".\Resources\JCQT\0001.json"; + + var jsonService = new JsonConfigurationQuery(filePath); + + await jsonService.SetAttributeAsync("server.db.connection", "changed:connection"); + } + + [Fact] + public async Task JCQT_0003() + { + var filePath = @".\Resources\JCQT\0001.json"; + + var jsonService = new JsonConfigurationQuery(filePath); + + await jsonService.SetAttributeAsync("server.db.port", 1234); + } + + [Fact] + public async Task JCQT_0004() + { + var filePath = @".\Resources\JCQT\0001.json"; + + var jsonService = new JsonConfigurationQuery(filePath); + + var admin = await jsonService.GetObjectAsync("server.admin"); + } + + [Fact] + public async Task JCQT_0005() + { + const string FILE_PATH = @".\Resources\JCQT\0001.json"; + + var jsonService = new JsonConfigurationQuery(FILE_PATH); + + var admin = await jsonService.GetObjectAsync("server.admin"); + admin.Name = "Kayaba Akihiko"; + + await jsonService.SetObjectAsync("server.admin", admin); + + admin = await jsonService.GetObjectAsync("server.admin"); + Assert.Equal("Kayaba Akihiko", admin.Name); + } + } +} diff --git a/_src/_Tests/ImplementationTest/Configuration/Model/Admin.cs b/_src/_Tests/ImplementationTest/Configuration/Model/Admin.cs new file mode 100644 index 0000000..9c27bb3 --- /dev/null +++ b/_src/_Tests/ImplementationTest/Configuration/Model/Admin.cs @@ -0,0 +1,14 @@ +using System.Text.Json.Serialization; +using Newtonsoft.Json; + +namespace ImplementationTest.Configuration.Model +{ + public class Admin + { + [JsonPropertyName("name")] + public string Name { get; set; } + + [JsonPropertyName("role")] + public string Role { get; set;} + } +} diff --git a/_src/_Tests/ImplementationTest/Configuration/Model/IAdmin.cs b/_src/_Tests/ImplementationTest/Configuration/Model/IAdmin.cs new file mode 100644 index 0000000..e629326 --- /dev/null +++ b/_src/_Tests/ImplementationTest/Configuration/Model/IAdmin.cs @@ -0,0 +1,8 @@ +namespace ImplementationTest.Configuration.Model +{ + public interface IAdmin + { + string Name { get; set; } + string Role { get; set; } + } +} diff --git a/_src/_Tests/ImplementationTest/ImplementationTest.csproj b/_src/_Tests/ImplementationTest/ImplementationTest.csproj index 3c7e515..ae34d3f 100644 --- a/_src/_Tests/ImplementationTest/ImplementationTest.csproj +++ b/_src/_Tests/ImplementationTest/ImplementationTest.csproj @@ -94,6 +94,18 @@ PreserveNewest + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + + diff --git a/_src/_Tests/ImplementationTest/Resources/JCQT/0001.json b/_src/_Tests/ImplementationTest/Resources/JCQT/0001.json new file mode 100644 index 0000000..ba320ee --- /dev/null +++ b/_src/_Tests/ImplementationTest/Resources/JCQT/0001.json @@ -0,0 +1,11 @@ +{ + "server": { + "db": { + "connection": "test:string" + }, + "admin": { + "name": "Bela", + "role": "Heathcliff" + } + } +} \ No newline at end of file