diff --git a/Bodies/NewDocumentBody.cs b/Bodies/NewDocumentBody.cs deleted file mode 100644 index 1ed3c12..0000000 --- a/Bodies/NewDocumentBody.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System; -using Newtonsoft.Json; - -namespace PaperlessAI.Bodies; - -public class NewDocumentBody -{ - [JsonProperty("document_id")] - public int DocumentId { get; set; } -} diff --git a/Bodies/NewPaperlessDocumentBody.cs b/Bodies/NewPaperlessDocumentBody.cs new file mode 100644 index 0000000..f9ee651 --- /dev/null +++ b/Bodies/NewPaperlessDocumentBody.cs @@ -0,0 +1,15 @@ +using System; +using Newtonsoft.Json; + +namespace PaperlessAI.Bodies; + +public class NewPaperlessDocumentBody +{ + [JsonProperty("document_id")] + public long DocumentId { get; set; } + + public override string ToString() + { + return JsonConvert.SerializeObject(this); + } +} diff --git a/Context/MyDbContext.cs b/Context/MyDbContext.cs index a658568..9cbf38a 100644 --- a/Context/MyDbContext.cs +++ b/Context/MyDbContext.cs @@ -1,5 +1,5 @@ -using System; using Microsoft.EntityFrameworkCore; +using PaperlessAI.Models; namespace PaperlessAI.Context; @@ -19,6 +19,13 @@ public class MyDbContext : DbContext modelBuilder .UseCollation("utf8mb4_uca1400_ai_ci") .HasCharSet("utf8mb4"); + + modelBuilder + .Entity() + .HasIndex(x => x.DocumentId) + .IsUnique(); } + public DbSet PaperlessDocuments { get; set; } + } diff --git a/Controllers/DocumentController.cs b/Controllers/DocumentController.cs deleted file mode 100644 index e0d73e7..0000000 --- a/Controllers/DocumentController.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using PaperlessAI.Bodies; - -namespace PaperlessAI.Controllers -{ - [Route("[controller]")] - [ApiController] - public class DocumentController : ControllerBase - { - [HttpPost] - public ActionResult AddNewDocument([FromBody]NewDocumentBody newDocument) - { - return Ok(); - } - } -} diff --git a/Controllers/PaperlessDocumentController.cs b/Controllers/PaperlessDocumentController.cs new file mode 100644 index 0000000..01d6bb7 --- /dev/null +++ b/Controllers/PaperlessDocumentController.cs @@ -0,0 +1,37 @@ +using Microsoft.AspNetCore.Mvc; +using PaperlessAI.Bodies; +using PaperlessAI.Services; + +namespace PaperlessAI.Controllers +{ + [Route("Document")] + [ApiController] + public class PaperlessDocumentController(PaperlessDocumentService documentService, ILogger logger) : ControllerBase + { + [HttpPost] + public async Task AddNewDocument([FromBody]NewPaperlessDocumentBody newPaperlessDocumentBody) + { + logger.LogDebug($"AddNewDocument called with body: {newPaperlessDocumentBody}"); + + try + { + if(await documentService.Exists(newPaperlessDocumentBody.DocumentId)) + { + logger.LogWarning($"Document {newPaperlessDocumentBody.DocumentId} already exists."); + return Conflict(); + } + + await documentService.Create(newPaperlessDocumentBody.DocumentId); + logger.LogWarning($"Document {newPaperlessDocumentBody.DocumentId} created."); + } + catch (Exception e) + { + logger.LogError(e.ToString()); + return StatusCode(500); + } + + logger.LogDebug($"AddNewDocument Ok!"); + return Ok(); + } + } +} diff --git a/Enums/EOperationState.cs b/Enums/EOperationState.cs new file mode 100644 index 0000000..6f1807f --- /dev/null +++ b/Enums/EOperationState.cs @@ -0,0 +1,10 @@ +namespace PaperlessAI.Enums; + +public enum EOperationState +{ + Created = 0, + Queued = 1, + Started = 2, + Success = 3, + Failed = 4 +} diff --git a/Interfaces/IRepository.cs b/Interfaces/IRepository.cs new file mode 100644 index 0000000..8addd73 --- /dev/null +++ b/Interfaces/IRepository.cs @@ -0,0 +1,11 @@ +namespace PaperlessAI.Interfaces; + +public interface IRepository +{ + Task Add(T model, bool persist = true); + Task Remove(T model, bool persist = true); + Task Update(T model, bool persist = true); + Task Get(long id); + Task> GetAll(); + +} diff --git a/Migrations/20261009132858_Init.Designer.cs b/Migrations/20261009132858_Init.Designer.cs new file mode 100644 index 0000000..8a727ed --- /dev/null +++ b/Migrations/20261009132858_Init.Designer.cs @@ -0,0 +1,60 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using PaperlessAI.Context; + +#nullable disable + +namespace PaperlessAI.Migrations +{ + [DbContext(typeof(MyDbContext))] + [Migration("20261009132858_Init")] + partial class Init + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .UseCollation("utf8mb4_uca1400_ai_ci") + .HasAnnotation("ProductVersion", "10.0.12") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.HasCharSet(modelBuilder, "utf8mb4"); + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("PaperlessAI.Models.PaperlessDocument", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("DocumentId") + .HasColumnType("bigint"); + + b.Property("DocumentState") + .HasColumnType("int"); + + b.Property("ModifiedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("DocumentId") + .IsUnique(); + + b.ToTable("PaperlessDocuments"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Migrations/20261009132858_Init.cs b/Migrations/20261009132858_Init.cs new file mode 100644 index 0000000..2d9486a --- /dev/null +++ b/Migrations/20261009132858_Init.cs @@ -0,0 +1,50 @@ +using System; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace PaperlessAI.Migrations +{ + /// + public partial class Init : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterDatabase() + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "PaperlessDocuments", + columns: table => new + { + Id = table.Column(type: "bigint", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + DocumentId = table.Column(type: "bigint", nullable: false), + DocumentState = table.Column(type: "int", nullable: false), + CreatedAt = table.Column(type: "datetime(6)", nullable: false), + ModifiedAt = table.Column(type: "datetime(6)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_PaperlessDocuments", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4") + .Annotation("Relational:Collation", "utf8mb4_uca1400_ai_ci"); + + migrationBuilder.CreateIndex( + name: "IX_PaperlessDocuments_DocumentId", + table: "PaperlessDocuments", + column: "DocumentId", + unique: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "PaperlessDocuments"); + } + } +} diff --git a/Migrations/MyDbContextModelSnapshot.cs b/Migrations/MyDbContextModelSnapshot.cs new file mode 100644 index 0000000..35a9c56 --- /dev/null +++ b/Migrations/MyDbContextModelSnapshot.cs @@ -0,0 +1,57 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using PaperlessAI.Context; + +#nullable disable + +namespace PaperlessAI.Migrations +{ + [DbContext(typeof(MyDbContext))] + partial class MyDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .UseCollation("utf8mb4_uca1400_ai_ci") + .HasAnnotation("ProductVersion", "10.0.12") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.HasCharSet(modelBuilder, "utf8mb4"); + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("PaperlessAI.Models.PaperlessDocument", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("DocumentId") + .HasColumnType("bigint"); + + b.Property("DocumentState") + .HasColumnType("int"); + + b.Property("ModifiedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("DocumentId") + .IsUnique(); + + b.ToTable("PaperlessDocuments"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Models/PaperlessDocument.cs b/Models/PaperlessDocument.cs new file mode 100644 index 0000000..84b5d82 --- /dev/null +++ b/Models/PaperlessDocument.cs @@ -0,0 +1,24 @@ +using PaperlessAI.Enums; + +namespace PaperlessAI.Models; + +public class PaperlessDocument +{ + public long Id { get; set; } + public long DocumentId { get; set; } + public EOperationState DocumentState { get; set; } = EOperationState.Created; + public DateTimeOffset CreatedAt { get; set; } = DateTime.UtcNow; + public DateTimeOffset ModifiedAt { get; set; } = DateTime.UtcNow; + + public PaperlessDocument(long documentId) + { + DocumentId = documentId; + } + + public static PaperlessDocument Create(long documentId) + { + var paperlessDocument = new PaperlessDocument(documentId); + return paperlessDocument; + } + +} diff --git a/PaperlessAI.csproj b/PaperlessAI.csproj index d6f3d5d..cad8cdd 100644 --- a/PaperlessAI.csproj +++ b/PaperlessAI.csproj @@ -11,7 +11,9 @@ - + + + diff --git a/Program.cs b/Program.cs index 391135f..ebbbdd1 100644 --- a/Program.cs +++ b/Program.cs @@ -1,28 +1,49 @@ using Microsoft.EntityFrameworkCore; using PaperlessAI.Context; +using PaperlessAI.Repositories; +using PaperlessAI.Services; +using Serilog; var builder = WebApplication.CreateBuilder(args); -// Add services to the container. +// Services +builder.Services.AddScoped(); +// Repositories +builder.Services.AddScoped(); + +// Other builder.Services.AddControllers(); +// Database builder.Services.AddDbContextPool(options => { options.UseMySql( builder.Configuration.GetConnectionString("MariaDbConnectionString"), ServerVersion.Parse("12.0.2-mariadb") - ); + ) + .LogTo(Console.WriteLine, LogLevel.Information) + .EnableSensitiveDataLogging() + .EnableDetailedErrors(); }); +// Logging +Log.Logger = new LoggerConfiguration() + .MinimumLevel.Debug() + .WriteTo.Console() + .WriteTo.File( + Path.Combine(AppContext.BaseDirectory, "logs", "log-.txt"), + rollingInterval: RollingInterval.Day, + retainedFileCountLimit: 30) + .CreateLogger(); + +builder.Host.UseSerilog(); + +// App var app = builder.Build(); // Configure the HTTP request pipeline. - app.UseHttpsRedirection(); - app.UseAuthorization(); - app.MapControllers(); - app.Run(); diff --git a/Properties/launchSettings.json b/Properties/launchSettings.json index 6aedc56..81f63eb 100644 --- a/Properties/launchSettings.json +++ b/Properties/launchSettings.json @@ -10,14 +10,5 @@ "ASPNETCORE_ENVIRONMENT": "Development" } }, - "https": { - "commandName": "Project", - "dotnetRunMessages": true, - "launchBrowser": false, - "applicationUrl": "https://localhost:7245;http://localhost:5276", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - } } } diff --git a/Repositories/DocumentRepository.cs b/Repositories/DocumentRepository.cs new file mode 100644 index 0000000..7718912 --- /dev/null +++ b/Repositories/DocumentRepository.cs @@ -0,0 +1,38 @@ +using Microsoft.EntityFrameworkCore; +using PaperlessAI.Context; +using PaperlessAI.Interfaces; +using PaperlessAI.Models; + +namespace PaperlessAI.Repositories; + +public class PaperlessDocumentRepository(MyDbContext context) : IRepository +{ + public async Task Add(PaperlessDocument model, bool persist = true) + { + await context.PaperlessDocuments.AddAsync(model); + await SaveOnPersist(persist); + } + + public async Task Remove(PaperlessDocument model, bool persist = true) + { + context.PaperlessDocuments.Remove(model); + await SaveOnPersist(persist); + } + + public async Task Update(PaperlessDocument model, bool persist = true) + { + context.PaperlessDocuments.Update(model); + await SaveOnPersist(persist); + } + + public async Task Get(long id) => await context.PaperlessDocuments.Where(x => x.DocumentId == id).FirstOrDefaultAsync(); + public async Task> GetAll() => await context.PaperlessDocuments.ToListAsync(); + + public async Task SaveOnPersist(bool persist) + { + if(persist) + { + await context.SaveChangesAsync(); + } + } +} diff --git a/Services/DocumentService.cs b/Services/DocumentService.cs new file mode 100644 index 0000000..a752b8a --- /dev/null +++ b/Services/DocumentService.cs @@ -0,0 +1,22 @@ +using PaperlessAI.Models; +using PaperlessAI.Repositories; + +namespace PaperlessAI.Services; + +public class PaperlessDocumentService(PaperlessDocumentRepository docRepo) +{ + public async Task Exists(long documentId) + { + if(documentId < 0 || documentId > long.MaxValue) + throw new Exception($"Value \"{documentId}\" must be in Range between 0 - {long.MaxValue}!"); + + return await docRepo.Get(documentId) != null; + } + + public async Task Create(long documentId) + { + var doc = PaperlessDocument.Create(documentId); + await docRepo.Add(doc); + return doc; + } +} diff --git a/api.http b/api.http new file mode 100644 index 0000000..1ddee66 --- /dev/null +++ b/api.http @@ -0,0 +1,6 @@ +POST http://localhost:5276/Document HTTP/1.1 +Content-Type: application/json + +{ + "document_id": 8 +} \ No newline at end of file