diff --git a/build.gradle.kts b/build.gradle.kts index a26b27fb..6dff9233 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -28,7 +28,7 @@ allprojects { } } - version = "0.0.9-apha.1-SNAPSHOT" + version = "0.0.9-apha.2-SNAPSHOT" group = "io.github.ustudiocompany" configurations.all { diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ec4820d8..aed2d9e4 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -3,7 +3,7 @@ coroutines = "1.7.3" #Libs -airflux-commons = "0.0.3-SNAPSHOT" +airflux-commons = "0.0.4-SNAPSHOT" feel-engine = "1.19.0" jackson = "2.19.0" json-path = "2.10.0" diff --git a/library/rules-engine/core/api/rules-engine-core.api b/library/rules-engine/core/api/rules-engine-core.api index 5940dd26..45fd41b2 100644 --- a/library/rules-engine/core/api/rules-engine-core.api +++ b/library/rules-engine/core/api/rules-engine-core.api @@ -1,6 +1,9 @@ public abstract interface class io/github/ustudiocompany/uframework/rulesengine/core/BasicRulesEngineError : io/github/ustudiocompany/uframework/failure/Failure { } +public abstract interface class io/github/ustudiocompany/uframework/rulesengine/core/BasicRulesEngineIncident : io/github/ustudiocompany/uframework/failure/Failure { +} + public final class io/github/ustudiocompany/uframework/rulesengine/core/context/Context : java/lang/Iterable, kotlin/jvm/internal/markers/KMappedMarker { public static final field Companion Lio/github/ustudiocompany/uframework/rulesengine/core/context/Context$Companion; public synthetic fun (Ljava/util/Map;Lkotlin/jvm/internal/DefaultConstructorMarker;)V @@ -16,7 +19,7 @@ public final class io/github/ustudiocompany/uframework/rulesengine/core/context/ public final class io/github/ustudiocompany/uframework/rulesengine/core/context/Context$Builder { public fun ()V public final fun build ()Lio/github/ustudiocompany/uframework/rulesengine/core/context/Context; - public final fun set (Lio/github/ustudiocompany/uframework/rulesengine/core/rule/Source;Lio/github/ustudiocompany/uframework/json/element/JsonElement;)Lio/github/ustudiocompany/uframework/rulesengine/core/context/Context$Builder; + public final fun put (Lio/github/ustudiocompany/uframework/rulesengine/core/rule/Source;Lio/github/ustudiocompany/uframework/json/element/JsonElement;)Lio/github/ustudiocompany/uframework/rulesengine/core/context/Context$Builder; } public final class io/github/ustudiocompany/uframework/rulesengine/core/context/Context$Companion { diff --git a/library/rules-engine/core/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/BasicRulesEngineIncident.kt b/library/rules-engine/core/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/BasicRulesEngineIncident.kt new file mode 100644 index 00000000..91f74f92 --- /dev/null +++ b/library/rules-engine/core/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/BasicRulesEngineIncident.kt @@ -0,0 +1,5 @@ +package io.github.ustudiocompany.uframework.rulesengine.core + +import io.github.ustudiocompany.uframework.failure.Failure + +public interface BasicRulesEngineIncident : Failure diff --git a/library/rules-engine/core/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/context/Context.kt b/library/rules-engine/core/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/context/Context.kt index a6cffe44..d27305be 100644 --- a/library/rules-engine/core/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/context/Context.kt +++ b/library/rules-engine/core/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/context/Context.kt @@ -27,16 +27,26 @@ public class Context private constructor( public fun empty(): Context = Context(data = mutableMapOf()) public operator fun invoke(vararg sources: Pair): Context = - Context(data = sources.toMap(mutableMapOf())) + Builder() + .apply { + for ((source, value) in sources) + put(source, value) + } + .build() public operator fun invoke(sources: Map = emptyMap()): Context = - Context(data = sources.toMutableMap()) + Builder() + .apply { + for ((source, value) in sources) + put(source, value) + } + .build() } public class Builder { private val sources: MutableMap = mutableMapOf() - public operator fun set(source: Source, value: JsonElement): Builder { + public fun put(source: Source, value: JsonElement): Builder { sources[source] = value return this } diff --git a/library/rules-engine/core/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/feel/FeelExpression.kt b/library/rules-engine/core/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/feel/FeelExpression.kt index 87797772..505d7235 100644 --- a/library/rules-engine/core/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/feel/FeelExpression.kt +++ b/library/rules-engine/core/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/feel/FeelExpression.kt @@ -26,7 +26,7 @@ public interface FeelExpression { ) private companion object { - private const val PREFIX = "EVALUATE-FEEL-EXPRESSION-" + private const val PREFIX = "EVALUATE-FEEL-EXPRESSION-ERROR-" private const val DETAILS_KEY_EXPRESSION = "feel-expression" } } diff --git a/library/rules-engine/executor/api/rules-engine-executor.api b/library/rules-engine/executor/api/rules-engine-executor.api index abdff99b..c51db30a 100644 --- a/library/rules-engine/executor/api/rules-engine-executor.api +++ b/library/rules-engine/executor/api/rules-engine-executor.api @@ -1,11 +1,3 @@ -public final class io/github/ustudiocompany/uframework/rulesengine/core/data/PathLookupError : io/github/ustudiocompany/uframework/rulesengine/core/BasicRulesEngineError { - public fun (Lio/github/ustudiocompany/uframework/json/path/Path;Lio/github/ustudiocompany/uframework/json/path/Path$SearchError;)V - public fun getCause ()Lio/github/ustudiocompany/uframework/failure/Failure$Cause; - public fun getCode ()Ljava/lang/String; - public fun getDescription ()Ljava/lang/String; - public fun getDetails ()Lio/github/ustudiocompany/uframework/failure/Failure$Details; -} - public abstract interface class io/github/ustudiocompany/uframework/rulesengine/executor/DataChangeTrackerProvider { public abstract fun prepare-CsHC-xs (Ljava/lang/String;Ljava/util/List;)Lio/github/airflux/commons/types/maybe/Maybe; } @@ -34,6 +26,17 @@ public final class io/github/ustudiocompany/uframework/rulesengine/executor/Data public fun getDetails ()Lio/github/ustudiocompany/uframework/failure/Failure$Details; } +public final class io/github/ustudiocompany/uframework/rulesengine/executor/DataChangeTrackerProvider$Incident : io/github/ustudiocompany/uframework/rulesengine/core/BasicRulesEngineIncident { + public fun ()V + public fun (Lio/github/ustudiocompany/uframework/failure/Failure;)V + public fun (Ljava/lang/String;Ljava/lang/Throwable;Lio/github/ustudiocompany/uframework/failure/Failure$Details;)V + public synthetic fun (Ljava/lang/String;Ljava/lang/Throwable;Lio/github/ustudiocompany/uframework/failure/Failure$Details;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun getCause ()Lio/github/ustudiocompany/uframework/failure/Failure$Cause; + public fun getCode ()Ljava/lang/String; + public fun getDescription ()Ljava/lang/String; + public fun getDetails ()Lio/github/ustudiocompany/uframework/failure/Failure$Details; +} + public final class io/github/ustudiocompany/uframework/rulesengine/executor/DataChangeTrackerProvider$Uiss { public static final field Companion Lio/github/ustudiocompany/uframework/rulesengine/executor/DataChangeTrackerProvider$Uiss$Companion; public static final synthetic fun box-impl (Ljava/lang/String;)Lio/github/ustudiocompany/uframework/rulesengine/executor/DataChangeTrackerProvider$Uiss; @@ -80,6 +83,17 @@ public final class io/github/ustudiocompany/uframework/rulesengine/executor/Data public fun getDetails ()Lio/github/ustudiocompany/uframework/failure/Failure$Details; } +public final class io/github/ustudiocompany/uframework/rulesengine/executor/DataProvider$Incident : io/github/ustudiocompany/uframework/rulesengine/core/BasicRulesEngineIncident { + public fun ()V + public fun (Lio/github/ustudiocompany/uframework/failure/Failure;)V + public fun (Ljava/lang/String;Ljava/lang/Throwable;Lio/github/ustudiocompany/uframework/failure/Failure$Details;)V + public synthetic fun (Ljava/lang/String;Ljava/lang/Throwable;Lio/github/ustudiocompany/uframework/failure/Failure$Details;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun getCause ()Lio/github/ustudiocompany/uframework/failure/Failure$Cause; + public fun getCode ()Ljava/lang/String; + public fun getDescription ()Ljava/lang/String; + public fun getDetails ()Lio/github/ustudiocompany/uframework/failure/Failure$Details; +} + public final class io/github/ustudiocompany/uframework/rulesengine/executor/DataProvider$Uri { public static final field Companion Lio/github/ustudiocompany/uframework/rulesengine/executor/DataProvider$Uri$Companion; public static final synthetic fun box-impl (Ljava/lang/String;)Lio/github/ustudiocompany/uframework/rulesengine/executor/DataProvider$Uri; @@ -141,6 +155,18 @@ public final class io/github/ustudiocompany/uframework/rulesengine/executor/Http public fun getDetails ()Lio/github/ustudiocompany/uframework/failure/Failure$Details; } +public final class io/github/ustudiocompany/uframework/rulesengine/executor/HttpCallProvider$Incident : io/github/ustudiocompany/uframework/rulesengine/core/BasicRulesEngineIncident { + public fun ()V + public fun (Ljava/lang/String;Lio/github/ustudiocompany/uframework/failure/Failure;Lio/github/ustudiocompany/uframework/failure/Failure$Details;)V + public synthetic fun (Ljava/lang/String;Lio/github/ustudiocompany/uframework/failure/Failure;Lio/github/ustudiocompany/uframework/failure/Failure$Details;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Ljava/lang/String;Ljava/lang/Throwable;Lio/github/ustudiocompany/uframework/failure/Failure$Details;)V + public synthetic fun (Ljava/lang/String;Ljava/lang/Throwable;Lio/github/ustudiocompany/uframework/failure/Failure$Details;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun getCause ()Lio/github/ustudiocompany/uframework/failure/Failure$Cause; + public fun getCode ()Ljava/lang/String; + public fun getDescription ()Ljava/lang/String; + public fun getDetails ()Lio/github/ustudiocompany/uframework/failure/Failure$Details; +} + public final class io/github/ustudiocompany/uframework/rulesengine/executor/HttpCallProvider$Uri { public static final field Companion Lio/github/ustudiocompany/uframework/rulesengine/executor/HttpCallProvider$Uri$Companion; public static final synthetic fun box-impl (Ljava/lang/String;)Lio/github/ustudiocompany/uframework/rulesengine/executor/HttpCallProvider$Uri; @@ -174,6 +200,17 @@ public final class io/github/ustudiocompany/uframework/rulesengine/executor/Merg public fun getDetails ()Lio/github/ustudiocompany/uframework/failure/Failure$Details; } +public final class io/github/ustudiocompany/uframework/rulesengine/executor/Merger$Incident : io/github/ustudiocompany/uframework/rulesengine/core/BasicRulesEngineIncident { + public fun ()V + public fun (Lio/github/ustudiocompany/uframework/failure/Failure;)V + public fun (Ljava/lang/String;Ljava/lang/Throwable;Lio/github/ustudiocompany/uframework/failure/Failure$Details;)V + public synthetic fun (Ljava/lang/String;Ljava/lang/Throwable;Lio/github/ustudiocompany/uframework/failure/Failure$Details;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun getCause ()Lio/github/ustudiocompany/uframework/failure/Failure$Cause; + public fun getCode ()Ljava/lang/String; + public fun getDescription ()Ljava/lang/String; + public fun getDetails ()Lio/github/ustudiocompany/uframework/failure/Failure$Details; +} + public abstract interface class io/github/ustudiocompany/uframework/rulesengine/executor/MessagePublisher { public abstract fun publish (Ljava/lang/String;Ljava/util/List;Ljava/lang/String;)Lio/github/airflux/commons/types/maybe/Maybe; } @@ -202,6 +239,17 @@ public final class io/github/ustudiocompany/uframework/rulesengine/executor/Mess public fun toString ()Ljava/lang/String; } +public final class io/github/ustudiocompany/uframework/rulesengine/executor/MessagePublisher$Incident : io/github/ustudiocompany/uframework/rulesengine/core/BasicRulesEngineIncident { + public fun ()V + public fun (Lio/github/ustudiocompany/uframework/failure/Failure;)V + public fun (Ljava/lang/String;Ljava/lang/Throwable;Lio/github/ustudiocompany/uframework/failure/Failure$Details;)V + public synthetic fun (Ljava/lang/String;Ljava/lang/Throwable;Lio/github/ustudiocompany/uframework/failure/Failure$Details;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun getCause ()Lio/github/ustudiocompany/uframework/failure/Failure$Cause; + public fun getCode ()Ljava/lang/String; + public fun getDescription ()Ljava/lang/String; + public fun getDetails ()Lio/github/ustudiocompany/uframework/failure/Failure$Details; +} + public final class io/github/ustudiocompany/uframework/rulesengine/executor/RulesEngineExecutor { public fun (Lio/github/ustudiocompany/uframework/rulesengine/executor/HttpCallProvider;Lio/github/ustudiocompany/uframework/rulesengine/executor/DataProvider;Lio/github/ustudiocompany/uframework/rulesengine/executor/MessagePublisher;Lio/github/ustudiocompany/uframework/rulesengine/executor/DataChangeTrackerProvider;Lio/github/ustudiocompany/uframework/rulesengine/executor/Merger;)V public final fun execute-LHWKZ78 (Lio/github/ustudiocompany/uframework/rulesengine/core/env/EnvVars;Lio/github/ustudiocompany/uframework/rulesengine/core/context/Context;Ljava/util/List;)Lio/github/airflux/commons/types/resultk/ResultK; @@ -271,3 +319,49 @@ public final class io/github/ustudiocompany/uframework/rulesengine/executor/erro public fun getDescription ()Ljava/lang/String; } +public abstract class io/github/ustudiocompany/uframework/rulesengine/executor/incident/RuleExecutionIncident : io/github/ustudiocompany/uframework/rulesengine/core/BasicRulesEngineIncident { + public synthetic fun (Ljava/lang/String;Lkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun getDetails ()Lio/github/ustudiocompany/uframework/failure/Failure$Details; +} + +public final class io/github/ustudiocompany/uframework/rulesengine/executor/incident/RuleExecutionIncident$Execution : io/github/ustudiocompany/uframework/rulesengine/executor/incident/RuleExecutionIncident { + public fun getCause ()Lio/github/ustudiocompany/uframework/failure/Failure$Cause; + public fun getCode ()Ljava/lang/String; + public fun getDescription ()Ljava/lang/String; +} + +public abstract class io/github/ustudiocompany/uframework/rulesengine/executor/incident/StepExecutionIncident : io/github/ustudiocompany/uframework/rulesengine/core/BasicRulesEngineIncident { + public synthetic fun (Ljava/lang/String;Lkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun getDetails ()Lio/github/ustudiocompany/uframework/failure/Failure$Details; +} + +public final class io/github/ustudiocompany/uframework/rulesengine/executor/incident/StepExecutionIncident$DataBuild : io/github/ustudiocompany/uframework/rulesengine/executor/incident/StepExecutionIncident { + public fun getCause ()Lio/github/ustudiocompany/uframework/failure/Failure$Cause; + public fun getCode ()Ljava/lang/String; + public fun getDescription ()Ljava/lang/String; +} + +public final class io/github/ustudiocompany/uframework/rulesengine/executor/incident/StepExecutionIncident$DataChangeTracking : io/github/ustudiocompany/uframework/rulesengine/executor/incident/StepExecutionIncident { + public fun getCause ()Lio/github/ustudiocompany/uframework/failure/Failure$Cause; + public fun getCode ()Ljava/lang/String; + public fun getDescription ()Ljava/lang/String; +} + +public final class io/github/ustudiocompany/uframework/rulesengine/executor/incident/StepExecutionIncident$DataRetrieve : io/github/ustudiocompany/uframework/rulesengine/executor/incident/StepExecutionIncident { + public fun getCause ()Lio/github/ustudiocompany/uframework/failure/Failure$Cause; + public fun getCode ()Ljava/lang/String; + public fun getDescription ()Ljava/lang/String; +} + +public final class io/github/ustudiocompany/uframework/rulesengine/executor/incident/StepExecutionIncident$HttpCall : io/github/ustudiocompany/uframework/rulesengine/executor/incident/StepExecutionIncident { + public fun getCause ()Lio/github/ustudiocompany/uframework/failure/Failure$Cause; + public fun getCode ()Ljava/lang/String; + public fun getDescription ()Ljava/lang/String; +} + +public final class io/github/ustudiocompany/uframework/rulesengine/executor/incident/StepExecutionIncident$MessagePublish : io/github/ustudiocompany/uframework/rulesengine/executor/incident/StepExecutionIncident { + public fun getCause ()Lio/github/ustudiocompany/uframework/failure/Failure$Cause; + public fun getCode ()Ljava/lang/String; + public fun getDescription ()Ljava/lang/String; +} + diff --git a/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/context/ContextExtensions.kt b/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/context/ContextExtensions.kt index 51dbbcda..449a9819 100644 --- a/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/context/ContextExtensions.kt +++ b/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/context/ContextExtensions.kt @@ -1,143 +1,59 @@ package io.github.ustudiocompany.uframework.rulesengine.core.context +import io.github.airflux.commons.types.fail.Fail import io.github.airflux.commons.types.maybe.Maybe -import io.github.airflux.commons.types.maybe.asSome -import io.github.airflux.commons.types.maybe.map +import io.github.airflux.commons.types.maybe.MaybeBiFailure +import io.github.airflux.commons.types.maybe.maybeError import io.github.airflux.commons.types.maybe.maybeFailure import io.github.airflux.commons.types.resultk.ResultK import io.github.airflux.commons.types.resultk.asFailure import io.github.airflux.commons.types.resultk.asSuccess +import io.github.airflux.commons.types.resultk.liftToError import io.github.airflux.commons.types.resultk.mapFailure import io.github.ustudiocompany.uframework.failure.Failure import io.github.ustudiocompany.uframework.json.element.JsonElement import io.github.ustudiocompany.uframework.rulesengine.core.BasicRulesEngineError +import io.github.ustudiocompany.uframework.rulesengine.core.BasicRulesEngineIncident import io.github.ustudiocompany.uframework.rulesengine.core.rule.Source import io.github.ustudiocompany.uframework.rulesengine.core.rule.step.StepResult import io.github.ustudiocompany.uframework.rulesengine.executor.Merger +internal operator fun Context.get(source: Source): ResultK = + getOrNull(source) + ?.asSuccess() + ?: ContextErrors.SourceMissing(source).asFailure() + internal fun Context.update( source: Source, action: StepResult.Action, value: JsonElement, merge: Merger -): Maybe = +): MaybeBiFailure = when (action) { is StepResult.Action.Put -> put(source = source, value = value) - .map { failure -> ContextUpdateErrors.DataAddition(source = source, cause = failure) } - is StepResult.Action.Replace -> replace(source = source, value = value) - .map { failure -> ContextUpdateErrors.DataReplacement(source = source, cause = failure) } - is StepResult.Action.Merge -> merge( source = source, value = value, strategyCode = action.strategyCode, merge = merge - ).map { failure -> ContextUpdateErrors.DataMerge(source = source, cause = failure) } - } - -internal sealed interface ContextUpdateErrors : BasicRulesEngineError { - - class DataAddition(source: Source, cause: ContextDataAdditionErrors) : ContextUpdateErrors { - override val code: String = PREFIX + "1" - override val description: String = - "Error adding data to context by source '${source.get}'." - override val cause: Failure.Cause = Failure.Cause.Failure(cause) - override val details: Failure.Details = Failure.Details.of( - DETAILS_KEY_SOURCE to source.get ) } - class DataReplacement(source: Source, cause: ContextDataReplacementErrors) : ContextUpdateErrors { - override val code: String = PREFIX + "2" - override val description: String = "Error replacing data in the context by source '${source.get}'." - override val cause: Failure.Cause = Failure.Cause.Failure(cause) - override val details: Failure.Details = Failure.Details.of( - DETAILS_KEY_SOURCE to source.get - ) - } - - class DataMerge(source: Source, cause: ContextDataMergeErrors) : ContextUpdateErrors { - override val code: String = PREFIX + "3" - override val description: String = "Error merging data in the context by source '${source.get}'." - override val cause: Failure.Cause = Failure.Cause.Failure(cause) - override val details: Failure.Details = Failure.Details.of( - DETAILS_KEY_SOURCE to source.get - ) - } - - private companion object { - private const val PREFIX = "UPDATE-DATA-IN-CONTEXT-" - } -} - -internal operator fun Context.get(source: Source): ResultK = - getOrNull(source) - ?.asSuccess() - ?: ContextDataRetrievalErrors.SourceMissing(source).asFailure() - -internal sealed interface ContextDataRetrievalErrors : BasicRulesEngineError { - - class SourceMissing(source: Source) : ContextDataRetrievalErrors { - override val code: String = PREFIX + "1" - override val description: String = "The source '${source.get}' is not found in the context." - override val details: Failure.Details = Failure.Details.of( - DETAILS_KEY_SOURCE to source.get - ) - } - - private companion object { - private const val PREFIX = "RETRIEVE-DATA-FROM-CONTEXT-" - } -} - -internal fun Context.put(source: Source, value: JsonElement): Maybe { +internal fun Context.put(source: Source, value: JsonElement): Maybe> { val isAdded = putIfAbsent(source, value) return if (isAdded) Maybe.none() else - ContextDataAdditionErrors.SourceAlreadyExists(source).asSome() + maybeError(ContextErrors.SourceAlreadyExists(source)) } -internal sealed interface ContextDataAdditionErrors : BasicRulesEngineError { - - class SourceAlreadyExists(source: Source) : ContextDataAdditionErrors { - override val code: String = PREFIX + "2" - override val description: String = "The source '${source.get}' is already exists in the context." - override val details: Failure.Details = Failure.Details.of( - DETAILS_KEY_SOURCE to source.get - ) - } - - private companion object { - private const val PREFIX = "ADD-DATA-TO-CONTEXT-" - } -} - -internal fun Context.replace( - source: Source, - value: JsonElement -): Maybe { +internal fun Context.replace(source: Source, value: JsonElement): Maybe> { val isReplaced = putIfPresent(source, value) return if (isReplaced) Maybe.none() else - ContextDataReplacementErrors.SourceMissing(source).asSome() -} - -internal sealed interface ContextDataReplacementErrors : BasicRulesEngineError { - - class SourceMissing(source: Source) : ContextDataReplacementErrors { - override val code: String = PREFIX + "1" - override val description: String = "The source '${source.get}' is not found in the context." - override val details: Failure.Details = Failure.Details.of( - DETAILS_KEY_SOURCE to source.get - ) - } - - private companion object { - private const val PREFIX = "REPLACE-DATA-FROM-CONTEXT-" - } + maybeError(ContextErrors.DataReplacement(source)) } internal fun Context.merge( @@ -145,40 +61,54 @@ internal fun Context.merge( value: JsonElement, strategyCode: StepResult.Action.Merge.StrategyCode, merge: Merger -): Maybe = +): MaybeBiFailure = maybeFailure { val context = this@merge - val (origin) = context[source] - .mapFailure { failure -> - ContextDataMergeErrors.DataRetrieval(source = source, cause = failure) - } + val (origin) = context[source].liftToError() val (updated) = merge.merge(strategyCode, origin, value) - .mapFailure { failure -> ContextDataMergeErrors.DataMerge(cause = failure) } - replace(source = source, value = updated) - .map { failure -> ContextDataMergeErrors.DataReplacement(source = source, cause = failure) } + .mapFailure( + onError = { error -> ContextErrors.DataMerge(source = source, cause = error) }, + onException = { incident -> ContextIncident.DataMerge(source = source, cause = incident) } + ) + + context.replace(source, updated) } -internal sealed interface ContextDataMergeErrors : BasicRulesEngineError { +private const val DETAILS_KEY_SOURCE = "source-name" + +internal sealed interface ContextErrors : BasicRulesEngineError { - class DataRetrieval(source: Source, cause: ContextDataRetrievalErrors) : ContextDataMergeErrors { + class SourceMissing(source: Source) : ContextErrors { override val code: String = PREFIX + "1" - override val description: String = - "Error getting data from the context by source '${source.get}'." - override val cause: Failure.Cause = Failure.Cause.Failure(cause) + override val description: String = "The source '${source.get}' is not found in the context." override val details: Failure.Details = Failure.Details.of( DETAILS_KEY_SOURCE to source.get ) } - class DataMerge(cause: Failure) : ContextDataMergeErrors { + class SourceAlreadyExists(source: Source) : ContextErrors { override val code: String = PREFIX + "2" - override val description: String = "Error merging data." - override val cause: Failure.Cause = Failure.Cause.Failure(cause) + override val description: String = + "Error adding data to context by source '${source.get}'. Source is already exists." + override val cause: Failure.Cause = Failure.Cause.None + override val details: Failure.Details = Failure.Details.of( + DETAILS_KEY_SOURCE to source.get + ) } - class DataReplacement(source: Source, cause: ContextDataReplacementErrors) : ContextDataMergeErrors { + class DataReplacement(source: Source) : ContextErrors { override val code: String = PREFIX + "3" - override val description: String = "Error replacing data in the context by source '${source.get}'." + override val description: String = + "Error replacing data in the context by source '${source.get}'. Source is missing." + override val cause: Failure.Cause = Failure.Cause.None + override val details: Failure.Details = Failure.Details.of( + DETAILS_KEY_SOURCE to source.get + ) + } + + class DataMerge(source: Source, cause: Merger.Error) : ContextErrors { + override val code: String = PREFIX + "4" + override val description: String = "Error merging data in the context by source '${source.get}'." override val cause: Failure.Cause = Failure.Cause.Failure(cause) override val details: Failure.Details = Failure.Details.of( DETAILS_KEY_SOURCE to source.get @@ -186,8 +116,22 @@ internal sealed interface ContextDataMergeErrors : BasicRulesEngineError { } private companion object { - private const val PREFIX = "MERGE-DATA-IN-CONTEXT-" + private const val PREFIX = "UPDATE-DATA-IN-CONTEXT-ERROR-" } } -private const val DETAILS_KEY_SOURCE = "source-name" +internal sealed interface ContextIncident : BasicRulesEngineIncident { + + class DataMerge(source: Source, cause: Merger.Incident) : ContextIncident { + override val code: String = PREFIX + "1" + override val description: String = "Incident updating data in the context by source '${source.get}'." + override val cause: Failure.Cause = Failure.Cause.Failure(cause) + override val details: Failure.Details = Failure.Details.of( + DETAILS_KEY_SOURCE to source.get + ) + } + + private companion object { + private const val PREFIX = "UPDATE-DATA-IN-CONTEXT-INCIDENT-" + } +} diff --git a/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/data/JsonElementExtensions.kt b/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/data/JsonElementExtensions.kt index 026c9177..ecaa1aa2 100644 --- a/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/data/JsonElementExtensions.kt +++ b/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/data/JsonElementExtensions.kt @@ -1,28 +1,9 @@ package io.github.ustudiocompany.uframework.rulesengine.core.data import io.github.airflux.commons.types.resultk.ResultK -import io.github.airflux.commons.types.resultk.mapFailure -import io.github.ustudiocompany.uframework.failure.Failure import io.github.ustudiocompany.uframework.json.element.JsonElement import io.github.ustudiocompany.uframework.json.path.Path -import io.github.ustudiocompany.uframework.rulesengine.core.BasicRulesEngineError -internal fun JsonElement.toStringValue() = - if (this is JsonElement.Text) this.get else this.toJson() +internal fun JsonElement.toStringValue() = if (this is JsonElement.Text) this.get else this.toJson() -internal fun JsonElement.search(path: Path): ResultK = - path.searchIn(this).mapFailure { failure -> PathLookupError(path = path, cause = failure) } - -public class PathLookupError(path: Path, cause: Path.SearchError) : BasicRulesEngineError { - override val code: String = PREFIX + "1" - override val description: String = "Error searching within data at the specified path '${path.text}'." - override val cause: Failure.Cause = Failure.Cause.Failure(cause) - override val details: Failure.Details = Failure.Details.of( - DETAILS_KEY_PATH to path.text - ) - - private companion object { - private const val PREFIX = "DATA-SEARCH-" - private const val DETAILS_KEY_PATH = "json-path" - } -} +internal fun JsonElement.search(path: Path): ResultK = path.searchIn(this) diff --git a/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/env/EnvVarsExtensions.kt b/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/env/EnvVarsExtensions.kt index ef49ccb7..67a81ab8 100644 --- a/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/env/EnvVarsExtensions.kt +++ b/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/env/EnvVarsExtensions.kt @@ -26,7 +26,7 @@ internal sealed interface EnvVarReadingErrors : BasicRulesEngineError { } private companion object { - private const val PREFIX = "GET-VALUE-FROM-ENV-VARS-" + private const val PREFIX = "GET-VALUE-FROM-ENV-VARS-ERROR-" } } diff --git a/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/operation/OperationCalculator.kt b/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/operation/OperationCalculator.kt index 0607f8ce..11b3e06b 100644 --- a/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/operation/OperationCalculator.kt +++ b/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/operation/OperationCalculator.kt @@ -36,6 +36,6 @@ internal sealed interface OperationCalculationErrors : BasicRulesEngineError { } private companion object { - private const val PREFIX = "CALCULATE-OPERATION-" + private const val PREFIX = "CALCULATE-OPERATION-ERROR-" } } diff --git a/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/rule/ValueCompute.kt b/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/rule/ValueCompute.kt index da2e6deb..6150be4f 100644 --- a/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/rule/ValueCompute.kt +++ b/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/rule/ValueCompute.kt @@ -10,9 +10,8 @@ import io.github.ustudiocompany.uframework.json.element.JsonElement import io.github.ustudiocompany.uframework.json.path.Path import io.github.ustudiocompany.uframework.rulesengine.core.BasicRulesEngineError import io.github.ustudiocompany.uframework.rulesengine.core.context.Context -import io.github.ustudiocompany.uframework.rulesengine.core.context.ContextDataRetrievalErrors +import io.github.ustudiocompany.uframework.rulesengine.core.context.ContextErrors import io.github.ustudiocompany.uframework.rulesengine.core.context.get -import io.github.ustudiocompany.uframework.rulesengine.core.data.PathLookupError import io.github.ustudiocompany.uframework.rulesengine.core.data.search import io.github.ustudiocompany.uframework.rulesengine.core.env.EnvVarName import io.github.ustudiocompany.uframework.rulesengine.core.env.EnvVarReadingErrors @@ -30,7 +29,7 @@ internal fun Value.compute(envVars: EnvVars, context: Context): ResultK ValueComputationErrors.ContextDataRetrieval(source = source, cause = failure) } .andThen { element -> element.search(path) - .mapFailure { failure -> ValueComputationErrors.PathLookup(path = path, cause = failure) } + .mapFailure { failure -> ValueComputationErrors.PathSearch(path = path, cause = failure) } .filterNotNull { ValueComputationErrors.DataNotFoundAtPath(source = source, path = path) } } @@ -40,13 +39,13 @@ internal fun Value.compute(envVars: EnvVars, context: Context): ResultK envVars[name] .mapFailure { failure -> ValueComputationErrors.EnvVarReading(name = name, cause = failure) } - is Value.DataStruct -> this.scheme.build(envVars, context) + is Value.DataStruct -> scheme.build(envVars, context) .mapFailure { failure -> ValueComputationErrors.DataStructBuild(cause = failure) } } internal sealed interface ValueComputationErrors : BasicRulesEngineError { - class ContextDataRetrieval(source: Source, cause: ContextDataRetrievalErrors) : ValueComputationErrors { + class ContextDataRetrieval(source: Source, cause: ContextErrors.SourceMissing) : ValueComputationErrors { override val code: String = PREFIX + "1" override val description: String = "Error retrieving a data from context by source '${source.get}' for computing value." @@ -69,7 +68,7 @@ internal sealed interface ValueComputationErrors : BasicRulesEngineError { ) } - class PathLookup(path: Path, cause: PathLookupError) : ValueComputationErrors { + class PathSearch(path: Path, cause: Path.SearchError) : ValueComputationErrors { override val code: String = PREFIX + "3" override val description: String = "Error searching within data at the specified path '${path.text}' for computing value." @@ -101,7 +100,7 @@ internal sealed interface ValueComputationErrors : BasicRulesEngineError { } private companion object { - private const val PREFIX = "VALUE-COMPUTE-" + private const val PREFIX = "VALUE-COMPUTE-ERROR-" private const val DETAILS_KEY_PATH = "json-path" private const val DETAILS_KEY_SOURCE = "source-name" private const val DETAILS_KEY_ENV_VAR = "env-var-name" @@ -121,20 +120,16 @@ internal fun Value.computeOrNull( } .andThen { element -> element.search(path) - .mapFailure { failure -> - OptionalValueComputationErrors.PathSearch(path = path, cause = failure) - } + .mapFailure { failure -> OptionalValueComputationErrors.PathSearch(path = path, cause = failure) } } is Value.Expression -> expression.evaluate(envVars, context) .mapFailure { failure -> OptionalValueComputationErrors.FeelExpressionEvaluate(cause = failure) } is Value.EnvVars -> envVars[name] - .mapFailure { failure -> - OptionalValueComputationErrors.EnvVarReading(name = name, cause = failure) - } + .mapFailure { failure -> OptionalValueComputationErrors.EnvVarReading(name = name, cause = failure) } - is Value.DataStruct -> this.scheme.build(envVars, context) + is Value.DataStruct -> scheme.build(envVars, context) .mapFailure { failure -> OptionalValueComputationErrors.DataStructBuild(cause = failure) } } @@ -142,7 +137,7 @@ internal sealed interface OptionalValueComputationErrors : BasicRulesEngineError class ContextDataRetrieval( source: Source, - cause: ContextDataRetrievalErrors.SourceMissing + cause: ContextErrors.SourceMissing ) : OptionalValueComputationErrors { override val code: String = PREFIX + "1" override val description: String = @@ -166,9 +161,10 @@ internal sealed interface OptionalValueComputationErrors : BasicRulesEngineError ) } - class PathSearch(path: Path, cause: PathLookupError) : OptionalValueComputationErrors { + class PathSearch(path: Path, cause: Path.SearchError) : OptionalValueComputationErrors { override val code: String = PREFIX + "3" - override val description: String = "Error searching a data by path '${path.text}' for computing value." + override val description: String = + "Error searching within data at the specified path '${path.text}' for computing value." override val cause: Failure.Cause = Failure.Cause.Failure(cause) override val details: Failure.Details = Failure.Details.of( DETAILS_KEY_PATH to path.text @@ -188,7 +184,7 @@ internal sealed interface OptionalValueComputationErrors : BasicRulesEngineError } private companion object { - private const val PREFIX = "OPTIONAL-VALUE-COMPUTE-" + private const val PREFIX = "OPTIONAL-VALUE-COMPUTE-ERROR-" private const val DETAILS_KEY_PATH = "json-path" private const val DETAILS_KEY_SOURCE = "source-name" private const val DETAILS_KEY_ENV_VAR = "env-var-name" diff --git a/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/rule/condition/ConditionExtensions.kt b/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/rule/condition/ConditionExtensions.kt index eb39e5a7..7914a70b 100644 --- a/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/rule/condition/ConditionExtensions.kt +++ b/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/rule/condition/ConditionExtensions.kt @@ -34,7 +34,7 @@ internal class ConditionEvaluationErrors(cause: PredicateEvaluationErrors) : Bas override val cause: Failure.Cause = Failure.Cause.Failure(cause) private companion object { - private const val PREFIX = "CONDITION-EVALUATION-" + private const val PREFIX = "CONDITION-EVALUATION-ERROR-" } } @@ -43,7 +43,7 @@ private fun Predicate.evaluate( context: Context ): ResultK = this.calculate(envVars, context) - .mapFailure { failure -> PredicateEvaluationErrors(failure) } + .mapFailure { failure -> PredicateEvaluationErrors(cause = failure) } internal class PredicateEvaluationErrors(cause: OperationCalculationErrors) : BasicRulesEngineError { override val code: String = PREFIX + "1" @@ -51,6 +51,6 @@ internal class PredicateEvaluationErrors(cause: OperationCalculationErrors) : Ba override val cause: Failure.Cause = Failure.Cause.Failure(cause) private companion object { - private const val PREFIX = "PREDICATE-EVALUATION-" + private const val PREFIX = "PREDICATE-EVALUATION-ERROR-" } } diff --git a/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/rule/step/ArgsBuilder.kt b/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/rule/step/ArgsBuilder.kt index 40a89d79..e12460b0 100644 --- a/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/rule/step/ArgsBuilder.kt +++ b/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/rule/step/ArgsBuilder.kt @@ -22,9 +22,7 @@ internal fun Args.build( .apply { args.get.forEach { arg -> val (value) = arg.value.compute(envVars, context) - .mapFailure { failure -> - ArgBuildErrors.ValueComputation(arg = arg, cause = failure) - } + .mapFailure { failure -> ArgBuildErrors.ValueComputation(arg = arg, cause = failure) } val argValue = value.toStringValue() add(builder(arg.name, argValue)) } @@ -40,6 +38,6 @@ internal sealed interface ArgBuildErrors : BasicRulesEngineError { } private companion object { - private const val PREFIX = "ARG-BUILD-" + private const val PREFIX = "ARG-BUILD-ERROR-" } } diff --git a/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/rule/step/DataBuildStepExecutor.kt b/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/rule/step/DataBuildStepExecutor.kt index 3775546d..5b9a6e0e 100644 --- a/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/rule/step/DataBuildStepExecutor.kt +++ b/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/rule/step/DataBuildStepExecutor.kt @@ -1,14 +1,17 @@ package io.github.ustudiocompany.uframework.rulesengine.core.rule.step import io.github.airflux.commons.types.maybe.Maybe +import io.github.airflux.commons.types.maybe.MaybeBiFailure import io.github.airflux.commons.types.maybe.map import io.github.airflux.commons.types.maybe.maybeFailure -import io.github.airflux.commons.types.resultk.mapFailure +import io.github.airflux.commons.types.resultk.mapFailureToError import io.github.ustudiocompany.uframework.failure.Failure import io.github.ustudiocompany.uframework.json.element.JsonElement import io.github.ustudiocompany.uframework.rulesengine.core.BasicRulesEngineError +import io.github.ustudiocompany.uframework.rulesengine.core.BasicRulesEngineIncident import io.github.ustudiocompany.uframework.rulesengine.core.context.Context -import io.github.ustudiocompany.uframework.rulesengine.core.context.ContextUpdateErrors +import io.github.ustudiocompany.uframework.rulesengine.core.context.ContextErrors +import io.github.ustudiocompany.uframework.rulesengine.core.context.ContextIncident import io.github.ustudiocompany.uframework.rulesengine.core.context.update import io.github.ustudiocompany.uframework.rulesengine.core.env.EnvVars import io.github.ustudiocompany.uframework.rulesengine.executor.Merger @@ -17,7 +20,7 @@ internal fun DataBuildStep.execute( envVars: EnvVars, context: Context, merger: Merger -): Maybe { +): MaybeBiFailure { val step = this return maybeFailure { val (value) = step.buildData(envVars, context) @@ -27,12 +30,15 @@ internal fun DataBuildStep.execute( private fun DataBuildStep.buildData(envVars: EnvVars, context: Context) = dataSchema.build(envVars, context) - .mapFailure { failure -> DataBuildStepExecutionErrors.DataBuild(failure) } + .mapFailureToError { failure -> DataBuildStepExecutionErrors.DataBuild(cause = failure) } private fun Context.update(value: JsonElement, result: StepResult?, merger: Merger) = result?.let { result -> update(result.source, result.action, value, merger) - .map { failure -> DataBuildStepExecutionErrors.ContextUpdate(failure) } + .map( + onError = { error -> DataBuildStepExecutionErrors.ResultApply(error) }, + onException = { incident -> DataBuildStepExecutionIncident.ResultApply(cause = incident) } + ) } ?: Maybe.none() internal sealed interface DataBuildStepExecutionErrors : BasicRulesEngineError { @@ -43,13 +49,26 @@ internal sealed interface DataBuildStepExecutionErrors : BasicRulesEngineError { override val cause: Failure.Cause = Failure.Cause.Failure(cause) } - class ContextUpdate(cause: ContextUpdateErrors) : DataBuildStepExecutionErrors { + class ResultApply(cause: ContextErrors) : DataBuildStepExecutionErrors { override val code: String = PREFIX + "2" - override val description: String = "Error updating context in the 'Data Build' step." + override val description: String = "Error of processing the result of the 'Data Build' step." override val cause: Failure.Cause = Failure.Cause.Failure(cause) } private companion object { - private const val PREFIX = "DATA-BUILD-STEP-EXECUTION-" + private const val PREFIX = "DATA-BUILD-STEP-EXECUTION-ERROR-" + } +} + +internal sealed interface DataBuildStepExecutionIncident : BasicRulesEngineIncident { + + class ResultApply(cause: ContextIncident) : DataBuildStepExecutionIncident { + override val code: String = PREFIX + "1" + override val description: String = "Incident of processing the result of the 'Data Build' step." + override val cause: Failure.Cause = Failure.Cause.Failure(cause) + } + + private companion object { + private const val PREFIX = "DATA-BUILD-STEP-EXECUTION-INCIDENT-" } } diff --git a/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/rule/step/DataByDataSchemeBuilder.kt b/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/rule/step/DataByDataSchemeBuilder.kt index bafb024d..2758cf14 100644 --- a/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/rule/step/DataByDataSchemeBuilder.kt +++ b/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/rule/step/DataByDataSchemeBuilder.kt @@ -82,6 +82,6 @@ internal sealed interface DataBuildErrors : BasicRulesEngineError { } private companion object { - private const val PREFIX = "DATA-BUILD-" + private const val PREFIX = "DATA-BUILD-ERROR-" } } diff --git a/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/rule/step/DataChangeTrackingStepExecutor.kt b/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/rule/step/DataChangeTrackingStepExecutor.kt index 9cb4fb83..3bb19b55 100644 --- a/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/rule/step/DataChangeTrackingStepExecutor.kt +++ b/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/rule/step/DataChangeTrackingStepExecutor.kt @@ -1,11 +1,12 @@ package io.github.ustudiocompany.uframework.rulesengine.core.rule.step -import io.github.airflux.commons.types.maybe.Maybe +import io.github.airflux.commons.types.maybe.MaybeBiFailure import io.github.airflux.commons.types.maybe.map import io.github.airflux.commons.types.maybe.maybeFailure -import io.github.airflux.commons.types.resultk.mapFailure +import io.github.airflux.commons.types.resultk.mapFailureToError import io.github.ustudiocompany.uframework.failure.Failure import io.github.ustudiocompany.uframework.rulesengine.core.BasicRulesEngineError +import io.github.ustudiocompany.uframework.rulesengine.core.BasicRulesEngineIncident import io.github.ustudiocompany.uframework.rulesengine.core.context.Context import io.github.ustudiocompany.uframework.rulesengine.core.env.EnvVars import io.github.ustudiocompany.uframework.rulesengine.executor.DataChangeTrackerProvider @@ -14,19 +15,22 @@ internal fun DataChangeTrackingStep.execute( envVars: EnvVars, context: Context, dataChangeTrackerProvider: DataChangeTrackerProvider, -): Maybe { +): MaybeBiFailure { val step = this return maybeFailure { val (args) = step.buildArgs(envVars, context) val uri = DataChangeTrackerProvider.Uiss.from(step.uri.get) dataChangeTrackerProvider.prepare(uri, args) - .map { failure -> DataChangeTrackingStepExecutionErrors.Preparing(failure) } + .map( + onError = { error -> DataChangeTrackingStepExecutionErrors.Preparing(error) }, + onException = { incident -> DataChangeTrackingStepExecutionIncident.Preparing(cause = incident) } + ) } } private fun DataChangeTrackingStep.buildArgs(envVars: EnvVars, context: Context) = args.build(envVars, context) { name, value -> DataChangeTrackerProvider.Arg(name, value) } - .mapFailure { failure -> DataChangeTrackingStepExecutionErrors.ArgBuild(failure) } + .mapFailureToError { failure -> DataChangeTrackingStepExecutionErrors.ArgBuild(cause = failure) } internal sealed interface DataChangeTrackingStepExecutionErrors : BasicRulesEngineError { @@ -43,6 +47,19 @@ internal sealed interface DataChangeTrackingStepExecutionErrors : BasicRulesEngi } private companion object { - private const val PREFIX = "DATA-CHANGE-TRACKING-STEP-EXECUTION-" + private const val PREFIX = "DATA-CHANGE-TRACKING-STEP-EXECUTION-ERROR-" + } +} + +internal sealed interface DataChangeTrackingStepExecutionIncident : BasicRulesEngineIncident { + + class Preparing(cause: DataChangeTrackerProvider.Incident) : DataChangeTrackingStepExecutionIncident { + override val code: String = PREFIX + "1" + override val description: String = "Error preparing to track data changes." + override val cause: Failure.Cause = Failure.Cause.Failure(cause) + } + + private companion object { + private const val PREFIX = "DATA-CHANGE-TRACKING-STEP-EXECUTION-INCIDENT-" } } diff --git a/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/rule/step/DataRetrieveStepExecutor.kt b/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/rule/step/DataRetrieveStepExecutor.kt index 4d7550cb..6ecfde6c 100644 --- a/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/rule/step/DataRetrieveStepExecutor.kt +++ b/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/rule/step/DataRetrieveStepExecutor.kt @@ -1,14 +1,17 @@ package io.github.ustudiocompany.uframework.rulesengine.core.rule.step -import io.github.airflux.commons.types.maybe.Maybe +import io.github.airflux.commons.types.maybe.MaybeBiFailure import io.github.airflux.commons.types.maybe.map import io.github.airflux.commons.types.maybe.maybeFailure import io.github.airflux.commons.types.resultk.mapFailure +import io.github.airflux.commons.types.resultk.mapFailureToError import io.github.ustudiocompany.uframework.failure.Failure import io.github.ustudiocompany.uframework.json.element.JsonElement import io.github.ustudiocompany.uframework.rulesengine.core.BasicRulesEngineError +import io.github.ustudiocompany.uframework.rulesengine.core.BasicRulesEngineIncident import io.github.ustudiocompany.uframework.rulesengine.core.context.Context -import io.github.ustudiocompany.uframework.rulesengine.core.context.ContextUpdateErrors +import io.github.ustudiocompany.uframework.rulesengine.core.context.ContextErrors +import io.github.ustudiocompany.uframework.rulesengine.core.context.ContextIncident import io.github.ustudiocompany.uframework.rulesengine.core.context.update import io.github.ustudiocompany.uframework.rulesengine.core.env.EnvVars import io.github.ustudiocompany.uframework.rulesengine.executor.DataProvider @@ -19,24 +22,30 @@ internal fun DataRetrieveStep.execute( context: Context, dataProvider: DataProvider, merger: Merger -): Maybe { +): MaybeBiFailure { val step = this return maybeFailure { - val (args) = step.buildArgs(envVars, context) - val uri = DataProvider.Uri.from(step.uri.get) + val (args: List) = step.buildArgs(envVars, context) + val uri: DataProvider.Uri = DataProvider.Uri.from(step.uri.get) val (value) = dataProvider.get(uri, args) - .mapFailure { failure -> DataRetrieveStepExecutionErrors.ExternalDataRetrieval(failure) } + .mapFailure( + onError = { error -> DataRetrieveStepExecutionErrors.ExternalDataRetrieval(error) }, + onException = { incident -> DataRetrieveStepExecutionIncident.ExternalDataRetrieval(cause = incident) } + ) context.update(value, step.result, merger) } } private fun DataRetrieveStep.buildArgs(envVars: EnvVars, context: Context) = args.build(envVars, context) { name, value -> DataProvider.Arg(name, value) } - .mapFailure { failure -> DataRetrieveStepExecutionErrors.ArgBuild(failure) } + .mapFailureToError { failure -> DataRetrieveStepExecutionErrors.ArgBuild(cause = failure) } private fun Context.update(value: JsonElement, result: StepResult, merger: Merger) = update(result.source, result.action, value, merger) - .map { failure -> DataRetrieveStepExecutionErrors.ContextUpdate(failure) } + .map( + onError = { error -> DataRetrieveStepExecutionErrors.ResultApply(error) }, + onException = { incident -> DataRetrieveStepExecutionIncident.ResultApply(cause = incident) } + ) internal sealed interface DataRetrieveStepExecutionErrors : BasicRulesEngineError { @@ -52,13 +61,32 @@ internal sealed interface DataRetrieveStepExecutionErrors : BasicRulesEngineErro override val cause: Failure.Cause = Failure.Cause.Failure(cause) } - class ContextUpdate(cause: ContextUpdateErrors) : DataRetrieveStepExecutionErrors { + class ResultApply(cause: ContextErrors) : DataRetrieveStepExecutionErrors { override val code: String = PREFIX + "3" - override val description: String = "Error updating context in the 'Data Retrieve' step." + override val description: String = "Error of processing the result of the 'Data Retrieve' step." override val cause: Failure.Cause = Failure.Cause.Failure(cause) } private companion object { - private const val PREFIX = "DATA-BUILD-STEP-EXECUTION-" + private const val PREFIX = "DATA-BUILD-STEP-EXECUTION-ERROR-" + } +} + +internal sealed interface DataRetrieveStepExecutionIncident : BasicRulesEngineIncident { + + class ExternalDataRetrieval(cause: DataProvider.Incident) : DataRetrieveStepExecutionIncident { + override val code: String = PREFIX + "1" + override val description: String = "Incident retrieving external data in the 'Data Retrieve' step." + override val cause: Failure.Cause = Failure.Cause.Failure(cause) + } + + class ResultApply(cause: ContextIncident) : DataRetrieveStepExecutionIncident { + override val code: String = PREFIX + "2" + override val description: String = "Incident of processing the result of the 'Data Retrieve' step." + override val cause: Failure.Cause = Failure.Cause.Failure(cause) + } + + private companion object { + private const val PREFIX = "DATA-BUILD-STEP-EXECUTION-INCIDENT-" } } diff --git a/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/rule/step/HttpCallStepExecutor.kt b/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/rule/step/HttpCallStepExecutor.kt index 6f9f1058..68122422 100644 --- a/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/rule/step/HttpCallStepExecutor.kt +++ b/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/rule/step/HttpCallStepExecutor.kt @@ -1,16 +1,21 @@ package io.github.ustudiocompany.uframework.rulesengine.core.rule.step +import io.github.airflux.commons.types.fail.asError import io.github.airflux.commons.types.maybe.Maybe +import io.github.airflux.commons.types.maybe.MaybeBiFailure import io.github.airflux.commons.types.maybe.map import io.github.airflux.commons.types.maybe.maybeFailure import io.github.airflux.commons.types.resultk.ResultK import io.github.airflux.commons.types.resultk.map2 import io.github.airflux.commons.types.resultk.mapFailure +import io.github.airflux.commons.types.resultk.mapFailureToError import io.github.ustudiocompany.uframework.failure.Failure import io.github.ustudiocompany.uframework.json.element.JsonElement import io.github.ustudiocompany.uframework.rulesengine.core.BasicRulesEngineError +import io.github.ustudiocompany.uframework.rulesengine.core.BasicRulesEngineIncident import io.github.ustudiocompany.uframework.rulesengine.core.context.Context -import io.github.ustudiocompany.uframework.rulesengine.core.context.ContextUpdateErrors +import io.github.ustudiocompany.uframework.rulesengine.core.context.ContextErrors +import io.github.ustudiocompany.uframework.rulesengine.core.context.ContextIncident import io.github.ustudiocompany.uframework.rulesengine.core.context.update import io.github.ustudiocompany.uframework.rulesengine.core.env.EnvVars import io.github.ustudiocompany.uframework.rulesengine.core.rule.ValueComputationErrors @@ -23,27 +28,30 @@ internal fun HttpCallStep.execute( context: Context, httpCallProvider: HttpCallProvider, merger: Merger -): Maybe { +): MaybeBiFailure { val step = this return maybeFailure { val uri = HttpCallProvider.Uri.from(step.uri.get) val (args) = step.buildArgs(envVars, context) val (body) = step.buildBody(envVars, context) val (value) = httpCallProvider.call(uri, args, body) - .mapFailure { failure -> HttpCallStepExecutionErrors.Call(failure) } + .mapFailure( + onError = { error -> HttpCallStepExecutionErrors.Call(error) }, + onException = { incident -> HttpCallStepExecutionIncident.Call(cause = incident) } + ) context.update(value, step.result, merger) } } private fun HttpCallStep.buildArgs(envVars: EnvVars, context: Context) = args.build(envVars, context) { name, value -> HttpCallProvider.Arg(name, value) } - .mapFailure { failure -> HttpCallStepExecutionErrors.ArgBuild(failure) } + .mapFailureToError { failure -> HttpCallStepExecutionErrors.ArgBuild(cause = failure) } private fun HttpCallStep.buildBody(envVars: EnvVars, context: Context) = body?.compute(envVars, context) ?.map2( onSuccess = { value -> HttpCallProvider.Body(value) }, - onFailure = { failure -> HttpCallStepExecutionErrors.BodyBuild(failure) } + onFailure = { error -> HttpCallStepExecutionErrors.BodyBuild(error).asError() } ) ?: ResultK.Success.asNull @@ -51,12 +59,15 @@ private fun Context.update( value: JsonElement?, result: StepResult?, merger: Merger -): Maybe = +): MaybeBiFailure = when { result != null && value != null -> update(result.source, result.action, value, merger) - .map { failure -> HttpCallStepExecutionErrors.ContextUpdate(failure) } + .map( + onError = { error -> HttpCallStepExecutionErrors.ResultApply(error) }, + onException = { incident -> HttpCallStepExecutionIncident.ResultApply(cause = incident) } + ) - result != null && value == null -> Maybe.some(HttpCallStepExecutionErrors.ExpectedResponseMissing()) + result != null && value == null -> Maybe.some(HttpCallStepExecutionErrors.ExpectedResponseMissing().asError()) result == null && value != null -> Maybe.none() else -> Maybe.none() } @@ -87,13 +98,32 @@ internal sealed interface HttpCallStepExecutionErrors : BasicRulesEngineError { override val cause: Failure.Cause = Failure.Cause.None } - class ContextUpdate(cause: ContextUpdateErrors) : HttpCallStepExecutionErrors { + class ResultApply(cause: ContextErrors) : HttpCallStepExecutionErrors { override val code: String = PREFIX + "5" - override val description: String = "Error updating context in the 'HTTP Call' step." + override val description: String = "Error of processing the result of the 'HTTP Call' step." override val cause: Failure.Cause = Failure.Cause.Failure(cause) } private companion object { - private const val PREFIX = "HTTP-CALL-STEP-EXECUTION-" + private const val PREFIX = "HTTP-CALL-STEP-EXECUTION-ERROR-" + } +} + +internal sealed interface HttpCallStepExecutionIncident : BasicRulesEngineIncident { + + class Call(cause: HttpCallProvider.Incident) : HttpCallStepExecutionIncident { + override val code: String = PREFIX + "1" + override val description: String = "Incident HTTP calling the 'HTTP Call' step." + override val cause: Failure.Cause = Failure.Cause.Failure(cause) + } + + class ResultApply(cause: ContextIncident) : HttpCallStepExecutionIncident { + override val code: String = PREFIX + "2" + override val description: String = "Incident of processing the result of the 'HTTP Call' step." + override val cause: Failure.Cause = Failure.Cause.Failure(cause) + } + + private companion object { + private const val PREFIX = "HTTP-CALL-STEP-EXECUTION-INCIDENT-" } } diff --git a/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/rule/step/MessageHeadersBuilder.kt b/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/rule/step/MessageHeadersBuilder.kt index 53bda033..5627c128 100644 --- a/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/rule/step/MessageHeadersBuilder.kt +++ b/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/rule/step/MessageHeadersBuilder.kt @@ -40,6 +40,6 @@ internal sealed interface MessageHeaderBuildErrors : BasicRulesEngineError { } private companion object { - private const val PREFIX = "MESSAGE-HEADER-BUILD-" + private const val PREFIX = "MESSAGE-HEADER-BUILD-ERROR-" } } diff --git a/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/rule/step/MessagePublishStepExecutor.kt b/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/rule/step/MessagePublishStepExecutor.kt index 4521bd6b..0910118f 100644 --- a/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/rule/step/MessagePublishStepExecutor.kt +++ b/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/rule/step/MessagePublishStepExecutor.kt @@ -1,13 +1,15 @@ package io.github.ustudiocompany.uframework.rulesengine.core.rule.step -import io.github.airflux.commons.types.maybe.Maybe +import io.github.airflux.commons.types.fail.asError +import io.github.airflux.commons.types.maybe.MaybeBiFailure import io.github.airflux.commons.types.maybe.map import io.github.airflux.commons.types.maybe.maybeFailure import io.github.airflux.commons.types.resultk.ResultK import io.github.airflux.commons.types.resultk.map2 -import io.github.airflux.commons.types.resultk.mapFailure +import io.github.airflux.commons.types.resultk.mapFailureToError import io.github.ustudiocompany.uframework.failure.Failure import io.github.ustudiocompany.uframework.rulesengine.core.BasicRulesEngineError +import io.github.ustudiocompany.uframework.rulesengine.core.BasicRulesEngineIncident import io.github.ustudiocompany.uframework.rulesengine.core.context.Context import io.github.ustudiocompany.uframework.rulesengine.core.data.toStringValue import io.github.ustudiocompany.uframework.rulesengine.core.env.EnvVars @@ -19,14 +21,17 @@ internal fun MessagePublishStep.execute( envVars: EnvVars, context: Context, messagePublisher: MessagePublisher -): Maybe { +): MaybeBiFailure { val step = this return maybeFailure { val (routeKey) = step.buildRouteKey(envVars, context) val (headers) = step.buildHeaders(envVars, context) val (body) = step.buildBody(envVars, context) messagePublisher.publish(routeKey, headers, body) - .map { failure -> MessagePublishStepExecutionErrors.Publish(failure) } + .map( + onError = { error -> MessagePublishStepExecutionErrors.Publish(error) }, + onException = { incident -> MessagePublishStepExecutionIncident.Publish(cause = incident) } + ) } } @@ -34,19 +39,19 @@ private fun MessagePublishStep.buildRouteKey(envVars: EnvVars, context: Context) routeKey?.compute(envVars, context) ?.map2( onSuccess = { value -> value.toStringValue() }, - onFailure = { failure -> MessagePublishStepExecutionErrors.RouteKeyBuild(failure) } + onFailure = { error -> MessagePublishStepExecutionErrors.RouteKeyBuild(error).asError() } ) ?: ResultK.Success.asNull private fun MessagePublishStep.buildHeaders(envVars: EnvVars, context: Context) = headers.build(envVars, context) { name, value -> MessagePublisher.Header(name, value) } - .mapFailure { failure -> MessagePublishStepExecutionErrors.HeadersBuild(cause = failure) } + .mapFailureToError { failure -> MessagePublishStepExecutionErrors.HeadersBuild(cause = failure) } private fun MessagePublishStep.buildBody(envVars: EnvVars, context: Context) = body?.compute(envVars, context) ?.map2( onSuccess = { value -> value.toStringValue() }, - onFailure = { failure -> MessagePublishStepExecutionErrors.BodyBuild(failure) } + onFailure = { failure -> MessagePublishStepExecutionErrors.BodyBuild(cause = failure).asError() } ) ?: ResultK.Success.asNull @@ -77,6 +82,19 @@ internal sealed interface MessagePublishStepExecutionErrors : BasicRulesEngineEr } private companion object { - private const val PREFIX = "MESSAGE-PUBLISH-STEP-EXECUTION-" + private const val PREFIX = "MESSAGE-PUBLISH-STEP-EXECUTION-ERROR-" + } +} + +internal sealed interface MessagePublishStepExecutionIncident : BasicRulesEngineIncident { + + class Publish(cause: MessagePublisher.Incident) : MessagePublishStepExecutionIncident { + override val code: String = PREFIX + "1" + override val description: String = "Error publishing message in the 'Message Publish' step." + override val cause: Failure.Cause = Failure.Cause.Failure(cause) + } + + private companion object { + private const val PREFIX = "MESSAGE-PUBLISH-STEP-EXECUTION-INCIDENT-" } } diff --git a/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/rule/step/ValidationStepExecutor.kt b/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/rule/step/ValidationStepExecutor.kt index 0b307d73..f0b11d86 100644 --- a/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/rule/step/ValidationStepExecutor.kt +++ b/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/rule/step/ValidationStepExecutor.kt @@ -17,7 +17,7 @@ internal fun ValidationStep.execute( context: Context ): ResultK = calculate(envVars, context) - .mapFailure { failure -> ValidationStepExecutingError.OperationCalculation(failure) } + .mapFailure { failure -> ValidationStepExecutingError.OperationCalculation(cause = failure) } .flatMapBoolean( ifTrue = { Success.asNull }, ifFalse = { errorCode.asSuccess() } @@ -32,6 +32,6 @@ internal sealed interface ValidationStepExecutingError : BasicRulesEngineError { } private companion object { - private const val PREFIX = "VALIDATION-STEP-EXECUTION-" + private const val PREFIX = "VALIDATION-STEP-EXECUTION-ERROR-" } } diff --git a/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/executor/DataChangeTrackerProvider.kt b/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/executor/DataChangeTrackerProvider.kt index a070bcda..605576e5 100644 --- a/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/executor/DataChangeTrackerProvider.kt +++ b/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/executor/DataChangeTrackerProvider.kt @@ -1,14 +1,15 @@ package io.github.ustudiocompany.uframework.rulesengine.executor -import io.github.airflux.commons.types.maybe.Maybe +import io.github.airflux.commons.types.maybe.MaybeBiFailure import io.github.ustudiocompany.uframework.failure.Failure import io.github.ustudiocompany.uframework.rulesengine.core.BasicRulesEngineError +import io.github.ustudiocompany.uframework.rulesengine.core.BasicRulesEngineIncident import java.net.URLEncoder import kotlin.text.Charsets.UTF_8 public fun interface DataChangeTrackerProvider { - public fun prepare(uiss: Uiss, args: List): Maybe + public fun prepare(uiss: Uiss, args: List): MaybeBiFailure @JvmInline public value class Uiss private constructor(public val get: String) { @@ -36,10 +37,46 @@ public fun interface DataChangeTrackerProvider { ) public constructor(cause: Failure) : + this(cause = Failure.Cause.Failure(cause)) + + public constructor( + message: String, + exception: Throwable? = null, + details: Failure.Details = Failure.Details.NONE + ) : this( - cause = Failure.Cause.Failure(cause) + message = message, + cause = if (exception != null) + Failure.Cause.Exception(exception) + else + Failure.Cause.None, + details = details ) + override val code: String = PREFIX + "1" + override val description: String = + "The error of preparing to track data changes." + if (message.isNotEmpty()) " $message" else "" + + private companion object { + private const val PREFIX = "DATA-CHANGE-TRACKER-PROVIDER-ERROR-" + } + } + + public class Incident private constructor( + message: String = "", + override val cause: Failure.Cause, + override val details: Failure.Details = Failure.Details.NONE + ) : BasicRulesEngineIncident { + + public constructor() : this( + message = "", + cause = Failure.Cause.None, + details = Failure.Details.NONE + ) + + public constructor(cause: Failure) : + this(cause = Failure.Cause.Failure(cause)) + public constructor( message: String, exception: Throwable? = null, @@ -56,10 +93,10 @@ public fun interface DataChangeTrackerProvider { override val code: String = PREFIX + "1" override val description: String = - "The error of preparing to track data changes." + if (message.isNotEmpty()) " $message" else "" + "The incident of preparing to track data changes." + if (message.isNotEmpty()) " $message" else "" private companion object { - private const val PREFIX = "DATA-CHANGE-TRACKER-PROVIDER-" + private const val PREFIX = "DATA-CHANGE-TRACKER-PROVIDER-INCIDENT-" } } } diff --git a/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/executor/DataProvider.kt b/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/executor/DataProvider.kt index 55ac2e20..2e238884 100644 --- a/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/executor/DataProvider.kt +++ b/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/executor/DataProvider.kt @@ -1,15 +1,17 @@ package io.github.ustudiocompany.uframework.rulesengine.executor +import io.github.airflux.commons.types.fail.Fail import io.github.airflux.commons.types.resultk.ResultK import io.github.ustudiocompany.uframework.failure.Failure import io.github.ustudiocompany.uframework.json.element.JsonElement import io.github.ustudiocompany.uframework.rulesengine.core.BasicRulesEngineError +import io.github.ustudiocompany.uframework.rulesengine.core.BasicRulesEngineIncident import java.net.URLEncoder import kotlin.text.Charsets.UTF_8 public fun interface DataProvider { - public fun get(uri: Uri, args: List): ResultK + public fun get(uri: Uri, args: List): ResultK> @JvmInline public value class Uri private constructor(public val get: String) { @@ -37,10 +39,46 @@ public fun interface DataProvider { ) public constructor(cause: Failure) : + this(cause = Failure.Cause.Failure(cause)) + + public constructor( + message: String, + exception: Throwable? = null, + details: Failure.Details = Failure.Details.NONE + ) : this( - cause = Failure.Cause.Failure(cause) + message = message, + cause = if (exception != null) + Failure.Cause.Exception(exception) + else + Failure.Cause.None, + details = details ) + override val code: String = PREFIX + "1" + override val description: String = + "The error of getting data." + if (message.isNotEmpty()) " $message" else "" + + private companion object { + private const val PREFIX = "DATA-PROVIDER-ERROR-" + } + } + + public class Incident private constructor( + message: String = "", + override val cause: Failure.Cause, + override val details: Failure.Details = Failure.Details.NONE + ) : BasicRulesEngineIncident { + + public constructor() : this( + message = "", + cause = Failure.Cause.None, + details = Failure.Details.NONE + ) + + public constructor(cause: Failure) : + this(cause = Failure.Cause.Failure(cause)) + public constructor( message: String, exception: Throwable? = null, @@ -57,10 +95,10 @@ public fun interface DataProvider { override val code: String = PREFIX + "1" override val description: String = - "The error of getting data." + if (message.isNotEmpty()) " $message" else "" + "The incident of getting data." + if (message.isNotEmpty()) " $message" else "" private companion object { - private const val PREFIX = "DATA-PROVIDER-" + private const val PREFIX = "DATA-PROVIDER-INCIDENT-" } } } diff --git a/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/executor/HttpCallProvider.kt b/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/executor/HttpCallProvider.kt index ab22e6b3..1d5c1d0b 100644 --- a/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/executor/HttpCallProvider.kt +++ b/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/executor/HttpCallProvider.kt @@ -1,16 +1,18 @@ package io.github.ustudiocompany.uframework.rulesengine.executor +import io.github.airflux.commons.types.fail.Fail import io.github.airflux.commons.types.resultk.ResultK import io.github.ustudiocompany.uframework.failure.Failure import io.github.ustudiocompany.uframework.json.element.JsonElement import io.github.ustudiocompany.uframework.rulesengine.core.BasicRulesEngineError +import io.github.ustudiocompany.uframework.rulesengine.core.BasicRulesEngineIncident import io.github.ustudiocompany.uframework.rulesengine.core.data.toStringValue import java.net.URLEncoder import kotlin.text.Charsets.UTF_8 public fun interface HttpCallProvider { - public fun call(uri: Uri, args: List, body: Body?): ResultK + public fun call(uri: Uri, args: List, body: Body?): ResultK> @JvmInline public value class Uri private constructor(public val get: String) { @@ -67,7 +69,51 @@ public fun interface HttpCallProvider { "The error of http call." + if (message.isNotEmpty()) " $message" else "" private companion object { - private const val PREFIX = "CALL-PROVIDER-" + private const val PREFIX = "HTTP-CALL-PROVIDER-ERROR-" + + private fun Throwable?.toFailureCause(): Failure.Cause = + this?.let { Failure.Cause.Exception(it) } ?: Failure.Cause.None + } + } + + public class Incident private constructor( + message: String = "", + override val cause: Failure.Cause = Failure.Cause.None, + override val details: Failure.Details = Failure.Details.NONE + ) : BasicRulesEngineIncident { + + public constructor() : this( + message = "", + cause = Failure.Cause.None, + details = Failure.Details.NONE + ) + + public constructor( + message: String = "", + cause: Failure, + details: Failure.Details = Failure.Details.NONE + ) : this( + message = message, + cause = Failure.Cause.Failure(cause), + details = details + ) + + public constructor( + message: String = "", + exception: Throwable? = null, + details: Failure.Details = Failure.Details.NONE + ) : this( + message = message, + cause = exception.toFailureCause(), + details = details + ) + + override val code: String = PREFIX + "1" + override val description: String = + "The incident of http call." + if (message.isNotEmpty()) " $message" else "" + + private companion object { + private const val PREFIX = "HTTP-CALL-PROVIDER-INCIDENT-" private fun Throwable?.toFailureCause(): Failure.Cause = this?.let { Failure.Cause.Exception(it) } ?: Failure.Cause.None diff --git a/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/executor/Merger.kt b/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/executor/Merger.kt index 0916ac41..47f43d64 100644 --- a/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/executor/Merger.kt +++ b/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/executor/Merger.kt @@ -1,9 +1,11 @@ package io.github.ustudiocompany.uframework.rulesengine.executor +import io.github.airflux.commons.types.fail.Fail import io.github.airflux.commons.types.resultk.ResultK import io.github.ustudiocompany.uframework.failure.Failure import io.github.ustudiocompany.uframework.json.element.JsonElement import io.github.ustudiocompany.uframework.rulesengine.core.BasicRulesEngineError +import io.github.ustudiocompany.uframework.rulesengine.core.BasicRulesEngineIncident import io.github.ustudiocompany.uframework.rulesengine.core.rule.step.StepResult public fun interface Merger { @@ -12,7 +14,7 @@ public fun interface Merger { strategyCode: StepResult.Action.Merge.StrategyCode, dst: JsonElement, src: JsonElement - ): ResultK + ): ResultK> public class Error private constructor( message: String = "", @@ -27,10 +29,46 @@ public fun interface Merger { ) public constructor(cause: Failure) : + this(cause = Failure.Cause.Failure(cause)) + + public constructor( + message: String, + exception: Throwable? = null, + details: Failure.Details = Failure.Details.NONE + ) : this( - cause = Failure.Cause.Failure(cause) + message = message, + cause = if (exception != null) + Failure.Cause.Exception(exception) + else + Failure.Cause.None, + details = details ) + override val code: String = PREFIX + "1" + override val description: String = + "The error of merging data." + if (message.isNotEmpty()) " $message" else "" + + private companion object { + private const val PREFIX = "DATA-MERGE-ERROR-" + } + } + + public class Incident private constructor( + message: String = "", + override val cause: Failure.Cause, + override val details: Failure.Details = Failure.Details.NONE + ) : BasicRulesEngineIncident { + + public constructor() : this( + message = "", + cause = Failure.Cause.None, + details = Failure.Details.NONE + ) + + public constructor(cause: Failure) : + this(cause = Failure.Cause.Failure(cause)) + public constructor( message: String, exception: Throwable? = null, @@ -47,10 +85,10 @@ public fun interface Merger { override val code: String = PREFIX + "1" override val description: String = - "The error of merging data." + if (message.isNotEmpty()) " $message" else "" + "The incident of merging data." + if (message.isNotEmpty()) " $message" else "" private companion object { - private const val PREFIX = "DATA-MERGE-" + private const val PREFIX = "DATA-MERGE-INCIDENT-" } } } diff --git a/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/executor/MessagePublisher.kt b/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/executor/MessagePublisher.kt index 78c9858b..fb31b84b 100644 --- a/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/executor/MessagePublisher.kt +++ b/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/executor/MessagePublisher.kt @@ -1,12 +1,13 @@ package io.github.ustudiocompany.uframework.rulesengine.executor -import io.github.airflux.commons.types.maybe.Maybe +import io.github.airflux.commons.types.maybe.MaybeBiFailure import io.github.ustudiocompany.uframework.failure.Failure import io.github.ustudiocompany.uframework.rulesengine.core.BasicRulesEngineError +import io.github.ustudiocompany.uframework.rulesengine.core.BasicRulesEngineIncident public fun interface MessagePublisher { - public fun publish(routeKey: String?, headers: List
, body: String?): Maybe + public fun publish(routeKey: String?, headers: List
, body: String?): MaybeBiFailure public data class Header( public val name: String, @@ -47,7 +48,45 @@ public fun interface MessagePublisher { "The error of publishing a message." + if (message.isNotEmpty()) " $message" else "" private companion object { - private const val PREFIX = "MESSAGE-PUBLISHER-" + private const val PREFIX = "MESSAGE-PUBLISHER-ERROR-" + } + } + + public class Incident private constructor( + message: String = "", + override val cause: Failure.Cause, + override val details: Failure.Details = Failure.Details.NONE + ) : BasicRulesEngineIncident { + + public constructor() : this( + message = "", + cause = Failure.Cause.None, + details = Failure.Details.NONE + ) + + public constructor(cause: Failure) : + this(cause = Failure.Cause.Failure(cause)) + + public constructor( + message: String, + exception: Throwable? = null, + details: Failure.Details = Failure.Details.NONE + ) : + this( + message = message, + cause = if (exception != null) + Failure.Cause.Exception(exception) + else + Failure.Cause.None, + details = details + ) + + override val code: String = PREFIX + "1" + override val description: String = + "The incident of publishing a message." + if (message.isNotEmpty()) " $message" else "" + + private companion object { + private const val PREFIX = "MESSAGE-PUBLISHER-INCIDENT-" } } } diff --git a/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/executor/RulesEngineExecutor.kt b/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/executor/RulesEngineExecutor.kt index 7741f97d..cb3192ec 100644 --- a/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/executor/RulesEngineExecutor.kt +++ b/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/executor/RulesEngineExecutor.kt @@ -1,5 +1,6 @@ package io.github.ustudiocompany.uframework.rulesengine.executor +import io.github.airflux.commons.types.fail.Fail import io.github.airflux.commons.types.maybe.map import io.github.airflux.commons.types.maybe.toResultAsFailureOr import io.github.airflux.commons.types.resultk.ResultK @@ -7,6 +8,7 @@ import io.github.airflux.commons.types.resultk.Success import io.github.airflux.commons.types.resultk.flatMapBoolean import io.github.airflux.commons.types.resultk.isFailure import io.github.airflux.commons.types.resultk.mapFailure +import io.github.airflux.commons.types.resultk.mapFailureToError import io.github.ustudiocompany.uframework.json.element.JsonElement import io.github.ustudiocompany.uframework.rulesengine.core.context.Context import io.github.ustudiocompany.uframework.rulesengine.core.env.EnvVarName @@ -25,10 +27,12 @@ import io.github.ustudiocompany.uframework.rulesengine.core.rule.step.Validation import io.github.ustudiocompany.uframework.rulesengine.core.rule.step.execute import io.github.ustudiocompany.uframework.rulesengine.executor.error.RuleExecutionErrors import io.github.ustudiocompany.uframework.rulesengine.executor.error.StepExecutionErrors +import io.github.ustudiocompany.uframework.rulesengine.executor.incident.RuleExecutionIncident +import io.github.ustudiocompany.uframework.rulesengine.executor.incident.StepExecutionIncident import io.github.ustudiocompany.uframework.rulesengine.executor.rule.isApplicable import io.github.ustudiocompany.uframework.rulesengine.executor.rule.step.isApplicable -public typealias ExecutionResult = ResultK +public typealias ExecutionResult = ResultK> @Suppress("TooManyFunctions") public class RulesEngineExecutor( @@ -45,7 +49,7 @@ public class RulesEngineExecutor( private fun Rules.execute( envVars: EnvVars, context: Context - ): ResultK { + ): ResultK> { for (rule in get) { val result = rule.executeIfApplicable(envVars, context) if (result.isFailure() || result.value != null) return result @@ -59,7 +63,10 @@ public class RulesEngineExecutor( ifTrue = { val vars = envVars.append(RULE_ID to JsonElement.Text(id.get)) steps.execute(vars, context) - .mapFailure { failure -> RuleExecutionErrors.Execution(ruleId = id, cause = failure) } + .mapFailure( + onError = { error -> RuleExecutionErrors.Execution(ruleId = id, cause = error) }, + onException = { incident -> RuleExecutionIncident.Execution(ruleId = id, cause = incident) } + ) }, ifFalse = { Success.asNull } ) @@ -67,7 +74,7 @@ public class RulesEngineExecutor( private fun Steps.execute( envVars: EnvVars, context: Context - ): ResultK { + ): ResultK> { for (step in get) { val result = step.executeIfApplicable(envVars, context) if (result.isFailure() || result.value != null) return result @@ -75,7 +82,10 @@ public class RulesEngineExecutor( return Success.asNull } - private fun Step.executeIfApplicable(envVars: EnvVars, context: Context) = + private fun Step.executeIfApplicable( + envVars: EnvVars, + context: Context + ): ResultK> = isApplicable(envVars, context) .flatMapBoolean( ifTrue = { @@ -94,31 +104,46 @@ public class RulesEngineExecutor( private fun DataRetrieveStep.tryExecute(envVars: EnvVars, context: Context) = execute(envVars, context, dataProvider, merger) - .map { failure -> StepExecutionErrors.DataRetrieve(stepId = id, cause = failure) } + .map( + onError = { error -> StepExecutionErrors.DataRetrieve(stepId = id, cause = error) }, + onException = { incident -> StepExecutionIncident.DataRetrieve(stepId = id, cause = incident) } + ) .toResultAsFailureOr(ResultK.Success.asNull) private fun DataBuildStep.tryExecute(envVars: EnvVars, context: Context) = execute(envVars, context, merger) - .map { failure -> StepExecutionErrors.DataBuild(stepId = id, cause = failure) } + .map( + onError = { error -> StepExecutionErrors.DataBuild(stepId = id, cause = error) }, + onException = { incident -> StepExecutionIncident.DataBuild(stepId = id, cause = incident) } + ) .toResultAsFailureOr(ResultK.Success.asNull) private fun ValidationStep.tryExecute(envVars: EnvVars, context: Context) = execute(envVars, context) - .mapFailure { failure -> StepExecutionErrors.Validation(stepId = id, cause = failure) } + .mapFailureToError { failure -> StepExecutionErrors.Validation(stepId = id, cause = failure) } private fun MessagePublishStep.tryExecute(envVars: EnvVars, context: Context) = execute(envVars, context, messagePublisher) - .map { failure -> StepExecutionErrors.MessagePublish(stepId = id, cause = failure) } + .map( + onError = { error -> StepExecutionErrors.MessagePublish(stepId = id, cause = error) }, + onException = { incident -> StepExecutionIncident.MessagePublish(stepId = id, cause = incident) } + ) .toResultAsFailureOr(ResultK.Success.asNull) private fun DataChangeTrackingStep.tryExecute(envVars: EnvVars, context: Context) = execute(envVars, context, dataChangeTrackerProvider) - .map { failure -> StepExecutionErrors.DataChangeTracking(stepId = id, cause = failure) } + .map( + onError = { error -> StepExecutionErrors.DataChangeTracking(stepId = id, cause = error) }, + onException = { incident -> StepExecutionIncident.DataChangeTracking(stepId = id, cause = incident) } + ) .toResultAsFailureOr(ResultK.Success.asNull) private fun HttpCallStep.tryExecute(envVars: EnvVars, context: Context) = execute(envVars, context, httpCallProvider, merger) - .map { failure -> StepExecutionErrors.HttpCall(stepId = id, cause = failure) } + .map( + onError = { error -> StepExecutionErrors.HttpCall(stepId = id, cause = error) }, + onException = { incident -> StepExecutionIncident.HttpCall(stepId = id, cause = incident) } + ) .toResultAsFailureOr(ResultK.Success.asNull) private companion object { diff --git a/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/executor/error/RuleExecutionErrors.kt b/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/executor/error/RuleExecutionErrors.kt index c4ba9c15..263537d6 100644 --- a/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/executor/error/RuleExecutionErrors.kt +++ b/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/executor/error/RuleExecutionErrors.kt @@ -28,7 +28,7 @@ public sealed class RuleExecutionErrors(ruleId: RuleId) : BasicRulesEngineError } private companion object { - private const val PREFIX = "RULE-EXECUTION-" + private const val PREFIX = "RULE-EXECUTION-ERROR-" private const val RULE_ID = "rule-id" } } diff --git a/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/executor/error/StepExecutionErrors.kt b/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/executor/error/StepExecutionErrors.kt index e172e96c..ce92a4a3 100644 --- a/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/executor/error/StepExecutionErrors.kt +++ b/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/executor/error/StepExecutionErrors.kt @@ -80,7 +80,7 @@ public sealed class StepExecutionErrors(stepId: StepId) : BasicRulesEngineError } private companion object { - private const val PREFIX = "STEP-EXECUTION-" + private const val PREFIX = "STEP-EXECUTION-ERROR-" private const val STEP_ID = "step-id" } } diff --git a/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/executor/incident/RuleExecutionIncident.kt b/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/executor/incident/RuleExecutionIncident.kt new file mode 100644 index 00000000..a82bc437 --- /dev/null +++ b/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/executor/incident/RuleExecutionIncident.kt @@ -0,0 +1,24 @@ +package io.github.ustudiocompany.uframework.rulesengine.executor.incident + +import io.github.ustudiocompany.uframework.failure.Failure +import io.github.ustudiocompany.uframework.rulesengine.core.BasicRulesEngineIncident +import io.github.ustudiocompany.uframework.rulesengine.core.rule.RuleId + +public sealed class RuleExecutionIncident(ruleId: RuleId) : BasicRulesEngineIncident { + + override val details: Failure.Details = Failure.Details.of(RULE_ID to ruleId.get) + + public class Execution internal constructor( + ruleId: RuleId, + cause: StepExecutionIncident + ) : RuleExecutionIncident(ruleId) { + override val code: String = PREFIX + "1" + override val description: String = "Incident execution the rule '${ruleId.get}'." + override val cause: Failure.Cause = Failure.Cause.Failure(cause) + } + + private companion object { + private const val PREFIX = "RULE-EXECUTION-INCIDENT-" + private const val RULE_ID = "rule-id" + } +} diff --git a/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/executor/incident/StepExecutionIncident.kt b/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/executor/incident/StepExecutionIncident.kt new file mode 100644 index 00000000..8ae19dc7 --- /dev/null +++ b/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/executor/incident/StepExecutionIncident.kt @@ -0,0 +1,65 @@ +package io.github.ustudiocompany.uframework.rulesengine.executor.incident + +import io.github.ustudiocompany.uframework.failure.Failure +import io.github.ustudiocompany.uframework.rulesengine.core.BasicRulesEngineIncident +import io.github.ustudiocompany.uframework.rulesengine.core.rule.step.DataBuildStepExecutionIncident +import io.github.ustudiocompany.uframework.rulesengine.core.rule.step.DataChangeTrackingStepExecutionIncident +import io.github.ustudiocompany.uframework.rulesengine.core.rule.step.DataRetrieveStepExecutionIncident +import io.github.ustudiocompany.uframework.rulesengine.core.rule.step.HttpCallStepExecutionIncident +import io.github.ustudiocompany.uframework.rulesengine.core.rule.step.MessagePublishStepExecutionIncident +import io.github.ustudiocompany.uframework.rulesengine.core.rule.step.StepId + +public sealed class StepExecutionIncident(stepId: StepId) : BasicRulesEngineIncident { + + override val details: Failure.Details = Failure.Details.of(STEP_ID to stepId.get) + + public class DataRetrieve internal constructor( + stepId: StepId, + cause: DataRetrieveStepExecutionIncident + ) : StepExecutionIncident(stepId) { + override val code: String = PREFIX + "1" + override val description: String = "Incident executing the 'Data Retrieve' step with id '${stepId.get}'." + override val cause: Failure.Cause = Failure.Cause.Failure(cause) + } + + public class DataBuild internal constructor( + stepId: StepId, + cause: DataBuildStepExecutionIncident + ) : StepExecutionIncident(stepId) { + override val code: String = PREFIX + "2" + override val description: String = "Incident executing the 'Data Build' step with id '${stepId.get}'." + override val cause: Failure.Cause = Failure.Cause.Failure(cause) + } + + public class MessagePublish internal constructor( + stepId: StepId, + cause: MessagePublishStepExecutionIncident + ) : StepExecutionIncident(stepId) { + override val code: String = PREFIX + "3" + override val description: String = "Incident executing the 'Message Publish' step with id '${stepId.get}'." + override val cause: Failure.Cause = Failure.Cause.Failure(cause) + } + + public class DataChangeTracking internal constructor( + stepId: StepId, + cause: DataChangeTrackingStepExecutionIncident + ) : StepExecutionIncident(stepId) { + override val code: String = PREFIX + "4" + override val description: String = "Incident executing the 'Data Change Tracking' step with id '${stepId.get}'." + override val cause: Failure.Cause = Failure.Cause.Failure(cause) + } + + public class HttpCall internal constructor( + stepId: StepId, + cause: HttpCallStepExecutionIncident + ) : StepExecutionIncident(stepId) { + override val code: String = PREFIX + "5" + override val description: String = "Incident executing the 'HTTP Call' step with id '${stepId.get}'." + override val cause: Failure.Cause = Failure.Cause.Failure(cause) + } + + private companion object { + private const val PREFIX = "STEP-EXECUTION-INCIDENT-" + private const val STEP_ID = "step-id" + } +} diff --git a/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/executor/rule/RuleExtensions.kt b/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/executor/rule/RuleExtensions.kt index cb517e33..c3525205 100644 --- a/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/executor/rule/RuleExtensions.kt +++ b/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/executor/rule/RuleExtensions.kt @@ -1,6 +1,6 @@ package io.github.ustudiocompany.uframework.rulesengine.executor.rule -import io.github.airflux.commons.types.resultk.mapFailure +import io.github.airflux.commons.types.resultk.mapFailureToError import io.github.ustudiocompany.uframework.rulesengine.core.context.Context import io.github.ustudiocompany.uframework.rulesengine.core.env.EnvVars import io.github.ustudiocompany.uframework.rulesengine.core.rule.Rule @@ -9,4 +9,4 @@ import io.github.ustudiocompany.uframework.rulesengine.executor.error.RuleExecut internal fun Rule.isApplicable(envVars: EnvVars, context: Context) = condition.isMet(envVars, context) - .mapFailure { failure -> RuleExecutionErrors.ConditionEvaluation(ruleId = id, cause = failure) } + .mapFailureToError { failure -> RuleExecutionErrors.ConditionEvaluation(ruleId = id, cause = failure) } diff --git a/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/executor/rule/step/StepExtensions.kt b/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/executor/rule/step/StepExtensions.kt index 93c431e3..ed79a187 100644 --- a/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/executor/rule/step/StepExtensions.kt +++ b/library/rules-engine/executor/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/executor/rule/step/StepExtensions.kt @@ -1,6 +1,6 @@ package io.github.ustudiocompany.uframework.rulesengine.executor.rule.step -import io.github.airflux.commons.types.resultk.mapFailure +import io.github.airflux.commons.types.resultk.mapFailureToError import io.github.ustudiocompany.uframework.rulesengine.core.context.Context import io.github.ustudiocompany.uframework.rulesengine.core.env.EnvVars import io.github.ustudiocompany.uframework.rulesengine.core.rule.condition.isMet @@ -9,4 +9,4 @@ import io.github.ustudiocompany.uframework.rulesengine.executor.error.StepExecut internal fun Step.isApplicable(envVars: EnvVars, context: Context) = condition.isMet(envVars, context) - .mapFailure { failure -> StepExecutionErrors.ConditionEvaluation(stepId = id, cause = failure) } + .mapFailureToError { failure -> StepExecutionErrors.ConditionEvaluation(stepId = id, cause = failure) } diff --git a/library/rules-engine/executor/src/test/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/context/GetDataFromContextTest.kt b/library/rules-engine/executor/src/test/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/context/GetDataFromContextTest.kt index 8e8ea107..3564319e 100644 --- a/library/rules-engine/executor/src/test/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/context/GetDataFromContextTest.kt +++ b/library/rules-engine/executor/src/test/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/context/GetDataFromContextTest.kt @@ -22,7 +22,7 @@ internal class GetDataFromContextTest : UnitTest() { "then function should return an error" { val result = context[SOURCE] result.shouldContainFailureInstance() - .shouldBeInstanceOf() + .shouldBeInstanceOf() } } @@ -35,7 +35,7 @@ internal class GetDataFromContextTest : UnitTest() { "then function should return an error" { val result = context[UNKNOWN_SOURCE] result.shouldContainFailureInstance() - .shouldBeInstanceOf() + .shouldBeInstanceOf() } } diff --git a/library/rules-engine/executor/src/test/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/context/PutDataToContextTest.kt b/library/rules-engine/executor/src/test/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/context/PutDataToContextTest.kt index b7323485..6c659e76 100644 --- a/library/rules-engine/executor/src/test/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/context/PutDataToContextTest.kt +++ b/library/rules-engine/executor/src/test/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/context/PutDataToContextTest.kt @@ -2,7 +2,7 @@ package io.github.ustudiocompany.uframework.rulesengine.core.context import io.github.airflux.commons.types.AirfluxTypesExperimental import io.github.airflux.commons.types.maybe.matcher.shouldBeNone -import io.github.airflux.commons.types.maybe.matcher.shouldContainSomeInstance +import io.github.airflux.commons.types.maybe.matcher.shouldContainErrorInstance import io.github.ustudiocompany.uframework.failure.Failure import io.github.ustudiocompany.uframework.json.element.JsonElement import io.github.ustudiocompany.uframework.rulesengine.core.rule.Source @@ -47,8 +47,8 @@ internal class PutDataToContextTest : UnitTest() { val result = context.put(SOURCE, newValue) "then function should return an error" { - result.shouldContainSomeInstance() - .shouldBeInstanceOf() + result.shouldContainErrorInstance() + .shouldBeInstanceOf() } "then the context should contain the source" { @@ -95,7 +95,7 @@ internal class PutDataToContextTest : UnitTest() { private sealed interface Errors : Failure { data object TestMergerError : Errors { - override val code: String = "MERGER_ERROR" + override val code: String = "MERGER-ERROR" } } } diff --git a/library/rules-engine/executor/src/test/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/context/ReplaceDataInContextTest.kt b/library/rules-engine/executor/src/test/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/context/ReplaceDataInContextTest.kt index 600d229c..1d054b24 100644 --- a/library/rules-engine/executor/src/test/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/context/ReplaceDataInContextTest.kt +++ b/library/rules-engine/executor/src/test/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/context/ReplaceDataInContextTest.kt @@ -2,7 +2,7 @@ package io.github.ustudiocompany.uframework.rulesengine.core.context import io.github.airflux.commons.types.AirfluxTypesExperimental import io.github.airflux.commons.types.maybe.matcher.shouldBeNone -import io.github.airflux.commons.types.maybe.matcher.shouldContainSomeInstance +import io.github.airflux.commons.types.maybe.matcher.shouldContainErrorInstance import io.github.ustudiocompany.uframework.failure.Failure import io.github.ustudiocompany.uframework.json.element.JsonElement import io.github.ustudiocompany.uframework.rulesengine.core.rule.Source @@ -24,8 +24,8 @@ internal class ReplaceDataInContextTest : UnitTest() { val result = context.replace(SOURCE, value) "then function should return an error" { - result.shouldContainSomeInstance() - .shouldBeInstanceOf() + result.shouldContainErrorInstance() + .shouldBeInstanceOf() } } @@ -60,8 +60,8 @@ internal class ReplaceDataInContextTest : UnitTest() { val result = context.replace(UNKNOWN_SOURCE, newValue) "then function should return an error" { - result.shouldContainSomeInstance() - .shouldBeInstanceOf() + result.shouldContainErrorInstance() + .shouldBeInstanceOf() } "then the context should not contain the source" { @@ -87,7 +87,7 @@ internal class ReplaceDataInContextTest : UnitTest() { private sealed interface Errors : Failure { data object TestMergerError : Errors { - override val code: String = "MERGER_ERROR" + override val code: String = "MERGER-ERROR" } } } diff --git a/library/rules-engine/executor/src/test/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/context/UpdateContextTest.kt b/library/rules-engine/executor/src/test/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/context/UpdateContextTest.kt index b12d002b..0e69db8b 100644 --- a/library/rules-engine/executor/src/test/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/context/UpdateContextTest.kt +++ b/library/rules-engine/executor/src/test/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/context/UpdateContextTest.kt @@ -1,8 +1,11 @@ package io.github.ustudiocompany.uframework.rulesengine.core.context import io.github.airflux.commons.types.AirfluxTypesExperimental +import io.github.airflux.commons.types.fail.asError +import io.github.airflux.commons.types.fail.asException import io.github.airflux.commons.types.maybe.matcher.shouldBeNone -import io.github.airflux.commons.types.maybe.matcher.shouldContainSomeInstance +import io.github.airflux.commons.types.maybe.matcher.shouldContainErrorInstance +import io.github.airflux.commons.types.maybe.matcher.shouldContainExceptionInstance import io.github.airflux.commons.types.resultk.asFailure import io.github.airflux.commons.types.resultk.asSuccess import io.github.ustudiocompany.uframework.json.element.JsonElement @@ -31,7 +34,7 @@ internal class UpdateContextTest : UnitTest() { source = SOURCE, action = action, value = value, - merge = { _, _, _ -> Merger.Error().asFailure() } + merge = { _, _, _ -> Merger.Error().asError().asFailure() } ) "then call the function should be successful" { @@ -47,12 +50,12 @@ internal class UpdateContextTest : UnitTest() { source = SOURCE, action = action, value = value, - merge = { _, _, _ -> Merger.Error().asFailure() } + merge = { _, _, _ -> Merger.Error().asError().asFailure() } ) "then call the function should be failed" { - result.shouldContainSomeInstance() - .shouldBeInstanceOf() + result.shouldContainErrorInstance() + .shouldBeInstanceOf() } } } @@ -67,8 +70,8 @@ internal class UpdateContextTest : UnitTest() { val result = context.update(source = SOURCE, action = action, value = value, merge = MERGER) "then call the function should be failed" { - result.shouldContainSomeInstance() - .shouldBeInstanceOf() + result.shouldContainErrorInstance() + .shouldBeInstanceOf() } } @@ -103,8 +106,8 @@ internal class UpdateContextTest : UnitTest() { val result = context.update(source = SOURCE, action = action, value = newValue, merge = MERGER) "then call the function should be failed" { - result.shouldContainSomeInstance() - .shouldBeInstanceOf() + result.shouldContainErrorInstance() + .shouldBeInstanceOf() } } @@ -130,17 +133,31 @@ internal class UpdateContextTest : UnitTest() { } } - "when the merge function is failed" - { + "when the merge function returns an error" - { val context = Context(sources = mapOf(SOURCE to JsonElement.Text(ORIGIN_VALUE))) val newValue = JsonElement.Text(NEW_VALUE) val result = context.update(source = SOURCE, action = action, value = newValue) { _, _, _ -> - Merger.Error().asFailure() + Merger.Error().asError().asFailure() } - "then call the function should be failed" { - result.shouldContainSomeInstance() - .shouldBeInstanceOf() + "then the function call should fail with an error" { + result.shouldContainErrorInstance() + .shouldBeInstanceOf() + } + } + + "when the merge function returns an incident" - { + val context = Context(sources = mapOf(SOURCE to JsonElement.Text(ORIGIN_VALUE))) + + val newValue = JsonElement.Text(NEW_VALUE) + val result = context.update(source = SOURCE, action = action, value = newValue) { _, _, _ -> + Merger.Incident().asException().asFailure() + } + + "then the function call should fail with an incident" { + result.shouldContainExceptionInstance() + .shouldBeInstanceOf() } } } diff --git a/library/rules-engine/executor/src/test/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/rule/ComputeDataStructValueTest.kt b/library/rules-engine/executor/src/test/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/rule/ComputeDataStructValueTest.kt new file mode 100644 index 00000000..28337e30 --- /dev/null +++ b/library/rules-engine/executor/src/test/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/rule/ComputeDataStructValueTest.kt @@ -0,0 +1,73 @@ +package io.github.ustudiocompany.uframework.rulesengine.core.rule + +import io.github.airflux.commons.types.AirfluxTypesExperimental +import io.github.airflux.commons.types.resultk.matcher.shouldBeSuccess +import io.github.airflux.commons.types.resultk.matcher.shouldContainFailureInstance +import io.github.ustudiocompany.uframework.json.element.JsonElement +import io.github.ustudiocompany.uframework.rulesengine.core.context.Context +import io.github.ustudiocompany.uframework.rulesengine.core.env.EnvVarName +import io.github.ustudiocompany.uframework.rulesengine.core.env.envVarsOf +import io.github.ustudiocompany.uframework.rulesengine.core.rule.step.DataSchema +import io.github.ustudiocompany.uframework.test.kotest.UnitTest +import io.kotest.matchers.types.shouldBeInstanceOf + +@OptIn(AirfluxTypesExperimental::class) +internal class ComputeDataStructValueTest : UnitTest() { + + init { + "Compute a value of the DataStruct type" - { + + "when success of building data struct" - { + val value = Value.DataStruct( + scheme = DataSchema.Struct( + properties = listOf( + DataSchema.Property.Element( + name = KEY, + value = Value.Literal(fact = TEXT_VALUE) + ) + ) + ) + ) + + "then the compute function should return a value" { + val result = value.compute(ENV_VARS, CONTEXT) + result shouldBeSuccess JsonElement.Struct(KEY to TEXT_VALUE) + } + } + + "when error of building data struct" - { + val value = Value.DataStruct( + scheme = DataSchema.Struct( + properties = listOf( + DataSchema.Property.Element( + name = KEY, + value = Value.EnvVars(name = UNKNOWN_ENV_VAR) + ) + ) + ) + ) + + "then the compute function should return a failure" { + val result = value.compute(ENV_VARS, CONTEXT) + result.shouldContainFailureInstance() + .shouldBeInstanceOf() + } + + "then the computeOrNull function should return a failure" { + val result = value.computeOrNull(ENV_VARS, CONTEXT) + result.shouldContainFailureInstance() + .shouldBeInstanceOf() + } + } + } + } + + companion object { + val ENV_VARS = envVarsOf() + val CONTEXT = Context.empty() + + private const val KEY = "id" + private val TEXT_VALUE = JsonElement.Text("value") + private val UNKNOWN_ENV_VAR = EnvVarName("unknown") + } +} diff --git a/library/rules-engine/executor/src/test/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/rule/ComputeEnvVarValueTest.kt b/library/rules-engine/executor/src/test/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/rule/ComputeEnvVarValueTest.kt index 0673eb4f..49636efd 100644 --- a/library/rules-engine/executor/src/test/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/rule/ComputeEnvVarValueTest.kt +++ b/library/rules-engine/executor/src/test/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/rule/ComputeEnvVarValueTest.kt @@ -15,7 +15,7 @@ internal class ComputeEnvVarValueTest : UnitTest() { init { - "when the value is the EnvVars type" - { + "Compute a value of the EnvVars type" - { "when the variable is missing from the environment variables" - { val envVars = envVarsOf() @@ -26,6 +26,13 @@ internal class ComputeEnvVarValueTest : UnitTest() { result.shouldContainFailureInstance() .shouldBeInstanceOf() } + + "then the computeOrNull function should return a failure" { + val value = Value.EnvVars(name = ENV_VAR_NAME) + val result = value.computeOrNull(envVars, CONTEXT) + result.shouldContainFailureInstance() + .shouldBeInstanceOf() + } } "when the variable is present from the environment variables" - { diff --git a/library/rules-engine/executor/src/test/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/rule/ComputeLiteralValueTest.kt b/library/rules-engine/executor/src/test/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/rule/ComputeLiteralValueTest.kt index 84ce3d1d..c7560d0d 100644 --- a/library/rules-engine/executor/src/test/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/rule/ComputeLiteralValueTest.kt +++ b/library/rules-engine/executor/src/test/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/rule/ComputeLiteralValueTest.kt @@ -11,12 +11,13 @@ import io.github.ustudiocompany.uframework.test.kotest.UnitTest internal class ComputeLiteralValueTest : UnitTest() { init { - "when the value is the Literal type" - { - val value = Value.Literal(JsonElement.Text(VALUE)) + + "Compute a value of the Literal type" - { + val value = Value.Literal(JsonElement.Text(FACT_VALUE)) "then the compute function should return the fact-value" { val result = value.compute(ENV_VARS, CONTEXT) - result shouldBeSuccess JsonElement.Text(VALUE) + result shouldBeSuccess JsonElement.Text(FACT_VALUE) } } } @@ -24,6 +25,6 @@ internal class ComputeLiteralValueTest : UnitTest() { companion object { private val ENV_VARS = envVarsOf() private val CONTEXT = Context.empty() - private const val VALUE = "value" + private const val FACT_VALUE = "value" } } diff --git a/library/rules-engine/executor/src/test/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/rule/ComputeNullableReferenceValueTest.kt b/library/rules-engine/executor/src/test/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/rule/ComputeNullableReferenceValueTest.kt deleted file mode 100644 index e264f1eb..00000000 --- a/library/rules-engine/executor/src/test/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/rule/ComputeNullableReferenceValueTest.kt +++ /dev/null @@ -1,88 +0,0 @@ -package io.github.ustudiocompany.uframework.rulesengine.core.rule - -import io.github.airflux.commons.types.AirfluxTypesExperimental -import io.github.airflux.commons.types.resultk.ResultK -import io.github.airflux.commons.types.resultk.asSuccess -import io.github.airflux.commons.types.resultk.matcher.shouldBeSuccess -import io.github.airflux.commons.types.resultk.matcher.shouldContainFailureInstance -import io.github.airflux.commons.types.resultk.matcher.shouldContainSuccessInstance -import io.github.ustudiocompany.uframework.json.element.JsonElement -import io.github.ustudiocompany.uframework.json.path.Path -import io.github.ustudiocompany.uframework.rulesengine.core.context.Context -import io.github.ustudiocompany.uframework.rulesengine.core.env.envVarsOf -import io.github.ustudiocompany.uframework.test.kotest.UnitTest -import io.kotest.matchers.nulls.shouldBeNull -import io.kotest.matchers.types.shouldBeInstanceOf - -@OptIn(AirfluxTypesExperimental::class) -internal class ComputeNullableReferenceValueTest : UnitTest() { - - init { - "when the value is the Reference type" - { - - "when the source is not in the context" - { - val envVars = envVarsOf() - val context = Context.empty() - - "then the compute function should return a failure" { - val value = Value.Reference( - source = SOURCE, - path = path(result = null) - ) - val result = value.computeOrNull(envVars, context) - result.shouldContainFailureInstance() - .shouldBeInstanceOf() - } - } - - "when the source is in the context" - { - val envVars = envVarsOf() - val context = Context(sources = mapOf(SOURCE to DATA)) - - "when the data does not contain values by path" - { - - "then the compute function should return a failure" { - val value = Value.Reference( - source = SOURCE, - path = path(result = null) - ) - val result = value.computeOrNull(envVars, context) - result.shouldContainSuccessInstance() - .shouldBeNull() - } - } - - "when the data contains values by path" - { - - "then the compute function should return a value" { - val value = Value.Reference( - source = SOURCE, - path = path(result = TEXT_VALUE) - ) - val result = value.computeOrNull(envVars, context) - result shouldBeSuccess JsonElement.Text(VALUE) - } - } - } - } - } - - companion object { - private const val SOURCE_NAME = "input.body" - private val SOURCE = Source(SOURCE_NAME) - - private const val KEY = "id" - private const val VALUE = "value" - private val TEXT_VALUE = JsonElement.Text(VALUE) - private val DATA = JsonElement.Struct(KEY to TEXT_VALUE) - - private const val PATH_VALUE = "$.id" - private fun path(result: JsonElement?) = - object : Path { - override val text: String = PATH_VALUE - - override fun searchIn(data: JsonElement): ResultK = - result.asSuccess() - } - } -} diff --git a/library/rules-engine/executor/src/test/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/rule/ComputeReferenceValueTest.kt b/library/rules-engine/executor/src/test/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/rule/ComputeReferenceValueTest.kt index 15f31acf..0d05da35 100644 --- a/library/rules-engine/executor/src/test/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/rule/ComputeReferenceValueTest.kt +++ b/library/rules-engine/executor/src/test/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/rule/ComputeReferenceValueTest.kt @@ -2,21 +2,25 @@ package io.github.ustudiocompany.uframework.rulesengine.core.rule import io.github.airflux.commons.types.AirfluxTypesExperimental import io.github.airflux.commons.types.resultk.ResultK +import io.github.airflux.commons.types.resultk.ResultK.Success import io.github.airflux.commons.types.resultk.asSuccess import io.github.airflux.commons.types.resultk.matcher.shouldBeSuccess import io.github.airflux.commons.types.resultk.matcher.shouldContainFailureInstance +import io.github.airflux.commons.types.resultk.matcher.shouldContainSuccessInstance import io.github.ustudiocompany.uframework.json.element.JsonElement import io.github.ustudiocompany.uframework.json.path.Path import io.github.ustudiocompany.uframework.rulesengine.core.context.Context import io.github.ustudiocompany.uframework.rulesengine.core.env.envVarsOf import io.github.ustudiocompany.uframework.test.kotest.UnitTest +import io.kotest.matchers.nulls.shouldBeNull import io.kotest.matchers.types.shouldBeInstanceOf @OptIn(AirfluxTypesExperimental::class) internal class ComputeReferenceValueTest : UnitTest() { init { - "when the value is the Reference type" - { + + "Compute a value of the Reference type" - { "when the source is not in the context" - { val envVars = envVarsOf() @@ -25,12 +29,22 @@ internal class ComputeReferenceValueTest : UnitTest() { "then the compute function should return a failure" { val value = Value.Reference( source = SOURCE, - path = path(result = null) + path = path(result = Success.asNull) ) val result = value.compute(envVars, context) result.shouldContainFailureInstance() .shouldBeInstanceOf() } + + "then the computeOrNull function should return a failure" { + val value = Value.Reference( + source = SOURCE, + path = path(result = Success.asNull) + ) + val result = value.computeOrNull(envVars, context) + result.shouldContainFailureInstance() + .shouldBeInstanceOf() + } } "when the source is in the context" - { @@ -39,15 +53,25 @@ internal class ComputeReferenceValueTest : UnitTest() { "when the data does not contain values by path" - { - "then the compute function should return a failure" { + "then the compute function should return an error" { val value = Value.Reference( source = SOURCE, - path = path(result = null) + path = path(result = Success.asNull) ) val result = value.compute(envVars, context) result.shouldContainFailureInstance() .shouldBeInstanceOf() } + + "then the computeOrNull function should return an error" { + val value = Value.Reference( + source = SOURCE, + path = path(result = Success.asNull) + ) + val result = value.computeOrNull(envVars, context) + result.shouldContainSuccessInstance() + .shouldBeNull() + } } "when the data contains values by path" - { @@ -55,11 +79,20 @@ internal class ComputeReferenceValueTest : UnitTest() { "then the compute function should return a value" { val value = Value.Reference( source = SOURCE, - path = path(result = TEXT_VALUE) + path = path(result = TEXT_VALUE.asSuccess()) ) val result = value.compute(envVars, context) result shouldBeSuccess JsonElement.Text(VALUE) } + + "then the computeOrNull function should return a value" { + val value = Value.Reference( + source = SOURCE, + path = path(result = TEXT_VALUE.asSuccess()) + ) + val result = value.computeOrNull(envVars, context) + result shouldBeSuccess JsonElement.Text(VALUE) + } } } } @@ -76,12 +109,10 @@ internal class ComputeReferenceValueTest : UnitTest() { private val DATA = JsonElement.Struct(KEY to TEXT_VALUE) private const val PATH_VALUE = "$.id" - private fun path(result: JsonElement?) = + private fun path(result: ResultK) = object : Path { override val text: String = PATH_VALUE - - override fun searchIn(data: JsonElement): ResultK = - result.asSuccess() + override fun searchIn(data: JsonElement): ResultK = result } } } diff --git a/library/rules-engine/executor/src/test/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/rule/step/DataBuildStepExecutorTest.kt b/library/rules-engine/executor/src/test/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/rule/step/DataBuildStepExecutorTest.kt index 8f779903..7369d4e0 100644 --- a/library/rules-engine/executor/src/test/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/rule/step/DataBuildStepExecutorTest.kt +++ b/library/rules-engine/executor/src/test/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/rule/step/DataBuildStepExecutorTest.kt @@ -1,17 +1,25 @@ package io.github.ustudiocompany.uframework.rulesengine.core.rule.step import io.github.airflux.commons.types.AirfluxTypesExperimental +import io.github.airflux.commons.types.fail.asError +import io.github.airflux.commons.types.fail.asException import io.github.airflux.commons.types.maybe.matcher.shouldBeNone -import io.github.airflux.commons.types.resultk.ResultK +import io.github.airflux.commons.types.maybe.matcher.shouldContainErrorInstance +import io.github.airflux.commons.types.maybe.matcher.shouldContainExceptionInstance +import io.github.airflux.commons.types.resultk.asFailure +import io.github.airflux.commons.types.resultk.asSuccess import io.github.ustudiocompany.uframework.json.element.JsonElement import io.github.ustudiocompany.uframework.rulesengine.core.context.Context +import io.github.ustudiocompany.uframework.rulesengine.core.env.EnvVarName import io.github.ustudiocompany.uframework.rulesengine.core.env.envVarsOf import io.github.ustudiocompany.uframework.rulesengine.core.rule.Source import io.github.ustudiocompany.uframework.rulesengine.core.rule.Value import io.github.ustudiocompany.uframework.rulesengine.core.rule.condition.Condition +import io.github.ustudiocompany.uframework.rulesengine.core.rule.step.StepResult.Action.Merge.StrategyCode import io.github.ustudiocompany.uframework.rulesengine.executor.Merger import io.github.ustudiocompany.uframework.test.kotest.UnitTest import io.kotest.matchers.shouldBe +import io.kotest.matchers.types.shouldBeInstanceOf @OptIn(AirfluxTypesExperimental::class) internal class DataBuildStepExecutorTest : UnitTest() { @@ -20,59 +28,149 @@ internal class DataBuildStepExecutorTest : UnitTest() { "The data step executor" - { - "then the executor should perform the step" - { - val envVars = envVarsOf() - val context = Context.empty() - val step = createStep() - val result = step.execute(envVars, context, TestMerger()) + "when execution of the step is successful" - { - "then the executor should return a success result" { - result.shouldBeNone() + "then the executor should perform the step" - { + val step = createStep(StepResult.Action.Put) + val result = step.execute( + envVars = ENV_VARS, + context = CONTEXT, + merger = { _, origin, _ -> origin.asSuccess() } + ) + + "then the executor should return a success result" { + result.shouldBeNone() + } + + "then the context should contain the generated data" { + val result = CONTEXT.getOrNull(SOURCE) + result shouldBe EXPECTED_DATA + } + } + } + + "when execution of the step is fail" - { + + "when an error of building data by scheme" - { + val step = DataBuildStep( + id = STEP_ID, + condition = Condition.NONE, + dataSchema = DataSchema.Struct( + properties = listOf( + DataSchema.Property.Element( + name = RESULT_ID_DATA_KEY, + value = Value.EnvVars(name = UNKNOW_ENV_VAR) + ) + ) + ), + result = StepResult( + source = SOURCE, + action = StepResult.Action.Put + ) + ) + val result = step.execute( + envVars = ENV_VARS, + context = CONTEXT, + merger = { _, origin, _ -> origin.asSuccess() } + ) + + "then the executor should return an error result" { + result.shouldContainErrorInstance() + .shouldBeInstanceOf() + } } - "then the context should contain the generated data" { - val result = context.getOrNull(SOURCE) - result shouldBe EXPECTED_DATA + "when an error of putting result" - { + val context = Context(SOURCE to ACTUAL_DATA) + val step = createStep(StepResult.Action.Put) + val result = step.execute( + envVars = ENV_VARS, + context = context, + merger = { _, origin, _ -> origin.asSuccess() } + ) + + "then the executor should return an error result" { + result.shouldContainErrorInstance() + .shouldBeInstanceOf() + } + } + + "when an error of replacing result" - { + val step = createStep(StepResult.Action.Put) + val result = step.execute( + envVars = ENV_VARS, + context = CONTEXT, + merger = { _, origin, _ -> origin.asSuccess() } + ) + + "then the executor should return an error result" { + result.shouldContainErrorInstance() + .shouldBeInstanceOf() + } + } + + "when an error of merging result" - { + val step = createStep(StepResult.Action.Merge(MERGE_STRATEGY_CODE)) + val result = step.execute( + envVars = ENV_VARS, + context = CONTEXT, + merger = { _, _, _ -> Merger.Error().asError().asFailure() } + ) + + "then the executor should return an error result" { + result.shouldContainErrorInstance() + .shouldBeInstanceOf() + } + } + + "when an incident of merging result" - { + val step = createStep(StepResult.Action.Merge(MERGE_STRATEGY_CODE)) + + val result = step.execute( + envVars = ENV_VARS, + context = CONTEXT, + merger = { _, _, _ -> Merger.Incident().asException().asFailure() } + ) + + "then the executor should return an error result" { + result.shouldContainExceptionInstance() + .shouldBeInstanceOf() + } } } } } private companion object { + private val ENV_VARS = envVarsOf() + private val CONTEXT = Context.empty() private val STEP_ID = StepId("step-1") - private const val ID_DATA_KEY = "id" - private const val ID_DATA_VALUE = "0000-0000-0000-0000" - + private const val ID_DATA_KEY = "code" + private const val ID_DATA_VALUE = "1111-1111-1111-1111" + private const val RESULT_ID_DATA_KEY = "id" + private const val RESULT_ID_DATA_VALUE = "0000-0000-0000-0000" private val SOURCE = Source("output") + private val MERGE_STRATEGY_CODE = StrategyCode("merge-strategy-code") + private val ACTUAL_DATA = JsonElement.Struct(ID_DATA_KEY to JsonElement.Text(ID_DATA_VALUE)) + private val EXPECTED_DATA = JsonElement.Struct(RESULT_ID_DATA_KEY to JsonElement.Text(RESULT_ID_DATA_VALUE)) + private val UNKNOW_ENV_VAR = EnvVarName("unknow") - private val EXPECTED_DATA = JsonElement.Struct(ID_DATA_KEY to JsonElement.Text(ID_DATA_VALUE)) - - private fun createStep() = + private fun createStep(action: StepResult.Action) = DataBuildStep( id = STEP_ID, condition = Condition.NONE, dataSchema = DataSchema.Struct( properties = listOf( DataSchema.Property.Element( - name = ID_DATA_KEY, - value = Value.Literal(fact = JsonElement.Text(ID_DATA_VALUE)) + name = RESULT_ID_DATA_KEY, + value = Value.Literal(fact = JsonElement.Text(RESULT_ID_DATA_VALUE)) ) ) ), result = StepResult( source = SOURCE, - action = StepResult.Action.Put + action = action ) ) } - - private class TestMerger : Merger { - override fun merge( - strategyCode: StepResult.Action.Merge.StrategyCode, - dst: JsonElement, - src: JsonElement - ): ResultK { - error("Not implemented") - } - } } diff --git a/library/rules-engine/executor/src/test/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/rule/step/DataRetrieveStepExecutorTest.kt b/library/rules-engine/executor/src/test/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/rule/step/DataRetrieveStepExecutorTest.kt index 412ee97d..a7d6af71 100644 --- a/library/rules-engine/executor/src/test/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/rule/step/DataRetrieveStepExecutorTest.kt +++ b/library/rules-engine/executor/src/test/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/rule/step/DataRetrieveStepExecutorTest.kt @@ -1,12 +1,16 @@ package io.github.ustudiocompany.uframework.rulesengine.core.rule.step import io.github.airflux.commons.types.AirfluxTypesExperimental +import io.github.airflux.commons.types.fail.asError +import io.github.airflux.commons.types.fail.asException import io.github.airflux.commons.types.maybe.matcher.shouldBeNone -import io.github.airflux.commons.types.maybe.matcher.shouldContainSomeInstance +import io.github.airflux.commons.types.maybe.matcher.shouldContainErrorInstance +import io.github.airflux.commons.types.maybe.matcher.shouldContainExceptionInstance import io.github.airflux.commons.types.resultk.asFailure import io.github.airflux.commons.types.resultk.asSuccess import io.github.ustudiocompany.uframework.json.element.JsonElement import io.github.ustudiocompany.uframework.rulesengine.core.context.Context +import io.github.ustudiocompany.uframework.rulesengine.core.env.EnvVarName import io.github.ustudiocompany.uframework.rulesengine.core.env.envVarsOf import io.github.ustudiocompany.uframework.rulesengine.core.rule.Source import io.github.ustudiocompany.uframework.rulesengine.core.rule.Value @@ -26,25 +30,7 @@ internal class DataRetrieveStepExecutorTest : UnitTest() { "when execution of the step is successful" - { val context = Context.empty() - val step = DataRetrieveStep( - id = STEP_ID, - condition = Condition.NONE, - uri = Uri, - args = Args( - listOf( - Arg( - name = ID_PARAM_NAME, - value = Value.Literal( - fact = JsonElement.Text(ID_PARAM_VALUE) - ) - ) - ) - ), - result = StepResult( - source = RESULT_SOURCE, - action = StepResult.Action.Put - ) - ) + val step = createStep(StepResult.Action.Put) val result = step.execute( envVars = ENV_VARS, @@ -65,7 +51,7 @@ internal class DataRetrieveStepExecutorTest : UnitTest() { "when execution of the step is fail" - { - "when an external call error" - { + "when an error of build args" - { val step = DataRetrieveStep( id = STEP_ID, condition = Condition.NONE, @@ -74,8 +60,8 @@ internal class DataRetrieveStepExecutorTest : UnitTest() { listOf( Arg( name = ID_PARAM_NAME, - value = Value.Literal( - fact = JsonElement.Text(ID_PARAM_VALUE) + value = Value.EnvVars( + name = UNKNOW_ENV_VAR ) ) ) @@ -90,52 +76,102 @@ internal class DataRetrieveStepExecutorTest : UnitTest() { val result = step.execute( envVars = ENV_VARS, context = CONTEXT, - dataProvider = { _, _ -> DataProvider.Error().asFailure() }, + dataProvider = { _, _ -> CALL_RESULT.asSuccess() }, merger = { _, origin, _ -> origin.asSuccess() } ) - result.shouldContainSomeInstance() + result.shouldContainErrorInstance() + .shouldBeInstanceOf() + } + } + + "when an external call error" - { + val step = createStep(StepResult.Action.Put) + + "then the executor should return an error result" { + val result = step.execute( + envVars = ENV_VARS, + context = CONTEXT, + dataProvider = { _, _ -> DataProvider.Error().asError().asFailure() }, + merger = { _, origin, _ -> origin.asSuccess() } + ) + result.shouldContainErrorInstance() .shouldBeInstanceOf() } } - "when an error of merging" - { - val context = Context(sources = mapOf(RESULT_SOURCE to JsonElement.Text(ORIGIN_VALUE))) - val step = DataRetrieveStep( - id = STEP_ID, - condition = Condition.NONE, - uri = Uri, - args = Args( - listOf( - Arg( - name = ID_PARAM_NAME, - value = Value.Literal( - fact = JsonElement.Text(ID_PARAM_VALUE) - ) - ) - ) - ), - result = StepResult( - source = RESULT_SOURCE, - action = StepResult.Action.Merge(strategyCode = MERGE_STRATEGY_CODE) + "when an external call incident" - { + val step = createStep(StepResult.Action.Put) + + "then the executor should return an error result" { + val result = step.execute( + envVars = ENV_VARS, + context = CONTEXT, + dataProvider = { _, _ -> DataProvider.Incident().asException().asFailure() }, + merger = { _, origin, _ -> origin.asSuccess() } ) + result.shouldContainExceptionInstance() + .shouldBeInstanceOf() + } + } + + "when an error of merging result" - { + val context = Context(sources = mapOf(RESULT_SOURCE to JsonElement.Text(ORIGIN_VALUE))) + val step = createStep(StepResult.Action.Merge(strategyCode = MERGE_STRATEGY_CODE)) + + val result = step.execute( + envVars = ENV_VARS, + context = context, + dataProvider = { _, _ -> CALL_RESULT.asSuccess() }, + merger = { _, _, _ -> Merger.Error().asError().asFailure() } ) + "then the executor should return an error result" { + result.shouldContainErrorInstance() + .shouldBeInstanceOf() + } + } + + "when an incident of merging result" - { + val context = Context(sources = mapOf(RESULT_SOURCE to JsonElement.Text(ORIGIN_VALUE))) + val step = createStep(StepResult.Action.Merge(strategyCode = MERGE_STRATEGY_CODE)) + val result = step.execute( envVars = ENV_VARS, context = context, dataProvider = { _, _ -> CALL_RESULT.asSuccess() }, - merger = { _, _, _ -> Merger.Error().asFailure() } + merger = { _, _, _ -> Merger.Incident().asException().asFailure() } ) "then the executor should return an error result" { - result.shouldContainSomeInstance() - .shouldBeInstanceOf() + result.shouldContainExceptionInstance() + .shouldBeInstanceOf() } } } } } + private fun createStep(action: StepResult.Action) = + DataRetrieveStep( + id = STEP_ID, + condition = Condition.NONE, + uri = Uri, + args = Args( + listOf( + Arg( + name = ID_PARAM_NAME, + value = Value.Literal( + fact = JsonElement.Text(ID_PARAM_VALUE) + ) + ) + ) + ), + result = StepResult( + source = RESULT_SOURCE, + action = action + ) + ) + private companion object { private val STEP_ID = StepId("step-1") private val ENV_VARS = envVarsOf() @@ -146,7 +182,7 @@ internal class DataRetrieveStepExecutorTest : UnitTest() { private const val ID_PARAM_NAME = "id" private const val ID_PARAM_VALUE = "1" - + private val UNKNOW_ENV_VAR = EnvVarName("unknow") private val RESULT_SOURCE = Source("output") private val CALL_RESULT = JsonElement.Text("data") diff --git a/library/rules-engine/executor/src/test/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/rule/step/DataSchemaBuilderTest.kt b/library/rules-engine/executor/src/test/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/rule/step/DataSchemaBuilderTest.kt index 9ae47509..31368dbc 100644 --- a/library/rules-engine/executor/src/test/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/rule/step/DataSchemaBuilderTest.kt +++ b/library/rules-engine/executor/src/test/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/rule/step/DataSchemaBuilderTest.kt @@ -1,15 +1,12 @@ package io.github.ustudiocompany.uframework.rulesengine.core.rule.step import io.github.airflux.commons.types.AirfluxTypesExperimental -import io.github.airflux.commons.types.resultk.ResultK -import io.github.airflux.commons.types.resultk.asFailure import io.github.airflux.commons.types.resultk.matcher.shouldBeSuccess import io.github.airflux.commons.types.resultk.matcher.shouldContainFailureInstance import io.github.ustudiocompany.uframework.json.element.JsonElement import io.github.ustudiocompany.uframework.rulesengine.core.context.Context -import io.github.ustudiocompany.uframework.rulesengine.core.env.EnvVars +import io.github.ustudiocompany.uframework.rulesengine.core.env.EnvVarName import io.github.ustudiocompany.uframework.rulesengine.core.env.envVarsOf -import io.github.ustudiocompany.uframework.rulesengine.core.feel.FeelExpression import io.github.ustudiocompany.uframework.rulesengine.core.rule.Value import io.github.ustudiocompany.uframework.test.kotest.UnitTest import io.kotest.matchers.shouldBe @@ -219,19 +216,38 @@ internal class DataSchemaBuilderTest : UnitTest() { } "when occurs an error" - { - val dataSchema = DataSchema.Struct( - properties = listOf( - DataSchema.Property.Element( - name = DATA_KEY_1, - value = Value.Expression(EXPRESSION) + + "when error of build a property" - { + val dataSchema = DataSchema.Struct( + properties = listOf( + DataSchema.Property.Element( + name = DATA_KEY_1, + value = Value.EnvVars(name = UNKNOW_ENV_VAR) + ) + ) + ) + + "then the builder should return a failure" { + val result = dataSchema.build(ENV_VARS, CONTEXT) + result.shouldContainFailureInstance() + .shouldBeInstanceOf() + } + } + + "when error of build an array" - { + val dataSchema = DataSchema.Array( + items = listOf( + DataSchema.Item.Element( + value = Value.EnvVars(name = UNKNOW_ENV_VAR) + ) ) ) - ) - "then the builder should return a failure" { - val result = dataSchema.build(ENV_VARS, CONTEXT) - result.shouldContainFailureInstance() - .shouldBeInstanceOf() + "then the builder should return a failure" { + val result = dataSchema.build(ENV_VARS, CONTEXT) + result.shouldContainFailureInstance() + .shouldBeInstanceOf() + } } } } @@ -251,17 +267,6 @@ internal class DataSchemaBuilderTest : UnitTest() { private const val ARRAY_ITEM_2 = "item-2" private const val ARRAY_ITEM_3 = "item-3" private const val ARRAY_ITEM_4 = "item-4" - - private val EXPRESSION = object : FeelExpression { - - override val text: String - get() = "a/0" - - override fun evaluate( - envVars: EnvVars, - context: Context - ): ResultK = - FeelExpression.EvaluateError(this).asFailure() - } + private val UNKNOW_ENV_VAR = EnvVarName("unknow") } } diff --git a/library/rules-engine/executor/src/test/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/rule/step/HttpCallStepExecutorTest.kt b/library/rules-engine/executor/src/test/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/rule/step/HttpCallStepExecutorTest.kt index aa2ddbf2..7048303c 100644 --- a/library/rules-engine/executor/src/test/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/rule/step/HttpCallStepExecutorTest.kt +++ b/library/rules-engine/executor/src/test/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/rule/step/HttpCallStepExecutorTest.kt @@ -1,8 +1,11 @@ package io.github.ustudiocompany.uframework.rulesengine.core.rule.step import io.github.airflux.commons.types.AirfluxTypesExperimental +import io.github.airflux.commons.types.fail.asError +import io.github.airflux.commons.types.fail.asException import io.github.airflux.commons.types.maybe.matcher.shouldBeNone -import io.github.airflux.commons.types.maybe.matcher.shouldContainSomeInstance +import io.github.airflux.commons.types.maybe.matcher.shouldContainErrorInstance +import io.github.airflux.commons.types.maybe.matcher.shouldContainExceptionInstance import io.github.airflux.commons.types.resultk.ResultK import io.github.airflux.commons.types.resultk.Success import io.github.airflux.commons.types.resultk.asFailure @@ -14,6 +17,7 @@ import io.github.ustudiocompany.uframework.rulesengine.core.env.envVarsOf import io.github.ustudiocompany.uframework.rulesengine.core.rule.Source import io.github.ustudiocompany.uframework.rulesengine.core.rule.Value import io.github.ustudiocompany.uframework.rulesengine.core.rule.condition.Condition +import io.github.ustudiocompany.uframework.rulesengine.core.rule.step.StepResult.Action.Merge.StrategyCode import io.github.ustudiocompany.uframework.rulesengine.executor.HttpCallProvider import io.github.ustudiocompany.uframework.rulesengine.executor.Merger import io.github.ustudiocompany.uframework.test.kotest.UnitTest @@ -32,7 +36,7 @@ internal class HttpCallStepExecutorTest : UnitTest() { "when args is missing" - { val context = Context.empty() - val step = createStepWithoutArgs() + val step = createStepWithoutArgs(StepResult.Action.Put) val result = step.execute( envVars = ENV_VARS, @@ -53,7 +57,7 @@ internal class HttpCallStepExecutorTest : UnitTest() { "when a body is missing" - { val context = Context.empty() - val step = createStepWithoutBody() + val step = createStepWithoutBody(StepResult.Action.Put) val result = step.execute( envVars = ENV_VARS, @@ -118,52 +122,67 @@ internal class HttpCallStepExecutorTest : UnitTest() { "when execution of the step is fail" - { "when building the args is fail" - { - val step = createStepWithInvalidArgs() + val step = createStepWithInvalidArgs(StepResult.Action.Put) "then the executor should return an error result" - { val result = step.execute( envVars = ENV_VARS, context = CONTEXT, - httpCallProvider = { _, _, _ -> HttpCallProvider.Error().asFailure() }, + httpCallProvider = { _, _, _ -> HttpCallProvider.Error().asError().asFailure() }, merger = { _, origin, _ -> origin.asSuccess() } ) - result.shouldContainSomeInstance() + result.shouldContainErrorInstance() .shouldBeInstanceOf() } } "when building the body is fail" - { - val step = createStepWithInvalidBody() + val step = createStepWithInvalidBody(StepResult.Action.Put) "then the executor should return an error result" - { val result = step.execute( envVars = ENV_VARS, context = CONTEXT, - httpCallProvider = { _, _, _ -> HttpCallProvider.Error().asFailure() }, + httpCallProvider = { _, _, _ -> HttpCallProvider.Error().asError().asFailure() }, merger = { _, origin, _ -> origin.asSuccess() } ) - result.shouldContainSomeInstance() + result.shouldContainErrorInstance() .shouldBeInstanceOf() } } "when an external call error" - { - val step = createSuccessStepWithFullInfo() + val step = createSuccessStepWithFullInfo(StepResult.Action.Put) "then the executor should return an error result" { val result = step.execute( envVars = ENV_VARS, context = CONTEXT, - httpCallProvider = { _, _, _ -> HttpCallProvider.Error().asFailure() }, + httpCallProvider = { _, _, _ -> HttpCallProvider.Error().asError().asFailure() }, merger = { _, origin, _ -> origin.asSuccess() } ) - result.shouldContainSomeInstance() + result.shouldContainErrorInstance() .shouldBeInstanceOf() } } + "when an external call incident" - { + val step = createSuccessStepWithFullInfo(StepResult.Action.Put) + + "then the executor should return an incident" { + val result = step.execute( + envVars = ENV_VARS, + context = CONTEXT, + httpCallProvider = { _, _, _ -> HttpCallProvider.Incident().asException().asFailure() }, + merger = { _, origin, _ -> origin.asSuccess() } + ) + result.shouldContainExceptionInstance() + .shouldBeInstanceOf() + } + } + "when a result is present but a response missing " - { - val step = createSuccessStepWithFullInfo() + val step = createSuccessStepWithFullInfo(StepResult.Action.Put) "then the executor should return an error result" { val result = step.execute( @@ -172,74 +191,91 @@ internal class HttpCallStepExecutorTest : UnitTest() { httpCallProvider = { _, _, _ -> Success.asNull }, merger = { _, origin, _ -> origin.asSuccess() } ) - result.shouldContainSomeInstance() + result.shouldContainErrorInstance() .shouldBeInstanceOf() } } - "when an error of merging" - { + "when an error of merging result" - { + val context = Context(sources = mapOf(RESULT_SOURCE to JsonElement.Text(ORIGIN_VALUE))) + val step = createSuccessStepWithFullInfo(StepResult.Action.Merge(StrategyCode(""))) + + val result = step.execute( + envVars = ENV_VARS, + context = context, + httpCallProvider = { _, _, _ -> CALL_RESULT.asSuccess() }, + merger = { _, _, _ -> Merger.Error().asError().asFailure() } + ) + + "then the executor should return an error result" { + result.shouldContainErrorInstance() + .shouldBeInstanceOf() + } + } + + "when an incident of merging result" - { val context = Context(sources = mapOf(RESULT_SOURCE to JsonElement.Text(ORIGIN_VALUE))) - val step = createSuccessStepWithFullInfo() + val step = createSuccessStepWithFullInfo(StepResult.Action.Merge(MERGE_STRATEGY_CODE)) val result = step.execute( envVars = ENV_VARS, context = context, httpCallProvider = { _, _, _ -> CALL_RESULT.asSuccess() }, - merger = { _, _, _ -> Merger.Error().asFailure() } + merger = { _, _, _ -> Merger.Incident().asException().asFailure() } ) "then the executor should return an error result" { - result.shouldContainSomeInstance() - .shouldBeInstanceOf() + result.shouldContainExceptionInstance() + .shouldBeInstanceOf() } } } } } - private fun createSuccessStepWithFullInfo() = HttpCallStep( + private fun createSuccessStepWithFullInfo(action: StepResult.Action) = HttpCallStep( id = STEP_ID, condition = Condition.NONE, uri = Uri, args = createArgs(), body = createBody(), - result = createResult() + result = createResult(action) ) - private fun createStepWithInvalidArgs() = HttpCallStep( + private fun createStepWithInvalidArgs(action: StepResult.Action) = HttpCallStep( id = STEP_ID, condition = Condition.NONE, uri = Uri, args = createInvalidArgs(), body = createBody(), - result = createResult() + result = createResult(action) ) - private fun createStepWithInvalidBody() = HttpCallStep( + private fun createStepWithInvalidBody(action: StepResult.Action) = HttpCallStep( id = STEP_ID, condition = Condition.NONE, uri = Uri, args = createArgs(), body = createInvalidBody(), - result = createResult() + result = createResult(action) ) - private fun createStepWithoutArgs() = HttpCallStep( + private fun createStepWithoutArgs(action: StepResult.Action) = HttpCallStep( id = STEP_ID, condition = Condition.NONE, uri = Uri, args = Args.NONE, body = createBody(), - result = createResult() + result = createResult(action) ) - private fun createStepWithoutBody() = HttpCallStep( + private fun createStepWithoutBody(action: StepResult.Action) = HttpCallStep( id = STEP_ID, condition = Condition.NONE, uri = Uri, args = createArgs(), body = null, - result = createResult() + result = createResult(action) ) private fun createStepWithoutResult() = HttpCallStep( @@ -278,9 +314,9 @@ internal class HttpCallStepExecutorTest : UnitTest() { private fun createInvalidBody() = Value.Reference(source = INVALID_ARG_SOURCE, path = path()) - private fun createResult() = StepResult( + private fun createResult(action: StepResult.Action) = StepResult( source = RESULT_SOURCE, - action = StepResult.Action.Put + action = action ) private companion object { @@ -301,6 +337,8 @@ internal class HttpCallStepExecutorTest : UnitTest() { private val CALL_RESULT = JsonElement.Text("data") private const val PATH_VALUE = "$.id" + private val MERGE_STRATEGY_CODE = StrategyCode("merge-strategy-code") + private fun path() = object : Path { override val text: String = PATH_VALUE diff --git a/library/rules-engine/executor/src/test/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/rule/step/ValidationStepExecutorTest.kt b/library/rules-engine/executor/src/test/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/rule/step/ValidationStepExecutorTest.kt index 986cff38..280d4b33 100644 --- a/library/rules-engine/executor/src/test/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/rule/step/ValidationStepExecutorTest.kt +++ b/library/rules-engine/executor/src/test/kotlin/io/github/ustudiocompany/uframework/rulesengine/core/rule/step/ValidationStepExecutorTest.kt @@ -1,9 +1,11 @@ package io.github.ustudiocompany.uframework.rulesengine.core.rule.step import io.github.airflux.commons.types.AirfluxTypesExperimental +import io.github.airflux.commons.types.resultk.matcher.shouldContainFailureInstance import io.github.airflux.commons.types.resultk.matcher.shouldContainSuccessInstance import io.github.ustudiocompany.uframework.json.element.JsonElement import io.github.ustudiocompany.uframework.rulesengine.core.context.Context +import io.github.ustudiocompany.uframework.rulesengine.core.env.EnvVarName import io.github.ustudiocompany.uframework.rulesengine.core.env.envVarsOf import io.github.ustudiocompany.uframework.rulesengine.core.rule.Value import io.github.ustudiocompany.uframework.rulesengine.core.rule.condition.Condition @@ -30,8 +32,15 @@ internal class ValidationStepExecutorTest : UnitTest() { } } - "when execution of the step is fail" - { - val step = failStep() + "when the validation is failed" - { + val step = ValidationStep( + id = STEP_ID, + condition = Condition.NONE, + target = Value.Literal(fact = TEXT_VALUE_1), + value = Value.Literal(fact = TEXT_VALUE_2), + operator = EQ, + errorCode = ERROR_CODE + ) "then the executor should return an error result" { val result = step.execute(ENV_VARS, CONTEXT) @@ -40,6 +49,23 @@ internal class ValidationStepExecutorTest : UnitTest() { error shouldBe ERROR_CODE } } + + "when an operand calculation error" - { + val step = ValidationStep( + id = STEP_ID, + condition = Condition.NONE, + target = Value.Literal(fact = TEXT_VALUE_1), + value = Value.EnvVars(name = UNKNOW_ENV_VAR), + operator = EQ, + errorCode = ERROR_CODE + ) + + "then the executor should return an error result" { + val result = step.execute(ENV_VARS, CONTEXT) + result.shouldContainFailureInstance() + .shouldBeInstanceOf() + } + } } } @@ -50,6 +76,7 @@ internal class ValidationStepExecutorTest : UnitTest() { private val ERROR_CODE = ValidationStep.ErrorCode("err-1") private val TEXT_VALUE_1 = JsonElement.Text("value-1") private val TEXT_VALUE_2 = JsonElement.Text("value-2") + private val UNKNOW_ENV_VAR = EnvVarName("unknow") private fun successfulStep() = ValidationStep( @@ -60,15 +87,5 @@ internal class ValidationStepExecutorTest : UnitTest() { operator = EQ, errorCode = ERROR_CODE ) - - private fun failStep() = - ValidationStep( - id = STEP_ID, - condition = Condition.NONE, - target = Value.Literal(fact = TEXT_VALUE_1), - value = Value.Literal(fact = TEXT_VALUE_2), - operator = EQ, - errorCode = ERROR_CODE - ) } } diff --git a/library/rules-engine/feel/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/feel/ExpressionParser.kt b/library/rules-engine/feel/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/feel/ExpressionParser.kt index fb10736b..77c3d80d 100644 --- a/library/rules-engine/feel/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/feel/ExpressionParser.kt +++ b/library/rules-engine/feel/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/feel/ExpressionParser.kt @@ -19,7 +19,7 @@ public fun interface ExpressionParser { } private companion object { - private const val PREFIX = "EXPRESSION-PARSER-" + private const val PREFIX = "EXPRESSION-PARSER-ERROR-" private const val DETAILS_KEY_PATH = "expression" } } diff --git a/library/rules-engine/parser/core/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/parser/RulesParser.kt b/library/rules-engine/parser/core/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/parser/RulesParser.kt index 1e524e0e..037f8296 100644 --- a/library/rules-engine/parser/core/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/parser/RulesParser.kt +++ b/library/rules-engine/parser/core/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/parser/RulesParser.kt @@ -30,7 +30,7 @@ public fun interface RulesParser { } private companion object { - private const val PREFIX = "RULES-PARSER-" + private const val PREFIX = "RULES-PARSER-ERROR-" } } } diff --git a/library/rules-engine/parser/jackson/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/parser/Converter.kt b/library/rules-engine/parser/jackson/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/parser/Converter.kt index cf8868d0..d78a5132 100644 --- a/library/rules-engine/parser/jackson/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/parser/Converter.kt +++ b/library/rules-engine/parser/jackson/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/parser/Converter.kt @@ -281,11 +281,11 @@ internal class Converter( private fun PathModel.convertPath(): ResultK = pathParser.parse(this) - .mapFailure { error -> Errors.Conversion(error) } + .mapFailure { failure -> Errors.Conversion(cause = failure) } private fun FeelExpressionModel.convertExpression(): ResultK = expressionParser.parse(this) - .mapFailure { error -> Errors.Conversion(error) } + .mapFailure { failure -> Errors.Conversion(cause = failure) } private fun EnvVarNameModel.convertEnvVarName(): EnvVarName = EnvVarName(this) @@ -302,7 +302,7 @@ internal class Converter( } private companion object { - private const val PREFIX = "RULES-CONVERTER-" + private const val PREFIX = "RULES-CONVERTER-ERROR-" } } } diff --git a/library/rules-engine/parser/jackson/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/parser/RulesDeserializer.kt b/library/rules-engine/parser/jackson/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/parser/RulesDeserializer.kt index 00bb8099..cd805562 100644 --- a/library/rules-engine/parser/jackson/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/parser/RulesDeserializer.kt +++ b/library/rules-engine/parser/jackson/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/parser/RulesDeserializer.kt @@ -25,7 +25,7 @@ internal class RulesDeserializer(private val mapper: ObjectMapper) { } private companion object { - private const val PREFIX = "RULES-DESERIALIZER-" + private const val PREFIX = "RULES-DESERIALIZER-ERROR-" } } } diff --git a/library/rules-engine/parser/jackson/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/parser/RulesParser.kt b/library/rules-engine/parser/jackson/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/parser/RulesParser.kt index 6fac4ebd..dc28cfb6 100644 --- a/library/rules-engine/parser/jackson/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/parser/RulesParser.kt +++ b/library/rules-engine/parser/jackson/src/main/kotlin/io/github/ustudiocompany/uframework/rulesengine/parser/RulesParser.kt @@ -26,5 +26,5 @@ private class JacksonRulesParser( override fun parse(input: String): ResultK = deserializer.deserialize(input) .andThen { model -> converter.convert(model.rules) } - .mapFailure { error -> RulesParser.Errors.Parsing(error) } + .mapFailure { failure -> RulesParser.Errors.Parsing(cause = failure) } }