├── .asciidoctor └── docinfo.html ├── .asciidoctorconfig ├── .classpath ├── .editorconfig ├── .factorypath ├── .github └── workflows │ └── build.yml ├── .gitignore ├── .idea ├── GitLink.xml ├── JetClient │ └── state.xml ├── codeStyles │ ├── Project.xml │ └── codeStyleConfig.xml ├── cody_history.xml ├── detekt.xml ├── git_toolbox_blame.xml ├── git_toolbox_prj.xml ├── icon.png ├── inspectionProfiles │ └── Project_Default.xml ├── jpa-buddy.xml ├── kotlinc.xml ├── ktfmt.xml ├── scala_compiler.xml └── vcs.xml ├── .project ├── .settings ├── org.eclipse.buildship.core.prefs ├── org.eclipse.jdt.apt.core.prefs ├── org.eclipse.jdt.core.prefs └── org.eclipse.m2e.core.prefs ├── .vscode └── settings.json ├── BUILD.bazel ├── CODEOWNERS ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.adoc ├── LICENSE ├── README.adoc ├── SECURITY.md ├── WORKSPACE ├── build.gradle.kts ├── buildSrc ├── build.gradle.kts ├── settings.gradle.kts └── src │ └── main │ └── kotlin │ ├── Config.kt │ ├── predef.kt │ ├── vador.kt-conventions.gradle.kts │ ├── vador.publishing-conventions.gradle.kts │ ├── vador.root-conventions.gradle.kts │ └── vador.sub-conventions.gradle.kts ├── detekt ├── baseline.xml └── config.yml ├── docs ├── api │ ├── vador-batch │ │ ├── validate-and-fail-fast-for-any-with-pair.adoc │ │ ├── validate-and-fail-fast-for-any.adoc │ │ ├── validate-and-fail-fast-for-each-with-pair.adoc │ │ └── validate-and-fail-fast-for-each.adoc │ └── vador │ │ └── validate-and-fail-fast.adoc ├── config-dsl │ ├── BatchValidationConfig.adoc │ ├── ContainerValidationConfig.adoc │ ├── ContainerValidationConfigWith2Levels.adoc │ ├── FilterDuplicatesConfig.adoc │ ├── IDConfig.adoc │ ├── ValidationConfig.adoc │ ├── config-driven-validation.adoc │ ├── nested │ │ ├── BatchOfBatch1ValidationConfig.adoc │ │ └── nested-validation.adoc │ └── specs.adoc ├── images │ ├── api.png │ ├── config-dsl │ │ ├── batch-of-batch-1.png │ │ ├── bean-batch.png │ │ ├── bean-nested.png │ │ ├── bean.png │ │ ├── config-1-1-validatable.png │ │ ├── container-level-2.png │ │ ├── container.png │ │ ├── hierarchical-validation.png │ │ ├── multi-filter.png │ │ └── spec-spectrum.png │ ├── config-spectrum.png │ ├── fcwfp-play-poster.jpeg │ ├── function-call-mess.png │ ├── more-power.gif │ └── vav-poster.png ├── matchers.adoc ├── presentations │ └── 2021-08-cancellation-api │ │ ├── 1-index.adoc │ │ ├── 10-track-record.adoc │ │ ├── 11-stability-and-support.adoc │ │ ├── 12-next-steps.adoc │ │ ├── 2-validatable.adoc │ │ ├── 3-config-driven-validation.adoc │ │ ├── 4-bsg-container.adoc │ │ ├── 5-root-container-with-2-levels.adoc │ │ ├── 6-bsg-container-compose.adoc │ │ ├── 7-bsg-batch.adoc │ │ ├── 8-refItem-batch.adoc │ │ ├── 9-retro-on-benefits.adoc │ │ └── images │ │ ├── bs-coverage-numbers.png │ │ ├── bsg-batch.png │ │ ├── bsg-container-with-2-levels.png │ │ ├── bsg-container.png │ │ └── refItem-batch.png ├── requirements.adoc ├── validation-sharing.adoc └── validator-types.adoc ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── libs.versions.toml ├── lombok.config ├── matchers ├── build.gradle.kts └── src │ ├── main │ └── kotlin │ │ └── com │ │ └── salesforce │ │ └── vador │ │ ├── matchers │ │ ├── AnyMatchers.kt │ │ ├── DateMatchers.kt │ │ └── IntMatchers.kt │ │ └── relates │ │ └── Relates.kt │ └── test │ ├── java │ └── com │ │ └── salesforce │ │ └── vador │ │ └── matchers │ │ ├── AnyMatchersTest.java │ │ └── DateMatchersTest.kt │ └── resources │ └── log4j2.xml ├── renovate.json ├── settings.gradle.kts ├── sonar-project.properties └── vador ├── build.gradle.kts └── src ├── main └── java │ └── com │ └── salesforce │ └── vador │ ├── annotation │ ├── MaxForInt.kt │ ├── MinForInt.kt │ ├── Negative.kt │ ├── NonNegative.kt │ ├── Positive.kt │ ├── Required.kt │ └── ValidateWith.kt │ ├── config │ ├── BatchOfBatch1ValidationConfig.java │ ├── BatchValidationConfig.java │ ├── FieldConfig.java │ ├── FilterDuplicatesConfig.java │ ├── IDConfig.java │ ├── ValidationConfig.java │ ├── base │ │ ├── BaseBatchValidationConfig.java │ │ ├── BaseContainerValidationConfig.java │ │ ├── BaseValidationConfig.java │ │ └── BaseValidationConfigEx.kt │ └── container │ │ ├── ContainerValidationConfig.java │ │ ├── ContainerValidationConfigEx.kt │ │ └── ContainerValidationConfigWith2Levels.java │ ├── execution │ ├── Vador.kt │ ├── VadorBatch.kt │ └── strategies │ │ ├── AccumulationStrategies.kt │ │ ├── AnnotationProcessor.kt │ │ ├── FailFastStrategies.kt │ │ └── util │ │ ├── ConfigToValidators.kt │ │ └── Utils.kt │ ├── lift │ ├── AggregationLiftEtrUtil.kt │ ├── AggregationLiftUtil.kt │ ├── InheritanceLiftEtrUtil.kt │ └── ValidatorLiftUtil.kt │ ├── specs │ ├── Destructures.kt │ ├── factory │ │ └── SpecFactory.java │ └── specs │ │ ├── Spec1.java │ │ ├── Spec2.java │ │ ├── Spec3.java │ │ ├── Spec4.java │ │ ├── Spec5.java │ │ ├── SpecEx.kt │ │ └── base │ │ └── BaseSpec.java │ └── types │ ├── Specs.kt │ ├── Validators.kt │ └── failures │ ├── FFABatchOfBatchFailureWithPair.kt │ ├── FFEBatchOfBatchFailure.kt │ └── FFEBatchOfBatchFailureWithPair.kt └── test ├── java ├── com │ └── salesforce │ │ └── vador │ │ ├── execution │ │ ├── UtilsTest.java │ │ ├── VadorAnnotationTest.java │ │ ├── VadorBatchTest.java │ │ ├── VadorTest.java │ │ ├── config │ │ │ ├── BaseValidationConfigTest.java │ │ │ ├── ContainerValidationConfigTest.java │ │ │ ├── IDConfigTest.java │ │ │ └── nested │ │ │ │ ├── BatchOfBatch1ValidationConfigTest.java │ │ │ │ ├── ContainerValidationConfigWith2LevelsTest.java │ │ │ │ └── FieldConfigTest.java │ │ └── spec │ │ │ ├── Spec1Test.java │ │ │ ├── Spec2Test.java │ │ │ ├── Spec3Test.java │ │ │ ├── Spec4Test.java │ │ │ └── Spec5Test.java │ │ ├── lift │ │ ├── AggregationLiftEtrUtilTest.java │ │ ├── AggregationLiftUtilTest.java │ │ ├── InheritanceLiftEtrUtilKtTest.java │ │ └── ValidatorLiftUtilTest.java │ │ └── specs │ │ ├── factory │ │ └── SpecFactoryTest.java │ │ └── failure │ │ ├── ValidationFailure.java │ │ └── ValidationFailureMessage.java └── sample │ └── consumer │ ├── BaseService.java │ ├── Service.java │ ├── bean │ ├── Container.java │ ├── ID.java │ ├── Member.java │ └── Parent.java │ ├── config │ ├── ConfigForValidators.java │ └── ServiceConfig.java │ ├── failure │ ├── ValidationFailure.java │ └── ValidationFailureMessage.java │ └── validators │ ├── BeanValidator.java │ ├── etr │ ├── BaseParentValidatorEtr.java │ ├── ContainerValidatorEtr.java │ └── MemberValidatorEtr.java │ └── simple │ ├── BaseParentValidator.java │ ├── ContainerValidator.java │ └── MemberValidator.java ├── kotlin └── com │ └── salesforce │ └── vador │ ├── annotation │ └── TestAnnotation.kt │ └── lift │ └── AggregationLiftUtilKtTest.kt └── resources └── log4j2.xml /.asciidoctor/docinfo.html: -------------------------------------------------------------------------------- 1 | 14 | -------------------------------------------------------------------------------- /.asciidoctorconfig: -------------------------------------------------------------------------------- 1 | :experimental: 2 | :linkcss: 3 | :docinfodir: {asciidoctorconfigdir}/.asciidoctor 4 | :docinfo: shared 5 | :icons: font 6 | ifdef::env-github[] 7 | :tip-caption: :bulb: 8 | :note-caption: :information_source: 9 | :important-caption: :heavy_exclamation_mark: 10 | :caution-caption: :fire: 11 | :warning-caption: :warning: 12 | endif::[] 13 | :listing-caption: Snippet 14 | :sectnums: 15 | :source-linenums-option: 16 | -------------------------------------------------------------------------------- /.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Run Gradle on Push 2 | on: push 3 | jobs: 4 | gradle: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - uses: actions/checkout@v4 8 | - uses: actions/setup-java@v4 9 | with: 10 | distribution: temurin 11 | java-version: 11 12 | 13 | - name: Setup Gradle 14 | uses: gradle/gradle-build-action@v3 15 | 16 | - name: Execute Gradle build 17 | run: ./gradlew build 18 | -------------------------------------------------------------------------------- /.idea/GitLink.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/JetClient/state.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /.idea/cody_history.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /.idea/detekt.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | -------------------------------------------------------------------------------- /.idea/git_toolbox_blame.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/git_toolbox_prj.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 14 | 15 | -------------------------------------------------------------------------------- /.idea/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/salesforce-misc/Vador/794cdb77d4574c128ed6c0f7137ed0d00c799584/.idea/icon.png -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /.idea/jpa-buddy.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/kotlinc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/ktfmt.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | -------------------------------------------------------------------------------- /.idea/scala_compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | batch-validation-framework 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | org.eclipse.buildship.core.gradleprojectbuilder 15 | 16 | 17 | 18 | 19 | org.eclipse.m2e.core.maven2Builder 20 | 21 | 22 | 23 | 24 | 25 | org.eclipse.jdt.core.javanature 26 | org.eclipse.m2e.core.maven2Nature 27 | org.eclipse.buildship.core.gradleprojectnature 28 | 29 | 30 | 31 | 1664781378798 32 | 33 | 30 34 | 35 | org.eclipse.core.resources.regexFilterMatcher 36 | node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /.settings/org.eclipse.buildship.core.prefs: -------------------------------------------------------------------------------- 1 | arguments=--init-script /var/folders/yp/74lzsmp17vqc5hzkxxb1vd1c0000gp/T/d146c9752a26f79b52047fb6dc6ed385d064e120494f96f08ca63a317c41f94c.gradle --init-script /var/folders/yp/74lzsmp17vqc5hzkxxb1vd1c0000gp/T/52cde0cfcf3e28b8b7510e992210d9614505e0911af0c190bd590d7158574963.gradle --init-script /var/folders/yp/74lzsmp17vqc5hzkxxb1vd1c0000gp/T/bd95fba452cd16942039615189e617a1932f63d661f1f661840274c40bc05cbb.gradle 2 | auto.sync=true 3 | build.scans.enabled=false 4 | connection.gradle.distribution=GRADLE_DISTRIBUTION(LOCAL_INSTALLATION(/Users/gopala.akshintala/.sdkman/candidates/gradle/current)) 5 | connection.project.dir= 6 | eclipse.preferences.version=1 7 | gradle.user.home= 8 | java.home=/Users/gopala.akshintala/.sdkman/candidates/java/current 9 | jvm.arguments= 10 | offline.mode=false 11 | override.workspace.settings=true 12 | show.console.view=true 13 | show.executions.view=true 14 | -------------------------------------------------------------------------------- /.settings/org.eclipse.jdt.apt.core.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | org.eclipse.jdt.apt.aptEnabled=false 3 | org.eclipse.jdt.apt.genSrcDir=target/generated-sources/annotations 4 | org.eclipse.jdt.apt.genTestSrcDir=target/generated-test-sources/test-annotations 5 | -------------------------------------------------------------------------------- /.settings/org.eclipse.m2e.core.prefs: -------------------------------------------------------------------------------- 1 | activeProfiles= 2 | eclipse.preferences.version=1 3 | resolveWorkspaceProjects=true 4 | version=1 5 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "cSpell.words": [ 3 | "Bazel", 4 | "Vador", 5 | "Validatable" 6 | ], 7 | "java.compile.nullAnalysis.mode": "automatic" 8 | } 9 | -------------------------------------------------------------------------------- /BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@rules_java//java:defs.bzl", "java_import") 2 | 3 | package(default_visibility = ["//visibility:public"]) 4 | 5 | java_import( 6 | name = "com_salesforce_vador_vador", 7 | jars = glob(["vador/build/libs/vador-*.jar"]), 8 | srcjar = glob(["vador/build/libs/vador-*-sources.jar"])[0], 9 | ) 10 | 11 | java_import( 12 | name = "com_salesforce_vador_vador-matchers", 13 | jars = glob(["matchers/build/libs/matchers-*.jar"]), 14 | srcjar = glob(["matchers/build/libs/matchers-*-sources.jar"])[0], 15 | ) 16 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | #GUSINFO:Rev Delphinus,Rev Delphinus Trust 2 | * 3 | # Comment line immediately above ownership line is reserved for related other information. Please be careful while editing. 4 | #ECCN:Open Source 5 | @overfullstack 6 | -------------------------------------------------------------------------------- /CONTRIBUTING.adoc: -------------------------------------------------------------------------------- 1 | = Contributing 2 | Gopal S Akshintala 3 | :Revision: 1.0 4 | ifdef::env-github[] 5 | :tip-caption: :bulb: 6 | :note-caption: :information_source: 7 | :important-caption: :heavy_exclamation_mark: 8 | :caution-caption: :fire: 9 | :warning-caption: :warning: 10 | endif::[] 11 | :icons: font 12 | ifdef::env-github[] 13 | :tip-caption: :bulb: 14 | :note-caption: :information_source: 15 | :important-caption: :heavy_exclamation_mark: 16 | :caution-caption: :fire: 17 | :warning-caption: :warning: 18 | endif::[] 19 | :hide-uri-scheme: 20 | :imagesdir: images 21 | :toc: 22 | 23 | [#_versioning_strategy] 24 | == Versioning Strategy 25 | 26 | ==== 27 | .. 28 | ==== 29 | 30 | * A = We broke something on purpose (Breaking API change) 31 | * B = Profit (Feature / Improvement) 32 | * C = We broke something by accident (Bug) 33 | 34 | == Source-code Setup 35 | 36 | === Install Java 11 37 | 38 | It needs JDK 11 installed in your system. 39 | Recommendation is to do it via https://sdkman.io/install[SDKMAN]. 40 | After you install SDKMAN, 41 | run `sdk list java` -> Pick Identifier for your favorite java distribution -> Run `sdk install java ` 42 | to install Java. For example: 43 | 44 | [source,bash] 45 | ---- 46 | sdk install java 11.0.22-tem 47 | ---- 48 | 49 | === Build with Gradle 50 | 51 | * This is a simple Gradle project and has its own Gradle wrapper. So nothing to install. Just run this command 52 | 53 | CAUTION: You may see some build errors related to the delombok task. 54 | They shouldn't fail your build, and you may ignore them. 55 | They will be addressed once Kotlin Lombok compiler supports Lombok `@Builders`. 56 | There is an https://youtrack.jetbrains.com/issue/KT-46959[Active Issue] filed for this as well. 57 | 58 | [source,bash] 59 | ---- 60 | ./gradlew clean build 61 | ---- 62 | 63 | TIP: You *don't* need a local Gradle installation as the `gradlew` (Gradle wrapper) takes care of everything. But if you wish to install gradle locally, the recommendation is to do it via https://sdkman.io/install[SDKMAN]. After you install SDKMAN, run `sdk install gradle` to install Gradle 64 | 65 | * For source code navigation, you need to have https://projectlombok.org/[*Lombok*] plugin, which is used to generate boilerplate code. 66 | There are plugins available for all popular IDEs, which you need to install. 67 | The latest version of the plugin should work. 68 | * You can run/debug the existing unit tests or write your own to play with Vador. 69 | 70 | === Kotlin 71 | 72 | * The code-base is a mix of Java and Kotlin. If you're a Java developer and new to Kotlin, don't worry, Kotlin is a JVM language and can be used anywhere Java is used. 73 | In fact, it has got the reputation of *"Better Java!"*. 74 | * A typical Java developer can ramp up on Kotlin in less than a week. 75 | This free-course: https://www.coursera.org/learn/kotlin-for-java-developers[*Kotlin for Java Developers | Coursera*] can help catalyze your ramp-up. 76 | * If you use Intellij, Kotlin plugin comes bundled. Similar development aids should be present for other code editors too. 77 | 78 | == Code Formatting 79 | 80 | * This repo uses https://github.com/diffplug/spotless[*Spotless*] for formatting files, as this repo has code from more than one programming language. 81 | * Please run `./gradlew spotlessApply` before check-in to fix any formatting errors. 82 | 83 | TIP: If you're on Intellij, replace your kbd:[Cmd+Shift+L] habit with kbd:[Ctrl]-kbd:[Ctrl] and run `gradle spotlessApply` (Or the respective action if you're on Eclipse). 84 | It may be slow for the first run, but later runs should be faster. 85 | 86 | == Manual publishing 87 | 88 | In link:build.gradle.kts[], follow the <<_versioning_strategy,Versioning Strategy>> 89 | 90 | * Follow the <<_versioning_strategy,Versioning Strategy>> to increment version link:buildSrc/{sourcedir}/Config.kt[here] 91 | ** For SNAPSHOT releases, add a `-SNAPSHOT` at the end of version number 92 | * Run this command to publish it to Nexus: 93 | 94 | [source,bash] 95 | ---- 96 | ./gradlew publishToSonatype closeAndReleaseSonatypeStagingRepository -Dorg.gradle.parallel=false --no-configuration-cache 97 | ---- 98 | 99 | * You can monitor for the new version jar to reflect in link:https://repo1.maven.org/maven2/com/salesforce/vador/vador/[Maven Central]. It usually takes less than 30 minutes. 100 | 101 | == Code of Conduct 102 | Please follow our link:CODE_OF_CONDUCT.md[Code of Conduct] 103 | 104 | == License 105 | By contributing your code, 106 | you agree to license your contribution under the terms of our project link:LICENSE[] 107 | and to sign the https://cla.salesforce.com/sign-cla[Salesforce CLA] 108 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2022, Salesforce.com, Inc. 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 11 | 12 | 3. Neither the name of Salesforce.com nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 15 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | ## Security 2 | 3 | Please report any security issue to [security@salesforce.com](mailto:security@salesforce.com) 4 | as soon as it is discovered. This library limits its runtime dependencies in 5 | order to reduce the total cost of ownership as much as can be, but all consumers 6 | should remain vigilant and have their security stakeholders review all third-party 7 | products (3PP) like this one and their dependencies. 8 | -------------------------------------------------------------------------------- /WORKSPACE: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/salesforce-misc/Vador/794cdb77d4574c128ed6c0f7137ed0d00c799584/WORKSPACE -------------------------------------------------------------------------------- /build.gradle.kts: -------------------------------------------------------------------------------- 1 | /** 2 | * **************************************************************************** 3 | * Copyright (c) 2022, salesforce.com, inc. All rights reserved. SPDX-License-Identifier: 4 | * BSD-3-Clause For full license text, see the LICENSE file in the repo root or 5 | * https://opensource.org/licenses/BSD-3-Clause 6 | * **************************************************************************** 7 | */ 8 | import io.freefair.gradle.plugins.lombok.LombokExtension.LOMBOK_VERSION 9 | import io.gitlab.arturbosch.detekt.Detekt 10 | import io.gitlab.arturbosch.detekt.report.ReportMergeTask 11 | 12 | plugins { 13 | `java-library` 14 | id(libs.plugins.detekt.pluginId) apply false 15 | alias(libs.plugins.lombok.gradle) apply false 16 | id(libs.plugins.kover.pluginId) 17 | alias(libs.plugins.nexus.publish) 18 | id("org.sonarqube") version "6.0.1.5171" 19 | } 20 | 21 | allprojects { apply(plugin = "vador.root-conventions") } 22 | 23 | kover { reports { total { html { onCheck = true } } } } 24 | 25 | dependencies { subprojects.forEach { kover(project(":${it.name}")) } } 26 | 27 | val detektReportMerge by 28 | tasks.registering(ReportMergeTask::class) { 29 | output = project.layout.buildDirectory.file("reports/detekt/merge.sarif") 30 | } 31 | 32 | subprojects { 33 | apply(plugin = "vador.sub-conventions") 34 | apply(plugin = "vador.kt-conventions") 35 | apply(plugin = "vador.publishing-conventions") 36 | tasks { 37 | withType().configureEach { 38 | finalizedBy(detektReportMerge) 39 | reports { sarif.required = true } 40 | } 41 | } 42 | detektReportMerge { input.from(tasks.withType().map { it.sarifReportFile }) } 43 | val lombokForSonarQube: Configuration by configurations.creating 44 | dependencies { lombokForSonarQube("org.projectlombok:lombok:$LOMBOK_VERSION") } 45 | sonarqube { 46 | properties { 47 | property("sonar.projectName", name) 48 | property("sonar.sources", "src/main") 49 | property("sonar.tests", "src/test") 50 | property("sonar.java.libraries", lombokForSonarQube.files.last().toString()) 51 | property("sonar.java.binaries", "build/classes") 52 | } 53 | } 54 | } 55 | 56 | sonarqube { 57 | properties { 58 | property("sonar.modules", subprojects.joinToString(",") { it.name }) 59 | property( 60 | "sonar.coverage.jacoco.xmlReportPaths", 61 | rootProject.layout.buildDirectory.file("/build/reports/kover/report.xml"), 62 | ) 63 | property( 64 | "detekt.sonar.kotlin.config.path", 65 | rootProject.layout.buildDirectory.file("/detekt/config.yml"), 66 | ) 67 | property( 68 | "sonar.kotlin.detekt.reportPaths", 69 | rootProject.layout.buildDirectory.file("/build/reports/detekt/merge.xml"), 70 | ) 71 | } 72 | } 73 | 74 | afterEvaluate { 75 | tasks { 76 | check.configure { dependsOn(detektReportMerge) } 77 | sonarqube.configure { dependsOn(check) } 78 | } 79 | } 80 | 81 | nexusPublishing { this.repositories { sonatype { stagingProfileId = STAGING_PROFILE_ID } } } 82 | -------------------------------------------------------------------------------- /buildSrc/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { `kotlin-dsl` } 2 | 3 | repositories { 4 | mavenCentral() 5 | gradlePluginPortal() 6 | } 7 | 8 | dependencies { 9 | implementation(libs.kotlin.gradle) 10 | implementation(libs.spotless.gradle) 11 | implementation(libs.detekt.gradle) 12 | implementation(libs.kover.gradle) 13 | implementation(libs.spotbugs.gradle) 14 | implementation(libs.testLogger.gradle) 15 | } 16 | -------------------------------------------------------------------------------- /buildSrc/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | /** 2 | * **************************************************************************** 3 | * Copyright (c) 2022, salesforce.com, inc. All rights reserved. SPDX-License-Identifier: 4 | * BSD-3-Clause For full license text, see the LICENSE file in the repo root or 5 | * https://opensource.org/licenses/BSD-3-Clause 6 | * **************************************************************************** 7 | */ 8 | dependencyResolutionManagement { 9 | versionCatalogs { create("libs") { from(files("../libs.versions.toml")) } } 10 | 11 | repositories { 12 | mavenCentral() 13 | gradlePluginPortal() 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/Config.kt: -------------------------------------------------------------------------------- 1 | const val GROUP_ID = "com.salesforce.vador" 2 | const val VERSION = "1.1.1-SNAPSHOT" 3 | const val STAGING_PROFILE_ID = "1ea0a23e61ba7d" 4 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/predef.kt: -------------------------------------------------------------------------------- 1 | import org.gradle.api.artifacts.ExternalModuleDependencyBundle 2 | import org.gradle.api.artifacts.VersionCatalog 3 | import org.gradle.api.provider.Property 4 | import org.gradle.api.provider.Provider 5 | import org.gradle.plugin.use.PluginDependency 6 | 7 | val Provider.pluginId: String 8 | get() = get().pluginId 9 | 10 | infix fun Property.by(value: T) { 11 | set(value) 12 | } 13 | 14 | internal val VersionCatalog.jdk 15 | get() = getVersion("jdk") 16 | 17 | internal val VersionCatalog.kotestBundle: Provider 18 | get() = getBundle("kotest") 19 | 20 | private fun VersionCatalog.getLibrary(library: String) = findLibrary(library).get() 21 | 22 | private fun VersionCatalog.getBundle(bundle: String) = findBundle(bundle).get() 23 | 24 | private fun VersionCatalog.getPlugin(plugin: String) = findPlugin(plugin).get() 25 | 26 | private fun VersionCatalog.getVersion(plugin: String) = findVersion(plugin).get() 27 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/vador.kt-conventions.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.gradle.kotlin.dsl.kotlin 2 | 3 | plugins { kotlin("jvm") } 4 | 5 | kotlin { compilerOptions { freeCompilerArgs.addAll("-Xcontext-receivers", "-progressive") } } 6 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/vador.publishing-conventions.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.gradle.api.file.DuplicatesStrategy 2 | import org.gradle.api.publish.maven.MavenPublication 3 | import org.gradle.api.tasks.bundling.Jar 4 | import org.gradle.jvm.toolchain.JavaLanguageVersion 5 | import org.gradle.kotlin.dsl.get 6 | import org.gradle.kotlin.dsl.invoke 7 | import org.gradle.kotlin.dsl.`java-library` 8 | import org.gradle.kotlin.dsl.withType 9 | 10 | plugins { 11 | `maven-publish` 12 | signing 13 | `java-library` 14 | } 15 | 16 | group = GROUP_ID 17 | 18 | version = VERSION 19 | 20 | description = "Vador - A framework for POJO/Data Structure/Bean validation" 21 | 22 | repositories { mavenCentral() } 23 | 24 | java { 25 | withJavadocJar() 26 | withSourcesJar() 27 | toolchain { languageVersion.set(JavaLanguageVersion.of(11)) } 28 | } 29 | 30 | publishing { 31 | publications.create("vador") { 32 | val subprojectJarName = tasks.jar.get().archiveBaseName.get() 33 | artifactId = if (subprojectJarName == "vador") "vador" else "vador-$subprojectJarName" 34 | from(components["java"]) 35 | pom { 36 | name.set(artifactId) 37 | description.set(project.description) 38 | url.set("https://github.com/salesforce-misc/Vador") 39 | inceptionYear.set("2020") 40 | licenses { 41 | license { 42 | name.set("The Apache License, Version 2.0") 43 | url.set("http://www.apache.org/licenses/LICENSE-2.0.txt") 44 | } 45 | } 46 | developers { 47 | developer { 48 | id.set("overfullstack") 49 | name.set("Gopal S Akshintala") 50 | email.set("gopalakshintala@gmail.com") 51 | } 52 | } 53 | scm { 54 | connection.set("scm:git:https://github.com/salesforce-misc/Vador") 55 | developerConnection.set("scm:git:git@github.com/salesforce-misc/vador.git") 56 | url.set("https://github.com/salesforce-misc/Vador") 57 | } 58 | } 59 | } 60 | } 61 | 62 | signing { sign(publishing.publications["vador"]) } 63 | 64 | tasks { 65 | withType { duplicatesStrategy = DuplicatesStrategy.EXCLUDE } 66 | javadoc { 67 | // TODO 22/05/21 gopala.akshintala: Turn this on after writing all javadocs 68 | isFailOnError = false 69 | options.encoding("UTF-8") 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/vador.root-conventions.gradle.kts: -------------------------------------------------------------------------------- 1 | import com.adarshr.gradle.testlogger.theme.ThemeType.MOCHA 2 | import com.diffplug.spotless.LineEnding.PLATFORM_NATIVE 3 | 4 | plugins { 5 | java 6 | idea 7 | id("com.diffplug.spotless") 8 | id("io.gitlab.arturbosch.detekt") 9 | id("com.adarshr.test-logger") 10 | id("com.github.spotbugs") apply false 11 | } 12 | 13 | repositories { mavenCentral() } 14 | 15 | spotless { 16 | lineEndings = PLATFORM_NATIVE 17 | kotlin { 18 | ktfmt().googleStyle() 19 | target("src/*/kotlin/**/*.kt", "src/*/java/**/*.kt") 20 | trimTrailingWhitespace() 21 | endWithNewline() 22 | targetExclude("build/**", ".gradle/**", "generated/**", "bin/**", "out/**", "tmp/**") 23 | } 24 | kotlinGradle { 25 | ktfmt().googleStyle() 26 | trimTrailingWhitespace() 27 | endWithNewline() 28 | targetExclude("build/**", ".gradle/**", "generated/**", "bin/**", "out/**", "tmp/**") 29 | } 30 | java { 31 | toggleOffOn() 32 | target("src/*/java/**/*.java") 33 | importOrder() 34 | removeUnusedImports() 35 | googleJavaFormat() 36 | trimTrailingWhitespace() 37 | indentWithSpaces(2) 38 | endWithNewline() 39 | targetExclude("build/**", ".gradle/**", "generated/**", "bin/**", "out/**", "tmp/**") 40 | } 41 | format("documentation") { 42 | target("*.md", "*.adoc") 43 | trimTrailingWhitespace() 44 | indentWithSpaces(2) 45 | endWithNewline() 46 | } 47 | } 48 | 49 | testlogger.theme = MOCHA 50 | 51 | tasks { 52 | spotbugsMain.get().enabled = false 53 | spotbugsTest.get().enabled = false 54 | spotbugs.ignoreFailures.set(true) 55 | } 56 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/vador.sub-conventions.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.gradle.api.plugins.jvm.JvmTestSuite 2 | import org.gradle.kotlin.dsl.`java-library` 3 | 4 | plugins { 5 | `java-library` 6 | id("org.jetbrains.kotlinx.kover") 7 | id("io.gitlab.arturbosch.detekt") 8 | } 9 | 10 | testing { 11 | suites { 12 | val test by getting(JvmTestSuite::class) { useJUnitJupiter("5.10.2") } 13 | } 14 | } 15 | 16 | detekt { 17 | parallel = true 18 | buildUponDefaultConfig = true 19 | baseline = file("$rootDir/detekt/baseline.xml") 20 | config.setFrom(file("$rootDir/detekt/config.yml")) 21 | ignoreFailures = true 22 | } 23 | -------------------------------------------------------------------------------- /detekt/baseline.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /detekt/config.yml: -------------------------------------------------------------------------------- 1 | build: 2 | maxIssues: 999 3 | comments: 4 | active: false 5 | style: 6 | MaxLineLength: 7 | active: false 8 | -------------------------------------------------------------------------------- /docs/api/vador-batch/validate-and-fail-fast-for-any-with-pair.adoc: -------------------------------------------------------------------------------- 1 | = `validateAndFailFastForAny (with Pair for Failure) 2 | Gopal S Akshintala 3 | :Revision: 1.0 4 | ifdef::env-github[] 5 | :tip-caption: :bulb: 6 | :note-caption: :information_source: 7 | :important-caption: :heavy_exclamation_mark: 8 | :caution-caption: :fire: 9 | :warning-caption: :warning: 10 | endif::[] 11 | :sectnums!: 12 | :sourcedir: ../../../vador/src/main/java 13 | :testdir: ../../../vador/src/test/java 14 | :imagesdir: ../../images 15 | 16 | This execution strategy lets you run validations for your batch data structure 17 | and _fail-fast_ on the first failure for _any_ member in the batch i.e., 18 | if a member fails one of the validations, it stops running further validations on the rest of the members in the batch. 19 | 20 | == Params 21 | 22 | [cols="1,1,1,3"] 23 | |=== 24 | |Param |Data type |Required? |Description 25 | 26 | |`validatables` 27 | |`List` 28 | |Yes 29 | |List of POJO/Beans under validation 30 | 31 | |`pairForInvalidMapper` 32 | |`(ValidatableT) -> PairT` 33 | |Yes 34 | |Function (mostly a getter) whose return value can be used as an identifier to identify the first failed member in `validatables`. 35 | 36 | |`batchValidationConfig` 37 | |`BatchValidationConfig` 38 | |Yes 39 | |Config for validation 40 | 41 | |`failureForNullValidatable` 42 | |`FailureT` 43 | |No 44 | |Failure to return if a member in `validatables` passed is `null` 45 | 46 | |`throwableMapper` 47 | |`(Throwable) -> FailureT?` 48 | |No 49 | |xref:../../../README.adoc#_what_if_there_is_an_exception_during_execution[Refer this section] 50 | 51 | |=== 52 | 53 | == Return 54 | 55 | `Optional>` - First failure for any member paired with the identifier of the failed member. 56 | If all members pass all validations, `Optional.empty()` shall be returned. 57 | -------------------------------------------------------------------------------- /docs/api/vador-batch/validate-and-fail-fast-for-any.adoc: -------------------------------------------------------------------------------- 1 | = `validateAndFailFastForAny` 2 | Gopal S Akshintala 3 | :Revision: 1.0 4 | ifdef::env-github[] 5 | :tip-caption: :bulb: 6 | :note-caption: :information_source: 7 | :important-caption: :heavy_exclamation_mark: 8 | :caution-caption: :fire: 9 | :warning-caption: :warning: 10 | endif::[] 11 | :sectnums!: 12 | :sourcedir: ../../../vador/src/main/java 13 | :testdir: ../../../vador/src/test/java 14 | :imagesdir: ../../images 15 | 16 | This execution strategy lets you run validations for your batch data structure 17 | and _fails-fast_ on the first failure for _any_ member in the batch i.e., 18 | if a member fails one of the validations, it stops running further validations on the rest of the members in the batch. 19 | 20 | == Params 21 | 22 | [cols="1,1,1,3"] 23 | |=== 24 | |Param |Data type |Required? |Description 25 | 26 | |`validatables` 27 | |`List` 28 | |Yes 29 | |List of POJO/Beans under validation 30 | 31 | |`batchValidationConfig` 32 | |`BatchValidationConfig` 33 | |Yes 34 | |Config for validation 35 | 36 | |`failureForNullValidatable` 37 | |`FailureT` 38 | |No 39 | |Failure to return if a member in `validatables` passed is `null` 40 | 41 | |`throwableMapper` 42 | |`(Throwable) -> FailureT?` 43 | |No 44 | |xref:../../../README.adoc#_what_if_there_is_an_exception_during_execution[Refer this section] 45 | 46 | |=== 47 | 48 | == Return 49 | 50 | `Optional` - First failure for any member. If all members pass all validations, `Optional.empty()` shall be returned. 51 | -------------------------------------------------------------------------------- /docs/api/vador-batch/validate-and-fail-fast-for-each-with-pair.adoc: -------------------------------------------------------------------------------- 1 | = `validateAndFailFastForEach (with Pair for Failure)` 2 | Gopal S Akshintala 3 | :Revision: 1.0 4 | ifdef::env-github[] 5 | :tip-caption: :bulb: 6 | :note-caption: :information_source: 7 | :important-caption: :heavy_exclamation_mark: 8 | :caution-caption: :fire: 9 | :warning-caption: :warning: 10 | endif::[] 11 | :sectnums!: 12 | :sourcedir: ../../../vador/src/main/java 13 | :testdir: ../../../vador/src/test/java 14 | :imagesdir: ../../images 15 | 16 | This execution strategy lets you run validations for your batch data structure 17 | and _fail-fast_ on the first failure for _each_ member in the batch i.e., 18 | if a member fails one of the validations, 19 | it stops running further validations on that member and moves on to the next member in the batch. 20 | 21 | == Params 22 | 23 | [cols="1,1,1,3"] 24 | |=== 25 | |Param |Data type |Required? |Description 26 | 27 | |`validatables` 28 | |`List` 29 | |Yes 30 | |List of POJO/Beans under validation 31 | 32 | |`pairForInvalidMapper` 33 | |`(ValidatableT) -> PairT` 34 | |Yes 35 | |Function (mostly a getter) whose return value can be used as an identifier to identify the first failed member in `validatables`. 36 | 37 | |`batchValidationConfig` 38 | |`BatchValidationConfig` 39 | |Yes 40 | |Config for validation 41 | 42 | |`failureForNullValidatable` 43 | |`FailureT` 44 | |No 45 | |Failure to return if a member in `validatables` passed is `null` 46 | 47 | |`throwableMapper` 48 | |`(Throwable) -> FailureT?` 49 | |No 50 | |xref:../../../README.adoc#_what_if_there_is_an_exception_during_execution[Refer to this section] 51 | 52 | |=== 53 | 54 | == Return 55 | 56 | You can read more about `Either` data type https://docs.vavr.io/#_either[here]. 57 | 58 | `List, ValidatableT>>` - One entry per member in the same order of `validatables`. 59 | If the member passes all validations, the result hold the validatable that is validated in the right state `Either.right(ValidatableT)`. 60 | Otherwise, the result will be `Either.left(Tuple2)`, holding the first failure for that validatable paired with an identifier of the failed member. 61 | -------------------------------------------------------------------------------- /docs/api/vador-batch/validate-and-fail-fast-for-each.adoc: -------------------------------------------------------------------------------- 1 | = `validateAndFailFastForEach` 2 | Gopal S Akshintala 3 | :Revision: 1.0 4 | ifdef::env-github[] 5 | :tip-caption: :bulb: 6 | :note-caption: :information_source: 7 | :important-caption: :heavy_exclamation_mark: 8 | :caution-caption: :fire: 9 | :warning-caption: :warning: 10 | endif::[] 11 | :sectnums!: 12 | :sourcedir: ../../../vador/src/main/java 13 | :testdir: ../../../vador/src/test/java 14 | :imagesdir: ../../images 15 | 16 | This execution strategy lets you run validations for your batch data structure 17 | and _fail-fast_ on the first failure for _each_ member in the batch i.e., 18 | if a member fails one of the validations, 19 | it stops running further validations on that member and moves on to the next member in the batch. 20 | 21 | == Params 22 | 23 | [cols="1,1,1,3"] 24 | |=== 25 | |Param |Data type |Required? |Description 26 | 27 | |`validatables` 28 | |`List` 29 | |Yes 30 | |List of POJO/Beans under validation 31 | 32 | |`batchValidationConfig` 33 | |link:../../config-dsl/BatchValidationConfig.adoc[`BatchValidationConfig`] 34 | |Yes 35 | |Config for Batch validation 36 | 37 | |`failureForNullValidatable` 38 | |`FailureT` 39 | |No 40 | |Failure to return if a member in `validatables` passed is `null` 41 | 42 | |`throwableMapper` 43 | |`(Throwable) -> FailureT?` 44 | |No 45 | |xref:../../../README.adoc#_what_if_there_is_an_exception_during_execution[Refer to this section] 46 | 47 | |=== 48 | 49 | == Return 50 | 51 | You can read more about `Either` data type https://docs.vavr.io/#_either[here]. 52 | 53 | `List>` - One entry per member in the same order of `validatables`. 54 | If the member passes all validations, 55 | the result holds the validatable that is validated in the right state `Either.right(Validatable)`. 56 | Otherwise, the result will be `Either.left(Failure)`, holding the first failure for that validatable. 57 | -------------------------------------------------------------------------------- /docs/api/vador/validate-and-fail-fast.adoc: -------------------------------------------------------------------------------- 1 | = `validateAndFailFast` 2 | Gopal S Akshintala 3 | :Revision: 1.0 4 | ifdef::env-github[] 5 | :tip-caption: :bulb: 6 | :note-caption: :information_source: 7 | :important-caption: :heavy_exclamation_mark: 8 | :caution-caption: :fire: 9 | :warning-caption: :warning: 10 | endif::[] 11 | :sectnums!: 12 | :sourcedir: ../../../vador/src/main/java 13 | :testdir: ../../../vador/src/test/java 14 | :imagesdir: ../../images 15 | 16 | This execution strategy lets you run validations and _fail-fast_ on the first failure. 17 | 18 | == Params 19 | 20 | [cols="1,1,1,3"] 21 | |=== 22 | |Param |Data type |Required? |Description 23 | 24 | |`validatable` 25 | |`ValidatableT` 26 | |Yes 27 | |POJO/Bean under validation 28 | 29 | |`validationConfig` 30 | |`ValidationConfig` 31 | |Yes 32 | |Config for validation 33 | 34 | |`throwableMapper` 35 | |`(Throwable) -> FailureT?` 36 | |No 37 | |xref:../../../README.adoc#_what_if_there_is_an_exception_during_execution[Refer this section] 38 | 39 | |=== 40 | 41 | == Return 42 | 43 | `Optional` - Wraps the First validation failure encountered. If there is no validation failure it returns `Optional.emtpy()` 44 | -------------------------------------------------------------------------------- /docs/config-dsl/BatchValidationConfig.adoc: -------------------------------------------------------------------------------- 1 | = BatchValidationConfig DSL 2 | Gopal S Akshintala 3 | :Revision: 1.0 4 | ifdef::env-github[] 5 | :tip-caption: :bulb: 6 | :note-caption: :information_source: 7 | :important-caption: :heavy_exclamation_mark: 8 | :caution-caption: :fire: 9 | :warning-caption: :warning: 10 | endif::[] 11 | :toc: 12 | :toc-placement: preamble 13 | :sourcedir: ../../vador/src/main/java 14 | :testdir: ../../vador/src/test/java 15 | :imagesdir: ../images/config-dsl 16 | 17 | As the name indicates, use this to configure validation requirements for a data-structure that exists in a _Collection_ or a _Batch_, say `List`. 18 | But why a new DSL, can't we just use `ValidationConfig` DSL and loop through each item and validate? `BatchValidationConfig` is a superset of `ValidationConfig`. 19 | We can configure more than just validations and required fields/Ids. You can configure specifications that need context of the entire Batch. 20 | 21 | image:bean-batch.png[] 22 | 23 | == link:FilterDuplicatesConfig.adoc[Find and Filter/Fail Duplicates] 24 | -------------------------------------------------------------------------------- /docs/config-dsl/IDConfig.adoc: -------------------------------------------------------------------------------- 1 | = Stricter Salesforce `ID` Validation for Core consumers 2 | Gopal S Akshintala 3 | :Revision: 1.0 4 | ifdef::env-github[] 5 | :tip-caption: :bulb: 6 | :note-caption: :information_source: 7 | :important-caption: :heavy_exclamation_mark: 8 | :caution-caption: :fire: 9 | :warning-caption: :warning: 10 | endif::[] 11 | :toc: 12 | :toc-placement: preamble 13 | :sourcedir: ../../vador/src/main/java 14 | :testdir: ../../vador/src/test/java 15 | :imagesdir: ../images/config-dsl 16 | :prewrap!: 17 | 18 | One of the handy features of Vador, is to list all the `ID` fields of you bean that need validation at one place. 19 | This `ID` validation by default uses `com.force.swag.id.IdTraits.isValidIdStrictChecking(idStr, true)` utility. 20 | But if you wish to have a more strict validation making use of the `EntityInfo` (For example, you can check if the `ID` field has specific prefix of the entity it represents), 21 | you can do that too, using `IDConfig`. Let's understand that through an example: 22 | 23 | == Example: Strict ID validations 24 | 25 | ifdef::env-github[] 26 | 27 | [source,java,indent=0,options="nowrap"] 28 | ---- 29 | private static class BeanWithIdFields { 30 | ID accountId; 31 | ID contactId; 32 | } 33 | 34 | /** 35 | * This imitates `common.udd.EntityId` interface from core which is implemented by all Entities. 36 | */ 37 | private interface EntityId {} 38 | 39 | @Value 40 | private static class AccountEntityId implements EntityId {} 41 | 42 | @Value 43 | private static class ContactEntityId implements EntityId {} 44 | 45 | /** This imitates entity UddConstants */ 46 | private static class AccountUddConstants { 47 | public static final EntityId EntityId = new AccountEntityId(); 48 | } 49 | 50 | private static class ContactUddConstants { 51 | public static final EntityId EntityId = new ContactEntityId(); 52 | } 53 | ---- 54 | 55 | endif::[] 56 | ifndef::env-github[] 57 | 58 | [source,java,indent=0,options="nowrap"] 59 | ---- 60 | include::{testdir}/com/salesforce/vador/execution/config/IDConfigTest.java[tag=bean-with-id-fields] 61 | ---- 62 | 63 | endif::[] 64 | 65 | === Validation Requirements 66 | 67 | * Both `ID` fields (`sfIdFormatField1`, `optionalSfIdFormatField2`) need to be validated `withIdValidator` passed by the consumer. 68 | 69 | === Demo 70 | 71 | ifdef::env-github[] 72 | 73 | [source,java,indent=0,options="nowrap"] 74 | ---- 75 | @Test 76 | void idConfigForBatch() { 77 | final var config = 78 | BatchValidationConfig.toValidate() 79 | .withIdConfig( 80 | IDConfig.toValidate() 81 | .withIdValidator(ValidIdUtil::isThisEntity) 82 | .shouldHaveValidSFIdFormatOrFailWith( 83 | Tuple.of(BeanWithIdFields::getAccountId, AccountUddConstants.EntityId), 84 | INVALID_UDD_ID) 85 | .absentOrHaveValidSFIdFormatOrFailWith( 86 | Tuple.of(BeanWithIdFields::getContactId, ContactUddConstants.EntityId), 87 | INVALID_OPTIONAL_UDD_ID)) 88 | .prepare(); 89 | final var validBean = new BeanWithIdFields(new ID("validId"), null); 90 | final var validatables = 91 | List.of( 92 | validBean, 93 | new BeanWithIdFields(new ID("invalidId"), null), 94 | new BeanWithIdFields(new ID("validId"), new ID("invalidId"))); 95 | final var results = VadorBatch.validateAndFailFastForEach(validatables, config); 96 | assertThat(results) 97 | .containsExactly(right(validBean), left(INVALID_UDD_ID), left(INVALID_OPTIONAL_UDD_ID)); 98 | } 99 | 100 | /** Dummy. A core client may use `common.udd.ValidIdUtil.isThisEntity(String, EntityId)` */ 101 | private static class ValidIdUtil { 102 | /** This should be implemented by the client and passed through `withIdValidator` config. */ 103 | private static boolean isThisEntity(ID idToValidate, EntityId entityId) { 104 | return !idToValidate.toString().equalsIgnoreCase("invalidId"); // fake implementation 105 | } 106 | } 107 | ---- 108 | 109 | endif::[] 110 | ifndef::env-github[] 111 | 112 | [source,java,indent=0,options="nowrap"] 113 | ---- 114 | include::{testdir}/com/salesforce/vador/execution/config/IDConfigTest.java[tag=bean-strict-id-validation] 115 | ---- 116 | 117 | endif::[] 118 | -------------------------------------------------------------------------------- /docs/config-dsl/nested/BatchOfBatch1ValidationConfig.adoc: -------------------------------------------------------------------------------- 1 | = BatchOfBatch1ValidationConfig DSL 2 | Gopal S Akshintala 3 | :Revision: 1.0 4 | ifdef::env-github[] 5 | :tip-caption: :bulb: 6 | :note-caption: :information_source: 7 | :important-caption: :heavy_exclamation_mark: 8 | :caution-caption: :fire: 9 | :warning-caption: :warning: 10 | endif::[] 11 | :toc: 12 | :toc-placement: preamble 13 | :prewrap!: 14 | :sourcedir: ../../../vador/src/main/java 15 | :testdir: ../../../vador/src/test/java 16 | :imagesdir: ../../images/config-dsl 17 | 18 | If each of your _Batch_ bean, say `List` HAS-A nested batch member say `List`. These batches are inter-dependent 1(ContainerT):N(MemberT). 19 | Each `ContainerT` validity depends on all its N nested `MemberT`\`s validity. But don't worry, the challenge is already solved by Vador. You just need to ask him to do it for you, by hooking `BatchValidationConfig` into `BatchOfBatch1ValidationConfig`. 20 | 21 | To easily wrap your head around such data structure, visualise it like a *Tree*: 22 | 23 | image:batch-of-batch-1.png[] 24 | 25 | NOTE: Currently, it is only supported for `validateAndFailFast` execution strategy and the order of validation is *Level-Order*. All the ``ContainerT``s are validated first and for all the valid ones, their ``MemberT``s are validated. If a requirement/use-case arises, we can introduce a variant for *Depth-First* validation. 26 | 27 | == Example 28 | 29 | ifdef::env-github[] 30 | 31 | [source,java,indent=0,options="nowrap"] 32 | ---- 33 | class Root { 34 | List itemsBatch; 35 | } 36 | 37 | class Item { 38 | List beanBatch; 39 | } 40 | 41 | class Bean { 42 | int value; 43 | String label; 44 | } 45 | ---- 46 | 47 | endif::[] 48 | ifndef::env-github[] 49 | 50 | [source,java,indent=0,options="nowrap"] 51 | ---- 52 | include::{testdir}/com/salesforce/vador/execution/config/nested/BatchOfBatch1ValidationConfigTest.java[tag=batch-of-batch-1] 53 | ---- 54 | 55 | endif::[] 56 | 57 | === Validation Requirements 58 | 59 | * Validate N `List` (``ContainerT``s), and for each `Item`, validate all its N `List` (``MemberT``s). 60 | 61 | === Demo 62 | 63 | ifdef::env-github[] 64 | 65 | [source,java,indent=0,options="nowrap"] 66 | ---- 67 | @DisplayName("FailFastForEach -> Root[batchOf(Items(batchOf(Beans))]) or like `List>`") 68 | @Test 69 | void batchOfBatch1FailFastForEach() { 70 | final var memberBatchValidationConfig = 71 | BatchValidationConfig.toValidate() 72 | .withSpec( 73 | spec -> 74 | spec._2() 75 | .when(Bean::getValue) 76 | .matches(is(1)) 77 | .then(Bean::getLabel) 78 | .shouldMatch(anyOf("1", "one")) 79 | .orFailWith(INVALID_COMBO_1)) 80 | .withValidator(ignore -> UNKNOWN_EXCEPTION, NONE) 81 | .prepare(); 82 | final var itemBatchValidationConfig = 83 | BatchOfBatch1ValidationConfig.toValidate() 84 | .withMemberBatchValidationConfig( 85 | Tuple.of(Item::getBeanBatch, memberBatchValidationConfig)) 86 | .prepare(); 87 | 88 | final var invalidBean = new Bean(1, "a"); 89 | final var beanBatch = List.of(invalidBean, new Bean(1, "1")); 90 | final var itemsBatch = List.of(new Item(beanBatch)); 91 | final var root = new Root(itemsBatch); 92 | 93 | final var results = 94 | validateAndFailFastForEach( 95 | root.getItemsBatch(), 96 | itemBatchValidationConfig, 97 | NONE, 98 | ValidationFailure::getValidationFailureForException); 99 | assertThat(results).hasSize(1); 100 | final var result = results.get(0); 101 | VavrAssertions.assertThat(result).isLeft(); 102 | final var failure = result.getLeft(); 103 | assertThat(failure.getContainerFailure()).isNull(); 104 | assertThat(failure.getBatchMemberFailures()) 105 | .containsExactly(INVALID_COMBO_1, UNKNOWN_EXCEPTION); 106 | } 107 | ---- 108 | 109 | endif::[] 110 | ifndef::env-github[] 111 | 112 | [source,java,indent=0,options="nowrap"] 113 | ---- 114 | include::{testdir}/com/salesforce/vador/execution/config/nested/BatchOfBatch1ValidationConfigTest.java[tag=batch-of-batch-1-demo] 115 | ---- 116 | 117 | endif::[] 118 | -------------------------------------------------------------------------------- /docs/images/api.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/salesforce-misc/Vador/794cdb77d4574c128ed6c0f7137ed0d00c799584/docs/images/api.png -------------------------------------------------------------------------------- /docs/images/config-dsl/batch-of-batch-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/salesforce-misc/Vador/794cdb77d4574c128ed6c0f7137ed0d00c799584/docs/images/config-dsl/batch-of-batch-1.png -------------------------------------------------------------------------------- /docs/images/config-dsl/bean-batch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/salesforce-misc/Vador/794cdb77d4574c128ed6c0f7137ed0d00c799584/docs/images/config-dsl/bean-batch.png -------------------------------------------------------------------------------- /docs/images/config-dsl/bean-nested.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/salesforce-misc/Vador/794cdb77d4574c128ed6c0f7137ed0d00c799584/docs/images/config-dsl/bean-nested.png -------------------------------------------------------------------------------- /docs/images/config-dsl/bean.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/salesforce-misc/Vador/794cdb77d4574c128ed6c0f7137ed0d00c799584/docs/images/config-dsl/bean.png -------------------------------------------------------------------------------- /docs/images/config-dsl/config-1-1-validatable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/salesforce-misc/Vador/794cdb77d4574c128ed6c0f7137ed0d00c799584/docs/images/config-dsl/config-1-1-validatable.png -------------------------------------------------------------------------------- /docs/images/config-dsl/container-level-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/salesforce-misc/Vador/794cdb77d4574c128ed6c0f7137ed0d00c799584/docs/images/config-dsl/container-level-2.png -------------------------------------------------------------------------------- /docs/images/config-dsl/container.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/salesforce-misc/Vador/794cdb77d4574c128ed6c0f7137ed0d00c799584/docs/images/config-dsl/container.png -------------------------------------------------------------------------------- /docs/images/config-dsl/hierarchical-validation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/salesforce-misc/Vador/794cdb77d4574c128ed6c0f7137ed0d00c799584/docs/images/config-dsl/hierarchical-validation.png -------------------------------------------------------------------------------- /docs/images/config-dsl/multi-filter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/salesforce-misc/Vador/794cdb77d4574c128ed6c0f7137ed0d00c799584/docs/images/config-dsl/multi-filter.png -------------------------------------------------------------------------------- /docs/images/config-dsl/spec-spectrum.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/salesforce-misc/Vador/794cdb77d4574c128ed6c0f7137ed0d00c799584/docs/images/config-dsl/spec-spectrum.png -------------------------------------------------------------------------------- /docs/images/config-spectrum.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/salesforce-misc/Vador/794cdb77d4574c128ed6c0f7137ed0d00c799584/docs/images/config-spectrum.png -------------------------------------------------------------------------------- /docs/images/fcwfp-play-poster.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/salesforce-misc/Vador/794cdb77d4574c128ed6c0f7137ed0d00c799584/docs/images/fcwfp-play-poster.jpeg -------------------------------------------------------------------------------- /docs/images/function-call-mess.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/salesforce-misc/Vador/794cdb77d4574c128ed6c0f7137ed0d00c799584/docs/images/function-call-mess.png -------------------------------------------------------------------------------- /docs/images/more-power.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/salesforce-misc/Vador/794cdb77d4574c128ed6c0f7137ed0d00c799584/docs/images/more-power.gif -------------------------------------------------------------------------------- /docs/images/vav-poster.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/salesforce-misc/Vador/794cdb77d4574c128ed6c0f7137ed0d00c799584/docs/images/vav-poster.png -------------------------------------------------------------------------------- /docs/matchers.adoc: -------------------------------------------------------------------------------- 1 | = Matchers 2 | Gopal S Akshintala 3 | :Revision: 1.0 4 | ifdef::env-github[] 5 | :tip-caption: :bulb: 6 | :note-caption: :information_source: 7 | :important-caption: :heavy_exclamation_mark: 8 | :caution-caption: :fire: 9 | :warning-caption: :warning: 10 | endif::[] 11 | :vador-version: 1.0.0 12 | :toc: 13 | :toc-placement: preamble 14 | :imagesdir: images 15 | 16 | Vador provides few matchers out of the box to help write Specs. 17 | These matchers address generic use-cases common among consumers. 18 | New matchers can be added as needed/requested. 19 | Currently, these are the available matchers: 20 | 21 | * AnyMatchers 22 | ** `anyOf` 23 | ** `anyOfOrNull` 24 | * DateMatchers 25 | ** `isOnOrBeforeIfBothArePresent` 26 | ** `isBeforeIfBothArePresent` 27 | ** `isEqualToDayOfDate` 28 | * IntMatchers 29 | ** inRangeInclusive 30 | 31 | [.lead] 32 | This is a separate artifact, please use these coordinates to include them in your module. 33 | 34 | [source,xml,subs=attributes+] 35 | ---- 36 | 37 | com.salesforce.vador 38 | vador-matchers 39 | {vador-version} 40 | 41 | ---- 42 | -------------------------------------------------------------------------------- /docs/presentations/2021-08-cancellation-api/1-index.adoc: -------------------------------------------------------------------------------- 1 | = 🦾 Vador for Cancellation API 🦾 2 | Gopal S Akshintala 3 | :Revision: 1.0 4 | ifdef::env-github[] 5 | :tip-caption: :bulb: 6 | :note-caption: :information_source: 7 | :important-caption: :heavy_exclamation_mark: 8 | :caution-caption: :fire: 9 | :warning-caption: :warning: 10 | endif::[] 11 | :hide-uri-scheme: 12 | :!sectnums: 13 | 14 | {empty} + 15 | {empty} + 16 | 17 | == But! this is not about [.line-through]#Vador#! 18 | 19 | === We have a Problem to solve and how can we solve it easily and elegantly 20 | 21 | **** 22 | * link:2-validatable.adoc[Data structure to Validate] 23 | * link:3-config-driven-validation.adoc[Config driven validation] 24 | * Migrate validations using Config-driven approach: 25 | ** link:4-bsg-container.adoc[Container Validation Config] 26 | ** link:5-root-container-with-2-levels.adoc[Container Validation Config with 2 levels] 27 | ** link:6-bsg-container-compose.adoc[Nested Container validation] 28 | ** link:7-bsg-batch.adoc[Batch validation with Filter config] 29 | ** link:8-refItem-batch.adoc[Batch Validation with more built-in validators] 30 | * link:9-retro-on-benefits.adoc[Retro on the benefits] 31 | * link:10-track-record.adoc[Vador's Track record] 32 | * link:11-stability-and-support.adoc[Stability & Support] 33 | * link:12-next-steps.adoc[Next Steps] 34 | **** 35 | -------------------------------------------------------------------------------- /docs/presentations/2021-08-cancellation-api/10-track-record.adoc: -------------------------------------------------------------------------------- 1 | = 🏁 Vador's Track record 2 | Gopal S Akshintala 3 | :Revision: 1.0 4 | ifdef::env-github[] 5 | :tip-caption: :bulb: 6 | :note-caption: :information_source: 7 | :important-caption: :heavy_exclamation_mark: 8 | :caution-caption: :fire: 9 | :warning-caption: :warning: 10 | endif::[] 11 | :hide-uri-scheme: 12 | :imagesdir: images 13 | 14 | == Impact 15 | 16 | * Although a generic bean validation framework, Vador is written with the link:../../docs/requirements.adoc[Context of Phoenix connect APIs]. 17 | * link:../../../README.adoc#_impact[Teams using it currently] 18 | 19 | === BillingSchedule Batch API in 234 20 | ** Nearly `LOC:250` with logic replaced with Config driven validations. 21 | ** It's a validation heavy service and the Testability metrics overshot. 22 | 23 | image:bs-coverage-numbers.png[] 24 | 25 | ** We effortlessly implemented support for `allOrNone` flag in the validation layer, with a simple switch like this: 26 | 27 | [source,java,indent=0,options=nowrap] 28 | ---- 29 | if (inputRep.getAllOrNone()) { 30 | validateAndFailFastForAny(); 31 | } else { 32 | validateAndFailFastForEach(); 33 | } 34 | ---- 35 | 36 | ** The cyclomatic complexity brought down by *~45%* (measured from the Cx metric in code-coverage dashboard). 37 | 38 | == Vador's design was presented at International conferences and meetups 39 | * 🇺🇸 *https://2020.allthingsopen.org/speakers/gopal-s-akshintala/[All Things Open]*, 2020, Raleigh, USA. https://www.youtube.com/watch?v=Dvr6gx4XaD8&list=PLrJbJ9wDl9EC0bG6y9fyDylcfmB_lT_Or&index=1[📹] 40 | * 🏴󠁧󠁢󠁥󠁮󠁧󠁿 https://www.youtube.com/watch?v=QVuMSsIUw6M&list=PLrJbJ9wDl9EC0bG6y9fyDylcfmB_lT_Or&index=2[Kotlin User Group, London] 41 | * 🇩🇪 https://www.youtube.com/watch?v=DBDTNmLbU2Y&list=PLrJbJ9wDl9EC0bG6y9fyDylcfmB_lT_Or&index=3[Berlin Functional Programming Group] 42 | * 🇳🇴 https://www.youtube.com/watch?v=tnpL1O8kTbM&list=PLrJbJ9wDl9EC0bG6y9fyDylcfmB_lT_Or&index=4[JavaBin, Norway] 43 | * 🇩🇪 https://www.youtube.com/watch?v=uGxx01yYAgk&list=PLrJbJ9wDl9EC0bG6y9fyDylcfmB_lT_Or&index=6[Kotlin User Group, Berlin] 44 | * 🇮🇳 https://devfest.gdghyderabad.in/speakers.html[Google Developer Group Devfest 2019] 45 | * 🇮🇳 https://www.meetup.com/en-AU/jughyderabad/events/264688807/[Java User Group Hyderabad (@JUGHyd)] 46 | * 🇮🇳 https://www.youtube.com/watch?v=l9jJ7m7_VpM&list=PLrJbJ9wDl9EC0bG6y9fyDylcfmB_lT_Or&index=7[Salesforce, Hyderabad, India] 47 | * 🇮🇳 https://www.youtube.com/watch?v=_QBlKtUY6ac&list=PLrJbJ9wDl9EC0bG6y9fyDylcfmB_lT_Or&index=8[Kotlin User Group, Hyderabad] 48 | * 🇮🇳 GHC, India - 2019, Bangalore 49 | * 🇪🇸 *https://www.jbcnconf.com/2020/[JBCN Conf]*, 2020, Barcelona, Spain (This event got cancelled due to COVID-19). 50 | -------------------------------------------------------------------------------- /docs/presentations/2021-08-cancellation-api/11-stability-and-support.adoc: -------------------------------------------------------------------------------- 1 | = 🏛 Stability and Support 2 | Gopal S Akshintala 3 | :Revision: 1.0 4 | ifdef::env-github[] 5 | :tip-caption: :bulb: 6 | :note-caption: :information_source: 7 | :important-caption: :heavy_exclamation_mark: 8 | :caution-caption: :fire: 9 | :warning-caption: :warning: 10 | endif::[] 11 | :hide-uri-scheme: 12 | :imagesdir: images 13 | :!sectnums: 14 | 15 | == Stability 16 | 17 | * It’s our responsibility to address all the edge cases and automate them thoroughly, for various types of POJOs. 18 | * We take code-quality & security seriously! 19 | ** ~100 unit tests currently guarding this, with a test coverage of `81.9%` 20 | * *0 bugs* and *no perf impact* reported by any of the consumer teams till now. 21 | 22 | == Long Term Support 23 | 24 | * This library is currently being supported by *Rev-Delphinus* 25 | * We added a lot of documentation with runnable examples (in-fact each unit test in the repo is a runnable example). 26 | We are committed to keep iterating and improving it. 27 | * We are happy to provide 1:1 support, until the documentation is comprehensive. 28 | * Vador is under active development. We have a *Healthy backlog* of features and are eager to take-up more feature requests. 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /docs/presentations/2021-08-cancellation-api/12-next-steps.adoc: -------------------------------------------------------------------------------- 1 | = 🐾 Next steps 🐾 2 | Gopal S Akshintala 3 | :Revision: 1.0 4 | ifdef::env-github[] 5 | :tip-caption: :bulb: 6 | :note-caption: :information_source: 7 | :important-caption: :heavy_exclamation_mark: 8 | :caution-caption: :fire: 9 | :warning-caption: :warning: 10 | endif::[] 11 | :hide-uri-scheme: 12 | :imagesdir: images 13 | :!sectnums: 14 | 15 | ==== 16 | * Please review the POC, all the examples we discussed are present in that. 17 | * Please help us with your *Feedback*. 18 | ==== 19 | 20 | == Incremental adaption 21 | * If you feel this is suitable and helpful for current Cancellation API and future services your team develops, we can come up with an incremental adaption plan. 22 | * Kick-start and pick the same piece used in the POC and turn it prod ready. 23 | -------------------------------------------------------------------------------- /docs/presentations/2021-08-cancellation-api/2-validatable.adoc: -------------------------------------------------------------------------------- 1 | = Data structure to Validate 2 | :Revision: 1.0 3 | :hide-uri-scheme: 4 | :imagesdir: images 5 | :!sectnums: 6 | 7 | [source,json,indent=0,options="nowrap"] 8 | ---- 9 | { 10 | "billingScheduleGroups": [ 11 | { 12 | "billingScheduleGroupId": "801RM0000007uUmYAI", 13 | "referenceItems": [ 14 | { 15 | "referenceId": "801xx000003GjsdAAC", 16 | "referenceItemId": "802xx000001ni4xAAA", 17 | "offsetReferenceItemId": "802xx000001ni6ZAAQ", 18 | "cancellationEffectiveDate": "2021-01-02", 19 | "offsetAmount": -120 20 | } 21 | ] 22 | }, 23 | { 24 | "billingScheduleGroupId": "801RM0000008uUmYAI", 25 | "referenceItems": [ 26 | { 27 | "referenceId": "801xx000004GjsdAAC", 28 | "referenceItemId": "802xx000001ni5xAAA", 29 | "offsetReferenceItemId": "802xx000002ni6ZAAQ", 30 | "cancellationEffectiveDate": "2021-02-02", 31 | "offsetAmount": -120 32 | }, 33 | { 34 | "referenceId": "801xx000004GjsdAAC", 35 | "referenceItemId": "802xx000001ni5xAAA", 36 | "offsetReferenceItemId": "802xx000002ni6ZAAQ", 37 | "cancellationEffectiveDate": "2021-02-02", 38 | "offsetAmount": -120 39 | } 40 | ] 41 | } 42 | ] 43 | } 44 | ---- 45 | 46 | [source,java,indent=0,options="nowrap"] 47 | ---- 48 | class class ReferenceItemInputRepresentation { 49 | ID referenceId; 50 | ID referenceItemId; 51 | ID offsetReferenceItemId; 52 | String cancellationEffectiveDate; 53 | Double offsetAmount; 54 | } 55 | 56 | class BillingScheduleGroupInputRepresentation { 57 | ID billingScheduleGroupId; 58 | List referenceItems; 59 | boolean isSetBillingScheduleGroupId; 60 | boolean isSetReferenceItems; 61 | } 62 | 63 | class BillingScheduleGroupListInputRepresentation { 64 | List billingScheduleGroups; 65 | } 66 | ---- 67 | 68 | == Flow picked to solve 69 | 70 | === `BillingCancellationValidationHelper.validateInput()` 71 | -------------------------------------------------------------------------------- /docs/presentations/2021-08-cancellation-api/3-config-driven-validation.adoc: -------------------------------------------------------------------------------- 1 | = Config Driven Validation 2 | Gopal S Akshintala 3 | :Revision: 1.0 4 | ifdef::env-github[] 5 | :tip-caption: :bulb: 6 | :note-caption: :information_source: 7 | :important-caption: :heavy_exclamation_mark: 8 | :caution-caption: :fire: 9 | :warning-caption: :warning: 10 | endif::[] 11 | :hide-uri-scheme: 12 | 13 | image:../../images/config-spectrum.png[] 14 | 15 | == Translate your Spike doc to Type-safe code (line-to-line)! 16 | 17 | The idea is, just take your validation requirements from the spike doc and describe them in a Type-safe format through a `Config`. 18 | Vador does the rest! 19 | 20 | == Configure your validation requirements 21 | 22 | Think of it as the entity XML we write, where we don't write the actual DDL, but a definition of it in XML, and platform takes care of the rest. 23 | We do something similar here, just that we use Java instead of XML for more data-type safety. 24 | 25 | == Config Types we use for this flow 26 | 27 | === link:../../../docs/config-dsl/ContainerValidationConfig.adoc[ContainerValidationConfig] 28 | 29 | === link:../../../docs/config-dsl/ContainerValidationConfigWith2Levels.adoc[ContainerValidationConfigWith2Levels] 30 | 31 | === link:../../../docs/config-dsl/BatchValidationConfig.adoc[BatchValidationConfig] 32 | -------------------------------------------------------------------------------- /docs/presentations/2021-08-cancellation-api/4-bsg-container.adoc: -------------------------------------------------------------------------------- 1 | = BSG Container level validation 2 | Gopal S Akshintala 3 | :Revision: 1.0 4 | ifdef::env-github[] 5 | :tip-caption: :bulb: 6 | :note-caption: :information_source: 7 | :important-caption: :heavy_exclamation_mark: 8 | :caution-caption: :fire: 9 | :warning-caption: :warning: 10 | endif::[] 11 | :hide-uri-scheme: 12 | :imagesdir: images 13 | :!sectnums: 14 | 15 | [source,java,indent=0,options="nowrap"] 16 | ---- 17 | class BillingScheduleGroupInputRepresentation { 18 | List referenceItems; 19 | } 20 | ---- 21 | 22 | image:bsg-container.png[] 23 | 24 | == Validation Requirements 25 | 26 | * Each `BSG` HAS-A batch **Member** - `List` 27 | * `List` should not be empty, need min size of 1, fail otherwise. 28 | * Validate each from the `List` and Fail fast. 29 | 30 | == Code 31 | 32 | [source,java,indent=0,options="nowrap"] 33 | ---- 34 | final var bsgHeaderValidationConfig = ContainerValidationConfig.toValidate() 35 | .withBatchMember(BillingScheduleGroupInputRepresentation::getReferenceItems) 36 | .shouldHaveMinBatchSizeOrFailWith(Tuple.of(1, ofInvalidBatchSize(INPUT_DATA_MISSING_REFERENCE_IDS))).prepare(); 37 | 38 | final Optional result = 39 | Vador.validateAndFailFastForContainer(rootContainer.getBillingScheduleGroups(), bsgHeaderValidationConfig) 40 | ---- 41 | -------------------------------------------------------------------------------- /docs/presentations/2021-08-cancellation-api/5-root-container-with-2-levels.adoc: -------------------------------------------------------------------------------- 1 | = Root Container with 2 levels validation 2 | Gopal S Akshintala 3 | :Revision: 1.0 4 | ifdef::env-github[] 5 | :tip-caption: :bulb: 6 | :note-caption: :information_source: 7 | :important-caption: :heavy_exclamation_mark: 8 | :caution-caption: :fire: 9 | :warning-caption: :warning: 10 | endif::[] 11 | :hide-uri-scheme: 12 | :imagesdir: images 13 | :!sectnums: 14 | 15 | [source,java,indent=0,options="nowrap"] 16 | ---- 17 | class BillingScheduleGroupInputRepresentation { 18 | List referenceItems; 19 | } 20 | 21 | class BillingScheduleGroupListInputRepresentation { 22 | List billingScheduleGroups; 23 | } 24 | ---- 25 | 26 | image:bsg-container-with-2-levels.png[] 27 | 28 | == Validation Requirements 29 | 30 | * `Root` HAS-A Batch **Member** - `List` 31 | * `List` should not be empty, need min size of 1, fail otherwise. 32 | * `List` size should be max 10, fail otherwise. 33 | * Stretch the scope of validation to 1 level deep 34 | ** Each `BSG` has a batch **Member** - `List` 35 | ** `List>` under the `Root` should be max 100, fail otherwise. 36 | 37 | == Code 38 | 39 | [source,java,indent=0,options="nowrap"] 40 | ---- 41 | final var rootHeaderValidationConfig = ContainerValidationConfigWith2Levels.toValidate() 42 | .withBatchMember(BillingScheduleGroupListInputRepresentation::getBillingScheduleGroups) 43 | .shouldHaveMinBatchSizeOrFailWith(Tuple.of(1, ofInvalidBatchSize(INPUT_DATA_MISSING_REFERENCE_IDS))) 44 | .shouldHaveMaxBatchSizeOrFailWith(Tuple.of(MAX_BILLING_SCHEDULE_GROUPS_ALLOWED, ofQueryTooComplicated(MAX_10_BSG_CANCELLATION_PERMITTED))) 45 | .withScopeOf1LevelDeep(bsgContainerConfigWithRootScope).prepare(); 46 | 47 | final var bsgContainerConfigWithRootScope = ContainerValidationConfig.toValidate() 48 | .withBatchMember(BillingScheduleGroupInputRepresentation::getReferenceItems) 49 | .shouldHaveMaxBatchSizeOrFailWith(Tuple.of(MAX_OFFSET_REFERENCE_IDS_ALLOWED, ofQueryTooComplicated(MAX_100_REFERENCE_BS_CAN_BE_OFFSET))).prepare(); 50 | 51 | final Optional result = 52 | Vador.validateAndFailFastForContainer(rootContainer, rootHeaderValidationConfig) 53 | ---- 54 | 55 | -------------------------------------------------------------------------------- /docs/presentations/2021-08-cancellation-api/6-bsg-container-compose.adoc: -------------------------------------------------------------------------------- 1 | = Compose Container validation results 2 | Gopal S Akshintala 3 | :Revision: 1.0 4 | ifdef::env-github[] 5 | :tip-caption: :bulb: 6 | :note-caption: :information_source: 7 | :important-caption: :heavy_exclamation_mark: 8 | :caution-caption: :fire: 9 | :warning-caption: :warning: 10 | endif::[] 11 | :hide-uri-scheme: 12 | :imagesdir: images 13 | :!sectnums: 14 | 15 | [source,java,indent=0,options="nowrap"] 16 | ---- 17 | class BillingScheduleGroupInputRepresentation { 18 | List referenceItems; 19 | } 20 | 21 | class BillingScheduleGroupListInputRepresentation { 22 | List billingScheduleGroups; 23 | } 24 | ---- 25 | 26 | == Code 27 | 28 | [source,java,indent=0,options="nowrap"] 29 | ---- 30 | final var rootHeaderValidationConfig = ContainerValidationConfigWith2Levels... 31 | final var bsgHeaderValidationConfig = ContainerValidationConfig... 32 | 33 | final Optional headerValidationResult = 34 | Vador.validateAndFailFastForContainer(rootContainer, rootHeaderValidationConfig) 35 | .or(() -> Vador.validateAndFailFastForContainer(rootContainer.getBillingScheduleGroups(), bsgHeaderValidationConfig)); 36 | ---- 37 | -------------------------------------------------------------------------------- /docs/presentations/2021-08-cancellation-api/7-bsg-batch.adoc: -------------------------------------------------------------------------------- 1 | = BSG Batch validation 2 | Gopal S Akshintala 3 | :Revision: 1.0 4 | ifdef::env-github[] 5 | :tip-caption: :bulb: 6 | :note-caption: :information_source: 7 | :important-caption: :heavy_exclamation_mark: 8 | :caution-caption: :fire: 9 | :warning-caption: :warning: 10 | endif::[] 11 | :hide-uri-scheme: 12 | :imagesdir: images 13 | :!sectnums: 14 | 15 | [source,java,indent=0,options="nowrap"] 16 | ---- 17 | class BillingScheduleGroupInputRepresentation { 18 | ID billingScheduleGroupId; 19 | List referenceItems; 20 | boolean isSetBillingScheduleGroupId; 21 | boolean isSetReferenceItems; 22 | } 23 | ---- 24 | 25 | image:bsg-batch.png[] 26 | 27 | == Validation Requirements 28 | 29 | * Fail all the duplicate in the batch with same `bsgId`. 30 | * `RefItems` should be a mandatory field. 31 | * `bsgId` should be mandatory and be in valid SF ID format. 32 | * Validate each from `List` and fail-fast on any failure. 33 | 34 | == Code 35 | 36 | [source,java,indent=0,options="nowrap"] 37 | ---- 38 | final var bsgBatchValidationConfig = BatchValidationConfig.toValidate() 39 | .findAndFilterDuplicatesConfig(FilterDuplicatesConfig.toValidate() 40 | .findAndFilterDuplicatesWith(BillingScheduleGroupInputRepresentation::getBillingScheduleGroupId) 41 | .andFailDuplicatesWith(ofInvalidAPIInput(DUPLICATE_BILLING_SCHEDULE_GROUP_NOT_ALLOWED))) 42 | .shouldHaveFieldOrFailWithFn(BillingScheduleGroupInputRepresentation::getReferenceItems, requiredFieldMissingMapper) 43 | .shouldHaveValidSFIdFormatOrFailWithFn(BillingScheduleGroupInputRepresentation::getBillingScheduleGroupId, invalidSfIdFormatFailureMapper).prepare(); 44 | 45 | final Optional> bsgValidationResult = 46 | VadorBatch.validateAndFailFastForAny( 47 | inputRequest.getBillingScheduleGroups(), 48 | BillingScheduleGroupInputRepresentation::getBillingScheduleGroupId, 49 | bsgBatchValidationConfig); 50 | ---- 51 | -------------------------------------------------------------------------------- /docs/presentations/2021-08-cancellation-api/8-refItem-batch.adoc: -------------------------------------------------------------------------------- 1 | = RefItem Batch validation 2 | Gopal S Akshintala 3 | :Revision: 1.0 4 | ifdef::env-github[] 5 | :tip-caption: :bulb: 6 | :note-caption: :information_source: 7 | :important-caption: :heavy_exclamation_mark: 8 | :caution-caption: :fire: 9 | :warning-caption: :warning: 10 | endif::[] 11 | :hide-uri-scheme: 12 | :imagesdir: images 13 | :!sectnums: 14 | 15 | [source,java,indent=0,options="nowrap"] 16 | ---- 17 | class class ReferenceItemInputRepresentation { 18 | ID referenceId; 19 | ID referenceItemId; 20 | ID offsetReferenceItemId; 21 | String cancellationEffectiveDate; 22 | Double offsetAmount; 23 | } 24 | ---- 25 | 26 | image:refItem-batch.png[] 27 | 28 | == Validation Requirements 29 | 30 | * Let's look at another batch validation, with more requirements - `List`. 31 | * Fail all the duplicates in the batch with *Multiple filters* 32 | ** `RefItemId` 33 | ** `OffsetRefItemId` 34 | * Mandatory fields 35 | ** `RefId` 36 | ** `RefItemId` 37 | ** `OffsetRefItemId` 38 | * `CancellationEffectiveDate` should be in `ISO8601Format` 39 | 40 | == Code 41 | 42 | [source,java,indent=0,options="nowrap"] 43 | ---- 44 | final var refItemBatchValidationConfig = BatchValidationConfig.toValidate() 45 | .findAndFilterDuplicatesConfigs(refItemFilterDuplicatesConfig) 46 | .shouldHaveFieldsOrFailWithFn(Tuple.of(List.of( 47 | ReferenceItemInputRepresentation::getOffsetAmount, 48 | ReferenceItemInputRepresentation::getcancellationEffectiveDate), requiredFieldMissingMapper)) 49 | .shouldHaveValidSFIdFormatForAllOrFailWithFn(Tuple.of(List.of( 50 | ReferenceItemInputRepresentation::getReferenceId, 51 | ReferenceItemInputRepresentation::getReferenceItemId, 52 | ReferenceItemInputRepresentation::getOffsetReferenceItemId), invalidSfIdFormatFailureMapper)) 53 | .withSpec(spec -> spec._1() // <1> 54 | .given(ReferenceItemInputRepresentation::getcancellationEffectiveDate) 55 | .shouldMatch(ISO8601DateFormat()) 56 | .orFailWith(ofInvalidAPIInput(INVALID_CANCELLATION_DATE_FORMAT))).prepare(); 57 | 58 | final var refItemFilterDuplicatesConfig = List.of( 59 | FilterDuplicatesConfig.toValidate() 60 | .findAndFilterDuplicatesWith(ReferenceItemInputRepresentation::getReferenceItemIdAsString) 61 | .andFailDuplicatesWith(ofInvalidAPIInput(DUPLICATE_REFERENCE_ITEM_IDS_IN_REQUEST)), 62 | FilterDuplicatesConfig.toValidate() 63 | .findAndFilterDuplicatesWith(ReferenceItemInputRepresentation::getOffsetReferenceItemIdAsString) 64 | .andFailDuplicatesWith(ofInvalidAPIInput(DUPLICATE_OFFSET_REFERENCE_ITEM_IDS_IN_REQUEST))); 65 | 66 | final Optional> refItemValidationResult = 67 | VadorBatch.validateAndFailFastForAny( 68 | getAllRefItems(inputRequest.getBillingScheduleGroups()), 69 | ReferenceItemInputRepresentation::getReferenceItemId, 70 | refItemBatchValidationConfig); 71 | ---- 72 | <1> 🤓 link:../../specs.adoc[Specs] 73 | -------------------------------------------------------------------------------- /docs/presentations/2021-08-cancellation-api/9-retro-on-benefits.adoc: -------------------------------------------------------------------------------- 1 | = 🍫 Retro on Benefits 2 | Gopal S Akshintala 3 | :Revision: 1.0 4 | ifdef::env-github[] 5 | :tip-caption: :bulb: 6 | :note-caption: :information_source: 7 | :important-caption: :heavy_exclamation_mark: 8 | :caution-caption: :fire: 9 | :warning-caption: :warning: 10 | endif::[] 11 | :hide-uri-scheme: 12 | :imagesdir: images 13 | 14 | == Readability 15 | 16 | We don't need analogies to stress on how important readability is and how Config is more readable than code with nested `if/else/for`. 17 | 18 | == Maintainability 19 | 20 | Strips out a lot of the code/logic to maintain. 21 | 22 | == Complexity 23 | 24 | * No branching => No Cyclomatic complexity/Cognitive complexity. 25 | * It abstracts away all the implementation complexity. 26 | * Saves a lot of Man hours while writing and _10X_ more while reading. 27 | * Eliminates the need to spike on your validation strategy/design. 28 | 29 | TIP: An *8-pointer* Story for Free 🤑 30 | 31 | == Testability 32 | 33 | Think of writing config as fill in the blanks for well-tested algorithm templates, so you don't need to write any code, which implies no need to write any tests. 34 | 35 | NOTE: You can always test your config (to double-check if right values are provided), but no need to re-test the already well-tested implementation. 36 | 37 | link:../../specs.adoc#_specs_do_not_need_tests[Read more here] 38 | 39 | == Flexibility 40 | 41 | * This is decoupled from the API orchestration method. For example, currently it's *Fail-Fast for Any*. 42 | But if you want to migrate to Fail-Fast for each item (to handle partial failures) or if you have another route like SObject where you need to accumulate all errors, that's as simple as calling a different API method without changing anything else. 43 | * If you wish to skip some validations or add new validations depending on the route, you can have different configs instances for different routes. 44 | 45 | == Extensibility 46 | 47 | * Config can easily be modified or extended if your Bean's data-structure changes, with new fields being added or removed. 48 | * Config can easily catch up, even when your service migrates from non-batch to batch mode. 49 | 50 | == Re-usability 51 | 52 | Config is mapped to a data structure. 53 | Which means, if the validation requirements are same, you can *reuse* the config everywhere the data-structure is used, say in a different API. 54 | Even if the data-structure (member) is nested inside another bean (container), the container bean can reuse all the validations of its member without rewriting. 55 | 56 | == Learning Curve 57 | 58 | Use of same Config pattern through-out, with self-explaining DSL methods to drive your development. 59 | This keeps the scope and slope of your learning curve required, low. 60 | -------------------------------------------------------------------------------- /docs/presentations/2021-08-cancellation-api/images/bs-coverage-numbers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/salesforce-misc/Vador/794cdb77d4574c128ed6c0f7137ed0d00c799584/docs/presentations/2021-08-cancellation-api/images/bs-coverage-numbers.png -------------------------------------------------------------------------------- /docs/presentations/2021-08-cancellation-api/images/bsg-batch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/salesforce-misc/Vador/794cdb77d4574c128ed6c0f7137ed0d00c799584/docs/presentations/2021-08-cancellation-api/images/bsg-batch.png -------------------------------------------------------------------------------- /docs/presentations/2021-08-cancellation-api/images/bsg-container-with-2-levels.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/salesforce-misc/Vador/794cdb77d4574c128ed6c0f7137ed0d00c799584/docs/presentations/2021-08-cancellation-api/images/bsg-container-with-2-levels.png -------------------------------------------------------------------------------- /docs/presentations/2021-08-cancellation-api/images/bsg-container.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/salesforce-misc/Vador/794cdb77d4574c128ed6c0f7137ed0d00c799584/docs/presentations/2021-08-cancellation-api/images/bsg-container.png -------------------------------------------------------------------------------- /docs/presentations/2021-08-cancellation-api/images/refItem-batch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/salesforce-misc/Vador/794cdb77d4574c128ed6c0f7137ed0d00c799584/docs/presentations/2021-08-cancellation-api/images/refItem-batch.png -------------------------------------------------------------------------------- /docs/requirements.adoc: -------------------------------------------------------------------------------- 1 | = Requirements 2 | Gopal S Akshintala 3 | :Revision: 1.0 4 | ifdef::env-github[] 5 | :tip-caption: :bulb: 6 | :note-caption: :information_source: 7 | :important-caption: :heavy_exclamation_mark: 8 | :caution-caption: :fire: 9 | :warning-caption: :warning: 10 | endif::[] 11 | :toc: 12 | :imagesdir: images 13 | 14 | == Why a new Framework for Bean validation? 15 | 16 | While shopping for Bean validation frameworks, we stumbled-upon these well-known ones: 17 | 18 | * https://www.baeldung.com/javax-validation[Java Bean validation]: This is only suitable for simple data validations 19 | like `@NotNull, @Min, @Max`. 20 | * https://reflectoring.io/bean-validation-with-spring-boot/[Spring Bean validation]: It comes with a lot of * 21 | Spring-baggage* and works with Spring REST. But we predominantly use Connect framework on core. 22 | 23 | == Problem with `@Annotation` based validators 24 | 25 | Annotations are reflection based, and they create a lot of _runtime magic_. They are not bad in-general, but using them 26 | for validations has these cons: 27 | 28 | * It's difficult to debug as you wouldn't know which `AnnotationProcessor` handles which `@Annotation` unless the 29 | Javadoc writer of that Annotation is gracious to provide those details. 30 | * You can't use a simple _⌘+Click_ to know what's going on underneath anymore. 31 | * Annotations offer limited type-safety. It’s not possible to specify contextual requirements. Any annotation can go any 32 | type. 33 | * Use of Reflections for Annotations also incur a runtime cost. 34 | * Annotations are not testable. 35 | 36 | Let's understand what kind of validations can services that belong to the same domain have. 37 | 38 | == Service validations that belong to a Domain 39 | 40 | We have a group of services under Payments-Platform domain, such as - Authorization, Capture, Refund, Void. Similar 41 | service groups exist in Tax, Billing, Invoice domains too. All of these are REST-APIs that accept JSON payload. Services 42 | that support _batch_ accept list of JSON sub-requests. A simplified version of a batch payload looks like this: 43 | 44 | [source,jsonc] 45 | ---- 46 | [ 47 | { 48 | "amount": 99, 49 | "accountId": "{{validAccountId}}", 50 | ..., 51 | "paymentMethod": { 52 | ... 53 | }, 54 | ... 55 | }, 56 | { 57 | "amount": 77, 58 | "accountId": "{{validAccountId}}", 59 | ..., 60 | "paymentMethod": { 61 | ... 62 | }, 63 | ... 64 | } 65 | ] 66 | ---- 67 | 68 | This JSON structure gets marshaled into a _Bean/POJO_, which needs to be validated at the entry point of our application 69 | layer. Since all services in this domain deal with similar fields, they have a lot of common fields 70 | like `amount, accountId` etc., as well as common member nodes like `paymentMethod` in their structure. Based on the type 71 | of field, there exist 4 kinds of validations. E.g.: 72 | 73 | * _Common Validations_ - for common fields that exist across services, such as `amount`, `accountId`. 74 | * _Nested Validations_ - for the member nodes like `paymentMethod` . These nested members share an Aggregation/Composition relationship with their container and have validations of their own. A service in the same 75 | domain may reuse this data-structure in its payload. Such service, along with its own validations, needs to execute all the validations of this nested member. 76 | 77 | === Requirements for Validation Orchestration 78 | 79 | Now that we talked about types of validations, let's understand the requirements for validation orchestration (how to 80 | execute these validations). 81 | 82 | * *Share Validations:* Instead of rewriting, Share Common and Nested Validations among services that share payload 83 | structure. 84 | * *2 Routes - 2 execution Strategies:* Our database entities can be CRUD through two routes i.e., *Connect* and **SObject**. 85 | They both need to be guarded with Validations. But the tricky part is - the Connect-route needs to _fail-fast_, while the SObject needs _error-accumulation_. 86 | * *Configure Validation Order for Fail-fast:* A way to configure Cheaper validations first and Costlier later. Costlier 87 | validations can include Effectful validations, so we need to fail-fast and avoid unnecessary DB calls. 88 | * *Partial failures for Batch APIs:* An aggregated error response for failed sub-requests can only be sent after 89 | valid requests are processed through multiple layers of the application. We have to hold on to the invalid 90 | sub-requests till the end and skip them from processing. 91 | * *Meta-requirements:* 92 | ** Accommodate a century of validations across a domain 93 | ** Unit testability for Validations 94 | ** No compromise on Performance 95 | -------------------------------------------------------------------------------- /docs/validation-sharing.adoc: -------------------------------------------------------------------------------- 1 | = Validation Sharing 2 | Gopal S Akshintala 3 | :Revision: 1.0 4 | ifdef::env-github[] 5 | :tip-caption: :bulb: 6 | :note-caption: :information_source: 7 | :important-caption: :heavy_exclamation_mark: 8 | :caution-caption: :fire: 9 | :warning-caption: :warning: 10 | endif::[] 11 | :toc: 12 | :toc-placement: preamble 13 | :imagesdir: images 14 | 15 | Beans sharing common data-structure can share/reuse validations too. Vador supports both: 16 | 17 | * Composition sharing 18 | * Inheritance sharing 19 | 20 | **** 21 | TBD 22 | **** 23 | -------------------------------------------------------------------------------- /docs/validator-types.adoc: -------------------------------------------------------------------------------- 1 | = ƛ Data-types for Validators 2 | Gopal S Akshintala 3 | :Revision: 1.0 4 | ifdef::env-github[] 5 | :tip-caption: :bulb: 6 | :note-caption: :information_source: 7 | :important-caption: :heavy_exclamation_mark: 8 | :caution-caption: :fire: 9 | :warning-caption: :warning: 10 | endif::[] 11 | :toc: 12 | :toc-placement: preamble 13 | :imagesdir: images 14 | 15 | The job of validator is simple, just to convey if a POJO is valid or why it's invalid (in the form of a Validation Failure). 16 | 17 | Vador provides various *Validator Data-Types*, to get this done. 18 | These are https://www.baeldung.com/java-8-functional-interfaces[Functional Interfaces] to which a lambda can be assigned. 19 | Such lambdas are called *https://dzone.com/articles/java-lambda-expressions-functions-as-first-class-citizens[First-Class functions aka Functions as values]* 20 | 21 | [#_validator] 22 | == Validator: `(ValidatableT) -> FailureT` 23 | 24 | The Data type for simple first-class functions. 25 | It takes in a bean to be validated, represented by `ValidatableT`, and returns a failure `FailureT`. 26 | 27 | [source,java,indent=0,options="nowrap"] 28 | ---- 29 | public static final Validator validator = 30 | bean -> { 31 | if (bean == null) { 32 | return new ValidationFailure(FIELD_NULL_OR_EMPTY); 33 | } else { 34 | return ValidationFailure.NONE; 35 | } 36 | }; 37 | ---- 38 | 39 | If you are not comfortable with lambda syntax, you can even write it as a normal function and use the `::` operator to refer the function as a value. 40 | 41 | [source,java,indent=0,options="nowrap"] 42 | ---- 43 | public static ValidationFailure validator(Bean bean) { 44 | if (bean == null) { 45 | return new ValidationFailure(FIELD_NULL_OR_EMPTY); 46 | } else { 47 | return ValidationFailure.NONE; 48 | } 49 | } 50 | ---- 51 | 52 | == If you need more ⚡️Power⚡️ 53 | 54 | image:more-power.gif[inline] 55 | 56 | == ValidatorEtr: `(Either) -> Either` 57 | 58 | === https://docs.vavr.io/#_either[The Either type] 59 | 60 | Unlike `Validator` type (which works with Simple input/output types), `ValidatorEtr` lambda type works with `Either` type as input/output. 61 | The `Either` type is borrowed from https://docs.vavr.io/#_either[Vavr]. 62 | 63 | === What's so powerful about `Either`? 64 | 65 | IMPORTANT: You may need some Functional Programming experience to use this Validator type. 66 | 67 | With `Either`, You get all the functional programming powers. 68 | You can write linear programs with a lot less *Cyclomatic Complexity* & *Cognitive Complexity*. 69 | 70 | Please refer to this tech talk discussing these concepts: https://www.youtube.com/watch?v=Dvr6gx4XaD8&list=PLrJbJ9wDl9EC0bG6y9fyDylcfmB_lT_Or["Fight Complexity with Functional Programming - Gopal S. Akshintala - All Things Open, USA, 2020"] 71 | 72 | Lambdas assigned to `ValidatorEtr` take `Either` as input and should return `Either`. 73 | Since the bean is pre-wrapped in an `Either`, you can perform all the `Either` operations on the input like `map` , `flatMap`, `fold`, `filterOrElse`, etc. 74 | Refer https://www.javadoc.io/doc/io.vavr/vavr/0.10.2/io/vavr/control/Either.html[Either API]. 75 | 76 | If there is a Validation Failure, keep the result in the _left_ state. 77 | If the `Either` in the result is in the _right_ state, it is considered that the bean **Passed** the validation. 78 | The wildcard `?` in the return type `Either` signifies that it doesn't matter what's the value in the right state. 79 | As long as it's on right the validation is considered to be passed ✅ 80 | 81 | [source,java,indent=0,options="nowrap"] 82 | ---- 83 | public static final ValidatorEtr validatorEtr = 84 | beanEtr -> beanEtr.filterOrElse( 85 | bean -> bean.id != null, badBean -> new ValidationFailure(FIELD_NULL_OR_EMPTY)); 86 | ---- 87 | 88 | TIP: Of-course, pre-wrapping bean into `Either` is just to avoid boilerplate. 89 | You can very well use `Validator` and wrap/unwrap the bean in and out of `Either` type, yourself. 90 | 91 | == Why are there different Validator types? 92 | 93 | [.lead] 94 | These types only differ *syntactically* 95 | 96 | NOTE: They are there to help developers focus only on their validation logic, not worry about boilerplate and use a programming style (imperative or functional) of their choice. 97 | You can essentially use any Data type for your validators and in-fact you can even have a mix, based on your needs. 98 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.caching=true 2 | org.gradle.configuration-cache=true 3 | org.gradle.configuration-cache.problems=warn 4 | org.gradle.parallel=true 5 | org.gradle.jvmargs=-XX:+UseParallelGC 6 | kotlin.incremental.useClasspathSnapshot=true 7 | sonar.gradle.skipCompile=true 8 | kotlin.build.report.output=file 9 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/salesforce-misc/Vador/794cdb77d4574c128ed6c0f7137ed0d00c799584/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.12.1-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | @rem SPDX-License-Identifier: Apache-2.0 17 | @rem 18 | 19 | @if "%DEBUG%"=="" @echo off 20 | @rem ########################################################################## 21 | @rem 22 | @rem Gradle startup script for Windows 23 | @rem 24 | @rem ########################################################################## 25 | 26 | @rem Set local scope for the variables with windows NT shell 27 | if "%OS%"=="Windows_NT" setlocal 28 | 29 | set DIRNAME=%~dp0 30 | if "%DIRNAME%"=="" set DIRNAME=. 31 | @rem This is normally unused 32 | set APP_BASE_NAME=%~n0 33 | set APP_HOME=%DIRNAME% 34 | 35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 37 | 38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 40 | 41 | @rem Find java.exe 42 | if defined JAVA_HOME goto findJavaFromJavaHome 43 | 44 | set JAVA_EXE=java.exe 45 | %JAVA_EXE% -version >NUL 2>&1 46 | if %ERRORLEVEL% equ 0 goto execute 47 | 48 | echo. 1>&2 49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 50 | echo. 1>&2 51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 52 | echo location of your Java installation. 1>&2 53 | 54 | goto fail 55 | 56 | :findJavaFromJavaHome 57 | set JAVA_HOME=%JAVA_HOME:"=% 58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 59 | 60 | if exist "%JAVA_EXE%" goto execute 61 | 62 | echo. 1>&2 63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 64 | echo. 1>&2 65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 66 | echo location of your Java installation. 1>&2 67 | 68 | goto fail 69 | 70 | :execute 71 | @rem Setup the command line 72 | 73 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 74 | 75 | 76 | @rem Execute Gradle 77 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 78 | 79 | :end 80 | @rem End local scope for the variables with windows NT shell 81 | if %ERRORLEVEL% equ 0 goto mainEnd 82 | 83 | :fail 84 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 85 | rem the _cmd.exe /c_ return code! 86 | set EXIT_CODE=%ERRORLEVEL% 87 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 88 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 89 | exit /b %EXIT_CODE% 90 | 91 | :mainEnd 92 | if "%OS%"=="Windows_NT" endlocal 93 | 94 | :omega 95 | -------------------------------------------------------------------------------- /libs.versions.toml: -------------------------------------------------------------------------------- 1 | [versions] 2 | jdk = "11" 3 | kotlin = "2.1.10" 4 | java-vavr = "0.10.6" 5 | kotlin-vavr = "0.10.2" 6 | lombok-gradle = "8.12.1" 7 | hamcrest-core = "3.0" 8 | hamcrest-date = "2.0.8" 9 | typeTools = "0.6.3" 10 | spotbugs = "6.1.5" 11 | assertj-vavr = "0.4.3" 12 | reflection-util = "2.15.0" # 2.15.0 is Compatable with Java 11 13 | nexus-publish = "2.0.0" 14 | apache-common-text-version = "1.13.0" 15 | 16 | # Common dependencies 17 | junit = "5.12.0" 18 | kover = "0.9.1" 19 | kotest = "5.9.1" 20 | assertj-core = "3.27.3" 21 | detekt = "1.23.8" 22 | spotless = "7.0.2" 23 | apache-log4j = "2.24.3" 24 | testLogger = "4.0.0" 25 | jetbrains-annotations = "26.0.2" 26 | 27 | [libraries] 28 | hamcrest-core = { module = "org.hamcrest:hamcrest", version.ref = "hamcrest-core" } 29 | hamcrest-date = { module = "org.exparity:hamcrest-date", version.ref = "hamcrest-date" } 30 | java-vavr = { module = "io.vavr:vavr", version.ref = "java-vavr" } 31 | kotlin-vavr = { module = "io.vavr:vavr-kotlin", version.ref = "kotlin-vavr" } 32 | jetbrains-annotations = { module = "org.jetbrains:annotations", version.ref = "jetbrains-annotations" } 33 | typeTools = { module = "net.jodah:typetools", version.ref = "typeTools" } 34 | reflection-util = { module = "de.cronn:reflection-util", version.ref = "reflection-util" } 35 | testLogger-gradle = { module = "com.adarshr.test-logger:com.adarshr.test-logger.gradle.plugin", version.ref = "testLogger" } 36 | apache-common-text = { module = "org.apache.commons:commons-text", version.ref = "apache-common-text-version" } 37 | kotest-assertionsCore = { module = "io.kotest:kotest-assertions-core", version.ref = "kotest" } 38 | kotest-frameworkEngine = { module = "io.kotest:kotest-framework-engine", version.ref = "kotest" } 39 | kotest-property = { module = "io.kotest:kotest-property", version.ref = "kotest" } 40 | kotest-runnerJUnit5 = { module = "io.kotest:kotest-runner-junit5", version.ref = "kotest" } 41 | assertj-core = { module = "org.assertj:assertj-core", version.ref = "assertj-core" } 42 | assertj-vavr = { module = "org.assertj:assertj-vavr", version.ref = "assertj-vavr" } 43 | junit-bom = { module = "org.junit:junit-bom", version.ref = "junit" } 44 | junit-jupiter = { module = "org.junit.jupiter:junit-jupiter-api", version.ref = "junit" } 45 | junit-engine = { module = "org.junit.jupiter:junit-jupiter-engine" } 46 | log4j-api = { module = "org.apache.logging.log4j:log4j-api", version.ref = "apache-log4j" } 47 | log4j-core = { module = "org.apache.logging.log4j:log4j-core", version.ref = "apache-log4j" } 48 | log4j-slf4j2-impl = { module = "org.apache.logging.log4j:log4j-slf4j2-impl", version.ref = "apache-log4j" } 49 | 50 | # Gradle plugins 51 | kotlin-gradle = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" } 52 | detekt-gradle = { module = "io.gitlab.arturbosch.detekt:detekt-gradle-plugin", version.ref = "detekt" } 53 | kover-gradle = { module = "org.jetbrains.kotlinx.kover:org.jetbrains.kotlinx.kover.gradle.plugin", version.ref = "kover" } 54 | spotless-gradle = { module = "com.diffplug.spotless:spotless-plugin-gradle", version.ref = "spotless" } 55 | spotbugs-gradle = { module = "com.github.spotbugs.snom:spotbugs-gradle-plugin", version.ref = "spotbugs" } 56 | 57 | [bundles] 58 | kotest = [ 59 | "kotest-assertionsCore", 60 | "kotest-frameworkEngine", 61 | "kotest-property", 62 | "kotest-runnerJUnit5", 63 | ] 64 | junit = ["junit-jupiter", "junit-engine"] 65 | apache-log4j = ["log4j-api", "log4j-core", "log4j-slf4j2-impl"] 66 | 67 | [plugins] 68 | kover = { id = "org.jetbrains.kotlinx.kover", version.ref = "kover" } 69 | kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } 70 | detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" } 71 | spotless = { id = "com.diffplug.spotless", version.ref = "spotless" } 72 | spotbugs = { id = "com.github.spotbugs", version.ref = "spotbugs" } 73 | lombok-gradle = { id = "io.freefair.lombok", version.ref = "lombok-gradle" } 74 | testLogger = { id = "com.adarshr.test-logger", version.ref = "testLogger" } 75 | nexus-publish = { id = "io.github.gradle-nexus.publish-plugin", version.ref = "nexus-publish" } 76 | -------------------------------------------------------------------------------- /lombok.config: -------------------------------------------------------------------------------- 1 | # This file is generated by the 'io.freefair.lombok' Gradle plugin 2 | config.stopBubbling = true 3 | lombok.addLombokGeneratedAnnotation = true 4 | lombok.extern.findbugs.addSuppressFBWarnings = true 5 | -------------------------------------------------------------------------------- /matchers/build.gradle.kts: -------------------------------------------------------------------------------- 1 | /** 2 | * **************************************************************************** 3 | * Copyright (c) 2022, salesforce.com, inc. All rights reserved. SPDX-License-Identifier: 4 | * BSD-3-Clause For full license text, see the LICENSE file in the repo root or 5 | * https://opensource.org/licenses/BSD-3-Clause 6 | * **************************************************************************** 7 | */ 8 | dependencies { 9 | implementation(libs.hamcrest.core) 10 | implementation(libs.hamcrest.date) 11 | api(libs.kotlin.vavr) 12 | api(libs.java.vavr) 13 | testImplementation(libs.bundles.kotest) 14 | } 15 | -------------------------------------------------------------------------------- /matchers/src/main/kotlin/com/salesforce/vador/matchers/AnyMatchers.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * **************************************************************************** 3 | * Copyright (c) 2022, salesforce.com, inc. All rights reserved. SPDX-License-Identifier: 4 | * BSD-3-Clause For full license text, see the LICENSE file in the repo root or 5 | * https://opensource.org/licenses/BSD-3-Clause 6 | * **************************************************************************** 7 | */ 8 | @file:JvmName("AnyMatchers") 9 | 10 | package com.salesforce.vador.matchers 11 | 12 | import org.hamcrest.Matcher 13 | import org.hamcrest.Matchers.anyOf 14 | import org.hamcrest.Matchers.`is` 15 | import org.hamcrest.core.AnyOf 16 | import org.hamcrest.core.IsNull 17 | 18 | fun anyOf(vararg ts: T): AnyOf = anyOf(ts.map { `is`(it) }) 19 | 20 | @SafeVarargs fun anyOf(vararg matchers: Matcher): AnyOf = anyOf(*matchers) 21 | 22 | @SafeVarargs 23 | fun anyOfOrNull(vararg matchers: Matcher): AnyOf = anyOf(matchers.toList() + IsNull()) 24 | -------------------------------------------------------------------------------- /matchers/src/main/kotlin/com/salesforce/vador/matchers/DateMatchers.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * **************************************************************************** 3 | * Copyright (c) 2022, salesforce.com, inc. All rights reserved. SPDX-License-Identifier: 4 | * BSD-3-Clause For full license text, see the LICENSE file in the repo root or 5 | * https://opensource.org/licenses/BSD-3-Clause 6 | * **************************************************************************** 7 | */ 8 | @file:JvmName("DateMatchers") 9 | 10 | package com.salesforce.vador.matchers 11 | 12 | import io.vavr.Function2 13 | import java.time.LocalDate 14 | import java.time.format.DateTimeFormatter 15 | import java.util.Date 16 | import org.hamcrest.Description 17 | import org.hamcrest.Matcher 18 | import org.hamcrest.TypeSafeMatcher 19 | 20 | val isOnOrBeforeIfBothArePresent = Function2 { date1: Any?, date2: Any? -> 21 | when { 22 | date1 == null || date2 == null -> true 23 | date1 !is Date || date2 !is Date -> false 24 | else -> date1 == date2 || date1.before(date2) 25 | } 26 | } 27 | 28 | val isBeforeIfBothArePresent = Function2 { date1: Any?, date2: Any? -> 29 | when { 30 | date1 == null && date2 == null -> true 31 | date1 !is Date || date2 !is Date -> false 32 | else -> date1.before(date2) 33 | } 34 | } 35 | 36 | @Suppress("DEPRECATION") 37 | val isEqualToDayOfDate = Function2 { day: Any?, date: Any? -> 38 | when { 39 | day == null && date == null -> false 40 | day !is Int || date !is Date -> false 41 | else -> day == date.date 42 | } 43 | } 44 | 45 | @get:JvmName("ISO8601DateFormat") 46 | val ISO8601DateFormat: Matcher = 47 | object : TypeSafeMatcher() { 48 | 49 | override fun describeTo(description: Description?) { 50 | description?.appendText("ISO 8601 format") 51 | } 52 | 53 | override fun matchesSafely(date: Any?): Boolean = 54 | date is String && isValidIS0LocalDateFormat(date) 55 | } 56 | 57 | private fun isValidIS0LocalDateFormat(dateAsString: String): Boolean = 58 | runCatching { parseStringToISOLocalDate(dateAsString) }.isSuccess 59 | 60 | private fun parseStringToISOLocalDate(date: String): LocalDate = 61 | LocalDate.parse(date.trim(), DateTimeFormatter.ISO_LOCAL_DATE) 62 | -------------------------------------------------------------------------------- /matchers/src/main/kotlin/com/salesforce/vador/matchers/IntMatchers.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * **************************************************************************** 3 | * Copyright (c) 2022, salesforce.com, inc. All rights reserved. SPDX-License-Identifier: 4 | * BSD-3-Clause For full license text, see the LICENSE file in the repo root or 5 | * https://opensource.org/licenses/BSD-3-Clause 6 | * **************************************************************************** 7 | */ 8 | @file:JvmName("IntMatchers") 9 | 10 | package com.salesforce.vador.matchers 11 | 12 | import org.hamcrest.Matcher 13 | import org.hamcrest.Matchers.allOf 14 | import org.hamcrest.Matchers.greaterThanOrEqualTo 15 | import org.hamcrest.Matchers.lessThanOrEqualTo 16 | 17 | fun inRangeInclusive(start: Int, end: Int): Matcher = 18 | allOf(greaterThanOrEqualTo(start), lessThanOrEqualTo(end)) 19 | -------------------------------------------------------------------------------- /matchers/src/main/kotlin/com/salesforce/vador/relates/Relates.kt: -------------------------------------------------------------------------------- 1 | package com.salesforce.vador.relates 2 | 3 | fun onlyOneShouldBeNonNull(w: WhenT, t: ThenT): Boolean = 4 | listOfNotNull(w, t).size == 1 5 | -------------------------------------------------------------------------------- /matchers/src/test/java/com/salesforce/vador/matchers/AnyMatchersTest.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2022, salesforce.com, inc. 3 | * All rights reserved. 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause 6 | ******************************************************************************/ 7 | 8 | package com.salesforce.vador.matchers; 9 | 10 | import static com.salesforce.vador.matchers.IntMatchers.inRangeInclusive; 11 | import static org.hamcrest.Matchers.lessThan; 12 | 13 | import org.junit.jupiter.api.Assertions; 14 | import org.junit.jupiter.api.Test; 15 | 16 | class AnyMatchersTest { 17 | 18 | @Test 19 | void anyOfOrNullMatchersTest() { 20 | Assertions.assertTrue(AnyMatchers.anyOfOrNull(inRangeInclusive(1, 31)).matches(null)); 21 | Assertions.assertTrue(AnyMatchers.anyOfOrNull(inRangeInclusive(1, 31)).matches(1)); 22 | Assertions.assertFalse(AnyMatchers.anyOfOrNull(inRangeInclusive(1, 31)).matches(0)); 23 | } 24 | 25 | @Test 26 | void anyOfMatchersTest() { 27 | Assertions.assertTrue(AnyMatchers.anyOf(inRangeInclusive(1, 31), lessThan(10)).matches(0)); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /matchers/src/test/java/com/salesforce/vador/matchers/DateMatchersTest.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * **************************************************************************** 3 | * Copyright (c) 2022, salesforce.com, inc. All rights reserved. SPDX-License-Identifier: 4 | * BSD-3-Clause For full license text, see the LICENSE file in the repo root or 5 | * https://opensource.org/licenses/BSD-3-Clause 6 | * **************************************************************************** 7 | */ 8 | package com.salesforce.vador.matchers 9 | 10 | import io.kotest.core.spec.style.StringSpec 11 | import io.kotest.data.forAll 12 | import io.kotest.data.row 13 | import io.kotest.matchers.shouldBe 14 | import java.util.Calendar 15 | import java.util.GregorianCalendar 16 | 17 | class DateMatchersTest : 18 | StringSpec({ 19 | "is Date's day matching" { 20 | forAll( 21 | row(1, GregorianCalendar(2021, Calendar.FEBRUARY, 1).time, true), 22 | row(2, GregorianCalendar(2021, Calendar.FEBRUARY, 1).time, false), 23 | row(null, GregorianCalendar(2021, Calendar.FEBRUARY, 1).time, false), 24 | row(null, null, false), 25 | ) { day, date, result -> 26 | isEqualToDayOfDate.apply(day, date) shouldBe result 27 | } 28 | } 29 | 30 | "ISO8601Format" { 31 | ISO8601DateFormat.matches("2021-07-30") shouldBe true 32 | ISO8601DateFormat.matches("2019-02-05T21:22:41.000Z") shouldBe false 33 | } 34 | 35 | "Is On Or Before If Both Are Present" { 36 | forAll( 37 | row(null, null, true), 38 | row(null, "", true), 39 | row("2021-12-28", "2021-12-28", false), 40 | row(GregorianCalendar(2021, Calendar.FEBRUARY, 1).time, "", false), 41 | row( 42 | GregorianCalendar(2021, Calendar.FEBRUARY, 2).time, 43 | GregorianCalendar(2021, Calendar.FEBRUARY, 1).time, 44 | false, 45 | ), 46 | row( 47 | GregorianCalendar(2021, Calendar.FEBRUARY, 1).time, 48 | GregorianCalendar(2021, Calendar.FEBRUARY, 1).time, 49 | true, 50 | ), 51 | row( 52 | GregorianCalendar(2021, Calendar.JANUARY, 31).time, 53 | GregorianCalendar(2021, Calendar.FEBRUARY, 1).time, 54 | true, 55 | ), 56 | ) { date1, date2, result -> 57 | isOnOrBeforeIfBothArePresent.apply(date1, date2) shouldBe result 58 | } 59 | } 60 | }) 61 | -------------------------------------------------------------------------------- /matchers/src/test/resources/log4j2.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends" : [ 3 | "config:base" 4 | ], 5 | "automerge" : true, 6 | "commitBodyTable" : true, 7 | "packageRules" : [ 8 | { 9 | "matchPackagePatterns" : [ 10 | "*" 11 | ], 12 | "matchUpdateTypes" : [ 13 | "major", 14 | "minor", 15 | "patch" 16 | ], 17 | "groupName" : "all dependencies", 18 | "groupSlug" : "all" 19 | }, 20 | { 21 | "matchPackageNames": [ 22 | "de.cronn:reflection-util" 23 | ], 24 | "enabled": false 25 | } 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | /** 2 | * **************************************************************************** 3 | * Copyright (c) 2022, salesforce.com, inc. All rights reserved. SPDX-License-Identifier: 4 | * BSD-3-Clause For full license text, see the LICENSE file in the repo root or 5 | * https://opensource.org/licenses/BSD-3-Clause 6 | * **************************************************************************** 7 | */ 8 | plugins { id("com.gradle.develocity") version "4.0" } 9 | 10 | dependencyResolutionManagement { 11 | versionCatalogs { create("libs") { from(files("libs.versions.toml")) } } 12 | } 13 | 14 | val isCI = !System.getenv("CI").isNullOrEmpty() 15 | 16 | develocity { 17 | buildScan { 18 | publishing.onlyIf { 19 | it.buildResult.failures.isNotEmpty() && !System.getenv("CI").isNullOrEmpty() 20 | } 21 | uploadInBackground.set(!isCI) 22 | termsOfUseUrl = "https://gradle.com/help/legal-terms-of-use" 23 | termsOfUseAgree = "yes" 24 | } 25 | } 26 | 27 | rootProject.name = "vador-root" 28 | 29 | include("matchers") 30 | 31 | include("vador") 32 | -------------------------------------------------------------------------------- /sonar-project.properties: -------------------------------------------------------------------------------- 1 | sonar.issue.ignore.multicriteria=e1,e2 2 | sonar.issue.ignore.multicriteria.e1.ruleKey=squid:S00119 3 | sonar.issue.ignore.multicriteria.e1.resourceKey=**/*.java 4 | sonar.issue.ignore.multicriteria.e2.ruleKey=squid:S00119 5 | sonar.issue.ignore.multicriteria.e2.resourceKey=**/*.kt 6 | sonar.sources=vador/src/main,specs/src/main,matchers/src/main 7 | sonar.tests=vador/src/test,specs/src/test,matchers/src/test 8 | sonar.coverage.jacoco.xmlReportPaths=build/reports/kover/merged/xml/report.xml 9 | sonar.kotlin.detekt.reportPaths=build/reports/detekt/merge.xml 10 | detekt.sonar.kotlin.config.path=config/detekt/detekt.yml 11 | -------------------------------------------------------------------------------- /vador/build.gradle.kts: -------------------------------------------------------------------------------- 1 | /** 2 | * **************************************************************************** 3 | * Copyright (c) 2022, salesforce.com, inc. All rights reserved. SPDX-License-Identifier: 4 | * BSD-3-Clause For full license text, see the LICENSE file in the repo root or 5 | * https://opensource.org/licenses/BSD-3-Clause 6 | * **************************************************************************** 7 | */ 8 | plugins { alias(libs.plugins.lombok.gradle) } 9 | 10 | dependencies { 11 | implementation(libs.hamcrest.core) 12 | implementation(libs.hamcrest.date) 13 | implementation(libs.java.vavr) 14 | implementation(libs.kotlin.vavr) 15 | implementation(libs.typeTools) 16 | implementation(libs.apache.common.text) 17 | compileOnly(libs.jetbrains.annotations) 18 | api(libs.reflection.util) 19 | implementation(libs.bundles.apache.log4j) 20 | testImplementation(project(":matchers")) 21 | testImplementation(libs.assertj.vavr) 22 | testImplementation(libs.assertj.core) 23 | testImplementation(libs.bundles.kotest) 24 | } 25 | 26 | if (!System.getProperty("idea.sync.active").toBoolean()) { 27 | kotlin.sourceSets.main { kotlin.setSrcDirs(listOf(tasks.delombok)) } 28 | } 29 | 30 | tasks { 31 | delombok { 32 | quiet.set(true) 33 | input.setFrom("src/main/java") 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /vador/src/main/java/com/salesforce/vador/annotation/MaxForInt.kt: -------------------------------------------------------------------------------- 1 | package com.salesforce.vador.annotation 2 | 3 | import com.salesforce.vador.types.ValidatorAnnotation2 4 | import java.lang.reflect.Field 5 | 6 | @Retention(AnnotationRetention.RUNTIME) 7 | @Target(AnnotationTarget.FIELD) 8 | annotation class MaxForInt(val limit: Int, val failureKey: String) 9 | 10 | class MaxForIntValidator : ValidatorAnnotation2 { 11 | override fun validate( 12 | field: Field, 13 | value1: Int, 14 | value2: Int, 15 | failure: FailureT, 16 | none: FailureT, 17 | ): FailureT { 18 | if (value1 > value2) { 19 | return failure 20 | } 21 | return none 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /vador/src/main/java/com/salesforce/vador/annotation/MinForInt.kt: -------------------------------------------------------------------------------- 1 | package com.salesforce.vador.annotation 2 | 3 | import com.salesforce.vador.types.ValidatorAnnotation2 4 | import java.lang.reflect.Field 5 | 6 | @Retention(AnnotationRetention.RUNTIME) 7 | @Target(AnnotationTarget.FIELD) 8 | annotation class MinForInt(val limit: Int, val failureKey: String) 9 | 10 | class MinForIntValidator : ValidatorAnnotation2 { 11 | override fun validate( 12 | field: Field, 13 | value1: Int, 14 | value2: Int, 15 | failure: FailureT, 16 | none: FailureT, 17 | ): FailureT { 18 | if (value1 < value2) { 19 | return failure 20 | } 21 | return none 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /vador/src/main/java/com/salesforce/vador/annotation/Negative.kt: -------------------------------------------------------------------------------- 1 | package com.salesforce.vador.annotation 2 | 3 | import com.salesforce.vador.types.ValidatorAnnotation1 4 | import java.lang.reflect.Field 5 | 6 | @Retention(AnnotationRetention.RUNTIME) 7 | @Target(AnnotationTarget.FIELD) 8 | annotation class Negative(val failureKey: String) 9 | 10 | class NegativeValidator : ValidatorAnnotation1 { 11 | override fun validate(field: Field, value: Int?, failure: FailureT, none: FailureT): FailureT { 12 | if (value != null && value > -1) { 13 | return failure 14 | } 15 | return none 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /vador/src/main/java/com/salesforce/vador/annotation/NonNegative.kt: -------------------------------------------------------------------------------- 1 | package com.salesforce.vador.annotation 2 | 3 | import com.salesforce.vador.types.ValidatorAnnotation1 4 | import java.lang.reflect.Field 5 | 6 | @Retention(AnnotationRetention.RUNTIME) 7 | @Target(AnnotationTarget.FIELD) 8 | annotation class NonNegative(val failureKey: String) 9 | 10 | class NonNegativeValidator : ValidatorAnnotation1 { 11 | override fun validate(field: Field, value: Int?, failure: FailureT, none: FailureT): FailureT { 12 | if (value != null && value < 0) { 13 | return failure 14 | } 15 | return none 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /vador/src/main/java/com/salesforce/vador/annotation/Positive.kt: -------------------------------------------------------------------------------- 1 | package com.salesforce.vador.annotation 2 | 3 | import com.salesforce.vador.types.ValidatorAnnotation1 4 | import java.lang.reflect.Field 5 | 6 | @Retention(AnnotationRetention.RUNTIME) 7 | @Target(AnnotationTarget.FIELD) 8 | annotation class Positive(val failureKey: String) 9 | 10 | class PositiveValidator : ValidatorAnnotation1 { 11 | override fun validate(field: Field, value: Int?, failure: FailureT, none: FailureT): FailureT { 12 | if (value != null && value < 1) { 13 | return failure 14 | } 15 | return none 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /vador/src/main/java/com/salesforce/vador/annotation/Required.kt: -------------------------------------------------------------------------------- 1 | package com.salesforce.vador.annotation 2 | 3 | import com.salesforce.vador.execution.strategies.util.isFieldPresent 4 | import com.salesforce.vador.types.ValidatorAnnotation1 5 | import java.lang.reflect.Field 6 | import java.util.* 7 | 8 | @Retention(AnnotationRetention.RUNTIME) 9 | @Target(AnnotationTarget.FIELD) 10 | annotation class Required(val failureKey: String) 11 | 12 | class RequiredValidator : ValidatorAnnotation1 { 13 | override fun validate(field: Field, value: T?, failure: FailureT, none: FailureT): FailureT { 14 | if (isFieldPresent(value)) return none else return failure 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /vador/src/main/java/com/salesforce/vador/annotation/ValidateWith.kt: -------------------------------------------------------------------------------- 1 | package com.salesforce.vador.annotation 2 | 3 | import kotlin.reflect.KClass 4 | 5 | @kotlin.annotation.Retention(AnnotationRetention.RUNTIME) 6 | @kotlin.annotation.Target(AnnotationTarget.FIELD) 7 | annotation class ValidateWith(val validator: KClass<*>, val failureKey: String) 8 | -------------------------------------------------------------------------------- /vador/src/main/java/com/salesforce/vador/config/BatchOfBatch1ValidationConfig.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2022, salesforce.com, inc. 3 | * All rights reserved. 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause 6 | ******************************************************************************/ 7 | 8 | /******************************************************************************* 9 | * Copyright (c) 2022, salesforce.com, inc. 10 | * All rights reserved. 11 | * SPDX-License-Identifier: BSD-3-Clause 12 | * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause 13 | ******************************************************************************/ 14 | 15 | package com.salesforce.vador.config; 16 | 17 | import com.salesforce.vador.config.base.BaseBatchValidationConfig; 18 | import io.vavr.Function1; 19 | import io.vavr.Tuple2; 20 | import java.util.Collection; 21 | import lombok.AccessLevel; 22 | import lombok.EqualsAndHashCode; 23 | import lombok.Value; 24 | import lombok.experimental.FieldDefaults; 25 | import lombok.experimental.SuperBuilder; 26 | import org.jetbrains.annotations.Nullable; 27 | 28 | /** 29 | * This should be used for Batch that contains (HAS-A) a nested Batch (of type `Collection`) member 30 | * and the member needs a BatchValidationConfig of its own. 31 | * 32 | *

* `1` in the data type indicates the number of batch member types this config supports. 33 | * 34 | *

* For other Simple fields, please use `liftUtil` to lift corresponding validators. 35 | * 36 | * @param Container data type 37 | * @param Batch Member data type 38 | * @param 39 | */ 40 | @Value 41 | @FieldDefaults(level = AccessLevel.PACKAGE) 42 | @EqualsAndHashCode(callSuper = true) 43 | @SuperBuilder(buildMethodName = "prepare", builderMethodName = "toValidate", toBuilder = true) 44 | public class BatchOfBatch1ValidationConfig 45 | extends BaseBatchValidationConfig { 46 | Tuple2< 47 | Function1>, 48 | BatchValidationConfig> 49 | withMemberBatchValidationConfig; 50 | } 51 | -------------------------------------------------------------------------------- /vador/src/main/java/com/salesforce/vador/config/BatchValidationConfig.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2022, salesforce.com, inc. 3 | * All rights reserved. 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause 6 | ******************************************************************************/ 7 | 8 | /******************************************************************************* 9 | * Copyright (c) 2022, salesforce.com, inc. 10 | * All rights reserved. 11 | * SPDX-License-Identifier: BSD-3-Clause 12 | * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause 13 | ******************************************************************************/ 14 | 15 | package com.salesforce.vador.config; 16 | 17 | import com.salesforce.vador.config.base.BaseBatchValidationConfig; 18 | import lombok.EqualsAndHashCode; 19 | import lombok.Value; 20 | import lombok.experimental.SuperBuilder; 21 | 22 | @Value 23 | @EqualsAndHashCode(callSuper = true) 24 | @SuperBuilder(buildMethodName = "prepare", builderMethodName = "toValidate", toBuilder = true) 25 | public class BatchValidationConfig 26 | extends BaseBatchValidationConfig {} 27 | -------------------------------------------------------------------------------- /vador/src/main/java/com/salesforce/vador/config/FieldConfig.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2022, salesforce.com, inc. 3 | * All rights reserved. 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause 6 | ******************************************************************************/ 7 | 8 | /******************************************************************************* 9 | * Copyright (c) 2022, salesforce.com, inc. 10 | * All rights reserved. 11 | * SPDX-License-Identifier: BSD-3-Clause 12 | * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause 13 | ******************************************************************************/ 14 | 15 | package com.salesforce.vador.config; 16 | 17 | import de.cronn.reflection.util.TypedPropertyGetter; 18 | import io.vavr.Function2; 19 | import io.vavr.Tuple2; 20 | import java.util.Collection; 21 | import java.util.Map; 22 | import java.util.function.Predicate; 23 | import lombok.AccessLevel; 24 | import lombok.Builder; 25 | import lombok.NonNull; 26 | import lombok.Singular; 27 | import lombok.Value; 28 | import lombok.experimental.FieldDefaults; 29 | import org.jetbrains.annotations.Nullable; 30 | 31 | @Value 32 | @FieldDefaults(level = AccessLevel.PACKAGE) 33 | @Builder(buildMethodName = "prepare", builderMethodName = "toValidate", toBuilder = true) 34 | public class FieldConfig { 35 | @Nullable Predicate withFieldValidator; 36 | 37 | @Singular("shouldHaveValidFormatOrFailWith") 38 | Map, FailureT> shouldHaveValidFormatForAllOrFailWith; 39 | 40 | @Nullable 41 | Tuple2< 42 | @NonNull Collection<@NonNull TypedPropertyGetter>, 43 | @NonNull Function2> 44 | shouldHaveValidFormatForAllOrFailWithFn; 45 | 46 | @Singular("shouldHaveValidFormatOrFailWithFn") 47 | Map, Function2> 48 | shouldHaveValidFormatOrFailWithFn; 49 | 50 | @Singular("absentOrHaveValidFormatOrFailWith") 51 | Map, FailureT> absentOrHaveValidFormatForAllOrFailWith; 52 | 53 | @Nullable 54 | Tuple2< 55 | @NonNull Collection<@NonNull TypedPropertyGetter>, 56 | @NonNull Function2> 57 | absentOrHaveValidFormatForAllOrFailWithFn; 58 | 59 | @Singular("absentOrHaveValidFormatOrFailWithFn") 60 | Map, Function2> 61 | absentOrHaveValidFormatOrFailWithFn; 62 | } 63 | -------------------------------------------------------------------------------- /vador/src/main/java/com/salesforce/vador/config/FilterDuplicatesConfig.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2022, salesforce.com, inc. 3 | * All rights reserved. 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause 6 | ******************************************************************************/ 7 | 8 | /******************************************************************************* 9 | * Copyright (c) 2022, salesforce.com, inc. 10 | * All rights reserved. 11 | * SPDX-License-Identifier: BSD-3-Clause 12 | * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause 13 | ******************************************************************************/ 14 | 15 | package com.salesforce.vador.config; 16 | 17 | import io.vavr.Function1; 18 | import lombok.AccessLevel; 19 | import lombok.Builder; 20 | import lombok.Value; 21 | import lombok.experimental.FieldDefaults; 22 | import org.jetbrains.annotations.Nullable; 23 | 24 | @Value 25 | @FieldDefaults(level = AccessLevel.PACKAGE) 26 | @Builder(buildMethodName = "prepare", builderMethodName = "toValidate", toBuilder = true) 27 | public class FilterDuplicatesConfig { 28 | // `andFailDuplicatesWith` is not mandatory for `findAndFilterDuplicatesWith`. 29 | // You may want to just filter without failing duplicates. So they are separated 30 | @Nullable Function1 findAndFilterDuplicatesWith; 31 | @Nullable FailureT andFailDuplicatesWith; 32 | @Nullable FailureT andFailNullKeysWith; 33 | } 34 | -------------------------------------------------------------------------------- /vador/src/main/java/com/salesforce/vador/config/ValidationConfig.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2022, salesforce.com, inc. 3 | * All rights reserved. 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause 6 | ******************************************************************************/ 7 | 8 | /******************************************************************************* 9 | * Copyright (c) 2022, salesforce.com, inc. 10 | * All rights reserved. 11 | * SPDX-License-Identifier: BSD-3-Clause 12 | * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause 13 | ******************************************************************************/ 14 | 15 | package com.salesforce.vador.config; 16 | 17 | import com.salesforce.vador.config.base.BaseValidationConfig; 18 | import lombok.AccessLevel; 19 | import lombok.EqualsAndHashCode; 20 | import lombok.Value; 21 | import lombok.experimental.FieldDefaults; 22 | import lombok.experimental.SuperBuilder; 23 | 24 | @Value 25 | @FieldDefaults(level = AccessLevel.PACKAGE) 26 | @EqualsAndHashCode(callSuper = true) 27 | @SuperBuilder(buildMethodName = "prepare", builderMethodName = "toValidate", toBuilder = true) 28 | public class ValidationConfig 29 | extends BaseValidationConfig {} 30 | -------------------------------------------------------------------------------- /vador/src/main/java/com/salesforce/vador/config/base/BaseBatchValidationConfig.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2022, salesforce.com, inc. 3 | * All rights reserved. 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause 6 | ******************************************************************************/ 7 | 8 | /******************************************************************************* 9 | * Copyright (c) 2022, salesforce.com, inc. 10 | * All rights reserved. 11 | * SPDX-License-Identifier: BSD-3-Clause 12 | * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause 13 | ******************************************************************************/ 14 | 15 | package com.salesforce.vador.config.base; 16 | 17 | import com.salesforce.vador.config.FilterDuplicatesConfig.FilterDuplicatesConfigBuilder; 18 | import java.util.Collection; 19 | import lombok.EqualsAndHashCode; 20 | import lombok.Getter; 21 | import lombok.Singular; 22 | import lombok.experimental.SuperBuilder; 23 | import org.jetbrains.annotations.Nullable; 24 | 25 | @Getter 26 | @EqualsAndHashCode(callSuper = true) 27 | @SuperBuilder(buildMethodName = "prepare", builderMethodName = "toValidate", toBuilder = true) 28 | public abstract class BaseBatchValidationConfig 29 | extends BaseValidationConfig { 30 | 31 | @Singular 32 | protected Collection> 33 | findAndFilterDuplicatesConfigs; 34 | } 35 | -------------------------------------------------------------------------------- /vador/src/main/java/com/salesforce/vador/config/base/BaseContainerValidationConfig.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2022, salesforce.com, inc. 3 | * All rights reserved. 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause 6 | ******************************************************************************/ 7 | 8 | /******************************************************************************* 9 | * Copyright (c) 2022, salesforce.com, inc. 10 | * All rights reserved. 11 | * SPDX-License-Identifier: BSD-3-Clause 12 | * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause 13 | ******************************************************************************/ 14 | 15 | package com.salesforce.vador.config.base; 16 | 17 | import com.salesforce.vador.config.container.ContainerValidationConfigEx; 18 | import com.salesforce.vador.types.Validator; 19 | import com.salesforce.vador.types.ValidatorEtr; 20 | import io.vavr.Tuple2; 21 | import java.util.Collection; 22 | import java.util.List; 23 | import java.util.Map; 24 | import lombok.Getter; 25 | import lombok.NonNull; 26 | import lombok.Singular; 27 | import lombok.experimental.SuperBuilder; 28 | import org.jetbrains.annotations.Nullable; 29 | 30 | @Getter 31 | @SuperBuilder(buildMethodName = "prepare", builderMethodName = "toValidate", toBuilder = true) 32 | public abstract class BaseContainerValidationConfig { 33 | @Nullable protected Tuple2<@NonNull Integer, @Nullable FailureT> shouldHaveMinBatchSizeOrFailWith; 34 | @Nullable protected Tuple2<@NonNull Integer, @Nullable FailureT> shouldHaveMaxBatchSizeOrFailWith; 35 | 36 | @Singular 37 | protected Collection> withContainerValidatorEtrs; 38 | 39 | @Nullable 40 | protected Tuple2< 41 | @NonNull Collection>, 42 | @Nullable FailureT> 43 | withContainerValidators; 44 | 45 | @Singular("withContainerValidator") 46 | protected Map, FailureT> 47 | withContainerValidator; 48 | 49 | public List> getContainerValidators() { 50 | return ContainerValidationConfigEx.getContainerValidatorsEx(this); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /vador/src/main/java/com/salesforce/vador/config/base/BaseValidationConfigEx.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * **************************************************************************** 3 | * Copyright (c) 2022, salesforce.com, inc. All rights reserved. SPDX-License-Identifier: 4 | * BSD-3-Clause For full license text, see the LICENSE file in the repo root or 5 | * https://opensource.org/licenses/BSD-3-Clause 6 | * **************************************************************************** 7 | */ 8 | @file:JvmName("BaseValidationConfigEx") 9 | 10 | package com.salesforce.vador.config.base 11 | 12 | import com.salesforce.vador.specs.factory.SpecFactory 13 | import com.salesforce.vador.specs.specs.Spec1 14 | import com.salesforce.vador.specs.specs.Spec2 15 | import com.salesforce.vador.specs.specs.Spec3 16 | import com.salesforce.vador.specs.specs.base.BaseSpec 17 | import com.salesforce.vador.types.Validator 18 | import de.cronn.reflection.util.PropertyUtils 19 | import de.cronn.reflection.util.TypedPropertyGetter 20 | import io.vavr.Function1 21 | import java.lang.reflect.Type 22 | import java.util.Optional 23 | import java.util.function.Predicate 24 | import net.jodah.typetools.TypeResolver 25 | 26 | internal fun BaseValidationConfig.getSpecsEx(): 27 | List> { 28 | val specFactory = SpecFactory() 29 | return (specify?.invoke(specFactory)?.map { it.done() as BaseSpec } 30 | ?: emptyList()) + 31 | withSpecs.map { it.invoke(specFactory).done() as BaseSpec } 32 | } 33 | 34 | internal fun BaseValidationConfig 35 | .getPredicateOfSpecForTestEx(nameForTest: String): Optional> { 36 | // TODO 29/04/21 gopala.akshintala: Move this duplicate-check to ValidationConfig `prepare` 37 | val specNameToSpecs = 38 | specs.groupingBy { it.nameForTest }.eachCount().filter { it.value > 1 }.keys.filterNotNull() 39 | require(specNameToSpecs.isEmpty()) { "Specs with Duplicate NamesForTest found: $specNameToSpecs" } 40 | return Optional.ofNullable(specs.first { it.nameForTest == nameForTest }?.toPredicate()) 41 | } 42 | 43 | internal fun BaseValidationConfig.getRequiredFieldNamesEx( 44 | beanClass: Class 45 | ): Set = 46 | (shouldHaveFieldsOrFailWith.keys + 47 | (shouldHaveFieldsOrFailWithFn?._1 ?: emptyList()) + 48 | shouldHaveFieldOrFailWithFn.keys) 49 | .map { PropertyUtils.getPropertyName(beanClass, it) } 50 | .toSet() 51 | 52 | internal fun getValidatableType( 53 | config: BaseValidationConfig 54 | ): Type? { 55 | (config.withValidators?._1?.firstOrNull() ?: config.withValidator.keys.firstOrNull())?.let { 56 | return TypeResolver.resolveRawArguments(Validator::class.java, it.javaClass)[0] 57 | } 58 | (config.shouldHaveFieldsOrFailWith.keys.firstOrNull() 59 | ?: config.shouldHaveFieldsOrFailWithFn?._1?.firstOrNull() 60 | ?: config.shouldHaveFieldOrFailWithFn.keys.firstOrNull()) 61 | ?.let { 62 | return TypeResolver.resolveRawArguments(TypedPropertyGetter::class.java, it.javaClass)[0] 63 | } 64 | // ! TODO gopala.akshintala 14/08/22: For other fields and FieldConfig 65 | config.withIdConfigs 66 | ?.firstOrNull() 67 | ?.prepare() 68 | ?.shouldHaveValidSFIdFormatForAllOrFailWith 69 | ?.keys 70 | ?.firstOrNull() 71 | ?._1 72 | ?.let { 73 | return TypeResolver.resolveRawArguments(TypedPropertyGetter::class.java, it.javaClass)[0] 74 | } 75 | config.getSpecsEx().firstOrNull()?.let { 76 | // ! TODO gopala.akshintala 15/08/22: Resolve for other Specs 77 | return when (it) { 78 | is Spec1<*, *, *> -> 79 | TypeResolver.resolveRawArguments(Function1::class.java, it.given.javaClass)[0] 80 | is Spec2<*, *, *, *> -> 81 | TypeResolver.resolveRawArguments(Function1::class.java, it.`when`.javaClass)[0] 82 | is Spec3<*, *, *, *, *> -> 83 | TypeResolver.resolveRawArguments(Function1::class.java, it.`when`.javaClass)[0] 84 | else -> null 85 | } 86 | } 87 | return null 88 | } 89 | -------------------------------------------------------------------------------- /vador/src/main/java/com/salesforce/vador/config/container/ContainerValidationConfig.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2022, salesforce.com, inc. 3 | * All rights reserved. 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause 6 | ******************************************************************************/ 7 | 8 | /******************************************************************************* 9 | * Copyright (c) 2022, salesforce.com, inc. 10 | * All rights reserved. 11 | * SPDX-License-Identifier: BSD-3-Clause 12 | * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause 13 | ******************************************************************************/ 14 | 15 | package com.salesforce.vador.config.container; 16 | 17 | import com.salesforce.vador.config.base.BaseContainerValidationConfig; 18 | import de.cronn.reflection.util.TypedPropertyGetter; 19 | import java.util.Collection; 20 | import java.util.Set; 21 | import lombok.AccessLevel; 22 | import lombok.EqualsAndHashCode; 23 | import lombok.Singular; 24 | import lombok.Value; 25 | import lombok.experimental.FieldDefaults; 26 | import lombok.experimental.SuperBuilder; 27 | import org.jetbrains.annotations.Nullable; 28 | 29 | @Value 30 | @FieldDefaults(level = AccessLevel.PACKAGE) 31 | @EqualsAndHashCode(callSuper = true) 32 | @SuperBuilder(buildMethodName = "prepare", builderMethodName = "toValidate", toBuilder = true) 33 | public class ContainerValidationConfig 34 | extends BaseContainerValidationConfig { 35 | 36 | @Singular 37 | Collection>> withBatchMembers; 38 | 39 | public Set getFieldNamesForBatch(Class validatableClazz) { 40 | return ContainerValidationConfigEx.getFieldNamesForBatchEx(this, validatableClazz); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /vador/src/main/java/com/salesforce/vador/config/container/ContainerValidationConfigEx.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * **************************************************************************** 3 | * Copyright (c) 2022, salesforce.com, inc. All rights reserved. SPDX-License-Identifier: 4 | * BSD-3-Clause For full license text, see the LICENSE file in the repo root or 5 | * https://opensource.org/licenses/BSD-3-Clause 6 | * **************************************************************************** 7 | */ 8 | @file:JvmName("ContainerValidationConfigEx") 9 | 10 | package com.salesforce.vador.config.container 11 | 12 | import com.salesforce.vador.config.base.BaseContainerValidationConfig 13 | import com.salesforce.vador.execution.strategies.util.fromValidators1 14 | import com.salesforce.vador.execution.strategies.util.fromValidators2 15 | import com.salesforce.vador.types.ValidatorEtr 16 | import de.cronn.reflection.util.PropertyUtils 17 | 18 | internal fun BaseContainerValidationConfig< 19 | ContainerValidatableT?, 20 | FailureT?, 21 | > 22 | .getContainerValidatorsEx(): List> = 23 | fromValidators1(withContainerValidators) + 24 | fromValidators2(withContainerValidator) + 25 | withContainerValidatorEtrs 26 | 27 | internal fun ContainerValidationConfig< 28 | ContainerValidatableT?, 29 | FailureT?, 30 | > 31 | .getFieldNamesForBatchEx(validatableClazz: Class): Set = 32 | withBatchMembers.map { PropertyUtils.getPropertyName(validatableClazz, it) }.toSet() 33 | 34 | internal fun ContainerValidationConfigWith2Levels< 35 | ContainerValidatableT?, 36 | *, 37 | FailureT?, 38 | > 39 | .getFieldNamesForBatchEx(validatableClazz: Class): Set = 40 | withBatchMembers.map { PropertyUtils.getPropertyName(validatableClazz, it) }.toSet() 41 | 42 | internal fun ContainerValidationConfigWith2Levels< 43 | *, 44 | NestedContainerValidatableT?, 45 | FailureT?, 46 | > 47 | .getFieldNamesForBatchLevel1Ex( 48 | validatableClazz: Class 49 | ): Set = 50 | withScopeOf1LevelDeep.withBatchMembers 51 | .map { PropertyUtils.getPropertyName(validatableClazz, it) } 52 | .toSet() 53 | -------------------------------------------------------------------------------- /vador/src/main/java/com/salesforce/vador/config/container/ContainerValidationConfigWith2Levels.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2022, salesforce.com, inc. 3 | * All rights reserved. 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause 6 | ******************************************************************************/ 7 | 8 | package com.salesforce.vador.config.container; 9 | 10 | import com.salesforce.vador.config.base.BaseContainerValidationConfig; 11 | import de.cronn.reflection.util.TypedPropertyGetter; 12 | import java.util.Collection; 13 | import java.util.Set; 14 | import lombok.AccessLevel; 15 | import lombok.EqualsAndHashCode; 16 | import lombok.Singular; 17 | import lombok.Value; 18 | import lombok.experimental.FieldDefaults; 19 | import lombok.experimental.SuperBuilder; 20 | import org.jetbrains.annotations.Nullable; 21 | 22 | @Value 23 | @FieldDefaults(level = AccessLevel.PACKAGE) 24 | @EqualsAndHashCode(callSuper = true) 25 | @SuperBuilder(buildMethodName = "prepare", builderMethodName = "toValidate", toBuilder = true) 26 | public class ContainerValidationConfigWith2Levels< 27 | ContainerRootLevelValidatableT, ContainerLevel1ValidatableT, FailureT> 28 | extends BaseContainerValidationConfig { 29 | 30 | @Singular 31 | Collection< 32 | TypedPropertyGetter< 33 | ContainerRootLevelValidatableT, @Nullable Collection>> 34 | withBatchMembers; 35 | 36 | ContainerValidationConfig withScopeOf1LevelDeep; 37 | 38 | public Set getFieldNamesForBatchLevel1( 39 | Class validatableClazz) { 40 | return ContainerValidationConfigEx.getFieldNamesForBatchLevel1Ex(this, validatableClazz); 41 | } 42 | 43 | public Set getFieldNamesForBatchRootLevel( 44 | Class validatableClazz) { 45 | return ContainerValidationConfigEx.getFieldNamesForBatchEx(this, validatableClazz); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /vador/src/main/java/com/salesforce/vador/execution/Vador.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * **************************************************************************** 3 | * Copyright (c) 2022, salesforce.com, inc. All rights reserved. SPDX-License-Identifier: 4 | * BSD-3-Clause For full license text, see the LICENSE file in the repo root or 5 | * https://opensource.org/licenses/BSD-3-Clause 6 | * **************************************************************************** 7 | */ 8 | @file:JvmName("Vador") 9 | 10 | package com.salesforce.vador.execution 11 | 12 | import com.salesforce.vador.config.ValidationConfig 13 | import com.salesforce.vador.config.container.ContainerValidationConfig 14 | import com.salesforce.vador.config.container.ContainerValidationConfigWith2Levels 15 | import com.salesforce.vador.execution.strategies.accumulationStrategy 16 | import com.salesforce.vador.execution.strategies.failFast 17 | import com.salesforce.vador.execution.strategies.failFastForContainer 18 | import com.salesforce.vador.lift.liftAllToEtr 19 | import com.salesforce.vador.types.Validator 20 | import com.salesforce.vador.types.ValidatorEtr 21 | import java.util.Optional 22 | 23 | object Vador { 24 | /** <--- CONTAINER --- */ 25 | @JvmStatic 26 | @JvmOverloads 27 | fun validateAndFailFastForContainer( 28 | container: ContainerValidatableT, 29 | containerValidationConfig: ContainerValidationConfig, 30 | throwableMapper: (Throwable) -> FailureT? = { throw it }, 31 | ): Optional = 32 | failFastForContainer(containerValidationConfig, throwableMapper)(container) 33 | 34 | @JvmStatic 35 | @JvmOverloads 36 | fun < 37 | FailureT : Any, 38 | ContainerValidatableT, 39 | NestedContainerValidatableT, 40 | > validateAndFailFastForContainer( 41 | container: ContainerValidatableT, 42 | containerValidationConfigWith2Levels: 43 | ContainerValidationConfigWith2Levels< 44 | ContainerValidatableT, 45 | NestedContainerValidatableT, 46 | FailureT?, 47 | >, 48 | throwableMapper: (Throwable) -> FailureT? = { throw it }, 49 | ): Optional = 50 | failFastForContainer(containerValidationConfigWith2Levels, throwableMapper)(container) 51 | 52 | /** --- CONTAINER ---> */ 53 | @JvmStatic 54 | @JvmOverloads 55 | fun validateAndFailFast( 56 | validatable: ValidatableT, 57 | validationConfig: ValidationConfig, 58 | throwableMapper: (Throwable) -> FailureT? = { throw it }, 59 | ): Optional = failFast(validationConfig, throwableMapper)(validatable) 60 | 61 | // --- ERROR ACCUMULATION --- 62 | /** 63 | * Applies the Simple validators on a Single validatable in error-accumulation mode. 64 | * 65 | * @param validatable 66 | * @param validators 67 | * @param none Value to be returned in case of no failures. 68 | * @param 69 | * @param 70 | * @return List of Validation failures. 71 | */ 72 | @JvmStatic 73 | @JvmOverloads 74 | fun validateAndAccumulateErrors( 75 | validatable: ValidatableT, 76 | validators: Collection>, 77 | none: FailureT, 78 | throwableMapper: (Throwable) -> FailureT? = { throw it }, 79 | ): List = 80 | validateAndAccumulateErrors(validatable, liftAllToEtr(validators, none), none, throwableMapper) 81 | 82 | /** 83 | * Applies the validators on a Single validatable in error-accumulation mode. The Accumulated 84 | * 85 | * @param validatable 86 | * @param validators 87 | * @param none Value to be returned in case of no failures. 88 | * @param 89 | * @param 90 | * @param throwableMapper Function to map throwable to Failure in case of exception 91 | * @return List of Validation failures. EmptyList if all the validations pass. 92 | * 93 | */ 94 | @JvmStatic 95 | @JvmOverloads 96 | fun validateAndAccumulateErrors( 97 | validatable: ValidatableT, 98 | validators: List>, 99 | none: FailureT, 100 | throwableMapper: (Throwable) -> FailureT? = { throw it }, 101 | ): List { 102 | val results = 103 | accumulationStrategy(validators, throwableMapper)(validatable).map { result -> 104 | result.fold({ it }, { none }) 105 | } 106 | return if (results.all { it == none }) emptyList() else results 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /vador/src/main/java/com/salesforce/vador/execution/strategies/AccumulationStrategies.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * **************************************************************************** 3 | * Copyright (c) 2022, salesforce.com, inc. All rights reserved. SPDX-License-Identifier: 4 | * BSD-3-Clause For full license text, see the LICENSE file in the repo root or 5 | * https://opensource.org/licenses/BSD-3-Clause 6 | * **************************************************************************** 7 | */ 8 | package com.salesforce.vador.execution.strategies 9 | 10 | import com.salesforce.vador.execution.strategies.util.fireValidators 11 | import com.salesforce.vador.types.ValidatorEtr 12 | import io.vavr.control.Either 13 | import io.vavr.kotlin.right 14 | 15 | /** 16 | * Higher-order function to compose list of validators into Accumulation Strategy. 17 | * 18 | * @param validators 19 | * @param invalidValidatable 20 | * @param 21 | * @param 22 | * @return Composed Accumulation Strategy 23 | */ 24 | @JvmSynthetic 25 | internal fun accumulationStrategy( 26 | validators: List>, 27 | throwableMapper: (Throwable) -> FailureT?, 28 | ): Accumulation = { 29 | fireValidators(right(it), validators, throwableMapper).toList() 30 | } 31 | 32 | internal typealias Accumulation = 33 | (ValidatableT) -> List> 34 | -------------------------------------------------------------------------------- /vador/src/main/java/com/salesforce/vador/lift/AggregationLiftEtrUtil.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * **************************************************************************** 3 | * Copyright (c) 2022, salesforce.com, inc. All rights reserved. SPDX-License-Identifier: 4 | * BSD-3-Clause For full license text, see the LICENSE file in the repo root or 5 | * https://opensource.org/licenses/BSD-3-Clause 6 | * **************************************************************************** 7 | */ 8 | @file:JvmName("AggregationLiftEtrUtil") 9 | 10 | package com.salesforce.vador.lift 11 | 12 | import com.salesforce.vador.types.ValidatorEtr 13 | 14 | /** 15 | * Lifts a member validation to container type. 16 | * 17 | * @param memberValidator 18 | * @param toMemberMapper Mapper function to extract member from container 19 | * @param nullContainer Failure to return if container is null 20 | * @param nullMember Failure to return if member is null 21 | * @param 22 | * @param 23 | * @param 24 | * @return container type validation 25 | */ 26 | fun liftToContainerValidatorType( 27 | memberValidator: ValidatorEtr, 28 | toMemberMapper: (ContainerT?) -> MemberT?, 29 | ): ValidatorEtr = ValidatorEtr { container -> 30 | val member = container?.map(toMemberMapper) 31 | memberValidator.unchecked().apply(member) 32 | } // This whole function is inside a CheckedFunction, so no problem with `uncChecked()` above 33 | 34 | /** 35 | * Lifts a list of member validations to container type. 36 | * 37 | * @param memberValidatorEtrs List of member validations 38 | * @param toChildMapper Mapper function to extract member from container 39 | * @param invalidParent Failure to return if container is null 40 | * @param invalidChild Failure to return if member is null 41 | * @param 42 | * @param 43 | * @param 44 | * @return List of container type validations 45 | */ 46 | fun liftAllToContainerValidatorType( 47 | memberValidatorEtrs: Collection>, 48 | toMemberMapper: (ContainerT?) -> MemberT?, 49 | ): List> = 50 | memberValidatorEtrs.map { liftToContainerValidatorType(it, toMemberMapper) } 51 | -------------------------------------------------------------------------------- /vador/src/main/java/com/salesforce/vador/lift/AggregationLiftUtil.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * **************************************************************************** 3 | * Copyright (c) 2022, salesforce.com, inc. All rights reserved. SPDX-License-Identifier: 4 | * BSD-3-Clause For full license text, see the LICENSE file in the repo root or 5 | * https://opensource.org/licenses/BSD-3-Clause 6 | * **************************************************************************** 7 | */ 8 | @file:JvmName("AggregationLiftUtil") 9 | 10 | package com.salesforce.vador.lift 11 | 12 | import com.salesforce.vador.types.Validator 13 | 14 | /** 15 | * Lifts a simple member validation to container type. 16 | * 17 | * @param memberValidation Validation on the Member 18 | * @param toMemberMapper Mapper function to extract member from container 19 | * @param 20 | * @param 21 | * @param 22 | * @return Container type validation 23 | */ 24 | fun liftToContainerValidatorType( 25 | memberValidation: Validator, 26 | toMemberMapper: (ContainerT?) -> MemberT?, 27 | ): Validator = Validator { memberValidation.apply(toMemberMapper(it)) } 28 | 29 | /** 30 | * Lifts a list of simple member validations to container type. 31 | * 32 | * @param memberValidations List of member validations 33 | * @param toMemberMapper Mapper function to extract member from container 34 | * @param 35 | * @param 36 | * @param 37 | * @return List of container type validations 38 | */ 39 | fun liftAllToContainerValidatorType( 40 | memberValidations: Collection>, 41 | toMemberMapper: (ContainerT?) -> MemberT?, 42 | ): List> = 43 | memberValidations.map { liftToContainerValidatorType(it, toMemberMapper) } 44 | -------------------------------------------------------------------------------- /vador/src/main/java/com/salesforce/vador/lift/InheritanceLiftEtrUtil.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * **************************************************************************** 3 | * Copyright (c) 2022, salesforce.com, inc. All rights reserved. SPDX-License-Identifier: 4 | * BSD-3-Clause For full license text, see the LICENSE file in the repo root or 5 | * https://opensource.org/licenses/BSD-3-Clause 6 | * **************************************************************************** 7 | */ 8 | @file:JvmName("InheritanceLiftEtrUtil") 9 | 10 | package com.salesforce.vador.lift 11 | 12 | import com.salesforce.vador.types.ValidatorEtr 13 | import io.vavr.control.Either.narrow 14 | 15 | /** 16 | * Assume `ParentT` is parent by inheritance to `ValidatableT`. To validate `ValidatableT`, all it's 17 | * validations + all its Parent validations has to pass. These utils help to lift it's parent's 18 | * validations in the context of ValidatableT, so that they can be chained together. 19 | */ 20 | fun liftToChildValidatorType( 21 | parentValidatorEtr: ValidatorEtr 22 | ): ValidatorEtr = ValidatorEtr { childValidatable -> 23 | parentValidatorEtr.apply(narrow(childValidatable)) 24 | } 25 | 26 | fun liftAllToChildValidatorType( 27 | parentValidatorEtrs: Collection> 28 | ): List> = 29 | parentValidatorEtrs.map { liftToChildValidatorType(it) } 30 | -------------------------------------------------------------------------------- /vador/src/main/java/com/salesforce/vador/lift/ValidatorLiftUtil.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * **************************************************************************** 3 | * Copyright (c) 2022, salesforce.com, inc. All rights reserved. SPDX-License-Identifier: 4 | * BSD-3-Clause For full license text, see the LICENSE file in the repo root or 5 | * https://opensource.org/licenses/BSD-3-Clause 6 | * **************************************************************************** 7 | */ 8 | @file:JvmName("ValidatorLiftUtil") 9 | 10 | package com.salesforce.vador.lift 11 | 12 | import com.salesforce.vador.types.Validator 13 | import com.salesforce.vador.types.ValidatorEtr 14 | import io.vavr.kotlin.left 15 | 16 | /** 17 | * Lifts Simple Validator to Validator type. 18 | * 19 | * @param toBeLifted Simple validator to be lifted 20 | * @param none Value to be returned in case of no failures 21 | * @param 22 | * @param 23 | * @return Validator 24 | */ 25 | fun liftToEtr( 26 | toBeLifted: Validator, 27 | none: FailureT?, 28 | ): ValidatorEtr = ValidatorEtr { 29 | it.flatMap { validatable -> 30 | val result = toBeLifted.unchecked().apply(validatable) 31 | if (result == none) it else left(result) 32 | } 33 | } 34 | 35 | /** 36 | * Lifts a list of Simple validators to list of Validator type. 37 | * 38 | * @param toBeLiftedFns List of Simple functions to be lifted. 39 | * @param none Value to be returned in case of no failures. 40 | * @param 41 | * @param 42 | * @return List of Validators 43 | */ 44 | fun liftAllToEtr( 45 | toBeLiftedFns: Collection>, 46 | none: FailureT, 47 | ): List> = toBeLiftedFns.map { liftToEtr(it, none) } 48 | -------------------------------------------------------------------------------- /vador/src/main/java/com/salesforce/vador/specs/Destructures.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * **************************************************************************** 3 | * Copyright (c) 2022, salesforce.com, inc. All rights reserved. SPDX-License-Identifier: 4 | * BSD-3-Clause For full license text, see the LICENSE file in the repo root or 5 | * https://opensource.org/licenses/BSD-3-Clause 6 | * **************************************************************************** 7 | */ 8 | @file:JvmName("Destructures") 9 | 10 | package com.salesforce.vador.specs 11 | 12 | import io.vavr.Tuple2 13 | import io.vavr.Tuple3 14 | 15 | // ! TODO gopala.akshintala 13/04/22: Move to a common module 16 | operator fun Tuple2.component1(): T1 = this._1 17 | 18 | operator fun Tuple2<*, T2>.component2(): T2 = this._2 19 | 20 | operator fun Tuple3.component1(): T1 = this._1 21 | 22 | operator fun Tuple3<*, T2, *>.component2(): T2 = this._2 23 | 24 | operator fun Tuple3<*, *, T2>.component3(): T2 = this._3 25 | -------------------------------------------------------------------------------- /vador/src/main/java/com/salesforce/vador/specs/factory/SpecFactory.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2022, salesforce.com, inc. 3 | * All rights reserved. 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause 6 | ******************************************************************************/ 7 | 8 | package com.salesforce.vador.specs.factory; 9 | 10 | import com.salesforce.vador.specs.specs.Spec1; 11 | import com.salesforce.vador.specs.specs.Spec2; 12 | import com.salesforce.vador.specs.specs.Spec3; 13 | import com.salesforce.vador.specs.specs.Spec4; 14 | import com.salesforce.vador.specs.specs.Spec5; 15 | 16 | public final class SpecFactory { 17 | 18 | @SuppressWarnings({"java:S100", "java:S1452"}) 19 | public Spec1.Spec1Builder _1() { 20 | return Spec1.check(); 21 | } 22 | 23 | @SuppressWarnings({"java:S100", "java:S1452"}) 24 | public Spec2.Spec2Builder _2() { 25 | return Spec2.check(); 26 | } 27 | 28 | @SuppressWarnings({"java:S100", "java:S1452"}) 29 | public 30 | Spec3.Spec3Builder _3() { 31 | return Spec3.check(); 32 | } 33 | 34 | @SuppressWarnings({"java:S100", "java:S1452"}) 35 | public Spec4.Spec4Builder _4() { 36 | return Spec4.check(); 37 | } 38 | 39 | public Spec5.Spec5Builder _5() { 40 | return Spec5.check(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /vador/src/main/java/com/salesforce/vador/specs/specs/Spec1.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2022, salesforce.com, inc. 3 | * All rights reserved. 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause 6 | ******************************************************************************/ 7 | 8 | package com.salesforce.vador.specs.specs; 9 | 10 | import com.salesforce.vador.specs.specs.base.BaseSpec; 11 | import io.vavr.Function1; 12 | import java.util.Collection; 13 | import java.util.function.Predicate; 14 | import lombok.AccessLevel; 15 | import lombok.EqualsAndHashCode; 16 | import lombok.NonNull; 17 | import lombok.Singular; 18 | import lombok.Value; 19 | import lombok.experimental.FieldDefaults; 20 | import lombok.experimental.SuperBuilder; 21 | import org.hamcrest.Matcher; 22 | import org.jetbrains.annotations.Nullable; 23 | 24 | @Value 25 | @EqualsAndHashCode(callSuper = true) 26 | @FieldDefaults(level = AccessLevel.PACKAGE) 27 | @SuperBuilder(buildMethodName = "done", builderMethodName = "check", toBuilder = true) 28 | public class Spec1 extends BaseSpec { 29 | 30 | @NonNull Function1 given; 31 | 32 | @Singular("shouldMatchField") 33 | Collection> shouldMatchAnyOfFields; 34 | 35 | @Singular("shouldMatch") 36 | Collection> shouldMatchAnyOf; 37 | 38 | @Nullable Function1 orFailWithFn; 39 | 40 | @Override 41 | public Predicate<@NonNull ValidatableT> toPredicate() { 42 | return SpecEx.toPredicateEx(this); 43 | } 44 | 45 | @Override 46 | public FailureT getFailure(@Nullable ValidatableT validatable) { 47 | if ((orFailWith == null) == (orFailWithFn == null)) { 48 | throw new IllegalArgumentException(String.format(INVALID_FAILURE_CONFIG, nameForTest)); 49 | } 50 | if (orFailWith != null) { 51 | return orFailWith; 52 | } 53 | return orFailWithFn.apply(getGiven().apply(validatable)); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /vador/src/main/java/com/salesforce/vador/specs/specs/Spec2.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2022, salesforce.com, inc. 3 | * All rights reserved. 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause 6 | ******************************************************************************/ 7 | 8 | package com.salesforce.vador.specs.specs; 9 | 10 | import com.salesforce.vador.specs.specs.base.BaseSpec; 11 | import io.vavr.Function1; 12 | import io.vavr.Function2; 13 | import java.util.Collection; 14 | import java.util.Map; 15 | import java.util.Set; 16 | import java.util.function.Predicate; 17 | import lombok.AccessLevel; 18 | import lombok.EqualsAndHashCode; 19 | import lombok.NonNull; 20 | import lombok.Singular; 21 | import lombok.Value; 22 | import lombok.experimental.FieldDefaults; 23 | import lombok.experimental.SuperBuilder; 24 | import org.hamcrest.Matcher; 25 | import org.jetbrains.annotations.Nullable; 26 | 27 | @Value 28 | @EqualsAndHashCode(callSuper = true) 29 | @FieldDefaults(level = AccessLevel.PACKAGE) 30 | @SuperBuilder(buildMethodName = "done", builderMethodName = "check", toBuilder = true) 31 | public class Spec2 extends BaseSpec { 32 | 33 | @NonNull Function1 when; 34 | 35 | @Singular("matches") 36 | Collection> matchesAnyOf; 37 | 38 | @NonNull Function1 then; 39 | 40 | // TODO 28/04/21 gopala.akshintala: Think about having `or` prefix 41 | @Singular("shouldMatch") 42 | Collection> shouldMatchAnyOf; 43 | 44 | @Singular("shouldRelateWithEntry") 45 | Map> shouldRelateWith; 46 | 47 | @Nullable Function2 shouldRelateWithFn; 48 | 49 | @Nullable Function2 orFailWithFn; 50 | 51 | @Override 52 | public Predicate<@Nullable ValidatableT> toPredicate() { 53 | return SpecEx.toPredicateEx(this); 54 | } 55 | 56 | @Override 57 | public FailureT getFailure(@Nullable ValidatableT validatable) { 58 | if ((orFailWith == null) == (orFailWithFn == null)) { 59 | throw new IllegalArgumentException(String.format(INVALID_FAILURE_CONFIG, nameForTest)); 60 | } 61 | if (orFailWith != null) { 62 | return orFailWith; 63 | } 64 | return orFailWithFn.apply(getWhen().apply(validatable), getThen().apply(validatable)); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /vador/src/main/java/com/salesforce/vador/specs/specs/Spec3.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2022, salesforce.com, inc. 3 | * All rights reserved. 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause 6 | ******************************************************************************/ 7 | 8 | package com.salesforce.vador.specs.specs; 9 | 10 | import com.salesforce.vador.specs.specs.base.BaseSpec; 11 | import io.vavr.Function1; 12 | import io.vavr.Function2; 13 | import io.vavr.Function3; 14 | import java.util.Collection; 15 | import java.util.Map; 16 | import java.util.Set; 17 | import java.util.function.Predicate; 18 | import lombok.AccessLevel; 19 | import lombok.EqualsAndHashCode; 20 | import lombok.NonNull; 21 | import lombok.Singular; 22 | import lombok.Value; 23 | import lombok.experimental.FieldDefaults; 24 | import lombok.experimental.SuperBuilder; 25 | import org.hamcrest.Matcher; 26 | import org.jetbrains.annotations.Nullable; 27 | 28 | @Value 29 | @EqualsAndHashCode(callSuper = true) 30 | @FieldDefaults(level = AccessLevel.PACKAGE) 31 | @SuperBuilder(buildMethodName = "done", builderMethodName = "check", toBuilder = true) 32 | public class Spec3 33 | extends BaseSpec { 34 | 35 | @NonNull Function1 when; 36 | 37 | @Singular("matches") 38 | Collection> matchesAnyOf; 39 | 40 | @NonNull Function1 thenField1; 41 | 42 | @NonNull Function1 thenField2; 43 | 44 | @Singular("shouldRelateWithEntry") 45 | Map> shouldRelateWith; 46 | 47 | @Nullable Function2 shouldRelateWithFn; 48 | 49 | @Singular("orField1ShouldMatch") 50 | Collection> orField1ShouldMatchAnyOf; 51 | 52 | @Singular("orField2ShouldMatch") 53 | Collection> orField2ShouldMatchAnyOf; 54 | 55 | @Nullable Function3 orFailWithFn; 56 | 57 | @Override 58 | public Predicate<@Nullable ValidatableT> toPredicate() { 59 | return SpecEx.toPredicateEx(this); 60 | } 61 | 62 | @Override 63 | public FailureT getFailure(@Nullable ValidatableT validatable) { 64 | return SpecEx.getFailureEx(this, validatable); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /vador/src/main/java/com/salesforce/vador/specs/specs/Spec4.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2022, salesforce.com, inc. 3 | * All rights reserved. 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause 6 | ******************************************************************************/ 7 | 8 | package com.salesforce.vador.specs.specs; 9 | 10 | import com.salesforce.vador.specs.specs.base.BaseSpec; 11 | import io.vavr.Function1; 12 | import io.vavr.Function2; 13 | import java.util.Collection; 14 | import java.util.Map; 15 | import java.util.function.Predicate; 16 | import lombok.AccessLevel; 17 | import lombok.EqualsAndHashCode; 18 | import lombok.NonNull; 19 | import lombok.Singular; 20 | import lombok.Value; 21 | import lombok.experimental.FieldDefaults; 22 | import lombok.experimental.SuperBuilder; 23 | import org.hamcrest.Matcher; 24 | import org.jetbrains.annotations.Nullable; 25 | 26 | @Value 27 | @EqualsAndHashCode(callSuper = true) 28 | @FieldDefaults(level = AccessLevel.PACKAGE) 29 | @SuperBuilder(buildMethodName = "done", builderMethodName = "check", toBuilder = true) 30 | public class Spec4 extends BaseSpec { 31 | 32 | @Singular("whenFieldMatches") 33 | @NonNull 34 | Map, Matcher> whenTheseFieldsMatch; 35 | 36 | @Singular("thenFieldShouldMatch") 37 | @NonNull 38 | Map, Matcher> thenThoseFieldsShouldMatch; 39 | 40 | @Nullable Function2, Collection, ? extends FailureT> orFailWithFn; 41 | 42 | @Override 43 | public Predicate<@Nullable ValidatableT> toPredicate() { 44 | return SpecEx.toPredicateEx(this); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /vador/src/main/java/com/salesforce/vador/specs/specs/Spec5.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2022, salesforce.com, inc. 3 | * All rights reserved. 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause 6 | ******************************************************************************/ 7 | 8 | package com.salesforce.vador.specs.specs; 9 | 10 | import com.salesforce.vador.specs.specs.base.BaseSpec; 11 | import io.vavr.Function1; 12 | import io.vavr.Function2; 13 | import io.vavr.Tuple2; 14 | import java.util.Collection; 15 | import java.util.function.Predicate; 16 | import lombok.AccessLevel; 17 | import lombok.EqualsAndHashCode; 18 | import lombok.NonNull; 19 | import lombok.Value; 20 | import lombok.experimental.FieldDefaults; 21 | import lombok.experimental.SuperBuilder; 22 | import org.hamcrest.Matcher; 23 | import org.jetbrains.annotations.Nullable; 24 | 25 | @Value 26 | @EqualsAndHashCode(callSuper = true) 27 | @FieldDefaults(level = AccessLevel.PACKAGE) 28 | @SuperBuilder(buildMethodName = "done", builderMethodName = "check", toBuilder = true) 29 | public class Spec5 extends BaseSpec { 30 | @NonNull 31 | Tuple2<@Nullable Collection>, @Nullable Matcher> 32 | whenAllTheseFieldsMatch; 33 | 34 | @NonNull 35 | Tuple2<@Nullable Collection>, @Nullable Matcher> 36 | thenAllThoseFieldsShouldMatch; 37 | 38 | @Nullable Function2, Collection, ? extends FailureT> orFailWithFn; 39 | 40 | @Override 41 | public Predicate<@Nullable ValidatableT> toPredicate() { 42 | return SpecEx.toPredicateEx(this); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /vador/src/main/java/com/salesforce/vador/specs/specs/base/BaseSpec.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2022, salesforce.com, inc. 3 | * All rights reserved. 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause 6 | ******************************************************************************/ 7 | 8 | package com.salesforce.vador.specs.specs.base; 9 | 10 | import java.util.function.Predicate; 11 | import lombok.AccessLevel; 12 | import lombok.Getter; 13 | import lombok.experimental.FieldDefaults; 14 | import lombok.experimental.SuperBuilder; 15 | import org.jetbrains.annotations.Nullable; 16 | 17 | @Getter 18 | @FieldDefaults(level = AccessLevel.PACKAGE) 19 | @SuperBuilder(buildMethodName = "done", builderMethodName = "check", toBuilder = true) 20 | public abstract class BaseSpec { 21 | 22 | public static final String INVALID_FAILURE_CONFIG = 23 | "For Spec with: %s Either 'orFailWith' or 'orFailWithFn' should be passed, but not both"; 24 | @Nullable protected String nameForTest; 25 | @Nullable protected FailureT orFailWith; 26 | 27 | public abstract Predicate<@Nullable ValidatableT> toPredicate(); 28 | 29 | // TODO 05/06/21 gopala.akshintala: Replace with `when` expression checking instanceOf 30 | @SuppressWarnings("unused") 31 | public FailureT getFailure(@Nullable ValidatableT ignore) { 32 | return orFailWith; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /vador/src/main/java/com/salesforce/vador/types/Specs.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * **************************************************************************** 3 | * Copyright (c) 2022, salesforce.com, inc. All rights reserved. SPDX-License-Identifier: 4 | * BSD-3-Clause For full license text, see the LICENSE file in the repo root or 5 | * https://opensource.org/licenses/BSD-3-Clause 6 | * **************************************************************************** 7 | */ 8 | package com.salesforce.vador.types 9 | 10 | import com.salesforce.vador.specs.factory.SpecFactory 11 | import com.salesforce.vador.specs.specs.base.BaseSpec 12 | 13 | fun interface Spec : 14 | Function1< 15 | SpecFactory, 16 | BaseSpec.BaseSpecBuilder, 17 | > 18 | 19 | fun interface Specs : 20 | Function1< 21 | SpecFactory, 22 | Collection>, 23 | > 24 | -------------------------------------------------------------------------------- /vador/src/main/java/com/salesforce/vador/types/Validators.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * **************************************************************************** 3 | * Copyright (c) 2022, salesforce.com, inc. All rights reserved. SPDX-License-Identifier: 4 | * BSD-3-Clause For full license text, see the LICENSE file in the repo root or 5 | * https://opensource.org/licenses/BSD-3-Clause 6 | * **************************************************************************** 7 | */ 8 | package com.salesforce.vador.types 9 | 10 | import io.vavr.CheckedFunction1 11 | import io.vavr.control.Either 12 | import java.lang.reflect.Field 13 | 14 | fun interface Validator : CheckedFunction1 15 | 16 | fun interface ValidatorEtr : 17 | CheckedFunction1, Either> 18 | 19 | fun interface ValidatorAnnotation1 { 20 | fun validate(field: Field, value: FieldT?, failure: FailureT, none: FailureT): FailureT 21 | } 22 | 23 | fun interface ValidatorAnnotation2 { 24 | fun validate( 25 | field: Field, 26 | value1: FieldT, 27 | value2: FieldT, 28 | failure: FailureT, 29 | none: FailureT, 30 | ): FailureT 31 | } 32 | -------------------------------------------------------------------------------- /vador/src/main/java/com/salesforce/vador/types/failures/FFABatchOfBatchFailureWithPair.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * **************************************************************************** 3 | * Copyright (c) 2022, salesforce.com, inc. All rights reserved. SPDX-License-Identifier: 4 | * BSD-3-Clause For full license text, see the LICENSE file in the repo root or 5 | * https://opensource.org/licenses/BSD-3-Clause 6 | * **************************************************************************** 7 | */ 8 | package com.salesforce.vador.types.failures 9 | 10 | import io.vavr.Tuple2 11 | import io.vavr.control.Either 12 | import java.util.Optional 13 | 14 | class FFABatchOfBatchFailureWithPair( 15 | val failure: Either, Tuple2> 16 | ) : Either, Tuple2> by failure { 17 | val containerFailure: Optional> 18 | get() = failure.swap().toJavaOptional() 19 | 20 | val batchMemberFailure: Optional> 21 | get() = failure.toJavaOptional() 22 | 23 | val isContainerValid: Boolean 24 | get() = failure.isRight 25 | 26 | val isBatchMemberValid: Boolean 27 | get() = failure.isLeft 28 | } 29 | -------------------------------------------------------------------------------- /vador/src/main/java/com/salesforce/vador/types/failures/FFEBatchOfBatchFailure.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * **************************************************************************** 3 | * Copyright (c) 2022, salesforce.com, inc. All rights reserved. SPDX-License-Identifier: 4 | * BSD-3-Clause For full license text, see the LICENSE file in the repo root or 5 | * https://opensource.org/licenses/BSD-3-Clause 6 | * **************************************************************************** 7 | */ 8 | package com.salesforce.vador.types.failures 9 | 10 | import io.vavr.control.Either 11 | 12 | /** This is an wrapper to represent `FFEBatchOfBatchFailure` */ 13 | class FFEBatchOfBatchFailure(val failure: Either>) : 14 | Either> by failure { 15 | val containerFailure: FailureT? 16 | get() = if (failure.isLeft) failure.left else null 17 | 18 | val batchMemberFailures: List 19 | get() = if (failure.isRight) failure.get() else emptyList() 20 | } 21 | -------------------------------------------------------------------------------- /vador/src/main/java/com/salesforce/vador/types/failures/FFEBatchOfBatchFailureWithPair.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * **************************************************************************** 3 | * Copyright (c) 2022, salesforce.com, inc. All rights reserved. SPDX-License-Identifier: 4 | * BSD-3-Clause For full license text, see the LICENSE file in the repo root or 5 | * https://opensource.org/licenses/BSD-3-Clause 6 | * **************************************************************************** 7 | */ 8 | package com.salesforce.vador.types.failures 9 | 10 | import io.vavr.Tuple2 11 | import io.vavr.control.Either 12 | 13 | /** This is a wrapper to represent `FFEBatchOfBatchFailure` */ 14 | class FFEBatchOfBatchFailureWithPair( 15 | val failure: Either?, List>> 16 | ) : Either?, List>> by failure { 17 | val containerFailure: Tuple2? 18 | get() = if (failure.isLeft) failure.left else null 19 | 20 | val batchMemberFailures: List> 21 | get() = if (failure.isRight) failure.get() else emptyList() 22 | } 23 | -------------------------------------------------------------------------------- /vador/src/test/java/com/salesforce/vador/lift/AggregationLiftEtrUtilTest.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2022, salesforce.com, inc. 3 | * All rights reserved. 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause 6 | ******************************************************************************/ 7 | 8 | package com.salesforce.vador.lift; 9 | 10 | import static com.salesforce.vador.lift.AggregationLiftEtrUtil.liftToContainerValidatorType; 11 | 12 | import com.salesforce.vador.types.ValidatorEtr; 13 | import io.vavr.control.Either; 14 | import org.junit.jupiter.api.Assertions; 15 | import org.junit.jupiter.api.Test; 16 | import sample.consumer.bean.Container; 17 | import sample.consumer.bean.Member; 18 | import sample.consumer.failure.ValidationFailure; 19 | 20 | class AggregationLiftEtrUtilTest { 21 | 22 | @Test 23 | void liftToContainerValidationType() { 24 | final var failure = Either.left(ValidationFailure.VALIDATION_FAILURE_1); 25 | var memberValidator = 26 | (ValidatorEtr) member -> failure; 27 | final var liftedContainerValidator = 28 | liftToContainerValidatorType(memberValidator, Container::getMember); 29 | final var toBeValidated = new Container(0, new Member(0)); 30 | Assertions.assertSame( 31 | failure, liftedContainerValidator.unchecked().apply(Either.right(toBeValidated))); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /vador/src/test/java/com/salesforce/vador/lift/AggregationLiftUtilTest.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2022, salesforce.com, inc. 3 | * All rights reserved. 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause 6 | ******************************************************************************/ 7 | 8 | package com.salesforce.vador.lift; 9 | 10 | import static com.salesforce.vador.lift.AggregationLiftUtil.liftAllToContainerValidatorType; 11 | import static com.salesforce.vador.lift.AggregationLiftUtil.liftToContainerValidatorType; 12 | import static org.junit.jupiter.api.Assertions.assertSame; 13 | import static org.junit.jupiter.api.Assertions.assertThrows; 14 | import static org.junit.jupiter.api.Assertions.assertTrue; 15 | import static sample.consumer.failure.ValidationFailure.NONE; 16 | 17 | import com.salesforce.vador.types.Validator; 18 | import java.util.List; 19 | import lombok.Value; 20 | import org.junit.jupiter.api.DisplayName; 21 | import org.junit.jupiter.api.Test; 22 | import sample.consumer.failure.ValidationFailure; 23 | 24 | class AggregationLiftUtilTest { 25 | 26 | @DisplayName("Lifts proper Member validation") 27 | @Test 28 | void liftToContainerValidationType() { 29 | Validator memberValidator = member -> NONE; 30 | final var liftedContainerValidation = 31 | liftToContainerValidatorType(memberValidator, Container::getMember); 32 | assertSame(NONE, liftedContainerValidation.unchecked().apply(new Container(new Member(0)))); 33 | } 34 | 35 | @DisplayName("Lifted Member validation does NOT deal with Null Member") 36 | @Test 37 | void liftToContainerValidationType2ThrowForNullMember() { 38 | Validator memberValidator = 39 | member -> { 40 | if (member.getId() >= 0) { 41 | return NONE; // accessing some member prop to cause NPE 42 | } 43 | return ValidationFailure.VALIDATION_FAILURE_1; 44 | }; 45 | final var liftedContainerValidation = 46 | liftToContainerValidatorType(memberValidator, Container::getMember); 47 | final var containerWithNullMember = new Container(null); 48 | assertThrows( 49 | NullPointerException.class, () -> liftedContainerValidation.apply(containerWithNullMember)); 50 | } 51 | 52 | @DisplayName("Lifted Member validation does NOT deal with Null Container") 53 | @Test 54 | void liftToContainerValidationType2NullContainer() { 55 | Validator memberValidator = member -> null; 56 | final var liftedContainerValidation = 57 | liftToContainerValidatorType(memberValidator, Container::getMember); 58 | assertThrows(NullPointerException.class, () -> liftedContainerValidation.apply(null)); 59 | } 60 | 61 | @DisplayName("Lift when Member validation is Null") 62 | @Test 63 | void liftNullToContainerValidationType() { 64 | Validator memberValidator = null; 65 | assertThrows( 66 | NullPointerException.class, 67 | () -> liftToContainerValidatorType(memberValidator, Container::getMember)); 68 | } 69 | 70 | @DisplayName("Lift All proper Member validations") 71 | @Test 72 | void liftAllToContainerValidationType() { 73 | List> memberValidators = 74 | List.of( 75 | member -> { 76 | if (member.getId() >= 0) { 77 | return NONE; // accessing a member prop to cause NPE 78 | } 79 | return ValidationFailure.VALIDATION_FAILURE_1; 80 | }, 81 | member -> { 82 | if (member.getId() >= 0) { 83 | return NONE; // accessing a member prop to cause NPE 84 | } 85 | return ValidationFailure.VALIDATION_FAILURE_1; 86 | }, 87 | member -> { 88 | if (member.getId() >= 0) { 89 | return NONE; // accessing a member prop to cause NPE 90 | } 91 | return ValidationFailure.VALIDATION_FAILURE_1; 92 | }); 93 | final var liftedContainerValidations = 94 | liftAllToContainerValidatorType(memberValidators, Container::getMember); 95 | final var validContainer = new Container(new Member(1)); 96 | assertTrue( 97 | liftedContainerValidations.stream() 98 | .allMatch(v -> v.unchecked().apply(validContainer) == NONE)); 99 | } 100 | 101 | @Value 102 | private static class Container { 103 | Member member; 104 | } 105 | 106 | @Value 107 | private static class Member { 108 | int id; 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /vador/src/test/java/com/salesforce/vador/lift/InheritanceLiftEtrUtilKtTest.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2022, salesforce.com, inc. 3 | * All rights reserved. 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause 6 | ******************************************************************************/ 7 | 8 | package com.salesforce.vador.lift; 9 | 10 | import static com.salesforce.vador.lift.InheritanceLiftEtrUtil.liftAllToChildValidatorType; 11 | import static org.assertj.core.api.Assertions.assertThat; 12 | import static sample.consumer.failure.ValidationFailure.NONE; 13 | import static sample.consumer.failure.ValidationFailure.UNKNOWN_EXCEPTION; 14 | 15 | import com.salesforce.vador.config.ValidationConfig; 16 | import com.salesforce.vador.execution.Vador; 17 | import com.salesforce.vador.types.Validator; 18 | import com.salesforce.vador.types.ValidatorEtr; 19 | import io.vavr.Tuple; 20 | import io.vavr.control.Either; 21 | import java.util.List; 22 | import lombok.Data; 23 | import lombok.EqualsAndHashCode; 24 | import org.junit.jupiter.api.Test; 25 | import sample.consumer.failure.ValidationFailure; 26 | 27 | class InheritanceLiftEtrUtilKtTest { 28 | 29 | @Test 30 | void liftParentToChildValidatorTypeTest() { 31 | final Validator v1 = ignore -> NONE; 32 | final Validator v2 = ignore -> UNKNOWN_EXCEPTION; 33 | final var validationConfig = 34 | ValidationConfig.toValidate() 35 | .withValidators(Tuple.of(List.of(v1, v2), NONE)) 36 | .prepare(); 37 | final var result = Vador.validateAndFailFast(new Child(), validationConfig); 38 | assertThat(result).contains(UNKNOWN_EXCEPTION); 39 | } 40 | 41 | @Test 42 | void liftParentToChildValidatorEtrTypeTest() { 43 | final ValidatorEtr v1 = ignore -> Either.right(NONE); 44 | final ValidatorEtr v2 = ignore -> Either.right(NONE); 45 | final ValidatorEtr v3 = ignore -> Either.left(UNKNOWN_EXCEPTION); 46 | final var validationConfig = 47 | ValidationConfig.toValidate() 48 | .withValidatorEtrs(liftAllToChildValidatorType(List.of(v1, v2))) 49 | .withValidatorEtr(v3) 50 | .prepare(); 51 | final var result = Vador.validateAndFailFast(new Child(), validationConfig); 52 | assertThat(result).contains(UNKNOWN_EXCEPTION); 53 | } 54 | 55 | private abstract class Parent {} 56 | 57 | @Data 58 | @EqualsAndHashCode(callSuper = true) 59 | private class Child extends Parent {} 60 | } 61 | -------------------------------------------------------------------------------- /vador/src/test/java/com/salesforce/vador/lift/ValidatorLiftUtilTest.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2022, salesforce.com, inc. 3 | * All rights reserved. 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause 6 | ******************************************************************************/ 7 | 8 | package com.salesforce.vador.lift; 9 | 10 | import com.salesforce.vador.types.Validator; 11 | import io.vavr.control.Either; 12 | import org.junit.jupiter.api.Assertions; 13 | import org.junit.jupiter.api.Test; 14 | import sample.consumer.bean.Parent; 15 | import sample.consumer.failure.ValidationFailure; 16 | 17 | class ValidatorLiftUtilTest { 18 | 19 | @Test 20 | void liftValidatorForFailure() { 21 | Validator validator = 22 | parent -> ValidationFailure.VALIDATION_FAILURE_1; 23 | final var liftedValidator = ValidatorLiftUtil.liftToEtr(validator, ValidationFailure.NONE); 24 | Assertions.assertEquals( 25 | liftedValidator.unchecked().apply(Either.right(new Parent(0, null, null))), 26 | Either.left(ValidationFailure.VALIDATION_FAILURE_1)); 27 | } 28 | 29 | @Test 30 | void liftValidatorForNoFailure() { 31 | Validator validator = parent -> ValidationFailure.NONE; 32 | final var liftedValidator = ValidatorLiftUtil.liftToEtr(validator, ValidationFailure.NONE); 33 | final Parent toBeValidated = new Parent(0, null, null); 34 | Assertions.assertEquals( 35 | liftedValidator.unchecked().apply(Either.right(toBeValidated)), 36 | Either.right(toBeValidated)); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /vador/src/test/java/com/salesforce/vador/specs/factory/SpecFactoryTest.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2022, salesforce.com, inc. 3 | * All rights reserved. 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause 6 | ******************************************************************************/ 7 | 8 | package com.salesforce.vador.specs.factory; 9 | 10 | import static org.junit.jupiter.api.Assertions.assertThrows; 11 | 12 | import com.salesforce.vador.specs.failure.ValidationFailure; 13 | import lombok.Value; 14 | import org.junit.jupiter.api.Test; 15 | 16 | class SpecFactoryTest { 17 | 18 | @Test 19 | void provideBothFailWithForSpec1() { 20 | final var spec1 = 21 | new SpecFactory() 22 | ._1() 23 | .given(Bean::getValue) 24 | .orFailWith(ValidationFailure.INVALID_VALUE) 25 | .orFailWithFn(ignore -> ValidationFailure.NONE) 26 | .done(); 27 | final var bean = new Bean(""); 28 | assertThrows(IllegalArgumentException.class, () -> spec1.getFailure(bean)); 29 | } 30 | 31 | @Test 32 | void provideBothFailWithForSpec2() { 33 | final var spec2 = 34 | new SpecFactory() 35 | ._2() 36 | .when(Bean::getValue) 37 | .then(Bean::getValue) 38 | .orFailWith(ValidationFailure.INVALID_VALUE) 39 | .orFailWithFn((ignore1, ignore2) -> ValidationFailure.NONE) 40 | .done(); 41 | final var bean = new Bean(""); 42 | assertThrows(IllegalArgumentException.class, () -> spec2.getFailure(bean)); 43 | } 44 | 45 | @Test 46 | void provideBothFailWithForSpec3() { 47 | final var spec3 = 48 | new SpecFactory() 49 | ._3() 50 | .when(Bean::getValue) 51 | .thenField1(Bean::getValue) 52 | .thenField2(Bean::getValue) 53 | .orFailWith(ValidationFailure.INVALID_VALUE) 54 | .orFailWithFn((ignore1, ignore2, ignore3) -> ValidationFailure.NONE) 55 | .done(); 56 | final var bean = new Bean(""); 57 | assertThrows(IllegalArgumentException.class, () -> spec3.getFailure(bean)); 58 | } 59 | 60 | @Value 61 | private static class Bean { 62 | String value; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /vador/src/test/java/com/salesforce/vador/specs/failure/ValidationFailure.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2022, salesforce.com, inc. 3 | * All rights reserved. 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause 6 | ******************************************************************************/ 7 | 8 | /* 9 | * Copyright 2020 salesforce.com, inc. 10 | * All Rights Reserved 11 | * Company Confidential 12 | */ 13 | 14 | package com.salesforce.vador.specs.failure; 15 | 16 | import lombok.Data; 17 | 18 | /** 19 | * Reference Validation Failure 20 | * 21 | * @author gakshintala 22 | * @since 228 23 | */ 24 | @Data 25 | public class ValidationFailure { 26 | public static final ValidationFailure NONE = new ValidationFailure(ValidationFailureMessage.NONE); 27 | public static final ValidationFailure INVALID_VALUE = 28 | new ValidationFailure(ValidationFailureMessage.INVALID_VALUE); 29 | private final ValidationFailureMessage validationFailureMessage; 30 | private String exceptionMsg; 31 | } 32 | -------------------------------------------------------------------------------- /vador/src/test/java/com/salesforce/vador/specs/failure/ValidationFailureMessage.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2022, salesforce.com, inc. 3 | * All rights reserved. 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause 6 | ******************************************************************************/ 7 | 8 | /* 9 | * Copyright 2020 salesforce.com, inc. 10 | * All Rights Reserved 11 | * Company Confidential 12 | */ 13 | 14 | package com.salesforce.vador.specs.failure; 15 | 16 | import lombok.Getter; 17 | import lombok.RequiredArgsConstructor; 18 | import lombok.Setter; 19 | import lombok.ToString; 20 | 21 | /** 22 | * This enum holds all localized representations of all Service validation Failures. 23 | * 24 | * @author gakshintala 25 | * @since 220 26 | */ 27 | @RequiredArgsConstructor 28 | @Getter 29 | @ToString 30 | public enum ValidationFailureMessage { 31 | NONE(Section.COMMON_VALIDATION_FAILURE, "Success"), 32 | INVALID_VALUE("", "InvalidValue"), 33 | ; 34 | 35 | private final String section; 36 | private final String name; 37 | @Setter private Object[] params; 38 | 39 | private static final class Section { 40 | static final String COMMON_VALIDATION_FAILURE = "CommonValidationFailure"; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /vador/src/test/java/sample/consumer/BaseService.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2022, salesforce.com, inc. 3 | * All rights reserved. 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause 6 | ******************************************************************************/ 7 | 8 | /* 9 | * Copyright 2020 salesforce.com, inc. 10 | * All Rights Reserved 11 | * Company Confidential 12 | */ 13 | 14 | package sample.consumer; 15 | 16 | import com.salesforce.vador.types.ValidatorEtr; 17 | import io.vavr.collection.List; 18 | import sample.consumer.failure.ValidationFailure; 19 | 20 | /** gakshintala created on 4/13/20. */ 21 | public abstract class BaseService { 22 | 23 | List> requestValidators; 24 | 25 | public void setRequestValidators( 26 | List> requestValidators) { 27 | this.requestValidators = requestValidators; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /vador/src/test/java/sample/consumer/Service.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2022, salesforce.com, inc. 3 | * All rights reserved. 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause 6 | ******************************************************************************/ 7 | 8 | /* 9 | * Copyright 2020 salesforce.com, inc. 10 | * All Rights Reserved 11 | * Company Confidential 12 | */ 13 | 14 | package sample.consumer; 15 | 16 | import sample.consumer.bean.Parent; 17 | 18 | /** gakshintala created on 4/13/20. */ 19 | public class Service extends BaseService {} 20 | -------------------------------------------------------------------------------- /vador/src/test/java/sample/consumer/bean/Container.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2022, salesforce.com, inc. 3 | * All rights reserved. 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause 6 | ******************************************************************************/ 7 | 8 | /* 9 | * Copyright 2018 salesforce.com, inc. 10 | * All Rights Reserved 11 | * Company Confidential 12 | */ 13 | 14 | package sample.consumer.bean; 15 | 16 | // import com.force.swag.id.ID; 17 | 18 | import lombok.ToString; 19 | 20 | @ToString(callSuper = true) 21 | public class Container extends Parent { 22 | 23 | public Container(int id, Member member) { 24 | super(id, null, member); 25 | } 26 | 27 | public Container(int id) { 28 | super(id, null, null); 29 | } 30 | 31 | public Container(String sfId1) { 32 | super(0, sfId1, null); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /vador/src/test/java/sample/consumer/bean/ID.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2022, salesforce.com, inc. 3 | * All rights reserved. 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause 6 | ******************************************************************************/ 7 | 8 | package sample.consumer.bean; 9 | 10 | class ID { 11 | String value; 12 | } 13 | -------------------------------------------------------------------------------- /vador/src/test/java/sample/consumer/bean/Member.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2022, salesforce.com, inc. 3 | * All rights reserved. 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause 6 | ******************************************************************************/ 7 | 8 | /* 9 | * Copyright 2018 salesforce.com, inc. 10 | * All Rights Reserved 11 | * Company Confidential 12 | */ 13 | 14 | package sample.consumer.bean; 15 | 16 | import lombok.Value; 17 | 18 | @Value 19 | public class Member { 20 | int id; 21 | } 22 | -------------------------------------------------------------------------------- /vador/src/test/java/sample/consumer/bean/Parent.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2022, salesforce.com, inc. 3 | * All rights reserved. 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause 6 | ******************************************************************************/ 7 | 8 | /* 9 | * Copyright 2018 salesforce.com, inc. 10 | * All Rights Reserved 11 | * Company Confidential 12 | */ 13 | 14 | package sample.consumer.bean; 15 | 16 | // import com.force.swag.id.ID; 17 | 18 | import lombok.AllArgsConstructor; 19 | import lombok.EqualsAndHashCode; 20 | import lombok.Getter; 21 | import lombok.RequiredArgsConstructor; 22 | import lombok.ToString; 23 | 24 | @AllArgsConstructor 25 | @RequiredArgsConstructor 26 | @EqualsAndHashCode 27 | @ToString 28 | @Getter 29 | public class Parent { 30 | final int id; 31 | final String sfId; 32 | final Member member; 33 | 34 | Integer requiredField1; 35 | String requiredField2; 36 | String requiredField3; 37 | String sfId1; 38 | String sfId2; 39 | } 40 | -------------------------------------------------------------------------------- /vador/src/test/java/sample/consumer/config/ConfigForValidators.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2022, salesforce.com, inc. 3 | * All rights reserved. 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause 6 | ******************************************************************************/ 7 | 8 | /* 9 | * Copyright 2020 salesforce.com, inc. 10 | * All Rights Reserved 11 | * Company Confidential 12 | */ 13 | 14 | package sample.consumer.config; 15 | 16 | import com.salesforce.vador.types.Validator; 17 | import com.salesforce.vador.types.ValidatorEtr; 18 | import io.vavr.collection.List; 19 | import lombok.experimental.UtilityClass; 20 | import sample.consumer.bean.Container; 21 | import sample.consumer.bean.Parent; 22 | import sample.consumer.failure.ValidationFailure; 23 | import sample.consumer.validators.etr.BaseParentValidatorEtr; 24 | import sample.consumer.validators.simple.BaseParentValidator; 25 | import sample.consumer.validators.simple.ContainerValidator; 26 | 27 | /** gakshintala created on 4/13/20. */ 28 | @UtilityClass 29 | public class ConfigForValidators { 30 | 31 | public static List> getServiceValidations() { 32 | return List.of(BaseParentValidatorEtr.validatorEtr1, BaseParentValidatorEtr.validatorEtr2); 33 | } 34 | 35 | public static List> getParentValidations() { 36 | return null; 37 | } 38 | 39 | public static List> getParentSimpleValidations() { 40 | return List.of(ContainerValidator.validator1, ContainerValidator.validator2); 41 | } 42 | 43 | public static List> getSimpleServiceValidations() { 44 | return List.of(BaseParentValidator.validator1, ContainerValidator.validator1); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /vador/src/test/java/sample/consumer/config/ServiceConfig.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2022, salesforce.com, inc. 3 | * All rights reserved. 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause 6 | ******************************************************************************/ 7 | 8 | /* 9 | * Copyright 2020 salesforce.com, inc. 10 | * All Rights Reserved 11 | * Company Confidential 12 | */ 13 | 14 | package sample.consumer.config; 15 | 16 | import com.salesforce.vador.types.ValidatorEtr; 17 | import io.vavr.collection.List; 18 | import sample.consumer.BaseService; 19 | import sample.consumer.Service; 20 | import sample.consumer.bean.Parent; 21 | import sample.consumer.failure.ValidationFailure; 22 | 23 | /** gakshintala created on 4/13/20. */ 24 | public class ServiceConfig { 25 | 26 | BaseService getService(List> requestValidators) { 27 | final Service service = new Service(); 28 | service.setRequestValidators(requestValidators); 29 | return service; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /vador/src/test/java/sample/consumer/failure/ValidationFailureMessage.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2022, salesforce.com, inc. 3 | * All rights reserved. 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause 6 | ******************************************************************************/ 7 | 8 | /* 9 | * Copyright 2020 salesforce.com, inc. 10 | * All Rights Reserved 11 | * Company Confidential 12 | */ 13 | 14 | package sample.consumer.failure; 15 | 16 | import lombok.Setter; 17 | import lombok.ToString; 18 | 19 | /** 20 | * This enum holds all localized representations of all Service validation Failures. 21 | * 22 | * @author gakshintala 23 | * @since 220 24 | */ 25 | @ToString 26 | public enum ValidationFailureMessage { 27 | NONE(Section.COMMON_VALIDATION_FAILURE, "Success"), 28 | FIELD_NULL_OR_EMPTY("", ""), 29 | NOTHING_TO_VALIDATE(Section.COMMON_VALIDATION_FAILURE, "Nothing"), 30 | DUPLICATE_ITEM(Section.COMMON_VALIDATION_FAILURE, "DuplicateItem"), 31 | DUPLICATE_ITEM_1(Section.COMMON_VALIDATION_FAILURE, "DuplicateItem1"), 32 | DUPLICATE_ITEM_2(Section.COMMON_VALIDATION_FAILURE, "DuplicateItem2"), 33 | NULL_KEY(Section.COMMON_VALIDATION_FAILURE, "NullKey"), 34 | INVALID_BEAN(Section.COMMON_VALIDATION_FAILURE, "InvalidBean"), 35 | INVALID_BEAN_1(Section.COMMON_VALIDATION_FAILURE, "InvalidBean1"), 36 | INVALID_BEAN_2(Section.COMMON_VALIDATION_FAILURE, "InvalidBean2"), 37 | INVALID_PARENT(Section.COMMON_VALIDATION_FAILURE, "InvalidParent"), 38 | INVALID_ITEM(Section.COMMON_VALIDATION_FAILURE, "InvalidItem"), 39 | INVALID_UDD_ID(Section.COMMON_VALIDATION_FAILURE, "InvalidUDDId"), 40 | INVALID_POLYMORPHIC_UDD_ID(Section.COMMON_VALIDATION_FAILURE, "InvalidPolyMorphicUDDId"), 41 | INVALID_OPTIONAL_UDD_ID(Section.COMMON_VALIDATION_FAILURE, "InvalidOptionalUddId"), 42 | INVALID_UDD_ID_2(Section.COMMON_VALIDATION_FAILURE, "InvalidUDDId2"), 43 | INVALID_UDD_ID_3(Section.COMMON_VALIDATION_FAILURE, "InvalidUDDId3"), 44 | INVALID_CONTAINER(Section.COMMON_VALIDATION_FAILURE, "InvalidContainer"), 45 | INVALID_CHILD(Section.COMMON_VALIDATION_FAILURE, "InvalidChild"), 46 | INVALID_MEMBER(Section.COMMON_VALIDATION_FAILURE, "InvalidMember"), 47 | UNKNOWN_EXCEPTION("", ""), 48 | OUT_OF_BOUND("", ""), 49 | VALIDATION_FAILURE_1("", ""), 50 | VALIDATION_FAILURE_2("", ""), 51 | VALIDATION_FAILURE_3("", ""), 52 | REQUIRED_FIELD_MISSING("", ""), 53 | REQUIRED_LIST_MISSING("", ""), 54 | REQUIRED_FIELD_MISSING_1("", ""), 55 | REQUIRED_FIELD_MISSING_2("", ""), 56 | INVALID_COMBO_1("", ""), 57 | INVALID_COMBO_2("", ""), 58 | INVALID_VALUE("", ""), 59 | FIELD_INTEGRITY_EXCEPTION("", ""), 60 | MIN_BATCH_SIZE_EXCEEDED("", ""), 61 | MIN_BATCH_SIZE_NOT_MET("", "MinBatchSizeNotMet"), 62 | MIN_BATCH_SIZE_NOT_MET_ROOT_LEVEL("", "MinBatchSizeNotMetRootLevel"), 63 | MAX_NESTED_BATCH_SIZE_EXCEEDED_2("", "MaxNestedBatchSizeExceeded2"), 64 | MIN_BATCH_SIZE_NOT_MET_LEVEL_1("", ""), 65 | MAX_BATCH_SIZE_EXCEEDED("", ""), 66 | MIN_BATCH_SIZE_NOT_MET_LEVEL_2("", ""), 67 | MAX_BATCH_SIZE_EXCEEDED_LEVEL_2("", ""), 68 | MAX_BATCH_SIZE_EXCEEDED_LEVEL_3("", ""), 69 | MSG_WITH_PARAMS("", ""), 70 | ; 71 | 72 | private final String section; 73 | private final String name; 74 | @Setter private Object[] params; 75 | 76 | ValidationFailureMessage(String commonValidationFailure, String success) { 77 | this.section = commonValidationFailure; 78 | this.name = success; 79 | } 80 | 81 | public Object[] getParams() { 82 | return params; 83 | } 84 | 85 | private static final class Section { 86 | static final String COMMON_VALIDATION_FAILURE = "CommonValidationFailure"; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /vador/src/test/java/sample/consumer/validators/BeanValidator.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2022, salesforce.com, inc. 3 | * All rights reserved. 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause 6 | ******************************************************************************/ 7 | 8 | package sample.consumer.validators; 9 | 10 | import static sample.consumer.failure.ValidationFailureMessage.FIELD_NULL_OR_EMPTY; 11 | 12 | import com.salesforce.vador.types.Validator; 13 | import com.salesforce.vador.types.ValidatorEtr; 14 | import lombok.Value; 15 | import sample.consumer.failure.ValidationFailure; 16 | 17 | /** Sample Validators for some random Bean */ 18 | public class BeanValidator { 19 | 20 | public static final Validator validator = 21 | bean -> { 22 | if (bean == null) { 23 | return new ValidationFailure(FIELD_NULL_OR_EMPTY); 24 | } else { 25 | return ValidationFailure.NONE; 26 | } 27 | }; 28 | 29 | public static final ValidatorEtr validatorEtr = 30 | beanEtr -> 31 | beanEtr.filterOrElse( 32 | bean -> bean.id != null, badBean -> new ValidationFailure(FIELD_NULL_OR_EMPTY)); 33 | 34 | @Value 35 | private static class Bean { 36 | String id; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /vador/src/test/java/sample/consumer/validators/etr/BaseParentValidatorEtr.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2022, salesforce.com, inc. 3 | * All rights reserved. 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause 6 | ******************************************************************************/ 7 | 8 | /* 9 | * Copyright 2019 salesforce.com, inc. 10 | * All Rights Reserved 11 | * Company Confidential 12 | */ 13 | 14 | package sample.consumer.validators.etr; 15 | 16 | import static sample.consumer.failure.ValidationFailureMessage.FIELD_NULL_OR_EMPTY; 17 | 18 | import com.salesforce.vador.types.ValidatorEtr; 19 | import sample.consumer.bean.Parent; 20 | import sample.consumer.failure.ValidationFailure; 21 | 22 | public class BaseParentValidatorEtr { 23 | 24 | /** 25 | * Validates if Auth id in request has a status PROCESSED. This is a lambda function 26 | * implementation. 27 | */ 28 | public static final ValidatorEtr validatorEtr1 = 29 | parentInputRepresentation -> 30 | parentInputRepresentation.filterOrElse( 31 | parent -> parent.getMember() != null, 32 | ignore -> new ValidationFailure(FIELD_NULL_OR_EMPTY)); 33 | 34 | public static final ValidatorEtr validatorEtr2 = 35 | parentInputRepresentation -> 36 | parentInputRepresentation.filterOrElse( 37 | parent -> parent.getMember() != null, 38 | ignore -> new ValidationFailure(FIELD_NULL_OR_EMPTY)); 39 | } 40 | -------------------------------------------------------------------------------- /vador/src/test/java/sample/consumer/validators/etr/ContainerValidatorEtr.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2022, salesforce.com, inc. 3 | * All rights reserved. 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause 6 | ******************************************************************************/ 7 | 8 | /* 9 | * Copyright 2019 salesforce.com, inc. 10 | * All Rights Reserved 11 | * Company Confidential 12 | */ 13 | 14 | package sample.consumer.validators.etr; 15 | 16 | import com.salesforce.vador.types.ValidatorEtr; 17 | import sample.consumer.bean.Container; 18 | import sample.consumer.failure.ValidationFailure; 19 | import sample.consumer.failure.ValidationFailureMessage; 20 | 21 | public class ContainerValidatorEtr { 22 | 23 | public static final ValidatorEtr validatorEtr1 = 24 | containerEtr -> 25 | containerEtr.filterOrElse( 26 | container -> container.getMember() != null, 27 | ignore -> new ValidationFailure(ValidationFailureMessage.FIELD_NULL_OR_EMPTY)); 28 | 29 | public static final ValidatorEtr validatorEtr2 = 30 | containerEtr -> 31 | containerEtr.filterOrElse( 32 | container -> container.getMember() != null, 33 | ignore -> new ValidationFailure(ValidationFailureMessage.FIELD_NULL_OR_EMPTY)); 34 | } 35 | -------------------------------------------------------------------------------- /vador/src/test/java/sample/consumer/validators/etr/MemberValidatorEtr.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2022, salesforce.com, inc. 3 | * All rights reserved. 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause 6 | ******************************************************************************/ 7 | 8 | /* 9 | * Copyright 2019 salesforce.com, inc. 10 | * All Rights Reserved 11 | * Company Confidential 12 | */ 13 | 14 | package sample.consumer.validators.etr; 15 | 16 | import com.salesforce.vador.types.ValidatorEtr; 17 | import java.util.Objects; 18 | import sample.consumer.bean.Member; 19 | import sample.consumer.failure.ValidationFailure; 20 | import sample.consumer.failure.ValidationFailureMessage; 21 | 22 | public class MemberValidatorEtr { 23 | 24 | public static final ValidatorEtr validatorEtr1 = 25 | member -> 26 | member.filterOrElse( 27 | Objects::nonNull, 28 | ignore -> new ValidationFailure(ValidationFailureMessage.FIELD_NULL_OR_EMPTY)); 29 | 30 | public static final ValidatorEtr validatorEtr2 = 31 | member -> 32 | member.filterOrElse( 33 | Objects::nonNull, 34 | ignore -> new ValidationFailure(ValidationFailureMessage.FIELD_NULL_OR_EMPTY)); 35 | } 36 | -------------------------------------------------------------------------------- /vador/src/test/java/sample/consumer/validators/simple/BaseParentValidator.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2022, salesforce.com, inc. 3 | * All rights reserved. 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause 6 | ******************************************************************************/ 7 | 8 | /* 9 | * Copyright 2019 salesforce.com, inc. 10 | * All Rights Reserved 11 | * Company Confidential 12 | */ 13 | 14 | package sample.consumer.validators.simple; 15 | 16 | import static sample.consumer.failure.ValidationFailureMessage.FIELD_NULL_OR_EMPTY; 17 | 18 | import com.salesforce.vador.types.Validator; 19 | import sample.consumer.bean.Parent; 20 | import sample.consumer.failure.ValidationFailure; 21 | 22 | public class BaseParentValidator { 23 | 24 | public static final Validator validator1 = 25 | parent -> { 26 | if (parent.getMember() == null) { 27 | return null; 28 | } else { 29 | return new ValidationFailure(FIELD_NULL_OR_EMPTY); 30 | } 31 | }; 32 | 33 | public static final Validator validator2 = 34 | parent -> { 35 | if (parent.getMember() == null) { 36 | return null; 37 | } else { 38 | return new ValidationFailure(FIELD_NULL_OR_EMPTY); 39 | } 40 | }; 41 | } 42 | -------------------------------------------------------------------------------- /vador/src/test/java/sample/consumer/validators/simple/ContainerValidator.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2022, salesforce.com, inc. 3 | * All rights reserved. 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause 6 | ******************************************************************************/ 7 | 8 | /* 9 | * Copyright 2019 salesforce.com, inc. 10 | * All Rights Reserved 11 | * Company Confidential 12 | */ 13 | 14 | package sample.consumer.validators.simple; 15 | 16 | import static sample.consumer.failure.ValidationFailureMessage.FIELD_NULL_OR_EMPTY; 17 | 18 | import com.salesforce.vador.types.Validator; 19 | import sample.consumer.bean.Container; 20 | import sample.consumer.failure.ValidationFailure; 21 | 22 | public class ContainerValidator { 23 | 24 | public static final Validator validator1 = 25 | container -> { 26 | if (container.getMember() == null) { 27 | return new ValidationFailure(FIELD_NULL_OR_EMPTY); 28 | } else { 29 | return ValidationFailure.NONE; 30 | } 31 | }; 32 | 33 | public static final Validator validator2 = 34 | container -> { 35 | if (container.getMember() == null) { 36 | return new ValidationFailure(FIELD_NULL_OR_EMPTY); 37 | } else { 38 | return ValidationFailure.NONE; 39 | } 40 | }; 41 | } 42 | -------------------------------------------------------------------------------- /vador/src/test/java/sample/consumer/validators/simple/MemberValidator.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2022, salesforce.com, inc. 3 | * All rights reserved. 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause 6 | ******************************************************************************/ 7 | 8 | /* 9 | * Copyright 2019 salesforce.com, inc. 10 | * All Rights Reserved 11 | * Company Confidential 12 | */ 13 | 14 | package sample.consumer.validators.simple; 15 | 16 | import static sample.consumer.failure.ValidationFailureMessage.FIELD_NULL_OR_EMPTY; 17 | 18 | import com.salesforce.vador.types.Validator; 19 | import sample.consumer.bean.Member; 20 | import sample.consumer.failure.ValidationFailure; 21 | 22 | public class MemberValidator { 23 | 24 | public static final Validator validator1 = 25 | member -> { 26 | if (member == null) { 27 | return new ValidationFailure(FIELD_NULL_OR_EMPTY); 28 | } else { 29 | return ValidationFailure.NONE; 30 | } 31 | }; 32 | 33 | static final Validator validator2 = 34 | member -> { 35 | if (member == null) { 36 | return new ValidationFailure(FIELD_NULL_OR_EMPTY); 37 | } else { 38 | return ValidationFailure.NONE; 39 | } 40 | }; 41 | } 42 | -------------------------------------------------------------------------------- /vador/src/test/kotlin/com/salesforce/vador/annotation/TestAnnotation.kt: -------------------------------------------------------------------------------- 1 | package com.salesforce.vador.annotation 2 | 3 | @Retention(AnnotationRetention.RUNTIME) 4 | @Target(AnnotationTarget.FIELD) 5 | annotation class TestAnnotation(val testParam: Int) 6 | -------------------------------------------------------------------------------- /vador/src/test/kotlin/com/salesforce/vador/lift/AggregationLiftUtilKtTest.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * **************************************************************************** 3 | * Copyright (c) 2022, salesforce.com, inc. All rights reserved. SPDX-License-Identifier: 4 | * BSD-3-Clause For full license text, see the LICENSE file in the repo root or 5 | * https://opensource.org/licenses/BSD-3-Clause 6 | * **************************************************************************** 7 | */ 8 | package com.salesforce.vador.lift 9 | 10 | import com.salesforce.vador.types.ValidatorEtr 11 | import io.kotest.core.spec.style.FunSpec 12 | import io.kotest.matchers.shouldBe 13 | import io.vavr.kotlin.right 14 | import sample.consumer.failure.ValidationFailure 15 | import sample.consumer.failure.ValidationFailure.NONE 16 | 17 | class AggregationLiftUtilKtTest : 18 | FunSpec({ 19 | test("liftToContainerValidatorType") { 20 | val memberValidator: ValidatorEtr = ValidatorEtr { right(NONE) } 21 | val liftedContainerValidator: ValidatorEtr = 22 | liftToContainerValidatorType(memberValidator) { it?.member } 23 | liftedContainerValidator.apply(right(Container(Member(0)))) shouldBe right(NONE) 24 | } 25 | }) 26 | 27 | data class Member(var id: Int = 0) 28 | 29 | data class Container(val member: Member = Member(0)) 30 | -------------------------------------------------------------------------------- /vador/src/test/resources/log4j2.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | --------------------------------------------------------------------------------