Skip to content
Open
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
10 changes: 10 additions & 0 deletions Backend/common/messaging/IKafkaProducer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using System.Threading;
using System.Threading.Tasks;

namespace ExpensesManager.Common.Messaging
{
public interface IKafkaProducer
{
Task ProduceAsync(string topic, object message, CancellationToken cancellationToken = default);
}
}
28 changes: 28 additions & 0 deletions Backend/common/messaging/KafkaProducer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using System.Text.Json;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using System.Threading;
using System.Threading.Tasks;

namespace ExpensesManager.Common.Messaging
{
public class KafkaProducer : IKafkaProducer
{
private readonly ILogger<KafkaProducer> _logger;
private readonly string _brokers;

public KafkaProducer(ILogger<KafkaProducer> logger, IConfiguration config)
{
_logger = logger;
_brokers = config["KAFKA_BROKERS"] ?? "localhost:9092";
}

public Task ProduceAsync(string topic, object message, CancellationToken cancellationToken = default)
{
var json = JsonSerializer.Serialize(message);
_logger.LogInformation("[KafkaProducer] ProduceAsync -> brokers={Brokers} topic={Topic} message={Message}", _brokers, topic, json);
// No-op implementation for now (dev-friendly). Replace with real Kafka client when ready.
return Task.CompletedTask;
}
}
}
11 changes: 11 additions & 0 deletions Backend/common/messaging/Messaging.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="7.0.0" />
</ItemGroup>
</Project>
31 changes: 31 additions & 0 deletions Backend/services/users-service/Controllers/UsersController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using System.Threading.Tasks;
using ExpensesManager.Common.Messaging;
using Microsoft.AspNetCore.Mvc;

namespace ExpensesManager.UsersService.Controllers
{
[ApiController]
[Route("api/[controller]")]
public class UsersController : ControllerBase
{
private readonly IKafkaProducer _producer;

public UsersController(IKafkaProducer producer)
{
_producer = producer;
}

[HttpGet("health")]
public IActionResult Health() => Ok(new { status = "ok" });

public record UserDto(int UserId, string Username, string? Email);

[HttpPost("signup")]
public async Task<IActionResult> SignUp([FromBody] UserDto dto)
{
var ev = new { eventType = "UserCreated", payload = dto };
await _producer.ProduceAsync("UserCreated", ev);
return Created(string.Empty, dto);
}
}
}
20 changes: 20 additions & 0 deletions Backend/services/users-service/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using ExpensesManager.Common.Messaging;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();
builder.Services.AddSingleton<IKafkaProducer, KafkaProducer>();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}

app.MapControllers();

app.Run();
17 changes: 17 additions & 0 deletions Backend/services/users-service/UsersService.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\common\messaging\Messaging.csproj" />
</ItemGroup>
<ItemGroup>
<!-- Exclude any test files located under the tests folder so they aren't compiled into the web project -->
<Compile Remove="tests\**\*.cs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
using System.Threading;
using System.Threading.Tasks;
using ExpensesManager.Common.Messaging;
using ExpensesManager.UsersService.Controllers;
using Microsoft.AspNetCore.Mvc;
using Xunit;

namespace UsersService.Tests
{
class RecordingProducer : IKafkaProducer
{
public bool WasCalled { get; private set; }
public Task ProduceAsync(string topic, object message, CancellationToken cancellationToken = default)
{
WasCalled = true;
return Task.CompletedTask;
}
}

public class UsersControllerTests
{
[Fact]
public void Health_returns_ok()
{
var controller = new UsersController(new RecordingProducer());
var result = controller.Health();
var ok = Assert.IsType<OkObjectResult>(result);
Assert.Equal(200, ok.StatusCode);
}

[Fact]
public async Task SignUp_returns_created_and_calls_producer()
{
var producer = new RecordingProducer();
var controller = new UsersController(producer);
var dto = new UsersController.UserDto(1, "alice", "a@a.com");
var res = await controller.SignUp(dto);
var created = Assert.IsType<CreatedResult>(res);
Assert.Equal(dto, created.Value);
Assert.True(producer.WasCalled);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="xunit" Version="2.4.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\UsersService.csproj" />
<ProjectReference Include="..\..\..\..\common\messaging\Messaging.csproj" />
</ItemGroup>
</Project>
Loading