refactor(api): extract ProfileCv DTOs, add missing AsNoTracking on reads
This commit is contained in:
@@ -123,7 +123,7 @@ public sealed class ProfileCvControllerTests
|
||||
var result = await controller.GetRuns();
|
||||
|
||||
var ok = Assert.IsType<OkObjectResult>(result.Result);
|
||||
var runs = Assert.IsAssignableFrom<IEnumerable<ProfileCvController.CvExtractionRunListItem>>(ok.Value);
|
||||
var runs = Assert.IsAssignableFrom<IEnumerable<CvExtractionRunListItem>>(ok.Value);
|
||||
var single = Assert.Single(runs);
|
||||
Assert.Equal("upload", single.Trigger);
|
||||
Assert.Equal("applied", single.Status);
|
||||
@@ -611,7 +611,7 @@ public sealed class ProfileCvControllerTests
|
||||
|
||||
var objectResult = Assert.IsType<ObjectResult>(result);
|
||||
Assert.Equal(StatusCodes.Status502BadGateway, objectResult.StatusCode);
|
||||
var payload = Assert.IsType<ProfileCvController.CvRewriteFailureDto>(objectResult.Value);
|
||||
var payload = Assert.IsType<CvRewriteFailureDto>(objectResult.Value);
|
||||
Assert.Equal("ai-service-unavailable", payload.Code);
|
||||
Assert.Contains("could not rewrite", payload.Message, StringComparison.OrdinalIgnoreCase);
|
||||
Assert.Contains("unavailable", payload.Detail ?? string.Empty, StringComparison.OrdinalIgnoreCase);
|
||||
@@ -673,7 +673,7 @@ public sealed class ProfileCvControllerTests
|
||||
|
||||
var objectResult = Assert.IsType<ObjectResult>(result);
|
||||
Assert.Equal(StatusCodes.Status502BadGateway, objectResult.StatusCode);
|
||||
var payload = Assert.IsType<ProfileCvController.CvRewriteFailureDto>(objectResult.Value);
|
||||
var payload = Assert.IsType<CvRewriteFailureDto>(objectResult.Value);
|
||||
Assert.Equal("rewrite-empty", payload.Code);
|
||||
Assert.Contains("empty", payload.Message, StringComparison.OrdinalIgnoreCase);
|
||||
Assert.Contains("no usable text", payload.Detail ?? string.Empty, StringComparison.OrdinalIgnoreCase);
|
||||
@@ -766,7 +766,7 @@ public sealed class ProfileCvControllerTests
|
||||
var paths = CreatePaths();
|
||||
var controller = CreateController(userManager.Object, aiService.Object, db, paths);
|
||||
|
||||
var result = await controller.Parse(new ProfileCvController.ParseCvRequest(user.ProfileCvText));
|
||||
var result = await controller.Parse(new ParseCvRequest(user.ProfileCvText));
|
||||
|
||||
var ok = Assert.IsType<OkObjectResult>(result.Result);
|
||||
var json = JsonSerializer.Serialize(ok.Value);
|
||||
@@ -800,7 +800,7 @@ public sealed class ProfileCvControllerTests
|
||||
var paths = CreatePaths();
|
||||
var controller = CreateController(userManager.Object, aiService.Object, db, paths);
|
||||
|
||||
var result = await controller.Parse(new ProfileCvController.ParseCvRequest(user.ProfileCvText));
|
||||
var result = await controller.Parse(new ParseCvRequest(user.ProfileCvText));
|
||||
|
||||
var ok = Assert.IsType<OkObjectResult>(result.Result);
|
||||
var json = JsonSerializer.Serialize(ok.Value);
|
||||
@@ -838,7 +838,7 @@ public sealed class ProfileCvControllerTests
|
||||
var paths = CreatePaths();
|
||||
var controller = CreateController(userManager.Object, aiService.Object, db, paths, classifier.Object);
|
||||
|
||||
var result = await controller.Parse(new ProfileCvController.ParseCvRequest(source));
|
||||
var result = await controller.Parse(new ParseCvRequest(source));
|
||||
|
||||
var ok = Assert.IsType<OkObjectResult>(result.Result);
|
||||
var json = JsonSerializer.Serialize(ok.Value);
|
||||
@@ -878,7 +878,7 @@ public sealed class ProfileCvControllerTests
|
||||
var paths = CreatePaths();
|
||||
var controller = CreateController(userManager.Object, aiService.Object, db, paths, classifier.Object);
|
||||
|
||||
var result = await controller.Parse(new ProfileCvController.ParseCvRequest(source));
|
||||
var result = await controller.Parse(new ParseCvRequest(source));
|
||||
|
||||
var ok = Assert.IsType<OkObjectResult>(result.Result);
|
||||
var json = JsonSerializer.Serialize(ok.Value);
|
||||
@@ -914,7 +914,7 @@ public sealed class ProfileCvControllerTests
|
||||
var paths = CreatePaths();
|
||||
var controller = CreateController(userManager.Object, aiService.Object, db, paths, classifier.Object);
|
||||
|
||||
var result = await controller.Parse(new ProfileCvController.ParseCvRequest(source));
|
||||
var result = await controller.Parse(new ParseCvRequest(source));
|
||||
|
||||
var ok = Assert.IsType<OkObjectResult>(result.Result);
|
||||
var json = JsonSerializer.Serialize(ok.Value);
|
||||
@@ -1030,7 +1030,7 @@ public sealed class ProfileCvControllerTests
|
||||
|
||||
await using var db = CreateDb();
|
||||
var controller = CreateController(userManager.Object, aiService.Object, db, CreatePaths(), null, normalizer.Object);
|
||||
var result = await controller.Parse(new ProfileCvController.ParseCvRequest(source));
|
||||
var result = await controller.Parse(new ParseCvRequest(source));
|
||||
Assert.IsType<OkObjectResult>(result.Result);
|
||||
|
||||
var actual = StructuredCvProfileJson.Deserialize(user.ProfileCvStructureJson);
|
||||
@@ -1069,7 +1069,7 @@ public sealed class ProfileCvControllerTests
|
||||
var paths = CreatePaths();
|
||||
var controller = CreateController(userManager.Object, aiService.Object, db, paths);
|
||||
|
||||
var result = await controller.Parse(new ProfileCvController.ParseCvRequest(rawSource));
|
||||
var result = await controller.Parse(new ParseCvRequest(rawSource));
|
||||
var ok = Assert.IsType<OkObjectResult>(result.Result);
|
||||
Assert.NotNull(ok.Value);
|
||||
|
||||
@@ -1098,7 +1098,7 @@ public sealed class ProfileCvControllerTests
|
||||
|
||||
await using var db = CreateDb();
|
||||
var controller = CreateController(userManager.Object, aiService.Object, db, CreatePaths());
|
||||
var result = await controller.Parse(new ProfileCvController.ParseCvRequest(source));
|
||||
var result = await controller.Parse(new ParseCvRequest(source));
|
||||
Assert.IsType<OkObjectResult>(result.Result);
|
||||
|
||||
var actual = StructuredCvProfileJson.Deserialize(user.ProfileCvStructureJson);
|
||||
@@ -1129,7 +1129,7 @@ public sealed class ProfileCvControllerTests
|
||||
|
||||
await using var db = CreateDb();
|
||||
var controller = CreateController(userManager.Object, aiService.Object, db, CreatePaths());
|
||||
var result = await controller.Parse(new ProfileCvController.ParseCvRequest(source));
|
||||
var result = await controller.Parse(new ParseCvRequest(source));
|
||||
Assert.IsType<OkObjectResult>(result.Result);
|
||||
|
||||
var actual = StructuredCvProfileJson.Deserialize(user.ProfileCvStructureJson);
|
||||
@@ -1158,7 +1158,7 @@ public sealed class ProfileCvControllerTests
|
||||
|
||||
await using var db = CreateDb();
|
||||
var controller = CreateController(userManager.Object, aiService.Object, db, CreatePaths());
|
||||
var result = await controller.Parse(new ProfileCvController.ParseCvRequest(source));
|
||||
var result = await controller.Parse(new ParseCvRequest(source));
|
||||
Assert.IsType<OkObjectResult>(result.Result);
|
||||
|
||||
var actual = StructuredCvProfileJson.Deserialize(user.ProfileCvStructureJson);
|
||||
@@ -1186,7 +1186,7 @@ public sealed class ProfileCvControllerTests
|
||||
|
||||
await using var db = CreateDb();
|
||||
var controller = CreateController(userManager.Object, aiService.Object, db, CreatePaths());
|
||||
var result = await controller.Parse(new ProfileCvController.ParseCvRequest(source));
|
||||
var result = await controller.Parse(new ParseCvRequest(source));
|
||||
Assert.IsType<OkObjectResult>(result.Result);
|
||||
|
||||
var actual = StructuredCvProfileJson.Deserialize(user.ProfileCvStructureJson);
|
||||
|
||||
@@ -113,25 +113,8 @@ public sealed class ProfileCvController : ControllerBase
|
||||
public string? Tone { get; set; }
|
||||
public string? Language { get; set; }
|
||||
}
|
||||
public sealed record ParseCvRequest(string? Text);
|
||||
public sealed record CvTemplateDescriptor(string Id, string Title, string Tone, string AccentColor, string PreviewTagline, string PreviewSummary, List<string> PreviewBullets);
|
||||
public sealed record ProfileCvPreviewDto(string TemplateId, string Html, string SuggestedFileName, string FullText, string RewrittenText, string? SectionName, StructuredCvProfile StructuredCv, TailoredCvDocument Document, string? TargetRole, int? JobApplicationId);
|
||||
public sealed record CvRewriteFailureDto(string Code, string Message, string? Detail = null, string? LastAiError = null);
|
||||
|
||||
private sealed record ExtractionPipelineResult(string RawText, string NormalizedText, StructuredCvProfile StructuredCv);
|
||||
private sealed record ClassifiedCvBlock(int Index, string OriginalBlock, string SectionName, string Content, CvBlockClassificationResult? Classification);
|
||||
public sealed record CvExtractionRunListItem(
|
||||
int Id,
|
||||
string Trigger,
|
||||
string Status,
|
||||
string? ArtifactFileName,
|
||||
DateTimeOffset StartedAtUtc,
|
||||
DateTimeOffset? CompletedAtUtc,
|
||||
DateTimeOffset? AppliedAtUtc,
|
||||
string ParserVersion,
|
||||
string NormalizerVersion,
|
||||
string LlmPromptVersion,
|
||||
string? ErrorMessage);
|
||||
|
||||
[HttpPost("upload")]
|
||||
[RequestSizeLimit(MaxFileSizeBytes)]
|
||||
@@ -254,6 +237,7 @@ public sealed class ProfileCvController : ControllerBase
|
||||
if (user is null) return Unauthorized();
|
||||
|
||||
var artifact = await _db.CvUploadArtifacts
|
||||
.AsNoTracking()
|
||||
.OrderByDescending(x => x.UploadedAtUtc)
|
||||
.FirstOrDefaultAsync(x => x.OwnerUserId == user.Id, HttpContext.RequestAborted);
|
||||
|
||||
@@ -941,7 +925,7 @@ public sealed class ProfileCvController : ControllerBase
|
||||
}
|
||||
case "reprocess":
|
||||
{
|
||||
var artifact = await _db.CvUploadArtifacts.FirstOrDefaultAsync(x => x.Id == run.ArtifactId && x.OwnerUserId == user.Id, cancellationToken);
|
||||
var artifact = await _db.CvUploadArtifacts.AsNoTracking().FirstOrDefaultAsync(x => x.Id == run.ArtifactId && x.OwnerUserId == user.Id, cancellationToken);
|
||||
if (artifact is null) throw new InvalidOperationException("Upload a CV before reprocessing it.");
|
||||
if (string.IsNullOrWhiteSpace(artifact.StoragePath) || !System.IO.File.Exists(artifact.StoragePath))
|
||||
{
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
using JobTrackerApi.Models;
|
||||
|
||||
namespace JobTrackerApi.Controllers;
|
||||
|
||||
public sealed record ParseCvRequest(string? Text);
|
||||
public sealed record CvTemplateDescriptor(string Id, string Title, string Tone, string AccentColor, string PreviewTagline, string PreviewSummary, List<string> PreviewBullets);
|
||||
public sealed record ProfileCvPreviewDto(string TemplateId, string Html, string SuggestedFileName, string FullText, string RewrittenText, string? SectionName, StructuredCvProfile StructuredCv, TailoredCvDocument Document, string? TargetRole, int? JobApplicationId);
|
||||
public sealed record CvRewriteFailureDto(string Code, string Message, string? Detail = null, string? LastAiError = null);
|
||||
public sealed record CvExtractionRunListItem(
|
||||
int Id,
|
||||
string Trigger,
|
||||
string Status,
|
||||
string? ArtifactFileName,
|
||||
DateTimeOffset StartedAtUtc,
|
||||
DateTimeOffset? CompletedAtUtc,
|
||||
DateTimeOffset? AppliedAtUtc,
|
||||
string ParserVersion,
|
||||
string NormalizerVersion,
|
||||
string LlmPromptVersion,
|
||||
string? ErrorMessage);
|
||||
Reference in New Issue
Block a user