Files
tm_back/pkg/notification
n.nakhostin 0857314906 Enhance Notification Request Structure and Service Interface
- Updated the NotificationRequest struct to include new fields: Title, Type, Priority, and ScheduledAt, improving the flexibility and usability of notification requests.
- Implemented validation for the new ScheduledAt field to ensure it is a future timestamp or zero for immediate delivery.
- Refactored the NotificationService interface and SDK methods to accept a new Model struct, streamlining the parameters for sending notifications.
- Enhanced the builder pattern for constructing notification requests, adding methods for setting Title, Type, Priority, and ScheduledAt, promoting a fluent API design.
- Updated service methods for sending notifications (Email, SMS, Push, OTP) to utilize the new Model struct, ensuring consistency across notification types.
2025-09-20 15:29:00 +03:30
..

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(".", &notification.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 := &notification.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 := &notification.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 notifications
  • notification.EventTypeSMS - SMS notifications
  • notification.EventTypePush - Push notifications
  • notification.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

  1. Configuration: Use environment variables for configuration in production
  2. Context: Always pass context for timeout and cancellation support
  3. Error Handling: Check specific error types for appropriate handling
  4. Logging: Implement structured logging for better observability
  5. Retries: Configure appropriate retry settings based on your use case
  6. Validation: Let the SDK handle validation, but provide clean input data
  7. Batch Operations: Use batch operations for sending multiple notifications efficiently

Examples

See the examples/ directory for complete working examples:

  • basic_usage.go - Basic notification sending
  • batch_operations.go - Batch notification processing
  • fluent_api.go - Using the builder pattern
  • error_handling.go - Comprehensive error handling
  • configuration.go - Different configuration methods

Contributing

  1. Fork the repository
  2. Create a feature branch
  3. Add tests for new functionality
  4. Ensure all tests pass
  5. Submit a pull request

License

This SDK is part of the Tender Management system and follows the same license terms.