Skip to content
Merged

Dev2 #79

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions bootstrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,12 @@ func LoadConfig(filePath string) error {
return lokstra_registry.LoadConfig(filePath)
}

// SetupNamedDbPools sets up database pools from loaded config.
// Must be called AFTER LoadConfig() if you use named-db-pools in config.
func SetupNamedDbPools() error {
return lokstra_registry.SetupNamedDbPools()
}

// InitAndRunServer initializes and runs the server based on loaded configuration.
func InitAndRunServer() error {
return lokstra_registry.InitAndRunServer()
Expand Down
60 changes: 57 additions & 3 deletions core/annotation/complex_processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,9 +90,12 @@ func ProcessComplexAnnotations(rootPath []string, maxWorkers int,
if _, err := os.Stat(genPath); err == nil {
// Get package import path for this folder
if pkgPath := getPackageImportPath(folder); pkgPath != "" {
packageMutex.Lock()
packagesWithServices = append(packagesWithServices, pkgPath)
packageMutex.Unlock()
// Skip package main to avoid circular import
if !isMainPackage(folder) {
packageMutex.Lock()
packagesWithServices = append(packagesWithServices, pkgPath)
packageMutex.Unlock()
}
}
}
}
Expand Down Expand Up @@ -696,6 +699,57 @@ func findMainGoFolder(startPath string) string {
}
}

// isMainPackage checks if a folder contains package main by reading .go files
func isMainPackage(folderPath string) bool {
// Read all .go files in folder (excluding test and generated files)
files, err := os.ReadDir(folderPath)
if err != nil {
return false
}

for _, file := range files {
if file.IsDir() {
continue
}

fileName := file.Name()
if !strings.HasSuffix(fileName, ".go") {
continue
}

// Skip test files and generated files
if strings.HasSuffix(fileName, "_test.go") ||
strings.HasPrefix(fileName, "zz_") {
continue
}

// Read file and check package declaration
filePath := filepath.Join(folderPath, fileName)
data, err := os.ReadFile(filePath)
if err != nil {
continue
}

// Parse package declaration (first non-comment line)
lines := strings.Split(string(data), "\n")
for _, line := range lines {
line = strings.TrimSpace(line)
if line == "" || strings.HasPrefix(line, "//") || strings.HasPrefix(line, "/*") {
continue
}

// Found first non-comment line
if strings.HasPrefix(line, "package ") {
pkgName := strings.TrimSpace(strings.TrimPrefix(line, "package"))
return pkgName == "main"
}
break
}
}

return false
}

// generateImportFile creates zz_lokstra_imports.go in the same folder as main.go
func generateImportFile(startPath string, packages []string) error {
// Find main.go folder
Expand Down
59 changes: 23 additions & 36 deletions core/deploy/cfg_deps_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,17 +49,19 @@ func TestConfigBasedDependencyInjection(t *testing.T) {
// Register logger
lokstra_registry.RegisterService("logger", "test-logger")

// Register user service with cfg: prefix dependency
// Set GLOBAL config (not service config!)
lokstra_registry.SetConfig("store.implementation", "postgres-store")

// Register user service with @ prefix for config-based dependency
lokstra_registry.RegisterLazyService("user-service",
func(deps map[string]any, config map[string]any) any {
return &UserService{
Store: deps["cfg:store.implementation"].(Store),
Store: deps["cfg"].(Store),
Logger: deps["logger"].(string),
}
},
map[string]any{
"depends-on": []string{"cfg:store.implementation", "logger"},
"store.implementation": "postgres-store", // Config specifies postgres
"depends-on": []string{"cfg:@store.implementation", "logger"},
},
)

Expand Down Expand Up @@ -96,17 +98,19 @@ func TestConfigBasedDependencyInjection_SwitchImplementation(t *testing.T) {
// Register logger
lokstra_registry.RegisterService("logger-2", "logger2")

// Set GLOBAL config to use MySQL
lokstra_registry.SetConfig("store.implementation", "mysql-store-2")

// Register user service with MySQL this time
lokstra_registry.RegisterLazyService("user-service-2",
func(deps map[string]any, config map[string]any) any {
return &UserService{
Store: deps["cfg:store.implementation"].(Store),
Store: deps["cfg"].(Store),
Logger: deps["logger-2"].(string),
}
},
map[string]any{
"depends-on": []string{"cfg:store.implementation", "logger-2"},
"store.implementation": "mysql-store-2", // Switch to MySQL!
"depends-on": []string{"cfg:@store.implementation", "logger-2"},
},
)

Expand All @@ -132,30 +136,24 @@ func TestConfigBasedDependency_MissingConfig(t *testing.T) {
return &PostgresStore{name: "test"}
}, nil)

// Register service WITHOUT config for cfg: dependency
// DO NOT set global config for "missing.config"

// Register service with @ dependency that won't be found
lokstra_registry.RegisterLazyService("bad-service",
func(deps map[string]any, config map[string]any) any {
return &UserService{
Store: deps["cfg:missing.config"].(Store),
Store: deps["cfg"].(Store),
}
},
map[string]any{
"depends-on": []string{"cfg:missing.config"},
// Missing: "missing.config" key!
"depends-on": []string{"cfg:@missing.config"},
},
)

// Should panic when trying to resolve
// Should panic when trying to resolve - config key not found
defer func() {
if r := recover(); r == nil {
t.Error("expected panic when config key is missing")
} else {
// Check error message mentions config requirement
if msg, ok := r.(string); ok {
if !contains(msg, "config-based dependency") {
t.Errorf("expected error about config-based dependency, got: %v", r)
}
}
}
}()

Expand All @@ -166,16 +164,18 @@ func TestConfigBasedDependency_EmptyConfig(t *testing.T) {
// Clear registry
_ = deploy.Global()

// Register service with EMPTY config value
// Set global config with EMPTY value
lokstra_registry.SetConfig("empty.config", "")

// Register service with @ dependency pointing to empty config
lokstra_registry.RegisterLazyService("bad-service-2",
func(deps map[string]any, config map[string]any) any {
return &UserService{
Store: deps["cfg:empty.config"].(Store),
Store: deps["cfg"].(Store),
}
},
map[string]any{
"depends-on": []string{"cfg:empty.config"},
"empty.config": "", // Empty string!
"depends-on": []string{"cfg:@empty.config"},
},
)

Expand All @@ -188,16 +188,3 @@ func TestConfigBasedDependency_EmptyConfig(t *testing.T) {

lokstra_registry.MustGetService[*UserService]("bad-service-2")
}

func contains(s, substr string) bool {
return len(s) >= len(substr) && (s == substr || len(s) > len(substr) && (s[:len(substr)] == substr || s[len(s)-len(substr):] == substr || containsMiddle(s, substr)))
}

func containsMiddle(s, substr string) bool {
for i := 0; i <= len(s)-len(substr); i++ {
if s[i:i+len(substr)] == substr {
return true
}
}
return false
}
Loading