- Modified the route for marking notifications as seen to ensure a consistent and clear API structure. - Updated the notification client to reflect the new endpoint path for marking all notifications as seen, enhancing clarity for API consumers. - These changes improve the overall usability and maintainability of the notification API.
Notification SDK
A comprehensive Go SDK for interacting with the Tender Management notification service. This SDK provides a clean, type-safe interface for sending various types of notifications including emails, SMS, push notifications, and OTP messages.
Features
- Multiple Notification Types: Support for EMAIL, SMS, PUSH, and OTP notifications
- Automatic Retries: Configurable retry mechanism with exponential backoff
- Structured Logging: Built-in logging with structured fields
- Fluent API: Builder pattern for constructing complex notifications
- Batch Operations: Send multiple notifications efficiently
- Type Safety: Full type safety with comprehensive validation
- Error Handling: Detailed error types and proper error wrapping
- Configuration: Flexible configuration via environment variables or code
- Health Checks: Built-in health checking capabilities
Installation
go get your-module/pkg/notification
Quick Start
Basic Usage
package main
import (
"context"
"log"
"your-module/pkg/notification"
"your-module/pkg/logger" // Your logger implementation
)
func main() {
// Create configuration
config := notification.DefaultConfig()
config.BaseURL = "http://127.0.0.1:9095" // Your notification service URL
// Create logger (implement notification.Logger interface)
logger := logger.New() // Your logger implementation
// Create SDK
sdk, err := notification.NewSDK(config, logger)
if err != nil {
log.Fatal("Failed to create notification SDK:", err)
}
ctx := context.Background()
// Send a simple email
err = sdk.QuickEmail(ctx, "user@example.com", "Hello World!")
if err != nil {
log.Printf("Failed to send email: %v", err)
}
}
Environment Configuration
Set environment variables for automatic configuration:
export NOTIFICATION_BASE_URL="http://127.0.0.1:9095"
export NOTIFICATION_TIMEOUT="30s"
export NOTIFICATION_RETRY_ATTEMPTS="3"
export NOTIFICATION_RETRY_DELAY="2s"
export NOTIFICATION_ENABLE_LOGGING="true"
export NOTIFICATION_USER_AGENT="MyApp-NotificationSDK/1.0"
Then load configuration:
import "your-module/pkg/config"
// Load configuration from environment
cfg, err := config.LoadConfig(".", ¬ification.Config{})
if err != nil {
log.Fatal("Failed to load config:", err)
}
sdk, err := notification.NewSDK(cfg, logger)
API Reference
Configuration
type Config struct {
BaseURL string `env:"NOTIFICATION_BASE_URL"`
Timeout time.Duration `env:"NOTIFICATION_TIMEOUT"`
RetryAttempts int `env:"NOTIFICATION_RETRY_ATTEMPTS"`
RetryDelay time.Duration `env:"NOTIFICATION_RETRY_DELAY"`
EnableLogging bool `env:"NOTIFICATION_ENABLE_LOGGING"`
UserAgent string `env:"NOTIFICATION_USER_AGENT"`
}
Creating SDK Instance
// With default configuration
sdk, err := notification.NewSDK(nil, logger)
// With custom configuration
config := ¬ification.Config{
BaseURL: "http://localhost:9095",
Timeout: 30 * time.Second,
RetryAttempts: 3,
RetryDelay: 2 * time.Second,
EnableLogging: true,
UserAgent: "MyApp/1.0",
}
sdk, err := notification.NewSDK(config, logger)
// With custom HTTP client
httpClient := &http.Client{Timeout: 10 * time.Second}
sdk, err := notification.NewSDKWithClient(config, httpClient, logger)
Sending Notifications
Simple Notifications
ctx := context.Background()
// Email
response, err := sdk.SendEmail(ctx, "user@example.com", "Welcome!", "user123")
// SMS
response, err := sdk.SendSMS(ctx, "+1234567890", "Your code: 1234", "user123")
// Push Notification
response, err := sdk.SendPush(ctx, "device_token_here", "New message!", "user123")
// OTP
response, err := sdk.SendOTP(ctx, "+1234567890", "Your OTP: 5678", "user123")
Quick Notifications (without user ID)
// Quick methods for simple use cases
err := sdk.QuickEmail(ctx, "user@example.com", "Hello!")
err := sdk.QuickSMS(ctx, "+1234567890", "Hello!")
err := sdk.QuickPush(ctx, "device_token", "Hello!")
err := sdk.QuickOTP(ctx, "+1234567890", "Code: 1234")
Custom Notifications
request := ¬ification.NotificationRequest{
EventType: notification.EventTypeEmail,
Message: "Custom notification message",
Methods: notification.NotificationMethods{
Email: "user@example.com",
SMS: "+1234567890", // Optional: multiple delivery methods
},
UserID: "user123",
}
response, err := sdk.SendNotification(ctx, request)
Fluent Builder API
// Build and send notification using fluent API
response, err := sdk.NewBuilder().
SetEventType(notification.EventTypeEmail).
SetMessage("Welcome to our service!").
SetEmail("user@example.com").
SetUserID("user123").
Send(ctx)
// Build without sending
request, err := sdk.NewBuilder().
SetEventType(notification.EventTypeSMS).
SetMessage("Your verification code: 1234").
SetSMS("+1234567890").
SetUserID("user123").
Build()
if err == nil {
response, err := sdk.SendNotification(ctx, request)
}
Batch Operations
// Prepare multiple requests
requests := []*notification.NotificationRequest{
{
EventType: notification.EventTypeEmail,
Message: "Welcome email",
Methods: notification.NotificationMethods{Email: "user1@example.com"},
UserID: "user1",
},
{
EventType: notification.EventTypeSMS,
Message: "Welcome SMS",
Methods: notification.NotificationMethods{SMS: "+1234567890"},
UserID: "user2",
},
}
// Send batch
batchResponse, err := sdk.SendBatch(ctx, requests)
if err != nil {
log.Printf("Batch failed: %v", err)
} else {
log.Printf("Batch completed: %d success, %d failed",
batchResponse.Success, batchResponse.Failed)
// Check individual results
for _, resp := range batchResponse.Responses {
if !resp.Success {
log.Printf("Request %d failed: %s", resp.Index, resp.Error)
}
}
}
Health Checks
// Check if notification service is available
err := sdk.Health(ctx)
if err != nil {
log.Printf("Notification service is unhealthy: %v", err)
} else {
log.Println("Notification service is healthy")
}
Event Types
The SDK supports the following notification types:
notification.EventTypeEmail- Email notificationsnotification.EventTypeSMS- SMS notificationsnotification.EventTypePush- Push notificationsnotification.EventTypeOTP- OTP notifications
Error Handling
The SDK provides detailed error types for different scenarios:
response, err := sdk.SendEmail(ctx, "invalid-email", "Hello!", "user123")
if err != nil {
switch e := err.(type) {
case notification.ErrHTTP:
log.Printf("HTTP error %d: %s", e.StatusCode, e.Message)
case notification.ErrValidation:
log.Printf("Validation error in field %s: %s", e.Field, e.Message)
case notification.ErrRetryExhausted:
log.Printf("Retry exhausted after %d attempts: %v", e.Attempts, e.LastError)
case notification.ErrInvalidConfig:
log.Printf("Configuration error in field %s: %s", e.Field, e.Message)
default:
log.Printf("Unknown error: %v", err)
}
}
Logging
The SDK requires a logger that implements the notification.Logger interface:
type Logger interface {
Info(msg string, fields map[string]interface{})
Error(msg string, fields map[string]interface{})
Debug(msg string, fields map[string]interface{})
Warn(msg string, fields map[string]interface{})
}
Example logger implementation with logrus:
import "github.com/sirupsen/logrus"
type LogrusLogger struct {
logger *logrus.Logger
}
func (l *LogrusLogger) Info(msg string, fields map[string]interface{}) {
l.logger.WithFields(fields).Info(msg)
}
func (l *LogrusLogger) Error(msg string, fields map[string]interface{}) {
l.logger.WithFields(fields).Error(msg)
}
func (l *LogrusLogger) Debug(msg string, fields map[string]interface{}) {
l.logger.WithFields(fields).Debug(msg)
}
func (l *LogrusLogger) Warn(msg string, fields map[string]interface{}) {
l.logger.WithFields(fields).Warn(msg)
}
Testing
The SDK is designed to be easily testable. You can mock the HTTP client:
import (
"testing"
"net/http"
"your-module/pkg/notification"
)
// Mock HTTP client
type MockHTTPClient struct {
DoFunc func(req *http.Request) (*http.Response, error)
}
func (m *MockHTTPClient) Do(req *http.Request) (*http.Response, error) {
return m.DoFunc(req)
}
// Mock logger
type MockLogger struct{}
func (l *MockLogger) Info(msg string, fields map[string]interface{}) {}
func (l *MockLogger) Error(msg string, fields map[string]interface{}) {}
func (l *MockLogger) Debug(msg string, fields map[string]interface{}) {}
func (l *MockLogger) Warn(msg string, fields map[string]interface{}) {}
func TestSendEmail(t *testing.T) {
mockClient := &MockHTTPClient{
DoFunc: func(req *http.Request) (*http.Response, error) {
// Return mock response
return &http.Response{
StatusCode: 200,
Body: io.NopCloser(strings.NewReader(`{"status":"Notification sent"}`)),
}, nil
},
}
config := notification.DefaultConfig()
logger := &MockLogger{}
sdk, err := notification.NewSDKWithClient(config, mockClient, logger)
if err != nil {
t.Fatal(err)
}
response, err := sdk.SendEmail(context.Background(), "test@example.com", "Test", "user123")
if err != nil {
t.Fatal(err)
}
if response.Status != "Notification sent" {
t.Errorf("Expected 'Notification sent', got %s", response.Status)
}
}
Best Practices
- Configuration: Use environment variables for configuration in production
- Context: Always pass context for timeout and cancellation support
- Error Handling: Check specific error types for appropriate handling
- Logging: Implement structured logging for better observability
- Retries: Configure appropriate retry settings based on your use case
- Validation: Let the SDK handle validation, but provide clean input data
- Batch Operations: Use batch operations for sending multiple notifications efficiently
Examples
See the examples/ directory for complete working examples:
basic_usage.go- Basic notification sendingbatch_operations.go- Batch notification processingfluent_api.go- Using the builder patternerror_handling.go- Comprehensive error handlingconfiguration.go- Different configuration methods
Contributing
- Fork the repository
- Create a feature branch
- Add tests for new functionality
- Ensure all tests pass
- Submit a pull request
License
This SDK is part of the Tender Management system and follows the same license terms.