From bc2dc969ff6afc1503dd32ab07701dc974a4c29e Mon Sep 17 00:00:00 2001 From: Jonathan Bourdon Date: Tue, 2 Jul 2024 00:23:14 -0400 Subject: [PATCH] Add content system --- src/Web/Contents/Data/Content.cs | 13 +++++ src/Web/Contents/Data/ContentDbContext.cs | 19 ++++++ src/Web/Contents/Handlers/GetContents.cs | 30 ++++++++++ .../Contents/Handlers/GetContentsByUser.cs | 30 ++++++++++ src/Web/Contents/Handlers/PostMessage.cs | 39 +++++++++++++ .../20240702034957_Initial.Designer.cs | 58 +++++++++++++++++++ .../Migrations/20240702034957_Initial.cs | 38 ++++++++++++ .../ContentDbContextModelSnapshot.cs | 55 ++++++++++++++++++ src/Web/Program.cs | 6 ++ src/Web/Web.csproj | 4 -- src/Web/appsettings.Development.json | 3 +- 11 files changed, 290 insertions(+), 5 deletions(-) create mode 100644 src/Web/Contents/Data/Content.cs create mode 100644 src/Web/Contents/Data/ContentDbContext.cs create mode 100644 src/Web/Contents/Handlers/GetContents.cs create mode 100644 src/Web/Contents/Handlers/GetContentsByUser.cs create mode 100644 src/Web/Contents/Handlers/PostMessage.cs create mode 100644 src/Web/Contents/Migrations/20240702034957_Initial.Designer.cs create mode 100644 src/Web/Contents/Migrations/20240702034957_Initial.cs create mode 100644 src/Web/Contents/Migrations/ContentDbContextModelSnapshot.cs diff --git a/src/Web/Contents/Data/Content.cs b/src/Web/Contents/Data/Content.cs new file mode 100644 index 0000000..c13bd0b --- /dev/null +++ b/src/Web/Contents/Data/Content.cs @@ -0,0 +1,13 @@ +namespace Hutopy.Web.Contents.Data; + +public class Content +{ + public Guid Id { get; init; } + public Guid CreatedBy { get; init; } + public DateTime CreatedAt { get; } + + public string? Title { get; init; } = null!; + public string? Description { get; init; } = null!; + public string? Uri { get; init; } = null!; +} + diff --git a/src/Web/Contents/Data/ContentDbContext.cs b/src/Web/Contents/Data/ContentDbContext.cs new file mode 100644 index 0000000..f525cbf --- /dev/null +++ b/src/Web/Contents/Data/ContentDbContext.cs @@ -0,0 +1,19 @@ +using Microsoft.EntityFrameworkCore; + +namespace Hutopy.Web.Contents.Data; + +public class ContentDbContext( + DbContextOptions options) + : DbContext(options) +{ + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder + .Entity() + .Property(c => c.CreatedAt) + .ValueGeneratedOnAdd() + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + } + + public DbSet Contents { get; set; } +} diff --git a/src/Web/Contents/Handlers/GetContents.cs b/src/Web/Contents/Handlers/GetContents.cs new file mode 100644 index 0000000..c9f3258 --- /dev/null +++ b/src/Web/Contents/Handlers/GetContents.cs @@ -0,0 +1,30 @@ +using FastEndpoints; +using Hutopy.Web.Contents.Data; +using Microsoft.EntityFrameworkCore; + +namespace Hutopy.Web.Contents.Handlers; + +public class GetContents( + ContentDbContext context) + : EndpointWithoutRequest +{ + public override void Configure() + { + Tags("Contents"); + Get("/api/contents/{ContentId:guid}"); + AllowAnonymous(); + } + + public override async Task HandleAsync( + CancellationToken ct) + { + var contentId = Route("ContentId"); + + var comments = await context + .Contents + .Where(c => c.Id == contentId) + .ToListAsync(cancellationToken: ct); + + await SendAsync(comments.First(), cancellation: ct); + } +} diff --git a/src/Web/Contents/Handlers/GetContentsByUser.cs b/src/Web/Contents/Handlers/GetContentsByUser.cs new file mode 100644 index 0000000..e3796d6 --- /dev/null +++ b/src/Web/Contents/Handlers/GetContentsByUser.cs @@ -0,0 +1,30 @@ +using FastEndpoints; +using Hutopy.Web.Messages.Data; +using Microsoft.EntityFrameworkCore; + +namespace Hutopy.Web.Contents.Handlers; + +public class GetContentsByUser( + MessagingDbContext context) + : EndpointWithoutRequest> +{ + public override void Configure() + { + Tags("Contents"); + Get("/api/contents/by-user/{UserId:guid}"); + AllowAnonymous(); + } + + public override async Task HandleAsync( + CancellationToken ct) + { + var userId = Route("UserId"); + + var posts = await context + .Messages + .Where(c => c.CreatedBy == userId) + .ToListAsync(cancellationToken: ct); + + await SendAsync(posts, cancellation: ct); + } +} diff --git a/src/Web/Contents/Handlers/PostMessage.cs b/src/Web/Contents/Handlers/PostMessage.cs new file mode 100644 index 0000000..850cfba --- /dev/null +++ b/src/Web/Contents/Handlers/PostMessage.cs @@ -0,0 +1,39 @@ +using FastEndpoints; +using Hutopy.Web.Common; +using Hutopy.Web.Contents.Data; + +namespace Hutopy.Web.Contents.Handlers; + +public record struct PostContentRequest( + string? Title, + string? Description, + string? Uri); + +public class PostMessage( + ContentDbContext context) + : Endpoint +{ + public override void Configure() + { + // TODO: Find how to specify the name we see in Swagger + Tags("Contents"); + Post("/api/contents"); + } + + public override async Task HandleAsync( + PostContentRequest req, + CancellationToken ct) + { + await context.Contents.AddAsync( + new Content + { + CreatedBy = User.GetUserId(), + Title = req.Title, + Description = req.Description, + Uri = req.Uri + }, + ct); + + await context.SaveChangesAsync(ct); + } +} diff --git a/src/Web/Contents/Migrations/20240702034957_Initial.Designer.cs b/src/Web/Contents/Migrations/20240702034957_Initial.Designer.cs new file mode 100644 index 0000000..c63f6b4 --- /dev/null +++ b/src/Web/Contents/Migrations/20240702034957_Initial.Designer.cs @@ -0,0 +1,58 @@ +// +using System; +using Hutopy.Web.Contents.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Hutopy.Web.Contents.Migrations +{ + [DbContext(typeof(ContentDbContext))] + [Migration("20240702034957_Initial")] + partial class Initial + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.3") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("Hutopy.Web.Contents.Data.Content", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("CreatedBy") + .HasColumnType("uniqueidentifier"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("Title") + .HasColumnType("nvarchar(max)"); + + b.Property("Uri") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("Contents"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Web/Contents/Migrations/20240702034957_Initial.cs b/src/Web/Contents/Migrations/20240702034957_Initial.cs new file mode 100644 index 0000000..593cc49 --- /dev/null +++ b/src/Web/Contents/Migrations/20240702034957_Initial.cs @@ -0,0 +1,38 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Hutopy.Web.Contents.Migrations +{ + /// + public partial class Initial : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "Contents", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + CreatedBy = table.Column(type: "uniqueidentifier", nullable: false), + CreatedAt = table.Column(type: "datetime2", nullable: false, defaultValueSql: "CURRENT_TIMESTAMP"), + Title = table.Column(type: "nvarchar(max)", nullable: true), + Description = table.Column(type: "nvarchar(max)", nullable: true), + Uri = table.Column(type: "nvarchar(max)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Contents", x => x.Id); + }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "Contents"); + } + } +} diff --git a/src/Web/Contents/Migrations/ContentDbContextModelSnapshot.cs b/src/Web/Contents/Migrations/ContentDbContextModelSnapshot.cs new file mode 100644 index 0000000..1892c37 --- /dev/null +++ b/src/Web/Contents/Migrations/ContentDbContextModelSnapshot.cs @@ -0,0 +1,55 @@ +// +using System; +using Hutopy.Web.Contents.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Hutopy.Web.Contents.Migrations +{ + [DbContext(typeof(ContentDbContext))] + partial class ContentDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.3") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("Hutopy.Web.Contents.Data.Content", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("CreatedBy") + .HasColumnType("uniqueidentifier"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("Title") + .HasColumnType("nvarchar(max)"); + + b.Property("Uri") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("Contents"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Web/Program.cs b/src/Web/Program.cs index 8bcb1ae..097dac9 100644 --- a/src/Web/Program.cs +++ b/src/Web/Program.cs @@ -4,6 +4,7 @@ using Hutopy.Application; using Hutopy.Infrastructure; using Hutopy.Infrastructure.Data; using Hutopy.Web; +using Hutopy.Web.Contents.Data; using Hutopy.Web.Messages.Data; using Microsoft.AspNetCore.HttpOverrides; using Microsoft.EntityFrameworkCore; @@ -83,6 +84,11 @@ builder.Services.AddDbContext((_, options) => options.UseSqlServer(builder.Configuration.GetConnectionString("CommentStore")); }); +builder.Services.AddDbContext((_, options) => +{ + options.UseSqlServer(builder.Configuration.GetConnectionString("ContentStore")); +}); + var app = builder.Build(); app.UseForwardedHeaders( diff --git a/src/Web/Web.csproj b/src/Web/Web.csproj index 053126a..817e221 100644 --- a/src/Web/Web.csproj +++ b/src/Web/Web.csproj @@ -30,8 +30,4 @@ - - - - diff --git a/src/Web/appsettings.Development.json b/src/Web/appsettings.Development.json index 9e2b285..decba8e 100644 --- a/src/Web/appsettings.Development.json +++ b/src/Web/appsettings.Development.json @@ -9,7 +9,8 @@ }, "ConnectionStrings": { "DefaultConnection": "Server=localhost,1433;Initial Catalog=Hutopy;User Id=sa;Password=P@ssword123!;MultipleActiveResultSets=true;TrustServerCertificate=True;MultiSubnetFailover=True", - "CommentStore": "Server=localhost,1433;Initial Catalog=Hutopy;User Id=sa;Password=P@ssword123!;MultipleActiveResultSets=true;TrustServerCertificate=True;MultiSubnetFailover=True" + "CommentStore": "Server=localhost,1433;Initial Catalog=Hutopy;User Id=sa;Password=P@ssword123!;MultipleActiveResultSets=true;TrustServerCertificate=True;MultiSubnetFailover=True", + "ContentStore": "Server=localhost,1433;Initial Catalog=Hutopy;User Id=sa;Password=P@ssword123!;MultipleActiveResultSets=true;TrustServerCertificate=True;MultiSubnetFailover=True" }, "Authentication": { "Jwt": {