Files
Jay Brown 17fc813823 Merged in feature/mutable-metadata1 (pull request #221)
M1, M2 and M3 complete

* M1, M2 and M3 complete

* review changes

* docs

* docs
2026-04-16 23:11:26 +00:00

75 lines
2.9 KiB
Go

// Package queryapi: Milestone 3 handler for
// POST /super-admin/documents/{id}/reset-metadata.
//
// This file is the M3-owned partition (see tracking §2.9). It must not
// introduce new types into models.go or customschemas.go.
package queryapi
import (
"errors"
"log/slog"
"net/http"
"queryorchestration/internal/cognitoauth"
"queryorchestration/internal/customschema"
"github.com/google/uuid"
"github.com/labstack/echo/v4"
"github.com/oapi-codegen/nullable"
openapi_types "github.com/oapi-codegen/runtime/types"
)
// ResetDocumentMetadata is POST /super-admin/documents/{id}/reset-metadata.
// It wipes the document's custom metadata history and clears the schema
// binding in a single atomic transaction. The operation is idempotent;
// already-clean documents return 200 with metadataVersionsDeleted: 0.
// Restricted to super_admin (enforced by Permit.io middleware upstream).
func (s *Controllers) ResetDocumentMetadata(ctx echo.Context, id openapi_types.UUID) error {
actor, ok := cognitoauth.GetUserSubject(ctx)
if !ok {
return echo.NewHTTPError(http.StatusUnauthorized, "missing user subject")
}
result, err := s.svc.CustomSchema.ResetDocumentMetadata(ctx.Request().Context(), uuid.UUID(id), actor)
if err != nil {
return mapResetError(err)
}
resp := DocumentMetadataResetResponse{
DocumentId: openapi_types.UUID(result.DocumentID),
MetadataVersionsDeleted: int32(result.MetadataVersionsDeleted), //nolint:gosec // bounded by row count
ResetAt: result.ResetAt,
ResetBy: result.ResetBy,
}
if result.PreviousSchemaID != nil {
resp.PreviousSchemaId = nullable.NewNullableWithValue(openapi_types.UUID(*result.PreviousSchemaID))
} else {
resp.PreviousSchemaId = nullable.NewNullNullable[openapi_types.UUID]()
}
if result.PreviousSchemaName != nil {
resp.PreviousSchemaName = nullable.NewNullableWithValue(*result.PreviousSchemaName)
} else {
resp.PreviousSchemaName = nullable.NewNullNullable[string]()
}
if result.PreviousSchemaVersion != nil {
resp.PreviousSchemaVersion = nullable.NewNullableWithValue(int32(*result.PreviousSchemaVersion)) //nolint:gosec // schema version is a small counter
} else {
resp.PreviousSchemaVersion = nullable.NewNullNullable[int32]()
}
return ctx.JSON(http.StatusOK, resp)
}
// mapResetError converts reset-specific service sentinels to HTTP errors.
// Errors not recognized here are treated as unexpected server failures.
func mapResetError(err error) error {
switch {
case errors.Is(err, customschema.ErrDocumentNotFound):
return echo.NewHTTPError(http.StatusNotFound, "document not found")
case errors.Is(err, customschema.ErrMutualExclusivityViolation):
return echo.NewHTTPError(http.StatusConflict, err.Error())
default:
slog.Error("reset document metadata: unexpected error", "error", err)
return echo.NewHTTPError(http.StatusInternalServerError, "internal server error")
}
}