Init All Docs
This commit is contained in:
@@ -12,10 +12,13 @@ import (
|
||||
|
||||
type DispositionRouteService interface {
|
||||
Create(ctx context.Context, req *contract.CreateDispositionRouteRequest) (*contract.DispositionRouteResponse, error)
|
||||
CreateOrUpdate(ctx context.Context, req *contract.CreateDispositionRouteRequest) (*contract.BulkCreateDispositionRouteResponse, error)
|
||||
Update(ctx context.Context, id uuid.UUID, req *contract.UpdateDispositionRouteRequest) (*contract.DispositionRouteResponse, error)
|
||||
Get(ctx context.Context, id uuid.UUID) (*contract.DispositionRouteResponse, error)
|
||||
ListByFromDept(ctx context.Context, from uuid.UUID) (*contract.ListDispositionRoutesResponse, error)
|
||||
SetActive(ctx context.Context, id uuid.UUID, active bool) error
|
||||
ListGrouped(ctx context.Context) (*contract.ListDispositionRoutesGroupedResponse, error)
|
||||
ListAll(ctx context.Context) (*contract.ListDispositionRoutesDetailedResponse, error)
|
||||
}
|
||||
|
||||
type DispositionRouteHandler struct{ svc DispositionRouteService }
|
||||
@@ -24,18 +27,61 @@ func NewDispositionRouteHandler(svc DispositionRouteService) *DispositionRouteHa
|
||||
return &DispositionRouteHandler{svc: svc}
|
||||
}
|
||||
|
||||
// Create handles both single and bulk route creation with upsert logic
|
||||
func (h *DispositionRouteHandler) Create(c *gin.Context) {
|
||||
var req contract.CreateDispositionRouteRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(400, &contract.ErrorResponse{Error: "invalid body", Code: 400})
|
||||
c.JSON(400, &contract.ErrorResponse{Error: "invalid body: " + err.Error(), Code: 400})
|
||||
return
|
||||
}
|
||||
resp, err := h.svc.Create(c.Request.Context(), &req)
|
||||
|
||||
// Validate request
|
||||
if len(req.ToDepartmentIDs) == 0 {
|
||||
c.JSON(400, &contract.ErrorResponse{Error: "to_department_ids cannot be empty", Code: 400})
|
||||
return
|
||||
}
|
||||
|
||||
// If single route, use Create for backward compatibility
|
||||
if len(req.ToDepartmentIDs) == 1 {
|
||||
resp, err := h.svc.Create(c.Request.Context(), &req)
|
||||
if err != nil {
|
||||
c.JSON(500, &contract.ErrorResponse{Error: err.Error(), Code: 500})
|
||||
return
|
||||
}
|
||||
c.JSON(201, contract.BuildSuccessResponse(resp))
|
||||
return
|
||||
}
|
||||
|
||||
// For multiple routes, use bulk create/update
|
||||
bulkResp, err := h.svc.CreateOrUpdate(c.Request.Context(), &req)
|
||||
if err != nil {
|
||||
c.JSON(500, &contract.ErrorResponse{Error: err.Error(), Code: 500})
|
||||
return
|
||||
}
|
||||
c.JSON(201, contract.BuildSuccessResponse(resp))
|
||||
c.JSON(201, contract.BuildSuccessResponse(bulkResp))
|
||||
}
|
||||
|
||||
// BulkCreateOrUpdate explicitly handles bulk create/update operations
|
||||
func (h *DispositionRouteHandler) BulkCreateOrUpdate(c *gin.Context) {
|
||||
var req contract.CreateDispositionRouteRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(400, &contract.ErrorResponse{Error: "invalid body: " + err.Error(), Code: 400})
|
||||
return
|
||||
}
|
||||
|
||||
// Validate request
|
||||
if len(req.ToDepartmentIDs) == 0 {
|
||||
c.JSON(400, &contract.ErrorResponse{Error: "to_department_ids cannot be empty", Code: 400})
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := h.svc.CreateOrUpdate(c.Request.Context(), &req)
|
||||
if err != nil {
|
||||
c.JSON(500, &contract.ErrorResponse{Error: err.Error(), Code: 500})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(200, contract.BuildSuccessResponse(resp))
|
||||
}
|
||||
|
||||
func (h *DispositionRouteHandler) Update(c *gin.Context) {
|
||||
@@ -96,3 +142,23 @@ func (h *DispositionRouteHandler) SetActive(c *gin.Context) {
|
||||
}
|
||||
c.JSON(200, &contract.SuccessResponse{Message: "updated"})
|
||||
}
|
||||
|
||||
// ListGrouped returns all disposition routes grouped by from_department_id
|
||||
func (h *DispositionRouteHandler) ListGrouped(c *gin.Context) {
|
||||
resp, err := h.svc.ListGrouped(c.Request.Context())
|
||||
if err != nil {
|
||||
c.JSON(500, &contract.ErrorResponse{Error: err.Error(), Code: 500})
|
||||
return
|
||||
}
|
||||
c.JSON(200, contract.BuildSuccessResponse(resp))
|
||||
}
|
||||
|
||||
// ListAll returns all disposition routes with department details
|
||||
func (h *DispositionRouteHandler) ListAll(c *gin.Context) {
|
||||
resp, err := h.svc.ListAll(c.Request.Context())
|
||||
if err != nil {
|
||||
c.JSON(500, &contract.ErrorResponse{Error: err.Error(), Code: 500})
|
||||
return
|
||||
}
|
||||
c.JSON(200, contract.BuildSuccessResponse(resp))
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"eslogad-be/internal/appcontext"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"eslogad-be/internal/contract"
|
||||
|
||||
@@ -16,14 +17,23 @@ type LetterService interface {
|
||||
CreateIncomingLetter(ctx context.Context, req *contract.CreateIncomingLetterRequest) (*contract.IncomingLetterResponse, error)
|
||||
GetIncomingLetterByID(ctx context.Context, id uuid.UUID) (*contract.IncomingLetterResponse, error)
|
||||
ListIncomingLetters(ctx context.Context, req *contract.ListIncomingLettersRequest) (*contract.ListIncomingLettersResponse, error)
|
||||
GetLetterUnreadCounts(ctx context.Context) (*contract.LetterUnreadCountResponse, error)
|
||||
MarkIncomingLetterAsRead(ctx context.Context, letterID uuid.UUID) (*contract.MarkLetterReadResponse, error)
|
||||
MarkOutgoingLetterAsRead(ctx context.Context, letterID uuid.UUID) (*contract.MarkLetterReadResponse, error)
|
||||
UpdateIncomingLetter(ctx context.Context, id uuid.UUID, req *contract.UpdateIncomingLetterRequest) (*contract.IncomingLetterResponse, error)
|
||||
SoftDeleteIncomingLetter(ctx context.Context, id uuid.UUID) error
|
||||
BulkArchiveIncomingLetters(ctx context.Context, letterIDs []uuid.UUID) (*contract.BulkArchiveLettersResponse, error)
|
||||
|
||||
CreateDispositions(ctx context.Context, req *contract.CreateLetterDispositionRequest) (*contract.ListDispositionsResponse, error)
|
||||
GetEnhancedDispositionsByLetter(ctx context.Context, letterID uuid.UUID) (*contract.ListEnhancedDispositionsResponse, error)
|
||||
|
||||
CreateDiscussion(ctx context.Context, letterID uuid.UUID, req *contract.CreateLetterDiscussionRequest) (*contract.LetterDiscussionResponse, error)
|
||||
UpdateDiscussion(ctx context.Context, letterID uuid.UUID, discussionID uuid.UUID, req *contract.UpdateLetterDiscussionRequest) (*contract.LetterDiscussionResponse, error)
|
||||
|
||||
GetDepartmentDispositionStatus(ctx context.Context, req *contract.GetDepartmentDispositionStatusRequest) (*contract.ListDepartmentDispositionStatusResponse, error)
|
||||
UpdateDispositionStatus(ctx context.Context, req *contract.UpdateDispositionStatusRequest) (*contract.DepartmentDispositionStatusResponse, error)
|
||||
|
||||
GetLetterCTA(ctx context.Context, letterID uuid.UUID) (*contract.LetterCTAResponse, error)
|
||||
}
|
||||
|
||||
type LetterHandler struct {
|
||||
@@ -119,6 +129,46 @@ func (h *LetterHandler) ListIncomingLetters(c *gin.Context) {
|
||||
h.respondSuccess(c, http.StatusOK, resp)
|
||||
}
|
||||
|
||||
func (h *LetterHandler) GetLetterUnreadCounts(c *gin.Context) {
|
||||
resp, err := h.svc.GetLetterUnreadCounts(c.Request.Context())
|
||||
if err != nil {
|
||||
h.handleServiceError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
h.respondSuccess(c, http.StatusOK, resp)
|
||||
}
|
||||
|
||||
func (h *LetterHandler) MarkIncomingLetterAsRead(c *gin.Context) {
|
||||
id, ok := h.parseUUID(c, "id")
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := h.svc.MarkIncomingLetterAsRead(c.Request.Context(), id)
|
||||
if err != nil {
|
||||
h.handleServiceError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
h.respondSuccess(c, http.StatusOK, resp)
|
||||
}
|
||||
|
||||
func (h *LetterHandler) MarkOutgoingLetterAsRead(c *gin.Context) {
|
||||
id, ok := h.parseUUID(c, "id")
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := h.svc.MarkOutgoingLetterAsRead(c.Request.Context(), id)
|
||||
if err != nil {
|
||||
h.handleServiceError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
h.respondSuccess(c, http.StatusOK, resp)
|
||||
}
|
||||
|
||||
func (h *LetterHandler) parseListRequest(c *gin.Context) *contract.ListIncomingLettersRequest {
|
||||
//appCtx := appcontext.FromGinContext(c)
|
||||
//departmentID := appCtx.DepartmentID
|
||||
@@ -145,10 +195,50 @@ func (h *LetterHandler) parseListRequest(c *gin.Context) *contract.ListIncomingL
|
||||
if status := c.Query("status"); status != "" {
|
||||
req.Status = &status
|
||||
}
|
||||
|
||||
if query := c.Query("q"); query != "" {
|
||||
req.Query = &query
|
||||
}
|
||||
|
||||
// Parse is_read filter
|
||||
if isReadStr := c.Query("is_read"); isReadStr != "" {
|
||||
isRead := isReadStr == "true" || isReadStr == "1"
|
||||
req.IsRead = &isRead
|
||||
}
|
||||
|
||||
// Parse priority_ids filter
|
||||
if priorityIDsStr := c.QueryArray("priority_ids[]"); len(priorityIDsStr) > 0 {
|
||||
priorityIDs := make([]uuid.UUID, 0, len(priorityIDsStr))
|
||||
for _, idStr := range priorityIDsStr {
|
||||
if id, err := uuid.Parse(idStr); err == nil {
|
||||
priorityIDs = append(priorityIDs, id)
|
||||
}
|
||||
}
|
||||
req.PriorityIDs = priorityIDs
|
||||
} else if priorityIDStr := c.Query("priority_ids"); priorityIDStr != "" {
|
||||
// Also support comma-separated format
|
||||
idStrs := strings.Split(priorityIDStr, ",")
|
||||
priorityIDs := make([]uuid.UUID, 0, len(idStrs))
|
||||
for _, idStr := range idStrs {
|
||||
if id, err := uuid.Parse(strings.TrimSpace(idStr)); err == nil {
|
||||
priorityIDs = append(priorityIDs, id)
|
||||
}
|
||||
}
|
||||
req.PriorityIDs = priorityIDs
|
||||
}
|
||||
|
||||
// Parse is_dispositioned filter
|
||||
if isDispositionedStr := c.Query("is_dispositioned"); isDispositionedStr != "" {
|
||||
isDispositioned := isDispositionedStr == "true" || isDispositionedStr == "1"
|
||||
req.IsDispositioned = &isDispositioned
|
||||
}
|
||||
|
||||
// Parse is_archived filter
|
||||
if isArchivedStr := c.Query("is_archived"); isArchivedStr != "" {
|
||||
isArchived := isArchivedStr == "true" || isArchivedStr == "1"
|
||||
req.IsArchived = &isArchived
|
||||
}
|
||||
|
||||
//req.DepartmentID = &departmentID
|
||||
|
||||
return req
|
||||
@@ -268,3 +358,82 @@ func (h *LetterHandler) UpdateDiscussion(c *gin.Context) {
|
||||
|
||||
h.respondSuccess(c, http.StatusOK, resp)
|
||||
}
|
||||
|
||||
func (h *LetterHandler) GetDepartmentDispositionStatus(c *gin.Context) {
|
||||
letterID, ok := h.parseUUID(c, "letter_id")
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
departmentID := appcontext.FromGinContext(c.Request.Context()).DepartmentID
|
||||
|
||||
req := &contract.GetDepartmentDispositionStatusRequest{
|
||||
LetterIncomingID: letterID,
|
||||
DepartmentID: departmentID,
|
||||
}
|
||||
|
||||
resp, err := h.svc.GetDepartmentDispositionStatus(c.Request.Context(), req)
|
||||
if err != nil {
|
||||
h.handleServiceError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
h.respondSuccess(c, http.StatusOK, resp)
|
||||
}
|
||||
|
||||
func (h *LetterHandler) UpdateDispositionStatus(c *gin.Context) {
|
||||
letterID, ok := h.parseUUID(c, "letter_id")
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
var req contract.UpdateDispositionStatusRequest
|
||||
if !h.bindJSON(c, &req) {
|
||||
return
|
||||
}
|
||||
|
||||
req.LetterIncomingID = letterID
|
||||
|
||||
resp, err := h.svc.UpdateDispositionStatus(c.Request.Context(), &req)
|
||||
if err != nil {
|
||||
h.handleServiceError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
h.respondSuccess(c, http.StatusOK, resp)
|
||||
}
|
||||
|
||||
func (h *LetterHandler) GetLetterCTA(c *gin.Context) {
|
||||
letterID, ok := h.parseUUID(c, "letter_id")
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := h.svc.GetLetterCTA(c.Request.Context(), letterID)
|
||||
if err != nil {
|
||||
h.handleServiceError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
h.respondSuccess(c, http.StatusOK, resp)
|
||||
}
|
||||
|
||||
func (h *LetterHandler) BulkArchiveIncomingLetters(c *gin.Context) {
|
||||
var req contract.BulkArchiveLettersRequest
|
||||
if !h.bindJSON(c, &req) {
|
||||
return
|
||||
}
|
||||
|
||||
if len(req.LetterIDs) == 0 {
|
||||
h.respondError(c, http.StatusBadRequest, "at least one letter ID is required")
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := h.svc.BulkArchiveIncomingLetters(c.Request.Context(), req.LetterIDs)
|
||||
if err != nil {
|
||||
h.handleServiceError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
h.respondSuccess(c, http.StatusOK, resp)
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ type LetterOutgoingService interface {
|
||||
GetLetterApprovals(ctx context.Context, letterID uuid.UUID) (*contract.GetLetterApprovalsResponse, error)
|
||||
GetApprovalDiscussions(ctx context.Context, letterID uuid.UUID) (*contract.OutgoingLetterApprovalDiscussionsResponse, error)
|
||||
GetApprovalTimeline(ctx context.Context, letterID uuid.UUID) (*contract.ApprovalTimelineResponse, error)
|
||||
BulkArchiveOutgoingLetters(ctx context.Context, letterIDs []uuid.UUID) (*contract.BulkArchiveLettersResponse, error)
|
||||
}
|
||||
|
||||
type LetterOutgoingHandler struct {
|
||||
@@ -479,3 +480,24 @@ func (h *LetterOutgoingHandler) GetApprovalTimeline(c *gin.Context) {
|
||||
|
||||
c.JSON(http.StatusOK, contract.BuildSuccessResponse(resp))
|
||||
}
|
||||
|
||||
func (h *LetterOutgoingHandler) BulkArchiveOutgoingLetters(c *gin.Context) {
|
||||
var req contract.BulkArchiveLettersRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid request body", Code: http.StatusBadRequest})
|
||||
return
|
||||
}
|
||||
|
||||
if len(req.LetterIDs) == 0 {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "at least one letter ID is required", Code: http.StatusBadRequest})
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := h.svc.BulkArchiveOutgoingLetters(c.Request.Context(), req.LetterIDs)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{Error: err.Error(), Code: http.StatusInternalServerError})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, contract.BuildSuccessResponse(resp))
|
||||
}
|
||||
|
||||
@@ -1,190 +0,0 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"eslogad-be/internal/contract"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
// MockMasterService is a mock implementation of MasterService
|
||||
type MockMasterService struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
func (m *MockMasterService) CreateLabel(ctx context.Context, req *contract.CreateLabelRequest) (*contract.LabelResponse, error) {
|
||||
args := m.Called(ctx, req)
|
||||
return args.Get(0).(*contract.LabelResponse), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockMasterService) UpdateLabel(ctx context.Context, id uuid.UUID, req *contract.UpdateLabelRequest) (*contract.LabelResponse, error) {
|
||||
args := m.Called(ctx, id, req)
|
||||
return args.Get(0).(*contract.LabelResponse), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockMasterService) DeleteLabel(ctx context.Context, id uuid.UUID) error {
|
||||
args := m.Called(ctx, id)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (m *MockMasterService) ListLabels(ctx context.Context) (*contract.ListLabelsResponse, error) {
|
||||
args := m.Called(ctx)
|
||||
return args.Get(0).(*contract.ListLabelsResponse), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockMasterService) CreatePriority(ctx context.Context, req *contract.CreatePriorityRequest) (*contract.PriorityResponse, error) {
|
||||
args := m.Called(ctx, req)
|
||||
return args.Get(0).(*contract.PriorityResponse), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockMasterService) UpdatePriority(ctx context.Context, id uuid.UUID, req *contract.UpdatePriorityRequest) (*contract.PriorityResponse, error) {
|
||||
args := m.Called(ctx, id, req)
|
||||
return args.Get(0).(*contract.PriorityResponse), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockMasterService) DeletePriority(ctx context.Context, id uuid.UUID) error {
|
||||
args := m.Called(ctx, id)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (m *MockMasterService) ListPriorities(ctx context.Context) (*contract.ListPrioritiesResponse, error) {
|
||||
args := m.Called(ctx)
|
||||
return args.Get(0).(*contract.ListPrioritiesResponse), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockMasterService) CreateInstitution(ctx context.Context, req *contract.CreateInstitutionRequest) (*contract.InstitutionResponse, error) {
|
||||
args := m.Called(ctx, req)
|
||||
return args.Get(0).(*contract.InstitutionResponse), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockMasterService) UpdateInstitution(ctx context.Context, id uuid.UUID, req *contract.UpdateInstitutionRequest) (*contract.InstitutionResponse, error) {
|
||||
args := m.Called(ctx, id, req)
|
||||
return args.Get(0).(*contract.InstitutionResponse), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockMasterService) DeleteInstitution(ctx context.Context, id uuid.UUID) error {
|
||||
args := m.Called(ctx, id)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (m *MockMasterService) ListInstitutions(ctx context.Context, req *contract.ListInstitutionsRequest) (*contract.ListInstitutionsResponse, error) {
|
||||
args := m.Called(ctx, req)
|
||||
return args.Get(0).(*contract.ListInstitutionsResponse), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockMasterService) CreateDispositionAction(ctx context.Context, req *contract.CreateDispositionActionRequest) (*contract.DispositionActionResponse, error) {
|
||||
args := m.Called(ctx, req)
|
||||
return args.Get(0).(*contract.DispositionActionResponse), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockMasterService) UpdateDispositionAction(ctx context.Context, id uuid.UUID, req *contract.UpdateDispositionActionRequest) (*contract.DispositionActionResponse, error) {
|
||||
args := m.Called(ctx, id, req)
|
||||
return args.Get(0).(*contract.DispositionActionResponse), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockMasterService) DeleteDispositionAction(ctx context.Context, id uuid.UUID) error {
|
||||
args := m.Called(ctx, id)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (m *MockMasterService) ListDispositionActions(ctx context.Context) (*contract.ListDispositionActionsResponse, error) {
|
||||
args := m.Called(ctx)
|
||||
return args.Get(0).(*contract.ListDispositionActionsResponse), args.Error(1)
|
||||
}
|
||||
|
||||
func TestMasterHandler_ListInstitutions_WithSearch(t *testing.T) {
|
||||
// Setup
|
||||
gin.SetMode(gin.TestMode)
|
||||
mockService := new(MockMasterService)
|
||||
handler := NewMasterHandler(mockService)
|
||||
|
||||
// Test data
|
||||
searchTerm := "university"
|
||||
expectedResponse := &contract.ListInstitutionsResponse{
|
||||
Institutions: []contract.InstitutionResponse{
|
||||
{
|
||||
ID: "123",
|
||||
Name: "Test University",
|
||||
Type: "university",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// Setup mock expectations
|
||||
mockService.On("ListInstitutions", mock.Anything, &contract.ListInstitutionsRequest{
|
||||
Search: &searchTerm,
|
||||
}).Return(expectedResponse, nil)
|
||||
|
||||
// Create request
|
||||
req, _ := http.NewRequest("GET", "/institutions?search="+searchTerm, nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
// Create gin context
|
||||
c, _ := gin.CreateTestContext(w)
|
||||
c.Request = req
|
||||
|
||||
// Execute
|
||||
handler.ListInstitutions(c)
|
||||
|
||||
// Assertions
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
|
||||
var response map[string]interface{}
|
||||
err := json.Unmarshal(w.Body.Bytes(), &response)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Verify mock was called correctly
|
||||
mockService.AssertExpectations(t)
|
||||
}
|
||||
|
||||
func TestMasterHandler_ListInstitutions_WithoutSearch(t *testing.T) {
|
||||
// Setup
|
||||
gin.SetMode(gin.TestMode)
|
||||
mockService := new(MockMasterService)
|
||||
handler := NewMasterHandler(mockService)
|
||||
|
||||
// Test data
|
||||
expectedResponse := &contract.ListInstitutionsResponse{
|
||||
Institutions: []contract.InstitutionResponse{
|
||||
{
|
||||
ID: "123",
|
||||
Name: "Test Institution",
|
||||
Type: "company",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// Setup mock expectations
|
||||
mockService.On("ListInstitutions", mock.Anything, &contract.ListInstitutionsRequest{
|
||||
Search: nil,
|
||||
}).Return(expectedResponse, nil)
|
||||
|
||||
// Create request
|
||||
req, _ := http.NewRequest("GET", "/institutions", nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
// Create gin context
|
||||
c, _ := gin.CreateTestContext(w)
|
||||
c.Request = req
|
||||
|
||||
// Execute
|
||||
handler.ListInstitutions(c)
|
||||
|
||||
// Assertions
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
|
||||
var response map[string]interface{}
|
||||
err := json.Unmarshal(w.Body.Bytes(), &response)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Verify mock was called correctly
|
||||
mockService.AssertExpectations(t)
|
||||
}
|
||||
@@ -0,0 +1,230 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"eslogad-be/internal/contract"
|
||||
"eslogad-be/internal/service"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type NotificationHandler struct {
|
||||
notificationService service.NotificationService
|
||||
}
|
||||
|
||||
func NewNotificationHandler(notificationService service.NotificationService) *NotificationHandler {
|
||||
return &NotificationHandler{
|
||||
notificationService: notificationService,
|
||||
}
|
||||
}
|
||||
|
||||
// TriggerNotification handles single notification trigger
|
||||
func (h *NotificationHandler) TriggerNotification(c *gin.Context) {
|
||||
var req contract.TriggerNotificationRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := h.notificationService.TriggerNotification(c.Request.Context(), &req)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
if !resp.Success {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"success": false,
|
||||
"message": resp.Message,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, resp)
|
||||
}
|
||||
|
||||
// BulkTriggerNotification handles bulk notification trigger
|
||||
func (h *NotificationHandler) BulkTriggerNotification(c *gin.Context) {
|
||||
var req contract.BulkTriggerNotificationRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := h.notificationService.BulkTriggerNotification(c.Request.Context(), &req)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, resp)
|
||||
}
|
||||
|
||||
// GetSubscriber retrieves subscriber information
|
||||
func (h *NotificationHandler) GetSubscriber(c *gin.Context) {
|
||||
userIDStr := c.Param("userId")
|
||||
userID, err := uuid.Parse(userIDStr)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid user ID"})
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := h.notificationService.GetSubscriber(c.Request.Context(), userID)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, resp)
|
||||
}
|
||||
|
||||
// UpdateSubscriberChannel updates subscriber channel credentials
|
||||
func (h *NotificationHandler) UpdateSubscriberChannel(c *gin.Context) {
|
||||
var req contract.UpdateSubscriberChannelRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := h.notificationService.UpdateSubscriberChannel(c.Request.Context(), &req)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
if !resp.Success {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"success": false,
|
||||
"message": resp.Message,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, resp)
|
||||
}
|
||||
|
||||
// TriggerNotificationForCurrentUser triggers notification for the authenticated user
|
||||
func (h *NotificationHandler) TriggerNotificationForCurrentUser(c *gin.Context) {
|
||||
// Get current user ID from context (set by auth middleware)
|
||||
userIDInterface, exists := c.Get("user_id")
|
||||
if !exists {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "user not authenticated"})
|
||||
return
|
||||
}
|
||||
|
||||
userID, ok := userIDInterface.(uuid.UUID)
|
||||
if !ok {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "invalid user ID in context"})
|
||||
return
|
||||
}
|
||||
|
||||
var req struct {
|
||||
TemplateID string `json:"template_id" validate:"required"`
|
||||
TemplateData map[string]interface{} `json:"template_data,omitempty"`
|
||||
Overrides *contract.NotificationOverrides `json:"overrides,omitempty"`
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// Create trigger request with current user ID
|
||||
triggerReq := &contract.TriggerNotificationRequest{
|
||||
UserID: userID,
|
||||
TemplateID: req.TemplateID,
|
||||
TemplateData: req.TemplateData,
|
||||
Overrides: req.Overrides,
|
||||
}
|
||||
|
||||
resp, err := h.notificationService.TriggerNotification(c.Request.Context(), triggerReq)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
if !resp.Success {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"success": false,
|
||||
"message": resp.Message,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, resp)
|
||||
}
|
||||
|
||||
// GetCurrentUserSubscriber retrieves subscriber information for the authenticated user
|
||||
func (h *NotificationHandler) GetCurrentUserSubscriber(c *gin.Context) {
|
||||
// Get current user ID from context (set by auth middleware)
|
||||
userIDInterface, exists := c.Get("user_id")
|
||||
if !exists {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "user not authenticated"})
|
||||
return
|
||||
}
|
||||
|
||||
userID, ok := userIDInterface.(uuid.UUID)
|
||||
if !ok {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "invalid user ID in context"})
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := h.notificationService.GetSubscriber(c.Request.Context(), userID)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, resp)
|
||||
}
|
||||
|
||||
// UpdateCurrentUserSubscriberChannel updates channel credentials for the authenticated user
|
||||
func (h *NotificationHandler) UpdateCurrentUserSubscriberChannel(c *gin.Context) {
|
||||
// Get current user ID from context (set by auth middleware)
|
||||
userIDInterface, exists := c.Get("user_id")
|
||||
if !exists {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "user not authenticated"})
|
||||
return
|
||||
}
|
||||
|
||||
userID, ok := userIDInterface.(uuid.UUID)
|
||||
if !ok {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "invalid user ID in context"})
|
||||
return
|
||||
}
|
||||
|
||||
var req struct {
|
||||
Channel string `json:"channel" validate:"required,oneof=email sms push chat in_app"`
|
||||
Credentials map[string]interface{} `json:"credentials" validate:"required"`
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// Create update request with current user ID
|
||||
updateReq := &contract.UpdateSubscriberChannelRequest{
|
||||
UserID: userID,
|
||||
Channel: req.Channel,
|
||||
Credentials: req.Credentials,
|
||||
}
|
||||
|
||||
resp, err := h.notificationService.UpdateSubscriberChannel(c.Request.Context(), updateReq)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
if !resp.Success {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"success": false,
|
||||
"message": resp.Message,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, resp)
|
||||
}
|
||||
Reference in New Issue
Block a user