Files
sonr/client/modules/svc/client.go
T

560 lines
21 KiB
Go
Raw Normal View History

2025-10-03 14:45:52 -04:00
// Package svc provides a client interface for interacting with the Sonr SVC (Service) module.
package svc
import (
"context"
"fmt"
"time"
"google.golang.org/grpc"
"github.com/cosmos/cosmos-sdk/types/tx"
"github.com/sonr-io/sonr/client/config"
"github.com/sonr-io/sonr/client/errors"
svctypes "github.com/sonr-io/sonr/x/svc/types"
)
// Client provides an interface for interacting with the SVC module.
type Client interface {
// Service Operations
RegisterService(ctx context.Context, opts *RegisterServiceOptions) (*Service, error)
UpdateService(ctx context.Context, serviceID string, opts *UpdateServiceOptions) (*Service, error)
DeregisterService(ctx context.Context, serviceID string) error
GetService(ctx context.Context, serviceID string) (*Service, error)
// Service Discovery
DiscoverServices(ctx context.Context, query *ServiceQuery) (*ServiceDiscoveryResponse, error)
ListServices(ctx context.Context, opts *ListServicesOptions) (*ServiceListResponse, error)
SearchServices(ctx context.Context, searchTerm string) (*ServiceSearchResponse, error)
// Domain Operations
RegisterDomain(ctx context.Context, opts *RegisterDomainOptions) (*Domain, error)
VerifyDomain(ctx context.Context, domain string) (*DomainVerification, error)
GetDomain(ctx context.Context, domain string) (*Domain, error)
ListDomains(ctx context.Context, opts *ListDomainsOptions) (*DomainListResponse, error)
// Service Capabilities
AddCapability(ctx context.Context, serviceID string, capability *Capability) error
RemoveCapability(ctx context.Context, serviceID string, capabilityID string) error
ListCapabilities(ctx context.Context, serviceID string) ([]*Capability, error)
// Service Endpoints
AddEndpoint(ctx context.Context, serviceID string, endpoint *Endpoint) error
UpdateEndpoint(ctx context.Context, serviceID string, endpointID string, opts *UpdateEndpointOptions) error
RemoveEndpoint(ctx context.Context, serviceID string, endpointID string) error
// Service Health
CheckServiceHealth(ctx context.Context, serviceID string) (*HealthStatus, error)
UpdateServiceHealth(ctx context.Context, serviceID string, status *HealthStatus) error
}
// Service represents a registered service.
type Service struct {
ID string `json:"id"`
Name string `json:"name"`
Description string `json:"description,omitempty"`
Owner string `json:"owner"`
Domain string `json:"domain,omitempty"`
Version string `json:"version"`
Type string `json:"type"`
Endpoints []*Endpoint `json:"endpoints"`
Capabilities []*Capability `json:"capabilities"`
Metadata map[string]any `json:"metadata,omitempty"`
HealthStatus *HealthStatus `json:"health_status,omitempty"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at,omitempty"`
Tags []string `json:"tags,omitempty"`
}
// RegisterServiceOptions configures service registration.
type RegisterServiceOptions struct {
Name string `json:"name"`
Description string `json:"description,omitempty"`
Domain string `json:"domain,omitempty"`
Version string `json:"version"`
Type string `json:"type"`
Endpoints []*Endpoint `json:"endpoints"`
Capabilities []*Capability `json:"capabilities,omitempty"`
Metadata map[string]any `json:"metadata,omitempty"`
Tags []string `json:"tags,omitempty"`
}
// UpdateServiceOptions configures service updates.
type UpdateServiceOptions struct {
Description string `json:"description,omitempty"`
Version string `json:"version,omitempty"`
Metadata map[string]any `json:"metadata,omitempty"`
Tags []string `json:"tags,omitempty"`
}
// Endpoint represents a service endpoint.
type Endpoint struct {
ID string `json:"id"`
URL string `json:"url"`
Type string `json:"type"` // REST, GraphQL, gRPC, WebSocket, etc.
Method string `json:"method,omitempty"`
Headers map[string]string `json:"headers,omitempty"`
Metadata map[string]any `json:"metadata,omitempty"`
Enabled bool `json:"enabled"`
}
// UpdateEndpointOptions configures endpoint updates.
type UpdateEndpointOptions struct {
URL string `json:"url,omitempty"`
Headers map[string]string `json:"headers,omitempty"`
Metadata map[string]any `json:"metadata,omitempty"`
Enabled *bool `json:"enabled,omitempty"`
}
// Capability represents a service capability.
type Capability struct {
ID string `json:"id"`
Name string `json:"name"`
Description string `json:"description,omitempty"`
Type string `json:"type"`
Schema map[string]any `json:"schema,omitempty"`
Metadata map[string]any `json:"metadata,omitempty"`
Enabled bool `json:"enabled"`
}
// ServiceQuery defines query parameters for service discovery.
type ServiceQuery struct {
Type string `json:"type,omitempty"`
Domain string `json:"domain,omitempty"`
Tags []string `json:"tags,omitempty"`
Capabilities []string `json:"capabilities,omitempty"`
Metadata map[string]string `json:"metadata,omitempty"`
HealthStatus string `json:"health_status,omitempty"`
}
// ServiceDiscoveryResponse contains service discovery results.
type ServiceDiscoveryResponse struct {
Services []*Service `json:"services"`
TotalCount uint64 `json:"total_count"`
Query *ServiceQuery `json:"query"`
}
// ListServicesOptions configures service listing.
type ListServicesOptions struct {
Owner string `json:"owner,omitempty"`
Type string `json:"type,omitempty"`
Limit uint64 `json:"limit,omitempty"`
Offset uint64 `json:"offset,omitempty"`
}
// ServiceListResponse contains a list of services.
type ServiceListResponse struct {
Services []*Service `json:"services"`
TotalCount uint64 `json:"total_count"`
Limit uint64 `json:"limit"`
Offset uint64 `json:"offset"`
}
// ServiceSearchResponse contains service search results.
type ServiceSearchResponse struct {
Services []*Service `json:"services"`
TotalCount uint64 `json:"total_count"`
SearchTerm string `json:"search_term"`
}
// Domain represents a registered domain.
type Domain struct {
Name string `json:"name"`
Owner string `json:"owner"`
Verified bool `json:"verified"`
Verification *DomainVerification `json:"verification,omitempty"`
Metadata map[string]any `json:"metadata,omitempty"`
RegisteredAt string `json:"registered_at"`
ExpiresAt string `json:"expires_at,omitempty"`
}
// RegisterDomainOptions configures domain registration.
type RegisterDomainOptions struct {
Name string `json:"name"`
Metadata map[string]any `json:"metadata,omitempty"`
ExpiresAt string `json:"expires_at,omitempty"`
}
// DomainVerification contains domain verification information.
type DomainVerification struct {
Method string `json:"method"` // DNS, HTTP, File
Token string `json:"token"` // Verification token
Challenge string `json:"challenge"` // Challenge string
Verified bool `json:"verified"`
VerifiedAt string `json:"verified_at,omitempty"`
ExpiresAt string `json:"expires_at,omitempty"`
}
// ListDomainsOptions configures domain listing.
type ListDomainsOptions struct {
Owner string `json:"owner,omitempty"`
Verified *bool `json:"verified,omitempty"`
Limit uint64 `json:"limit,omitempty"`
Offset uint64 `json:"offset,omitempty"`
}
// DomainListResponse contains a list of domains.
type DomainListResponse struct {
Domains []*Domain `json:"domains"`
TotalCount uint64 `json:"total_count"`
Limit uint64 `json:"limit"`
Offset uint64 `json:"offset"`
}
// HealthStatus represents service health status.
type HealthStatus struct {
Status string `json:"status"` // healthy, unhealthy, degraded, unknown
LastChecked string `json:"last_checked"`
Message string `json:"message,omitempty"`
Metrics map[string]any `json:"metrics,omitempty"`
Uptime string `json:"uptime,omitempty"`
}
// client implements the SVC Client interface.
type client struct {
grpcConn *grpc.ClientConn
config *config.NetworkConfig
// Service clients for SVC module
queryClient svctypes.QueryClient
msgClient svctypes.MsgClient
txClient tx.ServiceClient
}
// NewClient creates a new SVC module client.
func NewClient(grpcConn *grpc.ClientConn, cfg *config.NetworkConfig) Client {
return &client{
grpcConn: grpcConn,
config: cfg,
queryClient: svctypes.NewQueryClient(grpcConn),
msgClient: svctypes.NewMsgClient(grpcConn),
txClient: tx.NewServiceClient(grpcConn),
}
}
// RegisterService registers a new service.
func (c *client) RegisterService(ctx context.Context, opts *RegisterServiceOptions) (*Service, error) {
// TODO: Implement service registration using SVC module
// Should build MsgRegisterService with proper validation
// Submit transaction to chain and wait for confirmation
// Handle domain verification if domain is provided
// Return complete service record with generated ID
return nil, errors.NewModuleError("svc", "RegisterService",
fmt.Errorf("service registration not yet implemented"))
}
// UpdateService updates an existing service.
func (c *client) UpdateService(ctx context.Context, serviceID string, opts *UpdateServiceOptions) (*Service, error) {
// TODO: Implement service updates using SVC module
// Should validate service ownership and permissions
// Build MsgUpdateService with selective field updates
// Preserve existing endpoints and capabilities unless modified
// Return updated service record
return nil, errors.NewModuleError("svc", "UpdateService",
fmt.Errorf("service updates not yet implemented"))
}
// DeregisterService deregisters a service.
func (c *client) DeregisterService(ctx context.Context, serviceID string) error {
// TODO: Implement service deregistration using SVC module
// Should validate service ownership before deregistration
// Build MsgDeregisterService and submit to chain
// Clean up associated domain registrations and capabilities
// Handle graceful shutdown of service endpoints
return errors.NewModuleError("svc", "DeregisterService",
fmt.Errorf("service deregistration not yet implemented"))
}
// GetService retrieves a service by ID.
func (c *client) GetService(ctx context.Context, serviceID string) (*Service, error) {
// TODO: Implement service retrieval using SVC query client
// Should query chain state for service record
// Convert protobuf service to client Service type
// Include current health status and endpoint information
// Handle service not found errors gracefully
return nil, errors.NewModuleError("svc", "GetService",
fmt.Errorf("service retrieval not yet implemented"))
}
// DiscoverServices discovers services based on query criteria.
func (c *client) DiscoverServices(ctx context.Context, query *ServiceQuery) (*ServiceDiscoveryResponse, error) {
// TODO: Implement service discovery using SVC module
// Should support filtering by type, domain, tags, capabilities
// Query chain state with proper pagination
// Filter results by health status if specified
// Return ranked results based on relevance
return nil, errors.NewModuleError("svc", "DiscoverServices",
fmt.Errorf("service discovery not yet implemented"))
}
// ListServices lists services with pagination.
func (c *client) ListServices(ctx context.Context, opts *ListServicesOptions) (*ServiceListResponse, error) {
// TODO: Implement service listing using SVC module
// Should support pagination with limit/offset
// Filter by owner and service type if specified
// Return services with basic metadata and status
// Handle empty result sets gracefully
return nil, errors.NewModuleError("svc", "ListServices",
fmt.Errorf("service listing not yet implemented"))
}
// SearchServices searches for services by term.
func (c *client) SearchServices(ctx context.Context, searchTerm string) (*ServiceSearchResponse, error) {
// TODO: Implement service search using SVC module
// Should search service names, descriptions, and tags
// Support fuzzy matching and relevance scoring
// Query multiple fields with OR logic
// Return results ranked by relevance
return nil, errors.NewModuleError("svc", "SearchServices",
fmt.Errorf("service search not yet implemented"))
}
// RegisterDomain registers a new domain.
func (c *client) RegisterDomain(ctx context.Context, opts *RegisterDomainOptions) (*Domain, error) {
// TODO: Implement domain registration using SVC module
// Should validate domain name format and availability
// Build MsgInitiateDomainVerification and submit to chain
// Generate verification challenge tokens
// Return domain record with verification instructions
return nil, errors.NewModuleError("svc", "RegisterDomain",
fmt.Errorf("domain registration not yet implemented"))
}
// VerifyDomain verifies domain ownership.
func (c *client) VerifyDomain(ctx context.Context, domain string) (*DomainVerification, error) {
// TODO: Implement domain verification using SVC module
// Should check DNS records, HTTP endpoints, or file verification
// Build MsgVerifyDomain and submit proof to chain
// Update domain status to verified upon success
// Handle verification failures with clear error messages
return nil, errors.NewModuleError("svc", "VerifyDomain",
fmt.Errorf("domain verification not yet implemented"))
}
// GetDomain retrieves domain information.
func (c *client) GetDomain(ctx context.Context, domain string) (*Domain, error) {
// TODO: Implement domain retrieval using SVC query client
// Should query chain state for domain registration
// Include verification status and expiration information
// Convert protobuf domain to client Domain type
// Handle domain not found cases
return nil, errors.NewModuleError("svc", "GetDomain",
fmt.Errorf("domain retrieval not yet implemented"))
}
// ListDomains lists domains with pagination.
func (c *client) ListDomains(ctx context.Context, opts *ListDomainsOptions) (*DomainListResponse, error) {
// TODO: Implement domain listing using SVC module
// Should support pagination and filtering by owner
// Filter by verification status if specified
// Return domains with metadata and expiration info
// Handle empty result sets
return nil, errors.NewModuleError("svc", "ListDomains",
fmt.Errorf("domain listing not yet implemented"))
}
// AddCapability adds a capability to a service.
func (c *client) AddCapability(ctx context.Context, serviceID string, capability *Capability) error {
// TODO: Implement capability addition using SVC module
// Should validate service ownership and capability schema
// Build MsgAddCapability and submit to chain
// Update service record with new capability
// Validate capability name uniqueness within service
return errors.NewModuleError("svc", "AddCapability",
fmt.Errorf("capability addition not yet implemented"))
}
// RemoveCapability removes a capability from a service.
func (c *client) RemoveCapability(ctx context.Context, serviceID string, capabilityID string) error {
// TODO: Implement capability removal using SVC module
// Should validate service ownership and capability existence
// Build MsgRemoveCapability and submit to chain
// Check for dependent services using this capability
// Handle cascading capability removal safely
return errors.NewModuleError("svc", "RemoveCapability",
fmt.Errorf("capability removal not yet implemented"))
}
// ListCapabilities lists service capabilities.
func (c *client) ListCapabilities(ctx context.Context, serviceID string) ([]*Capability, error) {
// TODO: Implement capability listing using SVC module
// Should query service record for capabilities
// Return capabilities with schemas and metadata
// Include capability status (enabled/disabled)
// Handle service not found errors
return nil, errors.NewModuleError("svc", "ListCapabilities",
fmt.Errorf("capability listing not yet implemented"))
}
// AddEndpoint adds an endpoint to a service.
func (c *client) AddEndpoint(ctx context.Context, serviceID string, endpoint *Endpoint) error {
// TODO: Implement endpoint addition using SVC module
// Should validate service ownership and endpoint URL format
// Build MsgAddEndpoint and submit to chain
// Validate endpoint accessibility if enabled
// Update service record with new endpoint
return errors.NewModuleError("svc", "AddEndpoint",
fmt.Errorf("endpoint addition not yet implemented"))
}
// UpdateEndpoint updates a service endpoint.
func (c *client) UpdateEndpoint(ctx context.Context, serviceID string, endpointID string, opts *UpdateEndpointOptions) error {
// TODO: Implement endpoint updates using SVC module
// Should validate service ownership and endpoint existence
// Build MsgUpdateEndpoint with selective field updates
// Validate new URL format and accessibility
// Preserve existing headers and metadata unless modified
return errors.NewModuleError("svc", "UpdateEndpoint",
fmt.Errorf("endpoint updates not yet implemented"))
}
// RemoveEndpoint removes an endpoint from a service.
func (c *client) RemoveEndpoint(ctx context.Context, serviceID string, endpointID string) error {
// TODO: Implement endpoint removal using SVC module
// Should validate service ownership and endpoint existence
// Build MsgRemoveEndpoint and submit to chain
// Check if endpoint is primary before removal
// Handle graceful endpoint shutdown
return errors.NewModuleError("svc", "RemoveEndpoint",
fmt.Errorf("endpoint removal not yet implemented"))
}
// CheckServiceHealth checks the health status of a service.
func (c *client) CheckServiceHealth(ctx context.Context, serviceID string) (*HealthStatus, error) {
// TODO: Implement health checking using SVC module
// Should query service endpoints for health status
// Aggregate health across multiple endpoints
// Check endpoint response times and error rates
// Return comprehensive health report with metrics
return nil, errors.NewModuleError("svc", "CheckServiceHealth",
fmt.Errorf("health checking not yet implemented"))
}
// UpdateServiceHealth updates the health status of a service.
func (c *client) UpdateServiceHealth(ctx context.Context, serviceID string, status *HealthStatus) error {
// TODO: Implement health status updates using SVC module
// Should validate service ownership and status format
// Build MsgUpdateServiceHealth and submit to chain
// Update service discovery with new health status
// Store health metrics and historical data
return errors.NewModuleError("svc", "UpdateServiceHealth",
fmt.Errorf("health status updates not yet implemented"))
}
// Utility functions
// GenerateServiceID generates a unique service ID.
func GenerateServiceID(name string) string {
// TODO: Implement proper service ID generation
return fmt.Sprintf("svc_%s_%d", name, time.Now().UnixNano())
}
// ValidateServiceName validates a service name.
func ValidateServiceName(name string) error {
if len(name) == 0 {
return fmt.Errorf("service name cannot be empty")
}
if len(name) > 100 {
return fmt.Errorf("service name too long")
}
return nil
}
// CreateDefaultEndpoint creates a default HTTP endpoint.
func CreateDefaultEndpoint(url string) *Endpoint {
return &Endpoint{
ID: fmt.Sprintf("endpoint_%d", time.Now().UnixNano()),
URL: url,
Type: "REST",
Method: "GET",
Enabled: true,
}
}
// CreateHealthyStatus creates a healthy status.
func CreateHealthyStatus() *HealthStatus {
return &HealthStatus{
Status: "healthy",
LastChecked: time.Now().UTC().Format(time.RFC3339),
Message: "Service is operating normally",
}
}
// Message Builders - These create the actual transaction messages
// BuildMsgRegisterService builds a MsgRegisterService message.
func BuildMsgRegisterService(creator string, opts *RegisterServiceOptions) (*svctypes.MsgRegisterService, error) {
if opts == nil {
return nil, fmt.Errorf("options cannot be nil")
}
if err := ValidateServiceName(opts.Name); err != nil {
return nil, fmt.Errorf("invalid service name: %w", err)
}
// Generate service ID if not provided
serviceID := GenerateServiceID(opts.Name)
// Extract requested permissions from capabilities
var requestedPermissions []string
for _, cap := range opts.Capabilities {
if cap != nil {
requestedPermissions = append(requestedPermissions, cap.Name)
}
}
return &svctypes.MsgRegisterService{
Creator: creator,
ServiceId: serviceID,
Domain: opts.Domain,
RequestedPermissions: requestedPermissions,
UcanDelegationChain: "", // Will be set if UCAN is used
}, nil
}
// BuildMsgInitiateDomainVerification builds a MsgInitiateDomainVerification message.
func BuildMsgInitiateDomainVerification(creator, domain string) (*svctypes.MsgInitiateDomainVerification, error) {
if domain == "" {
return nil, fmt.Errorf("domain cannot be empty")
}
return &svctypes.MsgInitiateDomainVerification{
Creator: creator,
Domain: domain,
}, nil
}
// BuildMsgVerifyDomain builds a MsgVerifyDomain message.
func BuildMsgVerifyDomain(creator, domain string) (*svctypes.MsgVerifyDomain, error) {
if domain == "" {
return nil, fmt.Errorf("domain cannot be empty")
}
return &svctypes.MsgVerifyDomain{
Creator: creator,
Domain: domain,
}, nil
}