diff --git a/.gitignore b/.gitignore index 8c7f0e8..e67f738 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ *.suo *.user *.sln.docstates +.idea/ # Build results @@ -131,3 +132,6 @@ $RECYCLE.BIN/ .DS_Store _NCrunch* + +# Local user appsettings +appsettings.Local.json \ No newline at end of file diff --git a/FictionArchive.API/Program.cs b/FictionArchive.API/Program.cs index 5426d26..cdf2123 100644 --- a/FictionArchive.API/Program.cs +++ b/FictionArchive.API/Program.cs @@ -15,14 +15,10 @@ public class Program builder.Services.AddSwaggerGen(); builder.Services.AddMemoryCache(); - - builder.Services.AddHealthChecks(); var app = builder.Build(); - - // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) diff --git a/FictionArchive.Common/Extensions/HostApplicationBuilderExtensions.cs b/FictionArchive.Common/Extensions/HostApplicationBuilderExtensions.cs new file mode 100644 index 0000000..3400ff5 --- /dev/null +++ b/FictionArchive.Common/Extensions/HostApplicationBuilderExtensions.cs @@ -0,0 +1,13 @@ +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Hosting; + +namespace FictionArchive.Common.Extensions; + +public static class HostApplicationBuilderExtensions +{ + public static IHostApplicationBuilder AddLocalAppsettings(this IHostApplicationBuilder builder) + { + builder.Configuration.AddJsonFile("appsettings.Local.json", true, true); + return builder; + } +} \ No newline at end of file diff --git a/FictionArchive.Common/FictionArchive.Common.csproj b/FictionArchive.Common/FictionArchive.Common.csproj index 1467768..a4cbc9a 100644 --- a/FictionArchive.Common/FictionArchive.Common.csproj +++ b/FictionArchive.Common/FictionArchive.Common.csproj @@ -7,8 +7,16 @@ - - + + + + + + + + + ..\..\..\..\..\..\Program Files\dotnet\shared\Microsoft.AspNetCore.App\8.0.15\Microsoft.Extensions.Hosting.Abstractions.dll + diff --git a/FictionArchive.Service.NovelService/FictionArchive.Service.NovelService.csproj b/FictionArchive.Service.NovelService/FictionArchive.Service.NovelService.csproj index 5ac6730..fae206f 100644 --- a/FictionArchive.Service.NovelService/FictionArchive.Service.NovelService.csproj +++ b/FictionArchive.Service.NovelService/FictionArchive.Service.NovelService.csproj @@ -8,19 +8,6 @@ - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - @@ -36,7 +23,6 @@ - diff --git a/FictionArchive.Service.NovelService/GraphQL/Mutation.cs b/FictionArchive.Service.NovelService/GraphQL/Mutation.cs index ae2b0d0..0a1ac91 100644 --- a/FictionArchive.Service.NovelService/GraphQL/Mutation.cs +++ b/FictionArchive.Service.NovelService/GraphQL/Mutation.cs @@ -46,7 +46,7 @@ public class Mutation { Author = new Person() { - Name = metadata.AuthorName, + Name = LocalizationKey.CreateFromText(metadata.AuthorName, metadata.RawLanguage), ExternalUrl = metadata.AuthorUrl, }, RawLanguage = metadata.RawLanguage, diff --git a/FictionArchive.Service.NovelService/GraphQL/Query.cs b/FictionArchive.Service.NovelService/GraphQL/Query.cs index 27e3860..4539cfc 100644 --- a/FictionArchive.Service.NovelService/GraphQL/Query.cs +++ b/FictionArchive.Service.NovelService/GraphQL/Query.cs @@ -1,5 +1,7 @@ using FictionArchive.Service.NovelService.Models.Novels; using FictionArchive.Service.NovelService.Services; +using HotChocolate.Data; +using HotChocolate.Types; namespace FictionArchive.Service.NovelService.GraphQL; diff --git a/FictionArchive.Service.NovelService/Migrations/20251118021857_Initial.Designer.cs b/FictionArchive.Service.NovelService/Migrations/20251118021857_Initial.Designer.cs deleted file mode 100644 index 3893ff6..0000000 --- a/FictionArchive.Service.NovelService/Migrations/20251118021857_Initial.Designer.cs +++ /dev/null @@ -1,409 +0,0 @@ -// -using System; -using FictionArchive.Service.NovelService.Services; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -#nullable disable - -namespace FictionArchive.Service.NovelService.Migrations -{ - [DbContext(typeof(NovelServiceDbContext))] - [Migration("20251118021857_Initial")] - partial class Initial - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "9.0.11") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Localization.LocalizationKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("CreatedUtc") - .HasColumnType("timestamp with time zone"); - - b.Property("UpdatedUtc") - .HasColumnType("timestamp with time zone"); - - b.HasKey("Id"); - - b.ToTable("LocalizationKey"); - }); - - modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Localization.LocalizationText", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("CreatedUtc") - .HasColumnType("timestamp with time zone"); - - b.Property("Language") - .HasColumnType("integer"); - - b.Property("LocalizationKeyId") - .HasColumnType("bigint"); - - b.Property("Text") - .IsRequired() - .HasColumnType("text"); - - b.Property("TranslationEngineId") - .HasColumnType("bigint"); - - b.Property("UpdatedUtc") - .HasColumnType("timestamp with time zone"); - - b.HasKey("Id"); - - b.HasIndex("LocalizationKeyId"); - - b.HasIndex("TranslationEngineId"); - - b.ToTable("LocalizationText"); - }); - - modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Novels.Chapter", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("BodyId") - .HasColumnType("bigint"); - - b.Property("CreatedUtc") - .HasColumnType("timestamp with time zone"); - - b.Property("NameId") - .HasColumnType("bigint"); - - b.Property("NovelId") - .HasColumnType("bigint"); - - b.Property("Order") - .HasColumnType("bigint"); - - b.Property("Revision") - .HasColumnType("bigint"); - - b.Property("UpdatedUtc") - .HasColumnType("timestamp with time zone"); - - b.Property("Url") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("BodyId"); - - b.HasIndex("NameId"); - - b.HasIndex("NovelId"); - - b.ToTable("Chapter"); - }); - - modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Novels.Novel", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("AuthorId") - .HasColumnType("bigint"); - - b.Property("CreatedUtc") - .HasColumnType("timestamp with time zone"); - - b.Property("DescriptionId") - .HasColumnType("bigint"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("text"); - - b.Property("NameId") - .HasColumnType("bigint"); - - b.Property("RawLanguage") - .HasColumnType("integer"); - - b.Property("RawStatus") - .HasColumnType("integer"); - - b.Property("SourceId") - .HasColumnType("bigint"); - - b.Property("StatusOverride") - .HasColumnType("integer"); - - b.Property("UpdatedUtc") - .HasColumnType("timestamp with time zone"); - - b.Property("Url") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("AuthorId"); - - b.HasIndex("DescriptionId"); - - b.HasIndex("NameId"); - - b.HasIndex("SourceId"); - - b.ToTable("Novels"); - }); - - modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Novels.NovelTag", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("CreatedUtc") - .HasColumnType("timestamp with time zone"); - - b.Property("DisplayNameId") - .HasColumnType("bigint"); - - b.Property("Key") - .IsRequired() - .HasColumnType("text"); - - b.Property("NovelId") - .HasColumnType("bigint"); - - b.Property("SourceId") - .HasColumnType("bigint"); - - b.Property("TagType") - .HasColumnType("integer"); - - b.Property("UpdatedUtc") - .HasColumnType("timestamp with time zone"); - - b.HasKey("Id"); - - b.HasIndex("DisplayNameId"); - - b.HasIndex("NovelId"); - - b.HasIndex("SourceId"); - - b.ToTable("Tags"); - }); - - modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Novels.Person", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("CreatedUtc") - .HasColumnType("timestamp with time zone"); - - b.Property("ExternalUrl") - .HasColumnType("text"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.Property("UpdatedUtc") - .HasColumnType("timestamp with time zone"); - - b.HasKey("Id"); - - b.ToTable("Person"); - }); - - modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Novels.Source", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("CreatedUtc") - .HasColumnType("timestamp with time zone"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.Property("UpdatedUtc") - .HasColumnType("timestamp with time zone"); - - b.Property("Url") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.ToTable("Sources"); - }); - - modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Novels.TranslationEngine", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("CreatedUtc") - .HasColumnType("timestamp with time zone"); - - b.Property("DisplayName") - .IsRequired() - .HasColumnType("text"); - - b.Property("Key") - .IsRequired() - .HasColumnType("text"); - - b.Property("UpdatedUtc") - .HasColumnType("timestamp with time zone"); - - b.HasKey("Id"); - - b.ToTable("TranslationEngines"); - }); - - modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Localization.LocalizationText", b => - { - b.HasOne("FictionArchive.Service.NovelService.Models.Localization.LocalizationKey", null) - .WithMany("Texts") - .HasForeignKey("LocalizationKeyId"); - - b.HasOne("FictionArchive.Service.NovelService.Models.Novels.TranslationEngine", "TranslationEngine") - .WithMany() - .HasForeignKey("TranslationEngineId"); - - b.Navigation("TranslationEngine"); - }); - - modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Novels.Chapter", b => - { - b.HasOne("FictionArchive.Service.NovelService.Models.Localization.LocalizationKey", "Body") - .WithMany() - .HasForeignKey("BodyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("FictionArchive.Service.NovelService.Models.Localization.LocalizationKey", "Name") - .WithMany() - .HasForeignKey("NameId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("FictionArchive.Service.NovelService.Models.Novels.Novel", null) - .WithMany("Chapters") - .HasForeignKey("NovelId"); - - b.Navigation("Body"); - - b.Navigation("Name"); - }); - - modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Novels.Novel", b => - { - b.HasOne("FictionArchive.Service.NovelService.Models.Novels.Person", "Author") - .WithMany() - .HasForeignKey("AuthorId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("FictionArchive.Service.NovelService.Models.Localization.LocalizationKey", "Description") - .WithMany() - .HasForeignKey("DescriptionId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("FictionArchive.Service.NovelService.Models.Localization.LocalizationKey", "Name") - .WithMany() - .HasForeignKey("NameId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("FictionArchive.Service.NovelService.Models.Novels.Source", "Source") - .WithMany() - .HasForeignKey("SourceId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Author"); - - b.Navigation("Description"); - - b.Navigation("Name"); - - b.Navigation("Source"); - }); - - modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Novels.NovelTag", b => - { - b.HasOne("FictionArchive.Service.NovelService.Models.Localization.LocalizationKey", "DisplayName") - .WithMany() - .HasForeignKey("DisplayNameId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("FictionArchive.Service.NovelService.Models.Novels.Novel", null) - .WithMany("Tags") - .HasForeignKey("NovelId"); - - b.HasOne("FictionArchive.Service.NovelService.Models.Novels.Source", "Source") - .WithMany() - .HasForeignKey("SourceId"); - - b.Navigation("DisplayName"); - - b.Navigation("Source"); - }); - - modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Localization.LocalizationKey", b => - { - b.Navigation("Texts"); - }); - - modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Novels.Novel", b => - { - b.Navigation("Chapters"); - - b.Navigation("Tags"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/FictionArchive.Service.NovelService/Migrations/20251118023157_AddSourceKey.Designer.cs b/FictionArchive.Service.NovelService/Migrations/20251118023157_AddSourceKey.Designer.cs deleted file mode 100644 index 5a9570b..0000000 --- a/FictionArchive.Service.NovelService/Migrations/20251118023157_AddSourceKey.Designer.cs +++ /dev/null @@ -1,413 +0,0 @@ -// -using System; -using FictionArchive.Service.NovelService.Services; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -#nullable disable - -namespace FictionArchive.Service.NovelService.Migrations -{ - [DbContext(typeof(NovelServiceDbContext))] - [Migration("20251118023157_AddSourceKey")] - partial class AddSourceKey - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "9.0.11") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Localization.LocalizationKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("CreatedUtc") - .HasColumnType("timestamp with time zone"); - - b.Property("UpdatedUtc") - .HasColumnType("timestamp with time zone"); - - b.HasKey("Id"); - - b.ToTable("LocalizationKey"); - }); - - modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Localization.LocalizationText", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("CreatedUtc") - .HasColumnType("timestamp with time zone"); - - b.Property("Language") - .HasColumnType("integer"); - - b.Property("LocalizationKeyId") - .HasColumnType("bigint"); - - b.Property("Text") - .IsRequired() - .HasColumnType("text"); - - b.Property("TranslationEngineId") - .HasColumnType("bigint"); - - b.Property("UpdatedUtc") - .HasColumnType("timestamp with time zone"); - - b.HasKey("Id"); - - b.HasIndex("LocalizationKeyId"); - - b.HasIndex("TranslationEngineId"); - - b.ToTable("LocalizationText"); - }); - - modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Novels.Chapter", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("BodyId") - .HasColumnType("bigint"); - - b.Property("CreatedUtc") - .HasColumnType("timestamp with time zone"); - - b.Property("NameId") - .HasColumnType("bigint"); - - b.Property("NovelId") - .HasColumnType("bigint"); - - b.Property("Order") - .HasColumnType("bigint"); - - b.Property("Revision") - .HasColumnType("bigint"); - - b.Property("UpdatedUtc") - .HasColumnType("timestamp with time zone"); - - b.Property("Url") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("BodyId"); - - b.HasIndex("NameId"); - - b.HasIndex("NovelId"); - - b.ToTable("Chapter"); - }); - - modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Novels.Novel", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("AuthorId") - .HasColumnType("bigint"); - - b.Property("CreatedUtc") - .HasColumnType("timestamp with time zone"); - - b.Property("DescriptionId") - .HasColumnType("bigint"); - - b.Property("ExternalId") - .IsRequired() - .HasColumnType("text"); - - b.Property("NameId") - .HasColumnType("bigint"); - - b.Property("RawLanguage") - .HasColumnType("integer"); - - b.Property("RawStatus") - .HasColumnType("integer"); - - b.Property("SourceId") - .HasColumnType("bigint"); - - b.Property("StatusOverride") - .HasColumnType("integer"); - - b.Property("UpdatedUtc") - .HasColumnType("timestamp with time zone"); - - b.Property("Url") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("AuthorId"); - - b.HasIndex("DescriptionId"); - - b.HasIndex("NameId"); - - b.HasIndex("SourceId"); - - b.ToTable("Novels"); - }); - - modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Novels.NovelTag", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("CreatedUtc") - .HasColumnType("timestamp with time zone"); - - b.Property("DisplayNameId") - .HasColumnType("bigint"); - - b.Property("Key") - .IsRequired() - .HasColumnType("text"); - - b.Property("NovelId") - .HasColumnType("bigint"); - - b.Property("SourceId") - .HasColumnType("bigint"); - - b.Property("TagType") - .HasColumnType("integer"); - - b.Property("UpdatedUtc") - .HasColumnType("timestamp with time zone"); - - b.HasKey("Id"); - - b.HasIndex("DisplayNameId"); - - b.HasIndex("NovelId"); - - b.HasIndex("SourceId"); - - b.ToTable("Tags"); - }); - - modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Novels.Person", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("CreatedUtc") - .HasColumnType("timestamp with time zone"); - - b.Property("ExternalUrl") - .HasColumnType("text"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.Property("UpdatedUtc") - .HasColumnType("timestamp with time zone"); - - b.HasKey("Id"); - - b.ToTable("Person"); - }); - - modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Novels.Source", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("CreatedUtc") - .HasColumnType("timestamp with time zone"); - - b.Property("Key") - .IsRequired() - .HasColumnType("text"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.Property("UpdatedUtc") - .HasColumnType("timestamp with time zone"); - - b.Property("Url") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.ToTable("Sources"); - }); - - modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Novels.TranslationEngine", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("CreatedUtc") - .HasColumnType("timestamp with time zone"); - - b.Property("DisplayName") - .IsRequired() - .HasColumnType("text"); - - b.Property("Key") - .IsRequired() - .HasColumnType("text"); - - b.Property("UpdatedUtc") - .HasColumnType("timestamp with time zone"); - - b.HasKey("Id"); - - b.ToTable("TranslationEngines"); - }); - - modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Localization.LocalizationText", b => - { - b.HasOne("FictionArchive.Service.NovelService.Models.Localization.LocalizationKey", null) - .WithMany("Texts") - .HasForeignKey("LocalizationKeyId"); - - b.HasOne("FictionArchive.Service.NovelService.Models.Novels.TranslationEngine", "TranslationEngine") - .WithMany() - .HasForeignKey("TranslationEngineId"); - - b.Navigation("TranslationEngine"); - }); - - modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Novels.Chapter", b => - { - b.HasOne("FictionArchive.Service.NovelService.Models.Localization.LocalizationKey", "Body") - .WithMany() - .HasForeignKey("BodyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("FictionArchive.Service.NovelService.Models.Localization.LocalizationKey", "Name") - .WithMany() - .HasForeignKey("NameId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("FictionArchive.Service.NovelService.Models.Novels.Novel", null) - .WithMany("Chapters") - .HasForeignKey("NovelId"); - - b.Navigation("Body"); - - b.Navigation("Name"); - }); - - modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Novels.Novel", b => - { - b.HasOne("FictionArchive.Service.NovelService.Models.Novels.Person", "Author") - .WithMany() - .HasForeignKey("AuthorId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("FictionArchive.Service.NovelService.Models.Localization.LocalizationKey", "Description") - .WithMany() - .HasForeignKey("DescriptionId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("FictionArchive.Service.NovelService.Models.Localization.LocalizationKey", "Name") - .WithMany() - .HasForeignKey("NameId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("FictionArchive.Service.NovelService.Models.Novels.Source", "Source") - .WithMany() - .HasForeignKey("SourceId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Author"); - - b.Navigation("Description"); - - b.Navigation("Name"); - - b.Navigation("Source"); - }); - - modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Novels.NovelTag", b => - { - b.HasOne("FictionArchive.Service.NovelService.Models.Localization.LocalizationKey", "DisplayName") - .WithMany() - .HasForeignKey("DisplayNameId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("FictionArchive.Service.NovelService.Models.Novels.Novel", null) - .WithMany("Tags") - .HasForeignKey("NovelId"); - - b.HasOne("FictionArchive.Service.NovelService.Models.Novels.Source", "Source") - .WithMany() - .HasForeignKey("SourceId"); - - b.Navigation("DisplayName"); - - b.Navigation("Source"); - }); - - modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Localization.LocalizationKey", b => - { - b.Navigation("Texts"); - }); - - modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Novels.Novel", b => - { - b.Navigation("Chapters"); - - b.Navigation("Tags"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/FictionArchive.Service.NovelService/Migrations/20251118023157_AddSourceKey.cs b/FictionArchive.Service.NovelService/Migrations/20251118023157_AddSourceKey.cs deleted file mode 100644 index 6f93508..0000000 --- a/FictionArchive.Service.NovelService/Migrations/20251118023157_AddSourceKey.cs +++ /dev/null @@ -1,29 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace FictionArchive.Service.NovelService.Migrations -{ - /// - public partial class AddSourceKey : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AddColumn( - name: "Key", - table: "Sources", - type: "text", - nullable: false, - defaultValue: ""); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropColumn( - name: "Key", - table: "Sources"); - } - } -} diff --git a/FictionArchive.Service.NovelService/Migrations/20251118030953_FixTagAssociation.cs b/FictionArchive.Service.NovelService/Migrations/20251118030953_FixTagAssociation.cs deleted file mode 100644 index d441835..0000000 --- a/FictionArchive.Service.NovelService/Migrations/20251118030953_FixTagAssociation.cs +++ /dev/null @@ -1,80 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace FictionArchive.Service.NovelService.Migrations -{ - /// - public partial class FixTagAssociation : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropForeignKey( - name: "FK_Tags_Novels_NovelId", - table: "Tags"); - - migrationBuilder.DropIndex( - name: "IX_Tags_NovelId", - table: "Tags"); - - migrationBuilder.DropColumn( - name: "NovelId", - table: "Tags"); - - migrationBuilder.CreateTable( - name: "NovelNovelTag", - columns: table => new - { - NovelsId = table.Column(type: "bigint", nullable: false), - TagsId = table.Column(type: "bigint", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_NovelNovelTag", x => new { x.NovelsId, x.TagsId }); - table.ForeignKey( - name: "FK_NovelNovelTag_Novels_NovelsId", - column: x => x.NovelsId, - principalTable: "Novels", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK_NovelNovelTag_Tags_TagsId", - column: x => x.TagsId, - principalTable: "Tags", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateIndex( - name: "IX_NovelNovelTag_TagsId", - table: "NovelNovelTag", - column: "TagsId"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "NovelNovelTag"); - - migrationBuilder.AddColumn( - name: "NovelId", - table: "Tags", - type: "bigint", - nullable: true); - - migrationBuilder.CreateIndex( - name: "IX_Tags_NovelId", - table: "Tags", - column: "NovelId"); - - migrationBuilder.AddForeignKey( - name: "FK_Tags_Novels_NovelId", - table: "Tags", - column: "NovelId", - principalTable: "Novels", - principalColumn: "Id"); - } - } -} diff --git a/FictionArchive.Service.NovelService/Migrations/20251118030953_FixTagAssociation.Designer.cs b/FictionArchive.Service.NovelService/Migrations/20251118045235_Initial.Designer.cs similarity index 93% rename from FictionArchive.Service.NovelService/Migrations/20251118030953_FixTagAssociation.Designer.cs rename to FictionArchive.Service.NovelService/Migrations/20251118045235_Initial.Designer.cs index bb3e3e3..800d85e 100644 --- a/FictionArchive.Service.NovelService/Migrations/20251118030953_FixTagAssociation.Designer.cs +++ b/FictionArchive.Service.NovelService/Migrations/20251118045235_Initial.Designer.cs @@ -1,10 +1,10 @@ // -using System; using FictionArchive.Service.NovelService.Services; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using NodaTime; using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; #nullable disable @@ -12,8 +12,8 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; namespace FictionArchive.Service.NovelService.Migrations { [DbContext(typeof(NovelServiceDbContext))] - [Migration("20251118030953_FixTagAssociation")] - partial class FixTagAssociation + [Migration("20251118045235_Initial")] + partial class Initial { /// protected override void BuildTargetModel(ModelBuilder modelBuilder) @@ -33,10 +33,10 @@ namespace FictionArchive.Service.NovelService.Migrations NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - b.Property("CreatedUtc") + b.Property("CreatedTime") .HasColumnType("timestamp with time zone"); - b.Property("UpdatedUtc") + b.Property("LastUpdatedTime") .HasColumnType("timestamp with time zone"); b.HasKey("Id"); @@ -52,12 +52,15 @@ namespace FictionArchive.Service.NovelService.Migrations NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - b.Property("CreatedUtc") + b.Property("CreatedTime") .HasColumnType("timestamp with time zone"); b.Property("Language") .HasColumnType("integer"); + b.Property("LastUpdatedTime") + .HasColumnType("timestamp with time zone"); + b.Property("LocalizationKeyId") .HasColumnType("bigint"); @@ -68,9 +71,6 @@ namespace FictionArchive.Service.NovelService.Migrations b.Property("TranslationEngineId") .HasColumnType("bigint"); - b.Property("UpdatedUtc") - .HasColumnType("timestamp with time zone"); - b.HasKey("Id"); b.HasIndex("LocalizationKeyId"); @@ -91,7 +91,10 @@ namespace FictionArchive.Service.NovelService.Migrations b.Property("BodyId") .HasColumnType("bigint"); - b.Property("CreatedUtc") + b.Property("CreatedTime") + .HasColumnType("timestamp with time zone"); + + b.Property("LastUpdatedTime") .HasColumnType("timestamp with time zone"); b.Property("NameId") @@ -106,9 +109,6 @@ namespace FictionArchive.Service.NovelService.Migrations b.Property("Revision") .HasColumnType("bigint"); - b.Property("UpdatedUtc") - .HasColumnType("timestamp with time zone"); - b.Property("Url") .HasColumnType("text"); @@ -134,7 +134,7 @@ namespace FictionArchive.Service.NovelService.Migrations b.Property("AuthorId") .HasColumnType("bigint"); - b.Property("CreatedUtc") + b.Property("CreatedTime") .HasColumnType("timestamp with time zone"); b.Property("DescriptionId") @@ -144,6 +144,9 @@ namespace FictionArchive.Service.NovelService.Migrations .IsRequired() .HasColumnType("text"); + b.Property("LastUpdatedTime") + .HasColumnType("timestamp with time zone"); + b.Property("NameId") .HasColumnType("bigint"); @@ -159,9 +162,6 @@ namespace FictionArchive.Service.NovelService.Migrations b.Property("StatusOverride") .HasColumnType("integer"); - b.Property("UpdatedUtc") - .HasColumnType("timestamp with time zone"); - b.Property("Url") .IsRequired() .HasColumnType("text"); @@ -187,7 +187,7 @@ namespace FictionArchive.Service.NovelService.Migrations NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - b.Property("CreatedUtc") + b.Property("CreatedTime") .HasColumnType("timestamp with time zone"); b.Property("DisplayNameId") @@ -197,15 +197,15 @@ namespace FictionArchive.Service.NovelService.Migrations .IsRequired() .HasColumnType("text"); + b.Property("LastUpdatedTime") + .HasColumnType("timestamp with time zone"); + b.Property("SourceId") .HasColumnType("bigint"); b.Property("TagType") .HasColumnType("integer"); - b.Property("UpdatedUtc") - .HasColumnType("timestamp with time zone"); - b.HasKey("Id"); b.HasIndex("DisplayNameId"); @@ -223,19 +223,19 @@ namespace FictionArchive.Service.NovelService.Migrations NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - b.Property("CreatedUtc") + b.Property("CreatedTime") .HasColumnType("timestamp with time zone"); b.Property("ExternalUrl") .HasColumnType("text"); + b.Property("LastUpdatedTime") + .HasColumnType("timestamp with time zone"); + b.Property("Name") .IsRequired() .HasColumnType("text"); - b.Property("UpdatedUtc") - .HasColumnType("timestamp with time zone"); - b.HasKey("Id"); b.ToTable("Person"); @@ -249,20 +249,20 @@ namespace FictionArchive.Service.NovelService.Migrations NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - b.Property("CreatedUtc") + b.Property("CreatedTime") .HasColumnType("timestamp with time zone"); b.Property("Key") .IsRequired() .HasColumnType("text"); + b.Property("LastUpdatedTime") + .HasColumnType("timestamp with time zone"); + b.Property("Name") .IsRequired() .HasColumnType("text"); - b.Property("UpdatedUtc") - .HasColumnType("timestamp with time zone"); - b.Property("Url") .IsRequired() .HasColumnType("text"); @@ -280,7 +280,7 @@ namespace FictionArchive.Service.NovelService.Migrations NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - b.Property("CreatedUtc") + b.Property("CreatedTime") .HasColumnType("timestamp with time zone"); b.Property("DisplayName") @@ -291,7 +291,7 @@ namespace FictionArchive.Service.NovelService.Migrations .IsRequired() .HasColumnType("text"); - b.Property("UpdatedUtc") + b.Property("LastUpdatedTime") .HasColumnType("timestamp with time zone"); b.HasKey("Id"); diff --git a/FictionArchive.Service.NovelService/Migrations/20251118021857_Initial.cs b/FictionArchive.Service.NovelService/Migrations/20251118045235_Initial.cs similarity index 82% rename from FictionArchive.Service.NovelService/Migrations/20251118021857_Initial.cs rename to FictionArchive.Service.NovelService/Migrations/20251118045235_Initial.cs index d99ce90..c003e82 100644 --- a/FictionArchive.Service.NovelService/Migrations/20251118021857_Initial.cs +++ b/FictionArchive.Service.NovelService/Migrations/20251118045235_Initial.cs @@ -1,5 +1,5 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Migrations; +using NodaTime; using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; #nullable disable @@ -18,8 +18,8 @@ namespace FictionArchive.Service.NovelService.Migrations { Id = table.Column(type: "bigint", nullable: false) .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - CreatedUtc = table.Column(type: "timestamp with time zone", nullable: false), - UpdatedUtc = table.Column(type: "timestamp with time zone", nullable: false) + CreatedTime = table.Column(type: "timestamp with time zone", nullable: false), + LastUpdatedTime = table.Column(type: "timestamp with time zone", nullable: false) }, constraints: table => { @@ -34,8 +34,8 @@ namespace FictionArchive.Service.NovelService.Migrations .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), Name = table.Column(type: "text", nullable: false), ExternalUrl = table.Column(type: "text", nullable: true), - CreatedUtc = table.Column(type: "timestamp with time zone", nullable: false), - UpdatedUtc = table.Column(type: "timestamp with time zone", nullable: false) + CreatedTime = table.Column(type: "timestamp with time zone", nullable: false), + LastUpdatedTime = table.Column(type: "timestamp with time zone", nullable: false) }, constraints: table => { @@ -49,9 +49,10 @@ namespace FictionArchive.Service.NovelService.Migrations Id = table.Column(type: "bigint", nullable: false) .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), Name = table.Column(type: "text", nullable: false), + Key = table.Column(type: "text", nullable: false), Url = table.Column(type: "text", nullable: false), - CreatedUtc = table.Column(type: "timestamp with time zone", nullable: false), - UpdatedUtc = table.Column(type: "timestamp with time zone", nullable: false) + CreatedTime = table.Column(type: "timestamp with time zone", nullable: false), + LastUpdatedTime = table.Column(type: "timestamp with time zone", nullable: false) }, constraints: table => { @@ -66,8 +67,8 @@ namespace FictionArchive.Service.NovelService.Migrations .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), Key = table.Column(type: "text", nullable: false), DisplayName = table.Column(type: "text", nullable: false), - CreatedUtc = table.Column(type: "timestamp with time zone", nullable: false), - UpdatedUtc = table.Column(type: "timestamp with time zone", nullable: false) + CreatedTime = table.Column(type: "timestamp with time zone", nullable: false), + LastUpdatedTime = table.Column(type: "timestamp with time zone", nullable: false) }, constraints: table => { @@ -89,8 +90,8 @@ namespace FictionArchive.Service.NovelService.Migrations ExternalId = table.Column(type: "text", nullable: false), NameId = table.Column(type: "bigint", nullable: false), DescriptionId = table.Column(type: "bigint", nullable: false), - CreatedUtc = table.Column(type: "timestamp with time zone", nullable: false), - UpdatedUtc = table.Column(type: "timestamp with time zone", nullable: false) + CreatedTime = table.Column(type: "timestamp with time zone", nullable: false), + LastUpdatedTime = table.Column(type: "timestamp with time zone", nullable: false) }, constraints: table => { @@ -121,6 +122,35 @@ namespace FictionArchive.Service.NovelService.Migrations onDelete: ReferentialAction.Cascade); }); + migrationBuilder.CreateTable( + name: "Tags", + columns: table => new + { + Id = table.Column(type: "bigint", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + Key = table.Column(type: "text", nullable: false), + DisplayNameId = table.Column(type: "bigint", nullable: false), + TagType = table.Column(type: "integer", nullable: false), + SourceId = table.Column(type: "bigint", nullable: true), + CreatedTime = table.Column(type: "timestamp with time zone", nullable: false), + LastUpdatedTime = table.Column(type: "timestamp with time zone", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Tags", x => x.Id); + table.ForeignKey( + name: "FK_Tags_LocalizationKey_DisplayNameId", + column: x => x.DisplayNameId, + principalTable: "LocalizationKey", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_Tags_Sources_SourceId", + column: x => x.SourceId, + principalTable: "Sources", + principalColumn: "Id"); + }); + migrationBuilder.CreateTable( name: "LocalizationText", columns: table => new @@ -131,8 +161,8 @@ namespace FictionArchive.Service.NovelService.Migrations Text = table.Column(type: "text", nullable: false), TranslationEngineId = table.Column(type: "bigint", nullable: true), LocalizationKeyId = table.Column(type: "bigint", nullable: true), - CreatedUtc = table.Column(type: "timestamp with time zone", nullable: false), - UpdatedUtc = table.Column(type: "timestamp with time zone", nullable: false) + CreatedTime = table.Column(type: "timestamp with time zone", nullable: false), + LastUpdatedTime = table.Column(type: "timestamp with time zone", nullable: false) }, constraints: table => { @@ -161,8 +191,8 @@ namespace FictionArchive.Service.NovelService.Migrations NameId = table.Column(type: "bigint", nullable: false), BodyId = table.Column(type: "bigint", nullable: false), NovelId = table.Column(type: "bigint", nullable: true), - CreatedUtc = table.Column(type: "timestamp with time zone", nullable: false), - UpdatedUtc = table.Column(type: "timestamp with time zone", nullable: false) + CreatedTime = table.Column(type: "timestamp with time zone", nullable: false), + LastUpdatedTime = table.Column(type: "timestamp with time zone", nullable: false) }, constraints: table => { @@ -187,38 +217,27 @@ namespace FictionArchive.Service.NovelService.Migrations }); migrationBuilder.CreateTable( - name: "Tags", + name: "NovelNovelTag", columns: table => new { - Id = table.Column(type: "bigint", nullable: false) - .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - Key = table.Column(type: "text", nullable: false), - DisplayNameId = table.Column(type: "bigint", nullable: false), - TagType = table.Column(type: "integer", nullable: false), - SourceId = table.Column(type: "bigint", nullable: true), - NovelId = table.Column(type: "bigint", nullable: true), - CreatedUtc = table.Column(type: "timestamp with time zone", nullable: false), - UpdatedUtc = table.Column(type: "timestamp with time zone", nullable: false) + NovelsId = table.Column(type: "bigint", nullable: false), + TagsId = table.Column(type: "bigint", nullable: false) }, constraints: table => { - table.PrimaryKey("PK_Tags", x => x.Id); + table.PrimaryKey("PK_NovelNovelTag", x => new { x.NovelsId, x.TagsId }); table.ForeignKey( - name: "FK_Tags_LocalizationKey_DisplayNameId", - column: x => x.DisplayNameId, - principalTable: "LocalizationKey", + name: "FK_NovelNovelTag_Novels_NovelsId", + column: x => x.NovelsId, + principalTable: "Novels", principalColumn: "Id", onDelete: ReferentialAction.Cascade); table.ForeignKey( - name: "FK_Tags_Novels_NovelId", - column: x => x.NovelId, - principalTable: "Novels", - principalColumn: "Id"); - table.ForeignKey( - name: "FK_Tags_Sources_SourceId", - column: x => x.SourceId, - principalTable: "Sources", - principalColumn: "Id"); + name: "FK_NovelNovelTag_Tags_TagsId", + column: x => x.TagsId, + principalTable: "Tags", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); }); migrationBuilder.CreateIndex( @@ -246,6 +265,11 @@ namespace FictionArchive.Service.NovelService.Migrations table: "LocalizationText", column: "TranslationEngineId"); + migrationBuilder.CreateIndex( + name: "IX_NovelNovelTag_TagsId", + table: "NovelNovelTag", + column: "TagsId"); + migrationBuilder.CreateIndex( name: "IX_Novels_AuthorId", table: "Novels", @@ -271,11 +295,6 @@ namespace FictionArchive.Service.NovelService.Migrations table: "Tags", column: "DisplayNameId"); - migrationBuilder.CreateIndex( - name: "IX_Tags_NovelId", - table: "Tags", - column: "NovelId"); - migrationBuilder.CreateIndex( name: "IX_Tags_SourceId", table: "Tags", @@ -292,7 +311,7 @@ namespace FictionArchive.Service.NovelService.Migrations name: "LocalizationText"); migrationBuilder.DropTable( - name: "Tags"); + name: "NovelNovelTag"); migrationBuilder.DropTable( name: "TranslationEngines"); @@ -301,11 +320,14 @@ namespace FictionArchive.Service.NovelService.Migrations name: "Novels"); migrationBuilder.DropTable( - name: "LocalizationKey"); + name: "Tags"); migrationBuilder.DropTable( name: "Person"); + migrationBuilder.DropTable( + name: "LocalizationKey"); + migrationBuilder.DropTable( name: "Sources"); } diff --git a/FictionArchive.Service.NovelService/Migrations/NovelServiceDbContextModelSnapshot.cs b/FictionArchive.Service.NovelService/Migrations/NovelServiceDbContextModelSnapshot.cs index 745f440..e9fb627 100644 --- a/FictionArchive.Service.NovelService/Migrations/NovelServiceDbContextModelSnapshot.cs +++ b/FictionArchive.Service.NovelService/Migrations/NovelServiceDbContextModelSnapshot.cs @@ -1,9 +1,9 @@ // -using System; using FictionArchive.Service.NovelService.Services; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using NodaTime; using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; #nullable disable @@ -30,10 +30,10 @@ namespace FictionArchive.Service.NovelService.Migrations NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - b.Property("CreatedUtc") + b.Property("CreatedTime") .HasColumnType("timestamp with time zone"); - b.Property("UpdatedUtc") + b.Property("LastUpdatedTime") .HasColumnType("timestamp with time zone"); b.HasKey("Id"); @@ -49,12 +49,15 @@ namespace FictionArchive.Service.NovelService.Migrations NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - b.Property("CreatedUtc") + b.Property("CreatedTime") .HasColumnType("timestamp with time zone"); b.Property("Language") .HasColumnType("integer"); + b.Property("LastUpdatedTime") + .HasColumnType("timestamp with time zone"); + b.Property("LocalizationKeyId") .HasColumnType("bigint"); @@ -65,9 +68,6 @@ namespace FictionArchive.Service.NovelService.Migrations b.Property("TranslationEngineId") .HasColumnType("bigint"); - b.Property("UpdatedUtc") - .HasColumnType("timestamp with time zone"); - b.HasKey("Id"); b.HasIndex("LocalizationKeyId"); @@ -88,7 +88,10 @@ namespace FictionArchive.Service.NovelService.Migrations b.Property("BodyId") .HasColumnType("bigint"); - b.Property("CreatedUtc") + b.Property("CreatedTime") + .HasColumnType("timestamp with time zone"); + + b.Property("LastUpdatedTime") .HasColumnType("timestamp with time zone"); b.Property("NameId") @@ -103,9 +106,6 @@ namespace FictionArchive.Service.NovelService.Migrations b.Property("Revision") .HasColumnType("bigint"); - b.Property("UpdatedUtc") - .HasColumnType("timestamp with time zone"); - b.Property("Url") .HasColumnType("text"); @@ -131,7 +131,7 @@ namespace FictionArchive.Service.NovelService.Migrations b.Property("AuthorId") .HasColumnType("bigint"); - b.Property("CreatedUtc") + b.Property("CreatedTime") .HasColumnType("timestamp with time zone"); b.Property("DescriptionId") @@ -141,6 +141,9 @@ namespace FictionArchive.Service.NovelService.Migrations .IsRequired() .HasColumnType("text"); + b.Property("LastUpdatedTime") + .HasColumnType("timestamp with time zone"); + b.Property("NameId") .HasColumnType("bigint"); @@ -156,9 +159,6 @@ namespace FictionArchive.Service.NovelService.Migrations b.Property("StatusOverride") .HasColumnType("integer"); - b.Property("UpdatedUtc") - .HasColumnType("timestamp with time zone"); - b.Property("Url") .IsRequired() .HasColumnType("text"); @@ -184,7 +184,7 @@ namespace FictionArchive.Service.NovelService.Migrations NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - b.Property("CreatedUtc") + b.Property("CreatedTime") .HasColumnType("timestamp with time zone"); b.Property("DisplayNameId") @@ -194,15 +194,15 @@ namespace FictionArchive.Service.NovelService.Migrations .IsRequired() .HasColumnType("text"); + b.Property("LastUpdatedTime") + .HasColumnType("timestamp with time zone"); + b.Property("SourceId") .HasColumnType("bigint"); b.Property("TagType") .HasColumnType("integer"); - b.Property("UpdatedUtc") - .HasColumnType("timestamp with time zone"); - b.HasKey("Id"); b.HasIndex("DisplayNameId"); @@ -220,19 +220,19 @@ namespace FictionArchive.Service.NovelService.Migrations NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - b.Property("CreatedUtc") + b.Property("CreatedTime") .HasColumnType("timestamp with time zone"); b.Property("ExternalUrl") .HasColumnType("text"); + b.Property("LastUpdatedTime") + .HasColumnType("timestamp with time zone"); + b.Property("Name") .IsRequired() .HasColumnType("text"); - b.Property("UpdatedUtc") - .HasColumnType("timestamp with time zone"); - b.HasKey("Id"); b.ToTable("Person"); @@ -246,20 +246,20 @@ namespace FictionArchive.Service.NovelService.Migrations NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - b.Property("CreatedUtc") + b.Property("CreatedTime") .HasColumnType("timestamp with time zone"); b.Property("Key") .IsRequired() .HasColumnType("text"); + b.Property("LastUpdatedTime") + .HasColumnType("timestamp with time zone"); + b.Property("Name") .IsRequired() .HasColumnType("text"); - b.Property("UpdatedUtc") - .HasColumnType("timestamp with time zone"); - b.Property("Url") .IsRequired() .HasColumnType("text"); @@ -277,7 +277,7 @@ namespace FictionArchive.Service.NovelService.Migrations NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - b.Property("CreatedUtc") + b.Property("CreatedTime") .HasColumnType("timestamp with time zone"); b.Property("DisplayName") @@ -288,7 +288,7 @@ namespace FictionArchive.Service.NovelService.Migrations .IsRequired() .HasColumnType("text"); - b.Property("UpdatedUtc") + b.Property("LastUpdatedTime") .HasColumnType("timestamp with time zone"); b.HasKey("Id"); diff --git a/FictionArchive.Service.NovelService/Models/Localization/LocalizationKey.cs b/FictionArchive.Service.NovelService/Models/Localization/LocalizationKey.cs index fcb8b46..7edea60 100644 --- a/FictionArchive.Service.NovelService/Models/Localization/LocalizationKey.cs +++ b/FictionArchive.Service.NovelService/Models/Localization/LocalizationKey.cs @@ -1,5 +1,6 @@ using FictionArchive.Common.Enums; using FictionArchive.Service.NovelService.Models.Novels; +using FictionArchive.Service.Shared.Models; using Microsoft.EntityFrameworkCore; namespace FictionArchive.Service.NovelService.Models.Localization; diff --git a/FictionArchive.Service.NovelService/Models/Localization/LocalizationText.cs b/FictionArchive.Service.NovelService/Models/Localization/LocalizationText.cs index 216e20b..431e09b 100644 --- a/FictionArchive.Service.NovelService/Models/Localization/LocalizationText.cs +++ b/FictionArchive.Service.NovelService/Models/Localization/LocalizationText.cs @@ -1,5 +1,6 @@ using FictionArchive.Common.Enums; using FictionArchive.Service.NovelService.Models.Novels; +using FictionArchive.Service.Shared.Models; namespace FictionArchive.Service.NovelService.Models.Localization; diff --git a/FictionArchive.Service.NovelService/Models/Novels/BaseEntity.cs b/FictionArchive.Service.NovelService/Models/Novels/BaseEntity.cs deleted file mode 100644 index 626077e..0000000 --- a/FictionArchive.Service.NovelService/Models/Novels/BaseEntity.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace FictionArchive.Service.NovelService.Models.Novels; - -public abstract class BaseEntity -{ - public uint Id { get; set; } - public DateTime CreatedUtc { get; set; } - public DateTime UpdatedUtc { get; set; } -} \ No newline at end of file diff --git a/FictionArchive.Service.NovelService/Models/Novels/Chapter.cs b/FictionArchive.Service.NovelService/Models/Novels/Chapter.cs index 2dde365..e59705d 100644 --- a/FictionArchive.Service.NovelService/Models/Novels/Chapter.cs +++ b/FictionArchive.Service.NovelService/Models/Novels/Chapter.cs @@ -1,4 +1,5 @@ using FictionArchive.Service.NovelService.Models.Localization; +using FictionArchive.Service.Shared.Models; namespace FictionArchive.Service.NovelService.Models.Novels; diff --git a/FictionArchive.Service.NovelService/Models/Novels/Novel.cs b/FictionArchive.Service.NovelService/Models/Novels/Novel.cs index 17aed48..81d5ca7 100644 --- a/FictionArchive.Service.NovelService/Models/Novels/Novel.cs +++ b/FictionArchive.Service.NovelService/Models/Novels/Novel.cs @@ -1,5 +1,6 @@ using FictionArchive.Common.Enums; using FictionArchive.Service.NovelService.Models.Localization; +using FictionArchive.Service.Shared.Models; using NovelStatus = FictionArchive.Service.NovelService.Models.Enums.NovelStatus; namespace FictionArchive.Service.NovelService.Models.Novels; diff --git a/FictionArchive.Service.NovelService/Models/Novels/NovelTag.cs b/FictionArchive.Service.NovelService/Models/Novels/NovelTag.cs index 48625fb..1a9cf07 100644 --- a/FictionArchive.Service.NovelService/Models/Novels/NovelTag.cs +++ b/FictionArchive.Service.NovelService/Models/Novels/NovelTag.cs @@ -1,5 +1,6 @@ using FictionArchive.Service.NovelService.Models.Enums; using FictionArchive.Service.NovelService.Models.Localization; +using FictionArchive.Service.Shared.Models; namespace FictionArchive.Service.NovelService.Models.Novels; diff --git a/FictionArchive.Service.NovelService/Models/Novels/Person.cs b/FictionArchive.Service.NovelService/Models/Novels/Person.cs index 8e7d734..0a13dd0 100644 --- a/FictionArchive.Service.NovelService/Models/Novels/Person.cs +++ b/FictionArchive.Service.NovelService/Models/Novels/Person.cs @@ -1,7 +1,10 @@ +using FictionArchive.Service.NovelService.Models.Localization; +using FictionArchive.Service.Shared.Models; + namespace FictionArchive.Service.NovelService.Models.Novels; public class Person : BaseEntity { - public string Name { get; set; } + public LocalizationKey Name { get; set; } public string? ExternalUrl { get; set; } } \ No newline at end of file diff --git a/FictionArchive.Service.NovelService/Models/Novels/Source.cs b/FictionArchive.Service.NovelService/Models/Novels/Source.cs index 2dd82c6..e65619d 100644 --- a/FictionArchive.Service.NovelService/Models/Novels/Source.cs +++ b/FictionArchive.Service.NovelService/Models/Novels/Source.cs @@ -1,3 +1,5 @@ +using FictionArchive.Service.Shared.Models; + namespace FictionArchive.Service.NovelService.Models.Novels; public class Source : BaseEntity diff --git a/FictionArchive.Service.NovelService/Models/Novels/SourceConfiguration.cs b/FictionArchive.Service.NovelService/Models/Novels/SourceConfiguration.cs index 7bd1da7..5a3037d 100644 --- a/FictionArchive.Service.NovelService/Models/Novels/SourceConfiguration.cs +++ b/FictionArchive.Service.NovelService/Models/Novels/SourceConfiguration.cs @@ -1,5 +1,6 @@ using System.ComponentModel.DataAnnotations.Schema; using FictionArchive.Service.NovelService.Models.Novels; +using FictionArchive.Service.Shared.Models; namespace FictionArchive.Service.NovelService.Models; diff --git a/FictionArchive.Service.NovelService/Models/Novels/TranslationEngine.cs b/FictionArchive.Service.NovelService/Models/Novels/TranslationEngine.cs index 8b450ab..755e07d 100644 --- a/FictionArchive.Service.NovelService/Models/Novels/TranslationEngine.cs +++ b/FictionArchive.Service.NovelService/Models/Novels/TranslationEngine.cs @@ -1,3 +1,5 @@ +using FictionArchive.Service.Shared.Models; + namespace FictionArchive.Service.NovelService.Models.Novels; public class TranslationEngine : BaseEntity diff --git a/FictionArchive.Service.NovelService/Program.cs b/FictionArchive.Service.NovelService/Program.cs index cd2a55c..290fb1b 100644 --- a/FictionArchive.Service.NovelService/Program.cs +++ b/FictionArchive.Service.NovelService/Program.cs @@ -2,6 +2,7 @@ using FictionArchive.Service.NovelService.GraphQL; using FictionArchive.Service.NovelService.Services; using FictionArchive.Service.NovelService.Services.SourceAdapters; using FictionArchive.Service.NovelService.Services.SourceAdapters.Novelpia; +using FictionArchive.Service.Shared.Extensions; using FictionArchive.Service.Shared.Services.GraphQL; using Microsoft.EntityFrameworkCore; @@ -16,24 +17,14 @@ public class Program builder.Services.AddMemoryCache(); #region GraphQL - - builder.Services.AddGraphQLServer() - .AddQueryType() - .AddMutationType() - .AddType() - .AddMutationConventions(applyToAllMutations: true) - .AddFiltering(opt => opt.AddDefaults().BindRuntimeType()) - .AddSorting() - .AddProjections(); + + builder.Services.AddDefaultGraphQl(); #endregion #region Database - builder.Services.AddDbContext(opt => - { - opt.UseNpgsql(builder.Configuration.GetConnectionString("DefaultConnection")); - }); + builder.Services.RegisterDbContext(builder.Configuration.GetConnectionString("DefaultConnection")); #endregion diff --git a/FictionArchive.Service.NovelService/Services/NovelServiceDbContext.cs b/FictionArchive.Service.NovelService/Services/NovelServiceDbContext.cs index 12f5be2..6513c92 100644 --- a/FictionArchive.Service.NovelService/Services/NovelServiceDbContext.cs +++ b/FictionArchive.Service.NovelService/Services/NovelServiceDbContext.cs @@ -1,33 +1,14 @@ using FictionArchive.Service.NovelService.Models.Novels; +using FictionArchive.Service.Shared.Services.Database; using Microsoft.EntityFrameworkCore; namespace FictionArchive.Service.NovelService.Services; public class NovelServiceDbContext(DbContextOptions options, ILogger logger) - : DbContext(options) + : FictionArchiveDbContext(options, logger) { public DbSet Novels { get; set; } public DbSet Sources { get; set; } public DbSet TranslationEngines { get; set; } public DbSet Tags { get; set; } - - private readonly ILogger _logger = logger; - - public void UpdateDatabase() - { - IEnumerable pendingMigrations = Database.GetPendingMigrations(); - if (!pendingMigrations.Any()) - { - _logger.LogDebug("No pending migrations found, continuing."); - return; - } - - foreach (string migration in pendingMigrations) - { - _logger.LogInformation("Found pending migration with name {migrationName}.", migration); - } - _logger.LogInformation("Attempting to apply pending migrations..."); - Database.Migrate(); - _logger.LogInformation("Migrations applied."); - } } \ No newline at end of file diff --git a/FictionArchive.Service.Shared/DatabaseExtensions.cs b/FictionArchive.Service.Shared/DatabaseExtensions.cs new file mode 100644 index 0000000..dd38896 --- /dev/null +++ b/FictionArchive.Service.Shared/DatabaseExtensions.cs @@ -0,0 +1,19 @@ +using FictionArchive.Service.Shared.Services.Database; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; + +namespace FictionArchive.Service.Shared; + +public static class DatabaseExtensions +{ + public static IServiceCollection RegisterDbContext(this IServiceCollection services, Action optionConfiguration) + where TContext : FictionArchiveDbContext + { + services.AddDbContext(opt => + { + + optionConfiguration?.Invoke(opt); + }); + return services; + } +} \ No newline at end of file diff --git a/FictionArchive.Service.Shared/Extensions/DatabaseExtensions.cs b/FictionArchive.Service.Shared/Extensions/DatabaseExtensions.cs new file mode 100644 index 0000000..c373f51 --- /dev/null +++ b/FictionArchive.Service.Shared/Extensions/DatabaseExtensions.cs @@ -0,0 +1,21 @@ +using FictionArchive.Service.Shared.Services.Database; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; + +namespace FictionArchive.Service.Shared.Extensions; + +public static class DatabaseExtensions +{ + public static IServiceCollection RegisterDbContext(this IServiceCollection services, + string connectionString) where TContext : FictionArchiveDbContext + { + services.AddDbContext(options => + { + options.UseNpgsql(connectionString, o => + { + o.UseNodaTime(); + }); + }); + return services; + } +} \ No newline at end of file diff --git a/FictionArchive.Service.Shared/Extensions/GraphQLExtensions.cs b/FictionArchive.Service.Shared/Extensions/GraphQLExtensions.cs new file mode 100644 index 0000000..ccd1608 --- /dev/null +++ b/FictionArchive.Service.Shared/Extensions/GraphQLExtensions.cs @@ -0,0 +1,22 @@ +using FictionArchive.Service.Shared.Services.GraphQL; +using HotChocolate.Execution.Configuration; +using HotChocolate.Types.NodaTime; +using Microsoft.Extensions.DependencyInjection; + +namespace FictionArchive.Service.Shared.Extensions; + +public static class GraphQLExtensions +{ + public static IRequestExecutorBuilder AddDefaultGraphQl(this IServiceCollection services) where TQuery : class where TMutation : class + { + return services.AddGraphQLServer() + .AddQueryType() + .AddMutationType() + .AddType() + .AddType() + .AddMutationConventions(applyToAllMutations: true) + .AddFiltering(opt => opt.AddDefaults().BindRuntimeType()) + .AddSorting() + .AddProjections(); + } +} \ No newline at end of file diff --git a/FictionArchive.Service.Shared/FictionArchive.Service.Shared.csproj b/FictionArchive.Service.Shared/FictionArchive.Service.Shared.csproj index 413ef24..f7cb209 100644 --- a/FictionArchive.Service.Shared/FictionArchive.Service.Shared.csproj +++ b/FictionArchive.Service.Shared/FictionArchive.Service.Shared.csproj @@ -7,19 +7,32 @@ + + all runtime; build; native; contentfiles; analyzers; buildtransitive + all runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + diff --git a/FictionArchive.Service.Shared/Models/BaseEntity.cs b/FictionArchive.Service.Shared/Models/BaseEntity.cs new file mode 100644 index 0000000..3a8f6fc --- /dev/null +++ b/FictionArchive.Service.Shared/Models/BaseEntity.cs @@ -0,0 +1,11 @@ +using FictionArchive.Service.Shared.Models.Interfaces; +using NodaTime; + +namespace FictionArchive.Service.Shared.Models; + +public abstract class BaseEntity : IAuditable +{ + public uint Id { get; set; } + public Instant CreatedTime { get; set; } + public Instant LastUpdatedTime { get; set; } +} \ No newline at end of file diff --git a/FictionArchive.Service.Shared/Models/Interfaces/IAuditable.cs b/FictionArchive.Service.Shared/Models/Interfaces/IAuditable.cs new file mode 100644 index 0000000..3dfd5e2 --- /dev/null +++ b/FictionArchive.Service.Shared/Models/Interfaces/IAuditable.cs @@ -0,0 +1,9 @@ +using NodaTime; + +namespace FictionArchive.Service.Shared.Models.Interfaces; + +public interface IAuditable +{ + public Instant CreatedTime { get; set; } + public Instant LastUpdatedTime { get; set; } +} \ No newline at end of file diff --git a/FictionArchive.Service.Shared/Services/Database/AuditInterceptor.cs b/FictionArchive.Service.Shared/Services/Database/AuditInterceptor.cs new file mode 100644 index 0000000..4e6fa70 --- /dev/null +++ b/FictionArchive.Service.Shared/Services/Database/AuditInterceptor.cs @@ -0,0 +1,38 @@ +using FictionArchive.Service.Shared.Models.Interfaces; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Diagnostics; +using NodaTime; + +namespace FictionArchive.Service.Shared.Services.Database; + +public class AuditInterceptor : SaveChangesInterceptor +{ + public override InterceptionResult SavingChanges( + DbContextEventData eventData, + InterceptionResult result) + { + var context = eventData.Context; + + if (context == null) + return base.SavingChanges(eventData, result); + + var entries = context.ChangeTracker.Entries(); + + var now = Instant.FromDateTimeUtc(DateTime.UtcNow); + + foreach (var e in entries) + { + if (e.State == EntityState.Added) + { + e.Entity.CreatedTime = now; + e.Entity.LastUpdatedTime = now; + } + else if (e.State == EntityState.Modified) + { + e.Entity.LastUpdatedTime = now; + } + } + + return base.SavingChanges(eventData, result); + } +} diff --git a/FictionArchive.Service.Shared/Services/Database/FictionArchiveDbContext.cs b/FictionArchive.Service.Shared/Services/Database/FictionArchiveDbContext.cs new file mode 100644 index 0000000..4c6b1b2 --- /dev/null +++ b/FictionArchive.Service.Shared/Services/Database/FictionArchiveDbContext.cs @@ -0,0 +1,41 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; + +namespace FictionArchive.Service.Shared.Services.Database; + +/// +/// Abstract DbContext handling boilerplate shared between our contexts. Should not share actual data. +/// +public abstract class FictionArchiveDbContext : DbContext +{ + protected readonly ILogger _logger; + + protected FictionArchiveDbContext(DbContextOptions options, ILogger logger) : base(options) + { + _logger = logger; + } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + optionsBuilder.AddInterceptors(new AuditInterceptor()); + base.OnConfiguring(optionsBuilder); + } + + public void UpdateDatabase() + { + IEnumerable pendingMigrations = Database.GetPendingMigrations(); + if (!pendingMigrations.Any()) + { + _logger.LogDebug("No pending migrations found, continuing."); + return; + } + + foreach (string migration in pendingMigrations) + { + _logger.LogInformation("Found pending migration with name {migrationName}.", migration); + } + _logger.LogInformation("Attempting to apply pending migrations..."); + Database.Migrate(); + _logger.LogInformation("Migrations applied."); + } +} \ No newline at end of file diff --git a/FictionArchive.Service.Shared/Services/FictionArchiveDbContext.cs b/FictionArchive.Service.Shared/Services/FictionArchiveDbContext.cs deleted file mode 100644 index f5c3698..0000000 --- a/FictionArchive.Service.Shared/Services/FictionArchiveDbContext.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Logging; - -namespace FictionArchive.Service.Shared.Services; - -/// -/// Abstract DbContext handling boilerplate shared between our contexts. Should not share actual data. -/// -public abstract class FictionArchiveDbContext : DbContext -{ - protected readonly ILogger _logger; - - protected FictionArchiveDbContext(DbContextOptions options, ILogger logger) : base(options) - { - _logger = logger; - } -} \ No newline at end of file diff --git a/FictionArchive.Service.TranslationService/FictionArchive.Service.TranslationService.csproj b/FictionArchive.Service.TranslationService/FictionArchive.Service.TranslationService.csproj index 77a3c6f..cd4d3c3 100644 --- a/FictionArchive.Service.TranslationService/FictionArchive.Service.TranslationService.csproj +++ b/FictionArchive.Service.TranslationService/FictionArchive.Service.TranslationService.csproj @@ -29,6 +29,9 @@ .dockerignore + + Always + diff --git a/FictionArchive.Service.TranslationService/GraphQL/Mutation.cs b/FictionArchive.Service.TranslationService/GraphQL/Mutation.cs index fde7ac6..46a2bd2 100644 --- a/FictionArchive.Service.TranslationService/GraphQL/Mutation.cs +++ b/FictionArchive.Service.TranslationService/GraphQL/Mutation.cs @@ -1,14 +1,30 @@ using FictionArchive.Common.Enums; +using FictionArchive.Service.TranslationService.Models.Database; +using FictionArchive.Service.TranslationService.Models.Enums; +using FictionArchive.Service.TranslationService.Services.Database; using FictionArchive.Service.TranslationService.Services.TranslationEngines; namespace FictionArchive.Service.TranslationService.GraphQL; public class Mutation { - public async Task TranslateText(string text, Language from, Language to, string translationEngineKey, IEnumerable translationEngines) + public async Task TranslateText(string text, Language from, Language to, string translationEngineKey, IEnumerable translationEngines, TranslationServiceDbContext dbContext) { var engine = translationEngines.FirstOrDefault(engine => engine.Descriptor.Key == translationEngineKey); var translation = await engine.GetTranslation(text, from, to); + + dbContext.TranslationRequests.Add(new TranslationRequest() + { + OriginalText = text, + BilledCharacterCount = 0, // FILL ME + From = from, + To = to, + Status = translation != null ? TranslationRequestStatus.Success : TranslationRequestStatus.Failed, + TranslatedText = translation, + TranslationEngineKey = translationEngineKey + }); + await dbContext.SaveChangesAsync(); + return translation; } } \ No newline at end of file diff --git a/FictionArchive.Service.TranslationService/GraphQL/Query.cs b/FictionArchive.Service.TranslationService/GraphQL/Query.cs index d4737bf..bab091a 100644 --- a/FictionArchive.Service.TranslationService/GraphQL/Query.cs +++ b/FictionArchive.Service.TranslationService/GraphQL/Query.cs @@ -7,7 +7,7 @@ public class Query { [UseFiltering] [UseSorting] - public IEnumerable GetTranslationEngines(IEnumerable engines) + public IEnumerable GetTranslationEngines(IEnumerable engines) { return engines.Select(engine => engine.Descriptor); } diff --git a/FictionArchive.Service.TranslationService/Migrations/20251118052322_Initial.Designer.cs b/FictionArchive.Service.TranslationService/Migrations/20251118052322_Initial.Designer.cs new file mode 100644 index 0000000..e0cb23b --- /dev/null +++ b/FictionArchive.Service.TranslationService/Migrations/20251118052322_Initial.Designer.cs @@ -0,0 +1,72 @@ +// +using FictionArchive.Service.TranslationService.Services.Database; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using NodaTime; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace FictionArchive.Service.TranslationService.Migrations +{ + [DbContext(typeof(TranslationServiceDbContext))] + [Migration("20251118052322_Initial")] + partial class Initial + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("FictionArchive.Service.TranslationService.Models.Database.TranslationRequest", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("BilledCharacterCount") + .HasColumnType("bigint"); + + b.Property("CreatedTime") + .HasColumnType("timestamp with time zone"); + + b.Property("From") + .HasColumnType("integer"); + + b.Property("LastUpdatedTime") + .HasColumnType("timestamp with time zone"); + + b.Property("OriginalText") + .IsRequired() + .HasColumnType("text"); + + b.Property("Status") + .HasColumnType("integer"); + + b.Property("To") + .HasColumnType("integer"); + + b.Property("TranslatedText") + .HasColumnType("text"); + + b.Property("TranslationEngineKey") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("TranslationRequests"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/FictionArchive.Service.TranslationService/Migrations/20251118052322_Initial.cs b/FictionArchive.Service.TranslationService/Migrations/20251118052322_Initial.cs new file mode 100644 index 0000000..4018158 --- /dev/null +++ b/FictionArchive.Service.TranslationService/Migrations/20251118052322_Initial.cs @@ -0,0 +1,44 @@ +using Microsoft.EntityFrameworkCore.Migrations; +using NodaTime; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace FictionArchive.Service.TranslationService.Migrations +{ + /// + public partial class Initial : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "TranslationRequests", + columns: table => new + { + Id = table.Column(type: "bigint", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + OriginalText = table.Column(type: "text", nullable: false), + TranslatedText = table.Column(type: "text", nullable: true), + From = table.Column(type: "integer", nullable: false), + To = table.Column(type: "integer", nullable: false), + TranslationEngineKey = table.Column(type: "text", nullable: false), + Status = table.Column(type: "integer", nullable: false), + BilledCharacterCount = table.Column(type: "bigint", nullable: false), + CreatedTime = table.Column(type: "timestamp with time zone", nullable: false), + LastUpdatedTime = table.Column(type: "timestamp with time zone", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_TranslationRequests", x => x.Id); + }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "TranslationRequests"); + } + } +} diff --git a/FictionArchive.Service.TranslationService/Migrations/TranslationServiceDbContextModelSnapshot.cs b/FictionArchive.Service.TranslationService/Migrations/TranslationServiceDbContextModelSnapshot.cs new file mode 100644 index 0000000..d06ce38 --- /dev/null +++ b/FictionArchive.Service.TranslationService/Migrations/TranslationServiceDbContextModelSnapshot.cs @@ -0,0 +1,69 @@ +// +using FictionArchive.Service.TranslationService.Services.Database; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using NodaTime; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace FictionArchive.Service.TranslationService.Migrations +{ + [DbContext(typeof(TranslationServiceDbContext))] + partial class TranslationServiceDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("FictionArchive.Service.TranslationService.Models.Database.TranslationRequest", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("BilledCharacterCount") + .HasColumnType("bigint"); + + b.Property("CreatedTime") + .HasColumnType("timestamp with time zone"); + + b.Property("From") + .HasColumnType("integer"); + + b.Property("LastUpdatedTime") + .HasColumnType("timestamp with time zone"); + + b.Property("OriginalText") + .IsRequired() + .HasColumnType("text"); + + b.Property("Status") + .HasColumnType("integer"); + + b.Property("To") + .HasColumnType("integer"); + + b.Property("TranslatedText") + .HasColumnType("text"); + + b.Property("TranslationEngineKey") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("TranslationRequests"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/FictionArchive.Service.TranslationService/Models/Database/TranslationRequest.cs b/FictionArchive.Service.TranslationService/Models/Database/TranslationRequest.cs new file mode 100644 index 0000000..7b0a0ec --- /dev/null +++ b/FictionArchive.Service.TranslationService/Models/Database/TranslationRequest.cs @@ -0,0 +1,16 @@ +using FictionArchive.Common.Enums; +using FictionArchive.Service.Shared.Models; +using FictionArchive.Service.TranslationService.Models.Enums; + +namespace FictionArchive.Service.TranslationService.Models.Database; + +public class TranslationRequest : BaseEntity +{ + public required string OriginalText { get; set; } + public string? TranslatedText { get; set; } + public Language From { get; set; } + public Language To { get; set; } + public required string TranslationEngineKey { get; set; } + public TranslationRequestStatus Status { get; set; } + public uint BilledCharacterCount { get; set; } +} \ No newline at end of file diff --git a/FictionArchive.Service.TranslationService/Models/Enums/TranslationRequestStatus.cs b/FictionArchive.Service.TranslationService/Models/Enums/TranslationRequestStatus.cs new file mode 100644 index 0000000..ea91e92 --- /dev/null +++ b/FictionArchive.Service.TranslationService/Models/Enums/TranslationRequestStatus.cs @@ -0,0 +1,8 @@ +namespace FictionArchive.Service.TranslationService.Models.Enums; + +public enum TranslationRequestStatus +{ + Failed = -1, + Pending = 0, + Success = 1, +} \ No newline at end of file diff --git a/FictionArchive.Service.TranslationService/Program.cs b/FictionArchive.Service.TranslationService/Program.cs index fc20d89..73763d5 100644 --- a/FictionArchive.Service.TranslationService/Program.cs +++ b/FictionArchive.Service.TranslationService/Program.cs @@ -1,6 +1,9 @@ using DeepL; +using FictionArchive.Common.Extensions; +using FictionArchive.Service.Shared.Extensions; using FictionArchive.Service.Shared.Services.GraphQL; using FictionArchive.Service.TranslationService.GraphQL; +using FictionArchive.Service.TranslationService.Services.Database; using FictionArchive.Service.TranslationService.Services.TranslationEngines; using FictionArchive.Service.TranslationService.Services.TranslationEngines.DeepLTranslate; @@ -11,19 +14,19 @@ public class Program public static void Main(string[] args) { var builder = WebApplication.CreateBuilder(args); + builder.AddLocalAppsettings(); builder.Services.AddHealthChecks(); + #region Database + + builder.Services.RegisterDbContext(builder.Configuration.GetConnectionString("DefaultConnection")); + + #endregion + #region GraphQL - builder.Services.AddGraphQLServer() - .AddQueryType() - .AddMutationType() - .AddType() - .AddMutationConventions(applyToAllMutations: true) - .AddFiltering(opt => opt.AddDefaults().BindRuntimeType()) - .AddSorting() - .AddProjections(); + builder.Services.AddDefaultGraphQl(); #endregion @@ -33,7 +36,7 @@ public class Program { return new DeepLClient(builder.Configuration["DeepL:ApiKey"]); }); - builder.Services.AddTransient(); + builder.Services.AddTransient(); #endregion diff --git a/FictionArchive.Service.TranslationService/Services/Database/TranslationServiceDbContext.cs b/FictionArchive.Service.TranslationService/Services/Database/TranslationServiceDbContext.cs new file mode 100644 index 0000000..8b7c044 --- /dev/null +++ b/FictionArchive.Service.TranslationService/Services/Database/TranslationServiceDbContext.cs @@ -0,0 +1,14 @@ +using FictionArchive.Service.Shared.Services.Database; +using FictionArchive.Service.TranslationService.Models.Database; +using Microsoft.EntityFrameworkCore; + +namespace FictionArchive.Service.TranslationService.Services.Database; + +public class TranslationServiceDbContext : FictionArchiveDbContext +{ + public DbSet TranslationRequests { get; set; } + + public TranslationServiceDbContext(DbContextOptions options, ILogger logger) : base(options, logger) + { + } +} \ No newline at end of file diff --git a/FictionArchive.Service.TranslationService/Services/TranslationEngines/DeepLTranslate/DeepLTranslationAdapater.cs b/FictionArchive.Service.TranslationService/Services/TranslationEngines/DeepLTranslate/DeepLTranslationEngine.cs similarity index 79% rename from FictionArchive.Service.TranslationService/Services/TranslationEngines/DeepLTranslate/DeepLTranslationAdapater.cs rename to FictionArchive.Service.TranslationService/Services/TranslationEngines/DeepLTranslate/DeepLTranslationEngine.cs index af6b189..195f817 100644 --- a/FictionArchive.Service.TranslationService/Services/TranslationEngines/DeepLTranslate/DeepLTranslationAdapater.cs +++ b/FictionArchive.Service.TranslationService/Services/TranslationEngines/DeepLTranslate/DeepLTranslationEngine.cs @@ -5,15 +5,15 @@ using Language = FictionArchive.Common.Enums.Language; namespace FictionArchive.Service.TranslationService.Services.TranslationEngines.DeepLTranslate; -public class DeepLTranslationAdapater : ITranslationEngineAdapter +public class DeepLTranslationEngine : ITranslationEngine { private readonly DeepLClient _deepLClient; - private readonly ILogger _logger; + private readonly ILogger _logger; private const string DisplayName = "DeepL"; private const string Key = "deepl"; - public DeepLTranslationAdapater(DeepLClient deepLClient, ILogger logger) + public DeepLTranslationEngine(DeepLClient deepLClient, ILogger logger) { _deepLClient = deepLClient; _logger = logger; @@ -45,7 +45,8 @@ public class DeepLTranslationAdapater : ITranslationEngineAdapter Language.En => LanguageCode.EnglishAmerican, Language.Kr => LanguageCode.Korean, Language.Ch => LanguageCode.Chinese, - Language.Ja => LanguageCode.Japanese + Language.Ja => LanguageCode.Japanese, + _ => throw new ArgumentOutOfRangeException(nameof(language), language, null) }; } } \ No newline at end of file diff --git a/FictionArchive.Service.TranslationService/Services/TranslationEngines/ITranslationEngineAdapter.cs b/FictionArchive.Service.TranslationService/Services/TranslationEngines/ITranslationEngine.cs similarity index 88% rename from FictionArchive.Service.TranslationService/Services/TranslationEngines/ITranslationEngineAdapter.cs rename to FictionArchive.Service.TranslationService/Services/TranslationEngines/ITranslationEngine.cs index af9d71c..a494fb9 100644 --- a/FictionArchive.Service.TranslationService/Services/TranslationEngines/ITranslationEngineAdapter.cs +++ b/FictionArchive.Service.TranslationService/Services/TranslationEngines/ITranslationEngine.cs @@ -3,7 +3,7 @@ using FictionArchive.Service.TranslationService.Models; namespace FictionArchive.Service.TranslationService.Services.TranslationEngines; -public interface ITranslationEngineAdapter +public interface ITranslationEngine { public TranslationEngineDescriptor Descriptor { get; } public Task GetTranslation(string body, Language from, Language to); diff --git a/FictionArchive.Service.TranslationService/appsettings.json b/FictionArchive.Service.TranslationService/appsettings.json index 138752d..049c26f 100644 --- a/FictionArchive.Service.TranslationService/appsettings.json +++ b/FictionArchive.Service.TranslationService/appsettings.json @@ -8,5 +8,8 @@ "DeepL": { "ApiKey": "REPLACE_ME" }, + "ConnectionStrings": { + "DefaultConnection": "Host=localhost;Database=FictionArchive_NovelService;Username=postgres;password=postgres" + }, "AllowedHosts": "*" }