feature: Inspector Gadget
This commit is contained in:
182
pkg/inspector/inspector.go
Normal file
182
pkg/inspector/inspector.go
Normal file
@@ -0,0 +1,182 @@
|
||||
package inspector
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"git.warky.dev/wdevs/relspecgo/pkg/models"
|
||||
)
|
||||
|
||||
// Inspector performs validation on database models
|
||||
type Inspector struct {
|
||||
config *Config
|
||||
db *models.Database
|
||||
}
|
||||
|
||||
// ValidationResult represents the result of a single validation check
|
||||
type ValidationResult struct {
|
||||
RuleName string `json:"rule_name"`
|
||||
Level string `json:"level"` // "error" or "warning"
|
||||
Message string `json:"message"`
|
||||
Location string `json:"location"` // e.g., "schema.table.column"
|
||||
Context map[string]interface{} `json:"context"`
|
||||
Passed bool `json:"passed"`
|
||||
}
|
||||
|
||||
// InspectorReport contains the complete validation report
|
||||
type InspectorReport struct {
|
||||
Summary ReportSummary `json:"summary"`
|
||||
Violations []ValidationResult `json:"violations"`
|
||||
GeneratedAt time.Time `json:"generated_at"`
|
||||
Database string `json:"database"`
|
||||
SourceFormat string `json:"source_format"`
|
||||
}
|
||||
|
||||
// ReportSummary contains aggregate statistics
|
||||
type ReportSummary struct {
|
||||
TotalRules int `json:"total_rules"`
|
||||
RulesChecked int `json:"rules_checked"`
|
||||
RulesSkipped int `json:"rules_skipped"`
|
||||
ErrorCount int `json:"error_count"`
|
||||
WarningCount int `json:"warning_count"`
|
||||
PassedCount int `json:"passed_count"`
|
||||
}
|
||||
|
||||
// NewInspector creates a new inspector with the given database and configuration
|
||||
func NewInspector(db *models.Database, config *Config) *Inspector {
|
||||
return &Inspector{
|
||||
config: config,
|
||||
db: db,
|
||||
}
|
||||
}
|
||||
|
||||
// Inspect runs all enabled validation rules and returns a report
|
||||
func (i *Inspector) Inspect() (*InspectorReport, error) {
|
||||
results := []ValidationResult{}
|
||||
|
||||
// Run all enabled validators
|
||||
for ruleName, rule := range i.config.Rules {
|
||||
if !rule.IsEnabled() {
|
||||
continue
|
||||
}
|
||||
|
||||
// Get the validator function for this rule using the function field
|
||||
validator, exists := getValidator(rule.Function)
|
||||
if !exists {
|
||||
// Skip unknown validator functions
|
||||
continue
|
||||
}
|
||||
|
||||
// Run the validator
|
||||
ruleResults := validator(i.db, rule, ruleName)
|
||||
|
||||
// Set the level based on rule configuration
|
||||
level := "warning"
|
||||
if rule.IsEnforced() {
|
||||
level = "error"
|
||||
}
|
||||
|
||||
for idx := range ruleResults {
|
||||
ruleResults[idx].Level = level
|
||||
}
|
||||
|
||||
results = append(results, ruleResults...)
|
||||
}
|
||||
|
||||
// Generate summary
|
||||
summary := i.generateSummary(results)
|
||||
|
||||
report := &InspectorReport{
|
||||
Summary: summary,
|
||||
Violations: results,
|
||||
GeneratedAt: time.Now(),
|
||||
Database: i.db.Name,
|
||||
SourceFormat: i.db.SourceFormat,
|
||||
}
|
||||
|
||||
return report, nil
|
||||
}
|
||||
|
||||
// generateSummary creates summary statistics from validation results
|
||||
func (i *Inspector) generateSummary(results []ValidationResult) ReportSummary {
|
||||
summary := ReportSummary{
|
||||
TotalRules: len(i.config.Rules),
|
||||
}
|
||||
|
||||
// Count enabled rules
|
||||
for _, rule := range i.config.Rules {
|
||||
if rule.IsEnabled() {
|
||||
summary.RulesChecked++
|
||||
} else {
|
||||
summary.RulesSkipped++
|
||||
}
|
||||
}
|
||||
|
||||
// Count violations by level
|
||||
for _, result := range results {
|
||||
if result.Passed {
|
||||
summary.PassedCount++
|
||||
} else {
|
||||
if result.Level == "error" {
|
||||
summary.ErrorCount++
|
||||
} else {
|
||||
summary.WarningCount++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return summary
|
||||
}
|
||||
|
||||
// HasErrors returns true if the report contains any errors
|
||||
func (r *InspectorReport) HasErrors() bool {
|
||||
return r.Summary.ErrorCount > 0
|
||||
}
|
||||
|
||||
// validatorFunc is a function that validates a rule against a database
|
||||
type validatorFunc func(*models.Database, Rule, string) []ValidationResult
|
||||
|
||||
// getValidator returns the validator function for a given function name
|
||||
func getValidator(functionName string) (validatorFunc, bool) {
|
||||
validators := map[string]validatorFunc{
|
||||
"primary_key_naming": validatePrimaryKeyNaming,
|
||||
"primary_key_datatype": validatePrimaryKeyDatatype,
|
||||
"primary_key_auto_increment": validatePrimaryKeyAutoIncrement,
|
||||
"foreign_key_column_naming": validateForeignKeyColumnNaming,
|
||||
"foreign_key_constraint_naming": validateForeignKeyConstraintNaming,
|
||||
"foreign_key_index": validateForeignKeyIndex,
|
||||
"table_regexpr": validateTableNamingCase,
|
||||
"column_regexpr": validateColumnNamingCase,
|
||||
"table_name_length": validateTableNameLength,
|
||||
"column_name_length": validateColumnNameLength,
|
||||
"reserved_words": validateReservedKeywords,
|
||||
"have_primary_key": validateMissingPrimaryKey,
|
||||
"orphaned_foreign_key": validateOrphanedForeignKey,
|
||||
"circular_dependency": validateCircularDependency,
|
||||
}
|
||||
|
||||
fn, exists := validators[functionName]
|
||||
return fn, exists
|
||||
}
|
||||
|
||||
// createResult is a helper to create a validation result
|
||||
func createResult(ruleName string, passed bool, message string, location string, context map[string]interface{}) ValidationResult {
|
||||
return ValidationResult{
|
||||
RuleName: ruleName,
|
||||
Message: message,
|
||||
Location: location,
|
||||
Context: context,
|
||||
Passed: passed,
|
||||
}
|
||||
}
|
||||
|
||||
// formatLocation creates a location string from schema, table, and optional column
|
||||
func formatLocation(schema, table, column string) string {
|
||||
if column != "" {
|
||||
return fmt.Sprintf("%s.%s.%s", schema, table, column)
|
||||
}
|
||||
if table != "" {
|
||||
return fmt.Sprintf("%s.%s", schema, table)
|
||||
}
|
||||
return schema
|
||||
}
|
||||
Reference in New Issue
Block a user