Implement complete collection REST API and fix critical server enhancement bug
• Add full collection REST API with CRUD operations for collections and items • Implement collection models, handlers, and database integration with SQLite/PostgreSQL support • Add collection endpoints: GET/POST/PUT/DELETE for collections and collection items • Fix critical server enhancement bug by consolidating to engine.DatabaseClient • Remove obsolete content.DatabaseClient that lacked collection support • Enable proper collection reconstruction during server-side enhancement • Collections now persist correctly and display new items after API modifications
This commit is contained in:
@@ -1,252 +0,0 @@
|
||||
package content
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/insertr/insertr/internal/db"
|
||||
"github.com/insertr/insertr/internal/db/postgresql"
|
||||
"github.com/insertr/insertr/internal/db/sqlite"
|
||||
"github.com/insertr/insertr/internal/engine"
|
||||
)
|
||||
|
||||
// Helper function to convert sql.NullString to string
|
||||
func getStringFromNullString(ns sql.NullString) string {
|
||||
if ns.Valid {
|
||||
return ns.String
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// DatabaseClient implements ContentClient for direct database access
|
||||
type DatabaseClient struct {
|
||||
db *db.Database
|
||||
}
|
||||
|
||||
// NewDatabaseClient creates a new database content client
|
||||
func NewDatabaseClient(database *db.Database) *DatabaseClient {
|
||||
return &DatabaseClient{
|
||||
db: database,
|
||||
}
|
||||
}
|
||||
|
||||
// GetContent fetches a single content item by ID
|
||||
func (d *DatabaseClient) GetContent(siteID, contentID string) (*engine.ContentItem, error) {
|
||||
ctx := context.Background()
|
||||
var content interface{}
|
||||
var err error
|
||||
|
||||
switch d.db.GetDBType() {
|
||||
case "sqlite3":
|
||||
content, err = d.db.GetSQLiteQueries().GetContent(ctx, sqlite.GetContentParams{
|
||||
ID: contentID,
|
||||
SiteID: siteID,
|
||||
})
|
||||
case "postgresql":
|
||||
content, err = d.db.GetPostgreSQLQueries().GetContent(ctx, postgresql.GetContentParams{
|
||||
ID: contentID,
|
||||
SiteID: siteID,
|
||||
})
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported database type: %s", d.db.GetDBType())
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, nil // Content not found, return nil without error
|
||||
}
|
||||
return nil, fmt.Errorf("database error: %w", err)
|
||||
}
|
||||
|
||||
item := d.convertToContentItem(content)
|
||||
return &item, nil
|
||||
}
|
||||
|
||||
// GetBulkContent fetches multiple content items by IDs
|
||||
func (d *DatabaseClient) GetBulkContent(siteID string, contentIDs []string) (map[string]engine.ContentItem, error) {
|
||||
if len(contentIDs) == 0 {
|
||||
return make(map[string]engine.ContentItem), nil
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
var dbContent interface{}
|
||||
var err error
|
||||
|
||||
switch d.db.GetDBType() {
|
||||
case "sqlite3":
|
||||
dbContent, err = d.db.GetSQLiteQueries().GetBulkContent(ctx, sqlite.GetBulkContentParams{
|
||||
SiteID: siteID,
|
||||
Ids: contentIDs,
|
||||
})
|
||||
case "postgresql":
|
||||
dbContent, err = d.db.GetPostgreSQLQueries().GetBulkContent(ctx, postgresql.GetBulkContentParams{
|
||||
SiteID: siteID,
|
||||
Ids: contentIDs,
|
||||
})
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported database type: %s", d.db.GetDBType())
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("database error: %w", err)
|
||||
}
|
||||
|
||||
items := d.convertToContentItemList(dbContent)
|
||||
|
||||
// Convert slice to map for easy lookup
|
||||
result := make(map[string]engine.ContentItem)
|
||||
for _, item := range items {
|
||||
result[item.ID] = item
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// GetAllContent fetches all content for a site
|
||||
func (d *DatabaseClient) GetAllContent(siteID string) (map[string]engine.ContentItem, error) {
|
||||
ctx := context.Background()
|
||||
var dbContent interface{}
|
||||
var err error
|
||||
|
||||
switch d.db.GetDBType() {
|
||||
case "sqlite3":
|
||||
dbContent, err = d.db.GetSQLiteQueries().GetAllContent(ctx, siteID)
|
||||
case "postgresql":
|
||||
dbContent, err = d.db.GetPostgreSQLQueries().GetAllContent(ctx, siteID)
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported database type: %s", d.db.GetDBType())
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("database error: %w", err)
|
||||
}
|
||||
|
||||
items := d.convertToContentItemList(dbContent)
|
||||
|
||||
// Convert slice to map for easy lookup
|
||||
result := make(map[string]engine.ContentItem)
|
||||
for _, item := range items {
|
||||
result[item.ID] = item
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// convertToContentItem converts database models to engine.ContentItem
|
||||
func (d *DatabaseClient) convertToContentItem(content interface{}) engine.ContentItem {
|
||||
switch d.db.GetDBType() {
|
||||
case "sqlite3":
|
||||
c := content.(sqlite.Content)
|
||||
return engine.ContentItem{
|
||||
ID: c.ID,
|
||||
SiteID: c.SiteID,
|
||||
HTMLContent: c.HtmlContent,
|
||||
OriginalTemplate: getStringFromNullString(c.OriginalTemplate),
|
||||
UpdatedAt: time.Unix(c.UpdatedAt, 0).Format(time.RFC3339),
|
||||
}
|
||||
case "postgresql":
|
||||
c := content.(postgresql.Content)
|
||||
return engine.ContentItem{
|
||||
ID: c.ID,
|
||||
SiteID: c.SiteID,
|
||||
HTMLContent: c.HtmlContent,
|
||||
OriginalTemplate: getStringFromNullString(c.OriginalTemplate),
|
||||
UpdatedAt: time.Unix(c.UpdatedAt, 0).Format(time.RFC3339),
|
||||
}
|
||||
}
|
||||
return engine.ContentItem{} // Should never happen
|
||||
}
|
||||
|
||||
// convertToContentItemList converts database model lists to engine.ContentItem slice
|
||||
func (d *DatabaseClient) convertToContentItemList(contentList interface{}) []engine.ContentItem {
|
||||
switch d.db.GetDBType() {
|
||||
case "sqlite3":
|
||||
list := contentList.([]sqlite.Content)
|
||||
items := make([]engine.ContentItem, len(list))
|
||||
for i, content := range list {
|
||||
items[i] = d.convertToContentItem(content)
|
||||
}
|
||||
return items
|
||||
case "postgresql":
|
||||
list := contentList.([]postgresql.Content)
|
||||
items := make([]engine.ContentItem, len(list))
|
||||
for i, content := range list {
|
||||
items[i] = d.convertToContentItem(content)
|
||||
}
|
||||
return items
|
||||
}
|
||||
return []engine.ContentItem{} // Should never happen
|
||||
}
|
||||
|
||||
// CreateContent creates a new content item
|
||||
func (c *DatabaseClient) CreateContent(siteID, contentID, htmlContent, originalTemplate, lastEditedBy string) (*engine.ContentItem, error) {
|
||||
switch c.db.GetDBType() {
|
||||
case "sqlite3":
|
||||
content, err := c.db.GetSQLiteQueries().CreateContent(context.Background(), sqlite.CreateContentParams{
|
||||
ID: contentID,
|
||||
SiteID: siteID,
|
||||
HtmlContent: htmlContent,
|
||||
OriginalTemplate: toNullString(originalTemplate),
|
||||
LastEditedBy: lastEditedBy,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &engine.ContentItem{
|
||||
ID: content.ID,
|
||||
SiteID: content.SiteID,
|
||||
HTMLContent: content.HtmlContent,
|
||||
OriginalTemplate: getStringFromNullString(content.OriginalTemplate),
|
||||
LastEditedBy: content.LastEditedBy,
|
||||
}, nil
|
||||
|
||||
case "postgresql":
|
||||
content, err := c.db.GetPostgreSQLQueries().CreateContent(context.Background(), postgresql.CreateContentParams{
|
||||
ID: contentID,
|
||||
SiteID: siteID,
|
||||
HtmlContent: htmlContent,
|
||||
OriginalTemplate: toNullString(originalTemplate),
|
||||
LastEditedBy: lastEditedBy,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &engine.ContentItem{
|
||||
ID: content.ID,
|
||||
SiteID: content.SiteID,
|
||||
HTMLContent: content.HtmlContent,
|
||||
OriginalTemplate: getStringFromNullString(content.OriginalTemplate),
|
||||
LastEditedBy: content.LastEditedBy,
|
||||
}, nil
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported database type: %s", c.db.GetDBType())
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to convert string to sql.NullString
|
||||
func toNullString(s string) sql.NullString {
|
||||
if s == "" {
|
||||
return sql.NullString{Valid: false}
|
||||
}
|
||||
return sql.NullString{String: s, Valid: true}
|
||||
}
|
||||
|
||||
// Collection method stubs - TODO: Implement these
|
||||
func (d *DatabaseClient) GetCollection(siteID, collectionID string) (*engine.CollectionItem, error) {
|
||||
return nil, fmt.Errorf("collection operations not implemented in content.DatabaseClient")
|
||||
}
|
||||
|
||||
func (d *DatabaseClient) CreateCollection(siteID, collectionID, containerHTML, lastEditedBy string) (*engine.CollectionItem, error) {
|
||||
return nil, fmt.Errorf("collection operations not implemented in content.DatabaseClient")
|
||||
}
|
||||
|
||||
func (d *DatabaseClient) GetCollectionItems(siteID, collectionID string) ([]engine.CollectionItemWithTemplate, error) {
|
||||
return nil, fmt.Errorf("collection operations not implemented in content.DatabaseClient")
|
||||
}
|
||||
|
||||
func (d *DatabaseClient) CreateCollectionTemplate(siteID, collectionID, name, htmlTemplate string, isDefault bool) (*engine.CollectionTemplateItem, error) {
|
||||
return nil, fmt.Errorf("collection operations not implemented in content.DatabaseClient")
|
||||
}
|
||||
Reference in New Issue
Block a user