package service import ( "context" "github.com/calendarapi/internal/models" "github.com/calendarapi/internal/repository" "github.com/calendarapi/internal/utils" "github.com/google/uuid" "github.com/jackc/pgx/v5" ) type AttendeeService struct { queries *repository.Queries calendar *CalendarService } func NewAttendeeService(queries *repository.Queries, calendar *CalendarService) *AttendeeService { return &AttendeeService{queries: queries, calendar: calendar} } func (s *AttendeeService) AddAttendees(ctx context.Context, userID uuid.UUID, eventID uuid.UUID, attendees []AddAttendeeRequest) (*models.Event, error) { ev, err := s.queries.GetEventByID(ctx, utils.ToPgUUID(eventID)) if err != nil { if err == pgx.ErrNoRows { return nil, models.ErrNotFound } return nil, models.ErrInternal } calID := utils.FromPgUUID(ev.CalendarID) role, err := s.calendar.GetRole(ctx, calID, userID) if err != nil { return nil, err } if role != "owner" && role != "editor" { return nil, models.ErrForbidden } for _, a := range attendees { _, err := s.queries.CreateAttendee(ctx, repository.CreateAttendeeParams{ ID: utils.ToPgUUID(uuid.New()), EventID: utils.ToPgUUID(eventID), UserID: optionalPgUUID(a.UserID), Email: utils.ToPgTextPtr(a.Email), }) if err != nil { return nil, models.ErrInternal } } reminders, _ := loadRemindersHelper(ctx, s.queries, eventID) atts, _ := loadAttendeesHelper(ctx, s.queries, eventID) attachments, _ := loadAttachmentsHelper(ctx, s.queries, eventID) return eventFromDB(ev, reminders, atts, attachments), nil } func (s *AttendeeService) UpdateStatus(ctx context.Context, userID uuid.UUID, eventID uuid.UUID, attendeeID uuid.UUID, status string) (*models.Event, error) { ev, err := s.queries.GetEventByID(ctx, utils.ToPgUUID(eventID)) if err != nil { if err == pgx.ErrNoRows { return nil, models.ErrNotFound } return nil, models.ErrInternal } calID := utils.FromPgUUID(ev.CalendarID) role, err := s.calendar.GetRole(ctx, calID, userID) if err != nil { return nil, err } att, err := s.queries.GetAttendeeByID(ctx, utils.ToPgUUID(attendeeID)) if err != nil { if err == pgx.ErrNoRows { return nil, models.ErrNotFound } return nil, models.ErrInternal } isOrganizer := role == "owner" || role == "editor" isOwnAttendee := att.UserID.Valid && utils.FromPgUUID(att.UserID) == userID if !isOrganizer && !isOwnAttendee { return nil, models.ErrForbidden } if status != "pending" && status != "accepted" && status != "declined" && status != "tentative" { return nil, models.NewValidationError("status must be pending, accepted, declined, or tentative") } _, err = s.queries.UpdateAttendeeStatus(ctx, repository.UpdateAttendeeStatusParams{ ID: utils.ToPgUUID(attendeeID), Status: status, }) if err != nil { return nil, models.ErrInternal } reminders, _ := loadRemindersHelper(ctx, s.queries, eventID) atts, _ := loadAttendeesHelper(ctx, s.queries, eventID) attachments, _ := loadAttachmentsHelper(ctx, s.queries, eventID) return eventFromDB(ev, reminders, atts, attachments), nil } func (s *AttendeeService) DeleteAttendee(ctx context.Context, userID uuid.UUID, eventID uuid.UUID, attendeeID uuid.UUID) error { ev, err := s.queries.GetEventByID(ctx, utils.ToPgUUID(eventID)) if err != nil { if err == pgx.ErrNoRows { return models.ErrNotFound } return models.ErrInternal } calID := utils.FromPgUUID(ev.CalendarID) role, err := s.calendar.GetRole(ctx, calID, userID) if err != nil { return err } if role != "owner" && role != "editor" { return models.ErrForbidden } return s.queries.DeleteAttendee(ctx, repository.DeleteAttendeeParams{ ID: utils.ToPgUUID(attendeeID), EventID: utils.ToPgUUID(eventID), }) } type AddAttendeeRequest struct { UserID *uuid.UUID Email *string }