Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
package io.kform.test
import io.kform.*
import io.kform.internal.schemaInfoImpl
import io.kform.internal.validateValidation
import io.kform.internal.valueInfoImpl
import io.kform.schemas.AnySchema
import kotlin.jvm.JvmOverloads
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.emitAll
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.single
/**
* Runs a given [validation] within a form with schema [formSchema] and returns a flow over its
* issues.
*
* It is necessary to provide all information required to run the validation: the [path] of the
* value being validated, the [value] itself, the [values of the dependencies][dependencyValues] (by
* dependency name), and the values of the [external contexts][externalContexts] that the validation
* depends on.
*
* If the validation depends on values within the value being validated, then these may be omitted
* from the provided [dependencyValues].
*
* Otherwise, missing dependencies or external contexts will be passed to the validation as `null`,
* in which case the validation may or may not throw depending on its implementation.
*
* Exceptions thrown while running the validation will not be caught by this function. As opposed to
* how a [form validator][FormValidator] or [form manager][FormManager] operate, exceptions are
* **not** wrapped in a [ValidationExceptionError] nor emitted as part of the issues flow.
*
* @throws InvalidPathException If [path] matches no schemas or contains fragments other than ids.
* @throws InvalidDependencyPathException If [validation] contains an invalid dependency.
* @throws IllegalArgumentException When providing unknown names for [dependencyValues] or
* [externalContexts] or when providing mismatching dependency values for dependencies within
* [value].
*/
@Suppress("UNCHECKED_CAST")
@JvmOverloads
public fun runValidation(
formSchema: Schema<*>,
validation: Validation,
value: T,
path: Path = AbsolutePath.ROOT,
dependencyValues: Map? = null,
externalContexts: ExternalContexts? = null
): Flow =
path.toAbsolutePath().let { absolutePath ->
// Validate path
if (!isValidPath(formSchema, absolutePath)) {
throw InvalidPathException(absolutePath, "No schema matches this path.")
}
if (absolutePath.fragments.any { it !is AbsolutePathFragment.Id }) {
throw InvalidPathException(
absolutePath,
"The path of the value being validated must only contain ids."
)
}
// Validate keys of provided dependencies
for (key in dependencyValues?.keys ?: emptyList()) {
require(key in validation.dependencies) {
"No dependency with key '$key' was found in the validation."
}
}
// Validate names of provided external contexts
for (name in externalContexts?.keys ?: emptyList()) {
require(name in validation.externalContextDependencies) {
"No external context dependency named '$name' was found in the validation."
}
}
// Validate validation dependencies
validateValidation(formSchema, absolutePath, validation)
flow {
val schemaInfo = schemaInfo(formSchema, absolutePath).single() as SchemaInfo
val validationContext =
ValidationContext(
value,
schemaInfo.schema,
absolutePath,
schemaInfo.path,
dependenciesInfo(
formSchema,
validation,
value,
schemaInfo.schema,
absolutePath,
dependencyValues
),
validation.externalContextDependencies.associateWith {
externalContexts?.get(it)
}
)
validation.run { emitAll(validationContext.validate()) }
}
}
/**
* Runs a given [validation] within a form with schema [formSchema] and returns a flow over its
* issues.
*
* It is necessary to provide all information required to run the validation: the [path] of the
* value being validated, the [value] itself, the [values of the dependencies][dependencyValues] (by
* dependency name), and the values of the [external contexts][externalContexts] that the validation
* depends on.
*
* If the validation depends on values within the value being validated, then these may be omitted
* from the provided [dependencyValues].
*
* Otherwise, missing dependencies or external contexts will be passed to the validation as `null`,
* in which case the validation may or may not throw depending on its implementation.
*
* Exceptions thrown while running the validation will not be caught by this function. As opposed to
* how a [form validator][FormValidator] or [form manager][FormManager] operate, exceptions are
* **not** wrapped in a [ValidationExceptionError] nor emitted as part of the issues flow.
*
* @throws InvalidPathException If [path] matches no schemas or contains fragments other than ids.
* @throws InvalidDependencyPathException If [validation] contains an invalid dependency.
* @throws IllegalArgumentException When providing unknown names for [dependencyValues] or
* [externalContexts] or when providing mismatching dependency values for dependencies within
* [value].
*/
@JvmOverloads
public fun runValidation(
formSchema: Schema<*>,
validation: Validation,
value: T,
path: String,
dependencyValues: Map? = null,
externalContexts: ExternalContexts? = null
): Flow =
runValidation(
formSchema,
validation,
value,
AbsolutePath(path),
dependencyValues,
externalContexts
)
/**
* Runs a given [validation] within a form with a schema of type [AnySchema] and returns a flow over
* its issues.
*
* It is necessary to provide all information required to run the validation: the [path] of the
* value being validated, the [value] itself, the [values of the dependencies][dependencyValues] (by
* dependency name), and the values of the [external contexts][externalContexts] that the validation
* depends on.
*
* If the validation depends on values within the value being validated, then these may be omitted
* from the provided [dependencyValues].
*
* Otherwise, missing dependencies or external contexts will be passed to the validation as `null`,
* in which case the validation may or may not throw depending on its implementation.
*
* Exceptions thrown while running the validation will not be caught by this function. As opposed to
* how a [form validator][FormValidator] or [form manager][FormManager] operate, exceptions are
* **not** wrapped in a [ValidationExceptionError] nor emitted as part of the issues flow.
*
* @throws InvalidPathException If [path] matches no schemas or contains fragments other than ids.
* @throws InvalidDependencyPathException If [validation] contains an invalid dependency.
* @throws IllegalArgumentException When providing unknown names for [dependencyValues] or
* [externalContexts] or when providing mismatching dependency values for dependencies within
* [value].
*/
@JvmOverloads
public fun runValidation(
validation: Validation,
value: T,
path: Path = AbsolutePath.ROOT,
dependencyValues: Map? = null,
externalContexts: ExternalContexts? = null
): Flow =
runValidation(AnySchema(), validation, value, path, dependencyValues, externalContexts)
/**
* Runs a given [validation] within a form with a schema of type [AnySchema] and returns a flow over
* its issues.
*
* It is necessary to provide all information required to run the validation: the [path] of the
* value being validated, the [value] itself, the [values of the dependencies][dependencyValues] (by
* dependency name), and the values of the [external contexts][externalContexts] that the validation
* depends on.
*
* If the validation depends on values within the value being validated, then these may be omitted
* from the provided [dependencyValues].
*
* Otherwise, missing dependencies or external contexts will be passed to the validation as `null`,
* in which case the validation may or may not throw depending on its implementation.
*
* Exceptions thrown while running the validation will not be caught by this function. As opposed to
* how a [form validator][FormValidator] or [form manager][FormManager] operate, exceptions are
* **not** wrapped in a [ValidationExceptionError] nor emitted as part of the issues flow.
*
* @throws InvalidPathException If [path] matches no schemas or contains fragments other than ids.
* @throws InvalidDependencyPathException If [validation] contains an invalid dependency.
* @throws IllegalArgumentException When providing unknown names for [dependencyValues] or
* [externalContexts] or when providing mismatching dependency values for dependencies within
* [value].
*/
@JvmOverloads
public fun runValidation(
validation: Validation,
value: T,
path: String,
dependencyValues: Map? = null,
externalContexts: ExternalContexts? = null
): Flow =
runValidation(validation, value, AbsolutePath(path), dependencyValues, externalContexts)
/**
* Returns the value info of the validation dependencies as needed by [validation] when validating a
* [value] with [schema] at [absolutePath].
*/
private suspend fun dependenciesInfo(
formSchema: Schema<*>,
validation: Validation,
value: T,
schema: Schema,
absolutePath: AbsolutePath,
dependencyValues: Map?
): Map?> =
validation.dependencies.mapValues { (name, dep) ->
val resolvedDep = absolutePath.resolve(dep.path)
val resolvedDepWithoutDescendants =
if (resolvedDep.lastFragment is AbsolutePathFragment.RecursiveWildcard)
resolvedDep.parent()
else resolvedDep
val depValue =
// When the dependency is a descendant of [value], then we can just extract the
// dependency value from it; otherwise, the dependency value should be provided
if (
absolutePath
.append(AbsolutePathFragment.RecursiveWildcard)
.contains(resolvedDepWithoutDescendants)
)
valueInfoImpl(
schema,
value,
AbsolutePath(resolvedDepWithoutDescendants.relativeTo(absolutePath))
)
.single()
.value
// If the dependency value is explicitely provided but differs from the one
// obtained from [value] then we throw, since otherwise we'd have to pick one of
// them as the source of truth, which seems arbitrary
.also {
require(
dependencyValues == null ||
name !in dependencyValues ||
dependencyValues[name] == it
) {
"Provided value of dependency '$name' <${dependencyValues!![name]}> " +
"does not match its counterpart value within the value being " +
"validated <$it>.\n" +
"Because dependency '$name' is part of the value being " +
"validated, you should either omit it from the provided " +
"dependencies map or make sure that its value matches its " +
"counterpart within the value being validated."
}
}
else dependencyValues?.get(name)
val depInfo = schemaInfoImpl(formSchema, resolvedDepWithoutDescendants).single()
@Suppress("UNCHECKED_CAST")
ValueInfo(
depValue,
depInfo.schema as Schema,
resolvedDepWithoutDescendants,
depInfo.path
)
}