Consolidate type definitions and fix API contract
- Move all ContentItem, ContentClient, ContentResponse types to engine/types.go as single source of truth - Remove duplicate type definitions from content/types.go - Update all imports across codebase to use engine types - Enhance engine to extract existing data-content-id from HTML markup - Simplify frontend to always send html_markup, let server handle ID extraction/generation - Fix contentId reference errors in frontend error handling - Add getAttribute helper method to engine for ID extraction - Add GetAllContent method to engine.DatabaseClient - Update enhancer to use engine.ContentClient interface - All builds and API endpoints verified working This resolves the 400 Bad Request errors and creates a unified architecture where the server is the single source of truth for all ID generation and content type management.
This commit is contained in:
@@ -110,3 +110,47 @@ func (c *DatabaseClient) GetBulkContent(siteID string, contentIDs []string) (map
|
||||
return nil, fmt.Errorf("unsupported database type: %s", c.database.GetDBType())
|
||||
}
|
||||
}
|
||||
|
||||
// GetAllContent retrieves all content items for a site
|
||||
func (c *DatabaseClient) GetAllContent(siteID string) (map[string]ContentItem, error) {
|
||||
switch c.database.GetDBType() {
|
||||
case "sqlite3":
|
||||
contents, err := c.database.GetSQLiteQueries().GetAllContent(context.Background(), siteID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
items := make(map[string]ContentItem)
|
||||
for _, content := range contents {
|
||||
items[content.ID] = ContentItem{
|
||||
ID: content.ID,
|
||||
SiteID: content.SiteID,
|
||||
Value: content.Value,
|
||||
Type: content.Type,
|
||||
LastEditedBy: content.LastEditedBy,
|
||||
}
|
||||
}
|
||||
return items, nil
|
||||
|
||||
case "postgresql":
|
||||
contents, err := c.database.GetPostgreSQLQueries().GetAllContent(context.Background(), siteID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
items := make(map[string]ContentItem)
|
||||
for _, content := range contents {
|
||||
items[content.ID] = ContentItem{
|
||||
ID: content.ID,
|
||||
SiteID: content.SiteID,
|
||||
Value: content.Value,
|
||||
Type: content.Type,
|
||||
LastEditedBy: content.LastEditedBy,
|
||||
}
|
||||
}
|
||||
return items, nil
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported database type: %s", c.database.GetDBType())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,20 +37,33 @@ func (e *ContentEngine) ProcessContent(input ContentInput) (*ContentResult, erro
|
||||
processedElements := make([]ProcessedElement, len(elements))
|
||||
|
||||
for i, elem := range elements {
|
||||
// Generate ID using the same algorithm as the parser
|
||||
id := e.idGenerator.Generate(elem.Node, input.FilePath)
|
||||
// Check if element already has a data-content-id
|
||||
existingID := e.getAttribute(elem.Node, "data-content-id")
|
||||
var id string
|
||||
var wasGenerated bool
|
||||
|
||||
if existingID != "" {
|
||||
// Use existing ID from enhanced element
|
||||
id = existingID
|
||||
wasGenerated = false
|
||||
} else {
|
||||
// Generate new ID for unprocessed element
|
||||
id = e.idGenerator.Generate(elem.Node, input.FilePath)
|
||||
wasGenerated = true
|
||||
}
|
||||
|
||||
generatedIDs[fmt.Sprintf("element_%d", i)] = id
|
||||
|
||||
processedElements[i] = ProcessedElement{
|
||||
Node: elem.Node,
|
||||
ID: id,
|
||||
Type: elem.Type,
|
||||
Generated: true,
|
||||
Generated: wasGenerated,
|
||||
Tag: elem.Node.Data,
|
||||
Classes: GetClasses(elem.Node),
|
||||
}
|
||||
|
||||
// Add content attributes to the node
|
||||
// Add/update content attributes to the node
|
||||
e.addContentAttributes(elem.Node, id, elem.Type)
|
||||
}
|
||||
|
||||
@@ -133,6 +146,16 @@ func (e *ContentEngine) addContentAttributes(node *html.Node, contentID, content
|
||||
e.setAttribute(node, "data-content-type", contentType)
|
||||
}
|
||||
|
||||
// getAttribute gets an attribute value from an HTML node
|
||||
func (e *ContentEngine) getAttribute(node *html.Node, key string) string {
|
||||
for _, attr := range node.Attr {
|
||||
if attr.Key == key {
|
||||
return attr.Val
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// setAttribute sets an attribute on an HTML node
|
||||
func (e *ContentEngine) setAttribute(node *html.Node, key, value string) {
|
||||
// Remove existing attribute if it exists
|
||||
|
||||
@@ -43,17 +43,25 @@ type ProcessedElement struct {
|
||||
}
|
||||
|
||||
// ContentClient interface for accessing content data
|
||||
// This will be implemented by database clients
|
||||
// This will be implemented by database clients, HTTP clients, and mock clients
|
||||
type ContentClient interface {
|
||||
GetContent(siteID, contentID string) (*ContentItem, error)
|
||||
GetBulkContent(siteID string, contentIDs []string) (map[string]ContentItem, error)
|
||||
GetAllContent(siteID string) (map[string]ContentItem, error)
|
||||
}
|
||||
|
||||
// ContentItem represents a piece of content from the database
|
||||
type ContentItem struct {
|
||||
ID string
|
||||
SiteID string
|
||||
Value string
|
||||
Type string
|
||||
LastEditedBy string
|
||||
ID string `json:"id"`
|
||||
SiteID string `json:"site_id"`
|
||||
Value string `json:"value"`
|
||||
Type string `json:"type"`
|
||||
UpdatedAt string `json:"updated_at"`
|
||||
LastEditedBy string `json:"last_edited_by,omitempty"`
|
||||
}
|
||||
|
||||
// ContentResponse represents the API response structure
|
||||
type ContentResponse struct {
|
||||
Content []ContentItem `json:"content"`
|
||||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user