diff --git a/build.gradle b/build.gradle index 0ff4255..1ba8474 100644 --- a/build.gradle +++ b/build.gradle @@ -1,62 +1,64 @@ -buildscript { repositories { mavenCentral() } } +buildscript { repositories { mavenCentral() + } } -repositories { mavenCentral() } +allprojects { -apply plugin: 'java' -apply plugin: 'eclipse' -apply plugin: 'idea' -apply plugin: 'maven' -apply plugin: 'jacoco' + repositories { mavenCentral() } -group = 'com.github.jeffnelson' -version = currentVersion -archivesBaseName = project.name + apply plugin: 'java' + apply plugin: 'eclipse' + apply plugin: 'idea' + apply plugin: 'maven' + apply plugin: 'jacoco' -sourceCompatibility = 1.8 + group = 'com.github.jeffnelson' + version = currentVersion + archivesBaseName = project.name -dependencies { - compile 'com.fasterxml.jackson.core:jackson-databind:2.8.11' + sourceCompatibility = 1.8 - compileOnly 'org.projectlombok:lombok:1.16.22' + dependencies { + compile 'com.fasterxml.jackson.core:jackson-databind:2.8.11' - testCompileOnly 'org.projectlombok:lombok:1.16.22' - testCompile 'org.springframework.boot:spring-boot-starter-test:1.5.4.RELEASE' - testCompile 'org.springframework.boot:spring-boot-starter-web:1.5.4.RELEASE' - testCompile 'com.google.guava:guava:28.0-jre' - testCompile 'commons-io:commons-io:2.6' -} - -jacocoTestCoverageVerification { - violationRules { - rule { - element = 'CLASS' - includes = [ - 'com.github.jeffnelson.jackson.patch.deser.PatchFieldDeserializer' - ] - excludes = [] + compileOnly 'org.projectlombok:lombok:1.16.22' - limit { minimum = 1.0 } - } + testCompileOnly 'org.projectlombok:lombok:1.16.22' + testCompile 'org.springframework.boot:spring-boot-starter-test:1.5.4.RELEASE' + testCompile 'org.springframework.boot:spring-boot-starter-web:1.5.4.RELEASE' + testCompile 'com.google.guava:guava:28.0-jre' + testCompile 'commons-io:commons-io:2.6' } -} -// always run jacocoTestReport and coverageVerification -test.finalizedBy(jacocoTestReport) -jacocoTestReport.finalizedBy(jacocoTestCoverageVerification) + jacocoTestCoverageVerification { + violationRules { + rule { + element = 'CLASS' + includes = [ + 'com.github.jeffnelson.jackson.patch.deser.PatchFieldDeserializer' + ] + excludes = [] -jacocoTestReport { - reports { - xml.enabled false - csv.enabled false - html.destination "${buildDir}/jacocoHtml" + limit { minimum = 1.0 } + } + } } -} -task sourcesJar(type: Jar, dependsOn: classes) { - classifier = 'sources' - from sourceSets.main.allSource -} + // always run jacocoTestReport and coverageVerification + test.finalizedBy(jacocoTestReport) + jacocoTestReport.finalizedBy(jacocoTestCoverageVerification) -artifacts { archives sourcesJar } + jacocoTestReport { + reports { + xml.enabled false + csv.enabled false + html.destination "${buildDir}/jacocoHtml" + } + } + task sourcesJar(type: Jar, dependsOn: classes) { + classifier = 'sources' + from sourceSets.main.allSource + } + artifacts { archives sourcesJar } +} diff --git a/gradle.properties b/gradle.properties index f2df294..3a15752 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1 @@ -currentVersion=1.0.0-SNAPSHOT +currentVersion=1.0.1-SNAPSHOT diff --git a/jackson-merge-patch-validations/build.gradle b/jackson-merge-patch-validations/build.gradle new file mode 100644 index 0000000..77ec3f2 --- /dev/null +++ b/jackson-merge-patch-validations/build.gradle @@ -0,0 +1,6 @@ +dependencies { + compile rootProject + + compile 'org.apache.commons:commons-lang3:3.5' + compile 'javax.validation:validation-api:1.1.0.Final' +} diff --git a/jackson-merge-patch-validations/src/main/java/com/github/jeffnelson/jackson/patch/validator/PatchStringValidator.java b/jackson-merge-patch-validations/src/main/java/com/github/jeffnelson/jackson/patch/validator/PatchStringValidator.java new file mode 100644 index 0000000..2abd0c8 --- /dev/null +++ b/jackson-merge-patch-validations/src/main/java/com/github/jeffnelson/jackson/patch/validator/PatchStringValidator.java @@ -0,0 +1,42 @@ + +package com.github.jeffnelson.jackson.patch.validator; + +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; + +import org.apache.commons.lang3.StringUtils; + +import com.github.jeffnelson.jackson.patch.PatchField; +import com.github.jeffnelson.jackson.patch.validator.constraints.PatchStringValid; + +public class PatchStringValidator implements ConstraintValidator> { + + boolean allowNull, allowEmpty, allowBlank; + + @Override + public void initialize(PatchStringValid anno) { + this.allowNull = anno.allowNull(); + this.allowEmpty = anno.allowEmpty(); + this.allowBlank = anno.allowBlank(); + } + + @Override + public boolean isValid(PatchField value, ConstraintValidatorContext context) { + if (value.shouldPatch()) { + String val = value.getValue(); + if (!allowBlank) { + // this involves the most checks. isNotBlank requires not blank, not empty, and not null + return StringUtils.isNotBlank(val); + } + if (!allowEmpty) { + // isNotEmpty requires not empty and not null + return StringUtils.isNotEmpty(val); + } + if (!allowNull) { + return val != null; + } + } + return true; + } + +} diff --git a/jackson-merge-patch-validations/src/main/java/com/github/jeffnelson/jackson/patch/validator/constraints/PatchStringValid.java b/jackson-merge-patch-validations/src/main/java/com/github/jeffnelson/jackson/patch/validator/constraints/PatchStringValid.java new file mode 100644 index 0000000..bd33a87 --- /dev/null +++ b/jackson-merge-patch-validations/src/main/java/com/github/jeffnelson/jackson/patch/validator/constraints/PatchStringValid.java @@ -0,0 +1,45 @@ + +package com.github.jeffnelson.jackson.patch.validator.constraints; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import javax.validation.Constraint; +import javax.validation.Payload; + +import com.github.jeffnelson.jackson.patch.PatchField; +import com.github.jeffnelson.jackson.patch.validator.PatchStringValidator; + +/** + * Annotation to apply validation to {@literal PatchField} + *

+ * Default behavior. If {@link PatchField#shouldPatch()} returns true, + *

    + *
  • do not allow null + *
  • do not allow empty + *
  • do not allow blank + *
+ * + * @author jeff.nelson + * + */ +@Constraint(validatedBy = PatchStringValidator.class) +@Retention(RUNTIME) +@Target(FIELD) +public @interface PatchStringValid { + + String message() default "Required"; + + boolean allowNull() default false; + + boolean allowEmpty() default false; + + boolean allowBlank() default false; + + Class[] groups() default {}; + + Class[] payload() default {}; +} diff --git a/jackson-merge-patch-validations/src/test/java/com/github/jeffnelson/jackson/patch/validator/PatchStringValidatorTest.java b/jackson-merge-patch-validations/src/test/java/com/github/jeffnelson/jackson/patch/validator/PatchStringValidatorTest.java new file mode 100644 index 0000000..91e8e3f --- /dev/null +++ b/jackson-merge-patch-validations/src/test/java/com/github/jeffnelson/jackson/patch/validator/PatchStringValidatorTest.java @@ -0,0 +1,135 @@ + +package com.github.jeffnelson.jackson.patch.validator; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Before; +import org.junit.Test; + +import com.github.jeffnelson.jackson.patch.PatchField; + +public class PatchStringValidatorTest { + + private PatchStringValidator validator; + + @Before + public void setup() { + validator = new PatchStringValidator(); + } + + @Test + public void testNoPatch() { + PatchField value = PatchField. builder().shouldPatch(false).build(); + + validator.allowBlank = false; + validator.allowEmpty = false; + validator.allowNull = false; + + assertTrue(validator.isValid(value, null)); + + validator.allowBlank = true; + + assertTrue(validator.isValid(value, null)); + + validator.allowEmpty = true; + + assertTrue(validator.isValid(value, null)); + + validator.allowNull = true; + + assertTrue(validator.isValid(value, null)); + } + + @Test + public void testPatch_helloWorld() { + PatchField value = PatchField. builder().shouldPatch(true).value("hello, world").build(); + + validator.allowBlank = false; + validator.allowEmpty = false; + validator.allowNull = false; + + assertTrue(validator.isValid(value, null)); + + validator.allowBlank = true; + + assertTrue(validator.isValid(value, null)); + + validator.allowEmpty = true; + + assertTrue(validator.isValid(value, null)); + + validator.allowNull = true; + + assertTrue(validator.isValid(value, null)); + } + + @Test + public void testPatch_null() { + PatchField value = PatchField. builder().shouldPatch(true).value(null).build(); + + validator.allowBlank = false; + validator.allowEmpty = false; + validator.allowNull = false; + + assertFalse(validator.isValid(value, null)); + + validator.allowBlank = true; + + assertFalse(validator.isValid(value, null)); + + validator.allowEmpty = true; + + assertFalse(validator.isValid(value, null)); + + validator.allowNull = true; + + assertTrue(validator.isValid(value, null)); + } + + @Test + public void testPatch_empty() { + PatchField value = PatchField. builder().shouldPatch(true).value("").build(); + + validator.allowBlank = false; + validator.allowEmpty = false; + validator.allowNull = false; + + assertFalse(validator.isValid(value, null)); + + validator.allowBlank = true; + + assertFalse(validator.isValid(value, null)); + + validator.allowEmpty = true; + + assertTrue(validator.isValid(value, null)); + + validator.allowNull = true; + + assertTrue(validator.isValid(value, null)); + } + + @Test + public void testPatch_blank() { + PatchField value = PatchField. builder().shouldPatch(true).value(" ").build(); + + validator.allowBlank = false; + validator.allowEmpty = false; + validator.allowNull = false; + + assertFalse(validator.isValid(value, null)); + + validator.allowBlank = true; + + assertTrue(validator.isValid(value, null)); + + validator.allowEmpty = true; + + assertTrue(validator.isValid(value, null)); + + validator.allowNull = true; + + assertTrue(validator.isValid(value, null)); + } +} diff --git a/settings.gradle b/settings.gradle index 8b13789..88aa427 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1 @@ - +include 'jackson-merge-patch-validations'