Skip to content
Merged
Show file tree
Hide file tree
Changes from 52 commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
bde8d16
feature: Generalize write retry queue for all source implementations
RicoSuter Nov 20, 2025
b0eacee
comments
RicoSuter Nov 20, 2025
8bf9ecc
update
RicoSuter Nov 21, 2025
07dda46
update
RicoSuter Nov 21, 2025
2b1185c
Merge branch 'master' into feature/move-failure-queue-to-source
RicoSuter Nov 21, 2025
cfe04f5
update
RicoSuter Nov 21, 2025
ae75ed6
update docs
RicoSuter Nov 21, 2025
27242a1
refactor
RicoSuter Nov 21, 2025
7491b6b
update
RicoSuter Nov 21, 2025
70d9f95
update
RicoSuter Nov 21, 2025
dcd7644
fix
RicoSuter Nov 21, 2025
e21ab58
update
RicoSuter Nov 21, 2025
fd99cb7
refactor
RicoSuter Nov 21, 2025
def5568
up
RicoSuter Nov 21, 2025
91935a3
update
RicoSuter Nov 21, 2025
34e70ff
update
RicoSuter Nov 21, 2025
b2c04e4
add tests
RicoSuter Nov 21, 2025
1528d27
add tests
RicoSuter Nov 22, 2025
9c03557
add tests
RicoSuter Nov 22, 2025
09b0759
revert
RicoSuter Nov 23, 2025
2929154
up
RicoSuter Nov 23, 2025
dde3537
up
RicoSuter Nov 23, 2025
6a23c2f
up
RicoSuter Nov 23, 2025
4342f4b
up
RicoSuter Nov 23, 2025
f05d360
up
RicoSuter Nov 23, 2025
bf08da4
up
RicoSuter Nov 23, 2025
ef88ff3
up
RicoSuter Nov 23, 2025
052e5e3
up
RicoSuter Nov 23, 2025
0a2b97f
up
RicoSuter Nov 23, 2025
e1c2f64
rename
RicoSuter Nov 23, 2025
81f6755
up
RicoSuter Nov 23, 2025
4746351
up
RicoSuter Nov 23, 2025
88eb65b
up
RicoSuter Nov 23, 2025
3a335fb
feature: MQTT client and server
RicoSuter Nov 23, 2025
a760047
merge
RicoSuter Nov 24, 2025
dbe0d54
fix
RicoSuter Nov 24, 2025
a433937
fix
RicoSuter Nov 24, 2025
39ee286
update samples
RicoSuter Nov 24, 2025
b4e3ab4
update
RicoSuter Nov 24, 2025
5c8e33b
udpate
RicoSuter Nov 24, 2025
6ba6d59
up
RicoSuter Nov 24, 2025
cb3c7c6
update
RicoSuter Nov 24, 2025
eb21e79
up
RicoSuter Nov 24, 2025
cba63e3
update
RicoSuter Nov 24, 2025
82a1106
update
RicoSuter Nov 24, 2025
86390f8
improvement
RicoSuter Nov 24, 2025
c066ef8
improvements
RicoSuter Nov 24, 2025
5892832
fix
RicoSuter Nov 24, 2025
43896df
fix
RicoSuter Nov 24, 2025
e58a415
update
RicoSuter Nov 25, 2025
8fa4537
fixes
RicoSuter Nov 25, 2025
c89752b
Apply suggestion from @RicoSuter
RicoSuter Nov 27, 2025
c106384
merge
RicoSuter Nov 28, 2025
01c8b27
update
RicoSuter Nov 28, 2025
2651d84
up
RicoSuter Nov 28, 2025
0e0edd7
improvements
RicoSuter Nov 28, 2025
36fd4e1
up
RicoSuter Nov 28, 2025
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.10" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Namotion.Interceptor.Mqtt\Namotion.Interceptor.Mqtt.csproj" />
<ProjectReference Include="..\Namotion.Interceptor.SamplesModel\Namotion.Interceptor.SamplesModel.csproj" />
<ProjectReference Include="..\Namotion.Interceptor.Hosting\Namotion.Interceptor.Hosting.csproj" />
<ProjectReference Include="..\Namotion.Interceptor.Validation\Namotion.Interceptor.Validation.csproj" />
</ItemGroup>

</Project>
41 changes: 41 additions & 0 deletions src/Namotion.Interceptor.Mqtt.SampleClient/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Namotion.Interceptor;
using Namotion.Interceptor.Hosting;
using Namotion.Interceptor.Mqtt;
using Namotion.Interceptor.Mqtt.Client;
using Namotion.Interceptor.Registry;
using Namotion.Interceptor.SamplesModel;
using Namotion.Interceptor.SamplesModel.Workers;
using Namotion.Interceptor.Sources.Paths;
using Namotion.Interceptor.Tracking;
using Namotion.Interceptor.Validation;

var builder = Host.CreateApplicationBuilder(args);

var context = InterceptorSubjectContext
.Create()
.WithFullPropertyTracking()
.WithRegistry()
.WithParents()
.WithLifecycle()
.WithDataAnnotationValidation()
.WithHostedServices(builder.Services);

var root = Root.CreateWithPersons(context);
context.AddService(root);

builder.Services.AddSingleton(root);
builder.Services.AddHostedService<ClientWorker>();
builder.Services.AddMqttSubjectClient(
_ => root,
_ => new MqttClientConfiguration
{
BrokerHost = "localhost",
BrokerPort = 1883,
PathProvider = new AttributeBasedSourcePathProvider("mqtt", "/", null)
});

using var performanceProfiler = new PerformanceProfiler(context, "Client");
var host = builder.Build();
host.Run();
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.10" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Namotion.Interceptor.Mqtt\Namotion.Interceptor.Mqtt.csproj" />
<ProjectReference Include="..\Namotion.Interceptor.SamplesModel\Namotion.Interceptor.SamplesModel.csproj" />
<ProjectReference Include="..\Namotion.Interceptor.Hosting\Namotion.Interceptor.Hosting.csproj" />
<ProjectReference Include="..\Namotion.Interceptor.Validation\Namotion.Interceptor.Validation.csproj" />
</ItemGroup>

</Project>
40 changes: 40 additions & 0 deletions src/Namotion.Interceptor.Mqtt.SampleServer/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Namotion.Interceptor;
using Namotion.Interceptor.Hosting;
using Namotion.Interceptor.Mqtt.Server;
using Namotion.Interceptor.Registry;
using Namotion.Interceptor.SamplesModel;
using Namotion.Interceptor.SamplesModel.Workers;
using Namotion.Interceptor.Sources.Paths;
using Namotion.Interceptor.Tracking;
using Namotion.Interceptor.Validation;

var builder = Host.CreateApplicationBuilder(args);

var context = InterceptorSubjectContext
.Create()
.WithFullPropertyTracking()
.WithRegistry()
.WithParents()
.WithLifecycle()
.WithDataAnnotationValidation()
.WithHostedServices(builder.Services);

var root = Root.CreateWithPersons(context);
context.AddService(root);

builder.Services.AddSingleton(root);
builder.Services.AddHostedService<ServerWorker>();
builder.Services.AddMqttSubjectServer(
_ => root,
_ => new MqttServerConfiguration
{
BrokerHost = "localhost",
BrokerPort = 1883,
PathProvider = new AttributeBasedSourcePathProvider("mqtt", "/")
});

using var performanceProfiler = new PerformanceProfiler(context, "Server");
var host = builder.Build();
host.Run();
144 changes: 144 additions & 0 deletions src/Namotion.Interceptor.Mqtt.Tests/JsonMqttValueConverterTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
using System.Buffers;
using System.Text;
using System.Text.Json;
using Xunit;

namespace Namotion.Interceptor.Mqtt.Tests;

public class JsonMqttValueConverterTests
{
private readonly JsonMqttValueConverter _converter = new();

[Fact]
public void Serialize_NullValue_ReturnsJsonNull()
{
// Act
var result = _converter.Serialize(null, typeof(object));

// Assert
Assert.Equal("null", Encoding.UTF8.GetString(result));
}

[Theory]
[InlineData(42, typeof(int), "42")]
[InlineData(3.14, typeof(double), "3.14")]
[InlineData(true, typeof(bool), "true")]
[InlineData(false, typeof(bool), "false")]
[InlineData("test", typeof(string), "\"test\"")]
public void Serialize_PrimitiveTypes_ReturnsCorrectJson(object value, Type type, string expected)
{
// Act
var result = _converter.Serialize(value, type);

// Assert
Assert.Equal(expected, Encoding.UTF8.GetString(result));
}

[Fact]
public void Serialize_ComplexObject_ReturnsCorrectJson()
{
// Arrange
var obj = new TestObject { Name = "Test", Value = 123 };

// Act
var result = _converter.Serialize(obj, typeof(TestObject));

// Assert
var json = Encoding.UTF8.GetString(result);
Assert.Contains("\"name\":", json); // camelCase
Assert.Contains("\"Test\"", json);
Assert.Contains("\"value\":", json);
Assert.Contains("123", json);
}

[Fact]
public void Deserialize_EmptyPayload_ReturnsNull()
{
// Arrange
var payload = new ReadOnlySequence<byte>(Array.Empty<byte>());

// Act
var result = _converter.Deserialize(payload, typeof(string));

// Assert
Assert.Null(result);
}

[Fact]
public void Deserialize_JsonNull_ReturnsNull()
{
// Arrange
var payload = new ReadOnlySequence<byte>(Encoding.UTF8.GetBytes("null"));

// Act
var result = _converter.Deserialize(payload, typeof(string));

// Assert
Assert.Null(result);
}

[Theory]
[InlineData("42", typeof(int), 42)]
[InlineData("3.14", typeof(double), 3.14)]
[InlineData("true", typeof(bool), true)]
[InlineData("\"test\"", typeof(string), "test")]
public void Deserialize_PrimitiveTypes_ReturnsCorrectValue(string json, Type type, object expected)
{
// Arrange
var payload = new ReadOnlySequence<byte>(Encoding.UTF8.GetBytes(json));

// Act
var result = _converter.Deserialize(payload, type);

// Assert
Assert.Equal(expected, result);
}

[Fact]
public void Deserialize_ComplexObject_ReturnsCorrectObject()
{
// Arrange
var json = "{\"name\":\"Test\",\"value\":123}";
var payload = new ReadOnlySequence<byte>(Encoding.UTF8.GetBytes(json));

// Act
var result = _converter.Deserialize(payload, typeof(TestObject)) as TestObject;

// Assert
Assert.NotNull(result);
Assert.Equal("Test", result.Name);
Assert.Equal(123, result.Value);
}

[Fact]
public void Deserialize_InvalidJson_ThrowsJsonException()
{
// Arrange
var payload = new ReadOnlySequence<byte>(Encoding.UTF8.GetBytes("invalid json"));

// Act & Assert
Assert.Throws<JsonException>(() => _converter.Deserialize(payload, typeof(int)));
}

[Fact]
public void RoundTrip_PreservesValue()
{
// Arrange
var original = new TestObject { Name = "RoundTrip", Value = 456 };

// Act
var serialized = _converter.Serialize(original, typeof(TestObject));
var deserialized = _converter.Deserialize(new ReadOnlySequence<byte>(serialized), typeof(TestObject)) as TestObject;

// Assert
Assert.NotNull(deserialized);
Assert.Equal(original.Name, deserialized.Name);
Assert.Equal(original.Value, deserialized.Value);
}

private class TestObject
{
public string Name { get; set; } = string.Empty;
public int Value { get; set; }
}
}
Loading