diff --git a/src/Core/API/API.Core/API.Core.csproj b/src/Core/API/API.Core/API.Core.csproj
index 61c6ba5..aab193e 100644
--- a/src/Core/API/API.Core/API.Core.csproj
+++ b/src/Core/API/API.Core/API.Core.csproj
@@ -31,6 +31,7 @@
+
diff --git a/src/Core/API/API.Core/Contracts/Breweries/BreweryCreateDto.cs b/src/Core/API/API.Core/Contracts/Breweries/BreweryCreateDto.cs
deleted file mode 100644
index 342d125..0000000
--- a/src/Core/API/API.Core/Contracts/Breweries/BreweryCreateDto.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-using System;
-
-namespace API.Core.Contracts.Breweries;
-
-public class BreweryCreateDto
-{
- public Guid PostedById { get; set; }
- public string BreweryName { get; set; } = string.Empty;
- public string Description { get; set; } = string.Empty;
- public BreweryLocationCreateDto Location { get; set; } = new BreweryLocationCreateDto();
-}
diff --git a/src/Core/API/API.Core/Contracts/Breweries/BreweryCreateRequestValidator.cs b/src/Core/API/API.Core/Contracts/Breweries/BreweryCreateRequestValidator.cs
new file mode 100644
index 0000000..b181a74
--- /dev/null
+++ b/src/Core/API/API.Core/Contracts/Breweries/BreweryCreateRequestValidator.cs
@@ -0,0 +1,50 @@
+using FluentValidation;
+
+namespace API.Core.Contracts.Breweries;
+
+public class BreweryCreateDtoValidator : AbstractValidator
+{
+ public BreweryCreateDtoValidator()
+ {
+ RuleFor(x => x.PostedById)
+ .NotEmpty()
+ .WithMessage("PostedById is required.");
+
+ RuleFor(x => x.BreweryName)
+ .NotEmpty()
+ .WithMessage("Brewery name is required.")
+ .MaximumLength(256)
+ .WithMessage("Brewery name cannot exceed 256 characters.");
+
+ RuleFor(x => x.Description)
+ .NotEmpty()
+ .WithMessage("Description is required.")
+ .MaximumLength(512)
+ .WithMessage("Description cannot exceed 512 characters.");
+
+ RuleFor(x => x.Location)
+ .NotNull()
+ .WithMessage("Location is required.");
+
+ RuleFor(x => x.Location.CityId)
+ .NotEmpty()
+ .When(x => x.Location is not null)
+ .WithMessage("CityId is required.");
+
+ RuleFor(x => x.Location.AddressLine1)
+ .NotEmpty()
+ .When(x => x.Location is not null)
+ .WithMessage("Address line 1 is required.")
+ .MaximumLength(256)
+ .When(x => x.Location is not null)
+ .WithMessage("Address line 1 cannot exceed 256 characters.");
+
+ RuleFor(x => x.Location.PostalCode)
+ .NotEmpty()
+ .When(x => x.Location is not null)
+ .WithMessage("Postal code is required.")
+ .MaximumLength(20)
+ .When(x => x.Location is not null)
+ .WithMessage("Postal code cannot exceed 20 characters.");
+ }
+}
diff --git a/src/Core/API/API.Core/Contracts/Breweries/BreweryDto.cs b/src/Core/API/API.Core/Contracts/Breweries/BreweryDto.cs
index 3bd7c06..c6ff6bf 100644
--- a/src/Core/API/API.Core/Contracts/Breweries/BreweryDto.cs
+++ b/src/Core/API/API.Core/Contracts/Breweries/BreweryDto.cs
@@ -1,15 +1,41 @@
-using System;
-
namespace API.Core.Contracts.Breweries;
+public class BreweryLocationCreateDto
+{
+ public Guid CityId { get; set; }
+ public string AddressLine1 { get; set; } = string.Empty;
+ public string? AddressLine2 { get; set; }
+ public string PostalCode { get; set; } = string.Empty;
+ public byte[]? Coordinates { get; set; }
+}
+
+public class BreweryLocationDto
+{
+ public Guid BreweryPostLocationId { get; set; }
+ public Guid BreweryPostId { get; set; }
+ public Guid CityId { get; set; }
+ public string AddressLine1 { get; set; } = string.Empty;
+ public string? AddressLine2 { get; set; }
+ public string PostalCode { get; set; } = string.Empty;
+ public byte[]? Coordinates { get; set; }
+}
+
+public class BreweryCreateDto
+{
+ public Guid PostedById { get; set; }
+ public string BreweryName { get; set; } = string.Empty;
+ public string Description { get; set; } = string.Empty;
+ public BreweryLocationCreateDto Location { get; set; } = null!;
+}
+
public class BreweryDto
{
- public Guid BreweryPostId { get; set; }
- public Guid PostedById { get; set; }
- public string BreweryName { get; set; } = string.Empty;
- public string Description { get; set; } = string.Empty;
- public DateTime CreatedAt { get; set; }
- public DateTime? UpdatedAt { get; set; }
- public byte[]? Timer { get; set; }
- public BreweryLocationDto? Location { get; set; }
+ public Guid BreweryPostId { get; set; }
+ public Guid PostedById { get; set; }
+ public string BreweryName { get; set; } = string.Empty;
+ public string Description { get; set; } = string.Empty;
+ public DateTime CreatedAt { get; set; }
+ public DateTime? UpdatedAt { get; set; }
+ public byte[]? Timer { get; set; }
+ public BreweryLocationDto? Location { get; set; }
}
diff --git a/src/Core/API/API.Core/Contracts/Breweries/BreweryLocationCreateDto.cs b/src/Core/API/API.Core/Contracts/Breweries/BreweryLocationCreateDto.cs
deleted file mode 100644
index 8844981..0000000
--- a/src/Core/API/API.Core/Contracts/Breweries/BreweryLocationCreateDto.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using System;
-
-namespace API.Core.Contracts.Breweries;
-
-public class BreweryLocationCreateDto
-{
- public Guid CityId { get; set; }
- public string AddressLine1 { get; set; } = string.Empty;
- public string? AddressLine2 { get; set; }
- public string PostalCode { get; set; } = string.Empty;
- public byte[]? Coordinates { get; set; }
-}
diff --git a/src/Core/API/API.Core/Contracts/Breweries/BreweryLocationDto.cs b/src/Core/API/API.Core/Contracts/Breweries/BreweryLocationDto.cs
deleted file mode 100644
index 0c6173c..0000000
--- a/src/Core/API/API.Core/Contracts/Breweries/BreweryLocationDto.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-using System;
-
-namespace API.Core.Contracts.Breweries;
-
-public class BreweryLocationDto
-{
- public Guid BreweryPostLocationId { get; set; }
- public Guid BreweryPostId { get; set; }
- public Guid CityId { get; set; }
- public string AddressLine1 { get; set; } = string.Empty;
- public string? AddressLine2 { get; set; }
- public string PostalCode { get; set; } = string.Empty;
- public byte[]? Coordinates { get; set; }
-}
diff --git a/src/Core/API/API.Core/Controllers/BreweryController.cs b/src/Core/API/API.Core/Controllers/BreweryController.cs
new file mode 100644
index 0000000..d0fb8d4
--- /dev/null
+++ b/src/Core/API/API.Core/Controllers/BreweryController.cs
@@ -0,0 +1,129 @@
+using API.Core.Contracts.Breweries;
+using API.Core.Contracts.Common;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
+using Service.Breweries;
+
+namespace API.Core.Controllers;
+
+[ApiController]
+[Route("api/[controller]")]
+[Authorize(AuthenticationSchemes = "JWT")]
+public class BreweryController(IBreweryService breweryService) : ControllerBase
+{
+ [AllowAnonymous]
+ [HttpGet("{id:guid}")]
+ public async Task>> GetById(Guid id)
+ {
+ var brewery = await breweryService.GetByIdAsync(id);
+ if (brewery is null)
+ return NotFound(new ResponseBody { Message = $"Brewery with ID {id} not found." });
+
+ return Ok(new ResponseBody
+ {
+ Message = "Brewery retrieved successfully.",
+ Payload = MapToDto(brewery),
+ });
+ }
+
+ [AllowAnonymous]
+ [HttpGet]
+ public async Task>>> GetAll(
+ [FromQuery] int? limit,
+ [FromQuery] int? offset)
+ {
+ var breweries = await breweryService.GetAllAsync(limit, offset);
+ return Ok(new ResponseBody>
+ {
+ Message = "Breweries retrieved successfully.",
+ Payload = breweries.Select(MapToDto),
+ });
+ }
+
+ [HttpPost]
+ public async Task>> Create([FromBody] BreweryCreateDto dto)
+ {
+ var request = new BreweryCreateRequest(
+ dto.PostedById,
+ dto.BreweryName,
+ dto.Description,
+ new BreweryLocationCreateRequest(
+ dto.Location.CityId,
+ dto.Location.AddressLine1,
+ dto.Location.AddressLine2,
+ dto.Location.PostalCode,
+ dto.Location.Coordinates
+ )
+ );
+
+ var result = await breweryService.CreateAsync(request);
+ if (!result.Success)
+ return BadRequest(new ResponseBody { Message = result.Message });
+
+ return Created($"/api/brewery/{result.Brewery.BreweryPostId}", new ResponseBody
+ {
+ Message = "Brewery created successfully.",
+ Payload = MapToDto(result.Brewery),
+ });
+ }
+
+ [HttpPut("{id:guid}")]
+ public async Task>> Update(Guid id, [FromBody] BreweryDto dto)
+ {
+ if (dto.BreweryPostId != id)
+ return BadRequest(new ResponseBody { Message = "Route ID does not match payload ID." });
+
+ var request = new BreweryUpdateRequest(
+ dto.BreweryPostId,
+ dto.PostedById,
+ dto.BreweryName,
+ dto.Description,
+ dto.Location is null ? null : new BreweryLocationUpdateRequest(
+ dto.Location.BreweryPostLocationId,
+ dto.Location.CityId,
+ dto.Location.AddressLine1,
+ dto.Location.AddressLine2,
+ dto.Location.PostalCode,
+ dto.Location.Coordinates
+ )
+ );
+
+ var result = await breweryService.UpdateAsync(request);
+ if (!result.Success)
+ return BadRequest(new ResponseBody { Message = result.Message });
+
+ return Ok(new ResponseBody
+ {
+ Message = "Brewery updated successfully.",
+ Payload = MapToDto(result.Brewery),
+ });
+ }
+
+ [HttpDelete("{id:guid}")]
+ public async Task> Delete(Guid id)
+ {
+ await breweryService.DeleteAsync(id);
+ return Ok(new ResponseBody { Message = "Brewery deleted successfully." });
+ }
+
+ private static BreweryDto MapToDto(Domain.Entities.BreweryPost b) => new()
+ {
+ BreweryPostId = b.BreweryPostId,
+ PostedById = b.PostedById,
+ BreweryName = b.BreweryName,
+ Description = b.Description,
+ CreatedAt = b.CreatedAt,
+ UpdatedAt = b.UpdatedAt,
+ Timer = b.Timer,
+ Location = b.Location is null ? null : new BreweryLocationDto
+ {
+ BreweryPostLocationId = b.Location.BreweryPostLocationId,
+ BreweryPostId = b.Location.BreweryPostId,
+ CityId = b.Location.CityId,
+ AddressLine1 = b.Location.AddressLine1,
+ AddressLine2 = b.Location.AddressLine2,
+ PostalCode = b.Location.PostalCode,
+ Coordinates = b.Location.Coordinates,
+ },
+ };
+}
diff --git a/src/Core/Core.slnx b/src/Core/Core.slnx
index b1ff5d5..6add115 100644
--- a/src/Core/Core.slnx
+++ b/src/Core/Core.slnx
@@ -26,6 +26,7 @@
-
+
+
diff --git a/src/Core/Service/Service.Breweries/BreweryService.cs b/src/Core/Service/Service.Breweries/BreweryService.cs
index 2338e3c..5966f6c 100644
--- a/src/Core/Service/Service.Breweries/BreweryService.cs
+++ b/src/Core/Service/Service.Breweries/BreweryService.cs
@@ -1,72 +1,65 @@
using Domain.Entities;
using Infrastructure.Repository.Breweries;
-using API.Core.Contracts.Breweries;
namespace Service.Breweries;
public class BreweryService(IBreweryRepository repository) : IBreweryService
{
- private readonly IBreweryRepository _repository = repository;
+ public Task GetByIdAsync(Guid id) =>
+ repository.GetByIdAsync(id);
- public Task GetByIdAsync(Guid id) => _repository.GetByIdAsync(id);
+ public Task> GetAllAsync(int? limit = null, int? offset = null) =>
+ repository.GetAllAsync(limit, offset);
- public Task> GetAllAsync(int? limit = null, int? offset = null) => _repository.GetAllAsync(limit, offset);
+ public async Task CreateAsync(BreweryCreateRequest request)
+ {
+ var entity = new BreweryPost
+ {
+ BreweryPostId = Guid.NewGuid(),
+ PostedById = request.PostedById,
+ BreweryName = request.BreweryName,
+ Description = request.Description,
+ CreatedAt = DateTime.UtcNow,
+ Location = new BreweryPostLocation
+ {
+ BreweryPostLocationId = Guid.NewGuid(),
+ CityId = request.Location.CityId,
+ AddressLine1 = request.Location.AddressLine1,
+ AddressLine2 = request.Location.AddressLine2,
+ PostalCode = request.Location.PostalCode,
+ Coordinates = request.Location.Coordinates,
+ },
+ };
- public async Task CreateAsync(BreweryCreateDto brewery)
- {
- if (brewery.Location is null)
- return new BreweryServiceReturn("Location must be provided");
+ await repository.CreateAsync(entity);
+ return new BreweryServiceReturn(entity);
+ }
- var entity = new BreweryPost
- {
- BreweryPostId = Guid.NewGuid(),
- PostedById = brewery.PostedById,
- BreweryName = brewery.BreweryName,
- Description = brewery.Description,
- CreatedAt = DateTime.UtcNow,
- Location = new BreweryPostLocation
- {
- BreweryPostLocationId = Guid.NewGuid(),
- CityId = brewery.Location.CityId,
- AddressLine1 = brewery.Location.AddressLine1,
- AddressLine2 = brewery.Location.AddressLine2,
- PostalCode = brewery.Location.PostalCode,
- Coordinates = brewery.Location.Coordinates
- }
- };
+ public async Task UpdateAsync(BreweryUpdateRequest request)
+ {
+ var entity = new BreweryPost
+ {
+ BreweryPostId = request.BreweryPostId,
+ PostedById = request.PostedById,
+ BreweryName = request.BreweryName,
+ Description = request.Description,
+ UpdatedAt = DateTime.UtcNow,
+ Location = request.Location is null ? null : new BreweryPostLocation
+ {
+ BreweryPostLocationId = request.Location.BreweryPostLocationId,
+ BreweryPostId = request.BreweryPostId,
+ CityId = request.Location.CityId,
+ AddressLine1 = request.Location.AddressLine1,
+ AddressLine2 = request.Location.AddressLine2,
+ PostalCode = request.Location.PostalCode,
+ Coordinates = request.Location.Coordinates,
+ },
+ };
- await _repository.CreateAsync(entity);
- return new BreweryServiceReturn(entity);
- }
+ await repository.UpdateAsync(entity);
+ return new BreweryServiceReturn(entity);
+ }
- public async Task UpdateAsync(BreweryDto brewery)
- {
- if (brewery is null) return new BreweryServiceReturn("Brewery payload is null");
-
- var entity = new BreweryPost
- {
- BreweryPostId = brewery.BreweryPostId,
- PostedById = brewery.PostedById,
- BreweryName = brewery.BreweryName,
- Description = brewery.Description,
- CreatedAt = brewery.CreatedAt,
- UpdatedAt = brewery.UpdatedAt,
- Timer = brewery.Timer,
- Location = brewery.Location is null ? null : new BreweryPostLocation
- {
- BreweryPostLocationId = brewery.Location.BreweryPostLocationId,
- BreweryPostId = brewery.BreweryPostId,
- CityId = brewery.Location.CityId,
- AddressLine1 = brewery.Location.AddressLine1,
- AddressLine2 = brewery.Location.AddressLine2,
- PostalCode = brewery.Location.PostalCode,
- Coordinates = brewery.Location.Coordinates
- }
- };
-
- await _repository.UpdateAsync(entity);
- return new BreweryServiceReturn(entity);
- }
-
- public Task DeleteAsync(Guid id) => _repository.DeleteAsync(id);
+ public Task DeleteAsync(Guid id) =>
+ repository.DeleteAsync(id);
}
diff --git a/src/Core/Service/Service.Breweries/DependencyInjection/BreweryServiceCollectionExtensions.cs b/src/Core/Service/Service.Breweries/DependencyInjection/BreweryServiceCollectionExtensions.cs
deleted file mode 100644
index 6884bbf..0000000
--- a/src/Core/Service/Service.Breweries/DependencyInjection/BreweryServiceCollectionExtensions.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using Microsoft.Extensions.DependencyInjection;
-
-namespace Service.Breweries.DependencyInjection;
-
-public static class BreweryServiceCollectionExtensions
-{
- public static IServiceCollection AddBreweryServices(this IServiceCollection services)
- {
- services.AddScoped();
- return services;
- }
-}
diff --git a/src/Core/Service/Service.Breweries/IBreweryService.cs b/src/Core/Service/Service.Breweries/IBreweryService.cs
index b2ec445..5331634 100644
--- a/src/Core/Service/Service.Breweries/IBreweryService.cs
+++ b/src/Core/Service/Service.Breweries/IBreweryService.cs
@@ -1,33 +1,64 @@
-using API.Core.Contracts.Breweries;
using Domain.Entities;
namespace Service.Breweries;
-public interface IBreweryService
-{
- Task GetByIdAsync(Guid id);
- Task> GetAllAsync(int? limit = null, int? offset = null);
- Task CreateAsync(BreweryCreateDto brewery);
- Task UpdateAsync(BreweryDto brewery);
- Task DeleteAsync(Guid id);
-}
+public record BreweryCreateRequest(
+ Guid PostedById,
+ string BreweryName,
+ string Description,
+ BreweryLocationCreateRequest Location
+);
+
+public record BreweryLocationCreateRequest(
+ Guid CityId,
+ string AddressLine1,
+ string? AddressLine2,
+ string PostalCode,
+ byte[]? Coordinates
+);
+
+public record BreweryUpdateRequest(
+ Guid BreweryPostId,
+ Guid PostedById,
+ string BreweryName,
+ string Description,
+ BreweryLocationUpdateRequest? Location
+);
+
+public record BreweryLocationUpdateRequest(
+ Guid BreweryPostLocationId,
+ Guid CityId,
+ string AddressLine1,
+ string? AddressLine2,
+ string PostalCode,
+ byte[]? Coordinates
+);
public record BreweryServiceReturn
{
- public bool Success { get; init; } = false;
- public BreweryPost Brewery { get; init; }
- public string Message { get; init; } = string.Empty;
+ public bool Success { get; init; }
+ public BreweryPost Brewery { get; init; }
+ public string Message { get; init; } = string.Empty;
- public BreweryServiceReturn(BreweryPost brewery)
- {
- Success = true;
- Brewery = brewery;
- }
+ public BreweryServiceReturn(BreweryPost brewery)
+ {
+ Success = true;
+ Brewery = brewery;
+ }
- public BreweryServiceReturn(string message)
- {
- Success = false;
- Brewery = default!;
- Message = message;
- }
+ public BreweryServiceReturn(string message)
+ {
+ Success = false;
+ Brewery = default!;
+ Message = message;
+ }
+}
+
+public interface IBreweryService
+{
+ Task GetByIdAsync(Guid id);
+ Task> GetAllAsync(int? limit = null, int? offset = null);
+ Task CreateAsync(BreweryCreateRequest request);
+ Task UpdateAsync(BreweryUpdateRequest request);
+ Task DeleteAsync(Guid id);
}
diff --git a/src/Core/Service/Service.Breweries/Service.Breweries.csproj b/src/Core/Service/Service.Breweries/Service.Breweries.csproj
index 3ea9cbb..578b547 100644
--- a/src/Core/Service/Service.Breweries/Service.Breweries.csproj
+++ b/src/Core/Service/Service.Breweries/Service.Breweries.csproj
@@ -1,15 +1,12 @@
-
- net10.0
- enable
- enable
-
+
+ net10.0
+ enable
+ enable
+
-
-
-
-
-
-
+
+
+
+