first commit
Made-with: Cursor
This commit is contained in:
172
internal/service/contact.go
Normal file
172
internal/service/contact.go
Normal file
@@ -0,0 +1,172 @@
|
||||
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 ContactService struct {
|
||||
queries *repository.Queries
|
||||
audit *AuditService
|
||||
}
|
||||
|
||||
func NewContactService(queries *repository.Queries, audit *AuditService) *ContactService {
|
||||
return &ContactService{queries: queries, audit: audit}
|
||||
}
|
||||
|
||||
func (s *ContactService) Create(ctx context.Context, userID uuid.UUID, req CreateContactRequest) (*models.Contact, error) {
|
||||
if req.FirstName == nil && req.LastName == nil && req.Email == nil && req.Phone == nil {
|
||||
return nil, models.NewValidationError("at least one identifying field required")
|
||||
}
|
||||
if req.Email != nil {
|
||||
if err := utils.ValidateEmail(*req.Email); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
id := uuid.New()
|
||||
c, err := s.queries.CreateContact(ctx, repository.CreateContactParams{
|
||||
ID: utils.ToPgUUID(id),
|
||||
OwnerID: utils.ToPgUUID(userID),
|
||||
FirstName: utils.ToPgTextPtr(req.FirstName),
|
||||
LastName: utils.ToPgTextPtr(req.LastName),
|
||||
Email: utils.ToPgTextPtr(req.Email),
|
||||
Phone: utils.ToPgTextPtr(req.Phone),
|
||||
Company: utils.ToPgTextPtr(req.Company),
|
||||
Notes: utils.ToPgTextPtr(req.Notes),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, models.ErrInternal
|
||||
}
|
||||
|
||||
s.audit.Log(ctx, "contact", id, "CREATE_CONTACT", userID)
|
||||
return contactFromDB(c), nil
|
||||
}
|
||||
|
||||
func (s *ContactService) Get(ctx context.Context, userID uuid.UUID, contactID uuid.UUID) (*models.Contact, error) {
|
||||
c, err := s.queries.GetContactByID(ctx, repository.GetContactByIDParams{
|
||||
ID: utils.ToPgUUID(contactID),
|
||||
OwnerID: utils.ToPgUUID(userID),
|
||||
})
|
||||
if err != nil {
|
||||
if err == pgx.ErrNoRows {
|
||||
return nil, models.ErrNotFound
|
||||
}
|
||||
return nil, models.ErrInternal
|
||||
}
|
||||
return contactFromDB(c), nil
|
||||
}
|
||||
|
||||
func (s *ContactService) List(ctx context.Context, userID uuid.UUID, search *string, limit int, cursor string) ([]models.Contact, *string, error) {
|
||||
lim := utils.ClampLimit(limit)
|
||||
|
||||
var cursorTime, cursorID interface{}
|
||||
p := repository.ListContactsParams{
|
||||
OwnerID: utils.ToPgUUID(userID),
|
||||
Search: utils.ToPgTextPtr(search),
|
||||
Lim: lim + 1,
|
||||
}
|
||||
|
||||
if cursor != "" {
|
||||
ct, cid, err := utils.ParseCursor(cursor)
|
||||
if err != nil {
|
||||
return nil, nil, models.NewValidationError("invalid cursor")
|
||||
}
|
||||
p.CursorTime = utils.ToPgTimestamptzPtr(ct)
|
||||
p.CursorID = utils.ToPgUUID(*cid)
|
||||
cursorTime = ct
|
||||
cursorID = cid
|
||||
}
|
||||
_ = cursorTime
|
||||
_ = cursorID
|
||||
|
||||
rows, err := s.queries.ListContacts(ctx, p)
|
||||
if err != nil {
|
||||
return nil, nil, models.ErrInternal
|
||||
}
|
||||
|
||||
contacts := make([]models.Contact, 0, len(rows))
|
||||
for _, r := range rows {
|
||||
contacts = append(contacts, *contactFromDB(r))
|
||||
}
|
||||
|
||||
if int32(len(contacts)) > lim {
|
||||
contacts = contacts[:lim]
|
||||
last := contacts[len(contacts)-1]
|
||||
c := utils.EncodeCursor(last.CreatedAt, last.ID)
|
||||
return contacts, &c, nil
|
||||
}
|
||||
|
||||
return contacts, nil, nil
|
||||
}
|
||||
|
||||
func (s *ContactService) Update(ctx context.Context, userID uuid.UUID, contactID uuid.UUID, req UpdateContactRequest) (*models.Contact, error) {
|
||||
c, err := s.queries.UpdateContact(ctx, repository.UpdateContactParams{
|
||||
ID: utils.ToPgUUID(contactID),
|
||||
OwnerID: utils.ToPgUUID(userID),
|
||||
FirstName: utils.ToPgTextPtr(req.FirstName),
|
||||
LastName: utils.ToPgTextPtr(req.LastName),
|
||||
Email: utils.ToPgTextPtr(req.Email),
|
||||
Phone: utils.ToPgTextPtr(req.Phone),
|
||||
Company: utils.ToPgTextPtr(req.Company),
|
||||
Notes: utils.ToPgTextPtr(req.Notes),
|
||||
})
|
||||
if err != nil {
|
||||
if err == pgx.ErrNoRows {
|
||||
return nil, models.ErrNotFound
|
||||
}
|
||||
return nil, models.ErrInternal
|
||||
}
|
||||
|
||||
s.audit.Log(ctx, "contact", contactID, "UPDATE_CONTACT", userID)
|
||||
return contactFromDB(c), nil
|
||||
}
|
||||
|
||||
func (s *ContactService) Delete(ctx context.Context, userID uuid.UUID, contactID uuid.UUID) error {
|
||||
err := s.queries.SoftDeleteContact(ctx, repository.SoftDeleteContactParams{
|
||||
ID: utils.ToPgUUID(contactID),
|
||||
OwnerID: utils.ToPgUUID(userID),
|
||||
})
|
||||
if err != nil {
|
||||
return models.ErrInternal
|
||||
}
|
||||
s.audit.Log(ctx, "contact", contactID, "DELETE_CONTACT", userID)
|
||||
return nil
|
||||
}
|
||||
|
||||
func contactFromDB(v repository.Contact) *models.Contact {
|
||||
return &models.Contact{
|
||||
ID: utils.FromPgUUID(v.ID),
|
||||
FirstName: utils.FromPgText(v.FirstName),
|
||||
LastName: utils.FromPgText(v.LastName),
|
||||
Email: utils.FromPgText(v.Email),
|
||||
Phone: utils.FromPgText(v.Phone),
|
||||
Company: utils.FromPgText(v.Company),
|
||||
Notes: utils.FromPgText(v.Notes),
|
||||
CreatedAt: utils.FromPgTimestamptz(v.CreatedAt),
|
||||
UpdatedAt: utils.FromPgTimestamptz(v.UpdatedAt),
|
||||
}
|
||||
}
|
||||
|
||||
type CreateContactRequest struct {
|
||||
FirstName *string
|
||||
LastName *string
|
||||
Email *string
|
||||
Phone *string
|
||||
Company *string
|
||||
Notes *string
|
||||
}
|
||||
|
||||
type UpdateContactRequest struct {
|
||||
FirstName *string
|
||||
LastName *string
|
||||
Email *string
|
||||
Phone *string
|
||||
Company *string
|
||||
Notes *string
|
||||
}
|
||||
Reference in New Issue
Block a user