package repository import ( "fmt" "log/slog" "sort" "github.com/rogeecn/database_render/internal/config" "github.com/rogeecn/database_render/internal/database" "github.com/rogeecn/database_render/internal/model" ) // DataRepository handles data access operations type DataRepository struct { db *database.ConnectionManager config *config.Config logger *slog.Logger } // NewDataRepository creates a new data repository func NewDataRepository(db *database.ConnectionManager, cfg *config.Config) *DataRepository { return &DataRepository{ db: db, config: cfg, logger: slog.With("component", "repository"), } } // GetTables returns all configured tables func (r *DataRepository) GetTables() ([]model.TableInfo, error) { var tables []model.TableInfo for name, tableConfig := range r.config.Tables { // Get row count for each table rowCount, err := r.db.GetTableRowCount(name) if err != nil { r.logger.Error("failed to get row count for table", "table", name, "error", err) rowCount = 0 } // Use table alias as description if not provided description := tableConfig.Alias if desc, ok := tableConfig.Options["description"]; ok { if descStr, ok := desc.(string); ok { description = descStr } } tables = append(tables, model.TableInfo{ Name: name, Alias: tableConfig.Alias, Description: description, RowCount: rowCount, }) } // Sort tables by name to ensure consistent ordering sort.Slice(tables, func(i, j int) bool { return tables[i].Name < tables[j].Name }) return tables, nil } // GetTableConfig returns the configuration for a specific table func (r *DataRepository) GetTableConfig(tableName string) (*model.TableConfig, error) { tableConfig, exists := r.config.Tables[tableName] if !exists { return nil, fmt.Errorf("table %s not found in configuration", tableName) } // Convert internal config to model config config := &model.TableConfig{ Name: tableName, Alias: tableConfig.Alias, PageSize: tableConfig.PageSize, Columns: []model.ColumnConfig{}, Filters: []model.FilterConfig{}, Options: tableConfig.Options, } // Convert field configurations for fieldName, fieldConfig := range tableConfig.Fields { column := model.ColumnConfig{ Name: fieldName, Alias: fieldName, RenderType: fieldConfig.Type, Sortable: true, // Default to true Searchable: fieldConfig.Searchable, ShowInList: !fieldConfig.Hidden, IsPrimaryContent: fieldConfig.Markdown, MaxLength: fieldConfig.MaxLength, Width: fieldConfig.Size, Format: fieldConfig.Format, Values: make(map[string]model.TagValue), Options: fieldConfig.Options, } // Handle tag values if colors are provided if len(fieldConfig.Colors) > 0 { for key, color := range fieldConfig.Colors { label := key column.Values[key] = model.TagValue{ Label: label, Color: color, } } } config.Columns = append(config.Columns, column) } return config, nil } // GetTableData retrieves paginated data from a table func (r *DataRepository) GetTableData(tableName string, page, pageSize int, search string, sortField string, sortOrder string) ([]map[string]interface{}, int64, error) { // Validate table exists in config _, exists := r.config.Tables[tableName] if !exists { return nil, 0, fmt.Errorf("table %s not found in configuration", tableName) } // Get data from database data, total, err := r.db.GetTableData(tableName, page, pageSize, search, sortField, sortOrder) if err != nil { r.logger.Error("failed to get table data", "table", tableName, "page", page, "error", err) return nil, 0, fmt.Errorf("failed to get table data: %w", err) } r.logger.Debug("retrieved table data", "table", tableName, "page", page, "pageSize", pageSize, "total", total, "records", len(data)) return data, total, nil } // GetTableDataByID retrieves a single record by ID func (r *DataRepository) GetTableDataByID(tableName string, id interface{}) (map[string]interface{}, error) { // Validate table exists in config _, exists := r.config.Tables[tableName] if !exists { return nil, fmt.Errorf("table %s not found in configuration", tableName) } // Get data from database data, err := r.db.GetTableDataByID(tableName, id) if err != nil { r.logger.Error("failed to get table data by ID", "table", tableName, "id", id, "error", err) return nil, fmt.Errorf("failed to get table data by ID: %w", err) } r.logger.Debug("retrieved single record", "table", tableName, "id", id) return data, nil } // GetTableColumns returns column information for a table func (r *DataRepository) GetTableColumns(tableName string) ([]database.ColumnInfo, error) { // Validate table exists in config _, exists := r.config.Tables[tableName] if !exists { return nil, fmt.Errorf("table %s not found in configuration", tableName) } // Get column information from database columns, err := r.db.GetTableColumns(tableName) if err != nil { r.logger.Error("failed to get table columns", "table", tableName, "error", err) return nil, fmt.Errorf("failed to get table columns: %w", err) } return columns, nil } // ValidateTableConfig validates if the configured tables exist in the database func (r *DataRepository) ValidateTableConfig() error { // Get all table names from database dbTables, err := r.db.GetTableNames() if err != nil { return fmt.Errorf("failed to get table names from database: %w", err) } // Create a map for quick lookup dbTableMap := make(map[string]bool) for _, table := range dbTables { dbTableMap[table] = true } // Check if all configured tables exist for tableName := range r.config.Tables { if !dbTableMap[tableName] { return fmt.Errorf("table %s not found in database", tableName) } } r.logger.Info("table configuration validation completed", "configured_tables", len(r.config.Tables), "database_tables", len(dbTables)) return nil }