177 lines
4.4 KiB
Go
177 lines
4.4 KiB
Go
package serviceadapter
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
// Registry holds all loaded ServiceAdapters indexed by serviceName.
|
|
type Registry struct {
|
|
mu sync.RWMutex
|
|
adapters map[string]*ServiceAdapter
|
|
logger Logger
|
|
}
|
|
|
|
// Logger interface for flexible logging.
|
|
type Logger interface {
|
|
Infof(format string, args ...interface{})
|
|
Errorf(format string, args ...interface{})
|
|
}
|
|
|
|
// SimpleLogger logs to stdout/stderr.
|
|
type SimpleLogger struct{}
|
|
|
|
func (l *SimpleLogger) Infof(format string, args ...interface{}) {
|
|
fmt.Printf("[INFO] "+format+"\n", args...)
|
|
}
|
|
|
|
func (l *SimpleLogger) Errorf(format string, args ...interface{}) {
|
|
fmt.Printf("[ERROR] "+format+"\n", args...)
|
|
}
|
|
|
|
// NewRegistry creates a new ServiceAdapter registry.
|
|
func NewRegistry(logger Logger) *Registry {
|
|
if logger == nil {
|
|
logger = &SimpleLogger{}
|
|
}
|
|
return &Registry{
|
|
adapters: make(map[string]*ServiceAdapter),
|
|
logger: logger,
|
|
}
|
|
}
|
|
|
|
// Add adds or updates a ServiceAdapter in the registry.
|
|
// Malformed schemas are logged but don't crash the registry.
|
|
func (r *Registry) Add(adapter *ServiceAdapter) error {
|
|
r.mu.Lock()
|
|
defer r.mu.Unlock()
|
|
|
|
// Validate schemas (basic check - real validation in 8.3)
|
|
if err := r.validateSchemas(adapter); err != nil {
|
|
r.logger.Errorf("adapter %s has invalid schema: %v, skipping", adapter.Namespace+"/"+adapter.ServiceName, err)
|
|
return nil // Don't crash, just skip
|
|
}
|
|
|
|
r.logger.Infof("adding/updating ServiceAdapter %s/%s", adapter.Namespace, adapter.ServiceName)
|
|
adapter.CreatedAt = time.Now()
|
|
r.adapters[adapter.ServiceName] = adapter
|
|
return nil
|
|
}
|
|
|
|
// Update updates an existing ServiceAdapter.
|
|
func (r *Registry) Update(adapter *ServiceAdapter) error {
|
|
return r.Add(adapter)
|
|
}
|
|
|
|
// Delete removes a ServiceAdapter from the registry.
|
|
func (r *Registry) Delete(serviceName string) {
|
|
r.mu.Lock()
|
|
defer r.mu.Unlock()
|
|
|
|
if _, ok := r.adapters[serviceName]; ok {
|
|
r.logger.Infof("deleting ServiceAdapter %s", serviceName)
|
|
delete(r.adapters, serviceName)
|
|
}
|
|
}
|
|
|
|
// Get returns a ServiceAdapter by name.
|
|
func (r *Registry) Get(serviceName string) *ServiceAdapter {
|
|
r.mu.RLock()
|
|
defer r.mu.RUnlock()
|
|
|
|
return r.adapters[serviceName]
|
|
}
|
|
|
|
// List returns all ServiceAdapters.
|
|
func (r *Registry) List() []*ServiceAdapter {
|
|
r.mu.RLock()
|
|
defer r.mu.RUnlock()
|
|
|
|
result := make([]*ServiceAdapter, 0, len(r.adapters))
|
|
for _, adapter := range r.adapters {
|
|
result = append(result, adapter)
|
|
}
|
|
return result
|
|
}
|
|
|
|
// Count returns the number of registered adapters.
|
|
func (r *Registry) Count() int {
|
|
r.mu.RLock()
|
|
defer r.mu.RUnlock()
|
|
|
|
return len(r.adapters)
|
|
}
|
|
|
|
// validateSchemas checks for malformed requestSchema/responseSchema.
|
|
// Real validation is in 8.3 (flat KV+type DSL parser).
|
|
func (r *Registry) validateSchemas(adapter *ServiceAdapter) error {
|
|
for _, res := range adapter.Spec.Resources {
|
|
for _, method := range res.Methods {
|
|
// Basic validation: schemas shouldn't contain obviously malformed patterns
|
|
if method.RequestSchema != "" {
|
|
if err := basicSchemaCheck(method.RequestSchema); err != nil {
|
|
return fmt.Errorf("resource %s method %s requestSchema: %w", res.Name, method.Verb, err)
|
|
}
|
|
}
|
|
if method.ResponseSchema != "" {
|
|
if err := basicSchemaCheck(method.ResponseSchema); err != nil {
|
|
return fmt.Errorf("resource %s method %s responseSchema: %w", res.Name, method.Verb, err)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// basicSchemaCheck does a simple sanity check on schema strings.
|
|
// Real parsing is in 8.3.
|
|
func basicSchemaCheck(schema string) error {
|
|
if schema == "" {
|
|
return nil
|
|
}
|
|
|
|
// Reject obviously invalid patterns
|
|
if strings.Contains(schema, "{{") && !strings.Contains(schema, "}}") {
|
|
return fmt.Errorf("unclosed template braces")
|
|
}
|
|
if strings.Count(schema, "(") != strings.Count(schema, ")") {
|
|
return fmt.Errorf("mismatched parentheses")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// MockLogger for testing.
|
|
type MockLogger struct {
|
|
entries []string
|
|
mu sync.Mutex
|
|
}
|
|
|
|
func (l *MockLogger) Infof(format string, args ...interface{}) {
|
|
l.mu.Lock()
|
|
defer l.mu.Unlock()
|
|
l.entries = append(l.entries, fmt.Sprintf("[INFO] "+format, args...))
|
|
}
|
|
|
|
func (l *MockLogger) Errorf(format string, args ...interface{}) {
|
|
l.mu.Lock()
|
|
defer l.mu.Unlock()
|
|
l.entries = append(l.entries, fmt.Sprintf("[ERROR] "+format, args...))
|
|
}
|
|
|
|
func (l *MockLogger) Entries() []string {
|
|
l.mu.Lock()
|
|
defer l.mu.Unlock()
|
|
result := make([]string, len(l.entries))
|
|
copy(result, l.entries)
|
|
return result
|
|
}
|
|
|
|
func (l *MockLogger) Clear() {
|
|
l.mu.Lock()
|
|
defer l.mu.Unlock()
|
|
l.entries = nil
|
|
}
|