mirror of
https://github.com/bitechdev/ResolveSpec.git
synced 2025-12-14 01:20:36 +00:00
240 lines
6.8 KiB
Go
240 lines
6.8 KiB
Go
package websocketspec_test
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"net/http"
|
|
|
|
"github.com/bitechdev/ResolveSpec/pkg/websocketspec"
|
|
"gorm.io/driver/postgres"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
// User model example
|
|
type User struct {
|
|
ID uint `json:"id" gorm:"primaryKey"`
|
|
Name string `json:"name"`
|
|
Email string `json:"email"`
|
|
Status string `json:"status"`
|
|
}
|
|
|
|
// Post model example
|
|
type Post struct {
|
|
ID uint `json:"id" gorm:"primaryKey"`
|
|
Title string `json:"title"`
|
|
Content string `json:"content"`
|
|
UserID uint `json:"user_id"`
|
|
User *User `json:"user,omitempty" gorm:"foreignKey:UserID"`
|
|
}
|
|
|
|
// Example_basicSetup demonstrates basic WebSocketSpec setup
|
|
func Example_basicSetup() {
|
|
// Connect to database
|
|
db, err := gorm.Open(postgres.Open("your-connection-string"), &gorm.Config{})
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
// Create WebSocket handler
|
|
handler := websocketspec.NewHandlerWithGORM(db)
|
|
|
|
// Register models
|
|
handler.Registry().RegisterModel("public.users", &User{})
|
|
handler.Registry().RegisterModel("public.posts", &Post{})
|
|
|
|
// Setup WebSocket endpoint
|
|
http.HandleFunc("/ws", handler.HandleWebSocket)
|
|
|
|
// Start server
|
|
log.Println("WebSocket server starting on :8080")
|
|
if err := http.ListenAndServe(":8080", nil); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
}
|
|
|
|
// Example_withHooks demonstrates using lifecycle hooks
|
|
func Example_withHooks() {
|
|
db, _ := gorm.Open(postgres.Open("your-connection-string"), &gorm.Config{})
|
|
handler := websocketspec.NewHandlerWithGORM(db)
|
|
|
|
// Register models
|
|
handler.Registry().RegisterModel("public.users", &User{})
|
|
|
|
// Add authentication hook
|
|
handler.Hooks().Register(websocketspec.BeforeConnect, func(ctx *websocketspec.HookContext) error {
|
|
// Validate authentication token
|
|
// (In real implementation, extract from query params or headers)
|
|
userID := uint(123) // From token
|
|
|
|
// Store in connection metadata
|
|
ctx.Connection.SetMetadata("user_id", userID)
|
|
log.Printf("User %d connected", userID)
|
|
|
|
return nil
|
|
})
|
|
|
|
// Add authorization hook for read operations
|
|
handler.Hooks().RegisterBefore(websocketspec.OperationRead, func(ctx *websocketspec.HookContext) error {
|
|
userID, ok := ctx.Connection.GetMetadata("user_id")
|
|
if !ok {
|
|
return fmt.Errorf("unauthorized: not authenticated")
|
|
}
|
|
|
|
log.Printf("User %v reading %s.%s", userID, ctx.Schema, ctx.Entity)
|
|
|
|
// Add filter to only show user's own records
|
|
if ctx.Entity == "posts" {
|
|
// ctx.Options.Filters = append(ctx.Options.Filters, common.FilterOption{
|
|
// Column: "user_id",
|
|
// Operator: "eq",
|
|
// Value: userID,
|
|
// })
|
|
}
|
|
|
|
return nil
|
|
})
|
|
|
|
// Add logging hook after create
|
|
handler.Hooks().RegisterAfter(websocketspec.OperationCreate, func(ctx *websocketspec.HookContext) error {
|
|
userID, _ := ctx.Connection.GetMetadata("user_id")
|
|
log.Printf("User %v created record in %s.%s", userID, ctx.Schema, ctx.Entity)
|
|
return nil
|
|
})
|
|
|
|
// Add validation hook before create
|
|
handler.Hooks().RegisterBefore(websocketspec.OperationCreate, func(ctx *websocketspec.HookContext) error {
|
|
// Validate required fields
|
|
if data, ok := ctx.Data.(map[string]interface{}); ok {
|
|
if ctx.Entity == "users" {
|
|
if email, exists := data["email"]; !exists || email == "" {
|
|
return fmt.Errorf("validation error: email is required")
|
|
}
|
|
if name, exists := data["name"]; !exists || name == "" {
|
|
return fmt.Errorf("validation error: name is required")
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
})
|
|
|
|
// Add limit hook for subscriptions
|
|
handler.Hooks().Register(websocketspec.BeforeSubscribe, func(ctx *websocketspec.HookContext) error {
|
|
// Limit subscriptions per connection
|
|
maxSubscriptions := 10
|
|
currentCount := len(ctx.Connection.subscriptions)
|
|
|
|
if currentCount >= maxSubscriptions {
|
|
return fmt.Errorf("maximum subscriptions reached (%d)", maxSubscriptions)
|
|
}
|
|
|
|
log.Printf("Creating subscription %d/%d", currentCount+1, maxSubscriptions)
|
|
return nil
|
|
})
|
|
|
|
http.HandleFunc("/ws", handler.HandleWebSocket)
|
|
log.Println("Server with hooks starting on :8080")
|
|
http.ListenAndServe(":8080", nil)
|
|
}
|
|
|
|
// Example_monitoring demonstrates monitoring connections and subscriptions
|
|
func Example_monitoring() {
|
|
db, _ := gorm.Open(postgres.Open("your-connection-string"), &gorm.Config{})
|
|
handler := websocketspec.NewHandlerWithGORM(db)
|
|
|
|
handler.Registry.RegisterModel("public.users", &User{})
|
|
|
|
// Add connection tracking
|
|
handler.Hooks().Register(websocketspec.AfterConnect, func(ctx *websocketspec.HookContext) error {
|
|
count := handler.GetConnectionCount()
|
|
log.Printf("Client connected. Total connections: %d", count)
|
|
return nil
|
|
})
|
|
|
|
handler.Hooks().Register(websocketspec.AfterDisconnect, func(ctx *websocketspec.HookContext) error {
|
|
count := handler.GetConnectionCount()
|
|
log.Printf("Client disconnected. Total connections: %d", count)
|
|
return nil
|
|
})
|
|
|
|
// Add subscription tracking
|
|
handler.Hooks().Register(websocketspec.AfterSubscribe, func(ctx *websocketspec.HookContext) error {
|
|
count := handler.GetSubscriptionCount()
|
|
log.Printf("New subscription. Total subscriptions: %d", count)
|
|
return nil
|
|
})
|
|
|
|
// Monitoring endpoint
|
|
http.HandleFunc("/stats", func(w http.ResponseWriter, r *http.Request) {
|
|
fmt.Fprintf(w, "Active Connections: %d\n", handler.GetConnectionCount())
|
|
fmt.Fprintf(w, "Active Subscriptions: %d\n", handler.GetSubscriptionCount())
|
|
})
|
|
|
|
http.HandleFunc("/ws", handler.HandleWebSocket)
|
|
log.Println("Server with monitoring starting on :8080")
|
|
http.ListenAndServe(":8080", nil)
|
|
}
|
|
|
|
// Example_clientSide shows client-side usage example
|
|
func Example_clientSide() {
|
|
// This is JavaScript code for documentation purposes
|
|
jsCode := `
|
|
// JavaScript WebSocket Client Example
|
|
|
|
const ws = new WebSocket("ws://localhost:8080/ws");
|
|
|
|
ws.onopen = () => {
|
|
console.log("Connected to WebSocket");
|
|
|
|
// Read users
|
|
ws.send(JSON.stringify({
|
|
id: "msg-1",
|
|
type: "request",
|
|
operation: "read",
|
|
schema: "public",
|
|
entity: "users",
|
|
options: {
|
|
filters: [{column: "status", operator: "eq", value: "active"}],
|
|
limit: 10
|
|
}
|
|
}));
|
|
|
|
// Subscribe to user changes
|
|
ws.send(JSON.stringify({
|
|
id: "sub-1",
|
|
type: "subscription",
|
|
operation: "subscribe",
|
|
schema: "public",
|
|
entity: "users",
|
|
options: {
|
|
filters: [{column: "status", operator: "eq", value: "active"}]
|
|
}
|
|
}));
|
|
};
|
|
|
|
ws.onmessage = (event) => {
|
|
const message = JSON.parse(event.data);
|
|
|
|
if (message.type === "response") {
|
|
if (message.success) {
|
|
console.log("Response:", message.data);
|
|
} else {
|
|
console.error("Error:", message.error);
|
|
}
|
|
} else if (message.type === "notification") {
|
|
console.log("Notification:", message.operation, message.data);
|
|
}
|
|
};
|
|
|
|
ws.onerror = (error) => {
|
|
console.error("WebSocket error:", error);
|
|
};
|
|
|
|
ws.onclose = () => {
|
|
console.log("WebSocket connection closed");
|
|
// Implement reconnection logic here
|
|
};
|
|
`
|
|
|
|
fmt.Println(jsCode)
|
|
}
|