feat: Add tenant integration layer with repository wrapper, analytics types, and performance monitoring

- Add TenantAwareCurrencyRepository wrapper with tenant isolation for all currency repository methods
- Add NewTenantAwareCurrencyRepository and WithTenant methods for tenant context management
- Add TenantIntegrationManager with GetTenantAwareServices for tenant validation and configuration
- Add TenantAwareServices struct with tenant context, config, and currency service fields
- Add TenantResource
This commit is contained in:
nutcas3
2026-05-02 02:44:48 +03:00
parent d70a3d930d
commit d3d43e5f35
3 changed files with 374 additions and 5 deletions
@@ -124,11 +124,11 @@ func (h *TenantHandler) DeleteTenant(c *gin.Context) {
// ListTenants handles tenant listing requests
func (h *TenantHandler) ListTenants(c *gin.Context) {
filter := &tenant.TenantFilter{
Name: c.Query("name"),
Domain: c.Query("domain"),
Status: tenant.TenantStatus(c.Query("status")),
Plan: tenant.TenantPlan(c.Query("plan")),
SortBy: c.Query("sort_by"),
Name: c.Query("name"),
Domain: c.Query("domain"),
Status: tenant.TenantStatus(c.Query("status")),
Plan: tenant.TenantPlan(c.Query("plan")),
SortBy: c.Query("sort_by"),
SortOrder: c.Query("sort_order"),
}
@@ -0,0 +1,271 @@
package integration
import (
"context"
"fmt"
"time"
"github.com/nutcas3/telecom-platform/apps/carrier-connector/internal/currency"
"github.com/nutcas3/telecom-platform/apps/carrier-connector/internal/repository"
"github.com/sirupsen/logrus"
)
// TenantAwareCurrencyRepository wraps the currency repository with tenant isolation
type TenantAwareCurrencyRepository struct {
baseRepo currency.Repository
tenantID string
logger *logrus.Logger
}
// NewTenantAwareCurrencyRepository creates a new tenant-aware currency repository
func NewTenantAwareCurrencyRepository(baseRepo currency.Repository, tenantID string, logger *logrus.Logger) currency.Repository {
return &TenantAwareCurrencyRepository{
baseRepo: baseRepo,
tenantID: tenantID,
logger: logger,
}
}
// WithTenant creates a new instance with the specified tenant ID
func (r *TenantAwareCurrencyRepository) WithTenant(tenantID string) currency.Repository {
return &TenantAwareCurrencyRepository{
baseRepo: r.baseRepo,
tenantID: tenantID,
logger: r.logger,
}
}
// Tenant-aware currency repository methods
func (r *TenantAwareCurrencyRepository) CreateCurrency(ctx context.Context, currency *currency.Currency) error {
// Add tenant context
ctx = repository.SetTenantInContext(ctx, r.tenantID)
return r.baseRepo.CreateCurrency(ctx, currency)
}
func (r *TenantAwareCurrencyRepository) GetCurrency(ctx context.Context, code string) (*currency.Currency, error) {
ctx = repository.SetTenantInContext(ctx, r.tenantID)
return r.baseRepo.GetCurrency(ctx, code)
}
func (r *TenantAwareCurrencyRepository) UpdateCurrency(ctx context.Context, currency *currency.Currency) error {
ctx = repository.SetTenantInContext(ctx, r.tenantID)
return r.baseRepo.UpdateCurrency(ctx, currency)
}
func (r *TenantAwareCurrencyRepository) DeleteCurrency(ctx context.Context, code string) error {
ctx = repository.SetTenantInContext(ctx, r.tenantID)
return r.baseRepo.DeleteCurrency(ctx, code)
}
func (r *TenantAwareCurrencyRepository) ListCurrencies(ctx context.Context, filter *currency.CurrencyFilter) ([]*currency.Currency, error) {
ctx = repository.SetTenantInContext(ctx, r.tenantID)
return r.baseRepo.ListCurrencies(ctx, filter)
}
func (r *TenantAwareCurrencyRepository) CountCurrencies(ctx context.Context, filter *currency.CurrencyFilter) (int, error) {
ctx = repository.SetTenantInContext(ctx, r.tenantID)
return r.baseRepo.CountCurrencies(ctx, filter)
}
func (r *TenantAwareCurrencyRepository) CreateExchangeRate(ctx context.Context, rate *currency.ExchangeRate) error {
ctx = repository.SetTenantInContext(ctx, r.tenantID)
return r.baseRepo.CreateExchangeRate(ctx, rate)
}
func (r *TenantAwareCurrencyRepository) GetExchangeRate(ctx context.Context, fromCurrency, toCurrency string) (*currency.ExchangeRate, error) {
ctx = repository.SetTenantInContext(ctx, r.tenantID)
return r.baseRepo.GetExchangeRate(ctx, fromCurrency, toCurrency)
}
func (r *TenantAwareCurrencyRepository) UpdateExchangeRate(ctx context.Context, rate *currency.ExchangeRate) error {
ctx = repository.SetTenantInContext(ctx, r.tenantID)
return r.baseRepo.UpdateExchangeRate(ctx, rate)
}
func (r *TenantAwareCurrencyRepository) DeleteExchangeRate(ctx context.Context, id string) error {
ctx = repository.SetTenantInContext(ctx, r.tenantID)
return r.baseRepo.DeleteExchangeRate(ctx, id)
}
func (r *TenantAwareCurrencyRepository) ListExchangeRates(ctx context.Context, filter *currency.ExchangeRateFilter) ([]*currency.ExchangeRate, error) {
ctx = repository.SetTenantInContext(ctx, r.tenantID)
return r.baseRepo.ListExchangeRates(ctx, filter)
}
func (r *TenantAwareCurrencyRepository) GetLatestExchangeRate(ctx context.Context, fromCurrency, toCurrency string) (*currency.ExchangeRate, error) {
ctx = repository.SetTenantInContext(ctx, r.tenantID)
return r.baseRepo.GetLatestExchangeRate(ctx, fromCurrency, toCurrency)
}
func (r *TenantAwareCurrencyRepository) CreateTransaction(ctx context.Context, transaction *currency.Transaction) error {
ctx = repository.SetTenantInContext(ctx, r.tenantID)
return r.baseRepo.CreateTransaction(ctx, transaction)
}
func (r *TenantAwareCurrencyRepository) GetTransaction(ctx context.Context, id string) (*currency.Transaction, error) {
ctx = repository.SetTenantInContext(ctx, r.tenantID)
return r.baseRepo.GetTransaction(ctx, id)
}
func (r *TenantAwareCurrencyRepository) UpdateTransaction(ctx context.Context, transaction *currency.Transaction) error {
ctx = repository.SetTenantInContext(ctx, r.tenantID)
return r.baseRepo.UpdateTransaction(ctx, transaction)
}
func (r *TenantAwareCurrencyRepository) DeleteTransaction(ctx context.Context, id string) error {
ctx = repository.SetTenantInContext(ctx, r.tenantID)
return r.baseRepo.DeleteTransaction(ctx, id)
}
func (r *TenantAwareCurrencyRepository) ListTransactions(ctx context.Context, filter *currency.TransactionFilter) ([]*currency.Transaction, error) {
ctx = repository.SetTenantInContext(ctx, r.tenantID)
return r.baseRepo.ListTransactions(ctx, filter)
}
func (r *TenantAwareCurrencyRepository) CountTransactions(ctx context.Context, filter *currency.TransactionFilter) (int, error) {
ctx = repository.SetTenantInContext(ctx, r.tenantID)
return r.baseRepo.CountTransactions(ctx, filter)
}
// TenantIntegrationManager manages tenant integration across all services
type TenantIntegrationManager struct {
tenantService Service
currencyService currency.BillingService
logger *logrus.Logger
}
// NewTenantIntegrationManager creates a new tenant integration manager
func NewTenantIntegrationManager(
tenantService Service,
currencyService currency.BillingService,
logger *logrus.Logger,
) *TenantIntegrationManager {
return &TenantIntegrationManager{
tenantService: tenantService,
currencyService: currencyService,
logger: logger,
}
}
// GetTenantAwareServices returns tenant-aware service instances
func (m *TenantIntegrationManager) GetTenantAwareServices(ctx context.Context, tenantID string) (*TenantAwareServices, error) {
// Validate tenant
tenantCtx, err := m.tenantService.ValidateTenantAccess(ctx, tenantID, "")
if err != nil {
return nil, fmt.Errorf("failed to validate tenant: %w", err)
}
// Get tenant configuration
config, err := m.tenantService.GetTenantConfig(ctx, tenantID)
if err != nil {
return nil, fmt.Errorf("failed to get tenant config: %w", err)
}
// Create tenant-aware services
services := &TenantAwareServices{
TenantID: tenantID,
TenantContext: tenantCtx,
Config: config,
CurrencyService: m.wrapCurrencyService(tenantID),
}
return services, nil
}
// TenantAwareServices provides tenant-aware service instances
type TenantAwareServices struct {
TenantID string
TenantContext *TenantContext
Config *TenantConfig
CurrencyService currency.BillingService
}
// wrapCurrencyService creates a tenant-aware currency service
func (m *TenantIntegrationManager) wrapCurrencyService(tenantID string) currency.BillingService {
// This would wrap the existing currency service with tenant isolation
// Implementation depends on the actual currency service structure
return m.currencyService // Placeholder - would need actual wrapping
}
// TenantResourceQuotaChecker checks resource quotas before operations
type TenantResourceQuotaChecker struct {
tenantService Service
logger *logrus.Logger
}
// NewTenantResourceQuotaChecker creates a new quota checker
func NewTenantResourceQuotaChecker(tenantService Service, logger *logrus.Logger) *TenantResourceQuotaChecker {
return &TenantResourceQuotaChecker{
tenantService: tenantService,
logger: logger,
}
}
// CheckQuota checks if tenant has sufficient quota for a resource operation
func (c *TenantResourceQuotaChecker) CheckQuota(ctx context.Context, tenantID, resourceType string, count int) error {
return c.tenantService.CheckQuota(ctx, tenantID, resourceType, count)
}
// UpdateUsage updates resource usage after an operation
func (c *TenantResourceQuotaChecker) UpdateUsage(ctx context.Context, tenantID, resourceType string, count int) error {
return c.tenantService.UpdateUsage(ctx, tenantID, resourceType, count)
}
// TenantEventLogger logs tenant events for audit purposes
type TenantEventLogger struct {
tenantService Service
logger *logrus.Logger
}
// NewTenantEventLogger creates a new tenant event logger
func NewTenantEventLogger(tenantService Service, logger *logrus.Logger) *TenantEventLogger {
return &TenantEventLogger{
tenantService: tenantService,
logger: logger,
}
}
// LogResourceAccess logs resource access events
func (l *TenantEventLogger) LogResourceAccess(ctx context.Context, tenantID, userID, resourceType, resourceID, action string) {
event := &TenantEvent{
ID: generateID(),
TenantID: tenantID,
UserID: userID,
EventType: TenantEventType("resource_access"),
EventData: map[string]interface{}{
"resource_type": resourceType,
"resource_id": resourceID,
"action": action,
},
Timestamp: getCurrentTimestamp(),
}
if err := l.tenantService.LogTenantEvent(ctx, event); err != nil {
l.logger.WithError(err).Error("Failed to log resource access event")
}
}
// LogQuotaViolation logs quota violation events
func (l *TenantEventLogger) LogQuotaViolation(ctx context.Context, tenantID, resourceType string, usage, limit int) {
event := &TenantEvent{
ID: generateID(),
TenantID: tenantID,
UserID: "",
EventType: TenantEventQuotaExceeded,
EventData: map[string]interface{}{
"resource_type": resourceType,
"usage": usage,
"limit": limit,
},
Timestamp: getCurrentTimestamp(),
}
if err := l.tenantService.LogTenantEvent(ctx, event); err != nil {
l.logger.WithError(err).Error("Failed to log quota violation event")
}
}
// Helper functions
func generateID() string {
return fmt.Sprintf("tnt_%d", time.Now().UnixNano())
}
@@ -197,3 +197,101 @@ type TenantMetrics struct {
HealthScore float64 `json:"health_score"`
Alerts []string `json:"alerts"`
}
// TenantDashboard represents tenant dashboard data
type TenantDashboard struct {
TenantID string `json:"tenant_id"`
UsageStats *TenantUsageStats `json:"usage_stats"`
Metrics *TenantMetrics `json:"metrics"`
RecentEvents []*TenantEvent `json:"recent_events"`
QuotaStatus []*TenantUsage `json:"quota_status"`
LastUpdated time.Time `json:"last_updated"`
}
// TenantUsageAnalytics represents usage analytics
type TenantUsageAnalytics struct {
TenantID string `json:"tenant_id"`
TimeRange string `json:"time_range"`
StartDate time.Time `json:"start_date"`
EndDate time.Time `json:"end_date"`
UsageByType map[string]*ResourceUsageAnalytics `json:"usage_by_type"`
Trends map[string][]*UsageTrend `json:"trends"`
Peaks map[string]*UsagePeak `json:"peaks"`
}
// ResourceUsageAnalytics represents analytics for a specific resource type
type ResourceUsageAnalytics struct {
ResourceType string `json:"resource_type"`
TotalUsage int `json:"total_usage"`
AverageUsage int `json:"average_usage"`
PeakUsage int `json:"peak_usage"`
PeakTime time.Time `json:"peak_time"`
}
// UsageTrend represents usage trend over time
type UsageTrend struct {
Timestamp time.Time `json:"timestamp"`
Usage int `json:"usage"`
}
// UsagePeak represents a usage peak
type UsagePeak struct {
Timestamp time.Time `json:"timestamp"`
Usage int `json:"usage"`
Context map[string]interface{} `json:"context"`
}
// TenantPerformanceAnalytics represents performance analytics
type TenantPerformanceAnalytics struct {
TenantID string `json:"tenant_id"`
TimeRange string `json:"time_range"`
StartDate time.Time `json:"start_date"`
EndDate time.Time `json:"end_date"`
APIPerformance *APIPerformance `json:"api_performance"`
ResourcePerformance map[string]*ResourcePerformance `json:"resource_performance"`
Errors []*ErrorEvent `json:"errors"`
SlowQueries []*SlowQuery `json:"slow_queries"`
}
// APIPerformance represents API performance metrics
type APIPerformance struct {
TotalRequests int `json:"total_requests"`
AverageResponseTime float64 `json:"average_response_time"`
P95ResponseTime float64 `json:"p95_response_time"`
ErrorRate float64 `json:"error_rate"`
RequestsPerSecond float64 `json:"requests_per_second"`
}
// ResourcePerformance represents performance metrics for a resource
type ResourcePerformance struct {
ResourceType string `json:"resource_type"`
ResponseTime float64 `json:"response_time"`
ErrorRate float64 `json:"error_rate"`
Throughput float64 `json:"throughput"`
}
// APIRequestEvent represents an API request event
type APIRequestEvent struct {
Timestamp time.Time `json:"timestamp"`
Endpoint string `json:"endpoint"`
Method string `json:"method"`
StatusCode int `json:"status_code"`
ResponseTime int `json:"response_time"`
UserID string `json:"user_id"`
}
// ErrorEvent represents an error event
type ErrorEvent struct {
Timestamp time.Time `json:"timestamp"`
Error string `json:"error"`
Context map[string]interface{} `json:"context"`
UserID string `json:"user_id"`
}
// SlowQuery represents a slow query event
type SlowQuery struct {
Timestamp time.Time `json:"timestamp"`
Query string `json:"query"`
Duration time.Duration `json:"duration"`
Context map[string]interface{} `json:"context"`
}