diff --git a/src/Directory.Build.targets b/src/Directory.Build.targets
index ac4c564d..60a466e4 100644
--- a/src/Directory.Build.targets
+++ b/src/Directory.Build.targets
@@ -31,5 +31,6 @@
+
diff --git a/src/Trash.Tests/Cache/ServiceCacheTest.cs b/src/Trash.Tests/Cache/ServiceCacheTest.cs
index 2e0ce080..62b46c7a 100644
--- a/src/Trash.Tests/Cache/ServiceCacheTest.cs
+++ b/src/Trash.Tests/Cache/ServiceCacheTest.cs
@@ -6,6 +6,7 @@ using Newtonsoft.Json;
using NSubstitute;
using NUnit.Framework;
using Trash.Cache;
+using Trash.Config;
namespace Trash.Tests.Cache
{
@@ -13,6 +14,22 @@ namespace Trash.Tests.Cache
[Parallelizable(ParallelScope.All)]
public class ServiceCacheTest
{
+ private class Context
+ {
+ public Context(IFileSystem? fs = null)
+ {
+ Filesystem = fs ?? Substitute.For();
+ StoragePath = Substitute.For();
+ ServiceConfig = Substitute.For();
+ Cache = new ServiceCache(Filesystem, StoragePath, ServiceConfig);
+ }
+
+ public ServiceCache Cache { get; }
+ public IServiceConfiguration ServiceConfig { get; }
+ public ICacheStoragePath StoragePath { get; }
+ public IFileSystem Filesystem { get; }
+ }
+
private class ObjectWithoutAttribute
{
}
@@ -34,43 +51,36 @@ namespace Trash.Tests.Cache
public void Load_NoFileExists_ThrowsException()
{
// use a real filesystem to test no file existing
- var filesystem = new FileSystem();
- var storagePath = Substitute.For();
- var cache = new ServiceCache(filesystem, storagePath);
+ var ctx = new Context(new FileSystem());
- Action act = () => cache.Load();
+ Action act = () => ctx.Cache.Load();
- act.Should()
- .Throw();
+ act.Should().Throw();
}
[Test]
public void Load_WithAttribute_ParsesCorrectly()
{
- var filesystem = Substitute.For();
- var storagePath = Substitute.For();
- var cache = new ServiceCache(filesystem, storagePath);
+ var ctx = new Context();
- storagePath.Path.Returns("testpath");
+ ctx.StoragePath.Path.Returns("testpath");
dynamic testJson = new {TestValue = "Foo"};
- filesystem.File.ReadAllText(Arg.Any())
+ ctx.Filesystem.File.ReadAllText(Arg.Any())
.Returns(_ => JsonConvert.SerializeObject(testJson));
- var obj = cache.Load();
+ var obj = ctx.Cache.Load();
obj.TestValue.Should().Be("Foo");
- filesystem.File.Received().ReadAllText($"testpath{Path.DirectorySeparatorChar}{ValidObjectName}.json");
+ ctx.Filesystem.File.Received().ReadAllText(Path.Join("testpath", "c59d1c81", $"{ValidObjectName}.json"));
}
[Test]
public void Load_WithAttributeInvalidName_ThrowsException()
{
- var filesystem = Substitute.For();
- var storagePath = Substitute.For();
- var cache = new ServiceCache(filesystem, storagePath);
+ var ctx = new Context();
- Action act = () => cache.Load();
+ Action act = () => ctx.Cache.Load();
act.Should()
.Throw()
@@ -80,11 +90,9 @@ namespace Trash.Tests.Cache
[Test]
public void Load_WithoutAttribute_Throws()
{
- var filesystem = Substitute.For();
- var storagePath = Substitute.For();
- var cache = new ServiceCache(filesystem, storagePath);
+ var ctx = new Context();
- Action act = () => cache.Load();
+ Action act = () => ctx.Cache.Load();
act.Should()
.Throw()
@@ -94,28 +102,27 @@ namespace Trash.Tests.Cache
[Test]
public void Save_WithAttribute_ParsesCorrectly()
{
- var filesystem = Substitute.For();
- var storagePath = Substitute.For();
- var cache = new ServiceCache(filesystem, storagePath);
+ var ctx = new Context();
+
+ ctx.StoragePath.Path.Returns("testpath");
- storagePath.Path.Returns("testpath");
+ ctx.Cache.Save(new ObjectWithAttribute {TestValue = "Foo"});
- cache.Save(new ObjectWithAttribute {TestValue = "Foo"});
+ var expectedParentDirectory = Path.Join("testpath", "c59d1c81");
+ ctx.Filesystem.Directory.Received().CreateDirectory(expectedParentDirectory);
dynamic expectedJson = new {TestValue = "Foo"};
- var expectedPath = $"testpath{Path.DirectorySeparatorChar}{ValidObjectName}.json";
- filesystem.File.Received()
+ var expectedPath = Path.Join(expectedParentDirectory, $"{ValidObjectName}.json");
+ ctx.Filesystem.File.Received()
.WriteAllText(expectedPath, JsonConvert.SerializeObject(expectedJson, Formatting.Indented));
}
[Test]
public void Save_WithAttributeInvalidName_ThrowsException()
{
- var filesystem = Substitute.For();
- var storagePath = Substitute.For();
- var cache = new ServiceCache(filesystem, storagePath);
+ var ctx = new Context();
- Action act = () => cache.Save(new ObjectWithAttributeInvalidChars());
+ Action act = () => ctx.Cache.Save(new ObjectWithAttributeInvalidChars());
act.Should()
.Throw()
@@ -125,11 +132,9 @@ namespace Trash.Tests.Cache
[Test]
public void Save_WithoutAttribute_Throws()
{
- var filesystem = Substitute.For();
- var storagePath = Substitute.For();
- var cache = new ServiceCache(filesystem, storagePath);
+ var ctx = new Context();
- Action act = () => cache.Save(new ObjectWithoutAttribute());
+ Action act = () => ctx.Cache.Save(new ObjectWithoutAttribute());
act.Should()
.Throw()
diff --git a/src/Trash/Cache/ServiceCache.cs b/src/Trash/Cache/ServiceCache.cs
index 6c9402f8..37eae7a4 100644
--- a/src/Trash/Cache/ServiceCache.cs
+++ b/src/Trash/Cache/ServiceCache.cs
@@ -1,23 +1,30 @@
using System;
+using System.Data.HashFunction.FNV;
using System.IO;
using System.IO.Abstractions;
using System.Reflection;
+using System.Text;
using System.Text.RegularExpressions;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
+using Trash.Config;
namespace Trash.Cache
{
public class ServiceCache : IServiceCache
{
private static readonly Regex AllowedObjectNameCharacters = new(@"^\w+$", RegexOptions.Compiled);
+ private readonly IServiceConfiguration _config;
private readonly IFileSystem _fileSystem;
+ private readonly IFNV1a _hash;
private readonly ICacheStoragePath _storagePath;
- public ServiceCache(IFileSystem fileSystem, ICacheStoragePath storagePath)
+ public ServiceCache(IFileSystem fileSystem, ICacheStoragePath storagePath, IServiceConfiguration config)
{
_fileSystem = fileSystem;
_storagePath = storagePath;
+ _config = config;
+ _hash = FNV1aFactory.Instance.Create(FNVConfig.GetPredefinedConfig(32));
}
public T Load()
@@ -28,8 +35,9 @@ namespace Trash.Cache
public void Save(T obj)
{
- _fileSystem.File.WriteAllText(PathFromAttribute(),
- JsonConvert.SerializeObject(obj, Formatting.Indented));
+ var path = PathFromAttribute();
+ _fileSystem.Directory.CreateDirectory(Path.GetDirectoryName(path));
+ _fileSystem.File.WriteAllText(path, JsonConvert.SerializeObject(obj, Formatting.Indented));
}
private static string GetCacheObjectNameAttribute()
@@ -43,6 +51,11 @@ namespace Trash.Cache
return attribute.Name;
}
+ private string BuildServiceGuid()
+ {
+ return _hash.ComputeHash(Encoding.ASCII.GetBytes(_config.BaseUrl)).AsHexString();
+ }
+
private string PathFromAttribute()
{
var objectName = GetCacheObjectNameAttribute();
@@ -51,7 +64,7 @@ namespace Trash.Cache
throw new ArgumentException($"Object name '{objectName}' has unacceptable characters");
}
- return Path.Join(_storagePath.Path, objectName + ".json");
+ return Path.Join(_storagePath.Path, BuildServiceGuid(), objectName + ".json");
}
}
}
diff --git a/src/Trash/Trash.csproj b/src/Trash/Trash.csproj
index 962065ba..5c34f742 100644
--- a/src/Trash/Trash.csproj
+++ b/src/Trash/Trash.csproj
@@ -16,6 +16,7 @@
+