Skip to content

Tap30/ripple-kotlin

Ripple Kotlin SDK

A high-performance event tracking SDK for Kotlin and Java applications.

Build License

Features

  • 🚀 High Performance: O(1) queue operations with automatic batching
  • 🔄 Smart Retry Logic: Exponential backoff with jitter, 4xx vs 5xx handling
  • 🔒 Thread-Safe: All operations are thread-safe with mutex-protected flushes
  • 💾 Offline Support: Events persisted and sent when connectivity returns
  • 🌐 Multi-Platform: Android, Spring Boot, and reactive support
  • 📘 Type-Safe: Full Kotlin type safety with Java interoperability
  • 🔌 Pluggable Adapters: Customize HTTP, storage, and logging behavior
  • ♻️ Re-initializable: Supports dispose/init cycles for lifecycle management
  • 🔢 Multi-Instance: Run multiple client instances with different configs

Download

Repository Setup

Kotlin DSL (build.gradle.kts)
repositories {
    mavenCentral()
    maven {
        name = "GitHubPackages"
        url = uri("https://maven.pkg.github.com/Tap30/ripple-kotlin")
        credentials {
            username = project.findProperty("gpr.user") as String? ?: System.getenv("USERNAME")
            password = project.findProperty("gpr.key") as String? ?: System.getenv("TOKEN")
        }
    }
}
Groovy DSL (build.gradle)
repositories {
    mavenCentral()
    maven {
        name = "GitHubPackages"
        url = "https://maven.pkg.github.com/Tap30/ripple-kotlin"
        credentials {
            username = project.findProperty("gpr.user") ?: System.getenv("USERNAME")
            password = project.findProperty("gpr.key") ?: System.getenv("TOKEN")
        }
    }
}

Core Modules

// Core functionality
implementation("io.github.tap30.ripple:core:1.0.0")

// Platform modules (lightweight, no adapters included)
implementation("io.github.tap30.ripple:android-core:1.0.0")
implementation("io.github.tap30.ripple:spring-core:1.0.0")
implementation("io.github.tap30.ripple:reactive-core:1.0.0")

Adapter Modules (Optional)

Choose only the adapters you need:

Android Adapters
// HTTP with OkHttp
implementation("io.github.tap30.ripple:android-adapters-okhttp:1.0.0")
// → OkHttpAdapter

// Storage with SharedPreferences  
implementation("io.github.tap30.ripple:android-adapters-storage-preferences:1.0.0")
// → SharedPreferencesAdapter

// Storage with Room Database
implementation("io.github.tap30.ripple:android-adapters-room:1.0.0") 
// → RoomStorageAdapter, RoomStorageAdapterFactory

// Android Logging
implementation("io.github.tap30.ripple:android-adapters-logging:1.0.0")
// → AndroidLogAdapter
Spring Adapters
// HTTP with WebClient (WebFlux)
implementation("io.github.tap30.ripple:spring-adapters-webflux:1.0.0")
// → WebClientAdapter

// File System Storage
implementation("io.github.tap30.ripple:spring-adapters-storage-file:1.0.0")
// → FileStorageAdapter

// SLF4J Logging
implementation("io.github.tap30.ripple:spring-adapters-logging:1.0.0")
// → Slf4jLoggerAdapter
Reactive Adapters
// Project Reactor Support
implementation("io.github.tap30.ripple:reactive-adapters-reactor:1.0.0")
// → ReactorAdapter (coming soon)

Quick Start

Android (Kotlin)

// Add dependencies
implementation("io.github.tap30.ripple:android-core:1.0.0")
implementation("io.github.tap30.ripple:android-adapters-okhttp:1.0.0")
implementation("io.github.tap30.ripple:android-adapters-room:1.0.0")

// Usage
val config = RippleConfig(
    apiKey = "your-api-key",
    endpoint = "https://api.example.com/events",
    adapters = AdapterConfig(
        httpAdapter = OkHttpAdapter(),
        storageAdapter = RoomStorageAdapterFactory.create(context),
        loggerAdapter = AndroidLogAdapter(LogLevel.INFO)
    )
)

val client = AndroidRippleClient(config)
client.init()

// Track events (untyped)
client.track("user_login")
client.track("purchase", mapOf("product_id" to "abc123", "amount" to 29.99))

// Set global metadata
client.setMetadata("user_id", "12345")

// Clean up
client.dispose()

Type-Safe Events (Recommended)

Define your events with compile-time validation:

// Define events as sealed class
sealed class AppEvent : RippleEvent {
    data class UserLogin(val email: String, val method: String) : AppEvent() {
        override val name = "user.login"
        override fun toPayload() = mapOf("email" to email, "method" to method)
    }
    
    data class Purchase(val orderId: String, val amount: Double) : AppEvent() {
        override val name = "purchase"
        override fun toPayload() = mapOf("orderId" to orderId, "amount" to amount)
    }
}

// Define metadata
data class AppMetadata(
    val userId: String? = null,
    val version: String? = null
) : RippleMetadata {
    override fun toMap() = buildMap {
        userId?.let { put("userId", it) }
        version?.let { put("version", it) }
    }
}

// Usage - compile-time type checking
client.track(AppEvent.UserLogin("user@example.com", "google"))
client.track(AppEvent.Purchase("ORD-123", 99.99))
client.setMetadata(AppMetadata(userId = "user-123", version = "1.0.0"))

// Event-specific metadata
client.track(AppEvent.Purchase("ORD-456", 50.0), AppMetadata(userId = "vip-user"))

Spring Boot (Kotlin)

@Configuration
class RippleConfiguration {
    
    @Bean
    fun rippleClient(): SpringRippleClient {
        val config = RippleConfig(
            apiKey = "your-api-key",
            endpoint = "https://api.example.com/events",
            flushInterval = 5000L,
            maxBatchSize = 20,
            adapters = AdapterConfig(
                httpAdapter = WebClientAdapter(),
                storageAdapter = FileStorageAdapter(),
                loggerAdapter = Slf4jLoggerAdapter()
            )
        )
        
        return SpringRippleClient(config).apply {
            init()
            setMetadata("service", "user-service")
            setMetadata("environment", "production")
        }
    }
}

@Service
class UserService(private val rippleClient: SpringRippleClient) {
    
    fun createUser(user: User) {
        // Business logic...
        
        rippleClient.track("user_created", mapOf(
            "user_id" to user.id,
            "plan" to user.plan
        ))
    }
}

Spring Boot (Java)

@Configuration
public class RippleConfiguration {
    
    @Bean
    public SpringRippleClient rippleClient() {
        RippleConfig config = new RippleConfig(
            "your-api-key",
            "https://api.example.com/events",
            "X-API-Key",
            5000L,
            10,
            3,
            new AdapterConfig(
                new WebClientAdapter(),
                new FileStorageAdapter(),
                new Slf4jLoggerAdapter(LogLevel.INFO)
            )
        );
        
        SpringRippleClient client = new SpringRippleClient(config);
        client.init();
        client.setMetadata("service", "java-service");
        return client;
    }
}

@Service
public class UserService {
    
    private final SpringRippleClient rippleClient;
    
    public UserService(SpringRippleClient rippleClient) {
        this.rippleClient = rippleClient;
    }
    
    public void createUser(User user) {
        // Business logic...
        
        Map<String, Object> payload = new HashMap<>();
        payload.put("user_id", user.getId());
        payload.put("plan", user.getPlan());
        
        rippleClient.track("user_created", payload, null);
    }
}

API Reference

RippleClient

Method Description
init() Initialize the client. Must be called before tracking. Can be called after dispose().
track(name, payload?, metadata?) Track an event with optional payload and metadata.
setMetadata(key, value) Set global metadata attached to all events.
getMetadata() Get all stored metadata as a shallow copy.
getSessionId() Get the current session ID.
removeMetadata(key) Remove a global metadata key.
clearMetadata() Clear all global metadata.
flush() Flush queued events asynchronously.
flushSync() Flush queued events and wait for completion.
getQueueSize() Get the number of queued events.
dispose() Clean up resources. Persists unsent events. Supports re-initialization.

RippleConfig

Parameter Type Default Description
apiKey String required API authentication key
endpoint String required API endpoint URL
apiKeyHeader String "X-API-Key" Header name for API key
flushInterval Long 5000 Auto-flush interval in milliseconds
maxBatchSize Int 10 Maximum events per batch
maxRetries Int 3 Maximum retry attempts
adapters AdapterConfig required Platform adapters

Thread Safety

All public methods are thread-safe and can be called from any thread:

  • track() is non-blocking and returns immediately
  • flush() is non-blocking and submits work to background thread
  • flushSync() blocks until completion (use for critical events)
  • Multiple concurrent calls are handled gracefully

Offline Support

Events are automatically persisted when:

  • Network requests fail after all retries
  • dispose() is called with events in queue

Persisted events are restored on next init() call.

Multi-Instance Support

The SDK supports running multiple client instances simultaneously with different configurations:

// Analytics client
val analyticsClient = AndroidRippleClient(context, RippleConfig(
    apiKey = "analytics-key",
    endpoint = "https://analytics.example.com/events",
    adapters = AdapterConfig(...)
))

// Monitoring client  
val monitoringClient = AndroidRippleClient(context, RippleConfig(
    apiKey = "monitoring-key",
    endpoint = "https://monitoring.example.com/events",
    adapters = AdapterConfig(...)
))

analyticsClient.init()
monitoringClient.init()

// Track to different endpoints
analyticsClient.track("user_action", mapOf("action" to "click"))
monitoringClient.track("performance", mapOf("latency" to 150))

Each instance maintains its own:

  • Event queue
  • Metadata storage
  • Session ID
  • Flush scheduler

Retry Behavior

The SDK handles HTTP errors intelligently:

Status Code Behavior
2xx Success - clear storage
4xx No retry - persist events
5xx Retry with exponential backoff
Network error Retry with exponential backoff

Backoff formula: delay = (1000ms × 2^attempt) + jitter(0-1000ms), max 30 seconds.

Documentation

License

MIT License - see LICENSE file for details.

About

Official Ripple SDKs for Kotlin.

Topics

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages