Skip to content

Commit 58a549a

Browse files
authored
Merge pull request #18 from ajuna-network/add-ersistence-layer
Add Persistence Layer
2 parents b8e8715 + 8a87b21 commit 58a549a

File tree

10 files changed

+139
-19
lines changed

10 files changed

+139
-19
lines changed
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
using System;
2+
3+
namespace Ajuna.AspNetCore.Persistence
4+
{
5+
/// <summary>
6+
/// Representation of a Node Storage Change
7+
/// </summary>
8+
public class ChangeRecord
9+
{
10+
public ChangeRecord()
11+
{
12+
}
13+
14+
public ChangeRecord(string key, string identifier, ChangeAction action, string data )
15+
{
16+
Key = key;
17+
Identifier = identifier;
18+
Action = action;
19+
Data = data;
20+
UpdateDate = DateTimeOffset.Now;
21+
}
22+
23+
public string Key { get; set; }
24+
public string Identifier { get; set; }
25+
public ChangeAction Action { get; set; }
26+
public string Data { get; set; }
27+
public DateTimeOffset? UpdateDate { get; set; }
28+
}
29+
30+
public enum ChangeAction
31+
{
32+
Create,
33+
Update,
34+
Delete
35+
}
36+
37+
}
38+
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
using Ajuna.ServiceLayer.Storage;
2+
using System;
3+
using System.IO;
4+
5+
namespace Ajuna.AspNetCore.Persistence
6+
{
7+
/// <summary>
8+
/// Responsible for storing all changes in a CSV file including the Identifier, Action type, Data and Update Date
9+
/// </summary>
10+
public class StoragePersistenceChangeDelegate : IStorageChangeDelegate
11+
{
12+
private readonly string _csvFilePath;
13+
14+
/// <param name="fileDirectory"> File Path where the CSV file will be located</param>
15+
public StoragePersistenceChangeDelegate(string fileDirectory = null)
16+
{
17+
_csvFilePath = Path.Combine(fileDirectory ?? String.Empty, $"Storage_Changes_{DateTime.Now:yyyyMMdd_HH.mm.ss}.csv");
18+
}
19+
20+
public void OnUpdate(string identifier, string key, string data)
21+
{
22+
var change = new ChangeRecord(key, identifier, ChangeAction.Update, data);
23+
StoreChange(change);
24+
}
25+
26+
public void OnDelete(string identifier, string key, string data)
27+
{
28+
var change = new ChangeRecord(key, identifier, ChangeAction.Delete, data);
29+
StoreChange(change);
30+
}
31+
32+
public void OnCreate(string identifier, string key, string data)
33+
{
34+
var change = new ChangeRecord(key, identifier, ChangeAction.Create, data);
35+
StoreChange(change);
36+
}
37+
38+
private void StoreChange(ChangeRecord changeRecord)
39+
{
40+
if (!File.Exists(_csvFilePath))
41+
{
42+
// Create a file to write to.
43+
using (StreamWriter sw = File.CreateText(_csvFilePath))
44+
{
45+
sw.WriteLine("Key,Identifier,Action,Data,UpdateDate");
46+
}
47+
}
48+
49+
// This text is always added, making the file longer over time
50+
// if it is not deleted.
51+
using (StreamWriter sw = File.AppendText(_csvFilePath))
52+
{
53+
sw.WriteLine(String.Join(",",changeRecord.Key, changeRecord.Identifier, changeRecord.Action, changeRecord.Data, changeRecord.UpdateDate));
54+
}
55+
}
56+
}
57+
}

Ajuna.ServiceLayer/Storage/IStorageChangeDelegate.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
namespace Ajuna.ServiceLayer.Storage
22
{
3+
// This Delegate can be passed to the Storage classes and be triggered on every storage change
34
public interface IStorageChangeDelegate
45
{
56
void OnUpdate(string identifier, string key, string data);

Ajuna.ServiceLayer/Storage/TypedMapStorage.cs

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,23 @@ namespace Ajuna.ServiceLayer.Storage
1010
internal string Identifier { get; private set; }
1111
public Dictionary<string, T> Dictionary { get; private set; }
1212
public IStorageDataProvider DataProvider { get; private set; }
13-
public IStorageChangeDelegate ChangeDelegate { get; private set; }
13+
14+
/// <summary>
15+
/// A ChangeDelegate can be added in order to act upon any Storage Changes
16+
/// </summary>
17+
public List<IStorageChangeDelegate> ChangeDelegates { get; private set; }
1418

1519
public TypedMapStorage(string identifier, IStorageDataProvider dataProvider)
1620
{
1721
Identifier = identifier;
1822
DataProvider = dataProvider;
1923
}
2024

21-
public TypedMapStorage(string identifier, IStorageDataProvider dataProvider, IStorageChangeDelegate changeDelegate)
25+
public TypedMapStorage(string identifier, IStorageDataProvider dataProvider, List<IStorageChangeDelegate> changeDelegates)
2226
{
2327
Identifier = identifier;
2428
DataProvider = dataProvider;
25-
ChangeDelegate = changeDelegate;
29+
ChangeDelegates = changeDelegates;
2630
}
2731

2832
public async Task InitializeAsync(string module, string moduleItem)
@@ -47,7 +51,7 @@ public void Update(string key, string data)
4751
{
4852
Dictionary.Remove(key);
4953
Log.Debug($"[{Identifier}] item {{key}} was deleted.", key);
50-
ChangeDelegate?.OnDelete(Identifier, key, data);
54+
ChangeDelegates?.ForEach(x=>x.OnDelete(Identifier, key, data));
5155
}
5256
else
5357
{
@@ -58,13 +62,13 @@ public void Update(string key, string data)
5862
{
5963
Dictionary[key] = iType;
6064
Log.Debug($"[{Identifier}] item {{key}} was updated.", key);
61-
ChangeDelegate?.OnUpdate(Identifier, key, data);
65+
ChangeDelegates?.ForEach(x=>x.OnUpdate(Identifier, key, data));
6266
}
6367
else
6468
{
6569
Dictionary.Add(key, iType);
6670
Log.Debug($"[{Identifier}] item {{key}} was created.", key);
67-
ChangeDelegate?.OnCreate(Identifier, key, data);
71+
ChangeDelegates?.ForEach(x=>x.OnCreate(Identifier, key, data));
6872
}
6973
}
7074
}

Ajuna.ServiceLayer/Storage/TypedStorage.cs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using Ajuna.NetApi.Model.Types;
22
using Serilog;
3+
using System.Collections.Generic;
34
using System.Threading.Tasks;
45

56
namespace Ajuna.ServiceLayer.Storage
@@ -9,19 +10,23 @@ namespace Ajuna.ServiceLayer.Storage
910
internal string Identifier { get; private set; }
1011
public T Store { get; private set; }
1112
public IStorageDataProvider DataProvider { get; private set; }
12-
public IStorageChangeDelegate ChangeDelegate { get; private set; }
13+
14+
/// <summary>
15+
/// A ChangeDelegate can be added in order to act upon any Storage Changes
16+
/// </summary>
17+
public List<IStorageChangeDelegate> ChangeDelegates { get; private set; }
1318

1419
public TypedStorage(string identifier, IStorageDataProvider dataProvider)
1520
{
1621
Identifier = identifier;
1722
DataProvider = dataProvider;
1823
}
1924

20-
public TypedStorage(string identifier, IStorageDataProvider dataProvider, IStorageChangeDelegate changeDelegate)
25+
public TypedStorage(string identifier, IStorageDataProvider dataProvider, List<IStorageChangeDelegate> changeDelegates)
2126
{
2227
Identifier = identifier;
2328
DataProvider = dataProvider;
24-
ChangeDelegate = changeDelegate;
29+
ChangeDelegates = changeDelegates;
2530
}
2631

2732
public async Task InitializeAsync(string module, string moduleItem)
@@ -49,7 +54,7 @@ public void Update(string data)
4954
Store = iType;
5055
Log.Debug($"[{Identifier}] item was updated.");
5156

52-
ChangeDelegate?.OnUpdate(Identifier, string.Empty, data);
57+
ChangeDelegates?.ForEach(x=>x.OnUpdate(Identifier, string.Empty, data));
5358
}
5459
}
5560
}

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ Using a terminal of your choice, create a new directory for your project and exe
5454
```sh
5555
dotnet new sln
5656
dotnet new ajuna \
57-
--sdk_version 0.1.14 \
57+
--sdk_version 0.1.15 \
5858
--rest_service AjunaExample.RestService \
5959
--net_api AjunaExample.NetApi \
6060
--rest_client AjunaExample.RestClient \

Tools/Ajuna.DotNet.Template/templates/Ajuna/.template.config/template.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
"symbols": {
1313
"sdk_version": {
1414
"datatype": "string",
15-
"defaultValue": "0.1.14",
15+
"defaultValue": "0.1.15",
1616
"description": "Uses the given Ajuna SDK version.",
1717
"replaces": "AJUNA_SDK_VERSION",
1818
"type": "parameter"

Tools/Ajuna.DotNet.Template/templates/Ajuna/Ajuna.RestService/Startup.cs

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using Ajuna.AspNetCore;
2+
using Ajuna.AspNetCore.Persistence;
23
using Ajuna.AspNetCore.Extensions;
34
using Ajuna.RestService.Formatters;
45
using Ajuna.ServiceLayer;
@@ -48,6 +49,15 @@ public class Startup
4849
private IStorageDataProvider _storageDataProvider;
4950
private readonly StorageSubscriptionChangeDelegate _storageChangeDelegate = new StorageSubscriptionChangeDelegate();
5051

52+
// Delegate for adding local persistence for any Storage Changes
53+
// Changes are going to be saved in a CSV file. The default location of the CSV file will be in project root.
54+
// Alternatively, please set the fileDirectory parameter in the constructor below.
55+
private readonly StoragePersistenceChangeDelegate _storagePersistenceChangeDelegate = new StoragePersistenceChangeDelegate();
56+
57+
// Set to true to activate persistence
58+
private readonly bool _useLocalStoragePersistence = false;
59+
60+
5161
/// <summary>
5262
/// >> Startup
5363
/// Constructs and initializes the Startup class.
@@ -60,7 +70,7 @@ public Startup(IConfiguration configuration)
6070
}
6171

6272
/// <summary>
63-
/// Retreives the service configuration.
73+
/// Retrieves the service configuration.
6474
/// </summary>
6575
public IConfiguration Configuration { get; }
6676

@@ -104,15 +114,20 @@ public void ConfigureServices(IServiceCollection services)
104114
c.CustomSchemaIds(type => type.ToString());
105115
c.SwaggerDoc("v1", new OpenApiInfo { Title = "Ajuna.RestService", Version = "v1" });
106116
});
107-
108117
}
109118

110119
private List<IStorage> GetRuntimeStorages()
111120
{
121+
var storageChangeDelegates = new List<IStorageChangeDelegate> {_storageChangeDelegate,};
122+
123+
// If true, add local storage persistence
124+
if(_useLocalStoragePersistence)
125+
storageChangeDelegates.Add(_storagePersistenceChangeDelegate);
126+
112127
return Assembly.GetExecutingAssembly()
113128
.GetTypes()
114129
.Where(anyType => anyType.IsClass && typeof(IStorage).IsAssignableFrom(anyType))
115-
.Select(storageType => (IStorage)Activator.CreateInstance(storageType, new object[] { _storageDataProvider, _storageChangeDelegate }))
130+
.Select(storageType => (IStorage)Activator.CreateInstance(storageType, new object[] { _storageDataProvider, storageChangeDelegates }))
116131
.ToList();
117132
}
118133

Tools/Ajuna.DotNet/Service/Node/RestServiceStorageModuleBuilder.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ private void CreateStorage(CodeNamespace typeNamespace)
7777
};
7878

7979
constructor.Parameters.Add(new CodeParameterDeclarationExpression(new CodeTypeReference("IStorageDataProvider"), "storageDataProvider"));
80-
constructor.Parameters.Add(new CodeParameterDeclarationExpression(new CodeTypeReference("IStorageChangeDelegate"), "storageChangeDelegate"));
80+
constructor.Parameters.Add(new CodeParameterDeclarationExpression(new CodeTypeReference("List<IStorageChangeDelegate>"), "storageChangeDelegates"));
8181
constructor.Comments.AddRange(GetComments(new string[] { $"{ClassName} constructor." }));
8282

8383
targetClass.Members.Add(constructor);
@@ -174,7 +174,7 @@ private void CreateStorage(CodeNamespace typeNamespace)
174174
new CodeExpression[] {
175175
new CodePrimitiveExpression($"{Module.Storage.Prefix}.{entry.Name}"),
176176
new CodeVariableReferenceExpression("storageDataProvider"),
177-
new CodeVariableReferenceExpression("storageChangeDelegate")
177+
new CodeVariableReferenceExpression("storageChangeDelegates")
178178
})));
179179

180180
// create initialize records foreach storage

Version.props

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44
<!-- Configuration -->
55
<VersionMajor>0</VersionMajor>
66
<VersionMinor>1</VersionMinor>
7-
<VersionPatch>14</VersionPatch>
7+
<VersionPatch>15</VersionPatch>
88
<AssemblyVersion>$(VersionMajor).$(VersionMinor).$(VersionPatch)</AssemblyVersion>
9-
<AjunaPackageVersion>0.1.14</AjunaPackageVersion>
9+
<AjunaPackageVersion>0.1.15</AjunaPackageVersion>
1010

1111
<!-- Variables -->
1212
<AjunaVersion>$(AssemblyVersion)</AjunaVersion>

0 commit comments

Comments
 (0)