Skip to content

Commit

Permalink
Refactor ErrorRecord into a Simple Data Class
Browse files Browse the repository at this point in the history
The ErrorRecord class has been refactored into a simpler data class. Initially, ErrorRecord could only be determined
within the ErrorRelay implementation. To accommodate different models, ErrorRecord was designed as an interface. In the
new implementation, the decision point for ErrorRecord has been added to each Options object through the
`shouldSuppressErrorRelay` method. This allows for a simplified ErrorRecord while preserving the same
mechanism. Consequently, the implementation has been modified to utilize a simpler data type.

refs: #51
  • Loading branch information
ogaclejapan committed Aug 24, 2024
1 parent 9e98933 commit 5a6c79f
Show file tree
Hide file tree
Showing 9 changed files with 115 additions and 101 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package soil.query

import kotlinx.coroutines.NonCancellable
import kotlinx.coroutines.withContext
import soil.query.core.ErrorRecord
import soil.query.core.RetryCallback
import soil.query.core.RetryFn
import soil.query.core.UniqueId
Expand Down Expand Up @@ -40,6 +41,8 @@ interface MutationCommand<T> {
}
}

internal typealias MutationErrorRelay = (ErrorRecord) -> Unit

/**
* Determines whether a mutation operation is necessary based on the current state.
*
Expand Down Expand Up @@ -139,9 +142,12 @@ fun <T> MutationCommand.Context<T>.reportMutationError(error: Throwable, id: Uni
if (options.onError == null && relay == null) {
return
}
val record = MutationError(error, id, state)
options.onError?.invoke(record)
relay?.invoke(record)
val record = ErrorRecord(error, id)
options.onError?.invoke(record, state)
val errorRelay = relay
if (errorRelay != null && options.shouldSuppressErrorRelay?.invoke(record, state) != true) {
errorRelay(record)
}
}

internal fun <T> MutationCommand.Context<T>.onRetryCallback(
Expand Down
39 changes: 0 additions & 39 deletions soil-query-core/src/commonMain/kotlin/soil/query/MutationError.kt

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
package soil.query

import soil.query.core.ActorOptions
import soil.query.core.ErrorRecord
import soil.query.core.LoggerFn
import soil.query.core.LoggingOptions
import soil.query.core.RetryOptions
Expand All @@ -30,7 +31,12 @@ interface MutationOptions : ActorOptions, LoggingOptions, RetryOptions {
/**
* This callback function will be called if some mutation encounters an error.
*/
val onError: ((MutationError) -> Unit)?
val onError: ((ErrorRecord, MutationModel<*>) -> Unit)?

/**
* Determines whether to suppress error information when relaying it using [soil.query.core.ErrorRelay].
*/
val shouldSuppressErrorRelay: ((ErrorRecord, MutationModel<*>) -> Boolean)?

/**
* Whether the query side effect should be synchronous. If true, side effect will be executed synchronously.
Expand All @@ -40,7 +46,8 @@ interface MutationOptions : ActorOptions, LoggingOptions, RetryOptions {
companion object Default : MutationOptions {
override val isOneShot: Boolean = false
override val isStrictMode: Boolean = false
override val onError: ((MutationError) -> Unit)? = null
override val onError: ((ErrorRecord, MutationModel<*>) -> Unit)? = null
override val shouldSuppressErrorRelay: ((ErrorRecord, MutationModel<*>) -> Boolean)? = null
override val shouldExecuteEffectSynchronously: Boolean = false

// ----- ActorOptions ----- //
Expand All @@ -60,10 +67,29 @@ interface MutationOptions : ActorOptions, LoggingOptions, RetryOptions {
}
}

/**
* Creates a new [MutationOptions] with the specified settings.
*
* @param isOneShot Only allows mutate to execute once while active (until reset).
* @param isStrictMode Requires revision match as a precondition for executing mutate.
* @param onError This callback function will be called if some mutation encounters an error.
* @param shouldSuppressErrorRelay Determines whether to suppress error information when relaying it using [soil.query.core.ErrorRelay].
* @param shouldExecuteEffectSynchronously Whether the query side effect should be synchronous.
* @param keepAliveTime The duration to keep the actor alive after the last message is processed.
* @param logger The logger function to use for logging.
* @param shouldRetry The predicate function to determine whether to retry on a given exception.
* @param retryCount The maximum number of retry attempts.
* @param retryInitialInterval The initial interval for exponential backoff.
* @param retryMaxInterval The maximum interval for exponential backoff.
* @param retryMultiplier The multiplier for exponential backoff.
* @param retryRandomizationFactor The randomization factor for exponential backoff.
* @param retryRandomizer The random number generator for exponential backoff.
*/
fun MutationOptions(
isOneShot: Boolean = MutationOptions.isOneShot,
isStrictMode: Boolean = MutationOptions.isStrictMode,
onError: ((MutationError) -> Unit)? = MutationOptions.onError,
onError: ((ErrorRecord, MutationModel<*>) -> Unit)? = MutationOptions.onError,
shouldSuppressErrorRelay: ((ErrorRecord, MutationModel<*>) -> Boolean)? = MutationOptions.shouldSuppressErrorRelay,
shouldExecuteEffectSynchronously: Boolean = MutationOptions.shouldExecuteEffectSynchronously,
keepAliveTime: Duration = MutationOptions.keepAliveTime,
logger: LoggerFn? = MutationOptions.logger,
Expand All @@ -78,7 +104,8 @@ fun MutationOptions(
return object : MutationOptions {
override val isOneShot: Boolean = isOneShot
override val isStrictMode: Boolean = isStrictMode
override val onError: ((MutationError) -> Unit)? = onError
override val onError: ((ErrorRecord, MutationModel<*>) -> Unit)? = onError
override val shouldSuppressErrorRelay: ((ErrorRecord, MutationModel<*>) -> Boolean)? = shouldSuppressErrorRelay
override val shouldExecuteEffectSynchronously: Boolean = shouldExecuteEffectSynchronously
override val keepAliveTime: Duration = keepAliveTime
override val logger: LoggerFn? = logger
Expand All @@ -92,10 +119,14 @@ fun MutationOptions(
}
}

/**
* Copies the current [MutationOptions] with the specified settings.
*/
fun MutationOptions.copy(
isOneShot: Boolean = this.isOneShot,
isStrictMode: Boolean = this.isStrictMode,
onError: ((MutationError) -> Unit)? = this.onError,
onError: ((ErrorRecord, MutationModel<*>) -> Unit)? = this.onError,
shouldSuppressErrorRelay: ((ErrorRecord, MutationModel<*>) -> Boolean)? = this.shouldSuppressErrorRelay,
shouldExecuteEffectSynchronously: Boolean = this.shouldExecuteEffectSynchronously,
keepAliveTime: Duration = this.keepAliveTime,
logger: LoggerFn? = this.logger,
Expand All @@ -111,6 +142,7 @@ fun MutationOptions.copy(
isOneShot = isOneShot,
isStrictMode = isStrictMode,
onError = onError,
shouldSuppressErrorRelay = shouldSuppressErrorRelay,
shouldExecuteEffectSynchronously = shouldExecuteEffectSynchronously,
keepAliveTime = keepAliveTime,
logger = logger,
Expand Down
12 changes: 9 additions & 3 deletions soil-query-core/src/commonMain/kotlin/soil/query/QueryCommand.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

package soil.query

import soil.query.core.ErrorRecord
import soil.query.core.RetryCallback
import soil.query.core.RetryFn
import soil.query.core.UniqueId
Expand Down Expand Up @@ -38,6 +39,8 @@ interface QueryCommand<T> {
}
}

internal typealias QueryErrorRelay = (ErrorRecord) -> Unit

/**
* Determines whether a fetch operation is necessary based on the current state.
*
Expand Down Expand Up @@ -144,9 +147,12 @@ fun <T> QueryCommand.Context<T>.reportQueryError(error: Throwable, id: UniqueId)
if (options.onError == null && relay == null) {
return
}
val record = QueryError(error, id, state)
options.onError?.invoke(record)
relay?.invoke(record)
val record = ErrorRecord(error, id)
options.onError?.invoke(record, state)
val errorRelay = relay
if (errorRelay != null && options.shouldSuppressErrorRelay?.invoke(record, state) != true) {
errorRelay(record)
}
}

internal fun <T> QueryCommand.Context<T>.onRetryCallback(
Expand Down
39 changes: 0 additions & 39 deletions soil-query-core/src/commonMain/kotlin/soil/query/QueryError.kt

This file was deleted.

45 changes: 40 additions & 5 deletions soil-query-core/src/commonMain/kotlin/soil/query/QueryOptions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
package soil.query

import soil.query.core.ActorOptions
import soil.query.core.ErrorRecord
import soil.query.core.LoggerFn
import soil.query.core.LoggingOptions
import soil.query.core.RetryOptions
Expand Down Expand Up @@ -63,7 +64,12 @@ interface QueryOptions : ActorOptions, LoggingOptions, RetryOptions {
/**
* This callback function will be called if some query encounters an error.
*/
val onError: ((QueryError) -> Unit)?
val onError: ((ErrorRecord, QueryModel<*>) -> Unit)?

/**
* Determines whether to suppress error information when relaying it using [soil.query.core.ErrorRelay].
*/
val shouldSuppressErrorRelay: ((ErrorRecord, QueryModel<*>) -> Boolean)?

companion object Default : QueryOptions {
override val staleTime: Duration = Duration.ZERO
Expand All @@ -72,7 +78,8 @@ interface QueryOptions : ActorOptions, LoggingOptions, RetryOptions {
override val pauseDurationAfter: ((Throwable) -> Duration?)? = null
override val revalidateOnReconnect: Boolean = true
override val revalidateOnFocus: Boolean = true
override val onError: ((QueryError) -> Unit)? = null
override val onError: ((ErrorRecord, QueryModel<*>) -> Unit)? = null
override val shouldSuppressErrorRelay: ((ErrorRecord, QueryModel<*>) -> Boolean)? = null

// ----- ActorOptions ----- //
override val keepAliveTime: Duration = 5.seconds
Expand All @@ -93,14 +100,36 @@ interface QueryOptions : ActorOptions, LoggingOptions, RetryOptions {
}
}

/**
* Creates a new [QueryOptions] with the specified settings.
*
* @param staleTime The duration after which the returned value of the fetch function block is considered stale.
* @param gcTime The period during which the Key's return value, if not referenced anywhere, is temporarily cached in memory.
* @param prefetchWindowTime Maximum window time on prefetch processing.
* @param pauseDurationAfter Determines whether query processing needs to be paused based on error.
* @param revalidateOnReconnect Automatically revalidate active [Query] when the network reconnects.
* @param revalidateOnFocus Automatically revalidate active [Query] when the window is refocused.
* @param onError This callback function will be called if some query encounters an error.
* @param shouldSuppressErrorRelay Determines whether to suppress error information when relaying it using [soil.query.core.ErrorRelay].
* @param keepAliveTime The duration to keep the actor alive after the last command is executed.
* @param logger The logger function.
* @param shouldRetry Determines whether to retry the command when an error occurs.
* @param retryCount The number of times to retry the command.
* @param retryInitialInterval The initial interval for exponential backoff.
* @param retryMaxInterval The maximum interval for exponential backoff.
* @param retryMultiplier The multiplier for exponential backoff.
* @param retryRandomizationFactor The randomization factor for exponential backoff.
* @param retryRandomizer The randomizer for exponential backoff.
*/
fun QueryOptions(
staleTime: Duration = QueryOptions.staleTime,
gcTime: Duration = QueryOptions.gcTime,
prefetchWindowTime: Duration = QueryOptions.prefetchWindowTime,
pauseDurationAfter: ((Throwable) -> Duration?)? = QueryOptions.pauseDurationAfter,
revalidateOnReconnect: Boolean = QueryOptions.revalidateOnReconnect,
revalidateOnFocus: Boolean = QueryOptions.revalidateOnFocus,
onError: ((QueryError) -> Unit)? = QueryOptions.onError,
onError: ((ErrorRecord, QueryModel<*>) -> Unit)? = QueryOptions.onError,
shouldSuppressErrorRelay: ((ErrorRecord, QueryModel<*>) -> Boolean)? = QueryOptions.shouldSuppressErrorRelay,
keepAliveTime: Duration = QueryOptions.keepAliveTime,
logger: LoggerFn? = QueryOptions.logger,
shouldRetry: (Throwable) -> Boolean = QueryOptions.shouldRetry,
Expand All @@ -118,7 +147,8 @@ fun QueryOptions(
override val pauseDurationAfter: ((Throwable) -> Duration?)? = pauseDurationAfter
override val revalidateOnReconnect: Boolean = revalidateOnReconnect
override val revalidateOnFocus: Boolean = revalidateOnFocus
override val onError: ((QueryError) -> Unit)? = onError
override val onError: ((ErrorRecord, QueryModel<*>) -> Unit)? = onError
override val shouldSuppressErrorRelay: ((ErrorRecord, QueryModel<*>) -> Boolean)? = shouldSuppressErrorRelay
override val keepAliveTime: Duration = keepAliveTime
override val logger: LoggerFn? = logger
override val shouldRetry: (Throwable) -> Boolean = shouldRetry
Expand All @@ -131,14 +161,18 @@ fun QueryOptions(
}
}

/**
* Copies the current [QueryOptions] with the specified settings.
*/
fun QueryOptions.copy(
staleTime: Duration = this.staleTime,
gcTime: Duration = this.gcTime,
prefetchWindowTime: Duration = this.prefetchWindowTime,
pauseDurationAfter: ((Throwable) -> Duration?)? = this.pauseDurationAfter,
revalidateOnReconnect: Boolean = this.revalidateOnReconnect,
revalidateOnFocus: Boolean = this.revalidateOnFocus,
onError: ((QueryError) -> Unit)? = this.onError,
onError: ((ErrorRecord, QueryModel<*>) -> Unit)? = this.onError,
shouldSuppressErrorRelay: ((ErrorRecord, QueryModel<*>) -> Boolean)? = this.shouldSuppressErrorRelay,
keepAliveTime: Duration = this.keepAliveTime,
logger: LoggerFn? = this.logger,
shouldRetry: (Throwable) -> Boolean = this.shouldRetry,
Expand All @@ -157,6 +191,7 @@ fun QueryOptions.copy(
revalidateOnReconnect = revalidateOnReconnect,
revalidateOnFocus = revalidateOnFocus,
onError = onError,
shouldSuppressErrorRelay = shouldSuppressErrorRelay,
keepAliveTime = keepAliveTime,
logger = logger,
shouldRetry = shouldRetry,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,17 @@ package soil.query.core
* Error information that can be received via a back-channel
* such as [ErrorRelay] or `onError` options for Query/Mutation.
*/
interface ErrorRecord {
data class ErrorRecord internal constructor(

/**
* The details of the error.
*/
val exception: Throwable
val exception: Throwable,

/**
* Key information that caused the error.
*
* NOTE: Defining an ID with a custom interface, such as metadata, can be helpful when receiving error information.
*/
val key: UniqueId
}
)
Loading

0 comments on commit 5a6c79f

Please sign in to comment.