Skip to content

Commit 508ca3d

Browse files
Support JSON.MSET Command (#131)
* Support JSON.MSET Command * add tests * fixes * adding edge filter * bump * applying to .NET 6 too * think it might be picking up the version env var --------- Co-authored-by: slorello89 <[email protected]>
1 parent 3e73654 commit 508ca3d

File tree

9 files changed

+150
-10
lines changed

9 files changed

+150
-10
lines changed

.github/workflows/integration.yml

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ jobs:
2323
USER_NAME: ${{ secrets.USER_NAME }}
2424
PASSWORD: ${{ secrets.PASSWORD }}
2525
ENDPOINT: ${{ secrets.ENDPOINT }}
26+
REDIS_VERSION: ${{ matrix.redis-stack-version }}
2627
steps:
2728
- uses: actions/checkout@v3
2829
- name: .NET Core 6
@@ -45,14 +46,22 @@ jobs:
4546
echo "${{secrets.REDIS_USER_CRT}}" > tests/NRedisStack.Tests/bin/Debug/net6.0/redis_user.crt
4647
echo "${{secrets.REDIS_USER_PRIVATE_KEY}}" > tests/NRedisStack.Tests/bin/Debug/net6.0/redis_user_private.key
4748
ls -R
48-
dotnet test -f net6.0 --no-build --verbosity normal /p:CollectCoverage=true /p:CoverletOutputFormat=opencover
49+
if [ "$REDIS_VERSION" != "edge" ]; then
50+
dotnet test -f net6.0 --no-build --verbosity normal /p:CollectCoverage=true /p:CoverletOutputFormat=opencover --filter Category!=edge
51+
else
52+
dotnet test -f net6.0 --no-build --verbosity normal /p:CollectCoverage=true /p:CoverletOutputFormat=opencover
53+
fi
4954
- name: Test
5055
run: |
5156
echo "${{secrets.REDIS_CA_PEM}}" > tests/NRedisStack.Tests/bin/Debug/net7.0/redis_ca.pem
5257
echo "${{secrets.REDIS_USER_CRT}}" > tests/NRedisStack.Tests/bin/Debug/net7.0/redis_user.crt
5358
echo "${{secrets.REDIS_USER_PRIVATE_KEY}}" > tests/NRedisStack.Tests/bin/Debug/net7.0/redis_user_private.key
5459
ls -R
55-
dotnet test -f net7.0 --no-build --verbosity normal /p:CollectCoverage=true /p:CoverletOutputFormat=opencover
60+
if [ "$REDIS_VERSION" != "edge" ]; then
61+
dotnet test -f net7.0 --no-build --verbosity normal /p:CollectCoverage=true /p:CoverletOutputFormat=opencover --filter Category!=edge
62+
else
63+
dotnet test -f net7.0 --no-build --verbosity normal /p:CollectCoverage=true /p:CoverletOutputFormat=opencover
64+
fi
5665
- name: Codecov
5766
uses: codecov/codecov-action@v3
5867
with:
@@ -96,4 +105,4 @@ jobs:
96105
shell: cmd
97106
run: |
98107
START wsl ./redis-stack-server-${{env.redis_stack_version}}/bin/redis-stack-server &
99-
dotnet test -f net481 --no-build --verbosity normal
108+
dotnet test -f net481 --no-build --verbosity normal --filter Category!=edge
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
using System.Text.Json;
2+
3+
namespace NRedisStack.Json.DataTypes;
4+
5+
public struct KeyValuePath
6+
{
7+
public string Key { get; set; }
8+
public object Value { get; set; }
9+
public string Path { get; set; }
10+
11+
public KeyValuePath(string key, object value, string path = "$")
12+
{
13+
if (key == null || value == null)
14+
{
15+
throw new ArgumentNullException("Key and value cannot be null.");
16+
}
17+
18+
Key = key;
19+
Value = value;
20+
Path = path;
21+
}
22+
public string[] ToArray()
23+
{
24+
if (Value is string)
25+
{
26+
return new string[] { Key, Path, Value.ToString() };
27+
}
28+
return new string[] { Key, Path, JsonSerializer.Serialize(Value) };
29+
}
30+
}

src/NRedisStack/Json/IJsonCommands.cs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using StackExchange.Redis;
1+
using NRedisStack.Json.DataTypes;
2+
using StackExchange.Redis;
23

34
namespace NRedisStack;
45

@@ -206,7 +207,16 @@ public interface IJsonCommands
206207
bool Set(RedisKey key, RedisValue path, RedisValue json, When when = When.Always);
207208

208209
/// <summary>
209-
/// Set json file from the provided file Path.
210+
/// Sets or updates the JSON value of one or more keys.
211+
/// </summary>
212+
/// <param name="keyValuePathList">The key, The value to set and
213+
/// The path to set within the key, must be > 1 </param>
214+
/// <returns>The disposition of the command</returns>
215+
/// <remarks><seealso href="https://redis.io/commands/json.mset"/></remarks>
216+
bool MSet(KeyValuePath[] keyValuePathList);
217+
218+
/// <summary>
219+
/// Sets or updates the JSON value of one or more keys.
210220
/// </summary>
211221
/// <param name="key">The key.</param>
212222
/// <param name="path">The path to set within the key.</param>

src/NRedisStack/Json/IJsonCommandsAsync.cs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using StackExchange.Redis;
1+
using NRedisStack.Json.DataTypes;
2+
using StackExchange.Redis;
23

34
namespace NRedisStack;
45

@@ -205,6 +206,15 @@ public interface IJsonCommandsAsync
205206
/// <remarks><seealso href="https://redis.io/commands/json.set"/></remarks>
206207
Task<bool> SetAsync(RedisKey key, RedisValue path, RedisValue json, When when = When.Always);
207208

209+
/// <summary>
210+
/// Sets or updates the JSON value of one or more keys.
211+
/// </summary>
212+
/// <param name="keyValuePathList">The key, The value to set and
213+
/// The path to set within the key, must be > 1 </param>
214+
/// <returns>The disposition of the command</returns>
215+
/// <remarks><seealso href="https://redis.io/commands/json.mset"/></remarks>
216+
Task<bool> MSetAsync(KeyValuePath[] keyValuePathList);
217+
208218
/// <summary>
209219
/// Set json file from the provided file Path.
210220
/// </summary>

src/NRedisStack/Json/JsonCommandBuilder.cs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using NRedisStack.Json.Literals;
1+
using NRedisStack.Json.DataTypes;
2+
using NRedisStack.Json.Literals;
23
using NRedisStack.RedisStackCommands;
34
using StackExchange.Redis;
45
using System.Text.Json;
@@ -28,6 +29,15 @@ public static SerializedCommand Set(RedisKey key, RedisValue path, RedisValue js
2829
};
2930
}
3031

32+
public static SerializedCommand MSet(KeyValuePath[] keyValuePathList)
33+
{
34+
if (keyValuePathList.Length < 1)
35+
throw new ArgumentOutOfRangeException(nameof(keyValuePathList));
36+
37+
var args = keyValuePathList.SelectMany(x => x.ToArray()).ToArray();
38+
return new SerializedCommand(JSON.MSET, args);
39+
}
40+
3141
public static SerializedCommand StrAppend(RedisKey key, string value, string? path = null)
3242
{
3343
if (path == null)

src/NRedisStack/Json/JsonCommands.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using StackExchange.Redis;
1+
using NRedisStack.Json.DataTypes;
2+
using StackExchange.Redis;
23
using System.Text.Json;
34
using System.Text.Json.Nodes;
45

@@ -39,6 +40,12 @@ public bool Set(RedisKey key, RedisValue path, RedisValue json, When when = When
3940
return _db.Execute(JsonCommandBuilder.Set(key, path, json, when)).OKtoBoolean();
4041
}
4142

43+
/// <inheritdoc/>
44+
public bool MSet(KeyValuePath[] keyValuePathList)
45+
{
46+
return _db.Execute(JsonCommandBuilder.MSet(keyValuePathList)).OKtoBoolean();
47+
}
48+
4249
/// <inheritdoc/>
4350
public bool SetFromFile(RedisKey key, RedisValue path, string filePath, When when = When.Always)
4451
{

src/NRedisStack/Json/JsonCommandsAsync.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using StackExchange.Redis;
1+
using NRedisStack.Json.DataTypes;
2+
using StackExchange.Redis;
23
using System.Text.Json;
34
using System.Text.Json.Nodes;
45

@@ -143,6 +144,11 @@ public async Task<bool> SetAsync(RedisKey key, RedisValue path, RedisValue json,
143144
return (await _db.ExecuteAsync(JsonCommandBuilder.Set(key, path, json, when))).OKtoBoolean();
144145
}
145146

147+
public async Task<bool> MSetAsync(KeyValuePath[] keyValuePathList)
148+
{
149+
return (await _db.ExecuteAsync(JsonCommandBuilder.MSet(keyValuePathList))).OKtoBoolean();
150+
}
151+
146152
public async Task<bool> SetFromFileAsync(RedisKey key, RedisValue path, string filePath, When when = When.Always)
147153
{
148154
if (!File.Exists(filePath))

src/NRedisStack/Json/Literals/Commands.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ internal class JSON
1515
public const string FORGET = "JSON.FORGET";
1616
public const string GET = "JSON.GET";
1717
public const string MEMORY = "MEMORY";
18+
public const string MSET = "JSON.MSET";
1819
public const string MGET = "JSON.MGET";
1920
public const string NUMINCRBY = "JSON.NUMINCRBY";
2021
public const string NUMMULTBY = "JSON.NUMMULTBY";

tests/NRedisStack.Tests/Json/JsonTests.cs

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
using StackExchange.Redis;
55
using Moq;
66
using NRedisStack.RedisStackCommands;
7-
7+
using NRedisStack.Json.DataTypes;
88

99
namespace NRedisStack.Tests;
1010

@@ -725,6 +725,63 @@ public async Task GetAsync()
725725
Assert.Equal(35, people[1]!.Age);
726726
}
727727

728+
[Fact]
729+
[Trait("Category","edge")]
730+
public void MSet()
731+
{
732+
IJsonCommands commands = new JsonCommands(redisFixture.Redis.GetDatabase());
733+
var keys = CreateKeyNames(2);
734+
var key1 = keys[0];
735+
var key2 = keys[1];
736+
737+
KeyValuePath[] values = new[]
738+
{
739+
new KeyValuePath(key1, new { a = "hello" }),
740+
new KeyValuePath(key2, new { a = "world" })
741+
};
742+
commands.MSet(values)
743+
;
744+
var result = commands.MGet(keys.Select(x => new RedisKey(x)).ToArray(), "$.a");
745+
746+
Assert.Equal("[\"hello\"]", result[0].ToString());
747+
Assert.Equal("[\"world\"]", result[1].ToString());
748+
749+
// test errors:
750+
Assert.Throws<ArgumentOutOfRangeException>(() => commands.MSet(new KeyValuePath[0]));
751+
752+
}
753+
754+
[Fact]
755+
[Trait("Category","edge")]
756+
public async Task MSetAsync()
757+
{
758+
IJsonCommandsAsync commands = new JsonCommands(redisFixture.Redis.GetDatabase());
759+
var keys = CreateKeyNames(2);
760+
var key1 = keys[0];
761+
var key2 = keys[1];
762+
KeyValuePath[] values = new[]
763+
{
764+
new KeyValuePath(key1, new { a = "hello" }),
765+
new KeyValuePath(key2, new { a = "world" })
766+
};
767+
await commands.MSetAsync(values)
768+
;
769+
var result = await commands.MGetAsync(keys.Select(x => new RedisKey(x)).ToArray(), "$.a");
770+
771+
Assert.Equal("[\"hello\"]", result[0].ToString());
772+
Assert.Equal("[\"world\"]", result[1].ToString());
773+
774+
// test errors:
775+
await Assert.ThrowsAsync<ArgumentOutOfRangeException>(async () => await commands.MSetAsync(new KeyValuePath[0]));
776+
}
777+
778+
[Fact]
779+
public void TestKeyValuePathErrors()
780+
{
781+
Assert.Throws<ArgumentNullException>(() => new KeyValuePath(null!, new { a = "hello" }));
782+
Assert.Throws<ArgumentNullException>(() => new KeyValuePath("key", null!) );
783+
}
784+
728785
[Fact]
729786
public void MGet()
730787
{

0 commit comments

Comments
 (0)