@@ -0,0 +1,77 @@
|
||||
package storage
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"gardomatic.kleiax.de/internal/platform/validate"
|
||||
)
|
||||
|
||||
// MaxJournalAttachmentSize is the largest journal attachment accepted, in bytes.
|
||||
const MaxJournalAttachmentSize = 25 << 20
|
||||
|
||||
// JournalModelInterface persists journal entries and their attachments.
|
||||
type JournalModelInterface interface {
|
||||
Insert(entry JournalEntry) (JournalEntry, error)
|
||||
Get(gardenID, id int) (JournalEntry, error)
|
||||
GetAllForGarden(gardenID int, entryType JournalEntryType) ([]JournalEntry, error)
|
||||
Update(gardenID int, entry JournalEntry) (JournalEntry, error)
|
||||
Delete(gardenID, id int) error
|
||||
InsertAttachment(gardenID, entryID int, attachment JournalAttachment) (JournalAttachment, error)
|
||||
GetAttachment(gardenID, entryID, attachmentID int) (JournalAttachment, error)
|
||||
DeleteAttachment(gardenID, entryID, attachmentID int) error
|
||||
}
|
||||
|
||||
// JournalEntryType distinguishes chronological journal entries from pinboard
|
||||
// notes while sharing the same persistence model.
|
||||
type JournalEntryType string
|
||||
|
||||
// Supported journal entry types.
|
||||
const (
|
||||
JournalEntryTypeJournal JournalEntryType = "journal"
|
||||
JournalEntryTypePinboard JournalEntryType = "pinboard"
|
||||
)
|
||||
|
||||
// JournalEntry is a garden note with optional tags and attachments.
|
||||
type JournalEntry struct {
|
||||
ID int `json:"id"`
|
||||
GardenID int `json:"garden_id"`
|
||||
AuthorID int `json:"author_id"`
|
||||
AuthorName string `json:"author_name"`
|
||||
AuthorColor string `json:"author_color"`
|
||||
EntryType JournalEntryType `json:"entry_type"`
|
||||
Title string `json:"title"`
|
||||
Body string `json:"body"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
Version int `json:"version"`
|
||||
Tags []string `json:"tags,omitempty"`
|
||||
Attachments []JournalAttachment `json:"attachments,omitempty"`
|
||||
}
|
||||
|
||||
// JournalAttachment contains either inline binary data or a reference to a
|
||||
// reusable image-library item.
|
||||
type JournalAttachment struct {
|
||||
ID int `json:"id"`
|
||||
EntryID int `json:"entry_id"`
|
||||
FileName string `json:"file_name"`
|
||||
MediaType string `json:"media_type"`
|
||||
Size int64 `json:"size"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
Data []byte `json:"-"`
|
||||
ImageID *int `json:"image_id,omitempty"`
|
||||
}
|
||||
|
||||
// ValidateJournalEntry applies the journal-entry input rules.
|
||||
func ValidateJournalEntry(v *validate.Validator, entry JournalEntry) {
|
||||
entryType := entry.EntryType
|
||||
if entryType == "" {
|
||||
entryType = JournalEntryTypeJournal
|
||||
}
|
||||
v.Check(validate.PermittedValue(entryType, JournalEntryTypeJournal, JournalEntryTypePinboard), "entry_type", "must be journal or pinboard")
|
||||
if entryType == JournalEntryTypeJournal {
|
||||
v.Check(strings.TrimSpace(entry.Title) != "", "title", "must be provided")
|
||||
}
|
||||
v.Check(len(entry.Title) <= 500, "title", "must not be more than 500 bytes long")
|
||||
v.Check(len(entry.Body) <= 100_000, "body", "must not be more than 100000 bytes long")
|
||||
}
|
||||
Reference in New Issue
Block a user