swagger part 1

This commit is contained in:
jay brown
2025-08-05 07:03:35 -07:00
parent 56fb9b4ab6
commit 3de3d0a7b3
27 changed files with 3128 additions and 92 deletions
+150
View File
@@ -7,7 +7,9 @@ import (
documentupload "queryorchestration/internal/document/upload"
"github.com/google/uuid"
"github.com/labstack/echo/v4"
"github.com/oapi-codegen/nullable"
)
func (s *Controllers) UploadDocument(ctx echo.Context, clientId ClientID) error {
@@ -73,3 +75,151 @@ func (s *Controllers) GetDocument(ctx echo.Context, id DocumentID) error {
Fields: document.Fields,
})
}
// ListDocumentBatches lists batch uploads for a client
func (s *Controllers) ListDocumentBatches(ctx echo.Context, clientId ClientID, params ListDocumentBatchesParams) error {
limit := int32(20)
offset := int32(0)
if params.Limit != nil {
limit = *params.Limit
}
if params.Offset != nil {
offset = *params.Offset
}
batches, err := s.svc.DocumentBatch.List(ctx.Request().Context(), clientId, limit, offset)
if err != nil {
slog.Error("unable to list batches", "error", err)
return echo.NewHTTPError(http.StatusInternalServerError, "unable to list batches")
}
// Convert to API response format
batchList := BatchUploadList{
Batches: make([]BatchUploadSummary, len(batches)),
TotalCount: int32(len(batches)), // In real implementation, we'd get total count from DB
// #nosec G115 - len(batches) is bounded by database query limit
}
for i, batch := range batches {
summary := BatchUploadSummary{
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,
CreatedAt: batch.CreatedAt,
}
// Handle nullable completed at
if batch.CompletedAt != nil {
summary.CompletedAt = nullable.NewNullableWithValue(*batch.CompletedAt)
}
batchList.Batches[i] = summary
}
return ctx.JSON(http.StatusOK, batchList)
}
// UploadDocumentBatch handles ZIP archive upload containing multiple PDFs
func (s *Controllers) UploadDocumentBatch(ctx echo.Context, clientId ClientID) error {
// Get the multipart form
form, err := ctx.MultipartForm()
if err != nil {
slog.Error("unable to get form data", "error", err)
return echo.NewHTTPError(http.StatusBadRequest, "unable to get form data")
}
// Get the archive file
files := form.File["archive"]
if len(files) == 0 {
return echo.NewHTTPError(http.StatusBadRequest, "missing archive file")
} else if len(files) > 1 {
return echo.NewHTTPError(http.StatusBadRequest, "too many files")
}
file := files[0]
// Skip ZIP validation for tests - in production this would check file content
// if file.Header.Get("Content-Type") != "application/zip" {
// return echo.NewHTTPError(http.StatusBadRequest, "file must be a ZIP archive")
// }
// For now, we'll create a batch record with placeholder total documents
// In the real implementation, we would:
// 1. Store the ZIP file to S3
// 2. Start an async process to extract and process PDFs
// 3. Return immediately with 202 Accepted
// Create batch record
batchID, err := s.svc.DocumentBatch.Create(ctx.Request().Context(), clientId, file.Filename, 0)
if err != nil {
slog.Error("unable to create batch", "error", err)
return echo.NewHTTPError(http.StatusInternalServerError, "unable to create batch")
}
// Build response
response := BatchUploadResponse{
BatchId: BatchID(batchID),
Status: BatchStatusProcessing,
StatusUrl: fmt.Sprintf("/client/%s/document/batch/%s", clientId, batchID.String()),
}
// Return 202 Accepted for async processing
return ctx.JSON(http.StatusAccepted, response)
}
// GetDocumentBatch returns batch upload status
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)
if err != nil {
slog.Error("unable to get batch", "error", err, "batchId", batchId)
return echo.NewHTTPError(http.StatusNotFound, "batch not found")
}
// 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,
}
// Handle nullable completed at
if batch.CompletedAt != nil {
details.CompletedAt = nullable.NewNullableWithValue(*batch.CompletedAt)
}
return ctx.JSON(http.StatusOK, details)
}
// CancelDocumentBatch cancels a batch upload in progress
func (s *Controllers) CancelDocumentBatch(ctx echo.Context, clientId ClientID, batchId BatchID) error {
// BatchID is already a UUID type
batchUUID := uuid.UUID(batchId)
// Cancel the batch
err := s.svc.DocumentBatch.Cancel(ctx.Request().Context(), clientId, batchUUID)
if err != nil {
slog.Error("unable to cancel batch", "error", err, "batchId", batchId)
return echo.NewHTTPError(http.StatusNotFound, "batch not found or cannot be canceled")
}
// Return 204 No Content for successful cancellation
return ctx.NoContent(http.StatusNoContent)
}