tercul-backend/cmd/cli/commands/enrich.go
Damir Mukimov be97b587b2
feat: Implement Bleve migration script and unify CLI (#26) (#64)
* docs: Update TASKS.md and PRODUCTION-TASKS.md to reflect current codebase state (December 2024 audit)

* refactor: Unify all commands into a single Cobra CLI

- Refactor cmd/api/main.go into 'tercul serve' command
- Refactor cmd/worker/main.go into 'tercul worker' command
- Refactor cmd/tools/enrich/main.go into 'tercul enrich' command
- Add 'tercul bleve-migrate' command for Bleve index migration
- Extract common initialization logic into cmd/cli/internal/bootstrap
- Update Dockerfile to build unified CLI
- Update README with new CLI usage

This consolidates all entry points into a single, maintainable CLI structure.

* fix: Fix CodeQL workflow and add comprehensive test coverage

- Fix Go version mismatch by setting up Go before CodeQL init
- Add Go version verification step
- Improve error handling for code scanning upload
- Add comprehensive test suite for CLI commands:
  - Bleve migration tests with in-memory indexes
  - Edge case tests (empty data, large batches, errors)
  - Command-level integration tests
  - Bootstrap initialization tests
- Optimize tests to use in-memory Bleve indexes for speed
- Add test tags for skipping slow tests in short mode
- Update workflow documentation

Test coverage: 18.1% with 806 lines of test code
All tests passing in short mode

* fix: Fix test workflow and Bleve test double-close panic

- Add POSTGRES_USER to PostgreSQL service configuration in test workflow
- Fix TestInitBleveIndex double-close panic by removing defer before explicit close
- Test now passes successfully

Fixes failing Unit Tests workflow in PR #64
2025-11-30 21:54:18 +01:00

111 lines
2.8 KiB
Go

package commands
import (
"context"
"fmt"
"strconv"
"tercul/cmd/cli/internal/bootstrap"
"tercul/internal/enrichment"
"tercul/internal/platform/config"
"tercul/internal/platform/db"
"tercul/internal/platform/log"
"github.com/spf13/cobra"
)
// NewEnrichCommand creates a new Cobra command for enriching entities
func NewEnrichCommand() *cobra.Command {
var (
entityType string
entityID string
)
cmd := &cobra.Command{
Use: "enrich",
Short: "Enrich an entity with external data",
Long: `Enrich an entity (e.g., author) with external data from sources like OpenLibrary.
Example:
tercul enrich --type author --id 123`,
RunE: func(cmd *cobra.Command, args []string) error {
if entityType == "" || entityID == "" {
return fmt.Errorf("both --type and --id are required")
}
entityIDUint, err := strconv.ParseUint(entityID, 10, 64)
if err != nil {
return fmt.Errorf("invalid entity ID: %w", err)
}
// Load configuration
cfg, err := config.LoadConfig()
if err != nil {
return fmt.Errorf("failed to load config: %w", err)
}
// Initialize logger
log.Init("enrich-tool", "development")
database, err := db.InitDB(cfg, nil) // No metrics needed for this tool
if err != nil {
log.Fatal(err, "Failed to initialize database")
}
defer func() {
if err := db.Close(database); err != nil {
log.Error(err, "Error closing database")
}
}()
// Bootstrap dependencies
weaviateClient, err := bootstrap.NewWeaviateClient(cfg)
if err != nil {
return fmt.Errorf("failed to create weaviate client: %w", err)
}
deps, err := bootstrap.Bootstrap(cfg, database, weaviateClient)
if err != nil {
return fmt.Errorf("failed to bootstrap: %w", err)
}
enrichmentSvc := enrichment.NewService()
// Fetch, enrich, and save the entity
ctx := context.Background()
log.Info(fmt.Sprintf("Enriching %s with ID %d", entityType, entityIDUint))
switch entityType {
case "author":
author, err := deps.Repos.Author.GetByID(ctx, uint(entityIDUint))
if err != nil {
return fmt.Errorf("failed to get author: %w", err)
}
if err := enrichmentSvc.EnrichAuthor(ctx, author); err != nil {
return fmt.Errorf("failed to enrich author: %w", err)
}
if err := deps.Repos.Author.Update(ctx, author); err != nil {
return fmt.Errorf("failed to save enriched author: %w", err)
}
log.Info("Successfully enriched and saved author")
default:
return fmt.Errorf("unknown entity type: %s", entityType)
}
return nil
},
}
// Add flags
cmd.Flags().StringVarP(&entityType, "type", "t", "", "The type of entity to enrich (e.g., 'author')")
cmd.Flags().StringVarP(&entityID, "id", "i", "", "The ID of the entity to enrich")
// Mark flags as required
_ = cmd.MarkFlagRequired("type")
_ = cmd.MarkFlagRequired("id")
return cmd
}