Updated translation service and finished splitting out responsibilities for now

This commit is contained in:
gamer147
2025-11-18 10:07:23 -05:00
parent 3bb8f7f158
commit 0c1705ebe1
48 changed files with 617 additions and 1134 deletions

4
.gitignore vendored
View File

@@ -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

View File

@@ -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())

View File

@@ -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;
}
}

View File

@@ -7,8 +7,16 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="HotChocolate.Abstractions" Version="15.1.11" />
<PackageReference Include="HotChocolate.Types" Version="15.1.11" />
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="9.0.11" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="9.0.11" />
<PackageReference Include="NodaTime" Version="3.2.2" />
</ItemGroup>
<ItemGroup>
<Reference Include="Microsoft.Extensions.Configuration.Abstractions" />
<Reference Include="Microsoft.Extensions.Hosting.Abstractions">
<HintPath>..\..\..\..\..\..\Program Files\dotnet\shared\Microsoft.AspNetCore.App\8.0.15\Microsoft.Extensions.Hosting.Abstractions.dll</HintPath>
</Reference>
</ItemGroup>
</Project>

View File

@@ -8,19 +8,6 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="HotChocolate.AspNetCore" Version="15.1.11" />
<PackageReference Include="HotChocolate.Data.EntityFramework" Version="15.1.11" />
<PackageReference Include="HotChocolate.Types.Scalars" Version="15.1.11" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.11" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.11">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="9.0.11">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.4" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.6.2"/>
</ItemGroup>
@@ -36,7 +23,6 @@
</ItemGroup>
<ItemGroup>
<Folder Include="Models\Interfaces\" />
<Folder Include="Services\GraphQL\" />
</ItemGroup>

View File

@@ -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,

View File

@@ -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;

View File

@@ -1,409 +0,0 @@
// <auto-generated />
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
{
/// <inheritdoc />
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<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<DateTime>("CreatedUtc")
.HasColumnType("timestamp with time zone");
b.Property<DateTime>("UpdatedUtc")
.HasColumnType("timestamp with time zone");
b.HasKey("Id");
b.ToTable("LocalizationKey");
});
modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Localization.LocalizationText", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<DateTime>("CreatedUtc")
.HasColumnType("timestamp with time zone");
b.Property<int>("Language")
.HasColumnType("integer");
b.Property<long?>("LocalizationKeyId")
.HasColumnType("bigint");
b.Property<string>("Text")
.IsRequired()
.HasColumnType("text");
b.Property<long?>("TranslationEngineId")
.HasColumnType("bigint");
b.Property<DateTime>("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<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<long>("BodyId")
.HasColumnType("bigint");
b.Property<DateTime>("CreatedUtc")
.HasColumnType("timestamp with time zone");
b.Property<long>("NameId")
.HasColumnType("bigint");
b.Property<long?>("NovelId")
.HasColumnType("bigint");
b.Property<long>("Order")
.HasColumnType("bigint");
b.Property<long>("Revision")
.HasColumnType("bigint");
b.Property<DateTime>("UpdatedUtc")
.HasColumnType("timestamp with time zone");
b.Property<string>("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<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<long>("AuthorId")
.HasColumnType("bigint");
b.Property<DateTime>("CreatedUtc")
.HasColumnType("timestamp with time zone");
b.Property<long>("DescriptionId")
.HasColumnType("bigint");
b.Property<string>("ExternalId")
.IsRequired()
.HasColumnType("text");
b.Property<long>("NameId")
.HasColumnType("bigint");
b.Property<int>("RawLanguage")
.HasColumnType("integer");
b.Property<int>("RawStatus")
.HasColumnType("integer");
b.Property<long>("SourceId")
.HasColumnType("bigint");
b.Property<int?>("StatusOverride")
.HasColumnType("integer");
b.Property<DateTime>("UpdatedUtc")
.HasColumnType("timestamp with time zone");
b.Property<string>("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<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<DateTime>("CreatedUtc")
.HasColumnType("timestamp with time zone");
b.Property<long>("DisplayNameId")
.HasColumnType("bigint");
b.Property<string>("Key")
.IsRequired()
.HasColumnType("text");
b.Property<long?>("NovelId")
.HasColumnType("bigint");
b.Property<long?>("SourceId")
.HasColumnType("bigint");
b.Property<int>("TagType")
.HasColumnType("integer");
b.Property<DateTime>("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<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<DateTime>("CreatedUtc")
.HasColumnType("timestamp with time zone");
b.Property<string>("ExternalUrl")
.HasColumnType("text");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<DateTime>("UpdatedUtc")
.HasColumnType("timestamp with time zone");
b.HasKey("Id");
b.ToTable("Person");
});
modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Novels.Source", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<DateTime>("CreatedUtc")
.HasColumnType("timestamp with time zone");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<DateTime>("UpdatedUtc")
.HasColumnType("timestamp with time zone");
b.Property<string>("Url")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.ToTable("Sources");
});
modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Novels.TranslationEngine", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<DateTime>("CreatedUtc")
.HasColumnType("timestamp with time zone");
b.Property<string>("DisplayName")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Key")
.IsRequired()
.HasColumnType("text");
b.Property<DateTime>("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
}
}
}

View File

@@ -1,413 +0,0 @@
// <auto-generated />
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
{
/// <inheritdoc />
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<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<DateTime>("CreatedUtc")
.HasColumnType("timestamp with time zone");
b.Property<DateTime>("UpdatedUtc")
.HasColumnType("timestamp with time zone");
b.HasKey("Id");
b.ToTable("LocalizationKey");
});
modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Localization.LocalizationText", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<DateTime>("CreatedUtc")
.HasColumnType("timestamp with time zone");
b.Property<int>("Language")
.HasColumnType("integer");
b.Property<long?>("LocalizationKeyId")
.HasColumnType("bigint");
b.Property<string>("Text")
.IsRequired()
.HasColumnType("text");
b.Property<long?>("TranslationEngineId")
.HasColumnType("bigint");
b.Property<DateTime>("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<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<long>("BodyId")
.HasColumnType("bigint");
b.Property<DateTime>("CreatedUtc")
.HasColumnType("timestamp with time zone");
b.Property<long>("NameId")
.HasColumnType("bigint");
b.Property<long?>("NovelId")
.HasColumnType("bigint");
b.Property<long>("Order")
.HasColumnType("bigint");
b.Property<long>("Revision")
.HasColumnType("bigint");
b.Property<DateTime>("UpdatedUtc")
.HasColumnType("timestamp with time zone");
b.Property<string>("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<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<long>("AuthorId")
.HasColumnType("bigint");
b.Property<DateTime>("CreatedUtc")
.HasColumnType("timestamp with time zone");
b.Property<long>("DescriptionId")
.HasColumnType("bigint");
b.Property<string>("ExternalId")
.IsRequired()
.HasColumnType("text");
b.Property<long>("NameId")
.HasColumnType("bigint");
b.Property<int>("RawLanguage")
.HasColumnType("integer");
b.Property<int>("RawStatus")
.HasColumnType("integer");
b.Property<long>("SourceId")
.HasColumnType("bigint");
b.Property<int?>("StatusOverride")
.HasColumnType("integer");
b.Property<DateTime>("UpdatedUtc")
.HasColumnType("timestamp with time zone");
b.Property<string>("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<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<DateTime>("CreatedUtc")
.HasColumnType("timestamp with time zone");
b.Property<long>("DisplayNameId")
.HasColumnType("bigint");
b.Property<string>("Key")
.IsRequired()
.HasColumnType("text");
b.Property<long?>("NovelId")
.HasColumnType("bigint");
b.Property<long?>("SourceId")
.HasColumnType("bigint");
b.Property<int>("TagType")
.HasColumnType("integer");
b.Property<DateTime>("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<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<DateTime>("CreatedUtc")
.HasColumnType("timestamp with time zone");
b.Property<string>("ExternalUrl")
.HasColumnType("text");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<DateTime>("UpdatedUtc")
.HasColumnType("timestamp with time zone");
b.HasKey("Id");
b.ToTable("Person");
});
modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Novels.Source", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<DateTime>("CreatedUtc")
.HasColumnType("timestamp with time zone");
b.Property<string>("Key")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<DateTime>("UpdatedUtc")
.HasColumnType("timestamp with time zone");
b.Property<string>("Url")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.ToTable("Sources");
});
modelBuilder.Entity("FictionArchive.Service.NovelService.Models.Novels.TranslationEngine", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<DateTime>("CreatedUtc")
.HasColumnType("timestamp with time zone");
b.Property<string>("DisplayName")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Key")
.IsRequired()
.HasColumnType("text");
b.Property<DateTime>("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
}
}
}

View File

@@ -1,29 +0,0 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace FictionArchive.Service.NovelService.Migrations
{
/// <inheritdoc />
public partial class AddSourceKey : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "Key",
table: "Sources",
type: "text",
nullable: false,
defaultValue: "");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "Key",
table: "Sources");
}
}
}

View File

@@ -1,80 +0,0 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace FictionArchive.Service.NovelService.Migrations
{
/// <inheritdoc />
public partial class FixTagAssociation : Migration
{
/// <inheritdoc />
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<long>(type: "bigint", nullable: false),
TagsId = table.Column<long>(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");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "NovelNovelTag");
migrationBuilder.AddColumn<long>(
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");
}
}
}

View File

@@ -1,10 +1,10 @@
// <auto-generated />
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
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
@@ -33,10 +33,10 @@ namespace FictionArchive.Service.NovelService.Migrations
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<DateTime>("CreatedUtc")
b.Property<Instant>("CreatedTime")
.HasColumnType("timestamp with time zone");
b.Property<DateTime>("UpdatedUtc")
b.Property<Instant>("LastUpdatedTime")
.HasColumnType("timestamp with time zone");
b.HasKey("Id");
@@ -52,12 +52,15 @@ namespace FictionArchive.Service.NovelService.Migrations
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<DateTime>("CreatedUtc")
b.Property<Instant>("CreatedTime")
.HasColumnType("timestamp with time zone");
b.Property<int>("Language")
.HasColumnType("integer");
b.Property<Instant>("LastUpdatedTime")
.HasColumnType("timestamp with time zone");
b.Property<long?>("LocalizationKeyId")
.HasColumnType("bigint");
@@ -68,9 +71,6 @@ namespace FictionArchive.Service.NovelService.Migrations
b.Property<long?>("TranslationEngineId")
.HasColumnType("bigint");
b.Property<DateTime>("UpdatedUtc")
.HasColumnType("timestamp with time zone");
b.HasKey("Id");
b.HasIndex("LocalizationKeyId");
@@ -91,7 +91,10 @@ namespace FictionArchive.Service.NovelService.Migrations
b.Property<long>("BodyId")
.HasColumnType("bigint");
b.Property<DateTime>("CreatedUtc")
b.Property<Instant>("CreatedTime")
.HasColumnType("timestamp with time zone");
b.Property<Instant>("LastUpdatedTime")
.HasColumnType("timestamp with time zone");
b.Property<long>("NameId")
@@ -106,9 +109,6 @@ namespace FictionArchive.Service.NovelService.Migrations
b.Property<long>("Revision")
.HasColumnType("bigint");
b.Property<DateTime>("UpdatedUtc")
.HasColumnType("timestamp with time zone");
b.Property<string>("Url")
.HasColumnType("text");
@@ -134,7 +134,7 @@ namespace FictionArchive.Service.NovelService.Migrations
b.Property<long>("AuthorId")
.HasColumnType("bigint");
b.Property<DateTime>("CreatedUtc")
b.Property<Instant>("CreatedTime")
.HasColumnType("timestamp with time zone");
b.Property<long>("DescriptionId")
@@ -144,6 +144,9 @@ namespace FictionArchive.Service.NovelService.Migrations
.IsRequired()
.HasColumnType("text");
b.Property<Instant>("LastUpdatedTime")
.HasColumnType("timestamp with time zone");
b.Property<long>("NameId")
.HasColumnType("bigint");
@@ -159,9 +162,6 @@ namespace FictionArchive.Service.NovelService.Migrations
b.Property<int?>("StatusOverride")
.HasColumnType("integer");
b.Property<DateTime>("UpdatedUtc")
.HasColumnType("timestamp with time zone");
b.Property<string>("Url")
.IsRequired()
.HasColumnType("text");
@@ -187,7 +187,7 @@ namespace FictionArchive.Service.NovelService.Migrations
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<DateTime>("CreatedUtc")
b.Property<Instant>("CreatedTime")
.HasColumnType("timestamp with time zone");
b.Property<long>("DisplayNameId")
@@ -197,15 +197,15 @@ namespace FictionArchive.Service.NovelService.Migrations
.IsRequired()
.HasColumnType("text");
b.Property<Instant>("LastUpdatedTime")
.HasColumnType("timestamp with time zone");
b.Property<long?>("SourceId")
.HasColumnType("bigint");
b.Property<int>("TagType")
.HasColumnType("integer");
b.Property<DateTime>("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<long>("Id"));
b.Property<DateTime>("CreatedUtc")
b.Property<Instant>("CreatedTime")
.HasColumnType("timestamp with time zone");
b.Property<string>("ExternalUrl")
.HasColumnType("text");
b.Property<Instant>("LastUpdatedTime")
.HasColumnType("timestamp with time zone");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<DateTime>("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<long>("Id"));
b.Property<DateTime>("CreatedUtc")
b.Property<Instant>("CreatedTime")
.HasColumnType("timestamp with time zone");
b.Property<string>("Key")
.IsRequired()
.HasColumnType("text");
b.Property<Instant>("LastUpdatedTime")
.HasColumnType("timestamp with time zone");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<DateTime>("UpdatedUtc")
.HasColumnType("timestamp with time zone");
b.Property<string>("Url")
.IsRequired()
.HasColumnType("text");
@@ -280,7 +280,7 @@ namespace FictionArchive.Service.NovelService.Migrations
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<DateTime>("CreatedUtc")
b.Property<Instant>("CreatedTime")
.HasColumnType("timestamp with time zone");
b.Property<string>("DisplayName")
@@ -291,7 +291,7 @@ namespace FictionArchive.Service.NovelService.Migrations
.IsRequired()
.HasColumnType("text");
b.Property<DateTime>("UpdatedUtc")
b.Property<Instant>("LastUpdatedTime")
.HasColumnType("timestamp with time zone");
b.HasKey("Id");

View File

@@ -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<long>(type: "bigint", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
CreatedUtc = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
UpdatedUtc = table.Column<DateTime>(type: "timestamp with time zone", nullable: false)
CreatedTime = table.Column<Instant>(type: "timestamp with time zone", nullable: false),
LastUpdatedTime = table.Column<Instant>(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<string>(type: "text", nullable: false),
ExternalUrl = table.Column<string>(type: "text", nullable: true),
CreatedUtc = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
UpdatedUtc = table.Column<DateTime>(type: "timestamp with time zone", nullable: false)
CreatedTime = table.Column<Instant>(type: "timestamp with time zone", nullable: false),
LastUpdatedTime = table.Column<Instant>(type: "timestamp with time zone", nullable: false)
},
constraints: table =>
{
@@ -49,9 +49,10 @@ namespace FictionArchive.Service.NovelService.Migrations
Id = table.Column<long>(type: "bigint", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
Name = table.Column<string>(type: "text", nullable: false),
Key = table.Column<string>(type: "text", nullable: false),
Url = table.Column<string>(type: "text", nullable: false),
CreatedUtc = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
UpdatedUtc = table.Column<DateTime>(type: "timestamp with time zone", nullable: false)
CreatedTime = table.Column<Instant>(type: "timestamp with time zone", nullable: false),
LastUpdatedTime = table.Column<Instant>(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<string>(type: "text", nullable: false),
DisplayName = table.Column<string>(type: "text", nullable: false),
CreatedUtc = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
UpdatedUtc = table.Column<DateTime>(type: "timestamp with time zone", nullable: false)
CreatedTime = table.Column<Instant>(type: "timestamp with time zone", nullable: false),
LastUpdatedTime = table.Column<Instant>(type: "timestamp with time zone", nullable: false)
},
constraints: table =>
{
@@ -89,8 +90,8 @@ namespace FictionArchive.Service.NovelService.Migrations
ExternalId = table.Column<string>(type: "text", nullable: false),
NameId = table.Column<long>(type: "bigint", nullable: false),
DescriptionId = table.Column<long>(type: "bigint", nullable: false),
CreatedUtc = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
UpdatedUtc = table.Column<DateTime>(type: "timestamp with time zone", nullable: false)
CreatedTime = table.Column<Instant>(type: "timestamp with time zone", nullable: false),
LastUpdatedTime = table.Column<Instant>(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<long>(type: "bigint", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
Key = table.Column<string>(type: "text", nullable: false),
DisplayNameId = table.Column<long>(type: "bigint", nullable: false),
TagType = table.Column<int>(type: "integer", nullable: false),
SourceId = table.Column<long>(type: "bigint", nullable: true),
CreatedTime = table.Column<Instant>(type: "timestamp with time zone", nullable: false),
LastUpdatedTime = table.Column<Instant>(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<string>(type: "text", nullable: false),
TranslationEngineId = table.Column<long>(type: "bigint", nullable: true),
LocalizationKeyId = table.Column<long>(type: "bigint", nullable: true),
CreatedUtc = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
UpdatedUtc = table.Column<DateTime>(type: "timestamp with time zone", nullable: false)
CreatedTime = table.Column<Instant>(type: "timestamp with time zone", nullable: false),
LastUpdatedTime = table.Column<Instant>(type: "timestamp with time zone", nullable: false)
},
constraints: table =>
{
@@ -161,8 +191,8 @@ namespace FictionArchive.Service.NovelService.Migrations
NameId = table.Column<long>(type: "bigint", nullable: false),
BodyId = table.Column<long>(type: "bigint", nullable: false),
NovelId = table.Column<long>(type: "bigint", nullable: true),
CreatedUtc = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
UpdatedUtc = table.Column<DateTime>(type: "timestamp with time zone", nullable: false)
CreatedTime = table.Column<Instant>(type: "timestamp with time zone", nullable: false),
LastUpdatedTime = table.Column<Instant>(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<long>(type: "bigint", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
Key = table.Column<string>(type: "text", nullable: false),
DisplayNameId = table.Column<long>(type: "bigint", nullable: false),
TagType = table.Column<int>(type: "integer", nullable: false),
SourceId = table.Column<long>(type: "bigint", nullable: true),
NovelId = table.Column<long>(type: "bigint", nullable: true),
CreatedUtc = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
UpdatedUtc = table.Column<DateTime>(type: "timestamp with time zone", nullable: false)
NovelsId = table.Column<long>(type: "bigint", nullable: false),
TagsId = table.Column<long>(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");
}

View File

@@ -1,9 +1,9 @@
// <auto-generated />
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<long>("Id"));
b.Property<DateTime>("CreatedUtc")
b.Property<Instant>("CreatedTime")
.HasColumnType("timestamp with time zone");
b.Property<DateTime>("UpdatedUtc")
b.Property<Instant>("LastUpdatedTime")
.HasColumnType("timestamp with time zone");
b.HasKey("Id");
@@ -49,12 +49,15 @@ namespace FictionArchive.Service.NovelService.Migrations
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<DateTime>("CreatedUtc")
b.Property<Instant>("CreatedTime")
.HasColumnType("timestamp with time zone");
b.Property<int>("Language")
.HasColumnType("integer");
b.Property<Instant>("LastUpdatedTime")
.HasColumnType("timestamp with time zone");
b.Property<long?>("LocalizationKeyId")
.HasColumnType("bigint");
@@ -65,9 +68,6 @@ namespace FictionArchive.Service.NovelService.Migrations
b.Property<long?>("TranslationEngineId")
.HasColumnType("bigint");
b.Property<DateTime>("UpdatedUtc")
.HasColumnType("timestamp with time zone");
b.HasKey("Id");
b.HasIndex("LocalizationKeyId");
@@ -88,7 +88,10 @@ namespace FictionArchive.Service.NovelService.Migrations
b.Property<long>("BodyId")
.HasColumnType("bigint");
b.Property<DateTime>("CreatedUtc")
b.Property<Instant>("CreatedTime")
.HasColumnType("timestamp with time zone");
b.Property<Instant>("LastUpdatedTime")
.HasColumnType("timestamp with time zone");
b.Property<long>("NameId")
@@ -103,9 +106,6 @@ namespace FictionArchive.Service.NovelService.Migrations
b.Property<long>("Revision")
.HasColumnType("bigint");
b.Property<DateTime>("UpdatedUtc")
.HasColumnType("timestamp with time zone");
b.Property<string>("Url")
.HasColumnType("text");
@@ -131,7 +131,7 @@ namespace FictionArchive.Service.NovelService.Migrations
b.Property<long>("AuthorId")
.HasColumnType("bigint");
b.Property<DateTime>("CreatedUtc")
b.Property<Instant>("CreatedTime")
.HasColumnType("timestamp with time zone");
b.Property<long>("DescriptionId")
@@ -141,6 +141,9 @@ namespace FictionArchive.Service.NovelService.Migrations
.IsRequired()
.HasColumnType("text");
b.Property<Instant>("LastUpdatedTime")
.HasColumnType("timestamp with time zone");
b.Property<long>("NameId")
.HasColumnType("bigint");
@@ -156,9 +159,6 @@ namespace FictionArchive.Service.NovelService.Migrations
b.Property<int?>("StatusOverride")
.HasColumnType("integer");
b.Property<DateTime>("UpdatedUtc")
.HasColumnType("timestamp with time zone");
b.Property<string>("Url")
.IsRequired()
.HasColumnType("text");
@@ -184,7 +184,7 @@ namespace FictionArchive.Service.NovelService.Migrations
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<DateTime>("CreatedUtc")
b.Property<Instant>("CreatedTime")
.HasColumnType("timestamp with time zone");
b.Property<long>("DisplayNameId")
@@ -194,15 +194,15 @@ namespace FictionArchive.Service.NovelService.Migrations
.IsRequired()
.HasColumnType("text");
b.Property<Instant>("LastUpdatedTime")
.HasColumnType("timestamp with time zone");
b.Property<long?>("SourceId")
.HasColumnType("bigint");
b.Property<int>("TagType")
.HasColumnType("integer");
b.Property<DateTime>("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<long>("Id"));
b.Property<DateTime>("CreatedUtc")
b.Property<Instant>("CreatedTime")
.HasColumnType("timestamp with time zone");
b.Property<string>("ExternalUrl")
.HasColumnType("text");
b.Property<Instant>("LastUpdatedTime")
.HasColumnType("timestamp with time zone");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<DateTime>("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<long>("Id"));
b.Property<DateTime>("CreatedUtc")
b.Property<Instant>("CreatedTime")
.HasColumnType("timestamp with time zone");
b.Property<string>("Key")
.IsRequired()
.HasColumnType("text");
b.Property<Instant>("LastUpdatedTime")
.HasColumnType("timestamp with time zone");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<DateTime>("UpdatedUtc")
.HasColumnType("timestamp with time zone");
b.Property<string>("Url")
.IsRequired()
.HasColumnType("text");
@@ -277,7 +277,7 @@ namespace FictionArchive.Service.NovelService.Migrations
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<DateTime>("CreatedUtc")
b.Property<Instant>("CreatedTime")
.HasColumnType("timestamp with time zone");
b.Property<string>("DisplayName")
@@ -288,7 +288,7 @@ namespace FictionArchive.Service.NovelService.Migrations
.IsRequired()
.HasColumnType("text");
b.Property<DateTime>("UpdatedUtc")
b.Property<Instant>("LastUpdatedTime")
.HasColumnType("timestamp with time zone");
b.HasKey("Id");

View File

@@ -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;

View File

@@ -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;

View File

@@ -1,8 +0,0 @@
namespace FictionArchive.Service.NovelService.Models.Novels;
public abstract class BaseEntity<TKey>
{
public uint Id { get; set; }
public DateTime CreatedUtc { get; set; }
public DateTime UpdatedUtc { get; set; }
}

View File

@@ -1,4 +1,5 @@
using FictionArchive.Service.NovelService.Models.Localization;
using FictionArchive.Service.Shared.Models;
namespace FictionArchive.Service.NovelService.Models.Novels;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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<uint>
{
public string Name { get; set; }
public LocalizationKey Name { get; set; }
public string? ExternalUrl { get; set; }
}

View File

@@ -1,3 +1,5 @@
using FictionArchive.Service.Shared.Models;
namespace FictionArchive.Service.NovelService.Models.Novels;
public class Source : BaseEntity<uint>

View File

@@ -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;

View File

@@ -1,3 +1,5 @@
using FictionArchive.Service.Shared.Models;
namespace FictionArchive.Service.NovelService.Models.Novels;
public class TranslationEngine : BaseEntity<uint>

View File

@@ -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<Query>()
.AddMutationType<Mutation>()
.AddType<UnsignedIntType>()
.AddMutationConventions(applyToAllMutations: true)
.AddFiltering(opt => opt.AddDefaults().BindRuntimeType<uint, UnsignedIntOperationFilterInputType>())
.AddSorting()
.AddProjections();
builder.Services.AddDefaultGraphQl<Query, Mutation>();
#endregion
#region Database
builder.Services.AddDbContext<NovelServiceDbContext>(opt =>
{
opt.UseNpgsql(builder.Configuration.GetConnectionString("DefaultConnection"));
});
builder.Services.RegisterDbContext<NovelServiceDbContext>(builder.Configuration.GetConnectionString("DefaultConnection"));
#endregion

View File

@@ -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<NovelServiceDbContext> logger)
: DbContext(options)
: FictionArchiveDbContext(options, logger)
{
public DbSet<Novel> Novels { get; set; }
public DbSet<Source> Sources { get; set; }
public DbSet<TranslationEngine> TranslationEngines { get; set; }
public DbSet<NovelTag> Tags { get; set; }
private readonly ILogger _logger = logger;
public void UpdateDatabase()
{
IEnumerable<string> 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.");
}
}

View File

@@ -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<TContext>(this IServiceCollection services, Action<DbContextOptionsBuilder> optionConfiguration)
where TContext : FictionArchiveDbContext
{
services.AddDbContext<TContext>(opt =>
{
optionConfiguration?.Invoke(opt);
});
return services;
}
}

View File

@@ -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<TContext>(this IServiceCollection services,
string connectionString) where TContext : FictionArchiveDbContext
{
services.AddDbContext<TContext>(options =>
{
options.UseNpgsql(connectionString, o =>
{
o.UseNodaTime();
});
});
return services;
}
}

View File

@@ -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<TQuery, TMutation>(this IServiceCollection services) where TQuery : class where TMutation : class
{
return services.AddGraphQLServer()
.AddQueryType<TQuery>()
.AddMutationType<TMutation>()
.AddType<UnsignedIntType>()
.AddType<InstantType>()
.AddMutationConventions(applyToAllMutations: true)
.AddFiltering(opt => opt.AddDefaults().BindRuntimeType<uint, UnsignedIntOperationFilterInputType>())
.AddSorting()
.AddProjections();
}
}

View File

@@ -7,19 +7,32 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="GraphQL.Server.Ui.GraphiQL" Version="8.3.3" />
<PackageReference Include="HotChocolate.AspNetCore" Version="15.1.11" />
<PackageReference Include="HotChocolate.Data.EntityFramework" Version="15.1.11" />
<PackageReference Include="HotChocolate.Types.Scalars" Version="15.1.11" />
<PackageReference Include="HotChocolate.Types.NodaTime" Version="15.1.11" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.11" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.11">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="9.0.11" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="9.0.11">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.4" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL.NodaTime" Version="9.0.4" />
<PackageReference Include="RabbitMQ.Client" Version="7.2.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\FictionArchive.Common\FictionArchive.Common.csproj" />
</ItemGroup>
<ItemGroup>
<Folder Include="Services\Messaging\Interfaces\" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,11 @@
using FictionArchive.Service.Shared.Models.Interfaces;
using NodaTime;
namespace FictionArchive.Service.Shared.Models;
public abstract class BaseEntity<TKey> : IAuditable
{
public uint Id { get; set; }
public Instant CreatedTime { get; set; }
public Instant LastUpdatedTime { get; set; }
}

View File

@@ -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; }
}

View File

@@ -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<int> SavingChanges(
DbContextEventData eventData,
InterceptionResult<int> result)
{
var context = eventData.Context;
if (context == null)
return base.SavingChanges(eventData, result);
var entries = context.ChangeTracker.Entries<IAuditable>();
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);
}
}

View File

@@ -0,0 +1,41 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
namespace FictionArchive.Service.Shared.Services.Database;
/// <summary>
/// Abstract DbContext handling boilerplate shared between our contexts. Should not share actual data.
/// </summary>
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<string> 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.");
}
}

View File

@@ -1,17 +0,0 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
namespace FictionArchive.Service.Shared.Services;
/// <summary>
/// Abstract DbContext handling boilerplate shared between our contexts. Should not share actual data.
/// </summary>
public abstract class FictionArchiveDbContext : DbContext
{
protected readonly ILogger _logger;
protected FictionArchiveDbContext(DbContextOptions options, ILogger logger) : base(options)
{
_logger = logger;
}
}

View File

@@ -29,6 +29,9 @@
<Content Include="..\.dockerignore">
<Link>.dockerignore</Link>
</Content>
<Content Update="appsettings.Development.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>

View File

@@ -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<string> TranslateText(string text, Language from, Language to, string translationEngineKey, IEnumerable<ITranslationEngineAdapter> translationEngines)
public async Task<string> TranslateText(string text, Language from, Language to, string translationEngineKey, IEnumerable<ITranslationEngine> 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;
}
}

View File

@@ -7,7 +7,7 @@ public class Query
{
[UseFiltering]
[UseSorting]
public IEnumerable<TranslationEngineDescriptor> GetTranslationEngines(IEnumerable<ITranslationEngineAdapter> engines)
public IEnumerable<TranslationEngineDescriptor> GetTranslationEngines(IEnumerable<ITranslationEngine> engines)
{
return engines.Select(engine => engine.Descriptor);
}

View File

@@ -0,0 +1,72 @@
// <auto-generated />
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
{
/// <inheritdoc />
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<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<long>("BilledCharacterCount")
.HasColumnType("bigint");
b.Property<Instant>("CreatedTime")
.HasColumnType("timestamp with time zone");
b.Property<int>("From")
.HasColumnType("integer");
b.Property<Instant>("LastUpdatedTime")
.HasColumnType("timestamp with time zone");
b.Property<string>("OriginalText")
.IsRequired()
.HasColumnType("text");
b.Property<int>("Status")
.HasColumnType("integer");
b.Property<int>("To")
.HasColumnType("integer");
b.Property<string>("TranslatedText")
.HasColumnType("text");
b.Property<string>("TranslationEngineKey")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.ToTable("TranslationRequests");
});
#pragma warning restore 612, 618
}
}
}

View File

@@ -0,0 +1,44 @@
using Microsoft.EntityFrameworkCore.Migrations;
using NodaTime;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
namespace FictionArchive.Service.TranslationService.Migrations
{
/// <inheritdoc />
public partial class Initial : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "TranslationRequests",
columns: table => new
{
Id = table.Column<long>(type: "bigint", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
OriginalText = table.Column<string>(type: "text", nullable: false),
TranslatedText = table.Column<string>(type: "text", nullable: true),
From = table.Column<int>(type: "integer", nullable: false),
To = table.Column<int>(type: "integer", nullable: false),
TranslationEngineKey = table.Column<string>(type: "text", nullable: false),
Status = table.Column<int>(type: "integer", nullable: false),
BilledCharacterCount = table.Column<long>(type: "bigint", nullable: false),
CreatedTime = table.Column<Instant>(type: "timestamp with time zone", nullable: false),
LastUpdatedTime = table.Column<Instant>(type: "timestamp with time zone", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_TranslationRequests", x => x.Id);
});
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "TranslationRequests");
}
}
}

View File

@@ -0,0 +1,69 @@
// <auto-generated />
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<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<long>("BilledCharacterCount")
.HasColumnType("bigint");
b.Property<Instant>("CreatedTime")
.HasColumnType("timestamp with time zone");
b.Property<int>("From")
.HasColumnType("integer");
b.Property<Instant>("LastUpdatedTime")
.HasColumnType("timestamp with time zone");
b.Property<string>("OriginalText")
.IsRequired()
.HasColumnType("text");
b.Property<int>("Status")
.HasColumnType("integer");
b.Property<int>("To")
.HasColumnType("integer");
b.Property<string>("TranslatedText")
.HasColumnType("text");
b.Property<string>("TranslationEngineKey")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.ToTable("TranslationRequests");
});
#pragma warning restore 612, 618
}
}
}

View File

@@ -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<Guid>
{
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; }
}

View File

@@ -0,0 +1,8 @@
namespace FictionArchive.Service.TranslationService.Models.Enums;
public enum TranslationRequestStatus
{
Failed = -1,
Pending = 0,
Success = 1,
}

View File

@@ -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<TranslationServiceDbContext>(builder.Configuration.GetConnectionString("DefaultConnection"));
#endregion
#region GraphQL
builder.Services.AddGraphQLServer()
.AddQueryType<Query>()
.AddMutationType<Mutation>()
.AddType<UnsignedIntType>()
.AddMutationConventions(applyToAllMutations: true)
.AddFiltering(opt => opt.AddDefaults().BindRuntimeType<uint, UnsignedIntOperationFilterInputType>())
.AddSorting()
.AddProjections();
builder.Services.AddDefaultGraphQl<Query, Mutation>();
#endregion
@@ -33,7 +36,7 @@ public class Program
{
return new DeepLClient(builder.Configuration["DeepL:ApiKey"]);
});
builder.Services.AddTransient<ITranslationEngineAdapter, DeepLTranslationAdapater>();
builder.Services.AddTransient<ITranslationEngine, DeepLTranslationEngine>();
#endregion

View File

@@ -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<TranslationRequest> TranslationRequests { get; set; }
public TranslationServiceDbContext(DbContextOptions options, ILogger<TranslationServiceDbContext> logger) : base(options, logger)
{
}
}

View File

@@ -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<DeepLTranslationAdapater> _logger;
private readonly ILogger<DeepLTranslationEngine> _logger;
private const string DisplayName = "DeepL";
private const string Key = "deepl";
public DeepLTranslationAdapater(DeepLClient deepLClient, ILogger<DeepLTranslationAdapater> logger)
public DeepLTranslationEngine(DeepLClient deepLClient, ILogger<DeepLTranslationEngine> 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)
};
}
}

View File

@@ -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<string?> GetTranslation(string body, Language from, Language to);

View File

@@ -8,5 +8,8 @@
"DeepL": {
"ApiKey": "REPLACE_ME"
},
"ConnectionStrings": {
"DefaultConnection": "Host=localhost;Database=FictionArchive_NovelService;Username=postgres;password=postgres"
},
"AllowedHosts": "*"
}