diff --git a/.gitignore b/.gitignore index c0df4b2..364e10d 100644 --- a/.gitignore +++ b/.gitignore @@ -22,4 +22,6 @@ Thumbs.db # Environment / secrets .env -appsettings.Development.json \ No newline at end of file +appsettings.json +appsettings.Development.json +.fake diff --git a/Migrations/20260416121807_Init.Designer.cs b/Migrations/20260416121807_Init.Designer.cs new file mode 100644 index 0000000..4755071 --- /dev/null +++ b/Migrations/20260416121807_Init.Designer.cs @@ -0,0 +1,49 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using TicketAppIncrArchi.Infrastructure.Persistence; + +#nullable disable + +namespace TicketAppIncrArchi.Migrations +{ + [DbContext(typeof(AppDbContext))] + [Migration("20260416121807_Init")] + partial class Init + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.5") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("TicketAppIncrArchi.Domain.Entities.Ticket", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Tickets"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Migrations/20260416121807_Init.cs b/Migrations/20260416121807_Init.cs new file mode 100644 index 0000000..cc55799 --- /dev/null +++ b/Migrations/20260416121807_Init.cs @@ -0,0 +1,35 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace TicketAppIncrArchi.Migrations +{ + /// + public partial class Init : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "Tickets", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + Title = table.Column(type: "text", nullable: false), + Description = table.Column(type: "text", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Tickets", x => x.Id); + }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "Tickets"); + } + } +} diff --git a/Migrations/AppDbContextModelSnapshot.cs b/Migrations/AppDbContextModelSnapshot.cs new file mode 100644 index 0000000..1a59f91 --- /dev/null +++ b/Migrations/AppDbContextModelSnapshot.cs @@ -0,0 +1,46 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using TicketAppIncrArchi.Infrastructure.Persistence; + +#nullable disable + +namespace TicketAppIncrArchi.Migrations +{ + [DbContext(typeof(AppDbContext))] + partial class AppDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.5") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("TicketAppIncrArchi.Domain.Entities.Ticket", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Tickets"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Program.cs b/Program.cs index 3ad00f5..0fbdbe6 100644 --- a/Program.cs +++ b/Program.cs @@ -1,7 +1,8 @@ using TicketAppIncrArchi.API.Controllers; using TicketAppIncrArchi.Application.Interfaces; using TicketAppIncrArchi.Application.Services; - +using TicketAppIncrArchi.Infrastructure.Persistence; +using Microsoft.EntityFrameworkCore; var builder = WebApplication.CreateBuilder(args); @@ -13,7 +14,10 @@ builder.Services.AddOpenApi(); builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); +builder.Services.AddDbContext(options => + options.UseNpgsql(builder.Configuration.GetConnectionString("DefaultConnection"))); +builder.Services.AddScoped(); builder.Services.AddScoped(); var app = builder.Build(); diff --git a/TicketAppIncrArchi.API/Controllers/TicketsController.cs b/TicketAppIncrArchi.API/Controllers/TicketsController.cs index cad0b55..0f19448 100644 --- a/TicketAppIncrArchi.API/Controllers/TicketsController.cs +++ b/TicketAppIncrArchi.API/Controllers/TicketsController.cs @@ -17,36 +17,29 @@ public class TicketsController : ControllerBase { _service = service; } - //----------------------------------------------------------- - [HttpGet] public IEnumerable Get() { - return _service.GetAll(); + return _service.GetAll().Result; } [HttpGet("{id}")] public ActionResult Get(Guid id) { - var result = _service.GetById(id); + var result = _service.GetById(id).Result; if (result is FailureResult fail) return NotFound(fail.Error); var success = (SuccessResult) result; return Ok(success.Value); - - /* - if (ticket == null) return NotFound(); - return Ok(ticket); - */ } [HttpPost] public IActionResult Create(CreateTicketRequest request) { - var result = _service.Create(request); + var result = _service.Create(request).Result; if (result is FailureResult fail) return BadRequest(fail.Error); diff --git a/TicketAppIncrArchi.Application/Interfaces/ITicketRepository.cs b/TicketAppIncrArchi.Application/Interfaces/ITicketRepository.cs new file mode 100644 index 0000000..c127bbf --- /dev/null +++ b/TicketAppIncrArchi.Application/Interfaces/ITicketRepository.cs @@ -0,0 +1,11 @@ +using TicketAppIncrArchi.Domain.Entities; + +public interface ITicketRepository +{ + + Task GetByIdAsync(Guid id); + Task> GetAllAsync(); + Task AddAsync(Ticket ticket); + Task DeleteAsync(Guid id); + +} diff --git a/TicketAppIncrArchi.Application/Interfaces/ITicketService.cs b/TicketAppIncrArchi.Application/Interfaces/ITicketService.cs index 31d7c2a..9cf713e 100644 --- a/TicketAppIncrArchi.Application/Interfaces/ITicketService.cs +++ b/TicketAppIncrArchi.Application/Interfaces/ITicketService.cs @@ -1,14 +1,13 @@ +using System.Data.Entity.Infrastructure; using TicketAppIncrArchi.Application.DTO; using TicketAppIncrArchi.Domain.Entities; namespace TicketAppIncrArchi.Application.Interfaces; public interface ITicketService -{ - +{ //TODO: implement Repository - IEnumerable GetAll(); - - Result GetById(Guid id); - Result Create(CreateTicketRequest request); + Task> GetAll(); + Task> GetById(Guid id); + Task> Create(CreateTicketRequest request); } diff --git a/TicketAppIncrArchi.Application/Services/TicketServices.cs b/TicketAppIncrArchi.Application/Services/TicketServices.cs index 228e010..86d19a8 100644 --- a/TicketAppIncrArchi.Application/Services/TicketServices.cs +++ b/TicketAppIncrArchi.Application/Services/TicketServices.cs @@ -11,10 +11,19 @@ namespace TicketAppIncrArchi.Application.Services; public class TicketService : ITicketService { - private readonly List _tickets = new(); - public IEnumerable GetAll() + private readonly ITicketRepository _repo; + //private readonly List _tickets = new(); + + public TicketService(ITicketRepository repo) { - var result =_tickets.Select(t => new TicketResponse + _repo = repo; + } + + public async Task> GetAll() + { + var tickets = await _repo.GetAllAsync(); + + var result =tickets.Select(t => new TicketResponse { Id = t.Id, Title = t.Title, @@ -23,29 +32,27 @@ public class TicketService : ITicketService return result; } - - public Result GetById(Guid id) + public async Task> GetById(Guid id) { - //TODO: should use result - var found = _tickets.FirstOrDefault(ticket => ticket.Id == id); - - if (found == null) + var ticket = await _repo.GetByIdAsync(id); + + if (ticket == null) { return Result.Fail("No Ticket Found"); } var ticketResponse = new TicketResponse { - Id = found.Id, - Title = found.Title, - Description = found.Description, + Id = ticket.Id, + Title = ticket.Title, + Description = ticket.Description, }; - return Result.Ok(found); + return Result.Ok(ticketResponse); } - public Result Create(CreateTicketRequest request) + public async Task> Create(CreateTicketRequest request) { if (string.IsNullOrWhiteSpace(request.Title)) @@ -53,25 +60,21 @@ public class TicketService : ITicketService var ticket = new Ticket { - Id = Guid.NewGuid(), Title = request.Title, Description = request.Description }; - //send creation to repo - _tickets.Add(ticket); + Ticket resultTicket = await _repo.AddAsync(ticket); var ticketResponse = new CreateTicketResponse { - Id = ticket.Id, - Title = ticket.Title, - Description = ticket.Description + Id = resultTicket.Id, + Title = resultTicket.Title, + Description = resultTicket.Description }; - return Result.Ok(ticketResponse); - + return Result.Ok(ticketResponse); } - } diff --git a/TicketAppIncrArchi.Domain/Entities.cs/Tickets.cs b/TicketAppIncrArchi.Domain/Entities.cs/Tickets.cs index a801c05..afa13fb 100644 --- a/TicketAppIncrArchi.Domain/Entities.cs/Tickets.cs +++ b/TicketAppIncrArchi.Domain/Entities.cs/Tickets.cs @@ -1,6 +1,9 @@ +using System.ComponentModel.DataAnnotations; + namespace TicketAppIncrArchi.Domain.Entities; public class Ticket { + [Key] public Guid Id {get;set;} public string Title {get;set;} = ""; public string Description {get;set;} = ""; diff --git a/TicketAppIncrArchi.Infrastructure/DbContext.cs b/TicketAppIncrArchi.Infrastructure/DbContext.cs new file mode 100644 index 0000000..ba16279 --- /dev/null +++ b/TicketAppIncrArchi.Infrastructure/DbContext.cs @@ -0,0 +1,18 @@ + +using Microsoft.EntityFrameworkCore; + +namespace TicketAppIncrArchi.Infrastructure.Persistence; + +using TicketAppIncrArchi.Domain.Entities; + +public class AppDbContext : DbContext +{ + public AppDbContext(DbContextOptions options) + : base(options) + { + + } + + public DbSet Tickets {get;set;} +} + diff --git a/TicketAppIncrArchi.Infrastructure/TicketRepository.cs b/TicketAppIncrArchi.Infrastructure/TicketRepository.cs new file mode 100644 index 0000000..9e363a7 --- /dev/null +++ b/TicketAppIncrArchi.Infrastructure/TicketRepository.cs @@ -0,0 +1,37 @@ +using Microsoft.EntityFrameworkCore; +using TicketAppIncrArchi.Domain.Entities; +using TicketAppIncrArchi.Application.Interfaces; +using TicketAppIncrArchi.Infrastructure.Persistence; + + +public class TicketRepository : ITicketRepository +{ + private readonly AppDbContext _context; + + public TicketRepository(AppDbContext context) + { + _context = context; + } + + public async Task GetByIdAsync(Guid id) + => await _context.Tickets.FindAsync(id); + + public async Task> GetAllAsync() + => await _context.Tickets.ToListAsync(); + + public async Task AddAsync(Ticket ticket) + { + _context.Tickets.Add(ticket); + await _context.SaveChangesAsync(); + return(ticket); + } + + public async Task DeleteAsync(Guid id) + { + var ticket = await _context.Tickets.FindAsync(id); + if (ticket is null) return; + + _context.Tickets.Remove(ticket); + await _context.SaveChangesAsync(); + } +} diff --git a/TicketAppIncrArchi.csproj b/TicketAppIncrArchi.csproj index ad460fe..f167c54 100644 --- a/TicketAppIncrArchi.csproj +++ b/TicketAppIncrArchi.csproj @@ -7,7 +7,13 @@ + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + diff --git a/TicketAppIncrArchi.csproj.Backup.tmp b/TicketAppIncrArchi.csproj.Backup.tmp new file mode 100644 index 0000000..16c659d --- /dev/null +++ b/TicketAppIncrArchi.csproj.Backup.tmp @@ -0,0 +1,15 @@ + + + + net10.0 + enable + enable + + + + + + + + + diff --git a/appsettings.json b/appsettings.json index 10f68b8..6df0a13 100644 --- a/appsettings.json +++ b/appsettings.json @@ -5,5 +5,8 @@ "Microsoft.AspNetCore": "Warning" } }, - "AllowedHosts": "*" + "AllowedHosts": "*", + "ConnectionStrings":{ + "DefaultConnection": "Host=localhost;Port=45738;Database=mydb;Username=postgres;Password=password" + } } diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..8227d3d --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,20 @@ +services: + db: + image: postgres:14.5 + restart: always + environment: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: password + POSTGRES_DB: mydb + ports: + - "45738:5432" + volumes: + - pgdata:/var/lib/postgresql/data + + adminer: + image: adminer + restart: always + ports: + - 8080:8080 +volumes: + pgdata: