Skip to content
Merged
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
20 changes: 20 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,23 @@ updates:
commit-message:
include: scope
prefix: "fix: "
# Framework-aligned packages are versioned per target framework in Directory.Packages.props.
# A new major version means a new target framework block, which is added by hand.
ignore:
- dependency-name: "Microsoft.Extensions.*"
update-types: ["version-update:semver-major"]
- dependency-name: "Microsoft.EntityFrameworkCore*"
update-types: ["version-update:semver-major"]
- dependency-name: "Microsoft.AspNetCore.Authentication.JwtBearer"
update-types: ["version-update:semver-major"]
- dependency-name: "Microsoft.AspNetCore.JsonPatch"
update-types: ["version-update:semver-major"]
- dependency-name: "Microsoft.AspNetCore.Mvc.NewtonsoftJson"
update-types: ["version-update:semver-major"]
- package-ecosystem: "dotnet-sdk"
directory: "/"
schedule:
interval: "monthly"
commit-message:
include: scope
prefix: "chore: "
8 changes: 5 additions & 3 deletions .github/workflows/build-and-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,14 @@ jobs:

steps:
- name : Checkout Repo
uses: actions/checkout@v2
uses: actions/checkout@v4

- name: Setup .NET Core
uses: actions/setup-dotnet@v1
uses: actions/setup-dotnet@v4
with:
dotnet-version: 8.0.101
# The .NET 9 SDK supplies the net9.0 runtime for tests; global.json pins the SDK that builds.
dotnet-version: 9.0.x
global-json-file: global.json

- name: Run dotnet format
run: dotnet format lit-redis.sln --verify-no-changes
Expand Down
15 changes: 8 additions & 7 deletions .github/workflows/build-test-and-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,18 @@ jobs:
steps:

- name: Setup Git
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
token : ${{ secrets.FIREBEND_GIT_PUSH_PAT}}
fetch-depth: 0

- name: Setup .NET Core
uses: actions/setup-dotnet@v4
with:
# The .NET 9 SDK supplies the net9.0 runtime for tests; global.json pins the SDK that builds.
dotnet-version: 9.0.x
global-json-file: global.json

- name: Install Versionize
run: dotnet tool install --global Versionize

Expand All @@ -34,12 +41,6 @@ jobs:
if: steps.versionize.outcome != 'success'
run: echo "Skipping publishing. No release required."

- name: Setup .NET Core
if: steps.versionize.outcome == 'success'
uses: actions/setup-dotnet@v1
with:
dotnet-version: 8.0.101

- name: Install dependencies
if: steps.versionize.outcome == 'success'
run: dotnet restore lit-redis.sln
Expand Down
15 changes: 15 additions & 0 deletions Directory.Build.props
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<Project>

<PropertyGroup>
<!-- Frameworks every packable library and test project targets. Edit this list to add or drop a framework. -->
<FirebendTargetFrameworks>net9.0;net10.0</FirebendTargetFrameworks>
<!-- Single framework for samples, benchmarks, and other runnable apps. -->
<FirebendAppTargetFramework>net10.0</FirebendAppTargetFramework>
</PropertyGroup>

<ItemGroup Condition="'$(PackageIcon)' != ''">
<!-- Embeds the repo-root icon in every package that sets PackageIcon. -->
<None Include="$(MSBuildThisFileDirectory)nuget-image.png" Pack="true" PackagePath="\" Visible="false" />
</ItemGroup>

</Project>
44 changes: 44 additions & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<Project>

<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>

<!--
Framework-aligned packages (Microsoft.Extensions.*, System.*) follow the target framework's
release band, so each framework in FirebendTargetFrameworks gets its own block below.
To add a framework (for example net11.0), copy one block, change the condition, and set
the 11.0.x versions.
-->
<ItemGroup Condition="'$(TargetFramework)' == 'net9.0'">
<PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="9.0.20" />
<PackageVersion Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="9.0.20" />
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="9.0.20" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="9.0.20" />
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)' == 'net10.0'">
<PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="10.0.12" />
<PackageVersion Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="10.0.12" />
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="10.0.12" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="10.0.12" />
</ItemGroup>

<!-- Packages that do not follow the framework release band. -->
<ItemGroup>
<PackageVersion Include="StackExchange.Redis" Version="3.3.1" />
</ItemGroup>

<!-- Test packages. -->
<ItemGroup>
<PackageVersion Include="AutoFixture.AutoMoq" Version="4.18.1" />
<PackageVersion Include="coverlet.collector" Version="10.1.0" />
<!-- FluentAssertions v8 requires a commercial license; stay on the latest v7. -->
<PackageVersion Include="FluentAssertions" Version="7.2.2" />
<PackageVersion Include="GitHubActionsTestLogger" Version="3.0.5" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.10.1" />
<PackageVersion Include="MSTest.TestAdapter" Version="4.5.1" />
<PackageVersion Include="MSTest.TestFramework" Version="4.5.1" />
</ItemGroup>

</Project>
3 changes: 2 additions & 1 deletion LitRedis.Core/Implementations/LitRedisCacheStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using LitRedis.Core.Interfaces;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Primitives;
using StackExchange.Redis;

namespace LitRedis.Core.Implementations;

Expand Down Expand Up @@ -48,7 +49,7 @@ public async Task PutAsync<T>(string key, T model, TimeSpan? expiry, Cancellatio

var json = _jsonSerializer.Serialize(model);

await _litRedisConnectionService.UseDbAsync((db, _) => db.StringSetAsync(key, json, expiry), cancellationToken);
await _litRedisConnectionService.UseDbAsync((db, _) => db.StringSetAsync(key, json, expiry.HasValue ? new Expiration(expiry.Value) : Expiration.Default), cancellationToken);
}

/// <inheritdoc />
Expand Down
14 changes: 7 additions & 7 deletions LitRedis.Core/Interfaces/ILitRedisCacheStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,17 @@ namespace LitRedis.Core.Interfaces;

public interface ILitRedisCacheStore
{
Task PutAsync<T>(string key, T model, TimeSpan? expiry, CancellationToken cancellationToken);
public Task PutAsync<T>(string key, T model, TimeSpan? expiry, CancellationToken cancellationToken);

Task<T> GetAsync<T>(string key, CancellationToken cancellationToken);
public Task<T> GetAsync<T>(string key, CancellationToken cancellationToken);

Task<string> GetAsync(string key, CancellationToken cancellationToken);
public Task<string> GetAsync(string key, CancellationToken cancellationToken);

Task ClearAsync(string key, CancellationToken cancellationToken);
public Task ClearAsync(string key, CancellationToken cancellationToken);

Task<IEnumerable<string>> GetAllKeys(CancellationToken cancellationToken);
public Task<IEnumerable<string>> GetAllKeys(CancellationToken cancellationToken);

Task ClearAllAsync(CancellationToken cancellationToken);
public Task ClearAllAsync(CancellationToken cancellationToken);

Task SetExpiryAsync(string key, TimeSpan span, CancellationToken cancellationToken);
public Task SetExpiryAsync(string key, TimeSpan span, CancellationToken cancellationToken);
}
4 changes: 2 additions & 2 deletions LitRedis.Core/Interfaces/ILitRedisConnection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace LitRedis.Core.Interfaces;

public interface ILitRedisConnection
{
void ForceReconnect();
public void ForceReconnect();

Task<ConnectionMultiplexer> GetConnectionMultiplexer();
public Task<ConnectionMultiplexer> GetConnectionMultiplexer();
}
6 changes: 3 additions & 3 deletions LitRedis.Core/Interfaces/ILitRedisConnectionService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ namespace LitRedis.Core.Interfaces;

public interface ILitRedisConnectionService
{
Task<T> UseDbAsync<T>(Func<IDatabase, CancellationToken, Task<T>> fn, CancellationToken cancellationToken);
public Task<T> UseDbAsync<T>(Func<IDatabase, CancellationToken, Task<T>> fn, CancellationToken cancellationToken);

Task<T> UseServerAsync<T>(Func<IServer, CancellationToken, Task<T>> fn, CancellationToken cancellationToken);
public Task<T> UseServerAsync<T>(Func<IServer, CancellationToken, Task<T>> fn, CancellationToken cancellationToken);

Task UseServerAsync(Func<IServer, CancellationToken, Task> fn, CancellationToken cancellationToken);
public Task UseServerAsync(Func<IServer, CancellationToken, Task> fn, CancellationToken cancellationToken);
}
6 changes: 3 additions & 3 deletions LitRedis.Core/Interfaces/ILitRedisDistributedLock.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,18 @@ namespace LitRedis.Core.Interfaces;

public interface ILitRedisDistributedLock
{
Task<bool> TakeLockAsync(
public Task<bool> TakeLockAsync(
string key,
string token,
TimeSpan expiryTime,
CancellationToken cancellationToken);

Task<bool> ReleaseLockAsync(
public Task<bool> ReleaseLockAsync(
string key,
string token,
CancellationToken cancellationToken);

Task<bool> ExtendLockAsync(
public Task<bool> ExtendLockAsync(
string key,
string token,
TimeSpan expiryTime,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ namespace LitRedis.Core.Interfaces;

public interface ILitRedisDistributedLockService
{
Task<LitRedisDistributedLockModel> AcquireLockAsync(RequestLockModel model, CancellationToken cancellationToken);
public Task<LitRedisDistributedLockModel> AcquireLockAsync(RequestLockModel model, CancellationToken cancellationToken);
}
4 changes: 2 additions & 2 deletions LitRedis.Core/Interfaces/ILitRedisJsonSerializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ namespace LitRedis.Core.Interfaces;

public interface ILitRedisJsonSerializer
{
string Serialize<T>(T value);
public string Serialize<T>(T value);

T Deserialize<T>(string value);
public T Deserialize<T>(string value);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ namespace LitRedis.Core.Interfaces;

public interface ILitRedisSystemTextJsonOptionsProvider
{
JsonSerializerOptions GetOptions();
public JsonSerializerOptions GetOptions();
}
15 changes: 7 additions & 8 deletions LitRedis.Core/LitRedis.Core.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<TargetFrameworks>$(FirebendTargetFrameworks)</TargetFrameworks>
<Version>5.0.0</Version>
<LangVersion>latestmajor</LangVersion>
<Bumper>4</Bumper>
Expand All @@ -13,17 +13,16 @@
<PackageTags>firebend;redis;cache;caching;lock;distributed;locking</PackageTags>
<Authors>Firebend</Authors>
<RepositoryUrl>https://github.com/firebend/lit-redis</RepositoryUrl>
<PackageLicenseUrl>https://github.com/firebend/lit-redis/blob/main/LICENSE</PackageLicenseUrl>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageProjectUrl>https://github.com/firebend/lit-redis</PackageProjectUrl>
<PackageIconUrl>https://github.com/firebend/lit-redis/blob/main/nuget-image.png?raw=true</PackageIconUrl>
<PackageIcon>nuget-image.png</PackageIcon>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="9.0.8" />
<PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="9.0.8" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="9.0.8" />
<PackageReference Include="StackExchange.Redis" Version="2.9.11" />
<PackageReference Include="System.Text.Json" Version="9.0.8" />
<PackageReference Include="Microsoft.Extensions.Caching.Memory" />
<PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" />
<PackageReference Include="Microsoft.Extensions.Logging" />
<PackageReference Include="StackExchange.Redis" />
</ItemGroup>

</Project>
6 changes: 2 additions & 4 deletions LitRedis.Sample/LitRedis.Sample.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,11 @@

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>$(FirebendAppTargetFramework)</TargetFramework>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.8" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="9.0.8" />
<PackageReference Include="System.Text.Json" Version="9.0.8" />
<PackageReference Include="Microsoft.Extensions.Hosting" />
</ItemGroup>

<ItemGroup>
Expand Down
55 changes: 55 additions & 0 deletions LitRedis.Tests/Core/Implementations/LitRedisCacheStoreTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -100,4 +100,59 @@ public async Task Lit_Redis_Cache_Store_Should_Put_Value_Object()
mockRedisConnectionService.Verify(x =>
x.UseDbAsync(It.IsAny<Func<IDatabase, CancellationToken, Task<bool>>>(), It.IsAny<CancellationToken>()), Times.Once);
}

[TestMethod]
public async Task Lit_Redis_Cache_Store_Should_Put_Without_Expiry_When_Expiry_Is_Null()
{
//act
var expiration = await PutAndCaptureExpirationAsync(null);

//assert
// Expiration.Default sends a plain SET, which clears any existing TTL (it is not KEEPTTL).
expiration.Should().Be(Expiration.Default);
expiration.Should().NotBe(Expiration.KeepTtl);
}

[TestMethod]
public async Task Lit_Redis_Cache_Store_Should_Put_With_Relative_Expiry_When_Expiry_Is_Set()
{
//arrange
var expiry = TimeSpan.FromMinutes(5);

//act
var expiration = await PutAndCaptureExpirationAsync(expiry);

//assert
expiration.Should().Be(new Expiration(expiry));
}

private static async Task<Expiration> PutAndCaptureExpirationAsync(TimeSpan? expiry)
{
var fixture = new Fixture();
fixture.Customize(new AutoMoqCustomization());

var mockRedisConnectionService = fixture.Freeze<Mock<ILitRedisConnectionService>>();
var mockDatabase = new Mock<IDatabase>();
Expiration? captured = null;

mockDatabase.Setup(x => x.StringSetAsync(
It.IsAny<RedisKey>(), It.IsAny<RedisValue>(), It.IsAny<Expiration>(), It.IsAny<ValueCondition>(), It.IsAny<CommandFlags>()))
.Callback<RedisKey, RedisValue, Expiration, ValueCondition, CommandFlags>((_, _, expiration, _, _) => captured = expiration)
.ReturnsAsync(true);

mockRedisConnectionService.Setup(x =>
x.UseDbAsync(It.IsAny<Func<IDatabase, CancellationToken, Task<bool>>>(), It.IsAny<CancellationToken>()))
.Returns<Func<IDatabase, CancellationToken, Task<bool>>, CancellationToken>((func, ct) => func(mockDatabase.Object, ct));

var cacheStore = fixture.Create<LitRedisCacheStore>();

await cacheStore.PutAsync("fake-key", new FakeCacheObject { Value = "Fakey Fake" }, expiry, default);

mockDatabase.Verify(x => x.StringSetAsync(
"fake-key", It.IsAny<RedisValue>(), It.IsAny<Expiration>(), It.IsAny<ValueCondition>(), It.IsAny<CommandFlags>()), Times.Once);

captured.Should().NotBeNull();

return captured.Value;
}
}
17 changes: 8 additions & 9 deletions LitRedis.Tests/LitRedis.Tests.csproj
Original file line number Diff line number Diff line change
@@ -1,26 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<TargetFrameworks>$(FirebendTargetFrameworks)</TargetFrameworks>

<IsPackable>false</IsPackable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="AutoFixture.AutoMoq" Version="4.18.1" />
<PackageReference Include="FluentAssertions" Version="8.6.0" />
<PackageReference Include="GitHubActionsTestLogger" Version="2.4.1">
<PackageReference Include="AutoFixture.AutoMoq" />
<PackageReference Include="FluentAssertions" />
<PackageReference Include="GitHubActionsTestLogger">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
<PackageReference Include="MSTest.TestAdapter" Version="3.10.4" />
<PackageReference Include="MSTest.TestFramework" Version="3.10.4" />
<PackageReference Include="coverlet.collector" Version="6.0.4">
<PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="MSTest.TestAdapter" />
<PackageReference Include="MSTest.TestFramework" />
<PackageReference Include="coverlet.collector">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="System.ComponentModel.TypeConverter" Version="4.3.0" />
</ItemGroup>

<ItemGroup>
Expand Down
Loading
Loading