Complete tasks 3.2-3.3: Data models and DynamoDB table schemas

- Defined all 8 data models (Page, Widget, Bookmark, Note, TagAssociation, Group, Share, Preferences)
- Implemented DynamoDB table creation for all tables with proper schemas
- Added GSIs for efficient querying (UserBookmarksIndex, UserNotesIndex, TagItemsIndex, UserSharesIndex)
- Comprehensive test coverage for all table schemas
- Updated init-db command to create all tables
This commit is contained in:
2026-02-18 22:55:06 -05:00
parent 7175ff14ba
commit 9f07b0c6f9
13 changed files with 1363 additions and 6 deletions

View File

@@ -0,0 +1,20 @@
package models
import (
"time"
)
// Bookmark represents a link with title, URL, and optional grouping
type Bookmark struct {
ID string `dynamodbav:"bookmark_id" json:"id"`
WidgetID string `dynamodbav:"widget_id" json:"widget_id"`
UserID string `dynamodbav:"user_id" json:"user_id"`
Title string `dynamodbav:"title" json:"title"`
URL string `dynamodbav:"url" json:"url"`
GroupID *string `dynamodbav:"group_id,omitempty" json:"group_id,omitempty"`
Order int `dynamodbav:"order" json:"order"`
FaviconURL *string `dynamodbav:"favicon_url,omitempty" json:"favicon_url,omitempty"`
CreatedAt time.Time `dynamodbav:"created_at" json:"created_at"`
UpdatedAt time.Time `dynamodbav:"updated_at" json:"updated_at"`
Version int `dynamodbav:"version" json:"version"`
}

16
internal/models/group.go Normal file
View File

@@ -0,0 +1,16 @@
package models
import (
"time"
)
// Group represents a named collection of bookmarks within a widget
type Group struct {
ID string `dynamodbav:"group_id" json:"id"`
WidgetID string `dynamodbav:"widget_id" json:"widget_id"`
UserID string `dynamodbav:"user_id" json:"user_id"`
Name string `dynamodbav:"name" json:"name"`
Order int `dynamodbav:"order" json:"order"`
CreatedAt time.Time `dynamodbav:"created_at" json:"created_at"`
UpdatedAt time.Time `dynamodbav:"updated_at" json:"updated_at"`
}

31
internal/models/note.go Normal file
View File

@@ -0,0 +1,31 @@
package models
import (
"time"
)
// ContentFormat represents the format mode of note content
type ContentFormat string
const (
ContentFormatPlain ContentFormat = "plain"
ContentFormatRTF ContentFormat = "rtf"
ContentFormatCode ContentFormat = "code"
ContentFormatYAML ContentFormat = "yaml"
ContentFormatMarkdown ContentFormat = "markdown"
)
// Note represents a text note with rich content support
type Note struct {
ID string `dynamodbav:"note_id" json:"id"`
WidgetID string `dynamodbav:"widget_id" json:"widget_id"`
UserID string `dynamodbav:"user_id" json:"user_id"`
Title *string `dynamodbav:"title,omitempty" json:"title,omitempty"`
Content string `dynamodbav:"content" json:"content"`
ContentLocation string `dynamodbav:"content_location" json:"content_location"`
Format ContentFormat `dynamodbav:"format" json:"format"`
Language *string `dynamodbav:"language,omitempty" json:"language,omitempty"`
CreatedAt time.Time `dynamodbav:"created_at" json:"created_at"`
UpdatedAt time.Time `dynamodbav:"updated_at" json:"updated_at"`
Version int `dynamodbav:"version" json:"version"`
}

15
internal/models/page.go Normal file
View File

@@ -0,0 +1,15 @@
package models
import (
"time"
)
// Page represents a tab-based container that holds multiple widgets
type Page struct {
ID string `dynamodbav:"page_id" json:"id"`
UserID string `dynamodbav:"user_id" json:"user_id"`
Name string `dynamodbav:"name" json:"name"`
Order int `dynamodbav:"order" json:"order"`
CreatedAt time.Time `dynamodbav:"created_at" json:"created_at"`
UpdatedAt time.Time `dynamodbav:"updated_at" json:"updated_at"`
}

View File

@@ -0,0 +1,22 @@
package models
import (
"time"
)
// SearchProvider represents the configured search engine
type SearchProvider string
const (
SearchProviderGoogle SearchProvider = "google"
SearchProviderDuckDuckGo SearchProvider = "duckduckgo"
SearchProviderBing SearchProvider = "bing"
)
// Preferences represents user preferences and settings
type Preferences struct {
UserID string `dynamodbav:"user_id" json:"user_id"`
SearchProvider SearchProvider `dynamodbav:"search_provider" json:"search_provider"`
Theme *string `dynamodbav:"theme,omitempty" json:"theme,omitempty"`
UpdatedAt time.Time `dynamodbav:"updated_at" json:"updated_at"`
}

31
internal/models/share.go Normal file
View File

@@ -0,0 +1,31 @@
package models
import (
"time"
)
// Share represents a shareable link for bookmarks or notes
type Share struct {
ID string `dynamodbav:"share_id" json:"id"`
UserID string `dynamodbav:"user_id" json:"user_id"`
ItemID string `dynamodbav:"item_id" json:"item_id"`
ItemType ItemType `dynamodbav:"item_type" json:"item_type"`
CreatedAt time.Time `dynamodbav:"created_at" json:"created_at"`
ExpiresAt *time.Time `dynamodbav:"expires_at,omitempty" json:"expires_at,omitempty"`
AccessCount int `dynamodbav:"access_count" json:"access_count"`
}
// ShareLink represents a shareable link with full URL
type ShareLink struct {
ShareID string `json:"share_id"`
URL string `json:"url"`
ExpiresAt *time.Time `json:"expires_at,omitempty"`
}
// SharedItem represents the data returned when accessing a shared link
type SharedItem struct {
ItemType ItemType `json:"item_type"`
Data interface{} `json:"data"`
OwnerEmail *string `json:"owner_email,omitempty"`
CreatedAt time.Time `json:"created_at"`
}

29
internal/models/tag.go Normal file
View File

@@ -0,0 +1,29 @@
package models
import (
"time"
)
// ItemType represents the type of item that can be tagged
type ItemType string
const (
ItemTypeBookmark ItemType = "bookmark"
ItemTypeNote ItemType = "note"
)
// TagAssociation represents a many-to-many relationship between tags and items
type TagAssociation struct {
ItemID string `dynamodbav:"item_id" json:"item_id"`
TagName string `dynamodbav:"tag_name" json:"tag_name"`
UserID string `dynamodbav:"user_id" json:"user_id"`
ItemType ItemType `dynamodbav:"item_type" json:"item_type"`
CreatedAt time.Time `dynamodbav:"created_at" json:"created_at"`
}
// TagInfo represents tag metadata with usage statistics
type TagInfo struct {
Name string `json:"name"`
Count int `json:"count"`
ItemTypes []ItemType `json:"item_types"`
}

40
internal/models/widget.go Normal file
View File

@@ -0,0 +1,40 @@
package models
import (
"time"
)
// WidgetType represents the type of widget
type WidgetType string
const (
WidgetTypeBookmark WidgetType = "bookmark"
WidgetTypeNotes WidgetType = "notes"
WidgetTypeWeather WidgetType = "weather"
)
// Position represents the x,y coordinates of a widget
type Position struct {
X int `dynamodbav:"x" json:"x"`
Y int `dynamodbav:"y" json:"y"`
}
// Size represents the width and height of a widget
type Size struct {
Width int `dynamodbav:"width" json:"width"`
Height int `dynamodbav:"height" json:"height"`
}
// Widget represents a modular component that displays specific content
type Widget struct {
ID string `dynamodbav:"widget_id" json:"id"`
PageID string `dynamodbav:"page_id" json:"page_id"`
UserID string `dynamodbav:"user_id" json:"user_id"`
Type WidgetType `dynamodbav:"type" json:"type"`
Title *string `dynamodbav:"title,omitempty" json:"title,omitempty"`
Position Position `dynamodbav:"position" json:"position"`
Size Size `dynamodbav:"size" json:"size"`
Config map[string]interface{} `dynamodbav:"config" json:"config"`
CreatedAt time.Time `dynamodbav:"created_at" json:"created_at"`
UpdatedAt time.Time `dynamodbav:"updated_at" json:"updated_at"`
}