Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 55 additions & 49 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,7 @@
[![License](https://img.shields.io/github/license/akash-kansara/modak)](LICENSE)
[![Maven Central](https://img.shields.io/maven-central/v/io.github.akash-kansara/modak-core)](https://search.maven.org/search?q=g:io.github.akash-kansara)

Modak is a companion library to [Jakarta Bean Validation](https://beanvalidation.org/).
**Bean Validation** defines **constraints** and checks whether your object model is valid.
**Modak** focuses only on **data correction**: applying annotation-based rules to automatically fix data.

If you are already using Bean Validation (Hibernate Validator, Apache BVal, or any implementation) and need automatic correction/sanitization, Modak integrates seamlessly as a drop-in companion.

It can also be used standalone, without Bean Validation, whenever you need annotation-driven data correction.

Main features:
Modak is a library that helps you define data correction rules and provides APIs to correct data in your objects based on those rules. Main features:

1. Lets you express data correction rules on object models via annotations
2. Lets you write custom constraint in an extensible way
Expand All @@ -28,45 +20,24 @@ Main features:

## πŸ“¦ Installation

Available on [Maven Central](https://central.sonatype.com/artifact/io.github.akash-kansara/modak-core?smo=true)

### Gradle (Kotlin DSL)
```kotlin
dependencies {
// Add only if you're not using bean validation already
implementation("jakarta.validation:jakarta.validation-api:3.1.0")

// Modak dependencies
implementation("io.github.akash-kansara:modak-api:$version")
implementation("io.github.akash-kansara:modak-core:$version")
}
```

### Gradle (Groovy DSL)
```groovy
dependencies {
// Add only if you're not using bean validation already
implementation("jakarta.validation:jakarta.validation-api:3.1.0")

// Modak dependencies
implementation 'io.github.akash-kansara:modak-api:$version'
implementation 'io.github.akash-kansara:modak-core:$version'
}
```

### Maven
```xml
<!-- Add only if you're not using bean validation already -->
<dependency>
<groupId>jakarta.validation</groupId>
<artifactId>jakarta.validation-api</artifactId>
<version>3.1.0</version>
</dependency>

<!-- Modak dependencies -->
<dependency>
<groupId>io.github.akash-kansara</groupId>
<artifactId>modak-api</artifactId>
<version>VERSION</version>
</dependency>
<dependency>
<groupId>io.github.akash-kansara</groupId>
<artifactId>modak-core</artifactId>
Expand All @@ -79,7 +50,7 @@ dependencies {
### 1. Define your corrections

```java
// Annotation:
// Correction annotation:
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Correction(correctedBy = {UserCorrectionApplier.class})
Expand Down Expand Up @@ -118,20 +89,16 @@ public class UserCorrectionApplier implements CorrectionApplier<UserCorrection,
### 2. Define your model with corrections

```java
@UserCorrection(
@UserCorrection( // Your custom correction rule
defaultRole = "DEFAULT",
adminRole = "ADMIN"
)
public class User {
@Trim // Provided by library
@DefaultValue(strValue = "Anonymous") // If you're using getter/setter, you can annotate the getter instead of fields
public String name;
public String name; // public modifier is required

@NotNull
@DefaultValue( // Provided by library
intValue = 18,
constraintFilter = {NotNull.class}
)
@DefaultValue(intValue = 18) // Provided by library
public Integer age;

public String role;
Expand Down Expand Up @@ -161,24 +128,63 @@ User user = new User(null, null, null, "example@com!pany.com");
CorrectionResult<User, ErrorLike> result = corrector.correct(user);

System.out.println(result.isSuccess()); // true
CorrectionResult.Success<User> successResult = (CorrectionResult.Success<User>) result;
System.out.println( // 4
successResult.getAppliedCorrections().size()
);
System.out.println(user); // User{name='Anonymous', age=18, role='ADMIN', email='example@company.com'}

```

if (result instanceof CorrectionResult.Success<User, ErrorLike> success) {
System.out.println( // 4
success.getAppliedCorrections().size()
);
System.out.println(user); // User{name='Anonymous', age=18, role='ADMIN', email='example@company.com'}
## πŸ”— Synergy with Bean Validation

Modak is a companion library to [Jakarta Bean Validation](https://beanvalidation.org/). Its scope is limited to data correction and does not provide data validation features, but it seamlessly integrates with bean validation.
You can use any bean validation such as [Hibernate Validator](https://hibernate.org/validator/), [Apache BVal](https://bval.apache.org/) along with Modak.

### Bean Validation Example

```java
public class User {
private String name;

public User(String name) {
this.name = name;
}

@NotNull // Jakarta Bean Validation constraint
@DefaultValue(
strValue = "Anonymous",
constraintFilter = {NotNull.class} // Only apply if NotNull constraint fails
)
public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
}

User user = new User(null);
Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
Set<ConstraintViolation<User>> violations = validator.validate(user);
Corrector corrector = CorrectorFactory.buildCorrector();
CorrectionResult<User, ErrorLike> result = corrector.correct( // Since violations are supplied, correction will be applied only if NotNull constraint has failed
user,
violations
);
System.out.println(user.getName()); // Anonymous
```

## πŸ“š Documentation

**πŸ“– [Full Documentation](docs/API.md)**
**πŸ“– [Full Documentation](docs/REFERENCE_GUIDE.md)**

## ✨ Key Features

πŸ”§ **Automatic Data Correction** - Fix data issues
πŸ”§ **Automatic Data Correction** - Automatically fix inconsistent or invalid data

πŸ“ **Annotation-Based** - Simple annotations to define correction rules
πŸ“ **Annotation-Based** - Use simple, declarative annotations to define correction rules

πŸ”— **Jakarta Validation Integration** - Works with existing validation constraints

Expand All @@ -204,4 +210,4 @@ Licensed under the terms in the [LICENSE](LICENSE) file.

---

**Need help?** [Open an issue](https://github.com/akash-kansara/modak/issues) or check the [full documentation](docs/API.md).
**Need help?** [Open an issue](https://github.com/akash-kansara/modak/issues) or check the [full documentation](docs/REFERENCE_GUIDE.md).
3 changes: 2 additions & 1 deletion SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@

| Version | Supported |
|---------|--------------------|
| 1.1.x | :white_check_mark: |
| 1.2.x | :white_check_mark: |
| 1.1.x | :warning: |
| 1.0.x | :warning: |

## Reporting a Vulnerability
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package io.github.akashkansara.modak.api
/**
* Result of a correction operation performed by a Corrector.
*/
sealed class CorrectionResult<out T, out E : ErrorLike> {
sealed class CorrectionResult<out T> {
/**
* Indicates whether the correction operation was successful.
*/
Expand All @@ -17,14 +17,14 @@ sealed class CorrectionResult<out T, out E : ErrorLike> {
*/
class Success<T> (
val appliedCorrections: List<AppliedCorrection<T>>,
) : CorrectionResult<T, Nothing>()
) : CorrectionResult<T>()

/**
* Failed correction result.
*
* @param error The error that occurred during correction
*/
class Failure<E : ErrorLike> (
val error: E,
) : CorrectionResult<Nothing, E>()
class Failure(
val error: Error,
) : CorrectionResult<Nothing>()
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ interface Corrector {
fun <T> correct(
obj: T,
vararg groups: Class<*>,
): CorrectionResult<T, ErrorLike>
): CorrectionResult<T>

/**
* Applies corrections to the given object, considering existing constraint violations.
Expand All @@ -30,5 +30,5 @@ interface Corrector {
obj: T,
constraintViolations: Set<ConstraintViolation<T>>,
vararg groups: Class<*>,
): CorrectionResult<T, ErrorLike>
): CorrectionResult<T>
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package io.github.akashkansara.modak.api
/**
* Represents an error that occurred during correction operations.
*/
interface ErrorLike {
interface Error {
/** Error message describing what went wrong */
val message: String

Expand Down
2 changes: 1 addition & 1 deletion core/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ dependencies {

implementation(libs.arrow.kt.core)
implementation(libs.guava)
implementation(libs.jakarta.validation.api)
api(libs.jakarta.validation.api)

testImplementation(libs.junit.jupiter)
testImplementation(libs.hibernate.validator)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,16 @@ import arrow.core.Either
import io.github.akashkansara.modak.api.AppliedCorrection
import io.github.akashkansara.modak.api.CorrectionResult
import io.github.akashkansara.modak.api.Corrector
import io.github.akashkansara.modak.api.ErrorLike
import io.github.akashkansara.modak.core.beanmodification.BeanModifier
import io.github.akashkansara.modak.core.models.ErrorLikeImpl
import io.github.akashkansara.modak.core.models.ErrorImpl
import io.github.akashkansara.modak.core.models.InternalError
import jakarta.validation.ConstraintViolation

class CorrectorImpl(private val beanModifier: BeanModifier) : Corrector {
override fun <T> correct(
obj: T,
vararg groups: Class<*>,
): CorrectionResult<T, ErrorLike> {
): CorrectionResult<T> {
val groupList = getGroupList(*groups)
val modifyBeanResult = beanModifier.modifyBean(obj, null, groupList)
return processModificationResult(modifyBeanResult)
Expand All @@ -24,7 +23,7 @@ class CorrectorImpl(private val beanModifier: BeanModifier) : Corrector {
obj: T,
constraintViolations: Set<ConstraintViolation<T>>,
vararg groups: Class<*>,
): CorrectionResult<T, ErrorLike> {
): CorrectionResult<T> {
val groupList = getGroupList(*groups)
val modifyBeanResult = beanModifier.modifyBean(obj, constraintViolations, groupList)
return processModificationResult(modifyBeanResult)
Expand All @@ -40,15 +39,15 @@ class CorrectorImpl(private val beanModifier: BeanModifier) : Corrector {

private fun <T> processModificationResult(
result: Either<InternalError, List<AppliedCorrection<T>>>,
): CorrectionResult<T, ErrorLike> {
): CorrectionResult<T> {
return result.fold(
ifLeft = {
val appliedCorrections = when (it) {
is InternalError.BeanModificationError -> it.appliedCorrections
else -> emptyList()
}
CorrectionResult.Failure(
ErrorLikeImpl(
ErrorImpl(
message = it.message,
cause = it.cause,
appliedCorrections = appliedCorrections,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package io.github.akashkansara.modak.core.models

import io.github.akashkansara.modak.api.AppliedCorrection
import io.github.akashkansara.modak.api.ErrorLike
import io.github.akashkansara.modak.api.Error

data class ErrorLikeImpl(
data class ErrorImpl(
override val cause: Throwable? = null,
override val message: String,
override val appliedCorrections: List<AppliedCorrection<*>> = emptyList(),
) : ErrorLike
) : Error
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,9 @@ class GroupSequenceGeneratorTest {
val result = generator.generateGroupSequence(inputGroups)
assertTrue(result.isRight())
val groups = result.getOrNull()!!
assertTrue(groups.contains(RegionalBranchGroup::class.java))
assertTrue(groups.contains(BranchGroup::class.java))
assertEquals(2, groups.size)
assertEquals(BranchGroup::class.java, groups[0])
assertEquals(RegionalBranchGroup::class.java, groups[1])
}

@Test
Expand Down
Loading
Loading