Merged in feature/batch-status (pull request #215)
Implement and test the batch status feature * working
This commit is contained in:
+501
-437
File diff suppressed because it is too large
Load Diff
+44
-16
@@ -254,13 +254,13 @@ func (s *Controllers) UploadDocumentBatch(ctx echo.Context, clientId ClientID) e
|
||||
return ctx.JSON(http.StatusAccepted, response)
|
||||
}
|
||||
|
||||
// GetDocumentBatch returns batch upload status
|
||||
// GetDocumentBatch returns batch upload status including per-file document outcomes.
|
||||
func (s *Controllers) GetDocumentBatch(ctx echo.Context, clientId ClientID, batchId BatchID) error {
|
||||
// BatchID is already a UUID type
|
||||
batchUUID := uuid.UUID(batchId)
|
||||
|
||||
// Get batch details
|
||||
batch, err := s.svc.DocumentBatch.Get(ctx.Request().Context(), clientId, batchUUID)
|
||||
// Get batch details (includes document outcomes)
|
||||
batchDetails, err := s.svc.DocumentBatch.Get(ctx.Request().Context(), clientId, batchUUID)
|
||||
if err != nil {
|
||||
slog.Error("unable to get batch", "error", err, "batchId", batchId)
|
||||
return echo.NewHTTPError(http.StatusNotFound, "batch not found")
|
||||
@@ -268,22 +268,50 @@ func (s *Controllers) GetDocumentBatch(ctx echo.Context, clientId ClientID, batc
|
||||
|
||||
// Convert to API response format
|
||||
details := BatchUploadDetails{
|
||||
BatchId: BatchID(batch.ID),
|
||||
ClientId: ClientID(batch.ClientID),
|
||||
OriginalFilename: batch.OriginalFilename,
|
||||
Status: BatchStatus(batch.Status),
|
||||
TotalDocuments: batch.TotalDocuments,
|
||||
ProcessedDocuments: batch.ProcessedDocuments,
|
||||
FailedDocuments: batch.FailedDocuments,
|
||||
InvalidTypeDocuments: batch.InvalidTypeDocuments,
|
||||
ProgressPercent: batch.ProgressPercent,
|
||||
FailedFilenames: &batch.FailedFilenames,
|
||||
CreatedAt: batch.CreatedAt,
|
||||
BatchId: BatchID(batchDetails.ID),
|
||||
ClientId: ClientID(batchDetails.ClientID),
|
||||
OriginalFilename: batchDetails.OriginalFilename,
|
||||
Status: BatchStatus(batchDetails.Status),
|
||||
TotalDocuments: batchDetails.TotalDocuments,
|
||||
ProcessedDocuments: batchDetails.ProcessedDocuments,
|
||||
FailedDocuments: batchDetails.FailedDocuments,
|
||||
InvalidTypeDocuments: batchDetails.InvalidTypeDocuments,
|
||||
ProgressPercent: batchDetails.ProgressPercent,
|
||||
FailedFilenames: &batchDetails.FailedFilenames,
|
||||
CreatedAt: batchDetails.CreatedAt,
|
||||
}
|
||||
|
||||
// Handle nullable completed at
|
||||
if batch.CompletedAt != nil {
|
||||
details.CompletedAt = nullable.NewNullableWithValue(*batch.CompletedAt)
|
||||
if batchDetails.CompletedAt != nil {
|
||||
details.CompletedAt = nullable.NewNullableWithValue(*batchDetails.CompletedAt)
|
||||
}
|
||||
|
||||
// Map document outcomes to API response format
|
||||
if len(batchDetails.DocumentOutcomes) > 0 {
|
||||
outcomes := make([]BatchDocumentOutcome, len(batchDetails.DocumentOutcomes))
|
||||
for i, o := range batchDetails.DocumentOutcomes {
|
||||
outcome := BatchDocumentOutcome{
|
||||
Filename: o.Filename,
|
||||
Outcome: BatchDocumentOutcomeOutcome(o.Outcome),
|
||||
CreatedAt: &o.CreatedAt,
|
||||
UpdatedAt: &o.UpdatedAt,
|
||||
}
|
||||
|
||||
if o.ErrorDetail != nil {
|
||||
outcome.ErrorDetail = nullable.NewNullableWithValue(*o.ErrorDetail)
|
||||
}
|
||||
|
||||
if o.DocumentID != nil {
|
||||
outcome.DocumentId = nullable.NewNullableWithValue(openapi_types.UUID(*o.DocumentID))
|
||||
}
|
||||
|
||||
if o.CleanFail != nil {
|
||||
outcome.CleanFailReason = nullable.NewNullableWithValue(*o.CleanFail)
|
||||
}
|
||||
|
||||
outcomes[i] = outcome
|
||||
}
|
||||
details.DocumentOutcomes = &outcomes
|
||||
}
|
||||
|
||||
return ctx.JSON(http.StatusOK, details)
|
||||
|
||||
@@ -418,6 +418,94 @@ func TestGetDocumentBatch(t *testing.T) {
|
||||
assert.Equal(t, "test.zip", response.OriginalFilename)
|
||||
}
|
||||
|
||||
// TestGetDocumentBatch_IncludesOutcomes verifies that GetDocumentBatch returns
|
||||
// the document_outcomes array populated with per-file outcome rows.
|
||||
func TestGetDocumentBatch_IncludesOutcomes(t *testing.T) {
|
||||
cfg := &ControllerConfig{}
|
||||
test.CreateDB(t, cfg)
|
||||
initializeTestConfig(t, cfg)
|
||||
|
||||
svc := createControllerServices(cfg)
|
||||
cons := queryapi.NewControllers(svc, cfg)
|
||||
|
||||
clientID := fmt.Sprintf("outcome_client_%s", uuid.New().String()[:8])
|
||||
test.CreateTestClient(t, cfg, clientID, "Test Outcomes Client")
|
||||
|
||||
// Create batch
|
||||
batchID, err := cfg.GetDBQueries().CreateBatchUpload(t.Context(), &repository.CreateBatchUploadParams{
|
||||
ClientID: clientID,
|
||||
OriginalFilename: "outcomes.zip",
|
||||
TotalDocuments: 3,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create a document so we can reference it in outcome rows
|
||||
docID, err := cfg.GetDBQueries().CreateDocument(t.Context(), &repository.CreateDocumentParams{
|
||||
Clientid: clientID,
|
||||
Hash: "outcome_hash_" + uuid.New().String()[:8],
|
||||
BatchID: &batchID,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Insert outcomes with different statuses
|
||||
err = cfg.GetDBQueries().InsertBatchDocumentOutcome(t.Context(), &repository.InsertBatchDocumentOutcomeParams{
|
||||
BatchID: batchID,
|
||||
Filename: "invoice.pdf",
|
||||
Column3: repository.BatchOutcomeStatusCleanPassed,
|
||||
DocumentID: &docID,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
invalidErr := "not a PDF"
|
||||
err = cfg.GetDBQueries().InsertBatchDocumentOutcome(t.Context(), &repository.InsertBatchDocumentOutcomeParams{
|
||||
BatchID: batchID,
|
||||
Filename: "spreadsheet.xlsx",
|
||||
Column3: repository.BatchOutcomeStatusInvalidType,
|
||||
ErrorDetail: &invalidErr,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
err = cfg.GetDBQueries().InsertBatchDocumentOutcome(t.Context(), &repository.InsertBatchDocumentOutcomeParams{
|
||||
BatchID: batchID,
|
||||
Filename: "receipt.pdf",
|
||||
Column3: repository.BatchOutcomeStatusSubmitted,
|
||||
DocumentID: &docID,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Call GetDocumentBatch
|
||||
ctx, rec := createContext(t)
|
||||
err = cons.GetDocumentBatch(ctx, clientID, queryapi.BatchID(batchID))
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, http.StatusOK, rec.Code)
|
||||
|
||||
var response queryapi.BatchUploadDetails
|
||||
err = json.Unmarshal(rec.Body.Bytes(), &response)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify document_outcomes array is populated
|
||||
require.NotNil(t, response.DocumentOutcomes, "document_outcomes should be present")
|
||||
require.Len(t, *response.DocumentOutcomes, 3, "should have 3 outcome entries")
|
||||
|
||||
// Build a map by filename for easier assertion
|
||||
outcomeMap := make(map[string]queryapi.BatchDocumentOutcome)
|
||||
for _, o := range *response.DocumentOutcomes {
|
||||
outcomeMap[o.Filename] = o
|
||||
}
|
||||
|
||||
// Verify each outcome
|
||||
invoiceOutcome := outcomeMap["invoice.pdf"]
|
||||
assert.Equal(t, queryapi.BatchDocumentOutcomeOutcome("clean_passed"), invoiceOutcome.Outcome)
|
||||
assert.True(t, invoiceOutcome.DocumentId.IsSpecified(), "clean_passed outcome should have document_id")
|
||||
|
||||
xlsxOutcome := outcomeMap["spreadsheet.xlsx"]
|
||||
assert.Equal(t, queryapi.BatchDocumentOutcomeOutcome("invalid_type"), xlsxOutcome.Outcome)
|
||||
assert.True(t, xlsxOutcome.ErrorDetail.IsSpecified(), "invalid_type outcome should have error_detail")
|
||||
|
||||
receiptOutcome := outcomeMap["receipt.pdf"]
|
||||
assert.Equal(t, queryapi.BatchDocumentOutcomeOutcome("submitted"), receiptOutcome.Outcome)
|
||||
}
|
||||
|
||||
func TestUploadDocumentBatch_Part1_Success(t *testing.T) {
|
||||
// Setup: Create testcontainers (database + localstack)
|
||||
cfg := &ControllerConfig{}
|
||||
|
||||
Reference in New Issue
Block a user