270 lines
7.5 KiB
Go
270 lines
7.5 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"time"
|
|
)
|
|
|
|
// ExampleClient demonstrates how to interact with the event store API
|
|
type ExampleClient struct {
|
|
baseURL string
|
|
client *http.Client
|
|
}
|
|
|
|
func NewExampleClient(baseURL string) *ExampleClient {
|
|
return &ExampleClient{
|
|
baseURL: baseURL,
|
|
client: &http.Client{Timeout: 10 * time.Second},
|
|
}
|
|
}
|
|
|
|
func (c *ExampleClient) CreateItem(itemID, content string) error {
|
|
// Create item using JSON Patch "add" operations
|
|
patches := []PatchOperation{
|
|
{Op: "add", Path: "/content", Value: content},
|
|
}
|
|
|
|
return c.sendPatch("shopping_items", itemID, patches)
|
|
}
|
|
|
|
func (c *ExampleClient) UpdateItem(itemID, content string) error {
|
|
// Update item using JSON Patch "replace" operation
|
|
patches := []PatchOperation{
|
|
{Op: "replace", Path: "/content", Value: content},
|
|
}
|
|
|
|
return c.sendPatch("shopping_items", itemID, patches)
|
|
}
|
|
|
|
func (c *ExampleClient) DeleteItem(itemID string) error {
|
|
// Delete item using JSON Patch "add" operation for deleted_at
|
|
patches := []PatchOperation{
|
|
{Op: "add", Path: "/deleted_at", Value: time.Now()},
|
|
}
|
|
|
|
return c.sendPatch("shopping_items", itemID, patches)
|
|
}
|
|
|
|
// sendPatch sends JSON Patch operations using the PATCH method
|
|
func (c *ExampleClient) sendPatch(collection, itemID string, patches []PatchOperation) error {
|
|
jsonData, err := json.Marshal(patches)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to marshal patches: %w", err)
|
|
}
|
|
|
|
url := fmt.Sprintf("%s/api/collections/%s/items/%s", c.baseURL, collection, itemID)
|
|
req, err := http.NewRequest("PATCH", url, bytes.NewBuffer(jsonData))
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create request: %w", err)
|
|
}
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
resp, err := c.client.Do(req)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to send patch: %w", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
body, _ := io.ReadAll(resp.Body)
|
|
return fmt.Errorf("patch failed: %s", string(body))
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// sendEvent sends event using the legacy POST method
|
|
func (c *ExampleClient) sendEvent(event Event) error {
|
|
jsonData, err := json.Marshal(event)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to marshal event: %w", err)
|
|
}
|
|
|
|
resp, err := c.client.Post(c.baseURL+"/api/events", "application/json", bytes.NewBuffer(jsonData))
|
|
if err != nil {
|
|
return fmt.Errorf("failed to send event: %w", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != http.StatusCreated {
|
|
body, _ := io.ReadAll(resp.Body)
|
|
return fmt.Errorf("event creation failed: %s", string(body))
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (c *ExampleClient) SyncEvents(lastSeq int, lastHash string) (*SyncResponse, error) {
|
|
syncReq := SyncRequest{
|
|
LastSeq: lastSeq,
|
|
LastHash: lastHash,
|
|
}
|
|
|
|
jsonData, err := json.Marshal(syncReq)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to marshal sync request: %w", err)
|
|
}
|
|
|
|
resp, err := c.client.Post(c.baseURL+"/api/sync", "application/json", bytes.NewBuffer(jsonData))
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to sync: %w", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
body, _ := io.ReadAll(resp.Body)
|
|
return nil, fmt.Errorf("sync failed: %s", string(body))
|
|
}
|
|
|
|
var syncResp SyncResponse
|
|
if err := json.NewDecoder(resp.Body).Decode(&syncResp); err != nil {
|
|
return nil, fmt.Errorf("failed to decode sync response: %w", err)
|
|
}
|
|
|
|
return &syncResp, nil
|
|
}
|
|
|
|
func (c *ExampleClient) GetItems(collection string) ([]map[string]interface{}, error) {
|
|
resp, err := c.client.Get(c.baseURL + "/api/items/" + collection)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get items: %w", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
body, _ := io.ReadAll(resp.Body)
|
|
return nil, fmt.Errorf("get items failed: %s", string(body))
|
|
}
|
|
|
|
var result map[string]interface{}
|
|
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
|
|
return nil, fmt.Errorf("failed to decode response: %w", err)
|
|
}
|
|
|
|
items, ok := result["items"].([]interface{})
|
|
if !ok {
|
|
return nil, fmt.Errorf("invalid response format")
|
|
}
|
|
|
|
var typedItems []map[string]interface{}
|
|
for _, item := range items {
|
|
if typedItem, ok := item.(map[string]interface{}); ok {
|
|
typedItems = append(typedItems, typedItem)
|
|
}
|
|
}
|
|
|
|
return typedItems, nil
|
|
}
|
|
|
|
// Advanced JSON Patch operations examples
|
|
func (c *ExampleClient) AdvancedPatchExample(itemID string) error {
|
|
// Example: Create item with multiple fields
|
|
patches := []PatchOperation{
|
|
{Op: "add", Path: "/content", Value: "Advanced Shopping Item"},
|
|
{Op: "add", Path: "/quantity", Value: 5},
|
|
{Op: "add", Path: "/tags", Value: []string{"grocery", "urgent"}},
|
|
{Op: "add", Path: "/metadata", Value: map[string]interface{}{
|
|
"store": "SuperMart",
|
|
"category": "food",
|
|
}},
|
|
}
|
|
|
|
if err := c.sendPatch("shopping_items", itemID, patches); err != nil {
|
|
return fmt.Errorf("failed to create advanced item: %w", err)
|
|
}
|
|
|
|
// Example: Complex updates using different operations
|
|
updatePatches := []PatchOperation{
|
|
{Op: "replace", Path: "/quantity", Value: 3}, // Update quantity
|
|
{Op: "add", Path: "/tags/-", Value: "sale"}, // Append to array
|
|
{Op: "replace", Path: "/metadata/store", Value: "MegaMart"}, // Update nested field
|
|
{Op: "add", Path: "/metadata/priority", Value: "high"}, // Add new nested field
|
|
{Op: "remove", Path: "/tags/0"}, // Remove first tag
|
|
}
|
|
|
|
return c.sendPatch("shopping_items", itemID, updatePatches)
|
|
}
|
|
|
|
// Example usage function
|
|
func runClientExample() {
|
|
client := NewExampleClient("http://localhost:8090")
|
|
|
|
fmt.Println("=== RFC6902 JSON Patch Event Store Example ===")
|
|
|
|
// Create some shopping items using JSON Patch
|
|
fmt.Println("Creating shopping items with JSON Patch...")
|
|
if err := client.CreateItem("item1", "Milk"); err != nil {
|
|
fmt.Printf("Error creating item1: %v\n", err)
|
|
return
|
|
}
|
|
|
|
if err := client.CreateItem("item2", "Bread"); err != nil {
|
|
fmt.Printf("Error creating item2: %v\n", err)
|
|
return
|
|
}
|
|
|
|
// Update an item using JSON Patch replace
|
|
fmt.Println("Updating item1 with JSON Patch replace...")
|
|
if err := client.UpdateItem("item1", "Organic Milk"); err != nil {
|
|
fmt.Printf("Error updating item1: %v\n", err)
|
|
return
|
|
}
|
|
|
|
// Advanced JSON Patch operations
|
|
fmt.Println("Demonstrating advanced JSON Patch operations...")
|
|
if err := client.AdvancedPatchExample("item3"); err != nil {
|
|
fmt.Printf("Error with advanced patch example: %v\n", err)
|
|
return
|
|
}
|
|
|
|
// Get current items
|
|
fmt.Println("Fetching current items...")
|
|
items, err := client.GetItems("shopping_items")
|
|
if err != nil {
|
|
fmt.Printf("Error getting items: %v\n", err)
|
|
return
|
|
}
|
|
|
|
fmt.Printf("Current items: %+v\n", items)
|
|
|
|
// Sync events (simulate client sync)
|
|
fmt.Println("Syncing events...")
|
|
syncResp, err := client.SyncEvents(0, "")
|
|
if err != nil {
|
|
fmt.Printf("Error syncing: %v\n", err)
|
|
return
|
|
}
|
|
|
|
fmt.Printf("Sync response - Found %d events\n", len(syncResp.Events))
|
|
|
|
// Soft delete an item using JSON Patch
|
|
fmt.Println("Soft deleting item2 with JSON Patch...")
|
|
if err := client.DeleteItem("item2"); err != nil {
|
|
fmt.Printf("Error deleting item2: %v\n", err)
|
|
return
|
|
}
|
|
|
|
// Get items again to see the change
|
|
fmt.Println("Fetching items after deletion...")
|
|
items, err = client.GetItems("shopping_items")
|
|
if err != nil {
|
|
fmt.Printf("Error getting items: %v\n", err)
|
|
return
|
|
}
|
|
|
|
fmt.Printf("Items after deletion: %+v\n", items)
|
|
fmt.Println("=== JSON Patch Example completed ===")
|
|
}
|
|
|
|
// Uncomment the following to run the example:
|
|
// func init() {
|
|
// go func() {
|
|
// time.Sleep(2 * time.Second) // Wait for server to start
|
|
// runClientExample()
|
|
// }()
|
|
// }
|