From 7bfdc5e4249185e677689daca74a35b9e0995c49 Mon Sep 17 00:00:00 2001 From: "a.tabolin" Date: Wed, 17 Sep 2025 11:06:04 +0300 Subject: [PATCH] #5 - Added comprehensive KDoc documentation for all public APIs --- .../redmadrobot/konfeature/FeatureConfig.kt | 53 +++++++++++++++ .../konfeature/FeatureConfigSpec.kt | 29 ++++++++ .../redmadrobot/konfeature/FeatureValue.kt | 11 ++++ .../konfeature/FeatureValueSpec.kt | 13 ++++ .../com/redmadrobot/konfeature/Konfeature.kt | 30 +++++++++ .../com/redmadrobot/konfeature/Logger.kt | 31 ++++++++- .../konfeature/builder/KonfeatureBuilder.kt | 66 +++++++++++++++++++ .../exception/KonfeatureException.kt | 25 +++++++ .../konfeature/source/FeatureSource.kt | 29 ++++++++ .../konfeature/source/FeatureValueSource.kt | 23 +++++++ .../konfeature/source/Interceptor.kt | 31 +++++++++ .../source/SourceSelectionStrategy.kt | 37 +++++++++++ 12 files changed, 377 insertions(+), 1 deletion(-) diff --git a/konfeature/src/commonMain/kotlin/com/redmadrobot/konfeature/FeatureConfig.kt b/konfeature/src/commonMain/kotlin/com/redmadrobot/konfeature/FeatureConfig.kt index 197a83e..5966cf3 100644 --- a/konfeature/src/commonMain/kotlin/com/redmadrobot/konfeature/FeatureConfig.kt +++ b/konfeature/src/commonMain/kotlin/com/redmadrobot/konfeature/FeatureConfig.kt @@ -4,6 +4,37 @@ import com.redmadrobot.konfeature.source.SourceSelectionStrategy import kotlin.properties.ReadOnlyProperty import kotlin.reflect.KProperty +/** + * Abstract base class for defining feature configurations. + * + * FeatureConfig provides a declarative way to define configuration schemas using + * property delegates. Each configuration element is defined using either `by toggle()` + * for Boolean values or `by value()` for other types. + * + * Example usage: + * ```kotlin + * class MyFeatureConfig : FeatureConfig( + * name = "my_feature", + * description = "Configuration for my feature" + * ) { + * val isEnabled: Boolean by toggle( + * key = "my_feature_enabled", + * description = "Enable/disable my feature", + * defaultValue = false, + * sourceSelectionStrategy = SourceSelectionStrategy.Any + * ) + * + * val timeout: Long by value( + * key = "my_feature_timeout", + * description = "Timeout in milliseconds", + * defaultValue = 5000L + * ) + * } + * ``` + * + * @param name unique identifier for this configuration + * @param description human-readable description of this configuration's purpose + */ public abstract class FeatureConfig( override val name: String, override val description: String @@ -34,6 +65,16 @@ public abstract class FeatureConfig( error("Use toggle instead of boolean value") } + /** + * Creates a configuration value delegate for non-Boolean types. + * + * @param T the type of the configuration value + * @param key unique identifier for this value used in sources + * @param description human-readable description of this value's purpose + * @param defaultValue fallback value when no sources provide a value + * @param sourceSelectionStrategy strategy for selecting which sources can provide values + * @return a property delegate that resolves the configuration value + */ public fun value( key: String, description: String, @@ -48,6 +89,18 @@ public abstract class FeatureConfig( ) } + /** + * Creates a configuration toggle delegate for Boolean values. + * + * This method should be used instead of `value()` for Boolean configuration + * elements to provide better semantic clarity for feature flags and toggles. + * + * @param key unique identifier for this toggle used in sources + * @param description human-readable description of this toggle's purpose + * @param defaultValue fallback value when no sources provide a value + * @param sourceSelectionStrategy strategy for selecting which sources can provide values + * @return a property delegate that resolves the Boolean configuration value + */ public fun toggle( key: String, description: String, diff --git a/konfeature/src/commonMain/kotlin/com/redmadrobot/konfeature/FeatureConfigSpec.kt b/konfeature/src/commonMain/kotlin/com/redmadrobot/konfeature/FeatureConfigSpec.kt index f03557c..80bddb3 100644 --- a/konfeature/src/commonMain/kotlin/com/redmadrobot/konfeature/FeatureConfigSpec.kt +++ b/konfeature/src/commonMain/kotlin/com/redmadrobot/konfeature/FeatureConfigSpec.kt @@ -1,7 +1,36 @@ package com.redmadrobot.konfeature +/** + * Interface representing the specification of a feature configuration. + * + * This interface provides metadata about a feature configuration including + * its name, description, and the list of configuration values it contains. + * It's implemented by [FeatureConfig] and used for introspection and + * building debug interfaces. + * + * @see FeatureConfig for the concrete implementation + */ public interface FeatureConfigSpec { + /** + * Unique name identifying this feature configuration. + * + * The name should be descriptive and unique among all registered configurations. + */ public val name: String + + /** + * Human-readable description of what this feature configuration controls. + * + * This description helps developers understand the purpose and scope of + * the configuration when viewing it in debug panels or documentation. + */ public val description: String + + /** + * List of all configuration value specifications contained in this configuration. + * + * This provides access to metadata about each individual configuration element, + * including their keys, descriptions, default values, and source selection strategies. + */ public val values: List> } diff --git a/konfeature/src/commonMain/kotlin/com/redmadrobot/konfeature/FeatureValue.kt b/konfeature/src/commonMain/kotlin/com/redmadrobot/konfeature/FeatureValue.kt index e94517b..f4d82c9 100644 --- a/konfeature/src/commonMain/kotlin/com/redmadrobot/konfeature/FeatureValue.kt +++ b/konfeature/src/commonMain/kotlin/com/redmadrobot/konfeature/FeatureValue.kt @@ -3,6 +3,17 @@ package com.redmadrobot.konfeature import com.redmadrobot.konfeature.source.FeatureValueSource import dev.drewhamilton.poko.Poko +/** + * Represents a resolved configuration value along with information about its source. + * + * This class encapsulates both the actual configuration value and metadata about + * where the value came from (default, source, or interceptor). This information + * is useful for debugging, logging, and understanding the configuration resolution flow. + * + * @param T the type of the configuration value + * @property source information about where this value originated from + * @property value the resolved configuration value + */ @Poko public class FeatureValue( public val source: FeatureValueSource, diff --git a/konfeature/src/commonMain/kotlin/com/redmadrobot/konfeature/FeatureValueSpec.kt b/konfeature/src/commonMain/kotlin/com/redmadrobot/konfeature/FeatureValueSpec.kt index 08aaea5..21cb66a 100644 --- a/konfeature/src/commonMain/kotlin/com/redmadrobot/konfeature/FeatureValueSpec.kt +++ b/konfeature/src/commonMain/kotlin/com/redmadrobot/konfeature/FeatureValueSpec.kt @@ -3,6 +3,19 @@ package com.redmadrobot.konfeature import com.redmadrobot.konfeature.source.SourceSelectionStrategy import dev.drewhamilton.poko.Poko +/** + * Specification for a feature configuration value element. + * + * This class defines the metadata and behavior for a single configuration element + * within a [FeatureConfig]. It includes the key used to look up values in sources, + * documentation, fallback behavior, and source selection rules. + * + * @param T the type of the configuration value + * @property key the unique identifier used to retrieve this value from sources + * @property description human-readable description of what this configuration element controls + * @property defaultValue the fallback value used when no sources provide a value + * @property sourceSelectionStrategy strategy for determining which sources can provide values + */ @Poko public class FeatureValueSpec( public val key: String, diff --git a/konfeature/src/commonMain/kotlin/com/redmadrobot/konfeature/Konfeature.kt b/konfeature/src/commonMain/kotlin/com/redmadrobot/konfeature/Konfeature.kt index b8b0deb..bfbbe03 100644 --- a/konfeature/src/commonMain/kotlin/com/redmadrobot/konfeature/Konfeature.kt +++ b/konfeature/src/commonMain/kotlin/com/redmadrobot/konfeature/Konfeature.kt @@ -1,8 +1,38 @@ package com.redmadrobot.konfeature +/** + * Main entry point for the Konfeature library that manages feature configuration. + * + * Konfeature provides a unified way to work with remote feature configurations by combining + * multiple data sources, interceptors, and providing runtime access to configuration values. + * + * @see FeatureConfig for defining configuration schemas + * @see com.redmadrobot.konfeature.source.FeatureSource for implementing configuration data sources + * @see com.redmadrobot.konfeature.source.Interceptor for implementing value overrides + */ public interface Konfeature { + /** + * List of all registered feature configuration specifications. + * + * This property provides access to metadata about all registered [FeatureConfig] instances, + * including their names, descriptions, and value specifications. Useful for building + * debug panels or configuration management UIs. + */ public val spec: List + /** + * Retrieves the current value for a given feature configuration specification. + * + * The value resolution follows this order: + * 1. Default value is assigned + * 2. Sources are filtered using the spec's [com.redmadrobot.konfeature.source.SourceSelectionStrategy] + * 3. Sources are searched in registration order (first match wins) + * 4. Interceptors are applied in registration order (last non-null wins) + * + * @param T the type of the configuration value + * @param spec the feature value specification defining the configuration element + * @return [FeatureValue] containing the resolved value and its source + */ public fun getValue(spec: FeatureValueSpec): FeatureValue } diff --git a/konfeature/src/commonMain/kotlin/com/redmadrobot/konfeature/Logger.kt b/konfeature/src/commonMain/kotlin/com/redmadrobot/konfeature/Logger.kt index 6337eea..1783d76 100644 --- a/konfeature/src/commonMain/kotlin/com/redmadrobot/konfeature/Logger.kt +++ b/konfeature/src/commonMain/kotlin/com/redmadrobot/konfeature/Logger.kt @@ -1,11 +1,40 @@ package com.redmadrobot.konfeature +/** + * Interface for logging Konfeature events and errors. + * + * The logger is used to track configuration value access, type mismatches, + * and other diagnostic information. Implementations can integrate with + * existing logging frameworks like Timber, SLF4J, or custom solutions. + * + * Events that are logged include: + * - Configuration value access with source information + * - Type mismatch warnings when sources return unexpected types + * - Configuration validation warnings + */ public interface Logger { + /** + * Logs a message with the specified severity level. + * + * @param severity the severity level of the log message + * @param message the message to log + */ public fun log(severity: Severity, message: String) + /** + * Severity levels for log messages. + */ public enum class Severity { - WARNING, INFO + /** + * Warning level for non-critical issues like type mismatches or empty configurations. + */ + WARNING, + + /** + * Information level for normal operations like value access and source information. + */ + INFO } } diff --git a/konfeature/src/commonMain/kotlin/com/redmadrobot/konfeature/builder/KonfeatureBuilder.kt b/konfeature/src/commonMain/kotlin/com/redmadrobot/konfeature/builder/KonfeatureBuilder.kt index f2d6804..40250d7 100644 --- a/konfeature/src/commonMain/kotlin/com/redmadrobot/konfeature/builder/KonfeatureBuilder.kt +++ b/konfeature/src/commonMain/kotlin/com/redmadrobot/konfeature/builder/KonfeatureBuilder.kt @@ -8,17 +8,49 @@ import com.redmadrobot.konfeature.exception.SourceNameAlreadyExistException import com.redmadrobot.konfeature.source.FeatureSource import com.redmadrobot.konfeature.source.Interceptor +/** + * Builder class for configuring and creating a [Konfeature] instance. + * + * KonfeatureBuilder uses the builder pattern to configure all aspects of a Konfeature + * instance including sources, interceptors, feature configurations, and logging. + * It validates the configuration during the build process to ensure consistency. + * + * Example usage: + * ```kotlin + * val konfeature = konfeature { + * addSource(FirebaseFeatureSource(remoteConfig)) + * addInterceptor(DebugPanelInterceptor()) + * register(MyFeatureConfig()) + * setLogger(TimberLogger()) + * } + * ``` + * + * @see konfeature for the DSL function that creates and configures a builder + */ public class KonfeatureBuilder { private val sources = mutableListOf() private var interceptors = mutableListOf() private var spec = mutableListOf() private var logger: Logger? = null + /** + * Adds an interceptor to the Konfeature configuration. + * + * @param interceptor the interceptor to add + * @return this builder instance for method chaining + */ public fun addInterceptor(interceptor: Interceptor): KonfeatureBuilder { interceptors.add(interceptor) return this } + /** + * Adds a feature source to the Konfeature configuration. + * + * @param source the source to add + * @return this builder instance for method chaining + * @throws SourceNameAlreadyExistException if a source with the same name already exists + */ public fun addSource(source: FeatureSource): KonfeatureBuilder { if (sources.any { it.name == source.name }) { throw SourceNameAlreadyExistException(source.name) @@ -28,6 +60,13 @@ public class KonfeatureBuilder { return this } + /** + * Registers a feature configuration with the Konfeature instance. + * + * @param featureConfig the configuration to register + * @return this builder instance for method chaining + * @throws ConfigNameAlreadyExistException if a config with the same name already exists + */ public fun register(featureConfig: FeatureConfig): KonfeatureBuilder { if (spec.any { it.name == featureConfig.name }) { throw ConfigNameAlreadyExistException(featureConfig.name) @@ -36,11 +75,28 @@ public class KonfeatureBuilder { return this } + /** + * Sets the logger for the Konfeature instance. + * + * @param logger the logger to use for Konfeature events + * @return this builder instance for method chaining + */ public fun setLogger(logger: Logger): KonfeatureBuilder { this.logger = logger return this } + /** + * Builds and returns a configured Konfeature instance. + * + * This method validates the configuration and throws exceptions if: + * - No feature configurations are registered + * - Feature configurations have duplicate keys + * + * @return a fully configured Konfeature instance + * @throws NoFeatureConfigException if no configurations are registered + * @throws KeyDuplicationException if configurations have duplicate keys + */ public fun build(): Konfeature { if (spec.isEmpty()) throw NoFeatureConfigException() @@ -81,6 +137,16 @@ public class KonfeatureBuilder { } } +/** + * DSL function for creating and configuring a Konfeature instance. + * + * This function provides a convenient way to configure a Konfeature instance + * using a builder DSL. It creates a KonfeatureBuilder, applies the configuration + * block, and returns the built Konfeature instance. + * + * @param build configuration block applied to the KonfeatureBuilder + * @return a configured Konfeature instance + */ public fun konfeature(build: KonfeatureBuilder.() -> Unit): Konfeature { return KonfeatureBuilder().apply(build).build() } diff --git a/konfeature/src/commonMain/kotlin/com/redmadrobot/konfeature/exception/KonfeatureException.kt b/konfeature/src/commonMain/kotlin/com/redmadrobot/konfeature/exception/KonfeatureException.kt index 90becf0..f616d40 100644 --- a/konfeature/src/commonMain/kotlin/com/redmadrobot/konfeature/exception/KonfeatureException.kt +++ b/konfeature/src/commonMain/kotlin/com/redmadrobot/konfeature/exception/KonfeatureException.kt @@ -1,11 +1,28 @@ package com.redmadrobot.konfeature.exception +/** + * Base sealed class for all Konfeature-specific exceptions. + * + * All exceptions thrown by the Konfeature library inherit from this class, + * allowing for comprehensive error handling when working with the library. + */ public sealed class KonfeatureException(messageProvider: () -> String) : Exception(messageProvider.invoke()) +/** + * Exception thrown when attempting to register a feature configuration with a name that already exists. + * + * @param name the duplicate configuration name + */ public class ConfigNameAlreadyExistException( name: String ) : KonfeatureException({ "feature config with name '$name' already registered" }) +/** + * Exception thrown when a feature configuration contains duplicate keys. + * + * @param values the list of duplicate keys + * @param config the name of the configuration containing duplicates + */ public class KeyDuplicationException( values: List, config: String @@ -14,8 +31,16 @@ public class KeyDuplicationException( "values with keys <$duplicatedValues> are duplicated in config '$config'" }) +/** + * Exception thrown when attempting to build a Konfeature instance without any registered configurations. + */ public class NoFeatureConfigException : KonfeatureException({ "No feature config added" }) +/** + * Exception thrown when attempting to register a source with a name that already exists. + * + * @param name the duplicate source name + */ public class SourceNameAlreadyExistException( name: String ) : KonfeatureException({ "source with name '$name' already registered" }) diff --git a/konfeature/src/commonMain/kotlin/com/redmadrobot/konfeature/source/FeatureSource.kt b/konfeature/src/commonMain/kotlin/com/redmadrobot/konfeature/source/FeatureSource.kt index 2d7ed51..46cc285 100644 --- a/konfeature/src/commonMain/kotlin/com/redmadrobot/konfeature/source/FeatureSource.kt +++ b/konfeature/src/commonMain/kotlin/com/redmadrobot/konfeature/source/FeatureSource.kt @@ -1,8 +1,37 @@ package com.redmadrobot.konfeature.source +/** + * Abstraction over a data source for feature configuration values. + * + * Feature sources provide the actual configuration values that are used by + * the Konfeature system. Sources can be remote configuration services + * (Firebase Remote Config, custom backends), local storage, or any other + * data provider. + * + * Sources are searched in the order they were registered, and the first + * source that returns a non-null value for a given key wins. + * + * Example implementations: + * - Firebase Remote Config + * - REST API backend + * - Local preferences/storage + * - Environment variables + */ public interface FeatureSource { + /** + * Unique name identifying this source. + * + * The name is used for logging, debugging, and source selection strategies. + * It must be unique among all registered sources. + */ public val name: String + /** + * Retrieves a configuration value for the given key. + * + * @param key the configuration key to look up + * @return the configuration value, or null if not found or not available + */ public fun get(key: String): Any? } diff --git a/konfeature/src/commonMain/kotlin/com/redmadrobot/konfeature/source/FeatureValueSource.kt b/konfeature/src/commonMain/kotlin/com/redmadrobot/konfeature/source/FeatureValueSource.kt index 6148383..3e4f5d3 100644 --- a/konfeature/src/commonMain/kotlin/com/redmadrobot/konfeature/source/FeatureValueSource.kt +++ b/konfeature/src/commonMain/kotlin/com/redmadrobot/konfeature/source/FeatureValueSource.kt @@ -2,14 +2,37 @@ package com.redmadrobot.konfeature.source import dev.drewhamilton.poko.Poko +/** + * Sealed class representing the source of a feature configuration value. + * + * This class provides information about where a configuration value originated from + * during the resolution process. It helps with debugging, logging, and understanding + * the configuration flow. + */ public sealed class FeatureValueSource { + /** + * Indicates the value came from a registered [FeatureSource]. + * + * @property name the name of the source that provided the value + */ @Poko public class Source(public val name: String) : FeatureValueSource() + /** + * Indicates the value was modified by a registered [Interceptor]. + * + * @property name the name of the interceptor that provided the value + */ @Poko public class Interceptor(public val name: String) : FeatureValueSource() + /** + * Indicates the value is the default value specified in the [com.redmadrobot.konfeature.FeatureValueSpec]. + * + * This happens when no sources provide a value or when the source selection + * strategy excludes all available sources. + */ @Suppress("ConvertObjectToDataObject") public object Default : FeatureValueSource() { override fun toString(): String = "Default" diff --git a/konfeature/src/commonMain/kotlin/com/redmadrobot/konfeature/source/Interceptor.kt b/konfeature/src/commonMain/kotlin/com/redmadrobot/konfeature/source/Interceptor.kt index f05170d..d657e54 100644 --- a/konfeature/src/commonMain/kotlin/com/redmadrobot/konfeature/source/Interceptor.kt +++ b/konfeature/src/commonMain/kotlin/com/redmadrobot/konfeature/source/Interceptor.kt @@ -1,8 +1,39 @@ package com.redmadrobot.konfeature.source +/** + * Interface for intercepting and modifying feature configuration values. + * + * Interceptors allow runtime modification of configuration values after they + * have been resolved from sources. This is particularly useful for: + * - Debug panels that allow overriding values for testing + * - A/B testing frameworks that modify values based on user segments + * - Development-time value overrides + * - Analytics and monitoring of configuration access + * + * Interceptors are applied in the order they were registered, and the last + * interceptor that returns a non-null value wins. + */ public interface Interceptor { + /** + * Unique name identifying this interceptor. + * + * The name is used for logging and debugging purposes. It should be + * descriptive of the interceptor's purpose (e.g., "DebugPanel", "ABTestInterceptor"). + */ public val name: String + /** + * Intercepts a configuration value and optionally provides a replacement. + * + * This method is called after a value has been resolved from sources but before + * it's returned to the caller. The interceptor can examine the value and its + * source, and optionally return a different value. + * + * @param valueSource the source where the value originated from + * @param key the configuration key + * @param value the resolved value from the source + * @return the replacement value, or null to leave the value unchanged + */ public fun intercept(valueSource: FeatureValueSource, key: String, value: Any): Any? } diff --git a/konfeature/src/commonMain/kotlin/com/redmadrobot/konfeature/source/SourceSelectionStrategy.kt b/konfeature/src/commonMain/kotlin/com/redmadrobot/konfeature/source/SourceSelectionStrategy.kt index db127c3..c776d46 100644 --- a/konfeature/src/commonMain/kotlin/com/redmadrobot/konfeature/source/SourceSelectionStrategy.kt +++ b/konfeature/src/commonMain/kotlin/com/redmadrobot/konfeature/source/SourceSelectionStrategy.kt @@ -1,13 +1,50 @@ package com.redmadrobot.konfeature.source +/** + * Strategy for selecting which sources can provide values for a configuration element. + * + * Source selection strategies allow fine-grained control over which data sources + * are consulted for specific configuration values. This enables scenarios like: + * - Development-only values that should ignore remote sources + * - Production values that should only come from verified sources + * - Feature flags that should only use specific A/B testing sources + * + * The strategy receives the names of all available sources and returns the subset + * that should be consulted for a particular configuration element. + */ public fun interface SourceSelectionStrategy { + /** + * Selects which sources should be consulted for a configuration value. + * + * @param names the names of all available registered sources + * @return the subset of source names that should be used for this configuration element + */ public fun select(names: Set): Set public companion object { + /** + * Strategy that prohibits using any sources, always falling back to default values. + * + * This is the default strategy, ensuring that configuration elements are + * explicitly opted into remote configuration to prevent accidental dependencies. + */ public val None: SourceSelectionStrategy = SourceSelectionStrategy { emptySet() } + + /** + * Strategy that allows using any available source. + * + * This strategy permits the configuration element to use values from any + * registered source, following the normal resolution order. + */ public val Any: SourceSelectionStrategy = SourceSelectionStrategy { it } + /** + * Creates a strategy that only allows specific named sources. + * + * @param sources the names of sources that should be allowed + * @return a strategy that only permits the specified sources + */ public fun anyOf(vararg sources: String): SourceSelectionStrategy = SourceSelectionStrategy { sources.toSet() } } }