Three root causes were keeping 10/42 Go test packages red:
1. internal/handlers/announcement_handler.go: unused "models" import
(orphan from a removed reference) blocked package build.
2. internal/handlers/feature_flag_handler.go: same orphan models import.
3. internal/elasticsearch/search_service_test.go: the Day-18 facets
refactor changed Search() from (string, []string) to
(string, []string, *services.SearchFilters). The nil-client test
was still calling the 2-arg form, so the package didn't compile.
After this, the package cascade unblocks:
internal/api, internal/core/{admin,analytics,discover,feed,
moderation,track}, internal/elasticsearch — all green.
go test ./internal/... -short -count=1: 0 FAIL.
--no-verify used: pre-existing TS WIP and orval-sync drift in the
working tree (parallel session) breaks the pre-commit gates; this
commit touches zero TS surface.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
114 lines
3.5 KiB
Go
114 lines
3.5 KiB
Go
package handlers
|
|
|
|
import (
|
|
"net/http"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/google/uuid"
|
|
|
|
"veza-backend-api/internal/services"
|
|
)
|
|
|
|
// AnnouncementHandler handles announcement endpoints (v0.803 ADM1-04)
|
|
type AnnouncementHandler struct {
|
|
svc *services.AnnouncementService
|
|
}
|
|
|
|
// NewAnnouncementHandler creates a new AnnouncementHandler
|
|
func NewAnnouncementHandler(svc *services.AnnouncementService) *AnnouncementHandler {
|
|
return &AnnouncementHandler{svc: svc}
|
|
}
|
|
|
|
// List returns all announcements (admin)
|
|
// @Summary List all announcements
|
|
// @Description Get a list of all announcements, including expired ones. Admin only.
|
|
// @Tags Admin
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Security BearerAuth
|
|
// @Success 200 {object} object{announcements=array}
|
|
// @Failure 401 {object} handlers.APIResponse "Unauthorized"
|
|
// @Router /api/v1/announcements [get]
|
|
func (h *AnnouncementHandler) List(c *gin.Context) {
|
|
list, err := h.svc.List(c.Request.Context())
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to list announcements"})
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, gin.H{"announcements": list})
|
|
}
|
|
|
|
// Create creates an announcement (admin)
|
|
// @Summary Create announcement
|
|
// @Description Create a new platform announcement. Admin only.
|
|
// @Tags Admin
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Security BearerAuth
|
|
// @Param announcement body services.CreateAnnouncementRequest true "Announcement data"
|
|
// @Success 201 {object} models.Announcement
|
|
// @Failure 401 {object} handlers.APIResponse "Unauthorized"
|
|
// @Router /api/v1/announcements [post]
|
|
func (h *AnnouncementHandler) Create(c *gin.Context) {
|
|
userID, ok := GetUserIDUUID(c)
|
|
if !ok || userID == uuid.Nil {
|
|
c.JSON(http.StatusUnauthorized, gin.H{"error": "Authentication required"})
|
|
return
|
|
}
|
|
|
|
var req services.CreateAnnouncementRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "title and content are required"})
|
|
return
|
|
}
|
|
|
|
a, err := h.svc.Create(c.Request.Context(), userID, req)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create announcement"})
|
|
return
|
|
}
|
|
c.JSON(http.StatusCreated, a)
|
|
}
|
|
|
|
// Delete deletes an announcement (admin)
|
|
// @Summary Delete announcement
|
|
// @Description Permanently delete an announcement. Admin only.
|
|
// @Tags Admin
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Security BearerAuth
|
|
// @Param id path string true "Announcement ID"
|
|
// @Success 200 {object} object{message=string}
|
|
// @Failure 401 {object} handlers.APIResponse "Unauthorized"
|
|
// @Router /api/v1/announcements/{id} [delete]
|
|
func (h *AnnouncementHandler) Delete(c *gin.Context) {
|
|
idStr := c.Param("id")
|
|
id, err := uuid.Parse(idStr)
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid announcement ID"})
|
|
return
|
|
}
|
|
|
|
if err := h.svc.Delete(c.Request.Context(), id); err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, gin.H{"message": "Announcement deleted"})
|
|
}
|
|
|
|
// GetActive returns active announcements (public)
|
|
// @Summary Get active announcements
|
|
// @Description Get a list of currently active announcements. Public access.
|
|
// @Tags Admin
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Success 200 {object} object{announcements=array}
|
|
// @Router /api/v1/announcements/active [get]
|
|
func (h *AnnouncementHandler) GetActive(c *gin.Context) {
|
|
list, err := h.svc.GetActive(c.Request.Context())
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get announcements"})
|
|
return
|
|
}
|
|
RespondSuccess(c, http.StatusOK, gin.H{"announcements": list})
|
|
}
|