A Go client for the Orisun event store, providing a simple and intuitive interface for interacting with the Orisun gRPC service.
- Builder Pattern: Easy configuration with a fluent builder API
- Authentication: Support for basic authentication and token caching
- Logging: Configurable logging with multiple log levels
- Retry Logic: Built-in retry mechanisms for resilient operations
- Event Subscriptions: Support for streaming event subscriptions
- Connection Management: Automatic connection lifecycle management
- Load Balancing: Support for DNS and static load balancing
- TLS Support: Configurable TLS encryption
- Keep-alive: Configurable keep-alive settings
go get github.com/oexza/orisun-client-goIf you prefer to build from source or need the latest development version:
# Clone the repository
git clone https://github.com/oexza/orisun-client-go.git
cd orisun-client-go
# Build the client
go build ./...
# Or install it locally
go install ./...The client uses proto definitions from a submodule. To regenerate:
# Initialize submodules
git submodule update --init --recursive
# Regenerate proto files
protoc --go_out=. --go_opt=paths=source_relative \
--go-grpc_out=. --go-grpc_opt=paths=source_relative \
protos/*.proto
# Move generated files to eventstore directory
mv protos/*.go eventstore/package main
import (
"context"
"log"
"time"
"github.com/oexza/orisun-client-go/orisun"
)
func main() {
// Create a client with default configuration
client, err := orisun.NewClientBuilder().
WithHost("localhost").
WithPort(5005).
WithTimeout(30).
Build()
if err != nil {
log.Fatalf("Failed to create client: %v", err)
}
defer client.Close()
// Ping the server
ctx, cancel := orisun.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
if err := client.Ping(ctx); err != nil {
log.Printf("Ping failed: %v", err)
} else {
log.Println("Ping successful!")
}
}The client uses a builder pattern for configuration:
client, err := orisun.NewClientBuilder().
WithServer("localhost", 5005).
WithTimeout(30).
WithTLS(true).
WithBasicAuth("username", "password").
WithLogging(true).
WithLogLevel(orisun.DEBUG).
WithLoadBalancingPolicy("round_robin").
WithKeepAliveTime(30*time.Second).
WithKeepAliveTimeout(10*time.Second).
WithKeepAlivePermitWithoutCalls(true).
Build()- WithHost(host): Add a server with default port 50051
- WithPort(port): Set the port for the last added server
- WithServer(host, port): Add a server with specific host and port
- WithServers(servers): Add multiple servers
- WithTimeout(seconds): Set default timeout in seconds
- WithTLS(useTLS): Enable/disable TLS encryption
- WithBasicAuth(username, password): Set basic authentication credentials
- WithLogger(logger): Set a custom logger
- WithLogging(enableLogging): Enable/disable default logging
- WithLogLevel(level): Set log level (DEBUG, INFO, WARN, ERROR)
- WithLoadBalancingPolicy(policy): Set load balancing policy
- WithDnsTarget(target): Set DNS target for DNS-based load balancing
- WithStaticTarget(target): Set static target for static-based load balancing
- WithDnsResolver(useDns): Enable/disable DNS resolver
- WithKeepAliveTime(duration): Set keep-alive time
- WithKeepAliveTimeout(duration): Set keep-alive timeout
- WithKeepAlivePermitWithoutCalls(permit): Permit keep-alive without calls
- WithChannel(channel): Use a custom gRPC channel
events := []*eventstore.EventToSave{
{
EventId: "event-123",
EventType: "UserAction",
Data: `{"action": "login", "userId": "12345"}`,
Metadata: `{"source": "web-app"}`,
},
}
request := &eventstore.SaveEventsRequest{
Boundary: "my-boundary",
Stream: &eventstore.SaveStreamQuery{
Name: "user-stream",
ExpectedPosition: &eventstore.Position{
CommitPosition: 100,
PreparePosition: 100,
},
},
Events: events,
}
result, err := client.SaveEvents(ctx, request)request := &eventstore.GetEventsRequest{
Boundary: "my-boundary",
Stream: &eventstore.GetStreamQuery{
Name: "user-stream",
FromPosition: &eventstore.Position{
CommitPosition: 100,
PreparePosition: 100,
},
},
Count: 100,
Direction: eventstore.Direction_ASC,
}
response, err := client.GetEvents(ctx, request)handler := orisun.NewSimpleEventHandler().
WithOnEvent(func(event *eventstore.Event) error {
fmt.Printf("Received event: %s - %s\n", event.EventId, event.EventType)
return nil
}).
WithOnError(func(err error) {
fmt.Printf("Subscription error: %v\n", err)
}).
WithOnCompleted(func() {
fmt.Println("Subscription completed")
})
request := &eventstore.CatchUpSubscribeToEventStoreRequest{
Boundary: "my-boundary",
SubscriberName: "my-subscriber",
}
subscription, err := client.SubscribeToEvents(ctx, request, handler)
defer subscription.Close()healthy, err := client.HealthCheck(ctx, "my-boundary")The client provides structured error handling with context:
if err != nil {
if orisunErr, ok := err.(*orisun.OrisunException); ok {
fmt.Printf("Orisun error: %s\n", orisunErr.GetMessage())
if operation, exists := orisunErr.GetContext("operation"); exists {
fmt.Printf("Operation: %s\n", operation)
}
} else if concurrencyErr, ok := err.(*orisun.OptimisticConcurrencyException); ok {
fmt.Printf("Version conflict: expected=%d, actual=%d\n",
concurrencyErr.GetExpectedVersion(),
concurrencyErr.GetActualVersion())
}
}The client includes configurable logging:
// Enable logging with DEBUG level
client, err := orisun.NewClientBuilder().
WithLogging(true).
WithLogLevel(orisun.DEBUG).
Build()
// Custom logger
type CustomLogger struct{}
func (l *CustomLogger) Debug(msg string, args ...interface{}) {
fmt.Printf("[DEBUG] %s\n", msg)
}
func (l *CustomLogger) Info(msg string, args ...interface{}) {
fmt.Printf("[INFO] %s\n", msg)
}
func (l *CustomLogger) Error(msg string, args ...interface{}) {
fmt.Printf("[ERROR] %s\n", msg)
}
// Use custom logger
client, err := orisun.NewClientBuilder().
WithLogger(&CustomLogger{}).
Build()Run the tests:
cd clients/go
go test -vTo build the client:
cd clients/go
go build ./...NewClientBuilder(): Create a new client builderBuild(): Build the client instance
WithHost(host): Add a server with default portWithPort(port): Set the port for the last added serverWithServer(host, port): Add a server with specific host and portWithServers(servers): Add multiple serversWithTimeout(seconds): Set default timeout in secondsWithTLS(useTLS): Enable/disable TLS encryptionWithBasicAuth(username, password): Set basic authentication credentialsWithLogger(logger): Set a custom loggerWithLogging(enableLogging): Enable/disable default loggingWithLogLevel(level): Set log levelWithLoadBalancingPolicy(policy): Set load balancing policyWithDnsTarget(target): Set DNS target for DNS-based load balancingWithStaticTarget(target): Set static target for static-based load balancingWithDnsResolver(useDns): Enable/disable DNS resolverWithKeepAliveTime(duration): Set keep-alive timeWithKeepAliveTimeout(duration): Set keep-alive timeoutWithKeepAlivePermitWithoutCalls(permit): Permit keep-alive without callsWithChannel(channel): Use a custom gRPC channel
SaveEvents(ctx, request): Save events to a streamGetEvents(ctx, request): Get events from the event storeSubscribeToEvents(ctx, request, handler): Subscribe to eventsSubscribeToStream(ctx, request, handler): Subscribe to a specific streamPing(ctx): Ping the serverHealthCheck(ctx, boundary): Perform a health checkClose(): Close the client connectionGetLogger(): Get the client's loggerGetTokenCache(): Get the client's token cacheGetDefaultTimeout(): Get the default timeoutGetConnection(): Get the underlying gRPC connectionIsClosed(): Check if the client is closed
NewServerAddress(host, port): Create a server addressWithTimeout(parent, timeout): Create a context with timeoutWithDeadline(parent, deadline): Create a context with deadlineWithCancel(parent): Create a context with cancel functionNewTokenCache(logger): Create a token cacheNewDefaultLogger(level): Create a default loggerNewNoOpLogger(): Create a no-op loggerNewRetryHelper(config): Create a retry helperDefaultRetryConfig(): Get default retry configurationNewContextHelper(): Create a context helperNewStringHelper(): Create a string helper
ServerAddress: Represents a server address with host and portOrisunException: Base exception with contextOptimisticConcurrencyException: Version conflict exceptionLogLevel: Log level enumeration (DEBUG, INFO, WARN, ERROR)Logger: Logger interfaceEventHandler: Event handler interfaceSimpleEventHandler: Simple event handler implementationTokenCache: Token cache for authenticationRetryConfig: Retry configurationRetryHelper: Retry helper utility
This project is licensed under the MIT License.