197 lines
6.5 KiB
Go
197 lines
6.5 KiB
Go
package v1
|
|
|
|
// NoteHandler provides HTTP handlers for contact notes.
|
|
// Reference: Chatwoot app/controllers/api/v1/accounts/contacts/notes_controller.rb
|
|
//
|
|
// Routes:
|
|
// GET /api/v1/accounts/:account_id/contacts/:contact_id/notes -> List
|
|
// GET /api/v1/accounts/:account_id/contacts/:contact_id/notes/:id -> Show
|
|
// POST /api/v1/accounts/:account_id/contacts/:contact_id/notes -> Create
|
|
// PUT /api/v1/accounts/:account_id/contacts/:contact_id/notes/:id -> Update
|
|
// DELETE /api/v1/accounts/:account_id/contacts/:contact_id/notes/:id -> Destroy
|
|
|
|
import (
|
|
"strconv"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/gochat/gochat/internal/service"
|
|
"github.com/gochat/gochat/pkg/response"
|
|
)
|
|
|
|
// NoteHandler handles HTTP requests for contact notes.
|
|
type NoteHandler struct {
|
|
noteService *service.NoteService
|
|
}
|
|
|
|
// NewNoteHandler creates a new NoteHandler.
|
|
func NewNoteHandler(noteService *service.NoteService) *NoteHandler {
|
|
return &NoteHandler{noteService: noteService}
|
|
}
|
|
|
|
// noteRequest represents the JSON request body for creating/updating a note.
|
|
// Reference: Chatwoot `params.require(:note).permit(:content)`
|
|
type noteRequest struct {
|
|
Content string `json:"content" binding:"required"`
|
|
}
|
|
|
|
// List returns all notes for a contact.
|
|
// Reference: Chatwoot `def index`
|
|
func (h *NoteHandler) List(c *gin.Context) {
|
|
accountID, err := strconv.ParseUint(c.Param("account_id"), 10, 64)
|
|
if err != nil {
|
|
response.AbortWithStatusError(c, 400, response.ErrBadRequest, "Invalid account ID")
|
|
return
|
|
}
|
|
contactID, err := strconv.ParseUint(c.Param("contact_id"), 10, 64)
|
|
if err != nil {
|
|
response.AbortWithStatusError(c, 400, response.ErrBadRequest, "Invalid contact ID")
|
|
return
|
|
}
|
|
|
|
notes, err := h.noteService.List(uint(accountID), uint(contactID))
|
|
if err != nil {
|
|
response.AbortWithStatusError(c, 500, response.ErrInternal, "Failed to list notes")
|
|
return
|
|
}
|
|
response.OK(c, notes)
|
|
}
|
|
|
|
// Show returns a single note by ID.
|
|
// Reference: Chatwoot `def show; end`
|
|
func (h *NoteHandler) Show(c *gin.Context) {
|
|
accountID, err := strconv.ParseUint(c.Param("account_id"), 10, 64)
|
|
if err != nil {
|
|
response.AbortWithStatusError(c, 400, response.ErrBadRequest, "Invalid account ID")
|
|
return
|
|
}
|
|
contactID, err := strconv.ParseUint(c.Param("contact_id"), 10, 64)
|
|
if err != nil {
|
|
response.AbortWithStatusError(c, 400, response.ErrBadRequest, "Invalid contact ID")
|
|
return
|
|
}
|
|
noteID, err := strconv.ParseUint(c.Param("id"), 10, 64)
|
|
if err != nil {
|
|
response.AbortWithStatusError(c, 400, response.ErrBadRequest, "Invalid note ID")
|
|
return
|
|
}
|
|
|
|
note, err := h.noteService.Get(uint(accountID), uint(contactID), uint(noteID))
|
|
if err != nil {
|
|
response.AbortWithStatusError(c, 404, response.ErrNotFound, "Note not found")
|
|
return
|
|
}
|
|
response.OK(c, note)
|
|
}
|
|
|
|
// Create creates a new note for a contact.
|
|
// Reference: Chatwoot `def create`
|
|
// @note = @contact.notes.create!(note_params)
|
|
// note_params merges {contact_id: @contact.id, user_id: Current.user.id}
|
|
func (h *NoteHandler) Create(c *gin.Context) {
|
|
accountID, err := strconv.ParseUint(c.Param("account_id"), 10, 64)
|
|
if err != nil {
|
|
response.AbortWithStatusError(c, 400, response.ErrBadRequest, "Invalid account ID")
|
|
return
|
|
}
|
|
contactID, err := strconv.ParseUint(c.Param("contact_id"), 10, 64)
|
|
if err != nil {
|
|
response.AbortWithStatusError(c, 400, response.ErrBadRequest, "Invalid contact ID")
|
|
return
|
|
}
|
|
|
|
// Chatwoot: params.require(:note) → {"note": {"content": "..."}}
|
|
var wrapper struct {
|
|
Note noteRequest `json:"note"`
|
|
}
|
|
if err := c.ShouldBindJSON(&wrapper); err != nil {
|
|
response.AbortWithStatusError(c, 400, response.ErrBadRequest, "Content is required")
|
|
return
|
|
}
|
|
req := wrapper.Note
|
|
userID, exists := c.Get("user_id")
|
|
if !exists {
|
|
response.AbortWithStatusError(c, 401, response.ErrUnauthorized, "User not authenticated")
|
|
return
|
|
}
|
|
|
|
note, err := h.noteService.Create(uint(accountID), uint(contactID), userID.(uint), req.Content)
|
|
if err != nil {
|
|
if err == service.ErrNoteContentEmpty {
|
|
response.AbortWithStatusError(c, 400, response.ErrBadRequest, "Content is required")
|
|
return
|
|
}
|
|
response.AbortWithStatusError(c, 500, response.ErrInternal, "Failed to create note")
|
|
return
|
|
}
|
|
response.Created(c, note)
|
|
}
|
|
|
|
// Update updates an existing note's content.
|
|
// Reference: Chatwoot `def update; @note.update(note_params); end`
|
|
func (h *NoteHandler) Update(c *gin.Context) {
|
|
accountID, err := strconv.ParseUint(c.Param("account_id"), 10, 64)
|
|
if err != nil {
|
|
response.AbortWithStatusError(c, 400, response.ErrBadRequest, "Invalid account ID")
|
|
return
|
|
}
|
|
contactID, err := strconv.ParseUint(c.Param("contact_id"), 10, 64)
|
|
if err != nil {
|
|
response.AbortWithStatusError(c, 400, response.ErrBadRequest, "Invalid contact ID")
|
|
return
|
|
}
|
|
noteID, err := strconv.ParseUint(c.Param("id"), 10, 64)
|
|
if err != nil {
|
|
response.AbortWithStatusError(c, 400, response.ErrBadRequest, "Invalid note ID")
|
|
return
|
|
}
|
|
|
|
// Chatwoot: params.require(:note) → {"note": {"content": "..."}}
|
|
var wrapper struct {
|
|
Note noteRequest `json:"note"`
|
|
}
|
|
if err := c.ShouldBindJSON(&wrapper); err != nil {
|
|
response.AbortWithStatusError(c, 400, response.ErrBadRequest, "Content is required")
|
|
return
|
|
}
|
|
req := wrapper.Note
|
|
|
|
note, err := h.noteService.Update(uint(accountID), uint(contactID), uint(noteID), req.Content)
|
|
if err != nil {
|
|
if err == service.ErrNoteNotFound {
|
|
response.AbortWithStatusError(c, 404, response.ErrNotFound, "Note not found")
|
|
return
|
|
}
|
|
response.AbortWithStatusError(c, 500, response.ErrInternal, "Failed to update note")
|
|
return
|
|
}
|
|
response.OK(c, note)
|
|
}
|
|
|
|
// Destroy deletes a note.
|
|
// Reference: Chatwoot `def destroy; @note.destroy!; head :ok; end`
|
|
// Chatwoot returns 200 OK on destroy, NOT 204 NoContent.
|
|
func (h *NoteHandler) Destroy(c *gin.Context) {
|
|
accountID, err := strconv.ParseUint(c.Param("account_id"), 10, 64)
|
|
if err != nil {
|
|
response.AbortWithStatusError(c, 400, response.ErrBadRequest, "Invalid account ID")
|
|
return
|
|
}
|
|
contactID, err := strconv.ParseUint(c.Param("contact_id"), 10, 64)
|
|
if err != nil {
|
|
response.AbortWithStatusError(c, 400, response.ErrBadRequest, "Invalid contact ID")
|
|
return
|
|
}
|
|
noteID, err := strconv.ParseUint(c.Param("id"), 10, 64)
|
|
if err != nil {
|
|
response.AbortWithStatusError(c, 400, response.ErrBadRequest, "Invalid note ID")
|
|
return
|
|
}
|
|
|
|
err = h.noteService.Delete(uint(accountID), uint(contactID), uint(noteID))
|
|
if err != nil {
|
|
response.AbortWithStatusError(c, 404, response.ErrNotFound, "Note not found")
|
|
return
|
|
}
|
|
// Reference: Chatwoot returns `head :ok` → 200 OK with empty body
|
|
response.OK(c, gin.H{})
|
|
} |